Compare commits

..

No commits in common. "main" and "level_select" have entirely different histories.

56 changed files with 1604 additions and 3808 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@ release/
web/
compile_commands.json
.gdb_history
heaptrack.*

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "tracy"]
path = tracy
url = https://github.com/wolfpld/tracy.git

View File

@ -2,23 +2,15 @@ set(PROJECT_NAME HATPC_remake)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_FLAGS "-Wall -Wextra")
cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(${PROJECT_NAME} C CXX)
project(${PROJECT_NAME} C)
set(CMAKE_C_STANDARD 99)
set(RAYLIB_DIR /usr/local/lib CACHE FILEPATH "directory to Raylib")
set(LIBZSTD_DIR /usr/local/lib CACHE FILEPATH "directory to zstd")
option(RUN_PROFILER OFF)
option(INCLUDE_ASAN ON)
option(EXPORT_MMAP OFF)
option(BUILD_EXTRAS OFF)
# If you want to use Heaptrack to profile the memory
# Do not compile in ASAN
if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPLATFORM_WEB")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2 -s TOTAL_MEMORY=16777216 -s TOTAL_STACK=1048576 --preload-file ./res ")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s TOTAL_MEMORY=16777216 -s TOTAL_STACK=1048576 --preload-file ./res ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
@ -32,69 +24,162 @@ set(GAME_LIBS
lib_scenes
)
if (${RUN_PROFILER})
set(GAME_LIBS
lib_scenes
pthread
dl
)
endif()
add_subdirectory(engine)
add_subdirectory(scenes)
if (NOT EMSCRIPTEN)
add_subdirectory(res)
endif ()
macro(add_target_exe name)
add_executable(${name}
${name}.c
tracy/public/TracyClient.cpp
add_executable(${PROJECT_NAME}
main.c
)
target_include_directories(${name}
target_include_directories(${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
PUBLIC
tracy/public/
)
if (RUN_PROFILER)
target_compile_definitions(${name}
PUBLIC
TRACY_ENABLE
TRACY_ON_DEMAND
)
endif()
if (NOT EMSCRIPTEN)
if (INCLUDE_ASAN)
target_compile_options(${name} PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(${name} PRIVATE -fsanitize=address -gdwarf-4)
endif ()
if (EXPORT_MMAP)
target_link_options(${name} PRIVATE -Xlinker -Map=scene_test.map)
endif()
endif ()
target_link_libraries(${name}
PUBLIC
target_link_libraries(${PROJECT_NAME}
${GAME_LIBS}
)
endmacro()
add_executable(scene_test
scene_test.c
)
add_target_exe(main)
add_target_exe(level_test)
add_target_exe(scene_test)
target_include_directories(scene_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
if (NOT EMSCRIPTEN)
if (BUILD_EXTRAS AND NOT RUN_PROFILER)
add_target_exe(entManager_test)
add_target_exe(water_test)
add_target_exe(level_load_test)
add_target_exe(menu_test)
add_target_exe(assets_test)
add_target_exe(particle_test)
add_target_exe(scene_man_test)
add_target_exe(level_select_test)
target_compile_options(scene_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(scene_test PRIVATE -fsanitize=address -gdwarf-4)
endif ()
target_link_libraries(scene_test
${GAME_LIBS}
)
if (NOT EMSCRIPTEN)
add_executable(EntManager_test
entManager_test.c
)
target_compile_options(EntManager_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(EntManager_test PRIVATE -fsanitize=address -gdwarf-4)
target_include_directories(EntManager_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(EntManager_test
${GAME_LIBS}
)
add_executable(scene_test_mem
scene_test.c
)
target_include_directories(scene_test_mem
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_options(scene_test_mem PRIVATE -Xlinker -Map=scene_test.map)
target_link_libraries(scene_test_mem
${GAME_LIBS}
)
add_executable(water_test
water_test.c
)
target_include_directories(water_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(water_test
${GAME_LIBS}
)
target_compile_options(water_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(water_test PRIVATE -fsanitize=address -gdwarf-4)
add_executable(level_load_test
level_load_test.c
)
target_include_directories(level_load_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(level_load_test
${GAME_LIBS}
)
target_compile_options(level_load_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(level_load_test PRIVATE -fsanitize=address -gdwarf-4)
add_executable(menu_test
menu_test.c
)
target_include_directories(menu_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(menu_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(menu_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(menu_test
${GAME_LIBS}
)
add_executable(assets_test
assets_test.c
)
target_include_directories(assets_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(assets_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(assets_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(assets_test
${GAME_LIBS}
)
add_executable(particle_test
particle_test.c
)
target_include_directories(particle_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(particle_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(particle_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(particle_test
${GAME_LIBS}
)
add_executable(scene_man_test
scene_man_test.c
)
target_include_directories(scene_man_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(scene_man_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(scene_man_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(scene_man_test
${GAME_LIBS}
)
add_executable(level_select_test
level_select_test.c
)
target_include_directories(level_select_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(level_select_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(level_select_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(level_select_test
${GAME_LIBS}
)
if (BUILD_TESTING)
find_package(cmocka 1.1.0 REQUIRED)
add_subdirectory(tests)

View File

@ -11,13 +11,13 @@ The goal of the project is to make a game similar to Hannah and The Pirate Caves
## Implementation Notes
As mentioned, this is mostly a learning experience, so certain things are implemented for the sake of learning. However, there are certain things that I didn't bother with because dealing with them will open up another can of worms. Either that or I find it too large of a feature to implement on my own without hating my existance. For these, I will use libraries.
As this point of time (24/08/2024), these are the things I won't bother for now:
As this point of time (23/11/2023), these are the things I won't bother for now:
- Game rendering: I remember dealing with OpenGL for a little bit and it being a not-so-pleasant experience. This is also a large topic on its own too.
- Camera System: This is an interesting one. I would really like to implement this. However, this is sort of tied to game rendering, so no.
- Windows and raw input handling: Not keen on this.
- GUI: I'm not about to roll my own GUI library for this. I'll do a lite version, but that's about it.
- GUI: I'm not about to roll my own GUI library for this.
- Data structures: Will only do it for specific reasons. Otherwise, use a library.
- Level editor: ... maybe. I already have a sandbox, so maybe I can turn that into an editor lite??? Won't think too much about it for now.
- Level editor: ... no. This is more towards GUI design which I'm not currently keen on.
Libraries/Tools used:
- _raylib_ \[[link](https://github.com/raysan5/raylib)\] + _raygui_ \[[link](https://github.com/raysan5/raygui)\]: MVP of this project. Basically the backbone of the game. I've use it for some past projects and it's always a pleasant experience. Can recommend!
@ -29,16 +29,14 @@ Libraries/Tools used:
- _LDtk_ \[[link](https://ldtk.io/)\]: A nice level editor. I haven't use it to its fullest extent, but definitely having a good experience so far.
- _Aseprite_ \[[link](https://www.aseprite.org/)\]: Used it to create sprites. Good tool!
- _Emscripten_ \[[link](https://emscripten.org/)\]: For web build. It's really a marvel of technology!
- _heaptrack_ \[[link](https://github.com/KDE/heaptrack)\]: For heap profiling. Simple and straightforward to use.
## Progress
The engine features:
- An Entity-Component framework with an Entity Manager + memory pool that is specific for this project
- AABB collision system + Grid-based Broad phase collision detection
- Scene tree management and transition
- Scene management and transition
- Assets management and sprite transition
- Simple level loading
- Simple Particle effects management
Current progress:
- Simple main menu + sandbox scene
@ -49,6 +47,7 @@ Current progress:
- Chest and Level Ending
- Arrows and dynamites
- Water filling
- Simple Particle Effects
- Sound Effects
- Simple Camera Update System
- Demo level pack loading
@ -69,12 +68,5 @@ You may also turn off `BUILD_TESTING` to avoid building the tests.
There are also other binaries generated for testing purposes. Feel free to try them out if you manage to build them.
## Debugging && Profiling
All binaries except the _main_ one are built with ASAN, which helps to detect memory leakage.
_Heaptrack_ is used to do so for the main program to keep for memory leakage + heap usage. From my experience, it doesn't work with ASAN.
I'm looking for runtime profiler. Current candidates are: _Orbit_ and _Tracy_. For small-ish gameplay, the program still runs fine.
## Note on assets
This repository will not contain the assets used in the game, such as fonts, art, and sfx. However, the program should still run without those. Assets are placed in the _res/_ directory.

View File

@ -1,43 +1,34 @@
add_subdirectory(sc)
add_library(lib_assets STATIC
add_library(lib_engine STATIC
assets.c
AABB.c
gui.c
engine.c
collisions.c
rres.c
mempool.c
entManager.c
particle_sys.c
)
target_include_directories(lib_assets
target_include_directories(lib_engine
PRIVATE
${LIBZSTD_DIR}/include
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${RAYLIB_DIR}/include
)
target_link_directories(lib_assets
target_link_directories(lib_engine
PUBLIC
${RAYLIB_DIR}/lib
${LIBZSTD_DIR}/lib
)
target_link_libraries(lib_assets
target_link_libraries(lib_engine
PUBLIC
zstd
raylib
sc_queue
sc_map
m
)
add_library(lib_engine OBJECT
AABB.c
gui.c
engine.c
collisions.c
mempool.c
entManager.c
render_queue.c
)
target_link_libraries(lib_engine
PUBLIC
lib_assets
sc_heap
sc_map
sc_array
m
)

View File

@ -11,39 +11,36 @@
typedef struct EntityManager EntityManager_t;
typedef struct Entity Entity_t;
typedef enum AnchorPoint {
AP_TOP_LEFT,
AP_TOP_CENTER,
AP_TOP_RIGHT,
AP_MID_LEFT,
AP_MID_CENTER,
AP_MID_RIGHT,
AP_BOT_LEFT,
AP_BOT_CENTER,
AP_BOT_RIGHT,
} AnchorPoint_t;
typedef enum ComponentEnum {
CBBOX_COMP_T = 0,
CTRANSFORM_COMP_T,
CTILECOORD_COMP_T,
CMOVEMENTSTATE_T,
CJUMP_COMP_T,
CPLAYERSTATE_T,
CCONTAINER_T,
CHITBOXES_T,
CHURTBOX_T,
CSPRITE_T,
CMOVEABLE_T,
CLIFETIMER_T,
CWATERRUNNER_T,
CAIRTIMER_T,
CEMITTER_T,
} ComponentEnum_t;
typedef enum MovementMode {
REGULAR_MOVEMENT = 0,
KINEMATIC_MOVEMENT,
}MovementMode_t;
/** Fundamental components:
// - Transform
// - BBox
// - TileCoords
**/
/** The component enums are purely a index store. Here, there are 3 basic component, that is necessary for the engine to function (mostly collisions)
* To extend the component list, define another set of enums as pure integer store and begin the enum at N_BASIC_COMPS
* These integers are also used by the component mempool as indices. Thus, the mempool must have the first 3 components as the basic components
**/
#define N_BASIC_COMPS 3
enum BasicComponentEnum {
CBBOX_COMP_T = 0,
CTRANSFORM_COMP_T,
CTILECOORD_COMP_T,
};
typedef struct _CBBox_t {
Vector2 size;
Vector2 offset;
Vector2 half_size;
bool solid;
bool fragile;
} CBBox_t;
typedef struct _CTransform_t {
Vector2 prev_position;
@ -54,26 +51,17 @@ typedef struct _CTransform_t {
Vector2 shape_factor;
float grav_delay;
float grav_timer;
float bounce_coeff;
MovementMode_t movement_mode;
bool active;
} CTransform_t;
typedef struct _CBBox_t {
Vector2 size;
Vector2 offset;
Vector2 half_size;
bool solid;
bool fragile;
} CBBox_t;
typedef struct _CMovementState_t {
uint8_t ground_state;
uint8_t water_state;
uint8_t x_dir;
float water_overlap;
} CMovementState_t;
static inline void set_bbox(CBBox_t* p_bbox, unsigned int x, unsigned int y)
{
p_bbox->size.x = x;
p_bbox->size.y = y;
p_bbox->half_size.x = (unsigned int)(x / 2);
p_bbox->half_size.y = (unsigned int)(y / 2);
}
// This is to store the occupying tiles
// Limits to store 4 tiles at a tile,
// Thus the size of the entity cannot be larger than the tile
@ -82,7 +70,174 @@ typedef struct _CTileCoord_t {
unsigned int n_tiles;
} CTileCoord_t;
typedef struct _CJump_t {
int jump_speed;
uint8_t jumps;
uint8_t max_jumps;
uint8_t coyote_timer;
bool jumped;
bool jump_ready;
bool short_hop;
bool jump_released;
} CJump_t;
typedef enum PlayerState {
GROUNDED,
AIR,
} PlayerState_t;
typedef struct _CPlayerState_t {
Vector2 player_dir;
uint8_t jump_pressed;
uint8_t is_crouch;
bool ladder_state;
} CPlayerState_t;
typedef enum ContainerItem {
CONTAINER_EMPTY,
CONTAINER_LEFT_ARROW,
CONTAINER_RIGHT_ARROW,
CONTAINER_UP_ARROW,
CONTAINER_DOWN_ARROW,
CONTAINER_COIN,
CONTAINER_BOMB,
CONTAINER_EXPLOSION,
} ContainerItem_t;
typedef enum ContainerMaterial {
WOODEN_CONTAINER,
METAL_CONTAINER,
} ContainerMaterial_t;
typedef struct _CContainer_t {
ContainerMaterial_t material;
ContainerItem_t item;
} CContainer_t;
typedef struct _CHitBoxes_t {
Rectangle boxes[2];
uint8_t n_boxes;
uint8_t atk;
bool one_hit;
} CHitBoxes_t;
typedef struct _CHurtbox_t {
Vector2 offset;
Vector2 size;
uint8_t def;
unsigned int damage_src;
} CHurtbox_t;
typedef struct _CLifeTimer_t {
float life_time;
} CLifeTimer_t;
typedef struct _CAirTimer_t {
float max_ftimer;
float curr_ftimer;
float decay_rate;
uint8_t max_count;
uint8_t curr_count;
} CAirTimer_t;
typedef struct _BFSTile {
int32_t to;
int32_t from;
bool reachable;
}BFSTile_t;
typedef struct _BFSTileMap {
BFSTile_t* tilemap;
int32_t width;
int32_t height;
int32_t len;
}BFSTileMap_t;
typedef enum _WaterRunnerState
{
BFS_RESET = 0,
BFS_START,
LOWEST_POINT_SEARCH,
LOWEST_POINT_MOVEMENT,
REACHABILITY_SEARCH,
SCANLINE_FILL,
FILL_COMPLETE,
}WaterRunerState_t;
typedef struct _CWaterRunner {
BFSTileMap_t bfs_tilemap;
WaterRunerState_t state;
struct sc_queue_32 bfs_queue;
bool* visited;
int32_t current_tile;
int32_t target_tile;
int32_t fill_idx;
int16_t fill_range[2];
uint8_t movement_delay;
int8_t movement_speed;
int16_t counter;
float fractional;
}CWaterRunner_t;
// Credits to bedroomcoders.co.uk for this
typedef struct Sprite {
Texture2D* texture;
Vector2 frame_size;
Vector2 origin; // TL of the frame
Vector2 anchor; // Where transformation anchors on
uint8_t frame_per_row;
int frame_count;
int speed;
char* name;
} Sprite_t;
typedef unsigned int (*sprite_transition_func_t)(Entity_t *ent); // Transition requires knowledge of the entity
typedef struct _SpriteRenderInfo
{
Sprite_t* sprite;
Vector2 offset;
} SpriteRenderInfo_t;
typedef struct _CSprite_t {
SpriteRenderInfo_t* sprites;
sprite_transition_func_t transition_func;
unsigned int current_idx;
bool flip_x;
bool flip_y;
bool pause;
int current_frame;
float fractional;
float rotation; // Degree
float rotation_speed; // Degree / s
int elapsed;
Vector2 offset;
Color colour;
} CSprite_t;
typedef struct _CMoveable_t {
uint16_t move_speed;
Vector2 prev_pos;
Vector2 target_pos;
bool gridmove;
} CMoveable_t;
typedef uint16_t EmitterHandle;
typedef struct _CEmitter_t
{
EmitterHandle handle;
Vector2 offset;
} CEmitter_t;
static inline void set_bbox(CBBox_t* p_bbox, unsigned int x, unsigned int y)
{
p_bbox->size.x = x;
p_bbox->size.y = y;
p_bbox->half_size.x = (unsigned int)(x / 2);
p_bbox->half_size.y = (unsigned int)(y / 2);
}
struct Entity {
Vector2 spawn_pos;
Vector2 position;
unsigned long m_id;
unsigned int m_tag;
@ -101,7 +256,7 @@ enum EntityUpdateEvent
struct EntityUpdateEventInfo
{
unsigned long e_id;
unsigned int comp_type;
ComponentEnum_t comp_type;
unsigned long c_id;
enum EntityUpdateEvent evt_type;
};
@ -129,8 +284,8 @@ Entity_t* add_entity(EntityManager_t* p_manager, unsigned int tag);
void remove_entity(EntityManager_t* p_manager, unsigned long id);
Entity_t *get_entity(EntityManager_t* p_manager, unsigned long id);
void* add_component(Entity_t *entity, unsigned int comp_type);
void* get_component(Entity_t *entity, unsigned int comp_type);
void remove_component(Entity_t* entity, unsigned int comp_type);
void* add_component(Entity_t *entity, ComponentEnum_t comp_type);
void* get_component(Entity_t *entity, ComponentEnum_t comp_type);
void remove_component(Entity_t* entity, ComponentEnum_t comp_type);
#endif // __ENTITY_H

View File

@ -23,6 +23,5 @@ typedef enum ActionType
ACTION_SPAWN_TILE,
ACTION_REMOVE_TILE,
ACTION_SWITCH_TILESET,
ACTION_LOOKAHEAD,
}ActionType_t;
#endif // __ACTIONS_H

View File

@ -1,7 +1,6 @@
#include "assets.h"
#include "assert.h"
#include "engine_conf.h"
#include "raymath.h"
#define RRES_RAYLIB_IMPLEMENTATION
#include "rres.h"
@ -275,7 +274,7 @@ static LevelPack_t* add_level_pack_zst(Assets_t* assets, const char* name, const
for (lvls = 0; lvls < n_levels; ++lvls)
{
printf("Parsing level %u\n", lvls);
output.size = 40;
output.size = 36;
output.pos = 0;
do
@ -310,8 +309,6 @@ static LevelPack_t* add_level_pack_zst(Assets_t* assets, const char* name, const
memcpy(pack_info->pack.levels[lvls].level_name, level_decompressor.out_buffer, 32);
memcpy(&pack_info->pack.levels[lvls].width, level_decompressor.out_buffer + 32, 2);
memcpy(&pack_info->pack.levels[lvls].height, level_decompressor.out_buffer + 34, 2);
memcpy(&pack_info->pack.levels[lvls].n_chests, level_decompressor.out_buffer + 36, 2);
memcpy(&pack_info->pack.levels[lvls].flags, level_decompressor.out_buffer + 38, 2);
pack_info->pack.levels[lvls].level_name[31] = '\0';
printf("Level name: %s\n", pack_info->pack.levels[lvls].level_name);
printf("WxH: %u %u\n", pack_info->pack.levels[lvls].width, pack_info->pack.levels[lvls].height);
@ -554,20 +551,15 @@ void draw_sprite_pro(Sprite_t* spr, int frame_num, Vector2 pos, float rotation,
spr->frame_size.x * ((flip & 1) ? -1 : 1),
spr->frame_size.y * ((flip & 2) ? -1 : 1),
};
// The anchor here is only for rotation and scaling.
// Translational anchor is expected to be accounted for
// So need to offset render position with anchor position
Vector2 anchor = spr->anchor;
anchor.x *= scale.x;
anchor.y *= scale.y;
Rectangle dest = {
.x = pos.x + anchor.x,
.y = pos.y + anchor.y,
.x = pos.x,
.y = pos.y,
.width = spr->frame_size.x * scale.x,
.height = spr->frame_size.y * scale.y
};
Vector2 anchor = spr->anchor;
anchor.x *= scale.x;
anchor.y *= scale.y;
DrawTexturePro(
*spr->texture,
rec,
@ -576,73 +568,3 @@ void draw_sprite_pro(Sprite_t* spr, int frame_num, Vector2 pos, float rotation,
rotation, colour
);
}
static Vector2 internal_get_anchor_offset(Vector2 bbox, AnchorPoint_t anchor)
{
Vector2 offset = {0};
switch (anchor)
{
case AP_TOP_LEFT:
break;
case AP_TOP_CENTER:
offset.x = bbox.x / 2;
break;
case AP_TOP_RIGHT:
offset.x = bbox.x;
break;
case AP_MID_LEFT:
offset.x = 0;
offset.y = bbox.y / 2;
break;
case AP_MID_CENTER:
offset.x = bbox.x / 2;
offset.y = bbox.y / 2;
break;
case AP_MID_RIGHT:
offset.x = bbox.x;
offset.y = bbox.y / 2;
break;
case AP_BOT_LEFT:
offset.x = 0;
offset.y = bbox.y;
break;
case AP_BOT_CENTER:
offset.x = bbox.x / 2;
offset.y = bbox.y;
break;
case AP_BOT_RIGHT:
offset.x = bbox.x;
offset.y = bbox.y;
break;
}
return offset;
}
Vector2 shift_bbox(Vector2 bbox, Vector2 new_bbox, AnchorPoint_t anchor)
{
Vector2 p1 = internal_get_anchor_offset(bbox, anchor);
Vector2 p2 = internal_get_anchor_offset(new_bbox, anchor);
return Vector2Subtract(p1, p2);
}
Vector2 get_anchor_offset(Vector2 bbox, AnchorPoint_t anchor, bool flip_x)
{
if (flip_x)
{
switch(anchor)
{
case AP_TOP_LEFT: anchor = AP_TOP_RIGHT; break;
case AP_TOP_RIGHT: anchor = AP_TOP_LEFT; break;
case AP_MID_LEFT: anchor = AP_MID_RIGHT; break;
case AP_MID_RIGHT: anchor = AP_MID_LEFT; break;
case AP_BOT_LEFT: anchor = AP_BOT_RIGHT; break;
case AP_BOT_RIGHT: anchor = AP_BOT_LEFT; break;
default:
break;
}
}
return internal_get_anchor_offset(bbox, anchor);
}

View File

@ -4,7 +4,7 @@
#include "EC.h"
#include "raylib.h"
#include "rres.h"
#include "particle_sys.h"
#define N_ASSETS_TYPE 6
typedef enum AssetType
{
@ -16,24 +16,6 @@ typedef enum AssetType
AST_EMITTER_CONF,
}AssetType_t;
typedef enum PartEmitterType
{
EMITTER_UNKNOWN = 0,
EMITTER_BURST,
EMITTER_STREAM,
} PartEmitterType_t;
typedef struct EmitterConfig
{
float launch_range[2];
float speed_range[2];
float angle_range[2];
float rotation_range[2];
float particle_lifetime[2];
float initial_spawn_delay;
PartEmitterType_t type;
bool one_shot;
}EmitterConfig_t;
typedef struct Assets
{
@ -58,8 +40,6 @@ typedef struct LevelMap
char level_name[32];
uint16_t width;
uint16_t height;
uint16_t n_chests;
uint16_t flags; // In case of extras
LevelTileInfo_t* tiles;
}LevelMap_t;
@ -69,18 +49,6 @@ typedef struct LevelPack
LevelMap_t* levels;
}LevelPack_t;
// Credits to bedroomcoders.co.uk for this
typedef struct Sprite {
Texture2D* texture;
Vector2 frame_size;
Vector2 origin; // TL of the frame
Vector2 anchor; // Where transformation anchors on
uint8_t frame_per_row;
int frame_count;
int speed;
char* name;
} Sprite_t;
typedef struct RresFileInfo
{
rresCentralDir dir;
@ -115,8 +83,6 @@ LevelPack_t* get_level_pack(Assets_t* assets, const char* name);
void draw_sprite(Sprite_t* spr, int frame_num, Vector2 pos, float rotation, bool flip_x);
void draw_sprite_pro(Sprite_t* spr, int frame_num, Vector2 pos, float rotation, uint8_t flip, Vector2 scale, Color colour);
Vector2 get_anchor_offset(Vector2 bbox, AnchorPoint_t anchor, bool flip_x);
Vector2 shift_bbox(Vector2 bbox, Vector2 new_bbox, AnchorPoint_t anchor);
typedef struct SFX
{

View File

@ -160,7 +160,7 @@ uint8_t check_collision_at(Entity_t* p_ent, Vector2 pos, Vector2 bbox_sz, TileGr
CollideEntity_t ent = {
.p_ent = p_ent,
.bbox = (Rectangle){pos.x, pos.y, bbox_sz.x, bbox_sz.y},
.prev_bbox = (Rectangle){p_ent->position.x, p_ent->position.y, bbox_sz.x, bbox_sz.y},
.prev_bbox = (Rectangle){pos.x, pos.y, bbox_sz.x, bbox_sz.y},
.area = (TileArea_t){
.tile_x1 = (pos.x) / grid->tile_size,
.tile_y1 = (pos.y) / grid->tile_size,
@ -169,7 +169,7 @@ uint8_t check_collision_at(Entity_t* p_ent, Vector2 pos, Vector2 bbox_sz, TileGr
}
};
return check_collision(&ent, grid, true);
return check_collision(&ent, grid, false);
}
bool check_on_ground(Entity_t* p_ent, Vector2 prev_pos, Vector2 bbox_sz, TileGrid_t* grid)

View File

@ -1,7 +1,6 @@
#ifndef __COLLISION_FUNCS_H
#define __COLLISION_FUNCS_H
#include "EC.h"
#include "render_queue.h"
typedef enum SolidType
{
@ -43,7 +42,6 @@ typedef struct TileGrid
unsigned int max_tiles;
unsigned int tile_size;
Tile_t* tiles;
RenderInfoNode* render_nodes;
}TileGrid_t;
typedef struct TileArea {

View File

@ -15,7 +15,6 @@ void init_engine(GameEngine_t* engine, Vector2 starting_win_size)
init_assets(&engine->assets);
engine->intended_window_size = starting_win_size;
InitWindow(starting_win_size.x, starting_win_size.y, "raylib");
engine->base_canvas = LoadRenderTexture(starting_win_size.x, starting_win_size.y);
}
void deinit_engine(GameEngine_t* engine)
@ -25,7 +24,6 @@ void deinit_engine(GameEngine_t* engine)
sc_queue_term(&engine->key_buffer);
sc_queue_term(&engine->scene_stack);
sc_heap_term(&engine->scenes_render_order);
UnloadRenderTexture(engine->base_canvas);
CloseAudioDevice();
CloseWindow();
}
@ -91,10 +89,10 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene)
}
}
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx)
void change_scene(GameEngine_t* engine, unsigned int idx)
{
// Backwards compat
return change_active_scene(engine, idx);
change_active_scene(engine, idx);
}
bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx)
@ -118,7 +116,7 @@ void play_sfx_pitched(GameEngine_t* engine, unsigned int tag_idx, float pitch)
//if (sfx->snd != NULL)
{
PlaySound(*sfx->snd);
//sfx->plays++;
sfx->plays++;
}
//SetSoundPitch(*sfx->snd, 0.0f);
}
@ -152,20 +150,13 @@ void update_sfx_list(GameEngine_t* engine)
engine->sfx_list.played_sfx = 0;
}
void init_scene(Scene_t* scene, action_func_t action_func, uint32_t subsystem_init)
void init_scene(Scene_t* scene, action_func_t action_func)
{
sc_map_init_64(&scene->action_map, 32, 0);
sc_array_init(&scene->systems);
if (subsystem_init & ENABLE_ENTITY_MANAGEMENT_SYSTEM)
{
init_entity_manager(&scene->ent_manager);
}
if (subsystem_init & ENABLE_PARTICLE_SYSTEM)
{
init_particle_system(&scene->part_sys);
}
scene->subsystem_init = subsystem_init;
//scene->scene_type = scene_type;
scene->layers.n_layers = 0;
scene->bg_colour = WHITE;
@ -193,17 +184,9 @@ void free_scene(Scene_t* scene)
{
UnloadRenderTexture(scene->layers.render_layers[i].layer_tex);
}
if (scene->subsystem_init & ENABLE_ENTITY_MANAGEMENT_SYSTEM)
{
free_entity_manager(&scene->ent_manager);
}
if (scene->subsystem_init & ENABLE_PARTICLE_SYSTEM)
{
deinit_particle_system(&scene->part_sys);
}
}
inline void update_scene(Scene_t* scene, float delta_time)
{
@ -215,11 +198,8 @@ inline void update_scene(Scene_t* scene, float delta_time)
{
sys(scene);
}
if (scene->subsystem_init & ENABLE_PARTICLE_SYSTEM)
{
update_particle_system(&scene->part_sys, scene->delta_time);
}
}
static void _internal_render_scene(Scene_t* scene)
{
@ -253,43 +233,8 @@ static void _internal_render_scene(Scene_t* scene)
inline void render_scene(Scene_t* scene)
{
BeginTextureMode(scene->engine->base_canvas);
_internal_render_scene(scene);
EndTextureMode();
int curr_width = GetRenderWidth();
int curr_height = GetRenderHeight();
Vector2 original_size = scene->engine->intended_window_size;
float wscale = (curr_width / original_size.x);
float hscale = (curr_height / original_size.y);
float min_dim = (wscale > hscale) ? hscale : wscale;
Vector2 offset = {
scene->engine->intended_window_size.x * (wscale - min_dim) / 2,
scene->engine->intended_window_size.y * (hscale - min_dim) / 2
};
wscale = min_dim;
hscale = min_dim;
Rectangle draw_rec = {
0,0,
scene->engine->intended_window_size.x,
scene->engine->intended_window_size.y
};
Rectangle draw_pos = {
draw_rec.x * wscale + offset.x, draw_rec.y * hscale + offset.y,
draw_rec.width * wscale, draw_rec.height * hscale
};
draw_rec.height *= -1;
BeginDrawing();
ClearBackground((Color){0,0,0,255});
DrawTexturePro(
scene->engine->base_canvas.texture,
draw_rec,
draw_pos,
(Vector2){0,0}, 0.0, WHITE
);
_internal_render_scene(scene);
EndDrawing();
}
@ -416,13 +361,11 @@ void remove_child_scene(GameEngine_t* engine, unsigned int idx)
child->parent_scene = NULL;
}
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx)
void change_active_scene(GameEngine_t* engine, unsigned int idx)
{
engine->scenes[engine->curr_scene]->state = 0;
engine->curr_scene = idx;
engine->scenes[engine->curr_scene]->state = SCENE_COMPLETE_ACTIVE;
sc_queue_clear(&engine->key_buffer);
return engine->scenes[engine->curr_scene];
}
void change_focused_scene(GameEngine_t* engine, unsigned int idx)

View File

@ -6,7 +6,6 @@
#include "sc/heap/sc_heap.h"
#include "assets.h"
#include "particle_sys.h"
#include "render_queue.h"
typedef struct Scene Scene_t;
@ -38,7 +37,6 @@ typedef struct GameEngine {
// This is in case of window scaling, where there needs to be
// an absolute reference
Vector2 intended_window_size;
RenderTexture2D base_canvas;
} GameEngine_t;
#define SCENE_ACTIVE_BIT (1 << 0) // Systems Active
@ -62,7 +60,6 @@ typedef struct SceneRenderLayers {
struct Scene {
// Not all scene needs an entity manager
// but too late to change this
uint32_t subsystem_init;
EntityManager_t ent_manager;
Scene_t* parent_scene;
struct sc_map_64 action_map; // key -> actions
@ -89,8 +86,8 @@ void process_active_scene_inputs(GameEngine_t* engine);
void update_curr_scene(GameEngine_t* engine);
void render_curr_scene(GameEngine_t* engine);
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx);
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx);
void change_scene(GameEngine_t* engine, unsigned int idx);
void change_active_scene(GameEngine_t* engine, unsigned int idx);
void change_focused_scene(GameEngine_t* engine, unsigned int idx);
bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx);
void play_sfx(GameEngine_t* engine, unsigned int tag_idx);
@ -102,10 +99,7 @@ extern void update_scene(Scene_t* scene, float delta_time);
extern void render_scene(Scene_t* scene);
extern void do_action(Scene_t* scene, ActionType_t action, bool pressed);
//void init_scene(Scene_t* scene, action_func_t action_func);
#define ENABLE_ENTITY_MANAGEMENT_SYSTEM (1)
#define ENABLE_PARTICLE_SYSTEM (1 << 1)
void init_scene(Scene_t* scene, action_func_t action_func, uint32_t subsystem_init);
void init_scene(Scene_t* scene, action_func_t action_func);
bool add_scene_layer(Scene_t* scene, int width, int height, Rectangle render_area);
void free_scene(Scene_t* scene);
void add_child_scene(GameEngine_t* engine, unsigned int child_idx, unsigned int parent_idx);

View File

@ -1,24 +1,21 @@
#ifndef _ENGINE_CONF_H
#define _ENGINE_CONF_H
// Take care tuning these params. Web build doesn't work
// if memory used too high
#define MAX_SCENES_TO_RENDER 8
#define MAX_SCENES_TO_RENDER 16
#define MAX_RENDER_LAYERS 4
#define MAX_RENDERMANAGER_DEPTH 4
#define MAX_ENTITIES 2047
#define MAX_ENTITIES 2048
#define MAX_TEXTURES 16
#define MAX_SPRITES 127
#define MAX_SPRITES 64
#define MAX_SOUNDS 32
#define MAX_FONTS 4
#define MAX_N_TILES 16384
#define MAX_N_TILES 4096
#define MAX_NAME_LEN 32
#define MAX_LEVEL_PACK 4
#define N_SFX 32
#define MAX_EMITTER_CONF 8
//#define MAX_PARTICLE_EMITTER 8
#define MAX_ACTIVE_PARTICLE_EMITTER 255
#define MAX_PARTICLES 32
#define MAX_ACTIVE_PARTICLE_EMITTER 512
#define MAX_PARTICLES 64
#define MAX_TILE_TYPES 16
#define N_TAGS 10

View File

@ -115,6 +115,7 @@ Entity_t *add_entity(EntityManager_t* p_manager, unsigned int tag)
Entity_t* p_ent = new_entity_from_mempool(&e_idx);
if (p_ent == NULL) return NULL;
p_ent->spawn_pos = (Vector2){0, 0};
p_ent->m_tag = tag;
sc_queue_add_last(&p_manager->to_add, e_idx);
p_ent->manager = p_manager;
@ -144,7 +145,7 @@ Entity_t* get_entity(EntityManager_t* p_manager, unsigned long id)
return p_entity;
}
void* add_component(Entity_t* p_entity, unsigned int comp_type)
void* add_component(Entity_t* p_entity, ComponentEnum_t comp_type)
{
if (p_entity->components[comp_type] == MAX_COMP_POOL_SIZE)
{
@ -163,7 +164,7 @@ void* add_component(Entity_t* p_entity, unsigned int comp_type)
return get_component(p_entity, comp_type);
}
void* get_component(Entity_t *p_entity, unsigned int comp_type)
void* get_component(Entity_t *p_entity, ComponentEnum_t comp_type)
{
unsigned long comp_type_idx = (unsigned long)comp_type;
unsigned long c_idx = p_entity->components[comp_type_idx];
@ -171,7 +172,7 @@ void* get_component(Entity_t *p_entity, unsigned int comp_type)
return get_component_wtih_id(comp_type, c_idx);
}
void remove_component(Entity_t *p_entity, unsigned int comp_type)
void remove_component(Entity_t *p_entity, ComponentEnum_t comp_type)
{
if (p_entity->components[comp_type] == MAX_COMP_POOL_SIZE) return;
struct EntityUpdateEventInfo evt = (struct EntityUpdateEventInfo){p_entity->m_id, comp_type, p_entity->components[comp_type] , COMP_DELETION};

View File

@ -1,7 +1,5 @@
#include "gui.h"
#include "raylib.h"
#include <string.h>
#include <stdio.h>
#define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls
#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties
@ -11,11 +9,6 @@
static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization
static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib)
//
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
#define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
#endif
typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@ -376,403 +369,6 @@ void UI_button(const UIComp_t* comp, const char* text)
}
void hover_text(const UIComp_t* comp, Font font, const char* text, Vector2 pos, int font_size, int spacing, Color colour) {
if (comp->state == STATE_FOCUSED) {
DrawTextEx(font, text, pos, font_size, spacing, Fade(colour, 0.1));
pos.y -= font_size >> 2;
}
DrawTextEx(font, text, pos, font_size, spacing, colour);
}
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
int GuiSliderPro(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
{
Rectangle bounds = comp->bbox;
int result = 0;
GuiState state = comp->state;
float temp = (maxValue - minValue)/2.0f;
if (value == NULL) value = &temp;
int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
if (sliderWidth > 0) // Slider
{
slider.x += (sliderValue - (sliderWidth >> 1));
slider.width = (float)sliderWidth;
}
else if (sliderWidth == 0) // SliderBar
{
slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.width = (float)sliderValue;
}
// Update control
//--------------------------------------------------------------------
//if ((state != STATE_DISABLED) && !guiLocked)
if (state != STATE_DISABLED)
{
Vector2 mousePoint = GetMousePosition();
if (comp->pressed) // Keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
//if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePosition.x
*value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
}
}
//else
//{
// guiSliderDragging = false;
// guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
//}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
//guiSliderDragging = true;
//guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
*value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + (sliderWidth >> 1) )))/(float)(bounds.width - sliderWidth) + minValue;
if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider
else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar
}
}
else state = STATE_FOCUSED;
}
if (*value > maxValue) *value = maxValue;
else if (*value < minValue) *value = minValue;
}
// Bar limits check
if (sliderWidth > 0) // Slider
{
if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
else if (sliderWidth == 0) // SliderBar
{
if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
}
//--------------------------------------------------------------------
// Draw control
//--------------------------------------------------------------------
GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
// Draw slider internal bar (depends on state)
if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
// Draw left/right text if provided
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - (GuiGetStyle(DEFAULT, TEXT_SIZE) >> 1);
GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - (GuiGetStyle(DEFAULT, TEXT_SIZE) >> 1);
GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
}
//--------------------------------------------------------------------
return result;
}
//------------------------------------------------------------------------------------
// Controls Functions Definitions (local)
//------------------------------------------------------------------------------------
float GuiVerticalSliderPro(const UIComp_t* comp, const char *textTop, const char *textBottom, float* in_value, float minValue, float maxValue, int sliderHeight)
{
float value = *in_value;
GuiState state = comp->state;
Rectangle bounds = comp->bbox;
int sliderValue = (int)(((value - minValue)/(maxValue - minValue)) * (bounds.height - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH)));
Rectangle slider = {
bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
bounds.y + bounds.height - sliderValue,
bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING),
0.0f,
};
if (sliderHeight > 0) // Slider
{
slider.y -= sliderHeight >> 1;
slider.height = (float)sliderHeight;
}
else if (sliderHeight == 0) // SliderBar
{
slider.y -= GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.height = (float)sliderValue;
}
// Update control
//--------------------------------------------------------------------
//if ((state != STATE_DISABLED) && !guiLocked)
if (state != STATE_DISABLED)
{
Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePoint.x
float normalizedValue = (bounds.y + bounds.height - mousePoint.y - (float)(sliderHeight >> 1)) / (bounds.height - (float)sliderHeight);
value = (maxValue - minValue) * normalizedValue + minValue;
if (sliderHeight > 0) slider.y = mousePoint.y - slider.height / 2; // Slider
else if (sliderHeight == 0) // SliderBar
{
slider.y = mousePoint.y;
slider.height = bounds.y + bounds.height - slider.y - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
}
else state = STATE_FOCUSED;
}
if (value > maxValue) value = maxValue;
else if (value < minValue) value = minValue;
}
// Bar limits check
if (sliderHeight > 0) // Slider
{
if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH);
else if ((slider.y + slider.height) >= (bounds.y + bounds.height)) slider.y = bounds.y + bounds.height - slider.height - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
else if (sliderHeight == 0) // SliderBar
{
if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH)))
{
slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.height = bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
}
}
//--------------------------------------------------------------------
// Draw control
//--------------------------------------------------------------------
GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), comp->alpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), comp->alpha));
// Draw slider internal bar (depends on state)
if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), comp->alpha));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), comp->alpha));
// Draw top/bottom text if provided
if (textTop != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textTop);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2;
textBounds.y = bounds.y - textBounds.height - GuiGetStyle(SLIDER, TEXT_PADDING);
GuiDrawText(textTop, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), comp->alpha));
}
if (textBottom != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textBottom);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2;
textBounds.y = bounds.y + bounds.height + GuiGetStyle(SLIDER, TEXT_PADDING);
GuiDrawText(textBottom, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), comp->alpha));
}
//--------------------------------------------------------------------
*in_value = value;
return value;
}
float UI_vert_slider(const UIComp_t* comp, const char *textTop, const char *textBottom, float* value, float minValue, float maxValue)
{
return GuiVerticalSliderPro(comp, textTop, textBottom, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
}
// Slider control extended, returns selected value and has text
float UI_slider(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
return GuiSliderPro(comp, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
}
void vert_scrollarea_init(VertScrollArea_t* scroll_area, Rectangle display_area, Vector2 canvas_dims)
{
scroll_area->comp.font = GetFontDefault();
scroll_area->canvas = LoadRenderTexture(canvas_dims.x, canvas_dims.y);
scroll_area->scroll_pos = canvas_dims.y - display_area.height;
scroll_area->scroll_bounds = (Vector2){
0, scroll_area->scroll_pos
};
vert_scrollarea_set_item_dims(scroll_area, 12, 3);
scroll_area->curr_selection = 0;
scroll_area->display_area = display_area;
scroll_area->scroll_bar.alpha = 1;
scroll_area->scroll_bar.bbox = (Rectangle){
display_area.width + display_area.x, display_area.y,
0.1f * display_area.width, display_area.height,
};
scroll_area->scroll_bar.state = STATE_NORMAL;
scroll_area->comp.alpha = 1;
scroll_area->comp.bbox = display_area;
scroll_area->comp.bbox.width *= 1.1f;
scroll_area->scroll_bar.state = STATE_NORMAL;
}
void vert_scrollarea_set_item_dims(VertScrollArea_t* scroll_area, unsigned int item_height, unsigned int item_padding)
{
scroll_area->item_height = item_height;
scroll_area->item_padding = item_padding;
scroll_area->max_items = (scroll_area->canvas.texture.height - scroll_area->item_padding) / (scroll_area->item_height + scroll_area->item_padding);
}
bool vert_scrollarea_n_items(VertScrollArea_t* scroll_area, unsigned int n_items) {
if (n_items >= scroll_area->max_items) return false;
// Due to OpenGL convention where y is -1 downwards,
// The scroll bar is set to decreases the scroll_pos when going down
// This value is used as the offset when rendering, which results in correctly
// give the illusion of 'scrolling down'
// So, adjust the minimum bound which is the height of the items that are not shown
scroll_area->n_items = n_items;
scroll_area->scroll_bounds.x =
(scroll_area->max_items - n_items)* (scroll_area->item_height + scroll_area->item_padding) + scroll_area->item_padding;
if (scroll_area->scroll_bounds.x > scroll_area->scroll_bounds.y) {
scroll_area->scroll_bounds.x = scroll_area->scroll_bounds.y;
}
return true;
}
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx)
{
if (item_idx >= scroll_area->max_items) return;
DrawTextEx(
scroll_area->comp.font,
str,
(Vector2){0, scroll_area->item_padding + (scroll_area->item_height + scroll_area->item_padding) * item_idx}
, scroll_area->item_height, 0, BLACK);
}
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos)
{
float x = pos.x - scroll_area->display_area.x;
float y = pos.y - scroll_area->display_area.y;
if (x >= scroll_area->display_area.width || x < 0) return scroll_area->max_items;
if (y >= scroll_area->display_area.height || y < 0) return scroll_area->max_items;
// How much have scroll down
float canvas_offset = (scroll_area->scroll_bounds.y - scroll_area->scroll_pos);
scroll_area->curr_selection = (y + canvas_offset - scroll_area->item_padding) / (scroll_area->item_height + scroll_area->item_padding);
return scroll_area->curr_selection;
}
void vert_scrollarea_refocus(VertScrollArea_t* scroll_area)
{
float canvas_offset = (scroll_area->scroll_bounds.y - scroll_area->scroll_pos);
// Reverse from item selection to y in display area
float selection_y = scroll_area->curr_selection * (scroll_area->item_height + scroll_area->item_padding) + scroll_area->item_padding - canvas_offset;
// Auto adjust scroll based on selection
if (selection_y < 0)
{
scroll_area->scroll_pos -= selection_y;
}
else if (selection_y + scroll_area->item_height + scroll_area->item_padding > scroll_area->display_area.height)
{
scroll_area->scroll_pos -= selection_y + scroll_area->item_height + scroll_area->item_padding - scroll_area->display_area.height;
}
}
void vert_scrollarea_render(VertScrollArea_t* scroll_area)
{
BeginScissorMode(scroll_area->comp.bbox.x, scroll_area->comp.bbox.y, scroll_area->comp.bbox.width, scroll_area->comp.bbox.height);
UI_vert_slider(&scroll_area->scroll_bar,
NULL,
NULL,
&scroll_area->scroll_pos,
scroll_area->scroll_bounds.x,
scroll_area->scroll_bounds.y
);
Rectangle draw_rec = (Rectangle){
0, scroll_area->scroll_pos,
scroll_area->display_area.width,
scroll_area->display_area.height
};
float canvas_offset = (scroll_area->scroll_bounds.y - scroll_area->scroll_pos);
float selection_y = scroll_area->curr_selection * (scroll_area->item_height + scroll_area->item_padding) + scroll_area->item_padding - canvas_offset;
DrawRectangle(
scroll_area->display_area.x,
scroll_area->display_area.y + selection_y,
scroll_area->canvas.texture.width, scroll_area->item_height,
Fade(BLUE, 0.7)
);
Vector2 draw_pos = {scroll_area->display_area.x, scroll_area->display_area.y};
draw_rec.height *= -1;
DrawTextureRec(
scroll_area->canvas.texture,
draw_rec,
draw_pos,
WHITE
);
EndScissorMode();
}
void vert_scrollarea_free(VertScrollArea_t* scroll_area)
{
UnloadRenderTexture(scroll_area->canvas);
}
void init_UI(void)
{
GuiLoadStyleDefault();

View File

@ -3,52 +3,13 @@
#include "raylib.h"
#include "raygui.h"
// Generic Component
typedef struct UIComp {
Rectangle bbox;
GuiState state;
float alpha;
bool pressed;
Font font;
} UIComp_t;
typedef struct VertScrollArea {
UIComp_t comp; // Display area + scrollbar
Rectangle display_area; // Display dimension
RenderTexture2D canvas; // Complete canvas
unsigned int n_items;
unsigned int max_items;
unsigned int curr_selection;
unsigned int item_height;
unsigned int item_padding;
float scroll_pos;
Vector2 scroll_bounds;
UIComp_t scroll_bar;
} VertScrollArea_t;
void init_UI(void);
void vert_scrollarea_init(VertScrollArea_t* scroll_area, Rectangle display_area, Vector2 canvas_dims);
void vert_scrollarea_set_item_dims(VertScrollArea_t* scroll_area, unsigned int item_height, unsigned int item_padding);
bool vert_scrollarea_n_items(VertScrollArea_t* scroll_area, unsigned int n_items);
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx);
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos);
void vert_scrollarea_refocus(VertScrollArea_t* scroll_area);
void vert_scrollarea_render(VertScrollArea_t* scroll_area);
void vert_scrollarea_free(VertScrollArea_t* scroll_area);
static inline void ScrollAreaRenderBegin(VertScrollArea_t* scroll)
{
BeginTextureMode(scroll->canvas);
}
#define ScrollAreaRenderEnd() EndTextureMode()
void UI_button(const UIComp_t* bbox, const char* text);
float UI_slider(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue);
float UI_vert_slider(const UIComp_t* comp, const char *textTop, const char *textBottom, float* value, float minValue, float maxValue);
void hover_text(const UIComp_t* comp, Font font, const char* text, Vector2 pos, int font_size, int spacing, Color colour);
#endif

View File

@ -1,11 +1,102 @@
#include "mempool.h"
//#include "sc/queue/sc_queue.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
typedef struct ULongCircBuffer {
unsigned long* buffer; // data buffer
unsigned long* buffer_end; // end of data buffer
uint32_t capacity; // maximum number of items in the buffer
uint32_t count; // number of items in the buffer
unsigned long* head; // pointer to head
unsigned long* tail; // pointer to tail
}ULongCircBuffer_t;
static void cb_init(ULongCircBuffer_t* cb, size_t capacity)
{
cb->buffer = (unsigned long*)malloc(capacity * sizeof(unsigned long));
assert(cb->buffer != NULL);
cb->buffer_end = cb->buffer + capacity;
cb->capacity = capacity;
cb->count = 0;
cb->head = cb->buffer;
cb->tail = cb->buffer;
}
static void cb_free(ULongCircBuffer_t* cb)
{
free(cb->buffer);
// clear out other fields too, just to be safe
}
static bool cb_pop_front(ULongCircBuffer_t* cb, unsigned long* item)
{
if (cb->count == 0) return false;
*item = *cb->tail;
cb->tail++;
if(cb->tail == cb->buffer_end) cb->tail = cb->buffer;
cb->count--;
return true;
}
static bool cb_push_back(ULongCircBuffer_t* cb, unsigned long item)
{
if(cb->count == cb->capacity) return false;
*(cb->head) = item;
cb->head++;
if(cb->head == cb->buffer_end) cb->head = cb->buffer;
cb->count++;
return true;
}
typedef struct MemPool {
void * const buffer;
const unsigned long max_size;
const unsigned long elem_size;
bool *use_list;
ULongCircBuffer_t free_list;
} MemPool_t;
// Static allocate buffers
static Entity_t entity_buffer[MAX_COMP_POOL_SIZE];
static CBBox_t bbox_buffer[MAX_COMP_POOL_SIZE];
static CTransform_t ctransform_buffer[MAX_COMP_POOL_SIZE];
static CTileCoord_t ctilecoord_buffer[MAX_COMP_POOL_SIZE];
static CMovementState_t cmstate_buffer[MAX_COMP_POOL_SIZE];
static CJump_t cjump_buffer[8]; // Only player is expected to have this
static CPlayerState_t cplayerstate_buffer[8]; // Only player is expected to have this
static CContainer_t ccontainer_buffer[MAX_COMP_POOL_SIZE];
static CHitBoxes_t chitboxes_buffer[MAX_COMP_POOL_SIZE];
static CHurtbox_t churtbox_buffer[MAX_COMP_POOL_SIZE];
static CSprite_t csprite_buffer[MAX_COMP_POOL_SIZE];
static CMoveable_t cmoveable_buffer[MAX_COMP_POOL_SIZE];
static CLifeTimer_t clifetimer_buffer[MAX_COMP_POOL_SIZE];
static CWaterRunner_t cwaterrunner_buffer[32];
static CAirTimer_t cairtimer_buffer[8]; // Only player is expected to have this
static CEmitter_t cemitter_buffer[MAX_COMP_POOL_SIZE]; // Only player is expected to have this
// Static allocate mempools
static MemPool_t comp_mempools[N_COMPONENTS] = {
{bbox_buffer, MAX_COMP_POOL_SIZE, sizeof(CBBox_t), NULL, {0}},
{ctransform_buffer, MAX_COMP_POOL_SIZE, sizeof(CTransform_t), NULL, {0}},
{ctilecoord_buffer, MAX_COMP_POOL_SIZE, sizeof(CTileCoord_t), NULL, {0}},
{cmstate_buffer, MAX_COMP_POOL_SIZE, sizeof(CMovementState_t), NULL, {0}},
{cjump_buffer, 8, sizeof(CJump_t), NULL, {0}},
{cplayerstate_buffer, 8, sizeof(CPlayerState_t), NULL, {0}},
{ccontainer_buffer, MAX_COMP_POOL_SIZE, sizeof(CContainer_t), NULL, {0}},
{chitboxes_buffer, MAX_COMP_POOL_SIZE, sizeof(CHitBoxes_t), NULL, {0}},
{churtbox_buffer, MAX_COMP_POOL_SIZE, sizeof(CHurtbox_t), NULL, {0}},
{csprite_buffer, MAX_COMP_POOL_SIZE, sizeof(CSprite_t), NULL, {0}},
{cmoveable_buffer, MAX_COMP_POOL_SIZE, sizeof(CMoveable_t), NULL, {0}},
{clifetimer_buffer, MAX_COMP_POOL_SIZE, sizeof(CLifeTimer_t), NULL, {0}},
{cwaterrunner_buffer, 32, sizeof(CWaterRunner_t), NULL, {0}},
{cairtimer_buffer, 8, sizeof(CAirTimer_t), NULL, {0}},
{cemitter_buffer, MAX_COMP_POOL_SIZE, sizeof(CEmitter_t), NULL, {0}},
};
static MemPool_t ent_mempool = {
.buffer = entity_buffer,
.max_size = MAX_COMP_POOL_SIZE,
@ -24,21 +115,23 @@ void init_memory_pools(void)
memset(comp_mempools[i].buffer, 0, comp_mempools[i].elem_size * comp_mempools[i].max_size);
comp_mempools[i].use_list = (bool*)calloc(comp_mempools[i].max_size, sizeof(bool));
assert(comp_mempools[i].use_list != NULL);
sc_queue_init(&comp_mempools[i].free_list);
cb_init(&comp_mempools[i].free_list, comp_mempools[i].max_size);
for (size_t j = 0; j < comp_mempools[i].max_size; ++j)
{
sc_queue_add_last(&comp_mempools[i].free_list, j);
comp_mempools[i].free_list.buffer[j] = j;
}
comp_mempools[i].free_list.count = comp_mempools[i].max_size;
}
memset(ent_mempool.buffer, 0, ent_mempool.elem_size*ent_mempool.max_size);
sc_queue_init(&ent_mempool.free_list);
cb_init(&ent_mempool.free_list, ent_mempool.max_size);
ent_mempool.use_list = (bool *)calloc(ent_mempool.max_size, sizeof(bool));
for (size_t i = 0; i < ent_mempool.max_size; ++i)
{
entity_buffer[i].m_id = i;
sc_queue_add_last(&ent_mempool.free_list, i);
ent_mempool.free_list.buffer[i] = i;
}
ent_mempool.free_list.count = ent_mempool.max_size;
pool_inited = true;
}
}
@ -50,10 +143,10 @@ void free_memory_pools(void)
for (size_t i = 0; i < N_COMPONENTS; ++i)
{
free(comp_mempools[i].use_list);
sc_queue_term(&comp_mempools[i].free_list);
cb_free(&comp_mempools[i].free_list);
}
free(ent_mempool.use_list);
sc_queue_term(&ent_mempool.free_list);
cb_free(&ent_mempool.free_list);
pool_inited = false;
}
@ -61,9 +154,8 @@ void free_memory_pools(void)
Entity_t* new_entity_from_mempool(unsigned long* e_idx_ptr)
{
if (sc_queue_empty(&ent_mempool.free_list)) return NULL;
unsigned long e_idx = sc_queue_del_first(&ent_mempool.free_list);
unsigned long e_idx;
if (!cb_pop_front(&ent_mempool.free_list, &e_idx)) return NULL;
*e_idx_ptr = e_idx;
ent_mempool.use_list[e_idx] = true;
@ -88,25 +180,24 @@ void free_entity_to_mempool(unsigned long idx)
if (ent_mempool.use_list[idx])
{
ent_mempool.use_list[idx] = false;
sc_queue_add_first(&ent_mempool.free_list, idx);
cb_push_back(&ent_mempool.free_list, idx);
}
}
void* new_component_from_mempool(unsigned int comp_type, unsigned long* idx)
void* new_component_from_mempool(ComponentEnum_t comp_type, unsigned long* idx)
{
void* comp = NULL;
assert(comp_type < N_COMPONENTS);
if (sc_queue_empty(&comp_mempools[comp_type].free_list)) return NULL;
*idx = sc_queue_del_first(&comp_mempools[comp_type].free_list);
if (cb_pop_front(&comp_mempools[comp_type].free_list, idx))
{
comp_mempools[comp_type].use_list[*idx] = true;
void* comp = comp_mempools[comp_type].buffer + (*idx * comp_mempools[comp_type].elem_size);
comp = comp_mempools[comp_type].buffer + (*idx * comp_mempools[comp_type].elem_size);
memset(comp, 0, comp_mempools[comp_type].elem_size);
}
return comp;
}
void* get_component_wtih_id(unsigned int comp_type, unsigned long idx)
void* get_component_wtih_id(ComponentEnum_t comp_type, unsigned long idx)
{
void * comp = NULL;
assert(comp_type < N_COMPONENTS);
@ -117,30 +208,30 @@ void* get_component_wtih_id(unsigned int comp_type, unsigned long idx)
return comp;
}
void free_component_to_mempool(unsigned int comp_type, unsigned long idx)
void free_component_to_mempool(ComponentEnum_t comp_type, unsigned long idx)
{
assert(comp_type < N_COMPONENTS);
// This just free the component from the memory pool
if (comp_mempools[comp_type].use_list[idx])
{
comp_mempools[comp_type].use_list[idx] = false;
sc_queue_add_first(&comp_mempools[comp_type].free_list, idx);
cb_push_back(&comp_mempools[comp_type].free_list, idx);
}
}
void print_mempool_stats(char* buffer)
{
buffer += sprintf(buffer, "Entity free: %lu\n", sc_queue_size(&ent_mempool.free_list));
buffer += sprintf(buffer, "Entity free: %u\n", ent_mempool.free_list.count);
for (size_t i = 0; i < N_COMPONENTS; ++i)
{
buffer += sprintf(
buffer, "%lu: %lu/%lu\n",
i, sc_queue_size(&comp_mempools[i].free_list), comp_mempools[i].free_list.cap
buffer, "%lu: %u/%u\n",
i, comp_mempools[i].free_list.count, comp_mempools[i].free_list.capacity
);
}
}
uint32_t get_num_of_free_entities(void)
{
return sc_queue_size(&ent_mempool.free_list);
return ent_mempool.free_list.count;
}

View File

@ -1,48 +1,17 @@
#ifndef __MEMPOOL_H
#define __MEMPOOL_H
#include "EC.h"
#include "sc/queue/sc_queue.h"
void init_memory_pools(void);
void free_memory_pools(void);
typedef struct MemPool {
void * const buffer;
const unsigned long max_size;
const unsigned long elem_size;
bool *use_list;
struct sc_queue_32 free_list;
} MemPool_t;
// Game needs to implement this somewhere
extern MemPool_t comp_mempools[N_COMPONENTS];
Entity_t* new_entity_from_mempool(unsigned long* e_idx_ptr);
Entity_t* get_entity_wtih_id(unsigned long idx);
void free_entity_to_mempool(unsigned long idx);
void* new_component_from_mempool(unsigned int comp_type, unsigned long* idx);
void* get_component_wtih_id(unsigned int comp_type, unsigned long idx);
void free_component_to_mempool(unsigned int comp_type, unsigned long idx);
void* new_component_from_mempool(ComponentEnum_t comp_type, unsigned long* idx);
void* get_component_wtih_id(ComponentEnum_t comp_type, unsigned long idx);
void free_component_to_mempool(ComponentEnum_t comp_type, unsigned long idx);
void print_mempool_stats(char* buffer);
uint32_t get_num_of_free_entities(void);
#define DEFINE_COMP_MEMPOOL_BUF(type, n) \
static type type##_buf[n]; \
const unsigned long type##_CNT = n; \
#define ADD_COMP_MEMPOOL(type) \
{type##_buf, type##_CNT, sizeof(type), NULL, {0}}, \
#define BEGIN_DEFINE_COMP_MEMPOOL \
DEFINE_COMP_MEMPOOL_BUF(CBBox_t, MAX_COMP_POOL_SIZE); \
DEFINE_COMP_MEMPOOL_BUF(CTransform_t, MAX_COMP_POOL_SIZE); \
DEFINE_COMP_MEMPOOL_BUF(CTileCoord_t, MAX_COMP_POOL_SIZE); \
MemPool_t comp_mempools[N_COMPONENTS] = { \
ADD_COMP_MEMPOOL(CBBox_t) \
ADD_COMP_MEMPOOL(CTransform_t) \
ADD_COMP_MEMPOOL(CTileCoord_t) \
#define END_DEFINE_COMP_MEMPOOL };
#endif //__MEMPOOL_H

View File

@ -3,11 +3,16 @@
#include "raylib.h"
#include "engine_conf.h"
#include "sc_queue.h"
#include "assets.h"
#include "EC.h"
#include <stdint.h>
#include <stdbool.h>
typedef uint16_t EmitterHandle;
typedef enum PartEmitterType
{
EMITTER_UNKNOWN = 0,
EMITTER_BURST,
EMITTER_STREAM,
} PartEmitterType_t;
typedef struct Particle
{
@ -27,6 +32,18 @@ typedef struct ParticleEmitter ParticleEmitter_t;
typedef void (*particle_update_func_t)(Particle_t* part, void* user_data, float delta_time);
typedef bool (*emitter_check_func_t)(const ParticleEmitter_t* emitter, float delta_time);
typedef struct EmitterConfig
{
float launch_range[2];
float speed_range[2];
float angle_range[2];
float rotation_range[2];
float particle_lifetime[2];
float initial_spawn_delay;
PartEmitterType_t type;
bool one_shot;
}EmitterConfig_t;
struct ParticleEmitter
{
const EmitterConfig_t* config;

View File

@ -539,7 +539,7 @@ RAYGUIAPI bool GuiSpinner(Rectangle bounds, const char *text, int *value, int mi
RAYGUIAPI bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
RAYGUIAPI bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text
RAYGUIAPI bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines
RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue);
RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value
RAYGUIAPI float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value
RAYGUIAPI float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value
RAYGUIAPI void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text

View File

@ -1,42 +0,0 @@
#include "render_queue.h"
#include "raylib.h"
void init_render_manager(RenderManager *manager)
{
memset(manager, 0, sizeof(*manager));
}
bool add_render_node(RenderManager *manager, RenderInfoNode *node, uint8_t layer_num)
{
if (node->next != NULL)
{
return false;
}
if (node->spr == NULL) {
return false;
}
layer_num = (layer_num >= MAX_RENDERMANAGER_DEPTH) ? MAX_RENDERMANAGER_DEPTH - 1 : layer_num;
node->next = manager->layers[layer_num];
manager->layers[layer_num] = node;
return true;
}
void execute_render(RenderManager *manager) {
for (uint8_t depth = 0; depth < MAX_RENDERMANAGER_DEPTH; ++depth)
{
RenderInfoNode* curr = manager->layers[depth];
while (curr != NULL)
{
draw_sprite_pro(
curr->spr, curr->frame_num, curr->pos,
curr->rotation, curr->flip, curr->scale,
curr->colour
);
RenderInfoNode* next = curr->next;
curr->next = NULL;
curr = next;
}
}
reset_render_manager(manager);
}

View File

@ -1,29 +0,0 @@
#ifndef RENDER_QUEUE_H
#define RENDER_QUEUE_H
#include "assets.h"
#include "engine_conf.h"
typedef struct RenderInfoNode RenderInfoNode;
struct RenderInfoNode {
// Intrusive Linked-list Node
RenderInfoNode* next;
Sprite_t* spr;
Vector2 pos;
int frame_num;
float rotation;
Vector2 scale;
Color colour;
uint8_t flip;
};
typedef struct RenderManager {
RenderInfoNode* layers[MAX_RENDERMANAGER_DEPTH];
} RenderManager;
void init_render_manager(RenderManager* manager);
#define reset_render_manager init_render_manager
bool add_render_node(RenderManager* manager, RenderInfoNode* node, uint8_t layer_num);
void execute_render(RenderManager* manager);
#endif

View File

@ -1,6 +1,5 @@
#include "mempool.h"
#include "scene_impl.h"
#include "gui.h"
#include <stdio.h>
#include <unistd.h>
#include <math.h>
@ -15,15 +14,12 @@ int main(void)
InitWindow(1280, 640, "raylib");
SetTargetFPS(60);
init_memory_pools();
init_UI();
LevelSelectScene_t scene;
init_level_select_scene(&scene);
scene.scene.bg_colour = RAYWHITE;
while(true)
{
Vector2 raw_mouse_pos = GetMousePosition();
scene.scene.mouse_pos = raw_mouse_pos;
// This entire key processing relies on the assumption that a pressed key will
// appear in the polling of raylib
@ -54,18 +50,6 @@ int main(void)
do_action(&scene.scene, action, true);
sc_queue_add_last(&key_buffer, button);
}
ActionType_t action = sc_map_get_64(&scene.scene.action_map, MOUSE_BUTTON_LEFT);
if (sc_map_found(&scene.scene.action_map))
{
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
{
do_action(&scene.scene, action, true);
}
else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
{
do_action(&scene.scene, action, false);
}
}
float frame_time = GetFrameTime();
float delta_time = fminf(frame_time, DT);

View File

@ -1,128 +0,0 @@
#include "raylib.h"
#include "assets_loader.h"
#include "scene_impl.h"
#include "ent_impl.h"
#include "mempool.h"
#include "constants.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "tracy/TracyC.h"
#define N_SCENES 1
Scene_t *scenes[N_SCENES];
static GameEngine_t engine = {
.scenes = scenes,
.max_scenes = 1,
.curr_scene = 0,
.assets = {0}
};
const int screenWidth = VIEWABLE_MAP_WIDTH * TILE_SIZE;
const int screenHeight = VIEWABLE_MAP_HEIGHT * TILE_SIZE;
// Maintain own queue to handle key presses
struct sc_queue_32 key_buffer;
int main(int argc, char** argv)
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
init_engine(&engine, (Vector2){screenWidth, screenHeight});
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
load_from_infofile("res/assets.info.raw", &engine.assets);
init_player_creation("res/player_spr.info", &engine.assets);
init_item_creation(&engine.assets);
load_sfx(&engine, "snd_jump", PLAYER_JMP_SFX);
load_sfx(&engine, "snd_land", PLAYER_LAND_SFX);
load_sfx(&engine, "snd_wdrop", WATER_IN_SFX);
load_sfx(&engine, "snd_bland", BOULDER_LAND_SFX);
load_sfx(&engine, "snd_bubble", BUBBLE_SFX);
load_sfx(&engine, "snd_step", PLAYER_STEP_SFX);
load_sfx(&engine, "snd_dead", PLAYER_DEAD_SFX);
load_sfx(&engine, "snd_drwg", PLAYER_DROWNING_SFX);
load_sfx(&engine, "snd_mdestroy", METAL_DESTROY_SFX);
load_sfx(&engine, "snd_wdestroy", WOOD_DESTROY_SFX);
load_sfx(&engine, "snd_cland", WOOD_LAND_SFX);
load_sfx(&engine, "snd_explsn", EXPLOSION_SFX);
load_sfx(&engine, "snd_coin", COIN_SFX);
load_sfx(&engine, "snd_arrhit", ARROW_DESTROY_SFX);
load_sfx(&engine, "snd_launch", ARROW_RELEASE_SFX);
load_sfx(&engine, "snd_launch", BOMB_RELEASE_SFX);
LevelScene_t level_scene;
level_scene.scene.engine = &engine;
init_game_scene(&level_scene);
level_scene.data.tile_sprites[ONEWAY_TILE] = get_sprite(&engine.assets, "tl_owp");
level_scene.data.tile_sprites[LADDER] = get_sprite(&engine.assets, "tl_ldr");
level_scene.data.tile_sprites[SPIKES] = get_sprite(&engine.assets, "d_spikes");
level_scene.data.tile_sprites[SPIKES + TILE_90CWROT] = get_sprite(&engine.assets, "l_spikes");
level_scene.data.tile_sprites[SPIKES + TILE_90CCWROT] = get_sprite(&engine.assets, "r_spikes");
level_scene.data.tile_sprites[SPIKES + TILE_180ROT] = get_sprite(&engine.assets, "u_spikes");
Texture2D* tex = get_texture(&engine.assets, "bg_tex");
SetTextureWrap(*tex, TEXTURE_WRAP_REPEAT);
sc_map_del_64(&level_scene.scene.action_map, ACTION_EXIT);
// Load level
LevelPack_t* pack = get_level_pack(&engine.assets, "DefLevels");
if (pack == NULL)
{
puts("Default level pack not found!");
return 1;
}
unsigned int selected_level = 0;
if (argc == 2) {
selected_level = strtoul(argv[1], NULL, 10);
printf("Selected level: %u", selected_level);
}
if (selected_level >= pack->n_levels) {
printf("Level numbers out of bound. Picking 0");
selected_level = 0;
}
level_scene.data.level_pack = pack;
level_scene.data.current_level = selected_level;
scenes[0] = &level_scene.scene;
reload_level_tilemap(&level_scene);
change_scene(&engine, 0);
const float DT = 1.0f/60.0f;
while (!WindowShouldClose())
{
TracyCFrameMark;
// This entire key processing relies on the assumption that a pressed key will
// appear in the polling of raylib
Scene_t* curr_scene = engine.scenes[engine.curr_scene];
if (curr_scene->state == 0 && engine.curr_scene == 0)
{
break;
}
process_inputs(&engine, curr_scene);
float frame_time = GetFrameTime();
float delta_time = fminf(frame_time, DT);
{
TracyCZoneN(ctx, "Update", true)
update_scene(curr_scene, delta_time);
update_entity_manager(&curr_scene->ent_manager);
update_sfx_list(&engine);
TracyCZoneEnd(ctx)
}
// This is needed to advance time delta
render_scene(curr_scene);
if (curr_scene->state != 0)
{
sc_queue_clear(&key_buffer);
}
}
free_game_scene(&level_scene);
deinit_engine(&engine);
}

31
main.c
View File

@ -3,7 +3,6 @@
#include "scene_impl.h"
#include "ent_impl.h"
#include "mempool.h"
#include "constants.h"
#include <stdio.h>
#include <math.h>
#define N_SCENES 4
@ -11,13 +10,13 @@
Scene_t *scenes[N_SCENES];
static GameEngine_t engine = {
.scenes = scenes,
.max_scenes = 4,
.max_scenes = 3,
.curr_scene = 0,
.assets = {0}
};
const int screenWidth = VIEWABLE_MAP_WIDTH * TILE_SIZE;
const int screenHeight = VIEWABLE_MAP_HEIGHT * TILE_SIZE;
const int screenWidth = 1280;
const int screenHeight = 640;
// Maintain own queue to handle key presses
struct sc_queue_32 key_buffer;
@ -26,7 +25,6 @@ int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
init_engine(&engine, (Vector2){screenWidth, screenHeight});
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
#ifndef NDEBUG
@ -65,30 +63,21 @@ int main(void)
level_scene.data.tile_sprites[SPIKES + TILE_90CWROT] = get_sprite(&engine.assets, "l_spikes");
level_scene.data.tile_sprites[SPIKES + TILE_90CCWROT] = get_sprite(&engine.assets, "r_spikes");
level_scene.data.tile_sprites[SPIKES + TILE_180ROT] = get_sprite(&engine.assets, "u_spikes");
Texture2D* tex = get_texture(&engine.assets, "bg_tex");
SetTextureWrap(*tex, TEXTURE_WRAP_REPEAT);
LevelPack_t* pack = get_level_pack(&engine.assets, "DefLevels");
LevelPack_t* pack = get_level_pack(&engine.assets, "TestLevels");
if (pack != NULL)
{
level_scene.data.level_pack = pack;
level_scene.data.current_level = 0;
load_level_tilemap(&level_scene, 0);
}
MenuScene_t menu_scene;
menu_scene.scene.engine = &engine;
init_menu_scene(&menu_scene);
LevelSelectScene_t level_sel_scene;
level_sel_scene.scene.engine = &engine;
level_sel_scene.data.level_pack = pack;
init_level_select_scene(&level_sel_scene);
scenes[MAIN_MENU_SCENE] = &menu_scene.scene;
scenes[LEVEL_SELECT_SCENE] = &level_sel_scene.scene;
scenes[GAME_SCENE] = &level_scene.scene;
scenes[SANDBOX_SCENE] = &sandbox_scene.scene;
change_scene(&engine, MAIN_MENU_SCENE);
scenes[0] = &menu_scene.scene;
scenes[1] = &level_scene.scene;
scenes[2] = &sandbox_scene.scene;
change_scene(&engine, 0);
const float DT = 1.0f/60.0f;
while (!WindowShouldClose())
@ -118,10 +107,10 @@ int main(void)
{
sc_queue_clear(&key_buffer);
}
}
free_sandbox_scene(&sandbox_scene);
free_game_scene(&level_scene);
free_level_select_scene(&level_sel_scene);
free_menu_scene(&menu_scene);
deinit_engine(&engine);
}

5
res/.gitignore vendored
View File

@ -1,10 +1,7 @@
*
!.gitignore
!CMakeLists.txt
!*.py
!pack_ldtk.sh
!pack_resources.sh
!ldtk_repacker.py
!test_assets.info
!testLevels.lvldata.zst
!rres_packer.c
!requirements.txt

View File

@ -8,6 +8,6 @@ set_target_properties(rres_packer
target_link_libraries(rres_packer
PRIVATE
lib_assets
lib_engine
)

View File

@ -37,11 +37,12 @@ ENUMIDS_TILETYPE_MAPPING = {
'Boulder': 20,
'Runner': 21,
'Player': 22,
'Chest': 23,
'Exit': 24,
'Urchin': 25,
}
#ENTID_MAPPING = {
# 'Player': 1
#}
# First go to tilesets and find Simple_tiles identifier, then find enumTags to identifier which tile type is what tileid
ids_tiletype_map = {}
tileset_defs = level_pack_data["defs"]["tilesets"]
@ -58,18 +59,8 @@ if not ids_tiletype_map:
pprint.pprint(ids_tiletype_map)
def get_level_order(lvl) -> int:
order = 65535;
for data in lvl['fieldInstances']:
if data["__identifier"] == "Order" and data["realEditorValues"]:
order = data["__value"]
return order
all_levels = level_pack_data["levels"]
all_levels.sort(key=get_level_order)
# Number of levels is the length of the levels
n_levels = len(all_levels)
n_levels = len(level_pack_data["levels"])
print("Number of levels:", n_levels)
fileparts = args.filename.split('.')
@ -83,21 +74,12 @@ converted_filename = '.'.join(fileparts)
with open(converted_filename, 'wb+') as out_file:
out_file.write(struct.pack("<I", n_levels))
# Then loop the levels. Read the layerIndstances
for level in all_levels:
n_chests : int = 0
for level in level_pack_data["levels"]:
# Search for __identifier for the level layout
level_name = ""
level_metadata = level['fieldInstances']
level_tileset = 0;
for data in level_metadata:
if data["__identifier"] == "TileSet":
level_tileset = data["__value"]
if data["__identifier"] == "Name":
level_name = data["__value"]
level_name = level["identifier"]
print("Parsing level", level_name)
level_layout = {}
entity_layout = {}
water_layout = {}
@ -112,20 +94,13 @@ with open(converted_filename, 'wb+') as out_file:
# Dimensions of each level is obtained via __cWid and __cHei. Get the __gridSize as well
width = level_layout["__cWid"]
height = level_layout["__cHei"]
print(f"Dim.: {width}x{height}. N Tiles: {width * height}")
print(f"Dim.: {width}x{height}")
# Create a W x H array of tile information
n_tiles = width * height
tiles_info = [[0,0,0] for _ in range(n_tiles)]
# Loop through gridTiles, get "d" as the index to fill the info
for i, tile in enumerate(level_layout["gridTiles"]):
try:
for tile in level_layout["gridTiles"]:
tiles_info[tile["d"][0]][0] = ids_tiletype_map[tile["t"]]
if tiles_info[tile["d"][0]][0] == ENUMIDS_TILETYPE_MAPPING ["Chest"]:
n_chests += 1
except Exception as e:
print("Error on tile", i, i % width, i // height)
print(e)
tiles_info[tile["d"][0]][0] = 0
for i, water_level in enumerate(water_layout["intGridCsv"]):
tiles_info[i][2] = water_level
@ -134,23 +109,14 @@ with open(converted_filename, 'wb+') as out_file:
for ent in entity_layout["entityInstances"]:
x,y = ent["__grid"]
tiles_info[y*width + x][0] = ENUMIDS_TILETYPE_MAPPING[ent["__identifier"]]
if ent["__identifier"] == "Urchin":
spd_encoding = 0
for urchin_data in ent['fieldInstances']:
if urchin_data["__identifier"] == "Direction":
spd_encoding |= urchin_data["__value"] << 2
elif urchin_data["__identifier"] == "SpeedLevel":
spd_encoding |= urchin_data["__value"]
tiles_info[y*width + x][0] += spd_encoding
out_file.write(struct.pack("<32s4H", level_name.encode('utf-8'), width, height, n_chests, level_tileset))
out_file.write(struct.pack("<32s2H", level_name.encode('utf-8'), width, height))
for tile in tiles_info:
out_file.write(struct.pack("<3Bx", *tile))
#for y in range(height):
# for x in range(width):
# print(tiles_info[y*width + x], end=" ")
# print()
for y in range(height):
for x in range(width):
print(tiles_info[y*width + x], end=" ")
print()

View File

@ -1,257 +0,0 @@
import sys
import argparse
import pprint
import json
import struct
from PIL import Image, ImageDraw
parser = argparse.ArgumentParser()
parser.add_argument('filename')
args = parser.parse_args()
print("Rendering", args.filename)
with open(args.filename, 'r') as f:
level_pack_data = json.load(f)
#pprint.pprint(level_pack_data)
ENUMIDS_TILETYPE_MAPPING = {
'Solid': 1,
'WoodenPlat': 2,
'Ladder': 3,
'LSpike': 4,
'RSpike': 5,
'USpike': 6,
'DSpike': 7,
'EmptyWCrate': 8,
'LArrowWCrate': 9,
'RArrowWCrate': 10,
'UArrowWCrate': 11,
'DArrowWCrate': 12,
'BombWCrate': 13,
'EmptyMCrate': 14,
'LArrowMCrate': 15,
'RArrowMCrate': 16,
'UArrowMCrate': 17,
'DArrowMCrate': 18,
'BombMCrate': 19,
'Boulder': 20,
'Runner': 21,
'Player': 22,
'Chest': 23,
'Exit': 24,
'Urchin': 25,
}
DANGER_COLOUR = (239,79,81,255)
WCRATE_COLOUR = (160,117,48,255)
MCRATE_COLOUR = (110,110,110,255)
WATER_COLOUR = (0,0,128,64)
REC_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x,y), (x+s-1, y+s-1)), c)
RECLINE_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x,y), (x+s-1, y+s-1)), fill=None, outline=c)
UPHALFREC_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x,y), (x+s-1, y+s//2-1)), c)
DOWNHALFREC_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x, y+s//2), (x+s-1,y+s-1)), c)
LEFTHALFREC_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x,y), (x+s//2-1, y+s-1)), c)
RIGHTHALFREC_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.rectangle(((x+s//2,y), (x+s-1, y+s-1)), c)
CIRCLE_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.circle((x+s//2, y+s//2), s//2, c)
TRIANGLE_DRAW_FUNCTION = lambda ctx,x,y,s,c : ctx.regular_polygon(((x+s//2, y+s//2), s//2), 3, 0, c)
def draw_bomb_tile(ctx, x, y, s, c):
REC_DRAW_FUNCTION(ctx, x, y, s, c)
ctx.line((x,y,x+s-1,y+s-1), DANGER_COLOUR, 1)
ctx.line((x,y+s-1,x+s-1,y), DANGER_COLOUR, 1)
def draw_arrow_tile(ctx, x, y, s, c, d):
REC_DRAW_FUNCTION(ctx, x, y, s, c)
if d == 0:
ctx.line((x+s//2,y+s//2,x+s//2,y), DANGER_COLOUR, 1)
elif d == 1:
ctx.line((x+s//2,y+s//2,x+s-1,y+s//2), DANGER_COLOUR, 1)
elif d == 2:
ctx.line((x+s//2,y+s//2,x+s//2,y+s-1), DANGER_COLOUR, 1)
elif d == 3:
ctx.line((x+s//2,y+s//2,x,y+s//2), DANGER_COLOUR, 1)
def draw_urchin_tile(ctx, x, y, s, c):
ctx.line((x,y,x+s-1,y+s-1), c, 1)
ctx.line((x,y+s-1,x+s-1,y), c, 1)
ctx.line((x+(s-1)//2,y,x+(s-1)//2,y+s-1), c, 1)
ctx.line((x,y+(s-1)//2,x+s-1,y+(s-1)//2), c, 1)
TILETYPE_SHAPE_MAP = {
'Solid': (REC_DRAW_FUNCTION, (0,0,0,255)),
'WoodenPlat': (UPHALFREC_DRAW_FUNCTION, (128,64,0,255)),
'Ladder': (RECLINE_DRAW_FUNCTION, (214,141,64,255)),
'LSpike': (LEFTHALFREC_DRAW_FUNCTION, DANGER_COLOUR),
'RSpike': (RIGHTHALFREC_DRAW_FUNCTION, DANGER_COLOUR),
'USpike': (UPHALFREC_DRAW_FUNCTION, DANGER_COLOUR),
'DSpike': (DOWNHALFREC_DRAW_FUNCTION, DANGER_COLOUR),
'EmptyWCrate': (REC_DRAW_FUNCTION, WCRATE_COLOUR),
'LArrowWCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,3), WCRATE_COLOUR),
'RArrowWCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,1), WCRATE_COLOUR),
'UArrowWCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,0), WCRATE_COLOUR),
'DArrowWCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,2), WCRATE_COLOUR),
'BombWCrate': (draw_bomb_tile, WCRATE_COLOUR),
'EmptyMCrate': (REC_DRAW_FUNCTION, MCRATE_COLOUR),
'LArrowMCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,3), MCRATE_COLOUR),
'RArrowMCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,1), MCRATE_COLOUR),
'UArrowMCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,0), MCRATE_COLOUR),
'DArrowMCrate': (lambda ctx,x,y,s,c : draw_arrow_tile(ctx,x,y,s,c,2), MCRATE_COLOUR),
'BombMCrate': (draw_bomb_tile, MCRATE_COLOUR),
'Boulder': (CIRCLE_DRAW_FUNCTION, (45,45,45,255)),
'Runner': (TRIANGLE_DRAW_FUNCTION, (0,0,128,255)),
'Player': (REC_DRAW_FUNCTION, (255,0,255,255)),
'Chest': (REC_DRAW_FUNCTION, (255,255,0,255)),
'Exit': (REC_DRAW_FUNCTION, (0,255,0,255)),
'Urchin': (draw_urchin_tile, DANGER_COLOUR),
}
# First go to tilesets and find Simple_tiles identifier, then find enumTags to identifier which tile type is what tileid
ids_tiletype_map = {}
tileset_defs = level_pack_data["defs"]["tilesets"]
for ts_def in tileset_defs:
if ts_def["identifier"] != "Items_spritesheet":
continue
for tag in ts_def["enumTags"]:
ids_tiletype_map[tag["tileIds"][0]] = tag["enumValueId"]
if not ids_tiletype_map:
print("No tileset definition")
sys.exit(1)
pprint.pprint(ids_tiletype_map)
def get_level_order(lvl) -> int:
order = 65535;
for data in lvl['fieldInstances']:
if data["__identifier"] == "Order" and data["realEditorValues"]:
order = data["__value"]
return order
all_levels = level_pack_data["levels"]
all_levels.sort(key=get_level_order)
# Number of levels is the length of the levels
n_levels = len(all_levels)
print("Number of levels:", n_levels)
fileparts = args.filename.split('.')
if len(fileparts) == 1:
fileparts.append("lvldat")
else:
fileparts[-1] = "lvldata"
converted_filename = '.'.join(fileparts)
# First run the entire level pack to figure out the largest dimensions required.
# tile size needs to be dynamic. Fix the output dimensions (and therefore aspect ratio)
# figure out width and height of level
# Get the tile size that fit within the dimensions
# Error out if tile size is zero
# Figure out the offset to center the render
LEVELS_PER_ROW = 5
N_ROWS = n_levels // LEVELS_PER_ROW
if n_levels % LEVELS_PER_ROW != 0:
N_ROWS += 1
IMG_WIDTH = 400
IMG_HEIGHT = 400
FULL_IMG_WIDTH = LEVELS_PER_ROW * IMG_WIDTH
FULL_IMG_HEIGHT = N_ROWS * IMG_HEIGHT
# Each level should be packed as: [width, 2 bytes][height, 2 bytes][tile_type,entity,water,padding 1,1,1,1 bytes][tile_type,entity,water,padding 1,1,1,1 bytes]...
# Then loop the levels. Read the layerIndstances
lvl_render = Image.new("RGBA", (FULL_IMG_WIDTH, FULL_IMG_HEIGHT), (255,255,255,0))
water_render = Image.new("RGBA", (FULL_IMG_WIDTH, FULL_IMG_HEIGHT), (255,255,255,0))
render_ctx = ImageDraw.Draw(lvl_render)
water_render_ctx = ImageDraw.Draw(water_render)
for l, level in enumerate(all_levels):
lx = (l % LEVELS_PER_ROW) * IMG_WIDTH
ly = (l // LEVELS_PER_ROW) * IMG_HEIGHT
n_chests : int = 0
# Search for __identifier for the level layout
level_name = ""
level_metadata = level['fieldInstances']
level_tileset = 0;
for data in level_metadata:
if data["__identifier"] == "TileSet":
level_tileset = data["__value"]
if data["__identifier"] == "Name":
level_name = data["__value"]
print("Parsing level", level_name)
level_layout = {}
entity_layout = {}
water_layout = {}
for layer in level['layerInstances']:
if layer["__identifier"] == "Tiles":
level_layout = layer
elif layer["__identifier"] == "Entities":
entity_layout = layer
elif layer["__identifier"] == "Water":
water_layout = layer
# Dimensions of each level is obtained via __cWid and __cHei. Get the __gridSize as well
width = level_layout["__cWid"]
height = level_layout["__cHei"]
print(f"Dim.: {width}x{height}. N Tiles: {width * height}")
TILE_SIZE = 400 // max(width, height)
TILE_SIZE = max(TILE_SIZE,1)
# Create a W x H array of tile information
n_tiles = width * height
LVL_SIZE = (width*TILE_SIZE-1, height*TILE_SIZE-1)
OFFSET = ((IMG_WIDTH - LVL_SIZE[0]) // 2 + lx, (IMG_HEIGHT - LVL_SIZE[1])//2 + ly)
render_ctx.rectangle((OFFSET, (OFFSET[0]+width*TILE_SIZE-1, OFFSET[1] + height*TILE_SIZE-1)), (64,64,64,255))
# Loop through gridTiles, get "d" as the index to fill the info
for i, tile in enumerate(level_layout["gridTiles"]):
x, y= (tile["d"][0] % width * TILE_SIZE + OFFSET[0], tile["d"][0] // width * TILE_SIZE + OFFSET[1])
try:
if ids_tiletype_map[tile["t"]] in TILETYPE_SHAPE_MAP:
draw_func, colour = TILETYPE_SHAPE_MAP[ids_tiletype_map[tile["t"]]]
draw_func(render_ctx, x, y, TILE_SIZE, colour)
else:
print(ids_tiletype_map[tile["t"]], "is not mapped");
except Exception as e:
print("Error on tile", x, y)
render_ctx.circle((x,y), 2,(255,0,0,255))
print(e)
## Subject to change
for ent in entity_layout["entityInstances"]:
x,y = ent["__grid"]
x *= TILE_SIZE
x += OFFSET[0]
y *= TILE_SIZE
y += OFFSET[1]
ent = ent["__identifier"]
try:
if ent in TILETYPE_SHAPE_MAP:
draw_func, colour = TILETYPE_SHAPE_MAP[ent]
draw_func(render_ctx, x, y, TILE_SIZE, colour)
else:
print(ent, "is not mapped");
except Exception as e:
print("Error on tile", x, y)
render_ctx.circle((x,y), 2,(255,0,0,255))
print(e)
for i, water_level in enumerate(water_layout["intGridCsv"]):
if water_level == 0:
continue
x, y = (i % width * TILE_SIZE + OFFSET[0], i // width * TILE_SIZE + OFFSET[1])
height = TILE_SIZE * water_level / 4
water_render_ctx.rectangle(((x,y+TILE_SIZE-height), (x+TILE_SIZE-1, y+TILE_SIZE-1)), WATER_COLOUR)
lvl_render = Image.alpha_composite(lvl_render, water_render)
lvl_render.save("preview.png")
lvl_render.show()

View File

@ -1,3 +0,0 @@
#!/bin/bash
source venv/bin/activate
python ldtk_repacker.py $1.ldtk && zstd $1.lvldata && python level_render.py $1.ldtk

View File

@ -1,6 +0,0 @@
#!/bin/bash
sed 's|res/||g' assets.info.raw > assets.info
./rres_packer
mv ../web/res/myresources.rres ../web/res/myresources.rres.old
cp ./myresources.rres ../web/res/

View File

@ -1 +0,0 @@
Pillow

View File

@ -109,7 +109,7 @@ int main(void)
for (uint8_t i = 0; i < 6; ++i)
{
scenes[i] = &dummy_scenes[i].scene;
init_scene(&dummy_scenes[i].scene, &level_do_action, 0);
init_scene(&dummy_scenes[i].scene, &level_do_action);
dummy_scenes[i].scene.engine = &engine;
dummy_scenes[i].number = i;
add_scene_layer(

View File

@ -1,6 +1,3 @@
#ifdef TRACY_ENABLE
#include "tracy/TracyC.h"
#endif
#include "scene_impl.h"
#include "ent_impl.h"
#include "assets_loader.h"
@ -64,9 +61,6 @@ int main(void)
load_sfx(&engine, "snd_cland", WOOD_LAND_SFX);
load_sfx(&engine, "snd_explsn", EXPLOSION_SFX);
load_sfx(&engine, "snd_coin", COIN_SFX);
load_sfx(&engine, "snd_step", PLAYER_STEP_SFX);
load_sfx(&engine, "snd_dead", PLAYER_DEAD_SFX);
load_sfx(&engine, "snd_drwg", PLAYER_DROWNING_SFX);
load_sfx(&engine, "snd_arrhit", ARROW_DESTROY_SFX);
load_sfx(&engine, "snd_launch", ARROW_RELEASE_SFX);
load_sfx(&engine, "snd_launch", BOMB_RELEASE_SFX);
@ -95,22 +89,10 @@ int main(void)
float frame_time = GetFrameTime();
float delta_time = fminf(frame_time, DT);
#ifdef TRACY_ENABLE
TracyCZoneN(ctx, "Overall", true)
#endif
#ifdef TRACY_ENABLE
TracyCZoneN(ctx1, "Update", true)
#endif
update_scene(&scene.scene, delta_time);
update_entity_manager(&scene.scene.ent_manager);
#ifdef TRACY_ENABLE
TracyCZoneEnd(ctx1)
#endif
// This is needed to advance time delta
render_scene(&scene.scene);
#ifdef TRACY_ENABLE
TracyCZoneEnd(ctx)
#endif
update_sfx_list(&engine);
if (WindowShouldClose()) break;
}

View File

@ -9,23 +9,11 @@ add_library(lib_scenes STATIC
game_scene.c
game_systems.c
scene_systems.c
camera_systems.c
engine_impl.c
)
target_include_directories(lib_scenes
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/../tracy/public/
)
if (${RUN_PROFILER})
target_compile_definitions(lib_scenes
PUBLIC
TRACY_ENABLE
TRACY_ON_DEMAND
)
endif()
target_link_libraries(lib_scenes
PUBLIC
lib_engine

View File

@ -7,7 +7,6 @@ typedef enum AssetInfoType
TEXTURE_INFO,
SPRITE_INFO,
SOUND_INFO,
FONT_INFO,
EMITTER_INFO,
LEVELPACK_INFO,
INVALID_INFO
@ -81,8 +80,6 @@ static inline AssetInfoType_t get_asset_type(const char* str)
if (strcmp(str, "LevelPack") == 0) return LEVELPACK_INFO;
if (strcmp(str, "Font") == 0) return FONT_INFO;
return INVALID_INFO;
}
@ -275,22 +272,12 @@ bool load_from_infofile(const char* file, Assets_t* assets)
{
if (add_sound(assets, name, info_str) == NULL)
{
printf("Unable to add sound at line %lu\n", line_num);
printf("Unable to add texture at line %lu\n", line_num);
break;
}
printf("Added sound %s as %s\n", info_str, name);
}
break;
case FONT_INFO:
{
if (add_font(assets, name, info_str) == NULL)
{
printf("Unable to add font at line %lu\n", line_num);
break;
}
printf("Added font %s as %s\n", info_str, name);
}
break;
case LEVELPACK_INFO:
{
//if (add_level_pack(assets, name, info_str) == NULL)

View File

@ -11,7 +11,6 @@ typedef enum EntityTag {
LEVEL_END_TAG,
DESTRUCTABLE_ENT_TAG,
DYNMEM_ENT_TAG,
URCHIN_ENT_TAG,
} EntityTag_t;
typedef enum SFXTag {
@ -20,9 +19,6 @@ typedef enum SFXTag {
PLAYER_RUN_SFX,
PLAYER_LADDER_SFX,
PLAYER_WATER_RUN_SFX,
PLAYER_STEP_SFX,
PLAYER_DEAD_SFX,
PLAYER_DROWNING_SFX,
WATER_IN_SFX,
WATER_OUT_SFX,
WOOD_LAND_SFX,
@ -39,11 +35,9 @@ typedef enum SFXTag {
COIN_SFX,
} SFXTag_t;
typedef enum SceneType {
MAIN_MENU_SCENE = 0,
LEVEL_SELECT_SCENE,
GAME_SCENE,
SANDBOX_SCENE,
}SceneType_t;
//typedef enum SceneType {
// LEVEL_SCENE = 0,
// MENU_SCENE,
//}SceneType_t;
#endif

View File

@ -1,102 +0,0 @@
#include "game_systems.h"
#include "ent_impl.h"
#include "raymath.h"
void camera_update_system(Scene_t* scene)
{
LevelSceneData_t* data = &CONTAINER_OF(scene, LevelScene_t, scene)->data;
Entity_t* p_player;
const int width = data->game_rec.width;
const int height =data->game_rec.height;
data->camera.cam.offset = (Vector2){ width/2.0f, height/2.0f };
if (data->camera.mode == CAMERA_FOLLOW_PLAYER)
{
Vector2 target_vel = {0};
sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_player)
{
CTransform_t* p_ctransform = get_component(p_player, CTRANSFORM_COMP_T);
data->camera.target_pos.x = p_player->position.x;
target_vel = p_ctransform->velocity;
CMovementState_t* p_movement = get_component(p_player, CMOVEMENTSTATE_T);
CPlayerState_t* p_pstate = get_component(p_player, CPLAYERSTATE_T);
data->camera.target_pos.x += (p_movement->x_dir == 1) ? width/6: -width/6;
if (p_movement->ground_state == 0b01
|| (p_movement->water_state & 1)
|| (p_pstate->ladder_state & 1)
)
{
data->camera.base_y = p_player->position.y;
}
if (p_player->position.y >= data->camera.base_y)
{
data->camera.target_pos.y = p_player->position.y;
data->camera.target_pos.y += p_ctransform->velocity.y * 0.2;
}
}
data->camera.target_pos.x = Clamp(data->camera.target_pos.x, data->game_rec.width / 2,
fmax(data->tilemap.width * data->tilemap.tile_size, data->game_rec.width) - data->game_rec.width / 2);
data->camera.target_pos.y = Clamp(data->camera.target_pos.y, data->game_rec.height / 2,
fmax(data->tilemap.height * data->tilemap.tile_size, data->game_rec.width) - data->game_rec.height / 2);
// Mass-Spring damper update in x direction
float x = data->camera.target_pos.x - data->camera.cam.target.x;
float v = data->camera.current_vel.x - target_vel.x;
float F = data->camera.k * x - data->camera.c * v;
// Kinematics update
const float dt = scene->delta_time;
float a_dt = F * dt / data->camera.mass;
data->camera.cam.target.x += (data->camera.current_vel.x + a_dt * 0.5) * dt;
data->camera.current_vel.x += a_dt;
// Simple lerp for y direction
float dy = (data->camera.target_pos.y - data->camera.cam.target.y);
data->camera.cam.target.y += dy * 0.1f;
}
else
{
//sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_player)
//{
// break;
//}
}
// Bound camera within level limits
Vector2 max = GetWorldToScreen2D(
(Vector2){
fmax(data->tilemap.width * data->tilemap.tile_size, data->game_rec.width),
fmax(data->tilemap.height * data->tilemap.tile_size, data->game_rec.height)
},
data->camera.cam
);
Vector2 min = GetWorldToScreen2D((Vector2){0, 0}, data->camera.cam);
if (max.x < width)
{
//data->camera.cam.offset.x = width - (max.x - width/2.0f);
data->camera.cam.target.x = fmax(data->tilemap.width * data->tilemap.tile_size, data->game_rec.width) - (width >> 1);
data->camera.current_vel.x = 0;
}
if (min.x > 0)
{
//data->camera.cam.offset.x = (width >> 1) - min.x;
data->camera.cam.target.x = (width >> 1);
data->camera.current_vel.x = 0;
}
if (max.y < height)
{
//data->camera.cam.offset.y = height - (max.y - height/2.0f);
data->camera.cam.target.y = fmax(data->tilemap.height * data->tilemap.tile_size, data->game_rec.height) - (height >> 1);
data->camera.current_vel.y = 0;
}
if (min.y > 0)
{
//data->camera.cam.offset.y = (height >> 1) - min.y;
data->camera.cam.target.y = (height >> 1);
data->camera.current_vel.y = 0;
}
}

View File

@ -1,180 +0,0 @@
#include "EC.h"
#include "engine.h"
#include "render_queue.h"
#include "particle_sys.h" // includes assets
enum ComponentEnum {
CMOVEMENTSTATE_T = N_BASIC_COMPS,
CJUMP_COMP_T,
CPLAYERSTATE_T,
CCONTAINER_T,
CHITBOXES_T,
CHURTBOX_T,
CSPRITE_T,
CMOVEABLE_T,
CLIFETIMER_T,
CWATERRUNNER_T,
CAIRTIMER_T,
CEMITTER_T,
CSQUISHABLE_T
};
typedef struct _CMovementState_t {
uint8_t ground_state;
uint8_t water_state;
uint8_t x_dir;
float water_overlap;
} CMovementState_t;
typedef struct _CJump_t {
int jump_speed;
uint8_t jumps;
uint8_t max_jumps;
uint8_t coyote_timer;
bool jumped;
bool jump_ready;
bool short_hop;
bool jump_released;
} CJump_t;
typedef enum PlayerState {
GROUNDED,
AIR,
} PlayerState_t;
typedef struct _CPlayerState_t {
Vector2 player_dir;
uint8_t jump_pressed;
uint8_t is_crouch;
bool ladder_state;
bool locked; // Whether to respond to inputs
} CPlayerState_t;
typedef enum ContainerItem {
CONTAINER_EMPTY,
CONTAINER_LEFT_ARROW,
CONTAINER_RIGHT_ARROW,
CONTAINER_UP_ARROW,
CONTAINER_DOWN_ARROW,
CONTAINER_COIN,
CONTAINER_BOMB,
CONTAINER_EXPLOSION,
} ContainerItem_t;
typedef enum ContainerMaterial {
WOODEN_CONTAINER,
METAL_CONTAINER,
} ContainerMaterial_t;
typedef struct _CContainer_t {
ContainerMaterial_t material;
ContainerItem_t item;
} CContainer_t;
typedef struct _CHitBoxes_t {
Rectangle boxes[2];
uint8_t n_boxes;
uint8_t atk;
bool one_hit;
} CHitBoxes_t;
typedef struct _CHurtbox_t {
Vector2 offset;
Vector2 size;
uint8_t def;
unsigned int damage_src;
} CHurtbox_t;
typedef struct _CLifeTimer_t {
float life_time;
} CLifeTimer_t;
typedef struct _CAirTimer_t {
float max_ftimer;
float curr_ftimer;
float decay_rate;
uint8_t max_count;
uint8_t curr_count;
} CAirTimer_t;
typedef struct _BFSTile {
int32_t to;
int32_t from;
bool reachable;
}BFSTile_t;
typedef struct _BFSTileMap {
BFSTile_t* tilemap;
int32_t width;
int32_t height;
int32_t len;
}BFSTileMap_t;
typedef enum _WaterRunnerState
{
BFS_RESET = 0,
BFS_START,
LOWEST_POINT_SEARCH,
LOWEST_POINT_MOVEMENT,
REACHABILITY_SEARCH,
SCANLINE_FILL,
FILL_COMPLETE,
}WaterRunerState_t;
typedef struct _CWaterRunner {
BFSTileMap_t bfs_tilemap;
WaterRunerState_t state;
struct sc_queue_32 bfs_queue;
bool* visited;
int32_t current_tile;
int32_t target_tile;
int32_t fill_idx;
int16_t fill_range[2];
uint8_t movement_delay;
int8_t movement_speed;
int16_t counter;
float fractional;
}CWaterRunner_t;
typedef unsigned int (*sprite_transition_func_t)(Entity_t *ent); // Transition requires knowledge of the entity
typedef void (*sprite_sfx_func_t)(GameEngine_t*, Entity_t *ent); // Transition requires knowledge of the entity
typedef struct _SpriteRenderInfo
{
Sprite_t* sprite;
Vector2 offset;
AnchorPoint_t src_anchor;
AnchorPoint_t dest_anchor;
} SpriteRenderInfo_t;
typedef struct _CSprite_t {
RenderInfoNode node;
SpriteRenderInfo_t* sprites;
sprite_transition_func_t transition_func;
sprite_sfx_func_t sfx_func;
unsigned int current_idx;
int current_frame;
float fractional;
float rotation_speed; // Degree / s
int elapsed;
Vector2 offset;
uint8_t depth;
bool pause;
} CSprite_t;
typedef struct _CMoveable_t {
uint16_t move_speed;
Vector2 prev_pos;
Vector2 target_pos;
bool gridmove;
} CMoveable_t;
typedef struct _CEmitter_t
{
EmitterHandle handle;
Vector2 offset;
} CEmitter_t;
typedef struct _CSquishable_t {
bool active; //< Dummy variable
} CSquishable_t;

View File

@ -1,16 +1,12 @@
// Constants to be used in game
#ifndef __CONSTANTS_H
#define __CONSTANTS_H
#include <stdint.h>
#ifndef TILE16_SIZE
#define TILE_SIZE 32
#define DEFAULT_MAP_WIDTH 48
#define DEFAULT_MAP_HEIGHT 32
#define VIEWABLE_EDITOR_MAP_WIDTH 32
#define VIEWABLE_EDITOR_MAP_HEIGHT 16
#define VIEWABLE_MAP_WIDTH 25
#define VIEWABLE_MAP_HEIGHT 14
#define VIEWABLE_MAP_WIDTH 32
#define VIEWABLE_MAP_HEIGHT 16
#else
#define TILE_SIZE 16
#define DEFAULT_MAP_WIDTH 64
@ -21,13 +17,13 @@
#define DELTA_T 0.017
#define GRAV_ACCEL 1500
#define JUMP_SPEED 600
#define JUMP_SPEED 70
#define MOVE_ACCEL 1300
#ifndef TILE16_SIZE
#define PLAYER_WIDTH 22
#define PLAYER_WIDTH 28
#define PLAYER_HEIGHT 42
#define PLAYER_C_WIDTH 30
#define PLAYER_C_WIDTH 28
#define PLAYER_C_HEIGHT 26
#else
#define PLAYER_WIDTH 14
@ -43,17 +39,12 @@
#define GROUND_X_FRICTION 6.1
#define GROUND_Y_FRICTION 1.0
#define ARROW_SPEED 400
#define ARROW_SPEED 350
#define MAX_WATER_LEVEL 4
#define WATER_BBOX_STEP (TILE_SIZE / MAX_WATER_LEVEL)
#define MAX_LEVEL_NUM 50
#define MAX_N_TILES 4096
static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
0,3,15,14,
1,2,12,13,
7,6,11,10,
4,5,8 ,9 ,
};
#endif // __CONSTANTS_H

View File

@ -1,4 +1,3 @@
#include "EC.h"
#include "scene_impl.h"
#include "game_systems.h"
#include "water_flow.h"
@ -29,34 +28,29 @@ enum EntitySpawnSelection {
SPAWN_BOULDER,
SPAWN_WATER_RUNNER,
SPAWN_LEVEL_END,
SPAWN_URCHIN,
};
#define MAX_SPAWN_TYPE 17
#define MAX_SPAWN_TYPE 16
static unsigned int current_spawn_selection = 0;
static bool metal_toggle = false;
static bool crate_activation = false;
#define MAX_URCHIN_SPAWN_SPD 20
#define URCHIN_SPAWN_UI_RADIUS 52
#define URCHIN_SPAWN_UI_DIVISION 13
#define URCHIN_SPAWN_UI_N (URCHIN_SPAWN_UI_RADIUS / URCHIN_SPAWN_UI_DIVISION)
#define URCHIN_SPAWN_ANGLE_DIVISION 45
#define URCHIN_SPAWN_UI_ANGLE_N (360 / URCHIN_SPAWN_ANGLE_DIVISION)
#define URCHIN_VELOCITY_SCALE 8
static Vector2 urchin_spawn_vec = {0,0};
static Vector2 urchin_click_pos = {0,0};
#define SELECTION_TILE_SIZE 32
#define SELECTION_TILE_HALFSIZE (SELECTION_TILE_SIZE >> 1)
#define SELECTION_GAP 5
#define SELECTION_REGION_WIDTH (SELECTION_TILE_SIZE * MAX_SPAWN_TYPE)
#define SELECTION_REGION_HEIGHT SELECTION_TILE_SIZE
#define GAME_LAYER 2
#define SELECTION_LAYER 0
#define CONTROL_LAYER 1
#define GAME_LAYER 0
#define SELECTION_LAYER 1
#define CONTROL_LAYER 2
static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
0,3,15,14,
1,2,12,13,
7,6,11,10,
4,5,8 ,9 ,
};
#define N_SOLID_TILESETS 3
static const char* SOLID_TILE_SELECTIONS[N_SOLID_TILESETS] = {
@ -83,7 +77,6 @@ static char* get_spawn_selection_string(enum EntitySpawnSelection sel)
case SPAWN_BOULDER: return "boulder";
case SPAWN_WATER_RUNNER: return "water runner";
case SPAWN_LEVEL_END: return "level end";
case SPAWN_URCHIN: return "urchin";
default: return "unknown";
}
}
@ -103,20 +96,6 @@ static inline unsigned int get_tile_idx(int x, int y, const TileGrid_t* tilemap)
return MAX_N_TILES;
}
static int round_to_nearest(int in_val, int divider)
{
if (divider == 0) return in_val;
int remainder = in_val % divider;
int dividend = in_val / divider;
if (remainder > (divider >> 1))
{
dividend++;
}
return dividend * divider;
}
// This means you might be able to have two editor scene without running into problems
#define SELECTION_RENDER_HEIGHT (SELECTION_REGION_HEIGHT * 3)
static void level_scene_render_func(Scene_t* scene)
@ -131,95 +110,21 @@ static void level_scene_render_func(Scene_t* scene)
Vector2 draw_pos = {game_rec.x, game_rec.y + game_rec.height + SELECTION_GAP};
BeginTextureMode(scene->layers.render_layers[CONTROL_LAYER].layer_tex);
ClearBackground(BLANK);
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
DrawRectangle(game_rec.x - 3, game_rec.y - 3, game_rec.width + 6, game_rec.height + 6, RED);
}
DrawRectangleLines(
selection_rec.x + current_spawn_selection * SELECTION_TILE_SIZE, selection_rec.y,
SELECTION_TILE_SIZE, SELECTION_TILE_SIZE, GREEN
);
DrawText("R to reset the map, Q/E to cycle the selection,\nF to toggle metal crates. T to toggle crate spawn behaviour\nZ to change tileset, X to toggle grid\nC to set spawn point, V to toggle free cam, B to toggle slowmo", selection_rec.x, selection_rec.y + selection_rec.height + 2, 14, BLACK);
DrawText("R to reset the map, Q/E to cycle the selection,\nF to toggle metal crates. T to toggle crate spawn behaviour\nB to toggle grid, V to set spawn point", selection_rec.x, selection_rec.y + selection_rec.height + 2, 14, BLACK);
draw_pos.x = game_rec.x + (MAX_SPAWN_TYPE + 1) * SELECTION_TILE_SIZE;
sprintf(buffer, "%s", get_spawn_selection_string(current_spawn_selection));
sprintf(buffer, "Selection: %s", get_spawn_selection_string(current_spawn_selection));
DrawText(buffer, draw_pos.x, draw_pos.y, 20, BLACK);
draw_pos.y += 20 + 5;
draw_pos.y += SELECTION_TILE_SIZE + 5;
sprintf(buffer, "Crate %s on spawn", crate_activation? "active" : "inactive");
DrawText(buffer, draw_pos.x, draw_pos.y, 20, BLACK);
draw_pos.y += 20 + 5;
draw_pos.y += SELECTION_TILE_SIZE + 5;
sprintf(buffer, "Time scale: %.2f", scene->time_scale);
DrawText(buffer, draw_pos.x, draw_pos.y, 20, BLACK);
draw_pos.y += 20 + 5;
sprintf(buffer, "Camera mode: %s", data->camera.mode == CAMERA_FOLLOW_PLAYER ? "FOLLOW" : "FREE");
DrawText(buffer, draw_pos.x, draw_pos.y, 20, BLACK);
draw_pos.x = game_rec.x + game_rec.width - 50 - URCHIN_SPAWN_UI_RADIUS;
draw_pos.y = game_rec.y + game_rec.height + SELECTION_GAP + URCHIN_SPAWN_UI_RADIUS;
// Draw Urchin spawn info
DrawCircleV(draw_pos, URCHIN_SPAWN_UI_RADIUS, RED);
for (unsigned int i = 0; i <= URCHIN_SPAWN_UI_N; ++i)
{
DrawCircleLinesV(draw_pos, i * URCHIN_SPAWN_UI_DIVISION, BLACK);
}
for (unsigned int i = 0; i < URCHIN_SPAWN_UI_ANGLE_N; ++i)
{
float angle = i * URCHIN_SPAWN_ANGLE_DIVISION * PI / 180;
Vector2 draw_end = Vector2Scale(
(Vector2){cosf(angle), sinf(angle)}, URCHIN_SPAWN_UI_RADIUS
);
draw_end = Vector2Add(draw_end, draw_pos);
DrawLineEx(draw_pos, draw_end, 1, BLACK);
}
// HACK: because this ui is not stored, need to perform the mouse check here
bool to_snap = false;
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
{
Vector2 tmp = Vector2Subtract(
scene->mouse_pos, draw_pos
);
float mag = Vector2Length(tmp);
if (mag <= URCHIN_SPAWN_UI_RADIUS)
{
urchin_click_pos = Vector2Scale(
Vector2Normalize(tmp), mag
);
}
else
{
to_snap = true;
}
}
else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
{
to_snap = true;
}
if (to_snap)
{
float mag = Vector2Length(urchin_click_pos);
float dir = atan2f(urchin_click_pos.y, urchin_click_pos.x);
dir *= 180 / PI;
dir += dir < 0 ? 360 : 0;
mag = round_to_nearest((int)mag, URCHIN_SPAWN_UI_DIVISION);
dir = round_to_nearest((int)dir, URCHIN_SPAWN_ANGLE_DIVISION);
dir *= PI / 180;
urchin_spawn_vec = Vector2Scale(
(Vector2){cosf(dir), sinf(dir)}, mag
);
urchin_click_pos = urchin_spawn_vec;
}
DrawCircleV(
Vector2Add(urchin_click_pos, draw_pos),
4, BLUE
);
// For DEBUG
const int gui_x = game_rec.x + game_rec.width + 10;
@ -253,6 +158,14 @@ static void level_scene_render_func(Scene_t* scene)
gui_y += 30;
#endif
CAirTimer_t* p_air = get_component(p_ent, CAIRTIMER_T);
Vector2 air_pos = {game_rec.x + game_rec.width - 16, game_rec.y + game_rec.height - 16};
for (uint8_t i = 0; i < p_air->curr_count; i++)
{
DrawCircleV(air_pos, 16, BLUE);
air_pos.x -= 32;
}
}
//sprintf(buffer, "Spawn Entity: %s", get_spawn_selection_string(current_spawn_selection));
//DrawText(buffer, gui_x, 240, 12, BLACK);
@ -295,76 +208,9 @@ static void render_editor_game_scene(Scene_t* scene)
max.x = (int)fmin(tilemap.width, max.x + 1);
max.y = (int)fmin(tilemap.height, max.y + 1);
// Queue Sprite rendering
sc_map_foreach_value(&scene->ent_manager.entities, p_ent)
{
if (!p_ent->m_alive) continue;
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr == NULL) continue;
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite == NULL) continue;
Vector2 pos = p_ent->position;
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
if (p_bbox != NULL)
{
pos = Vector2Add(
pos,
get_anchor_offset(p_bbox->size, spr.dest_anchor, p_cspr->node.flip & 1)
);
}
pos = Vector2Subtract(
pos,
get_anchor_offset(spr.sprite->frame_size, spr.src_anchor, p_cspr->node.flip & 1)
);
Vector2 offset = spr.offset;
if (p_cspr->node.flip & 1) offset.x *= -1;
pos = Vector2Add(pos, offset);
// Entity culling
if (
pos.x + spr.sprite->frame_size.x < min.x * tilemap.tile_size
|| pos.x > max.x * tilemap.tile_size
|| pos.y + spr.sprite->frame_size.y < min.y * tilemap.tile_size
|| pos.y > max.y * tilemap.tile_size
)
{
continue;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
p_cspr->current_frame = 2 * data->selected_solid_tilemap + (
(data->coins.current < data->coins.total) ? 0 : 1);
}
p_cspr->node.spr = spr.sprite;
p_cspr->node.frame_num = p_cspr->current_frame;
p_cspr->node.pos = pos;
add_render_node(&data->render_manager, &p_cspr->node, p_cspr->depth);
}
Texture2D* bg = get_texture(&scene->engine->assets, "bg_tex");
BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex);
ClearBackground(WHITE);
DrawTexturePro(*bg,
//(Rectangle){0,0,64,64},
(Rectangle){min.x,0,(tilemap.width+1)*tilemap.tile_size*2, (tilemap.height+1)*tilemap.tile_size*2},
(Rectangle){0,0,(tilemap.width+1)*tilemap.tile_size*2, (tilemap.height+1)*tilemap.tile_size*2},
//(Rectangle){0,0,game_rec.width, game_rec.height},
(Vector2){0,0}, 0.0f, WHITE
);
BeginMode2D(data->camera.cam);
if (point_in_AABB(data->player_spawn, (Rectangle){min.x * tilemap.tile_size, min.y * tilemap.tile_size, data->game_rec.width, data->game_rec.height}))
{
DrawCircleV(data->player_spawn, 6, PURPLE);
}
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
@ -373,27 +219,91 @@ static void render_editor_game_scene(Scene_t* scene)
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
if (tilemap.tiles[i].tile_type == LADDER)
//if (!tilemap.tiles[i].moveable)
//{
// // Draw water tile
// Color water_colour = ColorAlpha(RED, 0.2);
// DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, water_colour);
//}
uint8_t tile_sprite_idx = tilemap.tiles[i].tile_type + tilemap.tiles[i].rotation;
if (data->tile_sprites[tile_sprite_idx] != NULL)
{
if (data->tile_sprites[tilemap.tiles[i].tile_type] != NULL)
{
draw_sprite(data->tile_sprites[tilemap.tiles[i].tile_type], 0, (Vector2){x,y}, 0.0f, false);
draw_sprite(data->tile_sprites[tile_sprite_idx], 0, (Vector2){x,y}, 0.0f, false);
}
else
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
draw_sprite(
data->solid_tile_sprites, CONNECTIVITY_TILE_MAPPING[tilemap.tiles[i].connectivity],
(Vector2){x,y}, 0.0f, false
);
//sprintf(buffer, "%u", tilemap.tiles[i].connectivity);
//DrawText(buffer, x + tilemap.tile_size / 2, y + tilemap.tile_size / 2, 12, WHITE);
}
else if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, MAROON);
}
else if (tilemap.tiles[i].tile_type == LADDER)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
}
else if (tilemap.tiles[i].tile_type == SPIKES)
{
DrawRectangle(
x + tilemap.tiles[i].offset.x, y + tilemap.tiles[i].offset.y,
tilemap.tiles[i].size.x, tilemap.tiles[i].size.y, RED
);
}
if (tilemap.tiles[i].wet)
{
#define SURFACE_THICKNESS 4
int up = i - tilemap.width;
unsigned int bot = i + tilemap.width;
int right = i + 1;
int left = i - 1;
int bot_line = y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP - SURFACE_THICKNESS / 2;
if (up >= 0 && tilemap.tiles[up].wet)
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, y}, (Vector2){x + TILE_SIZE / 2, y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (
bot <= tilemap.n_tiles
&& tilemap.tiles[i].water_level == 0
)
{
if (i % tilemap.width != 0 && tilemap.tiles[left].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot-1].solid == SOLID))
{
DrawLineEx((Vector2){x, bot_line}, (Vector2){x + TILE_SIZE / 2, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (right % tilemap.width != 0 && tilemap.tiles[right].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot+1].solid == SOLID))
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, bot_line}, (Vector2){x + TILE_SIZE, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
}
}
if (tilemap.tiles[i].max_water_level < MAX_WATER_LEVEL)
{
DrawRectangleLinesEx((Rectangle){x, y, TILE_SIZE, TILE_SIZE}, 2.0, ColorAlpha(BLUE, 0.5));
}
}
}
char buffer[64] = {0};
if (data->show_grid)
{
sc_map_foreach_value(&scene->ent_manager.entities, p_ent)
{
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
// Draw the spawn point
if (p_ent->m_tag == PLAYER_ENT_TAG)
{
DrawCircleV(p_ent->spawn_pos, 6, PURPLE);
}
// Entity culling
Vector2 box_size = {0};
if (p_bbox != NULL) box_size = p_bbox->size;
@ -407,7 +317,6 @@ static void render_editor_game_scene(Scene_t* scene)
Color colour;
switch(p_ent->m_tag)
{
case NO_ENT_TAG:
case PLAYER_ENT_TAG:
colour = RED;
break;
@ -428,21 +337,6 @@ static void render_editor_game_scene(Scene_t* scene)
break;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
DrawCircleV(p_ent->position, tilemap.tile_size >> 1, (data->coins.current < data->coins.total)? RED : GREEN);
continue;
}
if (p_ent->m_tag == DYNMEM_ENT_TAG)
{
CWaterRunner_t* p_runner = get_component(p_ent, CWATERRUNNER_T);
unsigned int x = ((p_runner->current_tile) % tilemap.width) * tilemap.tile_size;
unsigned int y = ((p_runner->current_tile) / tilemap.width) * tilemap.tile_size;
DrawCircle(x+16, y+16, 8, ColorAlpha(BLUE, 0.6));
}
if (p_bbox != NULL)
{
if (p_ent->m_tag == BOULDER_ENT_TAG)
@ -532,85 +426,21 @@ static void render_editor_game_scene(Scene_t* scene)
};
DrawRectangleLinesEx(rec, 1.5, PURPLE);
}
}
}
execute_render(&data->render_manager);
for (int tile_y = min.y; tile_y < max.y; tile_y++)
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr != NULL)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite != NULL)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
if (tilemap.tiles[i].tile_type != LADDER)
{
uint8_t tile_sprite_idx = tilemap.tiles[i].tile_type + tilemap.tiles[i].rotation;
if (data->tile_sprites[tile_sprite_idx] != NULL)
{
draw_sprite(data->tile_sprites[tile_sprite_idx], 0, (Vector2){x,y}, 0.0f, false);
}
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
draw_sprite(
data->solid_tile_sprites, CONNECTIVITY_TILE_MAPPING[tilemap.tiles[i].connectivity],
(Vector2){x,y}, 0.0f, false
);
//sprintf(buffer, "%u", tilemap.tiles[i].connectivity);
//DrawText(buffer, x + tilemap.tile_size / 2, y + tilemap.tile_size / 2, 12, WHITE);
}
else if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, MAROON);
}
else if (tilemap.tiles[i].tile_type == SPIKES)
{
DrawRectangle(
x + tilemap.tiles[i].offset.x, y + tilemap.tiles[i].offset.y,
tilemap.tiles[i].size.x, tilemap.tiles[i].size.y, RED
);
}
}
if (tilemap.tiles[i].wet)
{
#define SURFACE_THICKNESS 4
int up = i - tilemap.width;
unsigned int bot = i + tilemap.width;
int right = i + 1;
int left = i - 1;
int bot_line = y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP - SURFACE_THICKNESS / 2;
if (up >= 0 && tilemap.tiles[up].wet)
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, y}, (Vector2){x + TILE_SIZE / 2, y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (
bot <= tilemap.n_tiles
&& tilemap.tiles[i].water_level == 0
)
{
if (i % tilemap.width != 0 && tilemap.tiles[left].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot-1].solid == SOLID))
{
DrawLineEx((Vector2){x, bot_line}, (Vector2){x + TILE_SIZE / 2, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (right % tilemap.width != 0 && tilemap.tiles[right].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot+1].solid == SOLID))
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, bot_line}, (Vector2){x + TILE_SIZE, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
Vector2 pos = Vector2Add(p_ent->position, spr.offset);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
}
}
if (tilemap.tiles[i].max_water_level < MAX_WATER_LEVEL)
sc_map_foreach_value(&scene->ent_manager.entities_map[LEVEL_END_TAG], p_ent)
{
DrawRectangleLinesEx((Rectangle){x, y, TILE_SIZE, TILE_SIZE}, 2.0, ColorAlpha(BLUE, 0.5));
}
}
DrawCircleV(p_ent->position, tilemap.tile_size >> 1, (data->coins.current < data->coins.total)? RED : GREEN);
}
draw_particle_system(&scene->part_sys);
@ -636,6 +466,16 @@ static void render_editor_game_scene(Scene_t* scene)
}
}
sc_map_foreach_value(&scene->ent_manager.entities_map[DYNMEM_ENT_TAG], p_ent)
{
CWaterRunner_t* p_runner = get_component(p_ent, CWATERRUNNER_T);
unsigned int x = ((p_runner->current_tile) % tilemap.width) * tilemap.tile_size;
unsigned int y = ((p_runner->current_tile) / tilemap.width) * tilemap.tile_size;
DrawCircle(x+16, y+16, 8, ColorAlpha(BLUE, 0.6));
}
if (data->show_grid)
{
for (int tile_y = min.y; tile_y < max.y; tile_y++)
@ -648,15 +488,15 @@ static void render_editor_game_scene(Scene_t* scene)
sprintf(buffer, "%u", sc_map_size_64v(&tilemap.tiles[i].entities_set));
//if (tilemap.tiles[i].solid > 0)
if (tilemap.tiles[i].solid > 0)
{
DrawText(buffer, x, y, 10, WHITE);
}
//else
//{
// // Draw water tile
// DrawText(buffer, x, y, 10, BLACK);
//}
else
{
// Draw water tile
DrawText(buffer, x, y, 10, BLACK);
}
}
}
@ -664,28 +504,15 @@ static void render_editor_game_scene(Scene_t* scene)
for (size_t i = min.x; i < max.x; ++i)
{
int x = (i+1)*TILE_SIZE;
DrawLine(x, 0, x, tilemap.height * TILE_SIZE, WHITE);
DrawLine(x, 0, x, tilemap.height * TILE_SIZE, BLACK);
}
for (size_t i = min.y; i < max.y;++i)
{
int y = (i+1)*TILE_SIZE;
DrawLine(0, y, tilemap.width * TILE_SIZE, y, WHITE);
DrawLine(0, y, tilemap.width * TILE_SIZE, y, BLACK);
}
}
EndMode2D();
sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_ent)
{
CAirTimer_t* p_air = get_component(p_ent, CAIRTIMER_T);
Sprite_t* spr = get_sprite(&scene->engine->assets, "p_bigbubble");
Vector2 air_pos = {data->game_rec.width - 32, data->game_rec.height - 32};
for (uint8_t i = 0; i < p_air->curr_count; i++)
{
draw_sprite(spr, 0, air_pos, 0, false);
//DrawCircleV(air_pos, 16, BLUE);
air_pos.x -= 32;
}
}
EndTextureMode();
}
@ -831,22 +658,7 @@ static void toggle_block_system(Scene_t* scene, ActionType_t action, bool presse
if (p_ent != NULL)
{
p_ent->position.x = (tile_idx % tilemap.width) * tilemap.tile_size + (tilemap.tile_size >> 1);
p_ent->position.y = (tile_idx / tilemap.width) * tilemap.tile_size + (tilemap.tile_size >> 1);
}
}
break;
case SPAWN_URCHIN:
{
Entity_t* p_ent = create_urchin(&scene->ent_manager);
if (p_ent != NULL)
{
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
p_ent->position.x = (tile_idx % tilemap.width) * tilemap.tile_size
+ (tilemap.tile_size >> 1) - p_bbox->half_size.x;
p_ent->position.y = (tile_idx / tilemap.width) * tilemap.tile_size
+ (tilemap.tile_size >> 1) - p_bbox->half_size.y;
CTransform_t* p_ct = get_component(p_ent, CTRANSFORM_COMP_T);
p_ct->velocity = Vector2Scale(urchin_spawn_vec, URCHIN_VELOCITY_SCALE);
p_ent->position.y = (tile_idx / tilemap.width) * tilemap.tile_size + (tilemap.tile_size >> 1);;
}
}
break;
@ -944,19 +756,40 @@ static void restart_editor_level(Scene_t* scene)
tilemap.tiles[i].water_level = 0;
tilemap.tiles[i].wet = false;
tilemap.tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
}
clear_all_game_entities(CONTAINER_OF(scene, LevelScene_t, scene));
Entity_t* p_player = create_player(&scene->ent_manager);
p_player->position = data->player_spawn;
CPlayerState_t* p_pstate = get_component(p_player, CPLAYERSTATE_T);
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
Entity_t* ent;
unsigned int m_id;
sc_map_foreach(&tilemap.tiles[i].entities_set, m_id, ent)
{
p_pstate->locked = true;
if (ent->m_tag == PLAYER_ENT_TAG) continue;
CTileCoord_t* p_tilecoord = get_component(
ent, CTILECOORD_COMP_T
);
for (size_t i = 0;i < p_tilecoord->n_tiles; ++i)
{
// Use previously store tile position
// Clear from those positions
unsigned int tile_idx = p_tilecoord->tiles[i];
sc_map_del_64v(&(tilemap.tiles[tile_idx].entities_set), m_id);
}
//remove_entity(&scene->ent_manager, m_id);
CWaterRunner_t* p_crunner = get_component(ent, CWATERRUNNER_T);
if (p_crunner == NULL)
{
remove_entity(&scene->ent_manager, m_id);
}
else
{
free_water_runner(ent, &scene->ent_manager);
}
}
}
Entity_t* p_ent;
sc_map_foreach_value(&scene->ent_manager.entities_map[LEVEL_END_TAG], p_ent)
{
remove_entity(&scene->ent_manager, p_ent->m_id);
}
update_entity_manager(&scene->ent_manager);
for (size_t i = 0; i < tilemap.width; ++i)
{
unsigned int tile_idx = (tilemap.height - 1) * tilemap.width + i;
@ -989,43 +822,6 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
case ACTION_JUMP:
p_playerstate->jump_pressed = pressed;
break;
case ACTION_LOOKAHEAD:
if (!pressed)
{
p_playerstate->locked = !p_playerstate->locked;
}
break;
case ACTION_SET_SPAWNPOINT:
data->player_spawn = p_player->position;
break;
default:
break;
}
}
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
switch(action)
{
case ACTION_UP:
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.y -= 20;
break;
case ACTION_DOWN:
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.y += 20;
break;
case ACTION_LEFT:
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.x -= 20;
break;
case ACTION_RIGHT:
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.x += 20;
break;
default:
break;
}
}
switch(action)
{
case ACTION_NEXT_SPAWN:
if (!pressed)
{
@ -1165,10 +961,7 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
break;
case ACTION_NEXTLEVEL:
case ACTION_RESTART:
if (!pressed)
{
restart_editor_level(scene);
}
break;
case ACTION_TOGGLE_GRID:
if (!pressed)
@ -1176,6 +969,9 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
data->show_grid = !data->show_grid;
}
break;
case ACTION_SET_SPAWNPOINT:
p_player->spawn_pos = p_player->position;
break;
case ACTION_TOGGLE_TIMESLOW:
if (!pressed)
{
@ -1193,6 +989,7 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
case ACTION_REMOVE_TILE:
toggle_block_system(scene, action, pressed);
update_entity_manager(&scene->ent_manager);
default:
break;
case ACTION_SWITCH_TILESET:
if (!pressed)
@ -1203,57 +1000,13 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
data->solid_tile_sprites = get_sprite(&scene->engine->assets, SOLID_TILE_SELECTIONS[data->selected_solid_tilemap]);
}
break;
case ACTION_LOOKAHEAD:
if (!pressed)
{
data->camera.mode = (data->camera.mode == CAMERA_FOLLOW_PLAYER) ? CAMERA_RANGED_MOVEMENT : CAMERA_FOLLOW_PLAYER;
}
break;
default:
break;
}
}
static void at_level_start(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
data->sm.state = LEVEL_STATE_RUNNING;
}
static void at_level_dead(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
Entity_t* p_player = create_player(&scene->ent_manager);
p_player->position = data->player_spawn;
CPlayerState_t* p_pstate = get_component(p_player, CPLAYERSTATE_T);
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
p_pstate->locked = true;
}
change_level_state(data, LEVEL_STATE_STARTING);
}
static void at_level_complete(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
data->sm.fractional += scene->delta_time;
if (data->sm.fractional > 1.0f)
{
data->sm.fractional -= 1.0f;
data->sm.counter++;
}
if (data->sm.counter >= 1)
{
data->sm.counter = 0;
data->sm.fractional = 0;
at_level_dead(scene);
}
}
void init_sandbox_scene(LevelScene_t* scene)
{
init_scene(&scene->scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM | ENABLE_PARTICLE_SYSTEM);
init_scene(&scene->scene, &level_do_action);
init_entity_tag_map(&scene->scene.ent_manager, PLAYER_ENT_TAG, 4);
init_entity_tag_map(&scene->scene.ent_manager, BOULDER_ENT_TAG, MAX_COMP_POOL_SIZE);
init_entity_tag_map(&scene->scene.ent_manager, LEVEL_END_TAG, 16);
@ -1262,13 +1015,9 @@ void init_sandbox_scene(LevelScene_t* scene)
scene->data.tilemap.tiles = all_tiles;
init_level_scene_data(
&scene->data, MAX_N_TILES, all_tiles,
(Rectangle){10, 10, VIEWABLE_EDITOR_MAP_WIDTH*TILE_SIZE, VIEWABLE_EDITOR_MAP_HEIGHT*TILE_SIZE}
(Rectangle){25, 25, VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE}
);
scene->data.sm.state_functions[LEVEL_STATE_STARTING] = at_level_start;
scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_DEAD] = at_level_dead;
scene->data.sm.state_functions[LEVEL_STATE_COMPLETE] = at_level_complete;
scene->data.show_grid = true;
scene->data.tile_sprites[ONEWAY_TILE] = get_sprite(&scene->scene.engine->assets, "tl_owp");
scene->data.tile_sprites[LADDER] = get_sprite(&scene->scene.engine->assets, "tl_ldr");
scene->data.tile_sprites[SPIKES] = get_sprite(&scene->scene.engine->assets, "d_spikes");
@ -1277,8 +1026,6 @@ void init_sandbox_scene(LevelScene_t* scene)
scene->data.tile_sprites[SPIKES + TILE_180ROT] = get_sprite(&scene->scene.engine->assets, "u_spikes");
scene->data.selected_solid_tilemap = 0;
scene->data.solid_tile_sprites = get_sprite(&scene->scene.engine->assets, SOLID_TILE_SELECTIONS[0]);
Texture2D* tex = get_texture(&scene->scene.engine->assets, "bg_tex");
SetTextureWrap(*tex, TEXTURE_WRAP_REPEAT);
for (size_t i = 0; i < scene->data.tilemap.width; ++i)
{
@ -1287,6 +1034,10 @@ void init_sandbox_scene(LevelScene_t* scene)
}
scene->scene.bg_colour = LIGHTGRAY;
add_scene_layer(
&scene->scene, scene->data.game_rec.width, scene->data.game_rec.height,
scene->data.game_rec
);
add_scene_layer(
&scene->scene, SELECTION_REGION_WIDTH, SELECTION_REGION_HEIGHT,
(Rectangle){
@ -1303,24 +1054,6 @@ void init_sandbox_scene(LevelScene_t* scene)
scene->scene.engine->intended_window_size.y
}
);
add_scene_layer(
&scene->scene, scene->data.game_rec.width, scene->data.game_rec.height,
scene->data.game_rec
);
{
Entity_t* p_player = create_player(&scene->scene.ent_manager);
CSprite_t* p_cspr = get_component(p_player, CSPRITE_T);
p_cspr->node.flip |= 1;
p_player->position.x = 100;
p_player->position.y = (scene->data.tilemap.height - 1) * scene->data.tilemap.tile_size - PLAYER_HEIGHT;
scene->data.player_spawn = p_player->position;
scene->data.camera.target_pos = p_player->position;
scene->data.camera.cam.target = p_player->position;
}
update_entity_manager(&scene->scene.ent_manager);
BeginTextureMode(scene->scene.layers.render_layers[SELECTION_LAYER].layer_tex);
ClearBackground(LIGHTGRAY);
@ -1488,9 +1221,6 @@ void init_sandbox_scene(LevelScene_t* scene)
case SPAWN_LEVEL_END:
DrawCircleV(Vector2Add(draw_pos, half_size), half_size.x, GREEN);
break;
case SPAWN_URCHIN:
DrawCircleV(Vector2Add(draw_pos, half_size), half_size.x, RED);
break;
}
draw_pos.x += SELECTION_TILE_SIZE;
@ -1500,8 +1230,11 @@ void init_sandbox_scene(LevelScene_t* scene)
EndTextureMode();
create_player(&scene->scene.ent_manager);
update_entity_manager(&scene->scene.ent_manager);
// insert level scene systems
sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_array_add(&scene->scene.systems, &player_movement_input_system);
sc_array_add(&scene->scene.systems, &player_bbox_update_system);
sc_array_add(&scene->scene.systems, &player_pushing_system);
@ -1513,11 +1246,11 @@ void init_sandbox_scene(LevelScene_t* scene)
sc_array_add(&scene->scene.systems, &boulder_destroy_wooden_tile_system);
sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_array_add(&scene->scene.systems, &tile_collision_system);
sc_array_add(&scene->scene.systems, &update_tilemap_system);
//sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_array_add(&scene->scene.systems, &hitbox_update_system);
sc_array_add(&scene->scene.systems, &player_crushing_system);
sc_array_add(&scene->scene.systems, &spike_collision_system);
//sc_array_add(&scene->scene.systems, &edge_velocity_check_system);
sc_array_add(&scene->scene.systems, &edge_velocity_check_system);
sc_array_add(&scene->scene.systems, &state_transition_update_system);
sc_array_add(&scene->scene.systems, &update_entity_emitter_system);
sc_array_add(&scene->scene.systems, &player_ground_air_transition_system);
@ -1528,9 +1261,8 @@ void init_sandbox_scene(LevelScene_t* scene)
sc_array_add(&scene->scene.systems, &camera_update_system);
sc_array_add(&scene->scene.systems, &player_dir_reset_system);
sc_array_add(&scene->scene.systems, &update_water_runner_system);
sc_array_add(&scene->scene.systems, &check_player_dead_system);
sc_array_add(&scene->scene.systems, &player_respawn_system);
sc_array_add(&scene->scene.systems, &level_end_detection_system);
sc_array_add(&scene->scene.systems, &level_state_management_system);
sc_array_add(&scene->scene.systems, &render_editor_game_scene);
sc_array_add(&scene->scene.systems, &level_scene_render_func);
@ -1553,11 +1285,10 @@ void init_sandbox_scene(LevelScene_t* scene)
sc_map_put_64(&scene->scene.action_map, KEY_T, ACTION_CRATE_ACTIVATION);
sc_map_put_64(&scene->scene.action_map, KEY_L, ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_R, ACTION_RESTART);
sc_map_put_64(&scene->scene.action_map, KEY_B, ACTION_TOGGLE_GRID);
sc_map_put_64(&scene->scene.action_map, KEY_Z, ACTION_SWITCH_TILESET);
sc_map_put_64(&scene->scene.action_map, KEY_X, ACTION_TOGGLE_GRID);
sc_map_put_64(&scene->scene.action_map, KEY_C, ACTION_SET_SPAWNPOINT);
sc_map_put_64(&scene->scene.action_map, KEY_V, ACTION_LOOKAHEAD);
sc_map_put_64(&scene->scene.action_map, KEY_B, ACTION_TOGGLE_TIMESLOW);
sc_map_put_64(&scene->scene.action_map, KEY_V, ACTION_SET_SPAWNPOINT);
sc_map_put_64(&scene->scene.action_map, KEY_U, ACTION_TOGGLE_TIMESLOW);
sc_map_put_64(&scene->scene.action_map, MOUSE_LEFT_BUTTON, ACTION_SPAWN_TILE);
sc_map_put_64(&scene->scene.action_map, MOUSE_RIGHT_BUTTON, ACTION_REMOVE_TILE);
@ -1565,7 +1296,6 @@ void init_sandbox_scene(LevelScene_t* scene)
void free_sandbox_scene(LevelScene_t* scene)
{
clear_all_game_entities(scene);
free_scene(&scene->scene);
term_level_scene_data(&scene->data);
}

