946 lines
21 KiB
C++
946 lines
21 KiB
C++
//wp_saberLoad.cpp
|
|
// leave this line at the top for all NPC_xxxx.cpp files...
|
|
#include "g_headers.h"
|
|
|
|
#include "q_shared.h"
|
|
#include "wp_saber.h"
|
|
|
|
extern qboolean G_ParseLiteral( const char **data, const char *string );
|
|
extern saber_colors_t TranslateSaberColor( const char *name );
|
|
extern qboolean PM_SaberInStart( int move );
|
|
extern qboolean PM_SaberInTransition( int move );
|
|
extern qboolean PM_SaberInAttack( int move );
|
|
|
|
extern stringID_table_t FPTable[];
|
|
|
|
#define MAX_SABER_DATA_SIZE 0x8000
|
|
char SaberParms[MAX_SABER_DATA_SIZE];
|
|
|
|
void Saber_SithSwordPrecache( void )
|
|
{//*SIGH* special sounds used by the sith sword
|
|
for ( int i = 1; i < 5; i++ )
|
|
{
|
|
G_SoundIndex( va( "sound/weapons/sword/stab%d.wav", i ) );
|
|
}
|
|
for ( i = 1; i < 5; i++ )
|
|
{
|
|
G_SoundIndex( va( "sound/weapons/sword/swing%d.wav", i ) );
|
|
}
|
|
for ( i = 1; i < 7; i++ )
|
|
{
|
|
G_SoundIndex( va( "sound/weapons/sword/fall%d.wav", i ) );
|
|
}
|
|
/*
|
|
for ( i = 1; i < 4; i++ )
|
|
{
|
|
G_SoundIndex( va( "sound/weapons/sword/spin%d.wav", i ) );
|
|
}
|
|
*/
|
|
}
|
|
|
|
stringID_table_t SaberTable[] =
|
|
{
|
|
ENUM2STRING(SABER_NONE),
|
|
ENUM2STRING(SABER_SINGLE),
|
|
ENUM2STRING(SABER_STAFF),
|
|
ENUM2STRING(SABER_BROAD),
|
|
ENUM2STRING(SABER_PRONG),
|
|
ENUM2STRING(SABER_DAGGER),
|
|
ENUM2STRING(SABER_ARC),
|
|
ENUM2STRING(SABER_SAI),
|
|
ENUM2STRING(SABER_CLAW),
|
|
ENUM2STRING(SABER_LANCE),
|
|
ENUM2STRING(SABER_STAR),
|
|
ENUM2STRING(SABER_TRIDENT),
|
|
ENUM2STRING(SABER_SITH_SWORD),
|
|
"", -1
|
|
};
|
|
|
|
saber_styles_t TranslateSaberStyle( const char *name )
|
|
{
|
|
if ( !Q_stricmp( name, "fast" ) )
|
|
{
|
|
return SS_FAST;
|
|
}
|
|
if ( !Q_stricmp( name, "medium" ) )
|
|
{
|
|
return SS_MEDIUM;
|
|
}
|
|
if ( !Q_stricmp( name, "strong" ) )
|
|
{
|
|
return SS_STRONG;
|
|
}
|
|
if ( !Q_stricmp( name, "desann" ) )
|
|
{
|
|
return SS_DESANN;
|
|
}
|
|
if ( !Q_stricmp( name, "tavion" ) )
|
|
{
|
|
return SS_TAVION;
|
|
}
|
|
if ( !Q_stricmp( name, "dual" ) )
|
|
{
|
|
return SS_DUAL;
|
|
}
|
|
if ( !Q_stricmp( name, "staff" ) )
|
|
{
|
|
return SS_STAFF;
|
|
}
|
|
return SS_NONE;
|
|
}
|
|
|
|
void WP_SaberFreeStrings( saberInfo_t &saber )
|
|
{
|
|
if (saber.name && gi.bIsFromZone(saber.name , TAG_G_ALLOC)) {
|
|
gi.Free (saber.name);
|
|
saber.name=0;
|
|
}
|
|
if (saber.fullName && gi.bIsFromZone(saber.fullName , TAG_G_ALLOC)) {
|
|
gi.Free (saber.fullName);
|
|
saber.fullName=0;
|
|
}
|
|
if (saber.model && gi.bIsFromZone(saber.model , TAG_G_ALLOC)) {
|
|
gi.Free (saber.model);
|
|
saber.model=0;
|
|
}
|
|
if (saber.skin && gi.bIsFromZone(saber.skin , TAG_G_ALLOC)) {
|
|
gi.Free (saber.skin);
|
|
saber.skin=0;
|
|
}
|
|
if (saber.brokenSaber1 && gi.bIsFromZone(saber.brokenSaber1 , TAG_G_ALLOC)) {
|
|
gi.Free (saber.brokenSaber1);
|
|
saber.brokenSaber1=0;
|
|
}
|
|
if (saber.brokenSaber2 && gi.bIsFromZone(saber.brokenSaber2 , TAG_G_ALLOC)) {
|
|
gi.Free (saber.brokenSaber2);
|
|
saber.brokenSaber2=0;
|
|
}
|
|
}
|
|
|
|
void WP_SaberSetDefaults( saberInfo_t *saber, qboolean setColors = qtrue )
|
|
{
|
|
//Set defaults so that, if it fails, there's at least something there
|
|
saber->name = NULL;
|
|
saber->fullName = NULL;
|
|
for ( int i = 0; i < MAX_BLADES; i++ )
|
|
{
|
|
if ( setColors )
|
|
{
|
|
saber->blade[i].color = SABER_RED;
|
|
}
|
|
saber->blade[i].radius = SABER_RADIUS_STANDARD;
|
|
saber->blade[i].lengthMax = 32;
|
|
}
|
|
saber->model = "models/weapons2/saber_reborn/saber_w.glm";
|
|
saber->skin = NULL;
|
|
saber->soundOn = G_SoundIndex( "sound/weapons/saber/enemy_saber_on.wav" );
|
|
saber->soundLoop = G_SoundIndex( "sound/weapons/saber/saberhum3.wav" );
|
|
saber->soundOff = G_SoundIndex( "sound/weapons/saber/enemy_saber_off.wav" );
|
|
saber->numBlades = 1;
|
|
saber->type = SABER_SINGLE;
|
|
saber->style = SS_NONE;
|
|
saber->maxChain = 0;//0 = use default behavior
|
|
saber->lockable = qtrue;
|
|
saber->throwable = qtrue;
|
|
saber->disarmable = qtrue;
|
|
saber->activeBlocking = qtrue;
|
|
saber->twoHanded = qfalse;
|
|
saber->forceRestrictions = 0;
|
|
saber->lockBonus = 0;
|
|
saber->parryBonus = 0;
|
|
saber->breakParryBonus = 0;
|
|
saber->disarmBonus = 0;
|
|
saber->singleBladeStyle = SS_NONE;//makes it so that you use a different style if you only have the first blade active
|
|
saber->singleBladeThrowable = qfalse;//makes it so that you can throw this saber if only the first blade is on
|
|
saber->brokenSaber1 = NULL;//if saber is actually hit by another saber, it can be cut in half/broken and will be replaced with this saber in your right hand
|
|
saber->brokenSaber2 = NULL;//if saber is actually hit by another saber, it can be cut in half/broken and will be replaced with this saber in your left hand
|
|
saber->returnDamage = qfalse;//when returning from a saber throw, it keeps spinning and doing damage
|
|
}
|
|
|
|
qboolean WP_SaberParseParms( const char *SaberName, saberInfo_t *saber, qboolean setColors )
|
|
{
|
|
const char *token;
|
|
const char *value;
|
|
const char *p;
|
|
float f;
|
|
int n;
|
|
|
|
if ( !saber )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
//Set defaults so that, if it fails, there's at least something there
|
|
WP_SaberSetDefaults( saber, setColors );
|
|
|
|
if ( !SaberName || !SaberName[0] )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
saber->name = G_NewString( SaberName );
|
|
//try to parse it out
|
|
p = SaberParms;
|
|
COM_BeginParseSession();
|
|
|
|
// look for the right saber
|
|
while ( p )
|
|
{
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( token[0] == 0 )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, SaberName ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
SkipBracedSection( &p );
|
|
}
|
|
if ( !p )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if ( G_ParseLiteral( &p, "{" ) )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
// parse the saber info block
|
|
while ( 1 )
|
|
{
|
|
token = COM_ParseExt( &p, qtrue );
|
|
if ( !token[0] )
|
|
{
|
|
gi.Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", SaberName );
|
|
return qfalse;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "}" ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//saber fullName
|
|
if ( !Q_stricmp( token, "name" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->fullName = G_NewString( value );
|
|
continue;
|
|
}
|
|
|
|
//saber type
|
|
if ( !Q_stricmp( token, "saberType" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
int saberType = GetIDForString( SaberTable, value );
|
|
if ( saberType >= SABER_SINGLE && saberType <= NUM_SABERS )
|
|
{
|
|
saber->type = (saberType_t)saberType;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//saber hilt
|
|
if ( !Q_stricmp( token, "saberModel" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->model = G_NewString( value );
|
|
continue;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "customSkin" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->skin = G_NewString( value );
|
|
continue;
|
|
}
|
|
|
|
//on sound
|
|
if ( !Q_stricmp( token, "soundOn" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->soundOn = G_SoundIndex( G_NewString( value ) );
|
|
continue;
|
|
}
|
|
|
|
//loop sound
|
|
if ( !Q_stricmp( token, "soundLoop" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->soundLoop = G_SoundIndex( G_NewString( value ) );
|
|
continue;
|
|
}
|
|
|
|
//off sound
|
|
if ( !Q_stricmp( token, "soundOff" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->soundOff = G_SoundIndex( G_NewString( value ) );
|
|
continue;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "numBlades" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
if ( n < 1 || n >= MAX_BLADES )
|
|
{
|
|
G_Error( "WP_SaberParseParms: saber %s has illegal number of blades (%d) max: %d", SaberName, n, MAX_BLADES );
|
|
continue;
|
|
}
|
|
saber->numBlades = n;
|
|
continue;
|
|
}
|
|
|
|
// saberColor
|
|
if ( !Q_stricmpn( token, "saberColor", 10 ) )
|
|
{
|
|
if ( !setColors )
|
|
{//don't actually want to set the colors
|
|
//read the color out anyway just to advance the *p pointer
|
|
COM_ParseString( &p, &value );
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (strlen(token)==10)
|
|
{
|
|
n = -1;
|
|
}
|
|
else if (strlen(token)==11)
|
|
{
|
|
n = atoi(&token[10])-1;
|
|
if (n > 7 || n < 1 )
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberColor '%s' in %s\n", token, SaberName );
|
|
//read the color out anyway just to advance the *p pointer
|
|
COM_ParseString( &p, &value );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberColor '%s' in %s\n", token, SaberName );
|
|
//read the color out anyway just to advance the *p pointer
|
|
COM_ParseString( &p, &value );
|
|
continue;
|
|
}
|
|
|
|
if ( COM_ParseString( &p, &value ) ) //read the color
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (n==-1)
|
|
{//NOTE: this fills in the rest of the blades with the same color by default
|
|
saber_colors_t color = TranslateSaberColor( value );
|
|
for ( n = 0; n < MAX_BLADES; n++ )
|
|
{
|
|
saber->blade[n].color = color;
|
|
}
|
|
} else
|
|
{
|
|
saber->blade[n].color = TranslateSaberColor( value );
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//saber length
|
|
if ( !Q_stricmpn( token, "saberLength", 11 ) )
|
|
{
|
|
if (strlen(token)==11)
|
|
{
|
|
n = -1;
|
|
}
|
|
else if (strlen(token)==12)
|
|
{
|
|
n = atoi(&token[11])-1;
|
|
if (n > 7 || n < 1 )
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberLength '%s' in %s\n", token, SaberName );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberLength '%s' in %s\n", token, SaberName );
|
|
continue;
|
|
}
|
|
|
|
if ( COM_ParseFloat( &p, &f ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
//cap
|
|
if ( f < 4.0f )
|
|
{
|
|
f = 4.0f;
|
|
}
|
|
|
|
if (n==-1)
|
|
{//NOTE: this fills in the rest of the blades with the same length by default
|
|
for ( n = 0; n < MAX_BLADES; n++ )
|
|
{
|
|
saber->blade[n].lengthMax = f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
saber->blade[n].lengthMax = f;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//blade radius
|
|
if ( !Q_stricmpn( token, "saberRadius", 11 ) )
|
|
{
|
|
if (strlen(token)==11)
|
|
{
|
|
n = -1;
|
|
}
|
|
else if (strlen(token)==12)
|
|
{
|
|
n = atoi(&token[11])-1;
|
|
if (n > 7 || n < 1 )
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberRadius '%s' in %s\n", token, SaberName );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gi.Printf( S_COLOR_YELLOW"WARNING: bad saberRadius '%s' in %s\n", token, SaberName );
|
|
continue;
|
|
}
|
|
|
|
if ( COM_ParseFloat( &p, &f ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
//cap
|
|
if ( f < 0.25f )
|
|
{
|
|
f = 0.25f;
|
|
}
|
|
if (n==-1)
|
|
{//NOTE: this fills in the rest of the blades with the same length by default
|
|
for ( n = 0; n < MAX_BLADES; n++ )
|
|
{
|
|
saber->blade[n].radius = f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
saber->blade[n].radius = f;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//locked saber style
|
|
if ( !Q_stricmp( token, "saberStyle" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->style = TranslateSaberStyle( value );
|
|
continue;
|
|
}
|
|
|
|
//maxChain
|
|
if ( !Q_stricmp( token, "maxChain" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->maxChain = n;
|
|
continue;
|
|
}
|
|
|
|
//lockable
|
|
if ( !Q_stricmp( token, "lockable" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->lockable = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//throwable
|
|
if ( !Q_stricmp( token, "throwable" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->throwable = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//disarmable
|
|
if ( !Q_stricmp( token, "disarmable" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->disarmable = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//active blocking
|
|
if ( !Q_stricmp( token, "blocking" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->activeBlocking = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//twoHanded
|
|
if ( !Q_stricmp( token, "twoHanded" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->twoHanded = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//force power restrictions
|
|
if ( !Q_stricmp( token, "forceRestrict" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
int fp = GetIDForString( FPTable, value );
|
|
if ( fp >= FP_FIRST && fp < NUM_FORCE_POWERS )
|
|
{
|
|
saber->forceRestrictions |= (1<<fp);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//lockBonus
|
|
if ( !Q_stricmp( token, "lockBonus" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->lockBonus = n;
|
|
continue;
|
|
}
|
|
|
|
//parryBonus
|
|
if ( !Q_stricmp( token, "parryBonus" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->parryBonus = n;
|
|
continue;
|
|
}
|
|
|
|
//breakParryBonus
|
|
if ( !Q_stricmp( token, "breakParryBonus" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->breakParryBonus = n;
|
|
continue;
|
|
}
|
|
|
|
//disarmBonus
|
|
if ( !Q_stricmp( token, "disarmBonus" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->disarmBonus = n;
|
|
continue;
|
|
}
|
|
|
|
//single blade saber style
|
|
if ( !Q_stricmp( token, "singleBladeStyle" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->singleBladeStyle = TranslateSaberStyle( value );
|
|
continue;
|
|
}
|
|
|
|
//single blade throwable
|
|
if ( !Q_stricmp( token, "singleBladeThrowable" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->singleBladeThrowable = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
//broken replacement saber1 (right hand)
|
|
if ( !Q_stricmp( token, "brokenSaber1" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->brokenSaber1 = G_NewString( value );
|
|
continue;
|
|
}
|
|
|
|
//broken replacement saber2 (left hand)
|
|
if ( !Q_stricmp( token, "brokenSaber2" ) )
|
|
{
|
|
if ( COM_ParseString( &p, &value ) )
|
|
{
|
|
continue;
|
|
}
|
|
saber->brokenSaber2 = G_NewString( value );
|
|
continue;
|
|
}
|
|
|
|
//spins and does damage on return from saberthrow
|
|
if ( !Q_stricmp( token, "returnDamage" ) )
|
|
{
|
|
if ( COM_ParseInt( &p, &n ) )
|
|
{
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
saber->returnDamage = ((qboolean)(n!=0));
|
|
continue;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "notInMP" ) )
|
|
{//ignore this
|
|
SkipRestOfLine( &p );
|
|
continue;
|
|
}
|
|
|
|
gi.Printf( "WARNING: unknown keyword '%s' while parsing '%s'\n", token, SaberName );
|
|
SkipRestOfLine( &p );
|
|
}
|
|
|
|
//FIXME: precache the saberModel(s)?
|
|
|
|
if ( saber->type == SABER_SITH_SWORD )
|
|
{//precache all the sith sword sounds
|
|
Saber_SithSwordPrecache();
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
void WP_RemoveSaber( gentity_t *ent, int saberNum )
|
|
{
|
|
if ( !ent || !ent->client )
|
|
{
|
|
return;
|
|
}
|
|
//reset everything for this saber just in case
|
|
WP_SaberSetDefaults( &ent->client->ps.saber[saberNum] );
|
|
|
|
ent->client->ps.dualSabers = qfalse;
|
|
ent->client->ps.saber[saberNum].Deactivate();
|
|
ent->client->ps.saber[saberNum].SetLength( 0.0f );
|
|
if ( ent->weaponModel[saberNum] > 0 )
|
|
{
|
|
gi.G2API_SetSkin( &ent->ghoul2[ent->weaponModel[saberNum]], -1, 0 );
|
|
gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[saberNum] );
|
|
ent->weaponModel[saberNum] = -1;
|
|
}
|
|
if ( ent->client->ps.saberAnimLevel == SS_DUAL
|
|
|| ent->client->ps.saberAnimLevel == SS_STAFF )
|
|
{//change to the style to the default
|
|
for ( int i = SS_FAST; i < SS_NUM_SABER_STYLES; i++ )
|
|
{
|
|
if ( (ent->client->ps.saberStylesKnown&(1<<i)) )
|
|
{
|
|
ent->client->ps.saberAnimLevel = i;
|
|
if ( ent->s.number < MAX_CLIENTS )
|
|
{
|
|
cg.saberAnimLevelPending = ent->client->ps.saberAnimLevel;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WP_SetSaber( gentity_t *ent, int saberNum, char *saberName )
|
|
{
|
|
if ( !ent || !ent->client )
|
|
{
|
|
return;
|
|
}
|
|
if ( Q_stricmp( "none", saberName ) == 0 || Q_stricmp( "remove", saberName ) == 0 )
|
|
{
|
|
WP_RemoveSaber( ent, saberNum );
|
|
return;
|
|
}
|
|
if ( ent->weaponModel[saberNum] > 0 )
|
|
{
|
|
gi.G2API_RemoveGhoul2Model(ent->ghoul2, ent->weaponModel[saberNum]);
|
|
ent->weaponModel[saberNum] = -1;
|
|
}
|
|
WP_SaberParseParms( saberName, &ent->client->ps.saber[saberNum] );//get saber info
|
|
if ( ent->client->ps.saber[saberNum].style )
|
|
{
|
|
ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[saberNum].style);
|
|
}
|
|
if ( saberNum == 1 && ent->client->ps.saber[1].twoHanded )
|
|
{//not allowed to use a 2-handed saber as second saber
|
|
WP_RemoveSaber( ent, saberNum );
|
|
return;
|
|
}
|
|
G_ModelIndex( ent->client->ps.saber[saberNum].model );
|
|
WP_SaberInitBladeData( ent );
|
|
//int boltNum = ent->handRBolt;
|
|
if ( saberNum == 1 )
|
|
{
|
|
ent->client->ps.dualSabers = qtrue;
|
|
//boltNum = ent->handLBolt;
|
|
}
|
|
WP_SaberAddG2SaberModels( ent, saberNum );
|
|
ent->client->ps.saber[saberNum].SetLength( 0.0f );
|
|
ent->client->ps.saber[saberNum].Activate();
|
|
|
|
if ( ent->client->ps.saber[saberNum].style != SS_NONE )
|
|
{//change to the style we're supposed to be using
|
|
ent->client->ps.saberAnimLevel = ent->client->ps.saber[saberNum].style;
|
|
ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saberAnimLevel);
|
|
if ( ent->s.number < MAX_CLIENTS )
|
|
{
|
|
cg.saberAnimLevelPending = ent->client->ps.saberAnimLevel;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WP_SaberSetColor( gentity_t *ent, int saberNum, int bladeNum, char *colorName )
|
|
{
|
|
if ( !ent || !ent->client )
|
|
{
|
|
return;
|
|
}
|
|
ent->client->ps.saber[saberNum].blade[bladeNum].color = TranslateSaberColor( colorName );
|
|
}
|
|
|
|
extern void WP_SetSaberEntModelSkin( gentity_t *ent, gentity_t *saberent );
|
|
qboolean WP_BreakSaber( gentity_t *ent, const char *surfName, saberType_t saberType )
|
|
{//Make sure there *is* one specified and not using dualSabers
|
|
if ( ent == NULL || ent->client == NULL )
|
|
{//invalid ent or client
|
|
return qfalse;
|
|
}
|
|
|
|
if ( ent->s.number < MAX_CLIENTS )
|
|
{//player
|
|
//if ( g_spskill->integer < 3 )
|
|
{//only on the hardest level?
|
|
//FIXME: add a cvar?
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
if ( ent->health <= 0 )
|
|
{//not if they're dead
|
|
return qfalse;
|
|
}
|
|
|
|
if ( ent->client->ps.weapon != WP_SABER )
|
|
{//not holding saber
|
|
return qfalse;
|
|
}
|
|
|
|
if ( ent->client->ps.dualSabers )
|
|
{//FIXME: handle this?
|
|
return qfalse;
|
|
}
|
|
|
|
if ( !ent->client->ps.saber[0].brokenSaber1 )
|
|
{//not breakable into another type of saber
|
|
return qfalse;
|
|
}
|
|
|
|
if ( PM_SaberInStart( ent->client->ps.saberMove ) //in a start
|
|
|| PM_SaberInTransition( ent->client->ps.saberMove ) //in a transition
|
|
|| PM_SaberInAttack( ent->client->ps.saberMove ) )//in an attack
|
|
{//don't break when in the middle of an attack
|
|
return qfalse;
|
|
}
|
|
|
|
if ( Q_stricmpn( "w_", surfName, 2 )
|
|
&& Q_stricmpn( "saber_", surfName, 6 ) //hack because using mod-community made saber
|
|
&& Q_stricmp( "cylinder01", surfName ) )//hack because using mod-community made saber
|
|
{//didn't hit my weapon
|
|
return qfalse;
|
|
}
|
|
|
|
//Sith Sword should ALWAYS do this
|
|
if ( saberType != SABER_SITH_SWORD && Q_irand( 0, 50 ) )//&& Q_irand( 0, 10 ) )
|
|
{//10% chance - FIXME: extern this, too?
|
|
return qfalse;
|
|
}
|
|
|
|
//break it
|
|
char *replacementSaber1 = G_NewString( ent->client->ps.saber[0].brokenSaber1 );
|
|
char *replacementSaber2 = G_NewString( ent->client->ps.saber[0].brokenSaber2 );
|
|
int i, originalNumBlades = ent->client->ps.saber[0].numBlades;
|
|
qboolean broken = qfalse;
|
|
saber_colors_t colors[MAX_BLADES];
|
|
|
|
//store the colors
|
|
for ( i = 0; i < MAX_BLADES; i++ )
|
|
{
|
|
colors[i] = ent->client->ps.saber[0].blade[i].color;
|
|
}
|
|
|
|
//FIXME: chance of dropping the right-hand one? Based on damage, or...?
|
|
//FIXME: sound & effect when this happens, and send them into a broken parry?
|
|
|
|
//remove saber[0], replace with replacementSaber1
|
|
if ( replacementSaber1 )
|
|
{
|
|
WP_RemoveSaber( ent, 0 );
|
|
WP_SetSaber( ent, 0, replacementSaber1 );
|
|
for ( i = 0; i < ent->client->ps.saber[0].numBlades; i++ )
|
|
{
|
|
ent->client->ps.saber[0].blade[i].color = colors[i];
|
|
}
|
|
broken = qtrue;
|
|
//change my saberent's model and skin to match my new right-hand saber
|
|
WP_SetSaberEntModelSkin( ent, &g_entities[ent->client->ps.saberEntityNum] );
|
|
}
|
|
|
|
if ( originalNumBlades <= 1 )
|
|
{//nothing to split off
|
|
//FIXME: handle this?
|
|
}
|
|
else
|
|
{
|
|
//remove saber[1], replace with replacementSaber2
|
|
if ( replacementSaber2 )
|
|
{//FIXME: 25% chance that it just breaks - just spawn the second saber piece and toss it away immediately, can't be picked up.
|
|
//shouldn't be one in this hand, but just in case, remove it
|
|
WP_RemoveSaber( ent, 1 );
|
|
WP_SetSaber( ent, 1, replacementSaber2 );
|
|
|
|
//put the remainder of the original saber's blade colors onto this saber's blade(s)
|
|
for ( i = ent->client->ps.saber[0].numBlades; i < MAX_BLADES; i++ )
|
|
{
|
|
ent->client->ps.saber[1].blade[i-ent->client->ps.saber[0].numBlades].color = colors[i];
|
|
}
|
|
broken = qtrue;
|
|
}
|
|
}
|
|
return broken;
|
|
}
|
|
|
|
void WP_SaberLoadParms( void )
|
|
{
|
|
int len, totallen, saberExtFNLen, fileCnt, i;
|
|
char *buffer, *holdChar, *marker;
|
|
char saberExtensionListBuf[2048]; // The list of file names read in
|
|
|
|
//gi.Printf( "Parsing *.sab saber definitions\n" );
|
|
|
|
//set where to store the first one
|
|
totallen = 0;
|
|
marker = SaberParms;
|
|
marker[0] = '\0';
|
|
|
|
//now load in the .sab definitions
|
|
fileCnt = gi.FS_GetFileList("ext_data/sabers", ".sab", saberExtensionListBuf, sizeof(saberExtensionListBuf) );
|
|
|
|
holdChar = saberExtensionListBuf;
|
|
for ( i = 0; i < fileCnt; i++, holdChar += saberExtFNLen + 1 )
|
|
{
|
|
saberExtFNLen = strlen( holdChar );
|
|
|
|
len = gi.FS_ReadFile( va( "ext_data/sabers/%s", holdChar), (void **) &buffer );
|
|
|
|
if ( len == -1 )
|
|
{
|
|
gi.Printf( "WP_SaberLoadParms: error reading %s\n", holdChar );
|
|
}
|
|
else
|
|
{
|
|
if ( totallen && *(marker-1) == '}' )
|
|
{//don't let it end on a } because that should be a stand-alone token
|
|
strcat( marker, " " );
|
|
totallen++;
|
|
marker++;
|
|
}
|
|
len = COM_Compress( buffer );
|
|
|
|
if ( totallen + len >= MAX_SABER_DATA_SIZE ) {
|
|
G_Error( "WP_SaberLoadParms: ran out of space before reading %s\n(you must make the .npc files smaller)", holdChar );
|
|
}
|
|
strcat( marker, buffer );
|
|
gi.FS_FreeFile( buffer );
|
|
|
|
totallen += len;
|
|
marker += len;
|
|
}
|
|
}
|
|
}
|