Merge branch 'main' into hud_gameover_nodraw

This commit is contained in:
Peter0x44 2025-01-01 01:52:54 +00:00 committed by GitHub
commit b862913ea5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 574 additions and 71 deletions

View file

@ -20,6 +20,7 @@ defs/standard.qc
#endif
shared_defs.qc
defs/custom.qc
map_rotation.qc
utilities/sound_helper.qc
weapon_stats.qc
utilities/math.qc
@ -28,6 +29,8 @@ hash_table.qc
dummies.qc
rounds.qc
main.qc
plugins/plugin_mapvote.qc
plugins/plugin_core.qc
utilities/weapon_utilities.qc
utilities/game_restart.qc
utilities/command_parser.qc

View file

@ -73,6 +73,7 @@ void() Chase_Update =
// take contents of the view leaf
viewcontents = pointcontents(cl_vieworigin);
float attempts = 0;
for (best = 0; best < NUM_TESTS; best++) {
vector chase_newdest;
@ -85,8 +86,9 @@ void() Chase_Update =
{
// go back to the previous best as this one is bad
// unless the first one was also bad, (viewleaf contents != viewleaf contents!!!)
if (best > 0) {
if (best > 0 && attempts < 5) {
best--;
attempts++;
} else {
best = NUM_TESTS;
break;
@ -139,6 +141,7 @@ void() Chase_Update =
if (nummatches == 6) break;
}
// find the spot the player is looking at
dest[0] = cl_vieworigin[0] + 4096 * v_forward[0];
dest[1] = cl_vieworigin[1] + 4096 * v_forward[1];

View file

@ -98,6 +98,7 @@ string Chat_GetHexCodeForPlayer(float player_id)
case 2: return "^x07E";
case 3: return "^xCA0";
case 4: return "^x5D0";
case 255: return "^xF76";
}
return "^xFFF";
}
@ -112,7 +113,14 @@ void Chat_Register(int sender, int player_id, string message)
// Append name+color to the message
string player_hex = Chat_GetHexCodeForPlayer(player_id);
string player_name = getplayerkeyvalue(sender, "name");
message = strcat(player_hex, player_name, "^xCCC: ", message);
// Not a server message.
if (player_id != 255) {
message = strcat(player_hex, player_name, "^xCCC: ", message);
} else {
message = strcat(player_hex, message);
}
if (g_chatlines < (CHAT_LINES - 1)) {
g_chatbuffer[g_chatlines + 1] = message;

View file

@ -1483,9 +1483,14 @@ void() HUD_Powerups =
void() HUD_Broadcast =
{
// broadcast_num refers to the playernum of the client. This
// does NOT correspond to player key indexes, so we need to
// grab the player with the matching playernum and use infokeys.
entity player_target = findfloat(world, playernum, broadcast_num);
string broadcast_name = infokey(player_target, INFOKEY_P_NAME);
string broadcast_msg = "";
string broadcast_name = getplayerkeyvalue(broadcast_num - 1, "name");
switch(broadcast_type) {
case CSQC_BROADCAST_PLAYERDOWNED:
broadcast_msg = strcat(broadcast_name, " needs to be revived");

View file

@ -48,18 +48,6 @@ void() ToggleMenu =
}
}
//
// SetGamepadBindings()
// Since our menu architecture currently sucks,
// we don't yet support a good binds menu that
// let's you get good gamepad control.. so
// force these binds (sorry!)
//
void() SetGamepadBindings =
{
localcmd("unbindall; bind ~ \"toggleconsole\"; bind ` \"toggleconsole\"; bind ESCAPE \"togglemenu\"; joyadvaxisr 4; joyadvaxisu 2; joyadvaxisx 3; joyadvaxisy -1; bind JOY1 \"+button5\"; bindlevel JOY2 30 \"null\"; bind JOY3 \"+button4\"; bind GP_LSHOULDER \"impulse 33\"; bind AUX1 \"+moveup\"; bind AUX2 \"+button3\"; bindlevel AUX3 30 \"null\"; bindlevel AUX4 30 \"null\"; bind AUX5 \"togglemenu\"; bind AUX10 \"+attack\"; bind GP_Y \"+button4\"; bindlevel GP_A 30 \"impulse 10\"; bindlevel GP_B 30 \"impulse 30\"; bindlevel GP_X 30 \"+button5\"; bindlevel GP_LTHUMB 30 \"impulse 23\"; bindlevel GP_RTHUMB 30 \"+button6\"; bind GP_LTRIGGER \"+button8\"; bindlevel GP_DPAD_DOWN 30 \"+button7\"; bindlevel GP_RSHOULDER 30 \"+button3\"; bindlevel GP_DPAD_RIGHT 30 \"impulse 33\"; bindlevel GP_DPAD_UP 30 \"+button7\"; bind GP_VIEW \"showscores\"; joysidesensitivity 0.8; joyyawsensitivity 0.6; joypitchsensitivity 0.55\n");
}
float(float isnew) SetZombieSkinning =
{
self.drawmask = MASK_ENGINE;
@ -904,9 +892,15 @@ noref float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent
float last_input_storage = last_input_was_gamepad;
if (last_input_deviceid > 0)
{
last_input_was_gamepad = TRUE;
setlocaluserinfo(0, "using_gamepad", "1");
}
else
{
last_input_was_gamepad = FALSE;
setlocaluserinfo(0, "using_gamepad", "0");
}
if (current_menu != MENU_NONE)
{

View file

@ -623,6 +623,21 @@ static string(string s) str2ascii =
return s;
};
//
// Menu_CleanUpServerAddress(server_address)
// Strips stuff from the CNAME like /udp/ etc.
// if it causes conflicts when trying to connect.
//
string Menu_CleanUpServerAddress(string server_address) =
{
// Strip /udp/
if (substring(server_address, 0, 5) == "/udp/") {
return substring(server_address, 5, strlen(server_address) - 5);
}
return server_address;
};
//
// Menu_ServerList(id, pos, size, scrollofs, num_servers)
// Draw the master server list
@ -663,7 +678,9 @@ void(string id, vector pos, vector size, __inout vector scrollofs, float num_ser
Menu_PlaySound(MENU_SND_ENTER);
m_toggle(false);
map_name_override = gethostcachestring(gethostcacheindexforkey("map"), index);
localcmd("connect ", gethostcachestring(gethostcacheindexforkey("cname"), index), "\n");
string server_address = Menu_CleanUpServerAddress(gethostcachestring(gethostcacheindexforkey("cname"), index));
localcmd("connect ", server_address, "\n");
}
#endif // MENU

View file

@ -21,7 +21,7 @@ string(string prev_id) Menu_Pause_GetNextButton =
}
}
if (player_count != 0 && ret == "pm_reloa")
if (player_count != 1 && ret == "pm_reloa")
ret = "pm_opts";
if (ret == "")
@ -47,7 +47,7 @@ string(string next_id) Menu_Pause_GetPreviousButton =
}
}
if (player_count != 0 && ret == "pm_reloa")
if (player_count != 1 && ret == "pm_reloa")
ret = "pm_resum";
if (ret == "")
@ -86,7 +86,7 @@ void() Menu_Pause =
{
Menu_Button(1, "pm_resum", "RESUME CARNAGE", "Return to Game.") ? ToggleMenu() : 0;
if (player_count == 0)
if (player_count == 1)
Menu_Button(2, "pm_reloa", "RESTART LEVEL", "Tough luck? Give things another go.") ? Menu_Pause_EnterSubMenu(1) : 0;
else
Menu_GreyButton(2, "RESTART LEVEL");
@ -122,4 +122,4 @@ void() Menu_Pause =
}
sui_pop_frame();
};
};

