diff --git a/include/akgl/SDL_GameControllerDB.h b/include/akgl/SDL_GameControllerDB.h index 97e2d21..52136c5 100644 --- a/include/akgl/SDL_GameControllerDB.h +++ b/include/akgl/SDL_GameControllerDB.h @@ -1,7 +1,7 @@ #ifndef _SDL_GAMECONTROLLERDB_H_ #define _SDL_GAMECONTROLLERDB_H_ -// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Wed May 13 08:00:23 AM EDT 2026 +// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Wed May 13 09:55:53 AM EDT 2026 #define AKGL_SDL_GAMECONTROLLER_DB_LEN 2228 diff --git a/include/akgl/actor.h b/include/akgl/actor.h index ea8c551..7778381 100644 --- a/include/akgl/actor.h +++ b/include/akgl/actor.h @@ -59,28 +59,29 @@ extern char *AKGL_ACTOR_STATE_STRING_NAMES[AKGL_ACTOR_MAX_STATES+1]; #define AKGL_MAX_HEAP_ACTOR 64 typedef struct akgl_Actor { - uint8_t refcount; - char name[AKGL_ACTOR_MAX_NAME_LENGTH]; - akgl_Character *basechar; - uint8_t curSpriteFrameId; - SDL_Time curSpriteFrameTimer; - bool curSpriteReversing; - uint32_t layer; - int32_t state; - bool movement_controls_face; - void *actorData; - bool visible; - SDL_Time logictimer; - float32_t x; - float32_t y; - struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN]; - struct akgl_Actor *parent; - akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj); - akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj, SDL_Renderer *renderer); - akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj); - akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, SDL_Time curtimems); - akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); - akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child); + uint8_t refcount; + char name[AKGL_ACTOR_MAX_NAME_LENGTH]; + akgl_Character *basechar; + uint8_t curSpriteFrameId; + SDL_Time curSpriteFrameTimer; + bool curSpriteReversing; + uint32_t layer; + int32_t state; + bool movement_controls_face; + void *actorData; + bool visible; + SDL_Time logictimer; + float32_t x; + float32_t y; + float32_t scale; + struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN]; + struct akgl_Actor *parent; + akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj); + akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj, SDL_Renderer *renderer); + akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj); + akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, SDL_Time curtimems); + akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); + akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child); } akgl_Actor; akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_initialize(akgl_Actor *obj, char *name); diff --git a/include/akgl/iterator.h b/include/akgl/iterator.h index 2720e39..7df46ce 100644 --- a/include/akgl/iterator.h +++ b/include/akgl/iterator.h @@ -10,7 +10,7 @@ typedef struct { #define AKGL_ITERATOR_OP_RENDER 1 << 1 // 2 #define AKGL_ITERATOR_OP_RELEASE 1 << 2 // 4 #define AKGL_ITERATOR_OP_LAYERMASK 1 << 3 // 8 -#define AKGL_ITERATOR_OP_UNDEFINED_4 1 << 4 // 16 +#define AKGL_ITERATOR_OP_TILEMAPSCALE 1 << 4 // 16 #define AKGL_ITERATOR_OP_UNDEFINED_5 1 << 5 // 32 #define AKGL_ITERATOR_OP_UNDEFINED_6 1 << 6 // 64 #define AKGL_ITERATOR_OP_UNDEFINED_7 1 << 7 // 128 diff --git a/include/akgl/tilemap.h b/include/akgl/tilemap.h index f6feda0..041c2cb 100644 --- a/include/akgl/tilemap.h +++ b/include/akgl/tilemap.h @@ -87,6 +87,12 @@ typedef struct { int numlayers; int orientation; // 0 = orthogonal, 1 = isometric int numtilesets; + int p_foreground_y; + int p_vanishing_y; + int p_foreground_h; + int p_vanishing_h; + float p_scale; + float p_rate; akgl_Tileset tilesets[AKGL_TILEMAP_MAX_TILESETS]; akgl_TilemapLayer layers[AKGL_TILEMAP_MAX_LAYERS]; } akgl_Tilemap; @@ -109,6 +115,6 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *de akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root); akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx); akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root); - +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor); #endif //_TILEMAP_H_ diff --git a/src/actor.c b/src/actor.c index 4ca859f..b0d5965 100644 --- a/src/actor.c +++ b/src/actor.c @@ -254,8 +254,8 @@ akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer) dest.x = (obj->x - camera.x); dest.y = (obj->y - camera.y); } - dest.w = curSprite->width; - dest.h = curSprite->width; + dest.w = curSprite->width * obj->scale; + dest.h = curSprite->width * obj->scale; SDL_RenderTexture(renderer, curSprite->sheet->texture, &src, &dest); SUCCEED_RETURN(errctx); @@ -300,6 +300,11 @@ void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, cons if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_UPDATE) ) { CATCH(errctx, obj->updatefunc(obj)); } + if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_TILEMAPSCALE) ) { + CATCH(errctx, akgl_tilemap_scale_actor(&gamemap, obj)); + } else { + obj->scale = 1.0; + } if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_RENDER) ) { CATCH(errctx, obj->renderfunc(obj, renderer)); } diff --git a/src/tilemap.c b/src/tilemap.c index c4a8b6b..4712ee4 100644 --- a/src/tilemap.c +++ b/src/tilemap.c @@ -272,12 +272,22 @@ akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t * CATCH(errctx, akgl_heap_release_string(tmpstr)); CATCH(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "x", &curobj->x)); CATCH(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "y", &curobj->y)); - CATCH(errctx, akgl_get_json_boolean_value((json_t *)layerdatavalue, "visible", &curobj->visible)); + CATCH(errctx, akgl_get_json_boolean_value((json_t *)layerdatavalue, "visible", &curobj->visible)); CATCH(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr)); if ( strcmp(tmpstr->data, "actor") == 0 ) { CATCH(errctx, akgl_tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid)); + } else if ( strcmp(tmpstr->data, "perspective") == 0 ) { + curobj->visible = false; + if ( strcmp((char *)curobj->name, "p_foreground") == 0 ) { + dest->p_foreground_y = curobj->y; + CATCH(errctx, akgl_get_json_integer_value((json_t *)layerdatavalue, "height", &dest->p_foreground_h)); + } else if ( strcmp((char *)curobj->name, "p_vanishing") == 0 ) { + dest->p_vanishing_y = curobj->y; + CATCH(errctx, akgl_get_json_integer_value((json_t *)layerdatavalue, "height", &dest->p_vanishing_h)); + } } + layerdatavalue = NULL; } } CLEANUP { @@ -436,6 +446,21 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest) CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json)); CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json)); + + if ( dest->p_foreground_y && dest->p_vanishing_y ) { + // How much bigger is the foreground vs the vanishing point? + // if vanishing is height 16, and foreground is height 32, that is a 2x scale difference + dest->p_scale = (dest->p_foreground_h / dest->p_vanishing_h); + SDL_Log("Map perspective scale is (%f/%f) = %f", dest->p_foreground_h, dest->p_vanishing_h, dest->p_scale); + // Sprites are always size 1.0 at the foreground, so how much do we need to + // scale them for every pixel above foreground_y before they reach vanishing_y? + // If vanishing is at 320 and foreground is at 640, that is a 320 line difference + // If our scaling rate is 2x, then our rate is ((1.0 - (1.0 / 2.0)) / 320.0), or + // 0.0015625% scale per pixel. + // At position 640, + dest->p_rate = (1.0 / (dest->p_foreground_y - dest->p_vanishing_y)) / dest->p_scale; + SDL_Log("Map perspective rate is %f", dest->p_rate); + } } CLEANUP { //IGNORE(akgl_heap_release_string(tmpstr)); } PROCESS(errctx) { @@ -613,3 +638,25 @@ akerr_ErrorContext *akgl_tilemap_draw_tileset(SDL_Renderer *renderer, akgl_Tilem } SUCCEED_RETURN(errctx); } + +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor) +{ + PREPARE_ERROR(e); + + FAIL_ZERO_RETURN(e, map, AKERR_NULLPOINTER, "NULL map"); + FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "NULL actor"); + + SDL_Log("Map foreground is at %d, vanishes at %d, actor %p is at %f", + map->p_foreground_y, + map->p_vanishing_y, + actor, + actor->y); + if ( actor->y <= map->p_vanishing_y ) { + actor->scale = 1.0 / map->p_scale; + } else if ( actor->y >= map->p_foreground_y ) { + actor->scale = 1.0; + } else { + actor->scale = 1.0 - (map->p_rate * (map->p_foreground_y - actor->y)); + } + SDL_Log("Actor %p scaled to %f", actor, actor->scale); +}