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
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:
1 parent
b1edf7761c
commit
be3473caa0
15 files changed
+1057
-5
No files matched your search
@@ -25,3 +25,6 @@ static/vendor/
|
||||
|
||||
# Generated drizzle snapshots
|
||||
drizzle/*/snapshot.json
|
||||
|
||||
# Generated PUBG map intel (scripts/build-pubg-intel.py)
|
||||
static/pubg-intel/
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"}]}}
|
||||
@@ -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"}]}}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user