38 Commits

Author SHA1 Message Date
f695a035c8 Add a low FPS callback to aid in debugging, and fix a huge memory leak in akgl_text_rendertextat 2026-05-15 11:07:31 -04:00
73b1a4cab0 Make perspective scale work more explicitly from properties 2026-05-13 23:36:49 -04:00
aad196d84e akgl_heap_release* should not cascade into dependent objects (releasing actors should not release characters, sprites, and spritesheets) 2026-05-13 22:45:52 -04:00
9443fa8532 Fix bug in akgl_heap_release_actor preventing it from being cleared out of the registry 2026-05-13 19:28:38 -04:00
dc2e88b72f Fix a scale bug (default should be 1.0), add tilemap release function 2026-05-13 16:56:24 -04:00
53e4f5c14f Tilemaps can have two objects placed on it, 'p_foreground' and 'p_vanishing' whose placement form a virtual perspective plane, which causes sprites to scale as they move up and down the screen between them, creating the illusion of depth on the screen 2026-05-13 09:57:24 -04:00
b8dee456ca Add keyboard enumeration function. Make image layers work. 2026-05-13 08:02:59 -04:00
36dfd47a06 Fixed a bug in akgl_heap_next_string, made screen width and height load from properties, not on the game object 2026-05-13 04:55:19 -04:00
23dbc7d985 Make mkcontrollers script work when building as a submodule 2026-05-12 21:40:31 -04:00
6f62e674d5 Make mkcontrollers script work when building as a submodule 2026-05-12 21:38:08 -04:00
8ae99120b5 Wrap subdirectory compilation in conditional 2026-05-12 21:06:29 -04:00
c79d93dd58 Wrap subdirectory compilation in conditional 2026-05-12 21:01:47 -04:00
ccd26494d9 Import all dependencies as submodules, make cmake do the right thing 2026-05-12 16:45:42 -04:00
5439b8004b Move some of the sprite math out of the actor into the sprite 2026-05-12 15:21:36 -04:00
0f01126bad More akstdlib conversion 2026-05-11 20:58:36 -04:00
d928a8af0f Complete rename to akgl 2026-05-10 00:06:58 -04:00
4a02f0364f Start moving away from wrapping libc stuff in akerror, and move towards akstdlib that does this for me 2026-05-10 00:03:45 -04:00
cc0916cd1f Added a (registry object name -> registry object pointer) map to the save method so that registry reference by name can have their actor, sprite, spritesheet, and character references reset 2026-05-09 14:45:37 -04:00
4c771227f5 Encode library version and game version into savegame files and check them both 2026-05-08 23:48:35 -04:00
ff6b282112 Added basic game save function, doesn't save much yet, but implements version comparison of binary saves for compatibility 2026-05-08 23:15:11 -04:00
0bd1ae1df8 Move music tracks out of akgl_Game to make it more suitable for serialization 2026-05-08 22:16:43 -04:00
e0a59e2447 Add types.h, standardize integer types on stdint 2026-05-08 22:01:56 -04:00
5dda86d887 Add properties registry. Start bounding defines with conditionals so users can change library allocation size. 2026-05-08 10:16:33 -04:00
a0b2dda4cf Rename library to AKGL (AKLabs Game Library) 2026-05-07 22:20:10 -04:00
359ae23414 Unify the library on an akgl_ namespace 2026-05-07 22:01:27 -04:00
f416cb5dee Fix bad pointer reference in SDL3G_controller_default 2026-05-06 23:05:17 -04:00
ef24d7b843 Add missing text header file 2026-05-06 22:49:05 -04:00
87a5b1da21 Add ability to push controls to a control map without having to manually track controlmap index IDs 2026-05-06 12:02:36 -04:00
c3847160da Added a font registry, a text rendering helper, and an FPS counter function 2026-05-06 11:12:42 -04:00
284ffd7b4a Fix missing sdl3game.pc for pkg-config, change sprite speed and character movement values to long int expressed in nanoseconds for better compatibility with SDL_Time and locking against game clock, still expressed as milliseconds in json 2026-05-05 20:40:25 -04:00
90cbf41d75 Added missing error include 2026-05-04 00:02:31 -04:00
6763b5629f Got the suite rebuilding, most tests pass, actor and sprite are failing 2026-05-03 23:57:55 -04:00
f475dfb6ee Move from 'libsdlerror' to 'libakerror' 2026-01-05 08:58:06 -05:00
7cff27f035 Added charviewer 2026-01-05 08:57:38 -05:00
0601a8d767 Add rebuild script to make my life easier 2025-08-09 13:54:42 -04:00
4b901886a2 Add GameControllerDB support 2025-08-09 13:53:37 -04:00
62ba23a1a0 Simplified SDL3GActor_cmhf_* functions 2025-08-09 09:28:28 -04:00
93bc3addfe actor_logic_changeframe was allowing frame counters to increment out of control leading to unpredictable behavior when changing state, animations would be outside their frame range 2025-08-09 08:59:52 -04:00
83 changed files with 5551 additions and 2033 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
./build/* ./build/*
.aider*
*~

24
.gitmodules vendored Normal file
View File

@@ -0,0 +1,24 @@
[submodule "deps/semver"]
path = deps/semver
url = git@github.com:h2non/semver.c.git
[submodule "deps/SDL"]
path = deps/SDL
url = git@github.com:libsdl-org/SDL.git
[submodule "deps/SDL_image"]
path = deps/SDL_image
url = git@github.com:libsdl-org/SDL_image.git
[submodule "deps/SDL_mixer"]
path = deps/SDL_mixer
url = git@github.com:libsdl-org/SDL_mixer.git
[submodule "deps/SDL_ttf"]
path = deps/SDL_ttf
url = git@github.com:libsdl-org/SDL_ttf.git
[submodule "deps/libsdlerror"]
path = deps/libakerror
url = https://source.home.aklabs.net/andrew/libsdlerror.git
[submodule "deps/libakstdlib"]
path = deps/libakstdlib
url = https://source.home.aklabs.net/andrew/libakstdlib.git
[submodule "deps/jansson"]
path = deps/jansson
url = git@github.com:akheron/jansson.git

View File

@@ -1,28 +1,66 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(sdl3game LANGUAGES C) project(akgl LANGUAGES C)
include(CTest) include(CTest)
find_package(PkgConfig REQUIRED) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
find_package(SDL3 REQUIRED)
find_package(SDL3_image REQUIRED)
find_package(SDL3_mixer REQUIRED)
find_package(sdlerror REQUIRED)
find_package(jansson REQUIRED)
find_package(box2d REQUIRED)
# Check for SDL3 using pkg-config add_subdirectory(deps/jansson EXCLUDE_FROM_ALL)
pkg_check_modules(SDL3 REQUIRED sdl3) add_subdirectory(deps/libakerror EXCLUDE_FROM_ALL)
pkg_check_modules(SDL3_image REQUIRED sdl3-image) add_subdirectory(deps/libakstdlib EXCLUDE_FROM_ALL)
pkg_check_modules(SDL3_mixer REQUIRED sdl3-mixer) add_subdirectory(deps/SDL EXCLUDE_FROM_ALL)
pkg_check_modules(jansson REQUIRED jansson) add_subdirectory(deps/SDL_image EXCLUDE_FROM_ALL)
pkg_check_modules(sdlerror REQUIRED sdlerror) add_subdirectory(deps/SDL_mixer EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL_ttf EXCLUDE_FROM_ALL)
else()
find_package(PkgConfig REQUIRED)
if(NOT TARGET SDL3::SDL3)
find_package(SDL3 REQUIRED)
endif()
if(NOT TARGET SDL3_image::SDL3_image)
find_package(SDL3_image REQUIRED)
endif()
if(NOT TARGET SDL3_mixer::SDL3_mixer)
find_package(SDL3_mixer REQUIRED)
endif()
if(NOT TARGET SDL3_ttf::SDL3_ttf)
find_package(SDL3_ttf REQUIRED)
endif()
if(NOT TARGET akerror::akerror)
find_package(akerror REQUIRED)
endif()
if(NOT TARGET akstdlib::akstdlib)
find_package(akstdlib REQUIRED)
endif()
if(NOT TARGET jansson::jansson)
find_package(jansson)
endif()
endif()
set(GAMECONTROLLERDB_H "include/akgl/SDL_GameControllerDB.h")
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\${prefix}")
set(libdir "\${exec_prefix}/lib")
set(includedir "\${prefix}/include")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/akgl.pc.in ${CMAKE_CURRENT_BINARY_DIR}/akgl.pc @ONLY)
add_custom_command(
OUTPUT ${GAMECONTROLLERDB_H}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/mkcontrollermappings.sh ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating controller mappings ..."
)
# Add include directories # Add include directories
include_directories(${SDL3_INCLUDE_DIRS}) include_directories(${SDL3_INCLUDE_DIRS})
add_library(sdl3game SHARED add_library(akgl SHARED
deps/semver/semver.c
src/actor.c src/actor.c
src/actor_state_string_names.c src/actor_state_string_names.c
src/text.c
src/assets.c src/assets.c
src/character.c src/character.c
src/draw.c src/draw.c
@@ -35,10 +73,12 @@ add_library(sdl3game SHARED
src/staticstring.c src/staticstring.c
src/tilemap.c src/tilemap.c
src/util.c src/util.c
${GAMECONTROLLERDB_H}
) )
add_executable(charviewer util/charviewer.c) add_library(akgl::akgl ALIAS akgl)
add_executable(charviewer util/charviewer.c)
add_executable(test_actor tests/actor.c) add_executable(test_actor tests/actor.c)
add_executable(test_bitmasks tests/bitmasks.c) add_executable(test_bitmasks tests/bitmasks.c)
add_executable(test_character tests/character.c) add_executable(test_character tests/character.c)
@@ -47,6 +87,7 @@ add_executable(test_sprite tests/sprite.c)
add_executable(test_staticstring tests/staticstring.c) add_executable(test_staticstring tests/staticstring.c)
add_executable(test_tilemap tests/tilemap.c) add_executable(test_tilemap tests/tilemap.c)
add_executable(test_util tests/util.c) add_executable(test_util tests/util.c)
add_executable(test_semver_unit deps/semver/semver_unit.c)
add_test(NAME actor COMMAND test_actor) add_test(NAME actor COMMAND test_actor)
add_test(NAME bitmasks COMMAND test_bitmasks) add_test(NAME bitmasks COMMAND test_bitmasks)
add_test(NAME character COMMAND test_character) add_test(NAME character COMMAND test_character)
@@ -55,36 +96,55 @@ add_test(NAME sprite COMMAND test_sprite)
add_test(NAME staticstring COMMAND test_staticstring) add_test(NAME staticstring COMMAND test_staticstring)
add_test(NAME tilemap COMMAND test_tilemap) add_test(NAME tilemap COMMAND test_tilemap)
add_test(NAME util COMMAND test_util) add_test(NAME util COMMAND test_util)
add_test(NAME semver_unit COMMAND test_semver_unit)
# Specify include directories for the library's headers (if applicable) # Specify include directories for the library's headers (if applicable)
target_include_directories(sdl3game PUBLIC target_include_directories(akgl PUBLIC
include/ include/
deps/semver/
) )
target_link_libraries(test_actor PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) target_link_libraries(akgl
target_link_libraries(test_bitmasks PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) PUBLIC
target_link_libraries(test_character PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) SDL3::SDL3
target_link_libraries(test_registry PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) SDL3_image::SDL3_image
target_link_libraries(test_sprite PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) SDL3_mixer::SDL3_mixer
target_link_libraries(test_staticstring PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) SDL3_ttf::SDL3_ttf
target_link_libraries(test_tilemap PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) akstdlib::akstdlib
target_link_libraries(test_util PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) akerror::akerror
jansson::jansson
)
target_link_libraries(charviewer PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm) target_link_libraries(test_actor PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_bitmasks PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_character PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_registry PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_sprite PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_staticstring PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_tilemap PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
target_link_libraries(test_util PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
set(main_lib_dest "lib/sdl3game-${MY_LIBRARY_VERSION}") target_link_libraries(charviewer PRIVATE akstdlib::akstdlib akerror::akerror akgl SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer jansson::jansson -lm)
install(TARGETS sdl3game DESTINATION "lib/")
install(FILES "include/sdl3game/actor.h" DESTINATION "include/sdl3game/") set(main_lib_dest "lib/akgl-${MY_LIBRARY_VERSION}")
install(FILES "include/sdl3game/assets.h" DESTINATION "include/sdl3game/") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akgl.pc DESTINATION "lib/pkgconfig/")
install(FILES "include/sdl3game/character.h" DESTINATION "include/sdl3game/") install(TARGETS akgl DESTINATION "lib/")
install(FILES "include/sdl3game/draw.h" DESTINATION "include/sdl3game/") install(FILES "deps/semver/semver.h" DESTINATION "include/")
install(FILES "include/sdl3game/game.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/actor.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/controller.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/types.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/heap.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/text.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/iterator.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/assets.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/json_helpers.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/character.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/registry.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/error.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/sprite.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/draw.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/staticstring.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/game.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/tilemap.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/controller.h" DESTINATION "include/akgl/")
install(FILES "include/sdl3game/util.h" DESTINATION "include/sdl3game/") install(FILES "include/akgl/heap.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/iterator.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/json_helpers.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/registry.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/sprite.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/staticstring.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/tilemap.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/util.h" DESTINATION "include/akgl/")
install(FILES ${GAMECONTROLLERDB_H} DESTINATION "include/akgl/")

View File

@@ -1,5 +1,3 @@
Character definitions should allow for specifying multiple states per sprite mapping, rather than only one state per map. Use an array instead.
Rendering should move to the SDL GPU renderer so i can do lighting and particles etc Rendering should move to the SDL GPU renderer so i can do lighting and particles etc
- Example suitable for my most primitive use case: https://github.com/TheSpydog/SDL_gpu_examples/blob/main/Examples/Blit2DArray.c - Example suitable for my most primitive use case: https://github.com/TheSpydog/SDL_gpu_examples/blob/main/Examples/Blit2DArray.c
- Try vulkan and D3D tutorials to come up to speed on the moving pieces, then figure ou the details from the examples and API docs - Try vulkan and D3D tutorials to come up to speed on the moving pieces, then figure ou the details from the examples and API docs

10
akgl.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${exec_prefix}/include
Name: akgl
Description: AKLabs Game library
Version: @PROJECT_VERSION@
Cflags: -I${includedir}/
Libs: -L${libdir} -lakgl

1
deps/SDL vendored Submodule

Submodule deps/SDL added at d9d5536704

1
deps/SDL_image vendored Submodule

Submodule deps/SDL_image added at bec9134a26

1
deps/SDL_mixer vendored Submodule

Submodule deps/SDL_mixer added at a5e1890afc

1
deps/SDL_ttf vendored Submodule

Submodule deps/SDL_ttf added at a1ce3670ae

1
deps/jansson vendored Submodule

Submodule deps/jansson added at 1eb7a81297

1
deps/libakerror vendored Submodule

Submodule deps/libakerror added at 4a09ca87fd

1
deps/libakstdlib vendored Submodule

Submodule deps/libakstdlib added at b9d703f48a

1
deps/semver vendored Submodule

Submodule deps/semver added at bd1db234a6

File diff suppressed because it is too large Load Diff

107
include/akgl/actor.h Normal file
View File

@@ -0,0 +1,107 @@
#ifndef _AKGL_ACTOR_H_
#define _AKGL_ACTOR_H_
#include <stdint.h>
#include "types.h"
#include "character.h"
// ---- LOW WORD STATUSES ----
#define AKGL_ACTOR_STATE_FACE_DOWN 1 << 0 // 1 0000 0000 0000 0001
#define AKGL_ACTOR_STATE_FACE_LEFT 1 << 1 // 2 0000 0000 0000 0010
#define AKGL_ACTOR_STATE_FACE_RIGHT 1 << 2 // 4 0000 0000 0000 0100
#define AKGL_ACTOR_STATE_FACE_UP 1 << 3 // 8 0000 0000 0000 1000
#define AKGL_ACTOR_STATE_ALIVE 1 << 4 // 16 0000 0000 0001 0000
#define AKGL_ACTOR_STATE_DYING 1 << 5 // 32 0000 0000 0010 0000
#define AKGL_ACTOR_STATE_DEAD 1 << 6 // 64 0000 0000 0100 0000
#define AKGL_ACTOR_STATE_MOVING_LEFT 1 << 7 // 128 0000 0000 1000 0000
#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_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
// ----- HIGH WORD STATUSES -----
#define AKGL_ACTOR_STATE_UNDEFINED_16 1 << 16 // 65536 0000 0000 0000 0001
#define AKGL_ACTOR_STATE_UNDEFINED_17 1 << 17 // 131072 0000 0000 0000 0010
#define AKGL_ACTOR_STATE_UNDEFINED_18 1 << 18 // 262144 0000 0000 0000 0100
#define AKGL_ACTOR_STATE_UNDEFINED_19 1 << 19 // 524288 0000 0000 0000 1000
#define AKGL_ACTOR_STATE_UNDEFINED_20 1 << 20 // 1048576 0000 0000 0001 0000
#define AKGL_ACTOR_STATE_UNDEFINED_21 1 << 21 // 2097152 0000 0000 0010 0000
#define AKGL_ACTOR_STATE_UNDEFINED_22 1 << 22 // 4194304 0000 0000 0100 0000
#define AKGL_ACTOR_STATE_UNDEFINED_23 1 << 23 // 8388608 0000 0000 1000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_24 1 << 24 // 16777216 0000 0001 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_25 1 << 25 // 33554432 0000 0010 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_26 1 << 26 // 67108864 0000 0100 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_27 1 << 27 // 134217728 0000 1000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_28 1 << 28 // 268435456 0001 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_29 1 << 29 // 536870912 0010 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_30 1 << 30 // 1073741824 0100 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_31 1 << 31 // 2147483648 1000 0000 0000 0000
#define AKGL_ACTOR_MAX_STATES 32
// This is an array of strings equal to actor states from 1-32.
// This is built by a utility script and not kept in git, see
// the Makefile for lib_src/actor_state_string_names.c
extern char *AKGL_ACTOR_STATE_STRING_NAMES[AKGL_ACTOR_MAX_STATES+1];
#define AKGL_ACTOR_STATE_FACE_ALL (AKGL_ACTOR_STATE_FACE_DOWN | AKGL_ACTOR_STATE_FACE_LEFT | AKGL_ACTOR_STATE_FACE_RIGHT | AKGL_ACTOR_STATE_FACE_UP)
#define AKGL_ACTOR_STATE_MOVING_ALL (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_MOVING_DOWN)
#define AKGL_ACTOR_MAX_NAME_LENGTH 128
#define AKGL_ACTOR_MAX_CHILDREN 8
#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;
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);
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_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_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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event);
void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name);
#endif // _AKGL_ACTOR_H_

8
include/akgl/assets.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef _ASSETS_H_
#define _ASSETS_H_
#include <akerror.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_load_start_bgm(char *fname);
#endif //_ASSETS_H_

32
include/akgl/character.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef _AKGL_CHARACTER_H_
#define _AKGL_CHARACTER_H_
#include <SDL3/SDL_properties.h>
#include "types.h"
#include "sprite.h"
#define AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH 128
#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 **);
} akgl_Character;
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_initialize(akgl_Character *basechar, char *name);
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_sprite_add(akgl_Character *basechar, akgl_Sprite *ref, int state);
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_sprite_get(akgl_Character *basechar, int state, akgl_Sprite **dest);
// This is an SDL iterator so we can't return our error state from it.
void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID props, const char *name);
akerr_ErrorContext AKERR_NOIGNORE *akgl_character_load_json(char *filename);
#endif // _AKGL_CHARACTER_H_

47
include/akgl/controller.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef _CONTROLLER_H_
#define _CONTROLLER_H_
#include <SDL3/SDL.h>
#include <akerror.h>
#include "types.h"
#define AKGL_MAX_CONTROL_MAPS 8
#define AKGL_MAX_CONTROLS 32
typedef struct {
uint32_t event_on;
uint32_t event_off;
uint8_t button;
SDL_Keycode key;
uint8_t axis;
uint8_t axis_range_min;
uint8_t axis_range_max;
akerr_ErrorContext AKERR_NOIGNORE *(*handler_on)(akgl_Actor *obj, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *(*handler_off)(akgl_Actor *obj, SDL_Event *event);
} akgl_Control;
typedef struct {
akgl_Actor *target;
uint16_t nextMap;
akgl_Control controls[AKGL_MAX_CONTROLS];
SDL_KeyboardID kbid;
SDL_JoystickID jsid;
SDL_MouseID mouseid;
SDL_PenID penid;
} akgl_ControlMap;
extern akgl_ControlMap GAME_ControlMaps[AKGL_MAX_CONTROL_MAPS];
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_event(void *appstate, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_button_down(void *appstate, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_button_up(void *appstate, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_added(void *appstate, SDL_Event *event);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_removed(void *appstate, SDL_Event *event);
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);
#endif // _CONTROLLER_H_

View File

@@ -1,6 +1,6 @@
#ifndef _DRAW_H_ #ifndef _DRAW_H_
#define _DRAW_H_ #define _DRAW_H_
void GAME_draw_background(int w, int h); void akgl_draw_background(int w, int h);
#endif //_DRAW_H_ #endif //_DRAW_H_

6
include/akgl/error.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _ERROR_H_
#define _ERROR_H_
#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1
#endif // _ERROR_H_

64
include/akgl/game.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef _AKGL_GAME_H_
#define _AKGL_GAME_H_
#include <stdint.h>
#include "types.h"
#include <SDL3_mixer/SDL_mixer.h>
#include "tilemap.h"
#define AKGL_VERSION "0.1.0"
#define AKGL_GAME_AUDIO_TRACK_BGM 1
#define AKGL_GAME_AUDIO_MAX_TRACKS 64
#define AKGL_TIME_ONESEC_NS 1000000000
#define AKGL_TIME_ONESEC_MS 1000000
/* ==================== GAME STATE VARIABLES =================== */
typedef struct {
float32_t w;
float32_t h;
SDL_Texture *texture;
} akgl_Frame;
typedef struct {
int32_t flags;
} akgl_GameState;
typedef struct {
char libversion[32];
char version[32];
char name[256];
char uri[256];
akgl_GameState state;
int16_t fps;
SDL_Time gameStartTime;
SDL_Time lastIterTime;
SDL_Time lastFPSTime;
int16_t framesSinceUpdate;
void (*lowfpsfunc)(void);
} 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;
#define AKGL_BITMASK_HAS(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;
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init();
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init_screen();
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);
#endif //_AKGL_GAME_H_

45
include/akgl/heap.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef _AKGL_HEAP_H_
#define _AKGL_HEAP_H_
#include "sprite.h"
#include "actor.h"
#include "character.h"
#include "staticstring.h"
#include <akerror.h>
#ifndef AKGL_MAX_HEAP_ACTOR
#define AKGL_MAX_HEAP_ACTOR 64
#endif
#ifndef AKGL_MAX_HEAP_SPRITE
#define AKGL_MAX_HEAP_SPRITE (AKGL_MAX_HEAP_ACTOR * 16)
#endif
#ifndef AKGL_MAX_HEAP_SPRITESHEET
#define AKGL_MAX_HEAP_SPRITESHEET AKGL_MAX_HEAP_SPRITE
#endif
#ifndef AKGL_MAX_HEAP_CHARACTER
#define AKGL_MAX_HEAP_CHARACTER 256
#endif
#ifndef AKGL_MAX_HEAP_STRING
#define AKGL_MAX_HEAP_STRING 256
#endif
extern akgl_Actor HEAP_ACTOR[AKGL_MAX_HEAP_ACTOR];
extern akgl_Sprite HEAP_SPRITE[AKGL_MAX_HEAP_SPRITE];
extern akgl_SpriteSheet HEAP_SPRITESHEET[AKGL_MAX_HEAP_SPRITESHEET];
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_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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_character(akgl_Character **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_string(akgl_String **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_actor(akgl_Actor *ptr);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_sprite(akgl_Sprite *ptr);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_character(akgl_Character *ptr);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_string(akgl_String *ptr);
#endif //_AKGL_HEAP_H_

43
include/akgl/iterator.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef _AKGL_ITERATOR_H_
#define _AKGL_ITERATOR_H_
typedef struct {
uint32_t flags;
uint8_t layerid;
} akgl_Iterator;
#define AKGL_ITERATOR_OP_UPDATE 1 // 1
#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_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
#define AKGL_ITERATOR_OP_UNDEFINED_8 1 << 8 // 256
#define AKGL_ITERATOR_OP_UNDEFINED_9 1 << 9 // 512
#define AKGL_ITERATOR_OP_UNDEFINED_10 1 << 10 // 1024
#define AKGL_ITERATOR_OP_UNDEFINED_11 1 << 11 // 2048
#define AKGL_ITERATOR_OP_UNDEFINED_12 1 << 12 // 4096
#define AKGL_ITERATOR_OP_UNDEFINED_13 1 << 13 // 8192
#define AKGL_ITERATOR_OP_UNDEFINED_14 1 << 14 // 16384
#define AKGL_ITERATOR_OP_UNDEFINED_15 1 << 15 // 32768
#define AKGL_ITERATOR_OP_UNDEFINED_16 1 << 16 // 65536
#define AKGL_ITERATOR_OP_UNDEFINED_17 1 << 17 // 131072
#define AKGL_ITERATOR_OP_UNDEFINED_18 1 << 18 // 262144
#define AKGL_ITERATOR_OP_UNDEFINED_19 1 << 19 // 524288
#define AKGL_ITERATOR_OP_UNDEFINED_20 1 << 20 // 1048576
#define AKGL_ITERATOR_OP_UNDEFINED_21 1 << 21 // 2097152
#define AKGL_ITERATOR_OP_UNDEFINED_22 1 << 22 // 4194304
#define AKGL_ITERATOR_OP_UNDEFINED_23 1 << 23 // 8388608
#define AKGL_ITERATOR_OP_UNDEFINED_24 1 << 24 // 16777216
#define AKGL_ITERATOR_OP_UNDEFINED_25 1 << 25 // 33554432
#define AKGL_ITERATOR_OP_UNDEFINED_26 1 << 26 // 67108864
#define AKGL_ITERATOR_OP_UNDEFINED_27 1 << 27 // 134217728
#define AKGL_ITERATOR_OP_UNDEFINED_28 1 << 28 // 268435456
#define AKGL_ITERATOR_OP_UNDEFINED_29 1 << 29 // 536870912
#define AKGL_ITERATOR_OP_UNDEFINED_30 1 << 30 // 1073741824
#define AKGL_ITERATOR_OP_UNDEFINED_31 1 << 31 // 2147483648
#endif // _AKGL_ITERATOR_H_

View File

@@ -0,0 +1,17 @@
#ifndef _JSON_HELPERS_H_
#define _JSON_HELPERS_H_
#include <akerror.h>
#include "staticstring.h"
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_object_value(json_t *obj, char *key, json_t **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_boolean_value(json_t *obj, char *key, bool *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_integer_value(json_t *obj, char *key, int *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_number_value(json_t *obj, char *key, float *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_string_value(json_t *obj, char *key, akgl_String **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_array_value(json_t *obj, char *key, json_t **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_array_index_object(json_t *array, int index, json_t **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_array_index_integer(json_t *array, int index, int *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_array_index_string(json_t *array, int index, akgl_String **dest);
#endif // _JSON_HELPERS_H_

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

@@ -0,0 +1,29 @@
#ifndef _REGISTRY_H_
#define _REGISTRY_H_
#include <akgl/error.h>
#include <akgl/staticstring.h>
extern SDL_PropertiesID AKGL_REGISTRY_ACTOR;
extern SDL_PropertiesID AKGL_REGISTRY_SPRITE;
extern SDL_PropertiesID AKGL_REGISTRY_SPRITESHEET;
extern SDL_PropertiesID AKGL_REGISTRY_CHARACTER;
extern SDL_PropertiesID AKGL_REGISTRY_ACTOR_STATE_STRINGS;
extern SDL_PropertiesID AKGL_REGISTRY_FONT;
extern SDL_PropertiesID AKGL_REGISTRY_MUSIC;
extern SDL_PropertiesID AKGL_REGISTRY_PROPERTIES;
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_music();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_font();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_actor();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_sprite();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_spritesheet();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_character();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_properties();
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_load_properties(char *fname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_actor_state_strings();
akerr_ErrorContext AKERR_NOIGNORE *akgl_set_property(char *name, char *value);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_property(char *name, akgl_String **dest, char *def);
#endif //_REGISTRY_H_

46
include/akgl/sprite.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _AKGL_SPRITE_H_
#define _AKGL_SPRITE_H_
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL.h>
#include <akerror.h>
#define AKGL_SPRITE_MAX_FRAMES 16
#define AKGL_SPRITE_MAX_NAME_LENGTH 128
#define AKGL_SPRITE_MAX_REGISTRY_SIZE 1024
#define AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH 512
#define AKGL_MAX_HEAP_SPRITE (AKGL_MAX_HEAP_ACTOR * 16)
#define AKGL_MAX_HEAP_SPRITESHEET AKGL_MAX_HEAP_SPRITE
typedef struct {
uint8_t refcount;
SDL_Texture *texture;
char name[AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH];
uint16_t sprite_w;
uint16_t sprite_h;
} akgl_SpriteSheet;
typedef struct {
uint8_t refcount;
uint8_t frameids[AKGL_SPRITE_MAX_FRAMES]; // which IDs on the spritesheet belong to our frames
uint32_t frames; // how many frames are in this animation
uint32_t width;
uint32_t height;
uint32_t speed; // how many milliseconds a given sprite frame should be visible before cycling
bool loop; // when this sprite is done playing, it should immediately start again
bool loopReverse; // when this sprite is done playing, it should go in reverse order through its frames
char name[AKGL_SPRITE_MAX_NAME_LENGTH];
akgl_SpriteSheet *sheet;
} akgl_Sprite;
// initializes a new sprite to use the given sheet and otherwise sets to zero
akerr_ErrorContext AKERR_NOIGNORE *akgl_sprite_initialize(akgl_Sprite *spr, char *name, akgl_SpriteSheet *sheet);
// loads a given image file into a new spritesheet
akerr_ErrorContext AKERR_NOIGNORE *akgl_spritesheet_initialize(akgl_SpriteSheet *sheet, int sprite_w, int sprite_h, char *filename);
akerr_ErrorContext AKERR_NOIGNORE *akgl_sprite_load_json(char *filename);
akerr_ErrorContext *akgl_sprite_sheet_coords_for_frame(akgl_Sprite *self, SDL_FRect *srccoords, uint8_t frameid);
#endif //_AKGL_SPRITE_H_

View File

@@ -0,0 +1,17 @@
#ifndef _STRING_H_
#define _STRING_H_
#include "string.h"
#include <akerror.h>
#define AKGL_MAX_STRING_LENGTH 256
typedef struct
{
int refcount;
char data[AKGL_MAX_STRING_LENGTH];
} akgl_String;
akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init);
#endif //_STRING_H_

9
include/akgl/text.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _TEXT_H_
#define _TEXT_H_
#include <SDL3_ttf/SDL_ttf.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath, int size);
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y);
#endif // _TEXT_H_

123
include/akgl/tilemap.h Normal file
View File

@@ -0,0 +1,123 @@
#ifndef _TILEMAP_H_
#define _TILEMAP_H_
#include "actor.h"
#include "staticstring.h"
#include <jansson.h>
#define AKGL_TILEMAP_MAX_WIDTH 512
#define AKGL_TILEMAP_MAX_HEIGHT 512
#define AKGL_TILEMAP_MAX_LAYERS 16
#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_OBJECT_NAME_SIZE 512
#define AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER 128
#define AKGL_TILEMAP_OBJECT_TYPE_ACTOR 1
#define AKGL_TILEMAP_LAYER_TYPE_TILES 1
#define AKGL_TILEMAP_LAYER_TYPE_OBJECTS 2
#define AKGL_TILEMAP_LAYER_TYPE_IMAGE 3
typedef struct {
float x;
float y;
int gid;
int id;
int height;
int width;
int rotation;
int type;
bool visible;
akgl_Actor *actorptr;
char name[AKGL_TILEMAP_MAX_OBJECT_NAME_SIZE];
} akgl_TilemapObject;
typedef struct {
short type;
float opacity;
bool visible;
int height;
int width;
int x;
int y;
int id;
SDL_Texture *texture;
int data[AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT];
akgl_TilemapObject objects[AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER];
} akgl_TilemapLayer;
typedef struct {
int columns;
int firstgid;
char imagefilename[AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE];
int imageheight;
int imagewidth;
char name[AKGL_TILEMAP_MAX_TILESET_NAME_SIZE];
SDL_Texture *texture;
// Use this as a lookup table instead of storing tiles
// in individual textures to blit them from a single
// texture at runtime
// FIXME: This is probably not very efficient. For a map
// with a single tileset it makes sense. For a map with
// multiple tilesets you may have set A start at firstgid 1
// and have 1728 tiles. Set B may start at firstgid 1729 and
// have 1728 more tiles. This means Set B has 1728 empty
// tile_offsets[] entries before firstgid 1729 because of the
// way akgl_tilemap_load_tilesets() works. This is really inefficient
// and should be improved in the future, and will eventually
// lead to premature exhaustion of AKGL_TILEMAP_MAX_TILES_PER_IMAGE
// because set D or E may only have 64 tiles but they may be
// at the upper end of the array bound already because of this.
int tile_offsets[AKGL_TILEMAP_MAX_TILES_PER_IMAGE][2];
int tilecount;
int tileheight;
int tilewidth;
int spacing;
int margin;
} akgl_Tileset;
typedef struct {
int tilewidth;
int tileheight;
int width;
int height;
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_foreground_scale;
float p_vanishing_scale;
float p_scale;
float p_rate;
akgl_Tileset tilesets[AKGL_TILEMAP_MAX_TILESETS];
akgl_TilemapLayer layers[AKGL_TILEMAP_MAX_LAYERS];
} 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);
/*
* These functions are part of the internal API and should not be called by the user.
* They are only exposed here for unit testing.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest);
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_release(akgl_Tilemap *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor);
#endif //_TILEMAP_H_

9
include/akgl/types.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _AKGL_TYPES_H_
#define _AKGL_TYPES_H_
#include <stdint.h>
typedef float float32_t;
typedef double float64_t;
#endif // _AKGL_TYPES_H_

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

@@ -0,0 +1,29 @@
#ifndef _UTIL_H_
#define _UTIL_H_
#include <akerror.h>
typedef struct point {
int x;
int y;
int z;
} point;
typedef struct RectanglePoints {
point topleft;
point topright;
point bottomleft;
point bottomright;
} RectanglePoints;
#define AKGL_COLLIDE_RECTANGLES(r1x, r1y, r1w, r1h, r2x, r2y, r2w, r2h) ((r1x < (r2x + r2w)) || ((r1x + r1w) > r2x)
akerr_ErrorContext AKERR_NOIGNORE *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect);
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);
// 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);
#endif // _UTIL_H_

View File

@@ -1,105 +0,0 @@
#ifndef _ACTOR_H_
#define _ACTOR_H_
#include "character.h"
// ---- LOW WORD STATUSES ----
#define ACTOR_STATE_FACE_DOWN 1 << 0 // 1 0000 0000 0000 0001
#define ACTOR_STATE_FACE_LEFT 1 << 1 // 2 0000 0000 0000 0010
#define ACTOR_STATE_FACE_RIGHT 1 << 2 // 4 0000 0000 0000 0100
#define ACTOR_STATE_FACE_UP 1 << 3 // 8 0000 0000 0000 1000
#define ACTOR_STATE_ALIVE 1 << 4 // 16 0000 0000 0001 0000
#define ACTOR_STATE_DYING 1 << 5 // 32 0000 0000 0010 0000
#define ACTOR_STATE_DEAD 1 << 6 // 64 0000 0000 0100 0000
#define ACTOR_STATE_MOVING_LEFT 1 << 7 // 128 0000 0000 1000 0000
#define ACTOR_STATE_MOVING_RIGHT 1 << 8 // 256 0000 0001 0000 0000
#define ACTOR_STATE_MOVING_UP 1 << 9 // 512 0000 0010 0000 0000
#define ACTOR_STATE_MOVING_DOWN 1 << 10 // 1024 0000 0100 0000 0000
#define ACTOR_STATE_UNDEFINED_11 1 << 11 // 2048 0000 1000 0000 0000
#define ACTOR_STATE_UNDEFINED_12 1 << 12 // 4096 0001 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_13 1 << 13 // 8192 0010 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_14 1 << 14 // 16384 0100 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_15 1 << 15 // 32768 1000 0000 0000 0000
// ----- HIGH WORD STATUSES -----
#define ACTOR_STATE_UNDEFINED_16 1 << 16 // 65536 0000 0000 0000 0001
#define ACTOR_STATE_UNDEFINED_17 1 << 17 // 131072 0000 0000 0000 0010
#define ACTOR_STATE_UNDEFINED_18 1 << 18 // 262144 0000 0000 0000 0100
#define ACTOR_STATE_UNDEFINED_19 1 << 19 // 524288 0000 0000 0000 1000
#define ACTOR_STATE_UNDEFINED_20 1 << 20 // 1048576 0000 0000 0001 0000
#define ACTOR_STATE_UNDEFINED_21 1 << 21 // 2097152 0000 0000 0010 0000
#define ACTOR_STATE_UNDEFINED_22 1 << 22 // 4194304 0000 0000 0100 0000
#define ACTOR_STATE_UNDEFINED_23 1 << 23 // 8388608 0000 0000 1000 0000
#define ACTOR_STATE_UNDEFINED_24 1 << 24 // 16777216 0000 0001 0000 0000
#define ACTOR_STATE_UNDEFINED_25 1 << 25 // 33554432 0000 0010 0000 0000
#define ACTOR_STATE_UNDEFINED_26 1 << 26 // 67108864 0000 0100 0000 0000
#define ACTOR_STATE_UNDEFINED_27 1 << 27 // 134217728 0000 1000 0000 0000
#define ACTOR_STATE_UNDEFINED_28 1 << 28 // 268435456 0001 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_29 1 << 29 // 536870912 0010 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_30 1 << 30 // 1073741824 0100 0000 0000 0000
#define ACTOR_STATE_UNDEFINED_31 1 << 31 // 2147483648 1000 0000 0000 0000
#define ACTOR_MAX_STATES 32
// This is an array of strings equal to actor states from 1-32.
// This is built by a utility script and not kept in git, see
// the Makefile for lib_src/actor_state_string_names.c
extern char *ACTOR_STATE_STRING_NAMES[ACTOR_MAX_STATES+1];
#define ACTOR_STATE_FACE_ALL (ACTOR_STATE_FACE_DOWN | ACTOR_STATE_FACE_LEFT | ACTOR_STATE_FACE_RIGHT | ACTOR_STATE_FACE_UP)
#define ACTOR_STATE_MOVING_ALL (ACTOR_STATE_MOVING_LEFT | ACTOR_STATE_MOVING_RIGHT | ACTOR_STATE_MOVING_UP | ACTOR_STATE_MOVING_DOWN)
#define ACTOR_MAX_NAME_LENGTH 128
#define ACTOR_MAX_CHILDREN 8
#define MAX_HEAP_ACTOR 64
typedef struct actor {
int refcount;
char name[ACTOR_MAX_NAME_LENGTH];
character *basechar;
int curSpriteFrameId;
SDL_Time curSpriteFrameTimer;
bool curSpriteReversing;
int layer;
int state;
bool movement_controls_face;
void *actorData;
bool visible;
int logictimer;
float x;
float y;
struct actor *children[ACTOR_MAX_CHILDREN];
struct actor *parent;
ErrorContext ERROR_NOIGNORE *(*updatefunc)(struct actor *obj);
ErrorContext ERROR_NOIGNORE *(*renderfunc)(struct actor *obj, SDL_Renderer *renderer);
ErrorContext ERROR_NOIGNORE *(*facefunc)(struct actor *obj);
ErrorContext ERROR_NOIGNORE *(*movementlogicfunc)(struct actor *obj, SDL_Time curtimems);
ErrorContext ERROR_NOIGNORE *(*changeframefunc)(struct actor *obj, sprite *curSprite, SDL_Time curtimems);
ErrorContext ERROR_NOIGNORE *(*addchild)(struct actor *obj, struct actor *child);
} actor;
ErrorContext ERROR_NOIGNORE *actor_initialize(actor *obj, char *name);
ErrorContext ERROR_NOIGNORE *actor_set_character(actor *obj, char *basecharname);
ErrorContext ERROR_NOIGNORE *actor_render(actor *obj, SDL_Renderer *renderer);
ErrorContext ERROR_NOIGNORE *actor_update(actor *obj);
ErrorContext ERROR_NOIGNORE *actor_logic_movement(actor *obj, SDL_Time curtimems);
ErrorContext ERROR_NOIGNORE *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time curtimems);
ErrorContext ERROR_NOIGNORE *actor_automatic_face(actor *obj);
ErrorContext ERROR_NOIGNORE *actor_add_child(actor *obj, actor *child);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_left_on(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_left_off(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_right_on(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_right_off(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_up_on(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_up_off(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_down_on(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_down_off(actor *obj, SDL_Event *event);
void registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name);
#endif // _ACTOR_H_

View File

@@ -1,8 +0,0 @@
#ifndef _ASSETS_H_
#define _ASSETS_H_
#include <sdlerror.h>
ErrorContext ERROR_NOIGNORE *load_start_bgm(char *fname);
#endif //_ASSETS_H_

View File

@@ -1,31 +0,0 @@
#ifndef _CHARACTER_H_
#define _CHARACTER_H_
#include <SDL3/SDL_properties.h>
#include "sprite.h"
#define SPRITE_MAX_CHARACTER_NAME_LENGTH 128
#define MAX_HEAP_CHARACTER 256
typedef struct character {
int refcount;
char name[SPRITE_MAX_CHARACTER_NAME_LENGTH];
SDL_PropertiesID state_sprites;
ErrorContext ERROR_NOIGNORE *(*sprite_add)(struct character *, sprite *, int);
ErrorContext ERROR_NOIGNORE *(*sprite_get)(struct character *, int, sprite **);
int movementspeed;
float vx;
float vy;
} character;
ErrorContext ERROR_NOIGNORE *character_initialize(character *basechar, char *name);
ErrorContext ERROR_NOIGNORE *character_sprite_add(character *basechar, sprite *ref, int state);
ErrorContext ERROR_NOIGNORE *character_sprite_get(character *basechar, int state, sprite **dest);
// This is an SDL iterator so we can't return our error state from it.
void character_state_sprites_iterate(void *userdata, SDL_PropertiesID props, const char *name);
ErrorContext ERROR_NOIGNORE *character_load_json(char *filename);
#endif // _CHARACTER_H_

View File

@@ -1,40 +0,0 @@
#ifndef _CONTROLLER_H_
#define _CONTROLLER_H_
#include <SDL3/SDL.h>
#include <sdlerror.h>
#define MAX_CONTROL_MAPS 8
#define MAX_CONTROLS 32
typedef struct {
Uint32 event_on;
Uint32 event_off;
Uint8 button;
SDL_Keycode key;
Uint8 axis;
Uint8 axis_range_min;
Uint8 axis_range_max;
ErrorContext ERROR_NOIGNORE *(*handler_on)(actor *obj, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *(*handler_off)(actor *obj, SDL_Event *event);
} SDL3GControl;
typedef struct {
actor *target;
SDL3GControl controls[MAX_CONTROLS];
SDL_KeyboardID kbid;
SDL_JoystickID jsid;
SDL_MouseID mouseid;
SDL_PenID penid;
} SDL3GControlMap;
extern SDL3GControlMap GAME_ControlMaps[MAX_CONTROL_MAPS];
ErrorContext ERROR_NOIGNORE *controller_handle_event(void *appstate, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *controller_handle_button_down(void *appstate, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *controller_handle_button_up(void *appstate, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *controller_handle_added(void *appstate, SDL_Event *event);
ErrorContext ERROR_NOIGNORE *controller_handle_removed(void *appstate, SDL_Event *event);
#endif // _CONTROLLER_H_

View File

@@ -1,46 +0,0 @@
#ifndef _GAME_H_
#define _GAME_H_
#include <SDL3_mixer/SDL_mixer.h>
#include "tilemap.h"
/* ==================== GAME STATE VARIABLES =================== */
typedef struct {
float w;
float h;
SDL_Texture *texture;
} GAME_frame;
typedef struct {
int flags;
} GameState;
typedef struct {
char name[256];
char version[32];
char uri[256];
int screenwidth;
int screenheight;
GameState state;
MIX_Mixer *mixer;
MIX_Track *tracks[64];
} Game;
#define GAME_AUDIO_TRACK_BGM 1
extern SDL_Window *window;
extern SDL_Renderer *renderer;
extern tilemap gamemap;
extern MIX_Audio *bgm;
extern SDL_FRect camera;
extern Game game;
#define BITMASK_HAS(x, y) (x & y) == y
#define BITMASK_ADD(x, y) x |= y
#define BITMASK_DEL(x, y) x &= ~(y)
#define BITMASK_CLEAR(x) x = 0;
ErrorContext ERROR_NOIGNORE *GAME_init();
#endif //_GAME_H_

View File

@@ -1,35 +0,0 @@
#ifndef _HEAP_H_
#define _HEAP_H_
#include "sprite.h"
#include "actor.h"
#include "character.h"
#include "staticstring.h"
#include <sdlerror.h>
#define MAX_HEAP_ACTOR 64
#define MAX_HEAP_SPRITE (MAX_HEAP_ACTOR * 16)
#define MAX_HEAP_SPRITESHEET MAX_HEAP_SPRITE
#define MAX_HEAP_CHARACTER 256
#define MAX_HEAP_STRING 256
extern actor HEAP_ACTOR[MAX_HEAP_ACTOR];
extern sprite HEAP_SPRITE[MAX_HEAP_SPRITE];
extern spritesheet HEAP_SPRITESHEET[MAX_HEAP_SPRITESHEET];
extern character HEAP_CHARACTER[MAX_HEAP_CHARACTER];
extern string HEAP_STRING[MAX_HEAP_STRING];
ErrorContext ERROR_NOIGNORE *heap_init();
ErrorContext ERROR_NOIGNORE *heap_next_actor(actor **dest);
ErrorContext ERROR_NOIGNORE *heap_next_sprite(sprite **dest);
ErrorContext ERROR_NOIGNORE *heap_next_spritesheet(spritesheet **dest);
ErrorContext ERROR_NOIGNORE *heap_next_character(character **dest);
ErrorContext ERROR_NOIGNORE *heap_next_string(string **dest);
ErrorContext ERROR_NOIGNORE *heap_release_actor(actor *ptr);
ErrorContext ERROR_NOIGNORE *heap_release_sprite(sprite *ptr);
ErrorContext ERROR_NOIGNORE *heap_release_spritesheet(spritesheet *ptr);
ErrorContext ERROR_NOIGNORE *heap_release_character(character *ptr);
ErrorContext ERROR_NOIGNORE *heap_release_string(string *ptr);
#endif //_HEAP_H_

View File

@@ -1,43 +0,0 @@
#ifndef _ITERATOR_H_
#define _ITERATOR_H_
typedef struct {
int flags;
int layerid;
} iterator;
#define ITERATOR_OP_UPDATE 1 // 1
#define ITERATOR_OP_RENDER 1 << 1 // 2
#define ITERATOR_OP_RELEASE 1 << 2 // 4
#define ITERATOR_OP_LAYERMASK 1 << 3 // 8
#define ITERATOR_OP_UNDEFINED_4 1 << 4 // 16
#define ITERATOR_OP_UNDEFINED_5 1 << 5 // 32
#define ITERATOR_OP_UNDEFINED_6 1 << 6 // 64
#define ITERATOR_OP_UNDEFINED_7 1 << 7 // 128
#define ITERATOR_OP_UNDEFINED_8 1 << 8 // 256
#define ITERATOR_OP_UNDEFINED_9 1 << 9 // 512
#define ITERATOR_OP_UNDEFINED_10 1 << 10 // 1024
#define ITERATOR_OP_UNDEFINED_11 1 << 11 // 2048
#define ITERATOR_OP_UNDEFINED_12 1 << 12 // 4096
#define ITERATOR_OP_UNDEFINED_13 1 << 13 // 8192
#define ITERATOR_OP_UNDEFINED_14 1 << 14 // 16384
#define ITERATOR_OP_UNDEFINED_15 1 << 15 // 32768
#define ITERATOR_OP_UNDEFINED_16 1 << 16 // 65536
#define ITERATOR_OP_UNDEFINED_17 1 << 17 // 131072
#define ITERATOR_OP_UNDEFINED_18 1 << 18 // 262144
#define ITERATOR_OP_UNDEFINED_19 1 << 19 // 524288
#define ITERATOR_OP_UNDEFINED_20 1 << 20 // 1048576
#define ITERATOR_OP_UNDEFINED_21 1 << 21 // 2097152
#define ITERATOR_OP_UNDEFINED_22 1 << 22 // 4194304
#define ITERATOR_OP_UNDEFINED_23 1 << 23 // 8388608
#define ITERATOR_OP_UNDEFINED_24 1 << 24 // 16777216
#define ITERATOR_OP_UNDEFINED_25 1 << 25 // 33554432
#define ITERATOR_OP_UNDEFINED_26 1 << 26 // 67108864
#define ITERATOR_OP_UNDEFINED_27 1 << 27 // 134217728
#define ITERATOR_OP_UNDEFINED_28 1 << 28 // 268435456
#define ITERATOR_OP_UNDEFINED_29 1 << 29 // 536870912
#define ITERATOR_OP_UNDEFINED_30 1 << 30 // 1073741824
#define ITERATOR_OP_UNDEFINED_31 1 << 31 // 2147483648
#endif // _ITERATOR_H_

View File

@@ -1,17 +0,0 @@
#ifndef _JSON_HELPERS_H_
#define _JSON_HELPERS_H_
#include <sdlerror.h>
#include "staticstring.h"
ErrorContext ERROR_NOIGNORE *get_json_object_value(json_t *obj, char *key, json_t **dest);
ErrorContext ERROR_NOIGNORE *get_json_boolean_value(json_t *obj, char *key, bool *dest);
ErrorContext ERROR_NOIGNORE *get_json_integer_value(json_t *obj, char *key, int *dest);
ErrorContext ERROR_NOIGNORE *get_json_number_value(json_t *obj, char *key, float *dest);
ErrorContext ERROR_NOIGNORE *get_json_string_value(json_t *obj, char *key, string **dest);
ErrorContext ERROR_NOIGNORE *get_json_array_value(json_t *obj, char *key, json_t **dest);
ErrorContext ERROR_NOIGNORE *get_json_array_index_object(json_t *array, int index, json_t **dest);
ErrorContext ERROR_NOIGNORE *get_json_array_index_integer(json_t *array, int index, int *dest);
ErrorContext ERROR_NOIGNORE *get_json_array_index_string(json_t *array, int index, string **dest);
#endif // _JSON_HELPERS_H_

View File

@@ -1,20 +0,0 @@
#ifndef _REGISTRY_H_
#define _REGISTRY_H_
#include "error.h"
extern SDL_PropertiesID REGISTRY_ACTOR;
extern SDL_PropertiesID REGISTRY_SPRITE;
extern SDL_PropertiesID REGISTRY_SPRITESHEET;
extern SDL_PropertiesID REGISTRY_CHARACTER;
extern SDL_PropertiesID REGISTRY_ACTOR_STATE_STRINGS;
ErrorContext ERROR_NOIGNORE *registry_init();
ErrorContext ERROR_NOIGNORE *registry_init_actor();
ErrorContext ERROR_NOIGNORE *registry_init_sprite();
ErrorContext ERROR_NOIGNORE *registry_init_spritesheet();
ErrorContext ERROR_NOIGNORE *registry_init_character();
ErrorContext ERROR_NOIGNORE *registry_init_actor_state_strings();
#endif //_REGISTRY_H_

View File

@@ -1,43 +0,0 @@
#ifndef _SPRITE_H_
#define _SPRITE_H_
#include <SDL3/SDL_properties.h>
#include <sdlerror.h>
#define SPRITE_MAX_FRAMES 16
#define SPRITE_MAX_NAME_LENGTH 128
#define SPRITE_MAX_REGISTRY_SIZE 1024
#define SPRITE_SHEET_MAX_FILENAME_LENGTH 512
#define MAX_HEAP_SPRITE (MAX_HEAP_ACTOR * 16)
#define MAX_HEAP_SPRITESHEET MAX_HEAP_SPRITE
typedef struct {
int refcount;
SDL_Texture *texture;
char name[SPRITE_SHEET_MAX_FILENAME_LENGTH];
int sprite_w;
int sprite_h;
} spritesheet;
typedef struct {
int refcount;
spritesheet *sheet;
int frameids[SPRITE_MAX_FRAMES]; // which IDs on the spritesheet belong to our frames
int frames; // how many frames are in this animation
int width;
int height;
int speed; // how many milliseconds a given sprite frame should be visible before cycling
bool loop; // when this sprite is done playing, it should immediately start again
bool loopReverse; // when this sprite is done playing, it should go in reverse order through its frames
char name[SPRITE_MAX_NAME_LENGTH];
} sprite;
// initializes a new sprite to use the given sheet and otherwise sets to zero
ErrorContext ERROR_NOIGNORE *sprite_initialize(sprite *spr, char *name, spritesheet *sheet);
// loads a given image file into a new spritesheet
ErrorContext ERROR_NOIGNORE *spritesheet_initialize(spritesheet *sheet, int sprite_w, int sprite_h, char *filename);
ErrorContext ERROR_NOIGNORE *sprite_load_json(char *filename);
#endif //_SPRITE_H_

View File

@@ -1,17 +0,0 @@
#ifndef _STRING_H_
#define _STRING_H_
#include "string.h"
#include <sdlerror.h>
#define MAX_STRING_LENGTH 256
typedef struct
{
int refcount;
char data[MAX_STRING_LENGTH];
} string;
ErrorContext ERROR_NOIGNORE *string_initialize(string *obj, char *init);
#endif //_STRING_H_

View File

@@ -1,112 +0,0 @@
#ifndef _TILEMAP_H_
#define _TILEMAP_H_
#include "actor.h"
#include "staticstring.h"
#include <jansson.h>
#define TILEMAP_MAX_WIDTH 512
#define TILEMAP_MAX_HEIGHT 512
#define TILEMAP_MAX_LAYERS 16
#define TILEMAP_MAX_TILESETS 16
#define TILEMAP_MAX_TILES_PER_IMAGE 65536
#define TILEMAP_MAX_TILESET_NAME_SIZE 512
#define TILEMAP_MAX_TILESET_FILENAME_SIZE 512
#define TILEMAP_MAX_OBJECT_NAME_SIZE 512
#define TILEMAP_MAX_OBJECTS_PER_LAYER 128
#define TILEMAP_OBJECT_TYPE_ACTOR 1
#define TILEMAP_LAYER_TYPE_TILES 1
#define TILEMAP_LAYER_TYPE_OBJECTS 2
typedef struct {
float x;
float y;
int gid;
int id;
int height;
int width;
int rotation;
int type;
bool visible;
actor *actorptr;
char name[TILEMAP_MAX_OBJECT_NAME_SIZE];
} tilemap_object;
typedef struct {
short type;
float opacity;
bool visible;
int height;
int width;
int x;
int y;
int id;
int data[TILEMAP_MAX_WIDTH * TILEMAP_MAX_HEIGHT];
tilemap_object objects[TILEMAP_MAX_OBJECTS_PER_LAYER];
} tilemap_layer;
typedef struct {
int columns;
int firstgid;
char imagefilename[TILEMAP_MAX_TILESET_FILENAME_SIZE];
int imageheight;
int imagewidth;
char name[TILEMAP_MAX_TILESET_NAME_SIZE];
SDL_Texture *texture;
// Use this as a lookup table instead of storing tiles
// in individual textures to blit them from a single
// texture at runtime
// FIXME: This is probably not very efficient. For a map
// with a single tileset it makes sense. For a map with
// multiple tilesets you may have set A start at firstgid 1
// and have 1728 tiles. Set B may start at firstgid 1729 and
// have 1728 more tiles. This means Set B has 1728 empty
// tile_offsets[] entries before firstgid 1729 because of the
// way tilemap_load_tilesets() works. This is really inefficient
// and should be improved in the future, and will eventually
// lead to premature exhaustion of TILEMAP_MAX_TILES_PER_IMAGE
// because set D or E may only have 64 tiles but they may be
// at the upper end of the array bound already because of this.
int tile_offsets[TILEMAP_MAX_TILES_PER_IMAGE][2];
int tilecount;
int tileheight;
int tilewidth;
int spacing;
int margin;
} tileset;
typedef struct {
int tilewidth;
int tileheight;
int width;
int height;
int numlayers;
int orientation; // 0 = orthogonal, 1 = isometric
int numtilesets;
tileset tilesets[TILEMAP_MAX_TILESETS];
tilemap_layer layers[TILEMAP_MAX_LAYERS];
} tilemap;
ErrorContext ERROR_NOIGNORE *tilemap_load(char *fname, tilemap *dest);
ErrorContext ERROR_NOIGNORE *tilemap_draw(SDL_Renderer *renderer, tilemap *dest, SDL_FRect *viewport, int layeridx);
ErrorContext ERROR_NOIGNORE *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *dest, int tilesetidx);
/*
* These functions are part of the internal API and should not be called by the user.
* They are only exposed here for unit testing.
*/
ErrorContext ERROR_NOIGNORE *get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest);
ErrorContext ERROR_NOIGNORE *get_json_properties_string(json_t *obj, char *key, string **dest);
ErrorContext ERROR_NOIGNORE *get_json_properties_integer(json_t *obj, char *key, int *dest);
ErrorContext ERROR_NOIGNORE *tilemap_compute_tileset_offsets(tilemap *dest, int tilesetidx);
ErrorContext ERROR_NOIGNORE *tilemap_load_layer_objects(tilemap *dest, json_t *root, int layerid);
ErrorContext ERROR_NOIGNORE *tilemap_load_layer_tile(tilemap *dest, json_t *root, int layerid);
ErrorContext ERROR_NOIGNORE *tilemap_load_layers(tilemap *dest, json_t *root);
ErrorContext ERROR_NOIGNORE *tilemap_load_tilesets_each(json_t *tileset, tilemap *dest, int tsidx);
ErrorContext ERROR_NOIGNORE *tilemap_load_tilesets(tilemap *dest, json_t *root);
#endif //_TILEMAP_H_

View File

@@ -1,29 +0,0 @@
#ifndef _UTIL_H_
#define _UTIL_H_
#include <sdlerror.h>
typedef struct point {
int x;
int y;
int z;
} point;
typedef struct RectanglePoints {
point topleft;
point topright;
point bottomleft;
point bottomright;
} RectanglePoints;
#define COLLIDE_RECTANGLES(r1x, r1y, r1w, r1h, r2x, r2y, r2w, r2h) ((r1x < (r2x + r2w)) || ((r1x + r1w) > r2x)
ErrorContext ERROR_NOIGNORE *rectangle_points(RectanglePoints *dest, SDL_FRect *rect);
ErrorContext ERROR_NOIGNORE *collide_point_rectangle(point *p, RectanglePoints *r, bool *collide);
ErrorContext ERROR_NOIGNORE *collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide);
// These are REALLY slow routines that are only useful in testing harnesses
ErrorContext ERROR_NOIGNORE *compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2);
ErrorContext ERROR_NOIGNORE *render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout);
#endif // _UTIL_H_

32
mkcontrollermappings.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
rootdir=$1
curl https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt | grep -v '^#' | grep -v '^$' | sed s/',$'//g > mappings.txt
filelen=$(wc -l mappings.txt | cut -d ' ' -f 1)
cat > ${rootdir}/include/akgl/SDL_GameControllerDB.h <<EOF
#ifndef _SDL_GAMECONTROLLERDB_H_
#define _SDL_GAMECONTROLLERDB_H_
// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on $(date)
#define AKGL_SDL_GAMECONTROLLER_DB_LEN ${filelen}
const char *SDL_GAMECONTROLLER_DB[] = {
EOF
counter=0
cat mappings.txt | while read LINE;
do
if [[ $counter -gt 0 ]]; then
printf ",\n" >> ${rootdir}/include/akgl/SDL_GameControllerDB.h;
fi
printf " \"${LINE}\"" >> ${rootdir}/include/akgl/SDL_GameControllerDB.h;
counter=$((counter + 1))
done
printf "\n};\n" >> ${rootdir}/include/akgl/SDL_GameControllerDB.h
printf "#endif // _SDL_GAMECONTROLLERDB_H_\n" >> ${rootdir}/include/akgl/SDL_GameControllerDB.h

11
rebuild.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
export CMAKE_PREFIX_PATH=/home/andrew/local:/home/andrew/local/lib/cmake
export CMAKE_MODULE_PATH=/home/andrew/local/lib/cmake
#export SDL3_DIR=/home/andrew/local
rm -fr ~/local/lib/*akgl* ~/local/include/akgl build
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --parallel 4
cmake --install build --prefix /home/andrew/local

View File

@@ -1,38 +1,39 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <string.h> #include <string.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
ErrorContext *actor_initialize(actor *obj, char *name) akerr_ErrorContext *akgl_actor_initialize(akgl_Actor *obj, char *name)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "actor_initialize received null actor pointer"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "akgl_actor_initialize received null actor pointer");
FAIL_ZERO_RETURN(errctx, name, ERR_NULLPOINTER, "actor_initialize received null name string pointer"); FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "akgl_actor_initialize received null name string pointer");
memset(obj, 0x00, sizeof(actor)); memset(obj, 0x00, sizeof(akgl_Actor));
strncpy((char *)obj->name, name, ACTOR_MAX_NAME_LENGTH); strncpy((char *)obj->name, name, AKGL_ACTOR_MAX_NAME_LENGTH);
obj->curSpriteReversing = false; obj->curSpriteReversing = false;
obj->scale = 1.0;
obj->movement_controls_face = true; obj->movement_controls_face = true;
obj->updatefunc = &actor_update; obj->updatefunc = &akgl_actor_update;
obj->renderfunc = &actor_render; obj->renderfunc = &akgl_actor_render;
obj->facefunc = &actor_automatic_face; obj->facefunc = &akgl_actor_automatic_face;
obj->movementlogicfunc = &actor_logic_movement; obj->movementlogicfunc = &akgl_actor_logic_movement;
obj->changeframefunc = &actor_logic_changeframe; obj->changeframefunc = &akgl_actor_logic_changeframe;
obj->addchild = &actor_add_child; obj->addchild = &akgl_actor_add_child;
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, errctx,
SDL_SetPointerProperty(REGISTRY_ACTOR, name, (void *)obj), SDL_SetPointerProperty(AKGL_REGISTRY_ACTOR, name, (void *)obj),
ERR_KEY, AKERR_KEY,
"Unable to add actor to registry" "Unable to add actor to registry"
); );
obj->refcount += 1; obj->refcount += 1;
@@ -40,34 +41,34 @@ ErrorContext *actor_initialize(actor *obj, char *name)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_set_character(actor *obj, char *basecharname) akerr_ErrorContext *akgl_actor_set_character(akgl_Actor *obj, char *basecharname)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
FAIL_ZERO_RETURN(errctx, basecharname, ERR_NULLPOINTER, "Null character reference"); FAIL_ZERO_RETURN(errctx, basecharname, AKERR_NULLPOINTER, "Null character reference");
obj->basechar = SDL_GetPointerProperty(REGISTRY_CHARACTER, basecharname, NULL); obj->basechar = SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, basecharname, NULL);
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Character not found in the registry"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Character not found in the registry");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_automatic_face(actor *obj) akerr_ErrorContext *akgl_actor_automatic_face(akgl_Actor *obj)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
ATTEMPT { ATTEMPT {
if ( obj->movement_controls_face == true ) { if ( obj->movement_controls_face == true ) {
// TODO : This doesn't really work properly // TODO : This doesn't really work properly
BITMASK_DEL(obj->state, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_FACE_ALL);
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) { if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_LEFT); AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_LEFT);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) { } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_RIGHT); AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_RIGHT);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) { } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_UP); AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_UP);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) { } else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_DOWN); AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_DOWN);
} }
} }
} CLEANUP { } CLEANUP {
@@ -76,10 +77,10 @@ ErrorContext *actor_automatic_face(actor *obj)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time curtimems) akerr_ErrorContext *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtime)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
ATTEMPT { ATTEMPT {
// are we currently looping in reverse? // are we currently looping in reverse?
if ( curSprite->loop == true && obj->curSpriteReversing == true ) { if ( curSprite->loop == true && obj->curSpriteReversing == true ) {
@@ -91,13 +92,14 @@ ErrorContext *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time cu
obj->curSpriteFrameId -= 1; obj->curSpriteFrameId -= 1;
} }
// are we at the end of the animation? // are we at the end of the animation?
} else if ( obj->curSpriteFrameId == (curSprite->frames-1) ) { } else if ( obj->curSpriteFrameId >= (curSprite->frames-1) ) {
// are we set to loop in reverse? // are we set to loop in reverse?
if ( curSprite->loop == true && curSprite->loopReverse == true ) { if ( curSprite->loop == true && curSprite->loopReverse == true ) {
obj->curSpriteReversing = true; obj->curSpriteReversing = true;
obj->curSpriteFrameId -= 1; obj->curSpriteFrameId -= 1;
// are we set to loop forward? // are we set to loop forward?
} else if ( curSprite->loop == true ) { } else {
// we are at the end of the animation and we either loop forward or do not loop
obj->curSpriteFrameId = 0; obj->curSpriteFrameId = 0;
} }
// we are not looping in reverse and we are not at the end of the animation // we are not looping in reverse and we are not at the end of the animation
@@ -110,83 +112,81 @@ ErrorContext *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time cu
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_logic_movement(actor *obj, SDL_Time curtimems) akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtime)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
if ( obj->parent != NULL ) { if ( obj->parent != NULL ) {
// Children don't move independently of their parents, they just have an offset // Children don't move independently of their parents, they just have an offset
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} else { } else {
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) { if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
obj->x -= obj->basechar->vx; obj->x -= obj->basechar->vx;
} }
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) { if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
obj->x += obj->basechar->vx; obj->x += obj->basechar->vx;
} }
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) { if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
obj->y -= obj->basechar->vy; obj->y -= obj->basechar->vy;
} }
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) { if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
obj->y += obj->basechar->vy; obj->y += obj->basechar->vy;
} }
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_update(actor *obj) akerr_ErrorContext *akgl_actor_update(akgl_Actor *obj)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
SDL_Time curtime = 0; SDL_Time curtime = 0;
SDL_Time curtimems = 0; akgl_Sprite *curSprite = NULL;
sprite *curSprite = NULL;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor reference");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT { ATTEMPT {
SDL_GetCurrentTime(&curtime); SDL_GetCurrentTime(&curtime);
curtimems = curtime / 1000000;
CATCH(errctx, obj->facefunc(obj)); CATCH(errctx, obj->facefunc(obj));
// is it time to apply movement logic? // is it time to apply movement logic?
if ( (curtimems - obj->logictimer) >= obj->basechar->movementspeed ) { if ( (curtime - obj->logictimer) >= obj->basechar->movementspeed ) {
CATCH(errctx, obj->movementlogicfunc(obj, curtimems)); CATCH(errctx, obj->movementlogicfunc(obj, curtime));
obj->logictimer = curtimems; obj->logictimer = curtime;
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, false); } FINISH(errctx, false);
ATTEMPT { ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite)); CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
// is it time to change frames? // is it time to change frames?
if ( (curtimems - obj->curSpriteFrameTimer) >= curSprite->speed ) { if ( ((curtime) - obj->curSpriteFrameTimer) >= curSprite->speed) {
CATCH(errctx, obj->changeframefunc(obj, curSprite, curtimems)); CATCH(errctx, obj->changeframefunc(obj, curSprite, curtime));
obj->curSpriteFrameTimer = curtimems; obj->curSpriteFrameTimer = curtime;
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_KEY) { } HANDLE(errctx, AKERR_KEY) {
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
static ErrorContext *actor_visible(actor *obj, SDL_FRect *camera, bool *visible) static akerr_ErrorContext *actor_visible(akgl_Actor *obj, SDL_FRect *camera, bool *visible)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
sprite *curSprite = NULL; akgl_Sprite *curSprite = NULL;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, ERR_NULLPOINTER, "NULL renderer"); FAIL_ZERO_RETURN(errctx, renderer, AKERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT { ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite)); CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_KEY) { } HANDLE(errctx, AKERR_KEY) {
// TODO: Actor has no sprite matching the current state. Should we treat this as an error and throw? // TODO: Actor has no sprite matching the current state. Should we treat this as an error and throw?
*visible = false; *visible = false;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
@@ -203,24 +203,25 @@ static ErrorContext *actor_visible(actor *obj, SDL_FRect *camera, bool *visible)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_render(actor *obj, SDL_Renderer *renderer) akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj, SDL_Renderer *renderer)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
sprite *curSprite = NULL; akgl_Sprite *curSprite = NULL;
bool visible = false; bool visible = false;
SDL_FRect src; SDL_FRect src;
SDL_FRect dest; SDL_FRect dest;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, ERR_NULLPOINTER, "NULL renderer"); FAIL_ZERO_RETURN(errctx, renderer, AKERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference"); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT { ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite)); CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
CATCH(errctx, actor_visible(obj, &camera, &visible)); CATCH(errctx, actor_visible(obj, &camera, &visible));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_KEY) { } 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 // If an actor doesn't have a sprite for a state, just log it and move on
LOG_ERROR(errctx); LOG_ERROR(errctx);
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -229,15 +230,24 @@ ErrorContext *actor_render(actor *obj, SDL_Renderer *renderer)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
src.x = curSprite->width * curSprite->frameids[obj->curSpriteFrameId]; if ( (obj->curSpriteFrameId > curSprite->frames) ) {
if ( src.x >= curSprite->sheet->texture->w ) { // This isn't necessarily an error - this actor's frame index is outside the range of
src.y = ((int)src.x / curSprite->sheet->texture->w) * curSprite->height; // their current sprite. There are a number of reasons this could happen, and it will
src.x = ((int)src.x % curSprite->sheet->texture->w); // get cleaned up on the next logic update. Just pass on rendering them this frame.
} else { SUCCEED_RETURN(errctx);
src.y = 0;
} }
src.w = curSprite->width;
src.h = curSprite->height; ATTEMPT {
CATCH(errctx,
akgl_sprite_sheet_coords_for_frame(
curSprite,
&src,
obj->curSpriteFrameId)
);
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
if ( obj->parent != NULL ) { if ( obj->parent != NULL ) {
dest.x = (obj->parent->x + obj->x - camera.x); dest.x = (obj->parent->x + obj->x - camera.x);
dest.y = (obj->parent->y + obj->y - camera.y); dest.y = (obj->parent->y + obj->y - camera.y);
@@ -245,22 +255,22 @@ ErrorContext *actor_render(actor *obj, SDL_Renderer *renderer)
dest.x = (obj->x - camera.x); dest.x = (obj->x - camera.x);
dest.y = (obj->y - camera.y); dest.y = (obj->y - camera.y);
} }
dest.w = curSprite->width; dest.w = curSprite->width * obj->scale;
dest.h = curSprite->width; dest.h = curSprite->width * obj->scale;
SDL_RenderTexture(renderer, curSprite->sheet->texture, &src, &dest); SDL_RenderTexture(renderer, curSprite->sheet->texture, &src, &dest);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *actor_add_child(actor *obj, actor *child) akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child)
{ {
int i = 0; int i = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL parent pointer"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL parent pointer");
FAIL_ZERO_RETURN(errctx, child, ERR_NULLPOINTER, "NULL child pointer"); FAIL_ZERO_RETURN(errctx, child, AKERR_NULLPOINTER, "NULL child pointer");
FAIL_NONZERO_RETURN(errctx, child->parent, ERR_RELATIONSHIP, "Child object already has a parent"); FAIL_NONZERO_RETURN(errctx, child->parent, AKERR_RELATIONSHIP, "Child object already has a parent");
for ( i = 0; i < ACTOR_MAX_CHILDREN ; i++ ) { for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN ; i++ ) {
if ( obj->children[i] == NULL ) { if ( obj->children[i] == NULL ) {
obj->children[i] = child; obj->children[i] = child;
child->parent = obj; child->parent = obj;
@@ -268,30 +278,35 @@ ErrorContext *actor_add_child(actor *obj, actor *child)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
} }
FAIL_RETURN(errctx, ERR_OUTOFBOUNDS, "Parent object has no remaining child slots left"); 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 // 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 // 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 registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name) void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const char *name)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
iterator *opflags = (iterator *)userdata; akgl_Iterator *opflags = (akgl_Iterator *)userdata;
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, name, ERR_NULLPOINTER, "registry_iterate_actor received NULL property name"); FAIL_ZERO_BREAK(errctx, name, AKERR_NULLPOINTER, "registry_iterate_actor received NULL property name");
FAIL_ZERO_BREAK(errctx, opflags, ERR_NULLPOINTER, "received NULL iterator flags"); FAIL_ZERO_BREAK(errctx, opflags, AKERR_NULLPOINTER, "received NULL iterator flags");
actor *obj = (actor *)SDL_GetPointerProperty(registry, name, NULL); akgl_Actor *obj = (akgl_Actor *)SDL_GetPointerProperty(registry, name, NULL);
FAIL_ZERO_BREAK(errctx, obj, ERR_KEY, "registry_iterate_actor received property name that was not in the registry"); FAIL_ZERO_BREAK(errctx, obj, AKERR_KEY, "registry_iterate_actor received property name that was not in the registry");
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_LAYERMASK) ) { if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) {
if ( obj->layer != opflags->layerid ) { if ( obj->layer != opflags->layerid ) {
break; break;
} }
} }
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_UPDATE) ) { if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_UPDATE) ) {
CATCH(errctx, obj->updatefunc(obj)); CATCH(errctx, obj->updatefunc(obj));
} }
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_RENDER) ) { 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)); CATCH(errctx, obj->renderfunc(obj, renderer));
} }
} CLEANUP { } CLEANUP {
@@ -299,122 +314,94 @@ void registry_iterate_actor(void *userdata, SDL_PropertiesID registry, const cha
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_left_on(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) { //SDL_Log("event %d (button %d / key %d) moves actor left", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); 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("event %d (button %d / key %d) moves actor left", event->type, event->gbutton.which, event->key.key); //SDL_Log("new target actor state: %b", obj->state);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, (ACTOR_STATE_MOVING_LEFT | ACTOR_STATE_FACE_LEFT));
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_left_off(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) { //SDL_Log("event %d (button %d / key %d) stops moving actor left", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT);
} //SDL_Log("new target actor state: %b", obj->state);
SDL_Log("event %d (button %d / key %d) stops moving actor left", event->type, event->gbutton.which, event->key.key);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_LEFT);
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_right_on(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) { //SDL_Log("event %d (button %d / key %d) moves actor right", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); 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("event %d (button %d / key %d) moves actor right", event->type, event->gbutton.which, event->key.key); //SDL_Log("new target actor state: %b", obj->state);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, (ACTOR_STATE_MOVING_RIGHT | ACTOR_STATE_FACE_RIGHT));
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_right_off(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) { //SDL_Log("event %d (button %d / key %d) stops moving actor right", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
} //SDL_Log("new target actor state: %b", obj->state);
SDL_Log("event %d (button %d / key %d) stops moving actor right", event->type, event->gbutton.which, event->key.key);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_RIGHT);
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_up_on(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) { //SDL_Log("event %d (button %d / key %d) moves actor up", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); 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("event %d (button %d / key %d) moves actor up", event->type, event->gbutton.which, event->key.key); //SDL_Log("new target actor state: %b", obj->state);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, (ACTOR_STATE_FACE_UP | ACTOR_STATE_MOVING_UP));
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_up_off(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) { //SDL_Log("event %d (button %d / key %d) stops moving actor up", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_UP);
} //SDL_Log("new target actor state: %b", obj->state);
SDL_Log("event %d (button %d / key %d) stops moving actor up", event->type, event->gbutton.which, event->key.key);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_UP);
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_down_on(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) { //SDL_Log("event %d (button %d / key %d) moves actor down", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); 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("event %d (button %d / key %d) moves actor down", event->type, event->gbutton.which, event->key.key); //SDL_Log("new target actor state: %b", obj->state);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, (ACTOR_STATE_MOVING_DOWN | ACTOR_STATE_FACE_DOWN));
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_down_off(actor *obj, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) { //SDL_Log("event %d (button %d / key %d) stops moving actor down", event->type, event->gbutton.which, event->key.key);
SUCCEED_RETURN(errctx); AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN);
} //SDL_Log("new target actor state: %b", obj->state);
SDL_Log("event %d (button %d / key %d) stops moving actor down", event->type, event->gbutton.which, event->key.key);
BITMASK_DEL(obj->state, (ACTOR_STATE_FACE_ALL | ACTOR_STATE_MOVING_ALL));
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_DOWN);
SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }

View File

@@ -1,34 +1,34 @@
char *ACTOR_STATE_STRING_NAMES[32] = { char *AKGL_ACTOR_STATE_STRING_NAMES[32] = {
"ACTOR_STATE_FACE_DOWN", "AKGL_ACTOR_STATE_FACE_DOWN",
"ACTOR_STATE_FACE_LEFT", "AKGL_ACTOR_STATE_FACE_LEFT",
"ACTOR_STATE_FACE_RIGHT", "AKGL_ACTOR_STATE_FACE_RIGHT",
"ACTOR_STATE_FACE_UP", "AKGL_ACTOR_STATE_FACE_UP",
"ACTOR_STATE_ALIVE", "AKGL_ACTOR_STATE_ALIVE",
"ACTOR_STATE_DYING", "AKGL_ACTOR_STATE_DYING",
"ACTOR_STATE_DEAD", "AKGL_ACTOR_STATE_DEAD",
"ACTOR_STATE_MOVING_LEFT", "AKGL_ACTOR_STATE_MOVING_LEFT",
"ACTOR_STATE_MOVING_RIGHT", "AKGL_ACTOR_STATE_MOVING_RIGHT",
"ACTOR_STATE_MOVING_UP", "AKGL_ACTOR_STATE_MOVING_UP",
"ACTOR_STATE_MOVING_DOWN", "AKGL_ACTOR_STATE_MOVING_DOWN",
"ACTOR_STATE_UNDEFINED_11", "AKGL_ACTOR_STATE_UNDEFINED_11",
"ACTOR_STATE_UNDEFINED_12", "AKGL_ACTOR_STATE_UNDEFINED_12",
"ACTOR_STATE_UNDEFINED_13", "AKGL_ACTOR_STATE_UNDEFINED_13",
"ACTOR_STATE_UNDEFINED_14", "AKGL_ACTOR_STATE_UNDEFINED_14",
"ACTOR_STATE_UNDEFINED_15", "AKGL_ACTOR_STATE_UNDEFINED_15",
"ACTOR_STATE_UNDEFINED_16", "AKGL_ACTOR_STATE_UNDEFINED_16",
"ACTOR_STATE_UNDEFINED_17", "AKGL_ACTOR_STATE_UNDEFINED_17",
"ACTOR_STATE_UNDEFINED_18", "AKGL_ACTOR_STATE_UNDEFINED_18",
"ACTOR_STATE_UNDEFINED_19", "AKGL_ACTOR_STATE_UNDEFINED_19",
"ACTOR_STATE_UNDEFINED_20", "AKGL_ACTOR_STATE_UNDEFINED_20",
"ACTOR_STATE_UNDEFINED_21", "AKGL_ACTOR_STATE_UNDEFINED_21",
"ACTOR_STATE_UNDEFINED_22", "AKGL_ACTOR_STATE_UNDEFINED_22",
"ACTOR_STATE_UNDEFINED_23", "AKGL_ACTOR_STATE_UNDEFINED_23",
"ACTOR_STATE_UNDEFINED_24", "AKGL_ACTOR_STATE_UNDEFINED_24",
"ACTOR_STATE_UNDEFINED_25", "AKGL_ACTOR_STATE_UNDEFINED_25",
"ACTOR_STATE_UNDEFINED_26", "AKGL_ACTOR_STATE_UNDEFINED_26",
"ACTOR_STATE_UNDEFINED_27", "AKGL_ACTOR_STATE_UNDEFINED_27",
"ACTOR_STATE_UNDEFINED_28", "AKGL_ACTOR_STATE_UNDEFINED_28",
"ACTOR_STATE_UNDEFINED_29", "AKGL_ACTOR_STATE_UNDEFINED_29",
"ACTOR_STATE_UNDEFINED_30", "AKGL_ACTOR_STATE_UNDEFINED_30",
"ACTOR_STATE_UNDEFINED_31", "AKGL_ACTOR_STATE_UNDEFINED_31",
}; };

View File

@@ -1,47 +1,48 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <akgl/error.h>
ErrorContext *load_start_bgm(char *fname) akerr_ErrorContext *akgl_load_start_bgm(char *fname)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
string *tmpstr = NULL; //akgl_String *tmpstr = NULL;
MIX_Track *bgmtrack = NULL; MIX_Track *bgmtrack = NULL;
SDL_PropertiesID bgmprops = 0; SDL_PropertiesID bgmprops = 0;
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, fname, ERR_NULLPOINTER, "load_start_bgm received NULL filename"); FAIL_ZERO_BREAK(errctx, fname, AKERR_NULLPOINTER, "akgl_load_start_bgm received NULL filename");
CATCH(errctx, heap_next_string(&tmpstr)); //CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, string_initialize(tmpstr, NULL)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname); //SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname);
SDL_Log("Loading music asset from %s", (char *)&tmpstr->data); SDL_Log("Loading music asset from %s", fname);
bgm = MIX_LoadAudio(game.mixer, (char *)&tmpstr->data, true); bgm = MIX_LoadAudio(akgl_mixer, fname, true);
FAIL_ZERO_BREAK(errctx, bgm, ERR_NULLPOINTER, "Failed to load music asset %s : %s", (char *)&tmpstr->data, SDL_GetError()); FAIL_ZERO_BREAK(errctx, bgm, AKERR_NULLPOINTER, "Failed to load music asset %s : %s", fname, SDL_GetError());
bgmtrack = MIX_CreateTrack(game.mixer); bgmtrack = MIX_CreateTrack(akgl_mixer);
FAIL_ZERO_BREAK(errctx, bgmtrack, ERR_NULLPOINTER, "Failed to create audio track for background music: %s", SDL_GetError()); FAIL_ZERO_BREAK(errctx, bgmtrack, AKERR_NULLPOINTER, "Failed to create audio track for background music: %s", SDL_GetError());
game.tracks[GAME_AUDIO_TRACK_BGM] = bgmtrack; akgl_tracks[AKGL_GAME_AUDIO_TRACK_BGM] = bgmtrack;
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
MIX_SetTrackAudio(bgmtrack, bgm), MIX_SetTrackAudio(bgmtrack, bgm),
ERR_SDL, AKGL_ERR_SDL,
"%s", "%s",
SDL_GetError()); SDL_GetError());
SDL_SetNumberProperty(bgmprops, MIX_PROP_PLAY_LOOPS_NUMBER, -1); SDL_SetNumberProperty(bgmprops, MIX_PROP_PLAY_LOOPS_NUMBER, -1);
if (!MIX_PlayTrack(bgmtrack, bgmprops)) { if (!MIX_PlayTrack(bgmtrack, bgmprops)) {
FAIL_BREAK(errctx, ERR_SDL, "Failed to play music asset %s", fname); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Failed to play music asset %s", fname);
} }
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(tmpstr)); //IGNORE(akgl_heap_release_string(tmpstr));
if ( errctx != NULL ) { if ( errctx != NULL ) {
if ( errctx->status != 0 && bgm != NULL) { if ( errctx->status != 0 && bgm != NULL) {
MIX_DestroyAudio(bgm); MIX_DestroyAudio(bgm);

View File

@@ -1,46 +1,46 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <sdlerror.h> #include <akerror.h>
#include <string.h> #include <string.h>
#include <jansson.h> #include <jansson.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/json_helpers.h> #include <akgl/json_helpers.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
#include <sdl3game/util.h> #include <akgl/util.h>
ErrorContext *character_initialize(character *obj, char *name) akerr_ErrorContext *akgl_character_initialize(akgl_Character *obj, char *name)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL character reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL akgl_Character reference");
FAIL_ZERO_RETURN(errctx, name, ERR_NULLPOINTER, "NULL name string pointer"); FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "NULL name string pointer");
memset(obj, 0x00, sizeof(character)); memset(obj, 0x00, sizeof(akgl_Character));
strncpy(obj->name, name, SPRITE_MAX_CHARACTER_NAME_LENGTH); strncpy(obj->name, name, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH);
obj->state_sprites = SDL_CreateProperties(); obj->state_sprites = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, obj->state_sprites, ERR_NULLPOINTER, "Unable to initialize SDL_PropertiesID for character state map"); FAIL_ZERO_RETURN(errctx, obj->state_sprites, AKERR_NULLPOINTER, "Unable to initialize SDL_PropertiesID for character state map");
obj->sprite_add = &character_sprite_add; obj->sprite_add = &akgl_character_sprite_add;
obj->sprite_get = &character_sprite_get; obj->sprite_get = &akgl_character_sprite_get;
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, errctx,
SDL_SetPointerProperty(REGISTRY_CHARACTER, name, (void *)obj), SDL_SetPointerProperty(AKGL_REGISTRY_CHARACTER, name, (void *)obj),
ERR_KEY, AKERR_KEY,
"Unable to add character to registry"); "Unable to add character to registry");
obj->refcount += 1; obj->refcount += 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *character_sprite_add(character *basechar, sprite *ref, int state) akerr_ErrorContext *akgl_character_sprite_add(akgl_Character *basechar, akgl_Sprite *ref, int state)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
char stateval[32]; char stateval[32];
FAIL_ZERO_RETURN(errctx, basechar, ERR_NULLPOINTER, "NULL character reference"); FAIL_ZERO_RETURN(errctx, basechar, AKERR_NULLPOINTER, "NULL character reference");
FAIL_ZERO_RETURN(errctx, ref, ERR_NULLPOINTER, "NULL sprite reference"); FAIL_ZERO_RETURN(errctx, ref, AKERR_NULLPOINTER, "NULL sprite reference");
memset(&stateval, 0x00, 32); memset(&stateval, 0x00, 32);
SDL_itoa(state, (char *)&stateval, 10); SDL_itoa(state, (char *)&stateval, 10);
SDL_SetPointerProperty(basechar->state_sprites, (char *)&stateval, ref); SDL_SetPointerProperty(basechar->state_sprites, (char *)&stateval, ref);
@@ -49,17 +49,17 @@ ErrorContext *character_sprite_add(character *basechar, sprite *ref, int state)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *character_sprite_get(character *basechar, int state, sprite **dest) akerr_ErrorContext *akgl_character_sprite_get(akgl_Character *basechar, int state, akgl_Sprite **dest)
{ {
sprite *target = NULL; akgl_Sprite *target = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
char stateval[32]; char stateval[32];
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL pointer to sprite pointer (**dest)"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL pointer to sprite pointer (**dest)");
FAIL_ZERO_RETURN(errctx, basechar, ERR_NULLPOINTER, "NULL character reference"); FAIL_ZERO_RETURN(errctx, basechar, AKERR_NULLPOINTER, "NULL character reference");
memset(&stateval, 0x00, 32); memset(&stateval, 0x00, 32);
SDL_itoa(state, (char *)&stateval, 10); SDL_itoa(state, (char *)&stateval, 10);
*dest = (sprite *)SDL_GetPointerProperty(basechar->state_sprites, (char *)&stateval, NULL); *dest = (akgl_Sprite *)SDL_GetPointerProperty(basechar->state_sprites, (char *)&stateval, NULL);
FAIL_ZERO_RETURN(errctx, *dest, ERR_KEY, "Sprite for state %d (%b) not found in the character's registry", state, state); FAIL_ZERO_RETURN(errctx, *dest, AKERR_KEY, "Sprite for state %d (%b) not found in the character's registry", state, state);
target = *dest; target = *dest;
//SDL_Log("Sprite state %d (%s) has character %s", state, (char *)&stateval, target->name); //SDL_Log("Sprite state %d (%s) has character %s", state, (char *)&stateval, target->name);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
@@ -67,134 +67,137 @@ ErrorContext *character_sprite_get(character *basechar, int state, sprite **dest
// SDL iterator so we can't return error information here, void only // 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 // 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 character_state_sprites_iterate(void *userdata, SDL_PropertiesID registry, const char *name) void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID registry, const char *name)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
sprite *spriteptr; akgl_Sprite *spriteptr;
iterator *opflags = (iterator *)userdata; akgl_Iterator *opflags = (akgl_Iterator *)userdata;
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, opflags, ERR_NULLPOINTER, "Character state sprite iterator received null iterator op pointer"); FAIL_ZERO_BREAK(errctx, opflags, AKERR_NULLPOINTER, "Character state sprite iterator received null iterator op pointer");
FAIL_ZERO_BREAK(errctx, name, ERR_NULLPOINTER, "Character state sprite iterator received null sprite name"); FAIL_ZERO_BREAK(errctx, name, AKERR_NULLPOINTER, "Character state sprite iterator received null sprite name");
spriteptr = (sprite *)SDL_GetPointerProperty(registry, name, NULL); spriteptr = (akgl_Sprite *)SDL_GetPointerProperty(registry, name, NULL);
FAIL_ZERO_BREAK(errctx, spriteptr, ERR_NULLPOINTER, "Character state sprite for %s not found", name); FAIL_ZERO_BREAK(errctx, spriteptr, AKERR_NULLPOINTER, "Character state sprite for %s not found", name);
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_RELEASE) ) { if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_RELEASE) ) {
CATCH(errctx, heap_release_sprite(spriteptr)); CATCH(errctx, akgl_heap_release_sprite(spriteptr));
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);
} }
static ErrorContext *character_load_json_state_int_from_strings(json_t *states, int *dest) static akerr_ErrorContext *akgl_character_load_json_state_int_from_strings(json_t *states, int *dest)
{ {
int i = 0; int i = 0;
long newstate = 0; long newstate = 0;
string *tmpstring = NULL; akgl_String *tmpstring = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, states, ERR_NULLPOINTER, "NULL states array"); FAIL_ZERO_RETURN(errctx, states, AKERR_NULLPOINTER, "NULL states array");
FAIL_ZERO_RETURN(errctx, states, ERR_NULLPOINTER, "NULL destination integer"); FAIL_ZERO_RETURN(errctx, states, AKERR_NULLPOINTER, "NULL destination integer");
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstring)); CATCH(errctx, akgl_heap_next_string(&tmpstring));
for ( i = 0; i < json_array_size((json_t *)states) ; i++ ) { for ( i = 0; i < json_array_size((json_t *)states) ; i++ ) {
CATCH(errctx, get_json_array_index_string(states, i, &tmpstring)); CATCH(errctx, akgl_get_json_array_index_string(states, i, &tmpstring));
newstate = (long)SDL_GetNumberProperty(REGISTRY_ACTOR_STATE_STRINGS, (char *)&tmpstring->data, 0); newstate = (long)SDL_GetNumberProperty(AKGL_REGISTRY_ACTOR_STATE_STRINGS, (char *)&tmpstring->data, 0);
FAIL_ZERO_BREAK(errctx, newstate, ERR_KEY, "Unknown actor state %s", (char *)&tmpstring->data); FAIL_ZERO_BREAK(errctx, newstate, AKERR_KEY, "Unknown actor state %s", (char *)&tmpstring->data);
*dest = (*dest | (int)(newstate)); *dest = (*dest | (int)(newstate));
} }
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(tmpstring)); IGNORE(akgl_heap_release_string(tmpstring));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
static ErrorContext *character_load_json_inner(json_t *json, character *obj) static akerr_ErrorContext *akgl_character_load_json_inner(json_t *json, akgl_Character *obj)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *mappings = NULL; json_t *mappings = NULL;
json_t *curmapping = NULL; json_t *curmapping = NULL;
json_t *statearray = NULL; json_t *statearray = NULL;
sprite *spriteptr = NULL; akgl_Sprite *spriteptr = NULL;
int i = 0; int i = 0;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
string *tmpstr2 = NULL; akgl_String *tmpstr2 = NULL;
int stateval = 0; int stateval = 0;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_string_value((json_t *)json, "name", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &tmpstr));
CATCH(errctx, character_initialize((character *)obj, tmpstr->data)); CATCH(errctx, akgl_character_initialize((akgl_Character *)obj, tmpstr->data));
CATCH(errctx, get_json_array_value((json_t *)json, "sprite_mappings", &mappings)); CATCH(errctx, akgl_get_json_array_value((json_t *)json, "sprite_mappings", &mappings));
for ( i = 0; i < json_array_size((json_t *)mappings) ; i++ ) { for ( i = 0; i < json_array_size((json_t *)mappings) ; i++ ) {
stateval = 0; stateval = 0;
CATCH(errctx, get_json_array_index_object((json_t *)mappings, i, &curmapping)); CATCH(errctx, akgl_get_json_array_index_object((json_t *)mappings, i, &curmapping));
CATCH(errctx, get_json_string_value((json_t *)curmapping, "sprite", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)curmapping, "sprite", &tmpstr));
spriteptr = (sprite *)SDL_GetPointerProperty( spriteptr = (akgl_Sprite *)SDL_GetPointerProperty(
REGISTRY_SPRITE, AKGL_REGISTRY_SPRITE,
tmpstr->data, tmpstr->data,
NULL NULL
); );
CATCH(errctx, get_json_string_value((json_t *)json, "name", &tmpstr2)); CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &tmpstr2));
CATCH(errctx, get_json_array_value((json_t *)curmapping, "state", &statearray)); CATCH(errctx, akgl_get_json_array_value((json_t *)curmapping, "state", &statearray));
CATCH(errctx, character_load_json_state_int_from_strings(statearray, &stateval)); CATCH(errctx, akgl_character_load_json_state_int_from_strings(statearray, &stateval));
CATCH(errctx, get_json_string_value((json_t *)curmapping, "sprite", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)curmapping, "sprite", &tmpstr));
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
spriteptr, spriteptr,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Character %s for state %b references sprite %s but not found in the registry", "Character %s for state %b references sprite %s but not found in the registry",
tmpstr2->data, tmpstr2->data,
stateval, stateval,
tmpstr->data tmpstr->data
); );
CATCH(errctx, character_sprite_add((character *)obj, (sprite *)spriteptr, stateval)); CATCH(errctx, akgl_character_sprite_add((akgl_Character *)obj, (akgl_Sprite *)spriteptr, stateval));
} }
} CLEANUP { } CLEANUP {
if ( tmpstr != NULL ) { if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} }
if ( tmpstr2 != NULL ) { if ( tmpstr2 != NULL ) {
IGNORE(heap_release_string(tmpstr2)); IGNORE(akgl_heap_release_string(tmpstr2));
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *character_load_json(char *filename) akerr_ErrorContext *akgl_character_load_json(char *filename)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *json; json_t *json;
json_error_t error; json_error_t error;
character *obj = NULL; akgl_Character *obj = NULL;
//string *tmpstr = NULL; //akgl_String *tmpstr = NULL;
FAIL_ZERO_RETURN(errctx, filename, ERR_NULLPOINTER, "Received null filename"); FAIL_ZERO_RETURN(errctx, filename, AKERR_NULLPOINTER, "Received null filename");
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_character(&obj)); CATCH(errctx, akgl_heap_next_character(&obj));
//CATCH(errctx, heap_next_string(&tmpstr)); //CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, string_initialize(tmpstr, NULL)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
//SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename); //SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
json = (json_t *)json_load_file(filename, 0, &error); json = (json_t *)json_load_file(filename, 0, &error);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
json, json,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Error while loading character from %s on line %d: %s", filename, error.line, error.text "Error while loading character from %s on line %d: %s", filename, error.line, error.text
); );
CATCH(errctx, character_load_json_inner(json, obj)); CATCH(errctx, akgl_character_load_json_inner(json, obj));
CATCH(errctx, get_json_number_value(json, "velocity_x", &obj->vx)); CATCH(errctx, akgl_get_json_integer_value(json, "movementspeed", (int *)&obj->movementspeed));
CATCH(errctx, get_json_number_value(json, "velocity_y", &obj->vy)); 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));
} CLEANUP { } CLEANUP {
//IGNORE(heap_release_string(tmpstr)); //IGNORE(akgl_heap_release_string(tmpstr));
if ( errctx != NULL ) { if ( errctx != NULL ) {
if ( errctx->status != 0 ) { if ( errctx->status != 0 ) {
IGNORE(heap_release_character(obj)); IGNORE(akgl_heap_release_character(obj));
} }
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SDL_Log("Character %s loaded from %s", obj->name, filename);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }

View File

@@ -1,30 +1,47 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/controller.h> #include <akgl/controller.h>
SDL3GControlMap GAME_ControlMaps[MAX_CONTROL_MAPS]; akgl_ControlMap GAME_ControlMaps[AKGL_MAX_CONTROL_MAPS];
ErrorContext *controller_handle_event(void *appstate, SDL_Event *event) akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
{
int count;
SDL_KeyboardID *keyboards = SDL_GetKeyboards(&count);
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, keyboards, AKERR_NULLPOINTER, "%s", SDL_GetError());
for (int i = 0; i < count; i++) {
const char *name = SDL_GetKeyboardNameForID(keyboards[i]);
SDL_Log("Keyboard %d: ID %u, Name: %s\n", i, keyboards[i], name);
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *event)
{ {
int i = 0; int i = 0;
int j = 0; int j = 0;
int eventButtonComboMatch = 0; int eventButtonComboMatch = 0;
SDL3GControlMap *curmap = NULL; akgl_ControlMap *curmap = NULL;
SDL3GControl *curcontrol = NULL; akgl_Control *curcontrol = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
ATTEMPT { ATTEMPT {
for ( i = 0 ; i < MAX_CONTROL_MAPS; i++ ) { for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
curmap = &GAME_ControlMaps[i]; curmap = &GAME_ControlMaps[i];
if ( curmap->target == NULL ) { if ( curmap->target == NULL ) {
continue; continue;
} }
//SDL_Log("Control map %d maps to actor %s", i, curmap->target->name); //SDL_Log("Control map %d maps to actor %s", i, curmap->target->name);
//SDL_Log("event from keyboard %d", event->key.which); //SDL_Log("event from keyboard %d", event->key.which);
for ( j = 0; j < MAX_CONTROLS; j++ ) { for ( j = 0; j < AKGL_MAX_CONTROLS; j++ ) {
curcontrol = &curmap->controls[j]; curcontrol = &curmap->controls[j];
//SDL_Log("button/key is processed by controlmap %d control %d", i, j); //SDL_Log("button/key is processed by controlmap %d control %d", i, j);
//SDL_Log("event %d -> control on %d off %d", event->type, curcontrol->event_on, curcontrol->event_off); //SDL_Log("event %d -> control on %d off %d", event->type, curcontrol->event_on, curcontrol->event_off);
@@ -41,119 +58,119 @@ ErrorContext *controller_handle_event(void *appstate, SDL_Event *event)
); );
if ( event->type == curcontrol->event_on && eventButtonComboMatch) { if ( event->type == curcontrol->event_on && eventButtonComboMatch) {
CATCH(errctx, curcontrol->handler_on(curmap->target, event)); CATCH(errctx, curcontrol->handler_on(curmap->target, event));
goto _controller_handle_event_success; goto _akgl_controller_handle_event_success;
} else if ( event->type == curcontrol->event_off && eventButtonComboMatch ) { } else if ( event->type == curcontrol->event_off && eventButtonComboMatch ) {
CATCH(errctx, curcontrol->handler_off(curmap->target, event)); CATCH(errctx, curcontrol->handler_off(curmap->target, event));
goto _controller_handle_event_success; goto _akgl_controller_handle_event_success;
} }
} }
} }
_controller_handle_event_success: _akgl_controller_handle_event_success:
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *gamepad_handle_button_down(void *appstate, SDL_Event *event) akerr_ErrorContext *gamepad_handle_button_down(void *appstate, SDL_Event *event)
{ {
actor *player = NULL; akgl_Actor *player = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(REGISTRY_ACTOR, "player", NULL); player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "Player actor does not exist"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN || if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) { event->key.key == SDLK_DOWN ) {
SDL_Log("Processing dpad down : state %d", player->state); SDL_Log("Processing dpad down : state %d", player->state);
BITMASK_ADD(player->state, ACTOR_STATE_MOVING_DOWN); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_DOWN);
if ( !player->movement_controls_face ) { if ( !player->movement_controls_face ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_DOWN); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_DOWN);
} }
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_UP ) { event->key.key == SDLK_UP ) {
SDL_Log("Processing dpad up"); SDL_Log("Processing dpad up");
BITMASK_ADD(player->state, ACTOR_STATE_MOVING_UP); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_UP);
if ( !player->movement_controls_face ) { if ( !player->movement_controls_face ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_UP); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_UP);
} }
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_LEFT ) { event->key.key == SDLK_LEFT ) {
SDL_Log("Processing dpad left"); SDL_Log("Processing dpad left");
BITMASK_ADD(player->state, ACTOR_STATE_MOVING_LEFT); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_LEFT);
if ( !player->movement_controls_face ) { if ( !player->movement_controls_face ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_LEFT); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_LEFT);
} }
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_RIGHT ) { event->key.key == SDLK_RIGHT ) {
SDL_Log("Processing dpad right"); SDL_Log("Processing dpad right");
BITMASK_ADD(player->state, ACTOR_STATE_MOVING_RIGHT); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
if ( !player->movement_controls_face ) { if ( !player->movement_controls_face ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_RIGHT); AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_RIGHT);
} }
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *gamepad_handle_button_up(void *appstate, SDL_Event *event) akerr_ErrorContext *gamepad_handle_button_up(void *appstate, SDL_Event *event)
{ {
actor *player = NULL; akgl_Actor *player = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(REGISTRY_ACTOR, "player", NULL); player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "Player actor does not exist"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN || if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) { event->key.key == SDLK_DOWN ) {
SDL_Log("processing down release"); SDL_Log("processing down release");
BITMASK_DEL(player->state, ACTOR_STATE_MOVING_DOWN); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_DOWN);
player->curSpriteFrameId = 0; player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_UP ) { event->key.key == SDLK_UP ) {
SDL_Log("processing up release"); SDL_Log("processing up release");
BITMASK_DEL(player->state, ACTOR_STATE_MOVING_UP); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_UP);
player->curSpriteFrameId = 0; player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_RIGHT) { event->key.key == SDLK_RIGHT) {
SDL_Log("processing right release"); SDL_Log("processing right release");
BITMASK_DEL(player->state, ACTOR_STATE_MOVING_RIGHT); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
player->curSpriteFrameId = 0; player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT || } else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_LEFT ) { event->key.key == SDLK_LEFT ) {
SDL_Log("processing left release"); SDL_Log("processing left release");
BITMASK_DEL(player->state, ACTOR_STATE_MOVING_LEFT); AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_LEFT);
player->curSpriteFrameId = 0; player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state); SDL_Log("New state : %d", player->state);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event) akerr_ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
{ {
SDL_JoystickID which; SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL; SDL_Gamepad *gamepad = NULL;
char *mapping = NULL; char *mapping = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which; which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which); gamepad = SDL_GetGamepadFromID(which);
@@ -175,14 +192,14 @@ ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event) akerr_ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
{ {
SDL_JoystickID which; SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL; SDL_Gamepad *gamepad = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event"); FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which; which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which); gamepad = SDL_GetGamepadFromID(which);
@@ -193,3 +210,112 @@ ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
SDL_Log("Gamepad #%u removed", (unsigned int) which); SDL_Log("Gamepad #%u removed", (unsigned int) which);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akgl_Control *control)
{
int newmapid = 0;
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_RETURN(errctx, control, AKERR_NULLPOINTER, "NULL Control");
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
newmapid = GAME_ControlMaps[controlmapid].nextMap;
FAIL_ZERO_RETURN(errctx, (AKGL_MAX_CONTROLS - newmapid), AKERR_OUTOFBOUNDS, "Control map ID %d is full", controlmapid);
memcpy((void *)&GAME_ControlMaps[controlmapid].controls[newmapid], control, sizeof(akgl_Control));
GAME_ControlMaps[controlmapid].nextMap = newmapid + 1;
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid)
{
akgl_ControlMap *controlmap;
akgl_Control control;
PREPARE_ERROR(errctx);
ATTEMPT {
// set up the control map
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
memset((void *)&control, 0x00, sizeof(akgl_Control));
controlmap = &GAME_ControlMaps[controlmapid];
controlmap->kbid = kbid;
controlmap->jsid = jsid;
controlmap->target = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, actorname, NULL);
FAIL_ZERO_BREAK(errctx, controlmap->target, AKERR_REGISTRY, "Actor %s not found in registry", actorname);
// ---- KEYBOARD CONTROLS ----
// Move down
control.key = SDLK_DOWN;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_down_on;
control.handler_off = &akgl_Actor_cmhf_down_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move up
control.key = SDLK_UP;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_up_on;
control.handler_off = &akgl_Actor_cmhf_up_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move left
control.key = SDLK_LEFT;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_left_on;
control.handler_off = &akgl_Actor_cmhf_left_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move right
control.key = SDLK_RIGHT;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_right_on;
control.handler_off = &akgl_Actor_cmhf_right_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
control.key = 0;
// ----- GAMEPAD CONTROLS
// Move down
control.button = SDL_GAMEPAD_BUTTON_DPAD_DOWN;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_down_on;
control.handler_off = &akgl_Actor_cmhf_down_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move up
control.button = SDL_GAMEPAD_BUTTON_DPAD_UP;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_up_on;
control.handler_off = &akgl_Actor_cmhf_up_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move left
control.button = SDL_GAMEPAD_BUTTON_DPAD_LEFT;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_left_on;
control.handler_off = &akgl_Actor_cmhf_left_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move right
control.button = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_right_on;
control.handler_off = &akgl_Actor_cmhf_right_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
SUCCEED_RETURN(errctx);
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
}

View File

@@ -1,10 +1,10 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include <sdl3game/game.h> #include <akgl/game.h>
/* Draw a Gimpish background pattern to show transparency in the image */ /* Draw a Gimpish background pattern to show transparency in the image */
void GAME_draw_background(int w, int h) void akgl_draw_background(int w, int h)
{ {
SDL_Color col[2] = { SDL_Color col[2] = {
{ 0x66, 0x66, 0x66, 0xff }, { 0x66, 0x66, 0x66, 0xff },

View File

@@ -1,86 +1,406 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <stdio.h> #include <stdio.h>
#include <sdlerror.h> #include <akerror.h>
#include <semver.h>
#include <sdl3game/game.h> #include <akstdlib.h>
#include <sdl3game/controller.h> #include <akgl/game.h>
#include <sdl3game/tilemap.h> #include <akgl/controller.h>
#include <sdl3game/sprite.h> #include <akgl/tilemap.h>
#include <sdl3game/heap.h> #include <akgl/sprite.h>
#include <sdl3game/registry.h> #include <akgl/heap.h>
#include <sdl3game/staticstring.h> #include <akgl/registry.h>
#include <sdl3game/iterator.h> #include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/SDL_GameControllerDB.h>
SDL_Window *window = NULL; SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL; SDL_Renderer *renderer = NULL;
GAME_frame ball; akgl_Frame ball;
GAME_frame paddle1; akgl_Frame paddle1;
GAME_frame paddle2; akgl_Frame paddle2;
GAME_frame table; akgl_Frame table;
tilemap gamemap; akgl_Tilemap gamemap;
MIX_Audio *bgm = NULL; MIX_Audio *bgm = NULL;
MIX_Mixer *GAME_mixer = NULL; MIX_Mixer *akgl_mixer = NULL;
MIX_Track *GAME_tracks[64]; MIX_Track *akgl_tracks[AKGL_GAME_AUDIO_MAX_TRACKS];
SDL_FRect camera; SDL_FRect camera;
Game game; akgl_Game game;
ErrorContext ERROR_NOIGNORE *GAME_init() void akgl_game_lowfps(void)
{ {
SDL_Log("Low FPS! %d", game.fps);
return;
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
{
int screenwidth = 0;
int screenheight = 0;
int i = 0; int i = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.name), ERR_NULLPOINTER, "Must provide game name"); strncpy((char *)&game.libversion, AKGL_VERSION, 32);
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.version), ERR_NULLPOINTER, "Must provide game version"); game.gameStartTime = SDL_GetTicksNS();
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.uri), ERR_NULLPOINTER, "Must provide game uri"); game.lastIterTime = game.gameStartTime;
CATCH(errctx, heap_init()); game.lastFPSTime = game.gameStartTime;
CATCH(errctx, registry_init_actor()); game.lowfpsfunc = &akgl_game_lowfps;
CATCH(errctx, registry_init_sprite()); FAIL_ZERO_BREAK(errctx, strlen((char *)&game.name), AKERR_NULLPOINTER, "Must provide game name");
CATCH(errctx, registry_init_spritesheet()); FAIL_ZERO_BREAK(errctx, strlen((char *)&game.version), AKERR_NULLPOINTER, "Must provide game version");
CATCH(errctx, registry_init_character()); FAIL_ZERO_BREAK(errctx, strlen((char *)&game.uri), AKERR_NULLPOINTER, "Must provide game uri");
CATCH(errctx, registry_init_actor_state_strings()); 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());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true) } FINISH(errctx, true)
SDL_SetAppMetadata(game.name, game.version, game.uri); SDL_SetAppMetadata(game.name, game.version, game.uri);
for ( i = 0 ; i < MAX_CONTROL_MAPS; i++ ) { for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
memset(&GAME_ControlMaps[i], 0x00, sizeof(SDL3GControlMap)); memset(&GAME_ControlMaps[i], 0x00, sizeof(akgl_ControlMap));
} }
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, errctx,
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO), SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO),
ERR_SDL, AKGL_ERR_SDL,
"Couldn't initialize SDL: %s", "Couldn't initialize SDL: %s",
SDL_GetError()); SDL_GetError());
// Load the Game Controller DB
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());
}
}
SUCCEED_RETURN(errctx);
}
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( FAIL_ZERO_RETURN(
errctx, e,
SDL_CreateWindowAndRenderer(game.uri, game.screenwidth, game.screenheight, 0, &window, &renderer), SDL_CreateWindowAndRenderer(game.uri, screenwidth, screenheight, 0, &window, &renderer),
ERR_SDL, AKGL_ERR_SDL,
"Couldn't create window/renderer: %s", "Couldn't create window/renderer: %s",
SDL_GetError()); SDL_GetError());
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, e,
MIX_Init(), MIX_Init(),
ERR_SDL, AKGL_ERR_SDL,
"Couldn't initialize audio: %s", "Couldn't initialize audio: %s",
SDL_GetError()); SDL_GetError());
game.mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0); akgl_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0);
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, e,
game.mixer, akgl_mixer,
ERR_SDL, AKGL_ERR_SDL,
"Unable to create mixer device: %s", "Unable to create mixer device: %s",
SDL_GetError()); SDL_GetError());
FAIL_ZERO_RETURN(
e,
TTF_Init(),
AKGL_ERR_SDL,
"Couldn't initialize front engine: %s",
SDL_GetError());
camera.x = 0; camera.x = 0;
camera.y = 0; camera.y = 0;
camera.w = game.screenwidth; camera.w = screenwidth;
camera.h = game.screenheight; camera.h = screenheight;
SUCCEED(e);
}
void akgl_game_updateFPS()
{
SDL_Time curTime;
curTime = SDL_GetTicksNS();
if ( (curTime - game.lastFPSTime) > AKGL_TIME_ONESEC_NS ) {
game.fps = game.framesSinceUpdate;
game.framesSinceUpdate = 0;
game.lastFPSTime = curTime;
}
if ( game.fps < 30 ) {
game.lowfpsfunc();
}
game.framesSinceUpdate += 1;
game.lastIterTime = curTime;
}
/*
* entity name -> pointer map tables
*/
void akgl_game_save_actorname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_Actor *actor = NULL;
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, fp, AKERR_NULLPOINTER, "NULL file pointer");
CATCH(errctx, 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));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
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);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, 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));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
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);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, 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));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
void akgl_game_save_charactername_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
PREPARE_ERROR(errctx);
akgl_Character *character = NULL;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, 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));
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
{
PREPARE_ERROR(errctx);
char nullval = 0x00;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, 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));
// 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));
// 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));
// 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));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
{
FILE *fp = NULL;
PREPARE_ERROR(errctx);
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) {
} CLEANUP {
if ( fp != NULL )
fclose(fp);
} FINISH(errctx, true);
SUCCEED_RETURN(errctx); // 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)
{
void *ptr = NULL;
char ptrstring[32];
char objname[namelength];
int retval = 0;
PREPARE_ERROR(errctx);
while ( 1 ) {
CATCH(errctx, aksl_fread((void *)&objname, 1, namelength, fp));
CATCH(errctx, aksl_fread((void *)&ptr, 1, ptrlength, fp));
// End of the map
if ( ptr == 0x00 && objname[0] == 0x00 ) {
break;
}
// The map allows us to say "Object X has a reference to object Y at
// address Z. The object they had at address Z was named A. Our current
// instance of object named A is at address B. So we map address Z to
// address B, so that we can reconnect function pointers on objects loaded
// from the save game state."
// 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));
snprintf((char *)&ptrstring, 32, "%p", ptr);
SDL_SetPointerProperty(
map,
ptrstring,
SDL_GetPointerProperty(registry, objname, NULL));
};
SUCCEED_RETURN(errctx);
}
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");
ATTEMPT {
// Check save game library version
FAIL_NONZERO_BREAK(
errctx,
semver_parse((const char *)curversion, &current_version),
AKERR_VALUE,
"Invalid semantic %s version in current game: %s",
versiontype,
(char *)curversion);
FAIL_NONZERO_BREAK(
errctx,
semver_parse((const char *)newversion, &compare_version),
AKERR_VALUE,
"Invalid semantic %s version in save game: %s",
versiontype,
(char *)&newversion);
FAIL_ZERO_BREAK(
errctx,
semver_satisfies(compare_version, current_version, "="),
AKERR_API,
"Incompatible save game %s version (%s != %s)",
versiontype,
curversion,
(char *)&newversion);
} CLEANUP {
semver_free(&current_version);
semver_free(&compare_version);
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
{
akgl_Game savegame;
SDL_PropertiesID actormap;
SDL_PropertiesID spritemap;
SDL_PropertiesID spritesheetmap;
SDL_PropertiesID charactermap;
FILE *fp = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, 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));
FAIL_NONZERO_RETURN(
errctx,
strncmp((char *)&savegame.name, (char *)&game.name, 256),
AKERR_API,
"Savegame is not compatible with this game");
FAIL_NONZERO_RETURN(
errctx,
strncmp((char *)&savegame.uri, (char *)&game.uri, 256),
AKERR_API,
"Savegame is not compatible with this game");
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));
// 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));
// 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));
// 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));
// 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);
SUCCEED_RETURN(errctx);
} }

