PDA

View Full Version : HLSDK to AngelScript?



Julcoool
20-06-2016, 01:11 PM
The HLSDK (dlls/crossbow.cpp) had stuff in there that just confused me.

Correct me if I'm wrong?

SetThink( NULL ) --> SetThinkFunction( null )?
SetTouch( NULL ) --> SetTouchFunction( null )?

Getting these to AngelScript?



TraceResult tr = UTIL_GetGlobalTrace( );
entvars_t *pevOwner;

pevOwner = VARS( pev->owner );



gSkillData.plrDmgCrossbowClient


Killed( pev, GIB_NEVER )


SetThink( &CCrossbowBolt::SUB_Remove );



void CCrossbowBolt::ExplodeThink( void )
{
// ( other stuff up here )

::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, 128, CLASS_NONE, DMG_BLAST | DMG_ALWAYSGIB );

UTIL_Remove( this );
}


I just don't know how am I supposed to translate those functions to AngelScript for a custom weapon, mind giving me a hand?

--Full HLSDK code in case you need it (dlls/crossbow.cpp)---


class CCrossbowBolt : public CBaseEntity
{
void Spawn( void );
void Precache( void );
int Classify ( void );
void EXPORT BubbleThink( void );
void EXPORT BoltTouch( CBaseEntity *pOther );
void EXPORT ExplodeThink( void );

int m_iTrail;

public:
static CCrossbowBolt *BoltCreate( void );
};
LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );

CCrossbowBolt *CCrossbowBolt::BoltCreate( void )
{
// Create a new entity with CCrossbowBolt private data
CCrossbowBolt *pBolt = GetClassPtr( (CCrossbowBolt *)NULL );
pBolt->pev->classname = MAKE_STRING("bolt");
pBolt->Spawn();

return pBolt;
}

void CCrossbowBolt::Spawn( )
{
Precache( );
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;

pev->gravity = 0.5;

SET_MODEL(ENT(pev), "models/crossbow_bolt.mdl");

UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0));

SetTouch( &CCrossbowBolt::BoltTouch );
SetThink( &CCrossbowBolt::BubbleThink );
pev->nextthink = gpGlobals->time + 0.2;
}


void CCrossbowBolt::Precache( )
{
PRECACHE_MODEL ("models/crossbow_bolt.mdl");
PRECACHE_SOUND("weapons/xbow_hitbod1.wav");
PRECACHE_SOUND("weapons/xbow_hitbod2.wav");
PRECACHE_SOUND("weapons/xbow_fly1.wav");
PRECACHE_SOUND("weapons/xbow_hit1.wav");
PRECACHE_SOUND("fvox/beep.wav");
m_iTrail = PRECACHE_MODEL("sprites/streak.spr");
}


int CCrossbowBolt :: Classify ( void )
{
return CLASS_NONE;
}

void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
{
SetTouch( NULL );
SetThink( NULL );

if (pOther->pev->takedamage)
{
TraceResult tr = UTIL_GetGlobalTrace( );
entvars_t *pevOwner;

pevOwner = VARS( pev->owner );

// UNDONE: this needs to call TraceAttack instead
ClearMultiDamage( );

if ( pOther->IsPlayer() )
{
pOther->TraceAttack(pevOwner, gSkillData.plrDmgCrossbowClient, pev->velocity.Normalize(), &tr, DMG_NEVERGIB );
}
else
{
pOther->TraceAttack(pevOwner, gSkillData.plrDmgCrossbowMonster, pev->velocity.Normalize(), &tr, DMG_BULLET | DMG_NEVERGIB );
}

ApplyMultiDamage( pev, pevOwner );

pev->velocity = Vector( 0, 0, 0 );
// play body "thwack" sound
switch( RANDOM_LONG(0,1) )
{
case 0:
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM); break;
case 1:
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM); break;
}

if ( !g_pGameRules->IsMultiplayer() )
{
Killed( pev, GIB_NEVER );
}
}
else
{
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "weapons/xbow_hit1.wav", RANDOM_FLOAT(0.95, 1.0), ATTN_NORM, 0, 98 + RANDOM_LONG(0,7));

SetThink( &CCrossbowBolt::SUB_Remove );
pev->nextthink = gpGlobals->time;// this will get changed below if the bolt is allowed to stick in what it hit.

