summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--armour/bsuit.armour32
-rw-r--r--armour/helmet.armour16
-rw-r--r--armour/larmour.armour87
-rw-r--r--src/cgame/cg_buildable.c7
-rw-r--r--src/cgame/cg_consolecmds.c8
-rw-r--r--src/cgame/cg_draw.c11
-rw-r--r--src/cgame/cg_ents.c104
-rw-r--r--src/cgame/cg_event.c100
-rw-r--r--src/cgame/cg_local.h3
-rw-r--r--src/cgame/cg_main.c9
-rw-r--r--src/cgame/cg_public.h6
-rw-r--r--src/cgame/cg_tutorial.c38
-rw-r--r--src/cgame/cg_view.c19
-rw-r--r--src/cgame/cg_weapons.c23
-rw-r--r--src/game/bg_misc.c23
-rw-r--r--src/game/bg_pmove.c215
-rw-r--r--src/game/bg_public.h33
-rw-r--r--src/game/g_active.c390
-rw-r--r--src/game/g_buildable.c550
-rw-r--r--src/game/g_client.c126
-rw-r--r--src/game/g_cmds.c356
-rw-r--r--src/game/g_combat.c71
-rw-r--r--src/game/g_local.h58
-rw-r--r--src/game/g_main.c73
-rw-r--r--src/game/g_misc.c2
-rw-r--r--src/game/g_missile.c16
-rw-r--r--src/game/g_ptr.c2
-rw-r--r--src/game/g_weapon.c667
-rw-r--r--src/game/tremulous.h175
-rw-r--r--src/qcommon/q_shared.h10
-rw-r--r--src/server/server.h2
-rw-r--r--src/server/sv_client.c4
-rw-r--r--src/server/sv_init.c4
-rw-r--r--src/server/sv_main.c2
-rw-r--r--src/tools/lcc/lburg/gram.c2086
-rw-r--r--src/ui/ui_atoms.c4
-rw-r--r--src/ui/ui_main.c15
-rw-r--r--src/ui/ui_shared.c29
-rw-r--r--ui/ingame.txt1
-rw-r--r--ui/ingame_options.menu21
-rw-r--r--ui/say.menu7
-rw-r--r--ui/tremulous_common_hud.h9
-rw-r--r--ui/tremulous_human_hud.menu1
43 files changed, 3672 insertions, 1743 deletions
diff --git a/armour/bsuit.armour b/armour/bsuit.armour
new file mode 100644
index 00000000..93ab9411
--- /dev/null
+++ b/armour/bsuit.armour
@@ -0,0 +1,32 @@
+//entire body ;)
+{
+ minHeight 0.0
+ maxHeight 0.8
+ minAngle 0
+ maxAngle 360
+ modifier 0.2
+}
+{
+ minHeight 0.8
+ maxHeight 1.0
+ minAngle 0
+ maxAngle 360
+ modifier 0.27
+}
+
+{
+ crouch
+ minHeight 0.0
+ maxHeight 0.8
+ minAngle 0
+ maxAngle 360
+ modifier 0.2
+}
+{
+ crouch
+ minHeight 0.8
+ maxHeight 1.0
+ minAngle 0
+ maxAngle 360
+ modifier 0.27
+}
diff --git a/armour/helmet.armour b/armour/helmet.armour
new file mode 100644
index 00000000..a19785a2
--- /dev/null
+++ b/armour/helmet.armour
@@ -0,0 +1,16 @@
+//head
+{
+ minHeight 0.8
+ maxHeight 1.0
+ minAngle 0
+ maxAngle 360
+ modifier 0.4
+}
+{
+ minHeight 0.8
+ maxHeight 1.0
+ minAngle 0
+ maxAngle 360
+ modifer 0.4
+ crouch
+}
diff --git a/armour/larmour.armour b/armour/larmour.armour
new file mode 100644
index 00000000..745abd17
--- /dev/null
+++ b/armour/larmour.armour
@@ -0,0 +1,87 @@
+//chest, standing
+{
+ minHeight 0.5
+ maxHeight 0.8
+ minAngle 130
+ maxAngle 230
+ modifier 0.35
+}
+//back, standing
+{
+ minHeight 0.5
+ maxHeight 0.8
+ minAngle 310
+ maxAngle 50
+ modifier 0.4
+}
+
+//left arm, standing
+{
+ minHeight 0.5
+ maxHeight 0.8
+ minAngle 50
+ maxAngle 130
+ modifier 0.3
+}
+//right arm, standing
+{
+ minHeight 0.5
+ maxHeight 0.8
+ minAngle 230
+ maxAngle 310
+ modifier 0.3
+}
+//legs, standing
+{
+ minHeight 0.0
+ maxHeight 0.5
+ minAngle 0
+ maxAngle 360
+ modifier 0.3
+}
+
+//chest, crouching
+{
+ minHeight 0.2
+ maxHeight 0.8
+ minAngle 130
+ maxAngle 230
+ modifier 0.3
+ crouch
+}
+//back, crouching
+{
+ minHeight 0.2
+ maxHeight 0.8
+ minAngle 310
+ maxAngle 50
+ modifier 0.4
+ crouch
+}
+//left arm, crouching
+{
+ minHeight 0.2
+ maxHeight 0.8
+ minAngle 50
+ maxAngle 130
+ modifier 0.25
+ crouch
+}
+//right arm, crouching
+{
+ minHeight 0.2
+ maxHeight 0.8
+ minAngle 230
+ maxAngle 310
+ modifier 0.25
+ crouch
+}
+//legs, crouching
+{
+ minHeight 0.0
+ maxHeight 0.3
+ minAngle 0
+ maxAngle 360
+ modifier 0.25
+ crouch
+}
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c
index b8d03a48..0a1f3877 100644
--- a/src/cgame/cg_buildable.c
+++ b/src/cgame/cg_buildable.c
@@ -904,6 +904,7 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
qboolean visible = qfalse;
vec3_t mins, maxs;
entityState_t *hit;
+ int anim;
if( BG_FindTeamForBuildable( es->modelindex ) == BIT_ALIENS )
bs = &cgs.alienBuildStat;
@@ -922,6 +923,12 @@ static void CG_BuildableStatusDisplay( centity_t *cent )
// trace for center point
BG_FindBBoxForBuildable( es->modelindex, mins, maxs );
+ // hack for shrunken barricades
+ anim = es->torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT );
+ if( es->modelindex == BA_A_BARRICADE &&
+ ( anim == BANIM_DESTROYED || !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) )
+ maxs[ 2 ] = (int)( maxs[ 2 ] * BARRICADE_SHRINKPROP );
+
VectorCopy( cent->lerpOrigin, origin );
// center point
diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c
index a44a5497..697b0e98 100644
--- a/src/cgame/cg_consolecmds.c
+++ b/src/cgame/cg_consolecmds.c
@@ -279,10 +279,10 @@ void CG_InitConsoleCommands( void )
// forwarded to the server after they are not recognized locally
//
trap_AddCommand( "kill" );
- trap_AddCommand( "messagemode" );
- trap_AddCommand( "messagemode2" );
- trap_AddCommand( "messagemode3" );
- trap_AddCommand( "messagemode4" );
+ trap_AddCommand( "ui_messagemode" );
+ trap_AddCommand( "ui_messagemode2" );
+ trap_AddCommand( "ui_messagemode3" );
+ trap_AddCommand( "ui_messagemode4" );
trap_AddCommand( "say" );
trap_AddCommand( "say_team" );
trap_AddCommand( "tell" );
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 47e7d477..c8f75da0 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -593,14 +593,15 @@ CG_DrawPlayerBoosterBolt
static void CG_DrawPlayerBoosterBolt( rectDef_t *rect, vec4_t color, qhandle_t shader )
{
vec4_t localColor;
+ playerState_t *ps = &cg.snap->ps;
Vector4Copy( color, localColor );
- if( cg.boostedTime >= 0 )
+ if( ps->stats[ STAT_STATE ] & SS_BOOSTED )
{
- if( ( cg.time - cg.boostedTime ) > BOOST_TIME - 3000 )
+ if( ps->stats[ STAT_MISC2 ] < 3000 )
{
- qboolean flash = ( cg.time / 500 ) % 2;
+ qboolean flash = ( ps->stats[ STAT_MISC2 ] / 500 ) % 2;
if( flash )
localColor[ 3 ] = 1.0f;
@@ -1986,7 +1987,7 @@ static void CG_DrawCrosshair( void )
wi = &cg_weapons[ cg.snap->ps.weapon ];
- w = h = wi->crossHairSize;
+ w = h = wi->crossHairSize * cg_crosshairSize.value;
w *= cgDC.aspectScale;
@@ -3017,5 +3018,3 @@ void CG_DrawActive( stereoFrame_t stereoView )
CG_Draw2D( );
}
-
-
diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c
index 17f1a7d3..c6597b6b 100644
--- a/src/cgame/cg_ents.c
+++ b/src/cgame/cg_ents.c
@@ -216,6 +216,8 @@ Add continuous entity effects, like local entity emission and lighting
*/
static void CG_EntityEffects( centity_t *cent )
{
+ int i;
+
// update sound origins
CG_SetEntitySoundPosition( cent );
@@ -251,7 +253,7 @@ static void CG_EntityEffects( centity_t *cent )
if( CG_IsTrailSystemValid( &cent->muzzleTS ) )
{
- //FIXME hack to prevent tesla trails reaching too far
+ //FIXME hack to prevent tesla trails reaching too far
if( cent->currentState.eType == ET_BUILDABLE )
{
vec3_t front, back;
@@ -266,6 +268,33 @@ static void CG_EntityEffects( centity_t *cent )
if( cg.time > cent->muzzleTSDeathTime && CG_IsTrailSystemValid( &cent->muzzleTS ) )
CG_DestroyTrailSystem( &cent->muzzleTS );
}
+
+
+ if( cent->currentState.eType == ET_PLAYER )
+ {
+ centity_t *pcent = cent;;
+
+ // predicted entity doesn't have local cgame vars
+ if( cent == &cg.predictedPlayerEntity )
+ pcent = &cg_entities[ cg.clientNum ];
+
+ for( i = 0; i <= 2; i++ )
+ {
+ if( CG_IsTrailSystemValid( &pcent->level2ZapTS[ i ] ) )
+ {
+ vec3_t front, back;
+
+ CG_AttachmentPoint( &pcent->level2ZapTS[ i ]->frontAttachment, front );
+ CG_AttachmentPoint( &pcent->level2ZapTS[ i ]->backAttachment, back );
+
+ if( cg.time - pcent->level2ZapTime > 100 ||
+ Distance( front, back ) > LEVEL2_AREAZAP_CUTOFF )
+ {
+ CG_DestroyTrailSystem( &pcent->level2ZapTS[ i ] );
+ }
+ }
+ }
+ }
}
@@ -780,61 +809,6 @@ static void CG_LightFlare( centity_t *cent )
/*
=========================
-CG_Lev2ZapChain
-=========================
-*/
-static void CG_Lev2ZapChain( centity_t *cent )
-{
- int i;
- entityState_t *es;
- centity_t *source = NULL, *target = NULL;
-
- es = &cent->currentState;
-
- for( i = 0; i <= 2; i++ )
- {
- switch( i )
- {
- case 0:
- if( es->time <= 0 )
- continue;
-
- source = &cg_entities[ es->misc ];
- target = &cg_entities[ es->time ];
- break;
-
- case 1:
- if( es->time2 <= 0 )
- continue;
-
- source = &cg_entities[ es->time ];
- target = &cg_entities[ es->time2 ];
- break;
-
- case 2:
- if( es->constantLight <= 0 )
- continue;
-
- source = &cg_entities[ es->time2 ];
- target = &cg_entities[ es->constantLight ];
- break;
- }
-
- if( !CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
- cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
-
- if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
- {
- CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->frontAttachment, source );
- CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->backAttachment, target );
- CG_AttachToCent( &cent->level2ZapTS[ i ]->frontAttachment );
- CG_AttachToCent( &cent->level2ZapTS[ i ]->backAttachment );
- }
- }
-}
-
-/*
-=========================
CG_AdjustPositionForMover
Also called by client movement prediction code
@@ -1029,22 +1003,8 @@ CG_CEntityPVSLeave
*/
static void CG_CEntityPVSLeave( centity_t *cent )
{
- int i;
- entityState_t *es = &cent->currentState;
-
if( cg_debugPVS.integer )
CG_Printf( "Entity %d left PVS\n", cent->currentState.number );
-
- switch( es->eType )
- {
- case ET_LEV2_ZAP_CHAIN:
- for( i = 0; i <= 2; i++ )
- {
- if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
- CG_DestroyTrailSystem( &cent->level2ZapTS[ i ] );
- }
- break;
- }
}
@@ -1128,10 +1088,6 @@ static void CG_AddCEntity( centity_t *cent )
case ET_LIGHTFLARE:
CG_LightFlare( cent );
break;
-
- case ET_LEV2_ZAP_CHAIN:
- CG_Lev2ZapChain( cent );
- break;
}
}
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index 454f843e..1caba1f5 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -168,6 +168,24 @@ static void CG_Obituary( entityState_t *ent )
message = "blew himself up";
break;
+ case MOD_LEVEL3_BOUNCEBALL:
+ if( gender == GENDER_FEMALE )
+ message = "sniped herself";
+ else if( gender == GENDER_NEUTER )
+ message = "sniped itself";
+ else
+ message = "sniped himself";
+ break;
+
+ case MOD_PRIFLE:
+ if( gender == GENDER_FEMALE )
+ message = "pulse rifled herself";
+ else if( gender == GENDER_NEUTER )
+ message = "pulse rifled itself";
+ else
+ message = "pulse rifled himself";
+ break;
+
default:
if( gender == GENDER_FEMALE )
message = "killed herself";
@@ -181,7 +199,7 @@ static void CG_Obituary( entityState_t *ent )
if( message )
{
- CG_Printf( "%s" S_COLOR_WHITE " %s.\n", targetName, message );
+ CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message );
return;
}
@@ -297,12 +315,16 @@ static void CG_Obituary( entityState_t *ent )
BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
message2 = className;
break;
- case MOD_LEVEL4_CHARGE:
+ case MOD_LEVEL4_TRAMPLE:
message = "should have gotten out of the way of";
Com_sprintf( className, 64, "'s %s",
BG_FindHumanNameForClassNum( PCL_ALIEN_LEVEL4 ) );
message2 = className;
break;
+ case MOD_LEVEL4_CRUSH:
+ message = "was crushed under";
+ message2 = "'s weight";
+ break;
case MOD_POISON:
message = "should have used a medkit against";
@@ -342,7 +364,7 @@ static void CG_Obituary( entityState_t *ent )
}
// we don't know what it was
- CG_Printf( "%s died.\n", targetName );
+ CG_Printf( "%s died\n", targetName );
}
//==========================================================================
@@ -380,6 +402,60 @@ void CG_PainEvent( centity_t *cent, int health )
}
/*
+=========================
+CG_Level2Zap
+=========================
+*/
+static void CG_Level2Zap( entityState_t *es )
+{
+ int i;
+ centity_t *source = NULL, *target = NULL;
+
+ if( es->misc < 0 || es->misc >= MAX_CLIENTS )
+ return;
+
+ source = &cg_entities[ es->misc ];
+ for( i = 0; i <= 2; i++ )
+ {
+ switch( i )
+ {
+ case 0:
+ if( es->time <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time ];
+ break;
+
+ case 1:
+ if( es->time2 <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time2 ];
+ break;
+
+ case 2:
+ if( es->constantLight <= 0 )
+ continue;
+
+ target = &cg_entities[ es->constantLight ];
+ break;
+ }
+
+ if( !CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ source->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
+
+ if( CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ {
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->frontAttachment, source );
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->backAttachment, target );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->frontAttachment );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->backAttachment );
+ }
+ }
+ source->level2ZapTime = cg.time;
+}
+
+/*
==============
CG_EntityEvent
@@ -619,13 +695,13 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL1Grab );
break;
- case EV_LEV4_CHARGE_PREPARE:
- DEBUGNAME( "EV_LEV4_CHARGE_PREPARE" );
+ case EV_LEV4_TRAMPLE_PREPARE:
+ DEBUGNAME( "EV_LEV4_TRAMPLE_PREPARE" );
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargePrepare );
break;
- case EV_LEV4_CHARGE_START:
- DEBUGNAME( "EV_LEV4_CHARGE_START" );
+ case EV_LEV4_TRAMPLE_START:
+ DEBUGNAME( "EV_LEV4_TRAMPLE_START" );
//FIXME: stop cgs.media.alienL4ChargePrepare playing here
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargeStart );
break;
@@ -903,6 +979,11 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
}
break;
+ case EV_MGTURRET_SPINUP:
+ DEBUGNAME( "EV_MGTURRET_SPINUP" );
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound );
+ break;
+
case EV_OVERMIND_SPAWNS:
DEBUGNAME( "EV_OVERMIND_SPAWNS" );
if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -968,6 +1049,11 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
cg.spawnTime = cg.time;
break;
+ case EV_LEV2_ZAP:
+ DEBUGNAME( "EV_LEV2_ZAP" );
+ CG_Level2Zap( es );
+ break;
+
default:
DEBUGNAME( "UNKNOWN" );
CG_Error( "Unknown event: %i", event );
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index f70afc85..50381ff2 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -655,6 +655,7 @@ typedef struct centity_s
qboolean entityPSMissing;
trailSystem_t *level2ZapTS[ 3 ];
+ int level2ZapTime;
trailSystem_t *muzzleTS; //used for the tesla and reactor
int muzzleTSDeathTime;
@@ -1177,6 +1178,7 @@ typedef struct
sfxHandle_t humanTalkSound;
sfxHandle_t landSound;
sfxHandle_t fallSound;
+ sfxHandle_t turretSpinupSound;
sfxHandle_t hardBounceSound1;
sfxHandle_t hardBounceSound2;
@@ -1408,6 +1410,7 @@ extern vmCvar_t cg_drawTeamOverlay;
extern vmCvar_t cg_teamOverlayUserinfo;
extern vmCvar_t cg_crosshairX;
extern vmCvar_t cg_crosshairY;
+extern vmCvar_t cg_crosshairSize;
extern vmCvar_t cg_drawStatus;
extern vmCvar_t cg_draw2D;
extern vmCvar_t cg_animSpeed;
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index bbaf914f..01234d1d 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -68,6 +68,12 @@ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
CG_DrawActiveFrame( arg0, arg1, arg2 );
return 0;
+ case CG_CROSSHAIR_PLAYER:
+ return CG_CrosshairPlayer( );
+
+ case CG_LAST_ATTACKER:
+ return CG_LastAttacker( );
+
case CG_KEY_EVENT:
CG_KeyEvent( arg0, arg1 );
return 0;
@@ -124,6 +130,7 @@ vmCvar_t cg_drawCrosshairNames;
vmCvar_t cg_drawRewards;
vmCvar_t cg_crosshairX;
vmCvar_t cg_crosshairY;
+vmCvar_t cg_crosshairSize;
vmCvar_t cg_draw2D;
vmCvar_t cg_drawStatus;
vmCvar_t cg_animSpeed;
@@ -263,6 +270,7 @@ static cvarTable_t cvarTable[ ] =
{ &cg_drawRewards, "cg_drawRewards", "1", CVAR_ARCHIVE },
{ &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE },
{ &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE },
+ { &cg_crosshairSize, "cg_crosshairSize", "1", CVAR_ARCHIVE },
{ &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE },
{ &cg_simpleItems, "cg_simpleItems", "0", CVAR_ARCHIVE },
{ &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE },
@@ -667,6 +675,7 @@ static void CG_RegisterSounds( void )
cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/tracer.wav", qfalse );
cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse );
+ cgs.media.turretSpinupSound = trap_S_RegisterSound( "sound/buildables/mgturret/spinup.wav", qfalse );
cgs.media.talkSound = trap_S_RegisterSound( "sound/misc/talk.wav", qfalse );
cgs.media.alienTalkSound = trap_S_RegisterSound( "sound/misc/alien_talk.wav", qfalse );
diff --git a/src/cgame/cg_public.h b/src/cgame/cg_public.h
index 905b1db2..4d9e965a 100644
--- a/src/cgame/cg_public.h
+++ b/src/cgame/cg_public.h
@@ -232,6 +232,12 @@ typedef enum
// Generates and draws a game scene and status information at the given time.
// If demoPlayback is set, local movement prediction will not be enabled
+ CG_CROSSHAIR_PLAYER,
+ // int (*CG_CrosshairPlayer)( void );
+
+ CG_LAST_ATTACKER,
+ // int (*CG_LastAttacker)( void );
+
CG_KEY_EVENT,
// void (*CG_KeyEvent)( int key, qboolean down );
diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c
index 21242a75..bfcd03ac 100644
--- a/src/cgame/cg_tutorial.c
+++ b/src/cgame/cg_tutorial.c
@@ -36,6 +36,7 @@ static bind_t bindings[ ] =
{ "+button2", "Activate Upgrade", { -1, -1 } },
{ "+speed", "Run/Walk", { -1, -1 } },
{ "boost", "Sprint", { -1, -1 } },
+ { "+button6", "Dodge", { -1, -1 } },
{ "+moveup", "Jump", { -1, -1 } },
{ "+movedown", "Crouch", { -1, -1 } },
{ "+attack", "Primary Attack", { -1, -1 } },
@@ -340,7 +341,7 @@ static void CG_AlienLevel4Text( char *text, playerState_t *ps )
CG_KeyNameForCommand( "+attack" ) ) );
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Hold down and release %s to charge\n",
+ va( "Hold down and release %s to trample\n",
CG_KeyNameForCommand( "+button5" ) ) );
}
@@ -552,14 +553,25 @@ CG_SpectatorText
*/
static void CG_SpectatorText( char *text, playerState_t *ps )
{
+ if( cgs.clientinfo[ cg.clientNum ].team != PTE_NONE )
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to spawn\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+ }
+ else
+ {
+ Q_strcat( text, MAX_TUTORIAL_TEXT,
+ va( "Press %s to join a team\n",
+ CG_KeyNameForCommand( "+attack" ) ) );
+ }
+
if( ps->pm_flags & PMF_FOLLOW )
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to return to free spectator mode\n",
+ va( "Press %s to stop following\n",
CG_KeyNameForCommand( "+button2" ) ) );
- if( CG_PlayerCount( ) > 1 )
- {
Q_strcat( text, MAX_TUTORIAL_TEXT,
va( "Press %s or ",
CG_KeyNameForCommand( "weapprev" ) ) );
@@ -567,27 +579,13 @@ static void CG_SpectatorText( char *text, playerState_t *ps )
va( "%s to change player\n",
CG_KeyNameForCommand( "weapnext" ) ) );
}
- }
- else if( ps->pm_type == PM_SPECTATOR )
+ else
{
Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to join a team\n",
- CG_KeyNameForCommand( "+attack" ) ) );
-
- if( CG_PlayerCount( ) > 0 )
- {
- Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to enter spectator follow mode\n",
+ va( "Press %s to follow a player\n",
CG_KeyNameForCommand( "+button2" ) ) );
}
}
- else
- {
- Q_strcat( text, MAX_TUTORIAL_TEXT,
- va( "Press %s to spawn\n",
- CG_KeyNameForCommand( "+attack" ) ) );
- }
-}
/*
===============
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 781e068a..ae7cf7bb 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -470,7 +470,8 @@ static void CG_OffsetFirstPersonView( void )
{
if( ps->stats[ STAT_MISC ] > 0 )
{
- float fraction = (float)ps->stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME;
+ float fraction = (float)ps->stats[ STAT_MISC ] /
+ (float)LEVEL4_TRAMPLE_CHARGE_MAX;
if( fraction > 1.0f )
fraction = 1.0f;
@@ -593,17 +594,17 @@ static void CG_OffsetFirstPersonView( void )
}
// this *feels* more realisitic for humans
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS &&
+ ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
+ cg.predictedPlayerState.pm_type == PM_JETPACK ) )
{
angles[PITCH] += cg.bobfracsin * bob2 * 0.5;
// heavy breathing effects //FIXME: sound
- if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 )
+ if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < STAMINA_BREATHING_LEVEL )
{
- float deltaBreath = (float)(
- cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 ?
- -cg.predictedPlayerState.stats[ STAT_STAMINA ] :
- cg.predictedPlayerState.stats[ STAT_STAMINA ] ) / 200.0;
+ float deltaBreath = ( cg.predictedPlayerState.stats[ STAT_STAMINA ] -
+ STAMINA_BREATHING_LEVEL ) / -250.0;
float deltaAngle = cos( (float)cg.time/150.0 ) * deltaBreath;
deltaAngle += ( deltaAngle < 0 ? -deltaAngle : deltaAngle ) * 0.5;
@@ -1067,6 +1068,10 @@ static int CG_CalcViewValues( void )
cg.xyspeed = sqrt( ps->velocity[ 0 ] * ps->velocity[ 0 ] +
ps->velocity[ 1 ] * ps->velocity[ 1 ] );
+ // the bob velocity should't get too fast to avoid jerking
+ if( cg.xyspeed > 300.f )
+ cg.xyspeed = 300.f;
+
VectorCopy( ps->origin, cg.refdef.vieworg );
if( BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLCLIMBER ) )
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index fc8dc058..a74a15f4 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -1061,12 +1061,27 @@ void CG_AddViewWeapon( playerState_t *ps )
VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[ 1 ], hand.origin );
VectorMA( hand.origin, ( cg_gun_z.value + fovOffset ), cg.refdef.viewaxis[ 2 ], hand.origin );
- if( weapon == WP_LUCIFER_CANNON && ps->stats[ STAT_MISC ] > 0 )
+ if( weapon == WP_LUCIFER_CANNON )
{
- float fraction = (float)ps->stats[ STAT_MISC ] / (float)LCANNON_TOTAL_CHARGE;
+ float fraction;
- VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 0 ], hand.origin );
- VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 1 ], hand.origin );
+ if( ps->stats[ STAT_MISC ] > 0 )
+ {
+ // vibration effect
+ fraction = (float)ps->stats[ STAT_MISC ] / (float)LCANNON_TOTAL_CHARGE;
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 0 ],
+ hand.origin );
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 1 ],
+ hand.origin );
+ }
+ else if( ps->stats[ STAT_MISC2 ] > 0 )
+ {
+ // reloading effect
+ fraction = (float)ps->stats[ STAT_MISC2 ] / 250.0f;
+ fraction = ( fraction > 1.0f ) ? 1.0f : fraction;
+ VectorMA( hand.origin, fraction * -3.0f, cg.refdef.viewaxis[ 2 ],
+ hand.origin );
+ }
}
AnglesToAxis( angles, hand.axis );
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 8a4e7a8c..04ac117d 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -500,7 +500,7 @@ buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean invertNormal;
qfalse, //qboolean creepTest;
0, //int creepSize;
- qtrue, //qboolean dccTest;
+ qfalse, //qboolean dccTest;
qtrue, //qboolean transparentTest;
qfalse //qboolean reactorTest;
},
@@ -607,7 +607,7 @@ buildableAttributes_t bg_buildableList[ ] =
BIT_HUMANS, //int team;
( 1 << WP_HBUILD )|( 1 << WP_HBUILD2 ), //weapon_t buildWeapon;
BANIM_IDLE1, //int idleAnim;
- REACTOR_ATTACK_REPEAT, //int nextthink;
+ REACTOR_ATTACK_DCC_REPEAT, //int nextthink;
REACTOR_BT, //int buildTime;
qtrue, //qboolean usable;
0, //int turretRange;
@@ -1752,7 +1752,7 @@ classAttributes_t bg_classList[ ] =
1.0f, //float airAcceleration;
6.0f, //float friction;
300.0f, //float stopSpeed;
- 270.0f, //float jumpMagnitude;
+ 310.0f, //float jumpMagnitude;
1.2f, //float knockbackScale;
{ PCL_ALIEN_LEVEL2, PCL_ALIEN_LEVEL1_UPG, PCL_NONE }, //int children[ 3 ];
LEVEL1_COST, //int cost;
@@ -1794,7 +1794,7 @@ classAttributes_t bg_classList[ ] =
1.0f, //float airAcceleration;
6.0f, //float friction;
300.0f, //float stopSpeed;
- 270.0f, //float jumpMagnitude;
+ 310.0f, //float jumpMagnitude;
1.1f, //float knockbackScale;
{ PCL_ALIEN_LEVEL2, PCL_NONE, PCL_NONE }, //int children[ 3 ];
LEVEL1_UPG_COST, //int cost;
@@ -1936,7 +1936,7 @@ classAttributes_t bg_classList[ ] =
"default", //char *skinname;
1.0f, //float shadowScale;
"alien_general_hud", //char *hudname;
- ( 1 << S3 ), //int stages
+ ( 1 << S2 )|( 1 << S3 ), //int stages
{ -32, -32, -21 }, //vec3_t mins;
{ 32, 32, 21 }, //vec3_t maxs;
{ 32, 32, 21 }, //vec3_t crouchmaxs;
@@ -2024,7 +2024,7 @@ classAttributes_t bg_classList[ ] =
{ 15, 15, 16 }, //vec3_t crouchmaxs;
{ -15, -15, -4 }, //vec3_t deadmins;
{ 15, 15, 4 }, //vec3_t deadmaxs;
- 0.0f, //float zOffset
+ -2.0f, //float zOffset
26, 12, //int viewheight, crouchviewheight;
100, //int health;
1.0f, //float fallDamage;
@@ -2082,7 +2082,7 @@ classAttributes_t bg_classList[ ] =
1.0f, //float airAcceleration;
6.0f, //float friction;
100.0f, //float stopSpeed;
- 270.0f, //float jumpMagnitude;
+ 220.0f, //float jumpMagnitude;
1.0f, //float knockbackScale;
{ PCL_NONE, PCL_NONE, PCL_NONE }, //int children[ 3 ];
0, //int cost;
@@ -3514,10 +3514,10 @@ weaponAttributes_t bg_weapons[ ] =
qtrue, //int infiniteAmmo;
qfalse, //int usesEnergy;
ABUILDER_BUILD_REPEAT,//int repeatRate1;
- ABUILDER_BUILD_REPEAT,//int repeatRate2;
+ ABUILDER_CLAW_REPEAT, //int repeatRate2;
0, //int repeatRate3;
0, //int reloadTime;
- 0.0f, //float knockbackScale;
+ ABUILDER_CLAW_K_SCALE,//float knockbackScale;
qtrue, //qboolean hasAltMode;
qfalse, //qboolean hasThirdMode;
qfalse, //qboolean canZoom;
@@ -4837,8 +4837,8 @@ char *eventnames[ ] =
"EV_BULLET", // otherEntity is the shooter
"EV_LEV1_GRAB",
- "EV_LEV4_CHARGE_PREPARE",
- "EV_LEV4_CHARGE_START",
+ "EV_LEV4_TRAMPLE_PREPARE",
+ "EV_LEV4_TRAMPLE_START",
"EV_PAIN",
"EV_DEATH1",
@@ -5856,4 +5856,3 @@ void BG_ClientListParse( clientList_t *list, const char *s )
sscanf( s, "%x%x", &list->hi, &list->lo );
}
-
diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c
index 80ca7715..e7915b96 100644
--- a/src/game/bg_pmove.c
+++ b/src/game/bg_pmove.c
@@ -260,9 +260,15 @@ static void PM_Friction( void )
if( !( pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) )
{
float stopSpeed = BG_FindStopSpeedForClass( pm->ps->stats[ STAT_PCLASS ] );
+ float friction = BG_FindFrictionForClass( pm->ps->stats[ STAT_PCLASS ] );
+
+ // when landing a dodge, extra friction
+ if( pm->ps->pm_flags & PMF_TIME_LAND )
+ friction *= 1.f + HUMAN_LAND_FRICTION *
+ pm->ps->pm_time / HUMAN_DODGE_TIMEOUT;
control = speed < stopSpeed ? stopSpeed : speed;
- drop += control * BG_FindFrictionForClass( pm->ps->stats[ STAT_PCLASS ] ) * pml.frametime;
+ drop += control * friction * pml.frametime;
}
}
}
@@ -387,11 +393,19 @@ static float PM_CmdScale( usercmd_t *cmd )
else
modifier *= CREEP_MODIFIER;
}
+ if( pm->ps->stats[ STAT_STATE ] & SS_POISONCLOUDED )
+ {
+ if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, pm->ps->stats ) ||
+ BG_InventoryContainsUpgrade( UP_BATTLESUIT, pm->ps->stats ) )
+ modifier *= PCLOUD_ARMOUR_MODIFIER;
+ else
+ modifier *= PCLOUD_MODIFIER;
+ }
}
if( pm->ps->weapon == WP_ALEVEL4 && pm->ps->pm_flags & PMF_CHARGE )
- modifier *= ( 1.0f + ( pm->ps->stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME ) *
- ( LEVEL4_CHARGE_SPEED - 1.0f ) );
+ modifier *= ( 1.0f + ( pm->ps->stats[ STAT_MISC ] /
+ (float)LEVEL4_TRAMPLE_CHARGE_MAX ) * ( LEVEL4_TRAMPLE_SPEED - 1.0f ) );
//slow player if charging up for a pounce
if( ( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG ) &&
@@ -510,12 +524,22 @@ static qboolean PM_CheckPounce( void )
pm->ps->weapon != WP_ALEVEL3_UPG )
return qfalse;
+ // we were pouncing, but we've landed
+ if( pm->ps->groundEntityNum != ENTITYNUM_NONE &&
+ ( pm->ps->pm_flags & PMF_CHARGE ) )
+ {
+ pm->ps->pm_flags &= ~PMF_CHARGE;
+ return qfalse;
+ }
+
+ // we're building up for a pounce
if( pm->cmd.buttons & BUTTON_ATTACK2 )
{
pm->ps->pm_flags &= ~PMF_CHARGE;
return qfalse;
}
+ // already a pounce in progress
if( pm->ps->pm_flags & PMF_CHARGE )
return qfalse;
@@ -660,6 +684,10 @@ static qboolean PM_CheckJump( void )
{
vec3_t normal;
+ // don't rejump after a dodge or jump
+ if( pm->ps->pm_flags & PMF_TIME_LAND )
+ return qfalse;
+
if( BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] ) == 0.0f )
return qfalse;
@@ -709,7 +737,7 @@ static qboolean PM_CheckJump( void )
// take some stamina off
if( pm->ps->stats[ STAT_PTEAM ] == PTE_HUMANS )
- pm->ps->stats[ STAT_STAMINA ] -= 500;
+ pm->ps->stats[ STAT_STAMINA ] -= STAMINA_JUMP_TAKE;
pm->ps->groundEntityNum = ENTITYNUM_NONE;
@@ -789,6 +817,108 @@ static qboolean PM_CheckWaterJump( void )
return qtrue;
}
+/*
+==================
+PM_CheckDodge
+
+Starts a human dodge or sprint
+==================
+*/
+static qboolean PM_CheckDodge( void )
+{
+ vec3_t right, forward, velocity = { 0.0f, 0.0f, 0.0f };
+ float jump;
+ int i;
+
+ if( pm->ps->stats[ STAT_PTEAM ] != PTE_HUMANS )
+ return qfalse;
+
+ // Landed a dodge
+ if( ( pm->ps->pm_flags & PMF_CHARGE ) &&
+ pm->ps->groundEntityNum != ENTITYNUM_NONE )
+ {
+ pm->ps->pm_flags = ( pm->ps->pm_flags & ~PMF_CHARGE ) | PMF_TIME_LAND;
+ pm->ps->pm_time = HUMAN_DODGE_TIMEOUT;
+ }
+
+ // Reasons to stop a sprint
+ if( pm->cmd.forwardmove <= 64 ||
+ pm->cmd.upmove < 0 ||
+ pm->ps->pm_type != PM_NORMAL )
+ pm->ps->stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
+
+ // Reasons why we can't start a dodge or sprint
+ if( !( pm->cmd.buttons & BUTTON_DODGE ) ||
+ pm->ps->pm_type != PM_NORMAL ||
+ ( pm->ps->pm_flags & PMF_CROUCH_HELD ) ||
+ pm->ps->stats[ STAT_STAMINA ] < 0 )
+ return qfalse;
+
+ // Start a sprint instead of forward leaps
+ if( pm->cmd.forwardmove > 0 )
+ {
+ if( pm->cmd.buttons & BUTTON_WALKING )
+ return qfalse;
+ pm->ps->stats[ STAT_STATE ] |= SS_SPEEDBOOST;
+ return qfalse;
+ }
+
+ // Reasons why we can't start a dodge only
+ if( pm->ps->pm_flags & ( PMF_TIME_LAND | PMF_CHARGE ) ||
+ pm->ps->groundEntityNum == ENTITYNUM_NONE )
+ return qfalse;
+
+ // Dodge direction specified with movement keys
+ if( ( !pm->cmd.rightmove && !pm->cmd.forwardmove ) || pm->cmd.upmove )
+ return qfalse;
+ AngleVectors( pm->ps->viewangles, NULL, right, NULL );
+ forward[ 0 ] = -right[ 1 ];
+ forward[ 1 ] = right[ 0 ];
+ forward[ 2 ] = 0.0f;
+
+ // Dodge magnitude is based on the jump magnitude scaled by the modifiers
+ jump = BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] );
+ if( pm->cmd.rightmove && pm->cmd.forwardmove )
+ jump *= ( 0.5f * M_SQRT2 );
+
+ // The dodge sets minimum velocity
+ if( pm->cmd.rightmove )
+ {
+ if( pm->cmd.rightmove < 0 )
+ VectorNegate( right, right );
+ VectorMA( velocity, jump * HUMAN_DODGE_SIDE_MODIFIER, right, velocity );
+ }
+ if( pm->cmd.forwardmove )
+ {
+ if( pm->cmd.forwardmove < 0 )
+ VectorNegate( forward, forward );
+ VectorMA( velocity, jump * HUMAN_DODGE_SIDE_MODIFIER, forward, velocity );
+ }
+ velocity[ 2 ] = jump * HUMAN_DODGE_UP_MODIFIER;
+
+ // Make sure client has minimum velocity
+ for( i = 0; i < 3; i++ )
+ {
+ if( ( velocity[ i ] < 0.0f &&
+ pm->ps->velocity[ i ] > velocity[ i ] ) ||
+ ( velocity[ i ] > 0.0f &&
+ pm->ps->velocity[ i ] < velocity[ i ] ) )
+ pm->ps->velocity[ i ] = velocity[ i ];
+ }
+
+ // Jumped away
+ pml.groundPlane = qfalse;
+ pml.walking = qfalse;
+ pm->ps->groundEntityNum = ENTITYNUM_NONE;
+ pm->ps->pm_flags |= PMF_CHARGE;
+ pm->ps->stats[ STAT_STAMINA ] -= STAMINA_DODGE_TAKE;
+ pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^
+ ANIM_TOGGLEBIT ) | LEGS_JUMP;
+ PM_AddEvent( EV_JUMP );
+
+ return qtrue;
+}
+
//============================================================================
@@ -1176,8 +1306,7 @@ static void PM_WalkMove( void )
return;
}
-
- if( PM_CheckJump( ) || PM_CheckPounce( ) )
+ if( PM_CheckJump( ) || PM_CheckPounce( ) || PM_CheckDodge( ) )
{
// jumped away
if( pm->waterlevel > 1 )
@@ -1529,10 +1658,6 @@ static void PM_CrashLand( void )
delta = vel + t * acc;
delta = delta*delta * 0.0001;
- // ducking while falling doubles damage
- if( pm->ps->pm_flags & PMF_DUCKED )
- delta *= 2;
-
// never take falling damage if completely underwater
if( pm->waterlevel == 3 )
return;
@@ -1964,7 +2089,7 @@ static void PM_GroundClimbTrace( void )
// hitting solid ground will end a waterjump
if( pm->ps->pm_flags & PMF_TIME_WATERJUMP )
{
- pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
+ pm->ps->pm_flags &= ~PMF_TIME_WATERJUMP;
pm->ps->pm_time = 0;
}
@@ -2107,8 +2232,7 @@ static void PM_GroundTrace( void )
VectorMA( pm->ps->origin, 0.25f, movedir, point );
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask );
- if( trace.fraction < 1.0f && !( trace.surfaceFlags & ( SURF_SKY | SURF_SLICK ) ) &&
- ( trace.entityNum == ENTITYNUM_WORLD ) )
+ if( trace.fraction < 1.0f && !( trace.surfaceFlags & ( SURF_SKY | SURF_SLICK ) ) )
{
if( !VectorCompare( trace.plane.normal, pm->ps->grapplePoint ) )
{
@@ -2174,7 +2298,7 @@ static void PM_GroundTrace( void )
// hitting solid ground will end a waterjump
if( pm->ps->pm_flags & PMF_TIME_WATERJUMP )
{
- pm->ps->pm_flags &= ~( PMF_TIME_WATERJUMP | PMF_TIME_LAND );
+ pm->ps->pm_flags &= ~PMF_TIME_WATERJUMP;
pm->ps->pm_time = 0;
}
@@ -2184,17 +2308,12 @@ static void PM_GroundTrace( void )
if( pm->debugLevel )
Com_Printf( "%i:Land\n", c_pmove );
+ // communicate the fall velocity to the server
+ pm->pmext->fallVelocity = pml.previous_velocity[ 2 ];
+
if( BG_ClassHasAbility( pm->ps->stats[ STAT_PCLASS ], SCA_TAKESFALLDAMAGE ) )
PM_CrashLand( );
-
- // don't do landing time if we were just going down a slope
- if( pml.previous_velocity[ 2 ] < -200 )
- {
- // don't allow another jump for a little while
- pm->ps->pm_flags |= PMF_TIME_LAND;
- pm->ps->pm_time = 250;
}
- }
pm->ps->groundEntityNum = trace.entityNum;
@@ -2282,7 +2401,7 @@ static void PM_CheckDuck (void)
if( pm->ps->pm_type == PM_DEAD )
{
pm->maxs[ 2 ] = -8;
- pm->ps->viewheight = DEAD_VIEWHEIGHT;
+ pm->ps->viewheight = PCmins[ 2 ] + DEAD_VIEWHEIGHT;
return;
}
@@ -2678,6 +2797,9 @@ static void PM_Weapon( void )
if( pm->ps->stats[ STAT_STATE ] & SS_HOVELING )
return;
+ if( pm->ps->stats[ STAT_STATE ] & SS_CHARGING )
+ return;
+
// check for dead player
if( pm->ps->stats[ STAT_HEALTH ] <= 0 )
{
@@ -2685,9 +2807,24 @@ static void PM_Weapon( void )
return;
}
+ // no bite during pounce
+ if( ( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG )
+ && ( pm->cmd.buttons & BUTTON_ATTACK )
+ && ( pm->ps->pm_flags & PMF_CHARGE ) )
+ {
+ return;
+ }
+
// make weapon function
if( pm->ps->weaponTime > 0 )
pm->ps->weaponTime -= pml.msec;
+ if( pm->ps->weaponTime < 0 )
+ pm->ps->weaponTime = 0;
+
+ if( pm->ps->stats[ STAT_MISC2 ] > 0 )
+ pm->ps->stats[ STAT_MISC2 ] -= pml.msec;
+ if( pm->ps->stats[ STAT_MISC2 ] < 0 )
+ pm->ps->stats[ STAT_MISC2 ] = 0;
// check for weapon change
// can't change if weapon is firing, but can change
@@ -2733,6 +2870,10 @@ static void PM_Weapon( void )
if( pm->ps->weaponTime > 0 )
return;
+ // luci uses STAT_MISC2 as an alternate weaponTime
+ if( pm->ps->weapon == WP_LUCIFER_CANNON && pm->ps->stats[ STAT_MISC2 ] > 0 )
+ return;
+
// change weapon if time
if( pm->ps->weaponstate == WEAPON_DROPPING )
{
@@ -2812,15 +2953,7 @@ static void PM_Weapon( void )
{
case WP_ALEVEL0:
//venom is only autohit
- attack1 = attack2 = attack3 = qfalse;
-
- if( !pm->autoWeaponHit[ pm->ps->weapon ] )
- {
- pm->ps->weaponTime = 0;
- pm->ps->weaponstate = WEAPON_READY;
- return;
- }
- break;
+ return;
case WP_ALEVEL3:
case WP_ALEVEL3_UPG:
@@ -2829,12 +2962,9 @@ static void PM_Weapon( void )
attack2 = pm->cmd.buttons & BUTTON_ATTACK2;
attack3 = pm->cmd.buttons & BUTTON_USE_HOLDABLE;
- if( !pm->autoWeaponHit[ pm->ps->weapon ] && !attack1 && !attack2 && !attack3 )
- {
- pm->ps->weaponTime = 0;
- pm->ps->weaponstate = WEAPON_READY;
+ // pounce is autohit
+ if( !attack1 && !attack2 && !attack3 )
return;
- }
break;
case WP_LUCIFER_CANNON:
@@ -2842,7 +2972,9 @@ static void PM_Weapon( void )
attack2 = pm->cmd.buttons & BUTTON_ATTACK2;
attack3 = qfalse;
- if( attack1 )
+ if( attack1 || pm->ps->stats[ STAT_MISC ] > 0 )
+ attack2 = qfalse;
+ if( ( attack1 || pm->ps->stats[ STAT_MISC ] == 0 ) && !attack2 && !attack3 )
{
attack2 = qfalse;
@@ -3310,7 +3442,9 @@ void PmoveSingle( pmove_t *pmove )
pmove->cmd.buttons = BUTTON_TALK;
pmove->cmd.forwardmove = 0;
pmove->cmd.rightmove = 0;
- pmove->cmd.upmove = 0;
+
+ if( pmove->cmd.upmove > 0 )
+ pmove->cmd.upmove = 0;
}
// clear all pmove local vars
@@ -3485,6 +3619,9 @@ void Pmove( pmove_t *pmove )
msec = 66;
}
+ // force crouch
+ if( pmove->ps->pm_flags & PMF_FORCE_CROUCH )
+ pmove->cmd.upmove = -127;
pmove->cmd.serverTime = pmove->ps->commandTime + msec;
PmoveSingle( pmove );
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index 9be0d73b..8cbeb64d 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -37,7 +37,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MINS_Z -24
#define DEFAULT_VIEWHEIGHT 26
#define CROUCH_VIEWHEIGHT 12
-#define DEAD_VIEWHEIGHT -14 // watch for mins[ 2 ] less than this causing
+#define DEAD_VIEWHEIGHT 4 // height from ground
//
// config strings are a general means of communicating variable length strings
@@ -135,6 +135,7 @@ typedef enum
#define PMF_BACKWARDS_RUN 16 // coast down to backwards run
#define PMF_TIME_LAND 32 // pm_time is time before rejump
#define PMF_TIME_KNOCKBACK 64 // pm_time is an air-accelerate only time
+#define PMF_FORCE_CROUCH 128 // force the player to crouch
#define PMF_TIME_WATERJUMP 256 // pm_time is waterjump
#define PMF_RESPAWNED 512 // clear after attack and jump buttons come up
#define PMF_USE_ITEM_HELD 1024
@@ -151,10 +152,11 @@ typedef enum
typedef struct
{
int pouncePayload;
+ float fallVelocity;
} pmoveExt_t;
#define MAXTOUCH 32
-typedef struct
+typedef struct pmove_s
{
// state (in / out)
playerState_t *ps;
@@ -214,7 +216,8 @@ typedef enum
STAT_PTEAM, // player team
STAT_STAMINA, // stamina (human only)
STAT_STATE, // client states e.g. wall climbing
- STAT_MISC, // for uh...misc stuff
+ STAT_MISC, // for uh...misc stuff (pounce, trample, lcannon)
+ STAT_MISC2, // more uh...misc stuff (booster, lcannon repeat)
STAT_BUILDABLE, // which ghost model to display for building
STAT_FALLDIST, // the distance the player fell
STAT_VIEWLOCK // direction to lock the view in
@@ -521,8 +524,8 @@ typedef enum
EV_BULLET, // otherEntity is the shooter
EV_LEV1_GRAB,
- EV_LEV4_CHARGE_PREPARE,
- EV_LEV4_CHARGE_START,
+ EV_LEV4_TRAMPLE_PREPARE,
+ EV_LEV4_TRAMPLE_START,
EV_PAIN,
EV_DEATH1,
@@ -556,7 +559,10 @@ typedef enum
EV_DCC_ATTACK, // dcc under attack
- EV_RPTUSE_SOUND // trigger a sound
+ EV_MGTURRET_SPINUP, // turret spinup sound should play
+
+ EV_RPTUSE_SOUND, // trigger a sound
+ EV_LEV2_ZAP
} entity_event_t;
typedef enum
@@ -564,8 +570,6 @@ typedef enum
MN_TEAM,
MN_A_TEAMFULL,
MN_H_TEAMFULL,
- MN_A_TEAMCHANGEBUILDTIMER,
- MN_H_TEAMCHANGEBUILDTIMER,
//alien stuff
MN_A_CLASS,
@@ -576,7 +580,6 @@ typedef enum
MN_A_NOEROOM,
MN_A_TOOCLOSE,
MN_A_NOOVMND_EVOLVE,
- MN_A_EVOLVEBUILDTIMER,
//alien build
MN_A_SPWNWARN,
@@ -588,6 +591,8 @@ typedef enum
MN_A_NORMAL,
MN_A_HOVEL,
MN_A_HOVEL_EXIT,
+ MN_A_TEAMCHANGEBUILDTIMER,
+ MN_A_EVOLVEBUILDTIMER,
//human stuff
MN_H_SPAWN,
@@ -600,7 +605,6 @@ typedef enum
MN_H_NOARMOURYHERE,
MN_H_NOROOMBSUITON,
MN_H_NOROOMBSUITOFF,
- MN_H_ARMOURYBUILDTIMER,
//human build
MN_H_REPEATER,
@@ -612,7 +616,9 @@ typedef enum
MN_H_NORMAL,
MN_H_TNODEWARN,
MN_H_RPTWARN,
- MN_H_RPTWARN2
+ MN_H_RPTWARN2,
+ MN_H_TEAMCHANGEBUILDTIMER,
+ MN_H_ARMOURYBUILDTIMER
} dynMenu_t;
// animations
@@ -853,7 +859,8 @@ typedef enum
MOD_LEVEL2_CLAW,
MOD_LEVEL2_ZAP,
MOD_LEVEL4_CLAW,
- MOD_LEVEL4_CHARGE,
+ MOD_LEVEL4_TRAMPLE,
+ MOD_LEVEL4_CRUSH,
MOD_SLOWBLOB,
MOD_POISON,
@@ -1010,6 +1017,7 @@ typedef struct
qboolean dccTest;
qboolean transparentTest;
qboolean reactorTest;
+ qboolean replaceable;
} buildableAttributes_t;
typedef struct
@@ -1251,7 +1259,6 @@ typedef enum
ET_ANIMMAPOBJ,
ET_MODELDOOR,
ET_LIGHTFLARE,
- ET_LEV2_ZAP_CHAIN,
ET_EVENTS // any of the EV_* events can be added freestanding
// by setting eType to ET_EVENTS + eventNum
diff --git a/src/game/g_active.c b/src/game/g_active.c
index ef6330d4..a55e9e3b 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -274,11 +274,13 @@ void ClientImpacts( gentity_t *ent, pmove_t *pm )
if( other->client && other->client->unlaggedCalc.used )
other->client->unlaggedCalc.used = qfalse;
- //charge attack
- if( ent->client->ps.weapon == WP_ALEVEL4 &&
- ent->client->ps.stats[ STAT_MISC ] > 0 &&
- ent->client->charging )
- ChargeAttack( ent, other );
+ // tyrant impact attacks
+ if( ent->client->ps.weapon == WP_ALEVEL4 )
+ {
+ G_ChargeAttack( ent, other );
+ G_CrushAttack( ent, other,
+ ( pm->cmd.serverTime - pm->ps->commandTime ) * 0.001f );
+ }
if( ent->client && other->client )
G_ClientShove( ent, other );
@@ -375,12 +377,18 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
{
pmove_t pm;
gclient_t *client;
+ qboolean attack1, attack3;
client = ent->client;
client->oldbuttons = client->buttons;
client->buttons = ucmd->buttons;
+ attack1 = ( ( client->buttons & BUTTON_ATTACK ) &&
+ !( client->oldbuttons & BUTTON_ATTACK ) );
+ attack3 = ( ( client->buttons & BUTTON_USE_HOLDABLE ) &&
+ !( client->oldbuttons & BUTTON_USE_HOLDABLE ) );
+
if( client->sess.spectatorState != SPECTATOR_FOLLOW )
{
if( client->sess.spectatorState == SPECTATOR_LOCKED )
@@ -388,6 +396,15 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
else
client->ps.pm_type = PM_SPECTATOR;
+ // in case the client entered the queue while following a teammate
+ if( ( client->pers.teamSelection == PTE_ALIENS &&
+ G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) ||
+ ( client->pers.teamSelection == PTE_HUMANS &&
+ G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) )
+ {
+ client->ps.pm_flags |= PMF_QUEUED;
+ }
+
client->ps.speed = BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] );
client->ps.stats[ STAT_STAMINA ] = 0;
@@ -413,28 +430,25 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
G_TouchTriggers( ent );
trap_UnlinkEntity( ent );
- if( ( client->buttons & BUTTON_ATTACK ) && !( client->oldbuttons & BUTTON_ATTACK ) )
+ if( ( attack1 || attack3 ) && ( client->ps.pm_flags & PMF_QUEUED ) )
{
- //if waiting in a queue remove from the queue
- if( client->ps.pm_flags & PMF_QUEUED )
- {
- if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum );
- else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum );
+ if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum );
+ else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum );
- client->pers.classSelection = PCL_NONE;
- client->ps.stats[ STAT_PCLASS ] = PCL_NONE;
- }
- else if( client->pers.classSelection == PCL_NONE )
- {
- if( client->pers.teamSelection == PTE_NONE )
- G_TriggerMenu( client->ps.clientNum, MN_TEAM );
- else if( client->pers.teamSelection == PTE_ALIENS )
- G_TriggerMenu( client->ps.clientNum, MN_A_CLASS );
- else if( client->pers.teamSelection == PTE_HUMANS )
- G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN );
- }
+ client->pers.classSelection = PCL_NONE;
+ client->ps.stats[ STAT_PCLASS ] = PCL_NONE;
+ }
+
+ if( attack1 && client->pers.classSelection == PCL_NONE )
+ {
+ if( client->pers.teamSelection == PTE_NONE )
+ G_TriggerMenu( client->ps.clientNum, MN_TEAM );
+ else if( client->pers.teamSelection == PTE_ALIENS )
+ G_TriggerMenu( client->ps.clientNum, MN_A_CLASS );
+ else if( client->pers.teamSelection == PTE_HUMANS )
+ G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN );
}
//set the queue position for the client side
@@ -452,9 +466,22 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
}
}
}
+ else if( attack1 && ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+ {
+ G_StopFollowing( ent );
+ client->pers.classSelection = PCL_NONE;
+ if( client->pers.teamSelection == PTE_NONE )
+ G_TriggerMenu( ent-g_entities, MN_TEAM );
+ else if( client->pers.teamSelection == PTE_ALIENS )
+ G_TriggerMenu( ent-g_entities, MN_A_CLASS );
+ else if( client->pers.teamSelection == PTE_HUMANS )
+ G_TriggerMenu( ent-g_entities, MN_H_SPAWN );
+ }
- if( ( client->buttons & BUTTON_USE_HOLDABLE ) && !( client->oldbuttons & BUTTON_USE_HOLDABLE ) )
+ if( attack3 )
+ {
G_ToggleFollow( ent );
+ }
}
@@ -545,41 +572,18 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
client->time100 -= 100;
- //if not trying to run then not trying to sprint
- if( walking || stopped )
- client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
-
- if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
- client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
-
- if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && !crouched )
- {
- //subtract stamina
- if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
- client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE;
- else
- client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE;
-
- if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
- client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
- }
-
- if( walking || crouched )
- {
- //restore stamina
+ // Restore or subtract stamina
+ if( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST )
+ client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE;
+ else if( walking || crouched )
client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE;
-
- if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
- client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
- }
else if( stopped )
- {
- //restore stamina faster
client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE;
- if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
- client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
- }
+ if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
+ client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
+ else if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
+ client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
//client is charging up for a pounce
if( client->ps.weapon == WP_ALEVEL3 || client->ps.weapon == WP_ALEVEL3_UPG )
@@ -594,12 +598,6 @@ void ClientTimerActions( gentity_t *ent, int msec )
if( client->ps.stats[ STAT_MISC ] < pounceSpeed && ucmd->buttons & BUTTON_ATTACK2 )
client->ps.stats[ STAT_MISC ] += ( 100.0f / (float)LEVEL3_POUNCE_CHARGE_TIME ) * pounceSpeed;
- if( !( ucmd->buttons & BUTTON_ATTACK2 ) )
- {
- if( client->pmext.pouncePayload > 0 )
- client->allowedToPounce = qtrue;
- }
-
if( client->ps.stats[ STAT_MISC ] > pounceSpeed )
client->ps.stats[ STAT_MISC ] = pounceSpeed;
}
@@ -607,8 +605,8 @@ void ClientTimerActions( gentity_t *ent, int msec )
//client is charging up for a... charge
if( client->ps.weapon == WP_ALEVEL4 )
{
- if( client->ps.stats[ STAT_MISC ] < LEVEL4_CHARGE_TIME && ucmd->buttons & BUTTON_ATTACK2 &&
- !client->charging )
+ if( client->ps.stats[ STAT_MISC ] < LEVEL4_TRAMPLE_CHARGE_TRIGGER &&
+ ( ucmd->buttons & BUTTON_ATTACK2 ) && !client->charging )
{
client->charging = qfalse; //should already be off, just making sure
client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING;
@@ -617,26 +615,24 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
//trigger charge sound...is quite annoying
//if( client->ps.stats[ STAT_MISC ] <= 0 )
- // G_AddEvent( ent, EV_LEV4_CHARGE_PREPARE, 0 );
+ // G_AddEvent( ent, EV_LEV4_TRAMPLE_PREPARE, 0 );
- client->ps.stats[ STAT_MISC ] += (int)( 100 * (float)LEVEL4_CHARGE_CHARGE_RATIO );
+ client->ps.stats[ STAT_MISC ] += 100 * LEVEL4_TRAMPLE_CHARGE_RATE;
- if( client->ps.stats[ STAT_MISC ] > LEVEL4_CHARGE_TIME )
- client->ps.stats[ STAT_MISC ] = LEVEL4_CHARGE_TIME;
}
else
client->ps.stats[ STAT_MISC ] = 0;
}
if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging ||
- client->ps.stats[ STAT_MISC ] == LEVEL4_CHARGE_TIME )
+ client->ps.stats[ STAT_MISC ] >= LEVEL4_TRAMPLE_CHARGE_TRIGGER )
{
- if( client->ps.stats[ STAT_MISC ] > LEVEL4_MIN_CHARGE_TIME )
+ if( client->ps.stats[ STAT_MISC ] > LEVEL4_TRAMPLE_CHARGE_MIN )
{
- client->ps.stats[ STAT_MISC ] -= 100;
+ client->ps.stats[ STAT_MISC ] -= 100 * LEVEL4_TRAMPLE_DISCHARGE_RATE;
if( client->charging == qfalse )
- G_AddEvent( ent, EV_LEV4_CHARGE_START, 0 );
+ G_AddEvent( ent, EV_LEV4_TRAMPLE_START, 0 );
client->charging = qtrue;
client->ps.stats[ STAT_STATE ] |= SS_CHARGING;
@@ -648,6 +644,9 @@ void ClientTimerActions( gentity_t *ent, int msec )
//can't charge backwards
if( ucmd->forwardmove < 0 )
client->ps.stats[ STAT_MISC ] = 0;
+
+ if( client->ps.stats[ STAT_MISC ] > LEVEL4_TRAMPLE_CHARGE_MAX )
+ client->ps.stats[ STAT_MISC ] = LEVEL4_TRAMPLE_CHARGE_MAX;
}
else
client->ps.stats[ STAT_MISC ] = 0;
@@ -663,9 +662,14 @@ void ClientTimerActions( gentity_t *ent, int msec )
}
//client is charging up an lcannon
- if( client->ps.weapon == WP_LUCIFER_CANNON )
+ if( client->ps.weapon == WP_LUCIFER_CANNON &&
+ ( ucmd->buttons & BUTTON_ATTACK ) &&
+ client->ps.stats[ STAT_MISC2 ] <= 0 )
{
- if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE && ucmd->buttons & BUTTON_ATTACK )
+ if( client->ps.stats[ STAT_MISC ] <= 0 )
+ client->lcannonStartTime = level.time;
+
+ if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE )
client->ps.stats[ STAT_MISC ] += ( 100.0f / LCANNON_CHARGE_TIME ) * LCANNON_TOTAL_CHARGE;
if( client->ps.stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE )
@@ -675,6 +679,18 @@ void ClientTimerActions( gentity_t *ent, int msec )
client->ps.stats[ STAT_MISC ] = client->ps.ammo * LCANNON_TOTAL_CHARGE / 10;
}
+ if( client->ps.weapon == WP_ABUILD || client->ps.weapon == WP_ABUILD2 ||
+ BG_InventoryContainsWeapon( WP_HBUILD, client->ps.stats ) ||
+ BG_InventoryContainsWeapon( WP_HBUILD2, client->ps.stats ) )
+ {
+ //update build timer
+ if( client->ps.stats[ STAT_MISC ] > 0 )
+ client->ps.stats[ STAT_MISC ] -= 100;
+
+ if( client->ps.stats[ STAT_MISC ] < 0 )
+ client->ps.stats[ STAT_MISC ] = 0;
+ }
+
switch( client->ps.weapon )
{
case WP_ABUILD:
@@ -708,12 +724,6 @@ void ClientTimerActions( gentity_t *ent, int msec )
client->ps.misc[ i ] = 0;
}
- //update build timer
- if( client->ps.stats[ STAT_MISC ] > 0 )
- client->ps.stats[ STAT_MISC ] -= 100;
-
- if( client->ps.stats[ STAT_MISC ] < 0 )
- client->ps.stats[ STAT_MISC ] = 0;
break;
default:
@@ -762,31 +772,24 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
client->time1000 -= 1000;
- //client is poison clouded
- if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED )
- G_Damage( ent, client->lastPoisonCloudedClient, client->lastPoisonCloudedClient, NULL, NULL,
- LEVEL1_PCLOUD_DMG, 0, MOD_LEVEL1_PCLOUD );
//client is poisoned
if( client->ps.stats[ STAT_STATE ] & SS_POISONED )
{
- int i;
- int seconds = ( ( level.time - client->lastPoisonTime ) / 1000 ) + 1;
- int damage = ALIEN_POISON_DMG, damage2 = 0;
+ int damage = ALIEN_POISON_DMG;
- for( i = 0; i < seconds; i++ )
- {
- if( i == seconds - 1 )
- damage2 = damage;
+ if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) )
+ damage -= BSUIT_POISON_PROTECTION;
- damage *= ALIEN_POISON_DIVIDER;
- }
+ if( BG_InventoryContainsUpgrade( UP_HELMET, client->ps.stats ) )
+ damage -= HELMET_POISON_PROTECTION;
- damage = damage2 - damage;
+ if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
+ damage -= LIGHTARMOUR_POISON_PROTECTION;
- G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, NULL,
- damage, 0, MOD_POISON );
- }
+ G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL,
+ 0, damage, 0, MOD_POISON );
+ }
//replenish alien health
if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
@@ -798,6 +801,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
int i, num;
gentity_t *boostEntity;
float modifier = 1.0f;
+ qboolean modified = qfalse;
VectorAdd( client->ps.origin, range, maxs );
VectorSubtract( client->ps.origin, range, mins );
@@ -807,24 +811,49 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
boostEntity = &g_entities[ entityList[ i ] ];
- if( boostEntity->client && boostEntity->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
- boostEntity->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL4 )
- {
- modifier = LEVEL4_REGEN_MOD;
- break;
- }
- else if( boostEntity->s.eType == ET_BUILDABLE &&
+ if( boostEntity->s.eType == ET_BUILDABLE &&
boostEntity->s.modelindex == BA_A_BOOSTER &&
boostEntity->spawned && boostEntity->health > 0)
{
modifier = BOOSTER_REGEN_MOD;
+ modified = qtrue;
+ break;
+ }
+ else if( boostEntity->client && boostEntity->health > 0 &&
+ boostEntity->client->pers.teamSelection == PTE_ALIENS &&
+ ( boostEntity->client->pers.classSelection == PCL_ALIEN_LEVEL1 ||
+ boostEntity->client->pers.classSelection == PCL_ALIEN_LEVEL1_UPG ) )
+ {
+ if( boostEntity->client->pers.classSelection == PCL_ALIEN_LEVEL1_UPG )
+ modifier = LEVEL1_UPG_REGEN_MOD;
+ else
+ modifier = LEVEL1_REGEN_MOD;
+
+ modified = qtrue;
break;
}
}
- if( ent->health > 0 && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] &&
+ if( ent->health > 0 &&
+ ent->health < client->ps.stats[ STAT_MAX_HEALTH ] &&
( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )
- ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier;
+ {
+ if( !modified && !G_FindCreep( ent ) )
+ {
+ if( ( ent->lastRegenTime + ALIEN_REGEN_NOCREEP_TIME ) < level.time )
+ {
+ ent->health +=
+ BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] );
+ ent->lastRegenTime = level.time;
+ }
+ }
+ else
+ {
+ ent->health += modifier *
+ BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] );
+ ent->lastRegenTime = level.time;
+ }
+ }
if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] )
ent->health = client->ps.stats[ STAT_MAX_HEALTH ];
@@ -857,9 +886,9 @@ void ClientTimerActions( gentity_t *ent, int msec )
if( client->ps.ammo < maxAmmo )
client->ps.ammo++;
- }
}
}
+}
/*
====================
@@ -1281,6 +1310,37 @@ static void G_UnlaggedDetectCollisions( gentity_t *ent )
/*
==============
+G_CheckZap
+==============
+*/
+static void G_CheckZap( gentity_t *ent )
+{
+ int i;
+
+ if( !ent->zapping )
+ {
+ // clear out established targets
+ for( i = 0; i < LEVEL2_AREAZAP_MAX_TARGETS; i++ )
+ {
+ ent->zapTargets[ i ] = -1;
+ }
+ ent->zapDmg = 0.0f;
+ }
+ ent->wasZapping = ent->zapping;
+ ent->zapping = qfalse;
+
+ if( ent->client->ps.weapon == WP_ALEVEL2_UPG &&
+ ( ent->client->pers.cmd.buttons & BUTTON_ATTACK2 ) )
+ {
+ ent->zapping = qtrue;
+ }
+
+ if( ent->wasZapping && !ent->zapping )
+ ent->client->ps.weaponTime = LEVEL2_AREAZAP_REPEAT;
+}
+
+/*
+==============
ClientThink
This will be called once for each client frame, which will
@@ -1320,6 +1380,19 @@ void ClientThink_real( gentity_t *ent )
// G_Printf("serverTime >>>>>\n" );
}
+ // ucmd->serverTime is a client predicted value, but it works for making a
+ // replacement for client->ps.ping when in SPECTATOR_FOLLOW
+ client->pers.ping = level.time - ucmd->serverTime;
+
+ // account for the one frame of delay on client side
+ client->pers.ping -= level.time - level.previousTime;
+
+ // account for the time that's elapsed since the last ClientEndFrame()
+ client->pers.ping += trap_Milliseconds() - level.frameMsec;
+
+ if( client->pers.ping < 0 )
+ client->pers.ping = 0;
+
msec = ucmd->serverTime - client->ps.commandTime;
// following others may result in bad times, but we still want
// to check for follow toggles
@@ -1399,12 +1472,26 @@ void ClientThink_real( gentity_t *ent )
client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;
if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED &&
- client->lastBoostedTime + BOOST_TIME < level.time )
+ client->ps.stats[ STAT_MISC2 ] <= 0 )
client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED;
- if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
- client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time )
- client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED;
+ if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED )
+ {
+ int timeLeft = LEVEL1_PCLOUD_TIME -
+ ( level.time - client->lastPoisonCloudedTime );
+
+ if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) )
+ timeLeft -= BSUIT_PCLOUD_PROTECTION;
+
+ if( BG_InventoryContainsUpgrade( UP_HELMET, client->ps.stats ) )
+ timeLeft -= HELMET_PCLOUD_PROTECTION;
+
+ if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
+ timeLeft -= LIGHTARMOUR_PCLOUD_PROTECTION;
+
+ if( timeLeft <= 0 )
+ client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED;
+ }
if( client->ps.stats[ STAT_STATE ] & SS_POISONED &&
client->lastPoisonTime + ALIEN_POISON_TIME < level.time )
@@ -1483,31 +1570,6 @@ void ClientThink_real( gentity_t *ent )
memset( &pm, 0, sizeof( pm ) );
- if( !( ucmd->buttons & BUTTON_TALK ) && !( client->ps.pm_flags & PMF_RESPAWNED ) )
- {
- switch( client->ps.weapon )
- {
- case WP_ALEVEL0:
- if( client->ps.weaponTime <= 0 )
- pm.autoWeaponHit[ client->ps.weapon ] = CheckVenomAttack( ent );
- break;
-
- case WP_ALEVEL1:
- case WP_ALEVEL1_UPG:
- CheckGrabAttack( ent );
- break;
-
- case WP_ALEVEL3:
- case WP_ALEVEL3_UPG:
- if( client->ps.weaponTime <= 0 )
- pm.autoWeaponHit[ client->ps.weapon ] = CheckPounceAttack( ent );
- break;
-
- default:
- break;
- }
- }
-
if( ent->flags & FL_FORCE_GESTURE )
{
ent->flags &= ~FL_FORCE_GESTURE;
@@ -1542,6 +1604,13 @@ void ClientThink_real( gentity_t *ent )
if( !ent->client->noclip )
G_TouchTriggers( ent );
+ // Tyrant crush
+ pm.pmext->fallVelocity = 0;
+ if( ent->client->forceCrouchTime + 500 > level.time )
+ client->ps.pm_flags |= PMF_FORCE_CROUCH;
+ else
+ client->ps.pm_flags &= ~PMF_FORCE_CROUCH;
+
Pmove( &pm );
G_UnlaggedDetectCollisions( ent );
@@ -1555,6 +1624,42 @@ void ClientThink_real( gentity_t *ent )
else
BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
+ switch( client->ps.weapon )
+ {
+ case WP_ALEVEL0:
+ if( !CheckVenomAttack( ent ) )
+ {
+ client->ps.weaponstate = WEAPON_READY;
+ }
+ else
+ {
+ client->ps.generic1 = WPM_PRIMARY;
+ G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
+ }
+ break;
+
+ case WP_ALEVEL1:
+ case WP_ALEVEL1_UPG:
+ CheckGrabAttack( ent );
+ break;
+
+ case WP_ALEVEL3:
+ case WP_ALEVEL3_UPG:
+ if( !CheckPounceAttack( ent ) )
+ {
+ client->ps.weaponstate = WEAPON_READY;
+ }
+ else
+ {
+ client->ps.generic1 = WPM_SECONDARY;
+ G_AddEvent( ent, EV_FIRE_WEAPON2, 0 );
+ }
+ break;
+
+ default:
+ break;
+ }
+
SendPendingPredictableEvents( &ent->client->ps );
if( !( ent->client->ps.eFlags & EF_FIRING ) )
@@ -1574,6 +1679,8 @@ void ClientThink_real( gentity_t *ent )
// touch other objects
ClientImpacts( ent, &pm );
+ G_CheckZap( ent );
+
// execute client events
ClientEvents( ent, oldEventSequence );
@@ -1597,7 +1704,7 @@ void ClientThink_real( gentity_t *ent )
client->buttons = ucmd->buttons;
client->latched_buttons |= client->buttons & ~client->oldbuttons;
- if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) &&
+ if( ( client->buttons & BUTTON_USE_EVOLVE ) && !( client->oldbuttons & BUTTON_USE_EVOLVE ) &&
client->ps.stats[ STAT_HEALTH ] > 0 )
{
trace_t trace;
@@ -1797,6 +1904,10 @@ void ClientEndFrame( gentity_t *ent )
pers = &ent->client->pers;
+ // save a copy of things from playerState in case of SPECTATOR_FOLLOW
+ pers->score = ent->client->ps.persistant[ PERS_SCORE ];
+ pers->credit = ent->client->ps.persistant[ PERS_CREDIT ];
+
//
// If the end of unit layout is displayed, don't give
// the player any normal movement attributes
@@ -1824,6 +1935,8 @@ void ClientEndFrame( gentity_t *ent )
G_SetClientSound( ent );
+ G_UpdateZaps( ent );
+
// set the latest infor
if( g_smoothClients.integer )
BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
@@ -1833,4 +1946,3 @@ void ClientEndFrame( gentity_t *ent )
SendPendingPredictableEvents( &ent->client->ps );
}
-
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index c56dec3c..ad2ece9e 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -192,7 +192,7 @@ static qboolean G_FindPower( gentity_t *self )
//if entity is a power item calculate the distance to it
if( ( ent->s.modelindex == BA_H_REACTOR || ent->s.modelindex == BA_H_REPEATER ) &&
- ent->spawned )
+ ent->spawned && ent->health > 0 )
{
VectorSubtract( self->s.origin, ent->s.origin, temp_v );
distance = VectorLength( temp_v );
@@ -268,28 +268,19 @@ buildable_t G_IsPowered( vec3_t origin )
================
G_FindDCC
-attempt to find a controlling DCC for self, return qtrue if successful
+attempt to find a controlling DCC for self, return number found
================
*/
-static qboolean G_FindDCC( gentity_t *self )
+int G_FindDCC( gentity_t *self )
{
int i;
gentity_t *ent;
- gentity_t *closestDCC = NULL;
int distance = 0;
- int minDistance = 10000;
vec3_t temp_v;
- qboolean foundDCC = qfalse;
+ int foundDCC = 0;
if( self->biteam != BIT_HUMANS )
- return qfalse;
-
- //if this already has dcc then stop now
- if( self->dccNode && self->dccNode->powered )
- return qtrue;
-
- //reset parent
- self->dccNode = NULL;
+ return 0;
//iterate through entities
for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ )
@@ -302,22 +293,14 @@ static qboolean G_FindDCC( gentity_t *self )
{
VectorSubtract( self->s.origin, ent->s.origin, temp_v );
distance = VectorLength( temp_v );
- if( distance < minDistance && ent->powered )
+ if( distance < DC_RANGE && ent->powered )
{
- closestDCC = ent;
- minDistance = distance;
- foundDCC = qtrue;
+ foundDCC++;
}
}
}
- //if there was no nearby DCC give up
- if( !foundDCC )
- return qfalse;
-
- self->dccNode = closestDCC;
-
- return qtrue;
+ return foundDCC;
}
/*
@@ -333,7 +316,6 @@ qboolean G_IsDCCBuilt( void )
memset( &dummy, 0, sizeof( gentity_t ) );
- dummy.dccNode = NULL;
dummy.biteam = BIT_HUMANS;
return G_FindDCC( &dummy );
@@ -404,7 +386,7 @@ G_FindCreep
attempt to find creep for self, return qtrue if successful
================
*/
-static qboolean G_FindCreep( gentity_t *self )
+qboolean G_FindCreep( gentity_t *self )
{
int i;
gentity_t *ent;
@@ -418,9 +400,9 @@ static qboolean G_FindCreep( gentity_t *self )
return qtrue;
//if self does not have a parentNode or it's parentNode is invalid find a new one
- if( ( self->parentNode == NULL ) || !self->parentNode->inuse )
+ if( self->client || ( self->parentNode == NULL ) || !self->parentNode->inuse )
{
- for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ )
+ for ( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
{
if( ent->s.eType != ET_BUILDABLE )
continue;
@@ -440,13 +422,17 @@ static qboolean G_FindCreep( gentity_t *self )
if( minDistance <= CREEP_BASESIZE )
{
- self->parentNode = closestSpawn;
+ if( !self->client )
+ self->parentNode = closestSpawn;
return qtrue;
}
else
return qfalse;
}
+ if( self->client )
+ return qfalse;
+
//if we haven't returned by now then we must already have a valid parent
return qtrue;
}
@@ -845,29 +831,15 @@ void AOvermind_Think( gentity_t *self )
-/*
-================
-ABarricade_Pain
-
-pain function for Alien Spawn
-================
-*/
-void ABarricade_Pain( gentity_t *self, gentity_t *attacker, int damage )
-{
- if( rand( ) % 1 )
- G_SetBuildableAnim( self, BANIM_PAIN1, qfalse );
- else
- G_SetBuildableAnim( self, BANIM_PAIN2, qfalse );
-}
/*
================
-ABarricade_Blast
+AGeneric_Blast
-Called when an alien spawn dies
+Called when an Alien buildable explodes after dead state
================
*/
-void ABarricade_Blast( gentity_t *self )
+void AGeneric_Blast( gentity_t *self )
{
vec3_t dir;
@@ -890,18 +862,19 @@ void ABarricade_Blast( gentity_t *self )
/*
================
-ABarricade_Die
+AGeneric_Die
-Called when an alien spawn dies
+Called when an Alien buildable is killed and enters a brief dead state prior to
+exploding.
================
*/
-void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
+void AGeneric_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
{
G_SetBuildableAnim( self, BANIM_DESTROY1, qtrue );
G_SetIdleBuildableAnim( self, BANIM_DESTROYED );
self->die = nullDieFunction;
- self->think = ABarricade_Blast;
+ self->think = AGeneric_Blast;
self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
if( self->spawned )
@@ -928,12 +901,12 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
/*
================
-ABarricade_Think
+AGeneric_Think
-Think function for Alien Barricade
+A generic think function for Alien buildables
================
*/
-void ABarricade_Think( gentity_t *self )
+void AGeneric_Think( gentity_t *self )
{
self->powered = G_IsOvermindBuilt( );
@@ -950,6 +923,21 @@ void ABarricade_Think( gentity_t *self )
self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex );
}
+/*
+================
+AGeneric_Pain
+
+A generic pain function for Alien buildables
+================
+*/
+void AGeneric_Pain( gentity_t *self, gentity_t *attacker, int damage )
+{
+ if( rand( ) % 1 )
+ G_SetBuildableAnim( self, BANIM_PAIN1, qfalse );
+ else
+ G_SetBuildableAnim( self, BANIM_PAIN2, qfalse );
+}
+
@@ -958,6 +946,153 @@ void ABarricade_Think( gentity_t *self )
+
+/*
+================
+ABarricade_Pain
+
+Barricade pain animation depends on shrunk state
+================
+*/
+void ABarricade_Pain( gentity_t *self, gentity_t *attacker, int damage )
+{
+ if( !self->shrunkTime )
+ G_SetBuildableAnim( self, BANIM_PAIN1, qfalse );
+ else
+ G_SetBuildableAnim( self, BANIM_PAIN2, qfalse );
+}
+
+/*
+================
+ABarricade_Shrink
+
+Set shrink state for a barricade. When unshrinking, checks to make sure there
+is enough room.
+================
+*/
+void ABarricade_Shrink( gentity_t *self, qboolean shrink )
+{
+ if ( !self->spawned || self->health <= 0 )
+ shrink = qtrue;
+ if ( shrink && self->shrunkTime )
+ {
+ int anim;
+
+ // We need to make sure that the animation has been set to shrunk mode
+ // because we start out shrunk but with the construct animation when built
+ self->shrunkTime = level.time;
+ anim = self->s.torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT );
+ if ( self->spawned && self->health > 0 && anim != BANIM_DESTROYED )
+ {
+ G_SetIdleBuildableAnim( self, BANIM_DESTROYED );
+ G_SetBuildableAnim( self, BANIM_ATTACK1, qtrue );
+ }
+ return;
+ }
+ if ( !shrink &&
+ ( !self->shrunkTime ||
+ level.time < self->shrunkTime + BARRICADE_SHRINKTIMEOUT ) )
+ return;
+ BG_FindBBoxForBuildable( BA_A_BARRICADE, self->r.mins, self->r.maxs );
+ if ( shrink )
+ {
+ self->r.maxs[ 2 ] = (int)( self->r.maxs[ 2 ] * BARRICADE_SHRINKPROP );
+ self->shrunkTime = level.time;
+
+ // shrink animation, the destroy animation is used
+ if ( self->spawned && self->health > 0 )
+ {
+ G_SetBuildableAnim( self, BANIM_ATTACK1, qtrue );
+ G_SetIdleBuildableAnim( self, BANIM_DESTROYED );
+ }
+ }
+ else
+ {
+ trace_t tr;
+ int anim;
+
+ trap_Trace( &tr, self->s.origin, self->r.mins, self->r.maxs,
+ self->s.origin, self->s.number, MASK_PLAYERSOLID );
+ if ( tr.startsolid || tr.fraction < 1.f )
+ {
+ self->r.maxs[ 2 ] = (int)( self->r.maxs[ 2 ] * BARRICADE_SHRINKPROP );
+ return;
+ }
+ self->shrunkTime = 0;
+
+ // unshrink animation, IDLE2 has been hijacked for this
+ anim = self->s.legsAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT );
+ if ( self->spawned && self->health > 0 &&
+ anim != BANIM_CONSTRUCT1 && anim != BANIM_CONSTRUCT2 )
+ {
+ G_SetIdleBuildableAnim( self, BG_FindAnimForBuildable( BA_A_BARRICADE ) );
+ G_SetBuildableAnim( self, BANIM_ATTACK2, qtrue );
+ }
+ }
+
+ // a change in size requires a relink
+ if ( self->spawned )
+ trap_LinkEntity( self );
+}
+
+/*
+================
+ABarricade_Die
+
+Called when an alien spawn dies
+================
+*/
+void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
+{
+ AGeneric_Die( self, inflictor, attacker, damage, mod );
+ ABarricade_Shrink( self, qtrue );
+}
+
+/*
+================
+ABarricade_Think
+
+Think function for Alien Barricade
+================
+*/
+void ABarricade_Think( gentity_t *self )
+{
+ AGeneric_Think( self );
+ ABarricade_Shrink( self, !G_FindOvermind( self ) );
+}
+
+/*
+================
+ABarricade_Touch
+
+Barricades shrink when they are come into contact with an Alien that can
+pass through
+================
+*/
+
+void ABarricade_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
+{
+ gclient_t *client = other->client;
+ int client_z, min_z;
+
+ if( !client || client->pers.teamSelection != PTE_ALIENS )
+ return;
+
+ // Client must be high enough to pass over. Note that STEPSIZE (18) is
+ // hardcoded here because we don't include bg_local.h!
+ client_z = other->s.origin[ 2 ] + other->r.mins[ 2 ];
+ min_z = self->s.origin[ 2 ] - 18 +
+ (int)( self->r.maxs[ 2 ] * BARRICADE_SHRINKPROP );
+ if( client_z < min_z )
+ return;
+ ABarricade_Shrink( self, qtrue );
+}
+
+//==================================================================================
+
+
+
+
void AAcidTube_Think( gentity_t *self );
/*
@@ -1067,7 +1202,7 @@ Think function for Alien Hive
void AHive_Think( gentity_t *self )
{
int entityList[ MAX_GENTITIES ];
- vec3_t range = { ACIDTUBE_RANGE, ACIDTUBE_RANGE, ACIDTUBE_RANGE };
+ vec3_t range = { HIVE_SENSE_RANGE, HIVE_SENSE_RANGE, HIVE_SENSE_RANGE };
vec3_t mins, maxs;
int i, num;
gentity_t *enemy;
@@ -1125,6 +1260,61 @@ void AHive_Think( gentity_t *self )
G_CreepSlow( self );
}
+/*
+================
+AHive_Pain
+
+pain function for Alien Hive
+================
+*/
+void AHive_Pain( gentity_t *self, gentity_t *attacker, int damage )
+{
+ if( attacker && attacker->client && attacker->biteam == BIT_HUMANS &&
+ self->spawned && !self->active && G_FindOvermind( self ) )
+ {
+ vec3_t dirToTarget;
+
+ self->active = qtrue;
+ self->target_ent = attacker;
+ self->timestamp = level.time + HIVE_REPEAT;
+
+ VectorSubtract( attacker->s.pos.trBase, self->s.pos.trBase, dirToTarget );
+ VectorNormalize( dirToTarget );
+ vectoangles( dirToTarget, self->turretAim );
+
+ //fire at target
+ FireWeapon( self );
+ }
+ G_SetBuildableAnim( self, BANIM_PAIN1, qfalse );
+}
+
+/*
+================
+AHive_Die
+
+pain function for Alien Hive
+================
+*/
+void AHive_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
+{
+ if( attacker && attacker->client && attacker->biteam == BIT_HUMANS &&
+ self->spawned && !self->active && G_FindOvermind( self ) )
+ {
+ vec3_t dirToTarget;
+
+ self->active = qtrue;
+ self->target_ent = attacker;
+ self->timestamp = level.time + HIVE_REPEAT;
+
+ VectorSubtract( attacker->s.pos.trBase, self->s.pos.trBase, dirToTarget );
+ VectorNormalize( dirToTarget );
+ vectoangles( dirToTarget, self->turretAim );
+
+ //fire at target
+ FireWeapon( self );
+ }
+ AGeneric_Die( self, inflictor, attacker, damage, mod );
+}
@@ -1181,7 +1371,7 @@ qboolean AHovel_Blocked( gentity_t *hovel, gentity_t *player, qboolean provideEx
G_SetOrigin( player, origin );
VectorCopy( origin, player->client->ps.origin );
VectorCopy( vec3_origin, player->client->ps.velocity );
- SetClientViewAngle( player, angles );
+ G_SetClientViewAngle( player, angles );
}
if( tr.fraction < 1.0f )
@@ -1264,7 +1454,7 @@ void AHovel_Use( gentity_t *self, gentity_t *other, gentity_t *activator )
G_SetOrigin( activator, hovelOrigin );
VectorCopy( hovelOrigin, activator->client->ps.origin );
- SetClientViewAngle( activator, hovelAngles );
+ G_SetClientViewAngle( activator, hovelAngles );
}
}
}
@@ -1339,7 +1529,7 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
G_SetOrigin( builder, newOrigin );
VectorCopy( newOrigin, builder->client->ps.origin );
- SetClientViewAngle( builder, newAngles );
+ G_SetClientViewAngle( builder, newAngles );
//client leaves hovel
builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;
@@ -1397,15 +1587,8 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
if( client && client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
return;
- //only allow boostage once every 30 seconds
- if( client->lastBoostedTime + BOOSTER_INTERVAL > level.time )
- return;
-
- if( !( client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) )
- {
- client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- client->lastBoostedTime = level.time;
- }
+ client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
+ client->ps.stats[ STAT_MISC2 ] = BOOST_TIME;
}
@@ -1648,9 +1831,6 @@ void HRepeater_Use( gentity_t *self, gentity_t *other, gentity_t *activator )
G_GiveClientMaxAmmo( other, qtrue );
}
-
-#define DCC_ATTACK_PERIOD 10000
-
/*
================
HReactor_Think
@@ -1661,13 +1841,26 @@ Think function for Human Reactor
void HReactor_Think( gentity_t *self )
{
int entityList[ MAX_GENTITIES ];
- vec3_t range = { REACTOR_ATTACK_RANGE, REACTOR_ATTACK_RANGE, REACTOR_ATTACK_RANGE };
+ vec3_t range = { REACTOR_ATTACK_RANGE,
+ REACTOR_ATTACK_RANGE,
+ REACTOR_ATTACK_RANGE };
+ vec3_t dccrange = { REACTOR_ATTACK_DCC_RANGE,
+ REACTOR_ATTACK_DCC_RANGE,
+ REACTOR_ATTACK_DCC_RANGE };
vec3_t mins, maxs;
int i, num;
gentity_t *enemy, *tent;
- VectorAdd( self->s.origin, range, maxs );
- VectorSubtract( self->s.origin, range, mins );
+ if( self->dcc )
+ {
+ VectorAdd( self->s.origin, dccrange, maxs );
+ VectorSubtract( self->s.origin, dccrange, mins );
+ }
+ else
+ {
+ VectorAdd( self->s.origin, range, maxs );
+ VectorSubtract( self->s.origin, range, mins );
+ }
if( self->spawned && ( self->health > 0 ) )
{
@@ -1680,8 +1873,18 @@ void HReactor_Think( gentity_t *self )
if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
{
self->timestamp = level.time;
- G_SelectiveRadiusDamage( self->s.pos.trBase, self, REACTOR_ATTACK_DAMAGE,
- REACTOR_ATTACK_RANGE, self, MOD_REACTOR, PTE_HUMANS );
+ if( self->dcc )
+ {
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self,
+ REACTOR_ATTACK_DCC_DAMAGE, REACTOR_ATTACK_DCC_RANGE, self,
+ MOD_REACTOR, PTE_HUMANS );
+ }
+ else
+ {
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self,
+ REACTOR_ATTACK_DAMAGE, REACTOR_ATTACK_RANGE, self,
+ MOD_REACTOR, PTE_HUMANS );
+ }
tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL );
@@ -1691,19 +1894,12 @@ void HReactor_Think( gentity_t *self )
tent->s.clientNum = enemy->s.number; //dest
}
}
-
- //reactor under attack
- if( self->health < self->lastHealth &&
- level.time > level.humanBaseAttackTimer && G_IsDCCBuilt( ) )
- {
- level.humanBaseAttackTimer = level.time + DCC_ATTACK_PERIOD;
- G_BroadcastEvent( EV_DCC_ATTACK, 0 );
- }
-
- self->lastHealth = self->health;
}
- self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex );
+ if( self->dcc )
+ self->nextthink = level.time + REACTOR_ATTACK_DCC_REPEAT;
+ else
+ self->nextthink = level.time + REACTOR_ATTACK_REPEAT;
}
//==================================================================================
@@ -1847,7 +2043,8 @@ void HMedistat_Think( gentity_t *self )
if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
{
- if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
+ if( ( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] ||
+ player->client->ps.stats[ STAT_STAMINA ] < MAX_STAMINA ) &&
player->client->ps.pm_type != PM_DEAD )
{
self->enemy = player;
@@ -1873,17 +2070,26 @@ void HMedistat_Think( gentity_t *self )
self->active = qfalse;
}
- else if( self->enemy ) //heal!
+ else if( self->enemy && self->enemy->client ) //heal!
{
- if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED )
+ if( self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED )
self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
+ if( self->enemy->client->ps.stats[ STAT_STAMINA ] < MAX_STAMINA )
+ self->enemy->client->ps.stats[ STAT_STAMINA ] += STAMINA_MEDISTAT_RESTORE;
+
+ if( self->enemy->client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
+ self->enemy->client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
+
self->enemy->health++;
//if they're completely healed, give them a medkit
- if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] &&
- !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) )
- BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats );
+ if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] )
+ {
+ self->enemy->health = self->enemy->client->ps.stats[ STAT_MAX_HEALTH ];
+ if( !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) )
+ BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats );
+ }
}
}
}
@@ -1910,22 +2116,11 @@ qboolean HMGTurret_TrackEnemy( gentity_t *self )
float temp, rotAngle;
float accuracyTolerance, angularSpeed;
- if( self->lev1Grabbed )
- {
- //can't turn fast if grabbed
- accuracyTolerance = MGTURRET_GRAB_ACCURACYTOLERANCE;
- angularSpeed = MGTURRET_GRAB_ANGULARSPEED;
- }
- else if( self->dcced )
- {
- accuracyTolerance = MGTURRET_DCC_ACCURACYTOLERANCE;
- angularSpeed = MGTURRET_DCC_ANGULARSPEED;
- }
+ accuracyTolerance = MGTURRET_ACCURACYTOLERANCE;
+ if( self->locked )
+ angularSpeed = MGTURRET_ANGULARSPEED_LOCKED;
else
- {
- accuracyTolerance = MGTURRET_ACCURACYTOLERANCE;
angularSpeed = MGTURRET_ANGULARSPEED;
- }
VectorSubtract( self->enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget );
@@ -1942,9 +2137,9 @@ qboolean HMGTurret_TrackEnemy( gentity_t *self )
angularDiff[ YAW ] = AngleSubtract( self->s.angles2[ YAW ], angleToTarget[ YAW ] );
//if not pointing at our target then move accordingly
- if( angularDiff[ PITCH ] < (-accuracyTolerance) )
+ if( angularDiff[ PITCH ] < 0 && angularDiff[ PITCH ] < (-angularSpeed) )
self->s.angles2[ PITCH ] += angularSpeed;
- else if( angularDiff[ PITCH ] > accuracyTolerance )
+ else if( angularDiff[ PITCH ] > 0 && angularDiff[ PITCH ] > angularSpeed )
self->s.angles2[ PITCH ] -= angularSpeed;
else
self->s.angles2[ PITCH ] = angleToTarget[ PITCH ];
@@ -1958,9 +2153,9 @@ qboolean HMGTurret_TrackEnemy( gentity_t *self )
self->s.angles2[ PITCH ] = (-360) + MGTURRET_VERTICALCAP;
//if not pointing at our target then move accordingly
- if( angularDiff[ YAW ] < (-accuracyTolerance) )
+ if( angularDiff[ YAW ] < 0 && angularDiff[ YAW ] < ( -angularSpeed ) )
self->s.angles2[ YAW ] += angularSpeed;
- else if( angularDiff[ YAW ] > accuracyTolerance )
+ else if( angularDiff[ YAW ] > 0 && angularDiff[ YAW ] > angularSpeed )
self->s.angles2[ YAW ] -= angularSpeed;
else
self->s.angles2[ YAW ] = angleToTarget[ YAW ];
@@ -1985,7 +2180,7 @@ HMGTurret_CheckTarget
Used by HMGTurret_Think to check enemies for validity
================
*/
-qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ignorePainted )
+qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target )
{
trace_t trace;
gentity_t *traceEnt;
@@ -2005,8 +2200,14 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ign
if( Distance( self->s.origin, target->s.pos.trBase ) > MGTURRET_RANGE )
return qfalse;
- //some turret has already selected this target
- if( self->dcced && target->targeted && target->targeted->powered && !ignorePainted )
+ trap_Trace( &trace, self->s.pos.trBase, NULL, NULL, target->s.pos.trBase, self->s.number, MASK_SHOT );
+
+ traceEnt = &g_entities[ trace.entityNum ];
+
+ if( !traceEnt->client )
+ return qfalse;
+
+ if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
return qfalse;
trap_Trace( &trace, self->s.pos.trBase, NULL, NULL, target->s.pos.trBase, self->s.number, MASK_SHOT );
@@ -2051,7 +2252,7 @@ void HMGTurret_FindEnemy( gentity_t *self )
if( target->client && target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
{
//if target is not valid keep searching
- if( !HMGTurret_CheckTarget( self, target, qfalse ) )
+ if( !HMGTurret_CheckTarget( self, target ) )
continue;
//we found a target
@@ -2059,27 +2260,6 @@ void HMGTurret_FindEnemy( gentity_t *self )
return;
}
}
-
- if( self->dcced )
- {
- //check again, this time ignoring painted targets
- for( i = 0; i < num; i++ )
- {
- target = &g_entities[ entityList[ i ] ];
-
- if( target->client && target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- //if target is not valid keep searching
- if( !HMGTurret_CheckTarget( self, target, qtrue ) )
- continue;
-
- //we found a target
- self->enemy = target;
- return;
- }
- }
- }
-
//couldn't find a target
self->enemy = NULL;
}
@@ -2110,12 +2290,10 @@ void HMGTurret_Think( gentity_t *self )
if( self->spawned )
{
- //find a dcc for self
- self->dcced = G_FindDCC( self );
-
//if the current target is not valid find a new one
- if( !HMGTurret_CheckTarget( self, self->enemy, qfalse ) )
+ if( !HMGTurret_CheckTarget( self, self->enemy ) )
{
+ self->locked = qfalse;
if( self->enemy )
self->enemy->targeted = NULL;
@@ -2128,17 +2306,43 @@ void HMGTurret_Think( gentity_t *self )
self->enemy->targeted = self;
- //if we are pointing at our target and we can fire shoot it
- if( HMGTurret_TrackEnemy( self ) && ( self->count < level.time ) )
+ if( self->active )
{
- //fire at target
- FireWeapon( self );
+ qboolean canFire = HMGTurret_TrackEnemy( self );
- self->s.eFlags |= EF_FIRING;
- G_AddEvent( self, EV_FIRE_WEAPON, 0 );
- G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
+ if( self->turretSpinupTime < level.time )
+ {
+ if( canFire )
+ {
+ if( !self->locked )
+ {
+ self->active = qfalse;
+ }
+ else if( self->count < level.time )
+ {
+ //fire at target
+ FireWeapon( self );
+ self->s.eFlags |= EF_FIRING;
+ G_AddEvent( self, EV_FIRE_WEAPON, 0 );
+ G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
+ self->count = level.time + firespeed;
+ }
+ }
+ else
+ {
+ self->locked = qfalse;
+ }
+ }
+ return;
+ }
- self->count = level.time + firespeed;
+ //if we are pointing at our target, start spinning up
+ if( HMGTurret_TrackEnemy( self ) && self->count < level.time )
+ {
+ self->active = qtrue;
+ self->locked = qtrue;
+ self->turretSpinupTime = level.time + MGTURRET_SPINUP_TIME;
+ G_AddEvent( self, EV_MGTURRET_SPINUP, 0 );
}
}
}
@@ -2170,7 +2374,7 @@ void HTeslaGen_Think( gentity_t *self )
self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex );
//if not powered don't do anything and check again for power next think
- if( !( self->powered = G_FindPower( self ) ) || !( self->dcced = G_FindDCC( self ) ) )
+ if( !( self->powered = G_FindPower( self ) ) )
{
self->s.eFlags &= ~EF_FIRING;
self->nextthink = level.time + POWER_REFRESH_TIME;
@@ -2369,16 +2573,6 @@ void HSpawn_Think( gentity_t *self )
G_FreeEntity( ent ); //quietly remove
}
}
-
- //spawn under attack
- if( self->health < self->lastHealth &&
- level.time > level.humanBaseAttackTimer && G_IsDCCBuilt( ) )
- {
- level.humanBaseAttackTimer = level.time + DCC_ATTACK_PERIOD;
- G_BroadcastEvent( EV_DCC_ATTACK, 0 );
- }
-
- self->lastHealth = self->health;
}
self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex );
@@ -2471,6 +2665,8 @@ void G_BuildableThink( gentity_t *ent, int msec )
ent->spawned = qtrue;
}
+ ent->dcc = ( ent->biteam != BIT_HUMANS ) ? 0 : G_FindDCC( ent );
+
ent->s.generic1 = (int)( ( (float)ent->health / (float)bHealth ) * B_HEALTH_MASK );
if( ent->s.generic1 < 0 )
@@ -2479,7 +2675,7 @@ void G_BuildableThink( gentity_t *ent, int msec )
if( ent->powered )
ent->s.generic1 |= B_POWERED_TOGGLEBIT;
- if( ent->dcced )
+ if( ent->dcc )
ent->s.generic1 |= B_DCCED_TOGGLEBIT;
if( ent->spawned )
@@ -2496,9 +2692,19 @@ void G_BuildableThink( gentity_t *ent, int msec )
if( !ent->spawned && ent->health > 0 )
ent->health += (int)( ceil( (float)bHealth / (float)( bTime * 0.001 ) ) );
- else if( ent->biteam == BIT_ALIENS && ent->health > 0 && ent->health < bHealth &&
- bRegen && ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )
+ else if( ent->health > 0 && ent->health < bHealth )
+ {
+ if( ent->biteam == BIT_ALIENS && bRegen &&
+ ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )
+ {
ent->health += bRegen;
+ }
+ else if( ent->biteam == BIT_HUMANS && ent->dcc &&
+ ( ent->lastDamageTime + HUMAN_REGEN_DAMAGE_TIME ) < level.time )
+ {
+ ent->health += DC_HEALRATE * ent->dcc;
+ }
+ }
if( ent->health > bHealth )
ent->health = bHealth;
@@ -3002,7 +3208,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
tempent = G_FindBuildable( BA_A_OVERMIND );
if( tempent == NULL || !tempent->spawned || tempent->health <= 0 )
- reason = IBE_OVERMIND;
+ reason = IBE_NOOVERMIND;
}
//check there is creep near by for building on
@@ -3196,29 +3402,32 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
built->die = ABarricade_Die;
built->think = ABarricade_Think;
built->pain = ABarricade_Pain;
+ built->touch = ABarricade_Touch;
+ built->shrunkTime = 0;
+ ABarricade_Shrink( built, qtrue );
break;
case BA_A_BOOSTER:
- built->die = ABarricade_Die;
- built->think = ABarricade_Think;
- built->pain = ABarricade_Pain;
+ built->die = AGeneric_Die;
+ built->think = AGeneric_Think;
+ built->pain = AGeneric_Pain;
built->touch = ABooster_Touch;
break;
case BA_A_ACIDTUBE:
- built->die = ABarricade_Die;
+ built->die = AGeneric_Die;
built->think = AAcidTube_Think;
built->pain = ASpawn_Pain;
break;
case BA_A_HIVE:
- built->die = ABarricade_Die;
+ built->die = AHive_Die;
built->think = AHive_Think;
- built->pain = ASpawn_Pain;
+ built->pain = AHive_Pain;
break;
case BA_A_TRAPPER:
- built->die = ABarricade_Die;
+ built->die = AGeneric_Die;
built->think = ATrapper_Think;
built->pain = ASpawn_Pain;
break;
@@ -3327,9 +3536,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
else if( ( built->powered = G_FindPower( built ) ) )
built->s.generic1 |= B_POWERED_TOGGLEBIT;
- if( ( built->dcced = G_FindDCC( built ) ) )
- built->s.generic1 |= B_DCCED_TOGGLEBIT;
-
built->s.generic1 &= ~B_SPAWNED_TOGGLEBIT;
VectorCopy( normal, built->s.origin2 );
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 66ba3b6f..0e657f0a 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -86,51 +86,31 @@ G_AddCreditToClient
*/
void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap )
{
+ int capAmount;
+
if( !client )
return;
- //if we're already at the max and trying to add credit then stop
- if( cap )
- {
- if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- if( client->ps.persistant[ PERS_CREDIT ] >= ALIEN_MAX_KILLS &&
- credit > 0 )
- return;
- }
- else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- if( client->ps.persistant[ PERS_CREDIT ] >= HUMAN_MAX_CREDITS &&
- credit > 0 )
- return;
- }
- }
+ client->pers.credit += credit;
+ capAmount = client->pers.teamSelection == PTE_ALIENS ?
+ ALIEN_MAX_KILLS : HUMAN_MAX_CREDITS;
- client->ps.persistant[ PERS_CREDIT ] += credit;
+ if( client->pers.credit > capAmount )
+ client->pers.credit = capAmount;
- if( cap )
- {
- if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- if( client->ps.persistant[ PERS_CREDIT ] > ALIEN_MAX_KILLS )
- client->ps.persistant[ PERS_CREDIT ] = ALIEN_MAX_KILLS;
- }
- else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- if( client->ps.persistant[ PERS_CREDIT ] > HUMAN_MAX_CREDITS )
- client->ps.persistant[ PERS_CREDIT ] = HUMAN_MAX_CREDITS;
- }
- }
+ if( client->pers.credit < 0 )
+ client->pers.credit = 0;
- if( client->ps.persistant[ PERS_CREDIT ] < 0 )
- client->ps.persistant[ PERS_CREDIT ] = 0;
+ // keep PERS_CREDIT in sync if not following
+ if( client->sess.spectatorState != SPECTATOR_FOLLOW )
+ client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
}
/*
=======================================================================
- SelectSpawnPoint
+ G_SelectSpawnPoint
=======================================================================
*/
@@ -165,13 +145,13 @@ qboolean SpotWouldTelefrag( gentity_t *spot )
/*
================
-SelectNearestDeathmatchSpawnPoint
+G_SelectNearestDeathmatchSpawnPoint
Find the spot that we DON'T want to use
================
*/
#define MAX_SPAWN_POINTS 128
-gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from )
+gentity_t *G_SelectNearestDeathmatchSpawnPoint( vec3_t from )
{
gentity_t *spot;
vec3_t delta;
@@ -200,13 +180,13 @@ gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from )
/*
================
-SelectRandomDeathmatchSpawnPoint
+G_SelectRandomDeathmatchSpawnPoint
go to a random point that doesn't telefrag
================
*/
#define MAX_SPAWN_POINTS 128
-gentity_t *SelectRandomDeathmatchSpawnPoint( void )
+gentity_t *G_SelectRandomDeathmatchSpawnPoint( void )
{
gentity_t *spot;
int count;
@@ -235,12 +215,12 @@ gentity_t *SelectRandomDeathmatchSpawnPoint( void )
/*
===========
-SelectRandomFurthestSpawnPoint
+G_SelectRandomFurthestSpawnPoint
Chooses a player start, deathmatch start, etc
============
*/
-gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
{
gentity_t *spot;
vec3_t delta;
@@ -318,12 +298,12 @@ gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, ve
/*
================
-SelectAlienSpawnPoint
+G_SelectAlienSpawnPoint
go to a random point that doesn't telefrag
================
*/
-gentity_t *SelectAlienSpawnPoint( vec3_t preference )
+gentity_t *G_SelectAlienSpawnPoint( vec3_t preference )
{
gentity_t *spot;
int count;
@@ -367,12 +347,12 @@ gentity_t *SelectAlienSpawnPoint( vec3_t preference )
/*
================
-SelectHumanSpawnPoint
+G_SelectHumanSpawnPoint
go to a random point that doesn't telefrag
================
*/
-gentity_t *SelectHumanSpawnPoint( vec3_t preference )
+gentity_t *G_SelectHumanSpawnPoint( vec3_t preference )
{
gentity_t *spot;
int count;
@@ -416,32 +396,32 @@ gentity_t *SelectHumanSpawnPoint( vec3_t preference )
/*
===========
-SelectSpawnPoint
+G_SelectSpawnPoint
Chooses a player start, deathmatch start, etc
============
*/
-gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
{
- return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
+ return G_SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
}
/*
===========
-SelectTremulousSpawnPoint
+G_SelectTremulousSpawnPoint
Chooses a player start, deathmatch start, etc
============
*/
-gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles )
{
gentity_t *spot = NULL;
if( team == PTE_ALIENS )
- spot = SelectAlienSpawnPoint( preference );
+ spot = G_SelectAlienSpawnPoint( preference );
else if( team == PTE_HUMANS )
- spot = SelectHumanSpawnPoint( preference );
+ spot = G_SelectHumanSpawnPoint( preference );
//no available spots
if( !spot )
@@ -462,13 +442,13 @@ gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t or
/*
===========
-SelectInitialSpawnPoint
+G_SelectInitialSpawnPoint
Try to find a spawn point marked 'initial', otherwise
use normal spawn selection.
============
*/
-gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
{
gentity_t *spot;
@@ -481,7 +461,7 @@ gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
if( !spot || SpotWouldTelefrag( spot ) )
{
- return SelectSpawnPoint( vec3_origin, origin, angles );
+ return G_SelectSpawnPoint( vec3_origin, origin, angles );
}
VectorCopy( spot->s.origin, origin );
@@ -493,11 +473,11 @@ gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
/*
===========
-SelectSpectatorSpawnPoint
+G_SelectSpectatorSpawnPoint
============
*/
-gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
{
FindIntermissionPoint( );
@@ -510,13 +490,13 @@ gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
/*
===========
-SelectAlienLockSpawnPoint
+G_SelectAlienLockSpawnPoint
Try to find a spawn point for alien intermission otherwise
use normal intermission spawn.
============
*/
-gentity_t *SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
{
gentity_t *spot;
@@ -524,7 +504,7 @@ gentity_t *SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
spot = G_Find( spot, FOFS( classname ), "info_alien_intermission" );
if( !spot )
- return SelectSpectatorSpawnPoint( origin, angles );
+ return G_SelectSpectatorSpawnPoint( origin, angles );
VectorCopy( spot->s.origin, origin );
VectorCopy( spot->s.angles, angles );
@@ -535,13 +515,13 @@ gentity_t *SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
/*
===========
-SelectHumanLockSpawnPoint
+G_SelectHumanLockSpawnPoint
Try to find a spawn point for human intermission otherwise
use normal intermission spawn.
============
*/
-gentity_t *SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
{
gentity_t *spot;
@@ -549,7 +529,7 @@ gentity_t *SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
spot = G_Find( spot, FOFS( classname ), "info_human_intermission" );
if( !spot )
- return SelectSpectatorSpawnPoint( origin, angles );
+ return G_SelectSpectatorSpawnPoint( origin, angles );
VectorCopy( spot->s.origin, origin );
VectorCopy( spot->s.angles, angles );
@@ -731,11 +711,11 @@ void SpawnCorpse( gentity_t *ent )
/*
==================
-SetClientViewAngle
+G_SetClientViewAngle
==================
*/
-void SetClientViewAngle( gentity_t *ent, vec3_t angle )
+void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
{
int i;
@@ -1119,10 +1099,7 @@ void ClientUserinfoChanged( int clientNum )
strcpy( c1, Info_ValueForKey( userinfo, "color1" ) );
strcpy( c2, Info_ValueForKey( userinfo, "color2" ) );
- if( client->ps.pm_flags & PMF_FOLLOW )
- team = PTE_NONE;
- else
- team = client->ps.stats[ STAT_PTEAM ];
+ team = client->pers.teamSelection;
// send over a subset of the userinfo keys so other clients can
// print scoreboards, display models, and play custom sounds
@@ -1280,6 +1257,7 @@ void ClientBegin( int clientNum )
client->pers.connected = CON_CONNECTED;
client->pers.enterTime = level.time;
client->pers.teamState.state = TEAM_BEGIN;
+ client->pers.classSelection = PCL_NONE;
// save eflags around this, because changing teams will
// cause this to happen with a valid entity, and we
@@ -1292,7 +1270,6 @@ void ClientBegin( int clientNum )
client->ps.eFlags = flags;
// locate ent at a spawn point
-
ClientSpawn( ent, NULL, NULL, NULL );
trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
@@ -1337,7 +1314,6 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
int maxAmmo, maxClips;
weapon_t weapon;
-
index = ent - g_entities;
client = ent->client;
@@ -1367,11 +1343,11 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
if( client->sess.sessionTeam == TEAM_SPECTATOR )
{
if( teamLocal == PTE_NONE )
- spawnPoint = SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
+ spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
else if( teamLocal == PTE_ALIENS )
- spawnPoint = SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
+ spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
else if( teamLocal == PTE_HUMANS )
- spawnPoint = SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+ spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
}
else
{
@@ -1427,6 +1403,10 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->ps.persistant[ PERS_SPAWN_COUNT ]++;
client->ps.persistant[ PERS_TEAM ] = client->sess.sessionTeam;
+ // restore really persistant things
+ client->ps.persistant[ PERS_SCORE ] = client->pers.score;
+ client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
+
client->airOutTime = level.time + 12000;
trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
@@ -1550,7 +1530,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->ps.pm_flags |= PMF_RESPAWNED;
trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
- SetClientViewAngle( ent, spawn_angles );
+ G_SetClientViewAngle( ent, spawn_angles );
if( !( client->sess.sessionTeam == TEAM_SPECTATOR ) )
{
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 6364c8bb..0878957d 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -246,10 +246,12 @@ void ScoreboardMessage( gentity_t *ent )
if( cl->pers.connected == CON_CONNECTING )
ping = -1;
+ else if( cl->sess.spectatorState == SPECTATOR_FOLLOW )
+ ping = cl->pers.ping < 999 ? cl->pers.ping : 999;
else
ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
- if( cl->ps.stats[ STAT_HEALTH ] > 0 )
+ if( cl->sess.sessionTeam != TEAM_SPECTATOR )
{
weapon = cl->ps.weapon;
@@ -273,8 +275,8 @@ void ScoreboardMessage( gentity_t *ent )
}
Com_sprintf( entry, sizeof( entry ),
- " %d %d %d %d %d %d", level.sortedClients[ i ], cl->ps.persistant[ PERS_SCORE ],
- ping, ( level.time - cl->pers.enterTime ) / 60000, weapon, upgrade );
+ " %d %d %d %d %d %d", level.sortedClients[ i ], cl->pers.score, ping,
+ ( level.time - cl->pers.enterTime ) / 60000, weapon, upgrade );
j = strlen( entry );
@@ -363,7 +365,7 @@ void Cmd_Give_f( gentity_t *ent )
if( Q_stricmp( name, "poison" ) == 0 )
{
ent->client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- ent->client->lastBoostedTime = level.time;
+ ent->client->ps.stats[ STAT_MISC2 ] = BOOST_TIME;
}
if( give_all || Q_stricmp( name, "ammo" ) == 0 )
@@ -528,6 +530,9 @@ void G_LeaveTeam( gentity_t *self )
else
return;
+ // stop any following clients
+ G_StopFromFollowing( self );
+
G_TeamVote( self, qfalse );
for( i = 0; i < level.num_entities; i++ )
@@ -541,14 +546,6 @@ void G_LeaveTeam( gentity_t *self )
G_FreeEntity( ent );
if( ent->client && ent->client->pers.connected == CON_CONNECTED )
{
- // stop following clients
- if( ent->client->sess.sessionTeam == TEAM_SPECTATOR &&
- ent->client->sess.spectatorState == SPECTATOR_FOLLOW &&
- ent->client->sess.spectatorClient == self->client->ps.clientNum )
- {
- if( !G_FollowNewClient( ent, 1 ) )
- G_StopFollowing( ent );
- }
// cure poison
if( ent->client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
ent->client->lastPoisonCloudedClient == self )
@@ -581,39 +578,15 @@ void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam )
( ( oldTeam == PTE_HUMANS || oldTeam == PTE_ALIENS )
&& ( level.time - ent->client->pers.teamChangeTime ) > 60000 ) )
{
- if( oldTeam == PTE_NONE )
- {
- // ps.persistant[] from a spectator cannot be trusted
- ent->client->ps.persistant[ PERS_SCORE ] = ent->client->pers.savedScore;
- ent->client->ps.persistant[ PERS_CREDIT ] = ent->client->pers.savedCredit;
- }
- else if( oldTeam == PTE_ALIENS )
- {
- // always save in human credtis
- ent->client->ps.persistant[ PERS_CREDIT ] *=
- (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
- }
-
- if( newTeam == PTE_NONE )
- {
- // save values before the client enters the spectator team and their
- // ps.persistant[] values become trashed
- ent->client->pers.savedScore = ent->client->ps.persistant[ PERS_SCORE ];
- ent->client->pers.savedCredit = ent->client->ps.persistant[ PERS_CREDIT ];
- }
+ if( oldTeam == PTE_ALIENS )
+ ent->client->pers.credit *= (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
else if( newTeam == PTE_ALIENS )
- {
- // convert to alien currency
- ent->client->ps.persistant[ PERS_CREDIT ] *=
- (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
+ ent->client->pers.credit *= (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
}
- }
else
{
- ent->client->ps.persistant[ PERS_CREDIT ] = 0;
- ent->client->ps.persistant[ PERS_SCORE ] = 0;
- ent->client->pers.savedScore = 0;
- ent->client->pers.savedCredit = 0;
+ ent->client->pers.credit = 0;
+ ent->client->pers.score = 0;
}
ent->client->pers.classSelection = PCL_NONE;
@@ -741,7 +714,7 @@ void Cmd_Team_f( gentity_t *ent )
return;
//guard against build timer exploit
- if( oldteam != PTE_NONE &&
+ if( oldteam != PTE_NONE && ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
( ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 ||
ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ||
BG_InventoryContainsWeapon( WP_HBUILD, ent->client->ps.stats ) ||
@@ -800,10 +773,10 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons
if( BG_ClientListTest( &other->client->sess.ignoreList, ent-g_entities ) )
ignore = qtrue;
- trap_SendServerCommand( other-g_entities, va( "%s \"%s%s%c%c%s\"",
+ trap_SendServerCommand( other-g_entities, va( "%s \"%s%s%c%c%s%s\"",
mode == SAY_TEAM ? "tchat" : "chat",
( ignore ) ? "[skipnotify]" : "",
- name, Q_COLOR_ESCAPE, color, message ) );
+ name, Q_COLOR_ESCAPE, color, message, S_COLOR_WHITE ) );
}
#define EC "\x19"
@@ -880,10 +853,6 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
return;
}
- // echo the text to the console
- if( g_dedicated.integer )
- G_Printf( "%s%s\n", name, text);
-
// send it to all the apropriate clients
for( j = 0; j < level.maxclients; j++ )
{
@@ -1158,6 +1127,8 @@ void Cmd_CallVote_f( gentity_t *ent )
trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
" called a vote\n\"", ent->client->pers.netname ) );
+ G_Printf( "'%s' called a vote for '%s'\n", ent->client->pers.netname,
+ level.voteString ) ;
ent->client->pers.voteCount++;
@@ -1388,6 +1359,9 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
G_TeamCommand( team, va( "print \"%s " S_COLOR_WHITE "called a team vote\n\"",
ent->client->pers.netname ) );
+ G_Printf( "'%s' called a teamvote for '%s'\n", ent->client->pers.netname,
+ level.teamVoteString[ cs_offset ] ) ;
+
// start the voting, the caller autoamtically votes yes
level.teamVoteTime[ cs_offset ] = level.time;
level.teamVoteYes[ cs_offset ] = 1;
@@ -1552,9 +1526,7 @@ void Cmd_Class_f( gentity_t *ent )
int clientNum;
int i;
vec3_t infestOrigin;
- int allowedClasses[ PCL_NUM_CLASSES ];
- int numClasses = 0;
- pClass_t currentClass = ent->client->ps.stats[ STAT_PCLASS ];
+ pClass_t currentClass = ent->client->pers.classSelection;
pClass_t newClass;
int numLevels;
int entityList[ MAX_GENTITIES ];
@@ -1563,30 +1535,92 @@ void Cmd_Class_f( gentity_t *ent )
int num;
gentity_t *other;
- if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 )
- return;
-
clientNum = ent->client - level.clients;
trap_Argv( 1, s, sizeof( s ) );
+ newClass = BG_FindClassNumForName( s );
- if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0 ) )
- allowedClasses[ numClasses++ ] = PCL_ALIEN_BUILDER0;
+ if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
+ {
+ if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+ G_StopFollowing( ent );
+ if( ent->client->pers.teamSelection == PTE_ALIENS )
+ {
+ if( newClass != PCL_ALIEN_BUILDER0 &&
+ newClass != PCL_ALIEN_BUILDER0_UPG &&
+ newClass != PCL_ALIEN_LEVEL0 )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"You cannot spawn with class %s\n\"", s ) );
+ return;
+ }
- if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0_UPG ) &&
- BG_FindStagesForClass( PCL_ALIEN_BUILDER0_UPG, g_alienStage.integer ) )
- allowedClasses[ numClasses++ ] = PCL_ALIEN_BUILDER0_UPG;
+ if( !BG_ClassIsAllowed( newClass ) )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"Class %s is not allowed\n\"", s ) );
+ return;
+ }
+
+ if( !BG_FindStagesForClass( newClass, g_alienStage.integer ) )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"Class %s not allowed at stage %d\n\"",
+ s, g_alienStage.integer ) );
+ return;
+ }
+
+ // spawn from an egg
+ if( G_PushSpawnQueue( &level.alienSpawnQueue, clientNum ) )
+ {
+ ent->client->pers.classSelection = newClass;
+ ent->client->ps.stats[ STAT_PCLASS ] = newClass;
+ }
+ }
+ else if( ent->client->pers.teamSelection == PTE_HUMANS )
+ {
+ //set the item to spawn with
+ if( !Q_stricmp( s, BG_FindNameForWeapon( WP_MACHINEGUN ) ) &&
+ BG_WeaponIsAllowed( WP_MACHINEGUN ) )
+ {
+ ent->client->pers.humanItemSelection = WP_MACHINEGUN;
+ }
+ else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD ) ) &&
+ BG_WeaponIsAllowed( WP_HBUILD ) )
+ {
+ ent->client->pers.humanItemSelection = WP_HBUILD;
+ }
+ else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD2 ) ) &&
+ BG_WeaponIsAllowed( WP_HBUILD2 ) &&
+ BG_FindStagesForWeapon( WP_HBUILD2, g_humanStage.integer ) )
+ {
+ ent->client->pers.humanItemSelection = WP_HBUILD2;
+ }
+ else
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Unknown starting item\n\"" );
+ return;
+ }
+ // spawn from a telenode
+ if( G_PushSpawnQueue( &level.humanSpawnQueue, clientNum ) )
+ {
+ ent->client->pers.classSelection = PCL_HUMAN;
+ ent->client->ps.stats[ STAT_PCLASS ] = PCL_HUMAN;
+ }
+ }
+ return;
+ }
- if( BG_ClassIsAllowed( PCL_ALIEN_LEVEL0 ) )
- allowedClasses[ numClasses++ ] = PCL_ALIEN_LEVEL0;
+ if( ent->health <= 0 )
+ return;
if( ent->client->pers.teamSelection == PTE_ALIENS &&
!( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) &&
!( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) )
{
- newClass = BG_FindClassNumForName( s );
if( newClass == PCL_NONE )
{
- trap_SendServerCommand( ent-g_entities, va( "print \"Unknown class\n\"" ) );
+ trap_SendServerCommand( ent-g_entities, "print \"Unknown class\n\"" );
return;
}
@@ -1596,7 +1630,8 @@ void Cmd_Class_f( gentity_t *ent )
if( ( ent->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
( ent->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) )
{
- trap_SendServerCommand( ent-g_entities, va( "print \"You cannot evolve while wallwalking\n\"" ) );
+ trap_SendServerCommand( ent-g_entities,
+ "print \"You cannot evolve while wallwalking\n\"" );
return;
}
@@ -1624,7 +1659,8 @@ void Cmd_Class_f( gentity_t *ent )
}
//guard against selling the HBUILD weapons exploit
- if( ( currentClass == PCL_ALIEN_BUILDER0 ||
+ if( ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
+ ( currentClass == PCL_ALIEN_BUILDER0 ||
currentClass == PCL_ALIEN_BUILDER0_UPG ) &&
ent->client->ps.stats[ STAT_MISC ] > 0 )
{
@@ -1662,7 +1698,7 @@ void Cmd_Class_f( gentity_t *ent )
else
{
trap_SendServerCommand( ent-g_entities,
- va( "print \"You cannot evolve from your current class\n\"" ) );
+ "print \"You cannot evolve from your current class\n\"" );
return;
}
}
@@ -1672,53 +1708,13 @@ void Cmd_Class_f( gentity_t *ent )
return;
}
}
- else
- {
- //spawning from an egg
- for( i = 0; i < numClasses; i++ )
- {
- if( allowedClasses[ i ] == newClass &&
- BG_FindStagesForClass( newClass, g_alienStage.integer ) &&
- BG_ClassIsAllowed( newClass ) )
- {
- ent->client->pers.classSelection =
- ent->client->ps.stats[ STAT_PCLASS ] = newClass;
- G_PushSpawnQueue( &level.alienSpawnQueue, clientNum );
- return;
- }
- }
- trap_SendServerCommand( ent-g_entities, va( "print \"You cannot spawn as this class\n\"" ) );
- return;
- }
}
else if( ent->client->pers.teamSelection == PTE_HUMANS )
{
//humans cannot use this command whilst alive
- if( ent->client->pers.classSelection != PCL_NONE )
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"You must be dead to use the class command\n\"" ) );
- return;
- }
-
- ent->client->pers.classSelection =
- ent->client->ps.stats[ STAT_PCLASS ] = PCL_HUMAN;
-
- //set the item to spawn with
- if( !Q_stricmp( s, BG_FindNameForWeapon( WP_MACHINEGUN ) ) && BG_WeaponIsAllowed( WP_MACHINEGUN ) )
- ent->client->pers.humanItemSelection = WP_MACHINEGUN;
- else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD ) ) && BG_WeaponIsAllowed( WP_HBUILD ) )
- ent->client->pers.humanItemSelection = WP_HBUILD;
- else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD2 ) ) && BG_WeaponIsAllowed( WP_HBUILD2 ) &&
- BG_FindStagesForWeapon( WP_HBUILD2, g_humanStage.integer ) )
- ent->client->pers.humanItemSelection = WP_HBUILD2;
- else
- {
- ent->client->pers.classSelection = PCL_NONE;
- trap_SendServerCommand( ent-g_entities, va( "print \"Unknown starting item\n\"" ) );
+ trap_SendServerCommand( ent-g_entities,
+ "print \"You must be dead to use the class command\n\"" );
return;
- }
-
- G_PushSpawnQueue( &level.humanSpawnQueue, clientNum );
}
}
@@ -1764,6 +1760,13 @@ void Cmd_Destroy_f( gentity_t *ent )
( ( ent->client->ps.weapon >= WP_ABUILD ) &&
( ent->client->ps.weapon <= WP_HBUILD ) ) )
{
+ // Always let the builder prevent the explosion
+ if( traceEnt->health <= 0 )
+ {
+ G_FreeEntity( traceEnt );
+ return;
+ }
+
// Cancel deconstruction
if( g_markDeconstruct.integer && traceEnt->deconstruct )
{
@@ -1793,19 +1796,18 @@ void Cmd_Destroy_f( gentity_t *ent )
return;
// Don't allow destruction of buildables that cannot be rebuilt
- if( G_TimeTilSuddenDeath( ) <= 0 &&
- BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) )
+ if( G_TimeTilSuddenDeath( ) <= 0 )
{
return;
}
- if( ent->client->ps.stats[ STAT_MISC ] > 0 )
+ if( !g_markDeconstruct.integer && ent->client->ps.stats[ STAT_MISC ] > 0 )
{
G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum );
return;
}
- if( traceEnt->health > 0 )
+ if( g_markDeconstruct.integer )
{
if( g_markDeconstruct.integer )
{
@@ -1832,14 +1834,13 @@ void Cmd_Destroy_f( gentity_t *ent )
if( !g_cheats.integer )
ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon ) >> 2;
+ BG_FindBuildTimeForBuildable( traceEnt->s.modelindex );
}
}
}
}
}
-
/*
=================
Cmd_ActivateItem_f
@@ -2337,9 +2338,24 @@ void Cmd_Build_f( gentity_t *ent )
return;
}
+ if( ent->client->pers.teamSelection == level.surrenderTeam )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Building has been denied to traitorous cowards\n\"" );
+ return;
+ }
+
trap_Argv( 1, s, sizeof( s ) );
buildable = BG_FindBuildNumForName( s );
+
+ if( G_TimeTilSuddenDeath( ) <= 0 )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Building is not allowed during Sudden Death\n\"" );
+ return;
+ }
+
team = ent->client->ps.stats[ STAT_PTEAM ];
if( buildable != BA_NONE &&
@@ -2363,6 +2379,8 @@ void Cmd_Build_f( gentity_t *ent )
case IBE_NOROOM:
case IBE_NORMAL:
case IBE_HOVELEXIT:
+ case IBE_REPEATER:
+ case IBE_NOCREEP:
ent->client->ps.stats[ STAT_BUILDABLE ] = ( buildable | SB_VALID_TOGGLEBIT );
break;
@@ -2382,18 +2400,10 @@ void Cmd_Build_f( gentity_t *ent )
G_TriggerMenu( ent->client->ps.clientNum, MN_H_REACTOR );
break;
- case IBE_REPEATER:
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_REPEATER );
- break;
-
case IBE_NOPOWER:
G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOPOWER );
break;
- case IBE_NOCREEP:
- G_TriggerMenu( ent->client->ps.clientNum, MN_A_NOCREEP );
- break;
-
case IBE_NODCC:
G_TriggerMenu( ent->client->ps.clientNum, MN_H_NODCC );
break;
@@ -2406,34 +2416,39 @@ void Cmd_Build_f( gentity_t *ent )
trap_SendServerCommand( ent-g_entities, va( "print \"Cannot build this item\n\"" ) );
}
-
/*
=================
-Cmd_Boost_f
+Cmd_Reload_f
=================
*/
-void Cmd_Boost_f( gentity_t *ent )
+void Cmd_Reload_f( gentity_t *ent )
{
- if( BG_InventoryContainsUpgrade( UP_JETPACK, ent->client->ps.stats ) &&
- BG_UpgradeIsActive( UP_JETPACK, ent->client->ps.stats ) )
- return;
-
- if( ent->client->pers.cmd.buttons & BUTTON_WALKING )
- return;
-
- if( ent->client->ps.stats[ STAT_STAMINA ] > 0 )
- ent->client->ps.stats[ STAT_STATE ] |= SS_SPEEDBOOST;
+ if( ent->client->ps.weaponstate != WEAPON_RELOADING )
+ ent->client->ps.pm_flags |= PMF_WEAPON_RELOAD;
}
/*
=================
-Cmd_Reload_f
+G_StopFromFollowing
+
+stops any other clients from following this one
+called when a player leaves a team or dies
=================
*/
-void Cmd_Reload_f( gentity_t *ent )
+void G_StopFromFollowing( gentity_t *ent )
{
- if( ent->client->ps.weaponstate != WEAPON_RELOADING )
- ent->client->ps.pm_flags |= PMF_WEAPON_RELOAD;
+ int i;
+
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR &&
+ level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
+ level.clients[ i ].sess.spectatorClient == ent->client->ps.clientNum )
+ {
+ if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
+ G_StopFollowing( &g_entities[ i ] );
+ }
+ }
}
/*
@@ -2448,10 +2463,29 @@ void G_StopFollowing( gentity_t *ent )
{
ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
ent->client->sess.sessionTeam = TEAM_SPECTATOR;
- ent->client->sess.spectatorState = SPECTATOR_FREE;
+ ent->client->ps.stats[ STAT_PTEAM ] = ent->client->pers.teamSelection;
+
+ if( ent->client->pers.teamSelection == PTE_NONE )
+ {
+ ent->client->sess.spectatorState = SPECTATOR_FREE;
+ }
+ else
+ {
+ vec3_t spawn_origin, spawn_angles;
+
+ ent->client->sess.spectatorState = SPECTATOR_LOCKED;
+
+ if( ent->client->pers.teamSelection == PTE_ALIENS )
+ G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
+ else if( ent->client->pers.teamSelection == PTE_HUMANS )
+ G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+
+ G_SetOrigin( ent, spawn_origin );
+ VectorCopy( spawn_origin, ent->client->ps.origin );
+ G_SetClientViewAngle( ent, spawn_angles );
+ }
ent->client->sess.spectatorClient = -1;
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
- ent->client->ps.stats[ STAT_PTEAM ] = PTE_NONE;
ent->client->ps.stats[ STAT_STATE ] &= ~SS_WALLCLIMBING;
ent->client->ps.stats[ STAT_STATE ] &= ~SS_WALLCLIMBINGCEILING;
@@ -2519,6 +2553,14 @@ qboolean G_FollowNewClient( gentity_t *ent, int dir )
if( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR )
continue;
+ // can only follow teammates when dead and on a team
+ if( ent->client->pers.teamSelection != PTE_NONE &&
+ ( level.clients[ clientnum ].pers.teamSelection !=
+ ent->client->pers.teamSelection ) )
+ {
+ continue;
+ }
+
// this is good, we can use it
ent->client->sess.spectatorClient = clientnum;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
@@ -2557,8 +2599,7 @@ void Cmd_Follow_f( gentity_t *ent )
{
G_ToggleFollow( ent );
}
- else if( ent->client->sess.spectatorState == SPECTATOR_FREE ||
- ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+ else
{
trap_Argv( 1, arg, sizeof( arg ) );
if( G_ClientNumbersFromString( arg, pids, MAX_CLIENTS ) == 1 )
@@ -2582,9 +2623,17 @@ void Cmd_Follow_f( gentity_t *ent )
return;
// can't follow another spectator
- if( level.clients[ i ].pers.teamSelection == PTE_NONE )
+ if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR )
return;
+ // can only follow teammates when dead and on a team
+ if( ent->client->pers.teamSelection != PTE_NONE &&
+ ( level.clients[ i ].pers.teamSelection !=
+ ent->client->pers.teamSelection ) )
+ {
+ return;
+ }
+
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
ent->client->sess.spectatorClient = i;
}
@@ -2605,6 +2654,8 @@ void Cmd_FollowCycle_f( gentity_t *ent )
dir = -1;
// won't work unless spectating
+ if( ent->client->sess.sessionTeam != TEAM_SPECTATOR )
+ return;
if( ent->client->sess.spectatorState == SPECTATOR_NOT )
return;
@@ -2807,9 +2858,9 @@ commands_t cmds[ ] = {
{ "ptrcverify", 0, Cmd_PTRCVerify_f },
{ "ptrcrestore", 0, Cmd_PTRCRestore_f },
- { "follow", CMD_NOTEAM, Cmd_Follow_f },
- { "follownext", CMD_NOTEAM, Cmd_FollowCycle_f },
- { "followprev", CMD_NOTEAM, Cmd_FollowCycle_f },
+ { "follow", CMD_SPEC, Cmd_Follow_f },
+ { "follownext", CMD_SPEC, Cmd_FollowCycle_f },
+ { "followprev", CMD_SPEC, Cmd_FollowCycle_f },
{ "where", CMD_TEAM, Cmd_Where_f },
{ "teamvote", CMD_TEAM, Cmd_TeamVote_f },
@@ -2824,7 +2875,8 @@ commands_t cmds[ ] = {
{ "itemdeact", CMD_HUMAN|CMD_LIVING, Cmd_DeActivateItem_f },
{ "itemtoggle", CMD_HUMAN|CMD_LIVING, Cmd_ToggleItem_f },
{ "reload", CMD_HUMAN|CMD_LIVING, Cmd_Reload_f },
- { "boost", CMD_HUMAN|CMD_LIVING, Cmd_Boost_f }
+
+ { "test", 0, Cmd_Test_f }
};
static int numCmds = sizeof( cmds ) / sizeof( cmds[ 0 ] );
@@ -2881,11 +2933,11 @@ void ClientCommand( int clientNum )
return;
}
- if( cmds[ i ].cmdFlags & CMD_NOTEAM &&
- ent->client->pers.teamSelection != PTE_NONE )
+ if( cmds[ i ].cmdFlags & CMD_SPEC &&
+ ent->client->sess.sessionTeam != TEAM_SPECTATOR )
{
trap_SendServerCommand( clientNum,
- "print \"Cannot use this command when on a team\n\"" );
+ "print \"You can only use this command when spectating\n\"" );
return;
}
@@ -3163,3 +3215,7 @@ void G_PrivateMessage( gentity_t *ent )
}
}
+void Cmd_Test_f( gentity_t *ent )
+{
+ G_Printf("%d\n", ent->client->pers.score);
+}
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index 6c8dcc2c..cf068fc0 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -108,7 +108,8 @@ char *modNames[ ] =
"MOD_LEVEL2_CLAW",
"MOD_LEVEL2_ZAP",
"MOD_LEVEL4_CLAW",
- "MOD_LEVEL4_CHARGE",
+ "MOD_LEVEL4_TRAMPLE",
+ "MOD_LEVEL4_CRUSH",
"MOD_SLOWBLOB",
"MOD_POISON",
@@ -139,7 +140,6 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
float totalDamage = 0.0f;
gentity_t *player;
-
if( self->client->ps.pm_type == PM_DEAD )
return;
@@ -147,16 +147,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
return;
// stop any following clients
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR &&
- level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
- level.clients[ i ].sess.spectatorClient == self->client->ps.clientNum )
- {
- if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
- G_StopFollowing( &g_entities[ i ] );
- }
- }
+ G_StopFromFollowing( self );
self->client->ps.pm_type = PM_DEAD;
self->suicideTime = 0;
@@ -1008,6 +999,14 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
}
+ // don't do friendly fire on movement attacks
+ if( ( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
+ mod == MOD_LEVEL4_CRUSH ) &&
+ targ->s.eType == ET_BUILDABLE && targ->biteam == BIT_ALIENS )
+ {
+ return;
+ }
+
// check for completely getting out of the damage
if( !( dflags & DAMAGE_NO_PROTECTION ) )
{
@@ -1016,7 +1015,24 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
// if the attacker was on the same team
if( targ != attacker && OnSameTeam( targ, attacker ) )
{
- if( !g_friendlyFire.integer )
+ // don't do friendly fire on movement attacks
+ if( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
+ mod == MOD_LEVEL4_CRUSH )
+ return;
+
+ if( g_dretchPunt.integer &&
+ targ->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL0 )
+ {
+ vec3_t dir, push;
+
+ VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, dir );
+ VectorNormalizeFast( dir );
+ VectorScale( dir, ( damage * 10.0f ), push );
+ push[2] = 64.0f;
+ VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity );
+ return;
+ }
+ else if( !g_friendlyFire.integer )
{
if( !g_friendlyFireHumans.integer
&& targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
@@ -1031,13 +1047,22 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
}
- // If target is buildable on the same team as the attacking client
- if( targ->s.eType == ET_BUILDABLE && attacker->client &&
- targ->biteam == attacker->client->pers.teamSelection )
- {
- if( !g_friendlyBuildableFire.integer )
- return;
- }
+ if( targ->s.eType == ET_BUILDABLE && attacker->client )
+ {
+ if( targ->biteam == attacker->client->pers.teamSelection )
+ {
+ if( !g_friendlyBuildableFire.integer )
+ return;
+ }
+
+ // base is under attack warning if DCC'd
+ if( targ->biteam == BIT_HUMANS && G_FindDCC( targ ) &&
+ level.time > level.humanBaseAttackTimer )
+ {
+ level.humanBaseAttackTimer = level.time + DC_ATTACK_PERIOD;
+ G_BroadcastEvent( EV_DCC_ATTACK, 0 );
+ }
+ }
// check for godmode
if ( targ->flags & FL_GODMODE )
@@ -1092,9 +1117,9 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
//if boosted poison every attack
if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
{
- if( !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) &&
- !BG_InventoryContainsUpgrade( UP_BATTLESUIT, targ->client->ps.stats ) &&
- mod != MOD_LEVEL2_ZAP &&
+ if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
+ mod != MOD_LEVEL2_ZAP && mod != MOD_POISON &&
+ mod != MOD_LEVEL1_PCLOUD &&
targ->client->poisonImmunityTime < level.time )
{
targ->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
diff --git a/src/game/g_local.h b/src/game/g_local.h
index c9538bbd..14673f2f 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -172,7 +172,6 @@ struct gentity_s
int splashRadius;
int methodOfDeath;
int splashMethodOfDeath;
- int chargeRepeat;
int count;
@@ -197,12 +196,13 @@ struct gentity_s
int biteam; // buildable item team
gentity_t *parentNode; // for creep and defence/spawn dependencies
qboolean active; // for power repeater, but could be useful elsewhere
+ qboolean locked; // used for turret tracking
qboolean powered; // for human buildables
int builtBy; // clientNum of person that built this
- gentity_t *dccNode; // controlling dcc
gentity_t *overmindNode; // controlling overmind
- qboolean dcced; // controlled by a dcc or not?
+ int dcc; // number of controlling dccs
qboolean spawned; // whether or not this buildable has finished spawning
+ int shrunkTime; // time when a barricade shrunk or zero
int buildTime; // when this buildable was built
int time1000; // timer evaluated every second
qboolean deconstruct; // deconstruct if no BP left
@@ -222,6 +222,8 @@ struct gentity_s
gentity_t *targeted; // true if the player is currently a valid target of a turret
vec3_t turretAim; // aim vector for turrets
+ vec3_t turretAimRate; // track turn speed for norfenturrets
+ int turretSpinupTime; // spinup delay for norfenturrets
vec4_t animation; // animated map objects
@@ -239,6 +241,14 @@ struct gentity_s
int suicideTime; // when the client will suicide
int lastDamageTime;
+ int lastRegenTime;
+
+ qboolean zapping; // adv maurader is zapping
+ qboolean wasZapping; // adv maurader was zapping
+ int zapTargets[ LEVEL2_AREAZAP_MAX_TARGETS ];
+ float zapDmg; // keep track of damage
+
+ qboolean ownerClear; // used for missle tracking
};
typedef enum
@@ -342,9 +352,10 @@ typedef struct
int nameChangeTime;
int nameChanges;
- // used to save persistant[] values while in SPECTATOR_FOLLOW mode
- int savedScore;
- int savedCredit;
+ // used to save playerState_t values while in SPECTATOR_FOLLOW mode
+ int score;
+ int credit;
+ int ping;
// votes
qboolean vote;
@@ -441,14 +452,11 @@ struct gclient_s
int grabExpiryTime;
int lastLockTime;
int lastSlowTime;
- int lastBoostedTime;
int lastMedKitTime;
int medKitHealthToRestore;
int medKitIncrementTime;
int lastCreepSlowTime; // time until creep can be removed
- qboolean allowedToPounce;
-
qboolean charging;
vec3_t hovelOrigin; // player origin before entering hovel
@@ -460,6 +468,11 @@ struct gclient_s
unlagged_t unlaggedCalc;
int unlaggedTime;
+ int lcannonStartTime;
+
+ // Tyrant crush
+ int forceCrouchTime;
+ int lastCrushTime;
};
@@ -477,7 +490,8 @@ void G_InitSpawnQueue( spawnQueue_t *sq );
int G_GetSpawnQueueLength( spawnQueue_t *sq );
int G_PopSpawnQueue( spawnQueue_t *sq );
int G_PeekSpawnQueue( spawnQueue_t *sq );
-void G_PushSpawnQueue( spawnQueue_t *sq, int clientNum );
+qboolean G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum );
+qboolean G_PushSpawnQueue( spawnQueue_t *sq, int clientNum );
qboolean G_RemoveFromSpawnQueue( spawnQueue_t *sq, int clientNum );
int G_GetPosInSpawnQueue( spawnQueue_t *sq, int clientNum );
@@ -542,6 +556,7 @@ typedef struct
int framenum;
int time; // in msec
int previousTime; // so movers can back up when blocked
+ int frameMsec; // trap_Milliseconds() at end frame
int startTime; // level.time the map was started
@@ -661,7 +676,7 @@ typedef struct
#define CMD_CHEAT 0x01
#define CMD_MESSAGE 0x02 // sends message to others (skip when muted)
#define CMD_TEAM 0x04 // must be on a team
-#define CMD_NOTEAM 0x08 // must not be on a team
+#define CMD_SPEC 0x08 // must be in spectator mode
#define CMD_ALIEN 0x10
#define CMD_HUMAN 0x20
#define CMD_LIVING 0x40
@@ -689,6 +704,7 @@ char *G_NewString( const char *string );
// g_cmds.c
//
void Cmd_Score_f( gentity_t *ent );
+void G_StopFromFollowing( gentity_t *ent );
void G_StopFollowing( gentity_t *ent );
qboolean G_FollowNewClient( gentity_t *ent, int dir );
void G_ToggleFollow( gentity_t *ent );
@@ -702,6 +718,7 @@ void G_LeaveTeam( gentity_t *self );
void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam );
void G_SanitiseName( char *in, char *out );
void G_PrivateMessage( gentity_t *ent );
+void Cmd_Test_f( gentity_t *ent );
//
// g_physics.c
@@ -747,7 +764,9 @@ gentity_t *G_CheckSpawnPoint( int spawnNum, vec3_t origin, vec3_t normal
buildable_t G_IsPowered( vec3_t origin );
qboolean G_IsDCCBuilt( void );
+int G_FindDCC( gentity_t *self );
qboolean G_IsOvermindBuilt( void );
+qboolean G_FindCreep( gentity_t *self );
void G_BuildableThink( gentity_t *ent, int msec );
qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable );
@@ -840,7 +859,7 @@ void G_RunMissile( gentity_t *ent );
gentity_t *fire_flamer( gentity_t *self, vec3_t start, vec3_t aimdir );
gentity_t *fire_blaster( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir );
-gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius );
+gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius, int speed );
gentity_t *fire_lockblob( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *fire_paraLockBlob( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *fire_slowBlob( gentity_t *self, vec3_t start, vec3_t dir );
@@ -896,8 +915,9 @@ void SnapVectorTowards( vec3_t v, vec3_t to );
qboolean CheckVenomAttack( gentity_t *ent );
void CheckGrabAttack( gentity_t *ent );
qboolean CheckPounceAttack( gentity_t *ent );
-void ChargeAttack( gentity_t *ent, gentity_t *victim );
-void G_UpdateZaps( int msec );
+void G_ChargeAttack( gentity_t *ent, gentity_t *victim );
+void G_CrushAttack( gentity_t *ent, gentity_t *victim, float sec );
+void G_UpdateZaps( gentity_t *ent );
//
@@ -905,9 +925,11 @@ void G_UpdateZaps( int msec );
//
void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap );
team_t TeamCount( int ignoreClientNum, int team );
-void SetClientViewAngle( gentity_t *ent, vec3_t angle );
-gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles );
-gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles );
+void G_SetClientViewAngle( gentity_t *ent, vec3_t angle );
+gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles );
+gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles );
+gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles );
+gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles );
void SpawnCorpse( gentity_t *ent );
void respawn( gentity_t *ent );
void BeginIntermission( void );
@@ -1183,6 +1205,8 @@ extern vmCvar_t g_adminParseSay;
extern vmCvar_t g_adminNameProtect;
extern vmCvar_t g_adminTempBan;
+extern vmCvar_t g_dretchPunt;
+
extern vmCvar_t g_privateMessages;
void trap_Printf( const char *fmt );
diff --git a/src/game/g_main.c b/src/game/g_main.c
index dd258c13..82647557 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -128,6 +128,8 @@ vmCvar_t g_adminParseSay;
vmCvar_t g_adminNameProtect;
vmCvar_t g_adminTempBan;
+vmCvar_t g_dretchPunt;
+
vmCvar_t g_privateMessages;
vmCvar_t g_tag;
@@ -157,7 +159,7 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse },
- { &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue },
+ { &g_friendlyFire, "g_friendlyFire", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
{ &g_friendlyFireAliens, "g_friendlyFireAliens", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_friendlyFireHumans, "g_friendlyFireHumans", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_friendlyBuildableFire, "g_friendlyBuildableFire", "0", CVAR_ARCHIVE, 0, qtrue },
@@ -234,6 +236,7 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_currentMap, "g_currentMap", "0", 0, 0, qfalse },
{ &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_shove, "g_shove", "0.0", CVAR_ARCHIVE, 0, qfalse },
+
{ &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse },
{ NULL, "g_mapConfigsLoaded", "0", CVAR_ROM, 0, qfalse },
@@ -246,6 +249,8 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminTempBan, "g_adminTempBan", "120", CVAR_ARCHIVE, 0, qfalse },
+ { &g_dretchPunt, "g_dretchPunt", "0", CVAR_ARCHIVE, 0, qfalse },
+
{ &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse },
@@ -739,9 +744,9 @@ int QDECL SortRanks( const void *a, const void *b )
cb = &level.clients[ *(int *)b ];
// then sort by score
- if( ca->ps.persistant[ PERS_SCORE ] > cb->ps.persistant[ PERS_SCORE ] )
+ if( ca->pers.score > cb->pers.score )
return -1;
- else if( ca->ps.persistant[ PERS_SCORE ] < cb->ps.persistant[ PERS_SCORE ] )
+ else if( ca->pers.score < cb->pers.score )
return 1;
else
return 0;
@@ -823,17 +828,42 @@ int G_PeekSpawnQueue( spawnQueue_t *sq )
/*
============
+G_SearchSpawnQueue
+
+Look to see if clientNum is already in the spawnQueue
+============
+*/
+qboolean G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum )
+{
+ int i;
+
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+ if( sq->clients[ i ] == clientNum )
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+============
G_PushSpawnQueue
Add an element to the back of the spawn queue
============
*/
-void G_PushSpawnQueue( spawnQueue_t *sq, int clientNum )
+qboolean G_PushSpawnQueue( spawnQueue_t *sq, int clientNum )
{
+ // don't add the same client more than once
+ if( G_SearchSpawnQueue( sq, clientNum ) )
+ return qfalse;
+
sq->back = QUEUE_PLUS1( sq->back );
sq->clients[ sq->back ] = clientNum;
g_entities[ clientNum ].client->ps.pm_flags |= PMF_QUEUED;
+ return qtrue;
}
/*
@@ -967,7 +997,7 @@ void G_SpawnClients( pTeam_t team )
clientNum = G_PeekSpawnQueue( sq );
ent = &g_entities[ clientNum ];
- if( ( spawn = SelectTremulousSpawnPoint( team,
+ if( ( spawn = G_SelectTremulousSpawnPoint( team,
ent->client->pers.lastDeathLocation,
spawn_origin, spawn_angles ) ) )
{
@@ -1272,10 +1302,6 @@ and team change.
void CalculateRanks( void )
{
int i;
- int rank;
- int score;
- int newScore;
- gclient_t *cl;
char P[ MAX_CLIENTS + 1 ] = {""};
int ff = 0;
@@ -1340,30 +1366,6 @@ void CalculateRanks( void )
qsort( level.sortedClients, level.numConnectedClients,
sizeof( level.sortedClients[ 0 ] ), SortRanks );
- // set the rank value for all clients that are connected and not spectators
- rank = -1;
- score = 0;
- for( i = 0; i < level.numPlayingClients; i++ )
- {
- cl = &level.clients[ level.sortedClients[ i ] ];
- newScore = cl->ps.persistant[ PERS_SCORE ];
-
- if( i == 0 || newScore != score )
- {
- rank = i;
- // assume we aren't tied until the next client is checked
- level.clients[ level.sortedClients[ i ] ].ps.persistant[ PERS_RANK ] = rank;
- }
- else
- {
- // we are tied with the previous client
- level.clients[ level.sortedClients[ i - 1 ] ].ps.persistant[ PERS_RANK ] = rank;
- level.clients[ level.sortedClients[ i ] ].ps.persistant[ PERS_RANK ] = rank;
- }
-
- score = newScore;
- }
-
// see if it is time to end the level
CheckExitRules( );
@@ -1449,7 +1451,7 @@ void FindIntermissionPoint( void )
if( !ent )
{ // the map creator forgot to put in an intermission point...
- SelectSpawnPoint( vec3_origin, level.intermission_origin, level.intermission_angle );
+ G_SelectSpawnPoint( vec3_origin, level.intermission_origin, level.intermission_angle );
}
else
{
@@ -2358,7 +2360,6 @@ void G_RunFrame( int levelTime )
G_SpawnClients( PTE_ALIENS );
G_SpawnClients( PTE_HUMANS );
G_CalculateAvgPlayers( );
- G_UpdateZaps( msec );
// see if it is time to end the level
CheckExitRules( );
@@ -2383,5 +2384,7 @@ void G_RunFrame( int levelTime )
trap_Cvar_Set( "g_listEntity", "0" );
}
+
+ level.frameMsec = trap_Milliseconds();
}
diff --git a/src/game/g_misc.c b/src/game/g_misc.c
index 5c551104..a68eeb86 100644
--- a/src/game/g_misc.c
+++ b/src/game/g_misc.c
@@ -89,7 +89,7 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles )
G_UnlaggedClear( player );
// set angles
- SetClientViewAngle( player, angles );
+ G_SetClientViewAngle( player, angles );
// kill anything at the destination
if( player->client->sess.sessionTeam != TEAM_SPECTATOR )
diff --git a/src/game/g_missile.c b/src/game/g_missile.c
index 9602943b..9cdca6cf 100644
--- a/src/game/g_missile.c
+++ b/src/game/g_missile.c
@@ -408,8 +408,8 @@ gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir )
bolt->r.ownerNum = self->s.number;
bolt->parent = self;
bolt->damage = PRIFLE_DMG;
- bolt->splashDamage = 0;
- bolt->splashRadius = 0;
+ bolt->splashDamage = PRIFLE_DMG;
+ bolt->splashRadius = PRIFLE_SPLASH_RADIUS;
bolt->methodOfDeath = MOD_PRIFLE;
bolt->splashMethodOfDeath = MOD_PRIFLE;
bolt->clipmask = MASK_SHOT;
@@ -434,7 +434,8 @@ fire_luciferCannon
=================
*/
-gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius )
+gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir,
+ int damage, int radius, int speed )
{
gentity_t *bolt;
int localDamage = (int)( ceil( ( (float)damage /
@@ -468,7 +469,7 @@ gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int da
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
- VectorScale( dir, LCANNON_SPEED, bolt->s.pos.trDelta );
+ VectorScale( dir, speed, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->r.currentOrigin );
@@ -589,7 +590,7 @@ void AHive_SearchAndDestroy( gentity_t *self )
//if there is no LOS or the parent hive is too far away or the target is dead, return
if( tr.entityNum == ENTITYNUM_WORLD ||
- Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 5 ) ||
+ Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > HIVE_RANGE ||
self->target_ent->health <= 0 )
{
self->r.ownerNum = ENTITYNUM_WORLD;
@@ -789,8 +790,8 @@ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )
bolt->r.ownerNum = self->s.number;
bolt->parent = self;
bolt->damage = LEVEL3_BOUNCEBALL_DMG;
- bolt->splashDamage = 0;
- bolt->splashRadius = 0;
+ bolt->splashDamage = LEVEL3_BOUNCEBALL_DMG;
+ bolt->splashRadius = LEVEL3_BOUNCEBALL_RADIUS;
bolt->methodOfDeath = MOD_LEVEL3_BOUNCEBALL;
bolt->splashMethodOfDeath = MOD_LEVEL3_BOUNCEBALL;
bolt->clipmask = MASK_SHOT;
@@ -802,7 +803,6 @@ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )
VectorScale( dir, LEVEL3_BOUNCEBALL_SPEED, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->r.currentOrigin );
- /*bolt->s.eFlags |= EF_BOUNCE;*/
return bolt;
}
diff --git a/src/game/g_ptr.c b/src/game/g_ptr.c
index 5344aa1d..1b44f091 100644
--- a/src/game/g_ptr.c
+++ b/src/game/g_ptr.c
@@ -63,7 +63,7 @@ void G_UpdatePTRConnection( gclient_t *client )
{
client->pers.connection->clientTeam = client->pers.teamSelection;
if( client->pers.teamSelection == PTE_NONE )
- client->pers.connection->clientCredit = client->pers.savedCredit;
+ client->pers.connection->clientCredit = client->pers.credit;
else
client->pers.connection->clientCredit = client->ps.persistant[ PERS_CREDIT ];
}
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index ebfa230e..5bfb1fce 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -142,13 +142,15 @@ Trace a bounding box against entities, but not the world
Also check there is a line of sight between the start and end point
================
*/
-static void G_WideTrace( trace_t *tr, gentity_t *ent, float range, float width, gentity_t **target )
+static void G_WideTrace( trace_t *tr, gentity_t *ent, float range,
+ float width, float height, gentity_t **target )
{
vec3_t mins, maxs;
vec3_t end;
VectorSet( mins, -width, -width, -width );
VectorSet( maxs, width, width, width );
+ mins[ 2 ] -= height - width;
*target = NULL;
@@ -156,15 +158,25 @@ static void G_WideTrace( trace_t *tr, gentity_t *ent, float range, float width,
return;
// Set aiming directions
- AngleVectors( ent->client->ps.viewangles, forward, right, up );
- CalcMuzzlePoint( ent, forward, right, up, muzzle );
VectorMA( muzzle, range, forward, end );
G_UnlaggedOn( muzzle, range );
// Trace against entities
trap_Trace( tr, muzzle, mins, maxs, end, ent->s.number, CONTENTS_BODY );
- if( tr->entityNum != ENTITYNUM_NONE )
+
+ // If we started in a solid that means someone is within our muzzle box,
+ // the trace didn't give us the entity number though so do a trace
+ // from the entity origin to the muzzle
+ if( tr->startsolid )
+ {
+ trap_Trace( tr, ent->client->ps.origin, mins, maxs, muzzle,
+ ent->s.number, CONTENTS_BODY );
+ if( tr->entityNum != ENTITYNUM_NONE )
+ *target = &g_entities[ tr->entityNum ];
+ }
+
+ else if( tr->entityNum != ENTITYNUM_NONE )
{
*target = &g_entities[ tr->entityNum ];
@@ -207,32 +219,91 @@ void SnapVectorTowards( vec3_t v, vec3_t to )
/*
===============
-meleeAttack
+BloodSpurt
+
+Generates a blood spurt event for traces with accurate end points
===============
*/
-void meleeAttack( gentity_t *ent, float range, float width, int damage, meansOfDeath_t mod )
+static void BloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr )
{
- trace_t tr;
gentity_t *tent;
- gentity_t *traceEnt;
- G_WideTrace( &tr, ent, range, width, &traceEnt );
+ if( !attacker->client || !victim->client )
+ return;
+ tent = G_TempEntity( tr->endpos, EV_MISSILE_HIT );
+ tent->s.otherEntityNum = victim->s.number;
+ tent->s.eventParm = DirToByte( tr->plane.normal );
+ tent->s.weapon = attacker->s.weapon;
+ tent->s.generic1 = attacker->s.generic1; // weaponMode
+ }
+
+/*
+===============
+WideBloodSpurt
- if( traceEnt == NULL )
+Calculates the position of a blood spurt for wide traces and generates an event
+===============
+*/
+static void WideBloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr )
+{
+ gentity_t *tent;
+ vec3_t normal, origin;
+ float mag, radius;
+
+ if( !attacker->client || !victim->client )
return;
- // send blood impact
- if( traceEnt->takedamage && traceEnt->client )
+ if( tr )
+ VectorSubtract( tr->endpos, victim->client->ps.origin, normal );
+ else
+ VectorSubtract( attacker->client->ps.origin,
+ victim->client->ps.origin, normal );
+
+ // Normalize the horizontal components of the vector difference to the
+ // "radius" of the bounding box
+ mag = sqrt( normal[ 0 ] * normal[ 0 ] + normal[ 1 ] * normal[ 1 ] );
+ radius = victim->r.maxs[ 0 ] * 1.21f;
+ if( mag > radius )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ normal[ 0 ] = normal[ 0 ] / mag * radius;
+ normal[ 1 ] = normal[ 1 ] / mag * radius;
}
- if( traceEnt->takedamage )
- G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, mod );
+ // Clamp origin to be within bounding box vertically
+ if( normal[ 2 ] > victim->r.maxs[ 2 ] )
+ normal[ 2 ] = victim->r.maxs[ 2 ];
+ if( normal[ 2 ] < victim->r.mins[ 2 ] )
+ normal[ 2 ] = victim->r.mins[ 2 ];
+
+ VectorAdd( victim->client->ps.origin, normal, origin );
+ VectorNegate( normal, normal );
+ VectorNormalize( normal );
+
+ // Create the blood spurt effect entity
+ tent = G_TempEntity( origin, EV_MISSILE_HIT );
+ tent->s.eventParm = DirToByte( normal );
+ tent->s.otherEntityNum = victim->s.number;
+ tent->s.weapon = attacker->s.weapon;
+ tent->s.generic1 = attacker->s.generic1; // weaponMode
+}
+
+/*
+===============
+meleeAttack
+===============
+*/
+void meleeAttack( gentity_t *ent, float range, float width, float height,
+ int damage, meansOfDeath_t mod )
+{
+ trace_t tr;
+ gentity_t *traceEnt;
+
+ G_WideTrace( &tr, ent, range, width, height, &traceEnt );
+ if( traceEnt == NULL || !traceEnt->takedamage )
+ return;
+
+ WideBloodSpurt( ent, traceEnt, &tr );
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, mod );
}
/*
@@ -326,7 +397,7 @@ void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent )
{
r = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
u = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
- VectorMA( origin, 8192 * 16, forward, end );
+ VectorMA( origin, SHOTGUN_RANGE, forward, end );
VectorMA( end, r, right, end );
VectorMA( end, u, up, end );
@@ -353,7 +424,7 @@ void shotgunFire( gentity_t *ent )
SnapVector( tent->s.origin2 );
tent->s.eventParm = rand() & 255; // seed for spread pattern
tent->s.otherEntityNum = ent->s.number;
- G_UnlaggedOn( muzzle, 8192 * 16 );
+ G_UnlaggedOn( muzzle, SHOTGUN_RANGE );
ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
G_UnlaggedOff();
}
@@ -390,11 +461,7 @@ void massDriverFire( gentity_t *ent )
// send impact
if( traceEnt->takedamage && traceEnt->client )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ BloodSpurt( ent, traceEnt, &tr );
}
else
{
@@ -546,11 +613,7 @@ void lasGunFire( gentity_t *ent )
// send impact
if( traceEnt->takedamage && traceEnt->client )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ BloodSpurt( ent, traceEnt, &tr );
}
else
{
@@ -575,49 +638,31 @@ PAIN SAW
void painSawFire( gentity_t *ent )
{
trace_t tr;
- vec3_t end;
- gentity_t *tent;
- gentity_t *traceEnt;
+ vec3_t temp;
+ gentity_t *tent, *traceEnt;
- // set aiming directions
- AngleVectors( ent->client->ps.viewangles, forward, right, up );
-
- CalcMuzzlePoint( ent, forward, right, up, muzzle );
-
- VectorMA( muzzle, PAINSAW_RANGE, forward, end );
-
- G_UnlaggedOn( muzzle, PAINSAW_RANGE );
- trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
- G_UnlaggedOff( );
-
- if( tr.surfaceFlags & SURF_NOIMPACT )
+ G_WideTrace( &tr, ent, PAINSAW_RANGE, PAINSAW_WIDTH, PAINSAW_HEIGHT,
+ &traceEnt );
+ if( !traceEnt || !traceEnt->takedamage )
return;
- traceEnt = &g_entities[ tr.entityNum ];
+ // hack to line up particle system with weapon model
+ tr.endpos[ 2 ] -= 5.0f;
// send blood impact
- if( traceEnt->takedamage )
- {
- vec3_t temp;
-
- //hack to get the particle system to line up with the weapon
- VectorCopy( tr.endpos, temp );
- temp[ 2 ] -= 10.0f;
-
if( traceEnt->client )
{
- tent = G_TempEntity( temp, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
+ BloodSpurt( ent, traceEnt, &tr );
}
else
+ {
+ VectorCopy( tr.endpos, temp );
tent = G_TempEntity( temp, EV_MISSILE_MISS );
-
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
tent->s.generic1 = ent->s.generic1; //weaponMode
}
- if( traceEnt->takedamage )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, PAINSAW_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PAINSAW );
}
@@ -638,16 +683,18 @@ void LCChargeFire( gentity_t *ent, qboolean secondary )
{
gentity_t *m;
- if( secondary )
+ if( secondary && ent->client->ps.stats[ STAT_MISC ] <= 0 )
{
m = fire_luciferCannon( ent, muzzle, forward, LCANNON_SECONDARY_DAMAGE,
- LCANNON_SECONDARY_RADIUS );
- ent->client->ps.weaponTime = LCANNON_REPEAT;
+ LCANNON_SECONDARY_RADIUS, LCANNON_SECONDARY_SPEED );
+ ent->client->ps.stats[ STAT_MISC2 ] = LCANNON_REPEAT;
}
else
{
- m = fire_luciferCannon( ent, muzzle, forward, ent->client->ps.stats[ STAT_MISC ], LCANNON_RADIUS );
- ent->client->ps.weaponTime = LCANNON_CHARGEREPEAT;
+ m = fire_luciferCannon( ent, muzzle, forward,
+ ent->client->ps.stats[ STAT_MISC ], LCANNON_RADIUS, LCANNON_SPEED );
+ ent->client->ps.stats[ STAT_MISC2 ] = LCANNON_CHARGEREPEAT -
+ ( level.time - ent->client->lcannonStartTime );
}
ent->client->ps.stats[ STAT_MISC ] = 0;
@@ -769,9 +816,10 @@ void cancelBuildFire( gentity_t *ent )
G_AddEvent( ent, EV_BUILD_REPAIR, 0 );
}
}
- else if( ent->client->ps.weapon == WP_ABUILD2 )
+ else if( ent->client->ps.weapon == WP_ABUILD ||
+ ent->client->ps.weapon == WP_ABUILD2 )
meleeAttack( ent, ABUILDER_CLAW_RANGE, ABUILDER_CLAW_WIDTH,
- ABUILDER_CLAW_DMG, MOD_ABUILDER_CLAW ); //melee attack for alien builder
+ ABUILDER_CLAW_WIDTH, ABUILDER_CLAW_DMG, MOD_ABUILDER_CLAW );
}
/*
@@ -781,7 +829,10 @@ buildFire
*/
void buildFire( gentity_t *ent, dynMenu_t menu )
{
- if( ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE )
+ buildable_t buildable = ( ent->client->ps.stats[ STAT_BUILDABLE ]
+ & ~SB_VALID_TOGGLEBIT );
+
+ if( buildable > BA_NONE )
{
if( ent->client->ps.stats[ STAT_MISC ] > 0 )
{
@@ -789,27 +840,17 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
return;
}
- if( G_BuildIfValid( ent, ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) )
+ if( G_BuildIfValid( ent, buildable ) )
{
if( g_cheats.integer )
{
ent->client->ps.stats[ STAT_MISC ] = 0;
}
- else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && !G_IsOvermindBuilt( ) )
- {
- ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2;
- }
- else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
- G_IsPowered( muzzle ) == BA_NONE &&
- ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) != BA_H_REPEATER ) //hack
+ else
{
ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2;
+ BG_FindBuildTimeForBuildable( buildable );
}
- else
- ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon );
ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
@@ -849,11 +890,18 @@ CheckVenomAttack
qboolean CheckVenomAttack( gentity_t *ent )
{
trace_t tr;
- gentity_t *tent;
gentity_t *traceEnt;
int damage = LEVEL0_BITE_DMG;
- G_WideTrace( &tr, ent, LEVEL0_BITE_RANGE, LEVEL0_BITE_WIDTH, &traceEnt );
+ if( ent->client->ps.weaponTime )
+ return qfalse;
+
+ // Calculate muzzle point
+ AngleVectors( ent->client->ps.viewangles, forward, right, up );
+ CalcMuzzlePoint( ent, forward, right, up, muzzle );
+
+ G_WideTrace( &tr, ent, LEVEL0_BITE_RANGE, LEVEL0_BITE_WIDTH,
+ LEVEL0_BITE_WIDTH, &traceEnt );
if( traceEnt == NULL )
return qfalse;
@@ -864,15 +912,14 @@ qboolean CheckVenomAttack( gentity_t *ent )
if( !traceEnt->client && !traceEnt->s.eType == ET_BUILDABLE )
return qfalse;
- //allow bites to work against defensive buildables only
+ // only allow bites to work against buildings as they are constructing
if( traceEnt->s.eType == ET_BUILDABLE )
{
- if( traceEnt->s.modelindex != BA_H_MGTURRET &&
- traceEnt->s.modelindex != BA_H_TESLAGEN )
+ if( traceEnt->spawned )
return qfalse;
- //hackery
- damage *= 0.5f;
+ if( traceEnt->biteam == BIT_ALIENS )
+ return qfalse;
}
if( traceEnt->client )
@@ -884,17 +931,10 @@ qboolean CheckVenomAttack( gentity_t *ent )
}
// send blood impact
- if( traceEnt->takedamage && traceEnt->client )
- {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
- }
+ WideBloodSpurt( ent, traceEnt, &tr );
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_LEVEL0_BITE );
-
+ ent->client->ps.weaponTime += LEVEL0_BITE_REPEAT;
return qtrue;
}
@@ -993,12 +1033,6 @@ void poisonCloud( gentity_t *ent )
if( humanPlayer->client && humanPlayer->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
{
- if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, humanPlayer->client->ps.stats ) )
- continue;
-
- if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, humanPlayer->client->ps.stats ) )
- continue;
-
trap_Trace( &tr, muzzle, NULL, NULL, humanPlayer->s.origin, humanPlayer->s.number, MASK_SHOT );
//can't see target from here
@@ -1010,6 +1044,7 @@ void poisonCloud( gentity_t *ent )
humanPlayer->client->ps.stats[ STAT_STATE ] |= SS_POISONCLOUDED;
humanPlayer->client->lastPoisonCloudedTime = level.time;
humanPlayer->client->lastPoisonCloudedClient = ent;
+
trap_SendServerCommand( humanPlayer->client->ps.clientNum, "poisoncloud" );
}
}
@@ -1026,23 +1061,48 @@ LEVEL2
======================================================================
*/
-#define MAX_ZAPS 64
-
-static zap_t zaps[ MAX_CLIENTS ];
+static vec3_t sortReference;
+static int QDECL G_SortDistance( const void *a, const void *b )
+{
+ gentity_t *aent, *bent;
+ float adist, bdist;
+
+ aent = &g_entities[ *(int *)a ];
+ bent = &g_entities[ *(int *)b ];
+ adist = Distance( sortReference, aent->s.origin );
+ bdist = Distance( sortReference, bent->s.origin );
+ if( adist > bdist )
+ return -1;
+ else if( adist < bdist )
+ return 1;
+ else
+ return 0;
+}
/*
===============
-G_FindNewZapTarget
+G_UpdateZaps
===============
*/
-static gentity_t *G_FindNewZapTarget( gentity_t *ent )
+void G_UpdateZaps( gentity_t *ent )
{
int entityList[ MAX_GENTITIES ];
- vec3_t range = { LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE };
+ int hitList[ MAX_GENTITIES ];
+ vec3_t range = { LEVEL2_AREAZAP_CUTOFF,
+ LEVEL2_AREAZAP_CUTOFF,
+ LEVEL2_AREAZAP_CUTOFF };
vec3_t mins, maxs;
- int i, j, k, num;
+ int i, j;
+ int hit = 0;
+ int num;
gentity_t *enemy;
+ gentity_t *effect;
trace_t tr;
+ qboolean alreadyTargeted = qfalse;
+ int damage;
+
+ if( !ent->zapping || ent->health <= 0 )
+ return;
VectorScale( range, 1.0f / M_ROOT3, range );
VectorAdd( ent->s.origin, range, maxs );
@@ -1054,233 +1114,85 @@ static gentity_t *G_FindNewZapTarget( gentity_t *ent )
{
enemy = &g_entities[ entityList[ i ] ];
- if( ( ( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
+ if( ( ( enemy->client &&
+ enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
( enemy->s.eType == ET_BUILDABLE &&
- BG_FindTeamForBuildable( enemy->s.modelindex ) == BIT_HUMANS ) ) && enemy->health > 0 )
+ BG_FindTeamForBuildable( enemy->s.modelindex ) == BIT_HUMANS ) ) &&
+ enemy->health > 0 )
{
- qboolean foundOldTarget = qfalse;
-
- trap_Trace( &tr, muzzle, NULL, NULL, enemy->s.origin, ent->s.number, MASK_SHOT );
-
- //can't see target from here
- if( tr.entityNum == ENTITYNUM_WORLD )
- continue;
- for( j = 0; j < MAX_ZAPS; j++ )
+ alreadyTargeted = qfalse;
+ for( j = 0; j < LEVEL2_AREAZAP_MAX_TARGETS; j++ )
{
- zap_t *zap = &zaps[ j ];
-
- for( k = 0; k < zap->numTargets; k++ )
+ if( ent->zapTargets[ j ] == entityList[ i ] )
{
- if( zap->targets[ k ] == enemy )
- {
- foundOldTarget = qtrue;
+ alreadyTargeted = qtrue;
break;
- }
}
-
- if( foundOldTarget )
- break;
}
- // enemy is already targetted
- if( foundOldTarget )
+ if( !alreadyTargeted &&
+ Distance( ent->s.origin, enemy->s.origin ) > LEVEL2_AREAZAP_RANGE )
+ {
continue;
+ }
- return enemy;
+ trap_Trace( &tr, ent->s.origin, NULL, NULL, enemy->s.origin,
+ ent-g_entities, MASK_SHOT );
+ if( tr.entityNum == enemy-g_entities )
+ hitList[ hit++ ] = tr.entityNum;
}
}
- return NULL;
-}
-
-/*
-===============
-G_UpdateZapEffect
-===============
-*/
-static void G_UpdateZapEffect( zap_t *zap )
-{
- int j;
- gentity_t *effect = zap->effectChannel;
+ for( i = 0; i < LEVEL2_AREAZAP_MAX_TARGETS; i++ )
+ ent->zapTargets[ i ] = -1;
- effect->s.eType = ET_LEV2_ZAP_CHAIN;
- effect->classname = "lev2zapchain";
- G_SetOrigin( effect, zap->creator->s.origin );
- effect->s.misc = zap->creator->s.number;
-
- effect->s.time = effect->s.time2 = effect->s.constantLight = -1;
-
- for( j = 0; j < zap->numTargets; j++ )
- {
- int number = zap->targets[ j ]->s.number;
+ if( !hit )
+ return;
- switch( j )
- {
- case 0: effect->s.time = number; break;
- case 1: effect->s.time2 = number; break;
- case 2: effect->s.constantLight = number; break;
- default: break;
- }
- }
+ ent->zapDmg += ( (float)( level.time - level.previousTime ) / 1000.0f )
+ * LEVEL2_AREAZAP_DMG;
+ damage = (int)ent->zapDmg;
+ // wait until we've accumulated enough damage for bsuit to take at
+ // least 1 HP
+ if( damage < 5 )
+ damage = 0;
+ else
+ ent->zapDmg -= (int)damage;
- trap_LinkEntity( effect );
-}
-/*
-===============
-G_CreateNewZap
-===============
-*/
-static void G_CreateNewZap( gentity_t *creator, gentity_t *target )
-{
- int i, j;
- zap_t *zap;
+ VectorCopy( ent->s.origin, sortReference );
+ qsort( hitList, hit, sizeof( int ), G_SortDistance );
- for( i = 0; i < MAX_ZAPS; i++ )
+ effect = G_TempEntity( ent->s.origin, EV_LEV2_ZAP );
+ effect->s.misc = ent-g_entities;
+ effect->s.time = effect->s.time2 = effect->s.constantLight = -1;
+ for( i = 0; i < hit; i++ )
{
- zap = &zaps[ i ];
-
- if( !zap->used )
- {
- zap->used = qtrue;
-
- zap->timeToLive = LEVEL2_AREAZAP_TIME;
- zap->damageUsed = 0;
-
- zap->creator = creator;
-
- zap->targets[ 0 ] = target;
- zap->numTargets = 1;
-
- for( j = 1; j < MAX_ZAP_TARGETS && zap->targets[ j - 1 ]; j++ )
- {
- zap->targets[ j ] = G_FindNewZapTarget( zap->targets[ j - 1 ] );
-
- if( zap->targets[ j ] )
- zap->numTargets++;
- }
-
- zap->effectChannel = G_Spawn( );
- G_UpdateZapEffect( zap );
-
- return;
- }
- }
-}
+ if( i >= LEVEL2_AREAZAP_MAX_TARGETS )
+ break;
+ ent->zapTargets[ i ] = hitList[ i ];
-/*
-===============
-G_UpdateZaps
-===============
-*/
-void G_UpdateZaps( int msec )
-{
- int i, j;
- zap_t *zap;
- int damage;
+ enemy = &g_entities[ hitList[ i ] ];
- for( i = 0; i < MAX_ZAPS; i++ )
- {
- zap = &zaps[ i ];
- if( zap->used )
+ if( damage > 0 )
{
- //check each target is valid
- for( j = 0; j < zap->numTargets; j++ )
- {
- gentity_t *source;
- gentity_t *target = zap->targets[ j ];
-
- if( j == 0 )
- source = zap->creator;
- else
- source = zap->targets[ j - 1 ];
-
- if( target->health <= 0 || !target->inuse || //early out
- Distance( source->s.origin, target->s.origin ) > LEVEL2_AREAZAP_RANGE )
- {
- target = zap->targets[ j ] = G_FindNewZapTarget( source );
-
- //couldn't find a target, so forget about the rest of the chain
- if( !target )
- zap->numTargets = j;
- }
- }
-
- if( zap->numTargets )
- {
- for( j = 0; j < zap->numTargets; j++ )
- {
- gentity_t *source;
- gentity_t *target = zap->targets[ j ];
- float r = 1.0f / zap->numTargets;
- float damageFraction = 2 * r - 2 * j * r * r - r * r;
- vec3_t forward;
-
- if( j == 0 )
- source = zap->creator;
- else
- source = zap->targets[ j - 1 ];
-
- damage = ceil( ( (float)msec / LEVEL2_AREAZAP_TIME ) *
- LEVEL2_AREAZAP_DMG * damageFraction );
-
- // don't let a high msec value inflate the total damage
- if( damage + zap->damageUsed > LEVEL2_AREAZAP_DMG )
- damage = LEVEL2_AREAZAP_DMG - zap->damageUsed;
-
- VectorSubtract( target->s.origin, source->s.origin, forward );
- VectorNormalize( forward );
-
- //do the damage
- if( damage )
- {
- G_Damage( target, source, zap->creator, forward, target->s.origin,
+ G_Damage( enemy, ent, ent, NULL, enemy->s.origin,
damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP );
- zap->damageUsed += damage;
- }
- }
- }
-
- G_UpdateZapEffect( zap );
-
- zap->timeToLive -= msec;
-
- if( zap->timeToLive <= 0 || zap->numTargets == 0 || zap->creator->health <= 0 )
- {
- zap->used = qfalse;
- G_FreeEntity( zap->effectChannel );
- }
}
- }
-}
-
-/*
-===============
-areaZapFire
-===============
-*/
-void areaZapFire( gentity_t *ent )
-{
- trace_t tr;
- gentity_t *traceEnt;
-
- G_WideTrace( &tr, ent, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_WIDTH, &traceEnt );
-
- if( traceEnt == NULL )
- return;
- if( ( ( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
- ( traceEnt->s.eType == ET_BUILDABLE &&
- BG_FindTeamForBuildable( traceEnt->s.modelindex ) == BIT_HUMANS ) ) && traceEnt->health > 0 )
- {
- G_CreateNewZap( ent, traceEnt );
+ switch( i )
+ {
+ case 0: effect->s.time = hitList[ i ]; break;
+ case 1: effect->s.time2 = hitList[ i ]; break;
+ case 2: effect->s.constantLight = hitList[ i ]; break;
+ default: break;
+ }
}
}
-
/*
======================================================================
@@ -1297,50 +1209,47 @@ CheckPounceAttack
qboolean CheckPounceAttack( gentity_t *ent )
{
trace_t tr;
- gentity_t *tent;
gentity_t *traceEnt;
int damage;
+ int payload;
- if( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {
- ent->client->allowedToPounce = qfalse;
+ if( ent->client->pmext.pouncePayload <= 0 )
+ return qfalse;
+
+ // in case the goon lands on his target, he get's one shot after landing
+ payload = ent->client->pmext.pouncePayload;
+ if( !( ent->client->ps.pm_flags & PMF_CHARGE ) )
ent->client->pmext.pouncePayload = 0;
- }
- if( !ent->client->allowedToPounce )
+ if( ent->client->ps.weaponTime > 0 )
return qfalse;
- if( ent->client->ps.weaponTime )
- return qfalse;
+ // Calculate muzzle point
+ AngleVectors( ent->client->ps.viewangles, forward, right, up );
+ CalcMuzzlePoint( ent, forward, right, up, muzzle );
- G_WideTrace( &tr, ent, LEVEL3_POUNCE_RANGE, LEVEL3_POUNCE_WIDTH, &traceEnt );
+ G_WideTrace( &tr, ent, LEVEL3_POUNCE_RANGE, LEVEL3_POUNCE_WIDTH,
+ LEVEL3_POUNCE_WIDTH, &traceEnt );
if( traceEnt == NULL )
return qfalse;
// send blood impact
- if( traceEnt->takedamage && traceEnt->client )
- {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
- }
+ if( traceEnt->takedamage )
+ WideBloodSpurt( ent, traceEnt, &tr );
if( !traceEnt->takedamage )
return qfalse;
- damage = (int)( ( (float)ent->client->pmext.pouncePayload
- / (float)LEVEL3_POUNCE_SPEED ) * LEVEL3_POUNCE_DMG );
+ damage = (int)( ( (float)payload / (float)LEVEL3_POUNCE_SPEED )
+ * LEVEL3_POUNCE_DMG );
ent->client->pmext.pouncePayload = 0;
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage,
- DAMAGE_NO_KNOCKBACK|DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE );
+ DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE );
ent->client->ps.weaponTime += LEVEL3_POUNCE_TIME;
- ent->client->allowedToPounce = qfalse;
return qtrue;
}
@@ -1365,39 +1274,90 @@ LEVEL4
/*
===============
-ChargeAttack
+G_ChargeAttack
===============
*/
-void ChargeAttack( gentity_t *ent, gentity_t *victim )
+void G_ChargeAttack( gentity_t *ent, gentity_t *victim )
{
- gentity_t *tent;
int damage;
vec3_t forward, normal;
- if( level.time < victim->chargeRepeat )
+ if( ent->client->ps.stats[ STAT_MISC ] <= 0 || !ent->client->charging )
return;
- victim->chargeRepeat = level.time + LEVEL4_CHARGE_REPEAT;
-
VectorSubtract( victim->s.origin, ent->s.origin, forward );
VectorNormalize( forward );
VectorNegate( forward, normal );
- if( victim->client )
+ if( !victim->takedamage )
+ return;
+
+ WideBloodSpurt( ent, victim, NULL );
+
+ damage = (int)( ( (float)ent->client->ps.stats[ STAT_MISC ] /
+ (float)LEVEL4_TRAMPLE_CHARGE_MAX ) * LEVEL4_TRAMPLE_DMG );
+
+ G_Damage( victim, ent, ent, forward, victim->s.origin, damage,
+ 0, MOD_LEVEL4_TRAMPLE );
+
+ if( !victim->client )
+ ent->client->ps.stats[ STAT_MISC ] = 0;
+}
+
+/*
+===============
+G_CrushAttack
+
+Should only be called if there was an impact between a tyrant and another player
+===============
+*/
+void G_CrushAttack( gentity_t *ent, gentity_t *victim, float sec )
+{
+ vec3_t dir;
+ float jump;
+ int damage;
+
+ if( !victim->takedamage ||
+ ent->client->ps.origin[ 2 ] + ent->r.mins[ 2 ] <
+ victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] ||
+ ( victim->client &&
+ victim->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
+ return;
+
+ // Force target to crouch first if they can
+ if( victim->client &&
+ !BG_InventoryContainsUpgrade( UP_BATTLESUIT, victim->client->ps.stats ) &&
+ victim->client->ps.pm_type != PM_JETPACK &&
+ victim->client->pers.cmd.upmove >= 0 &&
+ !( victim->client->ps.pm_flags &
+ ( PMF_CROUCH_HELD | PMF_FORCE_CROUCH ) ) )
{
- tent = G_TempEntity( victim->s.origin, EV_MISSILE_HIT );
- tent->s.otherEntityNum = victim->s.number;
- tent->s.eventParm = DirToByte( normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ victim->client->forceCrouchTime = level.time;
+ return;
}
- if( !victim->takedamage )
- return;
+ // Deal velocity based damage to target
+ jump = BG_FindJumpMagnitudeForClass( ent->client->ps.stats[ STAT_PCLASS ] );
+ damage = ( ent->client->pmext.fallVelocity + jump ) *
+ -LEVEL4_CRUSH_DAMAGE_PER_V;
- damage = (int)( ( (float)ent->client->ps.stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME ) * LEVEL4_CHARGE_DMG );
+ if( damage < 0 )
+ damage = 0;
- G_Damage( victim, ent, ent, forward, victim->s.origin, damage, 0, MOD_LEVEL4_CHARGE );
+ if( victim->client &&
+ ent->client->lastCrushTime + LEVEL4_CRUSH_REPEAT < level.time )
+ {
+ ent->client->lastCrushTime = level.time;
+ damage += LEVEL4_CRUSH_DAMAGE;
+ }
+
+ if( damage < 1 )
+ return;
+
+ // Crush the victim over a period of time
+ VectorSubtract( victim->s.origin, ent->client->ps.origin, dir );
+ G_Damage( victim, ent, ent, dir, victim->s.origin, damage,
+ DAMAGE_NO_LOCDAMAGE, MOD_LEVEL4_CRUSH );
}
//======================================================================
@@ -1413,7 +1373,7 @@ void CalcMuzzlePoint( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, v
{
vec3_t normal;
- VectorCopy( ent->s.pos.trBase, muzzlePoint );
+ VectorCopy( ent->client->ps.origin, muzzlePoint );
BG_GetClientNormal( &ent->client->ps, normal );
VectorMA( muzzlePoint, ent->client->ps.viewheight, normal, muzzlePoint );
VectorMA( muzzlePoint, 1, forward, muzzlePoint );
@@ -1481,9 +1441,6 @@ void FireWeapon2( gentity_t *ent )
case WP_ALEVEL1_UPG:
poisonCloud( ent );
break;
- case WP_ALEVEL2_UPG:
- areaZapFire( ent );
- break;
case WP_LUCIFER_CANNON:
LCChargeFire( ent, qtrue );
@@ -1524,20 +1481,28 @@ void FireWeapon( gentity_t *ent )
{
case WP_ALEVEL1:
case WP_ALEVEL1_UPG:
- meleeAttack( ent, LEVEL1_CLAW_RANGE, LEVEL1_CLAW_WIDTH, LEVEL1_CLAW_DMG, MOD_LEVEL1_CLAW );
+ meleeAttack( ent, LEVEL1_CLAW_RANGE, LEVEL1_CLAW_WIDTH, LEVEL1_CLAW_WIDTH,
+ LEVEL1_CLAW_DMG, MOD_LEVEL1_CLAW );
break;
case WP_ALEVEL3:
+ meleeAttack( ent, LEVEL3_CLAW_RANGE, LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_WIDTH,
+ LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
+ break;
case WP_ALEVEL3_UPG:
- meleeAttack( ent, LEVEL3_CLAW_RANGE, LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
+ meleeAttack( ent, LEVEL3_CLAW_UPG_RANGE, LEVEL3_CLAW_WIDTH,
+ LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
break;
case WP_ALEVEL2:
- meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
+ meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_WIDTH,
+ LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
break;
case WP_ALEVEL2_UPG:
- meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
+ meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_WIDTH,
+ LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
break;
case WP_ALEVEL4:
- meleeAttack( ent, LEVEL4_CLAW_RANGE, LEVEL4_CLAW_WIDTH, LEVEL4_CLAW_DMG, MOD_LEVEL4_CLAW );
+ meleeAttack( ent, LEVEL4_CLAW_RANGE, LEVEL4_CLAW_WIDTH,
+ LEVEL4_CLAW_HEIGHT, LEVEL4_CLAW_DMG, MOD_LEVEL4_CLAW );
break;
case WP_BLASTER:
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index f7ccc38d..47dbb438 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -56,38 +56,41 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LEVEL0_BITE_K_SCALE 1.0f
#define LEVEL1_CLAW_DMG ADM(32)
-#define LEVEL1_CLAW_RANGE 96.0f
+#define LEVEL1_CLAW_RANGE 64.0f
#define LEVEL1_CLAW_WIDTH 10.0f
#define LEVEL1_CLAW_REPEAT 600
#define LEVEL1_CLAW_U_REPEAT 500
#define LEVEL1_CLAW_K_SCALE 1.0f
#define LEVEL1_CLAW_U_K_SCALE 1.0f
-#define LEVEL1_GRAB_RANGE 64.0f
-#define LEVEL1_GRAB_TIME 300
-#define LEVEL1_GRAB_U_TIME 450
+#define LEVEL1_GRAB_RANGE 96.0f
+#define LEVEL1_GRAB_TIME 400
+#define LEVEL1_GRAB_U_TIME 600
#define LEVEL1_PCLOUD_DMG ADM(4)
-#define LEVEL1_PCLOUD_RANGE 200.0f
-#define LEVEL1_PCLOUD_REPEAT 2000
+#define LEVEL1_PCLOUD_RANGE 150.0f
+#define LEVEL1_PCLOUD_REPEAT 2500
#define LEVEL1_PCLOUD_TIME 10000
+#define LEVEL1_REGEN_MOD 2.0f
+#define LEVEL1_UPG_REGEN_MOD 3.0f
#define LEVEL2_CLAW_DMG ADM(40)
#define LEVEL2_CLAW_RANGE 96.0f
-#define LEVEL2_CLAW_WIDTH 12.0f
+#define LEVEL2_CLAW_WIDTH 14.0f
#define LEVEL2_CLAW_REPEAT 500
#define LEVEL2_CLAW_K_SCALE 1.0f
#define LEVEL2_CLAW_U_REPEAT 400
#define LEVEL2_CLAW_U_K_SCALE 1.0f
-#define LEVEL2_AREAZAP_DMG ADM(80)
-#define LEVEL2_AREAZAP_RANGE 200.0f
-#define LEVEL2_AREAZAP_WIDTH 15.0f
-#define LEVEL2_AREAZAP_REPEAT 1500
-#define LEVEL2_AREAZAP_TIME 1000
+#define LEVEL2_AREAZAP_DMG ADM(50)
+#define LEVEL2_AREAZAP_RANGE 90.0f
+#define LEVEL2_AREAZAP_CUTOFF 350.0f
+#define LEVEL2_AREAZAP_REPEAT 500
#define LEVEL2_AREAZAP_MAX_TARGETS 3
#define LEVEL2_WALLJUMP_MAXSPEED 1000.0f
#define LEVEL3_CLAW_DMG ADM(80)
-#define LEVEL3_CLAW_RANGE 96.0f
-#define LEVEL3_CLAW_WIDTH 16.0f
+#define LEVEL3_CLAW_UPG_RANGE 96.0f
+#define LEVEL3_CLAW_RANGE 72.0f
+//#define LEVEL3_CLAW_WIDTH 16.0f
+#define LEVEL3_CLAW_WIDTH 12.0f
#define LEVEL3_CLAW_REPEAT 700
#define LEVEL3_CLAW_K_SCALE 1.0f
#define LEVEL3_CLAW_U_REPEAT 600
@@ -99,27 +102,41 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LEVEL3_POUNCE_UPG_SPEED 800
#define LEVEL3_POUNCE_SPEED_MOD 0.75f
#define LEVEL3_POUNCE_CHARGE_TIME 700
+#define LEVEL3_POUNCE_CHARGE_MIN 400
#define LEVEL3_POUNCE_TIME 400
#define LEVEL3_BOUNCEBALL_DMG ADM(110)
#define LEVEL3_BOUNCEBALL_REPEAT 1000
#define LEVEL3_BOUNCEBALL_SPEED 1000.0f
+#define LEVEL3_BOUNCEBALL_RADIUS 30
#define LEVEL4_CLAW_DMG ADM(100)
-#define LEVEL4_CLAW_RANGE 128.0f
-#define LEVEL4_CLAW_WIDTH 20.0f
+#define LEVEL4_CLAW_RANGE 116.0f
+#define LEVEL4_CLAW_WIDTH 14.0f
+#define LEVEL4_CLAW_HEIGHT 20.0f
#define LEVEL4_CLAW_REPEAT 750
#define LEVEL4_CLAW_K_SCALE 1.0f
#define LEVEL4_REGEN_RANGE 200.0f
-#define LEVEL4_REGEN_MOD 2.0f
-#define LEVEL4_CHARGE_SPEED 2.0f
-#define LEVEL4_CHARGE_TIME 3000
-#define LEVEL4_CHARGE_CHARGE_TIME 1500
-#define LEVEL4_MIN_CHARGE_TIME 750
-#define LEVEL4_CHARGE_CHARGE_RATIO (LEVEL4_CHARGE_TIME/LEVEL4_CHARGE_CHARGE_TIME)
-#define LEVEL4_CHARGE_REPEAT 1000
-#define LEVEL4_CHARGE_DMG ADM(110)
-
+#define LEVEL4_TRAMPLE_SPEED 2.0f
+#define LEVEL4_TRAMPLE_TRIGGER_TIME 3000
+#define LEVEL4_TRAMPLE_CHARGE_MIN_TIME 375
+#define LEVEL4_TRAMPLE_CHARGE_MAX_TIME 1000
+#define LEVEL4_TRAMPLE_DURATION 3000
+#define LEVEL4_TRAMPLE_DMG ADM(110)
+
+#define LEVEL4_TRAMPLE_CHARGE_RATE 2.0f
+#define LEVEL4_TRAMPLE_CHARGE_TRIGGER ( LEVEL4_TRAMPLE_TRIGGER_TIME * \
+ LEVEL4_TRAMPLE_CHARGE_RATE )
+#define LEVEL4_TRAMPLE_CHARGE_MIN ( LEVEL4_TRAMPLE_CHARGE_MIN_TIME * \
+ LEVEL4_TRAMPLE_CHARGE_RATE )
+#define LEVEL4_TRAMPLE_CHARGE_MAX ( LEVEL4_TRAMPLE_CHARGE_MAX_TIME * \
+ LEVEL4_TRAMPLE_CHARGE_RATE )
+#define LEVEL4_TRAMPLE_DISCHARGE_RATE ( (float)LEVEL4_TRAMPLE_CHARGE_MAX / \
+ (float)LEVEL4_TRAMPLE_DURATION )
+
+#define LEVEL4_CRUSH_DAMAGE_PER_V 0.5f
+#define LEVEL4_CRUSH_DAMAGE 120 // to players only
+#define LEVEL4_CRUSH_REPEAT 500 // player damage repeat
/*
* ALIEN classes
@@ -137,13 +154,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ALIEN_VALUE_MODIFIER 1.0f
#define AVM(h) ((int)((float)h*ALIEN_VALUE_MODIFIER))
-#define ABUILDER_SPEED 0.8f
+#define ABUILDER_SPEED 0.65f
#define ABUILDER_VALUE AVM(200)
#define ABUILDER_HEALTH AHM(50)
#define ABUILDER_REGEN 2
#define ABUILDER_COST 0
-#define ABUILDER_UPG_SPEED 1.0f
+#define ABUILDER_UPG_SPEED 0.65f
#define ABUILDER_UPG_VALUE AVM(250)
#define ABUILDER_UPG_HEALTH AHM(75)
#define ABUILDER_UPG_REGEN 3
@@ -193,8 +210,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LEVEL4_SPEED 1.2f
#define LEVEL4_VALUE AVM(800)
-#define LEVEL4_HEALTH AHM(400)
-#define LEVEL4_REGEN 7
+#define LEVEL4_HEALTH AHM(350)
+#define LEVEL4_REGEN 9
#define LEVEL4_COST 2
@@ -222,6 +239,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CREEP_ARMOUR_MODIFIER 0.75f
#define CREEP_SCALEDOWN_TIME 3000
+#define PCLOUD_MODIFIER 0.5f
+#define PCLOUD_ARMOUR_MODIFIER 0.75f
+
#define ASPAWN_BP 10
#define ASPAWN_BT 15000
#define ASPAWN_HEALTH ABHM(250)
@@ -231,13 +251,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ASPAWN_CREEPSIZE 120
#define ASPAWN_VALUE 150
-#define BARRICADE_BP 10
+#define BARRICADE_BP 8
#define BARRICADE_BT 20000
-#define BARRICADE_HEALTH ABHM(200)
+#define BARRICADE_HEALTH ABHM(300)
#define BARRICADE_REGEN 14
#define BARRICADE_SPLASHDAMAGE 50
#define BARRICADE_SPLASHRADIUS 50
#define BARRICADE_CREEPSIZE 120
+#define BARRICADE_SHRINKPROP 0.25f
+#define BARRICADE_SHRINKTIMEOUT 500
#define BOOSTER_BP 12
#define BOOSTER_BT 15000
@@ -246,9 +268,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define BOOSTER_SPLASHDAMAGE 50
#define BOOSTER_SPLASHRADIUS 50
#define BOOSTER_CREEPSIZE 120
-#define BOOSTER_INTERVAL 30000 //time in msec between uses (per player)
-#define BOOSTER_REGEN_MOD 2.0f
-#define BOOST_TIME 30000
+#define BOOSTER_REGEN_MOD 3.0f
+#define BOOST_TIME 20000
#define ACIDTUBE_BP 8
#define ACIDTUBE_BT 15000
@@ -268,7 +289,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define HIVE_SPLASHDAMAGE 30
#define HIVE_SPLASHRADIUS 200
#define HIVE_CREEPSIZE 120
-#define HIVE_RANGE 400.0f
+#define HIVE_SENSE_RANGE 500.0f
+#define HIVE_RANGE 1500.0f
#define HIVE_REPEAT 5000
#define HIVE_K_SCALE 1.0f
#define HIVE_DMG 50
@@ -321,12 +343,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ALIENSENSE_RANGE 1000.0f
#define ALIEN_POISON_TIME 10000
-#define ALIEN_POISON_DMG 30
+#define ALIEN_POISON_DMG 5
#define ALIEN_POISON_DIVIDER (1.0f/1.32f) //about 1.0/(time`th root of damage)
#define ALIEN_SPAWN_REPEAT_TIME 10000
#define ALIEN_REGEN_DAMAGE_TIME 2000 //msec since damage that regen starts again
+#define ALIEN_REGEN_NOCREEP_TIME 3000 //msec between regen off creep
/*
* HUMAN weapons
@@ -346,7 +369,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define BLASTER_K_SCALE 1.0f
#define BLASTER_SPREAD 200
#define BLASTER_SPEED 1400
-#define BLASTER_DMG HDM(9)
+#define BLASTER_DMG HDM(10)
#define RIFLE_CLIPSIZE 30
#define RIFLE_MAXCLIPS 6
@@ -360,8 +383,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define PAINSAW_PRICE 100
#define PAINSAW_REPEAT 75
#define PAINSAW_K_SCALE 1.0f
-#define PAINSAW_DAMAGE HDM(15)
-#define PAINSAW_RANGE 40.0f
+#define PAINSAW_DAMAGE HDM(11)
+#define PAINSAW_RANGE 64.0f
+#define PAINSAW_WIDTH 0.f
+#define PAINSAW_HEIGHT 8.f
#define GRENADE_PRICE 200
#define GRENADE_REPEAT 0
@@ -372,13 +397,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define SHOTGUN_PRICE 150
#define SHOTGUN_SHELLS 8
-#define SHOTGUN_PELLETS 8 //used to sync server and client side
+#define SHOTGUN_PELLETS 14 //used to sync server and client side
#define SHOTGUN_MAXCLIPS 3
#define SHOTGUN_REPEAT 1000
#define SHOTGUN_K_SCALE 1.0f
#define SHOTGUN_RELOAD 2000
#define SHOTGUN_SPREAD 900
-#define SHOTGUN_DMG HDM(7)
+#define SHOTGUN_DMG HDM(4)
+#define SHOTGUN_RANGE (8192 * 12)
#define LASGUN_PRICE 250
#define LASGUN_AMMO 200
@@ -400,7 +426,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define CHAINGUN_REPEAT 80
#define CHAINGUN_K_SCALE 1.0f
#define CHAINGUN_SPREAD 1000
-#define CHAINGUN_DMG HDM(6)
+#define CHAINGUN_DMG HDM(5)
#define PRIFLE_PRICE 400
#define PRIFLE_CLIPS 50
@@ -409,6 +435,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define PRIFLE_K_SCALE 1.0f
#define PRIFLE_RELOAD 2000
#define PRIFLE_DMG HDM(9)
+#define PRIFLE_SPLASH_RADIUS 16
#define PRIFLE_SPEED 1000
#define FLAMER_PRICE 450
@@ -418,23 +445,25 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define FLAMER_DMG HDM(20)
#define FLAMER_RADIUS 50
#define FLAMER_LIFETIME 800.0f
-#define FLAMER_SPEED 200.0f
+#define FLAMER_SPEED 300.0f
#define FLAMER_LAG 0.65f //the amount of player velocity that is added to the fireball
#define LCANNON_PRICE 600
-#define LCANNON_AMMO 90
-#define LCANNON_REPEAT 500
+#define LCANNON_AMMO 80
+#define LCANNON_REPEAT 1000
#define LCANNON_K_SCALE 1.0f
-#define LCANNON_CHARGEREPEAT 1000
-#define LCANNON_RELOAD 2000
+#define LCANNON_CHARGEREPEAT 500
+#define LCANNON_RELOAD 0
#define LCANNON_DAMAGE HDM(265)
#define LCANNON_RADIUS 150
-#define LCANNON_SECONDARY_DAMAGE HDM(27)
+#define LCANNON_SECONDARY_DAMAGE HDM(30)
#define LCANNON_SECONDARY_RADIUS 75
-#define LCANNON_SPEED 350
-#define LCANNON_CHARGE_TIME 2000
+#define LCANNON_SECONDARY_SPEED 1400
+#define LCANNON_SECONDARY_RELOAD 2000
+#define LCANNON_SPEED 700
+#define LCANNON_CHARGE_TIME 3000
#define LCANNON_TOTAL_CHARGE 255
-#define LCANNON_MIN_CHARGE 50
+#define LCANNON_MIN_CHARGE 1
#define HBUILD_PRICE 0
#define HBUILD_REPEAT 1000
@@ -452,9 +481,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define LIGHTARMOUR_PRICE 70
+#define LIGHTARMOUR_POISON_PROTECTION 1
+#define LIGHTARMOUR_PCLOUD_PROTECTION 1000
#define HELMET_PRICE 90
#define HELMET_RANGE 1000.0f
+#define HELMET_POISON_PROTECTION 1
+#define HELMET_PCLOUD_PROTECTION 1000
#define MEDKIT_PRICE 0
@@ -468,6 +501,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define JETPACK_DISABLE_CHANCE 0.3f
#define BSUIT_PRICE 400
+#define BSUIT_POISON_PROTECTION 3
+#define BSUIT_PCLOUD_PROTECTION 3000
#define MGCLIP_PRICE 0
@@ -475,7 +510,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define GAS_PRICE 0
-#define MEDKIT_POISON_IMMUNITY_TIME 30000
+#define MEDKIT_POISON_IMMUNITY_TIME 0
#define MEDKIT_STARTUP_TIME 4000
#define MEDKIT_STARTUP_SPEED 5
@@ -519,18 +554,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MGTURRET_HEALTH HBHM(190)
#define MGTURRET_SPLASHDAMAGE 100
#define MGTURRET_SPLASHRADIUS 100
-#define MGTURRET_ANGULARSPEED 8 //degrees/think ~= 200deg/sec
-#define MGTURRET_ACCURACYTOLERANCE MGTURRET_ANGULARSPEED / 1.5f //angular difference for turret to fire
+#define MGTURRET_ANGULARSPEED 12
+#define MGTURRET_ANGULARSPEED_LOCKED 8
+#define MGTURRET_ACCURACYTOLERANCE 0
#define MGTURRET_VERTICALCAP 30 // +/- maximum pitch
#define MGTURRET_REPEAT 100
#define MGTURRET_K_SCALE 1.0f
-#define MGTURRET_RANGE 300.0f
+#define MGTURRET_RANGE 400.0f
#define MGTURRET_SPREAD 200
-#define MGTURRET_DMG HDM(4)
-#define MGTURRET_DCC_ANGULARSPEED 10
-#define MGTURRET_DCC_ACCURACYTOLERANCE MGTURRET_DCC_ANGULARSPEED / 1.5f
-#define MGTURRET_GRAB_ANGULARSPEED 3
-#define MGTURRET_GRAB_ACCURACYTOLERANCE MGTURRET_GRAB_ANGULARSPEED / 1.5f
+#define MGTURRET_DMG HDM(8)
+#define MGTURRET_SPINUP_TIME 750 // time between target sighted and fire
#define TESLAGEN_BP 10
#define TESLAGEN_BT 15000
@@ -539,18 +572,21 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define TESLAGEN_SPLASHRADIUS 100
#define TESLAGEN_REPEAT 250
#define TESLAGEN_K_SCALE 4.0f
-#define TESLAGEN_RANGE 250
-#define TESLAGEN_DMG HDM(9)
+#define TESLAGEN_RANGE 150
+#define TESLAGEN_DMG HDM(10)
#define DC_BP 8
#define DC_BT 10000
#define DC_HEALTH HBHM(190)
#define DC_SPLASHDAMAGE 50
#define DC_SPLASHRADIUS 100
+#define DC_ATTACK_PERIOD 10000 // how often to spam "under attack"
+#define DC_HEALRATE 3
+#define DC_RANGE 10000
#define ARMOURY_BP 10
#define ARMOURY_BT 10000
-#define ARMOURY_HEALTH HBHM(280)
+#define ARMOURY_HEALTH HBHM(420)
#define ARMOURY_SPLASHDAMAGE 50
#define ARMOURY_SPLASHRADIUS 100
@@ -562,6 +598,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define REACTOR_ATTACK_RANGE 100.0f
#define REACTOR_ATTACK_REPEAT 1000
#define REACTOR_ATTACK_DAMAGE 40
+#define REACTOR_ATTACK_DCC_REPEAT 1000
+#define REACTOR_ATTACK_DCC_RANGE 150.0f
+#define REACTOR_ATTACK_DCC_DAMAGE 40
#define REACTOR_VALUE 2
#define REPEATER_BP 0
@@ -579,13 +618,21 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define HUMAN_JOG_MODIFIER 1.0f
#define HUMAN_BACK_MODIFIER 0.8f
#define HUMAN_SIDE_MODIFIER 0.9f
+#define HUMAN_DODGE_SIDE_MODIFIER 2.9f
+#define HUMAN_DODGE_UP_MODIFIER 0.5f
+#define HUMAN_DODGE_TIMEOUT 500
+#define HUMAN_LAND_FRICTION 3.f
#define STAMINA_STOP_RESTORE 25
#define STAMINA_WALK_RESTORE 15
+#define STAMINA_MEDISTAT_RESTORE 30 // stacked on STOP or WALK
#define STAMINA_SPRINT_TAKE 8
-#define STAMINA_LARMOUR_TAKE 4
+#define STAMINA_JUMP_TAKE 250
+#define STAMINA_DODGE_TAKE 250
+#define STAMINA_BREATHING_LEVEL 0
#define HUMAN_SPAWN_REPEAT_TIME 10000
+#define HUMAN_REGEN_DAMAGE_TIME 2000 //msec since damage before dcc repairs
/*
* Misc
diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h
index 65be0891..319d8df9 100644
--- a/src/qcommon/q_shared.h
+++ b/src/qcommon/q_shared.h
@@ -1109,7 +1109,7 @@ typedef struct playerState_s {
//
#define BUTTON_ATTACK 1
#define BUTTON_TALK 2 // displays talk balloon and disables actions
-#define BUTTON_USE_HOLDABLE 4
+#define BUTTON_USE_HOLDABLE 4 // activate upgrade
#define BUTTON_GESTURE 8
#define BUTTON_WALKING 16 // walking can't just be infered from MOVE_RUN
// because a key pressed late in the frame will
@@ -1117,12 +1117,8 @@ typedef struct playerState_s {
// walking will use different animations and
// won't generate footsteps
#define BUTTON_ATTACK2 32
-#define BUTTON_NEGATIVE 64
-
-#define BUTTON_GETFLAG 128
-#define BUTTON_GUARDBASE 256
-#define BUTTON_PATROL 512
-#define BUTTON_FOLLOWME 1024
+#define BUTTON_DODGE 64 // start a dodge or sprint motion
+#define BUTTON_USE_EVOLVE 128 // use target or open evolve menu
#define BUTTON_ANY 2048 // any key whatsoever
diff --git a/src/server/server.h b/src/server/server.h
index 4d73038e..331937e4 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -228,6 +228,8 @@ extern cvar_t *sv_zombietime;
extern cvar_t *sv_rconPassword;
extern cvar_t *sv_privatePassword;
extern cvar_t *sv_allowDownload;
+extern cvar_t *sv_wwwDownload;
+extern cvar_t *sv_wwwBaseURL;
extern cvar_t *sv_maxclients;
extern cvar_t *sv_privateClients;
diff --git a/src/server/sv_client.c b/src/server/sv_client.c
index 323ef602..40a3a9f8 100644
--- a/src/server/sv_client.c
+++ b/src/server/sv_client.c
@@ -994,6 +994,10 @@ static void SV_VerifyPaks_f( client_t *cl ) {
cl->nextSnapshotTime = -1;
cl->state = CS_ACTIVE;
SV_SendClientSnapshot( cl );
+ SV_SendServerCommand( cl, "disconnect \"Unpure Client. "
+ "You may need to enable in-game downloads "
+ "to connect to this server (set "
+ "cl_allowDownload 1)\"" );
SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" );
}
}
diff --git a/src/server/sv_init.c b/src/server/sv_init.c
index 4a7e292a..53a3d05c 100644
--- a/src/server/sv_init.c
+++ b/src/server/sv_init.c
@@ -620,6 +620,10 @@ void SV_Init (void) {
sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP );
sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO);
+ sv_wwwDownload = Cvar_Get ("sv_wwwDownload", "1",
+ CVAR_SYSTEMINFO|CVAR_ARCHIVE);
+ sv_wwwBaseURL = Cvar_Get ("sv_wwwBaseURL", "",
+ CVAR_SYSTEMINFO|CVAR_ARCHIVE);
Cvar_Get ("sv_dlURL", "", CVAR_SERVERINFO | CVAR_ARCHIVE);
sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 );
sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE );
diff --git a/src/server/sv_main.c b/src/server/sv_main.c
index e67d9b47..2940d95c 100644
--- a/src/server/sv_main.c
+++ b/src/server/sv_main.c
@@ -33,6 +33,8 @@ cvar_t *sv_zombietime; // seconds to sink messages after disconnect
cvar_t *sv_rconPassword; // password for remote server commands
cvar_t *sv_privatePassword; // password for the privateClient slots
cvar_t *sv_allowDownload;
+cvar_t *sv_wwwDownload;
+cvar_t *sv_wwwBaseURL;
cvar_t *sv_maxclients;
cvar_t *sv_privateClients; // number of clients reserved for password
diff --git a/src/tools/lcc/lburg/gram.c b/src/tools/lcc/lburg/gram.c
index a1cc890b..35f9c14c 100644
--- a/src/tools/lcc/lburg/gram.c
+++ b/src/tools/lcc/lburg/gram.c
@@ -1,262 +1,1633 @@
-#if defined(__STDC__) || defined(__cplusplus)
-#define YYCONST const
-#define YYPARAMS(x) x
-#define YYDEFUN(name, arglist, args) name(args)
-#define YYAND ,
-#define YYPTR void *
-#else
-#define YYCONST
-#define YYPARAMS(x) ()
-#define YYDEFUN(name, arglist, args) name arglist args;
-#define YYAND ;
-#define YYPTR char *
-#endif
-#ifndef lint
-YYCONST static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley +Cygnus.28) 01/20/91";
-#endif
-#define YYBYACC 1
-#ifndef YYDONT_INCLUDE_STDIO
-#include <stdio.h>
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ TERMINAL = 258,
+ START = 259,
+ PPERCENT = 260,
+ ID = 261,
+ TEMPLATE = 262,
+ CODE = 263,
+ INT = 264
+ };
#endif
-//#ifdef __cplusplus TA <tim@ngus.net> stdlib.h applies to C too
-#include <stdlib.h> /* for malloc/realloc/free */
-//#endif
-#line 2 "lburg/gram.y"
+/* Tokens. */
+#define TERMINAL 258
+#define START 259
+#define PPERCENT 260
+#define ID 261
+#define TEMPLATE 262
+#define CODE 263
+#define INT 264
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "src/tools/lcc/lburg/gram.y"
+
#include <stdio.h>
#include "lburg.h"
+static char rcsid[] = "$Id: gram.y 145 2001-10-17 21:53:10Z timo $";
/*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */
static int yylineno = 0;
-#line 8 "lburg/gram.y"
-typedef union {
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 8 "src/tools/lcc/lburg/gram.y"
+{
int n;
char *string;
Tree tree;
-} YYSTYPE;
-#line 37 "y.tab.c"
-#define TERMINAL 257
-#define START 258
-#define PPERCENT 259
-#define ID 260
-#define TEMPLATE 261
-#define CODE 262
-#define INT 263
-#define YYERRCODE 256
-static YYCONST short yylhs[] = { -1,
- 0, 0, 4, 4, 6, 6, 6, 6, 7, 7,
- 5, 5, 5, 5, 1, 3, 3, 3, 2,
+}
+/* Line 187 of yacc.c. */
+#line 128 "y.tab.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 141 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 35
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 16
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 9
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 20
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 37
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 264
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 10, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 13, 14, 2, 2, 15, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 12, 2,
+ 2, 11, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9
};
-static YYCONST short yylen[] = { 2,
- 3, 1, 0, 2, 3, 3, 1, 2, 0, 4,
- 0, 7, 2, 3, 1, 1, 4, 6, 1,
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 7, 9, 10, 13, 17, 21, 23,
+ 26, 27, 32, 33, 41, 44, 48, 50, 52, 57,
+ 64
};
-static YYCONST short yydefred[] = { 3,
- 0, 0, 0, 9, 0, 11, 7, 4, 8, 0,
- 15, 0, 0, 0, 5, 6, 0, 13, 0, 0,
- 14, 0, 10, 0, 0, 0, 0, 0, 19, 0,
- 17, 0, 12, 0, 18,
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 17, 0, -1, 18, 5, 21, -1, 18, -1, -1,
+ 18, 19, -1, 3, 20, 10, -1, 4, 22, 10,
+ -1, 10, -1, 1, 10, -1, -1, 20, 6, 11,
+ 9, -1, -1, 21, 22, 12, 23, 7, 24, 10,
+ -1, 21, 10, -1, 21, 1, 10, -1, 6, -1,
+ 6, -1, 6, 13, 23, 14, -1, 6, 13, 23,
+ 15, 23, 14, -1, 8, -1
};
-static YYCONST short yydgoto[] = { 1,
- 12, 30, 25, 2, 13, 8, 10,
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 22, 22, 23, 26, 27, 30, 31, 35, 36,
+ 39, 40, 43, 44, 45, 46, 49, 52, 53, 54,
+ 57
};
-static YYCONST short yysindex[] = { 0,
- 0, -4, -2, 0, -250, 0, 0, 0, 0, -9,
- 0, 1, -10, -49, 0, 0, 3, 0, -44, -248,
- 0, -244, 0, -22, -242, -244, -245, -37, 0, 10,
- 0, -244, 0, -20, 0,
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "TERMINAL", "START", "PPERCENT", "ID",
+ "TEMPLATE", "CODE", "INT", "'\\n'", "'='", "':'", "'('", "')'", "','",
+ "$accept", "spec", "decls", "decl", "blist", "rules", "nonterm", "tree",
+ "cost", 0
};
-static YYCONST short yyrindex[] = { 0,
- 0, 22, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 23, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, -39, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 10, 61, 58, 40, 41, 44
};
-static YYCONST short yygindex[] = { 0,
- 11, 0, -23, 0, 0, 0, 0,
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 16, 17, 17, 18, 18, 19, 19, 19, 19,
+ 20, 20, 21, 21, 21, 21, 22, 23, 23, 23,
+ 24
};
-#define YYTABLESIZE 255
-static YYCONST short yytable[] = { 18,
- 15, 16, 28, 31, 16, 7, 32, 9, 34, 11,
- 16, 20, 21, 22, 23, 24, 29, 26, 27, 33,
- 35, 2, 1, 19, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 17, 0, 0, 0, 11,
- 14, 3, 4, 5, 6,
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 3, 1, 0, 2, 3, 3, 1, 2,
+ 0, 4, 0, 7, 2, 3, 1, 1, 4, 6,
+ 1
};
-static YYCONST short yycheck[] = { 10,
- 10, 41, 26, 41, 44, 10, 44, 10, 32, 260,
- 10, 61, 10, 58, 263, 260, 262, 40, 261, 10,
- 41, 0, 0, 13, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 261, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 256, -1, -1, -1, 260,
- 260, 256, 257, 258, 259,
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 4, 0, 0, 1, 0, 10, 0, 12, 8, 5,
+ 9, 0, 16, 0, 0, 0, 6, 7, 0, 14,
+ 0, 0, 15, 0, 11, 17, 0, 0, 0, 0,
+ 20, 0, 18, 0, 13, 0, 19
};
-#define YYFINAL 1
-#ifndef YYDEBUG
-#define YYDEBUG 0
-#endif
-#define YYMAXTOKEN 263
-#if YYDEBUG
-static YYCONST char *YYCONST yyname[] = {
-"end-of-file",0,0,0,0,0,0,0,0,0,"'\\n'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,"':'",0,0,
-"'='",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-"TERMINAL","START","PPERCENT","ID","TEMPLATE","CODE","INT",
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 2, 9, 11, 14, 13, 26, 31
};
-static YYCONST char *YYCONST yyrule[] = {
-"$accept : spec",
-"spec : decls PPERCENT rules",
-"spec : decls",
-"decls :",
-"decls : decls decl",
-"decl : TERMINAL blist '\\n'",
-"decl : START nonterm '\\n'",
-"decl : '\\n'",
-"decl : error '\\n'",
-"blist :",
-"blist : blist ID '=' INT",
-"rules :",
-"rules : rules nonterm ':' tree TEMPLATE cost '\\n'",
-"rules : rules '\\n'",
-"rules : rules error '\\n'",
-"nonterm : ID",
-"tree : ID",
-"tree : ID '(' tree ')'",
-"tree : ID '(' tree ',' tree ')'",
-"cost : CODE",
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -26
+static const yytype_int8 yypact[] =
+{
+ -26, 11, 0, -26, 5, -26, 8, -26, -26, -26,
+ -26, 3, -26, 7, 6, 9, -26, -26, 12, -26,
+ 13, 14, -26, 15, -26, 16, 17, 15, 18, 4,
+ -26, 20, -26, 15, -26, 19, -26
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -26, -26, -26, -26, -26, -26, 21, -25, -26
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -4
+static const yytype_int8 yytable[] =
+{
+ -3, 4, 29, 5, 6, 7, -2, 18, 35, 15,
+ 8, 3, 12, 16, 12, 10, 19, 17, 32, 33,
+ 21, 25, 22, 24, 28, 23, 30, 0, 0, 27,
+ 34, 0, 0, 36, 0, 20
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 0, 1, 27, 3, 4, 5, 0, 1, 33, 6,
+ 10, 0, 6, 10, 6, 10, 10, 10, 14, 15,
+ 11, 6, 10, 9, 7, 12, 8, -1, -1, 13,
+ 10, -1, -1, 14, -1, 14
};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 17, 18, 0, 1, 3, 4, 5, 10, 19,
+ 10, 20, 6, 22, 21, 6, 10, 10, 1, 10,
+ 22, 11, 10, 12, 9, 6, 23, 13, 7, 23,
+ 8, 24, 14, 15, 10, 23, 14
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
#endif
-#define YYLEX yylex()
-#define YYEMPTY -1
-#define yyclearin (yychar=(YYEMPTY))
-#define yyerrok (yyerrflag=0)
-#ifndef YYINITDEPTH
-#define YYINITDEPTH 200
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
#endif
-#ifdef YYSTACKSIZE
-#ifndef YYMAXDEPTH
-#define YYMAXDEPTH YYSTACKSIZE
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
#else
-#ifdef YYMAXDEPTH
-#define YYSTACKSIZE YYMAXDEPTH
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
#else
-#define YYSTACKSIZE 500
-#define YYMAXDEPTH 500
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
#endif
-#ifndef YYMAXSTACKSIZE
-#define YYMAXSTACKSIZE 10000
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
int yydebug;
-int yynerrs;
-int yyerrflag;
-int yychar;
-YYSTYPE yyval;
-YYSTYPE yylval;
-static short *yyss;
-static YYSTYPE *yyvs;
-static int yystacksize;
-#define yyfree(x) free(x)
-extern int yylex();
-
-static YYPTR
-YYDEFUN (yymalloc, (bytes), unsigned bytes)
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
{
- YYPTR ptr = (YYPTR) malloc (bytes);
- if (ptr != 0) return (ptr);
- yyerror ("yyparse: memory exhausted");
- return (0);
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
}
+# endif
+# endif
-static YYPTR
-YYDEFUN (yyrealloc, (old, bytes), YYPTR old YYAND unsigned bytes)
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
{
- YYPTR ptr = (YYPTR) realloc (old, bytes);
- if (ptr != 0) return (ptr);
- yyerror ("yyparse: memory exhausted");
- return (0);
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
}
+# endif
+# endif
-static int
-#ifdef __GNUC__
-inline
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
#endif
-yygrow ()
{
-#if YYDEBUG
- int old_stacksize = yystacksize;
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
#endif
- short *new_yyss;
- YYSTYPE *new_yyvs;
+#endif /* ! YYPARSE_PARAM */
- if (yystacksize == YYMAXSTACKSIZE)
- return (1);
- yystacksize += (yystacksize + 1 ) / 2;
- if (yystacksize > YYMAXSTACKSIZE)
- yystacksize = YYMAXSTACKSIZE;
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: growing stack size from %d to %d\n",
- old_stacksize, yystacksize);
-#endif
- new_yyss = (short *) yyrealloc ((char *)yyss, yystacksize * sizeof (short));
- if (new_yyss == 0)
- return (1);
- new_yyvs = (YYSTYPE *) yyrealloc ((char *)yyvs, yystacksize * sizeof (YYSTYPE));
- if (new_yyvs == 0)
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
{
- yyfree (new_yyss);
- return (1);
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
}
- yyss = new_yyss;
- yyvs = new_yyvs;
- return (0);
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 22 "src/tools/lcc/lburg/gram.y"
+ { yylineno = 0; }
+ break;
+
+ case 3:
+#line 23 "src/tools/lcc/lburg/gram.y"
+ { yylineno = 0; }
+ break;
+
+ case 7:
+#line 31 "src/tools/lcc/lburg/gram.y"
+ {
+ if (nonterm((yyvsp[(2) - (3)].string))->number != 1)
+ yyerror("redeclaration of the start symbol\n");
+ }
+ break;
+
+ case 9:
+#line 36 "src/tools/lcc/lburg/gram.y"
+ { yyerrok; }
+ break;
+
+ case 11:
+#line 40 "src/tools/lcc/lburg/gram.y"
+ { term((yyvsp[(2) - (4)].string), (yyvsp[(4) - (4)].n)); }
+ break;
+
+ case 13:
+#line 44 "src/tools/lcc/lburg/gram.y"
+ { rule((yyvsp[(2) - (7)].string), (yyvsp[(4) - (7)].tree), (yyvsp[(5) - (7)].string), (yyvsp[(6) - (7)].string)); }
+ break;
+
+ case 15:
+#line 46 "src/tools/lcc/lburg/gram.y"
+ { yyerrok; }
+ break;
+
+ case 16:
+#line 49 "src/tools/lcc/lburg/gram.y"
+ { nonterm((yyval.string) = (yyvsp[(1) - (1)].string)); }
+ break;
+
+ case 17:
+#line 52 "src/tools/lcc/lburg/gram.y"
+ { (yyval.tree) = tree((yyvsp[(1) - (1)].string), 0, 0); }
+ break;
+
+ case 18:
+#line 53 "src/tools/lcc/lburg/gram.y"
+ { (yyval.tree) = tree((yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].tree), 0); }
+ break;
+
+ case 19:
+#line 54 "src/tools/lcc/lburg/gram.y"
+ { (yyval.tree) = tree((yyvsp[(1) - (6)].string), (yyvsp[(3) - (6)].tree), (yyvsp[(5) - (6)].tree)); }
+ break;
+
+ case 20:
+#line 57 "src/tools/lcc/lburg/gram.y"
+ { if (*(yyvsp[(1) - (1)].string) == 0) (yyval.string) = "0"; }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1416 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
}
-#line 60 "lburg/gram.y"
+
+
+#line 59 "src/tools/lcc/lburg/gram.y"
+
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
@@ -400,283 +1771,4 @@ void yywarn(char *fmt, ...) {
fprintf(stderr, "warning: ");
vfprintf(stderr, fmt, ap);
}
-#line 403 "y.tab.c"
-#define YYABORT goto yyabort
-#define YYACCEPT goto yyaccept
-#define YYERROR goto yyerrlab
-#if YYDEBUG
-#ifdef __cplusplus
-extern "C" char *getenv();
-#else
-extern char *getenv();
-#endif
-#endif
-
-int
-yyparse()
-{
- register int yym, yyn, yystate;
- register YYSTYPE *yyvsp;
- register short *yyssp;
- short *yysse;
-#if YYDEBUG
- register YYCONST char *yys;
-
- if (yys = getenv("YYDEBUG"))
- {
- yyn = *yys;
- if (yyn >= '0' && yyn <= '9')
- yydebug = yyn - '0';
- }
-#endif
-
- yynerrs = 0;
- yyerrflag = 0;
- yychar = (-1);
-
- if (yyss == 0)
- {
- yyss = (short *) yymalloc (YYSTACKSIZE * sizeof (short));
- if (yyss == 0)
- goto yyabort;
- yyvs = (YYSTYPE *) yymalloc (YYSTACKSIZE * sizeof (YYSTYPE));
- if (yyvs == 0)
- {
- yyfree (yyss);
- goto yyabort;
- }
- yystacksize = YYSTACKSIZE;
- }
- yysse = yyss + yystacksize - 1;
- yyssp = yyss;
- yyvsp = yyvs;
- *yyssp = yystate = 0;
- goto yyloop;
-
-yypush_lex:
- yyval = yylval;
- yystate = yytable[yyn];
-yypush:
- if (yyssp >= yysse)
- {
- int depth = yyssp - yyss;
- if (yygrow() != 0)
- goto yyoverflow;
- yysse = yyss + yystacksize -1;
- yyssp = depth + yyss;
- yyvsp = depth + yyvs;
- }
- *++yyssp = yystate;
- *++yyvsp = yyval;
-
-yyloop:
- if ((yyn = yydefred[yystate])) goto yyreduce;
- yyn = yysindex[yystate];
- if (yychar < 0)
- {
- if ((yychar = yylex()) < 0) yychar = 0;
-#if YYDEBUG
- if (yydebug)
- {
- yys = 0;
- if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
- if (!yys) yys = "illegal-symbol";
- printf("yydebug: state %d, reading %d (%s)\n", yystate,
- yychar, yys);
- }
-#endif
- }
- if (yyn != 0
- && ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
- && yycheck[yyn] == yychar)
- {
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: state %d, shifting to state %d\n",
- yystate, yytable[yyn]);
-#endif
- if (yyerrflag > 0) --yyerrflag;
- yychar = (-1);
- goto yypush_lex;
- }
- yyn = yyrindex[yystate];
- if (yyn != 0
- && ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
- && yycheck[yyn] == yychar)
- {
- yyn = yytable[yyn];
- goto yyreduce;
- }
- if (yyerrflag) goto yyinrecovery;
-#ifdef lint
- goto yynewerror;
-yynewerror:
-#endif
- yyerror("syntax error");
-#ifdef lint
- goto yyerrlab;
-yyerrlab:
-#endif
- ++yynerrs;
-yyinrecovery:
- if (yyerrflag < 3)
- {
- yyerrflag = 3;
- for (;;)
- {
- yyn = yysindex[*yyssp];
- if (yyn != 0
- && ((yyn += YYERRCODE), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
- && yycheck[yyn] == YYERRCODE)
- {
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: state %d, error recovery shifting\
- to state %d\n", *yyssp, yytable[yyn]);
-#endif
- goto yypush_lex;
- }
- else
- {
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: error recovery discarding state %d\n",
- *yyssp);
-#endif
- if (yyssp <= yyss) goto yyabort;
- --yyssp;
- --yyvsp;
- }
- }
- }
- else
- {
- if (yychar == 0) goto yyabort;
-#if YYDEBUG
- if (yydebug)
- {
- yys = 0;
- if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
- if (!yys) yys = "illegal-symbol";
- printf("yydebug: state %d, error recovery discards token %d (%s)\n",
- yystate, yychar, yys);
- }
-#endif
- yychar = (-1);
- goto yyloop;
- }
-yyreduce:
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: state %d, reducing by rule %d (%s)\n",
- yystate, yyn, yyrule[yyn]);
-#endif
- yym = yylen[yyn];
- yyval = yyvsp[1-yym];
- switch (yyn)
- {
-case 1:
-#line 22 "lburg/gram.y"
-{ yylineno = 0; }
-break;
-case 2:
-#line 23 "lburg/gram.y"
-{ yylineno = 0; }
-break;
-case 6:
-#line 31 "lburg/gram.y"
-{
- if (nonterm(yyvsp[-1].string)->number != 1)
- yyerror("redeclaration of the start symbol\n");
- }
-break;
-case 8:
-#line 36 "lburg/gram.y"
-{ yyerrok; }
-break;
-case 10:
-#line 40 "lburg/gram.y"
-{ term(yyvsp[-2].string, yyvsp[0].n); }
-break;
-case 12:
-#line 44 "lburg/gram.y"
-{ rule(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-2].string, yyvsp[-1].string); }
-break;
-case 14:
-#line 46 "lburg/gram.y"
-{ yyerrok; }
-break;
-case 15:
-#line 49 "lburg/gram.y"
-{ nonterm(yyval.string = yyvsp[0].string); }
-break;
-case 16:
-#line 52 "lburg/gram.y"
-{ yyval.tree = tree(yyvsp[0].string, 0, 0); }
-break;
-case 17:
-#line 53 "lburg/gram.y"
-{ yyval.tree = tree(yyvsp[-3].string, yyvsp[-1].tree, 0); }
-break;
-case 18:
-#line 54 "lburg/gram.y"
-{ yyval.tree = tree(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-1].tree); }
-break;
-case 19:
-#line 57 "lburg/gram.y"
-{ if (*yyvsp[0].string == 0) yyval.string = "0"; }
-break;
-#line 630 "y.tab.c"
- }
- yyssp -= yym;
- yyvsp -= yym;
- yym = yylhs[yyn];
- yystate = *yyssp;
- if (yystate == 0 && yym == 0)
- {
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: after reduction, shifting from state 0 to\
- state %d\n", YYFINAL);
-#endif
- yystate = YYFINAL;
- *++yyssp = YYFINAL;
- *++yyvsp = yyval;
- if (yychar < 0)
- {
- if ((yychar = yylex()) < 0) yychar = 0;
-#if YYDEBUG
- if (yydebug)
- {
- yys = 0;
- if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
- if (!yys) yys = "illegal-symbol";
- printf("yydebug: state %d, reading %d (%s)\n",
- YYFINAL, yychar, yys);
- }
-#endif
- }
- if (yychar == 0) goto yyaccept;
- goto yyloop;
- }
- yyn = yygindex[yym];
- if (yyn != 0
- && ((yyn += yystate), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
- && yycheck[yyn] == yystate)
- yystate = yytable[yyn];
- else
- yystate = yydgoto[yym];
-#if YYDEBUG
- if (yydebug)
- printf("yydebug: after reduction, shifting from state %d \
-to state %d\n", *yyssp, yystate);
-#endif
- goto yypush;
-yyoverflow:
- yyerror("yacc stack overflow");
-yyabort:
- return (1);
-yyaccept:
- return (0);
-}
diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c
index ec1e1e73..90b70114 100644
--- a/src/ui/ui_atoms.c
+++ b/src/ui/ui_atoms.c
@@ -160,11 +160,11 @@ qboolean UI_ConsoleCommand( int realTime )
return qtrue;
}
- if( Q_strncmp( cmd, "messagemode", 11 ) == 0 )
+ if( Q_strncmp( cmd, "ui_messagemode", 14 ) == 0 )
{
trap_Cvar_Set( "ui_sayBuffer", "" );
- switch( cmd[ 11 ] )
+ switch( cmd[ strlen(cmd) ] )
{
default:
case '\0':
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index fcc08e67..e8d6988c 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -2892,12 +2892,15 @@ static void UI_RunMenuScript( char **args )
char buffer[ MAX_CVAR_VALUE_STRING ];
trap_Cvar_VariableStringBuffer( "ui_sayBuffer", buffer, sizeof( buffer ) );
- if( uiInfo.chatTargetClientNum != -1 )
- trap_Cmd_ExecuteText( EXEC_APPEND, va( "tell %i \"%s\"\n", uiInfo.chatTargetClientNum, buffer ) );
- else if( uiInfo.chatTeam )
- trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"%s\"\n", buffer ) );
- else
- trap_Cmd_ExecuteText( EXEC_APPEND, va( "say \"%s\"\n", buffer ) );
+ if( buffer[ 0 ] )
+ {
+ if( uiInfo.chatTargetClientNum != -1 )
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "tell %i \"%s\"\n", uiInfo.chatTargetClientNum, buffer ) );
+ else if( uiInfo.chatTeam )
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"%s\"\n", buffer ) );
+ else
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "say \"%s\"\n", buffer ) );
+ }
}
else if( Q_stricmp( name, "playMovie" ) == 0 )
{
diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c
index 108113ed..f59a7349 100644
--- a/src/ui/ui_shared.c
+++ b/src/ui/ui_shared.c
@@ -4638,6 +4638,7 @@ void Item_Text_Wrapped_Paint( itemDef_t *item )
int paintLines, totalLines, lineNum = 0;
float paintY;
int i;
+ char lastCode = 0, newCode = 0;
UI_CreateCacheEntry( textPtr, &item->window.rect, item->textscale );
@@ -4688,6 +4689,10 @@ void Item_Text_Wrapped_Paint( itemDef_t *item )
{
int lineLength = &textPtr[ i ] - p;
+ // track any color escape sequences
+ if( i && Q_IsColorString( textPtr + i - 1 ) )
+ newCode = textPtr[ i ];
+
if( lineLength >= sizeof( buff ) - 1 )
break;
@@ -4696,8 +4701,24 @@ void Item_Text_Wrapped_Paint( itemDef_t *item )
itemDef_t lineItem;
int width, height;
- strncpy( buff, p, lineLength );
- buff[ lineLength ] = '\0';
+ // if there was a color escape before we need to insert it on the
+ // start of every line again, otherwise don't insert a color escape
+ // in order to preserve the item's assigned color
+ if( lastCode )
+ {
+ buff[ 0 ] = Q_COLOR_ESCAPE;
+ buff[ 1 ] = lastCode;
+ strncpy( buff + 2, p, lineLength );
+ buff[ lineLength + 2 ] = '\0';
+ }
+ else
+ {
+ strncpy( buff, p, lineLength );
+ buff[ lineLength ] = '\0';
+ }
+
+ lastCode = newCode;
+
p = &textPtr[ i + 1 ];
lineItem.type = ITEM_TYPE_TEXT;
@@ -5665,7 +5686,7 @@ void Item_ListBox_Paint( itemDef_t *item )
DC->drawHandlePic( x + columnPos, y + ( ( listPtr->elementHeight - height ) / 2.0f ),
width, height, optionalImage );
}
- else if( text )
+ else if( text[ 0 ] )
{
int alignOffset = 0.0f, tw;
@@ -5717,7 +5738,7 @@ void Item_ListBox_Paint( itemDef_t *item )
if( optionalImage >= 0 )
DC->drawHandlePic( x + offset, y, listPtr->elementHeight, listPtr->elementHeight, optionalImage );
- else if( text )
+ else if( text[ 0 ] )
{
UI_Text_Paint( x + offset, y + m + ( ( listPtr->elementHeight - m ) / 2.0f ),
item->textscale, item->window.foreColor, text, 0,
diff --git a/ui/ingame.txt b/ui/ingame.txt
index 251aa779..a71be329 100644
--- a/ui/ingame.txt
+++ b/ui/ingame.txt
@@ -5,4 +5,5 @@
loadMenu { "ui/ingame_game.menu" }
loadMenu { "ui/ingame_options.menu" }
loadMenu { "ui/ingame_leave.menu" }
+ loadMenu { "ui/fonttest.menu" }
}
diff --git a/ui/ingame_options.menu b/ui/ingame_options.menu
index fe8c5e37..f80c311c 100644
--- a/ui/ingame_options.menu
+++ b/ui/ingame_options.menu
@@ -360,6 +360,27 @@
}
}
+ itemDef
+ {
+ name game
+ group optionsGrp
+ type ITEM_TYPE_MULTI
+ text "Crosshair Size:"
+ cvar "cg_crosshairSize"
+ cvarFloatList { "Normal" 1 "Small" 0.75 "Tiny" 0.5 "Huge" 1.25 }
+ rect CONTENT_X (CONTENT_Y+(11*ELEM_H)) CONTENT_W ELEM_H
+ textalign ALIGN_RIGHT
+ textvalign VALIGN_CENTER
+ textalignx CONTENT_OFF
+ textscale .25
+ forecolor 1 1 1 1
+ visible MENU_FALSE
+ action
+ {
+ play "sound/misc/menu1.wav";
+ }
+ }
+
//////// CONTROLS
//Controls menu
diff --git a/ui/say.menu b/ui/say.menu
index dea298c9..bf20d38d 100644
--- a/ui/say.menu
+++ b/ui/say.menu
@@ -6,7 +6,7 @@
#define X BORDER
#define Y 200
-#define W (640-(2*BORDER))
+#define W (600-(2*BORDER))
#define H 40
menuDef
@@ -34,8 +34,9 @@
rect 0 0 W H
textalign ALIGN_LEFT
textvalign VALIGN_CENTER
- textscale .5
- forecolor 1 1 1 1
+ textstyle ITEM_TEXTSTYLE_SHADOWED
+ textscale .4
+ forecolor 0.93 0.93 0.92 1
visible MENU_TRUE
onTextEntry
{
diff --git a/ui/tremulous_common_hud.h b/ui/tremulous_common_hud.h
index 3bc62ba1..d7e497de 100644
--- a/ui/tremulous_common_hud.h
+++ b/ui/tremulous_common_hud.h
@@ -17,11 +17,11 @@ itemDef
style WINDOW_STYLE_EMPTY
visible MENU_TRUE
decoration
- forecolor 1 1 1 1
+ forecolor 0.93 0.93 0.92 1
textalign ALIGN_LEFT
textvalign VALIGN_TOP
- textscale 0.4
- textstyle ITEM_TEXTSTYLE_NORMAL
+ textscale 0.35
+ textstyle ITEM_TEXTSTYLE_SHADOWED
ownerdraw CG_CONSOLE
}
@@ -37,7 +37,7 @@ itemDef
forecolor 1 1 1 0.35
textalign ALIGN_LEFT
textvalign VALIGN_TOP
- textscale 0.3
+ textscale 0.4
textstyle ITEM_TEXTSTYLE_NORMAL
ownerdraw CG_TUTORIAL
}
@@ -158,4 +158,5 @@ itemDef
decoration
textScale .5
ownerdraw CG_PLAYER_CROSSHAIRNAMES
+ textstyle ITEM_TEXTSTYLE_SHADOWED
}
diff --git a/ui/tremulous_human_hud.menu b/ui/tremulous_human_hud.menu
index a2bdb82d..3d527608 100644
--- a/ui/tremulous_human_hud.menu
+++ b/ui/tremulous_human_hud.menu
@@ -242,6 +242,7 @@
decoration
textScale .5
ownerdraw CG_PLAYER_SELECTTEXT
+ textstyle ITEM_TEXTSTYLE_SHADOWED
}
//AMMO