diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c2282e2..b154e3e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,13 +17,13 @@ * `cg_bob`: If set to 0, it will disable cg_bobUp/cg_bobRoll/cg_bobPitch. **Default: 1.** * `cg_kickScale`: Controls how much the screen shakes when receiving damage. **Default: 1.0.** * `cg_muzzleflashStyle`: Alternative muzzleflash styles for player preference, or "aesthetic," or maybe just less flashing (flashes can also be disabled). **Default: 1.** - * `g_runes 2`: Enables the `droprune` command (see below) in the server. * `dmflags &4096`: Allows players to move significantly faster underwater. Mostly for demonstration. Could be fun in class based gametypes. * `g_grapple`: Gives Grappling Hook to all players. Replaces `elimination_grapple`. **Default: 0.** * `g_harvesterFromBodies`: In Harvester matches, skulls now spawn from dead bodies (a la [UT3!Greed](https://antifandom.com/unreal/wiki/Greed)) rather than a skull receptacle in the middle of the arena. Allows Harvester matches to take place in maps that don't feature a skull receptacle. **Default: 0.** * `g_ddCaptureTime` and `g_ddRespawnDelay`: New cvars for Double Domination that control the amount of holding time to score and the waiting time before a new round starts. **Default for both: 10.** * `g_weaponArena` and `g_weaponArenaWeapon`: two cvars that replace and extend `g_rockets` in order to be able to use `g_rockets` with every weapon other than rockets. * `elimination_selfdamage` now accepts four options: (Only Enemies), (Enemies and Self), (Enemies and Teammates) and (Enemies, Self and Teammates). + * `g_runes` was replaced with `g_classicMode`, a cvar that makes weapon/item replacements when enabled. Runes were locked to CTF, 1FCTF, Harvester and Overload, the only modes where their integration made sense. * Development mode with new cheat-protected cvars and debugging tools. * Shuffle has been reworked by implementing the solution from Aftershock. * New commands: @@ -31,7 +31,7 @@ * `droprune`: Tosses the rune that's been carried on. (Akin to TWCTF/TWCTF II) Needs `g_runes 2` in order to work. * `ui_writemappools`: If the arena files are loaded, this command dumps the gamelists so they can be used by g_autonextmap (should be used to generate new gamelists for new versions). * Classic UI: - * Slight reorganization of the Skirmish/Create Server menus in order to accomodate the newer gametype options. + * Reorganization of the Skirmish/Create Server menus in order to accomodate the newer gametype options. * Status bar texts are clearer and there are more explanations for more items. * `elimination_selfdamage` can now be set in the UI for Elimination and eCTF matches. * `g_grapple` can now be set for all match types. @@ -69,7 +69,14 @@ **Release date:** TBA -* Classic UI: * `g_ddCaptureTime` and `g_ddRespawnDelay` were added for Double Domination matches. +* Deletion of `g_runes`. In its place there's a new cvar, `g_classicMode`. Enabling it (via cvar or the Weapon Ruleset "Classic Arena" in Skirmish/Create Server) makes the following replacements: + * Nailgun and ammo with Shotgun and Shells. + * Chaingun and ammo with Machinegun ammo. + * Prox Launcher and ammo with Grenade Launcher and Grenades. + * Runes are disabled. + * Kamikaze with Personal Teleporter. + * Invulnerability with Medkit. +* Classic UI: `g_ddCaptureTime` and `g_ddRespawnDelay` were added for Double Domination matches. * Classic UI: `cg_weaponBarStyle` added to "Game Options" as, well, "Weapon Bar Style". * Classic UI: Minor overall fixes. * Classic UI: Skirmish/Create Server: Friendly Fire, Optimize for LAN and Host Name gained descriptions. diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 0b373f47..c3107c54 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -2616,14 +2616,17 @@ BotHasPersistantPowerupAndWeapon ================== */ int BotHasPersistantPowerupAndWeapon(bot_state_t *bs) { + // If the gamemode doesn't allow Runes + if (!G_GametypeUsesRunes(gametype)) { + return qfalse; + } // if the bot does not have a persistant powerup - //Sago - FIXME - This causes problems if there are no persistant powerups - /* if (!bs->inventory[INVENTORY_SCOUT] && - !bs->inventory[INVENTORY_GUARD] && - !bs->inventory[INVENTORY_DOUBLER] && - !bs->inventory[INVENTORY_AMMOREGEN] ) { - return qfalse; - }*/ + if ((bs->inventory[INVENTORY_SCOUT] <= 0) && + (bs->inventory[INVENTORY_GUARD] <= 0) && + (bs->inventory[INVENTORY_DOUBLER] <= 0) && + (bs->inventory[INVENTORY_AMMOREGEN] <= 0) ) { + return qfalse; + } //if the bot is very low on health if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; //if the bot is low on health diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 4a8b1b7e..fd0e72c3 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -666,8 +666,8 @@ void Cmd_DropRune_f( gentity_t *ent ) { if (!ent) { return; } - // If the command is disabled, the rune won't be tossed. - if (g_runes.integer < 2) { + // If not in an allowed gametype, nothing happens. + if (!G_GametypeUsesRunes(g_gametype.integer)) { return; } // Toss the rune. diff --git a/code/game/g_combat.c b/code/game/g_combat.c index 26431d19..86f4d5cf 100644 --- a/code/game/g_combat.c +++ b/code/game/g_combat.c @@ -236,6 +236,11 @@ void TossClientPersistantPowerups( gentity_t *ent ) { gentity_t *powerup; + // Only in gamemodes where Runes are allowed. + if (!G_GametypeUsesRunes(g_gametype.integer)) { + return; + } + if( !ent->client ) { return; } diff --git a/code/game/g_items.c b/code/game/g_items.c index 7c1bc499..d4bdf892 100644 --- a/code/game/g_items.c +++ b/code/game/g_items.c @@ -960,7 +960,9 @@ G_ItemDisabled int G_ItemDisabled( gitem_t *item ) { char name[128]; - if (!g_runes.integer && item->giType == IT_PERSISTANT_POWERUP) { + // In modes where the Runes are locked out, disable them. + if ((!G_GametypeUsesRunes(g_gametype.integer) || (g_classicMode.integer > 0)) + && item->giType == IT_PERSISTANT_POWERUP) { return qtrue; } @@ -1002,7 +1004,7 @@ void G_SpawnItem (gentity_t *ent, gitem_t *item) } if(g_gametype.integer == GT_DOUBLE_D && (strequals(ent->classname, "team_CTF_redflag") || strequals(ent->classname, "team_CTF_blueflag") - || strequals(ent->classname, "team_CTF_neutralflag") || item->giType == IT_PERSISTANT_POWERUP )) { + || strequals(ent->classname, "team_CTF_neutralflag"))) { ent->s.eFlags |= EF_NODRAW; //Don't draw the flag models/persistant powerups } diff --git a/code/game/g_local.h b/code/game/g_local.h index 3f939e50..d250c645 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -1128,7 +1128,6 @@ extern vmCvar_t g_lms_lives; extern vmCvar_t g_lms_mode; //How do we score: 0 = One Survivor get a point, 1 = same but without overtime, 2 = one point for each player killed (+overtime), 3 = same without overtime extern vmCvar_t g_elimination_ctf_oneway; //Only attack in one direction (level.eliminationSides+level.roundNumber)%2 == 0 red attacks extern vmCvar_t g_awardpushing; //The server can decide if players are awarded for pushing people in lave etc. -extern vmCvar_t g_runes; extern vmCvar_t g_catchup; //Favors the week players extern vmCvar_t g_autonextmap; //Autochange map extern vmCvar_t g_mappools; //mappools to be used for autochange @@ -1180,6 +1179,7 @@ extern vmCvar_t g_ddRespawnDelay; extern vmCvar_t g_developer; extern vmCvar_t g_spSkill; extern vmCvar_t g_bot_noChat; +extern vmCvar_t g_classicMode; void trap_Printf( const char *fmt ); void trap_Error( const char *fmt ) __attribute__((noreturn)); @@ -1437,4 +1437,5 @@ int G_GetWeaponArenaWeapon(int weapon); /* Takes a string and returns the value qboolean G_IsANoPickupsMode(void); /* Returns true if the match has a "no pickups" rule. */ qboolean G_GametypeUsesFragLimit(int check); /* Whether the gametype uses a frag-based scoring system. */ qboolean G_GametypeUsesCaptureLimit(int check); /* Whether the gametype uses a capture-based scoring system. */ +qboolean G_GametypeUsesRunes(int check); /* Whether the gametype uses the Runes. */ /* /Neon_Knight */ diff --git a/code/game/g_main.c b/code/game/g_main.c index 9191ca60..53d3a9b4 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -142,7 +142,6 @@ vmCvar_t g_lms_lives; vmCvar_t g_lms_mode; vmCvar_t g_elimination_ctf_oneway; vmCvar_t g_awardpushing; //The server can decide if players are awarded for pushing people in lave etc. -vmCvar_t g_runes; //Allow missionpack style persistant powerups? vmCvar_t g_catchup; //Favors the week players vmCvar_t g_autonextmap; //Autochange map vmCvar_t g_mappools; //mappools to be used for autochange @@ -196,6 +195,7 @@ vmCvar_t g_ddRespawnDelay; vmCvar_t g_developer; vmCvar_t g_spSkill; vmCvar_t g_bot_noChat; +vmCvar_t g_classicMode; mapinfo_result_t mapinfo; @@ -343,14 +343,6 @@ static cvarTable_t gameCvarTable[] = { { &g_awardpushing, "g_awardpushing", "1", CVAR_ARCHIVE, 0, qtrue }, - //g_persistantpowerups -#ifdef MISSIONPACK - { &g_runes, "g_runes", "1", CVAR_LATCH, 0, qfalse }, -#else - { &g_runes, "g_runes", "0", CVAR_LATCH|CVAR_ARCHIVE, 0, qfalse }, -#endif - - //nexuiz style rocket arena { &g_weaponArena, "g_weaponArena", "0", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse }, { &g_weaponArenaWeapon, "g_weaponArenaWeapon", "0", CVAR_SERVERINFO | CVAR_LATCH, 0, qfalse }, @@ -414,7 +406,8 @@ static cvarTable_t gameCvarTable[] = { /* Neon_Knight: Developer mode*/ { &g_developer, "developer", "0", CVAR_CHEAT, 0, qtrue}, { &g_spSkill, "g_spSkill", "2", 0, 0, qtrue}, - { &g_bot_noChat, "bot_nochat", "0", 0, 0, qtrue} + { &g_bot_noChat, "bot_nochat", "0", 0, 0, qtrue}, + { &g_classicMode, "g_classicMode", "0", 0, 0, qtrue} }; @@ -2959,4 +2952,18 @@ qboolean G_IsANoPickupsMode(void) { } return qfalse; } +/* +=================== +G_GametypeUsesRunes + +Returns true if the match has a "no pickups" rule. +=================== + */ +qboolean G_GametypeUsesRunes(int check) { + // If it's one of these gamemodes, it's true. + if (check == GT_CTF || check == GT_1FCTF || check == GT_HARVESTER || check == GT_OBELISK) { + return qtrue; + } + return qfalse; +} /* /Neon_Knight */ diff --git a/code/game/g_spawn.c b/code/game/g_spawn.c index aa54f4e8..c43ae550 100644 --- a/code/game/g_spawn.c +++ b/code/game/g_spawn.c @@ -294,16 +294,64 @@ qboolean G_CallSpawn( gentity_t *ent ) { char cvarname[128]; char itemname[128]; + // Neon_Knight: In CTF, outside of Arena mode, these items replace the TA items and weapons. + if (g_classicMode.integer > 0) { + if (strequals(ent->classname, "weapon_nailgun")) { + Com_sprintf(cvarname, sizeof(cvarname), "weapon_nailgun", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "weapon_shotgun", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "weapon_chaingun")) { + Com_sprintf(cvarname, sizeof(cvarname), "weapon_chaingun", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "ammo_bullets", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "weapon_prox_launcher")) { + Com_sprintf(cvarname, sizeof(cvarname), "weapon_prox_launcher", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "weapon_grenade_launcher", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "ammo_nails")) { + Com_sprintf(cvarname, sizeof(cvarname), "ammo_nails", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "ammo_shells", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "ammo_belt")) { + Com_sprintf(cvarname, sizeof(cvarname), "ammo_belt", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "ammo_bullets", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "ammo_prox")) { + Com_sprintf(cvarname, sizeof(cvarname), "ammo_prox", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "ammo_grenades", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "holdable_kamikaze")) { + Com_sprintf(cvarname, sizeof(cvarname), "holdable_kamikaze", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "holdable_teleporter", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "holdable_invulnerability")) { + Com_sprintf(cvarname, sizeof(cvarname), "holdable_kamikaze", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "holdable_medkit", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + else if (strequals(ent->classname, "holdable_portal")) { + Com_sprintf(cvarname, sizeof(cvarname), "holdable_portal", ent->classname); + Com_sprintf(itemname, sizeof(itemname), "holdable_medkit", ent->classname); + G_Printf ("%s replaced by %s\n", ent->classname, itemname); + } + } + else { //Construct a replace cvar: - Com_sprintf(cvarname, sizeof(cvarname), "replace_%s", ent->classname); - + Com_sprintf(cvarname, sizeof(cvarname), "replace_%s", ent->classname); //Look an alternative item up: trap_Cvar_VariableStringBuffer(cvarname,itemname,sizeof(itemname)); if(itemname[0]==0) //If nothing found use original Com_sprintf(itemname, sizeof(itemname), "%s", ent->classname); else G_Printf ("%s replaced by %s\n", ent->classname, itemname); - + } if ( itemname[0]==0) { G_Printf ("G_CallSpawn: NULL classname\n"); diff --git a/code/q3_ui/ui_startserver.c b/code/q3_ui/ui_startserver.c index 14e13356..ca919c11 100644 --- a/code/q3_ui/ui_startserver.c +++ b/code/q3_ui/ui_startserver.c @@ -892,6 +892,7 @@ static const char *weaponMode_list[] = { "All Weapons (Classic)", "Instantgib", "Single Weapon Arena", + "Classic Arena", "All Weapons (Elimination)", NULL }; @@ -900,6 +901,7 @@ static const char *weaponModeElimination_list[] = { "All Weapons (Elimination)", "Instantgib", "Single Weapon Arena", + "Classic Arena", "All Weapons (Elimination)", NULL }; @@ -1114,25 +1116,36 @@ static void ServerOptions_Start( void ) { trap_Cvar_SetValue( "g_instantgib", 1); trap_Cvar_SetValue( "g_weaponArena", 0); trap_Cvar_SetValue( "g_elimination", 0); + trap_Cvar_SetValue( "g_classicMode", 0); break; case 2: //Weapon Arena trap_Cvar_SetValue( "g_instantgib", 0); trap_Cvar_SetValue( "g_weaponArena", 1); trap_Cvar_SetValue( "g_elimination", 0); + trap_Cvar_SetValue( "g_classicMode", 0); break; case 3: + //"Classic" Arena + trap_Cvar_SetValue( "g_instantgib", 0); + trap_Cvar_SetValue( "g_weaponArena", 0); + trap_Cvar_SetValue( "g_elimination", 0); + trap_Cvar_SetValue( "g_classicMode", 1); + break; + case 4: if (UI_IsARoundBasedGametype(s_serveroptions.gametype)) { // Default mode for round-based gametypes. trap_Cvar_SetValue( "g_instantgib", 0); trap_Cvar_SetValue( "g_weaponArena", 0); trap_Cvar_SetValue( "g_elimination", 0); + trap_Cvar_SetValue( "g_classicMode", 0); } else { //Elimination mode. trap_Cvar_SetValue( "g_instantgib", 0); trap_Cvar_SetValue( "g_weaponArena", 0); trap_Cvar_SetValue( "g_elimination", 1); + trap_Cvar_SetValue( "g_classicMode", 0); } break; default: @@ -1140,6 +1153,7 @@ static void ServerOptions_Start( void ) { trap_Cvar_SetValue( "g_instantgib", 0); trap_Cvar_SetValue( "g_weaponArena", 0); trap_Cvar_SetValue( "g_elimination", 0); + trap_Cvar_SetValue( "g_classicMode", 0); break; } trap_Cvar_Set("sv_hostname", s_serveroptions.hostname.field.buffer ); @@ -1447,6 +1461,10 @@ static void ServerOptions_StatusBar_WeaponMode( void* ptr ) { UI_DrawString( 320, 460, "Players will spawn with a specific weapon.", UI_CENTER|UI_SMALLFONT, colorWhite ); break; case 3: + UI_DrawString( 320, 440, "Classic Arena: No pickups removed. Replaces some", UI_CENTER|UI_SMALLFONT, colorWhite ); + UI_DrawString( 320, 460, "weapons and items to match the OG experience.", UI_CENTER|UI_SMALLFONT, colorWhite ); + break; + case 4: UI_DrawString( 320, 440, "All Weapons (Elimination): All pickups removed.", UI_CENTER|UI_SMALLFONT, colorWhite ); UI_DrawString( 320, 460, "Players will spawn with all weapons.", UI_CENTER|UI_SMALLFONT, colorWhite ); break; @@ -1884,12 +1902,19 @@ static void ServerOptions_SetMenuItems( void ) { s_serveroptions.pmove.curvalue = 3; // Weapon Rules modes. Only one option can be active at a time. s_serveroptions.weaponMode.curvalue = 0; - if(trap_Cvar_VariableValue("g_instantgib") != 0 && trap_Cvar_VariableValue("g_weaponArena") == 0 && trap_Cvar_VariableValue("g_elimination") == 0) + // Instantgib mode + if(trap_Cvar_VariableValue("g_instantgib") != 0 && trap_Cvar_VariableValue("g_weaponArena") == 0 && trap_Cvar_VariableValue("g_elimination") == 0 && trap_Cvar_VariableValue("g_classicMode") == 0) s_serveroptions.weaponMode.curvalue = 1; - else if(trap_Cvar_VariableValue("g_instantgib") == 0 && trap_Cvar_VariableValue("g_weaponArena") != 0 && trap_Cvar_VariableValue("g_elimination") == 0) + // Single Weapon mode + else if(trap_Cvar_VariableValue("g_instantgib") == 0 && trap_Cvar_VariableValue("g_weaponArena") != 0 && trap_Cvar_VariableValue("g_elimination") == 0 && trap_Cvar_VariableValue("g_classicMode") == 0) s_serveroptions.weaponMode.curvalue = 2; - else if(trap_Cvar_VariableValue("g_instantgib") == 0 && trap_Cvar_VariableValue("g_weaponArena") == 0 && trap_Cvar_VariableValue("g_elimination") != 0) + // Classic mode + else if(trap_Cvar_VariableValue("g_instantgib") == 0 && trap_Cvar_VariableValue("g_weaponArena") == 0 && trap_Cvar_VariableValue("g_elimination") == 0 && trap_Cvar_VariableValue("g_classicMode") != 0) s_serveroptions.weaponMode.curvalue = 3; + // Elimination mode + else if(trap_Cvar_VariableValue("g_instantgib") == 0 && trap_Cvar_VariableValue("g_weaponArena") == 0 && trap_Cvar_VariableValue("g_elimination") != 0 && trap_Cvar_VariableValue("g_classicMode") == 0) + s_serveroptions.weaponMode.curvalue = 4; + // All Weapons mode else s_serveroptions.weaponMode.curvalue = 0;