Experimental duel gametype (forced 1v1 deathmatch mode, supports all the same maps)
authorMario <zacjardine@y7mail.com>
Sun, 9 Sep 2018 21:00:25 +0000 (07:00 +1000)
committerMario <zacjardine@y7mail.com>
Sun, 9 Sep 2018 21:00:25 +0000 (07:00 +1000)
16 files changed:
gamemodes-server.cfg
qcsrc/common/gamemodes/gamemode/_mod.inc
qcsrc/common/gamemodes/gamemode/_mod.qh
qcsrc/common/gamemodes/gamemode/duel/_mod.inc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/duel/_mod.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh [new file with mode: 0644]
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/playerstats.qc
qcsrc/menu/xonotic/util.qc
qcsrc/server/bot/default/bot.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/g_world.qc
qcsrc/server/mutators/events.qh

index f13dd854ac08ad2f2eea309ae36ba214388ce124..37f8085f8c25accf88d26026e881688c7a8a90a1 100644 (file)
@@ -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"
index c4cd002c782221fe420d38415c25fed570909a4d..a33ec87a01a34a5a8406f57aa3c3d52829cf3994 100644 (file)
@@ -6,6 +6,7 @@
 #include <common/gamemodes/gamemode/cts/_mod.inc>
 #include <common/gamemodes/gamemode/deathmatch/_mod.inc>
 #include <common/gamemodes/gamemode/domination/_mod.inc>
+#include <common/gamemodes/gamemode/duel/_mod.inc>
 #include <common/gamemodes/gamemode/freezetag/_mod.inc>
 #include <common/gamemodes/gamemode/invasion/_mod.inc>
 #include <common/gamemodes/gamemode/keepaway/_mod.inc>
index d7c1aa66cc35f6f3f8143031123542abf134a33c..ffd71d59d3f1092453b6d83f8048003693dfa531 100644 (file)
@@ -6,6 +6,7 @@
 #include <common/gamemodes/gamemode/cts/_mod.qh>
 #include <common/gamemodes/gamemode/deathmatch/_mod.qh>
 #include <common/gamemodes/gamemode/domination/_mod.qh>
+#include <common/gamemodes/gamemode/duel/_mod.qh>
 #include <common/gamemodes/gamemode/freezetag/_mod.qh>
 #include <common/gamemodes/gamemode/invasion/_mod.qh>
 #include <common/gamemodes/gamemode/keepaway/_mod.qh>
diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.inc b/qcsrc/common/gamemodes/gamemode/duel/_mod.inc
new file mode 100644 (file)
index 0000000..5925816
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/duel/sv_duel.qc>
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.qh b/qcsrc/common/gamemodes/gamemode/duel/_mod.qh
new file mode 100644 (file)
index 0000000..00e553c
--- /dev/null
@@ -0,0 +1,4 @@
+// generated file; do not modify
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/duel/sv_duel.qh>
+#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 (file)
index 0000000..7e4c891
--- /dev/null
@@ -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 (file)
index 0000000..9a0d716
--- /dev/null
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <common/mutators/base.qh>
+REGISTER_MUTATOR(duel, false)
+{
+    MUTATOR_STATIC();
+       return 0;
+}
index 0884bc8d79054c29d6d88bacb75ad88de011e09b..f5a0f666bdb57e3d8de103fe21b032aaefd53121 100644 (file)
@@ -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
index 4addd24001f6d32a594424a2761855c222f3b300..245b3ed7ba1818bb3f8f37b3853cdf7f9fbbea86 100644 (file)
@@ -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;
index 869af4da96533bb90f31f382b35ef0b3ac243a43..2c9bbcde805eb9770015c385877971563b872731 100644 (file)
@@ -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)
index fb4bb92a80b5421e80d65ef05d2cd178c04ef6e7..e994491121a8ae1bf012f6981d7c8fdbc1a7a465 100644 (file)
@@ -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) */ \
        /**/
 
index b80b498e7253fd0a13008d4ec2f0d214c8178734..10ececff6362a3964c13d9c4c3ea181e536bc671 100644 (file)
@@ -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
index 8b70a633af9ebc59a394e1093684a34d852d7470..88f423e7a6473939f180e5c014a0085f5ea1af14 100644 (file)
@@ -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)
index 099fac9f541f5ae71358f2fad6f2e0894a978e14..c6e4b7879d327f4936695ed77f58674ef9fd2c6b 100644 (file)
@@ -276,6 +276,8 @@ void FixPlayermodel(entity player);
 
 void ClientInit_misc(entity this);
 
+int GetPlayerLimit();
+
 bool joinAllowed(entity this);
 void Join(entity this);
 
index 038936ee49282bff375700dd76159197aea86d8f..05bff142184be3208664ab82f807bb27f4ccd34b 100644 (file)
@@ -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");
index 58e9ca127dabb4fb3183616f7815885fcfafa861..864623bbae4b888f5c3ca4e56e40d70b0d513246 100644 (file)
@@ -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);