View File

@@ -1,191 +1,187 @@
#include <stdlib.h> #include <stdlib.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
#include <akgl/error.h>
actor HEAP_ACTOR[MAX_HEAP_ACTOR]; akgl_Actor HEAP_ACTOR[AKGL_MAX_HEAP_ACTOR];
sprite HEAP_SPRITE[MAX_HEAP_SPRITE]; akgl_Sprite HEAP_SPRITE[AKGL_MAX_HEAP_SPRITE];
spritesheet HEAP_SPRITESHEET[MAX_HEAP_SPRITESHEET]; akgl_SpriteSheet HEAP_SPRITESHEET[AKGL_MAX_HEAP_SPRITESHEET];
character HEAP_CHARACTER[MAX_HEAP_CHARACTER]; akgl_Character HEAP_CHARACTER[AKGL_MAX_HEAP_CHARACTER];
string HEAP_STRING[MAX_HEAP_STRING]; akgl_String HEAP_STRING[AKGL_MAX_HEAP_STRING];
ErrorContext *heap_init() akerr_ErrorContext *akgl_heap_init()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
int i = 0; int i = 0;
for ( i = 0; i < MAX_HEAP_ACTOR; i++) { akerr_name_for_status(AKGL_ERR_SDL, "SDL Error");
memset(&HEAP_ACTOR[i], 0x00, sizeof(actor)); for ( i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) {
memset(&HEAP_ACTOR[i], 0x00, sizeof(akgl_Actor));
} }
for ( i = 0; i < MAX_HEAP_SPRITE; i++) { for ( i = 0; i < AKGL_MAX_HEAP_SPRITE; i++) {
memset(&HEAP_SPRITE[i], 0x00, sizeof(sprite)); memset(&HEAP_SPRITE[i], 0x00, sizeof(akgl_Sprite));
} }
for ( i = 0; i < MAX_HEAP_SPRITESHEET; i++) { for ( i = 0; i < AKGL_MAX_HEAP_SPRITESHEET; i++) {
memset(&HEAP_SPRITESHEET[i], 0x00, sizeof(spritesheet)); memset(&HEAP_SPRITESHEET[i], 0x00, sizeof(akgl_SpriteSheet));
} }
for ( i = 0; i < MAX_HEAP_CHARACTER; i++) { for ( i = 0; i < AKGL_MAX_HEAP_CHARACTER; i++) {
memset(&HEAP_CHARACTER[i], 0x00, sizeof(character)); memset(&HEAP_CHARACTER[i], 0x00, sizeof(akgl_Character));
} }
for ( i = 0; i < MAX_HEAP_STRING; i++) { for ( i = 0; i < AKGL_MAX_HEAP_STRING; i++) {
memset(&HEAP_STRING[i], 0x00, sizeof(string)); memset(&HEAP_STRING[i], 0x00, sizeof(akgl_String));
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *heap_next_actor(actor **dest) akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_ACTOR; i++ ) { for (int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
if ( HEAP_ACTOR[i].refcount != 0 ) { if ( HEAP_ACTOR[i].refcount != 0 ) {
continue; continue;
} }
*dest = &HEAP_ACTOR[i]; *dest = &HEAP_ACTOR[i];
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused actor on the heap"); FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused actor on the heap");
} }
ErrorContext *heap_next_sprite(sprite **dest) akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_SPRITE; i++ ) { for (int i = 0; i < AKGL_MAX_HEAP_SPRITE; i++ ) {
if ( HEAP_SPRITE[i].refcount != 0 ) { if ( HEAP_SPRITE[i].refcount != 0 ) {
continue; continue;
} }
*dest = &HEAP_SPRITE[i]; *dest = &HEAP_SPRITE[i];
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused sprite on the heap"); FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused sprite on the heap");
} }
ErrorContext *heap_next_spritesheet(spritesheet **dest) akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_SPRITESHEET; i++ ) { for (int i = 0; i < AKGL_MAX_HEAP_SPRITESHEET; i++ ) {
if ( HEAP_SPRITESHEET[i].refcount != 0 ) { if ( HEAP_SPRITESHEET[i].refcount != 0 ) {
continue; continue;
} }
*dest = &HEAP_SPRITESHEET[i]; *dest = &HEAP_SPRITESHEET[i];
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused spritesheet on the heap"); FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused spritesheet on the heap");
} }
ErrorContext *heap_next_character(character **dest) akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_CHARACTER; i++ ) { for (int i = 0; i < AKGL_MAX_HEAP_CHARACTER; i++ ) {
if ( HEAP_CHARACTER[i].refcount != 0 ) { if ( HEAP_CHARACTER[i].refcount != 0 ) {
continue; continue;
} }
*dest = &HEAP_CHARACTER[i]; *dest = &HEAP_CHARACTER[i];
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused character on the heap"); FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused character on the heap");
} }
ErrorContext *heap_next_string(string **dest) akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_STRING; i++ ) { for (int i = 0; i < AKGL_MAX_HEAP_STRING; i++ ) {
if ( HEAP_STRING[i].refcount != 0 ) { if ( HEAP_STRING[i].refcount != 0 ) {
continue; continue;
} }
*dest = &HEAP_STRING[i]; *dest = &HEAP_STRING[i];
HEAP_STRING[i].refcount += 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused string on the heap"); FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused string on the heap");
} }
ErrorContext *heap_release_actor(actor *ptr) akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr)
{ {
int i = 0; int i = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "NULL actor reference"); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL actor reference");
if ( ptr->refcount > 0 ) { if ( ptr->refcount > 0 ) {
ptr->refcount -= 1; ptr->refcount -= 1;
} }
if ( ptr->refcount == 0 ) { if ( ptr->refcount == 0 ) {
for ( i = 0; i < ACTOR_MAX_CHILDREN; i++ ) { for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN; i++ ) {
if ( ptr->children[i] != NULL ) { if ( ptr->children[i] != NULL ) {
CATCH_AND_RETURN(errctx, heap_release_actor(ptr->children[i])); CATCH_AND_RETURN(errctx, akgl_heap_release_actor(ptr->children[i]));
} }
} }
if ( ptr->basechar != NULL ) { SDL_ClearProperty(AKGL_REGISTRY_ACTOR, (char *)&ptr->name);
CATCH_AND_RETURN(errctx, heap_release_character(ptr->basechar)); memset(ptr, 0x00, sizeof(akgl_Actor));
}
memset(ptr, 0x00, sizeof(actor));
SDL_ClearProperty(REGISTRY_ACTOR, (char *)&ptr->name);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *heap_release_character(character *basechar) akerr_ErrorContext *akgl_heap_release_character(akgl_Character *basechar)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
iterator opflags; akgl_Iterator opflags;
FAIL_ZERO_RETURN(errctx, basechar, ERR_NULLPOINTER, "NULL character reference"); FAIL_ZERO_RETURN(errctx, basechar, AKERR_NULLPOINTER, "NULL character reference");
BITMASK_CLEAR(opflags.flags); AKGL_BITMASK_CLEAR(opflags.flags);
BITMASK_ADD(opflags.flags, ITERATOR_OP_RELEASE);
if ( basechar->refcount > 0 ) { if ( basechar->refcount > 0 ) {
basechar->refcount -= 1; basechar->refcount -= 1;
} }
if ( basechar->refcount == 0 ) { if ( basechar->refcount == 0 ) {
SDL_EnumerateProperties(basechar->state_sprites, &character_state_sprites_iterate, (void *)&opflags); SDL_ClearProperty(AKGL_REGISTRY_CHARACTER, (char *)&basechar->name);
SDL_ClearProperty(REGISTRY_CHARACTER, (char *)&basechar->name); memset(basechar, 0x00, sizeof(akgl_Character));
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *heap_release_sprite(sprite *ptr) akerr_ErrorContext *akgl_heap_release_sprite(akgl_Sprite *ptr)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "Received NULL sprite reference"); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL sprite reference");
if ( ptr->refcount > 0 ) { if ( ptr->refcount > 0 ) {
ptr->refcount -= 1; ptr->refcount -= 1;
} }
if ( ptr->refcount == 0 ) { if ( ptr->refcount == 0 ) {
ATTEMPT { SDL_ClearProperty(AKGL_REGISTRY_SPRITE, (char *)&ptr->name);
CATCH(errctx, heap_release_spritesheet(ptr->sheet)); memset(ptr, 0x00, sizeof(akgl_Sprite));
} CLEANUP {
SDL_ClearProperty(REGISTRY_SPRITE, (char *)&ptr->name);
} PROCESS(errctx) {
} FINISH(errctx, true);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *heap_release_spritesheet(spritesheet *ptr) akerr_ErrorContext *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "Received NULL spritesheet reference"); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL spritesheet reference");
if ( ptr->refcount > 0 ) { if ( ptr->refcount > 0 ) {
ptr->refcount -= 1; ptr->refcount -= 1;
} }
if ( ptr->refcount == 0 ) { if ( ptr->refcount == 0 ) {
// TODO : If we go threaded, make sure this is only happening on the main thread // TODO : If we go threaded, make sure this is only happening on the main thread
SDL_ClearProperty(REGISTRY_SPRITESHEET, (char *)&ptr->name); SDL_ClearProperty(AKGL_REGISTRY_SPRITESHEET, (char *)&ptr->name);
if ( ptr-> texture != NULL ) if ( ptr-> texture != NULL )
SDL_DestroyTexture(ptr->texture); SDL_DestroyTexture(ptr->texture);
ptr->texture = NULL; ptr->texture = NULL;
memset(ptr, 0x00, sizeof(akgl_SpriteSheet));
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *heap_release_string(string *ptr) akerr_ErrorContext *akgl_heap_release_string(akgl_String *ptr)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "Received NULL string reference"); FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL string reference");
if ( ptr->refcount > 0 ) { if ( ptr->refcount > 0 ) {
ptr->refcount -= 1; ptr->refcount -= 1;
} }
if ( ptr->refcount == 0 ) { if ( ptr->refcount == 0 ) {
memset(&ptr->data, 0x00, MAX_STRING_LENGTH); memset(&ptr->data, 0x00, AKGL_MAX_STRING_LENGTH);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }

View File

@@ -1,131 +1,131 @@
#include <jansson.h> #include <jansson.h>
#include <string.h> #include <string.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/json_helpers.h> #include <akgl/json_helpers.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
ErrorContext *get_json_object_value(json_t *obj, char *key, json_t **dest) akerr_ErrorContext *akgl_get_json_object_value(json_t *obj, char *key, json_t **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL object pointer"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
json_t *value = json_object_get(obj, key); json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = value; *dest = value;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_boolean_value(json_t *obj, char *key, bool *dest) akerr_ErrorContext *akgl_get_json_boolean_value(json_t *obj, char *key, bool *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL object pointer"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
json_t *value = json_object_get(obj, key); json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_boolean(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_boolean(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_boolean_value(value); *dest = json_boolean_value(value);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_integer_value(json_t *obj, char *key, int *dest) akerr_ErrorContext *akgl_get_json_integer_value(json_t *obj, char *key, int *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL object pointer"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
json_t *value = json_object_get(obj, key); json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_integer_value(value); *dest = json_integer_value(value);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_number_value(json_t *obj, char *key, float *dest) akerr_ErrorContext *akgl_get_json_number_value(json_t *obj, char *key, float *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
json_t *value = json_object_get(obj, key); json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_number(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_number(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_number_value(value); *dest = json_number_value(value);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_string_value(json_t *obj, char *key, string **dest) akerr_ErrorContext *akgl_get_json_string_value(json_t *obj, char *key, akgl_String **dest)
{ {
json_t *value = NULL; json_t *value = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, key, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL pointer reference");
value = json_object_get(obj, key); value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_string(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_string(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
ATTEMPT { ATTEMPT {
if ( *dest == NULL ) { if ( *dest == NULL ) {
CATCH(errctx, heap_next_string(dest)); CATCH(errctx, akgl_heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL)); CATCH(errctx, akgl_string_initialize(*dest, NULL));
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, false); } FINISH(errctx, false);
strncpy((char *)&(*dest)->data, json_string_value(value), MAX_STRING_LENGTH); strncpy((char *)&(*dest)->data, json_string_value(value), AKGL_MAX_STRING_LENGTH);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_array_value(json_t *obj, char *key, json_t **dest) akerr_ErrorContext *akgl_get_json_array_value(json_t *obj, char *key, json_t **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
json_t *value = json_object_get(obj, key); json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_KEY, "Key %s not found in object", key); FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_array(value)), ERR_TYPE, "Key %s in object has incorrect type", key); FAIL_ZERO_RETURN(errctx, (json_is_array(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = value; *dest = value;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_array_index_object(json_t *array, int index, json_t **dest) akerr_ErrorContext *akgl_get_json_array_index_object(json_t *array, int index, json_t **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, array, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
json_t *value = json_array_get(array, index); json_t *value = json_array_get(array, index);
FAIL_ZERO_RETURN(errctx, value, ERR_OUTOFBOUNDS, "Index %d out of bounds for array", index); FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), ERR_TYPE, "Index %d in object has incorrect type", index); FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
*dest = value; *dest = value;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_array_index_integer(json_t *array, int index, int *dest) akerr_ErrorContext *akgl_get_json_array_index_integer(json_t *array, int index, int *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, array, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
json_t *value = json_array_get(array, index); json_t *value = json_array_get(array, index);
FAIL_ZERO_RETURN(errctx, value, ERR_OUTOFBOUNDS, "Index %d out of bounds for array", index); FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), ERR_TYPE, "Index %d in object has incorrect type", index); FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
*dest = json_integer_value(value); *dest = json_integer_value(value);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_array_index_string(json_t *array, int index, string **dest) akerr_ErrorContext *akgl_get_json_array_index_string(json_t *array, int index, akgl_String **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, array, ERR_NULLPOINTER, "NULL pointer reference"); FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination pointer reference"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer reference");
json_t *value = json_array_get(array, index); json_t *value = json_array_get(array, index);
FAIL_ZERO_RETURN(errctx, value, ERR_OUTOFBOUNDS, "Index %d out of bounds for array", index); FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_string(value)), ERR_TYPE, "Index %d in object has incorrect type", index); FAIL_ZERO_RETURN(errctx, (json_is_string(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
ATTEMPT { ATTEMPT {
if ( *dest == NULL ) { if ( *dest == NULL ) {
CATCH(errctx, heap_next_string(dest)); CATCH(errctx, akgl_heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL)); CATCH(errctx, akgl_string_initialize(*dest, NULL));
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, false); } FINISH(errctx, false);
strncpy((char *)&(*dest)->data, json_string_value(value), MAX_STRING_LENGTH); strncpy((char *)&(*dest)->data, json_string_value(value), AKGL_MAX_STRING_LENGTH);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }

View File

@@ -1,77 +1,184 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <sdlerror.h> #include <akerror.h>
#include <jansson.h>
#include <sdl3game/sprite.h> #include <akstdlib.h>
#include <sdl3game/registry.h> #include <akgl/heap.h>
#include <sdl3game/iterator.h> #include <akgl/sprite.h>
#include <sdl3game/actor.h> #include <akgl/registry.h>
#include <akgl/iterator.h>
#include <akgl/actor.h>
#include <akgl/json_helpers.h>
SDL_PropertiesID REGISTRY_ACTOR; SDL_PropertiesID AKGL_REGISTRY_ACTOR;
SDL_PropertiesID REGISTRY_ACTOR_STATE_STRINGS; SDL_PropertiesID AKGL_REGISTRY_ACTOR_STATE_STRINGS;
SDL_PropertiesID REGISTRY_SPRITE; SDL_PropertiesID AKGL_REGISTRY_SPRITE;
SDL_PropertiesID REGISTRY_SPRITESHEET; SDL_PropertiesID AKGL_REGISTRY_SPRITESHEET;
SDL_PropertiesID REGISTRY_CHARACTER; SDL_PropertiesID AKGL_REGISTRY_CHARACTER;
SDL_PropertiesID AKGL_REGISTRY_MUSIC;
SDL_PropertiesID AKGL_REGISTRY_FONT;
SDL_PropertiesID AKGL_REGISTRY_PROPERTIES;
ErrorContext *registry_init() akerr_ErrorContext *akgl_registry_init()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, registry_init_spritesheet()); CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, registry_init_sprite()); CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, registry_init_character()); CATCH(errctx, akgl_registry_init_character());
CATCH(errctx, registry_init_actor()); CATCH(errctx, akgl_registry_init_actor());
CATCH(errctx, registry_init_actor_state_strings()); CATCH(errctx, akgl_registry_init_actor_state_strings());
CATCH(errctx, akgl_registry_init_font());
CATCH(errctx, akgl_registry_init_music());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *registry_init_actor() akerr_ErrorContext *akgl_registry_init_actor()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
REGISTRY_ACTOR = SDL_CreateProperties(); AKGL_REGISTRY_ACTOR = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_ACTOR, ERR_NULLPOINTER, "Error initializing actor registry"); FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_ACTOR, AKERR_NULLPOINTER, "Error initializing actor registry");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *registry_init_actor_state_strings() akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_font()
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_FONT = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_FONT, AKERR_NULLPOINTER, "Error initializing font registry");
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_music()
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_MUSIC = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_MUSIC, AKERR_NULLPOINTER, "Error initializing music registry");
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_properties()
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_PROPERTIES = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_PROPERTIES, AKERR_NULLPOINTER, "Error initializing properties registry");
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_actor_state_strings()
{ {
int i = 0; int i = 0;
int flag = 0; int flag = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
REGISTRY_ACTOR_STATE_STRINGS = SDL_CreateProperties(); AKGL_REGISTRY_ACTOR_STATE_STRINGS = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_ACTOR_STATE_STRINGS, ERR_NULLPOINTER, "Error initializing actor state strings registry"); FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_ACTOR_STATE_STRINGS, AKERR_NULLPOINTER, "Error initializing actor state strings registry");
for ( i = 0 ; i < ACTOR_MAX_STATES; i++ ) { for ( i = 0 ; i < AKGL_ACTOR_MAX_STATES; i++ ) {
flag = (1 << i); flag = (1 << i);
SDL_SetNumberProperty( SDL_SetNumberProperty(
REGISTRY_ACTOR_STATE_STRINGS, AKGL_REGISTRY_ACTOR_STATE_STRINGS,
ACTOR_STATE_STRING_NAMES[i], AKGL_ACTOR_STATE_STRING_NAMES[i],
flag); flag);
} }
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *registry_init_sprite() akerr_ErrorContext *akgl_registry_init_sprite()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
REGISTRY_SPRITE = SDL_CreateProperties(); AKGL_REGISTRY_SPRITE = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_SPRITE, ERR_NULLPOINTER, "Error initializing sprite registry"); FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_SPRITE, AKERR_NULLPOINTER, "Error initializing sprite registry");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *registry_init_spritesheet() akerr_ErrorContext *akgl_registry_init_spritesheet()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
REGISTRY_SPRITESHEET = SDL_CreateProperties(); AKGL_REGISTRY_SPRITESHEET = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_SPRITESHEET, ERR_NULLPOINTER, "Error initializing spritesheet registry"); FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_SPRITESHEET, AKERR_NULLPOINTER, "Error initializing spritesheet registry");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *registry_init_character() akerr_ErrorContext *akgl_registry_init_character()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
REGISTRY_CHARACTER = SDL_CreateProperties(); AKGL_REGISTRY_CHARACTER = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_CHARACTER, ERR_NULLPOINTER, "Error initializing character registry"); FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_CHARACTER, AKERR_NULLPOINTER, "Error initializing character registry");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_load_properties(char *fname)
{
json_t *json = NULL;
json_t *props = NULL;
const char *pkey = NULL;
json_t *pvalue = NULL;
json_error_t error;
akgl_String *tmpstr;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, fname, AKERR_NULLPOINTER, "null filename");
ATTEMPT {
SDL_Log("Loading from %s", fname);
json = json_load_file(fname, 0, &error);
FAIL_ZERO_BREAK(
errctx,
json,
AKERR_NULLPOINTER,
"Error while loading properties from %s on line %d: %s-",
fname,
error.line,
error.text);
CATCH(errctx, akgl_get_json_object_value(json, "properties", &props));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
json_object_foreach(props, pkey, pvalue) {
ATTEMPT {
CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, akgl_get_json_string_value(props, (char *)pkey, &tmpstr));
SDL_SetStringProperty(AKGL_REGISTRY_PROPERTIES, pkey, tmpstr->data);
SDL_Log("Set property %s = %s", pkey, tmpstr->data);
CATCH(errctx, akgl_heap_release_string(tmpstr));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
}
SDL_Log("Properties loaded");
SUCCEED(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_set_property(char *name, char *src)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, name, AKERR_NULLPOINTER, "NULL char *");
FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "NULL char *");
SDL_SetStringProperty(AKGL_REGISTRY_PROPERTIES, name, src);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_property(char *name, akgl_String **dest, char *def)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, name, AKERR_NULLPOINTER, "NULL char *");
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "NULL akgl_String *");
ATTEMPT {
if ( *dest == NULL ) {
CATCH(e, akgl_heap_next_string(dest));
}
CATCH(e,
aksl_memcpy(
(*dest)->data,
(void *)SDL_GetStringProperty(AKGL_REGISTRY_PROPERTIES, (const char *)name, (const char *)def),
AKGL_MAX_STRING_LENGTH)
);
} CLEANUP {
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}

View File

@@ -2,171 +2,193 @@
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <string.h> #include <string.h>
#include <jansson.h> #include <jansson.h>
#include <sdlerror.h> #include <akerror.h>
#include <libgen.h> #include <libgen.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/json_helpers.h> #include <akgl/json_helpers.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
static ErrorContext *sprite_load_json_spritesheet(json_t *json, spritesheet **sheet, char *relative_path) akerr_ErrorContext *akgl_sprite_sheet_coords_for_frame(akgl_Sprite *self, SDL_FRect *srccoords, uint8_t frameid)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "NULL sprite");
FAIL_ZERO_RETURN(e, srccoords, AKERR_NULLPOINTER, "NULL SDL_Rect");
FAIL_ZERO_RETURN(e, self->sheet, AKERR_NULLPOINTER, "NULL spritesheet");
srccoords->x = self->width * self->frameids[frameid];
if ( srccoords->x >= self->sheet->texture->w ) {
srccoords->y = ((int)srccoords->x / self->sheet->texture->w) * self->height;
srccoords->x = ((int)srccoords->x % self->sheet->texture->w);
} else {
srccoords->y = 0;
}
srccoords->w = self->width;
srccoords->h = self->height;
SUCCEED_RETURN(e);
}
static akerr_ErrorContext *akgl_sprite_load_json_spritesheet(json_t *json, akgl_SpriteSheet **sheet, char *relative_path)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *spritesheet_json = NULL; json_t *spritesheet_json = NULL;
int ss_frame_width = 0; int ss_frame_width = 0;
int ss_frame_height = 0; int ss_frame_height = 0;
string *ss_filename = NULL; akgl_String *ss_filename = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, string_initialize(tmpstr, NULL)); CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, get_json_object_value((json_t *)json, "spritesheet", &spritesheet_json)); CATCH(errctx, akgl_get_json_object_value((json_t *)json, "spritesheet", &spritesheet_json));
CATCH(errctx, get_json_string_value((json_t *)spritesheet_json, "filename", &ss_filename)); CATCH(errctx, akgl_get_json_string_value((json_t *)spritesheet_json, "filename", &ss_filename));
if ( ss_filename->data[0] != '/' ) { if ( ss_filename->data[0] != '/' ) {
SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s/%s", relative_path, ss_filename->data); SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s/%s", relative_path, ss_filename->data);
} else { } else {
SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s", ss_filename->data); SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s", ss_filename->data);
} }
*sheet = SDL_GetPointerProperty( *sheet = SDL_GetPointerProperty(
REGISTRY_SPRITESHEET, AKGL_REGISTRY_SPRITESHEET,
(char *)&tmpstr->data, (char *)&tmpstr->data,
NULL NULL
); );
if ( *sheet == NULL ) { if ( *sheet == NULL ) {
CATCH(errctx, heap_next_spritesheet(sheet)); CATCH(errctx, akgl_heap_next_spritesheet(sheet));
CATCH(errctx, get_json_integer_value((json_t *)spritesheet_json, "frame_width", &ss_frame_width)); CATCH(errctx, akgl_get_json_integer_value((json_t *)spritesheet_json, "frame_width", &ss_frame_width));
CATCH(errctx, get_json_integer_value((json_t *)spritesheet_json, "frame_height", &ss_frame_width)); CATCH(errctx, akgl_get_json_integer_value((json_t *)spritesheet_json, "frame_height", &ss_frame_width));
CATCH(errctx, CATCH(errctx,
spritesheet_initialize( akgl_spritesheet_initialize(
(spritesheet *)*sheet, (akgl_SpriteSheet *)*sheet,
ss_frame_width, ss_frame_width,
ss_frame_height, ss_frame_height,
(char *)&tmpstr->data) (char *)&tmpstr->data)
); );
} }
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(ss_filename)); IGNORE(akgl_heap_release_string(ss_filename));
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *sprite_load_json(char *filename) akerr_ErrorContext *akgl_sprite_load_json(char *filename)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *json = NULL; json_t *json = NULL;
json_t *frames = NULL; json_t *frames = NULL;
json_error_t error; json_error_t error;
sprite *obj = NULL; akgl_Sprite *obj = NULL;
spritesheet *sheet = NULL; akgl_SpriteSheet *sheet = NULL;
string *spritename = NULL; akgl_String *spritename = NULL;
//string *tmpstr = NULL; //string *tmpstr = NULL;
int i = 0; int i = 0;
FAIL_ZERO_RETURN(errctx, filename, ERR_NULLPOINTER, "Received null filename"); FAIL_ZERO_RETURN(errctx, filename, AKERR_NULLPOINTER, "Received null filename");
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_sprite(&obj)); CATCH(errctx, akgl_heap_next_sprite(&obj));
//CATCH(errctx, heap_next_string(&tmpstr)); //CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, string_initialize(tmpstr, NULL)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, heap_next_string(&spritename)); CATCH(errctx, akgl_heap_next_string(&spritename));
CATCH(errctx, string_initialize(spritename, NULL)); CATCH(errctx, akgl_string_initialize(spritename, NULL));
//SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename); //SDL_snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
json = (json_t *)json_load_file(filename, 0, &error); json = (json_t *)json_load_file(filename, 0, &error);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
json, json,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Error while loading sprite from %s on line %d: %s", filename, error.line, error.text "Error while loading sprite from %s on line %d: %s", filename, error.line, error.text
); );
CATCH(errctx, sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename))); CATCH(errctx, akgl_sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename)));
CATCH(errctx, get_json_string_value((json_t *)json, "name", &spritename)); CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &spritename));
CATCH(errctx, CATCH(errctx,
sprite_initialize( akgl_sprite_initialize(
(sprite *)obj, (akgl_Sprite *)obj,
spritename->data, spritename->data,
(spritesheet *)sheet) (akgl_SpriteSheet *)sheet)
); );
CATCH(errctx, get_json_integer_value((json_t *)json, "width", &obj->width)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "width", &obj->width));
CATCH(errctx, get_json_integer_value((json_t *)json, "height", &obj->height)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "height", &obj->height));
CATCH(errctx, get_json_integer_value((json_t *)json, "speed", &obj->speed)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "speed", &obj->speed));
CATCH(errctx, get_json_boolean_value((json_t *)json, "loop", &obj->loop)); obj->speed = obj->speed * AKGL_TIME_ONESEC_MS;
CATCH(errctx, get_json_boolean_value((json_t *)json, "loopReverse", &obj->loopReverse)); CATCH(errctx, akgl_get_json_boolean_value((json_t *)json, "loop", &obj->loop));
CATCH(errctx, akgl_get_json_boolean_value((json_t *)json, "loopReverse", &obj->loopReverse));
CATCH(errctx, get_json_array_value((json_t *)json, "frames", &frames)); CATCH(errctx, akgl_get_json_array_value((json_t *)json, "frames", &frames));
obj->frames = json_array_size((json_t *)frames); obj->frames = json_array_size((json_t *)frames);
for ( i = 0 ; i < obj->frames; i++ ) { for ( i = 0 ; i < obj->frames; i++ ) {
CATCH(errctx, get_json_array_index_integer((json_t *)frames, i, &obj->frameids[i])); CATCH(errctx, akgl_get_json_array_index_integer((json_t *)frames, i, (uint32_t *)&obj->frameids[i]));
} }
} CLEANUP { } CLEANUP {
if ( errctx != NULL && errctx->status != 0 ) { if ( errctx != NULL && errctx->status != 0 ) {
IGNORE(heap_release_sprite(obj)); IGNORE(akgl_heap_release_sprite(obj));
IGNORE(heap_release_spritesheet(sheet)); IGNORE(akgl_heap_release_spritesheet(sheet));
} }
IGNORE(heap_release_string(spritename)); IGNORE(akgl_heap_release_string(spritename));
//IGNORE(heap_release_string(tmpstr)); //IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *sprite_initialize(sprite *spr, char *name, spritesheet *sheet) akerr_ErrorContext *akgl_sprite_initialize(akgl_Sprite *spr, char *name, akgl_SpriteSheet *sheet)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, spr, ERR_NULLPOINTER, "Null sprite reference"); FAIL_ZERO_RETURN(errctx, spr, AKERR_NULLPOINTER, "Null sprite reference");
FAIL_ZERO_RETURN(errctx, name, ERR_NULLPOINTER, "Empty sprite name"); FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "Empty sprite name");
FAIL_ZERO_RETURN(errctx, sheet, ERR_NULLPOINTER, "Null spritesheet reference"); FAIL_ZERO_RETURN(errctx, sheet, AKERR_NULLPOINTER, "Null spritesheet reference");
memset(spr, 0x00, sizeof(sprite)); memset(spr, 0x00, sizeof(akgl_Sprite));
memcpy(spr->name, name, SPRITE_MAX_NAME_LENGTH); memcpy(spr->name, name, AKGL_SPRITE_MAX_NAME_LENGTH);
spr->sheet = sheet; spr->sheet = sheet;
FAIL_ZERO_RETURN( FAIL_ZERO_RETURN(
errctx, errctx,
SDL_SetPointerProperty(REGISTRY_SPRITE, (char *)&spr->name, (void *)spr), SDL_SetPointerProperty(AKGL_REGISTRY_SPRITE, (char *)&spr->name, (void *)spr),
ERR_KEY, AKERR_KEY,
"Unable to add sprite to registry"); "Unable to add sprite to registry");
spr->refcount += 1; spr->refcount += 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *spritesheet_initialize(spritesheet *sheet, int sprite_w, int sprite_h, char *filename) akerr_ErrorContext *akgl_spritesheet_initialize(akgl_SpriteSheet *sheet, int sprite_w, int sprite_h, char *filename)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
//string *tmpstr = NULL; //akgl_String *tmpstr = NULL;
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, sheet, ERR_NULLPOINTER, "Null spritesheet pointer"); FAIL_ZERO_BREAK(errctx, sheet, AKERR_NULLPOINTER, "Null spritesheet pointer");
FAIL_ZERO_BREAK(errctx, filename, ERR_NULLPOINTER, "Null filename pointer"); FAIL_ZERO_BREAK(errctx, filename, AKERR_NULLPOINTER, "Null filename pointer");
memset(sheet, 0x00, sizeof(spritesheet)); memset(sheet, 0x00, sizeof(akgl_SpriteSheet));
//CATCH(errctx, heap_next_string(&tmpstr)); //CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, string_initialize(tmpstr, NULL)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
strncpy((char *)&sheet->name, filename, SPRITE_SHEET_MAX_FILENAME_LENGTH); strncpy((char *)&sheet->name, filename, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH);
//snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename); //snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
sheet->texture = IMG_LoadTexture(renderer, filename); sheet->texture = IMG_LoadTexture(renderer, filename);
FAIL_ZERO_BREAK(errctx, sheet->texture, ERR_SDL, "Failed loading asset %s : %s", filename, SDL_GetError()); FAIL_ZERO_BREAK(errctx, sheet->texture, AKGL_ERR_SDL, "Failed loading asset %s : %s", filename, SDL_GetError());
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
SDL_SetPointerProperty(REGISTRY_SPRITESHEET, (char *)sheet->name, (void *)sheet), SDL_SetPointerProperty(AKGL_REGISTRY_SPRITESHEET, (char *)sheet->name, (void *)sheet),
ERR_KEY, AKERR_KEY,
"Unable to add spritesheet to registry: %s", "Unable to add spritesheet to registry: %s",
SDL_GetError()); SDL_GetError());
sheet->refcount += 1; sheet->refcount += 1;
} CLEANUP { } CLEANUP {
//IGNORE(heap_release_string(tmpstr)); //IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);

View File

@@ -1,14 +1,14 @@
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
ErrorContext *string_initialize(string *obj, char *init) akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Attempted to initialize NULL string reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Attempted to initialize NULL string reference");
if ( init != NULL ) { if ( init != NULL ) {
strncpy((char *)&obj->data, init, MAX_STRING_LENGTH); strncpy((char *)&obj->data, init, AKGL_MAX_STRING_LENGTH);
} else { } else {
memset(&obj->data, 0x00, sizeof(string)); memset(&obj->data, 0x00, sizeof(akgl_String));
} }
obj->refcount = 1; obj->refcount = 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);

