2013-04-19 02:52:48 +00:00
// leave this line at the top for all g_xxxx.cpp files...
# include "g_headers.h"
# include "g_local.h"
# include "g_functions.h"
2013-04-19 03:47:29 +00:00
# include "../cgame/cg_media.h"
2013-04-19 02:52:48 +00:00
//client side shortcut hacks from cg_local.h
//extern void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shake_speed, qboolean smoke );
extern void CG_MiscModelExplosion ( vec3_t mins , vec3_t maxs , int size , material_t chunkType ) ;
extern void CG_Chunks ( int owner , vec3_t origin , const vec3_t normal , const vec3_t mins , const vec3_t maxs ,
float speed , int numChunks , material_t chunkType , int customChunk , float baseScale , int customSound = 0 ) ;
extern void G_SetEnemy ( gentity_t * self , gentity_t * enemy ) ;
extern gentity_t * G_CreateObject ( gentity_t * owner , vec3_t origin , vec3_t angles , int modelIndex , int frame , trType_t trType , int effectID ) ;
extern qboolean player_locked ;
//---------------------------------------------------
static void CacheChunkEffects ( material_t material )
{
switch ( material )
{
case MAT_GLASS :
G_EffectIndex ( " chunks/glassbreak " ) ;
break ;
case MAT_GLASS_METAL :
G_EffectIndex ( " chunks/glassbreak " ) ;
G_EffectIndex ( " chunks/metalexplode " ) ;
break ;
case MAT_ELECTRICAL :
case MAT_ELEC_METAL :
G_EffectIndex ( " chunks/sparkexplode " ) ;
break ;
case MAT_METAL :
case MAT_METAL2 :
case MAT_METAL3 :
case MAT_CRATE1 :
case MAT_CRATE2 :
G_EffectIndex ( " chunks/metalexplode " ) ;
break ;
case MAT_GRATE1 :
G_EffectIndex ( " chunks/grateexplode " ) ;
break ;
case MAT_DRK_STONE :
case MAT_LT_STONE :
case MAT_GREY_STONE :
case MAT_WHITE_METAL : // what is this crap really supposed to be??
G_EffectIndex ( " chunks/rockbreaklg " ) ;
G_EffectIndex ( " chunks/rockbreakmed " ) ;
break ;
case MAT_ROPE :
G_EffectIndex ( " chunks/ropebreak " ) ;
// G_SoundIndex(); // FIXME: give it a sound
break ;
}
}
//--------------------------------------
void funcBBrushDieGo ( gentity_t * self )
{
vec3_t org , dir , up ;
gentity_t * attacker = self - > enemy ;
float scale ;
int numChunks , size = 0 ;
material_t chunkType = self - > material ;
// if a missile is stuck to us, blow it up so we don't look dumb
// FIXME: flag me so I should know to do this check!
for ( int i = 0 ; i < MAX_GENTITIES ; i + + )
{
if ( g_entities [ i ] . s . groundEntityNum = = self - > s . number & & ( g_entities [ i ] . s . eFlags & EF_MISSILE_STICK ) )
{
G_Damage ( & g_entities [ i ] , self , self , NULL , NULL , 99999 , 0 , MOD_CRUSH ) ; //?? MOD?
}
}
//NOTE: MUST do this BEFORE clearing contents, or you may not open the area portal!!!
gi . AdjustAreaPortalState ( self , qtrue ) ;
//So chunks don't get stuck inside me
self - > s . solid = 0 ;
self - > contents = 0 ;
self - > clipmask = 0 ;
gi . linkentity ( self ) ;
VectorSet ( up , 0 , 0 , 1 ) ;
if ( self - > target & & attacker ! = NULL )
{
G_UseTargets ( self , attacker ) ;
}
VectorSubtract ( self - > absmax , self - > absmin , org ) ; // size
numChunks = random ( ) * 6 + 18 ;
// This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted.
// Volume is length * width * height...then break that volume down based on how many chunks we have
scale = sqrt ( sqrt ( org [ 0 ] * org [ 1 ] * org [ 2 ] ) ) * 1.75f ;
if ( scale > 48 )
{
size = 2 ;
}
else if ( scale > 24 )
{
size = 1 ;
}
scale = scale / numChunks ;
if ( self - > radius > 0.0f )
{
// designer wants to scale number of chunks, helpful because the above scale code is far from perfect
// I do this after the scale calculation because it seems that the chunk size generally seems to be very close, it's just the number of chunks is a bit weak
numChunks * = self - > radius ;
}
VectorMA ( self - > absmin , 0.5 , org , org ) ;
VectorAdd ( self - > absmin , self - > absmax , org ) ;
VectorScale ( org , 0.5f , org ) ;
if ( attacker ! = NULL & & attacker - > client )
{
VectorSubtract ( org , attacker - > currentOrigin , dir ) ;
VectorNormalize ( dir ) ;
}
else
{
VectorCopy ( up , dir ) ;
}
if ( ! ( self - > spawnflags & 2048 ) ) // NO_EXPLOSION
{
// we are allowed to explode
CG_MiscModelExplosion ( self - > absmin , self - > absmax , size , chunkType ) ;
}
if ( self - > splashDamage > 0 & & self - > splashRadius > 0 )
{
//explode
AddSightEvent ( attacker , org , 256 , AEL_DISCOVERED , 100 ) ;
AddSoundEvent ( attacker , org , 128 , AEL_DISCOVERED , qfalse , qtrue ) ; //FIXME: am I on ground or not?
G_RadiusDamage ( org , self , self - > splashDamage , self - > splashRadius , self , MOD_UNKNOWN ) ;
gentity_t * te = G_TempEntity ( org , EV_GENERAL_SOUND ) ;
te - > s . eventParm = G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ;
}
else
{ //just break
AddSightEvent ( attacker , org , 128 , AEL_DISCOVERED ) ;
AddSoundEvent ( attacker , org , 64 , AEL_SUSPICIOUS , qfalse , qtrue ) ; //FIXME: am I on ground or not?
}
//FIXME: base numChunks off size?
CG_Chunks ( self - > s . number , org , dir , self - > absmin , self - > absmax , 300 , numChunks , chunkType , 0 , scale , self - > noise_index ) ;
self - > e_ThinkFunc = thinkF_G_FreeEntity ;
self - > nextthink = level . time + 50 ;
//G_FreeEntity( self );
}
void funcBBrushDie ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
{
self - > takedamage = qfalse ; //stop chain reaction runaway loops
G_SetEnemy ( self , attacker ) ;
if ( self - > delay )
{
self - > e_ThinkFunc = thinkF_funcBBrushDieGo ;
self - > nextthink = level . time + floor ( self - > delay * 1000.0f ) ;
return ;
}
funcBBrushDieGo ( self ) ;
}
void funcBBrushUse ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
G_ActivateBehavior ( self , BSET_USE ) ;
if ( self - > spawnflags & 64 )
{ //Using it doesn't break it, makes it use it's targets
if ( self - > target & & self - > target [ 0 ] )
{
G_UseTargets ( self , activator ) ;
}
}
else
{
funcBBrushDie ( self , other , activator , self - > health , MOD_UNKNOWN ) ;
}
}
void funcBBrushPain ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , const vec3_t point , int damage , int mod , int hitLoc )
{
if ( self - > painDebounceTime > level . time )
{
return ;
}
if ( self - > paintarget )
{
G_UseTargets2 ( self , self - > activator , self - > paintarget ) ;
}
G_ActivateBehavior ( self , BSET_PAIN ) ;
if ( self - > material = = MAT_DRK_STONE
| | self - > material = = MAT_LT_STONE
| | self - > material = = MAT_GREY_STONE )
{
vec3_t org , dir ;
float scale ;
VectorSubtract ( self - > absmax , self - > absmin , org ) ; // size
// This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted.
// Volume is length * width * height...then break that volume down based on how many chunks we have
scale = VectorLength ( org ) / 100.0f ;
VectorMA ( self - > absmin , 0.5 , org , org ) ;
VectorAdd ( self - > absmin , self - > absmax , org ) ;
VectorScale ( org , 0.5f , org ) ;
if ( attacker ! = NULL & & attacker - > client )
{
VectorSubtract ( attacker - > currentOrigin , org , dir ) ;
VectorNormalize ( dir ) ;
}
else
{
VectorSet ( dir , 0 , 0 , 1 ) ;
}
CG_Chunks ( self - > s . number , org , dir , self - > absmin , self - > absmax , 300 , Q_irand ( 1 , 3 ) , self - > material , 0 , scale ) ;
}
if ( self - > wait = = - 1 )
{
self - > e_PainFunc = painF_NULL ;
return ;
}
self - > painDebounceTime = level . time + self - > wait ;
}
static void InitBBrush ( gentity_t * ent )
{
float light ;
vec3_t color ;
qboolean lightSet , colorSet ;
VectorCopy ( ent - > s . origin , ent - > pos1 ) ;
gi . SetBrushModel ( ent , ent - > model ) ;
ent - > e_DieFunc = dieF_funcBBrushDie ;
ent - > svFlags | = SVF_BBRUSH ;
// if the "model2" key is set, use a seperate model
// for drawing, but clip against the brushes
if ( ent - > model2 )
{
ent - > s . modelindex2 = G_ModelIndex ( ent - > model2 ) ;
}
// if the "color" or "light" keys are set, setup constantLight
lightSet = G_SpawnFloat ( " light " , " 100 " , & light ) ;
colorSet = G_SpawnVector ( " color " , " 1 1 1 " , color ) ;
if ( lightSet | | colorSet )
{
int r , g , b , i ;
r = color [ 0 ] * 255 ;
if ( r > 255 ) {
r = 255 ;
}
g = color [ 1 ] * 255 ;
if ( g > 255 ) {
g = 255 ;
}
b = color [ 2 ] * 255 ;
if ( b > 255 ) {
b = 255 ;
}
i = light / 4 ;
if ( i > 255 ) {
i = 255 ;
}
ent - > s . constantLight = r | ( g < < 8 ) | ( b < < 16 ) | ( i < < 24 ) ;
}
if ( ent - > spawnflags & 128 )
{ //Can be used by the player's BUTTON_USE
ent - > svFlags | = SVF_PLAYER_USABLE ;
}
ent - > s . eType = ET_MOVER ;
gi . linkentity ( ent ) ;
ent - > s . pos . trType = TR_STATIONARY ;
VectorCopy ( ent - > pos1 , ent - > s . pos . trBase ) ;
}
void funcBBrushTouch ( gentity_t * ent , gentity_t * other , trace_t * trace )
{
}
/*QUAKED func_breakable (0 .8 .5) ? INVINCIBLE IMPACT CRUSHER THIN SABERONLY HEAVY_WEAP USE_NOT_BREAK PLAYER_USE NO_EXPLOSION
INVINCIBLE - can only be broken by being used
IMPACT - does damage on impact
CRUSHER - won ' t reverse movement when hit an obstacle
THIN - can be broken by impact damage , like glass
SABERONLY - only takes damage from sabers
HEAVY_WEAP - only takes damage by a heavy weapon , like an emplaced gun or AT - ST gun .
USE_NOT_BREAK - Using it doesn ' t make it break , still can be destroyed by damage
PLAYER_USE - Player can use it with the use button
NO_EXPLOSION - Does not play an explosion effect , though will still create chunks if specified
When destroyed , fires it ' s trigger and chunks and plays sound " noise " or sound for type if no noise specified
" targetname " entities with matching target will fire it
" paintarget " target to fire when hit ( but not destroyed )
" wait " how long minimum to wait between firing paintarget each time hit
" delay " When killed or used , how long ( in seconds ) to wait before blowing up ( none by default )
" model2 " . md3 model to also draw
" modelAngles " md3 model ' s angles < pitch yaw roll > ( in addition to any rotation on the part of the brush entity itself )
" target " all entities with a matching targetname will be used when this is destoryed
" health " default is 10
" radius " Chunk code tries to pick a good volume of chunks , but you can alter this to scale the number of spawned chunks . ( default 1 ) ( .5 ) is half as many chunks , ( 2 ) is twice as many chunks
" NPC_targetname " - Only the NPC with this name can damage this
" forcevisible " - When you turn on force sight ( any level ) , you can see these draw through the entire level . . .
" redCrosshair " - crosshair turns red when you look at this
Damage : default is none
" splashDamage " - damage to do
" splashRadius " - radius for above damage
" team " - This cannot take damage from members of this team :
" player "
" neutral "
" enemy "
Don ' t know if these work :
" color " constantLight color
" light " constantLight radius
" material " - default is " 0 - MAT_METAL " - choose from this list :
0 = MAT_METAL ( basic blue - grey scorched - DEFAULT )
1 = MAT_GLASS
2 = MAT_ELECTRICAL ( sparks only )
3 = MAT_ELEC_METAL ( METAL2 chunks and sparks )
4 = MAT_DRK_STONE ( brown stone chunks )
5 = MAT_LT_STONE ( tan stone chunks )
6 = MAT_GLASS_METAL ( glass and METAL2 chunks )
7 = MAT_METAL2 ( electronic type of metal )
8 = MAT_NONE ( no chunks )
9 = MAT_GREY_STONE ( grey colored stone )
10 = MAT_METAL3 ( METAL and METAL2 chunk combo )
11 = MAT_CRATE1 ( yellow multi - colored crate chunks )
12 = MAT_GRATE1 ( grate chunks - - looks horrible right now )
13 = MAT_ROPE ( for yavin_trial , no chunks , just wispy bits )
14 = MAT_CRATE2 ( red multi - colored crate chunks )
15 = MAT_WHITE_METAL ( white angular chunks for Stu , NS_hideout )
*/
void SP_func_breakable ( gentity_t * self )
{
if ( ! ( self - > spawnflags & 1 ) )
{
if ( ! self - > health )
{
self - > health = 10 ;
}
}
if ( self - > spawnflags & 16 ) // saber only
{
self - > flags | = FL_DMG_BY_SABER_ONLY ;
}
else if ( self - > spawnflags & 32 ) // heavy weap
{
self - > flags | = FL_DMG_BY_HEAVY_WEAP_ONLY ;
}
if ( self - > health )
{
self - > takedamage = qtrue ;
}
G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ; //precaching
G_SpawnFloat ( " radius " , " 1 " , & self - > radius ) ; // used to scale chunk code if desired by a designer
G_SpawnInt ( " material " , " 0 " , ( int * ) & self - > material ) ;
CacheChunkEffects ( self - > material ) ;
self - > e_UseFunc = useF_funcBBrushUse ;
//if ( self->paintarget )
{
self - > e_PainFunc = painF_funcBBrushPain ;
}
self - > e_TouchFunc = touchF_funcBBrushTouch ;
if ( self - > team & & self - > team [ 0 ] )
{
self - > noDamageTeam = ( team_t ) GetIDForString ( TeamTable , self - > team ) ;
if ( self - > noDamageTeam = = TEAM_FREE )
{
G_Error ( " team name %s not recognized \n " , self - > team ) ;
}
}
self - > team = NULL ;
if ( ! self - > model ) {
G_Error ( " func_breakable with NULL model \n " ) ;
}
InitBBrush ( self ) ;
char buffer [ MAX_QPATH ] ;
char * s ;
if ( G_SpawnString ( " noise " , " *NOSOUND* " , & s ) )
{
Q_strncpyz ( buffer , s , sizeof ( buffer ) ) ;
COM_DefaultExtension ( buffer , sizeof ( buffer ) , " .wav " ) ;
self - > noise_index = G_SoundIndex ( buffer ) ;
}
int forceVisible = 0 ;
G_SpawnInt ( " forcevisible " , " 0 " , & forceVisible ) ;
if ( forceVisible )
{ //can see these through walls with force sight, so must be broadcast
if ( VectorCompare ( self - > s . origin , vec3_origin ) )
{ //no origin brush
self - > svFlags | = SVF_BROADCAST ;
}
self - > s . eFlags | = EF_FORCE_VISIBLE ;
}
int redCrosshair = 0 ;
G_SpawnInt ( " redCrosshair " , " 0 " , & redCrosshair ) ;
if ( redCrosshair )
{ //can see these through walls with force sight, so must be broadcast
self - > flags | = FL_RED_CROSSHAIR ;
}
}
void misc_model_breakable_pain ( gentity_t * self , gentity_t * inflictor , gentity_t * other , const vec3_t point , int damage , int mod , int hitLoc )
{
if ( self - > health > 0 )
{
// still alive, react to the pain
if ( self - > paintarget )
{
G_UseTargets2 ( self , self - > activator , self - > paintarget ) ;
}
// Don't do script if dead
G_ActivateBehavior ( self , BSET_PAIN ) ;
}
}
void misc_model_breakable_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int meansOfDeath , int dFlags , int hitLoc )
{
int numChunks ;
float size = 0 , scale ;
vec3_t dir , up , dis ;
if ( self - > e_DieFunc = = dieF_NULL ) //i was probably already killed since my die func was removed
{
# ifndef FINAL_BUILD
gi . Printf ( S_COLOR_YELLOW " Recursive misc_model_breakable_die. Use targets probably pointing back at self. \n " ) ;
# endif
return ; //this happens when you have a cyclic target chain!
}
//NOTE: Stop any scripts that are currently running (FLUSH)... ?
//Turn off animation
self - > s . frame = self - > startFrame = self - > endFrame = 0 ;
self - > svFlags & = ~ SVF_ANIMATING ;
self - > health = 0 ;
//Throw some chunks
AngleVectors ( self - > s . apos . trBase , dir , NULL , NULL ) ;
VectorNormalize ( dir ) ;
numChunks = random ( ) * 6 + 20 ;
VectorSubtract ( self - > absmax , self - > absmin , dis ) ;
// This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted.
// Volume is length * width * height...then break that volume down based on how many chunks we have
scale = sqrt ( sqrt ( dis [ 0 ] * dis [ 1 ] * dis [ 2 ] ) ) * 1.75f ;
if ( scale > 48 )
{
size = 2 ;
}
else if ( scale > 24 )
{
size = 1 ;
}
scale = scale / numChunks ;
if ( self - > radius > 0.0f )
{
// designer wants to scale number of chunks, helpful because the above scale code is far from perfect
// I do this after the scale calculation because it seems that the chunk size generally seems to be very close, it's just the number of chunks is a bit weak
numChunks * = self - > radius ;
}
VectorAdd ( self - > absmax , self - > absmin , dis ) ;
VectorScale ( dis , 0.5f , dis ) ;
CG_Chunks ( self - > s . number , dis , dir , self - > absmin , self - > absmax , 300 , numChunks , self - > material , self - > s . modelindex3 , scale ) ;
self - > e_PainFunc = painF_NULL ;
self - > e_DieFunc = dieF_NULL ;
// self->e_UseFunc = useF_NULL;
self - > takedamage = qfalse ;
if ( ! ( self - > spawnflags & 4 ) )
{ //We don't want to stay solid
self - > s . solid = 0 ;
self - > contents = 0 ;
self - > clipmask = 0 ;
if ( self ! = 0 )
{
NAV : : WayEdgesNowClear ( self ) ;
}
gi . linkentity ( self ) ;
}
VectorSet ( up , 0 , 0 , 1 ) ;
if ( self - > target )
{
G_UseTargets ( self , attacker ) ;
}
if ( inflictor - > client )
{
VectorSubtract ( self - > currentOrigin , inflictor - > currentOrigin , dir ) ;
VectorNormalize ( dir ) ;
}
else
{
VectorCopy ( up , dir ) ;
}
if ( ! ( self - > spawnflags & 2048 ) ) // NO_EXPLOSION
{
// Ok, we are allowed to explode, so do it now!
if ( self - > splashDamage > 0 & & self - > splashRadius > 0 )
{ //explode
vec3_t org ;
AddSightEvent ( attacker , self - > currentOrigin , 256 , AEL_DISCOVERED , 100 ) ;
AddSoundEvent ( attacker , self - > currentOrigin , 128 , AEL_DISCOVERED , qfalse , qtrue ) ; //FIXME: am I on ground or not?
//FIXME: specify type of explosion? (barrel, electrical, etc.) Also, maybe just use the explosion effect below since it's
// a bit better?
// up the origin a little for the damage check, because several models have their origin on the ground, so they don't alwasy do damage, not the optimal solution...
VectorCopy ( self - > currentOrigin , org ) ;
if ( self - > mins [ 2 ] > - 4 )
{ //origin is going to be below it or very very low in the model
//center the origin
org [ 2 ] = self - > currentOrigin [ 2 ] + self - > mins [ 2 ] + ( self - > maxs [ 2 ] - self - > mins [ 2 ] ) / 2.0f ;
}
G_RadiusDamage ( org , self , self - > splashDamage , self - > splashRadius , self , MOD_UNKNOWN ) ;
if ( self - > model & & ( Q_stricmp ( " models/map_objects/ships/tie_fighter.md3 " , self - > model ) = = 0 | |
Q_stricmp ( " models/map_objects/ships/tie_bomber.md3 " , self - > model ) = = 0 ) )
{ //TEMP HACK for Tie Fighters- they're HUGE
G_PlayEffect ( " explosions/fighter_explosion2 " , self - > currentOrigin ) ;
G_Sound ( self , G_SoundIndex ( " sound/weapons/tie_fighter/TIEexplode.wav " ) ) ;
self - > s . loopSound = 0 ;
}
else
{
CG_MiscModelExplosion ( self - > absmin , self - > absmax , size , self - > material ) ;
G_Sound ( self , G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ) ;
self - > s . loopSound = 0 ;
}
}
else
{ //just break
AddSightEvent ( attacker , self - > currentOrigin , 128 , AEL_DISCOVERED ) ;
AddSoundEvent ( attacker , self - > currentOrigin , 64 , AEL_SUSPICIOUS , qfalse , qtrue ) ; //FIXME: am I on ground or not?
// This is the default explosion
CG_MiscModelExplosion ( self - > absmin , self - > absmax , size , self - > material ) ;
G_Sound ( self , G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ) ;
}
}
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
if ( self - > s . modelindex2 ! = - 1 & & ! ( self - > spawnflags & 8 ) )
{ //FIXME: modelindex doesn't get set to -1 if the damage model doesn't exist
self - > svFlags | = SVF_BROKEN ;
self - > s . modelindex = self - > s . modelindex2 ;
G_ActivateBehavior ( self , BSET_DEATH ) ;
}
else
{
G_FreeEntity ( self ) ;
}
}
void misc_model_throw_at_target4 ( gentity_t * self , gentity_t * activator )
{
vec3_t pushDir , kvel ;
float knockback = 200 ;
float mass = self - > mass ;
gentity_t * target = G_Find ( NULL , FOFS ( targetname ) , self - > target4 ) ;
if ( ! target )
{ //nothing to throw ourselves at...
return ;
}
VectorSubtract ( target - > currentOrigin , self - > currentOrigin , pushDir ) ;
knockback - = VectorNormalize ( pushDir ) ;
if ( knockback < 100 )
{
knockback = 100 ;
}
VectorCopy ( self - > currentOrigin , self - > s . pos . trBase ) ;
self - > s . pos . trTime = level . time ; // move a bit on the very first frame
if ( self - > s . pos . trType ! = TR_INTERPOLATE )
{ //don't do this to rolling missiles
self - > s . pos . trType = TR_GRAVITY ;
}
if ( mass < 50 )
{ //???
mass = 50 ;
}
if ( g_gravity - > value > 0 )
{
VectorScale ( pushDir , g_knockback - > value * knockback / mass * 0.8 , kvel ) ;
kvel [ 2 ] = pushDir [ 2 ] * g_knockback - > value * knockback / mass * 1.5 ;
}
else
{
VectorScale ( pushDir , g_knockback - > value * knockback / mass , kvel ) ;
}
VectorAdd ( self - > s . pos . trDelta , kvel , self - > s . pos . trDelta ) ;
if ( g_gravity - > value > 0 )
{
if ( self - > s . pos . trDelta [ 2 ] < knockback )
{
self - > s . pos . trDelta [ 2 ] = knockback ;
}
}
//no trDuration?
if ( self - > e_ThinkFunc ! = thinkF_G_RunObject )
{ //objects spin themselves?
//spin it
//FIXME: messing with roll ruins the rotational center???
self - > s . apos . trTime = level . time ;
self - > s . apos . trType = TR_LINEAR ;
VectorClear ( self - > s . apos . trDelta ) ;
self - > s . apos . trDelta [ 1 ] = Q_irand ( - 800 , 800 ) ;
}
self - > forcePushTime = level . time + 600 ; // let the push effect last for 600 ms
if ( activator )
{
self - > forcePuller = activator - > s . number ; //remember this regardless
}
else
{
self - > forcePuller = NULL ;
}
}
void misc_model_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( self - > target4 )
{ //throw me at my target!
misc_model_throw_at_target4 ( self , activator ) ;
return ;
}
if ( self - > health < = 0 & & self - > max_health > 0 )
{ //used while broken fired target3
G_UseTargets2 ( self , activator , self - > target3 ) ;
return ;
}
// Become solid again.
if ( ! self - > count )
{
self - > count = 1 ;
self - > activator = activator ;
self - > svFlags & = ~ SVF_NOCLIENT ;
self - > s . eFlags & = ~ EF_NODRAW ;
}
G_ActivateBehavior ( self , BSET_USE ) ;
//Don't explode if they've requested it to not
if ( self - > spawnflags & 64 )
{ //Usemodels toggling
if ( self - > spawnflags & 32 )
{
if ( self - > s . modelindex = = self - > sound1to2 )
{
self - > s . modelindex = self - > sound2to1 ;
}
else
{
self - > s . modelindex = self - > sound1to2 ;
}
}
return ;
}
self - > e_DieFunc = dieF_misc_model_breakable_die ;
misc_model_breakable_die ( self , other , activator , self - > health , MOD_UNKNOWN ) ;
}
# define MDL_OTHER 0
# define MDL_ARMOR_HEALTH 1
# define MDL_AMMO 2
void misc_model_breakable_init ( gentity_t * ent )
{
int type ;
type = MDL_OTHER ;
if ( ! ent - > model ) {
G_Error ( " no model set on %s at (%.1f %.1f %.1f) \n " , ent - > classname , ent - > s . origin [ 0 ] , ent - > s . origin [ 1 ] , ent - > s . origin [ 2 ] ) ;
}
//Main model
ent - > s . modelindex = ent - > sound2to1 = G_ModelIndex ( ent - > model ) ;
if ( ent - > spawnflags & 1 )
{ //Blocks movement
ent - > contents = CONTENTS_SOLID | CONTENTS_OPAQUE | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ; //Was CONTENTS_SOLID, but only architecture should be this
}
else if ( ent - > health )
{ //Can only be shot
ent - > contents = CONTENTS_SHOTCLIP ;
}
if ( type = = MDL_OTHER )
{
ent - > e_UseFunc = useF_misc_model_use ;
}
else if ( type = = MDL_ARMOR_HEALTH )
{
// G_SoundIndex("sound/player/suithealth.wav");
ent - > e_UseFunc = useF_health_use ;
if ( ! ent - > count )
{
ent - > count = 100 ;
}
ent - > health = 60 ;
}
else if ( type = = MDL_AMMO )
{
// G_SoundIndex("sound/player/suitenergy.wav");
ent - > e_UseFunc = useF_ammo_use ;
if ( ! ent - > count )
{
ent - > count = 100 ;
}
ent - > health = 60 ;
}
if ( ent - > health )
{
G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ;
ent - > max_health = ent - > health ;
ent - > takedamage = qtrue ;
ent - > e_PainFunc = painF_misc_model_breakable_pain ;
ent - > e_DieFunc = dieF_misc_model_breakable_die ;
}
}
void TieFighterThink ( gentity_t * self )
{
gentity_t * player = & g_entities [ 0 ] ;
if ( self - > health < = 0 )
{
return ;
}
self - > nextthink = level . time + FRAMETIME ;
if ( player )
{
vec3_t playerDir , fighterDir , fwd , rt ;
float playerDist , fighterSpeed ;
//use player eyepoint
VectorSubtract ( player - > currentOrigin , self - > currentOrigin , playerDir ) ;
playerDist = VectorNormalize ( playerDir ) ;
VectorSubtract ( self - > currentOrigin , self - > lastOrigin , fighterDir ) ;
VectorCopy ( self - > currentOrigin , self - > lastOrigin ) ;
fighterSpeed = VectorNormalize ( fighterDir ) * 1000 ;
AngleVectors ( self - > currentAngles , fwd , rt , NULL ) ;
if ( fighterSpeed )
{
float side ;
// Magic number fun! Speed is used for banking, so modulate the speed by a sine wave
fighterSpeed * = sin ( ( 100 ) * 0.003 ) ;
// Clamp to prevent harsh rolling
if ( fighterSpeed > 10 )
fighterSpeed = 10 ;
side = fighterSpeed * DotProduct ( fighterDir , rt ) ;
self - > s . apos . trBase [ 2 ] - = side ;
}
//FIXME: bob up/down, strafe left/right some
float dot = DotProduct ( playerDir , fighterDir ) ;
if ( dot > 0 )
{ //heading toward the player
if ( playerDist < 1024 )
{
if ( DotProduct ( playerDir , fwd ) > 0.7 )
{ //facing the player
if ( self - > attackDebounceTime < level . time )
{
gentity_t * bolt ;
bolt = G_Spawn ( ) ;
bolt - > classname = " tie_proj " ;
bolt - > nextthink = level . time + 10000 ;
bolt - > e_ThinkFunc = thinkF_G_FreeEntity ;
bolt - > s . eType = ET_MISSILE ;
bolt - > s . weapon = WP_BLASTER ;
bolt - > owner = self ;
bolt - > damage = 30 ;
bolt - > dflags = DAMAGE_NO_KNOCKBACK ; // Don't push them around, or else we are constantly re-aiming
bolt - > splashDamage = 0 ;
bolt - > splashRadius = 0 ;
bolt - > methodOfDeath = MOD_ENERGY ; // ?
bolt - > clipmask = MASK_SHOT ;
bolt - > s . pos . trType = TR_LINEAR ;
bolt - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( self - > currentOrigin , bolt - > s . pos . trBase ) ;
VectorScale ( fwd , 8000 , bolt - > s . pos . trDelta ) ;
SnapVector ( bolt - > s . pos . trDelta ) ; // save net bandwidth
VectorCopy ( self - > currentOrigin , bolt - > currentOrigin ) ;
if ( ! Q_irand ( 0 , 2 ) )
{
G_SoundOnEnt ( bolt , CHAN_VOICE , " sound/weapons/tie_fighter/tie_fire.wav " ) ;
}
else
{
G_SoundOnEnt ( bolt , CHAN_VOICE , va ( " sound/weapons/tie_fighter/tie_fire%d.wav " , Q_irand ( 2 , 3 ) ) ) ;
}
self - > attackDebounceTime = level . time + Q_irand ( 300 , 2000 ) ;
}
}
}
}
if ( playerDist < 1024 ) //512 )
{ //within range to start our sound
if ( dot > 0 )
{
if ( ! self - > fly_sound_debounce_time )
{ //start sound
G_SoundOnEnt ( self , CHAN_VOICE , va ( " sound/weapons/tie_fighter/tiepass%d.wav " , Q_irand ( 1 , 5 ) ) ) ;
self - > fly_sound_debounce_time = 2000 ;
}
else
{ //sound already started
self - > fly_sound_debounce_time = - 1 ;
}
}
}
else if ( self - > fly_sound_debounce_time < level . time )
{
self - > fly_sound_debounce_time = 0 ;
}
}
}
void TieFighterUse ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( ! self | | ! other | | ! activator )
return ;
vec3_t fwd , rt ;
AngleVectors ( self - > currentAngles , fwd , rt , NULL ) ;
gentity_t * bolt ;
bolt = G_Spawn ( ) ;
bolt - > classname = " tie_proj " ;
bolt - > nextthink = level . time + 10000 ;
bolt - > e_ThinkFunc = thinkF_G_FreeEntity ;
bolt - > s . eType = ET_MISSILE ;
bolt - > s . weapon = WP_TIE_FIGHTER ;
bolt - > owner = self ;
bolt - > damage = 30 ;
bolt - > dflags = DAMAGE_NO_KNOCKBACK ; // Don't push them around, or else we are constantly re-aiming
bolt - > splashDamage = 0 ;
bolt - > splashRadius = 0 ;
bolt - > methodOfDeath = MOD_ENERGY ; // ?
bolt - > clipmask = MASK_SHOT ;
bolt - > s . pos . trType = TR_LINEAR ;
bolt - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( self - > currentOrigin , bolt - > s . pos . trBase ) ;
rt [ 2 ] + = 2.0f ;
VectorMA ( bolt - > s . pos . trBase , - 15.0 , rt , bolt - > s . pos . trBase ) ;
VectorScale ( fwd , 3000 , bolt - > s . pos . trDelta ) ;
SnapVector ( bolt - > s . pos . trDelta ) ; // save net bandwidth
VectorCopy ( self - > currentOrigin , bolt - > currentOrigin ) ;
bolt = G_Spawn ( ) ;
bolt - > classname = " tie_proj " ;
bolt - > nextthink = level . time + 10000 ;
bolt - > e_ThinkFunc = thinkF_G_FreeEntity ;
bolt - > s . eType = ET_MISSILE ;
bolt - > s . weapon = WP_TIE_FIGHTER ;
bolt - > owner = self ;
bolt - > damage = 30 ;
bolt - > dflags = DAMAGE_NO_KNOCKBACK ; // Don't push them around, or else we are constantly re-aiming
bolt - > splashDamage = 0 ;
bolt - > splashRadius = 0 ;
bolt - > methodOfDeath = MOD_ENERGY ; // ?
bolt - > clipmask = MASK_SHOT ;
bolt - > s . pos . trType = TR_LINEAR ;
bolt - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( self - > currentOrigin , bolt - > s . pos . trBase ) ;
rt [ 2 ] - = 4.0f ;
VectorMA ( bolt - > s . pos . trBase , 15.0 , rt , bolt - > s . pos . trBase ) ;
VectorScale ( fwd , 3000 , bolt - > s . pos . trDelta ) ;
SnapVector ( bolt - > s . pos . trDelta ) ; // save net bandwidth
VectorCopy ( self - > currentOrigin , bolt - > currentOrigin ) ;
}
void TouchTieBomb ( gentity_t * self , gentity_t * other , trace_t * trace )
{
// Stop the effect.
G_StopEffect ( G_EffectIndex ( " ships/tiebomber_bomb_falling " ) , self - > playerModel , gi . G2API_AddBolt ( & self - > ghoul2 [ 0 ] , " model_root " ) , self - > s . number ) ;
self - > e_ThinkFunc = thinkF_G_FreeEntity ;
self - > nextthink = level . time + FRAMETIME ;
G_PlayEffect ( G_EffectIndex ( " ships/tiebomber_explosion2 " ) , self - > currentOrigin , self - > currentAngles ) ;
G_RadiusDamage ( self - > currentOrigin , self , 900 , 500 , self , MOD_EXPLOSIVE_SPLASH ) ;
}
# define MIN_PLAYER_DIST 1600
void TieBomberThink ( gentity_t * self )
{
// NOTE: Lerp2Angles will think this thinkfunc if the model is a misc_model_breakable. Watchout
// for that in a script (try to just use ROFF's?).
// Stop thinking, you're dead.
if ( self - > health < = 0 )
{
return ;
}
// Needed every think...
self - > nextthink = level . time + FRAMETIME ;
gentity_t * player = & g_entities [ 0 ] ;
vec3_t playerDir ;
float playerDist ;
//use player eyepoint
VectorSubtract ( player - > currentOrigin , self - > currentOrigin , playerDir ) ;
playerDist = VectorNormalize ( playerDir ) ;
// Time to attack?
if ( player - > health > 0 & & playerDist < MIN_PLAYER_DIST & & self - > attackDebounceTime < level . time )
{
char name1 [ 200 ] = " models/players/remote/model.glm " ;
gentity_t * bomb = G_CreateObject ( self , self - > s . pos . trBase , self - > s . apos . trBase , 0 , 0 , TR_GRAVITY , 0 ) ;
bomb - > s . modelindex = G_ModelIndex ( name1 ) ;
gi . G2API_InitGhoul2Model ( bomb - > ghoul2 , name1 , bomb - > s . modelindex ) ;
bomb - > s . radius = 50 ;
bomb - > s . eFlags | = EF_NODRAW ;
// Make the bombs go forward in the bombers direction a little.
vec3_t fwd , rt ;
AngleVectors ( self - > currentAngles , fwd , rt , NULL ) ;
rt [ 2 ] - = 0.5f ;
VectorMA ( bomb - > s . pos . trBase , - 30.0 , rt , bomb - > s . pos . trBase ) ;
VectorScale ( fwd , 300 , bomb - > s . pos . trDelta ) ;
SnapVector ( bomb - > s . pos . trDelta ) ; // save net bandwidth
// Start the effect.
G_PlayEffect ( G_EffectIndex ( " ships/tiebomber_bomb_falling " ) , bomb - > playerModel , gi . G2API_AddBolt ( & bomb - > ghoul2 [ 0 ] , " model_root " ) , bomb - > s . number , bomb - > currentOrigin , 1000 , qtrue ) ;
// Set the tie bomb to have a touch function, so when it hits the ground (or whatever), there's a nice 'boom'.
bomb - > e_TouchFunc = touchF_TouchTieBomb ;
self - > attackDebounceTime = level . time + 1000 ;
}
}
void misc_model_breakable_gravity_init ( gentity_t * ent , qboolean dropToFloor )
{
//G_SoundIndex( "sound/movers/objects/objectHurt.wav" );
G_EffectIndex ( " melee/kick_impact " ) ;
G_EffectIndex ( " melee/kick_impact_silent " ) ;
//G_SoundIndex( "sound/weapons/melee/punch1.mp3" );
//G_SoundIndex( "sound/weapons/melee/punch2.mp3" );
//G_SoundIndex( "sound/weapons/melee/punch3.mp3" );
//G_SoundIndex( "sound/weapons/melee/punch4.mp3" );
G_SoundIndex ( " sound/movers/objects/objectHit.wav " ) ;
G_SoundIndex ( " sound/movers/objects/objectHitHeavy.wav " ) ;
G_SoundIndex ( " sound/movers/objects/objectBreak.wav " ) ;
//FIXME: dust impact effect when hits ground?
ent - > s . eType = ET_GENERAL ;
ent - > s . eFlags | = EF_BOUNCE_HALF ;
ent - > clipmask = MASK_SOLID | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ; //?
if ( ! ent - > mass )
{ //not overridden by designer
ent - > mass = VectorLength ( ent - > maxs ) + VectorLength ( ent - > mins ) ;
}
ent - > physicsBounce = ent - > mass ;
//drop to floor
trace_t tr ;
vec3_t top , bottom ;
if ( dropToFloor )
{
VectorCopy ( ent - > currentOrigin , top ) ;
top [ 2 ] + = 1 ;
VectorCopy ( ent - > currentOrigin , bottom ) ;
bottom [ 2 ] = MIN_WORLD_COORD ;
gi . trace ( & tr , top , ent - > mins , ent - > maxs , bottom , ent - > s . number , MASK_NPCSOLID ) ;
if ( ! tr . allsolid & & ! tr . startsolid & & tr . fraction < 1.0 )
{
G_SetOrigin ( ent , tr . endpos ) ;
gi . linkentity ( ent ) ;
}
}
else
{
G_SetOrigin ( ent , ent - > currentOrigin ) ;
gi . linkentity ( ent ) ;
}
//set up for object thinking
if ( VectorCompare ( ent - > s . pos . trDelta , vec3_origin ) )
{ //not moving
ent - > s . pos . trType = TR_STATIONARY ;
}
else
{
ent - > s . pos . trType = TR_GRAVITY ;
}
VectorCopy ( ent - > currentOrigin , ent - > s . pos . trBase ) ;
VectorClear ( ent - > s . pos . trDelta ) ;
ent - > s . pos . trTime = level . time ;
if ( VectorCompare ( ent - > s . apos . trDelta , vec3_origin ) )
{ //not moving
ent - > s . apos . trType = TR_STATIONARY ;
}
else
{
ent - > s . apos . trType = TR_LINEAR ;
}
VectorCopy ( ent - > currentAngles , ent - > s . apos . trBase ) ;
VectorClear ( ent - > s . apos . trDelta ) ;
ent - > s . apos . trTime = level . time ;
ent - > nextthink = level . time + FRAMETIME ;
ent - > e_ThinkFunc = thinkF_G_RunObject ;
}
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION START_OFF
SOLID - Movement is blocked by it , if not set , can still be broken by explosions and shots if it has health
AUTOANIMATE - Will cycle it ' s anim
DEADSOLID - Stay solid even when destroyed ( in case damage model is rather large ) .
NO_DMODEL - Makes it NOT display a damage model when destroyed , even if one exists
USE_MODEL - When used , will toggle to it ' s usemodel ( model + " _u1.md3 " ) . . . this obviously does nothing if USE_NOT_BREAK is not checked
USE_NOT_BREAK - Using it , doesn ' t make it break , still can be destroyed by damage
PLAYER_USE - Player can use it with the use button
NO_EXPLOSION - By default , will explode when it dies . . . this is your override .
START_OFF - Will start off and will not appear until used .
" model " arbitrary . md3 file to display
" modelscale " " x " uniform scale
" modelscale_vec " " x y z " scale model in each axis - height , width and length - bbox will scale with it
" health " how much health to have - default is zero ( not breakable ) If you don ' t set the SOLID flag , but give it health , it can be shot but will not block NPCs or players from moving
" targetname " when used , dies and displays damagemodel ( model + " _d1.md3 " ) , if any ( if not , removes itself )
" target " What to use when it dies
" target2 " What to use when it ' s repaired
" target3 " What to use when it ' s used while it ' s broken
" paintarget " target to fire when hit ( but not destroyed )
" count " the amount of armor / health / ammo given ( default 50 )
" radius " Chunk code tries to pick a good volume of chunks , but you can alter this to scale the number of spawned chunks . ( default 1 ) ( .5 ) is half as many chunks , ( 2 ) is twice as many chunks
" NPC_targetname " - Only the NPC with this name can damage this
" forcevisible " - When you turn on force sight ( any level ) , you can see these draw through the entire level . . .
" redCrosshair " - crosshair turns red when you look at this
" gravity " if set to 1 , this will be affected by gravity
" throwtarget " if set ( along with gravity ) , this thing , when used , will throw itself at the entity whose targetname matches this string
" mass " if gravity is on , this determines how much damage this thing does when it hits someone . Default is the size of the object from one corner to the other , that works very well . Only override if this is an object whose mass should be very high or low for it ' s size ( density )
Damage : default is none
" splashDamage " - damage to do ( will make it explode on death )
" splashRadius " - radius for above damage
" team " - This cannot take damage from members of this team :
" player "
" neutral "
" enemy "
" material " - default is " 8 - MAT_NONE " - choose from this list :
0 = MAT_METAL ( grey metal )
1 = MAT_GLASS
2 = MAT_ELECTRICAL ( sparks only )
3 = MAT_ELEC_METAL ( METAL chunks and sparks )
4 = MAT_DRK_STONE ( brown stone chunks )
5 = MAT_LT_STONE ( tan stone chunks )
6 = MAT_GLASS_METAL ( glass and METAL chunks )
7 = MAT_METAL2 ( blue / grey metal )
8 = MAT_NONE ( no chunks - DEFAULT )
9 = MAT_GREY_STONE ( grey colored stone )
10 = MAT_METAL3 ( METAL and METAL2 chunk combo )
11 = MAT_CRATE1 ( yellow multi - colored crate chunks )
12 = MAT_GRATE1 ( grate chunks - - looks horrible right now )
13 = MAT_ROPE ( for yavin_trial , no chunks , just wispy bits )
14 = MAT_CRATE2 ( red multi - colored crate chunks )
15 = MAT_WHITE_METAL ( white angular chunks for Stu , NS_hideout )
*/
void SP_misc_model_breakable ( gentity_t * ent )
{
char damageModel [ MAX_QPATH ] ;
char chunkModel [ MAX_QPATH ] ;
char useModel [ MAX_QPATH ] ;
int len ;
// Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this.
G_SpawnInt ( " material " , " 8 " , ( int * ) & ent - > material ) ;
G_SpawnFloat ( " radius " , " 1 " , & ent - > radius ) ; // used to scale chunk code if desired by a designer
qboolean bHasScale = G_SpawnVector ( " modelscale_vec " , " 0 0 0 " , ent - > s . modelScale ) ;
if ( ! bHasScale )
{
float temp ;
G_SpawnFloat ( " modelscale " , " 0 " , & temp ) ;
if ( temp ! = 0.0f )
{
ent - > s . modelScale [ 0 ] = ent - > s . modelScale [ 1 ] = ent - > s . modelScale [ 2 ] = temp ;
bHasScale = qtrue ;
}
}
CacheChunkEffects ( ent - > material ) ;
misc_model_breakable_init ( ent ) ;
len = strlen ( ent - > model ) - 4 ;
assert ( ent - > model [ len ] = = ' . ' ) ; //we're expecting ".md3"
strncpy ( damageModel , ent - > model , sizeof ( damageModel ) ) ;
damageModel [ len ] = 0 ; //chop extension
strncpy ( chunkModel , damageModel , sizeof ( chunkModel ) ) ;
strncpy ( useModel , damageModel , sizeof ( useModel ) ) ;
if ( ent - > takedamage ) {
//Dead/damaged model
if ( ! ( ent - > spawnflags & 8 ) ) { //no dmodel
strcat ( damageModel , " _d1.md3 " ) ;
ent - > s . modelindex2 = G_ModelIndex ( damageModel ) ;
}
//Chunk model
strcat ( chunkModel , " _c1.md3 " ) ;
ent - > s . modelindex3 = G_ModelIndex ( chunkModel ) ;
}
//Use model
if ( ent - > spawnflags & 32 ) { //has umodel
strcat ( useModel , " _u1.md3 " ) ;
ent - > sound1to2 = G_ModelIndex ( useModel ) ;
}
if ( ! ent - > mins [ 0 ] & & ! ent - > mins [ 1 ] & & ! ent - > mins [ 2 ] )
{
VectorSet ( ent - > mins , - 16 , - 16 , - 16 ) ;
}
if ( ! ent - > maxs [ 0 ] & & ! ent - > maxs [ 1 ] & & ! ent - > maxs [ 2 ] )
{
VectorSet ( ent - > maxs , 16 , 16 , 16 ) ;
}
// Scale up the tie-bomber bbox a little.
if ( ent - > model & & Q_stricmp ( " models/map_objects/ships/tie_bomber.md3 " , ent - > model ) = = 0 )
{
VectorSet ( ent - > mins , - 80 , - 80 , - 80 ) ;
VectorSet ( ent - > maxs , 80 , 80 , 80 ) ;
//ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] *= 2.0f;
//bHasScale = qtrue;
}
if ( bHasScale )
{
//scale the x axis of the bbox up.
ent - > maxs [ 0 ] * = ent - > s . modelScale [ 0 ] ; //*scaleFactor;
ent - > mins [ 0 ] * = ent - > s . modelScale [ 0 ] ; //*scaleFactor;
//scale the y axis of the bbox up.
ent - > maxs [ 1 ] * = ent - > s . modelScale [ 1 ] ; //*scaleFactor;
ent - > mins [ 1 ] * = ent - > s . modelScale [ 1 ] ; //*scaleFactor;
//scale the z axis of the bbox up and adjust origin accordingly
ent - > maxs [ 2 ] * = ent - > s . modelScale [ 2 ] ;
float oldMins2 = ent - > mins [ 2 ] ;
ent - > mins [ 2 ] * = ent - > s . modelScale [ 2 ] ;
ent - > s . origin [ 2 ] + = ( oldMins2 - ent - > mins [ 2 ] ) ;
}
if ( ent - > spawnflags & 2 )
{
ent - > s . eFlags | = EF_ANIM_ALLFAST ;
}
G_SetOrigin ( ent , ent - > s . origin ) ;
G_SetAngles ( ent , ent - > s . angles ) ;
gi . linkentity ( ent ) ;
if ( ent - > spawnflags & 128 )
{ //Can be used by the player's BUTTON_USE
ent - > svFlags | = SVF_PLAYER_USABLE ;
}
if ( ent - > team & & ent - > team [ 0 ] )
{
ent - > noDamageTeam = ( team_t ) GetIDForString ( TeamTable , ent - > team ) ;
if ( ent - > noDamageTeam = = TEAM_FREE )
{
G_Error ( " team name %s not recognized \n " , ent - > team ) ;
}
}
ent - > team = NULL ;
//HACK
if ( ent - > model & & Q_stricmp ( " models/map_objects/ships/x_wing_nogear.md3 " , ent - > model ) = = 0 )
{
if ( ent - > splashDamage > 0 & & ent - > splashRadius > 0 )
{
ent - > s . loopSound = G_SoundIndex ( " sound/vehicles/x-wing/loop.wav " ) ;
ent - > s . eFlags | = EF_LESS_ATTEN ;
}
}
else if ( ent - > model & & Q_stricmp ( " models/map_objects/ships/tie_fighter.md3 " , ent - > model ) = = 0 )
{ //run a think
G_EffectIndex ( " explosions/fighter_explosion2 " ) ;
G_SoundIndex ( " sound/weapons/tie_fighter/tiepass1.wav " ) ;
/* G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" );
G_SoundIndex ( " sound/weapons/tie_fighter/tiepass3.wav " ) ;
G_SoundIndex ( " sound/weapons/tie_fighter/tiepass4.wav " ) ;
G_SoundIndex ( " sound/weapons/tie_fighter/tiepass5.wav " ) ; */
G_SoundIndex ( " sound/weapons/tie_fighter/tie_fire.wav " ) ;
/* G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" );
G_SoundIndex ( " sound/weapons/tie_fighter/tie_fire3.wav " ) ; */
G_SoundIndex ( " sound/weapons/tie_fighter/TIEexplode.wav " ) ;
RegisterItem ( FindItemForWeapon ( WP_TIE_FIGHTER ) ) ;
ent - > s . eFlags | = EF_LESS_ATTEN ;
if ( ent - > splashDamage > 0 & & ent - > splashRadius > 0 )
{
ent - > s . loopSound = G_SoundIndex ( " sound/vehicles/tie-bomber/loop.wav " ) ;
//ent->e_ThinkFunc = thinkF_TieFighterThink;
//ent->e_UseFunc = thinkF_TieFighterThink;
//ent->nextthink = level.time + FRAMETIME;
ent - > e_UseFunc = useF_TieFighterUse ;
// Yeah, I could have just made this value changable from the editor, but I
// need it immediately!
float light ;
vec3_t color ;
qboolean lightSet , colorSet ;
// if the "color" or "light" keys are set, setup constantLight
lightSet = qtrue ; //G_SpawnFloat( "light", "100", &light );
light = 255 ;
//colorSet = "1 1 1"//G_SpawnVector( "color", "1 1 1", color );
colorSet = qtrue ;
color [ 0 ] = 1 ; color [ 1 ] = 1 ; color [ 2 ] = 1 ;
if ( lightSet | | colorSet )
{
int r , g , b , i ;
r = color [ 0 ] * 255 ;
if ( r > 255 ) {
r = 255 ;
}
g = color [ 1 ] * 255 ;
if ( g > 255 ) {
g = 255 ;
}
b = color [ 2 ] * 255 ;
if ( b > 255 ) {
b = 255 ;
}
i = light / 4 ;
if ( i > 255 ) {
i = 255 ;
}
ent - > s . constantLight = r | ( g < < 8 ) | ( b < < 16 ) | ( i < < 24 ) ;
}
}
}
else if ( ent - > model & & Q_stricmp ( " models/map_objects/ships/tie_bomber.md3 " , ent - > model ) = = 0 )
{
G_EffectIndex ( " ships/tiebomber_bomb_falling " ) ;
G_EffectIndex ( " ships/tiebomber_explosion2 " ) ;
G_EffectIndex ( " explosions/fighter_explosion2 " ) ;
G_SoundIndex ( " sound/weapons/tie_fighter/TIEexplode.wav " ) ;
ent - > e_ThinkFunc = thinkF_TieBomberThink ;
ent - > nextthink = level . time + FRAMETIME ;
ent - > attackDebounceTime = level . time + 1000 ;
// We only take damage from a heavy weapon class missiles.
ent - > flags | = FL_DMG_BY_HEAVY_WEAP_ONLY ;
ent - > s . loopSound = G_SoundIndex ( " sound/vehicles/tie-bomber/loop.wav " ) ;
ent - > s . eFlags | = EF_LESS_ATTEN ;
}
float grav = 0 ;
G_SpawnFloat ( " gravity " , " 0 " , & grav ) ;
if ( grav )
{ //affected by gravity
G_SetAngles ( ent , ent - > s . angles ) ;
G_SetOrigin ( ent , ent - > currentOrigin ) ;
G_SpawnString ( " throwtarget " , NULL , & ent - > target4 ) ; // used to throw itself at something
misc_model_breakable_gravity_init ( ent , qtrue ) ;
}
// Start off.
if ( ent - > spawnflags & 4096 )
{
ent - > spawnContents = ent - > contents ; // It Navs can temporarly turn it "on"
ent - > s . solid = 0 ;
ent - > contents = 0 ;
ent - > clipmask = 0 ;
ent - > svFlags | = SVF_NOCLIENT ;
ent - > s . eFlags | = EF_NODRAW ;
ent - > count = 0 ;
}
int forceVisible = 0 ;
G_SpawnInt ( " forcevisible " , " 0 " , & forceVisible ) ;
if ( forceVisible )
{ //can see these through walls with force sight, so must be broadcast
//ent->svFlags |= SVF_BROADCAST;
ent - > s . eFlags | = EF_FORCE_VISIBLE ;
}
int redCrosshair = 0 ;
G_SpawnInt ( " redCrosshair " , " 0 " , & redCrosshair ) ;
if ( redCrosshair )
{ //can see these through walls with force sight, so must be broadcast
ent - > flags | = FL_RED_CROSSHAIR ;
}
}
//----------------------------------
//
// Breaking Glass Technology
//
//----------------------------------
// Really naughty cheating. Put in an EVENT at some point...
extern void cgi_R_GetBModelVerts ( int bmodelIndex , vec3_t * verts , vec3_t normal ) ;
extern void CG_DoGlass ( vec3_t verts [ 4 ] , vec3_t normal , vec3_t dmgPt , vec3_t dmgDir , float dmgRadius ) ;
extern cgs_t cgs ;
//-----------------------------------------------------
void funcGlassDie ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
{
vec3_t verts [ 4 ] , normal ;
// if a missile is stuck to us, blow it up so we don't look dumb....we could, alternately, just let the missile drop off??
for ( int i = 0 ; i < MAX_GENTITIES ; i + + )
{
if ( g_entities [ i ] . s . groundEntityNum = = self - > s . number & & ( g_entities [ i ] . s . eFlags & EF_MISSILE_STICK ) )
{
G_Damage ( & g_entities [ i ] , self , self , NULL , NULL , 99999 , 0 , MOD_CRUSH ) ; //?? MOD?
}
}
// Really naughty cheating. Put in an EVENT at some point...
cgi_R_GetBModelVerts ( cgs . inlineDrawModel [ self - > s . modelindex ] , verts , normal ) ;
CG_DoGlass ( verts , normal , self - > pos1 , self - > pos2 , self - > splashRadius ) ;
self - > takedamage = qfalse ; //stop chain reaction runaway loops
G_SetEnemy ( self , self - > enemy ) ;
//NOTE: MUST do this BEFORE clearing contents, or you may not open the area portal!!!
gi . AdjustAreaPortalState ( self , qtrue ) ;
//So chunks don't get stuck inside me
self - > s . solid = 0 ;
self - > contents = 0 ;
self - > clipmask = 0 ;
gi . linkentity ( self ) ;
if ( self - > target & & attacker ! = NULL )
{
G_UseTargets ( self , attacker ) ;
}
G_FreeEntity ( self ) ;
}
//-----------------------------------------------------
void funcGlassUse ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
vec3_t temp1 , temp2 ;
// For now, we just break on use
G_ActivateBehavior ( self , BSET_USE ) ;
VectorAdd ( self - > mins , self - > maxs , temp1 ) ;
VectorScale ( temp1 , 0.5f , temp1 ) ;
VectorAdd ( other - > mins , other - > maxs , temp2 ) ;
VectorScale ( temp2 , 0.5f , temp2 ) ;
VectorSubtract ( temp1 , temp2 , self - > pos2 ) ;
VectorCopy ( temp1 , self - > pos1 ) ;
VectorNormalize ( self - > pos2 ) ;
VectorScale ( self - > pos2 , 390 , self - > pos2 ) ;
self - > splashRadius = 40 ; // ?? some random number, maybe it's ok?
funcGlassDie ( self , other , activator , self - > health , MOD_UNKNOWN ) ;
}
//-----------------------------------------------------
/*QUAKED func_glass (0 .8 .5) ? INVINCIBLE
When destroyed , fires it ' s target , breaks into glass chunks and plays glass noise
For now , instantly breaks on impact
INVINCIBLE - can only be broken by being used
" targetname " entities with matching target will fire it
" target " all entities with a matching targetname will be used when this is destroyed
" health " default is 1
*/
//-----------------------------------------------------
void SP_func_glass ( gentity_t * self )
{
if ( ! ( self - > spawnflags & 1 ) )
{
if ( ! self - > health )
{
self - > health = 1 ;
}
}
if ( self - > health )
{
self - > takedamage = qtrue ;
}
self - > e_UseFunc = useF_funcGlassUse ;
self - > e_DieFunc = dieF_funcGlassDie ;
VectorCopy ( self - > s . origin , self - > pos1 ) ;
gi . SetBrushModel ( self , self - > model ) ;
self - > svFlags | = ( SVF_GLASS_BRUSH | SVF_BBRUSH ) ;
self - > material = MAT_GLASS ;
self - > s . eType = ET_MOVER ;
self - > s . pos . trType = TR_STATIONARY ;
VectorCopy ( self - > pos1 , self - > s . pos . trBase ) ;
G_SoundIndex ( " sound/effects/glassbreak1.wav " ) ;
G_EffectIndex ( " misc/glass_impact " ) ;
gi . linkentity ( self ) ;
}
qboolean G_EntIsBreakable ( int entityNum , gentity_t * breaker )
{ //breakable brush/model that can actually be broken
if ( entityNum < 0 | | entityNum > = ENTITYNUM_WORLD )
{
return qfalse ;
}
gentity_t * ent = & g_entities [ entityNum ] ;
if ( ! ent - > takedamage )
{
return qfalse ;
}
if ( ent - > NPC_targetname )
{ //only a specific entity can break this!
if ( ! breaker
| | ! breaker - > targetname
| | Q_stricmp ( ent - > NPC_targetname , breaker - > targetname ) ! = 0 )
{ //I'm not the one who can break it
return qfalse ;
}
}
if ( ( ent - > svFlags & SVF_GLASS_BRUSH ) )
{
return qtrue ;
}
if ( ( ent - > svFlags & SVF_BBRUSH ) )
{
return qtrue ;
}
if ( ! Q_stricmp ( " misc_model_breakable " , ent - > classname ) )
{
return qtrue ;
}
if ( ! Q_stricmp ( " misc_maglock " , ent - > classname ) )
{
return qtrue ;
}
return qfalse ;
}