Compare commits

..

No commits in common. "124e30281ba0df26f9ca96e94ff0eca55d73024d" and "858fe1d38dc021fbbb32fe727bec1327a2781879" have entirely different histories.

76 changed files with 99 additions and 3630 deletions

4
.ccls
View File

@ -1,3 +1 @@
gcc gcc
# Macros
-DDEBUG=2

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
.vscode/ccls-cache .vscode/ccls-cache
config.mk
build build

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "SDL3_Core"]
path = SDL3_Core
url = https://gitea.skadubpc.net/ulysse/2D_Engine_SDL3_Core.git

View File

@ -1,11 +1,5 @@
{ {
"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"
}
} }

View File

@ -1,41 +1,16 @@
SHELL = /usr/bin/env bash SHELL = /usr/bin/env bash
ROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) # Build folder
BUILD_DIR = build
ifneq ($(realpath $(ROOT)/config.mk), $(ROOT)/config.mk) # Source output target name
$(error ERROR: Please run the configure script before building.) SOURCE_OUTPUT = $(BUILD_DIR)/2D_Engine
endif
include $(ROOT)/config.mk # Core output target name
CORE_OUTPUT = $(BUILD_DIR)/libcore.a
# # Assets output
# User config ASSET_OUTPUT = $(BUILD_DIR)/game.data
#
BUILD_DIR_NAME = build
SOURCE_DIR = src
CORE_DIR = core
HOST_DIR = host
OUTPUT = 2D_Engine
ASSET_OUTPUT = game.data
ifneq ($(DEBUG),)
CFLAGS += -O1 -g -DDEBUG=$(DEBUG) -Wextra -Wall --pedantic -Wno-missing-braces \
-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
endif
#
# End
#
# Relocate build dir
BUILD_DIR := $(ROOT)/$(BUILD_DIR_NAME)/$(HOST)
# Verbosity # Verbosity
Q := @ Q := @
@ -49,21 +24,28 @@ YELLOW := \033[33m
RESET := \033[m RESET := \033[m
# Default targets # Default targets
all: setup $(BUILD_DIR)/$(OUTPUT) all: build_core build_src build_assets
include $(ROOT)/$(SOURCE_DIR)/Makefile # Sub Makefiles
include $(ROOT)/$(CORE_DIR)/Makefile SOURCE_DIR = src
CORE_DIR = SDL3_Core/src
ASSETS_DIR = assets
setup: include $(CORE_DIR)/Makefile
$(eval export TOTAL_BUILD_FILES := $(shell echo $$(($$(make -n $(OBJECTS) $(CORE_OBJECTS) 2>/dev/null | grep -c $(CC)) + 2)))) include $(SOURCE_DIR)/Makefile
$(Q)mkdir -p $(dir $(OBJECTS) $(CORE_OBJECTS)) include $(ASSETS_DIR)/Makefile
.PHONY: clean run print-var-% # Clean
.PHONY: clean
clean: clean:
$(Q)rm -rf $(dir $(BUILD_DIR)) $(Q)rm -rf $(BUILD_DIR)
@echo "Build dir cleaned"
# Run executable
.PHONY: run
run: all run: all
$(Q)cd $(BUILD_DIR) && ./$(OUTPUT) $(Q)cd $(BUILD_DIR) && ./$(notdir $(SOURCE_OUTPUT))
print-var-% : .PHONY: print-%
print-% :
@echo $($*) @echo $($*)

View File

@ -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.txt) 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) or [gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).

1
SDL3_Core Submodule

@ -0,0 +1 @@
Subproject commit bc3f9b4726b7a5cb9522bc959f6086095d5f4301

View File

@ -28,8 +28,6 @@
- [ ] Create lua component - [ ] Create lua component
- [ ] Rethink makefile structure
### Done ### Done
- [x] Make camera center on map - [x] Make camera center on map

View File

@ -13,6 +13,7 @@ ASSETS := $(addprefix $(ASSETS_DIR)/,$(ASSETS))
ASSETS_OBJECTS = $(ASSETS:%=$(BUILD_DIR)/%.o) ASSETS_OBJECTS = $(ASSETS:%=$(BUILD_DIR)/%.o)
ASSET_BUILDER_DIR = $(ASSETS_DIR)/asset_builder/src ASSET_BUILDER_DIR = $(ASSETS_DIR)/asset_builder/src
ASSET_BUILDER_OUTPUT = $(BUILD_DIR)/$(ASSETS_DIR)/asset_builder/asset_builder ASSET_BUILDER_OUTPUT = $(BUILD_DIR)/$(ASSETS_DIR)/asset_builder/asset_builder
include $(ASSET_BUILDER_DIR)/Makefile include $(ASSET_BUILDER_DIR)/Makefile

View File

@ -1,7 +1,7 @@
gcc gcc
# Headers # Headers
-Iassets/asset_builder/src/headers -Iassets/asset_builder/src/headers
-Isrc/components -Isrc/components/headers
# Macros # Macros
-DDEBUG=2 -DDEBUG=2

View File

@ -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 = $(SOURCE_DIR)/components $(ASSET_BUILDER_DIR) ASSET_BUILDER_INCLUDE_DIRS = $(ASSET_BUILDER_DIR)/headers $(SOURCE_DIR)/components/headers
ASSET_BUILDER_INCLUDE_DIRS := $(addprefix -I, $(ASSET_BUILDER_INCLUDE_DIRS)) ASSET_BUILDER_INCLUDE_DIRS := $(addprefix -I, $(ASSET_BUILDER_INCLUDE_DIRS))
# Deduce objects # Deduce objects

186
configure vendored
View File

@ -1,186 +0,0 @@
#!/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
#
CONFIG_FILE="config.mk"
#LUA_VERSION="5.5.1"
#LUA_CHECKSUM="1c4b4068d67061f2a2231ad2b5422e77acea1487ea9890f6320af614f4373dce"
#
# Main
#
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_FILE}
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
if [ "${CURRENT_HOST}" != "${TARGET_HOST}" ]; then
CROSS_COMPILE=TRUE
fi
if [ -n "${CROSS_COMPILE}" ]; then
case "${TARGET_HOST}" in
fxcg50)
case "${CURRENT_HOST}" in
linux)
;;
*)
print_error "Cross compiling from ${CURRENT_HOST} for ${TARGET_HOST} is not supported yet."
;;
esac
CC="sh-elf-gcc"
AR="sh-elf-ar"
LIBCORE="libcore.a"
;;
esac
else
case "${TARGET_HOST}" in
linux)
CC="gcc"
AR="ar"
CFLAGS="--std=c17 -Os -DASSET_FILE_NAME=\\\"\$(notdir \$(ASSET_OUTPUT))\\\" -D_GNU_SOURCE=1 \$(shell pkg-config --cflags sdl3)"
LDFLAGS="\$(shell pkg-config --libs sdl3) -lm"
LIBCORE="libcore.a"
;;
*)
;;
esac
fi
echo "Writing config file."
echo
echo "${CONFIG_FILE}:"
echo "\`\`\`"
echo "HOST = ${TARGET_HOST}" | tee ${CONFIG_FILE}
echo | tee -a ${CONFIG_FILE}
echo "CC = ${CC}" | tee -a ${CONFIG_FILE}
echo "AR = ${AR}" | tee -a ${CONFIG_FILE}
echo "CFLAGS = ${CFLAGS}" | tee -a ${CONFIG_FILE}
echo "LDFLAGS = ${LDFLAGS}" | tee -a ${CONFIG_FILE}
echo | tee -a ${CONFIG_FILE}
echo "LIBCORE = ${LIBCORE}" | tee -a ${CONFIG_FILE}
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
#

