Raised This Month: $51 Target: $400
 12% 

Counting and finding zombies in L4D2


Post New Thread Reply   
 
Thread Tools Display Modes
Neuro Toxin
Veteran Member
Join Date: Oct 2013
Location: { closing the void; }
Old 03-03-2015 , 15:19   Re: Counting and finding zombies in L4D2
Reply With Quote #11

That looks right
__________________
Neuro Toxin is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 03-03-2015 , 15:41   Re: Counting and finding zombies in L4D2
Reply With Quote #12

sm_dump_datamaps filename will give you the datamap properties (Prop_Data), keyvalues, and inputs for each classname (and serverclass name). This has the side-effect of giving you all the valid classnames.
sm_dump_netprops filename will give you the networked properties (netprops / sendprops / Prop_Send) for each serverclass name, but won't give you its classname.
__________________
Not currently working on SourceMod plugin development.
Powerlord is offline
Qub1
Junior Member
Join Date: Mar 2015
Old 03-04-2015 , 15:45   Re: Counting and finding zombies in L4D2
Reply With Quote #13

Okay, so I've written most of the code but I'm getting a couple of errors related to the arrays I'm using. Can anybody help resolve them? I have no idea what is wrong.

Also, bonus points for anything else that isn't done right. This is my first plugin so there is a good chance that's the case.

EDIT: Fixed the array issues, now I'm getting some other errors.

My code:
PHP Code:
/****************************************************************************************/
/* INCLUDES                                                                                */
/****************************************************************************************/
#include <sourcemod>
#include <sdktools>

/****************************************************************************************/
/* PLUGIN INFORMATION                                                                    */
/****************************************************************************************/
public Plugin:myinfo = {
    
name =                "Bots use throwables",
    
author =            "Qub1 ([email protected])",
    
description =    "Allows bots to pick up and use throwables.",
    
version =            "1.0.0",
    
url =                "http://qub1.infohoarder.com/"
};

/****************************************************************************************/
/* TODO                                                                                    */
/****************************************************************************************/
/**
 * - Add grabbing timer
 * - Finish throwing functions
 * - Add special infected responses
 *   - Target special infected before common infected
 * - Properly stop all timers on round end to prevent crashes
 */

/****************************************************************************************/
/* MODEL FILES                                                                            */
/****************************************************************************************/
#define MODEL_MOLOTOV    "models/w_models/weapons/w_eq_molotov.mdl"
#define MODEL_COACH        "models/survivors/survivor_coach.mdl"
#define MODEL_ELLIS        "models/survivors/survivor_mechanic.mdl"
#define MODEL_NICK        "models/survivors/survivor_gambler.mdl"
#define MODEL_ROCHELLE    "models/survivors/survivor_producer.mdl"

/****************************************************************************************/
/* SOUND FILES                                                                            */
/****************************************************************************************/
#define SOUND_MOLOTOV    "weapons/molotov/fire_ignite_2.wav"

/****************************************************************************************/
/* VOICE FILES - TAKING GRENADE                                                        */
/****************************************************************************************/
new String:s_CoachTakeMolotov[2][] = {
    
"scenes/coach/takemolotov01.vcd",
    
"scenes/coach/takemolotov02.vcd"
};

new 
String:s_CoachTakePipebomb[3][] = {
    
"scenes/coach/takepipebomb01.vcd",
    
"scenes/coach/takepipebomb02.vcd",
    
"scenes/coach/takepipebomb03.vcd"
};

new 
String:s_CoachTakeVomitjar[0][] = {};

new 
String:s_EllisTakeMolotov[8][] = {
    
"scenes/mechanic/takemolotov01.vcd",
    
"scenes/mechanic/takemolotov02.vcd",
    
"scenes/mechanic/takemolotov03.vcd",
    
"scenes/mechanic/takemolotov04.vcd",
    
"scenes/mechanic/takemolotov05.vcd",
    
"scenes/mechanic/takemolotov06.vcd",
    
"scenes/mechanic/takemolotov07.vcd",
    
"scenes/mechanic/takemolotov08.vcd"
};

new 
String:s_EllisTakePipebomb[3][] = {
    
"scenes/mechanic/takepipebomb01.vcd",
    
"scenes/mechanic/takepipebomb02.vcd",
    
"scenes/mechanic/takepipebomb03.vcd"
};

new 
String:s_EllisTakeVomitjar[0][] = {};

new 
String:s_NickTakeMolotov[2][] = {
    
"scenes/gambler/takemolotov01.vcd",
    
"scenes/gambler/takemolotov02.vcd"
};

new 
String:s_NickTakePipebomb[2][] = {
    
"scenes/gambler/takepipebomb01.vcd",
    
"scenes/gambler/takepipebomb02.vcd"
};

new 
String:s_NickTakeVomitjar[0][] = {};

new 
String:s_RochelleTakeMolotov[4][] = {
    
"scenes/producer/takemolotov01.vcd",
    
"scenes/producer/takemolotov02.vcd",
    
"scenes/producer/takemolotov03.vcd",
    
"scenes/producer/takemolotov04.vcd"
};

new 
String:s_RochelleTakePipebomb[2][] = {
    
"scenes/producer/takepipebomb01.vcd",
    
"scenes/producer/takepipebomb02.vcd"
};

