pubg map: built-in intel layers (vehicle spots, drops, loot density, Blue Chip towers, secret rooms) per map
Deploy production / deploy (push) Successful in 1m12s

Read-only layers from static/pubg-intel/<map>.v1.json (scripts/build-pubg-intel.py):
positions from pubg.gs (game level files), drops from guides, secret rooms and Deston
keys as approximate rings on the named place. Layer panel (K), per-player choices in
localStorage, drawn below room markers; clicks still place markers.
This commit is contained in:
stan committed 2026-10-26 00:05:46 +00:00
1 parent b1edf7761c
commit be3473caa0
15 files changed
+1057 -5

No files matched your search

+3
View File
@@ -25,3 +25,6 @@ static/vendor/
# Generated drizzle snapshots
drizzle/*/snapshot.json
# Generated PUBG map intel (scripts/build-pubg-intel.py)
static/pubg-intel/
+339
View File
@@ -0,0 +1,339 @@
#!/usr/bin/env python3
"""Build the PUBG drop map's built-in intel layers (static/pubg-intel/<slug>.v1.json).
Exact positions come from pubg.gs, which reads them from the game's level files
(https://pubg.gs, fetched once on 2026-10-25). Only selected layers are published
(named places, bridges, normal-BR vehicle spots, Blue Chip towers, vending machines,
bear spawns); the 20k+ item spots are reduced to coarse loot-density cells.
Hot/quiet drops and secret rooms come from guides, anchored to the game's own
named-location points and flagged approximate. Nothing is placed by eye.
Usage: python3 scripts/build-pubg-intel.py static/pubg-intel
Input: the pubg.gs layer files (layers-<slug>.json) in $PUBG_GS_DIR, default the
script's folder. They are not committed: pubg.gs asks not to republish its data wholesale.
"""
import json, math, sys, collections, os
SRC = os.environ.get('PUBG_GS_DIR') or os.path.dirname(os.path.abspath(__file__))
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(SRC, 'out')
BUILT = '2026-10-25'
DEV = {'erangel': 'erangel', 'miramar': 'miramar', 'sanhok': 'sanhok', 'vikendi': 'vikendi', 'taego': 'taego',
'deston': 'deston', 'rondo': 'rondo', 'karakin': 'karakin', 'paramo': 'paramo', 'haven': 'haven'}
SOURCES = {
'gs': {'name': 'pubg.gs', 'url': 'https://pubg.gs/maps/{slug}/',
'note': 'positions read from the game level files'},
'pcg': {'name': 'PCGamesN, PUBG map list', 'url': 'https://www.pcgamesn.com/playerunknowns-battlegrounds/pubg-map'},
'psr': {'name': 'pubgsecretroom.com, best drop locations',
'url': 'https://pubgsecretroom.com/pubg-best-drop-locations-guide.html'},
'epic': {'name': 'Epic Games Store, guide to Rondo',
'url': 'https://store.epicgames.com/en-US/news/a-guide-to-pubg-battlegrounds-massive-new-map-rondo'},
'isp': {'name': 'InsideSport, Rondo hot spots',
'url': 'https://www.insidesport.in/gaming/pubg-mobile-rondo-hot-spots-check-out-the-top-landing-locations/'},
'gamepur': {'name': 'Gamepur, Deston secret rooms',
'url': 'https://www.gamepur.com/guides/all-deston-secret-room-locations-in-pubg'},
'loadout': {'name': 'The Loadout, Deston security keys',
'url': 'https://www.theloadout.com/pubg/security-key-location-deston'},
'ib': {'name': 'ImmortalBoost, PUBG secret rooms', 'url': 'https://immortalboost.com/blog/pubg/secret-locations/'},
'pn141': {'name': 'PUBG Wiki (Fandom), Erangel: Update 14.1 church basement', 'url': 'https://pubg.fandom.com/wiki/Erangel_(Remastered)'},
'pn412': {'name': 'PUBG patch notes, Update 41.2 (Miramar secret rooms)', 'url': 'https://pubg.com/en/news/10080'},
'psrooms': {'name': 'pubgsecretrooms.com (exact secret room spots)', 'url': 'https://pubgsecretrooms.com/{slug}-secret-room-locations/'},
}
NAMES = {
'erangel': {'boatyard': 'Boatyard', 'farm': 'Farm', 'ferrypier': 'Ferry Pier', 'gatka': 'Gatka', 'georgopol': 'Georgopol',
'hospital': 'Hospital', 'kameshki': 'Kameshki', 'lipovka': 'Lipovka', 'mansion': 'Mansion', 'mylta': 'Mylta',
'myltapower': 'Mylta Power', 'novorepnoye': 'Novorepnoye', 'pochinki': 'Pochinki', 'primorsk': 'Primorsk',
'prison': 'Prison', 'quarry': 'Quarry', 'rozhok': 'Rozhok', 'ruins': 'Ruins', 'school': 'School',
'severny': 'Severny', 'shelter': 'Shelter', 'shootingrange': 'Shooting Range',
'sosnovkamilitarybase': 'Military Base', 'stalber': 'Stalber', 'yasnayapolyana': 'Yasnaya Polyana',
'zharki': 'Zharki'},
'miramar': {'alcantara': 'Alcantara', 'brickyard': 'Brick Yard', 'campomilitar': 'Campo Militar', 'cantera': 'Cantera',
'chumacera': 'Chumacera', 'craterfields': 'Crater Fields', 'cruzdelvalle': 'Cruz del Valle',
'elazahar': 'El Azahar', 'elpozo': 'El Pozo', 'graveyard': 'Graveyard',
'haciendadelpatron': 'Hacienda del Patron', 'impala': 'Impala', 'lacobreria': 'La Cobreria',
'losleones': 'Los Leones', 'manisgenerales': 'Minas Generales', 'montenuevo': 'Monte Nuevo',
'oasis': 'Oasis', 'partona': 'Partona', 'pecado': 'Pecado',
'powergrid': 'Power Grid', 'prison': 'Prison', 'puertoparaiso': 'Puerto Paraiso', 'resort': 'Resort',
'sanmartin': 'San Martin', 'tierrabronca': 'Tierra Bronca', 'truckstop': 'Truck Stop',
'valledelmar': 'Valle del Mar', 'watertreatment': 'Water Treatment'},
'sanhok': {'bantai': 'Ban Tai', 'bootcamp': 'Bootcamp', 'campalpha': 'Camp Alpha', 'campbravo': 'Camp Bravo',
'campcharlie': 'Camp Charlie', 'cave': 'Cave', 'docks': 'Docks', 'hatinh': 'Ha Tinh', 'kampong': 'Kampong',
'khao': 'Khao', 'lawaki': 'Lakawi', 'mongnai': 'Mongnai', 'nakham': 'Na Kham', 'painan': 'Pai Nan',
'paradiseresort': 'Paradise Resort', 'quarry': 'Quarry', 'ruins': 'Ruins', 'sahmee': 'Sahmee',
'tambang': 'Tambang', 'tatmok': 'Tat Mok'},
'vikendi': {'castle': 'Castle', 'coalmine': 'Coal Mine', 'dekamesto': 'Deka Mesto', 'dinoland': 'Dinoland',
'kranik': 'Kranik', 'laveni': 'Laveni', 'lumberyard': 'Lumber Yard', 'naros': 'Naros',
'naznova': 'Naznova', 'observatory': 'Observatory', 'pavilka': 'Pavilka',
'trainstation': 'Train Station', 'trika': 'Trika', 'winery': 'Winery'},
'taego': {'airport': 'Airport', 'armybase': 'Army Base', 'buksansa': 'Buk San Sa', 'fishingcamp': 'Fishing Camp',
'godok': 'Go Dok', 'haemoosa': 'Hae Moo Sa', 'hapo': 'Ha Po', 'happy_hospital': 'Happy Hospital',
'hosan': 'Ho San', 'hosanprison': 'Ho San Prison', 'hospital': 'Hospital', 'kangneung': 'Kang Neung',
'ohhyang': 'Oh Hyang', 'palace': 'Palace', 'school': 'School', 'shipyard': 'Shipyard', 'songam': 'Song Am',
'terminal': 'Terminal', 'wolsong': 'Wol Song', 'yongcheon': 'Yong Cheon'},
'rondo': {'BeiLi': 'Bei Li', 'danching': 'Dan Ching', 'fongtun': 'Fong Tun', 'hemaytown': 'Hemay Town',
'hungshan': 'Hung Shan', 'jadenacity': 'Jadena City', 'jaotin': 'Jao Tin', 'kunxia': 'Kun Xia',
'lanpo': 'Lan Po', 'lohuaxing': 'Lo Hua Xing', 'longho': 'Long Ho', 'meyran': 'Mey Ran', 'minhu': 'Min Hu',
'muhopan': 'Mu Ho Pan', 'nanchuan': 'Nan Chuan', 'neoxfactory': 'NEOX Factory', 'raian': 'Rai An',
'rinjiang': 'Rin Jiang', 'stadium': 'Stadium', 'testtrack': 'Test Track', 'tinlonggarden': 'Tin Long Garden',
'tuling': 'Tu Ling', 'yulin': 'Yu Lin', 'yungu': 'Yun Gu'},
'karakin': {'alhabar': 'Al Habar', 'alhayik': 'Al Hayik', 'bahrsahir': 'Bahr Sahir', 'bashara': 'Bashara',
'hadiqanemo': 'Hadiqa Nemo'},
'paramo': {'Lab': 'Lab', 'Level_A': 'City slot A', 'Level_B': 'City slot B', 'Level_C': 'City slot C',
'Level_D': 'City slot D'},
'haven': {'coalyards': 'Coal Yards', 'docks': 'Docks', 'industrialpark': 'Industrial Park', 'railyard': 'Rail Yard',
'residential': 'Residential', 'steelmill': 'Steel Mill'},
}
# Hot, balanced and quiet drops: (poi slug or display name, tier, note, source)
DROPS = {
'erangel': [('pochinki', 'hot', 'Centre, dense housing: guaranteed early fights', 'psr'),
('georgopol', 'hot', 'Containers and warehouses, a lot of loot, narrow lanes', 'psr'),
('sosnovkamilitarybase', 'hot', 'Richest loot on the map; bridges are easy to hold, hard to leave', 'psr'),
('school', 'hot', '"Drop School if you\'re feeling feisty"', 'pcg'),
('yasnayapolyana', 'busy', 'Large city block, high loot, medium-high traffic', 'psr'),
('rozhok', 'balanced', 'Small, loot-dense, quick to clear', 'psr'),
('novorepnoye', 'balanced', 'Warehouses with less traffic', 'psr'),
('gatka', 'quiet', 'Few buildings, very little competition', 'psr'),
('lipovka', 'quiet', 'Few buildings, very little competition', 'psr')],
'miramar': [('pecado', 'hot', 'One of Miramar\'s best loot spots', 'pcg'),
('haciendadelpatron', 'hot', 'One of Miramar\'s best loot spots', 'pcg'),
('losleones', 'hot', 'Big city, one of the best loot spots', 'pcg'),
('elpozo', 'hot', 'Big city, one of the best loot spots', 'pcg')],
'sanhok': [('bootcamp', 'hot', 'Top-tier gear, early fights almost guaranteed', 'pcg'),
('ruins', 'hot', 'Top-tier gear', 'pcg'),
('paradiseresort', 'hot', 'Top-tier gear', 'pcg'),
('kampong', 'quiet', 'Landing spot to save the heated battles for later', 'pcg'),
('docks', 'quiet', 'Landing spot to save the heated battles for later', 'pcg')],
'vikendi': [('castle', 'hot', 'Centre; among the most useful loot', 'psr'),
('dinoland', 'balanced', 'Good loot (PCGamesN); a quieter alternative (pubgsecretroom.com)', 'pcg')],
'taego': [('palace', 'hot', 'One of the two big loot magnets', 'psr'),
('terminal', 'hot', 'One of the two big loot magnets', 'psr'),
('hosan', 'balanced', 'The balanced option', 'psr')],
'deston': [('Ripton', 'hot', 'Downtown core: more contested than Concert and Lodge', 'psr'),
('Concert', 'balanced', 'One of the strongest early landings, less contested than downtown', 'psr'),
('Lodge', 'balanced', 'One of the strongest early landings, less contested than downtown', 'psr')],
'rondo': [('jadenacity', 'hot', 'Dense skyscrapers and strong loot; avoid when early on the flight path', 'epic'),
('stadium', 'hot', 'High-loot bloodbath', 'isp'),
('neoxfactory', 'balanced', 'Central, lower-risk; vehicle spawns and test track to rotate', 'epic'),
('yulin', 'quiet', 'For a stealthy start', 'isp'),
('hungshan', 'quiet', 'Safer early loot', 'isp')],
}
# Secret rooms and keys anchored to a named location: (poi, layer, title, where, source)
SECRETS = {
'erangel': [('pochinki', 'secret', 'Church basement room', 'Basement of the Pochinki church; no key needed (Update 14.1)', 'pn141')],
'miramar': [('craterfields', 'secret', 'Secret room (mine)', 'West of Crater Fields, rocky area between La Cobreria and El Pozo', 'ib'),
('oasis', 'secret', 'Secret room (mine)', 'South of Oasis, close to the northern road', 'ib'),
('watertreatment', 'secret', 'Secret room (mine)', 'North of Water Treatment, on the rocks above the compound', 'ib'),
('elazahar', 'secret', 'Secret room (mine)', 'North-west of El Azahar, near the hills below Tierra Bronca', 'ib'),
('elpozo', 'secret', 'Secret room (mine)', 'East of El Pozo, on the route toward San Martin', 'ib')],
'deston': [('Barclift', 'secret', 'Security room', 'Barclift, east', 'gamepur'),
('Buxley', 'secret', 'Security room', 'Buxley, north-west part', 'gamepur'),
("Carpenter’s End", 'secret', 'Security room', "Carpenter's End, centre", 'gamepur'),
('Cavala', 'secret', 'Security room', 'Cavala, north', 'gamepur'),
('Concert', 'secret', 'Security room', 'Concert, centre', 'gamepur'),
('Holston Meadows', 'secret', 'Security room', 'Holston Meadows, centre', 'gamepur'),
('Sancarna', 'secret', 'Security room', 'Sancarna, north-east', 'gamepur'),
('Ripton', 'secret', 'Security rooms', 'Ripton, north; Ripton has the most rooms and trailers', 'gamepur'),
('Ten Forts', 'secret', 'Security room', 'Ten Forts, south', 'gamepur'),
('Turrita', 'secret', 'Security room', 'Turrita, north', 'gamepur'),
('Wind Farms', 'secret', 'Security room', 'Wind Farm, north of the bridge', 'gamepur'),
('Ten Forts', 'keys', 'Security key (drone)', 'North of Ten Forts', 'loadout'),
("Carpenter’s End", 'keys', 'Security key (drone)', "North-west of Carpenter's End", 'loadout'),
('Wind Farms', 'keys', 'Security key (drone)', 'Middle of Wind Farm', 'loadout'),
('Buxley', 'keys', 'Security key (drone)', 'South-west of Buxley', 'loadout'),
('Turrita', 'keys', 'Security key (drone)', 'South-west of Turrita', 'loadout'),
('Barclift', 'keys', 'Security key (drone)', 'South of Barclift', 'loadout'),
('Lodge', 'keys', 'Security key (drone)', 'North-east of Lodge', 'loadout'),
('Arena', 'keys', 'Security keys (drone)', 'North and south of Arena', 'loadout'),
('Cavala', 'keys', 'Security key (drone)', 'North-west of Cavala', 'loadout'),
('Sancarna', 'keys', 'Security key (drone)', 'South-west of Sancarna', 'loadout'),
('El Koro', 'keys', 'Security key (drone)', 'North of El Koro', 'loadout'),
('Holston Meadows', 'keys', 'Security key (drone)', 'North of Holston Meadows', 'loadout')],
}
# Per-map notes for things without sourceable coordinates.
NOTES = {
'erangel': [('Secret rooms', 'Keyed secret rooms with underground entrances (keys are random world loot). Exact spots are not republished here; see the source map.', 'psrooms')],
'miramar': [('Secret rooms', 'Abandoned-mine secret rooms opened with world-spawned Secret Room Keys (Update 41.2, Normal and Custom matches). Five are placed here near their landmark; more exist, see the source map for all of them.', 'pn412')],
'taego': [('Secret rooms', 'Secret rooms behind blue corrugated roofs; keys spawn randomly (glowing pots can drop one). Exact spots: see the source map.', 'psrooms')],
'vikendi': [('Secret rooms, labs and bear caves', 'Bear spawns below come from the game files (bear caves hold airdrop-quality loot). Secret rooms and lab rooms: see the source map.', 'psrooms')],
'rondo': [('Secret rooms', 'Security door and keycard rooms built into compound clusters; keys are random floor loot. Exact spots: see the source map.', 'psrooms')],
'paramo': [('Dynamic map', 'Paramo\'s cities move between the four city slots each match; the slot points below are where a city can appear.', 'gs'),
('Secret rooms', 'Stone-door secret rooms. Exact spots: see the source map.', 'psrooms')],
'deston': [('Security rooms', 'Rooms open with Pillar security keys, which sit in drone-only spots near the listed places (not in El Koro, Lodge or Arena). Positions are the named place, not the door.', 'loadout')],
}
VEH_TYPE = {
'Road': 'car', 'RoadPlus': 'car', 'Offroad': 'car', 'Rich_Houses': 'car', 'Poor_Houses': 'car', 'Farm': 'car',
'FoodTruck': 'car', 'Sports': 'car', 'Picobus': 'car', 'Start': 'car',
'Garage': 'garage', 'D_Garage': 'garage', 'Police_Stations': 'garage',
'Water': 'boat', 'WaterA': 'boat', 'WaterB': 'boat', 'WaterC': 'boat', 'WaterCave': 'boat', 'WaterSea': 'boat',
'Ocean': 'boat', 'River': 'boat', 'Dam': 'boat', 'W_Garage': 'boat', 'Water_Ferry': 'boat',
'Motorglider': 'glider', 'Hacienda_GoldMirado': 'special', 'OnlyBikeForCaveOnly': 'special',
}
LAYER_OF = {'car': 'cars', 'garage': 'garages', 'boat': 'boats', 'glider': 'gliders', 'special': 'special'}
CAT_LABEL = {'Road': 'Road spot', 'RoadPlus': 'Road spot (extra)', 'Offroad': 'Off-road spot', 'Rich_Houses': 'Rich houses',
'Poor_Houses': 'Poor houses', 'Farm': 'Farm (Pony Coupe)', 'FoodTruck': 'Food truck', 'Sports': 'Sports',
'Picobus': 'Pico Bus', 'Start': 'Start buggy', 'Garage': 'Garage', 'D_Garage': 'Downtown garage',
'Police_Stations': 'Police station (Pillar car)', 'Water': 'Boat', 'WaterA': 'Boat', 'WaterB': 'Boat',
'WaterC': 'Boat', 'WaterCave': 'Cave boat', 'WaterSea': 'Sea boat', 'Ocean': 'Ocean (Aquarail)',
'River': 'River (Airboat)', 'Dam': 'Dam (Airboat)', 'W_Garage': 'Boat garage (Airboat)',
'Water_Ferry': 'Ferry', 'Motorglider': 'Motor glider', 'Hacienda_GoldMirado': 'Gold Mirado',
'OnlyBikeForCaveOnly': 'Cave bike'}
def r5(v):
return round(v, 5)
def build(slug):
d = json.load(open(os.path.join(SRC, f'layers-{DEV[slug]}.json')))
E = d['extent']
L = d['layers']
inb = lambda x, y: 1000 < x < E - 1000 and 1000 < y < E - 1000
out = collections.OrderedDict()
skipped = collections.Counter()
# Named places (dedupe by display name; Deston has slug duplicates of the zone markers).
names = NAMES.get(slug, {})
def display(p):
if slug == 'deston':
return 'Swamp' if p['name'] == 'swamp' else p['name'].replace('’', "'")
return names[p['name']]
pois = {}
for p in L.get('pois', []):
nm = p['name']
if slug == 'deston':
if p.get('level') != 'Level_Design_Zone':
continue
disp = 'Swamp' if nm == 'swamp' else nm.replace('’', "'")
else:
disp = names.get(nm)
if not disp:
skipped['poi:' + nm] += 1
continue
if disp in pois:
continue
pois[disp] = p
pois[nm] = p
towns = []
seen = set()
for k, p in pois.items():
if id(p) in seen:
continue
seen.add(id(p))
disp = display(p)
rr = max(p.get('r') or 0, 10000)
near = sum(1 for it in L.get('items', []) if (it[0] - p['x']) ** 2 + (it[1] - p['y']) ** 2 <= rr * rr)
towns.append({'x': r5(p['x'] / E), 'y': r5(p['y'] / E), 'n': disp, 'r': r5(rr / E),
'd': f'{near} item spots within {round(rr / 100)} m', 'w': near, 's': 'gs'})
out['towns'] = towns
def anchor(key):
p = pois.get(key)
if p is None:
raise SystemExit(f'{slug}: no named location {key!r}')
disp = display(p)
return p, disp
out['bridges'] = [{'x': r5(b['x'] / E), 'y': r5(b['y'] / E), 'n': 'Bridge', 's': 'gs'}
for b in L.get('bridges', []) if inb(b['x'], b['y']) and 'IBR' not in (b.get('level') or '')]
# Vehicles: normal battle royale only (no IBR levels, no esports groups).
rules = d.get('vehicleRules', {}).get('br', {}).get('groups', {})
spots = collections.defaultdict(list)
for v in L.get('vehicles', []):
lvl = v.get('level') or ''
if 'IBR' in lvl.upper() or 'IBR' in (v.get('tag') or '').upper():
continue
g = v.get('group') or 'Default'
spots[g].append(v)
for g, vs in spots.items():
if g == 'Default':
cat, rule = 'Road', rules.get('A', [{}])[0]
else:
rs = rules.get(g)
if not rs:
skipped[f'vehicles:{g} (no rule)'] += len(vs)
continue
rule = rs[-1] if slug == 'rondo' and g == 'E' else rs[0]
cat = rule['categories'][0]
if cat.lower().startswith('esports') or cat in ('Special', 'Air'):
skipped[f'vehicles:{cat}'] += len(vs)
continue
cnt = rule.get('count') or [0, 0]
if not cnt or max(cnt) == 0:
skipped[f'vehicles:{cat} (0 per match)'] += len(vs)
continue
t = VEH_TYPE.get(cat)
if not t:
skipped[f'vehicles:{cat}'] += len(vs)
continue
n_spots = len(vs) + (len(spots.get('A', [])) if g == 'Default' else 0)
if g == 'A':
n_spots += len(spots.get('Default', []))
lo, hi = cnt[0], cnt[-1]
pool = [x if isinstance(x, str) else (x.get('name') or x.get('vehicle')) for x in (rule.get('vehicles') or [])]
chance = min(1.0, hi / max(1, n_spots))
note = f"{lo if lo == hi else f'{lo}-{hi}'} of {n_spots} spots spawn a vehicle each match (~{round(chance * 100)}%)"
if pool:
note += '; pool: ' + ', '.join(dict.fromkeys(p for p in pool if p))
lay = LAYER_OF[t]
for v in vs:
if not inb(v['x'], v['y']):
continue
out.setdefault(lay, []).append({'x': r5(v['x'] / E), 'y': r5(v['y'] / E), 'n': CAT_LABEL.get(cat, cat),
't': t, 'd': note, 'p': round(chance, 2), 's': 'gs'})
# Drops (hot / balanced / quiet), anchored on the named place.
for key, tier, note, src in DROPS.get(slug, []):
p, disp = anchor(key)
out.setdefault('drops', []).append({'x': r5(p['x'] / E), 'y': r5(p['y'] / E), 'n': disp, 't': tier, 'd': note, 's': src})
# Secret rooms and keys: approximate, shown as a ring around the named place.
for key, lay, title, where, src in SECRETS.get(slug, []):
p, disp = anchor(key)
out.setdefault(lay, []).append({'x': r5(p['x'] / E), 'y': r5(p['y'] / E), 'n': title, 'd': where + ' (approximate: drawn on the named place)',
'a': 1, 'r': r5(max(p.get('r') or 0, 20000) / E), 's': src})
out['towers'] = [{'x': r5(t['x'] / E), 'y': r5(t['y'] / E), 'n': 'Blue Chip tower', 'd': 'Blue Chip Transmitter spot', 's': 'gs'}
for t in L.get('towers', []) if inb(t['x'], t['y']) and 'IBR' not in (t.get('level') or '')]
out['vending'] = [{'x': r5(t['x'] / E), 'y': r5(t['y'] / E), 'n': 'Vending machine', 's': 'gs'}
for t in L.get('vending', []) if inb(t['x'], t['y'])]
out['bears'] = [{'x': r5(t['x'] / E), 'y': r5(t['y'] / E), 'n': 'Polar bear spawn', 'd': 'Bear cave spawn; the caves hold airdrop-tier loot (see ImmortalBoost)', 's': 'gs'}
for t in L.get('other', []) if t.get('cls') == 'BP_BearSpawnStart_C' and inb(t['x'], t['y'])]
# Loot density: item spots per ~200 m cell (normal BR levels only), top cells kept.
cell = 20000 if E >= 400000 else 10000
grid = collections.Counter()
for it in L.get('items', []):
x, y = it[0], it[1]
if inb(x, y):
grid[(int(x // cell), int(y // cell))] += 1
if grid:
counts = sorted(grid.values(), reverse=True)
keep = max(10, len(counts) // 12)
floor = counts[min(keep, len(counts)) - 1]
top = counts[0]
out['loot'] = [{'x': r5((i + 0.5) * cell / E), 'y': r5((j + 0.5) * cell / E), 'r': r5(cell / E / 2),
'w': round(n / top, 2), 'n': f'{n} item spots in this {cell // 100} m square', 's': 'gs'}
for (i, j), n in sorted(grid.items(), key=lambda kv: -kv[1]) if n >= floor]
out = {k: v for k, v in out.items() if v}
notes = [{'title': t, 'text': x, 's': s} for t, x, s in NOTES.get(slug, [])]
used = {p['s'] for v in out.values() for p in v} | {n['s'] for n in notes} | ({'ib'} if out.get('bears') else set())
srcs = {k: {kk: (vv.replace('{slug}', slug) if isinstance(vv, str) else vv) for kk, vv in SOURCES[k].items()} for k in sorted(used)}
doc = {'v': 1, 'map': slug, 'built': BUILT, 'sources': srcs, 'notes': notes, 'layers': out}
os.makedirs(OUT, exist_ok=True)
path = os.path.join(OUT, f'{slug}.v1.json')
with open(path, 'w') as f:
json.dump(doc, f, ensure_ascii=False, separators=(',', ':'))
print(f'{slug:8s} {os.path.getsize(path) // 1024:4d} KB', {k: len(v) for k, v in out.items()}, dict(skipped) if skipped else '')
for s in DEV:
build(s)
+369 -5
View File
@@ -20,7 +20,22 @@
import Footprints from 'lucide-svelte/icons/footprints';
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
import X from 'lucide-svelte/icons/x';
import Layers from 'lucide-svelte/icons/layers';
import { PIN_SVG, esc, initials } from './pins';
import {
DROP_COLOR,
DROP_LABEL,
INTEL_GROUPS,
INTEL_KEY,
INTEL_LAYERS,
INTEL_SVG,
VEHICLE_ICON,
loadIntel,
readLayerPrefs,
type IntelDoc,
type IntelLayerId,
type IntelPoint
} from './intel';
import {
KIND_INFO,
KINDS,
@@ -75,6 +90,8 @@
timeZone: 'Europe/Amsterdam'
});
const ME_KEY = 'pubg-map:me';
const INTEL_CREDIT =
'Map intel: <a href="https://pubg.gs/" target="_blank" rel="noreferrer">pubg.gs</a> and drop guides (sources under Layers)';
// Connection and room state (the markers themselves live in `layers`, outside Svelte).
let conn = $state<'connecting' | 'live' | 'down' | 'gone'>('connecting');
@@ -95,6 +112,13 @@
let showGrid = $state(true);
let showPast = $state(false);
let drawer = $state(false);
let layersOpen = $state(false);
// Zoomed out: intel icons shrink to dots so the map stays readable.
let far = $state(true);
// Built-in intel layers: per player (localStorage), never sent to the room.
let layerOn = $state(readLayerPrefs(null));
// undefined while loading, null for a map without intel (Camp Jackal).
let intel = $state.raw<IntelDoc | null | undefined>(undefined);
let picker = $state(false);
let fullscreen = $state(false);
let toast = $state<string | null>(null);
@@ -124,6 +148,9 @@
let gridLayer: Leaflet.LayerGroup | undefined;
let pastLayer: Leaflet.LayerGroup | undefined;
let tempLayer: Leaflet.LayerGroup | undefined;
let intelLayer: Leaflet.LayerGroup | undefined;
let attrib: Leaflet.Control.Attribution | undefined;
let creditShown = false;
let ws: WebSocket | null = null;
let disposed = false;
let retry = 0;
@@ -463,6 +490,113 @@
}
}
function intelTip(p: IntelPoint, kind: string) {
const src = intel?.sources[p.s];
const parts = [`<b>${esc(p.n)}</b>`];
if (kind) parts.push(` <span class="pm-dim">${esc(kind)}</span>`);
if (p.d) parts.push(`<br>${esc(p.d)}`);
if (src)
parts.push(
`<br><span class="pm-dim">Source: ${esc(src.name)}${p.a ? ' · approximate spot' : ''}</span>`
);
return parts.join('');
}
function intelIcon(svg: string, color: string, cls = '', opacity = 1) {
return L.divIcon({
className: 'pm-icon',
html: `<div class="pm-ii ${cls}" style="--c:${color};opacity:${opacity}">${svg}</div>`,
iconSize: [18, 18],
iconAnchor: [9, 9]
});
}
// Few and important: these keep their full icon when zoomed out.
const KEEP_ICON = new Set<IntelLayerId>(['special', 'drops', 'secret', 'keys', 'bears']);
function drawIntel() {
if (!intelLayer || !L) return;
intelLayer.clearLayers();
if (!intel) return;
const tip = { className: 'pm-tip', direction: 'top', offset: [0, -8] } as const;
for (const def of INTEL_LAYERS) {
const pts = intel.layers[def.id];
if (!layerOn[def.id] || !pts?.length) continue;
for (const p of pts) {
if (def.id === 'loot') {
const r = p.r ?? 0.01;
L.rectangle([ll(p.x - r, p.y - r), ll(p.x + r, p.y + r)], {
pane: 'pm-area',
color: def.color,
weight: 0.5,
opacity: 0.6,
fillColor: def.color,
fillOpacity: 0.12 + 0.4 * (p.w ?? 0.5)
})
.bindTooltip(intelTip(p, 'loot density'), { className: 'pm-tip', sticky: true })
.addTo(intelLayer);
continue;
}
if (p.a && p.r)
L.circle(ll(p.x, p.y), {
pane: 'pm-area',
radius: p.r * S,
color: def.color,
weight: 1.5,
dashArray: '4 5',
fillColor: def.color,
fillOpacity: 0.06,
interactive: false
}).addTo(intelLayer);
let svg = INTEL_SVG[def.icon as keyof typeof INTEL_SVG];
let color = def.color;
let kind = '';
let opacity = 1;
if (def.group === 'Vehicles') {
svg = INTEL_SVG[VEHICLE_ICON[p.t ?? ''] ?? 'car'];
// Fainter = less likely to hold a vehicle this match.
if (p.p !== undefined) opacity = 0.45 + 0.55 * Math.min(1, p.p * 1.5);
if (p.p !== undefined) kind = `~${Math.round(p.p * 100)}% per match`;
} else if (def.id === 'drops') {
color = DROP_COLOR[p.t ?? ''] ?? def.color;
kind = DROP_LABEL[p.t ?? ''] ?? '';
}
L.marker(ll(p.x, p.y), {
pane: 'pm-intel',
icon: intelIcon(
svg,
color,
[p.a && 'approx', def.id === 'towns' && 'town', KEEP_ICON.has(def.id) && 'keep']
.filter(Boolean)
.join(' '),
opacity
),
keyboard: false,
bubblingMouseEvents: true
})
.bindTooltip(intelTip(p, kind), tip)
.addTo(intelLayer);
}
}
}
// The intel credit shows while the current map has intel.
function syncCredit() {
if (!attrib || !!intel === creditShown) return;
creditShown = !!intel;
if (creditShown) attrib.addAttribution(INTEL_CREDIT);
else attrib.removeAttribution(INTEL_CREDIT);
}
function setLayer(id: IntelLayerId, on: boolean) {
layerOn[id] = on;
try {
localStorage.setItem(INTEL_KEY, JSON.stringify(layerOn));
} catch {
// private mode: the choice lasts this tab only
}
}
let tilesSlug = '';
function setMap(slug: string, fit: boolean) {
if (!map) return;
@@ -640,11 +774,17 @@
tempLayer?.clearLayers();
note = null;
picker = false;
layersOpen = false;
confirmClear = false;
} else if (k === 'g') showGrid = !showGrid;
else if (k === 'o') showPast = !showPast;
else if (k === 't') drawer = !drawer;
else if (k === 'm') picker = !picker;
else if (k === 't') {
drawer = !drawer;
if (drawer) layersOpen = false;
} else if (k === 'k') {
layersOpen = !layersOpen;
if (layersOpen) drawer = false;
} else if (k === 'm') picker = !picker;
else if (k === 'f') toggleFullscreen();
else if (k === ' ' && hover) {
e.preventDefault();
@@ -657,6 +797,23 @@
void current;
drawGrid();
});
// Intel for the current map: fetched once per map, then redrawn when layers toggle.
$effect(() => {
const slug = current.slug;
intel = undefined;
loadIntel(slug).then((d) => {
if (current.slug === slug) intel = d;
});
});
$effect(() => {
void intel;
for (const l of INTEL_LAYERS) void layerOn[l.id];
drawIntel();
});
$effect(() => {
void intel;
syncCredit();
});
$effect(() => {
void showPast;
void focusMatch;
@@ -681,6 +838,11 @@
// no saved nickname
}
if (!me) openName();
try {
layerOn = readLayerPrefs(localStorage.getItem(INTEL_KEY));
} catch {
// storage blocked: defaults
}
import('leaflet').then((mod) => {
if (disposed) return;
L = (mod as unknown as { default: typeof Leaflet }).default ?? mod;
@@ -697,7 +859,14 @@
maxBoundsViscosity: 0.8,
attributionControl: false
});
L.control.attribution({ prefix: false, position: 'bottomleft' }).addTo(map);
attrib = L.control.attribution({ prefix: false, position: 'bottomleft' }).addTo(map);
creditShown = false;
syncCredit();
// Intel sits under everything players place: areas above the tiles, points under
// past landings (overlayPane 400) and room markers (markerPane 600).
map.createPane('pm-area').style.zIndex = '330';
map.createPane('pm-intel').style.zIndex = '380';
intelLayer = L.layerGroup().addTo(map);
L.control.zoom({ position: 'bottomright' }).addTo(map);
gridLayer = L.layerGroup().addTo(map);
pastLayer = L.layerGroup().addTo(map);
@@ -710,7 +879,12 @@
map.on('mousemove', (e: Leaflet.LeafletMouseEvent) => (hover = xy(e.latlng)));
map.on('mouseout', () => (hover = null));
map.on('movestart', () => (note = null));
// Dots below ~1.5 zoom steps past "whole map fits"; full icons once zoomed in.
const farBelow = map.getZoom() + 1.5;
far = true;
map.on('zoomend', () => (far = (map?.getZoom() ?? 0) < farBelow));
drawGrid();
drawIntel();
connect();
});
const onFs = () => {
@@ -726,6 +900,8 @@
map?.remove();
map = undefined;
tiles = undefined;
intelLayer = undefined;
attrib = undefined;
tilesSlug = '';
};
}
@@ -733,7 +909,7 @@
<svelte:window onkeydown={onKey} />
<div class={['pm', tool && 'picking']}>
<div class={['pm', tool && 'picking', far && 'far']}>
<div class="pm-map" {@attach attachMap}></div>
<!-- Top bar: room, match, map, connection; players and actions on the right. -->
@@ -779,9 +955,20 @@
onclick={() => (showPast = !showPast)}
title="Past landings on this map (O)"><Footprints size={16} /></button
>
<button
class={['btn', layersOpen && 'on']}
onclick={() => {
layersOpen = !layersOpen;
if (layersOpen) drawer = false;
}}
title="Map layers: vehicles, drops, loot, secrets (K)"><Layers size={16} /></button
>
<button
class={['btn', drawer && 'on']}
onclick={() => (drawer = !drawer)}
onclick={() => {
drawer = !drawer;
if (drawer) layersOpen = false;
}}
title="Match history (T)"><History size={16} /></button
>
<button class="btn" onclick={toggleFullscreen} title="Fullscreen (F)">
@@ -909,6 +1096,90 @@
</div>
{/if}
{#if layersOpen}
<aside class="drawer layers" aria-label="Map layers">
<div class="picker-head">
<span>{current.name} layers</span><button
class="btn"
onclick={() => (layersOpen = false)}
aria-label="Close"><X size={15} /></button
>
</div>
<p class="small top">Only on your screen; your friends pick their own.</p>
{#each INTEL_GROUPS as group (group)}
{@const defs = INTEL_LAYERS.filter((l) => l.group === group)}
<section>
<h3>{group}</h3>
{#each defs as def (def.id)}
{@const n = intel?.layers[def.id]?.length ?? 0}
<label class={['layer', !n && 'none']} title={def.hint}>
<input
type="checkbox"
checked={layerOn[def.id]}
disabled={!n}
onchange={(e) => {
setLayer(def.id, e.currentTarget.checked);
// Hand the keyboard shortcuts back to the map.
e.currentTarget.blur();
}}
/>
<span class="ic" style:--c={def.color}>
<!-- eslint-disable-next-line svelte/no-at-html-tags -- static Lucide icon nodes from intel.ts -->
{@html INTEL_SVG[def.icon as keyof typeof INTEL_SVG]}
</span>
<span class="name">{def.label}</span>
<span class="n">{intel ? n || 'none' : intel === null ? '' : '…'}</span>
</label>
{/each}
</section>
{/each}
{#if layerOn.drops && intel?.layers.drops?.length}
<p class="legend">
{#each Object.entries(DROP_COLOR) as [tier, c] (tier)}
<span style:--c={c}>{DROP_LABEL[tier]}</span>
{/each}
</p>
{/if}
{#if intel?.notes.length}
<section>
<h3>Notes</h3>
{#each intel.notes as nt, i (i)}
<p class="note-item">
<b>{nt.title}.</b>
{nt.text}
{#if intel.sources[nt.s]}<a
href={intel.sources[nt.s].url}
target="_blank"
rel="noreferrer">{intel.sources[nt.s].name}</a
>{/if}
</p>
{/each}
</section>
{/if}
<p class="small">
Dashed rings are approximate: drawn on the named place, not the exact spot. Faint vehicle
icons rarely hold a vehicle.
</p>
{#if intel}
<section>
<h3>Sources</h3>
<ul class="sources">
{#each Object.entries(intel.sources) as [k, src] (k)}
<li>
<a href={src.url} target="_blank" rel="noreferrer">{src.name}</a>{#if src.note}
<span class="pm-dim"> · {src.note}</span>{/if}
</li>
{/each}
</ul>
</section>
{:else if intel === null}
<p class="small">No built-in intel for this map.</p>
{:else}
<p class="small">Loading map intel…</p>
{/if}
</aside>
{/if}
{#if drawer}
<aside class="drawer" aria-label="Match history">
<div class="picker-head">
@@ -1328,6 +1599,74 @@
width: 300px;
overflow: auto;
}
.layers h3 {
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
margin: 10px 0 4px;
}
.layers .top {
margin-top: 0;
}
.layer {
display: flex;
align-items: center;
gap: 8px;
padding: 3px 4px;
border-radius: 7px;
cursor: pointer;
}
.layer:hover {
background: rgb(255 255 255 / 0.06);
}
.layer.none {
opacity: 0.45;
cursor: default;
}
.layer input {
accent-color: #fafafa;
}
.layer .ic {
color: var(--c);
display: grid;
}
.layer .name {
flex: 1;
}
.layer .n {
color: var(--muted);
font-size: 11.5px;
font-variant-numeric: tabular-nums;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 6px;
}
.legend span {
font-size: 11px;
padding: 1px 7px;
border-radius: 999px;
border: 1px solid var(--c);
color: var(--c);
}
.note-item {
font-size: 12px;
line-height: 1.45;
margin-bottom: 6px;
}
.layers a {
color: var(--text);
text-decoration: underline;
text-decoration-color: rgb(255 255 255 / 0.35);
}
.layers .sources {
gap: 3px;
font-size: 11.5px;
}
.drawer ul {
display: flex;
flex-direction: column;
@@ -1518,6 +1857,31 @@
.pm :global(.pm-tip::before) {
display: none;
}
.pm :global(.pm-ii) {
width: 100%;
height: 100%;
display: grid;
place-items: center;
border-radius: 50%;
background: rgb(10 10 10 / 0.78);
color: var(--c);
border: 1px solid color-mix(in oklab, var(--c) 65%, transparent);
}
.pm :global(.pm-ii.town) {
border-radius: 5px;
}
.pm.far :global(.pm-ii:not(.keep)) {
transform: scale(0.42);
background: var(--c);
border: 2px solid rgb(0 0 0 / 0.6);
}
.pm.far :global(.pm-ii:not(.keep) svg) {
display: none;
}
.pm :global(.pm-ii.approx) {
border-style: dashed;
border-width: 1.5px;
}
.pm :global(.pm-dim) {
color: var(--muted);
}
+40
View File
@@ -0,0 +1,40 @@
import { readFileSync, readdirSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
import { INTEL_LAYERS, INTEL_MAPS, readLayerPrefs, type IntelDoc } from './intel';
import { MAPS } from './shared';
describe('pubg map intel', () => {
it('defaults to places and vehicles, and keeps saved choices', () => {
const d = readLayerPrefs(null);
expect(d.towns && d.cars && d.boats).toBe(true);
expect(d.drops || d.loot || d.secret).toBe(false);
const saved = readLayerPrefs(JSON.stringify({ cars: false, drops: true, bogus: true }));
expect(saved.cars).toBe(false);
expect(saved.drops).toBe(true);
expect(readLayerPrefs('{not json')).toEqual(d);
});
it('ships sourced, in-bounds intel for every map', () => {
const dir = 'static/pubg-intel';
const files = readdirSync(dir);
const ids = new Set<string>(INTEL_LAYERS.map((l) => l.id));
expect(files.sort()).toEqual([...INTEL_MAPS].map((s) => `${s}.v1.json`).sort());
const slugs = new Set(MAPS.map((m) => m.slug));
for (const f of files) {
const doc: IntelDoc = JSON.parse(readFileSync(`${dir}/${f}`, 'utf8'));
expect(f).toBe(`${doc.map}.v1.json`);
expect(slugs.has(doc.map)).toBe(true);
for (const [id, pts] of Object.entries(doc.layers)) {
expect(ids.has(id)).toBe(true);
for (const p of pts ?? []) {
expect(p.x).toBeGreaterThan(0);
expect(p.x).toBeLessThan(1);
expect(p.y).toBeGreaterThan(0);
expect(p.y).toBeLessThan(1);
expect(doc.sources[p.s]?.url).toMatch(/^https:\/\//);
}
}
for (const n of doc.notes) expect(doc.sources[n.s]).toBeTruthy();
}
});
});
+296
View File
@@ -0,0 +1,296 @@
// Built-in, read-only intel layers for the PUBG drop map: towns, vehicle spots, drops,
// loot and secret rooms per map. Static JSON (static/pubg-intel/<slug>.v1.json, built by
// scripts/build-pubg-intel.py); nothing here is stored per room or sent over the socket.
export const INTEL_VERSION = 1;
export const INTEL_KEY = 'pubg-map:layers';
export type IntelPoint = {
x: number;
y: number;
/** Name shown first in the tooltip. */
n: string;
/** Note. */
d?: string;
/** Sub-type (vehicle type, drop tier). */
t?: string;
/** Source id (key into IntelDoc.sources). */
s: string;
/** 1 = approximate: drawn on the named place, not the exact spot. */
a?: 1;
/** Radius in map units (0..1), for rings and loot cells. */
r?: number;
/** Weight 0..1 (loot cells) or a count (towns). */
w?: number;
/** Spawn chance 0..1 (vehicle spots). */
p?: number;
};
export type IntelSource = { name: string; url: string; note?: string };
export type IntelDoc = {
v: number;
map: string;
built: string;
sources: Record<string, IntelSource>;
notes: { title: string; text: string; s: string }[];
layers: Partial<Record<IntelLayerId, IntelPoint[]>>;
};
export const INTEL_GROUPS = ['Places', 'Vehicles', 'Drops', 'Loot', 'Secrets'] as const;
export type IntelGroup = (typeof INTEL_GROUPS)[number];
export type IntelLayerId =
| 'towns'
| 'bridges'
| 'cars'
| 'garages'
| 'boats'
| 'gliders'
| 'special'
| 'drops'
| 'loot'
| 'towers'
| 'vending'
| 'bears'
| 'secret'
| 'keys';
export type IntelLayer = {
id: IntelLayerId;
label: string;
group: IntelGroup;
color: string;
/** On for a player who never touched the layer panel. */
on: boolean;
icon: string;
hint: string;
};
// Lucide icon nodes (ISC licence, copied from lucide-svelte) plus a hand-drawn bridge,
// inlined because Leaflet divIcons are plain HTML.
const svg = (body: string, size = 12) =>
`<svg viewBox="0 0 24 24" width="${size}" height="${size}" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
const P = {
town: '<path d="M10 12h4"/><path d="M10 8h4"/><path d="M14 21v-3a2 2 0 0 0-4 0v3"/><path d="M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2"/><path d="M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"/>',
bridge:
'<path d="M2 12h20"/><path d="M4 12V8"/><path d="M20 12V8"/><path d="M4 8q8 6 16 0"/><path d="M8 12v4"/><path d="M16 12v4"/>',
car: '<path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2"/><circle cx="7" cy="17" r="2"/><path d="M9 17h6"/><circle cx="17" cy="17" r="2"/>',
garage:
'<path d="M18 21V10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v11"/><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 1.132-1.803l7.95-3.974a2 2 0 0 1 1.837 0l7.948 3.974A2 2 0 0 1 22 8z"/><path d="M6 13h12"/><path d="M6 17h12"/>',
boat: '<path d="M12 10.189V14"/><path d="M12 2v3"/><path d="M19 13V7a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v6"/><path d="M19.38 20A11.6 11.6 0 0 0 21 14l-8.188-3.639a2 2 0 0 0-1.624 0L3 14a11.6 11.6 0 0 0 2.81 7.76"/><path d="M2 21c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1s1.2 1 2.5 1c2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>',
glider:
'<path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/>',
crown:
'<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
flame:
'<path d="M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4"/>',
loot: '<path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z"/><path d="M12 22V12"/><polyline points="3.29 7 12 12 20.71 7"/><path d="m7.5 4.27 9 5.15"/>',
tower:
'<path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9"/><path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"/><circle cx="12" cy="9" r="2"/><path d="M16.2 4.8c2 2 2.26 5.11.8 7.47"/><path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1"/><path d="M9.5 18h5"/><path d="m8 22 4-11 4 11"/>',
vending:
'<path d="M5 6a4 4 0 0 1 4-4h6a4 4 0 0 1 4 4v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6Z"/><path d="M5 10h14"/><path d="M15 7v6"/>',
bear: '<circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/>',
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
key: '<path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/>'
};
export const INTEL_SVG = Object.fromEntries(
Object.entries(P).map(([k, v]) => [k, svg(v)])
) as Record<keyof typeof P, string>;
export const INTEL_LAYERS: IntelLayer[] = [
{
id: 'towns',
label: 'Named places',
group: 'Places',
color: '#e4e4e7',
on: true,
icon: 'town',
hint: 'towns and landmarks, with how many item spots are inside'
},
{
id: 'bridges',
label: 'Bridges',
group: 'Places',
color: '#94a3b8',
on: false,
icon: 'bridge',
hint: 'bridge points from the game files'
},
{
id: 'cars',
label: 'Car spots',
group: 'Vehicles',
color: '#c084fc',
on: true,
icon: 'car',
hint: 'road and off-road spots; the chance is per spot per match'
},
{
id: 'garages',
label: 'Garages',
group: 'Vehicles',
color: '#a855f7',
on: true,
icon: 'garage',
hint: 'garage and police-station spots (high chance)'
},
{
id: 'boats',
label: 'Boats',
group: 'Vehicles',
color: '#38bdf8',
on: true,
icon: 'boat',
hint: 'boat, Aquarail and Airboat spots'
},
{
id: 'gliders',
label: 'Motor gliders',
group: 'Vehicles',
color: '#f0f9ff',
on: true,
icon: 'glider',
hint: 'motor glider spots'
},
{
id: 'special',
label: 'Special vehicles',
group: 'Vehicles',
color: '#facc15',
on: true,
icon: 'crown',
hint: 'fixed rare spawns such as the Gold Mirado'
},
{
id: 'drops',
label: 'Hot and quiet drops',
group: 'Drops',
color: '#f97316',
on: false,
icon: 'flame',
hint: 'from drop guides: red hot, amber balanced, green quiet'
},
{
id: 'loot',
label: 'Loot density',
group: 'Loot',
color: '#4ade80',
on: false,
icon: 'loot',
hint: 'the squares with the most item spots (game files, aggregated)'
},
{
id: 'towers',
label: 'Blue Chip towers',
group: 'Loot',
color: '#60a5fa',
on: false,
icon: 'tower',
hint: 'Blue Chip transmitter spots'
},
{
id: 'vending',
label: 'Vending machines',
group: 'Loot',
color: '#f472b6',
on: false,
icon: 'vending',
hint: 'vending machine spots'
},
{
id: 'bears',
label: 'Bear caves',
group: 'Loot',
color: '#f8fafc',
on: false,
icon: 'bear',
hint: 'polar bear spawns (bear caves, airdrop-tier loot)'
},
{
id: 'secret',
label: 'Secret rooms',
group: 'Secrets',
color: '#fb923c',
on: false,
icon: 'lock',
hint: 'approximate: drawn around the named place'
},
{
id: 'keys',
label: 'Security keys',
group: 'Secrets',
color: '#fde047',
on: false,
icon: 'key',
hint: 'approximate: drawn around the named place'
}
];
export const INTEL_BY_ID = Object.fromEntries(INTEL_LAYERS.map((l) => [l.id, l])) as Record<
IntelLayerId,
IntelLayer
>;
export const VEHICLE_ICON: Record<string, keyof typeof P> = {
car: 'car',
garage: 'garage',
boat: 'boat',
glider: 'glider',
special: 'crown'
};
export const DROP_COLOR: Record<string, string> = {
hot: '#ef4444',
busy: '#f97316',
balanced: '#facc15',
quiet: '#4ade80'
};
export const DROP_LABEL: Record<string, string> = {
hot: 'Hot drop',
busy: 'Busy drop',
balanced: 'Balanced drop',
quiet: 'Quiet drop'
};
/** The player's on/off choices merged over the defaults. */
export function readLayerPrefs(raw: string | null): Record<IntelLayerId, boolean> {
const out = Object.fromEntries(INTEL_LAYERS.map((l) => [l.id, l.on])) as Record<
IntelLayerId,
boolean
>;
try {
const saved = JSON.parse(raw ?? 'null');
if (saved && typeof saved === 'object')
for (const l of INTEL_LAYERS) if (typeof saved[l.id] === 'boolean') out[l.id] = saved[l.id];
} catch {
// keep the defaults
}
return out;
}
/** Maps with a static/pubg-intel file (Camp Jackal, the training map, has none). */
export const INTEL_MAPS = new Set([
'erangel',
'miramar',
'sanhok',
'vikendi',
'taego',
'deston',
'rondo',
'karakin',
'paramo',
'haven'
]);
const docs = new Map<string, Promise<IntelDoc | null>>();
/** Fetches a map's intel once per page load; null when the map has none. */
export function loadIntel(slug: string): Promise<IntelDoc | null> {
if (!INTEL_MAPS.has(slug)) return Promise.resolve(null);
let p = docs.get(slug);
if (!p) {
p = fetch(`/pubg-intel/${slug}.v${INTEL_VERSION}.json`)
.then((r) => (r.ok ? (r.json() as Promise<IntelDoc>) : null))
.catch(() => null);
p.then((d) => d || docs.delete(slug));
docs.set(slug, p);
}
return p;
}
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"deston","built":"2026-10-25","sources":{"gamepur":{"name":"Gamepur, Deston secret rooms","url":"https://www.gamepur.com/guides/all-deston-secret-room-locations-in-pubg"},"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/deston/","note":"positions read from the game level files"},"loadout":{"name":"The Loadout, Deston security keys","url":"https://www.theloadout.com/pubg/security-key-location-deston"},"psr":{"name":"pubgsecretroom.com, best drop locations","url":"https://pubgsecretroom.com/pubg-best-drop-locations-guide.html"}},"notes":[{"title":"Security rooms","text":"Rooms open with Pillar security keys, which sit in drone-only spots near the listed places (not in El Koro, Lodge or Arena). Positions are the named place, not the door.","s":"loadout"}],"layers":{"towns":[{"x":0.26455,"y":0.20565,"n":"Los Arcos","r":0.06127,"d":"2795 item spots within 500 m","w":2795,"s":"gs"},{"x":0.72656,"y":0.13457,"n":"Carpenter's End","r":0.03064,"d":"19 item spots within 250 m","w":19,"s":"gs"},{"x":0.38832,"y":0.46668,"n":"Turrita","r":0.04902,"d":"535 item spots within 400 m","w":535,"s":"gs"},{"x":0.57251,"y":0.34046,"n":"Buxley","r":0.05515,"d":"2258 item spots within 450 m","w":2258,"s":"gs"},{"x":0.19102,"y":0.77796,"n":"El Koro","r":0.04289,"d":"911 item spots within 350 m","w":911,"s":"gs"},{"x":0.4515,"y":0.70976,"n":"Sancarna","r":0.04289,"d":"1835 item spots within 350 m","w":1835,"s":"gs"},{"x":0.68336,"y":0.78776,"n":"Holston Meadows","r":0.04596,"d":"1473 item spots within 375 m","w":1473,"s":"gs"},{"x":0.33685,"y":0.06105,"n":"Ten Forts","r":0.03064,"d":"954 item spots within 250 m","w":954,"s":"gs"},{"x":0.19715,"y":0.57575,"n":"Cavala","r":0.06127,"d":"2502 item spots within 500 m","w":2502,"s":"gs"},{"x":0.56756,"y":0.50062,"n":"Arena","r":0.03064,"d":"346 item spots within 250 m","w":346,"s":"gs"},{"x":0.80867,"y":0.23304,"n":"Assembly","r":0.03676,"d":"724 item spots within 300 m","w":724,"s":"gs"},{"x":0.63845,"y":0.23813,"n":"Concert","r":0.03064,"d":"408 item spots within 250 m","w":408,"s":"gs"},{"x":0.28158,"y":0.58807,"n":"Hydroelectric Dam","r":0.01225,"d":"166 item spots within 100 m","w":166,"s":"gs"},{"x":0.74862,"y":0.52796,"n":"Ripton","r":0.12255,"d":"7472 item spots within 1000 m","w":7472,"s":"gs"},{"x":0.37736,"y":0.34101,"n":"Construction Site","r":0.02451,"d":"351 item spots within 200 m","w":351,"s":"gs"},{"x":0.46185,"y":0.55247,"n":"Lodge","r":0.03064,"d":"304 item spots within 250 m","w":304,"s":"gs"},{"x":0.46063,"y":0.10271,"n":"Swamp","r":0.09804,"d":"1589 item spots within 800 m","w":1589,"s":"gs"},{"x":0.20573,"y":0.41582,"n":"Barclift","r":0.02451,"d":"298 item spots within 200 m","w":298,"s":"gs"},{"x":0.91369,"y":0.40859,"n":"Coastal Towns","r":0.07966,"d":"1180 item spots within 650 m","w":1180,"s":"gs"},{"x":0.92392,"y":0.25455,"n":"Wind Farms","r":0.07353,"d":"760 item spots within 600 m","w":760,"s":"gs"}],"bridges":[{"x":0.71077,"y":0.53601,"n":"Bridge","s":"gs"},{"x":0.73655,"y":0.48584,"n":"Bridge","s":"gs"},{"x":0.63205,"y":0.42316,"n":"Bridge","s":"gs"},{"x":0.66211,"y":0.43364,"n":"Bridge","s":"gs"},{"x":0.70605,"y":0.4202,"n":"Bridge","s":"gs"},{"x":0.57282,"y":0.43404,"n":"Bridge","s":"gs"},{"x":0.49424,"y":0.44254,"n":"Bridge","s":"gs"},{"x":0.49679,"y":0.44006,"n":"Bridge","s":"gs"},{"x":0.39071,"y":0.5027,"n":"Bridge","s":"gs"},{"x":0.28863,"y":0.59898,"n":"Bridge","s":"gs"},{"x":0.29772,"y":0.58746,"n":"Bridge","s":"gs"},{"x":0.44246,"y":0.09613,"n":"Bridge","s":"gs"},{"x":0.59447,"y":0.18205,"n":"Bridge","s":"gs"},{"x":0.64186,"y":0.12042,"n":"Bridge","s":"gs"},{"x":0.49926,"y":0.73262,"n":"Bridge","s":"gs"},{"x":0.57185,"y":0.67404,"n":"Bridge","s":"gs"},{"x":0.67607,"y":0.55761,"n":"Bridge","s":"gs"},{"x":0.90165,"y":0.54439,"n":"Bridge","s":"gs"},{"x":0.9108,"y":0.42477,"n":"Bridge","s":"gs"},{"x":0.89692,"y":0.28722,"n":"Bridge","s":"gs"},{"x":0.68774,"y":0.57367,"n":"Bridge","s":"gs"},{"x":0.71069,"y":0.53751,"n":"Bridge","s":"gs"},{"x":0.24596,"y":0.78616,"n":"Bridge","s":"gs"},{"x":0.27603,"y":0.57735,"n":"Bridge","s":"gs"},{"x":0.49502,"y":0.44075,"n":"Bridge","s":"gs"},{"x":0.62649,"y":0.42477,"n":"Bridge","s":"gs"},{"x":0.57235,"y":0.4349,"n":"Bridge","s":"gs"},{"x":0.92039,"y":0.52671,"n":"Bridge","s":"gs"},{"x":0.86303,"y":0.27688,"n":"Bridge","s":"gs"},{"x":0.78448,"y":0.6069,"n":"Bridge","s":"gs"},{"x":0.69958,"y":0.46684,"n":"Bridge","s":"gs"},{"x":0.57163,"y":0.67421,"n":"Bridge","s":"gs"},{"x":0.6754,"y":0.55679,"n":"Bridge","s":"gs"},{"x":0.49864,"y":0.73216,"n":"Bridge","s":"gs"},{"x":0.24567,"y":0.78555,"n":"Bridge","s":"gs"},{"x":0.89881,"y":0.3972,"n":"Bridge","s":"gs"}],"cars":[{"x":0.72343,"y":0.75529,"n":"Road spot","t":"car","d":"78 of 112 spots spawn a vehicle each match (~70%); pool: Coupe RB, Pillar Car, Dacia, Pickup, Motorbike, Food Truck","p":0.7,"s":"gs"},{"x":0.8461,"y":0.7725,"n":"Road spot","t":"car","d":"78 of 112 spots spawn a vehicle each match (~70%); pool: Coupe RB, Pillar Car, Dacia, Pickup, MotoLine truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"erangel","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/erangel/","note":"positions read from the game level files"},"pcg":{"name":"PCGamesN, PUBG map list","url":"https://www.pcgamesn.com/playerunknowns-battlegrounds/pubg-map"},"pn141":{"name":"PUBG Wiki (Fandom), Erangel: Update 14.1 church basement","url":"https://pubg.fandom.com/wiki/Erangel_(Remastered)"},"psr":{"name":"pubgsecretroom.com, best drop locations","url":"https://pubgsecretroom.com/pubg-best-drop-locations-guide.html"},"psrooms":{"name":"pubgsecretrooms.com (exact secret room spots)","url":"https://pubgsecretrooms.com/erangel-secret-room-locations/"}},"notes":[{"title":"Secret rooms","text":"Keyed secret rooms with underground entrances (keys are random world loot). Exact spots are not republished here; see the source map.","s":"psrooms"}],"layers":{"towns":[{"x":0.14714,"y":0.15456,"n":"Zharki","r":0.03676,"d":"402 item spots within 300 m","w":402,"s":"gs"},{"x":0.76501,"y":0.36685,"n":"Mansion","r":0.02451,"d":"147 item spots within 200 m","w":147,"s":"gs"},{"x":0.69774,"y":0.47305,"n":"Shelter","r":0.02451,"d":"131 item spots within 200 m","w":131,"s":"gs"},{"x":0.76925,"y":0.45941,"n":"Prison","r":0.02451,"d":"178 item spots within 200 m","w":178,"s":"gs"},{"x":0.88061,"y":0.53832,"n":"Mylta Power","r":0.05515,"d":"342 item spots within 450 m","w":342,"s":"gs"},{"x":0.73264,"y":0.58913,"n":"Mylta","r":0.03676,"d":"497 item spots within 300 m","w":497,"s":"gs"},{"x":0.66705,"y":0.56175,"n":"Farm","r":0.02451,"d":"82 item spots within 200 m","w":82,"s":"gs"},{"x":0.47954,"y":0.3595,"n":"Rozhok","r":0.03676,"d":"324 item spots within 300 m","w":324,"s":"gs"},{"x":0.52308,"y":0.40409,"n":"School","r":0.03676,"d":"483 item spots within 300 m","w":483,"s":"gs"},{"x":0.21615,"y":0.31403,"n":"Georgopol","r":0.08578,"d":"3850 item spots within 700 m","w":3850,"s":"gs"},{"x":0.21931,"y":0.37921,"n":"Hospital","r":0.02451,"d":"78 item spots within 200 m","w":78,"s":"gs"},{"x":0.45952,"y":0.15717,"n":"Severny","r":0.03676,"d":"387 item spots within 300 m","w":387,"s":"gs"},{"x":0.26829,"y":0.47954,"n":"Gatka","r":0.02451,"d":"92 item spots within 200 m","w":92,"s":"gs"},{"x":0.20904,"y":0.63992,"n":"Quarry","r":0.03676,"d":"329 item spots within 300 m","w":329,"s":"gs"},{"x":0.19956,"y":0.7525,"n":"Primorsk","r":0.03676,"d":"566 item spots within 300 m","w":566,"s":"gs"},{"x":0.35322,"y":0.68851,"n":"Ferry Pier","r":0.03676,"d":"273 item spots within 300 m","w":273,"s":"gs"},{"x":0.55073,"y":0.79556,"n":"Military Base","r":0.07353,"d":"537 item spots within 600 m","w":537,"s":"gs"},{"x":0.76563,"y":0.74737,"n":"Novorepnoye","r":0.03676,"d":"1100 item spots within 300 m","w":1100,"s":"gs"},{"x":0.37474,"y":0.39662,"n":"Ruins","r":0.03676,"d":"186 item spots within 300 m","w":186,"s":"gs"},{"x":0.44201,"y":0.4955,"n":"Pochinki","r":0.04902,"d":"858 item spots within 400 m","w":858,"s":"gs"},{"x":0.4283,"y":0.38663,"n":"Boatyard","r":0.01961,"d":"264 item spots within 160 m","w":264,"s":"gs"},{"x":0.40999,"y":0.19453,"n":"Shooting Range","r":0.03676,"d":"183 item spots within 300 m","w":183,"s":"gs"},{"x":0.81706,"y":0.13737,"n":"Kameshki","r":0.03676,"d":"228 item spots within 300 m","w":228,"s":"gs"},{"x":0.70263,"y":0.15655,"n":"Stalber","r":0.04902,"d":"278 item spots within 400 m","w":278,"s":"gs"},{"x":0.66401,"y":0.2986,"n":"Yasnaya Polyana","r":0.04902,"d":"1484 item spots within 400 m","w":1484,"s":"gs"},{"x":0.86349,"y":0.40309,"n":"Lipovka","r":0.03676,"d":"292 item spots within 300 m","w":292,"s":"gs"}],"bridges":[{"x":0.67643,"y":0.6521,"n":"Bridge","s":"gs"},{"x":0.54562,"y":0.33,"n":"Bridge","s":"gs"},{"x":0.49817,"y":0.3213,"n":"Bridge","s":"gs"},{"x":0.4195,"y":0.36028,"n":"Bridge","s":"gs"},{"x":0.3894,"y":0.3416,"n":"Bridge","s":"gs"},{"x":0.33933,"y":0.32324,"n":"Bridge","s":"gs"},{"x":0.28901,"y":0.31707,"n":"Bridge","s":"gs"},{"x":0.19933,"y":0.27657,"n":"Bridge","s":"gs"},{"x":0.41184,"y":0.67159,"n":"Bridge","s":"gs"},{"x":0.56856,"y":0.2919,"n":"Bridge","s":"gs"}],"cars":[{"x":0.86881,"y":0.4232,"n":"Road spot","t":"car","d":"143 of 416 spots spawn a vehicle each match (~34%); pool: Uaz, Coupe RB, Buggy, Motorbike, Dacia, Dirt Bike","p":0.34,"s":"gs"},{"x":0.44079,"y":0.37271,"n":"Road spot","t":"car","d":"143 of 416 spots spawn a vehicle each match (~34%); pool: Uaz, Coupe RB, Buggy, Motorbike, Dacia, Dirt Bike","p":0.34,"s":"gs"},{"x":0.2244,"y":0.70308,"n":"Road spot","t":"car","d":"143 of 416 spots spawn a vehicle each match (~34%); pool: Uaz, Coupe RB, Buggy, Motorbike, Dacia, Dirt Bike","p":0.34,"s":"gs"},{"x":0.43536,"y":0.59346,"n":"Road spot","t":"car","d":"143 of 416 spots spawn a vehicle each match (~34%); pool: Uaz, Coupe RB, Buggy, Motorbike, Dacia, Dirt Bike","p":0.34,"s":"gs"},{"x":0.24762,"y":0.68923,"n":"Road spot","t":"car","d":"143 of 416 spots spawn a vehicle each match (~34%); pool: Uaz, Coupe RB, Buggy, Motorbike, Dacia, Dirt Bike","p":0.34,"s":"gs"},{"x"Line truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"haven","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/haven/","note":"positions read from the game level files"}},"notes":[],"layers":{"towns":[{"x":0.78078,"y":0.46118,"n":"Coal Yards","r":0.09804,"d":"68 item spots within 100 m","w":68,"s":"gs"},{"x":0.17461,"y":0.64304,"n":"Docks","r":0.09804,"d":"172 item spots within 100 m","w":172,"s":"gs"},{"x":0.31618,"y":0.16647,"n":"Industrial Park","r":0.19608,"d":"815 item spots within 200 m","w":815,"s":"gs"},{"x":0.32137,"y":0.42069,"n":"Rail Yard","r":0.09804,"d":"153 item spots within 100 m","w":153,"s":"gs"},{"x":0.6502,"y":0.80637,"n":"Residential","r":0.19608,"d":"3405 item spots within 200 m","w":3405,"s":"gs"},{"x":0.41382,"y":0.55804,"n":"Steel Mill","r":0.10784,"d":"265 item spots within 110 m","w":265,"s":"gs"}],"vending":[{"x":0.26047,"y":0.79107,"n":"Vending machine","s":"gs"},{"x":0.13654,"y":0.57338,"n":"Vending machine","s":"gs"},{"x":0.11726,"y":0.3686,"n":"Vending machine","s":"gs"},{"x":0.20017,"y":0.08545,"n":"Vending machine","s":"gs"},{"x":0.12396,"y":0.31752,"n":"Vending machine","s":"gs"},{"x":0.19537,"y":0.25512,"n":"Vending machine","s":"gs"},{"x":0.58327,"y":0.69538,"n":"Vending machine","s":"gs"},{"x":0.54638,"y":0.7088,"n":"Vending machine","s":"gs"},{"x":0.59464,"y":0.72995,"n":"Vending machine","s":"gs"},{"x":0.62364,"y":0.70636,"n":"Vending machine","s":"gs"},{"x":0.27932,"y":0.16221,"n":"Vending machine","s":"gs"},{"x":0.3146,"y":0.30975,"n":"Vending machine","s":"gs"},{"x":0.22901,"y":0.58969,"n":"Vending machine","s":"gs"},{"x":0.35425,"y":0.68822,"n":"Vending machine","s":"gs"},{"x":0.4989,"y":0.57154,"n":"Vending machine","s":"gs"},{"x":0.25691,"y":0.81252,"n":"Vending machine","s":"gs"},{"x":0.47615,"y":0.85119,"n":"Vending machine","s":"gs"},{"x":0.51154,"y":0.7871,"n":"Vending machine","s":"gs"},{"x":0.5421,"y":0.75803,"n":"Vending machine","s":"gs"}],"loot":[{"x":0.63725,"y":0.73529,"r":0.04902,"w":1.0,"n":"537 item spots in this 100 m square","s":"gs"},{"x":0.73529,"y":0.73529,"r":0.04902,"w":0.91,"n":"491 item spots in this 100 m square","s":"gs"},{"x":0.53922,"y":0.73529,"r":0.04902,"w":0.66,"n":"355 item spots in this 100 m square","s":"gs"},{"x":0.44118,"y":0.83333,"r":0.04902,"w":0.65,"n":"348 item spots in this 100 m square","s":"gs"},{"x":0.53922,"y":0.83333,"r":0.04902,"w":0.49,"n":"264 item spots in this 100 m square","s":"gs"},{"x":0.63725,"y":0.83333,"r":0.04902,"w":0.43,"n":"233 item spots in this 100 m square","s":"gs"},{"x":0.73529,"y":0.83333,"r":0.04902,"w":0.42,"n":"227 item spots in this 100 m square","s":"gs"},{"x":0.14706,"y":0.2451,"r":0.04902,"w":0.35,"n":"190 item spots in this 100 m square","s":"gs"},{"x":0.53922,"y":0.93137,"r":0.04902,"w":0.34,"n":"183 item spots in this 100 m square","s":"gs"},{"x":0.83333,"y":0.73529,"r":0.04902,"w":0.3,"n":"159 item spots in this 100 m square","s":"gs"}]}}
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"karakin","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/karakin/","note":"positions read from the game level files"}},"notes":[],"layers":{"towns":[{"x":0.72493,"y":0.21809,"n":"Al Habar","r":0.08333,"d":"1052 item spots within 170 m","w":1052,"s":"gs"},{"x":0.4824,"y":0.82735,"n":"Al Hayik","r":0.04902,"d":"214 item spots within 100 m","w":214,"s":"gs"},{"x":0.3226,"y":0.25147,"n":"Bahr Sahir","r":0.07353,"d":"466 item spots within 150 m","w":466,"s":"gs"},{"x":0.19172,"y":0.58152,"n":"Bashara","r":0.07353,"d":"546 item spots within 150 m","w":546,"s":"gs"},{"x":0.72669,"y":0.61346,"n":"Hadiqa Nemo","r":0.05882,"d":"488 item spots within 120 m","w":488,"s":"gs"}],"towers":[{"x":0.54683,"y":0.70376,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"},{"x":0.34238,"y":0.72275,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"},{"x":0.27892,"y":0.48781,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"},{"x":0.463,"y":0.36379,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"},{"x":0.65896,"y":0.32989,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"},{"x":0.64384,"y":0.53326,"n":"Blue Chip tower","d":"Blue Chip Transmitter spot","s":"gs"}],"loot":[{"x":0.71078,"y":0.61275,"r":0.02451,"w":1.0,"n":"395 item spots in this 100 m square","s":"gs"},{"x":0.17157,"y":0.56373,"r":0.02451,"w":0.89,"n":"352 item spots in this 100 m square","s":"gs"},{"x":0.7598,"y":0.22059,"r":0.02451,"w":0.66,"n":"260 item spots in this 100 m square","s":"gs"},{"x":0.7598,"y":0.17157,"r":0.02451,"w":0.57,"n":"227 item spots in this 100 m square","s":"gs"},{"x":0.71078,"y":0.22059,"r":0.02451,"w":0.55,"n":"217 item spots in this 100 m square","s":"gs"},{"x":0.71078,"y":0.26961,"r":0.02451,"w":0.38,"n":"150 item spots in this 100 m square","s":"gs"},{"x":0.71078,"y":0.17157,"r":0.02451,"w":0.36,"n":"142 item spots in this 100 m square","s":"gs"},{"x":0.26961,"y":0.26961,"r":0.02451,"w":0.33,"n":"132 item spots in this 100 m square","s":"gs"},{"x":0.31863,"y":0.22059,"r":0.02451,"w":0.33,"n":"131 item spots in this 100 m square","s":"gs"},{"x":0.56373,"y":0.46569,"r":0.02451,"w":0.28,"n":"110 item spots in this 100 m square","s":"gs"},{"x":0.56373,"y":0.71078,"r":0.02451,"w":0.26,"n":"102 item spots in this 100 m square","s":"gs"},{"x":0.31863,"y":0.26961,"r":0.02451,"w":0.26,"n":"101 item spots in this 100 m square","s":"gs"},{"x":0.22059,"y":0.61275,"r":0.02451,"w":0.24,"n":"93 item spots in this 100 m square","s":"gs"},{"x":0.46569,"y":0.80882,"r":0.02451,"w":0.23,"n":"91 item spots in this 100 m square","s":"gs"}]}}
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"miramar","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/miramar/","note":"positions read from the game level files"},"ib":{"name":"ImmortalBoost, PUBG secret rooms","url":"https://immortalboost.com/blog/pubg/secret-locations/"},"pcg":{"name":"PCGamesN, PUBG map list","url":"https://www.pcgamesn.com/playerunknowns-battlegrounds/pubg-map"},"pn412":{"name":"PUBG patch notes, Update 41.2 (Miramar secret rooms)","url":"https://pubg.com/en/news/10080"}},"notes":[{"title":"Secret rooms","text":"Abandoned-mine secret rooms opened with world-spawned Secret Room Keys (Update 41.2, Normal and Custom matches). Five are placed here near their landmark; more exist, see the source map for all of them.","s":"pn412"}],"layers":{"towns":[{"x":0.20159,"y":0.35143,"n":"El Pozo","r":0.06127,"d":"2251 item spots within 500 m","w":2251,"s":"gs"},{"x":0.46238,"y":0.35184,"n":"San Martin","r":0.04289,"d":"1281 item spots within 350 m","w":1281,"s":"gs"},{"x":0.45356,"y":0.53006,"n":"Pecado","r":0.02696,"d":"763 item spots within 220 m","w":763,"s":"gs"},{"x":0.32575,"y":0.65501,"n":"Chumacera","r":0.03186,"d":"608 item spots within 260 m","w":608,"s":"gs"},{"x":0.4231,"y":0.92025,"n":"Partona","r":0.03186,"d":"660 item spots within 260 m","w":660,"s":"gs"},{"x":0.71533,"y":0.07202,"n":"Campo Militar","r":0.05147,"d":"804 item spots within 420 m","w":804,"s":"gs"},{"x":0.67727,"y":0.18594,"n":"Cruz del Valle","r":0.03922,"d":"637 item spots within 320 m","w":637,"s":"gs"},{"x":0.52794,"y":0.22913,"n":"Water Treatment","r":0.02819,"d":"375 item spots within 230 m","w":375,"s":"gs"},{"x":0.53007,"y":0.34341,"n":"Hacienda del Patron","r":0.01838,"d":"199 item spots within 150 m","w":199,"s":"gs"},{"x":0.55176,"y":0.44793,"n":"Graveyard","r":0.01838,"d":"141 item spots within 150 m","w":141,"s":"gs"},{"x":0.61782,"y":0.44188,"n":"Minas Generales","r":0.04657,"d":"819 item spots within 380 m","w":819,"s":"gs"},{"x":0.2286,"y":0.50056,"n":"Monte Nuevo","r":0.03676,"d":"834 item spots within 300 m","w":834,"s":"gs"},{"x":0.74613,"y":0.39438,"n":"Truck Stop","r":0.02451,"d":"313 item spots within 200 m","w":313,"s":"gs"},{"x":0.56747,"y":0.68344,"n":"Los Leones","r":0.07353,"d":"3873 item spots within 600 m","w":3873,"s":"gs"},{"x":0.62517,"y":0.52195,"n":"Cantera","r":0.01961,"d":"133 item spots within 160 m","w":133,"s":"gs"},{"x":0.56033,"y":0.82445,"n":"Resort","r":0.01961,"d":"232 item spots within 160 m","w":232,"s":"gs"},{"x":0.80032,"y":0.15466,"n":"Tierra Bronca","r":0.04289,"d":"586 item spots within 350 m","w":586,"s":"gs"},{"x":0.78674,"y":0.3151,"n":"El Azahar","r":0.04289,"d":"816 item spots within 350 m","w":816,"s":"gs"},{"x":0.77981,"y":0.57061,"n":"Impala","r":0.03064,"d":"581 item spots within 250 m","w":581,"s":"gs"},{"x":0.76903,"y":0.77083,"n":"Puerto Paraiso","r":0.03064,"d":"635 item spots within 250 m","w":635,"s":"gs"},{"x":0.44548,"y":0.07297,"n":"Oasis","r":0.03676,"d":"560 item spots within 300 m","w":560,"s":"gs"},{"x":0.10223,"y":0.26525,"n":"Alcantara","r":0.03064,"d":"823 item spots within 250 m","w":823,"s":"gs"},{"x":0.17899,"y":0.7269,"n":"Valle del Mar","r":0.06127,"d":"853 item spots within 500 m","w":853,"s":"gs"},{"x":0.23382,"y":0.56626,"n":"Brick Yard","r":0.01838,"d":"113 item spots within 150 m","w":113,"s":"gs"},{"x":0.14965,"y":0.86066,"n":"Prison","r":0.02451,"d":"460 item spots within 200 m","w":460,"s":"gs"},{"x":0.31355,"y":0.1587,"n":"La Cobreria","r":0.04902,"d":"808 item spots within 400 m","w":808,"s":"gs"},{"x":0.25714,"y":0.24883,"n":"Crater Fields","r":0.02451,"d":"148 item spots within 200 m","w":148,"s":"gs"},{"x":0.39145,"y":0.43278,"n":"Power Grid","r":0.02451,"d":"185 item spots within 200 m","w":185,"s":"gs"}],"bridges":[{"x":0.31358,"y":0.85389,"n":"Bridge","s":"gs"},{"x":0.44583,"y":0.83146,"n":"Bridge","s":"gs"},{"x":0.79412,"y":0.70157,"n":"Bridge","s":"gs"},{"x":0.8455,"y":0.69115,"n":"Bridge","s":"gs"},{"x":0.84052,"y":0.55834,"n":"Bridge","s":"gs"},{"x":0.88242,"y":0.60885,"n":"Bridge","s":"gs"},{"x":0.16525,"y":0.72069,"n":"Bridge","s":"gs"}],"cars":[{"x":0.05773,"y":0.1911,"n":"Road spot","t":"car","d":"186 of 328 spots spawn a vehicle each match (~57%); pool: Pickup, Mirado, MiniBus, Coupe RB, Motorbike, Buggy, ATV","p":0.57,"s":"gs"},{"x":0.62043,"y":0.07515,"n":"Road spot","t":"car","d":"186 of 328 spots spawn a vehicle each match (~57%); pool: Pickup, Mirado, MiniBus, Coupe RB, Motorbike, Buggy, ATV","p":0.57,"s":"gs"},{"x":0.73008,"y":0.11762,"n":"Road spot","t":"car","d":"186 of 328 spots spawn a vehicle each match (~57%); pool: Pickup, Mirado, MiniBus, Coupe RB, Motorbike, Buggy, ATV","p":0.57,"s":"gs"},{"x":0.76712,"y":0.08783,"n":"Road spot","t":"car","d":"186 of 328 spots spawn a vehicle each match (~57%); pool: Pickup, Mirado, MiniBus, Coupe RB, Motorbike, Buggy, ATV","p":0.57,"s":"gs"},{"x":0.81384,"y":0.11817,"n":"Road spot","t":"car","d":"186 of 328 spots spawn a vehicle each match (~57%); pool: Pickup, MiLine truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"paramo","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/paramo/","note":"positions read from the game level files"},"psrooms":{"name":"pubgsecretrooms.com (exact secret room spots)","url":"https://pubgsecretrooms.com/paramo-secret-room-locations/"}},"notes":[{"title":"Dynamic map","text":"Paramo's cities move between the four city slots each match; the slot points below are where a city can appear.","s":"gs"},{"title":"Secret rooms","text":"Stone-door secret rooms. Exact spots: see the source map.","s":"psrooms"}],"layers":{"towns":[{"x":0.25654,"y":0.44118,"n":"City slot A","r":0.06536,"d":"670 item spots within 200 m","w":670,"s":"gs"},{"x":0.29902,"y":0.79575,"n":"City slot B","r":0.06536,"d":"685 item spots within 200 m","w":685,"s":"gs"},{"x":0.72059,"y":0.40686,"n":"City slot D","r":0.0719,"d":"735 item spots within 220 m","w":735,"s":"gs"},{"x":0.58497,"y":0.34804,"n":"Lab","r":0.03268,"d":"83 item spots within 100 m","w":83,"s":"gs"},{"x":0.52614,"y":0.53922,"n":"City slot C","r":0.05229,"d":"355 item spots within 160 m","w":355,"s":"gs"}],"cars":[{"x":0.37745,"y":0.85147,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.33578,"y":0.59118,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.35833,"y":0.58546,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.41193,"y":0.4482,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.4268,"y":0.44706,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.41846,"y":0.53693,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.42042,"y":0.4915,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.47876,"y":0.50049,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.45588,"y":0.50752,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.35715,"y":0.38908,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.36497,"y":0.39785,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.32271,"y":0.37206,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.33382,"y":0.36209,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.31961,"y":0.34199,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.39902,"y":0.33562,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.39706,"y":0.35065,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.41814,"y":0.40147,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.4018,"y":0.38987,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.44951,"y":0.35016,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.43987,"y":0.3366,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.47941,"y":0.33742,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.44183,"y":0.27288,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.46127,"y":0.28023,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.50098,"y":0.25768,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool: Dirt Bike, Motorbike","p":0.17,"s":"gs"},{"x":0.49559,"y":0.28137,"n":"Road spot","t":"car","d":"10-20 of 117 spots spawn a vehicle each match (~17%); pool:Line truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"rondo","built":"2026-10-25","sources":{"epic":{"name":"Epic Games Store, guide to Rondo","url":"https://store.epicgames.com/en-US/news/a-guide-to-pubg-battlegrounds-massive-new-map-rondo"},"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/rondo/","note":"positions read from the game level files"},"isp":{"name":"InsideSport, Rondo hot spots","url":"https://www.insidesport.in/gaming/pubg-mobile-rondo-hot-spots-check-out-the-top-landing-locations/"},"psrooms":{"name":"pubgsecretrooms.com (exact secret room spots)","url":"https://pubgsecretrooms.com/rondo-secret-room-locations/"}},"notes":[{"title":"Secret rooms","text":"Security door and keycard rooms built into compound clusters; keys are random floor loot. Exact spots: see the source map.","s":"psrooms"}],"layers":{"towns":[{"x":0.20333,"y":0.41328,"n":"Jao Tin","r":0.04534,"d":"1703 item spots within 370 m","w":1703,"s":"gs"},{"x":0.65584,"y":0.12032,"n":"Kun Xia","r":0.02328,"d":"304 item spots within 190 m","w":304,"s":"gs"},{"x":0.16995,"y":0.71573,"n":"Lan Po","r":0.01593,"d":"173 item spots within 130 m","w":173,"s":"gs"},{"x":0.14067,"y":0.11978,"n":"Min Hu","r":0.01593,"d":"116 item spots within 130 m","w":116,"s":"gs"},{"x":0.13769,"y":0.21897,"n":"Bei Li","r":0.02451,"d":"445 item spots within 200 m","w":445,"s":"gs"},{"x":0.47563,"y":0.10821,"n":"Rai An","r":0.01838,"d":"465 item spots within 150 m","w":465,"s":"gs"},{"x":0.12205,"y":0.61855,"n":"Fong Tun","r":0.02941,"d":"566 item spots within 240 m","w":566,"s":"gs"},{"x":0.59941,"y":0.63597,"n":"Dan Ching","r":0.02696,"d":"515 item spots within 220 m","w":515,"s":"gs"},{"x":0.29569,"y":0.10821,"n":"Nan Chuan","r":0.03186,"d":"536 item spots within 260 m","w":536,"s":"gs"},{"x":0.17127,"y":0.86401,"n":"Long Ho","r":0.02696,"d":"445 item spots within 220 m","w":445,"s":"gs"},{"x":0.85823,"y":0.18623,"n":"Hemay Town","r":0.02451,"d":"73 item spots within 200 m","w":73,"s":"gs"},{"x":0.72507,"y":0.19306,"n":"Mu Ho Pan","r":0.02696,"d":"563 item spots within 220 m","w":563,"s":"gs"},{"x":0.45198,"y":0.78312,"n":"Hung Shan","r":0.02206,"d":"509 item spots within 180 m","w":509,"s":"gs"},{"x":0.68406,"y":0.69688,"n":"Tu Ling","r":0.02819,"d":"157 item spots within 230 m","w":157,"s":"gs"},{"x":0.59954,"y":0.45366,"n":"NEOX Factory","r":0.02451,"d":"570 item spots within 200 m","w":570,"s":"gs"},{"x":0.86171,"y":0.7345,"n":"Jadena City","r":0.06373,"d":"3078 item spots within 520 m","w":3078,"s":"gs"},{"x":0.30827,"y":0.84483,"n":"Rin Jiang","r":0.03431,"d":"160 item spots within 280 m","w":160,"s":"gs"},{"x":0.79991,"y":0.424,"n":"Mey Ran","r":0.02696,"d":"639 item spots within 220 m","w":639,"s":"gs"},{"x":0.59918,"y":0.8874,"n":"Tin Long Garden","r":0.04289,"d":"837 item spots within 350 m","w":837,"s":"gs"},{"x":0.71856,"y":0.3783,"n":"Lo Hua Xing","r":0.02451,"d":"374 item spots within 200 m","w":374,"s":"gs"},{"x":0.34835,"y":0.29666,"n":"Stadium","r":0.02696,"d":"702 item spots within 220 m","w":702,"s":"gs"},{"x":0.54833,"y":0.44028,"n":"Test Track","r":0.04044,"d":"665 item spots within 330 m","w":665,"s":"gs"},{"x":0.37151,"y":0.54965,"n":"Yu Lin","r":0.03309,"d":"509 item spots within 270 m","w":509,"s":"gs"},{"x":0.77908,"y":0.54918,"n":"Yun Gu","r":0.01225,"d":"102 item spots within 100 m","w":102,"s":"gs"}],"bridges":[{"x":0.72502,"y":0.18256,"n":"Bridge","s":"gs"},{"x":0.718,"y":0.18845,"n":"Bridge","s":"gs"},{"x":0.76036,"y":0.08716,"n":"Bridge","s":"gs"},{"x":0.84392,"y":0.03136,"n":"Bridge","s":"gs"},{"x":0.65581,"y":0.28665,"n":"Bridge","s":"gs"},{"x":0.75247,"y":0.67182,"n":"Bridge","s":"gs"},{"x":0.87565,"y":0.77781,"n":"Bridge","s":"gs"},{"x":0.32167,"y":0.70029,"n":"Bridge","s":"gs"},{"x":0.32925,"y":0.70581,"n":"Bridge","s":"gs"},{"x":0.31037,"y":0.72722,"n":"Bridge","s":"gs"},{"x":0.26372,"y":0.79219,"n":"Bridge","s":"gs"},{"x":0.32317,"y":0.9233,"n":"Bridge","s":"gs"},{"x":0.25639,"y":0.08681,"n":"Bridge","s":"gs"},{"x":0.36723,"y":0.2418,"n":"Bridge","s":"gs"},{"x":0.53721,"y":0.20058,"n":"Bridge","s":"gs"},{"x":0.85214,"y":0.76818,"n":"Bridge","s":"gs"}],"cars":[{"x":0.32771,"y":0.86263,"n":"Road spot","t":"car","d":"87 of 214 spots spawn a vehicle each match (~41%); pool: Coupe RB, Blanc, Motorbike, Buggy, Dacia","p":0.41,"s":"gs"},{"x":0.25644,"y":0.87045,"n":"Road spot","t":"car","d":"87 of 214 spots spawn a vehicle each match (~41%); pool: Coupe RB, Blanc, Motorbike, Buggy, Dacia","p":0.41,"s":"gs"},{"x":0.306,"y":0.93412,"n":"Road spot","t":"car","d":"87 of 214 spots spawn a vehicle each match (~41%); pool: Coupe RB, Blanc, Motorbike, Buggy, Dacia","p":0.41,"s":"gs"},{"x":0.6563,"y":0.08702,"n":"Road spot","t":"car","d":"87 of 214 spots spawn a vehicle each match (~41%); pool: Coupe RB, Blanc, Motorbike, Buggy, Dacia","p":0.41,"s":"gs"},{"x":0.70999,"y":0.27592,"n":"Road spot","t":"car","d":"87 of 214 spots spawn a vehicle each match (~41%); pool: Coupe RB, Blanc, Motorbike, Buggy, Dacia","p":0.41,"s":"gs"},{"x":0.64597,"y":0.29077,"n":"Road spot","t":"cLine truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"sanhok","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/sanhok/","note":"positions read from the game level files"},"pcg":{"name":"PCGamesN, PUBG map list","url":"https://www.pcgamesn.com/playerunknowns-battlegrounds/pubg-map"}},"notes":[],"layers":{"towns":[{"x":0.59551,"y":0.91203,"n":"Ban Tai","r":0.02451,"d":"121 item spots within 100 m","w":121,"s":"gs"},{"x":0.47721,"y":0.46603,"n":"Bootcamp","r":0.04902,"d":"645 item spots within 200 m","w":645,"s":"gs"},{"x":0.19853,"y":0.40784,"n":"Camp Alpha","r":0.02941,"d":"214 item spots within 120 m","w":214,"s":"gs"},{"x":0.84402,"y":0.38262,"n":"Camp Bravo","r":0.02941,"d":"298 item spots within 120 m","w":298,"s":"gs"},{"x":0.57495,"y":0.84495,"n":"Camp Charlie","r":0.02941,"d":"364 item spots within 120 m","w":364,"s":"gs"},{"x":0.65588,"y":0.75654,"n":"Cave","r":0.03431,"d":"122 item spots within 140 m","w":122,"s":"gs"},{"x":0.8023,"y":0.8562,"n":"Docks","r":0.03431,"d":"300 item spots within 140 m","w":300,"s":"gs"},{"x":0.31789,"y":0.28091,"n":"Ha Tinh","r":0.03676,"d":"433 item spots within 150 m","w":433,"s":"gs"},{"x":0.84586,"y":0.68983,"n":"Kampong","r":0.04902,"d":"459 item spots within 200 m","w":459,"s":"gs"},{"x":0.57502,"y":0.18199,"n":"Khao","r":0.02941,"d":"375 item spots within 120 m","w":375,"s":"gs"},{"x":0.86373,"y":0.54581,"n":"Lakawi","r":0.02941,"d":"197 item spots within 120 m","w":197,"s":"gs"},{"x":0.78505,"y":0.19031,"n":"Mongnai","r":0.03431,"d":"129 item spots within 140 m","w":129,"s":"gs"},{"x":0.26696,"y":0.80691,"n":"Na Kham","r":0.02941,"d":"124 item spots within 120 m","w":124,"s":"gs"},{"x":0.46069,"y":0.66735,"n":"Pai Nan","r":0.03186,"d":"379 item spots within 130 m","w":379,"s":"gs"},{"x":0.61529,"y":0.33875,"n":"Paradise Resort","r":0.03676,"d":"477 item spots within 150 m","w":477,"s":"gs"},{"x":0.66419,"y":0.62211,"n":"Quarry","r":0.06618,"d":"484 item spots within 270 m","w":484,"s":"gs"},{"x":0.29904,"y":0.63377,"n":"Ruins","r":0.03431,"d":"301 item spots within 140 m","w":301,"s":"gs"},{"x":0.36304,"y":0.87554,"n":"Sahmee","r":0.02941,"d":"593 item spots within 120 m","w":593,"s":"gs"},{"x":0.19076,"y":0.71007,"n":"Tambang","r":0.02451,"d":"95 item spots within 100 m","w":95,"s":"gs"},{"x":0.55272,"y":0.2388,"n":"Tat Mok","r":0.02941,"d":"127 item spots within 120 m","w":127,"s":"gs"}],"bridges":[{"x":0.38752,"y":0.54122,"n":"Bridge","s":"gs"},{"x":0.40587,"y":0.62634,"n":"Bridge","s":"gs"},{"x":0.51967,"y":0.72697,"n":"Bridge","s":"gs"},{"x":0.5723,"y":0.79858,"n":"Bridge","s":"gs"},{"x":0.45117,"y":0.65499,"n":"Bridge","s":"gs"},{"x":0.46342,"y":0.66998,"n":"Bridge","s":"gs"},{"x":0.38708,"y":0.43517,"n":"Bridge","s":"gs"},{"x":0.22378,"y":0.54805,"n":"Bridge","s":"gs"},{"x":0.30579,"y":0.5322,"n":"Bridge","s":"gs"},{"x":0.34778,"y":0.51757,"n":"Bridge","s":"gs"},{"x":0.37435,"y":0.30832,"n":"Bridge","s":"gs"},{"x":0.39676,"y":0.56443,"n":"Bridge","s":"gs"},{"x":0.48651,"y":0.6867,"n":"Bridge","s":"gs"}],"cars":[{"x":0.60357,"y":0.85222,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.90579,"y":0.42364,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.79981,"y":0.51745,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.78877,"y":0.57788,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.82682,"y":0.62116,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.77416,"y":0.6314,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.62756,"y":0.5949,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.85123,"y":0.53555,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.85446,"y":0.67534,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.81556,"y":0.72316,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.7666,"y":0.75862,"n":"Road spot","t":"car","d":"48 of 117 spots spawn a vehicle each match (~41%); pool: Rony, Coupe RB, Motorbike, Scooter, Tukshai","p":0.41,"s":"gs"},{"x":0.73235,"y":0.73451,"n":"Road spot","tLine truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"taego","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/taego/","note":"positions read from the game level files"},"psr":{"name":"pubgsecretroom.com, best drop locations","url":"https://pubgsecretroom.com/pubg-best-drop-locations-guide.html"},"psrooms":{"name":"pubgsecretrooms.com (exact secret room spots)","url":"https://pubgsecretrooms.com/taego-secret-room-locations/"}},"notes":[{"title":"Secret rooms","text":"Secret rooms behind blue corrugated roofs; keys spawn randomly (glowing pots can drop one). Exact spots: see the source map.","s":"psrooms"}],"layers":{"towns":[{"x":0.38638,"y":0.78486,"n":"School","r":0.02451,"d":"467 item spots within 200 m","w":467,"s":"gs"},{"x":0.46595,"y":0.12359,"n":"Army Base","r":0.03676,"d":"499 item spots within 300 m","w":499,"s":"gs"},{"x":0.54433,"y":0.29961,"n":"Yong Cheon","r":0.04289,"d":"943 item spots within 350 m","w":943,"s":"gs"},{"x":0.56961,"y":0.44007,"n":"Terminal","r":0.04289,"d":"1628 item spots within 350 m","w":1628,"s":"gs"},{"x":0.61784,"y":0.62819,"n":"Buk San Sa","r":0.03064,"d":"600 item spots within 250 m","w":600,"s":"gs"},{"x":0.48601,"y":0.83157,"n":"Song Am","r":0.03064,"d":"524 item spots within 250 m","w":524,"s":"gs"},{"x":0.7051,"y":0.17023,"n":"Shipyard","r":0.07353,"d":"1170 item spots within 600 m","w":1170,"s":"gs"},{"x":0.88911,"y":0.38369,"n":"Airport","r":0.04902,"d":"1057 item spots within 400 m","w":1057,"s":"gs"},{"x":0.79215,"y":0.54876,"n":"Kang Neung","r":0.03676,"d":"727 item spots within 300 m","w":727,"s":"gs"},{"x":0.70271,"y":0.73115,"n":"Oh Hyang","r":0.03064,"d":"612 item spots within 250 m","w":612,"s":"gs"},{"x":0.19321,"y":0.1973,"n":"Wol Song","r":0.03064,"d":"604 item spots within 250 m","w":604,"s":"gs"},{"x":0.67694,"y":0.83623,"n":"Hospital","r":0.03676,"d":"906 item spots within 300 m","w":906,"s":"gs"},{"x":0.68743,"y":0.84022,"n":"Happy Hospital","r":0.01225,"d":"562 item spots within 100 m","w":562,"s":"gs"},{"x":0.12803,"y":0.29916,"n":"Hae Moo Sa","r":0.02451,"d":"445 item spots within 200 m","w":445,"s":"gs"},{"x":0.09392,"y":0.54542,"n":"Ha Po","r":0.02451,"d":"711 item spots within 200 m","w":711,"s":"gs"},{"x":0.23686,"y":0.71581,"n":"Ho San Prison","r":0.02451,"d":"474 item spots within 200 m","w":474,"s":"gs"},{"x":0.30513,"y":0.29675,"n":"Go Dok","r":0.04289,"d":"1610 item spots within 350 m","w":1610,"s":"gs"},{"x":0.37656,"y":0.44119,"n":"Palace","r":0.03676,"d":"1105 item spots within 300 m","w":1105,"s":"gs"},{"x":0.30812,"y":0.5214,"n":"Fishing Camp","r":0.02451,"d":"250 item spots within 200 m","w":250,"s":"gs"},{"x":0.41879,"y":0.62448,"n":"Ho San","r":0.04289,"d":"1847 item spots within 350 m","w":1847,"s":"gs"}],"bridges":[{"x":0.35981,"y":0.50519,"n":"Bridge","s":"gs"},{"x":0.52694,"y":0.37375,"n":"Bridge","s":"gs"},{"x":0.64076,"y":0.31136,"n":"Bridge","s":"gs"},{"x":0.69656,"y":0.33374,"n":"Bridge","s":"gs"},{"x":0.74443,"y":0.40505,"n":"Bridge","s":"gs"},{"x":0.65487,"y":0.21302,"n":"Bridge","s":"gs"},{"x":0.69792,"y":0.51985,"n":"Bridge","s":"gs"},{"x":0.1873,"y":0.47247,"n":"Bridge","s":"gs"}],"cars":[{"x":0.872,"y":0.1845,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.8579,"y":0.20974,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.80423,"y":0.19259,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.75637,"y":0.20582,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.71115,"y":0.26324,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.71152,"y":0.23536,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.71703,"y":0.29197,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.72806,"y":0.31489,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.77488,"y":0.33725,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each match (~45%); pool: Uaz, Coupe RB, Porter, Dacia, Pony Coupe, Motorbike, Buggy","p":0.45,"s":"gs"},{"x":0.69136,"y":0.34038,"n":"Road spot","t":"car","d":"132-152 of 337 spots spawn a vehicle each maLine truncated
+1
View File
@@ -0,0 +1 @@
{"v":1,"map":"vikendi","built":"2026-10-25","sources":{"gs":{"name":"pubg.gs","url":"https://pubg.gs/maps/vikendi/","note":"positions read from the game level files"},"ib":{"name":"ImmortalBoost, PUBG secret rooms","url":"https://immortalboost.com/blog/pubg/secret-locations/"},"pcg":{"name":"PCGamesN, PUBG map list","url":"https://www.pcgamesn.com/playerunknowns-battlegrounds/pubg-map"},"psr":{"name":"pubgsecretroom.com, best drop locations","url":"https://pubgsecretroom.com/pubg-best-drop-locations-guide.html"},"psrooms":{"name":"pubgsecretrooms.com (exact secret room spots)","url":"https://pubgsecretrooms.com/vikendi-secret-room-locations/"}},"notes":[{"title":"Secret rooms, labs and bear caves","text":"Bear spawns below come from the game files (bear caves hold airdrop-quality loot). Secret rooms and lab rooms: see the source map.","s":"psrooms"}],"layers":{"towns":[{"x":0.21392,"y":0.22557,"n":"Coal Mine","r":0.03676,"d":"454 item spots within 300 m","w":454,"s":"gs"},{"x":0.65387,"y":0.31466,"n":"Laveni","r":0.03676,"d":"1805 item spots within 300 m","w":1805,"s":"gs"},{"x":0.60827,"y":0.48946,"n":"Train Station","r":0.02451,"d":"958 item spots within 200 m","w":958,"s":"gs"},{"x":0.66135,"y":0.63451,"n":"Castle","r":0.01838,"d":"390 item spots within 150 m","w":390,"s":"gs"},{"x":0.7239,"y":0.79797,"n":"Kranik","r":0.03064,"d":"870 item spots within 250 m","w":870,"s":"gs"},{"x":0.85162,"y":0.57177,"n":"Trika","r":0.03676,"d":"1128 item spots within 300 m","w":1128,"s":"gs"},{"x":0.47482,"y":0.66308,"n":"Deka Mesto","r":0.03676,"d":"1845 item spots within 300 m","w":1845,"s":"gs"},{"x":0.23273,"y":0.41325,"n":"Dinoland","r":0.03064,"d":"731 item spots within 250 m","w":731,"s":"gs"},{"x":0.11738,"y":0.60663,"n":"Naznova","r":0.03064,"d":"782 item spots within 250 m","w":782,"s":"gs"},{"x":0.23981,"y":0.77105,"n":"Pavilka","r":0.03064,"d":"708 item spots within 250 m","w":708,"s":"gs"},{"x":0.46452,"y":0.1709,"n":"Observatory","r":0.02451,"d":"452 item spots within 200 m","w":452,"s":"gs"},{"x":0.42054,"y":0.34661,"n":"Naros","r":0.03064,"d":"1055 item spots within 250 m","w":1055,"s":"gs"},{"x":0.32516,"y":0.53449,"n":"Lumber Yard","r":0.03431,"d":"729 item spots within 280 m","w":729,"s":"gs"},{"x":0.37998,"y":0.80873,"n":"Winery","r":0.03431,"d":"794 item spots within 280 m","w":794,"s":"gs"}],"boats":[{"x":0.13206,"y":0.33019,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.07724,"y":0.44992,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.0833,"y":0.53028,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.09252,"y":0.58675,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.09505,"y":0.67316,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.15503,"y":0.76296,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.18436,"y":0.81241,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.22702,"y":0.84774,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.10593,"y":0.74047,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.25763,"y":0.91395,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.34642,"y":0.94134,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.40953,"y":0.93051,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.47211,"y":0.87942,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.5491,"y":0.86541,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.59238,"y":0.89066,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.66739,"y":0.91531,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.75619,"y":0.90792,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.77031,"y":0.86837,"n":"Sea boat","t":"boat","d":"8-12 of 37 spots spawn a vehicle each match (~32%); pool: Boat PG117","p":0.32,"s":"gs"},{"x":0.80199,"y":0.79346,"n":"Sea boat"Line truncated