Actionstack functions
An actionstack is like a stack, which contains predefined actions referenced by a positive name-ID.
The top-most action defines, which action is currently active,
where lower actions have to wait to become the top-most one.
This can be used e.g. in object-instances,
when in a game-loop only object-instances belonging to a certain action shall run.
All object-instances fetch the top-most action (with VG3_actionstack_get()) and decide whether to run or not.
Each action can have a hashmap of parameters assigned to specify the action in detail.
- Structures and Enumerations
- Creating and destroying an actionstack
- VG3_actionstack_new()
Create a new actionstack. - VG3_actionstack_free()
Destroy an actionstack. - Using an actionstack
- VG3_actionstack_push()
Push an action into an actionstack. - VG3_actionstack_get()
Fetch the top-most actionstack-element from an actionstack without removing it. - VG3_actionstack_pop()
Remove the top-most actionstack-element from an actionstack. - VG3_actionstack_parent()
Fetch the parent-actionstack-element of the top-most actionstack-element from an actionstack. - VG3_actionstack_dump()
Dump actual actionstack to stderr.
Example
/* A very useless pseudo-game just demonstrating the use of an actionstack * There are three objects: * - a (text-)dice, which must be stopped by a keystroke * - a select-object to select one of the two sunnyboys * or to select the direction of the active sunnyboy, * one instance for each action, distinguished by the sub-ID of the instance * - a sunnyboy-object which moves the selected dice-steps and direction, * two instances, distinguished by a number * * The actionstack always contains the action ASTACK_DICE, * if it is the top-most element, it activates the simulated falling (text-)dice. * After the keystroke the dice-steps are set, * now an action ASTACK_SELECT with sub-ID ASTACK_SELECT_SUNNYBOY is pushed * to select the sunnyboy to move. * Then the action ASTACK_SELECT is popped * and an action ASTACK_SUNNYBOY with the selected number is pushed. * The sunnyboy moves, and if he reaches the middle of the window, * an action ASTACK_SELECT with sub-ID ASTACK_SELECT_DIRECTION is pushed * to select the further direction of the sunnyboy (up or down), * this action has as parent the lower action ASTACK_SUNNYBOY to set its direction. */main.h
#ifndef MAIN_H_ #define MAIN_H_ #include <vgagames3.h> /* object-ID enumeration */ typedef enum { OID_NAME_DICE, OID_NAME_SELECT, OID_NAME_SUNNYBOY } OID_NAMES; /* actions of actionstack */ enum { ASTACK_DICE = 1, /* throw (text-)dice * parameters: no */ ASTACK_SELECT, /* select by key-stroke * according to sub-ID of object-instance: * - sub-ID = ASTACK_SELECT_SUNNYBOY: select sunnyboy * - sub-ID = ASTACK_SELECT_DIRECTION: select moving direction * parameters: * - steps: (for ASTACK_SELECT_SUNNYBOY:) number of moving-steps * - select: sub-ID */ ASTACK_SUNNYBOY /* move sunnyboy * parameters: * - steps: number of moving-steps * - number: selected sunnyboy * - direction: moving-direction */ }; /* sub-IDs for the object OID_NAME_SELECT */ enum { ASTACK_SELECT_SUNNYBOY = 1, ASTACK_SELECT_DIRECTION }; /* main game struct */ struct g_main { struct vg3_window *wstruct; /* window struct */ int winw, winh; /* window size: width and height */ int exit; /* exit, if set to 1 */ struct vg3_ofunc *ofstruct; /* object function main struct */ struct vg3_actionstack *astck; /* stack of actions */ }; /* to an object-ID-string from an object-ID-enumeration */ extern const char * get_oid_name(int); #endif /* MAIN_H_ */main.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <vgagames3.h> #include "main.h" extern struct vg3_ofunc * ofunc_new(void); const char * get_oid_name(int); /* get_oid_name: * returns object-ID as a string * @param oidn object-ID (OID_NAMES) * @return object-ID as string (or empty) */ const char * get_oid_name(int oidn) { switch(oidn) { case OID_NAME_DICE: return "dice"; case OID_NAME_SELECT: return "select"; case OID_NAME_SUNNYBOY: return "sunnyboy"; } return ""; } int main(int argc, char **argv) { struct g_main gmain; const struct vg3_ofunc_objfunc *ofc; memset(&gmain, 0, sizeof(gmain)); /* open window */ gmain.wstruct = VG3_window_new(argv[0], VGAG3_VGAVERSION_LOW, VGAG3_WINSCALE_BESTSCALE); if (gmain.wstruct == NULL) { fprintf(stderr, "%s\n", VG3_error()); exit(1); } /* get the size of the window */ VG3_window_getsize(gmain.wstruct, &gmain.winw, &gmain.winh); /* create main-struct for object-functions */ gmain.ofstruct = ofunc_new(); /* create object-instances */ /* dice */ ofc = VG3_ofunc_get_objfunc(gmain.ofstruct, get_oid_name(OID_NAME_DICE)); if (ofc == NULL) { fprintf(stderr, "Object \"%s\" not found\n", get_oid_name(OID_NAME_DICE)); goto endgame; } if (ofc->f_new(&gmain, 0) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* two select-instances with both sub-IDs */ ofc = VG3_ofunc_get_objfunc(gmain.ofstruct, get_oid_name(OID_NAME_SELECT)); if (ofc == NULL) { fprintf(stderr, "Object \"%s\" not found\n", get_oid_name(OID_NAME_SELECT)); goto endgame; } if (ofc->f_new(&gmain, 0, ASTACK_SELECT_SUNNYBOY) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } if (ofc->f_new(&gmain, 0, ASTACK_SELECT_DIRECTION) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* two sunnyboy-instances */ ofc = VG3_ofunc_get_objfunc(gmain.ofstruct, get_oid_name(OID_NAME_SUNNYBOY)); if (ofc == NULL) { fprintf(stderr, "Object \"%s\" not found\n", get_oid_name(OID_NAME_SUNNYBOY)); goto endgame; } if (ofc->f_new(&gmain, 0, 1) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } if (ofc->f_new(&gmain, 0, 2) == NULL) { fprintf(stderr, "%s\n", VG3_error()); goto endgame; } /* create actionstack and insert action ASTACK_DICE */ gmain.astck = VG3_actionstack_new(); VG3_actionstack_push(gmain.astck, ASTACK_DICE, NULL, 0); /* game-loop */ VG3_discard_input(gmain.wstruct); for (;;) { if (gmain.exit) { break; } if (VG3_inputevent_update(gmain.wstruct)) { break; } /* Q: exit */ if (VG3_key_ispressed(gmain.wstruct, VGAG3_KEY_Q, VGAG3_IS_NEW_PRESSED)) { break; } VG3_draw_clear(gmain.wstruct, NULL, VGAG3_COLOR_BLACK); /* call object-functions */ VG3_ofunc_objlist_call_run(gmain.ofstruct, &gmain); VG3_ofunc_objlist_call_draw(gmain.ofstruct, &gmain); VG3_window_update(gmain.wstruct, 0, 0); VG3_wait_time(80); } VG3_discard_input(gmain.wstruct); /* clean up */ if (gmain.ofstruct != NULL) { VG3_ofunc_objlist_call_free(gmain.ofstruct, &gmain, NULL); VG3_ofunc_free(gmain.ofstruct); } VG3_actionstack_free(gmain.astck); endgame: /* close window and exit */ VG3_window_free(gmain.wstruct); exit(0); }obj-dice.h
#ifndef OBJ_DICE_H_ #define OBJ_DICE_H_ struct g_obj_dice { int steps; /* actual number of steps */ int keysdiscarded; /* whether keys have been discarded */ }; #endif /* OBJ_DICE_H_ */obj-dice.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <vgagames3.h> #include "main.h" #include "obj-dice.h" void getofc_dice(struct vg3_ofunc_objfunc *); static struct vg3_ofunc_object * f_new(void *, unsigned int, ...); static void f_free(void *, struct vg3_ofunc_object *); static void f_run(void *, struct vg3_ofunc_object *); static void f_draw(void *, struct vg3_ofunc_object *); /* +++ get-function +++ */ void getofc_dice(struct vg3_ofunc_objfunc *ofc) { if (ofc == NULL) { return; } snprintf(ofc->oid, sizeof(ofc->oid), "%s", get_oid_name(OID_NAME_DICE)); ofc->f_new = f_new; ofc->f_free = f_free; ofc->f_run = f_run; ofc->f_draw = f_draw; } /* +++ object-instance-functions +++ */ /* new-function * variable parameter: none */ static struct vg3_ofunc_object * f_new(void *vmain, unsigned int iparent, ...) { struct g_main *gmain = vmain; struct vg3_ofunc_object *objp; struct g_obj_dice *gobj; va_list ap; if (gmain == NULL) { VG3_seterror(EINVAL, strerror(EINVAL)); return NULL; } /* get arguments */ va_start(ap, iparent); va_end(ap); /* create private struct for object */ gobj = calloc(1, sizeof(*gobj)); if (gobj == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } gobj->steps = 0; gobj->keysdiscarded = 0; /* create and fill dice-instance */ objp = calloc(1, sizeof(*objp)); if (objp == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } snprintf(objp->oid, sizeof(objp->oid), "%s", get_oid_name(OID_NAME_DICE)); objp->drawlevel = 2; objp->instanceid = 0; /* will be set in VG3_ofunc_objlist_insert() */ objp->ostruct = gobj; /* insert dice-instance into list of object-instances */ VG3_ofunc_objlist_insert(gmain->ofstruct, objp); return objp; } /* free-function */ static void f_free(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_dice *gobj; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_dice *)objp->ostruct; /* remove dice-instance from list of object-instances */ VG3_ofunc_objlist_remove(gmain->ofstruct, objp); /* free dice-instance */ free(gobj); free(objp); } /* run-function */ static void f_run(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_dice *gobj; struct vg3_actionstack_elem actionp; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_dice *)objp->ostruct; actionp = VG3_actionstack_get(gmain->astck); /* relevant? */ if (actionp.stack_id == 0) { return; } if (actionp.name_id != ASTACK_DICE) { return; } if (!gobj->keysdiscarded) { VG3_discard_input(gmain->wstruct); gobj->keysdiscarded = 1; } /* increment dice-number */ gobj->steps++; if (gobj->steps > 3) { gobj->steps = 1; } if (VG3_key_ispressed(gmain->wstruct, VGAG3_KEY_SPACE, VGAG3_IS_NEW_PRESSED)) { /* push action ASTACK_SELECT for sub-ID ASTACK_SELECT_SUNNYBOY onto actionstack */ struct vg3_hash *hparm = VG3_hash_new(); VG3_hash_setint(hparm, "steps", sizeof("steps"), gobj->steps); VG3_hash_setint(hparm, "select", sizeof("select"), ASTACK_SELECT_SUNNYBOY); VG3_actionstack_push(gmain->astck, ASTACK_SELECT, hparm, 0); VG3_hash_free(hparm); /* reset */ gobj->steps = 0; gobj->keysdiscarded = 0; } } /* draw-function */ static void f_draw(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_dice *gobj; struct vg3_actionstack_elem actionp; char msg[256]; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_dice *)objp->ostruct; actionp = VG3_actionstack_get(gmain->astck); /* relevant? */ if (actionp.stack_id == 0) { return; } if (actionp.name_id != ASTACK_DICE) { return; } /* draw dice-instance */ snprintf(msg, sizeof(msg), "Calculating steps.\nStop it with space-key\n%d", gobj->steps); VG3_text_simpledraw(gmain->wstruct, NULL, NULL, gmain->winw / 2, gmain->winh / 2, msg, VGAG3_COLOR_WHITE, VGAG3_COLOR_TRANSPARENT, 1); }obj-select.h
#ifndef OBJ_SELECT_H_ #define OBJ_SELECT_H_ struct g_obj_select { int keysdiscarded; /* whether to keys have been discarded */ }; #endif /* OBJ_SELECT_H_ */obj-select.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <vgagames3.h> #include "main.h" #include "obj-select.h" void getofc_select(struct vg3_ofunc_objfunc *); static struct vg3_ofunc_object * f_new(void *, unsigned int, ...); static void f_free(void *, struct vg3_ofunc_object *); static void f_run(void *, struct vg3_ofunc_object *); static void f_draw(void *, struct vg3_ofunc_object *); /* +++ get-function +++ */ void getofc_select(struct vg3_ofunc_objfunc *ofc) { if (ofc == NULL) { return; } snprintf(ofc->oid, sizeof(ofc->oid), "%s", get_oid_name(OID_NAME_SELECT)); ofc->f_new = f_new; ofc->f_free = f_free; ofc->f_run = f_run; ofc->f_draw = f_draw; } /* +++ object-instance-functions +++ */ /* new-function * variable parameter: * - int: sub-ID */ static struct vg3_ofunc_object * f_new(void *vmain, unsigned int iparent, ...) { struct g_main *gmain = vmain; struct vg3_ofunc_object *objp; struct g_obj_select *gobj; va_list ap; int subid; if (gmain == NULL) { VG3_seterror(EINVAL, strerror(EINVAL)); return NULL; } /* get arguments */ va_start(ap, iparent); subid = va_arg(ap, int); va_end(ap); if (subid < 0) { subid = 0; } /* create private struct for object */ gobj = calloc(1, sizeof(*gobj)); if (gobj == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } gobj->keysdiscarded = 0; /* create and fill select-instance */ objp = calloc(1, sizeof(*objp)); if (objp == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } snprintf(objp->oid, sizeof(objp->oid), "%s", get_oid_name(OID_NAME_SELECT)); objp->subid = (unsigned short)subid; objp->drawlevel = 2; objp->instanceid = 0; /* will be set in VG3_ofunc_objlist_insert() */ objp->ostruct = gobj; /* insert select-instance into list of object-instances */ VG3_ofunc_objlist_insert(gmain->ofstruct, objp); return objp; } /* free-function */ static void f_free(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_select *gobj; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_select *)objp->ostruct; /* remove select-instance from list of object-instances */ VG3_ofunc_objlist_remove(gmain->ofstruct, objp); /* free select-instance */ free(gobj); free(objp); } /* run-function */ static void f_run(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_select *gobj; struct vg3_actionstack_elem actionp; int keynumber; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_select *)objp->ostruct; actionp = VG3_actionstack_get(gmain->astck); /* relevant? */ if (actionp.stack_id == 0) { return; } if (actionp.name_id != ASTACK_SELECT) { return; } if (VG3_hash_getint(actionp.hparm, "select", sizeof("select")) != objp->subid) { return; } if (!gobj->keysdiscarded) { VG3_discard_input(gmain->wstruct); gobj->keysdiscarded = 1; } /* check for pressed keys */ keynumber = 0; if (VG3_key_ispressed(gmain->wstruct, VGAG3_KEY_1, VGAG3_IS_NEW_PRESSED)) { keynumber = 1; } if (VG3_key_ispressed(gmain->wstruct, VGAG3_KEY_2, VGAG3_IS_NEW_PRESSED)) { keynumber = 2; } /* act according to pressed key and sub-ID */ if (keynumber > 0) { if (objp->subid == ASTACK_SELECT_SUNNYBOY) { /* select sunnyboy */ /* pop itself, then push action ASTACK_SUNNYBOY onto actionstack */ struct vg3_hash *hparm = VG3_hash_new(); int steps = VG3_hash_getint(actionp.hparm, "steps", sizeof("steps")); VG3_hash_setint(hparm, "steps", sizeof("steps"), steps); VG3_hash_setint(hparm, "number", sizeof("number"), keynumber); VG3_actionstack_pop(gmain->astck); VG3_actionstack_push(gmain->astck, ASTACK_SUNNYBOY, hparm, 0); VG3_hash_free(hparm); /* reset */ gobj->keysdiscarded = 0; } else if (objp->subid == ASTACK_SELECT_DIRECTION) { /* select moving-direction */ /* set parameter "direction" of its parent-element, then pop itself */ struct vg3_actionstack_elem actionpp = VG3_actionstack_parent(gmain->astck); if (actionpp.stack_id > 0 && actionpp.name_id == ASTACK_SUNNYBOY) { /* correct parent */ VG3_hash_setint(actionpp.hparm, "direction", sizeof("direction"), keynumber); } VG3_actionstack_pop(gmain->astck); /* reset */ gobj->keysdiscarded = 0; } } } /* draw-function */ static void f_draw(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_select *gobj; struct vg3_actionstack_elem actionp; char msg[256]; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_select *)objp->ostruct; (void)gobj; actionp = VG3_actionstack_get(gmain->astck); /* relevant? */ if (actionp.stack_id == 0) { return; } if (actionp.name_id != ASTACK_SELECT) { return; } if (VG3_hash_getint(actionp.hparm, "select", sizeof("select")) != objp->subid) { return; } /* draw select-instance */ if (objp->subid == ASTACK_SELECT_SUNNYBOY) { /* select sunnyboy */ snprintf(msg, sizeof(msg), "Select sunnyboy.\n[1] or [2]"); } else if (objp->subid == ASTACK_SELECT_DIRECTION) { /* select moving-direction */ snprintf(msg, sizeof(msg), "Select direction.\n[1] or [2]"); } VG3_text_simpledraw(gmain->wstruct, NULL, NULL, gmain->winw / 2, gmain->winh / 2, msg, VGAG3_COLOR_WHITE, VGAG3_COLOR_TRANSPARENT, 1); }obj-sunnyboy.h
#ifndef OBJ_SUNNYBOY_H_ #define OBJ_SUNNYBOY_H_ struct g_obj_sunnyboy { struct vg3_rect rect; /* position-rectangle */ int number; /* sunnyboy-number: 1 or 2 */ int direction; /* actual moving-direction: 0, 1, or 2 */ struct vg3_image *img; /* sunnyboy-image */ }; #endif /* OBJ_SUNNYBOY_H_ */obj-sunnyboy.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <vgagames3.h> #include "main.h" #include "obj-sunnyboy.h" void getofc_sunnyboy(struct vg3_ofunc_objfunc *); static struct vg3_ofunc_object * f_new(void *, unsigned int, ...); static void f_free(void *, struct vg3_ofunc_object *); static void f_run(void *, struct vg3_ofunc_object *); static void f_draw(void *, struct vg3_ofunc_object *); /* +++ get-function +++ */ void getofc_sunnyboy(struct vg3_ofunc_objfunc *ofc) { if (ofc == NULL) { return; } snprintf(ofc->oid, sizeof(ofc->oid), "%s", get_oid_name(OID_NAME_SUNNYBOY)); ofc->f_new = f_new; ofc->f_free = f_free; ofc->f_run = f_run; ofc->f_draw = f_draw; } /* +++ object-instance-functions +++ */ /* new-function * variable parameter: * - int: number of sunnyboy */ static struct vg3_ofunc_object * f_new(void *vmain, unsigned int iparent, ...) { struct g_main *gmain = vmain; struct vg3_ofunc_object *objp; struct g_obj_sunnyboy *gobj; va_list ap; int number; char buf[128]; if (gmain == NULL) { VG3_seterror(EINVAL, strerror(EINVAL)); return NULL; } /* get arguments */ va_start(ap, iparent); number = va_arg(ap, int); va_end(ap); if (number != 1 && number != 2) { VG3_seterror(EINVAL, strerror(EINVAL)); return NULL; } /* create private struct for object */ gobj = calloc(1, sizeof(*gobj)); if (gobj == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } gobj->number = number; gobj->direction = 0; /* load image */ snprintf(buf, sizeof(buf), "sunnyboy%d.bmp", gobj->number); gobj->img = VG3_image_load(gmain->wstruct, buf, 1); if (gobj->img == NULL) { return NULL; } /* set position-rectangle */ VG3_image_getsize(gmain->wstruct, gobj->img, NULL, &gobj->rect.w, &gobj->rect.h); gobj->rect.x = gobj->rect.w / 2; if (gobj->number == 1) { gobj->rect.y = gmain->winh / 2 - gobj->rect.h / 2 - gobj->rect.h * 2; } else { gobj->rect.y = gmain->winh / 2 - gobj->rect.h / 2 + gobj->rect.h * 2; } /* create and fill sunnyboy-instance */ objp = calloc(1, sizeof(*objp)); if (objp == NULL) { VG3_seterror(ENOMEM, strerror(errno)); return NULL; } snprintf(objp->oid, sizeof(objp->oid), "%s", get_oid_name(OID_NAME_SUNNYBOY)); objp->drawlevel = 1; objp->instanceid = 0; /* will be set in VG3_ofunc_objlist_insert() */ objp->ostruct = gobj; /* insert sunnyboy-instance into list of object-instances */ VG3_ofunc_objlist_insert(gmain->ofstruct, objp); return objp; } /* free-function */ static void f_free(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_sunnyboy *gobj; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_sunnyboy *)objp->ostruct; /* remove sunnyboy-instance from list of object-instances */ VG3_ofunc_objlist_remove(gmain->ofstruct, objp); /* free sunnyboy-instance */ if (gobj->img != NULL) { VG3_image_unload(gmain->wstruct, gobj->img); } free(gobj); free(objp); } /* run-function */ static void f_run(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_sunnyboy *gobj; struct vg3_actionstack_elem actionp; int direction, steps; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_sunnyboy *)objp->ostruct; actionp = VG3_actionstack_get(gmain->astck); /* relevant? */ if (actionp.stack_id == 0) { return; } if (actionp.name_id != ASTACK_SUNNYBOY) { return; } /* check for the correct sunnyboy-instance */ if (VG3_hash_getint(actionp.hparm, "number", sizeof("number")) != gobj->number) { return; } /* update gobj->direction to "direction" in actionp.hparm if set */ direction = VG3_hash_getint(actionp.hparm, "direction", sizeof("direction")); if (direction > 0) { gobj->direction = direction; } /* move sunnyboy */ steps = VG3_hash_getint(actionp.hparm, "steps", sizeof("steps")); if (gobj->direction == 0) { /* move to the right */ if (gobj->rect.x > gmain->winw / 2) { /* middle of window reached */ /* push action ASTACK_SELECT for sub-ID ASTACK_SELECT_DIRECTION onto actionstack, * as this is an interruption of moving, to set the parameter "direction", * push it with the actual actionstack-element as parent */ struct vg3_hash *hparm = VG3_hash_new(); VG3_hash_setint(hparm, "select", sizeof("select"), ASTACK_SELECT_DIRECTION); VG3_actionstack_push(gmain->astck, ASTACK_SELECT, hparm, actionp.stack_id); VG3_hash_free(hparm); /* nothing else to be done, as waiting for ASTACK_SELECT to fill parameter "direction" */ return; } /* move */ gobj->rect.x += gobj->rect.w; } else if (gobj->direction == 1) { /* move upwards */ gobj->rect.y -= gobj->rect.h; } else if (gobj->direction == 2) { /* move downwards */ gobj->rect.y += gobj->rect.h; } /* exit, if moved out of the window */ if (gobj->rect.y < -gobj->rect.h || gobj->rect.y > gmain->winh + gobj->rect.h) { VG3_actionstack_pop(gmain->astck); /* remove ASTACK_SUNNYBOY */ gmain->exit = 1; return; } /* update parameter "steps" */ steps--; VG3_hash_setint(actionp.hparm, "steps", sizeof("steps"), steps); if (steps == 0) { /* moving done */ VG3_actionstack_pop(gmain->astck); /* remove ASTACK_SUNNYBOY */ } } /* draw-function */ static void f_draw(void *vmain, struct vg3_ofunc_object *objp) { struct g_main *gmain = vmain; struct g_obj_sunnyboy *gobj; if (gmain == NULL || objp == NULL) { return; } gobj = (struct g_obj_sunnyboy *)objp->ostruct; /* always relevant */ /* draw sunnyboy-instance */ VG3_image_copy(gmain->wstruct, NULL, gobj->img, gobj->rect.x + gobj->rect.w / 2, gobj->rect.y + gobj->rect.h / 2, NULL, 0); }