View File

@ -1,35 +0,0 @@
#include "mempool.h"
#include "components.h"
/** This file is supposed to implement any required engine functions
*/
DEFINE_COMP_MEMPOOL_BUF(CMovementState_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CJump_t, 8)
DEFINE_COMP_MEMPOOL_BUF(CPlayerState_t, 8)
DEFINE_COMP_MEMPOOL_BUF(CContainer_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CHitBoxes_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CHurtbox_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CSprite_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CMoveable_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CLifeTimer_t, MAX_COMP_POOL_SIZE)
DEFINE_COMP_MEMPOOL_BUF(CWaterRunner_t, 32)
DEFINE_COMP_MEMPOOL_BUF(CAirTimer_t, 8)
DEFINE_COMP_MEMPOOL_BUF(CEmitter_t, 32)
DEFINE_COMP_MEMPOOL_BUF(CSquishable_t, MAX_COMP_POOL_SIZE)
// Component mempools are added in the order of the component enums
BEGIN_DEFINE_COMP_MEMPOOL
ADD_COMP_MEMPOOL(CMovementState_t)
ADD_COMP_MEMPOOL(CJump_t)
ADD_COMP_MEMPOOL(CPlayerState_t)
ADD_COMP_MEMPOOL(CContainer_t)
ADD_COMP_MEMPOOL(CHitBoxes_t)
ADD_COMP_MEMPOOL(CHurtbox_t)
ADD_COMP_MEMPOOL(CSprite_t)
ADD_COMP_MEMPOOL(CMoveable_t)
ADD_COMP_MEMPOOL(CLifeTimer_t)
ADD_COMP_MEMPOOL(CWaterRunner_t)
ADD_COMP_MEMPOOL(CAirTimer_t)
ADD_COMP_MEMPOOL(CEmitter_t)
ADD_COMP_MEMPOOL(CSquishable_t)
END_DEFINE_COMP_MEMPOOL