61
src/text.c Normal file
View File

@@ -0,0 +1,61 @@
#include <akerror.h>
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <akgl/text.h>
#include <akgl/registry.h>
#include <akgl/game.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath, int size)
{
TTF_Font *font = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "Null font name");
FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "Null filepath");
font = TTF_OpenFont(filepath, size);
FAIL_ZERO_RETURN(errctx, font, AKGL_ERR_SDL, "%s", SDL_GetError());
FAIL_ZERO_RETURN(
errctx,
SDL_SetPointerProperty(AKGL_REGISTRY_FONT, name, (void *)font),
AKERR_KEY,
"Unable to add font %p to registry as %s : %s",
(void *)font,
name,
SDL_GetError());
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y)
{
SDL_Surface *textsurf = NULL;
SDL_Texture *texture = NULL;
SDL_FRect dest;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, font, AKERR_NULLPOINTER, "NULL font");
FAIL_ZERO_RETURN(errctx, text, AKERR_NULLPOINTER, "NULL text string");
if ( wraplength > 0 ) {
textsurf = TTF_RenderText_Blended_Wrapped(
font,
text,
0,
color,
wraplength);
} else {
textsurf = TTF_RenderText_Blended(
font,
text,
0,
color);
}
FAIL_ZERO_RETURN(errctx, textsurf, AKERR_NULLPOINTER, "%s", SDL_GetError());
texture = SDL_CreateTextureFromSurface(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());
SDL_DestroyTexture(texture);
SDL_DestroySurface(textsurf);
SUCCEED_RETURN(errctx);
}

