Skip to content

Calendar Time v2.1.1

Ageing — Two Time Models

Calendar Time supports two distinct ageing mechanics. Pick the one that matches your game design.

Status
Current
Version
v2.1.1
Source updated
2026-08-09
Generated on
2026-10-30

Correction · added 2026-10-28, after the v2.1.1 release

This page is the v2.1.1 tagged guide text. Its Model 2 recipe connects date_changed to a handler taking two DateTime arguments, which the v2.1.1 signal never emits, and it assumes one growth tick per signal even though a single advance can cross several days. The corrected recipe below comes from the plugin's post-release guide source; the v2.1.1 runtime already behaves this way. DateChangeEvent carries the signed day span as change.

func _ready() -> void:
    clock.signal_bus.date_changed.connect(_on_date_changed)

var growth_stage: int = 0
const MAX_STAGE: int = 3

func _on_date_changed(event: DateChangeEvent) -> void:
    # `change` is the signed day span of this advance, not always 1.
    growth_stage = mini(MAX_STAGE, growth_stage + maxi(event.change, 0))
    _update_sprite(growth_stage)

Source: calendar_time_dev@546d999 (post-release). Not part of the v2.1.1 tagged docs.

Calendar Time supports two distinct ageing mechanics. Pick the one that matches your game design.

Model 1: Clock-time ageing (continuous)

"This effect lasts N game-seconds."

AgeService tracks elapsed game time from GameClock.game_seconds_advanced. Ageing scales with clock speed — if you speed up the clock, ageing speeds up proportionally.

Use for:

  • Potion expiry ("expires in 5 game-minutes")
  • Buff/debuff durations
  • Cooldown timers
  • Real-time effects that should pause when the clock pauses
# One AgeService per clock; the demo assigns it where a clock lacks one.
var age_service:= AgeService.new()
age_service.clock= clock

# Scene usage: an AgeComponent node with `clock` and `age_preset` bound.
# In code:
var component:= AgeComponent.new(my_preset, clock)
add_child(component)

# Later: how much time has elapsed for this component?
var elapsed:= component.age_state.get_total_elapsed()
var current:= component.age_state.current

AddAfterGameSeconds is a convenience node: it adds a child node after N game-seconds have elapsed on the bound clock.

Model 2: Calendar-date ageing (discrete)

"This crop takes 3 game-days to mature."

Listen to clock.signal_bus.date_changed and advance growth stages discretely. Each calendar day triggers exactly one growth tick, regardless of clock speed. Speeding up the clock makes days pass faster, but each day still advances one stage.

This is the farming-game model (Stardew Valley, Rune Factory, Harvest Moon).

Use for:

  • Crop growth ("3 days to harvest")
  • Building construction ("7 days to complete")
  • Character aging ("1 year per game-year")
  • Seasonal changes
func _ready()-> void:
    clock.signal_bus.date_changed.connect(_on_date_changed)

var growth_stage:int = 0
const MAX_STAGE:int = 3

func _on_date_changed(current:DateTime, _previous:DateTime)-> void:
    if growth_stage< MAX_STAGE:
        growth_stage+= 1
        _update_sprite(growth_stage)

DateChangeEvent, DateChangeSpawner, DateChangeRandomizer, and DateChangeRandomToggler are ready-made nodes for common date-triggered behaviors.

Choosing between the models

Question Model
Should it pause when the clock pauses? Both do
Should it scale smoothly with clock speed? Model 1 (clock-time)
Should it advance in discrete day/month/year steps? Model 2 (calendar-date)
Is it a duration ("lasts N seconds")? Model 1
Is it a progression ("takes N days")? Model 2
Farming, construction, seasons? Model 2
Cooldowns, expiry, buffs? Model 1

Combining both models

Both models read from the same GameClock. Pausing the clock pauses both. Saving and loading preserve both: Model 1 through AgeComponent serialization, Model 2 through the calendar date in TimeSnapshot.

AgeService internals

AgeService is a per-clock Resource (not a singleton). Each clock can have its own AgeService with independent components. The service subscribes to game_seconds_advanced and advances the registry states that AgeComponent nodes read.

AgeStateRegistry tracks visual states per clock. AgeState defines what a component looks like at each threshold (sprite frame, material, scale). AgePreset bundles named presets for quick authoring.

AgeSceneReplacement swaps a node's scene at an age threshold — useful for "sapling → tree" or "ruins → rebuilt house" transitions.

Using this in a game?

Calendar Time v2.1.1 — full source, playable demos, commercial license

The guide above is the same setup the shipped demos use. Try the build in your browser before you buy it.