#include #include #include #include #include #include #include #include #include #include #include #include akerr_ErrorContext *akgl_actor_initialize(akgl_Actor *obj, char *name) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "akgl_actor_initialize received null actor pointer"); FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "akgl_actor_initialize received null name string pointer"); memset(obj, 0x00, sizeof(akgl_Actor)); strncpy((char *)obj->name, name, AKGL_ACTOR_MAX_NAME_LENGTH); obj->curSpriteReversing = false; obj->scale = 1.0; obj->movement_controls_face = true; obj->updatefunc = &akgl_actor_update; obj->renderfunc = &akgl_actor_render; obj->facefunc = &akgl_actor_automatic_face; obj->movementlogicfunc = &akgl_actor_logic_movement; obj->changeframefunc = &akgl_actor_logic_changeframe; obj->addchild = &akgl_actor_add_child; FAIL_ZERO_RETURN( errctx, SDL_SetPointerProperty(AKGL_REGISTRY_ACTOR, name, (void *)obj), AKERR_KEY, "Unable to add actor to registry" ); obj->refcount += 1; SDL_Log("Actor %s initialized and added to the registry", (char *)obj->name); SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_set_character(akgl_Actor *obj, char *basecharname) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference"); FAIL_ZERO_RETURN(errctx, basecharname, AKERR_NULLPOINTER, "Null character reference"); obj->basechar = SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, basecharname, NULL); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Character not found in the registry"); SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_automatic_face(akgl_Actor *obj) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference"); ATTEMPT { if ( obj->movement_controls_face == true ) { // TODO : This doesn't really work properly AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_FACE_ALL); if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) { AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_LEFT); } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) { AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_RIGHT); } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) { AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_UP); } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) { AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_DOWN); } } } CLEANUP { } PROCESS(errctx) { } FINISH(errctx, true); SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtime) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference"); ATTEMPT { // are we currently looping in reverse? if ( curSprite->loop == true && obj->curSpriteReversing == true ) { // are we at the beginning of the loop? if ( obj->curSpriteFrameId == 0 ) { obj->curSpriteReversing = false; obj->curSpriteFrameId += 1; } else { obj->curSpriteFrameId -= 1; } // are we at the end of the animation? } else if ( obj->curSpriteFrameId >= (curSprite->frames-1) ) { // are we set to loop in reverse? if ( curSprite->loop == true && curSprite->loopReverse == true ) { obj->curSpriteReversing = true; obj->curSpriteFrameId -= 1; // are we set to loop forward? } else { // we are at the end of the animation and we either loop forward or do not loop obj->curSpriteFrameId = 0; } // we are not looping in reverse and we are not at the end of the animation } else { obj->curSpriteFrameId += 1; } } CLEANUP { } PROCESS(errctx) { } FINISH(errctx, true); SUCCEED_RETURN(errctx); } // raises AKGL_ERR_LOGICINTERRUPT if we don't want the physics object to process us akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtime) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj->basechar"); if ( obj->vx > obj->basechar->sx ) { obj->vx = obj->basechar->sx; } if ( obj->vy > obj->basechar->sy ) { obj->vy = obj->basechar->sy; } // Effectively a NOOP, this is handled by the physics engine now // These functions are still present in case the library user wants per-actor behavior SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_update(akgl_Actor *obj) { PREPARE_ERROR(errctx); SDL_Time curtime = 0; akgl_Sprite *curSprite = NULL; FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor reference"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference"); SDL_GetCurrentTime(&curtime); PASS(errctx, obj->facefunc(obj)); ATTEMPT { CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite)); if ( ((curtime) - obj->curSpriteFrameTimer) >= curSprite->speed) { CATCH(errctx, obj->changeframefunc(obj, curSprite, curtime)); obj->curSpriteFrameTimer = curtime; } } CLEANUP { } PROCESS(errctx) { } HANDLE(errctx, AKERR_KEY) { // TODO : Why are we passing this error? It could only come from akgl_character_sprite_get // or changeframefunc, both of which should never return AKERR_KEY... SUCCEED_RETURN(errctx); } FINISH(errctx, true); SUCCEED_RETURN(errctx); } static akerr_ErrorContext *actor_visible(akgl_Actor *obj, SDL_FRect *camera, bool *visible) { PREPARE_ERROR(errctx); akgl_Sprite *curSprite = NULL; FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference"); ATTEMPT { CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite)); } CLEANUP { } PROCESS(errctx) { } HANDLE(errctx, AKERR_KEY) { // TODO: Actor has no sprite matching the current state. Should we treat this as an error and throw? *visible = false; SUCCEED_RETURN(errctx); } FINISH(errctx, true); if ( (obj->x < (camera->x - curSprite->width)) || (obj->x > (camera->x + camera->w)) || (obj->y < (camera->y - curSprite->height)) || (obj->y > (camera->y + camera->h)) ) { *visible = false; } else { *visible = obj->visible; } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj) { PREPARE_ERROR(errctx); akgl_Sprite *curSprite = NULL; bool visible = false; SDL_FRect src; SDL_FRect dest; FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference"); ATTEMPT { CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite)); CATCH(errctx, actor_visible(obj, &camera, &visible)); } CLEANUP { } PROCESS(errctx) { } HANDLE(errctx, AKERR_KEY) { } HANDLE_GROUP(errctx, AKERR_OUTOFBOUNDS) { // If an actor doesn't have a sprite for a state, just log it and move on LOG_ERROR(errctx); } FINISH(errctx, true); if ( ! visible ) { SUCCEED_RETURN(errctx); } if ( (obj->curSpriteFrameId > curSprite->frames) ) { // This isn't necessarily an error - this actor's frame index is outside the range of // their current sprite. There are a number of reasons this could happen, and it will // get cleaned up on the next logic update. Just pass on rendering them this frame. SUCCEED_RETURN(errctx); } ATTEMPT { CATCH(errctx, akgl_sprite_sheet_coords_for_frame( curSprite, &src, obj->curSpriteFrameId) ); } CLEANUP { } PROCESS(errctx) { } FINISH(errctx, true); if ( obj->parent != NULL ) { dest.x = (obj->parent->x + obj->x - camera.x); dest.y = (obj->parent->y + obj->y - camera.y); } else { dest.x = (obj->x - camera.x); dest.y = (obj->y - camera.y); } dest.w = curSprite->width * obj->scale; dest.h = curSprite->width * obj->scale; PASS(errctx, renderer.draw_texture(&renderer, curSprite->sheet->texture, &src, &dest, 0, NULL, SDL_FLIP_NONE)); SUCCEED_RETURN(errctx); } akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child) { int i = 0; PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL parent pointer"); FAIL_ZERO_RETURN(errctx, child, AKERR_NULLPOINTER, "NULL child pointer"); FAIL_NONZERO_RETURN(errctx, child->parent, AKERR_RELATIONSHIP, "Child object already has a parent"); for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN ; i++ ) { if ( obj->children[i] == NULL ) { obj->children[i] = child; child->parent = obj; child->refcount += 1; SUCCEED_RETURN(errctx); } } FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Parent object has no remaining child slots left"); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor left", event->type, event->gbutton.which, event->key.key); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_FACE_LEFT)); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor left", event->type, event->gbutton.which, event->key.key); obj->vx = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor right", event->type, event->gbutton.which, event->key.key); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_FACE_RIGHT)); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor right", event->type, event->gbutton.which, event->key.key); obj->vx = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor up", event->type, event->gbutton.which, event->key.key); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_FACE_UP | AKGL_ACTOR_STATE_MOVING_UP)); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor up", event->type, event->gbutton.which, event->key.key); obj->vy = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_UP); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor down", event->type, event->gbutton.which, event->key.key); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN)); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); } akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor down", event->type, event->gbutton.which, event->key.key); obj->vy = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); }