├── cover.jpg
├── mapvote
├── images
│ ├── mapvote1.png
│ └── mapvote2.png
├── mapvote_mp_extend.gsc
├── mapvote_zm_extend.gsc
├── README.md
└── mapvote.gsc
├── small_scripts
├── README.md
└── get_player_guid.gsc
├── chat_commands
├── mp
│ ├── README.md
│ └── chat_command_suicide.gsc
├── chat_command_text_help.gsc
├── chat_command_kick.gsc
├── zm
│ ├── chat_command_no_target.gsc
│ ├── chat_command_points.gsc
│ ├── chat_command_give.gsc
│ ├── chat_command_rounds.gsc
│ └── README.md
├── chat_command_teleport.gsc
├── chat_command_invisible.gsc
├── chat_command_freeze.gsc
├── chat_command_god_mode.gsc
├── chat_command_permissions.gsc
├── chat_command_unlimited_ammo .gsc
├── chat_command_give.gsc
├── chat_command_dvars.gsc
├── chat_command_ufo_mode.gsc
├── chat_command_unfair_aimbot.gsc
├── chat_command_info.gsc
├── README.md
└── chat_commands.gsc
└── README.md
/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Resxt/Plutonium-T6-Scripts/HEAD/cover.jpg
--------------------------------------------------------------------------------
/mapvote/images/mapvote1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Resxt/Plutonium-T6-Scripts/HEAD/mapvote/images/mapvote1.png
--------------------------------------------------------------------------------
/mapvote/images/mapvote2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Resxt/Plutonium-T6-Scripts/HEAD/mapvote/images/mapvote2.png
--------------------------------------------------------------------------------
/small_scripts/README.md:
--------------------------------------------------------------------------------
1 | # Small scripts
2 |
3 | Simple drag and drop scripts
4 |
5 | ## get_player_guid.gsc
6 |
7 | Print the GUID of a player in the console whenever he connects and whenever he spawns.
8 |
--------------------------------------------------------------------------------
/chat_commands/mp/README.md:
--------------------------------------------------------------------------------
1 | # MP Chat commands
2 |
3 | These scripts go in `scripts\mp`
4 |
5 | ## chat_command_suicide.gsc
6 |
7 | The player who runs the command dies.
8 |
9 | | Example |
10 | |---|
11 | | `!suicide` |
12 |
13 | | Permission level |
14 | |---|
15 | | 1 |
16 |
--------------------------------------------------------------------------------
/chat_commands/mp/chat_command_suicide.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "suicide", "function", ::SuicideCommand, 1);
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | SuicideCommand(args)
13 | {
14 | self Suicide();
15 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_text_help.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "help", "text", array("Type " + GetDvar("cc_prefix") + "commands to get a list of commands", "Type " + GetDvar("cc_prefix") + "help followed by a command name to see how to use it"), 1);
6 | }
--------------------------------------------------------------------------------
/small_scripts/get_player_guid.gsc:
--------------------------------------------------------------------------------
1 | Init()
2 | {
3 | level thread OnPlayerConnect();
4 | }
5 |
6 | OnPlayerConnect()
7 | {
8 | for(;;)
9 | {
10 | level waittill("connected", player);
11 |
12 | Print(player.name + " GUID: " + player.guid);
13 |
14 | player thread OnPlayerSpawned();
15 | }
16 | }
17 |
18 | OnPlayerSpawned()
19 | {
20 | self endon("disconnect");
21 |
22 | for(;;)
23 | {
24 | self waittill("spawned_player");
25 |
26 | Print(self.name + " GUID: " + self.guid);
27 | }
28 | }
--------------------------------------------------------------------------------
/mapvote/mapvote_mp_extend.gsc:
--------------------------------------------------------------------------------
1 | #include maps\mp\_utility;
2 |
3 | Init()
4 | {
5 | if (GetDvarInt("mapvote_enable"))
6 | {
7 | replaceFunc(maps\mp\gametypes\_killcam::finalkillcamwaiter, ::OnKillcamEnd);
8 | }
9 | }
10 |
11 | OnKillcamEnd()
12 | {
13 | if (!IsDefined(level.finalkillcam_winner))
14 | {
15 | if (isRoundBased() && !wasLastRound())
16 | return false;
17 |
18 | wait GetDvarInt("mapvote_display_wait_time");
19 | [[level.mapvote_rotate_function]]();
20 |
21 | return false;
22 | }
23 |
24 | level waittill("final_killcam_done");
25 | if (isRoundBased() && !wasLastRound())
26 | return true;
27 |
28 | wait GetDvarInt("mapvote_display_wait_time");
29 | [[level.mapvote_rotate_function]]();
30 |
31 | return true;
32 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_kick.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "kick", "function", ::KickCommand, 4, array("default_help_one_player"), array("k"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | KickCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = KickPlayer(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | KickPlayer(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | Kick(player GetEntityNumber());
41 | }
--------------------------------------------------------------------------------
/chat_commands/zm/chat_command_no_target.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "notarget", "function", ::NoTargetCommand, 3, array("default_help_one_player"), array("ignoreme", "ignore"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | NoTargetCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = ToggleNoTarget(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | ToggleNoTarget(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | commandName = "notarget";
41 |
42 | ToggleStatus(commandName, "No Target", player);
43 |
44 | player.ignoreme = GetStatus(commandName, player);
45 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_teleport.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "teleport", "function", ::TeleportCommand, 2, array("default_help_two_players"), array("tp"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | TeleportCommand(args)
13 | {
14 | if (args.size < 2)
15 | {
16 | return NotEnoughArgsError(2);
17 | }
18 |
19 | error = TeleportPlayer(args[0], args[1]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | TeleportPlayer(teleportedPlayerName, destinationPlayerName)
32 | {
33 | players = [];
34 | names = array(teleportedPlayerName, destinationPlayerName);
35 |
36 | for (i = 0; i < names.size; i++)
37 | {
38 | name = names[i];
39 |
40 | player = FindPlayerByName(name);
41 |
42 | if (!IsDefined(player))
43 | {
44 | return PlayerDoesNotExistError(name);
45 | }
46 |
47 | players = AddElementToArray(players, player);
48 | }
49 |
50 | players[0] SetOrigin(players[1].origin);
51 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_invisible.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "invisible", "function", ::InvisibleCommand, 3, array("default_help_one_player"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | InvisibleCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = ToggleInvisible(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | ToggleInvisible(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | commandName = "invisible";
41 |
42 | ToggleStatus(commandName, "Invisible", player);
43 |
44 | if (GetStatus(commandName, player))
45 | {
46 | player Hide();
47 | player.ignoreme = 1; // zombies won't target the player
48 | }
49 | else
50 | {
51 | player Show();
52 | player.ignoreme = 0; // zombies will target the player again
53 | }
54 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_freeze.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "freeze", "function", ::FreezeCommand, 3, array("default_help_one_player"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | FreezeCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = ToggleFreeze(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | ToggleFreeze(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | commandName = "freeze";
41 |
42 | ToggleStatus(commandName, "Freeze", player);
43 |
44 | if (GetStatus(commandName, player))
45 | {
46 | player DoFreeze(true);
47 | player thread ThreadFreeze();
48 | }
49 | else
50 | {
51 | player DoFreeze(false);
52 | player notify("chat_commands_freeze_off");
53 | }
54 | }
55 |
56 | ThreadFreeze()
57 | {
58 | self endon("disconnect");
59 | self endon("chat_commands_freeze_off");
60 |
61 | for(;;)
62 | {
63 | self waittill("spawned_player");
64 |
65 | self DoFreeze(true);
66 | }
67 | }
68 |
69 | DoFreeze(enabled)
70 | {
71 | if (enabled)
72 | {
73 | self FreezeControls(1);
74 | }
75 | else
76 | {
77 | self FreezeControls(0);
78 | }
79 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_god_mode.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 | #include common_scripts\utility;
3 |
4 | Init()
5 | {
6 | CreateCommand(level.chat_commands["ports"], "godmode", "function", ::GodModeCommand, 3, array("default_help_one_player"), array("god"));
7 | }
8 |
9 |
10 |
11 | /* Command section */
12 |
13 | GodModeCommand(args)
14 | {
15 | if (args.size < 1)
16 | {
17 | return NotEnoughArgsError(1);
18 | }
19 |
20 | error = ToggleGodMode(args[0]);
21 |
22 | if (IsDefined(error))
23 | {
24 | return error;
25 | }
26 | }
27 |
28 |
29 |
30 | /* Logic section */
31 |
32 | ToggleGodMode(playerName)
33 | {
34 | player = FindPlayerByName(playerName);
35 |
36 | if (!IsDefined(player))
37 | {
38 | return PlayerDoesNotExistError(playerName);
39 | }
40 |
41 | commandName = "god";
42 |
43 | ToggleStatus(commandName, "God Mode", player);
44 |
45 | if (GetStatus(commandName, player))
46 | {
47 | player DoGodMode(true);
48 | player thread ThreadGodMode();
49 | }
50 | else
51 | {
52 | player DoGodMode(false);
53 | player notify("chat_commands_god_mode_off");
54 | }
55 | }
56 |
57 | ThreadGodMode()
58 | {
59 | self endon("disconnect");
60 | self endon("chat_commands_god_mode_off");
61 |
62 | for(;;)
63 | {
64 | self waittill_any("spawned_player", "gr_eject_sequence_complete"); // Origins robot ejected
65 |
66 | self DoGodMode(true);
67 | }
68 | }
69 |
70 | DoGodMode(enabled)
71 | {
72 | if (enabled)
73 | {
74 | self EnableInvulnerability();
75 | }
76 | else
77 | {
78 | self DisableInvulnerability();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Plutonium-T6-Scripts
2 |
3 | [](https://plutonium.pw/)
4 |
5 | ## What is this?
6 |
7 | This is a collection of [Plutonium](https://plutonium.pw/docs/intro/) T6/BO2 scripts I created, written in [GSC](https://plutonium.pw/docs/modding/gsc/).
8 |
9 | Huge thanks to everyone who helped me learn GSC: Birchy, DoktorSAS, FutureRave and other people on Discord.
10 |
11 | ## How do I download a script?
12 |
13 | - [Download this repository](https://github.com/Resxt/Plutonium-T6-Scripts/archive/refs/heads/master.zip)
14 | - Open the downloaded ZIP file
15 | - Drag and drop the script(s) you want in the folder they need to be placed in.
16 | If no instructions are provided for the script you want then simply follow the instructions given in [How do I use a script?](#how-do-i-use-a-script)
17 |
18 | Just keep in mind that this downloads a copy of this repository at the moment you download it.
19 | If a script is updated after you downloaded this repository and you want the new version then you will need to download this repository again.
20 |
21 | ## How do I use a script?
22 |
23 | [Follow the instructions in the documentation](https://plutonium.pw/docs/modding/loading-mods/#loading-existing-scripts-on-t6)
24 |
25 | Open the start menu and go to `%localappdata%\Plutonium\storage\t6`.
26 | If the script should be loaded for both the multiplayer and the zombies mode then drop the file in the `scripts` folder.
27 | If the script should only be loaded for the multiplayer mode then put it in `scripts\mp`.
28 | If the script should only be loaded for the zombies mode then put it in `scripts\zm`.
29 |
30 | Note that you can use `map_restart` in the [console](https://plutonium.pw/docs/opening-console/) to quickly restart your current game and reload scripts.
31 |
--------------------------------------------------------------------------------
/chat_commands/chat_command_permissions.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | if (PermissionIsEnabled())
6 | {
7 | CreateCommand(level.chat_commands["ports"], "getpermission", "function", ::GetPlayerPermissionCommand, 2, array("default_help_one_player"), array("gp"));
8 | CreateCommand(level.chat_commands["ports"], "setpermission", "function", ::SetPlayerPermissionCommand, 4, [], array("sp"));
9 | }
10 | }
11 |
12 |
13 |
14 | /* Command section */
15 |
16 | GetPlayerPermissionCommand(args)
17 | {
18 | if (args.size < 1)
19 | {
20 | return NotEnoughArgsError(1);
21 | }
22 |
23 | error = GetPlayerPermission(args[0]);
24 |
25 | if (IsDefined(error))
26 | {
27 | return error;
28 | }
29 | }
30 |
31 | SetPlayerPermissionCommand(args)
32 | {
33 | if (args.size < 2)
34 | {
35 | return NotEnoughArgsError(2);
36 | }
37 |
38 | error = SetPlayerPermission(args[0], args[1]);
39 |
40 | if (IsDefined(error))
41 | {
42 | return error;
43 | }
44 | }
45 |
46 |
47 |
48 | /* Logic section */
49 |
50 | GetPlayerPermission(playerName)
51 | {
52 | player = FindPlayerByName(playerName);
53 |
54 | if (!IsDefined(player))
55 | {
56 | return PlayerDoesNotExistError(playerName);
57 | }
58 |
59 | self thread TellPlayer(array("^5" + player.name + " ^7permission level is ^5" + player GetPlayerPermissionLevel()), 1);
60 | }
61 |
62 | SetPlayerPermission(playerName, newPermissionLevel)
63 | {
64 | player = FindPlayerByName(playerName);
65 |
66 | if (!IsDefined(player))
67 | {
68 | return PlayerDoesNotExistError(playerName);
69 | }
70 |
71 | newPermissionLevel = int(newPermissionLevel);
72 |
73 | if (newPermissionLevel < 0 || newPermissionLevel > GetDvarInt("cc_permission_max"))
74 | {
75 | return InvalidPermissionLevelError(newPermissionLevel);
76 | }
77 |
78 | player SetPlayerPermissionLevel(newPermissionLevel);
79 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_unlimited_ammo .gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "unlimitedammo", "function", ::UnlimitedAmmoCommand, 3, array("default_help_one_player"), array("ammo", "ua"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | UnlimitedAmmoCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = ToggleUnlimitedAmmo(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | ToggleUnlimitedAmmo(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | commandName = "unlimitedammo";
41 |
42 | ToggleStatus(commandName, "Unlimited Ammo", player);
43 |
44 | if (GetStatus(commandName, player))
45 | {
46 | player thread DoUnlimitedAmmo();
47 | player thread ThreadUnlimitedAmmo();
48 | }
49 | else
50 | {
51 | player notify("chat_commands_unlimited_ammo_off");
52 | }
53 | }
54 |
55 | ThreadUnlimitedAmmo()
56 | {
57 | self endon("disconnect");
58 | self endon("chat_commands_unlimited_ammo_off");
59 |
60 | for(;;)
61 | {
62 | self waittill("spawned_player");
63 |
64 | self thread DoUnlimitedAmmo();
65 | }
66 | }
67 |
68 | DoUnlimitedAmmo()
69 | {
70 | self endon("chat_commands_unlimited_ammo_off");
71 |
72 | while (true)
73 | {
74 | currentWeapon = self getCurrentWeapon();
75 | currentoffhand = self GetCurrentOffhand();
76 |
77 | if (currentWeapon != "none")
78 | {
79 | self SetWeaponAmmoClip(currentWeapon, 9999);
80 | }
81 |
82 | if (IsSubStr(currentWeapon, "akimbo"))
83 | {
84 | self SetWeaponAmmoClip(currentWeapon, 9999, "left");
85 | self SetWeaponAmmoClip(currentWeapon, 9999, "right");
86 | }
87 |
88 | if ( currentoffhand != "none" )
89 | {
90 | self setWeaponAmmoClip( currentoffhand, 9999 );
91 | self GiveMaxAmmo( currentoffhand );
92 | }
93 |
94 | wait 0.05;
95 | }
96 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_give.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "giveweapon", "function", ::GiveWeaponCommand, 2);
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | GiveWeaponCommand(args)
13 | {
14 | if (args.size < 2)
15 | {
16 | return NotEnoughArgsError(2);
17 | }
18 |
19 | error = GivePlayerWeapon(args[0], args[1], args[2], true, true);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | GivePlayerWeapon(targetedPlayerName, weaponName, camoIndex, takeCurrentWeapon, playSwitchAnimation)
32 | {
33 | player = FindPlayerByName(targetedPlayerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(targetedPlayerName);
38 | }
39 |
40 | weaponName = ToLower(weaponName);
41 | weaponSplitted = StrTok(weaponName, "+");
42 | weaponName = weaponSplitted[0];
43 | attachments = "";
44 |
45 | for (i = 1; i < weaponSplitted.size; i++)
46 | {
47 | if (i == 1)
48 | {
49 | attachments += weaponSplitted[i];
50 | }
51 | else
52 | {
53 | attachments += "+" + weaponSplitted[i];
54 | }
55 | }
56 |
57 | if (IsMultiplayerMode())
58 | {
59 | if (GetSubStr(weaponName, weaponName.size - 3, weaponName.size) != "_mp")
60 | {
61 | weaponName += "_mp";
62 | }
63 | }
64 |
65 | if (!IsValidWeapon(weaponName))
66 | {
67 | return WeaponDoesNotExistError(weaponName);
68 | }
69 |
70 | finalCamoIndex = 0;
71 |
72 | if (IsDefined(camoIndex))
73 | {
74 | finalCamoIndex = int(camoIndex);
75 | }
76 |
77 | if (IsDefined(takeCurrentWeapon) && takeCurrentWeapon)
78 | {
79 | player TakeWeapon(player GetCurrentWeapon());
80 | }
81 |
82 | if (IsDefined(attachments))
83 | {
84 | weaponName = weaponName + "+" + attachments;
85 | }
86 |
87 | player GiveWeapon(weaponName, 0, finalCamoIndex);
88 |
89 | if (IsDefined(playSwitchAnimation) && playSwitchAnimation)
90 | {
91 | player SwitchToWeapon(weaponName);
92 | }
93 | else
94 | {
95 | player SetSpawnWeapon(weaponName);
96 | }
97 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_dvars.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "getdvar", "function", ::GetDvarCommand, 2, [], array("gd"));
6 | CreateCommand(level.chat_commands["ports"], "setdvar", "function", ::SetDvarCommand, 4, [], array("sd"));
7 | CreateCommand(level.chat_commands["ports"], "setclientdvar", "function", ::SetPlayerDvarCommand, 4, [], array("scd"));
8 | }
9 |
10 |
11 |
12 | /* Command section */
13 |
14 | GetDvarCommand(args)
15 | {
16 | if (args.size < 1)
17 | {
18 | return NotEnoughArgsError(1);
19 | }
20 |
21 | error = GetServerDvar(args[0]);
22 |
23 | if (IsDefined(error))
24 | {
25 | return error;
26 | }
27 | }
28 |
29 | SetDvarCommand(args)
30 | {
31 | if (args.size < 2)
32 | {
33 | return NotEnoughArgsError(2);
34 | }
35 |
36 | error = SetServerDvar(args[0], args[1], false);
37 |
38 | if (IsDefined(error))
39 | {
40 | return error;
41 | }
42 | }
43 |
44 | SetPlayerDvarCommand(args)
45 | {
46 | if (args.size < 3)
47 | {
48 | return NotEnoughArgsError(3);
49 | }
50 |
51 | error = SetPlayerDvar(args[0], args[1], args[2]);
52 |
53 | if (IsDefined(error))
54 | {
55 | return error;
56 | }
57 | }
58 |
59 |
60 |
61 | /* Logic section */
62 |
63 | GetServerDvar(dvarName)
64 | {
65 | if (DvarIsInitialized(dvarName))
66 | {
67 | self thread TellPlayer(array("^5" + dvarName + " ^7is currently set to ^5" + GetDvar(dvarName)), 1);
68 | }
69 | else
70 | {
71 | return DvarDoesNotExistError(dvarName);
72 | }
73 | }
74 |
75 | SetServerDvar(dvarName, dvarValue, canSetUndefinedDvar)
76 | {
77 | if (IsDefined(canSetUndefinedDvar) && canSetUndefinedDvar)
78 | {
79 | SetDvar(dvarName, dvarValue);
80 | }
81 | else
82 | {
83 | if (DvarIsInitialized(dvarName))
84 | {
85 | SetDvar(dvarName, dvarValue);
86 | }
87 | else
88 | {
89 | return DvarDoesNotExistError(dvarName);
90 | }
91 | }
92 | }
93 |
94 | SetPlayerDvar(playerName, dvarName, dvarValue)
95 | {
96 | player = FindPlayerByName(playerName);
97 |
98 | if (!IsDefined(player))
99 | {
100 | return PlayerDoesNotExistError(playerName);
101 | }
102 |
103 | player SetClientDvar(dvarName, dvarValue);
104 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_ufo_mode.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | // change to false to disable death barrier protection
6 | // in ZM: no death barrier for all players during the entire game
7 | // in MP: constant god mode for the player who's in UFO mode until toggling ufo off
8 | level.chat_commands_protect_from_death_barriers = true;
9 |
10 | if (level.chat_commands_protect_from_death_barriers && !IsMultiplayerMode())
11 | {
12 | level.player_out_of_playable_area_monitor = false;
13 | }
14 |
15 | CreateCommand(level.chat_commands["ports"], "ufomode", "function", ::UfoModeCommand, 3, array("default_help_one_player"), array("ufo"));
16 | }
17 |
18 |
19 |
20 | /* Command section */
21 |
22 | UfoModeCommand(args)
23 | {
24 | if (args.size < 1)
25 | {
26 | return NotEnoughArgsError(1);
27 | }
28 |
29 | error = ToggleUfoMode(args[0]);
30 |
31 | if (IsDefined(error))
32 | {
33 | return error;
34 | }
35 | }
36 |
37 |
38 |
39 | /* Logic section */
40 |
41 | ToggleUfoMode(playerName)
42 | {
43 | player = FindPlayerByName(playerName);
44 |
45 | if (!IsDefined(player))
46 | {
47 | return PlayerDoesNotExistError(playerName);
48 | }
49 |
50 | commandName = "ufo";
51 |
52 | ToggleStatus(commandName, "Ufo Mode", player);
53 |
54 | if (GetStatus(commandName, player))
55 | {
56 | player thread DoUfoMode();
57 | player thread ThreadUfoMode();
58 | }
59 | else
60 | {
61 | player notify("chat_commands_ufo_mode_off");
62 |
63 | if (IsMultiplayerMode() && !GetStatus("god", player))
64 | {
65 | self DisableInvulnerability();
66 | }
67 | }
68 | }
69 |
70 | ThreadUfoMode()
71 | {
72 | self endon("disconnect");
73 | self endon("chat_commands_ufo_mode_off");
74 |
75 | for(;;)
76 | {
77 | self waittill("spawned_player");
78 |
79 | self DoUfoMode();
80 | }
81 | }
82 |
83 | DoUfoMode()
84 | {
85 | self endon("disconnect");
86 | self endon("death");
87 | self endon("chat_commands_ufo_mode_off");
88 |
89 | if (level.chat_commands_protect_from_death_barriers && IsMultiplayerMode())
90 | {
91 | self EnableInvulnerability();
92 | }
93 |
94 | self.fly = 0;
95 | UFO = Spawn("script_model", self.origin);
96 |
97 | for(;;)
98 | {
99 | if(self MeleeButtonPressed())
100 | {
101 | self PlayerLinkTo(UFO);
102 | self.fly = 1;
103 | }
104 | else
105 | {
106 | self Unlink();
107 | self.fly = 0;
108 | }
109 | if(self.fly == 1)
110 | {
111 | fly = self.origin + VectorScale(AnglesToForward(self GetPlayerAngles()), 20);
112 | UFO MoveTo(fly, .01);
113 | }
114 |
115 | wait 0.05;
116 | }
117 | }
--------------------------------------------------------------------------------
/chat_commands/zm/chat_command_points.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "setpoints", "function", ::SetPointsCommand, 3);
6 | CreateCommand(level.chat_commands["ports"], "addpoints", "function", ::AddPointsCommand, 3);
7 | CreateCommand(level.chat_commands["ports"], "takepoints", "function", ::TakePointsCommand, 3);
8 | CreateCommand(level.chat_commands["ports"], "givepoints", "function", ::GivePointsCommand, 2, [], array("pay", "transferpoints"));
9 | }
10 |
11 |
12 |
13 | /* Command section */
14 |
15 | SetPointsCommand(args)
16 | {
17 | if (args.size < 2)
18 | {
19 | return NotEnoughArgsError(2);
20 | }
21 |
22 | error = SetPlayerPoints(args[0], args[1]);
23 |
24 | if (IsDefined(error))
25 | {
26 | return error;
27 | }
28 | }
29 |
30 | AddPointsCommand(args)
31 | {
32 | if (args.size < 2)
33 | {
34 | return NotEnoughArgsError(2);
35 | }
36 |
37 | error = AddPlayerPoints(args[0], args[1]);
38 |
39 | if (IsDefined(error))
40 | {
41 | return error;
42 | }
43 | }
44 |
45 | TakePointsCommand(args)
46 | {
47 | if (args.size < 2)
48 | {
49 | return NotEnoughArgsError(2);
50 | }
51 |
52 | error = TakePlayerPoints(args[0], args[1]);
53 |
54 | if (IsDefined(error))
55 | {
56 | return error;
57 | }
58 | }
59 |
60 | GivePointsCommand(args)
61 | {
62 | if (args.size < 2)
63 | {
64 | return NotEnoughArgsError(2);
65 | }
66 |
67 | error = GivePoints(args[0], args[1]);
68 |
69 | if (IsDefined(error))
70 | {
71 | return error;
72 | }
73 | }
74 |
75 |
76 |
77 | /* Logic section */
78 |
79 | SetPlayerPoints(playerName, points)
80 | {
81 | player = FindPlayerByName(playerName);
82 |
83 | if (!IsDefined(player))
84 | {
85 | return PlayerDoesNotExistError(playerName);
86 | }
87 |
88 | player.score = int(points);
89 | }
90 |
91 | AddPlayerPoints(playerName, points)
92 | {
93 | player = FindPlayerByName(playerName);
94 |
95 | if (!IsDefined(player))
96 | {
97 | return PlayerDoesNotExistError(playerName);
98 | }
99 |
100 | player.score += int(points);
101 | }
102 |
103 | TakePlayerPoints(playerName, points)
104 | {
105 | player = FindPlayerByName(playerName);
106 |
107 | if (!IsDefined(player))
108 | {
109 | return PlayerDoesNotExistError(playerName);
110 | }
111 |
112 | player.score -= int(points);
113 | }
114 |
115 | GivePoints(playerName, points)
116 | {
117 | player = FindPlayerByName(playerName);
118 |
119 | if (!IsDefined(player))
120 | {
121 | return PlayerDoesNotExistError(playerName);
122 | }
123 |
124 | if (int(points) <= 0)
125 | {
126 | return;
127 | }
128 |
129 | if (int(self.score) >= int(points))
130 | {
131 | self.score -= int(points);
132 | player.score += int(points);
133 | }
134 | else
135 | {
136 | return NotEnoughPointsError();
137 | }
138 | }
--------------------------------------------------------------------------------
/chat_commands/zm/chat_command_give.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "givepowerup", "function", ::GivePowerupCommand, 2, [], array("spawnpowerup", "powerup", "pu"));
6 | CreateCommand(level.chat_commands["ports"], "giveperk", "function", ::GivePerkCommand, 2, [], array("perk"));
7 | }
8 |
9 |
10 |
11 | /* Command section */
12 |
13 | GivePowerupCommand(args)
14 | {
15 | if (args.size < 1)
16 | {
17 | return NotEnoughArgsError(1);
18 | }
19 |
20 | error = GivePlayerPowerup(args[0]);
21 |
22 | if (IsDefined(error))
23 | {
24 | return error;
25 | }
26 | }
27 |
28 | GivePerkCommand(args)
29 | {
30 | if (args.size < 2)
31 | {
32 | return NotEnoughArgsError(2);
33 | }
34 |
35 | error = GivePlayerPerk(args[0], args[1], true, true);
36 |
37 | if (IsDefined(error))
38 | {
39 | return error;
40 | }
41 | }
42 |
43 |
44 |
45 | /* Logic section */
46 |
47 | GivePlayerPowerup(powerupName)
48 | {
49 | powerupName = ToLower(powerupName);
50 |
51 | if (powerupName == "all")
52 | {
53 | foreach (powerup in GetAvailablePowerups())
54 | {
55 | level thread maps\mp\zombies\_zm_powerups::specific_powerup_drop(powerup, self.origin);
56 | }
57 | }
58 | else if (powerupName == "allbutnuke" || powerupName == "all_but_nuke" || powerupName == "allnonuke" || powerupName == "all_no_nuke")
59 | {
60 | foreach (powerup in GetAvailablePowerups())
61 | {
62 | if (powerup != "nuke")
63 | {
64 | level thread maps\mp\zombies\_zm_powerups::specific_powerup_drop(powerup, self.origin);
65 | }
66 | }
67 | }
68 | else
69 | {
70 | if (IsValidPowerup(powerupName))
71 | {
72 | level thread maps\mp\zombies\_zm_powerups::specific_powerup_drop(powerupName, self.origin);
73 | }
74 | else
75 | {
76 | return PowerupDoesNotExistError(powerupName);
77 | }
78 | }
79 | }
80 |
81 | GivePlayerPerk(playerName, perkName, enableMusic, enableAnimation)
82 | {
83 | player = FindPlayerByName(playerName);
84 |
85 | if (!IsDefined(player))
86 | {
87 | return PlayerDoesNotExistError(playerName);
88 | }
89 |
90 | if (ToLower(perkName) == "all")
91 | {
92 | foreach (perk in GetAvailablePerks())
93 | {
94 | player thread maps\mp\zombies\_zm_perks::give_perk(perk, 0);
95 | }
96 | }
97 | else
98 | {
99 | perkInfos = GetPerkInfos(perkName);
100 |
101 | if (perkInfos.size > 0)
102 | {
103 | if (enableMusic)
104 | {
105 | player thread maps\mp\zombies\_zm_audio::play_jingle_or_stinger( perkInfos["music"]);
106 | }
107 |
108 | if (enableAnimation)
109 | {
110 | player thread vending_trigger_post_think(player, perkInfos["perk_name"]);
111 | }
112 | else
113 | {
114 | player thread maps\mp\zombies\_zm_perks::give_perk(perkInfos["perk_name"], 0);
115 | }
116 | }
117 | else
118 | {
119 | return PerkDoesNotExistError(perkName);
120 | }
121 | }
122 | }
--------------------------------------------------------------------------------
/chat_commands/zm/chat_command_rounds.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "setround", "function", ::SetRoundCommand, 4, [], array("sr"));
6 | CreateCommand(level.chat_commands["ports"], "previousround", "function", ::PreviousRoundCommand, 4, [], array("pr"));
7 | CreateCommand(level.chat_commands["ports"], "nextround", "function", ::NextRoundCommand, 4, [], array("nr"));
8 | CreateCommand(level.chat_commands["ports"], "restartround", "function", ::RestartRoundCommand, 4, [], array("rr"));
9 | }
10 |
11 |
12 |
13 | /* Command section */
14 |
15 | SetRoundCommand(args)
16 | {
17 | if (args.size < 1)
18 | {
19 | return NotEnoughArgsError(1);
20 | }
21 |
22 | error = SetRound(args[0], true);
23 |
24 | if (IsDefined(error))
25 | {
26 | return error;
27 | }
28 |
29 | TellAllPlayers(array("^5" + self.name + " ^7changed the round to round ^5" + args[0]));
30 | }
31 |
32 | PreviousRoundCommand(args)
33 | {
34 | error = SetRound((level.round_number - 1), true);
35 |
36 | if (IsDefined(error))
37 | {
38 | return error;
39 | }
40 |
41 | TellAllPlayers(array("^5" + self.name + " ^7changed the round to the ^5previous round"));
42 | }
43 |
44 | NextRoundCommand(args)
45 | {
46 | error = SetRound((level.round_number + 1), true);
47 |
48 | if (IsDefined(error))
49 | {
50 | return error;
51 | }
52 |
53 | TellAllPlayers(array("^5" + self.name + " ^7changed the round to the ^5next round"));
54 | }
55 |
56 | RestartRoundCommand(args)
57 | {
58 | error = SetRound((level.round_number), false);
59 |
60 | if (IsDefined(error))
61 | {
62 | return error;
63 | }
64 |
65 | TellAllPlayers(array("^5" + self.name + " ^7restarted the ^5current ^7round"));
66 | }
67 |
68 |
69 |
70 | /* Logic section */
71 |
72 | SetRound(roundNumber, doNukeEffect)
73 | {
74 | roundNumber = int(roundNumber);
75 |
76 | if (roundNumber <= 0)
77 | {
78 | return InvalidRoundError(roundNumber);
79 | }
80 |
81 | self thread KillAllZombies(doNukeEffect);
82 |
83 | if (level.round_number > 1 || roundNumber > 1 || level.round_number == roundNumber)
84 | {
85 | level.round_number = roundNumber - 1;
86 | }
87 | }
88 |
89 | KillAllZombies(doNukeEffect)
90 | {
91 | zombies = GetAIArray("axis");
92 |
93 | level.zombie_total = 0;
94 | playerScore = self.score;
95 | playerKills = self.kills;
96 |
97 | if(IsDefined(zombies))
98 | {
99 | for(i=0; i < zombies.size; i++)
100 | {
101 | zombies[i] DoDamage(zombies[i].health * 5000, (0,0,0), self);
102 |
103 | wait 0.05;
104 | }
105 |
106 | if (IsDefined(doNukeEffect) && doNukeEffect)
107 | {
108 | self DoNuke();
109 | }
110 | }
111 |
112 | self.score = playerScore; // reset the player's score to the score he had before calling the command
113 | self.kills = playerKills; // reset the player's kills to the kills he had before calling the command
114 | }
115 |
116 | DoNuke()
117 | {
118 | foreach(player in level.players)
119 | {
120 | level thread maps\mp\zombies\_zm_powerups::nuke_powerup(self, player.team);
121 | player maps\mp\zombies\_zm_powerups::powerup_vo("nuke");
122 |
123 | zombies = getaiarray(level.zombie_team);
124 |
125 | player.zombie_nuked = arraysort(zombies, self.origin);
126 |
127 | player notify("nuke_triggered");
128 | }
129 | }
--------------------------------------------------------------------------------
/chat_commands/chat_command_unfair_aimbot.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | CreateCommand(level.chat_commands["ports"], "unfairaimbot", "function", ::UnfairAimbotCommand, 4, array("default_help_one_player"), array("aimbot"));
6 | }
7 |
8 |
9 |
10 | /* Command section */
11 |
12 | UnfairAimbotCommand(args)
13 | {
14 | if (args.size < 1)
15 | {
16 | return NotEnoughArgsError(1);
17 | }
18 |
19 | error = ToggleUnfairAimbot(args[0]);
20 |
21 | if (IsDefined(error))
22 | {
23 | return error;
24 | }
25 | }
26 |
27 |
28 |
29 | /* Logic section */
30 |
31 | ToggleUnfairAimbot(playerName)
32 | {
33 | player = FindPlayerByName(playerName);
34 |
35 | if (!IsDefined(player))
36 | {
37 | return PlayerDoesNotExistError(playerName);
38 | }
39 |
40 | commandName = "unfairaimbot";
41 |
42 | ToggleStatus(commandName, "Unfair Aimbot", player);
43 |
44 | if (GetStatus(commandName, player))
45 | {
46 | player thread DoUnfairAimbot(true);
47 | player thread ThreadUnfairAimbot();
48 | }
49 | else
50 | {
51 | player notify("chat_commands_unfair_aimbot_off");
52 | }
53 | }
54 |
55 | ThreadUnfairAimbot()
56 | {
57 | self endon("disconnect");
58 | self endon("chat_commands_unfair_aimbot_off");
59 |
60 | for(;;)
61 | {
62 | self waittill("spawned_player");
63 |
64 | self thread DoUnfairAimbot(true);
65 | }
66 | }
67 |
68 | DoUnfairAimbot(requiresAiming)
69 | {
70 | self endon("death");
71 | self endon("disconnect");
72 | self endon("chat_commands_unfair_aimbot_off");
73 |
74 | targets = [];
75 | damageFunction = undefined;
76 |
77 | if (IsMultiplayerMode())
78 | {
79 | damageFunction = ::MultiplayerDamage;
80 | }
81 | else
82 | {
83 | damageFunction = ::ZombiesDamage;
84 | }
85 |
86 | while (true)
87 | {
88 | currentTarget = undefined;
89 |
90 | if (IsMultiplayerMode())
91 | {
92 | targets = level.players;
93 | }
94 | else
95 | {
96 | targets = getaiarray("axis");
97 | }
98 |
99 | foreach(potentialTarget in targets)
100 | {
101 | if((potentialTarget == self) || (level.teamBased && self.pers["team"] == potentialTarget.pers["team"]) || (!isAlive(potentialTarget))) // don't aim at yourself, allies and targets that aren't spawned
102 | {
103 | continue; // skip
104 | }
105 |
106 | if(IsDefined(currentTarget))
107 | {
108 | if(Closer( self getTagOrigin( "j_head" ), potentialTarget getTagOrigin( "j_head" ), currentTarget getTagOrigin( "j_head" )))
109 | {
110 | currentTarget = potentialTarget;
111 | }
112 | }
113 | else
114 | {
115 | currentTarget = potentialTarget;
116 | }
117 | }
118 |
119 | if(IsDefined( currentTarget ))
120 | {
121 | if (!IsDefined(requiresAiming) || !requiresAiming || requiresAiming && self AdsButtonPressed())
122 | {
123 | self SetPlayerAngles(VectorToAngles(( currentTarget getTagOrigin( "j_head" )) - (self getTagOrigin( "j_head" ))));
124 |
125 | if(self AttackButtonPressed())
126 | {
127 | currentTarget thread [[damageFunction]](self);
128 | }
129 | }
130 | }
131 |
132 | wait 0.05;
133 | }
134 | }
135 |
136 | MultiplayerDamage(attackingPlayer)
137 | {
138 | // for normal (non headshot) kills replace "MOD_HEAD_SHOT" with "MOD_RIFLE_BULLET" and replace "head" with "torso"
139 | self thread [[level.callbackPlayerDamage]]( attackingPlayer, attackingPlayer, 100, 8, "MOD_HEAD_SHOT", attackingPlayer getCurrentWeapon(), (0,0,0), (0,0,0), "head", 0, 0 );
140 | }
141 |
142 | ZombiesDamage(attackingPlayer)
143 | {
144 | self dodamage(self.health * 5000, (0,0,0), attackingPlayer);
145 | }
--------------------------------------------------------------------------------
/mapvote/mapvote_zm_extend.gsc:
--------------------------------------------------------------------------------
1 | /*
2 | All the credits for this script go to DoktorSAS for both the source code and for helping me figuring this out
3 | https://github.com/DoktorSAS/PlutoniumT6Mapvote/blob/master/Zombies/mapvote.gsc
4 |
5 | menuSounds = array("zmb_meteor_activate", "zmb_spawn_powerup", "zmb_powerup_grabbed", "zmb_switch_flip", "zmb_elec_start", "zmb_perks_packa_ready");
6 | */
7 |
8 | #include common_scripts\utility;
9 | #include maps\mp\_utility;
10 |
11 | Init()
12 | {
13 | if (GetDvarInt("mapvote_enable"))
14 | {
15 | replaceFunc(maps\mp\zombies\_zm::intermission, ::OnIntermissionStart);
16 | }
17 | }
18 |
19 | OnIntermissionStart()
20 | {
21 | level.intermission = 1;
22 | level notify("intermission");
23 |
24 | for (i = 0; i < level.players.size; i++)
25 | {
26 | level.players[i] thread player_intermission();
27 | level.players[i] hide();
28 | level.players[i] setclientuivisibilityflag("hud_visible", 0);
29 |
30 | level.players[i] setclientthirdperson(0);
31 | level.players[i].health = 100;
32 | level.players[i] stopsounds();
33 | level.players[i] stopsounds();
34 | }
35 |
36 | wait GetDvarInt("mapvote_display_wait_time");
37 |
38 | [[level.mapvote_rotate_function]]();
39 |
40 | for (i = 0; i < level.players.size; i++)
41 | {
42 | level.players[i] notify("_zombie_game_over");
43 | level.players[i].sessionstate = "intermission";
44 | }
45 |
46 | players = get_players();
47 | i = 0;
48 | while (i < players.size)
49 | {
50 | setclientsysstate("levelNotify", "zi", players[i]);
51 | i++;
52 | }
53 | wait 0.25;
54 | players = get_players();
55 | i = 0;
56 | while (i < players.size)
57 | {
58 | setclientsysstate("lsm", "0", players[i]);
59 | i++;
60 | }
61 | level thread maps\mp\zombies\_zm::zombie_game_over_death();
62 | }
63 |
64 | player_intermission()
65 | {
66 | self closemenu();
67 | self closeingamemenu();
68 |
69 | level endon("stop_intermission");
70 | self endon("disconnect");
71 | self endon("death");
72 |
73 | self.score = self.score_total;
74 |
75 | self.spectatorclient = -1;
76 | self.killcamentity = -1;
77 | self.archivetime = 0;
78 | self.psoffsettime = 0;
79 | self.friendlydamage = undefined;
80 | points = getstructarray("intermission", "targetname");
81 | if (!isDefined(points) || points.size == 0)
82 | {
83 | points = getentarray("info_intermission", "classname");
84 |
85 | location = getDvar("ui_zm_mapstartlocation");
86 | for(i = 0;i < points.size;i++)
87 | {
88 | if(points[i].script_string == location)
89 | {
90 | points = points[i];
91 | }
92 | }
93 |
94 | if (points.size < 1)
95 | {
96 | return;
97 | }
98 | }
99 | if (isdefined(self.game_over_bg))
100 | self.game_over_bg destroy();
101 | org = undefined;
102 | while (1)
103 | {
104 | points = array_randomize(points);
105 | i = 0;
106 | while (i < points.size)
107 | {
108 | point = points[i];
109 | if (!isDefined(org))
110 | {
111 | self spawn(point.origin, point.angles);
112 | }
113 | if (isDefined(points[i].target))
114 | {
115 | if (!isDefined(org))
116 | {
117 | org = spawn("script_model", self.origin + vectorScale((0, 0, -1), 60));
118 | org setmodel("tag_origin");
119 | }
120 | org.origin = points[i].origin;
121 | org.angles = points[i].angles;
122 | j = 0;
123 | while (j < get_players().size)
124 | {
125 | player = get_players()[j];
126 | player camerasetposition(org);
127 | player camerasetlookat();
128 | player cameraactivate(1);
129 | j++;
130 | }
131 | speed = 20;
132 | if (isDefined(points[i].speed))
133 | {
134 | speed = points[i].speed;
135 | }
136 | target_point = getstruct(points[i].target, "targetname");
137 | dist = distance(points[i].origin, target_point.origin);
138 | time = dist / speed;
139 | q_time = time * 0.25;
140 | if (q_time > 1)
141 | {
142 | q_time = 1;
143 | }
144 | org moveto(target_point.origin, time, q_time, q_time);
145 | org rotateto(target_point.angles, time, q_time, q_time);
146 | wait(time - q_time);
147 | wait q_time;
148 | i++;
149 | continue;
150 | }
151 | i++;
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/chat_commands/zm/README.md:
--------------------------------------------------------------------------------
1 | # ZM Chat commands
2 |
3 | These scripts go in `scripts\zm`
4 |
5 | ## chat_command_give.gsc
6 |
7 | | Name | Description | Arguments expected | Example | Permission level |
8 | |---|---|---|---|---|
9 | | givepowerup | Makes the powerup drop on the player, which activates it | (1) the code name of the powerup | `!givepowerup full_ammo` | 2 |
10 | | giveperk | Gives the chosen perk to the targeted player | (1) the name of the targeted player (2) the name of the perk (multiple aliases available, see the GetPerkInfos function in [chat_commands](../chat_commands.gsc)) | `!giveperk me jugg` | 2 |
11 |
12 | You can use [chat_command_info.gsc](../README.md#chat_command_infogsc) to list available powerups and perks.
13 | Alternatively you can use [this](https://github.com/plutoniummod/t6-scripts/blob/main/ZM/Core/maps/mp/zombies/_zm_powerups.gsc#L95) to get powerup code names from your browser (first parameter of the `add_zombie_powerup` function calls). Note that this lists all powerups without taking into account whether they're available on the map you play on or not.
14 |
15 | You can disable the perk music and/or the bottle animation whenever you use `!giveperk` by changing the corresponding boolean(s) to false in the `GivePlayerPerk` function call.
16 | Perks that are only found in the Wunderfizz or special events/rewards are currently not supported.
17 |
18 | Note that like other scripts on this page this is a ZM only script, but this script also has a global version (works in both MP and ZM) that you can find [here](../chat_command_give.gsc) if you're looking for more give commands.
19 | You can have both installed at the same time as long as this script is in the `zm` folder and the global version is either in the `scripts` folder or the `mp` folder.
20 |
21 | | More examples |
22 | |---|
23 | | `!givepowerup all` |
24 | | `!givepowerup all_no_nuke` |
25 | | `!giveperk me all` |
26 | | `!giveperk me quickrevive` |
27 |
28 | ## chat_command_no_target.gsc
29 |
30 | Toggles whether the targeted player is in no target mode (invisible to zombies) or not.
31 |
32 | | # | Argument | Mandatory |
33 | |---|---|---|
34 | | 1 | The name of the player to toggle no target for | :white_check_mark: |
35 |
36 | | Examples |
37 | |---|
38 | | `!notarget me` |
39 | | `!notarget Resxt` |
40 |
41 | | Permission level |
42 | |---|
43 | | 3 |
44 |
45 | ## chat_command_points.gsc
46 |
47 | 4 related commands in one file:
48 |
49 | - Set points
50 | - Add points
51 | - Take/remove points
52 | - Give/transfer points
53 |
54 | | Name | Description | Arguments expected | Example | Permission level |
55 | |---|---|---|---|---|
56 | | setpoints | Changes how much points the targeted player has | (1) the name of the targeted player (2) the new amount of points to set | `!setpoints me 50000` | 3 |
57 | | addpoints | Gives points to the targeted player | (1) the name of the targeted player (2) the amount of points to give | `!addpoints Resxt 2500` | 3 |
58 | | takepoints | Takes/removes points from the targeted player | (1) the name of the targeted player (2) the amount of points to take from the player | `!takepoints Resxt 500` | 3 |
59 | | givepoints | Gives/transfers points from the player running the command to the targeted player | (1) the name of the targeted player (2) the amount of points to give | `!givepoints Resxt 2500` | 2 |
60 |
61 | ## chat_command_rounds.gsc
62 |
63 | 4 related commands in one file:
64 |
65 | - Set round
66 | - Change to previous round
67 | - Change to next round
68 | - Restart current round
69 |
70 | | Name | Description | Arguments expected | Example | Permission level |
71 | |---|---|---|---|---|
72 | | setround | Kills all zombies and changes the round to the specified round | (1) the desired round number | `!setround 10` | 4 |
73 | | previousround | Kills all zombies and changes the round to the previous round | none | `!previousround` | 4 |
74 | | nextround | Kills all zombies and changes the round to the next round | none | `!nextround` | 4 |
75 | | restartround | Kills all zombies and restarts the current round | none | `!restartround` | 4 |
76 |
77 | It's recommend to only run these commands after at least one zombie has fully spawned (got out of the ground and starts walking).
78 | Your score and kills don't increase when running these commands even if you're technically the one who kills the zombies.
79 |
--------------------------------------------------------------------------------
/chat_commands/chat_command_info.gsc:
--------------------------------------------------------------------------------
1 | #include scripts\chat_commands;
2 |
3 | Init()
4 | {
5 | if (IsMultiplayerMode())
6 | {
7 | CreateCommand(level.chat_commands["ports"], "listattachments", "function", ::ListAttachmentsCommand, 2);
8 | }
9 |
10 | if (!IsMultiplayerMode())
11 | {
12 | CreateCommand(level.chat_commands["ports"], "listpowerups", "function", ::ListPowerupsCommand, 2);
13 | CreateCommand(level.chat_commands["ports"], "listperks", "function", ::ListPerksCommand, 2);
14 | }
15 |
16 | CreateCommand(level.chat_commands["ports"], "listweapons", "function", ::ListWeaponsCommand, 2);
17 | }
18 |
19 |
20 |
21 | /* Command section */
22 |
23 | ListWeaponsCommand(args)
24 | {
25 | self thread ListWeapons(args[0]);
26 | }
27 |
28 | ListAttachmentsCommand(args)
29 | {
30 | error = self thread ListAttachments(args[0]);
31 |
32 | if (IsDefined(error))
33 | {
34 | return error;
35 | }
36 | }
37 |
38 | ListPowerupsCommand(args)
39 | {
40 | error = self thread ListPowerups();
41 |
42 | if (IsDefined(error))
43 | {
44 | return error;
45 | }
46 | }
47 |
48 | ListPerksCommand(args)
49 | {
50 | error = self thread ListPerks();
51 |
52 | if (IsDefined(error))
53 | {
54 | return error;
55 | }
56 | }
57 |
58 |
59 |
60 | /* Logic section */
61 |
62 | ListWeapons(displayMode)
63 | {
64 | PrintLn("-------------------------------");
65 | PrintLn("Available weapons");
66 | PrintLn("-------------------------------");
67 |
68 | if (IsMultiplayerMode())
69 | {
70 | if (IsDefined(displayMode))
71 | {
72 | foreach (index in GetArrayKeys(level.tbl_weaponids))
73 | {
74 | PrintLn(getweapondisplayname(level.tbl_weaponids[index]["reference"] + "_mp"));
75 | }
76 | }
77 | else
78 | {
79 | foreach (index in GetArrayKeys(level.tbl_weaponids))
80 | {
81 | PrintLn(level.tbl_weaponids[index]["reference"] + "_mp");
82 | }
83 | }
84 | }
85 | else
86 | {
87 | if (IsDefined(displayMode))
88 | {
89 | foreach (weapon in GetArrayKeys(level.zombie_weapons))
90 | {
91 | PrintLn(getweapondisplayname(weapon));
92 | }
93 | }
94 | else
95 | {
96 | foreach (weapon in GetArrayKeys(level.zombie_weapons))
97 | {
98 | PrintLn(weapon);
99 | }
100 | }
101 | }
102 | }
103 |
104 | ListAttachments(weaponName)
105 | {
106 | weaponIndex = 0;
107 | finalWeaponName = "";
108 |
109 | if (IsDefined(weaponName))
110 | {
111 | if (GetSubStr(weaponName, weaponName.size - 3, weaponName.size) != "_mp")
112 | {
113 | weaponName += "_mp";
114 | }
115 |
116 | if (!IsValidWeapon(weaponName))
117 | {
118 | return WeaponDoesNotExistError(weaponName);
119 | }
120 |
121 | weaponIndex = getbaseweaponitemindex(weaponName);
122 | finalWeaponName = weaponName;
123 | }
124 | else
125 | {
126 | finalWeaponName = self getcurrentweapon();
127 | weaponIndex = getbaseweaponitemindex(self getcurrentweapon());
128 | }
129 |
130 | attachments = StrTok(level.tbl_weaponids[weaponIndex]["attachment"], " ");
131 | attachmentsFinal = [];
132 |
133 | // remove everything after _ in attachments name as this was always returning wrong names
134 | // for example it would return silencer_shotgun when the actual codename is just silencer etc
135 | foreach (attachment in attachments)
136 | {
137 | attachmentsFinal = AddElementToArray(attachmentsFinal, StrTok(attachment, "_")[0]);
138 | }
139 |
140 | PrintLn("-------------------------------");
141 | PrintLn("Available attachments for " + getweapondisplayname(finalWeaponName) + " (" + StrTok(finalWeaponName, "_mp")[0] + "_mp" + ")");
142 | PrintLn("-------------------------------");
143 |
144 | foreach (attachment in attachmentsFinal)
145 | {
146 | PrintLn(attachment);
147 | }
148 | }
149 |
150 | ListPowerups()
151 | {
152 | powerups = GetAvailablePowerups();
153 |
154 | self thread TellPlayer(powerups, 2);
155 |
156 | PrintLn("-------------------------------");
157 | PrintLn("Available powerups");
158 | PrintLn("-------------------------------");
159 |
160 | foreach (powerup in powerups)
161 | {
162 | PrintLn(powerup);
163 | }
164 | }
165 |
166 | ListPerks()
167 | {
168 | perks = GetAvailablePerks();
169 |
170 | self thread TellPlayer(perks, 2);
171 |
172 | PrintLn("-------------------------------");
173 | PrintLn("Available perks");
174 | PrintLn("-------------------------------");
175 |
176 | foreach (perk in perks)
177 | {
178 | PrintLn(perk);
179 | }
180 | }
--------------------------------------------------------------------------------
/chat_commands/README.md:
--------------------------------------------------------------------------------
1 | # Chat commands
2 |
3 | Let players execute commands by typing in the chat.
4 | This can be used to display text to the player, for example the server rules or execute GSC code, just like console commands.
5 | This works in private games, on dedicated servers that use [IW4MAdmin](https://github.com/RaidMax/IW4M-Admin) and those that don't.
6 | If you do monitor your server with [IW4MAdmin](https://github.com/RaidMax/IW4M-Admin) then make sure to read the [notes section](#notes).
7 |
8 | The `chat_command` scripts you find here work for both MP and ZM and can be placed in the `scripts` folder.
9 | MP only scripts are in the [mp](mp) folder and ZM only scripts are in the [zm](zm) folder.
10 |
11 | - **[IMPORTANT]** For dedicated servers, by default, each command is available on any server listed in the `level.chat_commands["ports"]` array variable declared in `chat_commands.gsc`.
12 | If you use a port other than 4976 and 4977 you have to add them manually to this array to be used by any command.
13 | If a specific command should only be available on certain server(s) (ports) then edit the CreateCommand function in the command's script to use an array you declare yourself with the port(s) you want
14 |
15 | ## chat_commands.gsc
16 |
17 | The core script that holds the configuration, runs all the chat logic and holds utils function that are shared between all the `chat_command` scripts.
18 |
19 | - **[IMPORTANT]** Installing `chat_commands.gsc` is **mandatory** to make the commands work as this is the core of this whole system and all the command scripts depend on it.
20 | - **[IMPORTANT]** By default `chat_commands.gsc` is made to be placed in the `scripts` folder.
21 | If you place it in the `scripts\mp` or `scripts\zm` folder instead then you will need to update the include of each command script accordingly (first line) or you will get errors.
22 | You simply have to replace `#include scripts\chat_commands` with `#include scripts\mp\chat_commands;` or `#include scripts\zm\chat_commands;` in each of your command script.
23 |
24 | Also note that `chat_commands.gsc` doesn't provide any command on its own.
25 | You must install at least one command script to be able to use commands. Otherwise it will always say that you don't have any command.
26 |
27 | ### Main features
28 |
29 | - Easy per server (port) commands configuration. You can either pass an array of one server port, or multiple, or the `level.chat_commands["ports"]` array to easily add a command to one/multiple/all servers
30 | - Chat text print and functions support
31 | - Optional permissions level system to restrict commands to players with a certain permission level (disabled by default)
32 | - All exceptions are handled with error messages (no commands on the server, not enough arguments, command doesn't exist, command doesn't have any help message, player doesn't exist etc.)
33 | - A `commands` command that lists all available commands on the server you're on dynamically (only lists commands you have access to if the permission system is enabled)
34 | - A `help` command that explains how to use a given command. For example `help map` (only works on commands you have access to if the permission system is enabled)
35 | - `alias` and `aliases` commands that list the available aliases for a command. For example `alias godmode` (only works on commands you have access to if the permission system is enabled)
36 | - All commands that require a target work with `me`. Also it doesn't matter how you type the player's name as long as you type his full name or type the beginning of his username (has to be unique, see [#3](https://github.com/Resxt/Plutonium-IW5-Scripts/pull/3/commits/2efa784709b5c42811510c67c3e6a2fc5eb3fc70)).
37 | - Configurable command prefix. Set to `!` by default
38 | - A plugin system to easily allow adding/removing commands. Each command has its own GSC file to easily add/remove/review/configure your commands. This also makes contributing by creating a PR to add a command a lot easier
39 |
40 | ### Dvars
41 |
42 | Here are the dvars you can configure:
43 |
44 | | Name | Description | Default value | Accepted values |
45 | |---|---|---|---|
46 | | cc_debug | Toggle whether the script is in debug mode or not. This is used to print players GUID in the console when they connect | 0 | 0 or 1 |
47 | | cc_prefix | The symbol to type before the command name in the chat. Only one character is supported. The `/` symbol won't work normally as it's reserved by the game. If you use the `/` symbol as prefix you will need to type double slash in the game | ! | Any working symbol |
48 | | cc_permission_enabled | Toggle whether the permission system is enabled or not. If it's disabled any player can run any available command | 0 | 0 or 1 |
49 | | cc_permission_mode | Changes whether the permission dvars values are names or guids | name | name or guid |
50 | | cc_permission_default | The default permission level players who aren't found in the permission dvars will be granted | 1 | Any plain number from 0 to `cc_permission_max` |
51 | | cc_permission_max | The maximum/most elevated permission level | 4 | Any plain number above 0 |
52 | | cc_permission_0 | A list of names or guids of players who will be granted the permission level 0 when connecting (no access to any command) | "" | Names or guids (depending on `cc_permission_mode`). Each value is separated with a colon (:) |
53 | | cc_permission_1 | A list of names or guids of players who will be granted the permission level 1 when connecting | "" | Names or guids (depending on `cc_permission_mode`). Each value is separated with a colon (:) |
54 | | cc_permission_2 | A list of names or guids of players who will be granted the permission level 2 when connecting | "" | Names or guids (depending on `cc_permission_mode`). Each value is separated with a colon (:) |
55 | | cc_permission_3 | A list of names or guids of players who will be granted the permission level 3 when connecting | "" | Names or guids (depending on `cc_permission_mode`). Each value is separated with a colon (:) |
56 | | cc_permission_4 | A list of names or guids of players who will be granted the permission level 4 when connecting | "" | Names or guids (depending on `cc_permission_mode`). Each value is separated with a colon (:) |
57 |
58 | ### Configuration
59 |
60 | Below is an example CFG showing how each dvars can be configured.
61 | The values you see are the default values that will be used if you don't set a dvar.
62 |
63 | ```c
64 | set cc_debug 0
65 | set cc_prefix "!"
66 | set cc_permission_enabled 0
67 | set cc_permission_mode "name"
68 | set cc_permission_default 1
69 | set cc_permission_max 4
70 | set cc_permission_0 ""
71 | set cc_permission_1 ""
72 | set cc_permission_2 ""
73 | set cc_permission_3 ""
74 | set cc_permission_4 ""
75 | ```
76 |
77 | ### Notes
78 |
79 | - To pass an argument with a space you need to put `'` around it. For example if a player name is `The Moonlight` then you would write `!teleport 'The Moonlight' Resxt`
80 | - If you use [IW4MAdmin](https://github.com/RaidMax/IW4M-Admin) make sure you have a different commands prefix to avoid conflicts. For example `!` for IW4MAdmin commands and `.` for this script. The commands prefix can be modified by changing the value of the `cc_prefix` dvar. As for [IW4MAdmin](https://github.com/RaidMax/IW4M-Admin), at the time of writing, if you want to change it you'll need to change the value of [CommandPrefix](https://github.com/RaidMax/IW4M-Admin/wiki/Configuration#advanced-configuration)
81 | - If you prefer to display information (error messages, status change etc.) in the player's chat rather than on his screen you can do that on a dedicated server. For this you need to install [t6-gsc-utils.dll](https://github.com/fedddddd/t6-gsc-utils#installation) (dedicated server only) and change `self IPrintLnBold(message);` to `self tell(message);` in the `TellPlayer` function
82 | - Support for clantags was added. You can use the player names in-game or in the dvars without having to care about their clantag. The [setClantag function](https://github.com/fedddddd/t6-gsc-utils#chat) replaces the player name so additional work was required to make the script ignore the clantag
83 |
84 | ## chat_command_dvars.gsc
85 |
86 | 3 related commands in one file:
87 |
88 | - Print server dvar
89 | - Change server dvar
90 | - Change client dvar
91 |
92 | | Name | Description | Arguments expected | Example | Permission level |
93 | |---|---|---|---|---|
94 | | getdvar | Prints the (server) dvar value in the player's chat | (1) the dvar name | `!getdvar g_speed` | 2 |
95 | | setdvar | Changes a dvar on the server | (1) the dvar name (2) the new dvar value | `!setdvar jump_height 500` | 4 |
96 | | setclientdvar | Changes a dvar on the targeted player | (1) the name of the player (2) the dvar name (3) the new dvar value | `!setclientdvar Resxt cg_thirdperson 1` | 4 |
97 |
98 | ## chat_command_freeze.gsc
99 |
100 | Toggles whether the targeted player can move or not.
101 | Note that this does not work during the prematch period.
102 |
103 | | # | Argument | Mandatory |
104 | |---|---|---|
105 | | 1 | The name of the player to freeze/unfreeze | :white_check_mark: |
106 |
107 | | Examples |
108 | |---|
109 | | `!freeze me` |
110 | | `!freeze Resxt` |
111 |
112 | | Permission level |
113 | |---|
114 | | 3 |
115 |
116 | ## chat_command_give.gsc
117 |
118 |
119 |
120 | - Give weapon (with or without attachment(s) and camo in MP)
121 |
122 | | Name | Description | Arguments expected | Example | Permission level |
123 | |---|---|---|---|---|
124 | | giveweapon | Gives the targeted player the specific weapon. Attachments and camo can be passed | (1) the name of the player (2) the weapon codename (3) optional, camo index from 0 to 45 | `!giveweapon me tar21_mp` | 2 |
125 |
126 | You can use [chat_command_info.gsc](#chat_command_infogsc) to list available weapons and their attachments.
127 | Alternatively you can use [this](https://forum.plutonium.pw/topic/1909/resource-stat-modification-checks-other-structures) to get weapon/attachment names from your browser.
128 |
129 | | More examples (MP) | Description |
130 | |---|---|
131 | | `!giveweapon me kard_mp` | Give weapon by codename |
132 | | `!giveweapon me an94` | Give weapon by codename without `_mp` at the end |
133 | | `!giveweapon me ballista_mp 15` | Give weapon by codename with camo index 15, in this case the Gold camo |
134 | | `!giveweapon me as50_mp+ir` | Give weapon by codename with an attachment |
135 | | `!giveweapon me dsr50_mp+silencer 16` | Give weapon by codename with an attachment and camo index 16, in this case the Diamond camo |
136 | | `!giveweapon me ballista+is+silencer 12` | Give weapon by codename without `_mp` at the end, with 2 attachments and with camo index 12, in this case the Art of War camo |
137 | | `!giveweapon me tar21+silencer+reflex+fastads 11` | Give weapon by codename with 3 attachments and camo index 11, in this case the Cherry Blossom camo |
138 |
139 | | More examples (ZM) |
140 | |---|
141 | | `!giveweapon me m14_zm` (give weapon by codename) |
142 | | `!giveweapon me m1911_upgraded_zm` (give upgraded/PAPed weapon by codename) |
143 |
144 | ## chat_command_god_mode.gsc
145 |
146 | Toggles whether the targeted player is in god mode (invincible) or not.
147 |
148 | | # | Argument | Mandatory |
149 | |---|---|---|
150 | | 1 | The name of the player to toggle god mode for | :white_check_mark: |
151 |
152 | | Examples |
153 | |---|
154 | | `!god me` |
155 | | `!god Resxt` |
156 |
157 | | Permission level |
158 | |---|
159 | | 3 |
160 |
161 | ## chat_command_info.gsc
162 |
163 | 4 related commands in one file:
164 |
165 | - List available weapons
166 | - List available attachments (MP only)
167 | - List available powerups (ZM only)
168 | - List available perks (ZM only)
169 |
170 | | Name | Description | Arguments expected | Example | Permission level |
171 | |---|---|---|---|---|
172 | | listweapons | Prints all the available weapons. No argument prints code names, any argument will print display/human readable names instead | (1) optional, any text | `!listweapons` | 2 |
173 | | listattachments | Prints all the available attachments for that weapon. No argument prints available attachments for the weapon you're holding, a valid weapon codename as argument will print this weapon's available attachments instead | (1) optional, valid weapon codename | `!listattachments dsr50_mp` | 2 |
174 | | listpowerups | Prints all the available powerups in the map you're currently playing on | | `!listpowerups` | 2 |
175 | | listperks | Prints all the available perks in the map you're currently playing on | | `!listperks` | 2 |
176 |
177 | You can check [this](https://forum.plutonium.pw/topic/1909/resource-stat-modification-checks-other-structures) to get weapon/attachment names from your browser instead.
178 |
179 | | More examples |
180 | |---|
181 | | `!listweapons` |
182 | | `!listweapons display` |
183 | | `!listattachments` |
184 | | `!listattachments tar21_mp` |
185 | | `!listattachments dsr50` |
186 |
187 | ## chat_command_invisible.gsc
188 |
189 | Toggles invisibility on the targeted player.
190 | Note that this does not make the player invisible to bots in multiplayer, in the sense that even if they can't see the player, they will still know his position and shoot him.
191 | However, in addition to being invisible, you will also be ignored by zombies in the zombies mode.
192 |
193 | | # | Argument | Mandatory |
194 | |---|---|---|
195 | | 1 | The name of the player to make invisible/visible | :white_check_mark: |
196 |
197 | | Examples |
198 | |---|
199 | | `!invisible me` |
200 | | `!invisible Resxt` |
201 |
202 | | Permission level |
203 | |---|
204 | | 3 |
205 |
206 | ## chat_command_kick.gsc
207 |
208 | Kicks the targeted player.
209 | Note that due to some game limitations you cannot kick the host.
210 |
211 | | # | Argument | Mandatory |
212 | |---|---|---|
213 | | 1 | The name of the player to kick | :white_check_mark: |
214 |
215 | | Examples |
216 | |---|
217 | | `!kick Resxt` |
218 |
219 | | Permission level |
220 | |---|
221 | | 4 |
222 |
223 | ## chat_command_permissions.gsc
224 |
225 | 2 related commands in one file:
226 |
227 | - Get permission level
228 | - Set permission level
229 |
230 | | Name | Description | Arguments expected | Example | Permission level |
231 | |---|---|---|---|---|
232 | | getpermission | Prints the targeted player's current permission level in the player's chat | (1) the name of the targeted player | `!getpermission me` | 2 |
233 | | setpermission | Changes the targeted player's permission level (for the current game only) | (1) the name of the targeted player (2) the permission level to grant | `!setpermission Resxt 4` | 4 |
234 |
235 | ## chat_command_teleport.gsc
236 |
237 | Teleports a player to the position of another player.
238 |
239 | | # | Argument | Mandatory |
240 | |---|---|---|
241 | | 1 | The name of the player to teleport | :white_check_mark: |
242 | | 2 | The name of the player to teleport to | :white_check_mark: |
243 |
244 | | Examples |
245 | |---|
246 | | `!teleport me Eldor` |
247 | | `!teleport Eldor me` |
248 | | `!teleport Eldor Rektinator` |
249 |
250 | | Permission level |
251 | |---|
252 | | 2 |
253 |
254 | ## chat_command_text_help.gsc
255 |
256 | Prints how to use the `commands` and the `help command` commands in the player's chat.
257 |
258 | | Example |
259 | |---|
260 | | `!help` |
261 |
262 | | Permission level |
263 | |---|
264 | | 1 |
265 |
266 | ## chat_command_ufo_mode.gsc
267 |
268 | Toggles whether the targeted player can use the ufo mode or not.
269 | This allows the player to fly around the map by holding the melee button and, if death barrier protection is on, to get out of map without dying.
270 |
271 | The death barrier protection is on by default.
272 | In ZM it will remove the death barriers for the entire team.
273 | In MP it will put the player in god mode until disabling UFO mode (running the command again).
274 | If the player had god mode from the god mode chat command he will keep the god mode.
275 |
276 | | # | Argument | Mandatory |
277 | |---|---|---|
278 | | 1 | The name of the player to toggle ufo mode for | :white_check_mark: |
279 |
280 | | Examples |
281 | |---|
282 | | `!ufomode me` |
283 | | `!ufomode Resxt` |
284 |
285 | | Permission level |
286 | |---|
287 | | 3 |
288 |
289 | ## chat_command_unfair_aimbot.gsc
290 |
291 | Toggles unfair aimbot on the targeted player.
292 |
293 | | # | Argument | Mandatory |
294 | |---|---|---|
295 | | 1 | The name of the player to toggle unfair aimbot for | :white_check_mark: |
296 |
297 | | Examples |
298 | |---|
299 | | `!unfairaimbot me` |
300 | | `!unfairaimbot Resxt` |
301 |
302 | | Permission level |
303 | |---|
304 | | 4 |
305 |
306 | ## chat_command_unlimited_ammo.gsc
307 |
308 | Toggles unlimited ammo on the targeted player.
309 |
310 | | # | Argument | Mandatory |
311 | |---|---|---|
312 | | 1 | The name of the player to toggle unlimited ammo for | :white_check_mark: |
313 |
314 | | Examples |
315 | |---|
316 | | `!unlimitedammo me` |
317 | | `!unlimitedammo Resxt` |
318 |
319 | | Permission level |
320 | |---|
321 | | 3 |
322 |
--------------------------------------------------------------------------------
/mapvote/README.md:
--------------------------------------------------------------------------------
1 | # Mapvote
2 |
3 | A customizable mapvote script for both multiplayer and zombies.
4 |
5 | Note that this only works for dedicated servers.
6 | You can still run the script in a private match to get a preview of the menu and configure it the way you like before using it on your server but it won't rotate to your chosen map/mode due to a technical limitation the game has.
7 |
8 | This is heavily inspired by [LastDemon99's IW5 mapvote](https://github.com/LastDemon99/IW5_VoteSystem).
9 | I also re-used some code from [DoktorSAS T6 mapvote](https://github.com/DoktorSAS/PlutoniumT6Mapvote).
10 | Huge thanks to both of them.
11 |
12 | 
13 | *Multiplayer mode. Mouse and keyboard input. Changed settings: limits modes: 3, horizontal spacing: 100*
14 |
15 | 
16 | *Zombies mode. Controller input. Changed settings: red colors, accent mode: max*
17 |
18 | ## mapvote.gsc
19 |
20 | This script can be installed in any folder depending on what you want to do.
21 | The recommended solution is to just install it in the `scripts` folder so that it will work out of the box for both mp and zm.
22 | Since it's disabled by default it won't affect your servers or run unless you enable it so it's safe to have it in the `scripts` folder.
23 |
24 | **[IMPORTANT]** Installing `mapvote_mp_extend.gsc` in `scripts\mp` is **mandatory** to make the mapvote work normally in multiplayer.
25 | **[IMPORTANT]** Installing `mapvote_zm_extend.gsc` in `scripts\zm` is **mandatory** to make the mapvote work normally in zombies.
26 |
27 | ### Main features
28 |
29 | - It allows up to 12 elements (maps + modes) to be displayed on screen
30 | - You can configure how much maps and modes you want on screen
31 | - It will automatically adapt the amount of elements to display if you don't choose a fixed amount of maps/modes to show
32 | - It has separate map and mode choices for multiplayer
33 | - It supports custom CFG
34 | - It supports custom gamemode names in multiplayer and custom map names in zombies
35 | - It rotates a random map from the list when there are no votes for maps. Same applies for modes in multiplayer too
36 | - You can choose to rotate a random map and mode from a list you define when the human players count is between a min and max values you define (disabled by default)
37 | - Controllers are fully supported and work out of the box
38 | - It has a good level of customization
39 | - It has a debug mode to quickly preview the menu and print some values in the console
40 |
41 | ### Getting started
42 |
43 | To configure the menu before putting it on your server I recommend running it in a custom game with the `mapvote_debug` dvar set to `1`.
44 | To do that use this command in the console `set mapvote_debug 1` before running a custom game.
45 | Start a custom game and you will see the menu. Everything will work but map rotation which is normal.
46 | You can then configure the dvars directly in your console and restart the map with `map_restart` in the console to edit the menu quickly and get your perfect setup.
47 |
48 | Note that by default the mapvote will be activated on all of your servers.
49 | If you run multiple servers and want it off on certain servers you will need to add `set mapvote_enable 0` in the server's CFG.
50 |
51 | ### Configuration
52 |
53 | Below is an example CFG showing how each dvars can be configured.
54 | The values you see are the default values that will be used if you don't set a dvar.
55 |
56 |
57 | Multiplayer CFG
58 |
59 | ```c
60 | set mapvote_enable 1
61 | set mapvote_maps "Aftermath:Cargo:Carrier:Drone:Express:Hijacked:Meltdown:Overflow:Plaza:Raid:Slums:Standoff:Turbine:Yemen:Nuketown:Downhill:Mirage:Hydro:Grind:Encore:Magma:Vertigo:Studio:Uplink:Detour:Cove:Rush:Dig:Frost:Pod:Takeoff" // Default value: Every map, including DLC maps
62 | set mapvote_modes "Team Deathmatch,tdm:Domination,dom:Hardpoint,koth" // Default value: Team Deathmatch, Domination and Hardpoint
63 | set mapvote_limits_maps 0
64 | set mapvote_limits_modes 0
65 | set mapvote_limits_max 12
66 | set mapvote_colors_selected "blue"
67 | set mapvote_colors_unselected "white"
68 | set mapvote_colors_timer "blue"
69 | set mapvote_colors_timer_low "red"
70 | set mapvote_colors_help_text "white"
71 | set mapvote_colors_help_accent "blue"
72 | set mapvote_colors_help_accent_mode "standard"
73 | set mapvote_sounds_menu_enabled 1
74 | set mapvote_sounds_timer_enabled 1
75 | set mapvote_vote_time 30
76 | set mapvote_blur_level 2.5
77 | set mapvote_blur_fade_in_time 2
78 | set mapvote_horizontal_spacing 75
79 | set mapvote_display_wait_time 1
80 | set mapvote_default_rotation_enable 0
81 | set mapvote_default_rotation_maps "Hijacked:Raid:Nuketown"
82 | set mapvote_default_rotation_modes "tdm"
83 | set mapvote_default_rotation_min_players 0
84 | set mapvote_default_rotation_max_players 0
85 | ```
86 |
87 | Here are some pre-set values if you want to quickly copy/paste something
88 |
89 | | Description | Value |
90 | |---|---|
91 | | All base game maps | "Aftermath:Cargo:Carrier:Drone:Express:Hijacked:Meltdown:Overflow:Plaza:Raid:Slums:Standoff:Turbine:Yemen" |
92 | | All DLC maps | "Nuketown:Downhill:Mirage:Hydro:Grind:Encore:Magma:Vertigo:Studio:Uplink:Detour:Cove:Rush:Dig:Frost:Pod:Takeoff" |
93 | | Classic modes | "Team Deathmatch,tdm:Domination,dom:Hardpoint,koth" |
94 | | Objective modes | "Demolition,dem:Headquaters,hq:Capture the Flag,ctf" |
95 | | Alternative modes | "Kill Confirmed,conf:One Flag CTF,oneflag" |
96 | | Party modes | "Gun Game,gun:One in the Chamber,oic:Sharpshooter,shrp:Sticks & Stones,sas" |
97 | | FFA 24/7 | "Free for All,dm" |
98 | | SND 24/7 | "Search & Destroy,sd" |
99 |
100 |
101 |
102 |
103 | Zombies CFG
104 |
105 | ```c
106 | set mapvote_enable 1
107 | set mapvote_maps "Bus Depot,Bus Depot,zm_standard_transit:Town,Town,zm_standard_town:Farm,Farm,zm_standard_farm:Mob of The Dead,Mob of The Dead,zm_classic_prison:Nuketown,Nuketown,zm_standard_nuked:Origins,Origins,zm_classic_tomb:Buried,Buried,zm_classic_processing:Die Rise,Die Rise,zm_classic_rooftop" // All survival/classic maps but Tranzit, including DLC maps
108 | set mapvote_limits_max 12
109 | set mapvote_colors_selected "blue"
110 | set mapvote_colors_unselected "white"
111 | set mapvote_colors_timer "blue"
112 | set mapvote_colors_timer_low "red"
113 | set mapvote_colors_help_text "white"
114 | set mapvote_colors_help_accent "blue"
115 | set mapvote_colors_help_accent_mode "standard"
116 | set mapvote_vote_time 30
117 | set mapvote_blur_level 2.5
118 | set mapvote_blur_fade_in_time 2
119 | set mapvote_horizontal_spacing 75
120 | set mapvote_display_wait_time 1
121 | set mapvote_default_rotation_enable 0
122 | set mapvote_default_rotation_maps "Town,zm_standard_town:Farm,zm_standard_farm"
123 | set mapvote_default_rotation_min_players 0
124 | set mapvote_default_rotation_max_players 0
125 | ```
126 |
127 | Here are some pre-set values if you want to quickly copy/paste something
128 |
129 | | Description | Value |
130 | |---|---|
131 | | Tranzit & Tranzit survival maps | "Tranzit,Tranzit,zm_classic_transit:Bus Depot,Bus Depot,zm_standard_transit:Town,Town,zm_standard_town:Farm,Farm,zm_standard_farm" |
132 | | DLC maps | "Buried,Buried,zm_classic_processing:Die Rise,Die Rise,zm_classic_rooftop:Mob of The Dead,Mob of The Dead,zm_classic_prison:Nuketown,Nuketown,zm_standard_nuked:Origins,Origins,zm_classic_tomb" |
133 | | Grief maps | "Buried (Grief),Buried,zm_grief_street:Mob of The Dead (Grief),Mob of The Dead,zm_grief_cellblock:Farm (Grief),Farm,zm_grief_farm:Town (Grief),Town,zm_grief_town:Bus Depot (Grief),Bus Depot,zm_grief_transit" |
134 | | Turned maps | "Buried (Turned),Buried,zm_cleansed_street:Diner (Turned),Diner,zm_cleansed_diner" |
135 |
136 |
137 |
138 | ### Dvars
139 |
140 | Here are the dvars you can configure:
141 |
142 |
143 | Multiplayer dvars
144 |
145 | | Name | Description | Accepted values |
146 | |---|---|---|
147 | | mapvote_enable | Toggle whether the mapvote is activated or not. 0 is off and 1 is on | 0 or 1 |
148 | | mapvote_debug | Toggle whether the mapvote runs in debug mode or not. This will display the mapvote menu a few seconds after starting the game. 0 is off and 1 is on | 0 or 1 |
149 | | mapvote_maps | A list of the maps that are available for rotation | Any map name. Each map is separated with a colon (:) |
150 | | mapvote_modes | A list of the modes that are available for rotation. The first parameter is how the mode will be displayed, it can be set to anything you like, the second parameter is the name of the cfg file to load | Any text followed by a comma (,) and then the cfg name. Each block is separated with a colon (:) |
151 | | mapvote_limits_maps | The amount of maps to display. 0 will handle it automatically | Any plain number from 0 to `mapvote_limits_max` |
152 | | mapvote_limits_modes | The amount of modes to display. 0 will handle it automatically | Any plain number from 0 to `mapvote_limits_max` |
153 | | mapvote_limits_max | The maximum amount of elements to display (maps + modes) | 2, 4, 6, 8, 10, 12 |
154 | | mapvote_colors_selected | The color of the text when hovered or selected. This is also the color of the votes count | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
155 | | mapvote_colors_unselected | The color of the text when not hovered and not selected | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
156 | | mapvote_colors_timer | The color of the timer as long as it has more than 5 seconds remaining | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
157 | | mapvote_colors_timer_low | The color of the timer when it has 5 or less seconds remaining | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
158 | | mapvote_colors_help_text | The color of the help text at the bottom explaining how to use the menu | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
159 | | mapvote_colors_help_accent | The color of the accented text of the help text at the bottom | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
160 | | mapvote_colors_help_accent_mode | The accenting mode for the help text. `standard` only puts the accent color on the button to press and `max` puts it on both the buttons and the action it does | standard or max |
161 | | mapvote_sounds_menu_enabled | Toggle whether the mapvote menu sounds are enabled or not. 0 is off and 1 is on | 0 or 1 |
162 | | mapvote_sounds_timer_enabled | Toggle whether the timer will start making a beeping sound every second when there's 5 or less seconds remaining to vote or not. 0 is off and 1 is on | 0 or 1 |
163 | | mapvote_vote_time | The time the vote lasts (in seconds) | Any plain number above 5 |
164 | | mapvote_blur_level | The amount of blur to put when the mapvote menu starts to show. The max recommended value is 5. 0 disables it | Any number |
165 | | mapvote_blur_fade_in_time | The time (in seconds) it takes for the blur to reach `mapvote_blur_level`. For example if you set it to 10 and `mapvote_blur_level` is 5 then it will progressively blur the screen from 0 to 5 in 10 seconds | Any number |
166 | | mapvote_horizontal_spacing | The horizontal spacing between the map/mode names on the left and the vote counts on the right. I recommend setting this value according to the longest map or mode name length so that it doesn't overlap with the vote counts | Any plain number |
167 | | mapvote_display_wait_time | Once the killcam ends, the time to wait before displaying the vote menu (in seconds) | Any number superior or equal to 0.05 |
168 | | mapvote_default_rotation_enable | Toggle whether the default rotation system is activated or not. This allows you to have one or more map(s) and mode(s) rotate by default when the human player count is between `mapvote_default_rotation_min_players` and `mapvote_default_rotation_max_players` (inclusive). 0 is off and 1 is on | 0 or 1 |
169 | | mapvote_default_rotation_maps | A list of the maps that are available for default rotation | Any map name. Each map is separated with a colon (:) |
170 | | mapvote_default_rotation_modes | A list of the modes that are available for default rotation. It needs to be the name of a CFG file | Any cfg file name. Each cfg file name is separated with a colon (:) |
171 | | mapvote_default_rotation_min_players | The minimum amount of human players required to rotate the default rotation instead of showing the mapvote. If the human players count is smaller than this then it will display the mapvote | Any plain number from 0 to 18 |
172 | | mapvote_default_rotation_max_players | The maximum amount of human players required to rotate the default rotation instead of showing the mapvote. If the human players count is higher than this then it will display the mapvote | Any plain number from 0 to 18 |
173 |
174 |
175 |
176 |
177 | Zombies dvars
178 |
179 | | Name | Description | Accepted values |
180 | |---|---|---|
181 | | mapvote_enable | Toggle whether the mapvote is activated or not. 0 is off and 1 is on | 0 or 1 |
182 | | mapvote_debug | Toggle whether the mapvote runs in debug mode or not. This will display the mapvote menu a few seconds after starting the game. 0 is off and 1 is on | 0 or 1 |
183 | | mapvote_maps | A list of the maps that are available for rotation, including how you want to display it and which CFG to load | Any text followed by a comma (,) with then the map name followed by a comma (,) and finally the CFG file name. Each block is separated with a colon (:) |
184 | | mapvote_limits_max | The maximum amount of maps to display | Any plain number from 2 to 12 |
185 | | mapvote_colors_selected | The color of the text when hovered or selected. This is also the color of the votes count | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
186 | | mapvote_colors_unselected | The color of the text when not hovered and not selected | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
187 | | mapvote_colors_timer | The color of the timer as long as it has more than 5 seconds remaining | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
188 | | mapvote_colors_timer_low | The color of the timer when it has 5 or less seconds remaining | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
189 | | mapvote_colors_help_text | The color of the help text at the bottom explaining how to use the menu | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
190 | | mapvote_colors_help_accent | The color of the accented text of the help text at the bottom | red, green, yellow, blue, cyan, purple, white, grey, gray, black |
191 | | mapvote_colors_help_accent_mode | The accenting mode for the help text. `standard` only puts the accent color on the button to press and `max` puts it on both the buttons and the action it does | standard or max |
192 | | mapvote_vote_time | The time the vote lasts (in seconds) | Any plain number above 5 |
193 | | mapvote_blur_level | The amount of blur to put when the mapvote menu starts to show. The max recommended value is 5. 0 disables it | Any number |
194 | | mapvote_blur_fade_in_time | The time (in seconds) it takes for the blur to reach `mapvote_blur_level`. For example if you set it to 10 and `mapvote_blur_level` is 5 then it will progressively blur the screen from 0 to 5 in 10 seconds | Any number |
195 | | mapvote_horizontal_spacing | The horizontal spacing between the map names on the left and the vote counts on the right. I recommend setting this value according to the longest map name length so that it doesn't overlap with the vote counts | Any plain number |
196 | | mapvote_display_wait_time | Once the game over screen ends, the time to wait before displaying the vote menu (in seconds) | Any number above 0.05 |
197 | | mapvote_default_rotation_enable | Toggle whether the default rotation system is activated or not. This allows you to have one or more map(s) and mode(s) rotate by default when the human player count is between `mapvote_default_rotation_min_players` and `mapvote_default_rotation_max_players` (inclusive). 0 is off and 1 is on | 0 or 1 |
198 | | mapvote_default_rotation_maps | A list of the maps that are available for default rotation | The map name followed by a comma (,) and then the CFG file name. Each block is separated with a colon (:) |
199 | | mapvote_default_rotation_min_players | The minimum amount of human players required to rotate the default rotation instead of showing the mapvote. If the human players count is smaller than this then it will display the mapvote | Any plain number from 0 to 18 |
200 | | mapvote_default_rotation_max_players | The maximum amount of human players required to rotate the default rotation instead of showing the mapvote. If the human players count is higher than this then it will display the mapvote | Any plain number from 0 to 18 |
201 |
202 |
203 |
204 | ### Notes
205 |
206 | - If right click is set to toggle ads then pressing right click will make the player go up by one every 0.25s until he right clicks again.
207 | If I didn't change it to be that way players with toggle ads would have to press right click twice to go up by one all the time.
208 | Now instead they simply right click once to start going up and right click again to stop which is a better user experience.
209 | - When there's only one map/mode, instead of showing a single vote possibility, your single map/mode will be hidden to make the user experience better but it will still always rotate to your one map/mode
210 | - All the sounds (menu and timer) don't work in zombies.
211 | The game seems to progressively (but quickly) mute the sound during the [intermission phase](https://github.com/plutoniummod/t6-scripts/blob/main/ZM/Core/maps/mp/zombies/_zm.gsc).
212 | Until I find a way to play sounds in the intermission, if it's even possible, the zombies menu will sadly not have any sound
213 | - If some map/mode names or vote count don't display at all then you probably have other scripts that create HUD elements and there's too much elements to display so either remove your script or lower `mapvote_limits_max` so that the mapvote will have less elements to display
214 | - When two maps/modes have the same votes, the lowest one in the list will win. In the future it would be nice to randomize between both
215 | - Ending the game with ESC doesn't work when in debug mode.
216 | Use `map_restart` in the console when your script is compiled. And if you want to leave use `disconnect` in the console until this is fixed
217 | - [Zombies++](https://github.com/Paintball/BO2-GSC-Releases/tree/master/Zombies%20Mods/Zombies%2B%2B/v1.2/Source%20Code) will cause conflict with the ZM mapvote but you can easily make them both work without having to remove one.
218 | Open the `clientids.gsc` file provided by Zombies++ and comment these lines: `368, 504, 505`.
219 | The part that overrides the end game logic in Zombies++ should no longer interfere with the mapvote.
220 | Both scripts should work normally after rotating to a new map / restarting the server
221 |
222 | ## mapvote_mp_extend.gsc
223 |
224 | A small script that goes with `mapvote.gsc` to make it work in multiplayer.
225 | It has to be installed in the `scripts\mp` directory since it contains multiplayer only code.
226 | Putting it in `scripts` or `scripts\zm` will throw an error when booting up a zombies map.
227 |
228 | ## mapvote_zm_extend.gsc
229 |
230 | A small script that goes with `mapvote.gsc` to make it work in zombies.
231 | It has to be installed in the `scripts\zm` directory since it contains zombies only code.
232 | Putting it in `scripts` or `scripts\mp` will throw an error when booting up a multiplayer map.
233 |
--------------------------------------------------------------------------------
/chat_commands/chat_commands.gsc:
--------------------------------------------------------------------------------
1 | /*
2 | ==========================================================================
3 | | Game: Plutonium T6 |
4 | | Description : Display text and run GSC code |
5 | | by typing commands in the chat |
6 | | Author: Resxt |
7 | ==========================================================================
8 | | https://github.com/Resxt/Plutonium-T6-Scripts/tree/main/chat_commands |
9 | ==========================================================================
10 | */
11 |
12 |
13 |
14 | /* Init section */
15 |
16 | Main()
17 | {
18 | InitChatCommands();
19 | }
20 |
21 | InitChatCommands()
22 | {
23 | InitChatCommandsDvars();
24 |
25 | level.chat_commands = []; // don't touch
26 | level.chat_commands["ports"] = array("4976", "4977"); // an array of the ports of all your servers you want to have the script running on. This is useful to easily pass this array as first arg of CreateCommand to have the command on all your servers
27 | level.chat_commands["no_commands_message"] = array("^1No commands found", "You either ^1didn't add any chat_command file ^7to add a new command ^1or ^7there are ^1no command configured on this port", "chat_commands.gsc is ^1just the base system. ^7It doesn't provide any command on its own", "Also ^1make sure the ports are configured properly ^7in the CreateCommand function of your command file(s)"); // the lines to print in the chat when the server doesn't have any command added
28 | level.chat_commands["no_commands_wait"] = 6; // time to wait between each line in when printing that specific message in the chat
29 |
30 | level thread OnPlayerConnect();
31 | level thread ChatListener();
32 | }
33 |
34 | InitChatCommandsDvars()
35 | {
36 | SetDvarIfNotInitialized("cc_debug", 0);
37 | SetDvarIfNotInitialized("cc_prefix", "!");
38 |
39 | SetDvarIfNotInitialized("cc_permission_enabled", 0);
40 | SetDvarIfNotInitialized("cc_permission_mode", "name");
41 | SetDvarIfNotInitialized("cc_permission_default", 1);
42 | SetDvarIfNotInitialized("cc_permission_max", 4);
43 |
44 | for (i = 0; i <= GetDvarInt("cc_permission_max"); i++)
45 | {
46 | SetDvarIfNotInitialized("cc_permission_" + i, "");
47 | }
48 | }
49 |
50 |
51 |
52 | /* Commands section */
53 |
54 | /*
55 | the ports of the servers this command will be created for
56 | the name of the command, this is what players will type in the chat
57 | the type of the command: is for arrays of text to display text in the player's chat and is to execute a function
58 | when is "text" this is an array of lines to print in the chat. When is "function" this is a function pointer (a reference to a function)
59 | (optional, if no value is provided then anyone who's permission level is default or above can run the command) the minimum permission level required to run this command. For example if this is set to 3 then any user with permission level 3 or 4 will be able to run this command
60 | (optional) an array of the lines to print when typing the help command in the chat followed by a command name. You can also pass an array of one preset string to have it auto generated, for example: ["default_help_one_player"]
61 | */
62 | CreateCommand(serverPorts, commandName, commandType, commandValue, commandMinimumPermission, commandHelp, commandAliases)
63 | {
64 | currentPort = GetDvar("net_port");
65 | commandName = ToLower(commandName); // always create commands in lower case to avoid any potential casing issue
66 |
67 | foreach (serverPort in serverPorts)
68 | {
69 | if (serverPort == currentPort)
70 | {
71 | level.commands[serverPort][commandName]["type"] = commandType;
72 |
73 | if (IsDefined(commandHelp) && commandHelp.size > 0)
74 | {
75 | commandHelpMessage = commandHelp;
76 | commandHelpString = commandHelp[0];
77 |
78 | if (commandHelpString == "default_help_one_player")
79 | {
80 | commandHelpMessage = array("Example: " + GetDvar("cc_prefix") + commandName + " me", "Example: " + GetDvar("cc_prefix") + commandName + " Resxt");
81 | }
82 | else if (commandHelpString == "default_help_two_players")
83 | {
84 | commandHelpMessage = array("Example: " + GetDvar("cc_prefix") + commandName + " me Resxt", "Example: " + GetDvar("cc_prefix") + commandName + " Resxt me", "Example: " + GetDvar("cc_prefix") + commandName + " Resxt Eldor");
85 | }
86 |
87 | level.commands[serverPort][commandName]["help"] = commandHelpMessage;
88 | }
89 |
90 | if (commandType == "text")
91 | {
92 | level.commands[serverPort][commandName]["text"] = commandValue;
93 | }
94 | else if (commandType == "function")
95 | {
96 | level.commands[serverPort][commandName]["function"] = commandValue;
97 | }
98 |
99 | if (IsDefined(commandAliases) && commandAliases.size > 0)
100 | {
101 | level.commands[serverPort][commandName]["aliases"] = commandAliases;
102 | }
103 |
104 | if (IsDefined(commandMinimumPermission))
105 | {
106 | level.commands[serverPort][commandName]["permission"] = commandMinimumPermission;
107 | }
108 | else
109 | {
110 | level.commands[serverPort][commandName]["permission"] = GetDvarInt("cc_permission_default");
111 | }
112 | }
113 | }
114 | }
115 |
116 | ExecuteChatCommand(command, args, player)
117 | {
118 | if (command["type"] == "text")
119 | {
120 | player thread TellPlayer(command["text"], 2);
121 | }
122 | else if (command["type"] == "function")
123 | {
124 | error = player [[command["function"]]](args);
125 |
126 | if (IsDefined(error))
127 | {
128 | player thread TellPlayer(error, 1.5);
129 | }
130 | }
131 | }
132 |
133 | TryExecuteChatCommand(commandValue, commandName, args, player)
134 | {
135 | if (!PermissionIsEnabled() || PlayerHasSufficientPermissions(player, commandValue["permission"]))
136 | {
137 | ExecuteChatCommand(commandValue, args, player);
138 | }
139 | else
140 | {
141 | player thread TellPlayer(InsufficientPermissionError(player GetPlayerPermissionLevel(), commandName, commandValue["permission"]), 1.5);
142 | }
143 | }
144 |
145 |
146 |
147 | /* Chat section */
148 |
149 | ChatListener()
150 | {
151 | while (true)
152 | {
153 | level waittill("say", message, player);
154 |
155 | if (message[0] != GetDvar("cc_prefix")) // For some reason checking for the buggy character doesn't work so we start at the second character if the first isn't the command prefix
156 | {
157 | message = GetSubStr(message, 1); // Remove the random/buggy character at index 0, get the real message
158 | }
159 |
160 | if (message[0] != GetDvar("cc_prefix")) // If the message doesn't start with the command prefix
161 | {
162 | continue; // stop
163 | }
164 |
165 | if (PermissionIsEnabled() && player GetPlayerPermissionLevel() == 0)
166 | {
167 | player thread TellPlayer(InsufficientPermissionError(0), 1);
168 | continue; // stop
169 | }
170 |
171 | commandArray = StrTok(message, " "); // Separate the command by space character. Example: ["!map", "mp_dome"]
172 | command = ToLower(commandArray[0]); // The command as text in lower text to support any casing. Example: !map
173 | args = []; // The arguments passed to the command. Example: ["mp_dome"]
174 | arg = "";
175 |
176 | for (i = 1; i < commandArray.size; i++)
177 | {
178 | checkedArg = commandArray[i];
179 |
180 | if (checkedArg[0] != "'" && arg == "")
181 | {
182 | args = AddElementToArray(args, checkedArg);
183 | }
184 | else if (checkedArg[0] == "'")
185 | {
186 | arg = StrTok(checkedArg, "'")[0] + " ";
187 | }
188 | else if (checkedArg[checkedArg.size - 1] == "'")
189 | {
190 | args = AddElementToArray(args, (arg + StrTok(checkedArg, "'")[0]));
191 | arg = "";
192 | }
193 | else
194 | {
195 | arg += (checkedArg + " ");
196 | }
197 | }
198 |
199 | if (IsDefined(level.commands[GetDvar("net_port")]))
200 | {
201 | if (command == GetDvar("cc_prefix") + "commands") // commands command
202 | {
203 | if (GetDvarInt("cc_permission_enabled"))
204 | {
205 | playerCommands = [];
206 |
207 | foreach (commandName in GetArrayKeys(level.commands[GetDvar("net_port")]))
208 | {
209 | if (PlayerHasSufficientPermissions(player, level.commands[GetDvar("net_port")][commandName]["permission"]))
210 | {
211 | playerCommands = AddElementToArray(playerCommands, commandName);
212 | }
213 | }
214 |
215 | player thread TellPlayer(playerCommands, 2, true);
216 | }
217 | else
218 | {
219 | player thread TellPlayer(GetArrayKeys(level.commands[GetDvar("net_port")]), 2, true);
220 | }
221 | }
222 | // help command (with args, for example help godmode)
223 | else if (command == GetDvar("cc_prefix") + "help" && !IsDefined(level.commands[GetDvar("net_port")]["help"]) || command == GetDvar("cc_prefix") + "help" && IsDefined(level.commands[GetDvar("net_port")]["help"]) && args.size >= 1)
224 | {
225 | if (args.size < 1)
226 | {
227 | player thread TellPlayer(NotEnoughArgsError(1), 1.5);
228 | }
229 | else
230 | {
231 | commandValue = level.commands[GetDvar("net_port")][args[0]];
232 |
233 | if (IsDefined(commandValue))
234 | {
235 | if (!PermissionIsEnabled() || PlayerHasSufficientPermissions(player, commandValue["permission"]))
236 | {
237 | commandHelp = commandValue["help"];
238 |
239 | if (IsDefined(commandHelp))
240 | {
241 | player thread TellPlayer(commandHelp, 1.5);
242 | }
243 | else
244 | {
245 | player thread TellPlayer(CommandHelpDoesNotExistError(args[0]), 1);
246 | }
247 | }
248 | else
249 | {
250 | player thread TellPlayer(InsufficientPermissionError(player GetPlayerPermissionLevel(), args[0], commandValue["permission"]), 1.5);
251 | }
252 | }
253 | else
254 | {
255 | originalCommandName = ToLower(GetCommandNameFromAlias(args[0]));
256 |
257 | if (args[0] == "commands" || args[0] == "help" || args[0] == "aliases" || args[0] == "alias")
258 | {
259 | player thread TellPlayer(CommandHelpDoesNotExistError(args[0]), 1);
260 | }
261 | else if (args[0] == originalCommandName) // the command wasn't found while searching by its name and all the commands aliases
262 | {
263 | player thread TellPlayer(CommandDoesNotExistError(args[0]), 1);
264 | }
265 | else
266 | {
267 | commandHelp = level.commands[GetDvar("net_port")][originalCommandName]["help"];
268 |
269 | if (IsDefined(commandHelp))
270 | {
271 | if (!PermissionIsEnabled() || PlayerHasSufficientPermissions(player, level.commands[GetDvar("net_port")][originalCommandName]["permission"]))
272 | {
273 | player thread TellPlayer(commandHelp, 1.5);
274 | }
275 | else
276 | {
277 | player thread TellPlayer(InsufficientPermissionError(player GetPlayerPermissionLevel(), args[0], level.commands[GetDvar("net_port")][originalCommandName]["permission"]), 1.5);
278 | }
279 | }
280 | else
281 | {
282 | player thread TellPlayer(CommandHelpDoesNotExistError(args[0]), 1);
283 | }
284 | }
285 | }
286 | }
287 | }
288 | else if (command == GetDvar("cc_prefix") + "alias" || command == GetDvar("cc_prefix") + "aliases") // alias/aliases command
289 | {
290 | if (args.size < 1)
291 | {
292 | player thread TellPlayer(NotEnoughArgsError(1), 1.5);
293 | }
294 | else
295 | {
296 | commandValue = level.commands[GetDvar("net_port")][args[0]];
297 |
298 | if (IsDefined(commandValue))
299 | {
300 | if (!PermissionIsEnabled() || PlayerHasSufficientPermissions(player, commandValue["permission"]))
301 | {
302 | commandAliases = commandValue["aliases"];
303 |
304 | if (IsDefined(commandAliases) && commandAliases.size > 0)
305 | {
306 | player thread TellPlayer(commandAliases, 1.5);
307 | }
308 | else
309 | {
310 | player thread TellPlayer(CommandAliasesDoesNotExistError(args[0]), 1);
311 | }
312 | }
313 | else
314 | {
315 | player thread TellPlayer(InsufficientPermissionError(player GetPlayerPermissionLevel(), args[0], commandValue["permission"]), 1.5);
316 | }
317 | }
318 | else
319 | {
320 | originalCommandName = ToLower(GetCommandNameFromAlias(args[0]));
321 |
322 | if (args[0] == "commands" || args[0] == "help" || args[0] == "aliases" || args[0] == "alias")
323 | {
324 | player thread TellPlayer(CommandAliasesDoesNotExistError(args[0]), 1);
325 | }
326 | else if (args[0] == originalCommandName) // the command wasn't found while searching by its name and all the commands aliases
327 | {
328 | player thread TellPlayer(CommandDoesNotExistError(args[0]), 1);
329 | }
330 | else
331 | {
332 | commandAliases = level.commands[GetDvar("net_port")][originalCommandName]["aliases"];
333 |
334 | if (IsDefined(commandAliases))
335 | {
336 | if (!PermissionIsEnabled() || PlayerHasSufficientPermissions(player, level.commands[GetDvar("net_port")][originalCommandName]["permission"]))
337 | {
338 | commandAliases = AddElementToArray(commandAliases, originalCommandName);
339 |
340 | player thread TellPlayer(commandAliases, 1.5);
341 | }
342 | else
343 | {
344 | player thread TellPlayer(InsufficientPermissionError(player GetPlayerPermissionLevel(), args[0], level.commands[GetDvar("net_port")][originalCommandName]["permission"]), 1.5);
345 | }
346 | }
347 | else
348 | {
349 | player thread TellPlayer(CommandAliasesDoesNotExistError(args[0]), 1);
350 | }
351 | }
352 | }
353 | }
354 | }
355 | else // any other command
356 | {
357 | inputCommandName = GetSubStr(command, 1);
358 | commandValue = level.commands[GetDvar("net_port")][inputCommandName];
359 |
360 | if (IsDefined(commandValue)) // try to find the command by its original name
361 | {
362 | TryExecuteChatCommand(commandValue, inputCommandName, args, player);
363 | }
364 | else // try to find the command by one of its aliases
365 | {
366 | originalCommandName = ToLower(GetCommandNameFromAlias(inputCommandName));
367 |
368 | if (inputCommandName == originalCommandName) // the command wasn't found while searching by its name and all the commands aliases
369 | {
370 | player thread TellPlayer(CommandDoesNotExistError(inputCommandName), 1);
371 | }
372 | else
373 | {
374 | TryExecuteChatCommand(level.commands[GetDvar("net_port")][originalCommandName], inputCommandName, args, player);
375 | }
376 | }
377 | }
378 | }
379 | else
380 | {
381 | player thread TellPlayer(level.chat_commands["no_commands_message"], level.chat_commands["no_commands_wait"], false);
382 | }
383 | }
384 | }
385 |
386 | TellPlayer(messages, waitTime, isCommand)
387 | {
388 | if (!IsDefined(waitTime))
389 | {
390 | waitTime = 1;
391 | }
392 |
393 | for (i = 0; i < messages.size; i++)
394 | {
395 | message = messages[i];
396 |
397 | if (IsDefined(isCommand) && isCommand)
398 | {
399 | message = GetDvar("cc_prefix") + message;
400 | }
401 |
402 | self IPrintLnBold(message);
403 |
404 | if (i < (messages.size - 1)) // Don't unnecessarily wait after the last message has been displayed
405 | {
406 | wait waitTime;
407 | }
408 | }
409 | }
410 |
411 | TellAllPlayers(messages, waitTime)
412 | {
413 | if (!IsDefined(waitTime))
414 | {
415 | waitTime = 1;
416 | }
417 |
418 | foreach (player in level.players)
419 | {
420 | player TellPlayer(messages, waitTime, false);
421 | }
422 | }
423 |
424 |
425 |
426 | /* Player section */
427 |
428 | OnPlayerConnect()
429 | {
430 | for(;;)
431 | {
432 | level waittill("connected", player);
433 |
434 | if (player IsBot())
435 | {
436 | continue; // stop
437 | }
438 |
439 | if (!IsDefined(player.pers["chat_commands"]))
440 | {
441 | player.pers["chat_commands"] = [];
442 |
443 | if (DebugIsOn())
444 | {
445 | Print("GUID of " + player.name + ": " + player.guid);
446 | }
447 | }
448 |
449 | player SetPlayerPermissionLevel(player GetPlayerPermissionLevelFromDvar());
450 | }
451 | }
452 |
453 |
454 |
455 | /* Error functions section */
456 |
457 | CommandDoesNotExistError(commandName)
458 | {
459 | return array("The command " + commandName + " doesn't exist", "Type " + GetDvar("cc_prefix") + "commands to get a list of commands");
460 | }
461 |
462 | CommandHelpDoesNotExistError(commandName)
463 | {
464 | return array("The command " + commandName + " doesn't have any help message");
465 | }
466 |
467 | CommandAliasesDoesNotExistError(commandName)
468 | {
469 | return array("The command " + commandName + " doesn't have any alias");
470 | }
471 |
472 | InsufficientPermissionError(playerPermissionLevel, commandName, requiredPermissionLevel)
473 | {
474 | if (playerPermissionLevel == 0)
475 | {
476 | return array("You don't have the permissions to run any command");
477 | }
478 |
479 | return array("Access to the ^5" + commandName + " ^7command refused", "Your permission level is ^5" + playerPermissionLevel + " ^7and the minimum permission level for this command is ^5" + requiredPermissionLevel);
480 | }
481 |
482 | InvalidPermissionLevelError(requestedPermissionLevel)
483 | {
484 | return array("^5" + requestedPermissionLevel + " ^7is not a valid permission level", "Permission levels range from ^50 ^7to ^5" + GetDvarInt("cc_permission_max"));
485 | }
486 |
487 | NotEnoughArgsError(minimumArgs)
488 | {
489 | return array("Not enough arguments supplied", "At least " + minimumArgs + " argument expected");
490 | }
491 |
492 | PlayerDoesNotExistError(playerName)
493 | {
494 | return array("Player " + playerName + " was not found");
495 | }
496 |
497 | DvarDoesNotExistError(dvarName)
498 | {
499 | return array("The dvar " + dvarName + " doesn't exist");
500 | }
501 |
502 | InvalidRoundError(roundNumber)
503 | {
504 | return array(roundNumber + " is not a valid round number");
505 | }
506 |
507 | WeaponDoesNotExistError(weaponName)
508 | {
509 | return array("The weapon " + weaponName + " doesn't exist");
510 | }
511 |
512 | NotEnoughPointsError()
513 | {
514 | return array("You don't have enough points to do that");
515 | }
516 |
517 | PowerupDoesNotExistError(powerupName)
518 | {
519 | return array("The powerup " + powerupName + " doesn't exist or isn't available on this map");
520 | }
521 |
522 | PerkDoesNotExistError(perkName)
523 | {
524 | return array("The perk " + perkName + " doesn't exist, isn't available on this map or doesn't have this alias");
525 | }
526 |
527 |
528 |
529 | /* Utils section */
530 |
531 | FindPlayerByName(name)
532 | {
533 | if (ToLower(name) == "me")
534 | {
535 | return self;
536 | }
537 |
538 | potentialPlayersFound = 0;
539 | foundPlayer = undefined;
540 |
541 | foreach (player in level.players)
542 | {
543 | playerName = player.name;
544 |
545 | if (IsSubStr(playerName, "]")) // support for clantags
546 | {
547 | playerName = StrTok(playerName, "]")[1]; // ignore the clantag
548 | }
549 |
550 | if (ToLower(playerName) == ToLower(name)) // if we get an exact match return the player
551 | {
552 | return player;
553 | }
554 |
555 | if (ToLower(GetSubStr(playerName, 0, name.size)) == ToLower(name)) // found a player who's name starts with the given text
556 | {
557 | potentialPlayersFound++;
558 | }
559 |
560 | if (potentialPlayersFound == 1 && !IsDefined(foundPlayer)) // store the first player we find, since we only return one if and only if it only finds one
561 | {
562 | foundPlayer = player;
563 | }
564 | }
565 |
566 | if (potentialPlayersFound == 1) // we only found one player who's name starts with the given text so it's safe to return the player we found
567 | {
568 | return foundPlayer;
569 | }
570 | }
571 |
572 | ToggleStatus(commandName, commandDisplayName, player)
573 | {
574 | SetStatus(commandName, player, !GetStatus(commandName, player));
575 |
576 | statusMessage = "^2ON";
577 |
578 | if (!GetStatus(commandName, player))
579 | {
580 | statusMessage = "^1OFF";
581 | }
582 |
583 | if (self.name == player.name)
584 | {
585 | self TellPlayer(array("You changed your " + commandDisplayName + " status to " + statusMessage), 1);
586 | }
587 | else
588 | {
589 | self TellPlayer(array(player.name + " " + commandDisplayName + " status changed to " + statusMessage), 1);
590 | player TellPlayer(array(self.name + " changed your " + commandDisplayName + " status to " + statusMessage), 1);
591 | }
592 | }
593 |
594 | GetStatus(commandName, player)
595 | {
596 | if (!IsDefined(player.pers["chat_commands"])) // avoid undefined errors in the console
597 | {
598 | player.pers["chat_commands"] = [];
599 | }
600 |
601 | if (!IsDefined(player.pers["chat_commands"]["status"])) // avoid undefined errors in the console
602 | {
603 | player.pers["chat_commands"]["status"] = [];
604 | }
605 |
606 | if (!IsDefined(player.pers["chat_commands"]["status"][commandName])) // status is set to OFF/false by default
607 | {
608 | SetStatus(commandName, player, false);
609 | }
610 |
611 | return player.pers["chat_commands"]["status"][commandName];
612 | }
613 |
614 | SetStatus(commandName, player, status)
615 | {
616 | player.pers["chat_commands"]["status"][commandName] = status;
617 | }
618 |
619 | GetPlayerPermissionLevelFromDvar()
620 | {
621 | for (dvarIndex = GetDvarInt("cc_permission_max"); dvarIndex > 0; dvarIndex--)
622 | {
623 | dvarName = "cc_permission_" + dvarIndex;
624 |
625 | foreach (value in StrTok(GetDvar(dvarName), ":"))
626 | {
627 | if (GetDvar("cc_permission_mode") == "name")
628 | {
629 | playerName = self.name;
630 |
631 | if (IsSubStr(playerName, "]")) // support for clantags
632 | {
633 | playerName = StrTok(playerName, "]")[1]; // ignore the clantag
634 | }
635 |
636 | if (ToLower(value) == ToLower(playerName))
637 | {
638 | return dvarIndex;
639 | }
640 | }
641 | else
642 | {
643 | if (value == self.guid)
644 | {
645 | return dvarIndex;
646 | }
647 | }
648 | }
649 | }
650 |
651 | return GetDvarInt("cc_permission_default");
652 | }
653 |
654 | GetPlayerPermissionLevel()
655 | {
656 | return self.pers["chat_commands"]["permission_level"];
657 | }
658 |
659 | SetPlayerPermissionLevel(newPermissionLevel)
660 | {
661 | self.pers["chat_commands"]["permission_level"] = newPermissionLevel;
662 | }
663 |
664 | PlayerHasSufficientPermissions(player, targetedPermissionLevel)
665 | {
666 | playerPermissionLevel = player GetPlayerPermissionLevel();
667 |
668 | if (playerPermissionLevel == 0)
669 | {
670 | return false;
671 | }
672 |
673 | if (!IsDefined(targetedPermissionLevel))
674 | {
675 | return true;
676 | }
677 |
678 | return playerPermissionLevel >= targetedPermissionLevel;
679 | }
680 |
681 | /*
682 | Returns the original command name of if it exists
683 | If is not a valid alias then it just returns itself meaning it didn't find a related command
684 | */
685 | GetCommandNameFromAlias(aliasToFind)
686 | {
687 | aliasToFind = ToLower(aliasToFind);
688 |
689 | foreach (commandName in GetArrayKeys(level.commands[GetDvar("net_port")]))
690 | {
691 | if (IsDefined(level.commands[GetDvar("net_port")][commandName]["aliases"]))
692 | {
693 | foreach (alias in level.commands[GetDvar("net_port")][commandName]["aliases"])
694 | {
695 | if (alias == aliasToFind)
696 | {
697 | return commandName;
698 | }
699 | }
700 | }
701 | }
702 |
703 | return aliasToFind;
704 | }
705 |
706 | DvarIsInitialized(dvarName)
707 | {
708 | result = GetDvar(dvarName);
709 | return result != "";
710 | }
711 |
712 | SetDvarIfNotInitialized(dvarName, dvarValue)
713 | {
714 | if (!DvarIsInitialized(dvarName))
715 | {
716 | SetDvar(dvarName, dvarValue);
717 | }
718 | }
719 |
720 | IsBot()
721 | {
722 | return IsDefined(self.pers["isBot"]) && self.pers["isBot"];
723 | }
724 |
725 | TargetIsMyself(targetName)
726 | {
727 | return ToLower(targetName) == "me" || ToLower(targetName) == ToLower(self.name);
728 | }
729 |
730 | AddElementToArray(array, element)
731 | {
732 | array[array.size] = element;
733 | return array;
734 | }
735 |
736 | DebugIsOn()
737 | {
738 | return GetDvarInt("cc_debug");
739 | }
740 |
741 | PermissionIsEnabled()
742 | {
743 | return GetDvarInt("cc_permission_enabled");
744 | }
745 |
746 | IsMultiplayerMode()
747 | {
748 | return !IsDefined(level.zombiemode) || !level.zombiemode;
749 | }
750 |
751 | IsValidWeapon(weaponName)
752 | {
753 | weaponIndex = 0;
754 | weaponIndex = getbaseweaponitemindex(weaponName);
755 |
756 | return weaponIndex != 0;
757 | }
758 |
759 | GetAvailablePowerups()
760 | {
761 | powerups = [];
762 |
763 | foreach (powerup in GetArrayKeys(level.zombie_powerups))
764 | {
765 | if (powerup != "insta_kill_ug")
766 | {
767 | powerups = AddElementToArray(powerups, powerup);
768 | }
769 | }
770 |
771 | return powerups;
772 | }
773 |
774 | IsValidPowerup(powerupName)
775 | {
776 | return IsInArray(GetAvailablePowerups(), powerupName);
777 | }
778 |
779 | GetAvailablePerks()
780 | {
781 | perks = [];
782 |
783 | if (GetEntArray("random_perk_machine", "targetname").size == 0)
784 | {
785 | foreach (machine in GetEntArray( "zombie_vending", "targetname" ))
786 | {
787 | if (machine.script_noteworthy != "specialty_weapupgrade")
788 | {
789 | perks = AddElementToArray(perks, machine.script_noteworthy);
790 | }
791 | }
792 | }
793 | else
794 | {
795 | perks = level._random_perk_machine_perk_list;
796 | }
797 |
798 | return perks;
799 | }
800 |
801 | GetPerkInfos(perkName)
802 | {
803 | perkInfos = [];
804 | realPerkName = "";
805 |
806 | switch (perkName)
807 | {
808 | case "specialty_armorvest":
809 | case "armorvest":
810 | case "jug":
811 | case "jugg":
812 | case "jugger":
813 | case "juggernaut":
814 | case "juggernaught":
815 | case "juggernog":
816 | realPerkName = "specialty_armorvest";
817 | break;
818 |
819 | case "specialty_quickrevive":
820 | case "quickrevive":
821 | case "revive":
822 | case "qr":
823 | case "rv":
824 | case "rev":
825 | realPerkName = "specialty_quickrevive";
826 | break;
827 |
828 | case "specialty_fastreload":
829 | case "fastreload":
830 | case "reload":
831 | case "quickreload":
832 | case "sleight":
833 | case "sleightofhand":
834 | case "speedcola":
835 | case "cola":
836 | realPerkName = "specialty_fastreload";
837 | break;
838 |
839 | case "specialty_longersprint":
840 | case "longersprint":
841 | case "longsprint":
842 | case "marathon":
843 | case "sprint":
844 | case "fast":
845 | case "stamin":
846 | case "stamin-up":
847 | case "staminup":
848 | case "stamina":
849 | realPerkName = "specialty_longersprint";
850 | break;
851 |
852 | case "specialty_flakjacket":
853 | case "flakjacket":
854 | case "phd":
855 | case "flopper":
856 | case "phdflopper":
857 | realPerkName = "specialty_flakjacket";
858 | break;
859 |
860 | case "specialty_deadshot":
861 | case "deadshot":
862 | case "ds":
863 | case "daiquiri":
864 | realPerkName = "specialty_deadshot";
865 | break;
866 |
867 | case "specialty_additionalprimaryweapon":
868 | case "additionalprimaryweapon":
869 | case "mulekick":
870 | case "mule":
871 | case "kick":
872 | case "mk":
873 | case "additionalweapon":
874 | case "moreweapons":
875 | realPerkName = "specialty_additionalprimaryweapon";
876 | break;
877 |
878 | case "specialty_rof":
879 | case "rof":
880 | case "doubletap":
881 | case "double-tap":
882 | case "dt":
883 | case "rapidfire":
884 | realPerkName = "specialty_rof";
885 | break;
886 |
887 | case "specialty_finalstand":
888 | case "finalstand":
889 | case "whoswho":
890 | case "whowho":
891 | case "fs":
892 | realPerkName = "specialty_finalstand";
893 | break;
894 |
895 | case "specialty_nomotionsensor":
896 | case "nomotionsensor":
897 | case "vultureaid":
898 | case "vulture":
899 | case "va":
900 | case "aid":
901 | case "perkwallhack":
902 | case "wh":
903 | realPerkName = "specialty_nomotionsensor";
904 | break;
905 |
906 | case "specialty_grenadepulldeath":
907 | case "grenadepulldeath":
908 | case "electriccherry":
909 | case "electric":
910 | case "cherry":
911 | case "ec":
912 | case "lighting":
913 | case "reloadstun":
914 | realPerkName = "specialty_grenadepulldeath";
915 | break;
916 |
917 | case "specialty_scavenger":
918 | case "scavenger":
919 | case "tombstone":
920 | case "stone":
921 | case "tomb":
922 | case "tumb":
923 | realPerkName = "specialty_scavenger";
924 | break;
925 | }
926 |
927 | Print("realPerkName: " + realPerkName);
928 |
929 | if (realPerkName != "")
930 | {
931 | foreach (machine in GetEntArray( "zombie_vending", "targetname" ))
932 | {
933 | if (machine.script_noteworthy == realPerkName) // e.g: specialty_armorvest
934 | {
935 | perkInfos["perk_name"] = machine.script_noteworthy; // e.g: specialty_armorvest
936 | perkInfos["bottle_model"] = "zombie_perk_bottle_" + GetSubStr(machine.target, 8, machine.target.size); // e.g: zombie_perk_bottle_jugg
937 | perkInfos["music"] = machine.script_label;
938 | }
939 | }
940 | }
941 |
942 | return perkInfos;
943 | }
--------------------------------------------------------------------------------
/mapvote/mapvote.gsc:
--------------------------------------------------------------------------------
1 | /*
2 | ======================================================================
3 | | Game: Plutonium T6 |
4 | | Description : Let players vote |
5 | | for a map and mode at the end of each game |
6 | | Author: Resxt |
7 | ======================================================================
8 | | https://github.com/Resxt/Plutonium-T6-Scripts/tree/main/mapvote |
9 | ======================================================================
10 | */
11 |
12 | #include maps\mp\gametypes\_hud_util;
13 | #include maps\mp\gametypes_zm\_hud_util;
14 | #include common_scripts\utility;
15 | #include maps\mp\_utility;
16 |
17 | /* Entry point */
18 |
19 | Main()
20 | {
21 | SetDvarIfNotInitialized("mapvote_enable", true);
22 | }
23 |
24 | Init()
25 | {
26 | if (GetDvarInt("mapvote_enable"))
27 | {
28 | level.mapvote_rotate_function = ::StartRotation;
29 |
30 | InitMapvote();
31 | }
32 | }
33 |
34 |
35 |
36 | /* Init section */
37 |
38 | InitMapvote()
39 | {
40 | InitDvars();
41 | InitVariables();
42 |
43 | if (GetDvarInt("mapvote_debug"))
44 | {
45 | Print("[MAPVOTE] Debug mode is ON");
46 | wait 3;
47 | level thread StartVote();
48 | level thread ListenForEndVote();
49 | }
50 | else
51 | {
52 | // Starting the mapvote normally is handled in mp\mapvote_mp_extend.gsc and zm\mapvote_zm_extend.gsc
53 | }
54 | }
55 |
56 | InitDvars()
57 | {
58 | SetDvarIfNotInitialized("mapvote_debug", false);
59 |
60 | if (IsMultiplayerMode())
61 | {
62 | SetDvarIfNotInitialized("mapvote_maps", "Aftermath:Cargo:Carrier:Drone:Express:Hijacked:Meltdown:Overflow:Plaza:Raid:Slums:Standoff:Turbine:Yemen:Nuketown:Downhill:Mirage:Hydro:Grind:Encore:Magma:Vertigo:Studio:Uplink:Detour:Cove:Rush:Dig:Frost:Pod:Takeoff");
63 | SetDvarIfNotInitialized("mapvote_modes", "Team Deathmatch,tdm:Domination,dom:Hardpoint,koth");
64 | SetDvarIfNotInitialized("mapvote_limits_maps", 0);
65 | SetDvarIfNotInitialized("mapvote_limits_modes", 0);
66 | SetDvarIfNotInitialized("mapvote_sounds_menu_enabled", 1);
67 | SetDvarIfNotInitialized("mapvote_sounds_timer_enabled", 1);
68 | SetDvarIfNotInitialized("mapvote_default_rotation_maps", "Hijacked:Raid:Nuketown");
69 | SetDvarIfNotInitialized("mapvote_default_rotation_modes", "tdm");
70 | }
71 | else
72 | {
73 | SetDvarIfNotInitialized("mapvote_maps", "Bus Depot,Bus Depot,zm_standard_transit:Town,Town,zm_standard_town:Farm,Farm,zm_standard_farm:Mob of The Dead,Mob of The Dead,zm_classic_prison:Nuketown,Nuketown,zm_standard_nuked:Origins,Origins,zm_classic_tomb:Buried,Buried,zm_classic_processing:Die Rise,Die Rise,zm_classic_rooftop");
74 | SetDvarIfNotInitialized("mapvote_default_rotation_maps", "Town,zm_standard_town:Farm,zm_standard_farm");
75 | }
76 |
77 | SetDvarIfNotInitialized("mapvote_limits_max", 12);
78 | SetDvarIfNotInitialized("mapvote_colors_selected", "blue");
79 | SetDvarIfNotInitialized("mapvote_colors_unselected", "white");
80 | SetDvarIfNotInitialized("mapvote_colors_timer", "blue");
81 | SetDvarIfNotInitialized("mapvote_colors_timer_low", "red");
82 | SetDvarIfNotInitialized("mapvote_colors_help_text", "white");
83 | SetDvarIfNotInitialized("mapvote_colors_help_accent", "blue");
84 | SetDvarIfNotInitialized("mapvote_colors_help_accent_mode", "standard");
85 | SetDvarIfNotInitialized("mapvote_vote_time", 30);
86 | SetDvarIfNotInitialized("mapvote_blur_level", 2.5);
87 | SetDvarIfNotInitialized("mapvote_blur_fade_in_time", 2);
88 | SetDvarIfNotInitialized("mapvote_horizontal_spacing", 75);
89 | SetDvarIfNotInitialized("mapvote_display_wait_time", 1);
90 | SetDvarIfNotInitialized("mapvote_default_rotation_enable", false);
91 | SetDvarIfNotInitialized("mapvote_default_rotation_min_players", 0);
92 | SetDvarIfNotInitialized("mapvote_default_rotation_max_players", 0);
93 | }
94 |
95 | InitVariables()
96 | {
97 | mapsArray = StrTok(GetDvar("mapvote_maps"), ":");
98 | voteLimits = [];
99 |
100 | if (IsMultiplayerMode())
101 | {
102 | modesArray = StrTok(GetDvar("mapvote_modes"), ":");
103 |
104 | if (GetDvarInt("mapvote_limits_maps") == 0 && GetDvarInt("mapvote_limits_modes") == 0)
105 | {
106 | voteLimits = GetVoteLimits(mapsArray.size, modesArray.size);
107 | }
108 | else if (GetDvarInt("mapvote_limits_maps") > 0 && GetDvarInt("mapvote_limits_modes") == 0)
109 | {
110 | voteLimits = GetVoteLimits(GetDvarInt("mapvote_limits_maps"), modesArray.size);
111 | }
112 | else if (GetDvarInt("mapvote_limits_maps") == 0 && GetDvarInt("mapvote_limits_modes") > 0)
113 | {
114 | voteLimits = GetVoteLimits(mapsArray.size, GetDvarInt("mapvote_limits_modes"));
115 | }
116 | else
117 | {
118 | voteLimits = GetVoteLimits(GetDvarInt("mapvote_limits_maps"), GetDvarInt("mapvote_limits_modes"));
119 | }
120 |
121 | level.mapvote["limit"]["maps"] = voteLimits["maps"];
122 | level.mapvote["limit"]["modes"] = voteLimits["modes"];
123 | }
124 | else
125 | {
126 | if (GetDvarInt("mapvote_limits_maps") == 0)
127 | {
128 | level.mapvote["limit"]["maps"] = GetVoteLimits(mapsArray.size);
129 | }
130 | else
131 | {
132 | level.mapvote["limit"]["maps"] = GetVoteLimits(GetDvarInt("mapvote_limits_maps"));
133 | }
134 | }
135 |
136 | SetMapvoteData("map");
137 |
138 | if (IsMultiplayerMode())
139 | {
140 | SetMapvoteData("mode");
141 | }
142 |
143 | level.mapvote["vote"]["maps"] = [];
144 | level.mapvote["vote"]["modes"] = [];
145 | level.mapvote["hud"]["maps"] = [];
146 | level.mapvote["hud"]["modes"] = [];
147 | }
148 |
149 |
150 |
151 | /* Player section */
152 |
153 | /*
154 | This is used instead of notifyonplayercommand("mapvote_up", "speed_throw")
155 | to fix an issue where players using toggle ads would have to press right click twice for it to register one right click.
156 | With this instead it keeps scrolling every 0.25s until they right click again which is a better user experience
157 | */
158 | ListenForRightClick()
159 | {
160 | self endon("disconnect");
161 |
162 | while (true)
163 | {
164 | if (self AdsButtonPressed())
165 | {
166 | self notify("mapvote_up");
167 | wait 0.25;
168 | }
169 |
170 | wait 0.05;
171 | }
172 | }
173 |
174 | ListenForVoteInputs()
175 | {
176 | self endon("disconnect");
177 |
178 | self thread ListenForRightClick();
179 |
180 | self notifyonplayercommand("mapvote_down", "+attack");
181 | self notifyonplayercommand("mapvote_select", "+gostand");
182 | self notifyonplayercommand("mapvote_unselect", "+usereload");
183 | self notifyonplayercommand("mapvote_unselect", "+activate");
184 |
185 | if (GetDvarInt("mapvote_debug"))
186 | {
187 | self notifyonplayercommand("mapvote_debug", "+melee");
188 | }
189 |
190 | while(true)
191 | {
192 | input = self waittill_any_return("mapvote_down", "mapvote_up", "mapvote_select", "mapvote_unselect", "mapvote_debug");
193 |
194 | section = self.mapvote["vote_section"];
195 |
196 | if (section == "end" && input != "mapvote_unselect" && input != "mapvote_debug")
197 | {
198 | continue; // stop/skip execution
199 | }
200 | else if (section == "mode" && level.mapvote["modes"]["by_index"].size <= 1 && input != "mapvote_unselect" && input != "mapvote_debug")
201 | {
202 | continue; // stop/skip execution
203 | }
204 | else if (section == "mode" && level.mapvote["maps"]["by_index"].size <= 1 && input == "mapvote_unselect")
205 | {
206 | continue; // stop/skip execution
207 | }
208 |
209 | if (input == "mapvote_down")
210 | {
211 | if (self.mapvote[section]["hovered_index"] < (level.mapvote[section + "s"]["by_index"].size - 1))
212 | {
213 | if (GetDvarInt("mapvote_sounds_menu_enabled"))
214 | {
215 | self playlocalsound("uin_start_count_down");
216 | }
217 |
218 | self UpdateSelection(section, (self.mapvote[section]["hovered_index"] + 1));
219 | }
220 | }
221 | else if (input == "mapvote_up")
222 | {
223 | if (self.mapvote[section]["hovered_index"] > 0)
224 | {
225 | if (GetDvarInt("mapvote_sounds_menu_enabled"))
226 | {
227 | self playlocalsound("uin_start_count_down");
228 | }
229 |
230 | self UpdateSelection(section, (self.mapvote[section]["hovered_index"] - 1));
231 | }
232 | }
233 | else if (input == "mapvote_select")
234 | {
235 | if (GetDvarInt("mapvote_sounds_menu_enabled"))
236 | {
237 | self playlocalsound("mpl_killconfirm_tags_pickup");
238 | }
239 |
240 | self ConfirmSelection(section);
241 | }
242 | else if (input == "mapvote_unselect")
243 | {
244 | if (section != "map")
245 | {
246 | if (GetDvarInt("mapvote_sounds_menu_enabled"))
247 | {
248 | self playlocalsound("fly_betty_jump");
249 | }
250 |
251 | self CancelSelection(section);
252 | }
253 | }
254 | else if (input == "mapvote_debug" && GetDvarInt("mapvote_debug"))
255 | {
256 | Print("--------------------------------");
257 |
258 | foreach (player in GetHumanPlayers())
259 | {
260 | if (player.mapvote["map"]["selected_index"] == -1)
261 | {
262 | Print(player.name + " did not vote for any map");
263 | }
264 | else
265 | {
266 | mapName = "";
267 |
268 | if (IsMultiplayerMode())
269 | {
270 | mapName = level.mapvote["maps"]["by_index"][player.mapvote["map"]["selected_index"]];
271 | }
272 | else
273 | {
274 | mapName = level.mapvote["maps"]["by_index"][player.mapvote["map"]["selected_index"]][0];
275 | }
276 |
277 | Print(player.name + " voted for map [" + player.mapvote["map"]["selected_index"] +"] " + mapName);
278 | }
279 |
280 | if (IsMultiplayerMode())
281 | {
282 | if (player.mapvote["mode"]["selected_index"] == -1)
283 | {
284 | Print(player.name + " did not vote for any mode");
285 | }
286 | else
287 | {
288 | Print(player.name + " voted for mode [" + player.mapvote["mode"]["selected_index"] + "] " + level.mapvote["modes"]["by_index"][player.mapvote["mode"]["selected_index"]]);
289 | }
290 | }
291 | }
292 | }
293 |
294 | wait 0.05;
295 | }
296 | }
297 |
298 | OnPlayerDisconnect()
299 | {
300 | self waittill("disconnect");
301 |
302 | if (self.mapvote["map"]["selected_index"] != -1)
303 | {
304 | level.mapvote["vote"]["maps"][self.mapvote["map"]["selected_index"]] = (level.mapvote["vote"]["maps"][self.mapvote["map"]["selected_index"]] - 1);
305 | level.mapvote["hud"]["maps"][self.mapvote["map"]["selected_index"]] SetValue(level.mapvote["vote"]["maps"][self.mapvote["map"]["selected_index"]]);
306 | }
307 |
308 | if (self.mapvote["mode"]["selected_index"] != -1)
309 | {
310 | level.mapvote["vote"]["modes"][self.mapvote["mode"]["selected_index"]] = (level.mapvote["vote"]["modes"][self.mapvote["mode"]["selected_index"]] - 1);
311 | level.mapvote["hud"]["modes"][self.mapvote["mode"]["selected_index"]] SetValue(level.mapvote["vote"]["modes"][self.mapvote["mode"]["selected_index"]]);
312 | }
313 | }
314 |
315 |
316 | /* Vote section */
317 |
318 | CreateVoteMenu()
319 | {
320 | spacing = 20;
321 | hudLastPosY = 0;
322 |
323 | if (IsMultiplayerMode())
324 | {
325 | sectionsSeparation = 0;
326 |
327 | if (level.mapvote["modes"]["by_index"].size > 1 && level.mapvote["maps"]["by_index"].size > 1)
328 | {
329 | sectionsSeparation = 1;
330 | }
331 |
332 | hudLastPosY = -((((level.mapvote["maps"]["by_index"].size + level.mapvote["modes"]["by_index"].size + sectionsSeparation) * spacing) / 2) - (spacing / 2));
333 | }
334 | else
335 | {
336 | hudLastPosY = -(((level.mapvote["maps"]["by_index"].size * spacing) / 2) - (spacing / 2));
337 | }
338 |
339 | if (level.mapvote["maps"]["by_index"].size > 1)
340 | {
341 | for (mapIndex = 0; mapIndex < level.mapvote["maps"]["by_index"].size; mapIndex++)
342 | {
343 | mapVotesHud = CreateHudText("", "objective", 1.5, "LEFT", "CENTER", GetDvarInt("mapvote_horizontal_spacing"), hudLastPosY, true, 0);
344 | mapVotesHud.color = GetGscColor(GetDvar("mapvote_colors_selected"));
345 |
346 | level.mapvote["hud"]["maps"][mapIndex] = mapVotesHud;
347 |
348 | foreach (player in GetHumanPlayers())
349 | {
350 | mapName = "";
351 |
352 | if (IsMultiplayerMode())
353 | {
354 | mapName = level.mapvote["maps"]["by_index"][mapIndex];
355 | }
356 | else
357 | {
358 | mapName = level.mapvote["maps"]["by_index"][mapIndex][0];
359 | }
360 |
361 | player.mapvote["map"][mapIndex]["hud"] = player CreateHudText(mapName, "objective", 1.5, "LEFT", "CENTER", -(GetDvarInt("mapvote_horizontal_spacing")), hudLastPosY);
362 |
363 | if (mapIndex == 0)
364 | {
365 | player UpdateSelection("map", 0);
366 | }
367 | else
368 | {
369 | SetElementUnselected(player.mapvote["map"][mapIndex]["hud"]);
370 | }
371 | }
372 |
373 | hudLastPosY += spacing;
374 | }
375 | }
376 |
377 | if (IsMultiplayerMode() && level.mapvote["modes"]["by_index"].size > 1)
378 | {
379 | hudLastPosY += spacing; // Space between maps and modes sections
380 |
381 | for (modeIndex = 0; modeIndex < level.mapvote["modes"]["by_index"].size; modeIndex++)
382 | {
383 | modeVotesHud = CreateHudText("", "objective", 1.5, "LEFT", "CENTER", GetDvarInt("mapvote_horizontal_spacing"), hudLastPosY, true, 0);
384 | modeVotesHud.color = GetGscColor(GetDvar("mapvote_colors_selected"));
385 |
386 | level.mapvote["hud"]["modes"][modeIndex] = modeVotesHud;
387 |
388 | foreach (player in GetHumanPlayers())
389 | {
390 | player.mapvote["mode"][modeIndex]["hud"] = player CreateHudText(level.mapvote["modes"]["by_index"][modeIndex], "objective", 1.5, "LEFT", "CENTER", -(GetDvarInt("mapvote_horizontal_spacing")), hudLastPosY);
391 |
392 | SetElementUnselected(player.mapvote["mode"][modeIndex]["hud"]);
393 | }
394 |
395 | hudLastPosY += spacing;
396 | }
397 |
398 | if (level.mapvote["maps"]["by_index"].size <= 1)
399 | {
400 | player UpdateSelection("mode", 0);
401 | }
402 | }
403 |
404 | foreach(player in GetHumanPlayers())
405 | {
406 | player.mapvote["map"]["selected_index"] = -1;
407 | player.mapvote["mode"]["selected_index"] = -1;
408 |
409 | buttonsHelpMessage = "";
410 |
411 | if (GetDvar("mapvote_colors_help_accent_mode") == "standard")
412 | {
413 | buttonsHelpMessage = GetChatColor(GetDvar("mapvote_colors_help_text")) + "Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+attack}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to go down - Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+speed_throw}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to go up - Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+gostand}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to select - Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+activate}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to undo";
414 | }
415 | else if(GetDvar("mapvote_colors_help_accent_mode") == "max")
416 | {
417 | buttonsHelpMessage = GetChatColor(GetDvar("mapvote_colors_help_text")) + "Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+attack}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to go " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "down " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "- Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+speed_throw}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to go " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "up " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "- Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+gostand}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "select " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "- Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+activate}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "undo";
418 | }
419 |
420 | if (GetDvarInt("mapvote_debug"))
421 | {
422 | if (GetDvar("mapvote_colors_help_accent_mode") == "standard")
423 | {
424 | buttonsHelpMessage = buttonsHelpMessage + " - Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+melee}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to debug";
425 | }
426 | else if(GetDvar("mapvote_colors_help_accent_mode") == "max")
427 | {
428 | buttonsHelpMessage = buttonsHelpMessage + GetChatColor(GetDvar("mapvote_colors_help_text")) + " - Press " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "[{+melee}] " + GetChatColor(GetDvar("mapvote_colors_help_text")) + "to " + GetChatColor(GetDvar("mapvote_colors_help_accent")) + "debug";
429 | }
430 | }
431 |
432 | player CreateHudText(buttonsHelpMessage, "objective", 1.5, "CENTER", "CENTER", 0, 210);
433 | }
434 | }
435 |
436 | CreateVoteTimer()
437 | {
438 | soundFX = spawn("script_origin", (0,0,0));
439 | soundFX hide();
440 |
441 | timerhud = CreateTimer(GetDvarInt("mapvote_vote_time"), &"Vote ends in: ", "objective", 1.5, "CENTER", "CENTER", 0, -210);
442 | timerhud.color = GetGscColor(GetDvar("mapvote_colors_timer"));
443 | for (i = GetDvarInt("mapvote_vote_time"); i > 0; i--)
444 | {
445 | if(i <= 5)
446 | {
447 | timerhud.color = GetGscColor(GetDvar("mapvote_colors_timer_low"));
448 |
449 | if (GetDvarInt("mapvote_sounds_timer_enabled"))
450 | {
451 | soundFX playSound( "mpl_ui_timer_countdown" );
452 | }
453 | }
454 | wait(1);
455 | }
456 | level notify("mapvote_vote_end");
457 | }
458 |
459 | StartVote()
460 | {
461 | level endon("end_game");
462 |
463 | for (i = 0; i < level.mapvote["maps"]["by_index"].size; i++)
464 | {
465 | level.mapvote["vote"]["maps"][i] = 0;
466 | }
467 |
468 | if (IsMultiplayerMode())
469 | {
470 | for (i = 0; i < level.mapvote["modes"]["by_index"].size; i++)
471 | {
472 | level.mapvote["vote"]["modes"][i] = 0;
473 | }
474 | }
475 |
476 | level thread CreateVoteMenu();
477 | level thread CreateVoteTimer();
478 |
479 | foreach (player in GetHumanPlayers())
480 | {
481 | player FreezeControlsAllowLook(1);
482 | player SetBlur(GetDvarInt("mapvote_blur_level"), GetDvarInt("mapvote_blur_fade_in_time"));
483 |
484 | player thread ListenForVoteInputs();
485 | player thread OnPlayerDisconnect();
486 | }
487 | }
488 |
489 | ListenForEndVote()
490 | {
491 | level endon("end_game");
492 | level waittill("mapvote_vote_end");
493 |
494 | mostVotedMapIndex = 0;
495 | mostVotedMapVotes = 0;
496 | mostVotedModeIndex = 0;
497 | mostVotedModeVotes = 0;
498 |
499 | foreach (mapIndex in GetArrayKeys(level.mapvote["vote"]["maps"]))
500 | {
501 | if (level.mapvote["vote"]["maps"][mapIndex] > mostVotedMapVotes)
502 | {
503 | mostVotedMapIndex = mapIndex;
504 | mostVotedMapVotes = level.mapvote["vote"]["maps"][mapIndex];
505 | }
506 | }
507 |
508 | foreach (modeIndex in GetArrayKeys(level.mapvote["vote"]["modes"]))
509 | {
510 | if (level.mapvote["vote"]["modes"][modeIndex] > mostVotedModeVotes)
511 | {
512 | mostVotedModeIndex = modeIndex;
513 | mostVotedModeVotes = level.mapvote["vote"]["modes"][modeIndex];
514 | }
515 | }
516 |
517 | if (mostVotedMapVotes == 0)
518 | {
519 | mostVotedMapIndex = GetRandomElementInArray(GetArrayKeys(level.mapvote["vote"]["maps"]));
520 |
521 | if (GetDvarInt("mapvote_debug"))
522 | {
523 | Print("[MAPVOTE] No vote for map. Chosen random map index: " + mostVotedMapIndex);
524 | }
525 | }
526 | else
527 | {
528 | if (GetDvarInt("mapvote_debug"))
529 | {
530 | Print("[MAPVOTE] Most voted map has " + mostVotedMapVotes + " votes. Most voted map index: " + mostVotedMapIndex);
531 | }
532 | }
533 |
534 | if (mostVotedModeVotes == 0)
535 | {
536 | mostVotedModeIndex = GetRandomElementInArray(GetArrayKeys(level.mapvote["vote"]["modes"]));
537 |
538 | if (GetDvarInt("mapvote_debug"))
539 | {
540 | Print("[MAPVOTE] No vote for mode. Chosen random mode index: " + mostVotedModeIndex);
541 | }
542 | }
543 | else
544 | {
545 | if (GetDvarInt("mapvote_debug"))
546 | {
547 | Print("[MAPVOTE] Most voted mode has " + mostVotedModeVotes + " votes. Most voted mode index: " + mostVotedModeIndex);
548 | }
549 | }
550 |
551 | modeName = "";
552 | modeCfg = "";
553 | mapName = "";
554 |
555 | if (IsMultiplayerMode())
556 | {
557 | modeName = level.mapvote["modes"]["by_index"][mostVotedModeIndex];
558 | modeCfg = level.mapvote["modes"]["by_name"][level.mapvote["modes"]["by_index"][mostVotedModeIndex]];
559 | mapName = GetMapCodeName(level.mapvote["maps"]["by_index"][mostVotedMapIndex]);
560 | }
561 | else
562 | {
563 | modeCfg = level.mapvote["maps"]["by_index"][mostVotedMapIndex][2];
564 | mapName = GetMapCodeName(level.mapvote["maps"]["by_index"][mostVotedMapIndex][1]);
565 | }
566 |
567 | if (GetDvarInt("mapvote_debug"))
568 | {
569 | Print("[MAPVOTE] mapName: " + mapName);
570 | Print("[MAPVOTE] modeName: " + modeName);
571 | Print("[MAPVOTE] modeCfg: " + modeCfg);
572 | Print("[MAPVOTE] Rotating to " + mapName + " | " + modeName + " (" + modeCfg + ".cfg)");
573 | }
574 |
575 | DoRotation(modeCfg, mapName);
576 | }
577 |
578 | SetMapvoteData(type)
579 | {
580 | limit = level.mapvote["limit"][type + "s"];
581 |
582 | availableElements = StrTok(GetDvar("mapvote_" + type + "s"), ":");
583 |
584 | if (availableElements.size < limit)
585 | {
586 | limit = availableElements.size;
587 | }
588 |
589 | if (type == "map")
590 | {
591 | if (IsMultiplayerMode())
592 | {
593 | level.mapvote["maps"]["by_index"] = GetRandomUniqueElementsInArray(availableElements, limit);
594 | }
595 | else
596 | {
597 | zombiesArrays = GetRandomUniqueElementsInArray(availableElements, limit);
598 |
599 | level.mapvote["maps"]["by_index"] = [];
600 |
601 | foreach (element in zombiesArrays)
602 | {
603 | splittedElement = StrTok(element, ",");
604 | level.mapvote["maps"]["by_index"] = AddElementToArray(level.mapvote["maps"]["by_index"], array(splittedElement[0], splittedElement[1], splittedElement[2]));
605 | }
606 | }
607 | }
608 | else if (type == "mode")
609 | {
610 | finalElements = [];
611 |
612 | foreach (mode in GetRandomUniqueElementsInArray(availableElements, limit))
613 | {
614 | splittedMode = StrTok(mode, ",");
615 | finalElements = AddElementToArray(finalElements, splittedMode[0]);
616 |
617 | level.mapvote["modes"]["by_name"][splittedMode[0]] = splittedMode[1];
618 | }
619 |
620 | level.mapvote["modes"]["by_index"] = finalElements;
621 | }
622 | }
623 |
624 | /*
625 | Gets the amount of maps and modes to display on screen
626 | This is used to get default values if the limits dvars are not set
627 | It will dynamically adjust the amount of maps and modes to show
628 | */
629 | GetVoteLimits(mapsAmount, modesAmount)
630 | {
631 | maxLimit = GetDvarInt("mapvote_limits_max");
632 | limits = [];
633 |
634 | if (!IsDefined(modesAmount))
635 | {
636 | if (mapsAmount <= maxLimit)
637 | {
638 | return mapsAmount;
639 | }
640 | else
641 | {
642 | return maxLimit;
643 | }
644 | }
645 |
646 | if ((mapsAmount + modesAmount) <= maxLimit)
647 | {
648 | limits["maps"] = mapsAmount;
649 | limits["modes"] = modesAmount;
650 | }
651 | else
652 | {
653 | if (mapsAmount >= (maxLimit / 2) && modesAmount >= (maxLimit))
654 | {
655 | limits["maps"] = (maxLimit / 2);
656 | limits["modes"] = (maxLimit / 2);
657 | }
658 | else
659 | {
660 | if (mapsAmount > (maxLimit / 2))
661 | {
662 | finalMapsAmount = 0;
663 |
664 | if (modesAmount <= 1)
665 | {
666 | limits["maps"] = maxLimit;
667 | }
668 | else
669 | {
670 | limits["maps"] = (maxLimit - modesAmount);
671 | }
672 |
673 | limits["modes"] = modesAmount;
674 | }
675 | else if (modesAmount > (maxLimit / 2))
676 | {
677 | limits["maps"] = mapsAmount;
678 | limits["modes"] = (maxLimit - mapsAmount);
679 | }
680 | }
681 | }
682 |
683 | return limits;
684 | }
685 |
686 | RotateDefault()
687 | {
688 | mapName = "";
689 | modeCfg = "";
690 |
691 | if (IsMultiplayerMode())
692 | {
693 | modeCfg = GetRandomElementInArray(StrTok(GetDvar("mapvote_default_rotation_modes"), ":"));
694 | mapName = GetMapCodeName(GetRandomElementInArray(StrTok(GetDvar("mapvote_default_rotation_maps"), ":")));
695 | }
696 | else
697 | {
698 | data = GetRandomElementInArray(StrTok(GetDvar("mapvote_default_rotation_maps"), ":"));
699 | dataSplitted = StrTok(data, ",");
700 |
701 | modeCfg = dataSplitted[1];
702 | mapName = GetMapCodeName(dataSplitted[0]);
703 | }
704 |
705 | DoRotation(modeCfg, mapName);
706 | }
707 |
708 | DoRotation(modeCfg, mapName)
709 | {
710 | SetDvar("sv_maprotationcurrent", "execgts " + modeCfg + ".cfg map " + mapName);
711 | SetDvar("sv_maprotation", "execgts " + modeCfg + ".cfg map " + mapName);
712 | }
713 |
714 | StartRotation()
715 | {
716 | humanPlayersCount = GetHumanPlayers().size;
717 |
718 | if (GetDvarInt("mapvote_default_rotation_enable") && humanPlayersCount >= GetDvarInt("mapvote_default_rotation_min_players") && humanPlayersCount <= GetDvarInt("mapvote_default_rotation_max_players"))
719 | {
720 | RotateDefault();
721 | }
722 | else
723 | {
724 | StartVote();
725 | ListenForEndVote();
726 | }
727 | }
728 |
729 |
730 |
731 | /* HUD section */
732 |
733 | UpdateSelection(type, index)
734 | {
735 | if (type == "map" || type == "mode")
736 | {
737 | if (!IsDefined(self.mapvote[type]["hovered_index"]))
738 | {
739 | self.mapvote[type]["hovered_index"] = 0;
740 | }
741 |
742 | self.mapvote["vote_section"] = type;
743 |
744 | SetElementUnselected(self.mapvote[type][self.mapvote[type]["hovered_index"]]["hud"]); // Unselect previous element
745 | SetElementSelected(self.mapvote[type][index]["hud"]); // Select new element
746 |
747 | self.mapvote[type]["hovered_index"] = index; // Update the index
748 | }
749 | else if (type == "end")
750 | {
751 | self.mapvote["vote_section"] = "end";
752 | }
753 | }
754 |
755 | ConfirmSelection(type)
756 | {
757 | self.mapvote[type]["selected_index"] = self.mapvote[type]["hovered_index"];
758 | level.mapvote["vote"][type + "s"][self.mapvote[type]["selected_index"]] = (level.mapvote["vote"][type + "s"][self.mapvote[type]["selected_index"]] + 1);
759 | level.mapvote["hud"][type + "s"][self.mapvote[type]["selected_index"]] SetValue(level.mapvote["vote"][type + "s"][self.mapvote[type]["selected_index"]]);
760 |
761 | if (type == "map")
762 | {
763 | modeIndex = 0;
764 |
765 | if (IsDefined(self.mapvote["mode"]["hovered_index"]))
766 | {
767 | modeIndex = self.mapvote["mode"]["hovered_index"];
768 | }
769 |
770 | self UpdateSelection("mode", modeIndex);
771 | }
772 | else if (type == "mode")
773 | {
774 | self UpdateSelection("end");
775 | }
776 | }
777 |
778 | CancelSelection(type)
779 | {
780 | typeToCancel = "";
781 |
782 | if (type == "mode")
783 | {
784 | typeToCancel = "map";
785 | }
786 | else if (type == "end")
787 | {
788 | typeToCancel = "mode";
789 | }
790 |
791 | level.mapvote["vote"][typeToCancel + "s"][self.mapvote[typeToCancel]["selected_index"]] = (level.mapvote["vote"][typeToCancel + "s"][self.mapvote[typeToCancel]["selected_index"]] - 1);
792 | level.mapvote["hud"][typeToCancel + "s"][self.mapvote[typeToCancel]["selected_index"]] SetValue(level.mapvote["vote"][typeToCancel + "s"][self.mapvote[typeToCancel]["selected_index"]]);
793 |
794 | self.mapvote[typeToCancel]["selected_index"] = -1;
795 |
796 | if (type == "mode")
797 | {
798 | SetElementUnselected(self.mapvote["mode"][self.mapvote["mode"]["hovered_index"]]["hud"]);
799 | self.mapvote["vote_section"] = "map";
800 | }
801 | else if (type == "end")
802 | {
803 | self.mapvote["vote_section"] = "mode";
804 | }
805 | }
806 |
807 | SetElementSelected(element)
808 | {
809 | element.color = GetGscColor(GetDvar("mapvote_colors_selected"));
810 | }
811 |
812 | SetElementUnselected(element)
813 | {
814 | element.color = GetGscColor(GetDvar("mapvote_colors_unselected"));
815 | }
816 |
817 | CreateHudText(text, font, fontScale, relativeToX, relativeToY, relativeX, relativeY, isServer, value)
818 | {
819 | hudText = "";
820 |
821 | if (IsDefined(isServer) && isServer)
822 | {
823 | hudText = CreateServerFontString( font, fontScale );
824 | }
825 | else
826 | {
827 | hudText = CreateFontString( font, fontScale );
828 | }
829 |
830 | if (IsDefined(value))
831 | {
832 | hudText.label = text;
833 | hudText SetValue(value);
834 | }
835 | else
836 | {
837 | hudText SetText(text);
838 | }
839 |
840 | hudText SetPoint(relativeToX, relativeToY, relativeX, relativeY);
841 |
842 | hudText.hideWhenInMenu = 1;
843 | hudText.glowAlpha = 0;
844 |
845 | return hudText;
846 | }
847 |
848 | CreateTimer(time, label, font, fontScale, relativeToX, relativeToY, relativeX, relativeY)
849 | {
850 | timer = createServerTimer(font, fontScale);
851 | timer setpoint(relativeToX, relativeToY, relativeX, relativeY);
852 | timer.label = label;
853 | timer.hideWhenInMenu = 1;
854 | timer.glowAlpha = 0;
855 | timer setTimer(time);
856 |
857 | return timer;
858 | }
859 |
860 |
861 |
862 | /* Utils section */
863 |
864 | SetDvarIfNotInitialized(dvar, value)
865 | {
866 | if (!IsInitialized(dvar))
867 | {
868 | SetDvar(dvar, value);
869 | }
870 | }
871 |
872 | IsInitialized(dvar)
873 | {
874 | result = GetDvar(dvar);
875 | return result != "";
876 | }
877 |
878 | IsBot()
879 | {
880 | return IsDefined(self.pers["isBot"]) && self.pers["isBot"];
881 | }
882 |
883 | IsMultiplayerMode()
884 | {
885 | return !IsDefined(level.zombiemode) || !level.zombiemode;
886 | }
887 |
888 | GetHumanPlayers()
889 | {
890 | humanPlayers = [];
891 |
892 | foreach (player in level.players)
893 | {
894 | if (!player IsBot())
895 | {
896 | humanPlayers = AddElementToArray(humanPlayers, player);
897 | }
898 | }
899 |
900 | return humanPlayers;
901 | }
902 |
903 | GetRandomElementInArray(array)
904 | {
905 | return array[GetArrayKeys(array)[randomint(array.size)]];
906 | }
907 |
908 | GetRandomUniqueElementsInArray(array, limit)
909 | {
910 | finalElements = [];
911 |
912 | for (i = 0; i < limit; i++)
913 | {
914 | findElement = true;
915 |
916 | while (findElement)
917 | {
918 | randomElement = GetRandomElementInArray(array);
919 |
920 | if (!ArrayContainsValue(finalElements, randomElement))
921 | {
922 | finalElements = AddElementToArray(finalElements, randomElement);
923 |
924 | findElement = false;
925 | }
926 | }
927 | }
928 |
929 | return finalElements;
930 | }
931 |
932 | ArrayContainsValue(array, valueToFind)
933 | {
934 | if (array.size == 0)
935 | {
936 | return false;
937 | }
938 |
939 | foreach (value in array)
940 | {
941 | if (value == valueToFind)
942 | {
943 | return true;
944 | }
945 | }
946 |
947 | return false;
948 | }
949 |
950 | AddElementToArray(array, element)
951 | {
952 | array[array.size] = element;
953 | return array;
954 | }
955 |
956 | GetMapCodeName(mapName)
957 | {
958 | formattedMapName = ToUpper(mapName);
959 |
960 | if (IsMultiplayerMode())
961 | {
962 | switch(formattedMapName)
963 | {
964 | case "NUKETOWN":
965 | return "mp_nuketown_2020";
966 |
967 | case "HIJACKED":
968 | return "mp_hijacked";
969 |
970 | case "MELTDOWN":
971 | return "mp_meltdown";
972 |
973 | case "EXPRESS":
974 | return "mp_express";
975 |
976 | case "CARRIER":
977 | return "mp_carrier";
978 |
979 | case "OVERFLOW":
980 | return "mp_overflow";
981 |
982 | case "SLUMS":
983 | return "mp_slums";
984 |
985 | case "AFTERMATH":
986 | return "mp_la";
987 |
988 | case "CARGO":
989 | return "mp_dockside";
990 |
991 | case "TURBINE":
992 | return "mp_turbine";
993 |
994 | case "DRONE":
995 | return "mp_drone";
996 |
997 | case "RAID":
998 | return "mp_raid";
999 |
1000 | case "STANDOFF":
1001 | return "mp_village";
1002 |
1003 | case "PLAZA":
1004 | return "mp_nightclub";
1005 |
1006 | case "YEMEN":
1007 | return "mp_socotra";
1008 |
1009 | case "UPLINK":
1010 | return "mp_uplink";
1011 |
1012 | case "DETOUR":
1013 | return "mp_bridge";
1014 |
1015 | case "COVE":
1016 | return "mp_castaway";
1017 |
1018 | case "RUSH":
1019 | return "mp_paintball";
1020 |
1021 | case "STUDIO":
1022 | return "mp_studio";
1023 |
1024 | case "MAGMA":
1025 | return "mp_magma";
1026 |
1027 | case "VERTIGO":
1028 | return "mp_vertigo";
1029 |
1030 | case "ENCORE":
1031 | return "mp_concert";
1032 |
1033 | case "DOWNHILL":
1034 | return "mp_downhill";
1035 |
1036 | case "GRIND":
1037 | return "mp_skate";
1038 |
1039 | case "HYDRO":
1040 | return "mp_hydro";
1041 |
1042 | case "MIRAGE":
1043 | return "mp_mirage";
1044 |
1045 | case "FROST":
1046 | return "mp_frostbite";
1047 |
1048 | case "TAKEOFF":
1049 | return "mp_takeoff";
1050 |
1051 | case "POD":
1052 | return "mp_pod";
1053 |
1054 | case "DIG":
1055 | return "mp_dig";
1056 | }
1057 | }
1058 | else
1059 | {
1060 | switch(formattedMapName)
1061 | {
1062 | case "BURIED":
1063 | return "zm_buried";
1064 |
1065 | case "DIE RISE":
1066 | return "zm_highrise";
1067 |
1068 | case "MOB OF THE DEAD":
1069 | return "zm_prison";
1070 |
1071 | case "NUKETOWN":
1072 | return "zm_nuked";
1073 |
1074 | case "ORIGINS":
1075 | return "zm_tomb";
1076 |
1077 | case "TRANZIT":
1078 | case "FARM":
1079 | case "TOWN":
1080 | case "BUS DEPOT":
1081 | return "zm_transit";
1082 |
1083 | case "DINER":
1084 | return "zm_transit_dr";
1085 | }
1086 | }
1087 | }
1088 |
1089 | GetGscColor(colorName)
1090 | {
1091 | switch (colorName)
1092 | {
1093 | case "red":
1094 | return (1, 0, 0.059);
1095 |
1096 | case "green":
1097 | return (0.549, 0.882, 0.043);
1098 |
1099 | case "yellow":
1100 | return (1, 0.725, 0);
1101 |
1102 | case "blue":
1103 | return (0, 0.553, 0.973);
1104 |
1105 | case "cyan":
1106 | return (0, 0.847, 0.922);
1107 |
1108 | case "purple":
1109 | return (0.427, 0.263, 0.651);
1110 |
1111 | case "white":
1112 | return (1, 1, 1);
1113 |
1114 | case "grey":
1115 | case "gray":
1116 | return (0.137, 0.137, 0.137);
1117 |
1118 | case "black":
1119 | return (0, 0, 0);
1120 | }
1121 | }
1122 |
1123 | GetChatColor(colorName)
1124 | {
1125 | switch(colorName)
1126 | {
1127 | case "red":
1128 | return "^1";
1129 |
1130 | case "green":
1131 | return "^2";
1132 |
1133 | case "yellow":
1134 | return "^3";
1135 |
1136 | case "blue":
1137 | return "^4";
1138 |
1139 | case "cyan":
1140 | return "^5";
1141 |
1142 | case "purple":
1143 | return "^6";
1144 |
1145 | case "white":
1146 | return "^7";
1147 |
1148 | case "grey":
1149 | return "^0";
1150 |
1151 | case "black":
1152 | return "^0";
1153 | }
1154 | }
1155 |
--------------------------------------------------------------------------------