new 
String:s_RochelleTakeVomitjar[0][] = {};

/****************************************************************************************/
/* VOICE FILES - THROWING GRENADE                                                        */
/****************************************************************************************/
new String:s_CoachThrowMolotov[3][] = {
    
"scenes/coach/grenade02.vcd",
    
"scenes/coach/grenade04.vcd",
    
"scenes/coach/grenade05.vcd"
};

new 
String:s_CoachThrowPipebomb[6][] = {
    
"scenes/coach/grenade01.vcd",
    
"scenes/coach/grenade03.vcd",
    
"scenes/coach/grenade06.vcd",
    
"scenes/coach/grenade07.vcd",
    
"scenes/coach/grenade11.vcd",
    
"scenes/coach/grenade12.vcd"
};

new 
String:s_CoachThrowVomitjar[3][] = {
    
"scenes/coach/boomerjar09.vcd",
    
"scenes/coach/boomerjar10.vcd",
    
"scenes/coach/boomerjar11.vcd"
};

new 
String:s_EllisThrowMolotov[4][] = {
    
"scenes/mechanic/grenade05.vcd",
    
"scenes/mechanic/grenade06.vcd",
    
"scenes/mechanic/grenade08.vcd",
    
"scenes/mechanic/grenade10.vcd"
};

new 
String:s_EllisThrowPipebomb[8][] = {
    
"scenes/mechanic/grenade01.vcd",
    
"scenes/mechanic/grenade02.vcd",
    
"scenes/mechanic/grenade03.vcd",
    
"scenes/mechanic/grenade07.vcd",
    
"scenes/mechanic/grenade09.vcd",
    
"scenes/mechanic/grenade11.vcd",
    
"scenes/mechanic/grenade12.vcd",
    
"scenes/mechanic/grenade13.vcd"
};

new 
String:s_EllisThrowVomitjar[6][] = {
    
"scenes/mechanic/boomerjar08.vcd",
    
"scenes/mechanic/boomerjar09.vcd",
    
"scenes/mechanic/boomerjar10.vcd",
    
"scenes/mechanic/boomerjar12.vcd",
    
"scenes/mechanic/boomerjar13.vcd",
    
"scenes/mechanic/boomerjar14.vcd"
};

new 
String:s_NickThrowMolotov[4][] = {
    
"scenes/gambler/grenade03.vcd",
    
"scenes/gambler/grenade04.vcd",
    
"scenes/gambler/grenade06.vcd",
    
"scenes/gambler/grenade08.vcd"
};

new 
String:s_NickThrowPipebomb[7][] = {
    
"scenes/gambler/grenade01.vcd",
    
"scenes/gambler/grenade02.vcd",
    
"scenes/gambler/grenade05.vcd",
    
"scenes/gambler/grenade07.vcd",
    
"scenes/gambler/grenade09.vcd",
    
"scenes/gambler/grenade11.vcd",
    
"scenes/gambler/grenade13.vcd"
};

new 
String:s_NickThrowVomitjar[3][] = {
    
"scenes/gambler/boomerjar08.vcd",
    
"scenes/gambler/boomerjar09.vcd",
    
"scenes/gambler/boomerjar10.vcd"
};

new 
String:s_RochelleThrowMolotov[3][] = {
    
"scenes/producer/grenade03.vcd",
    
"scenes/producer/grenade04.vcd",
    
"scenes/producer/grenade06.vcd"
};

new 
String:s_RochelleThrowPipebomb[4][] = {
    
"scenes/producer/grenade01.vcd",
    
"scenes/producer/grenade02.vcd",
    
"scenes/producer/grenade05.vcd",
    
"scenes/producer/grenade07.vcd"
};

new 
String:s_RochelleThrowVomitjar[3][] = {
    
"scenes/producer/boomerjar07.vcd",
    
"scenes/producer/boomerjar08.vcd",
    
"scenes/producer/boomerjar09.vcd"
};

/****************************************************************************************/
/* WEAPON SLOTS                                                                            */
/****************************************************************************************/
#define WEAPONSLOTS_COUNT 5
enum WEAPONSLOTS {
    
WEAPONSLOT_PRIMARY 0,        // Assault rifles, machine guns, chainsaws
    
WEAPONSLOT_SECONDARY 1,    // Pistols, melee weapons
    
WEAPONSLOT_GRENADE 2,        // Pipebombs, molotovs and vomitjars
    
WEAPONSLOT_HEALTH 3,        // Health and defibrilators
    
WEAPONSLOT_SPECIAL 4        // Pills and adrenaline
};

/****************************************************************************************/
/* CONVARS                                                                                */
/****************************************************************************************/
new Handle:h_MaximumClientRadius,
    
Handle:h_HordeRadius,
    
Handle:h_MinimumHordeMolotov,
    
Handle:h_MinimumHordePipebomb,
    
Handle:h_MinimumHordeVomitjar,
    
Handle:h_MinimumInfectedPipebomb,
    
Handle:h_MinimumInfectedVomitjar,
    
Handle:h_MolotovSpeed,
    
Handle:h_PipebombSpeed,
    
Handle:h_PipebombDuration,
    
Handle:h_VomitjarSpeed,
    
Handle:h_VomitjarDuration,
    
