Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ and `extractNames.<canonicalMapCode>.<key>` under
no strings — adding a language is a locale-only change.

`factions` is an array because one physical exit can serve multiple sides
(PMC + Scav share the same door). At render time the client **merges
(PMC + Scav share the same door). `transit` is a fourth pseudo-faction for
map-to-map transit points — not a real side (PMC-only mechanic), but riding
the faction pipeline gives icons/tooltips/filter/auto-hide for free; the
destination map lives only in the i18n display name, not in the data. At render time the client **merges
co-located extracts** that round to the same `(x, z)` bucket — Customs has
dorms V-Ex (PMC) and old road gate (Scav) ~1m apart, they become a single
composite marker. See "Map rendering layers" below for how that composite
Expand Down Expand Up @@ -190,7 +193,11 @@ key back in the running app.
(derived from registry `subgroup`: today only `player`, with `loot`/`quests`
dimmed future icons). Clicking a category icon opens a flyout listing its
layers: each row has a visibility `ToggleSwitch`, layer name, and a gear button
that expands that layer's settings component inline. Floor stepper at the rail's
that expands that layer's settings component inline. A layer may also register
a `filterComponent` — quick controls rendered always-expanded under its row
while the layer is visible, no gear click (extracts: the faction toggles, with
factions that have no exits on the current map auto-hidden via
`factionsForMap`). Floor stepper at the rail's
bottom (multi-floor maps only) is a vertical ▲/level/▼. Rail hides when the
overlay is click-through-locked. When locked on a multi-floor map, a compact
read-only floor chip appears bottom-left. Flyouts position relative to the rail
Expand Down Expand Up @@ -237,24 +244,21 @@ vertical span is pushed out to the rail's right edge (RAIL_GAP px beyond). Measu
each frame via `.layer-rail` rect so arrows flow dynamically as the rail animates
in/out on lock transitions.

Extract markers are `L.divIcon` instances (not `L.icon`) — the composite
PNG split via CSS `clip-path: polygon(...)` is built inline as HTML.
Single-faction = one `<img>`; 2 or 3 factions = stacked `<img>`s with `/`-
diagonal clip slices (area-balanced thirds for the 3-faction case). The
icon reacts to the faction filter dynamically: turning Scav off on a
PMC+Scav exit re-renders the icon as a clean PMC. Tailwind's preflight
gives `<img>` `max-width: 100%` which collapses the icon to 0 inside
Leaflet's positioned wrapper, so `styles.css` keeps an override:

```
.leaflet-extracts-pane .extract-icon-divicon img {
max-width: none !important; max-height: none !important;
}
```
Extract markers are `L.divIcon` instances (not `L.icon`) — the icon is
**inline SVG generated in `icon.ts`** (one hand-drawn shield+runner shape,
interior recoloured per faction from `FACTION_COLORS`; no static image
assets, nothing under `public/icons/`). Inline rather than `<img src>`
because an external SVG can't be recoloured from outside. Single-faction =
one `<svg>`; 2 or 3 factions = stacked recoloured copies with `/`-diagonal
CSS `clip-path` slices (area-balanced thirds for the 3-faction case; ≥4
co-located kinds show the first three per `FACTION_ORDER`). The icon
reacts to the faction filter dynamically: turning Scav off on a PMC+Scav
exit re-renders the icon as a clean PMC.

