Skip to main content
Character progression in Kaizen revolves around base stats, modifiers, and prestige multipliers that persist across resets.

Character model

The Character interface defines all character properties:
export interface Character {
  id: string;
  name: string;
  level: number;

  baseStrength: number;
  baseIntelligence: number;
  baseEndurance: number;

  strengthModifier: number;
  intelligenceModifier: number;
  enduranceModifier: number;

  prestigeLevel: number;
  prestigeMultipliers: {
    strength: number;
    intelligence: number;
    endurance: number;
  };
  prestigeCores: number;

  gold: number;

  currentStage: number;
  currentWave: number;

  createdAt: Date;
  lastActiveAt: Date;
}

Base stats

Every character starts with three core stats that form the foundation of combat power.
Base strength determines your damage output. The damage formula uses:
(character.baseStrength + flatBoosts) *
  character.strengthModifier *
  character.prestigeMultipliers.strength *
  character.prestigeLevel
Default starting value: 1
Currently, only Strength actively affects gameplay through damage calculation. Intelligence and Endurance are included in the character model for future features.

Stat modifiers

Each base stat has an associated modifier that provides multiplicative scaling:
  • strengthModifier: Multiplies base strength
  • intelligenceModifier: Multiplies base intelligence
  • enduranceModifier: Multiplies base endurance
All modifiers start at 1.0 (100%).
Modifiers are separate from prestige multipliers and can be adjusted independently for different game mechanics.

Default character

When creating a new character, these are the initial values from returnDefaultCharacter() (line 78):
private returnDefaultCharacter(): Character {
  return {
    id: '1',
    name: 'Hero',
    level: 1,
    baseStrength: 1,
    baseIntelligence: 1,
    baseEndurance: 1,
    strengthModifier: 1,
    intelligenceModifier: 1,
    enduranceModifier: 1,
    prestigeLevel: 1,
    prestigeMultipliers: {
      strength: 1,
      intelligence: 1,
      endurance: 1,
    },
    prestigeCores: 0,
    gold: 0,
    currentStage: 1,
    currentWave: 1,
    createdAt: new Date(),
    lastActiveAt: new Date(),
  };
}

Modifying stats

The CharacterService provides methods to modify character stats:

Stat modification

modifyStat(
  stat: keyof Pick<
    Character,
    | 'level'
    | 'baseStrength'
    | 'baseIntelligence'
    | 'baseEndurance'
    | 'gold'
    | 'prestigeLevel'
    | 'prestigeCores'
  >,
  amount: number,
) {
  const currentValue = this.character()[stat];
  const newValue = currentValue + amount;
  if (newValue >= 0) {
    this.character.update((char) => ({ ...char, [stat]: newValue }));
  }
}
Stats can only be modified if the resulting value is non-negative. You cannot reduce a stat below 0.

Currency spending

Specialized methods handle spending currencies:
spendPrestigeCores(cost: number) {
  this.character.update((char) => ({
    ...char,
    prestigeCores: char.prestigeCores - cost,
  }));
}
Used by the PrestigeUpgradeService when purchasing prestige upgrades.
spendGold(cost: number) {
  this.character.update((char) => ({
    ...char,
    gold: char.gold - cost,
  }));
}
Used by the GoldUpgradeService when purchasing gold upgrades.

Prestige properties

Several character properties are specifically related to the prestige system:

Prestige level

prestigeLevel starts at 1 and increases by 1 each time you prestige. This value is a direct multiplier in the damage formula:
damage = baseCalculation * character.prestigeLevel;
Prestige level provides a permanent, linear scaling to your damage. Prestiging 10 times gives you 11x damage from this multiplier alone!

Prestige multipliers

The prestigeMultipliers object contains separate multipliers for each stat:
prestigeMultipliers: {
  strength: 1,
  intelligence: 1,
  endurance: 1,
}
These are applied in the damage calculation:
damage *= character.prestigeMultipliers.strength;
Currently, prestige multipliers default to 1.0 and are not modified by the prestige system. They may be used for future prestige-based stat bonuses.

Prestige cores

prestigeCores is the currency earned from prestiging. Cores can be:
  • Spent on prestige upgrades
  • Kept unspent to benefit from “Hoarded Power” upgrade (+2% DPS per unused core)
See the prestige system page for core calculation details.

Wave and stage tracking

Progression through the game is tracked via two properties:
  • currentStage: The major progression marker (1, 2, 3, …)
  • currentWave: The minor progression marker within a stage (1-10)

Advancing waves

From advanceWave() (line 104):
advanceWave() {
  this.character.update((char) => ({
    ...char,
    currentWave: char.currentWave < 10 ? char.currentWave + 1 : 1,
    currentStage: char.currentWave === 10 ? char.currentStage + 1 : char.currentStage,
  }));
}
Logic:
  • If wave < 10: Increment wave
  • If wave = 10: Reset to wave 1 and increment stage
Your current stage and wave affect enemy HP scaling and gold rewards. Higher stages mean exponentially tougher enemies but better gold income.

Character reset on prestige

When you prestige, most stats are reset while prestige-related properties are preserved:
resetCharacter() {
  const currentChar = this.character();

  this.character.set({
    id: '1',
    name: 'Hero',
    level: 1,
    baseStrength: 1,
    baseIntelligence: 1,
    baseEndurance: 1,
    strengthModifier: 1,
    intelligenceModifier: 1,
    enduranceModifier: 1,
    prestigeLevel: currentChar.prestigeLevel, // Preserved
    prestigeMultipliers: currentChar.prestigeMultipliers, // Preserved
    prestigeCores: currentChar.prestigeCores, // Preserved
    gold: currentChar.gold, // Preserved
    currentStage: 1,
    currentWave: 1,
    createdAt: new Date(),
    lastActiveAt: new Date(),
  });
}
Prestiging resets your stage and wave progress back to 1-1, but you keep your gold, prestige level, prestige cores, and multipliers.

State persistence

Character state is persisted to the database via Convex:
public updateDatabase(): void {
  const characterToSave = {
    id: this.character().id,
    prestigeLevel: this.character().prestigeLevel,
    prestigeMultipliers: this.character().prestigeMultipliers,
    prestigeCores: this.character().prestigeCores,
    gold: this.character().gold,
    currentStage: this.character().currentStage,
    currentWave: this.character().currentWave,
  };
  this.databaseUpdateMutation.mutate(characterToSave);
}
Only prestige-related properties and progression are saved to the database. Base stats and modifiers are derived from defaults and upgrades.

Character loading

On initialization, character data is loaded from the database and merged with defaults:
effect(() => {
  const dbCharacter = this.getCharacterFromDatabase.data();
  if (!dbCharacter) return;
  if (this.hasLoadedFromDb()) {
    this.character.update((char) => ({
      ...char,
      ...dbCharacter,
    }));
  } else {
    const merged = { ...defaultCharacter, ...dbCharacter };
    this.character.set(merged);
    this.hasLoadedFromDb.set(true);
  }
});
Database properties override defaults, ensuring your progression is preserved across sessions.

Build docs developers (and LLMs) love