diff --git a/README.md b/README.md
index dfdc7b8..13166d6 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ A comprehensive GTA IV freeroam server for GTAConnected with multiple features i
- **World Control** - Weather and time manipulation
- **Enhanced Chat** - Private messages, local chat, actions, and OOC chat
- **Kill Tracking** - Statistics for kills, deaths, and K/D ratio
+- **Interactive Mod Menu** - Press F5 to access a full GUI menu with all features
## Installation
@@ -41,9 +42,13 @@ GTAConnected/
├── chat/ # Enhanced chat system
│ ├── meta.xml
│ └── server.js
- └── teleport/ # Teleportation system
+ ├── teleport/ # Teleportation system
+ │ ├── meta.xml
+ │ └── server.js
+ └── modmenu/ # Interactive GUI mod menu
├── meta.xml
- └── server.js
+ ├── server.js
+ └── client.js
```
## Commands
@@ -221,6 +226,71 @@ const admins = [
];
```
+## Mod Menu (F5)
+
+The server includes an interactive GUI mod menu accessible to all players by pressing **F5**.
+
+### Menu Controls
+
+| Key | Action |
+|-----|--------|
+| F5 | Open/Close Menu |
+| UP/DOWN | Navigate items |
+| ENTER | Select item |
+| BACKSPACE/ESC | Go back |
+| LEFT/RIGHT | Adjust values |
+
+### Menu Categories
+
+**Self Options**
+- Restore health/armor
+- Get all weapons
+- Clear wanted level
+- God Mode toggle
+- Infinite ammo
+- Super jump / Fast run
+- Change player skin
+
+**Vehicle Spawner**
+- Sports Cars (Infernus, Turismo, Comet, etc.)
+- Super Cars (Entity XF, Adder, Vacca, etc.)
+- Muscle Cars (Sabre GT, Dukes, Ruiner, etc.)
+- SUVs & Trucks
+- Motorcycles
+- Emergency Vehicles
+- Aircraft & Boats
+
+**Vehicle Options**
+- Repair / Flip / Clean vehicle
+- Change vehicle colors
+- Drift Mode toggle
+- Handling adjustments (Grip, Acceleration, Top Speed)
+- Indestructible vehicle
+- Nitro boost
+
+**Network Options**
+- View online players
+- Teleport to any player
+- Spectate players
+
+**Teleport Locations**
+- Quick teleport to 15+ Liberty City locations
+- All boroughs covered (Algonquin, Broker, Bohan, Alderney)
+
+**World Options**
+- Set time of day
+- Change weather
+
+**Weapons**
+- Get individual weapons
+- Get all weapons at once
+
+**Fun Options**
+- Launch yourself into the air
+- Spawn random peds
+- Ragdoll mode
+- Chaos mode
+
## Requirements
- GTAConnected Server (v1.0.72 or newer)
diff --git a/resources/modmenu/client.js b/resources/modmenu/client.js
new file mode 100644
index 0000000..324b804
--- /dev/null
+++ b/resources/modmenu/client.js
@@ -0,0 +1,929 @@
+// ============================================================================
+// MOD MENU - Client Side
+// Interactive GUI menu for all players
+// Press F5 to open/close menu, Arrow keys to navigate, Enter to select
+// ============================================================================
+
+// Menu state
+let menuOpen = false;
+let currentMenu = "main";
+let selectedIndex = 0;
+let menuStack = []; // For back navigation
+
+// Menu colors
+const colors = {
+ background: toColour(20, 20, 20, 200),
+ header: toColour(200, 50, 50, 255),
+ headerText: toColour(255, 255, 255, 255),
+ itemBg: toColour(40, 40, 40, 200),
+ itemBgSelected: toColour(200, 50, 50, 220),
+ itemText: toColour(255, 255, 255, 255),
+ itemTextSelected: toColour(255, 255, 255, 255),
+ footer: toColour(30, 30, 30, 200),
+ footerText: toColour(180, 180, 180, 255),
+ subText: toColour(150, 150, 150, 255)
+};
+
+// Menu dimensions
+const menu = {
+ x: 50,
+ y: 100,
+ width: 300,
+ headerHeight: 40,
+ itemHeight: 35,
+ footerHeight: 30,
+ maxVisibleItems: 12
+};
+
+// Scroll offset for long menus
+let scrollOffset = 0;
+
+// Player list cache
+let playerList = [];
+let lastPlayerUpdate = 0;
+
+// Vehicle handling values
+let handlingMods = {
+ grip: 1.0,
+ acceleration: 1.0,
+ topSpeed: 1.0,
+ braking: 1.0,
+ driftMode: false
+};
+
+// ============================================================================
+// MENU DATA STRUCTURE
+// ============================================================================
+
+const menuData = {
+ main: {
+ title: "MOD MENU",
+ items: [
+ { label: "Self Options", action: "submenu", target: "self" },
+ { label: "Vehicle Spawner", action: "submenu", target: "vehicles" },
+ { label: "Vehicle Options", action: "submenu", target: "vehicleOptions" },
+ { label: "Network Options", action: "submenu", target: "network" },
+ { label: "Teleport Locations", action: "submenu", target: "teleport" },
+ { label: "World Options", action: "submenu", target: "world" },
+ { label: "Weapons", action: "submenu", target: "weapons" },
+ { label: "Fun Options", action: "submenu", target: "fun" }
+ ]
+ },
+
+ self: {
+ title: "SELF OPTIONS",
+ items: [
+ { label: "Restore Health", action: "self_health" },
+ { label: "Restore Armor", action: "self_armor" },
+ { label: "Max Health & Armor", action: "self_max" },
+ { label: "Give All Weapons", action: "self_weapons" },
+ { label: "Clear Wanted Level", action: "self_wanted" },
+ { label: "Never Wanted", action: "toggle", target: "neverWanted", state: false },
+ { label: "God Mode", action: "toggle", target: "godMode", state: false },
+ { label: "Infinite Ammo", action: "toggle", target: "infiniteAmmo", state: false },
+ { label: "Super Jump", action: "toggle", target: "superJump", state: false },
+ { label: "Fast Run", action: "toggle", target: "fastRun", state: false },
+ { label: "Respawn", action: "self_respawn" },
+ { label: "Suicide", action: "self_suicide" },
+ { label: "Change Skin", action: "submenu", target: "skins" }
+ ]
+ },
+
+ skins: {
+ title: "PLAYER SKINS",
+ items: [
+ { label: "Niko Bellic", action: "skin", value: -1667301416 },
+ { label: "Roman Bellic", action: "skin", value: -163448165 },
+ { label: "Little Jacob", action: "skin", value: 1936355839 },
+ { label: "Brucie Kibbutz", action: "skin", value: -1938475496 },
+ { label: "Playboy X", action: "skin", value: 970234525 },
+ { label: "Johnny Klebitz", action: "skin", value: -1784875845 },
+ { label: "Luis Lopez", action: "skin", value: -1403507487 },
+ { label: "Police Officer", action: "skin", value: -1320879687 },
+ { label: "NOOSE", action: "skin", value: -1306011498 },
+ { label: "Paramedic", action: "skin", value: 2136829318 },
+ { label: "Firefighter", action: "skin", value: 1616659040 },
+ { label: "Business Man", action: "skin", value: -268651930 },
+ { label: "Hobo", action: "skin", value: 1943617350 },
+ { label: "Random Skin", action: "skin_random" }
+ ]
+ },
+
+ vehicles: {
+ title: "VEHICLE SPAWNER",
+ items: [
+ { label: "Sports Cars", action: "submenu", target: "veh_sports" },
+ { label: "Super Cars", action: "submenu", target: "veh_super" },
+ { label: "Muscle Cars", action: "submenu", target: "veh_muscle" },
+ { label: "SUVs & Trucks", action: "submenu", target: "veh_suv" },
+ { label: "Sedans & Compacts", action: "submenu", target: "veh_sedan" },
+ { label: "Motorcycles", action: "submenu", target: "veh_bikes" },
+ { label: "Emergency Vehicles", action: "submenu", target: "veh_emergency" },
+ { label: "Aircraft", action: "submenu", target: "veh_aircraft" },
+ { label: "Boats", action: "submenu", target: "veh_boats" },
+ { label: "Special Vehicles", action: "submenu", target: "veh_special" },
+ { label: "Delete My Vehicles", action: "vehicle_delete" }
+ ]
+ },
+
+ veh_sports: {
+ title: "SPORTS CARS",
+ items: [
+ { label: "Infernus", action: "spawn_vehicle", value: "infernus" },
+ { label: "Turismo", action: "spawn_vehicle", value: "turismo" },
+ { label: "Comet", action: "spawn_vehicle", value: "comet" },
+ { label: "Banshee", action: "spawn_vehicle", value: "banshee" },
+ { label: "Sultan", action: "spawn_vehicle", value: "sultan" },
+ { label: "Coquette", action: "spawn_vehicle", value: "coquette" },
+ { label: "Feltzer", action: "spawn_vehicle", value: "feltzer" },
+ { label: "F620", action: "spawn_vehicle", value: "f620" },
+ { label: "Buffalo", action: "spawn_vehicle", value: "buffalo" }
+ ]
+ },
+
+ veh_super: {
+ title: "SUPER CARS",
+ items: [
+ { label: "Entity XF", action: "spawn_vehicle", value: "entityxf" },
+ { label: "Adder", action: "spawn_vehicle", value: "adder" },
+ { label: "Vacca", action: "spawn_vehicle", value: "vacca" },
+ { label: "Bullet", action: "spawn_vehicle", value: "bullet" },
+ { label: "Cheetah", action: "spawn_vehicle", value: "cheetah" }
+ ]
+ },
+
+ veh_muscle: {
+ title: "MUSCLE CARS",
+ items: [
+ { label: "Sabre GT", action: "spawn_vehicle", value: "sabregt" },
+ { label: "Stallion", action: "spawn_vehicle", value: "stalion" },
+ { label: "Vigero", action: "spawn_vehicle", value: "vigero" },
+ { label: "Dukes", action: "spawn_vehicle", value: "dukes" },
+ { label: "Ruiner", action: "spawn_vehicle", value: "ruiner" },
+ { label: "Phoenix", action: "spawn_vehicle", value: "phoenix" },
+ { label: "Gauntlet", action: "spawn_vehicle", value: "gauntlet" },
+ { label: "Dominator", action: "spawn_vehicle", value: "dominator" }
+ ]
+ },
+
+ veh_suv: {
+ title: "SUVs & TRUCKS",
+ items: [
+ { label: "Patriot", action: "spawn_vehicle", value: "patriot" },
+ { label: "Cavalcade", action: "spawn_vehicle", value: "cavalcade" },
+ { label: "Granger", action: "spawn_vehicle", value: "granger" },
+ { label: "Huntley", action: "spawn_vehicle", value: "huntley" },
+ { label: "Landstalker", action: "spawn_vehicle", value: "landstalker" },
+ { label: "Habanero", action: "spawn_vehicle", value: "habanero" },
+ { label: "Serrano", action: "spawn_vehicle", value: "serrano" },
+ { label: "Rebla", action: "spawn_vehicle", value: "rebla" }
+ ]
+ },
+
+ veh_sedan: {
+ title: "SEDANS & COMPACTS",
+ items: [
+ { label: "Oracle", action: "spawn_vehicle", value: "oracle" },
+ { label: "Schafter", action: "spawn_vehicle", value: "schafter" },
+ { label: "Admiral", action: "spawn_vehicle", value: "admiral" },
+ { label: "Vincent", action: "spawn_vehicle", value: "vincent" },
+ { label: "Presidente", action: "spawn_vehicle", value: "presidente" },
+ { label: "Cognoscenti", action: "spawn_vehicle", value: "cognoscenti" },
+ { label: "Blista", action: "spawn_vehicle", value: "blista" },
+ { label: "Premier", action: "spawn_vehicle", value: "premier" }
+ ]
+ },
+
+ veh_bikes: {
+ title: "MOTORCYCLES",
+ items: [
+ { label: "NRG 900", action: "spawn_vehicle", value: "nrg900" },
+ { label: "PCJ 600", action: "spawn_vehicle", value: "pcj600" },
+ { label: "Sanchez", action: "spawn_vehicle", value: "sanchez" },
+ { label: "Faggio", action: "spawn_vehicle", value: "faggio" },
+ { label: "Bati", action: "spawn_vehicle", value: "bati" },
+ { label: "Akuma", action: "spawn_vehicle", value: "akuma" },
+ { label: "Double T", action: "spawn_vehicle", value: "double" },
+ { label: "Hakuchou", action: "spawn_vehicle", value: "hakuchou" },
+ { label: "Hexer", action: "spawn_vehicle", value: "hexer" },
+ { label: "Daemon", action: "spawn_vehicle", value: "daemon" }
+ ]
+ },
+
+ veh_emergency: {
+ title: "EMERGENCY VEHICLES",
+ items: [
+ { label: "Police Cruiser", action: "spawn_vehicle", value: "police" },
+ { label: "Police Buffalo", action: "spawn_vehicle", value: "police2" },
+ { label: "FBI Car", action: "spawn_vehicle", value: "fbi" },
+ { label: "NOOSE Cruiser", action: "spawn_vehicle", value: "noose" },
+ { label: "Ambulance", action: "spawn_vehicle", value: "ambulance" },
+ { label: "Fire Truck", action: "spawn_vehicle", value: "firetruk" },
+ { label: "Enforcer (SWAT)", action: "spawn_vehicle", value: "enforcer" }
+ ]
+ },
+
+ veh_aircraft: {
+ title: "AIRCRAFT",
+ items: [
+ { label: "Annihilator", action: "spawn_vehicle", value: "annihilator" },
+ { label: "Maverick", action: "spawn_vehicle", value: "maverick" },
+ { label: "Police Maverick", action: "spawn_vehicle", value: "polmav" },
+ { label: "Buzzard", action: "spawn_vehicle", value: "buzzard" },
+ { label: "Shamal (Jet)", action: "spawn_vehicle", value: "shamal" }
+ ]
+ },
+
+ veh_boats: {
+ title: "BOATS",
+ items: [
+ { label: "Jetmax", action: "spawn_vehicle", value: "jetmax" },
+ { label: "Marquis", action: "spawn_vehicle", value: "marquis" },
+ { label: "Predator", action: "spawn_vehicle", value: "predator" },
+ { label: "Tropic", action: "spawn_vehicle", value: "tropic" },
+ { label: "Dinghy", action: "spawn_vehicle", value: "dinghy" },
+ { label: "Squalo", action: "spawn_vehicle", value: "squalo" },
+ { label: "Reefer", action: "spawn_vehicle", value: "reefer" }
+ ]
+ },
+
+ veh_special: {
+ title: "SPECIAL VEHICLES",
+ items: [
+ { label: "Taxi", action: "spawn_vehicle", value: "taxi" },
+ { label: "Stretch Limo", action: "spawn_vehicle", value: "stretch" },
+ { label: "Bus", action: "spawn_vehicle", value: "bus" },
+ { label: "Trashmaster", action: "spawn_vehicle", value: "trashmaster" },
+ { label: "Forklift", action: "spawn_vehicle", value: "forklift" },
+ { label: "Caddy", action: "spawn_vehicle", value: "caddy" },
+ { label: "Bulldozer", action: "spawn_vehicle", value: "bulldozer" },
+ { label: "Phantom", action: "spawn_vehicle", value: "phantom" }
+ ]
+ },
+
+ vehicleOptions: {
+ title: "VEHICLE OPTIONS",
+ items: [
+ { label: "Repair Vehicle", action: "veh_repair" },
+ { label: "Flip Vehicle", action: "veh_flip" },
+ { label: "Clean Vehicle", action: "veh_clean" },
+ { label: "Max Upgrade", action: "veh_upgrade" },
+ { label: "Vehicle Colors", action: "submenu", target: "veh_colors" },
+ { label: "--- Handling ---", action: "none" },
+ { label: "Drift Mode", action: "toggle", target: "driftMode", state: false },
+ { label: "Grip +", action: "handling", target: "grip", delta: 0.2 },
+ { label: "Grip -", action: "handling", target: "grip", delta: -0.2 },
+ { label: "Acceleration +", action: "handling", target: "acceleration", delta: 0.5 },
+ { label: "Acceleration -", action: "handling", target: "acceleration", delta: -0.5 },
+ { label: "Top Speed +", action: "handling", target: "topSpeed", delta: 0.3 },
+ { label: "Top Speed -", action: "handling", target: "topSpeed", delta: -0.3 },
+ { label: "Reset Handling", action: "handling_reset" },
+ { label: "--- Special ---", action: "none" },
+ { label: "Indestructible", action: "toggle", target: "vehGodMode", state: false },
+ { label: "Nitro Boost", action: "veh_nitro" },
+ { label: "Super Brakes", action: "toggle", target: "superBrakes", state: false }
+ ]
+ },
+
+ veh_colors: {
+ title: "VEHICLE COLORS",
+ items: [
+ { label: "Black", action: "veh_color", value: [0, 0] },
+ { label: "White", action: "veh_color", value: [1, 1] },
+ { label: "Red", action: "veh_color", value: [27, 27] },
+ { label: "Blue", action: "veh_color", value: [51, 51] },
+ { label: "Yellow", action: "veh_color", value: [42, 42] },
+ { label: "Green", action: "veh_color", value: [53, 53] },
+ { label: "Orange", action: "veh_color", value: [38, 38] },
+ { label: "Purple", action: "veh_color", value: [61, 61] },
+ { label: "Pink", action: "veh_color", value: [68, 68] },
+ { label: "Gold", action: "veh_color", value: [37, 37] },
+ { label: "Chrome", action: "veh_color", value: [120, 120] },
+ { label: "Random", action: "veh_color_random" }
+ ]
+ },
+
+ network: {
+ title: "NETWORK OPTIONS",
+ items: [
+ { label: ">> Refresh Player List <<", action: "refresh_players" },
+ { label: "--- Players Online ---", action: "none" }
+ // Players will be added dynamically
+ ]
+ },
+
+ teleport: {
+ title: "TELEPORT LOCATIONS",
+ items: [
+ { label: "-- Algonquin --", action: "none" },
+ { label: "Star Junction", action: "teleport", value: { x: -252.0, y: 947.0, z: 15.0 } },
+ { label: "Middle Park", action: "teleport", value: { x: -365.0, y: 1163.0, z: 14.0 } },
+ { label: "Rotterdam Tower", action: "teleport", value: { x: 237.0, y: 1002.0, z: 18.0 } },
+ { label: "Chinatown", action: "teleport", value: { x: -141.0, y: 289.0, z: 14.0 } },
+ { label: "Happiness Island", action: "teleport", value: { x: -722.0, y: -17.0, z: 3.0 } },
+ { label: "-- Broker --", action: "none" },
+ { label: "Broker Bridge", action: "teleport", value: { x: 932.0, y: -495.0, z: 15.0 } },
+ { label: "Hove Beach", action: "teleport", value: { x: 1017.0, y: -505.0, z: 19.0 } },
+ { label: "Airport", action: "teleport", value: { x: 2140.0, y: 465.0, z: 6.0 } },
+ { label: "-- Bohan --", action: "none" },
+ { label: "South Bohan", action: "teleport", value: { x: 1243.0, y: -196.0, z: 26.0 } },
+ { label: "-- Alderney --", action: "none" },
+ { label: "Alderney City", action: "teleport", value: { x: -1149.0, y: 380.0, z: 21.0 } },
+ { label: "Westdyke", action: "teleport", value: { x: -1745.0, y: 1157.0, z: 25.0 } },
+ { label: "-- Special --", action: "none" },
+ { label: "Helipad (High)", action: "teleport", value: { x: -290.0, y: -400.0, z: 81.0 } },
+ { label: "Tower Top", action: "teleport", value: { x: 237.0, y: 1002.0, z: 200.0 } },
+ { label: "Waypoint (Marker)", action: "teleport_waypoint" }
+ ]
+ },
+
+ world: {
+ title: "WORLD OPTIONS",
+ items: [
+ { label: "-- Time --", action: "none" },
+ { label: "Morning (8:00)", action: "world_time", value: 8 },
+ { label: "Noon (12:00)", action: "world_time", value: 12 },
+ { label: "Evening (18:00)", action: "world_time", value: 18 },
+ { label: "Night (0:00)", action: "world_time", value: 0 },
+ { label: "-- Weather --", action: "none" },
+ { label: "Extra Sunny", action: "world_weather", value: 0 },
+ { label: "Sunny", action: "world_weather", value: 1 },
+ { label: "Cloudy", action: "world_weather", value: 3 },
+ { label: "Rainy", action: "world_weather", value: 4 },
+ { label: "Thunder", action: "world_weather", value: 7 },
+ { label: "Foggy", action: "world_weather", value: 6 }
+ ]
+ },
+
+ weapons: {
+ title: "WEAPONS",
+ items: [
+ { label: "Get All Weapons", action: "weapon_all" },
+ { label: "-- Melee --", action: "none" },
+ { label: "Baseball Bat", action: "weapon", value: 1 },
+ { label: "Knife", action: "weapon", value: 2 },
+ { label: "-- Pistols --", action: "none" },
+ { label: "Pistol", action: "weapon", value: 5 },
+ { label: "Desert Eagle", action: "weapon", value: 6 },
+ { label: "-- Shotguns --", action: "none" },
+ { label: "Shotgun", action: "weapon", value: 9 },
+ { label: "Combat Shotgun", action: "weapon", value: 10 },
+ { label: "-- SMGs --", action: "none" },
+ { label: "Micro SMG", action: "weapon", value: 11 },
+ { label: "SMG", action: "weapon", value: 12 },
+ { label: "-- Rifles --", action: "none" },
+ { label: "Assault Rifle", action: "weapon", value: 14 },
+ { label: "Carbine Rifle", action: "weapon", value: 15 },
+ { label: "Sniper Rifle", action: "weapon", value: 16 },
+ { label: "-- Explosives --", action: "none" },
+ { label: "RPG", action: "weapon", value: 18 },
+ { label: "Grenades", action: "weapon", value: 19 },
+ { label: "Molotov", action: "weapon", value: 20 }
+ ]
+ },
+
+ fun: {
+ title: "FUN OPTIONS",
+ items: [
+ { label: "Launch Me Up", action: "fun_launch" },
+ { label: "Explode Me", action: "fun_explode" },
+ { label: "Spawn Random Ped", action: "fun_ped" },
+ { label: "Ragdoll", action: "fun_ragdoll" },
+ { label: "Clear Area Peds", action: "fun_clearpeds" },
+ { label: "Clear Area Vehicles", action: "fun_clearvehicles" },
+ { label: "Chaos Mode", action: "toggle", target: "chaosMode", state: false },
+ { label: "Drunk Mode", action: "toggle", target: "drunkMode", state: false }
+ ]
+ }
+};
+
+// Toggle states
+let toggleStates = {
+ godMode: false,
+ neverWanted: false,
+ infiniteAmmo: false,
+ superJump: false,
+ fastRun: false,
+ driftMode: false,
+ vehGodMode: false,
+ superBrakes: false,
+ chaosMode: false,
+ drunkMode: false
+};
+
+// ============================================================================
+// INPUT HANDLING
+// ============================================================================
+
+addEventHandler("OnKeyUp", function(event, key, scanCode, mods) {
+ // F5 to toggle menu
+ if (key === SDLK_F5) {
+ menuOpen = !menuOpen;
+ if (menuOpen) {
+ currentMenu = "main";
+ selectedIndex = 0;
+ scrollOffset = 0;
+ menuStack = [];
+ gta.setCursorEnabled(true);
+ } else {
+ gta.setCursorEnabled(false);
+ }
+ return;
+ }
+
+ if (!menuOpen) return;
+
+ // Navigation
+ if (key === SDLK_UP) {
+ navigateUp();
+ } else if (key === SDLK_DOWN) {
+ navigateDown();
+ } else if (key === SDLK_RETURN || key === SDLK_KP_ENTER) {
+ selectItem();
+ } else if (key === SDLK_BACKSPACE || key === SDLK_ESCAPE) {
+ goBack();
+ } else if (key === SDLK_LEFT) {
+ // For sliders/adjustable options
+ adjustValue(-1);
+ } else if (key === SDLK_RIGHT) {
+ adjustValue(1);
+ }
+});
+
+function navigateUp() {
+ let items = getCurrentMenuItems();
+ do {
+ selectedIndex--;
+ if (selectedIndex < 0) {
+ selectedIndex = items.length - 1;
+ }
+ } while (items[selectedIndex] && items[selectedIndex].action === "none");
+
+ updateScroll(items);
+}
+
+function navigateDown() {
+ let items = getCurrentMenuItems();
+ do {
+ selectedIndex++;
+ if (selectedIndex >= items.length) {
+ selectedIndex = 0;
+ }
+ } while (items[selectedIndex] && items[selectedIndex].action === "none");
+
+ updateScroll(items);
+}
+
+function updateScroll(items) {
+ if (selectedIndex < scrollOffset) {
+ scrollOffset = selectedIndex;
+ } else if (selectedIndex >= scrollOffset + menu.maxVisibleItems) {
+ scrollOffset = selectedIndex - menu.maxVisibleItems + 1;
+ }
+}
+
+function goBack() {
+ if (menuStack.length > 0) {
+ let prev = menuStack.pop();
+ currentMenu = prev.menu;
+ selectedIndex = prev.index;
+ scrollOffset = prev.scroll;
+ } else {
+ menuOpen = false;
+ gta.setCursorEnabled(false);
+ }
+}
+
+function getCurrentMenuItems() {
+ if (currentMenu === "network") {
+ return getNetworkMenuItems();
+ }
+ return menuData[currentMenu] ? menuData[currentMenu].items : [];
+}
+
+function getNetworkMenuItems() {
+ let items = [
+ { label: ">> Refresh Player List <<", action: "refresh_players" },
+ { label: "--- Players Online ---", action: "none" }
+ ];
+
+ for (let i = 0; i < playerList.length; i++) {
+ items.push({
+ label: playerList[i].name,
+ action: "submenu",
+ target: "player_options",
+ playerData: playerList[i]
+ });
+ }
+
+ return items;
+}
+
+// ============================================================================
+// ACTION HANDLING
+// ============================================================================
+
+function selectItem() {
+ let items = getCurrentMenuItems();
+ let item = items[selectedIndex];
+ if (!item || item.action === "none") return;
+
+ switch (item.action) {
+ case "submenu":
+ if (item.target === "player_options" && item.playerData) {
+ // Store selected player for network options
+ selectedPlayer = item.playerData;
+ openPlayerMenu(item.playerData);
+ } else {
+ menuStack.push({ menu: currentMenu, index: selectedIndex, scroll: scrollOffset });
+ currentMenu = item.target;
+ selectedIndex = 0;
+ scrollOffset = 0;
+ }
+ break;
+
+ case "toggle":
+ toggleStates[item.target] = !toggleStates[item.target];
+ item.state = toggleStates[item.target];
+ triggerNetworkEvent("ModMenu:Toggle", item.target, toggleStates[item.target]);
+ showNotification(item.label + ": " + (toggleStates[item.target] ? "ON" : "OFF"));
+ break;
+
+ case "spawn_vehicle":
+ triggerNetworkEvent("ModMenu:SpawnVehicle", item.value);
+ showNotification("Spawning: " + item.label);
+ break;
+
+ case "teleport":
+ triggerNetworkEvent("ModMenu:Teleport", item.value.x, item.value.y, item.value.z);
+ showNotification("Teleporting to: " + item.label);
+ break;
+
+ case "teleport_waypoint":
+ triggerNetworkEvent("ModMenu:TeleportWaypoint");
+ showNotification("Teleporting to waypoint...");
+ break;
+
+ case "self_health":
+ triggerNetworkEvent("ModMenu:SelfOption", "health");
+ showNotification("Health restored!");
+ break;
+
+ case "self_armor":
+ triggerNetworkEvent("ModMenu:SelfOption", "armor");
+ showNotification("Armor restored!");
+ break;
+
+ case "self_max":
+ triggerNetworkEvent("ModMenu:SelfOption", "max");
+ showNotification("Max health & armor!");
+ break;
+
+ case "self_weapons":
+ triggerNetworkEvent("ModMenu:SelfOption", "weapons");
+ showNotification("All weapons given!");
+ break;
+
+ case "self_wanted":
+ triggerNetworkEvent("ModMenu:SelfOption", "wanted");
+ showNotification("Wanted level cleared!");
+ break;
+
+ case "self_respawn":
+ triggerNetworkEvent("ModMenu:SelfOption", "respawn");
+ showNotification("Respawning...");
+ break;
+
+ case "self_suicide":
+ triggerNetworkEvent("ModMenu:SelfOption", "suicide");
+ break;
+
+ case "skin":
+ triggerNetworkEvent("ModMenu:ChangeSkin", item.value);
+ showNotification("Skin changed!");
+ break;
+
+ case "skin_random":
+ triggerNetworkEvent("ModMenu:ChangeSkin", "random");
+ showNotification("Random skin applied!");
+ break;
+
+ case "veh_repair":
+ triggerNetworkEvent("ModMenu:VehicleOption", "repair");
+ showNotification("Vehicle repaired!");
+ break;
+
+ case "veh_flip":
+ triggerNetworkEvent("ModMenu:VehicleOption", "flip");
+ showNotification("Vehicle flipped!");
+ break;
+
+ case "veh_clean":
+ triggerNetworkEvent("ModMenu:VehicleOption", "clean");
+ showNotification("Vehicle cleaned!");
+ break;
+
+ case "veh_upgrade":
+ triggerNetworkEvent("ModMenu:VehicleOption", "upgrade");
+ showNotification("Vehicle upgraded!");
+ break;
+
+ case "veh_nitro":
+ triggerNetworkEvent("ModMenu:VehicleOption", "nitro");
+ showNotification("NITRO BOOST!");
+ break;
+
+ case "veh_color":
+ triggerNetworkEvent("ModMenu:VehicleColor", item.value[0], item.value[1]);
+ showNotification("Color changed!");
+ break;
+
+ case "veh_color_random":
+ let c1 = Math.floor(Math.random() * 132);
+ let c2 = Math.floor(Math.random() * 132);
+ triggerNetworkEvent("ModMenu:VehicleColor", c1, c2);
+ showNotification("Random color applied!");
+ break;
+
+ case "vehicle_delete":
+ triggerNetworkEvent("ModMenu:DeleteVehicles");
+ showNotification("Vehicles deleted!");
+ break;
+
+ case "handling":
+ handlingMods[item.target] = Math.max(0.1, Math.min(5.0, handlingMods[item.target] + item.delta));
+ triggerNetworkEvent("ModMenu:Handling", item.target, handlingMods[item.target]);
+ showNotification(item.target + ": " + handlingMods[item.target].toFixed(1));
+ break;
+
+ case "handling_reset":
+ handlingMods = { grip: 1.0, acceleration: 1.0, topSpeed: 1.0, braking: 1.0, driftMode: false };
+ triggerNetworkEvent("ModMenu:HandlingReset");
+ showNotification("Handling reset!");
+ break;
+
+ case "world_time":
+ triggerNetworkEvent("ModMenu:WorldTime", item.value);
+ showNotification("Time set to: " + item.value + ":00");
+ break;
+
+ case "world_weather":
+ triggerNetworkEvent("ModMenu:WorldWeather", item.value);
+ showNotification("Weather changed!");
+ break;
+
+ case "weapon":
+ triggerNetworkEvent("ModMenu:GiveWeapon", item.value);
+ showNotification("Weapon given: " + item.label);
+ break;
+
+ case "weapon_all":
+ triggerNetworkEvent("ModMenu:SelfOption", "weapons");
+ showNotification("All weapons given!");
+ break;
+
+ case "refresh_players":
+ triggerNetworkEvent("ModMenu:GetPlayers");
+ showNotification("Refreshing player list...");
+ break;
+
+ case "teleport_to_player":
+ if (selectedPlayer) {
+ triggerNetworkEvent("ModMenu:TeleportToPlayer", selectedPlayer.id);
+ showNotification("Teleporting to: " + selectedPlayer.name);
+ }
+ break;
+
+ case "fun_launch":
+ triggerNetworkEvent("ModMenu:Fun", "launch");
+ showNotification("LAUNCH!");
+ break;
+
+ case "fun_explode":
+ triggerNetworkEvent("ModMenu:Fun", "explode");
+ break;
+
+ case "fun_ped":
+ triggerNetworkEvent("ModMenu:Fun", "ped");
+ showNotification("Ped spawned!");
+ break;
+
+ case "fun_ragdoll":
+ triggerNetworkEvent("ModMenu:Fun", "ragdoll");
+ break;
+
+ case "fun_clearpeds":
+ triggerNetworkEvent("ModMenu:Fun", "clearpeds");
+ showNotification("Area cleared!");
+ break;
+
+ case "fun_clearvehicles":
+ triggerNetworkEvent("ModMenu:Fun", "clearvehicles");
+ showNotification("Vehicles cleared!");
+ break;
+ }
+}
+
+function adjustValue(direction) {
+ // For handling options when using left/right keys
+ let items = getCurrentMenuItems();
+ let item = items[selectedIndex];
+ if (!item) return;
+
+ if (item.action === "handling") {
+ let delta = item.delta * direction;
+ handlingMods[item.target] = Math.max(0.1, Math.min(5.0, handlingMods[item.target] + delta));
+ triggerNetworkEvent("ModMenu:Handling", item.target, handlingMods[item.target]);
+ showNotification(item.target + ": " + handlingMods[item.target].toFixed(1));
+ }
+}
+
+// Selected player for network options
+let selectedPlayer = null;
+
+function openPlayerMenu(playerData) {
+ // Create dynamic player options menu
+ menuData.player_options = {
+ title: playerData.name,
+ items: [
+ { label: "Teleport to Player", action: "teleport_to_player" },
+ { label: "Spectate Player", action: "spectate_player" },
+ { label: "Copy Position", action: "copy_pos" }
+ ]
+ };
+
+ menuStack.push({ menu: currentMenu, index: selectedIndex, scroll: scrollOffset });
+ currentMenu = "player_options";
+ selectedIndex = 0;
+ scrollOffset = 0;
+}
+
+// ============================================================================
+// NETWORK EVENT HANDLERS
+// ============================================================================
+
+addNetworkHandler("ModMenu:PlayerList", function(players) {
+ playerList = players;
+ showNotification("Found " + players.length + " players");
+});
+
+addNetworkHandler("ModMenu:Notification", function(message) {
+ showNotification(message);
+});
+
+// ============================================================================
+// RENDERING
+// ============================================================================
+
+addEventHandler("OnDrawnHUD", function(event) {
+ if (!menuOpen) return;
+
+ let currentData = menuData[currentMenu];
+ let items = getCurrentMenuItems();
+ let title = currentData ? currentData.title : currentMenu.toUpperCase();
+
+ // Calculate visible items
+ let visibleCount = Math.min(items.length, menu.maxVisibleItems);
+ let totalHeight = menu.headerHeight + (visibleCount * menu.itemHeight) + menu.footerHeight;
+
+ // Draw background
+ drawing.drawRectangle(null, menu.x - 5, menu.y - 5, menu.width + 10, totalHeight + 10, colors.background, colors.background, 0, 0, 0, false);
+
+ // Draw header
+ drawing.drawRectangle(null, menu.x, menu.y, menu.width, menu.headerHeight, colors.header, colors.header, 0, 0, 0, false);
+ drawing.drawText(title, menu.x + menu.width / 2, menu.y + 8, menu.width, menu.headerHeight, colors.headerText, 1.0, 1, true, false, false, true);
+
+ // Draw items
+ let yPos = menu.y + menu.headerHeight;
+ for (let i = scrollOffset; i < scrollOffset + visibleCount && i < items.length; i++) {
+ let item = items[i];
+ let isSelected = (i === selectedIndex);
+ let bgColor = isSelected ? colors.itemBgSelected : colors.itemBg;
+ let textColor = isSelected ? colors.itemTextSelected : colors.itemText;
+
+ // Draw item background
+ drawing.drawRectangle(null, menu.x, yPos, menu.width, menu.itemHeight, bgColor, bgColor, 0, 0, 0, false);
+
+ // Draw item text
+ let label = item.label;
+
+ // Add toggle state indicator
+ if (item.action === "toggle") {
+ let state = toggleStates[item.target];
+ label += state ? " [ON]" : " [OFF]";
+ }
+
+ // Add handling value indicator
+ if (item.action === "handling" && handlingMods[item.target] !== undefined) {
+ label += " [" + handlingMods[item.target].toFixed(1) + "]";
+ }
+
+ // Add submenu indicator
+ if (item.action === "submenu") {
+ label += " >>";
+ }
+
+ drawing.drawText(label, menu.x + 15, yPos + 8, menu.width - 30, menu.itemHeight, textColor, 0.9, 1, true, false, false, false);
+
+ yPos += menu.itemHeight;
+ }
+
+ // Draw footer
+ drawing.drawRectangle(null, menu.x, yPos, menu.width, menu.footerHeight, colors.footer, colors.footer, 0, 0, 0, false);
+
+ // Footer text with controls
+ let footerText = "UP/DOWN: Navigate | ENTER: Select | BACKSPACE: Back";
+ drawing.drawText(footerText, menu.x + menu.width / 2, yPos + 7, menu.width, menu.footerHeight, colors.footerText, 0.6, 1, true, false, false, true);
+
+ // Draw scroll indicator if needed
+ if (items.length > menu.maxVisibleItems) {
+ let scrollText = (scrollOffset + 1) + "-" + Math.min(scrollOffset + visibleCount, items.length) + " / " + items.length;
+ drawing.drawText(scrollText, menu.x + menu.width - 70, menu.y + 12, 60, 20, colors.subText, 0.7, 1, true, false, false, false);
+ }
+});
+
+// ============================================================================
+// NOTIFICATIONS
+// ============================================================================
+
+let notifications = [];
+
+function showNotification(text) {
+ notifications.push({
+ text: text,
+ time: Date.now(),
+ duration: 3000
+ });
+
+ // Clean old notifications
+ let now = Date.now();
+ notifications = notifications.filter(n => now - n.time < n.duration);
+}
+
+addEventHandler("OnDrawnHUD", function(event) {
+ let now = Date.now();
+ let yPos = 200;
+
+ for (let i = 0; i < notifications.length; i++) {
+ let notif = notifications[i];
+ let elapsed = now - notif.time;
+
+ if (elapsed < notif.duration) {
+ let alpha = elapsed < notif.duration - 500 ? 200 : Math.floor(200 * (notif.duration - elapsed) / 500);
+ let bgColor = toColour(20, 20, 20, alpha);
+ let textColor = toColour(255, 255, 100, alpha + 55);
+
+ drawing.drawRectangle(null, 10, yPos, 300, 30, bgColor, bgColor, 0, 0, 0, false);
+ drawing.drawText(notif.text, 20, yPos + 6, 280, 25, textColor, 0.8, 1, true, false, false, false);
+
+ yPos += 35;
+ }
+ }
+
+ // Clean expired
+ notifications = notifications.filter(n => now - n.time < n.duration);
+});
+
+// ============================================================================
+// TOGGLE EFFECTS (Client-side processing)
+// ============================================================================
+
+addEventHandler("OnProcess", function(event) {
+ if (!localPlayer) return;
+
+ // God Mode
+ if (toggleStates.godMode) {
+ localPlayer.health = 200;
+ localPlayer.armour = 100;
+ }
+
+ // Never Wanted
+ if (toggleStates.neverWanted) {
+ localPlayer.wantedLevel = 0;
+ }
+
+ // Super Jump (handled via natives if available)
+ // Fast Run (handled via natives if available)
+
+ // Drift Mode & Vehicle God Mode
+ if (localPlayer.vehicle) {
+ if (toggleStates.vehGodMode) {
+ localPlayer.vehicle.health = 1000;
+ }
+ }
+});
+
+// ============================================================================
+// INITIALIZATION
+// ============================================================================
+
+addEventHandler("OnResourceStart", function(event, resource) {
+ console.log("[ModMenu] Client script loaded!");
+ console.log("[ModMenu] Press F5 to open the menu");
+});
+
+addEventHandler("OnLocalPlayerSpawn", function(event) {
+ showNotification("Press F5 to open Mod Menu");
+});
+
+console.log("[ModMenu] Client script loaded - Press F5 to open menu!");
diff --git a/resources/modmenu/meta.xml b/resources/modmenu/meta.xml
new file mode 100644
index 0000000..ae8d132
--- /dev/null
+++ b/resources/modmenu/meta.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/resources/modmenu/server.js b/resources/modmenu/server.js
new file mode 100644
index 0000000..6591210
--- /dev/null
+++ b/resources/modmenu/server.js
@@ -0,0 +1,571 @@
+// ============================================================================
+// MOD MENU - Server Side
+// Handles all server-side actions triggered from the client menu
+// ============================================================================
+
+// Vehicle model hashes for spawning
+const vehicleModels = {
+ // Sports Cars
+ "infernus": -1461482751,
+ "turismo": -982130927,
+ "comet": 1063483177,
+ "banshee": -1823484407,
+ "sultan": 970598228,
+ "coquette": 108773431,
+ "feltzer": -349601129,
+ "f620": -591651781,
+ "buffalo": -304802106,
+
+ // Super Cars
+ "entityxf": -1291952903,
+ "adder": -1216765807,
+ "vacca": 338562499,
+ "bullet": -1696146015,
+ "cheetah": -1311154784,
+
+ // Muscle Cars
+ "sabregt": 1357660823,
+ "stalion": 1923400478,
+ "vigero": -825837129,
+ "dukes": 723973206,
+ "ruiner": -227741703,
+ "phoenix": -2095439403,
+ "gauntlet": -1800170043,
+ "dominator": 80636076,
+
+ // SUVs
+ "patriot": -808457413,
+ "cavalcade": 2006918058,
+ "granger": 1269098716,
+ "huntley": 486987393,
+ "landstalker": 1269098716,
+ "habanero": 884422927,
+ "serrano": 1337041428,
+ "rebla": 83136452,
+
+ // Sedans
+ "oracle": 1348744438,
+ "schafter": -888242983,
+ "admiral": -1645064850,
+ "vincent": -884237051,
+ "presidente": -1150599089,
+ "cognoscenti": -2030171296,
+ "blista": -344943009,
+ "premier": -1883869285,
+
+ // Motorcycles
+ "nrg900": -1706076364,
+ "pcj600": -909201658,
+ "sanchez": 788045382,
+ "faggio": 55628203,
+ "bati": -891462355,
+ "akuma": 1672195559,
+ "double": -1670998136,
+ "hakuchou": 1265391242,
+ "hexer": 301427732,
+ "daemon": 2006142190,
+
+ // Emergency
+ "police": 2046537925,
+ "police2": -1627000575,
+ "fbi": 1127131465,
+ "noose": -1683328900,
+ "ambulance": 1171614426,
+ "firetruk": 1938952078,
+ "enforcer": 2046537925,
+
+ // Aircraft
+ "annihilator": 837858166,
+ "maverick": -1660661558,
+ "polmav": 353883353,
+ "buzzard": 788747387,
+ "shamal": -1214293858,
+
+ // Boats
+ "jetmax": 861409633,
+ "marquis": -1043459709,
+ "predator": -488123221,
+ "tropic": 290013743,
+ "dinghy": 1033245328,
+ "squalo": 400514754,
+ "reefer": 1016996501,
+
+ // Special
+ "taxi": -956048545,
+ "stretch": -1961627517,
+ "bus": -713569950,
+ "trashmaster": 1917016601,
+ "forklift": 1491375716,
+ "caddy": 1147287684,
+ "bulldozer": 1886712733,
+ "phantom": -2137348917
+};
+
+// Player skin models
+const skinModels = [
+ -1667301416, // Niko
+ -163448165, // Roman
+ 1936355839, // Jacob
+ -1938475496, // Brucie
+ 970234525, // Playboy X
+ -1784875845, // Johnny
+ -1403507487, // Luis
+ -1320879687, // Cop
+ -1306011498, // NOOSE
+ 2136829318, // Paramedic
+ 1616659040, // Firefighter
+ -268651930, // Business
+ 1943617350 // Hobo
+];
+
+// Store spawned vehicles per player
+let playerVehicles = {};
+
+// Player toggle states (for server-validated features)
+let playerToggles = {};
+
+// ============================================================================
+// EVENTS
+// ============================================================================
+
+addEventHandler("OnResourceStart", function(event, resource) {
+ console.log("[ModMenu] Server resource started!");
+});
+
+addEventHandler("OnPlayerJoined", function(event, client) {
+ playerVehicles[client.index] = [];
+ playerToggles[client.index] = {};
+
+ // Inform player about the menu
+ messageClient("[MOD MENU] Press F5 to open the mod menu!", client, [255, 200, 100, 255]);
+});
+
+addEventHandler("OnPlayerQuit", function(event, client, reason) {
+ // Clean up player vehicles
+ if (playerVehicles[client.index]) {
+ for (let i = 0; i < playerVehicles[client.index].length; i++) {
+ let veh = playerVehicles[client.index][i];
+ if (veh) {
+ destroyElement(veh);
+ }
+ }
+ delete playerVehicles[client.index];
+ }
+ delete playerToggles[client.index];
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Self Options
+// ============================================================================
+
+addNetworkHandler("ModMenu:SelfOption", function(client, option) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ switch(option) {
+ case "health":
+ client.player.health = 200;
+ break;
+
+ case "armor":
+ client.player.armour = 100;
+ break;
+
+ case "max":
+ client.player.health = 200;
+ client.player.armour = 100;
+ break;
+
+ case "weapons":
+ giveAllWeapons(client);
+ break;
+
+ case "wanted":
+ client.player.wantedLevel = 0;
+ break;
+
+ case "respawn":
+ // Respawn at random location
+ let spawns = [
+ { x: -252.0, y: 947.0, z: 15.0 },
+ { x: 932.0, y: -495.0, z: 15.0 },
+ { x: -365.0, y: 1163.0, z: 14.0 },
+ { x: 1243.0, y: -196.0, z: 26.0 }
+ ];
+ let spawn = spawns[Math.floor(Math.random() * spawns.length)];
+ client.spawn([spawn.x, spawn.y, spawn.z], 0, -1667301416);
+ break;
+
+ case "suicide":
+ client.player.health = 0;
+ break;
+ }
+
+ console.log("[ModMenu] " + client.name + " used self option: " + option);
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Toggle Features
+// ============================================================================
+
+addNetworkHandler("ModMenu:Toggle", function(client, feature, state) {
+ if (!playerToggles[client.index]) {
+ playerToggles[client.index] = {};
+ }
+
+ playerToggles[client.index][feature] = state;
+ console.log("[ModMenu] " + client.name + " toggled " + feature + ": " + state);
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Skin Change
+// ============================================================================
+
+addNetworkHandler("ModMenu:ChangeSkin", function(client, skinId) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ if (skinId === "random") {
+ skinId = skinModels[Math.floor(Math.random() * skinModels.length)];
+ }
+
+ client.player.modelIndex = skinId;
+ console.log("[ModMenu] " + client.name + " changed skin to: " + skinId);
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Vehicle Spawning
+// ============================================================================
+
+addNetworkHandler("ModMenu:SpawnVehicle", function(client, vehicleName) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ let modelHash = vehicleModels[vehicleName];
+ if (!modelHash) {
+ triggerNetworkEvent("ModMenu:Notification", client, "Vehicle not found!");
+ return;
+ }
+
+ // Delete previous vehicles (limit to 1 per player)
+ deletePlayerVehicles(client);
+
+ // Get spawn position
+ let pos = client.player.position;
+ let heading = client.player.heading;
+
+ let spawnX = pos[0] + (Math.sin(heading) * 4);
+ let spawnY = pos[1] + (Math.cos(heading) * 4);
+ let spawnZ = pos[2] + 1;
+
+ // Create vehicle
+ let vehicle = gta.createVehicle(modelHash, [spawnX, spawnY, spawnZ], heading);
+
+ if (vehicle) {
+ if (!playerVehicles[client.index]) {
+ playerVehicles[client.index] = [];
+ }
+ playerVehicles[client.index].push(vehicle);
+
+ // Set vehicle properties
+ vehicle.health = 1000;
+ vehicle.locked = false;
+ vehicle.engine = true;
+
+ // Random color
+ let c1 = Math.floor(Math.random() * 132);
+ let c2 = Math.floor(Math.random() * 132);
+ vehicle.colour1 = c1;
+ vehicle.colour2 = c2;
+
+ triggerNetworkEvent("ModMenu:Notification", client, "Vehicle spawned: " + vehicleName.toUpperCase());
+ console.log("[ModMenu] " + client.name + " spawned " + vehicleName);
+ } else {
+ triggerNetworkEvent("ModMenu:Notification", client, "Failed to spawn vehicle!");
+ }
+});
+
+addNetworkHandler("ModMenu:DeleteVehicles", function(client) {
+ deletePlayerVehicles(client);
+ triggerNetworkEvent("ModMenu:Notification", client, "Your vehicles have been deleted!");
+});
+
+function deletePlayerVehicles(client) {
+ if (playerVehicles[client.index]) {
+ for (let i = 0; i < playerVehicles[client.index].length; i++) {
+ let veh = playerVehicles[client.index][i];
+ if (veh) {
+ destroyElement(veh);
+ }
+ }
+ playerVehicles[client.index] = [];
+ }
+}
+
+// ============================================================================
+// NETWORK HANDLERS - Vehicle Options
+// ============================================================================
+
+addNetworkHandler("ModMenu:VehicleOption", function(client, option) {
+ if (!client.player || !client.player.vehicle) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to be in a vehicle!");
+ return;
+ }
+
+ let vehicle = client.player.vehicle;
+
+ switch(option) {
+ case "repair":
+ vehicle.health = 1000;
+ break;
+
+ case "flip":
+ let rot = vehicle.rotation;
+ vehicle.rotation = [0, 0, rot[2]];
+ break;
+
+ case "clean":
+ vehicle.dirtLevel = 0;
+ break;
+
+ case "upgrade":
+ // Max out visual mods if supported
+ vehicle.health = 1000;
+ break;
+
+ case "nitro":
+ // Boost vehicle forward
+ let pos = vehicle.position;
+ let heading = vehicle.heading;
+ let boost = 50;
+ vehicle.position = [
+ pos[0] + (Math.sin(heading) * boost),
+ pos[1] + (Math.cos(heading) * boost),
+ pos[2]
+ ];
+ break;
+ }
+
+ console.log("[ModMenu] " + client.name + " used vehicle option: " + option);
+});
+
+addNetworkHandler("ModMenu:VehicleColor", function(client, color1, color2) {
+ if (!client.player || !client.player.vehicle) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to be in a vehicle!");
+ return;
+ }
+
+ client.player.vehicle.colour1 = color1;
+ client.player.vehicle.colour2 = color2;
+ console.log("[ModMenu] " + client.name + " changed vehicle color to: " + color1 + ", " + color2);
+});
+
+addNetworkHandler("ModMenu:Handling", function(client, property, value) {
+ // Store handling modifications for the player
+ // Note: Actual handling modification depends on GTAC's native support
+ if (!playerToggles[client.index]) {
+ playerToggles[client.index] = {};
+ }
+ playerToggles[client.index]["handling_" + property] = value;
+ console.log("[ModMenu] " + client.name + " set handling " + property + " to: " + value);
+});
+
+addNetworkHandler("ModMenu:HandlingReset", function(client) {
+ if (playerToggles[client.index]) {
+ delete playerToggles[client.index].handling_grip;
+ delete playerToggles[client.index].handling_acceleration;
+ delete playerToggles[client.index].handling_topSpeed;
+ delete playerToggles[client.index].handling_braking;
+ }
+ console.log("[ModMenu] " + client.name + " reset vehicle handling");
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Teleportation
+// ============================================================================
+
+addNetworkHandler("ModMenu:Teleport", function(client, x, y, z) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ if (client.player.vehicle) {
+ client.player.vehicle.position = [x, y, z];
+ } else {
+ client.player.position = [x, y, z];
+ }
+
+ console.log("[ModMenu] " + client.name + " teleported to: " + x + ", " + y + ", " + z);
+});
+
+addNetworkHandler("ModMenu:TeleportWaypoint", function(client) {
+ // Teleport to map waypoint - requires client-side waypoint data
+ triggerNetworkEvent("ModMenu:Notification", client, "Set a waypoint on the map first!");
+});
+
+addNetworkHandler("ModMenu:TeleportToPlayer", function(client, targetId) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ let clients = getClients();
+ let target = null;
+
+ for (let i = 0; i < clients.length; i++) {
+ if (clients[i].index == targetId) {
+ target = clients[i];
+ break;
+ }
+ }
+
+ if (target && target.player) {
+ let pos = target.player.position;
+ if (client.player.vehicle) {
+ client.player.vehicle.position = [pos[0] + 3, pos[1], pos[2]];
+ } else {
+ client.player.position = [pos[0] + 3, pos[1], pos[2]];
+ }
+ triggerNetworkEvent("ModMenu:Notification", client, "Teleported to: " + target.name);
+ console.log("[ModMenu] " + client.name + " teleported to " + target.name);
+ } else {
+ triggerNetworkEvent("ModMenu:Notification", client, "Player not found or not spawned!");
+ }
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Get Players
+// ============================================================================
+
+addNetworkHandler("ModMenu:GetPlayers", function(client) {
+ let clients = getClients();
+ let playerList = [];
+
+ for (let i = 0; i < clients.length; i++) {
+ let c = clients[i];
+ playerList.push({
+ id: c.index,
+ name: c.name
+ });
+ }
+
+ triggerNetworkEvent("ModMenu:PlayerList", client, playerList);
+});
+
+// ============================================================================
+// NETWORK HANDLERS - World Options
+// ============================================================================
+
+addNetworkHandler("ModMenu:WorldTime", function(client, hour) {
+ gta.time = [hour, 0];
+ message("[WORLD] " + client.name + " changed time to: " + hour + ":00", [100, 200, 255, 255]);
+ console.log("[ModMenu] " + client.name + " changed time to: " + hour);
+});
+
+addNetworkHandler("ModMenu:WorldWeather", function(client, weatherId) {
+ gta.weather = weatherId;
+ message("[WORLD] " + client.name + " changed the weather", [100, 200, 255, 255]);
+ console.log("[ModMenu] " + client.name + " changed weather to: " + weatherId);
+});
+
+// ============================================================================
+// NETWORK HANDLERS - Weapons
+// ============================================================================
+
+addNetworkHandler("ModMenu:GiveWeapon", function(client, weaponId) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ client.giveWeapon(weaponId, 500);
+ console.log("[ModMenu] " + client.name + " got weapon: " + weaponId);
+});
+
+function giveAllWeapons(client) {
+ if (!client.player) return;
+
+ // GTA IV Weapons
+ client.giveWeapon(1, 1); // Bat
+ client.giveWeapon(2, 1); // Knife
+ client.giveWeapon(5, 500); // Pistol
+ client.giveWeapon(6, 500); // Deagle
+ client.giveWeapon(9, 200); // Shotgun
+ client.giveWeapon(10, 200); // Combat Shotgun
+ client.giveWeapon(11, 500); // Micro SMG
+ client.giveWeapon(12, 500); // SMG
+ client.giveWeapon(14, 500); // AK47
+ client.giveWeapon(15, 500); // M4
+ client.giveWeapon(16, 100); // Sniper
+ client.giveWeapon(18, 20); // RPG
+ client.giveWeapon(19, 20); // Grenades
+ client.giveWeapon(20, 20); // Molotov
+}
+
+// ============================================================================
+// NETWORK HANDLERS - Fun Options
+// ============================================================================
+
+addNetworkHandler("ModMenu:Fun", function(client, option) {
+ if (!client.player) {
+ triggerNetworkEvent("ModMenu:Notification", client, "You need to spawn first!");
+ return;
+ }
+
+ let pos = client.player.position;
+
+ switch(option) {
+ case "launch":
+ client.player.position = [pos[0], pos[1], pos[2] + 50];
+ break;
+
+ case "explode":
+ gta.createExplosion(pos, 0, 5.0);
+ break;
+
+ case "ped":
+ // Spawn a random ped near player
+ let pedSkin = skinModels[Math.floor(Math.random() * skinModels.length)];
+ let ped = gta.createPed(pedSkin, [pos[0] + 3, pos[1] + 3, pos[2]], 0);
+ if (ped) {
+ console.log("[ModMenu] " + client.name + " spawned a ped");
+ }
+ break;
+
+ case "ragdoll":
+ // Trigger ragdoll via health damage
+ let currentHealth = client.player.health;
+ client.player.health = currentHealth - 10;
+ setTimeout(function() {
+ if (client.player) {
+ client.player.health = currentHealth;
+ }
+ }, 100);
+ break;
+
+ case "clearpeds":
+ // Clear peds in area - limited implementation
+ triggerNetworkEvent("ModMenu:Notification", client, "Area clearing requested");
+ break;
+
+ case "clearvehicles":
+ // Clear vehicles in area - limited implementation
+ triggerNetworkEvent("ModMenu:Notification", client, "Vehicle clearing requested");
+ break;
+ }
+
+ console.log("[ModMenu] " + client.name + " used fun option: " + option);
+});
+
+// ============================================================================
+// INITIALIZATION
+// ============================================================================
+
+console.log("[ModMenu] Server script loaded!");
diff --git a/server.xml b/server.xml
index 945687b..926ac6a 100644
--- a/server.xml
+++ b/server.xml
@@ -33,5 +33,6 @@
+