Scripts
Most of THUG2 isn't C++, it's QScript: menus, tricks, goals, the whole story. The port reads THUG2's
scripts as they ship, packed in qb_scripts.prx, and changes them without ever touching that file: small
patches, compiled with qb and laid on top when the original loads.
1Read the scripts
A script file (.qb) is bytecode: tokens, with names as CRC32 checksums. qb turns it back into text, and
takes a whole folder at once. Every THUG2 detail in the story started with a decompile and a
grep:
Names you won't find in the scripts stay checksums (0x7a6bf912). Some of them get a name later,
by brute force and luck. See chapter 6.
2Lay a patch on top
When the game loads any .qb, it looks for scripts\patches\<same path> and parses that
right after. A patch defines only what it changes; same name, newer definition, wins. Its symbols are registered under
the original file's name, so a level unloading its scripts takes the patch with them, and loading the level again
re-applies it.
3The patches
| Patch | Patches | Does |
|---|---|---|
Scripts/patches/levels/mainmenu/mainmenu_options.q | the wheel's Game Options | Adds Screen, Graphics, Keyboard and Gamepad after Load Game |
Scripts/patches/scripts/game/menu/gamemenu_options.q | the pause menu's Options | The same four after Save Game, and the four menus themselves |
Scripts/patches/levels/mainmenu/mainmenu_scripts.q | launch_main_menu | The quick start: THUG_LEVEL=TR goes straight to a level |
4Compile them with every build
The build scripts compile Scripts/patches/ into Game/Data/scripts/patches/ with
qb compile -target thug2. The compiled files are the same on every platform, and the releases ship them next to
THUG2's own data. qb is found on the PATH, as $QB, or built from ~/go/src/qb.
5When a script wants to be C++
Patching only works one way: scripts can replace scripts. When THUG2's scripts call a C function the engine doesn't
have, the call is logged once (THUG2 MISSING: SkaterExists (called by level_cleanup_assets)) and returns
false. The ones that matter get written in Code/Sk/Scripting/thug2funcs.cpp, listed in
thug2ftable.inc, and checked against what THUG2.exe does. And when THUG2 turned a THUG1 C function
into a script (Grind, Manual, GetTriggerArray), the script wins:
chapter 1.
6QScript, the fine print
x = ( some_global )with a checksum global gives you the global's name.( some_array [ index ] )evaluates. Write accordingly.fullscreen = screen_menu_fullscreenpasses a global by name, andGetIntegerquietly resolves it on the C++ side. Useful, once you know.- Comparing a missing function's result in an expression gives
None, not false: the log saysTHUG2 WARNING: 'None' from an expression, and the check counts as false.
The story of qb itself, a byte-for-byte rewrite of the QScript toolchain for THUG1, THUG2 and American Wasteland, is in the THUG1 port's notes.