View File

@ -1,8 +0,0 @@
gcc
# Headers
-Icore
-Icore/host/generic
-Isrc/components
# Macros
-DASSET_FILE_NAME="$(make print-var-ASSET_OUTPUT)"

View File

@ -1,31 +0,0 @@
CORE_SOURCES += \
main.c \
camera.c \
linked_list.c \
event_bus.c \
ecs.c \
asset_manager.c \
inputs.c \
map.c
CORE_INCLUDES += \
$(CORE_DIR) \
$(CORE_DIR)/$(HOST_DIR)/$(HOST) \
$(SOURCE_DIR)/components
include $(ROOT)/$(CORE_DIR)/$(HOST_DIR)/$(HOST)/Makefile
CORE_OBJECTS := $(CORE_SOURCES:%=$(BUILD_DIR)/$(CORE_DIR)/%.o)
$(BUILD_DIR)/$(LIBCORE): $(CORE_OBJECTS)
$(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1))))
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES)))))
@echo -e "[$(PERCENTAGE)%] $(YELLOW)Creating Static Lib $@$(RESET)"
$(Q)$(AR) rcs $@ $(CORE_OBJECTS)
$(BUILD_DIR)/$(CORE_DIR)/%.c.o: $(ROOT)/$(CORE_DIR)/%.c
$(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1))))
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES)))))
@echo -e "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)"
$(Q)$(CC) $(CFLAGS) $(CORE_INCLUDES:%=-I%) -MMD -MP -c $< -o $@
-include $(CORE_OBJECTS:.o=.d)

