diff options
Diffstat (limited to 'src/uqm/clock.c')
-rw-r--r-- | src/uqm/clock.c | 314 |
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); +} |