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
4 changes: 0 additions & 4 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ include, the following software:
- The .NET runtime and Microsoft.Extensions packages
(https://github.com/dotnet), MIT License.

apps/utility-sw/hud/radars/metadata.json, the map radar calibration, is
vendored from boltobserv by boltgolt
(https://github.com/boltgolt/boltobserv), GNU GPL version 3.

The gamedata validator (apps/gamedata-validator) is adapted from
swiftly-solution/gamedata-validator
(https://github.com/swiftly-solution/gamedata-validator), whose repository
Expand Down
27 changes: 18 additions & 9 deletions apps/utility-sw/hud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ asserts against the files in this directory that:
stylesheet;
- the one-of-N class groups (`x0…x63`, `y0…y63`, `p0…p10`) are declared for
exactly the counts `HudSlots` will pick between;
- every map with a `#radar.<map>` rule also has calibration in `radars/`;
- every map with a `#radar.<map>` rule also has calibration in `radars/`, and
every level that calibration names has a `#radar.<map>.<level>` image rule;
- every `PracticeStepColors` name has a chip and a swatch rule, and none of them
is ever green or red — those two belong to the aim ramp;
- `nade3_edit` contains no geometry field, so editing where a smoke lands can
Expand Down Expand Up @@ -116,14 +117,22 @@ clone is ~570 MB and would otherwise be copied into the pod on every `up`.
The addon ships **no raster images**. The minimap draws on CS2's own
`s2r://panorama/images/overheadmaps/<map>_radar.psd`, because a texture compiled
by PanoramaCompiler's experimental image path spawned an entity that rendered
nothing. The web panel's radars are SimpleRadar — a drop-in replacement for
Valve's, same framing — so `radars/metadata.json` projects correctly onto either
and the two UIs still agree to the pixel. That is why the swap cost nothing, and
why `scripts/sync-radars.sh` mirrors the calibration only.

Known limit: SimpleRadar puts Nuke's and Vertigo's two levels on one image and
Valve ships them as separate `_lower_radar` files, so lower-level markers on
those two maps are offset. The other eight are exact.
nothing. `radars/metadata.json` is built from the files that frame those
images, CS2's `game/csgo/resource/overviews/<map>.txt`, by
`scripts/sync-radars.sh <overviews directory>`; run it again when Valve redraws a
radar. It is not mirrored from the web panel, which frames its radars its own
way.

Nuke, Train and Vertigo draw their lower floor on an image of its own,
`<map>_lower_radar.psd`, in the same frame as the main one, and the overview's
`verticalsections` say below which height it applies. The picker swaps image the
way CS2's radar does, by the floor the player is standing on (the main one when
they have no position), and draws every spot on either: a detonation height says
little about which floor a throw is for, and one floor's throws are often thrown
from the other. That the lower images load by that path is inferred from the
VPK, which ships `de_nuke_lower_radar_psd.vtex_c` beside the main one; it has not
yet been seen in game. Check it after the Workshop publish, or with the item
unmounted: a mounted item draws the published `nadehud2.css` over a local build.

Icons are SVG. One of CS2's own draws from the stylesheet as
`background-image: url("s2r://panorama/images/icons/equipment/molotov.vsvg")`
Expand Down
3 changes: 3 additions & 0 deletions apps/utility-sw/hud/panorama/styles/custom_game/nadehud2.css
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,9 @@
#radar.de_overpass { background-image: url("s2r://panorama/images/overheadmaps/de_overpass_radar.psd"); }
#radar.de_train { background-image: url("s2r://panorama/images/overheadmaps/de_train_radar.psd"); }
#radar.de_vertigo { background-image: url("s2r://panorama/images/overheadmaps/de_vertigo_radar.psd"); }
#radar.de_nuke.lower { background-image: url("s2r://panorama/images/overheadmaps/de_nuke_lower_radar.psd"); }
#radar.de_train.lower { background-image: url("s2r://panorama/images/overheadmaps/de_train_lower_radar.psd"); }
#radar.de_vertigo.lower { background-image: url("s2r://panorama/images/overheadmaps/de_vertigo_lower_radar.psd"); }

/* The last five throws at this lineup, oldest on the left: a streak building
is easier to see than a number counting up. */
Expand Down
134 changes: 42 additions & 92 deletions apps/utility-sw/hud/radars/metadata.json
Original file line number Diff line number Diff line change
@@ -1,140 +1,90 @@
{
"_comment": "Mirrored from web/public/radars/metadata.json by scripts/sync-radars.sh. Do not edit here.",
"_comment": "Built from CS2's resource/overviews/<map>.txt by scripts/sync-radars.sh: resolution = scale, offset = (-pos_x, 1024 * scale - pos_y), levels from verticalsections. Do not edit here.",
"de_ancient": {
"resolution": 4.26,
"resolution": 5,
"offset": {
"x": 2590,
"y": 2520
},
"zRange": {
"min": -95,
"max": 220
"x": 2953,
"y": 2956
}
},
"de_anubis": {
"resolution": 5.25,
"resolution": 5.22,
"offset": {
"x": 2830,
"y": 2030
},
"zRange": {
"min": -85,
"max": 180
"x": 2796,
"y": 2017.28
}
},
"de_cache": {
"resolution": 5.54,
"resolution": 5.5,
"offset": {
"x": 2020,
"y": 2390
"x": 2000,
"y": 2382
}
},
"de_dust2": {
"resolution": 4.4,
"offset": {
"x": 2470,
"y": 1255
},
"zRange": {
"min": -50,
"max": 185
"x": 2476,
"y": 1266.6
}
},
"de_inferno": {
"resolution": 4.91,
"resolution": 4.9,
"offset": {
"x": 2090,
"y": 1150
},
"zRange": {
"min": 73,
"max": 310
"x": 2087,
"y": 1147.6
}
},
"de_mirage": {
"resolution": 5.02,
"resolution": 5,
"offset": {
"x": 3240,
"y": 3410
},
"zRange": {
"min": -300,
"max": 20
"x": 3230,
"y": 3407
}
},
"de_nuke": {
"resolution": 6.98,
"resolution": 7,
"offset": {
"x": 3290,
"y": 5990
},
"zRange": {
"min": -480,
"max": -20
"x": 3453,
"y": 4281
},
"splits": [
"levels": [
{
"bounds": {
"top": -482,
"bottom": -2500
},
"offset": {
"x": 0,
"y": -46
},
"zRange": {
"min": -770,
"max": -480
}
"name": "lower",
"maxZ": -495
}
]
},
"de_overpass": {
"resolution": 5.18,
"resolution": 5.2,
"offset": {
"x": 4830,
"y": 3540
},
"zRange": {
"min": 10,
"max": 500
"x": 4831,
"y": 3543.8
}
},
"de_train": {
"resolution": 4.74,
"resolution": 4.082077,
"offset": {
"x": 2730,
"y": 2360
"x": 2308,
"y": 2102.046848
},
"zRange": {
"min": -320,
"max": 15
}
"levels": [
{
"name": "lower",
"maxZ": -50
}
]
},
"de_vertigo": {
"resolution": 4.96,
"resolution": 4,
"offset": {
"x": 3890,
"y": 3800
},
"zRange": {
"min": 11680,
"max": 11920
"x": 3168,
"y": 2334
},
"splits": [
"levels": [
{
"bounds": {
"top": 11550,
"bottom": 11000
},
"offset": {
"x": 0.2,
"y": -42.6
},
"zRange": {
"min": 11550,
"max": 11680
}
"name": "lower",
"maxZ": 11700
}
]
}
Expand Down
10 changes: 10 additions & 0 deletions apps/utility-sw/src/Hud/NadePickTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public record Marker(

// Null on a map with no radar to draw: the list has the panel to itself.
public string? MapClass { get; set; }

// One of RadarMaps.Levels while the player stands on that floor, null for
// the map's main image.
public string? Level { get; set; }

public string Focus { get; set; } = "";

public NadePickTemplate Add(Row row)
Expand Down Expand Up @@ -118,6 +123,11 @@ public override void Apply(HudSurface surface)
surface.Class("radar", map, map == MapClass);
}

foreach (string level in RadarMaps.Levels)
{
surface.Class("radar", level, level == Level);
}

for (int index = 0; index < HudSlots.MapMarkers; index++)
{
int slot = index + 1;
Expand Down
9 changes: 8 additions & 1 deletion apps/utility-sw/src/Hud/PracticeHud.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private class MenuState

private readonly Dictionary<ulong, MenuState> _menus = new();

// Loaded once from hud/radars/metadata.json, mirrored from the web panel.
// Loaded once from hud/radars/metadata.json, built from CS2's overviews.
private Dictionary<string, RadarCalibration>? _radars;

// The target a player picked off the map, so the list knows what to filter
Expand Down Expand Up @@ -541,6 +541,12 @@ private bool RenderPicker(IPlayer player)
LineupRecord? loaded = _system.StateFor(steamId).Loaded;
Vec3? at = PracticeSystem.Where(player)?.feet_position;

// The floor the player is standing on, which is how CS2's own radar
// picks its image, and the main one while they have no position.
string? level = calibration == null || at == null
? null
: RadarProjection.Level(at.Value.z, calibration);

var template = new NadePickTemplate
{
Title = Narrowing(steamId) ?? "Pick a lineup",
Expand All @@ -555,6 +561,7 @@ private bool RenderPicker(IPlayer player)
MapClass = calibration == null
? null
: RadarProjection.NormalizeMapName(_library.Map),
Level = level,
Focus = selected == null
? "click a spot for the throws that land there"
: "click it again to let go of it",
Expand Down
29 changes: 29 additions & 0 deletions apps/utility-sw/test/HudLayoutContractTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ public void EveryClassThePickerTogglesIsStyled()
toggles.Add(("radar", map));
}

foreach (string level in RadarMaps.Levels)
{
toggles.Add(("radar", level));
}

for (int marker = 1; marker <= HudSlots.MapMarkers; marker++)
{
toggles.Add(($"m{marker}", "hidden"));
Expand Down Expand Up @@ -694,6 +699,30 @@ public void EveryRadarMapHasAnImageAStyleAndCalibration()
}
}

// Without a rule of its own a level keeps showing the main image while the
// player stands below it. A level RadarMaps.Levels does not list is never
// switched to at all.
[Fact]
public void EveryRadarLevelHasItsOwnImage()
{
string styles = Styles();
Dictionary<string, RadarCalibration> loaded = RadarMaps.Load(
Path.Combine(HudRoot(), "radars", "metadata.json")
);

foreach ((string map, RadarCalibration calibration) in loaded)
{
foreach (RadarLevel level in calibration.Levels)
{
Assert.Contains(level.Name, RadarMaps.Levels);
Assert.Contains(
$"#radar.{map}.{level.Name} {{ background-image: url(\"s2r://panorama/images/overheadmaps/{map}_{level.Name}_radar.psd\"); }}",
styles
);
}
}
}

[Fact]
public void EveryElementTheRunTogglesClassesOnExists()
{
Expand Down
Loading
Loading