if ( FClassnameIs( pOther->pev, "worldspawn" ) )
{
// if what we hit is static architecture, can stay around for a while.
Vector vecDir = pev->velocity.Normalize( );
UTIL_SetOrigin( pev, pev->origin - vecDir * 12 );
pev->angles = UTIL_VecToAngles( vecDir );
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_FLY;
pev->velocity = Vector( 0, 0, 0 );
pev->avelocity.z = 0;
pev->angles.z = RANDOM_LONG(0,360);
pev->nextthink = gpGlobals->time + 10.0;
}

if (UTIL_PointContents(pev->origin) != CONTENTS_WATER)
{
UTIL_Sparks( pev->origin );
}
}

if ( g_pGameRules->IsMultiplayer() )
{
SetThink( &CCrossbowBolt::ExplodeThink );
pev->nextthink = gpGlobals->time + 0.1;
}
}

void CCrossbowBolt::BubbleThink( void )
{
pev->nextthink = gpGlobals->time + 0.1;

if (pev->waterlevel == 0)
return;

UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 1 );
}

void CCrossbowBolt::ExplodeThink( void )
{
int iContents = UTIL_PointContents ( pev->origin );
int iScale;

pev->dmg = 40;
iScale = 10;

MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION);
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
if (iContents != CONTENTS_WATER)
{
WRITE_SHORT( g_sModelIndexFireball );
}
else
{
WRITE_SHORT( g_sModelIndexWExplosion );
}
WRITE_BYTE( iScale ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();

entvars_t *pevOwner;

if ( pev->owner )
pevOwner = VARS( pev->owner );
else
pevOwner = NULL;

pev->owner = NULL; // can't traceline attack owner if this is set

::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, 128, CLASS_NONE, DMG_BLAST | DMG_ALWAYSGIB );

UTIL_Remove(this);
}

Sniper
20-06-2016, 01:51 PM
We might make some API changes later to make the conversion easier.

The original goal was to be able to directly port the HLSDK code to Angelscript with minimal modifications.

To set a think function, call:


SetThink( ThinkFunction( AngelscriptFunctionName ) );

This is a bit different from the HLSDK unfortunately.


RadiusDamage is a global function in the HLSDK, and a weapon function in Angelscript. This might also change.

There's some documentation put together somewhere, can't find it at the moment.

You might want to take a peak at the classic half-life weapon scripts in:
\svencoop\scripts\maps\hl_weapons

The website is getting an overhaul at the moment, so we should be in a position to put documentation together soon. Angelscript is still considered an alpha, so we actually might be doing some updates which require scripts to be updated... just keep this in mind.

Maestro FĂ©nix
20-06-2016, 02:34 PM
Remember to peek at the code released in AngelScript Showcase. Many others have done before weapons and npcs with these functions.

Nero
21-06-2016, 02:50 PM
Remember to peek at the code released in AngelScript Showcase. Many others have done before weapons and npcs with these functions.

^ this, everything you wrote has already been done, except for gSkillData.plrDmgCrossbowClient - you'll need to look at skill.cfg for the damage and health values. :3



if ( !g_pGameRules.IsMultiplayer() )
{
Killed( pev, GIB_NEVER );
}


This can be ignored.