View File

@@ -3,64 +3,64 @@
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include <string.h> #include <string.h>
#include <jansson.h> #include <jansson.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/tilemap.h> #include <akgl/tilemap.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/json_helpers.h> #include <akgl/json_helpers.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
#include <sdl3game/game.h> #include <akgl/game.h>
ErrorContext *get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest) akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *properties = NULL; json_t *properties = NULL;
json_t *property = NULL; json_t *property = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
int i = 0; int i = 0;
// This is not a generic JSON helper. It assumes we are receiving an object with a 'properties' key // 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. // inside of it. That key is an array of objects, and each object has a name, type, and value.
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL json obj reference"); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL json obj reference");
FAIL_ZERO_RETURN(errctx, key, ERR_NULLPOINTER, "NULL key string"); FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_array_value(obj, "properties", &properties)); CATCH(errctx, akgl_get_json_array_value(obj, "properties", &properties));
for (i = 0; i < json_array_size(properties); i++) { for (i = 0; i < json_array_size(properties); i++) {
CATCH(errctx, get_json_array_index_object(properties, i, &property)); CATCH(errctx, akgl_get_json_array_index_object(properties, i, &property));
CATCH(errctx, get_json_string_value(property, "name", &tmpstr)); CATCH(errctx, akgl_get_json_string_value(property, "name", &tmpstr));
if ( strcmp(tmpstr->data, key) != 0 ) { if ( strcmp(tmpstr->data, key) != 0 ) {
CATCH(errctx, heap_release_string(tmpstr)); CATCH(errctx, akgl_heap_release_string(tmpstr));
continue; continue;
} }
CATCH(errctx, get_json_string_value(property, "type", &tmpstr)); CATCH(errctx, akgl_get_json_string_value(property, "type", &tmpstr));
if ( strcmp(tmpstr->data, type) != 0 ) { if ( strcmp(tmpstr->data, type) != 0 ) {
FAIL_BREAK(errctx, ERR_TYPE, "Character property is present but is incorrect type"); FAIL_BREAK(errctx, AKERR_TYPE, "Object property is present but is incorrect type");
} }
*dest = property; *dest = property;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
} CLEANUP { } CLEANUP {
if ( tmpstr != NULL ) { if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
FAIL_RETURN(errctx, ERR_KEY, "Property not found in properties map"); FAIL_RETURN(errctx, AKERR_KEY, "Property not found in properties map");
} }
ErrorContext *get_json_properties_string(json_t *obj, char *key, string **dest) akerr_ErrorContext *akgl_get_json_properties_string(json_t *obj, char *key, akgl_String **dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *property; json_t *property;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_tilemap_property(obj, key, "string", &property)); CATCH(errctx, akgl_get_json_tilemap_property(obj, key, "string", &property));
CATCH(errctx, heap_next_string(dest)); CATCH(errctx, akgl_heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL)); CATCH(errctx, akgl_string_initialize(*dest, NULL));
CATCH(errctx, get_json_string_value(property, "value", dest)); CATCH(errctx, akgl_get_json_string_value(property, "value", dest));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -68,13 +68,13 @@ ErrorContext *get_json_properties_string(json_t *obj, char *key, string **dest)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *get_json_properties_integer(json_t *obj, char *key, int *dest) akerr_ErrorContext *akgl_get_json_properties_integer(json_t *obj, char *key, int *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *property = NULL; json_t *property = NULL;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_tilemap_property(obj, key, "int", &property)); CATCH(errctx, akgl_get_json_tilemap_property(obj, key, "int", &property));
CATCH(errctx, get_json_integer_value(property, "value", dest)); CATCH(errctx, akgl_get_json_integer_value(property, "value", dest));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -82,38 +82,52 @@ ErrorContext *get_json_properties_integer(json_t *obj, char *key, int *dest)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_tilesets_each(json_t *tileset, tilemap *dest, int tsidx) akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, float *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
string *tmpstr = NULL; json_t *property = NULL;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_integer_value((json_t *)tileset, "columns", &dest->tilesets[tsidx].columns)); CATCH(errctx, akgl_get_json_tilemap_property(obj, key, "number", &property));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "firstgid", &dest->tilesets[tsidx].firstgid)); CATCH(errctx, akgl_get_json_number_value(property, "value", dest));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "imageheight", &dest->tilesets[tsidx].imageheight)); } CLEANUP {
CATCH(errctx, get_json_integer_value((json_t *)tileset, "imagewidth", &dest->tilesets[tsidx].imagewidth)); } PROCESS(errctx) {
CATCH(errctx, get_json_integer_value((json_t *)tileset, "margin", &dest->tilesets[tsidx].margin)); } FINISH(errctx, true);
CATCH(errctx, get_json_integer_value((json_t *)tileset, "spacing", &dest->tilesets[tsidx].spacing));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "tilecount", &dest->tilesets[tsidx].tilecount));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "tileheight", &dest->tilesets[tsidx].tileheight));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "tilewidth", &dest->tilesets[tsidx].tilewidth));
CATCH(errctx, get_json_string_value((json_t *)tileset, "name", &tmpstr)); SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx)
{
PREPARE_ERROR(errctx);
akgl_String *tmpstr = 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, strncpy((char *)&dest->tilesets[tsidx].name,
(char *)&tmpstr->data, (char *)&tmpstr->data,
TILEMAP_MAX_TILESET_NAME_SIZE AKGL_TILEMAP_MAX_TILESET_NAME_SIZE
); );
CATCH(errctx, get_json_string_value((json_t *)tileset, "image", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)tileset, "image", &tmpstr));
snprintf((char *)&dest->tilesets[tsidx].imagefilename, snprintf((char *)&dest->tilesets[tsidx].imagefilename,
TILEMAP_MAX_TILESET_FILENAME_SIZE, AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s%s", "%s%s",
SDL_GetBasePath(), SDL_GetBasePath(),
tmpstr->data tmpstr->data
); );
dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer, (char *)&dest->tilesets[tsidx].imagefilename); dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer, (char *)&dest->tilesets[tsidx].imagefilename);
FAIL_ZERO_BREAK(errctx, dest->tilesets[tsidx].texture, ERR_NULLPOINTER, "Failed loading tileset image"); FAIL_ZERO_BREAK(errctx, dest->tilesets[tsidx].texture, AKERR_NULLPOINTER, "Failed loading tileset image : %s", SDL_GetError());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -121,7 +135,7 @@ ErrorContext *tilemap_load_tilesets_each(json_t *tileset, tilemap *dest, int tsi
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_compute_tileset_offsets(tilemap *dest, int tilesetidx) akerr_ErrorContext *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int tilesetidx)
{ {
int x_offset = 0; int x_offset = 0;
int y_offset = 0; int y_offset = 0;
@@ -176,11 +190,11 @@ ErrorContext *tilemap_compute_tileset_offsets(tilemap *dest, int tilesetidx)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root) akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "Received NULL tilemap pointer"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "Received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "Received NULL json object pointer"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "Received NULL json object pointer");
json_t *tilesets = NULL; json_t *tilesets = NULL;
json_t *jstileset = NULL; json_t *jstileset = NULL;
@@ -188,11 +202,11 @@ ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root)
dest->numtilesets = 0; dest->numtilesets = 0;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_array_value(root, "tilesets", &tilesets)) CATCH(errctx, akgl_get_json_array_value(root, "tilesets", &tilesets))
for (i = 0; i < json_array_size((json_t *)tilesets); i++) { for (i = 0; i < json_array_size((json_t *)tilesets); i++) {
CATCH(errctx, get_json_array_index_object((json_t *)tilesets, i, &jstileset)); CATCH(errctx, akgl_get_json_array_index_object((json_t *)tilesets, i, &jstileset));
CATCH(errctx, tilemap_load_tilesets_each(jstileset, dest, i)); CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i));
CATCH(errctx, tilemap_compute_tileset_offsets(dest, i)); CATCH(errctx, akgl_tilemap_compute_tileset_offsets(dest, i));
dest->numtilesets += 1; dest->numtilesets += 1;
} }
} CLEANUP { } CLEANUP {
@@ -202,37 +216,37 @@ ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_layer_object_actor(tilemap_object *curobj, json_t *layerdatavalue, int layerid) akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
actor *actorobj = NULL; akgl_Actor *actorobj = NULL;
curobj->type = TILEMAP_OBJECT_TYPE_ACTOR; curobj->type = AKGL_TILEMAP_OBJECT_TYPE_ACTOR;
if ( strlen((char *)&curobj->name) == 0 ) { if ( strlen((char *)&curobj->name) == 0 ) {
FAIL_RETURN(errctx, ERR_KEY, "Actor in tile object layer cannot have empty name"); FAIL_RETURN(errctx, AKERR_KEY, "Actor in tile object layer cannot have empty name");
} }
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
actorobj = SDL_GetPointerProperty(REGISTRY_ACTOR, (char *)&curobj->name, NULL); actorobj = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, (char *)&curobj->name, NULL);
if ( actorobj == NULL ) { if ( actorobj == NULL ) {
CATCH(errctx, heap_next_actor(&actorobj)); CATCH(errctx, akgl_heap_next_actor(&actorobj));
CATCH(errctx, actor_initialize((actor *)actorobj, (char *)&curobj->name)); CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorobj, (char *)&curobj->name));
CATCH(errctx, get_json_properties_string((json_t *)layerdatavalue, "character", &tmpstr)); CATCH(errctx, akgl_get_json_properties_string((json_t *)layerdatavalue, "character", &tmpstr));
CATCH(errctx, CATCH(errctx,
actor_set_character( akgl_actor_set_character(
(actor *)actorobj, (akgl_Actor *)actorobj,
(char *)&tmpstr->data (char *)&tmpstr->data
) )
); );
} else { } else {
actorobj->refcount += 1; actorobj->refcount += 1;
} }
CATCH(errctx, get_json_properties_integer((json_t *)layerdatavalue, "state", &actorobj->state)); CATCH(errctx, akgl_get_json_properties_integer((json_t *)layerdatavalue, "state", &actorobj->state));
} CLEANUP { } CLEANUP {
if ( tmpstr != NULL ) { if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -241,43 +255,55 @@ ErrorContext *tilemap_load_layer_object_actor(tilemap_object *curobj, json_t *la
actorobj->x = curobj->x; actorobj->x = curobj->x;
actorobj->y = curobj->y; actorobj->y = curobj->y;
actorobj->visible = curobj->visible; actorobj->visible = curobj->visible;
curobj->actorptr = (actor *)actorobj; curobj->actorptr = (akgl_Actor *)actorobj;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_layer_objects(tilemap *dest, json_t *root, int layerid) akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *layerdata = NULL; json_t *layerdata = NULL;
json_t *layerdatavalue = NULL; json_t *layerdatavalue = NULL;
int j; int j;
int len; int len;
tilemap_layer *curlayer = NULL; akgl_TilemapLayer *curlayer = NULL;
tilemap_object *curobj = NULL; akgl_TilemapObject *curobj = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination tilemap reference"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "NULL tilemap root reference"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_array_value(root, "objects", &layerdata)); CATCH(errctx, akgl_get_json_array_value(root, "objects", &layerdata));
len = json_array_size((json_t *)layerdata); len = json_array_size((json_t *)layerdata);
curlayer = &dest->layers[layerid]; curlayer = &dest->layers[layerid];
for ( j = 0; j < len; j++ ) { for ( j = 0; j < len; j++ ) {
CATCH(errctx, get_json_array_index_object((json_t *)layerdata, j, &layerdatavalue)); CATCH(errctx, akgl_get_json_array_index_object((json_t *)layerdata, j, &layerdatavalue));
curobj = &curlayer->objects[j]; curobj = &curlayer->objects[j];
CATCH(errctx, get_json_string_value((json_t *)layerdatavalue, "name", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "name", &tmpstr));
strncpy((char *)curobj->name, tmpstr->data, ACTOR_MAX_NAME_LENGTH); strncpy((char *)curobj->name, tmpstr->data, AKGL_ACTOR_MAX_NAME_LENGTH);
CATCH(errctx, heap_release_string(tmpstr)); CATCH(errctx, akgl_heap_release_string(tmpstr));
CATCH(errctx, get_json_number_value((json_t *)layerdatavalue, "x", &curobj->x)); CATCH(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "x", &curobj->x));
CATCH(errctx, get_json_number_value((json_t *)layerdatavalue, "y", &curobj->y)); CATCH(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "y", &curobj->y));
CATCH(errctx, 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, get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr)); CATCH(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr));
if ( strcmp(tmpstr->data, "actor") == 0 ) { if ( strcmp(tmpstr->data, "actor") == 0 ) {
CATCH(errctx, tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid)); 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));
CATCH(errctx, akgl_get_json_properties_number((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));
}
} }
layerdatavalue = NULL; layerdatavalue = NULL;
} }
} CLEANUP { } CLEANUP {
@@ -287,26 +313,26 @@ ErrorContext *tilemap_load_layer_objects(tilemap *dest, json_t *root, int layeri
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_layer_tile(tilemap *dest, json_t *root, int layerid) akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *layerdata = NULL; json_t *layerdata = NULL;
int j; int j;
int layerdatalen; int layerdatalen;
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination tilemap reference"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "NULL tilemap root reference"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_integer_value(root, "height", &dest->layers[layerid].height)); CATCH(errctx, akgl_get_json_integer_value(root, "height", &dest->layers[layerid].height));
CATCH(errctx, get_json_integer_value(root, "width", &dest->layers[layerid].width)); CATCH(errctx, akgl_get_json_integer_value(root, "width", &dest->layers[layerid].width));
CATCH(errctx, get_json_array_value(root, "data", &layerdata)); CATCH(errctx, akgl_get_json_array_value(root, "data", &layerdata));
layerdatalen = (dest->layers[layerid].width * dest->layers[layerid].height); layerdatalen = (dest->layers[layerid].width * dest->layers[layerid].height);
if ( layerdatalen >= (TILEMAP_MAX_WIDTH * TILEMAP_MAX_HEIGHT) ) { if ( layerdatalen >= (AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT) ) {
FAIL_BREAK(errctx, ERR_OUTOFBOUNDS, "Map layer exceeds the maximum size"); FAIL_BREAK(errctx, AKERR_OUTOFBOUNDS, "Map layer exceeds the maximum size");
} }
for ( j = 0; j < layerdatalen; j++ ) { for ( j = 0; j < layerdatalen; j++ ) {
CATCH(errctx, get_json_array_index_integer(layerdata, j, &dest->layers[layerid].data[j])); CATCH(errctx, akgl_get_json_array_index_integer(layerdata, j, &dest->layers[layerid].data[j]));
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -315,47 +341,81 @@ ErrorContext *tilemap_load_layer_tile(tilemap *dest, json_t *root, int layerid)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load_layers(tilemap *dest, json_t *root) akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "tilemap_load_layers received NULL tilemap pointer"); akgl_String *tmpstr;
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "tilemap_load_layers received NULL json object pointer"); akgl_String *fpath;
json_t *layers = NULL;
json_t *layer = NULL; FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
string *tmpstr = NULL; FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
int i;
int tmpint;
ATTEMPT { ATTEMPT {
CATCH(errctx, get_json_array_value(root, "layers", &layers)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, akgl_heap_next_string(&fpath));
CATCH(errctx, akgl_get_json_string_value(root, "image", &tmpstr));
snprintf((char *)&fpath->data,
AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s%s",
SDL_GetBasePath(),
tmpstr->data
);
dest->layers[layerid].texture = IMG_LoadTexture(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;
} CLEANUP {
IGNORE(akgl_heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(fpath));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root)
{
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");
json_t *layers = NULL;
json_t *layer = NULL;
akgl_String *tmpstr = NULL;
int i;
int layerid = 0;
int tmpint = 0;
ATTEMPT {
CATCH(errctx, akgl_get_json_array_value(root, "layers", &layers));
dest->numlayers = json_array_size((json_t *)layers); dest->numlayers = json_array_size((json_t *)layers);
for ( i = 0; i < dest->numlayers; i++) { for ( i = 0; i < dest->numlayers; i++) {
if ( i >= TILEMAP_MAX_LAYERS ) { if ( i >= AKGL_TILEMAP_MAX_LAYERS ) {
FAIL_BREAK(errctx, ERR_OUTOFBOUNDS, "Map exceeds the maximum number of layers"); FAIL_BREAK(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum number of layers");
}
CATCH(errctx, get_json_array_index_object((json_t *)layers, i, &layer));
CATCH(errctx, get_json_integer_value((json_t *)layer, "id", &tmpint));
if ( (tmpint - 1) != i ) {
// TileD's map layer IDs start at 1, not 0, and are sequential but not necessarily contiguous. We may have a gap in IDs.
layer = NULL;
continue;
} }
CATCH(errctx, akgl_get_json_array_index_object((json_t *)layers, i, &layer));
CATCH(errctx, akgl_get_json_integer_value((json_t *)layer, "id", &tmpint));
CATCH(errctx, akgl_get_json_number_value((json_t *)layer, "opacity", &dest->layers[layerid].opacity));
CATCH(errctx, akgl_get_json_boolean_value((json_t *)layer, "visible", &dest->layers[layerid].visible));
CATCH(errctx, akgl_get_json_integer_value((json_t *)layer, "id", &dest->layers[layerid].id));
CATCH(errctx, akgl_get_json_integer_value((json_t *)layer, "x", &dest->layers[layerid].x));
CATCH(errctx, akgl_get_json_integer_value((json_t *)layer, "y", &dest->layers[layerid].y));
CATCH(errctx, get_json_number_value((json_t *)layer, "opacity", &dest->layers[i].opacity)); CATCH(errctx, akgl_get_json_string_value((json_t *)layer, "type", &tmpstr));
CATCH(errctx, get_json_boolean_value((json_t *)layer, "visible", &dest->layers[i].visible)); SDL_Log("Layer %d has type %s", layerid, tmpstr->data);
CATCH(errctx, get_json_integer_value((json_t *)layer, "id", &dest->layers[i].id));
CATCH(errctx, get_json_integer_value((json_t *)layer, "x", &dest->layers[i].x));
CATCH(errctx, get_json_integer_value((json_t *)layer, "y", &dest->layers[i].y));
CATCH(errctx, get_json_string_value((json_t *)layer, "type", &tmpstr));
if ( strncmp((char *)tmpstr->data, "objectgroup", strlen((char *)tmpstr->data)) == 0 ) { if ( strncmp((char *)tmpstr->data, "objectgroup", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[i].type = TILEMAP_LAYER_TYPE_OBJECTS; dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_OBJECTS;
CATCH(errctx, tilemap_load_layer_objects((tilemap *)dest, (json_t *)layer, i)); CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid));
} else if ( strncmp((char *)tmpstr->data, "tilelayer", strlen((char *)tmpstr->data)) == 0 ) { } else if ( strncmp((char *)tmpstr->data, "tilelayer", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[i].type = TILEMAP_LAYER_TYPE_TILES; dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_TILES;
CATCH(errctx, tilemap_load_layer_tile((tilemap *)dest, (json_t *)layer, i)); CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid));
} 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));
} }
layer = NULL; layer = NULL;
layerid += 1;
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -363,53 +423,68 @@ ErrorContext *tilemap_load_layers(tilemap *dest, json_t *root)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_load(char *fname, tilemap *dest) akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *json = NULL; json_t *json = NULL;
string *tmpstr = NULL; //akgl_String *tmpstr = NULL;
json_error_t error; json_error_t error;
FAIL_ZERO_RETURN(errctx, fname, ERR_NULLPOINTER, "load_tilemap received null filename"); FAIL_ZERO_RETURN(errctx, fname, AKERR_NULLPOINTER, "load_tilemap received null filename");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "load_tilemap received null tilemap"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "load_tilemap received null tilemap");
memset(dest, 0x00, sizeof(tilemap));
memset(dest, 0x00, sizeof(akgl_Tilemap));
dest->p_foreground_scale = 1.0;
dest->p_vanishing_scale = 1.0;
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr)); //CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, string_initialize(tmpstr, NULL)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
SDL_snprintf(tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname); //SDL_snprintf(tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname);
json = json_load_file(tmpstr->data, 0, &error); json = json_load_file(fname, 0, &error);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
json, json,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Error while loading tilemap from %s on line %d: %s-", "Error while loading tilemap from %s on line %d: %s-",
tmpstr->data, fname,
error.line, error.line,
error.text error.text
); );
CATCH(errctx, get_json_integer_value((json_t *)json, "tileheight", &dest->tileheight)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "tileheight", &dest->tileheight));
CATCH(errctx, get_json_integer_value((json_t *)json, "tilewidth", &dest->tilewidth)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "tilewidth", &dest->tilewidth));
CATCH(errctx, get_json_integer_value((json_t *)json, "height", &dest->height)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "height", &dest->height));
CATCH(errctx, get_json_integer_value((json_t *)json, "width", &dest->width)); CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "width", &dest->width));
dest->orientation = 0; dest->orientation = 0;
if ( (dest->width * dest->height) >= (TILEMAP_MAX_WIDTH * TILEMAP_MAX_HEIGHT) ) { if ( (dest->width * dest->height) >= (AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT) ) {
FAIL_RETURN(errctx, ERR_OUTOFBOUNDS, "Map exceeds the maximum size"); FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size");
} }
CATCH(errctx, tilemap_load_layers((tilemap *)dest, (json_t *)json)); CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json));
CATCH(errctx, tilemap_load_tilesets((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 = ((float)dest->p_foreground_h / (float)dest->p_vanishing_h); */
/* SDL_Log("Map perspective scale is (%d/%d) = %f", dest->p_foreground_h, dest->p_vanishing_h, dest->p_scale); */
// Sprites are scale N (default 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 (((N=1.0) / (640 - 320)) / (dest->p_scale = 2)), or
// 0.0066% scale per pixel.
dest->p_rate = ((dest->p_foreground_scale - dest->p_vanishing_scale) / (dest->p_foreground_y - dest->p_vanishing_y));
SDL_Log("Map perspective rate is %f", dest->p_rate);
}
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(tmpstr)); //IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *viewport, int layeridx) akerr_ErrorContext *akgl_tilemap_draw(SDL_Renderer *renderer, akgl_Tilemap *map, SDL_FRect *viewport, int layeridx)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
SDL_FRect dest = {.x = 0, .y = 0, .w = 0, .h = 0};; SDL_FRect dest = {.x = 0, .y = 0, .w = 0, .h = 0};;
@@ -439,8 +514,8 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
* 0 and 8 would not be rendered. 1, 9, 6, and E would be partially rendered at their corner. * 0 and 8 would not be rendered. 1, 9, 6, and E would be partially rendered at their corner.
* 2,3,4,5 and A,B,C,D would be partially rendered with a slice from their center. * 2,3,4,5 and A,B,C,D would be partially rendered with a slice from their center.
*/ */
FAIL_ZERO_RETURN(errctx, map, ERR_NULLPOINTER, "tilemap_draw received NULL pointer to tilemap"); FAIL_ZERO_RETURN(errctx, map, AKERR_NULLPOINTER, "akgl_tilemap_draw received NULL pointer to tilemap");
FAIL_ZERO_RETURN(errctx, viewport, ERR_NULLPOINTER, "tilemap_draw received NULL pointer to viewport"); FAIL_ZERO_RETURN(errctx, viewport, AKERR_NULLPOINTER, "akgl_tilemap_draw received NULL pointer to viewport");
/* Only try to render the stuff that is partially within the viewport */ /* Only try to render the stuff that is partially within the viewport */
@@ -458,6 +533,17 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
/*SDL_Log("Rendering map into viewport from (%d, %d) to (%d, %d)", /*SDL_Log("Rendering map into viewport from (%d, %d) to (%d, %d)",
start_x, start_y, end_x, end_y);*/ start_x, start_y, end_x, end_y);*/
if ( map->layers[layeridx].type == AKGL_TILEMAP_LAYER_TYPE_IMAGE ) {
dest.x = 0;
dest.y = 0;
src.w = map->layers[layeridx].width;
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);
SUCCEED_RETURN(errctx);
}
dest.x = 0; dest.x = 0;
dest.y = 0; dest.y = 0;
dest.w = map->tilewidth; dest.w = map->tilewidth;
@@ -519,7 +605,7 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *map, int tilesetidx) akerr_ErrorContext *akgl_tilemap_draw_tileset(SDL_Renderer *renderer, akgl_Tilemap *map, int tilesetidx)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
SDL_FRect dest; SDL_FRect dest;
@@ -531,8 +617,8 @@ ErrorContext *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *map, int til
* by proving that we can reconstruct the original tileset image) * by proving that we can reconstruct the original tileset image)
*/ */
FAIL_ZERO_RETURN(errctx, map, ERR_NULLPOINTER, "tilemap_draw_tileset received NULL pointer to tilemap"); FAIL_ZERO_RETURN(errctx, map, AKERR_NULLPOINTER, "akgl_tilemap_draw_tileset received NULL pointer to tilemap");
FAIL_NONZERO_RETURN(errctx, (tilesetidx >= map->numtilesets), ERR_OUTOFBOUNDS, "tilemap_draw_tileset received a tileset index out of bounds"); FAIL_NONZERO_RETURN(errctx, (tilesetidx >= map->numtilesets), AKERR_OUTOFBOUNDS, "akgl_tilemap_draw_tileset received a tileset index out of bounds");
for ( tilenum = 0; tilenum < map->tilesets[tilesetidx].tilecount; tilenum++) { for ( tilenum = 0; tilenum < map->tilesets[tilesetidx].tilecount; tilenum++) {
// Render this tile to the correct screen position // Render this tile to the correct screen position
@@ -568,3 +654,41 @@ ErrorContext *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *map, int til
} }
SUCCEED_RETURN(errctx); 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");
if ( actor->y <= map->p_vanishing_y ) {
actor->scale = map->p_vanishing_scale;
} else if ( actor->y >= map->p_foreground_y ) {
actor->scale = map->p_foreground_scale;
} else {
actor->scale = map->p_foreground_scale - (map->p_rate * (map->p_foreground_y - actor->y));
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_release(akgl_Tilemap *dest)
{
// Release all tileset textures
// Release all image layer textures
// Memset to zero
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "NULL map");
int i = 0;
for ( i = 0; i < AKGL_TILEMAP_MAX_TILESETS; i++ ) {
if ( dest->tilesets[i].texture != NULL ) {
SDL_DestroyTexture(dest->tilesets[i].texture);
}
}
for ( i = 0; i < AKGL_TILEMAP_MAX_LAYERS; i++ ) {
if ( dest->layers[i].texture != NULL ) {
SDL_DestroyTexture(dest->tilesets[i].texture);
}
}
SUCCEED_RETURN(e);
}

View File

