704 lines
21 KiB
C
704 lines
21 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* project : CGF (c) 1999 William van der Sterren */
|
|
/* parts (c) 1998 id software */
|
|
/* */
|
|
/* file : cgf_sfx_glass.cpp "special effects for glass entities" */
|
|
/* author(s): William van der Sterren */
|
|
/* version : 0.5 */
|
|
/* */
|
|
/* date (last revision): Jun 12, 99 */
|
|
/* date (creation) : Jun 04, 99 */
|
|
/* */
|
|
/* */
|
|
/* revision history */
|
|
/* -- date ---- | -- revision ---------------------- | -- revisor -- */
|
|
/* Jun 12, 1999 | fixed knife slash breaks glass | William */
|
|
/* Jun 08, 1999 | improved fragment limit | William */
|
|
/* */
|
|
/******* http://www.botepidemic.com/aid/cgf for CGF for Action Quake2 *******/
|
|
|
|
#ifdef __cplusplus
|
|
// VC++, for CGF
|
|
#include <cmath> // prevent problems between C and STL
|
|
extern "C"
|
|
{
|
|
#include "g_local.h"
|
|
#include "cgf_sfx_glass.h"
|
|
}
|
|
#else
|
|
// C, for other AQ2 variants
|
|
#include "g_local.h"
|
|
#include "cgf_sfx_glass.h"
|
|
#endif
|
|
|
|
|
|
// cvar for breaking glass
|
|
static cvar_t *breakableglass = 0;
|
|
|
|
// cvar for max glass fragment count
|
|
static cvar_t *glassfragmentlimit = 0;
|
|
|
|
static int glassfragmentcount = 0;
|
|
|
|
|
|
// additional functions - Q2 expects C calling convention
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
void CGF_SFX_TouchGlass(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
|
// called whenever an entity hits the trigger spawned for the glass
|
|
|
|
void CGF_SFX_EmitGlass (edict_t* aGlassPane, edict_t* anInflictor, vec3_t aPoint);
|
|
// emits glass fragments from aPoint, to show effects of firing thru window
|
|
|
|
void CGF_SFX_BreakGlass(edict_t* aGlassPane, edict_t* anOther, edict_t* anAttacker,
|
|
int aDamage, vec3_t aPoint, vec_t aPaneDestructDelay
|
|
);
|
|
// breaks glass
|
|
|
|
void CGF_SFX_InstallBreakableGlass(edict_t* aGlassPane);
|
|
// when working on a glass pane for the first time, just install trigger
|
|
// when working on a glass pane again (after a game ended), move
|
|
// glass back to original location
|
|
|
|
void CGF_SFX_HideBreakableGlass(edict_t* aGlassPane);
|
|
// after being broken, the pane cannot be removed as it is needed in
|
|
// subsequent missions/games, so hide it at about z = -1000
|
|
|
|
void CGF_SFX_ApplyGlassFragmentLimit(const char* aClassName);
|
|
// updates glassfragmentcount and removes oldest glass fragement if
|
|
// necessary to meet limit
|
|
|
|
void CGF_SFX_MiscGlassUse(edict_t *self, edict_t *other, edict_t *activator);
|
|
// catches use from unforeseen objects (weapons, debris,
|
|
// etc. touching the window)
|
|
|
|
void CGF_SFX_MiscGlassDie(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
|
// catches die calls caused by unforeseen objects (weapons, debris,
|
|
// etc. damaging the window)
|
|
|
|
void CGF_SFX_GlassThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin);
|
|
// variant of id software's ThrowDebris, now numbering the entity (for later removal)
|
|
|
|
extern // from a_game.c
|
|
edict_t *FindEdictByClassnum (char *classname, int classnum);
|
|
|
|
// declaration from g_misc.c
|
|
extern // from g_misc.c
|
|
void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
void CGF_SFX_InstallGlassSupport()
|
|
{
|
|
breakableglass = gi.cvar("breakableglass", "0", 0);
|
|
glassfragmentlimit = gi.cvar("glassfragmentlimit", "30", 0);
|
|
}
|
|
|
|
|
|
int CGF_SFX_IsBreakableGlassEnabled()
|
|
{
|
|
// returns whether breakable glass is enabled (cvar) and allowed (dm mode)
|
|
return breakableglass->value;
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think(edict_t* aPossibleGlassEntity)
|
|
{
|
|
// at level.time == 0.1 the entity has been introduced in the game,
|
|
// and we can use gi.pointcontents and gi.trace to check the entity
|
|
vec3_t origin;
|
|
int breakingglass;
|
|
trace_t trace;
|
|
|
|
// test for cvar
|
|
if (!CGF_SFX_IsBreakableGlassEnabled())
|
|
{
|
|
G_FreeEdict(aPossibleGlassEntity);
|
|
return;
|
|
}
|
|
|
|
VectorAdd(aPossibleGlassEntity->absmax, aPossibleGlassEntity->absmin, origin);
|
|
VectorScale(origin, 0.5, origin);
|
|
|
|
// detect glass (does not work for complex shapes,
|
|
// for example, the glass window near the satellite
|
|
// dish at Q2 base3
|
|
breakingglass = (gi.pointcontents(origin) & CONTENTS_TRANSLUCENT);
|
|
|
|
if (!breakingglass)
|
|
{
|
|
// test for complex brushes that happen to be
|
|
// hollow in their origin (for instance, the
|
|
// window at Q2 base3, near the satellite dish
|
|
trace = gi.trace(origin, vec3_origin, vec3_origin, aPossibleGlassEntity->absmax, 0,
|
|
MASK_PLAYERSOLID
|
|
);
|
|
breakingglass = ((trace.ent == aPossibleGlassEntity) &&
|
|
(trace.contents & CONTENTS_TRANSLUCENT)
|
|
);
|
|
trace = gi.trace(origin, vec3_origin, vec3_origin, aPossibleGlassEntity->absmin, 0,
|
|
MASK_PLAYERSOLID
|
|
);
|
|
breakingglass = ((breakingglass) ||
|
|
((trace.ent == aPossibleGlassEntity) &&
|
|
(trace.contents & CONTENTS_TRANSLUCENT)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (!breakingglass)
|
|
{
|
|
// do remove other func_explosives
|
|
G_FreeEdict (aPossibleGlassEntity);
|
|
return;
|
|
}
|
|
|
|
// discovered some glass - now make store the origin
|
|
// we need that after hiding the glass
|
|
VectorCopy(aPossibleGlassEntity->s.origin, aPossibleGlassEntity->pos1); // IMPORTANT!
|
|
|
|
// make a backup of the health in light_level
|
|
aPossibleGlassEntity->light_level = aPossibleGlassEntity->health;
|
|
|
|
// install the glass
|
|
CGF_SFX_InstallBreakableGlass(aPossibleGlassEntity);
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_InstallBreakableGlass(edict_t* aGlassPane)
|
|
{
|
|
// when working on a glass pane for the first time, just install trigger
|
|
// when working on a glass pane again (after a game ended), move
|
|
// glass back to original location
|
|
edict_t* trigger;
|
|
vec3_t maxs;
|
|
vec3_t mins;
|
|
|
|
// reset origin based on aGlassPane->pos1
|
|
VectorCopy(aGlassPane->pos1, aGlassPane->s.origin);
|
|
|
|
// reset health based on aGlassPane->light_level
|
|
aGlassPane->health = aGlassPane->light_level;
|
|
|
|
// replace die and use functions by glass specific ones
|
|
aGlassPane->die = CGF_SFX_MiscGlassDie;
|
|
aGlassPane->use = CGF_SFX_MiscGlassUse;
|
|
|
|
// reset some pane attributes
|
|
aGlassPane->takedamage = DAMAGE_YES;
|
|
aGlassPane->solid = SOLID_BSP;
|
|
aGlassPane->movetype = MOVETYPE_FLYMISSILE;
|
|
// for other movetypes, cannot move pane to hidden location and back
|
|
|
|
// try to establish size
|
|
VectorCopy(aGlassPane->maxs, maxs);
|
|
VectorCopy(aGlassPane->mins, mins);
|
|
|
|
// set up trigger, similar to triggers for doors
|
|
// but with a smaller box
|
|
mins[0] -= 24;
|
|
mins[1] -= 24;
|
|
mins[2] -= 24;
|
|
maxs[0] += 24;
|
|
maxs[1] += 24;
|
|
maxs[2] += 24;
|
|
|
|
// adjust some settings
|
|
trigger = G_Spawn ();
|
|
trigger->classname = "breakableglass_trigger";
|
|
VectorCopy (mins, trigger->mins);
|
|
VectorCopy (maxs, trigger->maxs);
|
|
trigger->owner = aGlassPane;
|
|
trigger->solid = SOLID_TRIGGER;
|
|
trigger->movetype = MOVETYPE_NONE;
|
|
trigger->touch = CGF_SFX_TouchGlass;
|
|
gi.linkentity (trigger);
|
|
}
|
|
|
|
|
|
void CGF_SFX_ShootBreakableGlass(edict_t* aGlassPane,
|
|
edict_t* anAttacker,
|
|
/*trace_t**/ void* tr,
|
|
int mod
|
|
)
|
|
{
|
|
// process gunshots thru glass
|
|
edict_t* trigger;
|
|
int destruct;
|
|
|
|
// depending on mod, destroy window or emit fragments
|
|
switch (mod)
|
|
{
|
|
// break for ap, shotgun, handcannon, and kick, destory window
|
|
case MOD_M3 :
|
|
case MOD_HC :
|
|
case MOD_SNIPER :
|
|
case MOD_KICK :
|
|
case MOD_GRENADE :
|
|
case MOD_G_SPLASH:
|
|
case MOD_HANDGRENADE:
|
|
case MOD_HG_SPLASH:
|
|
case MOD_KNIFE : // slash damage
|
|
destruct = true;
|
|
break;
|
|
default :
|
|
destruct = (rand() % 3 == 0);
|
|
break;
|
|
};
|
|
|
|
if (destruct)
|
|
{
|
|
// break glass (and hurt if doing kick)
|
|
CGF_SFX_BreakGlass(aGlassPane, anAttacker, 0, aGlassPane->health, vec3_origin, FRAMETIME);
|
|
if (mod == MOD_KICK)
|
|
{
|
|
vec3_t bloodorigin;
|
|
vec3_t dir;
|
|
vec3_t normal;
|
|
VectorAdd(aGlassPane->absmax, aGlassPane->absmin, bloodorigin);
|
|
VectorScale(bloodorigin, 0.5, bloodorigin);
|
|
VectorSubtract(bloodorigin, anAttacker->s.origin, dir);
|
|
VectorNormalize(dir);
|
|
VectorMA(anAttacker->s.origin, 32.0, dir, bloodorigin);
|
|
VectorSet(normal, 0, 0, -1);
|
|
T_Damage(anAttacker, aGlassPane, anAttacker, dir, bloodorigin, normal, 15.0, 0, 0, MOD_BREAKINGGLASS);
|
|
}
|
|
|
|
// remove corresponding trigger
|
|
trigger = 0;
|
|
while (trigger = G_Find(trigger, FOFS(classname), "breakableglass_trigger"))
|
|
{
|
|
if (trigger->owner == aGlassPane)
|
|
{
|
|
// remove it
|
|
G_FreeEdict(trigger);
|
|
// only one to be found
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// add decal (if not grenade)
|
|
if ( (mod != MOD_HANDGRENADE)
|
|
&& (mod != MOD_HG_SPLASH)
|
|
&& (mod != MOD_GRENADE)
|
|
&& (mod != MOD_G_SPLASH)
|
|
)
|
|
{
|
|
AddDecal(anAttacker, (trace_t*) tr);
|
|
}
|
|
// and emit glass
|
|
CGF_SFX_EmitGlass(aGlassPane, anAttacker, ((trace_t*) tr)->endpos);
|
|
}
|
|
}
|
|
|
|
|
|
void CGF_SFX_TouchGlass(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
// called whenever an entity hits the trigger spawned for the glass
|
|
|
|
vec3_t origin;
|
|
vec3_t normal;
|
|
vec3_t spot;
|
|
trace_t trace;
|
|
edict_t* glass;
|
|
vec3_t velocity;
|
|
vec_t speed;
|
|
vec_t projected_speed;
|
|
int is_hgrenade;
|
|
int is_knife;
|
|
|
|
is_hgrenade = is_knife = false;
|
|
|
|
// ignore non-clients-non-grenade-non-knife
|
|
if (!other->client)
|
|
{
|
|
is_knife = (0 == Q_stricmp("weapon_knife", other->classname));
|
|
if (!is_knife)
|
|
{
|
|
is_hgrenade = (0 == Q_stricmp("hgrenade", other->classname));
|
|
}
|
|
|
|
if ((!is_knife) && (!is_hgrenade))
|
|
return;
|
|
if (is_knife)
|
|
goto knife_and_grenade_handling;
|
|
}
|
|
|
|
// test whether other really hits the glass - deal with
|
|
// the special case that other hits some boundary close to the border of the glass pane
|
|
//
|
|
//
|
|
// ....trigger.......
|
|
// +++++++++ +++++++++++
|
|
// .+---glass------+.
|
|
// wall .+--------------+.wall
|
|
// +++++++++ +++++++++++
|
|
// ----->..................
|
|
// wrong ^ ^
|
|
// | |
|
|
// wrong ok
|
|
//
|
|
glass = self->owner;
|
|
// hack - set glass' movetype to MOVETYPE_PUSH as it is not
|
|
// moving as long as the trigger is active
|
|
glass->movetype = MOVETYPE_PUSH;
|
|
|
|
VectorAdd(glass->absmax, glass->absmin, origin);
|
|
VectorScale(origin, 0.5, origin);
|
|
|
|
// other needs to be able to trace to glass origin
|
|
trace = gi.trace(other->s.origin, vec3_origin, vec3_origin, origin, other,
|
|
MASK_PLAYERSOLID
|
|
);
|
|
if (trace.ent != glass)
|
|
return;
|
|
|
|
// we can reach the glass origin, so we have the normal of
|
|
// the glass plane
|
|
VectorCopy(trace.plane.normal, normal);
|
|
|
|
// we need to check if client is not running into wall next
|
|
// to the glass (the trigger stretches into the wall)
|
|
VectorScale(normal, -1000.0, spot);
|
|
VectorAdd(spot, other->s.origin, spot);
|
|
// line between other->s.origin and spot (perpendicular to glass
|
|
// surface should not hit wall but glass instead
|
|
trace = gi.trace(other->s.origin, vec3_origin, vec3_origin, spot, other,
|
|
MASK_PLAYERSOLID
|
|
);
|
|
if (trace.ent != glass)
|
|
return;
|
|
|
|
// now, we check if the client's speed perpendicular to
|
|
// the glass plane, exceeds the required 175
|
|
// (speed should be < -200, as the plane's normal
|
|
// points towards the client
|
|
VectorCopy(other->velocity, velocity);
|
|
speed = VectorNormalize(velocity);
|
|
projected_speed = speed * DotProduct(velocity, normal);
|
|
|
|
// bump projected speed for grenades - they should break
|
|
// the window more easily
|
|
if (is_hgrenade)
|
|
projected_speed *= 1.5;
|
|
|
|
// if hitting the glass with sufficient speed (project < -175),
|
|
// being jumpkicked (speed > 700, project < -5) break the window
|
|
if (!((projected_speed < -175.0) ||
|
|
((projected_speed < -5) && (speed > 700))
|
|
)
|
|
)
|
|
goto knife_and_grenade_handling;
|
|
|
|
// break glass
|
|
CGF_SFX_BreakGlass(glass, other, other, glass->health, vec3_origin, 3.0 * FRAMETIME);
|
|
// glass can take care of itself, but the trigger isn't needed anymore
|
|
G_FreeEdict (self);
|
|
|
|
/* not needed
|
|
// reduce momentum of the client (he just broke the window
|
|
// so he should lose speed. in addition, it doesn't feel
|
|
// right if he overtakes the glass fragments
|
|
// VectorScale(normal, 200.0, velocity);
|
|
// VectorAdd(other->velocity, velocity, other->velocity);
|
|
*/
|
|
|
|
// make sure client takes damage
|
|
T_Damage(other, glass, other, normal, other->s.origin, normal, 15.0, 0, 0, MOD_BREAKINGGLASS);
|
|
return;
|
|
|
|
// goto label
|
|
knife_and_grenade_handling:
|
|
// if knife or grenade, bounce them
|
|
if ((is_knife) || (is_hgrenade))
|
|
{
|
|
// change clipmask to bounce of glass
|
|
other->clipmask = MASK_SOLID;
|
|
}
|
|
}
|
|
|
|
|
|
void CGF_SFX_BreakGlass(edict_t* aGlassPane, edict_t* anInflictor, edict_t* anAttacker,
|
|
int aDamage, vec3_t aPoint, vec_t aPaneDestructDelay
|
|
)
|
|
{
|
|
// based on func_explode, but with lotsa subtle differences
|
|
vec3_t origin;
|
|
vec3_t old_origin;
|
|
vec3_t chunkorigin;
|
|
vec3_t size;
|
|
int count;
|
|
int mass;
|
|
|
|
// bmodel origins are (0 0 0), we need to adjust that here
|
|
VectorCopy(aGlassPane->s.origin, old_origin);
|
|
VectorScale (aGlassPane->size, 0.5, size);
|
|
VectorAdd (aGlassPane->absmin, size, origin);
|
|
VectorCopy (origin, aGlassPane->s.origin);
|
|
|
|
aGlassPane->takedamage = DAMAGE_NO;
|
|
|
|
VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin, aGlassPane->velocity);
|
|
VectorNormalize (aGlassPane->velocity);
|
|
// use speed 250 instead of 150 for funkier glass spray
|
|
VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity);
|
|
|
|
// start chunks towards the center
|
|
VectorScale (size, 0.75, size);
|
|
|
|
mass = aGlassPane->mass;
|
|
if (!mass)
|
|
mass = 75;
|
|
|
|
// big chunks
|
|
if (mass >= 100)
|
|
{
|
|
count = mass / 100;
|
|
if (count > 8)
|
|
count = 8;
|
|
while(count--)
|
|
{
|
|
CGF_SFX_ApplyGlassFragmentLimit("debris");
|
|
chunkorigin[0] = origin[0] + crandom() * size[0];
|
|
chunkorigin[1] = origin[1] + crandom() * size[1];
|
|
chunkorigin[2] = origin[2] + crandom() * size[2];
|
|
CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris1/tris.md2", 1, chunkorigin);
|
|
}
|
|
}
|
|
|
|
// small chunks
|
|
count = mass / 25;
|
|
if (count > 16)
|
|
count = 16;
|
|
while(count--)
|
|
{
|
|
CGF_SFX_ApplyGlassFragmentLimit("debris");
|
|
chunkorigin[0] = origin[0] + crandom() * size[0];
|
|
chunkorigin[1] = origin[1] + crandom() * size[1];
|
|
chunkorigin[2] = origin[2] + crandom() * size[2];
|
|
CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2", 2, chunkorigin);
|
|
}
|
|
|
|
// clear velocity, reset origin (that has been abused in ThrowDebris)
|
|
VectorClear(aGlassPane->velocity);
|
|
VectorCopy (old_origin, aGlassPane->s.origin);
|
|
|
|
if (anAttacker)
|
|
{
|
|
// jumping thru
|
|
G_UseTargets (aGlassPane, anAttacker);
|
|
}
|
|
else
|
|
{
|
|
// firing thru - the pane has no direct attacker to hurt,
|
|
// but G_UseTargets expects one. So make it a DIY
|
|
G_UseTargets (aGlassPane, aGlassPane);
|
|
}
|
|
|
|
// have glass plane be visible for two more frames,
|
|
// and have it self-destruct then
|
|
// meanwhile, make sure the player can move thru
|
|
aGlassPane->solid = SOLID_NOT;
|
|
aGlassPane->think = CGF_SFX_HideBreakableGlass;
|
|
aGlassPane->nextthink = level.time + aPaneDestructDelay;
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_EmitGlass (edict_t* aGlassPane, edict_t* anInflictor, vec3_t aPoint)
|
|
{
|
|
// based on func_explode, but with lotsa subtle differences
|
|
vec3_t old_origin;
|
|
vec3_t chunkorigin;
|
|
vec3_t size;
|
|
int count;
|
|
|
|
// bmodel origins are (0 0 0), we need to adjust that here
|
|
VectorCopy(aGlassPane->s.origin, old_origin);
|
|
VectorCopy (aPoint, aGlassPane->s.origin);
|
|
|
|
VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin, aGlassPane->velocity);
|
|
VectorNormalize (aGlassPane->velocity);
|
|
// use speed 250 instead of 150 for funkier glass spray
|
|
VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity);
|
|
|
|
// start chunks towards the center
|
|
VectorScale (aGlassPane->size, 0.25, size);
|
|
|
|
count = 4;
|
|
while(count--)
|
|
{
|
|
CGF_SFX_ApplyGlassFragmentLimit("debris");
|
|
chunkorigin[0] = aPoint[0] + crandom() * size[0];
|
|
chunkorigin[1] = aPoint[1] + crandom() * size[1];
|
|
chunkorigin[2] = aPoint[2] + crandom() * size[2];
|
|
CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2", 2, chunkorigin);
|
|
}
|
|
|
|
// clear velocity, reset origin (that has been abused in ThrowDebris)
|
|
VectorClear(aGlassPane->velocity);
|
|
VectorCopy (old_origin, aGlassPane->s.origin);
|
|
|
|
// firing thru - the pane has no direct attacker to hurt,
|
|
// but G_UseTargets expects one. So make it a DIY
|
|
G_UseTargets (aGlassPane, aGlassPane);
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_HideBreakableGlass(edict_t* aGlassPane)
|
|
{
|
|
// remove all attached decals
|
|
edict_t* decal;
|
|
decal = 0;
|
|
while (decal = G_Find(decal, FOFS(classname), "decal"))
|
|
{
|
|
if (decal->owner == aGlassPane)
|
|
{
|
|
// make it goaway in the next frame
|
|
decal->nextthink = level.time + FRAMETIME;
|
|
}
|
|
}
|
|
|
|
while (decal = G_Find(decal, FOFS(classname), "splat"))
|
|
{
|
|
if (decal->owner == aGlassPane)
|
|
{
|
|
// make it goaway in the next frame
|
|
decal->nextthink = level.time + FRAMETIME;
|
|
}
|
|
}
|
|
|
|
// after being broken, the pane cannot be freed as it is needed in
|
|
// subsequent missions/games, so hide it at about z = -1000 lower
|
|
aGlassPane->movetype = MOVETYPE_FLYMISSILE;
|
|
VectorCopy(aGlassPane->s.origin, aGlassPane->pos1);
|
|
aGlassPane->s.origin[2] -=1000.0;
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_AttachDecalToGlass(edict_t* aGlassPane, edict_t* aDecal)
|
|
{
|
|
// just set aDecal's owner to be the glass pane
|
|
aDecal->owner = aGlassPane;
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_RebuildAllBrokenGlass()
|
|
{
|
|
// iterate over all func_explosives
|
|
edict_t* glass;
|
|
glass = 0;
|
|
while (glass = G_Find(glass, FOFS(classname), "func_explosive"))
|
|
{
|
|
// glass is broken if solid != SOLID_BSP
|
|
if (glass->solid != SOLID_BSP)
|
|
{
|
|
CGF_SFX_InstallBreakableGlass(glass);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_ApplyGlassFragmentLimit(const char* aClassName)
|
|
{
|
|
edict_t* oldfragment;
|
|
|
|
glassfragmentcount++;
|
|
if (glassfragmentcount > glassfragmentlimit->value)
|
|
glassfragmentcount = 1;
|
|
|
|
// remove fragment with corresponding number if any
|
|
oldfragment = FindEdictByClassnum((char*) aClassName, glassfragmentcount);
|
|
|
|
if (oldfragment)
|
|
{
|
|
// oldfragment->nextthink = level.time + FRAMETIME;
|
|
G_FreeEdict(oldfragment);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_MiscGlassUse(edict_t *self, edict_t *other, edict_t *activator)
|
|
{
|
|
#ifdef _DEBUG
|
|
const char* classname;
|
|
classname = other->classname;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void CGF_SFX_MiscGlassDie(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
#ifdef _DEBUG
|
|
const char* classname;
|
|
classname = inflictor->classname;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
static vec_t previous_throw_time = 0;
|
|
static int this_throw_count = 0;
|
|
|
|
void CGF_SFX_GlassThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin)
|
|
{
|
|
// based on ThrowDebris from id software - now returns debris created
|
|
edict_t *chunk;
|
|
vec3_t v;
|
|
|
|
if (level.time != previous_throw_time)
|
|
{
|
|
previous_throw_time = level.time;
|
|
this_throw_count = 0;
|
|
}
|
|
else
|
|
{
|
|
this_throw_count++;
|
|
if (this_throw_count > glassfragmentlimit->value)
|
|
return;
|
|
}
|
|
|
|
chunk = G_Spawn();
|
|
VectorCopy (origin, chunk->s.origin);
|
|
gi.setmodel (chunk, modelname);
|
|
v[0] = 100 * crandom();
|
|
v[1] = 100 * crandom();
|
|
v[2] = 100 + 100 * crandom();
|
|
VectorMA (self->velocity, speed, v, chunk->velocity);
|
|
chunk->movetype = MOVETYPE_BOUNCE;
|
|
chunk->solid = SOLID_NOT;
|
|
chunk->avelocity[0] = random()*600;
|
|
chunk->avelocity[1] = random()*600;
|
|
chunk->avelocity[2] = random()*600;
|
|
chunk->think = G_FreeEdict;
|
|
chunk->nextthink = level.time + 5 + random()*5;
|
|
chunk->s.frame = 0;
|
|
chunk->flags = 0;
|
|
chunk->classname = "debris";
|
|
chunk->takedamage = DAMAGE_YES;
|
|
chunk->die = debris_die;
|
|
gi.linkentity (chunk);
|
|
|
|
// number chunk
|
|
chunk->classnum = glassfragmentcount;
|
|
}
|
|
|
|
|