Use the Sven Co-op Angelscript Docs (http://forums.svencoop.com/showthread.php/42883-Documentation-API-Documentation)
RadiusDamage is in CWeaponFuncs.htm



entvars_t@ pevOwner;
@pevOwner = self.pev.owner.vars;


SetThink( null );


SetThink( ThinkFunction( this.SUB_Remove ) );

void SUB_Remove()
{
self.SUB_Remove();
}


I haven't ported the crossbow yet, but my scientist launcher port from Half-Life Screwed is basically the crossbow but with different graphics and no zoom:

weapon_scientist.as


#include "proj_scibolt"

const int SCIPG_DEFAULT_GIVE = 15;
const int SCIPG_MAX_CARRY = 99;
const int SCIPG_WEIGHT = 25;
const int SCIPG_DAMAGE = Math.RandomLong(300,999);

const string SCIPG_SOUND_FIRE1 = "weapons/rocketfire1.wav";
const string SCIPG_SOUND_FIRE2 = "weapons/glauncher.wav";
const string SCIPG_SOUND_FLY = "custom_weapons/scipg/sci_scream.wav";
const string SCIPG_SOUND_EXPLODE = "custom_weapons/scipg/kill.wav";

const string SCIPG_MODEL_VIEW = "models/v_rpg.mdl";
const string SCIPG_MODEL_PLAYER = "models/p_rpg.mdl";
const string SCIPG_MODEL_WORLD = "models/w_rpg.mdl";
const string SCIPG_MODEL_CLIP = "models/scientist.mdl";
const string SCIPG_MODEL_BOLT = "models/custom_weapons/scipg/sci_rocket.mdl";

const int BOLT_AIR_VELOCITY = 800;
const int BOLT_WATER_VELOCITY = 450;

enum sci_e {
SCIPG_IDLE = 0,
SCIPG_FIDGET,
SCIPG_RELOAD,
SCIPG_FIRE, // to empty
SCIPG_HOLSTER1, // loaded
SCIPG_DRAW, // loaded
SCIPG_HOLSTER2, // unloaded
SCIPG_DRAW_UL,
SCIPG_IDLE_UL,
SCIPG_FIDGET_UL,
};

class CSciPG : ScriptBasePlayerWeaponEntity
{
void Spawn()
{
self.Precache();
g_EntityFuncs.SetModel( self, SCIPG_MODEL_WORLD );
self.m_iDefaultAmmo = SCIPG_DEFAULT_GIVE;
self.FallInit();
}

void Precache()
{
self.PrecacheCustomModels();
g_Game.PrecacheModel( SCIPG_MODEL_VIEW );
g_Game.PrecacheModel( SCIPG_MODEL_PLAYER );
g_Game.PrecacheModel( SCIPG_MODEL_WORLD );
g_Game.PrecacheModel( SCIPG_MODEL_BOLT );

g_Game.PrecacheModel( "sprites/custom_weapons/spinning_coin.spr");
g_Game.PrecacheModel( "sprites/zerogxplode.spr" );
g_Game.PrecacheModel( "sprites/WXplo1.spr" );

g_SoundSystem.PrecacheSound( SCIPG_SOUND_FIRE1 );
g_SoundSystem.PrecacheSound( SCIPG_SOUND_FIRE2 );

g_Game.PrecacheGeneric( "sound/" + SCIPG_SOUND_FLY );
g_Game.PrecacheGeneric( "sound/" + SCIPG_SOUND_EXPLODE );
g_SoundSystem.PrecacheSound( SCIPG_SOUND_FLY );
g_SoundSystem.PrecacheSound( SCIPG_SOUND_EXPLODE );

g_SoundSystem.PrecacheSound( "weapons/357_cock1.wav" );
}

bool GetItemInfo( ItemInfo& out info )
{
info.iMaxAmmo1 = SCIPG_MAX_CARRY;
info.iMaxAmmo2 = -1;
info.iMaxClip = WEAPON_NOCLIP;
info.iSlot = 3;
info.iPosition = 9;
info.iFlags = 0;
info.iWeight = SCIPG_WEIGHT;

return true;
}

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 Deploy()
{
return self.DefaultDeploy( self.GetV_Model( SCIPG_MODEL_VIEW ), self.GetP_Model( SCIPG_MODEL_PLAYER ), SCIPG_DRAW, "gauss" );
}

void Holster( int skipLocal = 0 )
{
self.m_fInReload = false;

self.m_pPlayer.m_flNextAttack = WeaponTimeBase() + 0.5;
self.SendWeaponAnim( SCIPG_HOLSTER1 );
}

float WeaponTimeBase()
{
return g_Engine.time;
}

void PrimaryAttack()
{
FireBolt();
}

void FireBolt()
{
TraceResult tr;

if( self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0)
{
self.PlayEmptySound();
self.m_flNextPrimaryAttack = WeaponTimeBase() + 0.5;
return;
}

self.m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME;

self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 1 );

self.SendWeaponAnim( SCIPG_FIRE );
g_SoundSystem.EmitSound( self.m_pPlayer.edict(), CHAN_WEAPON, SCIPG_SOUND_FIRE1, 0.9, ATTN_NORM );
g_SoundSystem.EmitSound( self.m_pPlayer.edict(), CHAN_ITEM, SCIPG_SOUND_FIRE2, 0.7, ATTN_NORM );

self.m_pPlayer.SetAnimation( PLAYER_ATTACK1 );

Vector anglesAim = self.m_pPlayer.pev.v_angle + self.m_pPlayer.pev.punchangle;
Math.MakeVectors( anglesAim );

anglesAim.x = -anglesAim.x;
Vector vecSrc = self.m_pPlayer.GetGunPosition() - g_Engine.v_up * 2;
Vector vecDir = g_Engine.v_forward;

CSciPGBolt@ pBolt = BoltCreate();
pBolt.pev.origin = vecSrc;
pBolt.pev.angles = anglesAim;
@pBolt.pev.owner = self.m_pPlayer.edict();

if( self.m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD )
{
pBolt.pev.velocity = vecDir * BOLT_WATER_VELOCITY;
pBolt.pev.speed = BOLT_WATER_VELOCITY;
}
else
{
pBolt.pev.velocity = vecDir * BOLT_AIR_VELOCITY;
pBolt.pev.speed = BOLT_AIR_VELOCITY;
}
pBolt.pev.avelocity.z = 10;


if( self.m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
self.m_pPlayer.SetSuitUpdate( "!HEV_AMO0", false, 0 );

self.m_flNextPrimaryAttack = WeaponTimeBase() + 0.75;

self.m_flNextSecondaryAttack = WeaponTimeBase() + 0.75;

self.m_flTimeWeaponIdle = WeaponTimeBase() + 5.0;
}

void WeaponIdle()
{
self.m_pPlayer.GetAutoaimVector( AUTOAIM_2DEGREES ); // get the autoaim vector but ignore it; used for autoaim crosshair in DM

self.ResetEmptySound();

if( self.m_flTimeWeaponIdle < WeaponTimeBase() )
{
float flRand = g_PlayerFuncs.SharedRandomFloat( self.m_pPlayer.random_seed, 0, 1 );
if (flRand <= 0.75)
{
self.SendWeaponAnim( SCIPG_IDLE );
self.m_flTimeWeaponIdle = WeaponTimeBase() + g_PlayerFuncs.SharedRandomFloat( self.m_pPlayer.random_seed, 10, 15 );
}
else
{
self.SendWeaponAnim( SCIPG_FIDGET );
self.m_flTimeWeaponIdle = WeaponTimeBase() + 90.0 / 30.0;
}
}
}
}

class CSciPGAmmo : ScriptBasePlayerAmmoEntity
{
void Spawn()
{
Precache();
g_EntityFuncs.SetModel( self, SCIPG_MODEL_CLIP );
self.pev.sequence = 13;//20
self.pev.scale = 0.3;
BaseClass.Spawn();
}

void Precache()
{
g_Game.PrecacheModel( SCIPG_MODEL_CLIP );
g_SoundSystem.PrecacheSound( "items/9mmclip1.wav" );
}

bool AddAmmo( CBaseEntity@ pOther )
{
if( pOther.GiveAmmo( 30, "scientist", SCIPG_MAX_CARRY ) != -1)
{
g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM );
return true;
}
return false;
}
}

