From 1e0672cec82fcde8e4b729b6e4150797ac965601 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Tue, 22 Dec 2020 13:51:06 +0100 Subject: [PATCH 01/11] docs: update README (change h2 to h3 in Settings-SETTINGS) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2fd3a9..f30160e 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `far` - `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)) From 7971bece5cf2bed9120de53632a978718be27b7c Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Tue, 22 Dec 2020 13:51:36 +0100 Subject: [PATCH 02/11] Add LICENSE & gitignore .idea/ --- .gitignore | 13 +++++++------ LICENSE.md | 6 ++++++ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 LICENSE.md 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..214116e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,6 @@ +Copyright 2020 David Lorenzo + +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. + From 2a39f8b07a061814017394fcc3210511add66ee2 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Tue, 22 Dec 2020 13:55:46 +0100 Subject: [PATCH 03/11] Add Github Workflow to create a release ZIP file --- .github/workflows/extract_changelog.py | 65 +++++++++++++++++++ .../extract_changelog_requirements.txt | 2 + .github/workflows/release.yaml | 61 +++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 .github/workflows/extract_changelog.py create mode 100644 .github/workflows/extract_changelog_requirements.txt create mode 100644 .github/workflows/release.yaml 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" From 29b95e695a34f595f826958527de1ba361628209 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:39:49 +0100 Subject: [PATCH 04/11] style: normalize tabs to spaces indentation --- SimpleGangWar.cs | 217 ++++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 108 deletions(-) diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index 8a0198a..ffce762 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; @@ -55,16 +55,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 }; @@ -80,11 +80,11 @@ private enum CombatMovement { // TODO setting to randomize movement for each ped? } - private enum CombatRange { + private enum CombatRange { Near = 0, Medium = 1, - Far = 2 - // TODO setting to randomize range for each ped + Far = 2 + // TODO setting to randomize range for each ped } private enum Stage { @@ -95,10 +95,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"; } @@ -165,8 +165,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 +176,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 +228,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,9 +248,9 @@ 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) { List spawnedPedsList = alliedTeam ? spawnedAllies : spawnedEnemies; @@ -260,10 +261,10 @@ private void SpawnPeds(bool alliedTeam) { } } - /// - /// 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; @@ -309,9 +310,9 @@ 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. - /// + /// + /// 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 +336,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 +356,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 +404,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 +418,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; } } From 464c41cefb8f13ea42030e712c9c99ac3bb9c84f Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:48:49 +0100 Subject: [PATCH 05/11] todo: warn bug where NPC skip reloading guns --- README.md | 1 + SimpleGangWar.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f30160e..5cccab4 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,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 diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index ffce762..a92e624 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -304,7 +304,7 @@ 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; From 02f372f172105147e7d1867e54bf19fc365a323b Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Thu, 18 Feb 2021 18:07:19 +0100 Subject: [PATCH 06/11] refactor: extract logic into method for determining if peds can spawn --- SimpleGangWar.cs | 228 ++++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 110 deletions(-) diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index 8a0198a..a3d9bdb 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; @@ -59,12 +59,12 @@ public class SimpleGangWar : Script { 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 }; @@ -80,11 +80,11 @@ private enum CombatMovement { // TODO setting to randomize movement for each ped? } - private enum CombatRange { + private enum CombatRange { Near = 0, Medium = 1, - Far = 2 - // TODO setting to randomize range for each ped + Far = 2 + // TODO setting to randomize range for each ped } private enum Stage { @@ -95,10 +95,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"; } @@ -166,7 +166,7 @@ public SimpleGangWar() { SetRelationshipBetweenGroups(Relationship.Respect, relationshipGroupAllies, relationshipGroupPlayer); SetRelationshipBetweenGroups(Relationship.Hate, relationshipGroupEnemies, relationshipGroupPlayer); processedRelationshipGroups.Add(relationshipGroupPlayer); - processedRelationshipGroups.Add(relationshipGroupAllies); + processedRelationshipGroups.Add(relationshipGroupAllies); processedRelationshipGroups.Add(relationshipGroupEnemies); random = new Random(); @@ -175,28 +175,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 +227,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 +247,31 @@ 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); - } + return spawnedPedsList.Count < maxPeds; } - /// - /// 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; @@ -309,9 +317,9 @@ 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. - /// + /// + /// 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 +343,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 +363,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 +411,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 +425,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; } } From 91e1229c72337efb2d40eef074ae8919b3132a71 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:08:54 +0100 Subject: [PATCH 07/11] feat: add spawnpoint anti-flood feature This avoids peds from flooding their spawnpoints --- README.md | 4 ++++ SimpleGangWar.cs | 20 +++++++++++++++++++- SimpleGangWar.ini | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f30160e..679e5cf 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,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. - `IdleInterval`: delay between loop runs, when battle is not running, in ms - `BattleInterval`: delay between loop runs, when battle is running, in ms @@ -82,6 +84,8 @@ _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) - 2.1.1 - Add CombatRange setting - Add ProcessOtherRelationshipGroups setting diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index a3d9bdb..e752de3 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -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; @@ -151,6 +153,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); @@ -265,7 +269,21 @@ private bool CanPedsSpawn(bool alliedTeam) { List spawnedPedsList = alliedTeam ? spawnedAllies : spawnedEnemies; int maxPeds = alliedTeam ? maxPedsAllies : maxPedsEnemies; - return spawnedPedsList.Count < maxPeds; + // 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; } /// diff --git a/SimpleGangWar.ini b/SimpleGangWar.ini index 35da9a9..2dd4883 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.0 IdleInterval=500 BattleInterval=100 From 9c1f78bea66c1c2757d82aaa122a5aae60b82572 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:50:55 +0100 Subject: [PATCH 08/11] feat: add options to randomize CombatRange & CombatMovement --- README.md | 6 +++++- SimpleGangWar.cs | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5cccab4..9de7bc8 100644 --- a/README.md +++ b/README.md @@ -37,16 +37,18 @@ _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 + - `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 - _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` + - `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 @@ -83,6 +85,8 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo ## Changelog +- 2.2.1 + - Add options to randomize CombatMovement & CombatRange - 2.1.1 - Add CombatRange setting - Add ProcessOtherRelationshipGroups setting diff --git a/SimpleGangWar.cs b/SimpleGangWar.cs index a92e624..a2cb57c 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -73,19 +73,23 @@ 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, + Random = -1 } + private static CombatMovement[] randomizableCombatMovements = { CombatMovement.Defensive, CombatMovement.Offensive }; 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, + Random = -1 } + private static CombatRange[] randomizableCombatRanges = { CombatRange.Near, CombatRange.Medium, CombatRange.Far }; private enum Stage { Initial = 0, @@ -291,6 +295,14 @@ private Ped SpawnRandomPed(bool alliedTeam) { ped.RelationshipGroup = alliedTeam ? relationshipGroupAllies : relationshipGroupEnemies; ped.DropsWeaponsOnDeath = dropWeaponOnDead; + CombatRange combatRange = alliedTeam ? combatRangeAllies : combatRangeEnemies; + 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.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)); From 6c464478d0a896e83a0a65e33cf7cdb0af662b38 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Fri, 19 Feb 2021 21:41:33 +0100 Subject: [PATCH 09/11] chore: update LICENSE header --- LICENSE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 214116e..4883d46 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,5 @@ -Copyright 2020 David Lorenzo +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. - From 4eb13468040170668ada2b5978c84fb989b93f3e Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Fri, 19 Feb 2021 21:53:40 +0100 Subject: [PATCH 10/11] chore: remove decimal from SpawnpointFloodLimitDistance on .ini file C# parses decimals using the system regional settings. Where commas are used instead of dots to split decimals, this can cause problems --- README.md | 2 +- SimpleGangWar.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e4cfbc0..6219a0b 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `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. +- `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 diff --git a/SimpleGangWar.ini b/SimpleGangWar.ini index 2dd4883..0ff0d95 100644 --- a/SimpleGangWar.ini +++ b/SimpleGangWar.ini @@ -31,6 +31,6 @@ RemoveDeadPeds=true RunToSpawnpoint=true ProcessOtherRelationshipGroups=false SpawnpointFloodLimitPeds=10 -SpawnpointFloodLimitDistance=8.0 +SpawnpointFloodLimitDistance=8 IdleInterval=500 BattleInterval=100 From 20620f7b7fcb12a5b4bdc0e19bb5e278043a3684 Mon Sep 17 00:00:00 2001 From: David-Lor <17401854+David-Lor@users.noreply.github.com> Date: Fri, 19 Feb 2021 22:10:31 +0100 Subject: [PATCH 11/11] feat: add options to disable altering CombatRange & CombatMovement --- README.md | 4 +++- SimpleGangWar.cs | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6219a0b..f77b1ce 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - `defensive`: stay near the spawnpoint and take cover - `offensive`: focus on attacking the enemy team - `suicidal`: more aggresive attack + - `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 - - _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` + - `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) @@ -90,6 +91,7 @@ _All lists of items (models & weapons) are separated by comma (`,`) or semi-colo - 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 ca0bfca..0da64d3 100644 --- a/SimpleGangWar.cs +++ b/SimpleGangWar.cs @@ -80,6 +80,7 @@ private enum CombatMovement { Defensive = 1, Offensive = 2, Suicidal = 3, + Disabled = 0, Random = -1 } private static CombatMovement[] randomizableCombatMovements = { CombatMovement.Defensive, CombatMovement.Offensive }; @@ -89,6 +90,7 @@ private enum CombatRange { Near = 0, Medium = 1, Far = 2, + Disabled = 0, Random = -1 } private static CombatRange[] randomizableCombatRanges = { CombatRange.Near, CombatRange.Medium, CombatRange.Far }; @@ -322,12 +324,16 @@ private Ped SpawnRandomPed(bool alliedTeam) { ped.DropsWeaponsOnDeath = dropWeaponOnDead; CombatRange combatRange = alliedTeam ? combatRangeAllies : combatRangeEnemies; - if (combatRange == CombatRange.Random) combatRange = RandomChoice(randomizableCombatRanges); - Function.Call(Hash.SET_PED_COMBAT_RANGE, ped, (int)combatRange); + 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.Random) combatMovement = RandomChoice(randomizableCombatMovements); - Function.Call(Hash.SET_PED_COMBAT_MOVEMENT, ped, (int)combatMovement); + 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);