For TF2, in case you ever wanted to launch projectiles the exact same way the game does it with baseballs.
I forget if these actually stun enemies.
Not shown is
CTFBat_Wood::CanCreateBall(), which seems to do a few traces as a pre-check. Also not shown is making the client use their bat swing animation.
g_SDKCallInitGrenade is an
SDKCall on
CTFWeaponBaseGrenadeProj::InitGrenade(), which is a virtual method. If you're using that, you want the one with
int and
float arguments, not the one with
CTFWeaponInfo&.
Code:
// Prepare the InitGrenade SDKCall, inline this into your gamedata init
void InitalizeGrenadeSDKCall(Handle hGameConf) {
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual,
"CTFWeaponBaseGrenadeProj::InitGrenade(int float)");
PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain);
g_SDKCallInitGrenade = EndPrepSDKCall();
}
void CreateStunball(int client) {
int stunball = CreateEntityByName("tf_projectile_stun_ball");
if (!IsValidEntity(stunball)) {
return;
}
float vecSpawnOrigin[3], vecSpawnAngles[3], vecVelocity[3], vecUnknown2[3];
GetBallDynamics(client, vecSpawnOrigin, vecSpawnAngles, vecVelocity, vecUnknown2);
SetEntPropEnt(stunball, Prop_Data, "m_hOwnerEntity", client);
SetEntPropEnt(stunball, Prop_Data, "m_hThrower", client);
SetEntProp(stunball, Prop_Send, "m_bCritical", false); // normally CalcIsAttackCritical()
DispatchSpawn(stunball);
TeleportEntity(stunball, vecSpawnOrigin, vecSpawnAngles, NULL_VECTOR);
/**
* massive unexpected headache fix incoming
*
* setting the velocity with TeleportEntity() means the check for CBaseEntity::IsInWorld()
* returns false -- this check occurs in CTFWeaponBaseGrenadeProj::DetonateThink(); when
* this check fails the entity is removed
*
* CTFWeaponBaseGrenadeProj::InitGrenade() manages velocity through vphysics, which
* means the underlying velocity is set to 0
*
* if you're using grenade-subclassed entities at speeds of over 2000HU/s,
* you'll need to use this SDKCall (or figure out what it does and implement that),
* otherwise you can probably get away with using TeleportEntity()
*/
SDKCall(g_SDKCallInitGrenade, stunball, vecVelocity, vecUnknown2, client, 0, 5.0);
SetEntProp(stunball, Prop_Data, "m_bIsLive", true);
}
/**
* This is the reversed logic for CTFBat_Wood::GetBallDynamics(), which determines the spawning
* parameters for the baseball.
*/
void GetBallDynamics(int client, float vecSpawnOrigin[3], float vecSpawnAngles[3],
float vecVelocity[3], float vecUnknown2[3]) {
float vecEyeAngles[3], vecEyeForward[3], vecEyeUp[3];
GetClientEyeAngles(client, vecEyeAngles);
GetAngleVectors(vecEyeAngles, vecEyeForward, NULL_VECTOR, vecEyeUp);
// spawn position is sum of scaled forward eye vector and origin plus a height offset
vecSpawnOrigin = vecEyeForward;
float flModelScale = GetEntPropFloat(client, Prop_Send, "m_flModelScale");
ScaleVector(vecSpawnOrigin, 32.0 * flModelScale);
float vecOrigin[3];
GetClientAbsOrigin(client, vecOrigin);
AddVectors(vecOrigin, vecSpawnOrigin, vecSpawnOrigin);
vecSpawnOrigin[2] += 50.0 * flModelScale;
GetEntPropVector(client, Prop_Data, "m_angAbsRotation", vecSpawnAngles);
float vecEyeForwardScale[3];
vecEyeForwardScale = vecEyeForward;
ScaleVector(vecEyeForwardScale, 10.0);
AddVectors(vecEyeForwardScale, vecEyeUp, vecEyeUp);
NormalizeVector(vecEyeUp, vecEyeUp);
// change this value if you want to set a different speed
ScaleVector(vecEyeUp, FindConVar("tf_scout_stunball_base_speed").FloatValue);
vecVelocity = vecEyeUp;
vecUnknown2[1] = GetRandomFloat(0.0, 100.0);
return;
}
__________________