diff --git a/ART_DIRECTION.md b/ART_DIRECTION.md new file mode 100644 index 0000000..b15d660 --- /dev/null +++ b/ART_DIRECTION.md @@ -0,0 +1,114 @@ +# SLIDE — Established Art Direction + +Reference notes captured from the merged art system so new sprites match rather +than clash. Derived from inspecting `runtimeManifest.json` and the on-disk source +art, not from guesswork. + +## Which merge established this + +The graphics turning point was **not a single PR** — it was a chain: + +| PR | Date | What it established | +|---|---|---| +| **#49** (closed, folded into #58) | Jun 2026 | The GTA-style dark cinematic direction and 21 initial art assets | +| **#58** MEGA MERGE | 2026-06-26 | Landed #49's visual overhaul into `main-tL2525` alongside TopDownShooter and ContactProfile | +| **#82** | 2026-07-20 | Repaired `assetManifest`, landed the graphics audit docs | +| **#83** | 2026-07-20 | Wired the generated art library into live renderers via `assetResolver` | +| **#84** | 2026-07-31 | **The real structural win.** Runtime image payload 276 MB to 4.03 MB, added `gridConfig.ts`, `projection.ts`, `worldActorResolver.ts`, and a build-time asset pipeline with an audit gate | +| **#93** | 2026-08-02 | Phaser 3 top-down block scene consuming the sprites, plus police sprites | + +The one that "really made a difference" is **#84** — it replaced ad-hoc +per-renderer coordinate math with a shared projection layer and made the manifest +incapable of drifting from disk. #58 set the *look*; #84 made the look shippable. + +## The look + +Photoreal-leaning digital painting, not pixel art. Near-monochrome. + +- **Palette**: black and charcoal clothing, desaturated. Skin and a weapon's + metal are effectively the only chroma in frame. +- **Wardrobe**: hoodies, balaclavas and ski masks, tactical plate carriers, + cargo pants, work boots. Straps, webbing, pouches. +- **Lighting**: soft top-down key, low contrast, no rim light, no glow. Reads as + overcast rather than neon. +- **Rendering**: fabric weave and leather scuffing are visible. Matte finish + throughout — nothing is glossy. +- **Background**: pure white matte, keyed out at build time by + `scripts/assets/process.mjs`. Never generate on a coloured or transparent + background; generate on white. + +## Technical contract + +| Property | Street / fullbody | Top-down | +|---|---|---| +| Source resolution | 1536 x 2304 (2:3 portrait) | 1920 x 1920 (1:1) | +| Runtime resolution | 427 x 640 | 384 x 384 | +| Runtime format | `.webp` | `.webp` | +| Pivot | (0.5, 1.0) foot contact | (0.5, 0.5) centre | +| Background | white, keyed at build | white, keyed at build | + +Naming: `character_{role}_{sex}_{view}_{state}_v001.png` +Location: `frontend/public/assets/generated/characters/{view}/` + +The audit gate (`frontend/scripts/assets/audit.mjs`) fails the build if the +manifest declares a path that is not on disk, so new art must be added to disk +and the manifest regenerated together. + +## Camera angles + +- **street**: eye-level, full standing figure, front or three-quarter facing. + This is the drive-by and block view. +- **topdown**: steep overhead, roughly 70-80 degrees, not a true 90-degree + bird's eye. The shooter reference shows the top of the hood, both shoulders, + and the boots — you can still read the weapon and the pose. Match this angle + exactly or figures will not sit correctly on the satellite ground plane. +- **fullbody**: eye-level neutral A-pose for roster cards. + +## Coverage + +Run `python3 scripts/sprite_gaps.py` from the repo root for a live matrix. + +### Before batch 2 + +87 of 98 (view, role, state) slots empty and silently falling back. + +- **street**: `lookout`, `driver`, `recruit`, `k9` had zero poses +- **topdown**: every role had one sprite but **`state` was `null` on all of + them**, because the first-generation filenames carried no state token. The + resolver only indexes assets with both a role and a state, so a downed or + arrested member rendered **standing upright** in the raid and block scenes +- **fullbody**: `recruit`, `k9` missing + +### After batch 2 + +62 of 98 remaining, and no role has zero art in any view. + +Added: top-down `downed` and `arrested` for shooter/dealer/enforcer/lookout; +`k9` across all three views plus a street `alert`; `recruit` across all three; +street `idle` for shooter, dealer, lookout and driver. + +Also renamed the six stateless top-down sprites to declare `idle` +(`scripts/name_topdown_states.py`), which is what actually repaired the +standing-when-downed bug — new art alone would not have fixed it. + +### Still open, in priority order + +1. `walk` in both views for every role — nothing animates while moving +2. `aim` in top-down for shooter and enforcer — raid scenes cannot show a + member returning fire +3. street `fire` and `reload` for shooter and enforcer +4. `arrested` in the street view for every role +5. `seated` and `driving` for `driver`, needed by the drive-by car interior + +## Role isolation rule + +`k9` and `police` declare single-entry `ROLE_CHAIN` arrays in +`worldActorResolver.ts`. A single-entry chain sets `isolated = true`, which +suppresses the cross-role Pass 2 search. Without that guard a `k9` resolves to +a hooded man and a `police` unit resolves to a gang shooter — both actively +misinform the player about what is on the tile. Prefer returning `null` and +letting the caller draw a placeholder. + +When adding a new non-gang actor, give it a single-entry chain and add its +role token to `ROLES` in `frontend/scripts/assets/process.mjs`, or the manifest +will record `role: null` and the sprite becomes unreachable. diff --git a/frontend/public/assets/bg_desktop.jpg b/frontend/public/assets/bg_desktop.jpg deleted file mode 100644 index 48788bd..0000000 Binary files a/frontend/public/assets/bg_desktop.jpg and /dev/null differ diff --git a/frontend/public/assets/bg_driveby.jpg b/frontend/public/assets/bg_driveby.jpg deleted file mode 100644 index 3b1dcb4..0000000 Binary files a/frontend/public/assets/bg_driveby.jpg and /dev/null differ diff --git a/frontend/public/assets/bg_menu.jpg b/frontend/public/assets/bg_menu.jpg deleted file mode 100644 index d076452..0000000 Binary files a/frontend/public/assets/bg_menu.jpg and /dev/null differ diff --git a/frontend/public/assets/car_interior_frame.png b/frontend/public/assets/car_interior_frame.png deleted file mode 100644 index 682a43d..0000000 Binary files a/frontend/public/assets/car_interior_frame.png and /dev/null differ diff --git a/frontend/public/assets/crosshair.png b/frontend/public/assets/crosshair.png deleted file mode 100644 index a572a62..0000000 Binary files a/frontend/public/assets/crosshair.png and /dev/null differ diff --git a/frontend/public/assets/game_logo.png b/frontend/public/assets/game_logo.png deleted file mode 100644 index fe72ab8..0000000 Binary files a/frontend/public/assets/game_logo.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.png b/frontend/public/assets/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.png deleted file mode 100644 index 8bc6e73..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_driver_male_fullbody_front_v001.png b/frontend/public/assets/generated/characters/fullbody/character_driver_male_fullbody_front_v001.png deleted file mode 100644 index 5d0fbb9..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_driver_male_fullbody_front_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.png b/frontend/public/assets/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.png deleted file mode 100644 index 26d80fd..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.png b/frontend/public/assets/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.png deleted file mode 100644 index 0f97ddb..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.png b/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.png deleted file mode 100644 index bc56204..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001_original.png b/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001_original.png deleted file mode 100644 index 2a2d13f..0000000 Binary files a/frontend/public/assets/generated/characters/fullbody/character_shooter_male_fullbody_front_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.png b/frontend/public/assets/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.png deleted file mode 100644 index 595ad84..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_driver_male_portrait_v001.png b/frontend/public/assets/generated/characters/portraits/character_driver_male_portrait_v001.png deleted file mode 100644 index d2051ea..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_driver_male_portrait_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v001.png b/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v001.png deleted file mode 100644 index dae4be1..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v002.png b/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v002.png deleted file mode 100644 index 3c6e0a6..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_enforcer_male_portrait_v002.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_lookout_female_portrait_v001.png b/frontend/public/assets/generated/characters/portraits/character_lookout_female_portrait_v001.png deleted file mode 100644 index 9d305d6..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_lookout_female_portrait_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/portraits/character_shooter_male_portrait_v001.png b/frontend/public/assets/generated/characters/portraits/character_shooter_male_portrait_v001.png deleted file mode 100644 index ba580fe..0000000 Binary files a/frontend/public/assets/generated/characters/portraits/character_shooter_male_portrait_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001.png deleted file mode 100644 index 5215dbd..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001_original.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001_original.png deleted file mode 100644 index 70f076b..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_aim_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001.png deleted file mode 100644 index 2c24130..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001_original.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001_original.png deleted file mode 100644 index 75a7a64..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_downed_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001.png deleted file mode 100644 index c4c1549..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001_original.png b/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001_original.png deleted file mode 100644 index cacc450..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_dealer_male_street_hit_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001.png b/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001.png deleted file mode 100644 index 64ae8c2..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001_original.png b/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001_original.png deleted file mode 100644 index 3f661b5..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_enforcer_male_street_idle_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001.png b/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001.png deleted file mode 100644 index 8038c20..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001_original.png b/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001_original.png deleted file mode 100644 index 5a91ba2..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_shooter_male_street_downed_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001.png b/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001.png deleted file mode 100644 index 8fb07da..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001_original.png b/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001_original.png deleted file mode 100644 index ddb847e..0000000 Binary files a/frontend/public/assets/generated/characters/street/character_shooter_male_street_hit_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_dealer_male_blacktee_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_dealer_male_blacktee_topdown_v001.png deleted file mode 100644 index 3beb74a..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_dealer_male_blacktee_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001.png deleted file mode 100644 index dabd3bf..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001_original.png b/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001_original.png deleted file mode 100644 index 0ccbc92..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_driver_male_topdown_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_enforcer_male_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_enforcer_male_topdown_v001.png deleted file mode 100644 index 692c7a8..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_enforcer_male_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001.png deleted file mode 100644 index 0a5746f..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001_original.png b/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001_original.png deleted file mode 100644 index 1ef8d8a..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_lookout_female_topdown_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001.png deleted file mode 100644 index fe6479e..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001_original.png b/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001_original.png deleted file mode 100644 index 2e86e24..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_police_male_topdown_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001.png b/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001.png deleted file mode 100644 index 2b4549b..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001_original.png b/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001_original.png deleted file mode 100644 index 723932f..0000000 Binary files a/frontend/public/assets/generated/characters/topdown/character_shooter_male_topdown_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/effects/fx_combat_sprite_sheet_v001.png b/frontend/public/assets/generated/effects/fx_combat_sprite_sheet_v001.png deleted file mode 100644 index c9da180..0000000 Binary files a/frontend/public/assets/generated/effects/fx_combat_sprite_sheet_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/environments/street/block_stripplaza_day_street_v001.png b/frontend/public/assets/generated/environments/street/block_stripplaza_day_street_v001.png deleted file mode 100644 index 1b828b8..0000000 Binary files a/frontend/public/assets/generated/environments/street/block_stripplaza_day_street_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/environments/street/block_stripplaza_night_street_v001.png b/frontend/public/assets/generated/environments/street/block_stripplaza_night_street_v001.png deleted file mode 100644 index 30f2d60..0000000 Binary files a/frontend/public/assets/generated/environments/street/block_stripplaza_night_street_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/environments/topdown/block_stripplaza_topdown_v001.png b/frontend/public/assets/generated/environments/topdown/block_stripplaza_topdown_v001.png deleted file mode 100644 index d8d555f..0000000 Binary files a/frontend/public/assets/generated/environments/topdown/block_stripplaza_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001.png b/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001.png deleted file mode 100644 index ba99f1f..0000000 Binary files a/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001_original.png b/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001_original.png deleted file mode 100644 index 04013cf..0000000 Binary files a/frontend/public/assets/generated/environments/topdown/vehicle_police_car_topdown_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/products/product_rarity_set_v001.png b/frontend/public/assets/generated/products/product_rarity_set_v001.png deleted file mode 100644 index 6b74ca8..0000000 Binary files a/frontend/public/assets/generated/products/product_rarity_set_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/ui/bullet_cam_ui_v001.png b/frontend/public/assets/generated/ui/bullet_cam_ui_v001.png deleted file mode 100644 index 20a2668..0000000 Binary files a/frontend/public/assets/generated/ui/bullet_cam_ui_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/ui/fps_hud_elements_v001.png b/frontend/public/assets/generated/ui/fps_hud_elements_v001.png deleted file mode 100644 index df306f3..0000000 Binary files a/frontend/public/assets/generated/ui/fps_hud_elements_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/ui/icons/app_icon_set_v001.png b/frontend/public/assets/generated/ui/icons/app_icon_set_v001.png deleted file mode 100644 index 1ae2bc7..0000000 Binary files a/frontend/public/assets/generated/ui/icons/app_icon_set_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/ui/icons/icon_news_v001.png b/frontend/public/assets/generated/ui/icons/icon_news_v001.png deleted file mode 100644 index 0ad45f3..0000000 Binary files a/frontend/public/assets/generated/ui/icons/icon_news_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/ui/security_cam_overlay_v001.png b/frontend/public/assets/generated/ui/security_cam_overlay_v001.png deleted file mode 100644 index df93f19..0000000 Binary files a/frontend/public/assets/generated/ui/security_cam_overlay_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.png b/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.png deleted file mode 100644 index d3f3d0b..0000000 Binary files a/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001_original.png b/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001_original.png deleted file mode 100644 index afe9923..0000000 Binary files a/frontend/public/assets/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.png b/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.png deleted file mode 100644 index c842f4f..0000000 Binary files a/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001_original.png b/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001_original.png deleted file mode 100644 index 0290cb8..0000000 Binary files a/frontend/public/assets/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001_original.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.png b/frontend/public/assets/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.png deleted file mode 100644 index 59f5bd7..0000000 Binary files a/frontend/public/assets/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.png b/frontend/public/assets/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.png deleted file mode 100644 index a21f0d4..0000000 Binary files a/frontend/public/assets/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/weapons/weapon_compact_prop_black_icon_v001.png b/frontend/public/assets/generated/weapons/weapon_compact_prop_black_icon_v001.png deleted file mode 100644 index 8a92ac7..0000000 Binary files a/frontend/public/assets/generated/weapons/weapon_compact_prop_black_icon_v001.png and /dev/null differ diff --git a/frontend/public/assets/generated/weapons/weapon_long_prop_black_icon_v001.png b/frontend/public/assets/generated/weapons/weapon_long_prop_black_icon_v001.png deleted file mode 100644 index b49da2b..0000000 Binary files a/frontend/public/assets/generated/weapons/weapon_long_prop_black_icon_v001.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_attack_new.png b/frontend/public/assets/icons/icon_attack_new.png deleted file mode 100644 index e9a0123..0000000 Binary files a/frontend/public/assets/icons/icon_attack_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_bipndip_new.png b/frontend/public/assets/icons/icon_bipndip_new.png deleted file mode 100644 index d3e95e1..0000000 Binary files a/frontend/public/assets/icons/icon_bipndip_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_casino.png b/frontend/public/assets/icons/icon_casino.png deleted file mode 100644 index efd9708..0000000 Binary files a/frontend/public/assets/icons/icon_casino.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_casino_new.png b/frontend/public/assets/icons/icon_casino_new.png deleted file mode 100644 index 7df0637..0000000 Binary files a/frontend/public/assets/icons/icon_casino_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_contacts_new.png b/frontend/public/assets/icons/icon_contacts_new.png deleted file mode 100644 index 7a41984..0000000 Binary files a/frontend/public/assets/icons/icon_contacts_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_cook.png b/frontend/public/assets/icons/icon_cook.png deleted file mode 100644 index 6e62c85..0000000 Binary files a/frontend/public/assets/icons/icon_cook.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_cook_new.png b/frontend/public/assets/icons/icon_cook_new.png deleted file mode 100644 index 40bc762..0000000 Binary files a/frontend/public/assets/icons/icon_cook_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_crew.png b/frontend/public/assets/icons/icon_crew.png deleted file mode 100644 index b26db2f..0000000 Binary files a/frontend/public/assets/icons/icon_crew.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_crew_new.png b/frontend/public/assets/icons/icon_crew_new.png deleted file mode 100644 index 4bc6f7e..0000000 Binary files a/frontend/public/assets/icons/icon_crew_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_dealt.png b/frontend/public/assets/icons/icon_dealt.png deleted file mode 100644 index 5ad51f0..0000000 Binary files a/frontend/public/assets/icons/icon_dealt.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_dealt_new.png b/frontend/public/assets/icons/icon_dealt_new.png deleted file mode 100644 index 9c71f54..0000000 Binary files a/frontend/public/assets/icons/icon_dealt_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_driveby_new.png b/frontend/public/assets/icons/icon_driveby_new.png deleted file mode 100644 index 71b990b..0000000 Binary files a/frontend/public/assets/icons/icon_driveby_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_graffiti_new.png b/frontend/public/assets/icons/icon_graffiti_new.png deleted file mode 100644 index d2d531e..0000000 Binary files a/frontend/public/assets/icons/icon_graffiti_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_leaderboard_new.png b/frontend/public/assets/icons/icon_leaderboard_new.png deleted file mode 100644 index ab03755..0000000 Binary files a/frontend/public/assets/icons/icon_leaderboard_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_map.png b/frontend/public/assets/icons/icon_map.png deleted file mode 100644 index 13e27fd..0000000 Binary files a/frontend/public/assets/icons/icon_map.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_map_new.png b/frontend/public/assets/icons/icon_map_new.png deleted file mode 100644 index b53e236..0000000 Binary files a/frontend/public/assets/icons/icon_map_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_market.png b/frontend/public/assets/icons/icon_market.png deleted file mode 100644 index f9c0b94..0000000 Binary files a/frontend/public/assets/icons/icon_market.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_market_new.png b/frontend/public/assets/icons/icon_market_new.png deleted file mode 100644 index e967208..0000000 Binary files a/frontend/public/assets/icons/icon_market_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_ops.png b/frontend/public/assets/icons/icon_ops.png deleted file mode 100644 index 682d8b0..0000000 Binary files a/frontend/public/assets/icons/icon_ops.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_ops_new.png b/frontend/public/assets/icons/icon_ops_new.png deleted file mode 100644 index e9fc246..0000000 Binary files a/frontend/public/assets/icons/icon_ops_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_planner_new.png b/frontend/public/assets/icons/icon_planner_new.png deleted file mode 100644 index 095f917..0000000 Binary files a/frontend/public/assets/icons/icon_planner_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_raid_new.png b/frontend/public/assets/icons/icon_raid_new.png deleted file mode 100644 index df3430c..0000000 Binary files a/frontend/public/assets/icons/icon_raid_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_settings.png b/frontend/public/assets/icons/icon_settings.png deleted file mode 100644 index ae76535..0000000 Binary files a/frontend/public/assets/icons/icon_settings.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_settings_new.png b/frontend/public/assets/icons/icon_settings_new.png deleted file mode 100644 index 5be4687..0000000 Binary files a/frontend/public/assets/icons/icon_settings_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_shoebox.png b/frontend/public/assets/icons/icon_shoebox.png deleted file mode 100644 index 057315f..0000000 Binary files a/frontend/public/assets/icons/icon_shoebox.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_shoebox_new.png b/frontend/public/assets/icons/icon_shoebox_new.png deleted file mode 100644 index 4be531f..0000000 Binary files a/frontend/public/assets/icons/icon_shoebox_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_slide.png b/frontend/public/assets/icons/icon_slide.png deleted file mode 100644 index e33bef1..0000000 Binary files a/frontend/public/assets/icons/icon_slide.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_slide_new.png b/frontend/public/assets/icons/icon_slide_new.png deleted file mode 100644 index 72ff938..0000000 Binary files a/frontend/public/assets/icons/icon_slide_new.png and /dev/null differ diff --git a/frontend/public/assets/icons/icon_trap_new.png b/frontend/public/assets/icons/icon_trap_new.png deleted file mode 100644 index a145587..0000000 Binary files a/frontend/public/assets/icons/icon_trap_new.png and /dev/null differ diff --git a/frontend/public/assets/muzzle_flash.png b/frontend/public/assets/muzzle_flash.png deleted file mode 100644 index 99210ae..0000000 Binary files a/frontend/public/assets/muzzle_flash.png and /dev/null differ diff --git a/frontend/public/assets/runtime/bg_desktop.webp b/frontend/public/assets/runtime/bg_desktop.webp new file mode 100644 index 0000000..806e452 Binary files /dev/null and b/frontend/public/assets/runtime/bg_desktop.webp differ diff --git a/frontend/public/assets/runtime/bg_driveby.webp b/frontend/public/assets/runtime/bg_driveby.webp new file mode 100644 index 0000000..e1d4b1c Binary files /dev/null and b/frontend/public/assets/runtime/bg_driveby.webp differ diff --git a/frontend/public/assets/runtime/bg_menu.webp b/frontend/public/assets/runtime/bg_menu.webp new file mode 100644 index 0000000..5bdf7a0 Binary files /dev/null and b/frontend/public/assets/runtime/bg_menu.webp differ diff --git a/frontend/public/assets/runtime/car_interior_frame.webp b/frontend/public/assets/runtime/car_interior_frame.webp new file mode 100644 index 0000000..bf3ca67 Binary files /dev/null and b/frontend/public/assets/runtime/car_interior_frame.webp differ diff --git a/frontend/public/assets/runtime/crosshair.webp b/frontend/public/assets/runtime/crosshair.webp new file mode 100644 index 0000000..82efd9e Binary files /dev/null and b/frontend/public/assets/runtime/crosshair.webp differ diff --git a/frontend/public/assets/runtime/game_logo.webp b/frontend/public/assets/runtime/game_logo.webp new file mode 100644 index 0000000..d553fc7 Binary files /dev/null and b/frontend/public/assets/runtime/game_logo.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.webp new file mode 100644 index 0000000..489bd1e Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_dealer_male_blacktee_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_driver_male_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_driver_male_fullbody_front_v001.webp new file mode 100644 index 0000000..678bf84 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_driver_male_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.webp new file mode 100644 index 0000000..11601be Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_enforcer_male_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_k9_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_k9_fullbody_front_v001.webp new file mode 100644 index 0000000..1b91a93 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_k9_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.webp new file mode 100644 index 0000000..ee9e25a Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_recruit_male_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_recruit_male_fullbody_front_v001.webp new file mode 100644 index 0000000..f8e11d5 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_recruit_male_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.webp b/frontend/public/assets/runtime/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.webp new file mode 100644 index 0000000..c0af577 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.webp new file mode 100644 index 0000000..8dc23a1 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_dealer_male_blacktee_portrait_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_driver_male_portrait_v001.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_driver_male_portrait_v001.webp new file mode 100644 index 0000000..ca33aa6 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_driver_male_portrait_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v001.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v001.webp new file mode 100644 index 0000000..7c9ad24 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v002.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v002.webp new file mode 100644 index 0000000..bf053bf Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_enforcer_male_portrait_v002.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_lookout_female_portrait_v001.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_lookout_female_portrait_v001.webp new file mode 100644 index 0000000..cc9a716 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_lookout_female_portrait_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/portraits/character_shooter_male_portrait_v001.webp b/frontend/public/assets/runtime/generated/characters/portraits/character_shooter_male_portrait_v001.webp new file mode 100644 index 0000000..00b0299 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/portraits/character_shooter_male_portrait_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_aim_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_aim_v001.webp new file mode 100644 index 0000000..dd5516c Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_aim_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_downed_v001.webp new file mode 100644 index 0000000..329bc4f Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_hit_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_hit_v001.webp new file mode 100644 index 0000000..d2f9b6e Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_hit_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_idle_v001.webp new file mode 100644 index 0000000..dca9576 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_dealer_male_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_driver_male_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_driver_male_street_idle_v001.webp new file mode 100644 index 0000000..c0f487c Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_driver_male_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_enforcer_male_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_enforcer_male_street_idle_v001.webp new file mode 100644 index 0000000..c768f32 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_enforcer_male_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_k9_street_alert_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_k9_street_alert_v001.webp new file mode 100644 index 0000000..5a7fb06 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_k9_street_alert_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_k9_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_k9_street_idle_v001.webp new file mode 100644 index 0000000..5092766 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_k9_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_lookout_female_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_lookout_female_street_idle_v001.webp new file mode 100644 index 0000000..a21c9f4 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_lookout_female_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_recruit_male_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_recruit_male_street_idle_v001.webp new file mode 100644 index 0000000..8866106 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_recruit_male_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_downed_v001.webp new file mode 100644 index 0000000..5cf1a82 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_hit_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_hit_v001.webp new file mode 100644 index 0000000..f3f203f Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_hit_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_idle_v001.webp new file mode 100644 index 0000000..4b8526c Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/street/character_shooter_male_street_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_blacktee_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_blacktee_topdown_idle_v001.webp new file mode 100644 index 0000000..431a1bf Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_blacktee_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_arrested_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_arrested_v001.webp new file mode 100644 index 0000000..701a716 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_arrested_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_downed_v001.webp new file mode 100644 index 0000000..f86d374 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_driver_male_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_driver_male_topdown_idle_v001.webp new file mode 100644 index 0000000..71746fa Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_driver_male_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_arrested_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_arrested_v001.webp new file mode 100644 index 0000000..041e2a3 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_arrested_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_downed_v001.webp new file mode 100644 index 0000000..ef774c3 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_idle_v001.webp new file mode 100644 index 0000000..6556ef7 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_downed_v001.webp new file mode 100644 index 0000000..ff0c44c Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_idle_v001.webp new file mode 100644 index 0000000..bfbb5ce Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_k9_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_arrested_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_arrested_v001.webp new file mode 100644 index 0000000..73758ec Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_arrested_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_downed_v001.webp new file mode 100644 index 0000000..90dd18e Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_idle_v001.webp new file mode 100644 index 0000000..7ab1df6 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_police_male_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_police_male_topdown_idle_v001.webp new file mode 100644 index 0000000..a111232 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_police_male_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_recruit_male_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_recruit_male_topdown_idle_v001.webp new file mode 100644 index 0000000..72a196d Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_recruit_male_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_arrested_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_arrested_v001.webp new file mode 100644 index 0000000..1a0eab9 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_arrested_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_downed_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_downed_v001.webp new file mode 100644 index 0000000..d279304 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_downed_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_idle_v001.webp b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_idle_v001.webp new file mode 100644 index 0000000..d32a3e3 Binary files /dev/null and b/frontend/public/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_idle_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/effects/fx_combat_sprite_sheet_v001.webp b/frontend/public/assets/runtime/generated/effects/fx_combat_sprite_sheet_v001.webp new file mode 100644 index 0000000..2e8e564 Binary files /dev/null and b/frontend/public/assets/runtime/generated/effects/fx_combat_sprite_sheet_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_day_street_v001.webp b/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_day_street_v001.webp new file mode 100644 index 0000000..f0343e7 Binary files /dev/null and b/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_day_street_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_night_street_v001.webp b/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_night_street_v001.webp new file mode 100644 index 0000000..2356e3f Binary files /dev/null and b/frontend/public/assets/runtime/generated/environments/street/block_stripplaza_night_street_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/environments/topdown/block_stripplaza_topdown_v001.webp b/frontend/public/assets/runtime/generated/environments/topdown/block_stripplaza_topdown_v001.webp new file mode 100644 index 0000000..a6bf6ad Binary files /dev/null and b/frontend/public/assets/runtime/generated/environments/topdown/block_stripplaza_topdown_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/environments/topdown/vehicle_police_car_topdown_v001.webp b/frontend/public/assets/runtime/generated/environments/topdown/vehicle_police_car_topdown_v001.webp new file mode 100644 index 0000000..cc0e18d Binary files /dev/null and b/frontend/public/assets/runtime/generated/environments/topdown/vehicle_police_car_topdown_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/products/product_rarity_set_v001.webp b/frontend/public/assets/runtime/generated/products/product_rarity_set_v001.webp new file mode 100644 index 0000000..6513063 Binary files /dev/null and b/frontend/public/assets/runtime/generated/products/product_rarity_set_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/ui/bullet_cam_ui_v001.webp b/frontend/public/assets/runtime/generated/ui/bullet_cam_ui_v001.webp new file mode 100644 index 0000000..6d005cd Binary files /dev/null and b/frontend/public/assets/runtime/generated/ui/bullet_cam_ui_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/ui/fps_hud_elements_v001.webp b/frontend/public/assets/runtime/generated/ui/fps_hud_elements_v001.webp new file mode 100644 index 0000000..c5680fd Binary files /dev/null and b/frontend/public/assets/runtime/generated/ui/fps_hud_elements_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/ui/icons/app_icon_set_v001.webp b/frontend/public/assets/runtime/generated/ui/icons/app_icon_set_v001.webp new file mode 100644 index 0000000..391ef25 Binary files /dev/null and b/frontend/public/assets/runtime/generated/ui/icons/app_icon_set_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/ui/icons/icon_news_v001.webp b/frontend/public/assets/runtime/generated/ui/icons/icon_news_v001.webp new file mode 100644 index 0000000..e948727 Binary files /dev/null and b/frontend/public/assets/runtime/generated/ui/icons/icon_news_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/ui/security_cam_overlay_v001.webp b/frontend/public/assets/runtime/generated/ui/security_cam_overlay_v001.webp new file mode 100644 index 0000000..9714ca1 Binary files /dev/null and b/frontend/public/assets/runtime/generated/ui/security_cam_overlay_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.webp b/frontend/public/assets/runtime/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.webp new file mode 100644 index 0000000..a99f5ec Binary files /dev/null and b/frontend/public/assets/runtime/generated/vehicles/damage/vehicle_luxury_sedan_black_damage_light_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.webp b/frontend/public/assets/runtime/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.webp new file mode 100644 index 0000000..8822901 Binary files /dev/null and b/frontend/public/assets/runtime/generated/vehicles/overlays/vehicle_luxury_sedan_passenger_overlay_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.webp b/frontend/public/assets/runtime/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.webp new file mode 100644 index 0000000..0835d3e Binary files /dev/null and b/frontend/public/assets/runtime/generated/vehicles/street/vehicle_luxury_sedan_black_street_side_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.webp b/frontend/public/assets/runtime/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.webp new file mode 100644 index 0000000..3f64fa0 Binary files /dev/null and b/frontend/public/assets/runtime/generated/vehicles/topdown/vehicle_luxury_sedan_black_topdown_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/weapons/weapon_compact_prop_black_icon_v001.webp b/frontend/public/assets/runtime/generated/weapons/weapon_compact_prop_black_icon_v001.webp new file mode 100644 index 0000000..52b9f6e Binary files /dev/null and b/frontend/public/assets/runtime/generated/weapons/weapon_compact_prop_black_icon_v001.webp differ diff --git a/frontend/public/assets/runtime/generated/weapons/weapon_long_prop_black_icon_v001.webp b/frontend/public/assets/runtime/generated/weapons/weapon_long_prop_black_icon_v001.webp new file mode 100644 index 0000000..c3687f1 Binary files /dev/null and b/frontend/public/assets/runtime/generated/weapons/weapon_long_prop_black_icon_v001.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_attack_new.webp b/frontend/public/assets/runtime/icons/icon_attack_new.webp new file mode 100644 index 0000000..8d86b95 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_attack_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_bipndip_new.webp b/frontend/public/assets/runtime/icons/icon_bipndip_new.webp new file mode 100644 index 0000000..4ce20aa Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_bipndip_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_casino.webp b/frontend/public/assets/runtime/icons/icon_casino.webp new file mode 100644 index 0000000..920726b Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_casino.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_casino_new.webp b/frontend/public/assets/runtime/icons/icon_casino_new.webp new file mode 100644 index 0000000..07032ad Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_casino_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_contacts_new.webp b/frontend/public/assets/runtime/icons/icon_contacts_new.webp new file mode 100644 index 0000000..a466b06 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_contacts_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_cook.webp b/frontend/public/assets/runtime/icons/icon_cook.webp new file mode 100644 index 0000000..f14304c Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_cook.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_cook_new.webp b/frontend/public/assets/runtime/icons/icon_cook_new.webp new file mode 100644 index 0000000..1a93482 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_cook_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_crew.webp b/frontend/public/assets/runtime/icons/icon_crew.webp new file mode 100644 index 0000000..bc3250b Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_crew.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_crew_new.webp b/frontend/public/assets/runtime/icons/icon_crew_new.webp new file mode 100644 index 0000000..d066d9d Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_crew_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_dealt.webp b/frontend/public/assets/runtime/icons/icon_dealt.webp new file mode 100644 index 0000000..9e812e3 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_dealt.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_dealt_new.webp b/frontend/public/assets/runtime/icons/icon_dealt_new.webp new file mode 100644 index 0000000..b2a12d0 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_dealt_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_driveby_new.webp b/frontend/public/assets/runtime/icons/icon_driveby_new.webp new file mode 100644 index 0000000..283350d Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_driveby_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_graffiti_new.webp b/frontend/public/assets/runtime/icons/icon_graffiti_new.webp new file mode 100644 index 0000000..99aae9a Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_graffiti_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_leaderboard_new.webp b/frontend/public/assets/runtime/icons/icon_leaderboard_new.webp new file mode 100644 index 0000000..aeb5ed2 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_leaderboard_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_map.webp b/frontend/public/assets/runtime/icons/icon_map.webp new file mode 100644 index 0000000..21b16cf Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_map.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_map_new.webp b/frontend/public/assets/runtime/icons/icon_map_new.webp new file mode 100644 index 0000000..8fb4c0d Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_map_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_market.webp b/frontend/public/assets/runtime/icons/icon_market.webp new file mode 100644 index 0000000..4f98681 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_market.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_market_new.webp b/frontend/public/assets/runtime/icons/icon_market_new.webp new file mode 100644 index 0000000..f747d6e Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_market_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_ops.webp b/frontend/public/assets/runtime/icons/icon_ops.webp new file mode 100644 index 0000000..b39e1fa Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_ops.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_ops_new.webp b/frontend/public/assets/runtime/icons/icon_ops_new.webp new file mode 100644 index 0000000..46c8b7d Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_ops_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_planner_new.webp b/frontend/public/assets/runtime/icons/icon_planner_new.webp new file mode 100644 index 0000000..f5beed6 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_planner_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_raid_new.webp b/frontend/public/assets/runtime/icons/icon_raid_new.webp new file mode 100644 index 0000000..fbf11e7 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_raid_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_settings.webp b/frontend/public/assets/runtime/icons/icon_settings.webp new file mode 100644 index 0000000..38bdabb Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_settings.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_settings_new.webp b/frontend/public/assets/runtime/icons/icon_settings_new.webp new file mode 100644 index 0000000..fdbcd03 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_settings_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_shoebox.webp b/frontend/public/assets/runtime/icons/icon_shoebox.webp new file mode 100644 index 0000000..bf5d566 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_shoebox.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_shoebox_new.webp b/frontend/public/assets/runtime/icons/icon_shoebox_new.webp new file mode 100644 index 0000000..a3edfbc Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_shoebox_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_slide.webp b/frontend/public/assets/runtime/icons/icon_slide.webp new file mode 100644 index 0000000..fdbc057 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_slide.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_slide_new.webp b/frontend/public/assets/runtime/icons/icon_slide_new.webp new file mode 100644 index 0000000..4275f52 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_slide_new.webp differ diff --git a/frontend/public/assets/runtime/icons/icon_trap_new.webp b/frontend/public/assets/runtime/icons/icon_trap_new.webp new file mode 100644 index 0000000..76dc4d5 Binary files /dev/null and b/frontend/public/assets/runtime/icons/icon_trap_new.webp differ diff --git a/frontend/public/assets/runtime/muzzle_flash.webp b/frontend/public/assets/runtime/muzzle_flash.webp new file mode 100644 index 0000000..5369723 Binary files /dev/null and b/frontend/public/assets/runtime/muzzle_flash.webp differ diff --git a/frontend/public/assets/runtime/skyline_layer.webp b/frontend/public/assets/runtime/skyline_layer.webp new file mode 100644 index 0000000..eaddfe8 Binary files /dev/null and b/frontend/public/assets/runtime/skyline_layer.webp differ diff --git a/frontend/public/assets/runtime/sprites/drugs.webp b/frontend/public/assets/runtime/sprites/drugs.webp new file mode 100644 index 0000000..33cc5b6 Binary files /dev/null and b/frontend/public/assets/runtime/sprites/drugs.webp differ diff --git a/frontend/public/assets/runtime/sprites/terrain.webp b/frontend/public/assets/runtime/sprites/terrain.webp new file mode 100644 index 0000000..e094703 Binary files /dev/null and b/frontend/public/assets/runtime/sprites/terrain.webp differ diff --git a/frontend/public/assets/runtime/sprites/weapons.webp b/frontend/public/assets/runtime/sprites/weapons.webp new file mode 100644 index 0000000..f3cb5aa Binary files /dev/null and b/frontend/public/assets/runtime/sprites/weapons.webp differ diff --git a/frontend/public/assets/runtime/street_bg_layer.webp b/frontend/public/assets/runtime/street_bg_layer.webp new file mode 100644 index 0000000..8c41b25 Binary files /dev/null and b/frontend/public/assets/runtime/street_bg_layer.webp differ diff --git a/frontend/public/assets/runtime/street_fg_layer.webp b/frontend/public/assets/runtime/street_fg_layer.webp new file mode 100644 index 0000000..86005cc Binary files /dev/null and b/frontend/public/assets/runtime/street_fg_layer.webp differ diff --git a/frontend/public/assets/skyline_layer.jpg b/frontend/public/assets/skyline_layer.jpg deleted file mode 100644 index 2bc1080..0000000 Binary files a/frontend/public/assets/skyline_layer.jpg and /dev/null differ diff --git a/frontend/public/assets/sprites/drugs.png b/frontend/public/assets/sprites/drugs.png deleted file mode 100644 index a40a0b8..0000000 Binary files a/frontend/public/assets/sprites/drugs.png and /dev/null differ diff --git a/frontend/public/assets/sprites/gang_members.png b/frontend/public/assets/sprites/gang_members.png deleted file mode 100644 index 4ee23f0..0000000 Binary files a/frontend/public/assets/sprites/gang_members.png and /dev/null differ diff --git a/frontend/public/assets/sprites/terrain.png b/frontend/public/assets/sprites/terrain.png deleted file mode 100644 index 36abd45..0000000 Binary files a/frontend/public/assets/sprites/terrain.png and /dev/null differ diff --git a/frontend/public/assets/sprites/weapons.png b/frontend/public/assets/sprites/weapons.png deleted file mode 100644 index 33ac84d..0000000 Binary files a/frontend/public/assets/sprites/weapons.png and /dev/null differ diff --git a/frontend/public/assets/street_bg_layer.jpg b/frontend/public/assets/street_bg_layer.jpg deleted file mode 100644 index 8973746..0000000 Binary files a/frontend/public/assets/street_bg_layer.jpg and /dev/null differ diff --git a/frontend/public/assets/street_fg_layer.jpg b/frontend/public/assets/street_fg_layer.jpg deleted file mode 100644 index c76d54e..0000000 Binary files a/frontend/public/assets/street_fg_layer.jpg and /dev/null differ diff --git a/frontend/scripts/assets/process.mjs b/frontend/scripts/assets/process.mjs index 70e8825..f887642 100644 --- a/frontend/scripts/assets/process.mjs +++ b/frontend/scripts/assets/process.mjs @@ -83,7 +83,7 @@ function parseName(file) { const base = path.basename(file).replace(/\.[^.]+$/, ''); const parts = base.split('_'); const STATES = ['idle','walk','aim','fire','reload','hit','downed','dead','arrested','seated','driving','alert','front','side']; - const ROLES = ['dealer','shooter','enforcer','lookout','driver','recruit','chemist','runner','boss','civilian','rival']; + const ROLES = ['dealer','shooter','enforcer','lookout','driver','recruit','chemist','runner','boss','civilian','rival','k9','police']; return { role: parts.find((p) => ROLES.includes(p)) ?? null, state: parts.reverse().find((p) => STATES.includes(p)) ?? null, diff --git a/frontend/src/assets/runtimeManifest.json b/frontend/src/assets/runtimeManifest.json index 60f04ce..795dff1 100644 --- a/frontend/src/assets/runtimeManifest.json +++ b/frontend/src/assets/runtimeManifest.json @@ -1,7 +1,7 @@ { - "generatedAt": "2026-07-31T00:31:43.737Z", + "generatedAt": "2026-08-06T02:36:25.102Z", "budgetMB": 20, - "totalBytes": 4224336, + "totalBytes": 5605208, "qualityPass": "standard", "entries": [ { @@ -166,6 +166,24 @@ }, "alphaRepaired": true }, + { + "id": "generated.characters.fullbody.character_k9_fullbody_front_v001", + "runtimePath": "/assets/runtime/generated/characters/fullbody/character_k9_fullbody_front_v001.webp", + "sourcePath": "art-src/generated/characters/fullbody/character_k9_fullbody_front_v001.png", + "class": "actor-fullbody", + "role": "k9", + "state": "front", + "width": 427, + "height": 640, + "bytes": 42850, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, { "id": "generated.characters.fullbody.character_lookout_female_fullbody_front_v001", "runtimePath": "/assets/runtime/generated/characters/fullbody/character_lookout_female_fullbody_front_v001.webp", @@ -184,6 +202,24 @@ }, "alphaRepaired": true }, + { + "id": "generated.characters.fullbody.character_recruit_male_fullbody_front_v001", + "runtimePath": "/assets/runtime/generated/characters/fullbody/character_recruit_male_fullbody_front_v001.webp", + "sourcePath": "art-src/generated/characters/fullbody/character_recruit_male_fullbody_front_v001.png", + "class": "actor-fullbody", + "role": "recruit", + "state": "front", + "width": 427, + "height": 640, + "bytes": 23084, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, { "id": "generated.characters.fullbody.character_shooter_male_fullbody_front_v001", "runtimePath": "/assets/runtime/generated/characters/fullbody/character_shooter_male_fullbody_front_v001.webp", @@ -364,6 +400,42 @@ }, "alphaRepaired": false }, + { + "id": "generated.characters.street.character_dealer_male_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_dealer_male_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_dealer_male_street_idle_v001.png", + "class": "actor-street", + "role": "dealer", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 26868, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.street.character_driver_male_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_driver_male_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_driver_male_street_idle_v001.png", + "class": "actor-street", + "role": "driver", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 23622, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, { "id": "generated.characters.street.character_enforcer_male_street_idle_v001", "runtimePath": "/assets/runtime/generated/characters/street/character_enforcer_male_street_idle_v001.webp", @@ -382,6 +454,78 @@ }, "alphaRepaired": false }, + { + "id": "generated.characters.street.character_k9_street_alert_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_k9_street_alert_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_k9_street_alert_v001.png", + "class": "actor-street", + "role": "k9", + "state": "alert", + "width": 427, + "height": 640, + "bytes": 36352, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.street.character_k9_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_k9_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_k9_street_idle_v001.png", + "class": "actor-street", + "role": "k9", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 46378, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.street.character_lookout_female_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_lookout_female_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_lookout_female_street_idle_v001.png", + "class": "actor-street", + "role": "lookout", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 21858, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.street.character_recruit_male_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_recruit_male_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_recruit_male_street_idle_v001.png", + "class": "actor-street", + "role": "recruit", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 21348, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, { "id": "generated.characters.street.character_shooter_male_street_downed_v001", "runtimePath": "/assets/runtime/generated/characters/street/character_shooter_male_street_downed_v001.webp", @@ -419,12 +563,30 @@ "alphaRepaired": false }, { - "id": "generated.characters.topdown.character_dealer_male_blacktee_topdown_v001", - "runtimePath": "/assets/runtime/generated/characters/topdown/character_dealer_male_blacktee_topdown_v001.webp", - "sourcePath": "art-src/generated/characters/topdown/character_dealer_male_blacktee_topdown_v001.png", + "id": "generated.characters.street.character_shooter_male_street_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/street/character_shooter_male_street_idle_v001.webp", + "sourcePath": "art-src/generated/characters/street/character_shooter_male_street_idle_v001.png", + "class": "actor-street", + "role": "shooter", + "state": "idle", + "width": 427, + "height": 640, + "bytes": 29510, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 1 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_dealer_male_blacktee_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_dealer_male_blacktee_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_dealer_male_blacktee_topdown_idle_v001.png", "class": "actor-topdown", "role": "dealer", - "state": null, + "state": "idle", "width": 384, "height": 384, "bytes": 12014, @@ -437,12 +599,48 @@ "alphaRepaired": true }, { - "id": "generated.characters.topdown.character_driver_male_topdown_v001", - "runtimePath": "/assets/runtime/generated/characters/topdown/character_driver_male_topdown_v001.webp", - "sourcePath": "art-src/generated/characters/topdown/character_driver_male_topdown_v001.png", + "id": "generated.characters.topdown.character_dealer_male_topdown_arrested_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_arrested_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_dealer_male_topdown_arrested_v001.png", + "class": "actor-topdown", + "role": "dealer", + "state": "arrested", + "width": 384, + "height": 384, + "bytes": 16738, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_dealer_male_topdown_downed_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_dealer_male_topdown_downed_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_dealer_male_topdown_downed_v001.png", + "class": "actor-topdown", + "role": "dealer", + "state": "downed", + "width": 384, + "height": 384, + "bytes": 19238, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_driver_male_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_driver_male_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_driver_male_topdown_idle_v001.png", "class": "actor-topdown", "role": "driver", - "state": null, + "state": "idle", "width": 384, "height": 384, "bytes": 16482, @@ -455,12 +653,48 @@ "alphaRepaired": false }, { - "id": "generated.characters.topdown.character_enforcer_male_topdown_v001", - "runtimePath": "/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_v001.webp", - "sourcePath": "art-src/generated/characters/topdown/character_enforcer_male_topdown_v001.png", + "id": "generated.characters.topdown.character_enforcer_male_topdown_arrested_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_arrested_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_enforcer_male_topdown_arrested_v001.png", "class": "actor-topdown", "role": "enforcer", - "state": null, + "state": "arrested", + "width": 384, + "height": 384, + "bytes": 17978, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_enforcer_male_topdown_downed_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_downed_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_enforcer_male_topdown_downed_v001.png", + "class": "actor-topdown", + "role": "enforcer", + "state": "downed", + "width": 384, + "height": 384, + "bytes": 18776, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_enforcer_male_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_enforcer_male_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_enforcer_male_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "enforcer", + "state": "idle", "width": 384, "height": 384, "bytes": 16570, @@ -473,12 +707,84 @@ "alphaRepaired": true }, { - "id": "generated.characters.topdown.character_lookout_female_topdown_v001", - "runtimePath": "/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_v001.webp", - "sourcePath": "art-src/generated/characters/topdown/character_lookout_female_topdown_v001.png", + "id": "generated.characters.topdown.character_k9_topdown_downed_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_k9_topdown_downed_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_k9_topdown_downed_v001.png", + "class": "actor-topdown", + "role": "k9", + "state": "downed", + "width": 384, + "height": 384, + "bytes": 25074, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_k9_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_k9_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_k9_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "k9", + "state": "idle", + "width": 384, + "height": 384, + "bytes": 18856, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_lookout_female_topdown_arrested_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_arrested_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_lookout_female_topdown_arrested_v001.png", "class": "actor-topdown", "role": "lookout", - "state": null, + "state": "arrested", + "width": 384, + "height": 384, + "bytes": 15534, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_lookout_female_topdown_downed_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_downed_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_lookout_female_topdown_downed_v001.png", + "class": "actor-topdown", + "role": "lookout", + "state": "downed", + "width": 384, + "height": 384, + "bytes": 19380, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_lookout_female_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_lookout_female_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_lookout_female_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "lookout", + "state": "idle", "width": 384, "height": 384, "bytes": 18998, @@ -491,12 +797,84 @@ "alphaRepaired": false }, { - "id": "generated.characters.topdown.character_shooter_male_topdown_v001", - "runtimePath": "/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_v001.webp", - "sourcePath": "art-src/generated/characters/topdown/character_shooter_male_topdown_v001.png", + "id": "generated.characters.topdown.character_police_male_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_police_male_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_police_male_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "police", + "state": "idle", + "width": 384, + "height": 384, + "bytes": 52134, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": false + }, + { + "id": "generated.characters.topdown.character_recruit_male_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_recruit_male_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_recruit_male_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "recruit", + "state": "idle", + "width": 384, + "height": 384, + "bytes": 13186, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_shooter_male_topdown_arrested_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_arrested_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_shooter_male_topdown_arrested_v001.png", "class": "actor-topdown", "role": "shooter", - "state": null, + "state": "arrested", + "width": 384, + "height": 384, + "bytes": 13774, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_shooter_male_topdown_downed_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_downed_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_shooter_male_topdown_downed_v001.png", + "class": "actor-topdown", + "role": "shooter", + "state": "downed", + "width": 384, + "height": 384, + "bytes": 21134, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "generated.characters.topdown.character_shooter_male_topdown_idle_v001", + "runtimePath": "/assets/runtime/generated/characters/topdown/character_shooter_male_topdown_idle_v001.webp", + "sourcePath": "art-src/generated/characters/topdown/character_shooter_male_topdown_idle_v001.png", + "class": "actor-topdown", + "role": "shooter", + "state": "idle", "width": 384, "height": 384, "bytes": 11120, @@ -580,6 +958,24 @@ }, "alphaRepaired": false }, + { + "id": "generated.environments.topdown.vehicle_police_car_topdown_v001", + "runtimePath": "/assets/runtime/generated/environments/topdown/vehicle_police_car_topdown_v001.webp", + "sourcePath": "art-src/generated/environments/topdown/vehicle_police_car_topdown_v001.png", + "class": "env-topdown", + "role": "police", + "state": null, + "width": 1536, + "height": 1536, + "bytes": 416160, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": false + }, { "id": "generated.products.product_rarity_set_v001", "runtimePath": "/assets/runtime/generated/products/product_rarity_set_v001.webp", @@ -796,6 +1192,42 @@ }, "alphaRepaired": true }, + { + "id": "icons.icon_attack_new", + "runtimePath": "/assets/runtime/icons/icon_attack_new.webp", + "sourcePath": "art-src/icons/icon_attack_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 44922, + "hasAlpha": false, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "icons.icon_bipndip_new", + "runtimePath": "/assets/runtime/icons/icon_bipndip_new.webp", + "sourcePath": "art-src/icons/icon_bipndip_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 31620, + "hasAlpha": false, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, { "id": "icons.icon_casino", "runtimePath": "/assets/runtime/icons/icon_casino.webp", @@ -832,6 +1264,24 @@ }, "alphaRepaired": false }, + { + "id": "icons.icon_contacts_new", + "runtimePath": "/assets/runtime/icons/icon_contacts_new.webp", + "sourcePath": "art-src/icons/icon_contacts_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 137556, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, { "id": "icons.icon_cook", "runtimePath": "/assets/runtime/icons/icon_cook.webp", @@ -976,6 +1426,24 @@ }, "alphaRepaired": false }, + { + "id": "icons.icon_leaderboard_new", + "runtimePath": "/assets/runtime/icons/icon_leaderboard_new.webp", + "sourcePath": "art-src/icons/icon_leaderboard_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 70408, + "hasAlpha": true, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, { "id": "icons.icon_map", "runtimePath": "/assets/runtime/icons/icon_map.webp", @@ -1084,6 +1552,42 @@ }, "alphaRepaired": false }, + { + "id": "icons.icon_planner_new", + "runtimePath": "/assets/runtime/icons/icon_planner_new.webp", + "sourcePath": "art-src/icons/icon_planner_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 59088, + "hasAlpha": false, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, + { + "id": "icons.icon_raid_new", + "runtimePath": "/assets/runtime/icons/icon_raid_new.webp", + "sourcePath": "art-src/icons/icon_raid_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 46500, + "hasAlpha": false, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, { "id": "icons.icon_settings", "runtimePath": "/assets/runtime/icons/icon_settings.webp", @@ -1192,6 +1696,24 @@ }, "alphaRepaired": false }, + { + "id": "icons.icon_trap_new", + "runtimePath": "/assets/runtime/icons/icon_trap_new.webp", + "sourcePath": "art-src/icons/icon_trap_new.png", + "class": "misc", + "role": null, + "state": null, + "width": 512, + "height": 512, + "bytes": 50946, + "hasAlpha": false, + "fringeRatio": 0, + "pivot": { + "x": 0.5, + "y": 0.5 + }, + "alphaRepaired": true + }, { "id": "muzzle_flash", "runtimePath": "/assets/runtime/muzzle_flash.webp", diff --git a/frontend/src/components/topdown/blockTopDownCoords.ts b/frontend/src/components/topdown/blockTopDownCoords.ts index 7cd5eb4..97a4144 100644 --- a/frontend/src/components/topdown/blockTopDownCoords.ts +++ b/frontend/src/components/topdown/blockTopDownCoords.ts @@ -49,6 +49,11 @@ export const ROLE_TINT: Record = { chemist: 0xa78bfa, runner: 0xfb7185, boss: 0xfbbf24, + // Hireable marketplace roles + recruit: 0x94a3b8, // slate — unproven + k9: 0xd97706, // amber — dog + // NPC + police: 0x3b82f6, // blue }; /** Map a role to the topdown sprite filename (without extension) */ @@ -62,6 +67,11 @@ export const ROLE_TO_SPRITE: Record = { chemist: 'character_dealer_male_blacktee_topdown_v001', runner: 'character_lookout_female_topdown_v001', boss: 'character_enforcer_male_topdown_v001', + // Hireable marketplace roles (worldActorResolver has real art for these) + recruit: 'character_recruit_male_topdown_v001', + k9: 'character_k9_topdown_v001', + // NPC + police: 'character_police_male_topdown_v001', }; /** Sprite asset path prefix (served from /public) */ diff --git a/frontend/src/render/__tests__/worldActorResolver.test.ts b/frontend/src/render/__tests__/worldActorResolver.test.ts index 80ba95d..fd6157f 100644 --- a/frontend/src/render/__tests__/worldActorResolver.test.ts +++ b/frontend/src/render/__tests__/worldActorResolver.test.ts @@ -87,3 +87,124 @@ describe('fallback correctness — state beats role', () => { } }); }); + +// ───────────────────────────────────────────────────────────── +// Sprite coverage batch 2 +// +// The bug these cover: every first-generation top-down sprite was named +// `character_{role}_{sex}_topdown_v001.png` with no state token, so +// `process.mjs::parseName` recorded `state: null`. The resolver only indexes +// assets that have BOTH a role and a state, so top-down art was reachable +// only through the last-resort byRole pass — and a downed or arrested member +// rendered standing upright in the raid and block scenes. +// ───────────────────────────────────────────────────────────── + +describe('top-down view — state must be honoured', () => { + it('indexes a top-down idle for every deployable role', () => { + for (const role of ['dealer', 'shooter', 'enforcer', 'lookout', 'driver', 'recruit']) { + const a = getWorldActor(role, 'idle', 'topdown'); + expect(a, role).not.toBeNull(); + } + }); + + it('never renders a downed member standing in the top-down view', () => { + // This is the assertion that would have caught the original bug: before + // the rename, resolvedState came back as 'idle' for every one of these. + for (const role of ['dealer', 'shooter', 'enforcer', 'lookout']) { + const a = getWorldActor(role, 'downed', 'topdown'); + expect(a, role).not.toBeNull(); + expect(['downed', 'dead', 'hit'], `${role} downed -> ${a!.resolvedState}`) + .toContain(a!.resolvedState); + } + }); + + it('renders arrested as restrained, not as a standing or downed pose', () => { + for (const role of ['dealer', 'shooter', 'enforcer', 'lookout']) { + const a = getWorldActor(role, 'arrested', 'topdown'); + expect(a, role).not.toBeNull(); + expect(a!.resolvedState, `${role} arrested -> ${a!.resolvedState}`).toBe('arrested'); + } + }); + + it('resolves top-down art from the top-down view, not a street sprite', () => { + const a = getWorldActor('shooter', 'downed', 'topdown'); + expect(a!.url).toMatch(/\/topdown\//); + }); + + it('uses a centre pivot for top-down actors', () => { + const a = getWorldActor('shooter', 'idle', 'topdown'); + expect(a!.pivot).toEqual({ x: 0.5, y: 0.5 }); + }); +}); + +describe('isolated roles must never borrow another role\'s art', () => { + // A k9 wearing a hooded man's silhouette, or a police unit wearing a gang + // shooter's, actively misinforms the player about what is on the tile. Both + // roles declare a single-entry ROLE_CHAIN, which suppresses the cross-role + // Pass 2 search. + + it('resolves k9 art that is actually k9 art', () => { + for (const view of ['street', 'topdown', 'fullbody'] as const) { + const a = getWorldActor('k9', 'idle', view); + expect(a, `k9/${view}`).not.toBeNull(); + expect(a!.url, `k9/${view} -> ${a!.url}`).toMatch(/character_k9_/); + } + }); + + it('never dresses a k9 in a human sprite for a state it lacks', () => { + // k9 has no 'aim' or 'arrested' art. The resolver must degrade within the + // k9 set or return null — never reach for a person. + for (const state of ['aim', 'fire', 'arrested', 'seated'] as const) { + const a = getWorldActor('k9', state, 'street'); + if (a) expect(a.url, `k9/${state} -> ${a.url}`).toMatch(/character_k9_/); + } + }); + + it('keeps police art out of gang roles and gang art out of police', () => { + const police = getWorldActor('police', 'idle', 'topdown'); + if (police) expect(police.url).toMatch(/character_police_/); + + for (const role of ['dealer', 'shooter', 'enforcer', 'lookout']) { + for (const state of ['idle', 'downed', 'arrested'] as const) { + const a = getWorldActor(role, state, 'topdown'); + expect(a!.url, `${role}/${state} leaked police art`).not.toMatch(/character_police_/); + } + } + }); + + it('reports k9 and police as known roles in the manifest', () => { + const roles = manifestStats().roles; + expect(roles).toContain('k9'); + expect(roles).toContain('police'); + }); +}); + +describe('street coverage added in batch 2', () => { + it('gives shooter and dealer a true idle rather than an aim pose', () => { + for (const role of ['shooter', 'dealer']) { + const a = getWorldActor(role, 'idle', 'street'); + expect(a!.resolvedState, `${role} idle -> ${a!.resolvedState}`).toBe('idle'); + expect(a!.isFallback, `${role} idle should now be exact`).toBe(false); + } + }); + + it('gives lookout, driver and recruit their own street idle', () => { + for (const role of ['lookout', 'driver', 'recruit']) { + const a = getWorldActor(role, 'idle', 'street'); + expect(a!.isFallback, `${role} idle should now be exact`).toBe(false); + expect(a!.url).toMatch(new RegExp(`character_${role}_`)); + } + }); + + it('gives recruit a fullbody roster pose of its own', () => { + const a = getWorldActor('recruit', 'idle', 'fullbody'); + expect(a!.url).toMatch(/character_recruit_male_fullbody_front/); + }); + + it('resolves k9 alert as a distinct pose from k9 idle', () => { + const idle = getWorldActor('k9', 'idle', 'street'); + const alert = getWorldActor('k9', 'alert', 'street'); + expect(alert!.resolvedState).toBe('alert'); + expect(alert!.url).not.toBe(idle!.url); + }); +}); diff --git a/frontend/src/render/worldActorResolver.ts b/frontend/src/render/worldActorResolver.ts index af8119f..811b73d 100644 --- a/frontend/src/render/worldActorResolver.ts +++ b/frontend/src/render/worldActorResolver.ts @@ -118,6 +118,18 @@ const ROLE_CHAIN: Record = { chemist: ['chemist', 'dealer'], runner: ['runner', 'driver', 'dealer'], boss: ['boss', 'enforcer', 'shooter'], + + // A dog has no human fallback. Borrowing a person's silhouette for a k9 + // would put a standing man on a tile the player assigned an animal to, + // which misreads worse than no sprite at all — so the chain terminates + // here and getWorldActor() returns null, letting the caller draw its own + // placeholder. + k9: ['k9'], + + // Police are not gang roles and must never borrow gang art: a raid tile + // showing a hooded shooter instead of an officer inverts the threat the + // player is reading. + police: ['police'], }; /** View degradation: a street pose beats a top-down chip beats a full body. */ @@ -155,6 +167,12 @@ export function getWorldActor( const views = VIEW_CHAIN[view]; const states = STATE_CHAIN[state] ?? [state, 'idle']; + // A single-entry chain is a declaration that this role must never wear + // another role's art. Pass 2 searches by state across ALL roles, so without + // this guard an isolated role would leak straight through it — a k9 would + // resolve to a hooded man and police would resolve to a gang shooter. + const isolated = roles.length === 1; + // Pass 1 — exhaust every (view, role, state) triple in the declared // chains before considering anything outside them. for (const v of views) { @@ -182,7 +200,7 @@ export function getWorldActor( // than showing a slightly wrong silhouette. for (const v of views) { for (const s of states) { - const match = ASSETS.find( + const match = isolated ? undefined : ASSETS.find( (a) => VIEW_CLASS[v] === a.class && a.state === s, ); if (!match) continue; diff --git a/frontend/src/stores/blockStore.ts b/frontend/src/stores/blockStore.ts index 611912f..b39481a 100644 --- a/frontend/src/stores/blockStore.ts +++ b/frontend/src/stores/blockStore.ts @@ -62,12 +62,17 @@ function generateDefaultGrid(): BlockZone[][] { ); } +/** Roles that generate income when placed on a block */ +const INCOME_ROLES = new Set(['dealer', 'chemist', 'runner']); + function calcPlacementIncome(placement: BlockPlacement, grid: BlockZone[][]): number { const zone = grid[placement.y]?.[placement.x]; - if (!zone || placement.role !== 'dealer') return 0; + if (!zone || !INCOME_ROLES.has(placement.role)) return 0; const base = zone.incomeModifier; const levelBonus = 1 + (placement.level - 1) * 0.12; // +12% per level - return Math.round(base * levelBonus); + // chemist and runner earn at 70% of dealer rate (indirect income) + const roleMult = placement.role === 'dealer' ? 1.0 : 0.7; + return Math.round(base * levelBonus * roleMult); } // ─── Store ─────────────────────────────────────────────────── diff --git a/frontend/src/types/block.types.ts b/frontend/src/types/block.types.ts index 26eacb5..8a5957b 100644 --- a/frontend/src/types/block.types.ts +++ b/frontend/src/types/block.types.ts @@ -41,7 +41,12 @@ export type MemberRole = | 'driver' | 'chemist' | 'runner' - | 'boss'; + | 'boss' + // Hireable from marketplace — must be kept in sync with HireableRole + | 'recruit' // Entry-level gang member; no dealing, low combat + | 'k9' // Attack dog; no income, high combat + // NPC / system roles — never placed by the player directly + | 'police'; // Police officer (NPC only) export interface BlockPlacement { memberId: string; diff --git a/scripts/inventory_assets.py b/scripts/inventory_assets.py new file mode 100644 index 0000000..daac4ba --- /dev/null +++ b/scripts/inventory_assets.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Inventory the runtime asset manifest so sprite gaps are visible at a glance. + +Groups every entry by its dotted key prefix and prints the leaf names, which is +how `worldActorResolver` and `assetResolver` look assets up at runtime. +""" + +import json +from collections import defaultdict +from pathlib import Path + +MANIFEST = Path("frontend/src/assets/runtimeManifest.json") + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + data = json.loads((root / MANIFEST).read_text(encoding="utf-8")) + entries = data["entries"] + + print(f"total entries: {len(entries)}") + print(f"budget MB: {data.get('budgetMB')}") + print(f"total bytes: {data.get('totalBytes')}") + print() + + groups: dict[str, list[str]] = defaultdict(list) + + # entries may be a dict keyed by asset id, or a list of objects. + if isinstance(entries, dict): + items = list(entries.keys()) + else: + items = [e.get("id") or e.get("key") or str(e) for e in entries] + + for key in items: + parts = key.split(".") + group = ".".join(parts[:-1]) if len(parts) > 1 else "(root)" + groups[group].append(parts[-1]) + + for group in sorted(groups): + leaves = sorted(groups[group]) + print(f"[{group}] ({len(leaves)})") + for leaf in leaves: + print(f" {leaf}") + print() + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/name_topdown_states.py b/scripts/name_topdown_states.py new file mode 100644 index 0000000..ee442e4 --- /dev/null +++ b/scripts/name_topdown_states.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Give the original top-down sprites an explicit `idle` state token. + +`process.mjs::parseName` derives `state` from filename tokens. The first +generation of top-down art was named `character_{role}_{sex}_topdown_v001.png` +with no state token, so every entry landed in the manifest with `state: null`. + +`worldActorResolver` only indexes assets that have BOTH a role and a state: + + if (a.state) index.set(`${view}|${a.role}|${a.state}`, a); + +A null state therefore leaves those sprites reachable only through the +last-resort `byRole` pass, which is why a downed member rendered standing. New +`downed` and `arrested` art would not have fixed that on its own — the resolver +would still have had no `idle` entry to fall back FROM, and the state-first +Pass 2 could still have matched a stateless sprite. + +Renaming to `..._topdown_idle_v001.png` makes the existing art declare what it +already is. No pixels change. + +Also drops `*_original.png` duplicates, which the processor quarantines anyway +(`QUARANTINE` in process.mjs) but which are pointlessly tracked in git. +""" + +import re +import subprocess +from pathlib import Path + +TOPDOWN = Path("frontend/public/assets/generated/characters/topdown") + +# Matches the stateless first-generation naming only. Anything that already +# carries a state token is left alone. +STATELESS = re.compile( + r"^character_(?P[a-z0-9]+)_(?Pmale|female)" + r"(?P(?:_[a-z0-9]+)*?)_topdown_(?Pv\d+)\.png$" +) + +STATES = { + "idle", "walk", "aim", "fire", "reload", "hit", "downed", + "dead", "arrested", "seated", "driving", "alert", "front", "side", +} + + +def git(*args: str) -> None: + subprocess.run(["git", *args], check=True) + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + d = root / TOPDOWN + + renamed: list[tuple[str, str]] = [] + removed: list[str] = [] + + for p in sorted(d.iterdir()): + if p.suffix != ".png": + continue + + if p.name.endswith("_original.png"): + removed.append(p.name) + continue + + m = STATELESS.match(p.name) + if not m: + continue + + # Guard: never touch a file that already declares a state. + tokens = set(p.stem.split("_")) + if tokens & STATES: + continue + + extra = m.group("extra") or "" + new = (f"character_{m.group('role')}_{m.group('sex')}{extra}" + f"_topdown_idle_{m.group('ver')}.png") + renamed.append((p.name, new)) + + for old, new in renamed: + git("mv", str(TOPDOWN / old), str(TOPDOWN / new)) + print(f"renamed {old}\n -> {new}") + + for name in removed: + git("rm", "-q", "--", str(TOPDOWN / name)) + print(f"dropped {name} (source-resolution duplicate)") + + print(f"\n{len(renamed)} renamed, {len(removed)} dropped") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/sprite_gaps.py b/scripts/sprite_gaps.py new file mode 100644 index 0000000..cc4df45 --- /dev/null +++ b/scripts/sprite_gaps.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Report which (view, role, state) sprites exist and which fall back. + +`worldActorResolver` never returns nothing — it degrades to a sibling pose. That +is good for robustness but it hides gaps, because a downed shooter silently +borrows a dealer's downed art. This script makes the gaps explicit so art can be +generated against real holes rather than guesses. +""" + +import json +from pathlib import Path + +MANIFEST = Path("frontend/src/assets/runtimeManifest.json") + +VIEW_CLASS = { + "street": "actor-street", + "topdown": "actor-topdown", + "fullbody": "actor-fullbody", +} + +# Roles the game actually deploys, including the ones added in Sprint 15-B. +ROLES = ["shooter", "dealer", "enforcer", "lookout", "driver", "recruit", "k9"] + +# States renderers request. Sourced from ActorState in worldActorResolver.ts. +STATES_BY_VIEW = { + "street": ["idle", "walk", "aim", "fire", "hit", "downed", "arrested", "alert"], + "topdown": ["idle", "walk", "aim", "downed", "arrested"], + "fullbody": ["front"], +} + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + data = json.loads((root / MANIFEST).read_text(encoding="utf-8")) + assets = data["entries"] + + have: set[tuple[str, str, str]] = set() + for a in assets: + cls, role, state = a.get("class"), a.get("role"), a.get("state") + if not role or not state: + continue + for view, vc in VIEW_CLASS.items(): + if cls == vc: + have.add((view, role, state)) + + print("=" * 62) + print("SPRITE COVERAGE (X = present, . = falls back to another sprite)") + print("=" * 62) + + missing: list[tuple[str, str, str]] = [] + for view, states in STATES_BY_VIEW.items(): + print(f"\n--- {view.upper()} ---") + header = "role".ljust(10) + "".join(s[:5].ljust(6) for s in states) + print(header) + for role in ROLES: + row = role.ljust(10) + for state in states: + if (view, role, state) in have: + row += "X".ljust(6) + else: + row += ".".ljust(6) + missing.append((view, role, state)) + print(row) + + print("\n" + "=" * 62) + print(f"MISSING: {len(missing)} of " + f"{sum(len(s) for s in STATES_BY_VIEW.values()) * len(ROLES)} slots") + print("=" * 62) + + # Roles with zero art in a view are the highest-value targets: every state + # for that role is currently borrowing somebody else's silhouette. + print("\nROLES WITH NO ART AT ALL IN A VIEW:") + for view in STATES_BY_VIEW: + for role in ROLES: + if not any((view, role, s) in have for s in STATES_BY_VIEW[view]): + print(f" {view:9s} {role}") + + # Non-actor classes, for awareness of what else the manifest carries. + print("\nNON-ACTOR CLASSES PRESENT:") + other: dict[str, int] = {} + for a in assets: + cls = a.get("class", "?") + if cls not in VIEW_CLASS.values(): + other[cls] = other.get(cls, 0) + 1 + for cls, n in sorted(other.items(), key=lambda kv: -kv[1]): + print(f" {cls:22s} {n}") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())