diff -ur sourceold/scripts/AudibleSoundDefinitionClass.h source/scripts/AudibleSoundDefinitionClass.h
--- sourceold/scripts/AudibleSoundDefinitionClass.h	2018-11-17 10:23:56.957600000 +1000
+++ source/scripts/AudibleSoundDefinitionClass.h	2019-06-22 07:43:44.219400000 +1000
@@ -55,6 +55,8 @@
 	virtual void Set_Pitch_Factor_Randomizer(float f) {m_PitchFactorRandomizer = f;}
 	virtual void Set_Virtual_Channel(int i) {m_VirtualChannel = i;}
 	virtual LogicalSoundClass* Create_Logical() const;
+	float Get_Logical_DropOff_Radius() const {return m_LogicalDropoffRadius;}
+	int Get_Logical_Type() const {return m_LogicalType;}
 	AudibleSoundDefinitionClass();
 	bool Save_Variables(ChunkSaveClass&);
 	bool Load_Variables(ChunkLoadClass&);
diff -ur sourceold/scripts/MasterCan_Scripts.cpp source/scripts/MasterCan_Scripts.cpp
--- sourceold/scripts/MasterCan_Scripts.cpp	2019-03-31 08:17:14.582000000 +1000
+++ source/scripts/MasterCan_Scripts.cpp	2019-06-22 07:43:44.221400000 +1000
@@ -1,9 +1,9 @@
-//MasterCan's Scripts for Public TT Release - v1
+//MasterCan's Scripts for Public TT Release - v3
 //Made by The Unstoppable (MasterCan)
-//This version includes 14 scripts.
+//This version includes 40 scripts.
 
-//14 Working
-//0 Buggy
+//39 Working
+//1 Buggy
 //0 Not Working
 //0 Not Tested
 
@@ -24,7 +24,9 @@
 #include "SoldierGameObj.h"
 #include "WeaponBagClass.h"
 #include "VehicleGameObj.h"
+#include "SimpleGameObj.h"
 #include "BuildingGameObj.h"
+#include "PowerupGameObj.h"
 #include "cPlayer.h"
 #include <string>
 #include <vector>
@@ -40,15 +42,12 @@
 		//TeleportSound: The sound that will be played after teleportation done.
 		//Changelog: 1.0 - First Release
 		//           1.1 - Gonna Make This Two-Way Teleportation
-		if (Commands->Find_Object(Get_Int_Parameter("BoxID")))
+		if (enterer->As_SoldierGameObj() || enterer->As_VehicleGameObj())
 		{
-			if (enterer->As_SoldierGameObj() || enterer->As_VehicleGameObj())
+			Commands->Set_Position(enterer, Commands->Get_Position(Commands->Find_Object(Get_Int_Parameter("ArrowID"))));
+			if (Get_Parameter("TeleportSound"))
 			{
-				Commands->Set_Position(enterer, Commands->Get_Position(Commands->Find_Object(Get_Int_Parameter("ArrowID"))));
-				if (Get_Parameter("TeleportSound"))
-				{
-					Create_2D_WAV_Sound_Player(enterer, Get_Parameter("TeleportSound"));
-				}
+				Create_2D_WAV_Sound_Player(enterer, Get_Parameter("TeleportSound"));
 			}
 		}
 	}
@@ -503,6 +502,7 @@
 	{
 		Vector3 Pos = Commands->Get_Position(obj);
 		Create_Explosion_Extended("Explosion_IonCannonBeacon", Pos, obj);
+		Commands->Create_Object("Beacon_Ion_Cannon_Anim_Post", Commands->Get_Position(obj));
 		Destroy_Script();
 	}
 };
@@ -792,6 +792,7 @@
 	}
 };
 
