You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+43-6Lines changed: 43 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -45,19 +45,47 @@ Achievements registered in `Achievements.initAchievements()` with event-based co
45
45
FmodConstants.hx is auto-generated from the FMOD Studio project in `fmod/`. Use `FmodManager.PlaySong()` / `PlaySoundOneShot()` / `StopSong()`. Constants are in FmodSongs and FmodSFX.
46
46
47
47
### Player (source/entities/Player.hx)
48
-
Player extends FlxSprite (48x48 graphic, 16x16 hitbox). Takes an `FlxState` reference in its constructor so it can add/remove its own child sprites (reticle, power bar, cast bobber) from the scene. Movement speed is 150px/s (225 in hot mode). Uses `InputCalculator.getInputCardinal()` for input and tracks `lastInputDir` for facing direction. Has a `frozen` flag that suppresses movement during cast charging.
48
+
Player extends FlxSprite (48x48 graphic, 16x16 hitbox). Takes an `FlxState` reference in its constructor so it can add/remove its own child sprites (reticle, power bar, cast bobber) from the scene. Movement speed is 100px/s (150 in hot mode). Uses `InputCalculator.getInputCardinal()` for input and tracks `lastInputDir` for facing direction. Has a `frozen` flag that suppresses movement during cast charging, catch animation, and retrieve (unfreezes only when the bobber is destroyed after reaching the player). Supports multiple skins (Q/E to cycle). Skin loading parses Aseprite JSON atlas (`loadSkin()`) and auto-detects one-shot animations by prefix (`cast_`, `throw_`, `catch_`).
49
49
50
50
### Fishing Cast System (source/entities/Player.hx)
51
-
Cast mechanic uses a `CastState` enum (IDLE → CHARGING → CAST_ANIM → CASTING → LANDED → CATCH_ANIM → RETURNING). Press Z to start charging — a power bar pulses below the player. Press Z again to launch a bobber toward the reticle at a distance proportional to power (max 96px / 6 tiles). Press Z or move to retract the bobber at any point (mid-flight, landed). The bobber flies back to the player at 500px/s before being destroyed. The `CastState` enum is defined at module level in Player.hx.
51
+
Cast mechanic uses a `CastState` enum (IDLE → CHARGING → CAST_ANIM → CASTING → LANDED → CATCH_ANIM → RETURNING). Press A (Z key) to start charging — a power bar pulses below the player. Press A again to launch a bobber toward the reticle at a distance proportional to power (max 96px / 6 tiles). Press A or move to retract the bobber at any point (mid-flight, landed). The `CastState` enum is defined at module level in Player.hx.
52
52
53
-
The bobber launches on frame 3 of the 5-frame cast animation (CAST_LAUNCH_FRAME) with velocity 300px/s. A dot-product overshoot check in CAST_ANIM clamps the bobber at the target if it arrives during the animation, preventing visual snap-back on short casts. The same dot-product check in CASTING transitions to LANDED. `Player.catchFish()` transitions to CATCH_ANIM, which plays a retract animation and returns the bobber.
53
+
The bobber launches on frame 3 of the 5-frame cast animation (CAST_LAUNCH_FRAME) using a parabolic arc at 150px/s. The arc uses `updateCastArc()` with formula `arcHeight * 4 * t * (1-t)`. CAST_ANIM clamps the bobber at the target if it arrives during the animation. The same check in CASTING transitions to LANDED.
54
+
55
+
**Retrieve:**`Player.catchFish(hasFish)` transitions to CATCH_ANIM. The `retractHasFish` flag controls retrieve style: with a fish, the bobber/fish arcs back via `updateCastArc()` at 188px/s; without a fish, straight-line velocity at 188px/s. On catch, the bobber sprite swaps to `fish.png` showing the caught fish frame (`caughtFishSpriteIndex`). CATCH_ANIM → RETURNING keeps `frozen = true`; player unfreezes only when the bobber is destroyed after reaching them. Movement animation is suppressed during CAST_ANIM, CATCH_ANIM, and RETURNING to prevent moonwalking.
56
+
57
+
**Fish delivery callback:**`onFishDelivered` fires when the bobber/fish reaches the player. PlayState wires this to add the fish to inventory (or spawn a GroundFish if inventory is full). The callback is set before `catchFish()` and nulled after firing.
58
+
59
+
**Fishing line:** Drawn pixel-by-pixel each frame from rod tip to bobber center. Rod tip positions (`getRodTipPos()`) vary per cast direction and per animation frame. Left/right casts use cubic Bezier curves with downward sag; up/down use Bresenham line drawing.
60
+
61
+
**Rod tip positions:** Manually calibrated per direction per frame. The CATCH_ANIM/RETURNING branch has 3 frames per direction (frame 0 = cast position, frame 1 = mid-retract, frame 2 = final resting position).
54
62
55
63
### Fish System (source/entities/)
56
-
**FishSpawner** (`FishSpawner.hx`) is a `FlxTypedGroup<WaterFish>` that flood-fills the FishSpawner IntGrid layer to find water bodies, then spawns `WaterFish` into each body. It handles separation (fleeing when fish are too close) and passes bobber references through to fish via `setBobber()`. Takes an `onCatch` callback in its constructor, wired to each fish at spawn time.
64
+
**FishSpawner** (`FishSpawner.hx`) is a `FlxTypedGroup<WaterFish>` that flood-fills the FishSpawner IntGrid layer to find water bodies, then spawns `WaterFish` into each body. Each FishSpawner LDTK entity has a `numFish` field controlling how many fish spawn in that body. FishSpawner handles separation — when two fish are closer than `SEPARATION_DIST` (20px), **both** flee from each other (not just one). Passes bobber references through to fish via `setBobber()`. Takes an `onCatch` callback in its constructor, wired to each fish at spawn time.
65
+
66
+
**WaterFish** (`WaterFish.hx`) owns its own bobber-awareness logic using center-to-center distance (`x + width/2, y + height/2`). Each fish has a nullable `bobber` reference and an `onCatch` callback. `checkBobber()` is called when `bobber != null || attracted` — this handles the case where the bobber is retracted while a fish is swimming toward it. Distance thresholds: attract within 32px, catch within 4px. When a fish is attracted and the bobber becomes null (retracted), the fish flees in the opposite direction via `fleeFrom()` then resumes normal wandering. `fleeFrom()` returns immediately if the fish is attracted to a bobber (attraction overrides separation). Flee picks the farthest water tile in the away direction and immediately sets velocity (no pause). Fish fade in over 1 second when spawning/respawning. After being caught, fish respawn at a random water tile after 3 seconds. Fish use `fishShadow.png` sprite with `centerOffsets()` for proper hitbox alignment.
67
+
68
+
**GroundFish** (`GroundFish.hx`) — fish that land on the ground when the player's inventory is full. Arcs from the player's head position to a random non-water landing spot. Uses `fish.png` spritesheet (32x32 frames, 5 fish types). Has a `FISH_SIZES` lookup table with actual pixel dimensions per frame: `[8,8], [9,9], [12,12], [13,14], [15,16]` (top-left aligned within the 32x32 cell). Origin is set to the center of the actual fish content for proper rotation. While landing (`landing = true`), the fish arcs through the air and can't be picked up. After landing, it flops (sine-wave rotation) and can be picked up by walking over it.
69
+
70
+
**GroundFishGroup** (`GroundFishGroup.hx`) manages ground fish spawning and pickup. `addFish()` picks a random landing spot 16-32px away, trying up to 20 times to avoid water tiles (checked via the LDTK FishSpawner IntGrid layer). Prevents pickup during landing arc.
57
71
58
-
**WaterFish**(`WaterFish.hx`) owns its own bobber-awareness logic. Each fish has a nullable `bobber` reference and an `onCatch` callback. In `update()`, the fish checks its distance to the bobber and autonomously decides to attract (within 32px), get caught (within 4px), or ignore. Fish wander between random water tiles with pause/retarget timers, and flee from other fish that get too close.
72
+
**PlayState wiring:**PlayState creates the spawner with an `onFishCaught` callback that sets `player.onFishDelivered` before calling `player.catchFish(true)`. The delivery callback adds fish to inventory; if full, spawns a GroundFish at the player's head (x+8, y-2) using the caught fish's sprite frame (`player.caughtFishSpriteIndex`). Each frame calls `fishSpawner.setBobber(player.isBobberLanded() ? player.castBobber : null)`, `rockGroup.checkPickup(player)`, and `groundFishGroup.checkPickup(player)`.
59
73
60
-
**PlayState wiring:** PlayState creates the spawner with `() -> player.catchFish()` as the catch callback, then each frame calls `fishSpawner.setBobber(player.isBobberLanded() ? player.castBobber : null)`.
74
+
### Rock Throwing (source/entities/Player.hx, source/entities/Rock.hx)
75
+
Press B to throw a rock from inventory toward the reticle (max 96px). The rock arcs via parabolic flight (`arcHeight * 4 * t * (1-t)`, max height = min(dist*0.5, 64)) at 200px/s. Player is frozen during the throw animation. The rock launches on frame 6 of the throw animation. `makeRock` factory is set by PlayState to create rocks that know about the spawner layer. After landing, `resolveThrow()` is called on the rock.
76
+
77
+
### Inventory (source/entities/Inventory.hx)
78
+
Simple array-based inventory with `MAX_SLOTS = 4`. Supports `add()`, `remove()`, `has()`, `isFull()`, `count()`. Items are the `InventoryItem` enum: `Rock`, `Fish`. Fires `onChange` signal on add/remove. InventoryHUD displays current inventory state.
79
+
80
+
### Networking (source/net/NetworkManager.hx)
81
+
Colyseus-based multiplayer. `NetworkManager` manages client connection, room joining, and message passing. Signals: `onJoined`, `onPlayerAdded`, `onPlayerChanged`, `onPlayerRemoved`, `onFishAdded`, `onFishMove`. `IS_HOST` determines whether this client spawns fish/rocks. The `sendMessage()` method has an optional `mute` parameter to suppress per-frame logging (used by `sendMove()`). In `-Dlocal` mode, all methods early-return as no-ops and `IS_HOST` defaults to `true`.
82
+
83
+
PlayState manages remote players (`remotePlayers` map) and remote fish (`remoteFish` map). Remote players are `Player` instances with `isRemote = true` that skip input processing and are driven by network events.
84
+
85
+
### Round/Game Management (source/managers/)
86
+
**GameManager** (`GameManager.hx`) — singleton (`ME`) that holds the `NetworkManager`, `FishManager`, and orchestrates rounds. Constructed with an array of `Round` definitions. Calls `net.sendMessage("round_update", ...)` at round transitions (lobby, pre-round, post-round, end-game).
87
+
88
+
**RoundManager** (`RoundManager.hx`) — manages a single round's goals. Signals completion when all goals (or any goal, depending on `allGoalsRequired`) are met. `initialize(state)` is called after PlayState creates to set up round-specific behavior.
61
89
62
90
### Analytics & Storage (source/helpers/)
63
91
Analytics.hx reports events to Bitlytics. Storage.hx handles local persistence for achievements and metrics.
@@ -81,6 +109,8 @@ Analytics.hx reports events to Bitlytics. Storage.hx handles local persistence f
81
109
-`API_KEY` — analytics token for production
82
110
-`dev_analytics` — dev mode analytics
83
111
-`llm_bridge` — enable LLM debug bridge (`window.__debug` API for Playwright introspection)
112
+
-`local` — fully offline mode: `NetworkManager.IS_HOST` defaults to `true`, `connect()`/`sendMove()`/`sendMessage()` are no-ops (early return). Fish/rocks spawn immediately (no 10s network delay). PlayState skips `setupNetwork()` and `fishSpawner.setNet()`. Call-site code does **not** need `#if !local` guards — NetworkManager handles it internally.
113
+
-`rocks` — debug flag: fills player inventory with `MAX_SLOTS` rocks at construction time
84
114
85
115
## Conventions
86
116
@@ -93,6 +123,13 @@ Analytics.hx reports events to Bitlytics. Storage.hx handles local persistence f
93
123
- When making sprites visible, set their position before setting `visible = true` to avoid a one-frame flash at the previous location
94
124
- Use `FlxPoint.get()`/`.put()` for pooled points; call `.put()` when done to return to pool
95
125
126
+
## Key Sprite Assets
127
+
-`assets/aseprite/characters/playerA.json` (and playerB-H) — player skins, 48x48 frames, Aseprite JSON atlas with frame tags for animations
128
+
-`assets/aseprite/characters/fishShadow.png` — water fish silhouette sprite
129
+
-`assets/aseprite/fish.png` — caught/ground fish spritesheet, 32x32 frames (3 columns x 2 rows), 5 fish types of varying sizes within the cells
0 commit comments