Caricamento...
126 files piaciuti
12 commenti
0 video
0 caricamenti
0 seguaci
  • Default

    A love AI

    24 agosto 2025
  • Default

    this is work add weapons to EnemiesPerWave

    24 agosto 2025
  • Default

    // 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);
    }
    }
    }

    Espandi per leggere l'intero commento
    24 agosto 2025
  • Default

    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);
    }
    }
    }

    Espandi per leggere l'intero commento
    24 agosto 2025
  • Default

    thankxxxxxxxxxxxx

    19 giugno 2024
  • Default

    thanks bro please fix set dd mm yy in time sitting

    14 maggio 2024
  • Default

    edit map error add any ped and vehicles click swap in map

    08 gennaio 2020