52 Commits

Author SHA1 Message Date
462fb12bd5 WIP on physics/stage BSP stuff 2026-06-27 13:16:28 -04:00
cb878cfaa5 Started adding doxygen blocks 2026-06-22 08:18:31 -04:00
71de95822c Stage BSP partitioning written. Changes to actors to keep track of their BSP node. Actors traverse the BSP as they move. Miscellanious error code def/usage updates. WIP 2026-06-22 08:18:15 -04:00
f4728bf19d WIP 2026-06-19 09:51:29 -04:00
42453c2eb6 Starting on BSP work 2026-06-19 08:19:34 -04:00
652ee4cdf3 Tilemaps can now load their own physics engines from map properties. The gravity for the arcade simulation is acting a little funny when populated from the map properties. 2026-06-02 17:11:16 -04:00
9fed59c4c8 Abstract the global renderer, physics, camera and gamemap objects behind a pointer, so you can swap in and out different ones as needed 2026-06-02 13:15:26 -04:00
941eeb2493 Got the physics system applying gravity and drag correctly. (Mostly? Seems like it? Seems good.) 2026-05-26 16:45:04 -04:00
314ce5e10d Physics simulation engine implemented, basic cases (null and 2d SideScroller) works 2026-05-26 10:36:31 -04:00
d87c5d2c20 More rendering subsystem breakout, added a physics subsystem, everything now fires from akgl_game_update() for the user 2026-05-25 21:29:18 -04:00
6314ad7f26 Start abstracting the rendering backend away to make it easier to switch to GPU rendering later 2026-05-24 21:59:29 -04:00
8f613397d6 Loading sprites and tiles both work correctly with relative paths, now using akgl_path_relative* helpers 2026-05-24 19:57:57 -04:00
980bbc56fb Fixed loading of tilesets and images from tilemaps to correctly use relative paths. 2026-05-24 10:28:35 -04:00
e3edd5b855 Add a method that automatically opens gamepads so they will send events 2026-05-21 21:43:51 -04:00
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
90 changed files with 10144 additions and 2181 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
./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)
project(sdl3game LANGUAGES C)
project(akgl LANGUAGES C)
include(CTest)
find_package(PkgConfig REQUIRED)
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)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
# Check for SDL3 using pkg-config
pkg_check_modules(SDL3 REQUIRED sdl3)
pkg_check_modules(SDL3_image REQUIRED sdl3-image)
pkg_check_modules(SDL3_mixer REQUIRED sdl3-mixer)
pkg_check_modules(jansson REQUIRED jansson)
pkg_check_modules(sdlerror REQUIRED sdlerror)
add_subdirectory(deps/jansson EXCLUDE_FROM_ALL)
add_subdirectory(deps/libakerror EXCLUDE_FROM_ALL)
add_subdirectory(deps/libakstdlib EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL EXCLUDE_FROM_ALL)
add_subdirectory(deps/SDL_image EXCLUDE_FROM_ALL)
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
include_directories(${SDL3_INCLUDE_DIRS})
add_library(sdl3game SHARED
add_library(akgl SHARED
deps/semver/semver.c
src/actor.c
src/actor_state_string_names.c
src/text.c
src/assets.c
src/character.c
src/draw.c
@@ -31,14 +69,19 @@ add_library(sdl3game SHARED
src/heap.c
src/json_helpers.c
src/registry.c
src/renderer.c
src/physics.c
src/sprite.c
src/staticstring.c
src/tilemap.c
src/util.c
src/stage.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_bitmasks tests/bitmasks.c)
add_executable(test_character tests/character.c)
@@ -47,6 +90,7 @@ add_executable(test_sprite tests/sprite.c)
add_executable(test_staticstring tests/staticstring.c)
add_executable(test_tilemap tests/tilemap.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 bitmasks COMMAND test_bitmasks)
add_test(NAME character COMMAND test_character)
@@ -55,36 +99,57 @@ add_test(NAME sprite COMMAND test_sprite)
add_test(NAME staticstring COMMAND test_staticstring)
add_test(NAME tilemap COMMAND test_tilemap)
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)
target_include_directories(sdl3game PUBLIC
include/
target_include_directories(akgl PUBLIC
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(test_bitmasks PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_character PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_registry PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_sprite PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_staticstring PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_tilemap PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(test_util PRIVATE sdlerror::sdlerror sdl3game SDL3::SDL3 SDL3_image::SDL3_image SDL3_mixer::SDL3_mixer box2d::box2d jansson::jansson -lm)
target_link_libraries(akgl
PUBLIC
SDL3::SDL3
SDL3_image::SDL3_image
SDL3_mixer::SDL3_mixer
SDL3_ttf::SDL3_ttf
akstdlib::akstdlib
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}")
install(TARGETS sdl3game DESTINATION "lib/")
install(FILES "include/sdl3game/actor.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/assets.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/character.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/draw.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/game.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/controller.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/heap.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/iterator.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/json_helpers.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/registry.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/sprite.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/staticstring.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/tilemap.h" DESTINATION "include/sdl3game/")
install(FILES "include/sdl3game/util.h" DESTINATION "include/sdl3game/")
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)
set(main_lib_dest "lib/akgl-${MY_LIBRARY_VERSION}")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akgl.pc DESTINATION "lib/pkgconfig/")
install(TARGETS akgl DESTINATION "lib/")
install(FILES "deps/semver/semver.h" DESTINATION "include/")
install(FILES "include/akgl/actor.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/types.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/text.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/assets.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/character.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/error.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/draw.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/game.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/controller.h" DESTINATION "include/akgl/")
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/renderer.h" DESTINATION "include/akgl/")
install(FILES "include/akgl/physics.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/")

2863
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

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
- 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

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

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

@@ -0,0 +1,151 @@
#ifndef _AKGL_ACTOR_H_
#define _AKGL_ACTOR_H_
#include <stdint.h>
#include <akstdlib.h>
#include <akgl/types.h>
#include <akgl/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_MOVING_IN 1 << 11 // 2048 0000 1000 0000 0000
#define AKGL_ACTOR_STATE_MOVING_OUT 1 << 12 // 4096 0001 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_13 1 << 13 // 8192 0010 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_14 1 << 14 // 16384 0100 0000 0000 0000
#define AKGL_ACTOR_STATE_UNDEFINED_15 1 << 15 // 32768 1000 0000 0000 0000
// ----- 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 type;
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;
bool simulating;
SDL_Time movetimer;
// Bounding box accounting for the actor's current real position plus physics delta
SDL_FRect bbox;
// Velocity. Combined effect of all forces acting on the actor resulting
// in energy along an axis.
float32_t vx;
float32_t vy;
float32_t vz;
// Environmental velocity. These are the forces acting on the actor by the
// environment (such as gravity and atmospheric drag)
float32_t ex;
float32_t ey;
float32_t ez;
// Thrust. Energy originating only from the actor's own acceleration on a
// given axis, before the effects of gravity and drag.
float32_t tx;
float32_t ty;
float32_t tz;
// Acceleration. These are borrowed from the base character object.
// A given axis resets to 0 when the actor stops moving in a given axis.
float32_t ax;
float32_t ay;
float32_t az;
// Max speed. These are borrowed from the base character object.
float32_t sx;
float32_t sy;
float32_t sz;
// Position.
float32_t x;
float32_t y;
float32_t z;
float32_t scale;
// Reference to the BSP Node currently containing this actor
aksl_TreeNode *bsphome;
// Reference to the BSP linked list node currently holding this actor
aksl_ListNode *bsplistnode;
struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN];
struct akgl_Actor *parent;
// method that controls logic update functions
akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj);
// method that renders the actor to the currently active renderer
akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj);
// method that changes the actor's face flags based on other state
akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj);
// method that performs movement logic on the actor based on state flags
akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt);
// method that calculates the correct sprite frame for the actor's current state
akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
// method to add a child to this actor
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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_update(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_automatic_face(akgl_Actor *obj);
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child);
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_

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

@@ -0,0 +1,36 @@
#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 speedtime;
float32_t ax;
float32_t ay;
float32_t az;
float32_t sx;
float32_t sy;
float32_t sz;
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_add)(struct akgl_Character *, akgl_Sprite *, int);
akerr_ErrorContext AKERR_NOIGNORE *(*sprite_get)(struct akgl_Character *, int, akgl_Sprite **);
} akgl_Character;
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_

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

@@ -0,0 +1,49 @@
#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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void);
#endif // _CONTROLLER_H_

View File

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

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

@@ -0,0 +1,20 @@
#ifndef _ERROR_H_
#define _ERROR_H_
// This macro is used to silence warnings on string concatenation operations that may fail.
// e.g., combining two element of PATH_MAX into a string buffer of AKGL_STRING_MAX_LENGTH.
// We have to draw a line in the sand somewhere or we will just let our buffers grow forever
// to keep the compiler happy.
#define DISABLE_GCC_WARNING_FORMAT_TRUNCATION \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wformat-truncation\"")
#define RESTORE_GCC_WARNINGS \
_Pragma("GCC diagnostic pop")
#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1 /** Error in the SDL library. See SDL_GetError() for more detail. */
#define AKGL_ERR_LOGICINTERRUPT AKERR_LAST_ERRNO_VALUE + 2 /** Raised by an actor to tell the physics simulator not to process this node */
#define AKGL_ERR_REGISTRY AKERR_LAST_ERRNO_VALUE + 3 /** The AKGL Registry was not able to fulfill the operation */
#define AKGL_ERR_OOHEAP AKERR_LAST_ERRNO_VALUE + 4 /** The AKGL Heap in this context has been exhausted */
#endif // _ERROR_H_

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

@@ -0,0 +1,80 @@
#ifndef _AKGL_GAME_H_
#define _AKGL_GAME_H_
#include <stdint.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <akgl/types.h>
#include <akgl/tilemap.h>
#include <akgl/renderer.h>
#include <akgl/physics.h>
#include <akgl/stage.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;
SDL_Mutex *statelock;
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 MIX_Audio *bgm;
extern MIX_Mixer *akgl_mixer;
extern MIX_Track *akgl_tracks[AKGL_GAME_AUDIO_MAX_TRACKS];
extern SDL_FRect _akgl_camera;
extern akgl_Game game;
extern akgl_Stage _stage;
extern akgl_RenderBackend _akgl_renderer;
extern akgl_PhysicsBackend _akgl_physics;
extern akgl_Tilemap _akgl_gamemap;
extern akgl_Stage *stage;
extern akgl_Tilemap *gamemap;
extern akgl_RenderBackend *renderer;
extern akgl_PhysicsBackend *physics;
extern SDL_FRect *camera;
#define AKGL_BITMASK_HAS(x, y) (x & y) == y
#define AKGL_BITMASK_HASNOT(x, y) (x & y) != y
#define AKGL_BITMASK_ADD(x, y) x |= y
#define AKGL_BITMASK_DEL(x, y) x &= ~(y)
#define AKGL_BITMASK_CLEAR(x) x = 0;
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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_unlock(void);
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags);
#endif //_AKGL_GAME_H_

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