@@ -3,18 +3,18 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/util.h> #include <akgl/util.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/game.h> #include <akgl/game.h>
ErrorContext *rectangle_points(RectanglePoints *dest, SDL_FRect *rect) akerr_ErrorContext *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL RectanglePoints reference"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, rect, ERR_NULLPOINTER, "NULL Rectangle reference"); FAIL_ZERO_RETURN(errctx, rect, AKERR_NULLPOINTER, "NULL Rectangle reference");
dest->topleft.x = rect->x; dest->topleft.x = rect->x;
dest->topleft.y = rect->y; dest->topleft.y = rect->y;
dest->bottomleft.x = rect->x; dest->bottomleft.x = rect->x;
@@ -26,12 +26,12 @@ ErrorContext *rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *collide_point_rectangle(point *p, RectanglePoints *rp, bool *collide) akerr_ErrorContext *akgl_collide_point_rectangle(point *p, RectanglePoints *rp, bool *collide)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, p, ERR_NULLPOINTER, "NULL Point reference"); FAIL_ZERO_RETURN(errctx, p, AKERR_NULLPOINTER, "NULL Point reference");
FAIL_ZERO_RETURN(errctx, rp, ERR_NULLPOINTER, "NULL RectanglePoints reference"); FAIL_ZERO_RETURN(errctx, rp, AKERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, collide, ERR_NULLPOINTER, "NULL boolean reference"); FAIL_ZERO_RETURN(errctx, collide, AKERR_NULLPOINTER, "NULL boolean reference");
if ( (p->x >= rp->topleft.x) && (p->y >= rp->topleft.y) && if ( (p->x >= rp->topleft.x) && (p->y >= rp->topleft.y) &&
(p->x <= rp->bottomright.x) && (p->y <= rp->bottomright.y) ) { (p->x <= rp->bottomright.x) && (p->y <= rp->bottomright.y) ) {
*collide = true; *collide = true;
@@ -41,49 +41,49 @@ ErrorContext *collide_point_rectangle(point *p, RectanglePoints *rp, bool *colli
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide) akerr_ErrorContext *akgl_collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide)
{ {
RectanglePoints r1p; RectanglePoints r1p;
RectanglePoints r2p; RectanglePoints r2p;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, r1, ERR_NULLPOINTER, "NULL rectangle reference"); FAIL_ZERO_RETURN(errctx, r1, AKERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, r2, ERR_NULLPOINTER, "NULL rectangle reference"); FAIL_ZERO_RETURN(errctx, r2, AKERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, collide, ERR_NULLPOINTER, "NULL collision flag reference"); FAIL_ZERO_RETURN(errctx, collide, AKERR_NULLPOINTER, "NULL collision flag reference");
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(&r1p, r1)); CATCH(errctx, akgl_rectangle_points(&r1p, r1));
CATCH(errctx, rectangle_points(&r2p, r2)); CATCH(errctx, akgl_rectangle_points(&r2p, r2));
// is the upper left corner of r1 contacting r2? // is the upper left corner of r1 contacting r2?
CATCH(errctx, collide_point_rectangle(&r1p.topleft, &r2p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r1p.topleft, &r2p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the upper left corner of r2 contacting r1? // is the upper left corner of r2 contacting r1?
CATCH(errctx, collide_point_rectangle(&r2p.topleft, &r1p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r2p.topleft, &r1p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the top right corner of r1 contacting r2? // is the top right corner of r1 contacting r2?
CATCH(errctx, collide_point_rectangle(&r1p.topright, &r2p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r1p.topright, &r2p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the top right corner of r2 contacting r1? // is the top right corner of r2 contacting r1?
CATCH(errctx, collide_point_rectangle(&r2p.topright, &r1p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r2p.topright, &r1p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the bottom left corner of r1 contacting r2? // is the bottom left corner of r1 contacting r2?
CATCH(errctx, collide_point_rectangle(&r1p.bottomleft, &r2p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r1p.bottomleft, &r2p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the bottom left corner of r2 contacting r1? // is the bottom left corner of r2 contacting r1?
CATCH(errctx, collide_point_rectangle(&r2p.bottomleft, &r1p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r2p.bottomleft, &r1p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the bottom right corner of r1 contacting r2? // is the bottom right corner of r1 contacting r2?
CATCH(errctx, collide_point_rectangle(&r1p.bottomright, &r2p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r1p.bottomright, &r2p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
// is the bottom right corner of r2 contacting r1? // is the bottom right corner of r2 contacting r1?
CATCH(errctx, collide_point_rectangle(&r2p.bottomright, &r1p, collide)); CATCH(errctx, akgl_collide_point_rectangle(&r2p.bottomright, &r1p, collide));
if ( *collide == true ) { SUCCEED_RETURN(errctx); } if ( *collide == true ) { SUCCEED_RETURN(errctx); }
} CLEANUP { } CLEANUP {
@@ -95,30 +95,30 @@ ErrorContext *collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide)
} }
ErrorContext *compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2) akerr_ErrorContext *akgl_compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, s1, ERR_NULLPOINTER, "NULL Surface pointer"); FAIL_ZERO_RETURN(errctx, s1, AKERR_NULLPOINTER, "NULL Surface pointer");
FAIL_ZERO_RETURN(errctx, s2, ERR_NULLPOINTER, "NULL Surface pointer"); FAIL_ZERO_RETURN(errctx, s2, AKERR_NULLPOINTER, "NULL Surface pointer");
FAIL_NONZERO_RETURN(errctx, memcmp(s1->pixels, s2->pixels, (s1->pitch * s1->h)), ERR_VALUE, "Comparison surfaces are not equal"); FAIL_NONZERO_RETURN(errctx, memcmp(s1->pixels, s2->pixels, (s1->pitch * s1->h)), AKERR_VALUE, "Comparison surfaces are not equal");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout) akerr_ErrorContext *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout)
{ {
SDL_Surface *s1 = NULL; SDL_Surface *s1 = NULL;
SDL_Surface *s2 = NULL; SDL_Surface *s2 = NULL;
SDL_FRect src = {.x = x, .y = y, .w = w, .h = h}; SDL_FRect src = {.x = x, .y = y, .w = w, .h = h};
SDL_FRect dest = {.x = x, .y = y, .w = w, .h = h}; SDL_FRect dest = {.x = x, .y = y, .w = w, .h = h};
SDL_Rect read = {.x = x, .y = y, .w = w, .h = h}; SDL_Rect read = {.x = x, .y = y, .w = w, .h = h};
string *tmpstring = NULL; akgl_String *tmpstring = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, t1, ERR_NULLPOINTER, "NULL texture"); FAIL_ZERO_BREAK(errctx, t1, AKERR_NULLPOINTER, "NULL texture");
FAIL_ZERO_BREAK(errctx, t2, ERR_NULLPOINTER, "NULL texture"); FAIL_ZERO_BREAK(errctx, t2, AKERR_NULLPOINTER, "NULL texture");
CATCH(errctx, heap_next_string(&tmpstring)); CATCH(errctx, akgl_heap_next_string(&tmpstring));
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
@@ -127,17 +127,17 @@ ErrorContext *render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y,
t1, t1,
&src, &src,
&dest), &dest),
ERR_SDL, AKGL_ERR_SDL,
"Failed to render test texture"); "Failed to render test texture");
s1 = SDL_RenderReadPixels(renderer, &read); s1 = SDL_RenderReadPixels(renderer, &read);
FAIL_ZERO_BREAK(errctx, s1, ERR_SDL, "Failed to read pixels from renderer"); FAIL_ZERO_BREAK(errctx, s1, AKGL_ERR_SDL, "Failed to read pixels from renderer");
if ( writeout != NULL ) { if ( writeout != NULL ) {
snprintf((char *)&tmpstring->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), writeout); snprintf((char *)&tmpstring->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), writeout);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
IMG_SavePNG(s1, (char *)&tmpstring->data), IMG_SavePNG(s1, (char *)&tmpstring->data),
ERR_IO, AKERR_IO,
"Unable to save %s: %s", "Unable to save %s: %s",
(char *)&tmpstring->data, (char *)&tmpstring->data,
SDL_GetError()); SDL_GetError());
@@ -152,18 +152,18 @@ ErrorContext *render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y,
t2, t2,
&src, &src,
&dest), &dest),
ERR_SDL, AKGL_ERR_SDL,
"Failed to render test texture"); "Failed to render test texture");
s2 = SDL_RenderReadPixels(renderer, &read); s2 = SDL_RenderReadPixels(renderer, &read);
FAIL_ZERO_BREAK(errctx, s2, ERR_SDL, "Failed to read pixels from renderer"); FAIL_ZERO_BREAK(errctx, s2, AKGL_ERR_SDL, "Failed to read pixels from renderer");
CATCH(errctx, compare_sdl_surfaces(s1, s2)); CATCH(errctx, akgl_compare_sdl_surfaces(s1, s2));
} CLEANUP { } CLEANUP {
if ( s1 != NULL ) if ( s1 != NULL )
SDL_DestroySurface(s1); SDL_DestroySurface(s1);
if ( s2 != NULL ) if ( s2 != NULL )
SDL_DestroySurface(s2); SDL_DestroySurface(s2);
IGNORE(heap_release_string(tmpstring)); IGNORE(akgl_heap_release_string(tmpstring));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);

View File

@@ -1,7 +1,7 @@
#define UNHANDLED_ERROR_TERMINATION_BEHAVIOR \ #define UNHANDLED_ERROR_TERMINATION_BEHAVIOR \
handle_unhandled_error(errctx); handle_unhandled_error(errctx);
#include <sdlerror.h> #include <akerror.h>
#define UNHANDLED_ERROR_EXIT 0 #define UNHANDLED_ERROR_EXIT 0
#define UNHANDLED_ERROR_SET 1 #define UNHANDLED_ERROR_SET 1
@@ -9,15 +9,15 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <stdlib.h> #include <stdlib.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
int UNHANDLED_ERROR_BEHAVIOR; int UNHANDLED_ERROR_BEHAVIOR;
ErrorContext *unhandled_error_context; akerr_ErrorContext *unhandled_error_context;
void handle_unhandled_error_noexit(ErrorContext *errctx) void handle_unhandled_error_noexit(akerr_ErrorContext *errctx)
{ {
if ( errctx == NULL ) { if ( errctx == NULL ) {
return; return;
@@ -32,203 +32,200 @@ void handle_unhandled_error_noexit(ErrorContext *errctx)
} }
} }
int actor_updated; int akgl_actor_updated;
ErrorContext *actor_update_noop(actor *obj) akerr_ErrorContext *akgl_actor_update_noop(akgl_Actor *obj)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
actor_updated = 1; akgl_actor_updated = 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
// Currently the renderer assumes there is a global variable named `renderer` // Currently the renderer assumes there is a global variable named `renderer`
int actor_rendered; int akgl_actor_rendered;
ErrorContext *actor_render_noop(actor *obj, SDL_Renderer *r) akerr_ErrorContext *akgl_actor_render_noop(akgl_Actor *obj, SDL_Renderer *r)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
actor_rendered = 1; akgl_actor_rendered = 1;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_registry_actor_iterator_nullpointers(void) akerr_ErrorContext *test_registry_actor_iterator_nullpointers(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ErrorUnhandledErrorHandler defaulthandler = error_handler_unhandled_error; akerr_ErrorUnhandledErrorHandler defaulthandler = akerr_handler_unhandled_error;
error_handler_unhandled_error = handle_unhandled_error_noexit; akerr_handler_unhandled_error = handle_unhandled_error_noexit;
ATTEMPT { ATTEMPT {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET;
DETECT(unhandled_error_context, registry_iterate_actor(NULL, REGISTRY_ACTOR, "")); DETECT(unhandled_error_context, akgl_registry_iterate_actor(NULL, AKGL_REGISTRY_ACTOR, ""));
} CLEANUP { } CLEANUP {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT;
} PROCESS(unhandled_error_context) { } PROCESS(unhandled_error_context) {
} HANDLE(unhandled_error_context, ERR_NULLPOINTER) { } HANDLE(unhandled_error_context, AKERR_NULLPOINTER) {
printf("Handled\n"); printf("Handled\n");
} FINISH(unhandled_error_context, true); } FINISH(unhandled_error_context, true);
IGNORE(heap_release_error(unhandled_error_context)); akerr_handler_unhandled_error = defaulthandler;
error_handler_unhandled_error = defaulthandler;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_registry_actor_iterator_missingactor(void) akerr_ErrorContext *test_registry_actor_iterator_missingactor(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ErrorUnhandledErrorHandler defaulthandler = error_handler_unhandled_error; akerr_ErrorUnhandledErrorHandler defaulthandler = akerr_handler_unhandled_error;
iterator iter = { akgl_Iterator iter = {
.layerid = 0, .layerid = 0,
.flags = 0 .flags = 0
}; };
error_handler_unhandled_error = handle_unhandled_error_noexit; akerr_handler_unhandled_error = handle_unhandled_error_noexit;
ATTEMPT { ATTEMPT {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET;
DETECT( DETECT(
unhandled_error_context, unhandled_error_context,
registry_iterate_actor( akgl_registry_iterate_actor(
&iter, &iter,
REGISTRY_ACTOR, AKGL_REGISTRY_ACTOR,
"Actor who doesn't exist") "Actor who doesn't exist")
); );
} CLEANUP { } CLEANUP {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT;
} PROCESS(unhandled_error_context) { } PROCESS(unhandled_error_context) {
} HANDLE(unhandled_error_context, ERR_KEY) { } HANDLE(unhandled_error_context, AKERR_KEY) {
printf("Handled\n"); printf("Handled\n");
} FINISH(unhandled_error_context, true); } FINISH(unhandled_error_context, true);
IGNORE(heap_release_error(unhandled_error_context)); akerr_handler_unhandled_error = defaulthandler;
error_handler_unhandled_error = defaulthandler;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_registry_actor_iterator_updaterender(void) akerr_ErrorContext *test_registry_actor_iterator_updaterender(void)
{ {
actor *testactor; akgl_Actor *testactor;
iterator iter = { akgl_Iterator iter = {
.layerid = 0, .layerid = 0,
.flags = ITERATOR_OP_UPDATE | ITERATOR_OP_RENDER .flags = AKGL_ITERATOR_OP_UPDATE | AKGL_ITERATOR_OP_RENDER
}; };
ErrorUnhandledErrorHandler defaulthandler = error_handler_unhandled_error; akerr_ErrorUnhandledErrorHandler defaulthandler = akerr_handler_unhandled_error;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
error_handler_unhandled_error = handle_unhandled_error_noexit; akerr_handler_unhandled_error = handle_unhandled_error_noexit;
ATTEMPT { ATTEMPT {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_SET;
CATCH(unhandled_error_context, heap_next_actor(&testactor)); CATCH(unhandled_error_context, akgl_heap_next_actor(&testactor));
CATCH(unhandled_error_context, actor_initialize(testactor, "test")); CATCH(unhandled_error_context, akgl_actor_initialize(testactor, "test"));
testactor->layer = 0; testactor->layer = 0;
testactor->updatefunc = &actor_update_noop; testactor->updatefunc = &akgl_actor_update_noop;
testactor->renderfunc = &actor_render_noop; testactor->renderfunc = &akgl_actor_render_noop;
DETECT( DETECT(
unhandled_error_context, unhandled_error_context,
registry_iterate_actor( akgl_registry_iterate_actor(
&iter, &iter,
REGISTRY_ACTOR, AKGL_REGISTRY_ACTOR,
"test") "test")
); );
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
unhandled_error_context, unhandled_error_context,
actor_updated, akgl_actor_updated,
ERR_BEHAVIOR, AKERR_BEHAVIOR,
"actor->updatefunc not called by the iterator" "actor->updatefunc not called by the iterator"
); );
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
unhandled_error_context, unhandled_error_context,
actor_rendered, akgl_actor_rendered,
ERR_BEHAVIOR, AKERR_BEHAVIOR,
"actor->renderfunc not called by the iterator" "actor->renderfunc not called by the iterator"
); );
} CLEANUP { } CLEANUP {
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT;
IGNORE(heap_release_actor(testactor)); IGNORE(akgl_heap_release_actor(testactor));
} PROCESS(unhandled_error_context) { } PROCESS(unhandled_error_context) {
} FINISH(unhandled_error_context, true); } FINISH(unhandled_error_context, true);
IGNORE(heap_release_error(unhandled_error_context)); akerr_handler_unhandled_error = defaulthandler;
error_handler_unhandled_error = defaulthandler;
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_actor_set_character(void) akerr_ErrorContext *test_akgl_actor_set_character(void)
{ {
actor *testactor = NULL; akgl_Actor *testactor = NULL;
character *testchar = NULL; akgl_Character *testchar = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, actor_set_character(NULL, "test")); CATCH(errctx, akgl_actor_set_character(NULL, "test"));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Handled\n"); printf("Handled\n");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_actor(&testactor)); CATCH(errctx, akgl_heap_next_actor(&testactor));
CATCH(errctx, actor_initialize(testactor, "test")); CATCH(errctx, akgl_actor_initialize(testactor, "test"));
testactor->layer = 0; testactor->layer = 0;
testactor->updatefunc = &actor_update_noop; testactor->updatefunc = &akgl_actor_update_noop;
testactor->renderfunc = &actor_render_noop; testactor->renderfunc = &akgl_actor_render_noop;
CATCH(errctx, actor_set_character(testactor, "test")); CATCH(errctx, akgl_actor_set_character(testactor, "test"));
} CLEANUP { } CLEANUP {
IGNORE(heap_release_actor(testactor)); IGNORE(akgl_heap_release_actor(testactor));
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Handled\n"); printf("Handled\n");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_actor(&testactor)); CATCH(errctx, akgl_heap_next_actor(&testactor));
CATCH(errctx, heap_next_character(&testchar)); CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, actor_initialize(testactor, "test")); CATCH(errctx, akgl_actor_initialize(testactor, "test"));
testactor->layer = 0; testactor->layer = 0;
testactor->updatefunc = &actor_update_noop; testactor->updatefunc = &akgl_actor_update_noop;
testactor->renderfunc = &actor_render_noop; testactor->renderfunc = &akgl_actor_render_noop;
CATCH(errctx, character_initialize(testchar, "test")); CATCH(errctx, akgl_character_initialize(testchar, "test"));
CATCH(errctx, actor_set_character(testactor, "test")); CATCH(errctx, akgl_actor_set_character(testactor, "test"));
} CLEANUP { } CLEANUP {
IGNORE(heap_release_actor(testactor)); IGNORE(akgl_heap_release_actor(testactor));
IGNORE(heap_release_character(testchar)); IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_actor_manage_children(void) akerr_ErrorContext *test_actor_manage_children(void)
{ {
actor *parent = NULL; akgl_Actor *parent = NULL;
actor *child = NULL; akgl_Actor *child = NULL;
string *tmpstring = NULL; akgl_String *tmpstring = NULL;
int i = 0; int i = 0;
int j = 0; int j = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_init()); CATCH(errctx, akgl_heap_init());
CATCH(errctx, heap_next_string(&tmpstring)); CATCH(errctx, akgl_heap_next_string(&tmpstring));
CATCH(errctx, heap_next_actor(&parent)); CATCH(errctx, akgl_heap_next_actor(&parent));
CATCH(errctx, actor_initialize(parent, "parent")); CATCH(errctx, akgl_actor_initialize(parent, "parent"));
for ( i = 0 ; i < ACTOR_MAX_CHILDREN; i++ ) { for ( i = 0 ; i < AKGL_ACTOR_MAX_CHILDREN; i++ ) {
sprintf((char *)&tmpstring->data, "child %d", i); sprintf((char *)&tmpstring->data, "child %d", i);
CATCH(errctx, heap_next_actor(&child)); CATCH(errctx, akgl_heap_next_actor(&child));
CATCH(errctx, actor_initialize(child, (char *)&tmpstring->data)); CATCH(errctx, akgl_actor_initialize(child, (char *)&tmpstring->data));
CATCH(errctx, parent->addchild(parent, child)); CATCH(errctx, parent->addchild(parent, child));
// Release our claim on the actor so the parent can own the child and kill it // Release our claim on the actor so the parent can own the child and kill it
CATCH(errctx, heap_release_actor(child)); CATCH(errctx, akgl_heap_release_actor(child));
} }
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(tmpstring)); IGNORE(akgl_heap_release_string(tmpstring));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -238,46 +235,46 @@ ErrorContext *test_actor_manage_children(void)
CATCH(errctx, parent->addchild(parent, child)); CATCH(errctx, parent->addchild(parent, child));
} CLEANUP { } CLEANUP {
if ( errctx == NULL ) { if ( errctx == NULL ) {
FAIL(errctx, ERR_BEHAVIOR, "addchild does not throw ERR_RELATIONSHIP when child already has a parent"); FAIL(errctx, AKERR_BEHAVIOR, "addchild does not throw AKERR_RELATIONSHIP when child already has a parent");
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_RELATIONSHIP) { } HANDLE(errctx, AKERR_RELATIONSHIP) {
// Expected behavior // Expected behavior
SDL_Log("addchild throws ERR_RELATIONSHIP when child already has a parent"); SDL_Log("addchild throws AKERR_RELATIONSHIP when child already has a parent");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_actor(&child)); CATCH(errctx, akgl_heap_next_actor(&child));
CATCH(errctx, parent->addchild(parent, child)); CATCH(errctx, parent->addchild(parent, child));
} CLEANUP { } CLEANUP {
if ( errctx == NULL ) { if ( errctx == NULL ) {
FAIL(errctx, ERR_BEHAVIOR, "addchild does not throw ERR_OUTOFBOUNDS when all children already set"); FAIL(errctx, AKERR_BEHAVIOR, "addchild does not throw AKERR_OUTOFBOUNDS when all children already set");
} }
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_OUTOFBOUNDS) { } HANDLE(errctx, AKERR_OUTOFBOUNDS) {
// Expected behavior // Expected behavior
SDL_Log("addchild throws ERR_OUTOFBOUNDS when all children already set"); SDL_Log("addchild throws AKERR_OUTOFBOUNDS when all children already set");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_release_actor(parent)); CATCH(errctx, akgl_heap_release_actor(parent));
// All actor objects on the heap should be empty now // All actor objects on the heap should be empty now
for ( i = 0; i < MAX_HEAP_ACTOR; i++) { for ( i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) {
FAIL_NONZERO_BREAK(errctx, HEAP_ACTOR[i].refcount, ERR_VALUE, "Actor not properly cleared"); FAIL_NONZERO_BREAK(errctx, HEAP_ACTOR[i].refcount, AKERR_VALUE, "Actor not properly cleared");
FAIL_NONZERO_BREAK(errctx, HEAP_ACTOR[i].parent, ERR_VALUE, "Actor not properly cleared"); FAIL_NONZERO_BREAK(errctx, HEAP_ACTOR[i].parent, AKERR_VALUE, "Actor not properly cleared");
for ( j = 0 ; j < ACTOR_MAX_CHILDREN; j++) { for ( j = 0 ; j < AKGL_ACTOR_MAX_CHILDREN; j++) {
if ( HEAP_ACTOR[i].children[j] != NULL ) { if ( HEAP_ACTOR[i].children[j] != NULL ) {
FAIL(errctx, ERR_VALUE, "Actor not properly cleared"); FAIL(errctx, AKERR_VALUE, "Actor not properly cleared");
goto _test_actor_addchild_heaprelease_cleanup; goto _test_actor_addchild_heaprelease_cleanup;
} }
} }
} }
for ( j = 0; j < ACTOR_MAX_CHILDREN; j++) { for ( j = 0; j < AKGL_ACTOR_MAX_CHILDREN; j++) {
sprintf((char *)&tmpstring->data, "child %d", i); sprintf((char *)&tmpstring->data, "child %d", i);
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
SDL_GetPointerProperty(REGISTRY_ACTOR, (char *)&tmpstring->data, NULL), SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, (char *)&tmpstring->data, NULL),
ERR_KEY, AKERR_KEY,
"Child %s was not removed from the registry", "Child %s was not removed from the registry",
(char *)&tmpstring->data); (char *)&tmpstring->data);
} }
@@ -287,22 +284,22 @@ _test_actor_addchild_heaprelease_cleanup:
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_actor(&parent)); CATCH(errctx, akgl_heap_next_actor(&parent));
CATCH(errctx, actor_initialize(parent, "parent")); CATCH(errctx, akgl_actor_initialize(parent, "parent"));
CATCH(errctx, heap_next_actor(&child)); CATCH(errctx, akgl_heap_next_actor(&child));
CATCH(errctx, actor_initialize(child, "child")); CATCH(errctx, akgl_actor_initialize(child, "child"));
// Don't release our claim on the child. The child should not be reclaimed. // Don't release our claim on the child. The child should not be reclaimed.
CATCH(errctx, heap_release_actor(parent)); CATCH(errctx, akgl_heap_release_actor(parent));
FAIL_NONZERO_BREAK(errctx, child->parent, ERR_VALUE, "Child still references released parent"); FAIL_NONZERO_BREAK(errctx, child->parent, AKERR_VALUE, "Child still references released parent");
FAIL_ZERO_BREAK(errctx, child->refcount, ERR_VALUE, "Child prematurely released"); FAIL_ZERO_BREAK(errctx, child->refcount, AKERR_VALUE, "Child prematurely released");
FAIL_NONZERO_BREAK(errctx, strcmp(child->name, "child"), ERR_VALUE, "Child had identity erased"); FAIL_NONZERO_BREAK(errctx, strcmp(child->name, "child"), AKERR_VALUE, "Child had identity erased");
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
(child == SDL_GetPointerProperty(REGISTRY_ACTOR, child->name, NULL)), (child == SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, child->name, NULL)),
ERR_KEY, AKERR_KEY,
"Child prematurely removed from the registry"); "Child prematurely removed from the registry");
// Now we can release the child // Now we can release the child
CATCH(errctx, heap_release_actor(child)); CATCH(errctx, akgl_heap_release_actor(child));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -312,20 +309,20 @@ _test_actor_addchild_heaprelease_cleanup:
int main(void) int main(void)
{ {
actor_updated = 0; akgl_actor_updated = 0;
actor_rendered = 0; akgl_actor_rendered = 0;
UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT; UNHANDLED_ERROR_BEHAVIOR = UNHANDLED_ERROR_EXIT;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, registry_init_actor()); CATCH(errctx, akgl_registry_init_actor());
CATCH(errctx, registry_init_sprite()); CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, registry_init_spritesheet()); CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, registry_init_character()); CATCH(errctx, akgl_registry_init_character());
CATCH(errctx, test_registry_actor_iterator_nullpointers()); CATCH(errctx, test_registry_actor_iterator_nullpointers());
CATCH(errctx, test_registry_actor_iterator_missingactor()); CATCH(errctx, test_registry_actor_iterator_missingactor());
CATCH(errctx, test_registry_actor_iterator_updaterender()); CATCH(errctx, test_registry_actor_iterator_updaterender());
CATCH(errctx, test_actor_set_character()); CATCH(errctx, test_akgl_actor_set_character());
CATCH(errctx, test_actor_manage_children()); CATCH(errctx, test_actor_manage_children());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {

View File

@@ -5,8 +5,8 @@
"sprite_mappings": [ "sprite_mappings": [
{ {
"state": [ "state": [
"ACTOR_STATE_ALIVE", "AKGL_ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_LEFT" "AKGL_ACTOR_STATE_FACE_LEFT"
], ],
"sprite": "testsprite" "sprite": "testsprite"
}, },

View File

@@ -1,31 +1,31 @@
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
int main(void) int main(void)
{ {
int mask = 0; int mask = 0;
BITMASK_ADD(mask, ACTOR_STATE_ALIVE); AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_ALIVE);
if ( mask != ACTOR_STATE_ALIVE ) if ( mask != AKGL_ACTOR_STATE_ALIVE )
return 1; return 1;
BITMASK_ADD(mask, ACTOR_STATE_FACE_LEFT); AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_LEFT);
if ( mask != (ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_LEFT) ) if ( mask != (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT) )
return 1; return 1;
BITMASK_DEL(mask, ACTOR_STATE_ALIVE); AKGL_BITMASK_DEL(mask, AKGL_ACTOR_STATE_ALIVE);
if ( mask != (ACTOR_STATE_FACE_LEFT) ) if ( mask != (AKGL_ACTOR_STATE_FACE_LEFT) )
return 1; return 1;
BITMASK_CLEAR(mask); AKGL_BITMASK_CLEAR(mask);
if ( mask != 0 ) if ( mask != 0 )
return 1; return 1;
BITMASK_ADD(mask, ACTOR_STATE_FACE_LEFT); AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_LEFT);
if ( !(BITMASK_HAS(mask, ACTOR_STATE_FACE_LEFT)) ) if ( !(AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_FACE_LEFT)) )
return 1; return 1;
mask = ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_UP; mask = AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_UP;
BITMASK_DEL(mask, ACTOR_STATE_FACE_ALL); AKGL_BITMASK_DEL(mask, AKGL_ACTOR_STATE_FACE_ALL);
if ( mask != ACTOR_STATE_ALIVE ) if ( mask != AKGL_ACTOR_STATE_ALIVE )
return 1; return 1;
BITMASK_ADD(mask, ACTOR_STATE_MOVING_DOWN); AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_MOVING_DOWN);
BITMASK_ADD(mask, ACTOR_STATE_FACE_DOWN); AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_DOWN);
if ( mask != (ACTOR_STATE_ALIVE | ACTOR_STATE_MOVING_DOWN | ACTOR_STATE_FACE_DOWN) ) if ( mask != (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN) )
return 1; return 1;
return 0; return 0;
} }

View File

@@ -1,186 +1,187 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/character.h> #include <akgl/character.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/heap.h> #include <akgl/error.h>
#include <sdl3game/registry.h> #include <akgl/heap.h>
#include <sdl3game/iterator.h> #include <akgl/registry.h>
#include <akgl/iterator.h>
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
ErrorContext *test_character_initialize() akerr_ErrorContext *test_akgl_character_initialize()
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
character *testchar = NULL; akgl_Character *testchar = NULL;
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_character(&testchar)); CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, character_initialize(testchar, "testchar")); CATCH(errctx, akgl_character_initialize(testchar, "testchar"));
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
SDL_GetPointerProperty(REGISTRY_CHARACTER, "testchar", NULL), SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testchar", NULL),
ERR_KEY, AKERR_KEY,
"Character was not placed in the registry"); "Character was not placed in the registry");
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
strcmp((char *)&testchar->name, "testchar"), strcmp((char *)&testchar->name, "testchar"),
ERR_VALUE, AKERR_VALUE,
"Character was not named properly ('testchar' vs '%s')", "Character was not named properly ('testchar' vs '%s')",
(char *)&testchar->name); (char *)&testchar->name);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testchar->state_sprites, testchar->state_sprites,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Character state sprites map was not initialized"); "Character state sprites map was not initialized");
} CLEANUP { } CLEANUP {
IGNORE(heap_release_character(testchar)); IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_character_sprite_mgmt() akerr_ErrorContext *test_character_sprite_mgmt()
{ {
character *testchar = NULL; akgl_Character *testchar = NULL;
sprite *testsprite = NULL; akgl_Sprite *testsprite = NULL;
sprite *testsprite2 = NULL; akgl_Sprite *testsprite2 = NULL;
sprite *comparesprite = NULL; akgl_Sprite *comparesprite = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_character(&testchar)); CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, sprite_load_json("assets/testsprite.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL); testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite, testsprite,
ERR_KEY, AKERR_KEY,
"Sprite loaded from json but not in registry"); "Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL); testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite2, testsprite2,
ERR_KEY, AKERR_KEY,
"Sprite 2 loaded from json but not in registry"); "Sprite 2 loaded from json but not in registry");
CATCH(errctx, testchar->sprite_add(testchar, testsprite, ACTOR_STATE_ALIVE)); CATCH(errctx, testchar->sprite_add(testchar, testsprite, AKGL_ACTOR_STATE_ALIVE));
CATCH(errctx, testchar->sprite_add(testchar, testsprite2, ACTOR_STATE_DEAD)); CATCH(errctx, testchar->sprite_add(testchar, testsprite2, AKGL_ACTOR_STATE_DEAD));
CATCH(errctx, testchar->sprite_get(testchar, (ACTOR_STATE_ALIVE), &comparesprite)); CATCH(errctx, testchar->sprite_get(testchar, (AKGL_ACTOR_STATE_ALIVE), &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_LEFT"); FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT");
CATCH(errctx, testchar->sprite_get(testchar, ACTOR_STATE_DEAD, &comparesprite)); CATCH(errctx, testchar->sprite_get(testchar, AKGL_ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_DEAD"); FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_DEAD");
} CLEANUP { } CLEANUP {
IGNORE(heap_release_sprite(testsprite)); IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(heap_release_sprite(testsprite2)); IGNORE(akgl_heap_release_sprite(testsprite2));
IGNORE(heap_release_character(testchar)); IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_character_iterate_state_sprites() akerr_ErrorContext *test_character_iterate_state_sprites()
{ {
character *testchar = NULL; akgl_Character *testchar = NULL;
sprite *testsprite = NULL; akgl_Sprite *testsprite = NULL;
sprite *testsprite2 = NULL; akgl_Sprite *testsprite2 = NULL;
iterator opflags = {.flags = ITERATOR_OP_RELEASE, .layerid = 0}; akgl_Iterator opflags = {.flags = AKGL_ITERATOR_OP_RELEASE, .layerid = 0};
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_character(&testchar)); CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, sprite_load_json("assets/testsprite.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL); testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite, testsprite,
ERR_KEY, AKERR_KEY,
"Sprite loaded from json but not in registry"); "Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL); testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite2, testsprite2,
ERR_KEY, AKERR_KEY,
"Sprite 2 loaded from json but not in registry"); "Sprite 2 loaded from json but not in registry");
CATCH(errctx, testchar->sprite_add(testchar, testsprite, ACTOR_STATE_ALIVE)); CATCH(errctx, testchar->sprite_add(testchar, testsprite, AKGL_ACTOR_STATE_ALIVE));
CATCH(errctx, testchar->sprite_add(testchar, testsprite2, ACTOR_STATE_DEAD)); CATCH(errctx, testchar->sprite_add(testchar, testsprite2, AKGL_ACTOR_STATE_DEAD));
SDL_EnumerateProperties(testchar->state_sprites, &character_state_sprites_iterate, &opflags); SDL_EnumerateProperties(testchar->state_sprites, &akgl_character_state_sprites_iterate, &opflags);
// This is called by heap_release_character so we should assume that our property map is being // This is called by akgl_heap_release_character so we should assume that our property map is being
// deleted soon after this. So we don't care if the sprites have been deleted from the sprite_states. // deleted soon after this. So we don't care if the sprites have been deleted from the sprite_states.
// We just want to know they've been released. // We just want to know they've been released.
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
(testsprite->refcount > 1), (testsprite->refcount > 1),
ERR_VALUE, AKERR_VALUE,
"heap_release_sprite not called for testsprite from iterator"); "akgl_heap_release_sprite not called for testsprite from iterator");
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
(testsprite2->refcount > 1), (testsprite2->refcount > 1),
ERR_VALUE, AKERR_VALUE,
"heap_release_sprite not called for testsprite from iterator"); "akgl_heap_release_sprite not called for testsprite from iterator");
} CLEANUP { } CLEANUP {
IGNORE(heap_release_sprite(testsprite)); IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(heap_release_sprite(testsprite2)); IGNORE(akgl_heap_release_sprite(testsprite2));
IGNORE(heap_release_character(testchar)); IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_character_load_json() akerr_ErrorContext *test_akgl_character_load_json()
{ {
character *testcharacter = NULL; akgl_Character *testcharacter = NULL;
sprite *testsprite = NULL; akgl_Sprite *testsprite = NULL;
sprite *testsprite2 = NULL; akgl_Sprite *testsprite2 = NULL;
sprite *comparesprite = NULL; akgl_Sprite *comparesprite = NULL;
int tsrc = 0; int tsrc = 0;
int tsrc2 = 0; int tsrc2 = 0;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_character(&testcharacter)); CATCH(errctx, akgl_heap_next_character(&testcharacter));
CATCH(errctx, sprite_load_json("assets/testsprite.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL); testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite, testsprite,
ERR_KEY, AKERR_KEY,
"Sprite loaded from json but not in registry"); "Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL); testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite2, testsprite2,
ERR_KEY, AKERR_KEY,
"Sprite 2 loaded from json but not in registry"); "Sprite 2 loaded from json but not in registry");
CATCH(errctx, character_load_json("assets/testcharacter.json")); CATCH(errctx, akgl_character_load_json("assets/testcharacter.json"));
testcharacter = SDL_GetPointerProperty(REGISTRY_CHARACTER, "testcharacter", NULL); testcharacter = SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testcharacter", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testcharacter, testcharacter,
ERR_KEY, AKERR_KEY,
"Character loaded from json but not in registry"); "Character loaded from json but not in registry");
CATCH(errctx, testcharacter->sprite_get(testcharacter, (ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_LEFT), &comparesprite)); CATCH(errctx, testcharacter->sprite_get(testcharacter, (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT), &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_ALIVE"); FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_ALIVE");
CATCH(errctx, testcharacter->sprite_get(testcharacter, ACTOR_STATE_DEAD, &comparesprite)); CATCH(errctx, testcharacter->sprite_get(testcharacter, AKGL_ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_DEAD"); FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_DEAD");
FAIL_ZERO_BREAK(errctx, (testcharacter->vx != 0.200000003), ERR_VALUE, "Wrong X velocity for test character"); FAIL_ZERO_BREAK(errctx, (testcharacter->vx != 0.200000003), AKERR_VALUE, "Wrong X velocity for test character");
FAIL_ZERO_BREAK(errctx, (testcharacter->vy != 0.200000003), ERR_VALUE, "Wrong Y velocity for test character"); FAIL_ZERO_BREAK(errctx, (testcharacter->vy != 0.200000003), AKERR_VALUE, "Wrong Y velocity for test character");
// Release our handles on the sprites so the character's heap_release can reduce them to 0 // Release our handles on the sprites so the character's heap_release can reduce them to 0
CATCH(errctx, heap_release_sprite(testsprite)); CATCH(errctx, akgl_heap_release_sprite(testsprite));
CATCH(errctx, heap_release_sprite(testsprite2)); CATCH(errctx, akgl_heap_release_sprite(testsprite2));
tsrc = testsprite->refcount; tsrc = testsprite->refcount;
tsrc2 = testsprite2->refcount; tsrc2 = testsprite2->refcount;
CATCH(errctx, heap_release_character(testcharacter)); CATCH(errctx, akgl_heap_release_character(testcharacter));
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
((testsprite->refcount < tsrc) || (testsprite2->refcount < tsrc2)), ((testsprite->refcount < tsrc) || (testsprite2->refcount < tsrc2)),
ERR_VALUE, AKERR_VALUE,
"character did not reduce reference count of its child sprites when released"); "character did not reduce reference count of its child sprites when released");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -195,19 +196,19 @@ int main(void)
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest"); SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
} }
if (!SDL_CreateWindowAndRenderer("net/aklabs/libsdl3game/test_character", 640, 480, SDL_WINDOW_HIDDEN, &window, &renderer)) { if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_character", 640, 480, SDL_WINDOW_HIDDEN, &window, &renderer)) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
} }
CATCH(errctx, heap_init()); CATCH(errctx, akgl_heap_init());
CATCH(errctx, registry_init()); CATCH(errctx, akgl_registry_init());
CATCH(errctx, test_character_initialize()); CATCH(errctx, test_akgl_character_initialize());
CATCH(errctx, test_character_sprite_mgmt()); CATCH(errctx, test_character_sprite_mgmt());
CATCH(errctx, test_character_iterate_state_sprites()); CATCH(errctx, test_character_iterate_state_sprites());
CATCH(errctx, test_character_load_json()); CATCH(errctx, test_akgl_character_load_json());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