void RegisterSciPG()
{
g_CustomEntityFuncs.RegisterCustomEntity( "CSciPG", "weapon_scientist" );
g_ItemRegistry.RegisterWeapon( "weapon_scientist", "custom_weapons", "scientist" );
}

void RegisterSciPGAmmoBox()
{
g_CustomEntityFuncs.RegisterCustomEntity( "CSciPGAmmo", "ammo_scientist" );
}



proj_scibolt.as


class CSciPGBolt : ScriptBaseEntity
{
string m_iExplode = "sprites/custom_weapons/spinning_coin.spr";
string g_sModelIndexFireball = "sprites/zerogxplode.spr";
string g_sModelIndexWExplosion = "sprites/WXplo1.spr";

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

self.pev.gravity = 0.5;

g_EntityFuncs.SetModel( self, SCIPG_MODEL_BOLT );
g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, SCIPG_SOUND_FLY, 1, ATTN_NORM );

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

SetTouch( TouchFunction( this.BoltTouch ) );
SetThink( ThinkFunction( this.BubbleThink ) );
self.pev.nextthink = g_Engine.time + 0.2;
}

void Precache()
{

}

int Classify ()
{
return CLASS_NONE;
}

void BoltTouch( CBaseEntity@ pOther )
{
SetTouch( null );
SetThink( null );

if( pOther.pev.takedamage != DAMAGE_NO )
{
TraceResult tr = g_Utility.GetGlobalTrace();
entvars_t@ pevOwner = self.pev.owner.vars;

// UNDONE: this needs to call TraceAttack instead
g_WeaponFuncs.ClearMultiDamage();

pOther.TraceAttack( pevOwner, Math.RandomLong(300,999), self.pev.velocity.Normalize(), tr, DMG_BULLET | DMG_ALWAYSGIB );//DMG_TIMEBASED, DMG_POISON

g_WeaponFuncs.ApplyMultiDamage( self.pev, pevOwner );

self.pev.velocity = g_vecZero;

g_SoundSystem.StopSound( self.edict(), CHAN_VOICE, SCIPG_SOUND_FLY );
SetThink( ThinkFunction( this.ExplodeThink ) );
SprayTest( self.pev.origin, Vector(0,0,1), m_iExplode, 24 );
g_EntityFuncs.Remove( self );
}
else
{
SetThink( ThinkFunction( this.SUB_Remove ) );
SetThink( ThinkFunction( this.ExplodeThink ) );
self.pev.nextthink = g_Engine.time;

if( pOther.pev.ClassNameIs("worldspawn") )
{
g_SoundSystem.StopSound( self.edict(), CHAN_VOICE, SCIPG_SOUND_FLY );
SetThink( ThinkFunction( this.ExplodeThink ) );
SprayTest( self.pev.origin, Vector(0,0,1), m_iExplode, 24 );
SetThink( ThinkFunction( this.SUB_Remove ) );
}

if( g_EngineFuncs.PointContents( self.pev.origin ) != CONTENTS_WATER )
{
g_SoundSystem.StopSound( self.edict(), CHAN_VOICE, SCIPG_SOUND_FLY );
SetThink( ThinkFunction( this.ExplodeThink ) );
SprayTest( self.pev.origin, Vector(0,0,1), m_iExplode, 24 );
g_EntityFuncs.Remove( self );
}
}
}

