From 0918eb3bcdefd0d6ee4bd573b761454c99f032f6 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Mon, 29 Jun 2015 00:52:09 +0200 Subject: Initial implementation of new grenades. --- src/cgame/cg_draw.c | 52 +++++++++++++------ src/cgame/cg_event.c | 8 +++ src/cgame/cg_local.h | 3 ++ src/cgame/cg_main.c | 6 ++- src/cgame/cg_weapons.c | 36 ++++++++++++- src/game/bg_misc.c | 42 +++++++++++---- src/game/bg_pmove.c | 135 +++++++++++++++++++++++++++++++++++++++---------- src/game/bg_public.h | 12 +++-- src/game/g_active.c | 9 ++-- src/game/g_client.c | 2 +- src/game/g_cmds.c | 81 ++++++++++++++++++++++++----- src/game/g_combat.c | 15 ++++++ src/game/g_local.h | 3 +- src/game/g_missile.c | 4 +- src/game/g_team.c | 2 +- src/game/g_weapon.c | 26 ++++++++-- src/game/tremulous.h | 8 ++- src/ui/ui_local.h | 1 + src/ui/ui_main.c | 31 ++++++++++-- 19 files changed, 384 insertions(+), 92 deletions(-) (limited to 'src') 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( ); -- cgit