diff --git a/include/py_combat_events.h b/include/py_combat_events.h index 1d5466a..153c285 100644 --- a/include/py_combat_events.h +++ b/include/py_combat_events.h @@ -43,6 +43,8 @@ #include #include +#include +#include #include #include @@ -93,57 +95,57 @@ struct RawCombatEvent { * Note: The values are NOT arbitrary - they're chosen to avoid conflicts * and are grouped by category (skills 1-8, attacks 13-15, damage 30-32, etc.) */ -namespace CombatEventTypes { +enum class CombatEventType : uint32_t { // ---- Skill Events (from GenericValue/GenericValueTarget packets) ---- // These fire when agents use skills (spells, signets, etc.) - constexpr uint32_t SKILL_ACTIVATED = 1; // Non-attack skill started casting - // agent_id=caster, value=skill_id, target_id=target + SKILL_ACTIVATED = 1, // Non-attack skill started casting + // agent_id=caster, value=skill_id, target_id=target - constexpr uint32_t ATTACK_SKILL_ACTIVATED = 2; // Attack skill started (e.g., Jagged Strike) - // agent_id=caster, value=skill_id, target_id=target + ATTACK_SKILL_ACTIVATED = 2, // Attack skill started (e.g., Jagged Strike) + // agent_id=caster, value=skill_id, target_id=target - constexpr uint32_t SKILL_STOPPED = 3; // Skill cast was cancelled (moved, etc.) - // agent_id=caster, value=skill_id + SKILL_STOPPED = 3, // Skill cast was cancelled (moved, etc.) + // agent_id=caster, value=skill_id - constexpr uint32_t SKILL_FINISHED = 4; // Skill completed successfully - // agent_id=caster, value=skill_id + SKILL_FINISHED = 4, // Skill completed successfully + // agent_id=caster, value=skill_id - constexpr uint32_t ATTACK_SKILL_FINISHED = 5; // Attack skill completed - // agent_id=caster, value=skill_id + ATTACK_SKILL_FINISHED = 5, // Attack skill completed + // agent_id=caster, value=skill_id - constexpr uint32_t INTERRUPTED = 6; // Skill was interrupted - // agent_id=interrupted agent, value=skill_id + INTERRUPTED = 6, // Skill was interrupted + // agent_id=interrupted agent, value=skill_id - constexpr uint32_t INSTANT_SKILL_ACTIVATED = 7; // Instant-cast skill used (no cast time) - // agent_id=caster, value=skill_id, target_id=target + INSTANT_SKILL_ACTIVATED = 7, // Instant-cast skill used (no cast time) + // agent_id=caster, value=skill_id, target_id=target - constexpr uint32_t ATTACK_SKILL_STOPPED = 8; // Attack skill was cancelled - // agent_id=caster, value=skill_id + ATTACK_SKILL_STOPPED = 8, // Attack skill was cancelled + // agent_id=caster, value=skill_id // ---- Attack Events (auto-attacks, from GenericValueTarget) ---- - constexpr uint32_t ATTACK_STARTED = 13; // Auto-attack started (not a skill) - // agent_id=attacker, target_id=target + ATTACK_STARTED = 13, // Auto-attack started (not a skill) + // agent_id=attacker, target_id=target - constexpr uint32_t ATTACK_STOPPED = 14; // Auto-attack stopped/cancelled - // agent_id=attacker + ATTACK_STOPPED = 14, // Auto-attack stopped/cancelled + // agent_id=attacker - constexpr uint32_t MELEE_ATTACK_FINISHED = 15; // Melee attack hit completed - // agent_id=attacker + MELEE_ATTACK_FINISHED = 15, // Melee attack hit completed + // agent_id=attacker // ---- State Events ---- - constexpr uint32_t DISABLED = 16; // Agent disabled state changed (cast-lock/aftercast) - // agent_id=agent, value=1 (disabled) or 0 (can act) - // This fires 4 times per skill: cast start, cast end, - // aftercast start, aftercast end + DISABLED = 16, // Agent disabled state changed (cast-lock/aftercast) + // agent_id=agent, value=1 (disabled) or 0 (can act) + // This fires 4 times per skill: cast start, cast end, + // aftercast start, aftercast end - constexpr uint32_t KNOCKED_DOWN = 17; // Agent was knocked down - // agent_id=knocked agent, float_value=duration in seconds + KNOCKED_DOWN = 17, // Agent was knocked down + // agent_id=knocked agent, float_value=duration in seconds - constexpr uint32_t CASTTIME = 18; // Cast time modifier received - // agent_id=caster, float_value=cast duration in seconds + CASTTIME = 18, // Cast time modifier received + // agent_id=caster, float_value=cast duration in seconds // ---- Damage Events (from GenericModifier packets) ---- // Note: For damage, the packet naming is counter-intuitive! @@ -151,57 +153,62 @@ namespace CombatEventTypes { // target_id = source (who DEALS damage) // float_value = damage as fraction of target's max HP - constexpr uint32_t DAMAGE = 30; // Normal damage dealt - // agent_id=target, target_id=source, float_value=damage% + DAMAGE = 30, // Normal damage dealt + // agent_id=target, target_id=source, float_value=damage% - constexpr uint32_t CRITICAL = 31; // Critical hit damage - // agent_id=target, target_id=source, float_value=damage% + CRITICAL = 31, // Critical hit damage + // agent_id=target, target_id=source, float_value=damage% - constexpr uint32_t ARMOR_IGNORING = 32; // Armor-ignoring damage (life steal, etc.) - // Can be negative for heals! - // agent_id=target, target_id=source, float_value=damage% + ARMOR_IGNORING = 32, // Armor-ignoring damage (life steal, etc.) + // Can be negative for heals! + // agent_id=target, target_id=source, float_value=damage% // ---- Effect Events (from GenericValue/GenericValueTarget) ---- - constexpr uint32_t EFFECT_APPLIED = 40; // Visual effect applied (internal effect_id, not skill_id!) - // agent_id=affected agent, value=effect_id + EFFECT_APPLIED = 40, // Visual effect applied (internal effect_id, not skill_id!) + // agent_id=affected agent, value=effect_id - constexpr uint32_t EFFECT_REMOVED = 41; // Visual effect removed - // agent_id=affected agent, value=effect_id + EFFECT_REMOVED = 41, // Visual effect removed + // agent_id=affected agent, value=effect_id - constexpr uint32_t EFFECT_ON_TARGET = 42; // Skill effect hit a target (from effect_on_target packet) - // agent_id=caster, value=effect_id, target_id=target - // Python correlates this with recent casts to get skill_id + EFFECT_ON_TARGET = 42, // Skill effect hit a target (from effect_on_target packet) + // agent_id=caster, value=effect_id, target_id=target + // Python correlates this with recent casts to get skill_id // ---- Energy Events ---- - constexpr uint32_t ENERGY_GAINED = 50; // Energy gained (from GenericValue energygain) - // agent_id=agent, float_value=energy amount (raw points) + ENERGY_GAINED = 50, // Energy gained (from GenericValue energygain) + // agent_id=agent, float_value=energy amount (raw points) - constexpr uint32_t ENERGY_SPENT = 51; // Energy spent (from GenericFloat energy_spent) - // agent_id=agent, float_value=energy as fraction of max + ENERGY_SPENT = 51, // Energy spent (from GenericFloat energy_spent) + // agent_id=agent, float_value=energy as fraction of max // ---- Skill-Damage Attribution ---- - constexpr uint32_t SKILL_DAMAGE = 60; // Pre-notification: this skill will cause damage - // agent_id=target, value=skill_id - // Sent to TARGET before DAMAGE packet arrives + SKILL_DAMAGE = 60, // Pre-notification: this skill will cause damage + // agent_id=target, value=skill_id + // Sent to TARGET before DAMAGE packet arrives // ---- Pre-Notification ---- - constexpr uint32_t SKILL_ACTIVATE_PACKET = 70; // Early skill activation notification - // agent_id=caster, value=skill_id - // From SkillActivate packet (arrives before GenericValue) + SKILL_ACTIVATE_PACKET = 70, // Early skill activation notification + // agent_id=caster, value=skill_id + // From SkillActivate packet (arrives before GenericValue) // ---- Skill Recharge Events (from SkillRecharge/SkillRecharged packets) ---- // These track when skills go on cooldown and come off cooldown. // Works for ANY agent - player, heroes, enemies, NPCs! - constexpr uint32_t SKILL_RECHARGE = 80; // Skill went on cooldown - // agent_id=agent, value=skill_id, float_value=recharge time in ms + SKILL_RECHARGE = 80, // Skill went on cooldown + // agent_id=agent, value=skill_id, float_value=recharge time in ms - constexpr uint32_t SKILL_RECHARGED = 81; // Skill came off cooldown - // agent_id=agent, value=skill_id + SKILL_RECHARGED = 81 // Skill came off cooldown + // agent_id=agent, value=skill_id +}; + +// Helper function to convert enum to uint32_t for backwards compatibility +constexpr uint32_t to_uint(CombatEventType type) { + return static_cast(type); } // ============================================================================ @@ -312,6 +319,15 @@ class CombatEventQueue { * If queue exceeds max_events, oldest events are dropped. */ void PushEvent(const RawCombatEvent& event); + + /** + * @brief Check if the map is ready for packet processing. + * @return true if map is loaded and not in loading state. + * + * Prevents crashes during map transitions by skipping packet processing + * when game memory may be invalid. + */ + bool IsMapReady() const; }; // ============================================================================ diff --git a/src/py_combat_events.cpp b/src/py_combat_events.cpp index 1b16cae..c565c93 100644 --- a/src/py_combat_events.cpp +++ b/src/py_combat_events.cpp @@ -37,6 +37,37 @@ namespace py = pybind11; +// Backwards compatibility: create namespace with constexpr aliases to enum class values +// This allows existing code using CombatEventTypes::FOO to continue working +namespace CombatEventTypes { + constexpr uint32_t SKILL_ACTIVATED = to_uint(CombatEventType::SKILL_ACTIVATED); + constexpr uint32_t ATTACK_SKILL_ACTIVATED = to_uint(CombatEventType::ATTACK_SKILL_ACTIVATED); + constexpr uint32_t SKILL_STOPPED = to_uint(CombatEventType::SKILL_STOPPED); + constexpr uint32_t SKILL_FINISHED = to_uint(CombatEventType::SKILL_FINISHED); + constexpr uint32_t ATTACK_SKILL_FINISHED = to_uint(CombatEventType::ATTACK_SKILL_FINISHED); + constexpr uint32_t INTERRUPTED = to_uint(CombatEventType::INTERRUPTED); + constexpr uint32_t INSTANT_SKILL_ACTIVATED = to_uint(CombatEventType::INSTANT_SKILL_ACTIVATED); + constexpr uint32_t ATTACK_SKILL_STOPPED = to_uint(CombatEventType::ATTACK_SKILL_STOPPED); + constexpr uint32_t ATTACK_STARTED = to_uint(CombatEventType::ATTACK_STARTED); + constexpr uint32_t ATTACK_STOPPED = to_uint(CombatEventType::ATTACK_STOPPED); + constexpr uint32_t MELEE_ATTACK_FINISHED = to_uint(CombatEventType::MELEE_ATTACK_FINISHED); + constexpr uint32_t DISABLED = to_uint(CombatEventType::DISABLED); + constexpr uint32_t KNOCKED_DOWN = to_uint(CombatEventType::KNOCKED_DOWN); + constexpr uint32_t CASTTIME = to_uint(CombatEventType::CASTTIME); + constexpr uint32_t DAMAGE = to_uint(CombatEventType::DAMAGE); + constexpr uint32_t CRITICAL = to_uint(CombatEventType::CRITICAL); + constexpr uint32_t ARMOR_IGNORING = to_uint(CombatEventType::ARMOR_IGNORING); + constexpr uint32_t EFFECT_APPLIED = to_uint(CombatEventType::EFFECT_APPLIED); + constexpr uint32_t EFFECT_REMOVED = to_uint(CombatEventType::EFFECT_REMOVED); + constexpr uint32_t EFFECT_ON_TARGET = to_uint(CombatEventType::EFFECT_ON_TARGET); + constexpr uint32_t ENERGY_GAINED = to_uint(CombatEventType::ENERGY_GAINED); + constexpr uint32_t ENERGY_SPENT = to_uint(CombatEventType::ENERGY_SPENT); + constexpr uint32_t SKILL_DAMAGE = to_uint(CombatEventType::SKILL_DAMAGE); + constexpr uint32_t SKILL_ACTIVATE_PACKET = to_uint(CombatEventType::SKILL_ACTIVATE_PACKET); + constexpr uint32_t SKILL_RECHARGE = to_uint(CombatEventType::SKILL_RECHARGE); + constexpr uint32_t SKILL_RECHARGED = to_uint(CombatEventType::SKILL_RECHARGED); +} + // ============================================================================ // Lifecycle Implementation // ============================================================================ @@ -156,6 +187,12 @@ void CombatEventQueue::PushEvent(const RawCombatEvent& event) { } } +bool CombatEventQueue::IsMapReady() const { + auto instance_type = GW::Map::GetInstanceType(); + return GW::Map::GetIsMapLoaded() && + instance_type != GW::Constants::InstanceType::Loading; +} + // ============================================================================ // Packet Handlers // ============================================================================ @@ -170,7 +207,8 @@ void CombatEventQueue::PushEvent(const RawCombatEvent& event) { * with other packets that may not have the skill_id. */ void CombatEventQueue::OnSkillActivate(GW::Packet::StoC::SkillActivate* packet) { - uint32_t now = GetTickCount64 (); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); PushEvent(RawCombatEvent(now, CombatEventTypes::SKILL_ACTIVATE_PACKET, packet->agent_id, packet->skill_id, 0, 0.0f)); } @@ -191,7 +229,8 @@ void CombatEventQueue::OnSkillActivate(GW::Packet::StoC::SkillActivate* packet) * - energygain: Energy gained */ void CombatEventQueue::OnGenericValue(GW::Packet::StoC::GenericValue* packet) { - uint32_t now = GetTickCount64(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); using namespace GW::Packet::StoC::GenericValueID; @@ -293,7 +332,8 @@ void CombatEventQueue::OnGenericValue(GW::Packet::StoC::GenericValue* packet) { * - target_id = target/victim */ void CombatEventQueue::OnGenericValueTarget(GW::Packet::StoC::GenericValueTarget* packet) { - uint32_t now = GetTickCount64(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); using namespace GW::Packet::StoC::GenericValueID; @@ -339,7 +379,8 @@ void CombatEventQueue::OnGenericValueTarget(GW::Packet::StoC::GenericValueTarget * - energy_spent: Energy consumed, float_value = energy as fraction of max */ void CombatEventQueue::OnGenericFloat(GW::Packet::StoC::GenericFloat* packet) { - uint32_t now = GetTickCount64(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); using namespace GW::Packet::StoC::GenericValueID; @@ -380,7 +421,8 @@ void CombatEventQueue::OnGenericFloat(GW::Packet::StoC::GenericFloat* packet) { * Example: float_value = 0.15 on a target with 480 HP = 72 damage */ void CombatEventQueue::OnGenericModifier(GW::Packet::StoC::GenericModifier* packet) { - uint32_t now = GetTickCount64(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); using namespace GW::Packet::StoC::GenericValueID; @@ -427,7 +469,8 @@ void CombatEventQueue::OnGenericModifier(GW::Packet::StoC::GenericModifier* pack * - recharge: Cooldown duration in milliseconds */ void CombatEventQueue::OnSkillRecharge(GW::Packet::StoC::SkillRecharge* packet) { - uint32_t now = GetTickCount(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); // agent_id=who, value=skill_id, float_value=recharge_ms PushEvent(RawCombatEvent(now, CombatEventTypes::SKILL_RECHARGE, packet->agent_id, packet->skill_id, 0, static_cast(packet->recharge))); @@ -449,7 +492,8 @@ void CombatEventQueue::OnSkillRecharge(GW::Packet::StoC::SkillRecharge* packet) * - skill_id: The skill that is now ready to use */ void CombatEventQueue::OnSkillRecharged(GW::Packet::StoC::SkillRecharged* packet) { - uint32_t now = GetTickCount(); + if (!IsMapReady()) return; + uint32_t now = static_cast(GetTickCount64()); // agent_id=who, value=skill_id PushEvent(RawCombatEvent(now, CombatEventTypes::SKILL_RECHARGED, packet->agent_id, packet->skill_id, 0, 0.0f)); diff --git a/vendor/gwca/Source/MapMgr.cpp b/vendor/gwca/Source/MapMgr.cpp index 2dc3a79..1ccaaef 100644 --- a/vendor/gwca/Source/MapMgr.cpp +++ b/vendor/gwca/Source/MapMgr.cpp @@ -178,10 +178,9 @@ namespace { map_type_instance_infos_size = (*(uint32_t*)(address + 5)) / sizeof(MapTypeInstanceInfo); } - //commented address is from exe 28-nov-2025 + //WorldMap_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x83\xe8\x04\x83\xf8\x43", "xxxxxx")); //WorldMap_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x83\xe8\x04\x83\xf8\x50", "xxxxxx")); - //WorldMap: volatile switch case count (was 0x50, 0x4d, 0x52). - WorldMap_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x83\xe8\x04\x83\xf8\x52", "xxxxxx")); + WorldMap_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x83\xe8\x04\x83\xf8\x4d", "xxxxxx")); //MissionMap_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x81\xfb\x67\x01\x00\x10", "xxxxxx")); diff --git a/vendor/gwca/Source/PartyMgr.cpp b/vendor/gwca/Source/PartyMgr.cpp index a0fa6a0..30dc020 100644 --- a/vendor/gwca/Source/PartyMgr.cpp +++ b/vendor/gwca/Source/PartyMgr.cpp @@ -142,11 +142,9 @@ namespace { SetReadyStatus_Func = (DoAction_pt)Scanner::FunctionFromNearCall(address); Logger::AssertAddress("SetReadyStatus_Func", (uintptr_t)SetReadyStatus_Func, "Party Module"); + //address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x4e\x57", "xxxxxxxx"); //address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x5b\x57", "xxxxxxxx"); - //FlagAgent: volatile push immediate (was 0x5b, 0x5c, 0x5d). Wildcard it. - //address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x5d\x57", "xxxxxxxx"); - address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x00\x57", "xxxxxx?x"); - + address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x5c\x57", "xxxxxxxx"); Logger::AssertAddress("FlagAgent address", (uintptr_t)address, "Party Module"); if (Scanner::IsValidPtr(address, ScannerSection::Section_TEXT)) { //address = Scanner::FindInRange("\x83\xc4\x04\x50\xe8", "xxxxx", 4, address, address + 0x64); diff --git a/vendor/gwca/Source/QuestMgr.cpp b/vendor/gwca/Source/QuestMgr.cpp index eeb5982..665d3c7 100644 --- a/vendor/gwca/Source/QuestMgr.cpp +++ b/vendor/gwca/Source/QuestMgr.cpp @@ -66,9 +66,9 @@ namespace { address = Scanner::Find("\xC7\x45\xF8\x02\x00\x00\x00\x50\xC7", "xxxxxxxxx", +0x55); AbandonQuest_Func = (DoAction_pt)Scanner::FunctionFromNearCall(address); + //address = Scanner::Find("\x75\x14\x68\x5d\x10\x00\x00", "xxxxxxx"); //address = Scanner::Find("\x75\x14\x68\x72\x10\x00\x00", "xxxxxxx"); address = Scanner::Find("\x75\x14\x68\x64\x10\x00\x00", "xxxxxxx"); - request_quest_info_address = address; if (address) { address = Scanner::FindInRange("\xe8\x00\x00\x00\x00\x83\xc4\x08", "x????xxx", 0, address, address + 0xff); RequestQuestData_Func = (RequestQuestData_pt)Scanner::FunctionFromNearCall(address); @@ -79,7 +79,8 @@ namespace { //address = Scanner::Find("\x75\x14\x68\x4b\x10\x00\x00", "xxxxxxx"); - address = request_quest_info_address; // Scanner::Find("\x75\x14\x68\x64\x10\x00\x00", "xxxxxxx"); + //address = Scanner::Find("\x75\x14\x68\x60\x10\x00\x00", "xxxxxxx"); + address = Scanner::Find("\x75\x14\x68\x53\x10\x00\x00", "xxxxxxx"); if (address) address = Scanner::FindInRange("\x55\x8b\xec", "xxx", 0, address, address - 0xff); if (address) diff --git a/vendor/gwca/Source/UIMgr.cpp b/vendor/gwca/Source/UIMgr.cpp index a554990..95db811 100644 --- a/vendor/gwca/Source/UIMgr.cpp +++ b/vendor/gwca/Source/UIMgr.cpp @@ -468,9 +468,9 @@ namespace { WorldMapState_Addr = *(uintptr_t*)address; - //SendFrameUIMessageById: volatile switch case count (was 0x47, 0x55, 0x56). - //address = Scanner::Find("\x83\xfb\x47\x73\x14", "xxxxx", -0x34); //commented address on exe 28-nov-2025 - address = Scanner::Find("\x83\xfb\x56\x73\x14", "xxxxx", -0x34); + //address = Scanner::Find("\x83\xfb\x47\x73\x14", "xxxxx", -0x34); + //address = Scanner::Find("\x83\xfb\x54\x73\x14", "xxxxx", -0x34); + address = Scanner::Find("\x83\xfb\x55\x73\x14", "xxxxx", -0x34); if (address) { SendFrameUIMessageById_Func = (SendFrameUIMessageById_pt)address; SendFrameUIMessage_Func = (SendFrameUIMessage_pt)Scanner::FunctionFromNearCall(address + 0x67); @@ -487,6 +487,8 @@ namespace { // @TODO: Grab the relationship array from memory, write this ourselves! //address = Scanner::FindAssertion("\\Code\\Engine\\Controls\\CtlView.cpp", "pageId", 0, 0x16); + //address = Scanner::FindAssertion("\\Code\\Engine\\Controls\\CtlView.cpp", "pageId", 0, 0x14); + // Fixed: Offset is 0x19 (CALL to GetChildFrameId at +0x19 from CtlView.cpp assertion) address = Scanner::FindAssertion("\\Code\\Engine\\Controls\\CtlView.cpp", "pageId", 0, 0x19); GetChildFrameId_Func = (GetChildFrameId_pt)GW::Scanner::FunctionFromNearCall(address); @@ -519,7 +521,10 @@ namespace { PreferencesInitialised_Addr = *(uintptr_t*)address; + //address = GW::Scanner::Find("\x8d\x85\x78\xf7\xff\xff\x50", "xxxxxxx", 0x7); //address = GW::Scanner::Find("\x8d\x85\x74\xf7\xff\xff\x50", "xxxxxxx", 0x7); + // Fixed: New call site for BuildLoginStruct (call moved to different function after update) + // Pattern: lea eax, [ebp-0x47c]; push eax; call BuildLoginStruct address = GW::Scanner::Find("\x8d\x85\x84\xfb\xff\xff\x50\xe8", "xxxxxxxx", 0x7); address = GW::Scanner::FunctionFromNearCall(address); // BuildLoginStruct if (address) {