+
 class MC_Objective_Message_Sender : public ScriptImpClass
 {
 	static StringClass Underscore_To_Space(StringClass strtext)
@@ -877,8 +878,19 @@
 	GameObject *This;
 	int StarTrigger;
 	int CustomTriggerID;
+
+	void Send_Debug_Message(const char *String)
+	{
+		if (Get_Int_Parameter("Debug") == 1)
+		{
+			Send_Message(255, 255, 255, StringClass::getFormattedString("[MCDBG] %s", String));
+			Console_Output(StringClass::getFormattedString("[MCDBG] %s\n", String));
+		}
+	}
+
 	void Created(GameObject *obj)
 	{
+
 		CustomTriggerID = 7236527;
 		Message = StringClass::getFormattedString("%s", Get_Parameter("Message"));
 		Order = Get_Int_Parameter("ObjectiveOrder");
@@ -886,32 +898,36 @@
 		StarTrigger = Get_Int_Parameter("StarTrigger");
 		Listener = Commands->Find_Object(Get_Int_Parameter("Listener"));
 		This = obj;
-		if (std::string(OverType).find("NONE") != std::string::npos)
+		Send_Debug_Message(StringClass::getFormattedString("Over Type is %s", OverType));
+		if (!strstr(OverType, "NONE"))
 		{
 			ParameterObject = Commands->Find_Object(Get_Int_Parameter("ParameterObject"));
 			if (ParameterObject)
 			{
 				if (std::string(OverType).find("KILLOBJECT") != std::string::npos)
 				{
+					Send_Debug_Message("Created listener with type KILLOBJECT.");
 					Attach_Script_Once(ParameterObject, "MC_Objective_Listener", StringClass::getFormattedString("%i,%s,%i,%i", Commands->Get_ID(obj), OverType, CustomTriggerID, StarTrigger));
 				}
 				else if (std::string(OverType).find("ENTER") != std::string::npos)
 				{
+					Send_Debug_Message("Created listener with type ENTER.");
 					Attach_Script_Once(ParameterObject, "MC_Objective_Listener", StringClass::getFormattedString("%i,%s,%i,%i", Commands->Get_ID(obj), OverType, CustomTriggerID, StarTrigger));
 				}
 				else if (std::string(OverType).find("POKE") != std::string::npos)
 				{
+					Send_Debug_Message("Created listener with type POKE.");
 					Attach_Script_Once(ParameterObject, "MC_Objective_Listener", StringClass::getFormattedString("%i,%s,%i,%i", Commands->Get_ID(obj), OverType, CustomTriggerID, StarTrigger));
 				}
 				else
 				{
-
+					Send_Debug_Message("Can't create listener. Event is unknown.");
 				}
 			}
 		}
 		else
 		{
-
+			Send_Debug_Message("Created listener with type NONE.");
 		}
 		//MC_Objective_Controller::Scripts->Add_Head(this); //FIX
 		Commands->Start_Timer(obj, this, 2.0f, 129);
@@ -941,8 +957,18 @@
 
 public:
 
+	void Send_Debug_Message(const char *String)
+	{
+		if (Get_Int_Parameter("Debug") == 1)
+		{
+			Send_Message(255, 255, 255, StringClass::getFormattedString("[MCDBG] %s", String));
+			Console_Output(StringClass::getFormattedString("[MCDBG] %s\n", String));
+		}
+	}
+
 	void Created(GameObject *obj)
 	{
+		Send_Debug_Message("Controller created.");
 		ObjectiveMessageSender = Commands->Find_Object(Get_Int_Parameter("MessageObject"));
 		//Objectives = /*&std::vector<GameObject*>();*/ &DynamicVectorClass<GameObject*>();
 		Scripts = (SList<MC_Objective>());
@@ -953,13 +979,16 @@
 		{
 			int ObjectID = stoi(Objects[i]);
 			GameObject *obj = Commands->Find_Object(ObjectID);
-			Objectives.Insert(0, obj); //Crashing server. Needs fix. ||||-|||| Tried: Add(obj) | Insert(0, obj)
+			Objectives.Insert(0, obj);
 		}
+		Send_Debug_Message(StringClass::getFormattedString("%i objectives found and included.", Objects.size()));
 		//Starting first objective.
 		CurrentObjective = Get_Objective(0);
+		Send_Debug_Message("Started first objective.");
 		if (CurrentObjective)
 		{
 			Attach_Script_V(ObjectiveMessageSender, "MC_Objective_Message_Sender", CurrentObjective->Message);
+			Send_Debug_Message("Attached message sender.");
 		}
 		else
 		{
@@ -974,15 +1003,18 @@
 		{
 			if (Current_Objective == param)
 			{
+				Send_Debug_Message("Received finish objective custom from listener.");
 				CurrentObjective = Get_Objective(Current_Objective + 1);
 				if (CurrentObjective)
 				{
 					Current_Objective++;
+					Send_Debug_Message("Found a new objective to start.");
 					Commands->Send_Custom_Event(obj, ObjectiveMessageSender, 734638724, 0, 0.0f);
 					Commands->Start_Timer(obj, this, 0.2f, 1);
 				}
 				else
 				{
+					Send_Debug_Message("No new objectives detected.");
 					Commands->Send_Custom_Event(obj, ObjectiveMessageSender, 734638724, 0, 0.0f);
 					Commands->Start_Timer(obj, this, 0.2f, 2);
 				}
@@ -997,6 +1029,7 @@
 			if (!Is_Script_Attached(ObjectiveMessageSender, "MC_Objective_Message_Sender"))
 			{
 				Attach_Script_V(ObjectiveMessageSender, "MC_Objective_Message_Sender", StringClass::getFormattedString("%s", CurrentObjective->Message));
+				Send_Debug_Message("Started a new objective.");
 			}
 			else
 			{
@@ -1008,9 +1041,11 @@
 			if (!Is_Script_Attached(ObjectiveMessageSender, "MC_Objective_Message_Sender"))
 			{
 				GameObject *NoObjective = Commands->Create_Object("Daves Arrow", Vector3(0.0f, 0.0f, 0.0f));
+				Commands->Set_Model(NoObjective, "NULL");
 				Attach_Script_V(NoObjective, "MC_Objective", "There_are_no_new_objectives_detected._Game_might_be_ended.,%i,NONE,0,%i,0", Current_Objective, Commands->Get_ID(obj));
 				Objectives.Insert(0, NoObjective);
-				Attach_Script_V(ObjectiveMessageSender, "MC_Objective_Message_Sender", StringClass::getFormattedString(""));
+				Attach_Script_V(ObjectiveMessageSender, "MC_Objective_Message_Sender", StringClass::getFormattedString("There_are_no_new_objectives_detected._Game_might_be_ended."));
+				Send_Debug_Message("Attached no objectives message.");
 			}
 			else
 			{
@@ -1090,8 +1125,18 @@
 	const char *TriggerType;
 	int CustomToSend;
 	int OnlyStarTriggered;
+	void Send_Debug_Message(const char *String)
+	{
+		if (Get_Int_Parameter("Debug") == 1)
+		{
+			Send_Message(255, 255, 255, StringClass::getFormattedString("[MCDBG] %s", String));
+			Console_Output(StringClass::getFormattedString("[MCDBG] %s\n", String));
+		}
+	}
+
 	void Created(GameObject *obj)
 	{
+		Send_Debug_Message(StringClass::getFormattedString("LISTENER: Created listener at %i...", Commands->Get_ID(obj)));
 		Objective = Commands->Find_Object(Get_Int_Parameter("Objective"));
 		TriggerType = Get_Parameter("Trigger");
 		CustomToSend = Get_Int_Parameter("CustomID");
@@ -1164,6 +1209,7 @@
 			{
 				if (type == 4222957) //Destroy listener if objective cancelled.
 				{
+					Send_Debug_Message(StringClass::getFormattedString("LISTENER: Received end objective event from Controller. Destroying listener at %i...", Commands->Get_ID(obj)));
 					Destroy_Script();
 				}
 			}
@@ -1197,15 +1243,665 @@
 	}
 };
 
+class MC_Select_Weapon_On_Zone_Enter : public ScriptImpClass
+{
+	void Entered(GameObject *obj, GameObject *enterer)
+	{
+		Commands->Select_Weapon(enterer, Get_Parameter("WeaponPreset"));
+	}
+};
+
+class MC_Select_Weapon_On_Zone_Exit : public ScriptImpClass
+{
+	void Exited(GameObject *obj, GameObject *exiter)
+	{
+		Commands->Select_Weapon(exiter, Get_Parameter("WeaponPreset"));
+	}
+};
+
+class MC_Select_Weapon_On_Zone : public ScriptImpClass
+{
+	void Entered(GameObject *obj, GameObject *enterer)
+	{
+		Commands->Select_Weapon(enterer, Get_Parameter("EnterWeaponPreset"));
+	}
+
+	void Exited(GameObject *obj, GameObject *exiter)
+	{
+		Commands->Select_Weapon(exiter, Get_Parameter("ExitWeaponPreset"));
+	}
+};
+
+class MC_Chinook_Reinforcements_Paradrop : public ScriptImpClass
+{
+	//Taken from Single Player scriot M03_Chinook_ParaDrop and modified to drop 3 different soldiers.
+public:
+	void Register_Auto_Save_Variables()
+	{
+		Auto_Save_Variable(&this->nodTransportHelicopterObjId, sizeof(this->nodTransportHelicopterObjId), 1);
+		Auto_Save_Variable(&this->chinookKilled, sizeof(this->chinookKilled), 2);
+		Auto_Save_Variable(&this->paratrooperIndex, sizeof(this->paratrooperIndex), 3);
+	}
+
+private:
+	int nodTransportHelicopterObjId;
+	bool chinookKilled;
+	int paratrooperIndex;
+	GameObject *presetObj1;
+	GameObject *presetObj2;
+	GameObject *presetObj3;
+	GameObject *nodTransportHelicopterObj;
+	GameObject *MaintrajectoryObj;
+	int SoundObj;
+
+	void Send_Debug_Message(const char *String, ...)
+	{
+		if (Get_Int_Parameter("Debug") == 1)
+		{
+			Send_Message(255, 255, 255, StringClass::getFormattedString("[MCDBG] %s", String));
+			Console_Output(StringClass::getFormattedString("[MCDBG] %s\n", String));
+		}
+	}
+
+	bool Check_Object(GameObject *obj)
+	{
+		if (obj)
+		{
+			if (obj->As_SmartGameObj())
+			{
+				if (obj->As_SoldierGameObj())
+				{
+					return true;
+				}
+				else
+				{
+					return false;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	void Created(GameObject *obj)
+	{
+		presetObj1 = 0;
+		presetObj2 = 0;
+		presetObj3 = 0;
+		Vector3 pos = Commands->Get_Position(obj);
+		float facing = Commands->Get_Facing(obj);
+		Send_Debug_Message("Chinook Drop script is now initializing...");
+		GameObject *MaintrajectoryObj = Commands->Create_Object("Generic_Cinematic", pos);
+		Commands->Set_Model(MaintrajectoryObj, "X5D_Chinookfly");
+		Commands->Set_Facing(MaintrajectoryObj, facing);
+		Commands->Set_Animation(MaintrajectoryObj, "X5D_Chinookfly.X5D_Chinookfly", false, NULL, 0.0f, -1.0f, false);
+		Send_Debug_Message("Created main Trajectory object.");
+		if (Get_Object_Type(obj) == 0)
+		{
+
+			Send_Debug_Message("Creating Nod Chinook...");
+			nodTransportHelicopterObj = Commands->Create_Object("Nod_Transport_Helicopter_Flyover", pos);
+			Set_Max_Health(nodTransportHelicopterObj, 100000.0f);
+			Set_Max_Shield_Strength(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Health(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Shield_Strength(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Facing(nodTransportHelicopterObj, facing);
+			Commands->Disable_Physical_Collisions(nodTransportHelicopterObj);
+			Commands->Set_Animation(nodTransportHelicopterObj, "v_Nod_trnspt.v_Nod_trnspt", true, NULL, 0.0f, -1.0f, false);
+			Commands->Attach_To_Object_Bone(nodTransportHelicopterObj, MaintrajectoryObj, "BN_Chinook_1");
+			Send_Debug_Message("Chinook creation succesful.");
+		}
+		else
+		{
+			Send_Debug_Message("Creating GDI Chinook...");
+			nodTransportHelicopterObj = Commands->Create_Object("GDI_Transport_Helicopter_Flyover", pos);
+			Set_Max_Health(nodTransportHelicopterObj, 100000.0f);
+			Set_Max_Shield_Strength(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Health(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Shield_Strength(nodTransportHelicopterObj, 100000.0f);
+			Commands->Set_Facing(nodTransportHelicopterObj, facing);
+			Commands->Disable_Physical_Collisions(nodTransportHelicopterObj);
+			Commands->Set_Animation(nodTransportHelicopterObj, "v_GDI_trnspt.v_GDI_trnspt", true, NULL, 0.0f, -1.0f, false);
+			Commands->Attach_To_Object_Bone(nodTransportHelicopterObj, MaintrajectoryObj, "BN_Chinook_1");
+			Send_Debug_Message("Chinook creation succesful.");
+		}
+		Send_Debug_Message("Finishing initialization...");
+		this->chinookKilled = false;
+		this->paratrooperIndex = 0;
+
+		int objId = Commands->Get_ID(obj);
+		char buffer[16];
+		sprintf(buffer, "%d", objId);
+		Commands->Attach_Script(nodTransportHelicopterObj, "M03_Reinforcement_Chinook", buffer);
+
+		this->nodTransportHelicopterObjId = Commands->Get_ID(nodTransportHelicopterObj);
+		Send_Debug_Message("Starting timers...");
+		Commands->Start_Timer(obj, this, 300 / 30.0f, 0);
+		Commands->Start_Timer(obj, this, 169 / 30.0f, 1);
+		Commands->Start_Timer(obj, this, 179 / 30.0f, 2);
+		Commands->Start_Timer(obj, this, 198 / 30.0f, 3);
+		Commands->Start_Timer(obj, this, 145 / 30.0f, 4);
+		Commands->Start_Timer(obj, this, 155 / 30.0f, 5);
+		Commands->Start_Timer(obj, this, 165 / 30.0f, 6);
+		Commands->Start_Timer(obj, this, 25 / 30.0f, 7);
+		Commands->Start_Timer(obj, this, 280 / 30.0f, 8);
+		Send_Debug_Message("Script is now enabled and playing cinematic...");
+	}
+
+	void Custom(GameObject *obj, int type, int param, GameObject *sender)
+	{
+		if (type == 23000 && param == 23000)
+		{
+			this->chinookKilled = true;
+		}
+
+		if (type == 15730)
+		{
+			GameObject *Attach = Commands->Find_Object(param);
+			if (Attach)
+			{
+				presetObj1 = Attach;
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 1 has changed to object ID %i by a custom.", param));
+			}
+			else
+			{
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 1 was tried to change with object %i but object was unable to found.", param));
+			}
+		}
+		if (type == 15731)
+		{
+			GameObject *Attach = Commands->Find_Object(param);
+			if (Attach)
+			{
+				presetObj2 = Attach;
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 2 has changed to object ID %i by a custom.", param));
+			}
+			else
+			{
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 2 was tried to change with object %i but object was unable to found.", param));
+			}
+		}
+		if (type == 15732)
+		{
+			GameObject *Attach = Commands->Find_Object(param);
+			if (Attach)
+			{
+				presetObj3 = Attach;
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 3 has changed to object ID %i by a custom.", param));
+			}
+			else
+			{
+				Send_Debug_Message(StringClass::getFormattedString("Object at Slot 3 was tried to change with object %i but object was unable to found.", param));
+			}
+		}
+	}
+
+	void Timer_Expired(GameObject *obj, int number)
+	{
+		Vector3 pos = Commands->Get_Position(obj);
+		const char *preset1 = Get_Parameter("FirstPreset");
+		const char *preset2 = Get_Parameter("SecondPreset");
+		const char *preset3 = Get_Parameter("ThirdPreset");
+		float facing = Commands->Get_Facing(obj);
+
+		if (number == 0)
+		{
+			Send_Debug_Message("Cinematic ended and now scripts is being detached...");
+			Commands->Send_Custom_Event(0, presetObj1, 1248753, 0, 0.0f);
+			Commands->Send_Custom_Event(0, presetObj2, 1248753, 0, 0.0f);
+			Commands->Send_Custom_Event(0, presetObj3, 1248753, 0, 0.0f);
+			Send_Debug_Message("Cinematic played successfully! Detaching script...");
+			Destroy_Script();
+		}
+
+		else if (number == 8)
+		{
+			GameObject *nodTransportHelicopterObj = Commands->Find_Object(this->nodTransportHelicopterObjId);
+			if (nodTransportHelicopterObj)
+			{
+				Commands->Destroy_Object(nodTransportHelicopterObj);
+				Send_Debug_Message("Destroyed Chinook.");
+			}
+			else
+			{
+				Send_Debug_Message("Couldn't destroy Chinook: Object did not found.");
+			}
+		}
+
+		else if (number == 7)
+		{
+			if (nodTransportHelicopterObj)
+			{
+				Commands->Enable_Engine(nodTransportHelicopterObj, true);
+				Send_Debug_Message("Enabled Chinook engine.");
+			}
+			else
+			{
+				Send_Debug_Message("Couldn't enable Chinook engine: Object did not found.");
+			}
+		}
+
+		else if (number == 1)
+		{
+			if (this->paratrooperIndex > 0)
+			{
+				if (presetObj1)
+				{
+					GameObject *cinObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Facing(cinObj, facing);
+					Commands->Set_Model(cinObj, "X5D_Parachute");
+					Commands->Set_Animation(cinObj, "X5D_Parachute.X5D_ParaC_1", false, NULL, 0.0f, -1.0f, false);
+					Commands->Create_3D_Sound_At_Bone("parachute_open", cinObj, "ROOTTRANSFORM");
+					Commands->Attach_Script(cinObj, "M03_No_More_Parachute", "");
+					Send_Debug_Message("Enabled chute for the object at slot 1.");
+				}
+				else
+				{
+					Send_Debug_Message("Couldn't enabled chute for the object at slot 1: Object did not found.");
+				}
+			}
+		}
+
+		else if (number == 2)
+		{
+			if (this->paratrooperIndex > 1)
+			{
+				if (presetObj2)
+				{
+					GameObject *cinObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Facing(cinObj, facing);
+					Commands->Set_Model(cinObj, "X5D_Parachute");
+					Commands->Set_Animation(cinObj, "X5D_Parachute.X5D_ParaC_2", false, NULL, 0.0f, -1.0f, false);
+					Commands->Create_3D_Sound_At_Bone("parachute_open", cinObj, "ROOTTRANSFORM");
+					Commands->Attach_Script(cinObj, "M03_No_More_Parachute", "");
+					Send_Debug_Message("Enabled chute for the object at slot 2.");
+				}
+				else
+				{
+					Send_Debug_Message("Couldn't enabled chute for the object at slot 2: Object did not found.");
+				}
+			}
+		}
+
+		else if (number == 3)
+		{
+			if (this->paratrooperIndex == 3)
+			{
+				if (presetObj3)
+				{
+					GameObject *cinObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Facing(cinObj, facing);
+					Commands->Set_Model(cinObj, "X5D_Parachute");
+					Commands->Set_Animation(cinObj, "X5D_Parachute.X5D_ParaC_3", false, NULL, 0.0f, -1.0f, false);
+					Commands->Create_3D_Sound_At_Bone("parachute_open", cinObj, "ROOTTRANSFORM");
+					Commands->Attach_Script(cinObj, "M03_No_More_Parachute", "");
+					Send_Debug_Message("Enabled chute for the object at slot 3.");
+				}
+				else
+				{
+					Send_Debug_Message("Couldn't enabled chute for the object at slot 3: Object did not found.");
+				}
+			}
+		}
+
+		else if (number == 4)
+		{
+			if (!this->chinookKilled)
+			{
+				if (Is_Valid_Preset(preset1) || presetObj1)
+				{
+					Send_Debug_Message("Initializing drop animation for object at slot 1...");
+					GameObject *trajectoryObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Model(trajectoryObj, "X5D_Box01");
+					Commands->Set_Facing(trajectoryObj, facing);
+					Commands->Set_Animation(trajectoryObj, "X5D_Box01.X5D_Box01", false, NULL, 0.0f, -1.0f, false);
+					Send_Debug_Message("<Slot 1> Animation created! Attaching object...");
+					if (!presetObj1)
+					{
+						presetObj1 = Commands->Create_Object_At_Bone(trajectoryObj, preset1, "Box01");
+						Send_Debug_Message("<Slot 1> The object have been created.");
+					}
+					else
+					{
+						Send_Debug_Message("<Slot 1> Object is already attached by a custom event!");
+					}
+
+					if (presetObj1)
+					{
+						if (Check_Object(presetObj1))
+						{
+							Commands->Set_Facing(presetObj1, facing);
+							Send_Debug_Message("<Slot 1> Facing adjusted.");
+							Attach_Script_Once(presetObj1, "DB_Innate_Soldier", "0.5,0,0,0");
+							Attach_Script_Once(presetObj1, "MC_Unkillable_Until_Custom", "1248753");
+							Commands->Attach_Script(presetObj1, "RMV_Trigger_Killed", "1144444, 1000, 1000");
+							Commands->Attach_Script(presetObj1, "M03_Killed_Sound", "");
+							Commands->Attach_Script(presetObj1, "DLS_Volcano_Stumble", "");
+							Send_Debug_Message("<Slot 1> Scripts attached.");
+							Commands->Set_Animation(presetObj1, "s_a_human.H_A_X5D_ParaT_1", false, NULL, 0.0f, -1.0f, false);
+							Send_Debug_Message("<Slot 1> Animation set.");
+						}
+						Commands->Attach_To_Object_Bone(presetObj1, trajectoryObj, "Box01");
+						Send_Debug_Message("<Slot 1> Object attached to trajectory.");
+					}
+					Send_Debug_Message("<Slot 1> This slot has completed it's initialization.");
+				}
+				else
+				{
+					Send_Debug_Message("Initialization of slot 1 has cancelled: Invalid preset and object is nothing, object cannot be useable.");
+				}
+				this->paratrooperIndex++;
+			}
+		}
+
+		else if (number == 5)
+		{
+			if (!this->chinookKilled)
+			{
+				if (Is_Valid_Preset(preset2) || presetObj2)
+				{
+					Send_Debug_Message("Initializing drop animation for object at slot 2...");
+					GameObject *trajectoryObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Model(trajectoryObj, "X5D_Box02");
+					Commands->Set_Facing(trajectoryObj, facing);
+					Commands->Set_Animation(trajectoryObj, "X5D_Box02.X5D_Box02", false, NULL, 0.0f, -1.0f, false);
+					Send_Debug_Message("<Slot 2> Animation created! Attaching object...");
+					if (!presetObj2)
+					{
+						presetObj2 = Commands->Create_Object_At_Bone(trajectoryObj, preset2, "Box02");
+						Send_Debug_Message("<Slot 2> The object have been created.");
+					}
+					else
+					{
+						Send_Debug_Message("<Slot 2> Object is already attached by a custom event!");
+					}
+
+					if (presetObj2)
+					{
+						if (Check_Object(presetObj2))
+						{
+							Commands->Set_Facing(presetObj2, facing);
+							Send_Debug_Message("<Slot 2> Facing adjusted.");
+							Attach_Script_Once(presetObj2, "DB_Innate_Soldier", "0.5,0,0,0");
+							Attach_Script_Once(presetObj2, "MC_Unkillable_Until_Custom", "1248753");
+							Commands->Attach_Script(presetObj2, "RMV_Trigger_Killed", "1144444, 1000, 1000");
+							Commands->Attach_Script(presetObj2, "M03_Killed_Sound", "");
+							Commands->Attach_Script(presetObj2, "DLS_Volcano_Stumble", "");
+							Send_Debug_Message("<Slot 2> Scripts attached.");
+							Commands->Set_Animation(presetObj2, "s_a_human.H_A_X5D_ParaT_2", false, NULL, 0.0f, -1.0f, false);
+							Send_Debug_Message("<Slot 2> Animation set.");
+						}
+						Commands->Attach_To_Object_Bone(presetObj2, trajectoryObj, "Box02");
+						Send_Debug_Message("<Slot 2> Object attached to trajectory.");
+					}
+					Send_Debug_Message("<Slot 2> This slot has completed it's initialization.");
+				}
+				else
+				{
+					Send_Debug_Message("Initialization of slot 2 has cancelled: Invalid preset and object is nothing, object cannot be useable.");
+				}
+				this->paratrooperIndex++;
+			}
+		}
+
+		else if (number == 6)
+		{
+			if (!this->chinookKilled)
+			{
+				if (Is_Valid_Preset(preset3) || presetObj3)
+				{
+					Send_Debug_Message("Initializing drop animation for object at slot 3...");
+					GameObject *trajectoryObj = Commands->Create_Object("Generic_Cinematic", pos);
+					Commands->Set_Model(trajectoryObj, "X5D_Box03");
+					Commands->Set_Facing(trajectoryObj, facing);
+					Commands->Set_Animation(trajectoryObj, "X5D_Box03.X5D_Box03", false, NULL, 0.0f, -1.0f, false);
+					Send_Debug_Message("<Slot 3> Animation created! Attaching object...");
+
+					if (!presetObj3)
+					{
+						presetObj3 = Commands->Create_Object_At_Bone(trajectoryObj, preset3, "Box03");
+						Send_Debug_Message("<Slot 3> The object have been created.");
+					}
+					else
+					{
+						Send_Debug_Message("<Slot 3> Object is already attached by a custom event!");
+					}
+
+					if (presetObj3)
+					{
+						if (Check_Object(presetObj3))
+						{
+							Commands->Set_Facing(presetObj3, facing);
+							Send_Debug_Message("<Slot 3> Facing adjusted.");
+							Attach_Script_Once(presetObj3, "DB_Innate_Soldier", "0.5,0,0,0");
+							Attach_Script_Once(presetObj3, "MC_Unkillable_Until_Custom", "1248753");
+							Commands->Attach_Script(presetObj3, "RMV_Trigger_Killed", "1144444, 1000, 1000");
+							Commands->Attach_Script(presetObj3, "M03_Killed_Sound", "");
+							Commands->Attach_Script(presetObj3, "DLS_Volcano_Stumble", "");
+							Send_Debug_Message("<Slot 3> Scripts attached.");
+							Commands->Set_Animation(presetObj3, "s_a_human.H_A_X5D_ParaT_3", false, NULL, 0.0f, -1.0f, false);
+							Send_Debug_Message("<Slot 3> Animation set.");
+						}
+						Commands->Attach_To_Object_Bone(presetObj3, trajectoryObj, "Box03");
+						Send_Debug_Message("<Slot 3> Object attached to trajectory.");
+					}
+					Send_Debug_Message("<Slot 3> This slot has completed it's initialization.");
+				}
+				else
+				{
+					Send_Debug_Message("Initialization of slot 3 has cancelled: Invalid preset and object is nothing, object cannot be useable.");
+				}
+				this->paratrooperIndex++;
+			}
+		}
+	}
+};
+
+class MC_Unkillable_Until_Custom : public ScriptImpClass
+{
+	void Custom(GameObject *obj, int type, int param, GameObject *sender)
+	{
+		if (type == Get_Int_Parameter("Message"))
+		{
+			Destroy_Script();
+		}
+	}
+
+	void Damaged(GameObject *obj, GameObject *damager, float amount)
+	{
+		float maxHealth = Commands->Get_Max_Health(obj);
+		Commands->Set_Health(obj, maxHealth);
+	}
+};
+
+class MC_AttachScript_On_Kill : public ScriptImpClass
+{
+	void Killed(GameObject *obj, GameObject *killer)
+	{
+		Attach_Script_Once(Commands->Find_Object(Get_Int_Parameter("ObjectID")), Get_Parameter("Script"), Get_Parameter("Parameters"));
+		Destroy_Script();
+	}
+
+	void Destroyed(GameObject *obj)
+	{
+		Attach_Script_Once(Commands->Find_Object(Get_Int_Parameter("ObjectID")), Get_Parameter("Script"), Get_Parameter("Parameters"));
+		Destroy_Script();
+	}
+};
+
+class MC_AttachScript_Interval_Self : public ScriptImpClass
+{
+	const char *Script;
+	const char *Parameters;
+	float Interval;
+	int LoopCount;
+	int Looped;
+	void Created(GameObject *obj)
+	{
+		Script = Get_Parameter("Script");
+		Parameters = Get_Parameter("Parameters");
+		Commands->Start_Timer(obj, this, Interval, 234245);
+		LoopCount = Get_Int_Parameter("LoopCount");
+		Looped = 0;
+	}
+
+	void Timer_Expired(GameObject *obj, int number)
+	{
+		if (number == 234245)
+		{
+			Attach_Script_Once(obj, Script, Parameters);
+			if (LoopCount != -1)
+			{
+				if (Looped >= LoopCount)
+				{
+					Destroy_Script();
+				}
+				else
+				{
+					Commands->Start_Timer(obj, this, Interval, 234245);
+				}
+			}
+			else
+			{
+				Commands->Start_Timer(obj, this, Interval, 234245);
+			}
+		}
+	}
+};
+
+class MC_AttachScript_Interval : public ScriptImpClass
+{
+	const char *Script;
+	const char *Parameters;
+	GameObject *Target;
+	float Interval;
+	int LoopCount;
+	int Looped;
+	void Created(GameObject *obj)
+	{
+		Target = Commands->Find_Object(Get_Int_Parameter("ObjectID"));
+		Script = Get_Parameter("Script");
+		Parameters = Get_Parameter("Parameters");
+		Commands->Start_Timer(obj, this, Interval, 234244);
+		LoopCount = Get_Int_Parameter("LoopCount");
+		Looped = 0;
+
+	}
+
+	void Timer_Expired(GameObject *obj, int number)
+	{
+		if (number == 234244)
+		{
+			Attach_Script_Once(Target, Script, Parameters);
+			if (LoopCount != -1)
+			{
+				if (Looped >= LoopCount)
+				{
+					Destroy_Script();
+				}
+				else
+				{
+					Commands->Start_Timer(obj, this, Interval, 234244);
+				}
+			}
+			else
+			{
+				Commands->Start_Timer(obj, this, Interval, 234244);
+			}
+		}
+	}
+};
+
+class MC_Object_Follower_AI : public ScriptImpClass
+{
+	int TargetChangeCustom;
+	int DestroyScriptCustom;
+	GameObject *TargetObject;
+
+	void Created(GameObject *obj)
+	{
+		TargetChangeCustom = Get_Int_Parameter("TargetChangeCustomID");
+		DestroyScriptCustom = Get_Int_Parameter("DestroyScriptCustomID");
+	}
+
+	void Custom(GameObject *obj, int type, int param, GameObject *sender)
+	{
+		if (type == TargetChangeCustom)
+		{
+			GameObject *Target = Commands->Find_Object(param);
+			if (Target)
+			{
+				TargetObject = Target;
+				ActionParamsStruct Parameters;
+				Parameters.Set_Basic(this, 100.0f, 20000);
+				Parameters.Set_Movement(Target, 0.8f, 2.5f);
+			}
+		}
+		else if (type == DestroyScriptCustom)
+		{
+			Destroy_Script();
+		}
+	}
+
+	void Timer_Expired(GameObject *obj, int number)
+	{
+		if (number == 12314178)
+		{
+			if (TargetObject)
+			{
+				ActionParamsStruct Parameters;
+				Parameters.Set_Basic(this, 100.0f, 20000);
+				Parameters.Set_Movement(TargetObject, 0.8f, 2.5f);
+			}
+		}
+	}
+};
+
+class MC_Private_Color_Message_On_Poke : public ScriptImpClass
+{
+	bool Pokeable;
+	void Created(GameObject *obj)
+	{
+		Pokeable = true;
+		Commands->Enable_HUD_Pokable_Indicator(obj, true);
+	}
+
+	void Timer_Expired(GameObject *obj, int number)
+	{
+		if (number == 1)
+		{
+			Pokeable = true;
+			Commands->Enable_HUD_Pokable_Indicator(obj, true);
+		}
+	}
+
+	void Poked(GameObject *obj, GameObject *poker)
+	{
+		if (Pokeable)
+		{
+			Send_Message_Player(poker, Get_Int_Parameter("Red"), Get_Int_Parameter("Green"), Get_Int_Parameter("Blue"), Get_Parameter("Message"));
+			Pokeable = false;
+			Commands->Enable_HUD_Pokable_Indicator(obj, false);
+			Commands->Start_Timer(obj, this, 5.0f, 1);
+		}
+	}
+};
+
 /* NotTest: Not tested yet */
 /* Working: Completely working */
 /* HasBugs: Has at least 1 bug */
 /* NotWork: Not working. Going to be fixed */
 /* Working */ ScriptRegistrant<MC_Stop_Vehicle_Engine> MC_Stop_Vehicle_Engine_Registrant("MC_Stop_Vehicle_Engine", "Duration=0:float");
 /* Working */ ScriptRegistrant<MC_Vehicle_Purchase_Terminal> MC_Vehicle_Purchase_Terminal_Registrant("MC_Vehicle_Purchase_Terminal", "Team=0:int,SpawnerID=0:int,VehiclePreset:string,Credits=400:int");
-/* Working */ ScriptRegistrant<MC_Character_Purchase_Terminal> MC_Character_Purchase_Terminal_Registrant("MC_Character_Purchase_Terminal", "Team=0:int,SpawnerID=0:int,CharacterPreset:string,Credits=400:int");
-/* Working */ ScriptRegistrant<MC_HUD_Message_Zone> MC_HUD_Message_Zone_Registrant("MC_HUD_Message_Zone", "BoxID=0:int,Message=YourMessage:string,Sound=SoundName.wav:string,Red=16.0:float,Green=255.0:float,Blue=16.0:float");
-/* Working */ ScriptRegistrant<MC_Teleport> MC_Teleport_Registrant("MC_Teleport", "BoxID=0:int,ArrowID=0:int,TeleportSound=SoundName.wav:string");
+/* Working */ ScriptRegistrant<MC_Character_Purchase_Terminal> MC_Character_Purchase_Terminal_Registrant("MC_Character_Purchase_Terminal", "Team=0:int,CharacterPreset:string,Credits=400:int");
+/* Working */ ScriptRegistrant<MC_HUD_Message_Zone> MC_HUD_Message_Zone_Registrant("MC_HUD_Message_Zone", "Message=YourMessage:string,Sound=SoundName.wav:string,Red=16.0:float,Green=255.0:float,Blue=16.0:float");
+/* Working */ ScriptRegistrant<MC_Teleport> MC_Teleport_Registrant("MC_Teleport", "ArrowID=0:int,TeleportSound=SoundName.wav:string");
 /* Working */ ScriptRegistrant<MC_Change_Vehicle_Weapon> MC_Change_Vehicle_Weapon_Registrant("MC_Change_Vehicle_Weapon", "Preset=Weapon:string,Rounds=1:int");
 /* Working */ ScriptRegistrant<MC_Change_Vehicle_Weapon_Zone> MC_Change_Vehicle_Weapon_Zone_Registrant("MC_Change_Vehicle_Weapon_Zone", "Preset=Weapon:string,Rounds=1:int");
 /* Working */ ScriptRegistrant<MC_Grant_Weapon> MC_Grant_Weapon_Registrant("MC_Grant_Weapon", "Preset=Weapon:string,Rounds=1:int");
@@ -1229,7 +1925,16 @@
 /* Working */ ScriptRegistrant<MC_Spectate_Zone> MC_Spectate_Zone_Registrant("MC_Spectate_Zone", "");
 /* HasBugs */ ScriptRegistrant<MC_Beacon_Block_Zone> MC_Beacon_Block_Zone_Registrant("MC_Beacon_Block_Zone", "Red=16:int,Green=255:int,Blue=16:int,BlockMessage:string");
 /* Working */ ScriptRegistrant<MC_Objective_Message_Sender> MC_Objective_Message_Sender_Registrant("MC_Objective_Message_Sender", "Message=Use_Underscore_For_Space:string");
-/* Working */ ScriptRegistrant<MC_Objective_Controller> MC_Objective_Controller_Registrant("MC_Objective_Controller", "Objectives=Objective-Objects-IDs-Here-Minus-Is-Delimeter:string,MessageObject=100000:int");
-/* Working */ ScriptRegistrant<MC_Objective_Listener> MC_Objective_Listener_Registrant("MC_Objective_Listener", "Objective=100000:int,Trigger:string,CustomID:int,OnlyStarCanTrigger:int");
-/* Working */ ScriptRegistrant<MC_Objective> MC_Objective_Registrant("MC_Objective", "Message=Use_Underscore_For_Space:string,ObjectiveOrder=0:int,ObjectiveCompleteType=Must_be_KILLOBJECT_ENTER_POKE_or_NONE:string,StarTrigger=0:int,Listener=100000:int,ParameterObject=100000:int");
+/* Working */ ScriptRegistrant<MC_Objective_Controller> MC_Objective_Controller_Registrant("MC_Objective_Controller", "Objectives=Objective-Objects-IDs-Here-Minus-Is-Delimeter:string,MessageObject=100000:int,Debug=1:int");
+/* Working */ ScriptRegistrant<MC_Objective_Listener> MC_Objective_Listener_Registrant("MC_Objective_Listener", "Objective=100000:int,Trigger:string,CustomID:int,OnlyStarCanTrigger:int,Debug=1:int");
+/* Working */ ScriptRegistrant<MC_Objective> MC_Objective_Registrant("MC_Objective", "Message=Use_Underscore_For_Space:string,ObjectiveOrder=0:int,ObjectiveCompleteType=Must_be_KILLOBJECT_ENTER_POKE_or_NONE:string,StarTrigger=0:int,Listener=100000:int,ParameterObject=100000:int,Debug=1:int");
 /* Working */ ScriptRegistrant<MC_Destroy_When_Objective_Completed> MC_Destroy_When_Objective_Completed_Registrant("MC_Destroy_When_Objective_Completed", "ObjectiveArrow=100000:int");
+/* Working */ ScriptRegistrant<MC_Select_Weapon_On_Zone> MC_Select_Weapon_On_Zone_Registrant("MC_Select_Weapon_On_Zone", "EnterWeaponPreset:string,ExitWeaponPreset:string");
+/* Working */ ScriptRegistrant<MC_Select_Weapon_On_Zone_Enter> MC_Select_Weapon_On_Zone_Enter_Registrant("MC_Select_Weapon_On_Zone_Enter", "WeaponPreset:string");
+/* Working */ ScriptRegistrant<MC_Select_Weapon_On_Zone_Exit> MC_Select_Weapon_On_Zone_Exit_Registrant("MC_Select_Weapon_On_Zone_Exit", "WeaponPreset:string");
+/* Working */ ScriptRegistrant<MC_Chinook_Reinforcements_Paradrop> MC_Chinook_Reinforcements_Paradrop_Registrant("MC_Chinook_Reinforcements_Paradrop", "FirstPreset:string,SecondPreset:string,ThirdPreset:string,Debug=1:int");
+/* Working */ ScriptRegistrant<MC_Unkillable_Until_Custom> MC_Unkillable_Until_Custom_Registrant("MC_Unkillable_Until_Custom", "Message:int");
+/* Working */ ScriptRegistrant<MC_AttachScript_On_Kill> MC_AttachScript_On_Kill_Registrant("MC_AttachScript_On_Kill", "ObjectID:int,Script:string,Parameters:string");
+/* Working */ ScriptRegistrant<MC_AttachScript_Interval_Self> MC_AttachScript_Interval_Self_Registrant("MC_AttachScript_Interval_Self", "Script:string,Parameters:string,LoopCount=-1:int");
+/* Working */ ScriptRegistrant<MC_AttachScript_Interval> MC_AttachScript_Interval_Registrant("MC_AttachScript_Interval", "ObjectID:int,Script:string,Parameters:string,LoopCount=-1:int");
+/* Working */ ScriptRegistrant<MC_Private_Color_Message_On_Poke> MC_Private_Color_Message_On_Poke_Registrant("MC_Private_Color_Message_On_Poke", "Red=255:int,Green=255:int,Blue=255:int,Message=Message:string");
\ No newline at end of file
diff -ur sourceold/scripts/PhysicsSceneClass.h source/scripts/PhysicsSceneClass.h
--- sourceold/scripts/PhysicsSceneClass.h	2019-03-31 08:17:14.583000000 +1000
+++ source/scripts/PhysicsSceneClass.h	2019-06-22 07:43:44.222400000 +1000
@@ -250,7 +250,9 @@
 	void Post_Load_Level_Dynamic_Data(void);
 	void Set_Polygon_Budgets(int static_count,int dynamic_count);
 	void Get_Polygon_Budgets(int * static_count,int * dynamic_count);
+#ifdef TT_EXPORTS
 	void Refresh_Polygon_Budgets();
+#endif
 	void Set_Update_Only_Visible_Objects(bool b) { UpdateOnlyVisibleObjects=b; }
 	bool Get_Update_Only_Visible_Objects() { return UpdateOnlyVisibleObjects; }
 	virtual void Add_Render_Object(RenderObjClass * obj);
@@ -391,7 +393,7 @@
 	static bool AllowCollisionFlags[NUM_COLLISION_FLAGS];
 	int DynamicPolyBudget;
 	int StaticPolyBudget;
-#ifndef TTLE_EXPORTS
+#ifdef TT_EXPORTS
 	float mapStaticPolyBudget;
 	float mapDynamicPolyBudget;
 #endif
diff -ur sourceold/scripts/SoldierGameObjDef.h source/scripts/SoldierGameObjDef.h
--- sourceold/scripts/SoldierGameObjDef.h	2018-11-17 10:23:57.089600000 +1000
+++ source/scripts/SoldierGameObjDef.h	2019-06-22 07:43:44.223400000 +1000
@@ -62,6 +62,11 @@
 	friend	class								SoldierGameObj;
 	friend	class								SoldierObserverClass;
 	friend class PresetDump;
+public:
+	bool	GetInnateBehavior(){ return UseInnateBehavior;}
+	float	GetInnateAggressiveness(){ return InnateAggressiveness;}
+	float	GetInnateTakeCoverProbability(){ return InnateTakeCoverProbability;}
+	bool	GetInnateIsStationary(){ return InnateIsStationary;}
 }; // size: 912
 
 
diff -ur sourceold/scripts/VisTableClass.h source/scripts/VisTableClass.h
--- sourceold/scripts/VisTableClass.h	2018-10-27 22:04:56.356400000 +1000
+++ source/scripts/VisTableClass.h	2019-06-22 07:43:44.224400000 +1000
@@ -1,118 +1,118 @@
-/*	Renegade Scripts.dll
-	Copyright 2013 Tiberian Technologies
-
-	This file is part of the Renegade scripts.dll
-	The Renegade scripts.dll is free software; you can redistribute it and/or modify it under
-	the terms of the GNU General Public License as published by the Free
-	Software Foundation; either version 2, or (at your option) any later
-	version. See the file COPYING for more details.
-	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
-	Only the source code to the module(s) containing the licenced code has to be released.
-*/
-#ifndef TT_INCLUDE__VISTABLECLASS_H
-#define TT_INCLUDE__VISTABLECLASS_H
-
-
-
-#include "engine_vector.h"
-#include "multilist.h"
-
-
-class CompressedVisTableClass;
-class VisTableClass :
-	public RefCountClass,
-	public MultiListObjectClass
-{
-
-	int bitCount; // 0010  0028
-	union
-	{
-		byte* bytes; // 0014  002C
-		uint32* longs; // 0014  002C
-	};
-
-	int visSectorId; // 0018  0030
-	int timeStamp; // 001C  0034
-
-public:
-
-	VisTableClass(uint, int);
-	VisTableClass(CompressedVisTableClass*, int, int);
-	VisTableClass(const VisTableClass& that) { *this = that; }
-	~VisTableClass();
-	const VisTableClass& operator =(const VisTableClass& that);
-
-	void Alloc_Buffer(int);
-	byte* Get_Bytes() const { return bytes; }
-	int Get_Byte_Count() const { return 4 * Get_Long_Count(); }
-	uint32* Get_Longs() const { return longs; }
-	int Get_Long_Count() const;
-	void Reset_All();
-	void Set_All();
-	void Delete_Bit(int);
-	void Merge(const VisTableClass &that);
-	void Invert();
-	bool Is_Equal_To(const VisTableClass &that) const;
-	int Count_Differences(const VisTableClass &that) const;
-	int Count_True_Bits() const;
-	float Match_Fraction(const VisTableClass &that) const;
-	int Get_Bit_Count() const { return bitCount; }
-	void Set_Vis_Sector_ID(int _visSectorId) { visSectorId = _visSectorId; }
-	int Get_Vis_Sector_ID() const { return visSectorId; }
-	void Set_Time_Stamp(int _timeStamp) { timeStamp = _timeStamp; }
-	int Get_Time_Stamp() const { return timeStamp; }
-	bool Get_Bit(int) const;
-	void Set_Bit(int, bool);
-
-}; // 0020  0038
-
-class CompressedVisTableClass
-{
-public:
-	CompressedVisTableClass() : BufferSize(0), Buffer(0)
-	{
-	}
-	CompressedVisTableClass(VisTableClass *bits) : BufferSize(0), Buffer(0)
-	{
-		Compress(bits->Get_Bytes(),bits->Get_Byte_Count());
-	}
-	CompressedVisTableClass(CompressedVisTableClass &that) : BufferSize(0), Buffer(0)
-	{
-		*this = that;
-	}
-	~CompressedVisTableClass()
-	{
-		if (Buffer)
-		{
-			delete[] Buffer;
-		}
-	}
-	CompressedVisTableClass& operator=(const CompressedVisTableClass& that)
-	{
-		if (Buffer)
-		{
-			delete[] Buffer;
-		}
-		BufferSize = that.BufferSize;
-		Buffer = new unsigned char[BufferSize];
-		memcpy(Buffer,that.Buffer,BufferSize);
-		return *this;
-	}
-	unsigned char *Get_Bytes()
-	{
-		return Buffer;
-	}
-	int Get_Byte_Count()
-	{
-		return BufferSize;
-	}
-	void Load(ChunkLoadClass &cload);
-	void Save(ChunkSaveClass &csave);
-	void Compress(unsigned char *bytes,int src_size);
-	void Decompress(unsigned char *bytes,int src_size);
-private:
-	int BufferSize;
-	unsigned char *Buffer;
-};
-
+/*	Renegade Scripts.dll
+	Copyright 2013 Tiberian Technologies
+
+	This file is part of the Renegade scripts.dll
+	The Renegade scripts.dll is free software; you can redistribute it and/or modify it under
+	the terms of the GNU General Public License as published by the Free
+	Software Foundation; either version 2, or (at your option) any later
+	version. See the file COPYING for more details.
+	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
+	Only the source code to the module(s) containing the licenced code has to be released.
+*/
+#ifndef TT_INCLUDE__VISTABLECLASS_H
+#define TT_INCLUDE__VISTABLECLASS_H
+
+
+
+#include "engine_vector.h"
+#include "multilist.h"
+
+
+class CompressedVisTableClass;
+class VisTableClass :
+	public RefCountClass,
+	public MultiListObjectClass
+{
+
+	int bitCount; // 0010  0028
+	union
+	{
+		byte* bytes; // 0014  002C
+		uint32* longs; // 0014  002C
+	};
+
+	int visSectorId; // 0018  0030
+	int timeStamp; // 001C  0034
+
+public:
+
+	VisTableClass(uint, int);
+	VisTableClass(CompressedVisTableClass*, int, int);
+	VisTableClass(const VisTableClass& that);
+	~VisTableClass();
+	const VisTableClass& operator =(const VisTableClass& that);
+
+	void Alloc_Buffer(int);
+	byte* Get_Bytes() const { return bytes; }
+	int Get_Byte_Count() const { return 4 * Get_Long_Count(); }
+	uint32* Get_Longs() const { return longs; }
+	int Get_Long_Count() const;
+	void Reset_All();
+	void Set_All();
+	void Delete_Bit(int);
+	void Merge(const VisTableClass &that);
+	void Invert();
+	bool Is_Equal_To(const VisTableClass &that) const;
+	int Count_Differences(const VisTableClass &that) const;
+	int Count_True_Bits() const;
+	float Match_Fraction(const VisTableClass &that) const;
+	int Get_Bit_Count() const { return bitCount; }
+	void Set_Vis_Sector_ID(int _visSectorId) { visSectorId = _visSectorId; }
+	int Get_Vis_Sector_ID() const { return visSectorId; }
+	void Set_Time_Stamp(int _timeStamp) { timeStamp = _timeStamp; }
+	int Get_Time_Stamp() const { return timeStamp; }
+	bool Get_Bit(int) const;
+	void Set_Bit(int, bool);
+
+}; // 0020  0038
+
+class CompressedVisTableClass
+{
+public:
+	CompressedVisTableClass() : BufferSize(0), Buffer(0)
+	{
+	}
+	CompressedVisTableClass(VisTableClass *bits) : BufferSize(0), Buffer(0)
+	{
+		Compress(bits->Get_Bytes(),bits->Get_Byte_Count());
+	}
+	CompressedVisTableClass(CompressedVisTableClass &that) : BufferSize(0), Buffer(0)
+	{
+		*this = that;
+	}
+	~CompressedVisTableClass()
+	{
+		if (Buffer)
+		{
+			delete[] Buffer;
+		}
+	}
+	CompressedVisTableClass& operator=(const CompressedVisTableClass& that)
+	{
+		if (Buffer)
+		{
+			delete[] Buffer;
+		}
+		BufferSize = that.BufferSize;
+		Buffer = new unsigned char[BufferSize];
+		memcpy(Buffer,that.Buffer,BufferSize);
+		return *this;
+	}
+	unsigned char *Get_Bytes()
+	{
+		return Buffer;
+	}
+	int Get_Byte_Count()
+	{
+		return BufferSize;
+	}
+	void Load(ChunkLoadClass &cload);
+	void Save(ChunkSaveClass &csave);
+	void Compress(unsigned char *bytes,int src_size);
+	void Decompress(unsigned char *bytes,int src_size);
+private:
+	int BufferSize;
+	unsigned char *Buffer;
+};
+
 #endif
\ No newline at end of file
diff -ur sourceold/scripts/dllmain.cpp source/scripts/dllmain.cpp
--- sourceold/scripts/dllmain.cpp	2018-11-17 10:23:57.133600000 +1000
+++ source/scripts/dllmain.cpp	2019-06-22 07:43:44.225400000 +1000
@@ -376,6 +376,8 @@
 	Set_Camera_Host_Network = (schn)Address(tt,"Set_Camera_Host_Network");
 	Get_Pathfind_Distance = (gpd)Address(tt,"Get_Pathfind_Distance");
 	Cancel_Get_Pathfind_Distance = (cgpd)Address(tt,"Cancel_Get_Pathfind_Distance");
+	Get_Pathfind_Distance_Async = (gpda)Address(tt, "Get_Pathfind_Distance_Async");
+	Get_Pathfind_Distance_Blocking = (gpdb)Address(tt, "Get_Pathfind_Distance_Blocking");
 
 #ifdef SSGM
 	SSGMGameManager::Init();
diff -ur sourceold/scripts/engine_tt.cpp source/scripts/engine_tt.cpp
--- sourceold/scripts/engine_tt.cpp	2018-11-17 10:23:57.178600000 +1000
+++ source/scripts/engine_tt.cpp	2019-06-22 07:43:44.226400000 +1000
@@ -225,7 +225,9 @@
 SCRIPTS_API fvup Force_Velocity_Update_Player;
 SCRIPTS_API schn Set_Camera_Host_Network;
 SCRIPTS_API gpd Get_Pathfind_Distance;
+SCRIPTS_API gpda Get_Pathfind_Distance_Async;
 SCRIPTS_API cgpd Cancel_Get_Pathfind_Distance;
+SCRIPTS_API gpdb Get_Pathfind_Distance_Blocking;
 
 SCRIPTS_API bool Can_Team_Build_Vehicle(int Team)
 {
diff -ur sourceold/scripts/engine_tt.h source/scripts/engine_tt.h
--- sourceold/scripts/engine_tt.h	2018-11-17 10:23:57.179600000 +1000
+++ source/scripts/engine_tt.h	2019-06-22 07:43:44.227400000 +1000
@@ -181,7 +181,9 @@
 typedef void (*fvu) (GameObject *obj);
 typedef void (*fvup) (GameObject *player,GameObject *obj);
 typedef void (*schn) (GameObject *obj);
-typedef uint32 (*gpd) (const Vector3 &start, const Vector3 &dest, PathfindDistanceCallback callback, SmartGameObj *pathObj, void *data);
+typedef uint32(*gpd) (const Vector3& start, const Vector3& dest, PathfindDistanceCallback callback, SmartGameObj* pathObj, void* data);
+typedef uint32 (*gpda) (SmartGameObj* pathObj, const Vector3 &dest, PathfindDistanceCallback callback, void *data);
+typedef bool(*gpdb) (SmartGameObj* pathObj, const Vector3& dest, float& distanceResult, PathfindDistanceResult& pathfindResult);
 typedef bool (*cgpd) (uint32 id);
 SCRIPTS_API extern gpl Get_Player_List;
 SCRIPTS_API extern gcmi Get_Current_Map_Index;
@@ -365,8 +367,10 @@
 SCRIPTS_API extern fvu Force_Velocity_Update;
 SCRIPTS_API extern fvup Force_Velocity_Update_Player;
 SCRIPTS_API extern schn Set_Camera_Host_Network;
-SCRIPTS_API extern gpd Get_Pathfind_Distance; // Returns > 0 when succusful. May trigger callback function straight away. pathObj must be a vehicle or a soldier
-SCRIPTS_API extern cgpd Cancel_Get_Pathfind_Distance; // Cancels a pathfind distance request. Use the result from Get_Pathfind_Distance to cancel
+SCRIPTS_API extern gpd Get_Pathfind_Distance; // Deprecated, use Get_Pathfind_Distance_Async in stead
+SCRIPTS_API extern gpda Get_Pathfind_Distance_Async; // Returns > 0 when succusful. pathObj must be a vehicle or a soldier
+SCRIPTS_API extern cgpd Cancel_Get_Pathfind_Distance; // Cancels a pathfind distance request. Use the result from Get_Pathfind_Distance_Async/Get_Pathfind_Distance to cancel
+SCRIPTS_API extern gpdb Get_Pathfind_Distance_Blocking; // Returns > 0 when succesful. Solves distances immediately. pathObj must be a vehicle or a soldier
 
 class SCRIPTS_API JFW_Key_Hook_Base : public ScriptImpClass {
 public:
diff -ur sourceold/scripts/engine_ttdef.h source/scripts/engine_ttdef.h
--- sourceold/scripts/engine_ttdef.h	2018-10-27 22:04:56.432400000 +1000
+++ source/scripts/engine_ttdef.h	2019-06-22 07:43:44.228400000 +1000
@@ -93,8 +93,8 @@
 
 	}
 
-	PathfindDistanceRequest(const Vector3 &start, const Vector3 &dest, PathfindDistanceCallback callback, void *data) :
-		Id(0), Start(start), Dest(dest), PathSolver(nullptr), Result(PATHFIND_DISTANCE_INVALID_RESULT), Distance(0.0f), Callback(callback), Data(data)
+	PathfindDistanceRequest(uint32 id, const Vector3 &start, const Vector3 &dest, PathSolveClass* solver, PathfindDistanceCallback callback, void *data) :
+		Id(id), Start(start), Dest(dest), PathSolver(solver), Result(PATHFIND_DISTANCE_INVALID_RESULT), Distance(0.0f), Callback(callback), Data(data)
 	{
 
 	}
@@ -111,7 +111,6 @@
 
 	void Do_Callback()
 	{
-		PathSolver = nullptr;
 		Callback((*this));
 	}
 
diff -ur sourceold/scripts/gmbuilding.cpp source/scripts/gmbuilding.cpp
--- sourceold/scripts/gmbuilding.cpp	2018-11-17 10:23:57.185600000 +1000
+++ source/scripts/gmbuilding.cpp	2019-06-22 07:43:44.229400000 +1000
@@ -227,13 +227,7 @@
 		delete[] str2;
 		SSGMGameLog::Log_Message(str,"_BUILDING");
 	}
-	Vector3	victimpos =	Commands->Get_Position(obj);
-	Vector3	damagerpos = Commands->Get_Position(killer);
-	const char *str = Get_Translated_Preset_Name_Ex(obj);
-	const char *str2 = Get_Translated_Preset_Name_Ex(killer);
-	SSGMGameLog::Log_Gamelog("KILLED;BUILDING;%d;%s;%d;%d;%d;%d;%d;%s;%d;%d;%d;%d;%s;%s;%s",Commands->Get_ID(obj), Commands->Get_Preset_Name(obj), int(victimpos.Y), int(victimpos.X), int(victimpos.Z), int(Commands->Get_Facing(obj)), Commands->Get_ID(killer),	Commands->Get_Preset_Name(killer),	int(damagerpos.Y), int(damagerpos.X), int(damagerpos.Z), int(Commands->Get_Facing(killer)),Get_Current_Weapon(killer),str,str2);
-	delete[] str;
-	delete[] str2;
+	Log_Killed_Message(obj, killer, "BUILDING");
 
 	if ( ((obj->As_BuildingGameObj() && obj->As_BuildingGameObj()->As_ConstructionYardGameObj())) || (Is_Script_Attached(obj,"TDA_ConYard_Controller")) )
 	{
diff -ur sourceold/scripts/gmgame.cpp source/scripts/gmgame.cpp
--- sourceold/scripts/gmgame.cpp	2018-11-17 10:23:57.187600000 +1000
+++ source/scripts/gmgame.cpp	2019-06-22 07:43:44.230400000 +1000
@@ -181,6 +181,29 @@
 	return Get_Translated_Preset_Name_Part(obj);
 }
 
+SCRIPTS_API void Log_Killed_Message(GameObject *obj, GameObject *killer, const char *objectType)
+{
+	const char *killerPreset = Commands->Get_Preset_Name(killer);
+	const char *killerWeapon = Get_Current_Weapon(killer);
+	if (auto killerVehicle = Get_Vehicle(killer))
+	{
+		killerPreset = Commands->Get_Preset_Name(killerVehicle);
+		killerWeapon = Get_Current_Weapon(killerVehicle);
+	}
+
+	auto killedObj = obj;
+	if (auto killedVehicle = Get_Vehicle(obj))
+	{
+		killedObj = Get_Vehicle_Occupant(obj, 0);
+	}
+
+	Vector3	victimpos = Commands->Get_Position(obj);
+	Vector3	damagerpos = Commands->Get_Position(killer);
+	const char *str1 = Get_Translated_Preset_Name_Ex(obj);
+	const char *str2 = Get_Translated_Preset_Name_Ex(killer);
+	SSGMGameLog::Log_Gamelog("KILLED;%s;%d;%s;%d;%d;%d;%d;%d;%s;%d;%d;%d;%d;%s;%s;%s;%s;%s", objectType, Commands->Get_ID(obj), Commands->Get_Preset_Name(obj), int(victimpos.Y), int(victimpos.X), int(victimpos.Z), int(Commands->Get_Facing(obj)), Commands->Get_ID(killer), killerPreset, int(damagerpos.Y), int(damagerpos.X), int(damagerpos.Z), int(Commands->Get_Facing(killer)), killerWeapon, str1, str2, Get_Player_Name(killer), Get_Player_Name(killedObj));
+}
+
 SCRIPTS_API void RegisterEvent(EventType evt,Plugin *plugin)
 {
 	RegisteredEvents[evt].Add(plugin);
@@ -943,9 +966,10 @@
 	const char *str2 = Get_Translated_Definition_Name_Ini(Get_Definition_Name(preset));
 	const char *str3 = Get_Player_Name(purchaser);
 	str.Format("Purchase: %s - %s",str3,str2);
+	SSGMGameLog::Log_Message(str,"_PURCHASE");
+	SSGMGameLog::Log_Gamelog("PURCHASED;POWERUP;%s;%s;%s", str3, Get_Definition_Name(preset), str2);
 	delete[] str3;
 	delete[] str2;
-	SSGMGameLog::Log_Message(str,"_PURCHASE");
 	bool fp = FreePurchases;
 	for (int i = 0;i < RegisteredEvents[EVENT_POWERUP_PURCHASE_HOOK].Count();i++)
 	{
@@ -976,9 +1000,10 @@
 	const char *str2 = Get_Translated_Definition_Name_Ini(Get_Definition_Name(preset));
 	const char *str3 = Get_Player_Name(purchaser);
 	str.Format("Purchase: %s - %s",str3,str2);
+	SSGMGameLog::Log_Message(str,"_PURCHASE");
+	SSGMGameLog::Log_Gamelog("PURCHASED;VEHICLE;%s;%s;%s", str3, Get_Definition_Name(preset), str2);
 	delete[] str3;
 	delete[] str2;
-	SSGMGameLog::Log_Message(str,"_PURCHASE");
 	bool fp = FreePurchases;
 	for (int i = 0;i < RegisteredEvents[EVENT_VEHICLE_PURCHASE_HOOK].Count();i++)
 	{
@@ -1002,13 +1027,13 @@
 int SSGMGameManager::CharacterPurchaseHook(BaseControllerClass *base,GameObject *purchaser,unsigned int cost,unsigned int preset,const char *data)
 {
 	SSGM_Soldier *script = (SSGM_Soldier *)Find_Script_On_Object(purchaser,"SSGM_Soldier");
-  if (script && RefillLimit)
+	if (script && RefillLimit)
 	{
 		if (The_Game()->Get_Game_Duration_S() - script->RefillTime < RefillLimit)
 		{
 			return 4;
 		}
-  }
+	}
 	if (IsPresetDisabled(preset))
 	{
 		return 4;
@@ -1017,9 +1042,10 @@
 	const char *str2 = Get_Translated_Definition_Name_Ini(Get_Definition_Name(preset));
 	const char *str3 = Get_Player_Name(purchaser);
 	str.Format("Purchase: %s - %s",str3,str2);
+	SSGMGameLog::Log_Message(str,"_PURCHASE");
+	SSGMGameLog::Log_Gamelog("PURCHASED;CHARACTER;%s;%s;%s", str3, Get_Definition_Name(preset), str2);
 	delete[] str3;
 	delete[] str2;
-	SSGMGameLog::Log_Message(str,"_PURCHASE");
 	bool fp = FreePurchases;
 	for (int i = 0;i < RegisteredEvents[EVENT_CHARACTER_PURCHASE_HOOK].Count();i++)
 	{
diff -ur sourceold/scripts/gmgame.h source/scripts/gmgame.h
--- sourceold/scripts/gmgame.h	2018-11-17 10:23:57.188600000 +1000
+++ source/scripts/gmgame.h	2019-06-22 07:43:44.231400000 +1000
@@ -169,6 +169,7 @@
 SCRIPTS_API const char *Get_Translated_Preset_Name_Ex(GameObject *obj);
 SCRIPTS_API const char *Get_Translated_Preset_Name_Ini(GameObject *obj);
 SCRIPTS_API const char *Get_Translated_Definition_Name_Ini(const char *preset);
+SCRIPTS_API void Log_Killed_Message(GameObject *obj, GameObject *killer, const char *objectType);
 SCRIPTS_API void RegisterEvent(EventType evt,Plugin *plugin);
 SCRIPTS_API void UnregisterEvent(EventType evt,Plugin *plugin);
 SCRIPTS_API bool IsPresetDisabled(int preset);
diff -ur sourceold/scripts/gmsoldier.cpp source/scripts/gmsoldier.cpp
--- sourceold/scripts/gmsoldier.cpp	2018-11-17 10:23:57.191600000 +1000
+++ source/scripts/gmsoldier.cpp	2019-06-22 07:43:44.232400000 +1000
@@ -259,6 +259,8 @@
 {
 	if (!Commands->Is_A_Star(obj))
 	{
+		// Some plugins need bot controlled data too
+		Log_Killed_Message(obj, killer, "SOLDIER");
 		return;
 	}
 	GameObject *preset = GetExplosionObj();
@@ -311,13 +313,8 @@
 		delete[] str5;
 		SSGMGameLog::Log_Message(str,"_PLAYERKILL");
 	}
-	Vector3	victimpos =	Commands->Get_Position(obj);
-	Vector3	damagerpos = Commands->Get_Position(killer);
-	const char *str = Get_Translated_Preset_Name_Ex(obj);
-	const char *str2 = Get_Translated_Preset_Name_Ex(killer);
-	SSGMGameLog::Log_Gamelog("KILLED;SOLDIER;%d;%s;%d;%d;%d;%d;%d;%s;%d;%d;%d;%d;%s;%s;%s",Commands->Get_ID(obj), Commands->Get_Preset_Name(obj), int(victimpos.Y), int(victimpos.X), int(victimpos.Z), int(Commands->Get_Facing(obj)), Commands->Get_ID(killer),	Commands->Get_Preset_Name(killer),	int(damagerpos.Y), int(damagerpos.X), int(damagerpos.Z), int(Commands->Get_Facing(killer)),Get_Current_Weapon(killer),str,str2);
-	delete[] str;
-	delete[] str2;
+
+	Log_Killed_Message(obj, killer, "SOLDIER");
 	Unbind_Vehicle(obj, true);
 	if (ExtraKillMessages)
 	{
diff -ur sourceold/scripts/gmvehicle.cpp source/scripts/gmvehicle.cpp
--- sourceold/scripts/gmvehicle.cpp	2018-11-17 10:23:57.194600000 +1000
+++ source/scripts/gmvehicle.cpp	2019-06-22 07:43:44.233400000 +1000
@@ -412,25 +412,15 @@
 		delete[] str6;
 		SSGMGameLog::Log_Message(str,"_VEHKILL");
 	}
+
+
 	if (Get_Vehicle_Mode(obj) == VEHICLE_TYPE_TURRET)
 	{
-		Vector3	victimpos =	Commands->Get_Position(obj);
-		Vector3	damagerpos = Commands->Get_Position(killer);
-		const char *str = Get_Translated_Preset_Name_Ex(obj);
-		const char *str2 = Get_Translated_Preset_Name_Ex(killer);
-		SSGMGameLog::Log_Gamelog("KILLED;BUILDING;%d;%s;%d;%d;%d;%d;%d;%s;%d;%d;%d;%d;%s;%s;%s",Commands->Get_ID(obj), Commands->Get_Preset_Name(obj), int(victimpos.Y), int(victimpos.X), int(victimpos.Z), int(Commands->Get_Facing(obj)), Commands->Get_ID(killer),	Commands->Get_Preset_Name(killer),	int(damagerpos.Y), int(damagerpos.X), int(damagerpos.Z), int(Commands->Get_Facing(killer)),Get_Current_Weapon(killer),str,str2);
-		delete[] str;
-		delete[] str2;
+		Log_Killed_Message(obj, killer, "BUILDING");
 	}
 	else
 	{
-		Vector3	victimpos =	Commands->Get_Position(obj);
-		Vector3	damagerpos = Commands->Get_Position(killer);
-		const char *str3 = Get_Translated_Preset_Name_Ex(obj);
-		const char *str2 = Get_Translated_Preset_Name_Ex(killer);
-		SSGMGameLog::Log_Gamelog("KILLED;VEHICLE;%d;%s;%d;%d;%d;%d;%d;%s;%d;%d;%d;%d;%s;%s;%s",Commands->Get_ID(obj), Commands->Get_Preset_Name(obj), int(victimpos.Y), int(victimpos.X), int(victimpos.Z), int(Commands->Get_Facing(obj)), Commands->Get_ID(killer),	Commands->Get_Preset_Name(killer),	int(damagerpos.Y), int(damagerpos.X), int(damagerpos.Z), int(Commands->Get_Facing(killer)),Get_Current_Weapon(killer),str3,str2);
-		delete[] str3;
-		delete[] str2;
+		Log_Killed_Message(obj, killer, "VEHICLE");
 	}
 }
 
diff -ur sourceold/scripts/jmgBearHunter.cpp source/scripts/jmgBearHunter.cpp
--- sourceold/scripts/jmgBearHunter.cpp	2018-11-17 10:23:57.230600000 +1000
+++ source/scripts/jmgBearHunter.cpp	2019-06-22 07:43:44.237400000 +1000
@@ -1864,6 +1864,8 @@
 }
 void JMG_Rp2_Hostile_Mutant_AI::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore"))
@@ -2172,6 +2174,8 @@
 }
 void JMG_Bear_Hunt_Mutant_Attacker::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore"))
@@ -2468,6 +2472,8 @@
 }
 void JMG_Security_Camera_Behavior::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Security_Camera_Behavior_Ignore"))
 		return;
 	if (!JmgUtility::CanSeeStealth(stealthModeOverride,obj,seen))
@@ -3286,6 +3292,8 @@
 }
 void JMG_Bear_Hunt_Final_Boss::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore_Up_To_25_Meters") && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 625)
@@ -3671,6 +3679,8 @@
 }
 void JMG_Bear_Hunter_Player_Assist_AI::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenId = Commands->Get_ID(seen);
 	if (!enemyId || enemyId == seenId || !lastSeen || JmgUtility::SimpleDistance(Commands->Get_Position(seen),Commands->Get_Position(obj)) < enemyDistance)
 	{
@@ -4330,6 +4340,8 @@
 }
 void JMG_AI_Follow_Player_When_Near::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (targetId && lastSeenTime)
@@ -4491,6 +4503,8 @@
 }
 void JMG_Bear_Hunter_Engineer_Follow_Player_When_Near::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Get_Vehicle(obj) || Is_Script_Attached(seen,"JMG_Bear_Hunter_Engineer_Ignore"))
 		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
@@ -4737,6 +4751,8 @@
 }
 void JMG_Bear_Hunter_Engineer_AI::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore_Up_To_25_Meters") && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 625)
@@ -5230,6 +5246,8 @@
 }
 void JMG_Bear_Hunter_Giant_Deer_Boss::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore"))
@@ -5689,6 +5707,8 @@
 }
 void JMG_Bear_Hunter_Guardian_Aircraft::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (!_stricmp(Get_Skin(seen),"Blamo") || !_stricmp(Get_Shield_Type(seen),"Blamo") || !Commands->Get_Health(seen))
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_Player_Vehicle"))
@@ -6021,6 +6041,8 @@
 {
 	if (!cameraActive)
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenID = Commands->Get_ID(seen);
 	if (!EnemyID)
 	{
@@ -6415,6 +6437,8 @@
 }
 void JMG_Wandering_AI::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore"))
@@ -6734,6 +6758,10 @@
 	JMG_Wandering_AI_Controller::setup = false;
 	JMG_Wandering_AI_Controller::wanderPoints.Empty_List();
 }
+void JMG_Wandering_AI_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Wandering_AI_Wander_Point::Created(GameObject *obj)
 {
 	if (!JMG_Wandering_AI_Controller::setup)
@@ -6759,6 +6787,47 @@
 	node->value = Get_Int_Parameter("GroupId");
 	JMG_Wandering_AI_Controller::wanderPoints += node;
 }
+void JMG_Wandering_AI_Wander_Point_Mobile::Created(GameObject *obj)
+{
+	if (!JMG_Wandering_AI_Controller::setup)
+	{
+		Console_Input("msg JMG_Wandering_AI_Wander_Point_Mobile ERROR:: Make sure you have the script JMG_Wandering_AI_Controller placed on the map!");
+		return;
+	}
+	Commands->Start_Timer(obj,this,0.25f,1);
+}
+void JMG_Wandering_AI_Wander_Point_Mobile::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		node = new Rp2SimplePositionSystem::SimplePositionNode(obj);
+		node->value = Get_Int_Parameter("GroupId");
+		faceObjectId = Get_Int_Parameter("FaceObjectID");
+		JMG_Wandering_AI_Controller::wanderPoints += node;
+		Commands->Start_Timer(obj,this,0.1f,2);
+	}
+	if (number == 2)
+	{
+		if (!JMG_Wandering_AI_Controller::setup)
+		{
+			Destroy_Script();
+			return;
+		}
+		node->position = Commands->Get_Position(obj);
+		if (faceObjectId)
+		{
+			GameObject *target = Commands->Find_Object(faceObjectId);
+			Vector3 facePos = Commands->Get_Position(target);
+			Vector3 myPos = Commands->Get_Position(obj);
+			facePos.X -= myPos.X;
+			facePos.Y -= myPos.Y;
+			node->facing = atan2(facePos.Y,facePos.X)*(180.0f/PI);
+		}
+		else
+			node->facing = Commands->Get_Facing(obj);
+		Commands->Start_Timer(obj,this,0.1f,2);
+	}
+}
 void JMG_Utility_Custom_Spawn_System_Controller::Destroyed(GameObject *obj)
 {
 	JMG_Utility_Custom_Spawn_System_Controller::setup = false;
@@ -6997,6 +7066,8 @@
 }
 void JMG_Bear_Hunter_Wolf::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (Is_Script_Attached(seen,"JMG_Bear_Hunter_AI_Ignore"))
@@ -7767,6 +7838,8 @@
 }
 void JMG_Bear_Hunter_AI_Avoid_Enemies::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (fleeTime < originalFleeTime)
 		setRetreatLocation(obj,seen);
 }
@@ -7902,6 +7975,7 @@
 ScriptRegistrant<JMG_Wandering_AI_Controller> JMG_Wandering_AI_Controller_Registrant("JMG_Wandering_AI_Controller","");
 ScriptRegistrant<JMG_Wandering_AI_Wander_Point> JMG_Wandering_AI_Wander_Point_Registrant("JMG_Wandering_AI_Wander_Point","GroupId:int");
 ScriptRegistrant<JMG_Wandering_AI_Wander_Point_Dont_Remove> JMG_Wandering_AI_Wander_Point_Dont_Remove_Registrant("JMG_Wandering_AI_Wander_Point_Dont_Remove","GroupId:int");
+ScriptRegistrant<JMG_Wandering_AI_Wander_Point_Mobile> JMG_Wandering_AI_Wander_Point_Mobile_Registrant("JMG_Wandering_AI_Wander_Point_Mobile","GroupId:int,FaceObjectID:int");
 ScriptRegistrant<JMG_Utility_Custom_Spawn_System_Controller> JMG_Utility_Custom_Spawn_System_Controller_Registrant("JMG_Utility_Custom_Spawn_System_Controller","");
 ScriptRegistrant<JMG_Utility_Custom_Spawn_System_Point> JMG_Utility_Custom_Spawn_System_Point_Registrant("JMG_Utility_Custom_Spawn_System_Point","GroupId:int");
 ScriptRegistrant<JMG_Utility_Custom_Spawn_System> JMG_Utility_Custom_Spawn_System_Registrant("JMG_Utility_Custom_Spawn_System","Spawn_Point_Group=-1:int,Max_Spawned_At_Once=1:int,Spawn_Limit=-1:int,Respawn_Delay=10.0:float,Random_Delay=0.0:float,Delay_First_Spawn=0:int,Starts_Enabled=1:int,Enable_Message=403401,Disable_Message=403400,Preset0=null:string,Preset1=null:string,Preset2=null:string,Preset3=null:String,Preset4=null:string,Preset5=null:string,Preset6=null:string,Preset7=null:string,Preset8=null:string,Preset9=null:string,Chance0=0:int,Chance1=0:int,Chance2=0:int,Chance3=0:int,Chance4=0:int,Chance5=0:int,Chance6=0:int,Chance7=0:int,Chance8=0:int,Chance9=0:int");
diff -ur sourceold/scripts/jmgBearHunter.h source/scripts/jmgBearHunter.h
--- sourceold/scripts/jmgBearHunter.h	2018-11-17 10:23:57.231600000 +1000
+++ source/scripts/jmgBearHunter.h	2019-06-22 07:43:44.238400000 +1000
@@ -3217,6 +3217,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static int aiIgnorePlayers[128];
 	static float soundAttentionWhoreNotify;
@@ -3239,6 +3240,12 @@
 class JMG_Wandering_AI_Wander_Point_Dont_Remove : public ScriptImpClass {
 	void Created(GameObject *obj);
 };
+class JMG_Wandering_AI_Wander_Point_Mobile : public ScriptImpClass {
+	Rp2SimplePositionSystem::SimplePositionNode *node;
+	int faceObjectId;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+};
 class JMG_Utility_Custom_Spawn_System_Controller : public ScriptImpClass {
 	void Destroyed(GameObject *obj);
 public:
diff -ur sourceold/scripts/jmgMetroid.cpp source/scripts/jmgMetroid.cpp
--- sourceold/scripts/jmgMetroid.cpp	2018-11-17 10:23:57.234600000 +1000
+++ source/scripts/jmgMetroid.cpp	2019-06-22 07:43:44.241400000 +1000
@@ -810,6 +810,8 @@
 }
 void JMG_Metroid_Move_To_Random_Ambush_Spot::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (!EnemySeen)
 		currentEnemyID = Commands->Get_ID(seen);
 	EnemySeen = 5;
@@ -1532,6 +1534,8 @@
 }
 void JMG_Metroid_Boss_Turret::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_VehicleGameObj())
 	{
 		int SeatCount = Get_Vehicle_Seat_Count(seen);
@@ -2492,6 +2496,8 @@
 }
 void JMG_Metroid_AI_Hunt_Attack::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenID = Commands->Get_ID(seen);
 	if (!EnemyID || seenID == EnemyID)
 	{
@@ -3412,6 +3418,8 @@
 }
 void JMG_Metroid_AI_Hunt_Equipment::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenID = Commands->Get_ID(seen);
 	if (!EnemyID || seenID == EnemyID)
 	{
@@ -3553,6 +3561,8 @@
 	GameObject *vehicle = Get_Vehicle(seen);
 	if (vehicle)
 		seen = vehicle;
+	if (!Commands->Get_Health(seen))
+		return;
 	float distance = JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen));
 	int seenId = Commands->Get_ID(seen);
 	if (!enemyID && distance >= MinDist && distance <= MaxDist)
@@ -3613,6 +3623,8 @@
 }
 void JMG_Metroid_Camera_Behavior::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenID = Commands->Get_ID(seen);
 	if (!EnemyID)
 	{
@@ -3781,6 +3793,8 @@
 }
 void JMG_Metroid_AI_Snow_Mini_Boss::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int SeenID = Commands->Get_ID(seen);
 	if (!currentTargetID || !LastSeen)
 	{
@@ -4923,6 +4937,8 @@
 }
 void JMG_Metroid_AI_Forest_Mini_Boss::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int SeenID = Commands->Get_ID(seen);
 	if (!currentTargetID || !LastSeen)
 	{
diff -ur sourceold/scripts/jmgUtility.cpp source/scripts/jmgUtility.cpp
--- sourceold/scripts/jmgUtility.cpp	2019-02-15 09:37:36.768000000 +1000
+++ source/scripts/jmgUtility.cpp	2019-06-22 07:43:44.244400000 +1000
@@ -11,6 +11,7 @@
 #include "BuildingGameObj.h"
 #include "engine_player.h"
 #include "DefinitionMgrClass.h"
+#include "AudibleSoundDefinitionClass.h"
 bool JmgUtility::hasStatedDeathMessage[128] = {false};
 
 void JMG_Utility_Check_If_Script_Is_In_Library::Created(GameObject *obj)
@@ -197,9 +198,11 @@
 void JMG_Utility_Display_HUD_Info_Text_All_Players_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
 	if (message == Get_Int_Parameter("Custom"))
+	{
 		JmgUtility::SetHUDHelpText(Get_Int_Parameter("StringId"),Get_Vector3_Parameter("ColorRGB"));
-	if (!Get_Int_Parameter("Repeatable"))
-		Destroy_Script();
+		if (!Get_Int_Parameter("Repeatable"))
+			Destroy_Script();
+	}
 }
 void JMG_Utility_Display_HUD_Info_Text_To_Sender_On_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
@@ -313,9 +316,7 @@
 }
 void JMG_Turret_Spawn::Killed(GameObject *obj,GameObject *killer)
 {
-	GameObject *turret = Commands->Find_Object(turretId);
-	if (turret)
-		Commands->Destroy_Object(turret);
+	Destroyed(obj);
 }
 void JMG_Turret_Spawn::Destroyed(GameObject *obj)
 {
@@ -323,6 +324,12 @@
 	if (turret)
 		Commands->Destroy_Object(turret);
 }