View File

@ -1,13 +1,12 @@
#ifndef __ENT_IMPL_H
#define __ENT_IMPL_H
#include "assets.h"
#include "assets_tag.h"
#include "components.h"
bool init_player_creation(const char* info_file, Assets_t* assets);
bool init_player_creation_rres(const char* rres_file, const char* file, Assets_t* assets);
Entity_t* create_player(EntityManager_t* ent_manager);
Entity_t* create_dead_player(EntityManager_t* ent_manager);
Entity_t* create_player_finish(EntityManager_t* ent_manager);
bool init_item_creation(Assets_t* assets);
@ -16,7 +15,6 @@ Entity_t* create_boulder(EntityManager_t* ent_manager);
Entity_t* create_arrow(EntityManager_t* ent_manager, uint8_t dir);
Entity_t* create_bomb(EntityManager_t* ent_manager, Vector2 launch_dir);
Entity_t* create_explosion(EntityManager_t* ent_manager);
Entity_t* create_urchin(EntityManager_t* ent_manager);
Entity_t* create_chest(EntityManager_t* ent_manager);
Entity_t* create_level_end(EntityManager_t* ent_manager);

View File

@ -1,5 +1,3 @@
#include "tracy/TracyC.h"
#include "scene_impl.h"
#include "game_systems.h"
#include "water_flow.h"
@ -11,7 +9,6 @@
#include <stdio.h>
static Tile_t all_tiles[MAX_N_TILES] = {0};
static RenderInfoNode all_tile_rendernodes[MAX_N_TILES] = {0};
#define GAME_LAYER 0
#define CONTROL_LAYER 1
@ -23,77 +20,33 @@ static void level_scene_render_func(Scene_t* scene)
BeginTextureMode(scene->layers.render_layers[CONTROL_LAYER].layer_tex);
ClearBackground(BLANK);
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
// TL
DrawLineEx((Vector2){32,32},
(Vector2){64,32}, 5, WHITE);
DrawLineEx((Vector2){32,32},
(Vector2){32,64}, 5, WHITE);
//TR
DrawLineEx((Vector2){data->game_rec.width - 64,32},
(Vector2){data->game_rec.width - 32, 32}, 5, WHITE);
DrawLineEx((Vector2){data->game_rec.width - 32,32},
(Vector2){data->game_rec.width - 32, 64}, 5, WHITE);
//BL
DrawLineEx((Vector2){32,data->game_rec.height-32},
(Vector2){64,data->game_rec.height - 32}, 5, WHITE);
DrawLineEx((Vector2){32,data->game_rec.height-64},
(Vector2){32, data->game_rec.height - 32}, 5, WHITE);
//BR
DrawLineEx((Vector2){data->game_rec.width - 64,data->game_rec.height-32},
(Vector2){data->game_rec.width - 32,data->game_rec.height - 32}, 5, WHITE);
DrawLineEx((Vector2){data->game_rec.width - 32,data->game_rec.height-64},
(Vector2){data->game_rec.width - 32, data->game_rec.height - 32}, 5, WHITE);
}
Entity_t* p_ent;
sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_ent)
{
CAirTimer_t* p_air = get_component(p_ent, CAIRTIMER_T);
Sprite_t* spr = get_sprite(&scene->engine->assets, "p_bigbubble");
Vector2 air_pos = {data->game_rec.width - 32, data->game_rec.height - 32};
Vector2 air_pos = {data->game_rec.x + data->game_rec.width - 16, data->game_rec.y + data->game_rec.height - 16};
for (uint8_t i = 0; i < p_air->curr_count; i++)
{
draw_sprite(spr, 0, air_pos, 0, false);
//DrawCircleV(air_pos, 16, BLUE);
DrawCircleV(air_pos, 16, BLUE);
air_pos.x -= 32;
}
}
// For DEBUG
int gui_x = 5;
sprintf(buffer, "%u %u", sc_map_size_64v(&scene->ent_manager.entities), GetFPS());
DrawText(buffer, gui_x, data->game_rec.height - 12, 12, WHITE);
const int gui_x = data->game_rec.x + data->game_rec.width + 10;
//sprintf(buffer, "Spawn Entity: %s", get_spawn_selection_string(current_spawn_selection));
//DrawText(buffer, gui_x, 240, 12, BLACK);
sprintf(buffer, "Number of Entities: %u", sc_map_size_64v(&scene->ent_manager.entities));
DrawText(buffer, gui_x, 70, 12, BLACK);
sprintf(buffer, "FPS: %u", GetFPS());
DrawText(buffer, gui_x, 120, 12, BLACK);
DrawRectangle(0, 0, data->game_rec.width, 32, (Color){0,0,0,128});
{
DrawText("Z", 300, 5, 24, RED);
Sprite_t* spr = get_sprite(&scene->engine->assets, "eye");
if (data->camera.mode == CAMERA_RANGED_MOVEMENT)
{
draw_sprite(spr, 1, (Vector2){332, 0}, 0, false);
}
else
{
draw_sprite(spr, 0, (Vector2){332, 0}, 0, false);
}
}
DrawText(data->level_pack->levels[data->current_level].level_name, 5, 5, 24, WHITE);
{
sprintf(buffer, "%u / %u", data->coins.current, data->coins.total);
gui_x = data->game_rec.width - MeasureText(buffer, 24) - 5;
// TODO: Use the chest sprite
Sprite_t* spr = get_sprite(&scene->engine->assets, "chest");
draw_sprite_pro(spr, 0, (Vector2){gui_x-32, 8}, 0, 0, (Vector2){0.5,0.5}, WHITE);
DrawText(buffer, gui_x, 5, 24, RED);
}
print_mempool_stats(buffer);
DrawText(buffer, gui_x, 150, 12, BLACK);
EndTextureMode();
}
static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
CPlayerState_t* p_playerstate;
sc_map_foreach_value(&scene->ent_manager.component_map[CPLAYERSTATE_T], p_playerstate)
{
@ -101,27 +54,19 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
{
case ACTION_UP:
p_playerstate->player_dir.y = (pressed)? -1 : 0;
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.y -= 20;
break;
case ACTION_DOWN:
p_playerstate->player_dir.y = (pressed)? 1 : 0;
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.y += 20;
break;
case ACTION_LEFT:
p_playerstate->player_dir.x = (pressed)? -1 : 0;
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.x -= 20;
break;
case ACTION_RIGHT:
p_playerstate->player_dir.x = (pressed)? 1 : 0;
if (data->camera.mode == CAMERA_RANGED_MOVEMENT) data->camera.cam.target.x += 20;
break;
case ACTION_JUMP:
p_playerstate->jump_pressed = pressed;
break;
case ACTION_LOOKAHEAD:
p_playerstate->locked = pressed;
data->camera.mode = pressed ? CAMERA_RANGED_MOVEMENT : CAMERA_FOLLOW_PLAYER;
break;
default:
break;
}
@ -144,7 +89,7 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
case ACTION_EXIT:
if(scene->engine != NULL)
{
change_scene(scene->engine, MAIN_MENU_SCENE);
change_scene(scene->engine, 0);
}
break;
default:
@ -155,18 +100,17 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
static void render_regular_game_scene(Scene_t* scene)
{
TracyCZoneN(ctx, "GameRender", true)
// This function will render the game scene outside of the intended draw function
// Just for clarity and separation of logic
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
TileGrid_t tilemap = data->tilemap;
Entity_t* p_ent;
Vector2 min = GetScreenToWorld2D((Vector2){0, 0}, data->camera.cam);
Vector2 min = GetScreenToWorld2D((Vector2){data->game_rec.x, data->game_rec.y}, data->camera.cam);
Vector2 max = GetScreenToWorld2D(
(Vector2){
data->game_rec.width,
data->game_rec.height
data->game_rec.x + data->game_rec.width,
data->game_rec.y + data->game_rec.height
},
data->camera.cam
);
@ -177,95 +121,10 @@ static void render_regular_game_scene(Scene_t* scene)
max.x = (int)fmin(tilemap.width, max.x + 1);
max.y = (int)fmin(tilemap.height, max.y + 1);
// Queue TileMap rendering
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
// Tile render nodes is static on load and is not dynamically updated.
// We exploit the fact that all tiles can only change into an empty
// tile.
// This won't work if a tile can change into something else
if (tilemap.tiles[i].tile_type == EMPTY_TILE) continue;
uint8_t depth = 2;
if (tilemap.tiles[i].tile_type == LADDER)
{
depth = 0;
}
add_render_node(&data->render_manager, all_tile_rendernodes + i, depth);
}
}
// Queue Sprite rendering
unsigned int ent_idx;
CSprite_t* p_cspr;
sc_map_foreach(&scene->ent_manager.component_map[CSPRITE_T], ent_idx, p_cspr)
{
Entity_t* p_ent = get_entity(&scene->ent_manager, ent_idx);
if (!p_ent->m_alive) continue;
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite == NULL) continue;
Vector2 pos = p_ent->position;
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
if (p_bbox != NULL)
{
pos = Vector2Add(
pos,
get_anchor_offset(p_bbox->size, spr.dest_anchor, p_cspr->node.flip & 1)
);
}
pos = Vector2Subtract(
pos,
get_anchor_offset(spr.sprite->frame_size, spr.src_anchor, p_cspr->node.flip & 1)
);
Vector2 offset = spr.offset;
if (p_cspr->node.flip & 1) offset.x *= -1;
pos = Vector2Add(pos, offset);
// Entity culling
if (
pos.x + spr.sprite->frame_size.x < min.x * tilemap.tile_size
|| pos.x > max.x * tilemap.tile_size
|| pos.y + spr.sprite->frame_size.y < min.y * tilemap.tile_size
|| pos.y > max.y * tilemap.tile_size
)
{
continue;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
p_cspr->current_frame = 2 * data->selected_solid_tilemap + (
(data->coins.current < data->coins.total) ? 0 : 1);
}
p_cspr->node.spr = spr.sprite;
p_cspr->node.frame_num = p_cspr->current_frame;
p_cspr->node.pos = pos;
add_render_node(&data->render_manager, &p_cspr->node, p_cspr->depth);
}
Texture2D* bg = get_texture(&scene->engine->assets, "bg_tex");
BeginTextureMode(scene->layers.render_layers[GAME_LAYER].layer_tex);
ClearBackground(WHITE);
DrawTexturePro(*bg,
//(Rectangle){0,0,64,64},
(Rectangle){min.x,0, data->game_rec.width, data->game_rec.height},
(Rectangle){0,0, data->game_rec.width, data->game_rec.height},
//(Rectangle){0,0,game_rec.width, game_rec.height},
(Vector2){0,0}, 0.0f, WHITE
);
BeginMode2D(data->camera.cam);
#ifdef ENABLE_RENDER_FALLBACK
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
@ -274,15 +133,66 @@ static void render_regular_game_scene(Scene_t* scene)
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
if (tilemap.tiles[i].tile_type == LADDER)
if (data->tile_sprites[tilemap.tiles[i].tile_type] != NULL)
{
if (data->tile_sprites[tilemap.tiles[i].tile_type] == NULL)
draw_sprite(data->tile_sprites[tilemap.tiles[i].tile_type], 0, (Vector2){x,y}, 0.0f, false);
}
else if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, BLACK);
}
else if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, MAROON);
}
else if (tilemap.tiles[i].tile_type == LADDER)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, ORANGE);
}
else if (tilemap.tiles[i].tile_type == SPIKES)
{
DrawRectangle(
x + tilemap.tiles[i].offset.x, y + tilemap.tiles[i].offset.y,
tilemap.tiles[i].size.x, tilemap.tiles[i].size.y, RED
);
}
if (tilemap.tiles[i].wet)
{
#define SURFACE_THICKNESS 4
int up = i - tilemap.width;
unsigned int bot = i + tilemap.width;
int right = i + 1;
int left = i - 1;
int bot_line = y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP - SURFACE_THICKNESS / 2;
if (up >= 0 && tilemap.tiles[up].wet)
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, y}, (Vector2){x + TILE_SIZE / 2, y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (
bot <= tilemap.n_tiles
&& tilemap.tiles[i].water_level == 0
)
{
if (i % tilemap.width != 0 && tilemap.tiles[left].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot-1].solid == SOLID))
{
DrawLineEx((Vector2){x, bot_line}, (Vector2){x + TILE_SIZE / 2, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (right % tilemap.width != 0 && tilemap.tiles[right].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot+1].solid == SOLID))
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, bot_line}, (Vector2){x + TILE_SIZE, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
}
if (tilemap.tiles[i].max_water_level < MAX_WATER_LEVEL)
{
DrawRectangleLinesEx((Rectangle){x, y, TILE_SIZE, TILE_SIZE}, 2.0, ColorAlpha(BLUE, 0.5));
}
}
}
}
sc_map_foreach_value(&scene->ent_manager.entities, p_ent)
{
CBBox_t* p_bbox = get_component(p_ent, CBBOX_COMP_T);
@ -297,8 +207,18 @@ static void render_regular_game_scene(Scene_t* scene)
|| p_ent->position.y > max.y * tilemap.tile_size
) continue;
// Render Sprite only
CSprite_t* p_cspr = get_component(p_ent, CSPRITE_T);
if (p_cspr != NULL) continue;
if (p_cspr != NULL)
{
const SpriteRenderInfo_t spr = p_cspr->sprites[p_cspr->current_idx];
if (spr.sprite != NULL)
{
Vector2 pos = Vector2Add(p_ent->position, spr.offset);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
continue;
}
// Continue here only if no sprite
Color colour;
@ -318,12 +238,6 @@ static void render_regular_game_scene(Scene_t* scene)
break;
}
if (p_ent->m_tag == LEVEL_END_TAG)
{
DrawCircleV(p_ent->position, tilemap.tile_size >> 1, (data->coins.current < data->coins.total)? RED : GREEN);
continue;
}
if (p_bbox != NULL)
{
if (p_ent->m_tag == BOULDER_ENT_TAG)
@ -388,92 +302,27 @@ static void render_regular_game_scene(Scene_t* scene)
}
}
}
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
if (tilemap.tiles[i].tile_type != LADDER)
{
uint8_t tile_sprite_idx = tilemap.tiles[i].tile_type + tilemap.tiles[i].rotation;
if (tilemap.tiles[i].tile_type == SOLID_TILE)
{
if (data->solid_tile_sprites == NULL) {
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, BLACK);
}
}
else if (data->tile_sprites[tile_sprite_idx] == NULL)
{
if (tilemap.tiles[i].tile_type == ONEWAY_TILE)
{
DrawRectangle(x, y, TILE_SIZE, TILE_SIZE, MAROON);
}
else if (tilemap.tiles[i].tile_type == SPIKES)
{
DrawRectangle(
x + tilemap.tiles[i].offset.x, y + tilemap.tiles[i].offset.y,
tilemap.tiles[i].size.x, tilemap.tiles[i].size.y, RED
);
}
}
}
}
}
#endif //ENABLE_RENDER_FALLBACK
execute_render(&data->render_manager);
sc_map_foreach_value(&scene->ent_manager.entities_map[DYNMEM_ENT_TAG], p_ent)
{
CWaterRunner_t* p_runner = get_component(p_ent, CWATERRUNNER_T);
// Particle effect will always be in front of everything except water
unsigned int x = ((p_runner->current_tile) % tilemap.width) * tilemap.tile_size;
unsigned int y = ((p_runner->current_tile) / tilemap.width) * tilemap.tile_size;
DrawCircle(x+16, y+16, 8, ColorAlpha(BLUE, 0.6));
}
draw_particle_system(&scene->part_sys);
// Render water
// Draw water tile
for (int tile_y = min.y; tile_y < max.y; tile_y++)
{
for (int tile_x = min.x; tile_x < max.x; tile_x++)
{
int i = tile_x + tile_y * tilemap.width;
if (!tilemap.tiles[i].wet && tilemap.tiles[i].water_level == 0) {
continue;
}
int x = tile_x * TILE_SIZE;
int y = tile_y * TILE_SIZE;
// Draw water flow
#define SURFACE_THICKNESS 4
int up = i - tilemap.width;
unsigned int bot = i + tilemap.width;
int right = i + 1;
int left = i - 1;
int bot_line = y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP - SURFACE_THICKNESS / 2;
if (up >= 0 && tilemap.tiles[up].wet)
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, y}, (Vector2){x + TILE_SIZE / 2, y + TILE_SIZE - tilemap.tiles[i].water_level * WATER_BBOX_STEP}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (
bot <= tilemap.n_tiles
&& tilemap.tiles[i].water_level == 0
)
{
if (i % tilemap.width != 0 && tilemap.tiles[left].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot-1].solid == SOLID))
{
DrawLineEx((Vector2){x, bot_line}, (Vector2){x + TILE_SIZE / 2, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
if (right % tilemap.width != 0 && tilemap.tiles[right].wet && (tilemap.tiles[bot].solid == SOLID || tilemap.tiles[bot+1].solid == SOLID))
{
DrawLineEx((Vector2){x + TILE_SIZE / 2, bot_line}, (Vector2){x + TILE_SIZE, bot_line}, SURFACE_THICKNESS, ColorAlpha(BLUE, 0.7));
}
}
//if (tilemap.tiles[i].max_water_level < MAX_WATER_LEVEL)
//{
// DrawRectangleLinesEx((Rectangle){x, y, TILE_SIZE, TILE_SIZE}, 2.0, ColorAlpha(BLUE, 0.5));
//}
uint32_t water_height = tilemap.tiles[i].water_level * WATER_BBOX_STEP;
Color water_colour = ColorAlpha(BLUE, 0.5);
DrawRectangle(
@ -485,64 +334,31 @@ static void render_regular_game_scene(Scene_t* scene)
);
}
}
// Draw Border
DrawLine(0, 0, 0, tilemap.height * tilemap.tile_size, BLACK);
DrawLine(0, 0, tilemap.width * TILE_SIZE, 0, BLACK);
int val = (tilemap.width) * tilemap.tile_size;
DrawLine(val, 0, val, tilemap.height * tilemap.tile_size, BLACK);
val = (tilemap.height) * tilemap.tile_size;
DrawLine(0, val, tilemap.width * TILE_SIZE, val, BLACK);
EndMode2D();
EndTextureMode();
TracyCZoneEnd(ctx)
}
static void at_level_start(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
data->sm.state = LEVEL_STATE_RUNNING;
}
static void at_level_complete(Scene_t* scene)
{
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
data->sm.fractional += scene->delta_time;
if (data->sm.fractional > 1.0f)
{
data->sm.fractional -= 1.0f;
data->sm.counter++;
}
if (data->sm.counter >= 1)
{
do_action(scene, ACTION_NEXTLEVEL, false);
data->sm.state = LEVEL_STATE_STARTING;
}
}
void init_game_scene(LevelScene_t* scene)
{
init_scene(&scene->scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM | ENABLE_PARTICLE_SYSTEM);
init_scene(&scene->scene, &level_do_action);
init_entity_tag_map(&scene->scene.ent_manager, PLAYER_ENT_TAG, 4);
init_entity_tag_map(&scene->scene.ent_manager, BOULDER_ENT_TAG, MAX_COMP_POOL_SIZE);
init_entity_tag_map(&scene->scene.ent_manager, LEVEL_END_TAG, 16);
init_entity_tag_map(&scene->scene.ent_manager, DYNMEM_ENT_TAG, 16);
scene->data.tilemap.tiles = all_tiles;
scene->data.tilemap.render_nodes = all_tile_rendernodes;
init_level_scene_data(
&scene->data, MAX_N_TILES, all_tiles,
(Rectangle){
0,0,
VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE
}
(Rectangle){25, 25, 32*TILE_SIZE, 18*TILE_SIZE}
);
for (size_t i = 0; i < MAX_N_TILES; i++)
{
memset(all_tile_rendernodes + i, 0, sizeof(RenderInfoNode));
all_tile_rendernodes[i].pos = (Vector2){
(i % scene->data.tilemap.width) * TILE_SIZE,
(i / scene->data.tilemap.width) * TILE_SIZE,
};
all_tile_rendernodes[i].scale = (Vector2){1,1};
all_tile_rendernodes[i].colour = WHITE;
}
scene->data.sm.state_functions[LEVEL_STATE_STARTING] = at_level_start;
scene->data.sm.state_functions[LEVEL_STATE_RUNNING] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_DEAD] = NULL;
scene->data.sm.state_functions[LEVEL_STATE_COMPLETE] = at_level_complete;
scene->scene.bg_colour = LIGHTGRAY;
add_scene_layer(
@ -550,17 +366,18 @@ void init_game_scene(LevelScene_t* scene)
scene->data.game_rec
);
add_scene_layer(
&scene->scene,
scene->data.game_rec.width, scene->data.game_rec.height,
scene->data.game_rec
&scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y,
(Rectangle){
0, 0,
scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y
}
);
create_player(&scene->scene.ent_manager);
update_entity_manager(&scene->scene.ent_manager);
// Set up textures
scene->data.solid_tile_sprites = get_sprite(&scene->scene.engine->assets, "stile0");
sc_array_add(&scene->scene.systems, &update_tilemap_system);
sc_array_add(&scene->scene.systems, &player_movement_input_system);
sc_array_add(&scene->scene.systems, &player_bbox_update_system);
@ -576,6 +393,7 @@ void init_game_scene(LevelScene_t* scene)
sc_array_add(&scene->scene.systems, &hitbox_update_system);
sc_array_add(&scene->scene.systems, &player_crushing_system);
sc_array_add(&scene->scene.systems, &spike_collision_system);
sc_array_add(&scene->scene.systems, &edge_velocity_check_system);
sc_array_add(&scene->scene.systems, &state_transition_update_system);
sc_array_add(&scene->scene.systems, &update_entity_emitter_system);
sc_array_add(&scene->scene.systems, &player_ground_air_transition_system);
@ -585,10 +403,8 @@ void init_game_scene(LevelScene_t* scene)
sc_array_add(&scene->scene.systems, &sprite_animation_system);
sc_array_add(&scene->scene.systems, &camera_update_system);
sc_array_add(&scene->scene.systems, &player_dir_reset_system);
sc_array_add(&scene->scene.systems, &player_respawn_system);
sc_array_add(&scene->scene.systems, &update_water_runner_system);
sc_array_add(&scene->scene.systems, &check_player_dead_system);
sc_array_add(&scene->scene.systems, &level_end_detection_system);
sc_array_add(&scene->scene.systems, &level_state_management_system);
sc_array_add(&scene->scene.systems, &render_regular_game_scene);
sc_array_add(&scene->scene.systems, &level_scene_render_func);
// This avoid graphical glitch, not essential
@ -603,13 +419,11 @@ void init_game_scene(LevelScene_t* scene)
sc_map_put_64(&scene->scene.action_map, KEY_R, ACTION_RESTART);
sc_map_put_64(&scene->scene.action_map, KEY_RIGHT_BRACKET, ACTION_NEXTLEVEL);
sc_map_put_64(&scene->scene.action_map, KEY_LEFT_BRACKET, ACTION_PREVLEVEL);
sc_map_put_64(&scene->scene.action_map, KEY_Z, ACTION_LOOKAHEAD);
}
void free_game_scene(LevelScene_t* scene)
{
clear_all_game_entities(scene);
free_scene(&scene->scene);
term_level_scene_data(&scene->data);
}

