A TADS3/adv3 module for handling calendars and calendar-based cycles

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.
                        c.advanceDay();
                }
        }

        // 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?)

2 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 Periods.

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 Periods 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