alias cl_hook_gamestart_ft
alias cl_hook_gamestart_inv
alias cl_hook_gamestart_duel
+alias cl_hook_gamestart_mh
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_ft
alias sv_hook_gamestart_inv
alias sv_hook_gamestart_duel
+alias sv_hook_gamestart_mh
// there is currently no hook for when the match is restarted
// see sv_hook_readyrestart for previous uses of this hook
//alias sv_hook_gamerestart
alias sv_vote_gametype_hook_rc
alias sv_vote_gametype_hook_tdm
alias sv_vote_gametype_hook_duel
+alias sv_vote_gametype_hook_mh
// Example preset to allow 1v1ctf to be used for the gametype voting screen.
// Aliases can have max 31 chars so the gametype can have max 9 chars.
set g_duel_respawn_delay_max 0
set g_duel_respawn_waves 0
set g_duel_weapon_stay 0
+set g_mh_respawn_delay_small 0
+set g_mh_respawn_delay_small_count 0
+set g_mh_respawn_delay_large 0
+set g_mh_respawn_delay_large_count 0
+set g_mh_respawn_delay_max 0
+set g_mh_respawn_waves 0
+set g_mh_weapon_stay 0
// =========
//set g_duel_warmup 180 "Have a short warmup period before beginning the actual duel"
set g_duel_with_powerups 0 "Enable powerups to spawn in the duel gamemode"
set g_duel_not_dm_maps 0 "when this is set, DM maps will NOT be listed in duel"
+
+// =========
+// manhunt
+// =========
+set g_mh 0 "Manhunt: Hunters go in search of the runners"
+set g_mh_not_dm_maps 0 "when this is set, DM maps will NOT be listed in MH"
+set g_mh_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in MH"
+
+// TODO: change this?
+set g_mh_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
+set g_mh_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+set g_mh_teams_override 0 "how many teams are in manhunt"
+set g_mh_point_limit -1 "MH point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_mh_point_leadlimit -1 "MH point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
#include <common/gamemodes/gamemode/keepaway/_mod.inc>
#include <common/gamemodes/gamemode/keyhunt/_mod.inc>
#include <common/gamemodes/gamemode/lms/_mod.inc>
+#include <common/gamemodes/gamemode/mh/_mod.inc>
#include <common/gamemodes/gamemode/nexball/_mod.inc>
#include <common/gamemodes/gamemode/onslaught/_mod.inc>
#include <common/gamemodes/gamemode/race/_mod.inc>
#include <common/gamemodes/gamemode/keepaway/_mod.qh>
#include <common/gamemodes/gamemode/keyhunt/_mod.qh>
#include <common/gamemodes/gamemode/lms/_mod.qh>
+#include <common/gamemodes/gamemode/mh/_mod.qh>
#include <common/gamemodes/gamemode/nexball/_mod.qh>
#include <common/gamemodes/gamemode/onslaught/_mod.qh>
#include <common/gamemodes/gamemode/race/_mod.qh>
--- /dev/null
+// generated file; do not modify
+#include <common/gamemodes/gamemode/mh/mh.qc>
+#ifdef CSQC
+ #include <common/gamemodes/gamemode/mh/cl_mh.qc>
+#endif
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/mh/sv_mh.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#include <common/gamemodes/gamemode/mh/mh.qh>
+#ifdef CSQC
+ #include <common/gamemodes/gamemode/mh/cl_mh.qh>
+#endif
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/mh/sv_mh.qh>
+#endif
--- /dev/null
+#include "cl_mh.qh"
+
+#include <client/draw.qh>
+
+// TODO: change this?
+
+void HUD_Mod_MH_Export(int fh)
+{
+ HUD_Write_Cvar("hud_panel_modicons_mh_layout");
+}
+
+void DrawMHItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i)
+{
+ TC(int, layout); TC(int, i);
+ int stat = -1;
+ string pic = "";
+ vector color = '0 0 0';
+ switch(i)
+ {
+ case 0: stat = STAT(REDALIVE); pic = "player_red"; color = '1 0 0'; break;
+ case 1: stat = STAT(BLUEALIVE); pic = "player_blue"; color = '0 0 1'; break;
+ case 2: stat = STAT(YELLOWALIVE); pic = "player_yellow"; color = '1 1 0'; break;
+ default:
+ case 3: stat = STAT(PINKALIVE); pic = "player_pink"; color = '1 0 1'; break;
+ }
+
+ if(mySize.x/mySize.y > aspect_ratio)
+ {
+ i = aspect_ratio * mySize.y;
+ myPos.x = myPos.x + (mySize.x - i) / 2;
+ mySize.x = i;
+ }
+ else
+ {
+ i = 1/aspect_ratio * mySize.x;
+ myPos.y = myPos.y + (mySize.y - i) / 2;
+ mySize.y = i;
+ }
+
+ if(layout)
+ {
+ drawpic_aspect_skin(myPos, pic, vec2(0.5 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(myPos + eX * 0.5 * mySize.x, ftos(stat), vec2(0.5 * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+void HUD_Mod_MH_Draw(vector myPos, vector mySize, int layout)
+{
+ int rows, columns;
+ float aspect_ratio;
+ aspect_ratio = (layout) ? 2 : 1;
+ rows = HUD_GetRowCount(team_count, mySize, aspect_ratio);
+ columns = ceil(team_count/rows);
+
+ int i;
+ float row = 0, column = 0;
+ vector pos = '0 0 0', itemSize;
+ itemSize = vec2(mySize.x / columns, mySize.y / rows);
+ for(i=0; i<team_count; ++i)
+ {
+ pos.x = myPos.x + column * itemSize.x;
+ pos.y = myPos.y + row * itemSize.y;
+
+ DrawMHItem(pos, itemSize, aspect_ratio, layout, i);
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
+ }
+}
+
+// Clan Arena and Freeze Tag HUD modicons
+void HUD_Mod_MH(vector myPos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+
+ HUD_Mod_MH_Draw(myPos, mySize, autocvar_hud_panel_modicons_mh_layout);
+}
--- /dev/null
+#pragma once
+
+int autocvar_hud_panel_modicons_mh_layout;
+
+void HUD_Mod_MH(vector myPos, vector mySize);
+void HUD_Mod_MH_Draw(vector myPos, vector mySize, int layout);
+void HUD_Mod_MH_Export(int fh);
--- /dev/null
+#include "mh.qh"
--- /dev/null
+#pragma once
+
+#include <common/mapinfo.qh>
+
+#ifdef CSQC
+void HUD_Mod_MH(vector pos, vector mySize);
+void HUD_Mod_MH_Export(int fh);
+#endif
+CLASS(Manhunt, Gametype)
+ INIT(Manhunt)
+ {
+ this.gametype_init(this, _("Manhunt"),"mh","g_mh",GAMETYPE_FLAG_TEAMPLAY | GAMETYPE_FLAG_USEPOINTS | GAMETYPE_FLAG_PRIORITY,"","timelimit=15 pointlimit=10 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team"));
+ }
+ METHOD(Manhunt, m_parse_mapinfo, bool(string k, string v))
+ {
+ if (!k) {
+ cvar_set("g_mh_teams", cvar_defstring("g_mh_teams"));
+ return true;
+ }
+ switch (k) {
+ case "teams":
+ cvar_set("g_mh_teams", v);
+ return true;
+ }
+ return false;
+ }
+ METHOD(Manhunt, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+ {
+ if(spawnpoints >= 8 && diameter > 4096)
+ return true;
+ return false;
+ }
+ METHOD(Manhunt, m_isForcedSupported, bool(Gametype this))
+ {
+ if(!cvar("g_mh_not_dm_maps"))
+ {
+ // if this is unset, all DM maps support MMM too
+ if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags))
+ return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
+ }
+
+ return false;
+ }
+ METHOD(Manhunt, m_setTeams, void(string sa))
+ {
+ cvar_set("g_mh_teams", sa);
+ }
+ METHOD(Manhunt, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
+ {
+ TC(Gametype, this);
+ returns(menu, _("Point limit:"), 5, 100, 5, "g_mh_point_limit", "g_mh_teams_override", _("The amount of points needed before the match will end"));
+ }
+ ATTRIB(Manhunt, m_legacydefaults, string, "50 20 2 0");
+ENDCLASS(Manhunt)
+REGISTER_GAMETYPE(MANHUNT, NEW(Manhunt));
+#define g_mh IS_GAMETYPE(MANHUNT)
--- /dev/null
+#include "sv_mh.qh"
+
+// TODO: change this?
+int autocvar_g_mh_teams;
+int autocvar_g_mh_teams_override;
+
+spawnfunc(mh_team)
+{
+ if(!g_mh || !this.cnt) { delete(this); return; }
+
+ this.team = this.cnt + 1;
+}
+
+// code from here on is just to support maps that don't have team entities
+void mh_SpawnTeam (string teamname, int teamcolor)
+{
+ entity this = new_pure(mh_team);
+ this.netname = teamname;
+ this.cnt = teamcolor - 1;
+ this.team = teamcolor;
+ this.spawnfunc_checked = true;
+ //spawnfunc_mh_team(this);
+}
+
+void mh_DelayedInit(entity this)
+{
+ // TODO: change this?
+
+ // if no teams are found, spawn defaults
+ if(find(NULL, classname, "mh_team") == NULL)
+ {
+ LOG_TRACE("No \"mh_team\" entities found on this map, creating them anyway.");
+
+ int numteams = autocvar_g_mh_teams_override;
+ if(numteams < 2) { numteams = autocvar_g_mh_teams; }
+
+ int teams = BITS(bound(2, numteams, 2));
+ if(teams & BIT(0))
+ mh_SpawnTeam("Red", NUM_TEAM_1);
+ if(teams & BIT(1))
+ mh_SpawnTeam("Blue", NUM_TEAM_2);
+ }
+}
+
+void mh_Initialize()
+{
+ GameRules_teams(true);
+ GameRules_spawning_teams(autocvar_g_mh_team_spawns);
+ GameRules_limit_score(autocvar_g_mh_point_limit);
+ GameRules_limit_lead(autocvar_g_mh_point_leadlimit);
+
+ InitializeEntity(NULL, mh_DelayedInit, INITPRIO_GAMETYPE);
+}
+
+MUTATOR_HOOKFUNCTION(mh, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
+{
+ M_ARGV(1, string) = "mh_team";
+}
+
+MUTATOR_HOOKFUNCTION(mh, Scores_CountFragsRemaining)
+{
+ // announce remaining frags
+ return true;
+}
--- /dev/null
+#pragma once
+
+#include <common/mutators/base.qh>
+
+// TODO: change this?
+int autocvar_g_mh_point_limit;
+int autocvar_g_mh_point_leadlimit;
+bool autocvar_g_mh_team_spawns;
+void mh_Initialize();
+
+REGISTER_MUTATOR(mh, false)
+{
+ MUTATOR_STATIC();
+ MUTATOR_ONADD
+ {
+ mh_Initialize();
+ }
+ return 0;
+}
GAMETYPE(MAPINFO_TYPE_NEXBALL) \
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
+ GAMETYPE(MAPINFO_TYPE_MANHUNT) \
/* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
/* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
/**/
BADCVAR("g_keyhunt");
BADCVAR("g_keyhunt_teams");
BADCVAR("g_lms");
+ BADCVAR("g_mh");
+ BADCVAR("g_mh_not_dm_maps");
BADCVAR("g_nexball");
BADCVAR("g_onslaught");
BADCVAR("g_race");