8 Commits

26 changed files with 928 additions and 330 deletions

View File

@@ -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 Fri May 15 11:05:32 AM EDT 2026
// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Tue May 26 01:00:21 PM EDT 2026
#define AKGL_SDL_GAMECONTROLLER_DB_LEN 2228

View File

@@ -18,8 +18,8 @@
#define AKGL_ACTOR_STATE_MOVING_RIGHT 1 << 8 // 256 0000 0001 0000 0000
#define AKGL_ACTOR_STATE_MOVING_UP 1 << 9 // 512 0000 0010 0000 0000
#define AKGL_ACTOR_STATE_MOVING_DOWN 1 << 10 // 1024 0000 0100 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_11 1 << 11 // 2048 0000 1000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_12 1 << 12 // 4096 0001 0000 0000 0000
#define AKGL_ACTOR_STATE_MOVING_IN 1 << 11 // 2048 0000 1000 0000 0000
#define AKGL_ACTOR_STATE_MOVING_OUT 1 << 12 // 4096 0001 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_13 1 << 13 // 8192 0010 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_14 1 << 14 // 16384 0100 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_15 1 << 15 // 32768 1000 0000 0000 0000
@@ -70,25 +70,42 @@ typedef struct akgl_Actor {
bool movement_controls_face;
void *actorData;
bool visible;
SDL_Time logictimer;
SDL_Time movetimer;
// Velocity. Combined effect of all forces acting on the actor resulting
// in energy along an axis.
float32_t vx;
float32_t vy;
float32_t vz;
// Gravity. Forces acting on the actor on a given axis due to the forces
// of gravity and drag.
float32_t gx;
float32_t gy;
float32_t gz;
// Thrust. Energy originating only from the actor's own acceleration on a
// given axis, before the effects of gravity and drag.
float32_t tx;
float32_t ty;
float32_t tz;
// Position.
float32_t x;
float32_t y;
float32_t z;
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 *(*renderfunc)(struct akgl_Actor *obj);
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 *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt);
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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_set_character(akgl_Actor *obj, char *basecharname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_render(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_update(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtimems);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_automatic_face(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child);

View File

@@ -9,14 +9,18 @@
#define AKGL_MAX_HEAP_CHARACTER 256
typedef struct akgl_Character {
uint8_t refcount;
char name[AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH];
SDL_PropertiesID state_sprites;
uint64_t movementspeed;
float32_t vx;
float32_t vy;
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_add)(struct akgl_Character *, akgl_Sprite *, int);
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_get)(struct akgl_Character *, int, akgl_Sprite **);
uint8_t refcount;
char name[AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH];
SDL_PropertiesID state_sprites;
uint64_t speedtime;
float32_t ax;
float32_t ay;
float32_t az;
float32_t sx;
float32_t sy;
float32_t sz;
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_add)(struct akgl_Character *, akgl_Sprite *, int);
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_get)(struct akgl_Character *, int, akgl_Sprite **);
} akgl_Character;

View File

@@ -44,4 +44,6 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_removed(void *appstate
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akgl_Control *control);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void);
#endif // _CONTROLLER_H_

View File

@@ -1,6 +1,18 @@
#ifndef _ERROR_H_
#define _ERROR_H_
// This macro is used to silence warnings on string concatenation operations that may fail.
// e.g., combining two element of PATH_MAX into a string buffer of AKGL_STRING_MAX_LENGTH.
// We have to draw a line in the sand somewhere or we will just let our buffers grow forever
// to keep the compiler happy.
#define DISABLE_GCC_WARNING_FORMAT_TRUNCATION \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wformat-truncation\"")
#define RESTORE_GCC_WARNINGS \
_Pragma("GCC diagnostic pop")
#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1
#define AKGL_ERR_LOGICINTERRUPT AKERR_LAST_ERRNO_VALUE + 2
#endif // _ERROR_H_

View File

