stvoy-mp-sdk/Code-DM/game/g_breakable.c

503 lines
12 KiB
C
Raw Permalink Normal View History

2000-12-11 00:00:00 +00:00
#include "g_local.h"
void breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
{
vec3_t size, org, dir;
gentity_t *te = NULL;
self->s.frame = 0;
//self->r.svFlags &= ~SVF_ANIMATING;
self->health = 0;
self->pain = 0/*NULL*/;
self->die = 0/*NULL*/;
self->use = 0/*NULL*/;
self->takedamage = qfalse;
if ( !(self->spawnflags & 4) )
{//We don't want to stay solid
self->s.solid = 0;
self->r.contents = 0;
self->clipmask = 0;
trap_LinkEntity(self);
}
if( self->target )
{
G_UseTargets(self, attacker);
}
if ( self->r.bmodel )
{
VectorSubtract( self->r.absmax, self->r.absmin, size );
VectorMA( self->r.absmin, 0.5, size, org );
}
else
{
VectorSubtract( self->r.maxs, self->r.mins, size );
VectorCopy( self->r.currentOrigin, org );
}
// Create a chunk effect
te = G_TempEntity( org, EV_FX_CHUNKS );
VectorSet( te->s.angles2, 0, 0, 1 ); // FIXME: temp direction
te->s.time2 = VectorNormalize( size );
te->s.powerups = self->s.powerups;
// Ok, we are allowed to explode, so do it now!
if ( self->splashDamage > 0 && self->splashRadius > 0 )
{
//fixme: what about chain reactions?
G_RadiusDamage( org, attacker, self->splashDamage, self->splashRadius, self, DAMAGE_RADIUS|DAMAGE_ALL_TEAMS, MOD_UNKNOWN );
//explosion effect
te = G_TempEntity( org, EV_MISSILE_MISS );
VectorSet( dir, 0, 0, 1 );
te->s.eventParm = DirToByte( dir );
te->s.weapon = WP_GRENADE_LAUNCHER;
//G_Sound( self, G_SoundIndex("sound/weapons/explosions/cargoexplode.wav") );
}
if ( self->r.bmodel )
{
trap_AdjustAreaPortalState( self, qtrue );
}
else if ( self->s.modelindex2 != -1 && !(self->spawnflags & 8) )
{
self->s.modelindex = self->s.modelindex2;
return;
}
G_FreeEntity( self );
}
void breakable_pain ( gentity_t *self, gentity_t *attacker, int damage )
{
if ( self->pain_debounce_time > level.time )
{
return;
}
if ( self->paintarget )
{
G_UseTargets2 ( self, self->activator, self->paintarget );
}
if(self->wait == -1)
{
self->pain = 0/*NULL*/;
return;
}
self->pain_debounce_time = level.time + self->wait;
}
void breakable_use (gentity_t *self, gentity_t *other, gentity_t *activator)
{
breakable_die( self, other, activator, self->health, MOD_UNKNOWN );
}
static void InitBBrush ( gentity_t *ent )
{
float light;
vec3_t color;
qboolean lightSet, colorSet;
VectorCopy( ent->s.origin, ent->pos1 );
trap_SetBrushModel( ent, ent->model );
ent->die = breakable_die;
//ent->r.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 );
}
ent->s.eType = ET_MOVER;
trap_LinkEntity (ent);
ent->s.pos.trType = TR_STATIONARY;
VectorCopy( ent->pos1, ent->s.pos.trBase );
}
/*QUAKED func_breakable (0 .8 .5) ? x x x x INVINCIBLE x x x
INVINCIBLE - can only be broken by being used
When destroyed, fires it's trigger and explodes
"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
"model2" .md3 model to also draw
"target" all entities with a matching targetname will be used when this is destoryed
"health" default is 10
"team" - This cannot take damage from members of this team (2 = blue, 1 = red)
Damage: default is none
"splashDamage" - damage to do (will make it explode on death
"splashRadius" - radius for above damage
"material" - sets the chunk type:
0 - none (default)
1 - metal
2 - glass
3 - glass and metal
4 - wood
5 - stone
Don't know if these work:
"color" constantLight color
"light" constantLight radius
*/
void SP_func_breakable( gentity_t *self )
{
if(!(self->spawnflags & 1))
{
if(!self->health)
{
self->health = 10;
}
}
if (self->health)
{
self->takedamage = qtrue;
}
G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");//precaching
self->use = breakable_use;
if ( self->paintarget )
{
self->pain = breakable_pain;
}
if (!self->model) {
G_Error("func_breakable with NULL model\n");
}
InitBBrush( self );
}
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL INVINCIBLE x x x x
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
INVINCIBLE - Can only be broken by being used
"model" arbitrary .md3 file to display
"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, if any (if not, removes itself)
"target" What to use when it dies
"paintarget" target to fire when hit (but not destroyed)
"wait" how long minimum to wait between firing paintarget each time hit
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 (2 = blue, 1 = red)
"material" - sets the chunk type:
0 - none (default)
1 - metal
2 - glass
3 - glass and metal
4 - wood
5 - stone
FIXME/TODO:
set size better?
multiple damage models?
don't throw chunks on pain, or throw level 1 chunks only on pains?
custom explosion effect/sound?
*/
void SP_misc_model_breakable( gentity_t *ent )
{
char damageModel[MAX_QPATH];
int len;
//Main model
ent->s.modelindex = ent->sound2to1 = G_ModelIndex( ent->model );
if ( ent->spawnflags & 1 )
{//Blocks movement
ent->r.contents = CONTENTS_BODY;//Was CONTENTS_SOLID, but only architecture should be this
}
else if ( ent->health )
{//Can only be shot
ent->r.contents = CONTENTS_SHOTCLIP;
}
ent->use = breakable_use;
if ( ent->health )
{
G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
//ent->max_health = ent->health;
ent->takedamage = qtrue;
ent->pain = breakable_pain;
ent->die = breakable_die;
}
len = strlen( ent->model ) - 4;
strncpy( damageModel, ent->model, len );
damageModel[len] = 0; //chop extension
if (ent->takedamage) {
//Dead/damaged model
if( !(ent->spawnflags & 8) ) { //no dmodel
strcat( damageModel, "_d1.md3" );
ent->s.modelindex2 = G_ModelIndex( damageModel );
}
}
if ( !ent->r.mins[0] && !ent->r.mins[1] && !ent->r.mins[2] )
{
VectorSet (ent->r.mins, -16, -16, -16);
}
if ( !ent->r.maxs[0] && !ent->r.maxs[1] && !ent->r.maxs[2] )
{
VectorSet (ent->r.maxs, 16, 16, 16);
}
if ( ent->spawnflags & 2 )
{
ent->s.eFlags |= EF_ANIM_ALLFAST;
}
G_SetOrigin( ent, ent->s.origin );
VectorCopy( ent->s.angles, ent->s.apos.trBase );
trap_LinkEntity (ent);
}
//-------------------------------------------------------------------------------
// --------------------------------------------------------------------
//
// AMMO plugin functions
//
// --------------------------------------------------------------------
void ammo_use( gentity_t *self, gentity_t *other, gentity_t *activator);
int Add_Ammo2 (gentity_t *ent, int weapon, int count)
{
ent->client->ps.ammo[weapon] += count;
if ( ent->client->ps.ammo[weapon] > Max_Ammo[weapon] )
{
ent->client->ps.ammo[weapon] = Max_Ammo[weapon];
return qfalse;
}
return qtrue;
}
void ammo_shutdown( gentity_t *self )
{
if (!(self->s.eFlags & EF_ANIM_ONCE))
{
self->s.eFlags &= ~ EF_ANIM_ALLFAST;
self->s.eFlags |= EF_ANIM_ONCE;
trap_LinkEntity (self);
}
}
void ammo_think( gentity_t *ent )
{
int dif = 0;
int i;
// Still has ammo to give
if ( ent->enemy && ent->enemy->client )
{
for ( i = 0; i < WP_NUM_WEAPONS && ent->count > 0; i++ )
{//go through all weapons
if ( (ent->enemy->client->ps.stats[STAT_WEAPONS]&( 1 << i )) )
{//has this weapon
dif = Max_Ammo[i] - ent->enemy->client->ps.ammo[i];//needs ammo?
if (dif > 2 )
{
dif= 2;
}
else if (dif < 0)
{
dif= 0;
}
if (ent->count < dif) // Can't give more than count
{
dif = ent->count;
}
// Give player ammo
if (Add_Ammo2(ent->enemy,ent->enemy->client->ps.weapon,dif) && (dif!=0))
{
ent->count-=dif;
if ( ent->splashDamage )
{
ent->splashDamage = floor((float)ent->count/10);
}
ent->nextthink = level.time + 10;
}
else // User has taken all ammo he can hold
{
ent->use = ammo_use;
ent->think = 0;//qvm complains about using NULL
}
}
}
}
if (ent->count < 1)
{
ammo_shutdown(ent);
}
}
//------------------------------------------------------------
void ammo_use( gentity_t *self, gentity_t *other, gentity_t *activator)
{
int dif = 0;
int i;
if (self->think != NULL)
{
if (self->use != NULL)
{
self->think = 0; /*NULL*/
}
}
else
{
if ( other->client )
{
for ( i = 0; i < WP_NUM_WEAPONS && dif == 0; i++ )
{//go through all weapons
if ( (other->client->ps.stats[STAT_WEAPONS]&( 1 << i )) )
{//has this weapon
dif = Max_Ammo[i] - other->client->ps.ammo[i];//needs ammo?
}
}
}
else
{ // Being triggered to be used up
dif = 1;
self->count = 0;
}
// Does player already have full ammo?
if ( dif > 0 )
{
G_Sound(self, G_SoundIndex("sound/player/suitenergy.wav") );
if ((dif >= self->count) || (self->count<1)) // use it all up?
{
ammo_shutdown(self);
}
}
else
{
G_Sound(self, G_SoundIndex("sound/weapons/noammo.wav") );
}
// Use target when used
if (self->spawnflags & 8)
{
G_UseTargets( self, activator );
}
self->use = 0; /*NULL*/
self->enemy = other;
self->think = ammo_think;
self->nextthink = level.time + 50;
}
}
void ammo_station_finish_spawning ( gentity_t *self )
{
self->s.eFlags &= ~EF_ITEMPLACEHOLDER;
self->think = 0; /*NULL*/
self->nextthink = -1;
self->use = ammo_use;
}
//------------------------------------------------------------
/*QUAKED misc_ammo_station (1 0 0) (-16 -16 -16) (16 16 16)
"health" - how much health the model has - default 60 (zero makes non-breakable)
"target" - what to use when it dies
"paintarget" - target to fire when hit (but not destroyed)
"count" - the amount of health given when used (default 1000)
"team" - This cannot take damage from members of this team and only members of this team can use it (2 = blue, 1 = red)
*/
//------------------------------------------------------------
void SP_misc_ammo_station( gentity_t *ent )
{
if (!ent->health)
{
ent->health = 60;
}
if ( !ent->count )
{
ent->count = 1000;
}
ent->s.powerups = 6;//material
ent->s.modelindex = G_ModelIndex( "models/mapobjects/dn/powercell.md3" );
ent->r.contents = CONTENTS_CORPSE;
// Set a generic use function
ent->use = ammo_use;
G_SoundIndex( "sound/player/suitenergy.wav" );
if ( ent->health )
{
ent->takedamage = qtrue;
ent->pain = breakable_pain;
ent->die = breakable_die;
}
VectorSet( ent->r.mins, -16, -16, -16 );
VectorSet( ent->r.maxs, 16, 16, 16 );
G_SetOrigin( ent, ent->s.origin );
VectorCopy( ent->s.angles, ent->s.apos.trBase );
trap_LinkEntity (ent);
}