+void JMG_Turret_Spawn::Detach(GameObject *obj)
+{
+	if (Exe == 4)
+		return;
+	Destroyed(obj);
+}
 ClientNetworkObjectPositionSync *clientNetworkObjectPositionSyncControl = NULL;
 void JMG_Utility_Sync_System_Object::Created(GameObject *obj)
 {
@@ -401,6 +408,10 @@
 	clientNetworkObjectPositionSyncControl->Empty_List();
 	clientNetworkObjectPositionSyncControl = NULL;
 }
+void JMG_Utility_Sync_System_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Sync_Object_Periodically::Created(GameObject *obj)
 {
 	Commands->Start_Timer(obj,this,0.25f,1);
@@ -419,6 +430,7 @@
 	spawnedId = 0;
 	spawnLimit = Get_Int_Parameter("SpawnLimit");
 	enabled = Get_Int_Parameter("StartsEnabled") ? true : false;
+	attachScriptsGroupId = Get_Int_Parameter("Attach_Scripts_Group_ID");
 	CalculateRespawnTime();
 	Commands->Disable_All_Collisions(obj);
 	Commands->Set_Is_Rendered(obj,false);
@@ -447,7 +459,7 @@
 					char params[256];
 					sprintf(params,"%d",Commands->Get_ID(obj));
 					Commands->Attach_Script(spawnedObject,"JMG_Utility_Basic_Spawner_Spawned_Object",params);
-					Commands->Send_Custom_Event(spawnedObject,obj,6873522,6873522,0.0f);
+					Commands->Send_Custom_Event(spawnedObject,obj,6873522,attachScriptsGroupId,0.0f);
 				}
 				else
 				{
@@ -566,6 +578,8 @@
 		return;
 	if (aiIgnorePlayers[JmgUtility::JMG_Get_Player_ID(seen)])
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	targetId = Commands->Get_ID(seen);
 	targetUpdate = 20;
 }
@@ -965,6 +979,8 @@
 		return;
 	if (Get_Float_Parameter("MinAttackRange") && dist < minDistanceSquared)
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed())
 		if (seen->As_SoldierGameObj() && dist > seen->As_SoldierGameObj()->Get_Stealth_Fade_Distance()*seen->As_SoldierGameObj()->Get_Stealth_Fade_Distance())
 			return;
@@ -1503,6 +1519,10 @@
 {
 	controllerPlaced = false;
 }
+void JMG_Utility_Play_Music_On_Join_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Play_Music_On_Join_Controller::Set_Music(const char *musicFilName,int fadeOut,int fadeIn)
 {
 	sprintf(JMG_Utility_Play_Music_On_Join_Controller::musicFileName,"%s",musicFilName);
@@ -1801,9 +1821,11 @@
 void JMG_Utility_Display_HUD_Info_Text_All_Players_Custom_Replace_String::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
 	if (message == Get_Int_Parameter("Custom"))
+	{
 		JmgUtility::SetHUDHelpText(Get_Int_Parameter("StringId"),Get_Parameter("ReplaceString"),Get_Vector3_Parameter("ColorRGB"));
-	if (!Get_Int_Parameter("Repeatable"))
-		Destroy_Script();
+		if (!Get_Int_Parameter("Repeatable"))
+			Destroy_Script();
+	}
 }
 void JMG_Utility_Display_Text_Message_To_All_Players_On_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
@@ -1852,6 +1874,8 @@
 }
 void JMG_Utility_AI_Aggressive_Melee::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	int SeenID = Commands->Get_ID(seen);
 	if (!currentTargetID || !LastSeen)
 	{
@@ -2498,11 +2522,18 @@
 }
 void JMG_Utility_AI_Guardian_Aircraft::Created(GameObject *obj)
 {
+	newPointDelay = Get_Int_Parameter("NewPointDelay");
+	remainingDelay = 0;
 	dpPosition = Commands->Get_Position(obj);
 	EnemyID = 0;
 	EnemyTimeOutTime = 0;
 	LastPos = Commands->Get_Position(obj);
 	stealthModeOverride = Get_Int_Parameter("StealthModeOverride");
+	float weaponRange = 0.0f;
+	const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(Get_Current_Weapon(obj),true);
+	if (ammo)
+		weaponRange = ammo->Range;
+	fireRange = Get_Float_Parameter("FireRange") >= 0 ? Get_Float_Parameter("FireRange") : weaponRange;
 	Commands->Enable_Enemy_Seen(obj,true);
 	Commands->Enable_Engine(obj,true);
 	Commands->Start_Timer(obj,this,1.0f,1);
@@ -2530,9 +2561,14 @@
 		Vector3 Pos = Commands->Get_Position(obj);
 		if (JmgUtility::SimpleDistance(Pos,LastPos) < 25.0)
 		{
-			if (Get_A_Defense_Point(&dpPosition))
+			if (!remainingDelay && Get_A_Defense_Point(&dpPosition))
+			{
+				remainingDelay = newPointDelay;
 				Goto_Location(obj);
+			}
 		}
+		if (remainingDelay)
+			remainingDelay--;
 		LastPos = Pos;
 		Commands->Start_Timer(obj,this,1.0f,1);
 	}
@@ -2543,6 +2579,8 @@
 		return;
 	if (!JmgUtility::CanSeeStealth(stealthModeOverride,obj,seen))
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	if (!EnemyID || !EnemyTimeOutTime)
 	{
 		EnemyID = Commands->Get_ID(seen);
@@ -2576,7 +2614,7 @@
 			params.Set_Face_Location(Commands->Get_Position(Target),5.0f);
 		else
 			params.AttackFaceTarget = false;
-		params.Set_Attack(Target,Get_Float_Parameter("FireRange"),0,true);
+		params.Set_Attack(Target,fireRange,0,true);
 		Commands->Action_Attack(obj,params);
 	}
 	else
@@ -2748,6 +2786,8 @@
 		return;
 	if (!_stricmp(Get_Skin(seen),"Blamo") || !_stricmp(Get_Shield_Type(seen),"Blamo"))
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Get_Int_Parameter("CanSeeStealth") != 2 && seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed())
 	{
 		if (!Get_Int_Parameter("CanSeeStealth"))
@@ -3207,6 +3247,8 @@
 }
 void JMG_Utility_AI_Aggressive_Attack_Spot::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if ((state == IDLE || state == RETURNING_HOME) && (!targetId || !lastSeenTime || Choose_Target(obj,seen)))
 	{
 		targetId = Commands->Get_ID(seen);
@@ -3505,6 +3547,8 @@
 }
 void JMG_Utility_Basic_Spawner_Attach_Script::Created(GameObject *obj)
 {
+	scriptsGroupId = Get_Int_Parameter("Scripts_Group_ID");
+	sprintf(script,"%s",Get_Parameter("Script"));
 	const char *parameters = Get_Parameter("Params");
 	params = newstr(parameters);
 	char delim = Get_Parameter("Delim")[0];
@@ -3515,9 +3559,9 @@
 }
 void JMG_Utility_Basic_Spawner_Attach_Script::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
-	if (message == 6873522)
+	if (message == 6873522 && (scriptsGroupId == -1 || param == -1 || scriptsGroupId == param))
 	{
-		Commands->Attach_Script(sender,Get_Parameter("Script"),params);
+		Commands->Attach_Script(sender,script,params);
 	}
 }
 void JMG_Utility_Send_Custom_On_Preset_Enter::Entered(GameObject *obj,GameObject *enter)
@@ -3680,20 +3724,26 @@
 }
 void JMG_Utility_Objective_System_Controller::Destroyed(GameObject *obj)
 {
+	if (!controllerPlaced)
+		return;
 	BasicObjectiveSystem.~NewObjectiveSystem();
 	controllerPlaced = false;
 }
+void JMG_Utility_Objective_System_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Objective_System_Objective_Update_Enter::Entered(GameObject *obj,GameObject *enter)
 {
+	if (!Commands->Is_A_Star(enter) || triggered)
+		return;
 	if (!JMG_Utility_Objective_System_Controller::controllerPlaced)
 	{
 		Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Make sure you have a JMG_Utility_Objective_System_Controller placed on the map!");
 		return;
 	}
-	if (!Commands->Is_A_Star(enter) || triggered)
-		return;
 	triggered = true;
-	if (Get_Int_Parameter("CompleteObjectiveID"))
+	if (Get_Int_Parameter("CompleteObjectiveID") && BasicObjectiveSystem.Get_Objective_Status(Get_Int_Parameter("CompleteObjectiveID")) != NewObjectiveSystem::Failed)
 	{
 		BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
 		BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
@@ -3732,7 +3782,7 @@
 		}
 		if (!Commands->Is_A_Star(sender))
 			return;
-		if (Get_Int_Parameter("CompleteObjectiveID"))
+		if (Get_Int_Parameter("CompleteObjectiveID") && BasicObjectiveSystem.Get_Objective_Status(Get_Int_Parameter("CompleteObjectiveID")) != NewObjectiveSystem::Failed)
 		{
 			BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
 			BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
@@ -3741,7 +3791,7 @@
 		GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? sender : Commands->Find_Object(objectiveMarkerId);
 		if (!ObjectiveMarker && objectiveMarkerId)
 		{
-			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Pickup ERROR: Could not find objective marker object!");
 			return;
 		}
 		if (ObjectiveMarker)
@@ -3760,7 +3810,7 @@
 		Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Killed ERROR: Make sure you have a JMG_Utility_Objective_System_Controller placed on the map!");
 		return;
 	}
-	if (Get_Int_Parameter("CompleteObjectiveID"))
+	if (Get_Int_Parameter("CompleteObjectiveID") && BasicObjectiveSystem.Get_Objective_Status(Get_Int_Parameter("CompleteObjectiveID")) != NewObjectiveSystem::Failed)
 	{
 		BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
 		BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
@@ -3769,7 +3819,7 @@
 	GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? damager : Commands->Find_Object(objectiveMarkerId);
 	if (!ObjectiveMarker && objectiveMarkerId)
 	{
-		Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+		Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Killed ERROR: Could not find objective marker object!");
 		return;
 	}
 	if (ObjectiveMarker)
@@ -3787,7 +3837,7 @@
 			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Custom ERROR: Make sure you have a JMG_Utility_Objective_System_Controller placed on the map!");
 			return;
 		}
-		if (Get_Int_Parameter("CompleteObjectiveID"))
+		if (Get_Int_Parameter("CompleteObjectiveID") && BasicObjectiveSystem.Get_Objective_Status(Get_Int_Parameter("CompleteObjectiveID")) != NewObjectiveSystem::Failed)
 		{
 			BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
 			BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
@@ -3796,7 +3846,7 @@
 		GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? sender : Commands->Find_Object(objectiveMarkerId);
 		if (!ObjectiveMarker && objectiveMarkerId)
 		{
-			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Custom ERROR: Could not find objective marker object!");
 			return;
 		}
 		if (ObjectiveMarker)
@@ -4062,6 +4112,11 @@
 	EnemyTimeOutTime = 0;
 	LastPos = Commands->Get_Position(obj);
 	stealthModeOverride = Get_Int_Parameter("StealthModeOverride");
+	float weaponRange = 0.0f;
+	const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(Get_Current_Weapon(obj),true);
+	if (ammo)
+		weaponRange = ammo->Range;
+	fireRange = Get_Float_Parameter("FireRange") >= 0 ? Get_Float_Parameter("FireRange") : weaponRange;
 	Commands->Enable_Enemy_Seen(obj,true);
 	Commands->Enable_Engine(obj,true);
 	Commands->Start_Timer(obj,this,1.0f,1);
@@ -4100,6 +4155,8 @@
 }
 void JMG_Utility_AI_Guardian_Infantry::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_AI_Guardian_Ignored"))
 		return;
 	if (!JmgUtility::CanSeeStealth(stealthModeOverride,obj,seen))
@@ -4136,7 +4193,7 @@
 			params.Set_Face_Location(Commands->Get_Position(Target),5.0f);
 		else
 			params.AttackFaceTarget = false;
-		params.Set_Attack(Target,Get_Float_Parameter("FireRange"),0,true);
+		params.Set_Attack(Target,fireRange,0,true);
 		params.AttackCheckBlocked = Get_Int_Parameter("CheckBlocked") ? true : false;
 		Commands->Action_Attack(obj,params);
 	}
@@ -4533,6 +4590,7 @@
 	playerType = Get_Int_Parameter("PlayerType");
 	safeTeleportDistance = Get_Float_Parameter("SafeTeleportDistance");
 	wanderPointGroup = Get_Int_Parameter("WanderingAIGroupID");
