Every solid in de_forge stops every bullet dead, so cover is binary and the AK, the AWP and the Glock all lose to the same crate. Give the crates — and only the crates — a thickness a strong round can cross, so standing behind a box is a choice with a cost.
Done looks like
[ ] weapons.ts gains a per-weapon penetration: how many units of crate the round crosses and how much of its damage survives. High for awp, ak47, m4a4, deagle; low for glock, usp, mp9; none for knife. One readable table, no magic numbers at the call site.
[ ] traceBullet continues past a box whose material is MAT.CRATEand whose thickness along the ray is under the weapon's limit. MAT.WALL, MAT.FLOOR, MAT.SITE and MAT.METAL are never crossed. Thickness is the entry and exit fractions rayBox now returns — no new geometry.
[ ] At most one surface per shot: two crates in a line stop the bullet.
[ ] What the bullet lost is carried on BulletHit and applied once, where applyHit already computes damage — not sprinkled through world.ts.
[ ] The tracer ends at what the bullet finally hit, and an impact effect is pushed at the entry face as well, so a punched crate shows where it was punched.
[ ] src/client/combat.test.ts: a rifle through a thin crate into a body deals damage, and less than the same shot with no crate; the same shot with a pistol deals none; through a wall, none; through a crate thicker than the limit, none; through two crates, none.
[ ] README's damage paragraph says the rule, and which materials are penetrable.
Files I expect to change
src/client/weapons.ts, src/client/combat.ts, src/client/world.ts (the entry impact effect only), src/client/combat.test.ts, README.md.
Risks
Do not touch visible(). The bots' sight, observe.ts and the grenade's line of sight all read it, and the shipped policy's 32 inputs are the contract its weights were trained against. A bot shot through a crate it thinks is cover is the player's advantage and is the point of the change; teaching observe.ts about penetration would mean retraining the game.
Bots shoot through crates too, the moment traceBullet lets them. Play a few seeded matches headless and say in the pull whether the round still ends the way it did.
#37 (smoke) is the other issue editing sightlines and trace.ts; whoever lands second rebases.
Waits on the trace.ts test issue: the exit fraction comes from there, and this is exactly the change that wants that file covered before it moves.
Brief that fits: vesper — this is the shooting feel, and it wants someone who will measure the round afterwards.
Every solid in
de_forgestops every bullet dead, so cover is binary and the AK, the AWP and the Glock all lose to the same crate. Give the crates — and only the crates — a thickness a strong round can cross, so standing behind a box is a choice with a cost.Done looks like
weapons.tsgains a per-weapon penetration: how many units of crate the round crosses and how much of its damage survives. High forawp,ak47,m4a4,deagle; low forglock,usp,mp9; none forknife. One readable table, no magic numbers at the call site.traceBulletcontinues past a box whose material isMAT.CRATEand whose thickness along the ray is under the weapon's limit.MAT.WALL,MAT.FLOOR,MAT.SITEandMAT.METALare never crossed. Thickness is the entry and exit fractionsrayBoxnow returns — no new geometry.BulletHitand applied once, whereapplyHitalready computes damage — not sprinkled throughworld.ts.impacteffect is pushed at the entry face as well, so a punched crate shows where it was punched.src/client/combat.test.ts: a rifle through a thin crate into a body deals damage, and less than the same shot with no crate; the same shot with a pistol deals none; through a wall, none; through a crate thicker than the limit, none; through two crates, none.Files I expect to change
src/client/weapons.ts,src/client/combat.ts,src/client/world.ts(the entry impact effect only),src/client/combat.test.ts,README.md.Risks
visible(). The bots' sight,observe.tsand the grenade's line of sight all read it, and the shipped policy's 32 inputs are the contract its weights were trained against. A bot shot through a crate it thinks is cover is the player's advantage and is the point of the change; teachingobserve.tsabout penetration would mean retraining the game.traceBulletlets them. Play a few seeded matches headless and say in the pull whether the round still ends the way it did.#37(smoke) is the other issue editing sightlines andtrace.ts; whoever lands second rebases.trace.tstest issue: the exit fraction comes from there, and this is exactly the change that wants that file covered before it moves.Brief that fits: vesper — this is the shooting feel, and it wants someone who will measure the round afterwards.