islands: extract in-game names (Landmark -> Toponym), incl. Wunderinsel = Strudel
Definitive island prefab -> in-game name link: each island_* prefab has a child Landmark GameObject (LandmarkBehaviour.name) = the Toponym key, localized via i2 Toponyms/<name>. New bundle/extract_island_names.py -> extracted/island_names.json. Key results: Demo_Wunderinsel=Strudel, Demo_Marktinsel=Segen, Gartenfreude=Insel St. Clemens, DeusExMashineSmall=Rauchwolke, and island_Factorio = Sprengstofffabrik (the explosives factory). The LittleFactory/LittleFactoryArmory prefabs are the Forts. Updates docs/PRODUCTION_LINES.md with in-game names and corrects the earlier Sprengstofffabrik note (it IS the explosives factory -> Factorio, not the Armory islands). Supersedes the name-guesses (e.g. DeusExMashine is Rauchwolke, not Maschineninsel).
This commit is contained in:
85
bundle/extract_island_names.py
Normal file
85
bundle/extract_island_names.py
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Map each island prefab to its in-game display name (Toponym).
|
||||
|
||||
The internal prefab name (e.g. island_Demo_Wunderinsel) often differs from the name
|
||||
shown in-game. The authoritative link is a child `Landmark` GameObject carrying a
|
||||
`LandmarkBehaviour` (Hologryph.Sand.Shared.World.DesignedEnvironment.Configuration)
|
||||
whose `name` field IS the Toponym key. The English display string is then
|
||||
i2 `Toponyms/<name>`.
|
||||
|
||||
Example: island_Demo_Wunderinsel -> Landmark.name "Strudel" -> Toponyms/Strudel.
|
||||
|
||||
Writes extracted/island_names.json.
|
||||
"""
|
||||
import os, sys, json, UnityPy
|
||||
from UnityPy.helpers.TypeTreeGenerator import TypeTreeGenerator
|
||||
|
||||
GAME = "/mnt/d/SteamLibrary/steamapps/common/Sand Playtest"
|
||||
META = os.path.join(GAME, "Sand_Data/il2cpp_data/Metadata/global-metadata.dat")
|
||||
DLL = os.path.join(GAME, "GameAssembly.dll")
|
||||
BUNDLES = "/home/downloadpizza/sand_tools/bundles"
|
||||
I2 = "/home/downloadpizza/sand_tools/i2_terms_en.json"
|
||||
OUT = "/home/downloadpizza/sand_tools/extracted/island_names.json"
|
||||
LM_FULL = "Hologryph.Sand.Shared.World.DesignedEnvironment.Configuration.LandmarkBehaviour"
|
||||
|
||||
|
||||
def main():
|
||||
gen = TypeTreeGenerator("6000.0.40f1")
|
||||
gen.load_il2cpp(open(DLL, "rb").read(), open(META, "rb").read())
|
||||
env = UnityPy.load(os.path.join(BUNDLES, "islands_assets_all.bundle"),
|
||||
os.path.join(BUNDLES, "sand_monoscripts.bundle"))
|
||||
nodes = [None]
|
||||
|
||||
def landmark_name(go, seen):
|
||||
for c in go.m_Components:
|
||||
try:
|
||||
co = c.read()
|
||||
except Exception:
|
||||
continue
|
||||
r = co.object_reader
|
||||
if r.type.name == "MonoBehaviour":
|
||||
try:
|
||||
sc = co.m_Script.read()
|
||||
if sc.m_ClassName == "LandmarkBehaviour":
|
||||
if nodes[0] is None:
|
||||
nodes[0] = json.loads(gen.get_nodes_as_json(sc.m_AssemblyName, LM_FULL))
|
||||
nm = r.read_typetree(nodes[0]).get("name")
|
||||
if nm:
|
||||
return nm
|
||||
except Exception:
|
||||
pass
|
||||
elif r.type.name == "Transform":
|
||||
for ch in co.m_Children:
|
||||
try:
|
||||
g = ch.read().m_GameObject.read()
|
||||
pid = g.object_reader.path_id
|
||||
if pid not in seen:
|
||||
seen.add(pid)
|
||||
res = landmark_name(g, seen)
|
||||
if res:
|
||||
return res
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
i2 = json.load(open(I2)) if os.path.exists(I2) else {}
|
||||
rows = {}
|
||||
for path, obj in env.container.items():
|
||||
n = path.split("/")[-1].replace(".prefab", "")
|
||||
if obj.type.name == "GameObject" and n.startswith("island_"):
|
||||
lm = landmark_name(obj.read(), set())
|
||||
rows[n] = {"toponym": lm, "display_en": i2.get("Toponyms/%s" % lm) if lm else None}
|
||||
|
||||
out = {
|
||||
"_source": "islands_assets_all.bundle island_* prefab -> child Landmark "
|
||||
"(LandmarkBehaviour.name) -> i2 Toponyms/<name>",
|
||||
"islands": dict(sorted(rows.items())),
|
||||
}
|
||||
json.dump(out, open(OUT, "w"), indent=1, ensure_ascii=False)
|
||||
print("wrote %s\n" % OUT)
|
||||
for n, d in out["islands"].items():
|
||||
print(" %-34s -> %s" % (n, d["toponym"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -68,20 +68,25 @@ venv/bin/python bundle/extract_conveyor_placements.py # -> extracted/conveyor_
|
||||
This walks every island prefab, collects the `game_conveyor_<product>` children (with local
|
||||
position) and cross-references each against `production_lines.json`. Result for this build:
|
||||
|
||||
| Island | Recipe conveyors placed |
|
||||
|---|---|
|
||||
| `island_Factorio` | energyRods, 80mmT3Cannon, contactGrenades, armorPiercingRocket* |
|
||||
| `island_Kaiserplatz` | energyRods, 40mmT3Cannon |
|
||||
| `island_Demo_Wunderinsel` | 70mmT3Cannon |
|
||||
| `island_DeusExMashineSmall` | computingModules, coralDust |
|
||||
| `island_testIsland`, `island_testIslandTramplers` | the 4 `base*` conveyors (inv↔large test combos, incl. the `1x wineBox → 1000x coinCrown` one) |
|
||||
| Island prefab | In-game name (Toponym) | Recipe conveyors placed |
|
||||
|---|---|---|
|
||||
| `island_Factorio` | **Sprengstofffabrik** | energyRods, 80mmT3Cannon, contactGrenades, armorPiercingRocket* |
|
||||
| `island_Kaiserplatz` | Kaiserplatz | energyRods, 40mmT3Cannon |
|
||||
| `island_Demo_Wunderinsel` | **Strudel** | 70mmT3Cannon |
|
||||
| `island_DeusExMashineSmall` | Rauchwolke | computingModules, coralDust |
|
||||
| `island_testIsland`, `island_testIslandTramplers` | (test, no Landmark) | the 4 `base*` conveyors (inv↔large test combos, incl. `1x wineBox → 1000x coinCrown`) |
|
||||
|
||||
In-game names come from each island prefab's child `Landmark` (`LandmarkBehaviour.name`) →
|
||||
i2 `Toponyms/<name>`; see `bundle/extract_island_names.py` / `extracted/island_names.json`.
|
||||
|
||||
Notes / corrections:
|
||||
- The earlier guess that the **`…Armory` (Sprengstofffabrik)** islands host the explosive /
|
||||
grenade / rocket conveyors is **not supported by the placements**: `island_LittleFactory01-03`
|
||||
and `island_LittleFactoryArmory01-03` exist (each has an `energy_grid_*`) but place **no**
|
||||
recipe conveyors at all. The grenade (`contactGrenades`) and rocket (`armorPiercingRocket`)
|
||||
lines are on **Factorio**. Factorio is the main multi-product factory here.
|
||||
- The **Sprengstofffabrik** (explosives factory) is literally **`island_Factorio`** — its
|
||||
in-game Landmark name is `Sprengstofffabrik`, and it makes the grenades + AP rockets +
|
||||
cannons. So the original "Sprengstofffabrik = explosives" intuition was right; it's just
|
||||
that the prefab is `Factorio`, not an `…Armory` island.
|
||||
- The `island_LittleFactory01-03` / `…Armory01-03` prefabs are actually the **Forts**
|
||||
in-game (Fort Istria/Arpad/Tarnopol and Fort Zimmer/Metternich/Starhemberg) and place **no**
|
||||
recipe conveyors.
|
||||
- `*` Factorio's child is named `game_conveyor_armorPiercingRocket` (singular) while the recipe
|
||||
blueprint is `game_conveyor_armorPiercingRockets` (plural) — a name mismatch; same line
|
||||
(→ `3x item_rocketLauncherAmmoArmorPiercing`).
|
||||
|
||||
137
extracted/island_names.json
Normal file
137
extracted/island_names.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"_source": "islands_assets_all.bundle island_* prefab -> child Landmark (LandmarkBehaviour.name) -> i2 Toponyms/<name>",
|
||||
"islands": {
|
||||
"island_Achilleon": {
|
||||
"toponym": "Achilleon",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Archipel": {
|
||||
"toponym": "Archipel von Leopold",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Fort": {
|
||||
"toponym": "Fort",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_KleinesHaus": {
|
||||
"toponym": "Eckzahn",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Lanzelin": {
|
||||
"toponym": "Lanzelin",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Marktinsel": {
|
||||
"toponym": "Segen",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Schlangeninsel": {
|
||||
"toponym": "Schlangeninsel",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Tieftauchparadies": {
|
||||
"toponym": "Felsig",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Windig": {
|
||||
"toponym": "Windig",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Demo_Wunderinsel": {
|
||||
"toponym": "Strudel",
|
||||
"display_en": null
|
||||
},
|
||||
"island_DeusExMashineSmall": {
|
||||
"toponym": "Rauchwolke",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Factorio": {
|
||||
"toponym": "Sprengstofffabrik",
|
||||
"display_en": null
|
||||
},
|
||||
"island_FortArpad": {
|
||||
"toponym": "Fort Arpad",
|
||||
"display_en": null
|
||||
},
|
||||
"island_FortIstria": {
|
||||
"toponym": "Fort Istria",
|
||||
"display_en": null
|
||||
},
|
||||
"island_FortTarnopol": {
|
||||
"toponym": "Fort Tarnopol",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Gartenfreude": {
|
||||
"toponym": "Insel St. Clemens",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Kaiserplatz": {
|
||||
"toponym": "Kaiserplatz",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactory01": {
|
||||
"toponym": "Fort Istria",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactory02": {
|
||||
"toponym": "Fort Arpad",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactory03": {
|
||||
"toponym": "Fort Tarnopol",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactoryArmory01": {
|
||||
"toponym": "Fort Zimmer",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactoryArmory02": {
|
||||
"toponym": "Fort Metternich",
|
||||
"display_en": null
|
||||
},
|
||||
"island_LittleFactoryArmory03": {
|
||||
"toponym": "Fort Starhemberg",
|
||||
"display_en": null
|
||||
},
|
||||
"island_MeereSauge": {
|
||||
"toponym": "MeereSauge",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Petro": {
|
||||
"toponym": "Kaiserplatz",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Raumnadel": {
|
||||
"toponym": "Raumnadel",
|
||||
"display_en": null
|
||||
},
|
||||
"island_ScharfeSpitzen": {
|
||||
"toponym": "ScharfeSpitzen",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Schwalbenlnsel": {
|
||||
"toponym": "Schwalbenberg",
|
||||
"display_en": null
|
||||
},
|
||||
"island_StufenInsel": {
|
||||
"toponym": "StufenInsel",
|
||||
"display_en": null
|
||||
},
|
||||
"island_Venedig": {
|
||||
"toponym": "Venedig",
|
||||
"display_en": null
|
||||
},
|
||||
"island_VenedigSmall": {
|
||||
"toponym": "Venedig",
|
||||
"display_en": null
|
||||
},
|
||||
"island_testIsland": {
|
||||
"toponym": null,
|
||||
"display_en": null
|
||||
},
|
||||
"island_testIslandTramplers": {
|
||||
"toponym": null,
|
||||
"display_en": null
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user