+	aiOnly = Get_Int_Parameter("AiOnly") != 0;
 }
 void JMG_Utility_Zone_Teleport_To_Random_Wander_Point::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
@@ -4543,6 +4601,8 @@
 }
 void JMG_Utility_Zone_Teleport_To_Random_Wander_Point::Entered(GameObject *obj,GameObject *enter)
 {
+	if (aiOnly && Commands->Is_A_Star(enter))
+		return;
 	if (CheckPlayerType(enter,playerType))
 		return;
 	Grab_Teleport_Spot(enter,5);
@@ -4598,7 +4658,7 @@
 {
 	safeTeleportDistance = Get_Float_Parameter("SafeTeleportDistance");
 	wanderPointGroup = Get_Int_Parameter("WanderingAIGroupID");
-	Commands->Start_Timer(obj,this,0.25f,1);
+	Commands->Start_Timer(obj,this,0.1f,1);
 }
 void JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach::Timer_Expired(GameObject *obj,int number)
 {
@@ -5003,21 +5063,30 @@
 {
 	if (!JMG_Utility_Objective_System_Controller::controllerPlaced)
 	{
-		Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Create ERROR: Make sure you have a JMG_Utility_Objective_System_Controller placed on the map!");
-		return;
-	}
-	if (triggered)
+		Commands->Start_Timer(obj,this,0.1f,1);
 		return;
-	triggered = true;
-	if (Get_Int_Parameter("CompleteObjectiveID"))
-	{
-		BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
-		BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
 	}
-	Commands->Start_Timer(obj,this,Get_Float_Parameter("Delay"),1);
+	Timer_Expired(obj,2);
 }
 void JMG_Utility_Objective_System_Objective_Update_Create::Timer_Expired(GameObject *obj,int number)
 {
+	if (number == 2)
+	{
+		if (!JMG_Utility_Objective_System_Controller::controllerPlaced)
+		{
+			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Create ERROR: Make sure you have a JMG_Utility_Objective_System_Controller placed on the map!");
+			return;
+		}
+		if (triggered)
+			return;
+		triggered = true;
+		if (Get_Int_Parameter("CompleteObjectiveID") && BasicObjectiveSystem.Get_Objective_Status(Get_Int_Parameter("CompleteObjectiveID")) != NewObjectiveSystem::Failed)
+		{
+			BasicObjectiveSystem.Add_Objective(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
+			BasicObjectiveSystem.Set_Objective_Status(Get_Int_Parameter("CompleteObjectiveID"),NewObjectiveSystem::Accomplished);
+		}
+		Commands->Start_Timer(obj,this,Get_Float_Parameter("Delay"),1);
+	}
 	if (number == 1)
 	{
 		if (Get_Int_Parameter("NewObjectiveID"))
@@ -5026,7 +5095,7 @@
 			GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? obj : Commands->Find_Object(objectiveMarkerId);
 			if (!ObjectiveMarker && objectiveMarkerId)
 			{
-				Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+				Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Create ERROR: Could not find objective marker object!");
 				return;
 			}
 			if (ObjectiveMarker)
@@ -5277,6 +5346,8 @@
 }
 void JMG_Utility_AI_Goto_Enemy::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_AI_Goto_Enemy_Ignore_Object"))
 		return;
 	if (!_stricmp(Get_Skin(seen),"Blamo") || !_stricmp(Get_Shield_Type(seen),"Blamo"))
@@ -5691,6 +5762,8 @@
 }
 void JMG_Utility_AI_Goto_Enemy_Not_Star::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_AI_Goto_Enemy_Not_Star_Ignore_Object"))
 		return;
 	if (!_stricmp(Get_Skin(seen),"Blamo") || !_stricmp(Get_Shield_Type(seen),"Blamo"))
@@ -6155,6 +6228,11 @@
 	EnemyTimeOutTime = 0;
 	LastPos = Commands->Get_Position(obj);
 	stealthModeOverride = Get_Int_Parameter("StealthModeOverride");
+	float weaponRange = 0.0f;
+	const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(Get_Current_Weapon(obj),true);
+	if (ammo)
+		weaponRange = ammo->Range;
+	fireRange = Get_Float_Parameter("FireRange") >= 0 ? Get_Float_Parameter("FireRange") : weaponRange;
 	Commands->Enable_Enemy_Seen(obj,true);
 	Commands->Enable_Engine(obj,true);
 	Commands->Start_Timer(obj,this,1.0f,1);
@@ -6223,6 +6301,8 @@
 }
 void JMG_Utility_AI_Guardian_Vehicle::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_AI_Guardian_Ignored"))
 		return;
 	if (!JmgUtility::CanSeeStealth(stealthModeOverride,obj,seen))
@@ -6264,10 +6344,10 @@
 		if (aimAtFeet && Target->As_SoldierGameObj())
 		{
 			params.AttackCheckBlocked = false;
-			params.Set_Attack(Commands->Get_Position(Target),Get_Float_Parameter("FireRange"),0,true);
+			params.Set_Attack(Commands->Get_Position(Target),fireRange,0,true);
 		}
 		else
-			params.Set_Attack(Target,Get_Float_Parameter("FireRange"),0,true);
+			params.Set_Attack(Target,fireRange,0,true);
 		Commands->Action_Attack(obj,params);
 	}
 	else
@@ -6325,6 +6405,8 @@
 int JMG_Utility_Send_Custom_On_Deaths_Controller::reminderMessageOrder = 0;
 int JMG_Utility_Send_Custom_On_Deaths_Controller::myId = 0;
 bool JMG_Utility_Send_Custom_On_Deaths_Controller::controllerPlaced = false;
+bool JMG_Utility_Send_Custom_On_Deaths_Controller::announceOnFirstDeath = false;
+bool JMG_Utility_Send_Custom_On_Deaths_Controller::onlyTrackPlayerDeaths = false;
 void JMG_Utility_Send_Custom_On_Deaths_Controller::Created(GameObject *obj)
 {
 	if (controllerPlaced)
@@ -6355,13 +6437,16 @@
 	if (addDeathsWhenNoPlayers > 0)
 		Commands->Start_Timer(obj,this,addDeathsWhenNoPlayers,1);
 	controllerPlaced = true;
+	requiresADeathToStartNoPlayerAdd = Get_Int_Parameter("RequiresADeathToStartNoPlayerAdd") ? true : false;
+	announceOnFirstDeath = Get_Int_Parameter("AnnounceOnFirstDeath") ? true : false;
+	onlyTrackPlayerDeaths = Get_Int_Parameter("OnlyTrackPlayerDeaths") ? true : false;
 	myId = Commands->Get_ID(obj);
 }
 void JMG_Utility_Send_Custom_On_Deaths_Controller::Timer_Expired(GameObject *obj,int number)
 {
 	if (number == 1)
 	{
-		if (!Get_Player_Count())
+		if (!Get_Player_Count() && (!requiresADeathToStartNoPlayerAdd || JMG_Utility_Send_Custom_On_Deaths_Controller::deathCount))
 		{
 			if (currentNoPlayersAddDeathSaftyTime)
 				currentNoPlayersAddDeathSaftyTime--;
@@ -6382,6 +6467,10 @@
 {
 	controllerPlaced = false;
 }
+void JMG_Utility_Send_Custom_On_Deaths_Controller::Detach(GameObject *obj)
+{
+	controllerPlaced = false;
+}
 void JMG_Utility_Send_Custom_On_Deaths_Controller::Custom(GameObject *obj,int message,int param,GameObject *sender)
 {
 	if (message == 8765432 && param == 8765432)
@@ -6403,9 +6492,11 @@
 {
 	if (!controllerPlaced)
 		return;
+	if (onlyTrackPlayerDeaths && !Commands->Is_A_Star(obj))
+		return;
 	deathCount++;
 	int remainingLives = maxDeaths-deathCount;
-	if ((!(remainingLives%deathReminder) || remainingLives <= urgentDeathReminder))
+	if ((!(remainingLives%deathReminder) || remainingLives <= urgentDeathReminder) || (deathCount == 1 && announceOnFirstDeath))
 	{
 		if (remainingLives > 0)
 		{
@@ -6564,6 +6655,10 @@
 	Update_Colors(Vector3(),0.0f);
 	controllerPlaced = false;
 }
+void JMG_Utility_Set_Screen_Color_Fade_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Set_Screen_Color_Fade_Controller::Update_Colors(Vector3 Color,float Opacity)
 {
 	controllerDefaultColor = Color;
@@ -6782,6 +6877,8 @@
 	}
 }
 JMG_Utility_HUD_Count_Down::SendCustomOnSecondNode *JMG_Utility_HUD_Count_Down::sendCustomOnSecondController = NULL;
+JMG_Utility_HUD_Count_Down::CountdownScreenCharacterNode *JMG_Utility_HUD_Count_Down::countdownScreenCharacterController = NULL;
+int JMG_Utility_HUD_Count_Down::highestCharPos = -1;
 bool JMG_Utility_HUD_Count_Down::controllerPlaced = false;
 void JMG_Utility_HUD_Count_Down::Created(GameObject *obj)
 {
@@ -6794,6 +6891,8 @@
 	sprintf(minutePlural,"%s",Get_Parameter("MinutePlural"));
 	sprintf(secondSingular,"%s",Get_Parameter("SecondSingular"));
 	sprintf(secondPlural,"%s",Get_Parameter("SecondPlural"));
+	pauseCustom = Get_Int_Parameter("PauseCustom");
+	paused = Get_Int_Parameter("StartsPaused") != 0;
 	const char *parameters = Get_Parameter("WarningMessage");
 	sprintf(warningMessage,"%s",parameters);
 	char delim = Get_Parameter("Delim")[0];
@@ -6803,14 +6902,17 @@
 			warningMessage[x] = ',';
 	controllerPlaced = true;
 	Commands->Start_Timer(obj,this,1.0f,1);
+	if (paused)
+		Commands->Start_Timer(obj,this,1.0f,2);
 }
 void JMG_Utility_HUD_Count_Down::Timer_Expired(GameObject *obj,int number)
 {
 	if (number == 1)
 	{
-		if (seconds)
+		if (seconds && !paused)
 		{
 			seconds--;
+			UpdateCountdownHudNodes();
 			if (seconds >= 3600 && !(seconds % 3600))
 				JmgUtility::SetHUDHelpText(stringId,formatReminderString(warningMessage,seconds/3600,seconds/3600 == 1 ? hourSingular : hourPlural),color);
 			else if (seconds >= 60 && (seconds == 1800 || seconds == 1200 || seconds == 600 || (seconds <= 300 && !(seconds % 60))))
@@ -6821,11 +6923,26 @@
 		}
 		Commands->Start_Timer(obj,this,1.0f,1);
 	}
+	if (number == 2)
+	{
+		UpdateCountdownHudNodes();
+	}
+}
+void JMG_Utility_HUD_Count_Down::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == pauseCustom)
+	{
+		paused = param != 0;
+	}
 }
 void JMG_Utility_HUD_Count_Down::Destroyed(GameObject *obj)
 {
-	controllerPlaced = false;
-	CleanupSecondNodes();
+	if (controllerPlaced)
+		CleanupSecondNodes();
+}
+void JMG_Utility_HUD_Count_Down::Detach(GameObject *obj)
+{
+	Destroyed(obj);
 }
 char *JMG_Utility_HUD_Count_Down::formatReminderString(const char *format,...)
 {
@@ -7103,6 +7220,10 @@
 {
 	JMG_Utility_AI_Follow_Player_On_Poke_Controller::controllerPlaced = false;
 }
+void JMG_Utility_AI_Follow_Player_On_Poke_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_AI_Follow_Player_On_Poke::Created(GameObject *obj)
 {
 	if (!JMG_Utility_AI_Follow_Player_On_Poke_Controller::controllerPlaced)
@@ -7161,6 +7282,8 @@
 }
 void JMG_Utility_AI_Follow_Player_On_Poke::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (seen->As_SmartGameObj() && seen->As_SmartGameObj()->Is_Stealthed() && JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen)) > 900)
 		return;
 	if (enemyId == Commands->Get_ID(seen))
@@ -7645,6 +7768,7 @@
 						this->Destroy_Script();
 						return;
 					}
+					sleeping = sleepTime;
 				}
 			}
 		Commands->Start_Timer(obj,this,0.1f,1);
@@ -7652,7 +7776,7 @@
 }
 void JMG_Utility_Set_Team_To_Neutral_When_No_Armor::Created(GameObject *obj)
 {
-	hurt = false;
+	hurt = Commands->Get_Shield_Strength(obj) < Commands->Get_Max_Shield_Strength(obj);
 	originalPlayerType = Commands->Get_Player_Type(obj);
 	Commands->Start_Timer(obj,this,0.1f,1);
 }
@@ -7981,7 +8105,7 @@
 		GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? sender : Commands->Find_Object(objectiveMarkerId);
 		if (!ObjectiveMarker && objectiveMarkerId)
 		{
-			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+			Console_Input("msg JMG_Utility_Objective_System_Objective_Status_Update_Custom ERROR: Could not find objective marker object!");
 			return;
 		}
 		int stringId = Get_Int_Parameter("NewObjectiveStringID");
@@ -7989,6 +8113,8 @@
 		BasicObjectiveSystem.Set_Objective_Mission(objectiveId,stringId,stringId);
 		if (ObjectiveMarker)
 			BasicObjectiveSystem.Set_Radar_Blip(objectiveId,ObjectiveMarker,NULL);
+		else if (!objectiveMarkerId)
+			BasicObjectiveSystem.Remove_Radar_Blip(objectiveId);
 		JmgUtility::SetHUDHelpText(stringId,BasicObjectiveSystem.Get_Hud_Help_Text_Color((NewObjectiveSystem::Priority)BasicObjectiveSystem.Get_Objective_Priority(objectiveId)));
 	}
 }
@@ -8038,6 +8164,11 @@
 	EnemyTimeOutTime = 0;
 	LastPos = Commands->Get_Position(obj);
 	stealthModeOverride = Get_Int_Parameter("StealthModeOverride");
+	float weaponRange = 0.0f;
+	const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(Get_Current_Weapon(obj),true);
+	if (ammo)
+		weaponRange = ammo->Range;
+	fireRange = Get_Float_Parameter("FireRange") >= 0 ? Get_Float_Parameter("FireRange") : weaponRange;
 	Commands->Enable_Enemy_Seen(obj,true);
 	Commands->Enable_Engine(obj,true);
 	Commands->Start_Timer(obj,this,1.0f,1);
@@ -8077,6 +8208,8 @@
 }
 void JMG_Utility_AI_Guardian_Generic::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_AI_Guardian_Ignored"))
 		return;
 	if (!JmgUtility::CanSeeStealth(stealthModeOverride,obj,seen))
@@ -8116,7 +8249,7 @@
 			params.Set_Face_Location(Commands->Get_Position(Target),5.0f);
 		else
 			params.AttackFaceTarget = false;
-		params.Set_Attack(Target,Get_Float_Parameter("FireRange"),0,primaryFire);
+		params.Set_Attack(Target,fireRange,0,primaryFire);
 		params.AttackCheckBlocked = Get_Int_Parameter("CheckBlocked") ? true : false;
 		Commands->Action_Attack(obj,params);
 	}
@@ -8812,11 +8945,14 @@
 }
 void JMG_Utility_Apply_Damage_While_In_Zone::Created(GameObject *obj)
 {
+	team = Get_Int_Parameter("Team");
 	int damagerId = Get_Int_Parameter("DamagerId");
 	sprintf(params,"%.2f,%.2f,%s,%d",Get_Float_Parameter("Rate"),Get_Float_Parameter("DamageAmount"),Get_Parameter("Warhead"),damagerId == -2 ? Commands->Get_ID(obj) : damagerId);
 }
 void JMG_Utility_Apply_Damage_While_In_Zone::Entered(GameObject *obj,GameObject *enterer)
 {
+	if (CheckPlayerType(enterer,team))
+		return;
 	Attach_Script_Once(enterer,"JMG_Utility_Apply_Damage_While_In_Zone_Attached",params);
 }
 void JMG_Utility_Apply_Damage_While_In_Zone::Exited(GameObject *obj,GameObject *exiter)
@@ -8983,6 +9119,7 @@
 						this->Destroy_Script();
 						return;
 					}
+					sleeping = sleepTime;
 				}
 			}
 		Commands->Start_Timer(obj,this,0.1f,1);
@@ -9023,6 +9160,10 @@
 {
 	controllerPlaced = false;
 }
+void JMG_Utility_Spawn_With_Last_Selected_Gun_Control::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Spawn_With_Last_Selected_Gun_Player::Created(GameObject *obj)
 {
 	if (!Commands->Is_A_Star(obj))
@@ -9180,7 +9321,7 @@
 		GameObject *ObjectiveMarker = objectiveMarkerId == -1 ? sender : Commands->Find_Object(objectiveMarkerId);
 		if (!ObjectiveMarker && objectiveMarkerId)
 		{
-			Console_Input("msg JMG_Utility_Objective_System_Objective_Update_Enter ERROR: Could not find objective marker object!");
+			Console_Input("msg JMG_Utility_Objective_System_Objective_Marker_Update_Custom ERROR: Could not find objective marker object!");
 			return;
 		}
 		BasicObjectiveSystem.Add_Objective(objectiveId,NewObjectiveSystem::Primary,NewObjectiveSystem::Hidden,0,"",0);
@@ -9290,6 +9431,10 @@
 {
 	controllerPlaced = false;
 }
+void JMG_Utility_Detect_AFK_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Credit_Trickle_When_Not_AFK::Created(GameObject *obj)
 {
 	custom = Get_Int_Parameter("Custom");
@@ -9343,6 +9488,7 @@
 SList<JMG_Utility_Basic_Spawner_In_Radius::SpawnObjectNode> JMG_Utility_Basic_Spawner_In_Radius::spawnObjectNodeList;
 void JMG_Utility_Basic_Spawner_In_Radius::Created(GameObject *obj)
 {
+	attachScriptsGroupId = Get_Int_Parameter("Attach_Scripts_Group_ID");
 	scriptId = Get_Int_Parameter("Script_ID");
 	if (scriptId == -1)
 		scriptId = this->Get_ID();
@@ -9369,7 +9515,7 @@
 	retryAttempts = Get_Int_Parameter("Collision_Retry_Attempts");
 	addHeight = Get_Float_Parameter("Collision_Add_Height");
 	changeSpawnCapCustom = Get_Int_Parameter("Change_Spawn_Cap_Custom");
-	sprintf(preset,Get_Parameter("Spawn_Preset"));
+	sprintf(preset,"%s",Get_Parameter("Spawn_Preset"));
 	spawnCount = 0;
 	initialSpawn = Get_Int_Parameter("Initial_Spawn");
 	if (initialSpawn == -1)
@@ -9384,6 +9530,8 @@
 	spawnGroupId = Get_Int_Parameter("Spawn_Group_ID");
 	enabled = Get_Int_Parameter("Starts_Disabled") == 0 ? true : false;
 	enableDisableCustom = Get_Int_Parameter("Enable_Disable_Custom");
+	raycastRange = Get_Vector3_Parameter("Raycast_Direction_Override[Bottom|Top|Unused]");
+	playersAddToSpawnAtATime = Get_Float_Parameter("PlayersAddToSpawnAtATime");
 	if (enabled)
 		Initial_Spawn(obj);
 	Commands->Start_Timer(obj,this,rate+(randomRate ? Commands->Get_Random(-randomRate,randomRate) : 0.0f),1);
@@ -9395,13 +9543,10 @@
 		if (enabled)
 		{
 			JMG_Utility_Basic_Spawner_In_Radius::SpawnFailureTypes spawnResult = AttemptSpawn(obj);
-			if (spawnResult == SpawnFailureTypes::SPAWN_BLOCKED)
-			{
-				Commands->Start_Timer(obj,this,0.1f,1);
-				return;
-			}
 			if (spawnResult == SpawnFailureTypes::SUCCESS)
 				Commands->Start_Timer(obj,this,rate+(randomRate ? Commands->Get_Random(-randomRate,randomRate) : 0.0f),1);
+			else if (spawnResult == SpawnFailureTypes::SPAWN_BLOCKED)
+				Commands->Start_Timer(obj,this,0.1f,1);
 		}
 		else
 			Commands->Start_Timer(obj,this,0.1f,1);
@@ -9424,7 +9569,7 @@
 	}
 	if (message == 6873525 && param == scriptId)
 	{
-		if (AttemptSpawn(obj) == SpawnFailureTypes::SPAWN_BLOCKED)
+		if (!enabled || AttemptSpawn(obj) == SpawnFailureTypes::SPAWN_BLOCKED)
 			Commands->Send_Custom_Event(obj,obj,6873525,scriptId,0.1f);
 	}
 	if (enableDisableCustom && message == enableDisableCustom)
@@ -9436,7 +9581,7 @@
 }
 JMG_Utility_Basic_Spawner_In_Radius::SpawnFailureTypes JMG_Utility_Basic_Spawner_In_Radius::AttemptSpawn(GameObject *obj)
 {
-	if (!(spawnedObjects < spawnAtATime))
+	if (!(spawnedObjects < spawnAtATime+GetPlayerLimitModifier()))
 		return SpawnFailureTypes::LIMIT_REACHED;
 	if (!((spawnLimit >= 0 && spawnCount < spawnLimit) || spawnLimit < 0))
 		return SpawnFailureTypes::LIMIT_REACHED;
@@ -9449,7 +9594,12 @@
 	topRay.Z = spawnLocation.Z;
 	Vector3 bottom,top;
 	PhysicsSceneClass::Get_Instance()->Get_Level_Extents(bottom,top);
-	if (spawnLocation.Z < bottom.Z)
+	if (raycastRange.X != 0.0f && raycastRange.Y != 0.0f)
+	{
+		bottomRay.Z = raycastRange.X;
+		topRay.Z = raycastRange.Y;
+	}
+	else if (spawnLocation.Z < bottom.Z)
 		bottomRay.Z = top.Z+1.0f;
 	else
 		bottomRay.Z = bottom.Z-1.0f;
@@ -9512,10 +9662,10 @@
 			Commands->Set_Position(spawned,bottomRay);
 		}
 		spawnCount++;
-		char params[32];
+		char params[512];
 		sprintf(params,"%d,%d,%d",Commands->Get_ID(obj),scriptId,spawnGroupId);
 		Commands->Attach_Script(spawned,"JMG_Utility_Basic_Spawner_In_Radius_Attached",params);
-		Commands->Send_Custom_Event(spawned,obj,6873522,6873522,0.0f);
+		Commands->Send_Custom_Event(spawned,obj,6873522,attachScriptsGroupId,0.0f);
 		return SpawnFailureTypes::SUCCESS;
 	}
 	if (!ignoreRayCastFailure)
@@ -9527,15 +9677,26 @@
 }
 void JMG_Utility_Basic_Spawner_In_Radius::Initial_Spawn(GameObject *obj)
 {
-	for (int x = 0;x < initialSpawn;x++)
+	int addSpawn = GetPlayerLimitModifier();
+	for (int x = spawnedObjects;x < initialSpawn+addSpawn;x++)
+	{
+		JMG_Utility_Basic_Spawner_In_Radius::SpawnFailureTypes spawnResult;
 		for (int y = 0;y < 5;y++)
 		{
-			JMG_Utility_Basic_Spawner_In_Radius::SpawnFailureTypes spawnResult = AttemptSpawn(obj);
+			 spawnResult = AttemptSpawn(obj);
 			if (spawnResult == SpawnFailureTypes::SUCCESS)
 				break;
 			if (spawnResult == SpawnFailureTypes::LIMIT_REACHED)
-				x = initialSpawn+1;
+				x = initialSpawn+1+addSpawn;
 		}
+		if (spawnResult == SpawnFailureTypes::SPAWN_BLOCKED)
+			Commands->Send_Custom_Event(obj,obj,6873525,scriptId,0.1f);
+		if (initialSpawn < spawnAtATime)
+		{
+			Stop_Timer(obj,1);
+			Commands->Start_Timer(obj,this,0.1f,1);
+		}
+	}
 }
 bool JMG_Utility_Basic_Spawner_In_Radius::CheckIfObjectIsNearAnotherObject(Vector3 pos)
 {
@@ -9557,13 +9718,17 @@
 {
 	if (obj->As_SoldierGameObj())
 	{
-		char params[220];
+		char params[512];
 		sprintf(params,"0.1,%.2f",facing);
 		Commands->Attach_Script(obj,"JMG_Utility_Delay_Then_Rotate_Camera",params);
 	}
 	else
 		Commands->Set_Facing(obj,facing);
 }
+int JMG_Utility_Basic_Spawner_In_Radius::GetPlayerLimitModifier()
+{
+	return (int)playersAddToSpawnAtATime*Get_Player_Count();
+}
 void JMG_Utility_Basic_Spawner_In_Radius_Attached::Created(GameObject *obj)
 {
 	GameObject *controller = Commands->Find_Object(Get_Int_Parameter("Controller_ID"));
@@ -9658,24 +9823,45 @@
 	visibleParam = Get_Int_Parameter("Visible_Param");
 	notVisibleParam = Get_Int_Parameter("Not_Visible_Param");
 	maxNotSeenTime = (int)(Get_Float_Parameter("Max_Lost_Sight_Time")*10);
+	repeatSendSeenCustom = Get_Int_Parameter("RepeatSendSeenCustom") != 0;
+	carTankBike = Get_Vector3_Parameter("AttackPriority[Car|Tank|Bike]");
+	flyingTurretBoat = Get_Vector3_Parameter("AttackPriority[Flying|Turret|Boat]");
+	submarineInfantryUnused = Get_Vector3_Parameter("AttackPriority[Submarine|Infantry|Unused]");
 	Commands->Enable_Enemy_Seen(obj,true);
 	Commands->Start_Timer(obj,this,0.1f,1);
 }
 void JMG_Utility_Enemy_Seen_Send_Custom::Enemy_Seen(GameObject *obj,GameObject *seen)
 {
+	GameObject *vehicle = Get_Vehicle(seen);
+	if (vehicle)
+		seen = vehicle;
 	if (!JmgUtility::CanSeeStealth(0,obj,seen))
 		return;
+	float seenPriority = GetPriority(seen);
+	if (!seenPriority)
+		return;
 	if (enemyPresetId && enemyPresetId != Commands->Get_Preset_ID(seen))
 		return;
 	if (Is_Script_Attached(seen,"JMG_Utility_Enemy_Seen_Send_Custom_Ignore"))
 		return;
+	if (!Commands->Get_Health(seen))
+		return;
 	int seenId = Commands->Get_ID(seen);
-	if (!seenTime)
+	if (!seenTime || !lastEnemyId)
 	{
-		GameObject *object = !id ? obj : (id == -1 ? seen : Commands->Find_Object(id));
-		if (object)
-			Commands->Send_Custom_Event(obj,object,visibleMessage,visibleParam,0);
 		lastEnemyId = seenId;
+		SendCustom(obj,visibleMessage,visibleParam);
+	}
+	else if (lastEnemyId != seenId)
+	{
+		GameObject *enemy = Commands->Find_Object(seenId);
+		if (!enemy || seenPriority > GetPriority(enemy))
+		{
+			if (lastEnemyId)
+				SendCustom(obj,notVisibleMessage,notVisibleParam);
+			lastEnemyId = seenId;
+			SendCustom(obj,visibleMessage,visibleParam);
+		}
 	}
 	if (lastEnemyId == seenId)
 		seenTime = maxNotSeenTime;
@@ -9685,19 +9871,53 @@
 {
 	if (number == 1)
 	{
+		GameObject *target = Commands->Find_Object(lastEnemyId);
+		if (!target || !Commands->Get_Health(target))
+		{
+			SendCustom(obj,notVisibleMessage,notVisibleParam);
+			lastEnemyId = 0;
+			seenTime = 0;
+		}
 		if (seenTime)
 		{
 			seenTime--;
 			if (!seenTime)
 			{
-				GameObject *object = !id ? obj : (id == -1 ? Commands->Find_Object(lastEnemyId) : Commands->Find_Object(id));
-				if (object)
-					Commands->Send_Custom_Event(obj,object,notVisibleMessage,notVisibleParam,0);
+				SendCustom(obj,notVisibleMessage,notVisibleParam);
+				lastEnemyId = 0;
 			}
 		}
+		if (repeatSendSeenCustom && target && seenTime)
+			SendCustom(obj,visibleMessage,visibleParam);
 		Commands->Start_Timer(obj,this,0.1f,1);
 	}
 }
+float JMG_Utility_Enemy_Seen_Send_Custom::GetPriority(GameObject *seen)
+{
+	if (seen->As_VehicleGameObj())
+		switch (Get_Vehicle_Mode(seen))
+		{
+			case VEHICLE_TYPE_CAR: return carTankBike.X;
+			case VEHICLE_TYPE_TANK: return carTankBike.Y;
+			case VEHICLE_TYPE_BIKE: return carTankBike.Z;
+			case VEHICLE_TYPE_FLYING: return flyingTurretBoat.X;
+			case VEHICLE_TYPE_TURRET: return flyingTurretBoat.Y;
+			case VEHICLE_TYPE_BOAT: return flyingTurretBoat.Z;
+			case VEHICLE_TYPE_SUB: return submarineInfantryUnused.X;
+		}
+	else if (seen->As_SoldierGameObj())
+		return submarineInfantryUnused.Y;
+	Console_Input("msg JMG_Utility_Enemy_Seen_Send_Custom ERROR: Undefined vehicle type!");
+	return 0;
+}
+void JMG_Utility_Enemy_Seen_Send_Custom::SendCustom(GameObject *obj,int custom,int param)
+{
+	if (!custom)
+		return;
+	GameObject *object = !id ? obj : (id == -1 ? Commands->Find_Object(lastEnemyId) : Commands->Find_Object(id));
+	if (object)
+		Commands->Send_Custom_Event(obj,object,custom,param == -1 ? lastEnemyId : param,0);
+}
 void JMG_Utility_Custom_Send_Custom_If_Script_Attached::Created(GameObject *obj)
 {
 	recieveMessage = Get_Int_Parameter("Custom");
@@ -9736,6 +9956,10 @@
 {
 	JMG_Utility_Basic_Spawner_In_Radius::spawnObjectNodeList.Remove_All();
 }
+void JMG_Utility_Basic_Spawner_In_Radius_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Custom_Set_Engine::Created(GameObject *obj)
 {
 	custom = Get_Int_Parameter("Custom");
@@ -10140,6 +10364,8 @@
 }
 void JMG_Utility_AI_Skittish_Herd_Animal::Enemy_Seen(GameObject *obj,GameObject *seen)	
 {
+	if (!Commands->Get_Health(seen))
+		return;
 	if (((HerdAnimalPositionControl[herdId].checkRetreatCloseEnough() || difftime(clock(),HerdAnimalPositionControl[herdId].herdRetreatStart) > HerdAnimalPositionControl[herdId].herdRetreatTime)))
 		setRetreatLocation(obj,seen);
 }
@@ -10280,6 +10506,10 @@
 	for (int x = 0;x < 128;x++)
 		JMG_Utility_AI_Skittish_Herd_Animal::HerdAnimalPositionControl[x].Empty_List();
 }
+void JMG_Utility_AI_Skittish_Herd_Animal_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Custom_Display_Dialog_Box::Created(GameObject *obj)
 {
 	custom = Get_Int_Parameter("Custom");
@@ -10803,6 +11033,10 @@
 	exists = false;
 	Empty_List();
 }
+void JMG_Utility_Swimming_Infantry_Advanced_Controller::Detach(GameObject *obj)
+{
+	Destroyed(obj);
+}
 void JMG_Utility_Swimming_Infantry_Advanced_Add_All_Of_Style::Created(GameObject *obj)
 {
 	if (!JMG_Utility_Swimming_Infantry_Advanced_Controller::exists)
@@ -10875,6 +11109,1012 @@
 		Console_Input(errorMsg);
 	}
 }
+void JMG_Utility_Custom_Send_Cycled_Customs::Created(GameObject *obj)
+{
+	cycle = 0;
+	recieveMessage = Get_Int_Parameter("Custom");
+	id = Get_Int_Parameter("ID");
+	for (int x = 0;x < 10;x++)
+	{
+		char parameterName[128];
+		sprintf(parameterName,"SendCustom%0d",x);
+		custom[x] = Get_Int_Parameter(parameterName);
+		sprintf(parameterName,"Param%0d",x);
+		Param[x] = Get_Int_Parameter(parameterName);
+		sprintf(parameterName,"Delay%0d",x);
+		delay[x] = Get_Float_Parameter(parameterName);
+	}
+	randomDelay = Get_Float_Parameter("RandomDelay");
+	randomChance = Get_Float_Parameter("RandomChance");
+}
+void JMG_Utility_Custom_Send_Cycled_Customs::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == recieveMessage)
+	{
+		if (randomChance && randomChance < Commands->Get_Random(0.0f,1.0f))
+			return;
+		GameObject *object = id ? (id == -1 ? sender : Commands->Find_Object(id)) : obj;
+ 		Commands->Send_Custom_Event(obj,object,custom[cycle],Param[cycle] == -1 ? param : Param[cycle],delay[cycle]+(randomDelay > 0 ? Commands->Get_Random(0.0f,randomDelay) : 0.0f));
+		cycle++;
+		if (!custom[cycle])
+			cycle = 0;
+	}
+}
+void JMG_Utility_Killed_Send_Custom_From_Killer::Killed(GameObject *obj,GameObject *killer)
+{
+	if (!Commands->Is_A_Star(killer))
+		return;
+	int id = Get_Int_Parameter("ID");
+	GameObject *object = id ? (id == -1 ? killer : Commands->Find_Object(id)) : obj;
+	Commands->Send_Custom_Event(killer,object,Get_Int_Parameter("Custom"),Get_Int_Parameter("Param"),Get_Float_Parameter("Delay"));
+	Destroy_Script();
+}
+void JMG_Utility_Emulate_Sound_Heard_On_FDS::Created(GameObject *obj)
+{
+	if (!The_Game()->Is_Dedicated())
+	{
+		Destroy_Script();
+		return;
+	}
+	if (!obj->As_SoldierGameObj())
+	{
+		Console_Input("msg JMG_Utility_Emulate_Sound_Heard_On_FDS::ERROR Only attach to soldiers!");
+		Destroy_Script();
+		return;
+	}
+	crouchSoundId = Get_Int_Parameter("DefaultCrouchMoveSoundID");
+	walkSoundId = Get_Int_Parameter("DefaultWalkMoveSoundID");
+	runSoundId = Get_Int_Parameter("DefaultRunMoveSoundID");
+	updateRate = Get_Float_Parameter("UpdateRate");
+	currentWeapon = WeaponNode(obj);
+	vehicleWeapon = WeaponNode();
+	Commands->Start_Timer(obj,this,updateRate,1);
+}
+void JMG_Utility_Emulate_Sound_Heard_On_FDS::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1 && Commands->Get_Health(obj))
+	{
+		CombatSound sound;
+		sound.Creator = obj;
+		sound.sound = 0;
+		sound.Position = Commands->Get_Position(obj);
+		WeaponNode::FireWeaponReturn weaponFire = FiredWeapon(obj);
+		if (weaponFire == WeaponNode::FireWeaponReturn::INFANTRY_FIRE)
+		{
+			const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(currentWeapon.weaponName,true);
+			if (ammo)
+				sound.sound = ammo->FireSoundDefID;
+		}
+		else if (weaponFire == WeaponNode::FireWeaponReturn::VEHICLE_FIRE)
+		{
+			const AmmoDefinitionClass *ammo = Get_Weapon_Ammo_Definition(vehicleWeapon.weaponName,true);
+			if (ammo)
+				sound.sound = ammo->FireSoundDefID;
+		}
+		else
+		{
+			Vector3 tempSpeed;
+			obj->As_SoldierGameObj()->Get_Velocity(tempSpeed);
+			if (tempSpeed.Length2() > 0)
+			{
+				SoldierGameObj *solderGameObj = obj->As_SoldierGameObj();
+				if (solderGameObj->Is_Crouched())
+					sound.sound = crouchSoundId;
+				else if (solderGameObj->Is_Slow())
+					sound.sound = walkSoundId;
+				else
+					sound.sound = runSoundId;
+			}
+		}
+		if (sound.sound)
+		{
+			AudibleSoundDefinitionClass *soundDef = (AudibleSoundDefinitionClass *)DefinitionMgrClass::Find_Definition(sound.sound,true);
+			if (soundDef)
+			{
+				float soundRangeSquared = soundDef->Get_Logical_DropOff_Radius()*soundDef->Get_Logical_DropOff_Radius();
+				sound.sound = soundDef->Get_Logical_Type();
+				for (SLNode<SmartGameObj> *node = GameObjManager::SmartGameObjList.Head();node;node = node->Next())
+				{
+					SmartGameObj* object = node->Data();
+					if (object == obj)
+						continue;
+					if (JmgUtility::SimpleDistance(Commands->Get_Position(object),sound.Position) > soundRangeSquared)
+						continue;
+					const SimpleDynVecClass<GameObjObserverClass *>& observer_list = object->Get_Observers();
+					for(int index = 0;index < observer_list.Count();index++)
+						observer_list[index]->Sound_Heard(object,sound);
+				}
+			}
+		}
+		Commands->Start_Timer(obj,this,updateRate,1);
+	}
+}
+JMG_Utility_Emulate_Sound_Heard_On_FDS::WeaponNode::FireWeaponReturn JMG_Utility_Emulate_Sound_Heard_On_FDS::FiredWeapon(GameObject *obj)
+{
+	if (_stricmp(currentWeapon.weaponName,Get_Current_Weapon(obj)))
+	{
+		currentWeapon = WeaponNode(obj);
+		return WeaponNode::FireWeaponReturn::NONE;
+	}
+	GameObject *vehicle = Get_Vehicle(obj);
+	if (vehicle)
+	{
+		int currentBullets = vehicleWeapon.GetWeaponBullets(vehicle);
+		if (currentBullets < vehicleWeapon.totalBullets)
+		{
+			vehicleWeapon.totalBullets = currentBullets;
+			return WeaponNode::FireWeaponReturn::VEHICLE_FIRE;
+		}
+		return WeaponNode::FireWeaponReturn::NONE;
+	}
+	int currentBullets = currentWeapon.GetWeaponBullets(obj);
+	if (currentBullets < currentWeapon.totalBullets)
+	{
+		currentWeapon.totalBullets = currentBullets;
+		return WeaponNode::FireWeaponReturn::INFANTRY_FIRE;
+	}
+	return WeaponNode::FireWeaponReturn::NONE;
+}
+void JMG_Utility_Send_Custom_When_Team_Dominates_Zone::Created(GameObject *obj)
+{
+	team = Get_Int_Parameter("Team");
+	teamHoldingZone = sBool::sNULL;
+	enabled = Get_Int_Parameter("StartsEnabled") != 0;
+	enableCustom = Get_Int_Parameter("EnableCustom");
+	id = Get_Int_Parameter("ID");
+	captureCustom = Get_Int_Parameter("CaptureCustom");
+	captureParam = Get_Int_Parameter("CaptureParam");
+	lostCustom = Get_Int_Parameter("LostCustom");
+	lostParam = Get_Int_Parameter("LostParam");
+	delay = Get_Float_Parameter("CaptureDelay");
+	rate = Get_Float_Parameter("Rate");
+	sendCustomEveryTick = Get_Int_Parameter("SendCustomEveryTick")!=0;
+	Commands->Start_Timer(obj,this,rate,1);
+}
+void JMG_Utility_Send_Custom_When_Team_Dominates_Zone::Timer_Expired(GameObject *obj, int number)
+{
+	if (number == 1)
+	{
+		if (enabled)
+		{
+			int teamCount = 0,enemyCount = 0;
+			for (SLNode<SmartGameObj> *current = GameObjManager::SmartGameObjList.Head();current;current = current->Next())
+			{
+				SmartGameObj* o = current->Data();
+				if (o != obj && Commands->Get_Health(o) && IsInsideZone(obj,o))
+				{
+					int myTeam = Commands->Get_Player_Type(o);
+					if (myTeam == -2 || Is_Script_Attached(o,"JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore"))
+						continue;
+					if (myTeam == team)
+						teamCount++;
+					else
+						enemyCount++;
+				}
+			}
+			if (teamCount > enemyCount)
+			{
+				if (teamHoldingZone != sBool::sTRUE || sendCustomEveryTick)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 					Commands->Send_Custom_Event(obj,object,captureCustom,captureParam == -1 ? teamCount : captureParam,delay);
+					teamHoldingZone = sBool::sTRUE;
+				}
+			}
+			else if (teamHoldingZone != sBool::sFALSE || sendCustomEveryTick)
+			{
+				GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 				Commands->Send_Custom_Event(obj,object,lostCustom,lostParam == -1 ? teamCount : lostParam,delay);
+				teamHoldingZone = sBool::sFALSE;
+			}
+		}
+		Commands->Start_Timer(obj,this,rate,1);
+	}
+}
+void JMG_Utility_Send_Custom_When_Team_Dominates_Zone::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == enableCustom)
+	{
+		if (enabled != (param!=0))
+			teamHoldingZone = sBool::sNULL;
+		enabled = param!=0;
+	}
+}
+void JMG_Utility_Send_Custom_When_Team_Zone::Created(GameObject *obj)
+{
+	team = Get_Int_Parameter("Team");
+	teamInZone = sBool::sNULL;
+	enabled = Get_Int_Parameter("StartsEnabled") != 0;
+	enableCustom = Get_Int_Parameter("EnableCustom");
+	id = Get_Int_Parameter("ID");
+	inCustom = Get_Int_Parameter("InCustom");
+	inParam = Get_Int_Parameter("InParam");
+	outCustom = Get_Int_Parameter("OutCustom");
+	outParam = Get_Int_Parameter("OutParam");
+	delay = Get_Float_Parameter("Delay");
+	rate = Get_Float_Parameter("Rate");
+	sendCustomEveryTick = Get_Int_Parameter("SendCustomEveryTick") != 0;
+	Commands->Start_Timer(obj,this,rate,1);
+}
+void JMG_Utility_Send_Custom_When_Team_Zone::Timer_Expired(GameObject *obj, int number)
+{
+	if (number == 1)
+	{
+		if (enabled)
+		{
+			int teamCount = 0;
+			for (SLNode<SmartGameObj> *current = GameObjManager::SmartGameObjList.Head();current;current = current->Next())
+			{
+				SmartGameObj* o = current->Data();
+				if (o != obj && Commands->Get_Health(o) && IsInsideZone(obj,o))
+				{
+					int myTeam = Commands->Get_Player_Type(o);
+					if (myTeam == -2 || Is_Script_Attached(o,"JMG_Utility_Send_Custom_When_Team_Zone_Ignore"))
+						continue;
+					if (myTeam == team)
+						teamCount++;
+				}
+			}
+			if (teamCount > 0)
+			{
+				if (teamInZone != sBool::sTRUE || sendCustomEveryTick)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 					Commands->Send_Custom_Event(obj,object,inCustom,inParam,delay);
+					teamInZone = sBool::sTRUE;
+				}
+			}
+			else
+			{
+				if (teamInZone != sBool::sFALSE || sendCustomEveryTick)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 					Commands->Send_Custom_Event(obj,object,outCustom,outParam,delay);
+					teamInZone = sBool::sFALSE;
+				}
+			}
+		}
+		Commands->Start_Timer(obj,this,rate,1);
+	}
+}
+void JMG_Utility_Send_Custom_When_Team_Zone::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == enableCustom)
+	{
+		if (enabled != (param!=0))
+			teamInZone = sBool::sNULL;
+		enabled = param!=0;
+	}
+}
+void JMG_Utility_HUD_Count_Down_Visible_Countdown::Created(GameObject *obj)
+{
+	Commands->Start_Timer(obj,this,0.1f,1);
+}
+void JMG_Utility_HUD_Count_Down_Visible_Countdown::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		JMG_Utility_HUD_Count_Down::AddVisualCountdownNode(Commands->Get_ID(obj),Get_Int_Parameter("CharPos"),Get_Parameter("BaseModelName"));
+	}
+}
+void JMG_Utility_Custom_Send_Custom_On_Secondary_Count::Created(GameObject *obj)
+{
+	triggerCustom = Get_Int_Parameter("TriggerCustom");
+	countCustom = Get_Int_Parameter("CountCustom");
+	count = Get_Int_Parameter("MatchCount");
+	id = Get_Int_Parameter("ID");
+	sendCustom = Get_Int_Parameter("SendCustom");
+	Param = Get_Int_Parameter("Param");
+	delay = Get_Float_Parameter("Delay");
+	resetCustom = Get_Int_Parameter("ResetCountCustom");
+	onceMatchedContinue = Get_Int_Parameter("OnceMatchedContinue") != 0;
+}
+void JMG_Utility_Custom_Send_Custom_On_Secondary_Count::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == resetCustom)
+	{
+		count = Get_Int_Parameter("Count");
+	}
+	if (message == countCustom)
+	{
+		if ((!onceMatchedContinue && count >= 0) || (onceMatchedContinue && count))
+			count--;
+	}
+	if (message == triggerCustom && !count)
+	{
+		GameObject *object = id ? (id == -1 ? sender : Commands->Find_Object(id)) : obj;
+		if (object)
+		{
+			Commands->Send_Custom_Event(obj,object,sendCustom,Param == -1 ? param : Param,delay);
+		}
+	}
+}
+void JMG_Utility_Send_Custom_When_Not_Team_Zone::Created(GameObject *obj)
+{
+	team = Get_Int_Parameter("Team");
+	teamInZone = sBool::sNULL;
+	enabled = Get_Int_Parameter("StartsEnabled") != 0;
+	enableCustom = Get_Int_Parameter("EnableCustom");
+	id = Get_Int_Parameter("ID");
+	inCustom = Get_Int_Parameter("InCustom");
+	inParam = Get_Int_Parameter("InParam");
+	outCustom = Get_Int_Parameter("OutCustom");
+	outParam = Get_Int_Parameter("OutParam");
+	delay = Get_Float_Parameter("Delay");
+	rate = Get_Float_Parameter("Rate");
+	sendCustomEveryTick = Get_Int_Parameter("SendCustomEveryTick") != 0;
+	Commands->Start_Timer(obj,this,rate,1);
+}
+void JMG_Utility_Send_Custom_When_Not_Team_Zone::Timer_Expired(GameObject *obj, int number)
+{
+	if (number == 1)
+	{
+		if (enabled)
+		{
+			int teamCount = 0;
+			for (SLNode<SmartGameObj> *current = GameObjManager::SmartGameObjList.Head();current;current = current->Next())
+			{
+				SmartGameObj* o = current->Data();
+				if (o != obj && Commands->Get_Health(o) && IsInsideZone(obj,o))
+				{
+					int myTeam = Commands->Get_Player_Type(o);
+					if (myTeam == -2 || Is_Script_Attached(o,"JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore"))
+						continue;
+					if (myTeam != team)
+						teamCount++;
+				}
+			}
+			if (teamCount > 0)
+			{
+				if (teamInZone != sBool::sTRUE || sendCustomEveryTick)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 					Commands->Send_Custom_Event(obj,object,inCustom,inParam,delay);
+					teamInZone = sBool::sTRUE;
+				}
+			}
+			else
+			{
+				if (teamInZone != sBool::sFALSE || sendCustomEveryTick)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 					Commands->Send_Custom_Event(obj,object,outCustom,outParam,delay);
+					teamInZone = sBool::sFALSE;
+				}
+			}
+		}
+		Commands->Start_Timer(obj,this,rate,1);
+	}
+}
+void JMG_Utility_Send_Custom_When_Not_Team_Zone::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == enableCustom)
+	{
+		if (enabled != (param!=0))
+			teamInZone = sBool::sNULL;
+		enabled = param!=0;
+	}
+}
+void JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore::Created(GameObject *obj)
+{
+}
+void JMG_Utility_Send_Custom_When_Team_Zone_Ignore::Created(GameObject *obj)
+{
+}
+void JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore::Created(GameObject *obj)
+{
+}
+void JMG_Utility_Send_Custom_On_Armor::Created(GameObject *obj)
+{
+	id = Get_Int_Parameter("ID");
+	armorCustom = Get_Int_Parameter("ArmorCustom");
+	armorParam = Get_Int_Parameter("ArmorParam");
+	noArmorCustom = Get_Int_Parameter("NoArmorCustom");
+	noArmorParam = Get_Int_Parameter("NoArmorParam");
+	delay = Get_Float_Parameter("Delay");
+	hurt = Commands->Get_Shield_Strength(obj) < Commands->Get_Max_Shield_Strength(obj);
+	Commands->Start_Timer(obj,this,0.1f,1);
+}
+void JMG_Utility_Send_Custom_On_Armor::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		if (hurt && Commands->Get_Shield_Strength(obj) >= Commands->Get_Max_Shield_Strength(obj))
+		{
+			hurt = false;
+			GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 			Commands->Send_Custom_Event(obj,object,armorCustom,armorParam,delay);
+		}
+		if (!hurt && !Commands->Get_Shield_Strength(obj))
+		{
+			hurt = true;
+			GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 			Commands->Send_Custom_Event(obj,object,noArmorCustom,noArmorParam,delay);
+		}
+		Commands->Start_Timer(obj,this,0.1f,1);
+	}
+}
+void JMG_Utility_Base_Defense_Simple::Created(GameObject *obj)
+{
+	resetTime = 0;
+	Commands->Enable_Enemy_Seen(obj,true);
+	enemyId = 0;
+	minDist = Get_Float_Parameter("MinAttackDistance")*Get_Float_Parameter("MinAttackDistance");
+	maxDistance = Get_Float_Parameter("MaxAttackDistance");
+	carTankBike = Get_Vector3_Parameter("AttackPriority[Car|Tank|Bike]");
+	flyingTurretBoat = Get_Vector3_Parameter("AttackPriority[Flying|Turret|Boat]");
+	submarineInfantryUnused = Get_Vector3_Parameter("AttackPriority[Submarine|Infantry|Unused]");
+	userSetResetTime = (int)(Get_Float_Parameter("ResetTime")*10);
+	maxDist = maxDistance*maxDistance;
+	Commands->Start_Timer(obj,this,0.1f,1);
+}
+void JMG_Utility_Base_Defense_Simple::Enemy_Seen(GameObject *obj,GameObject *seen)
+{
+	GameObject *vehicle = Get_Vehicle(seen);
+	if (vehicle)
+		seen = vehicle;
+	float seenPriority = GetPriority(seen);
+	if (!seenPriority)
+		return;
+	if (!Commands->Get_Health(seen))
+		return;
+	float distance = JmgUtility::SimpleDistance(Commands->Get_Position(obj),Commands->Get_Position(seen));
+	if (distance < minDist || distance > maxDist)
+		return;
+	int seenId = Commands->Get_ID(seen);
+	if (!enemyId)
+		AttackTarget(obj,seenId,seen);
+	else if (enemyId != seenId)
+	{
+		GameObject *enemy = Commands->Find_Object(enemyId);
+		if (!enemy || seenPriority > GetPriority(enemy))
+			AttackTarget(obj,seenId,seen);
+
+	}
+	if (enemyId == seenId)
+		resetTime = userSetResetTime;
+}
+void JMG_Utility_Base_Defense_Simple::Damaged(GameObject *obj,GameObject *damager,float damage)
+{
+	if (enemyId)
+		return;
+	Enemy_Seen(obj,damager);
+}
+void JMG_Utility_Base_Defense_Simple::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		if (enemyId)
+		{
+			GameObject *enemy = Commands->Find_Object(enemyId);
+			if (!enemy || !Commands->Get_Health(enemy))
+			{
+				enemyId = 0;
+				Commands->Action_Reset(obj,100);
+			}
+		}
+		if (resetTime)
+		{
+			resetTime--;
+			if (!resetTime)
+			{
+				enemyId = 0;
+				Commands->Action_Reset(obj,100);
+			}
+		}
+		Commands->Start_Timer(obj,this,0.1f,1);
+	}
+}
+float JMG_Utility_Base_Defense_Simple::GetPriority(GameObject *seen)
+{
+	if (seen->As_VehicleGameObj())
+		switch (Get_Vehicle_Mode(seen))
+		{
+			case VEHICLE_TYPE_CAR: return carTankBike.X;
+			case VEHICLE_TYPE_TANK: return carTankBike.Y;
+			case VEHICLE_TYPE_BIKE: return carTankBike.Z;
+			case VEHICLE_TYPE_FLYING: return flyingTurretBoat.X;
+			case VEHICLE_TYPE_TURRET: return flyingTurretBoat.Y;
+			case VEHICLE_TYPE_BOAT: return flyingTurretBoat.Z;
+			case VEHICLE_TYPE_SUB: return submarineInfantryUnused.X;
+		}
+	else if (seen->As_SoldierGameObj())
+		return submarineInfantryUnused.Y;
+	Console_Input("msg JMG_Utility_Base_Defense_Simple ERROR: Undefined vehicle type!");
+	return 0;
+}
+void JMG_Utility_Base_Defense_Simple::AttackTarget(GameObject *obj,int seenId,GameObject *seen)
+{
+	enemyId = seenId;
+	ActionParamsStruct params;
+	params.Set_Basic(this,100,2);
+	params.Set_Attack(seen,maxDistance,0.0,true);
+	Commands->Action_Attack(obj,params);
+	params.AttackCheckBlocked = false;
+}
+void JMG_Utility_Custom_Send_Custom_Supress_Spam::Created(GameObject *obj)
+{
+	char parameterName[128];
+	for (int x = 0;x < 10;x++)
+	{
+		sprintf(parameterName,"Custom%0d",x);
+		catchCustom[x] = Get_Int_Parameter(parameterName);
+	}
+	id = Get_Int_Parameter("ID");
+	lastCustom = 0;
+	lastParam = 0;
+	lastSentCustom = 0;
+	lastSentParam = 0;
+	senderId = 0;
+	spamDelay = Get_Float_Parameter("SpamDelay");
+	sendDuplicates = Get_Int_Parameter("SendDuplicates") != 0;
+	supressingSpam = false;
+}
+void JMG_Utility_Custom_Send_Custom_Supress_Spam::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		bool sent = false;
+		if (sendDuplicates || (lastCustom != lastSentCustom || lastParam != lastSentParam))
+			sent = SendCustom(obj);
+		if (!sent)
+			supressingSpam = false;
+		else
+			Commands->Start_Timer(obj,this,spamDelay,1);
+	}
+}
+void JMG_Utility_Custom_Send_Custom_Supress_Spam::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (catchCustom[0] == 0 || message == catchCustom[0] || message == catchCustom[1] || message == catchCustom[2] || message == catchCustom[3] || message == catchCustom[4] || message == catchCustom[5] || message == catchCustom[6] || message == catchCustom[7] || message == catchCustom[8] || message == catchCustom[9])
+	{
+		senderId = Commands->Get_ID(sender);
+		lastCustom = message;
+		lastParam = param;
+		if (!supressingSpam && (sendDuplicates || (message != lastSentCustom || param != lastSentParam)))
+		{
+			SendCustom(obj);
+			supressingSpam = true;
+			Commands->Start_Timer(obj,this,spamDelay,1);
+		}
+	}
+}
+bool JMG_Utility_Custom_Send_Custom_Supress_Spam::SendCustom(GameObject *obj)
+{
+	if (!lastCustom)
+		return false;
+	GameObject *sender = Commands->Find_Object(senderId);
+	if (!sender)
+		sender = obj;
+	GameObject *object = id ? Commands->Find_Object(id) : obj;
+ 	Commands->Send_Custom_Event(sender,object,lastCustom,lastParam,0);
+	lastSentCustom = lastCustom;
+	lastSentParam = lastParam;
+	senderId = 0;
+	lastCustom = 0;
+	lastParam = 0;
+	return true;
+}
+void JMG_Utility_Send_Custom_When_Player_Between_Range::Created(GameObject *obj)
+{
+	sleeping = 0;
+	minDistance = Get_Float_Parameter("MinDistance");
+	minDistance *= minDistance;
+	maxDistance = Get_Float_Parameter("MaxDistance");
+	maxDistance *= maxDistance;
+	id = Get_Int_Parameter("ID");
+	custom = Get_Int_Parameter("Custom");
+	param = Get_Int_Parameter("Param");
+	delay = Get_Float_Parameter("Delay");
+	triggerOnce = Get_Int_Parameter("TriggerOnce") ? true : false;
+	sleepTime = Get_Int_Parameter("SleepTime");
+	Commands->Start_Timer(obj,this,0.1f,1);
+}
+void JMG_Utility_Send_Custom_When_Player_Between_Range::Timer_Expired(GameObject *obj, int number)
+{
+	if (number == 1)
+	{
+		if (sleeping)
+			sleeping--;
+		else
+			for (int x = 1;x < 128;x++)
+			{
+				GameObject *player = Get_GameObj(x);
+				if (!player)
+					continue;
+				float playerDist = JmgUtility::SimpleDistance(Commands->Get_Position(player),Commands->Get_Position(obj));
+				if (playerDist <= maxDistance && playerDist >= minDistance)
+				{
+					GameObject *object = id ? Commands->Find_Object(id) : obj;
+					if (object)
+						Commands->Send_Custom_Event(obj,object,custom,param,delay);
+					if (triggerOnce)
+					{
+						this->Destroy_Script();
+						return;
+					}
+					sleeping = sleepTime;
+				}
+			}
+		Commands->Start_Timer(obj,this,0.1f,1);
+	}
+}
+void JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player::Created(GameObject *obj)
+{
+	BriefingText = NULL;
+	lastNode = NULL;
+	int File = Commands->Text_File_Open(Get_Parameter("FileName"));
+	if (!File)
+	{
+		Console_Input("msg JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player ERROR: Text File not found!");
+		return;
+	}
+	bool NextLineIsTarget = false;
+	char TextString[16384];
+	while (Commands->Text_File_Get_String(File,TextString,16383))
+	{
+		if (!_stricmp(TextString,""))
+			continue;
+		if (!NextLineIsTarget)
+			for (int x = 0;x < 16384;x++)
+				if (TextString[x] == '\n' || TextString[x] == 13 || TextString[x] == '\0')
+					TextString[x] = '\0';
+		if (NextLineIsTarget)
+		{
+			int Count = 0;
+			for (int x = 0;x < 16384;x++)
+			{
+				if ((Count > 140 && TextString[x] == ' ') || Count == 200)
+				{// String getting too long, start a new one.
+					lastNode->Text[Count] = '\0';
+					AddNewTextNode();
+					Count = 0;
+				}
+				else if (TextString[x] == '\n' || TextString[x] == '\0')
+				{
+					lastNode->Text[Count] = '\0';
+					break;// end of line your done
+				}
+				else if (TextString[x] == '\f')
+				{
+					lastNode->Text[Count] = '\0';
+					AddNewTextNode();
+					Count = 0;
+				}
+				else
+				{
+					lastNode->Text[Count] = TextString[x];
+					Count++;
+				}
+			}
+			break;
+		}
+		else if (!_stricmp(TextString,Get_Parameter("TextTag")))
+		{
+			NextLineIsTarget = true;
+			AddNewTextNode();
+		}
+		TextString[0] = '\0';
+	}
+	Commands->Text_File_Close(File);
+}
+void JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == Get_Int_Parameter("Custom"))
+	{
+		if (!Commands->Is_A_Star(sender))
+			return;
+		for (BriefingTextNode *node = lastNode;node;node = node->prev)
+		{
+			char tmpMsg[220];
+			sprintf(tmpMsg,"pamsg %d %s",JmgUtility::JMG_Get_Player_ID(sender),node->Text);
+			Console_Input(tmpMsg);
+		}
+	}
+}
+void JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player::Destroyed(GameObject *obj)
+{
+	RemoveTextNodes();
+}
+void JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player::AddNewTextNode()
+{
+	if (!BriefingText)
+	{
+		BriefingText = new BriefingTextNode;
+		sprintf(BriefingText->Text,"\0");
+		BriefingText->next = NULL;
+		lastNode = BriefingText;
+		return;
+	}
+	BriefingTextNode *Current = BriefingText;
+	while (Current)
+	{
+		if (!Current->next)
+		{
+			Current->next = new BriefingTextNode;
+			Current->next->prev = Current;
+			Current = Current->next;
+			sprintf(Current->Text,"\0");
+			Current->next = NULL;
+			lastNode = Current;
+			return;
+		}
+		Current = Current->next;
+	}
+}
+void JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player::RemoveTextNodes()
+{
+	BriefingTextNode *Current = BriefingText,*Temp;
+	while (Current)
+	{
+		Temp = Current;
+		Current = Current->next;
+		delete Temp;
+	}
+	BriefingText = NULL;
+	Destroy_Script();
+}
+void JMG_Utility_Send_Custom_On_Powerup_Pickup_Collector::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == CUSTOM_EVENT_POWERUP_GRANTED)
+	{
+		int id = Get_Int_Parameter("ID");
+		GameObject *object = id ? (id == -1 ? sender : Commands->Find_Object(id)) : obj;
+		if (object)
+			Commands->Send_Custom_Event(sender,object,Get_Int_Parameter("Message"),Get_Int_Parameter("Param"),Get_Float_Parameter("Delay"));
+	}
+}
+void JMG_Utility_Zone_Send_Custom_Enter_From_Enterer::Created(GameObject *obj)
+{
+	playerType = Get_Int_Parameter("PlayerType");
+	id = Get_Int_Parameter("ID");
+	custom = Get_Int_Parameter("Custom");
+	param = Get_Int_Parameter("Param");
+	delay = Get_Float_Parameter("Delay");
+	triggerOnce = Get_Int_Parameter("TriggerOnce") ? true : false;
+}
+void JMG_Utility_Zone_Send_Custom_Enter_From_Enterer::Entered(GameObject *obj,GameObject *enter)
+{
+	if (CheckPlayerType(enter,playerType) || Commands->Get_Player_Type(enter) == -4)
+		return;
+	GameObject *object = id ? (id == -1 ? enter : Commands->Find_Object(id)) : obj;
+	if (object)
+		Commands->Send_Custom_Event(enter,object,custom,param,delay);
+	if (triggerOnce)
+		Destroy_Script();
+}
+void JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom::Created(GameObject *obj)
+{
+	targetCustom = Get_Int_Parameter("TargetCustom");
+	minDistanceSquared = Get_Float_Parameter("MinDistance");
+	minDistanceSquared *= minDistanceSquared;
+	minAngle = Get_Float_Parameter("MinAngle");
+	maxAngle = Get_Float_Parameter("MaxAngle");
+	lowAngleMaxDistance = Get_Float_Parameter("UseLowAngleMaxDistance");
+	lowAngleMaxDistance *= lowAngleMaxDistance;
+	useLowAngleWhenAboveMinDistance = Get_Int_Parameter("UseLowAngleWhenAboveMinDistance") != 0;
+	useLowAngleTargetAboveHeight = Get_Float_Parameter("UseLowAngleTargetAboveHeight");
+	sprintf(vehicleProjectilePreset,"%s",Get_Parameter("VehicleProjectilePreset"));
+	fireVelocity = Get_Float_Parameter("FireVelocity");
+	gravityScale = Get_Float_Parameter("GravityScale");
+	missAmountPerMeter = Get_Float_Parameter("MissAmountPerMeter");
+	baseMissAmount = Get_Float_Parameter("BaseMissAmount");
+	fireDelay = Get_Float_Parameter("FireRate") == 0.0f ? 9999.9f : 1.0f/Get_Float_Parameter("FireRate");
+	reloadTime = Get_Float_Parameter("ReloadTime");
+	currentClipCount = clipCount = max(0,Get_Int_Parameter("ClipCount"));
+	velocitySquared = fireVelocity*fireVelocity;
+	sprintf(fireSound,"%s",Get_Parameter("FireSound"));
+	sprintf(projectileExplosion,"%s",Get_Parameter("ProjectileExplosion"));
+	sprintf(muzzleFlashExplosion,"%s",Get_Parameter("MuzzleFlashExplosion"));
+	sprintf(reloadSound,"%s",Get_Parameter("ReloadSound"));
+	aimTurret = Get_Int_Parameter("AimTurret") != 0;
+	refreshTime = Get_Int_Parameter("CustomTimeoutTime")*10;
+	projectedShotsChance = Get_Float_Parameter("ProjectedShotsChance");
+	if (Commands->Get_Random(0.0f,0.99999f) < projectedShotsChance)
+		projectShots = true;
+	delayComplete = true;
+	reloadComplete = true;
+	lastAngle = 9999.0f;
+	Commands->Disable_All_Collisions(obj);
+	Commands->Start_Timer(obj,this,0.01f,1);
+	Commands->Start_Timer(obj,this,0.1f,4);
+}
+void JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (targetCustom == message)
+	{
+		if (!targetId)
+			targetId = param;
+		if (param == targetId)
+			timeoutTime = refreshTime;
+	}
+}
+void JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom::Timer_Expired(GameObject *obj,int number)
+{
+	if (number == 1)
+	{
+		GameObject *seen = Commands->Find_Object(targetId);
+		if (!seen || !Commands->Get_Health(seen))
+			targetId = 0;
+		if (reloadComplete && delayComplete && targetId)
+		{
+			Vector3 myPos = Commands->Get_Bone_Position(obj,"FireSpot"),enemyPos = Commands->Get_Bone_Position(seen,"target");
+			if (seen->As_SmartGameObj() && projectShots)
+			{
+				double tmp = (fireVelocity*sin(lastAngle));
+				float flightTime = (float)((fireVelocity*sin(lastAngle)+sqrt(tmp*tmp+2*(gravityScale*9.8)*(myPos.Z-enemyPos.Z)))/(gravityScale*9.8));
+				Vector3 speed;
+				seen->As_SmartGameObj()->Get_Velocity(speed);
+				if (flightTime < 600.0f)
+				{
+					enemyPos.X += speed.X*flightTime;
+					enemyPos.Y += speed.Y*flightTime;
+					enemyPos.Z += speed.Z*flightTime;
+				}
+			}
+			float targetDist = JmgUtility::SimpleDistance(myPos,enemyPos),targetFlatDistance = JmgUtility::SimpleFlatDistance(myPos,enemyPos);
+			if (targetDist < minDistanceSquared)
+			{
+				Commands->Start_Timer(obj,this,0.01f,1);
+				return;
+			}
+			float targetMissAmount = 0.0f;
+			if (missAmountPerMeter)
+			{
+				float missAmount = Commands->Get_Distance(enemyPos,myPos)*missAmountPerMeter;
+				targetMissAmount = missAmount < baseMissAmount ? baseMissAmount : missAmount;
+			}
+			else if (baseMissAmount)
+				targetMissAmount = baseMissAmount;
+			if (targetMissAmount)
+			{
+				float rotation = Commands->Get_Random(-180.0f,180.0f);
+				enemyPos.X += cos(rotation)*Commands->Get_Random(0.0f,targetMissAmount);
+				enemyPos.Y += sin(rotation)*Commands->Get_Random(0.0f,targetMissAmount);
+			}
+			bool useHighAngle = true;
+			if (targetDist <= lowAngleMaxDistance)
+				useHighAngle = false;
+			else if (useLowAngleWhenAboveMinDistance && targetFlatDistance <= minDistanceSquared)
+				useHighAngle = false;
+			else if (useLowAngleTargetAboveHeight && Commands->Get_Position(obj).Z+useLowAngleTargetAboveHeight < Commands->Get_Position(seen).Z)
+				useHighAngle = false;
+			double height = enemyPos.Z-myPos.Z,angle;
+			if (!CalculateAngle(&angle,Commands->Get_Distance(enemyPos,myPos),height,useHighAngle))
+			{
+				Commands->Start_Timer(obj,this,0.01f,1);
+				return;
+			}
+			lastAngle = angle;
+			double zRotation = atan2(enemyPos.Y-myPos.Y,enemyPos.X-myPos.X);
+			FireProjectile(obj,zRotation,angle);
+			if (currentClipCount)
+				currentClipCount--;
+			if (!currentClipCount)
+			{
+				reloadComplete = false;
+				Commands->Start_Timer(obj,this,reloadTime,2);
+				Commands->Create_3D_Sound_At_Bone(reloadSound,obj,"Turret");
+			}
+			else
+			{
+				delayComplete = false;
+				Commands->Start_Timer(obj,this,fireDelay,3);
+			}
+			if (aimTurret)
+			{
+				float TempRotation = atan2(enemyPos.Y-myPos.Y,enemyPos.X-myPos.X);
+				Vector3 pos = Commands->Get_Bone_Position(obj,"barrel");
+				pos.X += (float)cos(TempRotation);
+				pos.Y += (float)sin(TempRotation);
+				pos.Z += (float)tan(angle);
+				ActionParamsStruct params;
+				params.Set_Attack(pos,0.0f,0,1);
+				params.AttackForceFire = false;
+				params.AttackCheckBlocked = false;
+				Commands->Action_Attack(obj,params);
+			}
+		}
+		Commands->Start_Timer(obj,this,0.01f,1);
+	}
+	if (number == 2)
+	{
+		if (Commands->Get_Random(0.0f,0.99999f) < projectedShotsChance)
+			projectShots = true;
+		else
+			projectShots = false;
+		currentClipCount = clipCount;
+		reloadComplete = true;
+	}
+	if (number == 3)
+	{
+		delayComplete = true;
+	}
+	if (number == 4)
+	{
+		if (timeoutTime)
+		{
+			timeoutTime--;
+			if (!timeoutTime)
+				targetId = 0;
+		}
+		Commands->Start_Timer(obj,this,0.1f,4);
+	}
+}
+bool JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom::CalculateAngle(double *returnAngle,double distance,double height,bool highAngle)
+{
+	double calculatedGravity = gravityScale*9.8;
+	if (distance <= 0.0)
+		return false;
+	double x = velocitySquared*velocitySquared-calculatedGravity*(calculatedGravity*(distance*distance)+2*height*velocitySquared);
+	if (x < 0)
+		return false;
+	*returnAngle = atan((velocitySquared+(highAngle ? 1 : -1)*sqrt(x))/(calculatedGravity*distance));
+	if (*returnAngle*180.0/PI < minAngle || *returnAngle*180.0/PI > maxAngle)
+		return false;
+	return true;
+}
+void JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom::FireProjectile(GameObject *obj,double zAngle,double aimAngle)
+{
+	Commands->Create_3D_Sound_At_Bone(fireSound,obj,"MuzzleA0");
+	GameObject *projectile = Commands->Create_Object(vehicleProjectilePreset,Commands->Get_Bone_Position(obj,"FireSpot"));
+	Commands->Create_Explosion(muzzleFlashExplosion,Commands->Get_Bone_Position(obj,"muzzleA0"),obj);
+	char params[220];
+	sprintf(params,"%d,%s",Commands->Get_ID(obj),projectileExplosion);
+	Commands->Attach_Script(projectile,"JMG_AI_Artillery_Targeting_Fire_Vehicle_Projectile_Attach",params);
+	Vector3 normalizedVector = Vector3((float)cos(zAngle),(float)sin(zAngle),(float)tan(aimAngle));
+	normalizedVector.Normalize();
+	normalizedVector.X *= fireVelocity;
+	normalizedVector.Y *= fireVelocity;
+	normalizedVector.Z *= fireVelocity;
+	Set_Velocity(projectile,normalizedVector);
+}
+void JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer::Created(GameObject *obj)
+{
+	team = Get_Int_Parameter("PlayerType");
+	id = Get_Int_Parameter("ID");
+	custom = Get_Int_Parameter("Custom");
+	Param = Get_Int_Parameter("Param");
+	delay = Get_Float_Parameter("Delay");
+	triggerOnce = Get_Int_Parameter("TriggerOnce") != 0;
+}
+void JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == CUSTOM_EVENT_VEHICLE_ENTERED)
+	{
+		if (CheckPlayerType(sender,team))
+			return;
+		GameObject *object = id ? (id == -1 ? sender : Commands->Find_Object(id)) : obj;
+		Commands->Send_Custom_Event(sender,object,custom,Param,delay);
+		if (triggerOnce)
+			Destroy_Script();
+	}
+}
+void JMG_Utility_Custom_Send_Custom_On_Count_From_Sender::Created(GameObject *obj)
+{
+	custom = Get_Int_Parameter("Custom");
+	resetCustom = Get_Int_Parameter("ResetCustom");
+	count = Get_Int_Parameter("Count");
+}
+void JMG_Utility_Custom_Send_Custom_On_Count_From_Sender::Custom(GameObject *obj,int message,int param,GameObject *sender)
+{
+	if (message == resetCustom)
+	{
+		count = Get_Int_Parameter("Count");
+	}
+	if (message == custom && count)
+	{
+		count--;
+		if (count)
+			return;
+		int id = Get_Int_Parameter("ID");
+		GameObject *object = id ? (id == -1 ? sender : Commands->Find_Object(id)) : obj;
+		if (object)
+		{
+			int Param = Get_Int_Parameter("Param");
+			Commands->Send_Custom_Event(sender,object,Get_Int_Parameter("SendCustom"),Param == -1 ? param : Param,Get_Float_Parameter("Delay"));
+		}
+	}
+}
 ScriptRegistrant<JMG_Utility_Check_If_Script_Is_In_Library> JMG_Utility_Check_If_Script_Is_In_Library_Registrant("JMG_Utility_Check_If_Script_Is_In_Library","ScriptName:string,CppName:string");
 ScriptRegistrant<JMG_Send_Custom_When_Custom_Sequence_Matched> JMG_Send_Custom_When_Custom_Sequence_Matched_Registrant("JMG_Send_Custom_When_Custom_Sequence_Matched","Success_Custom=0:int,Correct_Step_Custom=0:int,Partial_Failure_Custom=0:int,Failure_Custom=0:int,Send_To_ID=0:int,Custom_0=0:int,Custom_1=0:int,Custom_2=0:int,Custom_3=0:int,Custom_4=0:int,Custom_5=0:int,Custom_6=0:int,Custom_7=0:int,Custom_8=0:int,Custom_9=0:int,Disable_On_Success=1:int,Disable_On_Failure=0:int,Starts_Enabled=1:int,Enable_Custom=0:int,Correct_Step_Saftey=0:int,Failure_Saftey=1:int,Max_Failures=1:int");
 ScriptRegistrant<JMG_Utility_Change_Model_On_Timer> JMG_Utility_Change_Model_On_Timer_Registrant("JMG_Utility_Change_Model_On_Timer","Model=null:string,Time=0:float");
@@ -10887,7 +12127,7 @@
 ScriptRegistrant<JMG_Utility_Sync_System_Object> JMG_Utility_Sync_System_Object_Registrant("JMG_Utility_Sync_System_Object","");
 ScriptRegistrant<JMG_Utility_Sync_System_Controller> JMG_Utility_Sync_System_Controller_Registrant("JMG_Utility_Sync_System_Controller","Sync_Rate=1.0:float");
 ScriptRegistrant<JMG_Utility_Sync_Object_Periodically> JMG_Utility_Sync_Object_Periodically_Registrant("JMG_Utility_Sync_Object_Periodically","Sync_Rate=1.0:float");
-ScriptRegistrant<JMG_Utility_Basic_Spawner> JMG_Utility_Basic_Spawner_Registrant("JMG_Utility_Basic_Spawner","SpawnPreset:string,RespawnTime=0.0:float,RespawnTimeRandom=0:float,StartsEnabled=1:int,EnableOrDisableCustom=0:int,SpawnLimit=-1:int,DelayOnStartup=0:int");
+ScriptRegistrant<JMG_Utility_Basic_Spawner> JMG_Utility_Basic_Spawner_Registrant("JMG_Utility_Basic_Spawner","SpawnPreset:string,RespawnTime=0.0:float,RespawnTimeRandom=0:float,StartsEnabled=1:int,EnableOrDisableCustom=0:int,SpawnLimit=-1:int,DelayOnStartup=0:int,Attach_Scripts_Group_ID=-1:int");
 ScriptRegistrant<JMG_Utility_Basic_Spawner_Spawned_Object> JMG_Utility_Basic_Spawner_Spawned_Object_Registrant("JMG_Utility_Basic_Spawner_Spawned_Object","ControllerId:int");
 ScriptRegistrant<JMG_Utility_AI_Engineer> JMG_Utility_AI_Engineer_Registrant("JMG_Utility_AI_Engineer","RepaiarGun_Preset=Weapon_RepairGun_Player:string,Weapon_Preset=Weapon_Pistol_Player:string,PatrolRange=50.0:float,BaseCenterPoint=0.0 0.0 0.0:vector3,MinHP[Soldiers|Vehicles|Turrets]=0.5 0.75 0.9:vector3,MinHP[EngineerTarget|C4|Beacon]=0.25 0.0 0.0:vector3,Priority[RepairTargets|C4|Beacons]=1.0 1.0 1.0:vector3,WanderGroupID=-1:int");
 ScriptRegistrant<JMG_Utility_Set_Object_Visibility_For_Player_On_Custom> JMG_Utility_Set_Object_Visibility_For_Player_On_Custom_Registrant("JMG_Utility_Set_Object_Visibility_For_Player_On_Custom","Custom:int,PlayerID:int,Visible:int");
@@ -10918,7 +12158,7 @@
 ScriptRegistrant<JMG_Utility_Zone_Enable_Spawners_In_Range> JMG_Utility_Zone_Enable_Spawners_In_Range_Registrant("JMG_Utility_Zone_Enable_Spawners_In_Range","StartID:int,EndID:int,PlayerType=2:int,Enable=1:int,TriggerOnce=1:int");
 ScriptRegistrant<JMG_Utility_Display_Message_On_Vehicle_Enter> JMG_Utility_Display_Message_On_Vehicle_Enter_Registrant("JMG_Utility_Display_Message_On_Vehicle_Enter","StringId:int,MessageOverride=null:string,Color[R|G|B]=0.0 1.0 0.0:vector3,DriverOnly=1:int,ShowOnce=1:int,PlayerType=2:int");
 ScriptRegistrant<JMG_Utility_Zone_Apply_Damage_On_Enter> JMG_Utility_Zone_Apply_Damage_On_Enter_Registrant("JMG_Utility_Zone_Apply_Damage_On_Enter","ID:int,DamageAmount:float,Warhead=None:string,DamageOccupants=1:int,PlayerType=2:int,OnlyOnce=0:int");
-ScriptRegistrant<JMG_Utility_AI_Guardian_Aircraft> JMG_Utility_AI_Guardian_Aircraft_Registrant("JMG_Utility_AI_Guardian_Aircraft","WanderingAIGroupID:int,FlightHeight=25.0:float,FireRange=100.0:float,FaceTarget=1:int,StealthModeOverride=0:int");
+ScriptRegistrant<JMG_Utility_AI_Guardian_Aircraft> JMG_Utility_AI_Guardian_Aircraft_Registrant("JMG_Utility_AI_Guardian_Aircraft","WanderingAIGroupID:int,FlightHeight=25.0:float,FireRange=-1.0:float,FaceTarget=1:int,StealthModeOverride=0:int,NewPointDelay=0:int");
 ScriptRegistrant<JMG_Utility_Switch_Weapon_While_Primary_Empty> JMG_Utility_Switch_Weapon_While_Primary_Empty_Registrant("JMG_Utility_Switch_Weapon_While_Primary_Empty","PrimaryWeapon=null:string,SecondaryWeapon=null:string,ReloadTime=0.0:float,PrimaryToSecondaryTime=-1.0:float,SecondaryToPrimaryTime=-1.0:float,IdlePrimaryAnim=null:string,IdleSecondaryAnim=null:string,PrimaryToSecondaryAnim=null:string,SecondaryToPrimaryAnim=null:string,PrimaryToSecondarySound=null:string,SecondaryToPrimarySound=null:string");
 ScriptRegistrant<JMG_Utility_Send_Custom_When_Near_Building> JMG_Utility_Send_Custom_When_Near_Building_Registrant("JMG_Utility_Send_Custom_When_Near_Building","SendCustomObjectID=0:int,NearToBuildingCustom:int,FarFromBuildingCustom:int,CloseToBuildingDistance=1.0:float,BuildingPlayerType=2:int,CheckDeadBuildings=1:int,CheckRate=0.25:float");
 ScriptRegistrant<JMG_Utility_AI_Engineer_Repair_Target> JMG_Utility_AI_Engineer_Repair_Target_Registrant("JMG_Utility_AI_Engineer_Repair_Target","");
@@ -10932,7 +12172,7 @@
 ScriptRegistrant<JMG_Utility_Custom_Enable_Spawners_In_Range> JMG_Utility_Custom_Enable_Spawners_In_Range_Registrant("JMG_Utility_Custom_Enable_Spawners_In_Range","StartID:int,EndID:int,Custom:int,Enable=1:int");
 ScriptRegistrant<JMG_Utility_Send_Custom_On_Player_Count> JMG_Utility_Send_Custom_On_Player_Count_Registrant("JMG_Utility_Send_Custom_On_Player_Count","PlayerCount=0:int,ID=0:int,Custom:int,Param:int,Delay:float,TriggerOn[eq|<|>]=1.0 0.0 0.0:vector3,SupressMatchSpam=1:int,DestroyAfterTrigger=0:int");
 ScriptRegistrant<JMG_Utility_Send_Custom_To_Preset_On_Message> JMG_Utility_Send_Custom_To_Preset_On_Message_Registrant("JMG_Utility_Send_Custom_To_Preset_On_Message","TriggerCustom:int,Preset:string,Custom:int,Param:int,Delay=0.0:float");
-ScriptRegistrant<JMG_Utility_Basic_Spawner_Attach_Script> JMG_Utility_Basic_Spawner_Attach_Script_Registrant("JMG_Utility_Basic_Spawner_Attach_Script","Script:string,Params:string,Delim=@:string");
+ScriptRegistrant<JMG_Utility_Basic_Spawner_Attach_Script> JMG_Utility_Basic_Spawner_Attach_Script_Registrant("JMG_Utility_Basic_Spawner_Attach_Script","Script:string,Params:string,Delim=@:string,Scripts_Group_ID=-1:int");
 ScriptRegistrant<JMG_Utility_Send_Custom_On_Preset_Enter> JMG_Utility_Send_Custom_On_Preset_Enter_Registrant("JMG_Utility_Send_Custom_On_Preset_Enter","PresetName:string,ID:int,Custom:int,Param:int,Delay=0.0:float");
 ScriptRegistrant<JMG_Utility_Basic_Spawner_Set_Prespawn_Location> JMG_Utility_Basic_Spawner_Set_Prespawn_Location_Registrant("JMG_Utility_Basic_Spawner_Set_Prespawn_Location","OverrideLocation:Vector3");
 ScriptRegistrant<JMG_Utility_Teleport_On_Pickup> JMG_Utility_Teleport_On_Pickup_Registrant("JMG_Utility_Teleport_On_Pickup","ObjectID:int,Position=0.0 0.0 0.0:Vector3,MaxDistance=1.5:float,Facing=-999.0:float");
@@ -10953,7 +12193,7 @@
 ScriptRegistrant<JMG_Utility_Regen_HitPoints> JMG_Utility_Regen_HitPoints_Registrant("JMG_Utility_Regen_HitPoints","RegenHealth=1:int,HealthAmount=1.0:float,HealthPerPlayer=0.0:float,RegenArmor=1:int,ArmorAmount=1.0:float,ArmorPerPlayer=0.0:float,Rate=1.0:float,DamageDelay=0.0:float");
 ScriptRegistrant<JMG_Utility_Toggle_Flight_On_Delay> JMG_Utility_Toggle_Flight_On_Delay_Registrant("JMG_Utility_Toggle_Flight_On_Delay","Delay:float");
 ScriptRegistrant<JMG_Utility_Fainting_Soldier> JMG_Utility_Fainting_Soldier_Registrant("JMG_Utility_Fainting_Soldier","FaintAnimation:string,LayAnimation:string,StandAnimation:string,FaintSound=null:string,StandSound=null:string,ChangeArmorTypeWhenKnockedOut=0:int,ArmorTypeWhileKnockedOut=Blamo:string,TeamWhileKnockedOut=-2:int");
-ScriptRegistrant<JMG_Utility_AI_Guardian_Infantry> JMG_Utility_AI_Guardian_Infantry_Registrant("JMG_Utility_AI_Guardian_Infantry","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=100.0:float,FaceTarget=1:int,CheckBlocked=1:int,StealthModeOverride=0:int");
+ScriptRegistrant<JMG_Utility_AI_Guardian_Infantry> JMG_Utility_AI_Guardian_Infantry_Registrant("JMG_Utility_AI_Guardian_Infantry","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=-1.0:float,FaceTarget=1:int,CheckBlocked=1:int,StealthModeOverride=0:int");
 ScriptRegistrant<JMG_Utility_Set_Innate_Max_Wander_Distance> JMG_Utility_Set_Innate_Max_Wander_Distance_Registrant("JMG_Utility_Set_Innate_Max_Wander_Distance","Distance:float");
 ScriptRegistrant<JMG_Utility_Switch_Weapon_To_Empty_Hands_Until_Custom> JMG_Utility_Switch_Weapon_To_Empty_Hands_Until_Custom_Registrant("JMG_Utility_Switch_Weapon_To_Empty_Hands_Until_Custom","Custom:int");
 ScriptRegistrant<JMG_Utility_Set_Skin_And_Shield_Type_On_Custom> JMG_Utility_Set_Skin_And_Shield_Type_On_Custom_Registrant("JMG_Utility_Set_Skin_And_Shield_Type_On_Custom","Custom:int,SkinType=None:string,ShieldType=None:string");
@@ -10973,7 +12213,7 @@
 ScriptRegistrant<JMG_Utility_Play_Music_On_Join_Custom_Change_Music> JMG_Utility_Play_Music_On_Join_Custom_Change_Music_Registrant("JMG_Utility_Play_Music_On_Join_Custom_Change_Music","Custom:int,Music=null:string,FadeOut:int,FadeIn:int,Repeat=1:int");
 ScriptRegistrant<JMG_Utility_Zone_Send_Custom_If_Has_Weapon> JMG_Utility_Zone_Send_Custom_If_Has_Weapon_Registrant("JMG_Utility_Zone_Send_Custom_If_Has_Weapon","WeaponName:string,ID=0:int,Custom=0:int,Param=0:int,Delay=0.0:float,PlayerType=2:int,TriggerOnce=1:int,RemoveWeapon=0:int");
 ScriptRegistrant<JMG_Utility_Receive_All_Customs_To_Send_Custom> JMG_Utility_Receive_All_Customs_To_Send_Custom_Registrant("JMG_Utility_Receive_All_Customs_To_Send_Custom","Custom0=0:int,Custom1=0:int,Custom2=0:int,Custom3=0:int,Custom4=0:int,Custom5=0:int,Custom6=0:int,Custom7=0:int,Custom8=0:int,Custom9=0:int,ID=0:int,Custom=0:int,Param=0:int,Delay=0.0:float,TriggerOnce=1:int,ResetCustom=0:int");
-ScriptRegistrant<JMG_Utility_Zone_Teleport_To_Random_Wander_Point> JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Registrant("JMG_Utility_Zone_Teleport_To_Random_Wander_Point","WanderingAIGroupID=-1:int,SafeTeleportDistance=1.5:float,ChangeGroupIDCustom=0:int,PlayerType=2:int,RetryOnFailure=0:int");
+ScriptRegistrant<JMG_Utility_Zone_Teleport_To_Random_Wander_Point> JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Registrant("JMG_Utility_Zone_Teleport_To_Random_Wander_Point","WanderingAIGroupID=-1:int,SafeTeleportDistance=1.5:float,ChangeGroupIDCustom=0:int,PlayerType=2:int,RetryOnFailure=0:int,AiOnly=0:int");
 ScriptRegistrant<JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach> JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach_Registrant("JMG_Utility_Zone_Teleport_To_Random_Wander_Point_Attach","WanderingAIGroupID=-1:int,SafeTeleportDistance=1.5:float");
 ScriptRegistrant<JMG_Utility_Zone_Set_Player_Type> JMG_Utility_Zone_Set_Player_Type_Registrant("JMG_Utility_Zone_Set_Player_Type","SetPlayerType=0:int,RequiredPlayerType=2:int");
 ScriptRegistrant<JMG_Utility_Zone_Send_Custom_Enter> JMG_Utility_Zone_Send_Custom_Enter_Registrant("JMG_Utility_Zone_Send_Custom_Enter","ID=0:int,Custom=0:int,Param=0:int,Delay=0.0:float,PlayerType=2:int,TriggerOnce=1:int");
@@ -11005,9 +12245,9 @@
 ScriptRegistrant<JMG_Utility_Custom_Send_Custom> JMG_Utility_Custom_Send_Custom_Registrant("JMG_Utility_Custom_Send_Custom","Custom:int,ID=0:int,SendCustom:int,Param:int,Delay=0.0:float,RandomDelay=0.0:float,RandomChance=0.0:float");
 ScriptRegistrant<JMG_Utility_Damage_Unoccupied_Vehicle> JMG_Utility_Damage_Unoccupied_Vehicle_Registrant("JMG_Utility_Damage_Unoccupied_Vehicle","Rate=0.1:float,Delay=60.0:float,DecreaseTick=0.1:float,IncreaseTick=0.1:float,Damage=1.0:float,Warhead=None:string");
 ScriptRegistrant<JMG_Utility_Custom_Damage_All_Soldiers_On_Team> JMG_Utility_Custom_Damage_All_Soldiers_On_Team_Registrant("JMG_Utility_Custom_Damage_All_Soldiers_On_Team","Custom:int,Team:int,Damage:float,Warhead=None:string");
-ScriptRegistrant<JMG_Utility_AI_Guardian_Vehicle> JMG_Utility_AI_Guardian_Vehicle_Registrant("JMG_Utility_AI_Guardian_Vehicle","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=100.0:float,FaceTarget=1:int,CheckBlocked=1:int,AimAtFeet=1:int,TurnOffEngineOnArrival=1:int,StealthModeOverride=0:int");
+ScriptRegistrant<JMG_Utility_AI_Guardian_Vehicle> JMG_Utility_AI_Guardian_Vehicle_Registrant("JMG_Utility_AI_Guardian_Vehicle","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=-1.0:float,FaceTarget=1:int,CheckBlocked=1:int,AimAtFeet=1:int,TurnOffEngineOnArrival=1:int,StealthModeOverride=0:int");
 ScriptRegistrant<JMG_Utility_Custom_Destroy_Closest_Model_To_Position> JMG_Utility_Custom_Destroy_Closest_Model_To_Position_Registrant("JMG_Utility_Custom_Destroy_Closest_Model_To_Position","Custom:int,Model:string,Position:Vector3,MaxDistance=0.0:float");
-ScriptRegistrant<JMG_Utility_Send_Custom_On_Deaths_Controller> JMG_Utility_Send_Custom_On_Deaths_Controller_Registrant("JMG_Utility_Send_Custom_On_Deaths_Controller","MaxDeaths=100:int,DeathReminder=25:int,UrgentDeathReminder=10:int,StringID=12628:int,ReminderMessage=%d %s^ only %d more %s allowed!:string,Delim=^:string,ReminderMessageOrder=0:int,DeathSingular=casualty:string,DeathPlural=casualties:string,RemainingSingular=death:string,RemainingPlural=deaths:string,ReminderColor=1.0 0.0 0.0:Vector3,ID=0:int,Custom=0:int,Param=0:int,Delay=0:int,ReminderCustom=0:int,AddDeathsWhenNoPlayers=0.0:float,NoPlayersAddDeathSaftyTime=0:int");
+ScriptRegistrant<JMG_Utility_Send_Custom_On_Deaths_Controller> JMG_Utility_Send_Custom_On_Deaths_Controller_Registrant("JMG_Utility_Send_Custom_On_Deaths_Controller","MaxDeaths=15:int,DeathReminder=5:int,UrgentDeathReminder=5:int,StringID=12628:int,ReminderMessage=%d %s^ only %d more %s allowed!:string,Delim=^:string,ReminderMessageOrder=0:int,DeathSingular=casualty:string,DeathPlural=casualties:string,RemainingSingular=death:string,RemainingPlural=deaths:string,ReminderColor=1.0 0.0 0.0:Vector3,ID=0:int,Custom=0:int,Param=0:int,Delay=0:int,ReminderCustom=0:int,AddDeathsWhenNoPlayers=10.0:float,NoPlayersAddDeathSaftyTime=150:int,RequiresADeathToStartNoPlayerAdd=1:int,AnnounceOnFirstDeath=1:int,OnlyTrackPlayerDeaths=1:int");
 ScriptRegistrant<JMG_Utility_Send_Custom_On_Deaths_Reporter> JMG_Utility_Send_Custom_On_Deaths_Reporter_Registrant("JMG_Utility_Send_Custom_On_Deaths_Reporter","");
 ScriptRegistrant<JMG_Utility_AI_Goto_Player_Ignore_Object> JMG_Utility_AI_Goto_Player_Ignore_Object_Registrant("JMG_Utility_AI_Goto_Player_Ignore_Object","");
 ScriptRegistrant<JMG_Utility_AI_Goto_Enemy_Ignore_Object> JMG_Utility_AI_Goto_Enemy_Ignore_Object_Registrant("JMG_Utility_AI_Goto_Enemy_Ignore_Object","");
@@ -11025,7 +12265,7 @@
 ScriptRegistrant<JMG_Utility_Death_Create_Object_At_Bone_Position> JMG_Utility_Death_Create_Object_At_Bone_Position_Registrant("JMG_Utility_Death_Create_Object_At_Bone_Position","Preset:string,Bone:string,MaxDistance=1.5:float");
 ScriptRegistrant<JMG_Utility_Enable_Loiter> JMG_Utility_Enable_Loiter_Registrant("JMG_Utility_Enable_Loiter","");
 ScriptRegistrant<JMG_Utility_Custom_Switch_Weapon> JMG_Utility_Custom_Switch_Weapon_Registrant("JMG_Utility_Custom_Switch_Weapon","Custom:int,Weapon:string");
-ScriptRegistrant<JMG_Utility_HUD_Count_Down> JMG_Utility_HUD_Count_Down_Registrant("JMG_Utility_HUD_Count_Down","TimeInSeconds:int,StringID=12628:int,Color=1.0 0.0 0.0:Vector3,WarningMessage=%d %s before launch.:string,Delim=^:string,HourSingular=hour:string,HourPlural=hours:string,MinuteSingular=minute:string,MinutePlural=minutes:string,SecondSingular=second:string,SecondPlural=seconds:string");
+ScriptRegistrant<JMG_Utility_HUD_Count_Down> JMG_Utility_HUD_Count_Down_Registrant("JMG_Utility_HUD_Count_Down","TimeInSeconds:int,StringID=12628:int,Color=1.0 0.0 0.0:Vector3,WarningMessage=%d %s before launch.:string,Delim=^:string,HourSingular=hour:string,HourPlural=hours:string,MinuteSingular=minute:string,MinutePlural=minutes:string,SecondSingular=second:string,SecondPlural=seconds:string,StartsPaused=0:int,PauseCustom=0:int");
 ScriptRegistrant<JMG_Utility_HUD_Count_Down_Send_Custom> JMG_Utility_HUD_Count_Down_Send_Custom_Registrant("JMG_Utility_HUD_Count_Down_Send_Custom","TriggerTime:int,ID:int,Custom:int,Param=0:int,Delay=0.0:float");
 ScriptRegistrant<JMG_Utility_Zone_Screen_Fade> JMG_Utility_Zone_Screen_Fade_Registrant("JMG_Utility_Zone_Screen_Fade","Color:vector3,Opacity:float,Transition:float,PlayerType=2:int");
 ScriptRegistrant<JMG_Utility_Custom_Triggers_Enemy_Seen> JMG_Utility_Custom_Triggers_Enemy_Seen_Registrant("JMG_Utility_Custom_Triggers_Enemy_Seen","Custom:int,Visible:int");
