Faceplant
The skater stood up, rolled away and did tricks. For about four seconds the port was a game. Then he bailed and the game crashed. Then he crouched like a mannequin, grabbed when you said grind, sank knee-deep into the floor during a manual and crashed again on a manual gap. This is the chapter where everything that looked the same between THUG1 and THUG2 turned out not to be, one faceplant at a time.

1Skip the menu, go straight to the level
Testing in a level meant clicking through the wheel every time. The port learned a quick start: THUG_LEVEL=TR
in the environment, and a patched launch_main_menu asks the engine whether to jump straight into Training,
in High Score / Free Skate, with the current skater:
2The crash that crashed while reporting the crash
Bail, crash, every time. The stack ended in TextureSplat, the blood splat a bail paints on the ground.
THUG1's scripts give it a texture name as a string. THUG2's give it a checksum: name = blood_01. So the
engine decided there was no name and asserted. And the assert's own message had a %s with nothing passed
to print, so formatting the error read garbage and took the game down. The error message was the bug.
A splat whose texture isn't loaded is now skipped with a line in the log, instead of an assert. Bails are painful enough.
3The mannequin crouch
Hold Ollie and a THUG2 skater crouches, loaded like a spring. Here he stood straight up, like a mannequin someone had
put on a board. Steering played a pose from a store window too. THUG2's animation scripts pick animations by skating
style (ProfileEquals style = crappy, Cool or fat), and THUG1's
SkaterEquals had never heard of a style. Every check came back false, so every pick fell through to the
default. Teaching it the profile's skatestyle brought the crouch back:
4Grind button grabs, grab button grinds
On the keyboard, the grind key grabbed and the grab key ground. The port has two keyboard layouts: in menus, keys
mean "choose" and "back"; while skating, they mean tricks. And in menus, grind and grab swap pad buttons. So the port
thought the skater was in a menu. Why? It asks the game mode: are we in the front end? THUG1's front end game mode says
is_frontend = 1. THUG2's game modes don't have that line at all, and THUG2.exe never asks. THUG1's code
read the missing value into an uninitialised int and got whatever was on the stack. That day it was "yes".
The same garbage also made "change camera view" do nothing in every level: it checks for the front end first. One uninitialised integer, two bugs, zero warnings.
5Triggers speak a new language
The Training level's manual gap crashed the game the moment the skater hit it. The trigger script asks
GetTriggerArray through, then TriggerType <trigger_array>. Remember chapter 1, where
THUG2's script replaced THUG1's C function GetTriggerArray? THUG1's version returned trigger names. THUG2's
script returns numbers: grind onto becomes [13, 3, -1]. THUG1's TriggerType
wanted names and asserted on numbers.
So what are the numbers? THUG2.exe's TriggerType turns each into a bit and checks that the event has all of
them. Its TripTrigger builds the event from a three-number table, and those tables sit next to each other
in the executable's read-only data. Dumping them gives the whole vocabulary: skate=1, jump=2, onto=3, off=4, offedge=5,
wallplant=6, lip=7, land=8, bonk=9, climb=10, graffiti=11, spin=12, grind=13, walk=14.
The skater now trips triggers the THUG2 way too: rails as grinds, walking from surface to surface as walking, wallplants as wallplants. Everything except the climbing and spin moves THUG1 doesn't have.
6The manual meter that thought it was a rail
In THUG2, the manual meter is a vertical arc left of the skater. Here the arrow slid sideways, like on a rail, while the arc stood upright. The meter code turns the meter by -90° and, knowing that children don't turn with their parents, pre-rotated the arrow's position by hand. Except they do now, since chapter 3. The arrow got turned twice. The fix was deleting code: place the arrow as on a horizontal meter and let the parent do the turning.
The first attempt copied THUG2.exe's numbers exactly, a -80/+50 offset. It put the arrow under the arc, because THUG2 turns children about a different point than the port does. Matching the result beat matching the numbers.
7Knee-deep in the floor
Hold a manual, or balance on a rail, and the skater sank into the ground, half his body gone. Only while balancing.
Balance animations play through a different path, a "wobble" controller that scrubs the animation back and forth with
your lean. Remember flag 0x20000 from chapter 1, translations relative to the neutral pose? The normal
animation path hands the decoder that neutral pose. The wobble path never did, so every bone's offset started from zero
and the body collapsed onto its hips.
Three lines. The cutscene player had the same hole and got the same three lines.
8Back to the main menu, and boom
Quit a level to the main menu and the game asserted: "item already in hash table", while loading the multiplayer
icons. They were never unloaded. THUG2's scripts unload them in level_cleanup_assets, which nobody calls.
Searching THUG2.exe for that script's checksum found the one place that runs it: the level cleanup, right before the
skate module cleans up, after exit_pause_menu. Same place now:
Where it landed
The skater skates, crouches, steers, grinds on the grind key, manuals on top of the floor, bails without taking the game with him, and comes back to the main menu in one piece. Most of the fixes were one line. Finding each line was the job.