File diff suppressed because it is too large Load Diff

View File

@ -16,16 +16,16 @@ void state_transition_update_system(Scene_t* scene);
void update_entity_emitter_system(Scene_t* scene);
void update_tilemap_system(Scene_t* scene);
void hitbox_update_system(Scene_t* scene);
void edge_velocity_check_system(Scene_t* scene);
void sprite_animation_system(Scene_t* scene);
void boulder_destroy_wooden_tile_system(Scene_t* scene);
void camera_update_system(Scene_t* scene);
void container_destroy_system(Scene_t* scene);
void player_dir_reset_system(Scene_t* scene);
void check_player_dead_system(Scene_t* scene);
void player_respawn_system(Scene_t* scene);
void lifetimer_update_system(Scene_t* scene);
void airtimer_update_system(Scene_t* scene);
void spike_collision_system(Scene_t* scene);
void level_end_detection_system(Scene_t* scene);
void level_state_management_system(Scene_t* scene);
#endif // __GAME_SYSTEMS_H

View File

@ -1,31 +1,20 @@
#include "EC.h"
#include "assets_tag.h"
#include "ent_impl.h"
#include "constants.h"
#include "raymath.h"
static SpriteRenderInfo_t item_sprite_map[22] = {0};
#define URCHIN_OFFSET 6
static SpriteRenderInfo_t item_sprite_map[20] = {0};
bool init_item_creation(Assets_t* assets)
{
item_sprite_map[0].sprite = get_sprite(assets, "w_crate");
item_sprite_map[1].sprite = get_sprite(assets, "m_crate");
item_sprite_map[2].sprite = get_sprite(assets, "r_arrow");
//item_sprite_map[2].offset = (Vector2){-8, 6};
item_sprite_map[2].src_anchor = AP_MID_CENTER;
item_sprite_map[2].src_anchor = AP_MID_CENTER;
item_sprite_map[2].offset = (Vector2){-8, 4};
item_sprite_map[3].sprite = get_sprite(assets, "u_arrow");
item_sprite_map[3].src_anchor = AP_MID_CENTER;
item_sprite_map[3].src_anchor = AP_MID_CENTER;
item_sprite_map[4].sprite = get_sprite(assets, "l_arrow");
item_sprite_map[4].src_anchor = AP_MID_CENTER;
item_sprite_map[4].src_anchor = AP_MID_CENTER;
item_sprite_map[5].sprite = get_sprite(assets, "d_arrow");
item_sprite_map[5].src_anchor = AP_MID_CENTER;
item_sprite_map[5].src_anchor = AP_MID_CENTER;
item_sprite_map[6].sprite = get_sprite(assets, "bomb");
item_sprite_map[6].src_anchor = AP_MID_CENTER;
item_sprite_map[6].src_anchor = AP_MID_CENTER;
item_sprite_map[6].offset = (Vector2){0, -4};
item_sprite_map[7].sprite = get_sprite(assets, "w_ra_crate");
item_sprite_map[8].sprite = get_sprite(assets, "m_ra_crate");
item_sprite_map[9].sprite = get_sprite(assets, "w_ua_crate");
@ -37,16 +26,9 @@ bool init_item_creation(Assets_t* assets)
item_sprite_map[15].sprite = get_sprite(assets, "w_b_crate");
item_sprite_map[16].sprite = get_sprite(assets, "m_b_crate");
item_sprite_map[17].sprite = get_sprite(assets, "explode");
item_sprite_map[17].src_anchor = AP_MID_CENTER;
item_sprite_map[17].src_anchor = AP_MID_CENTER;
item_sprite_map[17].offset = (Vector2){-12, -12};
item_sprite_map[18].sprite = get_sprite(assets, "chest");
item_sprite_map[19].sprite = get_sprite(assets, "boulder");
item_sprite_map[20].sprite = get_sprite(assets, "exit");
item_sprite_map[20].src_anchor = AP_BOT_CENTER;
item_sprite_map[20].src_anchor = AP_BOT_CENTER;
item_sprite_map[20].offset = (Vector2){0, TILE_SIZE >> 1};
item_sprite_map[21].sprite = get_sprite(assets, "urchin");
item_sprite_map[21].offset = (Vector2){-URCHIN_OFFSET, -URCHIN_OFFSET};
return true;
}
@ -62,7 +44,7 @@ Entity_t* create_crate(EntityManager_t* ent_manager, bool metal, ContainerItem_t
p_bbox->fragile = false;
CTransform_t* p_ctransform = add_component(p_crate, CTRANSFORM_COMP_T);
p_ctransform->grav_delay = 0.10f;
p_ctransform->grav_delay = 0.20f;
p_ctransform->shape_factor = metal ? (Vector2){0.7,0.7} : (Vector2){0.8,0.8} ;
add_component(p_crate, CMOVEMENTSTATE_T);
@ -74,9 +56,6 @@ Entity_t* create_crate(EntityManager_t* ent_manager, bool metal, ContainerItem_t
CSprite_t* p_cspr = add_component(p_crate, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
{
CContainer_t* p_container = add_component(p_crate, CCONTAINER_T);
p_container->material = metal? METAL_CONTAINER : WOODEN_CONTAINER;
@ -125,9 +104,6 @@ Entity_t* create_boulder(EntityManager_t* ent_manager)
CSprite_t* p_cspr = add_component(p_boulder, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 19;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
p_cspr->depth = 2;
return p_boulder;
}
@ -151,32 +127,26 @@ Entity_t* create_arrow(EntityManager_t* ent_manager, uint8_t dir)
CSprite_t* p_cspr = add_component(p_arrow, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 2;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
//p_hitbox->boxes[0] = (Rectangle){TILE_SIZE - 5, TILE_SIZE / 2 - 5, 5, 5};
const int HITBOX_LONG_SIDE = 10;
const int HITBOX_SHORT_SIDE = 4;
const int CENTER_POSITION = (TILE_SIZE - HITBOX_SHORT_SIDE) >> 1;
const int HITBOX_CENTER = (HITBOX_SHORT_SIDE >> 1);
switch(dir)
{
case 0:
p_hitbox->boxes[0] = (Rectangle){10, TILE_SIZE / 2 - 5, 10, 5};
p_ctransform->velocity.x = -ARROW_SPEED;
p_cspr->current_idx += 2;
p_hitbox->boxes[0] = (Rectangle){-CENTER_POSITION, -HITBOX_CENTER, HITBOX_LONG_SIDE, HITBOX_SHORT_SIDE};
break;
case 2:
p_hitbox->boxes[0] = (Rectangle){-HITBOX_CENTER, -CENTER_POSITION, HITBOX_SHORT_SIDE, HITBOX_LONG_SIDE};
p_hitbox->boxes[0] = (Rectangle){TILE_SIZE / 2 - 5, 10, 5, 10};
p_ctransform->velocity.y = -ARROW_SPEED;
p_cspr->current_idx += 1;
break;
case 3:
p_hitbox->boxes[0] = (Rectangle){-HITBOX_CENTER, CENTER_POSITION - HITBOX_LONG_SIDE, HITBOX_SHORT_SIDE, HITBOX_LONG_SIDE};
p_hitbox->boxes[0] = (Rectangle){TILE_SIZE / 2 - 5, 10, 5, 10};
p_ctransform->velocity.y = ARROW_SPEED;
p_cspr->current_idx += 3;
break;
default:
p_hitbox->boxes[0] = (Rectangle){CENTER_POSITION - HITBOX_LONG_SIDE, -HITBOX_CENTER, HITBOX_LONG_SIDE, HITBOX_SHORT_SIDE};
p_hitbox->boxes[0] = (Rectangle){10, TILE_SIZE / 2 - 5, 10, 5};
p_ctransform->velocity.x = ARROW_SPEED;
break;
}
@ -189,12 +159,22 @@ Entity_t* create_bomb(EntityManager_t* ent_manager, Vector2 launch_dir)
Entity_t* p_bomb = add_entity(ent_manager, DESTRUCTABLE_ENT_TAG);
if (p_bomb == NULL) return NULL;
p_bomb->position.x += (TILE_SIZE - 25) / 2;
p_bomb->position.y += (TILE_SIZE - 25) / 2;
if (launch_dir.x > 0)
{
p_bomb->position.x += TILE_SIZE/ 2;
}
else if (launch_dir.x < 0)
{
p_bomb->position.x -= TILE_SIZE / 2;
}
add_component(p_bomb, CTILECOORD_COMP_T);
add_component(p_bomb, CMOVEMENTSTATE_T);
CHitBoxes_t* p_hitbox = add_component(p_bomb, CHITBOXES_T);
p_hitbox->n_boxes = 1;
p_hitbox->boxes[0] = (Rectangle){-13, -13, 26, 26};
p_hitbox->boxes[0] = (Rectangle){0, 0, 25, 25};
p_hitbox->atk = 0;
p_hitbox->one_hit = true;
@ -205,8 +185,6 @@ Entity_t* create_bomb(EntityManager_t* ent_manager, Vector2 launch_dir)
CSprite_t* p_cspr = add_component(p_bomb, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 6;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
CTransform_t* p_ctransform = add_component(p_bomb, CTRANSFORM_COMP_T);
p_ctransform->active = true;
@ -223,8 +201,8 @@ Entity_t* create_explosion(EntityManager_t* ent_manager)
Entity_t* p_explosion = add_entity(ent_manager, DESTRUCTABLE_ENT_TAG);
if (p_explosion == NULL) return NULL;
//p_explosion->position.x -= 16;
//p_explosion->position.y -= 16;
p_explosion->position.x -= 16;
p_explosion->position.y -= 16;
add_component(p_explosion, CTILECOORD_COMP_T);
CHitBoxes_t* p_hitbox = add_component(p_explosion, CHITBOXES_T);
p_hitbox->n_boxes = 1;
@ -234,63 +212,17 @@ Entity_t* create_explosion(EntityManager_t* ent_manager)
CTransform_t* p_ctransform = add_component(p_explosion, CTRANSFORM_COMP_T);
p_ctransform->movement_mode = KINEMATIC_MOVEMENT;
p_ctransform->active = true;
const int hitbox_sz = (TILE_SIZE << 1) + (TILE_SIZE >> 1);
p_hitbox->boxes[0] = (Rectangle){-(hitbox_sz >> 1), -(hitbox_sz >> 1), hitbox_sz, hitbox_sz};
p_hitbox->boxes[0] = (Rectangle){0, 0, TILE_SIZE + 32, TILE_SIZE + 32};
CSprite_t* p_cspr = add_component(p_explosion, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 17;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
p_cspr->depth = 3;
CLifeTimer_t* p_clifetimer = add_component(p_explosion, CLIFETIMER_T);
p_clifetimer->life_time = 0.05f;
return p_explosion;
}
Entity_t* create_urchin(EntityManager_t* ent_manager)
{
// The hit box is larger than the bbox
// Unfortunately, it's too late to incorporate the offset for the bbox component
// So, offset the hitbox instead and external reposition it.
Entity_t* p_urchin = add_entity(ent_manager, URCHIN_ENT_TAG);
if (p_urchin == NULL) return NULL;
CBBox_t* p_bbox = add_component(p_urchin, CBBOX_COMP_T);
set_bbox(p_bbox, TILE_SIZE-(URCHIN_OFFSET<<1), TILE_SIZE-(URCHIN_OFFSET<<1));
CTransform_t* p_ctransform = add_component(p_urchin, CTRANSFORM_COMP_T);
p_ctransform->movement_mode = KINEMATIC_MOVEMENT;
p_ctransform->bounce_coeff = 1.0f;
p_ctransform->velocity.x = -100;
p_ctransform->active = true;
add_component(p_urchin, CTILECOORD_COMP_T);
add_component(p_urchin, CMOVEMENTSTATE_T);
CHurtbox_t* p_hurtbox = add_component(p_urchin, CHURTBOX_T);
p_hurtbox->size = p_bbox->size;
p_hurtbox->def = 2;
p_hurtbox->damage_src = -1;
CHitBoxes_t* p_hitbox = add_component(p_urchin, CHITBOXES_T);
p_hitbox->n_boxes = 1;
p_hitbox->boxes[0] = (Rectangle) {-(URCHIN_OFFSET>>1),-(URCHIN_OFFSET>>1),TILE_SIZE-URCHIN_OFFSET,TILE_SIZE-URCHIN_OFFSET};
p_hitbox->atk = 2;
CSprite_t* p_cspr = add_component(p_urchin, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 21;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
p_cspr->depth = 2;
add_component(p_urchin, CSQUISHABLE_T);
return p_urchin;
}
Entity_t* create_chest(EntityManager_t* ent_manager)
{
Entity_t* p_chest = add_entity(ent_manager, CHEST_ENT_TAG);
@ -299,7 +231,7 @@ Entity_t* create_chest(EntityManager_t* ent_manager)
CBBox_t* p_bbox = add_component(p_chest, CBBOX_COMP_T);
set_bbox(p_bbox, TILE_SIZE, TILE_SIZE);
p_bbox->solid = true;
p_bbox->fragile = false;
p_bbox->fragile = true;
CTransform_t* p_ctransform = add_component(p_chest, CTRANSFORM_COMP_T);
p_ctransform->grav_delay = 0.3f;
@ -314,9 +246,6 @@ Entity_t* create_chest(EntityManager_t* ent_manager)
CSprite_t* p_cspr = add_component(p_chest, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 18;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
p_cspr->depth = 2;
return p_chest;
@ -327,12 +256,6 @@ Entity_t* create_level_end(EntityManager_t* ent_manager)
Entity_t* p_flag = add_entity(ent_manager, LEVEL_END_TAG);
if (p_flag == NULL) return NULL;
CSprite_t* p_cspr = add_component(p_flag, CSPRITE_T);
p_cspr->sprites = item_sprite_map;
p_cspr->current_idx = 20;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->node.colour = WHITE;
add_component(p_flag, CTILECOORD_COMP_T);
add_component(p_flag, CTRANSFORM_COMP_T);
return p_flag;
}

View File

@ -1,27 +1,25 @@
#include "scene_impl.h"
#include "assets_tag.h"
#include "gui.h"
#include "raymath.h"
#include <stdio.h>
static void level_select_render_func(Scene_t* scene)
{
LevelSelectSceneData_t* data = &(CONTAINER_OF(scene, LevelSelectScene_t, scene)->data);
Sprite_t* level_board = get_sprite(&scene->engine->assets, "lvl_board");
Sprite_t* level_select = get_sprite(&scene->engine->assets, "lvl_select");
Sprite_t* preview = get_sprite(&scene->engine->assets, "lvlprvw");
Font* menu_font = get_font(&scene->engine->assets, "MenuFont");
BeginTextureMode(scene->layers.render_layers[0].layer_tex);
ClearBackground(BLANK);
draw_sprite(level_select, 0, (Vector2){0,0},0, false);
draw_sprite(level_board, 0, (Vector2){level_select->frame_size.x,0},0, false);
draw_sprite(preview, data->scroll_area.curr_selection, (Vector2){
level_select->frame_size.x + (level_board->frame_size.x - preview->frame_size.x) / 2,
(level_board->frame_size.y - preview->frame_size.y) / 2,
},0, false);
DrawTextEx(*menu_font, "Level Select", (Vector2){60, 20}, 40, 4, BLACK);
vert_scrollarea_render(&data->scroll_area);
ClearBackground(RAYWHITE);
Rectangle draw_rec = (Rectangle){
0, data->scroll,
scene->layers.render_layers[0].render_area.width,
scene->layers.render_layers[0].render_area.height
};
Vector2 draw_pos = {50, 50};
draw_rec.height *= -1;
DrawTextureRec(
data->level_display.texture,
draw_rec,
draw_pos,
WHITE
);
EndTextureMode();
}
@ -31,70 +29,17 @@ static void level_select_do_action(Scene_t* scene, ActionType_t action, bool pre
switch(action)
{
case ACTION_UP:
if (!pressed)
if (pressed)
{
if (data->scroll_area.curr_selection > 0)
{
data->scroll_area.curr_selection--;
vert_scrollarea_refocus(&data->scroll_area);
}
data->scroll += 3;
data->scroll = Clamp(data->scroll, 0, 400);
}
break;
case ACTION_DOWN:
if (!pressed)
if (pressed)
{
if (data->scroll_area.curr_selection < data->level_pack->n_levels - 1)
{
data->scroll_area.curr_selection++;
vert_scrollarea_refocus(&data->scroll_area);
}
}
break;
case ACTION_EXIT:
if (!pressed)
{
if(scene->engine != NULL)
{
change_scene(scene->engine, MAIN_MENU_SCENE);
}
}
break;
case ACTION_NEXT_SPAWN:
if (!pressed)
{
unsigned int prev_sel = data->scroll_area.curr_selection;
// TODO: Add scene offset to scroll area calculation
if (vert_scrollarea_set_pos(&data->scroll_area, scene->mouse_pos) != data->scroll_area.max_items)
{
vert_scrollarea_refocus(&data->scroll_area);
if (prev_sel == data->scroll_area.curr_selection)
{
if (data->level_pack != NULL && data->scroll_area.curr_selection < data->level_pack->n_levels)
{
// TODO: Need to load the current level
LevelScene_t* level_scene = (LevelScene_t*)change_scene(scene->engine, GAME_SCENE);
level_scene->data.level_pack = data->level_pack;
level_scene->data.current_level = data->scroll_area.curr_selection;
reload_level_tilemap(level_scene);
}
}
}
}
break;
case ACTION_CONFIRM:
if (!pressed)
{
if (data->level_pack != NULL && data->scroll_area.curr_selection < data->level_pack->n_levels)
{
// TODO: Need to load the current level
LevelScene_t* level_scene = (LevelScene_t*)change_scene(scene->engine, GAME_SCENE);
level_scene->data.level_pack = data->level_pack;
level_scene->data.current_level = data->scroll_area.curr_selection;
reload_level_tilemap(level_scene);
}
data->scroll -= 3;
data->scroll = Clamp(data->scroll, 0, 400);
}
break;
default:
@ -102,64 +47,31 @@ static void level_select_do_action(Scene_t* scene, ActionType_t action, bool pre
}
}
#define FONT_SIZE 22
#define TEXT_PADDING 3
#define SCROLL_TOTAL_HEIGHT 800
void init_level_select_scene(LevelSelectScene_t* scene)
{
init_scene(&scene->scene, &level_select_do_action, 0);
init_scene(&scene->scene, &level_select_do_action);
add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y,
(Rectangle){
0, 0,
scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y
}
&scene->scene, 300, 400,
(Rectangle){0, 0, 300, 400}
);
scene->scene.bg_colour = BLACK;
Sprite_t* level_select = get_sprite(&scene->scene.engine->assets, "lvl_select");
vert_scrollarea_init(&scene->data.scroll_area, (Rectangle){50, 75, level_select->frame_size.x * 0.6, level_select->frame_size.y * 3 / 4}, (Vector2){level_select->frame_size.x * 0.6, SCROLL_TOTAL_HEIGHT});
vert_scrollarea_set_item_dims(&scene->data.scroll_area, FONT_SIZE, TEXT_PADDING);
Font* menu_font = get_font(&scene->scene.engine->assets, "MenuFont");
if (menu_font != NULL) {
scene->data.scroll_area.comp.font = *menu_font;
}
scene->data.scroll = 400;
scene->data.level_display = LoadRenderTexture(300, 800);
const unsigned int n_elems = 800 / (12+3);
BeginTextureMode(scene->data.level_display);
ClearBackground(GRAY);
for (unsigned int i = 0; i < n_elems; ++i)
{
char buf[32];
ScrollAreaRenderBegin(&scene->data.scroll_area);
ClearBackground(BLANK);
if (scene->data.level_pack != NULL)
{
vert_scrollarea_n_items(&scene->data.scroll_area, scene->data.level_pack->n_levels);
for (unsigned int i = 0; i < scene->data.level_pack->n_levels; ++i)
{
vert_scrollarea_insert_item(&scene->data.scroll_area, scene->data.level_pack->levels[i].level_name, i);
}
for (unsigned int i = scene->data.level_pack->n_levels; i < scene->data.scroll_area.max_items; ++i)
{
vert_scrollarea_insert_item(&scene->data.scroll_area, "---", i);
}
}
else
{
for (unsigned int i = 0; i < scene->data.scroll_area.max_items; ++i)
{
sprintf(buf, "Level %u", i);
vert_scrollarea_insert_item(&scene->data.scroll_area, buf, i);
DrawText(buf, 0, (12+3) * i, 12, BLACK);
}
}
ScrollAreaRenderEnd();
EndTextureMode();
sc_array_add(&scene->scene.systems, &level_select_render_func);
sc_map_put_64(&scene->scene.action_map, KEY_UP, ACTION_UP);
sc_map_put_64(&scene->scene.action_map, KEY_DOWN, ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, KEY_Q, ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_ENTER, ACTION_CONFIRM);
sc_map_put_64(&scene->scene.action_map, MOUSE_LEFT_BUTTON, ACTION_NEXT_SPAWN); // Abuse an unused action
}
void free_level_select_scene(LevelSelectScene_t* scene)
{
vert_scrollarea_free(&scene->data.scroll_area);
free_scene(&scene->scene);
}

View File

@ -1,50 +1,18 @@
#include "scene_impl.h"
#include "assets_tag.h"
#include "raymath.h"
#include <stdio.h>
static void menu_scene_render_func(Scene_t* scene)
{
MenuSceneData_t* data = &(CONTAINER_OF(scene, MenuScene_t, scene)->data);
Sprite_t* spr = get_sprite(&scene->engine->assets, "title_spr");
Sprite_t* title_spr = get_sprite(&scene->engine->assets, "title_board");
Sprite_t* title_select = get_sprite(&scene->engine->assets, "title_select");
Rectangle render_rec = scene->layers.render_layers[0].render_area;
Font* menu_font = get_font(&scene->engine->assets, "MenuFont");
BeginTextureMode(scene->layers.render_layers[0].layer_tex);
ClearBackground(RAYWHITE);
draw_sprite(spr, 0, (Vector2){0, 0}, 0, false);
draw_sprite(title_spr, 0, (Vector2){32, 10}, 0, false);
Vector2 font_sz = MeasureTextEx(*menu_font, "Bunny's Spelunking Adventure", 56, 0);
Vector2 title_pos = {
.x = (render_rec.width - font_sz.x) / 2,
.y = 32 + (title_spr->frame_size.y - font_sz.y) / 2
};
DrawTextEx(*menu_font, "Bunny's Spelunking Adventure", title_pos, 56, 0, BLACK);
const char* OPTIONS[3] = {"Start", "Credits", "Exit"};
for (uint8_t i = 0; i < 3; ++i)
{
Vector2 pos = (Vector2){data->buttons[i].bbox.x, data->buttons[i].bbox.y};
pos = Vector2Add(
pos,
shift_bbox(
(Vector2){
data->buttons[i].bbox.width,data->buttons[i].bbox.height
},
title_select->frame_size,
AP_MID_CENTER
)
);
draw_sprite(title_select, 0, pos, 0, false);
font_sz = MeasureTextEx(*menu_font, OPTIONS[i], 32, 6);
Vector2 title_pos = {
.x = pos.x + (title_select->frame_size.x - font_sz.x) / 2,
.y = pos.y + (title_spr->frame_size.y) / 2 - font_sz.y
};
hover_text(data->buttons + i, *menu_font, OPTIONS[i], title_pos, 32, 6, BLACK);
}
DrawText("This is a game", 25, 220, 12, BLACK);
UI_button(data->buttons, "Start");
UI_button(data->buttons + 1, "Sandbox");
UI_button(data->buttons + 2, "Continue");
UI_button(data->buttons + 3, "Exit");
EndTextureMode();
}
@ -53,9 +21,12 @@ static void exec_component_function(Scene_t* scene, int sel)
switch(sel)
{
case 0:
change_scene(scene->engine, LEVEL_SELECT_SCENE);
change_scene(scene->engine, 1);
break;
case 2:
case 1:
change_scene(scene->engine, 2);
break;
case 3:
scene->state = 0;
break;
default:
@ -161,31 +132,28 @@ static void gui_loop(Scene_t* scene)
void init_menu_scene(MenuScene_t* scene)
{
init_scene(&scene->scene, &menu_do_action, 0);
init_scene(&scene->scene, &menu_do_action);
sc_array_add(&scene->scene.systems, &gui_loop);
sc_array_add(&scene->scene.systems, &menu_scene_render_func);
int button_x = scene->scene.engine->intended_window_size.x / 8;
int button_y = scene->scene.engine->intended_window_size.y / 3;
int spacing = 100;
scene->data.buttons[0] = (UIComp_t) {
.bbox = {button_x,button_y,125,30},
.bbox = {25,255,125,30},
.state = STATE_NORMAL,
.alpha = 1.0
};
scene->data.buttons[1] = (UIComp_t) {
.bbox = {button_x,button_y+spacing,125,30},
.bbox = {25,300,125,30},
.state = STATE_NORMAL,
.alpha = 1.0
};
scene->data.buttons[2] = (UIComp_t) {
.bbox = {button_x,button_y+spacing * 2,125,30},
.bbox = {25,345,125,30},
.state = STATE_NORMAL,
.alpha = 1.0
};
scene->data.buttons[3] = (UIComp_t) {
.bbox = {button_x,button_y+spacing * 3,125,30},
.bbox = {25,390,125,30},
.state = STATE_NORMAL,
.alpha = 1.0
};

View File

@ -1,11 +1,10 @@
#include "engine.h"
#include "ent_impl.h"
#include "constants.h"
#include <stdio.h>
#include <string.h>
#include "raymath.h"
#define N_PLAYER_SPRITES 12
#define N_PLAYER_SPRITES 9
enum PlayerSpriteEnum
{
SPR_PLAYER_STAND = 0,
@ -16,10 +15,7 @@ enum PlayerSpriteEnum
SPR_PLAYER_CROUCH,
SPR_PLAYER_CRMOVE,
SPR_PLAYER_SWIM,
SPR_PLAYER_USWIM,
SPR_PLAYER_DSWIM,
SPR_PLAYER_DEAD,
SPR_PLAYER_ENTER,
};
static SpriteRenderInfo_t player_sprite_map[N_PLAYER_SPRITES] = {0};
@ -31,13 +27,8 @@ static unsigned int player_sprite_transition_func(Entity_t* ent)
CSprite_t* p_spr = get_component(ent, CSPRITE_T);
CPlayerState_t* p_plr = get_component(ent, CPLAYERSTATE_T);
if (p_ctrans->velocity.x > 0) {
p_spr->node.flip |= 1;
}
else if (p_ctrans->velocity.x < 0)
{
p_spr->node.flip &= ~1;
}
if (p_ctrans->velocity.x > 0) p_spr->flip_x = true;
else if (p_ctrans->velocity.x < 0) p_spr->flip_x = false;
p_spr->pause = false;
@ -59,29 +50,11 @@ static unsigned int player_sprite_transition_func(Entity_t* ent)
}
else if (p_move->water_state & 1)
{
if (p_ctrans->velocity.y > 50) return SPR_PLAYER_DSWIM;
if (p_ctrans->velocity.y < -50) return SPR_PLAYER_USWIM;
return SPR_PLAYER_SWIM;
}
return (p_ctrans->velocity.y < 0) ? SPR_PLAYER_JUMP : SPR_PLAYER_FALL;
}
static void player_sfx_func(GameEngine_t* engine, Entity_t* ent) {
CSprite_t* p_spr = get_component(ent, CSPRITE_T);
if (p_spr->current_idx == SPR_PLAYER_RUN) {
if (p_spr->current_frame % 3 == 0) {
play_sfx_pitched(engine, PLAYER_STEP_SFX, p_spr->current_frame == 0?1.0f : 0.75f );
}
}
if (p_spr->current_idx == SPR_PLAYER_CRMOVE) {
if (p_spr->current_frame % 2 == 0) {
play_sfx(engine, PLAYER_STEP_SFX);
}
}
}
Entity_t* create_player(EntityManager_t* ent_manager)
{
Entity_t* p_ent = add_entity(ent_manager, PLAYER_ENT_TAG);
@ -95,7 +68,7 @@ Entity_t* create_player(EntityManager_t* ent_manager)
p_ct->shape_factor = (Vector2){1, 1};
CJump_t* p_cjump = add_component(p_ent, CJUMP_COMP_T);
p_cjump->jump_speed = JUMP_SPEED;
p_cjump->jump_speed = 680;
p_cjump->jumps = 1;
p_cjump->max_jumps = 1;
p_cjump->jump_ready = true;
@ -125,22 +98,16 @@ Entity_t* create_player(EntityManager_t* ent_manager)
CAirTimer_t* p_air = add_component(p_ent, CAIRTIMER_T);
p_air->max_count = 10;
p_air->curr_count = 10;
p_air->max_ftimer = 0.9f;
p_air->max_ftimer = 1.0f;
p_air->decay_rate = 1.0f;
CSprite_t* p_cspr = add_component(p_ent, CSPRITE_T);
p_cspr->sprites = player_sprite_map;
p_cspr->transition_func = &player_sprite_transition_func;
p_cspr->sfx_func = &player_sfx_func;
p_cspr->node.colour = WHITE;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->depth = 1;
CEmitter_t* p_emitter = add_component(p_ent, CEMITTER_T);
p_emitter->offset = (Vector2){7,0};
add_component(p_ent, CSQUISHABLE_T);
return p_ent;
}
@ -157,69 +124,12 @@ Entity_t* create_dead_player(EntityManager_t* ent_manager)
CSprite_t* p_cspr = add_component(p_ent, CSPRITE_T);
p_cspr->sprites = player_sprite_map;
p_cspr->current_idx = SPR_PLAYER_DEAD;
p_cspr->node.colour = WHITE;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->depth = 3;
add_component(p_ent, CMOVEMENTSTATE_T);
return p_ent;
}
static unsigned int player_finish_transition_func(Entity_t* ent)
{
CSprite_t* p_spr = get_component(ent, CSPRITE_T);
// Due to index-from-0
if (p_spr->current_frame == p_spr->sprites[p_spr->current_idx].sprite->frame_count - 1)
{
p_spr->pause = true;
//remove_entity(ent->manager, ent->m_id);
}
return p_spr->current_idx;
}
Entity_t* create_player_finish(EntityManager_t* ent_manager)
{
Entity_t* p_ent = add_entity(ent_manager, NO_ENT_TAG);
if (p_ent == NULL) return NULL;
CSprite_t* p_cspr = add_component(p_ent, CSPRITE_T);
p_cspr->sprites = player_sprite_map;
p_cspr->current_idx = SPR_PLAYER_ENTER;
p_cspr->transition_func = &player_finish_transition_func;
p_cspr->node.colour = WHITE;
p_cspr->node.scale = (Vector2){1, 1};
p_cspr->depth = 1;
CLifeTimer_t* p_clifetimer = add_component(p_ent, CLIFETIMER_T);
p_clifetimer->life_time = 0.9f;
return p_ent;
}
static AnchorPoint_t parse_anchor_symbol(const char symbol[2])
{
if (symbol[0] == 't')
{
if (symbol[1] == 'l') return AP_TOP_LEFT;
if (symbol[1] == 'c') return AP_TOP_CENTER;
if (symbol[1] == 'r') return AP_TOP_RIGHT;
}
else if (symbol[0] == 'm')
{
if (symbol[1] == 'l') return AP_MID_LEFT;
if (symbol[1] == 'c') return AP_MID_CENTER;
if (symbol[1] == 'r') return AP_MID_RIGHT;
}
else if (symbol[0] == 'b')
{
if (symbol[1] == 'l') return AP_BOT_LEFT;
if (symbol[1] == 'c') return AP_BOT_CENTER;
if (symbol[1] == 'r') return AP_BOT_RIGHT;
}
return AP_TOP_LEFT;
}
static bool init_player_file(FILE* in_file, Assets_t* assets)
{
static bool already_init = false;
@ -246,23 +156,18 @@ static bool init_player_file(FILE* in_file, Assets_t* assets)
while(*info_str == ' ' || *info_str == '\t') info_str++;
Vector2 offset;
char src_ap_symbol[3];
char dest_ap_symbol[3];
int data_count = sscanf(
info_str, "%f,%f,%2s,%2s",
&offset.x, &offset.y, src_ap_symbol, dest_ap_symbol
info_str, "%f,%f",
&offset.x, &offset.y
);
if (data_count != 4)
if (data_count !=2)
{
printf("Unable to parse info for player at line %lu\n", line_num);
return false;
}
Sprite_t* spr = get_sprite(assets, name);
spr->anchor = Vector2Scale(spr->frame_size, 0.5f);
player_sprite_map[i].sprite = spr;
player_sprite_map[i].offset = offset;
player_sprite_map[i].src_anchor = parse_anchor_symbol(src_ap_symbol);
player_sprite_map[i].dest_anchor = parse_anchor_symbol(dest_ap_symbol);
i++;
}
already_init = true;

View File

@ -27,14 +27,8 @@ typedef struct CoinCounter
uint16_t total;
}CoinCounter_t;
typedef enum CameraMode {
CAMERA_FOLLOW_PLAYER = 0,
CAMERA_RANGED_MOVEMENT,
} CameraMode_t;
typedef struct LevelCamera {
Camera2D cam;
CameraMode_t mode;
Vector2 target_pos;
float base_y;
//Vector2 prev_pos;
@ -42,25 +36,8 @@ typedef struct LevelCamera {
float mass;
float c; // damping factor
float k; // spring constant
float range_limit;
}LevelCamera_t;
typedef enum LevelSceneState {
LEVEL_STATE_STARTING = 0,
LEVEL_STATE_RUNNING,
LEVEL_STATE_DEAD,
LEVEL_STATE_COMPLETE,
} LevelSceneState_t;
typedef struct LevelSceneStateMachine {
LevelSceneState_t state;
system_func_t state_functions[4];
// Engine has no timeline support, so make a pseudo timeline implementation
float fractional;
uint32_t counter;
} LevelSceneStateMachine_t;
typedef struct LevelSceneData {
TileGrid_t tilemap;
// TODO: game_rec is actually obsolete since this is in the scene game layer
@ -73,18 +50,8 @@ typedef struct LevelSceneData {
unsigned int current_level;
CoinCounter_t coins;
bool show_grid;
Vector2 player_spawn;
LevelSceneStateMachine_t sm;
RenderManager render_manager;
}LevelSceneData_t;
static inline void change_level_state(LevelSceneData_t* data, LevelSceneState_t state)
{
data->sm.state = state;
data->sm.counter = 0;
data->sm.fractional = 0;
}
typedef struct LevelScene {
Scene_t scene;
LevelSceneData_t data;
@ -95,8 +62,6 @@ void free_game_scene(LevelScene_t* scene);
void init_sandbox_scene(LevelScene_t* scene);
void free_sandbox_scene(LevelScene_t* scene);
void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles, Rectangle view_zone);
void clear_an_entity(Scene_t* scene, TileGrid_t* tilemap, Entity_t* p_ent);
void clear_all_game_entities(LevelScene_t* scene);
void term_level_scene_data(LevelSceneData_t* data);
void reload_level_tilemap(LevelScene_t* scene);
void load_next_level_tilemap(LevelScene_t* scene);
@ -122,8 +87,8 @@ typedef struct MenuScene {
} MenuScene_t;
typedef struct LevelSelectSceneData {
VertScrollArea_t scroll_area;
LevelPack_t* level_pack;
RenderTexture2D level_display;
float scroll;
} LevelSelectSceneData_t;
typedef struct LevelSelectScene {

View File

@ -1,15 +1,10 @@
#include "collisions.h"
#include "scene_impl.h"
#include "water_flow.h"
#include "ent_impl.h"
#include "constants.h"
#include "raymath.h"
void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* tiles, Rectangle view_zone)
{
init_render_manager(&data->render_manager);
data->game_rec = view_zone;
memset(&data->camera, 0, sizeof(LevelCamera_t));
data->camera.cam.rotation = 0.0f;
@ -17,7 +12,6 @@ void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* t
data->camera.mass = 0.2f;
data->camera.k = 6.2f;
data->camera.c = 2.2f;
data->camera.range_limit = 200.0f;
data->tilemap.max_tiles = max_tiles;
if (tiles != NULL)
@ -46,9 +40,6 @@ void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* t
}
memset(&data->coins, 0, sizeof(data->coins));
data->show_grid = false;
memset(&data->sm, 0, sizeof(data->sm));
}
void term_level_scene_data(LevelSceneData_t* data)
@ -59,44 +50,6 @@ void term_level_scene_data(LevelSceneData_t* data)
}
}
void clear_an_entity(Scene_t* scene, TileGrid_t* tilemap, Entity_t* p_ent)
{
/* Use the helper function to remove any entity
* This is because some components may have deinit steps
* This function will also take care of the tilemap collision handling
* */
CEmitter_t* p_emitter = get_component(p_ent, CEMITTER_T);
if (p_emitter != NULL)
{
unload_emitter_handle(&scene->part_sys, p_emitter->handle);
}
if (get_component(p_ent, CWATERRUNNER_T)!= NULL)
{
free_water_runner(p_ent, &scene->ent_manager);
}
remove_entity_from_tilemap(&scene->ent_manager, tilemap, p_ent);
}
void clear_all_game_entities(LevelScene_t* scene)
{
Entity_t* ent;
sc_map_foreach_value(&scene->scene.ent_manager.entities, ent)
{
clear_an_entity(&scene->scene, &scene->data.tilemap, ent);
}
// This is unnecessary as the first pass should clear everything
// For now, leave it in. This is not expected to call all the time
// so not too bad
clear_entity_manager(&scene->scene.ent_manager);
for (size_t i = 0; i < scene->data.tilemap.n_tiles;i++)
{
sc_map_clear_64v(&scene->data.tilemap.tiles[i].entities_set);
}
}
bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
{
if (level_num >= scene->data.level_pack->n_levels) return false;
@ -109,61 +62,27 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
scene->data.tilemap.width = lvl_map.width;
scene->data.tilemap.height = lvl_map.height;
scene->data.tilemap.n_tiles = n_tiles;
scene->data.coins.current = 0;
scene->data.coins.total = lvl_map.n_chests;
#define N_SOLID_TILESETS 3
static const char* SOLID_TILE_SELECTIONS[N_SOLID_TILESETS] = {
"stile0", "stile1", "stile2"
};
scene->data.selected_solid_tilemap = lvl_map.flags;
scene->data.solid_tile_sprites = get_sprite(&scene->scene.engine->assets, SOLID_TILE_SELECTIONS[lvl_map.flags]);
clear_all_game_entities(scene);
clear_entity_manager(&scene->scene.ent_manager);
for (size_t i = 0; i < scene->data.tilemap.n_tiles;i++)
{
scene->data.tilemap.tiles[i].solid = NOT_SOLID;
scene->data.tilemap.tiles[i].tile_type = EMPTY_TILE;
scene->data.tilemap.tiles[i].rotation = TILE_NOROTATE;
scene->data.tilemap.tiles[i].connectivity = 0;
scene->data.tilemap.tiles[i].moveable = true;
scene->data.tilemap.tiles[i].offset = (Vector2){0, 0};
scene->data.tilemap.tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
scene->data.tilemap.tiles[i].def = 0;
memset(scene->data.tilemap.render_nodes + i, 0, sizeof(RenderInfoNode));
scene->data.tilemap.render_nodes[i].pos = (Vector2){
(i % scene->data.tilemap.width) * TILE_SIZE,
(i / scene->data.tilemap.width) * TILE_SIZE,
};
scene->data.tilemap.render_nodes[i].scale = (Vector2){1,1};
scene->data.tilemap.render_nodes[i].colour = WHITE;
if (lvl_map.tiles[i].tile_type == SOLID_TILE)
{
change_a_tile(&scene->data.tilemap, i, SOLID_TILE);
}
if (lvl_map.tiles[i].water > MAX_WATER_LEVEL) {
scene->data.tilemap.tiles[i].max_water_level = 0;
}
else
{
scene->data.tilemap.tiles[i].max_water_level = 4;
scene->data.tilemap.tiles[i].water_level = lvl_map.tiles[i].water;
scene->data.tilemap.tiles[i].wet = scene->data.tilemap.tiles[i].water_level > 0;
}
}
scene->data.tilemap.tiles[i].solid = NOT_SOLID;
scene->data.tilemap.tiles[i].tile_type = EMPTY_TILE;
scene->data.tilemap.tiles[i].moveable = true;
scene->data.tilemap.tiles[i].size = (Vector2){TILE_SIZE, TILE_SIZE};
sc_map_clear_64v(&scene->data.tilemap.tiles[i].entities_set);
// Two pass, because some tile depends on the solidity of the tiles
for (size_t i = 0; i < scene->data.tilemap.n_tiles;i++)
{
if (lvl_map.tiles[i].tile_type == SOLID_TILE)
if (lvl_map.tiles[i].tile_type == 1)
{
change_a_tile(&scene->data.tilemap, i, SOLID_TILE);
}
scene->data.tilemap.tiles[i].water_level = lvl_map.tiles[i].water;
}
// Two pass
for (size_t i = 0; i < scene->data.tilemap.n_tiles;i++)
{
if (lvl_map.tiles[i].tile_type >= 8 && lvl_map.tiles[i].tile_type < 20)
{
uint32_t tmp_idx = lvl_map.tiles[i].tile_type - 8;
@ -217,74 +136,12 @@ bool load_level_tilemap(LevelScene_t* scene, unsigned int level_num)
Entity_t* ent = create_player(&scene->scene.ent_manager);
ent->position.x = (i % scene->data.tilemap.width) * scene->data.tilemap.tile_size;
ent->position.y = (i / scene->data.tilemap.width) * scene->data.tilemap.tile_size;
scene->data.camera.target_pos.x = ent->position.x;
scene->data.camera.target_pos.y = ent->position.y;
scene->data.camera.cam.target.x = ent->position.x;
scene->data.camera.cam.target.y = ent->position.y;
ent->spawn_pos = ent->position;
}
break;
case 23:
{
Entity_t* ent = create_chest(&scene->scene.ent_manager);
ent->position.x = (i % scene->data.tilemap.width) * scene->data.tilemap.tile_size;
ent->position.y = (i / scene->data.tilemap.width) * scene->data.tilemap.tile_size;
CTransform_t* p_ctransform = get_component(ent, CTRANSFORM_COMP_T);
p_ctransform->active = true;
}
break;
case 24:
{
Entity_t* ent = create_level_end(&scene->scene.ent_manager);
if (ent != NULL)
{
ent->position.x = (i % scene->data.tilemap.width) * scene->data.tilemap.tile_size;
ent->position.y = (i / scene->data.tilemap.width) * scene->data.tilemap.tile_size + (scene->data.tilemap.tile_size >> 1);
}
}
break;
default:
break;
}
if (lvl_map.tiles[i].tile_type >= 25)
{
Entity_t* ent = create_urchin(&scene->scene.ent_manager);
if (ent != NULL)
{
CBBox_t* p_bbox = get_component(ent, CBBOX_COMP_T);
ent->position.x = (i % scene->data.tilemap.width) * scene->data.tilemap.tile_size + (scene->data.tilemap.tile_size >> 1) - p_bbox->half_size.x;
ent->position.y = (int)(i / scene->data.tilemap.width) * scene->data.tilemap.tile_size + (scene->data.tilemap.tile_size >> 1) - p_bbox->half_size.y;
uint8_t spd_encoding = lvl_map.tiles[i].tile_type - 25;
float angle = 45.0f / 180.0f * PI * ((spd_encoding >> 2) & 7);
float mag = 75 * (spd_encoding & 3);
CTransform_t* p_ct = get_component(ent, CTRANSFORM_COMP_T);
p_ct->velocity = Vector2Scale(
(Vector2){cosf(angle), sinf(angle)}, mag
);
}
}
}
// This only works for static loading.
// If a tilemap change change to some other arbitrary tile.
// Then this should be done while changing a tile.
if (lvl_map.tiles[i].tile_type == SOLID_TILE)
{
scene->data.tilemap.render_nodes[i].spr = scene->data.solid_tile_sprites;
scene->data.tilemap.render_nodes[i].frame_num = CONNECTIVITY_TILE_MAPPING[
scene->data.tilemap.tiles[i].connectivity
];
}
else
{
uint8_t tile_sprite_idx = scene->data.tilemap.tiles[i].tile_type + scene->data.tilemap.tiles[i].rotation;
scene->data.tilemap.render_nodes[i].spr = scene->data.tile_sprites[
tile_sprite_idx
];
}
}
@ -331,20 +188,20 @@ void change_a_tile(TileGrid_t* tilemap, unsigned int tile_idx, TileType_t new_ty
break;
case LADDER:
{
//int up_tile = tile_idx - tilemap->width;
//if (up_tile > 0 && tilemap->tiles[up_tile].tile_type != LADDER)
//{
// tilemap->tiles[tile_idx].solid = ONE_WAY;
//}
//else
int up_tile = tile_idx - tilemap->width;
if (up_tile > 0 && tilemap->tiles[up_tile].tile_type != LADDER)
{
tilemap->tiles[tile_idx].solid = ONE_WAY;
}
else
{
tilemap->tiles[tile_idx].solid = NOT_SOLID;
}
//unsigned int down_tile = tile_idx + tilemap->width;
//if (down_tile < tilemap->n_tiles && tilemap->tiles[down_tile].tile_type == LADDER)
//{
// tilemap->tiles[down_tile].solid = (tilemap->tiles[tile_idx].tile_type != LADDER)? ONE_WAY : NOT_SOLID;
//}
unsigned int down_tile = tile_idx + tilemap->width;
if (down_tile < tilemap->n_tiles && tilemap->tiles[down_tile].tile_type == LADDER)
{
tilemap->tiles[down_tile].solid = (tilemap->tiles[tile_idx].tile_type != LADDER)? ONE_WAY : NOT_SOLID;
}
}
break;
case SPIKES:
@ -360,39 +217,37 @@ void change_a_tile(TileGrid_t* tilemap, unsigned int tile_idx, TileType_t new_ty
unsigned int down_tile = tile_idx + tilemap->width;
if (down_tile < tilemap->n_tiles && tilemap->tiles[down_tile].tile_type == LADDER)
{
tilemap->tiles[down_tile].solid = NOT_SOLID;
tilemap->tiles[down_tile].solid = ONE_WAY;
}
}
tilemap->tiles[tile_idx].rotation = TILE_NOROTATE;
const int SPIKE_HITBOX_LONGSIDE = 30;
const int SPIKE_HITBOX_SHORTSIDE = 12;
if (new_type == SPIKES)
{
// Priority: Down, Up, Left, Right
if (tile_idx + tilemap->width < tilemap->n_tiles && tilemap->tiles[tile_idx + tilemap->width].tile_type == SOLID_TILE)
{
tilemap->tiles[tile_idx].offset = (Vector2){0,tilemap->tile_size - SPIKE_HITBOX_SHORTSIDE};
tilemap->tiles[tile_idx].size = (Vector2){SPIKE_HITBOX_LONGSIDE, SPIKE_HITBOX_SHORTSIDE};
tilemap->tiles[tile_idx].offset = (Vector2){0,16};
tilemap->tiles[tile_idx].size = (Vector2){32,16};
}
else if (tile_idx >= tilemap->width && tilemap->tiles[tile_idx - tilemap->width].tile_type == SOLID_TILE)
else if (tile_idx - tilemap->width >= 0 && tilemap->tiles[tile_idx - tilemap->width].tile_type == SOLID_TILE)
{
tilemap->tiles[tile_idx].offset = (Vector2){0,0};
tilemap->tiles[tile_idx].size = (Vector2){SPIKE_HITBOX_LONGSIDE, SPIKE_HITBOX_SHORTSIDE};
tilemap->tiles[tile_idx].size = (Vector2){32,16};
tilemap->tiles[tile_idx].rotation = TILE_180ROT;
}
else if (tile_idx % tilemap->width != 0 && tilemap->tiles[tile_idx - 1].tile_type == SOLID_TILE)
{
tilemap->tiles[tile_idx].offset = (Vector2){0,0};
tilemap->tiles[tile_idx].size = (Vector2){SPIKE_HITBOX_SHORTSIDE, SPIKE_HITBOX_LONGSIDE};
tilemap->tiles[tile_idx].size = (Vector2){16,32};
tilemap->tiles[tile_idx].rotation = TILE_90CWROT;
}
else if ((tile_idx + 1) % tilemap->width != 0 && tilemap->tiles[tile_idx + 1].tile_type == SOLID_TILE)
{
tilemap->tiles[tile_idx].offset = (Vector2){tilemap->tile_size - SPIKE_HITBOX_SHORTSIDE,0};
tilemap->tiles[tile_idx].size = (Vector2){SPIKE_HITBOX_SHORTSIDE, SPIKE_HITBOX_LONGSIDE};
tilemap->tiles[tile_idx].offset = (Vector2){16,0};
tilemap->tiles[tile_idx].size = (Vector2){16,32};
tilemap->tiles[tile_idx].rotation = TILE_90CCWROT;
}
else
@ -415,7 +270,6 @@ void change_a_tile(TileGrid_t* tilemap, unsigned int tile_idx, TileType_t new_ty
tilemap->tiles[tile_idx].moveable = (
tilemap->tiles[tile_idx].tile_type == EMPTY_TILE
|| tilemap->tiles[tile_idx].tile_type == SPIKES
|| tilemap->tiles[tile_idx].tile_type == LADDER
);
tilemap->tiles[tile_idx].def = (tilemap->tiles[tile_idx].tile_type == SOLID_TILE) ? 5: 2;

View File

@ -289,10 +289,11 @@ void update_water_runner_system(Scene_t* scene)
break;
case SCANLINE_FILL:
{
const float FILL_RATE = 1.0f/22;
const float FILL_RATE = 1.0f/24;
p_crunner->fractional += scene->delta_time;
if (p_crunner->fractional < FILL_RATE) break;
p_crunner->fractional -= FILL_RATE;
// Unsigned usage here is okay
unsigned int start_tile =
(p_crunner->current_tile / p_crunner->bfs_tilemap.width) * p_crunner->bfs_tilemap.width;
@ -307,7 +308,6 @@ void update_water_runner_system(Scene_t* scene)
if (curr_tile->water_level < curr_tile->max_water_level)
{
curr_tile->water_level++;
p_crunner->fractional -= FILL_RATE;
}
if (curr_tile->water_level < curr_tile->max_water_level)
{

1
tracy

@ -1 +0,0 @@
Subproject commit 5d542dc09f3d9378d005092a4ad446bd405f819a

View File

@ -110,7 +110,7 @@ static void level_scene_render_func(Scene_t* scene)
if (spr.sprite != NULL)
{
Vector2 pos = Vector2Add(p_ent->position, spr.offset);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->node.flip & 1);
draw_sprite(spr.sprite, p_cspr->current_frame, pos, 0.0f, p_cspr->flip_x);
}
}
}
@ -400,7 +400,7 @@ int main(void)
LevelScene_t scene;
scene.scene.engine = &engine;
init_scene(&scene.scene, &level_do_action, ENABLE_ENTITY_MANAGEMENT_SYSTEM);
init_scene(&scene.scene, &level_do_action);
init_entity_tag_map(&scene.scene.ent_manager, PLAYER_ENT_TAG, 4);
init_entity_tag_map(&scene.scene.ent_manager, DYNMEM_ENT_TAG, 16);
init_level_scene_data(