mirror of
https://github.com/iDisaster/GTAConnected.git
synced 2026-03-08 09:25:23 +00:00
Merge pull request #2 from iDisaster/claude/enhance-gta-server-9Dezg
Claude/enhance gta server 9 dezg
This commit is contained in:
74
README.md
74
README.md
@@ -11,6 +11,7 @@ A comprehensive GTA IV freeroam server for GTAConnected with multiple features i
|
|||||||
- **World Control** - Weather and time manipulation
|
- **World Control** - Weather and time manipulation
|
||||||
- **Enhanced Chat** - Private messages, local chat, actions, and OOC chat
|
- **Enhanced Chat** - Private messages, local chat, actions, and OOC chat
|
||||||
- **Kill Tracking** - Statistics for kills, deaths, and K/D ratio
|
- **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
|
## Installation
|
||||||
|
|
||||||
@@ -41,9 +42,13 @@ GTAConnected/
|
|||||||
├── chat/ # Enhanced chat system
|
├── chat/ # Enhanced chat system
|
||||||
│ ├── meta.xml
|
│ ├── meta.xml
|
||||||
│ └── server.js
|
│ └── server.js
|
||||||
└── teleport/ # Teleportation system
|
├── teleport/ # Teleportation system
|
||||||
|
│ ├── meta.xml
|
||||||
|
│ └── server.js
|
||||||
|
└── modmenu/ # Interactive GUI mod menu
|
||||||
├── meta.xml
|
├── meta.xml
|
||||||
└── server.js
|
├── server.js
|
||||||
|
└── client.js
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
## 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
|
## Requirements
|
||||||
|
|
||||||
- GTAConnected Server (v1.0.72 or newer)
|
- GTAConnected Server (v1.0.72 or newer)
|
||||||
|
|||||||
@@ -3,11 +3,23 @@
|
|||||||
// Handles server moderation, kick/ban, and admin commands
|
// Handles server moderation, kick/ban, and admin commands
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Define colors using toColour
|
||||||
|
const COLOUR_WHITE = toColour(255, 255, 255, 255);
|
||||||
|
const COLOUR_RED = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_DARKRED = toColour(255, 50, 50, 255);
|
||||||
|
const COLOUR_GREEN = toColour(100, 255, 100, 255);
|
||||||
|
const COLOUR_BLUE = toColour(100, 200, 255, 255);
|
||||||
|
const COLOUR_YELLOW = toColour(255, 255, 100, 255);
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_GREY = toColour(200, 200, 200, 255);
|
||||||
|
const COLOUR_PINK = toColour(255, 150, 100, 255);
|
||||||
|
|
||||||
// Admin list - add player names or account identifiers here
|
// Admin list - add player names or account identifiers here
|
||||||
const admins = [
|
const admins = [
|
||||||
"Admin",
|
"Admin",
|
||||||
"Owner",
|
"Owner",
|
||||||
"ServerOwner"
|
"ServerOwner",
|
||||||
|
"Disaster"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Ban list (stored in memory - use a database for persistence)
|
// Ban list (stored in memory - use a database for persistence)
|
||||||
@@ -27,7 +39,6 @@ addEventHandler("OnPlayerConnect", function(event, client, ip) {
|
|||||||
for (let i = 0; i < bannedPlayers.length; i++) {
|
for (let i = 0; i < bannedPlayers.length; i++) {
|
||||||
if (bannedPlayers[i].ip === ip || bannedPlayers[i].name === client.name) {
|
if (bannedPlayers[i].ip === ip || bannedPlayers[i].name === client.name) {
|
||||||
console.log("[Admin] Banned player attempted to connect: " + client.name + " (" + ip + ")");
|
console.log("[Admin] Banned player attempted to connect: " + client.name + " (" + ip + ")");
|
||||||
// Kick banned player
|
|
||||||
client.disconnect("You are banned from this server: " + bannedPlayers[i].reason);
|
client.disconnect("You are banned from this server: " + bannedPlayers[i].reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -36,7 +47,7 @@ addEventHandler("OnPlayerConnect", function(event, client, ip) {
|
|||||||
|
|
||||||
addEventHandler("OnPlayerJoined", function(event, client) {
|
addEventHandler("OnPlayerJoined", function(event, client) {
|
||||||
if (isAdmin(client)) {
|
if (isAdmin(client)) {
|
||||||
messageClient("[ADMIN] You have administrator privileges", client, [255, 100, 100, 255]);
|
messageClient("[ADMIN] You have administrator privileges", client, COLOUR_RED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,7 +64,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
reportPlayer(client, params);
|
reportPlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /report <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /report <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -71,7 +82,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
let reason = parts.slice(1).join(" ") || "No reason specified";
|
let reason = parts.slice(1).join(" ") || "No reason specified";
|
||||||
kickPlayer(client, targetName, reason);
|
kickPlayer(client, targetName, reason);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /kick <player> [reason]", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /kick <player> [reason]", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -82,7 +93,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
let reason = parts.slice(1).join(" ") || "No reason specified";
|
let reason = parts.slice(1).join(" ") || "No reason specified";
|
||||||
banPlayer(client, targetName, reason);
|
banPlayer(client, targetName, reason);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /ban <player> [reason]", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /ban <player> [reason]", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -90,7 +101,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
unbanPlayer(client, params);
|
unbanPlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /unban <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /unban <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -98,7 +109,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
mutePlayer(client, params);
|
mutePlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /mute <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /mute <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -106,7 +117,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
unmutePlayer(client, params);
|
unmutePlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /unmute <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /unmute <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -114,7 +125,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
freezePlayer(client, params, true);
|
freezePlayer(client, params, true);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /freeze <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /freeze <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -122,7 +133,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
freezePlayer(client, params, false);
|
freezePlayer(client, params, false);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /unfreeze <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /unfreeze <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -130,7 +141,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
slapPlayer(client, params);
|
slapPlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /slap <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /slap <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -139,7 +150,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
teleportToPlayer(client, params);
|
teleportToPlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /goto <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /goto <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -148,7 +159,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
bringPlayer(client, params);
|
bringPlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /bring <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /bring <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -157,7 +168,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
announce(params);
|
announce(params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /announce <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /announce <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -165,7 +176,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
setAdmin(client, params);
|
setAdmin(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /setadmin <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /setadmin <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -173,7 +184,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
getPlayerIP(client, params);
|
getPlayerIP(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /getip <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /getip <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -188,7 +199,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
setPlayerHealth(client, parts[0], parseInt(parts[1]));
|
setPlayerHealth(client, parts[0], parseInt(parts[1]));
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /sethealth <player> <health>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /sethealth <player> <health>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -199,7 +210,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
setPlayerArmour(client, parts[0], parseInt(parts[1]));
|
setPlayerArmour(client, parts[0], parseInt(parts[1]));
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /setarmour <player> <armour>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /setarmour <player> <armour>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -208,7 +219,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
explodePlayer(client, params);
|
explodePlayer(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /explode <player>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /explode <player>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -217,8 +228,8 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
// Check if player is muted before chat
|
// Check if player is muted before chat
|
||||||
addEventHandler("OnPlayerChat", function(event, client, messageText) {
|
addEventHandler("OnPlayerChat", function(event, client, messageText) {
|
||||||
if (isMuted(client)) {
|
if (isMuted(client)) {
|
||||||
messageClient("[ADMIN] You are muted and cannot chat!", client, [255, 100, 100, 255]);
|
messageClient("[ADMIN] You are muted and cannot chat!", client, COLOUR_RED);
|
||||||
return false; // Cancel the chat
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -250,11 +261,11 @@ function findPlayer(name) {
|
|||||||
function kickPlayer(admin, targetName, reason) {
|
function kickPlayer(admin, targetName, reason) {
|
||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target) {
|
if (target) {
|
||||||
message("[ADMIN] " + target.name + " was kicked by " + admin.name + ": " + reason, [255, 100, 100, 255]);
|
message("[ADMIN] " + target.name + " was kicked by " + admin.name + ": " + reason, COLOUR_RED);
|
||||||
target.disconnect("Kicked: " + reason);
|
target.disconnect("Kicked: " + reason);
|
||||||
console.log("[Admin] " + admin.name + " kicked " + target.name + ": " + reason);
|
console.log("[Admin] " + admin.name + " kicked " + target.name + ": " + reason);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,11 +280,11 @@ function banPlayer(admin, targetName, reason) {
|
|||||||
date: new Date().toISOString()
|
date: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
message("[ADMIN] " + target.name + " was banned by " + admin.name + ": " + reason, [255, 50, 50, 255]);
|
message("[ADMIN] " + target.name + " was banned by " + admin.name + ": " + reason, COLOUR_DARKRED);
|
||||||
target.disconnect("Banned: " + reason);
|
target.disconnect("Banned: " + reason);
|
||||||
console.log("[Admin] " + admin.name + " banned " + target.name + ": " + reason);
|
console.log("[Admin] " + admin.name + " banned " + target.name + ": " + reason);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,10 +298,10 @@ function unbanPlayer(admin, targetName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
messageClient("[ADMIN] " + targetName + " has been unbanned", admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] " + targetName + " has been unbanned", admin, COLOUR_GREEN);
|
||||||
console.log("[Admin] " + admin.name + " unbanned " + targetName);
|
console.log("[Admin] " + admin.name + " unbanned " + targetName);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found in ban list: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found in ban list: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,10 +311,10 @@ function mutePlayer(admin, targetName) {
|
|||||||
if (mutedPlayers.indexOf(target.name) === -1) {
|
if (mutedPlayers.indexOf(target.name) === -1) {
|
||||||
mutedPlayers.push(target.name);
|
mutedPlayers.push(target.name);
|
||||||
}
|
}
|
||||||
message("[ADMIN] " + target.name + " was muted by " + admin.name, [255, 150, 100, 255]);
|
message("[ADMIN] " + target.name + " was muted by " + admin.name, COLOUR_PINK);
|
||||||
console.log("[Admin] " + admin.name + " muted " + target.name);
|
console.log("[Admin] " + admin.name + " muted " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,10 +325,10 @@ function unmutePlayer(admin, targetName) {
|
|||||||
let index = mutedPlayers.indexOf(name);
|
let index = mutedPlayers.indexOf(name);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
mutedPlayers.splice(index, 1);
|
mutedPlayers.splice(index, 1);
|
||||||
message("[ADMIN] " + name + " was unmuted by " + admin.name, [100, 255, 100, 255]);
|
message("[ADMIN] " + name + " was unmuted by " + admin.name, COLOUR_GREEN);
|
||||||
console.log("[Admin] " + admin.name + " unmuted " + name);
|
console.log("[Admin] " + admin.name + " unmuted " + name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player is not muted: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player is not muted: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,10 +337,10 @@ function freezePlayer(admin, targetName, freeze) {
|
|||||||
if (target && target.player) {
|
if (target && target.player) {
|
||||||
target.player.frozen = freeze;
|
target.player.frozen = freeze;
|
||||||
let status = freeze ? "frozen" : "unfrozen";
|
let status = freeze ? "frozen" : "unfrozen";
|
||||||
message("[ADMIN] " + target.name + " was " + status + " by " + admin.name, [255, 200, 100, 255]);
|
message("[ADMIN] " + target.name + " was " + status + " by " + admin.name, COLOUR_ORANGE);
|
||||||
console.log("[Admin] " + admin.name + " " + status + " " + target.name);
|
console.log("[Admin] " + admin.name + " " + status + " " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,12 +348,12 @@ function slapPlayer(admin, targetName) {
|
|||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target && target.player) {
|
if (target && target.player) {
|
||||||
let pos = target.player.position;
|
let pos = target.player.position;
|
||||||
target.player.position = [pos[0], pos[1], pos[2] + 5]; // Launch up
|
target.player.position = new Vec3(pos.x, pos.y, pos.z + 5);
|
||||||
target.player.health -= 10;
|
target.player.health -= 10;
|
||||||
message("[ADMIN] " + target.name + " was slapped by " + admin.name, [255, 200, 100, 255]);
|
message("[ADMIN] " + target.name + " was slapped by " + admin.name, COLOUR_ORANGE);
|
||||||
console.log("[Admin] " + admin.name + " slapped " + target.name);
|
console.log("[Admin] " + admin.name + " slapped " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,11 +361,11 @@ function teleportToPlayer(admin, targetName) {
|
|||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target && target.player && admin.player) {
|
if (target && target.player && admin.player) {
|
||||||
let pos = target.player.position;
|
let pos = target.player.position;
|
||||||
admin.player.position = [pos[0] + 2, pos[1], pos[2]];
|
admin.player.position = new Vec3(pos.x + 2, pos.y, pos.z);
|
||||||
messageClient("[ADMIN] Teleported to " + target.name, admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] Teleported to " + target.name, admin, COLOUR_GREEN);
|
||||||
console.log("[Admin] " + admin.name + " teleported to " + target.name);
|
console.log("[Admin] " + admin.name + " teleported to " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,19 +373,19 @@ function bringPlayer(admin, targetName) {
|
|||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target && target.player && admin.player) {
|
if (target && target.player && admin.player) {
|
||||||
let pos = admin.player.position;
|
let pos = admin.player.position;
|
||||||
target.player.position = [pos[0] + 2, pos[1], pos[2]];
|
target.player.position = new Vec3(pos.x + 2, pos.y, pos.z);
|
||||||
messageClient("[ADMIN] " + admin.name + " brought you to them", target, [255, 200, 100, 255]);
|
messageClient("[ADMIN] " + admin.name + " brought you to them", target, COLOUR_ORANGE);
|
||||||
messageClient("[ADMIN] Brought " + target.name + " to you", admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] Brought " + target.name + " to you", admin, COLOUR_GREEN);
|
||||||
console.log("[Admin] " + admin.name + " brought " + target.name);
|
console.log("[Admin] " + admin.name + " brought " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function announce(text) {
|
function announce(text) {
|
||||||
message("=================================", [255, 255, 100, 255]);
|
message("=================================", COLOUR_YELLOW);
|
||||||
message("[ANNOUNCEMENT] " + text, [255, 255, 100, 255]);
|
message("[ANNOUNCEMENT] " + text, COLOUR_YELLOW);
|
||||||
message("=================================", [255, 255, 100, 255]);
|
message("=================================", COLOUR_YELLOW);
|
||||||
console.log("[Admin] Announcement: " + text);
|
console.log("[Admin] Announcement: " + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,23 +394,23 @@ function setAdmin(admin, targetName) {
|
|||||||
if (target) {
|
if (target) {
|
||||||
if (admins.indexOf(target.name) === -1) {
|
if (admins.indexOf(target.name) === -1) {
|
||||||
admins.push(target.name);
|
admins.push(target.name);
|
||||||
messageClient("[ADMIN] You have been granted admin privileges by " + admin.name, target, [100, 255, 100, 255]);
|
messageClient("[ADMIN] You have been granted admin privileges by " + admin.name, target, COLOUR_GREEN);
|
||||||
messageClient("[ADMIN] " + target.name + " is now an admin", admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] " + target.name + " is now an admin", admin, COLOUR_GREEN);
|
||||||
console.log("[Admin] " + admin.name + " made " + target.name + " an admin");
|
console.log("[Admin] " + admin.name + " made " + target.name + " an admin");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] " + target.name + " is already an admin", admin, [255, 200, 100, 255]);
|
messageClient("[ADMIN] " + target.name + " is already an admin", admin, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlayerIP(admin, targetName) {
|
function getPlayerIP(admin, targetName) {
|
||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target) {
|
if (target) {
|
||||||
messageClient("[ADMIN] " + target.name + "'s IP: " + target.ip, admin, [200, 200, 255, 255]);
|
messageClient("[ADMIN] " + target.name + "'s IP: " + target.ip, admin, COLOUR_BLUE);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,9 +418,9 @@ function setPlayerHealth(admin, targetName, health) {
|
|||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target && target.player) {
|
if (target && target.player) {
|
||||||
target.player.health = health;
|
target.player.health = health;
|
||||||
messageClient("[ADMIN] Set " + target.name + "'s health to " + health, admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] Set " + target.name + "'s health to " + health, admin, COLOUR_GREEN);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,9 +428,9 @@ function setPlayerArmour(admin, targetName, armour) {
|
|||||||
let target = findPlayer(targetName);
|
let target = findPlayer(targetName);
|
||||||
if (target && target.player) {
|
if (target && target.player) {
|
||||||
target.player.armour = armour;
|
target.player.armour = armour;
|
||||||
messageClient("[ADMIN] Set " + target.name + "'s armour to " + armour, admin, [100, 255, 100, 255]);
|
messageClient("[ADMIN] Set " + target.name + "'s armour to " + armour, admin, COLOUR_GREEN);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,15 +439,15 @@ function explodePlayer(admin, targetName) {
|
|||||||
if (target && target.player) {
|
if (target && target.player) {
|
||||||
let pos = target.player.position;
|
let pos = target.player.position;
|
||||||
gta.createExplosion(pos, 0, 10.0);
|
gta.createExplosion(pos, 0, 10.0);
|
||||||
message("[ADMIN] " + target.name + " was exploded by " + admin.name, [255, 100, 100, 255]);
|
message("[ADMIN] " + target.name + " was exploded by " + admin.name, COLOUR_RED);
|
||||||
console.log("[Admin] " + admin.name + " exploded " + target.name);
|
console.log("[Admin] " + admin.name + " exploded " + target.name);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[ADMIN] Player not found: " + targetName, admin, [255, 100, 100, 255]);
|
messageClient("[ADMIN] Player not found: " + targetName, admin, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAdmins(client) {
|
function showAdmins(client) {
|
||||||
messageClient("=== ONLINE ADMINS ===", client, [255, 200, 100, 255]);
|
messageClient("=== ONLINE ADMINS ===", client, COLOUR_ORANGE);
|
||||||
let clients = getClients();
|
let clients = getClients();
|
||||||
let onlineAdmins = [];
|
let onlineAdmins = [];
|
||||||
|
|
||||||
@@ -447,9 +458,9 @@ function showAdmins(client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (onlineAdmins.length > 0) {
|
if (onlineAdmins.length > 0) {
|
||||||
messageClient(onlineAdmins.join(", "), client, [100, 255, 100, 255]);
|
messageClient(onlineAdmins.join(", "), client, COLOUR_GREEN);
|
||||||
} else {
|
} else {
|
||||||
messageClient("No admins online", client, [200, 200, 200, 255]);
|
messageClient("No admins online", client, COLOUR_GREY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,34 +468,33 @@ function reportPlayer(client, reportMessage) {
|
|||||||
let clients = getClients();
|
let clients = getClients();
|
||||||
console.log("[Report] " + client.name + ": " + reportMessage);
|
console.log("[Report] " + client.name + ": " + reportMessage);
|
||||||
|
|
||||||
// Notify all online admins
|
|
||||||
for (let i = 0; i < clients.length; i++) {
|
for (let i = 0; i < clients.length; i++) {
|
||||||
if (isAdmin(clients[i])) {
|
if (isAdmin(clients[i])) {
|
||||||
messageClient("[REPORT] " + client.name + ": " + reportMessage, clients[i], [255, 200, 100, 255]);
|
messageClient("[REPORT] " + client.name + ": " + reportMessage, clients[i], COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageClient("[REPORT] Your report has been sent to admins", client, [100, 255, 100, 255]);
|
messageClient("[REPORT] Your report has been sent to admins", client, COLOUR_GREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAdminHelp(client) {
|
function showAdminHelp(client) {
|
||||||
messageClient("=== ADMIN COMMANDS ===", client, [255, 200, 100, 255]);
|
messageClient("=== ADMIN COMMANDS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("/kick <player> [reason] - Kick a player", client, [200, 200, 200, 255]);
|
messageClient("/kick <player> [reason] - Kick a player", client, COLOUR_GREY);
|
||||||
messageClient("/ban <player> [reason] - Ban a player", client, [200, 200, 200, 255]);
|
messageClient("/ban <player> [reason] - Ban a player", client, COLOUR_GREY);
|
||||||
messageClient("/unban <player> - Unban a player", client, [200, 200, 200, 255]);
|
messageClient("/unban <player> - Unban a player", client, COLOUR_GREY);
|
||||||
messageClient("/mute <player> - Mute a player", client, [200, 200, 200, 255]);
|
messageClient("/mute <player> - Mute a player", client, COLOUR_GREY);
|
||||||
messageClient("/unmute <player> - Unmute a player", client, [200, 200, 200, 255]);
|
messageClient("/unmute <player> - Unmute a player", client, COLOUR_GREY);
|
||||||
messageClient("/freeze <player> - Freeze a player", client, [200, 200, 200, 255]);
|
messageClient("/freeze <player> - Freeze a player", client, COLOUR_GREY);
|
||||||
messageClient("/unfreeze <player> - Unfreeze a player", client, [200, 200, 200, 255]);
|
messageClient("/unfreeze <player> - Unfreeze a player", client, COLOUR_GREY);
|
||||||
messageClient("/slap <player> - Slap a player", client, [200, 200, 200, 255]);
|
messageClient("/slap <player> - Slap a player", client, COLOUR_GREY);
|
||||||
messageClient("/goto <player> - Teleport to a player", client, [200, 200, 200, 255]);
|
messageClient("/goto <player> - Teleport to a player", client, COLOUR_GREY);
|
||||||
messageClient("/bring <player> - Bring a player to you", client, [200, 200, 200, 255]);
|
messageClient("/bring <player> - Bring a player to you", client, COLOUR_GREY);
|
||||||
messageClient("/announce <msg> - Server announcement", client, [200, 200, 200, 255]);
|
messageClient("/announce <msg> - Server announcement", client, COLOUR_GREY);
|
||||||
messageClient("/setadmin <player> - Grant admin rights", client, [200, 200, 200, 255]);
|
messageClient("/setadmin <player> - Grant admin rights", client, COLOUR_GREY);
|
||||||
messageClient("/getip <player> - Get player's IP", client, [200, 200, 200, 255]);
|
messageClient("/getip <player> - Get player's IP", client, COLOUR_GREY);
|
||||||
messageClient("/sethealth <player> <hp> - Set health", client, [200, 200, 200, 255]);
|
messageClient("/sethealth <player> <hp> - Set health", client, COLOUR_GREY);
|
||||||
messageClient("/setarmour <player> <armor> - Set armour", client, [200, 200, 200, 255]);
|
messageClient("/setarmour <player> <armor> - Set armour", client, COLOUR_GREY);
|
||||||
messageClient("/explode <player> - Explode a player", client, [200, 200, 200, 255]);
|
messageClient("/explode <player> - Explode a player", client, COLOUR_GREY);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[Admin] Server script loaded!");
|
console.log("[Admin] Server script loaded!");
|
||||||
|
|||||||
@@ -3,16 +3,18 @@
|
|||||||
// Enhanced chat system with private messages, colors, and special features
|
// Enhanced chat system with private messages, colors, and special features
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// Chat colors for different message types
|
// Chat colors using toColour for integer format
|
||||||
const chatColors = {
|
const COLOUR_WHITE = toColour(255, 255, 255, 255);
|
||||||
normal: [255, 255, 255, 255],
|
const COLOUR_ACTION = toColour(200, 100, 255, 255);
|
||||||
action: [200, 100, 255, 255],
|
const COLOUR_WHISPER = toColour(255, 255, 100, 255);
|
||||||
whisper: [255, 255, 100, 255],
|
const COLOUR_SHOUT = toColour(255, 100, 100, 255);
|
||||||
shout: [255, 100, 100, 255],
|
const COLOUR_OOC = toColour(150, 150, 150, 255);
|
||||||
ooc: [150, 150, 150, 255],
|
const COLOUR_ADMIN = toColour(255, 100, 100, 255);
|
||||||
admin: [255, 100, 100, 255],
|
const COLOUR_SYSTEM = toColour(100, 200, 255, 255);
|
||||||
system: [100, 200, 255, 255]
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
};
|
const COLOUR_ERROR = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_GRAY = toColour(200, 200, 200, 255);
|
||||||
|
const COLOUR_LOCAL = toColour(200, 255, 200, 255);
|
||||||
|
|
||||||
// Chat history (for logging purposes)
|
// Chat history (for logging purposes)
|
||||||
let chatHistory = [];
|
let chatHistory = [];
|
||||||
@@ -29,7 +31,7 @@ addEventHandler("OnResourceStart", function(event, resource) {
|
|||||||
addEventHandler("OnPlayerChat", function(event, client, messageText) {
|
addEventHandler("OnPlayerChat", function(event, client, messageText) {
|
||||||
// Format and broadcast the chat message
|
// Format and broadcast the chat message
|
||||||
let formattedMessage = client.name + ": " + messageText;
|
let formattedMessage = client.name + ": " + messageText;
|
||||||
message(formattedMessage, chatColors.normal);
|
message(formattedMessage, COLOUR_WHITE);
|
||||||
|
|
||||||
// Log to history
|
// Log to history
|
||||||
logChat(client.name, messageText, "chat");
|
logChat(client.name, messageText, "chat");
|
||||||
@@ -54,10 +56,10 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (messageText.length > 0) {
|
if (messageText.length > 0) {
|
||||||
sendPrivateMessage(client, targetName, messageText);
|
sendPrivateMessage(client, targetName, messageText);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /pm <player> <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /pm <player> <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /pm <player> <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /pm <player> <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -65,20 +67,20 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "action":
|
case "action":
|
||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
let actionMessage = "* " + client.name + " " + params;
|
let actionMessage = "* " + client.name + " " + params;
|
||||||
message(actionMessage, chatColors.action);
|
message(actionMessage, COLOUR_ACTION);
|
||||||
logChat(client.name, params, "action");
|
logChat(client.name, params, "action");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /me <action>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /me <action>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "do":
|
case "do":
|
||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
let doMessage = "* " + params + " (" + client.name + ")";
|
let doMessage = "* " + params + " (" + client.name + ")";
|
||||||
message(doMessage, chatColors.action);
|
message(doMessage, COLOUR_ACTION);
|
||||||
logChat(client.name, params, "do");
|
logChat(client.name, params, "do");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /do <description>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /do <description>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -86,10 +88,10 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "s":
|
case "s":
|
||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
let shoutMessage = client.name + " shouts: " + params.toUpperCase() + "!";
|
let shoutMessage = client.name + " shouts: " + params.toUpperCase() + "!";
|
||||||
message(shoutMessage, chatColors.shout);
|
message(shoutMessage, COLOUR_SHOUT);
|
||||||
logChat(client.name, params, "shout");
|
logChat(client.name, params, "shout");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /shout <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /shout <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -97,10 +99,10 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "b":
|
case "b":
|
||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
let oocMessage = "(( " + client.name + ": " + params + " ))";
|
let oocMessage = "(( " + client.name + ": " + params + " ))";
|
||||||
message(oocMessage, chatColors.ooc);
|
message(oocMessage, COLOUR_OOC);
|
||||||
logChat(client.name, params, "ooc");
|
logChat(client.name, params, "ooc");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /ooc <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /ooc <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
sendLocalMessage(client, params);
|
sendLocalMessage(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /local <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /local <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -118,7 +120,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
replyToLastPM(client, params);
|
replyToLastPM(client, params);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /r <message>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /r <message>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -136,22 +138,22 @@ function sendPrivateMessage(sender, targetName, messageText) {
|
|||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
if (target.index === sender.index) {
|
if (target.index === sender.index) {
|
||||||
messageClient("[PM] You cannot message yourself!", sender, [255, 100, 100, 255]);
|
messageClient("[PM] You cannot message yourself!", sender, COLOUR_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to target
|
// Send to target
|
||||||
messageClient("[PM from " + sender.name + "]: " + messageText, target, chatColors.whisper);
|
messageClient("[PM from " + sender.name + "]: " + messageText, target, COLOUR_WHISPER);
|
||||||
|
|
||||||
// Confirm to sender
|
// Confirm to sender
|
||||||
messageClient("[PM to " + target.name + "]: " + messageText, sender, chatColors.whisper);
|
messageClient("[PM to " + target.name + "]: " + messageText, sender, COLOUR_WHISPER);
|
||||||
|
|
||||||
// Store for reply function
|
// Store for reply function
|
||||||
lastPMSender[target.index] = sender.index;
|
lastPMSender[target.index] = sender.index;
|
||||||
|
|
||||||
logChat(sender.name, "-> " + target.name + ": " + messageText, "pm");
|
logChat(sender.name, "-> " + target.name + ": " + messageText, "pm");
|
||||||
} else {
|
} else {
|
||||||
messageClient("[PM] Player not found: " + targetName, sender, [255, 100, 100, 255]);
|
messageClient("[PM] Player not found: " + targetName, sender, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,16 +173,16 @@ function replyToLastPM(client, messageText) {
|
|||||||
if (target) {
|
if (target) {
|
||||||
sendPrivateMessage(client, target.name, messageText);
|
sendPrivateMessage(client, target.name, messageText);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[PM] The player you're trying to reply to is no longer online", client, [255, 100, 100, 255]);
|
messageClient("[PM] The player you're trying to reply to is no longer online", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[PM] No one has messaged you yet", client, [255, 100, 100, 255]);
|
messageClient("[PM] No one has messaged you yet", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendLocalMessage(sender, messageText) {
|
function sendLocalMessage(sender, messageText) {
|
||||||
if (!sender.player) {
|
if (!sender.player) {
|
||||||
messageClient("[LOCAL] You need to spawn first!", sender, [255, 100, 100, 255]);
|
messageClient("[LOCAL] You need to spawn first!", sender, COLOUR_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +199,7 @@ function sendLocalMessage(sender, messageText) {
|
|||||||
|
|
||||||
if (distance <= localRange) {
|
if (distance <= localRange) {
|
||||||
let localMessage = "(Local) " + sender.name + ": " + messageText;
|
let localMessage = "(Local) " + sender.name + ": " + messageText;
|
||||||
messageClient(localMessage, client, [200, 255, 200, 255]);
|
messageClient(localMessage, client, COLOUR_LOCAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,9 +221,9 @@ function findPlayer(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getDistance(pos1, pos2) {
|
function getDistance(pos1, pos2) {
|
||||||
let dx = pos1[0] - pos2[0];
|
let dx = pos1.x - pos2.x;
|
||||||
let dy = pos1[1] - pos2[1];
|
let dy = pos1.y - pos2.y;
|
||||||
let dz = pos1[2] - pos2[2];
|
let dz = pos1.z - pos2.z;
|
||||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
// Handles player spawning, basic commands, and player management
|
// Handles player spawning, basic commands, and player management
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Define colors using toColour
|
||||||
|
const COLOUR_WHITE = toColour(255, 255, 255, 255);
|
||||||
|
const COLOUR_RED = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_GREEN = toColour(100, 255, 100, 255);
|
||||||
|
const COLOUR_BLUE = toColour(100, 200, 255, 255);
|
||||||
|
const COLOUR_YELLOW = toColour(255, 255, 100, 255);
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_GREY = toColour(200, 200, 200, 255);
|
||||||
|
const COLOUR_PINK = toColour(255, 150, 200, 255);
|
||||||
|
|
||||||
// Spawn points around Liberty City (GTA IV)
|
// Spawn points around Liberty City (GTA IV)
|
||||||
const spawnPoints = [
|
const spawnPoints = [
|
||||||
{ x: -252.0, y: 947.0, z: 15.0, name: "Star Junction" },
|
{ x: -252.0, y: 947.0, z: 15.0, name: "Star Junction" },
|
||||||
@@ -59,11 +69,11 @@ addEventHandler("OnPlayerJoined", function(event, client) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Welcome message
|
// Welcome message
|
||||||
messageClient("[SERVER] Welcome to Liberty City Freeroam, " + client.name + "!", client, [255, 200, 100, 255]);
|
messageClient("[SERVER] Welcome to Liberty City Freeroam, " + client.name + "!", client, COLOUR_ORANGE);
|
||||||
messageClient("[SERVER] Type /help for available commands", client, [200, 200, 200, 255]);
|
messageClient("[SERVER] Type /help for available commands", client, COLOUR_GREY);
|
||||||
|
|
||||||
// Spawn the player
|
// Spawn the player
|
||||||
spawnPlayer(client);
|
spawnPlayerAtRandom(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
// When a player quits
|
// When a player quits
|
||||||
@@ -71,7 +81,7 @@ addEventHandler("OnPlayerQuit", function(event, client, reason) {
|
|||||||
console.log("[Freeroam] Player quit: " + client.name + " - Reason: " + reason);
|
console.log("[Freeroam] Player quit: " + client.name + " - Reason: " + reason);
|
||||||
|
|
||||||
// Broadcast quit message
|
// Broadcast quit message
|
||||||
message("[SERVER] " + client.name + " has left the server", [255, 150, 100, 255]);
|
message("[SERVER] " + client.name + " has left the server", COLOUR_PINK);
|
||||||
|
|
||||||
// Clean up player data
|
// Clean up player data
|
||||||
delete playerData[client.index];
|
delete playerData[client.index];
|
||||||
@@ -91,14 +101,14 @@ addEventHandler("OnPedWasted", function(event, ped, killer, weapon) {
|
|||||||
let killerClient = getClientFromPed(killer);
|
let killerClient = getClientFromPed(killer);
|
||||||
if (killerClient && playerData[killerClient.index]) {
|
if (killerClient && playerData[killerClient.index]) {
|
||||||
playerData[killerClient.index].kills++;
|
playerData[killerClient.index].kills++;
|
||||||
message("[KILL] " + killerClient.name + " killed " + client.name, [255, 100, 100, 255]);
|
message("[KILL] " + killerClient.name + " killed " + client.name, COLOUR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respawn after delay
|
// Respawn after delay
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (client && client.player) {
|
if (client) {
|
||||||
spawnPlayer(client);
|
spawnPlayerAtRandom(client);
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
@@ -115,15 +125,15 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
|
|
||||||
case "spawn":
|
case "spawn":
|
||||||
case "respawn":
|
case "respawn":
|
||||||
spawnPlayer(client);
|
spawnPlayerAtRandom(client);
|
||||||
messageClient("[SERVER] You have been respawned!", client, [100, 255, 100, 255]);
|
messageClient("[SERVER] You have been respawned!", client, COLOUR_GREEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "kill":
|
case "kill":
|
||||||
case "suicide":
|
case "suicide":
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
client.player.health = 0;
|
client.player.health = 0;
|
||||||
messageClient("[SERVER] You killed yourself!", client, [255, 100, 100, 255]);
|
messageClient("[SERVER] You killed yourself!", client, COLOUR_RED);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -131,7 +141,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "position":
|
case "position":
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
let pos = client.player.position;
|
let pos = client.player.position;
|
||||||
messageClient("[POS] X: " + pos[0].toFixed(2) + " Y: " + pos[1].toFixed(2) + " Z: " + pos[2].toFixed(2), client, [200, 200, 255, 255]);
|
messageClient("[POS] X: " + pos.x.toFixed(2) + " Y: " + pos.y.toFixed(2) + " Z: " + pos.z.toFixed(2), client, COLOUR_BLUE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -147,7 +157,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "heal":
|
case "heal":
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
client.player.health = 100;
|
client.player.health = 100;
|
||||||
messageClient("[SERVER] You have been healed!", client, [100, 255, 100, 255]);
|
messageClient("[SERVER] You have been healed!", client, COLOUR_GREEN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -155,7 +165,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "armor":
|
case "armor":
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
client.player.armour = 100;
|
client.player.armour = 100;
|
||||||
messageClient("[SERVER] You have been given armour!", client, [100, 200, 255, 255]);
|
messageClient("[SERVER] You have been given armour!", client, COLOUR_BLUE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -165,28 +175,28 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (!isNaN(skinId)) {
|
if (!isNaN(skinId)) {
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
client.player.modelIndex = skinId;
|
client.player.modelIndex = skinId;
|
||||||
messageClient("[SERVER] Skin changed to: " + skinId, client, [200, 200, 255, 255]);
|
messageClient("[SERVER] Skin changed to: " + skinId, client, COLOUR_BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /skin <skin_id>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /skin <skin_id>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "weapons":
|
case "weapons":
|
||||||
case "giveweapons":
|
case "giveweapons":
|
||||||
giveWeapons(client);
|
giveWeapons(client);
|
||||||
messageClient("[SERVER] You have been given weapons!", client, [255, 200, 100, 255]);
|
messageClient("[SERVER] You have been given weapons!", client, COLOUR_ORANGE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "flip":
|
case "flip":
|
||||||
if (client.player && client.player.vehicle) {
|
if (client.player && client.player.vehicle) {
|
||||||
let veh = client.player.vehicle;
|
let veh = client.player.vehicle;
|
||||||
let rot = veh.rotation;
|
let rot = veh.rotation;
|
||||||
veh.rotation = [0, 0, rot[2]];
|
veh.rotation = new Vec3(0, 0, rot.z);
|
||||||
messageClient("[SERVER] Vehicle flipped!", client, [100, 255, 100, 255]);
|
messageClient("[SERVER] Vehicle flipped!", client, COLOUR_GREEN);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[SERVER] You need to be in a vehicle!", client, [255, 100, 100, 255]);
|
messageClient("[SERVER] You need to be in a vehicle!", client, COLOUR_RED);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -194,16 +204,16 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "repair":
|
case "repair":
|
||||||
if (client.player && client.player.vehicle) {
|
if (client.player && client.player.vehicle) {
|
||||||
client.player.vehicle.health = 1000;
|
client.player.vehicle.health = 1000;
|
||||||
messageClient("[SERVER] Vehicle repaired!", client, [100, 255, 100, 255]);
|
messageClient("[SERVER] Vehicle repaired!", client, COLOUR_GREEN);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[SERVER] You need to be in a vehicle!", client, [255, 100, 100, 255]);
|
messageClient("[SERVER] You need to be in a vehicle!", client, COLOUR_RED);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "eject":
|
case "eject":
|
||||||
if (client.player && client.player.vehicle) {
|
if (client.player && client.player.vehicle) {
|
||||||
client.player.removeFromVehicle();
|
client.player.removeFromVehicle();
|
||||||
messageClient("[SERVER] Ejected from vehicle!", client, [200, 200, 255, 255]);
|
messageClient("[SERVER] Ejected from vehicle!", client, COLOUR_BLUE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -217,15 +227,19 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
function spawnPlayer(client) {
|
function spawnPlayerAtRandom(client) {
|
||||||
// Select random spawn point
|
// Select random spawn point
|
||||||
let spawn = spawnPoints[Math.floor(Math.random() * spawnPoints.length)];
|
let spawn = spawnPoints[Math.floor(Math.random() * spawnPoints.length)];
|
||||||
|
|
||||||
// Select random skin
|
// Select random skin
|
||||||
let skin = playerSkins[Math.floor(Math.random() * playerSkins.length)];
|
let skin = playerSkins[Math.floor(Math.random() * playerSkins.length)];
|
||||||
|
|
||||||
// Spawn the player
|
// Create spawn position vector
|
||||||
client.spawn([spawn.x, spawn.y, spawn.z], 0, skin);
|
let spawnPos = new Vec3(spawn.x, spawn.y, spawn.z);
|
||||||
|
|
||||||
|
// Spawn the player using the correct function
|
||||||
|
client.despawnPlayer();
|
||||||
|
client.spawnPlayer(spawnPos, 0.0, skin);
|
||||||
|
|
||||||
// Give basic weapons after short delay
|
// Give basic weapons after short delay
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@@ -245,76 +259,74 @@ function giveStarterWeapons(client) {
|
|||||||
if (!client || !client.player) return;
|
if (!client || !client.player) return;
|
||||||
|
|
||||||
// Give basic starter weapons (GTA IV weapon IDs)
|
// Give basic starter weapons (GTA IV weapon IDs)
|
||||||
// Pistol
|
client.player.giveWeapon(5, 100); // Pistol with 100 ammo
|
||||||
client.giveWeapon(5, 100); // Pistol with 100 ammo
|
client.player.giveWeapon(11, 200); // Micro SMG with 200 ammo
|
||||||
// SMG
|
|
||||||
client.giveWeapon(11, 200); // Micro SMG with 200 ammo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function giveWeapons(client) {
|
function giveWeapons(client) {
|
||||||
if (!client || !client.player) return;
|
if (!client || !client.player) return;
|
||||||
|
|
||||||
// GTA IV Weapon IDs
|
// GTA IV Weapon IDs
|
||||||
client.giveWeapon(1, 1); // Baseball Bat
|
client.player.giveWeapon(1, 1); // Baseball Bat
|
||||||
client.giveWeapon(2, 1); // Knife
|
client.player.giveWeapon(2, 1); // Knife
|
||||||
client.giveWeapon(5, 500); // Pistol
|
client.player.giveWeapon(5, 500); // Pistol
|
||||||
client.giveWeapon(6, 500); // Desert Eagle
|
client.player.giveWeapon(6, 500); // Desert Eagle
|
||||||
client.giveWeapon(9, 200); // Shotgun
|
client.player.giveWeapon(9, 200); // Shotgun
|
||||||
client.giveWeapon(10, 200); // Combat Shotgun
|
client.player.giveWeapon(10, 200); // Combat Shotgun
|
||||||
client.giveWeapon(11, 500); // Micro SMG
|
client.player.giveWeapon(11, 500); // Micro SMG
|
||||||
client.giveWeapon(12, 500); // SMG
|
client.player.giveWeapon(12, 500); // SMG
|
||||||
client.giveWeapon(14, 500); // Assault Rifle
|
client.player.giveWeapon(14, 500); // Assault Rifle
|
||||||
client.giveWeapon(15, 500); // Carbine Rifle
|
client.player.giveWeapon(15, 500); // Carbine Rifle
|
||||||
client.giveWeapon(16, 100); // Sniper Rifle
|
client.player.giveWeapon(16, 100); // Sniper Rifle
|
||||||
client.giveWeapon(18, 20); // RPG
|
client.player.giveWeapon(18, 20); // RPG
|
||||||
client.giveWeapon(19, 20); // Grenades
|
client.player.giveWeapon(19, 20); // Grenades
|
||||||
client.giveWeapon(20, 20); // Molotov
|
client.player.giveWeapon(20, 20); // Molotov
|
||||||
}
|
}
|
||||||
|
|
||||||
function showHelp(client) {
|
function showHelp(client) {
|
||||||
messageClient("=== AVAILABLE COMMANDS ===", client, [255, 200, 100, 255]);
|
messageClient("=== AVAILABLE COMMANDS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("/spawn - Respawn at a random location", client, [200, 200, 200, 255]);
|
messageClient("/spawn - Respawn at a random location", client, COLOUR_GREY);
|
||||||
messageClient("/kill - Kill yourself", client, [200, 200, 200, 255]);
|
messageClient("/kill - Kill yourself", client, COLOUR_GREY);
|
||||||
messageClient("/pos - Show your current position", client, [200, 200, 200, 255]);
|
messageClient("/pos - Show your current position", client, COLOUR_GREY);
|
||||||
messageClient("/stats - Show your statistics", client, [200, 200, 200, 255]);
|
messageClient("/stats - Show your statistics", client, COLOUR_GREY);
|
||||||
messageClient("/players - Show online players", client, [200, 200, 200, 255]);
|
messageClient("/players - Show online players", client, COLOUR_GREY);
|
||||||
messageClient("/heal - Restore your health", client, [200, 200, 200, 255]);
|
messageClient("/heal - Restore your health", client, COLOUR_GREY);
|
||||||
messageClient("/armour - Give yourself armour", client, [200, 200, 200, 255]);
|
messageClient("/armour - Give yourself armour", client, COLOUR_GREY);
|
||||||
messageClient("/weapons - Get all weapons", client, [200, 200, 200, 255]);
|
messageClient("/weapons - Get all weapons", client, COLOUR_GREY);
|
||||||
messageClient("/skin <id> - Change your skin", client, [200, 200, 200, 255]);
|
messageClient("/skin <id> - Change your skin", client, COLOUR_GREY);
|
||||||
messageClient("/flip - Flip your vehicle", client, [200, 200, 200, 255]);
|
messageClient("/flip - Flip your vehicle", client, COLOUR_GREY);
|
||||||
messageClient("/fix - Repair your vehicle", client, [200, 200, 200, 255]);
|
messageClient("/fix - Repair your vehicle", client, COLOUR_GREY);
|
||||||
messageClient("/eject - Exit vehicle", client, [200, 200, 200, 255]);
|
messageClient("/eject - Exit vehicle", client, COLOUR_GREY);
|
||||||
messageClient("=== VEHICLE COMMANDS ===", client, [255, 200, 100, 255]);
|
messageClient("=== VEHICLE COMMANDS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("/v <name> - Spawn a vehicle", client, [200, 200, 200, 255]);
|
messageClient("/v <name> - Spawn a vehicle", client, COLOUR_GREY);
|
||||||
messageClient("/dv - Delete your vehicle", client, [200, 200, 200, 255]);
|
messageClient("/dv - Delete your vehicle", client, COLOUR_GREY);
|
||||||
messageClient("=== TELEPORT COMMANDS ===", client, [255, 200, 100, 255]);
|
messageClient("=== TELEPORT COMMANDS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("/tp <location> - Teleport to location", client, [200, 200, 200, 255]);
|
messageClient("/tp <location> - Teleport to location", client, COLOUR_GREY);
|
||||||
messageClient("/tplist - List teleport locations", client, [200, 200, 200, 255]);
|
messageClient("/tplist - List teleport locations", client, COLOUR_GREY);
|
||||||
messageClient("=== WORLD COMMANDS ===", client, [255, 200, 100, 255]);
|
messageClient("=== WORLD COMMANDS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("/weather <id> - Change weather", client, [200, 200, 200, 255]);
|
messageClient("/weather <id> - Change weather", client, COLOUR_GREY);
|
||||||
messageClient("/time <hour> - Change time", client, [200, 200, 200, 255]);
|
messageClient("/time <hour> - Change time", client, COLOUR_GREY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showStats(client) {
|
function showStats(client) {
|
||||||
let data = playerData[client.index];
|
let data = playerData[client.index];
|
||||||
if (data) {
|
if (data) {
|
||||||
messageClient("=== YOUR STATISTICS ===", client, [255, 200, 100, 255]);
|
messageClient("=== YOUR STATISTICS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("Kills: " + data.kills, client, [100, 255, 100, 255]);
|
messageClient("Kills: " + data.kills, client, COLOUR_GREEN);
|
||||||
messageClient("Deaths: " + data.deaths, client, [255, 100, 100, 255]);
|
messageClient("Deaths: " + data.deaths, client, COLOUR_RED);
|
||||||
let kd = data.deaths > 0 ? (data.kills / data.deaths).toFixed(2) : data.kills.toFixed(2);
|
let kd = data.deaths > 0 ? (data.kills / data.deaths).toFixed(2) : data.kills.toFixed(2);
|
||||||
messageClient("K/D Ratio: " + kd, client, [200, 200, 255, 255]);
|
messageClient("K/D Ratio: " + kd, client, COLOUR_BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showOnlinePlayers(client) {
|
function showOnlinePlayers(client) {
|
||||||
let clients = getClients();
|
let clients = getClients();
|
||||||
messageClient("=== ONLINE PLAYERS (" + clients.length + ") ===", client, [255, 200, 100, 255]);
|
messageClient("=== ONLINE PLAYERS (" + clients.length + ") ===", client, COLOUR_ORANGE);
|
||||||
|
|
||||||
for (let i = 0; i < clients.length; i++) {
|
for (let i = 0; i < clients.length; i++) {
|
||||||
let c = clients[i];
|
let c = clients[i];
|
||||||
let data = playerData[c.index] || { kills: 0, deaths: 0 };
|
let data = playerData[c.index] || { kills: 0, deaths: 0 };
|
||||||
messageClient(c.name + " - K: " + data.kills + " D: " + data.deaths, client, [200, 200, 200, 255]);
|
messageClient(c.name + " - K: " + data.kills + " D: " + data.deaths, client, COLOUR_GREY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
970
resources/modmenu/client.js
Normal file
970
resources/modmenu/client.js
Normal file
@@ -0,0 +1,970 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 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 (RGBA)
|
||||||
|
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 = [];
|
||||||
|
|
||||||
|
// 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: "God Mode", action: "toggle", target: "godMode", state: false },
|
||||||
|
{ label: "Never Wanted", action: "toggle", target: "neverWanted", 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" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
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 } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
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 = [];
|
||||||
|
// Show cursor - use gui if available
|
||||||
|
if (typeof gui !== "undefined" && gui.showCursor) {
|
||||||
|
gui.showCursor(true, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof gui !== "undefined" && gui.showCursor) {
|
||||||
|
gui.showCursor(false, 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) {
|
||||||
|
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;
|
||||||
|
if (typeof gui !== "undefined" && gui.showCursor) {
|
||||||
|
gui.showCursor(false, 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
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Selected player for network options
|
||||||
|
let selectedPlayer = null;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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 "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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPlayerMenu(playerData) {
|
||||||
|
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 - Using correct GTAC drawing API
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
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
|
||||||
|
drawRect(menu.x - 5, menu.y - 5, menu.width + 10, totalHeight + 10, colors.background);
|
||||||
|
|
||||||
|
// Draw header
|
||||||
|
drawRect(menu.x, menu.y, menu.width, menu.headerHeight, colors.header);
|
||||||
|
drawText(title, menu.x + menu.width / 2, menu.y + 10, colors.headerText, true, 1.0);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
drawRect(menu.x, yPos, menu.width, menu.itemHeight, bgColor);
|
||||||
|
|
||||||
|
// Build label with state indicators
|
||||||
|
let label = item.label;
|
||||||
|
|
||||||
|
if (item.action === "toggle") {
|
||||||
|
let state = toggleStates[item.target];
|
||||||
|
label += state ? " [ON]" : " [OFF]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.action === "handling" && handlingMods[item.target] !== undefined) {
|
||||||
|
label += " [" + handlingMods[item.target].toFixed(1) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.action === "submenu") {
|
||||||
|
label += " >>";
|
||||||
|
}
|
||||||
|
|
||||||
|
drawText(label, menu.x + 15, yPos + 8, textColor, false, 0.9);
|
||||||
|
|
||||||
|
yPos += menu.itemHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw footer
|
||||||
|
drawRect(menu.x, yPos, menu.width, menu.footerHeight, colors.footer);
|
||||||
|
drawText("UP/DOWN: Navigate | ENTER: Select | BACKSPACE: Back", menu.x + menu.width / 2, yPos + 8, colors.footerText, true, 0.6);
|
||||||
|
|
||||||
|
// Draw scroll indicator
|
||||||
|
if (items.length > menu.maxVisibleItems) {
|
||||||
|
let scrollText = (scrollOffset + 1) + "-" + Math.min(scrollOffset + visibleCount, items.length) + " / " + items.length;
|
||||||
|
drawText(scrollText, menu.x + menu.width - 50, menu.y + 12, colors.subText, false, 0.7);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper drawing functions using native drawing
|
||||||
|
function drawRect(x, y, width, height, colour) {
|
||||||
|
// Use natives for drawing rectangles
|
||||||
|
if (typeof natives !== "undefined" && natives.drawRect) {
|
||||||
|
// GTA IV native drawing - normalized coordinates (0-1)
|
||||||
|
let screenW = game.width || 1920;
|
||||||
|
let screenH = game.height || 1080;
|
||||||
|
|
||||||
|
let nx = (x + width/2) / screenW;
|
||||||
|
let ny = (y + height/2) / screenH;
|
||||||
|
let nw = width / screenW;
|
||||||
|
let nh = height / screenH;
|
||||||
|
|
||||||
|
// Extract RGBA from colour
|
||||||
|
let r = (colour >> 24) & 0xFF;
|
||||||
|
let g = (colour >> 16) & 0xFF;
|
||||||
|
let b = (colour >> 8) & 0xFF;
|
||||||
|
let a = colour & 0xFF;
|
||||||
|
|
||||||
|
natives.drawRect(nx, ny, nw, nh, r, g, b, a);
|
||||||
|
} else {
|
||||||
|
// Fallback to graphics drawing
|
||||||
|
try {
|
||||||
|
graphics.drawRectangle(null, [x, y], [width, height], colour, colour, 0, 0, 0);
|
||||||
|
} catch(e) {
|
||||||
|
// Silent fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawText(text, x, y, colour, centered, scale) {
|
||||||
|
// Use natives for drawing text
|
||||||
|
if (typeof natives !== "undefined" && natives.setTextScale) {
|
||||||
|
let screenW = game.width || 1920;
|
||||||
|
let screenH = game.height || 1080;
|
||||||
|
|
||||||
|
let nx = x / screenW;
|
||||||
|
let ny = y / screenH;
|
||||||
|
|
||||||
|
let r = (colour >> 24) & 0xFF;
|
||||||
|
let g = (colour >> 16) & 0xFF;
|
||||||
|
let b = (colour >> 8) & 0xFF;
|
||||||
|
let a = colour & 0xFF;
|
||||||
|
|
||||||
|
natives.setTextFont(0);
|
||||||
|
natives.setTextScale(scale || 0.35, scale || 0.35);
|
||||||
|
natives.setTextColour(r, g, b, a);
|
||||||
|
if (centered) {
|
||||||
|
natives.setTextCentre(true);
|
||||||
|
}
|
||||||
|
natives.setTextDropshadow(2, 0, 0, 0, 255);
|
||||||
|
natives.displayTextWithLiteralString(nx, ny, "STRING", text);
|
||||||
|
} else {
|
||||||
|
// Fallback
|
||||||
|
try {
|
||||||
|
graphics.drawText(text, [x, y], colour, scale || 1.0, "arial", centered || false);
|
||||||
|
} catch(e) {
|
||||||
|
// Silent fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// NOTIFICATIONS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
let notifications = [];
|
||||||
|
|
||||||
|
function showNotification(text) {
|
||||||
|
notifications.push({
|
||||||
|
text: text,
|
||||||
|
time: Date.now(),
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw notifications
|
||||||
|
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);
|
||||||
|
|
||||||
|
drawRect(10, yPos, 300, 30, bgColor);
|
||||||
|
drawText(notif.text, 20, yPos + 6, textColor, false, 0.8);
|
||||||
|
|
||||||
|
yPos += 35;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean expired notifications
|
||||||
|
notifications = notifications.filter(function(n) {
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vehicle God Mode
|
||||||
|
if (localPlayer.vehicle && 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");
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[ModMenu] Client loaded - Press F5 to open menu!");
|
||||||
6
resources/modmenu/meta.xml
Normal file
6
resources/modmenu/meta.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<meta>
|
||||||
|
<info author="GTAConnected Server" type="script" version="1.0.0" description="Interactive mod menu with vehicle spawner, player options, and more" />
|
||||||
|
<script src="server.js" type="server" language="javascript" />
|
||||||
|
<script src="client.js" type="client" language="javascript" />
|
||||||
|
</meta>
|
||||||
583
resources/modmenu/server.js
Normal file
583
resources/modmenu/server.js
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// MOD MENU - Server Side
|
||||||
|
// Handles all server-side actions triggered from the client menu
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Color constants using toColour for integer format
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_WORLD = toColour(100, 200, 255, 255);
|
||||||
|
|
||||||
|
// 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, COLOUR_ORANGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
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)];
|
||||||
|
let spawnPos = new Vec3(spawn.x, spawn.y, spawn.z);
|
||||||
|
client.despawnPlayer();
|
||||||
|
client.spawnPlayer(spawnPos, 0.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.x + (Math.sin(heading) * 4);
|
||||||
|
let spawnY = pos.y + (Math.cos(heading) * 4);
|
||||||
|
let spawnZ = pos.z + 1;
|
||||||
|
|
||||||
|
// Create vehicle
|
||||||
|
let spawnPos = new Vec3(spawnX, spawnY, spawnZ);
|
||||||
|
let vehicle = gta.createVehicle(modelHash, spawnPos, 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 = new Vec3(0, 0, rot.z);
|
||||||
|
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;
|
||||||
|
let newPos = new Vec3(
|
||||||
|
pos.x + (Math.sin(heading) * boost),
|
||||||
|
pos.y + (Math.cos(heading) * boost),
|
||||||
|
pos.z
|
||||||
|
);
|
||||||
|
vehicle.position = newPos;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newPos = new Vec3(x, y, z);
|
||||||
|
if (client.player.vehicle) {
|
||||||
|
client.player.vehicle.position = newPos;
|
||||||
|
} else {
|
||||||
|
client.player.position = newPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
let newPos = new Vec3(pos.x + 3, pos.y, pos.z);
|
||||||
|
if (client.player.vehicle) {
|
||||||
|
client.player.vehicle.position = newPos;
|
||||||
|
} else {
|
||||||
|
client.player.position = newPos;
|
||||||
|
}
|
||||||
|
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", COLOUR_WORLD);
|
||||||
|
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", COLOUR_WORLD);
|
||||||
|
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.player.giveWeapon(weaponId, 500);
|
||||||
|
console.log("[ModMenu] " + client.name + " got weapon: " + weaponId);
|
||||||
|
});
|
||||||
|
|
||||||
|
function giveAllWeapons(client) {
|
||||||
|
if (!client.player) return;
|
||||||
|
|
||||||
|
// GTA IV Weapons
|
||||||
|
client.player.giveWeapon(1, 1); // Bat
|
||||||
|
client.player.giveWeapon(2, 1); // Knife
|
||||||
|
client.player.giveWeapon(5, 500); // Pistol
|
||||||
|
client.player.giveWeapon(6, 500); // Deagle
|
||||||
|
client.player.giveWeapon(9, 200); // Shotgun
|
||||||
|
client.player.giveWeapon(10, 200); // Combat Shotgun
|
||||||
|
client.player.giveWeapon(11, 500); // Micro SMG
|
||||||
|
client.player.giveWeapon(12, 500); // SMG
|
||||||
|
client.player.giveWeapon(14, 500); // AK47
|
||||||
|
client.player.giveWeapon(15, 500); // M4
|
||||||
|
client.player.giveWeapon(16, 100); // Sniper
|
||||||
|
client.player.giveWeapon(18, 20); // RPG
|
||||||
|
client.player.giveWeapon(19, 20); // Grenades
|
||||||
|
client.player.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":
|
||||||
|
let launchPos = new Vec3(pos.x, pos.y, pos.z + 50);
|
||||||
|
client.player.position = launchPos;
|
||||||
|
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 pedPos = new Vec3(pos.x + 3, pos.y + 3, pos.z);
|
||||||
|
let ped = gta.createPed(pedSkin, pedPos, 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!");
|
||||||
@@ -3,6 +3,13 @@
|
|||||||
// Teleportation system with Liberty City (GTA IV) locations
|
// Teleportation system with Liberty City (GTA IV) locations
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Color constants using toColour for integer format
|
||||||
|
const COLOUR_SUCCESS = toColour(100, 255, 100, 255);
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_ERROR = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_GRAY = toColour(200, 200, 200, 255);
|
||||||
|
const COLOUR_INFO = toColour(100, 200, 255, 255);
|
||||||
|
|
||||||
// Liberty City Teleport Locations
|
// Liberty City Teleport Locations
|
||||||
const locations = {
|
const locations = {
|
||||||
// Algonquin (Manhattan)
|
// Algonquin (Manhattan)
|
||||||
@@ -81,8 +88,8 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
let locationName = params.toLowerCase().split(" ")[0];
|
let locationName = params.toLowerCase().split(" ")[0];
|
||||||
teleportToLocation(client, locationName);
|
teleportToLocation(client, locationName);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /tp <location>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /tp <location>", client, COLOUR_ORANGE);
|
||||||
messageClient("[TIP] Use /tplist to see available locations", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Use /tplist to see available locations", client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -96,7 +103,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
searchLocations(client, params.toLowerCase());
|
searchLocations(client, params.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /tpsearch <partial_name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /tpsearch <partial_name>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -112,13 +119,13 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (!isNaN(x) && !isNaN(y) && !isNaN(z)) {
|
if (!isNaN(x) && !isNaN(y) && !isNaN(z)) {
|
||||||
teleportToCoords(client, x, y, z);
|
teleportToCoords(client, x, y, z);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[TELEPORT] Invalid coordinates!", client, [255, 100, 100, 255]);
|
messageClient("[TELEPORT] Invalid coordinates!", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /setpos <x> <y> <z>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /setpos <x> <y> <z>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /setpos <x> <y> <z>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /setpos <x> <y> <z>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -127,7 +134,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
saveWaypoint(client, params.toLowerCase());
|
saveWaypoint(client, params.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /savewaypoint <name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /savewaypoint <name>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -136,7 +143,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
deleteWaypoint(client, params.toLowerCase());
|
deleteWaypoint(client, params.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /delwaypoint <name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /delwaypoint <name>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -150,7 +157,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
teleportToWaypoint(client, params.toLowerCase());
|
teleportToWaypoint(client, params.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /tpwp <waypoint_name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /tpwp <waypoint_name>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -182,7 +189,7 @@ function teleportToLocation(client, locationName) {
|
|||||||
if (locations[locationName]) {
|
if (locations[locationName]) {
|
||||||
let loc = locations[locationName];
|
let loc = locations[locationName];
|
||||||
teleportPlayer(client, loc.x, loc.y, loc.z);
|
teleportPlayer(client, loc.x, loc.y, loc.z);
|
||||||
messageClient("[TELEPORT] Teleported to: " + loc.name, client, [100, 255, 100, 255]);
|
messageClient("[TELEPORT] Teleported to: " + loc.name, client, COLOUR_SUCCESS);
|
||||||
console.log("[Teleport] " + client.name + " teleported to " + loc.name);
|
console.log("[Teleport] " + client.name + " teleported to " + loc.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -191,50 +198,51 @@ function teleportToLocation(client, locationName) {
|
|||||||
if (playerWaypoints[client.index] && playerWaypoints[client.index][locationName]) {
|
if (playerWaypoints[client.index] && playerWaypoints[client.index][locationName]) {
|
||||||
let wp = playerWaypoints[client.index][locationName];
|
let wp = playerWaypoints[client.index][locationName];
|
||||||
teleportPlayer(client, wp.x, wp.y, wp.z);
|
teleportPlayer(client, wp.x, wp.y, wp.z);
|
||||||
messageClient("[TELEPORT] Teleported to waypoint: " + locationName, client, [100, 255, 100, 255]);
|
messageClient("[TELEPORT] Teleported to waypoint: " + locationName, client, COLOUR_SUCCESS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
messageClient("[TELEPORT] Location '" + locationName + "' not found!", client, [255, 100, 100, 255]);
|
messageClient("[TELEPORT] Location '" + locationName + "' not found!", client, COLOUR_ERROR);
|
||||||
messageClient("[TIP] Use /tplist or /tpsearch <name>", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Use /tplist or /tpsearch <name>", client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function teleportToCoords(client, x, y, z) {
|
function teleportToCoords(client, x, y, z) {
|
||||||
teleportPlayer(client, x, y, z);
|
teleportPlayer(client, x, y, z);
|
||||||
messageClient("[TELEPORT] Teleported to: X:" + x.toFixed(1) + " Y:" + y.toFixed(1) + " Z:" + z.toFixed(1), client, [100, 255, 100, 255]);
|
messageClient("[TELEPORT] Teleported to: X:" + x.toFixed(1) + " Y:" + y.toFixed(1) + " Z:" + z.toFixed(1), client, COLOUR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
function teleportPlayer(client, x, y, z) {
|
function teleportPlayer(client, x, y, z) {
|
||||||
if (client.player) {
|
if (client.player) {
|
||||||
|
let newPos = new Vec3(x, y, z);
|
||||||
// If in vehicle, teleport vehicle
|
// If in vehicle, teleport vehicle
|
||||||
if (client.player.vehicle) {
|
if (client.player.vehicle) {
|
||||||
client.player.vehicle.position = [x, y, z];
|
client.player.vehicle.position = newPos;
|
||||||
} else {
|
} else {
|
||||||
client.player.position = [x, y, z];
|
client.player.position = newPos;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[TELEPORT] You need to spawn first!", client, [255, 100, 100, 255]);
|
messageClient("[TELEPORT] You need to spawn first!", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLocationList(client) {
|
function showLocationList(client) {
|
||||||
messageClient("=== TELEPORT LOCATIONS ===", client, [255, 200, 100, 255]);
|
messageClient("=== TELEPORT LOCATIONS ===", client, COLOUR_ORANGE);
|
||||||
messageClient("-- Algonquin --", client, [100, 200, 255, 255]);
|
messageClient("-- Algonquin --", client, COLOUR_INFO);
|
||||||
messageClient("starjunction, middlepark, rotterdam, chinatown, exchange, happiness", client, [200, 200, 200, 255]);
|
messageClient("starjunction, middlepark, rotterdam, chinatown, exchange, happiness", client, COLOUR_GRAY);
|
||||||
|
|
||||||
messageClient("-- Broker/Dukes --", client, [100, 200, 255, 255]);
|
messageClient("-- Broker/Dukes --", client, COLOUR_INFO);
|
||||||
messageClient("broker, firefly, outlook, beach, hove, meadows, willis, airport", client, [200, 200, 200, 255]);
|
messageClient("broker, firefly, outlook, beach, hove, meadows, willis, airport", client, COLOUR_GRAY);
|
||||||
|
|
||||||
messageClient("-- Bohan --", client, [100, 200, 255, 255]);
|
messageClient("-- Bohan --", client, COLOUR_INFO);
|
||||||
messageClient("bohan, northholland, industrial", client, [200, 200, 200, 255]);
|
messageClient("bohan, northholland, industrial", client, COLOUR_GRAY);
|
||||||
|
|
||||||
messageClient("-- Alderney --", client, [100, 200, 255, 255]);
|
messageClient("-- Alderney --", client, COLOUR_INFO);
|
||||||
messageClient("alderney, alderneyport, westdyke, acter, berchem, tudor, leftwood", client, [200, 200, 200, 255]);
|
messageClient("alderney, alderneyport, westdyke, acter, berchem, tudor, leftwood", client, COLOUR_GRAY);
|
||||||
|
|
||||||
messageClient("-- Points of Interest --", client, [100, 200, 255, 255]);
|
messageClient("-- Points of Interest --", client, COLOUR_INFO);
|
||||||
messageClient("hospital, hospitalalg, police, bowling, cabaret, burgershot, helipad", client, [200, 200, 200, 255]);
|
messageClient("hospital, hospitalalg, police, bowling, cabaret, burgershot, helipad", client, COLOUR_GRAY);
|
||||||
|
|
||||||
messageClient("[TIP] Use /tpsearch <name> to find specific locations", client, [255, 200, 100, 255]);
|
messageClient("[TIP] Use /tpsearch <name> to find specific locations", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchLocations(client, searchTerm) {
|
function searchLocations(client, searchTerm) {
|
||||||
@@ -247,18 +255,18 @@ function searchLocations(client, searchTerm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found.length > 0) {
|
if (found.length > 0) {
|
||||||
messageClient("=== LOCATIONS MATCHING '" + searchTerm.toUpperCase() + "' ===", client, [255, 200, 100, 255]);
|
messageClient("=== LOCATIONS MATCHING '" + searchTerm.toUpperCase() + "' ===", client, COLOUR_ORANGE);
|
||||||
for (let i = 0; i < found.length; i++) {
|
for (let i = 0; i < found.length; i++) {
|
||||||
messageClient(found[i], client, [200, 200, 200, 255]);
|
messageClient(found[i], client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[TELEPORT] No locations found matching '" + searchTerm + "'", client, [255, 100, 100, 255]);
|
messageClient("[TELEPORT] No locations found matching '" + searchTerm + "'", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveWaypoint(client, waypointName) {
|
function saveWaypoint(client, waypointName) {
|
||||||
if (!client.player) {
|
if (!client.player) {
|
||||||
messageClient("[WAYPOINT] You need to spawn first!", client, [255, 100, 100, 255]);
|
messageClient("[WAYPOINT] You need to spawn first!", client, COLOUR_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,35 +277,35 @@ function saveWaypoint(client, waypointName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
playerWaypoints[client.index][waypointName] = {
|
playerWaypoints[client.index][waypointName] = {
|
||||||
x: pos[0],
|
x: pos.x,
|
||||||
y: pos[1],
|
y: pos.y,
|
||||||
z: pos[2]
|
z: pos.z
|
||||||
};
|
};
|
||||||
|
|
||||||
messageClient("[WAYPOINT] Saved waypoint: " + waypointName, client, [100, 255, 100, 255]);
|
messageClient("[WAYPOINT] Saved waypoint: " + waypointName, client, COLOUR_SUCCESS);
|
||||||
messageClient("[WAYPOINT] Use /tpwp " + waypointName + " to teleport here", client, [200, 200, 200, 255]);
|
messageClient("[WAYPOINT] Use /tpwp " + waypointName + " to teleport here", client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteWaypoint(client, waypointName) {
|
function deleteWaypoint(client, waypointName) {
|
||||||
if (playerWaypoints[client.index] && playerWaypoints[client.index][waypointName]) {
|
if (playerWaypoints[client.index] && playerWaypoints[client.index][waypointName]) {
|
||||||
delete playerWaypoints[client.index][waypointName];
|
delete playerWaypoints[client.index][waypointName];
|
||||||
messageClient("[WAYPOINT] Deleted waypoint: " + waypointName, client, [100, 255, 100, 255]);
|
messageClient("[WAYPOINT] Deleted waypoint: " + waypointName, client, COLOUR_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[WAYPOINT] Waypoint not found: " + waypointName, client, [255, 100, 100, 255]);
|
messageClient("[WAYPOINT] Waypoint not found: " + waypointName, client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWaypoints(client) {
|
function showWaypoints(client) {
|
||||||
if (!playerWaypoints[client.index] || Object.keys(playerWaypoints[client.index]).length === 0) {
|
if (!playerWaypoints[client.index] || Object.keys(playerWaypoints[client.index]).length === 0) {
|
||||||
messageClient("[WAYPOINT] You have no saved waypoints", client, [255, 200, 100, 255]);
|
messageClient("[WAYPOINT] You have no saved waypoints", client, COLOUR_ORANGE);
|
||||||
messageClient("[TIP] Use /savewaypoint <name> to save your current position", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Use /savewaypoint <name> to save your current position", client, COLOUR_GRAY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
messageClient("=== YOUR WAYPOINTS ===", client, [255, 200, 100, 255]);
|
messageClient("=== YOUR WAYPOINTS ===", client, COLOUR_ORANGE);
|
||||||
for (let name in playerWaypoints[client.index]) {
|
for (let name in playerWaypoints[client.index]) {
|
||||||
let wp = playerWaypoints[client.index][name];
|
let wp = playerWaypoints[client.index][name];
|
||||||
messageClient(name + " - X:" + wp.x.toFixed(1) + " Y:" + wp.y.toFixed(1) + " Z:" + wp.z.toFixed(1), client, [200, 200, 200, 255]);
|
messageClient(name + " - X:" + wp.x.toFixed(1) + " Y:" + wp.y.toFixed(1) + " Z:" + wp.z.toFixed(1), client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,9 +313,9 @@ function teleportToWaypoint(client, waypointName) {
|
|||||||
if (playerWaypoints[client.index] && playerWaypoints[client.index][waypointName]) {
|
if (playerWaypoints[client.index] && playerWaypoints[client.index][waypointName]) {
|
||||||
let wp = playerWaypoints[client.index][waypointName];
|
let wp = playerWaypoints[client.index][waypointName];
|
||||||
teleportPlayer(client, wp.x, wp.y, wp.z);
|
teleportPlayer(client, wp.x, wp.y, wp.z);
|
||||||
messageClient("[WAYPOINT] Teleported to: " + waypointName, client, [100, 255, 100, 255]);
|
messageClient("[WAYPOINT] Teleported to: " + waypointName, client, COLOUR_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[WAYPOINT] Waypoint not found: " + waypointName, client, [255, 100, 100, 255]);
|
messageClient("[WAYPOINT] Waypoint not found: " + waypointName, client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
// Handles vehicle spawning and management for GTA IV
|
// Handles vehicle spawning and management for GTA IV
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Color constants using toColour for integer format
|
||||||
|
const COLOUR_SUCCESS = toColour(100, 255, 100, 255);
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_ERROR = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_GRAY = toColour(200, 200, 200, 255);
|
||||||
|
const COLOUR_MAGENTA = toColour(255, 100, 255, 255);
|
||||||
|
|
||||||
// GTA IV Vehicle Database - Model names and their hash IDs
|
// GTA IV Vehicle Database - Model names and their hash IDs
|
||||||
const vehicles = {
|
const vehicles = {
|
||||||
// Sports Cars
|
// Sports Cars
|
||||||
@@ -57,7 +64,6 @@ const vehicles = {
|
|||||||
"premier": -1883869285,
|
"premier": -1883869285,
|
||||||
"primo": -1150599089,
|
"primo": -1150599089,
|
||||||
"stratum": 1723137093,
|
"stratum": 1723137093,
|
||||||
"sultan": 970598228,
|
|
||||||
|
|
||||||
// Compacts
|
// Compacts
|
||||||
"blista": -344943009,
|
"blista": -344943009,
|
||||||
@@ -178,8 +184,8 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
let vehicleName = params.toLowerCase().split(" ")[0];
|
let vehicleName = params.toLowerCase().split(" ")[0];
|
||||||
spawnVehicleForPlayer(client, vehicleName);
|
spawnVehicleForPlayer(client, vehicleName);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /v <vehicle_name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /v <vehicle_name>", client, COLOUR_ORANGE);
|
||||||
messageClient("[TIP] Use /vlist to see available vehicles", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Use /vlist to see available vehicles", client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -187,7 +193,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "deletevehicle":
|
case "deletevehicle":
|
||||||
case "destroyvehicle":
|
case "destroyvehicle":
|
||||||
deletePlayerVehicles(client);
|
deletePlayerVehicles(client);
|
||||||
messageClient("[VEHICLES] Your vehicles have been deleted!", client, [100, 255, 100, 255]);
|
messageClient("[VEHICLES] Your vehicles have been deleted!", client, COLOUR_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "vlist":
|
case "vlist":
|
||||||
@@ -201,7 +207,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
searchVehicles(client, params.toLowerCase());
|
searchVehicles(client, params.toLowerCase());
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /vsearch <partial_name>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /vsearch <partial_name>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -212,11 +218,11 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (colors.length >= 2) {
|
if (colors.length >= 2) {
|
||||||
setVehicleColor(client, parseInt(colors[0]), parseInt(colors[1]));
|
setVehicleColor(client, parseInt(colors[0]), parseInt(colors[1]));
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /vcolor <color1> <color2>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /vcolor <color1> <color2>", client, COLOUR_ORANGE);
|
||||||
messageClient("[TIP] Colors are 0-131 for GTA IV", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Colors are 0-131 for GTA IV", client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /vcolor <color1> <color2>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /vcolor <color1> <color2>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -225,9 +231,9 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
if (client.player && client.player.vehicle) {
|
if (client.player && client.player.vehicle) {
|
||||||
// Simulate nitro boost by increasing vehicle speed
|
// Simulate nitro boost by increasing vehicle speed
|
||||||
let veh = client.player.vehicle;
|
let veh = client.player.vehicle;
|
||||||
messageClient("[VEHICLES] NITRO! Vehicle boosted!", client, [255, 100, 255, 255]);
|
messageClient("[VEHICLES] NITRO! Vehicle boosted!", client, COLOUR_MAGENTA);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[VEHICLES] You need to be in a vehicle!", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] You need to be in a vehicle!", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -240,13 +246,13 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
function spawnVehicleForPlayer(client, vehicleName) {
|
function spawnVehicleForPlayer(client, vehicleName) {
|
||||||
// Check if vehicle exists
|
// Check if vehicle exists
|
||||||
if (!vehicles[vehicleName]) {
|
if (!vehicles[vehicleName]) {
|
||||||
messageClient("[VEHICLES] Vehicle '" + vehicleName + "' not found!", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] Vehicle '" + vehicleName + "' not found!", client, COLOUR_ERROR);
|
||||||
messageClient("[TIP] Use /vlist or /vsearch <name> to find vehicles", client, [200, 200, 200, 255]);
|
messageClient("[TIP] Use /vlist or /vsearch <name> to find vehicles", client, COLOUR_GRAY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!client.player) {
|
if (!client.player) {
|
||||||
messageClient("[VEHICLES] You need to spawn first!", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] You need to spawn first!", client, COLOUR_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,13 +264,14 @@ function spawnVehicleForPlayer(client, vehicleName) {
|
|||||||
let heading = client.player.heading;
|
let heading = client.player.heading;
|
||||||
|
|
||||||
// Spawn slightly in front of player
|
// Spawn slightly in front of player
|
||||||
let spawnX = pos[0] + (Math.sin(heading) * 3);
|
let spawnX = pos.x + (Math.sin(heading) * 3);
|
||||||
let spawnY = pos[1] + (Math.cos(heading) * 3);
|
let spawnY = pos.y + (Math.cos(heading) * 3);
|
||||||
let spawnZ = pos[2] + 1;
|
let spawnZ = pos.z + 1;
|
||||||
|
|
||||||
// Create the vehicle
|
// Create the vehicle
|
||||||
let modelHash = vehicles[vehicleName];
|
let modelHash = vehicles[vehicleName];
|
||||||
let vehicle = gta.createVehicle(modelHash, [spawnX, spawnY, spawnZ], heading);
|
let spawnPos = new Vec3(spawnX, spawnY, spawnZ);
|
||||||
|
let vehicle = gta.createVehicle(modelHash, spawnPos, heading);
|
||||||
|
|
||||||
if (vehicle) {
|
if (vehicle) {
|
||||||
// Store vehicle for this player
|
// Store vehicle for this player
|
||||||
@@ -284,10 +291,10 @@ function spawnVehicleForPlayer(client, vehicleName) {
|
|||||||
vehicle.colour1 = color1;
|
vehicle.colour1 = color1;
|
||||||
vehicle.colour2 = color2;
|
vehicle.colour2 = color2;
|
||||||
|
|
||||||
messageClient("[VEHICLES] Spawned: " + vehicleName.toUpperCase(), client, [100, 255, 100, 255]);
|
messageClient("[VEHICLES] Spawned: " + vehicleName.toUpperCase(), client, COLOUR_SUCCESS);
|
||||||
console.log("[Vehicles] " + client.name + " spawned " + vehicleName);
|
console.log("[Vehicles] " + client.name + " spawned " + vehicleName);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[VEHICLES] Failed to spawn vehicle!", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] Failed to spawn vehicle!", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,17 +311,17 @@ function deletePlayerVehicles(client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showVehicleList(client) {
|
function showVehicleList(client) {
|
||||||
messageClient("=== VEHICLE CATEGORIES ===", client, [255, 200, 100, 255]);
|
messageClient("=== VEHICLE CATEGORIES ===", client, COLOUR_ORANGE);
|
||||||
messageClient("Sports: infernus, turismo, comet, banshee, sultan, coquette, feltzer", client, [200, 200, 200, 255]);
|
messageClient("Sports: infernus, turismo, comet, banshee, sultan, coquette, feltzer", client, COLOUR_GRAY);
|
||||||
messageClient("Muscle: sabregt, stalion, vigero, dukes, ruiner, phoenix", client, [200, 200, 200, 255]);
|
messageClient("Muscle: sabregt, stalion, vigero, dukes, ruiner, phoenix", client, COLOUR_GRAY);
|
||||||
messageClient("Super: entityxf, adder, vacca, bullet, cheetah", client, [200, 200, 200, 255]);
|
messageClient("Super: entityxf, adder, vacca, bullet, cheetah", client, COLOUR_GRAY);
|
||||||
messageClient("SUV: patriot, cavalcade, granger, huntley, landstalker", client, [200, 200, 200, 255]);
|
messageClient("SUV: patriot, cavalcade, granger, huntley, landstalker", client, COLOUR_GRAY);
|
||||||
messageClient("Sedan: oracle, schafter, admiral, vincent, presidente", client, [200, 200, 200, 255]);
|
messageClient("Sedan: oracle, schafter, admiral, vincent, presidente", client, COLOUR_GRAY);
|
||||||
messageClient("Emergency: police, fbi, noose, ambulance, firetruk", client, [200, 200, 200, 255]);
|
messageClient("Emergency: police, fbi, noose, ambulance, firetruk", client, COLOUR_GRAY);
|
||||||
messageClient("Bikes: nrg900, pcj600, sanchez, faggio, bati, akuma", client, [200, 200, 200, 255]);
|
messageClient("Bikes: nrg900, pcj600, sanchez, faggio, bati, akuma", client, COLOUR_GRAY);
|
||||||
messageClient("Air: annihilator, maverick, polmav, buzzard, shamal", client, [200, 200, 200, 255]);
|
messageClient("Air: annihilator, maverick, polmav, buzzard, shamal", client, COLOUR_GRAY);
|
||||||
messageClient("Boats: jetmax, marquis, predator, tropic, dinghy", client, [200, 200, 200, 255]);
|
messageClient("Boats: jetmax, marquis, predator, tropic, dinghy", client, COLOUR_GRAY);
|
||||||
messageClient("[TIP] Use /vsearch <name> to search for specific vehicles", client, [255, 200, 100, 255]);
|
messageClient("[TIP] Use /vsearch <name> to search for specific vehicles", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchVehicles(client, searchTerm) {
|
function searchVehicles(client, searchTerm) {
|
||||||
@@ -326,10 +333,10 @@ function searchVehicles(client, searchTerm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found.length > 0) {
|
if (found.length > 0) {
|
||||||
messageClient("=== VEHICLES MATCHING '" + searchTerm.toUpperCase() + "' ===", client, [255, 200, 100, 255]);
|
messageClient("=== VEHICLES MATCHING '" + searchTerm.toUpperCase() + "' ===", client, COLOUR_ORANGE);
|
||||||
messageClient(found.join(", "), client, [200, 200, 200, 255]);
|
messageClient(found.join(", "), client, COLOUR_GRAY);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[VEHICLES] No vehicles found matching '" + searchTerm + "'", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] No vehicles found matching '" + searchTerm + "'", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,9 +345,9 @@ function setVehicleColor(client, color1, color2) {
|
|||||||
let veh = client.player.vehicle;
|
let veh = client.player.vehicle;
|
||||||
veh.colour1 = color1;
|
veh.colour1 = color1;
|
||||||
veh.colour2 = color2;
|
veh.colour2 = color2;
|
||||||
messageClient("[VEHICLES] Vehicle color changed to: " + color1 + ", " + color2, client, [100, 255, 100, 255]);
|
messageClient("[VEHICLES] Vehicle color changed to: " + color1 + ", " + color2, client, COLOUR_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[VEHICLES] You need to be in a vehicle!", client, [255, 100, 100, 255]);
|
messageClient("[VEHICLES] You need to be in a vehicle!", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
// Handles weather, time, and world environment controls
|
// Handles weather, time, and world environment controls
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// Color constants using toColour for integer format
|
||||||
|
const COLOUR_WORLD = toColour(100, 200, 255, 255);
|
||||||
|
const COLOUR_ORANGE = toColour(255, 200, 100, 255);
|
||||||
|
const COLOUR_ERROR = toColour(255, 100, 100, 255);
|
||||||
|
const COLOUR_GRAY = toColour(200, 200, 200, 255);
|
||||||
|
|
||||||
// GTA IV Weather Types
|
// GTA IV Weather Types
|
||||||
const weatherTypes = {
|
const weatherTypes = {
|
||||||
0: "Extra Sunny",
|
0: "Extra Sunny",
|
||||||
@@ -68,12 +74,12 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
let weatherId = parseInt(params);
|
let weatherId = parseInt(params);
|
||||||
if (!isNaN(weatherId) && weatherId >= 0 && weatherId <= 9) {
|
if (!isNaN(weatherId) && weatherId >= 0 && weatherId <= 9) {
|
||||||
setWeather(weatherId);
|
setWeather(weatherId);
|
||||||
message("[WORLD] Weather changed to: " + weatherTypes[weatherId], [100, 200, 255, 255]);
|
message("[WORLD] Weather changed to: " + weatherTypes[weatherId], COLOUR_WORLD);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[WORLD] Invalid weather ID (0-9)", client, [255, 100, 100, 255]);
|
messageClient("[WORLD] Invalid weather ID (0-9)", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /weather <0-9>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /weather <0-9>", client, COLOUR_ORANGE);
|
||||||
showWeatherList(client);
|
showWeatherList(client);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -87,78 +93,78 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
|
|
||||||
if (!isNaN(hour) && hour >= 0 && hour <= 23) {
|
if (!isNaN(hour) && hour >= 0 && hour <= 23) {
|
||||||
setTime(hour, minute);
|
setTime(hour, minute);
|
||||||
message("[WORLD] Time changed to: " + formatTime(hour, minute), [100, 200, 255, 255]);
|
message("[WORLD] Time changed to: " + formatTime(hour, minute), COLOUR_WORLD);
|
||||||
} else {
|
} else {
|
||||||
messageClient("[WORLD] Invalid time (0-23 hours)", client, [255, 100, 100, 255]);
|
messageClient("[WORLD] Invalid time (0-23 hours)", client, COLOUR_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageClient("[USAGE] /time <hour> or /time <hour:minute>", client, [255, 200, 100, 255]);
|
messageClient("[USAGE] /time <hour> or /time <hour:minute>", client, COLOUR_ORANGE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "morning":
|
case "morning":
|
||||||
setTime(8, 0);
|
setTime(8, 0);
|
||||||
message("[WORLD] Time set to morning (8:00)", [100, 200, 255, 255]);
|
message("[WORLD] Time set to morning (8:00)", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "noon":
|
case "noon":
|
||||||
case "midday":
|
case "midday":
|
||||||
setTime(12, 0);
|
setTime(12, 0);
|
||||||
message("[WORLD] Time set to noon (12:00)", [100, 200, 255, 255]);
|
message("[WORLD] Time set to noon (12:00)", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "evening":
|
case "evening":
|
||||||
setTime(18, 0);
|
setTime(18, 0);
|
||||||
message("[WORLD] Time set to evening (18:00)", [100, 200, 255, 255]);
|
message("[WORLD] Time set to evening (18:00)", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "night":
|
case "night":
|
||||||
case "midnight":
|
case "midnight":
|
||||||
setTime(0, 0);
|
setTime(0, 0);
|
||||||
message("[WORLD] Time set to midnight (0:00)", [100, 200, 255, 255]);
|
message("[WORLD] Time set to midnight (0:00)", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "sunny":
|
case "sunny":
|
||||||
setWeather(1);
|
setWeather(1);
|
||||||
message("[WORLD] Weather set to Sunny", [100, 200, 255, 255]);
|
message("[WORLD] Weather set to Sunny", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "cloudy":
|
case "cloudy":
|
||||||
setWeather(3);
|
setWeather(3);
|
||||||
message("[WORLD] Weather set to Cloudy", [100, 200, 255, 255]);
|
message("[WORLD] Weather set to Cloudy", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "rain":
|
case "rain":
|
||||||
case "rainy":
|
case "rainy":
|
||||||
setWeather(4);
|
setWeather(4);
|
||||||
message("[WORLD] Weather set to Raining", [100, 200, 255, 255]);
|
message("[WORLD] Weather set to Raining", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "thunder":
|
case "thunder":
|
||||||
case "storm":
|
case "storm":
|
||||||
setWeather(7);
|
setWeather(7);
|
||||||
message("[WORLD] Weather set to Thunder", [100, 200, 255, 255]);
|
message("[WORLD] Weather set to Thunder", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "foggy":
|
case "foggy":
|
||||||
case "fog":
|
case "fog":
|
||||||
setWeather(6);
|
setWeather(6);
|
||||||
message("[WORLD] Weather set to Foggy", [100, 200, 255, 255]);
|
message("[WORLD] Weather set to Foggy", COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "freezetime":
|
case "freezetime":
|
||||||
case "stoptime":
|
case "stoptime":
|
||||||
worldState.timeFrozen = !worldState.timeFrozen;
|
worldState.timeFrozen = !worldState.timeFrozen;
|
||||||
let status = worldState.timeFrozen ? "frozen" : "unfrozen";
|
let status = worldState.timeFrozen ? "frozen" : "unfrozen";
|
||||||
message("[WORLD] Time has been " + status, [100, 200, 255, 255]);
|
message("[WORLD] Time has been " + status, COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "gettime":
|
case "gettime":
|
||||||
messageClient("[WORLD] Current time: " + formatTime(worldState.hour, worldState.minute), client, [100, 200, 255, 255]);
|
messageClient("[WORLD] Current time: " + formatTime(worldState.hour, worldState.minute), client, COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "getweather":
|
case "getweather":
|
||||||
messageClient("[WORLD] Current weather: " + weatherTypes[worldState.weather] + " (" + worldState.weather + ")", client, [100, 200, 255, 255]);
|
messageClient("[WORLD] Current weather: " + weatherTypes[worldState.weather] + " (" + worldState.weather + ")", client, COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "weatherlist":
|
case "weatherlist":
|
||||||
@@ -169,7 +175,7 @@ addEventHandler("OnPlayerCommand", function(event, client, command, params) {
|
|||||||
case "randomweather":
|
case "randomweather":
|
||||||
let randomWeather = Math.floor(Math.random() * 10);
|
let randomWeather = Math.floor(Math.random() * 10);
|
||||||
setWeather(randomWeather);
|
setWeather(randomWeather);
|
||||||
message("[WORLD] Random weather: " + weatherTypes[randomWeather], [100, 200, 255, 255]);
|
message("[WORLD] Random weather: " + weatherTypes[randomWeather], COLOUR_WORLD);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -215,9 +221,9 @@ function formatTime(hour, minute) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showWeatherList(client) {
|
function showWeatherList(client) {
|
||||||
messageClient("=== WEATHER TYPES ===", client, [255, 200, 100, 255]);
|
messageClient("=== WEATHER TYPES ===", client, COLOUR_ORANGE);
|
||||||
for (let id in weatherTypes) {
|
for (let id in weatherTypes) {
|
||||||
messageClient(id + " - " + weatherTypes[id], client, [200, 200, 200, 255]);
|
messageClient(id + " - " + weatherTypes[id], client, COLOUR_GRAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,5 +33,6 @@
|
|||||||
<resource src="chat" />
|
<resource src="chat" />
|
||||||
<resource src="teleport" />
|
<resource src="teleport" />
|
||||||
<resource src="vehicles" />
|
<resource src="vehicles" />
|
||||||
|
<resource src="modmenu" />
|
||||||
</resources>
|
</resources>
|
||||||
</server>
|
</server>
|
||||||
|
|||||||
Reference in New Issue
Block a user