Skip to content
3 Death Saves

Macros

Calendaria runs macros two ways: from trigger configuration on the Macros tab, and from a macro attached to a calendar note.


The Macros tab in the settings panel holds the trigger configuration. Trigger macros run on the primary GM’s client only.

The Set Date dialog opens with Skip Event Triggers checked. While it is checked, setting a date or jumping to a saved timepoint runs no macro on this page: time-based, new day, season, moon phase, and event-attached macros are all suppressed. To let the jump fire them, uncheck it.

TriggerDescription
Dawn (Sunrise)Fires at sunrise
Dusk (Sunset)Fires at sunset
Midday (Noon)Fires at noon
MidnightFires at midnight
New DayFires when the day changes

Dawn, dusk, midday, and midnight only fire when the clock moves forward. Rewinding time fires none of them.

Advancing across more than one day fires each of those four once per day crossed. Very long advances stop replaying intermediate days past an internal cap.

Each row pairs a season with a macro. Pick a season to fire on entering it, or All Seasons to fire on any season change.

Add Trigger appends a row, preselected to the first season that has no trigger yet. The trash button removes a row. When every season already has a trigger, the button warns instead of adding one.

A calendar with no seasons shows a hint in place of the controls, and no season trigger can be added there.

Each row pairs a moon and a phase with a macro. Pick All Moons or All Phases to match any.

Add Trigger appends a row, preselected to the first moon and phase combination that has no trigger yet. The trash button removes a row. When every combination already has a trigger, the button warns instead of adding one.

A calendar with no moons shows a hint in place of the controls, and no moon trigger can be added there.

Deleting a moon in the Calendar Editor removes its triggers. Triggers for the remaining moons still point at the same moons.


A calendar note can carry a macro that runs when the event triggers.

An event triggers when time crosses its start time. A multi-day event also fires on each day it spans, with progress data attached.

An event fires at most once per in-game day. Rewinding within the same day and advancing past the start time again does not run the macro a second time.

A macro on a note authored by a player runs only if that player has permission to execute it. A macro set as a note preset’s default macro runs regardless of the author’s permission.

The Silent flag suppresses the chat announcement at an event’s start, and the attached macro still runs. On a multi-day event, Silent skips the daily progress path entirely: neither the progress hook nor its macro fires.

Calendaria passes context to a macro through Foundry’s scope parameter. Destructure it inside the macro.

const { event, trigger } = scope;

scope is available in macro code without any import.

// Event trigger context
const { event } = scope;
console.log(event.id); // Note page ID
console.log(event.name); // Note name
console.log(event.flagData); // Full note data (startDate, endDate, categories, etc.)
// Multi-day progress context (if applicable)
const { trigger, progress } = scope;
if (trigger === 'multiDayProgress') {
console.log(progress.currentDay); // Current day number
console.log(progress.totalDays); // Total event duration
console.log(progress.percentage); // Completion percentage
console.log(progress.isFirstDay); // boolean
console.log(progress.isLastDay); // boolean
}

const { trigger, worldTime, components, calendar } = scope;
console.log(trigger); // "sunrise", "sunset", "midday", "midnight"
console.log(worldTime); // Current world time in seconds
console.log(components); // { year, month, dayOfMonth, hour, minute, ... }

components is the raw calendar component set: month and dayOfMonth are zero-indexed, and year carries no year-zero offset. The previous and current objects on the new day and season triggers do carry it.

const { trigger, previous, current, calendar } = scope;
console.log(trigger); // "newDay"
console.log(previous.year); // Previous date components
console.log(current.year); // Current date components
console.log(calendar); // Active calendar object
const { trigger, previous, current, previousSeason, currentSeason, calendar } = scope;
console.log(trigger); // "seasonChange"
console.log(previous); // Previous date components
console.log(current); // Current date components
console.log(previousSeason); // Previous season object { name, ... }
console.log(currentSeason); // Current season object { name, ... }
console.log(calendar); // Active calendar object
const { trigger, moon } = scope;
console.log(trigger); // "moonPhaseChange"
console.log(moon.moonIndex); // Moon index
console.log(moon.moonName); // Moon name
console.log(moon.visible); // Whether the moon is currently visible
console.log(moon.previousPhaseIndex);
console.log(moon.previousPhaseName);
console.log(moon.currentPhaseIndex);
console.log(moon.currentPhaseName);

