diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 0eb217637..ba9ecb2c9 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -55,7 +55,7 @@ namespace Laurion static inline void ServerToLaurionConvertLinks(std::string& message_out, const std::string& message_in); static inline void LaurionToServerConvertLinks(std::string& message_out, const std::string& message_in); - //SpawnAppearance + // SpawnAppearance static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type); static inline uint32 LaurionToServerSpawnAppearanceType(uint32 laurion_type); @@ -71,7 +71,16 @@ namespace Laurion static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); - item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + // Item packet types + static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + + // casting slots + static inline spells::CastingSlot ServerToLaurionCastingSlot(EQ::spells::CastingSlot slot); + static inline EQ::spells::CastingSlot LaurionToServerCastingSlot(spells::CastingSlot slot); + + // buff slots + static inline int ServerToLaurionBuffSlot(int index); + static inline int LaurionToServerBuffSlot(int index); void Register(EQStreamIdentifier& into) { @@ -1163,7 +1172,8 @@ namespace Laurion u8 status; */ - out.WriteUInt64(0); + out.WriteInt32(emu->guild_id); + out.WriteUInt32(0); out.WriteUInt8(0); out.WriteUInt8(5); @@ -1262,7 +1272,7 @@ namespace Laurion out.WriteUInt32(emu->careerEbonCrystals); /* - u32 momentum_balance; + u32 momentum_balance; u32 loyalty_reward_balance; u32 parcel_status; */ @@ -2926,6 +2936,104 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_BeginCast) + { + SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); + + OUT(spell_id); + OUT(caster_id); + OUT(cast_time); + eq->unknown0e = 1; //not sure what this is; but its usually 1 on live + + FINISH_ENCODE(); + } + + ENCODE(OP_BuffCreate) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + //Laurion has one extra 0x00 byte before the end byte + uint32 sz = 13 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(emu->tic_timer); + __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt16(emu->count); + + for (int i = 0; i < emu->count; ++i) + { + __packet->WriteUInt32(emu->type == 0 ? ServerToLaurionBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown + __packet->WriteString(emu->entries[i].caster); + } + __packet->WriteUInt8(0); // Unknown1 + __packet->WriteUInt8(emu->type); // Unknown2 + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct); + SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::EQAffectPacket_Struct); + + eq->entity_id = emu->entityid; + eq->unknown004 = 0; + + //fill in affect info + eq->affect.caster_id.Id = emu->buff.player_id; + eq->affect.flags = 0; + eq->affect.spell_id = emu->buff.spellid; + eq->affect.duration = emu->buff.duration; + eq->affect.initial_duration = emu->buff.duration; + eq->affect.hit_count = emu->buff.num_hits; + eq->affect.viral_timer = 0; + eq->affect.modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f; + eq->affect.y = emu->buff.y; + eq->affect.x = emu->buff.x; + eq->affect.z = emu->buff.z; + eq->affect.level = emu->buff.level; + + eq->slot_id = ServerToLaurionBuffSlot(emu->slotid); + if (emu->bufffade == 1) + { + eq->buff_fade = 1; + } + else + { + eq->buff_fade = 2; + } + + EQApplicationPacket* outapp = nullptr; + if (emu->bufffade == 1) + { + // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon + outapp = new EQApplicationPacket(OP_BuffCreate, 30); + outapp->WriteUInt32(emu->entityid); + outapp->WriteUInt32(0); // tic timer + outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? + outapp->WriteUInt16(1); // 1 buff in this packet + outapp->WriteUInt32(ServerToLaurionBuffSlot(emu->slotid)); + outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) + outapp->WriteUInt32(0); // Duration + outapp->WriteUInt32(0); // numhits + outapp->WriteUInt8(0); // Caster name + outapp->WriteUInt8(0); // Type + outapp->WriteUInt8(0); // Type + } + + FINISH_ENCODE(); + + if (outapp) { + dest->FastQueuePacket(&outapp); + } + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3124,6 +3232,23 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + emu->slot = static_cast(LaurionToServerCastingSlot(static_cast(eq->slot))); + + //We need to figure out the x y z position stuff + IN(spell_id); + emu->inventoryslot = LaurionToServerSlot(eq->inventory_slot); + IN(target_id); + //IN(y_pos); + //IN(x_pos); + //IN(z_pos); + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; @@ -4481,7 +4606,7 @@ namespace Laurion return ServerSlot; } - item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { + static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { switch (server_type) { case ItemPacketType::ItemPacketMerchant: return item::ItemPacketType::ItemPacketMerchant; @@ -4507,4 +4632,108 @@ namespace Laurion return item::ItemPacketType::ItemPacketInvalid; } } + + //This stuff isn't right because they for one removed potion belt + //This will probably be enough to get casting working for now though + static inline spells::CastingSlot ServerToLaurionCastingSlot(EQ::spells::CastingSlot slot) { + switch (slot) { + case EQ::spells::CastingSlot::Gem1: + return spells::CastingSlot::Gem1; + case EQ::spells::CastingSlot::Gem2: + return spells::CastingSlot::Gem2; + case EQ::spells::CastingSlot::Gem3: + return spells::CastingSlot::Gem3; + case EQ::spells::CastingSlot::Gem4: + return spells::CastingSlot::Gem4; + case EQ::spells::CastingSlot::Gem5: + return spells::CastingSlot::Gem5; + case EQ::spells::CastingSlot::Gem6: + return spells::CastingSlot::Gem6; + case EQ::spells::CastingSlot::Gem7: + return spells::CastingSlot::Gem7; + case EQ::spells::CastingSlot::Gem8: + return spells::CastingSlot::Gem8; + case EQ::spells::CastingSlot::Gem9: + return spells::CastingSlot::Gem9; + case EQ::spells::CastingSlot::Gem10: + return spells::CastingSlot::Gem10; + case EQ::spells::CastingSlot::Gem11: + return spells::CastingSlot::Gem11; + case EQ::spells::CastingSlot::Gem12: + return spells::CastingSlot::Gem12; + case EQ::spells::CastingSlot::Item: + case EQ::spells::CastingSlot::PotionBelt: + return spells::CastingSlot::Item; + case EQ::spells::CastingSlot::Discipline: + return spells::CastingSlot::Discipline; + case EQ::spells::CastingSlot::AltAbility: + return spells::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return spells::CastingSlot::Discipline; + } + } + + static inline EQ::spells::CastingSlot LaurionToServerCastingSlot(spells::CastingSlot slot) { + switch (slot) { + case spells::CastingSlot::Gem1: + return EQ::spells::CastingSlot::Gem1; + case spells::CastingSlot::Gem2: + return EQ::spells::CastingSlot::Gem2; + case spells::CastingSlot::Gem3: + return EQ::spells::CastingSlot::Gem3; + case spells::CastingSlot::Gem4: + return EQ::spells::CastingSlot::Gem4; + case spells::CastingSlot::Gem5: + return EQ::spells::CastingSlot::Gem5; + case spells::CastingSlot::Gem6: + return EQ::spells::CastingSlot::Gem6; + case spells::CastingSlot::Gem7: + return EQ::spells::CastingSlot::Gem7; + case spells::CastingSlot::Gem8: + return EQ::spells::CastingSlot::Gem8; + case spells::CastingSlot::Gem9: + return EQ::spells::CastingSlot::Gem9; + case spells::CastingSlot::Gem10: + return EQ::spells::CastingSlot::Gem10; + case spells::CastingSlot::Gem11: + return EQ::spells::CastingSlot::Gem11; + case spells::CastingSlot::Gem12: + return EQ::spells::CastingSlot::Gem12; + case spells::CastingSlot::Discipline: + return EQ::spells::CastingSlot::Discipline; + case spells::CastingSlot::Item: + return EQ::spells::CastingSlot::Item; + case spells::CastingSlot::AltAbility: + return EQ::spells::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return EQ::spells::CastingSlot::Discipline; + } + } + + //Laurion has the same # of long buffs as rof2, but 10 more short buffs + static inline int ServerToLaurionBuffSlot(int index) + { + // we're a disc + if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS) + return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS + + spells::LONG_BUFFS + spells::SHORT_BUFFS; + // we're a song + if (index >= EQ::spells::LONG_BUFFS) + return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS; + // we're a normal buff + return index; // as long as we guard against bad slots server side, we should be fine + } + + static inline int LaurionToServerBuffSlot(int index) + { + // we're a disc + if (index >= spells::LONG_BUFFS + spells::SHORT_BUFFS) + return index - spells::LONG_BUFFS - spells::SHORT_BUFFS + EQ::spells::LONG_BUFFS + + EQ::spells::SHORT_BUFFS; + // we're a song + if (index >= spells::LONG_BUFFS) + return index - spells::LONG_BUFFS + EQ::spells::LONG_BUFFS; + // we're a normal buff + return index; // as long as we guard against bad slots server side, we should be fine + } } /*Laurion*/ diff --git a/common/patches/laurion_limits.h b/common/patches/laurion_limits.h index 587844ce2..a0fc1be4e 100644 --- a/common/patches/laurion_limits.h +++ b/common/patches/laurion_limits.h @@ -341,10 +341,10 @@ namespace Laurion const int SPELL_GEM_RECAST_TIMER = 15; const int LONG_BUFFS = 42; - const int SHORT_BUFFS = 20; + const int SHORT_BUFFS = 30; const int DISC_BUFFS = 1; const int TOTAL_BUFFS = LONG_BUFFS + SHORT_BUFFS + DISC_BUFFS; - const int NPC_BUFFS = 97; + const int NPC_BUFFS = 400; const int PET_BUFFS = NPC_BUFFS; const int MERC_BUFFS = LONG_BUFFS; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 2db186c25..04dbe7ba0 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -35,6 +35,9 @@ E(OP_ExpUpdate) E(OP_SendAATable) E(OP_ItemPacket) E(OP_ShopRequest) +E(OP_BeginCast) +E(OP_BuffCreate) +E(OP_Buff) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -49,6 +52,7 @@ D(OP_ClickDoor) D(OP_SpawnAppearance) D(OP_MoveItem) D(OP_ShopRequest) +D(OP_CastSpell) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index c3c3e9794..39530e101 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -621,6 +621,65 @@ namespace Laurion { /*032*/ }; + struct BeginCast_Struct + { + /*000*/ uint32 spell_id; + /*004*/ uint16 caster_id; + /*006*/ uint32 cast_time; // in miliseconds + /*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0 + /*014*/ uint8 unknown0e; // 0 will short circuit the cast, seen 1 from live usually, maybe related to interrupts or particles or something + /*015*/ + }; + + struct CastSpell_Struct + { + /*00*/ uint32 slot; + /*04*/ uint32 spell_id; + /*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast + /*20*/ uint32 spell_crc; // this is pinstSpellManager_x->SpellsCrc[spell_id] + /*24*/ uint32 target_id; + /*28*/ uint8 unknown2[11]; + /*39*/ + }; + + struct EQAffectSlot_Struct { + /*00*/ int32 slot; + /*04*/ int32 padding; + /*08*/ int64 value; + /*16*/ + }; + + struct EQAffect_Struct + { + /*000*/ EQAffectSlot_Struct slots[6]; + /*096*/ EqGuid caster_id; + /*104*/ uint32 flags; + /*108*/ uint32 spell_id; + /*112*/ uint32 duration; + /*116*/ uint32 initial_duration; + /*120*/ uint32 hit_count; + /*124*/ uint32 viral_timer; + /*128*/ float modifier; + /*132*/ float y; + /*136*/ float x; + /*140*/ float z; + /*144*/ uint8 level; + /*145*/ uint8 type; + /*146*/ uint8 charges; //no idea if these are right; eqlib doesn't seem to know either + /*147*/ uint8 activatable; + /*148*/ uint32 unknown1; //might be some timer, not sure though + /*152*/ + }; + + struct EQAffectPacket_Struct { + /*000*/ uint32 entity_id; + /*004*/ int32 unknown004; + /*008*/ EQAffect_Struct affect; + /*160*/ uint32 slot_id; + /*164*/ uint32 buff_fade; + /*168*/ + }; + #pragma pack() }; //end namespace structs