feat(campaign): implement Event Tent campaign system

Adds the full campaign/event-tent feature:

Packet layer:
- MsgMhfApplyCampaign: Unk0/Unk1/Unk2 → CampaignID/Code (null-terminated 16-byte string)
- MsgMhfAcquireItem: Unk0/Length/Unk1 → RewardIDs []uint32
- MsgMhfEnumerateItem: remove Unk0/Unk1 (RE'd: zeroed + always-2, ignored)
- MsgMhfStateCampaign: Unk1 → NullPadding (RE'd: always zero)
- MsgMhfTransferItem: Unk0/Unk1/Unk2 → QuestID/ItemType/Quantity (RE'd)

Handler layer (handlers_campaign.go):
- handleMsgMhfEnumerateCampaign: reads campaigns, categories, links from DB;
  prefix moved into pascal string slot 3 of each event entry (RE confirmed
  3-section response format — removes spurious intermediate section)
- handleMsgMhfStateCampaign: returns stamp count and redeemable flag
- handleMsgMhfApplyCampaign: validates and records code redemption
- handleMsgMhfEnumerateItem: lists rewards gated by stamp count
- handleMsgMhfAcquireItem: marks rewards as claimed
- handleMsgMhfTransferItem: records campaign quest completion (item_type=9)

Quest gating (handlers_quest.go):
- makeEventQuest: for QuestTypeSpecialTool, check campaign stamp count and
  deadline before allowing the quest (WriteBool true/false)

Database:
- 0010_campaign.sql: 8-table schema (campaigns, categories, links, rewards,
  claimed, state, codes, quest)
- CampaignDemo.sql: community-researched live game campaign data
This commit is contained in:
Houmgaor
2026-03-20 11:22:25 +01:00
parent 97ef09be64
commit 77e7969579
10 changed files with 5528 additions and 120 deletions

View File

@@ -11,12 +11,9 @@ import (
// MsgMhfTransferItem represents the MSG_MHF_TRANSFER_ITEM
type MsgMhfTransferItem struct {
AckHandle uint32
// looking at packets, these were static across sessions and did not actually
// correlate with any item IDs that would make sense to get after quests so
// I have no idea what this actually does
Unk0 uint32
Unk1 uint8
Unk2 uint16
QuestID uint32
ItemType uint8
Quantity uint16
}
// Opcode returns the ID associated with this packet type.
@@ -27,10 +24,10 @@ func (m *MsgMhfTransferItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfTransferItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint8()
m.QuestID = bf.ReadUint32()
m.ItemType = bf.ReadUint8()
bf.ReadUint8() // Zeroed
m.Unk2 = bf.ReadUint16()
m.Quantity = bf.ReadUint16()
return nil
}