Compare commits
2 Commits
2e1e28b191
...
d0dda33a7a
| Author | SHA1 | Date |
|---|---|---|
|
|
d0dda33a7a | |
|
|
b2ec96bbf3 |
|
|
@ -1,2 +1,4 @@
|
||||||
.vscode/ccls-cache
|
.vscode/ccls-cache
|
||||||
|
.cache
|
||||||
|
.config
|
||||||
build
|
build
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "SDL3_Core"]
|
|
||||||
path = SDL3_Core
|
|
||||||
url = https://gitea.skadubpc.net/ulysse/2D_Engine_SDL3_Core.git
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
{
|
{
|
||||||
"nixEnvSelector.suggestion": false,
|
"nixEnvSelector.suggestion": false,
|
||||||
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix",
|
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix",
|
||||||
"nixEnvSelector.useFlakes": false
|
"nixEnvSelector.useFlakes": false,
|
||||||
|
|
||||||
|
"files.associations": {
|
||||||
|
"*.tsx": "xml",
|
||||||
|
"*.map": "json",
|
||||||
|
"configure": "shellscript"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
Makefile
18
Makefile
|
|
@ -3,13 +3,9 @@ SHELL = /usr/bin/env bash
|
||||||
# Build folder
|
# Build folder
|
||||||
BUILD_DIR = build
|
BUILD_DIR = build
|
||||||
|
|
||||||
# Source output target name
|
# Outputs target name and path
|
||||||
SOURCE_OUTPUT = $(BUILD_DIR)/2D_Engine
|
SOURCE_OUTPUT = $(BUILD_DIR)/2D_Engine
|
||||||
|
|
||||||
# Core output target name
|
|
||||||
CORE_OUTPUT = $(BUILD_DIR)/libcore.a
|
CORE_OUTPUT = $(BUILD_DIR)/libcore.a
|
||||||
|
|
||||||
# Assets output
|
|
||||||
ASSET_OUTPUT = $(BUILD_DIR)/game.data
|
ASSET_OUTPUT = $(BUILD_DIR)/game.data
|
||||||
|
|
||||||
# Verbosity
|
# Verbosity
|
||||||
|
|
@ -19,20 +15,20 @@ Q :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
GREEN := \033[32m
|
export GREEN := \033[32m
|
||||||
YELLOW := \033[33m
|
export YELLOW := \033[33m
|
||||||
RESET := \033[m
|
export RESET := \033[m
|
||||||
|
|
||||||
# Default targets
|
# Default targets
|
||||||
all: build_core build_src build_assets
|
all: $(SOURCE_OUTPUT)
|
||||||
|
|
||||||
# Sub Makefiles
|
# Sub Makefiles
|
||||||
SOURCE_DIR = src
|
SOURCE_DIR = src
|
||||||
CORE_DIR = SDL3_Core/src
|
CORE_DIR = core/src
|
||||||
ASSETS_DIR = assets
|
ASSETS_DIR = assets
|
||||||
|
|
||||||
include $(CORE_DIR)/Makefile
|
|
||||||
include $(SOURCE_DIR)/Makefile
|
include $(SOURCE_DIR)/Makefile
|
||||||
|
include $(CORE_DIR)/Makefile
|
||||||
include $(ASSETS_DIR)/Makefile
|
include $(ASSETS_DIR)/Makefile
|
||||||
|
|
||||||
# Clean
|
# Clean
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ A multi-platform 2D_Engine made in C.
|
||||||
|
|
||||||
## Licences
|
## Licences
|
||||||
- This program is open-source : you can redistribute and/or modify it under the term of the **GNU GPLv3**.
|
- This program is open-source : you can redistribute and/or modify it under the term of the **GNU GPLv3**.
|
||||||
Copyright (C) 2025 - 2026 Ulysse Cura. See [LICENSE](LICENSE) or [gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).
|
Copyright (C) 2025 - 2026 Ulysse Cura. See [LICENSE](LICENSE.txt) or [gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit bc3f9b4726b7a5cb9522bc959f6086095d5f4301
|
|
||||||
2
TODO.md
2
TODO.md
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
- [ ] Create lua component
|
- [ ] Create lua component
|
||||||
|
|
||||||
|
- [ ] Rethink makefile structure
|
||||||
|
|
||||||
### Done
|
### Done
|
||||||
|
|
||||||
- [x] Make camera center on map
|
- [x] Make camera center on map
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ ASSET_BUILDER_CFLAGS = --std=c17 --pedantic -O1 -DDEBUG=1 -g $(shell pkg-config
|
||||||
ASSET_BUILDER_LDFLAGS = -O1 $(shell pkg-config --libs libpng json-c)
|
ASSET_BUILDER_LDFLAGS = -O1 $(shell pkg-config --libs libpng json-c)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ASSET_BUILDER_INCLUDE_DIRS = $(ASSET_BUILDER_DIR)/headers $(SOURCE_DIR)/components/headers
|
ASSET_BUILDER_INCLUDE_DIRS = $(SOURCE_DIR)/components $(ASSET_BUILDER_DIR)
|
||||||
ASSET_BUILDER_INCLUDE_DIRS := $(addprefix -I, $(ASSET_BUILDER_INCLUDE_DIRS))
|
ASSET_BUILDER_INCLUDE_DIRS := $(addprefix -I, $(ASSET_BUILDER_INCLUDE_DIRS))
|
||||||
|
|
||||||
# Deduce objects
|
# Deduce objects
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configuration script to build the 2D_Engine for different platforms.
|
||||||
|
#
|
||||||
|
# Creates makefile configuration to build for target host.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Project settings no matter current and target hosts
|
||||||
|
#
|
||||||
|
|
||||||
|
#CACHE_DIR=".cache"
|
||||||
|
#LIB_DIR="lib"
|
||||||
|
#CONFIG_FILE=".config"
|
||||||
|
|
||||||
|
#LUA_VERSION="5.5.1"
|
||||||
|
#LUA_CHECKSUM="1c4b4068d67061f2a2231ad2b5422e77acea1487ea9890f6320af614f4373dce"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main
|
||||||
|
#
|
||||||
|
|
||||||
|
### Setup dirs and logs
|
||||||
|
#
|
||||||
|
## Cache dir
|
||||||
|
#mkdir -p "${CACHE_DIR}"
|
||||||
|
|
||||||
|
print_error()
|
||||||
|
{
|
||||||
|
echo -e "\033[31merror\033[m: $1"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
## Parse args
|
||||||
|
TARGET_HOST="guess"
|
||||||
|
|
||||||
|
for i in "$@"; do
|
||||||
|
case $i in
|
||||||
|
--target-host=*)
|
||||||
|
TARGET_HOST="${i#*=}"
|
||||||
|
shift # past argument=value
|
||||||
|
;;
|
||||||
|
--clean)
|
||||||
|
CLEAN=1
|
||||||
|
EXIT_AFTER_CLEAN=1
|
||||||
|
shift # past argument=value
|
||||||
|
;;
|
||||||
|
--reconfigure)
|
||||||
|
CLEAN=1
|
||||||
|
shift # past argument=value
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
echo "Usage : ./configure [OPTION]..."
|
||||||
|
echo "Add submodules depending on target host platform and"
|
||||||
|
echo "create makefile configuration to build for target host"
|
||||||
|
echo
|
||||||
|
echo -e "\033[1m --target-host=\033[mHOST"
|
||||||
|
echo -e "\tselect target host platform, default is \"guess\" and"
|
||||||
|
echo -e "\twill try to configure for your current OS"
|
||||||
|
echo -e "\033[1m --clean\033[m"
|
||||||
|
echo -e "\tclean build, config, cache, etc, and exit"
|
||||||
|
echo -e "\033[1m --reconfigure\033[m"
|
||||||
|
echo -e "\tcall clean, and configure"
|
||||||
|
echo -e "\033[1m -h, --help\033[m"
|
||||||
|
echo -e "\tshow help and exit"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
print_error "Unknown option $i"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
## Clean
|
||||||
|
if [ -n "${CLEAN}" ]; then
|
||||||
|
make clean
|
||||||
|
rm -rf .config .cache lib
|
||||||
|
echo "Cleaned project."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${EXIT_AFTER_CLEAN}" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Deduce current and target hosts
|
||||||
|
case "${OSTYPE}" in
|
||||||
|
linux*)
|
||||||
|
CURRENT_HOST="linux"
|
||||||
|
;;
|
||||||
|
# msys*|cygwin*)
|
||||||
|
# CURRENT_HOST="windows"
|
||||||
|
# ;;
|
||||||
|
# darwin*)
|
||||||
|
# CURRENT_HOST="macos"
|
||||||
|
# ;;
|
||||||
|
*)
|
||||||
|
print_error "Current detected os is unsuppoted to build from: ${OSTYPE}\nUse --help for more informations."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Detected current host: ${CURRENT_HOST}"
|
||||||
|
|
||||||
|
if [ "${TARGET_HOST}" = "guess" ]; then
|
||||||
|
TARGET_HOST=${CURRENT_HOST}
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${TARGET_HOST}" in
|
||||||
|
linux|fxcg50)
|
||||||
|
echo "Configuring for target: ${TARGET_HOST}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unsupported target host: ${TARGET_HOST}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
#
|
||||||
|
### Download libs
|
||||||
|
#echo "===== downloading lua ====="
|
||||||
|
#
|
||||||
|
#LUA_TAR_GZ="${CACHE_DIR}/lua-${LUA_VERSION}.tar.gz"
|
||||||
|
#if [ ! -e "${LUA_TAR_GZ}" ]; then
|
||||||
|
# echo "Downloading lua source..."
|
||||||
|
# if ! curl --output "${LUA_TAR_GZ}" "https://www.lua.org/ftp/lua-${LUA_VERSION}.tar.gz"; then
|
||||||
|
# print_error "Failed to download lua source."
|
||||||
|
# fi
|
||||||
|
#fi
|
||||||
|
#
|
||||||
|
#echo "Checking lua source sum..."
|
||||||
|
#if ! echo "${LUA_CHECKSUM} ${LUA_TAR_GZ}" | sha256sum -c; then
|
||||||
|
# CHECKSUM="$(sha256sum "${LUA_TAR_GZ}" | cut -d " " -f 1)"
|
||||||
|
# rm "${LUA_TAR_GZ}"
|
||||||
|
# print_error "Lua zip checksum not valid.\nExpected: \"${LUA_CHECKSUM}\"\nGot: \"${CHECKSUM}\"\nPlease rerun command to retry downloading."
|
||||||
|
#fi
|
||||||
|
#
|
||||||
|
#echo "Extracting lua source"
|
||||||
|
#if ! tar -xzf "${LUA_TAR_GZ}" --directory "${LIB_DIR}"; then
|
||||||
|
# print_error "Failed to extract lua source."
|
||||||
|
#fi
|
||||||
|
#
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
# 2D_Engine_C core
|
||||||
|
|
||||||
|
## Description
|
||||||
|
The core for the multi-platform 2D_Engine_C.
|
||||||
|
|
||||||
|
## Licences
|
||||||
|
- This program is open-source : you can redistribute and/or modify it under the term of the **GNU GPLv3**.
|
||||||
|
Copyright (C) 2025 - 2026 Ulysse Cura. See [LICENSE](../LICENSE.txt) or [gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
gcc
|
||||||
|
# Headers
|
||||||
|
-Icore/src
|
||||||
|
-Isrc/components
|
||||||
|
|
||||||
|
# Macros
|
||||||
|
-DDEBUG=2
|
||||||
|
-DASSET_FILE_NAME="$(make print-ASSET_OUTPUT)"
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Source files
|
||||||
|
CORE_SOURCES := \
|
||||||
|
linked_list.c \
|
||||||
|
vector2d.c \
|
||||||
|
inputs.c \
|
||||||
|
asset_manager.c \
|
||||||
|
texture.c \
|
||||||
|
map.c \
|
||||||
|
camera.c \
|
||||||
|
event_bus.c \
|
||||||
|
ecs.c \
|
||||||
|
display.c \
|
||||||
|
main.c
|
||||||
|
|
||||||
|
# Compiler and flags
|
||||||
|
CORE_CC = gcc
|
||||||
|
ifeq ($(RELEASE), 1)
|
||||||
|
CORE_CFLAGS = --std=c17 --pedantic -O3 -DASSET_FILE_NAME=\"$(notdir $(ASSET_OUTPUT))\" -D_GNU_SOURCE=1 $(shell pkg-config --cflags sdl3) # RELEASE
|
||||||
|
else
|
||||||
|
CORE_CFLAGS = --std=c17 --pedantic -O1 -g -DDEBUG=2 -DASSET_FILE_NAME=\"$(notdir $(ASSET_OUTPUT))\" -D_GNU_SOURCE=1 $(shell pkg-config --cflags sdl3) \
|
||||||
|
-Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers \
|
||||||
|
-Wformat=2 -Wswitch-default -Wswitch -Wcast-align \
|
||||||
|
-Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 \
|
||||||
|
-Wstrict-prototypes -Winline -Wundef -Wnested-externs \
|
||||||
|
-Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op \
|
||||||
|
-Wfloat-equal -Wstrict-aliasing=2 -Wredundant-decls \
|
||||||
|
-Wold-style-definition # DEVELOPEMENT
|
||||||
|
endif
|
||||||
|
|
||||||
|
CORE_INCLUDE_DIRS = $(SOURCE_DIR)/components $(CORE_DIR)
|
||||||
|
CORE_INCLUDE_DIRS := $(addprefix -I, $(CORE_INCLUDE_DIRS))
|
||||||
|
|
||||||
|
CORE_AR = ar
|
||||||
|
CORE_ARFLAGS =
|
||||||
|
|
||||||
|
# Used in final linking.
|
||||||
|
CORE_DEPS_FLAGS = $(shell pkg-config --libs sdl3) -lm
|
||||||
|
|
||||||
|
# Deduce objects
|
||||||
|
CORE_OBJECTS = $(CORE_SOURCES:%.c=$(BUILD_DIR)/$(CORE_DIR)/%.o)
|
||||||
|
|
||||||
|
# Current file nb to process
|
||||||
|
CURRENT_CORE_FILE := 0
|
||||||
|
|
||||||
|
# Build core target
|
||||||
|
build_core: core_setup count_core_build core_end
|
||||||
|
|
||||||
|
# Core build directories
|
||||||
|
core_setup:
|
||||||
|
@echo "Building $(notdir $(CORE_OUTPUT))"
|
||||||
|
$(Q)mkdir -p $(dir $(CORE_OBJECTS))
|
||||||
|
|
||||||
|
# Count nb of files to build
|
||||||
|
count_core_build:
|
||||||
|
$(eval export TOTAL_CORE_FILES := $(shell echo $$(($$(make -n $(CORE_OBJECTS) 2>/dev/null | grep -c "Building") + 1))))
|
||||||
|
|
||||||
|
# Link core target
|
||||||
|
$(CORE_OUTPUT): $(CORE_OBJECTS)
|
||||||
|
@echo -e "[100%] $(YELLOW)Linking $(notdir $(CORE_OUTPUT))$(RESET)"
|
||||||
|
$(Q)$(CORE_AR) rcs $(CORE_ARFLAGS) $@ $^
|
||||||
|
|
||||||
|
# Build .o files
|
||||||
|
$(BUILD_DIR)/$(CORE_DIR)/%.o: $(CORE_DIR)/%.c
|
||||||
|
$(eval CURRENT_CORE_FILE := $(shell echo $$(($(CURRENT_CORE_FILE)+1))))
|
||||||
|
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_CORE_FILE)*100/$(TOTAL_CORE_FILES)))))
|
||||||
|
@echo -e "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)"
|
||||||
|
$(Q)$(CORE_CC) $(CORE_CFLAGS) $(CORE_INCLUDE_DIRS) -MMD -MP -c $< -o $@
|
||||||
|
|
||||||
|
# Print ending message
|
||||||
|
core_end: $(CORE_OUTPUT)
|
||||||
|
@echo "Built target $(notdir $(CORE_OUTPUT))"
|
||||||
|
|
||||||
|
# Source files dependencies
|
||||||
|
-include $(CORE_OBJECTS:.o=.d)
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
#include "asset_manager.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "game.h"
|
||||||
|
#include "linked_list.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "memory_alloc.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
// Args : asset_type_t asset_type, const char *asset_name
|
||||||
|
int create_asset(asset_t *asset, va_list args)
|
||||||
|
{
|
||||||
|
asset->name = va_arg(args, const char *);
|
||||||
|
asset->type = ASSET_NULL;
|
||||||
|
asset->nb_refs = 0;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int destroy_asset(asset_t *asset)
|
||||||
|
{
|
||||||
|
switch(asset->type)
|
||||||
|
{
|
||||||
|
case ASSET_TEXTURE:
|
||||||
|
destroy_texture(asset->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASSET_MAP:
|
||||||
|
if(destroy_map(asset->data)) return_failure_int;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASSET_NULL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_printf("Asset type not valid : %d", asset->type);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int destroy_asset_header_def(asset_header_def_t *asset_header_def)
|
||||||
|
{
|
||||||
|
free(asset_header_def->name);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int asset_manager_init(void)
|
||||||
|
{
|
||||||
|
linked_list_init(&game.asset_manager.assets, sizeof(asset_t), (allocator_t)create_asset, (deleter_t)destroy_asset);
|
||||||
|
linked_list_init(&game.asset_manager.assets_header_defs, sizeof(asset_header_def_t), NULL, (deleter_t)destroy_asset_header_def);
|
||||||
|
|
||||||
|
game.asset_manager.asset_file = fopen(ASSET_FILE_NAME, "rb");
|
||||||
|
if(!game.asset_manager.asset_file)
|
||||||
|
{
|
||||||
|
error_printf("Failed to open asset file : %d", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nb_assets;
|
||||||
|
if(fread(&nb_assets, sizeof(nb_assets), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read asset file.");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(linked_list_reserve(&game.asset_manager.assets_header_defs, nb_assets)) return_failure_int;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < nb_assets; i++)
|
||||||
|
{
|
||||||
|
elem_t *asset_header_def_elem = create_elem(&game.asset_manager.assets_header_defs);
|
||||||
|
if(!asset_header_def_elem) return_failure_int;
|
||||||
|
asset_header_def_t *asset_header_def = asset_header_def_elem->data;
|
||||||
|
|
||||||
|
size_t cap = 0;
|
||||||
|
|
||||||
|
if(getdelim(&asset_header_def->name, &cap, '\0', game.asset_manager.asset_file) == -1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read asset file.");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&asset_header_def->start, sizeof(asset_header_def->start), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read asset file.");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list_push_back(&game.asset_manager.assets_header_defs, asset_header_def_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_printf("Initialized asset manager.");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int asset_manager_exit(void)
|
||||||
|
{
|
||||||
|
if(linked_list_exit(&game.asset_manager.assets_header_defs)) return_failure_int;
|
||||||
|
if(linked_list_exit(&game.asset_manager.assets)) return_failure_int;
|
||||||
|
|
||||||
|
if(fclose(game.asset_manager.asset_file))
|
||||||
|
{
|
||||||
|
error_printf("Failed to close asset file : %d", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_printf("Exited asset manager.");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for this file
|
||||||
|
// No args
|
||||||
|
static inline bool condition_is_asset_unused(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return !((asset_t *)elem->data)->nb_refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int asset_manager_collect_garbage(void)
|
||||||
|
{
|
||||||
|
if(linked_list_remove_if(&game.asset_manager.assets, condition_is_asset_unused, NULL)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for this file
|
||||||
|
// Arg : const char *asset_name
|
||||||
|
static inline bool condition_is_asset_name(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return !strcmp(((asset_t *)elem->data)->name, (const char *)arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only for this file
|
||||||
|
// Arg : const char *asset_name
|
||||||
|
static inline bool condition_is_asset_header_def_name(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return !strcmp(((asset_header_def_t *)elem->data)->name, (const char *)arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_t *asset_manager_load_asset(char *asset_name, asset_type_t asset_type)
|
||||||
|
{
|
||||||
|
// Check if asset has alread been loaded.
|
||||||
|
elem_t *asset_elem = linked_list_get_if(&game.asset_manager.assets, condition_is_asset_name, (void *)asset_name);
|
||||||
|
if(asset_elem)
|
||||||
|
{
|
||||||
|
asset_t *asset = asset_elem->data;
|
||||||
|
asset->nb_refs++;
|
||||||
|
|
||||||
|
debug_printf("Asset \"%s\" has already been loaded, nb refs : %d", asset->name, asset->nb_refs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If it didn't, load it
|
||||||
|
elem_t *asset_header_def_elem = linked_list_get_if(&game.asset_manager.assets_header_defs, condition_is_asset_header_def_name, (void *)asset_name);
|
||||||
|
if(!asset_header_def_elem)
|
||||||
|
{
|
||||||
|
error_printf("Asset doesn't exist in asset file : %s", asset_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
asset_header_def_t *asset_header_def = asset_header_def_elem->data;
|
||||||
|
|
||||||
|
asset_elem = create_elem(&game.asset_manager.assets, asset_name);
|
||||||
|
if(!asset_elem) return_failure_ptr;
|
||||||
|
asset_t *asset = asset_elem->data;
|
||||||
|
|
||||||
|
if(fseek(game.asset_manager.asset_file, asset_header_def->start, SEEK_SET))
|
||||||
|
{
|
||||||
|
error_printf("Failed to seek in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(asset_type)
|
||||||
|
{
|
||||||
|
case ASSET_TEXTURE:
|
||||||
|
asset->data = load_texture();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASSET_MAP:
|
||||||
|
asset->data = load_map();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_printf("Asset type doesn't exists : %d", asset_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!asset->data)
|
||||||
|
{
|
||||||
|
destroy_elem(&game.asset_manager.assets, asset_elem); // returns failure anyway
|
||||||
|
return_failure_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->type = asset_type;
|
||||||
|
asset->nb_refs++;
|
||||||
|
|
||||||
|
linked_list_push_back(&game.asset_manager.assets, asset_elem);
|
||||||
|
|
||||||
|
debug_printf("Asset \"%s\" has been loaded.", asset->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset_elem->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int asset_manager_let_go_asset(char *asset_name)
|
||||||
|
{
|
||||||
|
elem_t *asset_elem = linked_list_get_if(&game.asset_manager.assets, condition_is_asset_name, (void *)asset_name);
|
||||||
|
if(!asset_elem)
|
||||||
|
{
|
||||||
|
error_printf("Asset doesn't exists : %s", asset_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_t *asset = asset_elem->data;
|
||||||
|
asset->nb_refs--;
|
||||||
|
|
||||||
|
debug_printf("Asset \"%s\" has been let go, nb refs : %d", asset->name, asset->nb_refs);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_t *asset_manager_get_asset(char *asset_name)
|
||||||
|
{
|
||||||
|
elem_t *asset_elem = linked_list_get_if(&game.asset_manager.assets, condition_is_asset_name, (void *)asset_name);
|
||||||
|
if(!asset_elem)
|
||||||
|
{
|
||||||
|
error_printf("Asset \"%s\" hasn't been loaded yet.", asset_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset_elem->data;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef ASSET_MANAGER_H
|
||||||
|
#define ASSET_MANAGER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
typedef enum asset_type_t {
|
||||||
|
ASSET_NULL,
|
||||||
|
ASSET_TEXTURE,
|
||||||
|
ASSET_MAP
|
||||||
|
} asset_type_t;
|
||||||
|
|
||||||
|
typedef struct asset_t {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
uint32_t nb_refs;
|
||||||
|
|
||||||
|
asset_type_t type;
|
||||||
|
} asset_t;
|
||||||
|
|
||||||
|
int create_asset(asset_t *asset, va_list args);
|
||||||
|
int destroy_asset(asset_t *asset);
|
||||||
|
|
||||||
|
typedef struct asset_header_def_t {
|
||||||
|
char *name;
|
||||||
|
uint32_t start;
|
||||||
|
} asset_header_def_t;
|
||||||
|
|
||||||
|
int destroy_asset_header_def(asset_header_def_t *asset_header_def);
|
||||||
|
|
||||||
|
typedef struct asset_manager_t {
|
||||||
|
linked_list_t assets_header_defs;
|
||||||
|
linked_list_t assets;
|
||||||
|
FILE *asset_file;
|
||||||
|
} asset_manager_t;
|
||||||
|
|
||||||
|
int asset_manager_init(void);
|
||||||
|
int asset_manager_exit(void);
|
||||||
|
int asset_manager_collect_garbage(void);
|
||||||
|
|
||||||
|
asset_t *asset_manager_load_asset(char *asset_name, asset_type_t asset_type);
|
||||||
|
int asset_manager_let_go_asset(char *asset_name);
|
||||||
|
asset_t *asset_manager_get_asset(char *asset_name);
|
||||||
|
|
||||||
|
#endif // ASSET_MANAGER_H
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
void camera_init(void)
|
||||||
|
{
|
||||||
|
game.camera.view_rect = (rect_t) {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.w = DISPLAY_WIDTH / 2,
|
||||||
|
.h = DISPLAY_HEIGHT / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
game.camera.tracking_pos = NULL;
|
||||||
|
game.camera.tracking_center_offset = (vector2d_t) {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_update(void)
|
||||||
|
{
|
||||||
|
if(game.camera.tracking_pos)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.x = game.camera.tracking_pos->x + game.camera.tracking_center_offset.x - game.camera.view_rect.w / 2;
|
||||||
|
game.camera.view_rect.y = game.camera.tracking_pos->y + game.camera.tracking_center_offset.y - game.camera.view_rect.h / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(game.map)
|
||||||
|
{
|
||||||
|
if(game.camera.view_rect.w > game.map->width * TILE_SIZE)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.x = game.map->width * TILE_SIZE / 2 - game.camera.view_rect.w / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(game.camera.view_rect.x < 0)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.x = 0;
|
||||||
|
}
|
||||||
|
else if(game.camera.view_rect.x + game.camera.view_rect.w > game.map->width * TILE_SIZE)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.x = game.map->width * TILE_SIZE - game.camera.view_rect.w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(game.camera.view_rect.h > game.map->height * TILE_SIZE)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.y = game.map->height * TILE_SIZE / 2 - game.camera.view_rect.h / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(game.camera.view_rect.y < 0)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.y = 0;
|
||||||
|
}
|
||||||
|
else if(game.camera.view_rect.y + game.camera.view_rect.h > game.map->height * TILE_SIZE)
|
||||||
|
{
|
||||||
|
game.camera.view_rect.y = game.map->height * TILE_SIZE - game.camera.view_rect.h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include "vector2d.h"
|
||||||
|
|
||||||
|
typedef struct camera_t {
|
||||||
|
rect_t view_rect;
|
||||||
|
fvector2d_t *tracking_pos;
|
||||||
|
vector2d_t tracking_center_offset;
|
||||||
|
} camera_t;
|
||||||
|
|
||||||
|
void camera_init(void);
|
||||||
|
|
||||||
|
void camera_update(void);
|
||||||
|
|
||||||
|
#endif // CAMERA_H
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "asset_manager.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
inline int display_init(void)
|
||||||
|
{
|
||||||
|
if(!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
|
||||||
|
{
|
||||||
|
error_printf("Failed to initialise SDL3 : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_SetAppMetadata("2D_Engine_SDL3_Core", "1.0", "2D_Engine"))
|
||||||
|
{
|
||||||
|
error_printf("Failed to set app metadata : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
game.display.window = SDL_CreateWindow("2D_Engine", DISPLAY_WIDTH, DISPLAY_HEIGHT, 0);
|
||||||
|
if(!game.display.window)
|
||||||
|
{
|
||||||
|
error_printf("Failed to create window : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_printf("Listing available render drivers.");
|
||||||
|
const int NB_DRIVERS = SDL_GetNumRenderDrivers();
|
||||||
|
for(int i = 0; i < NB_DRIVERS; i++)
|
||||||
|
debug_printf("%d : %s", i, SDL_GetRenderDriver(i));
|
||||||
|
|
||||||
|
if(DRIVERS) warning_printf("This build of 2D_Engine_C only support the following drivers : %s", DRIVERS);
|
||||||
|
|
||||||
|
game.display.renderer = SDL_CreateRenderer(game.display.window, DRIVERS);
|
||||||
|
if(!game.display.renderer)
|
||||||
|
{
|
||||||
|
error_printf("Failed to create renderer : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_SetRenderVSync(game.display.renderer, SDL_RENDERER_VSYNC_ADAPTIVE))
|
||||||
|
{
|
||||||
|
error_printf("Failed to activate adaptative vsync : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_SetRenderDrawBlendMode(game.display.renderer, SDL_BLENDMODE_BLEND))
|
||||||
|
{
|
||||||
|
error_printf("Failed to set renderer draw blend mode : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_SetDefaultTextureScaleMode(game.display.renderer, SDL_SCALEMODE_NEAREST))
|
||||||
|
{
|
||||||
|
error_printf("Failed to set default texture scale mode : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_printf("Initialized display.");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void display_exit(void)
|
||||||
|
{
|
||||||
|
if(game.display.renderer) SDL_DestroyRenderer(game.display.renderer);
|
||||||
|
if(game.display.window) SDL_DestroyWindow(game.display.window);
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
debug_printf("Exited display.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int display_clear(void)
|
||||||
|
{
|
||||||
|
if(!SDL_SetRenderDrawColor(game.display.renderer, C_WHITE, 255))
|
||||||
|
{
|
||||||
|
error_printf("Failed to change renderer draw color : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_RenderClear(game.display.renderer))
|
||||||
|
{
|
||||||
|
error_printf("Failed to clear renderer : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int display_update(void)
|
||||||
|
{
|
||||||
|
if(!SDL_RenderPresent(game.display.renderer))
|
||||||
|
{
|
||||||
|
error_printf("Failed to present renderer : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG >= 1
|
||||||
|
float fps = 1000.0f / game.delta_time_ms;
|
||||||
|
static float max_fps_10s = 0.0f;
|
||||||
|
static float low_fps_10s = 10000000.0f;
|
||||||
|
|
||||||
|
if(fps > max_fps_10s) max_fps_10s = fps;
|
||||||
|
if(fps < low_fps_10s) low_fps_10s = fps;
|
||||||
|
|
||||||
|
static float fps_update_delta_time = 0.0f;
|
||||||
|
fps_update_delta_time += game.delta_time_ms;
|
||||||
|
|
||||||
|
if(fps_update_delta_time >= 100.0f)
|
||||||
|
{
|
||||||
|
fps_update_delta_time = 0.0f;
|
||||||
|
char title[100];
|
||||||
|
if(sprintf(title, "2D_Engine | cur: %.2ffps | max: %.2ffps | low: %.2ffps", fps, max_fps_10s, low_fps_10s) == -1)
|
||||||
|
{
|
||||||
|
error_printf("Sprintf failed : %d", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if(!SDL_SetWindowTitle(game.display.window, title))
|
||||||
|
{
|
||||||
|
error_printf("Failed to set window title : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float fps_max_low_update_delta_time = 0.0f;
|
||||||
|
fps_max_low_update_delta_time += game.delta_time_ms;
|
||||||
|
|
||||||
|
if(fps_max_low_update_delta_time > 10000.0f)
|
||||||
|
{
|
||||||
|
fps_max_low_update_delta_time = 0.0f;
|
||||||
|
max_fps_10s = 0.0f;
|
||||||
|
low_fps_10s = 10000000.0f;
|
||||||
|
}
|
||||||
|
#endif // DEBUG >= 1
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int display_draw_texture(texture_t *texture, const rect_t *src_rect, const rect_t *dst_rect, bool flip)
|
||||||
|
{
|
||||||
|
frect_t src_frect;
|
||||||
|
if(src_rect) rect_to_frect(src_rect, &src_frect);
|
||||||
|
|
||||||
|
frect_t dst_frect;
|
||||||
|
if(dst_rect)
|
||||||
|
dst_frect = (frect_t) {
|
||||||
|
.x = (dst_rect->x - game.camera.view_rect.x) * (DISPLAY_WIDTH / game.camera.view_rect.w),
|
||||||
|
.y = (dst_rect->y - game.camera.view_rect.y) * (DISPLAY_HEIGHT / game.camera.view_rect.h),
|
||||||
|
.w = dst_rect->w * (DISPLAY_WIDTH / game.camera.view_rect.w),
|
||||||
|
.h = dst_rect->h * (DISPLAY_HEIGHT / game.camera.view_rect.h)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!SDL_RenderTextureRotated(game.display.renderer, texture, src_rect ? &src_frect : NULL, dst_rect ? &dst_frect : NULL, 0, NULL, flip))
|
||||||
|
{
|
||||||
|
error_printf("Failed to copy texture to renderer : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int display_draw_frect(const frect_t *frect, unsigned int r, unsigned int g, unsigned int b, unsigned int a)
|
||||||
|
{
|
||||||
|
if(!SDL_SetRenderDrawColor(game.display.renderer, r, g, b, a))
|
||||||
|
{
|
||||||
|
error_printf("Failed to change renderer draw color : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
frect_t display_frect = {
|
||||||
|
.x = (frect->x - game.camera.view_rect.x) * (DISPLAY_WIDTH / game.camera.view_rect.w),
|
||||||
|
.y = (frect->y - game.camera.view_rect.y) * (DISPLAY_HEIGHT / game.camera.view_rect.h),
|
||||||
|
.w = frect->w * (DISPLAY_WIDTH / game.camera.view_rect.w),
|
||||||
|
.h = frect->h * (DISPLAY_HEIGHT / game.camera.view_rect.h)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!SDL_RenderRect(game.display.renderer, &display_frect))
|
||||||
|
{
|
||||||
|
error_printf("Failed to draw rectangle to renderer : %s", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef DISPLAY_H
|
||||||
|
#define DISPLAY_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_render.h>
|
||||||
|
#include "asset_manager.h"
|
||||||
|
#include "texture.h"
|
||||||
|
#include "vector2d.h"
|
||||||
|
|
||||||
|
#define C_BLACK 0, 0, 0
|
||||||
|
#define C_RED 255, 0, 0
|
||||||
|
#define C_GREEN 0, 255, 0
|
||||||
|
#define C_BLUE 0, 0, 255
|
||||||
|
#define C_WHITE 255, 255, 255
|
||||||
|
|
||||||
|
#define DISPLAY_WIDTH 720
|
||||||
|
#define DISPLAY_HEIGHT 480
|
||||||
|
|
||||||
|
//#define DRIVERS NULL
|
||||||
|
#define DRIVERS "vulkan,gpu,software"
|
||||||
|
|
||||||
|
typedef struct display_t {
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
} display_t;
|
||||||
|
|
||||||
|
int display_init(void);
|
||||||
|
void display_exit(void);
|
||||||
|
|
||||||
|
int display_clear(void);
|
||||||
|
int display_update(void);
|
||||||
|
|
||||||
|
int display_draw_texture(texture_t *texture, const rect_t *src_rect, const rect_t *dst_rect, bool flip);
|
||||||
|
int display_draw_frect(const frect_t *frect, unsigned int r, unsigned int g, unsigned int b, unsigned int a);
|
||||||
|
|
||||||
|
#endif // DISPLAY_H
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
#include "ecs.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "game.h"
|
||||||
|
#include "components.h"
|
||||||
|
#include "memory_alloc.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
const char *component_type_to_name(component_type_t component_type)
|
||||||
|
{
|
||||||
|
switch(component_type)
|
||||||
|
{
|
||||||
|
#define COMPONENT(NAME, ...) \
|
||||||
|
case NAME: \
|
||||||
|
return #NAME;
|
||||||
|
#include "components.def"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "INVALID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg : component_type_t component_type
|
||||||
|
int create_component(component_t *component, va_list args)
|
||||||
|
{
|
||||||
|
component->type = va_arg(args, component_type_t);
|
||||||
|
|
||||||
|
switch(component->type)
|
||||||
|
{
|
||||||
|
#define COMPONENT(NAME, PREFIX, DRAW) \
|
||||||
|
case NAME: \
|
||||||
|
component->init = PREFIX##_init; \
|
||||||
|
component->update = PREFIX##_update; \
|
||||||
|
component->draw = DRAW(PREFIX); \
|
||||||
|
component->data = malloc(sizeof(PREFIX##_data_t)); \
|
||||||
|
if(!component->data) \
|
||||||
|
{ \
|
||||||
|
error_printf("Failed to allocate memory : %d", errno); \
|
||||||
|
return EXIT_FAILURE; \
|
||||||
|
} \
|
||||||
|
component->deleter = PREFIX##_destroy; \
|
||||||
|
break;
|
||||||
|
#include "components.def"
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_printf("Component type doesn't exists : %d", component->type);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int destroy_component(component_t *component)
|
||||||
|
{
|
||||||
|
if(component->deleter(component)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_entity(entity_t *entity, va_list args)
|
||||||
|
{
|
||||||
|
entity->id = va_arg(args, size_t);
|
||||||
|
entity->draw_priority = 0;
|
||||||
|
|
||||||
|
linked_list_init(&entity->components, sizeof(component_t), (allocator_t)create_component, (deleter_t)destroy_component);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int destroy_entity(entity_t *entity)
|
||||||
|
{
|
||||||
|
if(linked_list_exit(&entity->components)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
component_t *entity_new_component(entity_t *entity, component_type_t component_type)
|
||||||
|
{
|
||||||
|
elem_t *component_elem = create_elem(&entity->components, component_type);
|
||||||
|
if(!component_elem) return_failure_ptr;
|
||||||
|
|
||||||
|
component_t *component = component_elem->data;
|
||||||
|
|
||||||
|
component->entity = entity;
|
||||||
|
|
||||||
|
if(component->init(component)) return_failure_ptr;
|
||||||
|
|
||||||
|
linked_list_push_front(&entity->components, component_elem);
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for this file
|
||||||
|
// Arg : component_type_t component_type
|
||||||
|
static inline bool condition_is_component_type(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return ((component_t *)elem->data)->type == *(component_type_t *)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline component_t *entity_get_component(const entity_t *entity, component_type_t component_type)
|
||||||
|
{
|
||||||
|
elem_t *component_elem = linked_list_get_if(&entity->components, condition_is_component_type, &component_type);
|
||||||
|
if(!component_elem) return NULL;
|
||||||
|
|
||||||
|
component_t *component = component_elem->data;
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for this file
|
||||||
|
// No args
|
||||||
|
static inline int action_update_component(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
component_t *component = elem->data;
|
||||||
|
|
||||||
|
if(component->update(component)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int update_entity(entity_t *entity)
|
||||||
|
{
|
||||||
|
if(linked_list_for_each(&entity->components, action_update_component, NULL)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No args
|
||||||
|
static inline int action_draw_component(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
component_t *component = elem->data;
|
||||||
|
|
||||||
|
if(component->draw)
|
||||||
|
if(component->draw(component)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int draw_entity(entity_t *entity)
|
||||||
|
{
|
||||||
|
if(linked_list_for_each(&entity->components, action_draw_component, NULL)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void entity_manager_init(void)
|
||||||
|
{
|
||||||
|
linked_list_init(&game.entity_manager.entities, sizeof(entity_t), (allocator_t)create_entity, (deleter_t)destroy_entity);
|
||||||
|
|
||||||
|
debug_printf("Initialized entity manager.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline entity_t *entity_manager_new_entity(size_t id)
|
||||||
|
{
|
||||||
|
elem_t *entity_elem = create_elem(&game.entity_manager.entities, id);
|
||||||
|
if(!entity_elem) return_failure_ptr;
|
||||||
|
|
||||||
|
linked_list_push_back(&game.entity_manager.entities, entity_elem);
|
||||||
|
|
||||||
|
debug_printf("Added entity %lu to entity manager.", id);
|
||||||
|
|
||||||
|
return entity_elem->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No args
|
||||||
|
static inline int action_update_entity(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return update_entity((entity_t *)elem->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int entity_manager_update(void)
|
||||||
|
{
|
||||||
|
if(linked_list_for_each(&game.entity_manager.entities, action_update_entity, NULL)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No args
|
||||||
|
static inline bool condition_is_draw_priority_higher(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return ((entity_t *)elem->data)->draw_priority > ((entity_t *)elem->next->data)->draw_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No args
|
||||||
|
static inline int action_draw_entity(elem_t *elem, void *arg __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return draw_entity((entity_t *)elem->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int entity_manager_draw(void)
|
||||||
|
{
|
||||||
|
linked_list_insertion_sort(&game.entity_manager.entities, condition_is_draw_priority_higher, NULL);
|
||||||
|
|
||||||
|
if(linked_list_for_each(&game.entity_manager.entities, action_draw_entity, NULL)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for this file
|
||||||
|
// Arg : size_t id
|
||||||
|
static inline bool condition_is_entity_name(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return ((entity_t *)elem->data)->id == *(size_t *)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int entity_manager_remove_entity(size_t id)
|
||||||
|
{
|
||||||
|
if(linked_list_remove_if(&game.entity_manager.entities, condition_is_entity_name, &id)) return_failure_int;
|
||||||
|
|
||||||
|
debug_printf("Removed entity %lu from entity manager.", id);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int entity_manager_exit(void)
|
||||||
|
{
|
||||||
|
if(linked_list_exit(&game.entity_manager.entities)) return_failure_int;
|
||||||
|
|
||||||
|
debug_printf("Exited entity manager.");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#ifndef ECS_H
|
||||||
|
#define ECS_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
typedef enum component_type_t {
|
||||||
|
#define COMPONENT(NAME, ...) NAME,
|
||||||
|
#include "components.def"
|
||||||
|
} component_type_t;
|
||||||
|
|
||||||
|
const char *component_type_to_name(component_type_t component_type);
|
||||||
|
|
||||||
|
// Component forwarding
|
||||||
|
typedef struct component_t component_t;
|
||||||
|
|
||||||
|
// Component init function. It is called when component added to entity. This function is necessary.
|
||||||
|
typedef int (*component_init_t)(component_t *component);
|
||||||
|
// Component update function. It is called in the main loop to update the component. This function is necessary.
|
||||||
|
typedef int (*component_update_t)(component_t *component);
|
||||||
|
// Component draw function. It is called in the main loop to draw the component on screen. This function is not necessary.
|
||||||
|
typedef int (*component_draw_t)(const component_t *component);
|
||||||
|
// Component deleter function. It is called when the component is removed. It should free the data in the component data but not the component itself.
|
||||||
|
typedef int (*component_deleter_t)(component_t *component);
|
||||||
|
|
||||||
|
typedef struct component_t {
|
||||||
|
component_init_t init;
|
||||||
|
component_update_t update;
|
||||||
|
component_draw_t draw;
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
component_deleter_t deleter;
|
||||||
|
|
||||||
|
struct entity_t *entity;
|
||||||
|
|
||||||
|
component_type_t type;
|
||||||
|
|
||||||
|
} component_t;
|
||||||
|
|
||||||
|
int create_component(component_t *component, va_list args);
|
||||||
|
int destroy_component(component_t *component);
|
||||||
|
|
||||||
|
typedef struct entity_t {
|
||||||
|
size_t id;
|
||||||
|
size_t draw_priority;
|
||||||
|
linked_list_t components;
|
||||||
|
} entity_t;
|
||||||
|
|
||||||
|
int create_entity(entity_t *entity, va_list args);
|
||||||
|
int destroy_entity(entity_t *entity);
|
||||||
|
|
||||||
|
component_t *entity_new_component(entity_t *entity, component_type_t component_type);
|
||||||
|
int update_entity(entity_t *entity);
|
||||||
|
int draw_entity(entity_t *entity);
|
||||||
|
component_t *entity_get_component(const entity_t *entity, component_type_t component_type);
|
||||||
|
|
||||||
|
typedef struct entity_manager_t {
|
||||||
|
linked_list_t entities;
|
||||||
|
} entity_manager_t;
|
||||||
|
|
||||||
|
void entity_manager_init(void);
|
||||||
|
int entity_manager_exit(void);
|
||||||
|
|
||||||
|
entity_t *entity_manager_new_entity(size_t id);
|
||||||
|
int entity_manager_update(void);
|
||||||
|
int entity_manager_draw(void);
|
||||||
|
int entity_manager_remove_entity(size_t id);
|
||||||
|
|
||||||
|
#endif // ECS_H
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef ERRORS_H
|
||||||
|
#define ERRORS_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifndef DEBUG
|
||||||
|
#define DEBUG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG > 0
|
||||||
|
#define debug_printf(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\033[32mINFO\033[m: " __VA_ARGS__); \
|
||||||
|
fputc('\n', stderr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define return_failure_int \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\tin %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
|
||||||
|
return EXIT_FAILURE; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#define return_failure_ptr \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\tin %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
|
||||||
|
return NULL; \
|
||||||
|
} while (0);
|
||||||
|
#define goto_failure \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\tin %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
|
||||||
|
goto failure; \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define debug_printf(...)
|
||||||
|
|
||||||
|
#define return_failure_int return EXIT_FAILURE;
|
||||||
|
#define return_failure_ptr return NULL;
|
||||||
|
#define goto_failure goto failure;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define warning_printf(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\033[35mWARNING\033[m: " __VA_ARGS__); \
|
||||||
|
fputc('\n', stderr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define error_printf(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "\033[31mERROR\033[m: %s:%d:%s(): ", __FILE__, __LINE__, __func__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fputc('\n', stderr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif // ERRORS_H
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
#include "event_bus.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "game.h"
|
||||||
|
#include "linked_list.h"
|
||||||
|
#include "memory_alloc.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
// Arg : callback_t callback
|
||||||
|
inline int create_subscription(subscription_t *subscription, va_list args)
|
||||||
|
{
|
||||||
|
subscription->callback = va_arg(args, callback_t);
|
||||||
|
subscription->data = va_arg(args, void *);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg : size_t id
|
||||||
|
inline int create_topic(topic_t *topic, va_list args)
|
||||||
|
{
|
||||||
|
topic->id = va_arg(args, size_t);
|
||||||
|
|
||||||
|
linked_list_init(&topic->subscriptions, sizeof(subscription_t), (allocator_t)create_subscription, NULL);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int destroy_topic(topic_t *topic)
|
||||||
|
{
|
||||||
|
if(linked_list_exit(&topic->subscriptions)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void event_bus_init(void)
|
||||||
|
{
|
||||||
|
linked_list_init(&game.event_bus.topics, sizeof(topic_t), (allocator_t)create_topic, (deleter_t)destroy_topic);
|
||||||
|
|
||||||
|
debug_printf("Initialized event bus.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int event_bus_exit(void)
|
||||||
|
{
|
||||||
|
if(linked_list_exit(&game.event_bus.topics)) return_failure_int;
|
||||||
|
|
||||||
|
debug_printf("Exited event bus.");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg : size_t id
|
||||||
|
static inline bool condition_is_topic_id(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return ((topic_t *)elem->data)->id == *(size_t *)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_bus_new_topic(size_t id)
|
||||||
|
{
|
||||||
|
if(linked_list_get_if(&game.event_bus.topics, condition_is_topic_id, &id)) return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
elem_t *topic_elem = create_elem(&game.event_bus.topics, id);
|
||||||
|
if(!topic_elem) return_failure_int;
|
||||||
|
|
||||||
|
linked_list_push_back(&game.event_bus.topics, topic_elem);
|
||||||
|
|
||||||
|
debug_printf("Created new topic with id : %lu", id);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int event_bus_remove_topic(size_t id)
|
||||||
|
{
|
||||||
|
if(linked_list_remove_if(&game.event_bus.topics, condition_is_topic_id, &id)) return_failure_int;
|
||||||
|
|
||||||
|
debug_printf("Removed topic with id : %lu", id);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args : va_list args
|
||||||
|
static int action_execute_callback(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
subscription_t *subscription = elem->data;
|
||||||
|
|
||||||
|
if(subscription->callback(arg, subscription->data)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_bus_publish(size_t id, void *arg)
|
||||||
|
{
|
||||||
|
elem_t *topic_elem = linked_list_get_if(&game.event_bus.topics, condition_is_topic_id, &id);
|
||||||
|
if(!topic_elem) return_failure_int;
|
||||||
|
|
||||||
|
topic_t *topic = topic_elem->data;
|
||||||
|
|
||||||
|
if(linked_list_for_each(&topic->subscriptions, action_execute_callback, arg)) return_failure_int;
|
||||||
|
|
||||||
|
debug_printf("Published event on topic with id : %lu", id);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_bus_subscribe(size_t id, callback_t callback, void *data)
|
||||||
|
{
|
||||||
|
elem_t *topic_elem = linked_list_get_if(&game.event_bus.topics, condition_is_topic_id, &id);
|
||||||
|
if(!topic_elem)
|
||||||
|
{
|
||||||
|
error_printf("Topic with id : %lu doesn't exists.", id);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
topic_t *topic = topic_elem->data;
|
||||||
|
|
||||||
|
elem_t *subscription_elem = create_elem(&topic->subscriptions, callback, data);
|
||||||
|
if(!subscription_elem) return_failure_int;
|
||||||
|
|
||||||
|
linked_list_push_back(&topic->subscriptions, subscription_elem);
|
||||||
|
|
||||||
|
debug_printf("Subscribed to topic with id : %lu", id);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arg : callback_t callback
|
||||||
|
static inline bool condition_is_callback(elem_t *elem, void *arg)
|
||||||
|
{
|
||||||
|
return ((subscription_t *)elem->data)->callback == *(callback_t *)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_bus_unsubscribe(size_t id, callback_t callback)
|
||||||
|
{
|
||||||
|
elem_t *topic_elem = linked_list_get_if(&game.event_bus.topics, condition_is_topic_id, &id);
|
||||||
|
if(!topic_elem) return_failure_int;
|
||||||
|
|
||||||
|
topic_t *topic = topic_elem->data;
|
||||||
|
|
||||||
|
if(linked_list_remove_if(&topic->subscriptions, condition_is_callback, &callback)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef EVENT_BUS_H
|
||||||
|
#define EVENT_BUS_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
typedef int (*callback_t)(void *event_arg, void *subscribtion_arg);
|
||||||
|
|
||||||
|
typedef struct subscription_t {
|
||||||
|
callback_t callback;
|
||||||
|
void *data;
|
||||||
|
} subscription_t;
|
||||||
|
|
||||||
|
int create_subscription(subscription_t *subscription, va_list args);
|
||||||
|
|
||||||
|
typedef struct topic_t {
|
||||||
|
size_t id;
|
||||||
|
linked_list_t subscriptions;
|
||||||
|
} topic_t;
|
||||||
|
|
||||||
|
int create_topic(topic_t *topic, va_list args);
|
||||||
|
int destroy_topic(topic_t *topic);
|
||||||
|
|
||||||
|
typedef struct event_bus_t {
|
||||||
|
linked_list_t topics;
|
||||||
|
} event_bus_t;
|
||||||
|
|
||||||
|
void event_bus_init(void);
|
||||||
|
int event_bus_exit(void);
|
||||||
|
|
||||||
|
int event_bus_new_topic(size_t id);
|
||||||
|
int event_bus_remove_topic(size_t id);
|
||||||
|
|
||||||
|
int event_bus_publish(size_t id, void *arg);
|
||||||
|
int event_bus_subscribe(size_t id, callback_t callback, void *data);
|
||||||
|
int event_bus_unsubscribe(size_t id, callback_t callback);
|
||||||
|
|
||||||
|
#endif // EVENT_BUS_H
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef GAME_H
|
||||||
|
#define GAME_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "display.h"
|
||||||
|
#include "asset_manager.h"
|
||||||
|
#include "camera.h"
|
||||||
|
#include "event_bus.h"
|
||||||
|
#include "ecs.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "inputs.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Game data.
|
||||||
|
* There should be only one instance of this struct.
|
||||||
|
*
|
||||||
|
* @param event Events data
|
||||||
|
* @param texture_manager Texture manager instance
|
||||||
|
* @param entity_manager Entity manager instance
|
||||||
|
* @param is_running Game running state
|
||||||
|
* @param delta_time_ms Frame time
|
||||||
|
*/
|
||||||
|
typedef struct game_t {
|
||||||
|
display_t display;
|
||||||
|
camera_t camera;
|
||||||
|
asset_manager_t asset_manager;
|
||||||
|
event_bus_t event_bus;
|
||||||
|
entity_manager_t entity_manager;
|
||||||
|
inputs_t inputs;
|
||||||
|
map_t *map;
|
||||||
|
|
||||||
|
float delta_time_ms;
|
||||||
|
bool is_running;
|
||||||
|
} game_t;
|
||||||
|
|
||||||
|
extern game_t game;
|
||||||
|
|
||||||
|
int game_init(void);
|
||||||
|
int game_exit(void);
|
||||||
|
|
||||||
|
void game_handle_event(void);
|
||||||
|
int game_update(void);
|
||||||
|
int game_render(void);
|
||||||
|
|
||||||
|
#endif // GAME_H
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "inputs.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
|
inline int poll_event(input_event_t *event)
|
||||||
|
{
|
||||||
|
SDL_Event sdl_event;
|
||||||
|
int ret = SDL_PollEvent(&sdl_event);
|
||||||
|
|
||||||
|
event->type = sdl_event.type;
|
||||||
|
event->key = sdl_event.key.scancode;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef KEYBOARD_H
|
||||||
|
#define KEYBOARD_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
#include "keycodes.h"
|
||||||
|
|
||||||
|
#define INPUT_EVENT_KEY_UP SDL_EVENT_KEY_UP
|
||||||
|
#define INPUT_EVENT_KEY_DOWN SDL_EVENT_KEY_DOWN
|
||||||
|
#define INPUT_EVENT_QUIT SDL_EVENT_QUIT
|
||||||
|
|
||||||
|
typedef struct input_event_t {
|
||||||
|
uint32_t type;
|
||||||
|
SDL_Scancode key;
|
||||||
|
} input_event_t;
|
||||||
|
|
||||||
|
int poll_event(input_event_t *event);
|
||||||
|
|
||||||
|
typedef struct inputs_t {
|
||||||
|
bool keys[MAX_KEYCODES];
|
||||||
|
} inputs_t;
|
||||||
|
|
||||||
|
#endif // KEYBOARD_H
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef KEYCODES_H
|
||||||
|
#define KEYCODES_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
|
#define MAX_KEYCODES SDL_SCANCODE_COUNT
|
||||||
|
|
||||||
|
#define KEY_UP SDL_SCANCODE_W
|
||||||
|
#define KEY_DOWN SDL_SCANCODE_S
|
||||||
|
#define KEY_LEFT SDL_SCANCODE_A
|
||||||
|
#define KEY_RIGHT SDL_SCANCODE_D
|
||||||
|
#define KEY_MOD SDL_SCANCODE_LCTRL
|
||||||
|
#define KEY_INTERACT SDL_SCANCODE_E
|
||||||
|
|
||||||
|
#endif // KEYCODES_H
|
||||||
|
|
@ -0,0 +1,429 @@
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "memory_alloc.h"
|
||||||
|
#include "linked_list.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
elem_t *create_elem(linked_list_t *linked_list, ...)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem_t *elem;
|
||||||
|
|
||||||
|
if(linked_list->reserved)
|
||||||
|
{
|
||||||
|
elem = linked_list->reserved;
|
||||||
|
linked_list->reserved = elem->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elem = malloc(sizeof(elem_t));
|
||||||
|
if(!elem)
|
||||||
|
{
|
||||||
|
error_printf("Failed to allocate memory : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem->data = malloc(linked_list->data_size);
|
||||||
|
if(!elem->data)
|
||||||
|
{
|
||||||
|
error_printf("Failed to allocate memory : %d", errno);
|
||||||
|
free(elem);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(linked_list->allocator)
|
||||||
|
{
|
||||||
|
va_list args, args_copy;
|
||||||
|
va_start(args, linked_list);
|
||||||
|
va_copy(args_copy, args);
|
||||||
|
|
||||||
|
if(linked_list->allocator(elem->data, args_copy)) return_failure_ptr;
|
||||||
|
|
||||||
|
va_end(args_copy);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int destroy_elem(linked_list_t *linked_list, elem_t *elem)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
if(linked_list->deleter)
|
||||||
|
if(linked_list->deleter(elem->data)) return_failure_int;
|
||||||
|
|
||||||
|
if(linked_list->size < linked_list->target_max_size)
|
||||||
|
{
|
||||||
|
elem->next = linked_list->reserved;
|
||||||
|
linked_list->reserved = elem;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(elem->data);
|
||||||
|
free(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linked_list_init(linked_list_t *linked_list, size_t data_size, const allocator_t allocator, const deleter_t elem_deleter)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
linked_list->data_size = data_size;
|
||||||
|
linked_list->allocator = allocator;
|
||||||
|
linked_list->deleter = elem_deleter;
|
||||||
|
|
||||||
|
linked_list->first = NULL;
|
||||||
|
linked_list->last = NULL;
|
||||||
|
linked_list->size = 0;
|
||||||
|
|
||||||
|
linked_list->reserved = NULL;
|
||||||
|
linked_list->target_max_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_exit(linked_list_t *linked_list)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
linked_list->target_max_size = 0;
|
||||||
|
|
||||||
|
elem_t *current_elem = linked_list->first;
|
||||||
|
elem_t *tmp;
|
||||||
|
|
||||||
|
while(current_elem)
|
||||||
|
{
|
||||||
|
tmp = current_elem;
|
||||||
|
|
||||||
|
current_elem = current_elem->next;
|
||||||
|
|
||||||
|
if(destroy_elem(linked_list, tmp)) return_failure_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_elem = linked_list->reserved;
|
||||||
|
|
||||||
|
while(current_elem)
|
||||||
|
{
|
||||||
|
tmp = current_elem;
|
||||||
|
|
||||||
|
current_elem = current_elem->next;
|
||||||
|
|
||||||
|
free(tmp->data);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_clear(linked_list_t *linked_list)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem_t *current_elem = linked_list->first;
|
||||||
|
elem_t *tmp;
|
||||||
|
|
||||||
|
while(current_elem)
|
||||||
|
{
|
||||||
|
tmp = current_elem;
|
||||||
|
|
||||||
|
current_elem = current_elem->next;
|
||||||
|
|
||||||
|
if(destroy_elem(linked_list, tmp)) return_failure_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list->first = NULL;
|
||||||
|
linked_list->last = NULL;
|
||||||
|
|
||||||
|
linked_list->size = 0;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_reserve(linked_list_t *linked_list, size_t target_max_size)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
linked_list->target_max_size = target_max_size;
|
||||||
|
|
||||||
|
for(size_t i = linked_list->size; i < target_max_size; i++)
|
||||||
|
{
|
||||||
|
elem_t *elem = malloc(sizeof(elem_t));
|
||||||
|
if(!elem)
|
||||||
|
{
|
||||||
|
error_printf("Failed to allocate memory : %d", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem->data = malloc(linked_list->data_size);
|
||||||
|
if(!elem->data)
|
||||||
|
{
|
||||||
|
free(elem);
|
||||||
|
error_printf("Failed to allocate memory : %d", errno);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem->next = linked_list->reserved;
|
||||||
|
linked_list->reserved = elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linked_list_push_back(linked_list_t *linked_list, elem_t *elem)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem->prev = linked_list->last;
|
||||||
|
elem->next = NULL;
|
||||||
|
|
||||||
|
if(linked_list->last)
|
||||||
|
linked_list->last->next = elem;
|
||||||
|
else
|
||||||
|
linked_list->first = elem;
|
||||||
|
|
||||||
|
linked_list->last = elem;
|
||||||
|
|
||||||
|
linked_list->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linked_list_push_front(linked_list_t *linked_list, elem_t *elem)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem->next = linked_list->first;
|
||||||
|
elem->prev = NULL;
|
||||||
|
|
||||||
|
if(linked_list->first)
|
||||||
|
linked_list->first->prev = elem;
|
||||||
|
else
|
||||||
|
linked_list->last = elem;
|
||||||
|
|
||||||
|
linked_list->first = elem;
|
||||||
|
|
||||||
|
linked_list->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_t *linked_list_pop_back(linked_list_t *linked_list)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem_t *elem = linked_list->last;
|
||||||
|
if(!elem)
|
||||||
|
{
|
||||||
|
error_printf("No elem to pop back.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list->last = elem->prev;
|
||||||
|
|
||||||
|
if(linked_list->last)
|
||||||
|
linked_list->last->next = NULL;
|
||||||
|
else
|
||||||
|
linked_list->first = NULL;
|
||||||
|
|
||||||
|
linked_list->size--;
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_t *linked_list_pop_front(linked_list_t *linked_list)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem_t *elem = linked_list->first;
|
||||||
|
if(!elem)
|
||||||
|
{
|
||||||
|
error_printf("No elem to pop front.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list->first = elem->next;
|
||||||
|
|
||||||
|
if(linked_list->first)
|
||||||
|
linked_list->first->prev = NULL;
|
||||||
|
else
|
||||||
|
linked_list->last = NULL;
|
||||||
|
|
||||||
|
linked_list->size--;
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_t *linked_list_get(const linked_list_t *linked_list, size_t index)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
if(index >= linked_list->size)
|
||||||
|
{
|
||||||
|
error_printf("List index out of range.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index == 0)
|
||||||
|
return linked_list->first;
|
||||||
|
|
||||||
|
if(index == linked_list->size - 1)
|
||||||
|
return linked_list->last;
|
||||||
|
|
||||||
|
elem_t *current_elem = linked_list->first;
|
||||||
|
if(!current_elem)
|
||||||
|
{
|
||||||
|
error_printf("Failed to get element : linked list is empty.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < index; i++)
|
||||||
|
{
|
||||||
|
current_elem = current_elem->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_insert(linked_list_t *linked_list, elem_t *elem, size_t index) // XXX TODO SIMPLIFY INSERT FUNC XXX //
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
if(index == 0)
|
||||||
|
{
|
||||||
|
linked_list_push_front(linked_list, elem);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
else if(index == linked_list->size)
|
||||||
|
{
|
||||||
|
linked_list_push_back(linked_list, elem);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_t *next_insert_elem = linked_list_get(linked_list, index);
|
||||||
|
if(!next_insert_elem) return_failure_int;
|
||||||
|
elem_t *prev_insert_elem = next_insert_elem->prev;
|
||||||
|
|
||||||
|
elem->prev = prev_insert_elem;
|
||||||
|
elem->next = next_insert_elem;
|
||||||
|
|
||||||
|
prev_insert_elem->next = elem;
|
||||||
|
next_insert_elem->prev = elem;
|
||||||
|
|
||||||
|
linked_list->size++;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_remove(linked_list_t *linked_list, elem_t *elem)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
|
||||||
|
elem_t *prev_elem = elem->prev;
|
||||||
|
elem_t *next_elem = elem->next;
|
||||||
|
|
||||||
|
if(prev_elem) prev_elem->next = next_elem;
|
||||||
|
else linked_list->first = next_elem;
|
||||||
|
if(next_elem) next_elem->prev = prev_elem;
|
||||||
|
else linked_list->last = prev_elem;
|
||||||
|
|
||||||
|
if(destroy_elem(linked_list, elem)) return_failure_int;
|
||||||
|
|
||||||
|
linked_list->size--;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_t *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, void *arg)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
assert("Condition cannot be NULL" && condition);
|
||||||
|
|
||||||
|
elem_t *current_elem = linked_list->first;
|
||||||
|
elem_t *next_elem;
|
||||||
|
|
||||||
|
while(current_elem)
|
||||||
|
{
|
||||||
|
next_elem = current_elem->next;
|
||||||
|
|
||||||
|
if(condition(current_elem, arg))
|
||||||
|
{
|
||||||
|
return current_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_elem = next_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_remove_if(linked_list_t *linked_list, const condition_t condition, void *arg)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
assert("Condition cannot be NULL" && condition);
|
||||||
|
|
||||||
|
elem_t *actual_elem = linked_list->first;
|
||||||
|
elem_t *next_elem;
|
||||||
|
|
||||||
|
while(actual_elem)
|
||||||
|
{
|
||||||
|
next_elem = actual_elem->next;
|
||||||
|
|
||||||
|
if(condition(actual_elem, arg))
|
||||||
|
{
|
||||||
|
if(linked_list_remove(linked_list, actual_elem))
|
||||||
|
{
|
||||||
|
return_failure_int;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_elem = next_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linked_list_for_each(const linked_list_t *linked_list, const action_t action, void *arg)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
assert("Action cannot be NULL" && action);
|
||||||
|
|
||||||
|
elem_t *current_elem = linked_list->first;
|
||||||
|
|
||||||
|
while(current_elem)
|
||||||
|
{
|
||||||
|
if(action(current_elem, arg))
|
||||||
|
{
|
||||||
|
return_failure_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_elem = current_elem->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void swap_elems_data(elem_t *elem_a, elem_t *elem_b)
|
||||||
|
{
|
||||||
|
void *tmp_data = elem_a->data;
|
||||||
|
elem_a->data = elem_b->data;
|
||||||
|
elem_b->data = tmp_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linked_list_insertion_sort(linked_list_t *linked_list, const condition_t condition, void *arg)
|
||||||
|
{
|
||||||
|
assert("Linked list cannot be NULL" && linked_list);
|
||||||
|
assert("Condition cannot be NULL" && condition);
|
||||||
|
|
||||||
|
elem_t *actual_elem = linked_list->first;
|
||||||
|
elem_t *next_elem;
|
||||||
|
|
||||||
|
while(actual_elem->next)
|
||||||
|
{
|
||||||
|
next_elem = actual_elem->next;
|
||||||
|
|
||||||
|
if(condition(actual_elem, arg))
|
||||||
|
{
|
||||||
|
swap_elems_data(actual_elem, next_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_elem = next_elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
#ifndef LINKED_LIST_H
|
||||||
|
#define LINKED_LIST_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Element struct for linked lists.
|
||||||
|
* This struct is the base for storing data in a linked list.
|
||||||
|
*
|
||||||
|
* @param data Raw pointer to any type of data
|
||||||
|
* @param prev Pointer to the prev element in the linked list
|
||||||
|
* @param next Pointer to the next element in the linked list
|
||||||
|
*/
|
||||||
|
typedef struct elem_t {
|
||||||
|
void *data;
|
||||||
|
struct elem_t *prev;
|
||||||
|
struct elem_t *next;
|
||||||
|
} elem_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A type for allocators used during the creation of data stored in elements' data.
|
||||||
|
*
|
||||||
|
* During the initialisation of linked lists you can pass in argument an allocator.
|
||||||
|
* You can pass NULL to the initialisation to not use an allocator.
|
||||||
|
* Your allocator_t function declaration must look like this :
|
||||||
|
*
|
||||||
|
* int name_of_your_allocator(void* data, va_list args);
|
||||||
|
*
|
||||||
|
* Then do what you need to fill your data.
|
||||||
|
*
|
||||||
|
* @param data Data to fill.
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
typedef int (*allocator_t)(void *data, va_list args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A type for deleters used during the destruction of data stored in elements' data.
|
||||||
|
*
|
||||||
|
* During the initialisation of linked lists you can pass in argument a deleter.
|
||||||
|
* You can pass NULL to the initialisation to not use a deleter.
|
||||||
|
* Your deleter_t function declaration must look like this :
|
||||||
|
*
|
||||||
|
* void name_of_your_deleter(void *data);
|
||||||
|
*
|
||||||
|
* Then do what you need to destroy your data (don't free the data itself).
|
||||||
|
*
|
||||||
|
* @param data Data to free.
|
||||||
|
*/
|
||||||
|
typedef int (*deleter_t)(void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A type for condition used for function like linked_list_remove_if.
|
||||||
|
*
|
||||||
|
* Your condition_t function declaration must look like this :
|
||||||
|
*
|
||||||
|
* bool name_of_your_condition(elem_t *elem, va_list);
|
||||||
|
*
|
||||||
|
* @param elem Processed element
|
||||||
|
* @param arg Argument to pass to the function
|
||||||
|
*
|
||||||
|
* @return False, true for the element to be removed.
|
||||||
|
*/
|
||||||
|
typedef bool (*condition_t)(elem_t *elem, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A type for action used for function like linked_list_for_each
|
||||||
|
*
|
||||||
|
* Your condition_t function declaration must look like this :
|
||||||
|
*
|
||||||
|
* int name_of_your_condition(elem_t *elem, va_list);
|
||||||
|
*
|
||||||
|
* @param elem Processed element
|
||||||
|
* @param arg Argument to pass to the function
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
typedef int (*action_t)(elem_t *elem, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base of linked lists.
|
||||||
|
*
|
||||||
|
* @param data_size Size of the data stored in elements
|
||||||
|
* @param allocator Allocator of data when creating new elements
|
||||||
|
* @param deleter Deleter of data when removing elements
|
||||||
|
* @param first Pointer to the first element in the list
|
||||||
|
* @param last Pointer to the last element in the list
|
||||||
|
* @param size Size of the linked list
|
||||||
|
* @param reserved Pointer to the first reserved element
|
||||||
|
* @param target_max_size Total target number of elements in the list
|
||||||
|
*/
|
||||||
|
typedef struct linked_list_t {
|
||||||
|
size_t data_size;
|
||||||
|
allocator_t allocator;
|
||||||
|
deleter_t deleter;
|
||||||
|
elem_t *first;
|
||||||
|
elem_t *last;
|
||||||
|
size_t size;
|
||||||
|
elem_t *reserved;
|
||||||
|
size_t target_max_size;
|
||||||
|
} linked_list_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create an elem to insert in a linked list. The data pointer is allocated.
|
||||||
|
* If there is an element available in reserved list then it will be used.
|
||||||
|
* The element returned is filled with garbage memory.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to an linked list
|
||||||
|
* @param ... Arguments to pass to the allocator
|
||||||
|
*
|
||||||
|
* @return pointer to initialised elem, NULL on error.
|
||||||
|
*/
|
||||||
|
elem_t *create_elem(linked_list_t *linked_list, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy the given element and its data.
|
||||||
|
* May move the element into the reserved list for future use depending on target size of the list.
|
||||||
|
* This function makes the given element unsafe to use.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param elem Pointer to an element
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int destroy_elem(linked_list_t *linked_list, elem_t *elem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Init a linked list.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param data_size Data size for elements data in the linked list
|
||||||
|
* @param deleter The deleter for elem
|
||||||
|
*/
|
||||||
|
void linked_list_init(linked_list_t *linked_list, size_t data_size, const allocator_t allocator, const deleter_t deleter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the given linked list and free reserved elements, do nothing on error.
|
||||||
|
* This function makes the given linked list unsafe to use.
|
||||||
|
*
|
||||||
|
* @brief linked_list Pointer to a linked list
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_exit(linked_list_t *linked_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pre-allocate elements for a target size.
|
||||||
|
* If you want n more elements in your list, then you want linked_list.size + n target_size.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param target_size Target size of linked list
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_reserve(linked_list_t *linked_list, size_t target_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the given linked list, do nothing on error.
|
||||||
|
* May move the elements into the reserved list for future use depending on target size of the list.
|
||||||
|
*
|
||||||
|
* @brief linked_list Pointer to a linked list
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_clear(linked_list_t *linked_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insert an element from the back in a linked list, do nothing on error.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param elem Pointer to an element
|
||||||
|
*/
|
||||||
|
void linked_list_push_back(linked_list_t *linked_list, elem_t *elem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insert an element from the front in a linked list, do nothing on error.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param elem Pointer to an element
|
||||||
|
*/
|
||||||
|
void linked_list_push_front(linked_list_t *linked_list, elem_t *elem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove and return the last element in the given linked list, do nothing on error.
|
||||||
|
* The element returned have unsafe `next` and `prev` pointers.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
*
|
||||||
|
* @return Pointer to element, NULL on error.
|
||||||
|
*/
|
||||||
|
elem_t *linked_list_pop_back(linked_list_t *linked_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove and return the first element in the given linked list, do nothing on error.
|
||||||
|
* The element returned have unsafe `next` and `prev` pointers.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
*
|
||||||
|
* @return Pointer to element, NULL on error.
|
||||||
|
*/
|
||||||
|
elem_t *linked_list_pop_front(linked_list_t *linked_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insert element at the given index in the linked list, do nothing on error.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param elem Pointer to an element
|
||||||
|
* @param index Index to insert element
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_insert(linked_list_t *linked_list, elem_t *elem, size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get element at the given index in the linked list.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param index Index of the element
|
||||||
|
*
|
||||||
|
* @return Pointer to element, NULL on error.
|
||||||
|
*/
|
||||||
|
elem_t *linked_list_get(const linked_list_t *linked_list, size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove the given elem in the linked list, do nothing on error.
|
||||||
|
* May move the elements into the reserved list for future use depending on target size of the list.
|
||||||
|
* This function makes the given element unsafe to use.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param elem Element to remove
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_remove(linked_list_t *linked_list, elem_t *elem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return first element that the condition verify.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to a linked list
|
||||||
|
* @param condition Condition to verify
|
||||||
|
* @param ... Argument to pass to the condition
|
||||||
|
*
|
||||||
|
* @return Pointer to element, NULL if no element verify the condition or if an error occurred.
|
||||||
|
*/
|
||||||
|
elem_t *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove every elements that the condition verify, do nothing on error.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to an linked list
|
||||||
|
* @param condition Condition to verify
|
||||||
|
* @param ... Argument to pass to the condition
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_remove_if(linked_list_t *linked_list, const condition_t condition, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Apply an action to every elements in the list.
|
||||||
|
* The action can be anything that doesn't destroy the element.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to an linked list
|
||||||
|
* @param action Action to apply
|
||||||
|
* @param ... Argument to pass to the action
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
int linked_list_for_each(const linked_list_t *linked_list, const action_t action, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do an insertion sort data of the given linked list.
|
||||||
|
* The condition return true to swap element's data with next one.
|
||||||
|
*
|
||||||
|
* @param linked_list Pointer to an linked list
|
||||||
|
* @param action Action to apply
|
||||||
|
*
|
||||||
|
* @return EXIT_SUCCESS, EXIT_FAILURE on error.
|
||||||
|
*/
|
||||||
|
void linked_list_insertion_sort(linked_list_t *linked_list, const condition_t condition, void *arg);
|
||||||
|
|
||||||
|
#endif // LINKED_LIST_H
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "game.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
game_t game;
|
||||||
|
|
||||||
|
static inline void update_time(void)
|
||||||
|
{
|
||||||
|
static uint64_t last_clock_state = 0;
|
||||||
|
uint64_t start_clock = SDL_GetTicksNS();
|
||||||
|
game.delta_time_ms = SDL_NS_TO_MS((float)(start_clock - last_clock_state));
|
||||||
|
if(game.delta_time_ms > 100.0f) game.delta_time_ms = 100.0f;
|
||||||
|
last_clock_state = start_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
if(game_init()) goto_failure;
|
||||||
|
|
||||||
|
while(game.is_running)
|
||||||
|
{
|
||||||
|
update_time();
|
||||||
|
game_handle_event();
|
||||||
|
if(game_update()) goto_failure;
|
||||||
|
if(game_render()) goto_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(game_exit()) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
game_exit(); // return failure anyway
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,485 @@
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
#include "asset_manager.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "ecs.h"
|
||||||
|
#include "components.h"
|
||||||
|
#include "vector2d.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "memory_alloc.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
map_t *load_map(void)
|
||||||
|
{
|
||||||
|
map_t *map = malloc(sizeof(map_t));
|
||||||
|
|
||||||
|
if(fread(&map->width, sizeof(map->width), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&map->height, sizeof(map->height), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&map->backgroundlayernb, sizeof(map->backgroundlayernb), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list_init(&map->backgroundlayers, map->width * map->height * sizeof(uint16_t), NULL, NULL);
|
||||||
|
if(linked_list_reserve(&map->backgroundlayers, map->backgroundlayernb)) return_failure_ptr;
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < map->backgroundlayernb; i++)
|
||||||
|
{
|
||||||
|
elem_t *backgroundlayer_elem = create_elem(&map->backgroundlayers);
|
||||||
|
if(!backgroundlayer_elem) return_failure_ptr;
|
||||||
|
|
||||||
|
uint16_t *backgroundlayer = backgroundlayer_elem->data;
|
||||||
|
if(fread(backgroundlayer, sizeof(*backgroundlayer), map->width * map->height, game.asset_manager.asset_file) != (size_t)map->width * (size_t)map->height)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
linked_list_push_back(&map->backgroundlayers, backgroundlayer_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
map->foregroundlayer = malloc(map->width * map->height * sizeof(*map->foregroundlayer));
|
||||||
|
if(fread(map->foregroundlayer, sizeof(*map->foregroundlayer), map->width * map->height, game.asset_manager.asset_file) != (size_t)map->width * (size_t)map->height)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
map->hitboxes = malloc(map->width * map->height * sizeof(*map->hitboxes));
|
||||||
|
if(fread(map->hitboxes, sizeof(*map->hitboxes), map->width * map->height, game.asset_manager.asset_file) != (size_t)map->width * (size_t)map->height)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&map->entities_nb, sizeof(map->entities_nb), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
map->entities_defs = calloc(map->entities_nb, sizeof(map_entity_def_t));
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < map->entities_nb; i++)
|
||||||
|
{
|
||||||
|
map_entity_def_t *entity_def = &map->entities_defs[i];
|
||||||
|
|
||||||
|
if(fread(&entity_def->id, sizeof(entity_def->id), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&entity_def->components_nb, sizeof(entity_def->components_nb), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity_def->components_defs = calloc(entity_def->components_nb, sizeof(map_entity_component_def_t));
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < entity_def->components_nb; j++)
|
||||||
|
{
|
||||||
|
map_entity_component_def_t *component_def = &entity_def->components_defs[j];
|
||||||
|
|
||||||
|
if(fread(&component_def->type, sizeof(component_def->type), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(component_def->type)
|
||||||
|
{
|
||||||
|
size_t data_size;
|
||||||
|
|
||||||
|
case TRANSFORM_COMPONENT:
|
||||||
|
case HITBOX_COMPONENT:
|
||||||
|
data_size = sizeof(fvector2d_t) * 2;
|
||||||
|
component_def->attributes = malloc(data_size);
|
||||||
|
if(fread(component_def->attributes, data_size, 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPRITE_COMPONENT:
|
||||||
|
{
|
||||||
|
size_t cap = 0;
|
||||||
|
if(getdelim((char **)&component_def->attributes, &cap, '\0', game.asset_manager.asset_file) == -1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ANIMATION_SYSTEM:
|
||||||
|
data_size = sizeof(int) * 2 + sizeof(float) + sizeof(bool) * 3;
|
||||||
|
component_def->attributes = malloc(data_size);
|
||||||
|
if(fread(component_def->attributes, data_size, 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INTERACT_COMPONENT:
|
||||||
|
data_size = sizeof(fvector2d_t) * 2 + sizeof(int) * 3;
|
||||||
|
component_def->attributes = malloc(data_size);
|
||||||
|
if(fread(component_def->attributes, data_size, 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DOOR_COMPONENT:
|
||||||
|
data_size = sizeof(int) + sizeof(bool);
|
||||||
|
component_def->attributes = malloc(data_size);
|
||||||
|
if(fread(component_def->attributes, data_size, 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LEVER_COMPONENT:
|
||||||
|
data_size = sizeof(int) + sizeof(bool);
|
||||||
|
component_def->attributes = malloc(data_size);
|
||||||
|
if(fread(component_def->attributes, data_size, 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file : %d", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_printf("Component type not supported for loading : %s (%d)", component_type_to_name(component_def->type), component_def->type);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
int destroy_map(map_t *map)
|
||||||
|
{
|
||||||
|
if(map->entities_defs)
|
||||||
|
{
|
||||||
|
for(uint32_t i = 0; i < map->entities_nb; i++)
|
||||||
|
{
|
||||||
|
map_entity_def_t *entity_def = &map->entities_defs[i];
|
||||||
|
|
||||||
|
if(entity_def->components_defs)
|
||||||
|
{
|
||||||
|
for(uint32_t j = 0; j < entity_def->components_nb; j++)
|
||||||
|
{
|
||||||
|
map_entity_component_def_t *component_def = &entity_def->components_defs[j];
|
||||||
|
if(component_def->attributes) free(component_def->attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(entity_def->components_defs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(map->entities_defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(map->hitboxes) free(map->hitboxes);
|
||||||
|
|
||||||
|
if(map->foregroundlayer) free(map->foregroundlayer);
|
||||||
|
|
||||||
|
if(linked_list_exit(&map->backgroundlayers)) return_failure_int;
|
||||||
|
|
||||||
|
free(map);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_load_entities(map_t *map)
|
||||||
|
{
|
||||||
|
for(uint32_t i = 0; i < map->entities_nb; i++)
|
||||||
|
{
|
||||||
|
map_entity_def_t *entity_def = &map->entities_defs[i];
|
||||||
|
|
||||||
|
entity_t *entity = entity_manager_new_entity(entity_def->id);
|
||||||
|
if(!entity) return_failure_int;
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < entity_def->components_nb; j++)
|
||||||
|
{
|
||||||
|
map_entity_component_def_t *component_def = &entity_def->components_defs[j];
|
||||||
|
|
||||||
|
component_t *component = entity_new_component(entity, component_def->type);
|
||||||
|
if(!entity) return_failure_int;
|
||||||
|
|
||||||
|
switch(component_def->type)
|
||||||
|
{
|
||||||
|
case TRANSFORM_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
frect_t bounds;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
transform_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
component_data->bounds = data->bounds;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SPRITE_COMPONENT:
|
||||||
|
{
|
||||||
|
sprite_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
asset_t *texture_asset = asset_manager_load_asset((char *)component_def->attributes, ASSET_TEXTURE);
|
||||||
|
if(!texture_asset) return_failure_int;
|
||||||
|
|
||||||
|
component_data->texture = texture_asset->data;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ANIMATION_SYSTEM:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
int nb_frames;
|
||||||
|
int current_frame_nb;
|
||||||
|
float frame_delay_ms;
|
||||||
|
bool play;
|
||||||
|
bool loop;
|
||||||
|
bool reverse;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
animation_system_data_t *system_data = component->data;
|
||||||
|
|
||||||
|
system_data->nb_frames = data->nb_frames;
|
||||||
|
system_data->current_frame_nb = data->current_frame_nb;
|
||||||
|
system_data->frame_delay_ms = data->frame_delay_ms;
|
||||||
|
system_data->play = data->play;
|
||||||
|
system_data->loop = data->loop;
|
||||||
|
system_data->reverse = data->reverse;
|
||||||
|
|
||||||
|
if(animation_system_apply_changes(component)) return_failure_int;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HITBOX_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
fvector2d_t position;
|
||||||
|
fvector2d_t scale;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
hitbox_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
component_data->position = data->position;
|
||||||
|
component_data->scale = data->scale;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DOOR_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
int topic_id;
|
||||||
|
bool state;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
door_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
component_data->topic_id = data->topic_id;
|
||||||
|
component_data->state = data->state;
|
||||||
|
|
||||||
|
if(door_component_subscribe(component)) return_failure_int;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case INTERACT_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
fvector2d_t position;
|
||||||
|
fvector2d_t scale;
|
||||||
|
int topic_id;
|
||||||
|
int type;
|
||||||
|
int state;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
interact_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
component_data->position = data->position;
|
||||||
|
component_data->scale = data->scale;
|
||||||
|
component_data->type = data->type;
|
||||||
|
component_data->state = data->state;
|
||||||
|
|
||||||
|
if(event_bus_new_topic(data->topic_id)) return_failure_int;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LEVER_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
int topic_id;
|
||||||
|
bool state;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
lever_component_data_t *component_data = component->data;
|
||||||
|
|
||||||
|
component_data->topic_id = data->topic_id;
|
||||||
|
component_data->state = data->state;
|
||||||
|
|
||||||
|
if(lever_component_subscribe(component)) return_failure_int;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_printf("Component type not supported for loading : %s (%d)", component_type_to_name(component_def->type), component_def->type);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_remove_entities(map_t *map)
|
||||||
|
{
|
||||||
|
for(uint32_t i = 0; i < map->entities_nb; i++)
|
||||||
|
{
|
||||||
|
map_entity_def_t *entity_def = &map->entities_defs[i];
|
||||||
|
|
||||||
|
if(entity_manager_remove_entity(entity_def->id)) return_failure_int;
|
||||||
|
|
||||||
|
for(uint32_t j = 0; j < entity_def->components_nb; j++)
|
||||||
|
{
|
||||||
|
map_entity_component_def_t *component_def = &entity_def->components_defs[j];
|
||||||
|
|
||||||
|
switch(component_def->type)
|
||||||
|
{
|
||||||
|
case SPRITE_COMPONENT:
|
||||||
|
if(asset_manager_let_go_asset((char *)component_def->attributes)) return_failure_int;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INTERACT_COMPONENT:
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
fvector2d_t position;
|
||||||
|
fvector2d_t scale;
|
||||||
|
int topic_id;
|
||||||
|
int type;
|
||||||
|
int state;
|
||||||
|
} *data = component_def->attributes;
|
||||||
|
|
||||||
|
if(event_bus_remove_topic(data->topic_id)) return_failure_int;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int draw_tile(uint16_t tile_x, uint16_t tile_y, uint16_t tile_id)
|
||||||
|
{
|
||||||
|
if(!tile_id) return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
rect_t src_frect = {
|
||||||
|
.x = tile_id * TILE_SIZE % game.map->tileset->w,
|
||||||
|
.y = tile_id * TILE_SIZE / game.map->tileset->w * TILE_SIZE,
|
||||||
|
.w = TILE_SIZE,
|
||||||
|
.h = TILE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
rect_t dst_frect = {
|
||||||
|
.x = tile_x * TILE_SIZE,
|
||||||
|
.y = tile_y * TILE_SIZE,
|
||||||
|
.w = TILE_SIZE,
|
||||||
|
.h = TILE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
if(display_draw_texture(game.map->tileset, &src_frect, &dst_frect, false)) return_failure_int;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_display_background(void)
|
||||||
|
{
|
||||||
|
uint16_t left_tile = 0;
|
||||||
|
if(game.camera.view_rect.x / TILE_SIZE > 0)
|
||||||
|
left_tile = game.camera.view_rect.x / TILE_SIZE;
|
||||||
|
|
||||||
|
uint16_t right_tile = game.map->width;
|
||||||
|
if((game.camera.view_rect.x + game.camera.view_rect.w) / TILE_SIZE + 1 < game.map->width)
|
||||||
|
right_tile = (game.camera.view_rect.x + game.camera.view_rect.w) / TILE_SIZE + 1;
|
||||||
|
|
||||||
|
uint16_t top_tile = 0;
|
||||||
|
if(game.camera.view_rect.y / TILE_SIZE > 0)
|
||||||
|
top_tile = game.camera.view_rect.y / TILE_SIZE;
|
||||||
|
|
||||||
|
uint16_t bottom_tile = game.map->height;
|
||||||
|
if((game.camera.view_rect.y + game.camera.view_rect.h) / TILE_SIZE + 1 < game.map->height)
|
||||||
|
bottom_tile = (game.camera.view_rect.y + game.camera.view_rect.h) / TILE_SIZE + 1;
|
||||||
|
|
||||||
|
for(uint16_t tile_y = top_tile; tile_y < bottom_tile; tile_y++)
|
||||||
|
for(uint16_t tile_x = left_tile; tile_x < right_tile; tile_x++)
|
||||||
|
{
|
||||||
|
elem_t *backgroundlayer_elem = game.map->backgroundlayers.first;
|
||||||
|
|
||||||
|
while(backgroundlayer_elem)
|
||||||
|
{
|
||||||
|
if(draw_tile(tile_x, tile_y, ((uint16_t *)backgroundlayer_elem->data)[tile_y * game.map->width + tile_x])) return_failure_int;
|
||||||
|
backgroundlayer_elem = backgroundlayer_elem->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_display_foreground(void)
|
||||||
|
{
|
||||||
|
uint32_t left_tile = 0;
|
||||||
|
if(game.camera.view_rect.x / TILE_SIZE > 0)
|
||||||
|
left_tile = game.camera.view_rect.x / TILE_SIZE;
|
||||||
|
|
||||||
|
uint32_t right_tile = game.map->width;
|
||||||
|
if((game.camera.view_rect.x + game.camera.view_rect.w) / TILE_SIZE + 1 < (int)game.map->width)
|
||||||
|
right_tile = (game.camera.view_rect.x + game.camera.view_rect.w) / TILE_SIZE + 1;
|
||||||
|
|
||||||
|
uint32_t top_tile = 0;
|
||||||
|
if(game.camera.view_rect.y / TILE_SIZE > 0)
|
||||||
|
top_tile = game.camera.view_rect.y / TILE_SIZE;
|
||||||
|
|
||||||
|
uint32_t bottom_tile = game.map->height;
|
||||||
|
if((game.camera.view_rect.y + game.camera.view_rect.h) / TILE_SIZE + 1 < (int)game.map->height)
|
||||||
|
bottom_tile = (game.camera.view_rect.y + game.camera.view_rect.h) / TILE_SIZE + 1;
|
||||||
|
|
||||||
|
for(uint32_t tile_y = top_tile; tile_y < bottom_tile; tile_y++)
|
||||||
|
for(uint32_t tile_x = left_tile; tile_x < right_tile; tile_x++)
|
||||||
|
{
|
||||||
|
if(draw_tile(tile_x, tile_y, game.map->foregroundlayer[tile_y * game.map->width + tile_x])) return_failure_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef MAP_H
|
||||||
|
#define MAP_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "linked_list.h"
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
|
#define TILE_SIZE 16
|
||||||
|
|
||||||
|
typedef struct map_entity_component_def_t {
|
||||||
|
uint16_t type;
|
||||||
|
void *attributes;
|
||||||
|
} map_entity_component_def_t;
|
||||||
|
|
||||||
|
typedef struct map_entity_def_t {
|
||||||
|
uint32_t id;
|
||||||
|
uint16_t components_nb;
|
||||||
|
map_entity_component_def_t *components_defs;
|
||||||
|
} map_entity_def_t;
|
||||||
|
|
||||||
|
typedef struct map_t {
|
||||||
|
uint16_t width, height;
|
||||||
|
uint8_t backgroundlayernb;
|
||||||
|
linked_list_t backgroundlayers;
|
||||||
|
uint16_t *foregroundlayer;
|
||||||
|
texture_t *tileset;
|
||||||
|
uint8_t *hitboxes;
|
||||||
|
uint16_t entities_nb;
|
||||||
|
map_entity_def_t *entities_defs;
|
||||||
|
} map_t;
|
||||||
|
|
||||||
|
map_t *load_map(void);
|
||||||
|
|
||||||
|
int destroy_map(map_t *map);
|
||||||
|
|
||||||
|
int map_load_entities(map_t *map);
|
||||||
|
|
||||||
|
int map_remove_entities(map_t *map);
|
||||||
|
|
||||||
|
int map_display_background(void);
|
||||||
|
|
||||||
|
int map_display_foreground(void);
|
||||||
|
|
||||||
|
#endif // MAP_H
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef MEMORY_ALLOC_H
|
||||||
|
#define MEMORY_ALLOC_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#endif // MEMORY_ALLOC_H
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
|
#include "asset_manager.h"
|
||||||
|
#include "game.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
texture_t *load_texture(void)
|
||||||
|
{
|
||||||
|
uint16_t width, height;
|
||||||
|
|
||||||
|
if(fread(&width, sizeof(width), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(&height, sizeof(height), 1, game.asset_manager.asset_file) != 1)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read in asset file.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *tmp_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGB565);
|
||||||
|
if(!tmp_surface)
|
||||||
|
{
|
||||||
|
error_printf("Failed to create temporary surface : %s", SDL_GetError());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_LockSurface(tmp_surface))
|
||||||
|
{
|
||||||
|
error_printf("Failed to lock temporary surface : %s", SDL_GetError());
|
||||||
|
goto_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(tmp_surface->pixels, sizeof(uint16_t), width * height, game.asset_manager.asset_file) != (size_t)width * (size_t)height)
|
||||||
|
{
|
||||||
|
error_printf("Failed to read asset file.");
|
||||||
|
goto_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!SDL_SetSurfaceColorKey(tmp_surface, true, TEXTURE_TRANSPARENT_COLOR))
|
||||||
|
{
|
||||||
|
error_printf("Failed to set color key of temporary surface : %s", SDL_GetError());
|
||||||
|
goto_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture_t *texture = SDL_CreateTextureFromSurface(game.display.renderer, tmp_surface);
|
||||||
|
if(!texture)
|
||||||
|
{
|
||||||
|
error_printf("Failed to create texture from temporary surface : %s", SDL_GetError());
|
||||||
|
goto_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DestroySurface(tmp_surface);
|
||||||
|
return texture;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
SDL_DestroySurface(tmp_surface);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_texture(texture_t *texture)
|
||||||
|
{
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef TEXTURE_H
|
||||||
|
#define TEXTURE_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_render.h>
|
||||||
|
|
||||||
|
#define TEXTURE_TRANSPARENT_COLOR 0x0001
|
||||||
|
|
||||||
|
typedef SDL_Texture texture_t;
|
||||||
|
|
||||||
|
texture_t *load_texture(void);
|
||||||
|
void destroy_texture(texture_t *texture);
|
||||||
|
|
||||||
|
#endif // TEXTURE_H
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "vector2d.h"
|
||||||
|
|
||||||
|
void rect_to_frect(const rect_t *rect, frect_t *frect)
|
||||||
|
{
|
||||||
|
frect->x = (float)rect->x;
|
||||||
|
frect->y = (float)rect->y;
|
||||||
|
frect->w = (float)rect->w;
|
||||||
|
frect->h = (float)rect->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frect_to_rect(const frect_t *frect, rect_t *rect)
|
||||||
|
{
|
||||||
|
rect->x = (int)frect->x;
|
||||||
|
rect->y = (int)frect->y;
|
||||||
|
rect->w = (int)frect->w;
|
||||||
|
rect->h = (int)frect->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float lerp(float a, float b, float s)
|
||||||
|
{
|
||||||
|
return a * (1 - s) + b * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float lerp_time(float a, float b, float s, float t)
|
||||||
|
{
|
||||||
|
return lerp(a, b, 1 - powf(1 - s, t * 60.0f));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
#ifndef VECTOR2D_H
|
||||||
|
#define VECTOR2D_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL_rect.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Point struct. (int)
|
||||||
|
*
|
||||||
|
* @param x X pos
|
||||||
|
* @param y Y pos
|
||||||
|
*/
|
||||||
|
typedef SDL_Point vector2d_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Point struct. (float)
|
||||||
|
*
|
||||||
|
* @param x X pos
|
||||||
|
* @param y Y pos
|
||||||
|
*/
|
||||||
|
typedef SDL_FPoint fvector2d_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rectangle struct. (int)
|
||||||
|
*
|
||||||
|
* @param x X pos
|
||||||
|
* @param y Y pos
|
||||||
|
* @param w Width
|
||||||
|
* @param h Height
|
||||||
|
*/
|
||||||
|
typedef SDL_Rect rect_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rectangle struct. (float)
|
||||||
|
*
|
||||||
|
* @param x X pos
|
||||||
|
* @param y Y pos
|
||||||
|
* @param w Width
|
||||||
|
* @param h Height
|
||||||
|
*/
|
||||||
|
typedef SDL_FRect frect_t;
|
||||||
|
|
||||||
|
#define EPSILON 0.000001f
|
||||||
|
|
||||||
|
#define f_is_zero(X) (fabsf(X) <= EPSILON)
|
||||||
|
|
||||||
|
#define f_is_not_zero(X) (fabsf(X) > EPSILON)
|
||||||
|
|
||||||
|
void rect_to_frect(const rect_t *rect, frect_t *frect);
|
||||||
|
|
||||||
|
void frect_to_rect(const frect_t *frect, rect_t *rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if a point is in a rectangle. (int)
|
||||||
|
*
|
||||||
|
* @param P Vector2d
|
||||||
|
* @param R Rectangle
|
||||||
|
*
|
||||||
|
* @return True if the point is in the rectangle, else false.
|
||||||
|
*/
|
||||||
|
#define point_in_rect(P, R) SDL_PointInRect(P, R)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if a point is in a rectangle. (float)
|
||||||
|
*
|
||||||
|
* @param P Vector2d
|
||||||
|
* @param R Rectangle
|
||||||
|
*
|
||||||
|
* @return True if the point is in the rectangle, else false.
|
||||||
|
*/
|
||||||
|
#define fpoint_in_frect(P, R) SDL_PointInRectF(P, R)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if a rectangle is empty
|
||||||
|
*
|
||||||
|
* @param R Rectangle
|
||||||
|
*
|
||||||
|
* @return True if the rectangle is empty, else false.
|
||||||
|
*/
|
||||||
|
#define rect_empty(R) SDL_RectEmpty(R)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if a rectangle is empty
|
||||||
|
*
|
||||||
|
* @param R Rectangle
|
||||||
|
*
|
||||||
|
* @return True if the rectangle is empty, else false.
|
||||||
|
*/
|
||||||
|
#define frect_empty(R) SDL_FRectEmpty(R)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if there is a intersction between two rectangles. (int)
|
||||||
|
*
|
||||||
|
* @param A Rectangle A
|
||||||
|
* @param B Rectangle B
|
||||||
|
*
|
||||||
|
* @return True if there is an intersection, else false.
|
||||||
|
*/
|
||||||
|
#define has_intersection(A, B) SDL_HasRectIntersection(A, B)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if there is a intersction between two rectangles. (float)
|
||||||
|
*
|
||||||
|
* @param A Rectangle A
|
||||||
|
* @param B Rectangle B
|
||||||
|
*
|
||||||
|
* @return True if there is an intersection, else false.
|
||||||
|
*/
|
||||||
|
#define fhas_intersection(A, B) SDL_HasRectIntersectionFloat(A, B)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if there is an intersection between two rectangles and get the intersection rectangle. (int)
|
||||||
|
*
|
||||||
|
* @param A Rectangle A
|
||||||
|
* @param B Rectangle B
|
||||||
|
* @param result The intersection rectangle
|
||||||
|
*
|
||||||
|
* @return True if there is an intersection, else false.
|
||||||
|
*/
|
||||||
|
#define intersect_rect(A, B, result) SDL_GetRectIntersection(A, B, result)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify if there is an intersection between two rectangles and get the intersection rectangle. (float)
|
||||||
|
*
|
||||||
|
* @param A Rectangle A
|
||||||
|
* @param B Rectangle B
|
||||||
|
* @param result The intersection rectangle
|
||||||
|
*
|
||||||
|
* @return True if there is an intersection, else false.
|
||||||
|
*/
|
||||||
|
#define intersect_frect(A, B, result) SDL_GetRectIntersectionFloat(A, B, result)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lerp a to b with smoothing factor s.
|
||||||
|
*
|
||||||
|
* @param a Current value
|
||||||
|
* @param b Target value
|
||||||
|
* @param s Smoothing factor
|
||||||
|
*/
|
||||||
|
float lerp(float a, float b, float s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lerp a to b with smoothing factor s and time basis t.
|
||||||
|
* Smoothing factor is for 60 fps call time.
|
||||||
|
*
|
||||||
|
* @param a Current value
|
||||||
|
* @param b Target value
|
||||||
|
* @param s Smoothing factor
|
||||||
|
* @param t Delta time
|
||||||
|
*/
|
||||||
|
float lerp_time(float a, float b, float s, float t);
|
||||||
|
|
||||||
|
#endif // VECTOR2D_H
|
||||||
|
|
@ -12,6 +12,7 @@ pkgs.mkShell {
|
||||||
libpng
|
libpng
|
||||||
json_c
|
json_c
|
||||||
sdl3
|
sdl3
|
||||||
|
lua
|
||||||
ncurses
|
ncurses
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
gcc
|
gcc
|
||||||
# Headers
|
# Headers
|
||||||
-Isrc/components/headers
|
-Isrc/components
|
||||||
-ISDL3_Core/src/headers
|
-Icore/src
|
||||||
|
|
||||||
# Macros
|
# Macros
|
||||||
-DDEBUG=2
|
-DDEBUG=2
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ SRC_CFLAGS = --std=c17 --pedantic -O1 -g -DDEBUG=2 \
|
||||||
SRC_LDFLAGS = -O1
|
SRC_LDFLAGS = -O1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SRC_INCLUDE_DIRS = $(SOURCE_DIR)/components/headers $(CORE_DIR)/headers
|
SRC_INCLUDE_DIRS = $(SOURCE_DIR)/components $(CORE_DIR)
|
||||||
SRC_INCLUDE_DIRS := $(addprefix -I, $(SRC_INCLUDE_DIRS))
|
SRC_INCLUDE_DIRS := $(addprefix -I, $(SRC_INCLUDE_DIRS))
|
||||||
|
|
||||||
# Deduce objects
|
# Deduce objects
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ COMPONENT(ANIMATION_SYSTEM, animation_system, NO_DRAW)
|
||||||
COMPONENT(PLAYER_SYSTEM, player_system, NO_DRAW)
|
COMPONENT(PLAYER_SYSTEM, player_system, NO_DRAW)
|
||||||
COMPONENT(LEVER_COMPONENT, lever_component, NO_DRAW)
|
COMPONENT(LEVER_COMPONENT, lever_component, NO_DRAW)
|
||||||
COMPONENT(DOOR_COMPONENT, door_component, NO_DRAW)
|
COMPONENT(DOOR_COMPONENT, door_component, NO_DRAW)
|
||||||
COMPONENT(LUA_COMPONENT, lua_component, NO_DRAW)
|
// COMPONENT(LUA_COMPONENT, lua_component, NO_DRAW)
|
||||||
// Add new components definition here
|
// Add new components definition here
|
||||||
|
|
||||||
#undef COMPONENT
|
#undef COMPONENT
|
||||||
Loading…
Reference in New Issue