nuclide/cstrike/src/server/gamerules_multiplayer.qc

1022 lines
23 KiB
Plaintext

/*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
*
* 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.
*/
int
CSMultiplayerRules::MaxItemPerSlot(int slot)
{
/* grenades */
if (slot == 3) {
return 3;
}
return 1;
}
void
CSMultiplayerRules::PlayerDeath(base_player pl)
{
/* obituary networking */
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_OBITUARY);
if (g_dmg_eAttacker.netname)
WriteString(MSG_MULTICAST, g_dmg_eAttacker.netname);
else
WriteString(MSG_MULTICAST, 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);
/* death-counter */
pl.deaths++;
forceinfokey(pl, "*deaths", ftos(pl.deaths));
/* update score-counter */
if (g_dmg_eTarget.flags & FL_CLIENT || g_dmg_eTarget.flags & FL_MONSTER)
if (g_dmg_eAttacker.flags & FL_CLIENT) {
if (g_dmg_eTarget == g_dmg_eAttacker || g_dmg_eTarget.team == g_dmg_eAttacker.team)
g_dmg_eAttacker.frags--;
else
g_dmg_eAttacker.frags++;
}
Weapon_DropCurrentWeapon(pl);
/* if we're the bomb carrier, make sure we drop the bomb. */
if (pl.g_items & ITEM_C4BOMB) {
pl.activeweapon = WEAPON_C4BOMB;
Weapon_DropCurrentWeapon(pl);
}
/* clear all ammo and inventory... */
PlayerClearWeaponry(pl);
pl.movetype = MOVETYPE_NONE;
pl.solid = SOLID_NOT;
pl.takedamage = DAMAGE_NO;
pl.gflags &= ~GF_FLASHLIGHT;
pl.armor = 0;
pl.health = 0;
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_DEATH1;
corpse.angles = pl.angles;
corpse.velocity = pl.velocity;
corpse.classname = "remove_me";
/* gamerule stuff */
PlayerMakeSpectator(pl);
pl.classname = "player";
forceinfokey(pl, "*dead", "1");
forceinfokey(pl, "*team", ftos(pl.team));
CountPlayers();
/* In Assassination, all Terrorists receive a $2500
* reward if they won by killing the VIP. */
if (self.team == TEAM_VIP) {
RoundOver(TEAM_T, 2500, FALSE);
return;
}
DeathCheck(pl);
}
void
CSMultiplayerRules::PlayerPreFrame(base_player pl)
{
player pp = (player)pl;
if (pl.health <= 0)
return;
if (g_cs_gamestate == GAME_FREEZE || pp.progress > 0.0f) {
pl.flags |= FL_FROZEN;
}
}
void
CSMultiplayerRules::FrameStart(void)
{
if ((g_cs_alive_t + g_cs_alive_ct) == 0) {
int iInGamePlayers = 0;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
iInGamePlayers++;
}
if ((iInGamePlayers > 0) && (g_cs_gamestate != GAME_COMMENCING && g_cs_gamestate != GAME_END)) {
TimerBegin(2, GAME_COMMENCING);
} else if (iInGamePlayers == 0) {
g_cs_gamestate = GAME_INACTIVE;
g_cs_gametime = 0;
g_cs_roundswon_t = 0;
g_cs_roundswon_ct = 0;
g_cs_roundsplayed = 0;
} else {
TimerUpdate(); // Timer that happens once players have started joining
}
} else {
TimerUpdate(); // Normal gameplay timer
}
}
void
CSMultiplayerRules::CreateRescueZones(void)
{
int zones = 0;
/* not in hostage rescue mode */
if (g_cs_hostagestotal <= 0) {
return;
}
/* count the already existing rescue zones. */
for (entity e = world; (e = find(e, ::classname, "func_hostage_rescue"));) {
zones++;
}
/* we don't need to create any additional rescue zones. */
if (zones > 0)
return;
/* since no buyzones are available, let's create one around every CT spawn */
for (entity e = world; (e = find(e, ::classname, "info_player_start"));) {
info_hostage_rescue newzone = spawn(info_hostage_rescue);
setorigin(newzone, e.origin);
}
}
void
CSMultiplayerRules::CreateCTBuyzones(void)
{
int zones = 0;
/* count the already existing CT zones. */
for (entity e = world; (e = find(e, ::classname, "func_buyzone"));) {
if (e.team == TEAM_CT) {
zones++;
}
}
/* we don't need to create any additional CT zones. */
if (zones > 0)
return;
/* since no buyzones are available, let's create one around every CT spawn */
for (entity e = world; (e = find(e, ::classname, "info_player_start"));) {
info_buyzone newzone = spawn(info_buyzone);
setorigin(newzone, e.origin);
newzone.team = TEAM_CT;
}
}
void
CSMultiplayerRules::CreateTBuyzones(void)
{
int zones = 0;
/* count the already existing T zones. */
for (entity e = world; (e = find(e, ::classname, "func_buyzone"));) {
if (e.team == TEAM_T) {
zones++;
}
}
/* we don't need to create any additional T zones. */
if (zones > 0)
return;
/* since no buyzones are available, let's create one around every T spawn */
for (entity e = world; (e = find(e, ::classname, "info_player_deathmatch"));) {
info_buyzone newzone = spawn(info_buyzone);
setorigin(newzone, e.origin);
newzone.team = TEAM_T;
}
}
void
CSMultiplayerRules::InitPostEnts(void)
{
/* let's check if we need to create buyzones */
switch (g_cstrike_buying) {
case BUY_CT:
CreateCTBuyzones();
break;
case BUY_T:
CreateTBuyzones();
break;
case BUY_NEITHER:
break;
default:
CreateCTBuyzones();
CreateTBuyzones();
}
CreateRescueZones();
}
void
CSMultiplayerRules::TimerBegin(float tleft, int mode)
{
if (mode == GAME_FREEZE) {
g_cs_gamestate = GAME_FREEZE;
} else if (mode == GAME_ACTIVE) {
g_cs_gamestate = GAME_ACTIVE;
} else if (mode == GAME_END) {
g_cs_gamestate = GAME_END;
} else if (mode == GAME_COMMENCING) {
g_cs_gamestate = GAME_COMMENCING;
} else if (mode == GAME_OVER) {
g_cs_gamestate = GAME_OVER;
}
g_cs_gametime = tleft;
}
void
CSMultiplayerRules::TimerUpdate(void)
{
if (cvar("sv_playerslots") == 1) {
g_cs_gametime = -1;
return;
}
if (g_cs_hostagestotal > 0) {
if (g_cs_hostagesrescued >= g_cs_hostagestotal) {
RoundOver(TEAM_CT, 0, FALSE);
return;
}
}
// This map has been played enough we think
if (g_cs_gamestate != GAME_OVER) {
if (cvar("mp_timelimit") > 0) {
if (time >= (cvar("mp_timelimit") * 60)) {
IntermissionStart();
g_cs_gamestate = GAME_OVER;
}
}
}
// Okay, this means that timelimit is not the only deciding factor
if (autocvar_mp_winlimit > 0 && g_cs_gamestate != GAME_OVER) {
// It really doesn't matter who won. Do some logging perhaps?
if (g_cs_roundswon_ct == autocvar_mp_winlimit) {
IntermissionStart();
} else if (g_cs_roundswon_t == autocvar_mp_winlimit) {
IntermissionStart();
}
}
if (g_cs_gamestate == GAME_INACTIVE) {
return;
}
if (g_cs_gametime > 0) {
g_cs_gametime -= frametime;
} else {
g_cs_gametime = 0;
}
if (g_cs_gamestate == GAME_COMMENCING || g_cs_gamestate == GAME_END) {
if (g_cs_gametime <= 0) {
if (g_cs_roundswon_t == 0 && g_cs_roundswon_ct == 0) {
Money_ResetTeamReward();
Money_ResetRoundReward();
RestartRound(TRUE);
} else {
if (autocvar_mp_halftime == TRUE && (autocvar_mp_winlimit / 2 == g_cs_roundsplayed)) {
Money_ResetTeamReward();
SwitchTeams();
RestartRound(TRUE);
} else {
RestartRound(FALSE);
}
}
}
return;
}
if ((g_cs_gamestate == GAME_ACTIVE) || (g_cs_gamestate == GAME_FREEZE)) {
if (g_cs_gametime <= 0) {
if (g_cs_gamestate == GAME_ACTIVE) {
/* 1.5 will make the T's lose if time runs out no matter what */
if (autocvar_fcs_fix_bombtimer == TRUE) {
if (g_cs_bombzones > 0 && g_cs_bombplanted == TRUE) {
return;
}
}
TimeOut();
TimerBegin(5, GAME_END); // Round is over, 5 seconds til a new round starts
} else {
TimerBegin(autocvar_mp_roundtime * 60, GAME_ACTIVE); // Unfreeze
Radio_StartMessage();
}
}
}
}
/*
=================
BuyingPossible
Checks if it is possible for players to buy anything
=================
*/
int
CSMultiplayerRules::BuyingPossible(base_player pl)
{
if (pl.health <= 0) {
return FALSE;
}
if (g_cs_gamestate == GAME_ACTIVE) {
if (((autocvar_mp_roundtime * 60) - g_cs_gametime) > autocvar_mp_buytime) {
centerprint(pl, sprintf("%d seconds have passed...\nYou can't buy anything now!", autocvar_mp_buytime));
return FALSE;
}
}
if (pl.team == TEAM_VIP) {
centerprint(pl, "You are the VIP...\nYou can't buy anything!\n");
return FALSE;
}
if (g_cstrike_buying == BUY_NEITHER) {
centerprint(pl, "Sorry, you aren't meant\nto be buying anything.\n");
return FALSE;
}
if (g_cstrike_buying != BUY_BOTH) {
if (g_cstrike_buying == BUY_CT && pl.team == TEAM_T) {
centerprint(pl, "Terrorists aren't allowed to\nbuy anything on this map!\n");
return FALSE;
} else if (g_cstrike_buying == BUY_T && pl.team == TEAM_CT) {
centerprint(pl, "CTs aren't allowed to buy\nanything on this map!\n");
return FALSE;
}
}
if (!(pl.gflags & GF_BUYZONE)) {
centerprint(pl, "Sorry, you aren't in a buyzone.\n");
return FALSE;
}
return TRUE;
}
void
CSMultiplayerRules::MakeBomber(base_player pl)
{
Weapons_AddItem(pl, WEAPON_C4BOMB, -1);
centerprint(pl, "You have the bomb!\nFind the target zone or DROP\nthe bomb for another Terrorist.");
}
void
CSMultiplayerRules::MakeVIP(base_player pl)
{
pl.team = TEAM_VIP;
PlayerRespawn(pl, pl.team);
centerprint(pl, "You are the VIP\nMake your way to the safety zones!");
forceinfokey(pl, "*dead", "2");
}
/*
=================
RestartRound
Loop through all ents and handle them
=================
*/
void
CSMultiplayerRules::RestartRound(int iWipe)
{
// Spawn/Respawn everyone at their team position and give them $$$
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
player pl = (player)eFind;
if (pl.health > 0 && iWipe == FALSE) {
PlayerRespawn(pl, pl.team);
} else {
PlayerMakeSpectator(pl);
PlayerMakePlayable(pl, pl.charmodel);
}
if (iWipe == FALSE) {
Money_GiveTeamReward(pl);
} else {
pl.money = 0;
Money_AddMoney(pl, autocvar_mp_startmoney);
}
}
/* clear the corpses/items/bombs */
for (entity eFind = world; (eFind = find(eFind, ::classname, "remove_me"));) {
remove(eFind);
}
// Select a random Terrorist for the bomb, if needed
if (g_cs_bombzones > 0) {
int iRandomT = floor(random(1, (float)g_cs_alive_t + 1));
int iPickT = 0;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
if (eFind.team == TEAM_T) {
iPickT++;
if (iPickT == iRandomT) {
MakeBomber((player)eFind);
}
}
}
}
// If there is a VIP, select a random CT to be it
if (g_cs_vipzones > 0) {
int iRandomCT = floor(random(1, (float)g_cs_alive_ct + 1));
int iPickCT = 0;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
if (eFind.team == TEAM_CT) {
iPickCT++;
if (iPickCT == iRandomCT) {
MakeVIP((player)eFind);
}
}
}
}
// Respawn all the entities
for (entity a = world; (a = findfloat(a, ::identity, 1));) {
CBaseEntity caw = (CBaseEntity)a;
if (caw.classname != "player")
caw.Respawn();
}
TimerBegin(autocvar_mp_freezetime, GAME_FREEZE);
Money_ResetTeamReward();
}
/*
=================
RoundOver
This happens whenever an objective is complete or time is up
=================
*/
void
CSMultiplayerRules::RoundOver(int iTeamWon, int iMoneyReward, int fSilent)
{
if (g_cs_gamestate != GAME_ACTIVE) {
return;
}
if (iTeamWon == TEAM_T) {
if (fSilent == FALSE) {
Radio_BroadcastMessage(RADIO_TERWIN);
}
g_cs_roundswon_t++;
} else if (iTeamWon == TEAM_CT) {
if (fSilent == FALSE) {
Radio_BroadcastMessage(RADIO_CTWIN);
}
g_cs_roundswon_ct++;
/* In Bomb Defusal, if Terrorists were able to plant the bomb
* but lose the round, all Terrorists receive an $800 bonus. */
if (g_cs_bombplanted) {
Money_QueTeamReward(TEAM_T, 800);
}
} else {
if (fSilent == FALSE) {
Radio_BroadcastMessage(RADIO_ROUNDDRAW);
}
}
Money_HandleRoundReward(iTeamWon);
Money_QueTeamReward(iTeamWon, iMoneyReward);
TimerBegin(5, GAME_END); // Round is over, 5 seconds til a new round starts
g_cs_hostagesrescued = 0;
g_cs_bombplanted = 0;
g_cs_roundsplayed++;
forceinfokey(world, "teamscore_1", sprintf("%i", g_cs_roundswon_t));
forceinfokey(world, "teamscore_2", sprintf("%i", g_cs_roundswon_ct));
}
/*
=================
TimeOut
Whenever mp_roundtime was being counted down to 0
=================
*/
void
CSMultiplayerRules::TimeOut(void)
{
if (g_cs_vipzones > 0) {
RoundOver(TEAM_T, 3250, FALSE);
} else if (g_cs_bombzones > 0) {
/* In Bomb Defusal, all Counter-Terrorists receive $3250
* if they won running down the time. */
RoundOver(TEAM_CT, 3250, FALSE);
} else if (g_cs_hostagestotal > 0) {
// TODO: Broadcast_Print: Hostages have not been rescued!
RoundOver(TEAM_T, 3250, FALSE);
} else {
RoundOver(0, 0, FALSE);
}
}
/*
=================
SwitchTeams
Happens rarely
=================
*/
void
CSMultiplayerRules::SwitchTeams(void)
{
int iCTW, iTW;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
player pl = (player)eFind;
if (pl.team == TEAM_CT) {
pl.team = TEAM_T;
pl.charmodel -= 4;
} else if (pl.team == TEAM_T) {
pl.team = TEAM_CT;
pl.charmodel += 4;
}
forceinfokey(pl, "*team", ftos(pl.team));
}
iCTW = g_cs_roundswon_ct;
iTW = g_cs_roundswon_t;
g_cs_roundswon_t = iCTW;
g_cs_roundswon_ct = iTW;
iCTW = g_cs_alive_ct;
iTW = g_cs_alive_t;
g_cs_alive_ct = iTW;
g_cs_alive_t = iCTW;
forceinfokey(world, "teamscore_1", sprintf("%i", g_cs_roundswon_t));
forceinfokey(world, "teamscore_2", sprintf("%i", g_cs_roundswon_ct));
}
void
CSMultiplayerRules::CountPlayers(void)
{
g_cs_alive_t = 0;
g_cs_alive_ct = 0;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
if (eFind.health > 0) {
if (eFind.team == TEAM_T) {
g_cs_alive_t++;
} else if (eFind.team == TEAM_CT) {
g_cs_alive_ct++;
} else if (eFind.team == TEAM_VIP) {
g_cs_alive_ct++;
}
}
}
}
void
CSMultiplayerRules::DeathCheck(base_player pl)
{
if ((g_cs_alive_t == 0) && (g_cs_alive_ct == 0)) {
if (g_cs_bombplanted == TRUE) {
RoundOver(TEAM_T, 3600, FALSE);
} else {
RoundOver(FALSE, 0, FALSE);
}
} else {
int winner;
if ((pl.team == TEAM_T) && (g_cs_alive_t == 0)) {
winner = TEAM_CT;
} else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 0)) {
winner = TEAM_T;
} else {
return;
}
if (g_cs_bombzones > 0) {
/* In Bomb Defusal, the winning team receives $3250
* if they won by eliminating the enemy team. */
if (!g_cs_bombplanted || g_cs_alive_ct == 0) {
RoundOver(winner, 3250, FALSE);
}
} else {
/* In Hostage Rescue, the winning team receives $3600
* if they won by eliminating the enemy team. */
RoundOver(winner, 3600, FALSE);
}
}
}
/*
=================
PlayerFindSpawn
Recursive function that gets the next spawnpoint
=================
*/
entity
CSMultiplayerRules::PlayerFindSpawn(float t)
{
entity point = world;
if (t == TEAM_T) {
m_eLastTSpawn = find(m_eLastTSpawn, ::classname, "info_player_deathmatch");
if (m_eLastTSpawn == world) {
m_eLastTSpawn = find(m_eLastTSpawn, ::classname, "info_player_deathmatch");
}
point = m_eLastTSpawn;
} else if (t == TEAM_CT) {
m_eLastCTSpawn = find(m_eLastCTSpawn, ::classname, "info_player_start");
if (m_eLastCTSpawn == world) {
m_eLastCTSpawn = find(m_eLastCTSpawn, ::classname, "info_player_start");
}
point = m_eLastCTSpawn;
} else if (t == TEAM_VIP) {
point = find(world, ::classname, "info_vip_start");
}
if (point == world) {
error("Error: No valid spawnpoints available.");
}
return point;
}
/*
=================
PlayerRespawn
Called whenever a player survived a round and needs a basic respawn.
=================
*/
void
CSMultiplayerRules::PlayerRespawn(base_player pp, int fTeam)
{
player pl = (player)pp;
entity eSpawn;
forceinfokey(pl, "*spec", "0");
eSpawn = PlayerFindSpawn(pl.team);
pl.classname = "player";
pl.health = pl.max_health = 100;
forceinfokey(pl, "*dead", "0");
CountPlayers();
pl.takedamage = DAMAGE_YES;
pl.solid = SOLID_SLIDEBOX;
pl.movetype = MOVETYPE_WALK;
pl.flags = FL_CLIENT;
pl.iBleeds = TRUE;
pl.viewzoom = 1.0;
pl.g_items &= ~ITEM_C4BOMB;
print(sprintf("Spawnpos: %v\n", eSpawn.origin));
pl.SetOrigin(eSpawn.origin);
pl.angles = eSpawn.angles;
pl.SendFlags = UPDATE_ALL;
Client_FixAngle(pl, pl.angles);
switch (pl.charmodel) {
case 1:
pl.model = "models/player/terror/terror.mdl";
break;
case 2:
pl.model = "models/player/leet/leet.mdl";
break;
case 3:
pl.model = "models/player/arctic/arctic.mdl";
break;
case 4:
pl.model = "models/player/guerilla/guerilla.mdl";
break;
case 5:
pl.model = "models/player/urban/urban.mdl";
break;
case 6:
pl.model = "models/player/gsg9/gsg9.mdl";
break;
case 7:
pl.model = "models/player/sas/sas.mdl";
break;
case 8:
pl.model = "models/player/gign/gign.mdl";
break;
default:
pl.model = "models/player/vip/vip.mdl";
}
pl.SetModel(pl.model);
pl.SetSize(VEC_HULL_MIN, VEC_HULL_MAX);
pl.view_ofs = VEC_PLAYER_VIEWPOS;
pl.velocity = [0,0,0];
pl.progress = 0.0f;
Weapons_SwitchBest(pl);
/*Ammo_AutoFill(pl.fSlotPrimary);
Ammo_AutoFill(pl.fSlotSecondary);*/
}
void
CSMultiplayerRules::PlayerClearWeaponry(base_player pp)
{
player pl = (player)pp;
pl.g_items = 0x0;
pl.activeweapon = 0;
pl.ammo_50ae = 0;
pl.ammo_762mm = 0;
pl.ammo_556mm = 0;
pl.ammo_556mmbox = 0;
pl.ammo_338mag = 0;
pl.ammo_9mm = 0;
pl.ammo_buckshot = 0;
pl.ammo_45acp = 0;
pl.ammo_357sig = 0;
pl.ammo_57mm = 0;
pl.ammo_hegrenade = 0;
pl.ammo_fbgrenade = 0;
pl.ammo_smokegrenade = 0;
pl.usp45_mag = 0;
pl.glock18_mag = 0;
pl.deagle_mag = 0;
pl.p228_mag = 0;
pl.elites_mag = 0;
pl.fiveseven_mag = 0;
pl.m3_mag = 0;
pl.xm1014_mag = 0;
pl.mp5_mag = 0;
pl.p90_mag = 0;
pl.ump45_mag = 0;
pl.mac10_mag = 0;
pl.tmp_mag = 0;
pl.ak47_mag = 0;
pl.sg552_mag = 0;
pl.m4a1_mag = 0;
pl.aug_mag = 0;
pl.scout_mag = 0;
pl.awp_mag = 0;
pl.g3sg1_mag = 0;
pl.sg550_mag = 0;
pl.para_mag = 0;
pl.viewzoom = 1.0f;
}
/*
=================
PlayerMakePlayable
Called whenever need a full-reinit of a player.
This may be after a player had died or when the game starts for the first time.
=================
*/
void
CSMultiplayerRules::PlayerMakePlayable(base_player pp, int chara)
{
player pl = (player)pp;
/* spectator */
if (chara == 0) {
PlayerSpawn(pl);
return;
}
pl.g_items |= ITEM_SUIT;
Weapons_AddItem(pl, WEAPON_KNIFE, -1);
if (chara < 5) {
/* terrorists */
pl.team = TEAM_T;
if (autocvar_fcs_knifeonly == FALSE) {
Weapons_AddItem(pl, WEAPON_GLOCK18, -1);
/*Weapon_GiveAmmo(WEAPON_GLOCK18, 40);*/
/*Weapon_Draw(WEAPON_GLOCK18);*/
} else {
/*Weapon_Draw(WEAPON_KNIFE);*/
}
} else {
/* counter */
pl.team = TEAM_CT;
if (autocvar_fcs_knifeonly == FALSE) {
Weapons_AddItem(pl, WEAPON_USP45, -1);
/*Weapon_GiveAmmo(WEAPON_USP45, 24);*/
/*Weapon_Draw(WEAPON_USP45);*/
} else {
/*Weapon_Draw(WEAPON_KNIFE);*/
}
}
pl.ingame = TRUE;
forceinfokey(pl, "*team", ftos(pl.team));
PlayerRespawn(pl, pl.team);
}
/*
=================
PlayerMakeSpectator
Force the player to become an observer.
=================
*/
void
CSMultiplayerRules::PlayerMakeSpectator(base_player pl)
{
pl.classname = "spectator";
pl.health = 0;
pl.armor = 0;
pl.takedamage = DAMAGE_NO;
pl.solid = SOLID_NOT;
pl.movetype = MOVETYPE_NOCLIP;
pl.SendEntity = Player_SendEntity;
pl.flags = FL_CLIENT;
pl.weapon = 0;
pl.viewzoom = 1.0f;
pl.model = 0;
setsize (pl, [-16,-16,-16], [16,16,16]);
pl.view_ofs = pl.velocity = [0,0,0];
forceinfokey(pl, "*spec", "2");
/* clear the inventory */
pl.items = 0x0;
pl.activeweapon = 0;
}
/*
=================
PlayerSpawn
Called on the client first joining the server.
=================
*/
void
CSMultiplayerRules::PlayerSpawn(base_player pl)
{
/* immediately put us into spectating mode */
PlayerMakeSpectator(pl);
Spawn_ObserverCam(pl);
/* give the initial server-joining money */
Money_AddMoney(pl, autocvar_mp_startmoney);
/* we don't belong to any team */
pl.team = 0;
forceinfokey(pl, "*team", "0");
}
float
CSMultiplayerRules::ConsoleCommand(base_player pp, string cmd)
{
tokenize(cmd);
switch (argv(0)) {
case "bot_add":
entity bot_ent = Bot_AddQuick();
if (bot_ent) {
bot_ent.think = CSEv_JoinAuto;
bot_ent.nextthink = time;
}
break;
default:
return FALSE;
}
return TRUE;
}
void
CSMultiplayerRules::CSMultiplayerRules(void)
{
forceinfokey(world, "teams", "2");
forceinfokey(world, "team_1", "Terrorist");
forceinfokey(world, "teamscore_1", "0");
forceinfokey(world, "team_2", "Counter-Terrorist");
forceinfokey(world, "teamscore_2", "0");
}
/*
=================
CSEv_JoinTeam_f
Event Handling, called by the Client codebase via 'sendevent'
=================
*/
void CSEv_JoinTeam_f(float flChar)
{
CSMultiplayerRules rules = (CSMultiplayerRules)g_grMode;
player pl = (player)self;
if (pl.team == TEAM_VIP) {
centerprint(pl, "You are the VIP!\nYou cannot switch roles now.\n");
return;
}
// alive and are trying to switch teams, so subtract us from the Alive_Team counter.
if (pl.health > 0) {
rules.PlayerKill(pl);
}
switch (g_cs_gamestate) {
/* spawn the players immediately when its in the freeze state */
case GAME_FREEZE:
pl.charmodel = (int)flChar;
rules.PlayerMakePlayable(pl, (int)flChar);
if ((pl.team == TEAM_T) && (g_cs_alive_t == 1)) {
if (g_cs_bombzones > 0) {
rules.MakeBomber(pl);
}
} else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 1)) {
if (g_cs_vipzones > 0) {
rules.MakeVIP(pl);
}
}
break;
/* otherwise, just prepare their fields for the next round */
default:
if (flChar == 0) {
rules.PlayerSpawn(pl);
return;
}
if (flChar < 5) {
pl.team = TEAM_T;
} else {
pl.team = TEAM_CT;
}
rules.PlayerMakeSpectator(pl);
pl.classname = "player";
pl.charmodel = (int)flChar;
pl.health = 0;
forceinfokey(pl, "*dead", "1");
forceinfokey(pl, "*team", ftos(pl.team));
break;
}
pl.frags = 0;
pl.deaths = 0;
forceinfokey(pl, "*deaths", ftos(pl.deaths));
/* if no players are present in the chosen team, force restart round */
if ((pl.team == TEAM_T) && (g_cs_alive_t == 0)) {
rules.RoundOver(FALSE, 0, FALSE);
} else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 0)) {
rules.RoundOver(FALSE, 0, FALSE);
}
}
void CSEv_JoinAuto(void)
{
int ct_count = 0;
int t_count = 1;
for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) {
player pl = (player)eFind;
if (pl.team == TEAM_T) {
t_count++;
}
if (pl.team == TEAM_CT) {
ct_count++;
}
}
if (ct_count >= t_count) {
CSEv_JoinTeam_f(floor(random(1,5)));
} else {
CSEv_JoinTeam_f(floor(random(5,9)));
}
}