@@ -0,0 +1,66 @@
#ifndef _AKGL_HEAP_H_
#define _AKGL_HEAP_H_
#include <akgl/sprite.h>
#include <akgl/actor.h>
#include <akgl/character.h>
#include <akgl/staticstring.h>
#include <akgl/stage.h>
#include <akerror.h>
#include <akstdlib.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
#ifndef AKGL_MAX_HEAP_LIST
#define AKGL_MAX_HEAP_LIST (AKGL_MAX_HEAP_ACTOR * 2) + 256
#endif
#ifndef AKGL_MAX_HEAP_TREE
#define AKGL_MAX_HEAP_TREE 32
#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];
extern aksl_ListNode HEAP_LIST[AKGL_MAX_HEAP_LIST];
extern aksl_TreeNode HEAP_TREE[AKGL_MAX_HEAP_TREE];
extern akgl_BSPLeaf HEAP_TREE_LEAVES[AKGL_MAX_HEAP_TREE];
extern void *AKGL_LIST_SENTINEL;
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_list();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_tree();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_actor();
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_actor(akgl_Actor **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_sprite(akgl_Sprite **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest);
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_next_list(aksl_ListNode **dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_tree(aksl_TreeNode **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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_list(aksl_ListNode *ptr);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_list_release(aksl_ListNode *ptr, void *data);
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_tree_release(aksl_TreeNode *ptr, void *data);
#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,20 @@
#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_double_value(json_t *obj, char *key, double *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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_with_default(akerr_ErrorContext *e, void *defval, void *dest, uint32_t defsize);
#endif // _JSON_HELPERS_H_

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

@@ -0,0 +1,41 @@
#ifndef _PHYSICS_H_
#define _PHYSICS_H_
#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/actor.h>
#include <akgl/iterator.h>
#include <akgl/staticstring.h>
typedef struct akgl_PhysicsBackend {
akerr_ErrorContext AKERR_NOIGNORE *(*simulate)(struct akgl_PhysicsBackend *self, akgl_Iterator *opflags);
akerr_ErrorContext AKERR_NOIGNORE *(*gravity)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *(*collide)(struct akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
double drag_x;
double drag_y;
double drag_z;
double gravity_x;
double gravity_y;
double gravity_z;
SDL_Time gravity_time;
SDL_Time timer_gravity;
} akgl_PhysicsBackend;
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_factory(akgl_PhysicsBackend *self, akgl_String *type);
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags);
#endif // _PHYSICS_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_

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

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

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_

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

@@ -0,0 +1,45 @@
#ifndef _STAGE_H_
#define _STAGE_H_
#include <SDL3/SDL.h>
#include <akstdlib.h>
#include <akgl/actor.h>
#define AKGL_STAGE_PARTITION_HORIZONTAL 0
#define AKGL_STAGE_PARTITION_VERTICAL 1
#define AKGL_STAGE_PARTITION_MAXDEPTH 4
typedef struct akgl_BSPContext {
aksl_TreeNode *left;
aksl_TreeNode *right;
aksl_TreeNode *parent;
} akgl_BSPContext;
typedef struct akgl_BSPLeaf {
SDL_FRect extent;
aksl_ListNode *actors;
} akgl_BSPLeaf;
// Stages are collections of lights and cameras in a 2D or 3D space wherein actors
// and backgrounds are placed and directed for action.
typedef struct akgl_Stage {
aksl_TreeNode bsp;
akgl_BSPLeaf _rootleaf;
akerr_ErrorContext AKERR_NOIGNORE *(*partition)(struct akgl_Stage *self);
akerr_ErrorContext AKERR_NOIGNORE *(*partition_actor)(struct akgl_Stage *self, akgl_Actor *actor);
} akgl_Stage;
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d(akgl_Stage *self);
// A function to divide the stage into a binary space partition tree with lists of actors in each partition
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition(akgl_Stage *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition_switchdirection(uint8_t direction, uint8_t *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_actoriter(aksl_ListNode *node, void *data);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp(aksl_TreeNode *root, aksl_ListNode *actors, SDL_FRect extents, uint8_t direction, uint8_t depth, uint8_t maxdepth);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bspfree(akgl_Stage *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition(akgl_Stage *self);
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_move(akgl_Stage *self, akgl_Actor *actor);
#endif // _STAGE_H_

View File

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

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_

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

@@ -0,0 +1,129 @@
#ifndef _TILEMAP_H_
#define _TILEMAP_H_
#include <limits.h>
#include <akgl/actor.h>
#include <akgl/staticstring.h>
#include <akgl/physics.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 PATH_MAX
#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];
// Different levels may have different physics.
bool use_own_physics;
akgl_PhysicsBackend physics;
} akgl_Tilemap;
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load(char *fname, akgl_Tilemap *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw(akgl_Tilemap *dest, SDL_FRect *viewport, int layeridx);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_draw_tileset(akgl_Tilemap *dest, int tilesetidx);
/*
* These functions are part of the internal API and should not be called by the user.
* 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, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_release(akgl_Tilemap *dest);
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor);
#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_

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

@@ -0,0 +1,32 @@
#ifndef _UTIL_H_
#define _UTIL_H_
#include <akerror.h>
#include <akgl/staticstring.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);
akerr_ErrorContext AKERR_NOIGNORE *akgl_path_relative(char *root, char *path, akgl_String *dst);
// These are REALLY slow routines that are only useful in testing harnesses
akerr_ErrorContext AKERR_NOIGNORE *akgl_compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2);
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout);
#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,42 @@
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <string.h>
#include <sdlerror.h>
#include <sdl3game/game.h>
#include <sdl3game/sprite.h>
#include <sdl3game/actor.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/iterator.h>
#include <akerror.h>
#include <akstdlib.h>
ErrorContext *actor_initialize(actor *obj, char *name)
#include <akgl/physics.h>
#include <akgl/game.h>
#include <akgl/sprite.h>
#include <akgl/actor.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
akerr_ErrorContext *akgl_actor_initialize(akgl_Actor *obj, char *name)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "actor_initialize received null actor pointer");
FAIL_ZERO_RETURN(errctx, name, ERR_NULLPOINTER, "actor_initialize received null name string pointer");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "akgl_actor_initialize received null actor pointer");
FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "akgl_actor_initialize received null name string pointer");
memset(obj, 0x00, sizeof(actor));
strncpy((char *)obj->name, name, ACTOR_MAX_NAME_LENGTH);
memset(obj, 0x00, sizeof(akgl_Actor));
strncpy((char *)obj->name, name, AKGL_ACTOR_MAX_NAME_LENGTH);
obj->curSpriteReversing = false;
obj->scale = 1.0;
obj->movement_controls_face = true;
obj->updatefunc = &actor_update;
obj->renderfunc = &actor_render;
obj->facefunc = &actor_automatic_face;
obj->movementlogicfunc = &actor_logic_movement;
obj->changeframefunc = &actor_logic_changeframe;
obj->addchild = &actor_add_child;
obj->updatefunc = &akgl_actor_update;
obj->renderfunc = &akgl_actor_render;
obj->facefunc = &akgl_actor_automatic_face;
obj->movementlogicfunc = &akgl_actor_logic_movement;
obj->changeframefunc = &akgl_actor_logic_changeframe;
obj->addchild = &akgl_actor_add_child;
FAIL_ZERO_RETURN(
errctx,
SDL_SetPointerProperty(REGISTRY_ACTOR, name, (void *)obj),
ERR_KEY,
SDL_SetPointerProperty(AKGL_REGISTRY_ACTOR, name, (void *)obj),
AKERR_KEY,
"Unable to add actor to registry"
);
obj->refcount += 1;
@@ -40,34 +44,38 @@ ErrorContext *actor_initialize(actor *obj, char *name)
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference");
FAIL_ZERO_RETURN(errctx, basecharname, ERR_NULLPOINTER, "Null character reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor 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");
obj->ax = 0;
obj->ay = 0;
obj->sx = obj->basechar->sx;
obj->sy = obj->basechar->sy;
SUCCEED_RETURN(errctx);
}
ErrorContext *actor_automatic_face(actor *obj)
akerr_ErrorContext *akgl_actor_automatic_face(akgl_Actor *obj)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
ATTEMPT {
if ( obj->movement_controls_face == true ) {
// TODO : This doesn't really work properly
BITMASK_DEL(obj->state, ACTOR_STATE_FACE_ALL);
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_LEFT);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_RIGHT);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_UP);
} else if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) {
BITMASK_ADD(obj->state, ACTOR_STATE_FACE_DOWN);
AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_FACE_ALL);
if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_LEFT);
} else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_RIGHT);
} else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_UP);
} else if ( AKGL_BITMASK_HAS(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
AKGL_BITMASK_ADD(obj->state, AKGL_ACTOR_STATE_FACE_DOWN);
}
}
} CLEANUP {
@@ -76,10 +84,10 @@ ErrorContext *actor_automatic_face(actor *obj)
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "Null actor reference");
ATTEMPT {
// are we currently looping in reverse?
if ( curSprite->loop == true && obj->curSpriteReversing == true ) {
@@ -91,13 +99,14 @@ ErrorContext *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time cu
obj->curSpriteFrameId -= 1;
}
// 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?
if ( curSprite->loop == true && curSprite->loopReverse == true ) {
obj->curSpriteReversing = true;
obj->curSpriteFrameId -= 1;
// 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;
}
// we are not looping in reverse and we are not at the end of the animation
@@ -110,83 +119,70 @@ ErrorContext *actor_logic_changeframe(actor *obj, sprite *curSprite, SDL_Time cu
SUCCEED_RETURN(errctx);
}
ErrorContext *actor_logic_movement(actor *obj, SDL_Time curtimems)
// raises AKGL_ERR_LOGICINTERRUPT if we don't want the physics simulator to process us
akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "Null actor reference");
if ( obj->parent != NULL ) {
// Children don't move independently of their parents, they just have an offset
SUCCEED_RETURN(errctx);
} else {
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) {
obj->x -= obj->basechar->vx;
}
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) {
obj->x += obj->basechar->vx;
}
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) {
obj->y -= obj->basechar->vy;
}
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) {
obj->y += obj->basechar->vy;
}
FAIL_ZERO_RETURN(errctx, actor, AKERR_NULLPOINTER, "actor");
FAIL_ZERO_RETURN(errctx, actor, AKERR_NULLPOINTER, "actor->basechar");
actor->sx = actor->basechar->sx;
actor->sy = actor->basechar->sy;
actor->sz = actor->basechar->sz;
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) {
actor->ax = -actor->basechar->ax;
} else if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
actor->ax = actor->basechar->ax;
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ) {
actor->ay = -actor->basechar->ay;
} else if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
actor->ay = actor->basechar->ay;
}
SUCCEED_RETURN(errctx);
}
ErrorContext *actor_update(actor *obj)
akerr_ErrorContext *akgl_actor_update(akgl_Actor *obj)
{
PREPARE_ERROR(errctx);
SDL_Time curtime = 0;
SDL_Time curtimems = 0;
sprite *curSprite = NULL;
akgl_Sprite *curSprite = NULL;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor reference");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor reference");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
SDL_GetCurrentTime(&curtime);
PASS(errctx, obj->facefunc(obj));
ATTEMPT {
SDL_GetCurrentTime(&curtime);
curtimems = curtime / 1000000;
CATCH(errctx, obj->facefunc(obj));
// is it time to apply movement logic?
if ( (curtimems - obj->logictimer) >= obj->basechar->movementspeed ) {
CATCH(errctx, obj->movementlogicfunc(obj, curtimems));
obj->logictimer = curtimems;
CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
if ( ((curtime) - obj->curSpriteFrameTimer) >= curSprite->speed) {
CATCH(errctx, obj->changeframefunc(obj, curSprite, curtime));
obj->curSpriteFrameTimer = curtime;
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, false);
ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite));
// is it time to change frames?
if ( (curtimems - obj->curSpriteFrameTimer) >= curSprite->speed ) {
CATCH(errctx, obj->changeframefunc(obj, curSprite, curtimems));
obj->curSpriteFrameTimer = curtimems;
}
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_KEY) {
} HANDLE(errctx, AKERR_KEY) {
// TODO : Why are we passing this error? It could only come from akgl_character_sprite_get
// or changeframefunc, both of which should never return AKERR_KEY...
SUCCEED_RETURN(errctx);
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
static 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);
sprite *curSprite = NULL;
akgl_Sprite *curSprite = NULL;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, ERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite));
CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
} CLEANUP {
} 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?
*visible = false;
SUCCEED_RETURN(errctx);
@@ -203,24 +199,24 @@ static ErrorContext *actor_visible(actor *obj, SDL_FRect *camera, bool *visible)
SUCCEED_RETURN(errctx);
}
ErrorContext *actor_render(actor *obj, SDL_Renderer *renderer)
akerr_ErrorContext *akgl_actor_render(akgl_Actor *obj)
{
PREPARE_ERROR(errctx);
sprite *curSprite = NULL;
akgl_Sprite *curSprite = NULL;
bool visible = false;
SDL_FRect src;
SDL_FRect dest;
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, renderer, ERR_NULLPOINTER, "NULL renderer");
FAIL_ZERO_RETURN(errctx, obj->basechar, ERR_NULLPOINTER, "Actor has NULL base character reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Actor has NULL base character reference");
ATTEMPT {
CATCH(errctx, character_sprite_get(obj->basechar, obj->state, &curSprite));
CATCH(errctx, actor_visible(obj, &camera, &visible));
CATCH(errctx, akgl_character_sprite_get(obj->basechar, obj->state, &curSprite));
CATCH(errctx, actor_visible(obj, camera, &visible));
} CLEANUP {
} 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
LOG_ERROR(errctx);
} FINISH(errctx, true);
@@ -229,38 +225,47 @@ ErrorContext *actor_render(actor *obj, SDL_Renderer *renderer)
SUCCEED_RETURN(errctx);
}
src.x = curSprite->width * curSprite->frameids[obj->curSpriteFrameId];
if ( src.x >= curSprite->sheet->texture->w ) {
src.y = ((int)src.x / curSprite->sheet->texture->w) * curSprite->height;
src.x = ((int)src.x % curSprite->sheet->texture->w);
} else {
src.y = 0;
if ( (obj->curSpriteFrameId > curSprite->frames) ) {
// This isn't necessarily an error - this actor's frame index is outside the range of
// their current sprite. There are a number of reasons this could happen, and it will
// get cleaned up on the next logic update. Just pass on rendering them this frame.
SUCCEED_RETURN(errctx);
}
src.w = curSprite->width;
src.h = curSprite->height;
if ( obj->parent != NULL ) {
dest.x = (obj->parent->x + obj->x - camera.x);
dest.y = (obj->parent->y + obj->y - camera.y);
} else {
dest.x = (obj->x - camera.x);
dest.y = (obj->y - camera.y);
}
dest.w = curSprite->width;
dest.h = curSprite->width;
SDL_RenderTexture(renderer, curSprite->sheet->texture, &src, &dest);
ATTEMPT {
CATCH(errctx,
akgl_sprite_sheet_coords_for_frame(
curSprite,
&src,
obj->curSpriteFrameId)
);
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
if ( obj->parent != NULL ) {
dest.x = (obj->parent->x + obj->x - camera->x);
dest.y = (obj->parent->y + obj->y - camera->y);
} else {
dest.x = (obj->x - camera->x);
dest.y = (obj->y - camera->y);
}
dest.w = curSprite->width * obj->scale;
dest.h = curSprite->width * obj->scale;
PASS(errctx, renderer->draw_texture(renderer, curSprite->sheet->texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
SUCCEED_RETURN(errctx);
}
ErrorContext *actor_add_child(actor *obj, actor *child)
akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child)
{
int i = 0;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL parent pointer");
FAIL_ZERO_RETURN(errctx, child, ERR_NULLPOINTER, "NULL child pointer");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL parent pointer");
FAIL_ZERO_RETURN(errctx, child, AKERR_NULLPOINTER, "NULL child pointer");
FAIL_NONZERO_RETURN(errctx, child->parent, ERR_RELATIONSHIP, "Child object already has a parent");
for ( i = 0; i < ACTOR_MAX_CHILDREN ; i++ ) {
FAIL_NONZERO_RETURN(errctx, child->parent, AKERR_RELATIONSHIP, "Child object already has a parent");
for ( i = 0; i < AKGL_ACTOR_MAX_CHILDREN ; i++ ) {
if ( obj->children[i] == NULL ) {
obj->children[i] = child;
child->parent = obj;
@@ -268,153 +273,119 @@ ErrorContext *actor_add_child(actor *obj, actor *child)
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
// 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)
akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
iterator *opflags = (iterator *)userdata;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, name, ERR_NULLPOINTER, "registry_iterate_actor received NULL property name");
FAIL_ZERO_BREAK(errctx, opflags, ERR_NULLPOINTER, "received NULL iterator flags");
actor *obj = (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");
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_LAYERMASK) ) {
if ( obj->layer != opflags->layerid ) {
break;
}
}
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_UPDATE) ) {
CATCH(errctx, obj->updatefunc(obj));
}
if ( BITMASK_HAS(opflags->flags, ITERATOR_OP_RENDER) ) {
CATCH(errctx, obj->renderfunc(obj, renderer));
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);
}
ErrorContext ERROR_NOIGNORE *SDL3GActor_cmhf_left_on(actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) {
SUCCEED_RETURN(errctx);
}
SDL_Log("event %d (button %d / key %d) moves 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_MOVING_LEFT | ACTOR_STATE_FACE_LEFT));
SDL_Log("new target actor state: %b", obj->state);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "event");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "actor->basechar");
//SDL_Log("event %d (button %d / key %d) moves actor left", event->type, event->gbutton.which, event->key.key);
AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL));
obj->ax = -(obj->basechar->ax);
AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_FACE_LEFT));
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_LEFT) ) {
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) stops moving actor left", event->type, event->gbutton.which, event->key.key);
obj->ax = 0;
obj->ex = 0;
obj->tx = 0;
obj->vx = 0;
AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT);
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) {
SUCCEED_RETURN(errctx);
}
SDL_Log("event %d (button %d / key %d) moves 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_MOVING_RIGHT | ACTOR_STATE_FACE_RIGHT));
SDL_Log("new target actor state: %b", obj->state);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "actor->basechar");
//SDL_Log("event %d (button %d / key %d) moves actor right", event->type, event->gbutton.which, event->key.key);
obj->ax = obj->basechar->ax;
AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL));
AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_FACE_RIGHT));
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_RIGHT) ) {
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) stops moving actor right", event->type, event->gbutton.which, event->key.key);
obj->ax = 0;
obj->ex = 0;
obj->tx = 0;
obj->vx = 0;
AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) {
SUCCEED_RETURN(errctx);
}
SDL_Log("event %d (button %d / key %d) moves 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 | ACTOR_STATE_MOVING_UP));
SDL_Log("new target actor state: %b", obj->state);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) moves actor up", event->type, event->gbutton.which, event->key.key);
obj->ay = -(obj->basechar->ay);
AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL));
AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_FACE_UP | AKGL_ACTOR_STATE_MOVING_UP));
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_UP) ) {
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) stops moving actor up", event->type, event->gbutton.which, event->key.key);
obj->ay = 0;
obj->ey = 0;
obj->ty = 0;
obj->vy = 0;
AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_UP);
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) {
SUCCEED_RETURN(errctx);
}
SDL_Log("event %d (button %d / key %d) moves 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_MOVING_DOWN | ACTOR_STATE_FACE_DOWN));
SDL_Log("new target actor state: %b", obj->state);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) moves actor down", event->type, event->gbutton.which, event->key.key);
obj->ay = obj->basechar->ay;
AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL));
AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN));
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, ERR_NULLPOINTER, "NULL event");
if ( !BITMASK_HAS(obj->state, ACTOR_STATE_MOVING_DOWN) ) {
SUCCEED_RETURN(errctx);
}
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);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
//SDL_Log("event %d (button %d / key %d) stops moving actor down", event->type, event->gbutton.which, event->key.key);
obj->ty = 0;
obj->ey = 0;
obj->ay = 0;
obj->vy = 0;
AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN);
//SDL_Log("new target actor state: %b", obj->state);
SUCCEED_RETURN(errctx);
}

View File

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

View File

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

View File

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

View File

@@ -1,30 +1,74 @@
#include <SDL3/SDL.h>
#include <sdlerror.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/game.h>
#include <sdl3game/controller.h>
#include <akerror.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/game.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 AKERR_NOIGNORE *akgl_controller_open_gamepads(void)
{
int count = 0;
int i = 0;
SDL_JoystickID *gamepads = NULL;
SDL_Gamepad *gamepad = NULL;
PREPARE_ERROR(e);
if ( SDL_HasGamepad() ) {
gamepads = SDL_GetGamepads(&count);
if ( count > 0 ) {
FAIL_ZERO_RETURN(e, gamepads, AKERR_NULLPOINTER, "%s", SDL_GetError());
for ( i = 0; i < count ; i++ ) {
gamepad = SDL_OpenGamepad(gamepads[i]);
FAIL_ZERO_RETURN(e, gamepad, AKERR_NULLPOINTER, "%s", SDL_GetError());
SDL_Log("Gamepad %d is %s", i, SDL_GetGamepadNameForID(gamepads[i]));
}
SDL_free(gamepads);
} else {
SDL_Log("No gamepads enumerated");
}
} else {
SDL_Log("No gamepads connected");
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *event)
{
int i = 0;
int j = 0;
int eventButtonComboMatch = 0;
SDL3GControlMap *curmap = NULL;
SDL3GControl *curcontrol = NULL;
akgl_ControlMap *curmap = NULL;
akgl_Control *curcontrol = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
ATTEMPT {
for ( i = 0 ; i < MAX_CONTROL_MAPS; i++ ) {
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
curmap = &GAME_ControlMaps[i];
if ( curmap->target == NULL ) {
continue;
}
//SDL_Log("Control map %d maps to actor %s", i, curmap->target->name);
//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];
//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);
@@ -39,121 +83,128 @@ ErrorContext *controller_handle_event(void *appstate, SDL_Event *event)
event->key.which == curmap->kbid &&
event->key.key == curcontrol->key)
);
if ( event->type == 768 && event->key.which == 11 && event->key.key == 13 ) {
SDL_Log("Event type=%d, keyboard=%d, key=%d", event->type, event->key.which, event->key.key);
SDL_Log("ControlMap[%d].Controls[%d] keyboard=%d, key=%d", i, j, curmap->kbid, curcontrol->key);
SDL_Log("event %d -> control on %d off %d", event->type, curcontrol->event_on, curcontrol->event_off);
SDL_Log("eventButtonComboMatch for controlmap %d id %d = %d",
i, j, eventButtonComboMatch);
}
if ( event->type == curcontrol->event_on && eventButtonComboMatch) {
CATCH(errctx, curcontrol->handler_on(curmap->target, event));
goto _controller_handle_event_success;
goto _akgl_controller_handle_event_success;
} else if ( event->type == curcontrol->event_off && eventButtonComboMatch ) {
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 {
} PROCESS(errctx) {
} FINISH(errctx, true);
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);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "Player actor does not exist");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) {
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 ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_DOWN);
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_DOWN);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_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 ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_UP);
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_UP);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_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 ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_LEFT);
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_LEFT);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_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 ) {
BITMASK_DEL(player->state, ACTOR_STATE_FACE_ALL);
BITMASK_ADD(player->state, ACTOR_STATE_FACE_RIGHT);
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_RIGHT);
}
SDL_Log("New state : %d", player->state);
}
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);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "Player actor does not exist");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) {
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;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_UP ) {
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;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_RIGHT) {
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;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_LEFT ) {
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;
SDL_Log("New state : %d", player->state);
}
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_Gamepad *gamepad = NULL;
char *mapping = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which);
@@ -175,14 +226,14 @@ ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
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_Gamepad *gamepad = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, ERR_NULLPOINTER, "NULL event");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which);
@@ -193,3 +244,112 @@ ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
SDL_Log("Gamepad #%u removed", (unsigned int) which);
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, AKGL_ERR_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_image/SDL_image.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 */
void GAME_draw_background(int w, int h)
void akgl_draw_background(int w, int h)
{
SDL_Color col[2] = {
{ 0x66, 0x66, 0x66, 0xff },
@@ -20,11 +20,11 @@ void GAME_draw_background(int w, int h)
for (x = 0; x < w; x += dx) {
/* use an 8x8 checkerboard pattern */
i = (((x ^ y) >> 3) & 1);
SDL_SetRenderDrawColor(renderer, col[i].r, col[i].g, col[i].b, col[i].a);
SDL_SetRenderDrawColor(renderer->sdl_renderer, col[i].r, col[i].g, col[i].b, col[i].a);
rect.x = (float)x;
rect.y = (float)y;
SDL_RenderFillRect(renderer, &rect);
SDL_RenderFillRect(renderer->sdl_renderer, &rect);
}
}
}

View File

@@ -1,86 +1,462 @@
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <stdio.h>
#include <sdlerror.h>
#include <akerror.h>
#include <semver.h>
#include <sdl3game/game.h>
#include <sdl3game/controller.h>
#include <sdl3game/tilemap.h>
#include <sdl3game/sprite.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/iterator.h>
#include <akstdlib.h>
#include <akgl/game.h>
#include <akgl/controller.h>
#include <akgl/tilemap.h>
#include <akgl/sprite.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/physics.h>
#include <akgl/SDL_GameControllerDB.h>
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
GAME_frame ball;
GAME_frame paddle1;
GAME_frame paddle2;
GAME_frame table;
tilemap gamemap;
MIX_Audio *bgm = NULL;
MIX_Mixer *GAME_mixer = NULL;
MIX_Track *GAME_tracks[64];
SDL_FRect camera;
Game game;
ErrorContext ERROR_NOIGNORE *GAME_init()
// Currently active objects
akgl_RenderBackend *renderer;
akgl_PhysicsBackend *physics;
SDL_FRect *camera;
akgl_Tilemap *gamemap;
akgl_Stage *stage;
// Default objects
akgl_RenderBackend _akgl_renderer;
akgl_PhysicsBackend _akgl_physics;
SDL_FRect _akgl_camera;
akgl_Tilemap _akgl_gamemap;
akgl_Stage _akgl_stage;
MIX_Audio *bgm = NULL;
MIX_Mixer *akgl_mixer = NULL;
MIX_Track *akgl_tracks[AKGL_GAME_AUDIO_MAX_TRACKS];
akgl_Game game;
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;
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.name), ERR_NULLPOINTER, "Must provide game name");
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.version), ERR_NULLPOINTER, "Must provide game version");
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.uri), ERR_NULLPOINTER, "Must provide game uri");
CATCH(errctx, heap_init());
CATCH(errctx, registry_init_actor());
CATCH(errctx, registry_init_sprite());
CATCH(errctx, registry_init_spritesheet());
CATCH(errctx, registry_init_character());
CATCH(errctx, registry_init_actor_state_strings());
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true)
PREPARE_ERROR(e);
akerr_name_for_status(AKGL_ERR_SDL, "SDL Error");
akerr_name_for_status(AKGL_ERR_LOGICINTERRUPT, "AKGL Logic Interrupt");
akerr_name_for_status(AKGL_ERR_REGISTRY, "AKGL Registry Error");
akerr_name_for_status(AKGL_ERR_OOHEAP, "AKGL Heap Capacity");
strncpy((char *)&game.libversion, AKGL_VERSION, 32);
game.gameStartTime = SDL_GetTicksNS();
game.lastIterTime = game.gameStartTime;
game.lastFPSTime = game.gameStartTime;
game.lowfpsfunc = &akgl_game_lowfps;
game.statelock = SDL_CreateMutex();
FAIL_ZERO_RETURN(e, game.statelock, AKGL_ERR_SDL, "%s", SDL_GetError());
PASS(e, akgl_game_state_lock());
FAIL_ZERO_RETURN(e, strlen((char *)&game.name), AKERR_NULLPOINTER, "Must provide game name");
FAIL_ZERO_RETURN(e, strlen((char *)&game.version), AKERR_NULLPOINTER, "Must provide game version");
FAIL_ZERO_RETURN(e, strlen((char *)&game.uri), AKERR_NULLPOINTER, "Must provide game uri");
PASS(e, akgl_heap_init());
PASS(e, akgl_registry_init_actor());
PASS(e, akgl_registry_init_sprite());
PASS(e, akgl_registry_init_spritesheet());
PASS(e, akgl_registry_init_character());
PASS(e, akgl_registry_init_font());
PASS(e, akgl_registry_init_music());
PASS(e, akgl_registry_init_properties());
PASS(e, akgl_registry_init_actor_state_strings());
SDL_SetAppMetadata(game.name, game.version, game.uri);
for ( i = 0 ; i < MAX_CONTROL_MAPS; i++ ) {
memset(&GAME_ControlMaps[i], 0x00, sizeof(SDL3GControlMap));
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
memset(&GAME_ControlMaps[i], 0x00, sizeof(akgl_ControlMap));
}
FAIL_ZERO_RETURN(
errctx,
e,
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO),
ERR_SDL,
AKGL_ERR_SDL,
"Couldn't initialize SDL: %s",
SDL_GetError());
FAIL_ZERO_RETURN(
errctx,
SDL_CreateWindowAndRenderer(game.uri, game.screenwidth, game.screenheight, 0, &window, &renderer),
ERR_SDL,
"Couldn't create window/renderer: %s",
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(e, 0, AKGL_ERR_SDL, "%s", SDL_GetError());
}
}
PASS(e, akgl_controller_open_gamepads());
FAIL_ZERO_RETURN(
errctx,
e,
MIX_Init(),
ERR_SDL,
AKGL_ERR_SDL,
"Couldn't initialize audio: %s",
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(
errctx,
game.mixer,
ERR_SDL,
e,
akgl_mixer,
AKGL_ERR_SDL,
"Unable to create mixer device: %s",
SDL_GetError());
camera.x = 0;
camera.y = 0;
camera.w = game.screenwidth;
camera.h = game.screenheight;
FAIL_ZERO_RETURN(
e,
TTF_Init(),
AKGL_ERR_SDL,
"Couldn't initialize front engine: %s",
SDL_GetError());
camera = &_akgl_camera;
renderer = &_akgl_renderer;
physics = &_akgl_physics;
gamemap = &_akgl_gamemap;
stage = &_akgl_stage;
PASS(e, akgl_game_state_unlock());
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void)
{
PREPARE_ERROR(e);
SDL_Time totaltime = 0;
while ( totaltime < AKGL_TIME_ONESEC_MS ) {
if ( SDL_TryLockMutex(game.statelock) == true ) {
SUCCEED_RETURN(e);
}
totaltime += 100;
SDL_Delay(100);
}
FAIL_RETURN(e, AKGL_ERR_SDL, "%s", SDL_GetError());
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_unlock(void)
{
PREPARE_ERROR(e);
SDL_UnlockMutex(game.statelock);
SUCCEED_RETURN(e);
}
void akgl_game_updateFPS()
{
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(e);
ATTEMPT {
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
actor = SDL_GetPointerProperty(props, name, NULL);
CATCH(e, aksl_fwrite(&actor, 1, sizeof(akgl_Actor *), fp));
} CLEANUP {
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_spritename_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_Sprite *sprite = NULL;
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
sprite = SDL_GetPointerProperty(props, name, NULL);
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&sprite, 1, sizeof(akgl_Sprite *), fp));
} CLEANUP {
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_spritesheetname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_SpriteSheet *spritesheet = NULL;
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
spritesheet = SDL_GetPointerProperty(props, name, NULL);
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&spritesheet, 1, sizeof(akgl_SpriteSheet *), fp));
} CLEANUP {
} PROCESS(e) {
} FINISH_NORETURN(e);
}
void akgl_game_save_charactername_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
PREPARE_ERROR(e);
akgl_Character *character = NULL;
ATTEMPT {
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
character = SDL_GetPointerProperty(props, name, NULL);
CATCH(e, aksl_fwrite((char *)name, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite(&character, 1, sizeof(akgl_Character *), fp));
} CLEANUP {
} PROCESS(e) {
} FINISH_NORETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
{
PREPARE_ERROR(e);
char nullval = 0x00;
ATTEMPT {
FAIL_ZERO_BREAK(e, fp, AKERR_NULLPOINTER, "NULL file pointer");
// write the actor name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_ACTOR,
&akgl_game_save_actorname_iterator,
(void *)fp);
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Actor *), fp));
// write the sprite name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITE,
&akgl_game_save_spritename_iterator,
(void *)fp);
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Sprite *), fp));
// write the spritesheet name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITESHEET,
&akgl_game_save_spritesheetname_iterator,
(void *)fp);
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_SpriteSheet *), fp));
// write the character name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_CHARACTER,
&akgl_game_save_charactername_iterator,
(void *)fp);
CATCH(e, aksl_fwrite((void *)&nullval, 1, AKGL_SPRITE_MAX_CHARACTER_NAME_LENGTH, fp));
CATCH(e, aksl_fwrite((void *)&nullval, 1, sizeof(akgl_Character *), fp));
} CLEANUP {
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
{
FILE *fp = NULL;
PREPARE_ERROR(e);
ATTEMPT {
FAIL_ZERO_BREAK(e, fpath, AKERR_NULLPOINTER, "NULL file path");
CATCH(e, aksl_fopen(fpath, "wb", &fp));
CATCH(e, aksl_fwrite(&game, 1, sizeof(akgl_Game), fp));
CATCH(e, akgl_game_save_actors(fp));
} PROCESS(e) {
} CLEANUP {
if ( fp != NULL )
fclose(fp);
} FINISH(e, true);
SUCCEED_RETURN(e); // SUCCEED_NORETURN if in main().
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_PropertiesID map, int namelength, int ptrlength, SDL_PropertiesID registry)
{
void *ptr = NULL;
char ptrstring[32];
char objname[namelength];
int retval = 0;
PREPARE_ERROR(e);
while ( 1 ) {
CATCH(e, aksl_fread((void *)&objname, 1, namelength, fp));
CATCH(e, aksl_fread((void *)&ptr, 1, ptrlength, fp));
// End of the map
if ( ptr == 0x00 && objname[0] == 0x00 ) {
break;
}
// 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(e, aksl_memset((void *)&ptrstring, 0x00, 32));
snprintf((char *)&ptrstring, 32, "%p", ptr);
SDL_SetPointerProperty(
map,
ptrstring,
SDL_GetPointerProperty(registry, objname, NULL));
};
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(char *versiontype, char *newversion, char *curversion)
{
semver_t current_version = {};
semver_t compare_version = {};
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, versiontype, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, curversion, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, newversion, AKERR_NULLPOINTER, "NULL argument");
ATTEMPT {
// Check save game library version
FAIL_NONZERO_BREAK(
e,
semver_parse((const char *)curversion, &current_version),
AKERR_VALUE,
"Invalid semantic %s version in current game: %s",
versiontype,
(char *)curversion);
FAIL_NONZERO_BREAK(
e,
semver_parse((const char *)newversion, &compare_version),
AKERR_VALUE,
"Invalid semantic %s version in save game: %s",
versiontype,
(char *)&newversion);
FAIL_ZERO_BREAK(
e,
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(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
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(e);
FAIL_ZERO_RETURN(e, fpath, AKERR_NULLPOINTER, "NULL file path");
ATTEMPT {
CATCH(e, aksl_fopen(fpath, "rb", &fp));
CATCH(e, aksl_fread((void *)&savegame, 1, sizeof(akgl_Game), fp));
CATCH(e, akgl_game_load_versioncmp("library", (char *)&savegame.libversion, (char *)AKGL_VERSION));
CATCH(e, akgl_game_load_versioncmp("game", (char *)&savegame.version, (char *)&game.version));
FAIL_NONZERO_RETURN(
e,
strncmp((char *)&savegame.name, (char *)&game.name, 256),
AKERR_API,
"Savegame is not compatible with this game");
FAIL_NONZERO_RETURN(
e,
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(e, akgl_game_load_objectnamemap(fp, actormap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Actor *), AKGL_REGISTRY_ACTOR));
// Load sprite name map
spritemap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, spritemap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Sprite *), AKGL_REGISTRY_SPRITE));
// Load spritesheet name map
spritesheetmap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, spritesheetmap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_SpriteSheet *), AKGL_REGISTRY_SPRITESHEET));
// Load character name map
charactermap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, charactermap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Character *), AKGL_REGISTRY_CHARACTER));
// Now that we have all of our pointer maps built, we can load the actual binary objects and reset their pointers
} CLEANUP {
if ( fp != NULL ) {
fclose(fp);
}
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {
.flags = (AKGL_ITERATOR_OP_LAYERMASK | AKGL_ITERATOR_OP_LAYERMASK),
.layerid = 0
};
SDL_Time curTime = SDL_GetTicksNS();
akgl_Actor *actor = NULL;
if ( opflags == NULL ) {
opflags = &defflags;
}
PASS(e, akgl_game_state_lock());
akgl_game_updateFPS();
for ( int i = 0; i < AKGL_TILEMAP_MAX_LAYERS; i++ ) {
if ( opflags == &defflags ) {
opflags->layerid = i;
}
for ( int j = 0; j < AKGL_MAX_HEAP_ACTOR; j++ ) {
actor = &HEAP_ACTOR[j];
if ( actor->refcount == 0 ) {
continue;
}
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_TILEMAPSCALE) ) {
PASS(e, akgl_tilemap_scale_actor(gamemap, actor));
} else {
actor->scale = 1.0;
}
PASS(e, actor->updatefunc(actor));
}
}
PASS(e, physics->simulate(physics, NULL));
PASS(e, renderer->draw_world(renderer, NULL));
PASS(e, akgl_game_state_unlock());
SUCCEED_RETURN(e);
}

