From 41f57d5ba85e72cf801e8ee91afe55d40d535701 Mon Sep 17 00:00:00 2001 From: Julia <145168563+julia-aph@users.noreply.github.com> Date: Thu, 9 May 2024 11:59:33 -0500 Subject: [PATCH] aa aa --- .vscode/settings.json | 3 +- .vscode/tasks.json | 37 ++++++++ build.py | 17 ++-- source/fumoengine/input/ctrl.c | 40 ++++----- source/fumoengine/input/ctrl.h | 36 ++------ source/fumoengine/input/input.h | 26 +++--- .../fumoengine/input/platforms/parseinput.c | 29 ++++--- source/fumotris/fumotris.c | 10 +-- source/fumotris/fumotris.h | 80 +++++++++++++----- test.exe | Bin 88290 -> 88287 bytes 10 files changed, 164 insertions(+), 114 deletions(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 94cdde1..8b5a32d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -59,5 +59,6 @@ "ringbuffer.h": "c", "fumoengine.h": "c", "fumocommon.h": "c" - } + }, + "cmake.configureOnOpen": false } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a785b23 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,37 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc.exe build active file", + "command": "C:/mingw64/bin/gcc.exe", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}\\${fileBasenameNoExtension}.exe" + ], + "options": { + "cwd": "C:/mingw64/bin" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + }, + { + "type": "shell", + "label": "Fumo build script", + "command": "python build.py", + "group": { + "kind": "build", + "isDefault": true + }, + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/build.py b/build.py index 23b9c28..8e11286 100644 --- a/build.py +++ b/build.py @@ -1,9 +1,12 @@ -import os, sys -import json, hashlib +import hashlib +import json +import os +import subprocess +import sys -def walk_source(path): - source_paths = [] +def walk_source_dir(path: str) -> tuple[list[str], list[str]]: + source_paths : list[str] = [] subdirs = [] for dirpath, dirnames, filenames in os.walk(path): @@ -55,8 +58,8 @@ def get_object_names(path): return object_names -def build(source_path, obj_path, out_path, recompile = False): - source_paths, subdirs = walk_source(source_path) +def build(source_path, obj_path, out_path, recompile): + source_paths, subdirs = walk_source_dir(source_path) if recompile: result = os.system(f"gcc {' '.join(source_paths)} -I {' -I '.join(subdirs)} -o {out_path} -pthread -Wall -std=c17 -pedantic") @@ -91,4 +94,4 @@ def build(source_path, obj_path, out_path, recompile = False): print(os.system(f"gcc {obj_path}\\*.o -o {out_path} -pthread -Wall -std=c17 -pedantic -g")) -build(sys.argv[1], sys.argv[2], sys.argv[3], True) \ No newline at end of file +build("source/", "objects/", "debug", True) \ No newline at end of file diff --git a/source/fumoengine/input/ctrl.c b/source/fumoengine/input/ctrl.c index b7f994a..deba581 100644 --- a/source/fumoengine/input/ctrl.c +++ b/source/fumoengine/input/ctrl.c @@ -32,44 +32,36 @@ void FreeController(struct Controller *ctrl) FreeDictionary(&ctrl->binds); } -u32 hash_bind(u16f bind, u16f type) +u32 hash_bind(u16f code, u16f type) { - return bind + (type << 16); + return code | (type << 16); } -struct InputAxis *ControllerMap( - struct Controller *ctrl, - struct ControlMapping *map -) { - struct InputAxis *axis = &ctrl->axes[map->code]; +bool ControllerBind(struct Controller *ctrl, u16 control, u16 code, u16 type) +{ + u32 hash = hash_bind(code, type); - u32 hash = hash_bind(map->bind, map->type); + struct InputAxis *axis = &ctrl->axes[control]; struct InputAxis **bind = DictionarySet(BIND_T, &ctrl->binds, hash, axis); - if (bind == nullptr) { - printf("whar"); - exit(1); - } + if (bind == nullptr) + return false; *bind = axis; - axis->type = map->type; - return axis; + return true; } -bool ControllerMapMulti( +bool ControllerBindMulti( struct Controller *ctrl, usize n, - struct ControlMapping *maps, - struct InputAxis **binds + u16 *controls, + u16 *codes, + u16 *types ) { for (usize i = 0; i < n; i++) { - struct InputAxis *axis = ControllerMap(ctrl, maps + i); - - if (axis == nullptr) + if (!ControllerBind(ctrl, controls[i], codes[i], types[i])) return false; - - binds[i] = axis; } return true; @@ -81,7 +73,7 @@ void dispatch_update(struct InputAxis *axis, struct InputRecord *rec) axis->is_down = true; axis->is_held = true; axis->last_pressed = rec->time; - } else if (rec->is_up) { + } else { axis->is_up = true; axis->is_held = false; axis->last_released = rec->time; @@ -104,7 +96,7 @@ void ControllerPoll(struct Controller *ctrl, struct RecordBuffer *recs) for (usize i = 0; i < recs->head.len; i++) { struct InputRecord *rec = recs->buf + i; - u32 hash = hash_bind(rec->bind, rec->type); + u32 hash = hash_bind(rec->code, rec->type); struct InputAxis *axis = DictionaryFind(BIND_T, &ctrl->binds, hash); if (axis == nullptr) diff --git a/source/fumoengine/input/ctrl.h b/source/fumoengine/input/ctrl.h index 24030c9..fce02e0 100644 --- a/source/fumoengine/input/ctrl.h +++ b/source/fumoengine/input/ctrl.h @@ -4,30 +4,6 @@ #include "input.h" -struct ControlMapping { - u16 code; - u16 bind; - u16 type; -}; - -struct InputAxis { - nsec last_pressed; - nsec last_released; - - union { - struct Button but; - struct Axis axis; - struct Joystick js; - union InputData data; - }; - - u16 type; - - bool is_down; - bool is_held; - bool is_up; -}; - struct Controller { struct InputAxis *pending[IO_BUF_SIZE]; usize pending_len; @@ -43,16 +19,14 @@ bool CreateController(struct Controller *ctrl); void FreeController(struct Controller *ctrl); -struct InputAxis *ControllerMap( - struct Controller *ctrl, - struct ControlMapping *map -); +bool ControllerBind(struct Controller *ctrl, u16 control, u16 code, u16 type); -bool ControllerMapMulti( +bool ControllerBindMulti( struct Controller *ctrl, usize n, - struct ControlMapping *maps, - struct InputAxis **axis_ptrs + u16 *controls, + u16 *codes, + u16 *types ); void ControllerPoll(struct Controller *ctrl, struct RecordBuffer *recs); diff --git a/source/fumoengine/input/input.h b/source/fumoengine/input/input.h index ae4e158..e7f91ce 100644 --- a/source/fumoengine/input/input.h +++ b/source/fumoengine/input/input.h @@ -33,24 +33,30 @@ struct Joystick { }; union InputData { - struct Button input_but; - struct Axis input_axis; - struct Joystick input_js; + struct Button but; + struct Axis axis; + struct Joystick js; }; struct InputRecord { nsec time; - union { - struct Button but; - struct Axis axis; - struct Joystick js; - union InputData data; - }; + union InputData data; - u16 bind; + u16 code; u16 type; + bool is_down; +}; + +struct InputAxis { + nsec last_pressed; + nsec last_released; + + union InputData data; + + u16 type; + bool is_down; bool is_held; bool is_up; diff --git a/source/fumoengine/input/platforms/parseinput.c b/source/fumoengine/input/platforms/parseinput.c index b92717e..d6e6bb8 100644 --- a/source/fumoengine/input/platforms/parseinput.c +++ b/source/fumoengine/input/platforms/parseinput.c @@ -1,30 +1,29 @@ #include "parseinput.h" -void ReadButton(struct InputRecord *rec, u16f bind, bool is_down) +void ReadButton(struct InputRecord *rec, u16f code, bool is_down) { - rec->bind = bind; + rec->code = code; rec->type = BUTTON; rec->is_down = is_down; - rec->is_up = !is_down; } -void ReadAxis(struct InputRecord *rec, u16f bind, u64 value) +void ReadAxis(struct InputRecord *rec, u16f code, u64 value) { - rec->bind = bind; + rec->code = code; rec->type = AXIS; - rec->axis.value = value; + rec->data.axis.value = value; } -void ReadJoystick(struct InputRecord *rec, u16f bind, i32 x, i32 y) +void ReadJoystick(struct InputRecord *rec, u16f code, i32 x, i32 y) { - rec->bind = bind; + rec->code = code; rec->type = JOYSTICK; - rec->js.x = x; - rec->js.y = y; + rec->data.js.x = x; + rec->data.js.y = y; } size_t UCS2ToUTF8(char *buf, u16f ucs2) @@ -40,8 +39,10 @@ size_t UCS2ToUTF8(char *buf, u16f ucs2) return 2; } - buf[0] = 0xE0 + (ucs2 >> 12); - buf[1] = 0x80 + ((ucs2 >> 6) & 0x3F); - buf[2] = 0x80 + (ucs2 & 0x3F); - return 3; + else { + buf[0] = 0xE0 + (ucs2 >> 12); + buf[1] = 0x80 + ((ucs2 >> 6) & 0x3F); + buf[2] = 0x80 + (ucs2 & 0x3F); + return 3; + } } \ No newline at end of file diff --git a/source/fumotris/fumotris.c b/source/fumotris/fumotris.c index 155efd1..ef7cfcc 100644 --- a/source/fumotris/fumotris.c +++ b/source/fumotris/fumotris.c @@ -2,9 +2,6 @@ struct Fumotris { - struct ControlMapping mappings[BINDS_N]; - struct InputAxis *input[BINDS_N]; - struct TetrMap board; struct TetrMap piece; }; @@ -15,7 +12,7 @@ void FumotrisStart(void *inst_arg, void *game_arg) struct FumoInstance *inst = inst_arg; struct Fumotris *game = game_arg; - ControllerMapMulti(&inst->ctrl, BINDS_N, game->mappings, game->input); + ControllerBindMulti(&inst->ctrl, BINDS_N, controls_g, codes_g, types_g); CreateTetrMap(&game->board, 10, 10); CreateTetrMap(&game->piece, 3, 3); @@ -27,9 +24,10 @@ void FumotrisUpdate(void *inst_arg, void *game_arg) struct FumoInstance *inst = inst_arg; struct Fumotris *game = game_arg; - if (game->input[LEFT]->is_down) + if (inst->ctrl.axes[LEFT].is_down) game->piece.x -= 1; - if (game->input[RIGHT]->is_down) + + if (inst->ctrl.axes[RIGHT].is_down) game->piece.x += 1; TetrMapDraw(&game->board, &inst->term); diff --git a/source/fumotris/fumotris.h b/source/fumotris/fumotris.h index 27cdfaa..de2d6f8 100644 --- a/source/fumotris/fumotris.h +++ b/source/fumotris/fumotris.h @@ -5,7 +5,7 @@ #define BINDS_N 12 -enum FumotrisCode { +enum FumotrisControls { LEFT, RIGHT, SOFT_DROP, @@ -15,64 +15,102 @@ enum FumotrisCode { ROTATE_180, SWAP, ESC, + VSCROLL, HSCROLL, + MOUSE }; -struct ControlMapping mappings_global[BINDS_N] = { - { LEFT, 0x25, BUTTON }, - { RIGHT, 0x27, BUTTON }, - { SOFT_DROP, 0x28, BUTTON }, - { HARD_DROP, 0x20, BUTTON }, - { ROTATE_CCW, 'Z', BUTTON }, - { ROTATE_CW, 'X', BUTTON }, - { ROTATE_180, 'A', BUTTON }, - { SWAP, 'C', BUTTON }, - { ESC, 0x1B, BUTTON }, - { VSCROLL, 0, AXIS }, - { HSCROLL, 1, AXIS }, - { MOUSE, 0, JOYSTICK } +u16 controls_g[BINDS_N] = { + LEFT, + RIGHT, + SOFT_DROP, + HARD_DROP, + ROTATE_CCW, + ROTATE_CW, + ROTATE_180, + SWAP, + ESC, + + VSCROLL, + HSCROLL, + + MOUSE, }; -const u8 I[16] = { +u16 codes_g[BINDS_N] = { + 0x25, + 0x27, + 0x28, + 0x20, + 'Z', + 'X', + 'A', + 'C', + 0x1B, + + 0, + 1, + + 0 +}; + +u16 types_g[BINDS_N] = { + BUTTON, + BUTTON, + BUTTON, + BUTTON, + BUTTON, + BUTTON, + BUTTON, + BUTTON, + BUTTON, + + AXIS, + AXIS, + + JOYSTICK +}; + +u8 I[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 }; -const u8 O[4] = { +u8 O[4] = { 1, 1, 1, 1 }; -const u8 T[9] = { +u8 T[9] = { 0, 1, 0, 1, 1, 1, 0, 0, 0 }; -const u8 S[9] = { +u8 S[9] = { 0, 1, 1, 1, 1, 0, 0, 0, 0 }; -const u8 Z[9] = { +u8 Z[9] = { 1, 1, 0, 0, 1, 1, 0, 0, 0 }; -const u8 J[9] = { +u8 J[9] = { 1, 0, 0, 1, 1, 1, 0, 0, 0 }; -const u8 L[9] = { +u8 L[9] = { 0, 0, 1, 1, 1, 1, 0, 0, 0 diff --git a/test.exe b/test.exe index 4ff9e847e8cdb50732f5dd540788e87fd745ac6d..cd52a7302b8831e646402e3a070cd1e279119c42 100644 GIT binary patch delta 17153 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ΞJ;Ei-e0SWpZ0A!C4ezmCvk3LR0MaE=&- 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 hjbQ&39Gah7P%vk~ti{>0<}aL