2012-04-06 17:28:38 +02:00
/*! \file timeline.c
* \ brief Timeline Manager framework code .
*/
/*
* Copyright ( c ) Sebastian Krzyszkowiak < dos @ dosowisko . net >
*
* 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
2017-07-22 18:22:12 +02:00
* the Free Software Foundation ; either version 3 of the License , or
2012-04-06 17:28:38 +02:00
* ( 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
2017-07-22 18:22:12 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2012-04-06 17:28:38 +02:00
*/
2018-11-26 01:25:56 +01:00
2016-09-08 00:32:21 +02:00
# include "internal.h"
2012-04-06 17:28:38 +02:00
2018-06-29 22:59:25 +02:00
static void DestroyArgs ( struct TM_Arguments * args ) {
struct TM_Arguments * pom ;
while ( args ) {
pom = args - > next ;
free ( args ) ;
args = pom ;
}
}
2018-06-30 00:50:55 +02:00
SYMBOL_EXPORT struct Timeline * TM_Init ( struct Game * game , struct GamestateResources * data , char * name ) {
2017-08-07 02:26:36 +02:00
PrintConsole ( game , " Timeline Manager[%s]: init " , name ) ;
2015-03-18 02:21:57 +01:00
struct Timeline * timeline = malloc ( sizeof ( struct Timeline ) ) ;
2017-08-07 02:26:36 +02:00
timeline - > game = game ;
2015-03-18 02:21:57 +01:00
timeline - > lastid = 0 ;
timeline - > queue = NULL ;
timeline - > background = NULL ;
timeline - > name = strdup ( name ) ;
2018-06-30 00:50:55 +02:00
timeline - > data = data ;
2017-08-07 02:26:36 +02:00
AddTimeline ( game , timeline ) ;
2015-03-18 02:21:57 +01:00
return timeline ;
2012-04-06 17:28:38 +02:00
}
2018-05-30 00:30:12 +02:00
SYMBOL_EXPORT void TM_Process ( struct Timeline * timeline , double delta ) {
2018-06-30 01:27:28 +02:00
// NOTICE: if you create background actions from running action,
// newly created ones have no way to know how much time has been
// "eaten" by the one that created it, which may result in incorrect
// result when calling TM_Process with huge delta. This behaviour
// may change in the future.
// TODO: make sure new action STARTS in the same tick as the old one
// DESTROYS, but make it RUNNING only in the next tick (or when there's
// some remaining delta).
2018-06-29 22:59:25 +02:00
/* process first element from queue.
if returns true , delete it and repeat for the next one */
2018-06-30 01:27:28 +02:00
double origDelta = delta ;
while ( delta > 0.0 ) {
2016-09-06 01:48:30 +02:00
if ( timeline - > queue ) {
2018-06-30 01:27:28 +02:00
timeline - > queue - > delta = delta ;
2018-06-29 22:59:25 +02:00
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 ) ;
2018-06-30 00:50:55 +02:00
timeline - > queue - > state = TM_ACTIONSTATE_START ;
( * timeline - > queue - > function ) ( timeline - > game , timeline - > data , timeline - > queue ) ;
2018-06-29 22:59:25 +02:00
} 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 ) {
2016-09-06 01:48:30 +02:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: queue: run action (%d - %s) " , timeline - > name , timeline - > queue - > id , timeline - > queue - > name ) ;
2018-06-30 00:50:55 +02:00
timeline - > queue - > state = TM_ACTIONSTATE_START ;
( * timeline - > queue - > function ) ( timeline - > game , timeline - > data , timeline - > queue ) ;
2018-06-29 22:59:25 +02:00
timeline - > queue - > started = true ;
2016-09-06 01:48:30 +02:00
}
2018-06-30 00:50:55 +02:00
timeline - > queue - > state = TM_ACTIONSTATE_RUNNING ;
if ( ( * timeline - > queue - > function ) ( timeline - > game , timeline - > data , timeline - > queue ) ) {
2016-09-06 01:48:30 +02:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: queue: destroy action (%d - %s) " , timeline - > name , timeline - > queue - > id , timeline - > queue - > name ) ;
2018-06-30 01:27:28 +02:00
delta - = timeline - > queue - > delta ;
2017-09-10 21:35:14 +02:00
struct TM_Action * tmp = timeline - > queue ;
2016-09-06 01:48:30 +02:00
timeline - > queue = timeline - > queue - > next ;
2018-06-30 00:50:55 +02:00
tmp - > state = TM_ACTIONSTATE_DESTROY ;
( * tmp - > function ) ( timeline - > game , timeline - > data , tmp ) ;
2018-06-29 22:59:25 +02:00
DestroyArgs ( tmp - > arguments ) ;
2016-09-06 01:48:30 +02:00
free ( tmp - > name ) ;
free ( tmp ) ;
} else {
2018-06-30 01:27:28 +02:00
delta = 0.0 ;
2016-09-06 01:48:30 +02:00
}
2012-04-07 23:03:34 +02:00
} else {
2016-09-06 01:48:30 +02:00
/* delay handling */
2018-06-29 22:59:25 +02:00
if ( timeline - > queue - > started ) {
2017-09-10 21:35:14 +02:00
struct TM_Action * tmp = timeline - > queue ;
2016-09-06 01:48:30 +02:00
timeline - > queue = timeline - > queue - > next ;
free ( tmp - > name ) ;
free ( tmp ) ;
} else {
2018-06-29 22:59:25 +02:00
if ( ! timeline - > queue - > active ) {
2018-06-30 01:27:28 +02:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: queue: delay started %d ms (%d - %s) " , timeline - > name , ( int ) ( timeline - > queue - > delay * 1000 ) , timeline - > queue - > id , timeline - > queue - > name ) ;
2018-06-29 22:59:25 +02:00
timeline - > queue - > active = true ;
2016-09-06 01:48:30 +02:00
}
2018-06-30 01:27:28 +02:00
delta = 0.0 ;
2012-09-29 02:34:42 +02:00
}
2012-04-07 23:03:34 +02:00
}
2016-09-06 01:48:30 +02:00
} else {
2018-06-30 01:27:28 +02:00
delta = 0.0 ;
2012-04-06 23:32:15 +02:00
}
}
2018-06-30 01:27:28 +02:00
delta = origDelta ;
2018-06-29 22:59:25 +02:00
/* process all elements from background queue */
2015-03-18 02:21:57 +01:00
struct TM_Action * tmp , * tmp2 , * pom = timeline - > background ;
2012-04-06 23:32:15 +02:00
tmp = NULL ;
2017-09-10 21:35:14 +02:00
while ( pom ! = NULL ) {
2015-03-15 05:38:15 +01:00
bool destroy = false ;
2018-06-30 01:27:28 +02:00
pom - > delta = delta ;
2018-06-29 22:59:25 +02:00
if ( pom - > started ) {
if ( pom - > function ) {
2018-06-30 00:50:55 +02:00
pom - > state = TM_ACTIONSTATE_RUNNING ;
if ( ( pom - > function ) ( timeline - > game , timeline - > data , pom ) ) {
2015-03-18 02:21:57 +01:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: background: destroy action (%d - %s) " , timeline - > name , pom - > id , pom - > name ) ;
2018-06-30 00:50:55 +02:00
pom - > state = TM_ACTIONSTATE_DESTROY ;
( pom - > function ) ( timeline - > game , timeline - > data , pom ) ;
2012-04-06 23:32:15 +02:00
if ( tmp ) {
tmp - > next = pom - > next ;
} else {
2015-03-18 02:21:57 +01:00
timeline - > background = pom - > next ;
2012-04-06 23:32:15 +02:00
}
2015-03-15 05:38:15 +01:00
destroy = true ;
2012-04-06 23:32:15 +02:00
}
} else {
2012-04-14 22:26:33 +02:00
/* delay handling */
2012-04-07 23:03:34 +02:00
if ( tmp ) {
tmp - > next = pom - > next ;
} else {
2015-03-18 02:21:57 +01:00
timeline - > background = pom - > next ;
2012-04-07 23:03:34 +02:00
}
2015-03-15 05:38:15 +01:00
destroy = true ;
2012-04-06 23:32:15 +02:00
}
2018-06-29 22:59:25 +02:00
} 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 ) {
2018-06-30 00:50:55 +02:00
pom - > state = TM_ACTIONSTATE_START ;
pom - > function ( timeline - > game , timeline - > data , pom ) ;
2018-06-29 22:59:25 +02:00
}
pom - > started = true ;
}
2012-04-06 23:32:15 +02:00
}
2015-03-15 05:38:15 +01:00
if ( ! destroy ) {
2012-04-06 23:32:15 +02:00
tmp = pom ;
pom = pom - > next ;
} else {
2018-06-29 22:59:25 +02:00
DestroyArgs ( pom - > arguments ) ;
2012-04-09 17:17:16 +02:00
free ( pom - > name ) ;
2012-04-06 23:32:15 +02:00
free ( pom ) ;
tmp2 = tmp ;
2015-03-15 05:38:15 +01:00
if ( ! tmp ) {
2015-03-18 02:21:57 +01:00
if ( timeline - > background ) {
2017-09-10 21:35:14 +02:00
pom = timeline - > background - > next ;
2015-03-15 05:38:15 +01:00
} else {
2017-09-10 21:35:14 +02:00
pom = NULL ;
2015-03-15 05:38:15 +01:00
}
2017-09-10 21:35:14 +02:00
} else {
pom = tmp - > next ;
2015-03-15 05:38:15 +01:00
}
2012-04-06 23:32:15 +02:00
tmp = tmp2 ;
}
}
2012-04-06 17:28:38 +02:00
}
2018-07-05 22:31:18 +02:00
static struct TM_Action * CreateAction ( struct Timeline * timeline , TM_ActionCallback * func , struct TM_Arguments * args , char * name ) {
2017-09-10 21:35:14 +02:00
struct TM_Action * action = malloc ( sizeof ( struct TM_Action ) ) ;
2012-04-06 23:32:15 +02:00
action - > next = NULL ;
action - > function = func ;
action - > arguments = args ;
2013-02-25 01:23:27 +01:00
action - > name = strdup ( name ) ;
2012-04-06 23:32:15 +02:00
action - > active = false ;
2018-06-29 22:59:25 +02:00
action - > started = false ;
action - > delay = 0.0 ;
2015-03-18 02:21:57 +01:00
action - > id = + + timeline - > lastid ;
2018-06-30 00:50:55 +02:00
action - > timeline = timeline ;
2012-09-03 02:25:32 +02:00
if ( action - > function ) {
2015-03-18 02:21:57 +01:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: queue: init action (%d - %s) " , timeline - > name , action - > id , action - > name ) ;
2018-06-30 00:50:55 +02:00
action - > state = TM_ACTIONSTATE_INIT ;
action - > function ( timeline - > game , timeline - > data , action ) ;
2012-04-07 23:03:34 +02:00
}
return action ;
2012-04-06 23:32:15 +02:00
}
2018-07-05 22:31:18 +02:00
SYMBOL_EXPORT struct TM_Action * TM_AddNamedAction ( struct Timeline * timeline , TM_ActionCallback * func , struct TM_Arguments * args , char * name ) {
struct TM_Action * action = CreateAction ( timeline , func , args , name ) ;
if ( timeline - > queue ) {
struct TM_Action * pom = timeline - > queue ;
while ( pom - > next ! = NULL ) {
pom = pom - > next ;
}
pom - > next = action ;
} else {
timeline - > queue = action ;
}
return action ;
}
SYMBOL_EXPORT struct TM_Action * TM_AddNamedActionAfter ( struct Timeline * timeline , TM_ActionCallback * func , struct TM_Arguments * args , struct TM_Action * after , char * name ) {
struct TM_Action * action = CreateAction ( timeline , func , args , name ) ;
action - > next = after - > next ;
after - > next = action ;
return action ;
}
2018-06-30 02:52:06 +02:00
SYMBOL_EXPORT struct TM_Action * TM_AddNamedBackgroundAction ( struct Timeline * timeline , TM_ActionCallback * func , struct TM_Arguments * args , int delay , char * name ) {
2017-09-10 21:35:14 +02:00
struct TM_Action * action = malloc ( sizeof ( struct TM_Action ) ) ;
2015-03-18 02:21:57 +01:00
if ( timeline - > background ) {
2017-09-10 21:35:14 +02:00
struct TM_Action * pom = timeline - > background ;
while ( pom - > next ! = NULL ) {
pom = pom - > next ;
2012-04-06 23:32:15 +02:00
}
pom - > next = action ;
} else {
2015-03-18 02:21:57 +01:00
timeline - > background = action ;
2012-04-06 23:32:15 +02:00
}
action - > next = NULL ;
action - > function = func ;
action - > arguments = args ;
2013-02-25 01:23:27 +01:00
action - > name = strdup ( name ) ;
2018-06-30 01:27:28 +02:00
action - > delay = delay / 1000.0 ;
2015-03-18 02:21:57 +01:00
action - > id = + + timeline - > lastid ;
2018-06-29 22:59:25 +02:00
action - > active = true ;
action - > started = false ;
2018-06-30 00:50:55 +02:00
action - > timeline = timeline ;
2018-06-29 22:59:25 +02:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: background: init action with delay %d ms (%d - %s) " , timeline - > name , delay , action - > id , action - > name ) ;
2018-06-30 00:50:55 +02:00
action - > state = TM_ACTIONSTATE_INIT ;
( * action - > function ) ( timeline - > game , timeline - > data , action ) ;
2012-04-08 23:25:14 +02:00
return action ;
2012-04-06 23:32:15 +02:00
}
2012-05-21 10:25:22 +02:00
/*! \brief Predefined action used by TM_AddQueuedBackgroundAction */
2018-06-30 16:37:43 +02:00
static TM_ACTION ( TM_RunInBackground ) {
2018-06-30 00:50:55 +02:00
int * delay = TM_Arg ( 1 ) ;
char * name = TM_Arg ( 2 ) ;
struct TM_Arguments * arguments = TM_Arg ( 3 ) ;
bool * used = TM_Arg ( 4 ) ;
if ( action - > state = = TM_ACTIONSTATE_START ) {
2018-06-30 02:52:06 +02:00
TM_AddNamedBackgroundAction ( action - > timeline , TM_Arg ( 0 ) , arguments , * delay , name ) ;
2016-08-16 18:01:12 +02:00
* used = true ;
}
2018-06-30 00:50:55 +02:00
if ( action - > state = = TM_ACTIONSTATE_DESTROY ) {
2016-08-16 18:01:12 +02:00
free ( name ) ;
free ( delay ) ;
if ( ! ( * used ) ) {
2018-06-29 22:59:25 +02:00
DestroyArgs ( arguments ) ;
2016-08-16 18:01:12 +02:00
}
free ( used ) ;
}
2012-04-09 12:55:04 +02:00
return true ;
}
2018-06-30 02:52:06 +02:00
SYMBOL_EXPORT struct TM_Action * TM_AddQueuedNamedBackgroundAction ( struct Timeline * timeline , TM_ActionCallback * func , struct TM_Arguments * args , int delay , char * name ) {
2013-06-05 23:59:02 +02:00
TM_WrapArg ( int , del , delay ) ;
2016-08-16 18:01:12 +02:00
TM_WrapArg ( bool , used , false ) ;
2018-06-30 00:50:55 +02:00
struct TM_Arguments * arguments = TM_Args ( func , del , strdup ( name ) , args , used ) ;
2018-06-30 02:52:06 +02:00
return TM_AddAction ( timeline , TM_RunInBackground , arguments ) ;
2012-04-09 12:55:04 +02:00
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void TM_AddDelay ( struct Timeline * timeline , int delay ) {
2018-06-30 02:52:06 +02:00
struct TM_Action * tmp = TM_AddNamedAction ( timeline , NULL , NULL , " TM_Delay " ) ;
2015-03-18 02:21:57 +01:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: queue: adding delay %d ms (%d) " , timeline - > name , delay , tmp - > id ) ;
2018-06-30 01:27:28 +02:00
tmp - > delay = delay / 1000.0 ;
2012-04-06 17:28:38 +02:00
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void TM_CleanQueue ( struct Timeline * timeline ) {
2015-03-18 02:48:56 +01:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: cleaning queue " , timeline - > name ) ;
2016-08-16 18:01:12 +02:00
struct TM_Action * tmp , * pom = timeline - > queue ;
2017-09-10 21:35:14 +02:00
while ( pom ! = NULL ) {
if ( * pom - > function ) {
2018-06-30 00:50:55 +02:00
pom - > state = TM_ACTIONSTATE_DESTROY ;
( * pom - > function ) ( timeline - > game , timeline - > data , pom ) ;
2017-09-10 21:35:14 +02:00
}
2018-06-29 22:59:25 +02:00
DestroyArgs ( pom - > arguments ) ;
2016-08-16 18:01:12 +02:00
tmp = pom - > next ;
free ( pom - > name ) ;
free ( pom ) ;
pom = tmp ;
2017-08-07 01:52:42 +02:00
timeline - > queue = pom ;
2012-04-07 18:32:47 +02:00
}
2015-03-18 02:48:56 +01:00
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void TM_CleanBackgroundQueue ( struct Timeline * timeline ) {
2015-03-18 02:48:56 +01:00
PrintConsole ( timeline - > game , " Timeline Manager[%s]: cleaning background queue " , timeline - > name ) ;
2016-08-16 18:01:12 +02:00
struct TM_Action * tmp , * pom = timeline - > background ;
2017-09-10 21:35:14 +02:00
while ( pom ! = NULL ) {
if ( * pom - > function ) {
2018-06-30 00:50:55 +02:00
pom - > state = TM_ACTIONSTATE_DESTROY ;
( * pom - > function ) ( timeline - > game , timeline - > data , pom ) ;
2017-09-10 21:35:14 +02:00
}
2018-06-29 22:59:25 +02:00
DestroyArgs ( pom - > arguments ) ;
2016-08-16 18:01:12 +02:00
tmp = pom - > next ;
free ( pom - > name ) ;
free ( pom ) ;
pom = tmp ;
2017-08-07 01:52:42 +02:00
timeline - > background = pom ;
2012-04-07 18:32:47 +02:00
}
2015-03-18 02:48:56 +01:00
}
2016-09-04 03:06:26 +02:00
SYMBOL_EXPORT void TM_SkipDelay ( struct Timeline * timeline ) {
2018-06-29 22:59:25 +02:00
if ( timeline - > queue & & timeline - > queue - > delay ) {
timeline - > queue - > delay = 0.0 ;
2016-09-04 03:06:26 +02:00
}
}
SYMBOL_EXPORT bool TM_IsEmpty ( struct Timeline * timeline ) {
return ! timeline - > queue ;
}
SYMBOL_EXPORT bool TM_IsBackgroundEmpty ( struct Timeline * timeline ) {
return ! timeline - > background ;
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT void TM_Destroy ( struct Timeline * timeline ) {
2017-08-07 02:26:36 +02:00
RemoveTimeline ( timeline - > game , timeline ) ;
2015-03-18 02:48:56 +01:00
TM_CleanQueue ( timeline ) ;
TM_CleanBackgroundQueue ( timeline ) ;
PrintConsole ( timeline - > game , " Timeline Manager[%s]: destroy " , timeline - > name ) ;
2015-03-18 02:21:57 +01:00
free ( timeline - > name ) ;
free ( timeline ) ;
2012-04-06 17:28:38 +02:00
}
2016-07-04 01:12:55 +02:00
SYMBOL_EXPORT struct TM_Arguments * TM_AddToArgs ( struct TM_Arguments * args , int num , . . . ) {
2013-06-05 23:59:02 +02:00
va_list ap ;
int i ;
va_start ( ap , num ) ;
2012-04-08 23:25:14 +02:00
struct TM_Arguments * tmp = args ;
2017-09-10 21:35:14 +02:00
for ( i = 0 ; i < num ; i + + ) {
2013-06-05 23:59:02 +02:00
if ( ! tmp ) {
tmp = malloc ( sizeof ( struct TM_Arguments ) ) ;
tmp - > value = va_arg ( ap , void * ) ;
tmp - > next = NULL ;
args = tmp ;
} else {
while ( tmp - > next ) {
tmp = tmp - > next ;
}
tmp - > next = malloc ( sizeof ( struct TM_Arguments ) ) ;
tmp - > next - > value = va_arg ( ap , void * ) ;
tmp - > next - > next = NULL ;
}
2012-04-07 20:32:47 +02:00
}
2013-06-05 23:59:02 +02:00
va_end ( ap ) ;
2012-04-07 20:32:47 +02:00
return args ;
2012-04-06 17:28:38 +02:00
}
2017-09-10 21:35:14 +02:00
SYMBOL_EXPORT void * TM_GetArg ( struct TM_Arguments * args , int num ) {
for ( int i = 0 ; i < num ; i + + ) {
2016-08-16 18:01:12 +02:00
args = args - > next ;
}
return args - > value ;
}