Another little module, this one for handling calendar-based cycles. Right now it specifically handles calculating seasons and the phase of the moon: calendar github repo.

Under the hood the module uses TADS3’s `Date` class to keep track of the current date.

Seasons are computed based on fixed-date solstices and equinoxes (which will be accurate to +/- one day).

The phase of the moon is computed using the same approximation method used by nethack. This treats the phase as an integer between 1 and 8, with 1 being a new moon and 4 being a full moon. It should also be accurate to within a day.

For my usage I need something that keeps track of the flow of time, but I’m not concerned about a specific in-game year/date.

Usage is pretty simple, here’s a snippet from the demo “game”:

``````        newGame() {
local c, i;

// Create a calendar with a current date of June 22, 1979.
c = new Calendar(1979, 6, 22);

// Loop through a hundred days.
for(i = 1; i <= 100; i++) {
// Output information about the current date.
_logDate(c);

// Advance the calendar's date by one day.
}
}

// Log some stuff about the date:
_logDate(d) {
"Date: <<d.getMonthName()>> <<toString(d.getDay())>>,
<<toString(d.getYear())>>\n ";
"Season: <<d.getSeasonName()>>\n ";
"Phase of moon: <<toString(d.getMoonPhaseName())>>\n ";
"<.p> ";
}
``````

This just creates a calendar with the date June 22, 1979, and loops over the next 100 days, outputting information about each date. Partial transcript:

``````Date: June 22, 1979
Season: summer
Phase of moon: new

Date: June 23, 1979
Season: summer
Phase of moon: new

Date: June 24, 1979
Season: summer
Phase of moon: new

Date: June 25, 1979
Season: summer
Phase of moon: new

Date: June 26, 1979
Season: summer
Phase of moon: waxing crescent

Date: June 27, 1979
Season: summer
Phase of moon: waxing crescent

Date: June 28, 1979
Season: summer
Phase of moon: waxing crescent

Date: June 29, 1979
Season: summer
Phase of moon: waxing crescent

Date: June 30, 1979
Season: summer
Phase of moon: first quarter

Date: July 1, 1979
Season: summer
Phase of moon: first quarter

Date: July 2, 1979
Season: summer
Phase of moon: first quarter

Date: July 3, 1979
Season: summer
Phase of moon: first quarter

Date: July 4, 1979
Season: summer
Phase of moon: waxing gibbous
``````

This is another one where I dunno if it’ll help anyone else, but here it is.

5 Likes

So, based on the incredibly thorough and intricate library functionality you have shared to date, I am concluding that your WIP IMPLEMENTS ALL OF CREATION.

Was going to say “can’t wait to see it,”… unless I’m living it already!? (And if I am, what is the code to disable the MAGA mod?)

3 Likes

A little update.

The `Calendar` class now provides a `getSiderealTime()` method which returns the approximate Greenwich sidereal time at local midnight for the current (for the `Calendar` instance) date, and a `getLocalSiderealTime(hour?, longitude?)` method that returns the approximate local sidereal time for the given local hour (first arg) and longitude (second arg).

The module now caches computed “stuff” (like the season, lunar phase, and sidereal time at midnight), clearing the cached values when the `Calendar` instance’s date changes (and caching them when computed).

1 Like

Mid-sized update.

The `calendar` module now implements daily cycles with fixed “slots”. This is for games where there are discrete “blocks” of time during the day, but individual actions/turns don’t advance the game clock from one block to another.

This involves two classes, `DailyCycle` and `Period`. Basically a `Period` is an interval during the day and a `DailyCycle` is a container for one or more `Period`s.

To just jump in with an example, here’s one daily cycle provided by the module (in `calendarCycles.t`):

``````simpleDay: DailyCycle;
+Period 'early' 'Early Morning' +4;
+Period 'morning' 'Morning' +8;
+Period 'afternoon' 'Afternoon' +12;
+Period 'evening' 'Evening' +19;
+Period 'night' 'Nighttime' +22;
``````

Each `Period` is an ID, an optional name, and a starting hour.

Periods run from their stated starting hour until the next declared period. So in this example `early` runs from 04:00 to 07:00, because the next period after it is `morning` starting at 08:00.

Periods automatically “wrap” around the end of the day, so in this case `night` runs from 22:00 (the declared starting hour) until 03:00 (the start of `early`).

METHODS

There are a bunch of methods for working with `Period`s on `DailyCycle`, but you probably just want to use the ones defined directly on `Calendar`:

• `Calendar.setDailyCycle(obj)` sets the calendar’s daily cycle. the `simpleDay` cycle described above is the default. The module also provides `canonicalHours`, a daily cycle for the medieval canonical hours.
• `Calendar.setPeriod(id)` sets the calendar’s current hour to be the start of the given period
• `Calendar.setDateAndPeriod(year, month, day, id)` sets the calendar’s current date and time
• `Calendar.setPeriodNextDay(id)` advances the calendar’s current time to be the given period during the following day
• `Calendar.matchPeriod(h?)` returns the ID of the period matching the given hour for the current day. if no argument is given, the current hour is used
1 Like

