1452 lines
36 KiB
C
1452 lines
36 KiB
C
// Copyright (C) 2001-2002 Raven Software.
|
|
//
|
|
// cg_gore.c -- handle client-side gore
|
|
|
|
#include "cg_local.h"
|
|
#if !defined(GHOUL2_SHARED_H_INC)
|
|
#include "..\ghoul2\G2_gore_shared.h"
|
|
#endif
|
|
|
|
|
|
void CG_AddGore(int type, float size, vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
SSkinGoreData goreSkin;
|
|
|
|
memset ( &goreSkin, 0, sizeof(goreSkin) );
|
|
|
|
goreSkin.growDuration = -1; // default expandy time
|
|
goreSkin.goreScaleStartFraction = 1.0; // default start scale
|
|
goreSkin.frontFaces = qtrue; // forever
|
|
goreSkin.backFaces = qtrue; // forever
|
|
goreSkin.lifeTime = 0;
|
|
goreSkin.baseModelOnly = qfalse;
|
|
|
|
goreSkin.currentTime = cg.time;
|
|
goreSkin.entNum = entnum;
|
|
goreSkin.SSize = size;
|
|
goreSkin.TSize = size;
|
|
goreSkin.theta = flrand(0,6.28);
|
|
goreSkin.shaderEnum = type;
|
|
|
|
VectorSet ( goreSkin.scale, 1, 1, 1 );
|
|
|
|
VectorCopy ( hitdirection, goreSkin.rayDirection);
|
|
|
|
VectorCopy ( hitloc, goreSkin.hitLocation );
|
|
VectorCopy ( entposition, goreSkin.position );
|
|
goreSkin.angles[YAW] = entangle;
|
|
|
|
trap_G2API_AddSkinGore(ghoul2,&goreSkin);
|
|
}
|
|
|
|
void CG_AddGrowGore(int type, float size, int growtime, float startfrac, vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
SSkinGoreData goreSkin;
|
|
|
|
memset ( &goreSkin, 0, sizeof(goreSkin) );
|
|
|
|
goreSkin.frontFaces = qtrue; // forever
|
|
goreSkin.backFaces = qtrue; // forever
|
|
goreSkin.lifeTime = 0;
|
|
goreSkin.baseModelOnly = qfalse;
|
|
|
|
goreSkin.currentTime = cg.time;
|
|
goreSkin.entNum = entnum;
|
|
goreSkin.SSize = size;
|
|
goreSkin.TSize = size;
|
|
goreSkin.theta = flrand(0,6.28);
|
|
goreSkin.shaderEnum = type;
|
|
goreSkin.growDuration = growtime; // default expandy time
|
|
goreSkin.goreScaleStartFraction = startfrac; // default start scale
|
|
|
|
VectorSet ( goreSkin.scale, 1, 1, 1 );
|
|
|
|
VectorCopy ( hitdirection, goreSkin.rayDirection);
|
|
|
|
VectorCopy ( hitloc, goreSkin.hitLocation );
|
|
VectorCopy ( entposition, goreSkin.position );
|
|
goreSkin.angles[YAW] = entangle;
|
|
|
|
trap_G2API_AddSkinGore(ghoul2,&goreSkin);
|
|
}
|
|
|
|
void CG_AddSlashGore(int type, float angle, float ssize, float tsize, vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
SSkinGoreData goreSkin;
|
|
|
|
memset ( &goreSkin, 0, sizeof(goreSkin) );
|
|
|
|
goreSkin.growDuration = -1; // default expandy time
|
|
goreSkin.goreScaleStartFraction = 1.0; // default start scale
|
|
goreSkin.frontFaces = qtrue; // forever
|
|
goreSkin.backFaces = qtrue; // forever
|
|
goreSkin.lifeTime = 0;
|
|
goreSkin.baseModelOnly = qfalse;
|
|
|
|
goreSkin.currentTime = cg.time;
|
|
goreSkin.entNum = entnum;
|
|
goreSkin.SSize = ssize;
|
|
goreSkin.TSize = tsize;
|
|
goreSkin.theta = angle;
|
|
goreSkin.shaderEnum = type;
|
|
|
|
VectorSet ( goreSkin.scale, 1, 1, 1 );
|
|
|
|
VectorCopy ( hitdirection, goreSkin.rayDirection);
|
|
|
|
VectorCopy ( hitloc, goreSkin.hitLocation );
|
|
VectorCopy ( entposition, goreSkin.position );
|
|
goreSkin.angles[YAW] = entangle;
|
|
|
|
trap_G2API_AddSkinGore(ghoul2,&goreSkin);
|
|
}
|
|
|
|
void CG_AddSlashGrowGore(int type, float angle, float ssize, float tsize, int growtime, float startfrac,
|
|
vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
SSkinGoreData goreSkin;
|
|
|
|
memset ( &goreSkin, 0, sizeof(goreSkin) );
|
|
|
|
goreSkin.frontFaces = qtrue; // forever
|
|
goreSkin.backFaces = qtrue; // forever
|
|
goreSkin.lifeTime = 0;
|
|
goreSkin.baseModelOnly = qfalse;
|
|
|
|
goreSkin.growDuration = growtime; // default expandy time
|
|
goreSkin.goreScaleStartFraction = startfrac; // default start scale
|
|
goreSkin.currentTime = cg.time;
|
|
goreSkin.entNum = entnum;
|
|
goreSkin.SSize = ssize;
|
|
goreSkin.TSize = tsize;
|
|
goreSkin.theta = angle;
|
|
goreSkin.shaderEnum = type;
|
|
|
|
VectorSet ( goreSkin.scale, 1, 1, 1 );
|
|
|
|
VectorCopy ( hitdirection, goreSkin.rayDirection);
|
|
|
|
VectorCopy ( hitloc, goreSkin.hitLocation );
|
|
VectorCopy ( entposition, goreSkin.position );
|
|
goreSkin.angles[YAW] = entangle;
|
|
|
|
trap_G2API_AddSkinGore(ghoul2,&goreSkin);
|
|
}
|
|
|
|
|
|
void CG_AddTimedGore(int type, float size, int duration, vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
SSkinGoreData goreSkin;
|
|
|
|
memset ( &goreSkin, 0, sizeof(goreSkin) );
|
|
|
|
goreSkin.growDuration = -1; // default expandy time
|
|
goreSkin.goreScaleStartFraction = 1.0; // default start scale
|
|
goreSkin.frontFaces = qtrue; // forever
|
|
goreSkin.backFaces = qtrue; // forever
|
|
goreSkin.baseModelOnly = qfalse;
|
|
|
|
goreSkin.currentTime = cg.time;
|
|
goreSkin.entNum = entnum;
|
|
goreSkin.SSize = size;
|
|
goreSkin.TSize = size;
|
|
goreSkin.theta = flrand(0,6.28);
|
|
goreSkin.shaderEnum = type;
|
|
goreSkin.lifeTime = duration;
|
|
|
|
VectorSet ( goreSkin.scale, 1, 1, 1 );
|
|
|
|
VectorCopy ( hitdirection, goreSkin.rayDirection);
|
|
|
|
VectorCopy ( hitloc, goreSkin.hitLocation );
|
|
VectorCopy ( entposition, goreSkin.position );
|
|
goreSkin.angles[YAW] = entangle;
|
|
|
|
trap_G2API_AddSkinGore(ghoul2,&goreSkin);
|
|
}
|
|
|
|
|
|
void CG_DoGoreFromWeapon( int weaponnum, int attack, vec3_t hitloc, vec3_t hitdirection,
|
|
int entnum, vec3_t entposition, float entangle, void *ghoul2)
|
|
{
|
|
float angle, size, size2;
|
|
|
|
switch (weaponnum)
|
|
{
|
|
case WP_KNIFE:
|
|
if (attack==ATTACK_ALTERNATE)
|
|
{
|
|
CG_AddGore(PGORE_PUNCTURE, flrand(3.5, 4.0),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 4.0*1.4, 15000, 0.1,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
angle= (M_PI / 2 * (1+2*irand(0,1))) + flrand( .7, .7);
|
|
switch(irand(1,3))
|
|
{
|
|
case 1:
|
|
size = flrand(2.8, 3.2);
|
|
size2 = flrand(1.8, 2.2);
|
|
CG_AddSlashGore(PGORE_KNIFESLASH,
|
|
angle, size, size2,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddSlashGrowGore(PGORE_KNIFE_SOAK, angle, size*1.2, size2*2.0, 15000, 0.1,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
case 2:
|
|
size = 8.0f*flrand(.8, 1.2);
|
|
size2 = 1.75f*flrand(.8, 1.2);
|
|
CG_AddSlashGore(PGORE_KNIFESLASH2,
|
|
angle, size, size2,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddSlashGrowGore(PGORE_KNIFE_SOAK, angle, size*1.2, size2*2.0, 15000, 0.1,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
default:
|
|
size = flrand(3.0f,4.0f);
|
|
size2 = flrand(0.5f,1.0f);
|
|
CG_AddSlashGore(PGORE_KNIFESLASH3,
|
|
angle, size, size2,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddSlashGrowGore(PGORE_KNIFE_SOAK, angle, size*1.2, size2*2.0, 15000, 0.1,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Smaller guns with pistol whip altfires
|
|
case WP_M1911A1_PISTOL:
|
|
case WP_USSOCOM_PISTOL:
|
|
if (attack==ATTACK_ALTERNATE)
|
|
{ // Bonk on the head
|
|
CG_AddGore(PGORE_BLOODY_SPLOTCH2, flrand(5.25f, 7.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
|
|
}
|
|
else
|
|
{
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 3.75f, 4.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 4.5*1.35, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Small guns
|
|
case WP_MICRO_UZI_SUBMACHINEGUN:
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 3.75f, 4.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 4.5f*1.35f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
|
|
// Shotgun with whip altfire
|
|
case WP_M590_SHOTGUN:
|
|
if (attack==ATTACK_ALTERNATE)
|
|
{ // Bond on de haid
|
|
CG_AddGore(PGORE_BLOODY_SPLOTCH2, flrand(7.75, 11.25),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
else
|
|
{
|
|
CG_AddGore(irand(PGORE_SHOTGUN, PGORE_SHOTGUNBIG), flrand( 8.25f, 11.25f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 11.25f*1.25f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>1)
|
|
{
|
|
CG_AddGore(PGORE_PELLETS, 8.25f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Medium guns
|
|
case WP_M3A1_SUBMACHINEGUN:
|
|
case WP_MP5:
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 5.25f, 7.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 7.5f*1.3f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
|
|
// Shotguns
|
|
case WP_USAS_12_SHOTGUN:
|
|
CG_AddGore(irand(PGORE_SHOTGUN, PGORE_SHOTGUNBIG), flrand( 8.25f, 11.25f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 11.25f*1.25f, 15000, 0.1,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>1)
|
|
{
|
|
CG_AddGore(PGORE_PELLETS, 8.25f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Assault rifle with grenade altfire
|
|
case WP_M4_ASSAULT_RIFLE:
|
|
if (attack==ATTACK_ALTERNATE)
|
|
{
|
|
CG_AddGore(PGORE_SHRAPNEL, flrand( 14.0f, 17.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>1)
|
|
{
|
|
CG_AddGore(PGORE_PELLETS, 10.0,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 5.25f, 7.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 7.5f*1.3f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Assault rifle with bayonet altfire
|
|
case WP_AK74_ASSAULT_RIFLE:
|
|
if (attack==ATTACK_ALTERNATE)
|
|
{
|
|
CG_AddGore(PGORE_PUNCTURE, flrand(3.5f, 4.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 4.0f*1.4f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 5.25f, 7.5f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 7.5f*1.3f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Large-caliber bullets
|
|
case WP_MSG90A1:
|
|
case WP_M60_MACHINEGUN:
|
|
CG_AddGore(irand(PGORE_BULLET_E, PGORE_BULLET_G), flrand( 6.0f, 9.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGrowGore(PGORE_KNIFE_SOAK, 9.0f*1.25f, 15000, 0.1f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
|
|
// Explosions
|
|
case WP_MM1_GRENADE_LAUNCHER:
|
|
case WP_RPG7_LAUNCHER:
|
|
case WP_SMOHG92_GRENADE:
|
|
CG_AddGore(PGORE_SHRAPNEL, flrand( 14.0f, 17.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>1)
|
|
{
|
|
CG_AddGore(PGORE_PELLETS, 10.0f,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
|
|
// Stun/char
|
|
case WP_M84_GRENADE:
|
|
case WP_M15_GRENADE:
|
|
CG_AddGore(PGORE_BURN, flrand( 14.0f, 18.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
break;
|
|
|
|
// Fire
|
|
case WP_ANM14_GRENADE:
|
|
CG_AddTimedGore(PGORE_IMMOLATE, flrand( 18.0f, 22.0f), 4000,
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
if (cg_goreDetail.integer>0)
|
|
{
|
|
CG_AddGore(PGORE_BURN, flrand( 18.0f, 22.0f),
|
|
hitloc, hitdirection, entnum, entposition, entangle, ghoul2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void CG_PredictedProcGore ( int weaponnum, int attack, vec3_t start, vec3_t end, centity_t* cent )
|
|
{
|
|
vec3_t direction;
|
|
|
|
// if no blood then dont add the proc gore
|
|
if ( cg_lockBlood.integer || !cent->ghoul2 || !cent->currentValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
VectorSubtract ( end, start, direction);
|
|
VectorNormalize ( direction);
|
|
|
|
CG_DoGoreFromWeapon(weaponnum, attack, // Weaponnum and altattack
|
|
end, direction, // hitloc and hitdirection
|
|
cent->currentState.number, // victim entity number
|
|
cent->lerpOrigin, cent->pe.ghoulLegsAngles[YAW], // entity position, entity yaw
|
|
cent->ghoul2);
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_AddProcGore
|
|
|
|
Adds procedural gore to the player specified in the given cent. The cent is a
|
|
temp ent containing all the information about the shot.
|
|
======================
|
|
*/
|
|
void CG_AddProcGore(centity_t *cent)
|
|
{
|
|
centity_t* source;
|
|
attackType_t attack;
|
|
vec3_t direction;
|
|
|
|
// Blood locked?
|
|
if ( cg_lockBlood.integer || !cent->currentState.time )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// No procedural gore on this shot.
|
|
if ( cent->currentState.time & GORE_NONE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
source = CG_GetEntity ( cent->currentState.otherEntityNum2 );
|
|
if (!source->ghoul2 || !source->currentValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Extract the direction of fire
|
|
ByteToDir( cent->currentState.eventParm, direction );
|
|
attack = ((cent->currentState.time>>8)&0xFF);
|
|
|
|
CG_UpdatePlayerModel ( source );
|
|
|
|
CG_DoGoreFromWeapon(cent->currentState.time&0xFF, attack, // Weaponnum and altattack
|
|
cent->lerpOrigin, direction, // hitloc and hitdirection
|
|
cent->currentState.otherEntityNum2, // victim entity number
|
|
cent->currentState.angles, (cent->currentState.time>>16)&0x7FFF, // entity position, entity yaw
|
|
source->ghoul2);
|
|
}
|
|
|
|
|
|
|
|
#define MAX_GORE_POOL 20000
|
|
|
|
static char gorePool[MAX_GORE_POOL];
|
|
static int gorePoolSize = 0;
|
|
|
|
static char *AllocGorePool(int size)
|
|
{
|
|
gorePoolSize = ((gorePoolSize + 0x00000003) & 0xfffffffc);
|
|
|
|
if (gorePoolSize + size > MAX_GORE_POOL)
|
|
{
|
|
Com_Error( ERR_DROP, "AllocGorePool: buffer exceeded (%d > %d)", gorePoolSize + size, MAX_GORE_POOL);
|
|
return 0;
|
|
}
|
|
|
|
gorePoolSize += size;
|
|
|
|
return &gorePool[gorePoolSize-size];
|
|
}
|
|
|
|
static char *AllocMultiString(TGPValue field)
|
|
{
|
|
TGPValue value;
|
|
int size = 1;
|
|
char name[256];
|
|
char *output, *pos;
|
|
|
|
if (!field)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
value = trap_GPV_GetList(field);
|
|
while(value)
|
|
{
|
|
trap_GPV_GetName(value, name);
|
|
size += strlen(name) + 1;
|
|
value = trap_GPV_GetNext(value);
|
|
}
|
|
|
|
output = pos = AllocGorePool(size);
|
|
value = trap_GPV_GetList(field);
|
|
while(value)
|
|
{
|
|
trap_GPV_GetName(value, name);
|
|
strcpy(pos, name);
|
|
pos += strlen(name) + 1;
|
|
value = trap_GPV_GetNext(value);
|
|
}
|
|
*pos = 0;
|
|
|
|
return output;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define GORE_CHILD 0x00000001
|
|
#define GORE_NO_CHILD_SURFACES_ON 0x00000002
|
|
#define GORE_NO_CHILD_FX 0x00000004
|
|
#define GORE_NO_CHILD_CHUNKS 0x00000008
|
|
#define GORE_NO_CHILD_BOLTONS 0x00000010
|
|
|
|
typedef enum
|
|
{
|
|
GORE_SIDE_RIGHT = 0,
|
|
GORE_SIDE_LEFT,
|
|
GORE_SIDE_MAX
|
|
} EGoreSide;
|
|
|
|
typedef struct SGoreInfo
|
|
{
|
|
const char *mLongName;
|
|
const char *mShortName;
|
|
} TGoreInfo;
|
|
|
|
typedef struct SGoreLocation
|
|
{
|
|
const char *mPublicName;
|
|
EGoreSide mPrimarySide;
|
|
EGoreSide mOppositeSide;
|
|
} TGoreLocation;
|
|
|
|
typedef struct SGoreEffectType
|
|
{
|
|
char mName[MAX_QPATH];
|
|
int mFXID;
|
|
|
|
struct SGoreEffectType *mNext;
|
|
} TGoreEffectType;
|
|
|
|
typedef struct SGorePieceType
|
|
{
|
|
char mName[MAX_QPATH];
|
|
char mBolt[MAX_QPATH];
|
|
|
|
TGhoul2 mG2Model;
|
|
qhandle_t mNormalModel;
|
|
|
|
struct SGorePieceType *mNext;
|
|
} TGorePieceType;
|
|
|
|
typedef struct SGoreEffect
|
|
{
|
|
char mName[MAX_QPATH];
|
|
char mBolt[MAX_QPATH];
|
|
|
|
struct SGoreEffect *mNext;
|
|
} TGoreEffect;
|
|
|
|
typedef struct SGoreBoltOn
|
|
{
|
|
char mName[MAX_QPATH];
|
|
char mBolt[MAX_QPATH];
|
|
|
|
struct SGoreBoltOn *mNext;
|
|
} TGoreBoltOn;
|
|
|
|
typedef struct SGoreChunk
|
|
{
|
|
char mRoot[MAX_QPATH];
|
|
char mBone[MAX_QPATH];
|
|
char *mSurfacesOn;
|
|
char *mChildrenOff;
|
|
float mMinForce;
|
|
float mMaxForce;
|
|
|
|
struct SGoreChunk *mNext;
|
|
} TGoreChunk;
|
|
|
|
typedef struct SGoreArea
|
|
{
|
|
char mLocation[MAX_QPATH];
|
|
char *mSurfacesOff;
|
|
char *mSurfacesOn;
|
|
char *mBoltsOff;
|
|
char *mChildren;
|
|
unsigned mFlags;
|
|
|
|
struct SGoreEffect *mFX;
|
|
struct SGoreBoltOn *mBoltOns;
|
|
struct SGoreChunk *mChunks;
|
|
|
|
struct SGoreArea *mNext;
|
|
} TGoreArea;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static TGoreEffectType *GoreEffectTypes;
|
|
static TGorePieceType *GorePieceTypes;
|
|
static TGoreArea *GoreAreas;
|
|
static TGoreInfo GoreInfo[GORE_SIDE_MAX] =
|
|
{
|
|
{ "right", "r" },
|
|
{ "left", "l" }
|
|
};
|
|
|
|
static TGoreLocation GoreLocations[] =
|
|
{
|
|
{ "none", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
|
|
{ "foot", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "foot", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
{ "leg_upper", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "leg_upper", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
{ "leg_lower", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "leg_lower", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
|
|
{ "hand", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "hand", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
{ "arm_lower", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "arm_lower", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
|
|
{ "head", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "torso", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
|
|
{ "torso", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "torso", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
{ "torso", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
//{ "torso", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
{ "arm_upper", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
//{ "torso", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
{ "arm_upper", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
|
|
{ "torso", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
|
|
{ "", GORE_SIDE_LEFT, GORE_SIDE_RIGHT },
|
|
|
|
// DEBUG ONE
|
|
{ "torso", GORE_SIDE_RIGHT, GORE_SIDE_LEFT },
|
|
|
|
/*
|
|
"hand_right",
|
|
"arm_lower_right",
|
|
"torso_right",
|
|
"arm_upper_right",
|
|
"leg_lower_right",
|
|
"leg_upper_right",
|
|
"hip_right",
|
|
"head_back_lower_right",
|
|
"head_back_upper_right",
|
|
"head_front_lower_right",
|
|
"head_front_mid_right",
|
|
"head_front_upper_right",
|
|
"head_side_right",
|
|
"head_right",
|
|
*/
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char *CreateFinalName(const char *Input, EGoreSide *Primary, EGoreSide *Opposite, qboolean SwapIfOpposite)
|
|
{
|
|
static char output[256];
|
|
char *outputPos;
|
|
const char *replace = "";
|
|
EGoreSide use = GORE_SIDE_RIGHT;
|
|
qboolean doSwap = qfalse;
|
|
EGoreSide save;
|
|
const char *origInput = Input;
|
|
|
|
outputPos = output;
|
|
while(*Input)
|
|
{
|
|
if ((*Input) == '<')
|
|
{
|
|
Input++;
|
|
if ((*Input) == 'P')
|
|
{ // default to primary
|
|
use = *Primary;
|
|
}
|
|
else if ((*Input) == 'O')
|
|
{ // use opposite
|
|
use = *Opposite;
|
|
doSwap = qtrue;
|
|
}
|
|
else
|
|
{
|
|
Com_Error(ERR_DROP, "CreateFinalName: bad input string: %s", origInput);
|
|
break;
|
|
}
|
|
|
|
Input++;
|
|
if ((*Input) == 'L')
|
|
{ // default to long name
|
|
replace = GoreInfo[use].mLongName;
|
|
}
|
|
else if ((*Input) == 'S')
|
|
{ // use short name
|
|
replace = GoreInfo[use].mShortName;
|
|
}
|
|
else
|
|
{
|
|
Com_Error(ERR_DROP, "CreateFinalName: bad input string: %s", origInput);
|
|
break;
|
|
}
|
|
|
|
strcpy(outputPos, replace);
|
|
outputPos += strlen(replace);
|
|
|
|
Input++;
|
|
|
|
if ((*Input) != '>')
|
|
{
|
|
Com_Error(ERR_DROP, "CreateFinalName: bad input string: %s", origInput);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*outputPos++ = *Input;
|
|
}
|
|
|
|
Input++;
|
|
}
|
|
|
|
*outputPos = 0;
|
|
if (SwapIfOpposite && doSwap)
|
|
{
|
|
save = *Primary;
|
|
*Primary = *Opposite;
|
|
*Opposite = save;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
static TGoreArea *FindGoreZone(const char *Location, EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
TGoreArea *gore = GoreAreas;
|
|
|
|
while(gore)
|
|
{
|
|
if (Q_stricmp(CreateFinalName(gore->mLocation, &Primary, &Opposite, qfalse), Location) == 0)
|
|
{
|
|
return gore;
|
|
}
|
|
|
|
gore = gore->mNext;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static TGoreEffectType *FindGoreEffectType(const char *Name)
|
|
{
|
|
TGoreEffectType *effect = GoreEffectTypes;
|
|
|
|
while(effect)
|
|
{
|
|
if (Q_stricmp(effect->mName, Name) == 0)
|
|
{
|
|
return effect;
|
|
}
|
|
|
|
effect = effect->mNext;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static TGorePieceType *FindGorePieceType(const char *Name)
|
|
{
|
|
TGorePieceType *piece = GorePieceTypes;
|
|
|
|
while(piece)
|
|
{
|
|
if (Q_stricmp(piece->mName, Name) == 0)
|
|
{
|
|
return piece;
|
|
}
|
|
|
|
piece = piece->mNext;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void CG_ParseGoreEffect(TGPGroup group)
|
|
{
|
|
TGoreEffectType *effect;
|
|
char file[256];
|
|
|
|
effect = (TGoreEffectType *)AllocGorePool(sizeof(*effect));
|
|
memset(effect, 0, sizeof(*effect));
|
|
|
|
effect->mNext = GoreEffectTypes;
|
|
GoreEffectTypes = effect;
|
|
|
|
trap_GPG_FindPairValue(group, "Name", "", effect->mName);
|
|
trap_GPG_FindPairValue(group, "File", "", file);
|
|
effect->mFXID = trap_FX_RegisterEffect(file);
|
|
}
|
|
|
|
static void CG_ParseGorePiece(TGPGroup group)
|
|
{
|
|
TGorePieceType *piece;
|
|
char file[256];
|
|
|
|
piece = (TGorePieceType *)AllocGorePool(sizeof(*piece));
|
|
memset(piece, 0, sizeof(*piece));
|
|
|
|
piece->mNext = GorePieceTypes;
|
|
GorePieceTypes = piece;
|
|
|
|
trap_GPG_FindPairValue(group, "Name", "", piece->mName);
|
|
trap_GPG_FindPairValue(group, "Bolt", "", piece->mBolt);
|
|
trap_GPG_FindPairValue(group, "Model", "", file);
|
|
|
|
if (trap_G2API_InitGhoul2Model(&piece->mG2Model, file, 0, 0, 0, 0, 0) == -1)
|
|
{ // wasn't a g2 model, so try a regular one
|
|
piece->mNormalModel = trap_R_RegisterModel(file);
|
|
}
|
|
}
|
|
|
|
static TGoreEffect *CG_ParseFX(TGPGroup group)
|
|
{
|
|
TGoreEffect *effect;
|
|
|
|
effect = (TGoreEffect *)AllocGorePool(sizeof(*effect));
|
|
memset(effect, 0, sizeof(*effect));
|
|
|
|
trap_GPG_FindPairValue(group, "Name", "", effect->mName);
|
|
trap_GPG_FindPairValue(group, "Bolt", "", effect->mBolt);
|
|
|
|
return effect;
|
|
}
|
|
|
|
static TGoreBoltOn *CG_ParseBoltOn(TGPGroup group)
|
|
{
|
|
TGoreBoltOn *bolt;
|
|
|
|
bolt = (TGoreBoltOn *)AllocGorePool(sizeof(*bolt));
|
|
memset(bolt, 0, sizeof(*bolt));
|
|
|
|
trap_GPG_FindPairValue(group, "Name", "", bolt->mName);
|
|
trap_GPG_FindPairValue(group, "Bolt", "", bolt->mBolt);
|
|
|
|
return bolt;
|
|
}
|
|
|
|
static TGoreChunk *CG_ParseChunk(TGPGroup group)
|
|
{
|
|
TGoreChunk *chunk;
|
|
char temp[256];
|
|
|
|
chunk = (TGoreChunk *)AllocGorePool(sizeof(*chunk));
|
|
memset(chunk, 0, sizeof(*chunk));
|
|
|
|
trap_GPG_FindPairValue(group, "Root", "", chunk->mRoot);
|
|
trap_GPG_FindPairValue(group, "Bone", "", chunk->mBone);
|
|
chunk->mSurfacesOn = AllocMultiString(trap_GPG_FindPair(group, "Surfaces_On"));
|
|
chunk->mChildrenOff = AllocMultiString(trap_GPG_FindPair(group, "Children_Off"));
|
|
|
|
trap_GPG_FindPairValue(group, "MinForce", "40", temp);
|
|
chunk->mMinForce = atof(temp);
|
|
trap_GPG_FindPairValue(group, "MaxForce", "80", temp);
|
|
chunk->mMaxForce = atof(temp);
|
|
|
|
return chunk;
|
|
}
|
|
|
|
static void CG_ParseGoreArea(TGPGroup group)
|
|
{
|
|
TGoreArea *gore;
|
|
TGoreEffect *effect;
|
|
TGoreBoltOn *bolt;
|
|
TGoreChunk *chunk;
|
|
TGPValue flags, value;
|
|
TGPGroup sub;
|
|
char name[256];
|
|
|
|
gore = (TGoreArea *)AllocGorePool(sizeof(*gore));
|
|
memset(gore, 0, sizeof(*gore));
|
|
|
|
gore->mNext = GoreAreas;
|
|
GoreAreas = gore;
|
|
|
|
trap_GPG_FindPairValue(group, "Location", "", gore->mLocation);
|
|
|
|
gore->mSurfacesOff = AllocMultiString(trap_GPG_FindPair(group, "Surfaces_Off"));
|
|
gore->mSurfacesOn = AllocMultiString(trap_GPG_FindPair(group, "Surfaces_On"));
|
|
gore->mBoltsOff = AllocMultiString(trap_GPG_FindPair(group, "Bolts_Off"));
|
|
gore->mChildren = AllocMultiString(trap_GPG_FindPair(group, "Children"));
|
|
|
|
flags = trap_GPG_FindPair(group, "Flags");
|
|
if (flags)
|
|
{
|
|
value = trap_GPV_GetList(flags);
|
|
while(value)
|
|
{
|
|
trap_GPV_GetName(value, name);
|
|
if (Q_stricmp(name, "NoChildSurfacesOn") == 0)
|
|
{
|
|
gore->mFlags |= GORE_NO_CHILD_SURFACES_ON;
|
|
}
|
|
else if (Q_stricmp(name, "NoChildFX") == 0)
|
|
{
|
|
gore->mFlags |= GORE_NO_CHILD_FX;
|
|
}
|
|
else if (Q_stricmp(name, "NoChildChunks") == 0)
|
|
{
|
|
gore->mFlags |= GORE_NO_CHILD_CHUNKS;
|
|
}
|
|
else if (Q_stricmp(name, "NoChildBoltOns") == 0)
|
|
{
|
|
gore->mFlags |= GORE_NO_CHILD_BOLTONS;
|
|
}
|
|
|
|
value = trap_GPV_GetNext(value);
|
|
}
|
|
}
|
|
|
|
sub = trap_GPG_GetSubGroups(group);
|
|
while(sub)
|
|
{
|
|
trap_GPG_GetName(sub, name);
|
|
if (Q_stricmp(name, "FX") == 0)
|
|
{
|
|
effect = CG_ParseFX(sub);
|
|
effect->mNext = gore->mFX;
|
|
gore->mFX = effect;
|
|
}
|
|
else if (Q_stricmp(name, "Chunk") == 0)
|
|
{
|
|
chunk = CG_ParseChunk(sub);
|
|
chunk->mNext = gore->mChunks;
|
|
gore->mChunks = chunk;
|
|
}
|
|
else if (Q_stricmp(name, "BoltOn") == 0)
|
|
{
|
|
bolt = CG_ParseBoltOn(sub);
|
|
bolt->mNext = gore->mBoltOns;
|
|
gore->mBoltOns = bolt;
|
|
}
|
|
|
|
sub = trap_GPG_GetNext(sub);
|
|
}
|
|
}
|
|
|
|
qboolean CG_ParseGore(void)
|
|
{
|
|
TGenericParser2 GP2;
|
|
TGPGroup topGroup, topSubs;
|
|
char name[256];
|
|
|
|
GP2 = trap_GP_ParseFile("ext_data/sof2.gore", qtrue, qfalse);
|
|
if (!GP2)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
gorePoolSize = 0;
|
|
GoreEffectTypes = 0;
|
|
GorePieceTypes = 0;
|
|
GoreAreas = 0;
|
|
|
|
topGroup = trap_GP_GetBaseParseGroup(GP2);
|
|
topSubs = trap_GPG_GetSubGroups(topGroup);
|
|
while(topSubs)
|
|
{
|
|
trap_GPG_GetName(topSubs, name);
|
|
if (Q_stricmp(name, "gore_area") == 0)
|
|
{
|
|
CG_ParseGoreArea(topSubs);
|
|
}
|
|
else if (Q_stricmp(name, "gore_effect") == 0)
|
|
{
|
|
CG_ParseGoreEffect(topSubs);
|
|
}
|
|
else if (Q_stricmp(name, "gore_piece") == 0)
|
|
{
|
|
CG_ParseGorePiece(topSubs);
|
|
}
|
|
|
|
topSubs = trap_GPG_GetNext(topSubs);
|
|
}
|
|
|
|
trap_GP_Delete(&GP2);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void CG_ProcessSurfaceList(void *model, char *surfaceList, int flags,
|
|
EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
while(surfaceList && surfaceList[0])
|
|
{
|
|
if (!trap_G2API_SetSurfaceOnOff(model, 0, CreateFinalName(surfaceList, &Primary, &Opposite, qfalse), flags))
|
|
{
|
|
|
|
#ifdef _DEBUG
|
|
// Com_Printf("Missing surface '%s'\n", surfaceList);
|
|
#endif
|
|
}
|
|
surfaceList += strlen(surfaceList) + 1;
|
|
}
|
|
}
|
|
|
|
static void CG_ProcessBoltList(void *model, char *boltList, EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
int numModels;
|
|
int boltIndex;
|
|
int i;
|
|
|
|
while(boltList && boltList[0])
|
|
{
|
|
numModels = trap_G2API_GetNumModels(model);
|
|
boltIndex = trap_G2API_FindBoltIndex(model, 0, CreateFinalName(boltList, &Primary, &Opposite, qfalse));
|
|
if (boltIndex != -1)
|
|
{
|
|
for(i=1;i<numModels;i++)
|
|
{
|
|
if (trap_G2API_GetBoltIndex(model, i) == boltIndex)
|
|
{
|
|
trap_G2API_DetachG2Model(model, i);
|
|
trap_G2API_RemoveGhoul2Model(&model, i);
|
|
numModels--;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
boltList += strlen(boltList) + 1;
|
|
}
|
|
}
|
|
|
|
static void CG_ProcessChunkChild(void *Ghoul2, char *Name,
|
|
EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
TGoreArea *gore;
|
|
char *child;
|
|
char finalName[256];
|
|
|
|
strcpy(finalName, CreateFinalName(Name, &Primary, &Opposite, qtrue));
|
|
gore = FindGoreZone(finalName, Primary, Opposite);
|
|
if (!gore)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CG_ProcessSurfaceList(Ghoul2, gore->mSurfacesOff, G2SURFACEFLAG_OFF, Primary, Opposite);
|
|
|
|
child = gore->mChildren;
|
|
while(child && child[0])
|
|
{
|
|
CG_ProcessChunkChild(Ghoul2, child, Primary, Opposite);
|
|
|
|
child += strlen(child) + 1;
|
|
}
|
|
}
|
|
|
|
static void CG_ProcessChunk(int clientNum, centity_t *cent, TGoreChunk *chunk, vec3_t Direction,
|
|
EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
localEntity_t *le;
|
|
refEntity_t *re;
|
|
int bolt;
|
|
char *child;
|
|
animation_t *anim;
|
|
float animSpeed;
|
|
int flags=BONE_ANIM_OVERRIDE_FREEZE;
|
|
clientInfo_t *ci;
|
|
mdxaBone_t matrix;
|
|
qboolean boltMatrixOK = qfalse;;
|
|
|
|
le = CG_AllocLocalEntity();
|
|
re = &le->refEntity;
|
|
|
|
le->leType = LE_GIB;
|
|
le->startTime = cg.time;
|
|
le->endTime = le->startTime + BODY_SINK_DELAY + BODY_SINK_TIME;
|
|
le->leFlags = LEF_TUMBLE;
|
|
le->bounceFactor = 0.2f;
|
|
re->radius = 50;
|
|
re->renderfx = RF_MINLIGHT;
|
|
|
|
VectorSet ( re->modelScale, 1, 1, 1 );
|
|
|
|
AxisCopy( axisDefault, re->axis );
|
|
|
|
le->pos.trType = TR_GRAVITY;
|
|
VectorMA ( vec3_origin, irand ( chunk->mMinForce * 2, chunk->mMaxForce * 2), Direction, le->pos.trDelta );
|
|
le->pos.trDelta[2] = flrand ( 100, 150 );
|
|
le->pos.trTime = cg.time;
|
|
|
|
le->angles.trType = TR_LINEAR_STOP;
|
|
VectorClear(le->angles.trBase);
|
|
le->angles.trBase[YAW] = crandom() * 15;
|
|
le->angles.trDelta[0] = 0.0; // crandom();
|
|
le->angles.trDelta[YAW] = crandom() * 15 - 7;
|
|
le->angles.trDelta[2] = 0.0; // crandom();
|
|
le->angles.trDuration = BODY_SINK_DELAY + BODY_SINK_TIME;
|
|
le->angles.trTime = cg.time;
|
|
|
|
le->zOffset = 26.0;
|
|
|
|
// ghoul stuff to do limbs
|
|
if (!cent->ghoul2)
|
|
{
|
|
Com_Error(ERR_DROP, "CG_ProcessChunk invalid g2 pointer for client %d\n", clientNum);
|
|
}
|
|
|
|
trap_G2API_DuplicateGhoul2Instance(cent->ghoul2, &re->ghoul2);
|
|
if (!re->ghoul2)
|
|
{ // whoa, that surface caused our model to go away???
|
|
CG_FreeLocalEntity(le);
|
|
return;
|
|
}
|
|
|
|
trap_G2API_SetRootSurface(&re->ghoul2, 0, CreateFinalName(chunk->mRoot, &Primary, &Opposite, qfalse));
|
|
|
|
bolt = trap_G2API_AddBolt(cent->ghoul2, 0, CreateFinalName(chunk->mBone, &Primary, &Opposite, qfalse));
|
|
if (bolt != -1)
|
|
{
|
|
boltMatrixOK = trap_G2API_GetBoltMatrix(cent->ghoul2, 0, bolt, &matrix, cent->lerpAngles,
|
|
cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
|
|
}
|
|
|
|
if (bolt == -1 || !boltMatrixOK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
matrix.matrix[0][3] = cent->lerpOrigin[0];
|
|
matrix.matrix[1][3] = cent->lerpOrigin[1];
|
|
matrix.matrix[2][3] = cent->lerpOrigin[2];
|
|
|
|
le->pos.trBase[0] = re->origin[0] = matrix.matrix[0][3];
|
|
le->pos.trBase[1] = re->origin[1] = matrix.matrix[1][3];
|
|
le->pos.trBase[2] = re->origin[2] = matrix.matrix[2][3];
|
|
|
|
bolt = trap_G2API_AddBolt(re->ghoul2, 0, CreateFinalName(chunk->mBone, &Primary, &Opposite, qfalse));
|
|
trap_G2API_SetNewOrigin(re->ghoul2, 0, bolt);
|
|
|
|
ci = &cgs.clientinfo[clientNum];
|
|
anim = &ci->animations[cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT];
|
|
animSpeed = 50.0f / anim->frameLerp;
|
|
|
|
trap_G2API_SetBoneAnim(re->ghoul2, 0, "model_root", anim->firstFrame + anim->numFrames - 1, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, -1, 0);
|
|
trap_G2API_SetBoneAnim(re->ghoul2, 0, "lower_lumbar", anim->firstFrame + anim->numFrames - 1, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, -1, 0);
|
|
|
|
CG_ProcessSurfaceList(re->ghoul2, chunk->mSurfacesOn, 0, Primary, Opposite);
|
|
|
|
child = chunk->mChildrenOff;
|
|
while(child && child[0])
|
|
{
|
|
CG_ProcessChunkChild(re->ghoul2, child, Primary, Opposite);
|
|
|
|
child += strlen(child) + 1;
|
|
}
|
|
}
|
|
|
|
static void CG_ProcessBoltOn(int clientNum, centity_t *cent, TGoreBoltOn *bolton,
|
|
EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
TGorePieceType *piece = FindGorePieceType(bolton->mName);
|
|
const char *boltPosition;
|
|
int pieceIndex;
|
|
int boltIndex;
|
|
|
|
if (!piece)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (piece->mG2Model)
|
|
{
|
|
boltPosition = bolton->mBolt;
|
|
if (!boltPosition[0])
|
|
{
|
|
boltPosition = piece->mBolt;
|
|
}
|
|
|
|
pieceIndex = trap_G2API_CopySpecificGhoul2Model(piece->mG2Model, 0, cent->ghoul2, -1);
|
|
if (pieceIndex != -1)
|
|
{
|
|
boltIndex = trap_G2API_AddBolt(cent->ghoul2, 0, CreateFinalName(boltPosition, &Primary, &Opposite, qfalse));
|
|
if (boltIndex != -1)
|
|
{
|
|
trap_G2API_AttachG2Model(cent->ghoul2, pieceIndex, cent->ghoul2, boltIndex, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CG_ProcessGore(int clientNum, centity_t *cent, const char *Location, unsigned Flags,
|
|
vec3_t Direction, EGoreSide Primary, EGoreSide Opposite)
|
|
{
|
|
TGoreArea *gore;
|
|
TGoreEffect *fx;
|
|
TGoreChunk *chunk;
|
|
TGoreBoltOn *bolton;
|
|
char *children;
|
|
int bolt;
|
|
mdxaBone_t matrix;
|
|
qboolean boltMatrixOK;
|
|
vec3_t origin;
|
|
TGoreEffectType *effect;
|
|
char finalName[256];
|
|
|
|
strcpy(finalName, CreateFinalName(Location, &Primary, &Opposite, qtrue));
|
|
gore = FindGoreZone(finalName, Primary, Opposite);
|
|
if (!gore)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!(Flags & GORE_CHILD) || !(Flags & GORE_NO_CHILD_FX) )
|
|
{
|
|
fx = gore->mFX;
|
|
while(fx)
|
|
{
|
|
effect = FindGoreEffectType(fx->mName);
|
|
if (effect)
|
|
{
|
|
bolt = trap_G2API_AddBolt(cent->ghoul2, 0, CreateFinalName(fx->mBolt, &Primary, &Opposite, qfalse));
|
|
if (bolt != -1)
|
|
{
|
|
vec3_t axis[3];
|
|
int boltInfo;
|
|
|
|
boltMatrixOK = trap_G2API_GetBoltMatrix(cent->ghoul2, 0, bolt, &matrix, cent->lerpAngles,
|
|
cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
|
|
axis[0][0] = matrix.matrix[0][0];
|
|
axis[0][1] = matrix.matrix[1][0];
|
|
axis[0][2] = matrix.matrix[2][0];
|
|
axis[1][0] = matrix.matrix[0][1];
|
|
axis[1][1] = matrix.matrix[1][1];
|
|
axis[1][2] = matrix.matrix[2][1];
|
|
axis[2][0] = matrix.matrix[0][2];
|
|
axis[2][1] = matrix.matrix[1][2];
|
|
axis[2][2] = matrix.matrix[2][2];
|
|
origin[0] = matrix.matrix[0][3];
|
|
origin[1] = matrix.matrix[1][3];
|
|
origin[2] = matrix.matrix[2][3];
|
|
|
|
|
|
boltInfo = (( 0/*modelnum*/ & MODEL_AND ) << MODEL_SHIFT );
|
|
boltInfo |= (( bolt & BOLT_AND ) << BOLT_SHIFT );
|
|
boltInfo |= (( cent->currentState.number & ENTITY_AND ) << ENTITY_SHIFT );
|
|
|
|
trap_FX_PlayEntityEffectID( effect->mFXID, origin, axis, boltInfo, -1, -1, -1);
|
|
/* boltMatrixOK = trap_G2API_GetBoltMatrix(cent->ghoul2, 0, bolt, &matrix, cent->lerpAngles,
|
|
cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
|
|
if (boltMatrixOK)
|
|
{
|
|
origin[0] = matrix.matrix[0][3];
|
|
origin[1] = matrix.matrix[1][3];
|
|
origin[2] = matrix.matrix[2][3];
|
|
trap_FX_PlayEffectID(effect->mFXID, origin, Direction, -1, -1 );
|
|
}
|
|
*/ }
|
|
}
|
|
|
|
fx = fx->mNext;
|
|
}
|
|
}
|
|
|
|
if (!(Flags & GORE_CHILD) || !(Flags & GORE_NO_CHILD_CHUNKS) )
|
|
{
|
|
chunk = gore->mChunks;
|
|
while(chunk)
|
|
{
|
|
CG_ProcessChunk(clientNum, cent, chunk, Direction, Primary, Opposite);
|
|
|
|
chunk = chunk->mNext;
|
|
}
|
|
}
|
|
|
|
if (!(Flags & GORE_CHILD) || !(Flags & GORE_NO_CHILD_BOLTONS) )
|
|
{
|
|
bolton = gore->mBoltOns;
|
|
while(bolton)
|
|
{
|
|
CG_ProcessBoltOn(clientNum, cent, bolton, Primary, Opposite);
|
|
|
|
bolton = bolton->mNext;
|
|
}
|
|
}
|
|
|
|
CG_ProcessSurfaceList(cent->ghoul2, gore->mSurfacesOff, G2SURFACEFLAG_OFF, Primary, Opposite);
|
|
if (!(Flags & GORE_CHILD) || !(Flags & GORE_NO_CHILD_SURFACES_ON) )
|
|
{ // children with no gore from the parents shouldn't do this
|
|
CG_ProcessSurfaceList(cent->ghoul2, gore->mSurfacesOn, 0, Primary, Opposite);
|
|
}
|
|
CG_ProcessBoltList(cent->ghoul2, gore->mBoltsOff, Primary, Opposite);
|
|
|
|
children = gore->mChildren;
|
|
while(children && children[0])
|
|
{
|
|
CG_ProcessGore(clientNum, cent, children, gore->mFlags | Flags | GORE_CHILD, Direction, Primary, Opposite);
|
|
children += strlen(children) + 1;
|
|
}
|
|
}
|
|
|
|
|
|
void CG_ApplyGore(int clientNum, centity_t *cent, int hitLocation, vec3_t Direction)
|
|
{
|
|
TGoreLocation *Location = 0;
|
|
const char *dg, *token;
|
|
char area[256];
|
|
EGoreSide side;
|
|
int i;
|
|
|
|
// Gore locked?
|
|
if ( cg_lockSever.integer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
dg = cg_DebugGore.string;
|
|
if (dg[0])
|
|
{
|
|
strcpy(area, COM_Parse(&dg));
|
|
token = COM_Parse(&dg);
|
|
for(side=0;side<GORE_SIDE_MAX;side++)
|
|
{
|
|
if (Q_stricmp(GoreInfo[side].mLongName, token) == 0 ||
|
|
Q_stricmp(GoreInfo[side].mShortName, token) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
for(i=0;i<HL_DEBUG;i++)
|
|
{
|
|
if (Q_stricmp(GoreLocations[i].mPublicName, area) == 0 &&
|
|
GoreLocations[i].mPrimarySide == side)
|
|
{
|
|
Location = &GoreLocations[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Location)
|
|
{
|
|
if (hitLocation < 0 || hitLocation >= (sizeof(GoreLocations) / sizeof(struct SGoreLocation)))
|
|
{
|
|
Com_Error( ERR_DROP, "CG_ApplyGore: invalid hit location %d\n", hitLocation);
|
|
return;
|
|
}
|
|
Location = &GoreLocations[hitLocation];
|
|
}
|
|
else
|
|
{
|
|
// just so that I don't have to keep restarting the game to change the file
|
|
CG_ParseGore();
|
|
|
|
Com_Printf ( "GORE: Applying gore to location '%s'\n", Location->mPublicName );
|
|
}
|
|
|
|
CG_ProcessGore(clientNum, cent, Location->mPublicName, 0, Direction, Location->mPrimarySide, Location->mOppositeSide);
|
|
}
|
|
|
|
void CG_ShutdownGore(void)
|
|
{
|
|
TGorePieceType *piece = GorePieceTypes;
|
|
|
|
while(piece)
|
|
{
|
|
if (piece->mG2Model)
|
|
{
|
|
trap_G2API_CleanGhoul2Models(&piece->mG2Model);
|
|
}
|
|
piece = piece->mNext;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|