View file

@ -134,7 +134,7 @@ void() Menu_Video_UpdateShowFPS =
Menu_PlaySound(MENU_SND_ENTER);
current_showfps++;
if (current_showfps > 2) current_showfps = 0;
localcmd(sprintf("show_fps %d", current_showfps));
localcmd(sprintf("show_fps %d\n", current_showfps));
};
void() Menu_Video_UpdateVsync =

View file

@ -513,14 +513,26 @@ void (float achievement_id, optional entity who) GiveAchievement =
#ifdef FTE
void CSQC_SendChatMessage(float player_id, string message) {
void CSQC_SendChatMessage(float player_id, string message) =
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_CHATMESSAGE);
WriteByte(MSG_MULTICAST, num_for_edict(self) - 1);
WriteByte(MSG_MULTICAST, player_id);
WriteString(MSG_MULTICAST, message);
multicast('0 0 0', MULTICAST_ALL);
}
};
void CSQC_SendChatMessageToPlayer(float player_id, string message, entity who) =
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_CHATMESSAGE);
WriteByte(MSG_MULTICAST, num_for_edict(self) - 1);
WriteByte(MSG_MULTICAST, player_id);
WriteString(MSG_MULTICAST, message);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
};
/*
=================

View file

@ -40,19 +40,18 @@ void(entity client) LastStand_Begin;
void() Barrel_Hit;
void() teddy_react;
void() EndGame_Restart =
{
localcmd("restart");
}
// Fade to black function, creates another think for restart
void() EndGame_FadePrompt =
{
nzp_screenflash(self, SCREENFLASH_COLOR_BLACK, 6, SCREENFLASH_FADE_IN);
entity players = find(world, classname, "player");
while(players != world) {
nzp_screenflash(players, SCREENFLASH_COLOR_BLACK, 5, SCREENFLASH_FADE_IN);
players = find(players, classname, "player");
}
#ifdef FTE
self.think = EndGame_Restart;
self.think = MapRotation_Decide;
#else
@ -66,22 +65,16 @@ void() EndGame_FadePrompt =
//Actual endgame function, all zombies die, music plays
void() EndGame =
{
local entity oldself;
local entity who;
self.health = 0;
self.velocity = '0 0 0';
oldself = self;
who = find(world,classname,"ai_zombie");
entity old_self;
entity who = find(world,classname,"ai_zombie");
while(who != world)
{
if(who.health)
{
old_self = self;
self = who;
self.th_die();
self = oldself;
self = old_self;
}
who = find(who,classname,"ai_zombie");
@ -135,17 +128,29 @@ float() PollPlayersAlive =
// Endgamesetup -- think function for setting up the death of everyone
void() EndGameSetup =
{
self.health = 10;
self.think = EndGame;
self.nextthink = time + 4;
self.weapon = 0;
self.currentammo = 0;
self.currentmag = 0;
self.weaponmodel = "";
self.weapon2model = "";
self.animend = SUB_Null;
self.perks = 0;
self.movetype = MOVETYPE_TOSS;
entity gameover_watcher = spawn();
gameover_watcher.think = EndGame;
gameover_watcher.nextthink = time + 4;
gameover_watcher.classname = "gameover_watcher";
entity players = find(world, classname, "player");
while(players != world) {
players.health = 0;
players.weapon = 0;
players.currentammo = 0;
players.currentmag = 0;
players.weaponmodel = "";
players.weapon2model = "";
players.animend = SUB_Null;
players.perks = 0;
players.movetype = MOVETYPE_TOSS;
players.velocity = '0 0 0';
Player_RemoveScore(players, players.points);
Player_AddScore(players, players.score, false);
players = find(players, classname, "player");
}
if (!game_over) {
Rounds_PlayTransition("sounds/music/end.wav");
@ -158,7 +163,7 @@ void() EndGameSetup =
{
Weapon_RemoveWeapon(i);
}
return;
return;
}
float() push_away_zombies;

View file

@ -43,7 +43,15 @@
float cheats_have_been_activated;
// Quake assumes these are defined.
// cypress - NOTE
// on/off are inverted compared to goldsrc for
// compatibility with existing nzp content.
#define TRIGGERSTATE_ON 0
#define TRIGGERSTATE_OFF 1
#define TRIGGERSTATE_TOGGLE 2
.float triggerstate;
string string_null;
.string killtarget;
entity activator;

View file

@ -273,6 +273,7 @@ void() misc_model =
self.model = "models/missing_model.mdl";
}
self.oldmodel = self.model;
//
// Set default stats.
@ -384,7 +385,10 @@ void() place_model =
#define SPAWNFLAG_COUNTER_RESETONFIRE 2
void() game_counter_increment =
{
self.frags++;
if (global_triggerstate == TRIGGERSTATE_OFF)
self.frags--;
else
self.frags++;
if (self.frags == self.health) {
SUB_UseTargets();

View file

@ -54,6 +54,8 @@ void() DelayThink =
remove(self);
};
float global_triggerstate;
void() SUB_UseTargets =
{
local entity t, stemp, otemp, act;
@ -146,6 +148,7 @@ void() SUB_UseTargets =
t.enemy = activator;
t.message = self.message;
t.killtarget = self.killtarget;
t.triggerstate = self.triggerstate;
t.target = self.target;
t.target2 = self.target2;
t.target3 = self.target3;
@ -189,8 +192,12 @@ void() SUB_UseTargets =
t.classname = "spawn_dog";
if (self.use != SUB_Null)
{
global_triggerstate = self.triggerstate;
if (self.use)
self.use();
global_triggerstate = -1;
}
self = stemp;
other = otemp;

View file

@ -215,12 +215,6 @@ void() precaches =
precache_model ("models/weapons/grenade/g_grenade.mdl");
precache_extra (W_BIATCH);
// perk machine defaults
//precache_model (PERK_QUICKREVIVE_DEFAULT_MODEL);
//precache_model (PERK_JUGGERNOG_DEFAULT_MODEL);
//precache_model (PERK_SPEEDCOLA_DEFAULT_MODEL);
//precache_model (PERK_)
#ifdef FTE
// FTE only has co-op, so don't precache morphine elsewhere.
@ -371,13 +365,17 @@ void() worldspawn =
clientstat(STAT_INSTA, EV_FLOAT, insta_icon);
clientstat(STAT_X2, EV_FLOAT, x2_icon);
clientstat(STAT_SPECTATING, EV_FLOAT, is_spectator);
clientstat(STAT_PLAYERNUM, EV_FLOAT, playernum); // literally useless but will be kept in case
clientstat(STAT_PLAYERNUM, EV_FLOAT, playernum);
clientstat(STAT_PLAYERSTANCE, EV_FLOAT, stance);
clientstat(STAT_FACINGENEMY, EV_FLOAT, facingenemy);
clientstat(STAT_VIEWZOOM, EV_FLOAT, viewzoom);
clientstat(STAT_MAXHEALTH, EV_FLOAT, max_health);
clientstat(STAT_PERKS, EV_FLOAT, perks);
autocvar(sv_enablechatplugins, 0);
autocvar(sv_maprotationbasename, "map_rotation");
autocvar(sv_maprotationmode, 0);
#endif // FTE
mappath = strcat("maps/", mapname);

View file

@ -0,0 +1,140 @@
/*
server/map_rotation.qc
Map Rotation Logic for Dedicated Servers.
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef FTE
#define MAPROTATION_MODE_DONTROTATE 0
#define MAPROTATION_MODE_FIXEDROTATION 1
#define MAPROTATION_MODE_RANDOM 2
//
// MapRotation_RestartMap()
// Loads the current map once again.
//
void() MapRotation_RestartMap =
{
localcmd(sprintf("changelevel %s\n", mapname));
};
//
// MapRotation_GetRandomMap()
// Loads a random map in the server's
// map directory.
//
void() MapRotation_GetRandomMap =
{
searchhandle maps;
string map_path;
string map_name;
float map_count;
float map_index;
// Grab a random map file
maps = search_begin("maps/*.bsp", 0, 0);
map_count = search_getsize(maps);
map_index = rint(random() * map_count);
map_path = search_getfilename(maps, map_index);
map_name = substring(map_path, 5, strlen(map_path)); // maps/
map_name = substring(map_name, 0, strlen(map_name) - 4); // .bsp
search_end(maps);
localcmd(sprintf("changelevel %s\n", map_name));
};
//
// MapRotation_SelectNextMap()
// Loads the next map in the map rotation
// text file.
//
void() MapRotation_SelectNextMap =
{
float rotation_file = fopen(sprintf("%s.txt", cvar_string("sv_maprotationbasename")), FILE_READ);
if (rotation_file == -1) {
MapRotation_GetRandomMap();
}
float load_loop = true;
string map_to_load = "";
string first_map = fgets(rotation_file);
string current_map = first_map;
while(load_loop) {
// End of file, use the first map in the file.
if not (current_map) {
load_loop = false;
break;
}
// Current line is for our map, so load the next
// and then break out.
if (mapname == current_map) {
map_to_load = fgets(rotation_file);
load_loop = false;
break;
}
current_map = fgets(rotation_file);
}
// If map_to_load is blank, use the first map in the rotation list.
if (map_to_load == "") {
map_to_load = first_map;
}
fclose(rotation_file);
localcmd(sprintf("changelevel %s\n", strtrim(map_to_load)));
};
//
// MapRotation_Decide()
// Called at game's end, picks an appropriate
// map rotation mode.
//
void() MapRotation_Decide =
{
float map_rotation_mode = cvar("sv_maprotationmode");
switch(map_rotation_mode) {
case MAPROTATION_MODE_DONTROTATE:
MapRotation_RestartMap();
break;
case MAPROTATION_MODE_FIXEDROTATION:
MapRotation_SelectNextMap();
break;
case MAPROTATION_MODE_RANDOM:
MapRotation_GetRandomMap();
break;
default:
MapRotation_RestartMap();
break;
}
};
#endif // FTE

View file

@ -204,7 +204,12 @@ void(entity client) LastStand_KillPlayer =
self = client;
DisableReviveIcon(self.playernum);
startspectate();
// If we are in the middle of the Game Over
// sequence, we should not allow a bleed
// out to override it.
if (!game_over)
startspectate();
self = old_self;
};

View file

@ -592,6 +592,9 @@ void() PlayerPostThink =
// Network everything
self.SendFlags = 1;
// Used to tell which gun to fire for akimbo weapons
self.is_using_gamepad = stof(infokey(self, "using_gamepad"));
// Obtain menu state from CSQC via infokeys.
self.is_in_menu = stof(infokey(self, "in_menu"));
@ -1048,6 +1051,12 @@ void() PutClientInServer =
GameRestart_ResetPerkaColas();
};
void() RestartDedicatedServer =
{
bprint(PRINT_HIGH, "[INFO]: Issuing server restart as player is connecting to an empty server.\n");
localcmd("restart\n");
}
//called when client disconnects from the server
void() ClientDisconnect =
{
@ -1079,6 +1088,18 @@ void() ClientDisconnect =
self.is_in_menu = 0;
#endif // FTE
// We can encounter a player count of zero if we are in a dedicated
// server. If this is the case we should restart the current level
// to prevent issues with a player joining mid-match stuck in spectator
// mode, or spawning in at a high round not able to progress.
if (player_count == 0) {
// We need to use a temp entity for this to add a delay, else this
// will interfere with SP games.
entity tempe = spawn();
tempe.think = RestartDedicatedServer;
tempe.nextthink = time + 0.25;
}
};

View file

@ -0,0 +1,47 @@
/*
server/plugins/plugin_core.qc
In-game chat plugin core.
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef FTE
//
// Plugin command table
// command_name : Command string entered into developer console.
// command_function : QuakeC function called when command is executed.
// Returns 0 for success, 1 for failure.
//
var struct {
string command_name;
float(string params) command_function;
float requires_arguments;
string command_description;
} plugin_commands[] = {
{"mapvote", ChatPlugin_MapVote, true, "Usage: mapvote <bsp_name>\nAttempts to initiate a Map Vote. Fails if one is already in progress.\n"},
{"mapvote_y", ChatPlugin_MapVoteYes, false, "Usage: mapvote_y\n"},
{"mapvote_n", ChatPlugin_MapVoteNo, false, "Usage: mapvote_n\n"},
};
#endif // FTE

View file

@ -0,0 +1,186 @@
/*
server/plugins/plugin_core.qc
Map Vote plugin for Dedicated Servers.
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef FTE
#define PLUGIN_MAPVOTE_VOTETIMER 60
.float plugin_mapvote_voted;
float plugin_mapvote_in_progress;
string plugin_mapvote_mapchoice;
//
// ChatPlugin_MapVoteChange()
// Changes map after successful vote.
//
void() ChatPlugin_MapVoteChange =
{
localcmd(sprintf("changelevel %s\n", plugin_mapvote_mapchoice));
};
//
// ChatPlugin_MapVotePollResults
// Called at end of voting. Changes maps if vote
// passed.
//
void() ChatPlugin_MapVotePollResults =
{
float total_yes_votes = serverkeyfloat("mapvotes_y");
float total_no_votes = serverkeyfloat("mapvotes_n");
if (total_yes_votes > total_no_votes) {
CSQC_SendChatMessage(255, sprintf("[MapVote] Vote to switch map to '%s' has PASSED.", plugin_mapvote_mapchoice));
CSQC_SendChatMessage(255, "[MapVote] Changing maps in 15 seconds.");
self.think = ChatPlugin_MapVoteChange;
self.nextthink = time + 15;
} else {
CSQC_SendChatMessage(255, sprintf("[MapVote] Vote to switch map to '%s' has FAILED.", plugin_mapvote_mapchoice));
plugin_mapvote_in_progress = false;
remove(self);
}
};
//
// ChatPlugin_MapVoteClear()
// Clears any previous Map Vote infokeys.
//
void() ChatPlugin_MapVoteClear =
{
forceinfokey(world, "mapvotes_y", "0");
forceinfokey(world, "mapvotes_n", "0");
entity players = find(world, classname, "player");
while (players != world) {
players.plugin_mapvote_voted = false;
players = find(players, classname, "player");
}
entity spectators = find(world, classname, "spectator");
while (spectators != world) {
spectators.plugin_mapvote_voted = false;
spectators = find(spectators, classname, "player");
}
};
//
// ChatPlugin_MapVoteYes()
// "mapvote_y" command, votes Yes on Map Vote.
//
float(string params) ChatPlugin_MapVoteYes =
{
// Fail if no map vote is active.
if (plugin_mapvote_in_progress == false) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot vote because no Map Vote is active.", self);
return 1;
}
// Fail if we have already voted.
if (self.plugin_mapvote_voted == true) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot vote because you have already voted.", self);
return 1;
}
forceinfokey(world, "mapvotes_y", ftos(serverkeyfloat("mapvotes_y") + 1));
self.plugin_mapvote_voted = true;
CSQC_SendChatMessage(255, sprintf("[MapVote] %s voted YES to change map to '%s'", self.netname, plugin_mapvote_mapchoice));
return 0;
};
//
// ChatPlugin_MapVoteNo()
// "mapvote_n" command, votes No on Map Vote.
//
float(string params) ChatPlugin_MapVoteNo =
{
// Fail if no map vote is active.
if (plugin_mapvote_in_progress == false) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot vote because no Map Vote is active.", self);
return 1;
}
// Fail if we have already voted.
if (self.plugin_mapvote_voted == true) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot vote because you have already voted.", self);
return 1;
}
forceinfokey(world, "mapvotes_n", ftos(serverkeyfloat("mapvotes_n") + 1));
self.plugin_mapvote_voted = true;
CSQC_SendChatMessage(255, sprintf("[MapVote] %s voted NO to change map to '%s'", self.netname, plugin_mapvote_mapchoice));
return 0;
};
//
// ChatPlugin_MapVote(params)
// "mapvote" command, initiates Map Vote.
//
float(string params) ChatPlugin_MapVote =
{
// Fail if a map vote is already in progress.
if (plugin_mapvote_in_progress) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot initiate a Map Vote while one is already in progress.", self);
return 1;
}
// Fail if the requested map is the one currently active.
if (mapname == params) {
CSQC_SendChatMessageToPlayer(255, "[MapVote] Cannot vote for a map that the server is already running.", self);
return 1;
}
plugin_mapvote_mapchoice = params;
// Fail if the requested map does not exist on disk.
float bsp = fopen(sprintf("maps/%s.bsp", plugin_mapvote_mapchoice), FILE_READ);
if (bsp == -1) {
CSQC_SendChatMessageToPlayer(255, sprintf("[MapVote] Could not find map '%s' on server.", plugin_mapvote_mapchoice), self);
return 1;
}
fclose(bsp);
// Reset any old votes.
ChatPlugin_MapVoteClear();
plugin_mapvote_in_progress = true;
CSQC_SendChatMessage(255, sprintf("[MapVote] Begining vote for map '%s'", plugin_mapvote_mapchoice));
CSQC_SendChatMessage(255, "Vote with 'mapvote_y' or 'mapvote_n' to switch maps.");
CSQC_SendChatMessage(255, sprintf("Voting will end in %d seconds.", PLUGIN_MAPVOTE_VOTETIMER));
// Spawn a temporary entity to handle polling results.
entity tempe = spawn();
tempe.classname = "plugin_mapvote_poller";
tempe.think = ChatPlugin_MapVotePollResults;
tempe.nextthink = time + PLUGIN_MAPVOTE_VOTETIMER;
return 0;
};
#endif // FTE

View file

@ -258,11 +258,13 @@ void() NewRound =
} else {
roundtype = 1;
if (world.fog) {
localcmd(strcat("fog ", world.fog));
} else {
localcmd("fog 0 0 0 0 0\n");
}
string fog_value;
if (world.fog)
fog_value = strcat(world.fog, "\n");
else
fog_value = "0 0 0 0 0\n";
localcmd(strcat("fog ", fog_value));
}
Remaining_Zombies = Total_Zombies = getZombieTotal();

View file

@ -196,10 +196,27 @@ float(string params) Command_say =
{
// Grab parameters.
tokenize(params);
string value = argv(0);
string first_word = argv(0);
#ifdef FTE
if (cvar("sv_enablechatplugins")) {
// Support for custom chat commands.
for (float i = 0; i < plugin_commands.length; i++) {
// Command names match
if (first_word == plugin_commands[i].command_name) {
// Return description if no params are given
if (argv(1) == "" && plugin_commands[i].requires_arguments == true) {
CSQC_SendChatMessageToPlayer(255, plugin_commands[i].command_description, self);
} else {
plugin_commands[i].command_function(argv(1));
}
return COMMAND_SUCCESS;
}
}
}
CSQC_SendChatMessage(self.playernum, params);
#endif // FTE

View file

@ -1912,9 +1912,20 @@ void() WeaponCore_FireButtonPressed =
// to which click of the mouse you've provided, which is different behavior
// to single-hand weapons, where left click fires its right-side. (Hooray
// for ternaries!).
// For gamepads, we don't want this behavior, because the gun that
// fires will be the wrong side
#ifdef FTE
(IsDualWeapon(self.weapon)) ? W_Fire(S_LEFT) : W_Fire(S_RIGHT);
if (self.is_using_gamepad)
{
// Gamepads always fire the right side
W_Fire(S_RIGHT);
}
else
{
// Otherwise, fire the side of the mouse click for akimbo weapons
(IsDualWeapon(self.weapon)) ? W_Fire(S_LEFT) : W_Fire(S_RIGHT);
}
#else
@ -1959,8 +1970,17 @@ void() WeaponCore_AimButtonPressed =
// fire our secondary as opposed to Aiming down the Sight.
if (IsDualWeapon(self.weapon)) {
#ifdef FTE
// On KBM this is the right click, so it should fire the right weapon.
// On gamepads, this is the left trigger, so it should fire the left weapon.
W_Fire(S_RIGHT);
if (self.is_using_gamepad)
{
W_Fire(S_LEFT);
}
else
{
W_Fire(S_RIGHT);
}
#else

View file

@ -316,6 +316,7 @@ float game_over;
#ifdef FTE
.float is_in_menu;
.float is_using_gamepad;
#endif // FTE
//