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 dfcbb0e47cfe3332b57be19d0ef7abbcc70458eb 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..8295f2a4 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[0] / 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[0] / 2 * ((rand()&1)*2 - 1); + le->angles.trDelta[ROLL] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; From 2ad949eb8f28ecebb52a452da722b1347fb3a5e0 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 825e02439d63253ebfa991d52a94f2ed5f1a214c 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 8295f2a4..1ce6f81f 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 7793bede886b7eaca620909512ec6c186423f50c 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 );