diff --git a/.github/workflows/extract_changelog.py b/.github/workflows/extract_changelog.py new file mode 100644 index 0000000..5e279b7 --- /dev/null +++ b/.github/workflows/extract_changelog.py @@ -0,0 +1,65 @@ +"""EXTRACT CHANGELOG Script +This script extracts the changelog for the given version from the README.md file, under the `## Changelog` section. +The version is given as x.y.z (e.g. 0.1.1) as an arg (e.g. running "python extract_changelog.py 0.1.1"). +The changelog is saved as markdown on a file. +""" + +import sys +import re +from typing import List + +from markdown2 import markdown +from bs4 import BeautifulSoup +from bs4.element import Tag + +README_FILE = "README.md" +CHANGELOG_OUTPUT_FILE = "changelog_generated.md" + + +def _is_valid_version(version: str) -> bool: + return bool(re.match(r"^(\d+\.)?(\d+\.)?(\*|\d+)$", version)) + + +def _find_changelog_list(soup: BeautifulSoup, version: str) -> Tag: + all_lists = soup.find_all("li") + return next(li for li in all_lists if li.text.startswith(version)) + + +def _parse_changelog(li: Tag) -> List[str]: + all_changelog_lists = li.find("ul").find_all("li") + return [li.text.strip() for li in all_changelog_lists] + + +def _format_changelog_output(changelog: List[str]) -> str: + output = "" + for change in changelog: + output += f"\n- {change}" + return output.strip() + + +def _read(filename: str) -> str: + with open(filename, "r") as f: + return f.read() + + +def _save(filename: str, content: str): + with open(filename, "w") as f: + f.write(content) + + +def extract_changelog(version: str): + assert _is_valid_version(version) + + readme_content = _read(README_FILE) + readme_html = markdown(readme_content) + + soup = BeautifulSoup(readme_html, "html.parser") + changelog_list = _find_changelog_list(soup=soup, version=version) + changelog_changes = _parse_changelog(changelog_list) + changelog_output = _format_changelog_output(changelog_changes) + + _save(filename=CHANGELOG_OUTPUT_FILE, content=changelog_output) + + +if __name__ == '__main__': + extract_changelog(sys.argv[-1]) diff --git a/.github/workflows/extract_changelog_requirements.txt b/.github/workflows/extract_changelog_requirements.txt new file mode 100644 index 0000000..8affe3c --- /dev/null +++ b/.github/workflows/extract_changelog_requirements.txt @@ -0,0 +1,2 @@ +markdown2==2.3.10 +beautifulsoup4==4.9.3 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..dc0e44c --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,61 @@ +on: + push: + tags: + - "*.*.*" + +name: Create Release + +jobs: + build: + name: Create Release + runs-on: ubuntu-latest + steps: + # Setup + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.7 + architecture: x64 + - name: Install Python requirements + run: python -m pip install -r .github/workflows/extract_changelog_requirements.txt + + # Fetch variables + - name: Extract version from tag + id: get_version + run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + - name: Fetch changelog for release description + id: get_changelog + run: "python .github/workflows/extract_changelog.py ${{ steps.get_version.outputs.VERSION }}" + + # Generate artifact + - name: Create release artifact (zip) + id: create_zip + run: | + cp README.md README.txt + cp LICENSE.md LICENSE.txt + zip release-artifact.zip SimpleGangWar.cs SimpleGangWar.ini README.txt LICENSE.txt + + # Release + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions + with: + tag_name: ${{ github.ref }} + release_name: "v${{ github.ref }}" + body_path: changelog_generated.md + draft: false + prerelease: false + - name: Upload release asset + id: upload_release_asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: "./release-artifact.zip" + asset_name: "GTAV-SimpleGangWar-${{ steps.get_version.outputs.VERSION }}.zip" + asset_content_type: "application/zip" diff --git a/.gitignore b/.gitignore index 79817cf..fd03010 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -bin/ -obj/ -.vs/ -*.csproj -*.config -*.sln +bin/ +obj/ +.vs/ +*.csproj +*.config +*.sln +.idea/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4883d46 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,5 @@ +Copyright 2021 David Lorenzo @ github.com/David-Lor + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index f2fd3a9..f77b1ce 100644 --- a/README.md +++ b/README.md @@ -37,19 +37,22 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `Health`: health for peds (should not be least than 100) - `Armor`: armor for peds (from 0) - `Accuracy`: accuracy for peds (from 0) -- `CombatMovement`: how the peds will move through the battlefield. This can be used to make one team defend its spawnpoint, while the other team tries to attack it. One of following: +- `CombatMovement`: how the peds will move through the battlefield. This can be used to make one team defend its spawnpoint, while the other team tries to attack it. If RunToSpawnpoint=true, this setting most probably will be ignored. One of following: - `stationary`: not move at all - `defensive`: stay near the spawnpoint and take cover - `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**_ + - `disabled`: do not alter this setting on peds + - `random`: randomize between `defensive` and `offensive` for each spawned ped. This does not always work as expected, since some peds can be stuck on the spawnpoint waiting for other peds to attack, but since they are defending their position, nobody would attack - `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` + - `disabled`: do not alter this setting on peds + - `random`: randomize between `near`, `medium`, `far` for each spawned ped - `MaxPeds`: maximum alive peds on the team (if not specified, the MaxPedsPerTeam setting will be used) -## SETTINGS +### SETTINGS - `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)) @@ -63,6 +66,8 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo 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). +- `SpawnpointFloodLimitPeds`: limit how many peds can be near its spawnpoint. If more than this quantity of peds are near the spawnpoint, no more peds on the team will spawn. Disable this feature by setting this variable to `0`. +- `SpawnpointFloodLimitDistance`: in-game distance from a team spawnpoint to keep track of the SpawnpointFloodLimitPeds. Can be integer or decimal (if using decimals, use dot or comma depending on your system regional settings) - `IdleInterval`: delay between loop runs, when battle is not running, in ms - `BattleInterval`: delay between loop runs, when battle is running, in ms @@ -70,6 +75,7 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - 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 +- Peds can avoid reloads (this is mostly noticeable with muskets) ## TODO @@ -82,6 +88,10 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo ## Changelog +- 2.2.1 + - Add spawnpoint anti-flood feature (avoid peds from flooding their spawnpoints) + - Add options to randomize CombatMovement & CombatRange + - Add options to disable altering CombatMovement & CombatRange - 2.1.1 - Add CombatRange setting - Add ProcessOtherRelationshipGroups setting diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index 8a0198a..0da64d3 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -4,8 +4,8 @@ using System; using System.Windows.Forms; using System.Collections.Generic; -using System.Linq; - +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 @@ -15,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; @@ -38,6 +38,8 @@ public class SimpleGangWar : Script { private static bool removeDeadPeds = true; private static bool runToSpawnpoint = true; private static bool processOtherRelationshipGroups = false; + private static int spawnpointFloodLimitPeds = 10; + private static float spawnpointFloodLimitDistance = 8.0f; private static int idleInterval = 500; private static int battleInterval = 100; private static int maxPedsAllies; @@ -55,16 +57,16 @@ public class SimpleGangWar : Script { private List pedsRemove = new List(); private List processedRelationshipGroups = new List(); - private bool spawnEnabled = true; + private bool spawnEnabled = true; private Stage stage = Stage.Initial; private Vector3 spawnpointAllies; - private Vector3 spawnpointEnemies; - private float spawnpointsDistance; + private Vector3 spawnpointEnemies; + private float spawnpointsDistance; private Blip spawnpointBlipAllies; - private Blip spawnpointBlipEnemies; - + private Blip spawnpointBlipEnemies; + private static Relationship[] allyRelationships = { Relationship.Companion, Relationship.Like, Relationship.Respect }; private static Relationship[] enemyRelationships = { Relationship.Hate, Relationship.Dislike }; @@ -73,19 +75,25 @@ public class SimpleGangWar : Script { private enum CombatMovement { // https://runtime.fivem.net/doc/natives/?_0x4D9CA1009AFBD057 + // NOTE Stationary, Suicidal - do not seem to work as expected Stationary = 0, Defensive = 1, Offensive = 2, - Suicidal = 3 - // TODO setting to randomize movement for each ped? + Suicidal = 3, + Disabled = 0, + Random = -1 } + private static CombatMovement[] randomizableCombatMovements = { CombatMovement.Defensive, CombatMovement.Offensive }; - private enum CombatRange { + private enum CombatRange { + // https://docs.fivem.net/natives/?_0x3C606747B23E497B Near = 0, Medium = 1, - Far = 2 - // TODO setting to randomize range for each ped + Far = 2, + Disabled = 0, + Random = -1 } + private static CombatRange[] randomizableCombatRanges = { CombatRange.Near, CombatRange.Medium, CombatRange.Far }; private enum Stage { Initial = 0, @@ -95,10 +103,10 @@ 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"; } @@ -151,6 +159,8 @@ public SimpleGangWar() { removeDeadPeds = config.GetValue(SettingsHeader.General, "RemoveDeadPeds", removeDeadPeds); runToSpawnpoint = config.GetValue(SettingsHeader.General, "RunToSpawnpoint", runToSpawnpoint); processOtherRelationshipGroups = config.GetValue(SettingsHeader.General, "ProcessOtherRelationshipGroups", processOtherRelationshipGroups); + spawnpointFloodLimitPeds = config.GetValue(SettingsHeader.General, "SpawnpointFloodLimitPeds", spawnpointFloodLimitPeds); + spawnpointFloodLimitDistance = config.GetValue(SettingsHeader.General, "SpawnpointFloodLimitDistance", spawnpointFloodLimitDistance); idleInterval = config.GetValue(SettingsHeader.General, "IdleInterval", idleInterval); battleInterval = config.GetValue(SettingsHeader.General, "BattleInterval", battleInterval); @@ -165,8 +175,9 @@ public SimpleGangWar() { SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupEnemies, relationshipGroupEnemies); SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupAllies, relationshipGroupPlayer); SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroupEnemies, relationshipGroupPlayer); + // TODO processedRelationshipGroups not being used? processedRelationshipGroups.Add(relationshipGroupPlayer); - processedRelationshipGroups.Add(relationshipGroupAllies); + processedRelationshipGroups.Add(relationshipGroupAllies); processedRelationshipGroups.Add(relationshipGroupEnemies); random = new Random(); @@ -175,28 +186,28 @@ public SimpleGangWar() { } - /// - /// 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. + /// + /// 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); - - SetUnmanagedPedsInRelationshipGroups(); - 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. + /// + /// Key event handler for key releases. /// private void OnKeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == hotkey) { @@ -227,15 +238,15 @@ private void OnKeyUp(object sender, KeyEventArgs e) { break; } } else if (e.KeyCode == spawnHotkey) { - spawnEnabled = !spawnEnabled; - BlinkSpawnpoint(true); - BlinkSpawnpoint(false); + spawnEnabled = !spawnEnabled; + BlinkSpawnpoint(true); + BlinkSpawnpoint(false); } } - /// - /// After the spawnpoints are defined, some tweaks are required just before the battle begins. + /// + /// After the spawnpoints are defined, some tweaks are required just before the battle begins. /// private void SetupBattle() { Interval = battleInterval; @@ -247,23 +258,45 @@ private void SetupBattle() { } } - /// - /// Spawn peds on the given team, until the ped limit for that team is reached. - /// + /// + /// 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) { + while (spawnEnabled && CanPedsSpawn(alliedTeam)) { + SpawnRandomPed(alliedTeam); + } + } + + /// + /// Determine if peds on the given team should spawn or not. + /// + /// true=ally team / false=enemy team + private bool CanPedsSpawn(bool alliedTeam) { List spawnedPedsList = alliedTeam ? spawnedAllies : spawnedEnemies; int maxPeds = alliedTeam ? maxPedsAllies : maxPedsEnemies; - while (spawnEnabled && spawnedPedsList.Count < maxPeds) { - SpawnRandomPed(alliedTeam); + // by MaxPeds in the team + if (spawnedPedsList.Count >= maxPeds) return false; + + // by SpawnpointFlood limit + if (spawnpointFloodLimitPeds < 1) return true; + + Vector3 spawnpointPosition = alliedTeam ? spawnpointAllies : spawnpointEnemies; + Ped[] pedsNearSpawnpoint = World.GetNearbyPeds(spawnpointPosition, spawnpointFloodLimitDistance); + + int pedsNearSpawnpointCount = 0; + foreach (Ped ped in pedsNearSpawnpoint) { + if (ped.IsAlive && spawnedPedsList.Contains(ped)) pedsNearSpawnpointCount++; } + + return pedsNearSpawnpointCount < spawnpointFloodLimitPeds; } - /// - /// Spawns a ped on the given team, ready to fight. - /// - /// true=ally team / false=enemy team + /// + /// 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; @@ -290,6 +323,18 @@ private Ped SpawnRandomPed(bool alliedTeam) { ped.RelationshipGroup = alliedTeam ? relationshipGroupAllies : relationshipGroupEnemies; ped.DropsWeaponsOnDeath = dropWeaponOnDead; + CombatRange combatRange = alliedTeam ? combatRangeAllies : combatRangeEnemies; + if (combatRange != CombatRange.Disabled) { + if (combatRange == CombatRange.Random) combatRange = RandomChoice(randomizableCombatRanges); + Function.Call(Hash.SET_PED_COMBAT_RANGE, ped, (int)combatRange); + } + + CombatMovement combatMovement = alliedTeam ? combatMovementAllies : combatMovementEnemies; + if (combatMovement != CombatMovement.Disabled) { + if (combatMovement == CombatMovement.Random) combatMovement = RandomChoice(randomizableCombatMovements); + Function.Call(Hash.SET_PED_COMBAT_MOVEMENT, ped, (int)combatMovement); + } + 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, (int)(alliedTeam ? combatRangeAllies : combatRangeEnemies)); @@ -303,15 +348,15 @@ private Ped SpawnRandomPed(bool alliedTeam) { } ped.Task.ClearAllImmediately(); - ped.AlwaysKeepTask = true; + ped.AlwaysKeepTask = true; // TODO Investigate if this could be making peds avoid reloads (alliedTeam ? spawnedAllies : spawnedEnemies).Add(ped); 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. - /// + /// + /// 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; @@ -335,9 +380,9 @@ private void ProcessSpawnedPeds(bool alliedTeam) { pedsRemove.Clear(); } - /// - /// Set the spawnpoint for the given team on the position where the player is at. - /// + /// + /// 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; @@ -355,47 +400,47 @@ 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). - /// + /// + /// 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) { + 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. + /// + /// 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); - } - } - } - } + 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. - /// + /// + /// 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) { @@ -403,8 +448,8 @@ private void TeardownPeds(List pedList) { } } - /// - /// Manage the battle teardown on user requests. This brings the game to an initial state, before battle start and spawnpoint definition. + /// + /// 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; @@ -417,56 +462,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 + /// + /// 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 + /// + /// 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)]; } - /// - /// 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 + /// + /// 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 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 of words to be split, split them and return a string array. - /// - /// Input string - /// Array to return if the input string contains no items + /// + /// 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; + 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 35da9a9..0ff0d95 100644 --- a/SimpleGangWar.ini +++ b/SimpleGangWar.ini @@ -1,4 +1,5 @@ //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 @@ -29,5 +30,7 @@ DropWeaponOnDead=false RemoveDeadPeds=true RunToSpawnpoint=true ProcessOtherRelationshipGroups=false +SpawnpointFloodLimitPeds=10 +SpawnpointFloodLimitDistance=8 IdleInterval=500 BattleInterval=100