125
tests/charviewer.c Normal file
View File

@@ -0,0 +1,125 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_properties.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <akerror.h>
#include <akgl/assets.h>
#include <akgl/iterator.h>
#include <akgl/tilemap.h>
#include <akgl/heap.h>
#include <akgl/game.h>
#include <akgl/controller.h>
#include <akgl/draw.h>
#include <akgl/sprite.h>
#include <akgl/actor.h>
#include <akgl/registry.h>
#include <akgl/error.h>
int numsprites = 8;
char *spritepaths[] = {
"assets/sprites/little_guy_walking_left.json",
"assets/sprites/little_guy_walking_right.json",
"assets/sprites/little_guy_walking_up.json",
"assets/sprites/little_guy_walking_down.json",
"assets/sprites/little_guy_facing_left.json",
"assets/sprites/little_guy_facing_right.json",
"assets/sprites/little_guy_facing_up.json",
"assets/sprites/little_guy_facing_down.json"
};
int main(void)
{
PREPARE_ERROR(errctx);
SDL3GControlMap *controlmap;
actor *actorptr = NULL;
ATTEMPT {
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
}
if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_sprite", 640, 480, 0, &window, &renderer)) {
FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
}
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init());
strcpy((char *)&game.name, "charviewer");
strcpy((char *)&game.version, "0.0.1");
strcpy((char *)&game.uri, "net.aklabs.libakgl.charviewer");
game.screenwidth = 640;
game.screenheight = 480;
CATCH(errctx, akgl_GAME_init());
for ( int i = 0; i < numsprites ; i++) {
CATCH(errctx, akgl_sprite_load_json(spritepaths[i]));
}
CATCH(errctx, akgl_character_load_json("assets/characters/littleguy.json"));
CATCH(errctx, akgl_heap_next_actor(&actorptr));
CATCH(errctx, akgl_actor_initialize((actor *)actorptr, "player"));
actorptr->basechar = SDL_GetPointerProperty(
AKGL_REGISTRY_CHARACTER,
"little guy",
NULL);
FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKERR_REGISTRY, "Can't load character 'little guy' from the registry");
actorptr->movement_controls_face = false;
actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT);
actorptr->x = 320;
actorptr->y = 240;
actorptr->visible = true;
// set up the control map
controlmap = &GAME_ControlMaps[0];
controlmap->kbid = 0;
controlmap->target = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
// Move down
controlmap->controls[0].key = SDLK_DOWN;
//controlmap->controls[0].target_state_gate = AKGL_ACTOR_STATE_MOVING_DOWN;
controlmap->controls[0].target_add_state_on = AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN;
controlmap->controls[0].target_del_state_on = AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_FACE_ALL;
controlmap->controls[0].target_del_state_off = AKGL_ACTOR_STATE_MOVING_DOWN;
controlmap->controls[0].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[0].event_off = SDL_EVENT_KEY_UP;
// Move up
controlmap->controls[1].key = SDLK_UP;
//controlmap->controls[1].target_state_gate = AKGL_ACTOR_STATE_MOVING_UP;
controlmap->controls[1].target_add_state_on = AKGL_ACTOR_STATE_MOVING_UP | AKGL_ACTOR_STATE_FACE_UP;
controlmap->controls[1].target_del_state_on = AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_ALL;
controlmap->controls[1].target_del_state_off = AKGL_ACTOR_STATE_MOVING_UP;
controlmap->controls[1].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[1].event_off = SDL_EVENT_KEY_UP;
// Move left
controlmap->controls[2].key = SDLK_LEFT;
//controlmap->controls[2].target_state_gate = AKGL_ACTOR_STATE_MOVING_LEFT;
controlmap->controls[2].target_add_state_on = AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_FACE_LEFT;
controlmap->controls[2].target_del_state_on = AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_FACE_ALL;
controlmap->controls[2].target_del_state_off = AKGL_ACTOR_STATE_MOVING_LEFT;
controlmap->controls[2].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[2].event_off = SDL_EVENT_KEY_UP;
// Move right
controlmap->controls[3].key = SDLK_RIGHT;
//controlmap->controls[3].target_state_gate = AKGL_ACTOR_STATE_MOVING_RIGHT;
controlmap->controls[3].target_add_state_on = AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_FACE_RIGHT;
controlmap->controls[3].target_del_state_on = AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_FACE_ALL;
controlmap->controls[3].target_del_state_off = AKGL_ACTOR_STATE_MOVING_RIGHT;
controlmap->controls[3].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[3].event_off = SDL_EVENT_KEY_UP;
} CLEANUP {
} PROCESS(errctx) {
} HANDLE_DEFAULT(errctx) {
LOG_ERROR(errctx);
return 1;
} FINISH_NORETURN(errctx);
return 0;
}

View File

@@ -1,9 +1,9 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <stdlib.h> #include <stdlib.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
typedef ErrorContext *(*RegistryFuncPtr)(void); typedef akerr_ErrorContext *(*RegistryFuncPtr)(void);
void *sdl_calloc_always_fails(size_t a, size_t b) void *sdl_calloc_always_fails(size_t a, size_t b)
{ {
@@ -11,7 +11,7 @@ void *sdl_calloc_always_fails(size_t a, size_t b)
return NULL; return NULL;
} }
ErrorContext *test_registry_init(RegistryFuncPtr funcptr) akerr_ErrorContext *test_akgl_registry_init(RegistryFuncPtr funcptr)
{ {
SDL_malloc_func malloc_func; SDL_malloc_func malloc_func;
SDL_calloc_func calloc_func; SDL_calloc_func calloc_func;
@@ -43,41 +43,41 @@ ErrorContext *test_registry_init(RegistryFuncPtr funcptr)
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
FAIL_RETURN(errctx, ERR_BEHAVIOR, "SDL memory allocator fails but registry reports successful property creation"); FAIL_RETURN(errctx, AKERR_BEHAVIOR, "SDL memory allocator fails but registry reports successful property creation");
} }
ErrorContext *test_registry_init_creation_failures(void) akerr_ErrorContext *test_akgl_registry_init_creation_failures(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_actor)); CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_actor));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n"); printf("Sucess\n");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_sprite)); CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_sprite));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n"); printf("Sucess\n");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_spritesheet)); CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_spritesheet));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n"); printf("Sucess\n");
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_character)); CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_character));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n"); printf("Sucess\n");
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
@@ -87,7 +87,7 @@ int main(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_registry_init_creation_failures()); CATCH(errctx, test_akgl_registry_init_creation_failures());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

View File

@@ -3,45 +3,46 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/util.h> #include <akgl/util.h>
#include <akgl/error.h>
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
ErrorContext *test_spritesheet_initialize(void) akerr_ErrorContext *test_akgl_spritesheet_initialize(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
spritesheet *sheet = NULL; akgl_SpriteSheet *sheet = NULL;
SDL_Texture *image = NULL; SDL_Texture *image = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
// Does the image file get loaded? // Does the image file get loaded?
// Is the image file loaded correctly? (Surface comparison) // Is the image file loaded correctly? (Surface comparison)
// Is the spritesheet in the registry? // Is the spritesheet in the registry?
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_spritesheet(&sheet)); CATCH(errctx, akgl_heap_next_spritesheet(&sheet));
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png"); snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, spritesheet_initialize(sheet, 48, 48, "assets/spritesheet.png")); CATCH(errctx, akgl_spritesheet_initialize(sheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, sheet->texture, ERR_VALUE, "spritesheet_initialize failed to load the sprite texture"); FAIL_ZERO_BREAK(errctx, sheet->texture, AKERR_VALUE, "akgl_spritesheet_initialize failed to load the sprite texture");
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
((sheet->texture->w != 576) || (sheet->texture->h != 384)), ((sheet->texture->w != 576) || (sheet->texture->h != 384)),
ERR_VALUE, AKERR_VALUE,
"Loaded texture was not the correct size"); "Loaded texture was not the correct size");
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png"); snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
image = IMG_LoadTexture(renderer, (char *)&tmpstr->data); image = IMG_LoadTexture(renderer, (char *)&tmpstr->data);
FAIL_ZERO_BREAK(errctx, image, ERR_SDL, "Failed to load comparison image"); FAIL_ZERO_BREAK(errctx, image, AKGL_ERR_SDL, "Failed to load comparison image");
CATCH( CATCH(
errctx, errctx,
render_and_compare( akgl_render_and_compare(
sheet->texture, sheet->texture,
image, image,
0, 0, 576, 384, 0, 0, 576, 384,
@@ -50,13 +51,13 @@ ErrorContext *test_spritesheet_initialize(void)
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
SDL_GetPointerProperty(REGISTRY_SPRITESHEET, "assets/spritesheet.png", NULL), SDL_GetPointerProperty(AKGL_REGISTRY_SPRITESHEET, "assets/spritesheet.png", NULL),
ERR_KEY, AKERR_KEY,
"Spritesheet was not placed in the registry"); "Spritesheet was not placed in the registry");
} CLEANUP { } CLEANUP {
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
IGNORE(heap_release_spritesheet(sheet)); IGNORE(akgl_heap_release_spritesheet(sheet));
if ( image != NULL ) if ( image != NULL )
SDL_DestroyTexture(image); SDL_DestroyTexture(image);
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -64,48 +65,48 @@ ErrorContext *test_spritesheet_initialize(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_sprite_initialize(void) akerr_ErrorContext *test_akgl_sprite_initialize(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
spritesheet *testsheet = NULL; akgl_SpriteSheet *testsheet = NULL;
sprite *testsprite = NULL; akgl_Sprite *testsprite = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
// Does the sprite get loaded? // Does the sprite get loaded?
// Do all frames of the sprite get loaded? // Do all frames of the sprite get loaded?
// Are all the frames of the sprite what we expect? (Surface comparison) // Are all the frames of the sprite what we expect? (Surface comparison)
// Is the sprite added to the registry? // Is the sprite added to the registry?
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_spritesheet(&testsheet)); CATCH(errctx, akgl_heap_next_spritesheet(&testsheet));
CATCH(errctx, heap_next_sprite(&testsprite)); CATCH(errctx, akgl_heap_next_sprite(&testsprite));
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png"); snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, spritesheet_initialize(testsheet, 48, 48, "assets/spritesheet.png")); CATCH(errctx, akgl_spritesheet_initialize(testsheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, testsheet, ERR_VALUE, "spritesheet_initialize failed"); FAIL_ZERO_BREAK(errctx, testsheet, AKERR_VALUE, "akgl_spritesheet_initialize failed");
CATCH(errctx, sprite_initialize(testsprite, "test", testsheet)); CATCH(errctx, akgl_sprite_initialize(testsprite, "test", testsheet));
FAIL_NONZERO_BREAK(errctx, (testsprite->sheet != testsheet), ERR_VALUE, "Initialized sprite uses wrong sheet"); FAIL_NONZERO_BREAK(errctx, (testsprite->sheet != testsheet), AKERR_VALUE, "Initialized sprite uses wrong sheet");
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
SDL_GetPointerProperty(REGISTRY_SPRITE, "test", NULL), SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "test", NULL),
ERR_KEY, AKERR_KEY,
"Sprite was not placed in the registry"); "Sprite was not placed in the registry");
} CLEANUP { } CLEANUP {
IGNORE(heap_release_sprite(testsprite)); IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_sprite_load_json(void) akerr_ErrorContext *test_akgl_sprite_load_json(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
sprite *testsprite = NULL; akgl_Sprite *testsprite = NULL;
sprite *testsprite2 = NULL; akgl_Sprite *testsprite2 = NULL;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
SDL_Texture *image = NULL; SDL_Texture *image = NULL;
// Does the sprite get loaded? // Does the sprite get loaded?
@@ -113,36 +114,36 @@ ErrorContext *test_sprite_load_json(void)
// Are all the frames of the sprite what we expect? (Surface comparison) // Are all the frames of the sprite what we expect? (Surface comparison)
// Is the sprite added to the registry? // Is the sprite added to the registry?
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, sprite_load_json("assets/testsprite.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL); testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite, testsprite,
ERR_KEY, AKERR_KEY,
"sprite_load_json succeeds but sprite is not placed in the registry"); "akgl_sprite_load_json succeeds but sprite is not placed in the registry");
FAIL_ZERO_BREAK(errctx, (testsprite->width == 48), ERR_VALUE, "width incorrect (48 : %d)", testsprite->width); FAIL_ZERO_BREAK(errctx, (testsprite->width == 48), AKERR_VALUE, "width incorrect (48 : %d)", testsprite->width);
FAIL_ZERO_BREAK(errctx, (testsprite->height == 48), ERR_VALUE, "height incorrect (48 : %d)", testsprite->height); FAIL_ZERO_BREAK(errctx, (testsprite->height == 48), AKERR_VALUE, "height incorrect (48 : %d)", testsprite->height);
FAIL_ZERO_BREAK(errctx, (testsprite->speed == 100), ERR_VALUE, "speed incorrect (100 : %d)", testsprite->speed); FAIL_ZERO_BREAK(errctx, (testsprite->speed == 100000000), AKERR_VALUE, "speed incorrect (100 : %d)", testsprite->speed);
FAIL_ZERO_BREAK(errctx, (testsprite->loop == true), ERR_VALUE, "loop incorrect (1 : %d)", testsprite->loop); FAIL_ZERO_BREAK(errctx, (testsprite->loop == true), AKERR_VALUE, "loop incorrect (1 : %d)", testsprite->loop);
FAIL_ZERO_BREAK(errctx, (testsprite->loopReverse == true), ERR_VALUE, "loopReverse incorrect (1 : %d)", testsprite->loopReverse); FAIL_ZERO_BREAK(errctx, (testsprite->loopReverse == true), AKERR_VALUE, "loopReverse incorrect (1 : %d)", testsprite->loopReverse);
FAIL_ZERO_BREAK(errctx, (testsprite->frames == 3), ERR_VALUE, "frame count incorrect (3 : %d)", testsprite->frames); FAIL_ZERO_BREAK(errctx, (testsprite->frames == 3), AKERR_VALUE, "frame count incorrect (3 : %d)", testsprite->frames);
FAIL_ZERO_BREAK(errctx, (testsprite->frameids[0] == 12), ERR_VALUE, "frameids[0] incorrect (12 : %d)", testsprite->frameids[0]); FAIL_ZERO_BREAK(errctx, (testsprite->frameids[0] == 12), AKERR_VALUE, "frameids[0] incorrect (12 : %d)", testsprite->frameids[0]);
FAIL_ZERO_BREAK(errctx, (testsprite->frameids[1] == 13), ERR_VALUE, "frameids[1] incorrect (13 : %d)", testsprite->frameids[1]); FAIL_ZERO_BREAK(errctx, (testsprite->frameids[1] == 13), AKERR_VALUE, "frameids[1] incorrect (13 : %d)", testsprite->frameids[1]);
FAIL_ZERO_BREAK(errctx, (testsprite->frameids[2] == 14), ERR_VALUE, "frameids[2] incorrect (14 : %d)", testsprite->frameids[2]); FAIL_ZERO_BREAK(errctx, (testsprite->frameids[2] == 14), AKERR_VALUE, "frameids[2] incorrect (14 : %d)", testsprite->frameids[2]);
FAIL_NONZERO_BREAK(errctx, strcmp(testsprite->name, "testsprite"), ERR_VALUE, "name incorrect (testsprite : %s)", (char *)testsprite->name); FAIL_NONZERO_BREAK(errctx, strcmp(testsprite->name, "testsprite"), AKERR_VALUE, "name incorrect (testsprite : %s)", (char *)testsprite->name);
// Is it using the right spritesheet? // Is it using the right spritesheet?
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png"); snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
image = IMG_LoadTexture(renderer, (char *)&tmpstr->data); image = IMG_LoadTexture(renderer, (char *)&tmpstr->data);
FAIL_ZERO_BREAK(errctx, image, ERR_SDL, "Failed to load comparison image"); FAIL_ZERO_BREAK(errctx, image, AKGL_ERR_SDL, "Failed to load comparison image");
CATCH( CATCH(
errctx, errctx,
render_and_compare( akgl_render_and_compare(
testsprite->sheet->texture, testsprite->sheet->texture,
image, image,
0, 0, 576, 384, 0, 0, 576, 384,
@@ -151,29 +152,29 @@ ErrorContext *test_sprite_load_json(void)
); );
// If we load a second sprite using the same sheet name, do they use the same sheet in memory? // If we load a second sprite using the same sheet name, do they use the same sheet in memory?
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testsprite2.json"); snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testsprite2.json");
CATCH(errctx, sprite_load_json("assets/testsprite2.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL); testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testsprite, testsprite,
ERR_KEY, AKERR_KEY,
"sprite_load_json succeeds but second sprite is not placed in the registry"); "akgl_sprite_load_json succeeds but second sprite is not placed in the registry");
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
(testsprite->sheet == testsprite2->sheet), (testsprite->sheet == testsprite2->sheet),
ERR_VALUE, AKERR_VALUE,
"Previously loaded spritesheets are not reused"); "Previously loaded spritesheets are not reused");
} CLEANUP { } CLEANUP {
if ( testsprite != NULL ) { if ( testsprite != NULL ) {
IGNORE(heap_release_sprite(testsprite)); IGNORE(akgl_heap_release_sprite(testsprite));
} }
if ( testsprite2 != NULL ) { if ( testsprite2 != NULL ) {
IGNORE(heap_release_sprite(testsprite2)); IGNORE(akgl_heap_release_sprite(testsprite2));
} }
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
if ( image != NULL ) if ( image != NULL )
SDL_DestroyTexture(image); SDL_DestroyTexture(image);
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -190,20 +191,20 @@ int main(void)
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest"); SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
} }
if (!SDL_CreateWindowAndRenderer("net/aklabs/libsdl3game/test_sprite", 640, 480, 0, &window, &renderer)) { if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_sprite", 640, 480, 0, &window, &renderer)) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
} }
CATCH(errctx, heap_init()); CATCH(errctx, akgl_heap_init());
CATCH(errctx, registry_init_sprite()); CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, registry_init_spritesheet()); CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, test_spritesheet_initialize()); CATCH(errctx, test_akgl_spritesheet_initialize());
CATCH(errctx, test_sprite_initialize()); CATCH(errctx, test_akgl_sprite_initialize());
CATCH(errctx, test_sprite_load_json()); CATCH(errctx, test_akgl_sprite_load_json());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

View File

