126 files piaciuti
12 commenti
0 video
0 caricamenti
0 seguaci
A love AI
this is work add weapons to EnemiesPerWave
// 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);
}
}
}
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);
}
}
}
thankxxxxxxxxxxxx
thanks bro please fix set dd mm yy in time sitting
edit map error add any ped and vehicles click swap in map