(c: Container | Renderable)
| 156 | // they can rest on the map2 pyramid platforms. |
| 157 | let allPlatformBits = 0; |
| 158 | const walk = (c: Container | Renderable) => { |
| 159 | const children = (c as Container).children; |
| 160 | if (!children) return; |
| 161 | for (const child of children) { |
| 162 | if (child.type === "platform") { |
| 163 | const width = child.width ?? 0; |
| 164 | // skip degenerate platforms (zero-width / missing geometry) |
| 165 | // — they'd build a zero-area body that matter can't |
| 166 | // resolve a contact for. |
| 167 | if (width > 0) { |
| 168 | adapter.updateShape(child, [ |
| 169 | new Rect(0, 0, width, PLATFORM_THICKNESS), |
| 170 | ]); |
| 171 | const bit = nextPlatformBit; |
| 172 | nextPlatformBit <<= 1; |
| 173 | allPlatformBits |= bit; |
| 174 | adapter.setCollisionType(child, bit); |
| 175 | // Mask: PLAYER (for one-way pass-through, driven |
| 176 | // by per-frame mask updates on the player) and |
| 177 | // ENEMY (so slimes can stand on the map2 pyramid |
| 178 | // platforms). Coins are static sensors with their |
| 179 | // own broad-phase filter — they don't need to see |
| 180 | // platforms. |
| 181 | adapter.setCollisionMask( |
| 182 | child, |
| 183 | collision.types.PLAYER_OBJECT | collision.types.ENEMY_OBJECT, |
| 184 | ); |
| 185 | oneWayPlatforms.push({ |
| 186 | renderable: child, |
| 187 | top: child.pos.y, |
| 188 | bit, |
| 189 | }); |
| 190 | } |
| 191 | } else if (child.type === "slope") { |
| 192 | adapter.setSensor(child, true); |
| 193 | } |
| 194 | walk(child); |
| 195 | } |
| 196 | }; |
| 197 | walk(_app.world); |
| 198 | |
| 199 | // Second pass: extend each enemy's mask to include every |
no test coverage detected