Skip to main content
The milestone system tracks player achievements and progress throughout the game, providing goals and rewards for major accomplishments.

Milestone model

Milestones are defined by the Milestone interface:
export interface Milestone {
  id: string;
  type: 'Strength' | 'Intelligence' | 'Endurance';
  requirement: string;
  reward: number;
  achieved: boolean;
  achievedAt?: Date;
  level: number;
}

Property breakdown

Unique identifier for the milestone.Example: "milestone_strength_100"

Milestone service

The MilestoneService manages milestone state and persistence:
@Injectable({
  providedIn: 'root',
})
export class MilestoneService {
  milestones = signal<Milestone[]>(this.loadMilestones());

  constructor() {
    effect(() => {
      localStorage.setItem('milestones', JSON.stringify(this.milestones()));
    });
  }

  private loadMilestones(): Milestone[] {
    const saved = localStorage.getItem('milestones');
    if (saved) {
      return JSON.parse(saved);
    }
    return [];
  }
}

State management

Milestones are stored as an Angular signal that automatically persists to localStorage:
On service initialization, milestones are loaded from localStorage:
private loadMilestones(): Milestone[] {
  const saved = localStorage.getItem('milestones');
  if (saved) {
    return JSON.parse(saved);
  }
  return [];
}
If no saved data exists, returns an empty array.
Using Angular’s effect(), milestones automatically save to localStorage whenever the signal updates:
effect(() => {
  localStorage.setItem('milestones', JSON.stringify(this.milestones()));
});
This reactive approach ensures milestone progress is never lost, even if the app crashes.

Milestone persistence

Milestones use localStorage for client-side persistence rather than the Convex database.

Storage format

[
  {
    "id": "milestone_stage_10",
    "type": "Strength",
    "requirement": "Reach stage 10",
    "reward": 100,
    "achieved": true,
    "achievedAt": "2026-03-01T10:30:00.000Z",
    "level": 1
  },
  {
    "id": "milestone_stage_20",
    "type": "Strength",
    "requirement": "Reach stage 20",
    "reward": 500,
    "achieved": false,
    "level": 2
  }
]
Using localStorage means milestone progress is browser-specific and won’t sync across devices. This differs from character data which uses Convex for cross-device sync.

Milestone UI integration

Milestone UI components are located in the pages directory:
src/app/pages/milestones/
├── milestones.ts
└── components/
    └── milestone-card/
        └── milestone-card.ts

Accessing milestones

Components can inject the MilestoneService to read milestone state:
import { MilestoneService } from '@services/milestone-service';

@Component({
  selector: 'app-milestones',
  // ...
})
export class MilestonesComponent {
  milestoneService = inject(MilestoneService);
  
  get allMilestones() {
    return this.milestoneService.milestones();
  }
  
  get achievedMilestones() {
    return this.milestoneService.milestones().filter(m => m.achieved);
  }
}
The milestones signal is readonly from outside the service, preventing accidental mutations.

Milestone types and categories

The three milestone types represent different progression paths:
Combat achievements:
  • Reaching high stages
  • Defeating enemies quickly
  • Dealing massive damage
  • Completing waves efficiently
These milestones track your raw combat power and progression.

Milestone levels

The level property creates milestone tiers with increasing difficulty: Example progression:
LevelRequirementReward TypeReward Amount
1Stage 10Gold100
2Stage 20Gold500
3Stage 50Cores5
4Stage 100Cores10
5Stage 200Cores25
Higher-level milestones typically require exponentially more effort but provide proportionally better rewards.

Current implementation status

Based on the source code review:
  • Milestone data model ✓
  • MilestoneService with signal-based state ✓
  • LocalStorage persistence ✓
  • UI components for milestone display ✓
  • Three milestone type categories ✓
The milestone system has the infrastructure but lacks gameplay integration. The service can store and display milestones, but there’s no code checking player progress against milestone requirements.

Extending the milestone system

To fully implement milestones, you would need to:

1. Define milestone list

Create a default set of milestones:
private getDefaultMilestones(): Milestone[] {
  return [
    {
      id: 'milestone_stage_10',
      type: 'Strength',
      requirement: 'Reach stage 10',
      reward: 100,
      achieved: false,
      level: 1,
    },
    // ... more milestones
  ];
}

2. Add milestone checking

Check for milestone completion after key events:
checkMilestones(character: Character) {
  this.milestones.update(milestones =>
    milestones.map(milestone => {
      if (!milestone.achieved && this.isMilestoneComplete(milestone, character)) {
        return {
          ...milestone,
          achieved: true,
          achievedAt: new Date(),
        };
      }
      return milestone;
    })
  );
}

3. Implement reward distribution

Grant rewards when milestones are achieved:
private grantReward(milestone: Milestone) {
  switch (milestone.type) {
    case 'Strength':
      this.characterService.modifyStat('gold', milestone.reward);
      break;
    // Handle other types
  }
}
Integrate milestone checking in CombatService.handleEnemyDefeat() and CharacterService.advanceWave() to track progress automatically.

Build docs developers (and LLMs) love