- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
- make
- - EXPECT=e62d1a2375f0976ab12e2d980add29bd
+ - EXPECT=14643a645a99b8f855a6af279569d2a1
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
alias cl_hook_gamestart_ka
alias cl_hook_gamestart_ft
alias cl_hook_gamestart_inv
+alias cl_hook_gamestart_duel
alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends
alias cl_hook_shutdown
alias cl_hook_activeweapon
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
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
+// Example preset to allow 1v1ctf 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
// sv_vote_gametype_*_name Contains a human-readable name of the gametype
// sv_vote_gametype_*_description Contains a longer description
-//set sv_vote_gametype_duel_type dm
-//set sv_vote_gametype_duel_name Duel
-//set sv_vote_gametype_duel_description "One vs One match"
+//set sv_vote_gametype_1v1ctf_type ctf
+//set sv_vote_gametype_1v1ctf_name "Capture the Flag Duel"
+//set sv_vote_gametype_1v1ctf_description "One vs One match in CTF"
//alias sv_vote_gametype_hook_all "set g_maxplayers 0"
-//alias sv_vote_gametype_hook_duel "set g_maxplayers 2"
+//alias sv_vote_gametype_hook_1v1ctf "set g_maxplayers 2"
// ===========
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
// =========
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 0 "Duel: frag the opponent more in a one versus one arena battle"
return;
this.csqcmodel_predraw_run = framecount;
- if(!this.modelindex || this.model == "null")
+ if(!this.modelindex || this.model == "null" || this.alpha < 0)
{
this.drawmask = 0;
return;
else
this.drawmask = MASK_NORMAL;
- if(this.isplayermodel) // this checks if it's a player MODEL!
+ if(this.isplayermodel && this.drawmask) // this checks if it's a player MODEL!
{
CSQCPlayer_ModelAppearance_Apply(this, this.entnum == player_localnum + 1);
CSQCPlayer_LOD_Apply(this);
#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>
#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>
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/duel/sv_duel.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/duel/sv_duel.qh>
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#pragma once
+
+#include <common/mutators/base.qh>
+REGISTER_MUTATOR(duel, false)
+{
+ MUTATOR_STATIC();
+ return 0;
+}
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 < 16384);
+ }
+ METHOD(Duel, m_isForcedSupported, bool(Gametype this))
+ {
+ // 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 & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags))
+ return true;
+ return false;
+ }
+ENDCLASS(Duel)
+REGISTER_GAMETYPE(DUEL, NEW(Duel));
+#define g_duel IS_GAMETYPE(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;
}
// 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)
W_SetupShot_Range(
own,
- weaponentity, // TODO
+ weaponentity,
true,
0,
SND_Null,
0,
WEP_CVAR(arc, beam_damage) * coefficient,
WEP_CVAR(arc, beam_range),
- WEP_ARC.m_id
+ thiswep.m_id
);
// After teleport, "lock" the beam until the teleport is confirmed.
void Arc_Smoke(entity actor, .entity weaponentity)
{
makevectors(actor.v_angle);
- W_SetupShot_Range(actor,weaponentity,true,0,SND_Null,0,0,0,WEP_ARC.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect
+ W_SetupShot_Range(actor,weaponentity,false,0,SND_Null,0,0,0,WEP_ARC.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect
vector smoke_origin = w_shotorg + actor.velocity*frametime;
if ( actor.arc_overheat > time )
GAMETYPE(MAPINFO_TYPE_NEXBALL) \
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
+ /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
/* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
/**/
// add bots to reach minplayers if needed
bots = max(minbots, minplayers - activerealplayers);
// cap bots to the max players allowed by the server
- if(autocvar_g_maxplayers)
- bots = min(bots, autocvar_g_maxplayers - activerealplayers);
+ int player_limit = GetPlayerLimit();
+ if(player_limit)
+ bots = min(bots, player_limit - activerealplayers);
bots = min(bots, maxclients - realplayers);
if(bots > minbots)
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
++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)
void ClientInit_misc(entity this);
+int GetPlayerLimit();
+
bool joinAllowed(entity this);
void Join(entity this);
gametypename = "team";
if(g_ctf)
gametypename = "ctf";
+ if(g_duel)
+ gametypename = "tournament";
if(maxclients == 1)
gametypename = "single";
// we do not have the other types (oneflag, obelisk, harvester, teamtournament)
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");
/** 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);
#include "../command/common.qh"
#include <server/mutators/_mod.qh>
#include "../round_handler.qh"
+#include <server/cheats.qh>
#include <server/resources.qh>
#include <common/t_items.qh>
#include <common/animdecide.qh>