';
const id = ing.items[0]; const alts = ing.items.length;
const title = (ing.tag ? ing.tag + ': ' : '') + ing.items.slice(0, 12).map(i => itemInfo(i).name).join(', ') + (alts > 12 ? ' …' : '');
return `${id ? icon(id, '') : ''}${alts > 1 ? '+' : ''}${count > 1 ? `${count}` : ''}`;
}
function recipeHtml(rid) {
const r = FG.recipes[rid]; if (!r) return `${esc(rid)}`;
let left;
if (r.grid) {
const w = Math.max(...r.grid.map(row => row.length));
left = `
${r.grid.map(row => { const cells = []; for (let i = 0; i < w; i++) cells.push(slot(row[i])); return cells.join(''); }).join('')}
`;
} else {
const n = r.slots.length; const w = n > 3 ? 3 : n;
left = `
${r.slots.map(s => slot(s)).join('')}
`;
}
return `
${pill(r.status, '')}${esc(r.label)}
${left}→${slot({items: [r.result]}, r.count)}
${esc(itemInfo(r.result).name)}
`;
}
function sourceHtml(s) {
let body = esc(s.text);
if (s.chance) body += ` ${esc(s.chance)}`;
if (s.species) body = body.replace(esc(FG.species[s.species]?.name || '\u0000'), monLink(s.species));
if (s.blocked && s.blocked.length && s.status !== 'available') body += `
Needs ${s.blocked.map(esc).join('; ')}
`;
return `
${esc(s.kind)}
${body}
${pill(s.status, '')}
`;
}
// ---------- pokemon page ----------
function pokemonPage() {
const side = $('#side'); const main = $('#main');
const kinds = [['surface', 'Surface'], ['cave', 'Caves'], ['water', 'Water & coast'], ['end', 'The End (conditional)']];
let s = ``;
s += ``;
for (const [k, label] of kinds) {
const locs = FG.locations.filter(l => l.kind === k); if (!locs.length) continue;
s += `
';
}
function speciesView(sid) {
const sp = FG.species[sid]; if (!sp) return '
Unknown species.
';
let h = `
${monDot(sid)}
${esc(sp.name)} #${sp.dex}
${types(sid)} ${esc(sid)}
`;
h += `
${sp.wild !== 'unavailable' ? 'Found in the wild on the island.' : sp.status !== 'unavailable' ? 'Not found in the wild here, but obtainable another way (below).' : sp.implemented ? 'No way to get it on this island.' : 'Not implemented in Cobblemon (no model), so it cannot be obtained.'}
`;
if (sp.locations.length) {
h += `
Where to find it
Location
Level
Rarity
`;
h += sp.locations.map(l => `
${esc(l.name)}${l.zones && l.zones.length ? ` ${esc(l.zones.join(', '))}` : ''}${l.special ? ' only near particular blocks' : ''}
${l.level[0]}–${l.level[1]}
${l.buckets.map(bucket).join(', ')}
`).join('');
h += `
All ${sp.spawns.length} spawn entries with conditions${sp.spawns.map(i => FG.spawns[i]).map(r => `
`;
return h;
}
function missingView() {
const list = Object.entries(FG.species).filter(([id, sp]) => sp.wild === 'unavailable').sort((a, b) => a[1].dex - b[1].dex);
let h = `
Not found in the wild on the island
${list.length} species. Green: obtainable another way (evolution, fossil, starter, trade). Red: no way on this island.
Pokémon
How to get it here
Wild spawns need
`;
for (const [id, sp] of list) {
const how = sp.sources.filter(s => s.status !== 'unavailable').slice(0, 3).map(s => esc(s.text)).join(' ') || (sp.implemented ? '' : 'not implemented in Cobblemon');
h += `
`;
const msg = it.technical ? 'Creative or admin item; not meant to be obtained in survival.' : it.status === 'available' ? 'Obtainable on the island.' : it.status === 'conditional' ? 'Only obtainable under a condition (e.g. reaching the End).' : 'No source on this island' + (it.reason ? ': ' + it.reason + '.' : '.');
h += `
`;
return h;
}
document.addEventListener('DOMContentLoaded', () => {
initTheme();
const page = document.body.dataset.page;
if (page === 'pokemon') pokemonPage();
if (page === 'items') itemsPage();
});
"""
def slim(data):
"""The JSON as the pages need it (spawns as short records, location indices)."""
loc_index = {loc["id"]: i for i, loc in enumerate(data["locations"])}
spawns = []
for s in data["spawns"]:
spawns.append({
"id": s["id"], "t": s["type"], "b": s["bucket"], "w": s["weight"], "lv": s["level"], "p": s["position"],
"h": s["max_herd"], "st": s["status"],
"m": [[m["species"], " ".join(p for p in m["props"] if not p.startswith(("level", "levelRange"))),
m["level"], m["role"]] for m in s["members"]],
"c": [c["text"] for c in s["conditions"] if c["biomes"]] or [c["text"] for c in s["conditions"]],
"a": s["anticonditions"], "mul": s["multipliers"],
"loc": [loc_index[b] for b in s["biomes"] if b in loc_index],
# needs particular blocks or a structure nearby: listed after the plain spawns
"x": s["needs_blocks"],
})
species = {k: {kk: v[kk] for kk in ("name", "dex", "types", "implemented", "status", "wild", "locations", "spawns",
"sources", "evolutions", "drops", "why_not_wild")}
for k, v in data["species"].items()}
return {"meta": data["meta"], "locations": data["locations"], "spawns": spawns, "species": species,
"items": data["items"], "vanilla": data["vanilla"], "recipes": data["recipes"]}
def page(title, active, body, page_id=""):
nav = "".join(f'{label}'
for href, label in (("index.html", "Overview"), ("pokemon.html", "Pokémon"), ("items.html", "Items")))
return f"""
{html.escape(title)}Cobblemon Survival · Field Guide
{body}
"""
def index_body(data):
e = html.escape
species = data["species"]
wild = sum(1 for s in species.values() if s["wild"] == "available")
other = sum(1 for s in species.values() if s["wild"] != "available" and s["status"] == "available")
cond = sum(1 for s in species.values() if s["status"] == "conditional")
none = sum(1 for s in species.values() if s["status"] == "unavailable")
items = data["items"]
nosrc = sorted((i for i, v in items.items() if v["status"] == "unavailable" and not v["technical"]),
key=lambda i: (items[i]["category"], items[i]["name"]))
used_nosrc = [i for i in nosrc if items[i]["uses"]]
cards = []
for loc in data["locations"]:
n = len({m["species"] for i in loc["spawns"] for m in data["spawns"][i]["members"]})
kind = {"surface": "Surface", "cave": "Cave", "water": "Water / coast", "end": "The End"}.get(loc["kind"], loc["kind"])
cards.append(f'{e(loc["name"])}
')
def item_row(i):
it = items[i]
img = f'' if it.get("icon") else '?'
why = it.get("reason") or "no source"
return (f'{img}{e(it["name"])}'
f' {e(why)}')
problems = "".join(f"{e(k)} ({len(v)})
{e(', '.join(map(str, v[:400])))}
"
for k, v in data["problems"].items() if v)
struct = "".join(f"
{e(s['name'])}
{s['status']}
{e(s['reason'])}
"
for s in data["island"]["structures"] if s["status"] != "unavailable")
return f"""
Field guide
Where to find every Pokémon on the island, how to get every item the mods add, and what each item is for,
generated from the pack's own data (Cobblemon spawn pools, species, recipes and loot tables, with datapacks/core on top).
Generated {e(data['meta']['generated'])}.
{wild}Pokémon in the wild here
{other}more by evolution, fossils, starters
{cond}only in the End
{none}Pokémon with no way to get them here
{len(items)}mod items
{len(nosrc)}items with no source here
Statuses: on the islandconditional needs something that may not exist (the End, a stronghold inside the border) no source here ore, Nether or missing biome only.
{len(used_nosrc)} of them are used by a recipe or a Cobblemon mechanic. Creative/admin items are left out.
{''.join(item_row(i) for i in nosrc)}
Structures that can generate
{struct}
What the generator could not resolve
{problems}
Unofficial fan project, not affiliated with Nintendo, Game Freak, Creatures Inc., The Pokémon Company, the Cobblemon team, Mojang Studios or Microsoft.
Icons are taken from the installed mods at build time and are their authors' work.