void SprayTest( const Vector& in position, const Vector& in direction, string spriteModel, int count )
{
int iSpeed;
iSpeed = 130;

g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, SCIPG_SOUND_EXPLODE, 1, ATTN_NORM );

NetworkMessage coinexp( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
coinexp.WriteByte( TE_SPRITE_SPRAY );
coinexp.WriteCoord( position.x );
coinexp.WriteCoord( position.y );
coinexp.WriteCoord( position.z );
coinexp.WriteCoord( direction.x );
coinexp.WriteCoord( direction.y );
coinexp.WriteCoord( direction.z );
coinexp.WriteShort( g_EngineFuncs.ModelIndex(spriteModel) );
coinexp.WriteByte( count );
coinexp.WriteByte( iSpeed );//speed
coinexp.WriteByte( 80 );//noise ( client will divide by 100 )
coinexp.End();
}

void BubbleThink()
{
self.pev.nextthink = g_Engine.time + 0.1;

if( self.pev.waterlevel == WATERLEVEL_DRY )
return;

g_Utility.BubbleTrail( self.pev.origin - self.pev.velocity * 0.1, self.pev.origin, 20 );
}

void ExplodeThink()
{
int iContents = g_EngineFuncs.PointContents ( self.pev.origin );
int iScale;

self.pev.dmg = 40;
iScale = 10;

NetworkMessage exp1( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
exp1.WriteByte( TE_EXPLOSION );
exp1.WriteCoord( self.pev.origin.x );
exp1.WriteCoord( self.pev.origin.y );
exp1.WriteCoord( self.pev.origin.z );
if( iContents != CONTENTS_WATER )
{
exp1.WriteShort( g_EngineFuncs.ModelIndex(g_sModelIndexFireball) );
}
else
{
exp1.WriteShort( g_EngineFuncs.ModelIndex(g_sModelIndexWExplosion) );
}
exp1.WriteByte( iScale );
exp1.WriteByte( 15 ); //framerate
exp1.WriteByte( TE_EXPLFLAG_NONE );
exp1.End();

entvars_t@ pevOwner;

if( self.pev.owner !is null )
@pevOwner = self.pev.owner.vars;
else
@pevOwner = null;

@self.pev.owner = null; // can't traceline attack owner if this is set

g_WeaponFuncs.RadiusDamage( self.pev.origin, self.pev, pevOwner, self.pev.dmg, 128, CLASS_PLAYER, DMG_BLAST | DMG_ALWAYSGIB );

g_EntityFuncs.Remove( self );
}

void SUB_Remove()
{
self.SUB_Remove();
}
}

CSciPGBolt@ BoltCreate()
{
CBaseEntity@ cbeBolt = g_EntityFuncs.CreateEntity( "scibolt", null, false);
CSciPGBolt@ pBolt = cast<CSciPGBolt@>(CastToScriptClass(cbeBolt));
g_EntityFuncs.DispatchSpawn( pBolt.self.edict() );

return pBolt;
}

void RegisterSciPGBolt()
{
g_CustomEntityFuncs.RegisterCustomEntity( "CSciPGBolt", "scibolt" );
}