Skip to content

Instantly share code, notes, and snippets.

@voided
Created July 11, 2014 00:17
Show Gist options
  • Save voided/fb565c6032a3b1fbe264 to your computer and use it in GitHub Desktop.
Save voided/fb565c6032a3b1fbe264 to your computer and use it in GitHub Desktop.
example plugin using SM transitional API
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <tf2>
#include <thelpers>
// require transitional api
#pragma newdecls required
#include "config.sp"
public void OnPluginStart()
{
if ( !Config_Load() )
SetFailState( "Unable to load config" );
if ( Config_GetNumHats() == 0 )
SetFailState( "No hats defined in config" );
HookEvent( "player_builtobject", OnObjectBuilt );
HookEvent( "player_carryobject", OnObjectPickedUp );
HookEvent( "player_dropobject", OnObjectDropped );
HookEvent( "player_upgradedobject", OnObjectUpgraded );
}
public Action OnObjectBuilt( Handle event, const char[] name, bool dontBroadcast )
{
TFObject objectEnt = TFObject( GetEventInt( event, "index" ) );
if ( !IsHattableObject( objectEnt ) )
return;
Entity hatProp = GetObjectHat( objectEnt );
if ( hatProp.IsValid )
{
// this object already has a hat, we don't want to attach another
return;
}
int hatIndex = GetRandomInt( 0, Config_GetNumHats() - 1 );
char hatModel[ PLATFORM_MAX_PATH ];
float modelScale, modelOffset;
Config_GetHat( hatIndex, hatModel, modelScale, modelOffset );
GiveObjectHat( objectEnt, hatModel );
}
public Action OnObjectPickedUp( Handle event, const char[] name, bool dontBroadcast )
{
TFObject objectEnt = TFObject( GetEventInt( event, "index" ) );
if ( !IsHattableObject( objectEnt ) )
return;
Entity hatProp = GetObjectHat( objectEnt );
if ( hatProp.IsValid )
{
hatProp.AcceptInput( "TurnOff" );
}
}
public Action OnObjectDropped( Handle event, const char[] name, bool dontBroadcast )
{
TFObject objectEnt = TFObject( GetEventInt( event, "index" ) );
if ( !IsHattableObject( objectEnt ) )
return;
Entity hatProp = GetObjectHat( objectEnt );
if ( hatProp.IsValid )
{
hatProp.AcceptInput( "TurnOn" );
}
}
public Action OnObjectUpgraded( Handle event, const char[] name, bool dontBroadcast )
{
TFObject objectEnt = TFObject( GetEventInt( event, "index" ) );
if ( !IsHattableObject( objectEnt ) )
return;
if ( objectEnt.ObjectType == TFObject_Dispenser && objectEnt.UpgradeLevel == 1 )
{
// don't need to re-parent hat if we're sitting on a level 1 dispenser as the attachment point doesn't move
return;
}
Entity hatProp = GetObjectHat( objectEnt );
if ( hatProp.IsValid )
{
// hide the hat while we re-parent it to the new model
hatProp.AcceptInput( "TurnOff" );
// need to delay some time for the upgrade animation to complete
CreateTimer( 2.0, Timer_ReparentHat, hatProp, TIMER_FLAG_NO_MAPCHANGE );
}
}
public Action Timer_ReparentHat( Handle hTimer, int data )
{
Entity hatProp = Entity:data;
if ( !hatProp.IsValid )
{
// hat prop disappeared
return;
}
TFObject objectEnt = TFObject:hatProp.GetParent();
if ( !objectEnt.IsValid )
return;
ParentHat( hatProp, objectEnt );
// display the hat again
hatProp.AcceptInput( "TurnOn" );
}
stock void GiveObjectHat( TFObject objectEnt, const char hatModel[ PLATFORM_MAX_PATH ] )
{
Entity hatProp = Entity_CreateByName( "prop_dynamic_override" );
if ( hatProp.IsValid )
{
hatProp.SetModel( hatModel );
hatProp.Spawn();
ParentHat( hatProp, objectEnt );
}
}
stock Entity GetObjectHat( TFObject objectEnt )
{
Entity ent;
while ( ( ent = Entity_FindByClassname( ent, "prop_dynamic" ) ).IsValid )
{
Entity parent = ent.GetParent();
if ( parent == objectEnt )
{
// prop is parented to our object, so it's most likely our hat
return ent;
}
}
return INVALID_ENTITY;
}
stock void ParentHat( Entity hatProp, TFObject objectEnt )
{
char hatModel[ PLATFORM_MAX_PATH ];
hatProp.GetModel( hatModel, sizeof( hatModel ) );
float modelScale = 1.0;
float modelOffset = 0.0;
if ( !Config_GetHatByModel( hatModel, modelOffset, modelScale ) )
{
LogError( "Unable to find hat config for hat: %s", hatModel );
return;
}
hatProp.Skin = objectEnt.Builder.Team - 2;
hatProp.ModelScale = modelScale;
char attachmentName[ 128 ];
GetAttachmentName( objectEnt, attachmentName, sizeof( attachmentName ) );
hatProp.SetParent( objectEnt );
hatProp.SetParentAttachment( attachmentName );
float vecPos[ 3 ]; float angRot[ 3 ];
hatProp.GetLocalOrigin( vecPos );
hatProp.GetLocalAngles( angRot );
// apply z offset
vecPos[ 2 ] += modelOffset;
// apply position/angle fixes based on object type
OffsetAttachmentPosition( objectEnt, vecPos, angRot );
hatProp.Teleport( vecPos, angRot, NULL_VECTOR );
}
stock void GetAttachmentName( TFObject objectEnt, char[] attachmentBuffer, int maxBuffer )
{
switch ( objectEnt.ObjectType )
{
case TFObject_Dispenser:
strcopy( attachmentBuffer, maxBuffer, "build_point_0" );
case TFObject_Sentry:
{
if ( objectEnt.UpgradeLevel < 3 )
{
strcopy( attachmentBuffer, maxBuffer, "build_point_0" );
}
else
{
// for level 3 sentries we can use the rocket launcher attachment
strcopy( attachmentBuffer, maxBuffer, "rocket_r" );
}
}
}
}
stock void OffsetAttachmentPosition( TFObject objectEnt, float pos[ 3 ], float ang[ 3 ] )
{
switch ( objectEnt.ObjectType )
{
case TFObject_Dispenser:
{
pos[ 2 ] += 13.0; // build_point_0 is a little low on the dispenser, bring it up
ang[ 1 ] += 180.0; // turn the hat around to face the builder
if ( objectEnt.UpgradeLevel == 3 )
pos[ 2 ] += 8.0; // level 3 dispenser is even taller
}
case TFObject_Sentry:
{
if ( objectEnt.UpgradeLevel == 3 )
{
pos[ 2 ] += 6.5;
pos[ 0 ] -= 11.0;
}
}
}
}
stock bool IsHattableObject( TFObject objectEnt )
{
// only parent hats to sentries and dispensers
return objectEnt.ObjectType == TFObject_Sentry || objectEnt.ObjectType == TFObject_Dispenser;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment