diff options
author | Michael Levin <risujin@fastmail.fm> | 2009-10-03 11:13:10 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-03 00:14:46 +0000 |
commit | d9d4a8c3cb1fb226738278923cf8d5ba2b74efdf (patch) | |
tree | 23637261238f8791a95e33eef077079608de4030 /src | |
parent | ae1fb9aecf949fb7261b3e4b065a4bddd016de6b (diff) |
Added armour files to version control.
Diffstat (limited to 'src')
35 files changed, 3505 insertions, 1736 deletions
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( ¢->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( ¢->muzzleTS ) ) CG_DestroyTrailSystem( ¢->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 = ¢->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( ¢->level2ZapTS[ i ] ) ) - cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS ); - - if( CG_IsTrailSystemValid( ¢->level2ZapTS[ i ] ) ) - { - CG_SetAttachmentCent( ¢->level2ZapTS[ i ]->frontAttachment, source ); - CG_SetAttachmentCent( ¢->level2ZapTS[ i ]->backAttachment, target ); - CG_AttachToCent( ¢->level2ZapTS[ i ]->frontAttachment ); - CG_AttachToCent( ¢->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 = ¢->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( ¢->level2ZapTS[ i ] ) ) - CG_DestroyTrailSystem( ¢->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, |