From af0cba358e09c0855180f2bad9b874fb18b97537 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 10 Sep 2018 07:00:25 +1000 Subject: [PATCH] Experimental duel gametype (forced 1v1 deathmatch mode, supports all the same maps) --- gamemodes-server.cfg | 14 ++++++++++++++ qcsrc/common/gamemodes/gamemode/_mod.inc | 1 + qcsrc/common/gamemodes/gamemode/_mod.qh | 1 + qcsrc/common/gamemodes/gamemode/duel/_mod.inc | 4 ++++ qcsrc/common/gamemodes/gamemode/duel/_mod.qh | 4 ++++ .../common/gamemodes/gamemode/duel/sv_duel.qc | 18 ++++++++++++++++++ .../common/gamemodes/gamemode/duel/sv_duel.qh | 8 ++++++++ qcsrc/common/mapinfo.qc | 5 +++++ qcsrc/common/mapinfo.qh | 12 ++++++++++++ qcsrc/common/playerstats.qc | 1 + qcsrc/menu/xonotic/util.qc | 1 + qcsrc/server/bot/default/bot.qc | 1 + qcsrc/server/client.qc | 16 +++++++++++++--- qcsrc/server/client.qh | 2 ++ qcsrc/server/g_world.qc | 1 + qcsrc/server/mutators/events.qh | 9 +++++++++ 16 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 qcsrc/common/gamemodes/gamemode/duel/_mod.inc create mode 100644 qcsrc/common/gamemodes/gamemode/duel/_mod.qh create mode 100644 qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc create mode 100644 qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh diff --git a/gamemodes-server.cfg b/gamemodes-server.cfg index f13dd854a..37f8085f8 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -28,6 +28,7 @@ alias sv_hook_gamestart_cts alias sv_hook_gamestart_ka alias sv_hook_gamestart_ft alias sv_hook_gamestart_inv +alias sv_hook_gamestart_duel alias sv_hook_gamerestart alias sv_hook_gameend @@ -54,6 +55,7 @@ alias sv_vote_gametype_hook_nb alias sv_vote_gametype_hook_ons alias sv_vote_gametype_hook_rc alias sv_vote_gametype_hook_tdm +alias sv_vote_gametype_hook_duel // Example preset to allow duel to be used for the gametype voting screen // sv_vote_gametype_*_type Must be set to the name of the gametype the option is based on @@ -196,6 +198,13 @@ set g_inv_respawn_delay_large_count 0 set g_inv_respawn_delay_max 0 set g_inv_respawn_waves 0 set g_inv_weapon_stay 0 +set g_duel_respawn_delay_small 0 +set g_duel_respawn_delay_small_count 0 +set g_duel_respawn_delay_large 0 +set g_duel_respawn_delay_large_count 0 +set g_duel_respawn_delay_max 0 +set g_duel_respawn_waves 0 +set g_duel_weapon_stay 0 // ========= @@ -523,3 +532,8 @@ set g_invasion_spawnpoint_spawn_delay 0.5 set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)" set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode" set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)" + +// ====== +// duel +// ====== +set g_duel 1 "Duel: frag the opponent more in a one versus one arena battle" diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index c4cd002c7..a33ec87a0 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index d7c1aa66c..ffd71d59d 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.inc b/qcsrc/common/gamemodes/gamemode/duel/_mod.inc new file mode 100644 index 000000000..592581675 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.qh b/qcsrc/common/gamemodes/gamemode/duel/_mod.qh new file mode 100644 index 000000000..00e553c20 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc new file mode 100644 index 000000000..7e4c891e6 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc @@ -0,0 +1,18 @@ +#include "sv_duel.qh" + +MUTATOR_HOOKFUNCTION(duel, ReadLevelCvars) +{ + warmup_stage = 1; + //sv_ready_restart_after_countdown = 0; +} + +MUTATOR_HOOKFUNCTION(duel, GetPlayerLimit) +{ + M_ARGV(0, int) = 2; // duel is always 1v1! +} + +MUTATOR_HOOKFUNCTION(duel, Scores_CountFragsRemaining) +{ + // announce remaining frags? + return true; +} diff --git a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh new file mode 100644 index 000000000..9a0d716b9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh @@ -0,0 +1,8 @@ +#pragma once + +#include +REGISTER_MUTATOR(duel, false) +{ + MUTATOR_STATIC(); + return 0; +} diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 0884bc8d7..f5a0f666b 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -1064,6 +1064,11 @@ int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametyp { int r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet); + // force all DM maps to work in duel?! + // TODO: we should really check the size of maps, some DM maps do not work for duel! + if(!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DUEL.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)) + _MapInfo_Map_ApplyGametypeEx ("", pGametypeToSet, MAPINFO_TYPE_DUEL); + if(cvar("g_tdm_on_dm_maps")) { // if this is set, all DM maps support TDM too diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 4addd2400..245b3ed7b 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -480,6 +480,18 @@ CLASS(Invasion, Gametype) ENDCLASS(Invasion) REGISTER_GAMETYPE(INVASION, NEW(Invasion)); +CLASS(Duel, Gametype) + INIT(Duel) + { + this.gametype_init(this, _("Duel"),"duel","g_duel",false,"","timelimit=10 pointlimit=0 leadlimit=0",_("Fight in a one versus one arena battle to decide the winner")); + } + METHOD(Duel, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return (diameter < 4096); + } +ENDCLASS(Duel) +REGISTER_GAMETYPE(DUEL, NEW(Duel)); + const int MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps const int MAPINFO_FEATURE_VEHICLES = 2; const int MAPINFO_FEATURE_TURRETS = 4; diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 869af4da9..2c9bbcde8 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -258,6 +258,7 @@ void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that } // this... is a hack, a temporary one until we get a proper duel gametype +// TODO: remove duel hack after servers have migrated to the proper duel gametype! string PlayerStats_GetGametype() { if(IS_GAMETYPE(DEATHMATCH) && autocvar_g_maxplayers == 2) diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index fb4bb92a8..e99449112 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -689,6 +689,7 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_NEXBALL) \ GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \ GAMETYPE(MAPINFO_TYPE_ASSAULT) \ + /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \ /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \ /**/ diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index b80b498e7..10ececff6 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -593,6 +593,7 @@ float bot_fixcount() }); } + //int player_limit = GetPlayerLimit(); // TODO: use this instead of maxclients! int bots; // add/remove bots if needed to make sure there are at least // minplayers+bot_number, or remove all bots if no one is playing diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 8b70a633a..88f423e7a 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -1905,6 +1905,14 @@ void Join(entity this) this.team_selected = false; } +int GetPlayerLimit() +{ + int player_limit = autocvar_g_maxplayers; + MUTATOR_CALLHOOK(GetPlayerLimit, player_limit); + player_limit = M_ARGV(0, int); + return player_limit; +} + /** * Determines whether the player is allowed to join. This depends on cvar * g_maxplayers, if it isn't used this function always return true, otherwise @@ -1937,11 +1945,13 @@ int nJoinAllowed(entity this, entity ignore) ++currentlyPlaying; }); + int player_limit = GetPlayerLimit(); + float free_slots = 0; - if (!autocvar_g_maxplayers) + if (!player_limit) free_slots = maxclients - totalClients; - else if(currentlyPlaying < autocvar_g_maxplayers) - free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); + else if(currentlyPlaying < player_limit) + free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying); static float join_prevent_msg_time = 0; if(this && ignore && !free_slots && time > join_prevent_msg_time) diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index 099fac9f5..c6e4b7879 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -276,6 +276,8 @@ void FixPlayermodel(entity player); void ClientInit_misc(entity this); +int GetPlayerLimit(); + bool joinAllowed(entity this); void Join(entity this); diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 038936ee4..05bff1421 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -265,6 +265,7 @@ void cvar_changes_init() BADCVAR("g_dm"); BADCVAR("g_domination"); BADCVAR("g_domination_default_teams"); + BADCVAR("g_duel"); BADCVAR("g_freezetag"); BADCVAR("g_freezetag_teams"); BADCVAR("g_invasion_teams"); diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 58e9ca127..864623bba 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -1203,3 +1203,12 @@ MUTATOR_HOOKABLE(Freeze, EV_Freeze); /** targ */ i(entity, MUTATOR_ARGV_0_entity) \ /**/ MUTATOR_HOOKABLE(Unfreeze, EV_Unfreeze); + +/** + * Called when a player is trying to join, argument is the number of players allowed to join the match + */ +#define EV_GetPlayerLimit(i, o) \ + /** g_maxplayers */ i(int, MUTATOR_ARGV_0_int) \ + /**/ o(int, MUTATOR_ARGV_0_int) \ + /**/ +MUTATOR_HOOKABLE(GetPlayerLimit, EV_GetPlayerLimit); -- 2.39.2