@@ -1,18 +1,18 @@
#include <string.h> #include <string.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/staticstring.h> #include <akgl/staticstring.h>
void reset_string_heap(void); void reset_string_heap(void);
ErrorContext *test_fresh_heap_gives_strings(void) akerr_ErrorContext *test_fresh_heap_gives_strings(void)
{ {
string *ptr = NULL; akgl_String *ptr = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for ( int i = 0; i < MAX_HEAP_STRING - 1; i++ ) { for ( int i = 0; i < AKGL_MAX_HEAP_STRING - 1; i++ ) {
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&ptr)); CATCH(errctx, akgl_heap_next_string(&ptr));
} CLEANUP { } CLEANUP {
reset_string_heap(); reset_string_heap();
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -22,73 +22,73 @@ ErrorContext *test_fresh_heap_gives_strings(void)
return 0; return 0;
} }
ErrorContext *test_string_heap_error_when_no_strings_left(void) akerr_ErrorContext *test_string_heap_error_when_no_strings_left(void)
{ {
string *ptr; akgl_String *ptr;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
for ( int i = 0; i < MAX_HEAP_STRING; i++ ) { for ( int i = 0; i < AKGL_MAX_HEAP_STRING; i++ ) {
HEAP_STRING[i].refcount = 1; HEAP_STRING[i].refcount = 1;
} }
for ( int i = 0; i < MAX_HEAP_STRING - 1; i++ ) { for ( int i = 0; i < AKGL_MAX_HEAP_STRING - 1; i++ ) {
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&ptr)); CATCH(errctx, akgl_heap_next_string(&ptr));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
return 0; return 0;
} FINISH(errctx, true); } FINISH(errctx, true);
} }
FAIL_RETURN(errctx, ERR_OUTOFBOUNDS, "Expected ERR_NULLPOINTER when accessing beyond string heap bounds"); FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Expected AKERR_NULLPOINTER when accessing beyond string heap bounds");
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_string_heap_honors_refcount(void) akerr_ErrorContext *test_string_heap_honors_refcount(void)
{ {
string *firstptr = &HEAP_STRING[0]; akgl_String *firstptr = &HEAP_STRING[0];
string *secondptr = &HEAP_STRING[1]; akgl_String *secondptr = &HEAP_STRING[1];
string *testptr = NULL; akgl_String *testptr = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&testptr)); CATCH(errctx, akgl_heap_next_string(&testptr));
if ( testptr != firstptr ) { if ( testptr != firstptr ) {
FAIL_RETURN( FAIL_RETURN(
errctx, errctx,
ERR_VALUE, AKERR_VALUE,
"Expected testptr to equal (HEAP_STRING[0] = %p) but got %p", "Expected testptr to equal (HEAP_STRING[0] = %p) but got %p",
firstptr, firstptr,
testptr testptr
); );
} }
CATCH(errctx, string_initialize(testptr, NULL)); CATCH(errctx, akgl_string_initialize(testptr, NULL));
if ( testptr->refcount == 0 ) { if ( testptr->refcount == 0 ) {
FAIL_RETURN(errctx, ERR_VALUE, "Expected string reference count to be nonzero but got 0"); FAIL_RETURN(errctx, AKERR_VALUE, "Expected string reference count to be nonzero but got 0");
} }
if ( testptr != firstptr ) { if ( testptr != firstptr ) {
FAIL_RETURN( FAIL_RETURN(
errctx, errctx,
ERR_VALUE, AKERR_VALUE,
"Expected testptr to equal (HEAP_STRING[1] = %p) but got %p", "Expected testptr to equal (HEAP_STRING[1] = %p) but got %p",
secondptr, secondptr,
testptr testptr
); );
} }
CATCH(errctx, heap_next_string(&testptr)); CATCH(errctx, akgl_heap_next_string(&testptr));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_strcpy_to_all_strings_no_segfault(void) akerr_ErrorContext *test_strcpy_to_all_strings_no_segfault(void)
{ {
char copybuf[MAX_STRING_LENGTH]; char copybuf[AKGL_MAX_STRING_LENGTH];
string *ptr; akgl_String *ptr;
memset((void *)&copybuf, 'a', MAX_STRING_LENGTH); memset((void *)&copybuf, 'a', AKGL_MAX_STRING_LENGTH);
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
for ( int i = 0; i < MAX_HEAP_STRING - 1; i++ ) { for ( int i = 0; i < AKGL_MAX_HEAP_STRING - 1; i++ ) {
CATCH(errctx, heap_next_string(&ptr)); CATCH(errctx, akgl_heap_next_string(&ptr));
strncpy(ptr->data, (char *)&copybuf, MAX_STRING_LENGTH); strncpy(ptr->data, (char *)&copybuf, AKGL_MAX_STRING_LENGTH);
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -96,22 +96,22 @@ ErrorContext *test_strcpy_to_all_strings_no_segfault(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_string_initialize(void) akerr_ErrorContext *test_akgl_string_initialize(void)
{ {
string *ptr; akgl_String *ptr;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&ptr)); CATCH(errctx, akgl_heap_next_string(&ptr));
CATCH(errctx, string_initialize(ptr, NULL)); CATCH(errctx, akgl_string_initialize(ptr, NULL));
FAIL_NONZERO_BREAK(errctx, ptr->data[0], ERR_VALUE, "Expected empty zero length string data"); FAIL_NONZERO_BREAK(errctx, ptr->data[0], AKERR_VALUE, "Expected empty zero length string data");
CATCH(errctx, heap_release_string(ptr)); CATCH(errctx, akgl_heap_release_string(ptr));
CATCH(errctx, heap_next_string(&ptr)); CATCH(errctx, akgl_heap_next_string(&ptr));
CATCH(errctx, string_initialize(ptr, "Test value")); CATCH(errctx, akgl_string_initialize(ptr, "Test value"));
FAIL_NONZERO_BREAK(errctx, strcmp((char *)&ptr->data, "Test value"), ERR_VALUE, "Expected 'Test value', got %s", (char *)&ptr->data); FAIL_NONZERO_BREAK(errctx, strcmp((char *)&ptr->data, "Test value"), AKERR_VALUE, "Expected 'Test value', got %s", (char *)&ptr->data);
CATCH(errctx, heap_release_string(NULL)); CATCH(errctx, akgl_heap_release_string(NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Failure to properly handle NULL pointer"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Failure to properly handle NULL pointer");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -122,8 +122,8 @@ ErrorContext *test_string_initialize(void)
void reset_string_heap(void) void reset_string_heap(void)
{ {
for ( int i = 0; i < MAX_HEAP_STRING; i++ ) { for ( int i = 0; i < AKGL_MAX_HEAP_STRING; i++ ) {
memset(&HEAP_STRING[i], 0x00, sizeof(string)); memset(&HEAP_STRING[i], 0x00, sizeof(akgl_String));
} }
} }
@@ -144,8 +144,8 @@ int main(void)
printf("test_strcpy_to_all_strings_no_segfault ...\n"); printf("test_strcpy_to_all_strings_no_segfault ...\n");
test_strcpy_to_all_strings_no_segfault(); test_strcpy_to_all_strings_no_segfault();
reset_string_heap(); reset_string_heap();
printf("test_string_initialize....\n"); printf("test_akgl_string_initialize....\n");
test_string_initialize(); test_akgl_string_initialize();
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

View File

@@ -2,37 +2,37 @@
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <jansson.h> #include <jansson.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/util.h> #include <akgl/util.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <sdl3game/tilemap.h> #include <akgl/tilemap.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/json_helpers.h> #include <akgl/json_helpers.h>
ErrorContext *test_tilemap_get_json_tilemap_property(void) akerr_ErrorContext *test_tilemap_akgl_get_json_tilemap_property(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *jsondoc = NULL; json_t *jsondoc = NULL;
json_error_t jsonerr; json_error_t jsonerr;
string *tmpstr = NULL; akgl_String *tmpstr = NULL;
int propnum; int propnum;
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&tmpstr));
snprintf( snprintf(
(char *)&tmpstr->data, (char *)&tmpstr->data,
MAX_STRING_LENGTH, AKGL_MAX_STRING_LENGTH,
"%s%s", "%s%s",
SDL_GetBasePath(), SDL_GetBasePath(),
"assets/snippets/test_tilemap_get_json_tilemap_property.json" "assets/snippets/test_tilemap_akgl_get_json_tilemap_property.json"
); );
jsondoc = json_load_file((char *)&tmpstr->data, 0, (json_error_t *)&jsonerr); jsondoc = json_load_file((char *)&tmpstr->data, 0, (json_error_t *)&jsonerr);
FAIL_ZERO_BREAK(errctx, jsondoc, ERR_NULLPOINTER, "Failure loading json fixture: %s", (char *)jsonerr.text); FAIL_ZERO_BREAK(errctx, jsondoc, AKERR_NULLPOINTER, "Failure loading json fixture: %s", (char *)jsonerr.text);
CATCH( CATCH(
errctx, errctx,
get_json_properties_string( akgl_get_json_properties_string(
jsondoc, jsondoc,
"character", "character",
&tmpstr &tmpstr
@@ -41,13 +41,13 @@ ErrorContext *test_tilemap_get_json_tilemap_property(void)
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
strcmp((char *)&tmpstr->data, "testcharacter"), strcmp((char *)&tmpstr->data, "testcharacter"),
ERR_VALUE, AKERR_VALUE,
"Incorrect value loaded from property `character` (`testcharacter` vs `%s`)", "Incorrect value loaded from property `character` (`testcharacter` vs `%s`)",
(char *)&tmpstr->data (char *)&tmpstr->data
); );
CATCH( CATCH(
errctx, errctx,
get_json_properties_integer( akgl_get_json_properties_integer(
jsondoc, jsondoc,
"state", "state",
&propnum &propnum
@@ -56,13 +56,13 @@ ErrorContext *test_tilemap_get_json_tilemap_property(void)
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
(propnum != 6), (propnum != 6),
ERR_VALUE, AKERR_VALUE,
"Incorrect value loaded from property `state` (6 vs %d)", "Incorrect value loaded from property `state` (6 vs %d)",
propnum propnum
); );
} CLEANUP { } CLEANUP {
if ( tmpstr != NULL ) { if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr)); IGNORE(akgl_heap_release_string(tmpstr));
} }
if ( jsondoc != NULL ) { if ( jsondoc != NULL ) {
json_decref(jsondoc); json_decref(jsondoc);
@@ -72,7 +72,7 @@ ErrorContext *test_tilemap_get_json_tilemap_property(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_compute_tileset_offsets(void) akerr_ErrorContext *test_akgl_tilemap_compute_tileset_offsets(void)
{ {
int comparison_values[8] = { int comparison_values[8] = {
0, // Tile 0 X 0, // Tile 0 X
@@ -96,7 +96,7 @@ ErrorContext *test_tilemap_compute_tileset_offsets(void)
}; };
int i = 0; int i = 0;
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
gamemap.tilesets[0].tilecount = 4; gamemap.tilesets[0].tilecount = 4;
gamemap.tilesets[0].columns = 2; gamemap.tilesets[0].columns = 2;
gamemap.tilesets[0].firstgid = 1; gamemap.tilesets[0].firstgid = 1;
@@ -110,13 +110,13 @@ ErrorContext *test_tilemap_compute_tileset_offsets(void)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, tilemap_compute_tileset_offsets(&gamemap, 0)); CATCH(errctx, akgl_tilemap_compute_tileset_offsets(&gamemap, 0));
// Tile 0 X offset // Tile 0 X offset
for ( i = 0; i < 8; i++ ) { for ( i = 0; i < 8; i++ ) {
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
(*comparison_ptrs[i] != comparison_values[i]), (*comparison_ptrs[i] != comparison_values[i]),
ERR_VALUE, AKERR_VALUE,
"Tile offset incorrectly calculated for index %d (%d vs %d)", "Tile offset incorrectly calculated for index %d (%d vs %d)",
i, i,
*comparison_ptrs[i], *comparison_ptrs[i],
@@ -129,44 +129,44 @@ ErrorContext *test_tilemap_compute_tileset_offsets(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_load_layer_objects(void) akerr_ErrorContext *test_akgl_tilemap_load_layer_objects(void)
{ {
string *pathstr; akgl_String *pathstr;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *doc = NULL; json_t *doc = NULL;
json_t *layers = NULL; json_t *layers = NULL;
json_t *objectlayer = NULL; json_t *objectlayer = NULL;
json_error_t errdata; json_error_t errdata;
actor *testactor; akgl_Actor *testactor;
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&pathstr)); CATCH(errctx, akgl_heap_next_string(&pathstr));
snprintf((char *)&pathstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj"); snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
doc = json_load_file((char *)&pathstr->data, 0, &errdata); doc = json_load_file((char *)&pathstr->data, 0, &errdata);
FAIL_ZERO_BREAK(errctx, doc, ERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text); FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
CATCH(errctx, get_json_array_value(doc, "layers", &layers)); CATCH(errctx, akgl_get_json_array_value(doc, "layers", &layers));
CATCH(errctx, get_json_array_index_object(layers, 1, &objectlayer)); CATCH(errctx, akgl_get_json_array_index_object(layers, 1, &objectlayer));
CATCH(errctx, tilemap_load_layer_objects(&gamemap, objectlayer, 1)); CATCH(errctx, akgl_tilemap_load_layer_objects(&gamemap, objectlayer, 1));
testactor = SDL_GetPointerProperty(REGISTRY_ACTOR, "testactor", NULL); testactor = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "testactor", NULL);
FAIL_ZERO_BREAK( FAIL_ZERO_BREAK(
errctx, errctx,
testactor, testactor,
ERR_NULLPOINTER, AKERR_NULLPOINTER,
"Test Actor was not loaded from the test map" "Test Actor was not loaded from the test map"
); );
if ( (testactor->basechar != SDL_GetPointerProperty(REGISTRY_CHARACTER, "testcharacter", NULL)) || if ( (testactor->basechar != SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testcharacter", NULL)) ||
(testactor->layer != 1) || (testactor->layer != 1) ||
(testactor->state != 6) || (testactor->state != 6) ||
(testactor->visible != true) || (testactor->visible != true) ||
(testactor->x != 16) || (testactor->x != 16) ||
(testactor->y != 16) ) { (testactor->y != 16) ) {
FAIL_BREAK(errctx, ERR_VALUE, "Test actor was loaded with incorrect values (check gdb)"); FAIL_BREAK(errctx, AKERR_VALUE, "Test actor was loaded with incorrect values (check gdb)");
} }
} CLEANUP { } CLEANUP {
if ( pathstr != NULL ) { if ( pathstr != NULL ) {
IGNORE(heap_release_string(pathstr)); IGNORE(akgl_heap_release_string(pathstr));
} }
if ( doc != NULL ) { if ( doc != NULL ) {
json_decref(doc); json_decref(doc);
@@ -176,9 +176,9 @@ ErrorContext *test_tilemap_load_layer_objects(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_load_layer_tile(void) akerr_ErrorContext *test_akgl_tilemap_load_layer_tile(void)
{ {
string *pathstr; akgl_String *pathstr;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *doc = NULL; json_t *doc = NULL;
json_t *layers = NULL; json_t *layers = NULL;
@@ -186,26 +186,26 @@ ErrorContext *test_tilemap_load_layer_tile(void)
json_t *tiledata = NULL; json_t *tiledata = NULL;
json_error_t errdata; json_error_t errdata;
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&pathstr)); CATCH(errctx, akgl_heap_next_string(&pathstr));
snprintf((char *)&pathstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj"); snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
doc = json_load_file((char *)&pathstr->data, 0, &errdata); doc = json_load_file((char *)&pathstr->data, 0, &errdata);
FAIL_ZERO_BREAK(errctx, doc, ERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text); FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
CATCH(errctx, get_json_array_value(doc, "layers", &layers)); CATCH(errctx, akgl_get_json_array_value(doc, "layers", &layers));
CATCH(errctx, get_json_array_index_object(layers, 0, &tilelayer)); CATCH(errctx, akgl_get_json_array_index_object(layers, 0, &tilelayer));
CATCH(errctx, get_json_array_value(tilelayer, "data", &tiledata)); CATCH(errctx, akgl_get_json_array_value(tilelayer, "data", &tiledata));
CATCH(errctx, tilemap_load_layer_tile(&gamemap, tilelayer, 0)); CATCH(errctx, akgl_tilemap_load_layer_tile(&gamemap, tilelayer, 0));
if ( (gamemap.layers[0].data[0] != 1) || if ( (gamemap.layers[0].data[0] != 1) ||
(gamemap.layers[0].data[1] != 2) || (gamemap.layers[0].data[1] != 2) ||
(gamemap.layers[0].data[2] != 3) || (gamemap.layers[0].data[2] != 3) ||
(gamemap.layers[0].data[3] != 4) ) { (gamemap.layers[0].data[3] != 4) ) {
FAIL_BREAK(errctx, ERR_VALUE, "Test tilemap layer 0 tiles loaded with incorrect values (check gdb)"); FAIL_BREAK(errctx, AKERR_VALUE, "Test tilemap layer 0 tiles loaded with incorrect values (check gdb)");
} }
} CLEANUP { } CLEANUP {
if ( pathstr != NULL ) { if ( pathstr != NULL ) {
IGNORE(heap_release_string(pathstr)); IGNORE(akgl_heap_release_string(pathstr));
} }
if ( doc != NULL ) { if ( doc != NULL ) {
json_decref(doc); json_decref(doc);
@@ -215,26 +215,26 @@ ErrorContext *test_tilemap_load_layer_tile(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_load_layers(void) akerr_ErrorContext *test_akgl_tilemap_load_layers(void)
{ {
string *pathstr; akgl_String *pathstr;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *doc = NULL; json_t *doc = NULL;
json_error_t errdata; json_error_t errdata;
int i = 0; int i = 0;
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&pathstr)); CATCH(errctx, akgl_heap_next_string(&pathstr));
snprintf((char *)&pathstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj"); snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
doc = json_load_file((char *)&pathstr->data, 0, &errdata); doc = json_load_file((char *)&pathstr->data, 0, &errdata);
FAIL_ZERO_BREAK(errctx, doc, ERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text); FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
CATCH(errctx, tilemap_load_layers(&gamemap, doc)); CATCH(errctx, akgl_tilemap_load_layers(&gamemap, doc));
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
(gamemap.numlayers != 3), (gamemap.numlayers != 3),
ERR_VALUE, AKERR_VALUE,
"Map layer count incorrect" "Map layer count incorrect"
); );
for ( i = 0; i < gamemap.numlayers; i++ ) { for ( i = 0; i < gamemap.numlayers; i++ ) {
@@ -243,27 +243,27 @@ ErrorContext *test_tilemap_load_layers(void)
(gamemap.layers[i].id != (i + 1)) || (gamemap.layers[i].id != (i + 1)) ||
(gamemap.layers[i].x != 0) || (gamemap.layers[i].x != 0) ||
(gamemap.layers[i].y != 0) ) { (gamemap.layers[i].y != 0) ) {
FAIL(errctx, ERR_VALUE, "Map layer data loaded incorrectly (see gdb)"); FAIL(errctx, AKERR_VALUE, "Map layer data loaded incorrectly (see gdb)");
goto _test_tilemap_load_layers_cleanup; goto _test_akgl_tilemap_load_layers_cleanup;
} }
} }
// Layer 2 should have 1 object loaded // Layer 2 should have 1 object loaded
if ( (gamemap.layers[1].objects[0].actorptr != SDL_GetPointerProperty(REGISTRY_ACTOR, "testactor", NULL)) || if ( (gamemap.layers[1].objects[0].actorptr != SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "testactor", NULL)) ||
(gamemap.layers[1].objects[1].name[0] != '\0' ) || (gamemap.layers[1].objects[1].name[0] != '\0' ) ||
(gamemap.layers[1].objects[1].id != 0) ) { (gamemap.layers[1].objects[1].id != 0) ) {
FAIL_BREAK(errctx, ERR_VALUE, "Map layer 2 should have 1 loaded object (testactor) and nothing else (see gdb)"); FAIL_BREAK(errctx, AKERR_VALUE, "Map layer 2 should have 1 loaded object (testactor) and nothing else (see gdb)");
} }
// Layer 1 and 3 should have no objects // Layer 1 and 3 should have no objects
for ( i = 0; i < TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) { for ( i = 0; i < AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) {
if ( gamemap.layers[0].objects[i].id != 0 ) { if ( gamemap.layers[0].objects[i].id != 0 ) {
FAIL(errctx, ERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects"); FAIL(errctx, AKERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects");
goto _test_tilemap_load_layers_cleanup; goto _test_akgl_tilemap_load_layers_cleanup;
} }
} }
for ( i = 0; i < TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) { for ( i = 0; i < AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER ; i++ ) {
if ( gamemap.layers[2].objects[i].id != 0 ) { if ( gamemap.layers[2].objects[i].id != 0 ) {
FAIL(errctx, ERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects"); FAIL(errctx, AKERR_VALUE, "Map layers 1 and 3 should have no objects loaded but found objects");
goto _test_tilemap_load_layers_cleanup; goto _test_akgl_tilemap_load_layers_cleanup;
} }
} }
// Layers 1 and 3 should have tile data // Layers 1 and 3 should have tile data
@@ -276,12 +276,12 @@ ErrorContext *test_tilemap_load_layers(void)
(gamemap.layers[2].data[2] != 0) || (gamemap.layers[2].data[2] != 0) ||
(gamemap.layers[2].data[3] != 6) (gamemap.layers[2].data[3] != 6)
) { ) {
FAIL_BREAK(errctx, ERR_VALUE, "Map layers 1 and 3 should have tile data but it is incorrect"); FAIL_BREAK(errctx, AKERR_VALUE, "Map layers 1 and 3 should have tile data but it is incorrect");
} }
_test_tilemap_load_layers_cleanup: _test_akgl_tilemap_load_layers_cleanup:
} CLEANUP { } CLEANUP {
if ( pathstr != NULL ) { if ( pathstr != NULL ) {
IGNORE(heap_release_string(pathstr)); IGNORE(akgl_heap_release_string(pathstr));
} }
if ( doc != NULL ) { if ( doc != NULL ) {
json_decref(doc); json_decref(doc);
@@ -291,23 +291,23 @@ _test_tilemap_load_layers_cleanup:
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_load_tilesets(void) akerr_ErrorContext *test_akgl_tilemap_load_tilesets(void)
{ {
string *pathstr = NULL; akgl_String *pathstr = NULL;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
json_t *doc = NULL; json_t *doc = NULL;
json_error_t errdata; json_error_t errdata;
SDL_Texture *image = NULL; SDL_Texture *image = NULL;
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
ATTEMPT { ATTEMPT {
CATCH(errctx, heap_next_string(&pathstr)); CATCH(errctx, akgl_heap_next_string(&pathstr));
snprintf((char *)&pathstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj"); snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testmap.tmj");
doc = json_load_file((char *)&pathstr->data, 0, &errdata); doc = json_load_file((char *)&pathstr->data, 0, &errdata);
FAIL_ZERO_BREAK(errctx, doc, ERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text); FAIL_ZERO_BREAK(errctx, doc, AKERR_NULLPOINTER, "Failed to load testmap: %s", (char *)&errdata.text);
CATCH(errctx, tilemap_load_tilesets(&gamemap, doc)); CATCH(errctx, akgl_tilemap_load_tilesets(&gamemap, doc));
FAIL_NONZERO_BREAK(errctx, (gamemap.numtilesets != 1), ERR_VALUE, "Incorrect number of tilesets loaded for map"); FAIL_NONZERO_BREAK(errctx, (gamemap.numtilesets != 1), AKERR_VALUE, "Incorrect number of tilesets loaded for map");
if ( (gamemap.tilesets[0].columns != 48 ) || if ( (gamemap.tilesets[0].columns != 48 ) ||
(gamemap.tilesets[0].firstgid != 1) || (gamemap.tilesets[0].firstgid != 1) ||
(gamemap.tilesets[0].imageheight != 576) || (gamemap.tilesets[0].imageheight != 576) ||
@@ -317,30 +317,30 @@ ErrorContext *test_tilemap_load_tilesets(void)
(gamemap.tilesets[0].tilecount != 1728) || (gamemap.tilesets[0].tilecount != 1728) ||
(gamemap.tilesets[0].tileheight != 16) || (gamemap.tilesets[0].tileheight != 16) ||
(gamemap.tilesets[0].tilewidth != 16) ) { (gamemap.tilesets[0].tilewidth != 16) ) {
FAIL_BREAK(errctx, ERR_VALUE, "Tileset loaded with incorrect values"); FAIL_BREAK(errctx, AKERR_VALUE, "Tileset loaded with incorrect values");
} }
FAIL_NONZERO_BREAK( FAIL_NONZERO_BREAK(
errctx, errctx,
strcmp((char *)&gamemap.tilesets[0].name, "World_A1"), strcmp((char *)&gamemap.tilesets[0].name, "World_A1"),
ERR_VALUE, AKERR_VALUE,
"Tileset loaded with incorrect name"); "Tileset loaded with incorrect name");
snprintf((char *)&pathstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/World_A1.png"); snprintf((char *)&pathstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/World_A1.png");
image = IMG_LoadTexture(renderer, (char *)&pathstr->data); image = IMG_LoadTexture(renderer, (char *)&pathstr->data);
FAIL_ZERO_BREAK(errctx, image, ERR_SDL, "Failed to load comparison image"); FAIL_ZERO_BREAK(errctx, image, AKGL_ERR_SDL, "Failed to load comparison image");
CATCH( CATCH(
errctx, errctx,
render_and_compare( akgl_render_and_compare(
gamemap.tilesets[0].texture, gamemap.tilesets[0].texture,
image, image,
0, 0, 768, 576, 0, 0, 768, 576,
"test_tilemap_loaded_tileset.png") "test_akgl_tilemap_loaded_tileset.png")
); );
} CLEANUP { } CLEANUP {
if ( pathstr != NULL ) { if ( pathstr != NULL ) {
IGNORE(heap_release_string(pathstr)); IGNORE(akgl_heap_release_string(pathstr));
} }
if ( doc != NULL ) { if ( doc != NULL ) {
json_decref(doc); json_decref(doc);
@@ -350,21 +350,21 @@ ErrorContext *test_tilemap_load_tilesets(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_load(void) akerr_ErrorContext *test_akgl_tilemap_load(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
memset((void *)&gamemap, 0x00, sizeof(tilemap)); memset((void *)&gamemap, 0x00, sizeof(akgl_Tilemap));
ATTEMPT { ATTEMPT {
CATCH(errctx, tilemap_load("assets/testmap.tmj", &gamemap)); CATCH(errctx, akgl_tilemap_load("assets/testmap.tmj", &gamemap));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_tilemap_draw(void) akerr_ErrorContext *test_akgl_tilemap_draw(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
@@ -376,7 +376,7 @@ ErrorContext *test_tilemap_draw(void)
} }
ErrorContext *test_tilemap_draw_tileset(void) akerr_ErrorContext *test_akgl_tilemap_draw_tileset(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
@@ -395,28 +395,28 @@ int main(void)
SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest"); SDL_SetAppMetadata("SDL3-GameTest", "0.1", "net.aklabs.sdl3-gametest");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO )) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't initialize SDL: %s", SDL_GetError());
} }
if (!SDL_CreateWindowAndRenderer("net/aklabs/libsdl3game/test_sprite", 768, 576, 0, &window, &renderer)) { if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_sprite", 768, 576, 0, &window, &renderer)) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError()); FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
} }
CATCH(errctx, registry_init()); CATCH(errctx, akgl_registry_init());
CATCH(errctx, heap_init()); CATCH(errctx, akgl_heap_init());
CATCH(errctx, sprite_load_json("assets/testsprite.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
CATCH(errctx, sprite_load_json("assets/testsprite2.json")); CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
CATCH(errctx, character_load_json("assets/testcharacter.json")); CATCH(errctx, akgl_character_load_json("assets/testcharacter.json"));
CATCH(errctx, test_tilemap_get_json_tilemap_property()); CATCH(errctx, test_tilemap_akgl_get_json_tilemap_property());
CATCH(errctx, test_tilemap_compute_tileset_offsets()); CATCH(errctx, test_akgl_tilemap_compute_tileset_offsets());
CATCH(errctx, test_tilemap_load_layer_objects()); CATCH(errctx, test_akgl_tilemap_load_layer_objects());
CATCH(errctx, test_tilemap_load_layer_tile()); CATCH(errctx, test_akgl_tilemap_load_layer_tile());
CATCH(errctx, test_tilemap_load_layers()); CATCH(errctx, test_akgl_tilemap_load_layers());
CATCH(errctx, test_tilemap_load_tilesets()); CATCH(errctx, test_akgl_tilemap_load_tilesets());
//CATCH(errctx, test_tilemap_load()); //CATCH(errctx, test_akgl_tilemap_load());
//CATCH(errctx, test_tilemap_draw_tileset()); //CATCH(errctx, test_akgl_tilemap_draw_tileset());
//CATCH(errctx, test_tilemap_draw()); //CATCH(errctx, test_akgl_tilemap_draw());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

View File

@@ -1,42 +1,42 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/util.h> #include <akgl/util.h>
ErrorContext *test_rectangle_points_nullpointers(void) akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
{ {
RectanglePoints points; RectanglePoints points;
SDL_FRect testrect; SDL_FRect testrect;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(NULL, NULL)); CATCH(errctx, akgl_rectangle_points(NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with all NULL pointers"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with all NULL pointers");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(NULL, &testrect)); CATCH(errctx, akgl_rectangle_points(NULL, &testrect));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with NULL SDL_FRect pointer"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL SDL_FRect pointer");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(&points, NULL)); CATCH(errctx, akgl_rectangle_points(&points, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with NULL RectanglePoints pointer"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL RectanglePoints pointer");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(&points, &testrect)); CATCH(errctx, akgl_rectangle_points(&points, &testrect));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -44,7 +44,7 @@ ErrorContext *test_rectangle_points_nullpointers(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_rectangle_points_math(void) akerr_ErrorContext *test_akgl_rectangle_points_math(void)
{ {
RectanglePoints points; RectanglePoints points;
SDL_FRect testrect = {.x = 0, .y = 0, .w = 32, .h = 32}; SDL_FRect testrect = {.x = 0, .y = 0, .w = 32, .h = 32};
@@ -52,7 +52,7 @@ ErrorContext *test_rectangle_points_math(void)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(&points, &testrect)); CATCH(errctx, akgl_rectangle_points(&points, &testrect));
if ( points.topleft.x != 0 || if ( points.topleft.x != 0 ||
points.topleft.y != 0 || points.topleft.y != 0 ||
points.topright.x != 32 || points.topright.x != 32 ||
@@ -63,8 +63,8 @@ ErrorContext *test_rectangle_points_math(void)
points.bottomright.y != 32 ) { points.bottomright.y != 32 ) {
FAIL_BREAK( FAIL_BREAK(
errctx, errctx,
ERR_BEHAVIOR, AKERR_BEHAVIOR,
"rectangle_points incorrectly calculated points for {x=0, y=0, w=32, h=32} to {topleft={%d, %d}, topright={%d, %d}, bottomleft={%d, %d}, bottomright={%d, %d}}", "akgl_rectangle_points incorrectly calculated points for {x=0, y=0, w=32, h=32} to {topleft={%d, %d}, topright={%d, %d}, bottomleft={%d, %d}, bottomright={%d, %d}}",
points.topleft.x, points.topleft.y, points.topleft.x, points.topleft.y,
points.topright.x, points.topright.y, points.topright.x, points.topright.y,
points.bottomleft.x, points.bottomleft.y, points.bottomleft.x, points.bottomleft.y,
@@ -77,7 +77,7 @@ ErrorContext *test_rectangle_points_math(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_collide_point_rectangle_nullpointers(void) akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
{ {
point testpoint; point testpoint;
RectanglePoints testrectpoints; RectanglePoints testrectpoints;
@@ -86,43 +86,43 @@ ErrorContext *test_collide_point_rectangle_nullpointers(void)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, NULL)); CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(*, *, NULL) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, *, NULL) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, NULL, &testcollide)); CATCH(errctx, akgl_collide_point_rectangle(&testpoint, NULL, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(*, NULL, *) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, NULL, *) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_point_rectangle(NULL, &testrectpoints, &testcollide)); CATCH(errctx, akgl_collide_point_rectangle(NULL, &testrectpoints, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(NULL, *, *) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, *, *) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_point_rectangle(NULL, NULL, NULL)); CATCH(errctx, akgl_collide_point_rectangle(NULL, NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(NULL, NULL, NULL) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, NULL, NULL) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide)); CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -130,7 +130,7 @@ ErrorContext *test_collide_point_rectangle_nullpointers(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_collide_point_rectangle_logic(void) akerr_ErrorContext *test_akgl_collide_point_rectangle_logic(void)
{ {
point testpoint = {.x = 16, .y = 16}; point testpoint = {.x = 16, .y = 16};
SDL_FRect testrect = { .x = 0, .y = 0, .w = 32, .h = 32}; SDL_FRect testrect = { .x = 0, .y = 0, .w = 32, .h = 32};
@@ -139,17 +139,17 @@ ErrorContext *test_collide_point_rectangle_logic(void)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, rectangle_points(&testrectpoints, &testrect)); CATCH(errctx, akgl_rectangle_points(&testrectpoints, &testrect));
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide)); CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
testpoint.x = 48; testpoint.x = 48;
testpoint.y = 48; testpoint.y = 48;
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide)); CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == true ) { if ( testcollide == true ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Invalid collision reported"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
} }
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -157,7 +157,7 @@ ErrorContext *test_collide_point_rectangle_logic(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_collide_rectangles_nullpointers(void) akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
{ {
SDL_FRect testrect1; SDL_FRect testrect1;
SDL_FRect testrect2; SDL_FRect testrect2;
@@ -166,43 +166,43 @@ ErrorContext *test_collide_rectangles_nullpointers(void)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, NULL)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(*, *, NULL) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, *, NULL) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, NULL, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, NULL, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(*, NULL, *) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, NULL, *) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_rectangles(NULL, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(NULL, &testrect2, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(NULL, *, *) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, *, *) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_rectangles(NULL, NULL, NULL)); CATCH(errctx, akgl_collide_rectangles(NULL, NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(NULL, NULL, NULL) failed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, NULL, NULL) failed");
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) { } HANDLE(errctx, AKERR_NULLPOINTER) {
// noop // noop
} FINISH(errctx, true); } FINISH(errctx, true);
ATTEMPT { ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH(errctx, true); } FINISH(errctx, true);
@@ -210,7 +210,7 @@ ErrorContext *test_collide_rectangles_nullpointers(void)
SUCCEED_RETURN(errctx); SUCCEED_RETURN(errctx);
} }
ErrorContext *test_collide_rectangles_logic(void) akerr_ErrorContext *test_akgl_collide_rectangles_logic(void)
{ {
SDL_FRect testrect1 = { .x = 0, .y = 0, .w = 32, .h = 32}; SDL_FRect testrect1 = { .x = 0, .y = 0, .w = 32, .h = 32};
SDL_FRect testrect2 = { .x = 30, .y = 30, .w = 40, .h = 40}; SDL_FRect testrect2 = { .x = 30, .y = 30, .w = 40, .h = 40};
@@ -220,32 +220,32 @@ ErrorContext *test_collide_rectangles_logic(void)
ATTEMPT { ATTEMPT {
// Collision overlapping on the top left // Collision overlapping on the top left
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping on the top right // Collision overlapping on the top right
testrect1.x = 64; testrect1.x = 64;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping on the bottom left // Collision overlapping on the bottom left
testrect1.x = 0; testrect1.x = 0;
testrect1.y = 32; testrect1.y = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping on the bottom right // Collision overlapping on the bottom right
testrect1.x = 32; testrect1.x = 32;
testrect1.y = 32; testrect1.y = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping the top edge // Collision overlapping the top edge
@@ -253,9 +253,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0; testrect1.y = 0;
testrect1.w = 60; testrect1.w = 60;
testrect1.h = 32; testrect1.h = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping the left edge // Collision overlapping the left edge
@@ -263,9 +263,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0; testrect1.y = 0;
testrect1.w = 35; testrect1.w = 35;
testrect1.h = 80; testrect1.h = 80;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping the right edge // Collision overlapping the right edge
@@ -273,9 +273,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0; testrect1.y = 0;
testrect1.w = 60; testrect1.w = 60;
testrect1.h = 80; testrect1.h = 80;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Collision overlapping the bottom edge // Collision overlapping the bottom edge
@@ -283,9 +283,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 65; testrect1.y = 65;
testrect1.w = 80; testrect1.w = 80;
testrect1.h = 32; testrect1.h = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) { if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
} }
// Not colliding // Not colliding
@@ -293,9 +293,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0; testrect1.y = 0;
testrect1.w = 16; testrect1.w = 16;
testrect1.h = 16; testrect1.h = 16;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide)); CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == true ) { if ( testcollide == true ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Invalid collision reported"); FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
} }
} CLEANUP { } CLEANUP {
@@ -309,11 +309,11 @@ int main(void)
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
CATCH(errctx, test_rectangle_points_nullpointers()); CATCH(errctx, test_akgl_rectangle_points_nullpointers());
CATCH(errctx, test_rectangle_points_math()); CATCH(errctx, test_akgl_rectangle_points_math());
CATCH(errctx, test_collide_point_rectangle_nullpointers()); CATCH(errctx, test_akgl_collide_point_rectangle_nullpointers());
CATCH(errctx, test_collide_rectangles_nullpointers()); CATCH(errctx, test_akgl_collide_rectangles_nullpointers());
CATCH(errctx, test_collide_rectangles_logic()); CATCH(errctx, test_akgl_collide_rectangles_logic());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} FINISH_NORETURN(errctx); } FINISH_NORETURN(errctx);

BIN
util/assets/Actor1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
util/assets/charviewer Executable file

Binary file not shown.

View File

@@ -0,0 +1,16 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy facing down",
"width": 48,
"height": 48,
"speed": 0,
"loop": false,
"loopReverse": false,
"frames": [
1
]
}

View File

@@ -0,0 +1,16 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy facing left",
"width": 48,
"height": 48,
"speed": 0,
"loop": false,
"loopReverse": false,
"frames": [
13
]
}

View File

@@ -0,0 +1,16 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy facing right",
"width": 48,
"height": 48,
"speed": 0,
"loop": false,
"loopReverse": false,
"frames": [
25
]
}

View File

@@ -0,0 +1,16 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy facing up",
"width": 48,
"height": 48,
"speed": 0,
"loop": false,
"loopReverse": false,
"frames": [
37
]
}

View File

@@ -0,0 +1,18 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy walking down",
"width": 48,
"height": 48,
"speed": 1000,
"loop": true,
"loopReverse": true,
"frames": [
0,
1,
2
]
}

View File

@@ -0,0 +1,18 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy walking left",
"width": 48,
"height": 48,
"speed": 1000,
"loop": true,
"loopReverse": true,
"frames": [
12,
13,
14
]
}

View File

@@ -0,0 +1,18 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy walking right",
"width": 48,
"height": 48,
"speed": 1000,
"loop": true,
"loopReverse": true,
"frames": [
24,
25,
26
]
}

View File

@@ -0,0 +1,18 @@
{
"spritesheet": {
"filename": "../assets/Actor1.png",
"frame_width": 48,
"frame_height": 48
},
"name": "little guy walking up",
"width": 48,
"height": 48,
"speed": 1000,
"loop": true,
"loopReverse": true,
"frames": [
36,
37,
38
]
}

View File

@@ -0,0 +1,68 @@
{
"name": "little guy",
"movementspeed": 1,
"velocity_x": 0.02,
"velocity_y": 0.02,
"sprite_mappings": [
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_LEFT",
"ACTOR_STATE_MOVING_LEFT"
],
"sprite": "little guy walking left"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_RIGHT",
"ACTOR_STATE_MOVING_RIGHT"
],
"sprite": "little guy walking right"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_UP",
"ACTOR_STATE_MOVING_UP"
],
"sprite": "little guy walking up"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_DOWN",
"ACTOR_STATE_MOVING_DOWN"
],
"sprite": "little guy walking down"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_UP"
],
"sprite": "little guy facing up"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_RIGHT"
],
"sprite": "little guy facing right"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_LEFT"
],
"sprite": "little guy facing left"
},
{
"state": [
"ACTOR_STATE_ALIVE",
"ACTOR_STATE_FACE_DOWN"
],
"sprite": "little guy facing down"
}
]
}

View File

@@ -5,26 +5,27 @@
#include <SDL3_image/SDL_image.h> #include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h> #include <SDL3_mixer/SDL_mixer.h>
#include <sdlerror.h> #include <akerror.h>
#include <sdl3game/assets.h> #include <akgl/assets.h>
#include <sdl3game/iterator.h> #include <akgl/iterator.h>
#include <sdl3game/tilemap.h> #include <akgl/tilemap.h>
#include <sdl3game/heap.h> #include <akgl/heap.h>
#include <sdl3game/game.h> #include <akgl/game.h>
#include <sdl3game/controller.h> #include <akgl/controller.h>
#include <sdl3game/draw.h> #include <akgl/draw.h>
#include <sdl3game/sprite.h> #include <akgl/sprite.h>
#include <sdl3game/actor.h> #include <akgl/actor.h>
#include <sdl3game/registry.h> #include <akgl/registry.h>
#include <akgl/error.h>
#include <unistd.h> #include <unistd.h>
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{ {
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
SDL3GControlMap *controlmap; akgl_Actor *actorptr = NULL;
actor *actorptr = NULL;
int i = 0; int i = 0;
int gamepadids[32];
char *characterjson = NULL; char *characterjson = NULL;
char pathbuf[4096]; char pathbuf[4096];
char cwdbuf[1024]; char cwdbuf[1024];
@@ -41,18 +42,18 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, appstate, ERR_NULLPOINTER, "NULL appstate pointer"); FAIL_ZERO_BREAK(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate pointer");
FAIL_ZERO_BREAK(errctx, getcwd((char *)&cwdbuf, 1024), ERR_NULLPOINTER, "Couldn't get current working directory"); FAIL_ZERO_BREAK(errctx, getcwd((char *)&cwdbuf, 1024), AKERR_NULLPOINTER, "Couldn't get current working directory");
strcpy((char *)&game.name, "charviewer"); strcpy((char *)&game.name, "charviewer");
strcpy((char *)&game.version, "0.0.1"); strcpy((char *)&game.version, "0.0.1");
strcpy((char *)&game.uri, "net.aklabs.libsdl3game.charviewer"); strcpy((char *)&game.uri, "net.aklabs.libakgl.charviewer");
game.screenwidth = 640; game.screenwidth = 640;
game.screenheight = 480; game.screenheight = 480;
CATCH(errctx, GAME_init()); CATCH(errctx, akgl_game_init());
CATCH(errctx, heap_init()); CATCH(errctx, akgl_heap_init());
CATCH(errctx, registry_init()); CATCH(errctx, akgl_registry_init());
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -69,7 +70,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
sprintf((char *)&pathbuf, "%s", argv[i]); sprintf((char *)&pathbuf, "%s", argv[i]);
} }
SDL_Log("Loading sprite %s...", (char *)&pathbuf); SDL_Log("Loading sprite %s...", (char *)&pathbuf);
CATCH(errctx, sprite_load_json((char *)&pathbuf)); CATCH(errctx, akgl_sprite_load_json((char *)&pathbuf));
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE_DEFAULT(errctx) { } HANDLE_DEFAULT(errctx) {
@@ -86,51 +87,25 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
sprintf((char *)&pathbuf, "%s", characterjson); sprintf((char *)&pathbuf, "%s", characterjson);
} }
SDL_Log("Loading character %s...", (char *)&pathbuf); SDL_Log("Loading character %s...", (char *)&pathbuf);
CATCH(errctx, character_load_json((char *)&pathbuf)); CATCH(errctx, akgl_character_load_json((char *)&pathbuf));
CATCH(errctx, heap_next_actor(&actorptr)); CATCH(errctx, akgl_heap_next_actor(&actorptr));
CATCH(errctx, actor_initialize((actor *)actorptr, "player")); CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorptr, "player"));
actorptr->basechar = SDL_GetPointerProperty( actorptr->basechar = SDL_GetPointerProperty(
REGISTRY_CHARACTER, AKGL_REGISTRY_CHARACTER,
"little guy", "little guy",
NULL); NULL);
FAIL_ZERO_BREAK(errctx, actorptr->basechar, ERR_REGISTRY, "Can't load character 'little guy' from the registry"); FAIL_ZERO_BREAK(errctx, actorptr->basechar, AKERR_REGISTRY, "Can't load character 'little guy' from the registry");
actorptr->movement_controls_face = false; actorptr->movement_controls_face = false;
actorptr->state = (ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_DOWN); actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_DOWN);
actorptr->x = 320; actorptr->x = 320;
actorptr->y = 240; actorptr->y = 240;
actorptr->visible = true; actorptr->visible = true;
// set up the control map // Open the first gamepad
controlmap = &GAME_ControlMaps[0]; FAIL_ZERO_BREAK(errctx, SDL_GetGamepads((int *)&gamepadids), AKGL_ERR_SDL, "%s", SDL_GetError());
controlmap->kbid = 0; SDL_Log("Opening gamepad %d", gamepadids[0]);
controlmap->target = SDL_GetPointerProperty(REGISTRY_ACTOR, "player", NULL); FAIL_ZERO_BREAK(errctx, SDL_OpenGamepad(gamepadids[0]), AKGL_ERR_SDL, "%s", SDL_GetError());
// Move down CATCH(errctx, akgl_controller_default(0, "player", 0, gamepadids[0]));
controlmap->controls[0].key = SDLK_DOWN;
controlmap->controls[0].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[0].event_off = SDL_EVENT_KEY_UP;
controlmap->controls[0].handler_on = &SDL3GActor_cmhf_down_on;
controlmap->controls[0].handler_off = &SDL3GActor_cmhf_down_off;
// Move up
controlmap->controls[1].key = SDLK_UP;
controlmap->controls[1].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[1].event_off = SDL_EVENT_KEY_UP;
controlmap->controls[1].handler_on = &SDL3GActor_cmhf_up_on;
controlmap->controls[1].handler_off = &SDL3GActor_cmhf_up_off;
// Move left
controlmap->controls[2].key = SDLK_LEFT;
controlmap->controls[2].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[2].event_off = SDL_EVENT_KEY_UP;
controlmap->controls[2].handler_on = &SDL3GActor_cmhf_left_on;
controlmap->controls[2].handler_off = &SDL3GActor_cmhf_left_off;
// Move right
controlmap->controls[3].key = SDLK_RIGHT;
controlmap->controls[3].event_on = SDL_EVENT_KEY_DOWN;
controlmap->controls[3].event_off = SDL_EVENT_KEY_UP;
controlmap->controls[3].handler_on = &SDL3GActor_cmhf_right_on;
controlmap->controls[3].handler_off = &SDL3GActor_cmhf_right_off;
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
@@ -147,10 +122,10 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
ATTEMPT { ATTEMPT {
FAIL_ZERO_BREAK(errctx, appstate, ERR_NULLPOINTER, "NULL appstate pointer"); FAIL_ZERO_BREAK(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate pointer");
FAIL_ZERO_BREAK(errctx, event, ERR_NULLPOINTER, "NULL event pointer"); FAIL_ZERO_BREAK(errctx, event, AKERR_NULLPOINTER, "NULL event pointer");
CATCH(errctx, controller_handle_event(appstate, event)); CATCH(errctx, akgl_controller_handle_event(appstate, event));
if (event->type == SDL_EVENT_QUIT) { if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
} }
@@ -163,17 +138,17 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
SDL_AppResult SDL_AppIterate(void *appstate) SDL_AppResult SDL_AppIterate(void *appstate)
{ {
int i = 0; int i = 0;
iterator opflags; akgl_Iterator opflags;
PREPARE_ERROR(errctx); PREPARE_ERROR(errctx);
BITMASK_CLEAR(opflags.flags); AKGL_BITMASK_CLEAR(opflags.flags);
BITMASK_ADD(opflags.flags, ITERATOR_OP_UPDATE); AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_UPDATE);
BITMASK_ADD(opflags.flags, ITERATOR_OP_RENDER); AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_RENDER);
GAME_draw_background(game.screenwidth, game.screenheight); akgl_draw_background(game.screenwidth, game.screenheight);
ATTEMPT { ATTEMPT {
CATCH(errctx, tilemap_draw(renderer, (tilemap *)&gamemap, &camera, i)); CATCH(errctx, akgl_tilemap_draw(renderer, (akgl_Tilemap *)&gamemap, &camera, i));
SDL_EnumerateProperties(REGISTRY_ACTOR, &registry_iterate_actor, (void *)&opflags); SDL_EnumerateProperties(AKGL_REGISTRY_ACTOR, &akgl_registry_iterate_actor, (void *)&opflags);
} CLEANUP { } CLEANUP {
} PROCESS(errctx) { } PROCESS(errctx) {
} HANDLE_DEFAULT(errctx) { } HANDLE_DEFAULT(errctx) {