A minor update.

You can now compile with the `-D CALENDAR_EVENTS` flag to enable calendar events. When enabled, objects can subscribe to a calendar object using the `eventHandler` interface.

AN EXAMPLE

For an object to use event subscriptions, you need to add the `EventListener` mixin to its class list.

By default, the object’s `eventHandler()` method will be called when a matching event is fired. The method must accept one argument (for an event object).

``````        // Declare an object with the EventListener class.
pebble: Thing, EventListener '(small) (round) pebble' 'pebble'
"A small, round pebble. "

eventHandler(e) {
"<.p>This is a useless event handler.<.p> ";
}
;
``````

Having done this, the object can subscribe for calendar notifications via:

``````        gCalendar.subscribe(pebble);
``````

Having done this `pebble.eventHandler()` will be called whenever the date/time of the global calendar object changes. The argument to the handler in this case will be the new date (an instance of the TADS3 builtin class `Date`).

I’m in the process of implementing logic for declaring daily schedules for NPCs, so my use case here is that calendar updates are based around the module’s standard daily cycle: “afternoon” becomes “evening”, “evening” becomes “night”, and so on, with transitions being due to event triggers instead of turn counts. As implemented `CalendarEvent` is probably less useful if you want “continuous time” (each turn takes n minutes or whatever).

The logic is provided by the `eventHandler` module and it supports arbitrary events, so in theory you could add fancier logic (like firing different events for hour, day, and so on changes) but I don’t have any plans to do so myself.

1 Like

if someone want to write First Things First II now has the main tool for handling timetravel… and the passing of time between time travelling

Best regards from Italy,
dott. Piergiorgio.

Hey jbg, this is very cool! In fact, I plan to create a puzzle that involves a kind of time travel to a moment in time that has a full-moon, so your module is exactly what I would be looking for anyway.
Could you give me a short hint on how to import it to an existing project? It is the first module I will use. Note that I do not use the workbench and code on Linux. Maybe you have some tipps for me
Thank you for your effort, and keep up the great work !

1 Like

Sure. I’ll assume you understand generally how file and path names work in linux, but let me know if you need anything clarified.

The way I handle this is I have a directory for all my TADS stuff, and projects (i.e. games) and modules are subdirectories of it.

So if the base TADS directory I’m working out of is `/home/jbg/tads/`, I’d go there and clone the repo:

``````> cd /home/jbg/tads
> git clone https://github.com/diegesisandmimesis/calendar
``````

That’ll create `/home/jbg/tads/calendar/`.

In my case the game I’m developing is tentatively called Sweet Thunder, so it’s in `/home/jbg/tads/thunder/`. In order to add the module to the project, I edit the makefile to:

• Add the path to all modules via `-Fs`. This has to be near the top of the makefile, before any of the `-lib` lines
• Add the module itself via `-lib`. This has to be near the end of the makefile
• Additionally add any preprocessor flags for the module via `-D`. Placement of these is more flexible, but I put them near the top of the file just to make them easier to find/edit

You can specific fully-qualified paths to directories and/or files, but I generally use relative paths to make things more portable. So if the module is in `/home/jbg/tads/calendar/` and the game is in `/home/jbg/tads/thunder/`, then the `makefile.t3m` looks something like:

``````-I .
-D LANGUAGE=en_us
-D MESSAGESTYLE=neu
-Fy obj
-Fo obj
-Fs ..
[other makefile stuff]
-lib calendar/calendar
``````

The lines at the top, before the `-Fs` line, are just general T3 makefile stuff, and yours might look slightly different.

The `-Fs ..` means to search for files using `..` as the base path. From the game directory, `/home/jbg/tads/thunder/`, that means `/home/jbg/tads/`, the top-level directory for the modules.

The `-lib` line is what actually includes the module, and `-lib calendar/calendar` means that, relative to the path specified by `-Fs`, look in a subdirectory called `calendar` for a file called `calendar.tl`. You could also use `-lib calendar/calendar.tl`, but `t3make` assumes the extension if one isn’t specified.

The `.tl` file is just the equivalent of the makefile for the module (I believe the extension means “TADS library”, but I’m not sure). It’s just a list of all the source files in the module, and is used by `t3make` to figure out what to add to the project.

If you’re compiling with the `-D CALENDAR_EVENTS` flag you’ll also need to install the `eventHandler` module the same way:

``````> cd /home/jbg/tads
> git clone https://github.com/diegesisandmimesis/eventHandler
``````

``````-lib eventHandler/eventHandler
…just before the `-lib calendar/calendar` line.
Each module has a `./demo/` subdirectory with demos/test cases, and each of them has a makefile that can probably be used as a reference (although the demos are designed to be compiled from the `./demo/` subdirectory, and so use `-Fs ../..` instead of `-Fs ..` because they’re one level deeper).