From 29281e810198e07f97d23c950582522023f87add Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Mon, 8 Jan 2024 15:34:11 -0800 Subject: [PATCH] Bot improvements, some more work done on the grenades. --- src/client/hallucination.qc | 91 ++++++++++++++++++++++++----- src/server/bot.qc | 21 +++++++ src/server/defs.h | 5 ++ src/server/gamerules.h | 1 + src/server/gamerules.qc | 67 ++++++++++++++++----- src/server/info_player_teamspawn.qc | 2 + src/server/info_tfgoal.qc | 9 +++ src/server/item_armor.qc | 1 + src/server/item_healthkit.qc | 1 + src/server/item_tfgoal.qc | 11 ++++ src/server/nades.qc | 22 +++++++ src/server/progs.src | 5 +- src/server/spawn.qc | 67 ++++++++++++++++++++- src/shared/player.qc | 2 +- 14 files changed, 270 insertions(+), 35 deletions(-) create mode 100644 src/server/bot.qc diff --git a/src/client/hallucination.qc b/src/client/hallucination.qc index 30e197f..99ad049 100644 --- a/src/client/hallucination.qc +++ b/src/client/hallucination.qc @@ -17,20 +17,22 @@ TFCHallucination_Insert(vector viewPosition, vector viewDirection) traceline(viewPosition, halluPos, MOVE_NORMAL, pSeat->m_ePlayer); halluPos = trace_endpos; - makevectors(viewDirection); + makevectors([0, viewDirection[1], 0]); - r = (int)(floor(random(0, 8))); + r = (int)(floor(random(0, 10))); switch (r) { - case 1: + case 1: /* blood splats */ for (int i = 0; i < 3; i++) { + halluPos = viewPosition; + halluPos += v_forward * random(8, 64); + halluPos += v_up * random(-64, 64); + halluPos += v_right * random(-64, 64); FX_Blood(halluPos, [1,0,0]); - halluPos += v_forward * random(8, 128); - halluPos += v_up * random(-64, 128); - halluPos += v_right * random(-256, 256); } + pointsound(viewPosition, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break; - case 2: + case 2: /* explosions */ pointparticles(particleeffectnum("fx_explosion.main"), halluPos, [0,0,0], 1); pSeat->m_flShakeDuration = 2; pSeat->m_flShakeAmp = 5.0; @@ -38,46 +40,105 @@ TFCHallucination_Insert(vector viewPosition, vector viewDirection) pSeat->m_flShakeTime = 2; pointsound(halluPos, sprintf("weapons/explode%d.wav", floor(random() * 2) + 3), 1, ATTN_NORM); break; - case 3: + case 3: /* shotgun impact */ traceline(viewPosition, halluPos + (v_forward * 1024), MOVE_NORMAL, pSeat->m_ePlayer); SurfData_Impact(world, trace_endpos, [0,0,0]); DecalGroups_Place("Impact.Shot", trace_endpos); pointsound(halluPos, "weapons/sbarrel1.wav", 1, ATTN_NORM); break; - case 4: + case 4: /* being hit by a crowbar */ pSeat->m_flShakeDuration = 1; pSeat->m_flShakeAmp = 1.0; pSeat->m_flShakeFreq = 2; pSeat->m_flShakeTime = 2; pointsound(halluPos, "weapons/cbar_hitbod1.wav", 1, ATTN_NORM); break; - case 5: + case 5: /* sniper impact */ traceline(viewPosition, halluPos + (v_forward * 1024), MOVE_NORMAL, pSeat->m_ePlayer); SurfData_Impact(world, trace_endpos, [0,0,0]); DecalGroups_Place("Impact.Shot", trace_endpos); pointsound(halluPos, "weapons/sniper.wav", 1, ATTN_NORM); break; - case 6: + case 6: /* nailgun impact */ traceline(viewPosition, halluPos + (v_forward * 1024), MOVE_NORMAL, pSeat->m_ePlayer); SurfData_Impact(world, trace_endpos, [0,0,0]); DecalGroups_Place("Impact.Shot", trace_endpos); pointsound(halluPos, "weapons/airgun_1.wav", 1, ATTN_NORM); break; - case 7: + case 7: /* nade thrown about */ + int nadeSelection = (int)floor(random(0, 8)); NSRenderableEntity eNade = spawn(NSRenderableEntity); - eNade.SetModel("models/w_grenade.mdl"); - eNade.SetOrigin(halluPos); + + switch (nadeSelection) { + case 1: + eNade.SetModel("models/emp_grenade.mdl"); + break; + case 2: + eNade.SetModel("models/spy_grenade.mdl"); + break; + case 3: + eNade.SetModel("models/napalm.mdl"); + break; + case 4: + eNade.SetModel("models/mirv_grenade.mdl"); + break; + case 5: + eNade.SetModel("models/ngrenade.mdl"); + break; + case 6: + eNade.SetModel("models/conc_grenade.mdl"); + break; + case 7: + eNade.SetModel("models/caltrop.mdl"); + break; + default: + eNade.SetModel("models/w_grenade.mdl"); + } + eNade.SetOrigin(viewPosition + (v_forward * -32) + (v_right * random(-64, 64))); eNade.SetMovetype(MOVETYPE_BOUNCE); eNade.SetSolid(SOLID_NOT); eNade.SetGravity(1.0f); - eNade.SetVelocity(v_forward * random(-320,320) + v_right * random(-64, 64) + v_up * 200); + eNade.SetVelocity(v_forward * 300 + v_right * random(-64, 64) + v_up * 200); eNade.SetAngularVelocity([300, 300, 300]); eNade.SetAngles(vectoangles(eNade.GetVelocity())); eNade.ScheduleThink(eNade.Destroy, 5.0f); eNade.drawmask = MASK_ENGINE; pointsound(halluPos, "weapons/grenade_hit3.wav", 1, ATTN_NORM); break; + case 8: /* nail shot around */ + halluPos = viewPosition + (v_forward * -32) + (v_right * random(-64, 64)); + NSRenderableEntity eNail = spawn(NSRenderableEntity); + eNail.SetModel("models/nail.mdl"); + eNail.SetOrigin(halluPos); + eNail.SetMovetype(MOVETYPE_NOCLIP); + eNail.SetSolid(SOLID_NOT); + eNail.SetGravity(1.0f); + eNail.SetVelocity(v_forward * 800 + v_right * random(-800, 800)); + eNail.SetAngles(vectoangles(eNail.GetVelocity())); + eNail.ScheduleThink(eNail.Destroy, 5.0f); + eNail.drawmask = MASK_ENGINE; + pointsound(halluPos, "weapons/airgun_1.wav", 1, ATTN_NORM); + break; + case 9: /* fire eruption */ + /* get it close to the player than usual */ + halluPos = viewPosition; + halluPos += v_forward * random(8, 64); + halluPos += v_up * random(-16, 16); + halluPos += v_right * random(-64, 64); + + env_sprite eFired = spawn(env_sprite); + eFired.SetOrigin(halluPos); + eFired.SetModel("sprites/fthrow.spr"); + eFired.SetMaxFrame(modelframecount(eFired.modelindex)); + eFired.SetFramerate(20); + eFired.SetLoopFlag(false); + eFired.SetRenderMode(RM_ADDITIVE); + eFired.SetRenderAmt(1.0f); + eFired.nextthink = time + 0.05f; + pointsound(halluPos, "weapons/flmfire2.wav", 1, ATTN_NORM); + break; default: + /* rocket launcher firing */ pointsound(halluPos, "weapons/rocketfire1.wav", 1, ATTN_NORM); break; } diff --git a/src/server/bot.qc b/src/server/bot.qc new file mode 100644 index 0000000..db24066 --- /dev/null +++ b/src/server/bot.qc @@ -0,0 +1,21 @@ +class +TFCBot:bot +{ + void TFCBot(void); + + //virtual void CreateObjective(void); +}; + +void +TFCBot::TFCBot(void) +{ + +} + +#if 0 +void +TFCBot::CreateObjective(void) +{ + super:: CreateObjective(); +} +#endif \ No newline at end of file diff --git a/src/server/defs.h b/src/server/defs.h index 37feccf..96dac0a 100644 --- a/src/server/defs.h +++ b/src/server/defs.h @@ -19,6 +19,11 @@ #include "../../../valve/src/server/flashlight.h" #include "sentry.h" +var bool g_tfcHasBlueTeam = false; +var bool g_tfcHasRedTeam = false; +var bool g_tfcHasYellowTeam = false; +var bool g_tfcHasGreenTeam = false; + /* returns if a player already has a teleporter/exit built */ bool TFC_ExistsForPlayer(entity pl, string cname) diff --git a/src/server/gamerules.h b/src/server/gamerules.h index 2579e65..4d1fab9 100644 --- a/src/server/gamerules.h +++ b/src/server/gamerules.h @@ -18,6 +18,7 @@ class TFCGameRules:CGameRules { void(void) TFCGameRules; + virtual bool ConsoleCommand(NSClientPlayer, string); virtual bool IsTeamplay(void); virtual void PlayerConnect(NSClientPlayer); diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc index f67bc8c..b6f7e9e 100644 --- a/src/server/gamerules.qc +++ b/src/server/gamerules.qc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Marco Cawthorne + * Copyright (c) 2016-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 @@ -14,6 +14,52 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +void CSEv_TeamJoin_f(float f); +void CSEv_ClassJoin_f(float f); + +void +TFCGameRules_BotJoin(void) +{ + spawnfunc_TFCBot(); + CSEv_TeamJoin_f(0); + CSEv_ClassJoin_f(0); +} + +void +TFCGameRules::TFCGameRules(void) +{ + /* wipe the serverinfo clean */ + forceinfokey(world, "teams", "0"); + forceinfokey(world, "team_1", ""); + forceinfokey(world, "team_2", ""); + forceinfokey(world, "team_3", ""); + forceinfokey(world, "team_4", ""); + forceinfokey(world, "teamscore_1", ""); + forceinfokey(world, "teamscore_2", ""); + forceinfokey(world, "teamscore_3", ""); + forceinfokey(world, "teamscore_4", ""); +} + +bool +TFCGameRules::ConsoleCommand(NSClientPlayer pp, string cmd) +{ + tokenize(cmd); + + switch (argv(0)) { + case "bot_add": + entity bot_ent = Bot_AddQuick(); + if (bot_ent) { + bot_ent.think = TFCGameRules_BotJoin; + bot_ent.nextthink = time; + } + break; + default: + return (false); + } + + return (true); +} + bool TFCGameRules::IsTeamplay(void) { @@ -182,6 +228,7 @@ TFCGameRules::InitPostEnts(void) team_count += 1; forceinfokey(world, sprintf("team_%i", team_count), "Blue"); forceinfokey(world, sprintf("teamscore_%i", team_count), "0"); + g_tfcHasBlueTeam = true; } e = find(world, ::classname, "info_teamspawn_red"); @@ -189,6 +236,7 @@ TFCGameRules::InitPostEnts(void) team_count += 1; forceinfokey(world, sprintf("team_%i", team_count), "Red"); forceinfokey(world, sprintf("teamscore_%i", team_count), "0"); + g_tfcHasRedTeam = true; } e = find(world, ::classname, "info_teamspawn_green"); @@ -196,6 +244,7 @@ TFCGameRules::InitPostEnts(void) team_count += 1; forceinfokey(world, sprintf("team_%i", team_count), "Green"); forceinfokey(world, sprintf("teamscore_%i", team_count), "0"); + g_tfcHasGreenTeam = true; } e = find(world, ::classname, "info_teamspawn_yellow"); @@ -203,22 +252,8 @@ TFCGameRules::InitPostEnts(void) team_count += 1; forceinfokey(world, sprintf("team_%i", team_count), "Yellow"); forceinfokey(world, sprintf("teamscore_%i", team_count), "0"); + g_tfcHasYellowTeam = true; } forceinfokey(world, "teams", itos(team_count)); } - -void -TFCGameRules::TFCGameRules(void) -{ - /* wipe the serverinfo clean */ - forceinfokey(world, "teams", "0"); - forceinfokey(world, "team_1", ""); - forceinfokey(world, "team_2", ""); - forceinfokey(world, "team_3", ""); - forceinfokey(world, "team_4", ""); - forceinfokey(world, "teamscore_1", ""); - forceinfokey(world, "teamscore_2", ""); - forceinfokey(world, "teamscore_3", ""); - forceinfokey(world, "teamscore_4", ""); -} diff --git a/src/server/info_player_teamspawn.qc b/src/server/info_player_teamspawn.qc index abc372b..1433498 100644 --- a/src/server/info_player_teamspawn.qc +++ b/src/server/info_player_teamspawn.qc @@ -47,6 +47,8 @@ info_player_teamspawn::info_player_teamspawn(void) classname = "info_teamspawn_green"; break; } + + botinfo = BOTINFO_SPAWNPOINT; } CLASSEXPORT(i_p_t, info_player_teamspawn) diff --git a/src/server/info_tfgoal.qc b/src/server/info_tfgoal.qc index 10eae5a..0da3aae 100644 --- a/src/server/info_tfgoal.qc +++ b/src/server/info_tfgoal.qc @@ -179,6 +179,7 @@ info_tfgoal::Touch(entity eToucher) /* mark as removed on the player end, too. */ pl.g_items &= ~ITEM_GOALITEM; + pl.flags &= ~FL_GOALITEM; forceinfokey(pl, "*goalitem_t", ""); } @@ -286,6 +287,10 @@ info_tfgoal::Respawn(void) SetModel(GetSpawnModel()); SetSize(VEC_HULL_MIN, VEC_HULL_MAX); SetOrigin(GetSpawnOrigin()); + team = m_iTeamUses; + + if (frags > 0) + botinfo = BOTINFO_TEAM_GOALCAPTURE; } void @@ -446,6 +451,10 @@ i_t_g::Respawn(void) SetModel(GetSpawnModel()); SetOrigin(GetSpawnOrigin()); Hide(); + team = m_iTeamUses; + + if (frags > 0) + botinfo = BOTINFO_TEAM_GOALCAPTURE; } void diff --git a/src/server/item_armor.qc b/src/server/item_armor.qc index f04d5ca..580e558 100644 --- a/src/server/item_armor.qc +++ b/src/server/item_armor.qc @@ -167,6 +167,7 @@ item_armor::Respawn(void) SetSolid(SOLID_TRIGGER); SetOrigin(GetSpawnOrigin()); DropToFloor(); + botinfo = BOTINFO_ARMOR; } void diff --git a/src/server/item_healthkit.qc b/src/server/item_healthkit.qc index 4c3c2de..03ef8d0 100644 --- a/src/server/item_healthkit.qc +++ b/src/server/item_healthkit.qc @@ -90,6 +90,7 @@ item_healthkit::Respawn(void) SetSolid(SOLID_TRIGGER); SetOrigin(GetSpawnOrigin()); DropToFloor(); + botinfo = BOTINFO_HEALTH; } void diff --git a/src/server/item_tfgoal.qc b/src/server/item_tfgoal.qc index 51c35cd..6f9b31a 100644 --- a/src/server/item_tfgoal.qc +++ b/src/server/item_tfgoal.qc @@ -158,6 +158,7 @@ item_tfgoal::Touch(entity eToucher) Disappear(); pl.g_items |= ITEM_GOALITEM; + pl.flags |= FL_GOALITEM; forceinfokey(pl, "*goalitem_t", itos(m_iTeamOwner)); m_eActivator = pl; @@ -221,6 +222,16 @@ item_tfgoal::Respawn(void) ReleaseThink(); m_status = GISTATUS_HOME; + /* helps with bots */ + team = m_iTeamOwner; + + /* it's either a goal item... or some other pickup */ + if (m_dItemID) { + botinfo = BOTINFO_TEAM_GOALITEM; + } else { + botinfo = BOTINFO_AMMO; + } + if (m_iTeamOwner) { SetRenderAmt(1.0f); SetRenderFX(RFX_GLOWSHELL); diff --git a/src/server/nades.qc b/src/server/nades.qc index 9a62ff6..1a0b345 100644 --- a/src/server/nades.qc +++ b/src/server/nades.qc @@ -103,6 +103,28 @@ TFCNade_ThrowConcussion(player pl) } } + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_BEAMCYLINDER); + WriteCoord(MSG_MULTICAST, self.origin[0]); + WriteCoord(MSG_MULTICAST, self.origin[1]); + WriteCoord(MSG_MULTICAST, self.origin[2]); + WriteCoord(MSG_MULTICAST, 0); + WriteCoord(MSG_MULTICAST, 128); + WriteCoord(MSG_MULTICAST, 0); + WriteShort(MSG_MULTICAST, getmodelindex("sprites/shockwave.spr")); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 10); + WriteByte(MSG_MULTICAST, 2); + WriteByte(MSG_MULTICAST, 16); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 0); + msg_entity = self; + multicast(self.origin, MULTICAST_PVS); + NSEntity::Destroy(); } diff --git a/src/server/progs.src b/src/server/progs.src index 15da28a..df8ae2e 100644 --- a/src/server/progs.src +++ b/src/server/progs.src @@ -31,6 +31,9 @@ func_nogrenades.qc vox.qc ../../../valve/src/server/items.qc +../../../src/botlib/include.src +bot.qc + info_player_teamspawn.qc item_tfgoal.qc info_tfgoal.qc @@ -46,8 +49,6 @@ server.qc ../../../valve/src/server/flashlight.qc ../../../valve/src/server/modelevent.qc -../../../src/botlib/include.src - spawn.qc ../../../src/server/include.src diff --git a/src/server/spawn.qc b/src/server/spawn.qc index d085c1d..ea5a518 100644 --- a/src/server/spawn.qc +++ b/src/server/spawn.qc @@ -14,11 +14,77 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +float +TFCTeamJoin_SmallestTeam(int blue, int red, int yellow, int green) +{ + while (blue > 0i || red > 0i || yellow > 0i || green > 0i) { + /* the first team that goes to 0, gets picked. */ + if (blue <= 0i) + return 1; + else if (red <= 0i) + return 2; + else if (yellow <= 0i) + return 3; + else if (green <= 0i) + return 4; + + blue--; + red--; + yellow--; + green--; + } + + /* the first team that goes to 0, gets picked. */ + if (blue == 0i) + return 1; + else if (red == 0i) + return 2; + else if (yellow == 0i) + return 3; + else if (green == 0i) + return 4; + + return 0; +} + void CSEv_TeamJoin_f(float f) { player pl = (player)self; + /* random... */ + if (f == 0) { + int bluePlayers = 0i; + int redPlayers = 0i; + int yellowPlayers = 0i; + int greenPlayers = 0i; + + /* hack to get it to never pick this team */ + if (g_tfcHasBlueTeam == false) + bluePlayers = (int)cvar("sv_playerslots"); + if (g_tfcHasRedTeam == false) + redPlayers = (int)cvar("sv_playerslots"); + if (g_tfcHasYellowTeam == false) + yellowPlayers = (int)cvar("sv_playerslots"); + if (g_tfcHasGreenTeam == false) + greenPlayers = (int)cvar("sv_playerslots"); + + /* count all valid players within each team */ + for (entity e = world; (e = find(e, ::classname, "player"));) { + if (e.team == 1) + bluePlayers++; + else if (e.team == 2) + redPlayers++; + else if (e.team == 3) + yellowPlayers++; + else if (e.team == 4) + greenPlayers++; + } + + /* assign us to the team with the lowest amount of players */ + f = TFCTeamJoin_SmallestTeam(bluePlayers, redPlayers, yellowPlayers, greenPlayers); + } + /* mess, do it better */ if (f == 1) { pl.team = 1; /* Blue */ @@ -40,7 +106,6 @@ CSEv_TeamJoin_f(float f) forceinfokey(pl, "topcolor", "0x3bff00"); forceinfokey(pl, "bottomcolor", "0x3bff00"); } else { - /* invalid */ return; } diff --git a/src/shared/player.qc b/src/shared/player.qc index 73a4e9f..2ee2072 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -420,7 +420,7 @@ player::ReceiveEntity(float new, float flChanged) return; TFCHallucination_Insert(origin, v_angle); - m_flNextHallucination = time + 0.5f + random(); + m_flNextHallucination = time + 0.25f + random(0.25,0.75); } }