@@ -11060,7 +12300,7 @@
 ScriptRegistrant<JMG_Utility_Objective_System_Objective_Status_Update_Custom> JMG_Utility_Objective_System_Objective_Status_Update_Custom_Registrant("JMG_Utility_Objective_System_Objective_Status_Update_Custom","Custom:int,ObjectiveID:int,NewObjectiveStringID:int,NewObjectiveMarkerObjectID:int");
 ScriptRegistrant<JMG_Utility_Objective_System_Objective_Failed_Custom> JMG_Utility_Objective_System_Objective_Failed_Custom_Registrant("JMG_Utility_Objective_System_Objective_Failed_Custom","Custom:int,ObjectiveID:int");
 ScriptRegistrant<JMG_Utility_Force_Player_Team_At_Gameover> JMG_Utility_Force_Player_Team_At_Gameover_Registrant("JMG_Utility_Force_Player_Team_At_Gameover","Team:int");
-ScriptRegistrant<JMG_Utility_AI_Guardian_Generic> JMG_Utility_AI_Guardian_Generic_Registrant("JMG_Utility_AI_Guardian_Generic","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=100.0:float,FaceTarget=1:int,CheckBlocked=1:int,ArriveDistance=1.0:float,FlightHeight=0.0:float,TurnOffEngineOnArrival=1:int,UseSecondaryAttack=0:int,StealthModeOverride=0:int");
+ScriptRegistrant<JMG_Utility_AI_Guardian_Generic> JMG_Utility_AI_Guardian_Generic_Registrant("JMG_Utility_AI_Guardian_Generic","WanderingAIGroupID:int,WanderSpeed=1.0:float,FireRange=-1.0:float,FaceTarget=1:int,CheckBlocked=1:int,ArriveDistance=1.0:float,FlightHeight=0.0:float,TurnOffEngineOnArrival=1:int,UseSecondaryAttack=0:int,StealthModeOverride=0:int");
 ScriptRegistrant<JMG_Utility_Custom_Create_Object_In_Front_Of_Self> JMG_Utility_Custom_Create_Object_In_Front_Of_Self_Registrant("JMG_Utility_Custom_Create_Object_In_Front_Of_Self","Custom:int,PresetName:string,Distance:float,Height:float,Rotation:float");
 ScriptRegistrant<JMG_Utility_Send_Custom_When_No_More_Units_On_Team_Exist_Ignore> JMG_Utility_Send_Custom_When_No_More_Units_On_Team_Exist_Ignore_Registrant("JMG_Utility_Send_Custom_When_No_More_Units_On_Team_Exist_Ignore","Ignore:int");
 ScriptRegistrant<JMG_Utility_Custom_Damage_All_Vehicles_On_Team> JMG_Utility_Custom_Damage_All_Vehicles_On_Team_Registrant("JMG_Utility_Custom_Damage_All_Vehicles_On_Team","Custom:int,Team:int,Damage:float,Warhead=None:string");
@@ -11082,7 +12322,7 @@
 ScriptRegistrant<JMG_Utility_Send_Custom_When_Speed_Below_Amount> JMG_Utility_Send_Custom_When_Speed_Below_Amount_Registrant("JMG_Utility_Send_Custom_When_Speed_Below_Amount","Speed:float,ID:int,Message:int,Param=0:int,Rate=0.1:float,Repeat=0:int,StartsEnabled=1:int,EnableCustom=0:int");
 ScriptRegistrant<JMG_Utility_Send_Custom_When_Velocity_Exceeds_Amount> JMG_Utility_Send_Custom_When_Velocity_Exceeds_Amount_Registrant("JMG_Utility_Send_Custom_When_Velocity_Exceeds_Amount","Velocity[Forward|Backward|Left]=-1.0 -1.0 -1.0:Vector3,Velocity[Right|Up|Down]=-1.0 -1.0 -1.0:Vector3,OnlyTriggerOn=0:int,OnlyTriggerOnMin[Horizontal|Vertical]=-1.0 -1.0 0.0:Vector3,OnlyTriggerOnMax[Horizontal|Vertical]=-1.0 -1.0 0.0:Vector3,ID:int,Message:int,Param=0:int,Rate=0.1:float,Repeat=0:int,StartsEnabled=1:int,EnableCustom=0:int");
 ScriptRegistrant<JMG_Utility_Custom_Change_Character> JMG_Utility_Custom_Change_Character_Registrant("JMG_Utility_Custom_Change_Character","Custom:int,Preset:string,Cost:float,SuccessSound:string,FailSound:string,AllowRepurchase=0:int");
-ScriptRegistrant<JMG_Utility_Apply_Damage_While_In_Zone> JMG_Utility_Apply_Damage_While_In_Zone_Registrant("JMG_Utility_Apply_Damage_While_In_Zone","Rate:float,DamageAmount:float,Warhead:string,DamagerId:int");
+ScriptRegistrant<JMG_Utility_Apply_Damage_While_In_Zone> JMG_Utility_Apply_Damage_While_In_Zone_Registrant("JMG_Utility_Apply_Damage_While_In_Zone","Rate:float,DamageAmount:float,Warhead:string,DamagerId:int,Team=2:int");
 ScriptRegistrant<JMG_Utility_Apply_Damage_On_Timer_Base> JMG_Utility_Apply_Damage_While_In_Zone_Attached_Registrant("JMG_Utility_Apply_Damage_While_In_Zone_Attached","Rate:float,DamageAmount:float,Warhead:string,DamagerId:int");
 ScriptRegistrant<JMG_Utility_Apply_Damage_On_Timer_Base> JMG_Utility_Apply_Damage_On_Timer_Registrant("JMG_Utility_Apply_Damage_On_Timer","Rate:float,DamageAmount:float,Warhead:string,DamagerId:int");
 ScriptRegistrant<JMG_Utility_Zone_Send_Custom_Exit> JMG_Utility_Zone_Send_Custom_Exit_Registrant("JMG_Utility_Zone_Send_Custom_Exit","ID=0:int,Custom=0:int,Param=0:int,Delay=0.0:float,PlayerType=2:int,TriggerOnce=1:int");
@@ -11106,10 +12346,10 @@
 ScriptRegistrant<JMG_Utility_Credit_Trickle_When_Not_AFK> JMG_Utility_Credit_Trickle_When_Not_AFK_Registrant("JMG_Utility_Credit_Trickle_When_Not_AFK","Credits=1.0:float,Rate=1.0:float,TrickleCap=0.0:float,Team=2:int,Custom=0:int");
 ScriptRegistrant<JMG_Utility_Killed_Create_Object> JMG_Utility_Killed_Create_Object_Registrant("JMG_Utility_Killed_Create_Object","Preset:string");
 ScriptRegistrant<JMG_Utility_Damaged_Create_Object_When_Shield_Zero> JMG_Utility_Damaged_Create_Object_When_Shield_Zero_Registrant("JMG_Utility_Damaged_Create_Object_When_Shield_Zero","Preset:string");
-ScriptRegistrant<JMG_Utility_Basic_Spawner_In_Radius> JMG_Utility_Basic_Spawner_In_Radius_Registrant("JMG_Utility_Basic_Spawner_In_Radius","Spawn_Preset:string,Spawn_Rate:float,Random_Spawn_Rate:float,Spawn_At_A_Time:int,Min_Spawn_Radius:float,Max_Spawn_Radius:float,Initial_Spawn_Height=0.0:float,Spawn_Limit=-1:int,Spawn_Location=-1.0 -1.0 -1.0:Vector3,X_Multiplier=1.0:float,Y_Multiplier=1.0:float,Collision_Check=1:int,Collision_Retry_Attempts=3:int,Collision_Add_Height=1.0:float,Change_Spawn_Cap_Custom=-1:int,Initial_Spawn=-1:int,Script_ID=-1:int,Point_Must_Be_In_Pathfind=1:int,Manual_Facing=0:int,Face_Location=0.0 0.0 0.0:vector3,Face_Direction=0.0:float,Ignore_Ray_Cast_Failure=0:int,Min_Distance_Between_Objects=0.0:float,Spawn_Group_ID=0:int,Starts_Disabled=0:int,Enable_Disable_Custom=0:int");
+ScriptRegistrant<JMG_Utility_Basic_Spawner_In_Radius> JMG_Utility_Basic_Spawner_In_Radius_Registrant("JMG_Utility_Basic_Spawner_In_Radius","Spawn_Preset:string,Spawn_Rate:float,Random_Spawn_Rate:float,Spawn_At_A_Time:int,Min_Spawn_Radius:float,Max_Spawn_Radius:float,Initial_Spawn_Height=0.0:float,Spawn_Limit=-1:int,Spawn_Location=-1.0 -1.0 -1.0:Vector3,X_Multiplier=1.0:float,Y_Multiplier=1.0:float,Collision_Check=1:int,Collision_Retry_Attempts=3:int,Collision_Add_Height=1.0:float,Change_Spawn_Cap_Custom=-1:int,Initial_Spawn=-1:int,Script_ID=-1:int,Point_Must_Be_In_Pathfind=1:int,Manual_Facing=0:int,Face_Location=0.0 0.0 0.0:vector3,Face_Direction=0.0:float,Ignore_Ray_Cast_Failure=0:int,Min_Distance_Between_Objects=0.0:float,Spawn_Group_ID=0:int,Starts_Disabled=0:int,Enable_Disable_Custom=0:int,Raycast_Direction_Override[Bottom|Top|Unused]=0.0 0.0 0.0:Vector3,Attach_Scripts_Group_ID=-1:int,PlayersAddToSpawnAtATime=0.0:float");
 ScriptRegistrant<JMG_Utility_Basic_Spawner_In_Radius_Attached> JMG_Utility_Basic_Spawner_In_Radius_Attached_Registrant("JMG_Utility_Basic_Spawner_In_Radius_Attached","Controller_ID:int,Script_ID:int,Spawn_Group_ID:int");
 ScriptRegistrant<JMG_Utility_Flying_Vehicle_Crash_Apply_Damage> JMG_Utility_Flying_Vehicle_Crash_Apply_Damage_Registrant("JMG_Utility_Flying_Vehicle_Crash_Apply_Damage","Min_Collision_Speed=33.528:float,Max_Collision_Speed=42.4688:float,Collision_Sound=vehicle_collide_01:string,Explosion_Preset=Plane_Air_Collision:string,Subtract_Min_Speed=0:int");
-ScriptRegistrant<JMG_Utility_Enemy_Seen_Send_Custom> JMG_Utility_Enemy_Seen_Send_Custom_Registrant("JMG_Utility_Enemy_Seen_Send_Custom","Enemy_Preset_ID:int,ID:int,Visible_Message:int,Visible_Param:int,Not_Visible_Message:int,Not_Visible_Param:int,Max_Lost_Sight_Time=2.5:float");
+ScriptRegistrant<JMG_Utility_Enemy_Seen_Send_Custom> JMG_Utility_Enemy_Seen_Send_Custom_Registrant("JMG_Utility_Enemy_Seen_Send_Custom","Enemy_Preset_ID:int,ID:int,Visible_Message:int,Visible_Param:int,Not_Visible_Message:int,Not_Visible_Param:int,Max_Lost_Sight_Time=2.5:float,RepeatSendSeenCustom=0:int,AttackPriority[Car|Tank|Bike]=1.0 1.0 1.0:vector3,AttackPriority[Flying|Turret|Boat]=1.0 1.0 1.0:vector3,AttackPriority[Submarine|Infantry|Unused]=1.0 1.0 0.0:vector3");
 ScriptRegistrant<JMG_Utility_Custom_Send_Custom_If_Script_Attached> JMG_Utility_Custom_Send_Custom_If_Script_Attached_Registrant("JMG_Utility_Custom_Send_Custom_If_Script_Attached","Custom:int,Script:string,ID=0:int,SendCustom:int,Param:int,Delay:float");
 ScriptRegistrant<JMG_Utility_Custom_Send_Custom_If_Script_Not_Attached> JMG_Utility_Custom_Send_Custom_If_Script_Not_Attached_Registrant("JMG_Utility_Custom_Send_Custom_If_Script_Not_Attached","Custom:int,Script:string,ID=0:int,SendCustom:int,Param:int,Delay:float");
 ScriptRegistrant<JMG_Utility_Basic_Spawner_In_Radius_Controller> JMG_Utility_Basic_Spawner_In_Radius_Controller_Registrant("JMG_Utility_Basic_Spawner_In_Radius_Controller","");
@@ -11141,3 +12381,24 @@
 ScriptRegistrant<JMG_Utility_Swimming_Infantry_Advanced_Controller> JMG_Utility_Swimming_Infantry_Advanced_Controller_Registrant("JMG_Utility_Swimming_Infantry_Advanced_Controller","");
 ScriptRegistrant<JMG_Utility_Swimming_Infantry_Advanced_Add_All_Of_Style> JMG_Utility_Swimming_Infantry_Advanced_Add_All_Of_Style_Registrant("JMG_Utility_Swimming_Infantry_Advanced_Add_All_Of_Style","WeaponGroupID:int,HoldStyle:int,AnimHoldStyle:int,MovementSpeed=1.0:float,Delay=0.0:float");
 ScriptRegistrant<JMG_Utility_Swimming_Infantry_Advanced_Add_Weapon> JMG_Utility_Swimming_Infantry_Advanced_Add_Weapon_Registrant("JMG_Utility_Swimming_Infantry_Advanced_Add_Weapon","WeaponGroupID:int,WeaponName:string,AnimHoldStyle:int,MovementSpeed=1.0:float,Delay=0.0:float");
+ScriptRegistrant<JMG_Utility_Custom_Send_Cycled_Customs> JMG_Utility_Custom_Send_Cycled_Customs_Registrant("JMG_Utility_Custom_Send_Cycled_Customs","Custom:int,ID=0:int,SendCustom0=0:int,SendCustom1=0:int,SendCustom2=0:int,SendCustom3=0:int,SendCustom4=0:int,SendCustom5=0:int,SendCustom6=0:int,SendCustom7=0:int,SendCustom8=0:int,SendCustom9=0:int,Param0=0:int,Param1=0:int,Param2=0:int,Param3=0:int,Param4=0:int,Param5=0:int,Param6:=0int,Param7=0:int,Param8=0:int,Param9=0:int,Delay0=0.0:float,Delay1=0.0:float,Delay2=0.0:float,Delay3=0.0:float,Delay4=0.0:float,Delay5=0.0:float,Delay6=0.0:float,Delay7=0.0:float,Delay8=0.0:float,Delay9=0.0:float,RandomDelay=0.0:float,RandomChance=0.0:float");
+ScriptRegistrant<JMG_Utility_Killed_Send_Custom_From_Killer> JMG_Utility_Killed_Send_Custom_From_Killer_Registrant("JMG_Utility_Killed_Send_Custom_From_Killer","ID:int,Custom:int,Param:int,Delay:float");
+ScriptRegistrant<JMG_Utility_Emulate_Sound_Heard_On_FDS> JMG_Utility_Emulate_Sound_Heard_On_FDS_Registrant("JMG_Utility_Emulate_Sound_Heard_On_FDS","DefaultCrouchMoveSoundID=3446:int,DefaultWalkMoveSoundID=3446:int,DefaultRunMoveSoundID=3569:int,UpdateRate=0.1:float");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Team_Dominates_Zone> JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Registrant("JMG_Utility_Send_Custom_When_Team_Dominates_Zone","Team:int,ID:int,CaptureCustom:int,CaptureParam:int,LostCustom:int,LostParam:int,CaptureDelay:float,Rate=0.1:float,StartsEnabled=1:int,EnableCustom=0:int,SendCustomEveryTick=0:int");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Team_Zone> JMG_Utility_Send_Custom_When_Team_Zone_Registrant("JMG_Utility_Send_Custom_When_Team_Zone","Team:int,ID:int,InCustom:int,InParam:int,OutCustom:int,OutParam:int,Delay:float,Rate=0.1:float,StartsEnabled=1:int,EnableCustom=0:int,SendCustomEveryTick=0:int");
+ScriptRegistrant<JMG_Utility_HUD_Count_Down_Visible_Countdown> JMG_Utility_HUD_Count_Down_Visible_Countdown_Registrant("JMG_Utility_HUD_Count_Down_Visible_Countdown","CharPos:int,BaseModelName:string");
+ScriptRegistrant<JMG_Utility_Custom_Send_Custom_On_Secondary_Count> JMG_Utility_Custom_Send_Custom_On_Secondary_Count_Registrant("JMG_Utility_Custom_Send_Custom_On_Secondary_Count","TriggerCustom:int,CountCustom:int,MatchCount:int,ID:int,SendCustom:int,Param:int,Delay:float,ResetCountCustom:int,OnceMatchedContinue:int");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Not_Team_Zone> JMG_Utility_Send_Custom_When_Not_Team_Zone_Registrant("JMG_Utility_Send_Custom_When_Not_Team_Zone","Team:int,ID:int,InCustom:int,InParam:int,OutCustom:int,OutParam:int,Delay:float,Rate=0.1:float,StartsEnabled=1:int,EnableCustom=0:int,SendCustomEveryTick=0:int");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore> JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore_Registrant("JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore","");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Team_Zone_Ignore> JMG_Utility_Send_Custom_When_Team_Zone_Ignore_Registrant("JMG_Utility_Send_Custom_When_Team_Zone_Ignore","");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore> JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore_Registrant("JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore","");
+ScriptRegistrant<JMG_Utility_Send_Custom_On_Armor> JMG_Utility_Send_Custom_On_Armor_Registrant("JMG_Utility_Send_Custom_On_Armor","ID:int,ArmorCustom:int,ArmorParam:int,NoArmorCustom:int,NoArmorParam:int,Delay:float");
+ScriptRegistrant<JMG_Utility_Base_Defense_Simple> JMG_Utility_Base_Defense_Simple_Registrant("JMG_Utility_Base_Defense_Simple","MinAttackDistance:float,MaxAttackDistance:float,AttackPriority[Car|Tank|Bike]=1.0 1.0 1.0:vector3,AttackPriority[Flying|Turret|Boat]=1.0 1.0 1.0:vector3,AttackPriority[Submarine|Infantry|Unused]=1.0 1.0 0.0:vector3,ResetTime=3:int");
+ScriptRegistrant<JMG_Utility_Custom_Send_Custom_Supress_Spam> JMG_Utility_Custom_Send_Custom_Supress_Spam_Registrant("JMG_Utility_Custom_Send_Custom_Supress_Spam","Custom0:int,Custom1:int,Custom2:int,Custom3:int,Custom4:int,Custom5:int,Custom6:int,Custom7:int,Custom8:int,Custom9:int,ID:int,SpamDelay:float,SendDuplicates=0:int");
+ScriptRegistrant<JMG_Utility_Send_Custom_When_Player_Between_Range> JMG_Utility_Send_Custom_When_Player_Between_Range_Registrant("JMG_Utility_Send_Custom_When_Player_Between_Range","MinDistance:float,MaxDistance:float,ID:int,Custom:int,Param:int,Delay:float,TriggerOnce=1:int,SleepTime=10:int");
+ScriptRegistrant<JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player> JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player_Registrant("JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player","Custom=0:int,FileName=null:string,TextTag=[]:string");
+ScriptRegistrant<JMG_Utility_Send_Custom_On_Powerup_Pickup_Collector> JMG_Utility_Send_Custom_On_Powerup_Pickup_Collector_Registrant("JMG_Utility_Send_Custom_On_Powerup_Pickup_Collector","ID:int,Message:int,Param=0:int,Delay=0:float");
+ScriptRegistrant<JMG_Utility_Zone_Send_Custom_Enter_From_Enterer> JMG_Utility_Zone_Send_Custom_Enter_From_Enterer_Registrant("JMG_Utility_Zone_Send_Custom_Enter_From_Enterer","ID=0:int,Custom=0:int,Param=0:int,Delay=0.0:float,PlayerType=2:int,TriggerOnce=1:int");
+ScriptRegistrant<JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom> JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom_Registrant("JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom","TargetCustom:int,MinDistance=0.0:float,MinAngle=-90.0:float,MaxAngle=90.0:float,UseLowAngleMaxDistance=0.0:float,UseLowAngleWhenAboveMinDistance=1:int,UseLowAngleTargetAboveHeight=9999.9:float,VehicleProjectilePreset:string,FireVelocity=1.0:float,GravityScale=1.0:float,MissAmountPerMeter=0.0:float,BaseMissAmount=0.0:float,ProjectedShotsChance=1.0:float,FireRate=1.0:float,ReloadTime=1.0:float,ReloadSound=null:string,ClipCount=1:int,FireSound=null:string,MuzzleFlashExplosion=null:string,ProjectileExplosion=null:string,CustomTimeoutTime=3:int,AimTurret=1:int");
+ScriptRegistrant<JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer> JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer_Registrant("JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer","PlayerType=2:int,ID:int,Custom:int,Param:int,Delay:float,TriggerOnce:int");
+ScriptRegistrant<JMG_Utility_Custom_Send_Custom_On_Count_From_Sender> JMG_Utility_Custom_Send_Custom_On_Count_From_Sender_Registrant("JMG_Utility_Custom_Send_Custom_On_Count_From_Sender","Custom:int,Count:int,ID=0:int,SendCustom:int,Param:int,Delay:float,ResetCustom:int");
diff -ur sourceold/scripts/jmgUtility.h source/scripts/jmgUtility.h
--- sourceold/scripts/jmgUtility.h	2019-02-15 09:37:36.770000000 +1000
+++ source/scripts/jmgUtility.h	2019-06-22 07:43:44.247400000 +1000
@@ -172,6 +172,7 @@
   void Custom(GameObject *obj,int message,int param,GameObject *sender);
   void Killed(GameObject *obj,GameObject *killer);
   void Destroyed(GameObject *obj);
+  void Detach(GameObject *obj);
 };
 
 /*!
@@ -577,7 +578,7 @@
 	}
 	static char *Rp2Encrypt(const char *String,int EncryptionFactor,int Start)
 	{
-		static char FinalString[2000];
+		static char FinalString[65536];
 		sprintf(FinalString," ");
 		int loops = strlen(String),x = 0;
 		int CurrentAdd = Start;
@@ -602,7 +603,57 @@
 	}
 	static char *Rp2Encrypt2(const char *String,int EncryptionFactor,int Start)
 	{
-		static char FinalString[2000];
+		static char FinalString[65536];
+		sprintf(FinalString," ");
+		int loops = strlen(String),x = 0;
+		int CurrentAdd = Start;
+		while (x < loops)
+		{
+			if (String[x] > 31 && String[x] < 127)
+			{
+				int Temp = (String[x]+CurrentAdd);
+				while (Temp > 126)
+					Temp -= 95;
+				FinalString[x] = (char)Temp;
+			}
+			else
+				FinalString[x] = String[x];
+			x++;
+			CurrentAdd++;
+			if (CurrentAdd > EncryptionFactor)
+				CurrentAdd = 1;
+			FinalString[x] = '\0';
+		}
+		return FinalString;
+	}
+	static char *Rp2Encrypt3(const char *String,int EncryptionFactor,int Start)
+	{
+		static char FinalString[65536];
+		sprintf(FinalString," ");
+		int loops = strlen(String),x = 0;
+		int CurrentAdd = Start;
+		while (x < loops)
+		{
+			if (String[x] > 31 && String[x] < 127)
+			{
+				int Temp = (String[x]+CurrentAdd);
+				while (Temp > 126)
+					Temp -= 95;
+				FinalString[x] = (char)Temp;
+			}
+			else
+				FinalString[x] = String[x];
+			x++;
+			CurrentAdd++;
+			if (CurrentAdd > EncryptionFactor)
+				CurrentAdd = 1;
+			FinalString[x] = '\0';
+		}
+		return FinalString;
+	}
+	static char *Rp2Encrypt4(const char *String,int EncryptionFactor,int Start)
+	{
+		static char FinalString[65536];
 		sprintf(FinalString," ");
 		int loops = strlen(String),x = 0;
 		int CurrentAdd = Start;
@@ -627,7 +678,7 @@
 	}
 	static char *Rp2Decrypt(const char *String,int EncryptionFactor,int Start)
 	{
-		static char FinalString[2000];
+		static char FinalString[65536];
 		int loops = strlen(String),x = 0;
 		int CurrentAdd = Start;
 		while (x < loops)
@@ -1009,6 +1060,19 @@
 			current = current->next;
 		}
 	}
+	void Remove_Radar_Blip(int objectiveId)
+	{
+		ObjectiveNode *current = objectiveNodeList;
+		while (current)
+		{
+			if (current->id == objectiveId)
+			{
+				Destroy_Radar_Marker(Commands->Find_Object(current->radarMarkerId));
+				return;
+			}
+			current = current->next;
+		}
+	}
 	void Set_Radar_Blip(int objectiveId,Vector3 blipPosition,const char *modelOverride = NULL)
 	{
 		ObjectiveNode *current = objectiveNodeList;
@@ -1401,6 +1465,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	JMG_Utility_Sync_System_Controller();
 };
@@ -1426,6 +1491,7 @@
 * \EnableOrDisableCustom - Message used to enable the spawner, the parameter sent with it tell whether to enable or disable (1 for enable, 0 for disable, -1 disables and kills the spawned object, -2 disables and destroys the spawned object)
 * \SpawnLimit - How many objects can this script spawn
 * \DelayOnStartup - Day to spawn the first object when enabled
+* \Attach_Scripts_Group_ID - Defines what scripts should be attached with JMG_Utility_Basic_Spawner_Attach_Script (-1 attachs all scripts on the spawn controller that can be attached)
 * \author jgray
 * \ingroup JmgUtility
 */
@@ -1435,6 +1501,7 @@
 	int spawnedId;
 	bool enabled;
 	float respawnTime;
+	int attachScriptsGroupId;
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Custom(GameObject *obj,int message,int param,GameObject *sender);
@@ -1723,6 +1790,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 	static char musicFileName[256];
 public:
 	static bool controllerPlaced;
@@ -2148,17 +2216,21 @@
 * \brief Makes an aircraft move between wander points, it'll strafe at the target while it has one
 * \WanderingAIGroupID - Group of points to wander between
 * \FlightHeight - How high it should stay above the points
-* \FireRange - Max range that it can use it's guns
+* \FireRange - Max range that it can use it's guns, if less than 0 it uses the current weapon held's max range
 * \StealthModeOverride - 0 = normal stealth detection, 1 = sees everything, -1 = can't see any stealthed enemies
+* \NewPointDelay - Useful for aircraft with slow acceleration so that it doesn't keep trying to grab new points
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_AI_Guardian_Aircraft : public ScriptImpClass {
+	float fireRange;
 	Vector3 dpPosition;
 	int EnemyID;
 	int EnemyTimeOutTime;
 	Vector3 LastPos;
 	int stealthModeOverride;
+	int newPointDelay;
+	int remainingDelay;
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Enemy_Seen(GameObject *obj,GameObject *seen);
@@ -2628,11 +2700,14 @@
 * \Script - Name of the script to attach
 * \Params - The parameters to use for the script
 * \Delim - The character to use in place of a ',' swapped at script creation
+* \Scripts_Group_ID - Defines what attach group this is in, if -1 it will attach to all the spawned objects spawned from this object.
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_Basic_Spawner_Attach_Script : public ScriptImpClass {
+	int scriptsGroupId;
 	char *params;
+	char script[128];
 	void Created(GameObject *obj);
 	void Custom(GameObject *obj,int message,int param,GameObject *sender);
 };
@@ -2798,6 +2873,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static bool controllerPlaced;
 };
@@ -3000,13 +3076,14 @@
 * \brief Makes a unit move between wander points, it'll strafe at the target while it has one, clone of JMG_Utility_AI_Guardian_Aircraft but with pathfind enabled
 * \WanderingAIGroupID - Group of points to wander between
 * \WanderSpeed - Speed the unit moves at 
