---
phase: 06.3
plan: "06"
subsystem: legacy-spec
tags: [client, animation, teleport, extraction]
requires:
  - phase: "06.2"
    provides: "Spawn delay legacy spec and local player render path"
provides:
  - "D-55 legacy JoinIn/JoinOut/TeleIn/TeleOut animation timing notes"
  - "Phaser tween implementation parameters for plan 06.3-17"
affects: [phase-06.3, D-55, PlayerRenderer, GameScene]
tech-stack:
  added: []
  patterns: ["Legacy animation spec documented before implementation"]
key-files:
  created:
    - .planning/phases/06.3-cycle-4-gap-closure-d-40-d-45-d-51-d-53-d-54-d-55-d-56-d-57-/06.3-06-SUMMARY.md
  modified: []
key-decisions:
  - "SpriteStateMachine remains pure deriveFrame logic; D-55 tween logic belongs in PlayerRenderer/GameScene."
  - "The 500ms/333ms Phaser tween timings are intentional visual approximations, not exact legacy frame-duration ports."
patterns-established:
  - "Teleport visuals should run concurrently with the 30-tick spawnDelayTicks hold."
requirements-completed:
  - REQ-CLI-04
  - REQ-CLI-08
duration: 12 min
completed: 2026-05-14
---

# Phase 06.3 Plan 06: D-55 Legacy Teleport Animation Spec Summary

[doc->REQ-CLI-04] [doc->REQ-CLI-08]

**D-55 has a source-backed teleport animation spec for 06.3-17, with exact legacy timing separated from the MVP Phaser tween approximation.**

## Legacy Findings

- `extracted/client-5-8/objects/0042-player/events/Create.gml` initializes `image_speed = 0.8`.
- `extracted/client-5-8/objects/0042-player/events/Other-7.gml` is the Animation End event. For `TeleIn` or `JoinIn`, it sets `image_speed = 0.4`, writes `global.p_spr[pid,0] = NaviStandD`, and switches `sprite_index = NaviStandD`.
- The same Animation End event handles `TeleOut` and `JoinOut`: if the player is offline or in a different room, it destroys the instance; otherwise it sets `visible = 0` and `image_speed = 1`.
- `extracted/client-5-8/objects/0042-player/events/Draw.dnd.json` draws `JoinInR` when `sprite_index == JoinIn` and `JoinOutR` when `sprite_index == JoinOut`.
- `extracted/client-5-8/sprites/0375-JoinIn/frames` contains 15 frames; exact duration at `image_speed = 0.4` is 37.5 ticks, about 1250ms at 30 Hz.
- `extracted/client-5-8/sprites/0373-JoinOut/frames` contains 15 frames; exact duration at `image_speed = 1` is 15 ticks, about 500ms at 30 Hz.
- `TeleIn` contains 22 frames and `TeleOut` contains 24 frames, but these sprites are not in the current MVP atlas.

## Phaser Tween Spec for 06.3-17

- TeleportIn: alpha `0 -> 1`, scaleX/scaleY `0.5 -> 1`, duration `500ms`, ease `Linear`.
- TeleportOut: alpha `1 -> 0`, scaleX/scaleY `1 -> 0.5`, duration `333ms`, ease `Linear`.
- These durations are the 06.3 implementation approximation selected because the current atlas lacks `JoinIn`, `JoinOut`, `TeleIn`, and `TeleOut`. The exact legacy JoinIn frame duration is longer than the earlier ~6-frame estimate, so 06.3-17 should cite this as an intentional MVP visual substitute rather than an exact animation port.
- TeleportIn runs during the existing 30-tick `spawnDelayTicks` hold, not before it and not after it.
- TeleportIn implementation location: `PlayerRenderer.ensureLocal()` immediately after `makeSprite(...)`, using `this.opts.scene.tweens.add(...)`.
- TeleportOut implementation location: `GameScene.onRemoteRemove()` before `playerRenderer.removeRemote(sid)`.

## Architecture Notes

- `apps/client/src/render/SpriteStateMachine.ts` is a pure function module around `deriveFrame(...)`; it has no state-machine class or enum to extend for TeleportIn/TeleportOut.
- D-55 should stay in render/tween orchestration. No new `SpriteState` values are needed for 06.3-17.
- `PlayerRenderer` stores remote render state in `this.remotes`, and `removeRemote(sessionId)` currently destroys the sprite immediately.
- 06.3-17 needs a getter before remove: `getRemoteSprite(sid: string): Phaser.GameObjects.Sprite | undefined`, implemented as `return this.remotes.get(sid)?.sprite;`.

## Task Commits

1. **Task 1/2: Read-only D-55 spec extraction** - this summary commit

## Files Created/Modified

- Created `.planning/phases/06.3-cycle-4-gap-closure-d-40-d-45-d-51-d-53-d-54-d-55-d-56-d-57-/06.3-06-SUMMARY.md`.
- No source files modified.

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] Legacy frame count was higher than the plan estimate**
- **Found during:** Frame directory inspection.
- **Issue:** The plan repeated the research approximation "if ~6 frames", but extracted `JoinIn` and `JoinOut` each have 15 frame files.
- **Fix:** Documented exact legacy timing and retained the planned 500ms/333ms Phaser tween values as intentional MVP approximations.
- **Files modified:** None beyond this summary.

## Verification

- Verified `Other-7.gml`, `Create.gml`, `Draw.dnd.json`, sprite frame directories, `SpriteStateMachine.ts`, `PlayerRenderer.ts`, and `GameScene.onRemoteRemove()`.
- Confirmed this was read-only: no source files modified.

## Next Phase Readiness

Ready for `06.3-17`. The implementation can add local TeleportIn tweening, remote TeleportOut tweening, and `getRemoteSprite(sid)` without touching `SpriteStateMachine.ts`.

---
*Phase: 06.3*
*Completed: 2026-05-14*
