public void SendMail(Character receiver, string subject, string message, uint money, bool cashOnDelivery, List <ushort> itemSlots = null) { Player sender = (Player)_Owner; Character_mail cMail = new Character_mail { Guid = CharMgr.GenerateMailGuid(), CharacterId = receiver.CharacterId, CharacterIdSender = sender.CharacterId, SenderName = sender.Info.Name, ReceiverName = receiver.Name, SendDate = (uint)TCPManager.GetTimeStamp(), Title = subject, Content = message, Money = money, Cr = cashOnDelivery, Opened = false }; if (itemSlots != null) { foreach (ushort itmslot in itemSlots) { Item itm = sender.ItmInterface.GetItemInSlot(itmslot); // This should never happen, double check. if (itm != null && itm.Info != null) { cMail.Items.Add(new MailItem(itm.Info.Entry, itm.GetTalismans(), itm.GetPrimaryDye(), itm.GetSecondaryDye(), itm.Count)); sender.ItmInterface.DeleteItem(itmslot, itm.Count); itm.Owner = null; } } } SendResult(MailResult.TEXT_MAIL_RESULT4); CharMgr.AddMail(cMail); _nextSend = (uint)TCPManager.GetTimeStamp() + 5; }
// Writes approximately: (100 + info._Stats.Count * 8 + info.EffectsList.Count * 6 + info.SpellId > 0 ? 8 : 1 + info.Type == 23 ? info.Crafts.Count * 3 : 1 + info.Description.Length + info.TalismanSlots * 47) public static void BuildItem(ref PacketOut Out, Item itm, Item_Info info, MailItem mail, ushort SlotId, ushort Count, Player Plr = null, bool frombuildrepairitem = false) { SlotId = SlotId == 0 ? (itm?.SlotId ?? SlotId) : SlotId; Count = Count == 0 ? (itm?.Count ?? Count) : Count; info = info ?? itm?.Info; bool lootbag = false; if (mail != null) { info = ItemService.GetItem_Info(mail.id); } if (SlotId != 0 && !frombuildrepairitem) { Out.WriteUInt16(SlotId); } if (info != null && info.Entry >= 2500000 && info.Entry < 2600000) { BuildRepairableItem(ref Out, itm, info, mail, SlotId, Count, Plr); return; } if (!frombuildrepairitem) { Out.WriteByte(0); // repairable item } Out.WriteUInt32(info?.Entry ?? 0); if (info == null) { return; } //if (info.Bind == 1 || (info.Bind == 2 && itm != null && itm.BoundtoPlayer)) // Out.WriteUInt32(info.Entry << 2); //else // Out.WriteUInt32(info.Entry); if (info.Entry == 9980 || info.Entry == 9940 || info.Entry == 9941 || info.Entry == 9942 || info.Entry == 9943) // lootbags { lootbag = true; } Out.WriteUInt16((ushort)info.ModelId); // Valid 1.4.8 //Appearance if (itm != null && itm.AltAppearanceEntry > 0 && itm.Info.Type != 24) { Item_Info tmp = ItemService.GetItem_Info(itm.AltAppearanceEntry); if (tmp == null) { itm.AltAppearanceEntry = 0; Out.Fill(0, 7); } else { Out.WriteUInt16((ushort)tmp.ModelId); // DisplayId Out.WriteUInt32(itm.AltAppearanceEntry); // Id Out.WritePascalString(tmp.Name); //name } } else { Out.Fill(0, 7); } Out.WriteUInt16(info.SlotId); // Valid 1.4.8 Out.WriteByte(info.Type); // Valid 1.4.8 Out.WriteByte(info.MinRank); // Min Level Out.WriteByte(info.ObjectLevel); // 1.3.5, Object Level Out.WriteByte(info.MinRenown); // 1.3.5, Min Renown Out.WriteByte(info.MinRenown); // ? Out.WriteByte(info.UniqueEquiped); // Unique - Equiped Out.WriteByte(info.Rarity); Out.WriteByte(info.Bind); // This byte should be part of race byte Out.WriteByte(info.Race); // Trophys have some extra bytes if (info.Type == (byte)ItemTypes.ITEMTYPES_TROPHY) { Out.WriteUInt32(0); Out.WriteUInt16(0x00); } if (info.Type == (byte)ItemTypes.ITEMTYPES_ENHANCEMENT) { Out.WriteUInt32(0); } if (SlotId != 0 && info.Type == 24) // Trophy { Out.WriteUInt16(0); Out.WriteUInt16((ushort)itm.AltAppearanceEntry); } else { Out.WriteUInt32(info.Career); } Out.WriteUInt16(info.BaseColor1); // basecolor Out.WriteUInt16(info.BaseColor2); Out.WriteUInt32(info.SellPrice); Out.WriteUInt16(info.MaxStack); if (mail != null) { Out.WriteUInt16((ushort)(mail.count > 0 ? mail.count : 1)); } else { Out.WriteUInt16((ushort)(Count > 0 ? Count : 1)); } Out.WriteUInt32(info.ItemSet); Out.WriteUInt32(info.Skills); // Valid 1.4.8 Out.WriteUInt16(info.Dps > 0 ? info.Dps : info.Armor); // Valid 1.4.8 Out.WriteUInt16(info.Speed); // Valid 1.4.8 Out.WritePascalString(info.Name); // Valid 1.4.8 + (info.Bind == 1 || (info.Bind == 2 && itm != null && itm.BoundtoPlayer) ? "_" : "" ) //66 Bytes + info.Name.Length Out.WriteByte((byte)info._Stats.Count); // Valid 1.4.8 foreach (KeyValuePair <byte, ushort> Key in info._Stats) { if (Key.Key == (byte)Stats.AutoAttackSpeed) { Out.WriteByte(Key.Key); Out.WriteUInt16(Key.Value); Out.Fill(1, 5); } else { Out.WriteByte(Key.Key); Out.WriteUInt16(Key.Value); Out.Fill(0, 5); } } Out.WriteByte((byte)info.EffectsList.Count); foreach (ushort effect in info.EffectsList) { Out.WriteUInt16(effect); Out.WriteUInt32(0); } if (info.SpellId == 0) { Out.WriteByte(0); } else { Out.WriteByte(1); // (byte)info._Spells.Count OK Out.WriteUInt32(info.SpellId); Out.WriteUInt16(AbilityMgr.GetCooldownFor(info.SpellId)); // cooldown time info if (Plr == null || itm?.CharSaveInfo == null) { Out.WriteUInt16(0); // current cooldown } else { Out.WriteUInt16(itm.CharSaveInfo.RemainingCooldown); } } // (uint32)entry, uint16 X, uint16 Y if (info.Type == 23) // talisman use craft to store its buff type { Out.WriteByte(0); } else { Out.WriteByte((byte)info._Crafts.Count); // OK foreach (KeyValuePair <byte, ushort> Kp in info._Crafts) { Out.WriteByte(Kp.Key); Out.WriteUInt16(Kp.Value); } } Out.WriteByte(0); // ?? if (lootbag) { Out.WriteByte(0); } else { Out.WriteByte(info.TalismanSlots); Talisman talis = null; for (int i = 0; i < info.TalismanSlots; ++i) { if (itm != null) { talis = itm.GetTalisman((byte)i); } else if (mail != null) { talis = mail.GetTalisman((byte)i); } if (talis == null) { Out.WriteUInt32(0); // entry; } else { Item_Info talismanInfo = ItemService.GetItem_Info(talis.Entry); // Out.Fill(0, 2); Out.WriteByte(0); // slot ??? Out.WriteByte(0); Out.WriteUInt16((ushort)talismanInfo.ModelId); Out.WriteByte(talis.Fused); // 0 fused 1 unfused Out.WriteByte(0); Out.WritePascalString(talismanInfo.Name); Out.WriteByte((byte)talismanInfo._Stats.Count); // Valid 1.4.8 foreach (KeyValuePair <byte, ushort> Key in talismanInfo._Stats) { Out.WriteByte(Key.Key); Out.WriteUInt16(Key.Value); Out.WriteUInt32(talis.Timer); Out.WriteByte(0); // Out.Fill(0, 5); } Out.WriteByte((byte)talismanInfo.EffectsList.Count); foreach (ushort effect in talismanInfo.EffectsList) { Out.WriteUInt16(effect); Out.WriteUInt32(0); } //if (talismanInfo.SpellId == 0) // Out.WriteByte(0); //else //{ // Out.WriteByte(1); // (byte)talismanInfo._Spells.Count // Out.WriteUInt16(talismanInfo.SpellId); // Out.WriteUInt32(AbilityMgr.GetCooldownFor(talismanInfo.SpellId)); //} Out.Fill(0, 3); Out.WriteUInt16(0x041C); } } } Out.WritePascalString(info.Description); // Note from wash : this algorithm updates shared byte[] Unks = info.Unk27; //if (info.Bind == 1 && (itm == null || !itm.BoundtoPlayer)) // Unks[5] = (byte)(4); // bind on pickup, if set to true for one item, all items with have this flag active client side //else if (info.Bind == 2 && (itm == null || !itm.BoundtoPlayer)) // // Unks[5] = (byte)(8); // bind on equip, if set to true for one item, all items with have this flag active client side //else // Unks[5] = 0; //Unks[5] = 8; if (info.DyeAble) { Unks[6] = (byte)(Unks[6] | 1); // dyeable } if (info.Salvageable) { Unks[6] = (byte)(Unks[6] | 2); // scavangable } // Prevents sale // Unks[6] = (byte) (Unks[6] | 32); // Allow Conversion text (Ctrl+Right Click) // Unks[6] = (byte) (Unks[6] | 128); //if (itm != null && itm.BoundtoPlayer) // info.Bind == 1 // Unks[8] = (byte)(1); // bound to player, if set to true for one item, all items with have this flag active client side //else // Unks[8] = (byte)(0); Out.WriteByte(Unks[0]); // londo : wut ? Out.WriteByte(Unks[1]); // londo : getUnk7 Out.WriteByte(Unks[2]); // londo : getUnk8 Out.WriteByte(Unks[3]); // londo : getNoChargeLeftDontDelete Out.WriteByte(Unks[4]); // londo : flag count if (info.Bind == 1 && (itm == null || !itm.BoundtoPlayer)) { Out.WriteByte(4); // bind on pickup, if set to true for one item, all items with have this flag active client side } else if (info.Bind == 2 && (itm == null || !itm.BoundtoPlayer)) // { Out.WriteByte(8); // bind on equip, if set to true for one item, all items with have this flag active client side } else { Out.WriteByte(0); } Out.WriteByte(Unks[6]); // dyeable from londo (+ scavangable from RoR ?) Out.WriteByte(Unks[7]); // 0 from londo if (info.Bind == 2 && itm != null && itm.BoundtoPlayer) { Out.WriteByte(1); } else { Out.WriteByte(0); } //Unks[19] = (byte)(Unks[19] | 5); // potion cd // 4 can crash the client if changed // 5: if 1, hides the crafting level requirement. if 2, shows it. // 7 with value of 8 can suppress the Item Level text //for (int i = 0; i < 9; i++) //9 //{ // Out.WriteByte(Unks[i]); //} if (lootbag) { Out.WriteUInt16(0); Out.WriteUInt16(0); } else { if (mail != null) { Out.WriteUInt16(mail.primary_dye); Out.WriteUInt16(mail.secondary_dye); } else { Out.WriteUInt16(itm?.GetPrimaryDye() ?? 0); Out.WriteUInt16(itm?.GetSecondaryDye() ?? 0); } } // Overwrite Unks27 with the TwoHanded flag if (info.TwoHanded) { Unks[26] = (byte)(Unks[26] | 1); // bitwise? oh well better to be safe. } if ((info.SlotId == 14 || info.SlotId == 27) && Plr?.GldInterface.Guild != null) { Out.Fill(0, 7); Out.WriteByte(1); // 1, Out, 1, 2 Plr.GldInterface.Guild.BuildHeraldry(Out); Out.WriteByte(1); Out.WriteByte(1); Out.Fill(0, 6); } else { // 14: Skill level // 15: Flags for Cultivating // 20: Crashes the client if nonzero... is set on the Fleet Stag Mantle // 21-24: Seconds until decayed // 26: two-handed flag for (int i = 13; i < /*21*/ 27; i++) { Out.WriteByte(Unks[i]); } /* * 21-26: * Out.WriteUInt32(0); // Seconds until decayed. Set on the Stag Mantle but doesn't show, possible double meaning based on value 20 * * Out.WriteByte(Unks[25]); * Out.WriteByte(Unks[26]); */ } //Out.Write(Unks); /*Out.WriteByte(0); * Out.WriteByte(0); * Out.WriteByte(0); * Out.WriteByte(0); * * Out.WriteUInt16(0x0302); * * Out.Fill(0, 8); * Out.WriteByte(0); // Type , Culture, etc etc * Out.WriteByte(0); // Type, Recipian , Soil , etc etc * Out.Fill(0, 11);*/ }