summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Levin <risujin@fastmail.fm>2009-10-03 11:26:51 +0000
committerTim Angus <tim@ngus.net>2013-01-03 00:15:00 +0000
commit0cf04fe37fef2e827a95bb19926a3cbf8e98c581 (patch)
tree93ac01309f47edb27be26fb31e3886f6082579b1 /src
parentd9709deb95375f2462e5ca9fc98ebf92fd91ce04 (diff)
* Added charge meter to the UI, can be disabled via GUI
* Trample and Lucifer Cannon charging reworked and moved to PMove * Can no longer "cancel" a charging Lucifer Cannon * STAT_MISC2 is now unused, booster and charge information can be inferred elsewhere (STAT_UNUSED) * Trying to fire an empty weapon makes a clicking noise (also a bugfix, apparently the server would spam EV_NOAMMO which did nothing but is now used for the clicking noise -- audible to all players) * Created an alternate, muffled Lucifer Cannon warning noise for other people's Lucifer Cannons * Fixed bug which prevented players from switching to a different player while spectating someone in the spawn queue * Spectators are now properly moved to the lock view position when spectating a player in the spawn queue
Diffstat (limited to 'src')
-rw-r--r--src/cgame/cg_draw.c141
-rw-r--r--src/cgame/cg_event.c4
-rw-r--r--src/cgame/cg_local.h8
-rw-r--r--src/cgame/cg_main.c6
-rw-r--r--src/cgame/cg_view.c2
-rw-r--r--src/cgame/cg_weapons.c33
-rw-r--r--src/game/bg_misc.c15
-rw-r--r--src/game/bg_pmove.c192
-rw-r--r--src/game/bg_public.h4
-rw-r--r--src/game/g_active.c93
-rw-r--r--src/game/g_buildable.c2
-rw-r--r--src/game/g_cmds.c31
-rw-r--r--src/game/g_local.h1
-rw-r--r--src/game/g_missile.c13
-rw-r--r--src/game/g_weapon.c22
-rw-r--r--src/game/tremulous.h32
16 files changed, 364 insertions, 235 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 08433c44..5002fc9a 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -588,21 +588,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;
+ vec4_t localColor;
Vector4Copy( color, localColor );
- if( ps->stats[ STAT_STATE ] & SS_BOOSTED )
- {
- if( ps->stats[ STAT_MISC2 ] < 3000 )
- {
- qboolean flash = ( ps->stats[ STAT_MISC2 ] / 500 ) % 2;
-
- if( flash )
- localColor[ 3 ] = 1.0f;
- }
- }
+ // Flash bolts when the boost is almost out
+ if( ( cg.snap->ps.stats[ STAT_STATE ] & SS_BOOSTED ) &&
+ cg.boostedTime > 0 && cg.time - cg.boostedTime > BOOST_TIME - 5000 &&
+ cg.time & 256 )
+ localColor[ 3 ] = 1.0f;
trap_R_SetColor( localColor );
CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
@@ -904,6 +898,123 @@ static void CG_DrawPlayerHealthCross3( rectDef_t *rect, vec4_t color, qhandle_t
trap_R_SetColor( NULL );
}
+static float CG_ChargeProgress( void )
+{
+ float progress;
+ int min = 0, max = 0;
+
+ if( cg.snap->ps.weapon == WP_ALEVEL3 )
+ {
+ min = LEVEL3_POUNCE_TIME_MIN;
+ max = LEVEL3_POUNCE_TIME;
+ }
+ else if( cg.snap->ps.weapon == WP_ALEVEL3_UPG )
+ {
+ min = LEVEL3_POUNCE_TIME_MIN;
+ max = LEVEL3_POUNCE_TIME_UPG;
+ }
+ else if( cg.snap->ps.weapon == WP_ALEVEL4 )
+ {
+ min = LEVEL4_TRAMPLE_CHARGE_MIN;
+ max = LEVEL4_TRAMPLE_CHARGE_MAX;
+ }
+ else if( cg.snap->ps.weapon == WP_LUCIFER_CANNON )
+ {
+ min = LCANNON_CHARGE_TIME_MIN;
+ max = LCANNON_CHARGE_TIME_MAX;
+ }
+ if( max - min < 0.0f )
+ return 0.0f;
+ progress = ( (float)cg.predictedPlayerState.stats[ STAT_MISC ] - min ) /
+ ( max - min );
+ if( progress > 1.0f )
+ return 1.0f;
+ if( progress < 0.0f )
+ return 0.0f;
+ return progress;
+}
+
+#define CHARGE_BAR_FADE_RATE 0.002f
+
+static void CG_DrawPlayerChargeBarBG( rectDef_t *rect, vec4_t ref_color,
+ qhandle_t shader )
+{
+ vec4_t color;
+
+ if( !cg_drawChargeBar.integer || cg.chargeMeterAlpha <= 0.0f )
+ return;
+
+ color[ 0 ] = ref_color[ 0 ];
+ color[ 1 ] = ref_color[ 1 ];
+ color[ 2 ] = ref_color[ 2 ];
+ color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha;
+
+ // Draw meter background
+ trap_R_SetColor( color );
+ CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
+ trap_R_SetColor( NULL );
+}
+
+static void CG_DrawPlayerChargeBar( rectDef_t *rect, vec4_t ref_color,
+ qhandle_t shader )
+{
+ vec4_t color;
+ float x, y, width, height, cap_width, progress;
+
+ if( !cg_drawChargeBar.integer )
+ return;
+
+ // Get progress proportion and pump fade
+ progress = CG_ChargeProgress();
+ if( progress <= 0.0f )
+ {
+ cg.chargeMeterAlpha -= CHARGE_BAR_FADE_RATE * cg.frametime;
+ if( cg.chargeMeterAlpha <= 0.0f )
+ {
+ cg.chargeMeterAlpha = 0.0f;
+ return;
+ }
+ }
+ else
+ {
+ cg.chargeMeterValue = progress;
+ cg.chargeMeterAlpha += CHARGE_BAR_FADE_RATE * cg.frametime;
+ if( cg.chargeMeterAlpha > 1.0f )
+ cg.chargeMeterAlpha = 1.0f;
+ }
+
+ color[ 0 ] = ref_color[ 0 ];
+ color[ 1 ] = ref_color[ 1 ];
+ color[ 2 ] = ref_color[ 2 ];
+ color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha;
+
+ // Flash red for Lucifer Cannon warning
+ if( cg.snap->ps.weapon == WP_LUCIFER_CANNON &&
+ cg.snap->ps.stats[ STAT_MISC ] >= LCANNON_CHARGE_TIME_WARN &&
+ ( cg.time & 128 ) )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = 0.0f;
+ color[ 2 ] = 0.0f;
+ }
+
+ // Calculate bar coords
+ x = rect->x;
+ y = rect->y;
+ width = ( rect->w - 6 ) * cg.chargeMeterValue;
+ height = rect->h;
+ CG_AdjustFrom640( &x, &y, &width, &height );
+ cap_width = 3 * cgs.screenXScale;
+
+ // Draw the meter
+ trap_R_SetColor( color );
+ trap_R_DrawStretchPic( x, y, cap_width, height, 0, 0, 1, 1, shader );
+ trap_R_DrawStretchPic( x + width + cap_width, y, cap_width, height,
+ 1, 0, 0, 1, shader );
+ trap_R_DrawStretchPic( x + cap_width, y, width, height, 1, 0, 1, 1, shader );
+ trap_R_SetColor( NULL );
+}
+
static void CG_DrawProgressLabel( rectDef_t *rect, float text_x, float text_y, vec4_t color,
float scale, int textalign, int textvalign,
const char *s, float fraction )
@@ -2151,6 +2262,12 @@ void CG_OwnerDraw( float x, float y, float w, float h, float text_x,
case CG_PLAYER_HEALTH_CROSS3:
CG_DrawPlayerHealthCross3( &rect, color, shader );
break;
+ case CG_PLAYER_CHARGE_BAR_BG:
+ CG_DrawPlayerChargeBarBG( &rect, color, shader );
+ break;
+ case CG_PLAYER_CHARGE_BAR:
+ CG_DrawPlayerChargeBar( &rect, color, shader );
+ break;
case CG_PLAYER_CLIPS_RING:
CG_DrawPlayerClipsRing( &rect, color, shader );
break;
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index 9b6afbe6..2d622088 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -737,8 +737,8 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
//
case EV_NOAMMO:
DEBUGNAME( "EV_NOAMMO" );
- {
- }
+ trap_S_StartSound( NULL, es->number, CHAN_WEAPON,
+ cgs.media.weaponEmptyClick );
break;
case EV_CHANGE_WEAPON:
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 360dac34..8dfd09a4 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1133,6 +1133,11 @@ typedef struct
playerState_t savedPmoveStates[ NUM_SAVED_STATES ];
int stateHead, stateTail;
int ping;
+
+ float chargeMeterAlpha;
+ float chargeMeterValue;
+
+ int nextWeaponClickTime;
} cg_t;
@@ -1179,6 +1184,7 @@ typedef struct
// sounds
sfxHandle_t tracerSound;
+ sfxHandle_t weaponEmptyClick;
sfxHandle_t selectSound;
sfxHandle_t footsteps[ FOOTSTEP_TOTAL ][ 4 ];
sfxHandle_t talkSound;
@@ -1263,6 +1269,7 @@ typedef struct
qhandle_t massDriverTS;
sfxHandle_t lCannonWarningSound;
+ sfxHandle_t lCannonWarningSound2;
qhandle_t buildWeaponTimerPie[ 8 ];
qhandle_t upgradeClassIconShader;
@@ -1409,6 +1416,7 @@ extern vmCvar_t cg_drawSnapshot;
extern vmCvar_t cg_draw3dIcons;
extern vmCvar_t cg_drawIcons;
extern vmCvar_t cg_drawAmmoWarning;
+extern vmCvar_t cg_drawChargeBar;
extern vmCvar_t cg_drawCrosshair;
extern vmCvar_t cg_drawCrosshairNames;
extern vmCvar_t cg_drawRewards;
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index ac0715fe..a2027b3e 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -125,6 +125,7 @@ vmCvar_t cg_drawSnapshot;
vmCvar_t cg_draw3dIcons;
vmCvar_t cg_drawIcons;
vmCvar_t cg_drawAmmoWarning;
+vmCvar_t cg_drawChargeBar;
vmCvar_t cg_drawCrosshair;
vmCvar_t cg_drawCrosshairNames;
vmCvar_t cg_drawRewards;
@@ -268,6 +269,7 @@ static cvarTable_t cvarTable[ ] =
{ &cg_draw3dIcons, "cg_draw3dIcons", "1", CVAR_ARCHIVE },
{ &cg_drawIcons, "cg_drawIcons", "1", CVAR_ARCHIVE },
{ &cg_drawAmmoWarning, "cg_drawAmmoWarning", "1", CVAR_ARCHIVE },
+ { &cg_drawChargeBar, "cg_drawChargeBar", "1", CVAR_ARCHIVE },
{ &cg_drawAttacker, "cg_drawAttacker", "1", CVAR_ARCHIVE },
{ &cg_drawCrosshair, "cg_drawCrosshair", "1", CVAR_ARCHIVE },
{ &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE },
@@ -692,6 +694,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.weaponEmptyClick = trap_S_RegisterSound( "sound/weapons/click.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 );
@@ -760,6 +763,7 @@ static void CG_RegisterSounds( void )
cgs.media.buildableRepairedSound = trap_S_RegisterSound( "sound/buildables/human/repaired.wav", qfalse );
cgs.media.lCannonWarningSound = trap_S_RegisterSound( "models/weapons/lcannon/warning.wav", qfalse );
+ cgs.media.lCannonWarningSound2 = trap_S_RegisterSound( "models/weapons/lcannon/warning2.wav", qfalse );
}
@@ -835,7 +839,7 @@ static void CG_RegisterGraphics( void )
cgs.media.buildWeaponTimerPie[ i ] = trap_R_RegisterShader( buildWeaponTimerPieShaders[ i ] );
cgs.media.upgradeClassIconShader = trap_R_RegisterShader( "icons/icona_upgrade.tga" );
-
+
cgs.media.balloonShader = trap_R_RegisterShader( "gfx/sprites/chatballoon" );
cgs.media.disconnectPS = CG_RegisterParticleSystem( "disconnectPS" );
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 2c718556..9772e1f5 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -471,7 +471,7 @@ static void CG_OffsetFirstPersonView( void )
if( ps->stats[ STAT_MISC ] > 0 )
{
float fraction = (float)ps->stats[ STAT_MISC ] /
- (float)LEVEL4_TRAMPLE_CHARGE_MAX;
+ LEVEL4_TRAMPLE_CHARGE_MAX;
if( fraction > 1.0f )
fraction = 1.0f;
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index f4c8fbf8..d0968dd9 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -872,8 +872,9 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
// Lucifer cannon charge warning beep
if( weaponNum == WP_LUCIFER_CANNON &&
( cent->currentState.eFlags & EF_WARN_CHARGE ) )
- trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
- vec3_origin, cgs.media.lCannonWarningSound );
+ trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin,
+ vec3_origin, ps ? cgs.media.lCannonWarningSound :
+ cgs.media.lCannonWarningSound2 );
if( !noGunModel )
{
@@ -987,6 +988,9 @@ CG_AddViewWeapon
Add the weapon, and flash for the player's view
==============
*/
+
+#define WEAPON_CLICK_REPEAT 500
+
void CG_AddViewWeapon( playerState_t *ps )
{
refEntity_t hand;
@@ -1072,27 +1076,16 @@ 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 )
+ // Lucifer Cannon vibration effect
+ if( weapon == WP_LUCIFER_CANNON && ps->stats[ STAT_MISC ] > 0 )
{
float fraction;
- 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 );
- }
+ fraction = (float)ps->stats[ STAT_MISC ] / LCANNON_CHARGE_TIME_MAX;
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 0 ],
+ hand.origin );
+ VectorMA( hand.origin, random( ) * fraction, cg.refdef.viewaxis[ 1 ],
+ hand.origin );
}
AnglesToAxis( angles, hand.axis );
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 5198fb28..02db5993 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -5430,6 +5430,21 @@ int BG_GetValueOfPlayer( playerState_t *ps )
}
/*
+=================
+BG_PlayerCanChangeWeapon
+=================
+*/
+qboolean BG_PlayerCanChangeWeapon( playerState_t *ps )
+{
+ // Do not allow Lucifer Cannon "canceling" via weapon switch
+ if( ps->weapon == WP_LUCIFER_CANNON &&
+ ps->stats[ STAT_MISC ] > LCANNON_CHARGE_TIME_MIN )
+ return qfalse;
+
+ return ps->weaponTime <= 0 || ps->weaponstate != WEAPON_FIRING;
+}
+
+/*
===============
atof_neg
diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c
index 51e1415c..d2c8acc5 100644
--- a/src/game/bg_pmove.c
+++ b/src/game/bg_pmove.c
@@ -404,8 +404,9 @@ static float PM_CmdScale( usercmd_t *cmd )
}
if( pm->ps->weapon == WP_ALEVEL4 && pm->ps->pm_flags & PMF_CHARGE )
- modifier *= ( 1.0f + ( pm->ps->stats[ STAT_MISC ] /
- (float)LEVEL4_TRAMPLE_CHARGE_MAX ) * ( LEVEL4_TRAMPLE_SPEED - 1.0f ) );
+ modifier *= 1.0f + ( pm->ps->stats[ STAT_MISC ] *
+ ( LEVEL4_TRAMPLE_SPEED - 1.0f ) /
+ LEVEL4_TRAMPLE_CHARGE_MAX );
//slow player if charging up for a pounce
if( ( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG ) &&
@@ -847,7 +848,7 @@ static qboolean PM_CheckDodge( void )
}
// Reasons to stop a sprint
- if( pm->cmd.forwardmove <= 64 ||
+ if( pm->cmd.forwardmove <= 0 ||
pm->cmd.upmove < 0 ||
pm->ps->pm_type != PM_NORMAL )
pm->ps->stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
@@ -2791,6 +2792,18 @@ static void PM_Weapon( void )
qboolean attack2 = qfalse;
qboolean attack3 = qfalse;
+ // Ignore weapons in some cases
+ if( pm->ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR ||
+ ( pm->ps->stats[ STAT_STATE ] & ( SS_INFESTING | SS_HOVELING ) ) )
+ return;
+
+ // Check for dead player
+ if( pm->ps->stats[ STAT_HEALTH ] <= 0 )
+ {
+ pm->ps->weapon = WP_NONE;
+ return;
+ }
+
// Charging for a pounce or canceling a pounce
if( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG )
{
@@ -2809,43 +2822,96 @@ static void PM_Weapon( void )
pm->ps->stats[ STAT_MISC ] = 0;
}
- // Set overcharging flag so other players can hear warning
+ // Trample charge mechanics
+ if( pm->ps->weapon == WP_ALEVEL4 )
+ {
+ // Charging up
+ if( !( pm->ps->stats[ STAT_STATE ] & SS_CHARGING ) )
+ {
+ // Charge button held
+ if( pm->ps->stats[ STAT_MISC ] < LEVEL4_TRAMPLE_CHARGE_MAX &&
+ ( pm->cmd.buttons & BUTTON_ATTACK2 ) )
+ {
+ pm->ps->stats[ STAT_STATE ] &= ~SS_CHARGING;
+ if( pm->cmd.forwardmove > 0 )
+ pm->ps->stats[ STAT_MISC ] += pml.msec * LEVEL4_TRAMPLE_CHARGE_MAX /
+ LEVEL4_TRAMPLE_CHARGE_TIME_MAX;
+ else
+ pm->ps->stats[ STAT_MISC ] = 0;
+ if( pm->ps->stats[ STAT_MISC ] > LEVEL4_TRAMPLE_CHARGE_MAX )
+ {
+ pm->ps->stats[ STAT_MISC ] = LEVEL4_TRAMPLE_CHARGE_MAX;
+ pm->ps->stats[ STAT_STATE ] |= SS_CHARGING;
+ PM_AddEvent( EV_LEV4_TRAMPLE_START );
+ }
+ }
+
+ // Charge button released
+ else if( !( pm->ps->stats[ STAT_STATE ] & SS_CHARGING ) )
+ {
+ if( pm->ps->stats[ STAT_MISC ] > LEVEL4_TRAMPLE_CHARGE_MIN )
+ {
+ pm->ps->stats[ STAT_STATE ] |= SS_CHARGING;
+ PM_AddEvent( EV_LEV4_TRAMPLE_START );
+ }
+ else
+ pm->ps->stats[ STAT_MISC ] -= pml.msec;
+ }
+ }
+
+ // Discharging
+ else
+ {
+ pm->ps->stats[ STAT_MISC ] -= pml.msec;
+
+ // If the charger has stopped moving take a chunk of charge away
+ if( VectorLength( pm->ps->velocity ) < 64.0f || pm->cmd.rightmove )
+ pm->ps->stats[ STAT_MISC ] -= LEVEL4_TRAMPLE_STOP_PENALTY * pml.msec;
+ }
+
+ // Charge is over
+ if( pm->ps->stats[ STAT_MISC ] <= 0 || pm->cmd.forwardmove <= 0 )
+ {
+ pm->ps->stats[ STAT_MISC ] = 0;
+ pm->ps->stats[ STAT_STATE ] &= ~SS_CHARGING;
+ }
+ }
+
+ // Charging up a Lucifer Cannon
pm->ps->eFlags &= ~EF_WARN_CHARGE;
- if( pm->ps->weapon == WP_LUCIFER_CANNON &&
- pm->ps->stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE * 2 / 3 )
- pm->ps->eFlags |= EF_WARN_CHARGE;
+ if( pm->ps->weapon == WP_LUCIFER_CANNON )
+ {
+ // Charging up
+ if( !pm->ps->weaponTime && pm->ps->weaponstate != WEAPON_NEEDS_RESET &&
+ ( pm->cmd.buttons & BUTTON_ATTACK ) )
+ {
+ pm->ps->stats[ STAT_MISC ] += pml.msec;
+ if( pm->ps->stats[ STAT_MISC ] >= LCANNON_CHARGE_TIME_MAX )
+ pm->ps->stats[ STAT_MISC ] = LCANNON_CHARGE_TIME_MAX;
+ if( pm->ps->stats[ STAT_MISC ] > pm->ps->ammo * LCANNON_CHARGE_TIME_MAX /
+ LCANNON_CHARGE_AMMO )
+ pm->ps->stats[ STAT_MISC ] = pm->ps->ammo * LCANNON_CHARGE_TIME_MAX /
+ LCANNON_CHARGE_AMMO;
+ }
+
+ // Set overcharging flag so other players can hear the warning beep
+ if( pm->ps->stats[ STAT_MISC ] > LCANNON_CHARGE_TIME_WARN )
+ pm->ps->eFlags |= EF_WARN_CHARGE;
+ }
// don't allow attack until all buttons are up
if( pm->ps->pm_flags & PMF_RESPAWNED )
return;
- // ignore if spectator
- if( pm->ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
- return;
-
- if( pm->ps->stats[ STAT_STATE ] & SS_INFESTING )
- return;
-
- if( pm->ps->stats[ STAT_STATE ] & SS_HOVELING )
- return;
-
+ // no slash during charge
if( pm->ps->stats[ STAT_STATE ] & SS_CHARGING )
return;
- // check for dead player
- if( pm->ps->stats[ STAT_HEALTH ] <= 0 )
- {
- pm->ps->weapon = WP_NONE;
- 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 ) )
- {
+ && ( pm->cmd.buttons & BUTTON_ATTACK )
+ && ( pm->ps->pm_flags & PMF_CHARGE ) )
return;
- }
// make weapon function
if( pm->ps->weaponTime > 0 )
@@ -2853,15 +2919,10 @@ static void PM_Weapon( void )
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
// again if lowering or raising
- if( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING )
+ if( BG_PlayerCanChangeWeapon( pm->ps ) )
{
// must press use to switch weapons
if( pm->cmd.buttons & BUTTON_USE_HOLDABLE )
@@ -2902,10 +2963,6 @@ 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 )
{
@@ -2913,10 +2970,10 @@ static void PM_Weapon( void )
return;
}
+ // Set proper animation
if( pm->ps->weaponstate == WEAPON_RAISING )
{
pm->ps->weaponstate = WEAPON_READY;
-
if( !( pm->ps->persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
{
if( pm->ps->weapon == WP_BLASTER )
@@ -2928,15 +2985,22 @@ static void PM_Weapon( void )
return;
}
- // start the animation even if out of ammo
-
BG_FindAmmoForWeapon( pm->ps->weapon, NULL, &maxClips );
// check for out of ammo
if( !pm->ps->ammo && !pm->ps->clips && !BG_FindInfinteAmmoForWeapon( pm->ps->weapon ) )
{
- PM_AddEvent( EV_NOAMMO );
- pm->ps->weaponTime += 200;
+ if( ( pm->cmd.buttons & BUTTON_ATTACK ) ||
+ ( BG_WeaponHasAltMode( pm->ps->weapon ) &&
+ ( pm->cmd.buttons & BUTTON_ATTACK2 ) ) ||
+ ( BG_WeaponHasThirdMode( pm->ps->weapon ) &&
+ ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) ) )
+ {
+ PM_AddEvent( EV_NOAMMO );
+ pm->ps->weaponTime += 500;
+ }
+ else
+ pm->ps->weaponTime += 50;
if( pm->ps->weaponstate == WEAPON_FIRING )
pm->ps->weaponstate = WEAPON_READY;
@@ -3002,7 +3066,6 @@ static void PM_Weapon( void )
case WP_LUCIFER_CANNON:
attack1 = pm->cmd.buttons & BUTTON_ATTACK;
attack2 = pm->cmd.buttons & BUTTON_ATTACK2;
- attack3 = qfalse;
// Prevent firing of the Lucifer Cannon after an overcharge
if( pm->ps->weaponstate == WEAPON_NEEDS_RESET )
@@ -3012,28 +3075,26 @@ static void PM_Weapon( void )
pm->ps->weaponstate = WEAPON_READY;
}
+ // Can't fire secondary while primary is charging
if( attack1 || pm->ps->stats[ STAT_MISC ] > 0 )
attack2 = qfalse;
- if( ( attack1 || pm->ps->stats[ STAT_MISC ] == 0 ) && !attack2 && !attack3 )
+
+ if( ( attack1 || pm->ps->stats[ STAT_MISC ] == 0 ) && !attack2 )
{
- attack2 = qfalse;
+ pm->ps->weaponTime = 0;
- if( pm->ps->stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE )
+ // Charging
+ if( pm->ps->stats[ STAT_MISC ] < LCANNON_CHARGE_TIME_MAX )
{
- // Charging
- pm->ps->weaponTime = 0;
pm->ps->weaponstate = WEAPON_READY;
return;
}
- else
- {
- // Overcharge
- pm->ps->weaponTime = 0;
- pm->ps->weaponstate = WEAPON_NEEDS_RESET;
- }
+
+ // Overcharge
+ pm->ps->weaponstate = WEAPON_NEEDS_RESET;
}
- if( pm->ps->stats[ STAT_MISC ] > LCANNON_MIN_CHARGE )
+ if( pm->ps->stats[ STAT_MISC ] > LCANNON_CHARGE_TIME_MIN )
{
// Fire primary attack
attack1 = qtrue;
@@ -3091,7 +3152,6 @@ static void PM_Weapon( void )
//hacky special case for slowblob
if( pm->ps->weapon == WP_ALEVEL3_UPG && !pm->ps->ammo )
{
- PM_AddEvent( EV_NOAMMO );
pm->ps->weaponTime += 200;
return;
}
@@ -3180,8 +3240,7 @@ static void PM_Weapon( void )
if( pm->ps->weapon == WP_ALEVEL4 )
{
//hack to get random attack animations
- //FIXME: does pm->ps->weaponTime cycle enough?
- int num = abs( pm->ps->weaponTime ) % 3;
+ int num = abs( pm->ps->commandTime ) % 3;
if( num == 0 )
PM_ForceLegsAnim( NSPA_ATTACK1 );
@@ -3209,17 +3268,16 @@ static void PM_Weapon( void )
// take an ammo away if not infinite
if( !BG_FindInfinteAmmoForWeapon( pm->ps->weapon ) )
{
- //special case for lcannon
+ // Special case for lcannon
if( pm->ps->weapon == WP_LUCIFER_CANNON && attack1 && !attack2 )
- {
- pm->ps->ammo -= (int)( ceil( ( (float)pm->ps->stats[ STAT_MISC ] / (float)LCANNON_TOTAL_CHARGE ) * 10.0f ) );
-
- //stay on the safe side
- if( pm->ps->ammo < 0 )
- pm->ps->ammo = 0;
- }
+ pm->ps->ammo -= ( pm->ps->stats[ STAT_MISC ] * LCANNON_CHARGE_AMMO +
+ LCANNON_CHARGE_TIME_MAX - 1 ) / LCANNON_CHARGE_TIME_MAX;
else
pm->ps->ammo--;
+
+ // Stay on the safe side
+ if( pm->ps->ammo < 0 )
+ pm->ps->ammo = 0;
}
else if( pm->ps->weapon == WP_ALEVEL3_UPG && attack3 )
{
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index e3ffc434..54101188 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -217,7 +217,7 @@ typedef enum
STAT_STAMINA, // stamina (human only)
STAT_STATE, // client states e.g. wall climbing
STAT_MISC, // for uh...misc stuff (pounce, trample, lcannon)
- STAT_MISC2, // more uh...misc stuff (booster, lcannon repeat)
+ STAT_UNUSED, // *** UNUSED ***
STAT_BUILDABLE, // which ghost model to display for building
STAT_FALLDIST, // the distance the player fell
STAT_VIEWLOCK // direction to lock the view in
@@ -1130,6 +1130,7 @@ void BG_PositionBuildableRelativeToPlayer( const playerState_t *ps,
const vec3_t, const vec3_t, int, int ),
vec3_t outOrigin, vec3_t outAngles, trace_t *tr );
int BG_GetValueOfPlayer( playerState_t *ps );
+qboolean BG_PlayerCanChangeWeapon( playerState_t *ps );
int BG_FindValueOfBuildable( int bclass );
int BG_FindBuildNumForName( char *name );
@@ -1316,7 +1317,6 @@ qboolean BG_WeaponIsAllowed( weapon_t weapon );
qboolean BG_UpgradeIsAllowed( upgrade_t upgrade );
qboolean BG_ClassIsAllowed( pClass_t class );
qboolean BG_BuildableIsAllowed( buildable_t buildable );
-qboolean BG_UpgradeClassAvailable( playerState_t *ps );
typedef struct
{
diff --git a/src/game/g_active.c b/src/game/g_active.c
index f232781b..74cb28e2 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -613,84 +613,6 @@ void ClientTimerActions( gentity_t *ent, int msec )
else if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
- //client is charging up for a... charge
- if( client->ps.weapon == WP_ALEVEL4 )
- {
- 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;
-
- if( ucmd->forwardmove > 0 )
- {
- //trigger charge sound...is quite annoying
- //if( client->ps.stats[ STAT_MISC ] <= 0 )
- // G_AddEvent( ent, EV_LEV4_TRAMPLE_PREPARE, 0 );
-
- client->ps.stats[ STAT_MISC ] += 100 * LEVEL4_TRAMPLE_CHARGE_RATE;
-
- }
- else
- client->ps.stats[ STAT_MISC ] = 0;
-
- }
-
- if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging ||
- client->ps.stats[ STAT_MISC ] >= LEVEL4_TRAMPLE_CHARGE_TRIGGER )
- {
- if( client->ps.stats[ STAT_MISC ] > LEVEL4_TRAMPLE_CHARGE_MIN )
- {
- client->ps.stats[ STAT_MISC ] -= 100 * LEVEL4_TRAMPLE_DISCHARGE_RATE;
-
- if( client->charging == qfalse )
- G_AddEvent( ent, EV_LEV4_TRAMPLE_START, 0 );
-
- client->charging = qtrue;
- client->ps.stats[ STAT_STATE ] |= SS_CHARGING;
-
- //if the charger has stopped moving take a chunk of charge away
- if( VectorLength( client->ps.velocity ) < 64.0f || strafing )
- client->ps.stats[ STAT_MISC ] = client->ps.stats[ STAT_MISC ] / 2;
-
- //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;
-
-
- if( client->ps.stats[ STAT_MISC ] <= 0 )
- {
- client->ps.stats[ STAT_MISC ] = 0;
- client->charging = qfalse;
- client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING;
- }
- }
- }
-
- //client is charging up an lcannon
- if( client->ps.weapon == WP_LUCIFER_CANNON &&
- ( ucmd->buttons & BUTTON_ATTACK ) &&
- client->ps.stats[ STAT_MISC2 ] <= 0 &&
- client->ps.weaponstate != WEAPON_NEEDS_RESET )
- {
- 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 )
- client->ps.stats[ STAT_MISC ] = LCANNON_TOTAL_CHARGE;
-
- if( client->ps.stats[ STAT_MISC ] > ( client->ps.ammo * LCANNON_TOTAL_CHARGE ) / 10 )
- 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 ) )
{
@@ -1477,20 +1399,20 @@ void ClientThink_real( gentity_t *ent )
else
client->ps.pm_type = PM_NORMAL;
- if( client->ps.stats[ STAT_STATE ] & SS_GRABBED &&
+ if( ( client->ps.stats[ STAT_STATE ] & SS_GRABBED ) &&
client->grabExpiryTime < level.time )
client->ps.stats[ STAT_STATE ] &= ~SS_GRABBED;
- if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED &&
+ if( ( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ) &&
client->lastLockTime + LOCKBLOB_LOCKTIME < level.time )
client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED;
- if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED &&
+ if( ( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED ) &&
client->lastSlowTime + ABUILDER_BLOB_TIME < level.time )
client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;
- if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED &&
- client->ps.stats[ STAT_MISC2 ] <= 0 )
+ if( ( client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) &&
+ level.time - client->boostedTime >= BOOST_TIME )
client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED;
if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED )
@@ -1891,11 +1813,10 @@ void SpectatorClientEndFrame( gentity_t *ent )
( ent->client->ps.eFlags & ( EF_VOTED | EF_TEAMVOTED ) );
ent->client->ps = cl->ps;
ent->client->ps.eFlags = flags;
- ent->client->ps.pm_flags |= PMF_FOLLOW;
}
- else
- ent->client->ps.pm_flags &= ~PMF_FOLLOW;
+
ent->client->ps.clientNum = clientNum;
+ ent->client->ps.pm_flags |= PMF_FOLLOW;
ent->client->ps.pm_flags &= ~PMF_QUEUED;
}
}
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index 1a532ca1..1ef65f43 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -1467,7 +1467,7 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
return;
client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- client->ps.stats[ STAT_MISC2 ] = BOOST_TIME;
+ client->boostedTime = level.time;
}
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 88d11646..56f8e8eb 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -367,7 +367,7 @@ void Cmd_Give_f( gentity_t *ent )
if( Q_stricmp( name, "poison" ) == 0 )
{
ent->client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- ent->client->ps.stats[ STAT_MISC2 ] = BOOST_TIME;
+ ent->client->boostedTime = level.time;
}
if( give_all || Q_stricmp( name, "ammo" ) == 0 )
@@ -1830,13 +1830,14 @@ void Cmd_ActivateItem_f( gentity_t *ent )
{
char s[ MAX_TOKEN_CHARS ];
int upgrade, weapon;
-
+
trap_Argv( 1, s, sizeof( s ) );
// "weapon" aliased to whatever weapon you have
if( !Q_stricmp( "weapon", s ) )
{
- if( ent->client->ps.weapon == WP_BLASTER )
+ if( ent->client->ps.weapon == WP_BLASTER &&
+ BG_PlayerCanChangeWeapon( &ent->client->ps ) )
G_ForceWeaponChange( ent, WP_NONE );
return;
}
@@ -1849,7 +1850,8 @@ void Cmd_ActivateItem_f( gentity_t *ent )
else if( weapon != WP_NONE &&
BG_InventoryContainsWeapon( weapon, ent->client->ps.stats ) )
{
- if( ent->client->ps.weapon != weapon )
+ if( ent->client->ps.weapon != weapon &&
+ BG_PlayerCanChangeWeapon( &ent->client->ps ) )
G_ForceWeaponChange( ent, weapon );
}
else
@@ -1895,9 +1897,11 @@ void Cmd_ToggleItem_f( gentity_t *ent )
if( weapon != WP_NONE )
{
+ if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
+ return;
+
//special case to allow switching between
//the blaster and the primary weapon
-
if( ent->client->ps.weapon != WP_BLASTER )
weapon = WP_BLASTER;
else
@@ -2037,6 +2041,9 @@ void Cmd_Buy_f( gentity_t *ent )
trap_SendServerCommand( ent-g_entities, va( "print \"You can't buy this item\n\"" ) );
return;
}
+
+ if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
+ return;
//add to inventory
BG_AddWeaponToInventory( weapon, ent->client->ps.stats );
@@ -2164,6 +2171,9 @@ void Cmd_Sell_f( gentity_t *ent )
if( weapon != WP_NONE )
{
+ if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
+ return;
+
//are we /allowed/ to sell this?
if( !BG_FindPurchasableForWeapon( weapon ) )
{
@@ -2229,6 +2239,9 @@ void Cmd_Sell_f( gentity_t *ent )
}
else if( !Q_stricmp( s, "weapons" ) )
{
+ if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
+ return;
+
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
{
//guard against selling the HBUILD weapons exploit
@@ -2502,8 +2515,10 @@ void G_FollowLockView( gentity_t *ent )
vec3_t spawn_origin, spawn_angles;
int clientNum;
+ clientNum = ent->client->sess.spectatorClient;
ent->client->sess.sessionTeam = TEAM_SPECTATOR;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
+ ent->client->ps.clientNum = clientNum;
ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
ent->client->ps.stats[ STAT_PTEAM ] = ent->client->pers.teamSelection;
@@ -2512,7 +2527,6 @@ void G_FollowLockView( gentity_t *ent )
ent->client->ps.stats[ STAT_VIEWLOCK ] = 0;
ent->client->ps.eFlags &= ~EF_WALLCLIMB;
ent->client->ps.viewangles[ PITCH ] = 0.0f;
- clientNum = ent->client->ps.clientNum = ent->client->sess.spectatorClient;
// Put the view at the team spectator lock position
if( level.clients[ clientNum ].pers.teamSelection == PTE_ALIENS )
@@ -2595,6 +2609,11 @@ qboolean G_FollowNewClient( gentity_t *ent, int dir )
// this is good, we can use it
ent->client->sess.spectatorClient = clientnum;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
+
+ // if this client is in the spawn queue, we need to do something special
+ if( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR )
+ G_FollowLockView( ent );
+
return qtrue;
} while( clientnum != original );
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 0305c3c9..0c2e6d7d 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -423,6 +423,7 @@ struct gclient_s
int inactivityTime; // kick players when time > this
qboolean inactivityWarning;// qtrue if the five seoond warning has been given
int rewardTime; // clear the EF_AWARD_IMPRESSIVE, etc when time > this
+ int boostedTime; // last time we touched a booster
int airOutTime;
diff --git a/src/game/g_missile.c b/src/game/g_missile.c
index 2991ec48..4d5a751a 100644
--- a/src/game/g_missile.c
+++ b/src/game/g_missile.c
@@ -438,15 +438,13 @@ 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 /
- (float)LCANNON_TOTAL_CHARGE ) * (float)LCANNON_DAMAGE ) );
VectorNormalize( dir );
bolt = G_Spawn( );
bolt->classname = "lcannon";
- if( damage == LCANNON_TOTAL_CHARGE )
+ if( damage == LCANNON_DAMAGE )
bolt->nextthink = level.time;
else
bolt->nextthink = level.time + 10000;
@@ -458,8 +456,8 @@ gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir,
bolt->s.generic1 = self->s.generic1; //weaponMode
bolt->r.ownerNum = self->s.number;
bolt->parent = self;
- bolt->damage = localDamage;
- bolt->splashDamage = localDamage / 2;
+ bolt->damage = damage;
+ bolt->splashDamage = damage / 2;
bolt->splashRadius = radius;
bolt->methodOfDeath = MOD_LCANNON;
bolt->splashMethodOfDeath = MOD_LCANNON_SPLASH;
@@ -467,7 +465,10 @@ gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir,
bolt->target_ent = NULL;
// Pass the missile charge through
- bolt->s.torsoAnim = damage;
+ bolt->s.torsoAnim = ( damage - LCANNON_SECONDARY_DAMAGE ) *
+ 255 / LCANNON_DAMAGE;
+ if( bolt->s.torsoAnim < 0 )
+ bolt->s.torsoAnim = 0;
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index fc915d86..f5a118fa 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -772,18 +772,13 @@ void LCChargeFire( gentity_t *ent, qboolean secondary )
gentity_t *m;
if( secondary && ent->client->ps.stats[ STAT_MISC ] <= 0 )
- {
m = fire_luciferCannon( ent, muzzle, forward, LCANNON_SECONDARY_DAMAGE,
- LCANNON_SECONDARY_RADIUS, LCANNON_SECONDARY_SPEED );
- ent->client->ps.stats[ STAT_MISC2 ] = LCANNON_REPEAT;
- }
+ LCANNON_SECONDARY_RADIUS, LCANNON_SECONDARY_SPEED );
else
- {
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 ] *
+ LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX,
+ LCANNON_RADIUS, LCANNON_SPEED );
ent->client->ps.stats[ STAT_MISC ] = 0;
}
@@ -1360,7 +1355,8 @@ void G_ChargeAttack( gentity_t *ent, gentity_t *victim )
int damage;
vec3_t forward, normal;
- if( ent->client->ps.stats[ STAT_MISC ] <= 0 || !ent->client->charging )
+ if( ent->client->ps.stats[ STAT_MISC ] <= 0 ||
+ !( ent->client->ps.stats[ STAT_STATE ] & SS_CHARGING ) )
return;
VectorSubtract( victim->s.origin, ent->s.origin, forward );
@@ -1372,11 +1368,11 @@ void G_ChargeAttack( gentity_t *ent, gentity_t *victim )
WideBloodSpurt( ent, victim, NULL );
- damage = (int)( ( (float)ent->client->ps.stats[ STAT_MISC ] /
- (float)LEVEL4_TRAMPLE_CHARGE_MAX ) * LEVEL4_TRAMPLE_DMG );
+ damage = LEVEL4_TRAMPLE_DMG * ent->client->ps.stats[ STAT_MISC ] /
+ LEVEL4_TRAMPLE_CHARGE_MAX;
G_Damage( victim, ent, ent, forward, victim->s.origin, damage,
- 0, MOD_LEVEL4_TRAMPLE );
+ 0, MOD_LEVEL4_TRAMPLE );
if( !victim->client )
ent->client->ps.stats[ STAT_MISC ] = 0;
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index 3bbac18f..585abe5d 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -115,24 +115,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LEVEL4_CLAW_K_SCALE 1.0f
#define LEVEL4_REGEN_RANGE 200.0f
+#define LEVEL4_TRAMPLE_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_CHARGE_TIME_MIN 375
+#define LEVEL4_TRAMPLE_CHARGE_TIME_MAX 1000
#define LEVEL4_TRAMPLE_DURATION 3000
-#define LEVEL4_TRAMPLE_DMG ADM(110)
+#define LEVEL4_TRAMPLE_STOP_PENALTY 1 // msec of charge lost when stopped
+
+#define LEVEL4_TRAMPLE_CHARGE_MIN ( LEVEL4_TRAMPLE_CHARGE_TIME_MIN * \
+ LEVEL4_TRAMPLE_DURATION / \
+ LEVEL4_TRAMPLE_CHARGE_TIME_MAX )
+#define LEVEL4_TRAMPLE_CHARGE_MAX LEVEL4_TRAMPLE_DURATION
-#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_PER_V 0.5f // damage per falling velocity
#define LEVEL4_CRUSH_DAMAGE 120 // to players only
#define LEVEL4_CRUSH_REPEAT 500 // player damage repeat
@@ -471,9 +466,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#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 1
+#define LCANNON_CHARGE_TIME_MAX 3000
+#define LCANNON_CHARGE_TIME_MIN 100
+#define LCANNON_CHARGE_TIME_WARN 2000
+#define LCANNON_CHARGE_AMMO 10 // ammo cost of a full charge shot
#define HBUILD_PRICE 0
#define HBUILD_REPEAT 1000