diff options
| author | Bobby <[email protected]> | 2026-04-22 07:51:18 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-04-22 07:51:18 +0530 |
| commit | 6e8adbaabedba12e81bf0fdfe3dc42108255bd11 (patch) | |
| tree | 6d0cb2f5d6d029ea93432a1f906ab74bcdfb7917 /time | |
| parent | 22912c4af19b9055ed95779c4d16020fe3a449eb (diff) | |
| download | hollowdark-6e8adbaabedba12e81bf0fdfe3dc42108255bd11.tar.xz hollowdark-6e8adbaabedba12e81bf0fdfe3dc42108255bd11.zip | |
Migrate remaining relative imports to @hollowdark/*; strip //-comments and doc references, JSDoc-only
Diffstat (limited to 'time')
| -rw-r--r-- | time/calendar.ts | 67 | ||||
| -rw-r--r-- | time/gameTime.ts | 21 | ||||
| -rw-r--r-- | time/granularity.ts | 20 | ||||
| -rw-r--r-- | time/speed.ts | 12 |
4 files changed, 72 insertions, 48 deletions
diff --git a/time/calendar.ts b/time/calendar.ts index ce61de4..e2f229f 100644 --- a/time/calendar.ts +++ b/time/calendar.ts @@ -1,8 +1,7 @@ /** - * The Hollowdark calendar: 12 months × 30 days + a 5-day year-end festival, - * 365 days per year, 7-day weeks. + * Number of days in a regular month. * - * Month order (per docs/01-world.md and style-bible/00-style-bible.md): + * Month order: * * spring 1 Thawing 2 Greening 3 Blossomtide * summer 4 Highsun 5 Amberhaze 6 Harvestmark @@ -10,20 +9,30 @@ * winter 10 Rainfall 11 Hollowdark 12 Rimefrost * festival 13 Year's End Festival (5 days) * - * The year begins with Thawing (spring) and closes with the festival, - * which sits between Rimefrost and the next year's Thawing. This matches - * the style bible's cold-half / warm-half grouping and the "year-end - * festival" phrasing in world lore. + * The year begins with Thawing and closes with the festival, which sits + * between Rimefrost and the next year's Thawing. */ - export const DAYS_PER_MONTH = 30 + +/** Twelve named months — all except the festival. */ export const REGULAR_MONTH_COUNT = 12 + +/** Days in the Year's End Festival — the thirteenth "month". */ export const FESTIVAL_DAYS = 5 + +/** Month index for the festival. */ export const FESTIVAL_MONTH = 13 -export const MONTHS_PER_YEAR = REGULAR_MONTH_COUNT + 1 // 12 regular + festival -export const DAYS_PER_YEAR = REGULAR_MONTH_COUNT * DAYS_PER_MONTH + FESTIVAL_DAYS // 365 + +/** Total month slots in the calendar cycle (12 regular + festival). */ +export const MONTHS_PER_YEAR = REGULAR_MONTH_COUNT + 1 + +/** Days in a year: 12 × 30 + 5 = 365. */ +export const DAYS_PER_YEAR = REGULAR_MONTH_COUNT * DAYS_PER_MONTH + FESTIVAL_DAYS + +/** Seven-day weeks. */ export const DAYS_PER_WEEK = 7 +/** Month names in calendar order. Index 0 is Thawing; index 12 is the festival. */ export const MONTH_NAMES = [ 'Thawing', 'Greening', @@ -40,23 +49,25 @@ export const MONTH_NAMES = [ "Year's End Festival" ] as const +/** The named-month type derived from `MONTH_NAMES`. */ export type MonthName = (typeof MONTH_NAMES)[number] +/** Four regular seasons plus a distinct `festival` tag for the year-end. */ export type Season = 'spring' | 'summer' | 'autumn' | 'winter' | 'festival' const SEASON_BY_MONTH: readonly Season[] = [ - 'spring', // Thawing - 'spring', // Greening - 'spring', // Blossomtide - 'summer', // Highsun - 'summer', // Amberhaze - 'summer', // Harvestmark - 'autumn', // Firstfall - 'autumn', // Stormturn - 'autumn', // Ashfall - 'winter', // Rainfall - 'winter', // Hollowdark - 'winter', // Rimefrost + 'spring', + 'spring', + 'spring', + 'summer', + 'summer', + 'summer', + 'autumn', + 'autumn', + 'autumn', + 'winter', + 'winter', + 'winter', 'festival' ] @@ -66,21 +77,33 @@ function validateMonth(monthIndex: number): void { } } +/** + * Return the named month for a 1-based month index. Throws on invalid input. + * @param monthIndex 1 (Thawing) through 13 (Year's End Festival). + */ export function monthName(monthIndex: number): MonthName { validateMonth(monthIndex) return MONTH_NAMES[monthIndex - 1] as MonthName } +/** + * Return the season label for a 1-based month index. The festival has its + * own tag ('festival') rather than reusing one of the four seasons. + */ export function monthSeason(monthIndex: number): Season { validateMonth(monthIndex) return SEASON_BY_MONTH[monthIndex - 1] as Season } +/** + * Days in the given month. 30 for regular months, 5 for the festival. + */ export function daysInMonth(monthIndex: number): number { validateMonth(monthIndex) return monthIndex === FESTIVAL_MONTH ? FESTIVAL_DAYS : DAYS_PER_MONTH } +/** True when the supplied month index is the festival slot. */ export function isFestival(monthIndex: number): boolean { return monthIndex === FESTIVAL_MONTH } diff --git a/time/gameTime.ts b/time/gameTime.ts index 2f2fed7..adebc4f 100644 --- a/time/gameTime.ts +++ b/time/gameTime.ts @@ -8,16 +8,15 @@ import { REGULAR_MONTH_COUNT, daysInMonth, monthName -} from './calendar' +} from '@hollowdark/time/calendar' /** * A position in game time. Immutable — all arithmetic returns a new value. * - * year integer (negative allowed for pre-1111 historical events) + * year integer (negative allowed for pre-epoch historical events) * month 1..12 for regular months, 13 for year-end festival * day 1..30 for regular months, 1..5 for festival * tickOfDay 0 outside crisis mode; crisis mode subdivides the day - * (docs/22-crisis-mode.md) */ export interface GameTime { readonly year: number @@ -61,23 +60,21 @@ function absoluteDays(time: GameTime): number { } function fromAbsoluteDays(abs: number, tickOfDay: number): GameTime { - // Guard against non-integer arithmetic drift — time is whole days only, - // tickOfDay handles sub-day resolution in crisis mode. if (!Number.isFinite(abs)) { throw new Error(`Non-finite absolute day count: ${abs}`) } const year = Math.floor(abs / DAYS_PER_YEAR) - const rem = abs - year * DAYS_PER_YEAR // 0..364 - const doy = rem + 1 // 1..365 - const festivalStart = REGULAR_MONTH_COUNT * DAYS_PER_MONTH + 1 // 361 + const rem = abs - year * DAYS_PER_YEAR + const doy = rem + 1 + const festivalStart = REGULAR_MONTH_COUNT * DAYS_PER_MONTH + 1 let month: number let day: number if (doy >= festivalStart) { month = FESTIVAL_MONTH - day = doy - festivalStart + 1 // 1..5 + day = doy - festivalStart + 1 } else { - month = Math.floor((doy - 1) / DAYS_PER_MONTH) + 1 // 1..12 - day = ((doy - 1) % DAYS_PER_MONTH) + 1 // 1..30 + month = Math.floor((doy - 1) / DAYS_PER_MONTH) + 1 + day = ((doy - 1) % DAYS_PER_MONTH) + 1 } return { year, month, day, tickOfDay } } @@ -105,7 +102,6 @@ export function addMonths(time: GameTime, months: number): GameTime { if (!Number.isInteger(months)) { throw new Error(`addMonths requires an integer (got ${months})`) } - // Zero-based cycle arithmetic: months are 1..13, so we work in 0..12. const totalCycles = (time.month - 1) + months const yearDelta = Math.floor(totalCycles / MONTHS_PER_YEAR) const monthIndex = ((totalCycles % MONTHS_PER_YEAR) + MONTHS_PER_YEAR) % MONTHS_PER_YEAR @@ -171,7 +167,6 @@ export function toAbsoluteDays(time: GameTime): number { return absoluteDays(time) } -// Re-exports for convenience at the 'time' import. export { DAYS_PER_MONTH, DAYS_PER_WEEK, diff --git a/time/granularity.ts b/time/granularity.ts index 0643c47..1acb122 100644 --- a/time/granularity.ts +++ b/time/granularity.ts @@ -1,12 +1,7 @@ /** - * Tick granularity by life stage. - * - * One tick represents a different span of time depending on the character's - * age — infancy advances in years because there isn't weekly texture worth - * resolving, adulthood in weeks because that's the rhythm the design lives - * at. See docs/05-time-system.md and ARCHITECTURE.md §5. + * The eight life stages a character moves through. Used to pick how much + * wall-clock time one simulation tick represents at a given age. */ - export type LifeStage = | 'infancy' | 'early_childhood' @@ -17,8 +12,15 @@ export type LifeStage = | 'late_adult' | 'elderly' +/** How much game time one tick advances. */ export type TickUnit = 'year' | 'season' | 'month' | 'week' +/** + * Tick granularity per life stage. Infancy advances in years because + * there isn't weekly texture worth resolving; adulthood in weeks because + * that's the rhythm the design lives at; elderly in months as the pace + * slows again. + */ export const TICK_UNIT_BY_LIFE_STAGE: Readonly<Record<LifeStage, TickUnit>> = { infancy: 'year', early_childhood: 'season', @@ -30,6 +32,9 @@ export const TICK_UNIT_BY_LIFE_STAGE: Readonly<Record<LifeStage, TickUnit>> = { elderly: 'month' } +/** + * Bucket an age in whole years into its life stage. Throws on negative age. + */ export function lifeStageForAge(ageYears: number): LifeStage { if (ageYears < 0) throw new Error(`Invalid age: ${ageYears}`) if (ageYears < 3) return 'infancy' @@ -42,6 +47,7 @@ export function lifeStageForAge(ageYears: number): LifeStage { return 'elderly' } +/** Shortcut: map an age directly to its tick unit. */ export function tickUnitForAge(ageYears: number): TickUnit { return TICK_UNIT_BY_LIFE_STAGE[lifeStageForAge(ageYears)] } diff --git a/time/speed.ts b/time/speed.ts index 9a9bb11..d5cba2e 100644 --- a/time/speed.ts +++ b/time/speed.ts @@ -1,11 +1,11 @@ /** - * Player-controlled simulation speed. Only three states ever exist - * (docs/05-time-system.md): time is stopped, running at reading pace, - * or running fast with compressed flow. - * - * Scenes auto-set the effective speed to 'paused'; the intended speed is - * preserved so the simulation returns to it when the scene resolves. + * Player-controlled simulation speed. Only three states ever exist: time + * is stopped, running at reading pace, or running fast with compressed + * flow. Scenes auto-set the effective speed to `paused`; the intended + * speed is preserved so the simulation returns to it when the scene + * resolves. */ export type Speed = 'paused' | 'play' | 'fast' +/** The three speeds, in canonical order. */ export const SPEEDS: readonly Speed[] = ['paused', 'play', 'fast'] |