View File

@ -1,9 +0,0 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](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).

View File

@ -1,237 +0,0 @@
#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(game.asset_manager.asset_file)
{
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;
}

View File

@ -1,48 +0,0 @@
#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

View File

@ -1,64 +0,0 @@
#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;
}
}
}
}

View File

@ -1,16 +0,0 @@
#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

View File

@ -1,222 +0,0 @@
#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;
}

View File

@ -1,71 +0,0 @@
#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

View File

@ -1,69 +0,0 @@
#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 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)
#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 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: " __VA_ARGS__); \
fputc('\n', stderr); \
} while (0)
#define return_failure_int return EXIT_FAILURE;
#define return_failure_ptr return NULL;
#define goto_failure goto failure;
#endif
#endif // ERRORS_H

View File

@ -1,140 +0,0 @@
#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;
}

View File

@ -1,38 +0,0 @@
#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

View File

@ -1,45 +0,0 @@
#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

View File

@ -1,163 +0,0 @@
#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 bool point_in_rect(const vector2d_t *p, const rect_t *r)
{
return (p && r && (p->x >= r->x) && (p->x < (r->x + r->w)) && (p->y >= r->y) && (p->y < (r->y + r->h)));
}
inline bool fpoint_in_rect(const fvector2d_t *p, const frect_t *r)
{
return (p && r && (p->x >= r->x) && (p->x < (r->x + r->w)) && (p->y >= r->y) && (p->y < (r->y + r->h)));
}
inline bool rect_empty(const rect_t *r)
{
return ((!r) || (r->w <= 0) || (r->h <= 0));
}
inline bool frect_empty(const frect_t *r)
{
return ((!r) || (r->w <= 0) || (r->h <= 0));
}
inline bool has_intersection(const rect_t *a, const rect_t *b)
{
int amin, amax, bmin, bmax;
// Horizontal intersection
amin = a->x;
amax = amin + a->w;
bmin = b->x;
bmax = bmin + b->w;
if(bmin > amin) amin = bmin;
if(bmax < amax) amax = bmax;
if((amax - EPSILON) < amin) return false;
// Vertical intersection
amin = a->y;
amax = amin + a->h;
bmin = b->y;
bmax = bmin + b->h;
if(bmin > amin) amin = bmin;
if(bmax < amax) amax = bmax;
if((amax - EPSILON) < amin) return false;
return true;
}
inline bool fhas_intersection(const frect_t *a, const frect_t *b)
{
float amin, amax, bmin, bmax;
// Horizontal intersection
amin = a->x;
amax = amin + a->w;
bmin = b->x;
bmax = bmin + b->w;
if(bmin > amin) amin = bmin;
if(bmax < amax) amax = bmax;
if((amax - EPSILON) < amin) return false;
// Vertical intersection
amin = a->y;
amax = amin + a->h;
bmin = b->y;
bmax = bmin + b->h;
if(bmin > amin) amin = bmin;
if(bmax < amax) amax = bmax;
if((amax - EPSILON) < amin) return false;
return true;
}
inline bool intersect_rect(const rect_t *a, const rect_t *b, rect_t *result)
{
int amin, amax, bmin, bmax;
// Horizontal intersection
amin = a->x;
amax = amin + a->w;
bmin = a->x;
bmax = bmin + a->w;
if (bmin > amin) amin = bmin;
result->x = amin;
if (bmax < amax) amax = bmax;
result->w = amax - amin;
// Vertical intersection
amin = a->y;
amax = amin + a->h;
bmin = b->y;
bmax = bmin + b->h;
if (bmin > amin) amin = bmin;
result->y = amin;
if (bmax < amax) amax = bmax;
result->h = amax - amin;
return !rect_empty(result);
}
inline bool intersect_frect(const frect_t *a, const frect_t *b, frect_t *result)
{
float Amin, Amax, Bmin, Bmax;
// Horizontal intersection
Amin = a->x;
Amax = Amin + a->w;
Bmin = a->x;
Bmax = Bmin + a->w;
if (Bmin > Amin) {
Amin = Bmin;
}
result->x = Amin;
if (Bmax < Amax) {
Amax = Bmax;
}
result->w = Amax - Amin;
// Vertical intersection
Amin = a->y;
Amax = Amin + a->h;
Bmin = b->y;
Bmax = Bmin + b->h;
if (Bmin > Amin) {
Amin = Bmin;
}
result->y = Amin;
if (Bmax < Amax) {
Amax = Bmax;
}
result->h = Amax - Amin;
return !frect_empty(result);
}
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));
}

