summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cgame/cg_event.c5
-rw-r--r--src/cgame/cg_local.h1
-rw-r--r--src/cgame/cg_weapons.c177
-rw-r--r--src/game/bg_misc.c117
-rw-r--r--src/game/bg_public.h13
-rw-r--r--src/game/g_cmds.c51
-rw-r--r--src/game/g_weapon.c63
-rw-r--r--src/game/tremulous.h10
-rw-r--r--src/ui/ui_main.c8
9 files changed, 284 insertions, 161 deletions
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index 83d95424..4833346d 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -736,6 +736,11 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )
CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
break;
+ case EV_SHOTGUN:
+ DEBUGNAME( "EV_SHOTGUN" );
+ CG_ShotgunFire( es );
+ break;
+
case EV_GENERAL_SOUND:
DEBUGNAME( "EV_GENERAL_SOUND" );
if( cgs.gameSounds[ es->eventParm ] )
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index b9df770d..2dd28bf2 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1607,6 +1607,7 @@ void CG_MissileHitWall( weapon_t weapon, weaponMode_t weaponMode, int cli
vec3_t origin, vec3_t dir, impactSound_t soundType );
void CG_MissileHitPlayer( weapon_t weapon, weaponMode_t weaponMode, vec3_t origin, vec3_t dir, int entityNum );
void CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum );
+void CG_ShotgunFire( entityState_t *es );
void CG_TeslaTrail( vec3_t start, vec3_t end, int srcENum, int destENum );
void CG_AlienZap( vec3_t start, vec3_t end, int srcENum, int destENum );
diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c
index 4f5235b2..9af1a76b 100644
--- a/src/cgame/cg_weapons.c
+++ b/src/cgame/cg_weapons.c
@@ -90,6 +90,75 @@ static void CG_MachineGunEjectBrass( centity_t *cent )
/*
==========================
+CG_ShotgunEjectBrass
+==========================
+*/
+static void CG_ShotgunEjectBrass( centity_t *cent )
+{
+ localEntity_t *le;
+ refEntity_t *re;
+ vec3_t velocity, xvelocity;
+ vec3_t offset, xoffset;
+ vec3_t v[ 3 ];
+ float waterScale = 1.0f;
+
+ if( cg_brassTime.integer <= 0 )
+ return;
+
+ le = CG_AllocLocalEntity( );
+ re = &le->refEntity;
+
+ velocity[ 0 ] = 60 + 60 * crandom( );
+ velocity[ 1 ] = 40 + 10 * crandom( );
+ velocity[ 2 ] = 100 + 50 * crandom( );
+
+ le->leType = LE_FRAGMENT;
+ le->startTime = cg.time;
+ le->endTime = le->startTime + cg_brassTime.integer * 3 + cg_brassTime.integer * random( );
+
+ le->pos.trType = TR_GRAVITY;
+ le->pos.trTime = cg.time;
+
+ AnglesToAxis( cent->lerpAngles, v );
+
+ offset[ 0 ] = 8;
+ offset[ 1 ] = 0;
+ offset[ 2 ] = 24;
+
+ xoffset[ 0 ] = offset[ 0 ] * v[ 0 ][ 0 ] + offset[ 1 ] * v[ 1 ][ 0 ] + offset[ 2 ] * v[ 2 ][ 0 ];
+ xoffset[ 1 ] = offset[ 0 ] * v[ 0 ][ 1 ] + offset[ 1 ] * v[ 1 ][ 1 ] + offset[ 2 ] * v[ 2 ][ 1 ];
+ xoffset[ 2 ] = offset[ 0 ] * v[ 0 ][ 2 ] + offset[ 1 ] * v[ 1 ][ 2 ] + offset[ 2 ] * v[ 2 ][ 2 ];
+ VectorAdd( cent->lerpOrigin, xoffset, re->origin );
+ VectorCopy( re->origin, le->pos.trBase );
+
+ if( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER )
+ waterScale = 0.10f;
+
+ xvelocity[ 0 ] = velocity[ 0 ] * v[ 0 ][ 0 ] + velocity[ 1 ] * v[ 1 ][ 0 ] + velocity[ 2 ] * v[ 2 ][ 0 ];
+ xvelocity[ 1 ] = velocity[ 0 ] * v[ 0 ][ 1 ] + velocity[ 1 ] * v[ 1 ][ 1 ] + velocity[ 2 ] * v[ 2 ][ 1 ];
+ xvelocity[ 2 ] = velocity[ 0 ] * v[ 0 ][ 2 ] + velocity[ 1 ] * v[ 1 ][ 2 ] + velocity[ 2 ] * v[ 2 ][ 2 ];
+ VectorScale( xvelocity, waterScale, le->pos.trDelta );
+
+ AxisCopy( axisDefault, re->axis );
+ re->hModel = cgs.media.shotgunBrassModel;
+ le->bounceFactor = 0.3f;
+
+ le->angles.trType = TR_LINEAR;
+ le->angles.trTime = cg.time;
+ le->angles.trBase[ 0 ] = rand( )&31;
+ le->angles.trBase[ 1 ] = rand( )&31;
+ le->angles.trBase[ 2 ] = rand( )&31;
+ le->angles.trDelta[ 0 ] = 1;
+ le->angles.trDelta[ 1 ] = 0.5;
+ le->angles.trDelta[ 2 ] = 0;
+
+ le->leFlags = LEF_TUMBLE;
+ le->leBounceSoundType = LEBS_BRASS;
+ le->leMarkType = LEMT_NONE;
+}
+
+/*
+==========================
CG_TeslaTrail
==========================
*/
@@ -758,6 +827,10 @@ void CG_RegisterWeapon( int weaponNum )
case WP_CHAINGUN:
weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass;
break;
+
+ case WP_SHOTGUN:
+ weaponInfo->ejectBrassFunc = CG_ShotgunEjectBrass;
+ break;
}
}
@@ -1032,13 +1105,10 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent
CG_SetParticleSystemTag( cent->muzzlePS, gun, weapon->weaponModel, "tag_flash" );
}
- //FIXME: this leaves open the possibility for keep a persistent muzzle system going
- // by hopping between firing buttons -- currently nothing with a persistent
- // muzzle system has multiple fire modes however
//if the PS is infinite disable it when not firing
- if( !( cent->currentState.eFlags & EF_FIRING ) &&
- !( cent->currentState.eFlags & EF_FIRING2 ) &&
- !( cent->currentState.eFlags & EF_FIRING3 ) &&
+ if( ( ( !( cent->currentState.eFlags & EF_FIRING ) && weaponMode == WPM_PRIMARY ) ||
+ ( !( cent->currentState.eFlags & EF_FIRING2 ) && weaponMode == WPM_SECONDARY ) ||
+ ( !( cent->currentState.eFlags & EF_FIRING3 ) && weaponMode == WPM_TERTIARY ) ) &&
CG_IsParticleSystemInfinite( cent->muzzlePS ) )
{
CG_DestroyParticleSystem( cent->muzzlePS );
@@ -1845,7 +1915,6 @@ Renders bullet effects.
void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum )
{
trace_t trace;
- int sourceContentType, destContentType;
vec3_t start;
// if the shooter is currently valid, calc a source point and possibly
@@ -1854,26 +1923,8 @@ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh,
{
if( CG_CalcMuzzlePoint( sourceEntityNum, start ) )
{
- sourceContentType = trap_CM_PointContents( start, 0 );
- destContentType = trap_CM_PointContents( end, 0 );
-
- // do a complete bubble trail if necessary
- if( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) )
- CG_BubbleTrail( start, end, 32 );
+ CG_BubbleTrail( start, end, 32 );
- // bubble trail from water into air
- else if( ( sourceContentType & CONTENTS_WATER ) )
- {
- trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER );
- CG_BubbleTrail( start, trace.endpos, 32 );
- }
- // bubble trail from air into water
- else if( ( destContentType & CONTENTS_WATER ) )
- {
- trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER );
- CG_BubbleTrail( trace.endpos, end, 32 );
- }
-
// draw a tracer
if( random( ) < cg_tracerChance.value )
CG_Tracer( start, end );
@@ -1886,3 +1937,77 @@ void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh,
else
CG_MissileHitWall( WP_MACHINEGUN, WPM_PRIMARY, 0, end, normal, IMPACTSOUND_DEFAULT );
}
+
+/*
+============================================================================
+
+SHOTGUN TRACING
+
+============================================================================
+*/
+
+/*
+================
+CG_ShotgunPattern
+
+Perform the same traces the server did to locate the
+hit splashes
+================
+*/
+static void CG_ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, int otherEntNum )
+{
+ int i;
+ float r, u;
+ vec3_t end;
+ vec3_t forward, right, up;
+ trace_t tr;
+
+ // derive the right and up vectors from the forward vector, because
+ // the client won't have any other information
+ VectorNormalize2( origin2, forward );
+ PerpendicularVector( right, forward );
+ CrossProduct( forward, right, up );
+
+ // generate the "random" spread pattern
+ for( i = 0; i < SHOTGUN_PELLETS; i++ )
+ {
+ r = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
+ u = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
+ VectorMA( origin, 8192 * 16, forward, end );
+ VectorMA( end, r, right, end );
+ VectorMA( end, u, up, end );
+
+ CG_Trace( &tr, origin, NULL, NULL, end, otherEntNum, MASK_SHOT );
+ CG_BubbleTrail( origin, end, 32 );
+
+ if( !( tr.surfaceFlags & SURF_NOIMPACT ) )
+ {
+ if( cg_entities[ tr.entityNum ].currentState.eType == ET_PLAYER )
+ CG_MissileHitPlayer( WP_SHOTGUN, WPM_PRIMARY, tr.endpos, tr.plane.normal, tr.entityNum );
+ else if( tr.surfaceFlags & SURF_METALSTEPS )
+ CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_METAL );
+ else
+ CG_MissileHitWall( WP_SHOTGUN, WPM_PRIMARY, 0, tr.endpos, tr.plane.normal, IMPACTSOUND_DEFAULT );
+ }
+ }
+}
+
+/*
+==============
+CG_ShotgunFire
+==============
+*/
+void CG_ShotgunFire( entityState_t *es )
+{
+ vec3_t up;
+ vec3_t v;
+ int contents;
+
+ VectorSubtract( es->origin2, es->pos.trBase, v );
+ VectorNormalize( v );
+ VectorScale( v, 32, v );
+ VectorAdd( es->pos.trBase, v, v );
+
+ CG_ShotgunPattern( es->pos.trBase, es->origin2, es->eventParm, es->otherEntityNum );
+}
+
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 185d58b7..aea70b2b 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -2852,6 +2852,28 @@ weaponAttributes_t bg_weapons[ ] =
WUT_HUMANS //WUTeam_t team;
},
{
+ WP_SHOTGUN, //int weaponNum;
+ SHOTGUN_PRICE, //int price;
+ ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ SLOT_WEAPON, //int slots;
+ "shotgun", //char *weaponName;
+ "Shotgun", //char *weaponHumanName;
+ SHOTGUN_SHELLS, //int quan;
+ SHOTGUN_SPAWNCLIPS, //int clips;
+ SHOTGUN_MAXCLIPS, //int maxClips;
+ qfalse, //int infiniteAmmo;
+ qfalse, //int usesEnergy;
+ SHOTGUN_REPEAT, //int repeatRate1;
+ 0, //int repeatRate2;
+ 0, //int repeatRate3;
+ SHOTGUN_RELOAD, //int reloadTime;
+ qfalse, //qboolean hasAltMode;
+ qfalse, //qboolean hasThirdMode;
+ qtrue, //qboolean purchasable;
+ 0, //int buildDelay;
+ WUT_HUMANS //WUTeam_t team;
+ },
+ {
WP_FLAMER, //int weaponNum;
FLAMER_PRICE, //int price;
( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
@@ -3733,9 +3755,6 @@ upgradeAttributes_t bg_upgrades[ ] =
"larmour", //char *upgradeName;
"Light Armour", //char *upgradeHumanName;
"icons/iconu_larmour",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
@@ -3746,9 +3765,6 @@ upgradeAttributes_t bg_upgrades[ ] =
"helmet", //char *upgradeName;
"Helmet", //char *upgradeHumanName;
"icons/iconu_helmet",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
@@ -3759,9 +3775,6 @@ upgradeAttributes_t bg_upgrades[ ] =
"atoxin", //char *upgradeName;
"Anti-toxin", //char *upgradeHumanName;
"icons/iconu_atoxin",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
@@ -3772,9 +3785,6 @@ upgradeAttributes_t bg_upgrades[ ] =
"battpack", //char *upgradeName;
"Battery Pack", //char *upgradeHumanName;
"icons/iconu_battpack",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
@@ -3785,9 +3795,6 @@ upgradeAttributes_t bg_upgrades[ ] =
"jetpack", //char *upgradeName;
"Jet Pack", //char *upgradeHumanName;
"icons/iconu_jetpack",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
@@ -3798,48 +3805,16 @@ upgradeAttributes_t bg_upgrades[ ] =
"bsuit", //char *upgradeName;
"Battlesuit", //char *upgradeHumanName;
"icons/iconu_bsuit",
- WP_NONE, //weapon_t weaponAmmo;
- 0, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
},
{
- UP_MGCLIP, //int upgradeNum;
- MGCLIP_PRICE, //int price;
+ UP_AMMO, //int upgradeNum;
+ 0, //int price;
( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
SLOT_NONE, //int slots;
- "mgclip", //char *upgradeName;
- "1 Rifle Clip", //char *upgradeHumanName;
+ "ammo", //char *upgradeName;
+ "Ammunition", //char *upgradeHumanName;
0,
- WP_MACHINEGUN, //weapon_t weaponAmmo;
- 0, //int ammo;
- 1, //int clips;
- WUT_HUMANS //WUTeam_t team;
- },
- {
- UP_CGAMMO, //int upgradeNum;
- CGAMMO_PRICE, //int price;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
- SLOT_NONE, //int slots;
- "cgammo", //char *upgradeName;
- "Chaingun bullets", //char *upgradeHumanName;
- 0,
- WP_CHAINGUN, //weapon_t weaponAmmo;
- 100, //int ammo;
- 0, //int clips;
- WUT_HUMANS //WUTeam_t team;
- },
- {
- UP_GAS, //int upgradeNum;
- GAS_PRICE, //int price;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
- SLOT_NONE, //int slots;
- "gas", //char *upgradeName;
- "Flamer gas", //char *upgradeHumanName;
- 0,
- WP_FLAMER, //weapon_t weaponAmmo;
- 200, //int ammo;
- 0, //int clips;
WUT_HUMANS //WUTeam_t team;
}
};
@@ -3987,46 +3962,6 @@ char *BG_FindIconForUpgrade( int upgrade )
/*
==============
-BG_FindWeaponAmmoForUpgrade
-==============
-*/
-weapon_t BG_FindWeaponAmmoForUpgrade( int upgrade )
-{
- int i;
-
- for( i = 0; i < bg_numUpgrades; i++ )
- {
- if( bg_upgrades[ i ].upgradeNum == upgrade )
- return bg_upgrades[ i ].weaponAmmo;
- }
-
- return WP_NONE;
-}
-
-/*
-==============
-BG_FindAmmoForUpgrade
-==============
-*/
-void BG_FindAmmoForUpgrade( int upgrade, int *ammo, int *clips )
-{
- int i;
-
- for( i = 0; i < bg_numUpgrades; i++ )
- {
- if( bg_upgrades[ i ].upgradeNum == upgrade )
- {
- if( ammo != NULL )
- *ammo = bg_upgrades[ i ].ammo;
-
- if( clips != NULL )
- *clips = bg_upgrades[ i ].clips;
- }
- }
-}
-
-/*
-==============
BG_FindTeamForUpgrade
==============
*/
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index ade44adf..49165a49 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -339,6 +339,7 @@ typedef enum
WP_BLASTER,
WP_MACHINEGUN,
+ WP_SHOTGUN,
WP_CHAINGUN,
WP_FLAMER,
WP_MASS_DRIVER,
@@ -373,9 +374,7 @@ typedef enum
UP_JETPACK,
UP_BATTLESUIT,
- UP_MGCLIP,
- UP_CGAMMO,
- UP_GAS,
+ UP_AMMO,
UP_NUM_UPGRADES
} upgrade_t;
@@ -521,6 +520,8 @@ typedef enum
EV_BULLET_HIT_FLESH,
EV_BULLET_HIT_WALL,
+ EV_SHOTGUN,
+
EV_MISSILE_HIT,
EV_MISSILE_MISS,
EV_MISSILE_MISS_METAL,
@@ -1045,10 +1046,6 @@ typedef struct
char *icon;
- weapon_t weaponAmmo;
- int ammo;
- int clips;
-
WUTeam_t team;
} upgradeAttributes_t;
@@ -1164,8 +1161,6 @@ char *BG_FindNameForUpgrade( int upgrade );
int BG_FindUpgradeNumForName( char *name );
char *BG_FindHumanNameForUpgrade( int upgrade );
char *BG_FindIconForUpgrade( int upgrade );
-weapon_t BG_FindWeaponAmmoForBuildable( int upgrade );
-void BG_FindAmmoForUpgrade( int upgrade, int *ammo, int *clips );
WUTeam_t BG_FindTeamForUpgrade( int upgrade );
// content masks
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 6bc84ed9..3662a8ab 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -1338,7 +1338,6 @@ void Cmd_Buy_f( gentity_t *ent )
}
else if( upgrade != UP_NONE )
{
- int maxAmmo, newAmmo, newClips;
weapon_t weaponAmmo;
//already got this?
@@ -1382,33 +1381,11 @@ void Cmd_Buy_f( gentity_t *ent )
return;
}
- BG_FindAmmoForUpgrade( upgrade, &newAmmo, &newClips );
-
- if( newAmmo || newClips )
- {
- //get current ammo for the weapon in question
- weaponAmmo = BG_FindWeaponAmmoForUpgrade( upgrade );
- BG_unpackAmmoArray( weaponAmmo, ent->client->ps.ammo, ent->client->ps.powerups,
- &quan, &clips, &maxClips );
+ weaponAmmo = ent->client->ps.weapon;
- BG_FindAmmoForWeapon( weaponAmmo, &maxAmmo, NULL, NULL );
-
- //this ammo package would exceed max ammo
- if( clips + newClips > maxClips )
- {
- //FIXME: different dialog?
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOSLOTS );
- return;
- }
- else
- clips += newClips;
-
- if( quan + newAmmo > maxAmmo )
- quan = maxAmmo;
- else
- quan += newAmmo;
-
- //updata ammo count
+ if( upgrade == UP_AMMO && !BG_FindUsesEnergyForWeapon( weaponAmmo ) )
+ {
+ BG_FindAmmoForWeapon( weaponAmmo, &quan, &clips, &maxClips );
BG_packAmmoArray( weaponAmmo, ent->client->ps.ammo, ent->client->ps.powerups,
quan, clips, maxClips );
}
@@ -1430,8 +1407,14 @@ void Cmd_Buy_f( gentity_t *ent )
if( numItems == 0 )
G_AddEvent( ent, EV_NEXT_WEAPON, ent->client->ps.clientNum );
- //retrigger the armoury menu
- ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;
+ if( trap_Argc( ) >= 2 )
+ {
+ trap_Argv( 2, s, sizeof( s ) );
+
+ //retrigger the armoury menu
+ if( !Q_stricmp( s, "retrigger" ) )
+ ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;
+ }
//update ClientInfo
ClientUserinfoChanged( ent->client->ps.clientNum );
@@ -1506,8 +1489,14 @@ void Cmd_Sell_f( gentity_t *ent )
else
trap_SendServerCommand( ent-g_entities, va( "print \"Unknown item\n\"" ) );
- //retrigger the armoury menu
- ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;
+ if( trap_Argc( ) >= 2 )
+ {
+ trap_Argv( 2, s, sizeof( s ) );
+
+ //retrigger the armoury menu
+ if( !Q_stricmp( s, "retrigger" ) )
+ ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;
+ }
//update ClientInfo
ClientUserinfoChanged( ent->client->ps.clientNum );
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 4dd1d67e..107128b5 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -159,6 +159,66 @@ void bulletFire( gentity_t *ent, float spread, int damage, int mod )
/*
======================================================================
+SHOTGUN
+
+======================================================================
+*/
+
+// this should match CG_ShotgunPattern
+void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent )
+{
+ int i;
+ float r, u;
+ vec3_t end;
+ vec3_t forward, right, up;
+ trace_t tr;
+ gentity_t *traceEnt;
+
+ // derive the right and up vectors from the forward vector, because
+ // the client won't have any other information
+ VectorNormalize2( origin2, forward );
+ PerpendicularVector( right, forward );
+ CrossProduct( forward, right, up );
+
+ // generate the "random" spread pattern
+ for( i = 0; i < SHOTGUN_PELLETS; i++ )
+ {
+ r = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
+ u = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
+ VectorMA( origin, 8192 * 16, forward, end );
+ VectorMA( end, r, right, end );
+ VectorMA( end, u, up, end );
+
+ trap_Trace( &tr, origin, NULL, NULL, end, ent->s.number, MASK_SHOT );
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ // send bullet impact
+ if( !( tr.surfaceFlags & SURF_NOIMPACT ) )
+ {
+ if( traceEnt->takedamage )
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos, SHOTGUN_DMG, 0, MOD_SHOTGUN );
+ }
+ }
+}
+
+
+void shotgunFire( gentity_t *ent )
+{
+ gentity_t *tent;
+
+ // send shotgun blast
+ tent = G_TempEntity( muzzle, EV_SHOTGUN );
+ VectorScale( forward, 4096, tent->s.origin2 );
+ SnapVector( tent->s.origin2 );
+ tent->s.eventParm = rand() & 255; // seed for spread pattern
+ tent->s.otherEntityNum = ent->s.number;
+
+ ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
+}
+
+/*
+======================================================================
+
MASS DRIVER
======================================================================
@@ -1120,6 +1180,9 @@ void FireWeapon( gentity_t *ent )
case WP_MACHINEGUN:
bulletFire( ent, RIFLE_SPREAD, RIFLE_DMG, MOD_MACHINEGUN );
break;
+ case WP_SHOTGUN:
+ shotgunFire( ent );
+ break;
case WP_CHAINGUN:
bulletFire( ent, CHAINGUN_SPREAD, CHAINGUN_DMG, MOD_CHAINGUN );
break;
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index ca7c48d8..7694887d 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -291,6 +291,16 @@
#define RIFLE_SPREAD 200
#define RIFLE_DMG HDM(5)
+#define SHOTGUN_SHELLS 8
+#define SHOTGUN_PELLETS 8 //used to sync server and client side
+#define SHOTGUN_SPAWNCLIPS 3
+#define SHOTGUN_MAXCLIPS 3
+#define SHOTGUN_REPEAT 1200
+#define SHOTGUN_RELOAD 2000
+#define SHOTGUN_PRICE 150
+#define SHOTGUN_SPREAD 900
+#define SHOTGUN_DMG HDM(5)
+
#define CHAINGUN_BULLETS 300
#define CHAINGUN_REPEAT 50
#define CHAINGUN_PRICE 200
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
index bf32f974..5a98d881 100644
--- a/src/ui/ui_main.c
+++ b/src/ui/ui_main.c
@@ -3486,7 +3486,7 @@ static void UI_LoadTremHumanArmouryBuys( )
uiInfo.tremHumanArmouryBuyList[ j ].text =
String_Alloc( BG_FindHumanNameForWeapon( i ) );
uiInfo.tremHumanArmouryBuyList[ j ].cmd =
- String_Alloc( va( "cmd buy %s", BG_FindNameForWeapon( i ) ) );
+ String_Alloc( va( "cmd buy %s retrigger", BG_FindNameForWeapon( i ) ) );
uiInfo.tremHumanArmouryBuyList[ j ].infopane =
UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForWeapon( i ) ) );
@@ -3506,7 +3506,7 @@ static void UI_LoadTremHumanArmouryBuys( )
uiInfo.tremHumanArmouryBuyList[ j ].text =
String_Alloc( BG_FindHumanNameForUpgrade( i ) );
uiInfo.tremHumanArmouryBuyList[ j ].cmd =
- String_Alloc( va( "cmd buy %s", BG_FindNameForUpgrade( i ) ) );
+ String_Alloc( va( "cmd buy %s retrigger", BG_FindNameForUpgrade( i ) ) );
uiInfo.tremHumanArmouryBuyList[ j ].infopane =
UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForUpgrade( i ) ) );
@@ -3536,7 +3536,7 @@ static void UI_LoadTremHumanArmourySells( )
{
uiInfo.tremHumanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForWeapon( i ) );
uiInfo.tremHumanArmourySellList[ j ].cmd =
- String_Alloc( va( "cmd sell %s", BG_FindNameForWeapon( i ) ) );
+ String_Alloc( va( "cmd sell %s retrigger", BG_FindNameForWeapon( i ) ) );
uiInfo.tremHumanArmourySellList[ j ].infopane =
UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForWeapon( i ) ) );
@@ -3552,7 +3552,7 @@ static void UI_LoadTremHumanArmourySells( )
{
uiInfo.tremHumanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForUpgrade( i ) );
uiInfo.tremHumanArmourySellList[ j ].cmd =
- String_Alloc( va( "cmd sell %s", BG_FindNameForUpgrade( i ) ) );
+ String_Alloc( va( "cmd sell %s retrigger", BG_FindNameForUpgrade( i ) ) );
uiInfo.tremHumanArmourySellList[ j ].infopane =
UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForUpgrade( i ) ) );