View File

@@ -1,191 +1,431 @@
/** @file */
#include <stdlib.h>
#include <sdlerror.h>
#include <akerror.h>
#include <sdl3game/game.h>
#include <sdl3game/sprite.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/iterator.h>
#include <akgl/game.h>
#include <akgl/sprite.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/error.h>
#include <akgl/stage.h>
actor HEAP_ACTOR[MAX_HEAP_ACTOR];
sprite HEAP_SPRITE[MAX_HEAP_SPRITE];
spritesheet HEAP_SPRITESHEET[MAX_HEAP_SPRITESHEET];
character HEAP_CHARACTER[MAX_HEAP_CHARACTER];
string HEAP_STRING[MAX_HEAP_STRING];
akgl_Actor HEAP_ACTOR[AKGL_MAX_HEAP_ACTOR];
akgl_Sprite HEAP_SPRITE[AKGL_MAX_HEAP_SPRITE];
akgl_SpriteSheet HEAP_SPRITESHEET[AKGL_MAX_HEAP_SPRITESHEET];
akgl_Character HEAP_CHARACTER[AKGL_MAX_HEAP_CHARACTER];
akgl_String HEAP_STRING[AKGL_MAX_HEAP_STRING];
aksl_ListNode HEAP_LIST[AKGL_MAX_HEAP_LIST];
aksl_TreeNode HEAP_TREE[AKGL_MAX_HEAP_TREE];
akgl_BSPLeaf HEAP_TREE_LEAVES[AKGL_MAX_HEAP_TREE];
ErrorContext *heap_init()
void *AKGL_LIST_SENTINEL = (void *)1; /** Sentinel value used for aksl_ListNode objects to determine if they are available */
akerr_ErrorContext *akgl_heap_init()
{
PREPARE_ERROR(errctx);
int i = 0;
for ( i = 0; i < MAX_HEAP_ACTOR; i++) {
memset(&HEAP_ACTOR[i], 0x00, sizeof(actor));
akerr_name_for_status(AKGL_ERR_SDL, "SDL Error");
PASS(errctx, akgl_heap_init_actor());
PASS(errctx, akgl_heap_init_list());
PASS(errctx, akgl_heap_init_tree());
for ( i = 0; i < AKGL_MAX_HEAP_SPRITE; i++) {
memset(&HEAP_SPRITE[i], 0x00, sizeof(akgl_Sprite));
}
for ( i = 0; i < MAX_HEAP_SPRITE; i++) {
memset(&HEAP_SPRITE[i], 0x00, sizeof(sprite));
for ( i = 0; i < AKGL_MAX_HEAP_SPRITESHEET; i++) {
memset(&HEAP_SPRITESHEET[i], 0x00, sizeof(akgl_SpriteSheet));
}
for ( i = 0; i < MAX_HEAP_SPRITESHEET; i++) {
memset(&HEAP_SPRITESHEET[i], 0x00, sizeof(spritesheet));
for ( i = 0; i < AKGL_MAX_HEAP_CHARACTER; i++) {
memset(&HEAP_CHARACTER[i], 0x00, sizeof(akgl_Character));
}
for ( i = 0; i < MAX_HEAP_CHARACTER; i++) {
memset(&HEAP_CHARACTER[i], 0x00, sizeof(character));
}
for ( i = 0; i < MAX_HEAP_STRING; i++) {
memset(&HEAP_STRING[i], 0x00, sizeof(string));
for ( i = 0; i < AKGL_MAX_HEAP_STRING; i++) {
memset(&HEAP_STRING[i], 0x00, sizeof(akgl_String));
}
SUCCEED_RETURN(errctx);
}
ErrorContext *heap_next_actor(actor **dest)
akerr_ErrorContext *akgl_heap_init_list(void)
{
PREPARE_ERROR(e);
for ( int i = 0; i < AKGL_MAX_HEAP_LIST; i++) {
memset(&HEAP_LIST[i], 0x00, sizeof(aksl_ListNode));
HEAP_LIST[i].data = AKGL_LIST_SENTINEL;
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_heap_init_tree(void)
{
PREPARE_ERROR(e);
for ( int i = 0; i < AKGL_MAX_HEAP_TREE; i++) {
memset(&HEAP_TREE[i], 0x00, sizeof(aksl_TreeNode));
HEAP_TREE[i].leaf = AKGL_LIST_SENTINEL;
memset(&HEAP_TREE_LEAVES[i], 0x00, sizeof(akgl_BSPLeaf));
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_heap_init_actor(void)
{
PREPARE_ERROR(e);
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++) {
memset(&HEAP_ACTOR[i], 0x00, sizeof(akgl_Actor));
}
SUCCEED_RETURN(e);
}
/**
* @brief Return the next aksl_ListNode from the heap
*
* @param[out] dest The pointer that will hold the procured aksl_ListNode
*
* @throws AKGL_ERR_OOHEAP There are no available nodes in HEAP_LIST
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_list(aksl_ListNode **dest)
{
PREPARE_ERROR(errctx);
for (int i = 0; i < MAX_HEAP_ACTOR; i++ ) {
for (int i = 0; i < AKGL_MAX_HEAP_LIST; i++ ) {
if ( HEAP_LIST[i].data != AKGL_LIST_SENTINEL ) {
continue;
}
*dest = &HEAP_LIST[i];
HEAP_LIST[i].data = NULL;
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused list on the heap");
}
/**
* @brief Return the next aksl_TreeNode from the heap
*
* @param[out] dest The pointer that will hold the procured tree node
*
* @throws AKGL_ERR_OOHEAP There are no available nodes in HEAP_TREE
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_tree(aksl_TreeNode **dest)
{
PREPARE_ERROR(errctx);
for (int i = 0; i < AKGL_MAX_HEAP_TREE; i++ ) {
if ( HEAP_TREE[i].leaf != AKGL_LIST_SENTINEL ) {
continue;
}
*dest = &HEAP_TREE[i];
HEAP_TREE[i].leaf = &HEAP_TREE_LEAVES[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused tree on the heap");
}
/**
* @brief Release an aksl_ListNode that came from HEAP_TREE to make it available again
*
* @param[in] tree The aksl_ListNode to release.
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_list(aksl_ListNode *list)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, list, AKERR_NULLPOINTER, "list");
list->next = NULL;
list->prev = NULL;
list->data = (void *)AKGL_LIST_SENTINEL;
SUCCEED_RETURN(e);
}
/**
* @brief Release an aksl_TreeNode that came from HEAP_TREE to make it available again
*
* @param[in] tree The aksl_TreeNode to release. Ensure the leaf on this node has been released and the leaf is set to NULL.
*
* @throws AKERR_VALUE The aksl_TreeNode contains a leaf that has not been released.
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_tree(aksl_TreeNode *tree)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, tree, AKERR_NULLPOINTER, "tree");
akgl_BSPLeaf *leaf = NULL;
tree->left = NULL;
tree->right = NULL;
tree->parent = NULL;
leaf = (akgl_BSPLeaf *)tree->leaf;
if ( tree->leaf != NULL && leaf->actors != NULL ) {
FAIL_RETURN(e, AKERR_VALUE, "Can't release tree node %p until its leaf actors %p is released and leaf->actors == NULL", tree, leaf->actors);
} else if ( tree->leaf != NULL ) {
memset((void *)tree->leaf, 0x00, sizeof(akgl_BSPLeaf));
}
tree->leaf = AKGL_LIST_SENTINEL;
SUCCEED_RETURN(e);
}
/**
* @brief Iterator callback which releases a given list item back to the heap
*
* @param[in] ptr The aksl_ListNode to free
* @param[in] data Context data that was passed to aksl_list_iterate
*
* @throws AKERR_VALUE The aksl_TreeNode contains a leaf that has not been released.
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_list_release(aksl_ListNode *ptr, void *data)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, ptr, AKERR_NULLPOINTER, "ptr)");
PASS(e, akgl_heap_release_list(ptr));
SUCCEED_RETURN(e);
}
/**
* @brief Iterator callback which releases a given tree node back to the heap. Calls akgl_heap_release_list on the tree leaf as well.
*
* @param[in] ptr The aksl_TreeNode to free
* @param[in] data Context data that was passed to aksl_tree_iterate
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_tree_release(aksl_TreeNode *ptr, void *data)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, ptr, AKERR_NULLPOINTER, "ptr");
if ( ptr->leaf != AKGL_LIST_SENTINEL && ptr->leaf != NULL ) {
PASS(e, akgl_heap_release_list(ptr->leaf));
//ptr->leaf = NULL;
}
PASS(e, akgl_heap_release_tree(ptr));
SUCCEED_RETURN(e);
}
/**
* @brief Return the next actor from the heap
*
* @param[out] dest The pointer that will hold the procured actor
*
* @throws AKGL_ERR_OOHEAP There are no available actors in HEAP_ACTOR
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest)
{
PREPARE_ERROR(errctx);
for (int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
if ( HEAP_ACTOR[i].refcount != 0 ) {
continue;
}
*dest = &HEAP_ACTOR[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused actor on the heap");
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused actor on the heap");
}
ErrorContext *heap_next_sprite(sprite **dest)
/**
* @brief Return the next akgl_Sprite from the heap
*
* @param[out] dest The pointer that will hold the procured sprite
*
* @throws AKGL_ERR_OOHEAP There are no available actors in HEAP_SPRITE
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest)
{
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 ) {
continue;
}
*dest = &HEAP_SPRITE[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused sprite on the heap");
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused sprite on the heap");
}
ErrorContext *heap_next_spritesheet(spritesheet **dest)
/**
* @brief Return the next akgl_SpriteSheet from the heap
*
* @param[out] dest The pointer that will hold the procured spritesheet
*
* @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_SPRITESHEET
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest)
{
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 ) {
continue;
}
*dest = &HEAP_SPRITESHEET[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused spritesheet on the heap");
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused spritesheet on the heap");
}
ErrorContext *heap_next_character(character **dest)
/**
* @brief Return the next akgl_Character from the heap
*
* @param[out] dest The pointer that will hold the procured character
*
* @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_CHARACTER
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest)
{
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 ) {
continue;
}
*dest = &HEAP_CHARACTER[i];
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused character on the heap");
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused character on the heap");
}
ErrorContext *heap_next_string(string **dest)
/**
* @brief Return the next akgl_String from the heap
*
* @param[out] dest The pointer that will hold the procured string
*
* @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_STRING
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest)
{
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 ) {
continue;
}
*dest = &HEAP_STRING[i];
HEAP_STRING[i].refcount += 1;
SUCCEED_RETURN(errctx);
}
FAIL_RETURN(errctx, ERR_HEAP, "Unable to find unused string on the heap");
FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused string on the heap");
}
ErrorContext *heap_release_actor(actor *ptr)
/**
* @brief Release an akgl_Actor from the heap to be used again
*
* @param[in] ptr The object that will be released
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr)
{
int i = 0;
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 ) {
ptr->refcount -= 1;
}
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 ) {
CATCH_AND_RETURN(errctx, heap_release_actor(ptr->children[i]));
PASS(errctx, akgl_heap_release_actor(ptr->children[i]));
}
}
if ( ptr->basechar != NULL ) {
CATCH_AND_RETURN(errctx, heap_release_character(ptr->basechar));
}
memset(ptr, 0x00, sizeof(actor));
SDL_ClearProperty(REGISTRY_ACTOR, (char *)&ptr->name);
SDL_ClearProperty(AKGL_REGISTRY_ACTOR, (char *)&ptr->name);
memset(ptr, 0x00, sizeof(akgl_Actor));
}
SUCCEED_RETURN(errctx);
}
ErrorContext *heap_release_character(character *basechar)
/**
* @brief Release an akgl_Character from the heap to be used again
*
* @param[in] ptr The object that will be released
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_character(akgl_Character *ptr)
{
PREPARE_ERROR(errctx);
iterator opflags;
FAIL_ZERO_RETURN(errctx, basechar, ERR_NULLPOINTER, "NULL character reference");
BITMASK_CLEAR(opflags.flags);
BITMASK_ADD(opflags.flags, ITERATOR_OP_RELEASE);
akgl_Iterator opflags;
FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL character reference");
AKGL_BITMASK_CLEAR(opflags.flags);
if ( basechar->refcount > 0 ) {
basechar->refcount -= 1;
}
if ( basechar->refcount == 0 ) {
SDL_EnumerateProperties(basechar->state_sprites, &character_state_sprites_iterate, (void *)&opflags);
SDL_ClearProperty(REGISTRY_CHARACTER, (char *)&basechar->name);
}
SUCCEED_RETURN(errctx);
}
ErrorContext *heap_release_sprite(sprite *ptr)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "Received NULL sprite reference");
if ( ptr->refcount > 0 ) {
ptr->refcount -= 1;
}
if ( ptr->refcount == 0 ) {
ATTEMPT {
CATCH(errctx, heap_release_spritesheet(ptr->sheet));
} CLEANUP {
SDL_ClearProperty(REGISTRY_SPRITE, (char *)&ptr->name);
} PROCESS(errctx) {
} FINISH(errctx, true);
SDL_ClearProperty(AKGL_REGISTRY_CHARACTER, (char *)&ptr->name);
memset(ptr, 0x00, sizeof(akgl_Character));
}
SUCCEED_RETURN(errctx);
}
ErrorContext *heap_release_spritesheet(spritesheet *ptr)
/**
* @brief Release an akgl_Sprite from the heap to be used again
*
* @param[in] ptr The object that will be released
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_sprite(akgl_Sprite *ptr)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, ERR_NULLPOINTER, "Received NULL spritesheet reference");
FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL sprite reference");
if ( ptr->refcount > 0 ) {
ptr->refcount -= 1;
}
if ( ptr->refcount == 0 ) {
SDL_ClearProperty(AKGL_REGISTRY_SPRITE, (char *)&ptr->name);
memset(ptr, 0x00, sizeof(akgl_Sprite));
}
SUCCEED_RETURN(errctx);
}
/**
* @brief Release an akgl_SpriteSheet from the heap to be used again
*
* @param[in] ptr The object that will be released
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "Received NULL spritesheet reference");
if ( ptr->refcount > 0 ) {
ptr->refcount -= 1;
}
if ( ptr->refcount == 0 ) {
// 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 )
SDL_DestroyTexture(ptr->texture);
ptr->texture = NULL;
memset(ptr, 0x00, sizeof(akgl_SpriteSheet));
}
SUCCEED_RETURN(errctx);
}
ErrorContext *heap_release_string(string *ptr)
/**
* @brief Release an akgl_String from the heap to be used again
*
* @param[in] ptr The object that will be released
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext *akgl_heap_release_string(akgl_String *ptr)
{
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 ) {
ptr->refcount -= 1;
}
if ( ptr->refcount == 0 ) {
memset(&ptr->data, 0x00, MAX_STRING_LENGTH);
memset(&ptr->data, 0x00, AKGL_MAX_STRING_LENGTH);
}
SUCCEED_RETURN(errctx);
}

View File

@@ -1,131 +1,165 @@
#include <jansson.h>
#include <string.h>
#include <sdlerror.h>
#include <akerror.h>
#include <sdl3game/json_helpers.h>
#include <sdl3game/game.h>
#include <sdl3game/heap.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/registry.h>
#include <akgl/json_helpers.h>
#include <akgl/game.h>
#include <akgl/heap.h>
#include <akgl/staticstring.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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = value;
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_boolean(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_boolean_value(value);
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_integer_value(value);
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_number(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_number_value(value);
SUCCEED_RETURN(errctx);
}
ErrorContext *get_json_string_value(json_t *obj, char *key, string **dest)
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_double_value(json_t *obj, char *key, double *dest)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
json_t *value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_number(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = json_number_value(value);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_get_json_string_value(json_t *obj, char *key, akgl_String **dest)
{
json_t *value = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, key, ERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL pointer reference");
value = json_object_get(obj, key);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_string(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
ATTEMPT {
if ( *dest == NULL ) {
CATCH(errctx, heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL));
CATCH(errctx, akgl_heap_next_string(dest));
CATCH(errctx, akgl_string_initialize(*dest, NULL));
}
} CLEANUP {
} PROCESS(errctx) {
} 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);
}
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_KEY, "Key %s not found in object", key);
FAIL_ZERO_RETURN(errctx, (json_is_array(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
*dest = value;
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
*dest = value;
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);
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);
FAIL_ZERO_RETURN(errctx, value, ERR_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, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
*dest = json_integer_value(value);
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);
FAIL_ZERO_RETURN(errctx, array, ERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination pointer reference");
FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer reference");
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, (json_is_string(value)), ERR_TYPE, "Index %d in object has incorrect type", index);
FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
FAIL_ZERO_RETURN(errctx, (json_is_string(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
ATTEMPT {
if ( *dest == NULL ) {
CATCH(errctx, heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL));
CATCH(errctx, akgl_heap_next_string(dest));
CATCH(errctx, akgl_string_initialize(*dest, NULL));
}
} CLEANUP {
} PROCESS(errctx) {
} 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);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_with_default(akerr_ErrorContext *err, void *defval, void *dest, uint32_t defsize)
{
PREPARE_ERROR(e);
if ( err == NULL ) {
SUCCEED_RETURN(e);
}
int docopy = 0;
FAIL_ZERO_RETURN(e, err, AKERR_NULLPOINTER, "err");
FAIL_ZERO_RETURN(e, defval, AKERR_NULLPOINTER, "defval");
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dest");
ATTEMPT {
} CLEANUP {
} PROCESS(err) {
} HANDLE_GROUP(err, AKERR_KEY) {
} HANDLE_GROUP(err, AKERR_INDEX) {
memcpy(dest, defval, defsize);
} FINISH(err, true);
SUCCEED_RETURN(e);
}

283
src/physics.c Normal file
View File

@@ -0,0 +1,283 @@
#include <math.h>
#include <akstdlib.h>
#include <akgl/physics.h>
#include <akgl/actor.h>
#include <akgl/game.h>
#include <akgl/error.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/stage.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
self->gravity = akgl_physics_null_gravity;
self->collide = akgl_physics_null_collide;
self->move = akgl_physics_null_move;
self->simulate = akgl_physics_simulate;
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
if ( self->gravity_x != 0 ) {
// Assume the X origin is - (screen left)
actor->ex -= (self->gravity_x * dt);
}
if ( self->gravity_y != 0 ) {
// Assume Y origin is + (down screen)
actor->ey += (self->gravity_y * dt);
}
if ( self->gravity_z != 0 ) {
// Assume Z origin is - (behind the camera)
actor->ez -= (self->gravity_z * dt);
}
SUCCEED_RETURN(e);
}
/**
* @brief Test two actors for collision
*
* @param[in] self The physics simulation to use
* @param[in] a1 actor 1
* @param[in] a2 actor 2
*
* @throws AKERR_NULLPOINTER on null pointer input
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, a1, AKERR_NULLPOINTER, "a1");
FAIL_ZERO_RETURN(e, a2, AKERR_NULLPOINTER, "a2");
FAIL_RETURN(e, AKERR_API, "Not implemented");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
akgl_Sprite *curspr = NULL;
actor->x += actor->vx * dt;
actor->y += actor->vy * dt;
actor->z += actor->vz * dt;
// Set the actor's bounding box for physics
if ( actor->basechar != NULL ) {
PASS(e, actor->basechar->sprite_get(actor->basechar, actor->state, &curspr));
actor->bbox = (SDL_FRect){actor->x, actor->y, curspr->width, curspr->height};
} else {
actor->bbox = (SDL_FRect){actor->x, actor->y, 0, 0};
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend *self)
{
akgl_String *tmp;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
PASS(e, akgl_heap_next_string(&tmp));
self->gravity = akgl_physics_arcade_gravity;
self->collide = akgl_physics_arcade_collide;
self->move = akgl_physics_arcade_move;
self->simulate = akgl_physics_simulate;
ATTEMPT {
CATCH(e, akgl_get_property("physics.gravity.x", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_x));
CATCH(e, akgl_get_property("physics.gravity.y", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_y));
CATCH(e, akgl_get_property("physics.gravity.z", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->gravity_z));
CATCH(e, akgl_get_property("physics.drag.x", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_x));
CATCH(e, akgl_get_property("physics.drag.y", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_y));
CATCH(e, akgl_get_property("physics.drag.z", &tmp, "0.0"));
CATCH(e, aksl_atof(tmp->data, &self->drag_z));
} CLEANUP {
IGNORE(akgl_heap_release_string(tmp));
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
/**
* @brief Perform arcade physics simulation on all alive actors
*
* @param[in] self Physics simulator object
* @param[in] opflags Iteration operation flags (or NULL)
*
* @throws AKERR_NULLPOINTER on null pointer input
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {
.flags = 0,
.layerid = 0
};
SDL_Time curtime = SDL_GetTicksNS();
float32_t dt = (float32_t)(curtime - self->gravity_time) / (float32_t)AKGL_TIME_ONESEC_NS;
akgl_Actor *actor = NULL;
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, self->move, AKERR_NULLPOINTER, "self->move");
if ( opflags == NULL ) {
opflags = &defflags;
}
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
actor = &HEAP_ACTOR[i];
if ( actor->refcount == 0 ) {
continue;
}
if ( actor->parent != NULL ) {
// Children don't move independently of their parents, they just have an offset
actor->x = actor->parent->x + actor->vx;
actor->y = actor->parent->y + actor->vy;
actor->z = actor->parent->z + actor->vz;
continue;
} else if ( actor->basechar == NULL ) {
continue;
}
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) {
if ( actor->layer != opflags->layerid ) {
continue;
}
}
// thrust is a function of acceleration on a given axis
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ||
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
actor->tx += actor->ax * dt;
}
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ||
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
actor->ty += actor->ay * dt;
}
// velocity equals thrust unless thrust exceeds max speed
if ( fabsf(actor->tx) > fabsf(actor->sx) ) {
if ( actor->tx < 0 ) {
actor->tx = -actor->sx;
} else {
actor->tx = actor->sx;
}
}
if ( fabsf(actor->ty) > fabsf(actor->sy) ) {
if ( actor->ty < 0 ) {
actor->ty = -actor->sy;
} else {
actor->ty = actor->sy;
}
}
if ( fabsf(actor->tz) > fabsf(actor->sz) ) {
if ( actor->tz < 0 ) {
actor->tz = -actor->sz;
} else {
actor->tz = actor->sz;
}
}
ATTEMPT {
CATCH(e, actor->movementlogicfunc(actor, dt));
} CLEANUP {
} PROCESS(e) {
} HANDLE(e, AKGL_ERR_LOGICINTERRUPT) {
// noop
} FINISH(e, true);
PASS(e, self->gravity(self, actor, dt));
// Counteract velocity with atmospheric drag
if ( self->drag_x != 0 ) {
actor->ex -= actor->ex * self->drag_x * dt;
}
if ( self->drag_y != 0 ) {
actor->ey -= actor->ey * self->drag_y * dt;
}
if ( self->drag_z != 0 ) {
actor->ez -= actor->ez * self->drag_z * dt;
}
actor->vx = actor->ex + actor->tx;
actor->vy = actor->ey + actor->ty;
actor->vz = actor->ez + actor->tz;
PASS(e, self->move(self, actor, dt));
PASS(e, stage->partition_actor(stage, actor));
}
self->gravity_time = curtime;
SUCCEED_RETURN(e);
}
/**
* @brief Initialize a physics backend according to a string type
*
* @param[out] self The physics backend to initialize
* @param[in] type The type to initialize
*
* @throws AKERR_NULLPOINTER on null pointer input
* @throws AKERR_KEY on invalid type
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_factory(akgl_PhysicsBackend *self, akgl_String *type)
{
uint32_t hashval;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, type, AKERR_NULLPOINTER, "type");
if ( strncmp(type->data, "null", 4) == 0) {
PASS(e, akgl_physics_init_null(self));
SUCCEED_RETURN(e);
}
if ( strncmp(type->data, "arcade", 6) == 0) {
PASS(e, akgl_physics_init_arcade(self));
SUCCEED_RETURN(e);
}
FAIL_RETURN(e, AKERR_KEY, "Invalid physics engine %s", type->data);
}

View File

@@ -1,77 +1,187 @@
#include <SDL3/SDL.h>
#include <sdlerror.h>
#include <akerror.h>
#include <jansson.h>
#include <sdl3game/sprite.h>
#include <sdl3game/registry.h>
#include <sdl3game/iterator.h>
#include <sdl3game/actor.h>
#include <akstdlib.h>
#include <akgl/heap.h>
#include <akgl/sprite.h>
#include <akgl/registry.h>
#include <akgl/iterator.h>
#include <akgl/actor.h>
#include <akgl/json_helpers.h>
SDL_PropertiesID REGISTRY_ACTOR;
SDL_PropertiesID REGISTRY_ACTOR_STATE_STRINGS;
SDL_PropertiesID REGISTRY_SPRITE;
SDL_PropertiesID REGISTRY_SPRITESHEET;
SDL_PropertiesID REGISTRY_CHARACTER;
SDL_PropertiesID AKGL_REGISTRY_ACTOR = 0;
SDL_PropertiesID AKGL_REGISTRY_ACTOR_STATE_STRINGS = 0;
SDL_PropertiesID AKGL_REGISTRY_SPRITE = 0;
SDL_PropertiesID AKGL_REGISTRY_SPRITESHEET = 0;
SDL_PropertiesID AKGL_REGISTRY_CHARACTER = 0;
SDL_PropertiesID AKGL_REGISTRY_MUSIC = 0;
SDL_PropertiesID AKGL_REGISTRY_FONT = 0;
SDL_PropertiesID AKGL_REGISTRY_PROPERTIES = 0;
ErrorContext *registry_init()
akerr_ErrorContext *akgl_registry_init()
{
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, registry_init_spritesheet());
CATCH(errctx, registry_init_sprite());
CATCH(errctx, registry_init_character());
CATCH(errctx, registry_init_actor());
CATCH(errctx, registry_init_actor_state_strings());
CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, akgl_registry_init_character());
CATCH(errctx, akgl_registry_init_actor());
CATCH(errctx, akgl_registry_init_actor_state_strings());
CATCH(errctx, akgl_registry_init_font());
CATCH(errctx, akgl_registry_init_music());
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *registry_init_actor()
akerr_ErrorContext *akgl_registry_init_actor()
{
PREPARE_ERROR(errctx);
REGISTRY_ACTOR = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_ACTOR, ERR_NULLPOINTER, "Error initializing actor registry");
if ( AKGL_REGISTRY_ACTOR != 0 ) {
SDL_DestroyProperties(AKGL_REGISTRY_ACTOR);
}
AKGL_REGISTRY_ACTOR = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_ACTOR, AKERR_NULLPOINTER, "Error initializing actor registry");
SUCCEED_RETURN(errctx);
}
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 flag = 0;
PREPARE_ERROR(errctx);
REGISTRY_ACTOR_STATE_STRINGS = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_ACTOR_STATE_STRINGS, ERR_NULLPOINTER, "Error initializing actor state strings registry");
for ( i = 0 ; i < ACTOR_MAX_STATES; i++ ) {
AKGL_REGISTRY_ACTOR_STATE_STRINGS = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_ACTOR_STATE_STRINGS, AKERR_NULLPOINTER, "Error initializing actor state strings registry");
for ( i = 0 ; i < AKGL_ACTOR_MAX_STATES; i++ ) {
flag = (1 << i);
SDL_SetNumberProperty(
REGISTRY_ACTOR_STATE_STRINGS,
ACTOR_STATE_STRING_NAMES[i],
AKGL_REGISTRY_ACTOR_STATE_STRINGS,
AKGL_ACTOR_STATE_STRING_NAMES[i],
flag);
}
SUCCEED_RETURN(errctx);
}
ErrorContext *registry_init_sprite()
akerr_ErrorContext *akgl_registry_init_sprite()
{
PREPARE_ERROR(errctx);
REGISTRY_SPRITE = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_SPRITE, ERR_NULLPOINTER, "Error initializing sprite registry");
PREPARE_ERROR(errctx);
AKGL_REGISTRY_SPRITE = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_SPRITE, AKERR_NULLPOINTER, "Error initializing sprite registry");
SUCCEED_RETURN(errctx);
}
ErrorContext *registry_init_spritesheet()
akerr_ErrorContext *akgl_registry_init_spritesheet()
{
PREPARE_ERROR(errctx);
REGISTRY_SPRITESHEET = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_SPRITESHEET, ERR_NULLPOINTER, "Error initializing spritesheet registry");
AKGL_REGISTRY_SPRITESHEET = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_SPRITESHEET, AKERR_NULLPOINTER, "Error initializing spritesheet registry");
SUCCEED_RETURN(errctx);
}
ErrorContext *registry_init_character()
akerr_ErrorContext *akgl_registry_init_character()
{
PREPARE_ERROR(errctx);
REGISTRY_CHARACTER = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, REGISTRY_CHARACTER, ERR_NULLPOINTER, "Error initializing character registry");
AKGL_REGISTRY_CHARACTER = SDL_CreateProperties();
FAIL_ZERO_RETURN(errctx, AKGL_REGISTRY_CHARACTER, AKERR_NULLPOINTER, "Error initializing character registry");
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_RETURN(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);
}

136
src/renderer.c Normal file
View File

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

View File

@@ -2,171 +2,190 @@
#include <SDL3_image/SDL_image.h>
#include <string.h>
#include <jansson.h>
#include <sdlerror.h>
#include <akerror.h>
#include <libgen.h>
#include <sdl3game/game.h>
#include <sdl3game/sprite.h>
#include <sdl3game/json_helpers.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/iterator.h>
#include <akgl/game.h>
#include <akgl/sprite.h>
#include <akgl/json_helpers.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/iterator.h>
#include <akgl/util.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);
json_t *spritesheet_json = NULL;
int ss_frame_width = 0;
int ss_frame_height = 0;
string *ss_filename = NULL;
string *tmpstr = NULL;
akgl_String *ss_filename = NULL;
akgl_String *tmpstr = NULL;
ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr));
CATCH(errctx, string_initialize(tmpstr, NULL));
CATCH(errctx, get_json_object_value((json_t *)json, "spritesheet", &spritesheet_json));
CATCH(errctx, get_json_string_value((json_t *)spritesheet_json, "filename", &ss_filename));
if ( ss_filename->data[0] != '/' ) {
SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s/%s", relative_path, ss_filename->data);
} else {
SDL_snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s", ss_filename->data);
}
CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, akgl_get_json_object_value((json_t *)json, "spritesheet", &spritesheet_json));
CATCH(errctx, akgl_get_json_string_value((json_t *)spritesheet_json, "filename", &ss_filename));
CATCH(errctx, akgl_path_relative(relative_path, ss_filename->data, tmpstr));
*sheet = SDL_GetPointerProperty(
REGISTRY_SPRITESHEET,
AKGL_REGISTRY_SPRITESHEET,
(char *)&tmpstr->data,
NULL
);
if ( *sheet == NULL ) {
CATCH(errctx, heap_next_spritesheet(sheet));
CATCH(errctx, 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_heap_next_spritesheet(sheet));
CATCH(errctx, akgl_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_height", &ss_frame_width));
CATCH(errctx,
spritesheet_initialize(
(spritesheet *)*sheet,
akgl_spritesheet_initialize(
(akgl_SpriteSheet *)*sheet,
ss_frame_width,
ss_frame_height,
(char *)&tmpstr->data)
);
}
} CLEANUP {
IGNORE(heap_release_string(ss_filename));
IGNORE(heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(ss_filename));
IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *sprite_load_json(char *filename)
akerr_ErrorContext *akgl_sprite_load_json(char *filename)
{
PREPARE_ERROR(errctx);
json_t *json = NULL;
json_t *frames = NULL;
json_error_t error;
sprite *obj = NULL;
spritesheet *sheet = NULL;
string *spritename = NULL;
akgl_Sprite *obj = NULL;
akgl_SpriteSheet *sheet = NULL;
akgl_String *spritename = NULL;
//string *tmpstr = NULL;
int i = 0;
FAIL_ZERO_RETURN(errctx, filename, ERR_NULLPOINTER, "Received null filename");
FAIL_ZERO_RETURN(errctx, filename, AKERR_NULLPOINTER, "Received null filename");
ATTEMPT {
CATCH(errctx, heap_next_sprite(&obj));
//CATCH(errctx, heap_next_string(&tmpstr));
//CATCH(errctx, string_initialize(tmpstr, NULL));
CATCH(errctx, heap_next_string(&spritename));
CATCH(errctx, string_initialize(spritename, NULL));
CATCH(errctx, akgl_heap_next_sprite(&obj));
//CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
CATCH(errctx, akgl_heap_next_string(&spritename));
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);
FAIL_ZERO_BREAK(
errctx,
json,
ERR_NULLPOINTER,
AKERR_NULLPOINTER,
"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, get_json_string_value((json_t *)json, "name", &spritename));
CATCH(errctx, akgl_sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename)));
CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &spritename));
CATCH(errctx,
sprite_initialize(
(sprite *)obj,
akgl_sprite_initialize(
(akgl_Sprite *)obj,
spritename->data,
(spritesheet *)sheet)
(akgl_SpriteSheet *)sheet)
);
CATCH(errctx, get_json_integer_value((json_t *)json, "width", &obj->width));
CATCH(errctx, get_json_integer_value((json_t *)json, "height", &obj->height));
CATCH(errctx, get_json_integer_value((json_t *)json, "speed", &obj->speed));
CATCH(errctx, get_json_boolean_value((json_t *)json, "loop", &obj->loop));
CATCH(errctx, get_json_boolean_value((json_t *)json, "loopReverse", &obj->loopReverse));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "width", &obj->width));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "height", &obj->height));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "speed", &obj->speed));
obj->speed = obj->speed * AKGL_TIME_ONESEC_MS;
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);
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 {
if ( errctx != NULL && errctx->status != 0 ) {
IGNORE(heap_release_sprite(obj));
IGNORE(heap_release_spritesheet(sheet));
IGNORE(akgl_heap_release_sprite(obj));
IGNORE(akgl_heap_release_spritesheet(sheet));
}
IGNORE(heap_release_string(spritename));
//IGNORE(heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(spritename));
//IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);
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);
FAIL_ZERO_RETURN(errctx, spr, ERR_NULLPOINTER, "Null sprite reference");
FAIL_ZERO_RETURN(errctx, name, ERR_NULLPOINTER, "Empty sprite name");
FAIL_ZERO_RETURN(errctx, sheet, ERR_NULLPOINTER, "Null spritesheet reference");
FAIL_ZERO_RETURN(errctx, spr, AKERR_NULLPOINTER, "Null sprite reference");
FAIL_ZERO_RETURN(errctx, name, AKERR_NULLPOINTER, "Empty sprite name");
FAIL_ZERO_RETURN(errctx, sheet, AKERR_NULLPOINTER, "Null spritesheet reference");
memset(spr, 0x00, sizeof(sprite));
memcpy(spr->name, name, SPRITE_MAX_NAME_LENGTH);
memset(spr, 0x00, sizeof(akgl_Sprite));
memcpy(spr->name, name, AKGL_SPRITE_MAX_NAME_LENGTH);
spr->sheet = sheet;
FAIL_ZERO_RETURN(
errctx,
SDL_SetPointerProperty(REGISTRY_SPRITE, (char *)&spr->name, (void *)spr),
ERR_KEY,
SDL_SetPointerProperty(AKGL_REGISTRY_SPRITE, (char *)&spr->name, (void *)spr),
AKERR_KEY,
"Unable to add sprite to registry");
spr->refcount += 1;
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);
//string *tmpstr = NULL;
//akgl_String *tmpstr = NULL;
ATTEMPT {
FAIL_ZERO_BREAK(errctx, sheet, ERR_NULLPOINTER, "Null spritesheet pointer");
FAIL_ZERO_BREAK(errctx, filename, ERR_NULLPOINTER, "Null filename pointer");
FAIL_ZERO_BREAK(errctx, sheet, AKERR_NULLPOINTER, "Null spritesheet 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));
strncpy((char *)&sheet->name, filename, SPRITE_SHEET_MAX_FILENAME_LENGTH);
//CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
strncpy((char *)&sheet->name, filename, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH);
//snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
sheet->texture = IMG_LoadTexture(renderer, filename);
FAIL_ZERO_BREAK(errctx, sheet->texture, ERR_SDL, "Failed loading asset %s : %s", filename, SDL_GetError());
//snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), filename);
sheet->texture = IMG_LoadTexture(renderer->sdl_renderer, filename);
FAIL_ZERO_BREAK(errctx, sheet->texture, AKGL_ERR_SDL, "Failed loading asset %s : %s", filename, SDL_GetError());
FAIL_ZERO_BREAK(
errctx,
SDL_SetPointerProperty(REGISTRY_SPRITESHEET, (char *)sheet->name, (void *)sheet),
ERR_KEY,
SDL_SetPointerProperty(AKGL_REGISTRY_SPRITESHEET, (char *)sheet->name, (void *)sheet),
AKERR_KEY,
"Unable to add spritesheet to registry: %s",
SDL_GetError());
sheet->refcount += 1;
} CLEANUP {
//IGNORE(heap_release_string(tmpstr));
//IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);

373
src/stage.c Normal file
View File

@@ -0,0 +1,373 @@
#include <SDL3/SDL.h>
#include <akstdlib.h>
#include <akgl/stage.h>
#include <akgl/actor.h>
#include <akgl/heap.h>
#include <akgl/game.h>
/**
* @brief Initialize a 2D stage
*
* @param[in] self akgl_Stage * to initialize
*
* @throw AKERR_NULLPOINTER on null input pointers
* @return akerr_ErrorContext
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d(akgl_Stage *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
memset((void *)self, 0x00, sizeof(akgl_Stage));
self->partition = akgl_stage_2d_partition;
self->partition_actor = akgl_stage_2d_bsp_move;
memset((void *)&self->bsp, 0x00, sizeof(aksl_TreeNode));
self->bsp.leaf = &self->_rootleaf;
memset((void *)&self->_rootleaf, 0x00, sizeof(akgl_BSPLeaf));
SUCCEED_RETURN(e);
}
/**
* @brief Flip the current partitioning direction
*
* @param[in] One of AKGL_STAGE_PARTITION_HORIZONTAL or AKGL_STAGE_PARTITION_VERTICAL
* @param[out] dest Location to place the modified direction
*
* @throws AKERR_NULLPOINTER on null pointer inputs
* @throws AKERR_VALUE on invalid direction
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition_switchdirection(uint8_t direction, uint8_t *dest)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dest");
if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) {
*dest = AKGL_STAGE_PARTITION_VERTICAL;
} else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) {
*dest = AKGL_STAGE_PARTITION_HORIZONTAL;
} else {
FAIL_RETURN(e, AKERR_VALUE, "Unknown partition direction %d", direction);
}
SUCCEED_RETURN(e);
}
/**
* @brief Iterator function that sorts an actor into one of three lists based on colliding rectangles
*
* This function tests which of two rectangles ("left" and "right") in the context collide with the actor.
* The actor is sorted into the appropriate linked list for each given rectangle.
* If the actor collides with neither rectangle, it is pushed onto the parent list.
* Note that the existing node is not pushed - a new node is taken from the heap and pushed.
* Otherwise we would have to pop the actor out of whatever list it was in, which would
* modify the list during iteration.
*
* @param[in] node A list node containing an akgl_Actor in ->data
* @param[in] data A pointer to akgl_BSPContext containing left, right, and parent SDL_FRect structures
*
* @throws AKERR_NULLPOINTER on null pointer inputs
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_actoriter(aksl_ListNode *node, void *data)
{
bool e1intersect;
bool e2intersect;
akgl_BSPContext *context = NULL;
akgl_Actor *actor = NULL;
aksl_TreeNode *dest = NULL;
aksl_ListNode *newnode = NULL;
akgl_BSPLeaf *leaf = NULL;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node");
FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data");
FAIL_ZERO_RETURN(e, node->data, AKERR_NULLPOINTER, "node->data");
context = (akgl_BSPContext *)data;
actor = (akgl_Actor *)node->data;
FAIL_ZERO_RETURN(e, context->left, AKERR_NULLPOINTER, "left");
FAIL_ZERO_RETURN(e, context->right, AKERR_NULLPOINTER, "right");
FAIL_ZERO_RETURN(e, context->parent, AKERR_NULLPOINTER, "parent");
FAIL_ZERO_RETURN(e, ((akgl_BSPLeaf *)context->left->leaf)->actors, AKERR_NULLPOINTER, "left->leaf");
FAIL_ZERO_RETURN(e, ((akgl_BSPLeaf *)context->right->leaf)->actors, AKERR_NULLPOINTER, "right->leaf");
FAIL_ZERO_RETURN(e, ((akgl_BSPLeaf *)context->parent->leaf)->actors, AKERR_NULLPOINTER, "parent->leaf");
leaf = (akgl_BSPLeaf *)context->left->leaf;
e1intersect = SDL_HasRectIntersectionFloat(&leaf->extent, &actor->bbox);
leaf = (akgl_BSPLeaf *)context->right->leaf;
e2intersect = SDL_HasRectIntersectionFloat(&leaf->extent, &actor->bbox);
if ( e1intersect && e2intersect ) {
// Actors who cross left/right boundaries get left in the context of their parent, so both
// child spaces test them for collision
dest = context->parent;
} else if ( e1intersect ) {
dest = context->left;
} else if ( e2intersect ) {
dest = context->right;
}
if ( dest != NULL ) {
PASS(e, akgl_heap_next_list(&newnode));
newnode->data = node->data;
leaf = (akgl_BSPLeaf *)dest->leaf;
PASS(e, aksl_list_push(leaf->actors, newnode));
actor->bsphome = dest;
actor->bsplistnode = newnode;
}
SUCCEED_RETURN(e);
}
/**
* @brief Recursive function used to divide the stage into a BSP tree of actor near neighbors
*
* This is a recursive function that builds a balanced tree until the tree is maxdepth levels deep.
* It creates a list of all actors which fit within the extents of the root node,
* then successively divides that actor list down into the extents of the child nodes of the balanced tree,
* until the maximum recursion depth has been achieved.
*
* @param[in] root The root node of the current level of the tree
* @param[in] actors The list of all actors that are within the root node's extents
* @param[in] extents The {x, y, w, h} extents of this level of the tree
* @param[in] direction The AKL_STAGE_PARTITION_* direction in which the extent is being divided
* @param[in] depth The depth of the current root node. Callers should provide 1 here.
* @param[in] maxdepth The maximum depth to which we should recurse
*
* @throws AKERR_VALUE Invalid direction provided
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp(aksl_TreeNode *root, aksl_ListNode *actors, SDL_FRect extents, uint8_t direction, uint8_t depth, uint8_t maxdepth)
{
akgl_BSPContext context = {0, 0, 0};
akgl_BSPLeaf *leaf = NULL;
// Until we have reached maxdepth levels of depth
PREPARE_ERROR(e);
if ( depth > maxdepth ) {
SUCCEED_RETURN(e);
}
// - Get a pointer to the left and right nodes of the tree representing the halves
// - Divide the current working space into two equal halves
// - Construct a SDL_FRect representing the two equal halves
// (the top half of a vertical division is "left", bottom half is "right")
if ( root->left == NULL ) {
PASS(e, akgl_heap_next_tree((aksl_TreeNode **)&root->left));
PASS(e, akgl_heap_next_list((aksl_ListNode **)&((akgl_BSPLeaf *)root->left->leaf)->actors));
root->left->parent = root;
leaf = root->left->leaf;
if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) {
leaf->extent.x = extents.x;
leaf->extent.y = extents.y;
leaf->extent.w = extents.w;
leaf->extent.h = (extents.h / 2);
} else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) {
leaf->extent.x = extents.x;
leaf->extent.y = extents.y;
leaf->extent.w = (extents.w / 2);
leaf->extent.h = extents.h;
} else {
FAIL_RETURN(e, AKERR_VALUE, "Invalid partition direction %d", direction);
}
}
if ( root->right == NULL ) {
PASS(e, akgl_heap_next_tree((aksl_TreeNode **)&root->right));
PASS(e, akgl_heap_next_list((aksl_ListNode **)&((akgl_BSPLeaf *)root->right->leaf)->actors));
root->right->parent = root;
leaf = root->right->leaf;
if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) {
leaf->extent.x = extents.x;
leaf->extent.y = extents.y + (extents.h / 2);
leaf->extent.w = extents.w;
leaf->extent.h = (extents.h / 2);
} else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) {
leaf->extent.x = extents.x + (extents.w / 2);
leaf->extent.y = extents.y;
leaf->extent.w = (extents.w / 2);
leaf->extent.h = extents.h;
} else {
FAIL_RETURN(e, AKERR_VALUE, "Invalid partition direction %d", direction);
}
}
if ( ((akgl_BSPLeaf *)root->leaf)->actors == NULL ) {
PASS(e, akgl_heap_next_list((aksl_ListNode **)&((akgl_BSPLeaf *)root->leaf)->actors));
}
context.left = root->left;
context.right = root->right;
context.parent = root;
// - Test (SDL_HasIntersection) all actor objects in the current working space
// against the SDL_Frects for both halves. If they intersect, push the actors
// down into the leaf linked lists for each half
ATTEMPT {
CATCH(e, aksl_list_iterate(actors, &akgl_stage_2d_bsp_actoriter, &context));
} CLEANUP {
} PROCESS(e) {
} HANDLE(e, AKERR_NULLPOINTER) {
SDL_Log("Empty actor list");
} FINISH(e, true);
PASS(e, akgl_stage_2d_partition_switchdirection(direction, &direction));
// - Recurse down into the left BSP node, changing our partitioning
// (vertical->horizontal, horizontal->vertical etc)
PASS(e, akgl_stage_2d_bsp(root->left, ((akgl_BSPLeaf *)root->left->leaf)->actors, ((akgl_BSPLeaf *)context.left->leaf)->extent, direction, depth + 1, maxdepth));
// - Recurse down into the right BSP node
// (vertical->horizontal, horizontal->vertical etc)
PASS(e, akgl_stage_2d_bsp(root->right, ((akgl_BSPLeaf *)root->right->leaf)->actors, ((akgl_BSPLeaf *)context.right->leaf)->extent, direction, depth + 1, maxdepth));
SUCCEED_RETURN(e);
}
/**
* @brief Release the stage's curent BSP tree
*
* This recursive function traverses down all nodes of the stage's BSP tree and release all the tree and list nodes
*
* @param[in] self The stage whose BSP tree should be freed
*
* @throws AKERR_NULLPOINTER on NULL pointer inputs
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bspfree(akgl_Stage *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
PASS(e, aksl_tree_iterate(&self->bsp, &akgl_heap_iter_tree_release, NULL, NULL, AKSL_TREE_SEARCH_DFS, NULL, NULL));
SUCCEED_RETURN(e);
}
/**
* @brief Partition the stage into a BSP tree, grouping near neighbor actors together
*
* @param[in] self The stage to partition
*
* @return akerr_ErrorContext*
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition(akgl_Stage *self)
{
aksl_ListNode *actors = NULL;
aksl_ListNode *curnode = NULL;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
PASS(e, akgl_stage_2d_bspfree(self));
self->bsp.leaf = &self->_rootleaf;
memset((void *)&self->_rootleaf, 0x00, sizeof(akgl_BSPLeaf));
// Build a linked list of all actors currently initialized
PASS(e, akgl_heap_next_list(&actors));
curnode = actors;
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
if ( HEAP_ACTOR[i].refcount != 0 ) {
curnode->data = (void *)&HEAP_ACTOR[i];
PASS(e, akgl_heap_next_list(&curnode->next));
curnode = curnode->next;
}
}
/* This could be better.
* Right now we divide the extents into 16 equal rectangles and sort actors
* into them by their bbox coordinates. This doesn't help when lots of actors are
* stacked up unequally on one area of the screen. A better solution might be to
* continue subdividing a given extent until it is small enough that only
* N number of actors fit inside of it. All of this is overkill for something like
* a simple Mario style sidescroller or even a JRPG, but for something like
* a bullet hell, it will be essential.
*/
((akgl_BSPLeaf *)self->bsp.leaf)->extent = (SDL_FRect){0, 0, camera->w, camera->h};
ATTEMPT {
CATCH(e, akgl_stage_2d_bsp(
&self->bsp,
actors,
// Define the current working space as the boundaries of the entire stage
// (from the origin to the extents of the largest and/or furthest object)
// .... let's see what we get if we just cheat and use the camera extents.
// (Do we *really* want off-screen objects in the collision detection?)
(SDL_FRect){0.0, 0.0, camera->w, camera->h},
// Define if we are partitioning horizontally or vertically
AKGL_STAGE_PARTITION_VERTICAL,
// Begin depth counter at 1
1,
// Maximum recursion depth
AKGL_STAGE_PARTITION_MAXDEPTH)
);
} CLEANUP {
PREPARE_ERROR(e2);
PASS(e2, aksl_list_iterate(actors, &akgl_heap_iter_list_release, NULL));
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
/**
* @brief Move this actor to the correct part of the BSP tree
*
* @param[in] self akgl_Stage *
* @param[in] actor akgl_Actor *
*
* @throws AKERR_NULLPOINTER on null pointer inputs
* @return akerr_ErrorContext
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_move(akgl_Stage *self, akgl_Actor *actor)
{
aksl_ListNode *tmp = NULL;
aksl_TreeNode *curnode = NULL;
akgl_BSPContext context;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
if ( actor->bsphome == NULL ) {
// New actor that is not currently part of the BSP tree. Push it down.
PASS(e, akgl_heap_next_list(&tmp));
tmp->data = actor;
ATTEMPT {
PASS(e, akgl_stage_2d_bsp(
&self->bsp,
tmp,
(SDL_FRect){0.0, 0.0, camera->w, camera->h},
AKGL_STAGE_PARTITION_VERTICAL,
1,
AKGL_STAGE_PARTITION_MAXDEPTH)
);
} CLEANUP {
PREPARE_ERROR(e2);
PASS(e2, akgl_heap_release_list(tmp));
} PROCESS(e) {
} FINISH(e, true);
} else {
// Actor already exists somewhere in the BSP tree
// 1. Is it still in the extents for its bsphome? If so, do nothing.
if ( SDL_HasRectIntersectionFloat(&((akgl_BSPLeaf *)actor->bsphome->leaf)->extent, &actor->bbox) ) {
SUCCEED_RETURN(e);
}
// 2. If not, pop the actor out of its current BSP actor list, and start walking up.
PASS(e, aksl_list_pop(actor->bsplistnode));
curnode = actor->bsphome->parent;
tmp = actor->bsplistnode;
while ( curnode != NULL ) {
// 3. Is it within the current BSP node's extents? If not, walk up and repeat.
if ( SDL_HasRectIntersectionFloat(&((akgl_BSPLeaf *)curnode->leaf)->extent, &actor->bbox) ) {
// 4. Find which extent it matches.
// FIXME : akgl_BSPContext should be replaced with aksl_TreeNode at this point, it's fully redundant
context.left = curnode->left;
context.right = curnode->right;
context.parent = curnode->parent;
PASS(e, akgl_stage_2d_bsp_actoriter(actor->bsplistnode, &context));
}
curnode = curnode->parent;
}
// Release the old actor node, it will have been moved to a new one by now
if ( tmp == actor->bsplistnode ) {
// Odd ...
SDL_Log("Expected actor %p to be moved to a different BSP node after moving, but it still has the same list node...\n", actor);
} else {
PASS(e, akgl_heap_release_list(tmp));
}
}
SUCCEED_RETURN(e);
}

View File

@@ -1,15 +1,30 @@
#include <sdlerror.h>
#include <sdl3game/staticstring.h>
#include <akerror.h>
#include <akgl/staticstring.h>
#include <errno.h>
ErrorContext *string_initialize(string *obj, char *init)
akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init)
{
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 ) {
strncpy((char *)&obj->data, init, MAX_STRING_LENGTH);
strncpy((char *)&obj->data, init, AKGL_MAX_STRING_LENGTH);
} else {
memset(&obj->data, 0x00, sizeof(string));
memset(&obj->data, 0x00, sizeof(akgl_String));
}
obj->refcount = 1;
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_string_copy(akgl_String *src, akgl_String *dst, int count)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
if ( count == 0 ) {
count = AKGL_MAX_STRING_LENGTH;
}
if ( (char *)dst->data != strncpy((char *)&dst->data, (char *)&src->data, count) ) {
FAIL_RETURN(e, errno, "strncpy");
}
SUCCEED_RETURN(e);
}

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->sdl_renderer, textsurf);
FAIL_ZERO_RETURN(errctx, texture, AKERR_NULLPOINTER, "%s", SDL_GetError());
dest.x = x;
dest.y = y;
SDL_GetTextureSize(texture, &dest.w, &dest.h);
PASS(errctx, renderer->draw_texture(renderer, texture, NULL, &dest, 0, NULL, SDL_FLIP_NONE));
SDL_DestroyTexture(texture);
SDL_DestroySurface(textsurf);
SUCCEED_RETURN(errctx);
}

View File

@@ -1,127 +1,158 @@
#include <string.h>
#include <libgen.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_mixer/SDL_mixer.h>
#include <string.h>
#include <jansson.h>
#include <sdlerror.h>
#include <sdl3game/tilemap.h>
#include <sdl3game/actor.h>
#include <sdl3game/json_helpers.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/staticstring.h>
#include <sdl3game/game.h>
#include <akerror.h>
#include <akstdlib.h>
ErrorContext *get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest)
#include <akgl/tilemap.h>
#include <akgl/actor.h>
#include <akgl/json_helpers.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/staticstring.h>
#include <akgl/game.h>
#include <akgl/util.h>
akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest)
{
PREPARE_ERROR(errctx);
json_t *properties = NULL;
json_t *property = NULL;
string *tmpstr = NULL;
akgl_String *tmpstr = NULL;
akgl_String *typestr = NULL;
int i = 0;
// This is not a generic JSON helper. It assumes we are receiving an object with a 'properties' key
// inside of it. That key is an array of objects, and each object has a name, type, and value.
FAIL_ZERO_RETURN(errctx, obj, ERR_NULLPOINTER, "NULL json obj reference");
FAIL_ZERO_RETURN(errctx, key, ERR_NULLPOINTER, "NULL key string");
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL json obj reference");
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
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++) {
CATCH(errctx, get_json_array_index_object(properties, i, &property));
CATCH(errctx, get_json_string_value(property, "name", &tmpstr));
CATCH(errctx, akgl_get_json_array_index_object(properties, i, &property));
CATCH(errctx, akgl_get_json_string_value(property, "name", &tmpstr));
if ( strcmp(tmpstr->data, key) != 0 ) {
CATCH(errctx, heap_release_string(tmpstr));
CATCH(errctx, akgl_heap_release_string(tmpstr));
continue;
}
CATCH(errctx, get_json_string_value(property, "type", &tmpstr));
if ( strcmp(tmpstr->data, type) != 0 ) {
FAIL_BREAK(errctx, ERR_TYPE, "Character property is present but is incorrect type");
CATCH(errctx, akgl_get_json_string_value(property, "type", &typestr));
if ( strcmp(typestr->data, type) != 0 ) {
FAIL_BREAK(errctx, AKERR_TYPE, "Property %s is present but is incorrect type(expected %s got %s)", key, type, (char *)typestr->data);
}
*dest = property;
SUCCEED_RETURN(errctx);
}
} CLEANUP {
if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(tmpstr));
}
if ( typestr != NULL ) {
IGNORE(akgl_heap_release_string(typestr));
}
} PROCESS(errctx) {
} 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);
json_t *property;
ATTEMPT {
CATCH(errctx, get_json_tilemap_property(obj, key, "string", &property));
CATCH(errctx, heap_next_string(dest));
CATCH(errctx, string_initialize(*dest, NULL));
CATCH(errctx, get_json_string_value(property, "value", dest));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
PASS(errctx, akgl_get_json_tilemap_property(obj, key, "string", &property));
PASS(errctx, akgl_heap_next_string(dest));
PASS(errctx, akgl_string_initialize(*dest, NULL));
PASS(errctx, akgl_get_json_string_value(property, "value", dest));
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);
json_t *property = NULL;
ATTEMPT {
CATCH(errctx, get_json_tilemap_property(obj, key, "int", &property));
CATCH(errctx, get_json_integer_value(property, "value", dest));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
PASS(errctx, akgl_get_json_tilemap_property(obj, key, "int", &property));
PASS(errctx, akgl_get_json_integer_value(property, "value", dest));
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);
string *tmpstr = NULL;
ATTEMPT {
CATCH(errctx, get_json_integer_value((json_t *)tileset, "columns", &dest->tilesets[tsidx].columns));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "firstgid", &dest->tilesets[tsidx].firstgid));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "imageheight", &dest->tilesets[tsidx].imageheight));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "imagewidth", &dest->tilesets[tsidx].imagewidth));
CATCH(errctx, get_json_integer_value((json_t *)tileset, "margin", &dest->tilesets[tsidx].margin));
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));
strncpy((char *)&dest->tilesets[tsidx].name,
(char *)&tmpstr->data,
TILEMAP_MAX_TILESET_NAME_SIZE
);
CATCH(errctx, get_json_string_value((json_t *)tileset, "image", &tmpstr));
snprintf((char *)&dest->tilesets[tsidx].imagefilename,
TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s%s",
SDL_GetBasePath(),
tmpstr->data
);
dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer, (char *)&dest->tilesets[tsidx].imagefilename);
FAIL_ZERO_BREAK(errctx, dest->tilesets[tsidx].texture, ERR_NULLPOINTER, "Failed loading tileset image");
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
json_t *property = NULL;
PASS(errctx, akgl_get_json_tilemap_property(obj, key, "number", &property));
PASS(errctx, akgl_get_json_number_value(property, "value", dest));
SUCCEED_RETURN(errctx);
}
ErrorContext *tilemap_compute_tileset_offsets(tilemap *dest, int tilesetidx)
akerr_ErrorContext *akgl_get_json_properties_float(json_t *obj, char *key, float *dest)
{
PREPARE_ERROR(errctx);
json_t *property = NULL;
PASS(errctx, akgl_get_json_tilemap_property(obj, key, "float", &property));
PASS(errctx, akgl_get_json_number_value(property, "value", dest));
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_get_json_properties_double(json_t *obj, char *key, double *dest)
{
PREPARE_ERROR(errctx);
json_t *property = NULL;
PASS(errctx, akgl_get_json_tilemap_property(obj, key, "float", &property));
PASS(errctx, akgl_get_json_double_value(property, "value", dest));
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname)
{
PREPARE_ERROR(e);
akgl_String *tmpstr = NULL;
akgl_String *tmppath = NULL;
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "columns", &dest->tilesets[tsidx].columns));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "firstgid", &dest->tilesets[tsidx].firstgid));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "imageheight", &dest->tilesets[tsidx].imageheight));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "imagewidth", &dest->tilesets[tsidx].imagewidth));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "margin", &dest->tilesets[tsidx].margin));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "spacing", &dest->tilesets[tsidx].spacing));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tilecount", &dest->tilesets[tsidx].tilecount));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tileheight", &dest->tilesets[tsidx].tileheight));
PASS(e, akgl_get_json_integer_value((json_t *)tileset, "tilewidth", &dest->tilesets[tsidx].tilewidth));
PASS(e, akgl_get_json_string_value((json_t *)tileset, "name", &tmpstr));
PASS(e, akgl_heap_next_string(&tmpstr));
PASS(e, akgl_heap_next_string(&tmppath));
ATTEMPT {
strncpy((char *)&dest->tilesets[tsidx].name,
(char *)&tmpstr->data,
AKGL_TILEMAP_MAX_TILESET_NAME_SIZE
);
CATCH(e, akgl_get_json_string_value((json_t *)tileset, "image", &tmpstr));
CATCH(e, akgl_path_relative((char *)&dirname->data, (char *)&tmpstr->data, tmppath));
strncpy((char *)&dest->tilesets[tsidx].imagefilename, tmppath->data, AKGL_MAX_STRING_LENGTH);
} CLEANUP {
IGNORE(akgl_heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(tmppath));
} PROCESS(e) {
} FINISH(e, true);
dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer->sdl_renderer, (char *)&dest->tilesets[tsidx].imagefilename);
FAIL_ZERO_RETURN(e, dest->tilesets[tsidx].texture, AKERR_NULLPOINTER, "Failed loading tileset image : %s", SDL_GetError());
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int tilesetidx)
{
int x_offset = 0;
int y_offset = 0;
@@ -176,11 +207,11 @@ ErrorContext *tilemap_compute_tileset_offsets(tilemap *dest, int tilesetidx)
SUCCEED_RETURN(errctx);
}
ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root)
akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "Received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "Received NULL json object pointer");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "Received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "Received NULL json object pointer");
json_t *tilesets = NULL;
json_t *jstileset = NULL;
@@ -188,11 +219,11 @@ ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root)
dest->numtilesets = 0;
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++) {
CATCH(errctx, get_json_array_index_object((json_t *)tilesets, i, &jstileset));
CATCH(errctx, tilemap_load_tilesets_each(jstileset, dest, i));
CATCH(errctx, tilemap_compute_tileset_offsets(dest, i));
CATCH(errctx, akgl_get_json_array_index_object((json_t *)tilesets, i, &jstileset));
CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i, dirname));
CATCH(errctx, akgl_tilemap_compute_tileset_offsets(dest, i));
dest->numtilesets += 1;
}
} CLEANUP {
@@ -202,37 +233,37 @@ ErrorContext *tilemap_load_tilesets(tilemap *dest, json_t *root)
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, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
string *tmpstr = NULL;
actor *actorobj = NULL;
akgl_String *tmpstr = NULL;
akgl_Actor *actorobj = NULL;
curobj->type = TILEMAP_OBJECT_TYPE_ACTOR;
curobj->type = AKGL_TILEMAP_OBJECT_TYPE_ACTOR;
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 {
CATCH(errctx, heap_next_string(&tmpstr));
actorobj = SDL_GetPointerProperty(REGISTRY_ACTOR, (char *)&curobj->name, NULL);
CATCH(errctx, akgl_heap_next_string(&tmpstr));
actorobj = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, (char *)&curobj->name, NULL);
if ( actorobj == NULL ) {
CATCH(errctx, heap_next_actor(&actorobj));
CATCH(errctx, actor_initialize((actor *)actorobj, (char *)&curobj->name));
CATCH(errctx, get_json_properties_string((json_t *)layerdatavalue, "character", &tmpstr));
CATCH(errctx, akgl_heap_next_actor(&actorobj));
CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorobj, (char *)&curobj->name));
CATCH(errctx, akgl_get_json_properties_string((json_t *)layerdatavalue, "character", &tmpstr));
CATCH(errctx,
actor_set_character(
(actor *)actorobj,
akgl_actor_set_character(
(akgl_Actor *)actorobj,
(char *)&tmpstr->data
)
);
} else {
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 {
if ( tmpstr != NULL ) {
IGNORE(heap_release_string(tmpstr));
IGNORE(akgl_heap_release_string(tmpstr));
}
} PROCESS(errctx) {
} FINISH(errctx, true);
@@ -241,72 +272,80 @@ ErrorContext *tilemap_load_layer_object_actor(tilemap_object *curobj, json_t *la
actorobj->x = curobj->x;
actorobj->y = curobj->y;
actorobj->visible = curobj->visible;
curobj->actorptr = (actor *)actorobj;
curobj->actorptr = (akgl_Actor *)actorobj;
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, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
json_t *layerdata = NULL;
json_t *layerdatavalue = NULL;
int j;
int len;
tilemap_layer *curlayer = NULL;
tilemap_object *curobj = NULL;
string *tmpstr = NULL;
akgl_TilemapLayer *curlayer = NULL;
akgl_TilemapObject *curobj = NULL;
akgl_String *tmpstr = NULL;
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
ATTEMPT {
CATCH(errctx, get_json_array_value(root, "objects", &layerdata));
len = json_array_size((json_t *)layerdata);
curlayer = &dest->layers[layerid];
for ( j = 0; j < len; j++ ) {
CATCH(errctx, get_json_array_index_object((json_t *)layerdata, j, &layerdatavalue));
curobj = &curlayer->objects[j];
CATCH(errctx, get_json_string_value((json_t *)layerdatavalue, "name", &tmpstr));
strncpy((char *)curobj->name, tmpstr->data, ACTOR_MAX_NAME_LENGTH);
CATCH(errctx, heap_release_string(tmpstr));
CATCH(errctx, get_json_number_value((json_t *)layerdatavalue, "x", &curobj->x));
CATCH(errctx, get_json_number_value((json_t *)layerdatavalue, "y", &curobj->y));
CATCH(errctx, get_json_boolean_value((json_t *)layerdatavalue, "visible", &curobj->visible));
PASS(errctx, akgl_get_json_array_value(root, "objects", &layerdata));
len = json_array_size((json_t *)layerdata);
curlayer = &dest->layers[layerid];
for ( j = 0; j < len; j++ ) {
PASS(errctx, akgl_get_json_array_index_object((json_t *)layerdata, j, &layerdatavalue));
curobj = &curlayer->objects[j];
PASS(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "name", &tmpstr));
strncpy((char *)curobj->name, tmpstr->data, AKGL_ACTOR_MAX_NAME_LENGTH);
PASS(errctx, akgl_heap_release_string(tmpstr));
PASS(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "x", &curobj->x));
PASS(errctx, akgl_get_json_number_value((json_t *)layerdatavalue, "y", &curobj->y));
PASS(errctx, akgl_get_json_boolean_value((json_t *)layerdatavalue, "visible", &curobj->visible));
CATCH(errctx, get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr));
if ( strcmp(tmpstr->data, "actor") == 0 ) {
CATCH(errctx, tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid));
PASS(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr));
if ( strcmp(tmpstr->data, "actor") == 0 ) {
PASS(errctx, akgl_tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid, dirname));
} else if ( strcmp(tmpstr->data, "perspective") == 0 ) {
curobj->visible = false;
if ( strcmp((char *)curobj->name, "p_foreground") == 0 ) {
dest->p_foreground_y = curobj->y;
PASS(errctx, akgl_get_json_integer_value((json_t *)layerdatavalue, "height", &dest->p_foreground_h));
PASS(errctx, akgl_get_json_properties_float((json_t *)layerdatavalue, "scale", &dest->p_foreground_scale));
} else if ( strcmp((char *)curobj->name, "p_vanishing") == 0 ) {
dest->p_vanishing_y = curobj->y;
PASS(errctx, akgl_get_json_integer_value((json_t *)layerdatavalue, "height", &dest->p_vanishing_h));
PASS(errctx, akgl_get_json_properties_float((json_t *)layerdatavalue, "scale", &dest->p_vanishing_scale));
}
layerdatavalue = NULL;
}
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
layerdatavalue = NULL;
}
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, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
json_t *layerdata = NULL;
int j;
int layerdatalen;
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
ATTEMPT {
CATCH(errctx, get_json_integer_value(root, "height", &dest->layers[layerid].height));
CATCH(errctx, get_json_integer_value(root, "width", &dest->layers[layerid].width));
CATCH(errctx, get_json_array_value(root, "data", &layerdata));
CATCH(errctx, akgl_get_json_integer_value(root, "height", &dest->layers[layerid].height));
CATCH(errctx, akgl_get_json_integer_value(root, "width", &dest->layers[layerid].width));
CATCH(errctx, akgl_get_json_array_value(root, "data", &layerdata));
layerdatalen = (dest->layers[layerid].width * dest->layers[layerid].height);
if ( layerdatalen >= (TILEMAP_MAX_WIDTH * TILEMAP_MAX_HEIGHT) ) {
FAIL_BREAK(errctx, ERR_OUTOFBOUNDS, "Map layer exceeds the maximum size");
if ( layerdatalen >= (AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT) ) {
FAIL_BREAK(errctx, AKERR_OUTOFBOUNDS, "Map layer exceeds the maximum size");
}
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 {
} PROCESS(errctx) {
@@ -315,47 +354,85 @@ ErrorContext *tilemap_load_layer_tile(tilemap *dest, json_t *root, int layerid)
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, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "tilemap_load_layers received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, ERR_NULLPOINTER, "tilemap_load_layers received NULL json object pointer");
json_t *layers = NULL;
json_t *layer = NULL;
string *tmpstr = NULL;
int i;
int tmpint;
akgl_String *tmpstr;
akgl_String *fpath;
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
ATTEMPT {
CATCH(errctx, 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));
DISABLE_GCC_WARNING_FORMAT_TRUNCATION
snprintf((char *)&fpath->data,
AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE,
"%s/%s",
dirname->data,
tmpstr->data
);
RESTORE_GCC_WARNINGS
dest->layers[layerid].texture = IMG_LoadTexture(renderer->sdl_renderer, (char *)fpath->data);
FAIL_ZERO_BREAK(errctx, dest->layers[layerid].texture, AKGL_ERR_SDL, "%s", SDL_GetError());
dest->layers[layerid].width = dest->layers[layerid].texture->w;
dest->layers[layerid].height = dest->layers[layerid].texture->h;
} 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, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL tilemap pointer");
FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL json object pointer");
FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname");
json_t *layers = NULL;
json_t *layer = NULL;
akgl_String *tmpstr = NULL;
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);
for ( i = 0; i < dest->numlayers; i++) {
if ( i >= TILEMAP_MAX_LAYERS ) {
FAIL_BREAK(errctx, ERR_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;
if ( i >= AKGL_TILEMAP_MAX_LAYERS ) {
FAIL_BREAK(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum number of layers");
}
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, get_json_boolean_value((json_t *)layer, "visible", &dest->layers[i].visible));
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));
CATCH(errctx, akgl_get_json_string_value((json_t *)layer, "type", &tmpstr));
SDL_Log("Layer %d has type %s", layerid, tmpstr->data);
if ( strncmp((char *)tmpstr->data, "objectgroup", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[i].type = TILEMAP_LAYER_TYPE_OBJECTS;
CATCH(errctx, tilemap_load_layer_objects((tilemap *)dest, (json_t *)layer, i));
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_OBJECTS;
CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
} else if ( strncmp((char *)tmpstr->data, "tilelayer", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[i].type = TILEMAP_LAYER_TYPE_TILES;
CATCH(errctx, tilemap_load_layer_tile((tilemap *)dest, (json_t *)layer, i));
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_TILES;
CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
} else if ( strncmp((char *)tmpstr->data, "imagelayer", strlen((char *)tmpstr->data)) == 0 ) {
dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_IMAGE;
CATCH(errctx, akgl_tilemap_load_layer_image((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname));
}
layer = NULL;
layerid += 1;
}
} CLEANUP {
} PROCESS(errctx) {
@@ -363,53 +440,156 @@ ErrorContext *tilemap_load_layers(tilemap *dest, json_t *root)
SUCCEED_RETURN(errctx);
}
ErrorContext *tilemap_load(char *fname, tilemap *dest)
akerr_ErrorContext *akgl_tilemap_load_physics(akgl_Tilemap *dest, json_t *root)
{
PREPARE_ERROR(e);
json_t *props = NULL;
akgl_String *tmpval = NULL;
double defzero = 0.0;
ATTEMPT {
CATCH(e, akgl_heap_next_string(&tmpval));
CATCH(e, akgl_get_json_array_value((json_t *)root, "properties", &props));
} CLEANUP {
IGNORE(akgl_heap_release_string(tmpval));
} PROCESS(e) {
} HANDLE(e, AKERR_KEY) {
// Map has no properties, do nothing
SDL_Log("Map has no properties");
SUCCEED_RETURN(e);
} FINISH(e, true);
ATTEMPT {
CATCH(e, akgl_heap_next_string(&tmpval));
CATCH(e, akgl_get_json_properties_string(
root,
"physics.model",
&tmpval
)
);
PASS(e, akgl_physics_factory(&dest->physics, tmpval));
dest->use_own_physics = true;
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.gravity.x", &dest->physics.gravity_x
),
(void *)&defzero,
(void *)&dest->physics.gravity_x,
sizeof(double)));
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.gravity.y", &dest->physics.gravity_y
),
(void *)&defzero,
(void *)&dest->physics.gravity_y,
sizeof(double)));
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.gravity.z", &dest->physics.gravity_z
),
(void *)&defzero,
(void *)&dest->physics.gravity_z,
sizeof(double)));
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.drag.x", &dest->physics.drag_x
),
(void *)&defzero,
(void *)&dest->physics.drag_x,
sizeof(double)));
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.drag.y", &dest->physics.drag_y
),
(void *)&defzero,
(void *)&dest->physics.drag_y,
sizeof(double)));
CATCH(e, akgl_get_json_with_default(
akgl_get_json_properties_double(
root, "physics.drag.z", &dest->physics.drag_z
),
(void *)&defzero,
(void *)&dest->physics.drag_z,
sizeof(double)));
} CLEANUP {
IGNORE(akgl_heap_release_string(tmpval));
} PROCESS(e) {
} HANDLE(e, AKERR_KEY) {
SDL_Log("Map uses game physics");
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
{
PREPARE_ERROR(errctx);
json_t *json = NULL;
string *tmpstr = NULL;
//akgl_String *tmpstr = NULL;
json_error_t error;
akgl_String *dirnamestr = NULL;
FAIL_ZERO_RETURN(errctx, fname, ERR_NULLPOINTER, "load_tilemap received null filename");
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "load_tilemap received null tilemap");
memset(dest, 0x00, sizeof(tilemap));
FAIL_ZERO_RETURN(errctx, fname, AKERR_NULLPOINTER, "load_tilemap received null filename");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "load_tilemap received null tilemap");
memset(dest, 0x00, sizeof(akgl_Tilemap));
dest->p_foreground_scale = 1.0;
dest->p_vanishing_scale = 1.0;
PASS(errctx, akgl_heap_next_string(&dirnamestr));
ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr));
CATCH(errctx, string_initialize(tmpstr, NULL));
SDL_snprintf(tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname);
json = json_load_file(tmpstr->data, 0, &error);
//CATCH(errctx, akgl_heap_next_string(&tmpstr));
//CATCH(errctx, akgl_string_initialize(tmpstr, NULL));
//SDL_snprintf(tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname);
CATCH(errctx, aksl_realpath(fname, (char *)&dirnamestr->data));
dirname((char *)&dirnamestr->data);
json = json_load_file(fname, 0, &error);
FAIL_ZERO_BREAK(
errctx,
json,
ERR_NULLPOINTER,
AKERR_NULLPOINTER,
"Error while loading tilemap from %s on line %d: %s-",
tmpstr->data,
fname,
error.line,
error.text
);
CATCH(errctx, get_json_integer_value((json_t *)json, "tileheight", &dest->tileheight));
CATCH(errctx, get_json_integer_value((json_t *)json, "tilewidth", &dest->tilewidth));
CATCH(errctx, 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_tilemap_load_physics(dest, json));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "tileheight", &dest->tileheight));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "tilewidth", &dest->tilewidth));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "height", &dest->height));
CATCH(errctx, akgl_get_json_integer_value((json_t *)json, "width", &dest->width));
dest->orientation = 0;
if ( (dest->width * dest->height) >= (TILEMAP_MAX_WIDTH * TILEMAP_MAX_HEIGHT) ) {
FAIL_RETURN(errctx, ERR_OUTOFBOUNDS, "Map exceeds the maximum size");
if ( (dest->width * dest->height) >= (AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT) ) {
FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size");
}
CATCH(errctx, tilemap_load_layers((tilemap *)dest, (json_t *)json));
CATCH(errctx, tilemap_load_tilesets((tilemap *)dest, (json_t *)json));
CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json, dirnamestr));
CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json, dirnamestr));
if ( dest->p_foreground_y && dest->p_vanishing_y ) {
// How much bigger is the foreground vs the vanishing point?
// 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 {
IGNORE(heap_release_string(tmpstr));
//IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *viewport, int layeridx)
akerr_ErrorContext *akgl_tilemap_draw(akgl_Tilemap *map, SDL_FRect *viewport, int layeridx)
{
PREPARE_ERROR(errctx);
SDL_FRect dest = {.x = 0, .y = 0, .w = 0, .h = 0};;
@@ -439,8 +619,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.
* 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, viewport, ERR_NULLPOINTER, "tilemap_draw received NULL pointer to viewport");
FAIL_ZERO_RETURN(errctx, map, AKERR_NULLPOINTER, "akgl_tilemap_draw received NULL pointer to tilemap");
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 */
@@ -458,6 +638,17 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
/*SDL_Log("Rendering map into viewport from (%d, %d) to (%d, %d)",
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;
PASS(errctx, renderer->draw_texture(renderer, map->layers[layeridx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
SUCCEED_RETURN(errctx);
}
dest.x = 0;
dest.y = 0;
dest.w = map->tilewidth;
@@ -509,7 +700,7 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
dest.y,
dest.w,
dest.h);*/
SDL_RenderTexture(renderer, map->tilesets[tilesetidx].texture, &src, &dest);
PASS(errctx, renderer->draw_texture(renderer, map->tilesets[tilesetidx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
}
}
dest.x += map->tilewidth;
@@ -519,20 +710,20 @@ ErrorContext *tilemap_draw(SDL_Renderer *renderer, tilemap *map, SDL_FRect *view
SUCCEED_RETURN(errctx);
}
ErrorContext *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *map, int tilesetidx)
akerr_ErrorContext *akgl_tilemap_draw_tileset(akgl_Tilemap *map, int tilesetidx)
{
PREPARE_ERROR(errctx);
SDL_FRect dest;
SDL_FRect src;
int tilenum = 0;
/*
* Render every tile in a tileset to the given renderer
* Render every tile in a tileset to the default renderer
* (this is a debugging tool that shows that the recorded tile offsets are correct,
* by proving that we can reconstruct the original tileset image)
*/
FAIL_ZERO_RETURN(errctx, map, ERR_NULLPOINTER, "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_ZERO_RETURN(errctx, map, AKERR_NULLPOINTER, "akgl_tilemap_draw_tileset received NULL pointer to tilemap");
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++) {
// Render this tile to the correct screen position
@@ -564,7 +755,45 @@ ErrorContext *tilemap_draw_tileset(SDL_Renderer *renderer, tilemap *map, int til
dest.y,
dest.w,
dest.h);*/
SDL_RenderTexture(renderer, map->tilesets[tilesetidx].texture, &src, &dest);
PASS(errctx, renderer->draw_texture(renderer, map->tilesets[tilesetidx].texture, &src, &dest, 0, NULL, SDL_FLIP_NONE));
}
SUCCEED_RETURN(errctx);
}
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

@@ -1,20 +1,103 @@
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <libgen.h>
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <sdlerror.h>
#include <akerror.h>
#include <sdl3game/util.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/game.h>
#include <akgl/util.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/game.h>
#include <akgl/staticstring.h>
ErrorContext *rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
#include <akstdlib.h>
akerr_ErrorContext *akgl_path_relative_root(char *root, char *path, akgl_String *dst)
{
PREPARE_ERROR(e);
akgl_String *pathbuf;
akgl_String *strbuf;
char *result;
int rootlen;
int pathlen;
int count;
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
PASS(e, akgl_heap_next_string(&strbuf));
PASS(e, akgl_heap_next_string(&pathbuf));
ATTEMPT {
// Is it relative to the root?
rootlen = strlen(root);
pathlen = strlen(path);
if ( (rootlen + pathlen) >= AKGL_MAX_STRING_LENGTH ) {
FAIL_RETURN(e, AKERR_OUTOFBOUNDS, "Total path length (%d) is greater than maximum akgl_String length (%d)", (rootlen + pathlen), AKGL_MAX_STRING_LENGTH);
}
DISABLE_GCC_WARNING_FORMAT_TRUNCATION
CATCH(e, aksl_sprintf(&count, (char *)&pathbuf->data, "%s/%s", root, path));
RESTORE_GCC_WARNINGS
CATCH(e, aksl_realpath((char *)&pathbuf->data, (char *)&strbuf->data));
CATCH(e, akgl_string_copy(strbuf, dst, 0));
} CLEANUP {
IGNORE(akgl_heap_release_string(strbuf));
IGNORE(akgl_heap_release_string(pathbuf));
} PROCESS(e) {
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst)
{
PREPARE_ERROR(e);
akgl_String *strbuf;
char *result;
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument");
FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument");
PASS(e, akgl_heap_next_string(&strbuf));
ATTEMPT {
// Is path relative to our current working directory?
CATCH(e, aksl_realpath(path, (char *)&strbuf->data));
// Yes it is. strbuf->data contains the absolute path.
CATCH(e, akgl_string_copy(strbuf, dst, 0));
} CLEANUP {
IGNORE(akgl_heap_release_string(strbuf));
} PROCESS(e) {
} HANDLE(e, ENOENT) {
// Path is not relative to our current working directory
// Noop - execution proceeds after the break
return akgl_path_relative_root(root, path, dst);
} FINISH(e, true);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_path_relative_from(char *path, char *from, akgl_String **dst)
{
akgl_String *dirnamestr;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "path");
FAIL_ZERO_RETURN(e, from, AKERR_NULLPOINTER, "from");
PASS(e, akgl_heap_next_string(&dirnamestr));
PASS(e, aksl_realpath(from, (char *)&dirnamestr->data));
dirname((char *)&dirnamestr->data);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, ERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, rect, ERR_NULLPOINTER, "NULL Rectangle reference");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, rect, AKERR_NULLPOINTER, "NULL Rectangle reference");
dest->topleft.x = rect->x;
dest->topleft.y = rect->y;
dest->bottomleft.x = rect->x;
@@ -26,12 +109,12 @@ ErrorContext *rectangle_points(RectanglePoints *dest, SDL_FRect *rect)
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);
FAIL_ZERO_RETURN(errctx, p, ERR_NULLPOINTER, "NULL Point reference");
FAIL_ZERO_RETURN(errctx, rp, ERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, collide, ERR_NULLPOINTER, "NULL boolean reference");
FAIL_ZERO_RETURN(errctx, p, AKERR_NULLPOINTER, "NULL Point reference");
FAIL_ZERO_RETURN(errctx, rp, AKERR_NULLPOINTER, "NULL RectanglePoints reference");
FAIL_ZERO_RETURN(errctx, collide, AKERR_NULLPOINTER, "NULL boolean reference");
if ( (p->x >= rp->topleft.x) && (p->y >= rp->topleft.y) &&
(p->x <= rp->bottomright.x) && (p->y <= rp->bottomright.y) ) {
*collide = true;
@@ -41,49 +124,49 @@ ErrorContext *collide_point_rectangle(point *p, RectanglePoints *rp, bool *colli
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 r2p;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, r1, ERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, r2, ERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, collide, ERR_NULLPOINTER, "NULL collision flag reference");
FAIL_ZERO_RETURN(errctx, r1, AKERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, r2, AKERR_NULLPOINTER, "NULL rectangle reference");
FAIL_ZERO_RETURN(errctx, collide, AKERR_NULLPOINTER, "NULL collision flag reference");
ATTEMPT {
CATCH(errctx, rectangle_points(&r1p, r1));
CATCH(errctx, rectangle_points(&r2p, r2));
CATCH(errctx, akgl_rectangle_points(&r1p, r1));
CATCH(errctx, akgl_rectangle_points(&r2p, 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); }
// 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); }
// 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); }
// 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); }
// 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); }
// 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); }
// 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); }
// 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); }
} CLEANUP {
@@ -95,75 +178,59 @@ 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);
FAIL_ZERO_RETURN(errctx, s1, ERR_NULLPOINTER, "NULL Surface pointer");
FAIL_ZERO_RETURN(errctx, s2, ERR_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_ZERO_RETURN(errctx, s1, AKERR_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)), AKERR_VALUE, "Comparison surfaces are not equal");
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 *s2 = NULL;
SDL_FRect src = {.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};
string *tmpstring = NULL;
akgl_String *tmpstring = NULL;
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, t1, ERR_NULLPOINTER, "NULL texture");
FAIL_ZERO_BREAK(errctx, t2, ERR_NULLPOINTER, "NULL texture");
FAIL_ZERO_BREAK(errctx, t1, AKERR_NULLPOINTER, "NULL texture");
FAIL_ZERO_BREAK(errctx, t2, AKERR_NULLPOINTER, "NULL texture");
CATCH(errctx, heap_next_string(&tmpstring));
SDL_RenderClear(renderer);
FAIL_ZERO_BREAK(
errctx,
SDL_RenderTexture(
renderer,
t1,
&src,
&dest),
ERR_SDL,
"Failed to render test texture");
s1 = SDL_RenderReadPixels(renderer, &read);
FAIL_ZERO_BREAK(errctx, s1, ERR_SDL, "Failed to read pixels from renderer");
CATCH(errctx, akgl_heap_next_string(&tmpstring));
SDL_RenderClear(renderer->sdl_renderer);
CATCH(errctx, renderer->draw_texture(renderer, t1, &src, &dest, 0, NULL, SDL_FLIP_NONE));
s1 = SDL_RenderReadPixels(renderer->sdl_renderer, &read);
FAIL_ZERO_BREAK(errctx, s1, AKGL_ERR_SDL, "Failed to read pixels from renderer");
if ( writeout != NULL ) {
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(
errctx,
IMG_SavePNG(s1, (char *)&tmpstring->data),
ERR_IO,
AKERR_IO,
"Unable to save %s: %s",
(char *)&tmpstring->data,
SDL_GetError());
}
SDL_RenderClear(renderer);
SDL_RenderClear(renderer->sdl_renderer);
FAIL_ZERO_BREAK(
errctx,
SDL_RenderTexture(
renderer,
t2,
&src,
&dest),
ERR_SDL,
"Failed to render test texture");
s2 = SDL_RenderReadPixels(renderer, &read);
FAIL_ZERO_BREAK(errctx, s2, ERR_SDL, "Failed to read pixels from renderer");
CATCH(errctx, renderer->draw_texture(renderer, t1, &src, &dest, 0, NULL, SDL_FLIP_NONE));
s2 = SDL_RenderReadPixels(renderer->sdl_renderer, &read);
FAIL_ZERO_BREAK(errctx, s2, AKGL_ERR_SDL, "Failed to read pixels from renderer");
CATCH(errctx, compare_sdl_surfaces(s1, s2));
CATCH(errctx, akgl_compare_sdl_surfaces(s1, s2));
} CLEANUP {
if ( s1 != NULL )
SDL_DestroySurface(s1);
if ( s2 != NULL )
SDL_DestroySurface(s2);
IGNORE(heap_release_string(tmpstring));
IGNORE(akgl_heap_release_string(tmpstring));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);

View File

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

View File

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

View File

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

View File

@@ -1,186 +1,187 @@
#include <SDL3/SDL.h>
#include <sdlerror.h>
#include <akerror.h>
#include <sdl3game/character.h>
#include <sdl3game/actor.h>
#include <sdl3game/heap.h>
#include <sdl3game/registry.h>
#include <sdl3game/iterator.h>
#include <akgl/character.h>
#include <akgl/actor.h>
#include <akgl/error.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/iterator.h>
SDL_Window *window;
SDL_Renderer *renderer;
ErrorContext *test_character_initialize()
akerr_ErrorContext *test_akgl_character_initialize()
{
PREPARE_ERROR(errctx);
character *testchar = NULL;
akgl_Character *testchar = NULL;
ATTEMPT {
CATCH(errctx, heap_next_character(&testchar));
CATCH(errctx, character_initialize(testchar, "testchar"));
CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, akgl_character_initialize(testchar, "testchar"));
FAIL_ZERO_BREAK(
errctx,
SDL_GetPointerProperty(REGISTRY_CHARACTER, "testchar", NULL),
ERR_KEY,
SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testchar", NULL),
AKERR_KEY,
"Character was not placed in the registry");
FAIL_NONZERO_BREAK(
errctx,
strcmp((char *)&testchar->name, "testchar"),
ERR_VALUE,
AKERR_VALUE,
"Character was not named properly ('testchar' vs '%s')",
(char *)&testchar->name);
FAIL_ZERO_BREAK(
errctx,
testchar->state_sprites,
ERR_NULLPOINTER,
AKERR_NULLPOINTER,
"Character state sprites map was not initialized");
} CLEANUP {
IGNORE(heap_release_character(testchar));
IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *test_character_sprite_mgmt()
akerr_ErrorContext *test_character_sprite_mgmt()
{
character *testchar = NULL;
sprite *testsprite = NULL;
sprite *testsprite2 = NULL;
sprite *comparesprite = NULL;
akgl_Character *testchar = NULL;
akgl_Sprite *testsprite = NULL;
akgl_Sprite *testsprite2 = NULL;
akgl_Sprite *comparesprite = NULL;
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, heap_next_character(&testchar));
CATCH(errctx, sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL);
CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite,
ERR_KEY,
AKERR_KEY,
"Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL);
CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite2,
ERR_KEY,
AKERR_KEY,
"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, testsprite2, ACTOR_STATE_DEAD));
CATCH(errctx, testchar->sprite_get(testchar, (ACTOR_STATE_ALIVE), &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_LEFT");
CATCH(errctx, testchar->sprite_get(testchar, ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_DEAD");
CATCH(errctx, testchar->sprite_add(testchar, testsprite, AKGL_ACTOR_STATE_ALIVE));
CATCH(errctx, testchar->sprite_add(testchar, testsprite2, AKGL_ACTOR_STATE_DEAD));
CATCH(errctx, testchar->sprite_get(testchar, (AKGL_ACTOR_STATE_ALIVE), &comparesprite));
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, AKGL_ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_DEAD");
} CLEANUP {
IGNORE(heap_release_sprite(testsprite));
IGNORE(heap_release_sprite(testsprite2));
IGNORE(heap_release_character(testchar));
IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(akgl_heap_release_sprite(testsprite2));
IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *test_character_iterate_state_sprites()
akerr_ErrorContext *test_character_iterate_state_sprites()
{
character *testchar = NULL;
sprite *testsprite = NULL;
sprite *testsprite2 = NULL;
iterator opflags = {.flags = ITERATOR_OP_RELEASE, .layerid = 0};
akgl_Character *testchar = NULL;
akgl_Sprite *testsprite = NULL;
akgl_Sprite *testsprite2 = NULL;
akgl_Iterator opflags = {.flags = AKGL_ITERATOR_OP_RELEASE, .layerid = 0};
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, heap_next_character(&testchar));
CATCH(errctx, sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL);
CATCH(errctx, akgl_heap_next_character(&testchar));
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite,
ERR_KEY,
AKERR_KEY,
"Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL);
CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite2,
ERR_KEY,
AKERR_KEY,
"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, testsprite2, ACTOR_STATE_DEAD));
CATCH(errctx, testchar->sprite_add(testchar, testsprite, AKGL_ACTOR_STATE_ALIVE));
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.
// We just want to know they've been released.
FAIL_NONZERO_BREAK(
errctx,
(testsprite->refcount > 1),
ERR_VALUE,
"heap_release_sprite not called for testsprite from iterator");
AKERR_VALUE,
"akgl_heap_release_sprite not called for testsprite from iterator");
FAIL_NONZERO_BREAK(
errctx,
(testsprite2->refcount > 1),
ERR_VALUE,
"heap_release_sprite not called for testsprite from iterator");
AKERR_VALUE,
"akgl_heap_release_sprite not called for testsprite from iterator");
} CLEANUP {
IGNORE(heap_release_sprite(testsprite));
IGNORE(heap_release_sprite(testsprite2));
IGNORE(heap_release_character(testchar));
IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(akgl_heap_release_sprite(testsprite2));
IGNORE(akgl_heap_release_character(testchar));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *test_character_load_json()
akerr_ErrorContext *test_akgl_character_load_json()
{
character *testcharacter = NULL;
sprite *testsprite = NULL;
sprite *testsprite2 = NULL;
sprite *comparesprite = NULL;
akgl_Character *testcharacter = NULL;
akgl_Sprite *testsprite = NULL;
akgl_Sprite *testsprite2 = NULL;
akgl_Sprite *comparesprite = NULL;
int tsrc = 0;
int tsrc2 = 0;
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, heap_next_character(&testcharacter));
CATCH(errctx, sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL);
CATCH(errctx, akgl_heap_next_character(&testcharacter));
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite,
ERR_KEY,
AKERR_KEY,
"Sprite loaded from json but not in registry");
CATCH(errctx, sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL);
CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite2,
ERR_KEY,
AKERR_KEY,
"Sprite 2 loaded from json but not in registry");
CATCH(errctx, character_load_json("assets/testcharacter.json"));
testcharacter = SDL_GetPointerProperty(REGISTRY_CHARACTER, "testcharacter", NULL);
CATCH(errctx, akgl_character_load_json("assets/testcharacter.json"));
testcharacter = SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, "testcharacter", NULL);
FAIL_ZERO_BREAK(
errctx,
testcharacter,
ERR_KEY,
AKERR_KEY,
"Character loaded from json but not in registry");
CATCH(errctx, testcharacter->sprite_get(testcharacter, (ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_LEFT), &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_ALIVE");
CATCH(errctx, testcharacter->sprite_get(testcharacter, ACTOR_STATE_DEAD, &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite2), ERR_VALUE, "Wrong sprite for state ACTOR_STATE_DEAD");
CATCH(errctx, testcharacter->sprite_get(testcharacter, (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT), &comparesprite));
FAIL_ZERO_BREAK(errctx, (comparesprite == testsprite), AKERR_VALUE, "Wrong sprite for state AKGL_ACTOR_STATE_ALIVE");
CATCH(errctx, testcharacter->sprite_get(testcharacter, AKGL_ACTOR_STATE_DEAD, &comparesprite));
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->vy != 0.200000003), ERR_VALUE, "Wrong Y 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), 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
CATCH(errctx, heap_release_sprite(testsprite));
CATCH(errctx, heap_release_sprite(testsprite2));
CATCH(errctx, akgl_heap_release_sprite(testsprite));
CATCH(errctx, akgl_heap_release_sprite(testsprite2));
tsrc = testsprite->refcount;
tsrc2 = testsprite2->refcount;
CATCH(errctx, heap_release_character(testcharacter));
CATCH(errctx, akgl_heap_release_character(testcharacter));
FAIL_ZERO_BREAK(
errctx,
((testsprite->refcount < tsrc) || (testsprite2->refcount < tsrc2)),
ERR_VALUE,
AKERR_VALUE,
"character did not reduce reference count of its child sprites when released");
} CLEANUP {
} PROCESS(errctx) {
@@ -195,19 +196,19 @@ int main(void)
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, 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)) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
if (!SDL_CreateWindowAndRenderer("net/aklabs/libakgl/test_character", 640, 480, SDL_WINDOW_HIDDEN, &window, &renderer)) {
FAIL_BREAK(errctx, AKGL_ERR_SDL, "Couldn't create window/renderer: %s", SDL_GetError());
}
CATCH(errctx, heap_init());
CATCH(errctx, registry_init());
CATCH(errctx, test_character_initialize());
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init());
CATCH(errctx, test_akgl_character_initialize());
CATCH(errctx, test_character_sprite_mgmt());
CATCH(errctx, test_character_iterate_state_sprites());
CATCH(errctx, test_character_load_json());
CATCH(errctx, test_akgl_character_load_json());
} CLEANUP {
} PROCESS(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 <stdlib.h>
#include <sdlerror.h>
#include <sdl3game/registry.h>
#include <akerror.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)
{
@@ -11,7 +11,7 @@ void *sdl_calloc_always_fails(size_t a, size_t b)
return NULL;
}
ErrorContext *test_registry_init(RegistryFuncPtr funcptr)
akerr_ErrorContext *test_akgl_registry_init(RegistryFuncPtr funcptr)
{
SDL_malloc_func malloc_func;
SDL_calloc_func calloc_func;
@@ -43,41 +43,41 @@ ErrorContext *test_registry_init(RegistryFuncPtr funcptr)
} PROCESS(errctx) {
} 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);
ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_actor));
CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_actor));
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n");
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_sprite));
CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_sprite));
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n");
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_spritesheet));
CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_spritesheet));
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n");
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, test_registry_init(&registry_init_character));
CATCH(errctx, test_akgl_registry_init(&akgl_registry_init_character));
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
printf("Sucess\n");
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
@@ -87,7 +87,7 @@ int main(void)
{
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, test_registry_init_creation_failures());
CATCH(errctx, test_akgl_registry_init_creation_failures());
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);