@@ -2,9 +2,11 @@
#define _AKGL_GAME_H_
#include <stdint.h>
#include "types.h"
#include <SDL3_mixer/SDL_mixer.h>
#include "types.h"
#include "tilemap.h"
#include "renderer.h"
#include "physics.h"
#define AKGL_VERSION "0.1.0"
@@ -32,6 +34,7 @@ typedef struct {
char name[256];
char uri[256];
akgl_GameState state;
SDL_Mutex *statelock;
int16_t fps;
SDL_Time gameStartTime;
SDL_Time lastIterTime;
@@ -41,15 +44,17 @@ typedef struct {
} akgl_Game;
extern SDL_Window *window;
extern SDL_Renderer *renderer;
extern akgl_Tilemap gamemap;
extern MIX_Audio *bgm;
extern MIX_Mixer *akgl_mixer;
extern MIX_Track *akgl_tracks[AKGL_GAME_AUDIO_MAX_TRACKS];
extern SDL_FRect camera;
extern akgl_Game game;
extern akgl_RenderBackend renderer;
extern akgl_PhysicsBackend physics;
#define AKGL_BITMASK_HAS(x, y) (x & y) == y
#define AKGL_BITMASK_HASNOT(x, y) (x & y) != y
#define AKGL_BITMASK_ADD(x, y) x |= y
#define AKGL_BITMASK_DEL(x, y) x &= ~(y)
#define AKGL_BITMASK_CLEAR(x) x = 0;
@@ -60,5 +65,8 @@ void akgl_game_updateFPS();
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath);
void akgl_game_lowfps(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_unlock(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags);
#endif //_AKGL_GAME_H_

View File

@@ -30,6 +30,7 @@ extern akgl_Character HEAP_CHARACTER[AKGL_MAX_HEAP_CHARACTER];
extern akgl_String HEAP_STRING[AKGL_MAX_HEAP_STRING];
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_actor();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_actor(akgl_Actor **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_sprite(akgl_Sprite **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest);

40
include/akgl/physics.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef _PHYSICS_H_
#define _PHYSICS_H_
#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/actor.h>
#include <akgl/iterator.h>
typedef struct akgl_PhysicsBackend {
akerr_ErrorContext AKERR_NOIGNORE *(*simulate)(struct akgl_PhysicsBackend *self, akgl_Iterator *opflags);
akerr_ErrorContext AKERR_NOIGNORE *(*gravity)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *(*collide)(struct akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
double drag_x;
double drag_y;
double drag_z;
double gravity_x;
double gravity_y;
double gravity_z;
SDL_Time gravity_time;
SDL_Time timer_gravity;
} akgl_PhysicsBackend;
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_sidescroller(akgl_PhysicsBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags);
#endif // _PHYSICS_H_

29
include/akgl/renderer.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _RENDERER_H_
#define _RENDERER_H_
#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/iterator.h>
typedef struct akgl_RenderBackend {
SDL_Renderer *sdl_renderer;
akerr_ErrorContext AKERR_NOIGNORE *(*shutdown)(struct akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *(*frame_start)(struct akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *(*frame_end)(struct akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *(*draw_texture)(struct akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip);
akerr_ErrorContext AKERR_NOIGNORE *(*draw_mesh)(struct akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *(*draw_world)(struct akgl_RenderBackend *self, akgl_Iterator *opflags);
} akgl_RenderBackend;
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_shutdown(akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_start(akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_end(akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_texture(akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_mesh(akgl_RenderBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_world(akgl_RenderBackend *self, akgl_Iterator *opflags);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_init2d(akgl_RenderBackend *self);
#endif // _RENDERER_H_

View File

@@ -3,8 +3,9 @@
#include "string.h"
#include <akerror.h>
#include <limits.h>
#define AKGL_MAX_STRING_LENGTH 256
#define AKGL_MAX_STRING_LENGTH PATH_MAX
typedef struct
{
@@ -13,5 +14,5 @@ typedef struct
} akgl_String;
akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init);
akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dst, int count);
#endif //_STRING_H_

View File

@@ -1,6 +1,7 @@
#ifndef _TILEMAP_H_
#define _TILEMAP_H_
#include <limits.h>
#include "actor.h"
#include "staticstring.h"
#include <jansson.h>
@@ -11,7 +12,7 @@
#define AKGL_TILEMAP_MAX_TILESETS 16
#define AKGL_TILEMAP_MAX_TILES_PER_IMAGE 65536
#define AKGL_TILEMAP_MAX_TILESET_NAME_SIZE 512
#define AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE 512
#define AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE PATH_MAX
#define AKGL_TILEMAP_MAX_OBJECT_NAME_SIZE 512
#define AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER 128
@@ -100,8 +101,8 @@ typedef struct {
} akgl_Tilemap;
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load(char *fname, akgl_Tilemap *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *dest, SDL_FRect *viewport, int layeridx);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw_tileset(SDL_Renderer *renderer, akgl_Tilemap *dest, int tilesetidx);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw(akgl_Tilemap *dest, SDL_FRect *viewport, int layeridx);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw_tileset(akgl_Tilemap *dest, int tilesetidx);
/*
* These functions are part of the internal API and should not be called by the user.
@@ -112,11 +113,11 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_tilemap_property(json_t *obj, c
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_properties_string(json_t *obj, char *key, akgl_String **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_properties_integer(json_t *obj, char *key, int *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int tilesetidx);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid);
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_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_release(akgl_Tilemap *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor);

View File

@@ -2,6 +2,7 @@
#define _UTIL_H_
#include <akerror.h>
#include <akgl/staticstring.h>
typedef struct point {
int x;
@@ -22,6 +23,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_rectangle_points(RectanglePoints *dest,
akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_point_rectangle(point *p, RectanglePoints *r, bool *collide);
akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide);
akerr_ErrorContext AKERR_NOIGNORE *akgl_path_relative(char *root, char *path, akgl_String *dst);
// These are REALLY slow routines that are only useful in testing harnesses
akerr_ErrorContext AKERR_NOIGNORE *akgl_compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout);

View File

@@ -3,6 +3,7 @@
#include <string.h>
#include <akerror.h>
#include <akgl/physics.h>
#include <akgl/game.h>
#include <akgl/sprite.h>
#include <akgl/actor.h>
@@ -112,27 +113,14 @@ akerr_ErrorContext *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *c
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtime)
// raises AKGL_ERR_LOGICINTERRUPT if we don't want the physics object to process us
akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
if ( obj->parent != NULL ) {
// Children don't move independently of their parents, they just have an offset
SUCCEED_RETURN(errctx);
} else {
if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
obj->x -= obj->basechar->vx;
}
if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
obj->x += obj->basechar->vx;
}
if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
obj->y -= obj->basechar->vy;
}
if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
obj->y += obj->basechar->vy;
}
}
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj->basechar");
// 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);
}
@@ -145,21 +133,11 @@ akerr_ErrorContext *akgl_actor_update(akgl_Actor *obj)
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor reference");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT {
SDL_GetCurrentTime(&curtime);
CATCH(errctx, obj->facefunc(obj));
// is it time to apply movement logic?
if ( (curtime - obj->logictimer) >= obj->basechar->movementspeed ) {
CATCH(errctx, obj->movementlogicfunc(obj, curtime));
obj->logictimer = curtime;
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, false);
SDL_GetCurrentTime(&curtime);
PASS(errctx, obj->facefunc(obj));
ATTEMPT {
CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
// is it time to change frames?
if ( ((curtime) - obj->curSpriteFrameTimer) >= curSprite->speed) {
CATCH(errctx, obj->changeframefunc(obj, curSprite, curtime));
obj->curSpriteFrameTimer = curtime;
@@ -167,6 +145,8 @@ akerr_ErrorContext *akgl_actor_update(akgl_Actor *obj)
} 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);
@@ -179,7 +159,6 @@ static akerr_ErrorContext *actor_visible(akgl_Actor *obj, SDL_FRect *camera, boo
akgl_Sprite *curSprite = NULL;
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, AKERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT {
@@ -203,7 +182,7 @@ static akerr_ErrorContext *actor_visible(akgl_Actor *obj, SDL_FRect *camera, boo
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer)
akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj)
{
PREPARE_ERROR(errctx);
akgl_Sprite *curSprite = NULL;
@@ -212,7 +191,6 @@ akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer)
SDL_FRect dest;
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, AKERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT {
@@ -229,7 +207,7 @@ akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer)
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
@@ -258,7 +236,7 @@ akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer)
dest.w = curSprite->width * obj->scale;
dest.h = curSprite->width * obj->scale;
SDL_RenderTexture(renderer, curSprite->sheet->texture, &src, &dest);
PASS(errctx, renderer.draw_texture(&renderer, curSprite->sheet->texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
SUCCEED_RETURN(errctx);
}
@@ -281,39 +259,6 @@ akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child)
FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Parent object has no remaining child slots left");
}
// SDL iterator so we can't return error information here, void only
// this means we don't have anywhere to send exceptions up to, so if we hit an error, we log and exit(1) here
void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name)
{
PREPARE_ERROR(errctx);
akgl_Iterator *opflags = (akgl_Iterator *)userdata;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, name, AKERR_NULLPOINTER, "registry_iterate_actor received NULL property name");
FAIL_ZERO_BREAK(errctx, opflags, AKERR_NULLPOINTER, "received NULL iterator flags");
akgl_Actor *obj = (akgl_Actor *)SDL_GetPointerProperty(registry, name, NULL);
FAIL_ZERO_BREAK(errctx, obj, AKERR_KEY, "registry_iterate_actor received property name that was not in the registry");
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) {
if ( obj->layer != opflags->layerid ) {
break;
}
}
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));
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
@@ -332,6 +277,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL
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->tx = 0;
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);
@@ -355,6 +302,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SD
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->tx = 0;
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);
@@ -378,6 +327,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_E
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->ty = 0;
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);
@@ -401,6 +352,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL
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->ty = 0;
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);

View File

@@ -185,10 +185,12 @@ akerr_ErrorContext *akgl_character_load_json(char *filename)
"Error while loading character from %s on line %d: %s", filename, error.line, error.text
);
CATCH(errctx, akgl_character_load_json_inner(json, obj));
CATCH(errctx, akgl_get_json_integer_value(json, "movementspeed", (int *)&obj->movementspeed));
obj->movementspeed = obj->movementspeed * AKGL_TIME_ONESEC_MS;
CATCH(errctx, akgl_get_json_number_value(json, "velocity_x", &obj->vx));
CATCH(errctx, akgl_get_json_number_value(json, "velocity_y", &obj->vy));
CATCH(errctx, akgl_get_json_integer_value(json, "speedtime", (int *)&obj->speedtime));
obj->speedtime = obj->speedtime * AKGL_TIME_ONESEC_MS;
CATCH(errctx, akgl_get_json_number_value(json, "speed_x", &obj->sx));
CATCH(errctx, akgl_get_json_number_value(json, "speed_y", &obj->sy));
CATCH(errctx, akgl_get_json_number_value(json, "acceleration_x", &obj->ax));
CATCH(errctx, akgl_get_json_number_value(json, "acceleration_y", &obj->ay));
} CLEANUP {
//IGNORE(akgl_heap_release_string(tmpstr));
if ( errctx != NULL ) {

View File

@@ -22,6 +22,33 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void)
{
int count = 0;
int i = 0;
SDL_JoystickID *gamepads = NULL;
SDL_Gamepad *gamepad = NULL;
PREPARE_ERROR(e);
if ( SDL_HasGamepad() ) {
gamepads = SDL_GetGamepads(&count);
if ( count > 0 ) {
FAIL_ZERO_RETURN(e, gamepads, AKERR_NULLPOINTER, "%s", SDL_GetError());
for ( i = 0; i < count ; i++ ) {
gamepad = SDL_OpenGamepad(gamepads[i]);
FAIL_ZERO_RETURN(e, gamepad, AKERR_NULLPOINTER, "%s", SDL_GetError());
SDL_Log("Gamepad %d is %s", i, SDL_GetGamepadNameForID(gamepads[i]));
}
SDL_free(gamepads);
} else {
SDL_Log("No gamepads enumerated");
}
} else {
SDL_Log("No gamepads connected");
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *event)
{
int i = 0;
@@ -32,7 +59,7 @@ akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *even
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
ATTEMPT {
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
curmap = &GAME_ControlMaps[i];
@@ -56,6 +83,13 @@ akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *even
event->key.which == curmap->kbid &&
event->key.key == curcontrol->key)
);
if ( event->type == 768 && event->key.which == 11 && event->key.key == 13 ) {
SDL_Log("Event type=%d, keyboard=%d, key=%d", event->type, event->key.which, event->key.key);
SDL_Log("ControlMap[%d].Controls[%d] keyboard=%d, key=%d", i, j, curmap->kbid, curcontrol->key);
SDL_Log("event %d -> control on %d off %d", event->type, curcontrol->event_on, curcontrol->event_off);
SDL_Log("eventButtonComboMatch for controlmap %d id %d = %d",
i, j, eventButtonComboMatch);
}
if ( event->type == curcontrol->event_on && eventButtonComboMatch) {
CATCH(errctx, curcontrol->handler_on(curmap->target, event));
goto _akgl_controller_handle_event_success;

View File

@@ -20,11 +20,11 @@ void akgl_draw_background(int w, int h)
for (x = 0; x < w; x += dx) {
/* use an 8x8 checkerboard pattern */
i = (((x ^ y) >> 3) & 1);
SDL_SetRenderDrawColor(renderer, col[i].r, col[i].g, col[i].b, col[i].a);
SDL_SetRenderDrawColor(renderer.sdl_renderer, col[i].r, col[i].g, col[i].b, col[i].a);
rect.x = (float)x;
rect.y = (float)y;
SDL_RenderFillRect(renderer, &rect);
SDL_RenderFillRect(renderer.sdl_renderer, &rect);
}
}
}

View File

@@ -15,10 +15,12 @@
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/physics.h>
#include <akgl/SDL_GameControllerDB.h>
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
akgl_RenderBackend renderer;
akgl_PhysicsBackend physics;
akgl_Frame ball;
akgl_Frame paddle1;
akgl_Frame paddle2;
@@ -42,28 +44,32 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
int screenheight = 0;
int i = 0;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
ATTEMPT {
strncpy((char *)&game.libversion, AKGL_VERSION, 32);
game.gameStartTime = SDL_GetTicksNS();
game.lastIterTime = game.gameStartTime;
game.lastFPSTime = game.gameStartTime;
game.lowfpsfunc = &akgl_game_lowfps;
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.name), AKERR_NULLPOINTER, "Must provide game name");
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.version), AKERR_NULLPOINTER, "Must provide game version");
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.uri), AKERR_NULLPOINTER, "Must provide game uri");
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init_actor());
CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, akgl_registry_init_character());
CATCH(errctx, akgl_registry_init_font());
CATCH(errctx, akgl_registry_init_music());
CATCH(errctx, akgl_registry_init_properties());
CATCH(errctx, akgl_registry_init_actor_state_strings());
game.statelock = SDL_CreateMutex();
FAIL_ZERO_RETURN(e, game.statelock, AKGL_ERR_SDL, "%s", SDL_GetError());
CATCH(e, akgl_game_state_lock());
FAIL_ZERO_BREAK(e, strlen((char *)&game.name), AKERR_NULLPOINTER, "Must provide game name");
FAIL_ZERO_BREAK(e, strlen((char *)&game.version), AKERR_NULLPOINTER, "Must provide game version");
FAIL_ZERO_BREAK(e, strlen((char *)&game.uri), AKERR_NULLPOINTER, "Must provide game uri");
CATCH(e, akgl_heap_init());
CATCH(e, akgl_registry_init_actor());
CATCH(e, akgl_registry_init_sprite());
CATCH(e, akgl_registry_init_spritesheet());
CATCH(e, akgl_registry_init_character());
CATCH(e, akgl_registry_init_font());
CATCH(e, akgl_registry_init_music());
CATCH(e, akgl_registry_init_properties());
CATCH(e, akgl_registry_init_actor_state_strings());
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true)
//IGNORE(akgl_game_state_unlock());
} PROCESS(e) {
} FINISH(e, true)
SDL_SetAppMetadata(game.name, game.version, game.uri);
@@ -72,7 +78,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
}
FAIL_ZERO_RETURN(
errctx,
e,
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO),
AKGL_ERR_SDL,
"Couldn't initialize SDL: %s",
@@ -82,36 +88,11 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
for ( i = 0; i < AKGL_SDL_GAMECONTROLLER_DB_LEN ; i++ ) {
if ( SDL_AddGamepadMapping(SDL_GAMECONTROLLER_DB[i]) == -1 ) {
FAIL_ZERO_RETURN(errctx, 0, AKGL_ERR_SDL, "%s", SDL_GetError());
FAIL_ZERO_RETURN(e, 0, AKGL_ERR_SDL, "%s", SDL_GetError());
}
}
SUCCEED_RETURN(errctx);
}
PASS(e, akgl_controller_open_gamepads());
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init_screen()
{
akgl_String *width = NULL;
akgl_String *height = NULL;
int screenwidth;
int screenheight;
PREPARE_ERROR(e);
PASS(e, akgl_get_property("game.screenwidth", &width, "0"));
PASS(e, akgl_get_property("game.screenheight", &height, "0"));
PASS(e, aksl_atoi(width->data, &screenwidth));
PASS(e, aksl_atoi(height->data, &screenheight));
SDL_Log("Initializing screen (%sx%s = %dx%d)", width->data, height->data, screenwidth, screenheight);
PASS(e, akgl_heap_release_string(width));
PASS(e, akgl_heap_release_string(height));
FAIL_ZERO_RETURN(
e,
SDL_CreateWindowAndRenderer(game.uri, screenwidth, screenheight, 0, &window, &renderer),
AKGL_ERR_SDL,
"Couldn't create window/renderer: %s",
SDL_GetError());
FAIL_ZERO_RETURN(
e,
MIX_Init(),
@@ -132,12 +113,30 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init_screen()
AKGL_ERR_SDL,
"Couldn't initialize front engine: %s",
SDL_GetError());
PASS(e, akgl_game_state_unlock());
SUCCEED_RETURN(e);
}
camera.x = 0;
camera.y = 0;
camera.w = screenwidth;
camera.h = screenheight;
SUCCEED(e);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void)
{
PREPARE_ERROR(e);
SDL_Time totaltime = 0;
while ( totaltime < AKGL_TIME_ONESEC_MS ) {
if ( SDL_TryLockMutex(game.statelock) == true ) {
SUCCEED_RETURN(e);
}
totaltime += 100;
SDL_Delay(100);
}
FAIL_RETURN(e, AKGL_ERR_SDL, "%s", SDL_GetError());
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_unlock(void)
{
PREPARE_ERROR(e);
SDL_UnlockMutex(game.statelock);
SUCCEED_RETURN(e);
}
void akgl_game_updateFPS()
@@ -164,119 +163,119 @@ void akgl_game_save_actorname_iterator(void *userdata, SDL_PropertiesID props, c
{
FILE *fp = (FILE *)userdata;
akgl_Actor *actor = NULL;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
CATCH(errctx, aksl_fwrite((char *)name, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
actor = SDL_GetPointerProperty(props, name, NULL);
CATCH(errctx, aksl_fwrite(&actor, 1, sizeof(akgl_Actor *), fp));
CATCH(e, aksl_fwrite(&actor, 1, sizeof(akgl_Actor *), fp));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_spritename_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_Sprite *sprite = NULL;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
sprite = SDL_GetPointerProperty(props, name, NULL);
CATCH(errctx, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite(&sprite, 1, sizeof(akgl_Sprite *), fp));
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&sprite, 1, sizeof(akgl_Sprite *), fp));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_spritesheetname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_SpriteSheet *spritesheet = NULL;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
spritesheet = SDL_GetPointerProperty(props, name, NULL);
CATCH(errctx, aksl_fwrite((char *)name, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite(&spritesheet, 1, sizeof(akgl_SpriteSheet *), fp));
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&spritesheet, 1, sizeof(akgl_SpriteSheet *), fp));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_charactername_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
akgl_Character *character = NULL;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
character = SDL_GetPointerProperty(props, name, NULL);
CATCH(errctx, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite(&character, 1, sizeof(akgl_Character *), fp));
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&character, 1, sizeof(akgl_Character *), fp));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
} PROCESS(e) {
} FINISH_NORETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
{
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
char nullval = 0x00;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
// write the actor name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_ACTOR,
&akgl_game_save_actorname_iterator,
(void *)fp);
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Actor *), fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Actor *), fp));
// write the sprite name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITE,
&akgl_game_save_spritename_iterator,
(void *)fp);
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Sprite *), fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Sprite *), fp));
// write the spritesheet name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITESHEET,
&akgl_game_save_spritesheetname_iterator,
(void *)fp);
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_SpriteSheet *), fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_SpriteSheet *), fp));
// write the character name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_CHARACTER,
&akgl_game_save_charactername_iterator,
(void *)fp);
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(errctx, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Character *), fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Character *), fp));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
{
FILE *fp = NULL;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fpath, AKERR_NULLPOINTER, "NULL file path");
CATCH(errctx, aksl_fopen(fpath, "wb", &fp));
CATCH(errctx, aksl_fwrite(&game, 1, sizeof(akgl_Game), fp));
CATCH(errctx, akgl_game_save_actors(fp));
} PROCESS(errctx) {
FAIL_ZERO_BREAK(e, fpath, AKERR_NULLPOINTER, "NULL file path");
CATCH(e, aksl_fopen(fpath, "wb", &fp));
CATCH(e, aksl_fwrite(&game, 1, sizeof(akgl_Game), fp));
CATCH(e, akgl_game_save_actors(fp));
} PROCESS(e) {
} CLEANUP {
if ( fp != NULL )
fclose(fp);
} FINISH(errctx, true);
SUCCEED_RETURN(errctx); // SUCCEED_NORETURN if in main().
} FINISH(e, true);
SUCCEED_RETURN(e); // SUCCEED_NORETURN if in main().
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_PropertiesID map, int namelength, int ptrlength, SDL_PropertiesID registry)
@@ -286,10 +285,10 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_Pr
char objname[namelength];
int retval = 0;
PREPARE_ERROR(errctx);
PREPARE_ERROR(e);
while ( 1 ) {
CATCH(errctx, aksl_fread((void *)&objname, 1, namelength, fp));
CATCH(errctx, aksl_fread((void *)&ptr, 1, ptrlength, fp));
CATCH(e, aksl_fread((void *)&objname, 1, namelength, fp));
CATCH(e, aksl_fread((void *)&ptr, 1, ptrlength, fp));
// End of the map
if ( ptr == 0x00 && objname[0] == 0x00 ) {
break;
@@ -302,43 +301,43 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_Pr
// SDL_Properties objects can only use string keys, so we can't use the
// old pointer as a key without first converting it to a string.
CATCH(errctx, aksl_memset((void *)&ptrstring, 0x00, 32));
CATCH(e, aksl_memset((void *)&ptrstring, 0x00, 32));
snprintf((char *)&ptrstring, 32, "%p", ptr);
SDL_SetPointerProperty(
map,
ptrstring,
SDL_GetPointerProperty(registry, objname, NULL));
};
SUCCEED_RETURN(errctx);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(char *versiontype, char *newversion, char *curversion)
{
semver_t current_version = {};
semver_t compare_version = {};
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, versiontype, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(errctx, curversion, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(errctx, newversion, AKERR_NULLPOINTER, "NULL argument");
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, versiontype, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, curversion, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, newversion, AKERR_NULLPOINTER, "NULL argument");
ATTEMPT {
// Check save game library version
FAIL_NONZERO_BREAK(
errctx,
e,
semver_parse((const char *)curversion, &current_version),
AKERR_VALUE,
"Invalid semantic %s version in current game: %s",
versiontype,
(char *)curversion);
FAIL_NONZERO_BREAK(
errctx,
e,
semver_parse((const char *)newversion, &compare_version),
AKERR_VALUE,
"Invalid semantic %s version in save game: %s",
versiontype,
(char *)&newversion);
FAIL_ZERO_BREAK(
errctx,
e,
semver_satisfies(compare_version, current_version, "="),
AKERR_API,
"Incompatible save game %s version (%s != %s)",
@@ -348,9 +347,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(char *versiontype,
} CLEANUP {
semver_free(&current_version);
semver_free(&compare_version);
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
@@ -362,21 +361,21 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
SDL_PropertiesID charactermap;
FILE *fp = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, fpath, AKERR_NULLPOINTER, "NULL file path");
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, fpath, AKERR_NULLPOINTER, "NULL file path");
ATTEMPT {
CATCH(errctx, aksl_fopen(fpath, "rb", &fp));
CATCH(errctx, aksl_fread((void *)&savegame, 1, sizeof(akgl_Game), fp));
CATCH(errctx, akgl_game_load_versioncmp("library", (char *)&savegame.libversion, (char *)AKGL_VERSION));
CATCH(errctx, akgl_game_load_versioncmp("game", (char *)&savegame.version, (char *)&game.version));
CATCH(e, aksl_fopen(fpath, "rb", &fp));
CATCH(e, aksl_fread((void *)&savegame, 1, sizeof(akgl_Game), fp));
CATCH(e, akgl_game_load_versioncmp("library", (char *)&savegame.libversion, (char *)AKGL_VERSION));
CATCH(e, akgl_game_load_versioncmp("game", (char *)&savegame.version, (char *)&game.version));
FAIL_NONZERO_RETURN(
errctx,
e,
strncmp((char *)&savegame.name, (char *)&game.name, 256),
AKERR_API,
"Savegame is not compatible with this game");
FAIL_NONZERO_RETURN(
errctx,
e,
strncmp((char *)&savegame.uri, (char *)&game.uri, 256),
AKERR_API,
"Savegame is not compatible with this game");
@@ -384,23 +383,64 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
memcpy((void *)&game, (void *)&savegame, sizeof(akgl_Game));
// Load actor name map
actormap = SDL_CreateProperties();
CATCH(errctx, akgl_game_load_objectnamemap(fp, actormap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Actor *), AKGL_REGISTRY_ACTOR));
CATCH(e, akgl_game_load_objectnamemap(fp, actormap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Actor *), AKGL_REGISTRY_ACTOR));
// Load sprite name map
spritemap = SDL_CreateProperties();
CATCH(errctx, akgl_game_load_objectnamemap(fp, spritemap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Sprite *), AKGL_REGISTRY_SPRITE));
CATCH(e, akgl_game_load_objectnamemap(fp, spritemap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Sprite *), AKGL_REGISTRY_SPRITE));
// Load spritesheet name map
spritesheetmap = SDL_CreateProperties();
CATCH(errctx, akgl_game_load_objectnamemap(fp, spritesheetmap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_SpriteSheet *), AKGL_REGISTRY_SPRITESHEET));
CATCH(e, akgl_game_load_objectnamemap(fp, spritesheetmap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_SpriteSheet *), AKGL_REGISTRY_SPRITESHEET));
// Load character name map
charactermap = SDL_CreateProperties();
CATCH(errctx, akgl_game_load_objectnamemap(fp, charactermap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Character *), AKGL_REGISTRY_CHARACTER));
CATCH(e, akgl_game_load_objectnamemap(fp, charactermap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Character *), AKGL_REGISTRY_CHARACTER));
// Now that we have all of our pointer maps built, we can load the actual binary objects and reset their pointers
} CLEANUP {
if ( fp != NULL ) {
fclose(fp);
}
} PROCESS(errctx) {
} FINISH(errctx, true);
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(errctx);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {
.flags = (AKGL_ITERATOR_OP_LAYERMASK | AKGL_ITERATOR_OP_LAYERMASK),
.layerid = 0
};
SDL_Time curTime = SDL_GetTicksNS();
akgl_Actor *actor = NULL;
if ( opflags == NULL ) {
opflags = &defflags;
}
PASS(e, akgl_game_state_lock());
akgl_game_updateFPS();
for ( int i = 0; i < AKGL_TILEMAP_MAX_LAYERS; i++ ) {
if ( opflags == &defflags ) {
opflags->layerid = i;
}
for ( int j = 0; j < AKGL_MAX_HEAP_ACTOR; j++ ) {
actor = &HEAP_ACTOR[j];
if ( actor->refcount == 0 ) {
continue;
}
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_TILEMAPSCALE) ) {
PASS(e, akgl_tilemap_scale_actor(&gamemap, actor));
} else {
actor->scale = 1.0;
}
PASS(e, actor->updatefunc(actor));
}
}
PASS(e, physics.simulate(&physics, NULL));
PASS(e, renderer.draw_world(&renderer, NULL));
PASS(e, akgl_game_state_unlock());
SUCCEED_RETURN(e);
}

