<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>rugrats</title>
<subtitle>Reverse engineering Rugrats: Search for Reptar for the PlayStation (n-Space / THQ, 1998, SLUS-00650) and porting it natively to the PC on retro_rt, using tools written for the job in Rust. No game data included.</subtitle>
<id>https://toyz.github.io/rugrats/</id>
<link rel="self" href="https://toyz.github.io/rugrats/feed.xml"/>
<link rel="alternate" href="https://toyz.github.io/rugrats/"/>
<link rel="alternate" type="application/json" href="https://toyz.github.io/rugrats/log.json"/>
<updated>2026-10-23T00:00:00Z</updated>
<generator>cairns 0.10.2</generator>
<entry>
<title>The marks&#39; face word, and the flight quads&#39; screen test</title>
<id>https://toyz.github.io/rugrats/248-the-marks-face-word-and-the-flight-quads-screen-test</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/248-the-marks-face-word-and-the-flight-quads-screen-test"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="render"/>
<summary>Two of the long-open drawing items are decoded.</summary>
<content type="html">&lt;p&gt;Two of the long-open drawing items are decoded. A mark&#39;s face word turns out to set its draw depth, and the flight quads have a screen test. The port also now draws a mark grey 128 in a room of flag 8, and culls flight quads as the original&#39;s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The face word&lt;/strong&gt; (0x800243c4 through 0x80023c2c, 0x800233f4, 0x800221cc; gameplay.md, the flights&#39; marks):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Top two bits:&lt;/strong&gt; the primitive (3 a quad).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x800000:&lt;/strong&gt; textured.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x2000000 and 0x1000000:&lt;/strong&gt; the POLY&#39;s semi and raw bits (code 0x2c for the FT4).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x600000:&lt;/strong&gt; the page&#39;s blend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x200:&lt;/strong&gt; a screen test (0x800239e8, 0x80023a8c).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x400:&lt;/strong&gt; a face with a corner clipped is skipped (vertex +6 &amp;amp; 48).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x800:&lt;/strong&gt; a face wholly off one side is skipped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;32, 64, 128 and 256:&lt;/strong&gt; the flag word, the corners, the texture and the colour are kept for the next face.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x7000:&lt;/strong&gt; the ordering table&#39;s depth (0x800221cc): 0 the first corner&#39;s, 0x1000 the last&#39;s, 0x2000 the corners&#39; mean, 0x3000 the nearest, 0x4000 the farthest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x8000:&lt;/strong&gt; that depth 64 nearer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The marks&lt;/strong&gt; (0x8008652c):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Which word.&lt;/strong&gt; It is chosen by the normal met, not by the tile&#39;s +4 as gameplay.md had it. A wall (the normal&#39;s y 0) gets 0xc080be00: in at its nearest corner pulled 64 forward. A floor or ceiling gets 0xc0802e00: in at the mean. +0x89 adds 0x2000000.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The draw&lt;/strong&gt; (0x8008e508) is one quad, or, with the record&#39;s bit 0x8000000, a 3 x 3 grid of four (the word | 416).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The colour.&lt;/strong&gt; The record&#39;s colour, but 128 when the room of the mark (its +0x0a, the flight&#39;s launch room +0x38) has flag 8 (room +0x0c).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The port&lt;/strong&gt; has a depth buffer and already passes a decal&#39;s equal depth and biases it forward. It now keeps each flight&#39;s launch room (&lt;code&gt;Flight::room&lt;/code&gt;, &lt;code&gt;Flown::room&lt;/code&gt;) and lays a mark grey 128 when that room has flag 8.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The flight quads&#39; screen test&lt;/strong&gt; (0x8008a954), worklog 114&#39;s and 239&#39;s open item:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It comes after the depth test (0 to 65535 ahead).&lt;/li&gt;
&lt;li&gt;The corners (-w, -h), (w, -h) and (-w, h) are turned by the roll (rcos, -rsin; rsin, rcos), x scaled as the view (0x8001e0f4), and projected (RTPT).&lt;/li&gt;
&lt;li&gt;The quad is drawn only when one of them is on the screen within 167 / 128 of its extents: 0x800c4a00&#39;s 512 and 240, so |sx| &amp;lt; 668 and |sy| &amp;lt; 313.&lt;/li&gt;
&lt;li&gt;The port&#39;s &lt;code&gt;Flights::draw&lt;/code&gt; takes that test from the game&#39;s render eye.&lt;/li&gt;
&lt;li&gt;The four-quad split (face 0x8000000: the centre and the edges&#39; midpoints by three more RTPTs) only changes how the texture lies; the port draws one quad.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt; every test file, and the hle suite.&lt;/p&gt;
&lt;p&gt;No room of any level has flag 8 once its LP kind 6 records are read (every level opened in the port, room +0x0c), so in the game a mark always takes its own colour.&lt;/p&gt;
</content>
</entry>
<entry>
<title>Collectibles in the air, and before the slide</title>
<id>https://toyz.github.io/rugrats/247-collectibles-in-the-air-and-before-the-slide</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/247-collectibles-in-the-air-and-before-the-slide"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>Collectibles in the air are now taken as the original&#39;s air move meets them, not within a radius.</summary>
<content type="html">&lt;p&gt;Collectibles in the air are now taken as the original&#39;s air move meets them, not within a radius. On the ground the collectible now comes before any slide along it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The air&lt;/strong&gt; (0x8005ee78, worklog 245&#39;s open item):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The air move&#39;s test (0x80066014) meets the objects of the nearby list. When it answers anything but clear and +0xf4 is set, 0x8005ed2c runs.&lt;/li&gt;
&lt;li&gt;For an object of class 11 (or template 711, not 1177) that clears +0xf4 and +0xf8 and runs its +0x58 (level 11&#39;s standing jump keeping its bones, 0x800c4bc4). It answers 1, and the move is then had as if clear.&lt;/li&gt;
&lt;li&gt;The port&#39;s &lt;code&gt;air_move&lt;/code&gt; meets the collectibles after the other objects (&lt;code&gt;touchable&lt;/code&gt;, as the walk&#39;s probes do). Met, one is &lt;code&gt;touched&lt;/code&gt; and the answer becomes Clear, across and down alike.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Game::touch&lt;/code&gt; no longer has a radius test at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The ground&#39;s order.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0x80064a34 only answers held; the walk handler (0x8005e1f4) looks at +0xf4&#39;s class first. Class 11 is the touch, with no step. Only otherwise does it slide or bump (0x80065120 at 0x8005e3c4).&lt;/li&gt;
&lt;li&gt;The port had slid along a collectible met at a slant before looking, so the touch was lost and the player slid round it. &lt;code&gt;ground_move&lt;/code&gt; now looks at the first probe&#39;s hold first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The play tests&#39; collecting.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where no walk led to a pickup, the harness puts the player on it. Its probes then start inside its box, as on the original.&lt;/li&gt;
&lt;li&gt;It now puts him 600 off on his own floor, the first of eight ways from the one he came, and he walks at it from there.&lt;/li&gt;
&lt;li&gt;Worklog 245&#39;s walk back had run off a ledge in level 15 and could not turn away in level 18.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;every test file, and the hle suite;&lt;/li&gt;
&lt;li&gt;levels 15, 17, 18 and the whole game won by play.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>A room&#39;s follow camera snaps as the original&#39;s switch</title>
<id>https://toyz.github.io/rugrats/246-a-room-s-follow-camera-snaps-as-the-original-s-switch</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/246-a-room-s-follow-camera-snaps-as-the-original-s-switch"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>A room&#39;s follow camera now switches as the original&#39;s: a camera byte different from the last runs the follow setup, which snaps the camera behind the player at the new height.</summary>
<content type="html">&lt;p&gt;A room&#39;s follow camera now switches as the original&#39;s: a camera byte different from the last runs the follow setup, which snaps the camera behind the player at the new height. Before, the port only changed the height and let the eye ease toward it at 9 a frame.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the original does.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CameraSwitch&lt;/strong&gt; (0x80077c88): on the room&#39;s entry, flag 4, the room&#39;s camera byte is compared with the last one switched to (D_800c4410). 0x80077070 (a level&#39;s start) and 0x8007737c (a cine&#39;s end) set D_800c4410 to 1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A different byte&lt;/strong&gt; is passed to 0x800291f8. Byte 6 becomes kind 1 with height 200; 7 and 11 become 500; 8 becomes 1600; 9 becomes 2000; 1, 10 and 12 become the level&#39;s own (1500); byte 13 passes a fifth argument of -1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0x800291f8 sets the height&lt;/strong&gt; (D_800c3f5c).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Then, for kind 1:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;with the camera in mode 1 (+0x4a, just set up), nothing more;&lt;/li&gt;
&lt;li&gt;else, but for the fifth argument -1, 0x80025e90, the follow setup: the eye 4096 behind the player at his height less 768 and the height, the camera in mode 1, then the reset (0x80026ddc).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kinds 6 to 11 of 0x800291f8&lt;/strong&gt; do nothing but the height. CameraSwitch never passes them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Seen.&lt;/strong&gt; Level 1, the player put in room 19 (byte 6) from room 23 (byte 8). The original&#39;s eye stands 200 over the look-at at once, at (24281, -189, 20153). The port&#39;s stood 1024 over it and would have taken about 90 frames to come down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The port&lt;/strong&gt; (&lt;code&gt;Game::room_change&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It keeps the byte switched to last (&lt;code&gt;camera_byte&lt;/code&gt;): 1 at a level&#39;s start and after a cine.&lt;/li&gt;
&lt;li&gt;It keeps whether the camera was just set up (&lt;code&gt;camera_set&lt;/code&gt;): from a level&#39;s start, or after the camera frame&#39;s own setup or the room&#39;s, until the next camera frame.&lt;/li&gt;
&lt;li&gt;A new follow byte, not 13, with the camera not just set up, now runs &lt;code&gt;set_camera_yaw&lt;/code&gt; (0x80025e90 and the reset) after the height is set.&lt;/li&gt;
&lt;li&gt;At a level&#39;s start the camera was just set up, so the start room&#39;s byte sets the height alone, as on the original. Level 1&#39;s start room is 23, byte 8: the original&#39;s eye stays 1024 over the look-at then.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 1 room 19 after a put: the eye&#39;s height is the original&#39;s on every frame, (24309, -189, 20153) at rest against the original&#39;s (24281, -189, 20153).&lt;/li&gt;
&lt;li&gt;The hub&#39;s run, turn and jump tests, which a snap at the level&#39;s start had broken in a first try.&lt;/li&gt;
&lt;li&gt;Every test file, and the hle suite.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Collectibles met by the walk&#39;s probes</title>
<id>https://toyz.github.io/rugrats/245-collectibles-met-by-the-walk-s-probes</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/245-collectibles-met-by-the-walk-s-probes"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>On the ground a collectible is now taken as the original takes it: the walk&#39;s probes meet its box, rather than the player coming within the model&#39;s radius plus his own.</summary>
<content type="html">&lt;p&gt;On the ground a collectible is now taken as the original takes it: the walk&#39;s probes meet its box, rather than the player coming within the model&#39;s radius plus his own. The walk takes no step on that frame.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the original does.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The box.&lt;/strong&gt; A collectible&#39;s set-up (0x80048a44) makes its model&#39;s first box the cube of 375 (worklog 244).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The probes.&lt;/strong&gt; The walk and the run (0x8005e1f4) call the move (0x80064a34). Its object probes (0x80063d3c) go over the nearby list and put the first object met in +0xf4.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The touch.&lt;/strong&gt; When the move answers 1 (held) and +0xf4 is of class 11 with a +0x58, it runs that (0x800514b0, the taking) and goes on to the end of the handler, at 0x8005e31c. No 0x8005caac: the step is not taken that frame.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seen on the oracle.&lt;/strong&gt; A hook on 0x800514b0 in level 1, running at goodie 3:
&lt;ul&gt;
&lt;li&gt;the touch is called from 0x8005e34c;&lt;/li&gt;
&lt;li&gt;the player stands at x 29055 two frames running, the take&#39;s and the next.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The port.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each frame &lt;code&gt;Game::touchable&lt;/code&gt; gives the player the collectibles not taken (not kind 16) as solids: the model&#39;s boxes turned by their angles, kind 12&#39;s model 0, sized as their set-up left them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Player::ground_move&lt;/code&gt; meets them after the other objects (&lt;code&gt;ground::objects&lt;/code&gt; over the list).&lt;/li&gt;
&lt;li&gt;Held by one, the player keeps it in &lt;code&gt;touched&lt;/code&gt;, its move is (0, 0), and it is not held for anything else (no bump, no slide).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Game::touch&lt;/code&gt; takes that one.&lt;/li&gt;
&lt;li&gt;In the air the old test is kept: within the model&#39;s radius plus the body&#39;s across, 1200 up or down.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tools.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The probes keep what they tried this frame (&lt;code&gt;Player::object_probes&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;tests/hle.rs &lt;code&gt;goodies_taken_as_the_original&lt;/code&gt; (GOODIE=level,k,away,side,heading,frames) prints the frame each side takes it, with GDBG=1 both sides&#39; probes and GREPORT=1 the frame-by-frame report.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 1&#39;s goodie 3, approached along x from 1500: the original takes it on frame 6 and the port on frame 7, and each stands still on the frame after.&lt;/li&gt;
&lt;li&gt;The frame between them is the camera. There the original&#39;s camera swings off the player&#39;s back as he runs (its heading 1024 to 585 over 11 frames, flags 0x9c0 to 0x2100000 and 0x300000, and its eye 200 above the look-at rather than 1024 even at rest). The port&#39;s camera stays behind him. So the original&#39;s player turns with the camera, its probes reach further across, and its last one meets the cube a frame sooner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The play tests&#39; collecting&lt;/strong&gt; (&lt;code&gt;collect&lt;/code&gt; in tests/play.rs) had, where no walk led to a pickup, put the player on it. Its probes then start inside the cube and never meet it, on the port as on the original (0x80067284: met past the start). After such a put it now goes 900 back the way it came, then walks at the pickup again. Levels 15, 17, 18 and the whole game are won again.&lt;/li&gt;
&lt;li&gt;Every test file, and the hle suite (41).&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Sizes in set-up order, and the collectibles&#39; box</title>
<id>https://toyz.github.io/rugrats/244-sizes-in-set-up-order-and-the-collectibles-box</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/244-sizes-in-set-up-order-and-the-collectibles-box"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>Worklog 243&#39;s leftover objects are resolved: an object&#39;s size is taken at its own set-up, before the set-up&#39;s changes to shared model boxes.</summary>
<content type="html">&lt;p&gt;Worklog 243&#39;s leftover objects are resolved: an object&#39;s size is taken at its own set-up, before the set-up&#39;s changes to shared model boxes. The port now keeps each placement&#39;s size as the original&#39;s set-up order leaves it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Found.&lt;/strong&gt; A hook on 0x80046570 during the boot (stack_check SETUP=template, &lt;code&gt;Play::boot_with&lt;/code&gt;) showed level 6&#39;s first spinner 1028 sized with its model&#39;s box at y ±557. The next two were sized with -1000. A stop on the write (RUGRATS_STOPW at the box) found the writer: 0x8004691c&#39;s template cases at 0x80047890 set 1028&#39;s first box top to -1000, after that object&#39;s own 0x80046570. Each object of the model set up after it is sized with the change, each one before it keeps the old size. The port already makes these box changes (&lt;code&gt;fix_up&lt;/code&gt;: 1028, 883, 381, 248 and the rest), but sized every object after all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The collectibles.&lt;/strong&gt; Their set-up (0x80048a44) makes the first box of their model the cube of 375 for every kind but 0 and 5 (kind 12 takes model 0 and changes that), then sizes them again: 649. Other objects sharing the model and set up after take 649 too: level 12&#39;s objects on model 0. The port never made this change, so collectibles&#39; models kept their boxes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The port:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fix_up&lt;/code&gt; makes the collectibles&#39; change in LP order.&lt;/li&gt;
&lt;li&gt;It records each placement&#39;s size before that placement&#39;s own changes (&lt;code&gt;Set::sizes&lt;/code&gt;); a copy takes its source&#39;s.&lt;/li&gt;
&lt;li&gt;An object&#39;s boxes carry their size (&lt;code&gt;Obj::size&lt;/code&gt;), which the solids&#39; bound and size take.&lt;/li&gt;
&lt;li&gt;The nearby and focus tests take the placement&#39;s size (&lt;code&gt;Game::size_of&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked.&lt;/strong&gt; stack_check OBJBOUNDS=1 matches each object to its placement by place and template. Every object of levels 1, 3, 6, 12 and 16 is the original&#39;s, but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the characters (1773 cube, +0x86 8192, set after);&lt;/li&gt;
&lt;li&gt;level 3&#39;s two 623s. Their record is 100&#39;s with 623&#39;s flags (worklog&#39;s fix_up rename), so the tool finds no placement of 623 and compares with 623&#39;s model; the port&#39;s own size is 100&#39;s, the original&#39;s 886.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every test file, and the hle suite.&lt;/p&gt;
</content>
</entry>
<entry>
<title>An object&#39;s size and bound as 0x80046570 makes them</title>
<id>https://toyz.github.io/rugrats/243-an-object-s-size-and-bound-as-0x80046570-makes-them</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/243-an-object-s-size-and-bound-as-0x80046570-makes-them"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>An object&#39;s size (+0x86) and bound cube (+0x78..+0x84) are now computed as the original&#39;s 0x80046570, not taken from the model&#39;s radius.</summary>
<content type="html">&lt;p&gt;An object&#39;s size (+0x86) and bound cube (+0x78..+0x84) are now computed as the original&#39;s 0x80046570, not taken from the model&#39;s radius.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0x80046570&lt;/strong&gt; (&lt;code&gt;boxhit::bound_size&lt;/code&gt;). Over the object&#39;s model boxes (+0x88&#39;s list) it takes the least corner and the greatest corner, each axis over all the boxes. +0x86 is the floor square root (0x8001f4ec) of the larger of their two squared lengths. With no boxes the bound is the cube of 1024&#39;s (1773: the characters&#39; cube; their +0x86 of 8192 is set after, by 0x8004822c, 0x800596e8 and 0x80081bac). +0x7e is the same across x and z alone. Then the bound is the cube of +0x86 about the object&#39;s place.&lt;/p&gt;
&lt;p&gt;0x8004691c, every object&#39;s set-up, calls it. The collectibles&#39; set-up (0x80048a44) first writes ±375 into their model&#39;s shared box list, which gives their 649.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checked&lt;/strong&gt; with stack_check OBJBOUNDS=1, every object of each level&#39;s room lists against its template&#39;s model&#39;s &lt;code&gt;bound_size&lt;/code&gt;. Characters, collectibles and the few below are left out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;level 1: 238 of 256 the same;&lt;/li&gt;
&lt;li&gt;level 3: 273 of 311;&lt;/li&gt;
&lt;li&gt;level 6: 155 of 178;&lt;/li&gt;
&lt;li&gt;level 12: 205 of 240.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Where the port used the model&#39;s radius for these, it now uses &lt;code&gt;bound_size&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this frame&#39;s solids&#39; bound and size (0x8006a714&#39;s screen);&lt;/li&gt;
&lt;li&gt;the focus test (0x8006af4c);&lt;/li&gt;
&lt;li&gt;a thing in flight as the characters&#39; tests see it;&lt;/li&gt;
&lt;li&gt;the nearby test for things, doors and placed objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For things the two agree, so worklog 242&#39;s things are unchanged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Left as the model&#39;s radius:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the draw&#39;s cull (0x80031b84);&lt;/li&gt;
&lt;li&gt;the particle and touch radii, which are the model&#39;s own.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt; every test file, and the hle suite.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The nearby test, and no flight outside the rooms</title>
<id>https://toyz.github.io/rugrats/242-the-nearby-test-and-no-flight-outside-the-rooms</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/242-the-nearby-test-and-no-flight-outside-the-rooms"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>The nearby test now measures as the original&#39;s (0x8006abbc).</summary>
<content type="html">&lt;p&gt;The nearby test now measures as the original&#39;s (0x8006abbc). A thing flight now starts only inside a room (0x8008864c), and a put-down with no flight is set on the floor (0x8005fdb4).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;0x8006abbc.&lt;/strong&gt; The test is against the object&#39;s bound box middle (+0x78..+0x84, turned, plus its place) and its +0x86. The original passes when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;across: d² is no more than (+0x86 + cylinder radius)², in 32-bit unsigned;&lt;/li&gt;
&lt;li&gt;up and down: within +0x86 of the cylinder&#39;s height.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What +0x78 and +0x86 hold.&lt;/strong&gt; stack_check OBJBOUNDS=1 printed every object in levels 1, 3, 6 and 16. Model objects mostly have a cube of the model&#39;s radius, with +0x86 that radius (154 of 178 in level 6). Every thing is such a cube (BOUNDS=1: level 3, 6 and 12&#39;s templates), so a thing&#39;s bound middle is its place. The ones that are not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;characters (a cube of 1773, +0x86 8192);&lt;/li&gt;
&lt;li&gt;collectibles (class 11, 649);&lt;/li&gt;
&lt;li&gt;a few big objects: 140, 185, 231 and 573 in level 1; 494, 623 and 883 in level 3; 1028 in level 6.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The port&#39;s change&lt;/strong&gt; (&lt;code&gt;Game::nearby&lt;/code&gt;). The port had measured from a thing&#39;s place with its farthest box corner, in floats. It now uses the model&#39;s radius and the integer compares, for things, doors and placed objects. For level 6&#39;s 1164 the corner was 484 and the radius is 483.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No room, no flight.&lt;/strong&gt; &lt;code&gt;start_flight&lt;/code&gt; finds the first room holding the start, as 0x8008864c does with no hint. Outside every room there is no flight, and the thing stays where it is at rest; a throw does nothing more (0x80060b44). A put-down then sets it at the player&#39;s floor (+0x148) plus the least top of its boxes (0x80062ac4 at 0x80062c64, 0x8005fdb4). With the lookup inclusive and unhinted, the clamp into the room&#39;s box never moves a start.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 1 in stack_check.&lt;/strong&gt; Two differences came from the boot, not the port:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the original refusing a put-down;&lt;/li&gt;
&lt;li&gt;the port not picking up a block.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The hle boot without cinematics leaves the hub&#39;s blocks (ids 55 to 59) shown. The port hides them at the start, as cine 7&#39;s end hook and EpisodeStart do (0x800732d4, 0x80078e7c).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checked:&lt;/strong&gt; every test file; the hle suite (40), and the thing tests after the room change.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The put-down&#39;s way ahead, and the object lists&#39; order</title>
<id>https://toyz.github.io/rugrats/241-the-put-down-s-way-ahead-and-the-object-lists-order</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/241-the-put-down-s-way-ahead-and-the-object-lists-order"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>The put-down now tests the way ahead as the original does (0x80060810).</summary>
<content type="html">&lt;p&gt;The put-down now tests the way ahead as the original does (0x80060810). A thing put down in front of an object comes back toward the player, as it does in the original; the port had set it down where the object stood. A level&#39;s or a cine&#39;s drop now goes through the same put-down and flies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What worklog 240&#39;s divergences were.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The original&#39;s 155 push-back.&lt;/strong&gt; It came from the tool: teleporting the player leaves the original&#39;s carried object (+0x18) where it was until its draw, and 0x80067ba4 stretches the body&#39;s box to it. Once stack_check moves the carried object with the player, neither side moves, and both refuse the plank&#39;s put-down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The port&#39;s 214 rise.&lt;/strong&gt; The tool had put the player inside the plank&#39;s footprint (the plank lies along z, 860 long).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The real difference showed up with both lined up.&lt;/strong&gt; Put down 700 before a plank, the pillar came to rest at z -24185 on the original and -23700 on the port.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;0x80060810&lt;/strong&gt; (objectives.md, the put-down&#39;s step 1; &lt;code&gt;Game::put_way&lt;/code&gt;):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The far end is the greatest z of the body&#39;s box together with the carried object&#39;s boxes in the player&#39;s frame (0x80060594).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The way runs from the player&#39;s place (with the animation&#39;s root) straight ahead to that far end.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;0x8006c438 tests the way against:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the world (0x8002cd7c);&lt;/li&gt;
&lt;li&gt;the first object of the nearby list it crosses (0x8006c334: list order, not flag 8, not the player nor the carried object).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The object&#39;s point wins when nearer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Met, the carried object moves by the hit less the far end, in x and z.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It was hooked in the oracle at 0x800608b0: the way from [104222, 838, -24180] to 778 ahead met plank 651 at -23887, pulling the pillar back by 485, and the drop flight&#39;s 96 did the rest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The put-down&lt;/strong&gt; (&lt;code&gt;Game::put_down&lt;/code&gt;, 0x80062ac4) is now shared by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the player&#39;s put-down;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drop_carried&lt;/code&gt;, which the levels&#39; and cines&#39; drops use (0x80072ef0 and 0x8007fd0c call 0x80062ac4(player, 1)). It had set the thing 100 above the player&#39;s feet with no flight.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Spike&#39;s put-down, not thrown, goes 1024 further ahead with line 190, as 0x80062ac4 has it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checked.&lt;/strong&gt; &lt;code&gt;things_put_down_as_the_original&lt;/code&gt; (tests/hle.rs), level 3: a tipped pillar picked up and put down 700 and 800 before a plank and 1500 off in the open. Its resting place is the original&#39;s in all three ([104211, 717, -24185], [104211, 717, -24184], [104212, 717, -24500]).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room lookup.&lt;/strong&gt; 0x8008864c&#39;s room lookup takes the first room holding the start (0x8002b660 with no hint, inclusive), so the clamp into that room&#39;s box never moves it. The clamp only matters for a hinted lookup, which the flights do not make.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worklog 240&#39;s lists, corrected.&lt;/strong&gt; 0x8001a1c0 is no ring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It puts a node {prev, next, item} at a list&#39;s head and returns the node as the new head; the head&#39;s prev is the tail.&lt;/li&gt;
&lt;li&gt;0x8001a640 and 0x8001a698 go down the nexts from the head, newest first, and 0x8001a534 pops the head.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What rested on a thing picked up falls newest first.&lt;/li&gt;
&lt;li&gt;In the think&#39;s case 2, the thing&#39;s own list is left as it was; the port had put the met object in it too.&lt;/li&gt;
&lt;li&gt;stack_check reads the lists that way.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The nearby list&#39;s build is also written down (objectives.md, Unknown):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0x8006add8 goes over the player&#39;s room&#39;s lists +0x48 and +0x4c, then each portal&#39;s room.&lt;/li&gt;
&lt;li&gt;0x8006abbc tests each object&#39;s bound box middle and +0x86.&lt;/li&gt;
&lt;li&gt;Each object it passes goes in at the head.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The port does not build it yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checked after.&lt;/strong&gt; The suites passed before the list fix: every test file, and hle 40. After it, the default tests and the two things_ hle tests were rerun and passed.&lt;/p&gt;
</content>
</entry>
<entry>
<title>Which rests on which, and GRAB&#39;s choice, as the original&#39;s</title>
<id>https://toyz.github.io/rugrats/240-which-rests-on-which-and-grab-s-choice-as-the-original-s</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/240-which-rests-on-which-and-grab-s-choice-as-the-original-s"/>
<updated>2026-10-23T00:00:00Z</updated>
<category term="play"/>
<summary>The stacking rule (0x8006c7a0) is decoded, ported and checked against the original.</summary>
<content type="html">&lt;p&gt;The stacking rule (0x8006c7a0) is decoded, ported and checked against the original. A thrown thing now rests on what its flight met, and GRAB now chooses its target as the original&#39;s 0x800631d8 does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The flight&#39;s +0x98.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is the last object the flight met that is not a character (object flag 0x4000). 0x80087594 and 0x80086ef0 set it at each meet.&lt;/li&gt;
&lt;li&gt;The port&#39;s &lt;code&gt;Thing::last_hit&lt;/code&gt; kept the opposite: the NPC or the player hit. Nothing read it, and &lt;code&gt;bounced&lt;/code&gt; was never read either.&lt;/li&gt;
&lt;li&gt;Both are replaced by &lt;code&gt;met&lt;/code&gt; (+0x98) and &lt;code&gt;on&lt;/code&gt; (+0x70). &lt;code&gt;Game::loads&lt;/code&gt; holds the +0x74 lists.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;0x8006c7a0&lt;/strong&gt; (objectives.md, &quot;Which rests on which&quot;; &lt;code&gt;things::stacking&lt;/code&gt;). For each of a&#39;s boxes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The box&#39;s middle goes into the world, then into b&#39;s frame.&lt;/li&gt;
&lt;li&gt;A b box holds it, grown across by the larger of the box&#39;s half-width and half-depth, and up and down by 3/4 of its height.&lt;/li&gt;
&lt;li&gt;The two half-heights then decide: 2 (b on a) or 1 (a on b).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The original&#39;s quirk is kept: once one box was inside, a later box is judged by b&#39;s last box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Checked.&lt;/strong&gt; &lt;code&gt;things_stack_as_the_original&lt;/code&gt; in tests/hle.rs moves thing a about thing b on a grid and compares each answer with the original&#39;s. It calls 0x8006c7a0 through &lt;code&gt;call_nested&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 3: planks and tipped pillars, 5 pairs.&lt;/li&gt;
&lt;li&gt;Level 6: boxes, 2 pairs.&lt;/li&gt;
&lt;li&gt;34587 calls in all, 5460 answers not 0, none off.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The think&#39;s answers&lt;/strong&gt; (0x8004df94 at 0x8004e018 and 0x8004e030):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1: the thing rests on what it met and joins its load.&lt;/li&gt;
&lt;li&gt;2: the object&#39;s +0x74 is made of the thing&#39;s list plus itself, and its +0x70 is the thing&#39;s. That list is empty, so the object ends up bearing itself and can never be touched again (&lt;code&gt;come_to_rest&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The pick-up&lt;/strong&gt; (0x800601e4): the thing leaves what it rested on, and what rested on it falls oldest first (0x80062928: (0, 0, 4096) unturned, at speed 10).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GRAB&lt;/strong&gt; (0x800631d8, &lt;code&gt;touched&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It skips a thing bearing a load.&lt;/li&gt;
&lt;li&gt;Going down the list, a thing whose first box rests on the nearest so far is taken over it at any distance. The nearest is forgotten even when the new one is out of reach. A thing under the nearest is passed over.&lt;/li&gt;
&lt;li&gt;The reach is now in integers:
&lt;ul&gt;
&lt;li&gt;the floor square root across (0x8001f4ec);&lt;/li&gt;
&lt;li&gt;the way&#39;s VectorNormal and ratan2 (0x8009f278, 0x800760f4) against the heading, strictly within D_800c430a.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The port had used a float hypot and atan2.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touched_any&lt;/code&gt; skips doors and placed objects bearing a load.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Also:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tests/flights.rs had not built since worklog 238, when &lt;code&gt;Flights::step&lt;/code&gt; gained its meets argument; the suites only ran lib, play and hle. It builds again. Its &lt;code&gt;a_thrown_thing_knocks_parts_off&lt;/code&gt; had set a thing flying with no flight record, which the record flights (&lt;code&gt;flown&lt;/code&gt;) never move; it now starts the flight with &lt;code&gt;start_flight&lt;/code&gt;. Every test file passes, and the hle suite (39) passes too.&lt;/li&gt;
&lt;li&gt;examples/stack_check: one thing put down onto another on both, with the oracle&#39;s think and the 0x8006c7a0 calls hooked.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>A launch&#39;s room, and the particles&#39; object touches</title>
<id>https://toyz.github.io/rugrats/239-a-launch-s-room-and-the-particles-object-touches</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/239-a-launch-s-room-and-the-particles-object-touches"/>
<updated>2026-10-22T00:00:00Z</updated>
<category term="play"/>
<summary>Two more of the flights&#39; &quot;not here&quot; are now done.</summary>
<content type="html">&lt;p&gt;Two more of the flights&#39; &quot;not here&quot; are now done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A launch&#39;s room.&lt;/strong&gt; 0x8008864c begins by looking up the room holding the start (0x8002b660 with no hint). With none it makes no flight; else it keeps the start inside that room&#39;s box (+0x10, +0x20) and the flight remembers the room (+0x38). &lt;code&gt;Flights&lt;/code&gt; now has the rooms&#39; boxes (set at a level&#39;s load) and does the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A flight&#39;s touch.&lt;/strong&gt; A flight of flag 0x40000 meeting an object runs the object&#39;s touch (+0x58), as a thrown thing&#39;s does: its parts (&lt;code&gt;touch_parts&lt;/code&gt;, touches 1-7), and touch 1&#39;s sound off the shared count. The particles&#39; object hits (&lt;code&gt;Flights::struck&lt;/code&gt;) now carry the object&#39;s place and the point met for it.&lt;/p&gt;
&lt;p&gt;The emitter places of levels 3, 5, 6, 16 and 29 are still all the original&#39;s (only the frames before the sync differ).&lt;/p&gt;
</content>
</entry>
<entry>
<title>Particle flights meet objects and hurt characters; class 17&#39;s enter sound</title>
<id>https://toyz.github.io/rugrats/238-particle-flights-meet-objects-and-hurt-characters-class-17</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/238-particle-flights-meet-objects-and-hurt-characters-class-17"/>
<updated>2026-10-22T00:00:00Z</updated>
<category term="play"/>
<summary>The particle flights now meet objects and characters as the thrown things do.</summary>
<content type="html">&lt;p&gt;The particle flights now meet objects and characters as the thrown things do. Level 6&#39;s flying toys hurt the player where the original&#39;s do, and level 6&#39;s class 17 toys sound as the player comes near.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&gt; (0x80086340). The class 10 callbacks launch flights of flag 0x20000 (in their 0xb0000 and 0xf0000 masks). The original tests those against the objects of the flight&#39;s room as well as the world, through 0x80086668. The port&#39;s flights tested the world only:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the 905 toys&#39; models, which carry damage 35 and flag 0x80000, flew through the player;&lt;/li&gt;
&lt;li&gt;the drops passed through the furniture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The test.&lt;/strong&gt; &lt;code&gt;Flights::step&lt;/code&gt; takes an object test, built from &lt;code&gt;things::meets&lt;/code&gt;, the same 0x80086668 the thrown things fly by (objects first, the world kept over them only when nearer on all three axes; the boxes grown by the radius with flag 0x40000000). The stop test&#39;s floor goes through it too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Damage.&lt;/strong&gt; What a flight meets comes back in &lt;code&gt;Flights::struck&lt;/code&gt;. With flag 0x80000 a character takes the flight&#39;s damage (+0x8e, 0x800780ac): the player through &lt;code&gt;post_damage&lt;/code&gt;, a character through &lt;code&gt;post_npc_damage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bounce and marks.&lt;/strong&gt; It bounces off an object as off the world. Only world hits leave marks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Checked: standing at (-56493, -2440, -3042) in level 6, 905&#39;s models hit the player for 35 at frames 35, 62, 89 and 116 on both sides; the flights are all the original&#39;s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Class 17&lt;/strong&gt; (0x80049d98, level 6&#39;s 79, 80 and 81 toys): its enter (0x8004d174) plays the template&#39;s +0x24 sound at it. The port&#39;s trigger enter had done nothing, so level 6 was missing sound 81 there.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The flights&#39; second mark from the stop test, and npc_check&#39;s put on the floor</title>
<id>https://toyz.github.io/rugrats/237-the-flights-second-mark-from-the-stop-test-and-npc-check-s</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/237-the-flights-second-mark-from-the-stop-test-and-npc-check-s"/>
<updated>2026-10-22T00:00:00Z</updated>
<category term="render"/>
<summary>The flights&#39; marks are now the original&#39;s mark for mark, and npc_check&#39;s put no longer leaves its own artifacts.</summary>
<content type="html">&lt;p&gt;The flights&#39; marks are now the original&#39;s mark for mark, and npc_check&#39;s put no longer leaves its own artifacts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two marks a hit.&lt;/strong&gt; The original made two marks where the port made one, about 57 apart. It is not the room walk: 0x80086668 walks rooms only for the objects and tests the world once. It is the stop test (0x80087414). That checks for a floor within the flight&#39;s radius under its backed-off place, through 0x80086668 again, which marks the floor it finds. The port&#39;s stop test now reports that floor hit too, and the flight marks it. npc_check MARKDBG=1 by level 3&#39;s drops shows the same eight lasting marks, in the same order, at the same places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npc_check&#39;s put&lt;/strong&gt; now puts both players:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on the floor under the given place (PUTAIR=1 keeps the height);&lt;/li&gt;
&lt;li&gt;with the body&#39;s matrix (+0x04) and the speed cleared, as the oracle tests&#39; put.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The port&#39;s player no longer fell and landed (the stray sound 11), and the camera stays with the original&#39;s. In level 3 by the 614 source the voices&#39; turns are then the same on all 149 frames, and in levels 1, 3 and 4 by their emitters every flight and sound is the original&#39;s.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The looping sound sources as the original&#39;s, and placed sounds panned to the right side</title>
<id>https://toyz.github.io/rugrats/236-the-looping-sound-sources-as-the-original-s-and-placed</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/236-the-looping-sound-sources-as-the-original-s-and-placed"/>
<updated>2026-10-22T00:00:00Z</updated>
<category term="audio"/>
<summary>The looping sound sources now play as the original&#39;s, and every placed sound now pans to the correct side: the port had them mirrored.</summary>
<content type="html">&lt;p&gt;The looping sound sources now play as the original&#39;s, and every placed sound now pans to the correct side: the port had them mirrored.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;retro_rt.&lt;/strong&gt; Approved and pushed (retro_rt worklog 19, c018dec): &lt;code&gt;Spu::key_on_voice&lt;/code&gt;, &lt;code&gt;set_voice_volume&lt;/code&gt;, &lt;code&gt;key_off&lt;/code&gt;, &lt;code&gt;voice_on&lt;/code&gt;. rugrats&#39; lock moves to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The sources&lt;/strong&gt; (&lt;code&gt;world::game::sources&lt;/code&gt;, sound.md &quot;Sound sources&quot;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Records:&lt;/strong&gt; 0x800e3218, 40 bytes each.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Starts:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;the loader&#39;s LP kind 3 records (new: the sound the template word&#39;s high half, near and far the rotation&#39;s first two words, the volume its third&#39;s low half), in LP order with the object set-up&#39;s;&lt;/li&gt;
&lt;li&gt;template 1178&#39;s, whose sound is drawn rand % 3 at load;&lt;/li&gt;
&lt;li&gt;level 4&#39;s start adds one; level 10&#39;s empties the list and adds three.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sounds:&lt;/strong&gt; the level&#39;s table 0x800b847c.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Each frame after the draw&lt;/strong&gt; (0x8006f88c), each source is heard through its own falloff (0x8006fd68 with its record):
&lt;ul&gt;
&lt;li&gt;keyed on a free voice of 15, 16 and 17 (0x800c4be8 = 7);&lt;/li&gt;
&lt;li&gt;turned (SsUtSetVVol);&lt;/li&gt;
&lt;li&gt;keyed off out of hearing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leaving the level&lt;/strong&gt; silences them (0x8006f828 from 0x80094bfc).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In level 3 the original runs 15 sources where the port, without the kind 3 records, had 5.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The pan.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The facing.&lt;/strong&gt; 0x8006f88c and 0x8006f4c0 take the heading the camera faces as the bearing toward the eye plus the matrix&#39;s third column (0x80010b54 copies it transposed). The port had used the third row, so every placed sound was panned to the wrong side.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The bearing&lt;/strong&gt; is the original&#39;s integer one: &lt;code&gt;Trig::bearing&lt;/code&gt;, 0x80021590, floor square root then asin or acos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checked&lt;/strong&gt; with npc_check SRCDBG=1, which hooks 0x800b0788 and compares the voices&#39; turns each frame. In level 3 by the 614 source they are the same wherever the cameras are the same (70 frames); the rest follow the camera drifting after the tool&#39;s put.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Also:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Game::state_block&lt;/code&gt; starts from the progress passed in, so a game outside the app hears at the default volumes (sources at volume 0 were never keyed);&lt;/li&gt;
&lt;li&gt;npc_check syncs the sources&#39; voices and on flags, and compares their key-ons with the other sounds.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Doors scheduled by their triggers as the original&#39;s, level 26&#39;s roar synced; the sweep&#39;s sounds and flights all the original&#39;s in 24 levels</title>
<id>https://toyz.github.io/rugrats/235-doors-scheduled-by-their-triggers-as-the-original-s-level</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/235-doors-scheduled-by-their-triggers-as-the-original-s-level"/>
<updated>2026-10-19T00:00:00Z</updated>
<category term="engine"/>
<summary>Doors now run by their trigger schedules, level 26&#39;s roar keeps the original&#39;s time, and the npc_check sync is wider.</summary>
<content type="html">&lt;p&gt;Doors now run by their trigger schedules, level 26&#39;s roar keeps the original&#39;s time, and the npc_check sync is wider. With that, the oracle sweep at SYNCAT=1 has no sound or flight differences left in 24 of the 28 levels it runs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Doors.&lt;/strong&gt; The original schedules a door&#39;s think in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Class 8&lt;/strong&gt; (0x80049e18 in, 0x80049e5c out, +0x9c): scheduled while its trigger entry is in. So it starts when the five-a-frame pass reaches it. The port had polled the player&#39;s distance; level 3&#39;s hinged 160 then started eight frames early and closed (sound 54) early.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An automatic door&lt;/strong&gt; (template flag 0x40000000, golf&#39;s 770: enter 0x8004f6d8, +0xcc): scheduled on its enter, which leaves the &quot;in&quot; bit clear. So the pass enters it again each time round and never leaves it: it cycles for good once the player has come near.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Game::door_entered&lt;/code&gt; does both from the trigger pass; a door off the list keeps the distance test. Level 24&#39;s 770 sounds 136 and 135 on its cycle exactly as the original does (doors.md had reckoned 136 never plays).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 26&#39;s roar&lt;/strong&gt; (0x80085168, 0x800c4ce4) was right but never synced; &lt;code&gt;Script::set_countdown&lt;/code&gt; lets tools set the count (golf&#39;s roar, the hub&#39;s barks, level 10&#39;s lines).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npc_check:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;doors are matched by the nearest object of their template (+0xc0 is not every door&#39;s start);&lt;/li&gt;
&lt;li&gt;a door&#39;s &lt;code&gt;moving&lt;/code&gt; is synced from +0xcc or +0x9c;&lt;/li&gt;
&lt;li&gt;the level&#39;s countdown is synced.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The sweep&lt;/strong&gt; (FLIGHTS, SOUNDS, SYNCAT=1, 300 frames):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 flights and 0 sounds off in levels 1-7, 10-18, 23-31.&lt;/li&gt;
&lt;li&gt;Levels 20 and 21 want CINES=1 (the round starts on cine 63&#39;s end): level 20 then matches.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>The render&#39;s rooms, the spinners&#39; and characters&#39; hit callbacks, the flights&#39; marks and splashes, the shared count</title>
<id>https://toyz.github.io/rugrats/234-the-render-s-rooms-the-spinners-and-characters-hit</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/234-the-render-s-rooms-the-spinners-and-characters-hit"/>
<updated>2026-10-19T00:00:00Z</updated>
<category term="engine"/>
<summary>The hit callbacks beyond the class 10 objects, the render&#39;s walk of rooms and portals, the flights&#39; marks and splashes, and the shared sound count now all run in the port as in the original.</summary>
<content type="html">&lt;p&gt;The hit callbacks beyond the class 10 objects, the render&#39;s walk of rooms and portals, the flights&#39; marks and splashes, and the shared sound count now all run in the port as in the original.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hit callbacks on other objects.&lt;/strong&gt; Any template with +0x09 gets +0x50 and the +0x98 record (&lt;code&gt;emitters::record&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The spinners (0x8004e2ac) call it each think. Their think also changed:
&lt;ul&gt;
&lt;li&gt;it runs only while scheduled by the trigger (the port had used its own 2D distance);&lt;/li&gt;
&lt;li&gt;and only while drawn in the last six frames (+0x60);&lt;/li&gt;
&lt;li&gt;the angles turn by the VSyncs since the last think (+0x64), not two a frame.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Callback 1 (0x80053604, levels 3 and 16): model 932 along the spinner&#39;s turning angle, and sound 150/178 off the shared count.&lt;/li&gt;
&lt;li&gt;The characters (0x80084084) call it at each think&#39;s start, from where they are then:
&lt;ul&gt;
&lt;li&gt;callback 11, the level 5 robots&#39; glow (its own LCG, % 250);&lt;/li&gt;
&lt;li&gt;callback 8, level 2&#39;s template 20.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Character 23 sounds 51 at its event 0x8000000.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The render&#39;s rooms&lt;/strong&gt; (&lt;code&gt;world::game::visible&lt;/code&gt;, 0x80034058 -&amp;gt; 0x80033ac4):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the camera&#39;s room, the portals (now in &lt;code&gt;Set&lt;/code&gt;), the stamps;&lt;/li&gt;
&lt;li&gt;the 65000 cut, the near-portal box (0x800307f8);&lt;/li&gt;
&lt;li&gt;the z-0 cut and projection (0x8003311c, 0x80033278);&lt;/li&gt;
&lt;li&gt;the first room&#39;s list, and the depth bands.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It gives the spinners&#39; draw stamp (0x80031b84 against the room&#39;s rectangle) and runs the kind 16 collectibles&#39; think from their draw (0x800324a0: level 4&#39;s 1141 sound 146).&lt;/p&gt;
&lt;p&gt;npc_check VISDBG=1 compares the rooms and rectangles each frame. With the follow camera synced (new &lt;code&gt;Game::sync_follow&lt;/code&gt;), all frames match in 24 levels; levels 20, 26 and 28 differ only through the tool&#39;s camera inputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flights meeting the world:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They call their +0x80. 706&#39;s drops carry the object&#39;s +0x5c, the table 0x800b7ea4 by template +0x0b. 2 is the splash 0x800529cc, five sprites 275 about the normal; 1 is the pie&#39;s splat.&lt;/li&gt;
&lt;li&gt;They make their mark in the pool:
&lt;ul&gt;
&lt;li&gt;8 marks, newest first, the oldest taken when full;&lt;/li&gt;
&lt;li&gt;+0x66&#39;s marks last, the rest are drawn once;&lt;/li&gt;
&lt;li&gt;209, 212 and 218 run sequences, and +0x65 makes them semi-transparent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The shared count 0x800c4ba0:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the sliding doors&#39; think (0x8004e458) takes dt off it;&lt;/li&gt;
&lt;li&gt;the level starts set it: 1, 7 and 29 100; 3 and 24 1000; 4 and 23 500;&lt;/li&gt;
&lt;li&gt;kind 16 collectibles use it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Smaller fixes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 16&#39;s sparklers&#39; trigger radius is the template&#39;s 15000. The set-up&#39;s 2500 is overwritten later.&lt;/li&gt;
&lt;li&gt;Their sparkles, the hub signs&#39; and the lobster&#39;s turn by the object&#39;s matrix.&lt;/li&gt;
&lt;li&gt;The class 10 enter beats a placement&#39;s speech line.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;VSync.&lt;/strong&gt; The thinks read the count the frame began at (&lt;code&gt;Game::vsync_lag&lt;/code&gt;, 2). The oracle&#39;s emulated timing reads one later in some runs; the tools copy that from an emitter&#39;s or spinner&#39;s +0x64 parity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npc_check syncs all placements&#39; trigger states, the spinners, the callbacks&#39; tenth seed (0x800c41b8), the ambience count and the follow camera.&lt;/li&gt;
&lt;li&gt;It also has EMITDBG, SPINDBG, VISDBG, PORTALS, TRIGLIST and MARKDBG.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emitters_launch_as_the_original&lt;/code&gt; now also covers level 3&#39;s and 16&#39;s spinners with level 16&#39;s sparkles (482 launches), and level 4&#39;s drops with their splashes. All 9 places are the original&#39;s.&lt;/li&gt;
&lt;li&gt;Level 5&#39;s robots are all the original&#39;s in npc_check (739 launches, 15 sounds). The Both harness lacks the characters&#39; body sync.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>The class 10 emitters as the original&#39;s: their clocks, schedule and callbacks</title>
<id>https://toyz.github.io/rugrats/233-the-class-10-emitters-as-the-original-s-their-clocks</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/233-the-class-10-emitters-as-the-original-s-their-clocks"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="engine"/>
<summary>The class 10 objects (think 0x8004e0f4) now run in the port as the original&#39;s: their clocks, their schedule and every callback the levels use, each launch checked against the oracle.</summary>
<content type="html">&lt;p&gt;The class 10 objects (think 0x8004e0f4) now run in the port as the original&#39;s: their clocks, their schedule and every callback the levels use, each launch checked against the oracle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The think.&lt;/strong&gt; A class 10 object is on the trigger list with its own enter (0x8004cfd8: record +0x00 = 1, scheduled) and leave (0x80049e5c). While scheduled, each think starts its clock (+0x64 from VSync(-1)) after an enter or a go, and goes when the count is past it by more than the period (template +0x18, VSyncs). 969 and 982 hurt the player first when near (0x900000 / 0x400000 squared), which replaces level 6&#39;s stand-in bursts. The class 10 enter is set after the set-up&#39;s speech line, so it wins over a talker (level 6&#39;s 905 at LP 46 had one, and went 25 frames early).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The callbacks&lt;/strong&gt; (0x800b7eb0 by template +0x09), in &lt;code&gt;world::game::emitters&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 (577) and 10 (982): &lt;code&gt;count&lt;/code&gt; drops at random speeds, the object&#39;s matrix less its third row;&lt;/li&gt;
&lt;li&gt;3 (611): sound 149, a drop that sounds 133 (177 in level 16) and leaves mark 263;&lt;/li&gt;
&lt;li&gt;4 (614): spinning model 931;&lt;/li&gt;
&lt;li&gt;5 (615): one of six sprites;&lt;/li&gt;
&lt;li&gt;6 (616, 1139): bubbles, and the shared count 0x800c4ba0&#39;s sounds;&lt;/li&gt;
&lt;li&gt;7 (706): model 1040 down, sound 147, mark 218;&lt;/li&gt;
&lt;li&gt;9 (682): model 1016, mark 212;&lt;/li&gt;
&lt;li&gt;13 (969): six sprites, a random cone, the period as a delay;&lt;/li&gt;
&lt;li&gt;14 (905) and 15 (906-908): a model along the object&#39;s matrix (0x8009f6f8);&lt;/li&gt;
&lt;li&gt;18 (1215): model 1628, bounce 2500.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each has its own seed (0x800c41a8..0x800c41c8, 0x800c41d8, all 48732). A launch now carries its sound (+0x64) and mark (+0x46, +0x65..+0x68). The flights play the sound where they meet something (0x80087594); the marks are kept but not drawn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the oracle showed besides:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The update list (0x800c43b8) is head-inserted and walked from the head, so the latest scheduled thinks first. Level 6&#39;s 969 and 982s going in one frame draw their cones in that order.&lt;/li&gt;
&lt;li&gt;The think reads VSync(-1) during the frame. In npc_check&#39;s frames that is the frame-end count less one, so the port&#39;s think reads &lt;code&gt;Game::vcount&lt;/code&gt; less one.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Flights::launched&lt;/code&gt; is now cleared each frame; it grew without end outside the tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tools.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npc_check FLIGHTS=1 compares every launch, the model kinds without their unset sprite and size, and with sound and mark.&lt;/li&gt;
&lt;li&gt;It syncs the emitters&#39; clocks, schedules and order, the trigger cursor and the seeds at the first sync frame (SYNCAT=1 avoids the boot&#39;s half frame).&lt;/li&gt;
&lt;li&gt;EMITDBG=1 prints both sides&#39; clocks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;emitters_launch_as_the_original&lt;/code&gt; runs five places in levels 29, 1, 3 and 6: 156 launches, all the original&#39;s.&lt;/p&gt;
&lt;p&gt;gameplay.md has &quot;Emitters (class 10, 0x8004e0f4)&quot;. Levels 1, 4, 6 and 7 and objectives.md are updated.&lt;/p&gt;
</content>
</entry>
<entry>
<title>Level 5&#39;s robot bite, the cry and the respawn as the original&#39;s, frame for frame</title>
<id>https://toyz.github.io/rugrats/232-level-5-s-robot-bite-the-cry-and-the-respawn-as-the</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/232-level-5-s-robot-bite-the-cry-and-the-respawn-as-the"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="play"/>
<summary>Level 5&#39;s robot biting Angelica, her cry and her respawn now play as the original&#39;s, frame for frame, over 400 frames.</summary>
<content type="html">&lt;p&gt;Level 5&#39;s robot biting Angelica, her cry and her respawn now play as the original&#39;s, frame for frame, over 400 frames.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The scene.&lt;/strong&gt; &lt;code&gt;level_5_robot_bites_as_the_original&lt;/code&gt; sets up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the oracle without lag frames, booted with its cinematics (cine 36&#39;s end sets the respawn, 0x8007b280);&lt;/li&gt;
&lt;li&gt;Angelica put 1200 short of robot 13000 on its floor;&lt;/li&gt;
&lt;li&gt;the robot synced, scheduled (+0x9c) and the trigger cursor set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The robot sinks to her and bites, she cries at 0 and starts again at the respawn. Her health and her height, lift and all, are compared every frame.&lt;/p&gt;
&lt;p&gt;Four differences came out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The cry&#39;s lift.&lt;/strong&gt; The cry&#39;s start runs the body&#39;s update after asking animation 16 (0x8007f920), which puts that step&#39;s lift into +0x1c: she sinks 18. The port stepped the animator alone. &lt;code&gt;Player::step_animation&lt;/code&gt; (the animator&#39;s step and the root&#39;s) is new, and &lt;code&gt;Game::cry&lt;/code&gt; uses it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The respawn a frame late.&lt;/strong&gt; The cry&#39;s loop returns after its last VSync wait, so the respawn and the rest of that frame come in the frame after its 2100 dt run out. The port respawned in that last frame. &lt;code&gt;Game::cry_over&lt;/code&gt; holds the respawn to the next frame, which then plays on as any other.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The stand asked at the loop&#39;s end&lt;/strong&gt; (0x800572a8(player, 0, 4096)), so that frame&#39;s body update plays its first frame. The port asked it only in the stand&#39;s handler, after the step, a frame late.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No lift left after the respawn.&lt;/strong&gt; The respawn writes +0x18..+0x20 whole (0x80078d60). The port kept the cry&#39;s lift in &lt;code&gt;root_y&lt;/code&gt;. The checkpoint respawn and level 5&#39;s own put now clear it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The player&#39;s root also takes a state left only by what the step spent on it (&lt;code&gt;Animator::left_from&lt;/code&gt;, as the characters&#39; since worklog 223). A state asked anew between steps gives nothing, where the port gave the rest of any state left on its last frame, a cry held there too.&lt;/p&gt;
&lt;p&gt;gameplay.md has the cry (&quot;The cry (ForceCry&#39;s loop, 0x8007f920)&quot;).&lt;/p&gt;
</content>
</entry>
<entry>
<title>The object set-up&#39;s changes to templates, models, places and copies; golf&#39;s ball and the guards&#39; bite by the full line test</title>
<id>https://toyz.github.io/rugrats/231-the-object-set-up-s-changes-to-templates-models-places-and</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/231-the-object-set-up-s-changes-to-templates-models-places-and"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="play"/>
<summary>The object set-up&#39;s changes to the shared tables, the objects&#39; places and the copies it makes are now the port&#39;s too.</summary>
<content type="html">&lt;p&gt;The object set-up&#39;s changes to the shared tables, the objects&#39; places and the copies it makes are now the port&#39;s too. The templates and model boxes equal the oracle&#39;s in every level checked, and golf&#39;s door runs at the original&#39;s speed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What 0x8004691c does beyond the object.&lt;/strong&gt; Every object of a loaded placement runs it, in the LP&#39;s order, and it writes into tables all objects share:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the template table (file 239, loaded once at boot at 0x80118a0c in the oracle);&lt;/li&gt;
&lt;li&gt;the models&#39; boxes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Per level (its switch at 0x80046e5c) it also moves some objects, renames one, gives ids, makes copies and starts sound sources. The full table is in gameplay.md, &quot;What the set-up changes beyond the object&quot;. The main lines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flags.&lt;/strong&gt; It clears 64 (climbable) on 248, 381, 960 and the two renamed 100s. It sets 8 (no object test) on 125, 527, 1244, 61, 687 and 1189.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template +0x24.&lt;/strong&gt; A pickable template with none gets 19, and 1164, 954 and 678 get their own values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template +0x1a.&lt;/strong&gt; The automatic doors&#39; speeds become 50 (770 in level 24) and 60 (855 in level 5). The golf door&#39;s cycle was 94 frames in the port, against the original&#39;s 72.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boxes.&lt;/strong&gt; It changes boxes of 381, 248, 883, 1070, 1028, 960, 425, 1185, 653, 1182-1184, 1264, 232, 712 and 1168.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copies.&lt;/strong&gt; Level 15 gets eight blocking copies, level 3 three, levels 10 and 18 one each. Levels 3, 4 (&quot;Created 2 oildrums&quot;), 7, 11 and 12 get drawn copies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ids.&lt;/strong&gt; 1231 becomes 524 (one of level 5&#39;s cine actors) and 473 becomes 519. Level 5&#39;s TV robots are numbered from 13000.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pickables.&lt;/strong&gt; 402 and 948 stop being pickable, and 1128 and 795 start.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Found with &lt;code&gt;npc_check&lt;/code&gt; TEMPLATES=1 and the oracle&#39;s new write watch (&lt;code&gt;RUGRATS_STOPW&lt;/code&gt;, worklog 230): the golf door&#39;s 50 was written at 0x80047418, a case of the set-up&#39;s switch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The port&lt;/strong&gt; (&lt;code&gt;world/fixups.rs&lt;/code&gt;, &lt;code&gt;Set::fix_up&lt;/code&gt; at the end of &lt;code&gt;Set::load&lt;/code&gt;, so play and the cines both see it):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Objects.&lt;/strong&gt; An object&#39;s flags are its placement&#39;s, computed as the original does. Pickability now comes from them, where it came from the template.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copies.&lt;/strong&gt; A copy is a placement more. A blocking one is not drawn (&lt;code&gt;Placement::drawn&lt;/code&gt;), and &lt;code&gt;objects_except&lt;/code&gt; skips it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Renamed objects.&lt;/strong&gt; The two renamed 100s get a set-up template: 100&#39;s record with 623&#39;s flags. Only their +0x96 changes; their model and calls were already 100&#39;s.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sound sources.&lt;/strong&gt; These are kept as &lt;code&gt;Set::sound_sources&lt;/code&gt; (0x8006fe04: sound, place, near, far, volume). Nothing plays them yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New:&lt;/strong&gt; &lt;code&gt;Models::get_mut&lt;/code&gt; and the tool &lt;code&gt;fixup_check LEVEL&lt;/code&gt;. It lists every object of the original&#39;s room lists without a placement of its template there, and the templates and model boxes that differ. In levels 1, 3-7, 10-12, 15, 16, 18, 20, 23, 24, 27 and 28 only spawned or scripted objects remain: collectibles, health, eggs, Cynthia&#39;s spot, characters settled on their floors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Golf&#39;s ball meets objects as the original&#39;s does.&lt;/strong&gt; With 1264&#39;s real box, the port&#39;s stand-in object test (any box the ball&#39;s edge entered turned it back) stopped every shot of hole 23. 0x80086668 tests the ball&#39;s centre segment, boxes not grown (only flag 0x40000000 grows them), and keeps the world only when nearer on every axis. &lt;code&gt;Game::ball_meets&lt;/code&gt; now does that through the flights&#39; &lt;code&gt;flight_meets&lt;/code&gt;, the ball bouncing off the normal as off the world. Hole 23 plays with its old shots again. &lt;code&gt;golf_course_search&lt;/code&gt; takes GOLF_HOLES and GOLF_PAR_EXTRA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A guard bites when its line test meets an object (0x80085220).&lt;/strong&gt; The guard&#39;s attack runs 0x80090554, the full line test (objects first, the world kept only when nearer on every axis), from its +0x18 (the lift in it) to the player. It attacks only when the test meets an object: the player, or anything but 1177 or 843. Nothing met (0) or the world (1) keep it still. The port tested the world alone, from its place without the lift. A robot of level 5 floating over Angelica on a floor the segment crossed never bit, where the original&#39;s bites her from frame 11. &lt;code&gt;Npc::line_test&lt;/code&gt; and &lt;code&gt;Line&lt;/code&gt; are new, and &lt;code&gt;Obstacle&lt;/code&gt; carries its template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tests.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;level_5_robot_wakes_as_the_original&lt;/code&gt; finds its robot by its placement (&lt;code&gt;set_up_id&lt;/code&gt;: LP id 2000 is now 13000-something on both sides).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;level_5_ambience_as_the_original&lt;/code&gt; had synced no robot at all. With ids 0 it took the nearest object of id 0 and put Angelica on that object&#39;s floor. It now leaves her where she starts, plays both sides from the start, and raises the oracle&#39;s frame budget (its lines had been a frame early where the original took dt 15 frames). All seven lines match on their frames.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Sounds.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 24&#39;s door sounds all match (8).&lt;/li&gt;
&lt;li&gt;Levels 26 and 29 are still off:
&lt;ul&gt;
&lt;li&gt;Level 26: the golf Reptar&#39;s roar timer (0x800c4ce4) is a global the oracle carries in from before.&lt;/li&gt;
&lt;li&gt;Level 29: class 10&#39;s think (0x8004e0f4) calls its +0x50 callback every period. For template 616 that is 0x80053344, a random sound (134, 143, 140) on the shared countdown 0x800c4ba0, and particles. The port has class 10 only in level 14&#39;s own code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Every body&#39;s steps sound as the original&#39;s and the frames&#39; events are the set-up&#39;s; the sweep compares the sounds</title>
<id>https://toyz.github.io/rugrats/230-every-body-s-steps-sound-as-the-original-s-and-the-frames</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/230-every-body-s-steps-sound-as-the-original-s-and-the-frames"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="play"/>
<summary>Every body&#39;s steps now sound as the original&#39;s.</summary>
<content type="html">&lt;p&gt;Every body&#39;s steps now sound as the original&#39;s. The frames&#39; events are rewritten as the original&#39;s set-up leaves them. The sweep tool compares the sounds each side keys on, and they match in every level it can run but golf&#39;s door phase and level 21&#39;s last notes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The sound comparison (&lt;code&gt;npc_check&lt;/code&gt; SOUNDS=1).&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The original&#39;s key-ons are hooked at 0x800a99a8: program, tone, and the note at sp + 0x10. Every effect (0x8006f26c, 0x8006f644), the crunch and the piano pass through it.&lt;/li&gt;
&lt;li&gt;The port&#39;s effects are mapped through the original&#39;s table (0x800b84fc, 3 bytes a sound).&lt;/li&gt;
&lt;li&gt;A sound a frame later in the original still counts, since the trigger pass runs after the oracle&#39;s frame ends.&lt;/li&gt;
&lt;li&gt;SOUNDRA=1 prints the callers.&lt;/li&gt;
&lt;li&gt;The tool now syncs the game&#39;s rand (0x800c3f94) and the doors&#39; cycles (+0xa8..+0xac, +0x3c bit 2, +0xcc, +0xd2: &lt;code&gt;Door::sync&lt;/code&gt;) at its frame 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The footstep (0x8007013c).&lt;/strong&gt; Out of water a step keys program 0, tone 0, note 22 + (rand &amp;amp; 7), drawing the game&#39;s rand only when heard (0x8006f4c0: within 0xfffe across and 0x7fff up of the camera, whatever the volume). The port&#39;s comment had it as a table that is empty in play, so nothing played.&lt;/p&gt;
&lt;p&gt;The step&#39;s sound by path (0x8005e1f4):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Running&lt;/strong&gt; (0x8005e830), by the player&#39;s character (0x800c5680):
&lt;ul&gt;
&lt;li&gt;Spike in water wades (57 on every fifth step in level 4), plays 176 in level 16, else 27.&lt;/li&gt;
&lt;li&gt;Character 34 plays 108.&lt;/li&gt;
&lt;li&gt;Others play 27 in water, but the footstep in the hub.&lt;/li&gt;
&lt;li&gt;Out of water it is the footstep.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Walking&lt;/strong&gt; (0x8005e920), by the body&#39;s own character and template: character 34 plays 108, Reptar (template 8) puffs then steps, the rest step.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;player::step_sound&lt;/code&gt; holds the rule.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The characters step too.&lt;/strong&gt; Their bodies run the same handlers. &lt;code&gt;Npc::stepped&lt;/code&gt; records a step in &lt;code&gt;drive_body&lt;/code&gt;. &lt;code&gt;npc::step&lt;/code&gt; sounds it before the next character&#39;s update, drawing a heard footstep&#39;s note from the rand there, as the original&#39;s body update does. &lt;code&gt;Event::Step&lt;/code&gt;, &lt;code&gt;Event::Wade&lt;/code&gt; and &lt;code&gt;Event::Footstep&lt;/code&gt; go to &lt;code&gt;Game::npc_steps&lt;/code&gt;. &lt;code&gt;Npc::template&lt;/code&gt; is new. &lt;code&gt;World&lt;/code&gt; gains the player&#39;s character and the listener (the camera).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The frames&#39; events (0x800596e8, 0x800573b0).&lt;/strong&gt; The player&#39;s set-up rewrites events in the shared frame table every level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;animation 9: frame 2 none, frame 3 a step (256);&lt;/li&gt;
&lt;li&gt;animation 5: frame 2 none;&lt;/li&gt;
&lt;li&gt;animations 232-235: frame 2 none;&lt;/li&gt;
&lt;li&gt;animation 234: frame 8 a grab (2048).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The landing&#39;s entry (state 10, 0x8005b2d8) sets 190&#39;s frame 3 to a step. The port read the file&#39;s events, so level 11&#39;s runner stepped twice a stride. &lt;code&gt;animator::set_up_events&lt;/code&gt; and &lt;code&gt;landing_events&lt;/code&gt; do it now. With them the frame tables equal the oracle&#39;s in levels 1, 6, 11, 16 and 24 (&lt;code&gt;npc_check&lt;/code&gt; FRAMES=1).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The sweep with SOUNDS&lt;/strong&gt; (FAST, 300 frames). Every NPC track still matches.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level 11: all 40 sounds the same.&lt;/li&gt;
&lt;li&gt;Level 13: all 50 the same.&lt;/li&gt;
&lt;li&gt;Levels 1-7, 10, 12, 14-18, 23, 25, 27, 28, 30 and 31: no sound either side.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Found, not yet done.&lt;/strong&gt; The generic object set-up (0x8004691c) changes the shared templates and models, as &lt;code&gt;npc_check&lt;/code&gt; TEMPLATES=1 shows against the oracle&#39;s table (0x80118a0c). The golf door (770) runs at +0x1a 50 in level 24 where the file says 32, so its cycle is 72 frames, not 94. The next worklog takes the whole function on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tools.&lt;/strong&gt; The oracle&#39;s bus stops on a write to a word (&lt;code&gt;RUGRATS_STOPW=addr[:was]&lt;/code&gt;). That is how the golf door&#39;s 50 was traced to 0x80047418.&lt;/p&gt;
</content>
</entry>
<entry>
<title>Reptar&#39;s fire and chomp match the original frame for frame without lag frames</title>
<id>https://toyz.github.io/rugrats/229-reptar-s-fire-and-chomp-match-the-original-frame-for-frame</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/229-reptar-s-fire-and-chomp-match-the-original-frame-for-frame"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="test"/>
<summary>Reptar&#39;s fire and chomp, which level 7&#39;s doc listed as running twice as fast in the port, match the original frame for frame.</summary>
<content type="html">&lt;p&gt;Reptar&#39;s fire and chomp, which level 7&#39;s doc listed as running twice as fast in the port, match the original frame for frame. The difference was the oracle&#39;s lag frames.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The measure.&lt;/strong&gt; The doc&#39;s 70 and 110 VSyncs for states 30 and 29 were read off an oracle that took frames of dt 15-20 in the city (the instruction budget, worklog 223). With &lt;code&gt;Hle::frame_steps&lt;/code&gt; raised, Reptar put at (25279, -703, -1418) facing 2048 runs as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Circle: state 30 (animation 51) for 40 frames, from the frame after the press.&lt;/li&gt;
&lt;li&gt;Triangle: state 29 (animation 49) for 60 frames.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both play at the written 2048 (template 8, &lt;code&gt;Animator::written_speed&lt;/code&gt;), as the port asks them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oracle.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;level_7_fire_as_the_original&lt;/code&gt;: 51 frames.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;level_7_chomp_as_the_original&lt;/code&gt;: 101 frames.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In both the tracks, the height (root and all) and the place are the original&#39;s on every frame. The Unknown item is gone from level-07.md, and gameplay.md&#39;s list no longer has the floor flags and the shadow as unknown.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The cines&#39; characters cast the original&#39;s shadows</title>
<id>https://toyz.github.io/rugrats/228-the-cines-characters-cast-the-original-s-shadows</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/228-the-cines-characters-cast-the-original-s-shadows"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="render"/>
<summary>The in-engine cines&#39; characters now cast shadows as the original&#39;s do.</summary>
<content type="html">&lt;p&gt;The in-engine cines&#39; characters now cast shadows as the original&#39;s do. All 3473 shadow quads of level 1&#39;s start cine match the original&#39;s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In the original.&lt;/strong&gt; play_cine (0x8007424c) draws through the same room render (0x80030b80), and the characters through their own draw, so the shadow (0x8003414c) is drawn there too. Only the opening cine&#39;s init (0x8008f078) turns shadows off (0x800c3fc4), until its end (0x8008f308). Booted with cinematics, level 1&#39;s cine 7 makes 3473 shadow calls with the switch on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The floor in a cine.&lt;/strong&gt; It is the one under where the stream puts the character, not the one under its placement. Characters 15, 16 and 1776 are placed upstairs (floors -1279, -1490, -876), but the original draws their shadows at the hall&#39;s 1024, where cine 7 moves them. So the cine&#39;s mover refreshes +0x13c. No object top was met in this cine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The port.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shadow::quad&lt;/code&gt; and &lt;code&gt;Quad::triangles&lt;/code&gt; are free of &lt;code&gt;Game&lt;/code&gt; now, and &lt;code&gt;world::game::shadow&lt;/code&gt; is public.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;media::cine::Shadows&lt;/code&gt; holds the GTE tables, the database&#39;s COL and TEXPOS 261.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Set::shadow_quads&lt;/code&gt; poses each character from the frame&#39;s keys with the port&#39;s integer posing (&lt;code&gt;pose::posed&lt;/code&gt;). It finds the floor under the posed place and builds the quad. &lt;code&gt;Set::shadows&lt;/code&gt; draws them, and &lt;code&gt;Cine::tick&lt;/code&gt; adds them to the frame, but not in the opening (cine 0).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Oracle.&lt;/strong&gt; &lt;code&gt;level_1_cine_shadows_as_the_original&lt;/code&gt; boots level 1 with its cinematics and reads every quad at 0x80034518, keyed by the cine (0x8007424c&#39;s argument), its frame (0x800c4c34) and the object&#39;s id (+0x38). It compares them with the port&#39;s from the same stream frame: 3473 the same, none off.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The characters cast the original&#39;s shadows</title>
<id>https://toyz.github.io/rugrats/227-the-characters-cast-the-original-s-shadows</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/227-the-characters-cast-the-original-s-shadows"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="render"/>
<summary>The characters now cast the original&#39;s shadows (0x8003414c).</summary>
<content type="html">&lt;p&gt;The characters now cast the original&#39;s shadows (0x8003414c). The port drew none before. On a run along level 1&#39;s hall, a turn and a stop, the quad matches the one the original builds, frame by frame.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the original draws.&lt;/strong&gt; The character draw (0x8003205c) draws a shadow after the parts, unless the character sinks (worklog 226), rides another (+0x00 0x80000) or level 3&#39;s cine 15 runs (+0x30 bit 128).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The floor (0x8006c118).&lt;/strong&gt; A segment runs from the body&#39;s place down to its floor (+0x13c) through its room&#39;s two object lists (0x8006a9cc). The nearest top it meets wins, else the floor. A character on the second list (+0x00 0x4000) is passed over.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The facing.&lt;/strong&gt; It is the first part&#39;s matrix row 2 across when |m21| &amp;lt; |m11|, else row 1 negated, made a unit (0x8009f278, &lt;code&gt;Trig::normal&lt;/code&gt;). R&#39;s rows are (z, 0, -x), (0, 4096, 0), (x, 0, z).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The size.&lt;/strong&gt; The parts at slots 0x10, 0xe, 0xb, 6, 2 and 0x11 (0x80012ff0, read in the oracle) are taken flat from the first part and turned by R (0x80010c2c → 0x8009fc64: mvmva, &amp;gt;&amp;gt; 12). Their box in x and z reaches at least 128 behind and 164 ahead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The quad.&lt;/strong&gt; The corners go back through R and sit at the first part&#39;s x and z over the floor. It is a POLY_FT4 (code 0x2e) of TEXPOS entry 261 (0x800c4484 + 3132): a 64-texel blob at (128, 128) of page 0x1b, CLUT 0x7dec, grey 128, the page | 64, so subtracted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The switch.&lt;/strong&gt; 0x800c3fc4 is off only from the opening cine&#39;s init (0x8008f078) until play&#39;s (0x8008f308).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The port.&lt;/strong&gt; &lt;code&gt;world/game/shadow.rs&lt;/code&gt; does the above: &lt;code&gt;Game::shadow_floor&lt;/code&gt;, &lt;code&gt;Game::shadow&lt;/code&gt; and the &lt;code&gt;Quad&lt;/code&gt;. &lt;code&gt;Game::draw&lt;/code&gt; gives the player and every character in a drawn room a shadow, unless sunk or riding. The renderer&#39;s semi-transparent world pass already blends by the page&#39;s mode 2 and pulls the quad off the floor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oracle.&lt;/strong&gt; &lt;code&gt;level_1_shadow_as_the_original&lt;/code&gt; reads the original&#39;s quad at 0x80034518 (facing, extents, place) for the player&#39;s hierarchy over 26 frames of a run, a turning run and a stop. It equals the port&#39;s of the frame before: the original draws before it updates.&lt;/p&gt;
&lt;p&gt;The level 1 start is not used. Booted without cinematics, the oracle never runs cine 7&#39;s end hook (0x800732d4), so Phil and Lil stay in Tommy&#39;s way. The port hides them as the game does.&lt;/p&gt;
</content>
</entry>
<entry>
<title>COL floor flags 32 and 64 sink a character as drawn: level 4&#39;s deep water</title>
<id>https://toyz.github.io/rugrats/226-col-floor-flags-32-and-64-sink-a-character-as-drawn-level-4</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/226-col-floor-flags-32-and-64-sink-a-character-as-drawn-level-4"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="render"/>
<summary>COL floor flags 32 and 64 are the sink depth: a character on such a tile is drawn 298 lower per step of depth and gets no shadow.</summary>
<content type="html">&lt;p&gt;COL floor flags 32 and 64 are the sink depth: a character on such a tile is drawn 298 lower per step of depth and gets no shadow. Level 4&#39;s deep water has them, and the port now draws its waders sunk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The reader (0x8003205c).&lt;/strong&gt; The character draw takes the body&#39;s tile (+0x8c) unless it stands on an object (+0xf8). Its flags (COL +4) give depth (flags &amp;amp; 96) &amp;gt;&amp;gt; 5, and fp = 298 × depth.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each part&#39;s matrix translation y gets fp before its draw (0x8001eb4c) and loses it after. The parts as kept, which the collision tests read, are not sunk.&lt;/li&gt;
&lt;li&gt;With fp set, the shadow (0x8003414c) is skipped.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only byte +4 tests in the code are of 1, 2, 4, 16 and 96. Nothing found reads 8.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where.&lt;/strong&gt; Only level 4 has such tiles: 54 of flags 33 (water and depth 1), the deep water around (-229000, 256, 64000). The oracle&#39;s room 40 floor in the level 4 doc carries the same 33.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The port.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Game::sink&lt;/code&gt; gives the depth for a place and whether it stands on an object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Player::triangles&lt;/code&gt; and &lt;code&gt;npc::triangles&lt;/code&gt; take it and draw each part that much lower. &lt;code&gt;Npc::drawn&lt;/code&gt; keeps the unsunk pose, and the hand still places the carried thing from the parts put back.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;collide::floor_flags&lt;/code&gt; names every bit.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>The follow camera&#39;s stale normal is a stack slot holding an address in the original&#39;s memory</title>
<id>https://toyz.github.io/rugrats/225-the-follow-camera-s-stale-normal-is-a-stack-slot-holding-an</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/225-the-follow-camera-s-stale-normal-is-a-stack-slot-holding-an"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="engine"/>
<summary>The follow camera&#39;s stale normal is traced to what it holds.</summary>
<content type="html">&lt;p&gt;The follow camera&#39;s stale normal is traced to what it holds. It cannot be carried over to the port: part of it is an address in the original&#39;s memory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The slot (0x8006a9cc&#39;s local +0x38).&lt;/strong&gt; The camera&#39;s line test (0x80090554 → 0x80090340 → 0x8006a9cc) hands each object&#39;s exact test (0x80069c40) one normal buffer in 0x8006a9cc&#39;s frame, at 0x801ff948 in the oracle. A hit turns the buffer in place by the object&#39;s matrix (0x80010c14 at 0x80069f8c). A start inside a box writes nothing before that turn.&lt;/p&gt;
&lt;p&gt;Traced on level 3&#39;s shelf, every 0x80069c40 call with its buffer before and after, for 14 frames of the turn-in-place run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The camera&#39;s line tests each frame (0x8002655c, 0x8002661c, 0x80026798, then the view test&#39;s 0x80026a30 and the side tests) all find the slot as [x, 0, P]. P is written just before each line test and is an address in the original&#39;s memory: 0x800E17C4, 0x800E162C, 0x800E17A4, 0x800E160C, by call.&lt;/li&gt;
&lt;li&gt;At the view test (0x80026914, from 0x800280a0) the slot was [1642, 0, 0x800E17C4]. The shelf&#39;s turn of it (the GTE taking the low halves) gave the [1062700, 0, 889182] that the push sets to 18.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the push from inside a box depends on where the original&#39;s heap put a record. The port keeps no such addresses and pushes nothing. The camera&#39;s doc (gameplay.md, mode 3) and &lt;code&gt;Game::blocked&lt;/code&gt; say so.&lt;/p&gt;
</content>
</entry>
<entry>
<title>The box toss&#39;s lunge steers, the box rides the hand without a grip, the knock lobs at the aim the babies leave, and the player&#39;s root across turns by frame</title>
<id>https://toyz.github.io/rugrats/224-the-box-toss-s-lunge-steers-the-box-rides-the-hand-without</id>
<link rel="alternate" href="https://toyz.github.io/rugrats/224-the-box-toss-s-lunge-steers-the-box-rides-the-hand-without"/>
<updated>2026-10-18T00:00:00Z</updated>
<category term="play"/>
<summary>The box toss&#39;s Angelica and her knock now play as the original&#39;s.</summary>
<content type="html">&lt;p&gt;The box toss&#39;s Angelica and her knock now play as the original&#39;s. Level 20 matches on all 300 frames. Level 21 matches up to its loss: the lunge, the box in hand, the knock&#39;s lob, the player&#39;s push and the babies turning to it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lunge steers (0x80084df0, 0x80082264, 0x8005bc80).&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The frame she lunges, mode 4 goes on to the follow&#39;s speed by the distance (0x80084f28) and the stick. The lunge&#39;s +0x688 bit 4 makes the stick skip its turn (0x800820e0). With a speed, its way round (0x800825fc) still turns her 512 to the open side.&lt;/li&gt;
&lt;li&gt;Through the lunge the body turns back toward the stick&#39;s last heading (+0x2c, now &lt;code&gt;Npc::aim&lt;/code&gt;) at its rate, 160 a frame. Level 20&#39;s frames 158-160 had the original turn 352 then back by 160 a frame, while the port held still.&lt;/li&gt;
&lt;li&gt;Mode 8 keeps the start frame&#39;s speed in +0x176 for the lunge&#39;s end (0x80085110).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;No grip in the box toss (0x80079cb0, 0x8007a094).&lt;/strong&gt; The round start and the body swap set +0x1f0 and place the box (0x8005ff88 mode 1), but never take a grip (mode 0). +0x1f8 stays 0 and the box rides hand 15 itself. The port took a grip from the pose, so the box sat about 330 off the original&#39;s; &lt;code&gt;Game::hand_over&lt;/code&gt; now puts it in hand without one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The knock&#39;s aim (0x8005f3a4, 0x80059b34, 0x80057bc4).&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The knock makes the dummy the player&#39;s target (+0x1f4). Each baby updated after Angelica runs the aim test and takes +0x1f4 back when it is in view and nearer, keeping the lob (+0x200) toward itself. In level 21 the box went up toward Phil.&lt;/li&gt;
&lt;li&gt;The dummy&#39;s place is the level&#39;s: (22086, 775, 12864) in level 20 and (6825, 775, 10814) in level 21 (0x8007a094). The port had level 20&#39;s in both.&lt;/li&gt;
&lt;li&gt;New: &lt;code&gt;Game::aim_dummy&lt;/code&gt;. &lt;code&gt;consider_aim&lt;/code&gt; compares against the dummy as against a held character.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The player&#39;s root across turns each frame&#39;s part by itself&lt;/strong&gt; (0x80055904 → 0x80075ee8), as the characters&#39; have since worklog 222. The knock&#39;s push back (animation 8) was a unit off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The sweep tool&lt;/strong&gt; (&lt;code&gt;npc_check&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PLAYERSYNC gives the carried thing its place as the original&#39;s frame began. The hand puts it there at the draw, after the characters aim.&lt;/li&gt;
&lt;li&gt;PLAYERBEFORE gives the player its place as that frame began, and the port&#39;s step moves it (a knocked player pushed back).&lt;/li&gt;
&lt;li&gt;PLAYERANIM gives the player the original&#39;s animation then.&lt;/li&gt;
&lt;li&gt;A cine begun ends the comparison, as an exit does: the app takes it after the frame, and the original&#39;s world waits on it.&lt;/li&gt;
&lt;li&gt;The tracks reading is shared (&lt;code&gt;record&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The sweep&lt;/strong&gt; (FAST, YCHECK, 300 frames):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 off in levels 1-7, 10-18, 20 and 21 (to its loss&#39;s cine 51 at frame 38) and holes 23-31.&lt;/li&gt;
&lt;li&gt;Level 9 matches from its sync at frame 20.&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
</feed>