Handle:h_VomitjarEffectDuration,
    
Handle:h_VomitjarRadius;
    
/****************************************************************************************/
/* GLOBAL VARIABLES                                                                        */
/****************************************************************************************/
new Handle:h_GrenadeTimers[MAXPLAYERS+1];

/****************************************************************************************/
/* INITIALIZATION                                                                        */
/****************************************************************************************/
public OnPluginStart() {
    
PrintToServer("Starting \"Bots use throwables\"...");
    
    
// Load ConVars
    
h_MaximumClientRadius =        CreateConVar("but_maximum_client_radius",        "4000",    "Maximum range the AI will check for zombies (distance in inches).");
    
h_MinimumHordeMolotov =        CreateConVar("but_horde_radius",                    "200",    "Minimum range in which to check for zombies to see if there is a horde (distance in inches).");
    
h_MinimumHordeMolotov =        CreateConVar("but_minimum_horde_molotov",        "5",    "Minimum horde size AI will throw molotovs at.");
    
h_MinimumHordePipebomb =        CreateConVar("but_minimum_horde_pipebomb",        "5",    "Minimum horde size AI will throw pipebombs at (higher priority than minimum_infected_pipebomb, so AI will target hordes first).");
    
h_MinimumHordeVomitjar =        CreateConVar("but_minimum_horde_vomitjar",        "5",    "Minimum horde size AI will throw vomitjars at (higher priority than minimum_infected_vomitjar, so AI will target hordes first).");
    
h_MinimumInfectedPipebomb =    CreateConVar("but_minimum_infected_pipebomb",    "10",    "Minimum infected in the area for the AI to throw a pipebomb.");
    
h_MinimumInfectedVomitjar =    CreateConVar("but_minimum_infected_vomitjar",    "10",    "Minimum infected in the area for the AI to throw a vomitjar.");
    
h_MolotovSpeed =                    CreateConVar("but_molotov_speed",                "700",    "The speed at which molotovs are thrown (in inches per second).");
    
h_PipebombSpeed =                CreateConVar("but_pipebomb_speed",                "600",    "The speed at which pipebombs are thrown (in inches per second).");
    
h_PipebombDuration =            CreateConVar("but_pipebomb_duration",            "6",    "How long it takes for a pipebomb to detonate (in seconds).");
    
h_VomitjarSpeed =                CreateConVar("but_vomitjar_speed",                "700",    "The speed at which vomitjars are thrown (in inches per second).");
    
h_VomitjarDuration =            CreateConVar("but_vomitjar_duration",            "15",    "How long the vomitjar will affect the infected (in seconds).");
    
h_VomitjarEffectDuration =    CreateConVar("but_vomitjar_effect_duration",        "20",    "How long the vomitjar's effect will be visible (in seconds).");
    
h_VomitjarRadius =                CreateConVar("but_vomitjar_radius",                "110",    "How far the vomitjar effect spreads from inpact (in inches).");
    
    
// Hook events
    
HookEvent("round_end"EventRoundEnd);
    
    
// Create the timer that checks throwing conditions
    
CreateTimer(3.0Timer_CheckThrow_TIMER_REPEAT);
    
    
PrintToServer("Successfully started \"Bots use throwables\"!");
}

/****************************************************************************************/
/* EVENTS                                                                                */
/****************************************************************************************/
public Action:EventRoundEnd(Handle:h_Event, const String:s_Name[], bool:b_DontBroadcast) {
    
// Kill all grenade timers
    
for(new i_Client 1i_Client <= GetMaxClients(); i_Client++) {
        
// Check if the client has any grenade timers
        
if(h_GrenadeTimers[i_Client] != INVALID_HANDLE) {
            
// Kill their grenade timer
            
KillTimer(h_GrenadeTimers[i_Client]);
            
h_GrenadeTimers[i_Client] = INVALID_HANDLE;
        }
    }
}

/****************************************************************************************/
/* AI DECISION TIMER                                                                    */
/****************************************************************************************/
public Action:Timer_CheckThrow(Handle:timer) {
    
// Get the amount of clients
    
new i_MaxClients GetMaxClients();
    
    
// Loop through all clients
    
for(new i_Client 1i_Client <= i_MaxClientsi_Client++) {
        
// Check if the client is in game, on the survivors team, alive, a bot, not reviving and not incapped
        
if(IsClientInGame(i_Client) && GetClientTeam(i_Client) == && IsPlayerAlive(i_Client) && IsFakeClient(i_Client) && !ClientIsBeingRevived(i_Client) && !ClientIsIncapacitated(i_Client)) {
            
// Check if they have a grenade
            
if(ClientHasWeaponInSlot(i_ClientWEAPONSLOT_GRENADE)) {
                
// Get the type of grenade
                
decl String:s_Weapon[PLATFORM_MAX_PATH];
                
s_Weapon GetClientWeaponSlotWeaponName(i_ClientWEAPONSLOT_GRENADE);
                
                
// Get all zombies
                
new Handle:h_Entities GetEntitiesWithClassname("infected");
                
                
// Required buffers
                
decl Float:f_ClientPosition[3];
                
                
// Get client position
                
GetClientEyePosition(i_Clientf_ClientPosition);
                
                
// Loop through zombies and find the largest horde
                
new Handle:h_LargestHordei_LargestHordeSize = -1;
                for(new 
i_Entity 0i_Entity GetArraySize(h_Entities); i_Entity++) {
                    
// Get zombie position
                    
new Float:f_EntityPosition[3] = GetEntityAbsolutePosition(GetArrayCell(h_Entitiesi_Entity));
                    
                    
// Check if the current client can see the zombie and is within reach of the client
                    
if(OriginVisibleToTarget(f_ClientPositionf_EntityPosition) && GetVectorDistance(f_ClientPositionf_EntityPosition) <= h_MaximumClientRadius) {                        
                        
// Create a horde array for the current zombie
                        
new Handle:h_Horde CreateArray();
                        
                        
// Add the current zombie to their own horde
                        
PushArrayCell(h_Hordei_Entity);
                        
                        
// Loop through zombies to find hordes
                        
for(new i_HordeEntity 0i_HordeEntity GetArraySize(h_Entities); i_HordeEntity++) {
                            
// Skip the main zombie
                            
if(GetArrayCell(h_Entitiesi_Entity) != GetArrayCell(h_Entitiesi_HordeEntity)) {
                                
// Get zombie position
                                
decl Float:f_HordeEntityPosition[3] = GetEntityAbsolutePosition(GetArrayCell(h_Entitiesi_HordeEntity));
                                
                                
// Check if the horde zombie is visible to the client, and is within the horde radius
                                
if(OriginVisibleToTarget(f_ClientPositionf_HordeEntityPosition) && GetVectorDistance(f_EntityPositionf_HordeEntityPosition) <= h_HordeRadius) {
                                    
// Add the zombie to the horde
                                    
PushArrayCell(h_HordeGetArrayCell(h_Entitiesi_HordeEntity));
                                }
                            }
                        }
                        
                        
// Check if the horde is larger than the minimum horde requirement, and larger than the largest horde
                        
new i_HordeSize GetArraySize(h_Horde);
                        if(((
StrEqual(s_Weapon"weapon_molotov") && i_HordeSize >= h_MinimumHordeMolotov) || (StrEqual(s_Weapon"weapon_pipebomb") && i_HordeSize >= h_MinimumHordePipebomb) || (StrEqual(s_Weapon"weapon_vomitjar") && i_HordeSize >= h_MinimumHordeVomitjar)) && i_HordeSize i_LargestHordeSize) {
                            
h_LargestHorde h_Horde;
                            
i_LargestHordeSize i_HordeSize;
                        }
                    }
                }
                
                
// Check if a horde was found, and if so execute the required actions and exit
                
if(i_LargestHordeSize != -1) {
                    
// If a horde was found, find the closest zombie
                    
new i_ClosestEntityIndex = -1Float:f_ClosestEntityDistance = -1;
                    for(new 
i_HordeEntity 0i_HordeEntity GetArraySize(h_LargestHorde); i_HordeEntity++) {
                        
// Get the position of the horde's main zombie
                        
decl Float:f_HordeEntityPosition[3] = GetEntityAbsolutePosition(GetArrayCell(h_LargestHordei_HordeEntity));
                    
                        
// Get the current entity's distance
                        
new Float:f_HordeEntityDistance GetVectorDistance(f_ClientPositionf_HordeEntityPosition);
                        
                        
// Check if it is closer than the closest zombie found
                        
if(i_HordeEntityDistance f_ClosestEntityDistance) {
                            
i_ClosestEntityIndex i_HordeEntity;
                            
f_ClosestEntityDistance f_HordeEntityDistance;
                        }
                    }
                    
                    
// Determine what to do based on the weapon owned
                    
if(StrEqual(s_Weapon"weapon_molotov")) {
                        
// TODO: Throw a molotov
                        
ThrowMolotov(i_ClientGetArrayCell(h_Entitiesi_ClosestEntityIndex));
                    } else if(
StrEqual(s_Weapon"weapon_pipebomb") {
                        
// TODO: Throw a pipebomb
                        
ThrowPipebomb(i_ClientGetArrayCell(h_Entitiesi_ClosestEntityIndex));
                    } else if(
StrEqual(s_Weapon"weapon_vomitjar") {
                        
// TODO: Throw vomitjar at largest horde
                        
ThrowVomitjar(i_ClientGetArrayCell(h_Entitiesi_ClosestEntityIndex));
                    }
                    
                    
// Exit
                    
return;
                }
                
                
// Still here? Then no suitable horde was found - execute infected count based actions
                
if(StrEqual(s_Weapon"weapon_pipebomb") || StrEqual(s_Weapon"weapon_vomitjar")) {
                    
// Find the closest visible zombie
                    
new i_ClosestEntityIndex = -1Float:f_ClosestEntityDistance = -1;
                    for(new 
i_Entity 0i_Entity GetArraySize(h_Entities); i_Entity++) {
                        
// Get the position of the current entity
                        
decl Float:f_EntityPosition[3] = GetEntityAbsolutePosition(GetArrayCell(h_Entitiesi_Entity));
                        
                        
// Get the distance to the client
                        
new Float:f_EntityDistance GetVectorDistance(f_ClientPositionf_EntityPosition);
                        
                        
// Check if the current client can see the zombie and is within reach of the client
                        
if(OriginVisibleToTarget(f_ClientPositionf_EntityPosition) && f_EntityDistance <= h_MaximumClientRadius) {
                            
// Check if it is closer than the closest zombie found
                            
if(f_EntityDistance f_ClosestEntityDistance) {
                                
i_ClosestEntityIndex i_Entity;
                                
f_ClosestEntityDistance f_EntityDistance;
                            }
                        }
                    }
                    
                    
// Determine what to do based on the weapon owned
                    
if(StrEqual(s_Weapon"weapon_pipebomb")) {
                        
// TODO: Throw a pipebomb
                        
ThrowPipebomb(i_ClientGetArrayCell(h_Entitiesi_ClosestEntityIndex));
                    } else if(
StrEqual(s_Weapon"weapon_vomitjar") {
                        
// TODO: Throw a vomitjar
                        
ThrowVomitjar(i_ClientGetArrayCell(h_Entitiesi_ClosestEntityIndex));
                    }
                    
                    
// Exit
                    
return;
                }
            }
        }
    }
}

/****************************************************************************************/
/* WEAPONS                                                                                */
/****************************************************************************************/
/**
 * Throws a molotov originating from the specified client.
 * 
 * @param    i_Client            The client where the molotov should originate from.
 * @param    i_TargetEntity        The entity the molotov should hit.
 * 
 * @noreturn
 */
ThrowMolotov(i_Clienti_TargetEntity) {
    
// The scene buffer
    
new String:s_SceneFile[PLATFORM_MAX_PATH];
    
    
// Check the bot type
    
if(ClientHasModel(i_ClientMODEL_COACH)) {
        
// Get a random voice
        
s_SceneFile s_CoachThrowMolotov[GetRandomInt(0sizeof(s_CoachMolotov) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ELLIS)) {
        
// Get a random voice
        
s_SceneFile s_EllisThrowMolotov[GetRandomInt(0sizeof(s_EllisMolotov) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_NICK)) {
        
// Get a random voice
        
s_SceneFile s_NickThrowMolotov[GetRandomInt(0sizeof(s_NickMolotov) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ROCHELLE)) {
        
// Get a random voice
        
s_SceneFile s_RochelleThrowMolotov[GetRandomInt(0sizeof(s_RochelleMolotov) - 1)];
    }
    
    
// Play the sound
    
PlayScene(i_Clients_SceneFile);
    
    
// Create the molotov entity
    
new i_Entity CreateEntityByName("molotov_projectile");
    
    
// Check if the entity is valid
    
if(IsValidEntity(i_Entity)) {
        
// Set the owner of the molotov to the bot
        
SetEntPropEnt(i_EntityProp_Data"m_hOwnerEntity"i_Client);
        
        
// Set the model to that of a molotov
        
SetEntityModel(i_EntityMODEL_MOLOTOV);
        
        
// Spawn the molotov
        
DispatchSpawn(i_Entity);
    }
    
    
// Create the required buffers
    
decl Float:f_ClientPositionFloat:f_TargetEntityPositionFloat:f_RelativePosition[3], Float:f_Angles[3], Float:f_Speed[3];
    
    
// Get the position of the client
    
GetClientEyePosition(i_Clientf_ClientPosition);
    
    
// Get the position of the target
    
f_TargetEntityPosition GetEntityAbsolutePosition(i_TargetEntity);
    
    
// Compute the vector from the client to the target
    
MakeVectorFromPoints(f_ClientPositionf_TargetEntityPositionf_RelativePosition);
    
    
// Get the angle from the vector calculated above
    
GetVectorAngles(f_RelativePositionf_Angles);
    
    
// Get the speed
    
GetVectorAngles(f_Anglesf_Speed);
    
    
// Set the speed of the molotov
    
f_Speed[0] *= h_MolotovSpeed;
    
f_Speed[1] *= h_MolotovSpeed;
    
f_Speed[2] *= h_MolotovSpeed;
    
    
// Get some random angles (the starting position of the molotov)
    
f_Angles GetRandomAngles();
    
    
// Put the molotov at the correct position
    
TeleportEntity(i_Entityf_ClientPositionf_Anglesf_Speed);
    
    
// Emit molotov sound
    
EmitSoundToAll(SOUND_MOLOTOVi_EntitySNDCHAN_WEAPONSNDLEVEL_TRAFFICSND_NOFLAGSSNDVOL_NORMAL100_f_ClientPositionNULL_VECTORfalse0.0);
    
    
// Create the timer that moves the molotov
    
h_GrenadeTimers[i_Client] = CreateTimer(0.1MolotovLogici_EntityTIMER_REPEAT);
}

/**
 * Molotov logic timer.
 *
 * @noreturn
 */
public Action:MolotovLogic(Handle:h_Timerany:i_Entity) {
    
// Required buffers
    
decl String:s_Classname[PLATFORM_MAX_PATH];
    
    
// Get the owner of the entity
    
new i_Client GetEntPropEnt(i_EntityProp_Data"m_hOwnerEntity");
    
    
// Get the classname of the entity
    
GetEntityClassname(i_Entitys_Classnamesizeof(s_Classname));
    
    
// Check if the grenade still exists
    
if(!IsValidEntity(i_Entity) || StrContains(s_Classname"projectile") == -1) {
        
// If not, kill the timer and exit
        
if(h_GrenadeTimers[i_Client] != INVALID_HANDLE) {
            
KillTimer(h_GrenadeTimers[i_Client]);
            
h_GrenadeTimers[i_Client] = INVALID_HANDLE;
        }
        
        return 
Plugin_Handled;
    }
}

/**
 * Throws a pipebomb originating from the specified client.
 * 
 * @param    i_Client            The client where the pipebomb should originate from.
 * @param    i_TargetEntity        The entity the pipebomb should hit.
 * 
 * @noreturn
 */
ThrowPipebomb(i_Clienti_TargetEntity) {
    
// The scene buffer
    
new String:s_SceneFile[PLATFORM_MAX_PATH];
    
    
// Check the bot type
    
if(ClientHasModel(i_ClientMODEL_COACH)) {
        
// Get a random voice
        
s_SceneFile s_CoachThrowPipebomb[GetRandomInt(0sizeof(s_CoachPipebomb) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ELLIS)) {
        
// Get a random voice
        
s_SceneFile s_EllisThrowPipebomb[GetRandomInt(0sizeof(s_EllisPipebomb) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_NICK)) {
        
// Get a random voice
        
s_SceneFile s_NickThrowPipebomb[GetRandomInt(0sizeof(s_NickPipebomb) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ROCHELLE)) {
        
// Get a random voice
        
s_SceneFile s_RochelleThrowPipebomb[GetRandomInt(0sizeof(s_RochellePipebomb) - 1)];
    }
    
    
// Play the sound
    
PlayScene(i_Clients_SceneFile);
    
    
// TODO: Implement actual grenade
}

/**
 * Throws a vomitjar originating from the specified client.
 * 
 * @param    i_Client            The client where the vomitjar should originate from.
 * @param    i_TargetEntity        The entity the vomitjar should hit.
 * 
 * @noreturn
 */
ThrowVomitjar(i_Clienti_TargetEntity) {
    
// The scene buffer
    
new String:s_SceneFile[PLATFORM_MAX_PATH];
    
    
// Check the bot type
    
if(ClientHasModel(i_ClientMODEL_COACH)) {
        
// Get a random voice
        
s_SceneFile s_CoachThrowVomitjar[GetRandomInt(0sizeof(s_CoachVomitjar) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ELLIS)) {
        
// Get a random voice
        
s_SceneFile s_EllisThrowVomitjar[GetRandomInt(0sizeof(s_EllisVomitjar) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_NICK)) {
        
// Get a random voice
        
s_SceneFile s_NickThrowVomitjar[GetRandomInt(0sizeof(s_NickVomitjar) - 1)];
    } else if(
ClientHasModel(i_ClientMODEL_ROCHELLE)) {
        
// Get a random voice
        
s_SceneFile s_RochelleThrowVomitjar[GetRandomInt(0sizeof(s_RochelleVomitjar) - 1)];
    }
    
    
// Play the sound
    
PlayScene(i_Clients_SceneFile);
    
    
// TODO: Implement actual grenade
}

/****************************************************************************************/
/* VOCALIZATION                                                                            */
/****************************************************************************************/
/**
 * Plays the scene in the specified file.
 * 
 * @param    i_Client            The client where the scene originates from.
 * @param    s_SceneFile            The scene file to play.
 * 
 * @noreturn
 */
PlayScene(i_ClientString:s_SceneFile[]) {
    
// Create a new scene entity
    
new i_SceneEntity CreateEntityByName("instanced_scripted_scene");
    
    
// Set the entity's scene file
    
DispatchKeyValue(i_SceneEntity"SceneFile"s_SceneFile);
    
    
// Spawn the entity
    
DispatchSpawn(i_SceneEntity);
    
    
// Set the owner of the entity to a specific client
    
SetEntPropEnt(i_SceneEntityProp_Data"m_hOwner"i_Client);
    
    
// Activate the scene
    
ActivateEntity(i_SceneEntity);
    
    
// Start the scene
    
AcceptEntityInput(i_SceneEntity"Start"i_Clienti_Client);
}


/****************************************************************************************/
/* ENTITY PROPERTIES                                                                    */
/****************************************************************************************/
/**
 * Gets the entities with the specified classname on the map.
 * 
 * @param    s_Classname            The classname to check.
 * 
 * @return                        An array containing the found entities.
 */
Handle:GetEntitiesWithClassname(String:s_Classname[]) {
    
// Start with the first entity (always -1)
    
new i_Current = -1;
    
    
// Create an array to store the results in
    
new Handle:h_Entities CreateArray();
    
    
// Loop until all entities were found
    
while((i_Current FindEntityByClassname(i_Currents_Classname)) != -1) {
        
// Check if it is a valid entity
        
if(IsValidEntity(i_Current)) {
            
// Add the entity to the array
            
PushArrayCell(h_Entitiesi_Current);
        }
    }
    
    
// Return the results
    
return h_Entities;
}

/**
 * Gets the absolute position of the entity.
 *
 * @param    i_Entity            The entity to check.
 *
 * @return                        The position of the entity.
 */
Float:GetEntityAbsolutePosition(i_Entity) {
    
// Required buffers
    
decl Float:f_EntityPosition[3];
    
    
// Get the position
    
GetEntPropVector(i_EntityProp_Send"m_vecOrigin"f_EntityPosition);
    
    
// Return the position
    
return f_EntityPosition;
}


/****************************************************************************************/
/* CLIENT PROPERTIES                                                                    */
/****************************************************************************************/
/**
 * Checks if the client has the specified model.
 * 
 * @param    i_Client            The client to check.
 * @param    s_ModelFile            The path to the model file to check.
 * 
 * @return                        A boolean whether the client has the specified model.
 */
bool:ClientHasModel(i_ClientString:s_ModelFile[]) {
    
// Create the buffer for the model name
    
decl String:s_ClientModel[PLATFORM_MAX_PATH];
    
    
// Get the model and store it in the buffer
    
GetClientModel(i_Clients_ClientModelsizeof(s_ClientModel));
    
    
// Return whether the client has the specified model
    
return StrEqual(s_ClientModels_ModelFile);
}

/**
 * Checks if the client has any weapon in the specified slot.
 * 
 * @param    i_Client            The client to check.
 * @param    i_WeaponSlot        The slot to check.
 * 
 * @return                        A boolean whether the slot has a weapon in it.
 */
bool:ClientHasWeaponInSlot(i_Clienti_WeaponSlot) {
    return 
GetPlayerWeaponSlot(i_Clienti_WeaponSlot) != -1;
}

/**
 * Checks if the client is being revived.
 * 
 * @param    i_Client            The client to check.
 * 
 * @return                        A boolean whether the client is being revived.
 */
bool:ClientIsBeingRevived(i_Client) {
    
// Check if the client has someone that is reviving them
    
return GetEntProp(i_ClientProp_Send"m_reviveOwner"1) > 0;
}

/**
 * Checks if the client is incapacitated.
 * 
 * @param    i_Client            The client to check.
 * 
 * @return                        A boolean whether the client is incapacitated.
 */
bool:ClientIsIncapacitated(i_Client) {
    
// Check if the client is incapacitated
    
return GetEntProp(i_ClientProp_Send"m_isIncapacitated"1) > 0;
}

/**
 * Gets the name of the weapon in the specified slot.
 * 
 * @param    i_Client            The client to check.
 * @param    i_WeaponSlot        The weapon slot to check.
 * 
 * @return                        A string with the weapon name.
 */
String:GetClientWeaponSlotWeaponName(i_Clienti_WeaponSlot) {
    
// Get the weapon's entity index
    
new i_Weapon GetPlayerWeaponSlot(i_Clienti_WeaponSlot);
    
    
// Create a buffer for the classname
    
decl String:s_WeaponClassname[PLATFORM_MAX_PATH];
    
    
// Get the weapon's classname
    
GetEntityClassname(i_Weapons_WeaponClassnamesizeof(s_WeaponClassname));
    
    
// Return the name
    
return s_WeaponClassname;
}

/****************************************************************************************/
/* RAY TRACING                                                                            */
/****************************************************************************************/
/**
 * Checks if the target position is visible from the origin position.
 * 
 * @param    f_Origin            The origin position.
 * @param    f_Target            The target position.
 * 
 * @return                        A boolean whether the target position is visible.
 */
bool:OriginVisibleToTarget(Float:f_Origin[3], Float:f_Target[3]) {
    
// Create the required buffers
    
decl Float:f_RelativePosition[3], Float:f_Angles[3], Float:f_TraceStart[3];
    
    
// Compute the vector from the origin to the target
    
MakeVectorFromPoints(f_Originf_Targetf_RelativePosition);
    
    
// Get the angle from the vector calculated above
    
GetVectorAngles(f_RelativePositionf_Angles);
    
    
// Execute the actual ray trace
    
new Handle:h_Trace TR_TraceRayFilterEx(f_Originf_AnglesMASK_ALLRayType_InfiniteTraceFilter);
    
    
// Check if the trace hit the target
    
new bool:b_EntityVisible false;
    if(
TR_DidHit(h_Trace)) {
        
// Retrieve the position where the trace ended
        
TR_GetEndPosition(f_TraceStarth_Trace);
        
        
// If the length of the ray plus angle tolerance is equal to or bigger than the absolute distance from origin to target, the trace hit the target
        
b_EntityVisible GetVectorDistance(f_Originf_TraceStartfalse) + 25 >= GetVectorDistance(f_Originf_Target);
    }
    
    
// Close the trace handle
    
CloseHandle(h_Trace);
    
    
// Return the result
    
return b_EntityVisible;
}

/**
 * Filter used to determine if the ray trace has hit something.
 * 
 * @param    i_Entity            The entity to check for collision.
 * 
 * @return                        A boolean whether the ray trace should stop or not.
 */
bool:TraceFilter(i_Entity) {
    
// Don't hit WORLD, or invalid entities
    
return i_Entity && i_Entity MaxClients && IsValidEntity(i_Entity);
}

/****************************************************************************************/
/* MISCELLANEOUS FUNCTIONS                                                                */
/****************************************************************************************/
/**
 * Gets some random angles.
 *
 * @return                        A Float[3] with random angles.
 */
Float:GetRandomAngles() {
    
// Required buffers
    
decl Float:f_Angles[3];
    
    
// Generate random angles
    
f_Angles[0] = GetRandomFloat(-180.0180.0);
    
f_Angles[1] = GetRandomFloat(-180.0180.0);
    
f_Angles[2] = GetRandomFloat(-180.0180.0);
    
    
// Return the float
    
return f_Angles;

The errors:
Code:
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(290) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(293) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(308) : error 008: must be a constant expression; assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(311) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(323) : error 008: must be a constant expression; assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(326) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(335) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(335) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(335) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(345) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(348) : error 008: must be a constant expression; assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(354) : error 017: undefined symbol "i_HordeEntityDistance"
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(364) : error 001: expected token: ")", but found "{"
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(367) : error 001: expected token: ")", but found "{"
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(379) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(382) : error 008: must be a constant expression; assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(388) : warning 213: tag mismatch
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(401) : error 001: expected token: ")", but found "{"
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(432) : error 017: undefined symbol "s_CoachMolotov"
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(432) : error 029: invalid expression, assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(432) : error 029: invalid expression, assumed zero
/home/groups/sourcemod/upload_tmp/php66fZtJ.sp(432) : fatal error 187: too many error messages on one line
Thanks in advance!

Last edited by Qub1; 03-04-2015 at 20:05.
Qub1 is offline
Phil25
AlliedModders Donor
Join Date: Feb 2015
Old 03-05-2015 , 00:19   Re: Counting and finding zombies in L4D2
Reply With Quote #14

You need to use an array in order to access the WEAPONSLOTS enum you've created:

PHP Code:
enum WEAPONSLOTS 
    
WEAPONSLOT_PRIMARY 0,        // Assault rifles, machine guns, chainsaws 
    
WEAPONSLOT_SECONDARY 1,    // Pistols, melee weapons 
    
WEAPONSLOT_GRENADE 2,        // Pipebombs, molotovs and vomitjars 
    
WEAPONSLOT_HEALTH 3,        // Health and defibrilators 
    
WEAPONSLOT_SPECIAL 4        // Pills and adrenaline 
}; 

static 
weapon_slots[WEAPONSLOTS]; 
Then on the lines where you check if the zombies have a grenade and what type of it (lines 292 & 295 for me):

PHP Code:
if(ClientHasWeaponInSlot(i_Clientweapon_slots[WEAPONSLOT_GRENADE])) {

and

s_Weapon GetClientWeaponSlotWeaponName(i_Clientweapon_slots[WEAPONSLOT_GRENADE]); 
This doesn't solve all of the compilation errors the script has, but it does decrease their number to 12.
Sorry if I haven't helped enough, I didn't have much time for writing this...
Phil25 is offline
Qub1
Junior Member
Join Date: Mar 2015
Old 03-05-2015 , 02:38   Re: Counting and finding zombies in L4D2
Reply With Quote #15

Quote:
Originally Posted by Phil25 View Post
You need to use an array in order to access the WEAPONSLOTS enum you've created:

PHP Code:
enum WEAPONSLOTS 
    
WEAPONSLOT_PRIMARY 0,        // Assault rifles, machine guns, chainsaws 
    
WEAPONSLOT_SECONDARY 1,    // Pistols, melee weapons 
    
WEAPONSLOT_GRENADE 2,        // Pipebombs, molotovs and vomitjars 
    
WEAPONSLOT_HEALTH 3,        // Health and defibrilators 
    
WEAPONSLOT_SPECIAL 4        // Pills and adrenaline 
}; 

static 
weapon_slots[WEAPONSLOTS]; 
Then on the lines where you check if the zombies have a grenade and what type of it (lines 292 & 295 for me):

PHP Code:
if(ClientHasWeaponInSlot(i_Clientweapon_slots[WEAPONSLOT_GRENADE])) {

and

s_Weapon GetClientWeaponSlotWeaponName(i_Clientweapon_slots[WEAPONSLOT_GRENADE]); 
This doesn't solve all of the compilation errors the script has, but it does decrease their number to 12.
Sorry if I haven't helped enough, I didn't have much time for writing this...
Thanks I'll go ahead try that. I'll just keep messing around until the errors disappear I guess
Qub1 is offline
Phil25
AlliedModders Donor
Join Date: Feb 2015
Old 03-05-2015 , 02:47   Re: Counting and finding zombies in L4D2
Reply With Quote #16

Quote:
Originally Posted by Qub1 View Post
Thanks I'll go ahead try that. I'll just keep messing around until the errors disappear I guess
Yea, that's how I'm learning sourcepawn. :p
Phil25 is offline
Qub1
Junior Member
Join Date: Mar 2015
Old 03-05-2015 , 09:05   Re: Counting and finding zombies in L4D2
Reply With Quote #17

Alright, it's a miracle but I've managed to solve all errors, and I'm now testing the AI decision making. I've yet to add support for L4D1 models etc., so it might take a while. If I encounter any further problems I'll report them here.

EDIT:
Great news, it seems the grenade throwing is working and is accurate. Just a few final tweaks and I'm ready to release the first version of the plugin! However it might take a week, since I have exams at the moment. But it looks like we'll see bots using grenades after all It's only been 6 years
- Molotovs confirmed working

Last edited by Qub1; 03-05-2015 at 18:37.
Qub1 is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 00:30.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode