diff --git a/_work/data/Scripts/Content/GFA/_intern/collision.d b/_work/data/Scripts/Content/GFA/_intern/collision.d index 3c7cf63..f7cb0df 100644 --- a/_work/data/Scripts/Content/GFA/_intern/collision.d +++ b/_work/data/Scripts/Content/GFA/_intern/collision.d @@ -561,7 +561,8 @@ func void GFA_ExtendCollisionCheck() { }; // Ignore by refined collision check with NPCs - if (GFA_Flags & GFA_RANGED) && (hit) { + var C_Npc shooter; shooter = _^(MEM_ReadInt(arrowAI+oCAIArrow_origin_offset)); + if (GFA_Flags & GFA_RANGED) && (hit) && (Npc_IsPlayer(shooter)) { hit = GFA_RefinedProjectileCollisionCheck(vobPtr, arrowAI); }; } else if (GFA_Flags & GFA_CUSTOM_COLLISIONS) && (GFA_TRIGGER_COLL_FIX) && (GOTHIC_BASE_VERSION == 2) { @@ -583,3 +584,51 @@ func void GFA_ExtendCollisionCheck() { ECX = 0; }; }; + + +/* + * Enable extended collision check for NPCs also. This function rewrites the opcode of oCAIArrow::CanThisCollideWith() + * such that the ignore list check and the hook GFA_ExtendCollisionCheck() are also executed for NPCs. This fixes the + * trigger collision bug for NPCs. Only implemented for Gothic 2, as the trigger bug does not exist in Gothic 1. + */ +func void GFA_ExtendCollisionCheckNpc() { + if (IsHooked(oCAIArrow__CanThisCollideWith_positive)) || (GOTHIC_BASE_VERSION != 2) { + return; + }; + + // Skip checks for shooter and target + MemoryProtectionOverride(oCAIArrow__CanThisCollideWith_skipCheck, 2); + MEM_WriteByte(oCAIArrow__CanThisCollideWith_skipCheck, /*EB*/ 235); // jmp 0x6A14F0 + MEM_WriteByte(oCAIArrow__CanThisCollideWith_skipCheck+1, /*5A*/ 90); // 0x6A14F0-0x6A1494-2 + + // Re-add these checks later (after ignore list iteration and Daedalus hook) + ASM_Open(42); + // Check if extended detection returned zero + ASM_1(/*85*/ 133); ASM_1(/*C9*/ 201); // test ecx, ecx + ASM_1(/*74*/ 116); ASM_1(/*1D*/ 29); // jz .continue + // Check if shooter is player + ASM_1(/*51*/ 81); // push ecx + ASM_1(/*8B*/ 139); ASM_1(/*4F*/ 79); ASM_1(/*5C*/ 92); // mov ecx, [edi+0x5C] + ASM_1(/*8B*/ 139); ASM_1(/*01*/ 1); // mov eax, [ecx] + ASM_1(/*FF*/ 255); ASM_1(/*90*/ 144); ASM_4(/*100*/256); // call DWORD [eax+0x100] + ASM_1(/*59*/ 89); // pop ecx + ASM_1(/*85*/ 133); ASM_1(/*C0*/ 192); // test eax, eax + ASM_1(/*75*/ 117); ASM_1(/*0C*/ 12); // jnz .continue + // Check if arrow AI has target + ASM_1(/*8B*/ 139); ASM_1(/*47*/ 71); ASM_1(/*64*/ 100); // mov eax, [edi+0x64] + ASM_1(/*85*/ 133); ASM_1(/*C0*/ 192); // test eax, eax + ASM_1(/*74*/ 116); ASM_1(/*05*/ 5); // jz .continue + ASM_1(/*E9*/ 233); ASM_4(oCAIArrow__CanThisCollideWith_npcShooter-ASM_Here()-4); // jmp 0x6A14AA + // .continue: + ASM_1(/*5F*/ 95); // pop edi + ASM_1(/*5E*/ 94); // pop esi + ASM_1(/*8B*/ 139); ASM_1(/*C1*/ 193); // mov eax, ecx + ASM_1(/*C2*/ 194); ASM_1(/*04*/ 4); ASM_1(/*00*/ 0); // ret 4 + + // Need absolute jump here, because Daedalus hook later relocates this jump + MemoryProtectionOverride(oCAIArrow__CanThisCollideWith_positive, 7); + MEM_WriteByte(oCAIArrow__CanThisCollideWith_positive, /*B8*/ 184); // mov eax, newCheck + MEM_WriteInt(oCAIArrow__CanThisCollideWith_positive+1, ASM_Close()); + MEM_WriteByte(oCAIArrow__CanThisCollideWith_positive+5, /*FF*/ 255); // jmp eax + MEM_WriteByte(oCAIArrow__CanThisCollideWith_positive+6, /*E0*/ 224); +}; diff --git a/_work/data/Scripts/Content/GFA/_intern/init.d b/_work/data/Scripts/Content/GFA/_intern/init.d index 1d0a543..78292a6 100644 --- a/_work/data/Scripts/Content/GFA/_intern/init.d +++ b/_work/data/Scripts/Content/GFA/_intern/init.d @@ -55,6 +55,7 @@ func void GFA_InitFeatureFreeAiming() { HookEngineF(oCAIArrow__ReportCollisionToAI_hitChc, 6, GFA_OverwriteHitChance); // Manipulate hit chance MemoryProtectionOverride(oCAIHuman__CheckFocusVob_ranged, 1); // Prevent toggling focus in ranged combat HookEngineF(zCModel__CalcModelBBox3DWorld_rtn, 6, GFA_EnlargeHumanModelBBox); // Include head in model bbox + GFA_ExtendCollisionCheckNpc(); HookEngineF(oCAIArrow__CanThisCollideWith_positive, MEMINT_SwitchG1G2(6, 7), GFA_ExtendCollisionCheck); if (GFA_STRAFING) { HookEngineF(oCAIHuman__BowMode_rtn, 7, GFA_RangedLockMovement); // Allow strafing or not when falling @@ -221,6 +222,7 @@ func void GFA_InitFeatureCustomCollisions() { // Extend and refine collision detection on vobs if ((GFA_COLL_PRIOR_NPC == -1) || ((GFA_TRIGGER_COLL_FIX) && (GOTHIC_BASE_VERSION == 2))) { + GFA_ExtendCollisionCheckNpc(); HookEngineF(oCAIArrow__CanThisCollideWith_positive, MEMINT_SwitchG1G2(6, 7), GFA_ExtendCollisionCheck); }; }; diff --git a/_work/data/Scripts/Content/GFA/_intern/offsets_G1.d b/_work/data/Scripts/Content/GFA/_intern/offsets_G1.d index 4d522be..c57fb26 100644 --- a/_work/data/Scripts/Content/GFA/_intern/offsets_G1.d +++ b/_work/data/Scripts/Content/GFA/_intern/offsets_G1.d @@ -103,6 +103,8 @@ const int oCAIArrowBase__ReportCollisionToAI_PFXon2 = 0; const int oCAIArrowBase__ReportCollisionToAI_collNpc = 0; // Does not exist in Gothic 1 const int oCAIArrow__ReportCollisionToAI_destroyPrj = 6396025; //0x619879 const int oCAIArrow__ReportCollisionToAI_keepPlyStrp = 6395401; //0x619609 +const int oCAIArrow__CanThisCollideWith_skipCheck = 0; // Not used for Gothic 1 +const int oCAIArrow__CanThisCollideWith_npcShooter = 0; // Not used for Gothic 1 const int oCAIHuman__MagicMode_g2ctrlCheck = 0; // Does not exist in Gothic 1 const int oCAIHuman__BowMode_g2ctrlCheck = 0; // Does not exist in Gothic 1 const int oCAIHuman__BowMode_shootingKey = 6359374; //0x61094E // Not used for Gothic 1 diff --git a/_work/data/Scripts/Content/GFA/_intern/offsets_G2.d b/_work/data/Scripts/Content/GFA/_intern/offsets_G2.d index f827645..bf05fc0 100644 --- a/_work/data/Scripts/Content/GFA/_intern/offsets_G2.d +++ b/_work/data/Scripts/Content/GFA/_intern/offsets_G2.d @@ -103,6 +103,8 @@ const int oCAIArrowBase__ReportCollisionToAI_PFXon2 = 6949396; //0x6A0A14 const int oCAIArrowBase__ReportCollisionToAI_collNpc = 6949734; //0x6A0B66 const int oCAIArrow__ReportCollisionToAI_destroyPrj = 0; // Does not exist in Gothic 2 const int oCAIArrow__ReportCollisionToAI_keepPlyStrp = 0; // Does not exist in Gothic 2 +const int oCAIArrow__CanThisCollideWith_skipCheck = 6952084; //0x6A1494 +const int oCAIArrow__CanThisCollideWith_npcShooter = 6952106; //0x6A14AA const int oCAIHuman__MagicMode_g2ctrlCheck = 4665380; //0x473024 const int oCAIHuman__BowMode_g2ctrlCheck = 6905643; //0x695F2B const int oCAIHuman__BowMode_shootingKey = 6906610; //0x6962F2