From 62b37ed348366d083da19c047390003aed40ff92 Mon Sep 17 00:00:00 2001 From: Julia <145168563+julia-aph@users.noreply.github.com> Date: Tue, 7 May 2024 17:10:15 -0500 Subject: [PATCH] hj kj --- Fumofumotris.code-workspace | 4 +- source/fumoengine/fumocommon.h | 2 + source/fumoengine/fumoengine.c | 58 +++++++++--- source/fumoengine/fumoengine.h | 16 +++- source/fumoengine/include/event.c | 32 ++++--- source/fumoengine/include/event.h | 19 ++-- source/fumoengine/include/ringbuffer.c | 22 ++--- source/fumoengine/include/ringbuffer.h | 42 ++++----- source/fumoengine/include/vector.c | 47 ++++++++++ source/fumoengine/include/vector.h | 26 ++++++ source/fumoengine/input/ctrl.c | 14 +++ source/fumoengine/input/ctrl.h | 10 +- source/fumoengine/input/input.c | 15 +-- source/fumoengine/input/input.h | 2 +- source/fumoengine/terminal/terminal.c | 63 ++++++------- source/fumoengine/terminal/terminal.h | 6 +- source/fumotris/fumotris.c | 121 +++++-------------------- source/fumotris/fumotris.h | 84 +++++++++++++++++ source/fumotris/tetr.c | 77 +++++++--------- source/fumotris/tetr.h | 21 ++--- test.exe | Bin 85669 -> 87052 bytes 21 files changed, 401 insertions(+), 280 deletions(-) create mode 100644 source/fumoengine/include/vector.c create mode 100644 source/fumoengine/include/vector.h create mode 100644 source/fumotris/fumotris.h diff --git a/Fumofumotris.code-workspace b/Fumofumotris.code-workspace index 76dc364..3a25141 100644 --- a/Fumofumotris.code-workspace +++ b/Fumofumotris.code-workspace @@ -58,7 +58,9 @@ "typeinfo": "cpp", "execution": "cpp", "stdbool.h": "c", - "fumoengine.h": "c" + "fumoengine.h": "c", + "event.h": "c", + "fumocommon.h": "c" } } } \ No newline at end of file diff --git a/source/fumoengine/fumocommon.h b/source/fumoengine/fumocommon.h index dc9d7ba..f1739d6 100644 --- a/source/fumoengine/fumocommon.h +++ b/source/fumoengine/fumocommon.h @@ -1,4 +1,6 @@ #pragma once +#include +#include #include #define nullptr ((void *)0) diff --git a/source/fumoengine/fumoengine.c b/source/fumoengine/fumoengine.c index 5e6cb08..7f900cd 100644 --- a/source/fumoengine/fumoengine.c +++ b/source/fumoengine/fumoengine.c @@ -2,32 +2,62 @@ #include "platform.h" +VectorT FUMOSYS_VEC_T = VECTOR_T(struct FumoHook); + + void Panic(char *message) { printf(message); exit(1); } -void InvokeUpdate(void_func func, void *arg) -{ - struct { nsec frametime; } *args = arg; - - ((update_func)func)(args->frametime); -} - -bool FumoInit(struct FumoGame *game) +bool CreateFumoInstance(struct FumoInstance *instance) { if (!PlatformInit()) Panic("Platform failed to initialize"); - if (!CreateController(&game->ctrl)) + if (!CreateController(&instance->ctrl)) Panic("Out of memory"); - if (!CreateEvent(&game->update)) - Panic("Out of memory"); - - if (!BeginInputThread(&game->input_hand)) + if (!CreateInputThread(&instance->input_hand)) Panic("Input handle failed to initialize"); - return 0; + if (!CreateTerminal(&instance->term, 20, 10)) + Panic("Out of memory"); + + if (!CreateEvent(&instance->on_start)) + Panic("Out of memory"); + + if (!CreateEvent(FUMOSYS_VEC_T, &instance->on_update)) + Panic("Out of memory"); + + instance->time = TimeNow(); + + return true; +} + +bool FumoInstanceRun(struct FumoInstance *instance) +{ + usize buf_n = TerminalMaxOut(&instance->term); + char *buf = malloc(buf_n); + + while (true) { + if (!InputAquire(&instance->input_hand)) + Panic("Aquire failed"); + + ControllerPoll(&instance->ctrl, &instance->input_hand.recs); + + if (!InputRelease(&instance->input_hand)) + Panic("Release failed"); + + nsec now = TimeNow(); + instance->frametime = now - instance->time; + FumoInvoke(instance, &instance->on_update); + instance->time = now; + + TerminalPrint(&instance->term, buf, buf_n); + puts(buf); + + _sleep(100); + } } \ No newline at end of file diff --git a/source/fumoengine/fumoengine.h b/source/fumoengine/fumoengine.h index 560b7b8..7997863 100644 --- a/source/fumoengine/fumoengine.h +++ b/source/fumoengine/fumoengine.h @@ -3,21 +3,27 @@ #include "event.h" #include "fumocommon.h" #include "input.h" +#include "terminal.h" +#include "vector.h" -struct FumoGame { +struct FumoInstance { struct Controller ctrl; struct InputHandle input_hand; + struct Terminal term; - struct Event start; - struct Event draw; - struct Event update; + struct Event on_start; + struct Event on_update; nsec time; + nsec frametime; }; void Panic(char *message); -bool FumoInit(struct FumoGame *game); +bool CreateFumoInstance(struct FumoInstance *game); +bool FumoInstanceHook(struct Vector *vec, update_func update, void *state); + +bool FumoInstanceRun(struct FumoInstance *game); \ No newline at end of file diff --git a/source/fumoengine/include/event.c b/source/fumoengine/include/event.c index 4e23fc9..fecef60 100644 --- a/source/fumoengine/include/event.c +++ b/source/fumoengine/include/event.c @@ -1,19 +1,17 @@ #include "event.h" -#define INIT_CAPACITY 16 - bool CreateEvent(struct Event *event) { - void_func *callbacks = malloc(INIT_CAPACITY * sizeof(void_func)); + struct Method *methods = malloc(16 * sizeof(struct Method)); - if (callbacks == nullptr) + if (methods == nullptr) return false; *event = (struct Event) { - .callbacks = callbacks, + .methods = methods, .len = 0, - .capacity = INIT_CAPACITY + .capacity = 16 }; return true; @@ -21,30 +19,34 @@ bool CreateEvent(struct Event *event) void FreeEvent(struct Event *event) { - free(event->callbacks); + free(event->methods); } -bool EventRegister(struct Event *event, void_func callback) +bool EventRegister(struct Event *event, callback func, void *instance) { if (event->len == event->capacity) { - usize new_size = event->capacity * 2 * sizeof(void_func); - void_func *new_callbacks = realloc(event->callbacks, new_size); + usize new_size = event->capacity * 2 * sizeof(struct Method); + struct Method *new_methods = realloc(event->methods, new_size); - if (new_callbacks == nullptr) + if (new_methods == nullptr) return false; - event->callbacks = new_callbacks; + event->methods = new_methods; event->capacity = new_size; } - event->callbacks[event->len++] = callback; + event->methods[event->len++] = (struct Method) { + .func = func, + .instance = instance + }; return true; } -void EventInvoke(struct Event *event, void *args) +void EventInvoke(struct Event *event, void *state) { for (usize i = 0; i < event->len; i++) { - event->callbacks[i](args); + struct Method *method = event->methods + i; + method->func(state, method->instance); } } \ No newline at end of file diff --git a/source/fumoengine/include/event.h b/source/fumoengine/include/event.h index 4a2e833..1efd478 100644 --- a/source/fumoengine/include/event.h +++ b/source/fumoengine/include/event.h @@ -1,27 +1,26 @@ #pragma once -#include -#include -#include #include #include #include "fumocommon.h" -typedef void (*void_func)(void *arg); - -typedef void (*invoker_func)(void_func func, void *arg); +typedef void (*callback)(void *state, void *instance); +struct Method { + callback func; + void *instance; +}; struct Event { - void_func *callbacks; + struct Method *methods; usize len; usize capacity; }; -bool CreateEvent(struct Event *event); +bool CreateEvent(struct Event *event, void *state); -bool EventRegister(struct Event *event, void_func callback); +bool EventRegister(struct Event *event, callback func, void *instance); -void EventInvoke(struct Event *event, void *args); \ No newline at end of file +void EventInvoke(struct Event *event); \ No newline at end of file diff --git a/source/fumoengine/include/ringbuffer.c b/source/fumoengine/include/ringbuffer.c index 2a7914b..6e6c16a 100644 --- a/source/fumoengine/include/ringbuffer.c +++ b/source/fumoengine/include/ringbuffer.c @@ -3,26 +3,26 @@ #include -void *get_ptr(RingBufferT T, struct RingBufferHead *head, size_t i) +void *get_ptr(RingBufferT T, struct RingBufferHead *head, usize i) { u8 *bytes = (u8 *)head; return bytes + T->OFFSET + T->SIZE * i; } -size_t RingBufferEmpty(RingBufferT T, struct RingBufferHead *head) +usize RingBufferEmpty(RingBufferT T, struct RingBufferHead *head) { return T->LEN - head->len; } -void *RingBufferGet(RingBufferT T, struct RingBufferHead *head, size_t i) +void *RingBufferGet(RingBufferT T, struct RingBufferHead *head, usize i) { - size_t wrap_i = (head->start + i) % T->LEN; + usize wrap_i = (head->start + i) % T->LEN; return get_ptr(T, head, wrap_i); } void *RingBufferNext(RingBufferT T, struct RingBufferHead *head) { - size_t wrap_i = (head->start + head->len) % T->LEN; + usize wrap_i = (head->start + head->len) % T->LEN; return get_ptr(T, head, wrap_i); } @@ -37,9 +37,9 @@ void RingBufferTransfer( struct RingBufferHead *dest, struct RingBufferHead *tmp ) { - size_t copy_max = min_usize(T->LEN - dest->len, tmp->len); + usize copy_max = min_usize(T->LEN - dest->len, tmp->len); - for (size_t i = 0; i < copy_max; i++) { + for (usize i = 0; i < copy_max; i++) { void *to = RingBufferGet(T, dest, dest->len + i); void *from = RingBufferGet(T, tmp, i); memcpy(to, from, T->SIZE); @@ -49,15 +49,15 @@ void RingBufferTransfer( tmp->len -= copy_max; } -size_t RingBufferOut( +usize RingBufferOut( RingBufferT T, - size_t n, + usize n, void *dest, struct RingBufferHead *src ) { - size_t copy_max = min_usize(n, src->len); + usize copy_max = min_usize(n, src->len); - for (size_t i = 0; i < copy_max; i++) { + for (usize i = 0; i < copy_max; i++) { void *to = (u8 *)dest + i * T->SIZE; void *from = RingBufferGet(T, src, i); memcpy(to, from, T->SIZE); diff --git a/source/fumoengine/include/ringbuffer.h b/source/fumoengine/include/ringbuffer.h index 57b0dde..57f1653 100644 --- a/source/fumoengine/include/ringbuffer.h +++ b/source/fumoengine/include/ringbuffer.h @@ -1,41 +1,39 @@ #pragma once -#include #include -#include -#include #include "fumocommon.h" - -typedef const struct RingBufferT { - size_t OFFSET; - size_t SIZE; - size_t LEN; -} *const RingBufferT; - -struct RingBufferHead { - size_t len; - size_t start; -}; - -#define RINGBUF_T_INIT(RBUF_STRUCT, RBUF_ITEM, RBUF_LEN) \ +#define RINGBUF_T(RBUF_ITEM_T, RBUF_LEN) \ (&(struct RingBufferT) { \ .OFFSET = offsetof(struct { \ struct RingBufferHead head; \ - RBUF_ITEM item; \ + RBUF_ITEM_T item; \ }, item), \ - .SIZE = sizeof(RBUF_ITEM), \ + .SIZE = sizeof(RBUF_ITEM_T), \ .LEN = RBUF_LEN \ }) \ #define RINGBUF_HEAD_INIT ((struct RingBufferHead) { 0, 0 }) -size_t RingBufferEmpty(RingBufferT T, struct RingBufferHead *head); + +typedef const struct RingBufferT { + usize OFFSET; + usize SIZE; + usize LEN; +} *const RingBufferT; + +struct RingBufferHead { + usize len; + usize start; +}; + + +usize RingBufferEmpty(RingBufferT T, struct RingBufferHead *head); void *RingBufferGet( RingBufferT T, struct RingBufferHead *head, - size_t i + usize i ); void *RingBufferNext(RingBufferT T, struct RingBufferHead *head); @@ -48,9 +46,9 @@ void RingBufferTransfer( struct RingBufferHead *tmp ); -size_t RingBufferOut( +usize RingBufferOut( RingBufferT T, - size_t n, + usize n, void *dest, struct RingBufferHead *src ); \ No newline at end of file diff --git a/source/fumoengine/include/vector.c b/source/fumoengine/include/vector.c new file mode 100644 index 0000000..2db2bfd --- /dev/null +++ b/source/fumoengine/include/vector.c @@ -0,0 +1,47 @@ +#include "vector.h" +#include +#include + + +bool CreateVector(VectorT T, struct Vector *vec) +{ + void *array = malloc(16 * T->SIZE); + + if (array == nullptr) + return false; + + *vec = (struct Vector) { + .len = 0, + .capacity = 16, + .array = array + }; + + return true; +} + +void FreeVector(struct Vector *vec) +{ + free(vec->array); +} + +void *VectorGet(VectorT T, struct Vector *vec, usize i) +{ + return (u8 *)vec->array + i * T->SIZE; +} + +bool VectorAdd(VectorT T, struct Vector *vec, void *item) +{ + if (vec->len == vec->capacity) { + usize new_capacity = vec->capacity * 2; + void *new_array = realloc(vec->array, new_capacity * T->SIZE); + + if (new_array == nullptr) + return false; + + vec->capacity = new_capacity; + } + + memcpy(VectorGet(T, vec, vec->len++), item, T->SIZE); + + return true; +} \ No newline at end of file diff --git a/source/fumoengine/include/vector.h b/source/fumoengine/include/vector.h new file mode 100644 index 0000000..8610531 --- /dev/null +++ b/source/fumoengine/include/vector.h @@ -0,0 +1,26 @@ +#include "fumocommon.h" + +#define VECTOR_T(VEC_ITEM_T) \ + (&(struct VectorT) { \ + .SIZE = sizeof(VEC_ITEM_T) \ + }) \ + + +typedef const struct VectorT { + usize SIZE; +} *const VectorT; + +struct Vector { + usize len; + usize capacity; + void *array; +}; + + +bool CreateVector(VectorT T, struct Vector *vec); + +void FreeVector(struct Vector *vec); + +void *VectorGet(VectorT T, struct Vector *vec, usize i); + +bool VectorAdd(VectorT T, struct Vector *vec, void *item); \ No newline at end of file diff --git a/source/fumoengine/input/ctrl.c b/source/fumoengine/input/ctrl.c index a46df95..b80440a 100644 --- a/source/fumoengine/input/ctrl.c +++ b/source/fumoengine/input/ctrl.c @@ -120,6 +120,20 @@ bool ControllerMap(struct Controller *ctrl, u16f code, u16f bind, u16f type) return true; } +bool ControllerMapMulti( + struct Controller *ctrl, + usize n_maps, + struct ControlMapping *maps +) { + for (usize i = 0; i < n_maps; i++) { + struct ControlMapping *map = maps + i; + if (!ControllerMap(ctrl, map->code, map->bind, map->type)) + return false; + } + + return true; +} + struct InputAxis *ControllerGet(struct Controller *ctrl, u16f code, u16f type) { struct ctrl_bkt *code_bkt = find(&ctrl->codes, as_id(code, type)); diff --git a/source/fumoengine/input/ctrl.h b/source/fumoengine/input/ctrl.h index c55c73c..1399c7b 100644 --- a/source/fumoengine/input/ctrl.h +++ b/source/fumoengine/input/ctrl.h @@ -57,8 +57,8 @@ struct Controller { struct ctrl_dict binds; }; -struct ControlBind { - int code; +struct ControlMapping { + u16 code; u16 bind; u16 type; }; @@ -69,6 +69,12 @@ void FreeController(struct Controller *ctrl); bool ControllerMap(struct Controller *ctrl, u16f code, u16f bind, u16f type); +bool ControllerMapMulti( + struct Controller *ctrl, + usize n_maps, + struct ControlMapping *maps +); + struct InputAxis *ControllerGet(struct Controller *ctrl, u16f code, u16f type); void ControllerPoll(struct Controller *ctrl, struct RecordBuffer *recs); diff --git a/source/fumoengine/input/input.c b/source/fumoengine/input/input.c index 6aa1ea7..365820a 100644 --- a/source/fumoengine/input/input.c +++ b/source/fumoengine/input/input.c @@ -4,17 +4,8 @@ #include "platform.h" -RingBufferT IO_BUF_T = RINGBUF_T_INIT( - struct RecordBuffer, - struct InputRecord, - IO_BUF_SIZE -); - -RingBufferT STR_BUF_T = RINGBUF_T_INIT( - struct StringBuffer, - char, - STR_BUF_SIZE -); +RingBufferT IO_BUF_T = RINGBUF_T(struct InputRecord, IO_BUF_SIZE); +RingBufferT STR_BUF_T = RINGBUF_T(char, STR_BUF_SIZE); void *input_worker(void *arg) @@ -54,7 +45,7 @@ void *input_worker(void *arg) return nullptr; } -bool BeginInputThread(struct InputHandle *hand) +bool CreateInputThread(struct InputHandle *hand) { *hand = (struct InputHandle) { .recs.head = RINGBUF_HEAD_INIT, diff --git a/source/fumoengine/input/input.h b/source/fumoengine/input/input.h index 1495b74..26e05c0 100644 --- a/source/fumoengine/input/input.h +++ b/source/fumoengine/input/input.h @@ -100,7 +100,7 @@ struct InputHandle { bool is_terminating; }; -bool BeginInputThread(struct InputHandle *hand); +bool CreateInputThread(struct InputHandle *hand); bool EndInputThread(struct InputHandle *hand); diff --git a/source/fumoengine/terminal/terminal.c b/source/fumoengine/terminal/terminal.c index cb11f6d..ddae341 100644 --- a/source/fumoengine/terminal/terminal.c +++ b/source/fumoengine/terminal/terminal.c @@ -1,14 +1,15 @@ #include "terminal.h" +#include #define RESET_STR_LEN 7 #define MAX_CH4_LEN 11 -usize TerminalStringSize(usize wid, usize hgt) +usize TerminalMaxOut(struct Terminal *term) { return RESET_STR_LEN - + MAX_CH4_LEN * wid * hgt - + hgt + + MAX_CH4_LEN * term->wid * term->hgt + + term->hgt + 1; } @@ -23,7 +24,7 @@ bool CreateTerminal(struct Terminal *term, usize wid, usize hgt) .wid = wid, .hgt = hgt, - .ch4s = ch4s + .buf = ch4s }; return true; @@ -31,10 +32,10 @@ bool CreateTerminal(struct Terminal *term, usize wid, usize hgt) void FreeTerminal(struct Terminal *term) { - free(term->ch4s); + free(term->buf); } -usize u8_to_buf(char *buf, u8f x) +usize u8_to_str(char *out, u8f x) { usize len = 1; @@ -51,17 +52,17 @@ usize u8_to_buf(char *buf, u8f x) hnds = tens / 10; len = 3; - buf[0] = hnds + 48; - buf[1] = tens + 48; - buf[2] = ones + 48; + out[0] = hnds + 48; + out[1] = tens + 48; + out[2] = ones + 48; } else { len = 2; - buf[0] = tens + 48; - buf[1] = ones + 48; + out[0] = tens + 48; + out[1] = ones + 48; } } else { - buf[0] = x + 48; + out[0] = x + 48; } return len; @@ -77,57 +78,57 @@ u8f ansi_fg(u8f fg) return fg + (fg < 8 ? 30 : 82); } -usize ch4_dif_to_buf(char *buf, struct Color4 *dif, struct Char4 *ch4) +usize ch4_dif_to_str(char *out, struct Color4 *dif, struct Char4 *ch4) { usize len = 0; if (dif->bg != ch4->color.bg) { - buf[len++] = '\x1b'; - buf[len++] = '['; - len += u8_to_buf(buf + len, ansi_bg(ch4->color.bg)); + out[len++] = '\x1b'; + out[len++] = '['; + len += u8_to_str(out + len, ansi_bg(ch4->color.bg)); if (dif->fg != ch4->color.fg) { - buf[len++] = ';'; - len += u8_to_buf(buf + len, ansi_fg(ch4->color.fg)); + out[len++] = ';'; + len += u8_to_str(out + len, ansi_fg(ch4->color.fg)); } - buf[len++] = 'm'; + out[len++] = 'm'; } else if (dif->fg != ch4->color.fg) { - buf[len++] = '\x1b'; - buf[len++] = '['; - len += u8_to_buf(buf + len, ansi_fg(ch4->color.fg)); - buf[len++] = 'm'; + out[len++] = '\x1b'; + out[len++] = '['; + len += u8_to_str(out + len, ansi_fg(ch4->color.fg)); + out[len++] = 'm'; } - buf[len++] = ch4->ch; + out[len++] = ch4->ch; *dif = ch4->color; return len; } -usize TerminalPrint(char *buf, usize n, struct Terminal *term) +usize TerminalPrint(struct Terminal *term, char *out, usize n) { - struct Char4 dif; + struct Color4 dif = { 0, 0 }; usize len = 7; - memcpy(buf, "\x1b[H\x1b[0m", 7); + memcpy(out, "\x1b[H\x1b[0m", 7); for (usize y = 0; y < term->hgt; y++) { for (usize x = 0; x < term->wid; x++) { usize i = y * term->wid + x; - struct Char4 *ch4 = &term->ch4s[i]; + struct Char4 *ch4 = &term->buf[i]; // DEBUG if (ch4->ch == 0) ch4->ch = '#'; // DEBUG - len += ch4_dif_to_buf(buf + len, &dif, ch4); + len += ch4_dif_to_str(out + len, &dif, ch4); } - buf[len++] = '\n'; + out[len++] = '\n'; } - buf[len] = 0; + out[len] = 0; return len; } \ No newline at end of file diff --git a/source/fumoengine/terminal/terminal.h b/source/fumoengine/terminal/terminal.h index 8039337..c3af9a0 100644 --- a/source/fumoengine/terminal/terminal.h +++ b/source/fumoengine/terminal/terminal.h @@ -22,11 +22,13 @@ struct Terminal { usize wid; usize hgt; - struct Char4 *ch4s; + struct Char4 *buf; }; +usize TerminalMaxOut(struct Terminal *term); + bool CreateTerminal(struct Terminal *term, usize wid, usize hgt); void FreeTerminal(struct Terminal *term); -usize TerminalPrint(char *buf, usize n, struct Terminal *term); \ No newline at end of file +usize TerminalPrint(struct Terminal *term, char *out, usize n); \ No newline at end of file diff --git a/source/fumotris/fumotris.c b/source/fumotris/fumotris.c index 39dce5e..89c1898 100644 --- a/source/fumotris/fumotris.c +++ b/source/fumotris/fumotris.c @@ -1,114 +1,35 @@ -#include "fumocommon.h" -#include "fumoengine.h" +#include "fumotris.h" -enum ControlCode { - LEFT, - RIGHT, - SOFT_DROP, - HARD_DROP, - ROTATE_CCW, - ROTATE_CW, - ROTATE_180, - SWAP, - ESC, - VSCROLL, - HSCROLL, - MOUSE -}; - -#define CODE_COUNT 12 - -const struct ControlBind ctrl_binds[12] = { - { 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 } -}; - -void Loop(struct FumoGame *game) +void Update(struct FumoInstance *instance, void *args) { - while (true) { - game->time = TimeNow(); + struct Fumotris *game = args; - if (!InputAquire(&game->input_hand)) - Panic("Aquire failed"); - - ControllerPoll(&game->ctrl, &game->input_hand.recs); + TetrMapDraw(&game->board, &instance->term); +} - if (!InputRelease(&game->input_hand)) - Panic("Release failed"); +bool CreateFumotris(struct Fumotris *game) +{ + if (!CreateTetrMap(&game->board, 10, 10)) + return false; - - - EventInvoke(&game->update, 0); - - _sleep(100); - } + return true; } int main() { - struct FumoGame game; - FumoInit(&game); + struct FumoInstance instance; + CreateFumoInstance(&instance); - for (size_t i = 0; i < CODE_COUNT; i++) { - const struct ControlBind *bind = &ctrl_binds[i]; - ControllerMap(&game.ctrl, bind->code, bind->bind, bind->type); - } + struct Fumotris game; + CreateFumotris(&game); - Loop(&game); + ControllerMapMulti(&instance.ctrl, CODE_COUNT, MAPPINGS); + + FumoInstanceHook(&instance.on_update, Update, &game); + + + FumoInstanceRun(&instance); return 0; -} - - -/*const u8 I[16] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 1, 1, 1, 1, - 0, 0, 0, 0 -}; - -const u8 O[4] = { - 1, 1, - 1, 1 -}; - -const u8 T[9] = { - 0, 1, 0, - 1, 1, 1, - 0, 0, 0 -}; - -const u8 S[9] = { - 0, 1, 1, - 1, 1, 0, - 0, 0, 0 -}; - -const u8 Z[9] = { - 1, 1, 0, - 0, 1, 1, - 0, 0, 0 -}; - -const u8 J[9] = { - 1, 0, 0, - 1, 1, 1, - 0, 0, 0 -}; - -const u8 L[9] = { - 0, 0, 1, - 1, 1, 1, - 0, 0, 0 -};*/ \ No newline at end of file +} \ No newline at end of file diff --git a/source/fumotris/fumotris.h b/source/fumotris/fumotris.h new file mode 100644 index 0000000..efedc28 --- /dev/null +++ b/source/fumotris/fumotris.h @@ -0,0 +1,84 @@ +#include "fumocommon.h" +#include "fumoengine.h" +#include "tetr.h" + + +#define CODE_COUNT 12 + +enum ControlCode { + LEFT, + RIGHT, + SOFT_DROP, + HARD_DROP, + ROTATE_CCW, + ROTATE_CW, + ROTATE_180, + SWAP, + ESC, + VSCROLL, + HSCROLL, + MOUSE +}; + +struct ControlMapping MAPPINGS[12] = { + { 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 } +}; + + +struct Fumotris { + struct TetrMap board; + struct TetrMap piece; +}; + +const u8 I[16] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 1, 1, 1, 1, + 0, 0, 0, 0 +}; + +const u8 O[4] = { + 1, 1, + 1, 1 +}; + +const u8 T[9] = { + 0, 1, 0, + 1, 1, 1, + 0, 0, 0 +}; + +const u8 S[9] = { + 0, 1, 1, + 1, 1, 0, + 0, 0, 0 +}; + +const u8 Z[9] = { + 1, 1, 0, + 0, 1, 1, + 0, 0, 0 +}; + +const u8 J[9] = { + 1, 0, 0, + 1, 1, 1, + 0, 0, 0 +}; + +const u8 L[9] = { + 0, 0, 1, + 1, 1, 1, + 0, 0, 0 +}; \ No newline at end of file diff --git a/source/fumotris/tetr.c b/source/fumotris/tetr.c index 47e799d..e9c514f 100644 --- a/source/fumotris/tetr.c +++ b/source/fumotris/tetr.c @@ -1,62 +1,55 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "fumocommon.h" -#include "term.h" +#include "tetr.h" -struct TetrMap { - size_t wid; - size_t hgt; - size_t area; - - int x; - int y; - u8 rot; - - u8 *blks; -}; - -struct TetrMap NewTetrMap(u8 *blks, size_t wid, size_t hgt) +bool CreateTetrMap(struct TetrMap *map, usize wid, usize hgt) { - size_t area = wid * hgt; - memset(blks, 0, area); + u8 *blks = calloc(wid * hgt, sizeof(u8)); + + if (blks == nullptr) + return false; - return (struct TetrMap) { - wid, hgt, area, - 0, 0, 0, - blks + *map = (struct TetrMap) { + .wid = wid, + .hgt = hgt, + + .x = 0, + .y = 0, + + .rot = 0, + + .blks = blks }; + + return true; } -void TetrMapToTermBuf(struct TetrMap *map, struct Terminal *term) +void FreeTetrMap(struct TetrMap *map) +{ + free(map->blks); +} + +void TetrMapDraw(struct TetrMap *map, struct Terminal *term) { static const u8f blk_colors[8] = { 8, 14, 11, 13, 10, 9, 12, 3 }; - for (size_t y = 0; y < map->hgt; y++) { - for (size_t x = 0; x < map->wid; x++) { - size_t map_i = y * map->wid + x; - size_t buf_i = (y + map->y) * term->wid + (x + map->x) * 2; + for (usize y = 0; y < map->hgt; y++) { + for (usize x = 0; x < map->wid; x++) { + usize map_i = y * map->wid + x; + usize term_i = (y + map->y) * term->wid + (x + map->x) * 2; - struct Char4 *a = &term->ch4s[buf_i]; - struct Char4 *b = &term->ch4s[buf_i + 1]; + struct Char4 *block = term->buf + term_i; if (map->blks[map_i] == 0) { - a->ch = '('; - b->ch = ')'; + block[0].ch = '('; + block[1].ch = ')'; } else { - a->ch = '['; - b->ch = ']'; + block[0].ch = '['; + block[1].ch = ']'; } u8 fg = blk_colors[map->blks[map_i]]; - a->fg = fg; - b->fg = fg; + block[0].color.fg = fg; + block[1].color.fg = fg; } } } diff --git a/source/fumotris/tetr.h b/source/fumotris/tetr.h index 57b7eb4..419f990 100644 --- a/source/fumotris/tetr.h +++ b/source/fumotris/tetr.h @@ -1,29 +1,26 @@ -#include -#include -#include -#include #include #include -#include #include "fumocommon.h" -#include "term.h" +#include "terminal.h" struct TetrMap { - size_t wid; - size_t hgt; - size_t area; + usize wid; + usize hgt; int x; int y; - u8 rot; + + u8f rot; u8 *blks; }; -struct TetrMap NewTetrMap(u8 *blks, size_t wid, size_t hgt); +bool CreateTetrMap(struct TetrMap *map, usize wid, usize hgt); -void TetrMapToTermBuf(struct TetrMap *map, struct Terminal *term); +void FreeTetrMap(struct TetrMap *map); + +void TetrMapDraw(struct TetrMap *map, struct Terminal *term); bool TetrCollisionCheck(struct TetrMap *board, struct TetrMap *piece, int dx, int dy); \ No newline at end of file diff --git a/test.exe b/test.exe index cbb98a9c8cba4dbfd502dcb0e818c94ebef959d8..32de7d9626a68f1f3a390632cea2c89367f5e99c 100644 GIT binary patch delta 22758 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