View File

@@ -20,9 +20,7 @@ akerr_ErrorContext *akgl_heap_init()
PREPARE_ERROR(errctx);
int i = 0;
akerr_name_for_status(AKGL_ERR_SDL, "SDL Error");
for ( i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) {
memset(&HEAP_ACTOR[i], 0x00, sizeof(akgl_Actor));
}
PASS(errctx, akgl_heap_init_actor());
for ( i = 0; i < AKGL_MAX_HEAP_SPRITE; i++) {
memset(&HEAP_SPRITE[i], 0x00, sizeof(akgl_Sprite));
}
@@ -38,6 +36,15 @@ akerr_ErrorContext *akgl_heap_init()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_heap_init_actor(void)
{
PREPARE_ERROR(e);
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) {
memset(&HEAP_ACTOR[i], 0x00, sizeof(akgl_Actor));
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest)
{
PREPARE_ERROR(errctx);
@@ -115,7 +122,7 @@ akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr)
if ( ptr->refcount == 0 ) {
for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN; i++ ) {
if ( ptr->children[i] != NULL ) {
CATCH_AND_RETURN(errctx, akgl_heap_release_actor(ptr->children[i]));
PASS(errctx, akgl_heap_release_actor(ptr->children[i]));
}
}
SDL_ClearProperty(AKGL_REGISTRY_ACTOR, (char *)&ptr->name);