Tooltip is multi-row (one row per distinct name), each row carries a
faction-coloured left stripe via `.extract-tooltip-row--{pmc|scav|shared}`
or `.extract-tooltip-row--multi` when factions share a name. `direction:
faction-coloured left stripe — the colour is inlined from `FACTION_COLORS`
in `tooltip.ts` (no per-faction CSS classes); rows where multiple factions
share a name keep the neutral stripe from the base CSS rule. `direction:
'top'` keeps the tooltip above the icon; `tooltipAnchor` inside the
icon's top edge gives a few pixels of overlap for visual cohesion.

Expand Down
Binary file removed apps/client/public/icons/extracts/extract_pmc.png
Binary file not shown.
Binary file removed apps/client/public/icons/extracts/extract_scav.png
Binary file not shown.
Binary file removed apps/client/public/icons/extracts/extract_shared.png
Binary file not shown.
60 changes: 43 additions & 17 deletions apps/client/src/features/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"factions": {
"pmc": "PMC",
"scav": "Scav",
"shared": "Shared"
"shared": "Shared",
"transit": "Transits"
},
"paths": {
"heading": "Game integration",
Expand Down Expand Up @@ -174,7 +175,6 @@
"railroad_to_port": "Railroad to Port",
"trailer_park_workers_shack": "Trailer Park Workers' Shack",
"railroad_to_tarkov": "Railroad to Tarkov",
"ruaf_roadblock_2": "RUAF Roadblock",
"warehouse_17": "Warehouse 17",
"factory_shacks": "Factory Shacks",
"warehouse_4": "Warehouse 4",
Expand All @@ -185,21 +185,28 @@
"scav_checkpoint": "Scav Checkpoint",
"boiler_room_basement_co_op": "Boiler Room Basement (Co-op)",
"smugglers_boat": "Smugglers' Boat",
"smugglers_bunker_zb_1012": "Smugglers' Bunker (ZB-1012)"
"smugglers_bunker_zb_1012": "Smugglers' Bunker (ZB-1012)",
"transit_reserve": "Transit to Reserve",
"transit_factory": "Transit to Factory",
"transit_interchange": "Transit to Interchange",
"transit_shoreline": "Transit to Shoreline"
},
"factory4_day": {
"cellars": "Cellars",
"gate_3": "Gate 3",
"gate_3_2": "Gate 3",
"camera_bunker_door": "Camera Bunker Door",
"office_window": "Office Window",
"gate_0": "Gate 0",
"med_tent_gate": "Med Tent Gate",
"courtyard_gate": "Courtyard Gate",
"smugglers_passage": "Smugglers' Passage"
"smugglers_passage": "Smugglers' Passage",
"transit_woods": "Transit to Woods",
"transit_customs": "Transit to Customs",
"transit_lab": "Transit to The Lab"
},
"woods": {
"friendship_bridge_co_op": "Friendship Bridge (Co-Op)",
"scav_bridge": "Scav Bridge",
"scav_bridge_co_op": "Scav Bridge (Co-op)",
"ruaf_gate": "RUAF Gate",
"zb_016": "ZB-016",
"zb_014": "ZB-014",
Expand All @@ -213,12 +220,14 @@
"mountain_stash": "Mountain Stash",
"eastern_rocks": "Eastern Rocks",
"old_railway_depot": "Old Railway Depot",
"un_roadblock_2": "UN Roadblock",
"ruaf_roadblock": "RUAF Roadblock",
"outskirts_2": "Outskirts",
"northern_un_roadblock": "Northern UN Roadblock",
"power_line_passage_flare": "Power Line Passage (Flare)",
"railway_bridge_to_tarkov": "Railway Bridge to Tarkov"
"railway_bridge_to_tarkov": "Railway Bridge to Tarkov",
"transit_factory": "Transit to Factory",
"transit_reserve": "Transit to Reserve",
"transit_lighthouse": "Transit to Lighthouse",
"transit_customs": "Transit to Customs"
},
"shoreline": {
"road_to_north_v_ex": "Road to North V-Ex",
Expand All @@ -233,7 +242,10 @@
"smugglers_path_co_op": "Smugglers' Path (Co-op)",
"pier_boat": "Pier Boat",
"climber_s_trail": "Climber's Trail",
"mountain_bunker": "Mountain Bunker"
"mountain_bunker": "Mountain Bunker",
"transit_lighthouse": "Transit to Lighthouse",
"transit_terminal": "Transit to Terminal",
"transit_labyrinth": "Transit to Labyrinth?"
},
"rezervbase": {
"armored_train": "Armored Train",
Expand All @@ -246,15 +258,21 @@
"heating_pipe": "Heating Pipe",
"depot_hermetic_door": "Depot Hermetic Door",
"checkpoint_fence": "Checkpoint Fence",
"exit_to_woods": "Exit to Woods"
"exit_to_woods": "Exit to Woods",
"transit_customs": "Transit to Customs",
"transit_woods": "Transit to Woods",
"transit_lighthouse": "Transit to Lighthouse"
},
"interchange": {
"emercom_checkpoint": "Emercom Checkpoint",
"railway_exfil": "Railway Exfil",
"power_station_v_ex": "Power Station V-Ex",
"scav_camp_co_op": "Scav Camp (Co-Op)",
"hole_in_the_fence": "Hole in the Fence",
"saferoom_exfil": "Saferoom Exfil"
"saferoom_exfil": "Saferoom Exfil",
"smugglers_tunnel": "Smugglers' Tunnel",
"path_to_river": "Path to River",
"transit_customs": "Transit to Customs",
"transit_streets": "Transit to Streets of Tarkov"
},
"lighthouse": {
"armored_train": "Armored Train",
Expand All @@ -268,7 +286,10 @@
"hideout_under_the_landing_stage": "Hideout Under the Landing Stage",
"scav_hideout_at_the_grotto": "Scav Hideout at the Grotto",
"industrial_zone_gates": "Industrial Zone Gates",
"passage_by_the_lake": "Passage by the Lake"
"passage_by_the_lake": "Passage by the Lake",
"transit_shoreline": "Transit to Shoreline",
"transit_reserve": "Transit to Reserve",
"transit_woods": "Transit to Woods"
},
"tarkovstreets": {
"entrance_to_catacombs": "Entrance to Catacombs",
Expand All @@ -287,7 +308,10 @@
"klimov_street_flare": "Klimov Street (Flare)",
"pinewood_basement_co_op": "Pinewood Basement (Co-Op)",
"expo_checkpoint": "Expo Checkpoint",
"smugglers_basement": "Smugglers' Basement"
"smugglers_basement": "Smugglers' Basement",
"transit_ground_zero": "Transit to Ground Zero",
"transit_interchange": "Transit to Interchange",
"transit_lab": "Transit to The Lab"
},
"laboratory": {
"medical_block_elevator": "Medical Block Elevator",
Expand All @@ -296,15 +320,17 @@
"ventilation_shaft": "Ventilation Shaft",
"sewage_conduit": "Sewage Conduit",
"parking_gate": "Parking Gate",
"hangar_gate": "Hangar Gate"
"hangar_gate": "Hangar Gate",
"transit_streets": "Transit to Streets of Tarkov"
},
"sandbox": {
"police_cordon_v_ex": "Police Cordon V-Ex",
"emercom_checkpoint": "Emercom Checkpoint",
"scav_checkpoint_co_op": "Scav Checkpoint (Co-op)",
"nakatani_basement_stairs": "Nakatani Basement Stairs",
"mira_ave_flare": "Mira Ave (Flare)",
"tartowers_sales_office": "Tartowers Sales Office"
"tartowers_sales_office": "Tartowers Sales Office",
"transit_streets": "Transit to Streets of Tarkov"
}
},
"mapNames": {
Expand Down
60 changes: 43 additions & 17 deletions apps/client/src/features/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"factions": {
"pmc": "ЧВК",
"scav": "Дикие",
"shared": "Общие"
"shared": "Общие",
"transit": "Переходы"
},
"paths": {
"heading": "Интеграция с игрой",
Expand Down Expand Up @@ -174,7 +175,6 @@
"railroad_to_port": "ЖД к порту",
"trailer_park_workers_shack": "Времянка у трейл. парка",
"railroad_to_tarkov": "ЖД до Таркова",
"ruaf_roadblock_2": "Блокпост ВС РФ",
"warehouse_17": "Склад 17",
"factory_shacks": "Заводские времянки",
"warehouse_4": "Склад 4",
Expand All @@ -185,21 +185,28 @@
"scav_checkpoint": "КПП Диких",
"boiler_room_basement_co_op": "Подвал котельной (Совм.)",
"smugglers_boat": "Лодка Контрабандистов",
"smugglers_bunker_zb_1012": "Бункер Контрабандистов (ЗБ-1012)"
"smugglers_bunker_zb_1012": "Бункер Контрабандистов (ЗБ-1012)",
"transit_reserve": "Переход на Резерв",
"transit_factory": "Переход на Завод",
"transit_interchange": "Переход на Развязку",
"transit_shoreline": "Переход на Берег"
},
"factory4_day": {
"cellars": "Подвалы",
"gate_3": "Ворота 3",
"gate_3_2": "Ворота 3",
"camera_bunker_door": "Бункерная дверь с камерой",
"office_window": "Офисное окно",
"gate_0": "Ворота 0",
"med_tent_gate": "Ворота у медицинской палатки",
"courtyard_gate": "Ворота во дворе",
"smugglers_passage": "Проход Контрабандистов"
"smugglers_passage": "Проход Контрабандистов",
"transit_woods": "Переход на Лес",
"transit_customs": "Переход на Таможню",
"transit_lab": "Переход на Лабораторию"
},
"woods": {
"friendship_bridge_co_op": "Мост дружбы (Совм.)",
"scav_bridge": "Дикий мост",
"scav_bridge_co_op": "Дикий мост (Совм.)",
"ruaf_gate": "Ворота ВС РФ",
"zb_016": "ЗБ-016",
"zb_014": "ЗБ-014",
Expand All @@ -213,12 +220,14 @@
"mountain_stash": "Горный тайник",
"eastern_rocks": "Восточные скалы",
"old_railway_depot": "Старое депо",
"un_roadblock_2": "Южный пост ООН",
"ruaf_roadblock": "Блокпост ВС РФ",
"outskirts_2": "Окраины",
"northern_un_roadblock": "Северный пост ООН",
"power_line_passage_flare": "Проход у ЛЭП (Сигнал)",
"railway_bridge_to_tarkov": "ЖД-мост к Таркову"
"railway_bridge_to_tarkov": "ЖД-мост к Таркову",
"transit_factory": "Переход на Завод",
"transit_reserve": "Переход на Резерв",
"transit_lighthouse": "Переход на Маяк",
"transit_customs": "Переход на Таможню"
},
"shoreline": {
"road_to_north_v_ex": "А-Выход дорога на север",
Expand All @@ -233,7 +242,10 @@
"smugglers_path_co_op": "Путь контрабандиста (Совм.)",
"pier_boat": "Лодка на причале",
"climber_s_trail": "Тропа альпиниста",
"mountain_bunker": "Бункер в горе"
"mountain_bunker": "Бункер в горе",
"transit_lighthouse": "Переход на Маяк",
"transit_terminal": "Переход на Терминал",
"transit_labyrinth": "Переход на Лабиринт?"
},
"rezervbase": {
"armored_train": "Бронепоезд",
Expand All @@ -246,15 +258,21 @@
"heating_pipe": "Трубопровод отопления",
"depot_hermetic_door": "Гермодверь к депо",
"checkpoint_fence": "Забор у КПП",
"exit_to_woods": "Выход в сторону Леса"
"exit_to_woods": "Выход в сторону Леса",
"transit_customs": "Переход на Таможню",
"transit_woods": "Переход на Лес",
"transit_lighthouse": "Переход на Маяк"
},
"interchange": {
"emercom_checkpoint": "КПП МЧС",
"railway_exfil": "Выход на железной дороге",
"power_station_v_ex": "А-Выход у ТЭЦ",
"scav_camp_co_op": "Лагерь Диких (Совм.)",
"hole_in_the_fence": "Дыра в заборе",
"saferoom_exfil": "Безопасная комната"
"saferoom_exfil": "Безопасная комната",
"smugglers_tunnel": "Тропа контрабандистов",
"path_to_river": "Тропа к реке",
"transit_customs": "Переход на Таможню",
"transit_streets": "Переход на Улицы Таркова"
},
"lighthouse": {
"armored_train": "Бронепоезд",
Expand All @@ -268,7 +286,10 @@
"hideout_under_the_landing_stage": "Схрон под дебаркадером",
"scav_hideout_at_the_grotto": "Укрытие в гроте",
"industrial_zone_gates": "Ворота промзоны",
"passage_by_the_lake": "Проход у озера"
"passage_by_the_lake": "Проход у озера",
"transit_shoreline": "Переход на Берег",
"transit_reserve": "Переход на Резерв",
"transit_woods": "Переход на Лес"
},
"tarkovstreets": {
"entrance_to_catacombs": "Вход в катакомбы",
Expand All @@ -287,7 +308,10 @@
"klimov_street_flare": "Улица Климова (Сигнал)",
"pinewood_basement_co_op": "Подвал в Pinewood (Совм.)",
"expo_checkpoint": "КПП у выставочного центра",
"smugglers_basement": "Подвал Контрабандистов"
"smugglers_basement": "Подвал Контрабандистов",
"transit_ground_zero": "Переход на Эпицентр",
"transit_interchange": "Переход на Развязку",
"transit_lab": "Переход на Лабораторию"
},
"laboratory": {
"medical_block_elevator": "Лифт медблока",
Expand All @@ -296,15 +320,17 @@
"ventilation_shaft": "Вентиляционная шахта",
"sewage_conduit": "Накопительный коллектор",
"parking_gate": "Парковка",
"hangar_gate": "Ангар"
"hangar_gate": "Ангар",
"transit_streets": "Переход на Улицы Таркова"
},
"sandbox": {
"police_cordon_v_ex": "А-Выход полицейский кордон",
"emercom_checkpoint": "Пункт МЧС",
"scav_checkpoint_co_op": "Блокпост Диких (Совм.)",
"nakatani_basement_stairs": "Спуск в подвал в Nakatani",
"mira_ave_flare": "Проспект Мира (Сигнал)",
"tartowers_sales_office": "Офис продаж Tartowers"
"tartowers_sales_office": "Офис продаж Tartowers",
"transit_streets": "Переход на Улицы Таркова"
}
},
"mapNames": {
Expand Down
5 changes: 5 additions & 0 deletions apps/client/src/features/map/components/LayerRail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ function stepFloor(delta: number): void {
<i class="pi pi-cog text-sm" />
</button>
</div>
<!-- Quick filters stay expanded while the layer is on; the gear
keeps only the rarely-touched knobs. -->
<div v-if="l.filterComponent && layerVis(l.id).value" class="mt-1.5 pl-1">
<component :is="l.filterComponent" />
</div>
<div v-if="l.settingsComponent && expanded === l.id" class="mt-2 pl-1">
<component :is="l.settingsComponent" />
</div>
Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/features/map/components/MapView.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useLeafletMap } from '../composables/useLeafletMap';
import { useDevCoordsCopy } from '../composables/useDevCoordsCopy';
import { useMapLayers } from '../layers/registry';
import { useLayerVisibility } from '../composables/useLayerVisibility';
import { useMapI18n } from '../composables/useMapI18n';
Expand Down Expand Up @@ -34,6 +35,8 @@ const {
reload,
} = useLeafletMap(mapContainer, props.mapCode);

useDevCoordsCopy(map);

for (const layer of useMapLayers().value) {
layer.mount({
map,
Expand Down
Loading