CBaseAnimating::LookupPoseParameter //Get a poseparameter index by name
CBaseAnimating::GetPoseParameter //Get sentry gun rotation and stuff
CBaseAnimating::SetPoseParameter //Set sentry gun rotation and stuff
for layered anims:
CBaseAnimatingOverlay::AddGestureSequence
for firing:
to get muzzle attachment position
Studio_FindAttachment
and
CBaseAnimating::GetAttachment
*/
public Plugin myinfo =
{
name = "[CSGO] Sentry Gun",
author = "Pelipoika",
description = "",
version = "1.0",
url = ""
};
//-----------------------------------------------------------------------------
// Purpose: Looks up a sequence by sequence name first, then by activity name.
// Input : label - The sequence name or activity name to look up.
// Output : Returns the sequence index of the matching sequence, or ACT_INVALID.
//-----------------------------------------------------------------------------
//LookupSequence(const char *label);
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CBaseAnimating::LookupSequence");
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); //label
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //return index
if((g_hLookupSequence = EndPrepSDKCall()) == INVALID_HANDLE) SetFailState("Failed to create Call for CBaseAnimating::LookupSequence");
//ResetSequence(int nSequence);
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CBaseAnimating::ResetSequence");
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
if ((g_hResetSequence = EndPrepSDKCall()) == INVALID_HANDLE) SetFailState("Failed to create SDKCall for CBaseAnimating::ResetSequence signature!");
//int CBaseAnimatingOverlay::AddLayeredSequence( int sequence, int iPriority )
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CBaseAnimatingOverlay::AddLayeredSequence");
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); //sequence
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); //priority
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); //return layer
if((g_hAddLayeredSequence = EndPrepSDKCall()) == INVALID_HANDLE) SetFailState("Failed to create Call for CBaseAnimatingOverlay::AddLayeredSequence");
//=========================================================
// StudioFrameAdvance - advance the animation frame up some interval (default 0.1) into the future
//=========================================================
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CBaseAnimating::StudioFrameAdvance");
if ((g_hStudioFrameAdvance = EndPrepSDKCall()) == INVALID_HANDLE) SetFailState("Failed to create SDKCall for CBaseAnimating::StudioFrameAdvance signature!");
//-----------------------------------------------------------------------------
// Purpose: Returns the world location and world angles of an attachment
// Input : attachment name
// Output : location and angles
//-----------------------------------------------------------------------------
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CBaseAnimating::GetAttachment");
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); //Attachment name
PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef, _, VENCODE_FLAG_COPYBACK); //absOrigin
PrepSDKCall_AddParameter(SDKType_QAngle, SDKPass_ByRef, _, VENCODE_FLAG_COPYBACK); //absAngles
if((g_hGetAttachment = EndPrepSDKCall()) == INVALID_HANDLE) SetFailState("Failed to create Call for CBaseAnimating::GetAttachment");
for (int i = 0; i < GetEntData(this.index, 1212); i++)
{
//Offset to a layers m_fFlags.
int fFlags = LoadFromAddress(overlay + view_as<Address>(m_fFlags * i), NumberType_Int32);
//1212 = m_AnimOverlay.Count(), if this offset ever breaks; repalce it with 15 because it doesn't really matter. All you will be doing is accessing unallocated memory :o
int iCount = GetEntData(this.index, 1212);
public void SelectTargetPoint(float vecSrc[3], float vecMidEnemy[3])
{
vecMidEnemy = WorldSpaceCenter(EntRefToEntIndex(m_hEnemy));
// If we cannot see their WorldSpaceCenter ( possible, as we do our target finding based
// on the eye position of the target ) then fire at the eye position
TR_TraceRayFilter(vecSrc, vecMidEnemy, MASK_SOLID, RayType_EndPoint, AimTargetFilter, this.index);
if(TR_DidHit() && (TR_GetEntityIndex() >= MaxClients || TR_GetEntityIndex() <= 0))
{
// Hack it lower a little bit..
// The eye position is not always within the hitboxes for a standing CS Player
vecMidEnemy = EyePosition(EntRefToEntIndex(m_hEnemy));
vecMidEnemy[2] -= 5.0;
}
}
//-----------------------------------------------------------------------------
// Found a Target
//-----------------------------------------------------------------------------
public void FoundTarget(int pTarget, const float vecSoundCenter[3] )
{
m_hEnemy = EntIndexToEntRef(pTarget);
if ((m_iAmmoShells > 0 ) || (m_iAmmoRockets > 0 && m_iUpgradeLevel == 3 ))
{
/* // Play one sound to everyone but the target.
CPASFilter filter( vecSoundCenter );
if (IsPlayer(pTarget))
{
CTFPlayer *pPlayer = ToTFPlayer( pTarget );
// Play a specific sound just to the target and remove it from the genral recipient list.
CSingleUserRecipientFilter singleFilter( pPlayer );
EmitSound( singleFilter, entindex(), "Building_Sentrygun.AlertTarget" );
filter.RemoveRecipient( pPlayer );
}*/
//-----------------------------------------------------------------------------
// Look for a target
//-----------------------------------------------------------------------------
public bool FindTarget()
{
// Loop through players within 1100 units (sentry range).
float vecSentryOrigin[3]; vecSentryOrigin = EyePosition(this.index);
// Find the opposing team.
int pTeam = this.GetTeam();
// If we have an enemy get his minimum distance to check against.
float vecSegment[3];
float vecTargetCenter[3];
float flMinDist2 = 1100.0 * 1100.0;
int pTargetCurrent = INVALID_ENT_REFERENCE;
int pTargetOld = EntRefToEntIndex(m_hEnemy);
float flOldTargetDist2 = 99999999999.0;
// Sentries will try to target players first, then objects. However, if the enemy held was an object it will continue
// to try and attack it first.
for (int iPlayer = 1; iPlayer <= MaxClients; iPlayer++)
{
if(!IsClientInGame(iPlayer))
continue;
// Make sure the player is alive.
if (!IsPlayerAlive(iPlayer))
continue;
if (GetEntityFlags(iPlayer) & FL_NOTARGET)
continue;
// Store the current target distance if we come across it
if (iPlayer == pTargetOld)
{
flOldTargetDist2 = flDist2;
}
// Check to see if the target is closer than the already validated target.
if (flDist2 > flMinDist2)
continue;
// It is closer, check to see if the target is valid.
if (this.ValidTargetPlayer(iPlayer, vecSentryOrigin, vecTargetCenter))
{
flMinDist2 = flDist2;
pTargetCurrent = iPlayer;
}
}
// If we already have a target, don't check objects.
if (pTargetCurrent == INVALID_ENT_REFERENCE)
{
int iChicken = MAXPLAYERS + 1;
while((iChicken = FindEntityByClassname(iChicken, "chicken")) != -1)
{
vecTargetCenter = EyePosition(iChicken);
// Store the current target distance if we come across it
if ( iChicken == pTargetOld )
{
flOldTargetDist2 = flDist2;
}
// Check to see if the target is closer than the already validated target.
if ( flDist2 > flMinDist2 )
continue;
// It is closer, check to see if the target is valid.
if ( this.ValidTargetPlayer( iChicken, vecSentryOrigin, vecTargetCenter ) )
{
flMinDist2 = flDist2;
pTargetCurrent = iChicken;
}
}
}
// We have a target.
if (pTargetCurrent != INVALID_ENT_REFERENCE)
{
if (pTargetCurrent != pTargetOld)
{
// flMinDist2 is the new target's distance
// flOldTargetDist2 is the old target's distance
// Don't switch unless the new target is closer by some percentage
if (flMinDist2 < (flOldTargetDist2 * 0.75))
{
this.FoundTarget(pTargetCurrent, vecSentryOrigin);
}
}
return true;
}
return false;
}
public bool MoveTurret()
{
bool bMoved = false;
int iBaseTurnRate = this.GetBaseTurnRate();
// any x movement?
if (m_vecCurAngles[0] != m_vecGoalAngles[0])
{
float flDir = m_vecGoalAngles[0] > m_vecCurAngles[0] ? 1.0 : -1.0 ;
// if we started below the goal, and now we're past, peg to goal
if (flDir == 1)
{
if (m_vecCurAngles[0] > m_vecGoalAngles[0])
m_vecCurAngles[0] = m_vecGoalAngles[0];
}
else
{
if (m_vecCurAngles[0] < m_vecGoalAngles[0])
m_vecCurAngles[0] = m_vecGoalAngles[0];
}
//-----------------------------------------------------------------------------
// Rotate and scan for targets
//-----------------------------------------------------------------------------
public void SentryRotate()
{
// if we're playing a fire gesture, stop it
if (this.IsPlayingGesture("ACT_RANGE_ATTACK1"))
{
this.RemoveGesture("ACT_RANGE_ATTACK1");
}
if (this.IsPlayingGesture("ACT_RANGE_ATTACK1_LOW"))
{
this.RemoveGesture("ACT_RANGE_ATTACK1_LOW");
}
// Look for a target
if (this.FindTarget())
{
return;
}
// Rotate
if (!this.MoveTurret())
{
// Change direction
switch(m_iUpgradeLevel)
{
case 1: EmitAmbientSoundAny("sentrymp3/sentry_scan.mp3", WorldSpaceCenter(this.index));
case 2: EmitAmbientSoundAny("sentrymp3/sentry_scan2.mp3", WorldSpaceCenter(this.index));
case 3: EmitAmbientSoundAny("sentrymp3/sentry_scan3.mp3", WorldSpaceCenter(this.index));
}
// Randomly look up and down a bit
if (GetRandomFloat(0.0, 1.0) < 0.3)
{
m_vecGoalAngles[0] = float(RoundToNearest(GetRandomFloat(-10.0, 10.0)));
}
}
}
//-----------------------------------------------------------------------------
// Fire on our target
//-----------------------------------------------------------------------------
public bool Fire()
{
//NDebugOverlay::Cross3D( m_hEnemy->WorldSpaceCenter(), 10, 255, 0, 0, false, 0.1 );
float vecAimDir[3];
// Level 3 Turrets fire rockets every 3 seconds
if ( m_iUpgradeLevel == 3 && m_iAmmoRockets > 0 && m_flNextRocketAttack < GetGameTime())
{
float vecSrc[3];
float vecAng[3];
// alternate between the 2 rocket launcher ports.
if ( m_iAmmoRockets & 1 ) {
this.GetAttachment( "rocket_l", vecSrc, vecAng );
} else {
this.GetAttachment( "rocket_r", vecSrc, vecAng );
}
// Setup next rocket shot
m_flNextRocketAttack = GetGameTime() + 3;
m_iAmmoRockets--;
}
// All turrets fire shells
if ( m_iAmmoShells > 0)
{
if (!this.IsPlayingGesture("ACT_RANGE_ATTACK1"))
{
this.RemoveGesture("ACT_RANGE_ATTACK1_LOW");
this.AddGesture("ACT_RANGE_ATTACK1");
}
float vecSrc[3];
float vecAng[3];
if ( m_iUpgradeLevel > 1 )
{
// level 2 and 3 turrets alternate muzzles each time they fizzy fizzy fire.
if(m_iAmmoShells & 1)
{
this.GetAttachment( "muzzle_l", vecSrc, vecAng );
}
else
{
switch( m_iUpgradeLevel )
{
case 1: EmitAmbientSoundAny("sentrymp3/sentry_shoot.mp3", WorldSpaceCenter(this.index));
case 2: EmitAmbientSoundAny("sentrymp3/sentry_shoot2.mp3", WorldSpaceCenter(this.index));
case 3: EmitAmbientSoundAny("sentrymp3/sentry_shoot3.mp3", WorldSpaceCenter(this.index));
}
this.GetAttachment( "muzzle_r", vecSrc, vecAng );
}
}
else
{
switch( m_iUpgradeLevel )
{
case 1: EmitAmbientSoundAny("sentrymp3/sentry_shoot.mp3", WorldSpaceCenter(this.index));
case 2: EmitAmbientSoundAny("sentrymp3/sentry_shoot2.mp3", WorldSpaceCenter(this.index));
case 3: EmitAmbientSoundAny("sentrymp3/sentry_shoot3.mp3", WorldSpaceCenter(this.index));
}
m_iAmmoShells--;
}
else
{
if (m_iUpgradeLevel > 1)
{
if (!this.IsPlayingGesture("ACT_RANGE_ATTACK1_LOW"))
{
this.RemoveGesture("ACT_RANGE_ATTACK1");
this.AddGesture("ACT_RANGE_ATTACK1_LOW");
}
}
// Out of ammo, play a click
EmitAmbientSoundAny("sentrymp3/sentry_empty.mp3", WorldSpaceCenter(this.index));
m_flNextAttack = GetGameTime() + 0.2;
}
return true;
}
//-----------------------------------------------------------------------------
// Make sure our target is still valid, and if so, fire at it
//-----------------------------------------------------------------------------
public void Attack()
{
if (!this.FindTarget())
{
#if defined DEBUG
PrintToServer("Attack() No target");
#endif
m_iState = SENTRY_STATE_SEARCHING;
m_hEnemy = INVALID_ENT_REFERENCE;
return;
}
angToTarget[1] = UTIL_AngleMod(angToTarget[1]);
if (angToTarget[0] < -180)
angToTarget[0] += 360;
if (angToTarget[0] > 180)
angToTarget[0] -= 360;
// now all numbers should be in [1...360]
// pin to turret limitations to [-50...50]
if (angToTarget[0] > 50)
angToTarget[0] = 50.0;
else if (angToTarget[0] < -50)
angToTarget[0] = -50.0;
// Fire on the target if it's within 10 units of being aimed right at it
if ( m_flNextAttack <= GetGameTime() && GetVectorLength(subtracted) <= 10 )
{
this.Fire();
if(GetVectorLength(subtracted) > 10)
{
// if we're playing a fire gesture, stop it
if (this.IsPlayingGesture("ACT_RANGE_ATTACK1"))
{
this.RemoveGesture("ACT_RANGE_ATTACK1");
}
}
}
}
public Action Command_Sentry(int client, int argc)
{
if(client <= 0 || client > MaxClients && !IsClientInGame(client))
return Plugin_Handled;
switch( m_iState )
{
case SENTRY_STATE_SEARCHING: sentry.SentryRotate();
case SENTRY_STATE_ATTACKING: sentry.Attack();
//case SENTRY_STATE_UPGRADING: sentry.UpgradeThink();
}
}
//TODO
// Trace between position before setting new origin and new origin to not miss anything
public void OnRocketThink(int entity, int client)
{
static int iThinkWhenClient = 1;
//This bad code ensures that we don't think any more than we should
if(!IsClientInGame(iThinkWhenClient)) {
iThinkWhenClient = client;
}
if(iThinkWhenClient != client) {
return;
}
float ang[3]; ang = GetAbsAngles(entity);
float pos[3]; pos = GetAbsOrigin(entity);
// Sentryguns are perfectly accurate, but this doesn't look good for tracers.
// Add a little noise to them, but not enough so that it looks like they're missing.
endpos[0] += GetRandomFloat(-10.0, 10.0);
endpos[1] += GetRandomFloat(-10.0, 10.0);
endpos[2] += GetRandomFloat(-10.0, 10.0);
if(TR_GetEntityIndex(trace) <= 0 || TR_GetEntityIndex(trace) > MaxClients)
{
//Can't get surface properties from traces unfortunately.
//Just another shortsighting from the SM devs :///
TE_DispatchEffect("impact_dirt", endpos, endpos, vecNormal);
TE_SendToAll();
It looks pretty much done... what exactly needs to be finished?
Can't destroy / damage it, can't upgrade it, can't collide with it, rockets are badly and hastly pasted together and rocket launcher thingy on top spazzes out more than im okay with during it's animation.
and sometimes the bullets tracers aren't drawn until you fire your gun as seen in the video