-* \FireRange - Max range that it can use it's guns
+* \FireRange - Max range that it can use it's guns, if less than 0 it uses the current weapon held's max range
 * \CheckBlocked - Whether to check if the target spot is blocked before firing
 * \StealthModeOverride - 0 = normal stealth detection, 1 = sees everything, -1 = can't see any stealthed enemies
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_AI_Guardian_Infantry : public ScriptImpClass {
+	float fireRange;
 	Vector3 dpPosition;
 	int EnemyID;
 	int EnemyTimeOutTime;
@@ -3340,6 +3417,7 @@
 * \ChangeGroupIDCustom - Changes the wander point group id to a new one, uses the parameter on the custom sent in
 * \PlayerType - Playertype the zone triggers for
 * \RetryOnFailure - If this is true a script will be attached that will continue to try to teleport the player until successful (Warning: Turning this on hides error messages)
+* \AiOnly - Only trigger for AI
 * \author jgray
 * \ingroup JmgUtility
 */
@@ -3349,6 +3427,7 @@
 	float safeTeleportDistance;
 	int wanderPointGroup;
 	int changeGroupIDCustom;
+	bool aiOnly;
 	void Created(GameObject *obj);
 	void Entered(GameObject *obj,GameObject *enterer);
 	void Custom(GameObject *obj,int message,int param,GameObject *sender);
@@ -4086,7 +4165,7 @@
 * \brief Makes a unit move between wander points, it'll strafe at the target while it has one, clone of JMG_Utility_AI_Guardian_Aircraft but with pathfind enabled and allows you to turn off the engine on arrival, also well throw itself into reverse if it gets stuck
 * \WanderingAIGroupID - Group of points to wander between
 * \WanderSpeed - Speed the unit moves at 
-* \FireRange - Max range that it can use it's guns
+* \FireRange - Max range that it can use it's guns, if less than 0 it uses the current weapon held's max range
 * \CheckBlocked - Whether to check if the target spot is blocked before firing
 * \AimAtFeet - Should the vehicle aim at the feet of infantry
 * \TurnOffEngineOnArrival - Should the vehicle turn off its engine when it gets to its points (useful for wheeled vehicles that roll away)
@@ -4095,6 +4174,7 @@
 * \ingroup JmgUtility
 */
 class JMG_Utility_AI_Guardian_Vehicle : public ScriptImpClass {
+	float fireRange;
 	bool aimAtFeet;
 	Vector3 dpPosition;
 	int EnemyID;
@@ -4147,11 +4227,15 @@
 * \ReminderCustom - custom to send every time a reminder message shows up, param is the remaining lives, no delay
 * \AddDeathsWhenNoPlayers - Increases the death count (by one) at the rate specified here when no players are in game, if 0 no increase
 * \NoPlayersAddDeathSaftyTime - This amount of time must pass before deaths will start to be added, if a player rejoins the time will reset
+* \RequiresADeathToStartNoPlayerAdd - If true a player must have died once before the death countdown can start
+* \AnnounceOnFirstDeath - Shows the death message the first time any player dies so they are aware of it
+* \OnlyTrackPlayerDeaths - Only track player deaths, not AI
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_Send_Custom_On_Deaths_Controller : public ScriptImpClass
 {
+	bool requiresADeathToStartNoPlayerAdd;
 	float addDeathsWhenNoPlayers;
 	int noPlayersAddDeathSaftyTime;
 	int currentNoPlayersAddDeathSaftyTime;
@@ -4159,6 +4243,7 @@
 	void Timer_Expired(GameObject *obj,int number);
 	void Custom(GameObject *obj,int message,int param,GameObject *sender);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 	static char *formatReminderString(const char *format,...);
 public:
 	static void ReportLogic(GameObject *obj);
@@ -4176,6 +4261,8 @@
 	static char remainingPlural[220];
 	static int reminderMessageOrder;
 	static bool controllerPlaced;
+	static bool announceOnFirstDeath;
+	static bool onlyTrackPlayerDeaths;
 };
 
 /*!
@@ -4288,6 +4375,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 	static Vector3 controllerDefaultColor;
 	static float controllerDefaultOpacity;
 public:
@@ -4440,11 +4528,63 @@
 * \MinutePlural - This is the part of the message that displays minutes plural
 * \SecondSingular - This is the part of the message that displays second singular
 * \SecondPlural - This is the part of the message that displays seconds plural
+* \StartsPaused - If 0 the countdown will start paused
+* \PauseCustom - If recieved this pauses the countdown when the param is non-zero, unpauses when 0
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_HUD_Count_Down : public ScriptImpClass {
 public:
+	struct CountdownScreenCharacterNode
+	{
+		int id;
+		int charPos;
+		char currentChar;
+		char baseModelName[15];
+		struct CountdownScreenCharacterNode *next;
+		CountdownScreenCharacterNode(int id,int charPos,const char *baseModelName)
+		{
+			this->id = id;
+			this->charPos = charPos;
+			this->currentChar = '\0';
+			sprintf(this->baseModelName,"%s",baseModelName);
+			this->next = NULL;
+		}
+	};
+	static int highestCharPos;
+	static CountdownScreenCharacterNode *countdownScreenCharacterController;
+	static void AddVisualCountdownNode(int id,int charPos,const char *baseModelName)
+	{
+		if (charPos > 5)
+		{
+			Console_Input("msg AddVisualCountdownNode::ERROR charPos can be at most 5!");
+			return;
+		}
+		if (strlen(baseModelName) > 15)
+		{
+			Console_Input("msg AddVisualCountdownNode::ERROR baseModelName can be at most 15 characters (remember a number is added to the end)!");
+			return;
+		}
+		if (highestCharPos < charPos)
+			highestCharPos = charPos;
+		CountdownScreenCharacterNode *current = countdownScreenCharacterController;
+		if (!countdownScreenCharacterController)
+			countdownScreenCharacterController = new CountdownScreenCharacterNode(id,charPos,baseModelName);
+		while (current)
+		{
+			if (current->id == id)
+			{
+				Console_Input("msg AddVisualCountdownNode::ERROR: id has already been added once!");
+				return;
+			}
+			if (!current->next)
+			{
+				current->next = new CountdownScreenCharacterNode(id,charPos,baseModelName);
+				return;
+			}
+			current = current->next;
+		}
+	}
 	struct SendCustomOnSecondNode
 	{
 		bool hasTriggered;
@@ -4498,9 +4638,13 @@
 	char secondSingular[220];
 	char secondPlural[220];
 	int seconds;
+	int pauseCustom;
+	bool paused;
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 	char *formatReminderString(const char *format,...);
 	
 	void NodeSendCustom(GameObject *obj,int second)
@@ -4520,6 +4664,7 @@
 	}
 	void CleanupSecondNodes()
 	{
+		controllerPlaced = false;
 		SendCustomOnSecondNode *temp = sendCustomOnSecondController,*die;
 		while (temp)
 		{
@@ -4528,6 +4673,38 @@
 			delete die;
 		}
 		sendCustomOnSecondController = NULL;
+		CountdownScreenCharacterNode *temp2 = countdownScreenCharacterController,*die2;
+		while (temp2)
+		{
+			GameObject *kill = Commands->Find_Object(temp2->id);
+			if (kill)
+				Commands->Destroy_Object(kill);
+			die2 = temp2;
+			temp2 = temp2->next;
+			delete die2;
+		}
+		countdownScreenCharacterController = NULL;
+		highestCharPos = -1;
+	}
+	void UpdateCountdownHudNodes()
+	{
+		if (highestCharPos <= 0)
+			return;
+		char hhmmss[6];
+		sprintf(hhmmss,"%02d%02d%02d",seconds/3600,(seconds%3600)/60,(seconds%3600)%60);
+		for (int x = 0;x <= highestCharPos;x++)
+			for (CountdownScreenCharacterNode *current = countdownScreenCharacterController;current;current = current->next)
+			{
+				if (current->charPos == x && current->currentChar != hhmmss[5-x])
+				{
+					GameObject *object = Commands->Find_Object(current->id);
+					if (!object)
+						continue;
+					char model[16];
+					sprintf(model,"%s%c",current->baseModelName,hhmmss[5-x]);
+					Commands->Set_Model(object,model);
+				}
+			}
 	}
 };
 /*!
@@ -4669,6 +4846,7 @@
 class JMG_Utility_AI_Follow_Player_On_Poke_Controller : public ScriptImpClass {
 	void Created(GameObject *obj);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static bool controllerPlaced;
 	static int maxFollowers;
@@ -5207,7 +5385,7 @@
 * \brief Makes a unit move between wander points, it'll strafe at the target while it has one, clone of JMG_Utility_AI_Guardian_Aircraft but with pathfind enabled
 * \WanderingAIGroupID - Group of points to wander between
 * \WanderSpeed - Speed the unit moves at 
-* \FireRange - Max range that it can use it's guns
+* \FireRange - Max range that it can use it's guns, if less than 0 it uses the current weapon held's max range
 * \CheckBlocked - Whether to check if the target spot is blocked before firing
 * \ArriveDistance - Distance to get close to
 * \FlightHeight - How high should the aircraft fly (use 0 if not an aircraft)
@@ -5218,6 +5396,7 @@
 * \ingroup JmgUtility
 */
 class JMG_Utility_AI_Guardian_Generic : public ScriptImpClass {
+	float fireRange;
 	float flightHeight;
 	float arriveDistance;
 	float arriveDistanceSq;
@@ -5488,7 +5667,7 @@
 };
 
 /*!
-* \brief Sends a custom when an object is destroyed
+* \brief Sends a custom when the attached powerup is collected
 * \ID - Id of the object to send the custom to, 0 sends to itself, -1 sends it to the person who picked it up
 * \Message - Custom to send
 * \Param - parameter to send with the custom
@@ -5671,10 +5850,12 @@
 * \DamageAmount - How much damage to apply
 * \Warhead - What warhead to use
 * \DamagerId - ID of what is applying the damage, 0 = nothing, -1 = enter, -2 = zone
+* \Team - 2 everyone, 0 Nod, 1 GDI
 * \author jgray
 * \ingroup JmgUtility
 */
 class JMG_Utility_Apply_Damage_While_In_Zone : public ScriptImpClass {
+	int team;
 	char params[512];
 	void Created(GameObject *obj);
 	void Entered(GameObject *obj,GameObject *enterer);
@@ -5861,6 +6042,7 @@
 class JMG_Utility_Spawn_With_Last_Selected_Gun_Control : public ScriptImpClass {
 	void Created(GameObject *obj);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static bool controllerPlaced;
 	static char playerWeapons[128][256];
@@ -6037,6 +6219,7 @@
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static bool controllerPlaced;
 	static bool isAFK[128];
@@ -6115,6 +6298,9 @@
 * \ if the value is above 0 the objects will only be checked against other objects with the same value.
 * \Starts_Disabled - If set to 1 the script won't be able to spawn objects until an Enable_Disable_Custom with a param of 1 is received
 * \Enable_Disable_Custom - Custom used to enable or disable the spawners, if the param is 0 it will be disabled, 1 will attempt to spawn the max, anything else just enables the spawn code
+* \Raycast_Direction_Override[Bottom|Top|Unused] - Specify the Z range in which the ray is cast (Default 0|0 lets the game script decide)
+* \Attach_Scripts_Group_ID - Defines what scripts should be attached with JMG_Utility_Basic_Spawner_Attach_Script (-1 attachs all scripts on the spawn controller that can be attached)
+* \PlayersAddToSpawnAtATime - Each player in game adds to the max number of things this can spawn at a time
 * \author jgray
 * \ingroup JmgUtility
 */
@@ -6161,6 +6347,9 @@
 	float faceDirection;
 	int enableDisableCustom;
 	int initialSpawn;
+	Vector3 raycastRange;
+	int attachScriptsGroupId;
+	float playersAddToSpawnAtATime;
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Custom(GameObject *obj,int message,int param,GameObject *sender);
@@ -6168,6 +6357,7 @@
 	bool CheckIfObjectIsNearAnotherObject(Vector3 pos);
 	void SetFacing(GameObject *obj,float facing);
 	void Initial_Spawn(GameObject *obj);
+	int GetPlayerLimitModifier();
 };
 
 /*!
@@ -6211,11 +6401,15 @@
 * \Enemy_Preset_ID - Preset ID required to be seen to remove the script, if 0 any enemy will trigger the script
 * \ID - Id of the object to send the custom to, 0 sends to itself, -1 sends to the seen object
 * \Visible_Message - Custom to send when there is a target that is visible
-* \Visible_Param - Param to send when there is a target that is visible
+* \Visible_Param - Param to send when there is a target that is visible, if -1 it's the id of the seen object
 * \Not_Visible_Message - Custom sent when no preset is visible anymore
-* \Not_Visible_Param - Param sent when no preset is visible anymore
-* \Max_Lost_Sight_Time - This is the max amount of time an enmey can go without being seen before being lost, 
-* \  game scans at least once a second so I reccomend at least 2.5 seconds if not longer
+* \Not_Visible_Param - Param sent when no preset is visible anymore, if -1 it's the id of the seen object
+* \Max_Lost_Sight_Time - This is the max amount of time an enemy can go without being seen before being lost
+* \  game scans at least once a second so I recommend at least 2.5 seconds if not longer
+* \RepeatSendSeenCustom - If set to true this custom will continue to be fired 10 times a second
+* \AttackPriority[Car|Tank|Bike] - Higher the value the more the object is prioritized, 0 can't be targeted
+* \AttackPriority[Flying|Turret|Boat] - Higher the value the more the object is prioritized, 0 can't be targeted
+* \AttackPriority[Submarine|Infantry|Unused - Higher the value the more the object is prioritized, 0 can't be targeted
 * \author jgray
 * \ingroup JmgUtility
 */
@@ -6229,9 +6423,15 @@
 	int visibleParam;
 	int notVisibleParam;
 	int maxNotSeenTime;
+	bool repeatSendSeenCustom;
+	Vector3 carTankBike;
+	Vector3 flyingTurretBoat;
+	Vector3 submarineInfantryUnused;
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
 	void Enemy_Seen(GameObject *obj,GameObject *seen);
+	float GetPriority(GameObject *seen);
+	void SendCustom(GameObject *obj,int custom,int param);
 };
 
 /*!
@@ -6277,6 +6477,7 @@
 */
 class JMG_Utility_Basic_Spawner_In_Radius_Controller: public ScriptImpClass {
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 };
 
 /*!
@@ -6716,6 +6917,7 @@
 			JMG_Utility_AI_Skittish_Herd_Animal::HerdAnimalPositionControl[x] = JMG_Utility_AI_Skittish_Herd_Animal::HerdAnimalPositionSystem();
 	}
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 };
 
 /*!
@@ -7018,6 +7220,7 @@
 	}
 private:
 	void Destroyed(GameObject *obj);
+	void Detach(GameObject *obj);
 public:
 	static bool exists;
 	JMG_Utility_Swimming_Infantry_Advanced_Controller()
@@ -7154,4 +7357,594 @@
 class JMG_Utility_Swimming_Infantry_Advanced_Add_Weapon : public ScriptImpClass {
 	void Created(GameObject *obj);
 	void Timer_Expired(GameObject *obj,int number);
+};
+
+/*!
+* \brief Sends a custom on a custom, the custom that is sent is cycled through the loop of customs provided (non-zero customs) once it hits the end of non-zero customs it returns to the start of the list
+* \Custom - Custom to watch for
+* \ID - ID to send to, 0 sends to self, -1 sends to sender
+* \SendCustom0 - first custom to send
+* \SendCustom1 - second custom to send
+* \SendCustom2 - third custom to send
+* \SendCustom3 - fourth custom to send
+* \SendCustom4 - fifth custom to send
+* \SendCustom5 - sixth custom to send
+* \SendCustom6 - seventh custom to send
+* \SendCustom7 - eight custom to send
+* \SendCustom8 - ninth custom to send
+* \SendCustom9 - tenth custom to send
+* \Param0 - first param to send (-1 sends the param that was received)
+* \Param1 - second param to send (-1 sends the param that was received)
+* \Param2 - third param to send (-1 sends the param that was received)
+* \Param3 - fourth param to send (-1 sends the param that was received)
+* \Param4 - fifth param to send (-1 sends the param that was received)
+* \Param5 - sixth param to send (-1 sends the param that was received)
+* \Param6 - seventh param to send (-1 sends the param that was received)
+* \Param7 - eighth param to send (-1 sends the param that was received)
+* \Param8 - ninth param to send (-1 sends the param that was received)
+* \Param9 - tenth param to send (-1 sends the param that was received)
+* \Delay0 - first delay to add
+* \Delay1 - second delay to add
+* \Delay2 - third delay to add
+* \Delay3 - forth delay to add
+* \Delay4 - fifth delay to add
+* \Delay5 - sixth delay to add
+* \Delay6 - seventh delay to add
+* \Delay7 - eighth delay to add
+* \Delay8 - ninth delay to add
+* \Delay9 - tenth delay to add
+* \RandomDelay - Max amount of random delay that can be added to the delay
+* \RandomChance - If non-zero this will be the chance that the custom can send 0.0-1.0, 1 will always send
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Custom_Send_Cycled_Customs : public ScriptImpClass {
+	int recieveMessage;
+	int id;
+	int custom[10];
+	int Param[10];
+	float delay[10];
+	float randomDelay;
+	float randomChance;
+	int cycle;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*!
+* \brief Sends the custom message from the object that killed the attached object
+* \ID - Id of the object to send the custom to, 0 sends to itself, -1 to killer
+* \Message - Custom to send
+* \Param - parameter to send with the custom
+* \Delay - Time amount to wait before sending the custom
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Killed_Send_Custom_From_Killer : public ScriptImpClass {
+	void Killed(GameObject *obj,GameObject *killer);
+};
+
+
+
+/*!
+* \brief This script makes for a rather cruddy emulation of sound_heard on a dedicated server. The script  
+* \ must be attached to a soldier which can emulate sounds and only notifies to scripts on other objects.
+* \DefaultCrouchMoveSoundID - ID of the footstep sound for crouched movement (I used a dirt sound)
+* \DefaultWalkMoveSoundID - ID of the footstep sound for walk movement (I used a dirt sound)
+* \DefaultRunMoveSoundID - ID of the footstep sound for run movement (I used a dirt sound)
+* \UpdateRate - How often to check if the attached soldier is moving or firing (I used 0.1 by default)
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Emulate_Sound_Heard_On_FDS : public ScriptImpClass {
+	class WeaponNode
+	{
+	public:
+		enum FireWeaponReturn{NONE,INFANTRY_FIRE,VEHICLE_FIRE};
+		bool infiniteBullets;
+		char weaponName[256];
+		int totalBullets;
+		int GetWeaponBullets(GameObject *obj)
+		{
+			return infiniteBullets ? Get_Current_Bullets(obj) : Get_Current_Total_Bullets(obj);
+		}
+		WeaponNode(GameObject *obj)
+		{
+			sprintf(weaponName,"%s",Get_Current_Weapon(obj));
+			infiniteBullets = Get_Current_Total_Bullets(obj) == -1 ? true : false;
+			totalBullets = GetWeaponBullets(obj);
+		}
+		WeaponNode()
+		{
+			sprintf(weaponName,"");
+			infiniteBullets = false;
+			totalBullets = 0;
+		}
+	};
+	float updateRate;
+	int crouchSoundId;
+	int walkSoundId;
+	int runSoundId;
+	WeaponNode currentWeapon;
+	WeaponNode vehicleWeapon;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+	WeaponNode::FireWeaponReturn FiredWeapon(GameObject *obj);
+};
+
+/*! 
+* \brief Sends a custom when the specified team holds the zone with more objects than all the other teams combind (only checks smart game objects and neutral is ignored).
+* \Team - Team to check for
+* \ID - ID to send the custom
+* \CaptureCustom - Custom to send once the team controls the zone
+* \CaptureParam - Param to send once the team controls the zone (if -1 sends how many members of the team are in the zone)
+* \LostCustom - Custom to send once the team lost control of the zone
+* \LostParam - Param to send once the team lost control of the zone (if -1 sends how many members of the team are in the zone)
+* \CaptureDelay - Delay to add to the custom once the team controls the zone
+* \Rate - How often to check if the team now controls the zone
+* \StartsEnabled - Should the script start running
+* \EnableCustom - Custom to enable/disable the script, param of non-zero enables
+* \SendCustomEveryTick - If non-zero the capture custom will send every tick the team controls the zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Team_Dominates_Zone : public ScriptImpClass {
+	enum sBool{sNULL=-1,sFALSE,sTRUE};
+	int team;
+	sBool teamHoldingZone;
+	bool enabled;
+	int enableCustom;
+	float rate;
+	int id;
+	int captureCustom;
+	int captureParam;
+	int lostCustom;
+	int lostParam;
+	float delay;
+	bool sendCustomEveryTick;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*! 
+* \brief Sends a custom when the specified team is in the zone (only checks smart game objects and neutral is ignored).
+* \Team - Team to check for
+* \ID - ID to send the custom to once the team controls the zone
+* \InCustom - Custom to send once the team is in the zone
+* \InParam - Param to send once the team is in the zone
+* \OutCustom - Custom to send once the team is out of the zone
+* \OutParam - Param to send once the team is out of the zone
+* \Delay - Delay to add to the custom once the team controls the zone
+* \Rate - How often to check if the team now controls the zone
+* \StartsEnabled - Should the script start running
+* \EnableCustom - Custom to enable/disable the script, param of non-zero enables
+* \SendCustomEveryTick - If non-zero the capture custom will send every tick the team controls the zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Team_Zone : public ScriptImpClass {
+	enum sBool{sNULL=-1,sFALSE,sTRUE};
+	int team;
+	sBool teamInZone;
+	bool enabled;
+	int enableCustom;
+	float rate;
+	int id;
+	int inCustom;
+	int inParam;
+	float delay;
+	int outCustom;
+	int outParam;
+	bool sendCustomEveryTick;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*!
+* \brief Ties a gameObject to JMG_Utility_HUD_Count_Down
+* \ Can be placed before JMG_Utility_HUD_Count_Down is placed.
+* \ If an object with JMG_Utility_HUD_Count_Down is destroyed all currently placed JMG_Utility_HUD_Count_Down_Visible_Countdown will be removed.
+* \CharPos - Character in the countdown string which this object represents SSMMHH 012345.
+* \BaseModelName - Model name that will be swapped as the counting happens, this model name will have a number appended to the end
+* \ IE: If the model name is number the new model name will be number#, where pound will be the current number to be displayed. 
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_HUD_Count_Down_Visible_Countdown : public ScriptImpClass {
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+};
+
+/*!
+* \brief Sends a custom when a custom is received while a secondary count of a secondary custom matches
+* \TriggerCustom - Custom to send on
+* \CountCustom - Custom to count
+* \MatchCount - how many count customs are needed for the trigger custom to send
+* \ID - ID to send to, 0 sends to self, -1 sends to sender
+* \SendCustom - custom to send when trigger custom is received and count is matched
+* \Param - param to send (-1 sends the param that was received)
+* \Delay - delay to add
+* \ResetCountCustom - custom to reset the count
+* \OnceMatchedContinue - If non-zero the custom can be sent after the number has been reached and won't stop on additional CountCustoms.
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Custom_Send_Custom_On_Secondary_Count : public ScriptImpClass {
+	int triggerCustom;
+	int countCustom;
+	int count;
+	int id;
+	int sendCustom;
+	int Param;
+	float delay;
+	int resetCustom;
+	bool onceMatchedContinue;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*! 
+* \brief Sends a custom when any team but the specified team is in the zone (only checks smart game objects and neutral is ignored).
+* \Team - Team to check for
+* \ID - ID to send the custom to once the team controls the zone
+* \InCustom - Custom to send once the team is in the zone
+* \InParam - Param to send once the team is in the zone
+* \OutCustom - Custom to send once the team is out of the zone
+* \OutParam - Param to send once the team is out of the zone
+* \Delay - Delay to add to the custom once the team controls the zone
+* \Rate - How often to check if the team now controls the zone
+* \StartsEnabled - Should the script start running
+* \EnableCustom - Custom to enable/disable the script, param of non-zero enables
+* \SendCustomEveryTick - If non-zero the capture custom will send every tick the team controls the zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Not_Team_Zone : public ScriptImpClass {
+	enum sBool{sNULL=-1,sFALSE,sTRUE};
+	int team;
+	sBool teamInZone;
+	bool enabled;
+	int enableCustom;
+	float rate;
+	int id;
+	int inCustom;
+	int inParam;
+	float delay;
+	int outCustom;
+	int outParam;
+	bool sendCustomEveryTick;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*! 
+* \brief The attached object will be ignored from checks by  JMG_Utility_Send_Custom_When_Team_Dominates_Zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Team_Dominates_Zone_Ignore : public ScriptImpClass {
+	void Created(GameObject *obj);
+};
+
+/*! 
+* \brief The attached object will be ignored from checks by  JMG_Utility_Send_Custom_When_Team_Zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Team_Zone_Ignore : public ScriptImpClass {
+	void Created(GameObject *obj);
+};
+
+/*! 
+* \brief The attached object will be ignored from checks by  JMG_Utility_Send_Custom_When_Not_Team_Zone
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Not_Team_Zone_Ignore : public ScriptImpClass {
+	void Created(GameObject *obj);
+};
+
+/*! 
+* \brief Sends a custom when the object's armor hits zero and then again when it hits full
+* \ID - ID to send the custom to once the team controls the zone
+* \ArmorCustom - Custom to send once the armor is full
+* \ArmorParam - Param to send once the armor is full
+* \NoArmorCustom - Custom to send once the armor is empty
+* \NoArmorParam - Param to send once the armor is empty
+* \Delay - Delay to add to the custom send
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_On_Armor : public ScriptImpClass {
+	bool hurt;
+	int id;
+	int armorCustom;
+	int armorParam;
+	int noArmorCustom;
+	int noArmorParam;
+	float delay;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+};
+
+/*!
+* \brief Very basic AI that won't reset its attack while still seeing a target, first come first serve
+* \MinAttackDistance - Min range at which it can attack
+* \MaxAttackDistance - Max range at which it can attack
+* \AttackPriority[Car|Tank|Bike] - Higher the value the more the object is prioritized, 0 can't be targeted
+* \AttackPriority[Flying|Turret|Boat] - Higher the value the more the object is prioritized, 0 can't be targeted
+* \AttackPriority[Submarine|Infantry|Unused - Higher the value the more the object is prioritized, 0 can't be targeted
+* \ResetTime - How many seconds should the AI keep its target after losing sight (I highly don't recommend anything less than 2 as the enemy seen function only fires once a second)
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Base_Defense_Simple : public ScriptImpClass {
+	int enemyId;
+	float maxDistance;
+	float maxDist;
+	float minDist;
+	int resetTime;
+	int userSetResetTime;
+	Vector3 carTankBike;
+	Vector3 flyingTurretBoat;
+	Vector3 submarineInfantryUnused;
+	void Created(GameObject *obj);
+	void Enemy_Seen(GameObject *obj,GameObject *seen);
+	void Timer_Expired(GameObject *obj,int number);
+	void Damaged(GameObject *obj,GameObject *damager,float damage);
+	float GetPriority(GameObject *seen);
+	void AttackTarget(GameObject *obj,int seenId,GameObject *seen);
+};
+
+/*!
+* \brief When one of the defined customs is caught it will be instantly sent, after sending a delay timer starts, any custom caught during this time won't be sent until the time runs out, 
+* \ when the time runs out only the newest is sent. Also attempts to send from the sender, if the sender no longer exists it sends from itself.
+* \Custom[0-9] - Custom to watch for, if custom 0 is 0 then all customs are caught
+* \ID - ID to send to, 0 sends to self, -1 sends to sender
+* \SpamDelay - Period in which after a custom is sent it must wait to send another
+* \SendDuplicates - If this is 0 the same custom can't be sent twice in a row
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Custom_Send_Custom_Supress_Spam : public ScriptImpClass {
+	int catchCustom[10];
+	int id;
+	int lastCustom;
+	int lastParam;
+	int lastSentCustom;
+	int lastSentParam;
+	float spamDelay;
+	bool supressingSpam;
+	bool sendDuplicates;
+	int senderId;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+	void Timer_Expired(GameObject *obj,int number);
+	bool SendCustom(GameObject *obj);
+};
+
+/*!
+* \brief Sends a custom message when a player is detected between the min and max range
+* \MinDistance - How close can the player be
+* \MaxDistance - How far can the player be
+* \ID - Id of the object to send the custom to, 0 sends to itself
+* \Message - Custom to send
+* \Param - parameter to send with the custom
+* \Delay - Time amount to wait before sending the custom
+* \TriggerOnce - If true the script can only fire once
+* \SleepTime - Value in 10ths of a second the script must wait before sending the custom again
+* \MaxRange - Players beyond this won't be detected, if 0 the param is ignored
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_When_Player_Between_Range : public ScriptImpClass {
+	int sleepTime;
+	int sleeping;
+	bool triggerOnce;
+	bool enabled;
+	float minDistance;
+	float maxDistance;
+	float delay;
+	int id;
+	int param;
+	int custom;
+	void Created(GameObject *obj);
+	void Timer_Expired(GameObject *obj,int number);
+};
+
+/*!
+* \brief Reads text from a file and displays it to the screen, as little popup dialogs
+* \ the text in a file is broken every 150 characters (tries to end the current word) and then delayed by 6 seconds
+* \Custom - Custom to trigger this script on, the sender is who will see it
+* \FileName - Name of the file to read from
+* \TextTag - Name of the tag to grab text from
+* \File Format Example
+[Tag]
+Text typed here will be displayed for this tag when the custom is received, the line can be up to 16384 characters long.
+[AnotherTag]
+Use this character ''(there is a character in there, you'll have to copy it out) to end the line, you can also use a bunch of them to add a delay to the message.
+[LastTag]
+The expected format of the file is always Tag Line Tag Line, never add blank lines or new line returns in tag text.
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Custom_Display_Briefing_PAMSG_Message_To_Player : public ScriptImpClass {
+	struct BriefingTextNode
+	{
+		char Text[256];
+		float Delay;
+		BriefingTextNode *prev;
+		BriefingTextNode *next;
+		BriefingTextNode(const char *text)
+		{
+			Delay = 0.0f;
+			sprintf(Text,"%s",text); 
+			next = NULL;
+			prev = NULL;
+		}
+		BriefingTextNode(const char *text,float delay)
+		{
+			Delay = delay;
+			sprintf(Text,"%s",text); 
+			next = NULL;
+			prev = NULL;
+		}
+		BriefingTextNode()
+		{
+			next = NULL;
+			prev = NULL;
+		}
+	};
+	BriefingTextNode *BriefingText;
+	BriefingTextNode *lastNode;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+	void Destroyed(GameObject *obj);
+	void AddNewTextNode();
+	void RemoveTextNodes();
+};
+
+/*!
+* \brief Sends a custom whne the attached powerup is collected from the collector
+* \ID - Id of the object to send the custom to, 0 sends to itself, -1 sends it to the person who picked it up
+* \Message - Custom to send
+* \Param - parameter to send with the custom
+* \Delay - Delay to add to the send
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Send_Custom_On_Powerup_Pickup_Collector : public ScriptImpClass {
+  void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*!
+* \brief Sends a custom to an object on entry from the enterer
+* \PlayerType - Player type the zone triggers for
+* \ID - ID to send the custom to, 0 sends to self, -1 sends to enter
+* \Custom - Custom message to send
+* \Param - Param to send
+* \Delay - Delay to add before sending custom
+* \TriggerOnce - Allows the script only to trigger the first time the zone is entered
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Zone_Send_Custom_Enter_From_Enterer : public ScriptImpClass {
+	int playerType;
+	int custom;
+	int param;
+	float delay;
+	int id;
+	bool triggerOnce;
+	void Created(GameObject *obj);
+	void Entered(GameObject *obj,GameObject *enterer);
+};
+
+/*!
+* \brief Uses maths to "launch" a vehicle with projectile phyiscs at an enemy's location provided by a spotter object, all effects come from the muzzleA0 bone
+* \TargetCustom - Custom provided by the 'spotter' object, param is the enemy's id, (recommend using JMG_Utility_Enemy_Seen_Send_Custom)
+* \MinDistance - Min distance at which a target can be before it won't be shot at
+* \MinAngle - Max angle the turret can aim (NOTE: Turret isn't actually used for this varrient of the script as it isn't using an actual weapon, turret will still try to aim at the location however).
+* \MaxAngle - Min angle the turret can aim 
+* \UseLowAngleMaxDistance - Max distance the turret will try to aim at the low angle before switching the the high arc angle
+* \UseLowAngleTargetAboveHeight - Try to fire the lower angle when above you, this is useful for aircraft and soforth
+* \VehicleProjectilePreset - Vehicle preset that has projectile physics (Created at the FireSpot bone) NOTE: Make sure it is type "flying" so it syncs on the network right
+* \FireVelocity - Speed at which to launch the vehicle preset
+* \GravityScale - Gravity scale setting on the projectile vehicle preset (used for calculations)
+* \BaseMissAmount - How much can the script miss around the target by
+* \MissAmountPerMeter - Max amount of distance the script can aim around the object per meter of distance between the target and firer. 
+* \  IE if 0.01 and the target is 150 meters away then the script can aim 1.5 meters infront of, to the left, etc of the target (Defaults to BaseMissAmount if its greater)
+* \ProjectedShotsChance - Chance that the shots will be projected, toggles between the modes every reload (0.0-1.0)
+* \FireRate - howmany in a second to fire
+* \ReloadTime - How long to wait once the clip is empty before firing again
+* \ReloadSound - Sound to play when reloading (Comes from the turret bone)
+* \ClipCount - How many rounds in a clip, if 0 it will never reload
+* \FireSound - Sound to play when the script shoots (Comes from the MuzzleA0 bone)
+* \MuzzleFlashExplosion - Explosion to create when the script shoots (Comes from the MuzzleA0 bone)
+* \ProjectileExplosion - Explosion to create where the vehicle projectile lands
+* \CustomTimeoutTime - If non-zero, this is how long the script will wait before forgetting a target if a custom with the seen id isn't sent again
+* \AimTurret - Being the turret isn't really used at all the AI doens't actually need to aim it, if you want you can make the script aim the turret as if it's actually taking shots (Note: This could inturrupt other AI commands).
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_AI_Lobbed_Vehicle_Projectile_Custom : public ScriptImpClass {
+	int targetCustom;
+	float minDistanceSquared;
+	float lowAngleMaxDistance;
+	bool useLowAngleWhenAboveMinDistance;
+	float useLowAngleTargetAboveHeight;
+	float reloadTime;
+	float fireDelay;
+	float fireVelocity;
+	float velocitySquared;
+	char fireSound[128];
+	char vehicleProjectilePreset[128];
+	char muzzleFlashExplosion[128];
+	char projectileExplosion[128];
+	char reloadSound[128];
+	float minAngle;
+	float maxAngle;
+	float gravityScale;
+	int clipCount;
+	int currentClipCount;
+	float missAmountPerMeter;
+	float baseMissAmount;
+	bool delayComplete;
+	bool reloadComplete;
+	int targetId;
+	int aimTurret;
+	int timeoutTime;
+	int refreshTime;
+	double lastAngle;
+	float projectedShotsChance;
+	bool projectShots;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+	void Timer_Expired(GameObject *obj,int number);
+	bool CalculateAngle(double *returnAngle,double distance,double height,bool highAngle);
+	void FireProjectile(GameObject *obj,double zAngle,double aimAngle);
+};
+
+/*!
+* \brief Sends a custom to an object on entry from the enterer
+* \PlayerType - Player type the zone triggers for
+* \ID - ID to send the custom to, 0 sends to self, -1 sends to enter
+* \Custom - Custom message to send
+* \Param - Param to send
+* \Delay - Delay to add before sending custom
+* \TriggerOnce - Allows the script only to trigger the first time the zone is entered
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Vehicle_Enter_Send_Custom_From_Enterer : public ScriptImpClass {
+	int id;
+	int custom;
+	int Param;
+	float delay;
+	bool triggerOnce;
+	int team;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
+};
+
+/*!
+* \brief Sends a custom when a custom has been received x times, message comes from sender
+* \Custom - Custom to count
+* \Count - how many customs needed
+* \ID - ID to send to, 0 sends to self, -1 sends to sender
+* \SendCustom - custom to send
+* \Param - param to send (-1 sends the param that was received)
+* \Delay - delay to add
+* \ResetCustom - custom to reset the count
+* \author jgray
+* \ingroup JmgUtility
+*/
+class JMG_Utility_Custom_Send_Custom_On_Count_From_Sender : public ScriptImpClass {
+	int custom;
+	int resetCustom;
+	int count;
+	void Created(GameObject *obj);
+	void Custom(GameObject *obj,int message,int param,GameObject *sender);
 };
\ No newline at end of file
