summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2015-06-29 00:52:09 +0200
committerPaweł Redman <pawel.redman@gmail.com>2015-06-29 16:40:28 +0200
commit0918eb3bcdefd0d6ee4bd573b761454c99f032f6 (patch)
treedd1bd0bfe5348ad3b4860f11b7d3dcbb6db9ca3c /src
parent93d2e4414ee25e417ee0d2ca0248448002ea4d5b (diff)
Initial implementation of new grenades.
Diffstat (limited to 'src')
-rw-r--r--src/cgame/cg_draw.c52
-rw-r--r--src/cgame/cg_event.c8
-rw-r--r--src/cgame/cg_local.h3
-rw-r--r--src/cgame/cg_main.c6
-rw-r--r--src/cgame/cg_weapons.c36
-rw-r--r--src/game/bg_misc.c42
-rw-r--r--src/game/bg_pmove.c135
-rw-r--r--src/game/bg_public.h12
-rw-r--r--src/game/g_active.c9
-rw-r--r--src/game/g_client.c2
-rw-r--r--src/game/g_cmds.c81
-rw-r--r--src/game/g_combat.c15
-rw-r--r--src/game/g_local.h3
-rw-r--r--src/game/g_missile.c4
-rw-r--r--src/game/g_team.c2
-rw-r--r--src/game/g_weapon.c26
-rw-r--r--src/game/tremulous.h8
-rw-r--r--src/ui/ui_local.h1
-rw-r--r--src/ui/ui_main.c31
19 files changed, 384 insertions, 92 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 0ee4042..4e2ba77 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -315,7 +315,7 @@ static void CG_DrawPlayerCreditsValue( rectDef_t *rect, vec4_t color, qboolean p
ps = &cg.snap->ps;
//if the build timer pie is showing don't show this
- if( ( cent->currentState.weapon == WP_ABUILD ) && ps->stats[ STAT_MISC ] )
+ if( ( cent->currentState.weapon == WP_ABUILD ) && ps->stats[ STAT_BUILD_TIMER ] )
return;
value = ps->persistant[ PERS_CREDIT ];
@@ -454,7 +454,7 @@ static void CG_DrawPlayerClipsRing( rectDef_t *rect, vec4_t backColor,
{
playerState_t *ps = &cg.snap->ps;
centity_t *cent;
- float buildTime = ps->stats[ STAT_MISC ];
+ float buildTime = ps->stats[ STAT_BUILD_TIMER ];
float progress;
float maxDelay;
weapon_t weapon;
@@ -502,7 +502,7 @@ static void CG_DrawPlayerBuildTimerRing( rectDef_t *rect, vec4_t backColor,
{
playerState_t *ps = &cg.snap->ps;
// centity_t *cent;
- float buildTime = ps->stats[ STAT_MISC ];
+ float buildTime = ps->stats[ STAT_BUILD_TIMER ];
float progress;
vec4_t color;
@@ -764,7 +764,11 @@ static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color )
int valueMarked = -1;
qboolean bp = qfalse;
- switch( BG_PrimaryWeapon( cg.snap->ps.stats ) )
+ if( cg.snap->ps.weapon == WP_GRENADE )
+ {
+ value = cg.snap->ps.stats[ STAT_GRENADES ];
+ }
+ else switch( BG_PrimaryWeapon( cg.snap->ps.stats ) )
{
case WP_NONE:
case WP_BLASTER:
@@ -932,7 +936,7 @@ static void CG_DrawPlayerBuildTimer( rectDef_t *rect, vec4_t color )
ps = &cg.snap->ps;
- if( ps->stats[ STAT_MISC ] <= 0 )
+ if( ps->stats[ STAT_BUILD_TIMER ] <= 0 )
return;
switch( BG_PrimaryWeapon( ps->stats ) )
@@ -945,7 +949,7 @@ static void CG_DrawPlayerBuildTimer( rectDef_t *rect, vec4_t color )
return;
}
- index = 8 * ( ps->stats[ STAT_MISC ] - 1 ) / MAXIMUM_BUILD_TIME;
+ index = 8 * ( ps->stats[ STAT_BUILD_TIMER ] - 1 ) / MAXIMUM_BUILD_TIME;
if( index > 7 )
index = 7;
else if( index < 0 )
@@ -970,6 +974,10 @@ static void CG_DrawPlayerClipsValue( rectDef_t *rect, vec4_t color )
int value;
playerState_t *ps = &cg.snap->ps;
+ if( cg.snap->ps.weapon == WP_GRENADE )
+ {
+ return;
+ }
switch( BG_PrimaryWeapon( ps->stats ) )
{
case WP_NONE:
@@ -1106,12 +1114,16 @@ static float CG_ChargeProgress( void )
min = 0;
max = LEVEL1_WARP_TIME;
}
-
else if( cg.snap->ps.weapon == WP_LUCIFER_CANNON || cg.snap->ps.weapon == WP_FLAMER )
{
min = LCANNON_CHARGE_TIME_MIN;
max = LCANNON_CHARGE_TIME_MAX;
}
+ else if( cg.snap->ps.weapon == WP_GRENADE )
+ {
+ min = 0;
+ max = GRENADE_FUSE_TIME;
+ }
if( max - min <= 0.0f )
return 0.0f;
@@ -1186,14 +1198,17 @@ static void CG_DrawPlayerChargeBar( rectDef_t *rect, vec4_t ref_color,
color[ 3 ] = ref_color[ 3 ] * cg.chargeMeterAlpha;
// Flash red for Lucifer Cannon warning
- if( (cg.snap->ps.weapon == WP_FLAMER ) &&
-
- 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;
+ if( cg.time & 128 )
+ {
+ if( ( cg.snap->ps.weapon == WP_LUCIFER_CANNON &&
+ cg.snap->ps.stats[ STAT_MISC ] >= LCANNON_CHARGE_TIME_WARN ) ||
+ ( cg.snap->ps.weapon == WP_GRENADE &&
+ cg.snap->ps.stats[ STAT_MISC ] >= GRENADE_TIME_WARN ) )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = 0.0f;
+ color[ 2 ] = 0.0f;
+ }
}
x = rect->x;
@@ -2745,7 +2760,12 @@ void CG_DrawWeaponIcon( rectDef_t *rect, vec4_t color )
return;
}
- if( ps->clips == 0 && !BG_Weapon( weapon )->infiniteAmmo )
+ if( weapon == WP_GRENADE && ps->stats[ STAT_GRENADES ] <= 0 )
+ {
+ color[ 0 ] = 1.0f;
+ color[ 1 ] = color[ 2 ] = 0.0f;
+ }
+ else if( weapon != WP_GRENADE && ps->clips == 0 && !BG_Weapon( weapon )->infiniteAmmo )
{
float ammoPercent = (float)ps->ammo / (float)maxAmmo;
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index eaf925e..b3a8396 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -1007,6 +1007,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
}
break;
+ case EV_GRENADE_PRIME:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.grenadePrimeSound );
+ break;
+
+ case EV_GRENADE_TICK:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.grenadeTickSound );
+ break;
+
//
// missile impacts
//
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 4642f64..7721e45 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1367,6 +1367,9 @@ typedef struct
qhandle_t warpParticle;
qhandle_t brightenShaders[ NUM_BRIGHTEN_SHADERS ];
+
+ sfxHandle_t grenadePrimeSound;
+ sfxHandle_t grenadeTickSound;
} cgMedia_t;
typedef struct
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index 158c0b5..2761922 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -461,7 +461,8 @@ static void CG_SetUIVars( void )
BG_Upgrade( i )->purchasable )
strcat( carriageCvar, va( "U%d ", i ) );
}
- strcat( carriageCvar, "$" );
+
+ strcat( carriageCvar, va( "G%d $", cg.snap->ps.stats[ STAT_GRENADES ] ) );
trap_Cvar_Set( "ui_carriage", carriageCvar );
@@ -777,6 +778,9 @@ static void CG_RegisterSounds( void )
cgs.media.warpEnterSound = trap_S_RegisterSound( "sound/player/level1/warp1.wav", qfalse );
cgs.media.warpExitSound = trap_S_RegisterSound( "sound/player/level1/warp2.wav", qfalse );
cgs.media.warpingSound = trap_S_RegisterSound( "sound/player/level1/warping.wav", qfalse );
+
+ cgs.media.grenadePrimeSound = trap_S_RegisterSound( "sound/weapons/grenade/prime.wav", qfalse );
+ cgs.media.grenadeTickSound = trap_S_RegisterSound( "sound/weapons/grenade/tick.wav", qfalse );
}
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index 4f2ef13..7fb68c9 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -1401,6 +1401,32 @@ void CG_AddViewWeapon( playerState_t *ps )
hand.origin );
}
+ // Grenade animations
+ // too tired to do this the right way...
+ if( weapon == WP_GRENADE )
+ {
+ float dx = 8.0f, dy = -5.0f, dz = -5.0f;
+
+ if( ps->stats[ STAT_GRENADES ] <= 0 )
+ {
+ return;
+ }
+
+ if( ps->stats[ STAT_MISC ] > 0 )
+ {
+ dx -= 4.0f * ps->stats[ STAT_MISC ] / 100;
+ dz += 2.0f * ps->stats[ STAT_MISC ] / 100;
+ }
+ else if( ps->weaponTime > 0 )
+ {
+ dz -= 0.015f * ps->weaponTime;
+ }
+
+ VectorMA( hand.origin, dx, cg.refdef.viewaxis[ 0 ], hand.origin );
+ VectorMA( hand.origin, dy, cg.refdef.viewaxis[ 1 ], hand.origin );
+ VectorMA( hand.origin, dz, cg.refdef.viewaxis[ 2 ], hand.origin );
+ }
+
AnglesToAxis( angles, hand.axis );
// map torso animations to weapon animations
@@ -1517,10 +1543,18 @@ void CG_DrawItemSelect( rectDef_t *rect, vec4_t color )
if( !BG_InventoryContainsWeapon( i, cg.snap->ps.stats ) )
continue;
- if( !ps->ammo && !ps->clips && !BG_Weapon( i )->infiniteAmmo )
+ if( i == WP_GRENADE && ps->stats[ STAT_GRENADES ] <= 0 )
+ {
+ colinfo[ numItems ] = 1;
+ }
+ else if( i != WP_GRENADE && !ps->ammo && !ps->clips && !BG_Weapon( i )->infiniteAmmo )
+ {
colinfo[ numItems ] = 1;
+ }
else
+ {
colinfo[ numItems ] = 0;
+ }
if( i == cg.weaponSelect )
selectedItem = numItems;
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index ff158bb..b5bd305 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -3190,8 +3190,7 @@ static const weaponAttributes_t bg_weapons[ ] =
qfalse, //qboolean longRanged;
TEAM_ALIENS //team_t team;
},
-
- {
+ {
WP_BLASTER, //int weaponNum;
0, //int price;
STAGE_GE_1, //int stages
@@ -3507,11 +3506,11 @@ static const weaponAttributes_t bg_weapons[ ] =
},
{
WP_GRENADE, //int weaponNum;
- GRENADE_PRICE, //int price;
- STAGE_GE_3, //int stages
- SLOT_NONE, //int slots;
+ 0, //int price;
+ STAGE_GE_1, //int stages
+ 0, //int slots;
"grenade", //char *weaponName;
- "Grenade", //char *humanName;
+ "[yenade]Grenades", //char *humanName;
"",
1, //int maxAmmo;
0, //int maxClips;
@@ -3838,7 +3837,7 @@ static const upgradeAttributes_t bg_upgrades[ ] =
STAGE_GE_4, //int stages
SLOT_NONE, //int slots;
"gren", //char *upgradeName;
- "[yenade]Explosive Grenade", //char *humanName;
+ "[yenade]Grenade", //char *humanName;
"A small incendinary device ideal for damaging tightly packed "
"alien structures. Has a five second timer.",
0,
@@ -4101,7 +4100,12 @@ char *eventnames[ ] =
"EV_MGTURRET_SPINUP", // trigger a sound
"EV_RPTUSE_SOUND", // trigger a sound
"EV_LEV2_ZAP",
- "EV_ACIDBOMB_BOUNCE"
+ "EV_ACIDBOMB_BOUNCE",
+ "EV_ROCKETL_PRIME",
+ "EV_WARP_ENTER",
+ "EV_WARP_EXIT",
+ "EV_GRENADE_PRIME",
+ "EV_GRENADE_TICK"
};
/*
@@ -4382,8 +4386,9 @@ Does the player hold a weapon?
*/
qboolean BG_InventoryContainsWeapon( int weapon, int stats[ ] )
{
- // humans always have a blaster
- if( stats[ STAT_TEAM ] == TEAM_HUMANS && weapon == WP_BLASTER )
+ // humans always have a blaster and a grenade thrower
+ if( stats[ STAT_TEAM ] == TEAM_HUMANS &&
+ ( weapon == WP_BLASTER || weapon == WP_GRENADE ) )
return qtrue;
return ( stats[ STAT_WEAPON ] == weapon );
@@ -4400,8 +4405,12 @@ int BG_SlotsForInventory( int stats[ ] )
int i, slot, slots;
slots = BG_Weapon( stats[ STAT_WEAPON ] )->slots;
+
if( stats[ STAT_TEAM ] == TEAM_HUMANS )
- slots |= BG_Weapon( WP_BLASTER )->slots;
+ {
+ slots |= BG_Weapon( WP_BLASTER )->slots |
+ BG_Weapon( WP_GRENADE )->slots;
+ }
for( i = UP_NONE; i < UP_NUM_UPGRADES; i++ )
{
@@ -4653,6 +4662,12 @@ qboolean BG_PlayerCanChangeWeapon( playerState_t *ps )
ps->stats[ STAT_MISC ] > LCANNON_CHARGE_TIME_MIN )
return qfalse;
+ if( ps->weapon == WP_GRENADE &&
+ ps->stats[ STAT_MISC ] > 0 )
+ {
+ return qfalse;
+ }
+
return ps->weaponTime <= 0 || ps->weaponstate != WEAPON_FIRING;
}
@@ -5101,6 +5116,11 @@ weapon_t BG_PrimaryWeapon( int stats[ ] )
if( BG_InventoryContainsWeapon( WP_BLASTER, stats ) )
return WP_BLASTER;
+ if( BG_InventoryContainsWeapon( WP_GRENADE, stats ) )
+ {
+ return WP_GRENADE;
+ }
+
return WP_NONE;
}
diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c
index 4fd73f1..f8f8a69 100644
--- a/src/game/bg_pmove.c
+++ b/src/game/bg_pmove.c
@@ -2861,9 +2861,10 @@ static void PM_BeginWeaponChange( int weapon )
if( pm->ps->weaponstate == WEAPON_DROPPING )
return;
- // prevent storing a primed rocket launcher
- if( pm->ps->weapon == WP_ROCKET_LAUNCHER &&
- pm->ps->stats[ STAT_MISC ] > 0 )
+ // prevent storing a primed rocket launcher or grenade
+ if( ( pm->ps->weapon == WP_ROCKET_LAUNCHER ||
+ pm->ps->weapon == WP_GRENADE ) &&
+ pm->ps->stats[ STAT_MISC ] > 0 )
return;
// cancel a reload
@@ -2932,9 +2933,17 @@ static void PM_TorsoAnimation( void )
if( !( pm->ps->persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
{
if( pm->ps->weapon == WP_BLASTER )
+ {
PM_ContinueTorsoAnim( TORSO_STAND2 );
+ }
+ else if( pm->ps->weapon == WP_GRENADE )
+ {
+ PM_ContinueTorsoAnim( TORSO_STAND3 );
+ }
else
+ {
PM_ContinueTorsoAnim( TORSO_STAND );
+ }
}
PM_ContinueWeaponAnim( WANIM_IDLE );
@@ -3104,6 +3113,32 @@ static void PM_Weapon( void )
}
}
+ if( pm->ps->weapon == WP_GRENADE )
+ {
+ if( !pm->ps->weaponTime &&
+ pm->ps->stats[ STAT_GRENADES ] > 0 &&
+ ( ( pm->cmd.buttons & BUTTON_ATTACK ) ||
+ pm->ps->stats[ STAT_MISC ] > 0 ) )
+ {
+ int old_time;
+
+ old_time = pm->ps->stats[ STAT_MISC ];
+
+ if( old_time == 0 )
+ {
+ PM_AddEvent( EV_GRENADE_PRIME );
+ }
+
+ pm->ps->stats[ STAT_MISC ] += pml.msec;
+
+ if( pm->ps->stats[ STAT_MISC ] < GRENADE_FUSE_TIME &&
+ old_time / 1000 < pm->ps->stats[ STAT_MISC ] / 1000 )
+ {
+ PM_AddEvent( EV_GRENADE_TICK );
+ }
+ }
+ }
+
// don't allow attack until all buttons are up
if( pm->ps->pm_flags & PMF_RESPAWNED )
return;
@@ -3200,7 +3235,9 @@ static void PM_Weapon( void )
else minAmmo = 1;
// check for out of ammo
- if( pm->ps->ammo < minAmmo && !pm->ps->clips && !BG_Weapon( pm->ps->weapon )->infiniteAmmo )
+ if( ( pm->ps->weapon != WP_GRENADE && pm->ps->ammo < minAmmo &&
+ !pm->ps->clips && !BG_Weapon( pm->ps->weapon )->infiniteAmmo ) ||
+ ( pm->ps->weapon == WP_GRENADE && pm->ps->stats[ STAT_GRENADES ] <= 0 ) )
{
if( attack1 || ( BG_Weapon( pm->ps->weapon )->hasAltMode && attack2 ) || ( BG_Weapon( pm->ps->weapon )->hasThirdMode && attack3 ) )
{
@@ -3345,6 +3382,36 @@ static void PM_Weapon( void )
return;
}
+ case WP_GRENADE:
+ if( attack1 || pm->ps->stats[ STAT_MISC ] == 0 )
+ {
+ pm->ps->weaponTime = 0;
+
+ if( pm->ps->stats[ STAT_MISC ] < GRENADE_FUSE_TIME )
+ {
+ pm->ps->weaponstate = WEAPON_READY;
+ return;
+ }
+ }
+
+ if( pm->ps->stats[ STAT_MISC ] > GRENADE_TIME_MIN )
+ {
+ attack1 = qtrue;
+ }
+ else if( pm->ps->stats[ STAT_MISC ] > 0 )
+ {
+ pm->ps->weaponTime = 0;
+ pm->ps->weaponstate = WEAPON_READY;
+ return;
+ }
+ else
+ {
+ pm->ps->weaponTime = 0;
+ pm->ps->weaponstate = WEAPON_READY;
+ return;
+ }
+ break;
+
default:
if( !attack1 && !attack2 && !attack3 )
{
@@ -3462,7 +3529,11 @@ static void PM_Weapon( void )
//FIXME: this should be an option in the client weapon.cfg
switch( pm->ps->weapon )
{
-
+ case WP_GRENADE:
+ PM_StartTorsoAnim( TORSO_ATTACK3 );
+ PM_StartWeaponAnim( WANIM_ATTACK1 );
+ break;
+
case WP_BLASTER:
PM_StartTorsoAnim( TORSO_ATTACK2 );
PM_StartWeaponAnim( WANIM_ATTACK1 );
@@ -3544,27 +3615,39 @@ static void PM_Weapon( void )
( pm->ps->weapon == WP_ALEVEL4 && attack3 )||
( pm->ps->weapon == WP_ALEVEL5 && attack3 ))
{
- switch( pm->ps->weapon ) {
- case WP_MASS_DRIVER:
- if( attack3 ) {
- pm->ps->ammo -= 7;
- if( pm->ps->ammo < 7 ) pm->ps->ammo += 1;
- } else pm->ps->ammo--;
- break;
- case WP_LUCIFER_CANNON:
- if( attack1 && !attack2 ) {
- pm->ps->ammo -= ( pm->ps->stats[ STAT_MISC ] * LCANNON_CHARGE_AMMO +
- LCANNON_CHARGE_TIME_MAX - 1 ) / LCANNON_CHARGE_TIME_MAX;
- } else pm->ps->ammo--;
- break;
- case WP_LAS_GUN:
- if( attack2 ) {
- pm->ps->ammo -= 25;
- } else pm->ps->ammo--;
- break;
- default:
- pm->ps->ammo--;
- break;
+ switch( pm->ps->weapon )
+ {
+ case WP_LUCIFER_CANNON:
+ if( attack1 && !attack2 )
+ {
+ pm->ps->ammo -=
+ ( pm->ps->stats[ STAT_MISC ] * LCANNON_CHARGE_AMMO +
+ LCANNON_CHARGE_TIME_MAX - 1 ) / LCANNON_CHARGE_TIME_MAX;
+ }
+ else
+ {
+ pm->ps->ammo--;
+ }
+ break;
+
+ case WP_LAS_GUN:
+ if( attack2 )
+ {
+ pm->ps->ammo -= 25;
+ }
+ else
+ {
+ pm->ps->ammo--;
+ }
+ break;
+
+ case WP_GRENADE:
+ pm->ps->stats[ STAT_GRENADES ]--;
+ break;
+
+ default:
+ pm->ps->ammo--;
+ break;
}
// Stay on the safe side
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index b0c1213..f377739 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -243,8 +243,10 @@ typedef enum
STAT_MISC, // for uh...misc stuff (pounce, trample, lcannon)
STAT_BUILDABLE, // which ghost model to display for building
STAT_FALLDIST, // the distance the player fell
- STAT_VIEWLOCK // direction to lock the view in
- // netcode has space for 3 more
+ STAT_VIEWLOCK, // direction to lock the view in
+ STAT_GRENADES,
+ STAT_BUILD_TIMER
+ // netcode has space for 1 more
} statIndex_t;
#define SCA_WALLCLIMBER 0x00000001
@@ -569,7 +571,9 @@ typedef enum
EV_ACIDBOMB_BOUNCE,
EV_ROCKETL_PRIME,
EV_WARP_ENTER,
- EV_WARP_EXIT
+ EV_WARP_EXIT,
+ EV_GRENADE_PRIME,
+ EV_GRENADE_TICK
} entity_event_t;
typedef enum
@@ -659,10 +663,12 @@ typedef enum
TORSO_GESTURE,
TORSO_ATTACK,
TORSO_ATTACK2,
+ TORSO_ATTACK3,
TORSO_DROP,
TORSO_RAISE,
TORSO_STAND,
TORSO_STAND2,
+ TORSO_STAND3,
LEGS_WALKCR,
LEGS_WALK,
LEGS_RUN,
diff --git a/src/game/g_active.c b/src/game/g_active.c
index b3df37b..c1a473d 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -579,6 +579,7 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
client->ps.speed = client->pers.flySpeed;
client->ps.stats[ STAT_STAMINA ] = 0;
client->ps.stats[ STAT_MISC ] = 0;
+ client->ps.stats[ STAT_BUILD_TIMER ] = 0;
client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
client->ps.stats[ STAT_CLASS ] = PCL_NONE;
client->ps.weapon = WP_NONE;
@@ -778,11 +779,11 @@ void ClientTimerActions( gentity_t *ent, int msec )
BG_InventoryContainsWeapon( WP_HBUILD, client->ps.stats ) )
{
// Update build timer
- if( client->ps.stats[ STAT_MISC ] > 0 )
- client->ps.stats[ STAT_MISC ] -= 100;
+ if( client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
+ client->ps.stats[ STAT_BUILD_TIMER ] -= 100;
- if( client->ps.stats[ STAT_MISC ] < 0 )
- client->ps.stats[ STAT_MISC ] = 0;
+ if( client->ps.stats[ STAT_BUILD_TIMER ] < 0 )
+ client->ps.stats[ STAT_BUILD_TIMER ] = 0;
}
switch( weapon )
diff --git a/src/game/g_client.c b/src/game/g_client.c
index ccf65e8..4fd2bb8 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -1554,7 +1554,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
{
spawn_angles[ YAW ] += 180.0f;
AngleNormalize360( spawn_angles[ YAW ] );
-
+
//
G_Sound( ent, CHAN_VOICE, G_SoundIndex( "sound/buildables/human/spawn1.wav" ) );
}
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 2b7b105..de0090b 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -449,7 +449,7 @@ void Cmd_Give_f( gentity_t *ent )
{
ADMP( "usage: give [what]\n" );
ADMP( "usage: valid choices are: all, health, funds [amount], stamina, "
- "ammo\n" );
+ "ammo, grenades\n" );
return;
}
@@ -488,18 +488,32 @@ void Cmd_Give_f( gentity_t *ent )
if( give_all || Q_stricmp( name, "stamina" ) == 0 )
ent->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
+ if( give_all || Q_stricmp( name, "grenades" ) == 0 )
+ {
+ ent->client->ps.stats[ STAT_GRENADES ] = GRENADE_MAX;
+ }
+
if( give_all || Q_stricmp( name, "ammo" ) == 0 )
{
gclient_t *client = ent->client;
+ int weapon;
if( client->ps.weapon != WP_ALEVEL3_UPG &&
BG_Weapon( client->ps.weapon )->infiniteAmmo )
return;
- client->ps.ammo = BG_Weapon( client->ps.weapon )->maxAmmo;
- client->ps.clips = BG_Weapon( client->ps.weapon )->maxClips;
+ weapon = BG_PrimaryWeapon( client->ps.stats );
+
+ if( weapon == WP_BLASTER ||
+ weapon == WP_GRENADE )
+ {
+ return;
+ }
+
+ client->ps.ammo = BG_Weapon( weapon )->maxAmmo;
+ client->ps.clips = BG_Weapon( weapon )->maxClips;
- if( BG_Weapon( client->ps.weapon )->usesEnergy &&
+ if( BG_Weapon( weapon )->usesEnergy &&
BG_InventoryContainsUpgrade( UP_BATTPACK, client->ps.stats ) )
client->ps.ammo = (int)( (float)client->ps.ammo * BATTPACK_MODIFIER );
}
@@ -1777,7 +1791,7 @@ void Cmd_Class_f( gentity_t *ent )
if( ent->client->sess.spectatorState == SPECTATOR_NOT &&
( currentClass == PCL_ALIEN_BUILDER0 ||
currentClass == PCL_ALIEN_BUILDER0_UPG ) &&
- ent->client->ps.stats[ STAT_MISC ] > 0 )
+ ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
{
G_TriggerMenu( ent->client->ps.clientNum, MN_A_EVOLVEBUILDTIMER );
return;
@@ -1925,7 +1939,7 @@ void Cmd_Destroy_f( gentity_t *ent )
( ent->client->pers.teamSelection == TEAM_HUMANS &&
!G_FindPower( traceEnt, qtrue ) ) )
{
- if( ent->client->ps.stats[ STAT_MISC ] > 0 )
+ if( ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
{
G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum );
return;
@@ -1950,7 +1964,7 @@ void Cmd_Destroy_f( gentity_t *ent )
{
if( !g_cheats.integer ) // add a bit to the build timer
{
- ent->client->ps.stats[ STAT_MISC ] +=
+ ent->client->ps.stats[ STAT_BUILD_TIMER ] +=
BG_Buildable( traceEnt->s.modelindex )->buildTime / 4;
}
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
@@ -1978,7 +1992,8 @@ void Cmd_ActivateItem_f( gentity_t *ent )
// "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 ||
+ ent->client->ps.weapon == WP_GRENADE ) &&
BG_PlayerCanChangeWeapon( &ent->client->ps ) )
G_ForceWeaponChange( ent, WP_NONE );
return;
@@ -1987,6 +2002,13 @@ void Cmd_ActivateItem_f( gentity_t *ent )
upgrade = BG_UpgradeByName( s )->number;
weapon = BG_WeaponByName( s )->number;
+ // for backward compatibility
+ if( upgrade == UP_GRENADE )
+ {
+ upgrade = UP_NONE;
+ weapon = WP_GRENADE;
+ }
+
if( upgrade != UP_NONE && BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
BG_ActivateUpgrade( upgrade, ent->client->ps.stats );
else if( weapon != WP_NONE &&
@@ -2045,7 +2067,8 @@ void Cmd_ToggleItem_f( gentity_t *ent )
//special case to allow switching between
//the blaster and the primary weapon
- if( ent->client->ps.weapon != WP_BLASTER )
+ if( ent->client->ps.weapon != WP_BLASTER &&
+ ent->client->ps.weapon != WP_GRENADE )
weapon = WP_BLASTER;
else
weapon = WP_NONE;
@@ -2211,6 +2234,12 @@ void Cmd_Buy_f( gentity_t *ent )
return;
}
+ if( upgrade == UP_GRENADE && ent->client->ps.stats[ STAT_GRENADES ] >= GRENADE_MAX )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"You can't carry any more grenades\n\"" );
+ return;
+ }
+
if( upgrade == UP_AMMO )
G_GiveClientMaxAmmo( ent, energyOnly );
else
@@ -2230,8 +2259,16 @@ void Cmd_Buy_f( gentity_t *ent )
ent->client->ps.eFlags ^= EF_TELEPORT_BIT;
}
- //add to inventory
- BG_AddUpgradeToInventory( upgrade, ent->client->ps.stats );
+ //UP_GRENADE isn't actually an upgrade, it's ammo for WP_GRENADE
+ if( upgrade == UP_GRENADE )
+ {
+ ent->client->ps.stats[ STAT_GRENADES ]++;
+ }
+ else
+ {
+ //add to inventory
+ BG_AddUpgradeToInventory( upgrade, ent->client->ps.stats );
+ }
}
if( upgrade == UP_BATTPACK )
@@ -2294,7 +2331,7 @@ void Cmd_Sell_f( gentity_t *ent )
if( BG_InventoryContainsWeapon( weapon, ent->client->ps.stats ) )
{
//guard against selling the HBUILD weapons exploit
- if( weapon == WP_HBUILD && ent->client->ps.stats[ STAT_MISC ] > 0 )
+ if( weapon == WP_HBUILD && ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
{
G_TriggerMenu( ent->client->ps.clientNum, MN_H_ARMOURYBUILDTIMER );
return;
@@ -2320,8 +2357,18 @@ void Cmd_Sell_f( gentity_t *ent )
trap_SendServerCommand( ent-g_entities, "print \"You can't sell this item\n\"" );
return;
}
+
+ //UP_GRENADE is not an actual upgrade, it's ammo for WP_GRENADE
+ if( upgrade == UP_GRENADE )
+ {
+ if( ent->client->ps.stats[ STAT_GRENADES ] > 0 )
+ {
+ ent->client->ps.stats[ STAT_GRENADES ]--;
+ G_AddCreditToClient( ent->client, (short)BG_Upgrade( upgrade )->price, qfalse );
+ }
+ }
//remove upgrade if carried
- if( BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
+ else if( BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
{
// shouldn't really need to test for this, but just to be safe
if( upgrade == UP_BATTLESUIT )
@@ -2413,6 +2460,14 @@ void Cmd_Sell_f( gentity_t *ent )
G_AddCreditToClient( ent->client, (short)BG_Upgrade( i )->price, qfalse );
}
}
+
+ if( ent->client->ps.stats[ STAT_GRENADES ] > 0 )
+ {
+ G_AddCreditToClient( ent->client,
+ ent->client->ps.stats[ STAT_GRENADES ] *
+ (short)BG_Upgrade( UP_GRENADE )->price, qfalse );
+ ent->client->ps.stats[ STAT_GRENADES ] = 0;
+ }
}
else
G_TriggerMenu( ent->client->ps.clientNum, MN_H_UNKNOWNITEM );
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index bfe0de9..397bde0 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -432,6 +432,21 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( level.intermissiontime )
return;
+ if( self->client->ps.weapon == WP_GRENADE &&
+ self->client->ps.stats[ STAT_MISC ] > 0 )
+ {
+ int fuse_left;
+
+ fuse_left = GRENADE_FUSE_TIME - self->client->ps.stats[ STAT_MISC ];
+
+ if( fuse_left < 0 )
+ {
+ fuse_left = 0;
+ }
+
+ launch_grenade( self, self->s.origin, vec3_origin, fuse_left );
+ }
+
self->client->ps.pm_type = PM_DEAD;
self->suicideTime = 0;
diff --git a/src/game/g_local.h b/src/game/g_local.h
index c1cde73..c968642 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -1011,8 +1011,7 @@ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *fire_bounceBall2( gentity_t *self, vec3_t start, vec3_t dir, int weapon, int dmg, int mod, int speed, int radius );
gentity_t *fire_bounceBall3( gentity_t *self, vec3_t start, vec3_t dir, int weapon, int dmg, int mod, int speed, int radius );
gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir );
-gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir );
-gentity_t *launch_grenade_flames( gentity_t *self, vec3_t start, vec3_t dir );
+gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir, int fuse_time );
gentity_t *launch_shield( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *launch_saw( gentity_t *self, vec3_t start, vec3_t dir );
gentity_t *fire_md2( gentity_t *self, vec3_t start, vec3_t dir );
diff --git a/src/game/g_missile.c b/src/game/g_missile.c
index 5dd9725..8d7a93c 100644
--- a/src/game/g_missile.c
+++ b/src/game/g_missile.c
@@ -695,7 +695,7 @@ gentity_t *fire_rocket( gentity_t *self, vec3_t start, vec3_t dir )
launch_grenade
=================
*/
-gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir )
+gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir, int fuse_time )
{
gentity_t *bolt;
@@ -703,7 +703,7 @@ gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir )
bolt = G_Spawn( );
bolt->classname = "grenade";
bolt->pointAgainstWorld = qfalse;
- bolt->nextthink = level.time + 5000;
+ bolt->nextthink = level.time + fuse_time;
bolt->think = G_ExplodeMissile;
bolt->s.eType = ET_MISSILE;
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
diff --git a/src/game/g_team.c b/src/game/g_team.c
index f92936f..ad0873c 100644
--- a/src/game/g_team.c
+++ b/src/game/g_team.c
@@ -302,7 +302,7 @@ void G_BalanceTeams( void )
// Refund all weapons and equipment before team change
for( i = WP_NONE+1; i < WP_NUM_WEAPONS; ++i )
{
- if ( i == WP_HBUILD && ent->client->ps.stats[ STAT_MISC ] > 0 )
+ if ( i == WP_HBUILD && ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
continue;
if (BG_InventoryContainsWeapon( i, ent->client->ps.stats ) && BG_Weapon( i )->purchasable )
{
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 0925973..265358a 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -80,7 +80,12 @@ void G_GiveClientMaxAmmo( gentity_t *ent, qboolean buyingEnergyAmmo )
for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
{
qboolean energyWeapon;
-
+
+ if( i == WP_GRENADE )
+ {
+ continue;
+ }
+
energyWeapon = BG_Weapon( i )->usesEnergy;
if( !BG_InventoryContainsWeapon( i, ent->client->ps.stats ) ||
BG_Weapon( i )->infiniteAmmo ||
@@ -626,9 +631,20 @@ GRENADE
void throwGrenade( gentity_t *ent )
{
+ int fuse_left;
+
G_CombatStats_Fire( ent, CSW_GRENADE, GRENADE_DAMAGE );
- launch_grenade( ent, muzzle, forward );
+ fuse_left = GRENADE_FUSE_TIME - ent->client->ps.stats[ STAT_MISC ];
+
+ if( fuse_left < 0 )
+ {
+ fuse_left = 0;
+ }
+
+ launch_grenade( ent, muzzle, forward, fuse_left );
+
+ ent->client->ps.stats[ STAT_MISC ] = 0;
}
/*
@@ -937,7 +953,7 @@ void CheckCkitRepair( gentity_t *ent )
int bHealth;
if( ent->client->ps.weaponTime > 0 ||
- ent->client->ps.stats[ STAT_MISC ] > 0 )
+ ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
return;
BG_GetClientViewOrigin( &ent->client->ps, viewOrigin );
@@ -997,7 +1013,7 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
if( buildable > BA_NONE )
{
- if( ent->client->ps.stats[ STAT_MISC ] > 0 )
+ if( ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 )
{
G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum );
return;
@@ -1007,7 +1023,7 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
{
if( !g_cheats.integer )
{
- ent->client->ps.stats[ STAT_MISC ] +=
+ ent->client->ps.stats[ STAT_BUILD_TIMER ] +=
BG_Buildable( buildable )->buildTime;
}
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index 3262b55..9a7a8c8 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -357,11 +357,15 @@ TREMULOUS EDGE MOD SRC FILE
#define PAINSAW_BLADERANGE 100.0f
#define GRENADE_PRICE 200
-#define GRENADE_REPEAT 0
+#define GRENADE_MAX 3
+#define GRENADE_REPEAT 500
#define GRENADE_K_SCALE 1.0f
-#define GRENADE_DAMAGE HDM(340)
+#define GRENADE_DAMAGE HDM(200)
#define GRENADE_RANGE 192.0f
#define GRENADE_SPEED 700.0f
+#define GRENADE_TIME_MIN 500
+#define GRENADE_TIME_WARN 1500
+#define GRENADE_FUSE_TIME 3000
#define SHOTGUN_PRICE 150
#define SHOTGUN_SHELLS 8
diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h
index 2c78e26..1d4303a 100644
--- a/src/ui/ui_local.h
+++ b/src/ui/ui_local.h
@@ -270,6 +270,7 @@ typedef struct
int weapons;
int upgrades;
+ int grenades;
serverStatus_t serverStatus;
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index 6487fae..dc5bc26 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -2353,6 +2353,17 @@ static void UI_ParseCarriageList( void )
uiInfo.upgrades |= ( 1 << i );
}
+ else if( iterator[ 0 ] == 'G' )
+ {
+ iterator++;
+
+ while( iterator[ 0 ] != ' ' )
+ *bufPointer++ = *iterator++;
+
+ *bufPointer++ = '\n';
+
+ uiInfo.grenades = atoi( buffer );
+ }
iterator++;
}
@@ -2444,7 +2455,7 @@ static void UI_LoadHumanArmourySells( void )
{
if( uiInfo.weapons & ( 1 << i ) )
{
- uiInfo.humanArmourySellList[ j ].text = BG_Weapon( i )->humanName;
+ uiInfo.humanArmourySellList[ j ].text =BG_Weapon( i )->humanName;
uiInfo.humanArmourySellList[ j ].cmd =
String_Alloc( va( "cmd sell %s\n", BG_Weapon( i )->name ) );
uiInfo.humanArmourySellList[ j ].type = INFOTYPE_WEAPON;
@@ -2458,9 +2469,18 @@ static void UI_LoadHumanArmourySells( void )
for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
{
- if( uiInfo.upgrades & ( 1 << i ) )
+ if( ( uiInfo.upgrades & ( 1 << i ) ) ||
+ ( i == UP_GRENADE && uiInfo.grenades ) )
{
- uiInfo.humanArmourySellList[ j ].text = BG_Upgrade( i )->humanName;
+ if( i == UP_GRENADE )
+ {
+ uiInfo.humanArmourySellList[ j ].text =
+ String_Alloc( va( "%s (%d)", BG_Upgrade( i )->humanName, uiInfo.grenades ) );
+ }
+ else
+ {
+ uiInfo.humanArmourySellList[ j ].text = BG_Upgrade( i )->humanName;
+ }
uiInfo.humanArmourySellList[ j ].cmd =
String_Alloc( va( "cmd sell %s\n", BG_Upgrade( i )->name ) );
uiInfo.humanArmourySellList[ j ].type = INFOTYPE_UPGRADE;
@@ -2482,10 +2502,13 @@ static void UI_ArmouryRefreshCb( void *data )
{
int oldWeapons = uiInfo.weapons;
int oldUpgrades = uiInfo.upgrades;
+ int oldGrenades = uiInfo.grenades;
UI_ParseCarriageList( );
- if( uiInfo.weapons != oldWeapons || uiInfo.upgrades != oldUpgrades )
+ if( uiInfo.weapons != oldWeapons ||
+ uiInfo.upgrades != oldUpgrades ||
+ uiInfo.grenades != oldGrenades )
{
UI_LoadHumanArmouryBuys( );
UI_LoadHumanArmourySells( );