/* * Copyright (c) 2016-2020 Marco Hladik * * 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))); } }