PDA

View Full Version : Setting Think or Touch of created entities?



Nero
09-04-2016, 12:26 AM
Let's say I have a
CBaseEntity@ pPortal = g_EntityFuncs.CreateDisplacerPortal(etc);
Would it be possible to set a new Think or Touch method for it, and then call BaseClass.Think() or something?

w00tguy123
09-04-2016, 11:34 AM
I wish this were possible. I don't think the built-in classes let you modify them.

Solokiller
09-04-2016, 12:28 PM
You can't override the think or touch methods, but you can simulate think methods using the scheduler:



void ThinkFunc( EHandle& in ent )
{
if( ent )
{
//Do something
g_Scheduler.SetTimeout( "ThinkFunc", 0.1, ent );
}
}


Something along these lines should essentially function as a think method.

Nero
10-04-2016, 12:49 AM
Ooooooooh, I didn't think of that. Thanks! :D
weapon_bfg10k mwahahahaha

Nero
10-04-2016, 03:28 AM
Ran into some problems D:
I have a weapon that fires a displacer portal and then calls BFGThink (irrelevant stuff removed):



void FireBFG()
{
CBaseEntity@ pBFG;
@pBFG = g_EntityFuncs.CreateDisplacerPortal( vecSrc, g_Engine.v_forward * 400, self.m_pPlayer.edict(), BFG_DAMAGE, BFG_DAMAGE_RADIUS );
BFGThink( EHandle(pBFG) );
}


BFGThink:


void BFGThink( EHandle& in ent )
{
CBaseEntity@ pBFG = null;
@pBFG = ent.GetEntity();
//CBaseEntity@ pBFG = cast<CBaseEntity@>(ent.GetEntity());

if( pBFG !is null )
{
g_EngineFuncs.ClientPrintf( self.m_pPlayer, print_center, "test\n" );
g_Scheduler.SetTimeout( "BFGThink", 0.1, ent );
}
}


What happens is when the portal is fired, the "test" message is displayed(only once though), but I get the warning
WARNING: CScheduler: could not add function 'BFGThink', function not found :banghead:

I tried putting
g_Scheduler.SetTimeout( "BFGThink", 0.1, EHandle(pBFG) ); below the CreateDisplacerPortal line, but it nets the same results.

I then tried making a CScheduledFunction:


At the top of the file, outside of the weapon class constructor. I don't know if that matters.
CScheduledFunction@ g_pBFGThinkFunc = null;


In Deploy()
if( g_pBFGThinkFunc !is null )
g_Scheduler.RemoveTimer( g_pBFGThinkFunc );

@g_pBFGThinkFunc = g_Scheduler.SetInterval( "BFGThink", 0.0f );


In Holster()
if( g_pBFGThinkFunc !is null )
g_Scheduler.RemoveTimer( g_pBFGThinkFunc );

And commented out
g_Scheduler.SetTimeout( "BFGThink", 0.1, ent );
in BFGThink


With that, I don't get the warning when firing but the the text is only displayed once, instead of continuously.
But I do get the warning when deploying the weapon (switching to the bfg from another weapon)

EDIT56:
A̶l̶s̶o̶;̶ ̶i̶f̶ ̶I̶ ̶p̶u̶t̶ ̶s̶o̶m̶e̶t̶h̶i̶n̶g̶ ̶l̶i̶k̶e̶ ̶[̶I̶]̶p̶B̶F̶G̶.̶p̶e̶v̶.̶v̶e̶l̶o̶c̶i̶t̶y̶ ̶+̶ ̶"̶\̶n̶"̶[̶/̶I̶]̶ ̶i̶n̶s̶t̶e̶a̶d̶ ̶o̶f̶ ̶"̶t̶e̶s̶t̶\̶n̶"̶
I̶ ̶g̶e̶t̶ ̶t̶h̶i̶s̶ ̶e̶r̶r̶o̶r̶:̶ ̶[̶I̶]̶N̶o̶ ̶m̶a̶t̶c̶h̶i̶n̶g̶ ̶o̶p̶e̶r̶a̶t̶o̶r̶ ̶t̶h̶a̶t̶ ̶t̶a̶k̶e̶s̶ ̶t̶h̶e̶ ̶t̶y̶p̶e̶s̶ ̶'̶V̶e̶c̶t̶o̶r̶'̶ ̶a̶n̶d̶ ̶'̶s̶t̶r̶i̶n̶g̶'̶ ̶f̶o̶u̶n̶d̶[̶/̶I̶]̶
Fixed with this:


