docs: add day counter + 2x indicator implementation plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-10 19:10:48 +00:00
parent 4526ba908c
commit 5d407d41fa
@@ -0,0 +1,339 @@
# Day Counter + 2x Indicator Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Add a "Day N" counter to the left of the day/night indicator and double the indicator's size.
**Architecture:** Server computes `dayNumber` centrally in GameLoop, broadcasts it in StateUpdate. Client TimeIndicator renders it. Existing inline day calculation in inventionSystem replaced with centralized method.
**Tech Stack:** TypeScript, Socket.io, HTML/CSS DOM (TimeIndicator), shared types
---
### Task 1: Add `dayNumber` to StateUpdate type
**Files:**
- Modify: `shared/src/types.ts:178-182`
**Step 1: Add dayNumber field to StateUpdate**
In `shared/src/types.ts`, add `dayNumber` to the `StateUpdate` interface:
```typescript
export interface StateUpdate {
entities: EntityState[];
tick: number;
gameTime: number; // 0.0-1.0 normalized position in day/night cycle
dayNumber: number; // 1-indexed day counter
}
```
**Step 2: Rebuild shared types**
Run: `npx -w shared tsc`
Expected: Clean compile, no errors
**Step 3: Commit**
```bash
git add shared/src/types.ts
git commit -m "feat: add dayNumber to StateUpdate type"
```
---
### Task 2: Add getCycleTicks() and getDayNumber() to GameLoop
**Files:**
- Modify: `server/src/game/GameLoop.ts:342-347`
**Step 1: Add getCycleTicks() private helper and getDayNumber() method**
Replace the existing `getGameTime()` method and add new methods after it in `server/src/game/GameLoop.ts`:
```typescript
private getCycleTicks(): number {
const dayTicks = 100 / this.runtimeConstants.get('ENERGY_DECAY_PER_TICK');
const nightTicks = dayTicks / this.runtimeConstants.get('DAY_NIGHT_RATIO');
return Math.round(dayTicks + nightTicks);
}
getGameTime(): number {
const cycleTicks = this.getCycleTicks();
return (this.tick % cycleTicks) / cycleTicks;
}
getDayNumber(): number {
return Math.floor(this.tick / this.getCycleTicks()) + 1;
}
```
**Step 2: Verify server compiles**
Run: `npx -w server tsc --noEmit`
Expected: Clean compile
**Step 3: Commit**
```bash
git add server/src/game/GameLoop.ts
git commit -m "feat: add getCycleTicks() and getDayNumber() to GameLoop"
```
---
### Task 3: Include dayNumber in state broadcast
**Files:**
- Modify: `server/src/network/stateSerializer.ts:100-102`
- Modify: `server/src/network/SocketServer.ts:36`
**Step 1: Update serializeStateUpdate to accept and include dayNumber**
In `server/src/network/stateSerializer.ts`, change the function signature and return value:
```typescript
export function serializeStateUpdate(world: World, tick: number, gameTime: number, dayNumber: number): StateUpdate {
const entities = world.query('position').map(id => serializeEntity(world, id));
return { entities, tick, gameTime, dayNumber };
}
```
**Step 2: Update SocketServer broadcast call**
In `server/src/network/SocketServer.ts` line 36, add `getDayNumber()`:
```typescript
const update = serializeStateUpdate(this.gameLoop.world, this.gameLoop.getTick(), this.gameLoop.getGameTime(), this.gameLoop.getDayNumber());
```
**Step 3: Verify server compiles**
Run: `npx -w server tsc --noEmit`
Expected: Clean compile
**Step 4: Commit**
```bash
git add server/src/network/stateSerializer.ts server/src/network/SocketServer.ts
git commit -m "feat: broadcast dayNumber in state updates"
```
---
### Task 4: Replace inline day calculation in inventionSystem
**Files:**
- Modify: `server/src/systems/inventionSystem.ts:14,21-26,148-152`
**Step 1: Pass getDayNumber callback into createInventionSystem**
Change the factory function signature to accept a `getDayNumber` callback:
```typescript
export function createInventionSystem(
llmService: LlmService,
narrationService: NarrationService,
eventMemoryService: EventMemoryService,
onInventionCreated?: (summary: InventionSummary) => void,
getDayNumber?: () => number,
): InventionSystem {
```
**Step 2: Replace inline day calculation**
Replace lines 148-152 (the inline day calculation):
```typescript
// Compute current day
const dayTicks = 100 / ENERGY_DECAY_PER_TICK;
const nightTicks = dayTicks / DAY_NIGHT_RATIO;
const cycleTicks = Math.round(dayTicks + nightTicks);
const day = Math.floor(tick / cycleTicks) + 1;
```
With:
```typescript
const day = getDayNumber ? getDayNumber() : 1;
```
**Step 3: Remove unused imports**
Remove `ENERGY_DECAY_PER_TICK` and `DAY_NIGHT_RATIO` from the import on line 14 (if no longer used elsewhere in file):
```typescript
// Remove this line:
import { ENERGY_DECAY_PER_TICK, DAY_NIGHT_RATIO } from '@dflike/shared';
```
**Step 4: Update GameLoop to pass getDayNumber**
In `server/src/game/GameLoop.ts` around line 75, update the `createInventionSystem` call:
```typescript
this.inventionSystem = createInventionSystem(
this.llmService,
this.narrationService,
this.eventMemoryService,
(summary) => this.onInventionCreated?.(summary),
() => this.getDayNumber(),
);
```
**Step 5: Run tests**
Run: `npm -w server run test`
Expected: All 408 tests pass
**Step 6: Commit**
```bash
git add server/src/systems/inventionSystem.ts server/src/game/GameLoop.ts
git commit -m "refactor: use centralized getDayNumber() in inventionSystem"
```
---
### Task 5: Double TimeIndicator dimensions and add day counter
**Files:**
- Modify: `client/src/ui/TimeIndicator.ts` (full file)
**Step 1: Add dayLabel field and update constructor**
Add a `dayLabel` property and a new DOM element for it. Update all dimensions to 2x. The full updated constructor and fields:
Add new field:
```typescript
private dayLabel: HTMLDivElement;
private lastDayNumber = 0;
```
In the constructor, update these CSS values:
- Container gap: `5px``10px`
- Sun icon font-size: `10px``20px`, text-shadow blur `4px``8px`
- Track height: `8px``16px`, border: `2px``4px`, box-shadow blur `6px``12px`
- Day section width: `dayFraction * 120``dayFraction * 240`
- Divider width: `1px``2px`
- Night section width: `(1 - dayFraction) * 120``(1 - dayFraction) * 240`
- Marker width: `3px``6px`, top: `-1px``-2px`, height calc: `calc(100% + 2px)``calc(100% + 4px)`, box-shadow blur `4px``8px`
- Moon icon font-size: `9px``18px`, text-shadow blur `4px``8px`
Add day label element before appending children:
```typescript
// Day counter label
this.dayLabel = document.createElement('div');
this.dayLabel.style.cssText = `
font-size: 10px;
color: #e0d0b0;
text-shadow: 0 0 4px #00000088;
line-height: 1;
white-space: nowrap;
`;
this.dayLabel.textContent = 'Day 1';
```
Update container children order — day label first:
```typescript
this.container.appendChild(this.dayLabel);
this.container.appendChild(this.sunIcon);
this.container.appendChild(track);
this.container.appendChild(this.moonIcon);
```
**Step 2: Update the update() method**
Change signature to accept dayNumber and update the track width constant:
```typescript
update(gameTime: number, dayNumber: number): void {
// Update day label when day changes
if (dayNumber !== this.lastDayNumber) {
this.lastDayNumber = dayNumber;
this.dayLabel.textContent = `Day ${dayNumber}`;
}
const key = Math.round(gameTime * 200).toString();
if (key === this.lastTimeKey) return;
this.lastTimeKey = key;
const totalWidth = 240 + 2; // 240px sections + 2px divider
const px = gameTime * totalWidth;
// ... rest unchanged
```
**Step 3: Commit**
```bash
git add client/src/ui/TimeIndicator.ts
git commit -m "feat: double TimeIndicator size and add day counter"
```
---
### Task 6: Pass dayNumber from GameScene to TimeIndicator
**Files:**
- Modify: `client/src/scenes/GameScene.ts`
**Step 1: Add dayNumber tracking field**
Find where `targetGameTime` and `currentGameTime` are declared and add:
```typescript
private currentDayNumber = 1;
```
**Step 2: Store dayNumber from state updates**
In `handleStateUpdate()` (around line 674-675), add:
```typescript
private handleStateUpdate(update: StateUpdate): void {
this.targetGameTime = update.gameTime;
this.currentDayNumber = update.dayNumber;
```
**Step 3: Pass dayNumber to TimeIndicator.update()**
In the `update()` method (around line 799), change:
```typescript
this.timeIndicator.update(this.currentGameTime, this.currentDayNumber);
```
**Step 4: Verify client builds**
Run: `npm -w client run build`
Expected: Clean build
**Step 5: Commit**
```bash
git add client/src/scenes/GameScene.ts
git commit -m "feat: pass dayNumber from state updates to TimeIndicator"
```
---
### Task 7: Manual verification
**Step 1: Start server and client**
Run server: `npm -w server run dev`
Run client: `npm -w client run dev`
**Step 2: Verify in browser**
- Day/night indicator should be approximately 2x larger
- "Day N" text should appear to the left of the sun icon
- Day counter should increment when the day/night cycle completes
- Marker should still move smoothly across the track
**Step 3: Run all server tests**
Run: `npm -w server run test`
Expected: All tests pass