From: drjaska Date: Thu, 5 May 2022 10:58:15 +0000 (+0300) Subject: initial CTS Cup commit X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=afc76d1e18ae5aebb9051a4c33b84a6f2c55dc26;p=xonotic%2Fxonotic-data.pk3dir.git initial CTS Cup commit copypasted CTS files, made a few cts functions and autocvar's public so they can be used by cts cup to avoid duplication --- diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index a33ec87a0..431923c4b 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -4,6 +4,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 ffd71d59d..9e883a633 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc index 45457d1a4..197a6e2ae 100644 --- a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc @@ -9,10 +9,6 @@ #include #include -float autocvar_g_cts_finish_kill_delay; -bool autocvar_g_cts_selfdamage; -bool autocvar_g_cts_removeprojectiles; - // legacy bot roles .float race_checkpoint; void havocbot_role_cts(entity this) diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh index 371f7a250..a90edf543 100644 --- a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh @@ -4,6 +4,13 @@ #include void cts_Initialize(); +void havocbot_role_cts(entity); +void cts_ScoreRules(); +void cts_EventLog(string, entity); + +float autocvar_g_cts_finish_kill_delay; +bool autocvar_g_cts_selfdamage; +bool autocvar_g_cts_removeprojectiles; REGISTER_MUTATOR(cts, false) { diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt b/qcsrc/common/gamemodes/gamemode/ctscup/TODO.txt new file mode 100644 index 000000000..e69de29bb diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc new file mode 100644 index 000000000..89d6a2c7a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc @@ -0,0 +1,8 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh new file mode 100644 index 000000000..e9459a017 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh @@ -0,0 +1,8 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qc b/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qc new file mode 100644 index 000000000..6afb8ccdf --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qc @@ -0,0 +1,54 @@ +#include "cl_ctscup.qh" + +#include + +REGISTER_MUTATOR(cl_ctscup, true); + +MUTATOR_HOOKFUNCTION(cl_ctscup, HUD_Physics_showoptional) +{ + return ISGAMETYPE(CTSCup); // show the optional physics panel +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, HUD_StrafeHUD_showoptional) +{ + return ISGAMETYPE(CTSCup); // show the optional strafehud +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, HUD_Score_show) +{ + return spectatee_status == -1 && ISGAMETYPE(CTSCup); // hide the score panel while observing +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, DrawScoreboardItemStats) +{ + return ISGAMETYPE(CTSCup); // hide the item stats panel +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, DrawDeathScoreboard) +{ + return ISGAMETYPE(CTSCup); // no scoreboard shown while dead +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, DrawScoreboardAccuracy) +{ + return ISGAMETYPE(CTSCup); // accuracy is not a factor in this gamemode +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, ShowRankings) +{ + if(ISGAMETYPE(CTSCup)) + { + M_ARGV(0, string) = _("Rankings"); + return true; + } +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, ShowNames_Draw) +{ + return (ISGAMETYPE(CTSCup) && M_ARGV(1, float) < ALPHA_MIN_VISIBLE); +} + +MUTATOR_HOOKFUNCTION(cl_ctscup, ShowRaceTimer) +{ + return ISGAMETYPE(CTSCup); // show the race timer panel +} diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qh b/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/cl_ctscup.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qc b/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qc new file mode 100644 index 000000000..4cb4c0b15 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qc @@ -0,0 +1 @@ +#include "ctscup.qh" diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qh b/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qh new file mode 100644 index 000000000..d51636b0e --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/ctscup.qh @@ -0,0 +1,36 @@ +#pragma once + +#include +#if defined(CSQC) + #include +#endif + +CLASS(RaceCTSCup, Gametype) + INIT(RaceCTSCup) + { + this.gametype_init(this, _("Race CTS Cup"),"ctscup","g_ctscup",0,"cloaked","timelimit=20",_("Race for fastest time.")); + } + METHOD(RaceCTSCup, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "target_startTimer") + MapInfo_Map_supportedGametypes |= this.m_flags; + } + METHOD(RaceCTSCup, m_setTeams, void(string sa)) + { + // this is the skill of the map + // not parsed by anything yet + // for map databases + // cvar_set("fraglimit", sa); + } + METHOD(RaceCTSCup, 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:"), 50, 500, 10, string_null, string_null, string_null); + } +#ifdef CSQC + ATTRIB(RaceCTSCup, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); +#endif + ATTRIB(RaceCTSCup, m_legacydefaults, string, "20 0 0"); +ENDCLASS(RaceCTSCup) +REGISTER_GAMETYPE(CTSCup, NEW(RaceCTSCup)); +#define g_ctscup IS_GAMETYPE(CTSCup) diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc new file mode 100644 index 000000000..7da09e0b0 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc @@ -0,0 +1,307 @@ +#include "sv_ctscup.qh" + +#include +#include +#include +#include +#include +#include +#include +#include + +MUTATOR_HOOKFUNCTION(ctscup, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + float dt = M_ARGV(1, float); + + player.race_movetime_frac += dt; + float f = floor(player.race_movetime_frac); + player.race_movetime_frac -= f; + player.race_movetime_count += f; + player.race_movetime = player.race_movetime_frac + player.race_movetime_count; + + if(IS_PLAYER(player)) + { + if (player.race_penalty) + if (time > player.race_penalty) + player.race_penalty = 0; + if(player.race_penalty) + { + player.velocity = '0 0 0'; + set_movetype(player, MOVETYPE_NONE); + player.disableclientprediction = 2; + } + } + + // force kbd movement for fairness + float wishspeed; + vector wishvel; + + // if record times matter + // ensure nothing EVIL is being done (i.e. div0_evade) + // this hinders joystick users though + // but it still gives SOME analog control + wishvel.x = fabs(CS(player).movement.x); + wishvel.y = fabs(CS(player).movement.y); + if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y) + { + wishvel.z = 0; + wishspeed = vlen(wishvel); + if(wishvel.x >= 2 * wishvel.y) + { + // pure X motion + if(CS(player).movement.x > 0) + CS(player).movement_x = wishspeed; + else + CS(player).movement_x = -wishspeed; + CS(player).movement_y = 0; + } + else if(wishvel.y >= 2 * wishvel.x) + { + // pure Y motion + CS(player).movement_x = 0; + if(CS(player).movement.y > 0) + CS(player).movement_y = wishspeed; + else + CS(player).movement_y = -wishspeed; + } + else + { + // diagonal + if(CS(player).movement.x > 0) + CS(player).movement_x = M_SQRT1_2 * wishspeed; + else + CS(player).movement_x = -M_SQRT1_2 * wishspeed; + if(CS(player).movement.y > 0) + CS(player).movement_y = M_SQRT1_2 * wishspeed; + else + CS(player).movement_y = -M_SQRT1_2 * wishspeed; + } + } +} + +MUTATOR_HOOKFUNCTION(ctscup, reset_map_global) +{ + float s; + + Score_NicePrint(NULL); + + race_ClearRecords(); + PlayerScore_Sort(race_place, 0, true, false); + + FOREACH_CLIENT(true, { + if(it.race_place) + { + s = GameRules_scoring_add(it, RACE_FASTEST, 0); + if(!s) + it.race_place = 0; + } + cts_EventLog(ftos(it.race_place), it); + }); + + if(g_race_qualifying == 2) + { + g_race_qualifying = 0; + independent_players = 0; + cvar_set("fraglimit", ftos(race_fraglimit)); + cvar_set("leadlimit", ftos(race_leadlimit)); + cvar_set("timelimit", ftos(race_timelimit)); + cts_ScoreRules(); + } +} + +MUTATOR_HOOKFUNCTION(ctscup, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + race_PreparePlayer(player); + player.race_checkpoint = -1; + + race_SendAll(player, false); +} + +MUTATOR_HOOKFUNCTION(ctscup, AbortSpeedrun) +{ + entity player = M_ARGV(0, entity); + + if(autocvar_g_allow_checkpoints) + race_PreparePlayer(player); // nice try +} + +MUTATOR_HOOKFUNCTION(ctscup, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + player.frags = FRAGS_SPECTATOR; + + race_PreparePlayer(player); + player.race_checkpoint = -1; +} + +MUTATOR_HOOKFUNCTION(ctscup, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + + if(spawn_spot.target == "") + // Emergency: this wasn't a real spawnpoint. Can this ever happen? + race_PreparePlayer(player); + + // if we need to respawn, do it right + player.race_respawn_checkpoint = player.race_checkpoint; + player.race_respawn_spotref = spawn_spot; + + player.race_place = 0; +} + +MUTATOR_HOOKFUNCTION(ctscup, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + if(!game_stopped) + { + if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn + race_PreparePlayer(player); + else // respawn + race_RetractPlayer(player); + + race_AbandonRaceCheck(player); + } +} + +MUTATOR_HOOKFUNCTION(ctscup, PlayerDamaged) +{ + int frag_deathtype = M_ARGV(5, int); + if (frag_deathtype == DEATH_KILL.m_id) + return true; // forbid logging damage +} + +MUTATOR_HOOKFUNCTION(ctscup, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.respawn_flags |= RESPAWN_FORCE; + race_AbandonRaceCheck(frag_target); + + if(autocvar_g_cts_removeprojectiles) + { + IL_EACH(g_projectiles, it.owner == frag_target && (it.flags & FL_PROJECTILE), + { + delete(it); + }); + } +} + +MUTATOR_HOOKFUNCTION(ctscup, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + bot.havocbot_role = havocbot_role_cts; + return true; +} + +MUTATOR_HOOKFUNCTION(ctscup, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + race_checkAndWriteName(player); + race_SpeedAwardFrame(player); +} + +MUTATOR_HOOKFUNCTION(ctscup, ForbidThrowCurrentWeapon) +{ + // no weapon dropping in CTS + return true; +} + +MUTATOR_HOOKFUNCTION(ctscup, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if (Item_IsLoot(item)) + { + return true; + } +} + +MUTATOR_HOOKFUNCTION(ctscup, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + + if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL.m_id) + if(!autocvar_g_cts_selfdamage) + { + frag_damage = 0; + M_ARGV(4, float) = frag_damage; + } +} + +MUTATOR_HOOKFUNCTION(ctscup, GetRecords) +{ + int record_page = M_ARGV(0, int); + string ret_string = M_ARGV(1, string); + + for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) + { + if(MapInfo_Get_ByID(i)) + { + float r = race_readTime(MapInfo_Map_bspname, 1); + + if(!r) + continue; + + string h = race_readName(MapInfo_Map_bspname, 1); + ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n"); + } + } + + M_ARGV(1, string) = ret_string; +} + +MUTATOR_HOOKFUNCTION(ctscup, ClientKill) +{ + M_ARGV(1, float) = 0; // kill delay +} + +MUTATOR_HOOKFUNCTION(ctscup, Race_FinalCheckpoint) +{ + entity player = M_ARGV(0, entity); + + // useful to prevent cheating by running back to the start line and starting out with more speed + if(autocvar_g_cts_finish_kill_delay) + ClientKill_Silent(player, autocvar_g_cts_finish_kill_delay); +} + +MUTATOR_HOOKFUNCTION(ctscup, HideTeamNagger) +{ + return true; // doesn't work so well (but isn't cts a teamless mode?) +} + +MUTATOR_HOOKFUNCTION(ctscup, FixClientCvars) +{ + entity player = M_ARGV(0, entity); + + stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n"); +} + +MUTATOR_HOOKFUNCTION(ctscup, WantWeapon) +{ + M_ARGV(1, float) = (M_ARGV(0, entity) == WEP_SHOTGUN); // want weapon = weapon info + M_ARGV(3, bool) = true; // want mutator blocked + return true; +} + +MUTATOR_HOOKFUNCTION(ctscup, ForbidDropCurrentWeapon) +{ + return true; +} + +// unused as of now, edit sv_ctscup.qh to use this if this becomes used +void ctscup_Initialize() +{ + cts_Initialize(); +} diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh new file mode 100644 index 000000000..6a554f4c8 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +//void ctscup_Initialize(); + +REGISTER_MUTATOR(ctscup, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + g_race_qualifying = 1; + independent_players = 1; + GameRules_limit_score(0); + GameRules_limit_lead(0); + + //ctscup_Initialize(); // commented due to not (yet) having + cts_Initialize(); // a reason to use its own init + } + return 0; +} +