From 753126c5c790f9199b77296062b7220f112e6a64 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Mon, 25 Sep 2023 16:14:48 -0700 Subject: [PATCH] Move gamerules over for override --- src/server/ctfitem.qc | 106 -------- src/server/gamerules_multiplayer.qc | 299 +++++++++++++++++++++++ src/server/gamerules_singleplayer.qc | 165 +++++++++++++ src/server/progs.src | 4 +- zpak001.pk3dir/particles/weapon_slam.cfg | 14 ++ 5 files changed, 480 insertions(+), 108 deletions(-) delete mode 100644 src/server/ctfitem.qc create mode 100644 src/server/gamerules_multiplayer.qc create mode 100644 src/server/gamerules_singleplayer.qc create mode 100644 zpak001.pk3dir/particles/weapon_slam.cfg diff --git a/src/server/ctfitem.qc b/src/server/ctfitem.qc deleted file mode 100644 index a41c8b2..0000000 --- a/src/server/ctfitem.qc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2023 Marco Cawthorne - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -class OP4CTFItem:NSRenderableEntity -{ -public: - void OP4CTFItem(void); - - virtual void Respawn(void); - virtual void Touch(entity); - nonvirtual bool CanPlayerGrabPowerup(entity); - virtual void SpawnKey(string, string); - -private: - int m_iItemID; - float m_iTeamID; - string m_strScoreIcon; - vector m_vecScoreColor; -}; - -void -OP4CTFItem::OP4CTFItem(void) -{ - m_iItemID = 0i; - m_strScoreIcon = __NULL__; - m_iTeamID = 0; - m_vecScoreColor = [1,1,1]; -} - -void -OP4CTFItem::Respawn(void) -{ - SetSolid(SOLID_TRIGGER); - SetMovetype(MOVETYPE_NONE); - SetOrigin(GetSpawnOrigin()); - SetModel(GetSpawnModel()); - SetSize([-16, -16, 0], [16, 16, 72]); - DropToFloor(); -} - -void -OP4CTFItem::SpawnKey(string strKey, string strValue) -{ - switch (strKey) { - case "goal_no": - m_iTeamID = ReadFloat(strValue); - break; - default: - super::SpawnKey(strKey, strValue); - } -} - -void -OP4CTFItem::Touch(entity toucherEntity) -{ - player pl = (player)toucherEntity; - - if (!m_iItemID) - return; - - if (!(toucherEntity.flags & FL_CLIENT)) - return; - - if (CanPlayerGrabPowerup(toucherEntity) == false) - return; - - pl.g_items |= m_iItemID; /* add to inventory */ - - forceinfokey(pl, "*icon2", m_strScoreIcon); - forceinfokey(pl, "*icon2_r", ftos(m_vecScoreColor[0])); - forceinfokey(pl, "*icon2_g", ftos(m_vecScoreColor[1])); - forceinfokey(pl, "*icon2_b", ftos(m_vecScoreColor[2])); - Destroy(); -} - -bool -OP4CTFItem::CanPlayerGrabPowerup(entity playerEntity) -{ - player pl = (player)playerEntity; - - if (pl.g_items & ITEM_CTF_JUMPPACK) - return false; - if (pl.g_items & ITEM_CTF_SHIELD) - return false; - if (pl.g_items & ITEM_CTF_HEALTH) - return false; - if (pl.g_items & ITEM_CTF_DEATH) - return false; - if (pl.g_items & ITEM_CTF_BACKPACK) - return false; - - return true; -} \ No newline at end of file diff --git a/src/server/gamerules_multiplayer.qc b/src/server/gamerules_multiplayer.qc new file mode 100644 index 0000000..7be8943 --- /dev/null +++ b/src/server/gamerules_multiplayer.qc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2016-2020 Marco Cawthorne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +const string mp_teamlist_fallback = "robo;hgrunt"; +var string autocvar_mp_teamlist = mp_teamlist_fallback; + +bool +HLMultiplayerRules::IsMultiplayer(void) +{ + return true; +} + +bool +HLMultiplayerRules::PlayerRequestRespawn(NSClientPlayer bp) +{ + if (bp.TimeSinceDeath() > 0.5f) { + bp.ScheduleThink(PutClientInServer, 0.0f); + return true; + } + + return false; +} + +bool +HLMultiplayerRules::IsTeamplay(void) +{ + return cvar("mp_teamplay") == 1 ? true : false; +} + +void +HLMultiplayerRules::InitPostEnts(void) +{ + MOTD_LoadDefault(); + + forceinfokey(world, "scorepoints", "0"); + + if (IsTeamplay() == true) { + int c; + + /* get the segments from our cvar */ + m_strTeamList = autocvar_mp_teamlist; + c = tokenizebyseparator(m_strTeamList, ";"); + + /* if we've got less than 2 teams, use the fallback... */ + if (c < 2) { + m_strTeamList = mp_teamlist_fallback; + c = tokenizebyseparator(m_strTeamList, ";"); + } + + forceinfokey(world, "teams", itos(c)); + + /* initialize all dem teams */ + for (int i = 0; i < c; i++) { + forceinfokey(world, sprintf("team_%i", i+1i), argv(i)); + forceinfokey(world, sprintf("teamscore_%i", i+1i), "0"); + } + } else { + forceinfokey(world, "teams", "0"); + } +} + +void +HLMultiplayerRules::FrameStart(void) +{ + if (cvar("timelimit")) + if (time >= (cvar("timelimit") * 60)) { + IntermissionStart(); + } + + IntermissionCycle(); +} + +void +HLMultiplayerRules::CheckRules(void) +{ + /* last person who killed somebody has hit the limit */ + if (cvar("fraglimit")) + if (g_dmg_eAttacker.frags >= cvar("fraglimit")) + IntermissionStart(); +} + +void +HLMultiplayerRules::PlayerDeath(NSClientPlayer pl) +{ + /* obituary networking */ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_OBITUARY); + WriteString(MSG_MULTICAST, (g_dmg_eAttacker.netname) ? g_dmg_eAttacker.netname : g_dmg_eAttacker.classname); + WriteString(MSG_MULTICAST, pl.netname); + WriteByte(MSG_MULTICAST, g_dmg_iWeapon); + WriteByte(MSG_MULTICAST, 0); + msg_entity = world; + multicast([0,0,0], MULTICAST_ALL); + + Plugin_PlayerObituary(g_dmg_eAttacker, g_dmg_eTarget, g_dmg_iWeapon, g_dmg_iHitBody, g_dmg_iDamage); + + /* death-counter */ + pl.deaths++; + pl.SetInfoKey("*deaths", ftos(pl.deaths)); + + /* update score-counter */ + if (pl.flags & FL_CLIENT || pl.flags & FL_MONSTER) + if (g_dmg_eAttacker.flags & FL_CLIENT) { + if (pl == g_dmg_eAttacker) + g_dmg_eAttacker.frags--; + else + g_dmg_eAttacker.frags++; + } + +#ifdef VALVE + /* explode all satchels */ + s_satchel_detonate((entity)pl); + /* drop their posessions into a weaponbox item */ + weaponbox_spawn((player)pl); +#endif + + /* either gib, or make a corpse */ + if (pl.health < -50) { + vector gibDir = vectoangles(pl.origin - g_dmg_eAttacker.origin); + float gibStrength = g_dmg_iDamage * 2.0f; + BreakModel_Entity(pl, gibDir, gibStrength); + } else { + FX_Corpse_Spawn((player)pl, ANIM_DIESIMPLE); + } + + /* now let's make the real client invisible */ + pl.Death(); + pl.SetTakedamage(DAMAGE_NO); + pl.gflags &= ~GF_FLASHLIGHT; + pl.gflags &= ~GF_EGONBEAM; + + Sound_Play(pl, CHAN_AUTO, "player.die"); + + /* force respawn */ + pl.ScheduleThink(PutClientInServer, 4.0f); + + /* have we gone over the fraglimit? */ + CheckRules(); +} + +void +HLMultiplayerRules::PlayerSpawn(NSClientPlayer pp) +{ + player pl = (player)pp; + string playerModel; + /* this is where the mods want to deviate */ + entity spot; + + pl.classname = "player"; + pl.SetMaxHealth(100); + pl.SetHealth(100); + pl.SetTakedamage(DAMAGE_YES); + pl.SetSolid(SOLID_SLIDEBOX); + pl.SetMovetype(MOVETYPE_WALK); + pl.AddFlags(FL_CLIENT); + pl.viewzoom = 1.0; + + /* player model selection */ + if (IsTeamplay() == true) { + int teamCount = tokenizebyseparator(m_strTeamList, ";"); + int playerTeam = (int)pl.GetTeam(); + + /* not part of a team? pick one of the ones we have */ + /* TODO: this should sort us into the lowest team */ + if (playerTeam == 0) { + playerTeam = 1i + (int)floor(random(0, (float)teamCount)); /* teams start at 1 after all */ + pl.SetTeam(playerTeam); + } + + /* assign our player model */ + playerModel = sprintf("models/player/%s/%s.mdl", argv(playerTeam - 1i), argv(playerTeam - 1i)); + } else { + /* interpret the 'model' InfoKey */ + playerModel = pl.GetInfoKey("model"); + + if (playerModel) { + playerModel = sprintf("models/player/%s/%s.mdl", playerModel, playerModel); + } + } + + /* fallback is always models/player.mdl for Half-Life */ + if not (whichpack(playerModel)) { + playerModel = "models/player.mdl"; + } + + pl.SetModel(playerModel); + pl.SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + pl.ClearVelocity(); + pl.gravity = __NULL__; + pl.SetFrame(1); + pl.SendFlags = UPDATE_ALL; + pl.SetInfoKey("*spec", "0"); + pl.SetInfoKey("*dead", "0"); + pl.SetInfoKey("*deaths", ftos(pl.deaths)); + pl.SetPropData("actor_human"); + pl.SetCanBleed(true); + + LevelNewParms(); + LevelDecodeParms(pl); + +#if defined (VALVE) || defined (GEARBOX) + pl.g_items = ITEM_CROWBAR | ITEM_GLOCK | ITEM_SUIT; + pl.activeweapon = WEAPON_GLOCK; + pl.glock_mag = 18; + pl.ammo_9mm = 44; +#elif defined (HL2) + pl.g_items = ITEM_CROWBAR | ITEM_PISTOL | ITEM_GRAVITYGUN | ITEM_SUIT; + pl.activeweapon = WEAPON_GRAVITYGUN; + pl.glock_mag = 18; + pl.ammo_9mm = 44; +#endif + + spot = Spawn_SelectRandom("info_player_deathmatch"); + pl.Transport(spot.origin, spot.angles); + Weapons_RefreshAmmo(pl); + Client_FixAngle(pl, pl.angles); +} + +bool +HLMultiplayerRules::ConsoleCommand(NSClientPlayer pp, string cmd) +{ + tokenize(cmd); + + switch (argv(0)) { + case "bot_add": + bot pete = (bot)Bot_AddQuick(); + Bot_RandomColormap(pete); + searchhandle pm = search_begin("models/player/*/*.mdl", TRUE, TRUE); + int r = floor(random(0, search_getsize(pm))); + string mdl = substring(search_getfilename(pm, r), 0, -5); + tokenizebyseparator(mdl, "/"); + pete.SetInfoKey("model", argv(2)); + search_end(pm); + break; + case "jumptest": + makevectors(pp.v_angle); + traceline(pp.origin + pp.view_ofs, pp.origin + pp.view_ofs + v_forward * 1024, FALSE, pp); + pp.velocity = Route_GetJumpVelocity(pp.origin, trace_endpos, pp.gravity); + break; + default: + return (false); + } + + return (true); +} + +bool +HLMultiplayerRules::MonstersSpawn(void) +{ + return (autocvar(mp_allowmonsters, 0)) ? true : false; +} + +void +HLMultiplayerRules::HLMultiplayerRules(void) +{ + /* these lines do nothing but tell the server to register those cvars */ + autocvar(timelimit, 15, "Timelimit for multiplayer rounds"); + autocvar(fraglimit, 15, "Points limit for multiplayer rounds"); +} + +void +CSEv_HLDM_Chooseteam_s(string teamName) +{ + HLGameRules rules = (HLGameRules)g_grMode; + player pl = (player)self; + + if (!teamName) + return; + if (rules.IsMultiplayer() == false) + return; + if (rules.IsTeamplay() == false) + return; + if (pl.IsDead() == true) + return; + + HLMultiplayerRules mprules = (HLMultiplayerRules)rules; + int c = tokenizebyseparator(mprules.m_strTeamList, ";"); + + for (int i = 0; i < c; i++) { + if (argv(i) == teamName) { + pl.SetTeam((float)i + 1); + Damage_Apply(pl, pl, 100, 0, DMG_SKIP_ARMOR); + return; + } + } +} diff --git a/src/server/gamerules_singleplayer.qc b/src/server/gamerules_singleplayer.qc new file mode 100644 index 0000000..b7c820a --- /dev/null +++ b/src/server/gamerules_singleplayer.qc @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2020 Marco Cawthorne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +bool +HLSingleplayerRules::IsMultiplayer(void) +{ + return false; +} + +void +HLSingleplayerRules::PlayerDeath(NSClientPlayer pl) +{ + pl.SetMovetype(MOVETYPE_NONE); + pl.SetSolid(SOLID_NOT); + pl.SetTakedamage(DAMAGE_NO); + pl.SetHealth(0); + pl.StartSoundDef("player.die", CHAN_AUTO, true); + + pl.gflags &= ~GF_FLASHLIGHT; + pl.armor = pl.activeweapon = pl.g_items = pl.weapon = 0; + + if (cvar("coop") == 1) { + pl.ScheduleThink(PutClientInServer, 4.0f); + } + + /* so much damage we're gonna gib */ + if (pl.GetHealth() < -50) { + //pl.Gib(); + //FX_GibHuman(pl.origin, vectoangles(pl.origin - g_dmg_eAttacker.origin), g_dmg_iDamage * 2.0f); + } + + /* Let's handle corpses on the clientside */ + entity corpse = spawn(); + setorigin(corpse, pl.origin + [0,0,32]); + setmodel(corpse, pl.model); + setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); + corpse.movetype = MOVETYPE_TOSS; + corpse.solid = SOLID_TRIGGER; + corpse.modelindex = pl.modelindex; + corpse.frame = ANIM_DIESIMPLE; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; +} + +void +HLSingleplayerRules::PlayerSpawn(NSClientPlayer pl) +{ + string playerModel = "models/player.mdl"; + + pl.classname = "player"; + pl.SetHealth(100); + pl.SetMaxHealth(100); + pl.SetTakedamage(DAMAGE_YES); + pl.SetSolid(SOLID_SLIDEBOX); + pl.SetMovetype(MOVETYPE_WALK); + pl.AddFlags(FL_CLIENT); + pl.viewzoom = 1.0; + + /* if in cooperative mode, we want to respect the player model */ + if (cvar("coop") == 1) { + string testModel = infokey(pl, "model"); + if (testModel) { + testModel = sprintf("models/player/%s/%s.mdl", testModel, testModel); + if (whichpack(testModel)) { + playerModel = testModel; + } + } + } + + pl.SetModel(playerModel); + pl.SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + pl.ClearVelocity(); + pl.SetInfoKey("*spec", "0"); + pl.SetInfoKey("*deaths", ftos(pl.deaths)); + pl.SetCanBleed(true); + + if (startspot != "") { + LevelDecodeParms(pl); + pl.SetOrigin(Landmark_GetSpot()); + } else { + entity spawnPoint; + LevelNewParms(); + spawnPoint = find(world, ::classname, "info_player_start"); + pl.Transport(spawnPoint.origin, spawnPoint.angles); + } + + Weapons_RefreshAmmo(pl); + Client_FixAngle(pl, pl.angles); +} + +bool +HLSingleplayerRules::ImpulseCommand(NSClient bp, float num) +{ + switch (num) { + case 101: + player pl = (player)bp; + pl.SetHealth(100); + pl.SetMaxHealth(100); + pl.SetArmor(100); + pl.g_items |= ITEM_SUIT; +#ifdef HL2 + Weapons_AddItem(pl, WEAPON_CROWBAR, -1); + Weapons_AddItem(pl, WEAPON_357, -1); + Weapons_AddItem(pl, WEAPON_AR2, -1); + Weapons_AddItem(pl, WEAPON_BUGBAIT, -1); + Weapons_AddItem(pl, WEAPON_CROSSBOW, -1); + Weapons_AddItem(pl, WEAPON_CROWBAR, -1); + Weapons_AddItem(pl, WEAPON_FRAG, -1); + Weapons_AddItem(pl, WEAPON_GRAVITYGUN, -1); + Weapons_AddItem(pl, WEAPON_PISTOL, -1); + Weapons_AddItem(pl, WEAPON_RPG, -1); + Weapons_AddItem(pl, WEAPON_SHOTGUN, -1); + Weapons_AddItem(pl, WEAPON_SMG1, -1); + Weapons_AddItem(pl, WEAPON_STUNSTICK, -1); + Weapons_AddItem(pl, WEAPON_SLAM, -1); + pl.ammo_m203_grenade = 10; +#else + Weapons_AddItem(pl, WEAPON_CROWBAR, -1); + Weapons_AddItem(pl, WEAPON_GLOCK, -1); + Weapons_AddItem(pl, WEAPON_PYTHON, -1); + Weapons_AddItem(pl, WEAPON_MP5, -1); + Weapons_AddItem(pl, WEAPON_SHOTGUN, -1); + Weapons_AddItem(pl, WEAPON_CROSSBOW, -1); + Weapons_AddItem(pl, WEAPON_RPG, -1); + Weapons_AddItem(pl, WEAPON_GAUSS, -1); + Weapons_AddItem(pl, WEAPON_EGON, -1); + Weapons_AddItem(pl, WEAPON_HORNETGUN, -1); + Weapons_AddItem(pl, WEAPON_HANDGRENADE, -1); + Weapons_AddItem(pl, WEAPON_SATCHEL, -1); + Weapons_AddItem(pl, WEAPON_TRIPMINE, -1); + Weapons_AddItem(pl, WEAPON_SNARK, -1); + pl.ammo_m203_grenade = 10; +#ifdef GEARBOX + Weapons_AddItem(pl, WEAPON_PIPEWRENCH, -1); + Weapons_AddItem(pl, WEAPON_KNIFE, -1); + Weapons_AddItem(pl, WEAPON_GRAPPLE, -1); + Weapons_AddItem(pl, WEAPON_EAGLE, -1); + Weapons_AddItem(pl, WEAPON_PENGUIN, -1); + Weapons_AddItem(pl, WEAPON_M249, -1); + Weapons_AddItem(pl, WEAPON_DISPLACER, -1); + Weapons_AddItem(pl, WEAPON_SNIPERRIFLE, -1); + Weapons_AddItem(pl, WEAPON_SPORELAUNCHER, -1); + Weapons_AddItem(pl, WEAPON_SHOCKRIFLE, -1); +#endif +#endif + break; + default: + return super::ImpulseCommand(bp, num); + } + + return true; +} diff --git a/src/server/progs.src b/src/server/progs.src index 1f4e457..c3ecd5f 100644 --- a/src/server/progs.src +++ b/src/server/progs.src @@ -38,8 +38,8 @@ defs.h ../../../src/botlib/include.src gamerules.qc -../../../valve/src/server/gamerules_singleplayer.qc -../../../valve/src/server/gamerules_multiplayer.qc +gamerules_singleplayer.qc +gamerules_multiplayer.qc server.qc ../../../valve/src/server/damage.qc ../../../valve/src/server/flashlight.qc diff --git a/zpak001.pk3dir/particles/weapon_slam.cfg b/zpak001.pk3dir/particles/weapon_slam.cfg new file mode 100644 index 0000000..dcd9cb0 --- /dev/null +++ b/zpak001.pk3dir/particles/weapon_slam.cfg @@ -0,0 +1,14 @@ +r_part beam +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + scale 1 + scaledelta 0.5 + alpha 0.4 + step 4 + randomvel 0 + rgb 255 0 0 + rgbdelta -150 0 0 + type beam + blend add +}