diff --git a/.gitignore b/.gitignore index c99836b..79817cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -bin/ -obj/ -.vs/ -*.csproj -*.config -*.sln +bin/ +obj/ +.vs/ +*.csproj +*.config +*.sln diff --git a/README.md b/README.md index f6ecddd..f2fd3a9 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The focus of my script is to provide a similar simple script to create instant b ## Usage -The key `B` is used to navigate through all the steps of the script. In-game help popups will describe what to do, but these are the different stages you will find: +The key `B` ("Hotkey") is used to navigate through all the steps of the script. In-game help popups will describe what to do, but these are the different stages you will find: 1. The script will ask you to move to where the enemies will spawn 2. After pressing the hotkey, you must do the same to define where the allies will spawn @@ -22,6 +22,8 @@ The key `B` is used to navigate through all the steps of the script. In-game hel 4. Press the hotkey once to enter the "exit mode" (it will ask for confirmation to stop the battle) 5. Pressing the hotkey again will inmediately stop the battle and remove all alive & dead peds from the map +An additional hotkey `N` ("SpawnHotkey") is used to pause/resume the ped spawning in both teams. The map blips ("E" and "A") will blink whenever the spawning is paused. + ## Settings Settings can be defined on the `SimpleGangWar.ini` file, being the following: @@ -30,7 +32,7 @@ Settings can be defined on the `SimpleGangWar.ini` file, being the following: _All lists of items (models & weapons) are separated by comma (`,`) or semi-colon (`;`). Spaces and case ignored._ -- `Models`: list of ped models ([Reference](https://github.com/crosire/scripthookvdotnet/blob/d1827497495567d810986aa752f8d903853088fd/source/scripting_v2/GTA.Native/PedHash.cs)) +- `Models`: list of ped models ([Reference](https://github.com/crosire/scripthookvdotnet/blob/d1827497495567d810986aa752f8d903853088fd/source/scripting_v2/GTA.Native/PedHash.cs) | [Reference with pics (use the names on the other link)](https://docs.fivem.net/docs/game-references/ped-models)) - `Weapons`: list of ped weapons ([Reference](https://github.com/crosire/scripthookvdotnet/blob/d1827497495567d810986aa752f8d903853088fd/source/scripting_v2/GTA.Native/WeaponHash.cs)) - `Health`: health for peds (should not be least than 100) - `Armor`: armor for peds (from 0) @@ -41,11 +43,16 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `offensive`: focus on attacking the enemy team - `suicidal`: more aggresive attack - _stationary & suicidal seem to take no effect, so is better to stick to just **defensive** and **offensive**_ +- `CombatRange`: how far or close the peds will fight against their enemies. This might not have a huge difference, depending on the scenario. One of following: + - `near` + - `medium` + - `far` - `MaxPeds`: maximum alive peds on the team (if not specified, the MaxPedsPerTeam setting will be used) ## SETTINGS -- `Hotkey`: the single hotkey used on the script ([Reference](https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.key?view=netcore-3.1#fields)) +- `Hotkey`: the single hotkey used to iterate over the script stages ([Reference](https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.key?view=netcore-3.1#fields)) +- `SpawnHotkey`: hotkey used to pause/resume ped spawn in both teams ([Reference](https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.key?view=netcore-3.1#fields)) - `MaxPedsPerTeam`: maximum alive peds on each team - teams with the setting MaxPeds will ignore this option - `NoWantedLevel`: if true, disable wanted level during the battle (true/false) - `ShowBlipsOnPeds`: if true, each spawned ped will have a blip on the map (true/false) @@ -54,20 +61,39 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `RunToSpawnpoint`: if true, the peds task will be to run to their enemies' spawnpoint; if false, will be to fight hated targets on the area (true/false). The task RunTo (true) seems to have lower negative effect on peds behaviour (avoid them from being idle stuck - but it can still happen if spawnpoints are too far away). The task FightAgainstHatedTargets (false) can be interesting when spawnpoints are closer, as peds might have more freedom to flank the enemy? +- `ProcessOtherRelationshipGroups`: if true, get all relationship groups from other existing peds and match these groups with the groups of SimpleGangWar peds. + Set it to true if you experience the spawned peds fighting against other peds (like mission peds) when they should not be (for example, enemy peds of a mission fighting against enemy peds of SimpleGangWar). +- `IdleInterval`: delay between loop runs, when battle is not running, in ms +- `BattleInterval`: delay between loop runs, when battle is running, in ms + +## Known bugs + +- If spawnpoints are too far away from each other, peds can idle and do nothing +- When using [Watch Your Death](https://gta5-mods.com/scripts/watch-your-death), while player is dead, enemies can run to ally spawnpoint without fighting, or be idle ## TODO -- Allow to pause & resume ped spawning - Avoid spawn-killing - Add winning conditions - Smooth transition from battle end to cleanup (extra step?) - Add menu/more hotkeys to improve UX? -- Respawn on ally spawnpoint after player dies +- Respawn player on ally spawnpoint after dying - Organize data, settings, variables - for each teams on the script structurally (struct?) ## Changelog -- 1.1.1 - Options to set ped limit per team +- 2.1.1 + - Add CombatRange setting + - Add ProcessOtherRelationshipGroups setting + - Add IdleInterval & BattleInterval settings to .ini file (they were defined on the script but not documented on the .ini file) + - Add docstrings to the script functions +- 2.0.1 + - Pause/resume ped spawning in both teams + - Fix usage of default hotkeys when not specified in .ini file + - Rearrange variables in script + - Refactor README +- 1.1.1 + - Options to set ped limit per team - 1.0.1 - Support settings through .ini file - Change if-else to switch in OnKeyUp @@ -78,9 +104,5 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - Option to select ped task behaviour - Option to select combat movement for each team - Different script Tick intervals for idle or in-battle -- 0.0.1 - Initial release - -## Known bugs - -- If spawnpoints are too far away from each other, peds can idle and do nothing -- When using [Watch Your Death](https://gta5-mods.com/scripts/watch-your-death), while player is dead, enemies can run to ally spawnpoint without fighting, or be idle +- 0.0.1 + - Initial release diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index 9d6a353..8a0198a 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -4,7 +4,8 @@ using System; using System.Windows.Forms; using System.Collections.Generic; - +using System.Linq; + public class SimpleGangWar : Script { // Settings defined on script variables serve as fallback for settings not defined (or invalid) on .ini config file @@ -14,7 +15,7 @@ public class SimpleGangWar : Script { private static string[] pedsAllies = { "Families01GFY", "Famca01GMY", "Famdnf01GMY", "Famfor01GMY" }; private static string[] weaponsAllies = { "AssaultRifle", "CompactRifle", "SNSPistol", "VintagePistol", "Pistol", "MicroSMG" }; private static string[] pedsEnemies = { "BallaEast01GMY", "BallaOrig01GMY", "Ballas01GFY", "BallaSout01GMY" }; - private static string[] weaponsEnemies = { "MicroSMG", "MachinePistol", "Gusenberg", "Musket", "Pistol", "VintagePistol", "CompactRifle" }; + private static string[] weaponsEnemies = { "MicroSMG", "MachinePistol", "Gusenberg", "Musket", "Pistol", "VintagePistol", "CompactRifle" }; private static readonly char[] StringSeparators = { ',', ';' }; private static int healthAllies = 120; @@ -25,14 +26,18 @@ public class SimpleGangWar : Script { private static int accuracyEnemies = 5; private static CombatMovement combatMovementAllies = CombatMovement.Offensive; private static CombatMovement combatMovementEnemies = CombatMovement.Offensive; + private static CombatRange combatRangeAllies = CombatRange.Far; + private static CombatRange combatRangeEnemies = CombatRange.Far; private static int maxPedsPerTeam = 10; private static Keys hotkey = Keys.B; + private static Keys spawnHotkey = Keys.N; private static bool noWantedLevel = true; private static bool showBlipsOnPeds = true; private static bool dropWeaponOnDead = false; private static bool removeDeadPeds = true; private static bool runToSpawnpoint = true; + private static bool processOtherRelationshipGroups = false; private static int idleInterval = 500; private static int battleInterval = 100; private static int maxPedsAllies; @@ -43,10 +48,28 @@ public class SimpleGangWar : Script { private int relationshipGroupAllies; private int relationshipGroupEnemies; private int originalWantedLevel; + private List spawnedAllies = new List(); private List spawnedEnemies = new List(); private List deadPeds = new List(); private List pedsRemove = new List(); + private List processedRelationshipGroups = new List(); + + private bool spawnEnabled = true; + private Stage stage = Stage.Initial; + + private Vector3 spawnpointAllies; + private Vector3 spawnpointEnemies; + private float spawnpointsDistance; + + private Blip spawnpointBlipAllies; + private Blip spawnpointBlipEnemies; + + private static Relationship[] allyRelationships = { Relationship.Companion, Relationship.Like, Relationship.Respect }; + private static Relationship[] enemyRelationships = { Relationship.Hate, Relationship.Dislike }; + + private int relationshipGroupPlayer; + private static Random random; private enum CombatMovement { // https://runtime.fivem.net/doc/natives/?_0x4D9CA1009AFBD057 @@ -54,6 +77,14 @@ private enum CombatMovement { Defensive = 1, Offensive = 2, Suicidal = 3 + // TODO setting to randomize movement for each ped? + } + + private enum CombatRange { + Near = 0, + Medium = 1, + Far = 2 + // TODO setting to randomize range for each ped } private enum Stage { @@ -64,23 +95,15 @@ private enum Stage { StopKeyPressed = 4 } - private class SettingsHeader { - public static readonly string Allies = "ALLIED_TEAM"; - public static readonly string Enemies = "ENEMY_TEAM"; - public static readonly string General = "SETTINGS"; + private class SettingsHeader { + public static readonly string Allies = "ALLIED_TEAM"; + public static readonly string Enemies = "ENEMY_TEAM"; + public static readonly string General = "SETTINGS"; } - private Stage stage = Stage.Initial; - private Vector3 spawnpointAllies; - private Vector3 spawnpointEnemies; - private Blip spawnpointBlipAllies; - private Blip spawnpointBlipEnemies; - private float spawnpointsDistance; - - private static Random random = new Random(); public SimpleGangWar() { - Tick += OnTick; + Tick += MainLoop; KeyUp += OnKeyUp; Interval = idleInterval; @@ -101,6 +124,11 @@ public SimpleGangWar() { configString = config.GetValue(SettingsHeader.Enemies, "CombatMovement", ""); combatMovementEnemies = EnumParse(configString, combatMovementEnemies); + configString = config.GetValue(SettingsHeader.Allies, "CombatRange", ""); + combatRangeAllies = EnumParse(configString, combatRangeAllies); + configString = config.GetValue(SettingsHeader.Enemies, "CombatRange", ""); + combatRangeEnemies = EnumParse(configString, combatRangeEnemies); + configString = config.GetValue(SettingsHeader.Allies, "Weapons", ""); weaponsAllies = ArrayParse(configString, weaponsAllies); configString = config.GetValue(SettingsHeader.Enemies, "Weapons", ""); @@ -113,6 +141,8 @@ public SimpleGangWar() { configString = config.GetValue(SettingsHeader.General, "Hotkey", ""); hotkey = EnumParse(configString, hotkey); + configString = config.GetValue(SettingsHeader.General, "SpawnHotkey", ""); + spawnHotkey = EnumParse(configString, spawnHotkey); maxPedsPerTeam = config.GetValue(SettingsHeader.General, "MaxPedsPerTeam", maxPedsPerTeam); noWantedLevel = config.GetValue(SettingsHeader.General, "NoWantedLevel", noWantedLevel); @@ -120,6 +150,7 @@ public SimpleGangWar() { dropWeaponOnDead = config.GetValue(SettingsHeader.General, "DropWeaponOnDead", dropWeaponOnDead); removeDeadPeds = config.GetValue(SettingsHeader.General, "RemoveDeadPeds", removeDeadPeds); runToSpawnpoint = config.GetValue(SettingsHeader.General, "RunToSpawnpoint", runToSpawnpoint); + processOtherRelationshipGroups = config.GetValue(SettingsHeader.General, "ProcessOtherRelationshipGroups", processOtherRelationshipGroups); idleInterval = config.GetValue(SettingsHeader.General, "IdleInterval", idleInterval); battleInterval = config.GetValue(SettingsHeader.General, "BattleInterval", battleInterval); @@ -128,30 +159,45 @@ public SimpleGangWar() { relationshipGroupAllies = World.AddRelationshipGroup("simplegangwar_allies"); relationshipGroupEnemies = World.AddRelationshipGroup("simplegangwar_enemies"); - int relationshipGroupPlayer = Game.Player.Character.RelationshipGroup; + relationshipGroupPlayer = Game.Player.Character.RelationshipGroup; SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroupAllies, relationshipGroupEnemies); SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupAllies, relationshipGroupAllies); SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupEnemies, relationshipGroupEnemies); SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupAllies, relationshipGroupPlayer); SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroupEnemies, relationshipGroupPlayer); + processedRelationshipGroups.Add(relationshipGroupPlayer); + processedRelationshipGroups.Add(relationshipGroupAllies); + processedRelationshipGroups.Add(relationshipGroupEnemies); + + random = new Random(); UI.Notify("SimpleGangWar loaded"); } - private void OnTick(object sender, EventArgs e) { + + /// + /// The main script loop runs at the frequency delimited by the Interval, which varies depending if the battle is running or not. + /// The loop only spawn peds and processes them as the battle is running. Any other actions that happen outside a battle are processed by Key event handlers. + /// + private void MainLoop(object sender, EventArgs e) { if (stage >= Stage.Running) { - try { - SpawnPeds(true); - SpawnPeds(false); - - ProcessSpawnedPeds(true); - ProcessSpawnedPeds(false); - } catch (FormatException exception) { - UI.ShowSubtitle("(SimpleGangWar) Error! " + exception.Message); + try { + SpawnPeds(true); + SpawnPeds(false); + + SetUnmanagedPedsInRelationshipGroups(); + ProcessSpawnedPeds(true); + ProcessSpawnedPeds(false); + } catch (FormatException exception) { + UI.ShowSubtitle("(SimpleGangWar) Error! " + exception.Message); } } } + + /// + /// Key event handler for key releases. + /// private void OnKeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == hotkey) { switch (stage) { @@ -180,9 +226,17 @@ private void OnKeyUp(object sender, KeyEventArgs e) { Teardown(); break; } + } else if (e.KeyCode == spawnHotkey) { + spawnEnabled = !spawnEnabled; + BlinkSpawnpoint(true); + BlinkSpawnpoint(false); } } + + /// + /// After the spawnpoints are defined, some tweaks are required just before the battle begins. + /// private void SetupBattle() { Interval = battleInterval; spawnpointsDistance = spawnpointEnemies.DistanceTo(spawnpointAllies); @@ -193,15 +247,24 @@ private void SetupBattle() { } } + /// + /// Spawn peds on the given team, until the ped limit for that team is reached. + /// + /// true=ally team / false=enemy team private void SpawnPeds(bool alliedTeam) { List spawnedPedsList = alliedTeam ? spawnedAllies : spawnedEnemies; int maxPeds = alliedTeam ? maxPedsAllies : maxPedsEnemies; - while (spawnedPedsList.Count < maxPeds) { + while (spawnEnabled && spawnedPedsList.Count < maxPeds) { SpawnRandomPed(alliedTeam); } } + /// + /// Spawns a ped on the given team, ready to fight. + /// + /// true=ally team / false=enemy team + /// The spawned ped private Ped SpawnRandomPed(bool alliedTeam) { Vector3 pedPosition = alliedTeam ? spawnpointAllies : spawnpointEnemies; string pedName = RandomChoice(alliedTeam ? pedsAllies : pedsEnemies); @@ -229,7 +292,7 @@ private Ped SpawnRandomPed(bool alliedTeam) { Function.Call(Hash.SET_PED_COMBAT_ATTRIBUTES, ped, 46, true); // force peds to fight Function.Call(Hash.SET_PED_SEEING_RANGE, ped, spawnpointsDistance); - //Function.Call(Hash.SET_PED_COMBAT_RANGE, ped, 0); // 0=near, 2=far + Function.Call(Hash.SET_PED_COMBAT_RANGE, ped, (int)(alliedTeam ? combatRangeAllies : combatRangeEnemies)); Function.Call(Hash.SET_PED_COMBAT_MOVEMENT, ped, (int)(alliedTeam ? combatMovementAllies : combatMovementEnemies)); if (showBlipsOnPeds) { @@ -246,6 +309,10 @@ private Ped SpawnRandomPed(bool alliedTeam) { return ped; } + /// + /// Processes the spawned peds of the given team. This includes making sure they fight and process their removal as they are killed in action. + /// + /// true=ally team / false=enemy team private void ProcessSpawnedPeds(bool alliedTeam) { List pedList = alliedTeam ? spawnedAllies : spawnedEnemies; @@ -255,7 +322,7 @@ private void ProcessSpawnedPeds(bool alliedTeam) { pedsRemove.Add(ped); deadPeds.Add(ped); if (removeDeadPeds) ped.MarkAsNoLongerNeeded(); - } else if (ped.IsIdle) { + } else if (ped.IsIdle && !ped.IsRunning) { if (runToSpawnpoint) ped.Task.RunTo(alliedTeam ? spawnpointEnemies : spawnpointAllies); else ped.Task.FightAgainstHatedTargets(spawnpointsDistance); } @@ -268,6 +335,10 @@ private void ProcessSpawnedPeds(bool alliedTeam) { pedsRemove.Clear(); } + /// + /// Set the spawnpoint for the given team on the position where the player is at. + /// + /// true=ally team / false=enemy team private void DefineSpawnpoint(bool alliedTeam) { Vector3 position = Game.Player.Character.Position; Blip blip = World.CreateBlip(position); @@ -284,15 +355,57 @@ private void DefineSpawnpoint(bool alliedTeam) { blip.Sprite = BlipSprite.TargetE; blip.Color = BlipColor.Orange; blip.Name = "Enemy spawnpoint"; + } + + BlinkSpawnpoint(alliedTeam); + } + + /// + /// Blink or stop blinking the spawnpoint blip of the given team, depending on if the spawn is disabled (blink) or not (stop blinking). + /// + /// true=ally team / false=enemy team + private void BlinkSpawnpoint(bool alliedTeam) { + Blip blip = alliedTeam ? spawnpointBlipAllies : spawnpointBlipEnemies; + if (blip != null) blip.IsFlashing = !spawnEnabled; + } + + /// + /// Get all the relationship groups from foreign peds (those that are not part of SimpleGangWar), and set the relationship between these groups and the SimpleGangWar groups. + /// + private void SetUnmanagedPedsInRelationshipGroups() { + if (processOtherRelationshipGroups) { + foreach (Ped ped in World.GetAllPeds()) { + if (ped.IsHuman && !ped.IsPlayer) { + Relationship pedRelationshipWithPlayer = ped.GetRelationshipWithPed(Game.Player.Character); + int relationshipGroup = ped.RelationshipGroup; + + if (relationshipGroup != relationshipGroupAllies && relationshipGroup != relationshipGroupEnemies && relationshipGroup != relationshipGroupPlayer) { + if (allyRelationships.Contains(pedRelationshipWithPlayer)) { + SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroup, relationshipGroupAllies); + SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroup, relationshipGroupEnemies); + } else if (enemyRelationships.Contains(pedRelationshipWithPlayer)) { + SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroup, relationshipGroupEnemies); + SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroup, relationshipGroupAllies); + } + } + } + } } } + /// + /// Physically delete the peds from the given list from the game world. + /// + /// List of peds to teardown private void TeardownPeds(List pedList) { foreach (Ped ped in pedList) { if (ped.Exists()) ped.Delete(); } } + /// + /// Manage the battle teardown on user requests. This brings the game to an initial state, before battle start and spawnpoint definition. + /// private void Teardown() { Interval = idleInterval; spawnpointBlipAllies.Remove(); @@ -304,29 +417,56 @@ private void Teardown() { spawnedAllies.Clear(); spawnedEnemies.Clear(); - deadPeds.Clear(); + deadPeds.Clear(); + pedsRemove.Clear(); + processedRelationshipGroups.Clear(); if (noWantedLevel) Game.MaxWantedLevel = originalWantedLevel; } + /// + /// Set the relationship between two given groups. The relationship is set twice, on both possible combinations. + /// + /// Relationship to set between the groups + /// One group + /// Other group private void SetRelationshipBetweenGroups(Relationship relationship, int groupA, int groupB) { World.SetRelationshipBetweenGroups(relationship, groupA, groupB); World.SetRelationshipBetweenGroups(relationship, groupB, groupA); } + /// + /// Choose a random item from a given array, containing objects of type T + /// + /// Type of objects in the array + /// Array to choose from + /// A random item from the array private T RandomChoice(T[] array) { return array[random.Next(0, array.Length)]; } - private EnumType EnumParse(string enumKey, EnumType defaultValue) where EnumType : struct { - EnumType returnValue; - if (!Enum.TryParse(enumKey, true, out returnValue)) returnValue = defaultValue; - return returnValue; + /// + /// Given a string key from an enum, return the referenced enum object. + /// + /// The whole enum object, to choose an option from + /// The enum key as string + /// What enum option to return if the referenced enum key does not exist in the enum + /// The chosen enum option + private EnumType EnumParse(string enumKey, EnumType defaultValue) where EnumType : struct { + EnumType returnValue; + if (!Enum.TryParse(enumKey, true, out returnValue)) returnValue = defaultValue; + return returnValue; } - private string[] ArrayParse(string stringInput, string[] defaultArray) { - string[] resultArray = stringInput.Replace(" ", string.Empty).Split(StringSeparators, StringSplitOptions.RemoveEmptyEntries); - if (resultArray.Length == 0) resultArray = defaultArray; - return resultArray; + /// + /// Given a string of words to be split, split them and return a string array. + /// + /// Input string + /// Array to return if the input string contains no items + /// A string array + private string[] ArrayParse(string stringInput, string[] defaultArray) { + string[] resultArray = stringInput.Replace(" ", string.Empty).Split(StringSeparators, StringSplitOptions.RemoveEmptyEntries); + if (resultArray.Length == 0) resultArray = defaultArray; + return resultArray; } } diff --git a/SimpleGangWar.ini b/SimpleGangWar.ini index 44c25ea..35da9a9 100644 --- a/SimpleGangWar.ini +++ b/SimpleGangWar.ini @@ -1,24 +1,33 @@ -[ALLIED_TEAM] -Models=Families01GFY, Famca01GMY, Famdnf01GMY, Famfor01GMY, Vagos01GFY +//Learn more about these settings in the mod repository: https://github.com/David-Lor/GTAV-SimpleGangWar#settings +[ENEMY_TEAM] +Models=Lost01GFY,Lost01GMY,Lost02GMY,Lost03GMY,BallaEast01GMY,BallaOrig01GMY,Ballas01GFY,BallaSout01GMY,Families01GFY,Famca01GMY,Famdnf01GMY,Famfor01GMY,Vagos01GFY Weapons=AssaultRifle, CompactRifle, Pistol, CombatPistol, SNSPistol, VintagePistol, APPistol, Pistol50, MicroSMG, DoubleActionRevolver, MiniSMG, MicroSMG, PumpShotgun Health=120 Armor=0 Accuracy=10 CombatMovement=offensive +CombatRange=far +MaxPeds=15 -[ENEMY_TEAM] -Models=Lost01GFY, Lost01GMY, Lost02GMY, Lost03GMY, BallaEast01GMY, BallaOrig01GMY, Ballas01GFY, BallaSout01GMY -Weapons=AssaultRifle, CompactRifle, Pistol, CombatPistol, SNSPistol, VintagePistol, APPistol, Pistol50, MicroSMG, DoubleActionRevolver, MiniSMG, MicroSMG, PumpShotgun +[ALLIED_TEAM] +Models=BlackOps01SMY,BlackOps02SMY,BlackOps03SMY,Cop01SFY,Cop01SMY,Sheriff01SFY,Sheriff01SMY,SecuroGuardMale01,Armoured01SMM,Armoured02SMM,FibSec01SMM +Weapons=CarbineRifle, CombatPistol, HeavyPistol, SMG, SMGMk2, Revolver, RevolverMk2, PumpShotgun, PumpShotgunMk2, CarbineRifleMk2, AdvancedRifle, CombatPDW, SpecialCarbine, SpecialCarbineMk2 Health=120 -Armor=0 +Armor=50 Accuracy=10 -CombatMovement=offensive +CombatMovement=defensive +CombatRange=far +MaxPeds=15 [SETTINGS] Hotkey=B -MaxPedsPerTeam=10 +SpawnHotkey=N +MaxPedsPerTeam=15 NoWantedLevel=true ShowBlipsOnPeds=true DropWeaponOnDead=false RemoveDeadPeds=true RunToSpawnpoint=true +ProcessOtherRelationshipGroups=false +IdleInterval=500 +BattleInterval=100