- Underbarrel attachments work across IW4, IW5, T5 and T6, including
per-family impact FX, T5 flamethrower streams and T6 launcher fuses.
- Viewmodel notetrack sounds play in third person.
- Transport operations and queues are bounded; Quinn congestion control
returns to its default.
- Match publication, presentation context and prepared content semantics
are qualified and validated; cosmetic and entity occurrence lifecycles
are preserved.
- GPU skinning batches within supported device budgets.
- Immutable UI projections and correlated class replies are reconciled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An effect model element with the model-physics flag (a cash register's
top and base, a card reader's pad, suitcase halves and contents, bottles
and tins) is launched once by its velocity graphs and then falls, bounces
off the world and settles. Nothing simulated it. It took the ordinary
path instead, where the velocity graphs keep pushing for the whole 7-10 s
life, the element's own reflection factor (1.0 for most of these) bounces
it with no loss, and with no at-rest fraction it never stops spinning.
Since 3039cf4 these pieces are drawn, so on mp_terminal register screens
hovered at head height for seconds, turning two to four times a second,
and card reader pads rose and stuck to the ceiling.
evaluate_elem_physics_motion now runs these pieces in the collide pass:
launch velocity from the graphs at age 0, gravity 800, a fixed bounce of
0.2 and friction of 0.5, a box reaching 1 unit each way from the model's
origin, a lift out of a floor it spawns in, and a stop once it is slow on
the ground.
The at-rest fraction marks when the piece first touches the ground, and
its spin stops there with no easing turn. Spinning on until it was slow,
plus the 150 ms of turn clamp_elem_rotation_time eases in after the at-rest
time, twists a luggage half (authored at 600-950 degrees a second) up to
another half turn on the floor, and it froze standing on an edge or a
corner. From the landing on, the piece is drawn turned the least way that
stands its thinnest bounds axis upright, so it lies on its broad face,
raised or lowered so that face meets what its box rests on. It still
bounces and slides to a stop as before.
The box is not sized from the model's bounds (IW4's midpoint and half
size). A piece's model origin sits at an edge of the model (a suitcase
half's at the case's bottom edge), so a box sized from the bounds around
it is no truer, and a 5-unit one starts the halves inside the floor, where
the lift out of it drops their launch. The bounds only pick the face it
lies on.
The clothes from a suitcase are ordinary colliding model elements that
spawn around the case's origin on the floor. Their first step starts
inside the floor and leaves it (startsolid, not allsolid), and that froze
them there, spinning in place. A model element is now let out of a brush
it starts in when its step leaves it; other element types still freeze.
No draws or traces are added for the landing; a landed piece costs one
3x3 turn a frame.
Still short of a physics world: the box doesn't turn with the model, a
piece spins at the authored rate in the air instead of a simulated one, it
is laid flat on landing instead of tumbling onto a face, and pieces don't
meet each other or entities.
Checked on IW4 Pocket's code, which carries the same change with bounce
and friction from each model's physics preset; this port is compile-checked
only. On the Mac and on an iPhone build on mp_terminal, shot registers'
pieces landed on the counter and stayed, bills flew as before, and a shot
suitcase's halves and clothes landed and stayed until their life ended.
For the landing pose, on the Mac at an mp_terminal suitcase, a screenshot
every 0.1 s, shot with the SCAR and with the AA-12: before, the halves
stood on an edge or a corner until they vanished; after, they land and lie
flat. At the food-court register the screen top, base and card-reader pads
lie flat on the counter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Muzzle flashes, bullet impacts and explosions lit walls and snow blue or
cyan while their own flash and fireball sprites stayed orange. The effect
colour is stored B,G,R,A. render_fx swaps it once to R,G,B,A for every
sprite, trail and spark, and the light path then passed that value to a
helper that swapped red and blue a second time. GfxLightPack.color is
R,G,B like a map primary light, so every effect omni and spot light came
out with red and blue reversed.
The helper is now elem_light_color_rgb and keeps the order. The light
instance field and the omni and spot pack parameters are renamed
color_rgb so the names say what the value holds. Read in the same order
the sprites use, the light elements of these effects are amber and
orange, matching their flash and fireball sprites.
Measured on the Mac on mp_subbase, facing the concrete wall above the
snow bank: the light an RPG blast adds to wall and snow went from linear
R:G:B 0.28:0.81:1.00 (cyan) to 1.00:0.82:0.33 (amber), and an M4 burst
from 0.34:0.48:1.00 (blue) to 1.00:0.60:0.55 (warm). Wall and snow
before the shots are unchanged, since map lights take another path. On
an iPhone build on mp_subbase, a long burst at a concrete wall lit it
warm instead of blue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prepare progression and loading through Bevy scheduling, preserve round HUD and bombsite props, restore grenade and T5 shadow presentation, and keep menus usable while classes load. Report update progress and continue startup after update failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effect model pieces (the bills and halves of a shot cash register, food,
luggage contents, plant pots, glass pieces) have not drawn since 69cf9ff
moved the effect model geometry into the Plan phase of the world spawn.
That phase runs before world_occupancy::place builds the model lighting
atlas, so prepare_fx_model_geometry found no atlas and quietly returned an
empty plan, and every model elem that passed its LOD and cull checks was
dropped for want of geometry.
The geometry is now built in spawn_world_finish straight after
world_occupancy::place. The Plan phase still installs an empty plan so its
readers find the resource. A missing atlas is now a warning with the model
count, and one line reports the models and LODs prepared and refused.
The bills had a second fault. mp_terminal stores `,fx_cash01` as a
placeholder for common_mp's fx_cash01, and the effect catalog kept the
comma in the model hint, so the edge never bound. Model hints now use the
bare name (AssetRef::bare_name), so fx_cash01 resolves against common_mp.
One model whose packed rows do not match its decoded vertices would make
the packed payload of all effect model geometry Unavailable, and the merged
xmodel lane then drops every xmodel draw (viewmodel, bodies, script models)
for the frame. Such a LOD is now refused and logged when the geometry is
built, and fill_fx_model_plan only draws effect models from geometry whose
Iw4 packed rows cover every decoded vertex.
The player's own first-person shell casings are still skipped by the
render-flag check in fill_fx_model_plan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shooting a vending machine on mp_terminal drew large flat yellow, white
and red sheets next to it and made the game stutter. A spark fountain that
runs relative to its effect was sprayed from the effect-local spawn origin,
and the custom draw then placed the cells with the cloud matrix T(pos)
R(quat) S(vis scale). Prop sparks (props/credit_card_touchpad_exp on the
card touchpads, and explosions/powerlines_* from the vending machine's
tv_explosion) carry a vis scale of 150, so every spark was drawn 150 times
too big; barrelexp and small_vehicle_explosion (scale 10) were 10 times
too big. New cells also had every sample time at 0, which ends the ribbon at
origins[3]: about 1,780 untraced cells stacked one 150-unit quad on the
effect point until the tracer, 16 cells per update, reached them.
The spray now gets the world spawn point (spawn_origin_world), untraced cells
start as an unobstructed arc (spark_fountain_integrate_miss_cell), and the
custom draw is submitted with a unit placement (spark_fountain_draw_cloud).
The cells are already in world space and the fountain shader only applies
view and projection, so the vis scale stays a draw gate and no longer scales
the vertices. Bounce traces now run in the same space as world collision.
The elem keeps drifting on its velocity graph (0.4 units/ms on most
fountains) while the sparks stay where they were sprayed, so the fountain is
now culled around the spray point, with the radius grown by how far a spark
can have flown by then (max speed * t + |gravity| * t^2 / 2, times the bounce
fraction when it is above 1). Sparks no longer follow an effect that moves
after spraying them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decode Black Ops audio natively and load Black Ops II worlds, faction
models, equipment, materials, lighting and weapon camouflage.
Rework capsule collision, solid recovery, penetration, recoil, effects
and sentry placement; synchronize late joins and player event delivery.
Keep local skill ratings and signed player accounts.
Show the selected community and master ping on the main menu. Add twenty
asset-filtered class presets across IW4, IW5, T5 and T6, plus fifty-two
unique player-style names shared by AI bots and dummies. Honor the chosen
dev descriptor in the two-client approved scenario and credit contributors
and weapon, map and attachment QA testers.
Validated with the approved gameplay lifecycle and two-client dev-master
scenarios, installed class-asset audit, bot-name uniqueness checks and
Linux IW4/IW5/T5/T6 gameplay smoke runs. Foreign-weapon match-stat schema
coverage remains incomplete.
Scoped weapons hold breath with recovery feedback and sound. Riot shields
block and deflect bullets in hand and on the back; explosive crossbow bolts,
destructible map cars with round resets, and T5 map model collision work.
Remote players show last stand, weapon change, jump, mantle, landing,
ladder and stun animations, and server hitboxes follow the shared torso tree.
GSC gains spectator follow cameras, chat, kick, vehicle paths and flight
controls, physics spheres, slow motion, slide and mantle movers, damage
triggers, music, ambience, rumble and live audio fades over a shared mixer.
IW5 and T5 maps get sun shadows with per-stage sun selection; post-fx
pipelines compile before a vision blend needs them. The lobby browser keeps
focus and selection, and rooms can be password protected. Console supply
and give commands search every item category. Frames and replays decode
only the current wire protocol.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- One common set per content profile, shared by every match and the loadout
menu; a restart over the same map keeps its images and static geometry.
- Weapons resolve as families: attachments are chosen from each game's own
tables, checked, and composed into prepared configurations with their
first-person skeleton assembled once.
- Sessions end through one teardown and one process exit; retired resources
are dropped on a worker, not the frame.
- Sound is captured by the walks that already read each zone.
- A loading screen that reports what the load is doing.
- Tests live only in crates/approved_tests (heavy_gameplay_lifecycle);
publish-check refuses them anywhere else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Host bots bake a navigation graph off the clip map and walk it — drop and
break-glass edges, routes that resume across ticks under a shared per-tick
query budget, firing positions chosen from baked supports rather than a ring
around the bot. They pick a weapon, reload or switch when the magazine runs
out, and plant, defuse and claim flags on their own.
A broken pane now builds a planar crack graph and emits shards carrying their
holes and open cracks, so tessellation and repeated hits read the stored
geometry instead of assuming a convex fan from vertex zero.
Sun cascades are clipped to the view. Class select and the options menu match
the layout players expect. Weapon definitions load from IW5 and T5 fastfiles.
The test suite is removed: it had grown into scaffolding for the dig rather
than a description of the runtime, and new tests will be written against the
behaviour that stayed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mapent TVs, lights, fuse boxes, lockers, fans, ACs, bins, hydrants,
transformers, copiers, generators, mirrors and cop cars become toys and
vehicles, so a shot husks them, throws the authored FX and explodes.
Weapon, xanim, FPV/body/world-gun/projectile mesh and FX catalogs publish
without the capture cursors and zone slots that stamped them, so the runtime
registry cannot restamp an edge after install. World and smodel tess tables
become published Arcs that extract steals a share of instead of cloning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkgiSVEPTbsVZ1RpARujF6
The zone walk and the runtime were the same types. A catalog carried the
walk's `Ptr → row` link maps, its half-captured names and its scratch into
the published result, so nothing said the linking was over except a
convention — and the runtime, which has no zone open any more, could not
have used them anyway.
Build and published types are now separate for materials, effects, tracers
and reticles: `MaterialDefinitions`, `FxDefinitions`, `TracerDefinitions`
are what a finished build hands on, `publish` is the only way to get one,
and the pointer lookups and absorbs stay behind on the builder. The
presence those pointers stood in for is one shared type, `AuthoredRef {
slot, alias }`. Every published asset type is free of zone pointers except
`WeaponCombatFx`, which waits on splitting `WeaponRegistry` the same way.
That split also turned up dead code the old types had hidden:
`resolve_after_absorb` looked weapon icons up by pointer after `finalize`
had emptied the map, so every lookup there returned `None`.
Map load is a transaction with one terminal outcome. Preflight assembles a
`MatchInstallPlan` — nothing to publish exists until every check that can
refuse has passed — and any refusal writes `MapLoadFailed` for the request,
including "no drawable world", which used to be a `MatchInstalled` the
lifecycle read as success and the UI read as failure. A canceled walk
returns `MatchLoadOutcome` instead of orchestrating on with empty products.
The match carries one material population (`PreparedMatch.materials`)
rather than reaching for it through geometry that may not exist, and the
transport schema around the boundary — the six `LoadCapture` wrappers,
`PreparedSpawn`, the doubly-owned map facts, `ZoneMemory` held to the end
of install — is gone.
Copies that had no reason to exist: the absorbs that already consume their
donor now move its rows instead of cloning every `ModelSkel` and
`RawXAnimParts`; sim and the dyn-ent physics share one `ClipMeshTables`
behind an Arc instead of each holding a deep copy of the collision.
Ownership on the live side follows the same cut. `SessionCore` takes a full
room identity and revision before any transport effect, so a stale
teardown cannot override a different match key. `HostMatchEvent::Applied`
carries no match key, so the peer's phase is its whole guard — it now
passes before anything is written, instead of landing a retired match's
connection id on the live peer on the way to being refused. `ClipStore` owns every PCM
conversion and every terminal decode failure, with Play/Stop on one ordered
channel scoped by namespace, entity and epoch. `SimContent` holds the
immutable definitions behind a consumed builder, so authority and
prediction share one backing instead of deep-copying per tick. Direct UDP
launch is removed; relay, listen and replay stay.
In the renderer each producer publishes whether its rows actually moved,
and the xmodel merge stops re-hashing seven producers' draws, materials and
vertex counts every frame to find out. Payload fields are `pub(crate)`
behind read-only accessors, so a consumer that wanted to finish a
producer's work no longer compiles. Colour extract is split from the
installed world, scene and shadow get their own bind caches, and record
census stays off the hot refusal path.
Docs: `CONTEXT.md` gains "Before shipping: the scaffolding goes back out",
and `make mr` and `cargo xtask mr` point at it. MAP-LOAD describes the
transaction as it now ends.
Scaffolding: the share and census probes that only proved the split are
out. What stays is five tests, each holding several independent systems to
one claim and each verified to fail when that claim is broken:
- one alias carried from two-zone capture through absorb, edge binding,
the alias-chain walk, the prep workers' decode and finally cancel —
every stage has to mean the same clip by it;
- a replacement match driven through all three cores that decide whether
a player is in one (host admission, session view, local load), then
every event the retired match still had in flight, replayed against a
live peer, moving nothing;
- a rejected room view leaving no bridge identity, membership, bootstrap
or admission effect behind;
- the seven draw producers published through their own API and asked of
the consumer that replaced the merge's per-frame hashing: the stamp
moves for exactly the producers whose rows moved;
- authority and prediction proved to share one immutable world by
tracing it, and to keep tracing their own after the other replaces it.