From 8671540db34d55f7a3ce1fecea1b956e9750d949 Mon Sep 17 00:00:00 2001
From: Oliver Schieche <post@oliver-schieche.de>
Date: Sat, 4 Jan 2025 23:15:31 +0100
Subject: [PATCH] WBK Update 2.2

---
 addons/main/script_version.hpp                |  2 +-
 addons/zombies/include/functions.hpp          |  1 +
 addons/zombies/src/ai/fn_Zombies_AI_Melee.sqf | 29 ++---------
 .../zombies/src/ai/fn_Zombies_AI_Runner.sqf   | 32 +------------
 .../zombies/src/ai/fn_Zombies_AI_Shambler.sqf | 32 +------------
 .../zombies/src/ai/fn_Zombies_AI_Walker.sqf   | 28 +----------
 .../shared/fn_Zombies_AI_AddEventHandlers.sqf | 34 +++++++++++++
 .../fn_Zombies_AI_GetActionHandlerCode.sqf    | 12 ++---
 .../fn_Zombies_AI_GetLoopFindAndMoveCode.sqf  |  4 ++
 .../fn_Zombies_AI_GetLoopPathFindCode.sqf     | 14 ++++--
 .../src/ai/shared/fn_Zombies_AI_Prologue.sqf  | 48 +++++++++++++++++++
 addons/zombies/src/zombies.hpp                | 13 +----
 12 files changed, 114 insertions(+), 135 deletions(-)
 create mode 100644 addons/zombies/src/ai/shared/fn_Zombies_AI_Prologue.sqf

diff --git a/addons/main/script_version.hpp b/addons/main/script_version.hpp
index b3987ba6..dd391b3d 100644
--- a/addons/main/script_version.hpp
+++ b/addons/main/script_version.hpp
@@ -1,4 +1,4 @@
 #define MAJOR 1
 #define MINOR 11
 #define PATCH 1
-#define BUILD 1521
+#define BUILD 1523
diff --git a/addons/zombies/include/functions.hpp b/addons/zombies/include/functions.hpp
index df532d43..08f80d69 100644
--- a/addons/zombies/include/functions.hpp
+++ b/addons/zombies/include/functions.hpp
@@ -27,6 +27,7 @@ class Module_Zombies_AI_Shared {
 	class Zombies_AI_GetActionHandlerCode {};
 	class Zombies_AI_GetLoopPathFindCode {};
 	class Zombies_AI_GetLoopFindAndMoveCode {};
+	class Zombies_AI_Prologue {};
 };
 
 class Module_Zombies_Client {
diff --git a/addons/zombies/src/ai/fn_Zombies_AI_Melee.sqf b/addons/zombies/src/ai/fn_Zombies_AI_Melee.sqf
index 045c65d4..1cfe199e 100644
--- a/addons/zombies/src/ai/fn_Zombies_AI_Melee.sqf
+++ b/addons/zombies/src/ai/fn_Zombies_AI_Melee.sqf
@@ -11,19 +11,7 @@ if (getText(configOf _unit >> "moves") isNotEqualTo 'CfgMovesMaleSdr') then {
     throw "Incompatible skeleton; cannot apply AI";
 };
 
-if is3DEN exitWith {
-    _unit switchMove "WBK_Zombie_Melee_Idle";
-    _unit setFace selectRandom["WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"];
-    nil;
-};
-
-if (isPlayer _unit || !alive _unit || HAS_VARIABLE(_unit, "WBK_AI_ISZombie")) exitWith {};
-
-if (primaryWeapon _unit != "") then {
-    {
-        _unit removeWeapon _x;
-    } forEach [primaryWeapon _unit, handgunWeapon _unit, secondaryWeapon _unit];
-};
+if ([_unit, "WBK_Zombie_Melee_Idle"] call GM_FN(Zombies_AI_Prologue, SRC_Z_SHARED)) exitWith {};
 
 if !(handgunWeapon _unit in IMS_Melee_Weapons) then {
 	_unit removeWeapon handgunWeapon _unit;
@@ -35,23 +23,12 @@ private _weaponHolder = createVehicle["Weapon_Empty", [0, 0, 0], [], 0, "CAN_COL
 _weaponHolder addWeaponCargoGlobal[_weapon, 1];
 _weaponHolder attachTo [_unit,[0,0,0.07],"rightHand",true];
 
-_unit disableAI "MINEDETECTION";
-_unit disableAI "WEAPONAIM";
-_unit disableAI "SUPPRESSION";
-_unit disableAI "COVER";
-_unit disableAI "AIMINGERROR";
-_unit disableAI "TARGET";
-_unit disableAI "AUTOCOMBAT";
-_unit disableAI "FSM";
-
-_unit setUnitPos "UP";
+_unit setVariable["WBK_Zombie_WeaponHolders", [_weapon, _weaponHolder]];
 _unit setVariable["WBK_AI_Type", ZOMBIE_AI_TYPE_MELEE, true];
 _unit setVariable["WBK_AI_ISZombie",true,true];
 _unit setVariable["WBK_AI_ZombieMoveSet","WBK_Zombie_Melee_Idle",true];
 [_unit, "WBK_Zombie_Melee_Idle"] remoteExec ["switchMove",0];
 _unit setVariable["WBK_SynthHP",WBK_Zombies_MiddleHP,true];
-_unit setSpeaker "NoVoice";
-_unit disableConversation true;
 
 if (getText(configOf _unit >> "editorSubcategory") in ["WBK_Zombies_WW2_US","WBK_Zombies_WW2_RKKA","WBK_Zombies_WW2_German","LIB_WEHRMACHT","LIB_US_ARMY","LIB_RKKA"]) then {
     _unit setVariable ["WBK_Zombie_CustomSounds", [
@@ -75,7 +52,7 @@ if !(isNil "WBK_IsPresent_PIR") then {
 [_unit, selectRandom ["WBK_ZombieFace_blood_1","WBK_ZombieFace_blood_2","WBK_ZombieFace_blood_3","WBK_ZombieFace_blood_4","WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"]] remoteExec ["setFace", 0];
 _unit spawn {
     uiSleep 0.5;
-    _this doMove getPos _this;
+    _this doMove getPosATLVisual _this;
 };
 
 _unit setVariable["WBK_HitPartEH_MoveSetParams", createHashMapFromArray[
diff --git a/addons/zombies/src/ai/fn_Zombies_AI_Runner.sqf b/addons/zombies/src/ai/fn_Zombies_AI_Runner.sqf
index abb9c38d..38580219 100644
--- a/addons/zombies/src/ai/fn_Zombies_AI_Runner.sqf
+++ b/addons/zombies/src/ai/fn_Zombies_AI_Runner.sqf
@@ -7,36 +7,8 @@
 params[["_unit",objNull,[objNull]], ["_isCalm",true,[true]], ["_isCorrupted",false,[true]]];
 if !assert(!isNull _unit) exitWith {};
 
-if (getText(configOf _unit >> "moves") isNotEqualTo 'CfgMovesMaleSdr') then {
-    throw "Incompatible skeleton; cannot apply AI";
-};
-
-if is3DEN exitWith {
-    _unit switchMove(["WBK_Runner_Angry_Idle", "WBK_Runner_Calm_Idle"] select _isCalm);
-    _unit setFace selectRandom["WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"];
-    nil;
-};
-
-if (isPlayer _unit || !alive _unit || HAS_VARIABLE(_unit, "WBK_AI_ISZombie")) exitWith {};
-
-if (primaryWeapon _unit != "") then {
-	{
-		_unit removeWeapon _x;
-	} forEach [primaryWeapon _unit, handgunWeapon _unit, secondaryWeapon _unit];
-};
+if ([_unit, ["WBK_Runner_Angry_Idle", "WBK_Runner_Calm_Idle"] select _isCalm] call GM_FN(Zombies_AI_Prologue, SRC_Z_SHARED)) exitWith {};
 
-_unit disableAI "MINEDETECTION";
-_unit disableAI "WEAPONAIM";
-_unit disableAI "SUPPRESSION";
-_unit disableAI "COVER";
-_unit disableAI "AIMINGERROR";
-_unit disableAI "TARGET";
-_unit disableAI "AUTOCOMBAT";
-_unit disableAI "FSM";
-
-_unit setSpeaker "NoVoice";
-_unit disableConversation true;
-_unit setUnitPos "UP";
 _unit setVariable["WBK_AI_Type", ZOMBIE_AI_TYPE_RUNNER, true];
 _unit setVariable["WBK_AI_ISZombie", true, true];
 _unit setVariable["WBK_AI_ZombieMoveSet","WBK_Runner_Angry_Idle", true];
@@ -90,7 +62,7 @@ if !(isNil "WBK_IsPresent_PIR") then {
 
 _unit spawn {
 	uiSleep 0.5;
-	_this doMove (getPos _this);
+    _this doMove getPosATLVisual _this;
 };
 
 _unit setVariable["WBK_Zombie_ClassSounds", [
diff --git a/addons/zombies/src/ai/fn_Zombies_AI_Shambler.sqf b/addons/zombies/src/ai/fn_Zombies_AI_Shambler.sqf
index 7c1e4a1f..2929dccd 100644
--- a/addons/zombies/src/ai/fn_Zombies_AI_Shambler.sqf
+++ b/addons/zombies/src/ai/fn_Zombies_AI_Shambler.sqf
@@ -7,42 +7,14 @@
 if !assert(params[["_unit",objNull,[objNull]]]) exitWith {};
 if !assert(!isNull _unit) exitWith {};
 
-if (getText(configOf _unit >> "moves") isNotEqualTo 'CfgMovesMaleSdr') then {
-    throw "Incompatible skeleton; cannot apply AI";
-};
-
-if is3DEN exitWith {
-    _unit switchMove selectRandom["WBK_Middle_Idle_1","WBK_Middle_Idle"];
-    _unit setFace selectRandom["WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"];
-    nil;
-};
-
-if (isPlayer _unit || !alive _unit || HAS_VARIABLE(_unit, "WBK_AI_ISZombie")) exitWith {};
-
-if (primaryWeapon _unit != "") then {
-    {
-        _unit removeWeapon _x;
-    } forEach [primaryWeapon _unit, handgunWeapon _unit, secondaryWeapon _unit];
-};
-
-_unit disableAI "MINEDETECTION";
-_unit disableAI "WEAPONAIM";
-_unit disableAI "SUPPRESSION";
-_unit disableAI "COVER";
-_unit disableAI "AIMINGERROR";
-_unit disableAI "TARGET";
-_unit disableAI "AUTOCOMBAT";
-_unit disableAI "FSM";
+if ([_unit, ["WBK_Middle_Idle_1","WBK_Middle_Idle"]] call GM_FN(Zombies_AI_Prologue, SRC_Z_SHARED)) exitWith {};
 
-_unit setUnitPos "UP";
 _unit setVariable["WBK_AI_Type", ZOMBIE_AI_TYPE_SHAMBLER, true];
 _unit setVariable["WBK_AI_ISZombie",true,true];
 private _rndMoveset = selectRandom ["WBK_Middle_Idle","WBK_Middle_Idle_1"];
 _unit setVariable["WBK_AI_ZombieMoveSet",_rndMoveset,true];
 [_unit, _rndMoveset] remoteExec ["switchMove",0];
 _unit setVariable["WBK_SynthHP",WBK_Zombies_MiddleHP,true];
-_unit setSpeaker "NoVoice";
-_unit disableConversation true;
 
 if (getText(configOf _unit >> "editorSubcategory") in ["WBK_Zombies_WW2_US","WBK_Zombies_WW2_RKKA","WBK_Zombies_WW2_German","LIB_WEHRMACHT","LIB_US_ARMY","LIB_RKKA"]) then {
     _unit setVariable ["WBK_Zombie_CustomSounds", [
@@ -66,7 +38,7 @@ if !(isNil "WBK_IsPresent_PIR") then {
 [_unit, selectRandom ["WBK_ZombieFace_blood_1","WBK_ZombieFace_blood_2","WBK_ZombieFace_blood_3","WBK_ZombieFace_blood_4","WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"]] remoteExec ["setFace", 0];
 _unit spawn {
     uiSleep 0.5;
-    _this doMove getPos _this;
+    _this doMove getPosATLVisual _this;
 };
 
 _unit setVariable["WBK_HitPartEH_MoveSetParams", createHashMapFromArray[
diff --git a/addons/zombies/src/ai/fn_Zombies_AI_Walker.sqf b/addons/zombies/src/ai/fn_Zombies_AI_Walker.sqf
index b1f92610..3ebb21c4 100644
--- a/addons/zombies/src/ai/fn_Zombies_AI_Walker.sqf
+++ b/addons/zombies/src/ai/fn_Zombies_AI_Walker.sqf
@@ -11,30 +11,8 @@ if (getText(configOf _unit >> "moves") isNotEqualTo 'CfgMovesMaleSdr') then {
     throw "Incompatible skeleton; cannot apply AI";
 };
 
-if is3DEN exitWith {
-    _unit switchMove([selectRandom["WBK_Walker_Idle_1","WBK_Walker_Idle_2","WBK_Walker_Idle_3"], "WBK_Crawler_Idle"] select _isCrawler);
-    _unit setFace selectRandom["WBK_ZombieFace_blood_1","WBK_ZombieFace_blood_2","WBK_ZombieFace_blood_3","WBK_ZombieFace_blood_4","WBK_DosHead_Normal_1","WBK_DosHead_Normal_2","WBK_DosHead_Normal_3"];
-    nil;
-};
-
-if (isPlayer _unit || !alive _unit || HAS_VARIABLE(_unit, "WBK_AI_ISZombie")) exitWith {};
-
-if (primaryWeapon _unit != "") then {
-	{
-		_unit removeWeapon _x;
-	} forEach [primaryWeapon _unit, handgunWeapon _unit, secondaryWeapon _unit];
-};
-
-_unit disableAI "MINEDETECTION";
-_unit disableAI "WEAPONAIM";
-_unit disableAI "SUPPRESSION";
-_unit disableAI "COVER";
-_unit disableAI "AIMINGERROR";
-_unit disableAI "TARGET";
-_unit disableAI "AUTOCOMBAT";
-_unit disableAI "FSM";
+if ([_unit, [selectRandom["WBK_Walker_Idle_1","WBK_Walker_Idle_2","WBK_Walker_Idle_3"], "WBK_Crawler_Idle"] select _isCrawler] call GM_FN(Zombies_AI_Prologue, SRC_Z_SHARED)) exitWith {};
 
-_unit setUnitPos "UP";
 _unit setVariable["WBK_AI_Type", ZOMBIE_AI_TYPE_WALKER, true];
 _unit setVariable["WBK_AI_ISZombie", true, true];
 
@@ -47,8 +25,6 @@ if _isCrawler then {
 	[_unit, _rndMoveset] remoteExec["switchMove", REMOTE_EXEC_GLOBAL];
 };
 _unit setVariable ["WBK_SynthHP",WBK_Zombies_WalkerHP,true];
-_unit setSpeaker "NoVoice";
-_unit disableConversation true;
 
 if (getText(configOf _unit >> "editorSubcategory") in ["WBK_Zombies_WW2_US","WBK_Zombies_WW2_RKKA","WBK_Zombies_WW2_German","LIB_WEHRMACHT","LIB_US_ARMY","LIB_RKKA"]) then {
 	_unit setVariable ["WBK_Zombie_CustomSounds",
@@ -73,7 +49,7 @@ _unit spawn {
 	private _rndFace = selectRandom ["WBK_ZombieFace_blood_1","WBK_ZombieFace_blood_2","WBK_ZombieFace_blood_3","WBK_ZombieFace_blood_4","WBK_DosHead_Normal_1","WBK_DosHead_Normal_2","WBK_DosHead_Normal_3"];
 	[_this, _rndFace] remoteExec["setFace", REMOTE_EXEC_GLOBAL];
 	uiSleep 0.5;
-	_this doMove getPos _this;
+    _this doMove getPosATLVisual _this;
 	if (_rndFace in ["WBK_DosHead_Normal_1","WBK_DosHead_Normal_2","WBK_DosHead_Normal_3"]) then {
 		removeHeadgear _this;
 		removeGoggles _this;
diff --git a/addons/zombies/src/ai/shared/fn_Zombies_AI_AddEventHandlers.sqf b/addons/zombies/src/ai/shared/fn_Zombies_AI_AddEventHandlers.sqf
index d5052fd1..c92893c2 100644
--- a/addons/zombies/src/ai/shared/fn_Zombies_AI_AddEventHandlers.sqf
+++ b/addons/zombies/src/ai/shared/fn_Zombies_AI_AddEventHandlers.sqf
@@ -7,6 +7,23 @@
 if !assert(params[["_zombie",objNull,[objNull]], ["_handlers",false,[createHashMap]]]) exitWith {};
 if !assert(!isNull _zombie) exitWith {};
 
+_zombie setVariable["WBK_AI_CleanupCode", {
+    params["_zombie"];
+
+    _zombie getVariable["WBK_AI_AttachedHandlers", []] apply {
+        [_x] call CBA_fnc_removePerFrameHandler;
+    };
+
+    [_zombie, "HitPart"] remoteExec["removeAllEventHandlers", -REMOTE_EXEC_SERVER];
+    {
+        _zombie removeEventHandler[_x, _y];
+    } forEach (_zombie getVariable["WBK_AI_EventHandlers", createHashMap]);
+
+    if HAS_VARIABLE(_zombie, "WBK_Zombie_WeaponHolders") then {
+	    deleteVehicle((_zombie getVariable "WBK_Zombie_WeaponHolders") select 1);
+    };
+}];
+
 _handlers set["AnimStateChanged", _zombie addEventHandler["AnimStateChanged", {
     _this spawn {
         params ["_unit", "_anim"];
@@ -102,6 +119,13 @@ _handlers set["AnimStateChanged", _zombie addEventHandler["AnimStateChanged", {
     };
 }]];
 
+_handlers set["Deleted", _zombie addEventHandler["Deleted", {
+    params["_zombie"];
+    if HAS_VARIABLE(_zombie, "WBK_AI_CleanupCode") then {
+        [_zombie] call (_zombie getVariable "WBK_AI_CleanupCode");
+    };
+}]];
+
 _handlers set["FiredNear", _zombie addEventHandler["FiredNear", {
     if (_this call GM_FN(Zombies_FiredNearCheck, SRC_Z_ROOT)) then {
         params["_unit","_firer","_distance"];
@@ -114,6 +138,14 @@ _handlers set["Killed", _zombie addEventHandler["Killed", {
     _this spawn {
         params["_zombie","_killer"];
 
+        if HAS_VARIABLE(_zombie, "WBK_AI_CleanupCode") then {
+            [_zombie] call (_zombie getVariable "WBK_AI_CleanupCode");
+        };
+
+        if HAS_VARIABLE(_zombie, "WBK_Zombie_WeaponHolders") then {
+	        _zombie addWeapon((_zombie getVariable "WBK_Zombie_WeaponHolders") select 0);
+        };
+
         _zombie spawn {
             uiSleep (0.3 + random 0.1);
             if !isNull _this then {
@@ -133,4 +165,6 @@ _handlers set["Killed", _zombie addEventHandler["Killed", {
     };
 }]];
 
+_zombie setVariable["WBK_AI_EventHandlers", _handlers];
+
 nil;
diff --git a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetActionHandlerCode.sqf b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetActionHandlerCode.sqf
index f18cc645..5edfb324 100644
--- a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetActionHandlerCode.sqf
+++ b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetActionHandlerCode.sqf
@@ -39,22 +39,22 @@
         };
     };
 
-    if !HAS_LOS(_unit,_en) exitWith {};
+    //if !HAS_LOS(_unit,_en) exitWith {};
+    if HAS_VARIABLE(_unit, "WBK_IsUnitLocked") exitWith {};
 
     switch true do {
         // Shamblers, Walkers
-        case ((_type in [ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 1.2) && (_ms isEqualTo "WBK_Walker_Idle_1"));
-        case ((_type in [ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 1.2) && (_ms isEqualTo "WBK_Walker_Idle_3")): {
+        case ((_type in [ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 2) && (_ms in ["WBK_Walker_Idle_1", "WBK_Walker_Idle_3"])): {
             [_unit, ["WBK_Walker_Idle_1_attack", 0, 0.2, false]] remoteExec["switchMove", REMOTE_EXEC_GLOBAL];
             [_unit, "WBK_Walker_Idle_1"] remoteExec["playMove", REMOTE_EXEC_GLOBAL];
             ZOMBIE_PLAY_SOUND(_unit, 2, 55);
         };
-        case ((_type in [ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 1.2) && (_ms isEqualTo "WBK_Walker_Idle_2")): {
+        case ((_type in [ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 2) && (_ms isEqualTo "WBK_Walker_Idle_2")): {
             [_unit, ["WBK_Walker_Idle_2_attack", 0, 0.2, false]] remoteExec["switchMove", REMOTE_EXEC_GLOBAL];
             [_unit, "WBK_Walker_Idle_2"] remoteExec["playMove", REMOTE_EXEC_GLOBAL];
             ZOMBIE_PLAY_SOUND(_unit, 2, 55);
         };
-        case ((_type in [ZOMBIE_AI_TYPE_RUNNER,ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 1.2) && (_ms isEqualTo "WBK_Crawler_Idle")): {
+        case ((_type in [ZOMBIE_AI_TYPE_RUNNER,ZOMBIE_AI_TYPE_SHAMBLER,ZOMBIE_AI_TYPE_WALKER]) && (_distance <= 2) && (_ms isEqualTo "WBK_Crawler_Idle")): {
 			[_unit, ["WBK_Crawler_Attack", 0, 0.2, false]] remoteExec["switchMove", REMOTE_EXEC_GLOBAL];
             [_unit, "WBK_Crawler_Idle"] remoteExec["playMove", REMOTE_EXEC_GLOBAL];
             ZOMBIE_PLAY_SOUND(_unit, 2, 55);
@@ -71,7 +71,7 @@
                 [_unit, "WBK_Runner_Angry_Idle"] remoteExec["playMove", 0];
                 ZOMBIE_PLAY_SOUND(_unit, 2, 55);
             };
-        case ((_type isEqualTo ZOMBIE_AI_TYPE_RUNNER) && ((_distance <= 1.5) && (_ms isNotEqualTo "WBK_Crawler_Idle")) && ((lifeState _en == "INCAPACITATED" || stance _en == "PRONE"))): {
+        case ((_type isEqualTo ZOMBIE_AI_TYPE_RUNNER) && ((_distance <= 2) && (_ms isNotEqualTo "WBK_Crawler_Idle")) && ((lifeState _en == "INCAPACITATED" || stance _en == "PRONE"))): {
 			[_unit, ["WBK_Runner_Attack_Unconscious", 0, 0.2, false]] remoteExec ["switchMove", 0];
 			[_unit, "WBK_Runner_Angry_Sprint"] remoteExec ["playMove", 0];
             ZOMBIE_PLAY_SOUND(_unit, 2, 55);
diff --git a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopFindAndMoveCode.sqf b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopFindAndMoveCode.sqf
index 886c8c90..134bda6d 100644
--- a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopFindAndMoveCode.sqf
+++ b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopFindAndMoveCode.sqf
@@ -8,6 +8,10 @@
     params["_arguments"];
     _arguments params["_unit",["_defaultSoundSet","WBK_Zombie_ClassSounds",[""]]];
 
+    if HAS_VARIABLE(_unit, "WBK_IsUnitLocked") exitWith {
+        [_unit, selectRandom(_sounds select 1), 25] call CBA_fnc_GlobalSay3D;
+    };
+
 	_unit enableAI "MOVE";
 	_unit enableAI "ANIM";
 
diff --git a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopPathFindCode.sqf b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopPathFindCode.sqf
index 00ab1ab6..1afb5fab 100644
--- a/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopPathFindCode.sqf
+++ b/addons/zombies/src/ai/shared/fn_Zombies_AI_GetLoopPathFindCode.sqf
@@ -11,7 +11,7 @@
     // WTF
 	private _isStriderTaked = missionNamespace getVariable["bis_fnc_moduleRemoteControl_unit", player];
 
-	if (!(simulationEnabled _unit) || (_unit == _isStriderTaked) || !(alive _unit) || !(isNull attachedTo _unit) || (lifeState _unit == "INCAPACITATED")) exitWith {
+	if (!(simulationEnabled _unit) || !(isNull remoteControlled _unit) || !(alive _unit) || !(isNull attachedTo _unit) || (lifeState _unit == "INCAPACITATED")) exitWith {
 		_unit setVariable ["WBK_IsUnitLocked",nil];
 	};
 
@@ -87,11 +87,15 @@
             _dir,
             vectorUp _unit,
             vectorUp _unit,
-            0.1
+            0.5
         ];
     };
 
-    _unit setVariable ["WBK_IsUnitLocked",nil];
-    _unit enableAI "ANIM";
-    _unit enableAI "MOVE";
+    if HAS_VARIABLE(_unit, "WBK_IsUnitLocked") then {
+        _unit setVariable["WBK_IsUnitLocked",nil];
+        _unit enableAI "ANIM";
+        _unit enableAI "MOVE";
+        _unit doMove getPosATLVisual _nearEnemy;
+        _unit setVariable["WBK_AI_LastKnownLoc", getPosATLVisual _nearEnemy];
+    };
 };
diff --git a/addons/zombies/src/ai/shared/fn_Zombies_AI_Prologue.sqf b/addons/zombies/src/ai/shared/fn_Zombies_AI_Prologue.sqf
new file mode 100644
index 00000000..803e2036
--- /dev/null
+++ b/addons/zombies/src/ai/shared/fn_Zombies_AI_Prologue.sqf
@@ -0,0 +1,48 @@
+/**
+ * GHOTI_fnc_Zombies_AI_Prologue(Object unit)
+ */
+#pragma hemtt suppress pw3_padded_arg file
+#include "..\..\zombies.hpp"
+
+if !assert(params[["_unit",objNull,[objNull]], ["_edenSwitchMoves",[],["",[]]]]) exitWith {};
+if !assert(!isNull _unit) exitWith {};
+
+if (getText(configOf _unit >> "moves") isNotEqualTo 'CfgMovesMaleSdr') then {
+    throw "Incompatible skeleton; cannot apply AI";
+};
+
+if is3DEN exitWith {
+    if (_edenSwitchMoves isEqualType "") then {
+        _unit switchMove _edenSwitchMoves;
+    } else {
+        _unit switchMove selectRandom _edenSwitchMoves;
+    };
+    _unit setFace selectRandom["WBK_ZombieFace_1","WBK_ZombieFace_2","WBK_ZombieFace_3","WBK_ZombieFace_4","WBK_ZombieFace_5","WBK_ZombieFace_6"];
+    true;
+};
+
+if (isPlayer _unit || !alive _unit || HAS_VARIABLE(_unit, "WBK_AI_ISZombie")) exitWith {};
+
+if (primaryWeapon _unit != "") then {
+    {
+        _unit removeWeapon _x;
+    } forEach [primaryWeapon _unit, handgunWeapon _unit, secondaryWeapon _unit];
+};
+
+_unit disableAI "MINEDETECTION";
+_unit disableAI "WEAPONAIM";
+_unit disableAI "SUPPRESSION";
+_unit disableAI "COVER";
+_unit disableAI "AIMINGERROR";
+_unit disableAI "TARGET";
+_unit disableAI "AUTOCOMBAT";
+_unit disableAI "FSM";
+
+_unit setSpeaker "NoVoice";
+_unit disableConversation true;
+_unit setUnitPos "UP";
+
+_unit setCombatMode "BLUE";
+_unit enableAttack false;
+
+false;
diff --git a/addons/zombies/src/zombies.hpp b/addons/zombies/src/zombies.hpp
index e1306237..125cab0a 100644
--- a/addons/zombies/src/zombies.hpp
+++ b/addons/zombies/src/zombies.hpp
@@ -7,17 +7,7 @@
 #define NS_AI_CODE_CACHE "GM_Zombies_AI_Code"
 #define NS_ALL_ZOMBIES "GM_AllZombies"
 #define ZOMBIE_AI_CODE_EPILOGUE if true then {\
-    _unit setVariable["WBK_Zombie_UnitReady", true];\
-    waitUntil {\
-        sleep 0.5;\
-        isNull _unit || !alive _unit || !HAS_VARIABLE(_unit, "WBK_AI_ISZombie")\
-    };\
-    \
-    [_actFr] call CBA_fnc_removePerFrameHandler;\
-    [_loopPathfind] call CBA_fnc_removePerFrameHandler;\
-    [_loopPathfindDoMove] call CBA_fnc_removePerFrameHandler;\
-    [_unit, "HitPart"] remoteExec["removeAllEventHandlers", -REMOTE_EXEC_SERVER];\
-    { _unit removeEventHandler[_x, _y] } forEach _handlers;\
+    _unit setVariable["WBK_AI_AttachedHandlers", [_actFr, _loopPathfindDoMove, _loopPathfind]];\
 }
 #define ZOMBIE_AI_TYPE_MELEE 0
 #define ZOMBIE_AI_TYPE_RUNNER 1
@@ -27,3 +17,4 @@
     private _sounds = (Z) getVariable(["WBK_Zombie_ClassSounds","WBK_Zombie_CustomSounds"] select parseNumber(HAS_VARIABLE((Z), "WBK_Zombie_CustomSounds")));\
     [_unit, selectRandom(_sounds select (INDEX)), (RADIUS)] call CBA_fnc_GlobalSay3D;\
 }
+ 
\ No newline at end of file
-- 
GitLab