package mhfitem import ( "erupe-ce/common/byteframe" "erupe-ce/common/token" cfg "erupe-ce/config" ) // MHFItem represents a single item identified by its in-game item ID. type MHFItem struct { ItemID uint16 } // MHFSigilEffect represents a single effect slot on a sigil with an ID and level. type MHFSigilEffect struct { ID uint16 Level uint16 } // MHFSigil represents a weapon sigil containing up to three effects. type MHFSigil struct { Effects []MHFSigilEffect Unk0 uint8 Unk1 uint8 Unk2 uint8 Unk3 uint8 } // MHFEquipment represents an equipment piece (weapon or armor) with its // decorations and sigils as stored in the player's warehouse. type MHFEquipment struct { WarehouseID uint32 ItemType uint8 Unk0 uint8 ItemID uint16 Level uint16 Decorations []MHFItem Sigils []MHFSigil Unk1 uint16 } // MHFItemStack represents a stacked item slot in the warehouse with a quantity. type MHFItemStack struct { WarehouseID uint32 Item MHFItem Quantity uint16 Unk0 uint32 } // ReadWarehouseItem deserializes an MHFItemStack from a ByteFrame, assigning a // random warehouse ID if the encoded ID is zero. func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack { var item MHFItemStack item.WarehouseID = bf.ReadUint32() if item.WarehouseID == 0 { item.WarehouseID = token.RNG.Uint32() } item.Item.ItemID = bf.ReadUint16() item.Quantity = bf.ReadUint16() item.Unk0 = bf.ReadUint32() return item } // DiffItemStacks merges an updated item stack list into an existing one, // matching by warehouse ID. New items receive a random ID; items with zero // quantity in the old list are removed. func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack { // o = old, u = update, f = final var f []MHFItemStack for _, uItem := range u { exists := false for i := range o { if o[i].WarehouseID == uItem.WarehouseID { exists = true o[i].Quantity = uItem.Quantity } } if !exists { uItem.WarehouseID = token.RNG.Uint32() f = append(f, uItem) } } for _, oItem := range o { if oItem.Quantity > 0 { f = append(f, oItem) } } return f } // ToBytes serializes the item stack to its binary protocol representation. func (is MHFItemStack) ToBytes() []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(is.WarehouseID) bf.WriteUint16(is.Item.ItemID) bf.WriteUint16(is.Quantity) bf.WriteUint32(is.Unk0) return bf.Data() } // SerializeWarehouseItems serializes a slice of item stacks with a uint16 // count header for transmission in warehouse response packets. func SerializeWarehouseItems(i []MHFItemStack) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(i))) bf.WriteUint16(0) // Unused for _, j := range i { bf.WriteBytes(j.ToBytes()) } return bf.Data() } // ReadWarehouseEquipment deserializes an MHFEquipment from a ByteFrame. The // binary layout varies by game version: sigils are present from G1 onward and // an additional field is present from Z1 onward. func ReadWarehouseEquipment(bf *byteframe.ByteFrame, mode cfg.Mode) MHFEquipment { var equipment MHFEquipment equipment.Decorations = make([]MHFItem, 3) equipment.Sigils = make([]MHFSigil, 3) for i := 0; i < 3; i++ { equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3) } equipment.WarehouseID = bf.ReadUint32() if equipment.WarehouseID == 0 { equipment.WarehouseID = token.RNG.Uint32() } equipment.ItemType = bf.ReadUint8() equipment.Unk0 = bf.ReadUint8() equipment.ItemID = bf.ReadUint16() equipment.Level = bf.ReadUint16() for i := 0; i < 3; i++ { equipment.Decorations[i].ItemID = bf.ReadUint16() } if mode >= cfg.G1 { for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { equipment.Sigils[i].Effects[j].ID = bf.ReadUint16() } for j := 0; j < 3; j++ { equipment.Sigils[i].Effects[j].Level = bf.ReadUint16() } equipment.Sigils[i].Unk0 = bf.ReadUint8() equipment.Sigils[i].Unk1 = bf.ReadUint8() equipment.Sigils[i].Unk2 = bf.ReadUint8() equipment.Sigils[i].Unk3 = bf.ReadUint8() } } if mode >= cfg.Z1 { equipment.Unk1 = bf.ReadUint16() } return equipment } // ToBytes serializes the equipment to its binary protocol representation. func (e MHFEquipment) ToBytes(mode cfg.Mode) []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(e.WarehouseID) bf.WriteUint8(e.ItemType) bf.WriteUint8(e.Unk0) bf.WriteUint16(e.ItemID) bf.WriteUint16(e.Level) for i := 0; i < 3; i++ { bf.WriteUint16(e.Decorations[i].ItemID) } if mode >= cfg.G1 { for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { bf.WriteUint16(e.Sigils[i].Effects[j].ID) } for j := 0; j < 3; j++ { bf.WriteUint16(e.Sigils[i].Effects[j].Level) } bf.WriteUint8(e.Sigils[i].Unk0) bf.WriteUint8(e.Sigils[i].Unk1) bf.WriteUint8(e.Sigils[i].Unk2) bf.WriteUint8(e.Sigils[i].Unk3) } } if mode >= cfg.Z1 { bf.WriteUint16(e.Unk1) } return bf.Data() } // SerializeWarehouseEquipment serializes a slice of equipment with a uint16 // count header for transmission in warehouse response packets. func SerializeWarehouseEquipment(i []MHFEquipment, mode cfg.Mode) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(i))) bf.WriteUint16(0) // Unused for _, j := range i { bf.WriteBytes(j.ToBytes(mode)) } return bf.Data() }