116 lines
7.8 KiB
Markdown
116 lines
7.8 KiB
Markdown
# Mod "freecam" – Free camera & noclip (CHEATS)
|
||
|
||
Package `mods.freecam` (`FreeCamMod`, `FreeCamPatch`); uses `mods.practice.Asm`/`Hotkeys`. Version 3.
|
||
|
||
## Options and controls
|
||
|
||
| Key | Default | Meaning |
|
||
|---|---|---|
|
||
| `camKey` | F7 | toggle free camera |
|
||
| `noclipKey` | F9 | toggle noclip |
|
||
| `freeze` | true | stop all strats (objects, llama, camera strat) while the free camera is on |
|
||
| `camSpeed` | 3 (1–32) | free camera speed, 1/32 tile per logic tick (30 ticks/s) |
|
||
| `noclipSpeed` | 2 (1–32) | noclip speed, 1/32 tile per tick |
|
||
|
||
The controls are the game's own (default bindings below; remapped keys and joysticks work, because
|
||
the mod reads the game's control bits, not raw keys):
|
||
|
||
| Game control (default key) | Free camera | Noclip |
|
||
|---|---|---|
|
||
| Up / Down (arrows) | fly forward / back (horizontal, along the view) | llama away from / towards the camera |
|
||
| Left / Right (arrows) | strafe left / right | llama screen-left / screen-right |
|
||
| Camera left / right (A / S) | turn left / right | orbit the camera around the llama |
|
||
| Jump (X) / Action (Z) | up / down | llama up / down |
|
||
| PgUp / PgDn | look up / down (±88°) | – |
|
||
| Power (Space) or Shift | ×4 speed | ×4 speed |
|
||
|
||
While the free camera is on, the game gets no movement input (pads cleared, only pause kept), so
|
||
with `freeze` off the game keeps running but the llama stands still; Escape/P still opens the pause
|
||
menu (no camera movement while it is open). In noclip the llama still gets the input (walk/jump
|
||
animations), but its position is the mod's. Toggles (F7/F9) and Shift use GetAsyncKeyState.
|
||
Both toggles are switched off when a level (re)starts.
|
||
|
||
## How it works
|
||
|
||
| Site | Original | Cave |
|
||
|---|---|---|
|
||
| 0x54b5fd | `call FUN_00427c40` (right after the frame's input read FUN_00419e00/FUN_0041a220) | copy control bits for the free camera; while it is on: clear pads, rerun FUN_004256c0 |
|
||
| 0x54b79f / 0x54b7e0 | `call FUN_0054bad0` (running / paused frame) | call it, then toggles + free camera or noclip chase camera |
|
||
| 0x54b614 / 0x54b7a4 / 0x54b7e5 | `call FUN_0041e020` (region mask from the camera cell) | with free cam/noclip on: run it with the llama's x/z as camera cell |
|
||
| 0x54b783 | `call FUN_004287c0` (particles, after each tick's strat update) | noclip, then jmp to the original |
|
||
| 0x54c4a0 | entry of FUN_0054c4a0 `55 8B EC 83 EC 4C` | `ret` while the free camera is on (only with `freeze`) |
|
||
| 0x54d0d5 | `mov [0x6d9e40], edi` in FUN_0054d0d0 (strat system reset at level start) | clear both toggles |
|
||
|
||
Input facts: FUN_00419e00 ORs, for each of the 14 bindings (DIK codes at 0x584758, order Left,
|
||
Right, Up, Down, A, S, Space, X, Return, P, PgUp, PgDn, Z, O), the bit from table 0x572100 into the
|
||
u16 pad word `*(0x5fcf70)` (12-byte pad structs, count 0x5fcf6c; +2/+3 stick). FUN_004256c0 turns
|
||
the pads into the held control bits 0x5fcf20 (Up 0x10, Right 0x20, Down 0x40, Left 0x80,
|
||
A 0x400, S 0x800, Return 0x1000, Z 0x2000, X 0x4000, Space 0x8000, pause 0x8; PgUp/PgDn 0x100/0x200
|
||
are **not** copied there, the mod reads them from the keyboard pad word), stick magnitude 0x5fcefc
|
||
and direction; FUN_00425bb0/FUN_00425c00 derive the edge bits the strats use.
|
||
|
||
Camera facts: the game camera is a strat (`*(0x6d9e28)`). FUN_0054bad0 copies its position to
|
||
0x6d9d20.. and 0x5790b0/b4/b8 and computes yaw/pitch (0x5790a4/0x5790a0, 0..4095, pitch < 0 =
|
||
looking down) from the position and the look-at target 0x6d9d80/84/88. The renderer
|
||
FUN_0041a920(&0x5790a0, &0x6d9d70) recomputes the angles from position → target (and writes them
|
||
into the camera strat +0x20/+0x24), so position + target are what count. Forward =
|
||
(sin yaw · cos pitch, sin pitch, cos yaw · cos pitch) from the sine table 0x574318 (s32[4096],
|
||
4096 = 1.0); screen-right on the ground = (−cos yaw, sin yaw).
|
||
FUN_0041e020 takes the camera's cell, looks up its region (MAP grid +0x48) and sets the region mask
|
||
0x58642c (which rooms/tiles/strats are drawn; 0xffffffff outside the grid). A free camera in a cell
|
||
of another region got mask 1 in Village 1, so the whole level vanished and only the static backdrop
|
||
was left – that is why the free camera "only froze the game" for the user. The mod now computes the
|
||
mask from the llama's cell.
|
||
|
||
Noclip: after every tick's strat update the player strat (`*(0x6d9e1c)`) gets the noclip position
|
||
at +0x30.. (current), +0x50.. (previous tick, so the next wall sweep starts there) and +0x94..
|
||
(render copy), and its collision flags +0xe8 & 0x48000 (0x8000 floor check FUN_00403020, 0x40000
|
||
wall check FUN_00403ee0) are cleared, so it neither falls nor dies over the void. The camera strat
|
||
does not follow a llama moved this way (it stays where it was and only turns), so while noclip is on
|
||
(and the free camera off) the mod drives a chase camera: target = llama + (target − llama) and
|
||
camera = target + (camera − target), both offsets taken when noclip is switched on; A/S rotate the
|
||
camera offset around the llama (64/4096 per tick ≈ the game's 90° per 0.5 s). Switching noclip off
|
||
ORs the collision bits back (same player strat only) and hands the camera back to the camera strat.
|
||
|
||
## Fix history (version 3, 2026-11-06)
|
||
|
||
User report (Village 1/TestLevel, with testworld, cheats, fog 500 %/off, intro-skip):
|
||
1. "Free camera just freezes the game": the camera did move, but I (forward) flew along the pitched
|
||
view into the ground within a second, and outside the llama's region the region mask hid the
|
||
whole level (only the backdrop stayed). Fixed: region mask from the llama's cell; arrows move
|
||
horizontally, X/Z change height.
|
||
2. Noclip camera did not follow: the camera strat ignores the teleported llama → chase camera.
|
||
3. Noclip left/right swapped: right vector was (cos, −sin), correct is (−cos, sin).
|
||
4. Free camera J/L turned the wrong way (L = +yaw = left); now S (camera right) = −yaw. During
|
||
noclip the camera strat's own turning fought with the llama → replaced by the orbiting chase cam.
|
||
Also: controls switched from I/K/J/L/T/G/U/H (GetAsyncKeyState) to the game's own controls, input is
|
||
swallowed while the free camera is on.
|
||
|
||
## Verified in game (2026-11-06, apptest, user's mod set: testworld, cheats, fog 500/off, intro-skip)
|
||
|
||
* TestLevel (world 9), freeze on (`img/mods/freecam-fix-controls.jpg`): F7, Up flies forward,
|
||
S turns right, Right strafes right, X rises, PgDn looks down, A turns left, Z sinks; F7 off →
|
||
normal camera behind the llama.
|
||
* TestLevel noclip (`img/mods/freecam-fix-noclip.jpg`): Up/Right/Left move the llama away/screen-
|
||
right/screen-left with the camera following, A orbits, X lifts it, F9 off → it lands and the game
|
||
camera takes over.
|
||
* TestLevel, freeze off: the game runs while the camera flies; arrows/X/Z/A/S do not move or turn
|
||
the llama; after F7 off it walks normally.
|
||
* Village 1 (`img/mods/freecam-fix-village.jpg`): flying up and back shows the village from above
|
||
(before the fix: only backdrop); noclip over the cliff edge with chase camera; pause menu with
|
||
the free camera on (menu navigation does not move the camera), no crash.
|
||
* Values checked with winedbg (`attach`, `x /4x`): control bits, camera/target, region mask.
|
||
* `FreeCamModTest` (practice + freecam on the real exe) passes; PracticeMod untouched.
|
||
|
||
## Limits
|
||
|
||
* After switching noclip off in the air, the llama falls to the floor below as usual.
|
||
* Enemies, water and triggers still work during noclip (a trigger may start a cutscene/dialog).
|
||
* F7/F9/Shift react even when the game window is not focused (GetAsyncKeyState).
|
||
* The region mask follows the llama: flying far into another room may show it without its own
|
||
region's objects, or the backdrop only where the level has no geometry (open sea around islands).
|
||
* A cutscene camera set via 0x57f8bc in the render function still wins over the free camera.
|
||
* No mouse look.
|
||
|
||
Hotkeys are checked together with the other mods' hotkeys (`mods/Hotkeys.conflicts`): applying fails with a clear message if two enabled hotkey options share a key.
|