Macros
Calendaria runs macros two ways: from trigger configuration on the Macros tab, and from a macro attached to a calendar note.
Macro triggers
Section titled “Macro triggers”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.
Time-Based Triggers
Section titled “Time-Based Triggers”| Trigger | Description |
|---|---|
| Dawn (Sunrise) | Fires at sunrise |
| Dusk (Sunset) | Fires at sunset |
| Midday (Noon) | Fires at noon |
| Midnight | Fires at midnight |
| New Day | Fires 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.
Season triggers
Section titled “Season triggers”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.
Moon phase triggers
Section titled “Moon phase triggers”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.
Event-attached macros
Section titled “Event-attached macros”A calendar note can carry a macro that runs when the event triggers.
Trigger conditions
Section titled “Trigger conditions”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.
The scope parameter
Section titled “The scope parameter”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.
Context data
Section titled “Context data”// Event trigger contextconst { event } = scope;console.log(event.id); // Note page IDconsole.log(event.name); // Note nameconsole.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}Trigger context
Section titled “Trigger context”Dawn, dusk, midday, midnight
Section titled “Dawn, dusk, midday, midnight”const { trigger, worldTime, components, calendar } = scope;console.log(trigger); // "sunrise", "sunset", "midday", "midnight"console.log(worldTime); // Current world time in secondsconsole.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.
New day
Section titled “New day”const { trigger, previous, current, calendar } = scope;console.log(trigger); // "newDay"console.log(previous.year); // Previous date componentsconsole.log(current.year); // Current date componentsconsole.log(calendar); // Active calendar objectSeason change
Section titled “Season change”const { trigger, previous, current, previousSeason, currentSeason, calendar } = scope;console.log(trigger); // "seasonChange"console.log(previous); // Previous date componentsconsole.log(current); // Current date componentsconsole.log(previousSeason); // Previous season object { name, ... }console.log(currentSeason); // Current season object { name, ... }console.log(calendar); // Active calendar objectMoon phase change
Section titled “Moon phase change”const { trigger, moon } = scope;console.log(trigger); // "moonPhaseChange"console.log(moon.moonIndex); // Moon indexconsole.log(moon.moonName); // Moon nameconsole.log(moon.visible); // Whether the moon is currently visibleconsole.log(moon.previousPhaseIndex);console.log(moon.previousPhaseName);console.log(moon.currentPhaseIndex);console.log(moon.currentPhaseName);Example macros
Section titled “Example macros”Time control
Section titled “Time control”// Advance 1 hourawait CALENDARIA.api.advanceTime({ hour: 1 });
// Advance 8 hours (long rest)await CALENDARIA.api.advanceTime({ hour: 8 });
// Advance 1 dayawait CALENDARIA.api.advanceTime({ day: 1 });
// Jump to specific dateawait CALENDARIA.api.jumpToDate({ year: 1492, month: 5, day: 15 });
// Advance to next sunriseawait CALENDARIA.api.advanceTimeToPreset('sunrise');
// Advance to next sunsetawait CALENDARIA.api.advanceTimeToPreset('sunset');
// Force the cinematic overlay when advancingawait CALENDARIA.api.advanceTime({ day: 7 }, { cinematic: true });await CALENDARIA.api.jumpToDate({ year: 1492, month: 5, day: 15 }, { cinematic: true });Clock control
Section titled “Clock control”// Toggle the real-time clock on/offCALENDARIA.api.toggleClock();
// Start the clock (if not already running)if (!CALENDARIA.api.isClockRunning()) CALENDARIA.api.startClock();
// Stop the clockCALENDARIA.api.stopClock();
// Check current clock speed (game seconds per real second)const speed = CALENDARIA.api.getClockSpeed();ui.notifications.info(`Clock speed: ${speed}x`);Display information
Section titled “Display information”// Show current date/timeconst now = CALENDARIA.api.getCurrentDateTime();const formatted = CALENDARIA.api.formatDate(now, 'datetime24');ChatMessage.create({ content: `<b>Current Time:</b> ${formatted}` });
// Show weatherconst weather = CALENDARIA.api.getCurrentWeather();const severity = CALENDARIA.api.getWeatherSeverityLabel(weather.severity);ChatMessage.create({ content: `<b>Weather:</b> ${weather.label}, ${weather.temperature} (${severity})`});
// Show moon phaseconst phase = CALENDARIA.api.getMoonPhase(0);ChatMessage.create({ content: `<b>Moon:</b> ${phase.name}` });
// Show seasonconst season = CALENDARIA.api.getCurrentSeason();ChatMessage.create({ content: `<b>Season:</b> ${season.name}` });Check conditions
Section titled “Check conditions”// 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}!`);}Notes management
Section titled “Notes management”// Create a quick noteconst 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 eventsconst 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}`);}Weather control
Section titled “Weather control”// Set specific weatherawait 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 zoneawait CALENDARIA.api.generateWeather();
// Get forecast (synchronous, returns the forecast array)const forecast = CALENDARIA.api.getWeatherForecast({ days: 7 });
// Clear all weather historyawait CALENDARIA.api.clearWeatherHistory({ all: true });
// Clear future history onlyawait CALENDARIA.api.clearWeatherHistory({ future: true });For developers
Section titled “For developers”See API Reference and Hooks.