Overview
The CharacterService manages the player’s character state using Angular signals and integrates with Convex for real-time database synchronization. It handles character stats, progression through stages and waves, and persists prestige-related progress.
Properties
character
character: WritableSignal<Character>
Reactive signal containing the current character state. Updates trigger automatic UI re-renders.
hasLoadedFromDb
hasLoadedFromDb: Signal<boolean>
Indicates whether the initial character data has been loaded from the database.
Methods
updateDatabase
public updateDatabase(): void
Persists the current character state to the database. Only saves essential fields:
id - Character identifier
prestigeLevel - Current prestige level
prestigeMultipliers - Stat multipliers from prestige
prestigeCores - Available prestige currency
gold - Current gold amount
currentStage - Current stage number
currentWave - Current wave within stage
Usage:
characterService.updateDatabase();
This method is typically called by GameStateService.pushUpdatesToDatabase() rather than directly.
resetCharacter
Resets the character to base stats while preserving prestige progress. Called during prestige operations.
Preserved values:
prestigeLevel
prestigeMultipliers
prestigeCores
gold
Reset values:
level → 1
- All base stats → 1
- All stat modifiers → 1
currentStage → 1
currentWave → 1
Usage:
// Called during prestige
characterService.resetCharacter();
This method permanently resets character stats. Always call during prestige operations only.
advanceWave
Progresses the character to the next wave or stage. Increments wave number (1-10), and advances to the next stage when completing wave 10.
Logic:
- If current wave < 10: increment wave
- If current wave = 10: reset wave to 1 and increment stage
Usage:
// Called after defeating an enemy
characterService.advanceWave();
modifyStat
modifyStat(
stat: keyof Pick<
Character,
'level' | 'baseStrength' | 'baseIntelligence' | 'baseEndurance' | 'gold' | 'prestigeLevel' | 'prestigeCores'
>,
amount: number
): void
Modifies a character stat by the specified amount. Prevents negative values.
The stat to modify. Valid values: 'level', 'baseStrength', 'baseIntelligence', 'baseEndurance', 'gold', 'prestigeLevel', 'prestigeCores'
The amount to add (positive) or subtract (negative) from the stat
Usage:
// Add 100 gold
characterService.modifyStat('gold', 100);
// Add 5 strength
characterService.modifyStat('baseStrength', 5);
// Add prestige cores
characterService.modifyStat('prestigeCores', 10);
If the resulting value would be negative, the stat is not modified.
spendPrestigeCores
spendPrestigeCores(cost: number): void
Deducts prestige cores from the character. Used when purchasing prestige upgrades.
The number of prestige cores to spend
Usage:
characterService.spendPrestigeCores(5);
This method does not validate if the character has enough prestige cores. Always check availability before calling.
spendGold
spendGold(cost: number): void
Deducts gold from the character. Used when purchasing gold upgrades.
The amount of gold to spend
Usage:
characterService.spendGold(150);
This method does not validate if the character has enough gold. Always check availability before calling.
Database Integration
The service uses Convex queries and mutations for real-time synchronization:
private getCharacterFromDatabase = injectQuery(api.character.getCharacter, () => ({}));
private databaseUpdateMutation = injectMutation(api.character.updateCharacter);
Character data is automatically loaded on initialization and merged with default values using an effect:
effect(() => {
const dbCharacter = this.getCharacterFromDatabase.data();
if (!dbCharacter) return;
if (this.hasLoadedFromDb()) {
// Merge updates after initial load
this.character.update((char) => ({ ...char, ...dbCharacter }));
} else {
// Initial load: merge with defaults
const merged = { ...defaultCharacter, ...dbCharacter };
this.character.set(merged);
this.hasLoadedFromDb.set(true);
}
});
See the Character model for the complete Character interface definition.