summaryrefslogtreecommitdiff
path: root/src/uqm/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/clock.c')
-rw-r--r--src/uqm/clock.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/uqm/clock.c b/src/uqm/clock.c
new file mode 100644
index 0000000..6660226
--- /dev/null
+++ b/src/uqm/clock.c
@@ -0,0 +1,314 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gameev.h"
+#include "globdata.h"
+#include "sis.h"
+ // for DrawStatusMessage()
+#include "setup.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "libs/tasklib.h"
+#include "libs/threadlib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+// the running of the game-clock is based on game framerates
+// *not* on the system (or translated) timer
+// and is hard-coded to the original 24 fps
+#define CLOCK_BASE_FRAMERATE 24
+
+// WARNING: Most of clock functions are only meant to be called by the
+// Starcon2Main thread! If you need access from other threads, examine
+// the locking system!
+// XXX: This mutex is only necessary because debugging functions
+// may access the clock and event data from a different thread
+static Mutex clock_mutex;
+
+static BOOLEAN
+IsLeapYear (COUNT year)
+{
+ // every 4th year but not 100s yet still 400s
+ return (year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0);
+}
+
+/* month is 1-based: 1=Jan, 2=Feb, etc. */
+static BYTE
+DaysInMonth (COUNT month, COUNT year)
+{
+ static const BYTE days_in_month[12] =
+ {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ };
+
+ if (month == 2 && IsLeapYear (year))
+ return 29; /* February, leap year */
+
+ return days_in_month[month - 1];
+}
+
+static void
+nextClockDay (void)
+{
+ ++GLOBAL (GameClock.day_index);
+ if (GLOBAL (GameClock.day_index) > DaysInMonth (
+ GLOBAL (GameClock.month_index),
+ GLOBAL (GameClock.year_index)))
+ {
+ GLOBAL (GameClock.day_index) = 1;
+ ++GLOBAL (GameClock.month_index);
+ if (GLOBAL (GameClock.month_index) > 12)
+ {
+ GLOBAL (GameClock.month_index) = 1;
+ ++GLOBAL (GameClock.year_index);
+ }
+ }
+
+ // update the date on screen
+ DrawStatusMessage (NULL);
+}
+
+static void
+processClockDayEvents (void)
+{
+ HEVENT hEvent;
+
+ while ((hEvent = GetHeadEvent ()))
+ {
+ EVENT *EventPtr;
+
+ LockEvent (hEvent, &EventPtr);
+
+ if (GLOBAL (GameClock.day_index) != EventPtr->day_index
+ || GLOBAL (GameClock.month_index) != EventPtr->month_index
+ || GLOBAL (GameClock.year_index) != EventPtr->year_index)
+ {
+ UnlockEvent (hEvent);
+ break;
+ }
+ RemoveEvent (hEvent);
+ EventHandler (EventPtr->func_index);
+
+ UnlockEvent (hEvent);
+ FreeEvent (hEvent);
+ }
+}
+
+BOOLEAN
+InitGameClock (void)
+{
+ if (!InitQueue (&GLOBAL (GameClock.event_q), NUM_EVENTS, sizeof (EVENT)))
+ return (FALSE);
+ clock_mutex = CreateMutex ("Clock Mutex", SYNC_CLASS_TOPLEVEL);
+ GLOBAL (GameClock.month_index) = 2;
+ GLOBAL (GameClock.day_index) = 17;
+ GLOBAL (GameClock.year_index) = START_YEAR; /* Feb 17, START_YEAR */
+ GLOBAL (GameClock.tick_count) = 0;
+ GLOBAL (GameClock.day_in_ticks) = 0;
+
+ return (TRUE);
+}
+
+BOOLEAN
+UninitGameClock (void)
+{
+ DestroyMutex (clock_mutex);
+ clock_mutex = NULL;
+
+ UninitQueue (&GLOBAL (GameClock.event_q));
+
+ return (TRUE);
+}
+
+// For debugging use only
+void
+LockGameClock (void)
+{
+ // Block the GameClockTick() for executing
+ if (clock_mutex)
+ LockMutex (clock_mutex);
+}
+
+// For debugging use only
+void
+UnlockGameClock (void)
+{
+ if (clock_mutex)
+ UnlockMutex (clock_mutex);
+}
+
+// For debugging use only
+BOOLEAN
+GameClockRunning (void)
+{
+ SIZE day_in_ticks;
+
+ if (!clock_mutex)
+ return FALSE;
+
+ LockMutex (clock_mutex);
+ day_in_ticks = GLOBAL (GameClock.day_in_ticks);
+ UnlockMutex (clock_mutex);
+
+ return day_in_ticks != 0;
+}
+
+void
+SetGameClockRate (COUNT seconds_per_day)
+{
+ SIZE new_day_in_ticks, new_tick_count;
+
+ new_day_in_ticks = (SIZE)(seconds_per_day * CLOCK_BASE_FRAMERATE);
+ if (GLOBAL (GameClock.day_in_ticks) == 0)
+ new_tick_count = new_day_in_ticks;
+ else if (GLOBAL (GameClock.tick_count) <= 0)
+ new_tick_count = 0;
+ else if ((new_tick_count = (SIZE)((DWORD)GLOBAL (GameClock.tick_count)
+ * new_day_in_ticks / GLOBAL (GameClock.day_in_ticks))) == 0)
+ new_tick_count = 1;
+ GLOBAL (GameClock.day_in_ticks) = new_day_in_ticks;
+ GLOBAL (GameClock.tick_count) = new_tick_count;
+}
+
+BOOLEAN
+ValidateEvent (EVENT_TYPE type, COUNT *pmonth_index, COUNT *pday_index,
+ COUNT *pyear_index)
+{
+ COUNT month_index, day_index, year_index;
+
+ month_index = *pmonth_index;
+ day_index = *pday_index;
+ year_index = *pyear_index;
+ if (type == RELATIVE_EVENT)
+ {
+ month_index += GLOBAL (GameClock.month_index) - 1;
+ year_index += GLOBAL (GameClock.year_index) + (month_index / 12);
+ month_index = (month_index % 12) + 1;
+
+ day_index += GLOBAL (GameClock.day_index);
+ while (day_index > DaysInMonth (month_index, year_index))
+ {
+ day_index -= DaysInMonth (month_index, year_index);
+ if (++month_index > 12)
+ {
+ month_index = 1;
+ ++year_index;
+ }
+ }
+
+ *pmonth_index = month_index;
+ *pday_index = day_index;
+ *pyear_index = year_index;
+ }
+
+ // translation: return (BOOLEAN) !(date < GLOBAL (Gameclock.date));
+ return (BOOLEAN) (!(year_index < GLOBAL (GameClock.year_index)
+ || (year_index == GLOBAL (GameClock.year_index)
+ && (month_index < GLOBAL (GameClock.month_index)
+ || (month_index == GLOBAL (GameClock.month_index)
+ && day_index < GLOBAL (GameClock.day_index))))));
+}
+
+HEVENT
+AddEvent (EVENT_TYPE type, COUNT month_index, COUNT day_index, COUNT
+ year_index, BYTE func_index)
+{
+ HEVENT hNewEvent;
+
+ if (type == RELATIVE_EVENT
+ && month_index == 0
+ && day_index == 0
+ && year_index == 0)
+ EventHandler (func_index);
+ else if (ValidateEvent (type, &month_index, &day_index, &year_index)
+ && (hNewEvent = AllocEvent ()))
+ {
+ EVENT *EventPtr;
+
+ LockEvent (hNewEvent, &EventPtr);
+ EventPtr->day_index = (BYTE)day_index;
+ EventPtr->month_index = (BYTE)month_index;
+ EventPtr->year_index = year_index;
+ EventPtr->func_index = func_index;
+ UnlockEvent (hNewEvent);
+
+ {
+ HEVENT hEvent, hSuccEvent;
+ for (hEvent = GetHeadEvent (); hEvent != 0; hEvent = hSuccEvent)
+ {
+ LockEvent (hEvent, &EventPtr);
+ if (year_index < EventPtr->year_index
+ || (year_index == EventPtr->year_index
+ && (month_index < EventPtr->month_index
+ || (month_index == EventPtr->month_index
+ && day_index < EventPtr->day_index))))
+ {
+ UnlockEvent (hEvent);
+ break;
+ }
+
+ hSuccEvent = GetSuccEvent (EventPtr);
+ UnlockEvent (hEvent);
+ }
+
+ InsertEvent (hNewEvent, hEvent);
+ }
+
+ return (hNewEvent);
+ }
+
+ return (0);
+}
+
+void
+GameClockTick (void)
+{
+ // XXX: This mutex is only necessary because debugging functions
+ // may access the clock and event data from a different thread
+ LockMutex (clock_mutex);
+
+ --GLOBAL (GameClock.tick_count);
+ if (GLOBAL (GameClock.tick_count) <= 0)
+ { // next day -- move the calendar
+ GLOBAL (GameClock.tick_count) = GLOBAL (GameClock.day_in_ticks);
+ // Do not do anything until the clock is inited
+ if (GLOBAL (GameClock.day_in_ticks) > 0)
+ {
+ nextClockDay ();
+ processClockDayEvents ();
+ }
+ }
+
+ UnlockMutex (clock_mutex);
+}
+
+void
+MoveGameClockDays (COUNT days)
+{
+ // XXX: This should theoretically hold the clock_mutex, but if
+ // someone manages to hit the debug button while this function
+ // runs, it's their own fault :-P
+
+ for ( ; days > 0; --days)
+ {
+ nextClockDay ();
+ processClockDayEvents ();
+ }
+ GLOBAL (GameClock.tick_count) = GLOBAL (GameClock.day_in_ticks);
+}