more docs
This commit is contained in:
parent
1ad746ba82
commit
a70ec15899
21 changed files with 1357 additions and 25 deletions
1
.github/copilot-instructions.md
vendored
1
.github/copilot-instructions.md
vendored
|
|
@ -16,4 +16,5 @@ applyTo: '**'
|
|||
- Prefer small, focused changes.
|
||||
- Validate reverse-engineering and tooling changes with the narrowest relevant check.
|
||||
- Keep read-only analysis separate from any explicit writable workflow.
|
||||
- For authored map-placement or link investigation in Crusader-Map-Viewer, prefer decompressed `.cache/scene-cache/<game>/map-*/<hash>/scene.json` over `site/data`; the cache scenes preserve direct item objects and world coordinates.
|
||||
- If the shell becomes stuck on multiline input or otherwise unhealthy, you could try to press esc in the shell to see if it gets unstuck, otherwise immediately use `vscode_askQuestions` to ask the user to fix the terminal state, then continue the task once the user confirms it is fixed.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
_tmp_find_text_section.py
Normal file
5
_tmp_find_text_section.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import re
|
||||
from pathlib import Path
|
||||
xml = Path('exports/CRUSADER.EXE.xml').read_text(encoding='utf-8', errors='ignore')
|
||||
for m in re.finditer(r'<MEMORY_SECTION NAME="\.text" START_ADDR="([^"]+)" LENGTH="([^"]+)"', xml):
|
||||
print(m.group(0))
|
||||
227
_tmp_valuebox_cache_scan.py
Normal file
227
_tmp_valuebox_cache_scan.py
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
import json
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(r"e:\disasm\Crusader-Map-Viewer\map_renderer\.cache")
|
||||
SCENE_ROOT = ROOT / "scene-cache"
|
||||
REF_ROOT = ROOT / "reference-data"
|
||||
TARGET_SHAPE = "shape:251"
|
||||
MAX_DISTANCE = 1600
|
||||
MAX_NEIGHBORS_PER_ITEM = 8
|
||||
INTERESTING_SHAPES = {
|
||||
"shape:251": "VALUEBOX",
|
||||
"shape:258": "MONITNS",
|
||||
"shape:357": "MONITEW",
|
||||
"shape:871": "WALLMNS",
|
||||
"shape:1086": "WALLMEW",
|
||||
"shape:1019": "SECURNS",
|
||||
"shape:1085": "SECUREW",
|
||||
"shape:1214": "WATCHNS",
|
||||
"shape:1246": "WATCHEW",
|
||||
"shape:2573": "KEYPAD",
|
||||
"shape:2574": "KEYPAD?",
|
||||
}
|
||||
|
||||
|
||||
def load_shape_names(game: str) -> dict[str, str]:
|
||||
ref_path = REF_ROOT / game / "reference-data.json"
|
||||
with ref_path.open("r", encoding="utf-8") as handle:
|
||||
data = json.load(handle)
|
||||
shape_names = {}
|
||||
for entry in data.get("shapeDefinitions", []):
|
||||
shape_id = entry.get("id")
|
||||
if not shape_id:
|
||||
continue
|
||||
name = (
|
||||
entry.get("catalog", {}).get("label")
|
||||
or entry.get("displayName")
|
||||
or shape_id
|
||||
)
|
||||
shape_names[shape_id] = name
|
||||
return shape_names
|
||||
|
||||
|
||||
def squared_distance(a: dict, b: dict) -> int:
|
||||
a_world = a.get("world") or {}
|
||||
b_world = b.get("world") or {}
|
||||
dx = int(a_world.get("x", 0)) - int(b_world.get("x", 0))
|
||||
dy = int(a_world.get("y", 0)) - int(b_world.get("y", 0))
|
||||
dz = int(a_world.get("z", 0)) - int(b_world.get("z", 0))
|
||||
return dx * dx + dy * dy + dz * dz
|
||||
|
||||
|
||||
def main() -> None:
|
||||
shape_names_by_game = {
|
||||
"remorse": load_shape_names("remorse"),
|
||||
"regret": load_shape_names("regret"),
|
||||
}
|
||||
summary = []
|
||||
per_game_shape_counts = defaultdict(Counter)
|
||||
per_game_qlo = defaultdict(Counter)
|
||||
per_game_qhi = defaultdict(Counter)
|
||||
interesting_links = defaultdict(Counter)
|
||||
examples = defaultdict(list)
|
||||
nonzero_examples = defaultdict(list)
|
||||
|
||||
for game_dir in sorted(SCENE_ROOT.iterdir()):
|
||||
if not game_dir.is_dir():
|
||||
continue
|
||||
game_name = game_dir.name
|
||||
base_game = "regret" if game_name.startswith("regret") else "remorse"
|
||||
shape_names = shape_names_by_game[base_game]
|
||||
for map_dir in sorted(game_dir.iterdir()):
|
||||
if not map_dir.is_dir() or not map_dir.name.startswith("map-"):
|
||||
continue
|
||||
map_name = map_dir.name
|
||||
for hash_dir in sorted(map_dir.iterdir()):
|
||||
scene_path = hash_dir / "scene.json"
|
||||
if not scene_path.exists():
|
||||
continue
|
||||
with scene_path.open("r", encoding="utf-8") as handle:
|
||||
scene = json.load(handle)
|
||||
items = scene.get("items", [])
|
||||
valueboxes = [item for item in items if item.get("shapeDefId") == TARGET_SHAPE and item.get("frame") == 0]
|
||||
if not valueboxes:
|
||||
continue
|
||||
|
||||
for item in valueboxes:
|
||||
quality = int(item.get("quality", 0))
|
||||
qlo = quality & 0xFF
|
||||
qhi = (quality >> 8) & 0xFF
|
||||
per_game_qlo[game_name][qlo] += 1
|
||||
per_game_qhi[game_name][qhi] += 1
|
||||
|
||||
nearby = []
|
||||
for other in items:
|
||||
if other is item:
|
||||
continue
|
||||
dist2 = squared_distance(item, other)
|
||||
if dist2 <= MAX_DISTANCE * MAX_DISTANCE:
|
||||
nearby.append((dist2, other))
|
||||
nearby.sort(key=lambda pair: pair[0])
|
||||
|
||||
for _, other in nearby[:MAX_NEIGHBORS_PER_ITEM]:
|
||||
per_game_shape_counts[game_name][other.get("shapeDefId", "<none>")] += 1
|
||||
|
||||
for dist2, other in nearby:
|
||||
shape_id = other.get("shapeDefId", "<none>")
|
||||
if shape_id in INTERESTING_SHAPES:
|
||||
interesting_links[game_name][shape_id] += 1
|
||||
|
||||
if len(examples[game_name]) < 12:
|
||||
world = item.get("world") or {}
|
||||
example_row = {
|
||||
"map": map_name,
|
||||
"id": item.get("id"),
|
||||
"coords": [world.get("x"), world.get("y"), world.get("z")],
|
||||
"quality": quality,
|
||||
"qlo": qlo,
|
||||
"qhi": qhi,
|
||||
"mapNum": item.get("mapNum"),
|
||||
"npcNum": item.get("npcNum"),
|
||||
"nextItem": item.get("nextItem"),
|
||||
"nearby": [
|
||||
{
|
||||
"shape": other.get("shapeDefId"),
|
||||
"name": shape_names.get(other.get("shapeDefId", ""), other.get("shapeDefId", "")),
|
||||
"frame": other.get("frame"),
|
||||
"quality": other.get("quality"),
|
||||
"mapNum": other.get("mapNum"),
|
||||
"npcNum": other.get("npcNum"),
|
||||
"nextItem": other.get("nextItem"),
|
||||
"coords": [
|
||||
(other.get("world") or {}).get("x"),
|
||||
(other.get("world") or {}).get("y"),
|
||||
(other.get("world") or {}).get("z"),
|
||||
],
|
||||
"dist2": dist2,
|
||||
}
|
||||
for dist2, other in nearby
|
||||
if other.get("shapeDefId") in INTERESTING_SHAPES
|
||||
][:MAX_NEIGHBORS_PER_ITEM]
|
||||
or [
|
||||
{
|
||||
"shape": other.get("shapeDefId"),
|
||||
"name": shape_names.get(other.get("shapeDefId", ""), other.get("shapeDefId", "")),
|
||||
"frame": other.get("frame"),
|
||||
"quality": other.get("quality"),
|
||||
"mapNum": other.get("mapNum"),
|
||||
"npcNum": other.get("npcNum"),
|
||||
"nextItem": other.get("nextItem"),
|
||||
"coords": [
|
||||
(other.get("world") or {}).get("x"),
|
||||
(other.get("world") or {}).get("y"),
|
||||
(other.get("world") or {}).get("z"),
|
||||
],
|
||||
"dist2": dist2,
|
||||
}
|
||||
for dist2, other in nearby[:MAX_NEIGHBORS_PER_ITEM]
|
||||
],
|
||||
}
|
||||
examples[game_name].append(example_row)
|
||||
if (qlo or qhi or item.get("mapNum") or item.get("npcNum")) and len(nonzero_examples[game_name]) < 12:
|
||||
nonzero_examples[game_name].append(example_row)
|
||||
elif (qlo or qhi or item.get("mapNum") or item.get("npcNum")) and len(nonzero_examples[game_name]) < 12:
|
||||
world = item.get("world") or {}
|
||||
nonzero_examples[game_name].append(
|
||||
{
|
||||
"map": map_name,
|
||||
"id": item.get("id"),
|
||||
"coords": [world.get("x"), world.get("y"), world.get("z")],
|
||||
"quality": quality,
|
||||
"qlo": qlo,
|
||||
"qhi": qhi,
|
||||
"mapNum": item.get("mapNum"),
|
||||
"npcNum": item.get("npcNum"),
|
||||
"nextItem": item.get("nextItem"),
|
||||
}
|
||||
)
|
||||
|
||||
summary.append({"game": game_name, "map": map_name, "count": len(valueboxes)})
|
||||
|
||||
print("VALUEBOX frame-0 counts by map")
|
||||
for row in sorted(summary, key=lambda entry: (-entry["count"], entry["game"], entry["map"]))[:40]:
|
||||
print(f"{row['game']:12} {row['map']:8} count={row['count']}")
|
||||
|
||||
print("\nTop nearby shapes per game")
|
||||
for game_name, counter in sorted(per_game_shape_counts.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for shape_id, count in counter.most_common(20):
|
||||
base_game = "regret" if game_name.startswith("regret") else "remorse"
|
||||
shape_name = shape_names_by_game[base_game].get(shape_id, shape_id)
|
||||
print(f"{shape_id:10} {count:5} {shape_name}")
|
||||
|
||||
print("\nInteresting nearby controller families")
|
||||
for game_name, counter in sorted(interesting_links.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for shape_id, count in counter.most_common():
|
||||
print(f"{shape_id:10} {count:5} {INTERESTING_SHAPES[shape_id]}")
|
||||
|
||||
print("\nTop QLo values per game")
|
||||
for game_name, counter in sorted(per_game_qlo.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for value, count in counter.most_common(20):
|
||||
print(f"QLo {value:3} -> {count}")
|
||||
|
||||
print("\nTop QHi values per game")
|
||||
for game_name, counter in sorted(per_game_qhi.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for value, count in counter.most_common(20):
|
||||
print(f"QHi {value:3} -> {count}")
|
||||
|
||||
print("\nRepresentative examples")
|
||||
for game_name, rows in sorted(examples.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for row in rows:
|
||||
print(json.dumps(row, sort_keys=True))
|
||||
|
||||
print("\nNonzero payload examples")
|
||||
for game_name, rows in sorted(nonzero_examples.items()):
|
||||
print(f"\n[{game_name}]")
|
||||
for row in rows:
|
||||
print(json.dumps(row, sort_keys=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
329
_tmp_valuebox_cache_scan_output.txt
Normal file
329
_tmp_valuebox_cache_scan_output.txt
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
VALUEBOX frame-0 counts by map
|
||||
remorse map-19 count=57
|
||||
remorse-101 map-19 count=57
|
||||
remorse-jp map-19 count=57
|
||||
remorse map-29 count=32
|
||||
remorse-101 map-29 count=32
|
||||
remorse-jp map-29 count=32
|
||||
remorse map-62 count=28
|
||||
remorse-101 map-62 count=28
|
||||
remorse-jp map-62 count=28
|
||||
regret map-15 count=27
|
||||
remorse map-47 count=27
|
||||
remorse-101 map-47 count=27
|
||||
remorse-jp map-47 count=27
|
||||
remorse map-25 count=25
|
||||
remorse map-69 count=25
|
||||
remorse-101 map-25 count=25
|
||||
remorse-101 map-69 count=25
|
||||
remorse-jp map-25 count=25
|
||||
remorse-jp map-69 count=25
|
||||
remorse map-141 count=24
|
||||
remorse-101 map-141 count=24
|
||||
remorse-jp map-141 count=24
|
||||
remorse map-10 count=22
|
||||
remorse map-26 count=22
|
||||
remorse-101 map-10 count=22
|
||||
remorse-101 map-26 count=22
|
||||
remorse-jp map-10 count=22
|
||||
remorse-jp map-26 count=22
|
||||
regret map-16 count=16
|
||||
regret map-13 count=14
|
||||
regret map-14 count=14
|
||||
regret map-8 count=11
|
||||
regret map-28 count=10
|
||||
regret map-5 count=10
|
||||
regret map-7 count=10
|
||||
regret map-200 count=9
|
||||
regret map-201 count=9
|
||||
remorse map-15 count=9
|
||||
remorse-101 map-15 count=9
|
||||
remorse-jp map-15 count=9
|
||||
|
||||
Top nearby shapes per game
|
||||
|
||||
[regret]
|
||||
shape:248 226 shape_00f8
|
||||
shape:249 76 shape_00f9
|
||||
shape:1232 71 shape_04d0
|
||||
shape:253 49 shape_00fd
|
||||
shape:573 47 shape_023d
|
||||
shape:16 42 shape_0010
|
||||
shape:1061 42 shape_0425
|
||||
shape:534 38 shape_0216
|
||||
shape:1365 28 shape_0555
|
||||
shape:246 22 shape_00f6
|
||||
shape:1278 21 shape_04fe
|
||||
shape:567 20 shape_0237
|
||||
shape:1201 19 CMD_LINK
|
||||
shape:1142 19 shape_0476
|
||||
shape:1364 19 shape_0554
|
||||
shape:247 18 shape_00f7
|
||||
shape:1363 16 shape_0553
|
||||
shape:1369 15 shape_0559
|
||||
shape:1347 15 shape_0543
|
||||
shape:1366 14 shape_0556
|
||||
|
||||
[regret-demo]
|
||||
shape:248 8 shape_00f8
|
||||
shape:99 6 shape_0063
|
||||
shape:249 6 shape_00f9
|
||||
shape:574 5 shape_023e
|
||||
shape:1232 5 shape_04d0
|
||||
shape:431 5 shape_01af
|
||||
shape:250 4 shape_00fa
|
||||
shape:1142 3 shape_0476
|
||||
shape:16 3 shape_0010
|
||||
shape:246 2 shape_00f6
|
||||
shape:252 2 shape_00fc
|
||||
shape:534 1 shape_0216
|
||||
shape:949 1 shape_03b5
|
||||
shape:593 1 shape_0251
|
||||
shape:1377 1 shape_0561
|
||||
shape:1593 1 Roof_Regret_Level1
|
||||
shape:253 1 shape_00fd
|
||||
shape:1201 1 CMD_LINK
|
||||
|
||||
[remorse]
|
||||
shape:248 444 shape_00f8
|
||||
shape:249 132 shape_00f9
|
||||
shape:569 77 shape_0239
|
||||
shape:534 62 shape_0216
|
||||
shape:16 60 shape_0010
|
||||
shape:253 58 shape_00fd
|
||||
shape:563 49 shape_0233
|
||||
shape:564 49 shape_0234
|
||||
shape:1253 49 shape_04e5
|
||||
shape:572 46 shape_023c
|
||||
shape:486 42 shape_01e6
|
||||
shape:250 40 shape_00fa
|
||||
shape:43 40 shape_002b
|
||||
shape:573 39 shape_023d
|
||||
shape:716 36 shape_02cc
|
||||
shape:252 35 shape_00fc
|
||||
shape:631 35 shape_0277
|
||||
shape:246 33 shape_00f6
|
||||
shape:15 33 shape_000f
|
||||
shape:547 30 shape_0223
|
||||
|
||||
[remorse-101]
|
||||
shape:248 444 shape_00f8
|
||||
shape:249 132 shape_00f9
|
||||
shape:569 77 shape_0239
|
||||
shape:534 62 shape_0216
|
||||
shape:16 60 shape_0010
|
||||
shape:253 58 shape_00fd
|
||||
shape:563 49 shape_0233
|
||||
shape:564 49 shape_0234
|
||||
shape:1253 49 shape_04e5
|
||||
shape:572 46 shape_023c
|
||||
shape:486 42 shape_01e6
|
||||
shape:250 40 shape_00fa
|
||||
shape:43 40 shape_002b
|
||||
shape:573 39 shape_023d
|
||||
shape:716 36 shape_02cc
|
||||
shape:252 35 shape_00fc
|
||||
shape:631 35 shape_0277
|
||||
shape:246 33 shape_00f6
|
||||
shape:15 33 shape_000f
|
||||
shape:547 30 shape_0223
|
||||
|
||||
[remorse-jp]
|
||||
shape:248 444 shape_00f8
|
||||
shape:249 132 shape_00f9
|
||||
shape:569 77 shape_0239
|
||||
shape:534 62 shape_0216
|
||||
shape:16 60 shape_0010
|
||||
shape:253 58 shape_00fd
|
||||
shape:563 49 shape_0233
|
||||
shape:564 49 shape_0234
|
||||
shape:1253 49 shape_04e5
|
||||
shape:572 46 shape_023c
|
||||
shape:486 42 shape_01e6
|
||||
shape:250 40 shape_00fa
|
||||
shape:43 40 shape_002b
|
||||
shape:573 39 shape_023d
|
||||
shape:716 36 shape_02cc
|
||||
shape:252 35 shape_00fc
|
||||
shape:631 35 shape_0277
|
||||
shape:246 33 shape_00f6
|
||||
shape:15 33 shape_000f
|
||||
shape:547 30 shape_0223
|
||||
|
||||
Interesting nearby controller families
|
||||
|
||||
[regret]
|
||||
shape:251 226 VALUEBOX
|
||||
shape:357 32 MONITEW
|
||||
shape:258 21 MONITNS
|
||||
shape:1246 20 WATCHEW
|
||||
shape:1019 11 SECURNS
|
||||
shape:1085 6 SECUREW
|
||||
shape:871 6 WALLMNS
|
||||
shape:1086 1 WALLMEW
|
||||
|
||||
[regret-demo]
|
||||
shape:251 6 VALUEBOX
|
||||
shape:258 2 MONITNS
|
||||
shape:1246 2 WATCHEW
|
||||
|
||||
[remorse]
|
||||
shape:251 456 VALUEBOX
|
||||
shape:357 90 MONITEW
|
||||
shape:258 65 MONITNS
|
||||
shape:1246 18 WATCHEW
|
||||
shape:1019 1 SECURNS
|
||||
|
||||
[remorse-101]
|
||||
shape:251 456 VALUEBOX
|
||||
shape:357 90 MONITEW
|
||||
shape:258 65 MONITNS
|
||||
shape:1246 18 WATCHEW
|
||||
shape:1019 1 SECURNS
|
||||
|
||||
[remorse-jp]
|
||||
shape:251 456 VALUEBOX
|
||||
shape:357 90 MONITEW
|
||||
shape:258 65 MONITNS
|
||||
shape:1246 18 WATCHEW
|
||||
shape:1019 1 SECURNS
|
||||
|
||||
Top QLo values per game
|
||||
|
||||
[regret]
|
||||
QLo 0 -> 170
|
||||
QLo 23 -> 1
|
||||
|
||||
[regret-demo]
|
||||
QLo 0 -> 7
|
||||
|
||||
[remorse]
|
||||
QLo 0 -> 299
|
||||
QLo 60 -> 3
|
||||
|
||||
[remorse-101]
|
||||
QLo 0 -> 299
|
||||
QLo 60 -> 3
|
||||
|
||||
[remorse-jp]
|
||||
QLo 0 -> 299
|
||||
QLo 60 -> 3
|
||||
|
||||
Top QHi values per game
|
||||
|
||||
[regret]
|
||||
QHi 0 -> 171
|
||||
|
||||
[regret-demo]
|
||||
QHi 0 -> 7
|
||||
|
||||
[remorse]
|
||||
QHi 0 -> 299
|
||||
QHi 3 -> 3
|
||||
|
||||
[remorse-101]
|
||||
QHi 0 -> 299
|
||||
QHi 3 -> 3
|
||||
|
||||
[remorse-jp]
|
||||
QHi 0 -> 299
|
||||
QHi 3 -> 3
|
||||
|
||||
Representative examples
|
||||
|
||||
[regret]
|
||||
{"coords": [57662, 5790, 0], "id": "item:1425:fixed:251:0:57662:5790:0", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [58302, 5790, 0], "dist2": 409600, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4501, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58590, 7070, 0], "dist2": 2499584, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4229, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 4523, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [58302, 5790, 0], "id": "item:1430:fixed:251:0:58302:5790:0", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [57662, 5790, 0], "dist2": 409600, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4523, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58590, 7070, 0], "dist2": 1721344, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4229, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58590, 7358, 0], "dist2": 2541568, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4080, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 4501, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [58590, 7070, 0], "id": "item:1621:fixed:251:0:58590:7070:0", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [58590, 7358, 0], "dist2": 82944, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4080, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58302, 5790, 0], "dist2": 1721344, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4501, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57662, 5790, 0], "dist2": 2499584, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4523, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 4229, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [58590, 7358, 0], "id": "item:1710:fixed:251:0:58590:7358:0", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [58590, 7070, 0], "dist2": 82944, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4229, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58302, 5790, 0], "dist2": 2541568, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 4501, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 4080, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [59358, 11486, 0], "id": "item:1991:fixed:251:0:59358:11486:0", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [59230, 11486, 0], "dist2": 16384, "frame": 0, "mapNum": 0, "name": "shape_00fd", "nextItem": 5364, "npcNum": 0, "quality": 0, "shape": "shape:253"}, {"coords": [59390, 11262, 0], "dist2": 51200, "frame": 4, "mapNum": 0, "name": "shape_023d", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:573"}, {"coords": [59390, 11262, 0], "dist2": 51200, "frame": 0, "mapNum": 0, "name": "shape_0010", "nextItem": 3701, "npcNum": 0, "quality": 2651, "shape": "shape:16"}, {"coords": [59390, 11262, 0], "dist2": 51200, "frame": 2, "mapNum": 0, "name": "shape_0237", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:567"}, {"coords": [59390, 11262, 0], "dist2": 51200, "frame": 2, "mapNum": 0, "name": "shape_0239", "nextItem": 3719, "npcNum": 0, "quality": 0, "shape": "shape:569"}, {"coords": [59422, 11262, 0], "dist2": 54272, "frame": 1, "mapNum": 0, "name": "shape_0239", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:569"}, {"coords": [59486, 11262, 0], "dist2": 66560, "frame": 1, "mapNum": 0, "name": "shape_023c", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:572"}, {"coords": [59390, 11774, 0], "dist2": 83968, "frame": 4, "mapNum": 0, "name": "shape_023d", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:573"}], "nextItem": 5365, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [58334, 9214, 96], "id": "item:5998:fixed:251:0:58334:9214:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [57758, 9022, 96], "dist2": 368640, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3949, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 9630, 96], "dist2": 504832, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3819, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 10110, 96], "dist2": 1134592, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 3957, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [57758, 9022, 96], "id": "item:6001:fixed:251:0:57758:9022:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [58334, 9214, 96], "dist2": 368640, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3957, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 9630, 96], "dist2": 369664, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3819, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 10110, 96], "dist2": 1183744, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 3949, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [57758, 10110, 96], "id": "item:6046:fixed:251:0:57758:10110:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [57758, 9630, 96], "dist2": 230400, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3819, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58334, 9214, 96], "dist2": 1134592, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3957, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 9022, 96], "dist2": 1183744, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3949, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [57758, 9630, 96], "id": "item:6051:fixed:251:0:57758:9630:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [57758, 10110, 96], "dist2": 230400, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [57758, 9022, 96], "dist2": 369664, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3949, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [58334, 9214, 96], "dist2": 504832, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3957, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 3819, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [37406, 28222, 96], "id": "item:6414:fixed:251:0:37406:28222:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [36958, 28222, 96], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2040, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [37144, 29232, 128], "dist2": 1089768, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1983, "npcNum": 0, "quality": 18972, "shape": "shape:357"}, {"coords": [35902, 28478, 128], "dist2": 2328576, "frame": 0, "mapNum": 22, "name": "shape_043d", "nextItem": 2071, "npcNum": 164, "quality": 18, "shape": "shape:1085"}], "nextItem": 2043, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36958, 28222, 96], "id": "item:6416:fixed:251:0:36958:28222:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [37406, 28222, 96], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2043, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [37144, 29232, 128], "dist2": 1055720, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1983, "npcNum": 0, "quality": 18972, "shape": "shape:357"}, {"coords": [35902, 28478, 128], "dist2": 1181696, "frame": 0, "mapNum": 22, "name": "shape_043d", "nextItem": 2071, "npcNum": 164, "quality": 18, "shape": "shape:1085"}], "nextItem": 2040, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [32414, 33310, 96], "id": "item:6666:fixed:251:0:32414:33310:96", "map": "map-13", "mapNum": 0, "nearby": [{"coords": [33694, 33310, 96], "dist2": 1638400, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1605, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1660, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
|
||||
[regret-demo]
|
||||
{"coords": [48158, 54750, 8], "id": "item:2223:fixed:251:0:48158:54750:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [48158, 55486, 8], "dist2": 541696, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 475, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 642, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [50206, 54750, 8], "id": "item:2250:fixed:251:0:50206:54750:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [50652, 54868, 56], "dist2": 215144, "frame": 0, "mapNum": 167, "name": "MONITNS", "nextItem": 564, "npcNum": 139, "quality": 43, "shape": "shape:258"}, {"coords": [50206, 55486, 8], "dist2": 541696, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 442, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 638, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [54430, 55230, 8], "id": "item:2280:fixed:251:0:54430:55230:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [53982, 55070, 8], "dist2": 226304, "frame": 0, "mapNum": 102, "name": "WATCHEW", "nextItem": 492, "npcNum": 24, "quality": 282, "shape": "shape:1246"}, {"coords": [54366, 54750, 8], "dist2": 234496, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 3670, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 534, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [54366, 54750, 8], "id": "item:2285:fixed:251:0:54366:54750:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [54430, 55230, 8], "dist2": 234496, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 534, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [53982, 55070, 8], "dist2": 249856, "frame": 0, "mapNum": 102, "name": "WATCHEW", "nextItem": 492, "npcNum": 24, "quality": 282, "shape": "shape:1246"}], "nextItem": 3670, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [48158, 55486, 8], "id": "item:2295:fixed:251:0:48158:55486:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [48158, 54750, 8], "dist2": 541696, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 642, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 475, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [50206, 55486, 8], "id": "item:2306:fixed:251:0:50206:55486:8", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [50206, 54750, 8], "dist2": 541696, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 638, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [50652, 54868, 56], "dist2": 583144, "frame": 0, "mapNum": 167, "name": "MONITNS", "nextItem": 564, "npcNum": 139, "quality": 43, "shape": "shape:258"}], "nextItem": 442, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [26526, 28670, 96], "id": "item:3506:fixed:251:0:26526:28670:96", "map": "map-2", "mapNum": 0, "nearby": [{"coords": [26590, 28670, 96], "dist2": 4096, "frame": 0, "mapNum": 0, "name": "shape_00f9", "nextItem": 4941, "npcNum": 0, "quality": 0, "shape": "shape:249"}, {"coords": [26622, 28670, 96], "dist2": 9216, "frame": 2, "mapNum": 0, "name": "shape_0063", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:99"}, {"coords": [26622, 28670, 96], "dist2": 9216, "frame": 0, "mapNum": 0, "name": "shape_0010", "nextItem": 4933, "npcNum": 0, "quality": 347, "shape": "shape:16"}, {"coords": [26622, 28670, 96], "dist2": 9216, "frame": 1, "mapNum": 0, "name": "shape_00f8", "nextItem": 4940, "npcNum": 0, "quality": 0, "shape": "shape:248"}, {"coords": [26398, 28670, 96], "dist2": 16384, "frame": 0, "mapNum": 0, "name": "shape_00fd", "nextItem": 4943, "npcNum": 0, "quality": 0, "shape": "shape:253"}, {"coords": [26494, 28894, 136], "dist2": 52800, "frame": 0, "mapNum": 200, "name": "shape_0476", "nextItem": 4827, "npcNum": 128, "quality": 28425, "shape": "shape:1142"}, {"coords": [26558, 28958, 96], "dist2": 83968, "frame": 0, "mapNum": 239, "name": "shape_0476", "nextItem": 4824, "npcNum": 64, "quality": 9, "shape": "shape:1142"}, {"coords": [26462, 28382, 96], "dist2": 87040, "frame": 8, "mapNum": 111, "name": "CMD_LINK", "nextItem": 4935, "npcNum": 97, "quality": 3334, "shape": "shape:1201"}], "nextItem": 4942, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
|
||||
[remorse]
|
||||
{"coords": [22782, 11326, 0], "id": "item:2327:fixed:251:0:22782:11326:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [23262, 10302, 112], "dist2": 1291520, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2988, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 1676864, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 2726, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 2839, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 12254, 0], "id": "item:2408:fixed:251:0:25534:12254:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 11710, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2753, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1857792, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2883, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2754, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 11710, 0], "id": "item:2411:fixed:251:0:25534:11710:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 12254, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2754, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1074432, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2883, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25534, 10302, 112], "dist2": 1995008, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2898, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25790, 10270, 112], "dist2": 2151680, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2800, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 2205248, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 2726, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 2753, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [22398, 13374, 0], "id": "item:2550:fixed:251:0:22398:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [21950, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2542, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2545, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [21950, 13374, 0], "id": "item:2554:fixed:251:0:21950:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [22398, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2545, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2542, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [20670, 15422, 0], "id": "item:2768:fixed:251:0:20670:15422:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [20350, 15294, 24], "dist2": 119360, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 2530, "npcNum": 0, "quality": 14906, "shape": "shape:357"}, {"coords": [19998, 14558, 72], "dist2": 1203264, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 2531, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 2424, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [30910, 17502, 0], "id": "item:2988:fixed:251:0:30910:17502:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [31070, 16734, 40], "dist2": 617024, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 2224, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 2049, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38462, 19518, 0], "id": "item:3216:fixed:251:0:38462:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38078, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1738, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 217152, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1924, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 293952, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1925, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38014, 19166, 136], "dist2": 343104, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1923, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38704, 18592, 152], "dist2": 939144, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1208576, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1879, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [39742, 20382, 24], "dist2": 2385472, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 3339, "npcNum": 0, "quality": 0, "shape": "shape:258"}], "nextItem": 1745, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38078, 19518, 0], "id": "item:3225:fixed:251:0:38078:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38014, 19166, 136], "dist2": 146496, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1923, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38462, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1745, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 684096, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1924, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 760896, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1925, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38704, 18592, 152], "dist2": 1272456, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1650944, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1879, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1738, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [32414, 20542, 0], "id": "item:3354:fixed:251:0:32414:20542:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [32926, 19998, 0], "dist2": 558080, "frame": 0, "mapNum": 0, "name": "shape_04de", "nextItem": 1767, "npcNum": 0, "quality": 14362, "shape": "shape:1246"}], "nextItem": 3361, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36062, 23614, 0], "id": "item:3782:fixed:251:0:36062:23614:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [36478, 23710, 32], "dist2": 183296, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1177, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [35966, 24030, 32], "dist2": 183296, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 1176, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36926, 23710, 16], "dist2": 755968, "frame": 2, "mapNum": 0, "name": "MONITNS", "nextItem": 1099, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36030, 25054, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 962, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1130, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36030, 25054, 0], "id": "item:3932:fixed:251:0:36030:25054:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [35966, 24030, 32], "dist2": 1053696, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 1176, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36478, 23710, 32], "dist2": 2008064, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1177, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36062, 23614, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1130, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 962, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
|
||||
[remorse-101]
|
||||
{"coords": [22782, 11326, 0], "id": "item:2326:fixed:251:0:22782:11326:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [23262, 10302, 112], "dist2": 1291520, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 533, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 1676864, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 752, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 707, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 11710, 0], "id": "item:2402:fixed:251:0:25534:11710:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 12254, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 782, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1074432, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 664, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25534, 10302, 112], "dist2": 1995008, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 622, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25790, 10270, 112], "dist2": 2151680, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 639, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 2205248, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 752, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 783, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 12254, 0], "id": "item:2410:fixed:251:0:25534:12254:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 11710, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 783, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1857792, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 664, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 782, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [22398, 13374, 0], "id": "item:2536:fixed:251:0:22398:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [21950, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 940, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 937, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [21950, 13374, 0], "id": "item:2545:fixed:251:0:21950:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [22398, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 937, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 940, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [20670, 15422, 0], "id": "item:2753:fixed:251:0:20670:15422:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [20350, 15294, 24], "dist2": 119360, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1039, "npcNum": 0, "quality": 14906, "shape": "shape:357"}, {"coords": [19998, 14558, 72], "dist2": 1203264, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 1038, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 1217, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [30910, 17502, 0], "id": "item:2982:fixed:251:0:30910:17502:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [31070, 16734, 40], "dist2": 617024, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1396, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 1566, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38462, 19518, 0], "id": "item:3225:fixed:251:0:38462:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38078, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1987, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 217152, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1810, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 293952, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1809, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38014, 19166, 136], "dist2": 343104, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1811, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38638, 18602, 152], "dist2": 893136, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1759, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1208576, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1778, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [39742, 20382, 24], "dist2": 2385472, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 2104, "npcNum": 0, "quality": 0, "shape": "shape:258"}], "nextItem": 1980, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38078, 19518, 0], "id": "item:3229:fixed:251:0:38078:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38014, 19166, 136], "dist2": 146496, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1811, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38462, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1980, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 684096, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1810, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 760896, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1809, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38638, 18602, 152], "dist2": 1175760, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1759, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1650944, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1778, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1987, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [32414, 20542, 0], "id": "item:3347:fixed:251:0:32414:20542:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [32926, 19998, 0], "dist2": 558080, "frame": 0, "mapNum": 0, "name": "shape_04de", "nextItem": 1924, "npcNum": 0, "quality": 14362, "shape": "shape:1246"}], "nextItem": 2182, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36062, 23614, 0], "id": "item:3771:fixed:251:0:36062:23614:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [35966, 24030, 32], "dist2": 183296, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 2765, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36478, 23710, 32], "dist2": 183296, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 2764, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36926, 23710, 16], "dist2": 755968, "frame": 2, "mapNum": 0, "name": "MONITNS", "nextItem": 2844, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36030, 25054, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2960, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2748, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36030, 25054, 0], "id": "item:3950:fixed:251:0:36030:25054:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [35966, 24030, 32], "dist2": 1053696, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 2765, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36478, 23710, 32], "dist2": 2008064, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 2764, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36062, 23614, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2748, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2960, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
|
||||
[remorse-jp]
|
||||
{"coords": [22782, 11326, 0], "id": "item:2327:fixed:251:0:22782:11326:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [23262, 10302, 112], "dist2": 1291520, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2988, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 1676864, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 2726, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 2839, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 12254, 0], "id": "item:2408:fixed:251:0:25534:12254:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 11710, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2753, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1857792, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2883, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2754, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [25534, 11710, 0], "id": "item:2411:fixed:251:0:25534:11710:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [25534, 12254, 0], "dist2": 295936, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2754, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [26462, 11262, 112], "dist2": 1074432, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2883, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25534, 10302, 112], "dist2": 1995008, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2898, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [25790, 10270, 112], "dist2": 2151680, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2800, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [24062, 11518, 40], "dist2": 2205248, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 2726, "npcNum": 0, "quality": 50, "shape": "shape:357"}], "nextItem": 2753, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [22398, 13374, 0], "id": "item:2550:fixed:251:0:22398:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [21950, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2542, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2545, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [21950, 13374, 0], "id": "item:2554:fixed:251:0:21950:13374:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [22398, 13374, 0], "dist2": 200704, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 2545, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 2542, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [20670, 15422, 0], "id": "item:2768:fixed:251:0:20670:15422:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [20350, 15294, 24], "dist2": 119360, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 2530, "npcNum": 0, "quality": 14906, "shape": "shape:357"}, {"coords": [19998, 14558, 72], "dist2": 1203264, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 2531, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 2424, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [30910, 17502, 0], "id": "item:2988:fixed:251:0:30910:17502:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [31070, 16734, 40], "dist2": 617024, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 2224, "npcNum": 0, "quality": 0, "shape": "shape:357"}], "nextItem": 2049, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38462, 19518, 0], "id": "item:3216:fixed:251:0:38462:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38078, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1738, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 217152, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1924, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 293952, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1925, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38014, 19166, 136], "dist2": 343104, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1923, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38704, 18592, 152], "dist2": 939144, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1208576, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1879, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [39742, 20382, 24], "dist2": 2385472, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 3339, "npcNum": 0, "quality": 0, "shape": "shape:258"}], "nextItem": 1745, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [38078, 19518, 0], "id": "item:3225:fixed:251:0:38078:19518:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [38014, 19166, 136], "dist2": 146496, "frame": 0, "mapNum": 0, "name": "MONITEW", "nextItem": 1923, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38462, 19518, 0], "dist2": 147456, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1745, "npcNum": 0, "quality": 0, "shape": "shape:251"}, {"coords": [38878, 19358, 136], "dist2": 684096, "frame": 1, "mapNum": 0, "name": "MONITEW", "nextItem": 1924, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [38878, 19198, 136], "dist2": 760896, "frame": 1, "mapNum": 0, "name": "MONITNS", "nextItem": 1925, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38704, 18592, 152], "dist2": 1272456, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 0, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [38846, 18494, 112], "dist2": 1650944, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1879, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1738, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [32414, 20542, 0], "id": "item:3354:fixed:251:0:32414:20542:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [32926, 19998, 0], "dist2": 558080, "frame": 0, "mapNum": 0, "name": "shape_04de", "nextItem": 1767, "npcNum": 0, "quality": 14362, "shape": "shape:1246"}], "nextItem": 3361, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36062, 23614, 0], "id": "item:3782:fixed:251:0:36062:23614:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [36478, 23710, 32], "dist2": 183296, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1177, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [35966, 24030, 32], "dist2": 183296, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 1176, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36926, 23710, 16], "dist2": 755968, "frame": 2, "mapNum": 0, "name": "MONITNS", "nextItem": 1099, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36030, 25054, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 962, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 1130, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [36030, 25054, 0], "id": "item:3932:fixed:251:0:36030:25054:0", "map": "map-10", "mapNum": 0, "nearby": [{"coords": [35966, 24030, 32], "dist2": 1053696, "frame": 2, "mapNum": 0, "name": "MONITEW", "nextItem": 1176, "npcNum": 0, "quality": 0, "shape": "shape:357"}, {"coords": [36478, 23710, 32], "dist2": 2008064, "frame": 0, "mapNum": 0, "name": "MONITNS", "nextItem": 1177, "npcNum": 0, "quality": 0, "shape": "shape:258"}, {"coords": [36062, 23614, 0], "dist2": 2074624, "frame": 0, "mapNum": 0, "name": "shape_00fb", "nextItem": 1130, "npcNum": 0, "quality": 0, "shape": "shape:251"}], "nextItem": 962, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
|
||||
Nonzero payload examples
|
||||
|
||||
[regret]
|
||||
{"coords": [4926, 14302, 96], "id": "item:5149:fixed:251:0:4926:14302:96", "map": "map-29", "mapNum": 0, "nextItem": 1596, "npcNum": 0, "qhi": 0, "qlo": 23, "quality": 23}
|
||||
|
||||
[remorse]
|
||||
{"coords": [53118, 30558, 96], "id": "item:10711:fixed:251:0:53118:30558:96", "map": "map-141", "mapNum": 0, "nextItem": 799, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:12590:glob:251:0:64702:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:12594:glob:251:0:65438:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:11657:fixed:251:0:53118:30558:96", "map": "map-25", "mapNum": 0, "nextItem": 1078, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:13546:glob:251:0:64702:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:13550:glob:251:0:65438:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:10397:fixed:251:0:53118:30558:96", "map": "map-26", "mapNum": 0, "nextItem": 279, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
|
||||
[remorse-101]
|
||||
{"coords": [53118, 30558, 96], "id": "item:10711:fixed:251:0:53118:30558:96", "map": "map-141", "mapNum": 0, "nextItem": 799, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:12590:glob:251:0:64702:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:12594:glob:251:0:65438:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:11664:fixed:251:0:53118:30558:96", "map": "map-25", "mapNum": 0, "nextItem": 1561, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:13546:glob:251:0:64702:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:13550:glob:251:0:65438:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:10397:fixed:251:0:53118:30558:96", "map": "map-26", "mapNum": 0, "nextItem": 279, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
|
||||
[remorse-jp]
|
||||
{"coords": [53118, 30558, 96], "id": "item:10711:fixed:251:0:53118:30558:96", "map": "map-141", "mapNum": 0, "nextItem": 799, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:12590:glob:251:0:64702:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:12594:glob:251:0:65438:34814:96", "map": "map-141", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:11657:fixed:251:0:53118:30558:96", "map": "map-25", "mapNum": 0, "nextItem": 1078, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
{"coords": [64702, 34814, 96], "id": "item:13546:glob:251:0:64702:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [65438, 34814, 96], "id": "item:13550:glob:251:0:65438:34814:96", "map": "map-25", "mapNum": 8, "nextItem": 0, "npcNum": 0, "qhi": 0, "qlo": 0, "quality": 0}
|
||||
{"coords": [53118, 30558, 96], "id": "item:10397:fixed:251:0:53118:30558:96", "map": "map-26", "mapNum": 0, "nextItem": 279, "npcNum": 2, "qhi": 3, "qlo": 60, "quality": 828}
|
||||
|
|
@ -4,6 +4,14 @@ This file is an index. Detailed notes have been split into the `docs/` folder by
|
|||
|
||||
Active live analysis target is now `CRUSADER.EXE`. Existing `CRUSADER-RAW.EXE` notes remain in scope as cross-reference evidence and should be cited alongside live NE addresses when they support a rename, variable role, or behavior claim.
|
||||
|
||||
Recent map-renderer egg-link follow-up: [docs/map_renderer/egg-identification.md](docs/map_renderer/egg-identification.md) now closes the old No Regret map-`3` destination-egg `102` gap. Current best read is that Regret uses a second elevator family at `shape:400` (`0x0190`) in addition to the earlier Remorse-focused `shape:542` rule: recovered Regret `ELEVATOR::gotHit` accepts `QLo >= 100`, treats `QLo < 0x00c8` as the generic same-map lane, and map `3` contains a concrete source object `item:664:fixed:400:0:44030:9662:0` with `quality 614` (`QLo 102`) that resolves the previously unexplained destination egg `102`.
|
||||
|
||||
Recent map-renderer editor-object follow-up: [docs/map_renderer/trigger-usecode-links.md](docs/map_renderer/trigger-usecode-links.md) and [docs/map_renderer/editor-object-survey.md](docs/map_renderer/editor-object-survey.md) now promote a Regret-only controller cluster that had still been sitting in the unresolved editor bucket. Current best read is that `0x04c6` / `0x04de` are `WATCHNS` / `WATCHEW` secret-door watcher controllers, `0x0510` is their nearby `SECRET_DOOR_POST` target keyed by shared `QLo`, `0x05e1` is `CRYOBOX`, and `0x05df` / `0x05e0` are the paired pressure-barrier faces it drives by shared `QLo`. The same batch also closes `0x0451` / `0x05ae` as `CRAZYEW` / `CRAZYNS` hit-driven NPC wake-up relays and `0x056d` as `VIDEOBOX`, then promotes cautious local viewer arrows for `WATCH* -> 0x0510` and `CRYOBOX -> 0x05DF/0x05E0` in the map renderer.
|
||||
|
||||
Recent map-renderer control-pad follow-up: [docs/map_renderer/egg-identification.md](docs/map_renderer/egg-identification.md), [docs/map_renderer/trigger-usecode-links.md](docs/map_renderer/trigger-usecode-links.md), and [docs/map_renderer/editor-object-survey.md](docs/map_renderer/editor-object-survey.md) now tighten the Regret read on `0x0318` / `0x0366` using the decompressed `.cache` scenes rather than the packed site export. Current best read is that `0x0318` is `CRUMORPH`, a control-transfer pad whose `equip` body scans nearby NPCs for a local-`QLo` key before bracketing `TRIGGER.slot_20`, while `0x0366` remains `NPC_ONLY`, a hit-driven NPC-only trigger pad keyed by an internal actor field. The viewer now promotes cautious same-`QLo` local `CRUMORPH -> 0x04B1` and `NPC_ONLY -> 0x04B1` arrows where authored matches exist, but still keeps `NPC_ONLY -> actor` out of the overlay.
|
||||
|
||||
Recent actor-key follow-up: the same map-renderer notes now make the current blocker explicit instead of leaving it as an implied missing export. Current best read is that the hidden actor-side value behind `CRUMORPH` / `NPC_ONLY` is mutable actor field `0x63`, not a stable DTABLE row: sampled Regret DTABLE rows still read as zero at record byte `0x63`, while recovered `TRIGGER.slot_29` / `slot_2B` lanes can rewrite actor field `0x63` on nearby matched NPCs after load. The same pass also widens the sibling-family set that uses this mechanism: `WATCHNS` / `WATCHEW`, `THRMBCKN` / `THRMBCKE`, and `SURCAMNS` / `SURCAMEW` all compare controller-local bytes against actor field `0x63` in recovered lanes, so the viewer now documents a broader actor-key controller family while still withholding speculative actor-target arrows.
|
||||
|
||||
Recent verified PSX pre-alpha batch: [docs/psx/prealpha.md](docs/psx/prealpha.md) now records a focused Ghidra pass on `/psx/prealpha/SLUS_002.68` plus a disc-tree comparison against the released PlayStation `Crusader: No Remorse` build. Current best read is that this pre-pre alpha still looks much more like a trimmed early No Remorse PSX branch than a clearly rebranded `Crusader 2` executable: it still carries direct `Crusader: No Remorse` save/quit text, the renamed `wdl_resource_bundle_load_by_index` still embeds the full retail `\LSET1\L` through `\LSET7\L` prefix table and the same `10/20/30/40/50/60` threshold ladder, and the mission/passcode UI scaffolding is still present with the same visible `15` mission briefing strings and consonant/digit passcode alphabet. The main concrete differences in this batch are the heavily reduced shipped content (`3` level bundles, `1` XA, no `.STR` movies) and the surviving architectural leftovers that no longer match the current disc literally, especially the missing-file `\AUDIO\TALK1.XA;1` path and the `LoadExec` helper for `MENU.EXE` / `ENGINE.EXE` / `PSX.EXE`.
|
||||
|
||||
Recent verified PSX executable batch: [docs/psx/psx.md](docs/psx/psx.md) now records a focused Ghidra pass on `SLUS_002.68` for mission/map inventory, passcode handling, and catalog text. Current best read is that the PSX loader hardcodes seven `\LSETn\L` folder prefixes and the extracted disc ships `62` level bundles (`L0..L58`, `L62..L64`) with a real gap at `L59..L61`, while the executable still exposes only `15` plain-text `Mission Briefing ^Mission N` strings. The same pass closes the visible passcode-generation side too: mission-complete flow synthesizes `4`-character passcodes from the alphabet `BCDFGHJKLMNPQRSTVWXZ0123456789`, and the executable preserves direct ammo/item/weapon name tables. The hidden password-screen cheat codes remain less direct: public PSX references point to `XXXX` and `L0SR`/`L0SER`, but those values are not stored as plain ASCII in `SLUS_002.68`, so the compare path still looks numeric or transformed rather than table-driven.
|
||||
|
|
@ -38,10 +46,14 @@ Latest `-u` deep dive: new note [docs/usecode-startup-override.md](docs/usecode-
|
|||
|
||||
Latest `-u` token-shape follow-up: the same [docs/usecode-startup-override.md](docs/usecode-startup-override.md) note now tightens the argument semantics materially. In live retail `CRUSADER.EXE`, `startup_apply_u_override_if_present` does not pass the copied argv token through as an arbitrary final filename. It loads the mutable filename template `eusecode.flx` from `1478:07a0` via the far pointer at `1478:06d6/06d8`, forces the first byte to `'e'`, and calls `Filespec_GetFullPath(0, s_usecode, "eusecode.flx", 0)`. Current safest read is therefore `path/root override for the standard EUSECODE archive family`, not `free-form arbitrary filename switch`. The same note now also separates the stock-path status more cleanly: the raw-side VM bootstrap is strongly cross-referenced, but the exact live-NE writer that seeds `1478:6611/6613` without `-u` is still not directly closed.
|
||||
|
||||
Latest `-u` practical follow-up: that same [docs/usecode-startup-override.md](docs/usecode-startup-override.md) and [docs/command-line-parameters.md](docs/command-line-parameters.md) notes now make the immediate user-facing consequence explicit. Failed attempts like `-u USECODE/FLICTEST.FLX` and `-u FLICTEST.FLX` fit the live helper badly because retail No Remorse still appends the fixed filename template `EUSECODE.FLX`; the copied argv token is only the path/root component. Current safest experiment shape is therefore `directory passed to -u, complete replacement archive named EUSECODE.FLX inside that directory`, not `free-form archive basename override`.
|
||||
|
||||
Latest `-u` loader-layout follow-up: the same [docs/usecode-startup-override.md](docs/usecode-startup-override.md) note now records the direct constructor/loader pair behind the override in the live NE session. `1420:1499` is now renamed `entity_vm_runtime_create` and currently reads as a `0x1319`-byte runtime-object constructor with a `0x1300`-byte front region that behaves like `0x80` stride-`0x26` slot/runtime records plus tail metadata at `0x1300..0x1318`. `1430:0000` is now renamed `entity_vm_runtime_owner_resource_create` and currently reads as the compact `0x14`-byte file-backed helper allocated from the resolved `eusecode.flx` path and attached to the runtime object at `+0x1315/+0x1317`.
|
||||
|
||||
Latest doc-reconciliation batch: [docs/ne-segment1.md](docs/ne-segment1.md) now has a combined hidden-debugger component table that explicitly separates the seg109/raw-reference UI wrappers (`000b:9a86`, `000b:9c0d`, `000b:b3b1`, `000b:b62c`, `000b:2882`) from the live seg1408 breakpoint-state helpers (`1408:0000`, `1408:0053`, `1408:00dd`, `1408:029e`, `1408:03b0`, `1408:03f7`, `1408:0419`, `1408:0432`, `1408:0444`) and the interpreter hook at `1418:04aa..04b5`. Current best read remains `two connected layers of one hidden usecode debugger`, not `conflicting address claims for the same function family`.
|
||||
|
||||
Latest hidden-debugger floor pass: [docs/retail-debugger-patch-attempts.md](docs/retail-debugger-patch-attempts.md) now records a fresh live-Ghidra constraint check on the smallest viable retail unlock patch. Current best read is tighter than the earlier "there must be one tiny jump" idea: retail still has no recovered writer for `1478:659c/659e`, the constructor still seeds only the inert shared callbacks `1478:65ab -> 1408:046f` / `1478:65af -> 1408:0474`, and the interpreter-side gate at `1418:049e..04b5` only checks for a non-null debugger object before handing off to `1408:0053`. That makes the current O/P interpreter-callsite-retarget family the smallest structurally defensible executable patch shape so far, because smaller one-site ideas lose either object bootstrap, one-shot deferred gating, or wrapper-argument sanitation.
|
||||
|
||||
Follow-up cheat-key correction pass: [docs/ne-segment1.md](docs/ne-segment1.md) now also records a live NE cleanup of several folklore keyboard-cheat claims. `~` is a real runtime cheat-latch toggle at `13e8:203d`, `Ctrl+C` is wrong for this build and should be `Ctrl+L` for the coordinate popup at `13e8:255e`, and the third F7-family overlay really does exist as a separate `Ctrl+F7` path at `13e8:1a20` alongside the other two cheat-gated F7 overlay toggles.
|
||||
|
||||
That same note now also separates `~` from `jassica16` more cleanly: `jassica16` is the raw scan-code unlock path that toggles both `1478:0844` and `1478:6045` and sets the extra post-sequence latch `1478:8c52`, while `~` is only the later translated logical-`0x7e` hotkey that flips `1478:6045` after `1478:0844` is already on. The F7-family clarification is tighter too: `Ctrl+F7` is best read as an egg-hatcher trigger-range overlay rather than a third generic background grid.
|
||||
|
|
@ -90,5 +102,6 @@ Latest F7 overlay follow-up: new note [docs/f7-overlays.md](docs/f7-overlays.md)
|
|||
| [docs/usecode-jelyhack-analysis.md](docs/usecode-jelyhack-analysis.md) | Focused analysis of exported `JELYHACK` / `JELYH2` pseudocode, the tiny shared `use` stub, and why the current best model remains `referent anchor + neighboring event-bearing attachment` |
|
||||
| [docs/usecode-equipment-system.md](docs/usecode-equipment-system.md) | Evidence-backed note on Crusader's surviving `equip` / `unequip` event system, including live compiled-side dispatcher proof, corpus-wide slot counts, actor/turret/environment examples, and the current best model of `equip` as a generalized inherited Ultima-style item event |
|
||||
| [docs/usecode-alarmhat-analysis.md](docs/usecode-alarmhat-analysis.md) | Focused analysis of exported `ALARMHAT::equip`, the nearby `shape 0x04D0` equip loops, alarm-family comparisons, and the current gameplay-facing read of `ALARMHAT` as a local alarm-state driver |
|
||||
| [docs/usecode/flictest-investigation.md](docs/usecode/flictest-investigation.md) | Focused investigation of `FLICTEST`: class/body structure, Remorse versus Regret differences, confirmed caller scripts, and the stronger Regret finding that a hidden `KEYPADNS` `VIDEO PLAYER` route appears to have matching shipped placement evidence |
|
||||
| [docs/usecode/windsurf-regret-vs-remorse.md](docs/usecode/windsurf-regret-vs-remorse.md) | Side-by-side comparison of `WINDSURF` in Regret and No Remorse, including shared slot behavior, helper-family drift, body-size differences, and the current best read of `WINDSURF` as a directional wind-force helper used by vent scripts |
|
||||
| [docs/removed_items.md](docs/removed_items.md) | Evidence summary for suspicious removed item shapes in old No Remorse maps: grenade-family leftovers `0343/034E/034F/0350`, the inventory-labeled `0548` `Invalid` item, and unresolved non-pickup shapes `0110/0112` |
|
||||
|
|
|
|||
|
|
@ -151,14 +151,76 @@ What is now materially tighter:
|
|||
- it uses the token as the `path` component to `Filespec_GetFullPath`
|
||||
- it uses the mutable filename template at `1478:07a0`, which is `eusecode.flx`, as the fixed `filename` component
|
||||
- it forces that template's first byte to `'e'` before the existence probe and final load call
|
||||
- the path builder uses DOS-style backslashes via the literal `"\\"` string from `FILE\\FILESPEC.C`
|
||||
- the existence probe goes through the DOS file-search path in `File_Exists`, so the override must resolve inside the game's DOS-visible filesystem
|
||||
- the parser copies the token into the `0x1e`-byte buffer at `1478:065a`, so the practical maximum is `29` visible characters plus the terminator
|
||||
|
||||
So the safest current retail read is:
|
||||
|
||||
- `-u <arg>` expects a directory/resource-root style path argument for the standard `eusecode.flx` archive family
|
||||
- it does **not** currently look like a free-form arbitrary filename override
|
||||
- the safest token spelling uses DOS-style backslashes, not forward slashes
|
||||
|
||||
That gives a direct explanation for failed attempts like:
|
||||
|
||||
- `-u USECODE/FLICTEST.FLX`
|
||||
- `-u FLICTEST.FLX`
|
||||
- `-u C:\MADDOCODE` when that path only exists on the Windows host and not inside the guest DOS drive mapping
|
||||
|
||||
Those forms fit the recovered helper badly because the token is used as the `Filespec_GetFullPath` path component while the filename remains the fixed template `eusecode.flx`. Current safest interpretation is therefore:
|
||||
|
||||
- pass a directory or resource-root to `-u`
|
||||
- put the replacement archive at `<that root>/EUSECODE.FLX`
|
||||
- do not expect `-u` to open `FLICTEST.FLX` directly as the final archive name
|
||||
|
||||
The current safest syntax examples are:
|
||||
|
||||
- `-u MADDOCODE`
|
||||
- `-u .\MADDOCODE`
|
||||
- `-u \MADDOCODE`
|
||||
- `-u C:\MADDOCODE`
|
||||
|
||||
But for real DOS-facing installs, the safer practical examples are the `8.3`-safe variants:
|
||||
|
||||
- `-u MADDOC~1`
|
||||
- `-u .\MADDOC~1`
|
||||
|
||||
with these current best path interpretations:
|
||||
|
||||
- `MADDOCODE` = relative to the game's current DOS working directory
|
||||
- `.\MADDOCODE` = explicit relative DOS path
|
||||
- `\MADDOCODE` = root-relative path on the current DOS drive
|
||||
- `C:\MADDOCODE` = absolute DOS path on drive `C:` as seen by the DOS game, not automatically the Windows host path
|
||||
|
||||
In the current GOG install used for this investigation, Windows reports the short alias:
|
||||
|
||||
- `MADDOCODE` -> `MADDOC~1`
|
||||
|
||||
That makes `MADDOC~1` the highest-probability next token for this specific setup.
|
||||
|
||||
Current safest negative guidance:
|
||||
|
||||
- avoid forward slashes for `-u`
|
||||
- avoid long path tokens because silent truncation at `1478:065a` can change the real lookup target
|
||||
- do not use the archive filename itself as the `-u` argument
|
||||
- prefer `8.3`-safe directory names or explicit DOS short aliases when testing under DOSBox-like environments
|
||||
|
||||
But the important uncertainty is now only `exact naming rules`, not `whether the switch is real`. In the regular non-Japanese `CRUSADER.EXE`, the switch is clearly still live.
|
||||
|
||||
## Expected Console Output For `-u`
|
||||
|
||||
Current best answer: probably none.
|
||||
|
||||
The recovered `-u` parser case does not print a banner, and the startup helper that probes and swaps the override path also has no recovered `ConsolePrintf` call. So unlike `-debug`, `-warp`, `-skill`, `-mapoff`, `-egg`, or `-demo`, `-u` does not currently appear to advertise itself on the console before the game loads.
|
||||
|
||||
The safest current expectation is:
|
||||
|
||||
- success: no dedicated `-u` console message
|
||||
- missing path/file: no dedicated `-u` console message, likely silent fallback to stock usecode
|
||||
- malformed but existing replacement: no dedicated `-u` status line from the parser/helper path; later behavior depends on the deeper loader/runtime path
|
||||
|
||||
This also sharpens the practical diagnosis for the current `MADDOCODE` setup. In the tested GOG install, only `EUSECODE.FLX` differs between `USECODE` and `MADDOCODE`; the sidecar files are byte-identical. So if replacing `USECODE\EUSECODE.FLX` with the hacked file produces the expected obvious no-usecode-style breakage, but `-u MADDOCODE` behaves like stock gameplay instead, the strongest current read is that the override path was probably not accepted and startup silently fell back to the stock usecode root.
|
||||
|
||||
This also aligns with the already-stronger JP Win32 result, where the matching `-u` lane was recovered as the same kind of usecode override.
|
||||
|
||||
For the deeper runtime-side investigation of whether `-u` replaces or augments the stock usecode root, and what game systems that replacement feeds, see [docs/usecode-startup-override.md](docs/usecode-startup-override.md).
|
||||
|
|
|
|||
|
|
@ -10,9 +10,29 @@ This pass widened the renderer research beyond egg and NPC spawner objects and f
|
|||
- `0x005A`, `0x005B`, `0x005C`, `0x005D`, `0x0066`-`0x0069`: invisible/editor wall objects
|
||||
- `0x01B8`: `camera`
|
||||
- `0x0290`, `0x0336`: `LIGHT_BRIDGE_*`
|
||||
- `0x0251`, `0x0318`, `0x0337`, `0x0361`: placeholder cubes and placeholder UI/editor markers
|
||||
- `0x0337`, `0x0361`: placeholder cubes and placeholder UI/editor markers
|
||||
- `0x0108`, `0x0113`, `0x01B9`, `0x01BA`, `0x025F`, `0x0260`, `0x02F0`, `0x0373`, `0x0399`, `0x03A1`, `0x04C8`: `wallgun_shape_*` helper cluster
|
||||
|
||||
## `0x0251` Frame `0`: `VALUEBOX`
|
||||
|
||||
- The recovered usecode corpus now closes `0x0251` directly as `VALUEBOX` in both retail games, not as a generic placeholder cube.
|
||||
- Current safest read is `local payload box`, not `world prop`: the object stores numeric or text-selection data for nearby authored controllers instead of behaving like a visible gameplay pickup.
|
||||
- The caller side is much clearer than `VALUEBOX.slot_20(...)` itself. Checked bodies that explicitly scan nearby `shape=0x0251` items include:
|
||||
- `MONITNS` (`0x0102`) and `MONITEW` (`0x0165`)
|
||||
- `WALLMNS` (`0x0367`) and Regret `WALLMEW` (`0x0436`)
|
||||
- Regret `SECURNS` (`0x03FB`) and `SECUREW` (`0x043D`)
|
||||
- Regret `WATCHNS` (`0x04C6`) and `WATCHEW` (`0x04DE`)
|
||||
- `KEYPAD` (`0x0A0E` in Remorse, `0x0A0D` in Regret)
|
||||
- Those families usually match the box on shared `QLo`, then call `VALBOX.slot_20(valueBox)` to recover the stored value. Monitor, wall-display, and security-terminal paths also feed `Item.getQHi(valueBox)` into `TEXTFILE.slot_23(...)`, so `QHi` behaves like a second text/value selector rather than dead metadata.
|
||||
- `VALUEBOX::cachein` also hints at a small self-initialization lane: when the decoded value is zero it calls `FREE.slot_20(0x0383)` and writes a replacement through `VALUEBOX.slot_20(...)`. The slot-20 decompilation is still weak, so the conservative read is `possible value initialization`, not a fully closed random-number claim.
|
||||
- Decompressed `.cache` scenes back the helper role strongly:
|
||||
- Remorse retail caches currently expose `299` frame-0 placements. The strongest nearby controller families in the local scan are `MONITEW` (`90` hits), `MONITNS` (`65`), and `WATCHEW` (`18`).
|
||||
- Regret retail caches currently expose `171` frame-0 placements. The strongest nearby controller families are `MONITEW` (`32`), `MONITNS` (`21`), `WATCHEW` (`20`), `SECURNS` (`11`), `SECUREW` (`6`), and `WALLMNS` (`6`).
|
||||
- Most frame-0 boxes still carry `quality = 0`, which is consistent with the common `QLo == 0` default-link path in the callers.
|
||||
- Rare authored payloads do exist: Regret map `29` has a `VALUEBOX` with `quality 23` (`QLo 23`), and Remorse maps `25`, `26`, and `141` all contain a nonzero example at `53118,30558,96` with `quality 828` (`QLo 60`, `QHi 3`, `npcNum 2`).
|
||||
- Many placements occur in small chained local clusters through `nextItem`, which fits a backing-store/helper role better than a standalone scene-prop interpretation.
|
||||
- Practical renderer implication: `0x0251` should be labeled `VALUEBOX`, should keep `QLo`, `QHi`, and `nextItem` visible in tooltips, and should be treated as a local controller payload object rather than as a generic placeholder cube.
|
||||
|
||||
## What This Means For The Renderer
|
||||
|
||||
- A lot of the useful information is already present without more reverse-engineering. The main problem was presentation, not raw data availability.
|
||||
|
|
@ -46,7 +66,26 @@ The tooltip now exposes generalized metadata for editor/helper objects instead o
|
|||
|
||||
- Add dedicated filters or list views for helper subfamilies such as invisible walls, camera markers, light bridges, and placeholder cubes.
|
||||
- Add shape-family frequency summaries so repeated helper markers can be audited across a map.
|
||||
- Decode more shape-specific field semantics for the still-unresolved editor objects, especially the remaining non-promoted invisible-wall, camera/helper, and music-controller families, and keep folding any new results back into the dedicated USECODE-link note.
|
||||
- Decode more shape-specific field semantics for the still-unresolved editor objects, especially the remaining non-promoted invisible-wall, camera/helper, music-controller, and secret-door-switch families, and keep folding any new results back into the dedicated USECODE-link note.
|
||||
- Find the No Regret replacement for the Remorse `0x024F` monster-egg workflow instead of assuming the same shape is reused.
|
||||
|
||||
## Newly Promoted Regret-Only Controllers
|
||||
|
||||
- `0x0318` is now promoted as `CRUMORPH`, not a blank placeholder cube. The recovered `equip` body scans nearby NPCs for a shared internal control key derived from the item's `QLo`, temporarily transfers player control to the first live match, and then brackets `TRIGGER.slot_20` with success or failure lanes.
|
||||
- `0x0366` remains `NPC_ONLY`, but the latest decompressed `.cache` sweep tightens its practical viewer behavior: actor-target arrows are still not justified, while cautious local `NPC_ONLY -> 0x04B1` same-`QLo` arrows are now strong enough to expose.
|
||||
- `0x04c6` / `0x04de` are now promoted as `WATCHNS` / `WATCHEW`, not generic editor leftovers. Their recovered `slot_20` bodies scan nearby `0x0510` posts by shared `QLo` and then bracket `TRIGGER.slot_20` around a watcher-specific follow-up lane.
|
||||
- `0x0510` is now better treated as a `SECRET_DOOR_POST` helper target rather than an unresolved standalone controller. The strongest current viewer behavior is a cautious local arrow from `WATCHNS` / `WATCHEW` plus tooltip decoding of its `QLo`/`QHi` bytes.
|
||||
- `0x05e1` is closed as `CRYOBOX`, and nearby `0x05df` / `0x05e0` pressure-barrier faces are now promoted out of the unresolved bucket as local arrow targets keyed by shared `QLo`.
|
||||
- `0x0451` / `0x05ae` are now closed as `CRAZYEW` / `CRAZYNS`, small Regret-only hit-driven NPC wake-up relays rather than vague contextual map labels.
|
||||
- `0x056d` is now closed as `VIDEOBOX`, a gated controller with a direct `equip` body, even though its higher-level gameplay meaning is still less explicit than the watcher and cryobox lanes.
|
||||
|
||||
## Actor-Key Family Follow-Up
|
||||
|
||||
- The latest actor-link follow-up did not justify exporting a stable `NPC_ONLY -> actor` or `CRUMORPH -> actor` overlay from static map/cache data alone.
|
||||
- Current best read is that the compared value is mutable actor field `0x63`, not a DTABLE row and not a field that the current scene export already carries.
|
||||
- A direct Regret DTABLE row check at byte offset `0x63` is not enough to rescue that idea: sampled rows still read as zero there, so the actor key is not simply a persistent `NPCDat` attribute that can be copied through the existing preview pipeline.
|
||||
- The same corpus also shows why the field is unstable: `TRIGGER.slot_29` / `slot_2B` can rewrite actor field `0x63` on nearby matched NPCs, so the relevant actor-side link id can change after startup.
|
||||
- The broader family using this same hidden actor-key mechanism is now clearer though: `CRUMORPH`, `NPC_ONLY`, `WATCHNS`, `WATCHEW`, `THRMBCKN`, `THRMBCKE`, `SURCAMNS`, and `SURCAMEW` all compare controller-local bytes against actor field `0x63` in one of their recovered lanes.
|
||||
- Practical renderer stance stays conservative: keep only the already-evidenced local helper arrows in the overlay, and surface the actor-key behavior in metadata/tooltips until a runtime or spawn-time export can close field `0x63` directly.
|
||||
|
||||
`0x04B1` now has a stable `CMD_LINK -> TRIGGER.slot_20` viewer target, and `0x04E3` is already promoted as `SKILLBOX::equip`, so they no longer belong in the unresolved editor-object bucket here.
|
||||
|
|
@ -223,12 +223,15 @@ Scene evidence lines up with that lane in No Remorse map `1`:
|
|||
|
||||
So the public renderer now treats same-map `shape:542` frame-`0` placements as elevator-link sources when their checked `QLo` values map to a local destination egg id.
|
||||
|
||||
## Current Elevator Gap
|
||||
No Regret now closes one additional elevator lane that the earlier Remorse-only rule missed.
|
||||
|
||||
- No Regret map `3` destination egg `102` does not sit on the same currently verified `ELEVATOR` (`shape:542`) or `LIFT` (`shape:307`) lanes.
|
||||
- Current scene exports for that map show no nearby `shape:542` or `shape:307` objects at all.
|
||||
- A nearby editor/helper record `item:1056:fixed:1201:0:41758:33694:0` carries `mapNum 102`, but there is not yet enough executable-side evidence to promote that into a viewer arrow rule.
|
||||
- The map renderer therefore leaves egg `102` unresolved for now instead of hardcoding a speculative elevator source pattern.
|
||||
- Regret also ships a separate elevator family at `shape:400` (`0x0190`), which the current shape catalog leaves unnamed but the recovered usecode closes as `ELEVATOR` through `ELEVATOR::slot_20`'s local `nearby_items(shape=0x0190, origin=global[0x001e])` scan.
|
||||
- In Regret `ELEVATOR::gotHit`, the gate is different from the Remorse `shape:542` body: the object must have `QLo >= 100`, and the generic local-elevator lane is `QLo < 0x00c8`.
|
||||
- That generic Regret lane dispatches `ELEVATOR::slot_20` with the current map and the source object's `QLo` as the destination egg id, while `QHi` still carries the direction/state argument.
|
||||
- Regret map `3` now has a concrete same-map source for destination egg `102`: `item:664:fixed:400:0:44030:9662:0` with `quality 614` (`QLo 102`, `QHi 2`).
|
||||
- Current scene exports for that map still show no `shape:542` or `shape:307` objects at all near destination egg `102`, and the nearby `0x04B1` cmd-link helpers instead carry unrelated local keys such as `7`, `9`, `41`, and `60`.
|
||||
|
||||
So the old Regret map-`3` egg-`102` gap is now closed: it is a same-map Regret `shape:400` elevator destination, not an unresolved `shape:542`/`shape:307` case and not a `0x04B1` helper lane.
|
||||
|
||||
## Adjacent Editor Objects: `0x04E3` And `0x04B1`
|
||||
|
||||
|
|
@ -601,9 +604,9 @@ Current best read:
|
|||
- `0x0366` frame `0` is an idle NPC-only trigger pad
|
||||
- `QLo` is the author-selected NPC-group key, but the executable compares it against actor field `0x63`, not against the scene-export `npcNum` or a simple DTABLE row
|
||||
- current scene exports do not expose that actor-side field directly, so there is not yet enough evidence to draw trustworthy `NPC_ONLY -> actor` arrows in the renderer
|
||||
- a follow-up scene-cache sweep also failed to produce a convincing generic `NPC_ONLY -> 0x04B1` helper pattern; shared-`QLo` local matches look incidental rather than like a dedicated authored link lane
|
||||
- a decompressed `.cache` scene sweep does show repeated nearby same-`QLo` `0x04B1` cmd helpers in authored Regret maps, so the renderer can now safely expose cautious `NPC_ONLY -> cmd QLo` arrows while still keeping `NPC_ONLY -> actor` arrows out of the overlay
|
||||
|
||||
That means the safe editor improvement here is naming and field emphasis, not a target-arrow rule yet.
|
||||
That means the safe editor improvement here is `NPC_ONLY` naming plus local `cmd QLo` arrows only; the actor-target lane still remains tooltip-only.
|
||||
|
||||
### `0x0403` frame `0`: `FLAMEBOX`
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ The implementation uses extracted `class_event_index.tsv` results plus existing
|
|||
| `MONITNS` (`0x0102`) | `MONITNS::use` (`slot 0x01`) | Existing gameplay notes tie shape `258` / `0x0102` to a live monitor/computer-adjacent use handler, making it a strong non-editor first-view script target. |
|
||||
| `MONITEW` (`0x0165`) | `MONITEW::use` (`slot 0x01`) | Disasm crosswalks shape `0x0165` to the east-west monitor variant, which keeps the same live computer-adjacent use handler family. |
|
||||
| `PANELNS` (`0x00A1`) | `PANELNS::use` (`slot 0x01`) | Verified panel-switch wrapper for the same nearby trigger-helper chain. |
|
||||
| `CRUMORPH` (`0x0318`) | `CRUMORPH::equip` (`slot 0x0A`) | Recovered control-transfer pad body scans nearby NPCs for a local-`QLo` control key match, temporarily hands control to the first live hit, and then dispatches `TRIGGER.slot_20` lane `0` or `1`. |
|
||||
| `NPCTRIG` (`0x0363`) | `NPCTRIG::equip` (`slot 0x0A`) | Crosswalked shape/class match; the compact slot-`0x0A` body is still the strongest active-event frontier for this trigger family. |
|
||||
| `CRUZTRIG` (`0x0365`) | `CRUZTRIG::gotHit` (`slot 0x06`) | Disasm crosswalks shape `0x0365` to CRUZTRIG, and `gotHit` is the recovered live body for this trigger/helper family. |
|
||||
| `VMAIL` (`0x0367`) | `VMAIL::slot_0a` (`slot 0x0A`) | Disasm crosswalks shape `0x0367` to VMAIL; slot `0x0A` is the active helper body even though its final semantic label is still weaker than the slot number. |
|
||||
|
|
@ -27,7 +28,7 @@ The implementation uses extracted `class_event_index.tsv` results plus existing
|
|||
| `TIMER` (`0x04C9`) | `TIMER::enterFastArea` (`slot 0x0F`) | Fast-area timer helper; the first active body arms slot `0x20` from qHi enter/leave flags and the packed `mapNum:npcNum` delay payload. |
|
||||
| `SPECIAL` (`0x04CA`) | `SPECIAL::enterFastArea` (`slot 0x0F`) | Fast-area phase helper; the active entry body reads `mapNum` / `npcNum` as phase bytes and `qHi` as the delay byte before fanning out through `TRIGGER.slot_20` and `SPECIAL.slot_21`. |
|
||||
| `TRIGPAD` (`0x04CD`) | `TRIGPAD::gotHit` (`slot 0x06`) | Occupancy/surface-gated trigger-pad logic lives in the recovered `gotHit` body. |
|
||||
| `NPC_ONLY` (`0x0366`) | `NPC_ONLY::gotHit` (`slot 0x06`) | Active hit-driven helper lane from the extracted class/event table. |
|
||||
| `NPC_ONLY` (`0x0366`) | `NPC_ONLY::gotHit` (`slot 0x06`) | Active hit-driven helper lane; the body gates on an NPC-only actor key, then brackets `TRIGGER.slot_20` lane `0` / `1` from the pad itself. |
|
||||
| `FLAMEBOX` (`0x0403`) | `FLAMEBOX::equip` (`slot 0x0A`) | Recovered flame-controller body scans nearby flame helper shapes by shared `QLo` and can swap helper markers into live flame actors. |
|
||||
| `SFXTRIG` (`0x04E2`) | `SFXTRIG::slot_0a` (`slot 0x0A`) | Disasm crosswalks shape `0x04E2` to the compact event-bearing SFXTRIG helper; slot `0x0A` is the stable active body even though a precise semantic label is still weaker than the slot number. |
|
||||
| `DEATHBOX` (`0x04E7`) | `DEATHBOX::slot_0a` (`slot 0x0A`) | The recovered helper body matches death-link `QLo` and forwards NPC death events into `TRIGGER` lanes, so opening the helper body is now more useful than leaving the shape unmapped. |
|
||||
|
|
@ -52,8 +53,26 @@ That is why the viewer opens `TRIGGER.slot_20` for pinned `0x04B1` helpers inste
|
|||
- Pinned controller objects and the small set of promoted gameplay objects now expose a `USECODE` action in the tooltip.
|
||||
- The action switches the workspace to the USECODE tab.
|
||||
- The USECODE viewer resolves the exact class/slot target against the generated cache index instead of relying on fuzzy filename search.
|
||||
- `CRUMORPH` and `NPC_ONLY` now also participate in the same cautious local `... -> cmd QLo ...` overlay rule used for other `TRIGGER.slot_20` controller families, but only for nearby `0x04B1` helpers that actually share the source object's low `quality` byte.
|
||||
- `0x0011` usecode-trigger eggs now decode their `npcNum` nibble-packed X/Y ranges, resolve `QLo` into the authored family-4 class, open the matching subtype body in the USECODE tab, and draw arrows only for the narrower subtype families whose local target scans are actually recovered.
|
||||
|
||||
## Actor-Key Family Blocker
|
||||
|
||||
- The current static scene/cache export still cannot support trustworthy `controller -> actor` arrows for the Regret actor-key family.
|
||||
- The strongest current reason is that the compared value is mutable actor field `0x63`, not a stable DTABLE row or an already-exported scene field.
|
||||
- A direct Regret DTABLE byte check on record offset `0x63` is not enough to close that gap: sampled rows are still zero there, so the actor key is not just a plain `NPCDat` byte copied into the runtime actor.
|
||||
- The same recovered corpus shows why the value is unstable: `TRIGGER.slot_29` / `slot_2B` can rewrite actor field `0x63` on nearby matched NPCs, which means the practical link id can change after the map loads.
|
||||
- Current safest viewer stance is therefore: keep actor-key families named and tooltip-decoded, allow only the already-evidenced local helper arrows, and leave actor-target arrows disabled until a runtime or spawn-time export closes field `0x63` directly.
|
||||
|
||||
### Known Actor-Key Families
|
||||
|
||||
- `CRUMORPH` (`0x0318`) compares nearby actor field `0x63` against the pad `QLo` before transferring control and bracketing `TRIGGER.slot_20`.
|
||||
- `NPC_ONLY` (`0x0366`) compares the incoming NPC-like source's actor field `0x63` against the pad `QLo` before bracketing `TRIGGER.slot_20` lane `0` / `1`.
|
||||
- `WATCHNS` / `WATCHEW` (`0x04c6` / `0x04de`) have a stronger current local `0x0510` post lane in the viewer, but their deeper watcher body also checks nearby actor field `0x63` against controller `QLo`.
|
||||
- `THRMBCKN` / `THRMBCKE` (`0x0566` / `0x0567` classes) compare nearby Thermatron actor field `0x63` against controller `QLo`.
|
||||
- `SURCAMNS` / `SURCAMEW` also scan nearby NPCs by actor field `0x63` and controller `QLo` in their camera/control lane.
|
||||
- `TRIGGER.slot_29` / `slot_2B` are part of the same ecosystem because one subcommand explicitly rewrites actor field `0x63` on matched nearby NPCs.
|
||||
|
||||
`0x04F8` remains intentionally outside the `USECODE` target list for now. The current evidence says it is a destroyable-door helper scanned by `DOOR.slot_23`, not a proven standalone usecode class the viewer should open directly.
|
||||
|
||||
## Newly Decoded Field Notes
|
||||
|
|
@ -157,14 +176,24 @@ No currently unresolved Remorse-only editor rows remain in this note after the `
|
|||
|---|---|---|
|
||||
| `0x00cf` | `HAND` | Needs examination for usecode-link integration |
|
||||
| `0x01d6` | `MUTANT_HOOK_CONTROL` | Needs examination for usecode-link integration |
|
||||
| `0x0451` | `GIMP_DISPENSER` | Needs examination for usecode-link integration |
|
||||
| `0x0510` | `SECRET_DOOR_POST` | Needs examination for usecode-link integration |
|
||||
| `0x0451` | `CRAZYEW` | Integrated: `CRAZYEW::gotHit` as a Regret-only NPC wake-up relay; tooltip now treats it as a hit-driven controller rather than a generic editor placeholder. |
|
||||
| `0x0510` | `SECRET_DOOR_POST` | Integrated as a local arrow target for nearby `WATCHNS` / `WATCHEW` controllers that match it by `QLo`; no separate direct usecode body promoted yet. |
|
||||
| `0x0548` | `SECRET_DOOR_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x056d` | `STEAM_COLLISION_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x05ae` | `VOLCANO_CONTROLLER` | Needs examination for usecode-link integration |
|
||||
| `0x05df` | `PRESSURE_BARRIER_V` | Needs examination for usecode-link integration |
|
||||
| `0x05e0` | `PRESSURE_BARRIER_H` | Needs examination for usecode-link integration |
|
||||
| `0x05e1` | `PRESSURE_BARRIER_SWITCH` | Needs examination for usecode-link integration |
|
||||
| `0x056d` | `VIDEOBOX` | Integrated: `VIDEOBOX::equip` as the recovered Regret-only gated controller body. |
|
||||
| `0x05ae` | `CRAZYNS` | Integrated: `CRAZYNS::gotHit` as a Regret-only NPC wake-up relay; tooltip now treats it as a hit-driven controller rather than a generic editor placeholder. |
|
||||
| `0x05df` | `PRESSURE_BARRIER_V` | Integrated as a local arrow target for nearby `CRYOBOX` controllers that match it by `QLo`. |
|
||||
| `0x05e0` | `PRESSURE_BARRIER_H` | Integrated as a local arrow target for nearby `CRYOBOX` controllers that match it by `QLo`. |
|
||||
| `0x05e1` | `CRYOBOX` | Integrated: `CRYOBOX::equip` plus local `QLo` arrows to nearby `0x05DF` / `0x05E0` pressure-barrier faces. |
|
||||
|
||||
### Regret-Only Batch: Watchers, Cryobox, And Wake-Up Relays
|
||||
|
||||
- `0x04c6` and `0x04de` are no longer anonymous shared editor rows in Regret scenes. The recovered corpus names them `WATCHNS` and `WATCHEW`, and both `slot_20` bodies scan nearby `shape=0x0510` placements before bracketing `TRIGGER.slot_20` around their watcher-specific follow-up lane.
|
||||
- The scene-cache cross-check supports a cautious viewer arrow rule here. Across Regret maps `1`, `10`, `13`, `14`, `15`, `16`, `18`, `200`, `201`, `215`, `29`, `30`, and others, placed `WATCHNS` / `WATCHEW` objects repeatedly sit within local helper range of `0x0510` posts and share the same low quality byte even when the raw 16-bit quality differs.
|
||||
- `0x0510` therefore belongs in the editor as a local secret-door post/helper target, not as an unresolved generic editor placeholder. The recovered watcher body only treats `qHi == 0` posts as the text/door-side lane, so the current viewer promotion stays conservative and only adds the local arrow plus tooltip decoding.
|
||||
- `0x05e1` is now closed as `CRYOBOX`, not a vague pressure-barrier switch. Its `equip` body matches nearby `0x05DF` and `0x05E0` shapes by shared `QLo`, then hands off into `slot_20` / `slot_21` worker lanes that wait on animation state, flip `ITEM` control slots, and spawn the steam worker path.
|
||||
- The paired faces `0x05DF` and `0x05E0` remain useful human-facing labels as `PRESSURE_BARRIER_V` and `PRESSURE_BARRIER_H`, but they no longer belong in the unresolved bucket. In the viewer they are now arrow targets of nearby `CRYOBOX` controllers instead of unlabeled editor debris.
|
||||
- `0x0451` and `0x05AE` are now closed as `CRAZYEW` and `CRAZYNS`. The recovered `gotHit` bodies are small but concrete: when the incoming hit source is an actor handle (`>= 0x00FF`), they check `NPC.slot_2A` and, unless the target is already in activity `12`, spawn `NPC.slot_2C` to wake or re-arm that actor. That is enough to classify them as hit-driven NPC wake-up relays rather than dispensers or volcano-only map art.
|
||||
- `0x056D` is also no longer an unresolved `STEAM_COLLISION_SWITCH`. The recovered class is `VIDEOBOX`, and its `equip` body is a thin global-latch gate that either falls straight into `ITEM.slot_21` or runs a short scripted helper loop first. That is enough for a direct usecode-view target even though the higher-level gameplay meaning is still thinner than the `WATCH*` and `CRYOBOX` lanes.
|
||||
|
||||
## Remaining Steps
|
||||
|
||||
|
|
@ -173,14 +202,14 @@ The next map-viewer USECODE passes should stay evidence-backed and prioritize it
|
|||
### Highest Priority
|
||||
|
||||
1. Extend the `0x0011` subtype table beyond the currently promoted `TRIGEGG` / `ONCEEGG`, `FLOOR1`, `MHATCHER`, `DOOREGG`, `MISS1*`, and `VIDEOEGG` lanes only when the recovered pseudocode justifies a reusable viewer target or arrow rule.
|
||||
2. Do the `SURCAMNS` / `SURCAMEW` placement crosswalk so the renderer can decide whether placed `0x04c6` / `0x04de` objects deserve a direct USECODE jump or should remain callback-holder-only families.
|
||||
2. Revisit the remaining Regret-only door-side helpers around `WATCHNS` / `WATCHEW`, especially `0x0548`, to decide whether they form a second stable secret-door lane beyond the now-promoted `0x0510` post targets.
|
||||
3. Finish the remaining `CMD_LINK` field write-up: the current tooltip now decodes `quality`, `mapNum`, and `npcNum`, but `nextItem` still lacks a stable standalone semantic beyond appearing in authored controller records.
|
||||
|
||||
### Catalog And Viewer Cleanup
|
||||
|
||||
1. Sweep the remaining shared editor/controller shapes in the catalog table and promote the next solid names instead of leaving `(unnamed)` placeholders where the disasm or extracted corpus already gives a stable class anchor.
|
||||
2. Revisit the `0x05b2`-`0x05be` music-controller cluster and decide whether those shapes belong in the USECODE viewer backlog, a scene-audio note, or both.
|
||||
3. Recheck the Regret-only controller shapes (`HAND`, `MUTANT_HOOK_CONTROL`, `GIMP_DISPENSER`, `SECRET_DOOR_*`, `STEAM_COLLISION_SWITCH`, `VOLCANO_CONTROLLER`, `PRESSURE_BARRIER_*`) against the extracted class/event table before adding any direct links.
|
||||
3. Recheck the remaining unresolved Regret-only controller shapes (`HAND`, `MUTANT_HOOK_CONTROL`, `SECRET_DOOR_SWITCH`) against the extracted class/event table before adding any direct links.
|
||||
|
||||
### Gameplay Coverage Extensions
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ The broader Remorse catalog does contain more unused-looking or obviously non-re
|
|||
|
||||
### Placeholder cube family
|
||||
|
||||
- The strongest additional item-adjacent candidates from the catalog/scene pass are placeholder cube entries:
|
||||
- `0x0251` = `PLACEHOLDER_KEY_CUBE`
|
||||
- `0x0251` no longer fits the generic placeholder bucket well. Current best read is `VALUEBOX`, a local data/helper box used by monitors, watcher panels, security displays, and keypads.
|
||||
- The strongest remaining placeholder-cube entries from the catalog/scene pass are:
|
||||
- `0x0318` = `PLACEHOLDER_CUBE`
|
||||
- `0x0337` = `PLACEHOLDER_CUBE_BIG`
|
||||
- `0x0361` = `PLACEHOLDER_CUBE_RED_BLACK`
|
||||
|
|
|
|||
|
|
@ -26,6 +26,76 @@ Purpose:
|
|||
| Constructor writes `0x65ab` to object `+0` | `1408:0024..0028` |
|
||||
| `1478:65ab` is method 0 and `1478:65af` is method 1 of the same vtable | `CALLF [BX]` and `CALLF [BX+4]` dispatch paths |
|
||||
|
||||
## Minimum Viable Patch Floor (2026-04-03)
|
||||
|
||||
Fresh live-Ghidra re-checks tighten the lower bound on what an executable-side debugger-unlock patch must do.
|
||||
|
||||
What the live binary still proves:
|
||||
- `1408:0000` is only a constructor. It allocates/initializes the seg1408 debugger object, seeds object `+0` with `0x65ab`, and returns the far pointer in `DX:AX`; it does **not** store that pointer into `1478:659c/659e`.
|
||||
- current instruction searches still show reads of `1478:659c/659e` in seg13a0 and seg1418, but no recovered retail writer that seeds those globals before the interpreter hook runs.
|
||||
- raw debugger-adjacent data at `1478:65ab/65af` still resolves to the two inert retail callbacks `1408:046f` and `1408:0474`; there is no hidden already-live UI-opening target sitting behind the stock vtable.
|
||||
- the interpreter-side pre-call guard at `1418:049e..04b5` only checks whether `1478:659c/659e` is non-null before calling `1408:0053`. The one-shot breakpoint/step gating lives inside `1408:0053`, not at the callsite itself.
|
||||
- both UI wrappers still inherit caller words at `13a0:008f` / `13a0:024a`, so any deferred entry that reuses those wrappers from a non-native caller still needs wrapper-argument sanitization.
|
||||
|
||||
That rules out the common "there must be one tiny direct jump" theories:
|
||||
- a constructor-only retarget is insufficient because the returned far pointer still has to be stored into `1478:659c/659e` somewhere.
|
||||
- a vtable-dword-only patch is insufficient because the retail callbacks at `1478:65ab/65af` are inert shared stubs, and the previously tested shared-slot rewrites already crashed at startup.
|
||||
- a direct `1418:04b5 -> 13a0:020d/0086` retarget is insufficient because once `1478:659c/659e` becomes non-null, that callsite would fire on every eligible interpreter pass unless a private one-shot stub preserves the original `1408:0053` gating semantics.
|
||||
|
||||
Current best conclusion:
|
||||
- the smallest structurally defensible patch family is still the current interpreter-callsite-retarget design (Candidates O/P): one embedded `13e8:230d..232d` body that lazily creates/stores/arms the debugger object, one retarget of `1418:04b5` into the private stub, and one wrapper-argument sanitization site (`13a0:024a` or `13a0:008f`).
|
||||
- anything smaller than that is currently missing at least one required behavior: object creation, global pointer seeding, one-shot deferred gating, or safe wrapper arguments.
|
||||
|
||||
## Practical Candidate Byte Maps
|
||||
|
||||
These are the current live-candidate edits in practical patching terms.
|
||||
|
||||
Important NE note:
|
||||
- for internal far calls, the on-disk opcode bytes stay as placeholder `FF FF 00 00`
|
||||
- the real old/new target change is in the NE fixup entry, not in the immediate bytes themselves
|
||||
- so for those sites below, `raw bytes` may be unchanged while the `fixup target` changes
|
||||
|
||||
### Common edits shared by Candidate O and Candidate P
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C970D` | `13e8:230d` | Replace the retail `0x410` body with the corrected private bootstrap/stub body | `A0 4F 60 B4 00 F7 D8 1B C0 40 A2 4F 60 80 3E 4F 60 00 74 47 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 D2 60 6A 00 6A 00 83 EC 06 C7 86 76 FF 00 00 8B 86 76 FF F7 D0 89 86 78 FF C6 86 7A FF 00 6A 00 6A 00 9A FF FF 00 00 83 C4 14 52 50 9A FF FF 00 00 83 C4 08 5F 5E C9 CB 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 EE 60 6A 00 6A 00 83 EC 06 C7` | `A1 9C 65 0B 06 9E 65 74 10 C4 1E 9C 65 C6 47 75 00 C6 47 74 01 5F 5E C9 CB 6A 00 6A 00 E9 25 00 55 8B EC A1 9C 65 39 46 06 75 16 A1 9E 65 39 46 08 75 0E C4 5E 06 C7 47 74 00 00 6A 00 6A 00 EB 0E 5D CB 90 90 9A FF FF 00 00 83 C4 04 EB 0A 9A FF FF 00 00 83 C4 04 5D CB 0B C2 74 13 A3 9C 65 89 16 9E 65 89 C3 8E C2 C6 47 75 00 C6 47 74 01 5F 5E C9 CB` |
|
||||
| `0x0C9753` | `13e8:2352` | First reused far-call fixup inside the patched `0x410` body | fixup target `1350:0046` | fixup target `1408:0000` |
|
||||
| `0x0CFAB5` | `1418:04b5` | Interpreter debugger callsite retarget | raw bytes `9A FF FF 00 00`; fixup target `1408:0053` | raw bytes `9A FF FF 00 00`; fixup target `13e8:232d` |
|
||||
|
||||
### Candidate O only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:020d` |
|
||||
| `0x0B9C48` | `13a0:0248` | Zero inherited modal-wrapper caller words while preserving the leading local default `PUSH 0` | `6A 00 FF 76 08 FF 76 06` | `6A 00 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- the patched `0x410` body now creates/stores the debugger object if needed, or reuses the live one and arms break-next
|
||||
- the interpreter callback at `1418:04b5` no longer enters `1408:0053` directly; it enters the private stub at `13e8:232d`
|
||||
- that private stub eventually uses the repointed second far-call slot at `13e8:235c` to open `usecode_debugger_open_modal`
|
||||
|
||||
### Candidate P only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:0086` |
|
||||
| `0x0B9A8D` | `13a0:008d` | Zero inherited current-unit-wrapper caller words while preserving the leading mode byte `PUSH 1` | `6A 01 FF 76 08 FF 76 06` | `6A 01 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- same bootstrap and interpreter-callsite retarget as Candidate O
|
||||
- the only behavioral difference is the final UI target: this one routes to `usecode_debugger_open_for_current_unit` instead of the generic modal wrapper
|
||||
|
||||
### Restore values for the current live-candidate family
|
||||
|
||||
If reverting O/P back to retail, these are the practical targets:
|
||||
- `0x0C970D` / `13e8:230d`: restore the original retail `0x410` body bytes shown above
|
||||
- `0x0C9753` / `13e8:2352`: restore fixup target `1350:0046`
|
||||
- `0x0CFAB5` / `1418:04b5`: restore fixup target `1408:0053`
|
||||
- `0x0C975D` / `13e8:235c`: restore fixup target `1320:1588`
|
||||
- `0x0B9C48` / `13a0:0248`: restore `6A 00 FF 76 08 FF 76 06` if Candidate O was active
|
||||
- `0x0B9A8D` / `13a0:008d`: restore `6A 01 FF 76 08 FF 76 06` if Candidate P was active
|
||||
|
||||
## Attempt Log
|
||||
|
||||
| ID | Patch shape | Mechanical result | Runtime result | Verdict |
|
||||
|
|
|
|||
|
|
@ -842,4 +842,12 @@ That gets to a reversible editor sooner than waiting for a full semantic VM reco
|
|||
- **Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now recognizes that stacked-shape whitelist selector and emits readable loops such as `for roof in nearby_items(shapes=[...], distance=(100 * 32), origin=arg_06)` instead of collapsing back to `while (condition)`.
|
||||
- **Readability Impact:**: The cached Remorse and Regret `CHANGER.slot_07` pseudocode bodies now expose the actual nearby-roof selector inputs directly: the hardcoded roof-shape whitelist, the recovered `3200`-unit range, and the egg-origin scan. That makes the later `Item.getQLo(...) == eggId` destroy branch legible without a raw-byte fallback pass.
|
||||
- **Editor Impact:**: The same selector close justified promoting Regret `QLo 8 -> CHANGER` from tooltip-only metadata into the map editor overlay. The viewer can now expose the same local roof-target lane for Regret that was already proven for Remorse, using the recovered Regret whitelist and the same `3200`-unit scan distance.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one synthetic regression for the `loopscr 0x24/0x4c` stacked-shape selector and one real-data regression for Regret `CHANGER.slot_07`, so future renderer changes fail if this selector family falls back to opaque loop output again.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one synthetic regression for the `loopscr 0x24/0x4c` stacked-shape selector and one real-data regression for Regret `CHANGER.slot_07`, so future renderer changes fail if this selector family falls back to opaque loop output again.
|
||||
|
||||
## **Recent Renderer Work (2026-04-03, switch literal normalization)**
|
||||
|
||||
- **Root Cause Closed:**: `FLICTEST.slot_20` exposed a literal-formatting seam in the JS renderer. The compare ladder crosses the byte/word immediate boundary at `127 -> 128`, so the decompiler was structuring the ladder into one `switch` but was preserving the raw operand spelling from each compare. That produced mixed case labels such as `case 127:` followed by `case 0x0080:` and `case 0x0081:` even though the selector is one small ordinal lane.
|
||||
- **Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now normalizes switch case labels after selector-chain recovery. When every case label is a non-negative integer in the small ordinal range (`0..255`), the renderer canonicalizes the labels to decimal before emitting the final `switch`.
|
||||
- **Heuristic Boundary:**: The normalization is intentionally narrow. It applies only to structured `switch` output, and only when every case label parses as a small ordinal integer. Larger identifier-like constants such as shape ids (`0x053a`) are left in hex so the renderer does not erase domain-significant formatting.
|
||||
- **Readability Impact:**: FLICTEST-style keypad/movie dispatch ladders now render as consistent ordinal switches across the byte/word immediate boundary, so cases `128+` appear as `128`, `129`, and so on rather than width-leaking `0x0080`, `0x0081`, etc.
|
||||
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now includes one synthetic regression for the mixed `127/0x0080/0x0081` selector ladder and one guard that keeps larger ID-style switch cases in hex.
|
||||
|
|
@ -53,6 +53,145 @@ The remaining uncertainty is narrower now:
|
|||
- whether it can also be a loader alias/resource root that `Filespec_GetFullPath` understands
|
||||
- whether relative, absolute, and CD-backed forms are all accepted equally
|
||||
|
||||
What is now materially tighter from the live `Filespec_GetFullPath` and `File_Exists` implementations:
|
||||
|
||||
- the path is interpreted with DOS file APIs, not Windows host-path APIs
|
||||
- the builder uses the literal separator string `"\\"`, not `"/"`
|
||||
- the parser-side `s_usecode` buffer is only `0x1e` bytes long, so the raw `-u` token is truncated to at most `29` visible characters plus the terminator
|
||||
|
||||
That makes the safest current syntax guidance:
|
||||
|
||||
- prefer DOS-style backslashes, not forward slashes
|
||||
- prefer short path tokens
|
||||
- assume the path must exist inside the game's DOS-visible filesystem view
|
||||
|
||||
If the game is running under DOSBox or a similar DOS environment, `C:\MADDOCODE` refers to the guest DOS `C:` drive, not automatically to the Windows host `C:` drive.
|
||||
|
||||
One more practical constraint is now strong enough to call out explicitly:
|
||||
|
||||
- the `-u` existence probe uses an old DOS find-first style path, so `8.3`-safe directory names are the safest override roots
|
||||
|
||||
For the current GOG install used in this investigation, Windows reports the short alias:
|
||||
|
||||
- `MADDOCODE` -> `MADDOC~1`
|
||||
|
||||
So the highest-probability next token for this specific setup is:
|
||||
|
||||
- `-u MADDOC~1`
|
||||
|
||||
or the explicit relative form:
|
||||
|
||||
- `-u .\MADDOC~1`
|
||||
|
||||
## Why `-u FLICTEST.FLX` Fails
|
||||
|
||||
The live helper shape is now tight enough to explain the failed `FLICTEST` attempts directly.
|
||||
|
||||
What retail `CRUSADER.EXE` does at `1420:0cf0..0d33` is:
|
||||
|
||||
- read the copied argv token from `1478:065a`
|
||||
- load the fixed filename template through `1478:06d6/06d8 -> 1478:07a0`
|
||||
- use that template as `eusecode.flx`
|
||||
- call `Filespec_GetFullPath(0, <copied token>, "eusecode.flx", 0)`
|
||||
|
||||
The raw data bytes at `1478:079a` confirm the fixed template directly:
|
||||
|
||||
- `65 75 73 65 63 6f 64 65 2e 66 6c 78 00`
|
||||
- ASCII = `eusecode.flx\0`
|
||||
|
||||
So these example invocations do **not** mean `load this exact archive file`:
|
||||
|
||||
- `-u USECODE/FLICTEST.FLX`
|
||||
- `-u FLICTEST.FLX`
|
||||
|
||||
They instead mean, in the current safest static interpretation:
|
||||
|
||||
- use `USECODE/FLICTEST.FLX` as the `path` component and still append the fixed filename `eusecode.flx`
|
||||
- or use `FLICTEST.FLX` as the `path` component and still append the fixed filename `eusecode.flx`
|
||||
|
||||
That makes both attempts wrong for a direct single-file override. The game is not being told to open your `FLICTEST.FLX` archive as the final filename at all.
|
||||
|
||||
## Safest Current Experiment Shape
|
||||
|
||||
The current safest static-model experiment is therefore:
|
||||
|
||||
1. create a directory that will act as the override root
|
||||
2. place a structurally valid replacement archive in that directory
|
||||
3. name that archive `EUSECODE.FLX`
|
||||
4. pass the directory path to `-u`, not the archive filename
|
||||
|
||||
Examples that now fit the recovered helper better are:
|
||||
|
||||
- `-u USECODE`
|
||||
- `-u .\\USECODE`
|
||||
- `-u \USECODE`
|
||||
- `-u C:\USECODE`
|
||||
- `-u MOD_UC`
|
||||
|
||||
with the important condition that the target directory must contain `EUSECODE.FLX`, not `FLICTEST.FLX`.
|
||||
|
||||
The safest current path interpretations for those examples are:
|
||||
|
||||
- `-u MADDOCODE` -> tries `MADDOCODE\EUSECODE.FLX` relative to the game's current DOS working directory
|
||||
- `-u .\MADDOCODE` -> tries `.\MADDOCODE\EUSECODE.FLX`
|
||||
- `-u \MADDOCODE` -> tries `\MADDOCODE\EUSECODE.FLX` on the current DOS drive
|
||||
- `-u C:\MADDOCODE` -> tries `C:\MADDOCODE\EUSECODE.FLX` on DOS drive `C:`
|
||||
|
||||
The current safest negative guidance is:
|
||||
|
||||
- do not use forward slashes like `USECODE/FLICTEST.FLX`
|
||||
- do not pass the archive filename itself as the `-u` token
|
||||
- do not assume a long absolute host path will survive the `0x1e`-byte parser buffer intact
|
||||
- do not assume a directory name longer than classic `8.3` DOS spelling will be accepted exactly as typed
|
||||
|
||||
## Expected Visible Output
|
||||
|
||||
There is currently no evidence that retail `-u` prints a success or failure banner before the game loads.
|
||||
|
||||
What the live code shows:
|
||||
|
||||
- the `-u` parser case at `1048:0a46` only copies the token into `1478:065a`
|
||||
- unlike `-debug`, `-warp`, `-skill`, `-mapoff`, `-egg`, and `-demo`, that parser case does **not** call `ConsolePrintf`
|
||||
- `startup_apply_u_override_if_present` also does not call `ConsolePrintf`
|
||||
- if `File_Exists` says the constructed path is missing, the helper just returns and startup continues
|
||||
|
||||
So the safest current user-facing expectation is:
|
||||
|
||||
- no dedicated `-u` console line on success
|
||||
- no dedicated `-u` console line on file-not-found failure
|
||||
- if the override path is wrong, the game most likely falls back silently to stock usecode
|
||||
|
||||
That means `no message appeared before the game loaded` is currently normal and does **not** prove the override worked.
|
||||
|
||||
## Strongest Current Diagnosis For The `MADDOCODE` Test Case
|
||||
|
||||
The current install-side evidence makes this case much tighter than a generic `-u` experiment.
|
||||
|
||||
What is now verified in the GOG install:
|
||||
|
||||
- `MADDOCODE` contains `EUSECODE.FLX`, `OVERLOAD.DAT`, `UNKCOFF.DAT`, and `UNKDS.DAT`
|
||||
- `OVERLOAD.DAT`, `UNKCOFF.DAT`, and `UNKDS.DAT` in `MADDOCODE` are byte-identical to the working copies in `USECODE`
|
||||
- only `EUSECODE.FLX` differs between the stock `USECODE` folder and `MADDOCODE`
|
||||
|
||||
That makes the A/B interpretation unusually clean.
|
||||
|
||||
If:
|
||||
|
||||
- replacing `USECODE\EUSECODE.FLX` with the hacked file produces the expected obvious gameplay breakage
|
||||
- but launching with `-u MADDOCODE` produces stock gameplay instead
|
||||
|
||||
the strongest current read is **not** `the alternate root loaded but behaved differently`. The stronger current read is:
|
||||
|
||||
- the alternate root was probably never selected
|
||||
- startup most likely fell back silently to stock usecode after the override path probe failed
|
||||
|
||||
Given the recovered code and the current install layout, the leading practical failure mode is now:
|
||||
|
||||
- the DOS-side file probe did not accept `MADDOCODE` exactly as typed
|
||||
- the short alias `MADDOC~1` is the next highest-probability working token
|
||||
|
||||
This is still a static-analysis conclusion rather than a runtime-tested acceptance matrix, so the exact relative-path and alias forms remain open. But the filename side is no longer the main uncertainty: the recovered helper is strongly telling us `directory/root override for EUSECODE.FLX`, not `arbitrary filename override`.
|
||||
|
||||
External engine code reinforces the fixed-filename part of this read. Both Pentagram and ScummVM build the default Crusader main usecode path as `usecode/` + language letter + `usecode.flx`, and for Remorse/Regret the language-usecode letter is `'e'`, which matches the retail helper's forced `eusecode.flx` template.
|
||||
|
||||
## Why This Looks Like Replacement, Not Addition
|
||||
|
|
@ -284,6 +423,8 @@ For current tooling planning, the defensive assumption should be:
|
|||
- prepare a structurally complete replacement source that satisfies the loader's normal expectations
|
||||
- do not assume the game will merge one partial class file into the stock runtime for us
|
||||
|
||||
That also means a standalone archive like `FLICTEST.FLX` is currently a poor first experiment even if its internals are valid. The live retail loader path is much more likely to accept a complete replacement `EUSECODE.FLX` rooted under a directory passed to `-u`.
|
||||
|
||||
## Best Current Usefulness For Decompilation Work
|
||||
|
||||
This path is interesting for the current usecode-decompilation lane for two reasons.
|
||||
|
|
|
|||
402
docs/usecode/flictest-investigation.md
Normal file
402
docs/usecode/flictest-investigation.md
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
# FLICTEST usecode investigation
|
||||
|
||||
## Verdict
|
||||
|
||||
`FLICTEST` is real shipped usecode, and it is absolutely movie-related.
|
||||
|
||||
The strongest current reading is:
|
||||
|
||||
- `FLICTEST` is a movie playback helper/jukebox class, not a normal authored world-object class that maps place directly on maps.
|
||||
- It is reachable through other shipped usecode classes.
|
||||
- Reachability differs by game: Remorse keeps a much larger movie-browser helper, while Regret keeps a smaller helper and uses it through a handful of specific caller scripts.
|
||||
- In Regret specifically, the strongest surviving route is not just a generic helper call. It sits behind a hidden `KEYPADNS` menu path that explicitly exposes both a `Cheaters Menu` branch and a `VIDEO PLAYER` branch.
|
||||
|
||||
I did not find evidence that any placed map item or egg resolves directly to the `FLICTEST` class itself. The evidence instead points to `FLICTEST` being spawned as a helper from other classes.
|
||||
|
||||
## Core class evidence
|
||||
|
||||
Remorse extracted class metadata:
|
||||
|
||||
- `USECODE/EUSECODE_extracted/class_event_index.tsv`
|
||||
- `entry_index = 402`, `object_index = 0xA22`, `class_id = 0x0A20`, `class_name_hint = FLICTEST`
|
||||
- only slots `0x20` and `0x21` are populated
|
||||
- slot `0x20`: body `0x00E0..0x279F`, length `9919`
|
||||
- slot `0x21`: body `0x279F..0x4E4C`, length `9901`
|
||||
|
||||
Regret extracted class metadata:
|
||||
|
||||
- `USECODE/REGRET/REGRET_USECODE_extracted/class_event_index.tsv`
|
||||
- `entry_index = 456`, `object_index = 0xA0C`, `class_id = 0x0A0A`, `class_name_hint = FLICTEST`
|
||||
- only slots `0x20` and `0x21` are populated
|
||||
- slot `0x20`: body `0x00E0..0x0CA4`, length `3012`
|
||||
- slot `0x21`: body `0x0CA4..0x0CAF`, length `11`
|
||||
|
||||
That already looks less like an ordinary object family and more like a dedicated helper with anonymous high-slot entry points.
|
||||
|
||||
## What the bodies do
|
||||
|
||||
### Remorse
|
||||
|
||||
`map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/FLICTEST/slot_20_slot_20.txt` is a keypad-driven movie browser.
|
||||
|
||||
- It calls `KeypadGump.showKeypad(0)`.
|
||||
- It switches on the entered number.
|
||||
- Nearly every case calls `playFlic(...)` with a short movie name such as `01a`, `04h`, `m03`, `mva01`, `o01`, `t02`, `test`, `z1`, or `wec`.
|
||||
- The helper includes special names such as `hideoutx`, many `mva*` clips, and an explicit `test` clip.
|
||||
|
||||
`map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/FLICTEST/slot_21_slot_21.txt` is a second movie dispatcher that takes the movie selector from `arg_10` instead of reading a keypad itself. In other words, Remorse ships both:
|
||||
|
||||
- an interactive movie-picker entry point in `slot_20`
|
||||
- a parameter-driven dispatcher in `slot_21`
|
||||
|
||||
That is consistent with a general movie test or movie browser utility.
|
||||
|
||||
### Regret
|
||||
|
||||
Regret still ships `FLICTEST`, but the helper is cut down.
|
||||
|
||||
- `slot_20` remains a real body.
|
||||
- `slot_21` is only:
|
||||
|
||||
```c
|
||||
function flictest_slot_21()
|
||||
{
|
||||
set_info(1, *(arg_06));
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
So Regret preserves the helper class but no longer keeps the large second dispatch body that Remorse has.
|
||||
|
||||
## Where it is used
|
||||
|
||||
### Remorse: `EVENT::equip`
|
||||
|
||||
The clearest Remorse use is inside the shipped `EVENT` controller family.
|
||||
|
||||
Evidence:
|
||||
|
||||
- `USECODE/EUSECODE_extracted/class_event_index.tsv` shows `EVENT` slot `0x0A` as the single large active body.
|
||||
- `docs/map_renderer/trigger-usecode-links.md` already treats `EVENT` as a shipped controller family and maps shape `0x0361` to `EVENT::equip`.
|
||||
- `map_renderer/Catalogs/usecode_shape_catalog_remorse.csv` names shape `0x0361` as `EVENT_CONTROLLER`.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/EVENT/slot_0A_equip.txt`:
|
||||
|
||||
```c
|
||||
case 0x00fa:
|
||||
spawn FLICTEST.slot_20(pid, flicMan);
|
||||
suspend;
|
||||
return;
|
||||
```
|
||||
|
||||
This is strong evidence that Remorse's event-controller ecosystem can launch `FLICTEST` as a helper, using an event-local `flicMan` value as the selector.
|
||||
|
||||
Current best read: Remorse uses `FLICTEST` as a generic movie-playback utility behind at least some `EVENT`-driven scripted situations.
|
||||
|
||||
### Remorse: current access status
|
||||
|
||||
The newer comparison pass makes Remorse look much less recoverable than Regret.
|
||||
|
||||
- The only confirmed Remorse `spawn FLICTEST.slot_20(...)` call found in the extracted pseudocode is the `EVENT::equip` `case 0x00fa` branch above.
|
||||
- Remorse `KEYPADNS::use` is only the standard keypad wrapper. It has no `VIDEO PLAYER` or `Cheaters Menu` string path.
|
||||
- Remorse `DATALINK::use` does not spawn `FLICTEST`. Instead it plays the campaign MVA clips directly with `playFlicsomething(...)` and then shows mission-objective text.
|
||||
|
||||
That means the current best No Remorse answer is:
|
||||
|
||||
- `FLICTEST` exists and its keypad browser body is real.
|
||||
- But I did not find a shipped player-facing keypad, monitor, or datalink route that opens that browser directly.
|
||||
- The only current workspace evidence for launching the browser in Remorse is the generic `EVENT` controller case `0x00fa`, and I do not yet have a verified placed world route for that case.
|
||||
|
||||
The latest map pass strengthens that answer with actual `KEYPADNS` placement data:
|
||||
|
||||
- In the generated Remorse scene export, the only placed `shape:1099` (`0x044b`, `KEYPADNS`) records I found are all on map 1.
|
||||
- Those three records are ordinary `frame 0` keypad objects with full quality values `267`, `832`, and `41997`.
|
||||
- Their local low bytes are therefore `0x0b`, `0x40`, and `0x0d`, not the Regret-style hidden selector values `0` / `1`.
|
||||
- Remorse `KEYPADNS::use` has no `VIDEO PLAYER` or `Cheaters Menu` branch anyway, so these map-1 placements currently read as normal gameplay keypads rather than hidden `FLICTEST` terminals.
|
||||
- A broader exported-map scan also found no placed `shape:1236` (`0x04d4`, `DATALINK`) records in the rendered Remorse map exports, which fits the usecode-side picture that the shipped datalink path is inventory/startup-facing rather than a placed world terminal route.
|
||||
- The exported `shape:865` (`0x0361`, `EVENT_CONTROLLER`) placements I found are also all on map 1, and sampled controllers are not colocated with the three `KEYPADNS` objects above.
|
||||
|
||||
So, unlike Regret, No Remorse currently looks like `movie-browser helper present in data, but no confirmed shipped user-facing access path`.
|
||||
|
||||
## Remorse datalink hack-in patch
|
||||
|
||||
The minimal reversible Remorse hack is a 2-byte `EUSECODE.FLX` edit, not an executable rewrite.
|
||||
|
||||
### Why the patch moved out of `CRUSADER.EXE`
|
||||
|
||||
The first executable-side `Item_Use` hook was functionally plausible, but it changed too many bytes in a relocation-sensitive path and the user reported that build crashed on startup.
|
||||
|
||||
That made the right bar much stricter:
|
||||
|
||||
- avoid `CRUSADER.EXE` entirely if possible
|
||||
- avoid far-call rewrites
|
||||
- change the smallest stable token already present in shipped data
|
||||
|
||||
The better target turned out to be the tail of Remorse `DATALINK::use` inside `USECODE\EUSECODE.FLX`.
|
||||
|
||||
### Why I did not use a direct class-event row swap
|
||||
|
||||
I checked the raw Remorse `EUSECODE.FLX` class-event rows directly.
|
||||
|
||||
- `DATALINK slot 0x01 use` row bytes are `B4 13 01 00 00 00`
|
||||
- `FLICTEST slot 0x20` row bytes are `BF 26 01 00 00 00`
|
||||
|
||||
Those are not global cross-class code pointers. The extracted parser and raw headers show they are class-local metadata:
|
||||
|
||||
- each row is parsed as `<u16 raw_event_entry_word, u32 raw_code_offset>`
|
||||
- the code body is resolved relative to the current class chunk
|
||||
- copying a `FLICTEST` row into `DATALINK` would still resolve inside the `DATALINK` chunk
|
||||
|
||||
So a row swap would not produce a real `DATALINK -> FLICTEST` redirect.
|
||||
|
||||
### Final patch shape
|
||||
|
||||
The validated patch site is the final spawned callee token in Remorse `DATALINK::use`.
|
||||
|
||||
Pseudocode evidence from the extracted Remorse cache:
|
||||
|
||||
```c
|
||||
spawn TEXTFILE.slot_20(pid, textFile, arg_06);
|
||||
suspend;
|
||||
```
|
||||
|
||||
Raw file evidence from the installed retail `USECODE\EUSECODE.FLX` near the end of the `DATALINK` body:
|
||||
|
||||
```text
|
||||
04 59 41 FE 40 06 57 02 02 17 0A 20 00 65 00 6E FE 5E 54 01 01 12 53 5C
|
||||
```
|
||||
|
||||
The important token is the class id in the final spawn:
|
||||
|
||||
- `17 0A` = `TEXTFILE` (`0x0A17`)
|
||||
- `20 0A` = `FLICTEST` (`0x0A20`)
|
||||
|
||||
So the effective patch is simply:
|
||||
|
||||
- file: `USECODE\EUSECODE.FLX`
|
||||
- installed retail offset resolved by signature scan: `0x1FF55`
|
||||
- retail bytes: `17 0A`
|
||||
- patched bytes: `20 0A`
|
||||
|
||||
That preserves the existing `slot_20` dispatch and only swaps the target class.
|
||||
|
||||
So the effective behavior becomes:
|
||||
|
||||
- normal Remorse datalink tail: `spawn TEXTFILE.slot_20(...)`
|
||||
- patched Remorse datalink tail: `spawn FLICTEST.slot_20(...)`
|
||||
|
||||
This exposes the shipped keypad-driven `FLICTEST` movie browser with only a 2-byte usecode-file change.
|
||||
|
||||
### Raw hex patch
|
||||
|
||||
At the resolved installed retail site:
|
||||
|
||||
```text
|
||||
Offset 0x1FF55
|
||||
Retail : 17 0A
|
||||
Patched: 20 0A
|
||||
```
|
||||
|
||||
Or with surrounding context:
|
||||
|
||||
```text
|
||||
Retail : 57 02 02 17 0A 20 00 65 00 6E FE
|
||||
Patched: 57 02 02 20 0A 20 00 65 00 6E FE
|
||||
```
|
||||
|
||||
### Patcher integration
|
||||
|
||||
I updated `Crusader-Map-Viewer/patch_crusader_map_load.ps1` so the Remorse-only movie-browser option now patches `USECODE\EUSECODE.FLX` instead of the executable.
|
||||
|
||||
- CLI enable: `-Choice flictest-enable`
|
||||
- CLI restore: `-Choice flictest-default`
|
||||
- interactive enable: `6`
|
||||
- interactive restore: `7`
|
||||
|
||||
The script resolves the site by scanning for the full DATALINK tail signature, then applies only the 2-byte token swap. It refuses to write if the current bytes are neither the expected retail bytes nor the already-enabled bytes.
|
||||
|
||||
### Current validation state
|
||||
|
||||
The rewritten PowerShell script was copied into the installed No Remorse folder and validated with `-Choice status`.
|
||||
|
||||
Current retail detection from the live install:
|
||||
|
||||
- `CRUSADER.EXE` startup selector resolved normally
|
||||
- both editor-visibility sites resolved normally
|
||||
- Remorse datalink movie-browser site resolved at `USECODE\EUSECODE.FLX` offset `0x1FF55`
|
||||
- current state reported as `Retail default`
|
||||
|
||||
So the signature-scan and offset-resolution logic for the minimal patch is now confirmed against the installed retail files.
|
||||
|
||||
### Regret: `DATALINK::use`
|
||||
|
||||
Regret has a shipped, clearly reachable `DATALINK` path into `FLICTEST`.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/DATALINK/slot_01_use.txt`:
|
||||
|
||||
```c
|
||||
else {
|
||||
spawn FLICTEST.slot_20(pid, global[0x0001], local_02);
|
||||
suspend;
|
||||
}
|
||||
```
|
||||
|
||||
This branch sits at the tail of the normal `DATALINK::use` body, after the map-specific mission/objective text handling.
|
||||
|
||||
Why this matters for reachability:
|
||||
|
||||
- `docs/scummvm-crusader-reference.md` notes that `games/start_crusader_process.cpp` seeds inventory with shape `0x4d4` `datalink` at game start.
|
||||
- That means `DATALINK` is not hypothetical leftover content. It is part of the live shipped startup flow.
|
||||
|
||||
So even if the exact branch condition still needs fuller runtime narrowing, `FLICTEST` is attached to a definitely shipped and definitely player-facing object family in Regret.
|
||||
|
||||
### Regret: `KEYPADNS::use`
|
||||
|
||||
Regret also has an even more explicit movie-browser route through `KEYPADNS`.
|
||||
|
||||
This is also where the strongest hidden/debug-style evidence lives. Unlike Remorse, whose `KEYPADNS::use` is just the normal keypad wrapper that either spawns `KEYPAD.slot_20` or forwards to `TRIGGER.slot_20`, Regret adds two special map-gated branches ahead of the ordinary keypad logic.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt`:
|
||||
|
||||
```c
|
||||
local_06 = "VIDEO PLAYER^_____________^^Mission video 0-72^Mission MVAs 73-89^Game Flicks 90-102";
|
||||
ComputerGump.readComputer(local_06);
|
||||
local_02 = KeypadGump.showKeypad(0);
|
||||
...
|
||||
spawn FLICTEST.slot_20(pid, local_02, local_04);
|
||||
```
|
||||
|
||||
This is the single strongest semantic clue in the whole pass. `KEYPADNS` literally presents a `VIDEO PLAYER` menu and then hands the user-selected number to `FLICTEST.slot_20`.
|
||||
|
||||
The same body also contains a second literal branch:
|
||||
|
||||
```c
|
||||
local_06 = "Cheaters Menu^_____________^^Select a mission^number (1-10)^and Col. Shepherd^will assign it to you.";
|
||||
```
|
||||
|
||||
That changes the interpretation materially. In Regret, `KEYPADNS` is not just a generic keypad helper that happens to call a movie routine. It preserves an overt hidden-menu path with both mission-cheat and movie-browser behavior.
|
||||
|
||||
That makes `FLICTEST` look less like a purely abandoned developer test and more like a retained hidden utility that still sits behind shipped object logic, at least for this Regret keypad/computer path.
|
||||
|
||||
#### Recoverability status
|
||||
|
||||
This is where the evidence gets better than the first pass.
|
||||
|
||||
- Remorse contrast: `map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt` has no `Cheaters Menu` or `VIDEO PLAYER` branch. It is only the standard keypad wrapper.
|
||||
- Regret demo persistence: `map_renderer/.cache/usecode/regret-demo/EUSECODE/pseudocode/KEYPADNS/slot_01_use.txt` still contains both the `Cheaters Menu` string and the `VIDEO PLAYER` string. So this was not a one-build accident unique to one retail extract.
|
||||
- Shape alignment: Regret extracted metadata gives `KEYPADNS` class id `0x044b`, and the Regret shape catalog also has a placed but unnamed base entry at `0x044b`.
|
||||
- Map placement: a structured scan of the Regret scene cache found an actual placed `shape:1099` (`0x044b`) object on map 26 at world position `(32746, 32798, 48)` with `quality = 1` and `frame = 1`.
|
||||
- Stable locator: in the viewer, that map-26 object resolves to fixed scene id `fixed:123` and runtime id `item:757:fixed:1099:1:32746:32798:48`.
|
||||
- Adjacent-state persistence: the same visible keypad also exists on Regret map 27 at the same world coordinates with the same `quality = 1` / `frame = 1` signature, so this is not unique to a single cache variant.
|
||||
|
||||
That last point matters because the hidden movie-browser branch checks for the exact pair `Actor.getMap(...) == 26` and `Item.getQLo(arg_06) == 1` before showing `VIDEO PLAYER` and spawning `FLICTEST.slot_20`.
|
||||
|
||||
So the movie-browser route is no longer just semantically plausible. There is concrete shipped placement evidence for the exact object signature that the branch expects.
|
||||
|
||||
The remaining gap is narrower now:
|
||||
|
||||
- I did not find a matching base `shape:1099` object with `quality = 0` in the map-26 scene cache, so the `Cheaters Menu` branch is still not proven to be exposed by a shipped interactable in the same way.
|
||||
- I did find a `shape:1099` helper-geometry record with `quality = 0` on map 26, but it is flagged invisible in the scene cache, so it is not strong enough to claim a player-facing cheat terminal without more runtime or map-authoring evidence.
|
||||
|
||||
#### Practical locator
|
||||
|
||||
For the visible Regret movie keypad route, the current best locator set is:
|
||||
|
||||
- Map 26 placed item: `fixed:123`
|
||||
- Map 26 runtime id: `item:757:fixed:1099:1:32746:32798:48`
|
||||
- World coordinates: `(32746, 32798, 48)`
|
||||
- Disk coordinates used by the built-in warp path: `(16373, 16399, 48)`
|
||||
- Direct debug warp into map 26: `-warp 0 16373 16399 48 -mapoff 26`
|
||||
|
||||
Because map 27 is a base mission map in the executable warp table and carries the same keypad placement, there is also a second practical route:
|
||||
|
||||
- Map 27 runtime id: `item:755:fixed:1099:1:32746:32798:48`
|
||||
- Direct mission warp into the same keypad position on the adjacent state: `-warp 14 16373 16399 48`
|
||||
|
||||
Current best access read:
|
||||
|
||||
- `VIDEO PLAYER` / `FLICTEST` keypad: plausibly reachable from shipped content, and now backed by a visible placed keypad record.
|
||||
- `Cheaters Menu` keypad: branch definitely exists, but I have not found a matching visible `map 26 + QLo 0` placement. The only current map-26 `QLo 0` sibling is invisible helper geometry, so normal-player access to the cheat branch is still unproven.
|
||||
- If someone wants to force-test the cheat path, the cleanest current experiment would be to duplicate or retag the visible `0x044b` keypad so its local quality byte is `0` instead of `1`, then use that object on map 26.
|
||||
|
||||
#### Runtime confirmation
|
||||
|
||||
A live runtime test now closes the practical behavior:
|
||||
|
||||
- Warping to map 26 and using the visible `QLo 1` keypad does open the `FLICTEST` movie browser.
|
||||
- Talking to the nearby mission-giver NPC instead produces cheat-menu instructions and accepts mission numbers for the mission-assignment warp behavior.
|
||||
- Warping to map 27 does not reproduce that movie-browser route: the matching keypad placement there is nonfunctional for `FLICTEST`, and the nearby cheat-menu interaction is absent in that state.
|
||||
|
||||
So the Regret hidden branch is no longer only static-analysis evidence. The visible movie keypad behavior is runtime-confirmed, and the map-27 comparison now suggests that the hidden menu path is state-gated rather than broadly enabled on every neighboring camp variant.
|
||||
|
||||
#### Is map 26 reachable without warp codes?
|
||||
|
||||
Current best answer: probably not through the normal campaign flow.
|
||||
|
||||
The strongest reasons are:
|
||||
|
||||
- Regret map 26 scene metadata marks the map as `offset-only-or-unmapped`, while map 25 and map 27 are the neighboring campaign-facing camp states.
|
||||
- The live No Regret mission table uses base maps `0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 40`; map 26 is absent from that table.
|
||||
- The local map-25, map-26, and map-27 scene files all expose the same two local teleport-destination eggs (`teleport-id` `30` and `250`), but I did not find any teleporter helper or destination record in those scenes that explicitly names map 26 as the player-facing destination.
|
||||
- A broader Regret scene-cache search for `mapNum 26` does return many hits, but sampled hits resolve mostly to family-4 usecode-trigger egg records whose `labelId` happens to be `26`, not to a confirmed cross-map destination lane into map 26.
|
||||
|
||||
That does not prove there is no obscure script-only handoff anywhere in the executable, but the current workspace evidence favors `debug or offset-only camp variant` rather than `normally entered campaign map`.
|
||||
|
||||
### Regret: `ELEVATOR::slot_20`
|
||||
|
||||
Regret also contains a smaller scripted launch from an elevator body.
|
||||
|
||||
Direct caller snippet from `map_renderer/.cache/usecode/regret/EUSECODE/pseudocode/ELEVATOR/slot_20_slot_20.txt`:
|
||||
|
||||
```c
|
||||
if (arg_12 == 6) {
|
||||
...
|
||||
setUnkFlagA4();
|
||||
spawn FLICTEST.slot_20(pid, 19, local_15);
|
||||
suspend;
|
||||
clearUnkFlagA4();
|
||||
}
|
||||
```
|
||||
|
||||
This path fades to black, stops SFX, sets a temporary flag, and then spawns `FLICTEST`. That looks like a cutscene or scripted transition, not a debug-only leftover.
|
||||
|
||||
## What I did not find
|
||||
|
||||
- No direct evidence that map placements instantiate the `FLICTEST` class itself.
|
||||
- No direct shape-catalog or egg-subtype crosswalk that points a placed object straight at `FLICTEST`.
|
||||
- No ScummVM engine-side special case named `FLICTEST`; it looks like data-driven usecode, not a hardcoded engine mode.
|
||||
- The remaining uncertainty is now concentrated in the `Cheaters Menu` side of Regret `KEYPADNS`, not the `VIDEO PLAYER` side. The movie-browser branch has stronger placement evidence than the cheat branch.
|
||||
|
||||
That absence matters. The current evidence is much stronger for `helper class spawned by other scripts` than for `standalone world object that level designers placed directly`.
|
||||
|
||||
## Engine-side movie context
|
||||
|
||||
The ScummVM Crusader reference supports the general movie interpretation.
|
||||
|
||||
- `docs/scummvm-crusader-reference.md` notes that Crusader intro playback requires the `FLICS` directory.
|
||||
- The same note lists `MovieGump::I_playMovieOverlay` among the Crusader-side USECODE bridge intrinsics.
|
||||
- It also notes that Crusader movie playback uses assets under `flics/`.
|
||||
|
||||
That does not prove every `playFlic` signature detail in the original DOS binary, but it does support the high-level claim that `FLICTEST` is using the normal Crusader movie subsystem rather than a separate hidden debugger subsystem.
|
||||
|
||||
## Current best answer
|
||||
|
||||
Yes: `FLICTEST` really is a movie-test/movie-browser style usecode class.
|
||||
|
||||
More specifically:
|
||||
|
||||
- In Remorse it looks like a fairly full-featured movie selector/dispatcher helper.
|
||||
- In Regret it survives in reduced form, but one surviving route is a clearly hidden `KEYPADNS` menu that still advertises `VIDEO PLAYER` and `Cheaters Menu` behaviors.
|
||||
- It is not obviously placed directly on maps as its own authored class.
|
||||
- It is used indirectly by shipped content.
|
||||
- The hidden `VIDEO PLAYER` route in Regret is plausibly recoverable from shipped content because map 26 contains a placed `0x044b` object with the exact local quality value the branch checks.
|
||||
- The hidden `Cheaters Menu` route is still only partially proven. The branch exists, but I did not yet find a matching player-facing `map 26 + QLo 0` object.
|
||||
|
||||
Most defensible live uses found in this pass:
|
||||
|
||||
- Remorse: `EVENT::equip` can spawn `FLICTEST.slot_20`.
|
||||
- Regret: `DATALINK::use` can spawn `FLICTEST.slot_20`.
|
||||
- Regret: `KEYPADNS::use` exposes a literal `VIDEO PLAYER` UI and then spawns `FLICTEST.slot_20`.
|
||||
- Regret: `ELEVATOR::slot_20` has at least one cutscene-style spawn of `FLICTEST.slot_20`.
|
||||
|
||||
So the answer is not just `dead test script`. The better reading is `retained movie helper that still participates in shipped scripted flows`, even if its original name probably came from development/test usage.
|
||||
|
|
@ -63,16 +63,18 @@ Detailed completed analysis belongs in the files under `docs/`, not in this plan
|
|||
- That same warp-table lane is now exact across both retail DOS executables too. Byte checks against `CRUSADER.EXE` and `REGRET.EXE` now show matching 17-word `-warp mission` base-map tables (`0,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,40`) at `1478:0488` and `1480:075c`, each followed by a `0,0` terminator. The public map renderer now also has a dedicated mission-table extractor and generated JSON cache, so scene metadata no longer has to treat mission/base-map usage as an unknown ownership question.
|
||||
- That same startup lane is now tighter at the argument level too. Current best parser/control-flow read in `REGRET.EXE` is `-warp <mission> [x y z]`, with X/Y/Z carried as positional argv tokens after the mission number rather than as separate recovered switches. The corresponding runtime branch in `Game_RunNewGameFlow` is also clearer: nonnegative `-egg` overrides beat the coordinate path, while the real eggless-map workaround is `-warp <mission> <x> <y> <z>` plus `-mapoff` with `-egg` omitted so the game falls into direct `NPC_Teleport` instead of the teleporter-egg lookup.
|
||||
- The matching No Remorse cross-check is now closed too. Live `CRUSADER.EXE` `HandleCommandlineArgs` at `1048:0adc` uses the same positional `-warp <mission> [x y z]` parser shape, and `Game_Start` at `1020:029e` / `1020:02d0` uses the same runtime precedence: direct coordinates only win when the egg override is still negative, otherwise the code falls back to `Teleporter_CreateProcessDirect`. The parameter-only eggless-map workaround is therefore shared across both retail games, not Regret-specific.
|
||||
- The public map-renderer link lane is tighter again. Cross-game `0x01DB` support now covers both the earlier frame-`1` teleporter-light helpers and the remaining Regret map `3` frame-`0` telepad helper placements that carry destination ids `27/28` in `quality`. The same pass also adds the checked same-map `ELEVATOR` lane: frame-`0` `shape:542` sources now link to local teleport-destination eggs by verified `QLo` rules (`1..0x0f -> same egg id`, `0x10 -> egg 4`). Current best gap is still Regret map `3` egg `102`, which does not sit on the verified `shape:542` / `shape:307` elevator lanes yet.
|
||||
- The public map-renderer link lane is tighter again. Cross-game `0x01DB` support now covers both the earlier frame-`1` teleporter-light helpers and the remaining Regret map `3` frame-`0` telepad helper placements that carry destination ids `27/28` in `quality`. The same pass also adds the checked same-map `ELEVATOR` lane: frame-`0` `shape:542` sources now link to local teleport-destination eggs by verified `QLo` rules (`1..0x0f -> same egg id`, `0x10 -> egg 4`). A new Regret follow-up closes the remaining map-`3` egg-`102` gap too: Regret `shape:400` (`0x0190`) is a second `ELEVATOR` family, `ELEVATOR::gotHit` uses a generic same-map lane for `QLo >= 100 && < 0x00c8`, and map `3` contains concrete source `item:664:fixed:400:0:44030:9662:0` with `quality 614` (`QLo 102`) linking to destination egg `102`. A second Regret-only editor-object follow-up now also promotes `WATCHNS`, `WATCHEW`, `CRYOBOX`, `CRAZYEW`, `CRAZYNS`, `VIDEOBOX`, and the `SECRET_DOOR_POST` / `PRESSURE_BARRIER_*` target shapes into the viewer, including cautious local arrows for `WATCH* -> 0x0510` and `CRYOBOX -> 0x05DF/0x05E0`.
|
||||
- The map-13 wall-jump follow-up is tighter too. The suspicious nearby start tile `fixed:4767` is now closed as `FFFLOOR` (`shape 0x0135` / `shape:309`) rather than as an editor/helper collision override: decoded reference data still marks it as ordinary `terrain` (`4 x 4 x 0`, `solid`, `fixed`, `land`), but the extracted EUSECODE corpus shows `FFFLOOR::gotHit/equip/unequip` as an environmental hazard/sensor lane that toggles nearby same-shape floors and pushes standing actors through `NPC.slot_2d` into the normal hit/damage path. The nearest same-level trigger companion in the retail cache is family-4 egg `fixed:4770`, which currently resolves to `CHANGER`, so the local map-13 setup now reads better as a small scripted floor/trigger cluster than as proof that the rare jump-through wall has an authored per-instance non-solid flag.
|
||||
- That same map-13 trigger companion is tighter now too. The nearby family-4 egg `fixed:4770` is no longer just a subtype label: retail Remorse clearly uses `QLo 4 -> CHANGER`, and the extracted `CHANGER::hatch` body plus the local decoded scene now line up as a keyed roof-destruction trigger (`mapNum` egg id `37`, nearby roof tiles with `QLo 37`). Current safest local read is `FFFLOOR hazard tile plus CHANGER roof-removal cluster` on the same upper platform, still not a direct wall-solidity override.
|
||||
- The editor-helper overlay lane is tighter too. A broader exported-scene sweep now shows that `BRO_BOOT` (`0x04FE`) really does form a repeatable local helper lane into nearby same-`QLo` `SPANEL` items, with concrete Remorse examples on maps `9`, `10`, `11`, `21`, `23`, `160`, and `246`, so the renderer now promotes `BRO_BOOT -> SPANEL` alongside the existing cmd-link, alarm, steam, door, and flame helper arrows. The same follow-up kept two tempting false positives out of the overlay: `NPC_ONLY -> 0x04B1` and `DEATHBOX -> 0x04B1` still read better as incidental local overlap than as a dedicated helper-source relationship. The latest tooltip pass also upgrades `0x04B1` from a mostly structural decode to concrete operation notes: helper dispatch via nearby `0x0476`, direct target mutation, timed pulses through `TRIGGER.slot_22` / `DOOR.slot_21`, verified link rewrites, and a create-and-drop lane.
|
||||
- The editor-helper overlay lane is tighter too. A broader exported-scene sweep now shows that `BRO_BOOT` (`0x04FE`) really does form a repeatable local helper lane into nearby same-`QLo` `SPANEL` items, with concrete Remorse examples on maps `9`, `10`, `11`, `21`, `23`, `160`, and `246`, so the renderer now promotes `BRO_BOOT -> SPANEL` alongside the existing cmd-link, alarm, steam, door, and flame helper arrows. A later decompressed `.cache` pass tightens two Regret trigger families as well: `NPC_ONLY` (`0x0366`) and `CRUMORPH` (`0x0318`) now promote cautious local `-> 0x04B1` same-`QLo` arrows where authored matches exist, while `NPC_ONLY -> actor` and `DEATHBOX -> 0x04B1` still stay out of the overlay. The latest tooltip pass also upgrades `0x04B1` from a mostly structural decode to concrete operation notes: helper dispatch via nearby `0x0476`, direct target mutation, timed pulses through `TRIGGER.slot_22` / `DOOR.slot_21`, verified link rewrites, and a create-and-drop lane.
|
||||
- The same Regret controller lane is now better separated into `static helper links` versus `runtime actor-key links`. Current best read is that the withheld actor-target arrows (`CRUMORPH`, `NPC_ONLY`, and the wider sibling family) depend on mutable actor field `0x63`, not on a stable DTABLE export: sampled Regret DTABLE rows still show `0x00` at record byte `0x63`, while recovered `TRIGGER.slot_29` / `slot_2B` can rewrite field `0x63` on nearby matched NPCs after startup. The newly identified sibling families using that same hidden actor-key mechanism are `WATCHNS` / `WATCHEW`, `THRMBCKN` / `THRMBCKE`, and `SURCAMNS` / `SURCAMEW`. Current viewer/tooling stance remains `metadata + cautious helper arrows only` until a runtime or spawn-time export can close actor field `0x63` directly.
|
||||
- The skill-controller lane is tighter too. Shape `0x0120` is now closed as `FASTSKIL`, distinct from `SKILLBOX`: `enterFastArea` waits briefly, only runs while map-array is clear, uses frame `0/1` as difficulty thresholds for `TRIGGER.slot_20` lane `0` versus `1`, and uses frame `2` as an explicit `QLo/+1/+2` difficulty router. The renderer now exposes that decode in tooltips and adds conservative local `FASTSKIL -> 0x04B1` helper arrows, with frame-`2` variants for the recovered `QLo + 1` and `QLo + 2` lanes.
|
||||
- The switch/pad clarification lane is tighter too. Shape `0x0080` now closes as `BOX_EW`, and sampled exported scenes are strong enough to promote a conservative `BOX_EW frame 0 -> nearby same-QLo 0x04B1` helper arrow rule. Shape `0x04CD` now closes as `TRIGPAD`, but its broader occupancy/elevator behavior and the negative scene sweep keep it metadata-only instead of promoting a generic cmd-link overlay. Shape `0x033A` now reads best as a tiny `NUMBERS` readout/display helper family clustered with nearby `0x0501/0x0502/0x0503/0x0505/0x0507` pieces, so it also stays label-only.
|
||||
- The readable-usecode viewer lane is tighter too. New note `docs/map_renderer/trigger-usecode-links.md` records the evidence-backed class/event mapping now used for pinned controller tooltips: `BOX_EW`, `PANELNS`, `CARD_NS`, and `SPANEL` open `use`; `FASTSKIL` opens `enterFastArea`; `SKILLBOX`, `EVENT`, `ALARMHAT`, and `ALRMTRIG` open `equip`; `TRIGPAD` and `NPC_ONLY` open `gotHit`; and the `0x04B1` cmd helper jumps directly to `TRIGGER.slot_20`, the shared high-slot fan-out lane recovered from the extracted corpus and existing trigger notes.
|
||||
- The readable-usecode viewer lane is tighter too. New note `docs/map_renderer/trigger-usecode-links.md` records the evidence-backed class/event mapping now used for pinned controller tooltips: `BOX_EW`, `PANELNS`, `CARD_NS`, and `SPANEL` open `use`; `CRUMORPH`, `SKILLBOX`, `EVENT`, `ALARMHAT`, and `ALRMTRIG` open `equip`; `FASTSKIL` opens `enterFastArea`; `TRIGPAD` and `NPC_ONLY` open `gotHit`; and the `0x04B1` cmd helper jumps directly to `TRIGGER.slot_20`, the shared high-slot fan-out lane recovered from the extracted corpus and existing trigger notes.
|
||||
- The command-line lane is tighter around `-u` now too. In live non-Japanese `CRUSADER.EXE`, the parser case at `1048:0a46` copies the following token into `1478:065a`, and the renamed `startup_apply_u_override_if_present` at `1420:0cdf` later consumes that buffer to resolve/load an alternate usecode/EUSECODE source into `1478:6611/6613`, mark `1478:6615`, and rebuild the cumulative slot-base words at `1478:8c7c..8c82`. Current best read is `real retail startup usecode override`, not `JP-only` and not `dead string-table residue`; the paired consequence is that the older CRUSADER-side `-setver` attribution should now be treated as reopened until its exact retail consumer is isolated directly.
|
||||
- That same `-u` lane is now tighter at the runtime-scope level too. The follow-up note `docs/usecode-startup-override.md` now records that retail `-u` appears to replace the single live usecode root at `1478:6611/6613`, not add a sidecar table: `startup_apply_u_override_if_present` overwrites that root directly, rebuilds the cumulative slot-base words, and later consumers including `Usecode_ItemCallEvent`, `UsecodeProcess_CreateProcess`, `Interpreter_NextUsecodeOp`, and `Item_GetDamaged` all read the same replacement root. Current safest tooling implication is `runtime swap for the existing Crusader usecode VM`, which makes `-u` a potentially important future validation hook for round-tripped/custom usecode archives once the accepted source format is nailed down.
|
||||
- The same `-u` lane is tighter at the token-shape level now too. Live `1420:0cdf` does not use the copied argv token as an arbitrary final filename; it treats `1478:065a` as the `Filespec_GetFullPath` path component while loading the fixed mutable filename template `eusecode.flx` from `1478:07a0` through `1478:06d6/06d8` and forcing the first byte to `'e'` before both the existence probe and the final load call. Current safest read is therefore `path/root override for standard EUSECODE archive naming`, not `free-form filename override`. The stock bootstrap side is also better scoped: `1478:6611/6613` starts zero in the live NE image and the only currently recovered explicit writer there is the `-u` helper, so the normal non-`-u` seed remains only cross-referenced through the verified raw-side VM bootstrap note rather than fully live-NE-closed.
|
||||
- The same `-u` lane is tighter at the practical experiment level now too. The live helper shape plus direct bytes at `1478:079a` make the fixed filename template concrete as `eusecode.flx`, which materially weakens the older `maybe it accepts arbitrary archive filenames` hope. Current safest user-facing read is now `pass a directory/root to -u and place a complete replacement EUSECODE.FLX there`; failed forms like `-u USECODE/FLICTEST.FLX` and `-u FLICTEST.FLX` are best explained as path/root misuse rather than as evidence that the `-u` switch itself is dead.
|
||||
- The same override lane now has a concrete live-NE constructor pair too. `1420:1499` is now renamed `entity_vm_runtime_create` and currently reads as a `0x1319`-byte runtime-object allocator that zeroes a `0x1300`-byte front region behaving like `0x80` stride-`0x26` slot/runtime records before storing an attached helper pointer at `+0x1315/+0x1317`. `1430:0000` is now renamed `entity_vm_runtime_owner_resource_create` and currently reads as the compact `0x14`-byte file-backed helper that opens the resolved `eusecode.flx` path, queries entry count through vtable `+0x04`, allocates a backing buffer at `+0x10/+0x12`, and materializes indexed owner/resource records through vtable `+0x0c`. Current safest implication is that `-u` swaps the live VM runtime object graph, not just a raw archive handle.
|
||||
- The USECODE/VM owner/resource/runtime lane now has a workable partial model, a named sequencer entry, paired external file-family loader evidence, and supporting extraction/reporting tooling.
|
||||
- The USECODE/VM tooling lane now also has a concrete near-term implementation path: a Pentagram-derived proof-of-concept parser can reuse opcode decoding while swapping in the locally verified owner-loaded class and slot arithmetic, with a hybrid Ghidra comment/bookmark import path instead of a premature custom processor module.
|
||||
|
|
@ -147,6 +149,7 @@ Detailed completed analysis belongs in the files under `docs/`, not in this plan
|
|||
- Candidate O = interpreter callsite retarget -> `13a0:020d`, with `13a0:024a` zeroed inherited modal-wrapper words
|
||||
- Candidate P = interpreter callsite retarget -> `13a0:0086`, with `13a0:008f` zeroed inherited current-unit-wrapper words
|
||||
Both apply/restore cleanly on a disposable retail copy and are the next runtime tests.
|
||||
- Fresh live-Ghidra re-checks now tighten the lower bound on this patch family too. Retail still shows no recovered writer that seeds `1478:659c/659e`, the constructor at `1408:0000` still only returns the debugger object and seeds the inert shared callbacks `1478:65ab -> 1408:046f` / `1478:65af -> 1408:0474`, and the interpreter pre-call guard at `1418:049e..04b5` still only checks for a non-null debugger pointer before handing off to `1408:0053`. Current best implication: there is still no evidence-backed one-site direct jump that can safely load the hidden debugger. Candidate O/P are now the smallest structurally defensible executable patches because they are the first design that preserves all four required pieces together: object bootstrap, global pointer seeding, one-shot deferred entry, and sanitized wrapper arguments.
|
||||
- Full chronology for this patch line now lives in `docs/retail-debugger-patch-attempts.md`, including the failed global callback rewrite, direct wrapper call, single-step `65af` build, break-next `65af` build, guarded `0474` trampoline, shared `046f` method patch, and the current private-vtable candidate.
|
||||
- The hidden-menu orphan model is now materially stronger too. New live renames in seg1408 (`usecode_debugger_break_state_create`, `usecode_debugger_maybe_break_on_current_line`, `usecode_debugger_breakpoint_insert_sorted`, `usecode_debugger_has_breakpoint`, `usecode_debugger_callstack_push_entry`, `usecode_debugger_callstack_pop_entry`, `usecode_debugger_enable_single_step`, `usecode_debugger_clear_step_state`, `usecode_debugger_current_entry_get_unit_name`) line up with the seg109 UI in a way the cheat-only hook never did. The concrete interpreter-side handoff at `1418:04aa..04b5` now calls `usecode_debugger_maybe_break_on_current_line` whenever the far pointer at `0x659c/0x659e` is non-null, and that helper checks `(file,line)` breakpoints before callbacking through the debugger-state object's vtable. Current best read is therefore that the retail orphan happened one layer earlier than the cheat/event experiments: the seg109 current-unit debugger UI likely used to be entered from this seg1408 breakpoint object, but retail no longer appears to instantiate/store that object at `0x659c/0x659e`. That makes the breakpoint callback lane a stronger original-entry candidate than direct event `0x103` retargeting.
|
||||
- The follow-up doc reconciliation is now closed too. `docs/ne-segment1.md` no longer presents the seg109/raw-reference UI addresses (`000b:*`) and the live seg1408 breakpoint-state addresses (`1408:*`) as if they were competing versions of one table; it now uses one combined component map that makes the layering explicit and preserves the interpreter callback at `1418:04aa..04b5` as the bridge between them.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue