mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 00:01:53 +02:00
2166 lines
58 KiB
C++
2166 lines
58 KiB
C++
// Copyright (C) 2004 Id Software, Inc.
|
|
//
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "Game_local.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idProjectile
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
static const int BFG_DAMAGE_FREQUENCY = 333;
|
|
static const float BOUNCE_SOUND_MIN_VELOCITY = 200.0f;
|
|
static const float BOUNCE_SOUND_MAX_VELOCITY = 400.0f;
|
|
|
|
const idEventDef EV_Explode( "<explode>", NULL );
|
|
const idEventDef EV_Fizzle( "<fizzle>", NULL );
|
|
const idEventDef EV_RadiusDamage( "<radiusdmg>", "e" );
|
|
const idEventDef EV_GetProjectileState( "getProjectileState", NULL, 'd' );
|
|
|
|
CLASS_DECLARATION( idEntity, idProjectile )
|
|
EVENT( EV_Explode, idProjectile::Event_Explode )
|
|
EVENT( EV_Fizzle, idProjectile::Event_Fizzle )
|
|
EVENT( EV_Touch, idProjectile::Event_Touch )
|
|
EVENT( EV_RadiusDamage, idProjectile::Event_RadiusDamage )
|
|
EVENT( EV_GetProjectileState, idProjectile::Event_GetProjectileState )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idProjectile::idProjectile
|
|
================
|
|
*/
|
|
idProjectile::idProjectile( void ) {
|
|
owner = NULL;
|
|
lightDefHandle = -1;
|
|
thrust = 0.0f;
|
|
thrust_end = 0;
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
state = SPAWNED;
|
|
lightOffset = vec3_zero;
|
|
lightStartTime = 0;
|
|
lightEndTime = 0;
|
|
lightColor = vec3_zero;
|
|
state = SPAWNED;
|
|
damagePower = 1.0f;
|
|
memset( &projectileFlags, 0, sizeof( projectileFlags ) );
|
|
memset( &renderLight, 0, sizeof( renderLight ) );
|
|
|
|
// note: for net_instanthit projectiles, we will force this back to false at spawn time
|
|
fl.networkSync = true;
|
|
|
|
netSyncPhysics = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Spawn
|
|
================
|
|
*/
|
|
void idProjectile::Spawn( void ) {
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.SetClipMask( 0 );
|
|
physicsObj.PutToRest();
|
|
SetPhysics( &physicsObj );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Save
|
|
================
|
|
*/
|
|
void idProjectile::Save( idSaveGame *savefile ) const {
|
|
|
|
owner.Save( savefile );
|
|
|
|
projectileFlags_s flags = projectileFlags;
|
|
LittleBitField( &flags, sizeof( flags ) );
|
|
savefile->Write( &flags, sizeof( flags ) );
|
|
|
|
savefile->WriteFloat( thrust );
|
|
savefile->WriteInt( thrust_end );
|
|
|
|
savefile->WriteRenderLight( renderLight );
|
|
//HUMANHEAD PCF mdl 05/04/06 - Don't save light handles
|
|
//savefile->WriteInt( (int)lightDefHandle );
|
|
savefile->WriteVec3( lightOffset );
|
|
savefile->WriteInt( lightStartTime );
|
|
savefile->WriteInt( lightEndTime );
|
|
savefile->WriteVec3( lightColor );
|
|
|
|
savefile->WriteParticle( smokeFly );
|
|
savefile->WriteInt( smokeFlyTime );
|
|
|
|
savefile->WriteInt( (int)state );
|
|
|
|
savefile->WriteFloat( damagePower );
|
|
|
|
savefile->WriteStaticObject( physicsObj );
|
|
//HUMANHEAD rww
|
|
//savefile->WriteStaticObject( thruster );
|
|
|
|
//HUMANHEAD PCF mdl 05/04/06 - Save whether the light handle is active
|
|
savefile->WriteBool( lightDefHandle != -1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Restore
|
|
================
|
|
*/
|
|
void idProjectile::Restore( idRestoreGame *savefile ) {
|
|
|
|
owner.Restore( savefile );
|
|
|
|
savefile->Read( &projectileFlags, sizeof( projectileFlags ) );
|
|
LittleBitField( &projectileFlags, sizeof( projectileFlags ) );
|
|
|
|
savefile->ReadFloat( thrust );
|
|
savefile->ReadInt( thrust_end );
|
|
|
|
savefile->ReadRenderLight( renderLight );
|
|
//HUMANHEAD PCF mdl 05/04/06 - Don't save light handles
|
|
//savefile->ReadInt( (int &)lightDefHandle );
|
|
savefile->ReadVec3( lightOffset );
|
|
savefile->ReadInt( lightStartTime );
|
|
savefile->ReadInt( lightEndTime );
|
|
savefile->ReadVec3( lightColor );
|
|
|
|
savefile->ReadParticle( smokeFly );
|
|
savefile->ReadInt( smokeFlyTime );
|
|
|
|
savefile->ReadInt( (int &)state );
|
|
|
|
savefile->ReadFloat( damagePower );
|
|
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
|
|
//HUMANHEAD rww
|
|
//savefile->ReadStaticObject( thruster );
|
|
//thruster.SetPhysics( &physicsObj );
|
|
|
|
//HUMANHEAD PCF mdl 05/04/06 - Save whether the light handle is active
|
|
bool bLight;
|
|
savefile->ReadBool( bLight );
|
|
if ( bLight ) {
|
|
lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
|
|
}
|
|
|
|
if ( smokeFly != NULL ) {
|
|
idVec3 dir;
|
|
dir = physicsObj.GetLinearVelocity();
|
|
dir.NormalizeFast();
|
|
gameLocal.smokeParticles->EmitSmoke( smokeFly, gameLocal.time, gameLocal.random.RandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::GetOwner
|
|
================
|
|
*/
|
|
idEntity *idProjectile::GetOwner( void ) const {
|
|
return owner.GetEntity();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Create
|
|
|
|
HUMANHEAD: AOBMERGE - Overridden
|
|
================
|
|
*/
|
|
void idProjectile::Create( idEntity *owner, const idVec3 &start, const idVec3 &dir ) {
|
|
/* HUMANHEAD pdm: Overridden
|
|
idDict args;
|
|
idStr shaderName;
|
|
idVec3 light_color;
|
|
idVec3 light_offset;
|
|
idVec3 tmp;
|
|
idMat3 axis;
|
|
|
|
Unbind();
|
|
|
|
// align z-axis of model with the direction
|
|
axis = dir.ToMat3();
|
|
tmp = axis[2];
|
|
axis[2] = axis[0];
|
|
axis[0] = -tmp;
|
|
|
|
physicsObj.SetOrigin( start );
|
|
physicsObj.SetAxis( axis );
|
|
|
|
physicsObj.GetClipModel()->SetOwner( owner );
|
|
|
|
this->owner = owner;
|
|
|
|
memset( &renderLight, 0, sizeof( renderLight ) );
|
|
shaderName = spawnArgs.GetString( "mtr_light_shader" );
|
|
if ( *(const char *)shaderName ) {
|
|
renderLight.shader = declManager->FindMaterial( shaderName, false );
|
|
renderLight.pointLight = true;
|
|
renderLight.lightRadius[0] =
|
|
renderLight.lightRadius[1] =
|
|
renderLight.lightRadius[2] = spawnArgs.GetFloat( "light_radius" );
|
|
spawnArgs.GetVector( "light_color", "1 1 1", light_color );
|
|
renderLight.shaderParms[0] = light_color[0];
|
|
renderLight.shaderParms[1] = light_color[1];
|
|
renderLight.shaderParms[2] = light_color[2];
|
|
renderLight.shaderParms[3] = 1.0f;
|
|
}
|
|
|
|
spawnArgs.GetVector( "light_offset", "0 0 0", lightOffset );
|
|
|
|
lightStartTime = 0;
|
|
lightEndTime = 0;
|
|
smokeFlyTime = 0;
|
|
|
|
damagePower = 1.0f;
|
|
|
|
UpdateVisuals();
|
|
|
|
state = CREATED;
|
|
|
|
if ( spawnArgs.GetBool( "net_fullphysics" ) ) {
|
|
netSyncPhysics = true;
|
|
}
|
|
HUMANHEAD END */
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::~idProjectile
|
|
=================
|
|
*/
|
|
idProjectile::~idProjectile() {
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
FreeLightDef();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::FreeLightDef
|
|
=================
|
|
*/
|
|
void idProjectile::FreeLightDef( void ) {
|
|
if ( lightDefHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( lightDefHandle );
|
|
lightDefHandle = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::Launch
|
|
|
|
HUMANHEAD: AOBMERGE - Overridden
|
|
=================
|
|
*/
|
|
void idProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, const float dmgPower ) {
|
|
/* HUMANHEAD pdm: overridden
|
|
float fuse;
|
|
float startthrust;
|
|
float endthrust;
|
|
idVec3 velocity;
|
|
idAngles angular_velocity;
|
|
float linear_friction;
|
|
float angular_friction;
|
|
float contact_friction;
|
|
float bounce;
|
|
float mass;
|
|
float speed;
|
|
float gravity;
|
|
idVec3 gravVec;
|
|
idVec3 tmp;
|
|
idMat3 axis;
|
|
int thrust_start;
|
|
int contents;
|
|
int clipMask;
|
|
|
|
// allow characters to throw projectiles during cinematics, but not the player
|
|
if ( owner.GetEntity() && !owner.GetEntity()->IsType( idPlayer::Type ) ) {
|
|
cinematic = owner.GetEntity()->cinematic;
|
|
} else {
|
|
cinematic = false;
|
|
}
|
|
|
|
thrust = spawnArgs.GetFloat( "thrust" );
|
|
startthrust = spawnArgs.GetFloat( "thrust_start" );
|
|
endthrust = spawnArgs.GetFloat( "thrust_end" );
|
|
|
|
spawnArgs.GetVector( "velocity", "0 0 0", velocity );
|
|
|
|
speed = velocity.Length() * launchPower;
|
|
|
|
damagePower = dmgPower;
|
|
|
|
spawnArgs.GetAngles( "angular_velocity", "0 0 0", angular_velocity );
|
|
|
|
linear_friction = spawnArgs.GetFloat( "linear_friction" );
|
|
angular_friction = spawnArgs.GetFloat( "angular_friction" );
|
|
contact_friction = spawnArgs.GetFloat( "contact_friction" );
|
|
bounce = spawnArgs.GetFloat( "bounce" );
|
|
mass = spawnArgs.GetFloat( "mass" );
|
|
gravity = spawnArgs.GetFloat( "gravity" );
|
|
fuse = spawnArgs.GetFloat( "fuse" );
|
|
|
|
projectileFlags.detonate_on_world = spawnArgs.GetBool( "detonate_on_world" );
|
|
projectileFlags.detonate_on_actor = spawnArgs.GetBool( "detonate_on_actor" );
|
|
projectileFlags.randomShaderSpin = spawnArgs.GetBool( "random_shader_spin" );
|
|
|
|
if ( mass <= 0 ) {
|
|
gameLocal.Error( "Invalid mass on '%s'\n", GetEntityDefName() );
|
|
}
|
|
|
|
thrust *= mass;
|
|
thrust_start = SEC2MS( startthrust ) + gameLocal.time;
|
|
thrust_end = SEC2MS( endthrust ) + gameLocal.time;
|
|
|
|
lightStartTime = 0;
|
|
lightEndTime = 0;
|
|
|
|
if ( health ) {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
gravVec = gameLocal.GetGravity();
|
|
gravVec.NormalizeFast();
|
|
|
|
Unbind();
|
|
|
|
// align z-axis of model with the direction
|
|
axis = dir.ToMat3();
|
|
tmp = axis[2];
|
|
axis[2] = axis[0];
|
|
axis[0] = -tmp;
|
|
|
|
contents = 0;
|
|
clipMask = MASK_SHOT_RENDERMODEL;
|
|
if ( spawnArgs.GetBool( "detonate_on_trigger" ) ) {
|
|
contents |= CONTENTS_TRIGGER;
|
|
}
|
|
if ( !spawnArgs.GetBool( "no_contents" ) ) {
|
|
contents |= CONTENTS_PROJECTILE;
|
|
clipMask |= CONTENTS_PROJECTILE;
|
|
}
|
|
|
|
// don't do tracers on client, we don't know origin and direction
|
|
if ( spawnArgs.GetBool( "tracers" ) && gameLocal.random.RandomFloat() > 0.5f ) {
|
|
SetModel( spawnArgs.GetString( "model_tracer" ) );
|
|
projectileFlags.isTracer = true;
|
|
}
|
|
|
|
physicsObj.SetMass( mass );
|
|
physicsObj.SetFriction( linear_friction, angular_friction, contact_friction );
|
|
if ( contact_friction == 0.0f ) {
|
|
physicsObj.NoContact();
|
|
}
|
|
physicsObj.SetBouncyness( bounce );
|
|
physicsObj.SetGravity( gravVec * gravity );
|
|
physicsObj.SetContents( contents );
|
|
physicsObj.SetClipMask( clipMask );
|
|
physicsObj.SetLinearVelocity( axis[ 2 ] * speed + pushVelocity );
|
|
physicsObj.SetAngularVelocity( angular_velocity.ToAngularVelocity() * axis );
|
|
physicsObj.SetOrigin( start );
|
|
physicsObj.SetAxis( axis );
|
|
|
|
thruster.SetPosition( &physicsObj, 0, idVec3( GetPhysics()->GetBounds()[ 0 ].x, 0, 0 ) );
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
if ( fuse <= 0 ) {
|
|
// run physics for 1 second
|
|
RunPhysics();
|
|
PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
|
|
} else if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
|
|
fuse -= timeSinceFire;
|
|
if ( fuse < 0.0f ) {
|
|
fuse = 0.0f;
|
|
}
|
|
PostEventSec( &EV_Explode, fuse );
|
|
} else {
|
|
fuse -= timeSinceFire;
|
|
if ( fuse < 0.0f ) {
|
|
fuse = 0.0f;
|
|
}
|
|
PostEventSec( &EV_Fizzle, fuse );
|
|
}
|
|
}
|
|
|
|
if ( projectileFlags.isTracer ) {
|
|
StartSound( "snd_tracer", SND_CHANNEL_BODY, 0, false, NULL );
|
|
} else {
|
|
StartSound( "snd_fly", SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
|
|
smokeFlyTime = 0;
|
|
const char *smokeName = spawnArgs.GetString( "smoke_fly" );
|
|
if ( *smokeName != '\0' ) {
|
|
smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
smokeFlyTime = gameLocal.time;
|
|
}
|
|
|
|
// used for the plasma bolts but may have other uses as well
|
|
if ( projectileFlags.randomShaderSpin ) {
|
|
float f = gameLocal.random.RandomFloat();
|
|
f *= 0.5f;
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = f;
|
|
}
|
|
|
|
UpdateVisuals();
|
|
|
|
state = LAUNCHED;
|
|
HUMANHEAD END */
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Think
|
|
================
|
|
*/
|
|
void idProjectile::Think( void ) {
|
|
/* HUMANHEAD pdm: overridden
|
|
if ( thinkFlags & TH_THINK ) {
|
|
if ( thrust && ( gameLocal.time < thrust_end ) ) {
|
|
// evaluate force
|
|
thruster.SetForce( GetPhysics()->GetAxis()[ 0 ] * thrust );
|
|
thruster.Evaluate( gameLocal.time );
|
|
}
|
|
}
|
|
|
|
// run physics
|
|
RunPhysics();
|
|
|
|
Present();
|
|
|
|
// add the particles
|
|
if ( smokeFly != NULL && smokeFlyTime && !IsHidden() ) {
|
|
idVec3 dir = -GetPhysics()->GetLinearVelocity();
|
|
dir.Normalize();
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.RandomFloat(), GetPhysics()->GetOrigin(), dir.ToMat3() ) ) {
|
|
smokeFlyTime = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
// add the light
|
|
if ( renderLight.lightRadius.x > 0.0f && g_projectileLights.GetBool() ) {
|
|
renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * lightOffset;
|
|
renderLight.axis = GetPhysics()->GetAxis();
|
|
if ( ( lightDefHandle != -1 ) ) {
|
|
if ( lightEndTime > 0 && gameLocal.time <= lightEndTime + gameLocal.GetMSec() ) {
|
|
idVec3 color( 0, 0, 0 );
|
|
if ( gameLocal.time < lightEndTime ) {
|
|
float frac = ( float )( gameLocal.time - lightStartTime ) / ( float )( lightEndTime - lightStartTime );
|
|
color.Lerp( lightColor, color, frac );
|
|
}
|
|
renderLight.shaderParms[SHADERPARM_RED] = color.x;
|
|
renderLight.shaderParms[SHADERPARM_GREEN] = color.y;
|
|
renderLight.shaderParms[SHADERPARM_BLUE] = color.z;
|
|
}
|
|
gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
|
|
} else {
|
|
lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::Collide
|
|
=================
|
|
*/
|
|
bool idProjectile::Collide( const trace_t &collision, const idVec3 &velocity ) {
|
|
/* HUMANHEAD pdm: overridden
|
|
idEntity *ent;
|
|
idEntity *ignore;
|
|
const char *damageDefName;
|
|
idVec3 dir;
|
|
float push;
|
|
float damageScale;
|
|
|
|
if ( state == EXPLODED || state == FIZZLED || state == COLLIDED ) { //HUMANHEAD bjk
|
|
return true;
|
|
}
|
|
|
|
// predict the explosion
|
|
if ( gameLocal.isClient ) {
|
|
if ( ClientPredictionCollide( this, spawnArgs, collision, velocity, !spawnArgs.GetBool( "net_instanthit" ) ) ) {
|
|
Explode( collision, NULL );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// remove projectile when a 'noimpact' surface is hit
|
|
if ( ( collision.c.material != NULL ) && ( collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) ) {
|
|
PostEventMS( &EV_Remove, 0 );
|
|
common->DPrintf( "Projectile collision no impact\n" );
|
|
return true;
|
|
}
|
|
|
|
// get the entity the projectile collided with
|
|
ent = gameLocal.entities[ collision.c.entityNum ];
|
|
if ( ent == owner.GetEntity() ) {
|
|
assert( 0 );
|
|
return true;
|
|
}
|
|
|
|
// just get rid of the projectile when it hits a player in noclip
|
|
if ( ent->IsType( idPlayer::Type ) && static_cast<idPlayer *>( ent )->noclip ) {
|
|
PostEventMS( &EV_Remove, 0 );
|
|
return true;
|
|
}
|
|
|
|
// direction of projectile
|
|
dir = velocity;
|
|
dir.Normalize();
|
|
|
|
// projectiles can apply an additional impulse next to the rigid body physics impulse
|
|
if ( spawnArgs.GetFloat( "push", "0", push ) && push > 0.0f ) {
|
|
ent->ApplyImpulse( this, collision.c.id, collision.c.point, push * dir );
|
|
}
|
|
|
|
// MP: projectiles open doors
|
|
if ( gameLocal.isMultiplayer && ent->IsType( idDoor::Type ) && !static_cast< idDoor * >(ent)->IsOpen() && !ent->spawnArgs.GetBool( "no_touch" ) ) {
|
|
ent->ProcessEvent( &EV_Activate , this );
|
|
}
|
|
|
|
if ( ent->IsType( idActor::Type ) || ( ent->IsType( idAFAttachment::Type ) && static_cast<const idAFAttachment*>(ent)->GetBody()->IsType( idActor::Type ) ) ) {
|
|
if ( !projectileFlags.detonate_on_actor ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ( !projectileFlags.detonate_on_world ) {
|
|
if ( !StartSound( "snd_ricochet", SND_CHANNEL_ITEM, 0, true, NULL ) ) {
|
|
float len = velocity.Length();
|
|
if ( len > BOUNCE_SOUND_MIN_VELOCITY ) {
|
|
SetSoundVolume( len > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( len - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) ) );
|
|
StartSound( "snd_bounce", SND_CHANNEL_ANY, 0, true, NULL );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SetOrigin( collision.endpos );
|
|
SetAxis( collision.endAxis );
|
|
|
|
// unlink the clip model because we no longer need it
|
|
GetPhysics()->UnlinkClip();
|
|
|
|
damageDefName = spawnArgs.GetString( "def_damage" );
|
|
|
|
ignore = NULL;
|
|
|
|
// if the hit entity takes damage
|
|
if ( ent->fl.takedamage ) {
|
|
if ( damagePower ) {
|
|
damageScale = damagePower;
|
|
} else {
|
|
damageScale = 1.0f;
|
|
}
|
|
|
|
// if the projectile owner is a player
|
|
if ( owner.GetEntity() && owner.GetEntity()->IsType( idPlayer::Type ) ) {
|
|
// if the projectile hit an actor
|
|
if ( ent->IsType( idActor::Type ) ) {
|
|
idPlayer *player = static_cast<idPlayer *>( owner.GetEntity() );
|
|
player->AddProjectileHits( 1 );
|
|
damageScale *= player->PowerUpModifier( PROJECTILE_DAMAGE );
|
|
}
|
|
}
|
|
|
|
if ( damageDefName[0] != '\0' ) {
|
|
ent->Damage( this, owner.GetEntity(), dir, damageDefName, damageScale, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
|
|
ignore = ent;
|
|
}
|
|
}
|
|
|
|
// if the projectile causes a damage effect
|
|
if ( spawnArgs.GetBool( "impact_damage_effect" ) ) {
|
|
// if the hit entity has a special damage effect
|
|
if ( ent->spawnArgs.GetBool( "bleed" ) ) {
|
|
ent->AddDamageEffect( collision, velocity, damageDefName );
|
|
} else {
|
|
AddDefaultDamageEffect( collision, velocity );
|
|
}
|
|
}
|
|
|
|
Explode( collision, ignore );
|
|
|
|
return true;
|
|
*/
|
|
return false; // HUMANHEAD pdm: overridden
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::DefaultDamageEffect
|
|
=================
|
|
*/
|
|
void idProjectile::DefaultDamageEffect( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity ) {
|
|
/* HUMANHEAD pdm: not used
|
|
const char *decal, *sound, *typeName;
|
|
surfTypes_t materialType;
|
|
|
|
if ( collision.c.material != NULL ) {
|
|
materialType = collision.c.material->GetSurfaceType();
|
|
} else {
|
|
materialType = SURFTYPE_METAL;
|
|
}
|
|
|
|
// get material type name
|
|
typeName = gameLocal.sufaceTypeNames[ materialType ];
|
|
|
|
// play impact sound
|
|
sound = projectileDef.GetString( va( "snd_%s", typeName ) );
|
|
if ( *sound == '\0' ) {
|
|
sound = projectileDef.GetString( "snd_metal" );
|
|
}
|
|
if ( *sound == '\0' ) {
|
|
sound = projectileDef.GetString( "snd_impact" );
|
|
}
|
|
if ( *sound != '\0' ) {
|
|
soundEnt->StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
|
|
// project decal
|
|
decal = projectileDef.GetString( va( "mtr_detonate_%s", typeName ) );
|
|
if ( *decal == '\0' ) {
|
|
decal = projectileDef.GetString( "mtr_detonate" );
|
|
}
|
|
if ( *decal != '\0' ) {
|
|
gameLocal.ProjectDecal( collision.c.point, -collision.c.normal, 8.0f, true, projectileDef.GetFloat( "decal_size", "6.0" ), decal );
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::AddDefaultDamageEffect
|
|
=================
|
|
*/
|
|
void idProjectile::AddDefaultDamageEffect( const trace_t &collision, const idVec3 &velocity ) {
|
|
/* HUMANHEAD pdm: Overridden
|
|
|
|
DefaultDamageEffect( this, spawnArgs, collision, velocity );
|
|
|
|
if ( gameLocal.isServer && fl.networkSync ) {
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
int excludeClient;
|
|
|
|
if ( spawnArgs.GetBool( "net_instanthit" ) ) {
|
|
excludeClient = owner.GetEntityNum();
|
|
} else {
|
|
excludeClient = -1;
|
|
}
|
|
|
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
|
msg.BeginWriting();
|
|
msg.WriteFloat( collision.c.point[0] );
|
|
msg.WriteFloat( collision.c.point[1] );
|
|
msg.WriteFloat( collision.c.point[2] );
|
|
msg.WriteDir( collision.c.normal, 24 );
|
|
msg.WriteLong( ( collision.c.material != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_MATERIAL, collision.c.material->Index() ) : -1 );
|
|
msg.WriteFloat( velocity[0], 5, 10 );
|
|
msg.WriteFloat( velocity[1], 5, 10 );
|
|
msg.WriteFloat( velocity[2], 5, 10 );
|
|
ServerSendEvent( EVENT_DAMAGE_EFFECT, &msg, false, excludeClient );
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Killed
|
|
================
|
|
*/
|
|
void idProjectile::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
if ( spawnArgs.GetBool( "detonate_on_death" ) ) {
|
|
trace_t collision;
|
|
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.endAxis = GetPhysics()->GetAxis();
|
|
collision.endpos = GetPhysics()->GetOrigin();
|
|
collision.c.point = GetPhysics()->GetOrigin();
|
|
collision.c.normal.Set( 0, 0, 1 );
|
|
Explode( collision, NULL );
|
|
physicsObj.ClearContacts();
|
|
physicsObj.PutToRest();
|
|
} else {
|
|
Fizzle();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Fizzle
|
|
================
|
|
*/
|
|
void idProjectile::Fizzle( void ) {
|
|
|
|
if ( state == EXPLODED || state == FIZZLED || state == COLLIDED ) { //HUMANHEAD bjk
|
|
return;
|
|
}
|
|
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
StartSound( "snd_fizzle", SND_CHANNEL_BODY, 0, false, NULL );
|
|
|
|
// fizzle FX
|
|
const char *psystem = spawnArgs.GetString( "smoke_fuse" );
|
|
if ( psystem && *psystem ) {
|
|
//FIXME:SMOKE gameLocal.particles->SpawnParticles( GetPhysics()->GetOrigin(), vec3_origin, psystem );
|
|
}
|
|
|
|
// we need to work out how long the effects last and then remove them at that time
|
|
// for example, bullets have no real effects
|
|
if ( smokeFly && smokeFlyTime ) {
|
|
smokeFlyTime = 0;
|
|
}
|
|
|
|
fl.takedamage = false;
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.GetClipModel()->Unlink();
|
|
physicsObj.PutToRest();
|
|
|
|
Hide();
|
|
FreeLightDef();
|
|
|
|
state = FIZZLED;
|
|
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
CancelEvents( &EV_Fizzle );
|
|
PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Event_RadiusDamage
|
|
================
|
|
*/
|
|
void idProjectile::Event_RadiusDamage( idEntity *ignore ) {
|
|
const char *splash_damage = spawnArgs.GetString( "def_splash_damage" );
|
|
if ( splash_damage[0] != '\0' ) {
|
|
gameLocal.RadiusDamage( physicsObj.GetOrigin(), this, owner.GetEntity(), ignore, this, splash_damage, damagePower );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Event_RadiusDamage
|
|
================
|
|
*/
|
|
void idProjectile::Event_GetProjectileState( void ) {
|
|
idThread::ReturnInt( state );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Explode
|
|
================
|
|
*/
|
|
void idProjectile::Explode( const trace_t &collision, idEntity *ignore ) {
|
|
/* HUMANHEAD pdm: overridden
|
|
const char *fxname, *light_shader, *sndExplode;
|
|
float light_fadetime;
|
|
idVec3 normal;
|
|
int removeTime;
|
|
|
|
if ( state == EXPLODED || state == FIZZLED || state == COLLIDED ) { //HUMANHEAD bjk
|
|
return;
|
|
}
|
|
|
|
// stop sound
|
|
StopSound( SND_CHANNEL_BODY2, false );
|
|
|
|
// play explode sound
|
|
switch ( ( int ) damagePower ) {
|
|
case 2: sndExplode = "snd_explode2"; break;
|
|
case 3: sndExplode = "snd_explode3"; break;
|
|
case 4: sndExplode = "snd_explode4"; break;
|
|
default: sndExplode = "snd_explode"; break;
|
|
}
|
|
StartSound( sndExplode, SND_CHANNEL_BODY, 0, true, NULL );
|
|
|
|
// we need to work out how long the effects last and then remove them at that time
|
|
// for example, bullets have no real effects
|
|
if ( smokeFly && smokeFlyTime ) {
|
|
smokeFlyTime = 0;
|
|
}
|
|
|
|
Hide();
|
|
FreeLightDef();
|
|
|
|
if ( spawnArgs.GetVector( "detonation_axis", "", normal ) ) {
|
|
GetPhysics()->SetAxis( normal.ToMat3() );
|
|
}
|
|
GetPhysics()->SetOrigin( collision.endpos + 2.0f * collision.c.normal );
|
|
|
|
// default remove time
|
|
removeTime = spawnArgs.GetInt( "remove_time", "1500" );
|
|
|
|
// change the model, usually to a PRT
|
|
fxname = NULL;
|
|
if ( g_testParticle.GetInteger() == TEST_PARTICLE_IMPACT ) {
|
|
fxname = g_testParticleName.GetString();
|
|
} else {
|
|
fxname = spawnArgs.GetString( "model_detonate" );
|
|
}
|
|
|
|
int surfaceType = collision.c.material != NULL ? collision.c.material->GetSurfaceType() : SURFTYPE_METAL;
|
|
if ( !( fxname && *fxname ) ) {
|
|
if ( ( surfaceType == SURFTYPE_NONE ) || ( surfaceType == SURFTYPE_METAL ) || ( surfaceType == SURFTYPE_STONE ) ) {
|
|
fxname = spawnArgs.GetString( "model_smokespark" );
|
|
} else if ( surfaceType == SURFTYPE_RICOCHET ) {
|
|
fxname = spawnArgs.GetString( "model_ricochet" );
|
|
} else {
|
|
fxname = spawnArgs.GetString( "model_smoke" );
|
|
}
|
|
}
|
|
|
|
if ( fxname && *fxname ) {
|
|
SetModel( fxname );
|
|
renderEntity.shaderParms[SHADERPARM_RED] =
|
|
renderEntity.shaderParms[SHADERPARM_GREEN] =
|
|
renderEntity.shaderParms[SHADERPARM_BLUE] =
|
|
renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
|
|
renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
|
|
Show();
|
|
removeTime = ( removeTime > 3000 ) ? removeTime : 3000;
|
|
}
|
|
|
|
// explosion light
|
|
light_shader = spawnArgs.GetString( "mtr_explode_light_shader" );
|
|
if ( *light_shader ) {
|
|
renderLight.shader = declManager->FindMaterial( light_shader, false );
|
|
renderLight.pointLight = true;
|
|
renderLight.lightRadius[0] =
|
|
renderLight.lightRadius[1] =
|
|
renderLight.lightRadius[2] = spawnArgs.GetFloat( "explode_light_radius" );
|
|
spawnArgs.GetVector( "explode_light_color", "1 1 1", lightColor );
|
|
renderLight.shaderParms[SHADERPARM_RED] = lightColor.x;
|
|
renderLight.shaderParms[SHADERPARM_GREEN] = lightColor.y;
|
|
renderLight.shaderParms[SHADERPARM_BLUE] = lightColor.z;
|
|
renderLight.shaderParms[SHADERPARM_ALPHA] = 1.0f;
|
|
renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
|
|
light_fadetime = spawnArgs.GetFloat( "explode_light_fadetime", "0.5" );
|
|
lightStartTime = gameLocal.time;
|
|
lightEndTime = gameLocal.time + SEC2MS( light_fadetime );
|
|
BecomeActive( TH_THINK );
|
|
}
|
|
|
|
fl.takedamage = false;
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.PutToRest();
|
|
|
|
state = EXPLODED;
|
|
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
// alert the ai
|
|
gameLocal.AlertAI( owner.GetEntity() );
|
|
|
|
// bind the projectile to the impact entity if necesary
|
|
if ( gameLocal.entities[collision.c.entityNum] && spawnArgs.GetBool( "bindOnImpact" ) ) {
|
|
Bind( gameLocal.entities[collision.c.entityNum], true );
|
|
}
|
|
|
|
// splash damage
|
|
if ( !projectileFlags.noSplashDamage ) {
|
|
float delay = spawnArgs.GetFloat( "delay_splash" );
|
|
if ( delay ) {
|
|
if ( removeTime < delay * 1000 ) {
|
|
removeTime = ( delay + 0.10 ) * 1000;
|
|
}
|
|
PostEventSec( &EV_RadiusDamage, delay, ignore );
|
|
} else {
|
|
Event_RadiusDamage( ignore );
|
|
}
|
|
}
|
|
|
|
// spawn debris entities
|
|
int fxdebris = spawnArgs.GetInt( "debris_count" );
|
|
if ( fxdebris ) {
|
|
const idDict *debris = gameLocal.FindEntityDefDict( "projectile_debris", false );
|
|
if ( debris ) {
|
|
int amount = gameLocal.random.RandomInt( fxdebris );
|
|
for ( int i = 0; i < amount; i++ ) {
|
|
idEntity *ent;
|
|
idVec3 dir;
|
|
dir.x = gameLocal.random.CRandomFloat() * 4.0f;
|
|
dir.y = gameLocal.random.CRandomFloat() * 4.0f;
|
|
dir.z = gameLocal.random.RandomFloat() * 8.0f;
|
|
dir.Normalize();
|
|
|
|
gameLocal.SpawnEntityDef( *debris, &ent, false );
|
|
if ( !ent || !ent->IsType( idDebris::Type ) ) {
|
|
gameLocal.Error( "idProjectile: 'projectile_debris' is not an idDebris" );
|
|
}
|
|
|
|
idDebris *debris = static_cast<idDebris *>(ent);
|
|
debris->Create( owner.GetEntity(), physicsObj.GetOrigin(), dir.ToMat3() );
|
|
debris->Launch();
|
|
}
|
|
}
|
|
debris = gameLocal.FindEntityDefDict( "projectile_shrapnel", false );
|
|
if ( debris ) {
|
|
int amount = gameLocal.random.RandomInt( fxdebris );
|
|
for ( int i = 0; i < amount; i++ ) {
|
|
idEntity *ent;
|
|
idVec3 dir;
|
|
dir.x = gameLocal.random.CRandomFloat() * 8.0f;
|
|
dir.y = gameLocal.random.CRandomFloat() * 8.0f;
|
|
dir.z = gameLocal.random.RandomFloat() * 8.0f + 8.0f;
|
|
dir.Normalize();
|
|
|
|
gameLocal.SpawnEntityDef( *debris, &ent, false );
|
|
if ( !ent || !ent->IsType( idDebris::Type ) ) {
|
|
gameLocal.Error( "'projectile_shrapnel' is not an idDebris" );
|
|
}
|
|
|
|
idDebris *debris = static_cast<idDebris *>(ent);
|
|
debris->Create( owner.GetEntity(), physicsObj.GetOrigin(), dir.ToMat3() );
|
|
debris->Launch();
|
|
}
|
|
}
|
|
}
|
|
|
|
CancelEvents( &EV_Explode );
|
|
PostEventMS( &EV_Remove, removeTime );
|
|
*/
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::GetVelocity
|
|
================
|
|
*/
|
|
idVec3 idProjectile::GetVelocity( const idDict *projectile ) {
|
|
idVec3 velocity;
|
|
|
|
projectile->GetVector( "velocity", "0 0 0", velocity );
|
|
return velocity;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::GetGravity
|
|
================
|
|
*/
|
|
idVec3 idProjectile::GetGravity( const idDict *projectile ) {
|
|
float gravity;
|
|
|
|
gravity = projectile->GetFloat( "gravity" );
|
|
return idVec3( 0, 0, -gravity );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Event_Explode
|
|
================
|
|
*/
|
|
void idProjectile::Event_Explode( void ) {
|
|
/* HUMANHEAD pdm: overridden
|
|
trace_t collision;
|
|
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.endAxis = GetPhysics()->GetAxis();
|
|
collision.endpos = GetPhysics()->GetOrigin();
|
|
collision.c.point = GetPhysics()->GetOrigin();
|
|
collision.c.normal.Set( 0, 0, 1 );
|
|
AddDefaultDamageEffect( collision, collision.c.normal );
|
|
Explode( collision, NULL );
|
|
*/
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Event_Fizzle
|
|
================
|
|
*/
|
|
void idProjectile::Event_Fizzle( void ) {
|
|
Fizzle();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::Event_Touch
|
|
================
|
|
*/
|
|
void idProjectile::Event_Touch( idEntity *other, trace_t *trace ) {
|
|
/* HUMANHEAD pdm: Overridden
|
|
if ( IsHidden() ) {
|
|
return;
|
|
}
|
|
|
|
if ( other != owner.GetEntity() ) {
|
|
trace_t collision;
|
|
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.endAxis = GetPhysics()->GetAxis();
|
|
collision.endpos = GetPhysics()->GetOrigin();
|
|
collision.c.point = GetPhysics()->GetOrigin();
|
|
collision.c.normal.Set( 0, 0, 1 );
|
|
AddDefaultDamageEffect( collision, collision.c.normal );
|
|
Explode( collision, NULL );
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idProjectile::ClientPredictionCollide
|
|
=================
|
|
*/
|
|
bool idProjectile::ClientPredictionCollide( idEntity *soundEnt, const idDict &projectileDef, const trace_t &collision, const idVec3 &velocity, bool addDamageEffect ) {
|
|
/* HUMANHEAD pdm: not used
|
|
idEntity *ent;
|
|
|
|
// remove projectile when a 'noimpact' surface is hit
|
|
if ( collision.c.material && ( collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) ) {
|
|
return false;
|
|
}
|
|
|
|
// get the entity the projectile collided with
|
|
ent = gameLocal.entities[ collision.c.entityNum ];
|
|
if ( ent == NULL ) {
|
|
return false;
|
|
}
|
|
|
|
// don't do anything if hitting a noclip player
|
|
if ( ent->IsType( idPlayer::Type ) && static_cast<idPlayer *>( ent )->noclip ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ent->IsType( idActor::Type ) || ( ent->IsType( idAFAttachment::Type ) && static_cast<const idAFAttachment*>(ent)->GetBody()->IsType( idActor::Type ) ) ) {
|
|
if ( !projectileDef.GetBool( "detonate_on_actor" ) ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ( !projectileDef.GetBool( "detonate_on_world" ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// if the projectile causes a damage effect
|
|
if ( addDamageEffect && projectileDef.GetBool( "impact_damage_effect" ) ) {
|
|
// if the hit entity does not have a special damage effect
|
|
if ( !ent->spawnArgs.GetBool( "bleed" ) ) {
|
|
// predict damage effect
|
|
DefaultDamageEffect( soundEnt, projectileDef, collision, velocity );
|
|
}
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::ClientPredictionThink
|
|
================
|
|
*/
|
|
void idProjectile::ClientPredictionThink( void ) {
|
|
if (!gameLocal.isNewFrame) { //HUMANHEAD rww
|
|
return;
|
|
}
|
|
//HUMANHEAD rww - let the crawler grenade continue regardless because it uses a proxy model.
|
|
if ( !IsType(hhProjectileCrawlerGrenade::Type) ) {
|
|
if (!renderEntity.hModel) {
|
|
return;
|
|
}
|
|
if (IsHidden()) {
|
|
BecomeInactive(TH_PHYSICS);
|
|
}
|
|
}
|
|
//HUMANHEAD END
|
|
Think();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idProjectile::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
msg.WriteBits( owner.GetSpawnId(), 32 );
|
|
msg.WriteBits( state, 3 );
|
|
msg.WriteBits( fl.hidden, 1 );
|
|
|
|
//RWWMERGE: They added some logic here
|
|
#if HUMANHEAD
|
|
//HUMANHEAD rww
|
|
if (netSyncPhysics) {
|
|
WriteBindToSnapshot(msg);
|
|
physicsObj.WriteToSnapshot( msg );
|
|
}
|
|
//HUMANHEAD END
|
|
#else
|
|
if ( netSyncPhysics ) {
|
|
msg.WriteBits( 1, 1 );
|
|
physicsObj.WriteToSnapshot( msg );
|
|
} else {
|
|
msg.WriteBits( 0, 1 );
|
|
const idVec3 &origin = physicsObj.GetOrigin();
|
|
const idVec3 &velocity = physicsObj.GetLinearVelocity();
|
|
|
|
msg.WriteFloat( origin.x );
|
|
msg.WriteFloat( origin.y );
|
|
msg.WriteFloat( origin.z );
|
|
|
|
msg.WriteDeltaFloat( 0.0f, velocity[0], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
msg.WriteDeltaFloat( 0.0f, velocity[1], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
msg.WriteDeltaFloat( 0.0f, velocity[2], RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
idProjectile::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idProjectile::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
projectileState_t newState;
|
|
|
|
owner.SetSpawnId( msg.ReadBits( 32 ) );
|
|
newState = (projectileState_t) msg.ReadBits( 3 );
|
|
if ( msg.ReadBits( 1 ) ) {
|
|
Hide();
|
|
if (IsType(hhProjectile::Type) && teamChain) { //HUMANHEAD rww - if server hid the projectile clear it on the client.
|
|
hhProjectile *hhSelf = static_cast<hhProjectile *>(this);
|
|
hhSelf->RemoveProjectile(3000);
|
|
}
|
|
} else {
|
|
if (state == EXPLODED) { //HUMANHEAD rww
|
|
Hide();
|
|
}
|
|
else {
|
|
Show();
|
|
}
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
if (netSyncPhysics) {
|
|
ReadBindFromSnapshot(msg);
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
//store
|
|
idMat3 ax = GetAxis();
|
|
idVec3 dir = ax[0];
|
|
idVec3 origin = physicsObj.GetOrigin();
|
|
idVec3 pushVel = physicsObj.GetPushedLinearVelocity();
|
|
|
|
//HUMANHEAD rww - i have seen this happen, not yet sure what causes it.
|
|
assert(newState <= COLLIDED);
|
|
//HUMANHEAD END
|
|
|
|
if (state != EXPLODED && state != FIZZLED || state != COLLIDED) { //HUMANHEAD rww
|
|
while( state != newState ) {
|
|
switch( state ) {
|
|
case SPAWNED: {
|
|
//HUMANHEAD rww - supply origin/axis
|
|
//Create( owner.GetEntity(), origin, ax );
|
|
state = CREATED;
|
|
break;
|
|
}
|
|
case CREATED: {
|
|
// the right origin and direction are required if you want bullet traces
|
|
//HUMANHEAD rww
|
|
if (IsType(hhProjectile::Type)) {
|
|
hhProjectile *hhSelf = static_cast<hhProjectile *>(this);
|
|
origin = hhSelf->launchPos;
|
|
ax = hhSelf->launchQuat.ToMat3();
|
|
Create( owner.GetEntity(), origin, ax );
|
|
hhSelf->Launch(origin, ax, pushVel);
|
|
}
|
|
//HUMAMHEAD END
|
|
break;
|
|
}
|
|
case LAUNCHED: {
|
|
if (netSyncPhysics) { //HUMANHEAD rww - only things with full physics sync will bother with this
|
|
if ( newState == FIZZLED ) {
|
|
Fizzle();
|
|
} else {
|
|
trace_t collision;
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.c.entityNum = ENTITYNUM_WORLD;
|
|
collision.endAxis = GetPhysics()->GetAxis();
|
|
collision.endpos = GetPhysics()->GetOrigin();
|
|
collision.c.point = GetPhysics()->GetOrigin();
|
|
collision.c.normal.Set( 0, 0, 1 );
|
|
//HUMANHEAD rww
|
|
if (IsType(hhProjectile::Type)) {
|
|
hhProjectile *hhSelf = static_cast<hhProjectile *>(this);
|
|
hhSelf->ProcessCollision(&collision, GetPhysics()->GetLinearVelocity());
|
|
hhSelf->ProcessCollisionEvent(&collision, GetPhysics()->GetLinearVelocity());
|
|
if (newState == EXPLODED && state != EXPLODED) { //crawler grenades etc
|
|
hhSelf->Explode(&collision, pushVel, 0);
|
|
}
|
|
else if (newState == COLLIDED) { //crawler secondary etc
|
|
state = COLLIDED;
|
|
}
|
|
}
|
|
else {
|
|
Explode( collision, NULL );
|
|
}
|
|
//HUMANHEAD END
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
netSyncPhysics = true;
|
|
int lies = gameLocal.time+200;
|
|
GetPhysics()->Evaluate(lies-gameLocal.previousTime, lies);
|
|
netSyncPhysics = false;
|
|
*/
|
|
ClientHideProjectile();
|
|
state = newState;
|
|
}
|
|
break;
|
|
}
|
|
case FIZZLED:
|
|
case COLLIDED: //HUMANHEAD bjk
|
|
case EXPLODED: {
|
|
StopSound( SND_CHANNEL_BODY2, false );
|
|
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
|
|
state = SPAWNED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//HUMANHEAD rww - moved to up above
|
|
//physicsObj.ReadFromSnapshot( msg );
|
|
//HUMANHEAD END
|
|
|
|
if ( msg.HasChanged() ) {
|
|
//Show();
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
void idProjectile::ClientHideProjectile(void) {
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
//RWWMERGE: Id's new version: handmerge this
|
|
/*
|
|
void idProjectile::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
projectileState_t newState;
|
|
|
|
owner.SetSpawnId( msg.ReadBits( 32 ) );
|
|
newState = (projectileState_t) msg.ReadBits( 3 );
|
|
if ( msg.ReadBits( 1 ) ) {
|
|
Hide();
|
|
} else {
|
|
Show();
|
|
}
|
|
|
|
while( state != newState ) {
|
|
switch( state ) {
|
|
case SPAWNED: {
|
|
Create( owner.GetEntity(), vec3_origin, idVec3( 1, 0, 0 ) );
|
|
break;
|
|
}
|
|
case CREATED: {
|
|
// the right origin and direction are required if you want bullet traces
|
|
Launch( vec3_origin, idVec3( 1, 0, 0 ), vec3_origin );
|
|
break;
|
|
}
|
|
case LAUNCHED: {
|
|
if ( newState == FIZZLED ) {
|
|
Fizzle();
|
|
} else {
|
|
trace_t collision;
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.endAxis = GetPhysics()->GetAxis();
|
|
collision.endpos = GetPhysics()->GetOrigin();
|
|
collision.c.point = GetPhysics()->GetOrigin();
|
|
collision.c.normal.Set( 0, 0, 1 );
|
|
Explode( collision, NULL );
|
|
}
|
|
break;
|
|
}
|
|
case FIZZLED:
|
|
case EXPLODED: {
|
|
StopSound( SND_CHANNEL_BODY2, false );
|
|
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
|
|
state = SPAWNED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( msg.ReadBits( 1 ) ) {
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
} else {
|
|
idVec3 origin;
|
|
idVec3 velocity;
|
|
idVec3 tmp;
|
|
idMat3 axis;
|
|
|
|
origin.x = msg.ReadFloat();
|
|
origin.y = msg.ReadFloat();
|
|
origin.z = msg.ReadFloat();
|
|
|
|
velocity.x = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
velocity.y = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
velocity.z = msg.ReadDeltaFloat( 0.0f, RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS );
|
|
|
|
physicsObj.SetOrigin( origin );
|
|
physicsObj.SetLinearVelocity( velocity );
|
|
|
|
// align z-axis of model with the direction
|
|
velocity.NormalizeFast();
|
|
axis = velocity.ToMat3();
|
|
tmp = axis[2];
|
|
axis[2] = axis[0];
|
|
axis[0] = -tmp;
|
|
physicsObj.SetAxis( axis );
|
|
}
|
|
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idProjectile::ClientReceiveEvent
|
|
================
|
|
*/
|
|
extern const int RB_VELOCITY_EXPONENT_BITS; //HUMANHEAD rww
|
|
extern const int RB_VELOCITY_MANTISSA_BITS; //HUMANHEAD rww
|
|
bool idProjectile::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
|
//HUMANHEAD rww
|
|
if (!IsType(hhProjectile::Type)) { //desperate attempts to catch terrible hateful crash bug
|
|
gameLocal.Error("idProjectile::ClientReceiveEvent: this is not of type hhProjectile!");
|
|
}
|
|
|
|
switch (event) {
|
|
case EVENT_PROJECTILE_EXPLOSION: {
|
|
trace_t collision;
|
|
memset(&collision, 0, sizeof(collision));
|
|
|
|
collision.c.contents = msg.ReadBits(32);
|
|
collision.c.dist = msg.ReadFloat();
|
|
collision.c.entityNum = msg.ReadBits(GENTITYNUM_BITS);
|
|
collision.c.id = msg.ReadShort();
|
|
|
|
int materialIndex = msg.ReadShort();
|
|
const idMaterial *collisionMaterial = NULL;
|
|
if (materialIndex != -1) {
|
|
collisionMaterial = static_cast<const idMaterial *>( declManager->DeclByIndex( DECL_MATERIAL, materialIndex ) );
|
|
}
|
|
collision.c.material = collisionMaterial;
|
|
collision.c.modelFeature = msg.ReadBits(32);
|
|
collision.c.normal = msg.ReadDir(24);
|
|
collision.c.point.x = msg.ReadFloat();
|
|
collision.c.point.y = msg.ReadFloat();
|
|
collision.c.point.z = msg.ReadFloat();
|
|
collision.c.trmFeature = msg.ReadBits(32);
|
|
collision.c.type = (contactType_t)msg.ReadBits(4);
|
|
collision.fraction = msg.ReadFloat();
|
|
|
|
//unfortunately, this is needed for proper decal projections
|
|
idVec3 collisionVel;
|
|
collisionVel.x = msg.ReadFloat(RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS);
|
|
collisionVel.y = msg.ReadFloat(RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS);
|
|
collisionVel.z = msg.ReadFloat(RB_VELOCITY_EXPONENT_BITS, RB_VELOCITY_MANTISSA_BITS);
|
|
|
|
collision.c.normal.Normalize(); //renormalize the normal
|
|
collision.endpos = collision.c.point;
|
|
idVec3 endDir = msg.ReadDir(24);
|
|
endDir.Normalize();
|
|
collision.endAxis = endDir.ToMat3();
|
|
|
|
//make sure the ent we hit is still valid on the client before doing anything.
|
|
if (gameLocal.entities[collision.c.entityNum] && gameLocal.entities[collision.c.entityNum]->IsType(idEntity::Type)) {
|
|
projectileState_t oldState = state;
|
|
state = LAUNCHED;
|
|
assert(IsType(hhProjectile::Type));
|
|
|
|
hhProjectile *hhSelf = static_cast<hhProjectile *>(this);
|
|
hhSelf->ProcessCollisionEvent(&collision, collisionVel);
|
|
|
|
state = oldState;
|
|
}
|
|
return true;
|
|
}
|
|
default: {
|
|
return idEntity::ClientReceiveEvent( event, time, msg );
|
|
}
|
|
|
|
}
|
|
return false;
|
|
//HUMANHEAD END
|
|
/* HUMANHEAD pdm: not used
|
|
trace_t collision;
|
|
idVec3 velocity;
|
|
|
|
switch( event ) {
|
|
case EVENT_DAMAGE_EFFECT: {
|
|
memset( &collision, 0, sizeof( collision ) );
|
|
collision.c.point[0] = msg.ReadFloat();
|
|
collision.c.point[1] = msg.ReadFloat();
|
|
collision.c.point[2] = msg.ReadFloat();
|
|
collision.c.normal = msg.ReadDir( 24 );
|
|
int index = gameLocal.ClientRemapDecl( DECL_MATERIAL, msg.ReadLong() );
|
|
collision.c.material = ( index != -1 ) ? static_cast<const idMaterial *>( declManager->DeclByIndex( DECL_MATERIAL, index ) ) : NULL;
|
|
velocity[0] = msg.ReadFloat( 5, 10 );
|
|
velocity[1] = msg.ReadFloat( 5, 10 );
|
|
velocity[2] = msg.ReadFloat( 5, 10 );
|
|
DefaultDamageEffect( this, spawnArgs, collision, velocity );
|
|
return true;
|
|
}
|
|
default: {
|
|
return idEntity::ClientReceiveEvent( event, time, msg );
|
|
}
|
|
}
|
|
return false;
|
|
*/
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idGuidedProjectile
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idProjectile, idGuidedProjectile )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::idGuidedProjectile
|
|
================
|
|
*/
|
|
idGuidedProjectile::idGuidedProjectile( void ) {
|
|
enemy = NULL;
|
|
speed = 0.0f;
|
|
turn_max = 0.0f;
|
|
clamp_dist = 0.0f;
|
|
rndScale = ang_zero;
|
|
rndAng = ang_zero;
|
|
rndUpdateTime = 0;
|
|
angles = ang_zero;
|
|
burstMode = false;
|
|
burstDist = 0;
|
|
burstVelocity = 0.0f;
|
|
unGuided = false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idGuidedProjectile::~idGuidedProjectile
|
|
=================
|
|
*/
|
|
idGuidedProjectile::~idGuidedProjectile( void ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::Spawn
|
|
================
|
|
*/
|
|
void idGuidedProjectile::Spawn( void ) {
|
|
}
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::Save
|
|
================
|
|
*/
|
|
void idGuidedProjectile::Save( idSaveGame *savefile ) const {
|
|
enemy.Save( savefile );
|
|
savefile->WriteFloat( speed );
|
|
savefile->WriteAngles( rndScale );
|
|
savefile->WriteAngles( rndAng );
|
|
savefile->WriteInt( rndUpdateTime );
|
|
savefile->WriteFloat( turn_max );
|
|
savefile->WriteFloat( clamp_dist );
|
|
savefile->WriteAngles( angles );
|
|
savefile->WriteBool( burstMode );
|
|
savefile->WriteBool( unGuided );
|
|
savefile->WriteFloat( burstDist );
|
|
savefile->WriteFloat( burstVelocity );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::Restore
|
|
================
|
|
*/
|
|
void idGuidedProjectile::Restore( idRestoreGame *savefile ) {
|
|
enemy.Restore( savefile );
|
|
savefile->ReadFloat( speed );
|
|
savefile->ReadAngles( rndScale );
|
|
savefile->ReadAngles( rndAng );
|
|
savefile->ReadInt( rndUpdateTime );
|
|
savefile->ReadFloat( turn_max );
|
|
savefile->ReadFloat( clamp_dist );
|
|
savefile->ReadAngles( angles );
|
|
savefile->ReadBool( burstMode );
|
|
savefile->ReadBool( unGuided );
|
|
savefile->ReadFloat( burstDist );
|
|
savefile->ReadFloat( burstVelocity );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::GetSeekPos
|
|
================
|
|
*/
|
|
void idGuidedProjectile::GetSeekPos( idVec3 &out ) {
|
|
idEntity *enemyEnt = enemy.GetEntity();
|
|
if ( enemyEnt ) {
|
|
if ( enemyEnt->IsType( idActor::Type ) ) {
|
|
out = static_cast<idActor *>(enemyEnt)->GetEyePosition();
|
|
out.z -= 12.0f;
|
|
} else {
|
|
out = enemyEnt->GetPhysics()->GetOrigin();
|
|
}
|
|
} else {
|
|
out = GetPhysics()->GetOrigin() + physicsObj.GetLinearVelocity() * 2.0f;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idGuidedProjectile::Think
|
|
================
|
|
*/
|
|
void idGuidedProjectile::Think( void ) {
|
|
idVec3 dir;
|
|
idVec3 seekPos;
|
|
idVec3 velocity;
|
|
idVec3 nose;
|
|
idVec3 tmp;
|
|
idMat3 axis;
|
|
idAngles dirAng;
|
|
idAngles diff;
|
|
float dist;
|
|
float frac;
|
|
int i;
|
|
|
|
if ( state == LAUNCHED && !unGuided ) {
|
|
|
|
GetSeekPos( seekPos );
|
|
|
|
if ( rndUpdateTime < gameLocal.time ) {
|
|
rndAng[ 0 ] = rndScale[ 0 ] * gameLocal.random.CRandomFloat();
|
|
rndAng[ 1 ] = rndScale[ 1 ] * gameLocal.random.CRandomFloat();
|
|
rndAng[ 2 ] = rndScale[ 2 ] * gameLocal.random.CRandomFloat();
|
|
rndUpdateTime = gameLocal.time + 200;
|
|
}
|
|
|
|
nose = physicsObj.GetOrigin() + 10.0f * physicsObj.GetAxis()[0];
|
|
|
|
dir = seekPos - nose;
|
|
dist = dir.Normalize();
|
|
dirAng = dir.ToAngles();
|
|
|
|
// make it more accurate as it gets closer
|
|
frac = dist / clamp_dist;
|
|
if ( frac > 1.0f ) {
|
|
frac = 1.0f;
|
|
}
|
|
|
|
diff = dirAng - angles + rndAng * frac;
|
|
|
|
// clamp the to the max turn rate
|
|
diff.Normalize180();
|
|
for( i = 0; i < 3; i++ ) {
|
|
if ( diff[ i ] > turn_max ) {
|
|
diff[ i ] = turn_max;
|
|
} else if ( diff[ i ] < -turn_max ) {
|
|
diff[ i ] = -turn_max;
|
|
}
|
|
}
|
|
angles += diff;
|
|
|
|
// make the visual model always points the dir we're traveling
|
|
dir = angles.ToForward();
|
|
velocity = dir * speed;
|
|
|
|
if ( burstMode && dist < burstDist ) {
|
|
unGuided = true;
|
|
velocity *= burstVelocity;
|
|
}
|
|
|
|
physicsObj.SetLinearVelocity( velocity );
|
|
|
|
// align z-axis of model with the direction
|
|
axis = dir.ToMat3();
|
|
tmp = axis[2];
|
|
axis[2] = axis[0];
|
|
axis[0] = -tmp;
|
|
|
|
GetPhysics()->SetAxis( axis );
|
|
}
|
|
|
|
idProjectile::Think();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idGuidedProjectile::Launch
|
|
=================
|
|
*/
|
|
void idGuidedProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, float dmgPower ) {
|
|
idProjectile::Launch( start, dir, pushVelocity, timeSinceFire, launchPower, dmgPower );
|
|
if ( owner.GetEntity() ) {
|
|
if ( owner.GetEntity()->IsType( idAI::Type ) ) {
|
|
enemy = static_cast<idAI *>( owner.GetEntity() )->GetEnemy();
|
|
} else if ( owner.GetEntity()->IsType( idPlayer::Type ) ) {
|
|
trace_t tr;
|
|
idPlayer *player = static_cast<idPlayer*>( owner.GetEntity() );
|
|
idVec3 start = player->GetEyePosition();
|
|
idVec3 end = start + player->viewAxis[0] * 1000.0f;
|
|
gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL | CONTENTS_BODY, owner.GetEntity() );
|
|
if ( tr.fraction < 1.0f ) {
|
|
enemy = gameLocal.GetTraceEntity( tr );
|
|
}
|
|
// ignore actors on the player's team
|
|
if ( enemy.GetEntity() == NULL || !enemy.GetEntity()->IsType( idActor::Type ) || ( static_cast<idActor *>( enemy.GetEntity() )->team == player->team ) ) {
|
|
enemy = player->EnemyWithMostHealth();
|
|
}
|
|
}
|
|
}
|
|
const idVec3 &vel = physicsObj.GetLinearVelocity();
|
|
angles = vel.ToAngles();
|
|
speed = vel.Length();
|
|
rndScale = spawnArgs.GetAngles( "random", "15 15 0" );
|
|
turn_max = spawnArgs.GetFloat( "turn_max", "180" ) / ( float )USERCMD_HZ;
|
|
clamp_dist = spawnArgs.GetFloat( "clamp_dist", "256" );
|
|
burstMode = spawnArgs.GetBool( "burstMode" );
|
|
unGuided = false;
|
|
burstDist = spawnArgs.GetFloat( "burstDist", "64" );
|
|
burstVelocity = spawnArgs.GetFloat( "burstVelocity", "1.25" );
|
|
UpdateVisuals();
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idSoulCubeMissile (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idBFGProjectile (HUMANHEAD pdm: removed)
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
idDebris
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
const idEventDef EV_CheckClip( "<checkclip>" ); // HUMANHEAD mdl
|
|
|
|
CLASS_DECLARATION( idEntity, idDebris )
|
|
EVENT( EV_Explode, idDebris::Event_Explode )
|
|
EVENT( EV_Fizzle, idDebris::Event_Fizzle )
|
|
EVENT( EV_CheckClip, idDebris::Event_CheckClip ) // HUMANHEAD mdl
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
idDebris::Spawn
|
|
================
|
|
*/
|
|
void idDebris::Spawn( void ) {
|
|
owner = NULL;
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
|
|
//HUMANHEAD: aob
|
|
float gravityMagnitude = physicsObj.GetGravity().Length();
|
|
collisionSpeed_max = hhUtils::DetermineFinalFallVelocityMagnitude( spawnArgs.GetFloat("collideDist_max", "25"), gravityMagnitude );
|
|
collisionSpeed_min = hhUtils::DetermineFinalFallVelocityMagnitude( spawnArgs.GetFloat("collideDist_min", "1"), gravityMagnitude );
|
|
//HUMANHEAD END
|
|
|
|
//HUMANHEAD rww
|
|
solidTest = 0; // mdl
|
|
if (gameLocal.isClient) {
|
|
Launch();
|
|
}
|
|
//HUMANHEAD END
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Create
|
|
================
|
|
*/
|
|
void idDebris::Create( idEntity *owner, const idVec3 &start, const idMat3 &axis ) {
|
|
Unbind();
|
|
GetPhysics()->SetOrigin( start );
|
|
GetPhysics()->SetAxis( axis );
|
|
GetPhysics()->SetContents( 0 );
|
|
this->owner = owner;
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
sndBounce = NULL;
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDebris::idDebris
|
|
=================
|
|
*/
|
|
idDebris::idDebris( void ) {
|
|
owner = NULL;
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
sndBounce = NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDebris::~idDebris
|
|
=================
|
|
*/
|
|
idDebris::~idDebris( void ) {
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDebris::Save
|
|
=================
|
|
*/
|
|
void idDebris::Save( idSaveGame *savefile ) const {
|
|
owner.Save( savefile );
|
|
|
|
savefile->WriteStaticObject( physicsObj );
|
|
|
|
savefile->WriteParticle( smokeFly );
|
|
savefile->WriteInt( smokeFlyTime );
|
|
savefile->WriteSoundShader( sndBounce );
|
|
|
|
// HUMANHEAD mdl
|
|
savefile->WriteFloat( collisionSpeed_max );
|
|
savefile->WriteFloat( collisionSpeed_min );
|
|
savefile->WriteInt( currentChannel );
|
|
// HUMANHEAD END
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDebris::Restore
|
|
=================
|
|
*/
|
|
void idDebris::Restore( idRestoreGame *savefile ) {
|
|
owner.Restore( savefile );
|
|
|
|
savefile->ReadStaticObject( physicsObj );
|
|
RestorePhysics( &physicsObj );
|
|
|
|
savefile->ReadParticle( smokeFly );
|
|
savefile->ReadInt( smokeFlyTime );
|
|
savefile->ReadSoundShader( sndBounce );
|
|
|
|
// HUMANHEAD mdl
|
|
savefile->ReadFloat( collisionSpeed_max );
|
|
savefile->ReadFloat( collisionSpeed_min );
|
|
savefile->ReadInt( currentChannel );
|
|
solidTest = 0; // Don't worry about saving this
|
|
// HUMANHEAD END
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
/*
|
|
=================
|
|
idDebris::ClientPredictionThink
|
|
=================
|
|
*/
|
|
void idDebris::ClientPredictionThink( void ) {
|
|
if (!gameLocal.isNewFrame) {
|
|
return;
|
|
}
|
|
Think();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idDebris::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
GetPhysics()->WriteToSnapshot(msg);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idDebris::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
GetPhysics()->ReadFromSnapshot(msg);
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
|
|
/*
|
|
=================
|
|
idDebris::Launch
|
|
=================
|
|
*/
|
|
void idDebris::Launch( void ) {
|
|
float fuse;
|
|
idVec3 velocity;
|
|
idAngles angular_velocity;
|
|
float linear_friction;
|
|
float angular_friction;
|
|
float contact_friction;
|
|
float bounce;
|
|
float mass;
|
|
float gravity;
|
|
idVec3 gravVec;
|
|
bool randomVelocity;
|
|
idMat3 axis;
|
|
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
|
|
spawnArgs.GetVector( "velocity", "0 0 0", velocity );
|
|
spawnArgs.GetAngles( "angular_velocity", "0 0 0", angular_velocity );
|
|
|
|
linear_friction = spawnArgs.GetFloat( "linear_friction" );
|
|
angular_friction = spawnArgs.GetFloat( "angular_friction" );
|
|
contact_friction = spawnArgs.GetFloat( "contact_friction" );
|
|
bounce = spawnArgs.GetFloat( "bounce" );
|
|
mass = spawnArgs.GetFloat( "mass" );
|
|
gravity = spawnArgs.GetFloat( "gravity" );
|
|
fuse = spawnArgs.GetFloat( "fuse" );
|
|
randomVelocity = spawnArgs.GetBool ( "random_velocity" );
|
|
|
|
if ( mass <= 0 ) {
|
|
gameLocal.Error( "Invalid mass on '%s'\n", GetEntityDefName() );
|
|
}
|
|
|
|
if ( randomVelocity ) {
|
|
velocity.x *= gameLocal.random.RandomFloat() + 0.5f;
|
|
velocity.y *= gameLocal.random.RandomFloat() + 0.5f;
|
|
velocity.z *= gameLocal.random.RandomFloat() + 0.5f;
|
|
}
|
|
|
|
if ( health ) {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
//HUMANHEAD rww
|
|
fl.networkSync = true;
|
|
//HUMANHEAD END
|
|
|
|
// HUMANHEAD pdm: use local gravity on debris
|
|
idBounds pointBounds(vec3_origin);
|
|
gravVec = hhUtils::GetLocalGravity( GetOrigin(), pointBounds );
|
|
// HUMANHEAD END
|
|
gravVec.NormalizeFast();
|
|
axis = GetPhysics()->GetAxis();
|
|
|
|
Unbind();
|
|
|
|
physicsObj.SetSelf( this );
|
|
|
|
// check if a clip model is set
|
|
const char *clipModelName;
|
|
idTraceModel trm;
|
|
spawnArgs.GetString( "clipmodel", "", &clipModelName );
|
|
if ( !clipModelName[0] ) {
|
|
clipModelName = spawnArgs.GetString( "model" ); // use the visual model
|
|
}
|
|
|
|
// load the trace model
|
|
if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) {
|
|
// default to a box
|
|
physicsObj.SetClipBox( renderEntity.bounds, 1.0f );
|
|
} else {
|
|
physicsObj.SetClipModel( new idClipModel( trm ), 1.0f );
|
|
}
|
|
|
|
physicsObj.GetClipModel()->SetOwner( owner.GetEntity() );
|
|
physicsObj.SetMass( mass );
|
|
physicsObj.SetFriction( linear_friction, angular_friction, contact_friction );
|
|
if ( contact_friction == 0.0f ) {
|
|
physicsObj.NoContact();
|
|
}
|
|
physicsObj.SetBouncyness( bounce );
|
|
physicsObj.SetGravity( gravVec * gravity );
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP );
|
|
physicsObj.SetLinearVelocity( axis[ 0 ] * velocity[ 0 ] + axis[ 1 ] * velocity[ 1 ] + axis[ 2 ] * velocity[ 2 ] );
|
|
physicsObj.SetAngularVelocity( angular_velocity.ToAngularVelocity() * axis );
|
|
physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
|
|
physicsObj.SetAxis( axis );
|
|
SetPhysics( &physicsObj );
|
|
|
|
if ( !gameLocal.isClient || fl.clientEntity ) { //HUMANHEAD rww - if clientEntity, post events.
|
|
if ( fuse <= 0 ) {
|
|
// run physics for 1 second
|
|
RunPhysics();
|
|
PostEventMS( &EV_Remove, 0 );
|
|
} else if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
|
|
if ( fuse < 0.0f ) {
|
|
fuse = 0.0f;
|
|
}
|
|
RunPhysics();
|
|
PostEventSec( &EV_Explode, fuse );
|
|
} else {
|
|
if ( fuse < 0.0f ) {
|
|
fuse = 0.0f;
|
|
}
|
|
PostEventSec( &EV_Fizzle, fuse );
|
|
}
|
|
}
|
|
|
|
StartSound( "snd_fly", SND_CHANNEL_BODY, 0, false, NULL );
|
|
|
|
//ProcessEvent( &EV_CheckClip ); // HUMANHEAD mdl: Check to see if we're inside a solid
|
|
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
const char *smokeName = spawnArgs.GetString( "smoke_fly" );
|
|
if ( *smokeName != '\0' ) {
|
|
smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
smokeFlyTime = gameLocal.time;
|
|
gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
|
|
}
|
|
|
|
const char *sndName = spawnArgs.GetString( "snd_bounce" );
|
|
if ( *sndName != '\0' ) {
|
|
sndBounce = declManager->FindSound( sndName );
|
|
}
|
|
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Think
|
|
================
|
|
*/
|
|
void idDebris::Think( void ) {
|
|
|
|
// run physics
|
|
RunPhysics();
|
|
Present();
|
|
|
|
if ( smokeFly && smokeFlyTime ) {
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ) ) {
|
|
smokeFlyTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Killed
|
|
================
|
|
*/
|
|
void idDebris::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
if ( spawnArgs.GetBool( "detonate_on_death" ) ) {
|
|
Explode();
|
|
} else {
|
|
Fizzle();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDebris::Collide
|
|
=================
|
|
*/
|
|
bool idDebris::Collide( const trace_t &collision, const idVec3 &velocity ) {
|
|
//HUMANHEAD: aob - replaced id's bounce sound code
|
|
AttemptToPlayBounceSound( collision, velocity );
|
|
//HUMANHEAD END
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::DetermineNextChannel
|
|
|
|
HUMANHEAD: aob - copied from hhMovable
|
|
================
|
|
*/
|
|
s_channelType idDebris::DetermineNextChannel() {
|
|
static int NUM_CHANNELS = 6;
|
|
|
|
currentChannel = (currentChannel + 1) % NUM_CHANNELS;
|
|
return (s_channelType)((currentChannel == SCHANNEL_ANY) ? ++currentChannel : currentChannel);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::AttemptToPlayBounceSound
|
|
|
|
HUMANHEAD: aob - copied from hhMovable
|
|
================
|
|
*/
|
|
void idDebris::AttemptToPlayBounceSound( const trace_t &collision, const idVec3 &velocity ) {
|
|
s_channelType channel;
|
|
|
|
float len = velocity * -collision.c.normal;
|
|
if( len > collisionSpeed_min && collision.c.material && !(collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT) ) {
|
|
channel = DetermineNextChannel();
|
|
if( StartSoundShader(sndBounce, channel, 0, true, NULL) ) {
|
|
// Change volume only after we know the sound played
|
|
float volume = hhUtils::CalculateSoundVolume( len, collisionSpeed_min, collisionSpeed_max );
|
|
HH_SetSoundVolume( volume, channel );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Fizzle
|
|
================
|
|
*/
|
|
void idDebris::Fizzle( void ) {
|
|
if ( IsHidden() ) {
|
|
// already exploded
|
|
return;
|
|
}
|
|
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
StartSound( "snd_fizzle", SND_CHANNEL_BODY, 0, false, NULL );
|
|
|
|
// fizzle FX
|
|
const char *smokeName = spawnArgs.GetString( "smoke_fuse" );
|
|
if ( *smokeName != '\0' ) {
|
|
smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
smokeFlyTime = gameLocal.time;
|
|
gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
|
|
}
|
|
|
|
fl.takedamage = false;
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.PutToRest();
|
|
|
|
Hide();
|
|
|
|
if ( gameLocal.isClient && !fl.clientEntity ) { //HUMANHEAD rww - continue with events for clientEntities.
|
|
return;
|
|
}
|
|
|
|
CancelEvents( &EV_Fizzle );
|
|
PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Explode
|
|
================
|
|
*/
|
|
void idDebris::Explode( void ) {
|
|
if ( IsHidden() ) {
|
|
// already exploded
|
|
return;
|
|
}
|
|
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
StartSound( "snd_explode", SND_CHANNEL_BODY, 0, false, NULL );
|
|
|
|
Hide();
|
|
|
|
// these must not be "live forever" particle systems
|
|
smokeFly = NULL;
|
|
smokeFlyTime = 0;
|
|
const char *smokeName = spawnArgs.GetString( "smoke_detonate" );
|
|
if ( *smokeName != '\0' ) {
|
|
smokeFly = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
smokeFlyTime = gameLocal.time;
|
|
gameLocal.smokeParticles->EmitSmoke( smokeFly, smokeFlyTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
|
|
}
|
|
|
|
fl.takedamage = false;
|
|
physicsObj.SetContents( 0 );
|
|
physicsObj.PutToRest();
|
|
|
|
CancelEvents( &EV_Explode );
|
|
PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Event_Explode
|
|
================
|
|
*/
|
|
void idDebris::Event_Explode( void ) {
|
|
Explode();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDebris::Event_Fizzle
|
|
================
|
|
*/
|
|
void idDebris::Event_Fizzle( void ) {
|
|
Fizzle();
|
|
}
|
|
|
|
// HUMANHEAD mdl: Derived from AF solid test code
|
|
void idDebris::Event_CheckClip( void ) {
|
|
trace_t trace;
|
|
|
|
solidTest++;
|
|
if (solidTest > 10) {
|
|
gameLocal.DWarning( "%s STILL STUCK, removing.\n", name.c_str() );
|
|
PostEventMS(&EV_Remove, 100);
|
|
return;
|
|
}
|
|
|
|
if ( gameLocal.clip.Translation( trace, physicsObj.GetOrigin(), physicsObj.GetOrigin(), physicsObj.GetClipModel(), physicsObj.GetAxis(), physicsObj.GetClipMask(), this ) ) {
|
|
float depth = idMath::Fabs( trace.c.point * trace.c.normal - trace.c.dist );
|
|
|
|
physicsObj.SetOrigin( physicsObj.GetOrigin() + trace.c.normal * ( depth + 8.0f ) );
|
|
|
|
gameLocal.DWarning( "%s: debris stuck in %d (normal = %.2f %.2f %.2f, depth = %.2f)", this->name.c_str(), trace.c.contents, trace.c.normal.x, trace.c.normal.y, trace.c.normal.z, depth );
|
|
PostEventMS(&EV_CheckClip, 100);
|
|
} else {
|
|
gameLocal.DPrintf("Cleared solid after %d tries. \n", solidTest);
|
|
}
|
|
}
|
|
// HUMANHEAD END
|
|
|