Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d6853f4
Plot Midtown breakables on the current minimap
kai8484 Oct 6, 2026
ddd9eed
Outline breakables map dots so neighbouring dots stay distinct
kai8484 Oct 6, 2026
f3a1e8c
Split Midtown breakables maps by spawn time and address review
kai8484 Oct 6, 2026
e48e250
Use opaque Midtown minimap and enlarge breakables legend
ThePyrotechnic Oct 10, 2026
9b08822
Reuse _world_to_pixel for breakables dots
ThePyrotechnic Oct 10, 2026
9adc9bb
Skip Maps category check on dry runs
ThePyrotechnic Oct 10, 2026
a5091fa
Tidy compact legend after restyle
ThePyrotechnic Oct 10, 2026
63cbe3b
remove unused assets
ThePyrotechnic Oct 10, 2026
ceafb27
Pass spawn order to _breakables_plot explicitly
ThePyrotechnic Oct 10, 2026
e26930d
Plot single-kind breakables maps from BREAKABLES
ThePyrotechnic Oct 10, 2026
e5e6076
Export map_file_stems from game_map
ThePyrotechnic Oct 10, 2026
d8eeb2a
Derive uploaded map files from game_map.map_file_stems
ThePyrotechnic Oct 10, 2026
8483609
Update stale everything_5min example in game_map comment
ThePyrotechnic Oct 10, 2026
78ea39e
Default MapPlotter to the base map's own size
ThePyrotechnic Oct 10, 2026
5737ac7
Scale breakables dot radius with the output size
ThePyrotechnic Oct 10, 2026
c504944
Correct _breakables_plot docstring after the move to MAP_OUTPUT_SIZE
ThePyrotechnic Oct 10, 2026
ecdfcc6
Warn when breakables map colours fall back to a shared colour
ThePyrotechnic Oct 10, 2026
f4d9d12
Merge develop and resolve import conflicts
kai8484 Oct 24, 2026
d13e856
Shrink map PNGs with nearest-neighbour upscaling and palette saving
kai8484 Oct 24, 2026
c2b10e3
Use the Source2Viewer CLI in update_minimap.py instead of a custom VP…
kai8484 Oct 24, 2026
2dc8ef2
fix: fixed png path
hassayag Oct 24, 2026
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM python:3.11-slim AS base
RUN apt update && \
apt install -y --no-install-recommends wget unzip libicu-dev binutils git dos2unix gcc patchelf ccache make && \
apt install -y --no-install-recommends wget unzip libicu-dev binutils git dos2unix gcc patchelf ccache make fonts-dejavu-core && \
rm -rf /var/lib/apt/lists/*

WORKDIR /tools
Expand Down
87 changes: 87 additions & 0 deletions scripts/update_minimap.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate you've written a vpk parser 🤯. We can instead do this much easier with the source2viewer cli. We used to use this to parse vpk files before the days of SteamDB integration

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, swapped it out for the Source2Viewer CLI. The script now just runs -i pak01_dir.vpk --vpk_filepath panorama/images/minimap/base/minimap_midtown_mid_psd.vtex_c -o … -d, using --decompiler or $DECOMPILER_CMD like deadbot used to, then flattens the texture onto the tan background to produce minimap_midtown_opaque.png. The flatten blends in linear light so it matches the committed map, to within 1/255.

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
Regenerate the Midtown base map used by the game_map parser from an installed copy of Deadlock

The minimap texture lives in game/citadel/pak01_*.vpk, which deadbot does not download, so the
map is committed to src/parser/parsers/game_map/assets/minimap_midtown_opaque.png.
Re-run this whenever the map changes.

Requires the Source2Viewer CLI (https://github.com/ValveResourceFormat/ValveResourceFormat/releases),
passed with --decompiler or the DECOMPILER_CMD environment variable:

python scripts/update_minimap.py --game "C:/Program Files (x86)/Steam/steamapps/common/Deadlock/game" --decompiler Source2Viewer-CLI.exe
"""

import argparse
import os
import subprocess
import tempfile
from pathlib import Path

from PIL import Image

# The street map. Older revisions ship alongside it with a hash suffix, e.g. minimap_midtown_mid_psd_dd4bcbf9.vtex_c
MINIMAP_TEXTURE = 'panorama/images/minimap/base/minimap_midtown_mid_psd.vtex_c'
OUTPUT_PATH = Path('src/parser/parsers/game_map/assets/minimap_midtown_opaque.png')
# The texture is translucent everywhere, so it is drawn onto this colour to make it readable on any page background
BACKGROUND = (175, 161, 144)


def _srgb_to_linear(value: int) -> float:
c = value / 255
return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4


def _linear_to_srgb(c: float) -> int:
c = c * 12.92 if c <= 0.0031308 else 1.055 * c ** (1 / 2.4) - 0.055
return round(min(max(c, 0), 1) * 255)


SRGB_TO_LINEAR = [_srgb_to_linear(v) for v in range(256)]


def extract_minimap(decompiler: str, game_dir: str, texture: str) -> Image.Image:
"""Export the minimap texture from pak01 as a PNG with the Source2Viewer CLI"""
vpk = os.path.join(game_dir, 'citadel', 'pak01_dir.vpk')

with tempfile.TemporaryDirectory() as tmp:
output_dir = os.path.join(tmp, 'minimap.png')

# With a --vpk_filepath matching a single file, --output is that file's path
subprocess.run([decompiler, '-i', vpk, '--vpk_filepath', texture, '-o', output_dir, '-d'], check=True)

png = Path(output_dir) / Path(texture).with_suffix('.png')
with Image.open(png) as image:
return image.convert('RGBA')


def flatten(image: Image.Image, background: tuple[int, int, int]) -> Image.Image:
"""
Draw an RGBA image onto a solid background. Blends in linear light, like image editors do, rather than on the
raw sRGB values, which would leave the translucent map much darker
"""
background_linear = [SRGB_TO_LINEAR[c] for c in background]
rgba = image.convert('RGBA').tobytes()
pixels = bytearray()
for i in range(0, len(rgba), 4):
a = rgba[i + 3] / 255
pixels += bytes(_linear_to_srgb(SRGB_TO_LINEAR[c] * a + bg * (1 - a)) for c, bg in zip(rgba[i : i + 3], background_linear))
return Image.frombytes('RGB', image.size, bytes(pixels))


def main():
parser = argparse.ArgumentParser(description='Regenerate the Midtown base map asset from the game files')
parser.add_argument('--game', required=True, help='Deadlock game directory, the one containing citadel/')
parser.add_argument('--decompiler', default=os.getenv('DECOMPILER_CMD'), help='Path to the Source2Viewer CLI (default: $DECOMPILER_CMD)')
parser.add_argument('--texture', default=MINIMAP_TEXTURE, help=f'Minimap texture inside pak01 (default: {MINIMAP_TEXTURE})')
parser.add_argument('--out', default=OUTPUT_PATH, type=Path, help=f'Output path (default: {OUTPUT_PATH})')
args = parser.parse_args()
if not args.decompiler:
parser.error('pass --decompiler or set DECOMPILER_CMD to the Source2Viewer CLI')

image = flatten(extract_minimap(args.decompiler, args.game, args.texture), BACKGROUND)
image.save(args.out, optimize=True)
print(f'Wrote {image.width}x{image.height} map to {args.out}')


if __name__ == '__main__':
main()
8 changes: 4 additions & 4 deletions src/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from loguru import logger

from utils import json_utils
from utils.plot_utils import save_compact_png

from .parsers import (
abilities,
Expand Down Expand Up @@ -304,14 +305,13 @@ def _parse_map(self):
if self.parse_map:
logger.trace('Parsing Map...')
map_vpk = os.path.join(self.game_dir, 'game/citadel/maps/dl_midtown.vpk')
map_data = game_map.GameMapParser(map_vpk).run()
map_data = game_map.GameMapParser(map_vpk, self.data['scripts']['generic_data']['m_BreakableSpawnTimeDesc']).run()

json_utils.write(os.path.join(self.OUTPUT_DIR, 'json/midtown-metadata.json'), map_data['midtown']['metadata'])

os.makedirs(os.path.join(self.OUTPUT_DIR, 'assets'), exist_ok=True)
map_data['midtown']['plots']['golden_statues'].save(os.path.join(self.OUTPUT_DIR, 'assets/golden_statues_map.png'))
map_data['midtown']['plots']['crate'].save(os.path.join(self.OUTPUT_DIR, 'assets/crate_map.png'))
map_data['midtown']['plots']['shops'].save(os.path.join(self.OUTPUT_DIR, 'assets/shops_map.png'))
for name, plot in map_data['midtown']['plots'].items():
save_compact_png(plot, os.path.join(self.OUTPUT_DIR, f'assets/{name}_map.png'))

def _generate_resource_lookup(self, parsed_heroes, parsed_abilities, parsed_items):
logger.trace('Generating resource lookup...')
Expand Down
1 change: 1 addition & 0 deletions src/parser/parsers/game_map/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .__main__ import GameMapParser as GameMapParser
from .__main__ import map_file_stems as map_file_stems
Loading
Loading