Split The code is public; the app answers only to the crew.

Utility, part two: smoke that actually takes a sightline off the map #37

open told opened this
opened this

A smoke grenade that blinds both sides for long enough to cross the ground it covers — the same thrown object issue #36 builds, with a cloud instead of a blast.

Done looks like:

  • [ ] Smoke buyable at $300 in the buy panel, carried one at a time, thrown by exactly the code path the HE uses — no second throwing mechanism.
  • [ ] On coming to rest it blooms over about a second to a radius of roughly 144 units, holds ~15 seconds and fades. Live clouds are capped and the oldest is dropped, so a long round cannot grow the list without bound.
  • [ ] Sight is blocked for real, in one function that says whether a segment passes through a cloud, used by canSee in world.ts — a bot cannot see a player through smoke, and neither can the HUD's sight checks. Bullets are not blocked: a bullet through smoke does its usual damage, as in CS.
  • [ ] You cannot see through it either: drawn in renderer.ts as a soft, animated volume that hides what is behind it, with 60 fps held with two clouds up on the machine the frame counter is measured on. Say what you measured, before and after.
  • [ ] Bots do not cheat and do not gain new code: nothing in bots.ts reads a cloud; they simply lose the target when the sightline goes, and they still hear shots through it via listen.
  • [ ] Tests: a segment through the centre is blocked and one grazing past the edge is not; a cloud expires and sight comes back; a bullet still hits through smoke; canSee flips false then true across a cloud's life; the cap holds; a cloud thrown in one round is gone in the next.
  • [ ] bun test green, README updated — Playing for the key and the price, What is real for what smoke does and does not stop.

Not in this issue: flashbang and molotov, and bots buying or throwing utility. Smoke is never spawned in src/rl/arena.ts: the shipped weights were trained without it and the observation stays at 32 inputs.

Files I expect to change: src/client/smoke.ts (new, or a second concept inside grenades.ts only if it truly is one) + its test, src/client/world.ts, trace.ts, renderer.ts, hud.ts, weapons.ts, README.md.

Risks: the sight test runs per bot per tick over every cloud and sits in the hot path of the bot loop — make it a cheap ray-versus-sphere over a small array with a zero-length fast path, and keep the arena's cost unchanged (bun run scripts/train.ts --bench before and after). Translucent volume is where frame time goes: soft billboards that fade near the camera are likely enough, and a cloud must not make the whole scene shimmer or shift layout on the HUD canvas.

Crew: vesper. Waits on #36 for the thrown object.

commented

Two details this issue could not know, now that #36 has landed as #47:

  • The pocket is a number. Player.grenades is a count with MAX_GRENADES = 1, and he is a single WeaponId on the fourth slot. A second kind of grenade cannot be a second count bolted on — the pocket has to become per-kind (one HE and one smoke, as in CS) and slot 4 needs a stated rule for which one it draws and how you get to the other. Say what that rule is before writing the cloud.
  • Reuse the flight, not a copy. grenades.ts already owns the throw (thrown), the bounce and the fuse, with Grenade.kind as the seam; a smoke is that same object with a different thing happening when the fuse ends. The line-of-sight rule differs though: the HE blast requires visible(g.pos, eye, boxes), and a cloud's own sight test is the new concept this issue is really about.
commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)

commented

Rewritten — the body above is superseded by this comment. I cannot edit another author's issue, so read this as what done looks like now.

The blocker this issue has been parked on twice (both times an agent's run died on an API error, not on the work) is real: world.ts:428 keeps grenades as one integer, MAX_GRENADES = 1, so a smoke cannot be carried beside an HE. That half is now #130, and this issue waits on it and is only the cloud.

Done looks like:

  • [ ] Smoke at $300 in the buy panel, carried through #130's pocket, thrown by exactly the path the HE uses in grenades.ts — the fuse ends in a cloud instead of a blast, and there is no second throwing mechanism.
  • [ ] On coming to rest it blooms over about a second to a radius of ~144 units, holds ~15 seconds, fades. Live clouds are capped and the oldest dropped, so a long round cannot grow the list without bound.
  • [ ] One function says whether a segment passes through a cloud, and canSee in world.ts is its only caller besides the renderer. A bot cannot see through smoke and gains no code to know that — it simply stops seeing. Bullets are not blocked.
  • [ ] Drawn in renderer.ts as a soft animated volume, with the frame budget from #89 still met with two clouds up. State the before and after you measured.
  • [ ] observe.ts is untouched; if a bot's observation changes, the shipped policy's card is wrong and the pull must say so.
  • [ ] Tests: a segment through a cloud fails canSee and the same segment a second after the fade passes; a cloud list that cannot exceed its cap.

Crew: vesper — the cloud is rendering and the sight rule is geometry.

commented

Unblocked: #130 merged as pull #142 (d54675d), so the pocket this issue was waiting on exists. What it actually looks like, so the smoke goes in against the real shape rather than the one the old body imagined:

// src/client/world.ts:47-52
/** The pocket: one slot per grenade kind, so a second kind (smoke, when it … */
grenades: Partial<Record<WeaponId, number>>;

// world.ts:441-448 — the ceiling is per kind, not shared
export const MAX_GRENADES = 1;

Three things that changes for this issue:

  • MAX_GRENADES is a per-kind ceiling now, and the comment beside it says the pocket deliberately has no shared total. If a smoke plus an HE should be capped at two, that decision belongs in this pull, in that same place, with the reason written down.
  • GRENADE_IDS is derived from WEAPONS, so a smoke becomes a grenade the pocket understands by being a weapon whose kind is "grenade" — no new list to keep in step.
  • The round boundary already keeps the pocket (world.ts:382) and clears only what is in the air, so a bought smoke survives to the next round exactly as an HE does. Nothing to add there; just do not undo it.

The rest of my rewrite above stands: the cloud, one sight function used by canSee and the renderer, bullets not blocked, the frame budget stated, and observe.ts untouched. Ready now.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs