Skip to main content

Seasons System

Titan implements Path of Exile-style seasons (leagues) with temporary and permanent leagues.

Season Types

Season Model

interface Season {
seasonId: string; // e.g., "standard", "season-1"
name: string; // Display name
description?: string;
status: SeasonStatus;
isPermanent: boolean;
targetSeasonId?: string; // Migration destination
startedAt?: string;
endsAt?: string;
}

Season Status

Status Values

StatusDescription
UpcomingVisible, no characters yet
ActiveOpen for character creation
EndingWarning period, no new characters
EndedFinished, migration pending
MigratingCharacters being moved
MigratedAll characters moved

Character Restrictions

When creating a character, players can choose restrictions:

RestrictionValueEffect
None0Normal gameplay
Hardcore1Death = migration to permanent
SoloSelfFound2Cannot trade with others

Hardcore Characters

Solo Self-Found (SSF)

SSF characters:

  • Cannot participate in trades (blocked by SoloSelfFoundRule)
  • Cannot use shared stash tabs
  • Play completely self-reliant

Season Migration

When a temporary season ends, characters migrate to the permanent league:

What Migrates

EntityBehavior
CharactersMoved to permanent league
EquipmentStays on character
Bag ItemsMoved to stash
StashMerged with permanent stash
ProgressCarried over

Migration API

// Admin: End the season
await connection.invoke("EndSeason", "season-1");

// Admin: Start migration
await connection.invoke("StartMigration", "season-1");

// Check progress
const status = await connection.invoke("GetMigrationStatus", "season-1");
console.log(`${status.completed}/${status.total} characters migrated`);

Void League

For experimental mechanics, Void leagues prevent migration:

// Create void season
await connection.invoke("CreateSeason", {
seasonId: "void-experimental",
name: "Experimental Void",
isPermanent: true,
targetSeasonId: null // Items don't leave
});

Characters and items in Void leagues:

  • Never migrate to permanent
  • Are "stuck" in the void season
  • Can still be played but isolated

Season Events

Subscribe to season updates:

connection.on("SeasonEvent", (event) => {
switch (event.eventType) {
case "SeasonCreated":
showNewSeason(event.season);
break;
case "SeasonStatusChanged":
updateSeasonStatus(event.season);
break;
case "MigrationStarted":
showMigrationBanner();
break;
case "MigrationCompleted":
refreshCharacterList();
break;
}
});

await connection.invoke("JoinAllSeasonsGroup");