public static void Write(BitWriter writer, ItemStatList itemStatList) { for (int i = 0; i < itemStatList.Stats.Count; i++) { var stat = itemStatList.Stats[i]; TXTRow property = ItemStat.GetStatRow(stat); UInt16 id = property["ID"].ToUInt16(); writer.WriteUInt16(id, 9); ItemStat.Write(writer, stat); //assume these stats are in order... //https://github.com/ThePhrozenKeep/D2MOO/blob/master/source/D2Common/src/Items/Items.cpp#L7332 if (id == 52 || //magicmindam id == 17 || //item_maxdamage_percent id == 48 || //firemindam id == 50) //lightmindam { ItemStat.Write(writer, itemStatList.Stats[++i]); } else if (id == 54 || //coldmindam id == 57 //poisonmindam ) { ItemStat.Write(writer, itemStatList.Stats[++i]); ItemStat.Write(writer, itemStatList.Stats[++i]); } } writer.WriteUInt16(0x1ff, 9); }
public static ItemStat Read(BitReader reader, UInt16 id) { ItemStat itemStat = new ItemStat(); TXTRow property = Core.TXT.ItemStatCostTXT[id]; if (property == null) { throw new Exception($"No ItemStatCost record found for id: {id} at bit {reader.Position - 9}"); } itemStat.Id = id; itemStat.Stat = property["Stat"].Value; Int32 saveParamBitCount = property["Save Param Bits"].ToInt32(); Int32 encode = property["Encode"].ToInt32(); if (saveParamBitCount != 0) { Int32 saveParam = reader.ReadInt32(saveParamBitCount); //todo is there a better way to identify skill tab stats. switch (property["descfunc"].ToInt32()) { case 14: //+[value] to [skilltab] Skill Levels ([class] Only) : stat id 188 itemStat.SkillTab = saveParam & 0x7; itemStat.SkillLevel = (saveParam >> 3) & 0x1fff; break; default: break; } switch (encode) { case 2: //chance to cast skill case 3: //skill charges itemStat.SkillLevel = saveParam & 0x3f; itemStat.SkillId = (saveParam >> 6) & 0x3ff; break; case 1: case 4: //by times default: itemStat.Param = saveParam; break; } } Int32 saveBits = reader.ReadInt32(property["Save Bits"].ToInt32()); saveBits -= property["Save Add"].ToInt32(); switch (encode) { case 3: //skill charges itemStat.MaxCharges = (saveBits >> 8) & 0xff; itemStat.Value = saveBits & 0xff; break; default: itemStat.Value = saveBits; break; } return(itemStat); }
public static void Write(BitWriter writer, ItemStat stat) { TXTRow property = GetStatRow(stat); if (property == null) { throw new Exception($"No ItemStatCost record found for id: {stat.Id}"); } Int32 saveParamBitCount = property["Save Param Bits"].ToInt32(); Int32 encode = property["Encode"].ToInt32(); if (saveParamBitCount != 0) { if (stat.Param != null) { writer.WriteInt32((Int32)stat.Param, saveParamBitCount); } else { Int32 saveParamBits = 0; switch (property["descfunc"].ToInt32()) { case 14: //+[value] to [skilltab] Skill Levels ([class] Only) : stat id 188 saveParamBits |= (stat.SkillTab ?? 0 & 0x7); saveParamBits |= ((stat.SkillLevel ?? 0 & 0x1fff) << 3); break; default: break; } switch (encode) { case 2: //chance to cast skill case 3: //skill charges saveParamBits |= (stat.SkillLevel ?? 0 & 0x3f); saveParamBits |= ((stat.SkillId ?? 0 & 0x3ff) << 6); break; case 4: //by times case 1: default: break; } //always use param if it is there. if (stat.Param != null) { saveParamBits = (Int32)stat.Param; } writer.WriteInt32(saveParamBits, saveParamBitCount); } } Int32 saveBits = stat.Value; saveBits += property["Save Add"].ToInt32(); switch (encode) { case 3: //skill charges saveBits &= 0xff; saveBits |= ((stat.MaxCharges ?? 0 & 0xff) << 8); break; default: break; } writer.WriteInt32(saveBits, property["Save Bits"].ToInt32()); }
protected static void WriteComplete(BitWriter writer, Item item, UInt32 version) { writer.WriteUInt32(item.Id); writer.WriteByte(item.ItemLevel, 7); writer.WriteByte((byte)item.Quality, 4); writer.WriteBit(item.HasMultipleGraphics); if (item.HasMultipleGraphics) { writer.WriteByte(item.GraphicId, 3); } writer.WriteBit(item.IsAutoAffix); if (item.IsAutoAffix) { writer.WriteUInt16(item.AutoAffixId, 11); } switch (item.Quality) { case ItemQuality.Normal: break; case ItemQuality.Inferior: case ItemQuality.Superior: writer.WriteUInt32(item.FileIndex, 3); break; case ItemQuality.Magic: writer.WriteUInt16(item.MagicPrefixIds[0], 11); writer.WriteUInt16(item.MagicSuffixIds[0], 11); break; case ItemQuality.Rare: case ItemQuality.Craft: writer.WriteUInt16(item.RarePrefixId, 8); writer.WriteUInt16(item.RareSuffixId, 8); for (int i = 0; i < 3; i++) { var hasPrefix = item.MagicPrefixIds[i] > 0; var hasSuffix = item.MagicSuffixIds[i] > 0; writer.WriteBit(hasPrefix); if (hasPrefix) { writer.WriteUInt16(item.MagicPrefixIds[i], 11); } writer.WriteBit(hasSuffix); if (hasSuffix) { writer.WriteUInt16(item.MagicSuffixIds[i], 11); } } break; case ItemQuality.Set: case ItemQuality.Unique: writer.WriteUInt32(item.FileIndex, 12); break; } UInt16 propertyLists = 0; if (item.IsRuneword) { writer.WriteUInt32(item.RunewordId, 12); propertyLists |= 1 << 6; writer.WriteUInt16((UInt16)5, 4); } if (item.IsPersonalized) { WritePlayerName(writer, item.PlayerName); } if (item.Code.Trim() == "tbk" || item.Code.Trim() == "ibk") { writer.WriteUInt16(item.MagicSuffixIds[0], 5); } writer.WriteBit(item.HasRealmData); if (item.HasRealmData) { //todo 96 bits } ItemStatCostTXT itemStatCostTXT = Core.TXT.ItemStatCostTXT; TXTRow row = Core.TXT.ItemsTXT.GetByCode(item.Code); bool isArmor = Core.TXT.ItemsTXT.IsArmor(item.Code); bool isWeapon = Core.TXT.ItemsTXT.IsWeapon(item.Code); bool isStackable = row["stackable"].ToBool(); if (isArmor) { writer.WriteUInt16((UInt16)(item.Armor - itemStatCostTXT["armorclass"]["Save Add"].ToUInt16()), 11); } if (isArmor || isWeapon) { var maxDurabilityStat = itemStatCostTXT["maxdurability"]; var durabilityStat = itemStatCostTXT["maxdurability"]; writer.WriteUInt16((UInt16)(item.MaxDurability - maxDurabilityStat["Save Add"].ToUInt16()), maxDurabilityStat["Save Bits"].ToInt32()); if (item.MaxDurability > 0) { writer.WriteUInt16((UInt16)(item.Durability - durabilityStat["Save Add"].ToUInt16()), durabilityStat["Save Bits"].ToInt32()); ////what is this? writer.WriteBit(false); } } if (isStackable) { writer.WriteUInt16(item.Quantity, 9); } if (item.IsSocketed) { writer.WriteByte(item.TotalNumberOfSockets, 4); } if (item.Quality == ItemQuality.Set) { writer.WriteByte(item.SetItemMask, 5); propertyLists |= item.SetItemMask; } ItemStatList.Write(writer, item.StatLists[0]); var idx = 1; for (int i = 1; i <= 64; i <<= 1) { if ((propertyLists & i) != 0) { ItemStatList.Write(writer, item.StatLists[idx++]); } } }
protected static void ReadComplete(BitReader reader, Item item, UInt32 version) { item.Id = reader.ReadUInt32(); item.ItemLevel = reader.ReadByte(7); item.Quality = (ItemQuality)reader.ReadByte(4); item.HasMultipleGraphics = reader.ReadBit(); if (item.HasMultipleGraphics) { item.GraphicId = reader.ReadByte(3); } item.IsAutoAffix = reader.ReadBit(); if (item.IsAutoAffix) { item.AutoAffixId = reader.ReadUInt16(11); } switch (item.Quality) { case ItemQuality.Normal: break; case ItemQuality.Inferior: case ItemQuality.Superior: item.FileIndex = reader.ReadUInt16(3); break; case ItemQuality.Magic: item.MagicPrefixIds[0] = reader.ReadUInt16(11); item.MagicSuffixIds[0] = reader.ReadUInt16(11); break; case ItemQuality.Rare: case ItemQuality.Craft: item.RarePrefixId = reader.ReadUInt16(8); item.RareSuffixId = reader.ReadUInt16(8); for (int i = 0; i < 3; i++) { if (reader.ReadBit()) { item.MagicPrefixIds[i] = reader.ReadUInt16(11); } if (reader.ReadBit()) { item.MagicSuffixIds[i] = reader.ReadUInt16(11); } } break; case ItemQuality.Set: case ItemQuality.Unique: item.FileIndex = reader.ReadUInt16(12); break; } UInt16 propertyLists = 0; if (item.IsRuneword) { item.RunewordId = reader.ReadUInt32(12); propertyLists |= (UInt16)(1 << (reader.ReadUInt16(4) + 1)); } if (item.IsPersonalized) { item.PlayerName = ReadPlayerName(reader); } if (item.Code.Trim() == "tbk" || item.Code.Trim() == "ibk") { item.MagicSuffixIds[0] = reader.ReadByte(5); } item.HasRealmData = reader.ReadBit(); if (item.HasRealmData) { reader.ReadBits(96); } ItemStatCostTXT itemStatCostTXT = Core.TXT.ItemStatCostTXT; TXTRow row = Core.TXT.ItemsTXT.GetByCode(item.Code); bool isArmor = Core.TXT.ItemsTXT.IsArmor(item.Code); bool isWeapon = Core.TXT.ItemsTXT.IsWeapon(item.Code); bool isStackable = row["stackable"].ToBool(); if (isArmor) { //why do i need this cast? item.Armor = (UInt16)(reader.ReadUInt16(11) + itemStatCostTXT["armorclass"]["Save Add"].ToUInt16()); } if (isArmor || isWeapon) { var maxDurabilityStat = itemStatCostTXT["maxdurability"]; var durabilityStat = itemStatCostTXT["maxdurability"]; item.MaxDurability = (UInt16)(reader.ReadUInt16(maxDurabilityStat["Save Bits"].ToInt32()) + maxDurabilityStat["Save Add"].ToUInt16()); if (item.MaxDurability > 0) { item.Durability = (UInt16)(reader.ReadUInt16(durabilityStat["Save Bits"].ToInt32()) + durabilityStat["Save Add"].ToUInt16()); //what is this? reader.ReadBit(); } } if (isStackable) { item.Quantity = reader.ReadUInt16(9); } if (item.IsSocketed) { item.TotalNumberOfSockets = reader.ReadByte(4); } item.SetItemMask = 0; if (item.Quality == ItemQuality.Set) { item.SetItemMask = reader.ReadByte(5); propertyLists |= item.SetItemMask; } item.StatLists.Add(ItemStatList.Read(reader)); for (int i = 1; i <= 64; i <<= 1) { if ((propertyLists & i) != 0) { item.StatLists.Add(ItemStatList.Read(reader)); } } }