From a4a1c356dfb7e9bf07e2c4886c4939a9d765b46f Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Fri, 29 Jun 2018 22:59:25 +0200 Subject: [PATCH] timeline: don't use ALLEGRO_TIMERs anymore, rely on delta time instead --- src/internal.c | 6 +- src/timeline.c | 224 ++++++++++++++++--------------------------------- src/timeline.h | 29 ++----- 3 files changed, 83 insertions(+), 176 deletions(-) diff --git a/src/internal.c b/src/internal.c index 3ecaf21..ec5585a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -457,12 +457,12 @@ static void DrawQueue(struct Game* game, struct TM_Action* queue, int clipX, int struct TM_Action* pom = queue; while (pom != NULL) { int width = al_get_text_width(game->_priv.font_console, pom->name); - al_draw_filled_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), pom->active ? al_map_rgba(255, 255, 255, 192) : al_map_rgba(0, 0, 0, 0)); + al_draw_filled_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), pom->started ? al_map_rgba(255, 255, 255, 192) : al_map_rgba(0, 0, 0, 0)); al_draw_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), al_map_rgb(255, 255, 255), 2); - al_draw_text(game->_priv.font_console, pom->active ? al_map_rgb(0, 0, 0) : al_map_rgb(255, 255, 255), pos, clipY, ALLEGRO_ALIGN_LEFT, pom->name); + al_draw_text(game->_priv.font_console, pom->started ? al_map_rgb(0, 0, 0) : al_map_rgb(255, 255, 255), pos, clipY, ALLEGRO_ALIGN_LEFT, pom->name); if (pom->delay) { - al_draw_textf(game->_priv.font_console, al_map_rgb(255, 255, 255), pos, clipY - (50 / 1800.0) * al_get_display_height(game->display), ALLEGRO_ALIGN_LEFT, "%d", pom->delay); + al_draw_textf(game->_priv.font_console, al_map_rgb(255, 255, 255), pos, clipY - (50 / 1800.0) * al_get_display_height(game->display), ALLEGRO_ALIGN_LEFT, "%d", (int)pom->delay); } if (strncmp(pom->name, "TM_BackgroundAction", 19) == 0) { diff --git a/src/timeline.c b/src/timeline.c index 9c9209f..0bfb55d 100644 --- a/src/timeline.c +++ b/src/timeline.c @@ -22,6 +22,15 @@ #include "utils.h" #include +static void DestroyArgs(struct TM_Arguments* args) { + struct TM_Arguments* pom; + while (args) { + pom = args->next; + free(args); + args = pom; + } +} + SYMBOL_EXPORT struct Timeline* TM_Init(struct Game* game, char* name) { PrintConsole(game, "Timeline Manager[%s]: init", name); struct Timeline* timeline = malloc(sizeof(struct Timeline)); @@ -35,26 +44,46 @@ SYMBOL_EXPORT struct Timeline* TM_Init(struct Game* game, char* name) { } SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { - /* process first element from queue - if returns true, delete it - and repeat for the next one */ + // NOTICE: current implementation has no way to know how much time + // an action has "eaten". This means that if you pass a huge delta + // that spans across multiple actions, the end result will most likely + // differ from what you would get from calling TM_Process repeatively + // with smaller deltas that sum up to the first value. Be aware! + + /* process first element from queue. + if returns true, delete it and repeat for the next one */ + delta *= 1000; bool next = true; while (next) { if (timeline->queue) { - timeline->queue->delta = delta; - if (*timeline->queue->function) { - if (!timeline->queue->active) { + timeline->queue->delta = delta / 1000.0; + + if (timeline->queue->active && timeline->queue->delay > 0.0) { + timeline->queue->delay -= delta; + if (timeline->queue->delay <= 0.0) { + timeline->queue->started = true; + if (timeline->queue->function) { + PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); + (*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START); + } else { + PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay reached (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); + } + timeline->queue->delay = 0.0; + } + } + + if (timeline->queue->function) { + if (!timeline->queue->started) { PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); (*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START); + timeline->queue->started = true; } - timeline->queue->active = true; if ((*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_RUNNING)) { PrintConsole(timeline->game, "Timeline Manager[%s]: queue: destroy action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); - timeline->queue->active = false; struct TM_Action* tmp = timeline->queue; timeline->queue = timeline->queue->next; (*tmp->function)(timeline->game, tmp, TM_ACTIONSTATE_DESTROY); - TM_DestroyArgs(tmp->arguments); + DestroyArgs(tmp->arguments); free(tmp->name); free(tmp); } else { @@ -62,15 +91,15 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { } } else { /* delay handling */ - if (timeline->queue->active) { + if (timeline->queue->started) { struct TM_Action* tmp = timeline->queue; timeline->queue = timeline->queue->next; free(tmp->name); free(tmp); } else { - if (!al_get_timer_started(timeline->queue->timer)) { - PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay started %d ms (%d - %s)", timeline->name, timeline->queue->delay, timeline->queue->id, timeline->queue->name); - al_start_timer(timeline->queue->timer); + if (!timeline->queue->active) { + PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay started %d ms (%d - %s)", timeline->name, (int)timeline->queue->delay, timeline->queue->id, timeline->queue->name); + timeline->queue->active = true; } next = false; } @@ -79,18 +108,18 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { next = false; } } - /* process all elements from background marked as active */ + + /* process all elements from background queue */ struct TM_Action *tmp, *tmp2, *pom = timeline->background; tmp = NULL; while (pom != NULL) { bool destroy = false; - pom->delta = delta; - if (pom->active) { - if (*pom->function) { - if ((*pom->function)(timeline->game, pom, TM_ACTIONSTATE_RUNNING)) { - pom->active = false; + pom->delta = delta / 1000.0; + if (pom->started) { + if (pom->function) { + if ((pom->function)(timeline->game, pom, TM_ACTIONSTATE_RUNNING)) { PrintConsole(timeline->game, "Timeline Manager[%s]: background: destroy action (%d - %s)", timeline->name, pom->id, pom->name); - (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); + (pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); if (tmp) { tmp->next = pom->next; } else { @@ -107,13 +136,23 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { } destroy = true; } + } else { + pom->delay -= delta; + if (pom->delay <= 0.0) { + PrintConsole(timeline->game, "Timeline Manager[%s]: background: delay reached, run action (%d - %s)", timeline->name, pom->id, pom->name); + pom->delay = 0.0; + if (pom->function) { + pom->function(timeline->game, pom, TM_ACTIONSTATE_START); + } + pom->started = true; + } } if (!destroy) { tmp = pom; pom = pom->next; } else { - TM_DestroyArgs(pom->arguments); + DestroyArgs(pom->arguments); free(pom->name); free(pom); tmp2 = tmp; @@ -131,93 +170,6 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { } } -static void PauseTimers(struct Timeline* timeline, bool pause) { - if (timeline->queue) { - if (timeline->queue->timer) { - if (pause) { - al_stop_timer(timeline->queue->timer); - } else if (!timeline->queue->active) { - al_resume_timer(timeline->queue->timer); - } - } - } - struct TM_Action* tmp = timeline->background; - while (tmp) { - if (tmp->timer) { - if (pause) { - al_stop_timer(tmp->timer); - } else if (!tmp->active) { - al_resume_timer(tmp->timer); - } - } - tmp = tmp->next; - } -} - -static void TM_Propagate(struct Timeline* timeline, enum TM_ActionState action) { - if (timeline->queue) { - if ((*timeline->queue->function) && (timeline->queue->active)) { - (*timeline->queue->function)(timeline->game, timeline->queue, action); - } - } - /* process all elements from background marked as active */ - struct TM_Action* pom = timeline->background; - while (pom != NULL) { - if (pom->active) { - if (*pom->function) { - (*pom->function)(timeline->game, pom, action); - } - } - pom = pom->next; - } -} - -SYMBOL_EXPORT void TM_Draw(struct Timeline* timeline) { - TM_Propagate(timeline, TM_ACTIONSTATE_DRAW); -} - -SYMBOL_EXPORT void TM_Pause(struct Timeline* timeline) { - PrintConsole(timeline->game, "Timeline Manager[%s]: Pause.", timeline->name); - PauseTimers(timeline, true); - TM_Propagate(timeline, TM_ACTIONSTATE_PAUSE); -} - -SYMBOL_EXPORT void TM_Resume(struct Timeline* timeline) { - PrintConsole(timeline->game, "Timeline Manager[%s]: Resume.", timeline->name); - TM_Propagate(timeline, TM_ACTIONSTATE_RESUME); - PauseTimers(timeline, false); -} - -SYMBOL_EXPORT void TM_HandleEvent(struct Timeline* timeline, ALLEGRO_EVENT* ev) { - if (ev->type != ALLEGRO_EVENT_TIMER) { return; } - if (timeline->queue) { - if (ev->timer.source == timeline->queue->timer) { - timeline->queue->active = true; - al_destroy_timer(timeline->queue->timer); - timeline->queue->timer = NULL; - if (timeline->queue->function) { - PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); - (*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START); - } else { - PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay reached (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); - } - return; - } - } - struct TM_Action* pom = timeline->background; - while (pom) { - if (ev->timer.source == pom->timer) { - PrintConsole(timeline->game, "Timeline Manager[%s]: background: delay reached, run action (%d - %s)", timeline->name, pom->id, pom->name); - pom->active = true; - al_destroy_timer(pom->timer); - pom->timer = NULL; - (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_START); - return; - } - pom = pom->next; - } -} - SYMBOL_EXPORT struct TM_Action* TM_AddAction(struct Timeline* timeline, TM_ActionCallback* func, struct TM_Arguments* args, char* name) { struct TM_Action* action = malloc(sizeof(struct TM_Action)); if (timeline->queue) { @@ -233,13 +185,13 @@ SYMBOL_EXPORT struct TM_Action* TM_AddAction(struct Timeline* timeline, TM_Actio action->function = func; action->arguments = args; action->name = strdup(name); - action->timer = NULL; action->active = false; - action->delay = 0; + action->started = false; + action->delay = 0.0; action->id = ++timeline->lastid; if (action->function) { PrintConsole(timeline->game, "Timeline Manager[%s]: queue: init action (%d - %s)", timeline->name, action->id, action->name); - (*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); + action->function(timeline->game, action, TM_ACTIONSTATE_INIT); } return action; } @@ -261,21 +213,10 @@ SYMBOL_EXPORT struct TM_Action* TM_AddBackgroundAction(struct Timeline* timeline action->name = strdup(name); action->delay = delay; action->id = ++timeline->lastid; - if (delay) { - PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action with delay %d ms (%d - %s)", timeline->name, delay, action->id, action->name); - (*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); - action->active = false; - action->timer = al_create_timer(delay / 1000.0); - al_register_event_source(timeline->game->_priv.event_queue, al_get_timer_event_source(action->timer)); - al_start_timer(action->timer); - } else { - PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action (%d - %s)", timeline->name, action->id, action->name); - (*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); - action->timer = NULL; - action->active = true; - PrintConsole(timeline->game, "Timeline Manager[%s]: background: run action (%d - %s)", timeline->name, action->id, action->name); - (*action->function)(timeline->game, action, TM_ACTIONSTATE_START); - } + action->active = true; + action->started = false; + PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action with delay %d ms (%d - %s)", timeline->name, delay, action->id, action->name); + (*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); return action; } @@ -294,7 +235,7 @@ static TM_Action(RunInBackground) { free(name); free(delay); if (!(*used)) { - TM_DestroyArgs(arguments); + DestroyArgs(arguments); } free(used); } @@ -312,8 +253,6 @@ SYMBOL_EXPORT void TM_AddDelay(struct Timeline* timeline, int delay) { struct TM_Action* tmp = TM_AddAction(timeline, NULL, NULL, "TM_Delay"); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: adding delay %d ms (%d)", timeline->name, delay, tmp->id); tmp->delay = delay; - tmp->timer = al_create_timer(delay / 1000.0); - al_register_event_source(timeline->game->_priv.event_queue, al_get_timer_event_source(tmp->timer)); } SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) { @@ -323,11 +262,7 @@ SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) { if (*pom->function) { (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); } - if (pom->timer) { - al_stop_timer(pom->timer); - al_destroy_timer(pom->timer); - } - TM_DestroyArgs(pom->arguments); + DestroyArgs(pom->arguments); tmp = pom->next; free(pom->name); free(pom); @@ -343,11 +278,7 @@ SYMBOL_EXPORT void TM_CleanBackgroundQueue(struct Timeline* timeline) { if (*pom->function) { (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); } - if (pom->timer) { - al_stop_timer(pom->timer); - al_destroy_timer(pom->timer); - } - TM_DestroyArgs(pom->arguments); + DestroyArgs(pom->arguments); tmp = pom->next; free(pom->name); free(pom); @@ -357,10 +288,8 @@ SYMBOL_EXPORT void TM_CleanBackgroundQueue(struct Timeline* timeline) { } SYMBOL_EXPORT void TM_SkipDelay(struct Timeline* timeline) { - if (timeline->queue && timeline->queue->timer) { - al_stop_timer(timeline->queue->timer); - al_set_timer_speed(timeline->queue->timer, 0.01); - al_start_timer(timeline->queue->timer); + if (timeline->queue && timeline->queue->delay) { + timeline->queue->delay = 0.0; } } @@ -410,12 +339,3 @@ SYMBOL_EXPORT void* TM_GetArg(struct TM_Arguments* args, int num) { } return args->value; } - -SYMBOL_EXPORT void TM_DestroyArgs(struct TM_Arguments* args) { - struct TM_Arguments* pom; - while (args) { - pom = args->next; - free(args); - args = pom; - } -} diff --git a/src/timeline.h b/src/timeline.h index 7cf0928..bc82cc2 100644 --- a/src/timeline.h +++ b/src/timeline.h @@ -32,10 +32,7 @@ enum TM_ActionState { TM_ACTIONSTATE_INIT, TM_ACTIONSTATE_START, TM_ACTIONSTATE_RUNNING, - TM_ACTIONSTATE_DRAW, - TM_ACTIONSTATE_DESTROY, - TM_ACTIONSTATE_PAUSE, - TM_ACTIONSTATE_RESUME + TM_ACTIONSTATE_DESTROY }; struct TM_Action; @@ -61,27 +58,19 @@ struct TM_Arguments { struct TM_Action { TM_ActionCallback* function; /*!< Function callback of the action. */ struct TM_Arguments* arguments; /*!< Arguments of the action. */ - ALLEGRO_TIMER* timer; /*!< Delay timer. */ - bool active; /*!< If false, then this action is waiting for it's delay to finish. */ - int delay; /*!< Number of miliseconds to delay before action is started. */ + bool active; /*!< Whether this action is being processed by the queue right now. */ + bool started; /*!< If false, then the action is waiting for its delay to finish. */ + double delay; /*!< Number of miliseconds to delay before action is started. */ double delta; /*!< Number of miliseconds since the last TM_Process invocation. */ - struct TM_Action* next; /*!< Pointer to next action in queue. */ unsigned int id; /*!< ID of the action. */ char* name; /*!< "User friendly" name of the action. */ + struct TM_Action* next; /*!< Pointer to next action in queue. */ }; /*! \brief Init timeline. */ struct Timeline* TM_Init(struct Game* game, char* name); /*! \brief Process current timeline actions. */ void TM_Process(struct Timeline*, double delta); -/*! \brief Ask current timeline actions to draw. */ -void TM_Draw(struct Timeline*); -/*! \brief Pauses the timeline. */ -void TM_Pause(struct Timeline*); -/*! \brief Resumes the timeline. */ -void TM_Resume(struct Timeline*); -/*! \brief Handle timer events. */ -void TM_HandleEvent(struct Timeline*, ALLEGRO_EVENT* ev); /*! \brief Add new action to main queue. */ struct TM_Action* TM_AddAction(struct Timeline*, TM_ActionCallback* func, struct TM_Arguments* args, char* name); /*! \brief Add new action to background queue. */ @@ -94,15 +83,13 @@ void TM_AddDelay(struct Timeline*, int delay); void TM_CleanQueue(struct Timeline*); /*! \brief Remove all actions from background queue. */ void TM_CleanBackgroundQueue(struct Timeline*); -/*! \brief Destroy timeline. */ +/*! \brief Destroy given timeline. */ void TM_Destroy(struct Timeline*); -/*! \brief Add data to TM_Arguments queue. */ +/*! \brief Add data to TM_Arguments queue (or create if NULL). */ struct TM_Arguments* TM_AddToArgs(struct TM_Arguments* args, int num, ...); /*! \brief Get nth argument from TM_Arguments queue (counted from 0). */ void* TM_GetArg(struct TM_Arguments* args, int num); -/*! \brief Destroy TM_Arguments queue. */ -void TM_DestroyArgs(struct TM_Arguments* args); -/*! \brief Skip delay if it's currently the first action in the queue */ +/*! \brief Skip delay of the first action in the queue */ void TM_SkipDelay(struct Timeline*); /*! \brief Checks if the main queue is empty */ bool TM_IsEmpty(struct Timeline* timeline);