Kart-Public/src/d_netcmd.c

5783 lines
169 KiB
C
Raw Normal View History

2014-03-15 09:59:03 -07:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 04:35:38 -08:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 09:59:03 -07:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file d_netcmd.c
/// \brief host/client network commands
/// commands are executed through the command buffer
/// like console commands, other miscellaneous commands (at the end)
#include "doomdef.h"
#include "console.h"
#include "command.h"
#include "i_system.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "g_input.h"
#include "m_menu.h"
#include "r_local.h"
#include "r_things.h"
#include "p_local.h"
#include "p_setup.h"
#include "s_sound.h"
#include "i_sound.h"
2014-03-15 09:59:03 -07:00
#include "m_misc.h"
#include "am_map.h"
#include "byteptr.h"
#include "d_netfil.h"
#include "p_spec.h"
#include "m_cheat.h"
#include "d_clisrv.h"
#include "v_video.h"
#include "d_main.h"
#include "m_random.h"
#include "f_finale.h"
2018-08-28 13:08:47 -07:00
#include "filesrch.h"
2014-03-15 09:59:03 -07:00
#include "mserv.h"
#include "md5.h"
#include "z_zone.h"
#include "lua_script.h"
#include "lua_hook.h"
#include "m_cond.h"
#include "m_anigif.h"
2016-08-11 18:42:11 -07:00
#include "k_kart.h" // SRB2kart
#include "y_inter.h"
2014-03-15 09:59:03 -07:00
#ifdef NETGAME_DEVMODE
#define CV_RESTRICT CV_NETVAR
#else
#define CV_RESTRICT 0
#endif
// ------
// protos
// ------
static void Got_NameAndColor(UINT8 **cp, INT32 playernum);
static void Got_WeaponPref(UINT8 **cp, INT32 playernum);
static void Got_Mapcmd(UINT8 **cp, INT32 playernum);
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum);
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum);
static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum);
static void Got_PickVotecmd(UINT8 **cp, INT32 playernum);
2014-03-15 09:59:03 -07:00
static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum);
#ifdef DELFILE
static void Got_Delfilecmd(UINT8 **cp, INT32 playernum);
#endif
static void Got_Addfilecmd(UINT8 **cp, INT32 playernum);
static void Got_Pause(UINT8 **cp, INT32 playernum);
static void Got_Respawn(UINT8 **cp, INT32 playernum);
2014-03-15 09:59:03 -07:00
static void Got_RandomSeed(UINT8 **cp, INT32 playernum);
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum);
static void Got_Teamchange(UINT8 **cp, INT32 playernum);
static void Got_Clearscores(UINT8 **cp, INT32 playernum);
static void PointLimit_OnChange(void);
static void TimeLimit_OnChange(void);
static void NumLaps_OnChange(void);
static void Mute_OnChange(void);
static void Hidetime_OnChange(void);
static void AutoBalance_OnChange(void);
static void TeamScramble_OnChange(void);
static void NetTimeout_OnChange(void);
static void JoinTimeout_OnChange(void);
2014-03-15 09:59:03 -07:00
static void Ringslinger_OnChange(void);
static void Gravity_OnChange(void);
static void ForceSkin_OnChange(void);
static void Name_OnChange(void);
static void Name2_OnChange(void);
2017-12-07 23:29:01 -08:00
static void Name3_OnChange(void);
static void Name4_OnChange(void);
2014-03-15 09:59:03 -07:00
static void Skin_OnChange(void);
static void Skin2_OnChange(void);
2017-12-07 23:29:01 -08:00
static void Skin3_OnChange(void);
static void Skin4_OnChange(void);
2014-03-15 09:59:03 -07:00
static void Color_OnChange(void);
static void Color2_OnChange(void);
2017-12-07 23:29:01 -08:00
static void Color3_OnChange(void);
static void Color4_OnChange(void);
2014-03-15 09:59:03 -07:00
static void DummyConsvar_OnChange(void);
static void SoundTest_OnChange(void);
static void BaseNumLaps_OnChange(void);
static void KartFrantic_OnChange(void);
static void KartSpeed_OnChange(void);
static void KartEncore_OnChange(void);
static void KartComeback_OnChange(void);
static void KartEliminateLast_OnChange(void);
2014-03-15 09:59:03 -07:00
#ifdef NETGAME_DEVMODE
static void Fishcake_OnChange(void);
#endif
static void Command_Playdemo_f(void);
static void Command_Timedemo_f(void);
static void Command_Stopdemo_f(void);
static void Command_StartMovie_f(void);
static void Command_StopMovie_f(void);
static void Command_Map_f(void);
static void Command_ResetCamera_f(void);
static void Command_View_f (void);
2019-02-13 16:33:45 -08:00
static void Command_SetViews_f(void);
2014-03-15 09:59:03 -07:00
static void Command_Addfile(void);
static void Command_ListWADS_f(void);
#ifdef DELFILE
static void Command_Delfile(void);
#endif
static void Command_RunSOC(void);
static void Command_Pause(void);
static void Command_Respawn(void);
2014-03-15 09:59:03 -07:00
static void Command_Version_f(void);
#ifdef UPDATE_ALERT
static void Command_ModDetails_f(void);
#endif
static void Command_ShowGametype_f(void);
FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void);
static void Command_Playintro_f(void);
static void Command_Displayplayer_f(void);
static void Command_ExitLevel_f(void);
static void Command_Showmap_f(void);
2014-04-13 22:14:58 -07:00
static void Command_Mapmd5_f(void);
2014-03-15 09:59:03 -07:00
static void Command_Teamchange_f(void);
static void Command_Teamchange2_f(void);
2017-12-07 23:29:01 -08:00
static void Command_Teamchange3_f(void);
static void Command_Teamchange4_f(void);
2014-03-15 09:59:03 -07:00
static void Command_ServerTeamChange_f(void);
static void Command_Clearscores_f(void);
// Remote Administration
static void Command_Changepassword_f(void);
static void Command_Login_f(void);
static void Got_Login(UINT8 **cp, INT32 playernum);
static void Got_Verification(UINT8 **cp, INT32 playernum);
2017-12-07 20:45:39 -08:00
static void Got_Removal(UINT8 **cp, INT32 playernum);
2014-03-15 09:59:03 -07:00
static void Command_Verify_f(void);
2017-12-07 20:45:39 -08:00
static void Command_RemoveAdmin_f(void);
2019-03-05 20:27:25 -08:00
static void Command_ChangeJoinPassword_f(void);
2014-03-15 09:59:03 -07:00
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
static void Command_ShowScores_f(void);
static void Command_ShowTime_f(void);
static void Command_Isgamemodified_f(void);
static void Command_Cheats_f(void);
#ifdef _DEBUG
static void Command_Togglemodified_f(void);
#ifdef HAVE_BLUA
static void Command_Archivetest_f(void);
#endif
#endif
// =========================================================================
// CLIENT VARIABLES
// =========================================================================
void SendWeaponPref(void);
2014-11-11 16:55:07 -08:00
void SendWeaponPref2(void);
void SendWeaponPref3(void);
void SendWeaponPref4(void);
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}};
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
static CV_PossibleValue_t mouse2port_cons_t[] = {{0, "/dev/gpmdata"}, {1, "/dev/ttyS0"},
{2, "/dev/ttyS1"}, {3, "/dev/ttyS2"}, {4, "/dev/ttyS3"}, {0, NULL}};
#else
static CV_PossibleValue_t mouse2port_cons_t[] = {{1, "COM1"}, {2, "COM2"}, {3, "COM3"}, {4, "COM4"},
{0, NULL}};
#endif
#ifdef LJOYSTICK
static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"},
{4, "/dev/js3"}, {0, NULL}};
#else
// accept whatever value - it is in fact the joystick device number
#define usejoystick_cons_t NULL
#endif
static CV_PossibleValue_t autobalance_cons_t[] = {{0, "MIN"}, {4, "MAX"}, {0, NULL}};
static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
static CV_PossibleValue_t sleeping_cons_t[] = {{0, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Teleports"},
{3, "None"}, {0, NULL}};
static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Non-Random"},
{3, "None"}, {0, NULL}};
2018-06-07 21:02:28 -07:00
//static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t match_scoring_cons_t[] = {{0, "Normal"}, {1, "Classic"}, {0, NULL}};
static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}};
static CV_PossibleValue_t timetic_cons_t[] = {{0, "Normal"}, {1, "Tics"}, {2, "Centiseconds"}, {0, NULL}};
#ifdef NETGAME_DEVMODE
static consvar_t cv_fishcake = {"fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange, 0, NULL, NULL, 0, 0, NULL};
#endif
static consvar_t cv_dummyconsvar = {"dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff,
DummyConsvar_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_restrictskinchange = {"restrictskinchange", "No", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_allowteamchange = {"allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t ingamecap_cons_t[] = {{0, "MIN"}, {MAXPLAYERS-1, "MAX"}, {0, NULL}};
consvar_t cv_ingamecap = {"ingamecap", "0", CV_NETVAR, ingamecap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_startinglives = {"startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t respawntime_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
consvar_t cv_respawntime = {"respawndelay", "1", CV_NETVAR|CV_CHEAT, respawntime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_competitionboxes = {"competitionboxes", "Random", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, competitionboxes_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
#ifdef SEENAMES
static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}};
2018-06-14 17:18:29 -07:00
consvar_t cv_seenames = {"seenames", "Off", CV_SAVE, seenames_cons_t, 0, 0, NULL, NULL, 0, 0, NULL};
2018-09-04 18:18:20 -07:00
consvar_t cv_allowseenames = {"allowseenames", "No", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
#endif
// these are just meant to be saved to the config
consvar_t cv_playername = {"name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playername2 = {"name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange, 0, NULL, NULL, 0, 0, NULL};
2017-12-07 23:29:01 -08:00
consvar_t cv_playername3 = {"name3", "Knuckles", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name3_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playername4 = {"name4", "Dr. Eggman", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name4_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
// player colors
consvar_t cv_playercolor = {"color", "Blue", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playercolor2 = {"color2", "Orange", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playercolor3 = {"color3", "Red", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color3_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playercolor4 = {"color4", "Red", CV_SAVE|CV_CALL|CV_NOINIT, Color_cons_t, Color4_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
// player's skin, saved for commodity, when using a favorite skins wad..
consvar_t cv_skin = {"skin", DEFAULTSKIN, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skin2 = {"skin2", DEFAULTSKIN2, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin2_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skin3 = {"skin3", DEFAULTSKIN3, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin3_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skin4 = {"skin4", DEFAULTSKIN4, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin4_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
INT32 cv_debug;
consvar_t cv_usemouse = {"use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL};
2018-12-06 03:50:18 -08:00
#if defined (DC) || defined (_XBOX) || defined (WMINPUT) || defined (_WII) || defined(HAVE_SDL) || defined(_WINDOWS) //joystick 1 and 2
2014-03-15 09:59:03 -07:00
consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick3 = {"use_joystick3", "3", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick3, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick4 = {"use_joystick4", "4", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick4, 0, NULL, NULL, 0, 0, NULL};
2019-01-05 22:00:30 -08:00
#endif
2014-07-25 16:10:24 -07:00
#if (defined (LJOYSTICK) || defined (HAVE_SDL))
2014-03-15 09:59:03 -07:00
#ifdef LJOYSTICK
consvar_t cv_joyport = {"joyport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_joyport2 = {"joyport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: for later
#endif
consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, 0, NULL, NULL, 0, 0, NULL};
2017-12-07 23:29:01 -08:00
consvar_t cv_joyscale3 = {"joyscale3", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale3, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_joyscale4 = {"joyscale4", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale4, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
#else
consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save
consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save
#endif
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
consvar_t cv_mouse2port = {"mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mouse2opt = {"mouse2opt", "0", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
#else
consvar_t cv_mouse2port = {"mouse2port", "COM2", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
consvar_t cv_matchboxes = {"matchboxes", "Normal", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, matchboxes_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_specialrings = {"specialrings", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_powerstones = {"powerstones", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
/*consvar_t cv_recycler = {"tv_recycler", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_teleporters = {"tv_teleporter", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_superring = {"tv_superring", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_supersneakers = {"tv_supersneaker", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invincibility = {"tv_invincibility", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_jumpshield = {"tv_jumpshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_watershield = {"tv_watershield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ringshield = {"tv_ringshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_forceshield = {"tv_forceshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_bombshield = {"tv_bombshield", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_1up = {"tv_1up", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_eggmanbox = {"tv_eggman", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};*/
2014-03-15 09:59:03 -07:00
2016-08-14 20:51:08 -07:00
// SRB2kart
consvar_t cv_sneaker = {"sneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rocketsneaker = {"rocketsneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invincibility = {"invincibility", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_banana = {"banana", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-03-13 18:07:08 -07:00
consvar_t cv_eggmanmonitor = {"eggmanmonitor", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_orbinaut = {"orbinaut", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_jawz = {"jawz", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mine = {"mine", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ballhog = {"ballhog", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_selfpropelledbomb = {"selfpropelledbomb", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grow = {"grow", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_shrink = {"shrink", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-08-04 12:48:31 -07:00
consvar_t cv_thundershield = {"thundershield", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_hyudoro = {"hyudoro", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_pogospring = {"pogospring", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kitchensink = {"kitchensink", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_triplesneaker = {"triplesneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_triplebanana = {"triplebanana", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_decabanana = {"decabanana", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_tripleorbinaut = {"tripleorbinaut", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_quadorbinaut = {"quadorbinaut", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_dualjawz = {"dualjawz", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2016-08-14 20:51:08 -07:00
static CV_PossibleValue_t kartminimap_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
consvar_t cv_kartminimap = {"kartminimap", "4", CV_SAVE, kartminimap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartcheck = {"kartcheck", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-06-14 17:18:29 -07:00
static CV_PossibleValue_t kartinvinsfx_cons_t[] = {{0, "Music"}, {1, "SFX"}, {0, NULL}};
consvar_t cv_kartinvinsfx = {"kartinvinsfx", "SFX", CV_SAVE, kartinvinsfx_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartspeed = {"kartspeed", "Normal", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartbumpers_cons_t[] = {{1, "MIN"}, {12, "MAX"}, {0, NULL}};
consvar_t cv_kartbumpers = {"kartbumpers", "3", CV_NETVAR|CV_CHEAT, kartbumpers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartfrantic = {"kartfrantic", "Off", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartFrantic_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartComeback_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-06-14 17:18:29 -07:00
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {0, NULL}};
consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartdebugitem_cons_t[] = {{-1, "MIN"}, {NUMKARTITEMS-1, "MAX"}, {0, NULL}};
consvar_t cv_kartdebugitem = {"kartdebugitem", "0", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugitem_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartdebugamount_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}};
consvar_t cv_kartdebugamount = {"kartdebugamount", "1", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugamount_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugshrink = {"kartdebugshrink", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugdistribution = {"kartdebugdistribution", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebughuddrop = {"kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugcheckpoint = {"kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugnodes = {"kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugcolorize = {"kartdebugcolorize", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2018-01-27 20:52:01 -08:00
static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_votetime = {"votetime", "20", CV_NETVAR, votetime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_ringslinger = {"ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT, CV_YesNo,
Ringslinger_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_gravity = {"gravity", "0.8", CV_RESTRICT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_soundtest = {"soundtest", "0", CV_CALL, NULL, SoundTest_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t minitimelimit_cons_t[] = {{15, "MIN"}, {9999, "MAX"}, {0, NULL}};
2018-09-04 18:18:20 -07:00
consvar_t cv_countdowntime = {"countdowntime", "30", CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_touchtag = {"touchtag", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_hidetime = {"hidetime", "30", CV_NETVAR|CV_CALL|CV_NOSHOWHELP, minitimelimit_cons_t, Hidetime_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_autobalance = {"autobalance", "0", CV_NETVAR|CV_CALL, autobalance_cons_t, AutoBalance_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_teamscramble = {"teamscramble", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_scrambleonchange = {"scrambleonchange", "Off", CV_NETVAR, teamscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_friendlyfire = {"friendlyfire", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, ItemFinder_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
// Scoring type options
consvar_t cv_match_scoring = {"matchscoring", "Normal", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, match_scoring_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_timetic = {"timerres", "Normal", CV_SAVE|CV_NOSHOWHELP, timetic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t pointlimit_cons_t[] = {{0, "MIN"}, {999999990, "MAX"}, {0, NULL}};
consvar_t cv_pointlimit = {"pointlimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t,
PointLimit_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t timelimit_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
consvar_t cv_timelimit = {"timelimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t,
TimeLimit_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}};
consvar_t cv_numlaps = {"numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t,
2014-03-15 09:59:03 -07:00
NumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}};
consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
2019-03-05 16:04:15 -08:00
consvar_t cv_forceskin = {"forceskin", "Off", CV_NETVAR|CV_CALL|CV_CHEAT, Forceskin_cons_t, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_downloading = {"downloading", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowexitlevel = {"allowexitlevel", "No", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_nettimeout = {"nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
//static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t maxping_cons_t[] = {{0, "MIN"}, {1000, "MAX"}, {0, NULL}};
2018-11-30 00:36:44 -08:00
consvar_t cv_maxping = {"maxping", "800", CV_SAVE, maxping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}};
consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}};
consvar_t cv_showping = {"showping", "Always", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
// Intermission time Tails 04-19-2002
static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_inttime = {"inttime", "20", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Same"}, {1, "Next"}, {2, "Random"}, {3, "Vote"}, {0, NULL}};
consvar_t cv_advancemap = {"advancemap", "Vote", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "All"}, {0, NULL}};
consvar_t cv_playersforexit = {"playersforexit", "One", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_sleep = {"cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL};
2014-03-15 09:59:03 -07:00
INT16 gametype = GT_RACE; // SRB2kart
boolean forceresetplayers = false;
boolean deferencoremode = false;
UINT8 splitscreen = 0;
boolean circuitmap = true; // SRB2kart
2017-12-07 20:45:39 -08:00
INT32 adminplayers[MAXPLAYERS];
2014-03-15 09:59:03 -07:00
2016-12-31 10:26:33 -08:00
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
{
"NAMEANDCOLOR",
"WEAPONPREF",
"KICK",
"NETVAR",
"SAY",
"MAP",
"EXITLEVEL",
"ADDFILE",
"PAUSE",
"ADDPLAYER",
"TEAMCHANGE",
"CLEARSCORES",
"LOGIN",
"VERIFIED",
"RANDOMSEED",
"RUNSOC",
"REQADDFILE",
"DELFILE",
"SETMOTD",
"RESPAWN",
2017-12-07 23:29:01 -08:00
"DEMOTED",
"SETUPVOTE",
"MODIFYVOTE",
"PICKVOTE",
"REMOVEPLAYER",
2016-12-31 10:26:33 -08:00
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
#endif
};
2014-03-15 09:59:03 -07:00
// =========================================================================
// SERVER STARTUP
// =========================================================================
/** Registers server commands and variables.
* Anything required by a dedicated server should probably go here.
*
* \sa D_RegisterClientCommands
*/
void D_RegisterServerCommands(void)
{
INT32 i;
2019-03-05 16:04:15 -08:00
Forceskin_cons_t[0].value = -1;
Forceskin_cons_t[0].strvalue = "Off";
for (i = 0; i < NUMGAMETYPES; i++)
{
gametype_cons_t[i].value = i;
gametype_cons_t[i].strvalue = Gametype_Names[i];
}
gametype_cons_t[NUMGAMETYPES].value = 0;
gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
// Set the values to 0/NULL, it will be overwritten later when a skin is assigned to the slot.
for (i = 1; i < MAXSKINS; i++)
{
Forceskin_cons_t[i].value = 0;
Forceskin_cons_t[i].strvalue = NULL;
}
2014-03-15 09:59:03 -07:00
RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor);
RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref);
RegisterNetXCmd(XD_MAP, Got_Mapcmd);
RegisterNetXCmd(XD_EXITLEVEL, Got_ExitLevelcmd);
RegisterNetXCmd(XD_ADDFILE, Got_Addfilecmd);
RegisterNetXCmd(XD_REQADDFILE, Got_RequestAddfilecmd);
#ifdef DELFILE
RegisterNetXCmd(XD_DELFILE, Got_Delfilecmd);
#endif
RegisterNetXCmd(XD_PAUSE, Got_Pause);
RegisterNetXCmd(XD_RESPAWN, Got_Respawn);
2014-03-15 09:59:03 -07:00
RegisterNetXCmd(XD_RUNSOC, Got_RunSOCcmd);
#ifdef HAVE_BLUA
RegisterNetXCmd(XD_LUACMD, Got_Luacmd);
#endif
RegisterNetXCmd(XD_SETUPVOTE, Got_SetupVotecmd);
RegisterNetXCmd(XD_MODIFYVOTE, Got_ModifyVotecmd);
RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd);
2014-03-15 09:59:03 -07:00
// Remote Administration
2019-03-09 13:00:48 -08:00
CV_RegisterVar(&cv_dummyjoinpassword);
2019-03-05 20:27:25 -08:00
COM_AddCommand("joinpassword", Command_ChangeJoinPassword_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
COM_AddCommand("promote", Command_Verify_f);
2014-03-15 09:59:03 -07:00
RegisterNetXCmd(XD_VERIFIED, Got_Verification);
COM_AddCommand("demote", Command_RemoveAdmin_f);
2017-12-07 20:45:39 -08:00
RegisterNetXCmd(XD_DEMOTED, Got_Removal);
2014-03-15 09:59:03 -07:00
COM_AddCommand("motd", Command_MotD_f);
RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
RegisterNetXCmd(XD_TEAMCHANGE, Got_Teamchange);
COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f);
RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
COM_AddCommand("clearscores", Command_Clearscores_f);
COM_AddCommand("map", Command_Map_f);
COM_AddCommand("exitgame", Command_ExitGame_f);
COM_AddCommand("retry", Command_Retry_f);
COM_AddCommand("exitlevel", Command_ExitLevel_f);
COM_AddCommand("showmap", Command_Showmap_f);
2014-04-13 22:14:58 -07:00
COM_AddCommand("mapmd5", Command_Mapmd5_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("addfile", Command_Addfile);
COM_AddCommand("listwad", Command_ListWADS_f);
#ifdef DELFILE
COM_AddCommand("delfile", Command_Delfile);
#endif
COM_AddCommand("runsoc", Command_RunSOC);
COM_AddCommand("pause", Command_Pause);
COM_AddCommand("respawn", Command_Respawn);
2014-03-15 09:59:03 -07:00
COM_AddCommand("gametype", Command_ShowGametype_f);
COM_AddCommand("version", Command_Version_f);
#ifdef UPDATE_ALERT
COM_AddCommand("mod_details", Command_ModDetails_f);
#endif
COM_AddCommand("quit", Command_Quit_f);
COM_AddCommand("saveconfig", Command_SaveConfig_f);
COM_AddCommand("loadconfig", Command_LoadConfig_f);
COM_AddCommand("changeconfig", Command_ChangeConfig_f);
COM_AddCommand("isgamemodified", Command_Isgamemodified_f); // test
COM_AddCommand("showscores", Command_ShowScores_f);
COM_AddCommand("showtime", Command_ShowTime_f);
COM_AddCommand("cheats", Command_Cheats_f); // test
#ifdef _DEBUG
COM_AddCommand("togglemodified", Command_Togglemodified_f);
#ifdef HAVE_BLUA
COM_AddCommand("archivetest", Command_Archivetest_f);
#endif
#endif
// for master server connection
AddMServCommands();
// p_mobj.c
CV_RegisterVar(&cv_itemrespawntime);
CV_RegisterVar(&cv_itemrespawn);
CV_RegisterVar(&cv_flagtime);
CV_RegisterVar(&cv_suddendeath);
// misc
CV_RegisterVar(&cv_friendlyfire);
CV_RegisterVar(&cv_pointlimit);
CV_RegisterVar(&cv_numlaps);
CV_RegisterVar(&cv_basenumlaps);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_autobalance);
CV_RegisterVar(&cv_teamscramble);
CV_RegisterVar(&cv_scrambleonchange);
CV_RegisterVar(&cv_touchtag);
CV_RegisterVar(&cv_hidetime);
CV_RegisterVar(&cv_inttime);
CV_RegisterVar(&cv_advancemap);
CV_RegisterVar(&cv_playersforexit);
CV_RegisterVar(&cv_timelimit);
CV_RegisterVar(&cv_playbackspeed);
CV_RegisterVar(&cv_forceskin);
CV_RegisterVar(&cv_downloading);
CV_RegisterVar(&cv_specialrings);
CV_RegisterVar(&cv_powerstones);
CV_RegisterVar(&cv_competitionboxes);
CV_RegisterVar(&cv_matchboxes);
/*CV_RegisterVar(&cv_recycler);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_teleporters);
CV_RegisterVar(&cv_superring);
CV_RegisterVar(&cv_supersneakers);
CV_RegisterVar(&cv_invincibility);
CV_RegisterVar(&cv_jumpshield);
CV_RegisterVar(&cv_watershield);
CV_RegisterVar(&cv_ringshield);
CV_RegisterVar(&cv_forceshield);
CV_RegisterVar(&cv_bombshield);
CV_RegisterVar(&cv_1up);
CV_RegisterVar(&cv_eggmanbox);*/
2016-08-14 20:51:08 -07:00
K_RegisterKartStuff(); // SRB2kart
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_ringslinger);
CV_RegisterVar(&cv_startinglives);
CV_RegisterVar(&cv_countdowntime);
CV_RegisterVar(&cv_runscripts);
CV_RegisterVar(&cv_match_scoring);
CV_RegisterVar(&cv_overtime);
CV_RegisterVar(&cv_pause);
CV_RegisterVar(&cv_mute);
RegisterNetXCmd(XD_RANDOMSEED, Got_RandomSeed);
CV_RegisterVar(&cv_allowexitlevel);
CV_RegisterVar(&cv_restrictskinchange);
CV_RegisterVar(&cv_allowteamchange);
CV_RegisterVar(&cv_ingamecap);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_respawntime);
CV_RegisterVar(&cv_killingdead);
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_resynchattempts);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_maxsend);
CV_RegisterVar(&cv_noticedownload);
CV_RegisterVar(&cv_downloadspeed);
#ifndef NONET
CV_RegisterVar(&cv_allownewplayer);
#ifdef VANILLAJOINNEXTROUND
CV_RegisterVar(&cv_joinnextround);
#endif
CV_RegisterVar(&cv_showjoinaddress);
CV_RegisterVar(&cv_blamecfail);
#endif
2014-03-15 09:59:03 -07:00
COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout);
CV_RegisterVar(&cv_jointimeout);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep);
CV_RegisterVar(&cv_maxping);
CV_RegisterVar(&cv_pingtimeout);
CV_RegisterVar(&cv_showping);
2014-03-15 09:59:03 -07:00
#ifdef SEENAMES
CV_RegisterVar(&cv_allowseenames);
#endif
CV_RegisterVar(&cv_dummyconsvar);
}
// =========================================================================
// CLIENT STARTUP
// =========================================================================
/** Registers client commands and variables.
* Nothing needed for a dedicated server should be registered here.
*
* \sa D_RegisterServerCommands
*/
void D_RegisterClientCommands(void)
{
INT32 i;
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = i;
2016-08-11 18:42:11 -07:00
Color_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart
2014-03-15 09:59:03 -07:00
}
Color_cons_t[MAXSKINCOLORS].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = NULL;
if (dedicated)
return;
COM_AddCommand("numthinkers", Command_Numthinkers_f);
COM_AddCommand("countmobjs", Command_CountMobjs_f);
COM_AddCommand("changeteam", Command_Teamchange_f);
COM_AddCommand("changeteam2", Command_Teamchange2_f);
2017-12-07 23:29:01 -08:00
COM_AddCommand("changeteam3", Command_Teamchange3_f);
COM_AddCommand("changeteam4", Command_Teamchange4_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("playdemo", Command_Playdemo_f);
COM_AddCommand("timedemo", Command_Timedemo_f);
COM_AddCommand("stopdemo", Command_Stopdemo_f);
COM_AddCommand("playintro", Command_Playintro_f);
COM_AddCommand("resetcamera", Command_ResetCamera_f);
COM_AddCommand("view", Command_View_f);
COM_AddCommand("view2", Command_View_f);
COM_AddCommand("view3", Command_View_f);
COM_AddCommand("view4", Command_View_f);
2019-02-13 16:33:45 -08:00
COM_AddCommand("setviews", Command_SetViews_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("setcontrol", Command_Setcontrol_f);
COM_AddCommand("setcontrol2", Command_Setcontrol2_f);
2017-12-07 23:29:01 -08:00
COM_AddCommand("setcontrol3", Command_Setcontrol3_f);
COM_AddCommand("setcontrol4", Command_Setcontrol4_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("screenshot", M_ScreenShot);
COM_AddCommand("startmovie", Command_StartMovie_f);
COM_AddCommand("stopmovie", Command_StopMovie_f);
CV_RegisterVar(&cv_screenshot_option);
CV_RegisterVar(&cv_screenshot_folder);
CV_RegisterVar(&cv_moviemode);
// PNG variables
CV_RegisterVar(&cv_zlib_level);
CV_RegisterVar(&cv_zlib_memory);
CV_RegisterVar(&cv_zlib_strategy);
CV_RegisterVar(&cv_zlib_window_bits);
// APNG variables
CV_RegisterVar(&cv_zlib_levela);
CV_RegisterVar(&cv_zlib_memorya);
CV_RegisterVar(&cv_zlib_strategya);
CV_RegisterVar(&cv_zlib_window_bitsa);
CV_RegisterVar(&cv_apng_delay);
// GIF variables
CV_RegisterVar(&cv_gif_optimize);
CV_RegisterVar(&cv_gif_downscale);
2016-07-05 21:09:17 -07:00
#ifdef WALLSPLATS
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_splats);
2016-07-05 21:09:17 -07:00
#endif
2014-03-15 09:59:03 -07:00
// register these so it is saved to config
CV_RegisterVar(&cv_playername);
CV_RegisterVar(&cv_playercolor);
CV_RegisterVar(&cv_skin); // r_things.c (skin NAME)
// secondary player (splitscreen)
CV_RegisterVar(&cv_playername2);
CV_RegisterVar(&cv_playercolor2);
CV_RegisterVar(&cv_skin2);
2017-12-07 23:29:01 -08:00
// third player
CV_RegisterVar(&cv_playername3);
CV_RegisterVar(&cv_playercolor3);
CV_RegisterVar(&cv_skin3);
// fourth player
CV_RegisterVar(&cv_playername4);
CV_RegisterVar(&cv_playercolor4);
CV_RegisterVar(&cv_skin4);
// preferred number of players
CV_RegisterVar(&cv_splitplayers);
2014-03-15 09:59:03 -07:00
#ifdef SEENAMES
CV_RegisterVar(&cv_seenames);
#endif
CV_RegisterVar(&cv_rollingdemos);
CV_RegisterVar(&cv_netstat);
CV_RegisterVar(&cv_netticbuffer);
2014-03-15 09:59:03 -07:00
#ifdef NETGAME_DEVMODE
CV_RegisterVar(&cv_fishcake);
#endif
// HUD
CV_RegisterVar(&cv_timetic);
CV_RegisterVar(&cv_itemfinder);
// time attack ghost options are also saved to config
CV_RegisterVar(&cv_ghost_besttime);
CV_RegisterVar(&cv_ghost_bestlap);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_ghost_last);
CV_RegisterVar(&cv_ghost_guest);
CV_RegisterVar(&cv_ghost_staff);
2014-03-15 09:59:03 -07:00
COM_AddCommand("displayplayer", Command_Displayplayer_f);
2019-02-16 16:29:51 -08:00
CV_RegisterVar(&cv_recordmultiplayerdemos);
CV_RegisterVar(&cv_netdemosyncquality);
2019-02-16 16:29:51 -08:00
2014-03-15 09:59:03 -07:00
// FIXME: not to be here.. but needs be done for config loading
CV_RegisterVar(&cv_usegamma);
// m_menu.c
2019-01-05 12:59:23 -08:00
//CV_RegisterVar(&cv_compactscoreboard);
2018-06-15 03:20:01 -07:00
CV_RegisterVar(&cv_chatheight);
CV_RegisterVar(&cv_chatwidth);
CV_RegisterVar(&cv_chattime);
CV_RegisterVar(&cv_chatspamprotection);
CV_RegisterVar(&cv_consolechat);
CV_RegisterVar(&cv_chatnotifications);
CV_RegisterVar(&cv_chatbacktint);
2019-01-04 01:44:55 -08:00
CV_RegisterVar(&cv_songcredits);
2018-10-01 04:21:02 -07:00
//CV_RegisterVar(&cv_crosshair);
//CV_RegisterVar(&cv_crosshair2);
//CV_RegisterVar(&cv_crosshair3);
//CV_RegisterVar(&cv_crosshair4);
//CV_RegisterVar(&cv_alwaysfreelook);
//CV_RegisterVar(&cv_alwaysfreelook2);
//CV_RegisterVar(&cv_chasefreelook);
//CV_RegisterVar(&cv_chasefreelook2);
CV_RegisterVar(&cv_showfocuslost);
CV_RegisterVar(&cv_pauseifunfocused);
2014-03-15 09:59:03 -07:00
// g_input.c
CV_RegisterVar(&cv_turnaxis);
CV_RegisterVar(&cv_turnaxis2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_turnaxis3);
CV_RegisterVar(&cv_turnaxis4);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_moveaxis);
CV_RegisterVar(&cv_moveaxis2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_moveaxis3);
CV_RegisterVar(&cv_moveaxis4);
2018-03-11 15:07:28 -07:00
CV_RegisterVar(&cv_brakeaxis);
CV_RegisterVar(&cv_brakeaxis2);
CV_RegisterVar(&cv_brakeaxis3);
CV_RegisterVar(&cv_brakeaxis4);
CV_RegisterVar(&cv_aimaxis);
CV_RegisterVar(&cv_aimaxis2);
CV_RegisterVar(&cv_aimaxis3);
CV_RegisterVar(&cv_aimaxis4);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_lookaxis);
CV_RegisterVar(&cv_lookaxis2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_lookaxis3);
CV_RegisterVar(&cv_lookaxis4);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_fireaxis);
CV_RegisterVar(&cv_fireaxis2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_fireaxis3);
CV_RegisterVar(&cv_fireaxis4);
2018-03-11 15:07:28 -07:00
CV_RegisterVar(&cv_driftaxis);
CV_RegisterVar(&cv_driftaxis2);
CV_RegisterVar(&cv_driftaxis3);
CV_RegisterVar(&cv_driftaxis4);
CV_RegisterVar(&cv_deadzone);
CV_RegisterVar(&cv_deadzone2);
CV_RegisterVar(&cv_deadzone3);
CV_RegisterVar(&cv_deadzone4);
2014-03-15 09:59:03 -07:00
2018-08-28 13:08:47 -07:00
// filesrch.c
CV_RegisterVar(&cv_addons_option);
CV_RegisterVar(&cv_addons_folder);
CV_RegisterVar(&cv_addons_md5);
CV_RegisterVar(&cv_addons_showall);
CV_RegisterVar(&cv_addons_search_type);
CV_RegisterVar(&cv_addons_search_case);
2014-03-15 09:59:03 -07:00
// WARNING: the order is important when initialising mouse2
// we need the mouse2port
CV_RegisterVar(&cv_mouse2port);
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
CV_RegisterVar(&cv_mouse2opt);
#endif
CV_RegisterVar(&cv_controlperkey);
CV_RegisterVar(&cv_usemouse);
CV_RegisterVar(&cv_usemouse2);
CV_RegisterVar(&cv_invertmouse);
CV_RegisterVar(&cv_invertmouse2);
CV_RegisterVar(&cv_mousesens);
CV_RegisterVar(&cv_mousesens2);
2015-01-01 11:50:31 -08:00
CV_RegisterVar(&cv_mouseysens);
CV_RegisterVar(&cv_mouseysens2);
//CV_RegisterVar(&cv_mousemove);
//CV_RegisterVar(&cv_mousemove2);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_usejoystick);
CV_RegisterVar(&cv_usejoystick2);
2017-12-07 23:29:01 -08:00
CV_RegisterVar(&cv_usejoystick3);
CV_RegisterVar(&cv_usejoystick4);
2014-03-15 09:59:03 -07:00
#ifdef LJOYSTICK
CV_RegisterVar(&cv_joyport);
CV_RegisterVar(&cv_joyport2);
#endif
CV_RegisterVar(&cv_joyscale);
CV_RegisterVar(&cv_joyscale2);
2017-12-07 23:29:01 -08:00
CV_RegisterVar(&cv_joyscale3);
CV_RegisterVar(&cv_joyscale4);
2014-03-15 09:59:03 -07:00
// Analog Control
/*CV_RegisterVar(&cv_analog);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_analog2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_analog3);
CV_RegisterVar(&cv_analog4);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_useranalog);
CV_RegisterVar(&cv_useranalog2);
2017-12-17 18:23:11 -08:00
CV_RegisterVar(&cv_useranalog3);
CV_RegisterVar(&cv_useranalog4);*/
2014-03-15 09:59:03 -07:00
// s_sound.c
CV_RegisterVar(&cv_soundvolume);
CV_RegisterVar(&cv_digmusicvolume);
#ifndef NO_MIDI
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_midimusicvolume);
#endif
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_numChannels);
// i_cdmus.c
CV_RegisterVar(&cd_volume);
CV_RegisterVar(&cdUpdate);
// screen.c
CV_RegisterVar(&cv_fullscreen);
CV_RegisterVar(&cv_renderview);
2019-04-08 21:21:11 -07:00
CV_RegisterVar(&cv_vhseffect);
CV_RegisterVar(&cv_shittyscreen);
2014-03-15 09:59:03 -07:00
CV_RegisterVar(&cv_scr_depth);
CV_RegisterVar(&cv_scr_width);
CV_RegisterVar(&cv_scr_height);
CV_RegisterVar(&cv_soundtest);
// ingame object placing
COM_AddCommand("objectplace", Command_ObjectPlace_f);
COM_AddCommand("writethings", Command_Writethings_f);
CV_RegisterVar(&cv_speed);
CV_RegisterVar(&cv_opflags);
CV_RegisterVar(&cv_mapthingnum);
// CV_RegisterVar(&cv_grid);
// CV_RegisterVar(&cv_snapto);
// add cheat commands
COM_AddCommand("noclip", Command_CheatNoClip_f);
COM_AddCommand("god", Command_CheatGod_f);
COM_AddCommand("notarget", Command_CheatNoTarget_f);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*COM_AddCommand("getallemeralds", Command_Getallemeralds_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("resetemeralds", Command_Resetemeralds_f);
COM_AddCommand("setrings", Command_Setrings_f);
COM_AddCommand("setlives", Command_Setlives_f);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
COM_AddCommand("setcontinues", Command_Setcontinues_f);*/
2014-03-15 09:59:03 -07:00
COM_AddCommand("devmode", Command_Devmode_f);
COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f);
COM_AddCommand("scale", Command_Scale_f);
COM_AddCommand("gravflip", Command_Gravflip_f);
COM_AddCommand("hurtme", Command_Hurtme_f);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*COM_AddCommand("jumptoaxis", Command_JumpToAxis_f);
2014-03-15 09:59:03 -07:00
COM_AddCommand("charability", Command_Charability_f);
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
COM_AddCommand("charspeed", Command_Charspeed_f);*/
2014-03-15 09:59:03 -07:00
COM_AddCommand("teleport", Command_Teleport_f);
COM_AddCommand("rteleport", Command_RTeleport_f);
COM_AddCommand("skynum", Command_Skynum_f);
2014-08-03 20:49:33 -07:00
COM_AddCommand("weather", Command_Weather_f);
2014-03-15 09:59:03 -07:00
#ifdef _DEBUG
COM_AddCommand("causecfail", Command_CauseCfail_f);
#endif
#if defined(HAVE_BLUA) && defined(LUA_ALLOW_BYTECODE)
COM_AddCommand("dumplua", Command_Dumplua_f);
#endif
}
/** Checks if a name (as received from another player) is okay.
* A name is okay if it is no fewer than 1 and no more than ::MAXPLAYERNAME
* chars long (not including NUL), it does not begin or end with a space,
* it does not contain non-printing characters (according to isprint(), which
* allows space), it does not start with a digit, and no other player is
* currently using it.
* \param name Name to check.
* \param playernum Player who wants the name, so we can check if they already
* have it, and let them keep it if so.
* \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor
* \author Graue <graue@oceanbase.org>
*/
static boolean IsNameGood(char *name, INT32 playernum)
{
INT32 ix;
if (strlen(name) == 0 || strlen(name) > MAXPLAYERNAME)
return false; // Empty or too long.
if (name[0] == ' ' || name[strlen(name)-1] == ' ')
return false; // Starts or ends with a space.
if (isdigit(name[0]))
return false; // Starts with a digit.
if (name[0] == '@' || name[0] == '~')
return false; // Starts with an admin symbol.
// Check if it contains a non-printing character.
// Note: ANSI C isprint() considers space a printing character.
// Also don't allow semicolons, since they are used as
// console command separators.
// Also, anything over 0x80 is disallowed too, since compilers love to
// differ on whether they're printable characters or not.
for (ix = 0; name[ix] != '\0'; ix++)
if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80)
return false;
// Check if a player is currently using the name, case-insensitively.
for (ix = 0; ix < MAXPLAYERS; ix++)
{
if (ix != playernum && playeringame[ix]
&& strcasecmp(name, player_names[ix]) == 0)
{
// We shouldn't kick people out just because
// they joined the game with the same name
// as someone else -- modify the name instead.
size_t len = strlen(name);
// Recursion!
// Slowly strip characters off the end of the
// name until we no longer have a duplicate.
if (len > 1)
{
name[len-1] = '\0';
if (!IsNameGood (name, playernum))
return false;
}
else if (len == 1) // Agh!
{
// Last ditch effort...
2016-07-05 21:09:17 -07:00
sprintf(name, "%d", M_RandomKey(10));
2014-03-15 09:59:03 -07:00
if (!IsNameGood (name, playernum))
return false;
}
else
return false;
}
}
return true;
}
/** Cleans up a local player's name before sending a name change.
* Spaces at the beginning or end of the name are removed. Then if the new
* name is identical to another player's name, ignoring case, the name change
* is canceled, and the name in cv_playername.value or cv_playername2.value
* is restored to what it was before.
*
* We assume that if playernum is ::consoleplayer or ::secondarydisplayplayer
* the console variable ::cv_playername or ::cv_playername2 respectively is
* already set to newname. However, the player name table is assumed to
* contain the old name.
*
* \param playernum Player number who has requested a name change.
* Should be ::consoleplayer or ::secondarydisplayplayer.
* \param newname New name for that player; should already be in
* ::cv_playername or ::cv_playername2 if player is the
* console or secondary display player, respectively.
* \sa cv_playername, cv_playername2, SendNameAndColor, SendNameAndColor2,
* SetPlayerName
* \author Graue <graue@oceanbase.org>
*/
static void CleanupPlayerName(INT32 playernum, const char *newname)
{
char *buf;
char *p;
char *tmpname = NULL;
INT32 i;
boolean namefailed = true;
buf = Z_StrDup(newname);
do
{
p = buf;
while (*p == ' ')
p++; // remove leading spaces
if (strlen(p) == 0)
break; // empty names not allowed
if (isdigit(*p))
break; // names starting with digits not allowed
if (*p == '@' || *p == '~')
break; // names that start with @ or ~ (admin symbols) not allowed
tmpname = p;
// Remove trailing spaces.
p = &tmpname[strlen(tmpname)-1]; // last character
while (*p == ' ' && p >= tmpname)
{
*p = '\0';
p--;
}
if (strlen(tmpname) == 0)
break; // another case of an empty name
// Truncate name if it's too long (max MAXPLAYERNAME chars
// excluding NUL).
if (strlen(tmpname) > MAXPLAYERNAME)
tmpname[MAXPLAYERNAME] = '\0';
// Remove trailing spaces again.
p = &tmpname[strlen(tmpname)-1]; // last character
while (*p == ' ' && p >= tmpname)
{
*p = '\0';
p--;
}
// no stealing another player's name
for (i = 0; i < MAXPLAYERS; i++)
{
if (i != playernum && playeringame[i]
&& strcasecmp(tmpname, player_names[i]) == 0)
{
break;
}
}
if (i < MAXPLAYERS)
break;
// name is okay then
namefailed = false;
} while (0);
if (namefailed)
tmpname = player_names[playernum];
// set consvars whether namefailed or not, because even if it succeeded,
// spaces may have been removed
if (playernum == consoleplayer)
CV_StealthSet(&cv_playername, tmpname);
else if (playernum == displayplayers[1] || (!netgame && playernum == 1))
2014-03-15 09:59:03 -07:00
CV_StealthSet(&cv_playername2, tmpname);
else if (playernum == displayplayers[2] || (!netgame && playernum == 2))
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_playername3, tmpname);
else if (playernum == displayplayers[3] || (!netgame && playernum == 3))
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_playername4, tmpname);
2014-03-15 09:59:03 -07:00
else I_Assert(((void)"CleanupPlayerName used on non-local player", 0));
Z_Free(buf);
}
/** Sets a player's name, if it is good.
* If the name is not good (indicating a modified or buggy client), it is not
* set, and if we are the server in a netgame, the player responsible is
* kicked with a consistency failure.
*
* This function prints a message indicating the name change, unless the game
2016-07-05 21:09:17 -07:00
* is currently showing the intro, e.g. when processing autoexec.cfg.
2014-03-15 09:59:03 -07:00
*
* \param playernum Player number who has requested a name change.
* \param newname New name for that player. Should be good, but won't
* necessarily be if the client is maliciously modified or
* buggy.
* \sa CleanupPlayerName, IsNameGood
* \author Graue <graue@oceanbase.org>
*/
static void SetPlayerName(INT32 playernum, char *newname)
{
if (IsNameGood(newname, playernum))
{
if (strcasecmp(newname, player_names[playernum]) != 0)
{
if (netgame)
HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false);
2014-03-15 09:59:03 -07:00
strcpy(player_names[playernum], newname);
demo_extradata[playernum] |= DXD_NAME;
2014-03-15 09:59:03 -07:00
}
}
else
{
CONS_Printf(M_GetText("Player %d sent a bad name change\n"), playernum+1);
if (server && netgame)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
}
UINT8 CanChangeSkin(INT32 playernum)
{
// Of course we can change if we're not playing
if (!Playing() || !addedtogame)
return true;
// Force skin in effect.
2017-11-10 21:34:37 -08:00
if (client && (cv_forceskin.value != -1) && !(IsPlayerAdmin(playernum) && serverplayer == -1))
2014-03-15 09:59:03 -07:00
return false;
// Can change skin in intermission and whatnot.
if (gamestate != GS_LEVEL)
return true;
// Server has skin change restrictions.
if (cv_restrictskinchange.value)
{
if (gametype == GT_COOP)
return true;
// Can change skin during initial countdown.
if (leveltime < starttime)
2014-03-15 09:59:03 -07:00
return true;
if (G_TagGametype())
{
// Can change skin during hidetime.
if (leveltime < hidetime * TICRATE)
return true;
// IT players can always change skins to persue players hiding in character only locations.
if (players[playernum].pflags & PF_TAGIT)
return true;
}
if (players[playernum].spectator || players[playernum].playerstate == PST_DEAD || players[playernum].playerstate == PST_REBORN)
return true;
return false;
}
return true;
}
2014-03-24 19:17:59 -07:00
static void ForceAllSkins(INT32 forcedskin)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
continue;
SetPlayerSkinByNum(i, forcedskin);
// If it's me (or my brother (or my sister (or my trusty pet dog))), set appropriate skin value in cv_skin
2014-03-24 19:17:59 -07:00
if (!dedicated) // But don't do this for dedicated servers, of course.
{
if (i == consoleplayer)
CV_StealthSet(&cv_skin, skins[forcedskin].name);
else if (i == displayplayers[1])
2014-03-24 19:17:59 -07:00
CV_StealthSet(&cv_skin2, skins[forcedskin].name);
else if (i == displayplayers[2])
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_skin3, skins[forcedskin].name);
else if (i == displayplayers[3])
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_skin4, skins[forcedskin].name);
2014-03-24 19:17:59 -07:00
}
}
}
2014-03-15 09:59:03 -07:00
2017-12-16 22:21:24 -08:00
static INT32 snacpending = 0, snac2pending = 0, snac3pending = 0, snac4pending = 0, chmappending = 0;
2014-03-15 09:59:03 -07:00
// name, color, or skin has changed
//
static void SendNameAndColor(void)
{
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
p = buf;
// normal player colors
if (G_GametypeHasTeams())
{
if (players[consoleplayer].ctfteam == 1 && cv_playercolor.value != skincolor_redteam)
CV_StealthSetValue(&cv_playercolor, skincolor_redteam);
else if (players[consoleplayer].ctfteam == 2 && cv_playercolor.value != skincolor_blueteam)
CV_StealthSetValue(&cv_playercolor, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor.value)
{
if (players[consoleplayer].skincolor)
CV_StealthSetValue(&cv_playercolor, players[consoleplayer].skincolor);
else if (skins[players[consoleplayer].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue);
}
if (!strcmp(cv_playername.string, player_names[consoleplayer])
&& cv_playercolor.value == players[consoleplayer].skincolor
&& !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name))
2014-03-15 09:59:03 -07:00
return;
2014-03-18 10:56:54 -07:00
// We'll handle it later if we're not playing.
if (!Playing())
return;
2014-03-15 09:59:03 -07:00
// If you're not in a netgame, merely update the skin, color, and name.
if (!netgame)
2014-03-15 09:59:03 -07:00
{
INT32 foundskin;
CleanupPlayerName(consoleplayer, cv_playername.zstring);
strcpy(player_names[consoleplayer], cv_playername.zstring);
players[consoleplayer].skincolor = cv_playercolor.value;
if (players[consoleplayer].mo)
players[consoleplayer].mo->color = players[consoleplayer].skincolor;
if (metalrecording)
{ // Metal Sonic is Sonic, obviously.
SetPlayerSkinByNum(consoleplayer, 0);
CV_StealthSet(&cv_skin, skins[0].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1)
{
//boolean notsame;
2014-03-15 09:59:03 -07:00
cv_skin.value = foundskin;
//notsame = (cv_skin.value != players[consoleplayer].skin);
2014-03-15 09:59:03 -07:00
SetPlayerSkin(consoleplayer, cv_skin.string);
CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
// SRB2Kart
/*if (notsame)
2014-03-15 09:59:03 -07:00
{
CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
2016-08-11 18:42:11 -07:00
players[consoleplayer].skincolor = (cv_playercolor.value&0x3F) % MAXSKINCOLORS;
2014-03-15 09:59:03 -07:00
if (players[consoleplayer].mo)
players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor;
}*/
2014-03-15 09:59:03 -07:00
}
2016-07-05 21:09:17 -07:00
else
{
cv_skin.value = players[consoleplayer].skin;
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
// will always be same as current
SetPlayerSkin(consoleplayer, cv_skin.string);
}
2014-03-15 09:59:03 -07:00
return;
}
snacpending++;
// Don't change name if muted
2017-11-10 21:34:37 -08:00
if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
else // Cleanup name if changing it
CleanupPlayerName(consoleplayer, cv_playername.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(consoleplayer))
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
// check if player has the skin loaded (cv_skin may have
// the name of a skin that was available in the previous game)
cv_skin.value = R_SkinAvailable(cv_skin.string);
if (cv_skin.value < 0)
{
CV_StealthSet(&cv_skin, DEFAULTSKIN);
cv_skin.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor.value);
WRITEUINT8(p, (UINT8)cv_skin.value);
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
}
// splitscreen
static void SendNameAndColor2(void)
{
INT32 secondplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
2014-03-15 09:59:03 -07:00
if (splitscreen < 1 && !botingame)
2014-03-15 09:59:03 -07:00
return; // can happen if skin2/color2/name2 changed
if (displayplayers[1] != consoleplayer)
secondplaya = displayplayers[1];
else if (!netgame) // HACK
2014-03-15 09:59:03 -07:00
secondplaya = 1;
if (secondplaya == -1)
return;
p = buf;
2014-03-15 09:59:03 -07:00
// normal player colors
if (G_GametypeHasTeams())
{
if (players[secondplaya].ctfteam == 1 && cv_playercolor2.value != skincolor_redteam)
CV_StealthSetValue(&cv_playercolor2, skincolor_redteam);
else if (players[secondplaya].ctfteam == 2 && cv_playercolor2.value != skincolor_blueteam)
CV_StealthSetValue(&cv_playercolor2, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor2.value)
{
if (players[secondplaya].skincolor)
CV_StealthSetValue(&cv_playercolor2, players[secondplaya].skincolor);
else if (skins[players[secondplaya].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue);
}
2014-03-18 10:56:54 -07:00
// We'll handle it later if we're not playing.
if (!Playing())
return;
2014-03-15 09:59:03 -07:00
// If you're not in a netgame, merely update the skin, color, and name.
if (botingame)
{
players[secondplaya].skincolor = botcolor;
if (players[secondplaya].mo)
players[secondplaya].mo->color = players[secondplaya].skincolor;
SetPlayerSkinByNum(secondplaya, botskin-1);
return;
}
else if (!netgame)
2014-03-15 09:59:03 -07:00
{
INT32 foundskin;
CleanupPlayerName(secondplaya, cv_playername2.zstring);
strcpy(player_names[secondplaya], cv_playername2.zstring);
// don't use displayplayers[1]: the second player must be 1
2014-03-15 09:59:03 -07:00
players[secondplaya].skincolor = cv_playercolor2.value;
if (players[secondplaya].mo)
players[secondplaya].mo->color = players[secondplaya].skincolor;
if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1)
2014-03-15 09:59:03 -07:00
{
//boolean notsame;
2014-03-15 09:59:03 -07:00
cv_skin2.value = foundskin;
//notsame = (cv_skin2.value != players[secondplaya].skin);
2014-03-15 09:59:03 -07:00
SetPlayerSkin(secondplaya, cv_skin2.string);
// SRB2Kart
/*if (notsame)
2014-03-15 09:59:03 -07:00
{
CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
2016-08-11 18:42:11 -07:00
players[secondplaya].skincolor = (cv_playercolor2.value&0x3F) % MAXSKINCOLORS;
2014-03-15 09:59:03 -07:00
if (players[secondplaya].mo)
players[secondplaya].mo->color = players[secondplaya].skincolor;
}*/
2014-03-15 09:59:03 -07:00
}
2016-07-05 21:09:17 -07:00
else
{
cv_skin2.value = players[secondplaya].skin;
CV_StealthSet(&cv_skin2, skins[players[secondplaya].skin].name);
// will always be same as current
SetPlayerSkin(secondplaya, cv_skin2.string);
}
2014-03-15 09:59:03 -07:00
return;
}
snac2pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[1])))
CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]);
else // Cleanup name if changing it
CleanupPlayerName(displayplayers[1], cv_playername2.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(displayplayers[1]))
CV_StealthSet(&cv_skin2, skins[players[displayplayers[1]].skin].name);
// check if player has the skin loaded (cv_skin2 may have
// the name of a skin that was available in the previous game)
cv_skin2.value = R_SkinAvailable(cv_skin2.string);
if (cv_skin2.value < 0)
{
CV_StealthSet(&cv_skin2, DEFAULTSKIN);
cv_skin2.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername2.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor2.value);
WRITEUINT8(p, (UINT8)cv_skin2.value);
SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf);
2014-03-15 09:59:03 -07:00
}
2017-12-07 23:29:01 -08:00
static void SendNameAndColor3(void)
{
INT32 thirdplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
2017-12-07 23:29:01 -08:00
if (splitscreen < 2)
2017-12-16 22:21:24 -08:00
return; // can happen if skin3/color3/name3 changed
2017-12-07 23:29:01 -08:00
if (displayplayers[2] != consoleplayer)
thirdplaya = displayplayers[2];
else if (!netgame) // HACK
2017-12-07 23:29:01 -08:00
thirdplaya = 2;
if (thirdplaya == -1)
return;
p = buf;
2017-12-07 23:29:01 -08:00
// normal player colors
if (G_GametypeHasTeams())
{
if (players[thirdplaya].ctfteam == 1 && cv_playercolor3.value != skincolor_redteam)
CV_StealthSetValue(&cv_playercolor3, skincolor_redteam);
else if (players[thirdplaya].ctfteam == 2 && cv_playercolor3.value != skincolor_blueteam)
CV_StealthSetValue(&cv_playercolor3, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor3.value)
{
if (players[thirdplaya].skincolor)
CV_StealthSetValue(&cv_playercolor3, players[thirdplaya].skincolor);
else if (skins[players[thirdplaya].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor3, skins[players[thirdplaya].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor3, cv_playercolor3.defaultvalue);
}
// We'll handle it later if we're not playing.
if (!Playing())
return;
// If you're not in a netgame, merely update the skin, color, and name.
if (!netgame)
2017-12-07 23:29:01 -08:00
{
INT32 foundskin;
CleanupPlayerName(thirdplaya, cv_playername3.zstring);
strcpy(player_names[thirdplaya], cv_playername3.zstring);
// don't use displayplayers[2]: the third player must be 2
2017-12-07 23:29:01 -08:00
players[thirdplaya].skincolor = cv_playercolor3.value;
if (players[thirdplaya].mo)
players[thirdplaya].mo->color = players[thirdplaya].skincolor;
if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1)
2017-12-07 23:29:01 -08:00
{
//boolean notsame;
2017-12-07 23:29:01 -08:00
2017-12-16 22:21:24 -08:00
cv_skin3.value = foundskin;
2017-12-07 23:29:01 -08:00
//notsame = (cv_skin3.value != players[thirdplaya].skin);
2017-12-07 23:29:01 -08:00
SetPlayerSkin(thirdplaya, cv_skin3.string);
// SRB2Kart
/*if (notsame)
2017-12-07 23:29:01 -08:00
{
CV_StealthSetValue(&cv_playercolor3, skins[players[thirdplaya].skin].prefcolor);
players[thirdplaya].skincolor = (cv_playercolor3.value&0x3F) % MAXSKINCOLORS;
if (players[thirdplaya].mo)
players[thirdplaya].mo->color = players[thirdplaya].skincolor;
}*/
2017-12-07 23:29:01 -08:00
}
else
{
cv_skin3.value = players[thirdplaya].skin;
CV_StealthSet(&cv_skin3, skins[players[thirdplaya].skin].name);
// will always be same as current
SetPlayerSkin(thirdplaya, cv_skin3.string);
}
return;
}
snac3pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[2])))
CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]);
else // Cleanup name if changing it
CleanupPlayerName(displayplayers[2], cv_playername3.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(displayplayers[2]))
CV_StealthSet(&cv_skin3, skins[players[displayplayers[2]].skin].name);
// check if player has the skin loaded (cv_skin3 may have
// the name of a skin that was available in the previous game)
cv_skin3.value = R_SkinAvailable(cv_skin3.string);
if (cv_skin3.value < 0)
{
CV_StealthSet(&cv_skin3, DEFAULTSKIN);
cv_skin3.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername3.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor3.value);
WRITEUINT8(p, (UINT8)cv_skin3.value);
SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf);
2017-12-07 23:29:01 -08:00
}
static void SendNameAndColor4(void)
{
INT32 fourthplaya = -1;
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
2017-12-07 23:29:01 -08:00
if (splitscreen < 3)
2017-12-07 23:29:01 -08:00
return; // can happen if skin4/color4/name4 changed
if (displayplayers[3] != consoleplayer)
fourthplaya = displayplayers[3];
else if (!netgame) // HACK
2017-12-07 23:29:01 -08:00
fourthplaya = 3;
if (fourthplaya == -1)
return;
p = buf;
2017-12-07 23:29:01 -08:00
// normal player colors
if (G_GametypeHasTeams())
{
if (players[fourthplaya].ctfteam == 1 && cv_playercolor4.value != skincolor_redteam)
CV_StealthSetValue(&cv_playercolor4, skincolor_redteam);
else if (players[fourthplaya].ctfteam == 2 && cv_playercolor4.value != skincolor_blueteam)
CV_StealthSetValue(&cv_playercolor4, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor4.value)
{
if (players[fourthplaya].skincolor)
CV_StealthSetValue(&cv_playercolor4, players[fourthplaya].skincolor);
else if (skins[players[fourthplaya].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor4, skins[players[fourthplaya].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor4, cv_playercolor4.defaultvalue);
}
// We'll handle it later if we're not playing.
if (!Playing())
return;
// If you're not in a netgame, merely update the skin, color, and name.
if (botingame)
{
players[fourthplaya].skincolor = botcolor;
if (players[fourthplaya].mo)
players[fourthplaya].mo->color = players[fourthplaya].skincolor;
SetPlayerSkinByNum(fourthplaya, botskin-1);
return;
}
else if (!netgame)
2017-12-07 23:29:01 -08:00
{
INT32 foundskin;
CleanupPlayerName(fourthplaya, cv_playername4.zstring);
strcpy(player_names[fourthplaya], cv_playername4.zstring);
// don't use displayplayers[3]: the second player must be 4
2017-12-07 23:29:01 -08:00
players[fourthplaya].skincolor = cv_playercolor4.value;
if (players[fourthplaya].mo)
players[fourthplaya].mo->color = players[fourthplaya].skincolor;
if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1)
2017-12-07 23:29:01 -08:00
{
//boolean notsame;
2017-12-07 23:29:01 -08:00
cv_skin4.value = foundskin;
//notsame = (cv_skin4.value != players[fourthplaya].skin);
2017-12-07 23:29:01 -08:00
SetPlayerSkin(fourthplaya, cv_skin4.string);
// SRB2Kart
/*if (notsame)
2017-12-07 23:29:01 -08:00
{
CV_StealthSetValue(&cv_playercolor4, skins[players[fourthplaya].skin].prefcolor);
players[fourthplaya].skincolor = (cv_playercolor4.value&0x3F) % MAXSKINCOLORS;
if (players[fourthplaya].mo)
players[fourthplaya].mo->color = players[fourthplaya].skincolor;
}*/
2017-12-07 23:29:01 -08:00
}
else
{
cv_skin4.value = players[fourthplaya].skin;
CV_StealthSet(&cv_skin4, skins[players[fourthplaya].skin].name);
// will always be same as current
SetPlayerSkin(fourthplaya, cv_skin4.string);
}
return;
}
snac4pending++;
// Don't change name if muted
if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[3])))
CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]);
else // Cleanup name if changing it
CleanupPlayerName(displayplayers[3], cv_playername4.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(displayplayers[3]))
CV_StealthSet(&cv_skin4, skins[players[displayplayers[3]].skin].name);
// check if player has the skin loaded (cv_skin4 may have
// the name of a skin that was available in the previous game)
cv_skin4.value = R_SkinAvailable(cv_skin4.string);
if (cv_skin4.value < 0)
{
CV_StealthSet(&cv_skin4, DEFAULTSKIN);
cv_skin4.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername4.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor4.value);
WRITEUINT8(p, (UINT8)cv_skin4.value);
SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf);
2017-12-07 23:29:01 -08:00
}
2014-03-15 09:59:03 -07:00
static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
{
player_t *p = &players[playernum];
char name[MAXPLAYERNAME+1];
UINT8 color, skin;
#ifdef PARANOIA
if (playernum < 0 || playernum > MAXPLAYERS)
I_Error("There is no player %d!", playernum);
#endif
if (playernum == consoleplayer)
snacpending--; // TODO: make snacpending an array instead of 4 separate vars?
else if (playernum == displayplayers[1])
2014-03-15 09:59:03 -07:00
snac2pending--;
else if (playernum == displayplayers[2])
2017-12-16 22:21:24 -08:00
snac3pending--;
else if (playernum == displayplayers[3])
2017-12-16 22:21:24 -08:00
snac4pending--;
2014-03-15 09:59:03 -07:00
#ifdef PARANOIA
2017-12-16 22:21:24 -08:00
if (snacpending < 0 || snac2pending < 0 || snac3pending < 0 || snac4pending < 0)
2014-03-15 09:59:03 -07:00
I_Error("snacpending negative!");
#endif
READSTRINGN(*cp, name, MAXPLAYERNAME);
color = READUINT8(*cp);
skin = READUINT8(*cp);
// set name
if (strcasecmp(player_names[playernum], name) != 0)
SetPlayerName(playernum, name);
// set color
p->skincolor = color % MAXSKINCOLORS;
if (p->mo)
p->mo->color = (UINT8)p->skincolor;
demo_extradata[playernum] |= DXD_COLOR;
2014-03-15 09:59:03 -07:00
// normal player colors
if (server && (p != &players[consoleplayer] && p != &players[displayplayers[1]]
&& p != &players[displayplayers[2]] && p != &players[displayplayers[3]]))
2014-03-15 09:59:03 -07:00
{
boolean kick = false;
// team colors
if (G_GametypeHasTeams())
{
if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
kick = true;
else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
kick = true;
}
// don't allow color "none"
if (!p->skincolor)
kick = true;
if (kick)
{
XBOXSTATIC UINT8 buf[2];
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor);
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
return;
}
}
// set skin
if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
{
const INT32 forcedskin = cv_forceskin.value;
2014-03-24 19:17:59 -07:00
SetPlayerSkinByNum(playernum, forcedskin);
2014-03-15 09:59:03 -07:00
2014-03-24 19:17:59 -07:00
if (playernum == consoleplayer)
CV_StealthSet(&cv_skin, skins[forcedskin].name);
else if (playernum == displayplayers[1])
2014-03-24 19:17:59 -07:00
CV_StealthSet(&cv_skin2, skins[forcedskin].name);
else if (playernum == displayplayers[2])
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_skin3, skins[forcedskin].name);
else if (playernum == displayplayers[3])
2017-12-16 22:21:24 -08:00
CV_StealthSet(&cv_skin4, skins[forcedskin].name);
2014-03-15 09:59:03 -07:00
}
else
SetPlayerSkinByNum(playernum, skin);
}
void SendWeaponPref(void)
{
XBOXSTATIC UINT8 buf[1];
buf[0] = 0;
if (cv_flipcam.value)
2014-03-15 09:59:03 -07:00
buf[0] |= 1;
if (cv_analog.value)
2014-11-11 16:55:07 -08:00
buf[0] |= 2;
2014-03-15 09:59:03 -07:00
SendNetXCmd(XD_WEAPONPREF, buf, 1);
2014-11-11 16:55:07 -08:00
}
2014-03-15 09:59:03 -07:00
2014-11-11 16:55:07 -08:00
void SendWeaponPref2(void)
{
XBOXSTATIC UINT8 buf[1];
buf[0] = 0;
if (cv_flipcam2.value)
2014-11-11 16:55:07 -08:00
buf[0] |= 1;
if (cv_analog2.value)
2014-11-11 16:55:07 -08:00
buf[0] |= 2;
SendNetXCmd2(XD_WEAPONPREF, buf, 1);
2014-03-15 09:59:03 -07:00
}
2017-12-10 22:12:38 -08:00
void SendWeaponPref3(void)
{
XBOXSTATIC UINT8 buf[1];
buf[0] = 0;
if (cv_flipcam3.value)
2017-12-10 22:12:38 -08:00
buf[0] |= 1;
if (cv_analog3.value)
2017-12-10 22:12:38 -08:00
buf[0] |= 2;
SendNetXCmd3(XD_WEAPONPREF, buf, 1);
2017-12-10 22:12:38 -08:00
}
void SendWeaponPref4(void)
{
XBOXSTATIC UINT8 buf[1];
buf[0] = 0;
if (cv_flipcam4.value)
2017-12-10 22:12:38 -08:00
buf[0] |= 1;
if (cv_analog4.value)
2017-12-10 22:12:38 -08:00
buf[0] |= 2;
SendNetXCmd4(XD_WEAPONPREF, buf, 1);
2017-12-10 22:12:38 -08:00
}
2014-03-15 09:59:03 -07:00
static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
{
UINT8 prefs = READUINT8(*cp);
2014-11-11 16:55:07 -08:00
players[playernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE);
2014-03-15 09:59:03 -07:00
if (prefs & 1)
players[playernum].pflags |= PF_FLIPCAM;
2014-11-11 16:55:07 -08:00
if (prefs & 2)
players[playernum].pflags |= PF_ANALOGMODE;
2014-03-15 09:59:03 -07:00
}
void D_SendPlayerConfig(void)
{
SendNameAndColor();
if (splitscreen || botingame)
2014-03-15 09:59:03 -07:00
SendNameAndColor2();
if (splitscreen > 1)
2017-12-10 22:12:38 -08:00
SendNameAndColor3();
if (splitscreen > 2)
2017-12-10 22:12:38 -08:00
SendNameAndColor4();
2014-03-15 09:59:03 -07:00
SendWeaponPref();
if (splitscreen)
2014-11-11 16:55:07 -08:00
SendWeaponPref2();
if (splitscreen > 1)
2017-12-10 22:12:38 -08:00
SendWeaponPref3();
if (splitscreen > 2)
2017-12-10 22:12:38 -08:00
SendWeaponPref4();
2014-03-15 09:59:03 -07:00
}
// Only works for displayplayer, sorry!
static void Command_ResetCamera_f(void)
{
P_ResetCamera(&players[displayplayers[0]], &camera[0]);
2014-03-15 09:59:03 -07:00
}
/* Consider replacing nametonum with this */
static INT32 LookupPlayer(const char *s)
{
INT32 playernum;
if (*s == '0')/* clever way to bypass atoi */
return 0;
if (( playernum = atoi(s) ))
{
playernum = max(min(playernum, MAXPLAYERS-1), 0);/* not out of range */
return playernum;
}
for (playernum = 0; playernum < MAXPLAYERS; ++playernum)
{
/* Match name case-insensitively: fully, or partially the start. */
if (playeringame[playernum])
2019-02-21 22:54:09 -08:00
if (strnicmp(player_names[playernum], s, strlen(s)) == 0)
{
return playernum;
}
}
return -1;
}
static INT32 FindPlayerByPlace(INT32 place)
{
INT32 playernum;
for (playernum = 0; playernum < MAXPLAYERS; ++playernum)
if (playeringame[playernum])
{
if (players[playernum].kartstuff[k_position] == place)
{
return playernum;
}
}
return -1;
}
//
// GetViewablePlayerPlaceRange
// Return in first and last, that player available to view, sorted by placement
// in the race.
//
static void GetViewablePlayerPlaceRange(INT32 *first, INT32 *last)
{
INT32 i;
INT32 place;
(*first) = MAXPLAYERS;
(*last) = 0;
for (i = 0; i < MAXPLAYERS; ++i)
if (G_CouldView(i))
{
place = players[i].kartstuff[k_position];
if (place < (*first))
(*first) = place;
if (place > (*last))
(*last) = place;
}
}
#define PRINTVIEWPOINT( pre,suf ) \
CONS_Printf(pre"viewing \x84(%d) \x83%s\x80"suf".\n",\
(*displayplayerp), player_names[(*displayplayerp)]);
static void Command_View_f(void)
{
INT32 *displayplayerp;
INT32 olddisplayplayer;
int viewnum;
const char *playerparam;
INT32 placenum;
INT32 playernum;
INT32 firstplace, lastplace;
char c;
/* easy peasy */
c = COM_Argv(0)[strlen(COM_Argv(0))-1];/* may be digit */
switch (c)
{
case '2': viewnum = 2; break;
case '3': viewnum = 3; break;
case '4': viewnum = 4; break;
default: viewnum = 1;
}
2019-02-04 20:48:48 -08:00
if (viewnum > 1 && !( multiplayer && demo.playback ))
2019-02-04 20:48:48 -08:00
{
CONS_Alert(CONS_NOTICE,
"You must be viewing a multiplayer replay to use this.\n");
2019-02-04 20:48:48 -08:00
return;
}
displayplayerp = &displayplayers[viewnum];
if (COM_Argc() > 1)/* switch to player */
{
playerparam = COM_Argv(1);
if (playerparam[0] == '#')/* search by placement */
{
placenum = atoi(&playerparam[1]);
playernum = FindPlayerByPlace(placenum);
if (playernum == -1 || !G_CouldView(playernum))
{
GetViewablePlayerPlaceRange(&firstplace, &lastplace);
if (playernum == -1)
{
CONS_Alert(CONS_WARNING, "There is no player in that place! ");
}
else
{
CONS_Alert(CONS_WARNING,
"That player cannot be viewed currently! "
"The first player that you can view is \x82#%d\x80; ",
firstplace);
}
CONS_Printf("Last place is \x82#%d\x80.\n", lastplace);
return;
}
}
else
{
if (( playernum = LookupPlayer(COM_Argv(1)) ) == -1)
{
CONS_Alert(CONS_WARNING, "There is no player by that name!\n");
return;
}
if (!playeringame[playernum])
{
CONS_Alert(CONS_WARNING, "There is no player using that slot!\n");
return;
}
}
olddisplayplayer = (*displayplayerp);
2019-02-17 08:03:34 -08:00
G_ResetView(viewnum, playernum, false);
/* The player we wanted was corrected to who it already was. */
if ((*displayplayerp) == olddisplayplayer)
return;
2019-02-04 20:29:52 -08:00
if ((*displayplayerp) != playernum)/* differ parameter */
{
/* skipped some */
CONS_Alert(CONS_NOTICE, "That player cannot be viewed currently.\n");
PRINTVIEWPOINT ("Now "," instead")
}
else
PRINTVIEWPOINT ("Now ",)
}
else/* print current view */
{
if (splitscreen < viewnum-1)/* We can't see those guys! */
return;
PRINTVIEWPOINT ("Currently ",)
}
}
#undef PRINTVIEWPOINT
2019-02-13 16:33:45 -08:00
static void Command_SetViews_f(void)
{
UINT8 splits;
UINT8 newsplits;
if (!( demo.playback && multiplayer ))
2019-02-13 16:33:45 -08:00
{
CONS_Alert(CONS_NOTICE,
"You must be viewing a multiplayer replay to use this.\n");
return;
}
if (COM_Argc() != 2)
{
CONS_Printf("setviews <views>: set the number of split screens\n");
return;
}
splits = splitscreen+1;
newsplits = atoi(COM_Argv(1));
newsplits = min(max(newsplits, 1), 4);
if (newsplits > splits)
G_AdjustView(newsplits, 0, true);
2019-02-13 16:33:45 -08:00
else
{
splitscreen = newsplits-1;
R_ExecuteSetViewSize();
}
2014-03-15 09:59:03 -07:00
}
// ========================================================================
// play a demo, add .lmp for external demos
// eg: playdemo demo1 plays the internal game demo
//
// UINT8 *demofile; // demo file buffer
static void Command_Playdemo_f(void)
{
char name[256];
2019-02-17 08:04:48 -08:00
if (COM_Argc() < 2)
2014-03-15 09:59:03 -07:00
{
2019-02-20 19:25:56 -08:00
CONS_Printf("playdemo <demoname> [-addfiles / -force]:\n");
CONS_Printf(M_GetText(
"Play back a demo file. The full path from your Kart directory must be given.\n\n"
"* With \"-addfiles\", any required files are added from a list contained within the demo file.\n"
"* With \"-force\", the demo is played even if the necessary files have not been added.\n"));
2014-03-15 09:59:03 -07:00
return;
}
if (netgame)
{
CONS_Printf(M_GetText("You can't play a demo while in a netgame.\n"));
return;
}
2014-03-23 09:00:29 -07:00
// disconnect from server here?
if (demo.playback)
2014-03-23 09:00:29 -07:00
G_StopDemo();
if (metalplayback)
G_StopMetalDemo();
2014-03-15 09:59:03 -07:00
// open the demo file
strcpy(name, COM_Argv(1));
// dont add .lmp so internal game demos can be played
CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name);
demo.loadfiles = strcmp(COM_Argv(2), "-addfiles") == 0;
demo.ignorefiles = strcmp(COM_Argv(2), "-force") == 0;
2019-02-17 08:04:48 -08:00
// Internal if no extension, external if one exists
// If external, convert the file name to a path in SRB2's home directory
if (FIL_CheckExtension(name))
2018-01-12 12:05:09 -08:00
G_DoPlayDemo(va("%s"PATHSEP"%s", srb2home, name));
else
G_DoPlayDemo(name);
2014-03-15 09:59:03 -07:00
}
static void Command_Timedemo_f(void)
{
char name[256];
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("timedemo <demoname>: time a demo\n"));
return;
}
if (netgame)
{
CONS_Printf(M_GetText("You can't play a demo while in a netgame.\n"));
return;
}
2014-03-23 09:00:29 -07:00
// disconnect from server here?
if (demo.playback)
2014-03-23 09:00:29 -07:00
G_StopDemo();
if (metalplayback)
G_StopMetalDemo();
2014-03-15 09:59:03 -07:00
// open the demo file
strcpy (name, COM_Argv(1));
// dont add .lmp so internal game demos can be played
CONS_Printf(M_GetText("Timing demo '%s'.\n"), name);
G_TimeDemo(name);
}
// stop current demo
static void Command_Stopdemo_f(void)
{
G_CheckDemoStatus();
CONS_Printf(M_GetText("Stopped demo.\n"));
}
static void Command_StartMovie_f(void)
{
M_StartMovie();
}
static void Command_StopMovie_f(void)
{
M_StopMovie();
}
INT32 mapchangepending = 0;
/** Runs a map change.
* The supplied data are assumed to be good. If provided by a user, they will
* have already been checked in Command_Map_f().
*
* Do \b NOT call this function directly from a menu! M_Responder() is called
* from within the event processing loop, and this function calls
* SV_SpawnServer(), which calls CL_ConnectToServer(), which gives you "Press
* ESC to abort", which calls I_GetKey(), which adds an event. In other words,
* 63 old events will get reexecuted, with ridiculous results. Just don't do
* it (without setting delay to 1, which is the current solution).
*
* \param mapnum Map number to change to.
* \param gametype Gametype to switch to.
* \param pencoremode Is this 'Encore Mode'?
2014-03-15 09:59:03 -07:00
* \param resetplayers 1 to reset player scores and lives and such, 0 not to.
* \param delay Determines how the function will be executed: 0 to do
* it all right now (must not be done from a menu), 1 to
* do step one and prepare step two, 2 to do step two.
* \param skipprecutscene To skip the precutscence or not?
* \sa D_GameTypeChanged, Command_Map_f
* \author Graue <graue@oceanbase.org>
*/
void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean resetplayers, INT32 delay, boolean skipprecutscene, boolean FLS)
2014-03-15 09:59:03 -07:00
{
static char buf[2+MAX_WADPATH+1+4];
static char *buf_p = buf;
// The supplied data are assumed to be good.
I_Assert(delay >= 0 && delay <= 2);
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d encoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene);
2014-03-15 09:59:03 -07:00
if (netgame || multiplayer)
FLS = false;
if (delay != 2)
{
UINT8 flags = 0;
const char *mapname = G_BuildMapName(mapnum);
I_Assert(W_CheckNumForName(mapname) != LUMPERROR);
buf_p = buf;
if (pencoremode)
2014-03-15 09:59:03 -07:00
flags |= 1;
if (!resetplayers)
flags |= 1<<1;
if (skipprecutscene)
flags |= 1<<2;
if (FLS)
flags |= 1<<3;
WRITEUINT8(buf_p, flags);
// new gametype value
WRITEUINT8(buf_p, newgametype);
WRITESTRINGN(buf_p, mapname, MAX_WADPATH);
}
if (delay == 1)
mapchangepending = 1;
else
{
mapchangepending = 0;
// spawn the server if needed
// reset players if there is a new one
2017-11-10 21:34:37 -08:00
if (!IsPlayerAdmin(consoleplayer))
{
if (SV_SpawnServer())
buf[0] &= ~(1<<1);
if (!Playing()) // you failed to start a server somehow, so cancel the map change
return;
}
2014-03-15 09:59:03 -07:00
// Kick bot from special stages
if (botskin)
{
if (G_IsSpecialStage(mapnum))
{
if (botingame)
{
//CL_RemoveSplitscreenPlayer();
botingame = false;
playeringame[1] = false;
}
}
else if (!botingame)
{
//CL_AddSplitscreenPlayer();
botingame = true;
displayplayers[1] = 1;
2014-03-15 09:59:03 -07:00
playeringame[1] = true;
players[1].bot = 1;
SendNameAndColor2();
}
}
chmappending++;
if (netgame)
WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed
SendNetXCmd(XD_MAP, buf, buf_p - buf);
}
}
void D_SetupVote(void)
{
UINT8 buf[6*2]; // five UINT16 maps (at twice the width of a UINT8), and two gametypes
UINT8 *p = buf;
INT32 i;
UINT8 secondgt = G_SometimesGetDifferentGametype();
INT16 votebuffer[3] = {-1,-1,-1};
if (cv_kartencore.value && G_RaceGametype())
WRITEUINT8(p, (gametype|0x80));
else
WRITEUINT8(p, gametype);
WRITEUINT8(p, secondgt);
secondgt &= ~0x80;
for (i = 0; i < 5; i++)
{
UINT16 m;
if (i == 2) // sometimes a different gametype
m = G_RandMap(G_TOLFlag(secondgt), prevmap, false, 0, true, votebuffer);
else if (i >= 3) // unknown-random and force-unknown MAP HELL
m = G_RandMap(G_TOLFlag(gametype), prevmap, false, (i-2), (i < 4), votebuffer);
else
m = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, true, votebuffer);
if (i < 3)
votebuffer[min(i, 2)] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error
WRITEUINT16(p, m);
}
SendNetXCmd(XD_SETUPVOTE, buf, p - buf);
}
2018-05-31 15:49:19 -07:00
void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer)
{
char buf[2];
char *p = buf;
UINT8 player = consoleplayer;
if (splitplayer > 0)
player = displayplayers[splitplayer];
WRITESINT8(p, voted);
WRITEUINT8(p, player);
SendNetXCmd(XD_MODIFYVOTE, &buf, 2);
}
void D_PickVote(void)
{
char buf[2];
char* p = buf;
SINT8 temppicks[MAXPLAYERS];
SINT8 templevels[MAXPLAYERS];
SINT8 votecompare = -1;
UINT8 numvotes = 0, key = 0;
boolean force = true;
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
if (votes[i] != -1)
{
temppicks[numvotes] = i;
templevels[numvotes] = votes[i];
numvotes++;
if (votecompare == -1)
votecompare = votes[i];
else if (votes[i] != votecompare)
force = false;
}
}
2018-02-08 15:14:56 -08:00
key = M_RandomKey(numvotes);
if (numvotes > 0)
{
WRITESINT8(p, temppicks[key]);
if (force && templevels[key] == 3 && numvotes > 1)
WRITESINT8(p, 4);
else
WRITESINT8(p, templevels[key]);
}
else
{
WRITESINT8(p, -1);
WRITESINT8(p, 0);
}
SendNetXCmd(XD_PICKVOTE, &buf, 2);
}
2014-03-15 09:59:03 -07:00
// Warp to map code.
// Called either from map <mapname> console command, or idclev cheat.
//
static void Command_Map_f(void)
{
const char *mapname;
size_t i;
INT32 newmapnum;
boolean newresetplayers, newencoremode;
2014-03-15 09:59:03 -07:00
INT32 newgametype = gametype;
// max length of command: map map03 -gametype race -noresetplayers -force -encore
// 1 2 3 4 5 6 7
2014-03-15 09:59:03 -07:00
// = 8 arg max
// i don't know whether this is intrinsic to the system or just someone being weird but
// "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit
2014-03-15 09:59:03 -07:00
if (COM_Argc() < 2 || COM_Argc() > 8)
{
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n"));
return;
}
2017-11-10 21:34:37 -08:00
if (client && !IsPlayerAdmin(consoleplayer))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
// internal wad lump always: map command doesn't support external files as in doom legacy
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR)
{
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1));
return;
}
if (!(netgame || multiplayer) && !majormods)
2014-03-15 09:59:03 -07:00
{
if (COM_CheckParm("-force"))
{
G_SetGameModified(false, true);
}
2014-03-15 09:59:03 -07:00
else
{
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return;
}
}
newresetplayers = !COM_CheckParm("-noresetplayers");
if (!newresetplayers && !cv_debug)
{
CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
return;
}
mapname = COM_Argv(1);
if (strlen(mapname) != 5
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
{
CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname);
return;
}
// new gametype value
// use current one by default
i = COM_CheckParm("-gametype");
if (i)
{
if (!multiplayer)
{
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n"));
return;
}
newgametype = G_GetGametypeByName(COM_Argv(i+1));
if (newgametype == -1) // reached end of the list with no match
2014-03-15 09:59:03 -07:00
{
INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too
if (j >= 0 && j < NUMGAMETYPES)
newgametype = (INT16)j;
2014-03-15 09:59:03 -07:00
}
}
// new encoremode value
// use cvar by default
newencoremode = (boolean)cv_kartencore.value;
if (COM_CheckParm("-encore"))
{
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
newencoremode = !newencoremode;
}
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
newresetplayers = false; // if not forcing and gametypes is the same
2014-03-15 09:59:03 -07:00
// don't use a gametype the map doesn't support
if (cv_debug || i || cv_skipmapcheck.value)
2014-03-15 09:59:03 -07:00
; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway.
2019-05-07 00:06:59 -07:00
else if (!mapheaderinfo[newmapnum-1]
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
2014-03-15 09:59:03 -07:00
{
char gametypestring[32] = "Single Player";
if (multiplayer)
{
if (newgametype >= 0 && newgametype < NUMGAMETYPES
&& Gametype_Names[newgametype])
strcpy(gametypestring, Gametype_Names[newgametype]);
}
2014-03-15 09:59:03 -07:00
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return;
}
// Prevent warping to locked levels
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
return;
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
2014-03-15 09:59:03 -07:00
}
/** Receives a map command and changes the map.
*
* \param cp Data buffer.
* \param playernum Player number responsible for the message. Should be
* ::serverplayer or ::adminplayer.
* \sa D_MapChange
*/
static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
{
char mapname[MAX_WADPATH+1];
UINT8 flags;
INT32 resetplayer = 1, lastgametype;
UINT8 skipprecutscene, FLS;
boolean pencoremode;
/*#ifdef HAVE_BLUA
INT16 mapnumber;
#endif*/
forceresetplayers = deferencoremode = false;
2014-03-15 09:59:03 -07:00
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal map change received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
if (chmappending)
chmappending--;
flags = READUINT8(*cp);
pencoremode = ((flags & 1) != 0);
2014-03-15 09:59:03 -07:00
resetplayer = ((flags & (1<<1)) == 0);
lastgametype = gametype;
gametype = READUINT8(*cp);
if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!G_RaceGametype())
pencoremode = false;
2014-03-15 09:59:03 -07:00
skipprecutscene = ((flags & (1<<2)) != 0);
FLS = ((flags & (1<<3)) != 0);
READSTRINGN(*cp, mapname, MAX_WADPATH);
if (netgame)
P_SetRandSeed(READUINT32(*cp));
if (!skipprecutscene)
{
DEBFILE(va("Warping to %s [resetplayer=%d lastgametype=%d gametype=%d cpnd=%d]\n",
mapname, resetplayer, lastgametype, gametype, chmappending));
CON_LogMessage(M_GetText("Speeding off to level...\n"));
2014-03-15 09:59:03 -07:00
}
if (demo.playback && !demo.timing)
2014-03-15 09:59:03 -07:00
precache = false;
if (resetplayer)
{
if (!FLS || (netgame || multiplayer))
emeralds = 0;
}
if (modeattacking) // i remember moving this here in internal fixed a heisenbug so
SetPlayerSkinByNum(0, cv_chooseskin.value-1);
/*#ifdef HAVE_BLUA
mapnumber = M_MapNumber(mapname[3], mapname[4]);
LUAh_MapChange(mapnumber);
#endif*/
2014-03-15 09:59:03 -07:00
demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING;
demo.savebutton = 0;
G_InitNew(pencoremode, mapname, resetplayer, skipprecutscene);
if (demo.playback && !demo.timing)
2014-03-15 09:59:03 -07:00
precache = true;
if (demo.timing)
2014-03-15 09:59:03 -07:00
G_DoneLevelLoad();
if (metalrecording)
G_BeginMetal();
if (demo.recording) // Okay, level loaded, character spawned and skinned,
2014-03-15 09:59:03 -07:00
G_BeginRecording(); // I AM NOW READY TO RECORD.
demo.deferstart = true;
2014-03-15 09:59:03 -07:00
}
static void Command_Pause(void)
{
XBOXSTATIC UINT8 buf[2];
UINT8 *cp = buf;
if (COM_Argc() > 1)
WRITEUINT8(cp, (char)(atoi(COM_Argv(1)) != 0));
else
WRITEUINT8(cp, (char)(!paused));
if (dedicated)
WRITEUINT8(cp, 1);
else
WRITEUINT8(cp, 0);
2017-11-10 21:34:37 -08:00
if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
if (!paused && (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS)))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("You can't pause here.\n"));
return;
}
SendNetXCmd(XD_PAUSE, &buf, 2);
}
else
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
}
static void Got_Pause(UINT8 **cp, INT32 playernum)
{
UINT8 dedicatedpause = false;
const char *playername;
2017-11-10 21:34:37 -08:00
if (netgame && !cv_pause.value && playernum != serverplayer && !IsPlayerAdmin(playernum))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal pause command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
2019-04-05 00:38:48 -07:00
if (modeattacking && !demo.playback)
2014-03-18 10:56:54 -07:00
return;
2014-03-15 09:59:03 -07:00
paused = READUINT8(*cp);
dedicatedpause = READUINT8(*cp);
if (!demo.playback)
2014-03-15 09:59:03 -07:00
{
if (netgame)
{
if (dedicatedpause)
playername = "SERVER";
else
playername = player_names[playernum];
if (paused)
CONS_Printf(M_GetText("Game paused by %s\n"), playername);
else
CONS_Printf(M_GetText("Game unpaused by %s\n"), playername);
}
if (paused)
{
if (!menuactive || netgame)
2016-07-05 21:09:17 -07:00
S_PauseAudio();
2014-03-15 09:59:03 -07:00
}
else
2016-07-05 21:09:17 -07:00
S_ResumeAudio();
2014-03-15 09:59:03 -07:00
}
}
// Command for stuck characters in netgames, griefing, etc.
static void Command_Respawn(void)
2014-03-15 09:59:03 -07:00
{
XBOXSTATIC UINT8 buf[4];
UINT8 *cp = buf;
WRITEINT32(cp, consoleplayer);
2018-01-27 20:52:01 -08:00
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
return;
}
if (players[consoleplayer].mo && !P_IsObjectOnGround(players[consoleplayer].mo)) // KART: Nice try, but no, you won't be cheesing spb anymore.
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("You must be on the floor to use this.\n"));
2014-03-15 09:59:03 -07:00
return;
}
/*if (!G_RaceGametype()) // srb2kart: not necessary, respawning makes you lose a bumper in battle, so it's not desirable to use as a way to escape a hit
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("You may only use this in co-op, race, and competition!\n"));
return;
}*/
2014-03-15 09:59:03 -07:00
// Retry is quicker. Probably should force people to use it.
// nope, this is srb2kart - a complete retry is overkill
/*if (!(netgame || multiplayer))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("You can't use this in Single Player! Use \"retry\" instead.\n"));
return;
}*/
2014-03-15 09:59:03 -07:00
SendNetXCmd(XD_RESPAWN, &buf, 4);
2014-03-15 09:59:03 -07:00
}
static void Got_Respawn(UINT8 **cp, INT32 playernum)
2014-03-15 09:59:03 -07:00
{
INT32 respawnplayer = READINT32(*cp);
2014-03-15 09:59:03 -07:00
// You can't respawn someone else. Nice try, there.
if (respawnplayer != playernum) // srb2kart: "|| (!G_RaceGametype())"
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal respawn command received from %s\n"), player_names[playernum]);
2014-03-15 09:59:03 -07:00
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
// incase the above checks were modified to allow sending a respawn on these occasions:
if (players[respawnplayer].mo && !P_IsObjectOnGround(players[respawnplayer].mo))
return;
if (players[respawnplayer].mo)
P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 10000);
2019-01-27 17:30:20 -08:00
demo_extradata[playernum] |= DXD_RESPAWN;
2014-03-15 09:59:03 -07:00
}
/** Deals with an ::XD_RANDOMSEED message in a netgame.
* These messages set the position of the random number LUT and are crucial to
* correct synchronization.
*
* Such a message should only ever come from the ::serverplayer. If it comes
* from any other player, it is ignored.
*
* \param cp Data buffer.
* \param playernum Player responsible for the message. Must be ::serverplayer.
* \author Graue <graue@oceanbase.org>
*/
static void Got_RandomSeed(UINT8 **cp, INT32 playernum)
{
UINT32 seed;
seed = READUINT32(*cp);
if (playernum != serverplayer) // it's not from the server, wtf?
return;
P_SetRandSeed(seed);
}
/** Clears all players' scores in a netgame.
* Only the server or a remote admin can use this command, for obvious reasons.
*
* \sa XD_CLEARSCORES, Got_Clearscores
* \author SSNTails <http://www.ssntails.org>
*/
static void Command_Clearscores_f(void)
{
2017-11-10 21:34:37 -08:00
if (!(server || (IsPlayerAdmin(consoleplayer))))
2014-03-15 09:59:03 -07:00
return;
SendNetXCmd(XD_CLEARSCORES, NULL, 1);
}
/** Handles an ::XD_CLEARSCORES message, which resets all players' scores in a
* netgame to zero.
*
* \param cp Data buffer.
* \param playernum Player responsible for the message. Must be ::serverplayer
* or ::adminplayer.
* \sa XD_CLEARSCORES, Command_Clearscores_f
* \author SSNTails <http://www.ssntails.org>
*/
static void Got_Clearscores(UINT8 **cp, INT32 playernum)
{
INT32 i;
(void)cp;
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal clear scores command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
for (i = 0; i < MAXPLAYERS; i++)
players[i].score = 0;
CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
}
// Team changing functions
static void Command_Teamchange_f(void)
{
changeteam_union NetPacket;
boolean error = false;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
// 0 1
// changeteam <color>
if (COM_Argc() <= 1)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "spectator or playing");
else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (G_GametypeHasTeams())
{
if (!strcasecmp(COM_Argv(1), "red") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasSpectators())
{
if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else if (!strcasecmp(COM_Argv(1), "playing") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 3;
else
error = true;
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (error)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam <team>: switch to a new team (%s)\n"), "spectator or playing");
return;
}
if (players[consoleplayer].spectator)
error = !(NetPacket.packet.newteam || (players[consoleplayer].pflags & PF_WANTSTOJOIN)); // :lancer:
else if (G_GametypeHasTeams())
error = (NetPacket.packet.newteam == (unsigned)players[consoleplayer].ctfteam);
else if (G_GametypeHasSpectators() && !players[consoleplayer].spectator)
error = (NetPacket.packet.newteam == 3);
2014-03-15 09:59:03 -07:00
#ifdef PARANOIA
else
I_Error("Invalid gametype after initial checks!");
#endif
if (error)
{
CONS_Alert(CONS_NOTICE, M_GetText("You're already on that team!\n"));
return;
}
if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return;
}
//additional check for hide and seek. Don't allow change of status after hidetime ends.
if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
{
CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
return;
}
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
}
static void Command_Teamchange2_f(void)
{
changeteam_union NetPacket;
boolean error = false;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
// 0 1
// changeteam2 <color>
if (COM_Argc() <= 1)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
2014-03-15 09:59:03 -07:00
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "spectator or playing");
2014-03-15 09:59:03 -07:00
else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (G_GametypeHasTeams())
{
if (!strcasecmp(COM_Argv(1), "red") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasSpectators())
{
if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else if (!strcasecmp(COM_Argv(1), "playing") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 3;
else
error = true;
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (error)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam2 <team>: switch to a new team (%s)\n"), "spectator or playing");
return;
}
if (players[displayplayers[1]].spectator)
error = !(NetPacket.packet.newteam || (players[displayplayers[1]].pflags & PF_WANTSTOJOIN));
else if (G_GametypeHasTeams())
error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[1]].ctfteam);
else if (G_GametypeHasSpectators() && !players[displayplayers[1]].spectator)
error = (NetPacket.packet.newteam == 3);
2014-03-15 09:59:03 -07:00
#ifdef PARANOIA
else
I_Error("Invalid gametype after initial checks!");
#endif
if (error)
{
CONS_Alert(CONS_NOTICE, M_GetText("You're already on that team!\n"));
return;
}
if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return;
}
//additional check for hide and seek. Don't allow change of status after hidetime ends.
if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
{
CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
return;
}
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd2(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
}
2017-12-10 22:12:38 -08:00
static void Command_Teamchange3_f(void)
{
changeteam_union NetPacket;
boolean error = false;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
// 0 1
// changeteam3 <color>
if (COM_Argc() <= 1)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam3 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam3 <team>: switch to a new team (%s)\n"), "spectator or playing");
else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (G_GametypeHasTeams())
{
if (!strcasecmp(COM_Argv(1), "red") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasSpectators())
{
if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else if (!strcasecmp(COM_Argv(1), "playing") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 3;
else
error = true;
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (error)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam3 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam3 <team>: switch to a new team (%s)\n"), "spectator or playing");
return;
}
if (players[displayplayers[2]].spectator)
error = !(NetPacket.packet.newteam || (players[displayplayers[2]].pflags & PF_WANTSTOJOIN));
else if (G_GametypeHasTeams())
error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[2]].ctfteam);
else if (G_GametypeHasSpectators() && !players[displayplayers[2]].spectator)
error = (NetPacket.packet.newteam == 3);
#ifdef PARANOIA
else
I_Error("Invalid gametype after initial checks!");
#endif
if (error)
{
CONS_Alert(CONS_NOTICE, M_GetText("You're already on that team!\n"));
return;
}
if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
{
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return;
}
//additional check for hide and seek. Don't allow change of status after hidetime ends.
if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
{
CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
return;
}
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd3(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
2017-12-10 22:12:38 -08:00
}
static void Command_Teamchange4_f(void)
{
changeteam_union NetPacket;
boolean error = false;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
// 0 1
// changeteam4 <color>
if (COM_Argc() <= 1)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam4 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam4 <team>: switch to a new team (%s)\n"), "spectator or playing");
else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (G_GametypeHasTeams())
{
if (!strcasecmp(COM_Argv(1), "red") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasSpectators())
{
if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
NetPacket.packet.newteam = 0;
else if (!strcasecmp(COM_Argv(1), "playing") || !strcasecmp(COM_Argv(1), "1"))
NetPacket.packet.newteam = 3;
else
error = true;
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (error)
{
if (G_GametypeHasTeams())
CONS_Printf(M_GetText("changeteam4 <team>: switch to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("changeteam4 <team>: switch to a new team (%s)\n"), "spectator or playing");
return;
}
if (players[displayplayers[3]].spectator)
error = !(NetPacket.packet.newteam || (players[displayplayers[3]].pflags & PF_WANTSTOJOIN));
else if (G_GametypeHasTeams())
error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[3]].ctfteam);
else if (G_GametypeHasSpectators() && !players[displayplayers[3]].spectator)
error = (NetPacket.packet.newteam == 3);
#ifdef PARANOIA
else
I_Error("Invalid gametype after initial checks!");
#endif
if (error)
{
CONS_Alert(CONS_NOTICE, M_GetText("You're already on that team!\n"));
return;
}
if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
{
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
return;
}
//additional check for hide and seek. Don't allow change of status after hidetime ends.
if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
{
CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
return;
}
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd4(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
2017-12-10 22:12:38 -08:00
}
2014-03-15 09:59:03 -07:00
static void Command_ServerTeamChange_f(void)
{
changeteam_union NetPacket;
boolean error = false;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
2017-11-10 21:34:37 -08:00
if (!(server || (IsPlayerAdmin(consoleplayer))))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
// 0 1 2
// serverchangeteam <playernum> <team>
if (COM_Argc() < 3)
{
if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (G_TagGametype())
{
if (!strcasecmp(COM_Argv(2), "it") || !strcasecmp(COM_Argv(2), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(2), "notit") || !strcasecmp(COM_Argv(2), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(2), "playing") || !strcasecmp(COM_Argv(2), "3"))
NetPacket.packet.newteam = 3;
else if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasTeams())
{
if (!strcasecmp(COM_Argv(2), "red") || !strcasecmp(COM_Argv(2), "1"))
NetPacket.packet.newteam = 1;
else if (!strcasecmp(COM_Argv(2), "blue") || !strcasecmp(COM_Argv(2), "2"))
NetPacket.packet.newteam = 2;
else if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
NetPacket.packet.newteam = 0;
else
error = true;
}
else if (G_GametypeHasSpectators())
{
if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
NetPacket.packet.newteam = 0;
else if (!strcasecmp(COM_Argv(2), "playing") || !strcasecmp(COM_Argv(2), "1"))
NetPacket.packet.newteam = 3;
else
error = true;
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
}
if (error)
{
if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
return;
}
NetPacket.packet.playernum = atoi(COM_Argv(1));
if (!playeringame[NetPacket.packet.playernum])
{
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), NetPacket.packet.playernum);
return;
}
if (G_TagGametype())
{
if (( (players[NetPacket.packet.playernum].pflags & PF_TAGIT) && NetPacket.packet.newteam == 1) ||
(!(players[NetPacket.packet.playernum].pflags & PF_TAGIT) && NetPacket.packet.newteam == 2) ||
( players[NetPacket.packet.playernum].spectator && !NetPacket.packet.newteam) ||
(!players[NetPacket.packet.playernum].spectator && NetPacket.packet.newteam == 3))
error = true;
}
else if (G_GametypeHasTeams())
{
if (NetPacket.packet.newteam == (unsigned)players[NetPacket.packet.playernum].ctfteam ||
(players[NetPacket.packet.playernum].spectator && !NetPacket.packet.newteam))
error = true;
}
else if (G_GametypeHasSpectators())
{
if ((players[NetPacket.packet.playernum].spectator && !NetPacket.packet.newteam) ||
(!players[NetPacket.packet.playernum].spectator && NetPacket.packet.newteam == 3))
error = true;
}
#ifdef PARANOIA
else
I_Error("Invalid gametype after initial checks!");
#endif
if (error)
{
CONS_Alert(CONS_NOTICE, M_GetText("That player is already on that team!\n"));
return;
}
//additional check for hide and seek. Don't allow change of status after hidetime ends.
if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
{
CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
return;
}
NetPacket.packet.verification = true; // This signals that it's a server change
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
}
//todo: This and the other teamchange functions are getting too long and messy. Needs cleaning.
static void Got_Teamchange(UINT8 **cp, INT32 playernum)
{
changeteam_union NetPacket;
boolean error = false;
NetPacket.value.l = NetPacket.value.b = READINT16(*cp);
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators()) //Make sure you're in the right gametype.
{
// this should never happen unless the client is hacked/buggy
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
if (NetPacket.packet.verification) // Special marker that the server sent the request
{
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
playernum = NetPacket.packet.playernum;
}
// Prevent multiple changes in one go.
if (players[playernum].spectator && !(players[playernum].pflags & PF_WANTSTOJOIN) && !NetPacket.packet.newteam)
return;
else if (G_TagGametype())
2014-03-15 09:59:03 -07:00
{
if (((players[playernum].pflags & PF_TAGIT) && NetPacket.packet.newteam == 1) ||
(!(players[playernum].pflags & PF_TAGIT) && NetPacket.packet.newteam == 2) ||
(!players[playernum].spectator && NetPacket.packet.newteam == 3))
return;
}
else if (G_GametypeHasTeams())
{
if (NetPacket.packet.newteam && (NetPacket.packet.newteam == (unsigned)players[playernum].ctfteam))
2014-03-15 09:59:03 -07:00
return;
}
else if (G_GametypeHasSpectators())
{
if (!players[playernum].spectator && NetPacket.packet.newteam == 3)
2014-03-15 09:59:03 -07:00
return;
}
else
{
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
return;
}
//Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes.
switch (gametype)
{
case GT_HIDEANDSEEK:
//no status changes after hidetime
if (leveltime >= (hidetime * TICRATE))
{
error = true;
break;
}
/* FALLTHRU */
2014-03-15 09:59:03 -07:00
case GT_TAG:
switch (NetPacket.packet.newteam)
{
case 0:
break;
case 1: case 2:
if (!NetPacket.packet.verification)
error = true; //Only admin can change player's IT status' in tag.
break;
case 3: //Join game via console.
if (!NetPacket.packet.verification && !cv_allowteamchange.value)
error = true;
break;
}
break;
default:
#ifdef PARANOIA
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators())
I_Error("Invalid gametype after initial checks!");
#endif
if (!cv_allowteamchange.value)
{
if (!NetPacket.packet.verification && NetPacket.packet.newteam)
error = true; //Only admin can change status, unless changing to spectator.
}
break; //Otherwise, you don't need special permissions.
}
if (server && ((NetPacket.packet.newteam < 0 || NetPacket.packet.newteam > 3) || error))
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
SendNetXCmd(XD_KICK, &buf, 2);
}
//Safety first!
// (not respawning spectators here...)
if (!players[playernum].spectator)
2014-03-15 09:59:03 -07:00
{
if (players[playernum].mo)
2014-03-15 09:59:03 -07:00
{
//if (!players[playernum].spectator)
P_DamageMobj(players[playernum].mo, NULL, NULL, 10000);
/*else
{
if (players[playernum].mo)
{
P_RemoveMobj(players[playernum].mo);
players[playernum].mo = NULL;
}
players[playernum].playerstate = PST_REBORN;
}*/
}
else
2014-03-15 09:59:03 -07:00
players[playernum].playerstate = PST_REBORN;
}
2018-10-31 00:06:36 -07:00
players[playernum].pflags &= ~PF_WANTSTOJOIN;
2014-03-15 09:59:03 -07:00
//Now that we've done our error checking and killed the player
//if necessary, put the player on the correct team/status.
if (G_TagGametype())
{
if (!NetPacket.packet.newteam)
{
players[playernum].spectator = true;
players[playernum].pflags &= ~PF_TAGIT;
players[playernum].pflags &= ~PF_TAGGED;
}
else if (NetPacket.packet.newteam != 3) // .newteam == 1 or 2.
{
players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false;
2014-03-15 09:59:03 -07:00
players[playernum].pflags &= ~PF_TAGGED;//Just in case.
if (NetPacket.packet.newteam == 1) //Make the player IT.
players[playernum].pflags |= PF_TAGIT;
else
players[playernum].pflags &= ~PF_TAGIT;
}
else // Just join the game.
{
players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false;
2014-03-15 09:59:03 -07:00
//If joining after hidetime in normal tag, default to being IT.
if (gametype == GT_TAG && (leveltime > (hidetime * TICRATE)))
{
NetPacket.packet.newteam = 1; //minor hack, causes the "is it" message to be printed later.
players[playernum].pflags |= PF_TAGIT; //make the player IT.
}
}
}
else if (G_GametypeHasTeams())
{
if (!NetPacket.packet.newteam)
{
players[playernum].ctfteam = 0;
players[playernum].spectator = true;
}
else
{
players[playernum].ctfteam = NetPacket.packet.newteam;
players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false;
2014-03-15 09:59:03 -07:00
}
}
else if (G_GametypeHasSpectators())
{
if (!NetPacket.packet.newteam)
players[playernum].spectator = true;
else
players[playernum].pflags |= PF_WANTSTOJOIN; //players[playernum].spectator = false;
2014-03-15 09:59:03 -07:00
}
if (NetPacket.packet.autobalance)
{
if (NetPacket.packet.newteam == 1)
CONS_Printf(M_GetText("%s was autobalanced to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
else if (NetPacket.packet.newteam == 2)
CONS_Printf(M_GetText("%s was autobalanced to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
}
else if (NetPacket.packet.scrambled)
{
if (NetPacket.packet.newteam == 1)
CONS_Printf(M_GetText("%s was scrambled to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
else if (NetPacket.packet.newteam == 2)
CONS_Printf(M_GetText("%s was scrambled to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
}
else if (NetPacket.packet.newteam == 1)
{
if (G_TagGametype())
CONS_Printf(M_GetText("%s is now IT!\n"), player_names[playernum]);
else
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
}
else if (NetPacket.packet.newteam == 2)
{
if (G_TagGametype())
CONS_Printf(M_GetText("%s is no longer IT!\n"), player_names[playernum]);
else
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
}
else if (NetPacket.packet.newteam == 0)
HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false); // "entered the game" text was moved to P_SpectatorJoinGame
2014-03-15 09:59:03 -07:00
//reset view if you are changed, or viewing someone who was changed.
if (playernum == consoleplayer || displayplayers[0] == playernum)
displayplayers[0] = consoleplayer;
2014-03-15 09:59:03 -07:00
if (G_GametypeHasTeams())
{
if (NetPacket.packet.newteam)
{
if (playernum == consoleplayer) //CTF and Team Match colors.
CV_SetValue(&cv_playercolor, NetPacket.packet.newteam + 5);
else if (playernum == displayplayers[1])
2014-03-15 09:59:03 -07:00
CV_SetValue(&cv_playercolor2, NetPacket.packet.newteam + 5);
else if (playernum == displayplayers[2])
2017-12-16 22:21:24 -08:00
CV_SetValue(&cv_playercolor3, NetPacket.packet.newteam + 5);
else if (playernum == displayplayers[3])
2017-12-16 22:21:24 -08:00
CV_SetValue(&cv_playercolor4, NetPacket.packet.newteam + 5);
2014-03-15 09:59:03 -07:00
}
}
if (gamestate != GS_LEVEL)
return;
demo_extradata[playernum] |= DXD_PLAYSTATE;
2014-03-15 09:59:03 -07:00
// Clear player score and rings if a spectator.
if (players[playernum].spectator)
{
2018-07-01 01:36:09 -07:00
if (G_BattleGametype()) // SRB2kart
{
players[playernum].marescore = 0;
2018-07-01 01:36:09 -07:00
if (K_IsPlayerWanted(&players[playernum]))
K_CalculateBattleWanted();
}
2014-03-15 09:59:03 -07:00
players[playernum].health = 1;
if (players[playernum].mo)
players[playernum].mo->health = 1;
}
// In tag, check to see if you still have a game.
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
/*if (G_TagGametype())
2014-03-15 09:59:03 -07:00
P_CheckSurvivors();
Nuke a bunch of iteration things that have no purpose in SRB2Kart. A full explanation of my reasoning and what it affects is as follows. p_inter.c - Everything to do with setting states for starposts In SRB2Kart, starposts are invisble. We don't need to loop through all thinkers just to set their states when there's no visible effect of the state-setting. In addition, it has no consequences for gameplay - starposts have long been silent here, and all checking is done regarding their health, not their state. Remove extremely low-traffic conditionals (MT_FLINGEMERALD collision height extension, for example) These objects serve no functional purpose during regular SRB2Kart gameplay. Why should every other object have to pay an admittedly minor performance hit just for them? Disable all mechanisms of damaging bosses or enemies with the player's physical contact With the exception of Sapphire Coast, no MF_ENEMY objects exist in the entirety of the standard roster. In addition, the conditions for damaging the enemies were impossible to achieve, because they required vanilla SRB2 mechanics such as "jumping", "spindashing", or "super". Therefore, they can be safely commented out. Disable NiGHTS-related material (excepting bumper, hoop, and wing-emblem objects) NiGHTS is fundamentally incompatible with regular kart gameplay and I believe was already broken. Therefore, any mechanism which enters, aids, or abets it can be safely disabled. Comment out Tag mechanisms Tag is the only vanilla multiplayer gametype which has sufficient gameplay depth and complexity (HEYOOOOOOOOO) to require dedicated thinking in and of itself in order to manage. This thinking is irrelevant to Kart's functioning, and can be neutered easily. d_clisrv.c Comment out Tag mechanisms See p_inter.c d_netcmd.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay When investigating for references to NiGHTS material, I discovered that these remained untouched. In order to present a more coherent game, I have hidden the ones that serve no purpose for us. Comment out Tag mechanisms See p_inter.c g_game.c Disable NiGHTS-related material See p_inter.c Disable some team-related material Teams are not present in SRB2Kart at present. Obviously we'd want to reconsider for future, but it doesn't need to be run right now. Everything to do with setting states for starposts See p_inter.c m_cheat.c Disable several devmode commands which are irrelevant to SRB2Kart gameplay See d_netcmd.c p_map.c Remove extremely low-traffic conditionals (MT_EGGSHIELD collision, for example) See p_inter.c Disable NiGHTS-related material See p_inter.c p_mobj.c Disable P_EmeraldManager Power stones, despite their relevance in vanilla Match, are not in SRB2Kart's Battle. No management of nonexistent emeralds is required. p_setup.c Everything to do with setting states for starposts See p_inter.c p_spec.c Disable NiGHTS-related material See p_inter.c Everything to do with setting states for starposts See p_inter.c p_telept.c Everything to do with setting states for starposts See p_inter.c p_tick.c Disable some team-related material See g_game.c Disable P_EmeraldManager See p_mobj.c Do not run shields Shield objects are not run under the vanilla system; the Thunder Shield is a domain-specific recreation using a standard mobjthinker. Do not run special stages SRB2Kart does not have special stages. Comment out Tag mechanisms See p_inter.c y_inter.c Disable some team-related material See g_game.c p_user.c Disable NiGHTS-related material See p_inter.c Disable 2d movement for players 2D mode? In a kart racer? :nick:
2018-10-03 09:04:41 -07:00
else*/ if (G_BattleGametype())
K_CheckBumpers(); // SRB2Kart
else if (G_RaceGametype())
2018-06-03 18:07:36 -07:00
P_CheckRacers(); // also SRB2Kart
2014-03-15 09:59:03 -07:00
}
//
// Attempts to make password system a little sane without
// rewriting the entire goddamn XD_file system
//
#include "md5.h"
static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
{
#ifdef NOMD5
(void)buffer;
(void)len;
(void)salt;
memset(dest, 0, 16);
#else
XBOXSTATIC char tmpbuf[256];
2016-07-05 21:09:17 -07:00
const size_t sl = strlen(salt);
2014-03-15 09:59:03 -07:00
2016-07-05 21:09:17 -07:00
if (len > 256-sl)
len = 256-sl;
2019-03-05 20:27:25 -08:00
2014-03-15 09:59:03 -07:00
memcpy(tmpbuf, buffer, len);
2016-07-05 21:09:17 -07:00
memmove(&tmpbuf[len], salt, sl);
//strcpy(&tmpbuf[len], salt);
2014-03-15 09:59:03 -07:00
len += strlen(salt);
if (len < 256)
memset(&tmpbuf[len],0,256-len);
// Yes, we intentionally md5 the ENTIRE buffer regardless of size...
md5_buffer(tmpbuf, 256, dest);
#endif
}
#define BASESALT "basepasswordstorage"
2019-03-09 14:46:55 -08:00
static UINT8 adminpassmd5[MD5_LEN];
2018-06-04 13:14:01 -07:00
static boolean adminpasswordset = false;
2014-03-15 09:59:03 -07:00
void D_SetPassword(const char *pw)
{
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5);
2018-06-04 13:14:01 -07:00
adminpasswordset = true;
2014-03-15 09:59:03 -07:00
}
// Remote Administration
static void Command_Changepassword_f(void)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
if (client) // cannot change remotely
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("password <password>: change remote admin password\n"));
return;
}
D_SetPassword(COM_Argv(1));
CONS_Printf(M_GetText("Password set.\n"));
#endif
}
static void Command_Login_f(void)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
2019-03-09 14:46:55 -08:00
XBOXSTATIC UINT8 finalmd5[MD5_LEN];
2014-03-15 09:59:03 -07:00
const char *pw;
if (!netgame)
{
CONS_Printf(M_GetText("This only works in a netgame.\n"));
return;
}
2014-03-15 09:59:03 -07:00
// If the server uses login, it will effectively just remove admin privileges
// from whoever has them. This is good.
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("login <password>: Administrator login\n"));
return;
}
pw = COM_Argv(1);
// Do the base pass to get what the server has (or should?)
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &finalmd5);
// Do the final pass to get the comparison the server will come up with
2019-03-09 14:46:55 -08:00
D_MD5PasswordPass(finalmd5, MD5_LEN, va("PNUM%02d", consoleplayer), &finalmd5);
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n"));
2019-03-09 14:46:55 -08:00
SendNetXCmd(XD_LOGIN, finalmd5, MD5_LEN);
2014-03-15 09:59:03 -07:00
#endif
}
static void Got_Login(UINT8 **cp, INT32 playernum)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
(void)cp;
(void)playernum;
#else
2019-03-09 14:46:55 -08:00
UINT8 sentmd5[MD5_LEN], finalmd5[MD5_LEN];
2014-03-15 09:59:03 -07:00
2019-03-09 14:46:55 -08:00
READMEM(*cp, sentmd5, MD5_LEN);
2014-03-15 09:59:03 -07:00
if (client)
2014-03-15 09:59:03 -07:00
return;
2018-06-04 13:14:01 -07:00
if (!adminpasswordset)
2018-06-04 13:30:27 -07:00
{
2018-06-04 13:14:01 -07:00
CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[playernum]);
2018-06-04 13:30:27 -07:00
return;
}
2018-06-04 13:14:01 -07:00
2014-03-15 09:59:03 -07:00
// Do the final pass to compare with the sent md5
2019-03-09 14:46:55 -08:00
D_MD5PasswordPass(adminpassmd5, MD5_LEN, va("PNUM%02d", playernum), &finalmd5);
2014-03-15 09:59:03 -07:00
2019-03-09 14:46:55 -08:00
if (!memcmp(sentmd5, finalmd5, MD5_LEN))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
2014-03-15 09:59:03 -07:00
}
else
CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[playernum]);
#endif
}
2017-11-10 21:34:37 -08:00
boolean IsPlayerAdmin(INT32 playernum)
{
INT32 i;
2017-12-07 20:45:39 -08:00
for (i = 0; i < MAXPLAYERS; i++)
2017-11-10 21:34:37 -08:00
if (playernum == adminplayers[i])
return true;
return false;
}
void SetAdminPlayer(INT32 playernum)
{
INT32 i;
2017-12-07 20:45:39 -08:00
for (i = 0; i < MAXPLAYERS; i++)
2017-11-10 21:34:37 -08:00
{
if (playernum == adminplayers[i])
return; // Player is already admin
if (adminplayers[i] == -1)
{
adminplayers[i] = playernum; // Set the player to a free spot
break; // End the loop now. If it keeps going, the same player might get assigned to two slots.
}
}
}
void ClearAdminPlayers(void)
{
INT32 i;
2017-12-07 20:45:39 -08:00
for (i = 0; i < MAXPLAYERS; i++)
2017-11-10 21:34:37 -08:00
adminplayers[i] = -1;
}
void RemoveAdminPlayer(INT32 playernum)
{
INT32 i;
2017-12-07 20:45:39 -08:00
for (i = 0; i < MAXPLAYERS; i++)
2017-11-10 21:34:37 -08:00
if (playernum == adminplayers[i])
adminplayers[i] = -1;
}
2014-03-15 09:59:03 -07:00
static void Command_Verify_f(void)
{
XBOXSTATIC char buf[8]; // Should be plenty
char *temp;
INT32 playernum;
if (client)
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (!netgame)
{
CONS_Printf(M_GetText("This only works in a netgame.\n"));
return;
}
2014-03-15 09:59:03 -07:00
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("promote <node>: give admin privileges to a node\n"));
2014-03-15 09:59:03 -07:00
return;
}
strlcpy(buf, COM_Argv(1), sizeof (buf));
playernum = atoi(buf);
temp = buf;
WRITEUINT8(temp, playernum);
if (playeringame[playernum])
SendNetXCmd(XD_VERIFIED, buf, 1);
}
static void Got_Verification(UINT8 **cp, INT32 playernum)
{
INT16 num = READUINT8(*cp);
if (playernum != serverplayer) // it's not from the server (hacker or bug)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal verification received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
2017-11-10 21:34:37 -08:00
SetAdminPlayer(num);
2014-03-15 09:59:03 -07:00
if (num != consoleplayer)
return;
CONS_Printf(M_GetText("You are now a server administrator.\n"));
}
2017-12-07 20:45:39 -08:00
static void Command_RemoveAdmin_f(void)
{
XBOXSTATIC char buf[8]; // Should be plenty
char *temp;
INT32 playernum;
if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("demote <node>: remove admin privileges from a node\n"));
2017-12-07 20:45:39 -08:00
return;
}
strlcpy(buf, COM_Argv(1), sizeof(buf));
playernum = atoi(buf);
temp = buf;
WRITEUINT8(temp, playernum);
if (playeringame[playernum])
SendNetXCmd(XD_DEMOTED, buf, 1);
}
static void Got_Removal(UINT8 **cp, INT32 playernum)
{
INT16 num = READUINT8(*cp);
if (playernum != serverplayer) // it's not from the server (hacker or bug)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal demotion received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
RemoveAdminPlayer(num);
if (num != consoleplayer)
return;
CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
}
2019-03-05 20:27:25 -08:00
// Join password stuff
consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
2019-03-09 13:00:48 -08:00
2019-03-05 20:27:25 -08:00
#define NUMJOINCHALLENGES 32
2019-03-09 14:46:55 -08:00
static UINT8 joinpassmd5[MD5_LEN+1];
2019-03-09 13:00:48 -08:00
boolean joinpasswordset = false;
static UINT8 joinpasschallenges[NUMJOINCHALLENGES][MD5_LEN];
static tic_t joinpasschallengeson[NUMJOINCHALLENGES];
2019-03-05 20:27:25 -08:00
boolean D_IsJoinPasswordOn(void)
{
return joinpasswordset;
}
static inline void GetChallengeAnswer(UINT8 *question, UINT8 *passwordmd5, UINT8 *answer)
{
2019-03-09 14:46:55 -08:00
D_MD5PasswordPass(question, MD5_LEN, (char *) passwordmd5, answer);
2019-03-05 20:27:25 -08:00
}
void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer)
{
2019-03-09 14:46:55 -08:00
static UINT8 passwordmd5[MD5_LEN+1];
2019-03-05 20:27:25 -08:00
2019-03-09 14:46:55 -08:00
memset(passwordmd5, 0x00, MD5_LEN+1);
2019-03-05 20:27:25 -08:00
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &passwordmd5);
GetChallengeAnswer(question, passwordmd5, answer);
}
void D_SetJoinPassword(const char *pw)
{
2019-03-09 14:46:55 -08:00
memset(joinpassmd5, 0x00, MD5_LEN+1);
2019-03-05 20:27:25 -08:00
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &joinpassmd5);
joinpasswordset = true;
}
boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer)
{
boolean passed = false;
num %= NUMJOINCHALLENGES;
//@TODO use a constant-time memcmp....
2019-03-09 14:46:55 -08:00
if (joinpasschallengeson[num] > 0 && memcmp(answer, joinpasschallenges[num], MD5_LEN) == 0)
2019-03-05 20:27:25 -08:00
passed = true;
// Wipe and reset the challenge so that it can't be tried against again, as a small measure against brute-force attacks.
memset(joinpasschallenges[num], 0x00, MD5_LEN);
joinpasschallengeson[num] = 0;
2019-03-05 20:27:25 -08:00
return passed;
}
void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question)
{
size_t i;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
(*num) = M_RandomKey(NUMJOINCHALLENGES);
if (joinpasschallengeson[(*num)] == 0)
2019-03-05 20:27:25 -08:00
break;
}
if (joinpasschallengeson[(*num)] > 0)
{
// Ugh, all challenges are (probably) taken. Let's find the oldest one and overwrite it.
tic_t oldesttic = INT32_MAX;
for (i = 0; i < NUMJOINCHALLENGES; i++)
{
if (joinpasschallengeson[i] < oldesttic)
{
(*num) = i;
oldesttic = joinpasschallengeson[i];
}
}
}
joinpasschallengeson[(*num)] = I_GetTime();
2019-03-05 20:27:25 -08:00
memset(question, 0x00, MD5_LEN);
2019-03-09 14:46:55 -08:00
for (i = 0; i < MD5_LEN; i++)
2019-03-05 20:27:25 -08:00
question[i] = M_RandomByte();
// Store the answer in memory. What was the question again?
GetChallengeAnswer(question, joinpassmd5, joinpasschallenges[(*num)]);
// This ensures that num is always non-zero and will be valid when used for the answer
if ((*num) == 0)
(*num) = NUMJOINCHALLENGES;
}
// Remote Administration
static void Command_ChangeJoinPassword_f(void)
{
#ifdef NOMD5
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
if (client) // cannot change remotely
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
}
if (COM_Argc() != 2)
{
2019-03-06 19:30:39 -08:00
CONS_Printf(M_GetText("joinpassword <password>: set a password to join the server\nUse -remove to disable the password.\n"));
2019-03-05 20:27:25 -08:00
return;
}
2019-03-06 19:30:39 -08:00
if (strcmp(COM_Argv(1), "-remove") == 0)
{
joinpasswordset = false;
CONS_Printf(M_GetText("Join password removed.\n"));
}
else
{
D_SetJoinPassword(COM_Argv(1));
CONS_Printf(M_GetText("Join password set.\n"));
}
2019-03-05 20:27:25 -08:00
#endif
}
2014-03-15 09:59:03 -07:00
static void Command_MotD_f(void)
{
size_t i, j;
char *mymotd;
if ((j = COM_Argc()) < 2)
{
CONS_Printf(M_GetText("motd <message>: Set a message that clients see upon join.\n"));
return;
}
2017-11-10 21:34:37 -08:00
if (!(server || (IsPlayerAdmin(consoleplayer))))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
mymotd = Z_Malloc(sizeof(motd), PU_STATIC, NULL);
strlcpy(mymotd, COM_Argv(1), sizeof motd);
for (i = 2; i < j; i++)
{
strlcat(mymotd, " ", sizeof motd);
strlcat(mymotd, COM_Argv(i), sizeof motd);
}
// Disallow non-printing characters and semicolons.
for (i = 0; mymotd[i] != '\0'; i++)
if (!isprint(mymotd[i]) || mymotd[i] == ';')
{
Z_Free(mymotd);
return;
}
if ((netgame || multiplayer) && client)
SendNetXCmd(XD_SETMOTD, mymotd, i); // send the actual size of the motd string, not the full buffer's size
2014-03-15 09:59:03 -07:00
else
{
strcpy(motd, mymotd);
CONS_Printf(M_GetText("Message of the day set.\n"));
}
Z_Free(mymotd);
}
static void Got_MotD_f(UINT8 **cp, INT32 playernum)
{
char *mymotd = Z_Malloc(sizeof(motd), PU_STATIC, NULL);
INT32 i;
boolean kick = false;
READSTRINGN(*cp, mymotd, sizeof(motd));
// Disallow non-printing characters and semicolons.
for (i = 0; mymotd[i] != '\0'; i++)
if (!isprint(mymotd[i]) || mymotd[i] == ';')
kick = true;
2017-11-10 21:34:37 -08:00
if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal motd change received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
Z_Free(mymotd);
return;
}
strcpy(motd, mymotd);
CONS_Printf(M_GetText("Message of the day set.\n"));
Z_Free(mymotd);
}
static void Command_RunSOC(void)
{
const char *fn;
XBOXSTATIC char buf[255];
size_t length = 0;
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("runsoc <socfile.soc> or <lumpname>: run a soc\n"));
return;
}
else
fn = COM_Argv(1);
2017-11-10 21:34:37 -08:00
if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
if (!(netgame || multiplayer))
{
if (!P_RunSOC(fn))
CONS_Printf(M_GetText("Could not find SOC.\n"));
else
G_SetGameModified(multiplayer, false);
2014-03-15 09:59:03 -07:00
return;
}
nameonly(strcpy(buf, fn));
length = strlen(buf)+1;
SendNetXCmd(XD_RUNSOC, buf, length);
}
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
{
char filename[256];
filestatus_t ncs = FS_NOTFOUND;
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal runsoc command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
READSTRINGN(*cp, filename, 255);
// Maybe add md5 support?
if (strstr(filename, ".soc") != NULL)
{
ncs = findfile(filename,NULL,true);
if (ncs != FS_FOUND)
{
Command_ExitGame_f();
if (ncs == FS_NOTFOUND)
{
CONS_Printf(M_GetText("The server tried to add %s,\nbut you don't have this file.\nYou need to find it in order\nto play on this server.\n"), filename);
M_StartMessage(va("The server added a file\n(%s)\nthat you do not have.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
else
{
CONS_Printf(M_GetText("Unknown error finding soc file (%s) the server added.\n"), filename);
M_StartMessage(va("Unknown error trying to load a file\nthat the server added\n(%s).\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
return;
}
}
P_RunSOC(filename);
G_SetGameModified(true, false);
2014-03-15 09:59:03 -07:00
}
/** Adds a pwad at runtime.
* Searches for sounds, maps, music, new images.
*/
static void Command_Addfile(void)
{
const char *fn, *p;
XBOXSTATIC char buf[256];
char *buf_p = buf;
INT32 i;
int musiconly; // W_VerifyNMUSlumps isn't boolean
2014-03-15 09:59:03 -07:00
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("addfile <wadfile.wad>: load wad file\n"));
return;
}
else
fn = COM_Argv(1);
// Disallow non-printing characters and semicolons.
for (i = 0; fn[i] != '\0'; i++)
if (!isprint(fn[i]) || fn[i] == ';')
return;
musiconly = W_VerifyNMUSlumps(fn);
if (!musiconly)
2014-03-15 09:59:03 -07:00
{
// ... But only so long as they contain nothing more then music and sprites.
2017-11-10 21:34:37 -08:00
if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
G_SetGameModified(multiplayer, false);
2014-03-15 09:59:03 -07:00
}
// Add file on your client directly if it is trivial, or you aren't in a netgame.
if (!(netgame || multiplayer) || musiconly)
2014-03-15 09:59:03 -07:00
{
P_AddWadFile(fn);
2014-03-15 09:59:03 -07:00
return;
}
p = fn+strlen(fn);
2014-03-18 10:56:54 -07:00
while(--p >= fn)
2014-03-17 05:13:16 -07:00
if (*p == '\\' || *p == '/' || *p == ':')
break;
2014-03-21 11:42:55 -07:00
++p;
2014-03-15 09:59:03 -07:00
WRITESTRINGN(buf_p,p,240);
// calculate and check md5
2014-03-15 09:59:03 -07:00
{
UINT8 md5sum[16];
#ifdef NOMD5
memset(md5sum,0,16);
#else
FILE *fhandle;
if ((fhandle = W_OpenWadFile(&fn, true)) != NULL)
2014-03-15 09:59:03 -07:00
{
tic_t t = I_GetTime();
CONS_Debug(DBG_SETUP, "Making MD5 for %s\n",fn);
md5_stream(fhandle, md5sum);
CONS_Debug(DBG_SETUP, "MD5 calc for %s took %f second\n", fn, (float)(I_GetTime() - t)/TICRATE);
fclose(fhandle);
}
else // file not found
2014-03-15 09:59:03 -07:00
return;
for (i = 0; i < numwadfiles; i++)
{
if (!memcmp(wadfiles[i]->md5sum, md5sum, 16))
{
CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), fn);
return;
}
}
2014-03-15 09:59:03 -07:00
#endif
WRITEMEM(buf_p, md5sum, 16);
}
if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file
2014-03-15 09:59:03 -07:00
SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
else
SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
}
#ifdef DELFILE
/** removes the last added pwad at runtime.
* Searches for sounds, maps, music and images to remove
*/
static void Command_Delfile(void)
{
if (gamestate == GS_LEVEL)
{
CONS_Printf(M_GetText("You must NOT be in a level to use this.\n"));
return;
}
if (netgame && !(server || adminplayer == consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
if (numwadfiles <= mainwads)
{
CONS_Printf(M_GetText("No additional WADs are loaded.\n"));
return;
}
if (!(netgame || multiplayer))
{
P_DelWadFile();
if (mainwads == numwadfiles && modifiedgame)
modifiedgame = false;
return;
}
SendNetXCmd(XD_DELFILE, NULL, 0);
}
#endif
static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
{
char filename[241];
filestatus_t ncs = FS_NOTFOUND;
UINT8 md5sum[16];
boolean kick = false;
boolean toomany = false;
2017-11-10 21:34:37 -08:00
INT32 i,j;
serverinfo_pak *dummycheck = NULL;
// Shut the compiler up.
(void)dummycheck;
2014-03-15 09:59:03 -07:00
READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16);
// Only the server processes this message.
if (client)
2014-03-15 09:59:03 -07:00
return;
// Disallow non-printing characters and semicolons.
for (i = 0; filename[i] != '\0'; i++)
if (!isprint(filename[i]) || filename[i] == ';')
kick = true;
2017-11-10 21:34:37 -08:00
if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
2014-03-15 09:59:03 -07:00
{
XBOXSTATIC UINT8 buf[2];
CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]);
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
return;
}
// See W_LoadWadFile in w_wad.c
if (numwadfiles >= MAX_WADFILES)
toomany = true;
else
ncs = findfile(filename,md5sum,true);
2014-03-15 09:59:03 -07:00
if (ncs != FS_FOUND || toomany)
2014-03-15 09:59:03 -07:00
{
char message[275];
2014-03-15 09:59:03 -07:00
if (toomany)
sprintf(message, M_GetText("Too many files loaded to add %s\n"), filename);
else if (ncs == FS_NOTFOUND)
2014-03-15 09:59:03 -07:00
sprintf(message, M_GetText("The server doesn't have %s\n"), filename);
else if (ncs == FS_MD5SUMBAD)
sprintf(message, M_GetText("Checksum mismatch on %s\n"), filename);
else
sprintf(message, M_GetText("Unknown error finding wad file (%s)\n"), filename);
CONS_Printf("%s",message);
2017-12-07 20:45:39 -08:00
for (j = 0; j < MAXPLAYERS; j++)
2017-11-10 21:34:37 -08:00
if (adminplayers[j])
COM_BufAddText(va("sayto %d %s", adminplayers[j], message));
2014-03-15 09:59:03 -07:00
return;
}
COM_BufAddText(va("addfile %s\n", filename));
}
#ifdef DELFILE
static void Got_Delfilecmd(UINT8 **cp, INT32 playernum)
{
if (playernum != serverplayer && playernum != adminplayer)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal delfile command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
(void)cp;
if (numwadfiles <= mainwads) //sanity
return;
P_DelWadFile();
if (mainwads == numwadfiles && modifiedgame)
modifiedgame = false;
}
#endif
static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
{
char filename[241];
filestatus_t ncs = FS_NOTFOUND;
UINT8 md5sum[16];
READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16);
if (playernum != serverplayer)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
ncs = findfile(filename,md5sum,true);
if (ncs != FS_FOUND || !P_AddWadFile(filename))
2014-03-15 09:59:03 -07:00
{
Command_ExitGame_f();
if (ncs == FS_FOUND)
{
CONS_Printf(M_GetText("The server tried to add %s,\nbut you have too many files added.\nRestart the game to clear loaded files\nand play on this server."), filename);
M_StartMessage(va("The server added a file \n(%s)\nbut you have too many files added.\nRestart the game to clear loaded files.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
else if (ncs == FS_NOTFOUND)
2014-03-15 09:59:03 -07:00
{
CONS_Printf(M_GetText("The server tried to add %s,\nbut you don't have this file.\nYou need to find it in order\nto play on this server."), filename);
M_StartMessage(va("The server added a file \n(%s)\nthat you do not have.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
else if (ncs == FS_MD5SUMBAD)
{
CONS_Printf(M_GetText("Checksum mismatch while loading %s.\nMake sure you have the copy of\nthis file that the server has.\n"), filename);
M_StartMessage(va("Checksum mismatch while loading \n%s.\nThe server seems to have a\ndifferent version of this file.\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
else
{
CONS_Printf(M_GetText("Unknown error finding wad file (%s) the server added.\n"), filename);
M_StartMessage(va("Unknown error trying to load a file\nthat the server added \n(%s).\n\nPress ESC\n",filename), NULL, MM_NOTHING);
}
return;
}
G_SetGameModified(true, false);
2014-03-15 09:59:03 -07:00
}
static void Command_ListWADS_f(void)
{
INT32 i = numwadfiles;
char *tempname;
CONS_Printf(M_GetText("There are %d wads loaded:\n"),numwadfiles);
2014-08-26 20:56:30 -07:00
for (i--; i >= 0; i--)
2014-03-15 09:59:03 -07:00
{
nameonly(tempname = va("%s", wadfiles[i]->filename));
2014-08-26 20:56:30 -07:00
if (!i)
CONS_Printf("\x82 IWAD\x80: %s\n", tempname);
else if (i <= mainwads)
CONS_Printf("\x82 * %.2d\x80: %s\n", i, tempname);
2014-03-15 09:59:03 -07:00
else
2014-08-26 20:56:30 -07:00
CONS_Printf(" %.2d: %s\n", i, tempname);
2014-03-15 09:59:03 -07:00
}
}
// =========================================================================
// MISC. COMMANDS
// =========================================================================
/** Prints program version.
*/
static void Command_Version_f(void)
{
2016-07-05 21:09:17 -07:00
#ifdef DEVELOP
CONS_Printf("SRB2Kart %s-%s (%s %s)\n", compbranch, comprevision, compdate, comptime);
2016-07-05 21:09:17 -07:00
#else
CONS_Printf("SRB2Kart %s (%s %s %s)\n", VERSIONSTRING, compdate, comptime, comprevision);
2016-07-05 21:09:17 -07:00
#endif
2018-12-05 08:43:33 -08:00
// Base library
2018-12-05 10:00:42 -08:00
#if defined( HAVE_SDL)
2018-12-05 08:43:33 -08:00
CONS_Printf("SDL ");
2018-12-05 10:00:42 -08:00
#elif defined(_WINDOWS)
2018-12-05 08:43:33 -08:00
CONS_Printf("DD ");
#endif
// OS
// Would be nice to use SDL_GetPlatform for this
2018-12-06 12:42:58 -08:00
#if defined (_WIN32) || defined (_WIN64)
2018-12-05 08:43:33 -08:00
CONS_Printf("Windows ");
2018-12-06 12:39:19 -08:00
#elif defined(__linux__)
2018-12-05 08:43:33 -08:00
CONS_Printf("Linux ");
2018-12-05 10:00:42 -08:00
#elif defined(MACOSX)
CONS_Printf("macOS ");
2018-12-05 10:00:42 -08:00
#elif defined(UNIXCOMMON)
2018-12-05 08:43:33 -08:00
CONS_Printf("Unix (Common) ");
#else
CONS_Printf("Other OS ");
#endif
// Bitness
2018-12-05 10:06:56 -08:00
if (sizeof(void*) == 4)
CONS_Printf("32-bit ");
else if (sizeof(void*) == 8)
CONS_Printf("64-bit ");
else // 16-bit? 128-bit?
CONS_Printf("Bits Unknown ");
2018-12-05 08:43:33 -08:00
// No ASM?
#ifdef NOASM
2018-12-05 10:08:25 -08:00
CONS_Printf("\x85" "NOASM " "\x80");
#endif
2018-12-05 08:43:33 -08:00
// Debug build
#ifdef _DEBUG
2018-12-05 10:08:25 -08:00
CONS_Printf("\x85" "DEBUG " "\x80");
2018-12-05 08:43:33 -08:00
#endif
// DEVELOP build
#ifdef DEVELOP
CONS_Printf("\x87" "DEVELOP " "\x80");
#endif
2018-12-05 08:43:33 -08:00
CONS_Printf("\n");
2014-03-15 09:59:03 -07:00
}
#ifdef UPDATE_ALERT
static void Command_ModDetails_f(void)
{
CONS_Printf(M_GetText("Mod ID: %d\nMod Version: %d\nCode Base:%d\n"), MODID, MODVERSION, CODEBASE);
}
#endif
// Returns current gametype being used.
//
static void Command_ShowGametype_f(void)
{
const char *gametypestr = NULL;
if (!(netgame || multiplayer)) // print "Single player" instead of "Race"
2016-07-05 21:09:17 -07:00
{
CONS_Printf(M_GetText("Current gametype is %s\n"), "Single Player");
2016-07-05 21:09:17 -07:00
return;
}
// get name string for current gametype
if (gametype >= 0 && gametype < NUMGAMETYPES)
gametypestr = Gametype_Names[gametype];
if (gametypestr)
CONS_Printf(M_GetText("Current gametype is %s\n"), gametypestr);
else // string for current gametype was not found above (should never happen)
CONS_Printf(M_GetText("Unknown gametype set (%d)\n"), gametype);
2014-03-15 09:59:03 -07:00
}
/** Plays the intro.
*/
static void Command_Playintro_f(void)
{
if (netgame)
return;
2018-08-28 13:08:47 -07:00
if (dirmenu)
closefilemenu(true);
2014-03-15 09:59:03 -07:00
F_StartIntro();
}
/** Quits the game immediately.
*/
FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void)
{
I_Quit();
}
void ItemFinder_OnChange(void)
{
if (!cv_itemfinder.value)
return; // it's fine.
if (!M_SecretUnlocked(SECRET_ITEMFINDER))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
else if (netgame || multiplayer)
{
CONS_Printf(M_GetText("This only works in single player.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
}
/** Deals with a pointlimit change by printing the change to the console.
* If the gametype is single player, cooperative, or race, the pointlimit is
* silently disabled again.
*
* Timelimit and pointlimit can be used at the same time.
*
* We don't check immediately for the pointlimit having been reached,
* because you would get "caught" when turning it up in the menu.
* \sa cv_pointlimit, TimeLimit_OnChange
* \author Graue <graue@oceanbase.org>
*/
static void PointLimit_OnChange(void)
{
// Don't allow pointlimit in Single Player/Co-Op/Race!
if (server && Playing() && G_RaceGametype())
2014-03-15 09:59:03 -07:00
{
if (cv_pointlimit.value)
CV_StealthSetValue(&cv_pointlimit, 0);
return;
}
if (cv_pointlimit.value)
{
CONS_Printf(M_GetText("Levels will end after %s scores %d point%s.\n"),
G_GametypeHasTeams() ? M_GetText("a team") : M_GetText("someone"),
cv_pointlimit.value,
cv_pointlimit.value > 1 ? "s" : "");
}
else if (netgame || multiplayer)
CONS_Printf(M_GetText("Point limit disabled\n"));
}
static void NumLaps_OnChange(void)
{
if (!G_RaceGametype() || (modeattacking || demo.playback))
return;
if (server && Playing()
&& (netgame || multiplayer)
&& (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE)
&& (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps))
CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps);
2014-03-15 09:59:03 -07:00
// Just don't be verbose
CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
2014-03-15 09:59:03 -07:00
}
static void NetTimeout_OnChange(void)
{
connectiontimeout = (tic_t)cv_nettimeout.value;
}
static void JoinTimeout_OnChange(void)
{
jointimeout = (tic_t)cv_jointimeout.value;
}
2014-03-15 09:59:03 -07:00
UINT32 timelimitintics = 0;
/** Deals with a timelimit change by printing the change to the console.
* If the gametype is single player, cooperative, or race, the timelimit is
* silently disabled again.
*
* Timelimit and pointlimit can be used at the same time.
*
* \sa cv_timelimit, PointLimit_OnChange
*/
static void TimeLimit_OnChange(void)
{
// Don't allow timelimit in Single Player/Co-Op/Race!
if (server && Playing() && cv_timelimit.value != 0 && G_RaceGametype())
2014-03-15 09:59:03 -07:00
{
CV_SetValue(&cv_timelimit, 0);
return;
}
if (cv_timelimit.value != 0)
{
CONS_Printf(M_GetText("Levels will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * 60 * TICRATE;
//add hidetime for tag too!
if (G_TagGametype())
timelimitintics += hidetime * TICRATE;
// Note the deliberate absence of any code preventing
// pointlimit and timelimit from being set simultaneously.
// Some people might like to use them together. It works.
}
else if (netgame || multiplayer)
CONS_Printf(M_GetText("Time limit disabled\n"));
}
/** Adjusts certain settings to match a changed gametype.
*
* \param lastgametype The gametype we were playing before now.
* \sa D_MapChange
* \author Graue <graue@oceanbase.org>
* \todo Get rid of the hardcoded stuff, ugly stuff, etc.
*/
void D_GameTypeChanged(INT32 lastgametype)
{
if (netgame)
{
const char *oldgt = NULL, *newgt = NULL;
if (lastgametype >= 0 && lastgametype < NUMGAMETYPES)
oldgt = Gametype_Names[lastgametype];
if (gametype >= 0 && lastgametype < NUMGAMETYPES)
newgt = Gametype_Names[gametype];
2014-03-15 09:59:03 -07:00
if (oldgt && newgt)
CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt);
}
// Only do the following as the server, not as remote admin.
// There will always be a server, and this only needs to be done once.
if (server && (multiplayer || netgame))
{
if (gametype == GT_COMPETITION || gametype == GT_COOP)
CV_SetValue(&cv_itemrespawn, 0);
else if (!cv_itemrespawn.changed)
CV_SetValue(&cv_itemrespawn, 1);
switch (gametype)
{
case GT_MATCH:
case GT_TEAMMATCH:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{
2017-10-22 00:06:35 -07:00
// default settings for match: no timelimit, no pointlimit
2014-03-15 09:59:03 -07:00
CV_SetValue(&cv_pointlimit, 0);
2017-10-22 00:06:35 -07:00
CV_SetValue(&cv_timelimit, 0);
2014-03-15 09:59:03 -07:00
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
case GT_TAG:
case GT_HIDEANDSEEK:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{
// default settings for tag: 5 mins, no pointlimit
// Note that tag mode also uses an alternate timing mechanism in tandem with timelimit.
CV_SetValue(&cv_timelimit, 5);
CV_SetValue(&cv_pointlimit, 0);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
case GT_CTF:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{
// default settings for CTF: no timelimit, pointlimit 5
CV_SetValue(&cv_timelimit, 0);
CV_SetValue(&cv_pointlimit, 5);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
break;
}
}
else if (!multiplayer && !netgame)
{
gametype = GT_RACE; // SRB2kart
2014-03-15 09:59:03 -07:00
// These shouldn't matter anymore
//CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue);
//CV_SetValue(&cv_itemrespawn, 0);
}
// reset timelimit and pointlimit in race/coop, prevent stupid cheats
if (server)
{
if (G_RaceGametype())
2014-03-15 09:59:03 -07:00
{
if (cv_timelimit.value)
CV_SetValue(&cv_timelimit, 0);
if (cv_pointlimit.value)
CV_SetValue(&cv_pointlimit, 0);
}
else if ((cv_pointlimit.changed || cv_timelimit.changed) && cv_pointlimit.value)
{
if (lastgametype != GT_CTF && gametype == GT_CTF)
CV_SetValue(&cv_pointlimit, cv_pointlimit.value / 500);
else if (lastgametype == GT_CTF && gametype != GT_CTF)
CV_SetValue(&cv_pointlimit, cv_pointlimit.value * 500);
}
}
// When swapping to a gametype that supports spectators,
// make everyone a spectator initially.
/*if (G_GametypeHasSpectators())
2014-03-15 09:59:03 -07:00
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
{
players[i].ctfteam = 0;
players[i].spectator = true;
}
2017-10-22 00:06:35 -07:00
}*/
2014-03-15 09:59:03 -07:00
// don't retain teams in other modes or between changes from ctf to team match.
// also, stop any and all forms of team scrambling that might otherwise take place.
if (G_GametypeHasTeams())
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
players[i].ctfteam = 0;
2017-11-10 21:34:37 -08:00
if (server || (IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
CV_StealthSetValue(&cv_teamscramble, 0);
teamscramble = 0;
}
}
}
static void Ringslinger_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_ringslinger, 0);
return;
}
if (cv_ringslinger.value) // Only if it's been turned on
G_SetGameModified(multiplayer, true);
2014-03-15 09:59:03 -07:00
}
static void Gravity_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSet(&cv_gravity, cv_gravity.defaultvalue);
return;
}
#ifndef NETGAME_GRAVITY
if(netgame)
{
CV_StealthSet(&cv_gravity, cv_gravity.defaultvalue);
return;
}
#endif
if (!CV_IsSetToDefault(&cv_gravity))
G_SetGameModified(multiplayer, true);
2014-03-15 09:59:03 -07:00
gravity = cv_gravity.value;
}
static void SoundTest_OnChange(void)
{
if (cv_soundtest.value < 0)
{
CV_SetValue(&cv_soundtest, NUMSFX-1);
return;
}
if (cv_soundtest.value >= NUMSFX)
{
CV_SetValue(&cv_soundtest, 0);
return;
}
S_StopSounds();
S_StartSound(NULL, cv_soundtest.value);
}
static void AutoBalance_OnChange(void)
{
autobalance = (INT16)cv_autobalance.value;
}
static void TeamScramble_OnChange(void)
{
INT16 i = 0, j = 0, playercount = 0;
boolean repick = true;
INT32 blue = 0, red = 0;
INT32 maxcomposition = 0;
INT16 newteam = 0;
INT32 retries = 0;
boolean success = false;
// Don't trigger outside level or intermission!
2018-01-27 20:52:01 -08:00
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
2014-03-15 09:59:03 -07:00
return;
if (!cv_teamscramble.value)
teamscramble = 0;
2017-11-10 21:34:37 -08:00
if (!G_GametypeHasTeams() && (server || IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
CV_StealthSetValue(&cv_teamscramble, 0);
return;
}
// If a team scramble is already in progress, do not allow another one to be started!
if (teamscramble)
return;
retryscramble:
// Clear related global variables. These will get used again in p_tick.c/y_inter.c as the teams are scrambled.
memset(&scrambleplayers, 0, sizeof(scrambleplayers));
memset(&scrambleteams, 0, sizeof(scrambleplayers));
scrambletotal = scramblecount = 0;
blue = red = maxcomposition = newteam = playercount = 0;
repick = true;
// Put each player's node in the array.
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].spectator)
{
scrambleplayers[playercount] = i;
playercount++;
}
}
if (playercount < 2)
{
CV_StealthSetValue(&cv_teamscramble, 0);
return; // Don't scramble one or zero players.
}
// Randomly place players on teams.
if (cv_teamscramble.value == 1)
{
maxcomposition = playercount / 2;
// Now randomly assign players to teams.
// If the teams get out of hand, assign the rest to the other team.
for (i = 0; i < playercount; i++)
{
if (repick)
2016-07-05 21:09:17 -07:00
newteam = (INT16)((M_RandomByte() % 2) + 1);
2014-03-15 09:59:03 -07:00
// One team has the most players they can get, assign the rest to the other team.
if (red == maxcomposition || blue == maxcomposition)
{
if (red == maxcomposition)
newteam = 2;
else if (blue == maxcomposition)
newteam = 1;
repick = false;
}
scrambleteams[i] = newteam;
if (newteam == 1)
red++;
else
blue++;
}
}
else if (cv_teamscramble.value == 2) // Same as before, except split teams based on current score.
{
// Now, sort the array based on points scored.
for (i = 1; i < playercount; i++)
{
for (j = i; j < playercount; j++)
{
INT16 tempplayer = 0;
if ((players[scrambleplayers[i-1]].score > players[scrambleplayers[j]].score))
{
tempplayer = scrambleplayers[i-1];
scrambleplayers[i-1] = scrambleplayers[j];
scrambleplayers[j] = tempplayer;
}
}
}
// Now assign players to teams based on score. Scramble in pairs.
// If there is an odd number, one team will end up with the unlucky slob who has no points. =(
for (i = 0; i < playercount; i++)
{
if (repick)
{
2016-07-05 21:09:17 -07:00
newteam = (INT16)((M_RandomByte() % 2) + 1);
2014-03-15 09:59:03 -07:00
repick = false;
}
else
{
// We will only randomly pick the team for the first guy.
// Otherwise, just alternate back and forth, distributing players.
if (newteam == 1)
newteam = 2;
else
newteam = 1;
}
scrambleteams[i] = newteam;
}
}
// Check to see if our random selection actually
// changed anybody. If not, we run through and try again.
for (i = 0; i < playercount; i++)
{
if (players[scrambleplayers[i]].ctfteam != scrambleteams[i])
success = true;
}
if (!success && retries < 5)
{
retries++;
goto retryscramble; //try again
}
// Display a witty message, but only during scrambles specifically triggered by an admin.
if (cv_teamscramble.value)
{
scrambletotal = playercount;
teamscramble = (INT16)cv_teamscramble.value;
if (!(gamestate == GS_INTERMISSION && cv_scrambleonchange.value))
CONS_Printf(M_GetText("Teams will be scrambled next round.\n"));
}
}
static void Hidetime_OnChange(void)
{
if (Playing() && G_TagGametype() && leveltime >= (hidetime * TICRATE))
{
// Don't allow hidetime changes after it expires.
CV_StealthSetValue(&cv_hidetime, hidetime);
return;
}
hidetime = cv_hidetime.value;
//uh oh, gotta change timelimitintics now too
if (G_TagGametype())
timelimitintics = (cv_timelimit.value * 60 * TICRATE) + (hidetime * TICRATE);
}
static void Command_Showmap_f(void)
{
if (gamestate == GS_LEVEL)
{
if (mapheaderinfo[gamemap-1]->zonttl[0] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
{
if (mapheaderinfo[gamemap-1]->actnum[0])
CONS_Printf("%s (%d): %s %s %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl, mapheaderinfo[gamemap-1]->actnum);
else
CONS_Printf("%s (%d): %s %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl);
}
2014-03-15 09:59:03 -07:00
else
{
if (mapheaderinfo[gamemap-1]->actnum[0])
CONS_Printf("%s (%d): %s %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->actnum);
else
CONS_Printf("%s (%d): %s\n", G_BuildMapName(gamemap), gamemap, mapheaderinfo[gamemap-1]->lvlttl);
}
2014-03-15 09:59:03 -07:00
}
else
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
2014-04-13 22:14:58 -07:00
}
static void Command_Mapmd5_f(void)
{
if (gamestate == GS_LEVEL)
{
INT32 i;
char md5tmp[33];
for (i = 0; i < 16; ++i)
sprintf(&md5tmp[i*2], "%02x", mapmd5[i]);
CONS_Printf("%s: %s\n", G_BuildMapName(gamemap), md5tmp);
}
else
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
2014-03-15 09:59:03 -07:00
}
static void Command_ExitLevel_f(void)
{
if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
CONS_Printf(M_GetText("This only works in a netgame.\n"));
2017-11-10 21:34:37 -08:00
else if (!(server || (IsPlayerAdmin(consoleplayer))))
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (gamestate != GS_LEVEL || demo.playback)
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
{
(void)cp;
// Ignore duplicate XD_EXITLEVEL commands.
if (gameaction == ga_completed)
return;
2017-11-10 21:34:37 -08:00
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal exitlevel command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
G_ExitLevel();
}
static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
{
INT32 i;
UINT8 gt, secondgt;
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
gt = (UINT8)READUINT8(*cp);
secondgt = (UINT8)READUINT8(*cp);
for (i = 0; i < 5; i++)
{
votelevels[i][0] = (UINT16)READUINT16(*cp);
votelevels[i][1] = gt;
if (!mapheaderinfo[votelevels[i][0]])
P_AllocMapHeader(votelevels[i][0]);
}
votelevels[2][1] = secondgt;
G_SetGamestate(GS_VOTING);
Y_StartVote();
}
static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum)
{
2018-02-22 17:04:52 -08:00
SINT8 voted = READSINT8(*cp);
UINT8 p = READUINT8(*cp);
(void)playernum;
votes[p] = voted;
}
static void Got_PickVotecmd(UINT8 **cp, INT32 playernum)
{
2018-02-22 17:04:52 -08:00
SINT8 pick = READSINT8(*cp);
SINT8 level = READSINT8(*cp);
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
Y_SetupVoteFinish(pick, level);
}
/** Prints the number of displayplayers[0].
2014-03-15 09:59:03 -07:00
*
* \todo Possibly remove this; it was useful for debugging at one point.
*/
static void Command_Displayplayer_f(void)
{
CONS_Printf(M_GetText("Displayplayer is %d\n"), displayplayers[0]);
2014-03-15 09:59:03 -07:00
}
/** Quits a game and returns to the title screen.
*
*/
void Command_ExitGame_f(void)
{
INT32 i;
D_QuitNetGame();
CL_Reset();
CV_ClearChangedFlags();
for (i = 0; i < MAXPLAYERS; i++)
CL_ClearPlayer(i);
splitscreen = 0;
2014-03-15 09:59:03 -07:00
SplitScreen_OnChange();
botingame = false;
botskin = 0;
cv_debug = 0;
emeralds = 0;
2018-08-28 13:08:47 -07:00
if (dirmenu)
closefilemenu(true);
2014-03-15 09:59:03 -07:00
if (!modeattacking)
D_StartTitle();
}
void Command_Retry_f(void)
{
2018-01-27 20:52:01 -08:00
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else if (netgame || multiplayer)
CONS_Printf(M_GetText("This only works in single player.\n"));
/*else if (!&players[consoleplayer] || players[consoleplayer].lives <= 1)
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("You can't retry without any lives remaining!\n"));
else if (G_IsSpecialStage(gamemap))
CONS_Printf(M_GetText("You can't retry special stages!\n"));*/
2014-03-15 09:59:03 -07:00
else
{
M_ClearMenus(true);
G_SetRetryFlag();
}
}
#ifdef NETGAME_DEVMODE
// Allow the use of devmode in netgames.
static void Fishcake_OnChange(void)
{
cv_debug = cv_fishcake.value;
// consvar_t's get changed to default when registered
// so don't make modifiedgame always on!
if (cv_debug)
{
G_SetGameModified(multiplayer, true);
2014-03-15 09:59:03 -07:00
}
else if (cv_debug != cv_fishcake.value)
CV_SetValue(&cv_fishcake, cv_debug);
}
#endif
/** Reports to the console whether or not the game has been modified.
*
* \todo Make it obvious, so a console command won't be necessary.
* \sa modifiedgame
* \author Graue <graue@oceanbase.org>
*/
static void Command_Isgamemodified_f(void)
{
if (majormods)
2019-01-21 13:49:39 -08:00
CONS_Printf("The game has been modified with major add-ons, so you cannot play Record Attack.\n");
else if (savemoddata)
CONS_Printf("The game has been modified with an add-on with its own save data, so you can play Record Attack and earn medals.\n");
2014-03-15 09:59:03 -07:00
else if (modifiedgame)
2019-01-21 13:49:39 -08:00
CONS_Printf("The game has been modified with only minor add-ons. You can play Record Attack, earn medals and unlock extras.\n");
2014-03-15 09:59:03 -07:00
else
2019-01-21 13:49:39 -08:00
CONS_Printf("The game has not been modified. You can play Record Attack, earn medals and unlock extras.\n");
2014-03-15 09:59:03 -07:00
}
static void Command_Cheats_f(void)
{
2014-03-21 11:42:55 -07:00
if (COM_CheckParm("off"))
{
2017-11-10 21:34:37 -08:00
if (!(server || (IsPlayerAdmin(consoleplayer))))
2015-01-01 11:50:31 -08:00
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else
CV_ResetCheatNetVars();
2014-03-21 11:42:55 -07:00
return;
}
2014-03-15 09:59:03 -07:00
if (CV_CheatsEnabled())
2014-03-21 11:42:55 -07:00
{
2014-03-15 09:59:03 -07:00
CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed -- Cheats are enabled.\n"));
2017-11-10 21:34:37 -08:00
if (server || (IsPlayerAdmin(consoleplayer)))
2015-01-01 11:50:31 -08:00
CONS_Printf(M_GetText("Type CHEATS OFF to reset all cheat variables to default.\n"));
2014-03-21 11:42:55 -07:00
}
2014-03-15 09:59:03 -07:00
else
CONS_Printf(M_GetText("No CHEAT-marked variables are changed -- Cheats are disabled.\n"));
}
#ifdef _DEBUG
static void Command_Togglemodified_f(void)
{
modifiedgame = !modifiedgame;
}
#ifdef HAVE_BLUA
extern UINT8 *save_p;
static void Command_Archivetest_f(void)
{
UINT8 *buf;
UINT32 i, wrote;
thinker_t *th;
if (gamestate != GS_LEVEL)
{
CONS_Printf("This command only works in-game, you dummy.\n");
return;
}
// assign mobjnum
i = 1;
2014-03-15 09:59:03 -07:00
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
((mobj_t *)th)->mobjnum = i++;
// allocate buffer
buf = save_p = ZZ_Alloc(1024);
// test archive
CONS_Printf("LUA_Archive...\n");
LUA_Archive();
WRITEUINT8(save_p, 0x7F);
wrote = (UINT32)(save_p-buf);
// clear Lua state, so we can really see what happens!
CONS_Printf("Clearing state!\n");
LUA_ClearExtVars();
// test unarchive
save_p = buf;
CONS_Printf("LUA_UnArchive...\n");
LUA_UnArchive();
i = READUINT8(save_p);
if (i != 0x7F || wrote != (UINT32)(save_p-buf))
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save_p-buf));
// free buffer
Z_Free(buf);
CONS_Printf("Done. No crash.\n");
}
#endif
#endif
/** Makes a change to ::cv_forceskin take effect immediately.
*
* \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin
* \author Graue <graue@oceanbase.org>
*/
static void ForceSkin_OnChange(void)
{
2014-03-24 19:17:59 -07:00
// NOT in SP, silly!
if (!(netgame || multiplayer))
return;
if (cv_forceskin.value < 0)
CONS_Printf("The server has lifted the forced skin restrictions.\n");
else
2014-03-15 09:59:03 -07:00
{
2019-03-05 16:04:15 -08:00
CONS_Printf("The server is restricting all players to skin \"%s\".\n",cv_forceskin.string);
2014-03-24 19:17:59 -07:00
ForceAllSkins(cv_forceskin.value);
2014-03-15 09:59:03 -07:00
}
}
//Allows the player's name to be changed if cv_mute is off.
static void Name_OnChange(void)
{
2017-11-10 21:34:37 -08:00
if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
2014-03-15 09:59:03 -07:00
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
}
else
SendNameAndColor();
}
static void Name2_OnChange(void)
{
if (cv_mute.value) //Secondary player can't be admin.
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]);
2014-03-15 09:59:03 -07:00
}
else
SendNameAndColor2();
}
2017-12-10 22:12:38 -08:00
static void Name3_OnChange(void)
{
if (cv_mute.value) //Third player can't be admin.
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]);
2017-12-10 22:12:38 -08:00
}
else
SendNameAndColor3();
}
static void Name4_OnChange(void)
{
if (cv_mute.value) //Secondary player can't be admin.
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]);
2017-12-10 22:12:38 -08:00
}
else
SendNameAndColor4();
}
2014-03-15 09:59:03 -07:00
/** Sends a skin change for the console player, unless that player is moving.
* \sa cv_skin, Skin2_OnChange, Color_OnChange
* \author Graue <graue@oceanbase.org>
*/
static void Skin_OnChange(void)
{
2014-08-26 20:56:30 -07:00
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
&& (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y
2014-08-26 20:56:30 -07:00
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
}
2014-03-15 09:59:03 -07:00
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
SendNameAndColor();
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
}
}
/** Sends a skin change for the secondary splitscreen player, unless that
* player is moving.
* \sa cv_skin2, Skin_OnChange, Color2_OnChange
* \author Graue <graue@oceanbase.org>
*/
static void Skin2_OnChange(void)
{
if (!Playing() || !splitscreen)
2014-08-26 20:56:30 -07:00
return; // do whatever you want
if (CanChangeSkin(displayplayers[1]) && !P_PlayerMoving(displayplayers[1]))
2014-03-15 09:59:03 -07:00
SendNameAndColor2();
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin2, skins[players[displayplayers[1]].skin].name);
2014-03-15 09:59:03 -07:00
}
}
2017-12-10 22:12:38 -08:00
static void Skin3_OnChange(void)
{
if (!Playing() || splitscreen < 2)
2017-12-10 22:12:38 -08:00
return; // do whatever you want
if (CanChangeSkin(displayplayers[2]) && !P_PlayerMoving(displayplayers[2]))
2017-12-10 22:12:38 -08:00
SendNameAndColor3();
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin3, skins[players[displayplayers[2]].skin].name);
2017-12-10 22:12:38 -08:00
}
}
static void Skin4_OnChange(void)
{
if (!Playing() || splitscreen < 3)
2017-12-10 22:12:38 -08:00
return; // do whatever you want
if (CanChangeSkin(displayplayers[3]) && !P_PlayerMoving(displayplayers[3]))
2017-12-10 22:12:38 -08:00
SendNameAndColor4();
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin4, skins[players[displayplayers[3]].skin].name);
2017-12-10 22:12:38 -08:00
}
}
2014-03-15 09:59:03 -07:00
/** Sends a color change for the console player, unless that player is moving.
* \sa cv_playercolor, Color2_OnChange, Skin_OnChange
* \author Graue <graue@oceanbase.org>
*/
static void Color_OnChange(void)
{
2014-08-26 20:56:30 -07:00
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame || modeattacking)) // In single player.
2014-08-26 20:56:30 -07:00
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
}
2014-03-15 09:59:03 -07:00
if (!P_PlayerMoving(consoleplayer))
{
2014-03-18 10:56:54 -07:00
// Color change menu scrolling fix is no longer necessary
2014-03-15 09:59:03 -07:00
SendNameAndColor();
}
2014-03-18 10:56:54 -07:00
else
{
2014-03-15 09:59:03 -07:00
CV_StealthSetValue(&cv_playercolor,
players[consoleplayer].skincolor);
}
}
/** Sends a color change for the secondary splitscreen player, unless that
* player is moving.
* \sa cv_playercolor2, Color_OnChange, Skin2_OnChange
* \author Graue <graue@oceanbase.org>
*/
static void Color2_OnChange(void)
{
if (!Playing() || !splitscreen)
2014-08-26 20:56:30 -07:00
return; // do whatever you want
if (!P_PlayerMoving(displayplayers[1]))
2014-03-15 09:59:03 -07:00
{
2014-03-18 10:56:54 -07:00
// Color change menu scrolling fix is no longer necessary
2014-03-15 09:59:03 -07:00
SendNameAndColor2();
}
2014-03-18 10:56:54 -07:00
else
{
2014-03-15 09:59:03 -07:00
CV_StealthSetValue(&cv_playercolor2,
players[displayplayers[1]].skincolor);
2017-12-10 22:12:38 -08:00
}
}
static void Color3_OnChange(void)
{
if (!Playing() || splitscreen < 2)
2017-12-10 22:12:38 -08:00
return; // do whatever you want
if (!P_PlayerMoving(displayplayers[2]))
2017-12-10 22:12:38 -08:00
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor3();
}
else
{
CV_StealthSetValue(&cv_playercolor3,
players[displayplayers[2]].skincolor);
2017-12-10 22:12:38 -08:00
}
}
static void Color4_OnChange(void)
{
if (!Playing() || splitscreen < 3)
2017-12-10 22:12:38 -08:00
return; // do whatever you want
if (!P_PlayerMoving(displayplayers[3]))
2017-12-10 22:12:38 -08:00
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor4();
}
else
{
CV_StealthSetValue(&cv_playercolor4,
players[displayplayers[3]].skincolor);
2014-03-15 09:59:03 -07:00
}
}
/** Displays the result of the chat being muted or unmuted.
* The server or remote admin should already know and be able to talk
* regardless, so this is only displayed to clients.
*
* \sa cv_mute
* \author Graue <graue@oceanbase.org>
*/
static void Mute_OnChange(void)
{
/*if (server || (IsPlayerAdmin(consoleplayer)))
return;*/
// Kinda dumb IMO, you should be able to see confirmation for having muted the chat as the host or admin.
if (leveltime <= 1)
return; // avoid having this notification put in our console / log when we boot the server.
2014-03-15 09:59:03 -07:00
if (cv_mute.value)
HU_AddChatText(M_GetText("\x82*Chat has been muted."), false);
2014-03-15 09:59:03 -07:00
else
HU_AddChatText(M_GetText("\x82*Chat is no longer muted."), false);
2014-03-15 09:59:03 -07:00
}
/** Hack to clear all changed flags after game start.
* A lot of code (written by dummies, obviously) uses COM_BufAddText() to run
* commands and change consvars, especially on game start. This is problematic
* because CV_ClearChangedFlags() needs to get called on game start \b after
* all those commands are run.
*
* Here's how it's done: the last thing in COM_BufAddText() is "dummyconsvar
* 1", so we end up here, where dummyconsvar is reset to 0 and all the changed
* flags are set to 0.
*
* \todo Fix the aforementioned code and make this hack unnecessary.
* \sa cv_dummyconsvar
* \author Graue <graue@oceanbase.org>
*/
static void DummyConsvar_OnChange(void)
{
if (cv_dummyconsvar.value == 1)
{
CV_SetValue(&cv_dummyconsvar, 0);
CV_ClearChangedFlags();
}
}
static void Command_ShowScores_f(void)
{
UINT8 i;
if (!(netgame || multiplayer))
{
CONS_Printf(M_GetText("This only works in a netgame.\n"));
return;
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
// FIXME: %lu? what's wrong with %u? ~Callum (produces warnings...)
CONS_Printf(M_GetText("%s's score is %u\n"), player_names[i], players[i].score);
}
CONS_Printf(M_GetText("The pointlimit is %d\n"), cv_pointlimit.value);
}
static void Command_ShowTime_f(void)
{
if (!(netgame || multiplayer))
{
CONS_Printf(M_GetText("This only works in a netgame.\n"));
return;
}
CONS_Printf(M_GetText("The current time is %f.\nThe timelimit is %f\n"), (double)leveltime/TICRATE, (double)timelimitintics/TICRATE);
}
// SRB2Kart: On change messages
static void BaseNumLaps_OnChange(void)
{
if (gamestate == GS_LEVEL)
{
if (cv_basenumlaps.value)
CONS_Printf(M_GetText("Number of laps will be changed to %d next round.\n"), cv_basenumlaps.value);
2018-07-27 19:21:13 -07:00
else
CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n"));
}
}
static void KartFrantic_OnChange(void)
{
if ((boolean)cv_kartfrantic.value != franticitems && gamestate == GS_LEVEL && leveltime > starttime)
CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
else
{
CONS_Printf(M_GetText("Frantic items has been turned %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off"));
franticitems = (boolean)cv_kartfrantic.value;
}
}
static void KartSpeed_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_HARDSPEED) && cv_kartspeed.value == 2)
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_kartspeed, 1);
return;
}
if (G_RaceGametype())
{
if ((UINT8)cv_kartspeed.value != gamespeed && gamestate == GS_LEVEL && leveltime > starttime)
CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string);
else
{
CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string);
gamespeed = (UINT8)cv_kartspeed.value;
}
}
}
static void KartEncore_OnChange(void)
{
if (G_RaceGametype())
{
if ((boolean)cv_kartencore.value != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/)
CONS_Printf(M_GetText("Encore Mode will be turned %s next round.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off"));
else
CONS_Printf(M_GetText("Encore Mode has been turned %s.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off"));
}
}
static void KartComeback_OnChange(void)
{
if (G_BattleGametype())
{
if ((boolean)cv_kartcomeback.value != comeback && gamestate == GS_LEVEL && leveltime > starttime)
CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
else
{
CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off"));
comeback = (boolean)cv_kartcomeback.value;
}
}
}
static void KartEliminateLast_OnChange(void)
{
if (G_RaceGametype() && cv_karteliminatelast.value)
P_CheckRacers();
}