commit f3f667738640a5da6cf3b42b07583eff9457d8b3 Author: KikooDX Date: Tue Mar 2 00:21:01 2021 +0100 Decently small and minimalistic working base. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f21bab --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Build files +/build-fx +/build-cg +/*.g1a +/*.g3a + +# Python bytecode + __pycache__/ + +# Common IDE files +*.sublime-project +*.sublime-workspace +.vscode + +# KBLE backup files +backup_*.kble + +# Generated C files. +gen_*.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..37d27d8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +# Configure with [fxsdk build-fx] or [fxsdk build-cg], which provide the +# toolchain file and module path of the fxSDK + +cmake_minimum_required(VERSION 3.18) +project(MyAddin) + +execute_process(COMMAND python3 kble.py + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + +include(GenerateG1A) +include(GenerateG3A) +include(Fxconv) +include_directories(include) +find_package(Gint 2.1 REQUIRED) + +set(SOURCES + src/main.c + src/player.c + src/input.c + src/gen_levels.c + # ... +) +# Shared assets, fx-9860G-only assets and fx-CG-50-only assets +set(ASSETS + # ... +) +#set(ASSETS_fx +# assets-fx/example.png +# # ... +#) +#set(ASSETS_cg +# assets-cg/example.png +# # ... +#) + +fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA) + +add_executable(myaddin ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}}) +target_compile_options(myaddin PRIVATE -Wall -Wextra -Os) +target_link_libraries(myaddin Gint::Gint) + +#if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G) +# generate_g1a(TARGET myaddin OUTPUT "MyAddin.g1a" +# NAME "Painful" ICON assets-fx/icon.png) +if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50) + generate_g3a(TARGET myaddin OUTPUT "Painfull.g3a" + NAME "Painful" ICONS assets-cg/icon-uns.png assets-cg/icon-sel.png) +endif() diff --git a/assets-cg/fxconv-metadata.txt b/assets-cg/fxconv-metadata.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assets-cg/fxconv-metadata.txt @@ -0,0 +1 @@ + diff --git a/assets-cg/icon-sel.png b/assets-cg/icon-sel.png new file mode 100644 index 0000000..7137b50 Binary files /dev/null and b/assets-cg/icon-sel.png differ diff --git a/assets-cg/icon-uns.png b/assets-cg/icon-uns.png new file mode 100644 index 0000000..3c99f62 Binary files /dev/null and b/assets-cg/icon-uns.png differ diff --git a/assets/levels/chaos.kble b/assets/levels/chaos.kble new file mode 100644 index 0000000..2533911 Binary files /dev/null and b/assets/levels/chaos.kble differ diff --git a/assets/levels/damage_boosting_101.kble b/assets/levels/damage_boosting_101.kble new file mode 100644 index 0000000..7e4eaff Binary files /dev/null and b/assets/levels/damage_boosting_101.kble differ diff --git a/assets/levels/die_and_retry.kble b/assets/levels/die_and_retry.kble new file mode 100644 index 0000000..8369365 Binary files /dev/null and b/assets/levels/die_and_retry.kble differ diff --git a/assets/levels/dome.kble b/assets/levels/dome.kble new file mode 100644 index 0000000..9a94f36 Binary files /dev/null and b/assets/levels/dome.kble differ diff --git a/assets/levels/end.kble b/assets/levels/end.kble new file mode 100644 index 0000000..a070128 Binary files /dev/null and b/assets/levels/end.kble differ diff --git a/assets/levels/hello_world.kble b/assets/levels/hello_world.kble new file mode 100644 index 0000000..e53f7d8 Binary files /dev/null and b/assets/levels/hello_world.kble differ diff --git a/assets/levels/key_101.kble b/assets/levels/key_101.kble new file mode 100644 index 0000000..9f25fb9 Binary files /dev/null and b/assets/levels/key_101.kble differ diff --git a/assets/levels/so_far_but_so_close.kble b/assets/levels/so_far_but_so_close.kble new file mode 100644 index 0000000..22910b7 Binary files /dev/null and b/assets/levels/so_far_but_so_close.kble differ diff --git a/assets/levels/two_for_one.kble b/assets/levels/two_for_one.kble new file mode 100644 index 0000000..70983be Binary files /dev/null and b/assets/levels/two_for_one.kble differ diff --git a/assets/levels/up_and_down.kble b/assets/levels/up_and_down.kble new file mode 100644 index 0000000..44b5590 Binary files /dev/null and b/assets/levels/up_and_down.kble differ diff --git a/include/conf.h b/include/conf.h new file mode 100644 index 0000000..455717d --- /dev/null +++ b/include/conf.h @@ -0,0 +1,8 @@ +#pragma once + +#define TARGET_UPS 60 +#define TARGET_FPS 30 +#define TILE_SIZE 16 +#define LEVEL_WIDTH 16 +#define LEVEL_HEIGHT 16 +#define LEVEL_SIZE (LEVEL_WIDTH * LEVEL_HEIGHT) diff --git a/include/input.h b/include/input.h new file mode 100644 index 0000000..acd0228 --- /dev/null +++ b/include/input.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#define KEYS_COUNT 6 +enum { + K_LEFT, + K_RIGHT, + K_DOWN, + K_JUMP, + K_RESTART, + K_EXIT +}; + +#define S_PRESSED 0 +#define S_DOWN 1 +#define S_RELEASED 2 +#define S_UP 3 + +typedef struct Input { + uint8_t keys[KEYS_COUNT]; + uint8_t states[KEYS_COUNT]; +} Input; + +/* Check for new key inputs and update accordingly. */ +void input_update(Input *input); + +/* Initialize values. */ +void input_init(Input *input); + +/* Get state of keys. */ +bool input_is_pressed(Input *input, uint8_t key); +bool input_is_down(Input *input, uint8_t key); +bool input_is_released(Input *input, uint8_t key); +bool input_is_up(Input *input, uint8_t key); diff --git a/include/level.h b/include/level.h new file mode 100644 index 0000000..9cad609 --- /dev/null +++ b/include/level.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include "conf.h" +#include "vec2.h" + +typedef uint8_t tile_t; +typedef struct Level{ + tile_t content[LEVEL_WIDTH * LEVEL_HEIGHT]; + Vec2 start_pos; +} Level; diff --git a/include/player.h b/include/player.h new file mode 100644 index 0000000..87b5396 --- /dev/null +++ b/include/player.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include "vec2.h" + +typedef struct Player{ + Vec2 pos; + Vec2 spd; + int8_t facing; + bool stun; + bool knocked; + uint8_t keys_left; + uint8_t jump_buffer; + uint8_t coyot; +} Player; + +Player player_init(); diff --git a/include/tiles.h b/include/tiles.h new file mode 100644 index 0000000..e4b1a43 --- /dev/null +++ b/include/tiles.h @@ -0,0 +1,13 @@ +#pragma once + +enum { + AIR_TILE, + SOLID_TILE, + PAIN_TILE, + SPAWN_TILE, + EXIT_TILE, + KEY_TILE, + SEMI_SOLID, + CHECKY_TILE, +}; +#define OUT_OF_BOUNDS AIR_TILE diff --git a/include/vec2.h b/include/vec2.h new file mode 100644 index 0000000..c7cac25 --- /dev/null +++ b/include/vec2.h @@ -0,0 +1,8 @@ +#pragma once +#include + +typedef int16_t vec2_int_t; +typedef struct Vec2{ + vec2_int_t x; + vec2_int_t y; +} Vec2; diff --git a/kble.py b/kble.py new file mode 100644 index 0000000..05b9c88 --- /dev/null +++ b/kble.py @@ -0,0 +1,109 @@ +""" +The MIT License (MIT) + +Copyright © 2021 KikooDX + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +“Software”), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +def info(*args) -> None: + #print("info:", *args) + pass + +def merge_bytes(_bytes: bytes) -> int: + """ + Get a bytes slice and merge them into an integer. + """ + mul = 256 ** len(_bytes) + sum = 0 + for byte in _bytes: + mul //= 256 + sum += byte * mul + return sum + +def kble_parse(_bytes: bytes) -> list: + """ + Read content of binary file using the KBLE format and output a two + dimensional int grid. + """ + # Read whole file at once. + # Check version byte. + assert(_bytes[0] == 0) + # Get byte length of cells. + cell_size: int = _bytes[1] + info("Cell size:", cell_size) + # Get level dimensions. + width = merge_bytes(_bytes[2:4]) + height = merge_bytes(_bytes[4:6]) + info("Level width:", width) + info("Level height:", height) + # Check than the number of bytes matches the obtained informations. + assert(len(_bytes) == width * height * cell_size + 6) + info("Correct format.") + # Process remaining bytes and add them to a list. + grid: list = [[0 for _ in range(width)] for _ in range(height)] + i: int = 6 + x: int = 0 + y: int = 0 + while (i < len(_bytes)): + SLICE = _bytes[i:i + cell_size] + grid[y][x] = merge_bytes(SLICE) + i += cell_size + x += 1 + if x == width: + x = 0 + y += 1 + info("Parsing ended successfully.") + return grid + +if __name__ == "__main__": + LEVELS_PATHS = ( + "assets/levels/hello_world.kble", + "assets/levels/damage_boosting_101.kble", + "assets/levels/chaos.kble", + "assets/levels/so_far_but_so_close.kble", + "assets/levels/key_101.kble", + "assets/levels/two_for_one.kble", + "assets/levels/up_and_down.kble", + "assets/levels/dome.kble", + "assets/levels/die_and_retry.kble", + "assets/levels/end.kble") + with open("src/gen_levels.c", "w") as c_file: + c_file.write("#include \"level.h\"\n#include \"vec2.h\"\n") + c_file.write("const Level levels[] = {\n") + for level_path in LEVELS_PATHS: + c_file.write("\t{\n\t\t.content = {") + with open(level_path, "rb") as file: + BYTES = file.read() + data = kble_parse(BYTES) + start_x = 0 + start_y = 0 + for y, line in enumerate(data): + for x, tile in enumerate(line): + c_file.write(str(tile)) + c_file.write(", ") + if tile == 3: # spawn tile + start_x, start_y = x, y + c_file.write("},\n") + c_file.write("\t\t.start_pos = (Vec2){") + c_file.write(f"{start_x}, {start_y}") + c_file.write("}\n\t},\n") + c_file.write("};") + diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..d5c12eb --- /dev/null +++ b/src/input.c @@ -0,0 +1,55 @@ +#include +#include "input.h" + +void input_update(Input *input) { + /* Read full input stream. */ + clearevents(); + /* For each key, update state. */ + for (int i = 0; i < KEYS_COUNT; ++i) { + uint8_t *state = &input->states[i]; + const uint8_t key = input->keys[i]; + /* See if the key is pressed. */ + const bool pressed = keydown(key); + /* Update input status. */ + if (pressed) { + if (*state == S_RELEASED || *state == S_UP) + *state = S_PRESSED; + else + *state = S_DOWN; + } + else { + if (*state == S_PRESSED || *state == S_DOWN) + *state = S_RELEASED; + else + *state = S_UP; + } + } +} + +void input_init(Input *input) { + /* initialize all values to S_UP, avoid random bugs */ + input->keys[K_LEFT] = KEY_LEFT; + input->keys[K_RIGHT] = KEY_RIGHT; + input->keys[K_DOWN] = KEY_DOWN; + input->keys[K_JUMP] = KEY_SHIFT; + input->keys[K_RESTART] = KEY_6; + input->keys[K_EXIT] = KEY_EXIT; + for (int i = 0; i < KEYS_COUNT; ++i) + input->states[i] = S_UP; +} + +bool input_is_pressed(Input *input, uint8_t key) { + return input->states[key] == S_PRESSED; +} + +bool input_is_down(Input *input, uint8_t key) { + return (input->states[key] == S_DOWN) || (input->states[key] == S_PRESSED); +} + +bool input_is_released(Input *input, uint8_t key) { + return input->states[key] == S_RELEASED; +} + +bool input_is_up(Input *input, uint8_t key) { + return (input->states[key] == S_UP) || (input->states[key] == S_RELEASED); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f90dcce --- /dev/null +++ b/src/main.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include "conf.h" +#include "input.h" +#include "level.h" +#include "player.h" + +void load_level(Level *level, Player *player, uint8_t id) { + extern Level levels[LEVEL_SIZE]; + memcpy(level->content, levels[id].content, LEVEL_SIZE); + level->start_pos = levels[id].start_pos; + player->pos = levels[id].start_pos; +} + +int callback(volatile void *arg) { + volatile bool *has_ticked = arg; + *has_ticked = true; + return 0; +} + +int main(void) { + /* Initialize player. */ + Player player = player_init(); + /* Initialize level. */ + Level level = (Level){}; + uint8_t level_id = 0; + load_level(&level, &player, level_id); + /* Initialize input. */ + Input input = (Input){}; + input_init(&input); + + /* UPS control. */ + volatile bool has_ticked = true; + int timer = timer_setup(TIMER_ANY, 1000000/TARGET_UPS, callback, &has_ticked); + timer_start(timer); + + /* Core loop. */ + while (!input_is_down(&input, K_EXIT)) { + /* Repeat step event so the UPS is constant. */ + for (uint8_t i = 0; i < TARGET_UPS / TARGET_FPS; i += 1) { + /* UPS control. */ + while (!has_ticked) sleep(); + has_ticked = false; + /* Update. */ + input_update(&input); + /* player_update(&player, &level, &level_id); */ + } + /* Draw. */ + dclear(C_BLACK); + /* player_draw(player); */ + dupdate(); + } + + return 1; +} diff --git a/src/player.c b/src/player.c new file mode 100644 index 0000000..7a71554 --- /dev/null +++ b/src/player.c @@ -0,0 +1,15 @@ +#include "player.h" +#include "vec2.h" + +Player player_init() { + return (Player){ + .pos = (Vec2){}, + .spd = (Vec2){}, + .facing = 1, + .stun = false, + .knocked = false, + .keys_left = 0, + .jump_buffer = 0, + .coyot = 0, + }; +}