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/AgentMgr.cpp b/vendor/gwca/Source/AgentMgr.cpp index f375d7c..db5aff0 100644 --- a/vendor/gwca/Source/AgentMgr.cpp +++ b/vendor/gwca/Source/AgentMgr.cpp @@ -185,8 +185,13 @@ namespace { int success = HookBase::CreateHook((void**)&DoWorldActon_Func, OnDoWorldActon_Func, (void**)&DoWorldActon_Ret); Logger::AssertHook("DoWorldActon_Func", success); - success = HookBase::CreateHook((void**)&CallTarget_Func, OnCallTarget_Func, (void**)&CallTarget_Ret); - Logger::AssertHook("CallTarget_Func", success); + // NOTE: CallTarget_Func initialization is currently missing - hook is disabled + // The CallTarget functionality still works through UI messages (kSendCallTarget) + // TODO: Add Scanner::Find for CallTarget_Func if direct hooking is needed + if (CallTarget_Func) { + success = HookBase::CreateHook((void**)&CallTarget_Func, OnCallTarget_Func, (void**)&CallTarget_Ret); + Logger::AssertHook("CallTarget_Func", success); + } success = HookBase::CreateHook((void**)&SendAgentDialog_Func, OnSendAgentDialog_Func, (void**)&SendAgentDialog_Ret); Logger::AssertHook("SendAgentDialog_Func", success); @@ -217,7 +222,7 @@ namespace { void EnableHooks() { //return; // Temporarily disable gamethread hooks to investigate issues if (CallTarget_Func) - HookBase::EnableHooks(DoWorldActon_Func); + HookBase::EnableHooks(CallTarget_Func); if (DoWorldActon_Func) HookBase::EnableHooks(DoWorldActon_Func); if (SendAgentDialog_Func) @@ -231,7 +236,7 @@ namespace { } void DisableHooks() { if (CallTarget_Func) - HookBase::DisableHooks(DoWorldActon_Func); + HookBase::DisableHooks(CallTarget_Func); if (DoWorldActon_Func) HookBase::DisableHooks(DoWorldActon_Func); if (SendAgentDialog_Func) @@ -243,7 +248,7 @@ namespace { } void Exit() { if (CallTarget_Func) - HookBase::RemoveHook(DoWorldActon_Func); + HookBase::RemoveHook(CallTarget_Func); if (DoWorldActon_Func) HookBase::RemoveHook(DoWorldActon_Func); if (SendAgentDialog_Func) diff --git a/vendor/gwca/Source/MapMgr.cpp b/vendor/gwca/Source/MapMgr.cpp index 1635130..12f9c69 100644 --- a/vendor/gwca/Source/MapMgr.cpp +++ b/vendor/gwca/Source/MapMgr.cpp @@ -170,7 +170,8 @@ namespace { } //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_UICallback_Func = (UI::UIInteractionCallback)GW::Scanner::ToFunctionStart(GW::Scanner::Find("\x83\xe8\x04\x83\xf8\x50", "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 5f441b5..30dc020 100644 --- a/vendor/gwca/Source/PartyMgr.cpp +++ b/vendor/gwca/Source/PartyMgr.cpp @@ -143,7 +143,8 @@ namespace { 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"); + //address = Scanner::Find("\x8d\x45\x10\x50\x56\x6a\x5b\x57", "xxxxxxxx"); + 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 1cc291f..b041727 100644 --- a/vendor/gwca/Source/QuestMgr.cpp +++ b/vendor/gwca/Source/QuestMgr.cpp @@ -66,7 +66,8 @@ namespace { 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\x72\x10\x00\x00", "xxxxxxx"); + address = Scanner::Find("\x75\x14\x68\x64\x10\x00\x00", "xxxxxxx"); 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); @@ -77,7 +78,8 @@ namespace { //address = Scanner::Find("\x75\x14\x68\x4b\x10\x00\x00", "xxxxxxx"); - address = Scanner::Find("\x75\x14\x68\x60\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 3278e81..4bd291e 100644 --- a/vendor/gwca/Source/UIMgr.cpp +++ b/vendor/gwca/Source/UIMgr.cpp @@ -450,7 +450,8 @@ namespace { //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\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); @@ -465,8 +466,10 @@ namespace { // @TODO: Grab the relationship array from memory, write this ourselves! - //address = Scanner::FindAssertion("\\Code\\Engine\\Controls\\CtlView.cpp", "pageId", 0, 0x19); - address = Scanner::FindAssertion("\\Code\\Engine\\Controls\\CtlView.cpp", "pageId", 0, 0x16); + //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); @@ -499,7 +502,10 @@ namespace { //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); + //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) { GetCommandLineFlag_Func = (GetFlagPreference_pt)GW::Scanner::FunctionFromNearCall(address + 0xf);