193
src/physics.c Normal file
View File

@@ -0,0 +1,193 @@
#include <akstdlib.h>
#include <akgl/physics.h>
#include <akgl/actor.h>
#include <akgl/game.h>
#include <akgl/error.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
self->gravity = akgl_physics_null_gravity;
self->collide = akgl_physics_null_collide;
self->move = akgl_physics_null_move;
self->simulate = akgl_physics_simulate;
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
// Assume the X origin is - (screen left)
actor->gx -= (self->gravity_x * dt);
// Assume Y origin is + (down screen)
actor->gy += (self->gravity_y * dt);
// Assume Z origin is - (behind the camera)
actor->gz -= (self->gravity_z * dt);
// Counteract velocity with atmospheric drag
actor->gx += actor->vx * self->drag_x * dt;
actor->gy -= actor->vy * self->drag_y * dt;
actor->gz += actor->vz * self->drag_z * dt;
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_RETURN(e, AKERR_API, "Not implemented");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
actor->x += (-actor->vx * dt) + (actor->gx * dt);
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
actor->x += (actor->vx * dt) + (actor->gx * dt);
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
actor->y += (-actor->vy * dt) + (actor->gy * dt);
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
actor->y += (actor->vy * dt) + (actor->gy * dt);
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_sidescroller(akgl_PhysicsBackend *self)
{
akgl_String *tmp;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
PASS(e, akgl_heap_next_string(&tmp));
self->gravity = akgl_physics_ss_gravity;
self->collide = akgl_physics_ss_collide;
self->move = akgl_physics_ss_move;
self->simulate = akgl_physics_simulate;
ATTEMPT {
CATCH(e, akgl_get_property("physics.gravity.x", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_x));
CATCH(e, akgl_get_property("physics.gravity.y", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_y));
CATCH(e, akgl_get_property("physics.gravity.z", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_z));
CATCH(e, akgl_get_property("physics.drag.x", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_x));
CATCH(e, akgl_get_property("physics.drag.y", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_y));
CATCH(e, akgl_get_property("physics.drag.z", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_z));
} CLEANUP {
IGNORE(akgl_heap_release_string(tmp));
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {
.flags = 0,
.layerid = 0
};
SDL_Time curtime = SDL_GetTicksNS();
float32_t dt = (float32_t)(curtime - self->gravity_time) / (float32_t)AKGL_TIME_ONESEC_NS;
akgl_Actor *actor = NULL;
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, self->move, AKERR_NULLPOINTER, "self->move");
if ( opflags == NULL ) {
opflags = &defflags;
}
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
actor = &HEAP_ACTOR[i];
if ( actor->refcount == 0 ) {
continue;
}
if ( actor->parent != NULL ) {
// Children don't move independently of their parents, they just have an offset
continue;
} else if ( actor->basechar == NULL ) {
continue;
}
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) {
if ( actor->layer != opflags->layerid ) {
continue;
}
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ||
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
actor->tx += actor->basechar->ax * dt;
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ||
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
actor->ty += actor->basechar->ay * dt;
}
// velocity equals thrust unless thrust exceeds max speed
if ( actor->tx > actor->basechar->sx ) {
actor->vx = actor->basechar->sx;
} else {
actor->vx = actor->tx;
}
if ( actor->ty > actor->basechar->sy ) {
actor->vy = actor->basechar->sy;
} else {
actor->vy = actor->ty;
}
ATTEMPT {
CATCH(e, actor->movementlogicfunc(actor, dt));
PASS(e, self->gravity(self, actor, dt));
PASS(e, self->move(self, actor, dt));
} CLEANUP {
} PROCESS(e) {
} HANDLE(e, AKGL_ERR_LOGICINTERRUPT) {
// noop
} FINISH(e, true);
}
self->gravity_time = curtime;
SUCCEED_RETURN(e);
}

