Files
Tiago Valente 08c9abb573 CheatSpecter 0.4.1
A quality pass after 0.4.0 was tried on a real console.

- Fix the Learn page crashing in current browsers when changing lesson steps
- Clear feedback for every search outcome; real progress with time left and Cancel
- Dialogs scroll inside the screen with Save and Cancel always in view
- Fix a console error on every closed connection to the payload
- Copy buttons work when the page is served from the PS5
- The practice game sits beside the lesson's tools and lasts between steps
- Damaged cheat files, vanishing error messages, unreadable memory shown as zeros
- Phone layout: one-line header, larger switches, readable rows
- Browser tests under test/ui and a guard against effects that return values
2026-10-10 15:19:56 -07:00

222 lines
8.3 KiB
JSON

{
"format": "cheatspecter-lesson",
"version": 1,
"id": "structures",
"title": "Structures: the things next to your value",
"summary": "Health, ammo and position live side by side, and enemies are laid out the same way as you.",
"minutes": 10,
"games": {
"duel": {
"name": "Specter Duel",
"objects": [
{
"id": "you",
"fields": [
{
"id": "health",
"type": "u32",
"value": 100,
"offset": 16
},
{
"id": "armor",
"type": "u32",
"value": 35,
"offset": 20
},
{
"id": "team",
"type": "u32",
"value": 1,
"offset": 24
}
]
},
{
"id": "guard",
"fields": [
{
"id": "health",
"type": "u32",
"value": 60,
"offset": 16
},
{
"id": "armor",
"type": "u32",
"value": 10,
"offset": 20
},
{
"id": "team",
"type": "u32",
"value": 2,
"offset": 24
}
]
}
],
"actions": [
{
"id": "hurt",
"label": "Guard hits you",
"effects": [
{
"field": "you.health",
"op": "sub",
"by": 12,
"min": 0,
"refill": 100,
"site": "damage"
}
]
},
{
"id": "strike",
"label": "You hit the guard",
"effects": [
{
"field": "guard.health",
"op": "sub",
"by": 12,
"min": 0,
"site": "damage"
}
]
}
],
"hud": [
{
"label": "Your health",
"field": "you.health",
"show": "number"
},
{
"label": "Your armor",
"field": "you.armor",
"show": "number"
},
{
"label": "Guard health",
"field": "guard.health",
"show": "number"
}
],
"decoys": [
{
"type": "u32",
"value": 100,
"count": 8
},
{
"type": "u32",
"value": 88,
"count": 3
}
]
}
},
"steps": [
{
"kind": "read",
"title": "Values come in bundles",
"text": "A game does not scatter your health, armor and ammo across memory. It keeps everything about one thing together, in a block called a **structure** (or an object): the player's block, an enemy's block, a car's block.\n\nInside a block, each value has its place, always the same distance from the start: health 16 bytes in, armor 20 bytes in, team 24 bytes in. That is what the `+0x28` meant in \"sub dword ptr [rbx+0x28], 1\": rbx points at the start of the block, and 0x28 is how far in the ammo sits.\n\nTwo useful things follow.\n\n# Find one value and you have found its neighbors\n\nOnce you know where your health is, your armor is very likely a few bytes away. No searching needed. You look around.\n\n# Things of the same kind are built the same\n\nThe game has one blueprint for \"a fighter\" and uses it for you and for every enemy. Different blocks, same layout. And usually the **same code** works on all of them: one \"take damage\" for everybody, told each time whose block to work on.\n\nThat second point is about to matter."
},
{
"kind": "practice",
"title": "Look around your health",
"text": "1. Find **Your health**. Getting hit by the guard changes it.\n2. On the row, open **…** and choose **Show in Memory**.\n\nThe Memory tab shows the bytes of the game around your health, with your health marked. Click the bytes just after it: the panel below says what they mean as a number. Four bytes after your health starts, you will find a number you recognize from the game's screen.\n\nClick its first byte. Under **Change it as a number**, leave the type on 4 bytes, type **500** and press **Write**.",
"game": "duel",
"tool": "find",
"goal": {
"check": "value",
"label": "Give yourself 500 armor or more, without searching for armor",
"field": "you.armor",
"is": "atLeast",
"value": 500
},
"hints": [
"Find tab: search for Your health, let the guard hit you, type the new number, Narrow down.",
"In the Memory tab each pair of characters is one byte, and your health takes four of them. The armor is the next four.",
"Your health is marked in the grid. Click the byte four places after its first byte. The panel underneath then shows \"4 bytes\" as 35, which is your armor. Type 500 in the box under \"Change it as a number\" and press Write."
],
"done": "You changed your armor without ever searching for it. On a real game, the bytes around a value you found are the first place to look for the next one."
},
{
"kind": "practice",
"title": "One instruction for everyone",
"text": "Make yourself invincible the lesson 6 way: on your health row, **…** and **Find what writes to this**, get hit, and **Disable it**.\n\nThen hit the guard a couple of times and watch **Guard health**.",
"game": "duel",
"tool": "find",
"goal": {
"check": "all",
"of": [
{
"check": "off",
"label": "Switch off the code that lowers your health",
"action": "hurt"
},
{
"check": "pressed",
"label": "Now hit the guard, twice",
"action": "strike",
"times": 2
}
]
},
"hints": [
"If your health is no longer in the Find tab, find it again: search, get hit, narrow down.",
"While the Code tab is watching, press \"Guard hits you\". Then press \"Disable it\" on the instruction that appears.",
"Now press \"You hit the guard\" twice and look at the Guard health number."
],
"done": "The guard is invincible too. There is one \"take damage\" instruction, and it serves you and him alike. Switching it off switched off damage for everyone. This is the most common surprise in cheat making: \"god mode\" that also makes every enemy immortal."
},
{
"kind": "choice",
"title": "Check yourself",
"text": "",
"question": "Why did disabling one instruction protect both you and the guard?",
"options": [
"The tool disabled two instructions.",
"Both fighters are damaged by the same code, which is told whose block to work on each time.",
"The guard copies your health."
],
"answer": 1,
"explain": "One blueprint, one piece of code, many blocks. The instruction does not know or care whose health it is lowering."
},
{
"kind": "practice",
"title": "On your own: only you",
"text": "Fix it. You should take no damage, and the guard should go down when you hit him.\n\nYou have two tools that can tell you and the guard apart, because they work on **your** block rather than on the shared code.",
"game": "duel",
"tool": "cheats",
"goal": {
"check": "all",
"of": [
{
"check": "held",
"label": "Take three hits without losing health",
"field": "you.health",
"action": "hurt",
"times": 3
},
{
"check": "value",
"label": "Bring the guard down to 0 health",
"field": "guard.health",
"is": "atMost",
"value": 0
}
]
},
"hints": [
"If your \"take damage\" cheat from before is still on in Your cheats, switch it off first, so the guard can be hurt again.",
"Freezing works on one address: yours. Find your health and press its padlock.",
"With your health frozen, let the guard hit you three times, then hit him until his health reaches 0."
],
"done": "Freezing your own health leaves the shared code alone, so the guard still takes damage. The cleaner fix is to change the code so it checks who is being hit, and that is the next lesson.",
"challenge": true
}
]
}