// Advance 1 hour
await CALENDARIA.api.advanceTime({ hour: 1 });
// Advance 8 hours (long rest)
await CALENDARIA.api.advanceTime({ hour: 8 });
// Advance 1 day
await CALENDARIA.api.advanceTime({ day: 1 });
// Jump to specific date
await CALENDARIA.api.jumpToDate({ year: 1492, month: 5, day: 15 });
// Advance to next sunrise
await CALENDARIA.api.advanceTimeToPreset('sunrise');
// Advance to next sunset
await CALENDARIA.api.advanceTimeToPreset('sunset');
// Force the cinematic overlay when advancing
await CALENDARIA.api.advanceTime({ day: 7 }, { cinematic: true });
await CALENDARIA.api.jumpToDate({ year: 1492, month: 5, day: 15 }, { cinematic: true });
// Toggle the real-time clock on/off
CALENDARIA.api.toggleClock();
// Start the clock (if not already running)
if (!CALENDARIA.api.isClockRunning()) CALENDARIA.api.startClock();
// Stop the clock
CALENDARIA.api.stopClock();
// Check current clock speed (game seconds per real second)
const speed = CALENDARIA.api.getClockSpeed();
ui.notifications.info(`Clock speed: ${speed}x`);
// Show current date/time
const now = CALENDARIA.api.getCurrentDateTime();
const formatted = CALENDARIA.api.formatDate(now, 'datetime24');
ChatMessage.create({ content: `<b>Current Time:</b> ${formatted}` });
// Show weather
const weather = CALENDARIA.api.getCurrentWeather();
const severity = CALENDARIA.api.getWeatherSeverityLabel(weather.severity);
ChatMessage.create({
content: `<b>Weather:</b> ${weather.label}, ${weather.temperature} (${severity})`
});
// Show moon phase
const phase = CALENDARIA.api.getMoonPhase(0);
ChatMessage.create({ content: `<b>Moon:</b> ${phase.name}` });
// Show season
const season = CALENDARIA.api.getCurrentSeason();
ChatMessage.create({ content: `<b>Season:</b> ${season.name}` });
// Is it night?
const isNight = CALENDARIA.api.isNighttime();
ui.notifications.info(isNight ? 'It is nighttime' : 'It is daytime');
// Is it a rest day?
if (CALENDARIA.api.isRestDay()) {
ui.notifications.info('Today is a rest day');
}
// Is it a festival?
if (CALENDARIA.api.isFestivalDay()) {
const festival = CALENDARIA.api.getCurrentFestival();
ui.notifications.info(`Today is ${festival.name}!`);
}
// Create a quick note
const now = CALENDARIA.api.getCurrentDateTime();
await CALENDARIA.api.createNote({
name: 'Session Note',
content: '<p>Something important happened here.</p>',
startDate: { year: now.year, month: now.month, day: now.day },
allDay: true
});
// Get today's events
const notes = CALENDARIA.api.getNotesForDate(now.year, now.month, now.day);
if (notes.length > 0) {
const list = notes.map((n) => n.name).join(', ');
ui.notifications.info(`Today: ${list}`);
}
// Set specific weather
await CALENDARIA.api.setWeather('thunderstorm', { temperature: 55 });
// Override wind and precipitation on top of a preset (morning period only)
await CALENDARIA.api.setWeather('cloudy', {
period: 'morning',
temperature: 11,
wind: { speed: 2, direction: 'SW', forced: false }, // speed 0-5; direction: compass string ('SW') or degrees (225)
precipitation: { type: null, intensity: 0 } // type: null|drizzle|rain|snow|sleet|hail
});
// Generate weather from climate zone
await CALENDARIA.api.generateWeather();
// Get forecast (synchronous, returns the forecast array)
const forecast = CALENDARIA.api.getWeatherForecast({ days: 7 });
// Clear all weather history
await CALENDARIA.api.clearWeatherHistory({ all: true });
// Clear future history only
await CALENDARIA.api.clearWeatherHistory({ future: true });

See API Reference and Hooks.