From c930a6e70731d1f61eab3df3db4c1133312792cb Mon Sep 17 00:00:00 2001 From: WofWca Date: Mon, 1 Jun 2026 15:21:06 +0400 Subject: [PATCH 1/5] fix: machinegun / shotgun shells initial angles Make the angles relative to where the shooter is looking and not absolute. --- code/cgame/cg_weapons.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 57b13d28..82846b45 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -63,9 +63,11 @@ static void CG_MachineGunEjectBrass( centity_t *cent ) { 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; + VectorCopy ( cent->lerpAngles, le->angles.trBase ); + le->angles.trBase[PITCH] += 90; + le->angles.trBase[0] += rand()&31 - 15; + le->angles.trBase[1] += rand()&31 - 15; + le->angles.trBase[2] += rand()&31 - 15; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; @@ -139,9 +141,11 @@ static void CG_ShotgunEjectBrass( centity_t *cent ) { 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; + VectorCopy( cent->lerpAngles, le->angles.trBase ); + le->angles.trBase[PITCH] += 90; + le->angles.trBase[0] += rand()&31 - 15; + le->angles.trBase[1] += rand()&31 - 15; + le->angles.trBase[2] += rand()&31 - 15; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; From f49bb9435b2e64ab2ff3cdd9f98701dde56aac78 Mon Sep 17 00:00:00 2001 From: WofWca Date: Mon, 1 Jun 2026 19:35:37 +0400 Subject: [PATCH 2/5] fix: make shotgun / machinegun shells rotate The rotation rate is represented in degrees per second, so in vanilla it's absolutely minescule. It appears that the intent was to make the rate 1000x higher, i.e. the author probably assumed that the rate was represented in degrees per millisecond. Anyways, the result looks pretty good. --- code/cgame/cg_weapons.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 82846b45..91d5acb3 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -68,9 +68,9 @@ static void CG_MachineGunEjectBrass( centity_t *cent ) { le->angles.trBase[0] += rand()&31 - 15; le->angles.trBase[1] += rand()&31 - 15; le->angles.trBase[2] += rand()&31 - 15; - le->angles.trDelta[0] = 2; - le->angles.trDelta[1] = 1; - le->angles.trDelta[2] = 0; + le->angles.trDelta[PITCH] = 1750 + rand()&511; + le->angles.trDelta[YAW] = le->angles.trDelta[PITCH] / 2 * ((rand()&1)*2 - 1); + le->angles.trDelta[ROLL] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; @@ -146,9 +146,9 @@ static void CG_ShotgunEjectBrass( centity_t *cent ) { le->angles.trBase[0] += rand()&31 - 15; le->angles.trBase[1] += rand()&31 - 15; le->angles.trBase[2] += rand()&31 - 15; - le->angles.trDelta[0] = 1; - le->angles.trDelta[1] = 0.5; - le->angles.trDelta[2] = 0; + le->angles.trDelta[PITCH] = 750 + rand()&511; + le->angles.trDelta[YAW] = le->angles.trDelta[PITCH] / 2 * ((rand()&1)*2 - 1); + le->angles.trDelta[ROLL] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; From e233d41c635d529cbcd6fab446124332a3632cab Mon Sep 17 00:00:00 2001 From: WofWca Date: Mon, 1 Jun 2026 19:42:15 +0400 Subject: [PATCH 3/5] fix: weapon shells: lie flat on the ground --- code/cgame/cg_localents.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/code/cgame/cg_localents.c b/code/cgame/cg_localents.c index 45535ab7..66183ee9 100644 --- a/code/cgame/cg_localents.c +++ b/code/cgame/cg_localents.c @@ -283,6 +283,22 @@ static void CG_AddFragment( localEntity_t *le ) { return; } + // stop pitch spin so that when it settles it lies flat on the ground + if ( le->leBounceSoundType == LEBS_BRASS ) { + // save current as base + BG_EvaluateTrajectory( &le->angles, cg.time, le->angles.trBase ); + le->angles.trTime = cg.time; + + // "fall over" to nearest horizontal orientation + // + // FIXME: handle non-horizontal surfaces (see `trace.plane.normal`) + // (YAW would need to be taken into account then) + le->angles.trBase[PITCH] = 90 + 180 * floor( le->angles.trBase[PITCH] / 180 ); + le->angles.trDelta[PITCH] = 0; + + AnglesToAxis( le->angles.trBase, le->refEntity.axis ); + } + // leave a mark CG_FragmentBounceMark( le, &trace ); From 7ae51748bc54a8833775717d76ff0722448a07c9 Mon Sep 17 00:00:00 2001 From: WofWca Date: Mon, 1 Jun 2026 19:43:07 +0400 Subject: [PATCH 4/5] feat: weapon shells: inherit player velocity --- code/cgame/cg_weapons.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 91d5acb3..1c3ebfcc 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -51,10 +51,12 @@ static void CG_MachineGunEjectBrass( centity_t *cent ) { waterScale = 0.10f; } + // don't inherit the full velocity, to emulate air resistance + VectorScale( cent->currentState.pos.trDelta, 0.75, le->pos.trDelta ); 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 ); + VectorMA( le->pos.trDelta, waterScale, xvelocity, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.machinegunBrassModel; @@ -130,10 +132,12 @@ static void CG_ShotgunEjectBrass( centity_t *cent ) { waterScale = 0.10f; } + // don't inherit the full velocity, to emulate air resistance + VectorScale( cent->currentState.pos.trDelta, 0.675, le->pos.trDelta ); 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 ); + VectorMA( le->pos.trDelta, waterScale, xvelocity, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.shotgunBrassModel; From 32337ff05178ef58331450045686c28cb30dd19f Mon Sep 17 00:00:00 2001 From: WofWca Date: Mon, 1 Jun 2026 20:14:16 +0400 Subject: [PATCH 5/5] fix: don't half-sink weapons shells in the ground That is "increase the physics box for brass" and not about the "sink after a few seconds" thing. --- code/cgame/cg_localents.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/code/cgame/cg_localents.c b/code/cgame/cg_localents.c index 66183ee9..3ffa5bf8 100644 --- a/code/cgame/cg_localents.c +++ b/code/cgame/cg_localents.c @@ -217,6 +217,24 @@ void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) { } } +static void GetFragmentMinsMaxs( const localEntity_t *le, vec3_t mins, vec3_t maxs ) { + VectorCopy( vec3_origin, mins ); + VectorCopy( vec3_origin, maxs ); + + // can't use `le->leBounceSoundType` because for brass it gets unset + // after first impact + if ( le->refEntity.hModel == cgs.media.machinegunBrassModel ) { + // The primary point of this is to ensure + // that they don't half-sink into the the ground. + // Same for shotgun. + VectorSet(mins, -1, -1, -0.2); + VectorSet(maxs, 1, 1, 0.2); + } else if ( le->refEntity.hModel == cgs.media.shotgunBrassModel ) { + VectorSet(mins, -1, -1, -0.5); + VectorSet(maxs, 1, 1, 0.5); + } +} + /* ================ CG_AddFragment @@ -225,6 +243,11 @@ CG_AddFragment static void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; + vec3_t mins; + vec3_t maxs; + + VectorCopy( vec3_origin, mins ); + VectorCopy( vec3_origin, maxs ); if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time @@ -252,8 +275,9 @@ static void CG_AddFragment( localEntity_t *le ) { // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); + GetFragmentMinsMaxs( le, mins, maxs ); // trace a line from previous position to new position - CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); + CG_Trace( &trace, le->refEntity.origin, mins, maxs, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin );