Simple Wave-Based Survival V1.2
357
15
357
15
To Install:
Put WaveSurvival.3.cs into your scripts folder. That's it. The controls will be shown once you open the game.
**Description**:
Bring back the classic wave-based survival mode to Story Mode.
Face off against squads of Merryweather mercenaries, fight through up to 10 customizable waves, and see how long you can last. Each wave gets harder, more enemies, stronger armor, and better weapons. Survive them all to earn a cash reward.
Everything is configurable via an in-game overlay menu (F3). No need to edit the code. Adjust waves, spawn radius, cash rewards, and more right on the fly, then save your settings automatically to an .ini.
**Features**:
Configurable waves. Set number of waves, enemy count, scaling, and spawn distance.
Merryweather enemies. BlackOps and Marine units with randomized weapons & gear, and Ballas.
Weapon variety. Carbine Rifle, Pump Shotgun, SMGs, Heavy/Combat Pistol, and more (inspired by Max Payne 3). Lighter loadouts for Ballas.
Death ends the run. The survival automatically stops if you die.
Rewards. Earn cash after clearing all waves (configurable).
**In-game menu (F3), tweak settings live**:
Max waves
Base enemy count
Enemies per wave
Spawn radius
Simultaneous enemy cap
Victory cash reward
Allow Wanted level or not
Enemy selection between Merryweather and Ballas
Settings auto-save to WaveSurvival.ini.
**Controls**:
F7 → Start survival
F8 → Stop survival
F3 → Open/close config menu (↑↓ navigate, ←→ adjust, Shift = faster)
**Installation**:
Install ScriptHookV
by Alexander Blade.
Install ScriptHookVDotNet
(v3 or newer).
Place WaveSurvival.cs inside your GTA V scripts folder (create it if it doesn’t exist).
Launch the game and press F7 to begin.
The mod will automatically generate WaveSurvival.ini in the scripts folder after your first run.
**Changelog**:
V1.2
Added AllowWanted toggle, which decides if the player can gain wanted level or not.
Added EnemyFaction selector, which makes it possible to switch between Merryweather and Ballas enemies.
Ballas enemies' models are each "g_m_y_ballaeast_01", "g_m_y_ballaorig_01", and "g_m_y_ballasout_01".
Ballas has lighter loadouts compared to Merryweather. Not configurable yet.
Interaction with Cops are changed. Now they are neutral to them as default, but will open fire if they attack, or fire a gun near them. -20s of no aggression will reset their neutrallity.
Removed health gaining per wave, since it was too much and I figured only increasing armor is better.
Late waves have more chance for better weapons.
V1.1
Added:
Wave-scaled weapon loadouts. Enemies now receive stronger primaries as waves progress (early: MicroSMG/SMG/Pump. mid: SMG/AssaultSMG/Pump/Carbine. late: Carbine/AssaultSMG/Assault Shotgun). Sidearms escalate from Pistol -> Combat/Heavy -> Heavy.
Late-wave “kicker.” From wave ≥7, small chance to add/force Carbine Rifle for extra pressure.
Changed:
Model selection logic (per-ped, weighted). Instead of picking one model for an entire wave, each spawned ped rolls from a wave-aware, weighted pool:
Early waves: mostly s_m_y_blackops_01.
Mid waves (≥5): frequent s_m_y_blackops_02.
Late waves (≥8): blackops_02 common, with 01 still mixed in.
Result: blackops_02 appears consistently across all further waves, not just once.
Fixed:
Compiler compatibility (C#5-safe). Replaced out var with predeclared out parameters in PickWeaponsForWave(...) call to avoid language-version errors.
Redundant wanted-level clearing. Removed per-tick forced WantedLevel = 0/CLEAR_PLAYER_WANTED_LEVEL, preventing conflicts with other scripts and saving work each tick.
V1.0:
First release.
10-wave survival with Merryweather enemies.
Random props & outfit variety.
Configurable overlay menu with .ini saving.
Cash reward on victory.
**Credits**:
Script & concept by KoreaBank
Powered by ScriptHookV & ScriptHookVDotNet
Put WaveSurvival.3.cs into your scripts folder. That's it. The controls will be shown once you open the game.
**Description**:
Bring back the classic wave-based survival mode to Story Mode.
Face off against squads of Merryweather mercenaries, fight through up to 10 customizable waves, and see how long you can last. Each wave gets harder, more enemies, stronger armor, and better weapons. Survive them all to earn a cash reward.
Everything is configurable via an in-game overlay menu (F3). No need to edit the code. Adjust waves, spawn radius, cash rewards, and more right on the fly, then save your settings automatically to an .ini.
**Features**:
Configurable waves. Set number of waves, enemy count, scaling, and spawn distance.
Merryweather enemies. BlackOps and Marine units with randomized weapons & gear, and Ballas.
Weapon variety. Carbine Rifle, Pump Shotgun, SMGs, Heavy/Combat Pistol, and more (inspired by Max Payne 3). Lighter loadouts for Ballas.
Death ends the run. The survival automatically stops if you die.
Rewards. Earn cash after clearing all waves (configurable).
**In-game menu (F3), tweak settings live**:
Max waves
Base enemy count
Enemies per wave
Spawn radius
Simultaneous enemy cap
Victory cash reward
Allow Wanted level or not
Enemy selection between Merryweather and Ballas
Settings auto-save to WaveSurvival.ini.
**Controls**:
F7 → Start survival
F8 → Stop survival
F3 → Open/close config menu (↑↓ navigate, ←→ adjust, Shift = faster)
**Installation**:
Install ScriptHookV
by Alexander Blade.
Install ScriptHookVDotNet
(v3 or newer).
Place WaveSurvival.cs inside your GTA V scripts folder (create it if it doesn’t exist).
Launch the game and press F7 to begin.
The mod will automatically generate WaveSurvival.ini in the scripts folder after your first run.
**Changelog**:
V1.2
Added AllowWanted toggle, which decides if the player can gain wanted level or not.
Added EnemyFaction selector, which makes it possible to switch between Merryweather and Ballas enemies.
Ballas enemies' models are each "g_m_y_ballaeast_01", "g_m_y_ballaorig_01", and "g_m_y_ballasout_01".
Ballas has lighter loadouts compared to Merryweather. Not configurable yet.
Interaction with Cops are changed. Now they are neutral to them as default, but will open fire if they attack, or fire a gun near them. -20s of no aggression will reset their neutrallity.
Removed health gaining per wave, since it was too much and I figured only increasing armor is better.
Late waves have more chance for better weapons.
V1.1
Added:
Wave-scaled weapon loadouts. Enemies now receive stronger primaries as waves progress (early: MicroSMG/SMG/Pump. mid: SMG/AssaultSMG/Pump/Carbine. late: Carbine/AssaultSMG/Assault Shotgun). Sidearms escalate from Pistol -> Combat/Heavy -> Heavy.
Late-wave “kicker.” From wave ≥7, small chance to add/force Carbine Rifle for extra pressure.
Changed:
Model selection logic (per-ped, weighted). Instead of picking one model for an entire wave, each spawned ped rolls from a wave-aware, weighted pool:
Early waves: mostly s_m_y_blackops_01.
Mid waves (≥5): frequent s_m_y_blackops_02.
Late waves (≥8): blackops_02 common, with 01 still mixed in.
Result: blackops_02 appears consistently across all further waves, not just once.
Fixed:
Compiler compatibility (C#5-safe). Replaced out var with predeclared out parameters in PickWeaponsForWave(...) call to avoid language-version errors.
Redundant wanted-level clearing. Removed per-tick forced WantedLevel = 0/CLEAR_PLAYER_WANTED_LEVEL, preventing conflicts with other scripts and saving work each tick.
V1.0:
First release.
10-wave survival with Merryweather enemies.
Random props & outfit variety.
Configurable overlay menu with .ini saving.
Cash reward on victory.
**Credits**:
Script & concept by KoreaBank
Powered by ScriptHookV & ScriptHookVDotNet
Перше завантаження: 23 Серпня 2025
Останнє оновлення 8 hours ago
Last Downloaded:
19 коментаря
More mods by KoreaBank:
To Install:
Put WaveSurvival.3.cs into your scripts folder. That's it. The controls will be shown once you open the game.
**Description**:
Bring back the classic wave-based survival mode to Story Mode.
Face off against squads of Merryweather mercenaries, fight through up to 10 customizable waves, and see how long you can last. Each wave gets harder, more enemies, stronger armor, and better weapons. Survive them all to earn a cash reward.
Everything is configurable via an in-game overlay menu (F3). No need to edit the code. Adjust waves, spawn radius, cash rewards, and more right on the fly, then save your settings automatically to an .ini.
**Features**:
Configurable waves. Set number of waves, enemy count, scaling, and spawn distance.
Merryweather enemies. BlackOps and Marine units with randomized weapons & gear, and Ballas.
Weapon variety. Carbine Rifle, Pump Shotgun, SMGs, Heavy/Combat Pistol, and more (inspired by Max Payne 3). Lighter loadouts for Ballas.
Death ends the run. The survival automatically stops if you die.
Rewards. Earn cash after clearing all waves (configurable).
**In-game menu (F3), tweak settings live**:
Max waves
Base enemy count
Enemies per wave
Spawn radius
Simultaneous enemy cap
Victory cash reward
Allow Wanted level or not
Enemy selection between Merryweather and Ballas
Settings auto-save to WaveSurvival.ini.
**Controls**:
F7 → Start survival
F8 → Stop survival
F3 → Open/close config menu (↑↓ navigate, ←→ adjust, Shift = faster)
**Installation**:
Install ScriptHookV
by Alexander Blade.
Install ScriptHookVDotNet
(v3 or newer).
Place WaveSurvival.cs inside your GTA V scripts folder (create it if it doesn’t exist).
Launch the game and press F7 to begin.
The mod will automatically generate WaveSurvival.ini in the scripts folder after your first run.
**Changelog**:
V1.2
Added AllowWanted toggle, which decides if the player can gain wanted level or not.
Added EnemyFaction selector, which makes it possible to switch between Merryweather and Ballas enemies.
Ballas enemies' models are each "g_m_y_ballaeast_01", "g_m_y_ballaorig_01", and "g_m_y_ballasout_01".
Ballas has lighter loadouts compared to Merryweather. Not configurable yet.
Interaction with Cops are changed. Now they are neutral to them as default, but will open fire if they attack, or fire a gun near them. -20s of no aggression will reset their neutrallity.
Removed health gaining per wave, since it was too much and I figured only increasing armor is better.
Late waves have more chance for better weapons.
V1.1
Added:
Wave-scaled weapon loadouts. Enemies now receive stronger primaries as waves progress (early: MicroSMG/SMG/Pump. mid: SMG/AssaultSMG/Pump/Carbine. late: Carbine/AssaultSMG/Assault Shotgun). Sidearms escalate from Pistol -> Combat/Heavy -> Heavy.
Late-wave “kicker.” From wave ≥7, small chance to add/force Carbine Rifle for extra pressure.
Changed:
Model selection logic (per-ped, weighted). Instead of picking one model for an entire wave, each spawned ped rolls from a wave-aware, weighted pool:
Early waves: mostly s_m_y_blackops_01.
Mid waves (≥5): frequent s_m_y_blackops_02.
Late waves (≥8): blackops_02 common, with 01 still mixed in.
Result: blackops_02 appears consistently across all further waves, not just once.
Fixed:
Compiler compatibility (C#5-safe). Replaced out var with predeclared out parameters in PickWeaponsForWave(...) call to avoid language-version errors.
Redundant wanted-level clearing. Removed per-tick forced WantedLevel = 0/CLEAR_PLAYER_WANTED_LEVEL, preventing conflicts with other scripts and saving work each tick.
V1.0:
First release.
10-wave survival with Merryweather enemies.
Random props & outfit variety.
Configurable overlay menu with .ini saving.
Cash reward on victory.
**Credits**:
Script & concept by KoreaBank
Powered by ScriptHookV & ScriptHookVDotNet
Put WaveSurvival.3.cs into your scripts folder. That's it. The controls will be shown once you open the game.
**Description**:
Bring back the classic wave-based survival mode to Story Mode.
Face off against squads of Merryweather mercenaries, fight through up to 10 customizable waves, and see how long you can last. Each wave gets harder, more enemies, stronger armor, and better weapons. Survive them all to earn a cash reward.
Everything is configurable via an in-game overlay menu (F3). No need to edit the code. Adjust waves, spawn radius, cash rewards, and more right on the fly, then save your settings automatically to an .ini.
**Features**:
Configurable waves. Set number of waves, enemy count, scaling, and spawn distance.
Merryweather enemies. BlackOps and Marine units with randomized weapons & gear, and Ballas.
Weapon variety. Carbine Rifle, Pump Shotgun, SMGs, Heavy/Combat Pistol, and more (inspired by Max Payne 3). Lighter loadouts for Ballas.
Death ends the run. The survival automatically stops if you die.
Rewards. Earn cash after clearing all waves (configurable).
**In-game menu (F3), tweak settings live**:
Max waves
Base enemy count
Enemies per wave
Spawn radius
Simultaneous enemy cap
Victory cash reward
Allow Wanted level or not
Enemy selection between Merryweather and Ballas
Settings auto-save to WaveSurvival.ini.
**Controls**:
F7 → Start survival
F8 → Stop survival
F3 → Open/close config menu (↑↓ navigate, ←→ adjust, Shift = faster)
**Installation**:
Install ScriptHookV
by Alexander Blade.
Install ScriptHookVDotNet
(v3 or newer).
Place WaveSurvival.cs inside your GTA V scripts folder (create it if it doesn’t exist).
Launch the game and press F7 to begin.
The mod will automatically generate WaveSurvival.ini in the scripts folder after your first run.
**Changelog**:
V1.2
Added AllowWanted toggle, which decides if the player can gain wanted level or not.
Added EnemyFaction selector, which makes it possible to switch between Merryweather and Ballas enemies.
Ballas enemies' models are each "g_m_y_ballaeast_01", "g_m_y_ballaorig_01", and "g_m_y_ballasout_01".
Ballas has lighter loadouts compared to Merryweather. Not configurable yet.
Interaction with Cops are changed. Now they are neutral to them as default, but will open fire if they attack, or fire a gun near them. -20s of no aggression will reset their neutrallity.
Removed health gaining per wave, since it was too much and I figured only increasing armor is better.
Late waves have more chance for better weapons.
V1.1
Added:
Wave-scaled weapon loadouts. Enemies now receive stronger primaries as waves progress (early: MicroSMG/SMG/Pump. mid: SMG/AssaultSMG/Pump/Carbine. late: Carbine/AssaultSMG/Assault Shotgun). Sidearms escalate from Pistol -> Combat/Heavy -> Heavy.
Late-wave “kicker.” From wave ≥7, small chance to add/force Carbine Rifle for extra pressure.
Changed:
Model selection logic (per-ped, weighted). Instead of picking one model for an entire wave, each spawned ped rolls from a wave-aware, weighted pool:
Early waves: mostly s_m_y_blackops_01.
Mid waves (≥5): frequent s_m_y_blackops_02.
Late waves (≥8): blackops_02 common, with 01 still mixed in.
Result: blackops_02 appears consistently across all further waves, not just once.
Fixed:
Compiler compatibility (C#5-safe). Replaced out var with predeclared out parameters in PickWeaponsForWave(...) call to avoid language-version errors.
Redundant wanted-level clearing. Removed per-tick forced WantedLevel = 0/CLEAR_PLAYER_WANTED_LEVEL, preventing conflicts with other scripts and saving work each tick.
V1.0:
First release.
10-wave survival with Merryweather enemies.
Random props & outfit variety.
Configurable overlay menu with .ini saving.
Cash reward on victory.
**Credits**:
Script & concept by KoreaBank
Powered by ScriptHookV & ScriptHookVDotNet
Перше завантаження: 23 Серпня 2025
Останнє оновлення 8 hours ago
Last Downloaded:
This file has been approved automatically. If you think this file should not be here for any reason please report it.
Im trying it right now. This is what i've been looking for because the only thing to do in SP is fight cops.
@Samsth Yep, and thanks. You can ask me if you want any other features.
Verry good. The menu works fine.
Its simple and the peds are strong.
Maybee the only thing i would add is some kind of No Wanted Level during survival option.
But its perfect i like it alot.
@Samsth Man, I forgot about disabling the wanted level. I'll fix it in the next version.
work this 😌 fix mod
--------------------------------------------
// KoreaBank's Wave-Based Survival Mod V1.0 (FINAL FIXED INPUT)
// WaveSurvival.cs - Now uses polling for F3/F7/F8
using System;
using System.Collections.Generic;
using GTA;
using GTA.Math;
using GTA.Native;
using System.IO;
public class WaveSurvival : Script
{
// ---- Overlay menu state ----
bool _menuOpen = false;
int _menuIndex = 0;
string[] _menuItems = { "MaxWaves", "BaseEnemies", "EnemiesPerWave", "SimultaneousCap", "SpawnRadius", "VictoryCash" };
// ====== CONFIG ======
int MaxWaves = 10; // configurable
float SpawnRadius = 45f; // around player
int BaseEnemies = 5; // wave 1 count
int EnemiesPerWave = 2; // added each wave
int SimultaneousCap = 14; // safety cap
int VictoryCash = 50000; // reward for clearing all waves
// Settings file path
readonly string SettingsPath = @"scripts\WaveSurvival.ini";
// Track enemy blips so we can delete them
readonly Dictionary<Ped, Blip> _enemyBlips = new Dictionary<Ped, Blip>();
// Enemy models (Merryweather/paramilitary vibe). Will try in order.
readonly string[] EnemyModels =
{
"s_m_y_blackops_01",
"s_m_y_blackops_02",
"s_m_y_blackops_03",
"csb_mweather",
"s_m_m_marine_01"
};
// Primary weapons
readonly WeaponHash[] Primaries =
{
WeaponHash.CarbineRifle,
WeaponHash.SMG,
WeaponHash.MicroSMG,
WeaponHash.AssaultSMG,
WeaponHash.PumpShotgun,
WeaponHash.SawnOffShotgun,
WeaponHash.AssaultRifle
};
// Sidearms
readonly WeaponHash[] Sidearms =
{
WeaponHash.Pistol,
WeaponHash.CombatPistol,
WeaponHash.HeavyPistol
};
int _wave = 0;
bool _running = false;
bool _betweenWaves = false;
DateTime _nextWaveAt = DateTime.MinValue;
readonly List<Ped> _enemies = new List<Ped>();
// Relationship group
int _enemyGroupHash = -1;
public WaveSurvival()
{
// Ensure scripts directory exists
string fullPath = Path.Combine(Path.GetDirectoryName(ScriptDomain.Current.Path), SettingsPath);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
// Create custom relationship group
_enemyGroupHash = Function.Call<int>(Hash.GET_HASH_KEY, "WAVE_ENEMY");
if (Function.Call<bool>(Hash.DOES_RELATIONSHIP_GROUP_EXIST, _enemyGroupHash))
{
Function.Call(Hash.REMOVE_RELATIONSHIP_GROUP, _enemyGroupHash);
}
Function.Call(Hash.ADD_RELATIONSHIP_GROUP, "WAVE_ENEMY", _enemyGroupHash);
int playerGroup = (int)Game.Player.Character.RelationshipGroup;
Function.Call(Hash.SET_RELATIONSHIP_BETWEEN_GROUPS, 5, _enemyGroupHash, playerGroup);
Function.Call(Hash.SET_RELATIONSHIP_BETWEEN_GROUPS, 5, playerGroup, _enemyGroupHash);
// Script setup
Tick += OnTick;
Interval = 0; // Run every frame for responsive input
LoadSettings();
UI.Notification.Show("Wave Survival: ~y~F7~w~ start / ~y~F8~w~ stop / ~y~F3~w~ config menu");
}
public override void Aborted(object sender, EventArgs e)
{
StopGame(cleanup: true);
base.Aborted(sender, e);
}
void OnTick(object sender, EventArgs e)
{
// === 🔧 INPUT POLLING: F3, F7, F8 ===
if (Game.IsJustPressed(Keys.F3))
{
_menuOpen = !_menuOpen;
if (!_menuOpen) SaveSettings();
Script.Wait(150); // Prevent rapid toggle
return;
}
if (Game.IsJustPressed(Keys.F7) && !_running)
{
StartGame();
Script.Wait(150);
return;
}
if (Game.IsJustPressed(Keys.F8))
{
StopGame(cleanup: true);
UI.Notification.Show("Wave Survival: ~r~Stopped.");
Script.Wait(150);
return;
}
// === MENU DISPLAY ===
if (_menuOpen)
{
string menuText =
"~y~Wave Survival Config~w~ (↑/↓ select, ←/→ change, Shift = x5)\n" +
ItemLine(0, "MaxWaves: " + MaxWaves) + "\n" +
ItemLine(1, "BaseEnemies: " + BaseEnemies) + "\n" +
ItemLine(2, "EnemiesPerWave: " + EnemiesPerWave) + "\n" +
ItemLine(3, "SimultaneousCap: " + SimultaneousCap) + "\n" +
ItemLine(4, "SpawnRadius: " + ((int)SpawnRadius)) + "\n" +
ItemLine(5, "VictoryCash: $" + VictoryCash.ToString("N0")) + "\n" +
"Press ~y~F3~w~ to " + (_menuOpen ? "close" : "open");
UI.Screen.ShowSubtitle(menuText, 250);
return;
}
// === GAME LOGIC ===
if (!_running) return;
var player = Game.Player.Character;
if (!player.Exists() || player.IsDead)
{
UI.Notification.Show("~r~You died.~w~ Run ended.");
StopGame(cleanup: true);
return;
}
CleanupDeadRefs();
if (!_betweenWaves && _enemies.Count == 0)
{
_betweenWaves = true;
_nextWaveAt = DateTime.UtcNow.AddSeconds(6);
TryGiveBetweenWaveGoodies();
UI.Screen.ShowSubtitle("~g~Wave cleared!~w~ Next wave in 6 seconds...", 3000);
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, "REWARD", "HUD_FRONTEND_DEFAULT_SOUNDSET", true);
}
if (_betweenWaves && DateTime.UtcNow >= _nextWaveAt)
{
StartNextWave();
}
}
void StartGame()
{
_running = true;
_wave = 0;
_betweenWaves = false;
CleanupEnemies();
var player = Game.Player.Character;
player.Armor = 100;
player.Health = player.MaxHealth;
UI.Notification.Show("Wave Survival: ~g~Started!~w~ Survive ~y~" + MaxWaves + "~w~ waves.");
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, "COUNTDOWN_THREE_TWO_ONE", "HUD_FRONTEND_DEFAULT_SOUNDSET", true);
StartNextWave();
}
void StopGame(bool cleanup)
{
_running = false;
_betweenWaves = false;
if (cleanup) CleanupEnemies();
}
void StartNextWave()
{
if (!_running) return;
_wave++;
if (_wave > MaxWaves)
{
Game.Player.Money += VictoryCash;
UI.Notification.Show("~g~Victory!~w~ You survived all " + MaxWaves + " waves! ~g~+$" + VictoryCash.ToString("N0"));
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, "ACHIEVEMENT_UNLOCKED", "HUD_AWARDS", true);
StopGame(cleanup: true);
return;
}
int targetCount = Math.Min(SimultaneousCap, BaseEnemies + (_wave - 1) * EnemiesPerWave);
SpawnWave(targetCount);
UI.Screen.ShowSubtitle("~y~Wave " + _wave + "/" + MaxWaves + "~w~ Enemies: ~r~" + targetCount, 5000);
_betweenWaves = false;
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, "OBJECTIVE_PRINT", "HUD_FRONTEND_DEFAULT_SOUNDSET", true);
}
void SpawnWave(int count)
{
CleanupDeadRefs();
Model chosen = ModelForEnemies();
if (!chosen.IsValid) return;
for (int i = 0; i < count; i++)
{
Vector3 spawnPos = SafeSpawnPosition(Game.Player.Character.Position, SpawnRadius);
if (spawnPos == Vector3.Zero) continue;
Ped ped = World.CreatePed(chosen, spawnPos);
if (ped == null || !ped.Exists()) continue;
if (ped.IsOnScreen || Function.Call<bool>(Hash.IS_SPHERE_VISIBLE, spawnPos.X, spawnPos.Y, spawnPos.Z, 2.0f))
{
Vector3 retry = SpawnPositionOutOfView(Game.Player.Character.Position, SpawnRadius + 15f);
if (retry != Vector3.Zero) ped.Position = retry;
}
SetupEnemy(ped);
_enemies.Add(ped);
}
Script.Wait(1000);
chosen.MarkAsNoLongerNeeded();
}
Model ModelForEnemies()
{
foreach (var name in EnemyModels)
{
var model = new Model(name);
if (!model.IsInCdImage) continue;
int attempts = 0;
while (!model.IsLoaded && attempts < 30)
{
model.Request();
Script.Yield();
attempts++;
}
if (model.IsLoaded) return model;
model.MarkAsNoLongerNeeded();
}
return new Model("s_m_m_marine_01");
}
void SetupEnemy(Ped p)
{
Function.Call(Hash.SET_PED_RELATIONSHIP_GROUP_HASH, p.Handle, (uint)_enemyGroupHash);
int baseHealth = 200;
int healthPerWave = 15;
int maxH = baseHealth + (_wave * healthPerWave);
Function.Call(Hash.SET_ENTITY_MAX_HEALTH, p.Handle, maxH);
p.MaxHealth = maxH;
p.Health = maxH;
int baseArmor = 40;
int armorPerWave = 10;
p.Armor = Math.Min(100, baseArmor + _wave * armorPerWave);
Function.Call(Hash.SET_PED_SUFFERS_CRITICAL_HITS, p.Handle, true);
p.Accuracy = (int)Math.Min(80, 20 + _wave * 5);
p.CanSwitchWeapons = true;
p.CanWrithe = false;
var side = Sidearms[Game.Random.Next(Sidearms.Length)];
var primary = Primaries[Game.Random.Next(Primaries.Length)];
p.Weapons.RemoveAll();
p.Weapons.Give(side, 90, false, true);
p.Weapons.Give(primary, 999, true, true);
if (_wave >= 6 && Game.Random.NextDouble() < 0.35)
{
p.Weapons.Give(WeaponHash.CarbineRifle, 999, true, true);
}
p.Task.ClearAllImmediately();
Script.Yield();
p.Task.Combat(Game.Player.Character);
p.KeepTaskWhenMarkedAsNoLongerNeeded = true;
p.Position += new Vector3(Game.Random.Next(-3, 4), Game.Random.Next(-3, 4), 0f);
RandomizePedAppearance(p);
Blip b = p.AddBlip();
if (b != null)
{
b.Sprite = BlipSprite.Enemy;
b.Color = BlipColor.Red;
b.Scale = 0.7f;
b.IsShortRange = false;
_enemyBlips[p] = b;
}
}
void RandomizePedAppearance(Ped p)
{
int hatCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_PROP_DRAWABLE_VARIATIONS, p.Handle, 0);
if (hatCount > 0 && Game.Random.NextDouble() < 0.6)
{
int chosen = Game.Random.Next(hatCount);
Function.Call(Hash.SET_PED_PROP_INDEX, p.Handle, 0, chosen, 0, true);
}
int glassesCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_PROP_DRAWABLE_VARIATIONS, p.Handle, 1);
if (glassesCount > 0 && Game.Random.NextDouble() < 0.3)
{
int chosen = Game.Random.Next(glassesCount);
Function.Call(Hash.SET_PED_PROP_INDEX, p.Handle, 1, chosen, 0, true);
}
for (int slot = 0; slot <= 8; slot++)
{
int drawableCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_DRAWABLE_VARIATIONS, p.Handle, slot);
if (drawableCount > 0)
{
int chosenDrawable = Game.Random.Next(drawableCount);
int textureCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_TEXTURE_VARIATIONS, p.Handle, slot, chosenDrawable);
int chosenTexture = textureCount > 0 ? Game.Random.Next(textureCount) : 0;
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, p.Handle, slot, chosenDrawable, chosenTexture, 0);
}
}
}
Vector3 SpawnPositionOutOfView(Vector3 center, float radius)
{
Vector3 camPos = GameplayCamera.Position;
Vector3 camDir = GameplayCamera.Direction;
for (int i = 0; i < 15; i++)
{
float angle = (float)(Math.PI + (Game.Random.NextDouble() * (Math.PI * 0.8) - Math.PI * 0.4));
Vector3 offset = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0f) * (float)(Game.Random.NextDouble() * radius * 0.8 + radius * 0.2);
Vector3 candidate = center + offset;
Vector3 onStreet = World.GetNextPositionOnStreet(candidate);
if (onStreet == Vector3.Zero || onStreet.DistanceTo(center) < 15f) continue;
if (Function.Call<bool>(Hash.IS_SPHERE_VISIBLE, onStreet.X, onStreet.Y, onStreet.Z, 2.0f)) continue;
Vector3 toCand = (onStreet - camPos).Normalized;
double dot = Vector3.Dot(camDir, toCand);
if (dot > -0.2) continue;
return onStreet;
}
Vector3 back = Game.Player.Character.ForwardVector * -1f * (radius * 0.9f);
Vector3 fallback = center + back;
Vector3 result = World.GetNextPositionOnStreet(fallback);
return result == Vector3.Zero ? center + new Vector3(0, -radius, 0) : result;
}
Vector3 SafeSpawnPosition(Vector3 center, float radius)
{
for (int i = 0; i < 10; i++)
{
float angle = (float)(Game.Random.NextDouble() * Math.PI * 2.0);
float distance = (float)(Game.Random.NextDouble() * radius);
Vector3 offset = new Vector3((float)Math.Cos(angle), (float)Math.Sin(angle), 0f) * distance;
Vector3 candidate = center + offset;
Vector3 onStreet = World.GetNextPositionOnStreet(candidate);
if (onStreet == Vector3.Zero) continue;
if (onStreet.DistanceTo(center) < 10f) continue;
if (!Function.Call<bool>(Hash.IS_SPHERE_VISIBLE, onStreet.X, onStreet.Y, onStreet.Z, 2.0f))
{
return onStreet;
}
}
return SpawnPositionOutOfView(center, radius);
}
void TryGiveBetweenWaveGoodies()
{
var me = Game.Player.Character;
if (!me.Exists()) return;
me.Armor = Math.Min(100, me.Armor + 35);
me.Health = Math.Min(me.MaxHealth, me.Health + 35);
var commonGuns = new[] { WeaponHash.CarbineRifle, WeaponHash.SMG, WeaponHash.PumpShotgun, WeaponHash.Pistol };
foreach (var w in commonGuns)
{
if (me.Weapons.HasWeapon(w) && me.Weapons[w].Ammo < 120)
{
me.Weapons.Give(w, 120 - me.Weapons[w].Ammo, false, false);
}
}
}
void CleanupDeadRefs()
{
for (int i = _enemies.Count - 1; i >= 0; i--)
{
Ped p = _enemies[i];
if (p == null || !p.Exists() || p.IsDead)
{
RemoveBlipFor(p);
if (p.Exists()) p.MarkAsNoLongerNeeded();
_enemies.RemoveAt(i);
}
}
}
void CleanupEnemies()
{
foreach (var ped in _enemies)
{
if (ped != null && ped.Exists())
{
RemoveBlipFor(ped);
ped.Delete();
}
}
_enemies.Clear();
_enemyBlips.Clear();
}
void RemoveBlipFor(Ped ped)
{
if (ped == null || !_enemyBlips.ContainsKey(ped)) return;
Blip blip = _enemyBlips[ped];
if (blip.Exists()) blip.Delete();
_enemyBlips.Remove(ped);
}
string ItemLine(int idx, string text)
{
return (idx == _menuIndex ? "~g~> " + text : " " + text);
}
void LoadSettings()
{
try
{
var cfg = ScriptSettings.Load(SettingsPath);
MaxWaves = cfg.GetValue("General", "MaxWaves", MaxWaves);
BaseEnemies = cfg.GetValue("General", "BaseEnemies", BaseEnemies);
EnemiesPerWave = cfg.GetValue("General", "EnemiesPerWave", EnemiesPerWave);
SimultaneousCap = cfg.GetValue("General", "SimultaneousCap", SimultaneousCap);
SpawnRadius = cfg.GetValue("General", "SpawnRadius", SpawnRadius);
VictoryCash = cfg.GetValue("General", "VictoryCash", VictoryCash);
}
catch (Exception ex)
{
UI.Notification.Show("~r~WaveSurvival: Failed to load settings.~w~ Using defaults.\n" + ex.Message);
}
}
void SaveSettings()
{
try
{
var cfg = ScriptSettings.Load(SettingsPath);
cfg.SetValue("General", "MaxWaves", MaxWaves);
cfg.SetValue("General", "BaseEnemies", BaseEnemies);
cfg.SetValue("General", "EnemiesPerWave", EnemiesPerWave);
cfg.SetValue("General", "SimultaneousCap", SimultaneousCap);
cfg.SetValue("General", "SpawnRadius", SpawnRadius);
cfg.SetValue("General", "VictoryCash", VictoryCash);
cfg.Save();
UI.Notification.Show("~g~WaveSurvival: Settings saved.");
}
catch (Exception ex)
{
UI.Notification.Show("~r~WaveSurvival: Failed to save settings.\n" + ex.Message);
}
}
}
// KoreaBank's Wave-Based Survival Mod V1.0
// Fully compatible with ScriptHookVDotNet 3.7.0
// No errors, no warnings, ready to run.
using System;
using System.Collections.Generic;
using GTA;
using GTA.Math;
using GTA.Native;
using System.Windows.Forms;
public class WaveSurvival : Script
{
// ---- Overlay menu state ----
bool _menuOpen = false;
int _menuIndex = 0;
string[] _menuItems = { "MaxWaves", "BaseEnemies", "EnemiesPerWave", "SimultaneousCap", "SpawnRadius", "VictoryCash" };
readonly Keys ToggleMenuKey = Keys.F3; // open/close menu
// ====== CONFIG ======
int MaxWaves = 10; // configurable
float SpawnRadius = 45f; // around player
int BaseEnemies = 5; // wave 1 count
int EnemiesPerWave = 2; // added each wave
int SimultaneousCap = 14; // safety cap
int VictoryCash = 50000; // reward for clearing all waves
// Settings file path
readonly string SettingsPath = @"scripts\\WaveSurvival.ini";
// Track enemy blips so we can delete them
readonly Dictionary<Ped, Blip> _enemyBlips = new Dictionary<Ped, Blip>();
// Enemy models (Merryweather/paramilitary vibe)
readonly string[] EnemyModels =
{
"s_m_y_blackops_01",
"s_m_y_blackops_02",
"s_m_y_blackops_03",
"csb_mweather"
};
// Weapon tiers
readonly WeaponHash[] PrimariesCommon =
{
WeaponHash.CarbineRifle,
WeaponHash.SMG,
WeaponHash.AssaultSMG,
WeaponHash.PumpShotgun,
WeaponHash.SawnOffShotgun,
WeaponHash.CombatPDW,
WeaponHash.MicroSMG,
WeaponHash.AssaultRifle
};
readonly WeaponHash[] PrimariesAdvanced =
{
WeaponHash.AssaultShotgun,
WeaponHash.BullpupShotgun,
WeaponHash.MG,
WeaponHash.HeavyShotgun,
WeaponHash.SpecialCarbine
};
readonly WeaponHash[] PrimariesElite =
{
WeaponHash.CarbineRifleMk2,
WeaponHash.CompactGrenadeLauncher,
WeaponHash.HeavySniper,
WeaponHash.RPG,
WeaponHash.Railgun
};
readonly WeaponHash[] Sidearms =
{
WeaponHash.Pistol,
WeaponHash.CombatPistol,
WeaponHash.HeavyPistol,
WeaponHash.Revolver,
WeaponHash.SNSPistol,
WeaponHash.MarksmanPistol
};
int _wave = 0;
bool _running = false;
bool _betweenWaves = false;
DateTime _nextWaveAt = DateTime.MinValue;
readonly List<Ped> _enemies = new List<Ped>();
readonly Random _rng = new Random();
public WaveSurvival()
{
Tick += OnTick;
KeyDown += OnKeyDown;
Interval = 10;
LoadSettings();
GTA.UI.Notification.Show("Wave Survival: ~y~F7~w~ start / ~y~F8~w~ stop / ~y~F3~w~ config menu");
}
void OnKeyDown(object sender, KeyEventArgs e)
{
// Toggle the config menu
if (e.KeyCode == ToggleMenuKey)
{
_menuOpen = !_menuOpen;
if (!_menuOpen) SaveSettings(); // auto-save when closing
return;
}
// If menu is open, handle navigation and edits
if (_menuOpen)
{
if (e.KeyCode == Keys.Up) _menuIndex = (_menuIndex - 1 + _menuItems.Length) % _menuItems.Length;
if (e.KeyCode == Keys.Down) _menuIndex = (_menuIndex + 1) % _menuItems.Length;
int stepI = ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) == Keys.Shift ? 5 : 1);
float stepF = ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) == Keys.Shift ? 5f : 1f);
if (e.KeyCode == Keys.Left)
{
switch (_menuItems[_menuIndex])
{
case "MaxWaves": MaxWaves = Math.Max(1, MaxWaves - stepI); break;
case "BaseEnemies": BaseEnemies = Math.Max(1, BaseEnemies - stepI); break;
case "EnemiesPerWave": EnemiesPerWave = Math.Max(1, EnemiesPerWave - stepI); break;
case "SimultaneousCap": SimultaneousCap = Math.Max(1, SimultaneousCap - stepI); break;
case "SpawnRadius": SpawnRadius = Math.Max(10f, SpawnRadius - stepF); break;
case "VictoryCash": VictoryCash = Math.Max(0, VictoryCash - stepI * 1000); break;
}
}
else if (e.KeyCode == Keys.Right)
{
switch (_menuItems[_menuIndex])
{
case "MaxWaves": MaxWaves += stepI; break;
case "BaseEnemies": BaseEnemies += stepI; break;
case "EnemiesPerWave": EnemiesPerWave += stepI; break;
case "SimultaneousCap": SimultaneousCap += stepI; break;
case "SpawnRadius": SpawnRadius += stepF; break;
case "VictoryCash": VictoryCash += stepI * 1000; break;
}
}
return;
}
// Start/stop keys
if (e.KeyCode == Keys.F7 && !_running)
{
StartGame();
}
else if (e.KeyCode == Keys.F8)
{
StopGame(cleanup: true);
GTA.UI.Notification.Show("Wave Survival: ~r~Stopped.");
}
}
void StartGame()
{
_running = true;
_wave = 0;
_betweenWaves = false;
CleanupEnemies();
Game.Player.Character.Armor = 100;
Game.Player.Character.Health = Game.Player.Character.MaxHealth;
GTA.UI.Notification.Show("Wave Survival: ~g~Started!~w~ Survive ~y~" + MaxWaves + "~w~ waves.");
StartNextWave();
}
void StopGame(bool cleanup)
{
_running = false;
_betweenWaves = false;
if (cleanup) CleanupEnemies();
}
void StartNextWave()
{
if (!_running) return;
_wave++;
if (_wave > MaxWaves)
{
Game.Player.Money += VictoryCash;
GTA.UI.Notification.Show("~g~Victory!~w~ You survived all " + MaxWaves + " waves! ~g~+$" + VictoryCash.ToString("N0"));
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, "PICK_UP", "HUD_FRONTEND_DEFAULT_SOUNDSET", true);
StopGame(cleanup: true);
return;
}
int targetCount = Math.Min(SimultaneousCap, BaseEnemies + (_wave - 1) * EnemiesPerWave);
SpawnWave(targetCount);
GTA.UI.Screen.ShowSubtitle("~y~Wave " + _wave + "/" + MaxWaves + "~w~ Enemies: ~r~" + targetCount, 5000);
_betweenWaves = false;
}
void SpawnWave(int count)
{
CleanupDeadRefs();
Model chosen = ModelForEnemies();
if (!chosen.IsInCdImage || !chosen.IsValid) return;
for (int i = 0; i < count; i++)
{
Vector3 spawnPos = SafeSpawnPosition(Game.Player.Character.Position, SpawnRadius);
Ped ped = World.CreatePed(chosen, spawnPos);
if (ped == null || !ped.Exists()) continue;
SetupEnemy(ped);
if (ped.IsOnScreen)
{
Vector3 retry = SpawnPositionOutOfView(Game.Player.Character.Position, SpawnRadius + 10f);
if (retry != Vector3.Zero) ped.Position = retry;
}
_enemies.Add(ped);
}
chosen.MarkAsNoLongerNeeded();
}
Model ModelForEnemies()
{
foreach (var name in EnemyModels)
{
var model = new Model(name);
if (model.IsInCdImage && model.Request(300))
return model;
model.MarkAsNoLongerNeeded();
}
var fallback = new Model("s_m_m_marine_01");
if (fallback.IsInCdImage && fallback.Request(300)) return fallback;
return new Model();
}
void SetupEnemy(Ped p)
{
// Relationship group setup
int armyHash = Function.Call<int>(Hash.GET_HASH_KEY, "ARMY");
int playerHash = Function.Call<int>(Hash.GET_HASH_KEY, "PLAYER");
Function.Call(Hash.SET_PED_RELATIONSHIP_GROUP_HASH, p.Handle, (uint)armyHash);
Function.Call(Hash.SET_RELATIONSHIP_BETWEEN_GROUPS, 5, (uint)armyHash, (uint)playerHash);
Function.Call(Hash.SET_RELATIONSHIP_BETWEEN_GROUPS, 5, (uint)playerHash, (uint)armyHash);
// Health & Armor scaling
int baseHealth = 200;
int healthPerWave = 15;
int maxH = baseHealth + (_wave * healthPerWave);
Function.Call(Hash.SET_ENTITY_MAX_HEALTH, p.Handle, maxH);
p.MaxHealth = maxH;
p.Health = maxH;
int baseArmor = 40;
int armorPerWave = 10;
p.Armor = Math.Min(100, baseArmor + _wave * armorPerWave);
Function.Call(Hash.SET_PED_SUFFERS_CRITICAL_HITS, p.Handle, true);
p.Accuracy = (int)Math.Min(80, 20 + _wave * 5);
p.CanSwitchWeapons = true;
p.CanWrithe = false;
// Weapons: Sidearm + Tiered Primary
var side = Sidearms[_rng.Next(Sidearms.Length)];
p.Weapons.RemoveAll();
p.Weapons.Give(side, 90, false, true);
WeaponHash primary;
if (_wave >= 8 && _rng.NextDouble() < 0.3)
{
primary = PrimariesElite[_rng.Next(PrimariesElite.Length)];
}
else if (_wave >= 5 && _rng.NextDouble() < 0.4)
{
primary = PrimariesAdvanced[_rng.Next(PrimariesAdvanced.Length)];
}
else
{
primary = PrimariesCommon[_rng.Next(PrimariesCommon.Length)];
}
p.Weapons.Give(primary, 999, true, true);
// Bonus weapon in later waves
if (_wave >= 6 && _rng.NextDouble() < 0.3)
{
var backup = PrimariesCommon[_rng.Next(PrimariesCommon.Length)];
if (backup != primary)
{
p.Weapons.Give(backup, 999, false, true);
}
}
p.Weapons.Select(primary);
// Tasking
p.Task.ClearAllImmediately();
p.Task.Combat(Game.Player.Character);
p.KeepTaskWhenMarkedAsNoLongerNeeded = true;
// Spread
p.Position += new Vector3(_rng.Next(-2, 3), _rng.Next(-2, 3), 0f);
// Appearance
int hatCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_PROP_DRAWABLE_VARIATIONS, p.Handle, 0);
if (hatCount > 0 && _rng.NextDouble() < 0.6)
{
int chosenHat = _rng.Next(hatCount);
Function.Call(Hash.SET_PED_PROP_INDEX, p.Handle, 0, chosenHat, 0, true);
}
int glassesCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_PROP_DRAWABLE_VARIATIONS, p.Handle, 1);
if (glassesCount > 0 && _rng.NextDouble() < 0.3)
{
int chosenGlasses = _rng.Next(glassesCount);
Function.Call(Hash.SET_PED_PROP_INDEX, p.Handle, 1, chosenGlasses, 0, true);
}
for (int slot = 0; slot <= 8; slot++)
{
int drawableCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_DRAWABLE_VARIATIONS, p.Handle, slot);
if (drawableCount > 0)
{
int chosenDrawable = _rng.Next(drawableCount);
int textureCount = Function.Call<int>(Hash.GET_NUMBER_OF_PED_TEXTURE_VARIATIONS, p.Handle, slot, chosenDrawable);
int chosenTexture = textureCount > 0 ? _rng.Next(textureCount) : 0;
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, p.Handle, slot, chosenDrawable, chosenTexture, 0);
}
}
// Blip
Blip b = p.AddBlip();
if (b != null)
{
b.Sprite = BlipSprite.Enemy;
b.Color = BlipColor.Red;
b.Scale = 0.7f;
_enemyBlips[p] = b;
}
}
Vector3 SpawnPositionOutOfView(Vector3 center, float radius)
{
Vector3 camPos = GameplayCamera.Position;
Vector3 camDir = GameplayCamera.Direction;
for (int i = 0; i < 12; i++)
{
float ang = (float)(Math.PI + (_rng.NextDouble() * (Math.PI * 0.66) - Math.PI * 0.33));
Vector3 offset = new Vector3((float)Math.Cos(ang), (float)Math.Sin(ang), 0f) * radius;
Vector3 candidate = center + offset;
Vector3 onStreet = World.GetNextPositionOnStreet(candidate);
if (onStreet.DistanceTo(Game.Player.Character.Position) < 20f) continue;
bool inFrustum = Function.Call<bool>(Hash.IS_SPHERE_VISIBLE, onStreet.X, onStreet.Y, onStreet.Z, 1.5f);
if (inFrustum) continue;
Vector3 toCand = onStreet - camPos;
if (toCand.Length() < 1f) continue;
toCand.Normalize();
double dot = camDir.X * toCand.X + camDir.Y * toCand.Y + camDir.Z * toCand.Z;
if (dot > 0.0) continue;
return onStreet;
}
Vector3 back = Game.Player.Character.ForwardVector * -1f;
Vector3 fallback = center + back * (radius * 0.9f);
return World.GetNextPositionOnStreet(fallback);
}
Vector3 SafeSpawnPosition(Vector3 center, float radius)
{
return SpawnPositionOutOfView(center, radius);
}
void OnTick(object sender, EventArgs e)
{
if (_menuOpen)
{
string menuText =
"~y~Wave Survival Config~w~ (↑/↓ select, ←/→ change, Shift = x5)\n" +
ItemLine(0, "MaxWaves: " + MaxWaves) + "\n" +
ItemLine(1, "BaseEnemies: " + BaseEnemies) + "\n" +
ItemLine(2, "EnemiesPerWave: " + EnemiesPerWave) + "\n" +
ItemLine(3, "SimultaneousCap: " + SimultaneousCap) + "\n" +
ItemLine(4, "SpawnRadius: " + ((int)SpawnRadius).ToString()) + "\n" +
ItemLine(5, "VictoryCash: $" + VictoryCash.ToString("N0")) + "\n" +
"Press ~y~F3~w~ to " + (_menuOpen ? "close" : "open");
GTA.UI.Screen.ShowSubtitle(menuText, 250);
}
if (!_running) return;
if (!Game.Player.Character.Exists() || Game.Player.Character.IsDead)
{
GTA.UI.Notification.Show("~r~You died.~w~ Run ended.");
StopGame(cleanup: true);
return;
}
CleanupDeadRefs();
if (!_betweenWaves && _enemies.Count == 0)
{
_betweenWaves = true;
_nextWaveAt = DateTime.UtcNow.AddSeconds(6);
TryGiveBetweenWaveGoodies();
GTA.UI.Screen.ShowSubtitle("~g~Wave cleared!~w~ Next wave in 6s.", 3000);
}
if (_betweenWaves && DateTime.UtcNow >= _nextWaveAt)
{
StartNextWave();
}
}
void TryGiveBetweenWaveGoodies()
{
var me = Game.Player.Character;
if (!me.Exists()) return;
me.Armor = Math.Min(100, me.Armor + 35);
me.Health = Math.Min(me.MaxHealth, me.Health + 35);
var commonGuns = new[] { WeaponHash.CarbineRifle, WeaponHash.SMG, WeaponHash.PumpShotgun, WeaponHash.Pistol };
foreach (var w in commonGuns)
{
if (me.Weapons.HasWeapon(w))
{
var weap = me.Weapons[w];
if (weap != null && weap.Ammo < 120)
{
me.Weapons.Give(w, 120, false, false);
}
}
}
}
void CleanupDeadRefs()
{
for (int i = _enemies.Count - 1; i >= 0; i--)
{
Ped ped = _enemies[i];
if (ped == null || !ped.Exists())
{
RemoveBlipFor(ped);
_enemies.RemoveAt(i);
continue;
}
if (ped.IsDead)
{
RemoveBlipFor(ped);
ped.MarkAsNoLongerNeeded();
_enemies.RemoveAt(i);
}
}
}
void CleanupEnemies()
{
foreach (var ped in _enemies)
{
if (ped == null) continue;
try { if (ped.Exists()) ped.Delete(); } catch {}
}
_enemies.Clear();
foreach (var kv in _enemyBlips)
{
try { if (kv.Value != null && kv.Value.Exists()) kv.Value.Delete(); } catch {}
}
_enemyBlips.Clear();
}
void RemoveBlipFor(Ped ped)
{
if (ped == null) return;
Blip b;
if (_enemyBlips.TryGetValue(ped, out b))
{
try { if (b != null && b.Exists()) b.Delete(); } catch {}
_enemyBlips.Remove(ped);
}
}
string ItemLine(int idx, string text)
{
return (idx == _menuIndex ? "~g~> " + text : " " + text);
}
void LoadSettings()
{
try
{
var cfg = ScriptSettings.Load(SettingsPath);
MaxWaves = cfg.GetValue("General", "MaxWaves", MaxWaves);
BaseEnemies = cfg.GetValue("General", "BaseEnemies", BaseEnemies);
EnemiesPerWave = cfg.GetValue("General", "EnemiesPerWave", EnemiesPerWave);
SimultaneousCap = cfg.GetValue("General", "SimultaneousCap", SimultaneousCap);
SpawnRadius = cfg.GetValue("General", "SpawnRadius", SpawnRadius);
VictoryCash = cfg.GetValue("General", "VictoryCash", VictoryCash);
}
catch (Exception ex)
{
GTA.UI.Notification.Show("~r~WaveSurvival: Failed to load settings.~w~ Using defaults.");
GTA.UI.Screen.ShowSubtitle(ex.Message, 3000);
}
}
void SaveSettings()
{
try
{
var cfg = ScriptSettings.Load(SettingsPath);
cfg.SetValue("General", "MaxWaves", MaxWaves);
cfg.SetValue("General", "BaseEnemies", BaseEnemies);
cfg.SetValue("General", "EnemiesPerWave", EnemiesPerWave);
cfg.SetValue("General", "SimultaneousCap", SimultaneousCap);
cfg.SetValue("General", "SpawnRadius", SpawnRadius);
cfg.SetValue("General", "VictoryCash", VictoryCash);
cfg.Save();
GTA.UI.Notification.Show("~g~WaveSurvival: Settings saved.");
}
catch (Exception ex)
{
GTA.UI.Notification.Show("~r~WaveSurvival: Failed to save settings.");
GTA.UI.Screen.ShowSubtitle(ex.Message, 3000);
}
}
}
this is work add weapons to EnemiesPerWave
A love AI
Looks good but the peds don't spawn s_m_y_blackops_02 and s_m_y_blackops_03. Only s_m_y_blackops_01 since it's the first line for the ped models used as enemies. Maybe add a fix for that so that we could add ped models
@SelfRayRayBusiness I'll look into it. Thanks.
Hello, I really like this mod. Can you possibly add a way to select different peds to battle. like different gangs or factions. thank you.
@scoomdoom Sure. I'll add the ballas peds next.
This crashes my game. F7 doesn't work. F3 is native Trainer and F4 just crashes my game. Also, could you make this work with LSPDFR? I'd love to call in cops while battling it out with terrorists.
@ryan1845 Not sure what's causing the game to crash for you. Were there any errors, or check ScriptHookVDotNet.log?
@koreabank nevermind. I got it working. But on LSPDFR, they all seem to run away. Is it possible you can make it compatible with a wanted level please?
@ryan1845 I can play with wanted levels raised completely fine, without them running but opening fire against the cops. Probably because I'm using the experimental 1.2 Version. The enemies are set to not attack the cops. Like they are in the same group of peds. That's changed in 1.2. I'll drop it after I got other things added and fixed.
@koreabank Okay. Thanks. i just want to call in backup while fighting them. In LSPDFR, i play as a cop. So that's probably why. But if you could, i'd like that. thanks
think someone already mentioned it but selecting the gangs or mercs you'd like to face would definitely be a +.