View File

@@ -10,14 +10,14 @@
#include <akgl/actor.h>
#include <akgl/json_helpers.h>
SDL_PropertiesID AKGL_REGISTRY_ACTOR;
SDL_PropertiesID AKGL_REGISTRY_ACTOR_STATE_STRINGS;
SDL_PropertiesID AKGL_REGISTRY_SPRITE;
SDL_PropertiesID AKGL_REGISTRY_SPRITESHEET;
SDL_PropertiesID AKGL_REGISTRY_CHARACTER;
SDL_PropertiesID AKGL_REGISTRY_MUSIC;
SDL_PropertiesID AKGL_REGISTRY_FONT;
SDL_PropertiesID AKGL_REGISTRY_PROPERTIES;
SDL_PropertiesID AKGL_REGISTRY_ACTOR = 0;
SDL_PropertiesID AKGL_REGISTRY_ACTOR_STATE_STRINGS = 0;
SDL_PropertiesID AKGL_REGISTRY_SPRITE = 0;
SDL_PropertiesID AKGL_REGISTRY_SPRITESHEET = 0;
SDL_PropertiesID AKGL_REGISTRY_CHARACTER = 0;
SDL_PropertiesID AKGL_REGISTRY_MUSIC = 0;
SDL_PropertiesID AKGL_REGISTRY_FONT = 0;
SDL_PropertiesID AKGL_REGISTRY_PROPERTIES = 0;
akerr_ErrorContext *akgl_registry_init()
{
@@ -39,6 +39,9 @@ akerr_ErrorContext *akgl_registry_init()
akerr_ErrorContext *akgl_registry_init_actor()
{
PREPARE_ERROR(errctx);
if ( AKGL_REGISTRY_ACTOR != 0 ) {
SDL_DestroyProperties(AKGL_REGISTRY_ACTOR);
}
AKGL_REGISTRY_ACTOR = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_ACTOR, AKERR_NULLPOINTER, "Error initializing actor registry");
SUCCEED_RETURN(errctx);
@@ -149,7 +152,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_load_properties(char *fname)
} FINISH(errctx, true);
}
SDL_Log("Properties loaded");
SUCCEED(errctx);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_set_property(char *name, char *src)