View File

@ -1,163 +0,0 @@
#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 struct vector2d_t {
int x, y;
} vector2d_t;
/**
* @brief Point struct. (float)
*
* @param x X pos
* @param y Y pos
*/
typedef struct fvector2d_t {
float x, y;
} fvector2d_t;
/**
* @brief Rectangle struct. (int)
*
* @param x X pos
* @param y Y pos
* @param w Width
* @param h Height
*/
typedef struct rect_t {
int x, y;
int w, h;
} rect_t;
/**
* @brief Rectangle struct. (float)
*
* @param x X pos
* @param y Y pos
* @param w Width
* @param h Height
*/
typedef struct frect_t {
float x, y;
float w, h;
} frect_t;
#define EPSILON 0.0f
#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.
*/
bool point_in_rect(const vector2d_t *p, const rect_t *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.
*/
bool fpoint_in_rect(const fvector2d_t *p, const frect_t *r);
/**
* @brief Verify if a rectangle is empty
*
* @param r Rectangle
*
* @return True if the rectangle is empty, else false.
*/
bool rect_empty(const rect_t *r);
/**
* @brief Verify if a rectangle is empty
*
* @param r Rectangle
*
* @return True if the rectangle is empty, else false.
*/
bool frect_empty(const frect_t *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.
*/
bool has_intersection(const rect_t *a, const rect_t *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.
*/
bool fhas_intersection(const frect_t *a, const frect_t *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.
*/
bool intersect_rect(const rect_t *a, const rect_t *b, rect_t *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.
*/
bool intersect_frect(const frect_t *a, const frect_t *b, frect_t *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

View File

@ -1,31 +0,0 @@
#ifndef DISPLAY_H
#define DISPLAY_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 {
} 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

View File

@ -1,6 +0,0 @@
#ifndef MEMORY_ALLOC_H
#define MEMORY_ALLOC_H
#include <stdlib.h>
#endif // MEMORY_ALLOC_H

View File

@ -1,14 +0,0 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#define TEXTURE_TRANSPARENT_COLOR 0x0001
typedef struct texture_t texture_t;
#define get_width(T) 1
#define get_height(T) 1
texture_t *load_texture(void);
void destroy_texture(texture_t *texture);
#endif // TEXTURE_H

View File

@ -1,163 +0,0 @@
#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 struct vector2d_t {
int x, y;
} vector2d_t;
/**
* @brief Point struct. (float)
*
* @param x X pos
* @param y Y pos
*/
typedef struct fvector2d_t {
float x, y;
} fvector2d_t;
/**
* @brief Rectangle struct. (int)
*
* @param x X pos
* @param y Y pos
* @param w Width
* @param h Height
*/
typedef struct rect_t {
int x, y;
int w, h;
} rect_t;
/**
* @brief Rectangle struct. (float)
*
* @param x X pos
* @param y Y pos
* @param w Width
* @param h Height
*/
typedef struct frect_t {
float x, y;
float w, h;
} frect_t;
#define EPSILON 0.0f
#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.
*/
bool point_in_rect(const vector2d_t *p, const rect_t *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.
*/
bool fpoint_in_rect(const fvector2d_t *p, const frect_t *r);
/**
* @brief Verify if a rectangle is empty
*
* @param r Rectangle
*
* @return True if the rectangle is empty, else false.
*/
bool rect_empty(const rect_t *r);
/**
* @brief Verify if a rectangle is empty
*
* @param r Rectangle
*
* @return True if the rectangle is empty, else false.
*/
bool frect_empty(const frect_t *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.
*/
bool has_intersection(const rect_t *a, const rect_t *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.
*/
bool fhas_intersection(const frect_t *a, const frect_t *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.
*/
bool intersect_rect(const rect_t *a, const rect_t *b, rect_t *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.
*/
bool intersect_frect(const frect_t *a, const frect_t *b, frect_t *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

View File

@ -1,5 +0,0 @@
gcc
# Headers
-Icore
-Icore/host/linux
-Isrc/components

View File

@ -1,7 +0,0 @@
CORE_SOURCES += \
$(HOST_DIR)/$(HOST)/display.c \
$(HOST_DIR)/$(HOST)/vector2d.c \
$(HOST_DIR)/$(HOST)/texture.c
CORE_INCLUDES += \
$(CORE_DIRE)

View File

@ -1,188 +0,0 @@
#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;
}

View File

@ -1,34 +0,0 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <SDL3/SDL_render.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

View File

@ -1,6 +0,0 @@
#ifndef MEMORY_ALLOC_H
#define MEMORY_ALLOC_H
#include <stdlib.h>
#endif // MEMORY_ALLOC_H

View File

@ -1,67 +0,0 @@
#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);
}

View File

@ -1,16 +0,0 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL3/SDL_render.h>
#define TEXTURE_TRANSPARENT_COLOR 0x0001
typedef SDL_Texture texture_t;
#define get_width(T) T->w
#define get_height(T) T->h
texture_t *load_texture(void);
void destroy_texture(texture_t *texture);
#endif // TEXTURE_H

View File

@ -1,19 +0,0 @@
#include "vector2d.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));
}

View File

@ -1,153 +0,0 @@
#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.0f
#define f_is_zero(X) (fabsf(X) <= EPSILON)
#define f_is_not_zero(X) (fabsf(X) > EPSILON)
#define rect_to_frect(R, FR) SDL_RectToFRect(R, FR)
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

View File

@ -1,14 +0,0 @@
#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;
}

View File

@ -1,22 +0,0 @@
#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

View File

@ -1,15 +0,0 @@
#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

View File

@ -1,429 +0,0 @@
#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;
}
}

View File

@ -1,283 +0,0 @@
#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

View File

@ -1,38 +0,0 @@
#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;
}

View File

@ -1,485 +0,0 @@
#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 % get_width(game.map->tileset),
.y = tile_id * TILE_SIZE / get_height(game.map->tileset) * 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;
}

View File

@ -1,44 +0,0 @@
#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

View File

@ -12,7 +12,6 @@ pkgs.mkShell {
libpng libpng
json_c json_c
sdl3 sdl3
lua
ncurses ncurses
]; ];

View File

@ -1,5 +1,7 @@
gcc gcc
# Headers # Headers
-Isrc/components -Isrc/components/headers
-Icore -ISDL3_Core/src/headers
-Icore/host/generic
# Macros
-DDEBUG=2

View File

@ -1,4 +1,5 @@
SOURCES += \ # Source files
SOURCES := \
game.c \ game.c \
components/transform_component.c \ components/transform_component.c \
components/sprite_component.c \ components/sprite_component.c \
@ -11,21 +12,61 @@ SOURCES += \
components/door_component.c \ components/door_component.c \
components/lua_component.c components/lua_component.c
INCLUDES += \ # Compiler and flags
$(SOURCE_DIR)/components \ SRC_CC = gcc
$(CORE_DIR) \ ifeq ($(RELEASE), 1)
$(CORE_DIR)/$(HOST_DIR)/$(HOST) SRC_CFLAGS = --std=c17 --pedantic -O3 # RELEASE
OBJECTS := $(SOURCES:%=$(BUILD_DIR)/$(SOURCE_DIR)/%.o) SRC_LDFLAGS = -O3
else
SRC_CFLAGS = --std=c17 --pedantic -O1 -g -DDEBUG=2 \
-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
$(BUILD_DIR)/$(OUTPUT): $(BUILD_DIR)/$(LIBCORE) $(OBJECTS) SRC_LDFLAGS = -O1
@echo -e "[100%] $(YELLOW)Linking $(OUTPUT)$(RESET)" endif
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) $(BUILD_DIR)/$(LIBCORE) -o $@
$(BUILD_DIR)/$(SOURCE_DIR)/%.c.o: $(ROOT)/$(SOURCE_DIR)/%.c SRC_INCLUDE_DIRS = $(SOURCE_DIR)/components/headers $(CORE_DIR)/headers
$(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1)))) SRC_INCLUDE_DIRS := $(addprefix -I, $(SRC_INCLUDE_DIRS))
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES)))))
# Deduce objects
SOURCE_OBJECTS = $(SOURCES:%.c=$(BUILD_DIR)/$(SOURCE_DIR)/%.o)
# Current file nb to process
CURRENT_SRC_FILE := 0
# Build src target
build_src: src_setup count_src_build src_end
# Source build directories
src_setup:
@echo "Building $(notdir $(SOURCE_OUTPUT))"
$(Q)mkdir -p $(dir $(SOURCE_OBJECTS))
# Count nb of files to build
count_src_build:
$(eval export TOTAL_SRC_FILES := $(shell echo $$(($$(make -n $(SOURCE_OBJECTS) 2>/dev/null | grep -c "Building") + 1))))
# Link game target
$(SOURCE_OUTPUT): $(SOURCE_OBJECTS) $(CORE_OUTPUT)
@echo -e "[100%] $(YELLOW)Linking $(notdir $(SOURCE_OUTPUT))$(RESET)"
$(Q)$(SRC_CC) $(SOURCE_OBJECTS) -L./$(BUILD_DIR) $(CORE_OUTPUT) -o $@ $(SRC_LDFLAGS) $(CORE_DEPS_FLAGS)
# Build .o files
$(BUILD_DIR)/$(SOURCE_DIR)/%.o: $(SOURCE_DIR)/%.c
$(eval CURRENT_SRC_FILE := $(shell echo $$(($(CURRENT_SRC_FILE)+1))))
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_SRC_FILE)*100/$(TOTAL_SRC_FILES)))))
@echo -e "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" @echo -e "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)"
$(Q)$(CC) $(CFLAGS) $(INCLUDES:%=-I%) -MMD -MP -c $< -o $@ $(Q)$(SRC_CC) $(SRC_CFLAGS) $(SRC_INCLUDE_DIRS) -MMD -MP -c $< -o $@
-include $(OBJECTS:.o=.d) # Print ending message
src_end: $(SOURCE_OUTPUT)
@echo "Built target $(notdir $(SOURCE_OUTPUT))"
# Source files dependencies
-include $(SOURCE_OBJECTS:.o=.d)

View File

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

View File

@ -96,11 +96,11 @@ static inline int action_detect_solve_entities_collision(elem_t *elem, void *arg
const frect_t *hitbox_bounds = &hitbox_component_data->bounds; const frect_t *hitbox_bounds = &hitbox_component_data->bounds;
const frect_t *target_hitbox_bounds = &target_hitbox_component_data->bounds; const frect_t *target_hitbox_bounds = &target_hitbox_component_data->bounds;
frect_t intersect; frect_t intersect_frect;
if(intersect_frect(hitbox_bounds, target_hitbox_bounds, &intersect)) if(intersect_frect(hitbox_bounds, target_hitbox_bounds, &intersect_frect))
{ {
if(intersect.w < intersect.h) if(intersect_frect.w < intersect_frect.h)
{ {
const float left_correction = hitbox_bounds->x + hitbox_bounds->w - target_hitbox_bounds->x; const float left_correction = hitbox_bounds->x + hitbox_bounds->w - target_hitbox_bounds->x;
const float right_correction = target_hitbox_bounds->x + target_hitbox_bounds->w - hitbox_bounds->x; const float right_correction = target_hitbox_bounds->x + target_hitbox_bounds->w - hitbox_bounds->x;