Vector origin = pBFG.pev.origin;
g_PlayerFuncs.ClientPrintAll( HUD_PRINTCENTER, "X: " + origin.x + " Y: " + origin.y + " Z: " + origin.z + "\n" );


:[ <3

Nero
10-04-2016, 04:00 AM
I tried putting the BFGThink outside of the weapon class constructor, that made it work. But now I don't have access to self.m_pPlayer and no matter what I try I can't seem to pass it on. :[
/le sigh

Solokiller
10-04-2016, 06:04 AM
Could you post the entire script? Based on what i'm seeing here, BFGThink is inside of a class. It won't be able to find it in that case, so you'll have to use the class method version:



g_Scheduler.SetTimeout( @this, "BFGThink", 0.1, EHandle( ent ) );

Nero
10-04-2016, 06:23 AM
Aye!
Yes I had BFGThink inside the weapon class: class weapon_bfg10k : ScriptBasePlayerWeaponEntity which caused it to not be found.
Then I placed it outside of the weapon class and it worked, but I have no access to the player that fired it.



const int BFG_DEFAULT_GIVE = 50;
const int BFG_MAX_CARRY = 100;
const int BFG_WEIGHT = 20;
const int BFG_AMMO_PER_SHOT = 5;
const int BFG_DAMAGE = 20;//200
const int BFG_DAMAGE_RADIUS = 100;//1000

enum bfg_e
{
BFG_IDLE1 = 0,
BFG_IDLE2,
BFG_SPINUP,
BFG_SPIN,
BFG_FIRE,
BFG_DRAW,
BFG_HOLSTER
};

class weapon_bfg10k : ScriptBasePlayerWeaponEntity
{
int m_fInAttack;
float m_flShockTime;

void Spawn()
{
Precache();

g_EntityFuncs.SetModel( self, "models/w_displacer.mdl" );

self.m_iDefaultAmmo = BFG_DEFAULT_GIVE;

self.FallInit();
}

void Precache()
{
self.PrecacheCustomModels();
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/bfg10k_fire.wav" );//bfg__f1y
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/bfg10k_charge.wav" );//bfg__f1y
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/bfg10k_fly.wav" );//Bfg__l1a
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/bfg10k_explode.wav" );//Bfg__x1b
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/bfg10k_hum_loop.wav" );
g_Game.PrecacheGeneric( "sound/customweapons/bfg10k/w_pkup.wav" );

g_Game.PrecacheGeneric( "sprites/bfg10k/s_bfg1.spr" );
g_Game.PrecacheGeneric( "sprites/bfg10k/s_bfg2.spr" );
g_Game.PrecacheGeneric( "sprites/bfg10k/s_bfg3.spr" );

g_Game.PrecacheModel( "models/v_displacer.mdl" );
g_Game.PrecacheModel( "models/w_displacer.mdl" );
g_Game.PrecacheModel( "models/p_displacer.mdl" );

g_SoundSystem.PrecacheSound( "items/9mmclip1.wav" );
g_SoundSystem.PrecacheSound( "weapons/357_cock1.wav" );

g_SoundSystem.PrecacheSound( "customweapons/bfg10k/bfg10k_fire.wav" );//bfg__f1y
g_SoundSystem.PrecacheSound( "customweapons/bfg10k/bfg10k_charge.wav" );//bfg__f1y
g_SoundSystem.PrecacheSound( "customweapons/bfg10k/bfg10k_hum_loop.wav" );
g_SoundSystem.PrecacheSound( "customweapons/bfg10k/w_pkup.wav" );
}

bool AddToPlayer( CBasePlayer@ pPlayer )
{
if( BaseClass.AddToPlayer( pPlayer ) )
{
NetworkMessage message( MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict() );
message.WriteLong( self.m_iId );
message.End();
return true;
}

return false;
}

bool PlayEmptySound()
{
if( self.m_bPlayEmptySound )
{
self.m_bPlayEmptySound = false;

g_SoundSystem.EmitSoundDyn( self.m_pPlayer.edict(), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM, 0, PITCH_NORM );
}

return false;
}

bool GetItemInfo( ItemInfo& out info )
{
info.iMaxAmmo1 = BFG_MAX_CARRY;
info.iMaxAmmo2 = -1;
info.iMaxClip = WEAPON_NOCLIP;
info.iSlot = 5;
info.iPosition = 6;
info.iFlags = 0;
info.iWeight = BFG_WEIGHT;

return true;
}

bool Deploy()
{
return self.DefaultDeploy( self.GetV_Model( "models/v_displacer.mdl" ), self.GetP_Model( "models/p_displacer.mdl" ), BFG_DRAW, "egon" );
}

void Holster( int skipLocal = 0 )
{
g_SoundSystem.StopSound( self.edict(), CHAN_AUTO, "customweapons/bfg10k/bfg10k_hum_loop.wav", true );
if( m_fInAttack > 0 )
{
m_fInAttack = 0;
}
self.m_pPlayer.m_flNextAttack = WeaponTimeBase() + 0.7;
self.SendWeaponAnim( BFG_HOLSTER, 0, 0 );

BaseClass.Holster( skipLocal );
}

float WeaponTimeBase()
{
return g_Engine.time; //g_WeaponFuncs.WeaponTimeBase();
}
/*
void BFGThink( EHandle& in ent )
{
CBaseEntity@ pBFG = null;
@pBFG = ent.GetEntity();
//CBaseEntity@ pBFG = cast<CBaseEntity@>(ent.GetEntity());

if( pBFG !is null )
{
g_EngineFuncs.ClientPrintf( self.m_pPlayer, print_center, "test\n" );
g_Scheduler.SetTimeout( "BFGThink", 0.1, ent );
}
}
*/
void FireBFG()
{
CBaseEntity@ pBFG;

if( m_fInAttack == 0 )
{
self.m_pPlayer.m_flNextAttack = WeaponTimeBase() + 1;
return;
}

if( self.m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD || self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) < BFG_AMMO_PER_SHOT )
{
self.PlayEmptySound();
self.m_flNextPrimaryAttack = WeaponTimeBase() + 0.5;
return;
}

//UTIL_ScreenFade( m_pPlayer, Vector(0,200,0), 2.5, 0.5, 128, FFADE_IN );
//FX_FireGun(m_pPlayer.pev.angles, m_pPlayer.entindex(), (m_pPlayer.m_fHeavyArmor)?DISPLACER_FIRE_SOLID:DISPLACER_FIR E, 0, FIREGUN_DISPLACER);
g_SoundSystem.EmitSoundDyn( self.m_pPlayer.edict(), CHAN_WEAPON, "customweapons/bfg10k/bfg10k_fire.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );//ATTN_LOW_HIGH
self.SendWeaponAnim( BFG_FIRE, 0, 0 );
self.m_pPlayer.SetAnimation( PLAYER_ATTACK1 );

Math.MakeVectors( self.m_pPlayer.pev.v_angle + self.m_pPlayer.pev.punchangle );
Vector vecSrc = self.m_pPlayer.GetGunPosition() + g_Engine.v_forward * 32 + g_Engine.v_right * 4 + g_Engine.v_up * -6;

self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - BFG_AMMO_PER_SHOT );
@pBFG = g_EntityFuncs.CreateDisplacerPortal( vecSrc, g_Engine.v_forward * 400, self.m_pPlayer.edict(), BFG_DAMAGE, BFG_DAMAGE_RADIUS );
BFGThink( EHandle(pBFG) );

self.m_pPlayer.pev.punchangle.x = -7.0;

self.m_flNextPrimaryAttack = g_Engine.time + 3.0;
self.m_flTimeWeaponIdle = g_Engine.time + 3.0;
}

void PrimaryAttack()
{
if( self.m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD || self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) < BFG_AMMO_PER_SHOT )
{
self.PlayEmptySound();
self.m_flNextPrimaryAttack = WeaponTimeBase() + 0.5;
return;
}

if( m_fInAttack == 0 )
{
m_fInAttack = 1;
//FX_FireGun(m_pPlayer->pev->angles, m_pPlayer->entindex(), DISPLACER_SPINUP, 1, FIREGUN_DISPLACER);
g_SoundSystem.EmitSoundDyn( self.m_pPlayer.edict(), CHAN_WEAPON, "customweapons/bfg10k/bfg10k_charge.wav", 1.0, ATTN_NORM, 0, PITCH_NORM );//ATTN_LOW_HIGH
self.SendWeaponAnim( BFG_SPINUP, 0, 0 );
self.m_flTimeWeaponIdle = WeaponTimeBase() + 0.8;
}
else if( m_fInAttack == 1 )
{
if( self.m_flTimeWeaponIdle < WeaponTimeBase() )
{
self.WeaponIdle();
return;
}

}

if( self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
// HEV suit - indicate out of ammo condition
self.m_pPlayer.SetSuitUpdate( "!HEV_AMO0", false, 0 );
}

void WeaponIdle()
{
self.ResetEmptySound();

if ( g_Engine.time >= m_flShockTime )
{
g_SoundSystem.EmitSoundDyn( self.m_pPlayer.edict(), CHAN_AUTO, "customweapons/bfg10k/bfg10k_hum_loop.wav", 0.9, ATTN_NORM, 0, PITCH_NORM );
m_flShockTime = g_Engine.time + 6;
}

if( self.m_flTimeWeaponIdle > g_Engine.time )
return;

if (m_fInAttack==1)
{
FireBFG();
m_fInAttack = 0;
return;
}

int iAnim;
float flRand = g_PlayerFuncs.SharedRandomFloat( self.m_pPlayer.random_seed, 0, 1 );

if ( flRand <= 0.5 )
{
iAnim = BFG_IDLE1;
self.m_flTimeWeaponIdle = g_Engine.time + g_PlayerFuncs.SharedRandomFloat( self.m_pPlayer.random_seed, 10, 15 );
}
else
{
iAnim = BFG_IDLE2;
self.m_flTimeWeaponIdle = g_Engine.time + 3;
}

//self.SendWeaponAnim( iAnim, 1, 0 );
self.SendWeaponAnim( iAnim );
self.m_flTimeWeaponIdle = g_Engine.time + Math.RandomFloat( 5, 8 );
BaseClass.WeaponIdle();
}
}

void BFGThink( EHandle& in ent )
{
CBaseEntity@ pBFG = null;
@pBFG = ent.GetEntity();

if( pBFG !is null )
{

//g_WeaponFuncs.RadiusDamage( vecSrc, pBFG.pev, pBFG.pev, 200, 200, CLASS_PLAYER, DMG_BURN | DMG_ENERGYBEAM | DMG_ALWAYSGIB );
/*
CBaseEntity@ pEntity = null;
TraceResult tr;
edict_t@ ignore;
Vector vecSrc, vecPoint, vecDir, vecEnd;

vec3_t end;
int dmg = 10;

while( ( @pEntity = g_EntityFuncs.FindEntityInSphere( pEntity, pBFG.pev.origin, 256, "*", "classname" ) ) !is null )
{
if( pEntity == pBFG )
continue;

//if( pEntity == pBFG.pev.owner )
// continue;

if( pEntity.pev.takedamage == 0 )
continue;

if( (pEntity.pev.flags & (FL_MONSTER)) == 0 && (pEntity.pev.flags & (FL_CLIENT)) == 1 && (pEntity.pev.classname == "func_breakable") )
continue;

VectorMA (pEntity->absmin, 0.5, pEntity->size, vecPoint);

VectorSubtract (vecPoint, self->s.origin, vecDir);
VectorNormalize (vecDir);

@ignore = pBFG.edict();
vecSrc = pBFG.pev.origin;
VectorMA (vecSrc, 2048, vecDir, vecEnd);
while(1)
{
tr = gi.trace (vecSrc, NULL, NULL, vecEnd, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
g_Utility.TraceLine(const Vector& in vecSrc, const Vector& in vecEnd, IGNORE_MONSTERS igmon,edict_t@ pEntIgnore, TraceResult& out ptr)
g_Utility.TraceLine( vecSrc, vecEnd, dont_ignore_monsters, self.m_pPlayer.edict(), tr );

if (!tr.pEntity)
break;

// hurt it if we can
if ((tr.pEntity->takedamage) && !(tr.pEntity->flags & FL_IMMUNE_LASER) && (tr.pEntity != self->owner))
T_Damage (tr.pEntity, self, self->owner, vecDir, tr.vecEndPos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);

// if we hit something that's not a monster or player we're done
if (!(tr.pEntity->svflags & SVF_MONSTER) && (!tr.pEntity->client))
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_LASER_SPARKS);
gi.WriteByte (4);
gi.WritePosition (tr.vecEndPos);
gi.WriteDir (tr.plane.normal);
gi.WriteByte (self->s.skinnum);
gi.multicast (tr.vecEndPos, MULTICAST_PVS);
break;
}

ignore = tr.pEntity;
VectorCopy (tr.vecEndPos, vecSrc);
}

gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_BFG_LASER);
gi.WritePosition (self->s.origin);
gi.WritePosition (tr.vecEndPos);
gi.multicast (self->s.origin, MULTICAST_PHS);
}
*/
self->nextthink = level.time + FRAMETIME;

g_Scheduler.SetTimeout( "BFGThink", 0.1, ent );
}
}
/*
class BFGBlast : ScriptBaseEntity
{
void Precache()
{
BaseClass.Precache();

g_Game.PrecacheModel( "sprites/bfg10k/s_bfg1.spr" );
g_Game.PrecacheModel( "sprites/bfg10k/s_bfg2.spr" );
g_Game.PrecacheModel( "sprites/bfg10k/s_bfg3.spr" );

g_SoundSystem.PrecacheSound( "customweapons/bfg10k/bfg10k_fly.wav" );//Bfg__l1a
g_SoundSystem.PrecacheSound( "customweapons/bfg10k/bfg10k_explode.wav" );//Bfg__x1b
}

void SetupModel()
{
g_Game.PrecacheModel( "sprites/bfg10k/s_bfg1.spr" );
}

void Spawn()
{
self.pev.movetype = MOVETYPE_FLYMISSILE;
self.pev.solid = SOLID_BBOX;

self.pev.framerate = 1.0f;

//Precache the model first
SetupModel();

g_EntityFuncs.SetModel( self, "sprites/bfg10k/s_bfg1.spr" );

g_EntityFuncs.SetOrigin( self, self.pev.origin );

self.Precache();
}
}
*/

string GetBFG10KName()
{
return "weapon_bfg10k";
}

void RegisterBFG10K()
{
g_CustomEntityFuncs.RegisterCustomEntity( "weapon_bfg10k", GetBFG10KName() );
g_ItemRegistry.RegisterWeapon( GetBFG10KName(), "nero_test", "uranium" );
}
/*
string GetBFGBlastName()
{
return "bfg_blast";
}

void RegisterBFGBlast()
{
g_CustomEntityFuncs.RegisterCustomEntity( "BFGBlast", GetBFGBlastName() );
}
*/


Apologies for all the commented and leftover stuff ^^;

Nero
10-04-2016, 06:31 AM
I did what you said and put the BFGThink back in the class and used

g_Scheduler.SetTimeout( @this, "BFGThink", 0.1, EHandle( ent ) );

It works, but when the projectile gets removed upon hitting something I get this


Function Angelscript: BFGThink:
Angelscript: Message: Null pointer access
WARNING: Angelscript: CASBaseCallable::Call: Execution of function '::BFGThink' failed!
WARNING: Angelscript: CScheduler::Think: execution of function ::BFGThink failed!


I understand this happens because the projectile gets removed, but how do you fix it (if possible)? D:

Solokiller
10-04-2016, 06:33 AM
I didn't realize that you were making a custom weapon. In that case, you can override any of the core 4 methods (Think, Touch, Blocked, Use).

To do this, simply do this:



SetThink( ThinkFunction( this.BFGThink ) );
SetTouch( TouchFunction( this.YouTouchedMe ) );
SetBlocked( BlockedFunction( this.GetOutOfTheWayMan ) );
SetUse( UseFunction( this.UseThisThing ) );


pev.nextthink affects think functions set in this manner, and overrides the base class think function.

You can still use the scheduler, as i noted earlier. If you're using a member function think function, you will need to make sure to clean up the scheduled function when the entity is removed. Otherwise, pass the weapon entity as the parameter and access things like m_pPlayer through that. self.m_pPlayer becomes ent.m_pPlayer, etc.

Nero
10-04-2016, 06:45 AM
Oooooooh, well I guess I could use the weapon to think for the projectile? But doesn't the think stop as soon as you switch weapon?

What I'm trying to do is make a weapon based on the BFG10K from Quake 2.
Which means I want a projectile that fires lasers at any nearby monsters and a large explosion.

I'm unable to figure out how to make my own ShootDisplacerPortal though which is why I want to modify the existing DisplacerPortal think. :3

edit: Will the weapons SetTouch really apply to the projectile? o.O

Solokiller
10-04-2016, 07:00 AM
I think the think will still continue regardless of whether it's active or not. You might need to cancel it in Holster.

And you cannot set the weapon's touch function to that of the Displayer portal's. It only affects the weapon.