136
src/renderer.c Normal file
View File

@@ -0,0 +1,136 @@
#include <SDL3/SDL.h>
#include <akgl/renderer.h>
#include <akgl/staticstring.h>
#include <akgl/registry.h>
#include <akgl/heap.h>
#include <akgl/game.h>
#include <akerror.h>
#include <akstdlib.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_init2d(akgl_RenderBackend *self)
{
akgl_String *width = NULL;
akgl_String *height = NULL;
int screenwidth;
int screenheight;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
PASS(e, akgl_get_property("game.screenwidth", &width, "0"));
PASS(e, akgl_get_property("game.screenheight", &height, "0"));
PASS(e, aksl_atoi(width->data, &screenwidth));
PASS(e, aksl_atoi(height->data, &screenheight));
SDL_Log("Initializing screen (%sx%s = %dx%d)", width->data, height->data, screenwidth, screenheight);
PASS(e, akgl_heap_release_string(width));
PASS(e, akgl_heap_release_string(height));
FAIL_ZERO_RETURN(
e,
SDL_CreateWindowAndRenderer(game.uri, screenwidth, screenheight, 0, &window, &self->sdl_renderer),
AKGL_ERR_SDL,
"Couldn't create window/renderer: %s",
SDL_GetError());
camera.x = 0;
camera.y = 0;
camera.w = screenwidth;
camera.h = screenheight;
self->shutdown = &akgl_render_2d_shutdown;
self->frame_start = &akgl_render_2d_frame_start;
self->frame_end = &akgl_render_2d_frame_end;
self->draw_texture = &akgl_render_2d_draw_texture;
self->draw_mesh = &akgl_render_2d_draw_mesh;
self->draw_world = &akgl_render_2d_draw_world;
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_shutdown(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_start(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
SDL_SetRenderDrawColor(self->sdl_renderer, 0, 0, 0, 255);
SDL_RenderClear(self->sdl_renderer);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_end(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
SDL_RenderPresent(self->sdl_renderer);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_texture(akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, texture, AKERR_NULLPOINTER, "texture");
//FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "src");
//FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dest");
if ( angle != 0 ) {
FAIL_ZERO_RETURN(e, center, AKERR_NULLPOINTER, "center");
FAIL_ZERO_RETURN(
e,
SDL_RenderTextureRotated(self->sdl_renderer, texture, src, dest, angle, center, flip),
AKERR_NULLPOINTER, "%s", SDL_GetError()
);
} else {
FAIL_ZERO_RETURN(
e,
SDL_RenderTexture(self->sdl_renderer, texture, src, dest),
AKERR_NULLPOINTER, "%s", SDL_GetError()
);
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_mesh(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_RETURN(e, AKERR_API, "Not implemented");
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_world(akgl_RenderBackend *self, akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags;
SDL_Time curTime = SDL_GetTicksNS();
akgl_Actor *actor = NULL;
int j = 0;
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
if ( opflags == NULL ) {
opflags = &defflags;
PASS(e, aksl_memset((void *)opflags, 0x00, sizeof(akgl_Iterator)));
}
for ( int i = 0; i < AKGL_TILEMAP_MAX_LAYERS ; i++ ) {
if ( i < gamemap.numlayers ) {
PASS(e, akgl_tilemap_draw((akgl_Tilemap *)&gamemap, &camera, i));
}
for ( int j = 0; j < AKGL_MAX_HEAP_ACTOR ; j++ ) {
actor = &HEAP_ACTOR[j];
if ( actor->refcount == 0 ) {
continue;
}
if ( actor->layer != i ) {
continue;
}
PASS(e, actor->renderfunc(actor));
}
}
SUCCEED_RETURN(e);
}

View File

@@ -12,6 +12,7 @@
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/util.h>
akerr_ErrorContext *akgl_sprite_sheet_coords_for_frame(akgl_Sprite *self, SDL_FRect *srccoords, uint8_t frameid)
{
@@ -48,11 +49,7 @@ static akerr_ErrorContext *akgl_sprite_load_json_spritesheet(json_t *json, akgl_
CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, akgl_get_json_object_value((json_t *)json, "spritesheet", &spritesheet_json));
CATCH(errctx, akgl_get_json_string_value((json_t *)spritesheet_json, "filename", &ss_filename));
if ( ss_filename->data[0] != '/' ) {
SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s/%s", relative_path, ss_filename->data);
} else {
SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s", ss_filename->data);
}
CATCH(errctx, akgl_path_relative(relative_path, ss_filename->data, tmpstr));
*sheet = SDL_GetPointerProperty(
AKGL_REGISTRY_SPRITESHEET,
(char *)&tmpstr->data,
@@ -177,7 +174,7 @@ akerr_ErrorContext *akgl_spritesheet_initialize(akgl_SpriteSheet *sheet, int spr
strncpy((char *)&sheet->name, filename, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH);
//snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
sheet->texture = IMG_LoadTexture(renderer, filename);
sheet->texture = IMG_LoadTexture(renderer.sdl_renderer, filename);
FAIL_ZERO_BREAK(errctx, sheet->texture, AKGL_ERR_SDL, "Failed loading asset %s : %s", filename, SDL_GetError());
FAIL_ZERO_BREAK(

View File

@@ -1,5 +1,6 @@
#include <akerror.h>
#include <akgl/staticstring.h>
#include <errno.h>
akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init)
{
@@ -13,3 +14,17 @@ akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init)
obj->refcount = 1;
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_string_copy(akgl_String *src, akgl_String *dst, int count)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
if ( count == 0 ) {
count = AKGL_MAX_STRING_LENGTH;
}
if ( (char *)dst->data != strncpy((char *)&dst->data, (char *)&src->data, count) ) {
FAIL_RETURN(e, errno, "strncpy");
}
SUCCEED_RETURN(e);
}

View File

@@ -49,12 +49,12 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *
color);
}
FAIL_ZERO_RETURN(errctx, textsurf, AKERR_NULLPOINTER, "%s", SDL_GetError());
texture = SDL_CreateTextureFromSurface(renderer, textsurf);
texture = SDL_CreateTextureFromSurface(renderer.sdl_renderer, textsurf);
FAIL_ZERO_RETURN(errctx, texture, AKERR_NULLPOINTER, "%s", SDL_GetError());
dest.x = x;
dest.y = y;
SDL_GetTextureSize(texture, &dest.w, &dest.h);
FAIL_ZERO_RETURN(errctx, SDL_RenderTexture(renderer, texture, NULL, &dest), AKERR_NULLPOINTER, "%s", SDL_GetError());
PASS(errctx, renderer.draw_texture(&renderer, texture, NULL, &dest, 0, NULL, SDL_FLIP_NONE));
SDL_DestroyTexture(texture);
SDL_DestroySurface(textsurf);
SUCCEED_RETURN(errctx);

View File

@@ -1,9 +1,13 @@
#include <string.h>
#include <libgen.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <string.h>
#include <jansson.h>
#include <akerror.h>
#include <akstdlib.h>
#include <akgl/tilemap.h>
#include <akgl/actor.h>
@@ -12,6 +16,7 @@
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/game.h>
#include <akgl/util.h>
akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest)
{
@@ -19,6 +24,7 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
json_t *properties = NULL;
json_t *property = NULL;
akgl_String *tmpstr = NULL;
akgl_String *typestr = NULL;
int i = 0;
// This is not a generic JSON helper. It assumes we are receiving an object with a 'properties' key
// inside of it. That key is an array of objects, and each object has a name, type, and value.
@@ -33,9 +39,9 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
CATCH(errctx, akgl_heap_release_string(tmpstr));
continue;
}
CATCH(errctx, akgl_get_json_string_value(property, "type", &tmpstr));
if ( strcmp(tmpstr->data, type) != 0 ) {
FAIL_BREAK(errctx, AKERR_TYPE, "Object property is present but is incorrect type");
CATCH(errctx, akgl_get_json_string_value(property, "type", &typestr));
if ( strcmp(typestr->data, type) != 0 ) {
FAIL_BREAK(errctx, AKERR_TYPE, "Property %s is present but is incorrect type(expected %s got %s)", key, type, (char *)typestr->data);
}
*dest = property;
SUCCEED_RETURN(errctx);
@@ -44,13 +50,15 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
if ( tmpstr != NULL ) {
IGNORE(akgl_heap_release_string(tmpstr));
}
if ( typestr != NULL ) {
IGNORE(akgl_heap_release_string(typestr));
}
} PROCESS(errctx) {
} FINISH(errctx, true);
FAIL_RETURN(errctx, AKERR_KEY, "Property not found in properties map");
}
akerr_ErrorContext *akgl_get_json_properties_string(json_t *obj, char *key, akgl_String **dest)
{
PREPARE_ERROR(errctx);
@@ -96,38 +104,13 @@ akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, floa
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx)
akerr_ErrorContext *akgl_get_json_properties_float(json_t *obj, char *key, float *dest)
{
PREPARE_ERROR(errctx);
akgl_String *tmpstr = NULL;
json_t *property = NULL;
ATTEMPT {
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "columns", &dest->tilesets[tsidx].columns));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "firstgid", &dest->tilesets[tsidx].firstgid));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "imageheight", &dest->tilesets[tsidx].imageheight));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "imagewidth", &dest->tilesets[tsidx].imagewidth));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "margin", &dest->tilesets[tsidx].margin));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "spacing", &dest->tilesets[tsidx].spacing));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "tilecount", &dest->tilesets[tsidx].tilecount));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "tileheight", &dest->tilesets[tsidx].tileheight));
CATCH(errctx, akgl_get_json_integer_value((json_t *)tileset, "tilewidth", &dest->tilesets[tsidx].tilewidth));
CATCH(errctx, akgl_get_json_string_value((json_t *)tileset, "name", &tmpstr));
strncpy((char *)&dest->tilesets[tsidx].name,
(char *)&tmpstr->data,
AKGL_TILEMAP_MAX_TILESET_NAME_SIZE
);
CATCH(errctx, akgl_get_json_string_value((json_t *)tileset, "image", &tmpstr));
snprintf((char *)&dest->tilesets[tsidx].imagefilename,
AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s%s",
SDL_GetBasePath(),
tmpstr->data
);
dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer, (char *)&dest->tilesets[tsidx].imagefilename);
FAIL_ZERO_BREAK(errctx, dest->tilesets[tsidx].texture, AKERR_NULLPOINTER, "Failed loading tileset image : %s", SDL_GetError());
CATCH(errctx, akgl_get_json_tilemap_property(obj, key, "float", &property));
CATCH(errctx, akgl_get_json_number_value(property, "value", dest));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
@@ -135,6 +118,46 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilema
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname)
{
PREPARE_ERROR(e);
akgl_String *tmpstr = NULL;
akgl_String *tmppath = NULL;
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "columns", &dest->tilesets[tsidx].columns));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "firstgid", &dest->tilesets[tsidx].firstgid));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "imageheight", &dest->tilesets[tsidx].imageheight));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "imagewidth", &dest->tilesets[tsidx].imagewidth));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "margin", &dest->tilesets[tsidx].margin));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "spacing", &dest->tilesets[tsidx].spacing));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tilecount", &dest->tilesets[tsidx].tilecount));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tileheight", &dest->tilesets[tsidx].tileheight));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tilewidth", &dest->tilesets[tsidx].tilewidth));
PASS(e, akgl_get_json_string_value((json_t *)tileset, "name", &tmpstr));
PASS(e, akgl_heap_next_string(&tmpstr));
PASS(e, akgl_heap_next_string(&tmppath));
ATTEMPT {
strncpy((char *)&dest->tilesets[tsidx].name,
(char *)&tmpstr->data,
AKGL_TILEMAP_MAX_TILESET_NAME_SIZE
);
CATCH(e, akgl_get_json_string_value((json_t *)tileset, "image", &tmpstr));
CATCH(e, akgl_path_relative((char *)&dirname->data, (char *)&tmpstr->data, tmppath));
strncpy((char *)&dest->tilesets[tsidx].imagefilename, tmppath->data, AKGL_MAX_STRING_LENGTH);
} CLEANUP {
IGNORE(akgl_heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(tmppath));
} PROCESS(e) {
} FINISH(e, true);
dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer.sdl_renderer, (char *)&dest->tilesets[tsidx].imagefilename);
FAIL_ZERO_RETURN(e, dest->tilesets[tsidx].texture, AKERR_NULLPOINTER, "Failed loading tileset image : %s", SDL_GetError());
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int tilesetidx)
{
int x_offset = 0;
@@ -190,7 +213,7 @@ akerr_ErrorContext *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root)
akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "Received NULL tilemap pointer");
@@ -205,7 +228,7 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root)
CATCH(errctx, akgl_get_json_array_value(root, "tilesets", &tilesets))
for (i = 0; i < json_array_size((json_t *)tilesets); i++) {
CATCH(errctx, akgl_get_json_array_index_object((json_t *)tilesets, i, &jstileset));
CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i));
CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i, dirname));
CATCH(errctx, akgl_tilemap_compute_tileset_offsets(dest, i));
dest->numtilesets += 1;
}
@@ -216,7 +239,7 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid)
akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
akgl_String *tmpstr = NULL;
@@ -260,7 +283,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *cur
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid)
akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
json_t *layerdata = NULL;
@@ -290,17 +313,17 @@ akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *
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));
CATCH(errctx, akgl_tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid, dirname));
} 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));
CATCH(errctx, akgl_get_json_properties_number((json_t *)layerdatavalue, "scale", &dest->p_foreground_scale));
CATCH(errctx, akgl_get_json_properties_float((json_t *)layerdatavalue, "scale", &dest->p_foreground_scale));
} 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));
CATCH(errctx, akgl_get_json_properties_number((json_t *)layerdatavalue, "scale", &dest->p_vanishing_scale));
CATCH(errctx, akgl_get_json_properties_float((json_t *)layerdatavalue, "scale", &dest->p_vanishing_scale));
}
}
@@ -313,7 +336,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid)
akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
json_t *layerdata = NULL;
@@ -322,6 +345,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
ATTEMPT {
CATCH(errctx, akgl_get_json_integer_value(root, "height", &dest->layers[layerid].height));
@@ -341,7 +365,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid)
akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
akgl_String *tmpstr;
@@ -349,20 +373,23 @@ akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *ro
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
ATTEMPT {
CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, akgl_heap_next_string(&fpath));
CATCH(errctx, akgl_get_json_string_value(root, "image", &tmpstr));
DISABLE_GCC_WARNING_FORMAT_TRUNCATION
snprintf((char *)&fpath->data,
AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s%s",
SDL_GetBasePath(),
"%s/%s",
dirname->data,
tmpstr->data
);
RESTORE_GCC_WARNINGS
dest->layers[layerid].texture = IMG_LoadTexture(renderer, (char *)fpath->data);
dest->layers[layerid].texture = IMG_LoadTexture(renderer.sdl_renderer, (char *)fpath->data);
FAIL_ZERO_BREAK(errctx, dest->layers[layerid].texture, AKGL_ERR_SDL, "%s", SDL_GetError());
dest->layers[layerid].width = dest->layers[layerid].texture->w;
dest->layers[layerid].height = dest->layers[layerid].texture->h;
@@ -375,11 +402,12 @@ akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *ro
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root)
akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL json object pointer");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
json_t *layers = NULL;
json_t *layer = NULL;
akgl_String *tmpstr = NULL;
@@ -406,13 +434,13 @@ akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root)
SDL_Log("Layer %d has type %s", layerid, tmpstr->data);
if ( strncmp((char *)tmpstr->data, "objectgroup", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_OBJECTS;
CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid));
CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
} else if ( strncmp((char *)tmpstr->data, "tilelayer", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_TILES;
CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid));
CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
} else if ( strncmp((char *)tmpstr->data, "imagelayer", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_IMAGE;
CATCH(errctx, akgl_tilemap_load_layer_image((akgl_Tilemap *)dest, (json_t *)layer, layerid));
CATCH(errctx, akgl_tilemap_load_layer_image((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
}
layer = NULL;
layerid += 1;
@@ -429,6 +457,7 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
json_t *json = NULL;
//akgl_String *tmpstr = NULL;
json_error_t error;
akgl_String *dirnamestr = NULL;
FAIL_ZERO_RETURN(errctx, fname, AKERR_NULLPOINTER, "load_tilemap received null filename");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "load_tilemap received null tilemap");
@@ -437,10 +466,14 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
dest->p_foreground_scale = 1.0;
dest->p_vanishing_scale = 1.0;
PASS(errctx, akgl_heap_next_string(&dirnamestr));
ATTEMPT {
//CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
//SDL_snprintf(tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname);
CATCH(errctx, aksl_realpath(fname, (char *)&dirnamestr->data));
dirname((char *)&dirnamestr->data);
json = json_load_file(fname, 0, &error);
FAIL_ZERO_BREAK(
errctx,
@@ -461,8 +494,8 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size");
}
CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json));
CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json));
CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json, dirnamestr));
CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json, dirnamestr));
if ( dest->p_foreground_y && dest->p_vanishing_y ) {
// How much bigger is the foreground vs the vanishing point?
@@ -484,7 +517,7 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *map, SDL_FRect *viewport, int layeridx)
akerr_ErrorContext *akgl_tilemap_draw(akgl_Tilemap *map, SDL_FRect *viewport, int layeridx)
{
PREPARE_ERROR(errctx);
SDL_FRect dest = {.x = 0, .y = 0, .w = 0, .h = 0};;
@@ -540,7 +573,7 @@ akerr_ErrorContext *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *map,
src.h = map->layers[layeridx].height;
dest.w = map->layers[layeridx].width;
dest.h = map->layers[layeridx].height;
SDL_RenderTexture(renderer, map->layers[layeridx].texture, &src, &dest);
PASS(errctx, renderer.draw_texture(&renderer, map->layers[layeridx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
SUCCEED_RETURN(errctx);
}
@@ -595,7 +628,7 @@ akerr_ErrorContext *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *map,
dest.y,
dest.w,
dest.h);*/
SDL_RenderTexture(renderer, map->tilesets[tilesetidx].texture, &src, &dest);
PASS(errctx, renderer.draw_texture(&renderer, map->tilesets[tilesetidx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
}
}
dest.x += map->tilewidth;
@@ -605,14 +638,14 @@ akerr_ErrorContext *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *map,
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_draw_tileset(SDL_Renderer *renderer, akgl_Tilemap *map, int tilesetidx)
akerr_ErrorContext *akgl_tilemap_draw_tileset(akgl_Tilemap *map, int tilesetidx)
{
PREPARE_ERROR(errctx);
SDL_FRect dest;
SDL_FRect src;
int tilenum = 0;
/*
* Render every tile in a tileset to the given renderer
* Render every tile in a tileset to the default renderer
* (this is a debugging tool that shows that the recorded tile offsets are correct,
* by proving that we can reconstruct the original tileset image)
*/
@@ -650,7 +683,7 @@ akerr_ErrorContext *akgl_tilemap_draw_tileset(SDL_Renderer *renderer, akgl_Tilem
dest.y,
dest.w,
dest.h);*/
SDL_RenderTexture(renderer, map->tilesets[tilesetidx].texture, &src, &dest);
PASS(errctx, renderer.draw_texture(&renderer, map->tilesets[tilesetidx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
}
SUCCEED_RETURN(errctx);
}

View File

@@ -1,5 +1,7 @@
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <libgen.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
@@ -9,6 +11,87 @@
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/game.h>
#include <akgl/staticstring.h>
#include <akstdlib.h>
akerr_ErrorContext *akgl_path_relative_root(char *root, char *path, akgl_String *dst)
{
PREPARE_ERROR(e);
akgl_String *pathbuf;
akgl_String *strbuf;
char *result;
int rootlen;
int pathlen;
int count;
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
PASS(e, akgl_heap_next_string(&strbuf));
PASS(e, akgl_heap_next_string(&pathbuf));
ATTEMPT {
// Is it relative to the root?
rootlen = strlen(root);
pathlen = strlen(path);
if ( (rootlen + pathlen) >= AKGL_MAX_STRING_LENGTH ) {
FAIL_RETURN(e, AKERR_OUTOFBOUNDS, "Total path length (%d) is greater than maximum akgl_String length (%d)", (rootlen + pathlen), AKGL_MAX_STRING_LENGTH);
}
DISABLE_GCC_WARNING_FORMAT_TRUNCATION
CATCH(e, aksl_sprintf(&count, (char *)&pathbuf->data, "%s/%s", root, path));
RESTORE_GCC_WARNINGS
CATCH(e, aksl_realpath((char *)&pathbuf->data, (char *)&strbuf->data));
CATCH(e, akgl_string_copy(strbuf, dst, 0));
} CLEANUP {
IGNORE(akgl_heap_release_string(strbuf));
IGNORE(akgl_heap_release_string(pathbuf));
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst)
{
PREPARE_ERROR(e);
akgl_String *strbuf;
char *result;
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
PASS(e, akgl_heap_next_string(&strbuf));
ATTEMPT {
// Is path relative to our current working directory?
CATCH(e, aksl_realpath(path, (char *)&strbuf->data));
// Yes it is. strbuf->data contains the absolute path.
CATCH(e, akgl_string_copy(strbuf, dst, 0));
} CLEANUP {
IGNORE(akgl_heap_release_string(strbuf));
} PROCESS(e) {
} HANDLE(e, ENOENT) {
// Path is not relative to our current working directory
// Noop - execution proceeds after the break
return akgl_path_relative_root(root, path, dst);
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_path_relative_from(char *path, char *from, akgl_String **dst)
{
akgl_String *dirnamestr;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "path");
FAIL_ZERO_RETURN(e, from, AKERR_NULLPOINTER, "from");
PASS(e, akgl_heap_next_string(&dirnamestr));
PASS(e, aksl_realpath(from, (char *)&dirnamestr->data));
dirname((char *)&dirnamestr->data);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
{
@@ -119,17 +202,9 @@ akerr_ErrorContext *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, in
FAIL_ZERO_BREAK(errctx, t2, AKERR_NULLPOINTER, "NULL texture");
CATCH(errctx, akgl_heap_next_string(&tmpstring));
SDL_RenderClear(renderer);
FAIL_ZERO_BREAK(
errctx,
SDL_RenderTexture(
renderer,
t1,
&src,
&dest),
AKGL_ERR_SDL,
"Failed to render test texture");
s1 = SDL_RenderReadPixels(renderer, &read);
SDL_RenderClear(renderer.sdl_renderer);
CATCH(errctx, renderer.draw_texture(&renderer, t1, &src, &dest, 0, NULL, SDL_FLIP_NONE));
s1 = SDL_RenderReadPixels(renderer.sdl_renderer, &read);
FAIL_ZERO_BREAK(errctx, s1, AKGL_ERR_SDL, "Failed to read pixels from renderer");
if ( writeout != NULL ) {
@@ -143,18 +218,10 @@ akerr_ErrorContext *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, in
SDL_GetError());
}
SDL_RenderClear(renderer);
SDL_RenderClear(renderer.sdl_renderer);
FAIL_ZERO_BREAK(
errctx,
SDL_RenderTexture(
renderer,
t2,
&src,
&dest),
AKGL_ERR_SDL,
"Failed to render test texture");
s2 = SDL_RenderReadPixels(renderer, &read);
CATCH(errctx, renderer.draw_texture(&renderer, t1, &src, &dest, 0, NULL, SDL_FLIP_NONE));
s2 = SDL_RenderReadPixels(renderer.sdl_renderer, &read);
FAIL_ZERO_BREAK(errctx, s2, AKGL_ERR_SDL, "Failed to read pixels from renderer");
CATCH(errctx, akgl_compare_sdl_surfaces(s1, s2));