sourcemod-plugins/scripting/l4d2_extraplayeritems.sp

722 lines
No EOL
24 KiB
SourcePawn

/*
Logic Flow:
Once a player reaches the saferoom, it will give at a minimum a kit for each extra player over 4.
There is a small chance of bonus kit, and will give bonus depending on average team health
Kits are provided when a player attempts to pickup a new kit,
or when they load back in after map transition (and don't already have one)
Once a new map starts, all item spawners are checked and randomly their spawn count will be increased by 1.
Also on map start, cabinets will be populated with extra items dependent on player count
extraitems = (playerCount) * (cabinetAmount/4) - cabinetAmount
*/
//TODO: On 3rd/4th kit pickup in area, add more
//TODO: Add extra pills too, on pickup
#pragma semicolon 1
#pragma newdecls required
#define DEBUG_INFO 0
#define DEBUG_GENERIC 1
#define DEBUG_SPAWNLOGIC 2
#define DEBUG_ANY 3
//Set the debug level
#define DEBUG_LEVEL DEBUG_GENERIC
//Sets abmExtraCount to this value if set
//#define DEBUG_FORCE_PLAYERS 5
#define PLUGIN_VERSION "1.0"
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <left4dhooks>
#include <l4d_info_editor>
#include <jutils>
#define L4D2_WEPUPGFLAG_NONE (0 << 0)
#define L4D2_WEPUPGFLAG_INCENDIARY (1 << 0)
#define L4D2_WEPUPGFLAG_EXPLOSIVE (1 << 1)
#define L4D2_WEPUPGFLAG_LASER (1 << 2)
#define AMMOPACK_ENTID 0
#define AMMOPACK_USERS 1
public Plugin myinfo =
{
name = "L4D2 Extra Player Tools",
author = "jackzmc",
description = "Automatic system for management of 5+ player games. Provides extra kits, items, and more",
version = PLUGIN_VERSION,
url = ""
};
static ConVar hExtraItemBasePercentage, hAddExtraKits, hMinPlayers, hUpdateMinPlayers, hMinPlayersSaferoomDoor, hSaferoomDoorWaitSeconds, hSaferoomDoorAutoOpen;
static int extraKitsAmount, extraKitsStarted, abmExtraCount, firstSaferoomDoorEntity, playersLoadedIn, playerstoWaitFor;
static int isBeingGivenKit[MAXPLAYERS+1];
static bool isCheckpointReached, isLateLoaded, firstGiven, isFailureRound;
static ArrayList ammoPacks;
static StringMap weaponMaxClipSizes;
#define CABINET_ITEM_BLOCKS 4
enum struct Cabinet {
int id;
int items[CABINET_ITEM_BLOCKS];
}
static Cabinet cabinets[10]; //Store 10 cabinets
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
if(late) isLateLoaded = true;
}
public void OnPluginStart() {
EngineVersion g_Game = GetEngineVersion();
if(g_Game != Engine_Left4Dead2) {
SetFailState("This plugin is for L4D2 only.");
}
weaponMaxClipSizes = new StringMap();
ammoPacks = new ArrayList(2); //<int entityID, ArrayList clients>
HookEvent("player_spawn", Event_PlayerSpawn);
HookEvent("player_first_spawn", Event_PlayerFirstSpawn);
HookEvent("round_end", Event_RoundEnd);
HookEvent("map_transition", Event_MapTransition);
HookEvent("game_start", Event_GameStart);
HookEvent("round_freeze_end", Event_RoundFreezeEnd);
hExtraItemBasePercentage = CreateConVar("l4d2_extraitem_chance", "0.056", "The base chance (multiplied by player count) of an extra item being spawned.", FCVAR_NONE, true, 0.0, true, 1.0);
hAddExtraKits = CreateConVar("l4d2_extraitems_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits, 1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
hUpdateMinPlayers = CreateConVar("l4d2_extraitems_updateminplayers", "1", "Should the plugin update abm's cvar min_players convar to the player count?\n 0 -> NO, 1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
hMinPlayersSaferoomDoor = CreateConVar("l4d2_extraitems_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "55", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
hSaferoomDoorAutoOpen = CreateConVar("l4d2_extraitems_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
if(hUpdateMinPlayers.BoolValue) {
hMinPlayers = FindConVar("abm_minplayers");
if(hMinPlayers != null) PrintDebug(DEBUG_INFO, "Found convar abm_minplayers");
}
if(isLateLoaded) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2)
SDKHook(i, SDKHook_WeaponEquip, Event_Pickup);
}
}
#if defined DEBUG_FORCE_PLAYERS
abmExtraCount = DEBUG_FORCE_PLAYERS;
#endif
AutoExecConfig(true, "l4d2_extraplayeritems");
#if defined DEBUG_LEVEL
RegAdminCmd("sm_epi_setkits", Command_SetKitAmount, ADMFLAG_CHEATS, "Sets the amount of extra kits that will be provided");
RegAdminCmd("sm_epi_lock", Command_ToggleDoorLocks, ADMFLAG_CHEATS, "Toggle all toggle's lock state");
RegAdminCmd("sm_epi_kits", Command_GetKitAmount, ADMFLAG_CHEATS);
RegAdminCmd("sm_epi_items", Command_RunExtraItems, ADMFLAG_CHEATS);
#endif
}
public void OnPluginEnd() {
delete weaponMaxClipSizes;
delete ammoPacks;
}
/////////////////////////////////////
/// COMMANDS
////////////////////////////////////
#if defined DEBUG_LEVEL
public Action Command_SetKitAmount(int client, int args) {
char arg[32];
GetCmdArg(1, arg, sizeof(arg));
int number = StringToInt(arg);
if(number > 0 || number == -1) {
extraKitsAmount = number;
extraKitsStarted = extraKitsAmount;
ReplyToCommand(client, "Set extra kits amount to %d", number);
}else{
ReplyToCommand(client, "Must be a number greater than 0. -1 to disable");
}
return Plugin_Handled;
}
public Action Command_ToggleDoorLocks(int client, int args) {
for(int i = MaxClients + 1; i < GetMaxEntities(); i++) {
if(HasEntProp(i, Prop_Send, "m_bLocked")) {
int state = GetEntProp(i, Prop_Send, "m_bLocked");
SetEntProp(i, Prop_Send, "m_bLocked", state > 0 ? 0 : 1);
}
}
return Plugin_Handled;
}
public Action Command_GetKitAmount(int client, int args) {
ReplyToCommand(client, "Extra kits available: %d (%d) | Survivors: %d", extraKitsAmount, extraKitsStarted, GetSurvivorsCount());
ReplyToCommand(client, "isCheckpointReached %b, isLateLoaded %b, firstGiven %b", isCheckpointReached, isLateLoaded, firstGiven);
return Plugin_Handled;
}
public Action Command_RunExtraItems(int client, int args) {
ReplyToCommand(client, "Running extra item count increaser...");
PopulateItems();
return Plugin_Handled;
}
#endif
/////////////////////////////////////
/// EVENTS
////////////////////////////////////
public void OnGetWeaponsInfo(int pThis, const char[] classname) {
char clipsize[8];
InfoEditor_GetString(pThis, "clip_size", clipsize, sizeof(clipsize));
int maxClipSize = StringToInt(clipsize);
if(maxClipSize > 0)
weaponMaxClipSizes.SetValue(classname, maxClipSize);
}
//Called on the first spawn in a mission.
public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast) {
firstGiven = false;
extraKitsAmount = 0;
extraKitsStarted = 0;
abmExtraCount = 4;
hMinPlayers.IntValue = 4;
}
public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(GetClientTeam(client) == 2 && !IsFakeClient(client)) {
if(L4D_IsFirstMapInScenario() && !firstGiven) {
//Check if all clients are ready, and survivor count is > 4.
if(AreAllClientsReady()) {
abmExtraCount = GetRealSurvivorsCount();
if(abmExtraCount > 4) {
firstGiven = true;
//Set the initial value ofhMinPlayers
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
hMinPlayers.IntValue = abmExtraCount;
}
PopulateItems();
CreateTimer(1.0, Timer_GiveKits);
}
if(firstSaferoomDoorEntity > 0)
UnlockDoor(firstSaferoomDoorEntity, 2);
}
} else {
RequestFrame(Frame_GiveNewClientKit, client);
}
}
}
public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
int user = event.GetInt("userid");
int client = GetClientOfUserId(user);
if(GetClientTeam(client) == 2) {
if(!IsFakeClient(client)) {
if(!L4D_IsFirstMapInScenario()) {
if(++playersLoadedIn == 1) {
CreateTimer(hSaferoomDoorWaitSeconds.FloatValue, Timer_OpenSaferoomDoor, _, TIMER_FLAG_NO_MAPCHANGE);
}
if(playerstoWaitFor > 0) {
float percentIn = float(playersLoadedIn) / float(playerstoWaitFor);
if(firstSaferoomDoorEntity > 0 && percentIn > hMinPlayersSaferoomDoor.FloatValue) {
UnlockDoor(firstSaferoomDoorEntity, 2);
}
}else{
UnlockDoor(firstSaferoomDoorEntity, 2);
}
}
}
CreateTimer(0.5, Timer_GiveClientKit, user);
SDKHook(client, SDKHook_WeaponEquip, Event_Pickup);
}
}
public Action L4D_OnIsTeamFull(int team, bool &full) {
if(team == 2 && full) {
full = false;
return Plugin_Continue;
}
return Plugin_Continue;
}
public void Frame_GiveNewClientKit(int client) {
if(!DoesClientHaveKit(client) && GetRealSurvivorsCount() > 4) {
int item = GivePlayerItem(client, "weapon_first_aid_kit");
EquipPlayerWeapon(client, item);
}
}
public Action Timer_GiveClientKit(Handle hdl, int user) {
int client = GetClientOfUserId(user);
if(client > 0 && !DoesClientHaveKit(client)) {
UseExtraKit(client);
}
}
public Action Timer_UpdateMinPlayers(Handle hdl) {
//Set abm's min players to the amount of real survivors. Ran AFTER spawned incase they are pending joining
int newPlayerCount = GetRealSurvivorsCount();
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
if(newPlayerCount > 4 && hMinPlayers.IntValue < newPlayerCount && newPlayerCount < 18) {
abmExtraCount = newPlayerCount;
#if defined DEBUG
PrintDebug(DEBUG_GENERIC, "update abm_minplayers -> %d", abmExtraCount);
#endif
hMinPlayers.IntValue = abmExtraCount;
}
}
}
public Action Timer_GiveKits(Handle timer) { GiveStartingKits(); }
public void OnMapStart() {
//If previous round was a failure, restore the amount of kits that were left directly after map transition
if(isFailureRound) {
extraKitsAmount = extraKitsStarted;
//give kits if first
if(L4D_IsFirstMapInScenario()) {
GiveStartingKits();
}
isFailureRound = false;
}else if(!L4D_IsFirstMapInScenario()) {
//Re-set value incase it reset.
//hMinPlayers.IntValue = abmExtraCount;
}else if(L4D_IsMissionFinalMap()) {
//Add extra kits for finales
int extraKits = GetSurvivorsCount() - 4;
if(extraKits > 0) {
extraKitsAmount += extraKits;
extraKitsStarted = extraKitsAmount;
}
}
if(!isLateLoaded) {
isLateLoaded = false;
}
//Lock the beginning door
if(hMinPlayersSaferoomDoor.FloatValue > 0.0) {
int entity = -1;
while ((entity = FindEntityByClassname(entity, "prop_door_rotating_checkpoint")) != -1 && entity > MaxClients) {
bool isLocked = GetEntProp(entity, Prop_Send, "m_bLocked") == 1;
if(isLocked) {
firstSaferoomDoorEntity = entity;
AcceptEntityInput(firstSaferoomDoorEntity, "Close");
AcceptEntityInput(firstSaferoomDoorEntity, "Lock");
AcceptEntityInput(firstSaferoomDoorEntity, "ForceClosed");
SDKHook(firstSaferoomDoorEntity, SDKHook_Use, Hook_Use);
break;
}
}
}
//Hook the end saferoom as event
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
playersLoadedIn = 0;
}
public void OnMapEnd() {
for(int i = 0; i < ammoPacks.Length; i++) {
ArrayList clients = ammoPacks.Get(i, AMMOPACK_USERS);
delete clients;
}
for(int i = 0; i < sizeof(cabinets); i++) {
cabinets[i].id = 0;
for(int b = 0; b < CABINET_ITEM_BLOCKS; b++) {
cabinets[i].items[b] = 0;
}
}
ammoPacks.Clear();
playersLoadedIn = 0;
}
public void Event_RoundFreezeEnd(Event event, const char[] name, bool dontBroadcast) {
CreateTimer(50.0, Timer_Populate);
}
public Action Timer_Populate(Handle h) {
PopulateItems();
}
public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, int client, float time) {
if(!isCheckpointReached && client > 0 && client <= MaxClients && IsValidClient(client) && GetClientTeam(client) == 2) {
isCheckpointReached = true;
abmExtraCount = GetSurvivorsCount();
if(abmExtraCount > 4) {
int extraPlayers = abmExtraCount - 4;
float averageTeamHP = GetAverageHP();
if(averageTeamHP <= 30.0) extraPlayers += extraPlayers; //if perm. health < 30, give an extra 4 on top of the extra
else if(averageTeamHP <= 50.0) extraPlayers = (extraPlayers / 2); //if the team's average health is less than 50 (permament) then give another
//Chance to get 1-2 extra kits (might need to be nerfed or restricted to > 50 HP)
if(GetRandomFloat() < 0.3 && averageTeamHP <= 80.0) ++extraPlayers;
//If hAddExtraKits TRUE: Append to previous, FALSE: Overwrite
if(hAddExtraKits.BoolValue)
extraKitsAmount += extraPlayers;
else
extraKitsAmount = extraPlayers;
extraKitsStarted = extraKitsAmount;
PrintToConsoleAll("CHECKPOINT REACHED BY %N | EXTRA KITS: %d", client, extraPlayers);
PrintToServer("Player entered saferoom. Providing %d extra kits", extraKitsAmount);
}
}
}
public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) {
if(!isFailureRound) isFailureRound = true;
}
public Action Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
#if defined DEBUG
PrintToServer("Map transition | %d Extra Kits", extraKitsAmount);
#endif
isCheckpointReached = false;
isLateLoaded = false;
extraKitsStarted = extraKitsAmount;
abmExtraCount = GetRealSurvivorsCount();
playerstoWaitFor = GetSurvivorsCount();
}
//TODO: Possibly hacky logic of on third different ent id picked up, in short timespan, detect as set of 4 (pills, kits) & give extra
public Action Event_Pickup(int client, int weapon) {
char name[32];
GetEntityClassname(weapon, name, sizeof(name));
if(StrEqual(name, "weapon_first_aid_kit", true)) {
if(isBeingGivenKit[client]) return Plugin_Continue;
if((L4D_IsInFirstCheckpoint(client) || L4D_IsInLastCheckpoint(client)) && UseExtraKit(client)) {
return Plugin_Handled;
}
}
return Plugin_Continue;
}
public void OnEntityCreated(int entity, const char[] classname) {
if(StrEqual(classname, "weapon_pain_pills_spawn") || StrEqual(classname, "weapon_first_aid_kit_spawn")) {
SDKHook(entity, SDKHook_SpawnPost, Hook_CabinetItemSpawn);
}else if(StrEqual(classname, "prop_health_cabinet", true)) {
SDKHook(entity, SDKHook_SpawnPost, Hook_CabinetSpawn);
}else if (StrEqual(classname, "upgrade_ammo_explosive") || StrEqual(classname, "upgrade_ammo_incendiary")) {
int index = ammoPacks.Push(entity);
ammoPacks.Set(index, new ArrayList(1), AMMOPACK_USERS);
SDKHook(entity, SDKHook_Use, OnUpgradePackUse);
}
}
///////////////////////////////////////////////////////////////////////////////
// Hooks
///////////////////////////////////////////////////////////////////////////////
//TODO: Implement extra kit amount to this
//TODO: Possibly check ammo stash and kit (relv. distance). Would fire on Last Stand 2nd .
public Action Hook_CabinetItemSpawn(int entity) {
int cabinet = FindNearestEntityInRange(entity, "prop_health_cabinet", 60.0);
if(cabinet > 0) {
int ci = FindCabinetIndex(cabinet);
//Check for any open block
for(int block = 0; block < CABINET_ITEM_BLOCKS; block++) {
int cabEnt = cabinets[ci].items[block];
PrintDebug(DEBUG_ANY, "cabinet %d spawner %d block %d: %d", cabinet, entity, block, cabEnt);
if(cabEnt <= 0) {
cabinets[ci].items[block] = entity;
PrintDebug(DEBUG_SPAWNLOGIC, "Adding spawner %d for cabinet %d block %d", entity, cabinet, block);
break;
}
}
//If Cabinet is full, spawner can not be a part of cabinet and is ignored.
}
}
public Action Hook_CabinetSpawn(int entity) {
for(int i = 0; i < sizeof(cabinets); i++) {
if(cabinets[i].id == 0) {
cabinets[i].id = entity;
break;
}
}
PrintDebug(DEBUG_SPAWNLOGIC, "Adding cabinet %d", entity);
}
public Action OnUpgradePackUse(int entity, int activator, int caller, UseType type, float value) {
if (entity > 2048 || entity <= MaxClients || !IsValidEntity(entity)) return Plugin_Continue;
int primaryWeapon = GetPlayerWeaponSlot(activator, 0);
if(IsValidEdict(primaryWeapon) && HasEntProp(primaryWeapon, Prop_Send, "m_upgradeBitVec")) {
int index = ammoPacks.FindValue(entity, AMMOPACK_ENTID);
if(index == -1) return Plugin_Continue;
ArrayList clients = ammoPacks.Get(index, AMMOPACK_USERS);
if(clients.FindValue(activator) > -1) {
ClientCommand(activator, "play ui/menu_invalid.wav");
return Plugin_Handled;
}
char classname[32];
int upgradeBits = GetEntProp(primaryWeapon, Prop_Send, "m_upgradeBitVec"), ammo;
//Get the new flag bits
GetEntityClassname(entity, classname, sizeof(classname));
//SetUsedBySurvivor(activator, entity);
int newFlags = StrEqual(classname, "upgrade_ammo_explosive") ? L4D2_WEPUPGFLAG_EXPLOSIVE : L4D2_WEPUPGFLAG_INCENDIARY;
if(upgradeBits & L4D2_WEPUPGFLAG_LASER == L4D2_WEPUPGFLAG_LASER) newFlags |= L4D2_WEPUPGFLAG_LASER;
SetEntProp(primaryWeapon, Prop_Send, "m_upgradeBitVec", newFlags);
GetEntityClassname(primaryWeapon, classname, sizeof(classname));
if(!weaponMaxClipSizes.GetValue(classname, ammo)) {
if(StrEqual(classname, "weapon_grenade_launcher", true)) ammo = 1;
else if(StrEqual(classname, "weapon_rifle_m60", true)) ammo = 150;
else {
int currentAmmo = GetEntProp(primaryWeapon, Prop_Send, "m_iClip1");
if(currentAmmo > 10) ammo = 10;
}
}
//Reset after minute after first pickup
if(clients.Length == 0) {
CreateTimer(60.0, Timer_ResetAmmoPack, entity);
}
SetEntProp(primaryWeapon, Prop_Send, "m_nUpgradedPrimaryAmmoLoaded", ammo);
clients.Push(activator);
ClientCommand(activator, "play player/orch_hit_csharp_short.wav");
if(clients.Length >= GetSurvivorsCount()) {
AcceptEntityInput(entity, "kill");
delete clients;
ammoPacks.Erase(index);
}
return Plugin_Handled;
}
return Plugin_Continue;
}
public Action Hook_Use(int entity, int activator, int caller, UseType type, float value) {
SetEntProp(entity, Prop_Send, "m_bLocked", 1);
AcceptEntityInput(entity, "Close");
ClientCommand(activator, "play ui/menu_invalid.wav");
PrintHintText(activator, "Waiting for players");
return Plugin_Handled;
}
/////////////////////////////////////
/// TIMERS
////////////////////////////////////
//TODO: In future, possibly have a total percentage of spawns that are affected instead on a per-level
//TODO: In future, also randomize what items are selected? Two loops:
/*
first loop pushes any valid _spawns into dynamic array
while / for loop that runs until X amount affected (based on % of GetEntityCount()).
Prioritize first aid kits somehow? Or split two groups: "utility" (throwables, kits, pill/shots), and "weapon" (all other spawns)
*/
public Action Timer_ResetAmmoPack(Handle h, int entity) {
if(IsValidEntity(entity)) {
int index = ammoPacks.FindValue(entity, AMMOPACK_ENTID);
if(index == -1) return Plugin_Continue;
ArrayList clients = ammoPacks.Get(index, AMMOPACK_USERS);
clients.Clear();
}
return Plugin_Continue;
}
public Action Timer_OpenSaferoomDoor(Handle h) {
if(firstSaferoomDoorEntity > 0)
UnlockDoor(firstSaferoomDoorEntity, 1);
return Plugin_Continue;
}
void UnlockDoor(int entity, int flag) {
PrintDebug(DEBUG_GENERIC, "Door unlocked, flag %d", flag);
SetEntProp(entity, Prop_Send, "m_bLocked", 0);
SDKUnhook(entity, SDKHook_Use, Hook_Use);
if(hSaferoomDoorAutoOpen.IntValue % flag == flag) {
AcceptEntityInput(entity, "Open");
}
firstSaferoomDoorEntity = -1;
PopulateItems();
}
int FindCabinetIndex(int cabinetId) {
for(int i = 0; i < sizeof(cabinets); i++) {
if(cabinets[i].id == cabinetId) return i;
}
return -1;
}
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
public void PopulateItems() {
int survivors = GetRealSurvivorsCount();
if(survivors <= 4) return;
//Generic Logic
float percentage = hExtraItemBasePercentage.FloatValue * survivors;
PrintToServer("Populating extra items based on player count (%d) | Percentage %.2f%%", survivors, percentage * 100);
PrintToConsoleAll("Populating extra items based on player count (%d) | Percentage %.2f%%", survivors, percentage * 100);
static char classname[32];
int affected = 0;
for(int i = MaxClients + 1; i < 2048; i++) {
if(IsValidEntity(i)) {
GetEntityClassname(i, classname, sizeof(classname));
if(StrContains(classname, "_spawn", true) > -1
&& StrContains(classname, "zombie", true) == -1
&& StrContains(classname, "scavenge", true) == -1
) {
int count = GetEntProp(i, Prop_Data, "m_itemCount");
if(count > 0 && GetRandomFloat() < percentage) {
SetEntProp(i, Prop_Data, "m_itemCount", ++count);
++affected;
}
}
}
}
PrintDebug(DEBUG_SPAWNLOGIC, "Incremented counts for %d items", affected);
//Cabinet logic
PrintDebug(DEBUG_SPAWNLOGIC, "Populating cabinets with extra items");
int spawner, count;
for(int i = 0; i < sizeof(cabinets); i++) {
if(cabinets[i].id == 0) break;
int spawnCount = GetEntProp(cabinets[i].id, Prop_Data, "m_pillCount");
int extraAmount = RoundToCeil(float(abmExtraCount) * (float(spawnCount)/4.0) - spawnCount);
bool hasASpawner;
while(extraAmount > 0) {
//FIXME: spawner is sometimes invalid entity. Ref needed?
for(int block = 0; block < CABINET_ITEM_BLOCKS; block++) {
spawner = cabinets[i].items[block];
if(spawner > 0) {
hasASpawner = true;
count = GetEntProp(spawner, Prop_Data, "m_itemCount") + 1;
SetEntProp(spawner, Prop_Data, "m_itemCount", count);
if(--extraAmount == 0) break;
}
}
//Incase cabinet is empty
if(!hasASpawner) break;
}
}
}
/////////////////////////////////////
/// Stocks
////////////////////////////////////
stock int FindFirstSurvivor() {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
return i;
}
}
return -1;
}
stock void GiveStartingKits() {
int skipLeft = 4;
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
//Skip at least the first 4 players, as they will pickup default kits.
//If player somehow already has it ,also skip them.
if(skipLeft > 0 || DoesClientHaveKit(i)) {
--skipLeft;
continue;
}else{
int item = GivePlayerItem(i, "weapon_first_aid_kit");
EquipPlayerWeapon(i, item);
}
}
}
}
stock int GetSurvivorsCount() {
#if defined DEBUG_FORCE_PLAYERS
return DEBUG_FORCE_PLAYERS;
#endif
int count = 0;
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
++count;
}
}
return count;
}
stock int GetRealSurvivorsCount() {
#if defined DEBUG_FORCE_PLAYERS
return DEBUG_FORCE_PLAYERS;
#endif
int count = 0;
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
if(IsFakeClient(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID") && GetEntProp(i, Prop_Send, "m_humanSpectatorUserID") == 0) continue;
++count;
}
}
return count;
}
stock bool AreAllClientsReady() {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && !IsClientInGame(i)) {
return false;
}
}
return true;
}
stock bool DoesClientHaveKit(int client) {
char wpn[32];
if(IsClientConnected(client) && IsClientInGame(client) && GetClientWeaponName(client, 3, wpn, sizeof(wpn))) {
if(StrEqual(wpn, "weapon_first_aid_kit")) {
return true;
}
}
return false;
}
stock bool UseExtraKit(int client) {
if(extraKitsAmount > 0) {
isBeingGivenKit[client] = true;
int ent = GivePlayerItem(client, "weapon_first_aid_kit");
EquipPlayerWeapon(client, ent);
isBeingGivenKit[client] = false;
if(--extraKitsAmount <= 0) {
extraKitsAmount = 0;
}
return true;
}
return false;
}
stock void PrintDebug(int level, const char[] format, any ... ) {
#if defined DEBUG_LEVEL
if(level <= DEBUG_LEVEL) {
char buffer[256];
VFormat(buffer, sizeof(buffer), format, 3);
PrintToServer("[Debug] %s", buffer);
PrintToConsoleAll("[Debug] %s", buffer);
}
#endif
}
stock float GetAverageHP() {
int totalHP, clients;
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
totalHP += GetClientHealth(i);
++clients;
}
}
return float(totalHP) / float(clients);
}