Skip to content

Commit 6658f81

Browse files
authored
Merge pull request #44 from paulcheeba/v13.5.0.0
V13.5.0.0
2 parents fd4c0b9 + d515e94 commit 6658f81

File tree

12 files changed

+1185
-105
lines changed

12 files changed

+1185
-105
lines changed

README.md

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# About Time Next
88

99
**About Time Next** is a timekeeping and event scheduling utility for Foundry VTT v13+. It is a spiritual successor to about-time by Tim Posney and is built on top of the original code in an attempt to keep legacy functions.
10-
It supports **D&D 5e v5.2+ native calendar**, **Seasons & Stars**, **Simple Calendar**, or falls back to Foundry's core time system.
10+
It supports **D&D 5e v5.2+ native calendar**, **Seasons & Stars**, **Simple Calendar Reborn**, or falls back to Foundry's core time system.
1111

1212
---
1313

@@ -21,10 +21,10 @@ It supports **D&D 5e v5.2+ native calendar**, **Seasons & Stars**, **Simple Cale
2121
```
2222

2323
2. Enable the module in your world.
24-
3. (Optional) Configure calendar system in settings: Auto-detect (default), D&D 5e Calendar, Simple Calendar, Seasons & Stars, or None.
24+
3. (Optional) Configure calendar system in settings: D&D 5e Calendar, Simple Calendar Reborn, Seasons & Stars, or None (default).
2525

2626
> Compatibility: Designed for FVTT v13 (min 13, max 13.x).
27-
> **Calendar Support:** D&D 5e v5.2+ native calendar, Seasons & Stars, Simple Calendar, and core time fallback.
27+
> **Calendar Support:** D&D 5e v5.2+ native calendar, Seasons & Stars, Simple Calendar Reborn, and core time fallback.
2828
> Settings dropdown dynamically shows only available calendar systems. Detection display shows all systems with status.
2929
3030
---
@@ -36,18 +36,18 @@ About Time Next uses a **calendar adapter system** to integrate with multiple ca
3636
**Supported Calendars:**
3737
- **D&D 5e Calendar (v5.2+)**: Native Foundry v13 calendar system with Harptos, Greyhawk, Gregorian, and Khorvaire calendars.
3838
- **Seasons & Stars**: Full integration with formatted date/time display.
39-
- **Simple Calendar**: Legacy compatibility layer retained for reference. Full support depends on a FVTT v13 update of the Simple Calendar module. SC integration may soon be removed if confirmed abandoned.
39+
- **Simple Calendar Reborn**: Full integration with time authority model. Simple Calendar has been reborn as Simple Calendar Reborn (maintained by Arctis Fireblight for Foundry v13+), and we have integrated it with complete time management support.
4040

41-
**Auto-Detection (Default):**
42-
When set to "Auto-detect", the module checks in priority order:
43-
1. Seasons & Stars (if module active with API)
44-
2. Simple Calendar (if module active with API)
45-
3. D&D 5e Calendar (if system v5.2+ with calendar configured)
46-
4. Falls back to "None" (Foundry core time)
47-
5. Additional calendars to be added in future updates.
41+
**Neutral Calendar Selection:**
42+
The module uses neutral selection logic with no automatic favoritism:
43+
- **0 calendars available** → Uses "None" (Foundry core time)
44+
- **1 calendar available** → Auto-selects that calendar
45+
- **2+ calendars available** → Uses "None" and shows selection dialog
46+
47+
All supported calendars (D&D 5e, Simple Calendar Reborn, Seasons & Stars) are treated equally. The module never automatically picks one third-party calendar over another.
4848

4949
**Calendar Integration Settings:**
50-
- Dropdown shows **only detected** calendars (plus "Auto-detect" and "None")
50+
- Dropdown shows **only detected** calendars (plus "None")
5151
- Detection info panel shows **all calendars** with ✓/✗ status
5252
- If your selected calendar becomes unavailable, module falls back automatically
5353

@@ -91,7 +91,7 @@ Enable **“Enable AT Time Manager”** to show a compact panel:
9191
<img width="300" height="151" alt="image" src="https://github.com/user-attachments/assets/8e631dd5-a7b5-4037-9bc5-28e7bebf7c7e" />
9292

9393
- **Play/Pause world time** (GM)
94-
- Live **clock display** (SC-formatted if SC is present)
94+
- Live **clock display** (calendar-formatted when available)
9595
- Small toggles for realtime behavior (GM):
9696
- **Link Pause** (pause realtime if the game is paused)
9797
- **Auto-Pause on Combat** (pause/resume around combats)
@@ -164,7 +164,7 @@ Actions (top buttons):
164164
- **Send Queue to Chat** (GM-whisper)
165165
- **Stop all Events** / **Stop all + 1h reminder**
166166

167-
> Time formatting uses the active calendar adapter (D&D 5e, Seasons & Stars, or Simple Calendar when available). Falls back to Foundry core time if no calendar system is configured.
167+
> Time formatting uses the active calendar adapter (D&D 5e, Seasons & Stars, or Simple Calendar Reborn when available). Falls back to Foundry core time if no calendar system is configured.
168168
169169
### Event Notification Cards
170170
When events trigger, they display standardized notification cards with detailed information:
@@ -262,7 +262,7 @@ Event notifications play automatically when scheduled events fire, helping GMs t
262262
- M4A (.m4a) - AAC audio, good quality
263263
Best practice: MP3 is the safest choice for maximum browser compatibility across all platforms (which is why we used it for the notification sounds in v13.2.0.0+).
264264

265-
> Date/time formatting uses the active calendar adapter (D&D 5e, Seasons & Stars, Simple Calendar when available), otherwise it falls back to Foundry core time.
265+
> Date/time formatting uses the active calendar adapter (D&D 5e, Seasons & Stars, Simple Calendar Reborn when available), otherwise it falls back to Foundry core time.
266266
267267
---
268268

@@ -280,7 +280,6 @@ Best practice: MP3 is the safest choice for maximum browser compatibility across
280280
- This module is part of the **OverEngineeredVTT Suite** and Requires the installation of the lightweight OEV Suite Monitor, a master module that tracks OEV module versions for you and lets you know when updates or new modules are available.
281281

282282
## Additional links
283-
284283
- Join our [Discord](https://discord.gg/VNZwZTCB5U) server
285284
- Support me on [Patreon](https://www.patreon.com/cw/u45257624)
286285

about-time.js

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// about-time.js — Entry point
2-
// v13.4.0.0 — D&D 5e Calendar integration (native calendar support for D&D 5e v5.2+)
2+
// v13.5.0.0 — Simple Calendar Reborn (SCR) integration + neutral calendar selection
33

44
import { registerSettings, MODULE_ID } from './module/settings.js';
55
import { preloadTemplates } from './module/preloadTemplates.js';
@@ -11,7 +11,8 @@ import { DTCalc } from './module/calendar/DTCalc.js';
1111
// Calendar Adapter System (v13.3.1.0 - Phase 1)
1212
import { CalendarAdapter } from './module/calendar/CalendarAdapter.js';
1313
import './module/calendar/Dnd5eAdapter.js'; // Self-registers (v13.3.5.0)
14-
import './module/calendar/SimpleCalendarAdapter.js'; // Self-registers
14+
import './module/calendar/SCRAdapter.js'; // Self-registers (v13.4.2.0 - Simple Calendar Reborn)
15+
// import './module/calendar/SimpleCalendarAdapter.js'; // ARCHIVED - v13 incompatible, preserved for reference
1516
import './module/calendar/SandSAdapter.js'; // Self-registers
1617

1718
// New (modular, non-destructive)
@@ -192,20 +193,31 @@ Hooks.once('ready', async () => {
192193

193194
let newSystem = "auto"; // Keep auto as default
194195

196+
// Count how many calendars are available
197+
const availableCount = [detected.dnd5e, detected.simpleCalendarReborn, detected.seasonsStars].filter(Boolean).length;
198+
195199
// If user had explicitly disabled SC, respect that
196200
if (legacySetting === false) {
197201
newSystem = "none";
198202
if (debug) console.log(`${MODULE_ID} | [Migration] User had disabled SC → setting to "none"`);
199203
}
200-
// If user had SC enabled and it's still available, use it explicitly
201-
else if (legacySetting === true && detected.simpleCalendar) {
202-
newSystem = "simple-calendar";
203-
if (debug) console.log(`${MODULE_ID} | [Migration] SC was enabled and is available → setting to "simple-calendar"`);
204+
// Only auto-migrate if there's exactly ONE calendar available
205+
// If multiple are available, let checkForCalendarChanges() prompt the user
206+
else if (legacySetting === true && availableCount === 1) {
207+
if (detected.simpleCalendarReborn) {
208+
newSystem = "simple-calendar-reborn";
209+
if (debug) console.log(`${MODULE_ID} | [Migration] Only SCR available → setting to "simple-calendar-reborn"`);
210+
} else if (detected.seasonsStars) {
211+
newSystem = "seasons-and-stars";
212+
if (debug) console.log(`${MODULE_ID} | [Migration] Only S&S available → setting to "seasons-and-stars"`);
213+
} else if (detected.dnd5e) {
214+
newSystem = "dnd5e";
215+
if (debug) console.log(`${MODULE_ID} | [Migration] Only D&D5e available → setting to "dnd5e"`);
216+
}
204217
}
205-
// If SC was enabled but no longer available, check for S&S
206-
else if (legacySetting === true && !detected.simpleCalendar && detected.seasonsStars) {
207-
newSystem = "seasons-and-stars";
208-
if (debug) console.log(`${MODULE_ID} | [Migration] SC unavailable but S&S detected → setting to "seasons-and-stars"`);
218+
else if (legacySetting === true && availableCount > 1) {
219+
if (debug) console.log(`${MODULE_ID} | [Migration] Multiple calendars available (${availableCount}) → keeping auto to allow user choice`);
220+
// Keep auto so checkForCalendarChanges() will prompt
209221
}
210222

211223
// Save migrated setting
@@ -236,15 +248,6 @@ Hooks.once('ready', async () => {
236248
// INITIALIZATION
237249
// ============================================================================
238250

239-
try {
240-
if (game.settings.get(MODULE_ID, "debug")) {
241-
if (!game.modules.get("foundryvtt-simple-calendar")?.active) {
242-
console.warn(`${MODULE_ID} | Simple Calendar not active (optional).`);
243-
}
244-
}
245-
} catch {
246-
// ignore
247-
}
248251
PseudoClock.init();
249252
ElapsedTime.init();
250253

@@ -302,9 +305,23 @@ Hooks.once('ready', () => {
302305

303306
// --- Pause/Combat coupling (merged from 13.0.6.7.4) ---
304307
Hooks.once('ready', () => {
305-
// Keep AT’s internal runner in sync with Foundry’s pause (if linked)
308+
// Helper to check if SCR is managing time/pause behavior
309+
const isSCRActive = () => {
310+
try {
311+
const calendarSystem = game.settings.get(MODULE_ID, "calendar-system");
312+
return calendarSystem === "simple-calendar-reborn" ||
313+
(calendarSystem === "auto" && globalThis.SimpleCalendar?.api);
314+
} catch {
315+
return false;
316+
}
317+
};
318+
319+
// Keep AT's internal runner in sync with Foundry's pause (if linked)
306320
Hooks.on("pauseGame", (paused) => {
307321
try {
322+
// Skip if SCR is managing pause behavior
323+
if (isSCRActive()) return;
324+
308325
const link = !!getSettingSafe("rtLinkPause", true);
309326
if (!link) return;
310327
if (paused) {
@@ -323,6 +340,9 @@ Hooks.once('ready', () => {
323340
let _rtWasRunning = false;
324341

325342
async function handleCombatStart() {
343+
// Skip if SCR is managing combat pause behavior
344+
if (isSCRActive()) return;
345+
326346
// Only act when the first combat appears (collection 0 -> 1)
327347
const count = (game.combats?.size ?? 0);
328348
if (count !== 1) return;
@@ -342,6 +362,8 @@ Hooks.once('ready', () => {
342362
}
343363

344364
async function handleCombatEnd() {
365+
// Skip if SCR is managing combat pause behavior
366+
if (isSCRActive()) return;
345367
// Only act when we just removed the last combat (collection 1 -> 0)
346368
const count = (game.combats?.size ?? 0);
347369
if (count !== 0) return;

changelog.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,131 @@
1+
# Changelog (v13.5.0.0)
2+
3+
**🎉 MAJOR UPDATE: Simple Calendar Reborn Integration + Neutral Calendar Selection**
4+
5+
**Simple Calendar** has been forked and updated for Foundry v13+ as **Simple Calendar Reborn** by Arctis Fireblight. This release brings full SCR integration to About Time Next with proper time authority delegation.
6+
7+
---
8+
9+
## Simple Calendar Reborn Support ✅
10+
11+
- **New SCRAdapter Class**
12+
Added `SCRAdapter.js` implementing **Time Authority Model B**: When SCR is active, it becomes the authoritative time controller. ATN routes all time manipulation through SCR's API instead of directly modifying `game.time.worldTime`.
13+
14+
- **Time Routing Through Adapters**
15+
All ATN time controls now use the adapter pattern:
16+
- Realtime clock → `adapter.advanceTime(seconds)`
17+
- Fast forward/rewind buttons → `adapter.advanceTime(seconds)`
18+
- Time-of-day quick jumps → `adapter.advanceTime(seconds)`
19+
20+
When SCR is active, these calls route through `SimpleCalendar.api.changeDate()`. When using Seasons & Stars or D&D5e, they use `game.time.advance()` as before.
21+
22+
- **SCR as Time Authority**
23+
When SCR is enabled, it controls worldTime and ATN enhances it with:
24+
- Event scheduling and notifications
25+
- Elapsed time tracking
26+
- Additional UI controls (mini panel, toolbar)
27+
28+
ATN respects SCR's pause/combat settings and bypasses its own hooks to prevent conflicts.
29+
30+
- **0-Based Indexing Support**
31+
SCRAdapter correctly handles Simple Calendar's 0-based month/day indexing (JavaScript Date-style), converting to 1-based display for users while maintaining API compatibility.
32+
33+
---
34+
35+
## Neutral Calendar Selection 🤝
36+
37+
- **No Calendar Hierarchy**
38+
Removed the concept of "top-tier" calendars. All supported calendars (D&D5e, Simple Calendar Reborn, Seasons & Stars) are now treated equally.
39+
40+
- **Ethical Auto-Detection**
41+
- **0 calendars available** → Use "none" (Foundry core time)
42+
- **1 calendar available** → Auto-select that calendar (no favoritism)
43+
- **2+ calendars available** → Use "none", show selection dialog
44+
45+
ATN no longer automatically picks one third-party calendar over another.
46+
47+
- **Selection Dialog for Multiple Calendars**
48+
When multiple calendars are detected, GMs see a dropdown with all available options sorted alphabetically. Users make an explicit choice with a note: "All calendar systems are equivalent. Choose based on your preference."
49+
50+
- **Change Detection**
51+
Dialog appears when new calendar modules are enabled, even if you're already using a different calendar system. Users are always informed of their options.
52+
53+
---
54+
55+
## Settings UI Enhancements 🎨
56+
57+
- **Calendar System Dropdown Tooltip**
58+
Added explanation of time authority: "⚙️ Time Authority: SCR controls worldTime when active; ATN manages worldTime for D&D5e/S&S."
59+
60+
- **Detection Display Updates**
61+
Calendar detection info now shows which system manages time:
62+
- "✓ D&D 5e Calendar (available) - *Uses ATN Time Management*"
63+
- "✓ Simple Calendar Reborn (available) - *Uses SCR Time Management*"
64+
- "✓ Seasons & Stars (available) - *Uses ATN Time Management*"
65+
66+
- **Smart Checkbox Disabling**
67+
When SCR is active, ATN's pause/combat settings are disabled with informational boxes showing SCR's equivalent settings:
68+
- `rtAutoPauseCombat` → Shows SCR's `combatRunning` value
69+
- `rtLinkPause` → Shows SCR's `unifyGameAndClockPause` value
70+
71+
Both display: "⚠️ **Managed by SCR:** [setting status]"
72+
73+
---
74+
75+
## Architecture Improvements 🏗️
76+
77+
- **Time Authority Models**
78+
Formalized two patterns:
79+
- **Model A** (S&S, D&D5e): ATN controls time, calendar provides display
80+
- **Model B** (SCR): Calendar controls time, ATN enhances with events
81+
82+
- **Logic Bypass for SCR**
83+
Added `isSCRActive()` helper in `about-time.js` that skips ATN's pause/combat hooks when SCR is managing behavior, preventing conflicts.
84+
85+
- **Timestamp-Based Events Confirmed**
86+
Events are stored as timestamps (seconds since epoch), making them calendar-agnostic. Switching between calendars preserves all events—only display format changes.
87+
88+
---
89+
90+
## Documentation 📚
91+
92+
- **SCR-ref-Doc.md**
93+
Comprehensive Simple Calendar Reborn API reference with ATN integration patterns, time authority explanation, and 0-based indexing notes.
94+
95+
- **SCR-Integration-Summary.md**
96+
Complete implementation guide covering time routing architecture, settings UI changes, logic bypass, and testing checklist.
97+
98+
- **Updated Reference Documentation**
99+
`referenceDocumentation.md` now includes calendar system status, adapter architecture, and integration details for all supported calendars.
100+
101+
---
102+
103+
## User Interface Refinements 🎨
104+
105+
- A new calendar scan is run on load and shows a dialog allowing you to pick the calendar you want ATN to use. This may be dismissed until the next load or until a different calendar is selected in the settings page.
106+
107+
---
108+
109+
## Documentation Updates 📚
110+
111+
- **README.md**
112+
Updated all Simple Calendar references to Simple Calendar Reborn with integration notes. Added explanation that Simple Calendar has been reborn and is now fully integrated with ATN.
113+
114+
---
115+
116+
## Migration Notes 📝
117+
118+
- **Legacy Simple Calendar**
119+
Simple Calendar v1.x is archived and incompatible with Foundry v13. Users should migrate to Simple Calendar Reborn v2.4.0+.
120+
121+
- **Existing Events**
122+
All existing events continue to work. Event timestamps are calendar-agnostic, so switching between SCR, S&S, and D&D5e preserves event timing.
123+
124+
- **Settings**
125+
First-time users with multiple calendars will see a selection dialog. Existing users keep their current selection unless they enable additional calendar modules.
126+
127+
---
128+
1129
# Changelog (v13.4.1.0)
2130

3131
**Dependencies + Docs**

macros/debugSettingsUI.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,21 @@ if (calendarInput) {
5353
if (!detectionBox && hint) {
5454
console.log("\n4. Detection box missing - manually adding...");
5555

56-
const detected = window.AboutTimeNext?.CalendarAdapter?.detectAvailableAsObject() || { simpleCalendar: false, seasonsStars: false };
56+
const detected = window.AboutTimeNext?.CalendarAdapter?.detectAvailableAsObject() || { dnd5e: false, simpleCalendarReborn: false, seasonsStars: false };
5757

5858
let detectionHTML = '<div style="margin-top: 0.5em; padding: 0.5em; background: rgba(0,0,0,0.1); border-radius: 3px; font-size: 0.9em;">';
5959
detectionHTML += '<strong>Detected Calendar Modules:</strong><br>';
6060

61-
if (detected.simpleCalendar) {
62-
detectionHTML += '✓ Simple Calendar (available)<br>';
61+
if (detected.dnd5e) {
62+
detectionHTML += '✓ D&D 5e Calendar (available)<br>';
6363
} else {
64-
detectionHTML += '✗ Simple Calendar (not detected)<br>';
64+
detectionHTML += '✗ D&D 5e Calendar (not detected)<br>';
65+
}
66+
67+
if (detected.simpleCalendarReborn) {
68+
detectionHTML += '✓ Simple Calendar Reborn (available)<br>';
69+
} else {
70+
detectionHTML += '✗ Simple Calendar Reborn (not detected)<br>';
6571
}
6672

6773
if (detected.seasonsStars) {

module/ATMiniPanel.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// module/ATMiniPanel.js
2-
// v13.3.4.0 — Refactored to use CalendarAdapter for time formatting
2+
// v13.5.0.0 — Time controls route through CalendarAdapter.advanceTime()
33

44
import { MODULE_ID } from "./settings.js";
55
import { CalendarAdapter } from "./calendar/CalendarAdapter.js";
@@ -435,7 +435,10 @@ function wireBehavior(ctx) {
435435
const s = parseDuration(d[id] || "0");
436436
if (!s) return;
437437
const sign = id?.startsWith("rwd") ? -1 : +1;
438-
await game.time.advance(sign * s);
438+
// Route time advancement through calendar adapter
439+
// When SCR is active, this delegates to SCR's time control
440+
const adapter = game.abouttime.CalendarAdapter.getActive();
441+
await adapter.advanceTime(sign * s);
439442
};
440443
steps.querySelectorAll(".atmp-btn").forEach((b) => {
441444
b.addEventListener("click", onStep);
@@ -456,7 +459,10 @@ function wireBehavior(ctx) {
456459
else if (id === "midnight") targetSOD = 0;
457460
if (targetSOD == null) return;
458461
const delta = secondsToNextBoundary(targetSOD);
459-
if (delta > 0) await game.time.advance(delta);
462+
// Route time advancement through calendar adapter
463+
// When SCR is active, this delegates to SCR's time control
464+
const adapter = game.abouttime.CalendarAdapter.getActive();
465+
if (delta > 0) await adapter.advanceTime(delta);
460466
};
461467
tod.querySelectorAll(".atmp-btn").forEach((b) => {
462468
b.addEventListener("click", onTod);

0 commit comments

Comments
 (0)