View File

@@ -3,45 +3,46 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sdlerror.h>
#include <akerror.h>
#include <sdl3game/registry.h>
#include <sdl3game/sprite.h>
#include <sdl3game/heap.h>
#include <sdl3game/util.h>
#include <akgl/registry.h>
#include <akgl/sprite.h>
#include <akgl/heap.h>
#include <akgl/util.h>
#include <akgl/error.h>
SDL_Window *window;
SDL_Renderer *renderer;
ErrorContext *test_spritesheet_initialize(void)
akerr_ErrorContext *test_akgl_spritesheet_initialize(void)
{
PREPARE_ERROR(errctx);
spritesheet *sheet = NULL;
akgl_SpriteSheet *sheet = NULL;
SDL_Texture *image = NULL;
string *tmpstr = NULL;
akgl_String *tmpstr = NULL;
// Does the image file get loaded?
// Is the image file loaded correctly? (Surface comparison)
// Is the spritesheet in the registry?
ATTEMPT {
CATCH(errctx, heap_next_spritesheet(&sheet));
CATCH(errctx, heap_next_string(&tmpstr));
CATCH(errctx, akgl_heap_next_spritesheet(&sheet));
CATCH(errctx, akgl_heap_next_string(&tmpstr));
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, spritesheet_initialize(sheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, sheet->texture, ERR_VALUE, "spritesheet_initialize failed to load the sprite texture");
snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, akgl_spritesheet_initialize(sheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, sheet->texture, AKERR_VALUE, "akgl_spritesheet_initialize failed to load the sprite texture");
FAIL_NONZERO_BREAK(
errctx,
((sheet->texture->w != 576) || (sheet->texture->h != 384)),
ERR_VALUE,
AKERR_VALUE,
"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);
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(
errctx,
render_and_compare(
akgl_render_and_compare(
sheet->texture,
image,
0, 0, 576, 384,
@@ -50,13 +51,13 @@ ErrorContext *test_spritesheet_initialize(void)
FAIL_ZERO_BREAK(
errctx,
SDL_GetPointerProperty(REGISTRY_SPRITESHEET, "assets/spritesheet.png", NULL),
ERR_KEY,
SDL_GetPointerProperty(AKGL_REGISTRY_SPRITESHEET, "assets/spritesheet.png", NULL),
AKERR_KEY,
"Spritesheet was not placed in the registry");
} CLEANUP {
IGNORE(heap_release_string(tmpstr));
IGNORE(heap_release_spritesheet(sheet));
IGNORE(akgl_heap_release_string(tmpstr));
IGNORE(akgl_heap_release_spritesheet(sheet));
if ( image != NULL )
SDL_DestroyTexture(image);
} PROCESS(errctx) {
@@ -64,48 +65,48 @@ ErrorContext *test_spritesheet_initialize(void)
SUCCEED_RETURN(errctx);
}
ErrorContext *test_sprite_initialize(void)
akerr_ErrorContext *test_akgl_sprite_initialize(void)
{
PREPARE_ERROR(errctx);
spritesheet *testsheet = NULL;
sprite *testsprite = NULL;
string *tmpstr = NULL;
akgl_SpriteSheet *testsheet = NULL;
akgl_Sprite *testsprite = NULL;
akgl_String *tmpstr = NULL;
// Does the sprite get loaded?
// Do all frames of the sprite get loaded?
// Are all the frames of the sprite what we expect? (Surface comparison)
// Is the sprite added to the registry?
ATTEMPT {
CATCH(errctx, heap_next_spritesheet(&testsheet));
CATCH(errctx, heap_next_sprite(&testsprite));
CATCH(errctx, heap_next_string(&tmpstr));
CATCH(errctx, akgl_heap_next_spritesheet(&testsheet));
CATCH(errctx, akgl_heap_next_sprite(&testsprite));
CATCH(errctx, akgl_heap_next_string(&tmpstr));
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, spritesheet_initialize(testsheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, testsheet, ERR_VALUE, "spritesheet_initialize failed");
snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/spritesheet.png");
CATCH(errctx, akgl_spritesheet_initialize(testsheet, 48, 48, "assets/spritesheet.png"));
FAIL_ZERO_BREAK(errctx, testsheet, AKERR_VALUE, "akgl_spritesheet_initialize failed");
CATCH(errctx, sprite_initialize(testsprite, "test", testsheet));
FAIL_NONZERO_BREAK(errctx, (testsprite->sheet != testsheet), ERR_VALUE, "Initialized sprite uses wrong sheet");
CATCH(errctx, akgl_sprite_initialize(testsprite, "test", testsheet));
FAIL_NONZERO_BREAK(errctx, (testsprite->sheet != testsheet), AKERR_VALUE, "Initialized sprite uses wrong sheet");
FAIL_ZERO_BREAK(
errctx,
SDL_GetPointerProperty(REGISTRY_SPRITE, "test", NULL),
ERR_KEY,
SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "test", NULL),
AKERR_KEY,
"Sprite was not placed in the registry");
} CLEANUP {
IGNORE(heap_release_sprite(testsprite));
IGNORE(heap_release_string(tmpstr));
IGNORE(akgl_heap_release_sprite(testsprite));
IGNORE(akgl_heap_release_string(tmpstr));
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
ErrorContext *test_sprite_load_json(void)
akerr_ErrorContext *test_akgl_sprite_load_json(void)
{
PREPARE_ERROR(errctx);
sprite *testsprite = NULL;
sprite *testsprite2 = NULL;
string *tmpstr = NULL;
akgl_Sprite *testsprite = NULL;
akgl_Sprite *testsprite2 = NULL;
akgl_String *tmpstr = NULL;
SDL_Texture *image = NULL;
// 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)
// Is the sprite added to the registry?
ATTEMPT {
CATCH(errctx, heap_next_string(&tmpstr));
CATCH(errctx, akgl_heap_next_string(&tmpstr));
CATCH(errctx, sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite", NULL);
CATCH(errctx, akgl_sprite_load_json("assets/testsprite.json"));
testsprite = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite,
ERR_KEY,
"sprite_load_json succeeds but sprite is not placed in the registry");
AKERR_KEY,
"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->height == 48), ERR_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->loop == true), ERR_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->frames == 3), ERR_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[1] == 13), ERR_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_NONZERO_BREAK(errctx, strcmp(testsprite->name, "testsprite"), ERR_VALUE, "name incorrect (testsprite : %s)", (char *)testsprite->name);
FAIL_ZERO_BREAK(errctx, (testsprite->width == 48), AKERR_VALUE, "width incorrect (48 : %d)", testsprite->width);
FAIL_ZERO_BREAK(errctx, (testsprite->height == 48), AKERR_VALUE, "height incorrect (48 : %d)", testsprite->height);
FAIL_ZERO_BREAK(errctx, (testsprite->speed == 100000000), AKERR_VALUE, "speed incorrect (100 : %d)", testsprite->speed);
FAIL_ZERO_BREAK(errctx, (testsprite->loop == true), AKERR_VALUE, "loop incorrect (1 : %d)", testsprite->loop);
FAIL_ZERO_BREAK(errctx, (testsprite->loopReverse == true), AKERR_VALUE, "loopReverse incorrect (1 : %d)", testsprite->loopReverse);
FAIL_ZERO_BREAK(errctx, (testsprite->frames == 3), AKERR_VALUE, "frame count incorrect (3 : %d)", testsprite->frames);
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), AKERR_VALUE, "frameids[1] incorrect (13 : %d)", testsprite->frameids[1]);
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"), AKERR_VALUE, "name incorrect (testsprite : %s)", (char *)testsprite->name);
// 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);
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(
errctx,
render_and_compare(
akgl_render_and_compare(
testsprite->sheet->texture,
image,
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?
snprintf((char *)&tmpstr->data, MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testsprite2.json");
CATCH(errctx, sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(REGISTRY_SPRITE, "testsprite2", NULL);
snprintf((char *)&tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), "assets/testsprite2.json");
CATCH(errctx, akgl_sprite_load_json("assets/testsprite2.json"));
testsprite2 = SDL_GetPointerProperty(AKGL_REGISTRY_SPRITE, "testsprite2", NULL);
FAIL_ZERO_BREAK(
errctx,
testsprite,
ERR_KEY,
"sprite_load_json succeeds but second sprite is not placed in the registry");
AKERR_KEY,
"akgl_sprite_load_json succeeds but second sprite is not placed in the registry");
FAIL_ZERO_BREAK(
errctx,
(testsprite->sheet == testsprite2->sheet),
ERR_VALUE,
AKERR_VALUE,
"Previously loaded spritesheets are not reused");
} CLEANUP {
if ( testsprite != NULL ) {
IGNORE(heap_release_sprite(testsprite));
IGNORE(akgl_heap_release_sprite(testsprite));
}
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 )
SDL_DestroyTexture(image);
} PROCESS(errctx) {
@@ -190,20 +191,20 @@ int main(void)
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, 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)) {
FAIL_BREAK(errctx, ERR_SDL, "Couldn't create window/renderer: %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, heap_init());
CATCH(errctx, registry_init_sprite());
CATCH(errctx, registry_init_spritesheet());
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init_sprite());
CATCH(errctx, akgl_registry_init_spritesheet());
CATCH(errctx, test_spritesheet_initialize());
CATCH(errctx, test_sprite_initialize());
CATCH(errctx, test_sprite_load_json());
CATCH(errctx, test_akgl_spritesheet_initialize());
CATCH(errctx, test_akgl_sprite_initialize());
CATCH(errctx, test_akgl_sprite_load_json());
} CLEANUP {
} PROCESS(errctx) {
} FINISH_NORETURN(errctx);

View File

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

View File

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

View File

@@ -1,42 +1,42 @@
#include <SDL3/SDL.h>
#include <sdlerror.h>
#include <sdl3game/util.h>
#include <akerror.h>
#include <akgl/util.h>
ErrorContext *test_rectangle_points_nullpointers(void)
akerr_ErrorContext *test_akgl_rectangle_points_nullpointers(void)
{
RectanglePoints points;
SDL_FRect testrect;
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, rectangle_points(NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with all NULL pointers");
CATCH(errctx, akgl_rectangle_points(NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with all NULL pointers");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, rectangle_points(NULL, &testrect));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with NULL SDL_FRect pointer");
CATCH(errctx, akgl_rectangle_points(NULL, &testrect));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL SDL_FRect pointer");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, rectangle_points(&points, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "rectangle_points fails to FAIL with NULL RectanglePoints pointer");
CATCH(errctx, akgl_rectangle_points(&points, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_rectangle_points fails to FAIL with NULL RectanglePoints pointer");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, rectangle_points(&points, &testrect));
CATCH(errctx, akgl_rectangle_points(&points, &testrect));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
@@ -44,7 +44,7 @@ ErrorContext *test_rectangle_points_nullpointers(void)
SUCCEED_RETURN(errctx);
}
ErrorContext *test_rectangle_points_math(void)
akerr_ErrorContext *test_akgl_rectangle_points_math(void)
{
RectanglePoints points;
SDL_FRect testrect = {.x = 0, .y = 0, .w = 32, .h = 32};
@@ -52,7 +52,7 @@ ErrorContext *test_rectangle_points_math(void)
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, rectangle_points(&points, &testrect));
CATCH(errctx, akgl_rectangle_points(&points, &testrect));
if ( points.topleft.x != 0 ||
points.topleft.y != 0 ||
points.topright.x != 32 ||
@@ -63,8 +63,8 @@ ErrorContext *test_rectangle_points_math(void)
points.bottomright.y != 32 ) {
FAIL_BREAK(
errctx,
ERR_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}}",
AKERR_BEHAVIOR,
"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.topright.x, points.topright.y,
points.bottomleft.x, points.bottomleft.y,
@@ -77,7 +77,7 @@ ErrorContext *test_rectangle_points_math(void)
SUCCEED_RETURN(errctx);
}
ErrorContext *test_collide_point_rectangle_nullpointers(void)
akerr_ErrorContext *test_akgl_collide_point_rectangle_nullpointers(void)
{
point testpoint;
RectanglePoints testrectpoints;
@@ -86,43 +86,43 @@ ErrorContext *test_collide_point_rectangle_nullpointers(void)
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(*, *, NULL) failed");
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, *, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, NULL, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(*, NULL, *) failed");
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, NULL, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(*, NULL, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_point_rectangle(NULL, &testrectpoints, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(NULL, *, *) failed");
CATCH(errctx, akgl_collide_point_rectangle(NULL, &testrectpoints, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, *, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_point_rectangle(NULL, NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_point_rectangle(NULL, NULL, NULL) failed");
CATCH(errctx, akgl_collide_point_rectangle(NULL, NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_point_rectangle(NULL, NULL, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
@@ -130,7 +130,7 @@ ErrorContext *test_collide_point_rectangle_nullpointers(void)
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};
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);
ATTEMPT {
CATCH(errctx, rectangle_points(&testrectpoints, &testrect));
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
CATCH(errctx, akgl_rectangle_points(&testrectpoints, &testrect));
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
testpoint.x = 48;
testpoint.y = 48;
CATCH(errctx, collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
CATCH(errctx, akgl_collide_point_rectangle(&testpoint, &testrectpoints, &testcollide));
if ( testcollide == true ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Invalid collision reported");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
}
} CLEANUP {
} PROCESS(errctx) {
@@ -157,7 +157,7 @@ ErrorContext *test_collide_point_rectangle_logic(void)
SUCCEED_RETURN(errctx);
}
ErrorContext *test_collide_rectangles_nullpointers(void)
akerr_ErrorContext *test_akgl_collide_rectangles_nullpointers(void)
{
SDL_FRect testrect1;
SDL_FRect testrect2;
@@ -166,43 +166,43 @@ ErrorContext *test_collide_rectangles_nullpointers(void)
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(*, *, NULL) failed");
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, *, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, NULL, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(*, NULL, *) failed");
CATCH(errctx, akgl_collide_rectangles(&testrect1, NULL, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(*, NULL, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_rectangles(NULL, &testrect2, &testcollide));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(NULL, *, *) failed");
CATCH(errctx, akgl_collide_rectangles(NULL, &testrect2, &testcollide));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, *, *) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_rectangles(NULL, NULL, NULL));
FAIL_BREAK(errctx, ERR_BEHAVIOR, "collide_rectangles(NULL, NULL, NULL) failed");
CATCH(errctx, akgl_collide_rectangles(NULL, NULL, NULL));
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "akgl_collide_rectangles(NULL, NULL, NULL) failed");
} CLEANUP {
} PROCESS(errctx) {
} HANDLE(errctx, ERR_NULLPOINTER) {
} HANDLE(errctx, AKERR_NULLPOINTER) {
// noop
} FINISH(errctx, true);
ATTEMPT {
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
@@ -210,7 +210,7 @@ ErrorContext *test_collide_rectangles_nullpointers(void)
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 testrect2 = { .x = 30, .y = 30, .w = 40, .h = 40};
@@ -220,32 +220,32 @@ ErrorContext *test_collide_rectangles_logic(void)
ATTEMPT {
// Collision overlapping on the top left
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
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
testrect1.x = 64;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
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
testrect1.x = 0;
testrect1.y = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
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
testrect1.x = 32;
testrect1.y = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the top edge
@@ -253,9 +253,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0;
testrect1.w = 60;
testrect1.h = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the left edge
@@ -263,9 +263,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0;
testrect1.w = 35;
testrect1.h = 80;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the right edge
@@ -273,9 +273,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0;
testrect1.w = 60;
testrect1.h = 80;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
// Collision overlapping the bottom edge
@@ -283,9 +283,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 65;
testrect1.w = 80;
testrect1.h = 32;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == false ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Valid collision missed");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Valid collision missed");
}
// Not colliding
@@ -293,9 +293,9 @@ ErrorContext *test_collide_rectangles_logic(void)
testrect1.y = 0;
testrect1.w = 16;
testrect1.h = 16;
CATCH(errctx, collide_rectangles(&testrect1, &testrect2, &testcollide));
CATCH(errctx, akgl_collide_rectangles(&testrect1, &testrect2, &testcollide));
if ( testcollide == true ) {
FAIL_BREAK(errctx, ERR_BEHAVIOR, "Invalid collision reported");
FAIL_BREAK(errctx, AKERR_BEHAVIOR, "Invalid collision reported");
}
} CLEANUP {
@@ -309,11 +309,11 @@ int main(void)
{
PREPARE_ERROR(errctx);
ATTEMPT {
CATCH(errctx, test_rectangle_points_nullpointers());
CATCH(errctx, test_rectangle_points_math());
CATCH(errctx, test_collide_point_rectangle_nullpointers());
CATCH(errctx, test_collide_rectangles_nullpointers());
CATCH(errctx, test_collide_rectangles_logic());
CATCH(errctx, test_akgl_rectangle_points_nullpointers());
CATCH(errctx, test_akgl_rectangle_points_math());
CATCH(errctx, test_akgl_collide_point_rectangle_nullpointers());
CATCH(errctx, test_akgl_collide_rectangles_nullpointers());
CATCH(errctx, test_akgl_collide_rectangles_logic());
} CLEANUP {
} PROCESS(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_mixer/SDL_mixer.h>
#include <sdlerror.h>
#include <sdl3game/assets.h>
#include <sdl3game/iterator.h>
#include <sdl3game/tilemap.h>
#include <sdl3game/heap.h>
#include <sdl3game/game.h>
#include <sdl3game/controller.h>
#include <sdl3game/draw.h>
#include <sdl3game/sprite.h>
#include <sdl3game/actor.h>
#include <sdl3game/registry.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>
#include <unistd.h>
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
PREPARE_ERROR(errctx);
SDL3GControlMap *controlmap;
actor *actorptr = NULL;
akgl_Actor *actorptr = NULL;
int i = 0;
int gamepadids[32];
char *characterjson = NULL;
char pathbuf[4096];
char cwdbuf[1024];
@@ -41,18 +42,18 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
ATTEMPT {
FAIL_ZERO_BREAK(errctx, appstate, ERR_NULLPOINTER, "NULL appstate pointer");
FAIL_ZERO_BREAK(errctx, getcwd((char *)&cwdbuf, 1024), ERR_NULLPOINTER, "Couldn't get current working directory");
FAIL_ZERO_BREAK(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate pointer");
FAIL_ZERO_BREAK(errctx, getcwd((char *)&cwdbuf, 1024), AKERR_NULLPOINTER, "Couldn't get current working directory");
strcpy((char *)&game.name, "charviewer");
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.screenheight = 480;
CATCH(errctx, GAME_init());
CATCH(errctx, heap_init());
CATCH(errctx, registry_init());
CATCH(errctx, akgl_game_init());
CATCH(errctx, akgl_heap_init());
CATCH(errctx, akgl_registry_init());
} CLEANUP {
} PROCESS(errctx) {
@@ -69,7 +70,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
sprintf((char *)&pathbuf, "%s", argv[i]);
}
SDL_Log("Loading sprite %s...", (char *)&pathbuf);
CATCH(errctx, sprite_load_json((char *)&pathbuf));
CATCH(errctx, akgl_sprite_load_json((char *)&pathbuf));
} CLEANUP {
} PROCESS(errctx) {
} HANDLE_DEFAULT(errctx) {
@@ -86,51 +87,25 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
sprintf((char *)&pathbuf, "%s", characterjson);
}
SDL_Log("Loading character %s...", (char *)&pathbuf);
CATCH(errctx, character_load_json((char *)&pathbuf));
CATCH(errctx, heap_next_actor(&actorptr));
CATCH(errctx, actor_initialize((actor *)actorptr, "player"));
CATCH(errctx, akgl_character_load_json((char *)&pathbuf));
CATCH(errctx, akgl_heap_next_actor(&actorptr));
CATCH(errctx, akgl_actor_initialize((akgl_Actor *)actorptr, "player"));
actorptr->basechar = SDL_GetPointerProperty(
REGISTRY_CHARACTER,
AKGL_REGISTRY_CHARACTER,
"little guy",
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->state = (ACTOR_STATE_ALIVE | ACTOR_STATE_FACE_DOWN);
actorptr->state = (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_DOWN);
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(REGISTRY_ACTOR, "player", NULL);
// Move down
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;
// Open the first gamepad
FAIL_ZERO_BREAK(errctx, SDL_GetGamepads((int *)&gamepadids), AKGL_ERR_SDL, "%s", SDL_GetError());
SDL_Log("Opening gamepad %d", gamepadids[0]);
FAIL_ZERO_BREAK(errctx, SDL_OpenGamepad(gamepadids[0]), AKGL_ERR_SDL, "%s", SDL_GetError());
CATCH(errctx, akgl_controller_default(0, "player", 0, gamepadids[0]));
} CLEANUP {
} PROCESS(errctx) {
@@ -147,10 +122,10 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_BREAK(errctx, appstate, ERR_NULLPOINTER, "NULL appstate pointer");
FAIL_ZERO_BREAK(errctx, event, ERR_NULLPOINTER, "NULL event pointer");
FAIL_ZERO_BREAK(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate 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) {
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)
{
int i = 0;
iterator opflags;
akgl_Iterator opflags;
PREPARE_ERROR(errctx);
BITMASK_CLEAR(opflags.flags);
BITMASK_ADD(opflags.flags, ITERATOR_OP_UPDATE);
BITMASK_ADD(opflags.flags, ITERATOR_OP_RENDER);
GAME_draw_background(game.screenwidth, game.screenheight);
AKGL_BITMASK_CLEAR(opflags.flags);
AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_UPDATE);
AKGL_BITMASK_ADD(opflags.flags, AKGL_ITERATOR_OP_RENDER);
akgl_draw_background(game.screenwidth, game.screenheight);
ATTEMPT {
CATCH(errctx, tilemap_draw(renderer, (tilemap *)&gamemap, &camera, i));
SDL_EnumerateProperties(REGISTRY_ACTOR, &registry_iterate_actor, (void *)&opflags);
CATCH(errctx, akgl_tilemap_draw(renderer, (akgl_Tilemap *)&gamemap, &camera, i));
SDL_EnumerateProperties(AKGL_REGISTRY_ACTOR, &akgl_registry_iterate_actor, (void *)&opflags);
} CLEANUP {
} PROCESS(errctx) {
} HANDLE_DEFAULT(errctx) {