From ccc24cda99a6972bdd92d2674da1273e499a3844 Mon Sep 17 00:00:00 2001 From: kdx Date: Sun, 26 Mar 2023 18:59:06 +0200 Subject: base done --- Makefile | 2 +- map/brulez.tmj | 65 +++++++++++++++++++++++++++--------- map/tmj2c.h | 32 ++++++++++++++++++ src/entity.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/entity.h | 25 ++++++++++++++ src/entityimpl.h | 30 +++++++++++++++++ src/entitytag.c | 4 +++ src/entitytag.h | 10 ++++++ src/game.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/game.h | 19 +++++++++++ src/main.c | 27 +++++++++++++-- src/map.c | 54 ++++++++++++++++++++++++++++++ src/map.h | 10 ++++++ src/player.c | 17 ++++++++++ 14 files changed, 470 insertions(+), 19 deletions(-) create mode 100644 map/tmj2c.h create mode 100644 src/entity.c create mode 100644 src/entity.h create mode 100644 src/entityimpl.h create mode 100644 src/entitytag.c create mode 100644 src/entitytag.h create mode 100644 src/game.c create mode 100644 src/game.h create mode 100644 src/map.c create mode 100644 src/map.h create mode 100644 src/player.c diff --git a/Makefile b/Makefile index 7d80fd7..74b3a51 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC := gcc LD := $(CC) WINCC := x86_64-w64-mingw32-gcc -CFLAGS := -Os -std=c2x -Wall -Wextra -Imap -DLZR_DISABLE_DEVMODE= +CFLAGS := -g -Os -std=c2x -Wall -Wextra -DLZR_DISABLE_DEVMODE= LDFLAGS := -lm -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_gfx NAME := jambase SRC := $(wildcard src/*.c) diff --git a/map/brulez.tmj b/map/brulez.tmj index 1497182..d5997f7 100644 --- a/map/brulez.tmj +++ b/map/brulez.tmj @@ -3,20 +3,20 @@ "infinite":false, "layers":[ { - "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 1, 1, 1, - 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 1, 1, - 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 0, 1, 1, - 1, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 5, 5, 0, 1, 1, - 1, 0, 0, 5, 5, 5, 0, 0, 0, 1, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 5, 0, 1, 1, - 1, 0, 4, 5, 0, 5, 5, 0, 0, 5, 5, 1, 5, 5, 1, 0, 0, 0, 0, 0, 0, 5, 5, 0, 1, - 1, 0, 5, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 5, 1, 0, 0, 0, 0, 0, 0, 5, 5, 1, - 1, 0, 5, 0, 1, 1, 0, 5, 5, 0, 5, 5, 0, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 5, 1, - 1, 5, 5, 0, 1, 0, 0, 5, 0, 1, 0, 5, 0, 1, 0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 1, - 1, 5, 0, 1, 1, 0, 5, 5, 0, 1, 0, 5, 0, 0, 5, 5, 0, 5, 5, 1, 0, 0, 2, 0, 1, - 1, 0, 1, 1, 0, 5, 5, 0, 1, 1, 0, 5, 5, 5, 5, 0, 1, 0, 5, 5, 0, 1, 0, 0, 1, - 1, 1, 1, 1, 0, 5, 0, 1, 1, 1, 1, 0, 5, 5, 0, 1, 1, 1, 0, 5, 5, 5, 5, 5, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "height":14, "id":1, "name":"Tile Layer 1", @@ -26,9 +26,42 @@ "width":25, "x":0, "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"Object Layer 1", + "objects":[ + { + "height":16, + "id":1, + "name":"player", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":144, + "y":96 + }, + { + "height":32, + "id":2, + "name":"player", + "rotation":0, + "type":"", + "visible":true, + "width":32, + "x":256, + "y":144 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 }], - "nextlayerid":2, - "nextobjectid":1, + "nextlayerid":3, + "nextobjectid":3, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.10.0", diff --git a/map/tmj2c.h b/map/tmj2c.h new file mode 100644 index 0000000..ff5baba --- /dev/null +++ b/map/tmj2c.h @@ -0,0 +1,32 @@ +#pragma once + +typedef struct { + const char *name; + double opacity; + unsigned int visible; + const unsigned int *data; +} Tmj2cLayer; + +typedef struct { + const char *name; + const char *type; + unsigned int id; + double x; + double y; + double width; + double height; + double rotation; + unsigned int visible; +} Tmj2cObject; + +typedef struct { + const char *path; + unsigned int width; + unsigned int height; + unsigned int tilewidth; + unsigned int tileheight; + unsigned int numlayers; + const Tmj2cLayer *layers; + unsigned int numobjects; + const Tmj2cObject *objects; +} Tmj2cMap; diff --git a/src/entity.c b/src/entity.c new file mode 100644 index 0000000..75ad7ec --- /dev/null +++ b/src/entity.c @@ -0,0 +1,100 @@ +#include "entity.h" +#include "entitytag.h" +#include "map.h" +#include "game.h" +#include +#include + +static void +_points(Entity *this, int ox, int oy, int *x0, int *x1, int *y0, int *y1) +{ + *x0 = this->pos[0] - this->width / 2 + ox; + *y0 = this->pos[1] - this->width / 2 + oy; + *x1 = *x0 + this->width - 1; + *y1 = *y0 + this->height - 1; +} + +unsigned int +entity_type(const char *typename) +{ + for (unsigned i = 0; i < num_entitytags; i++) + if (strcmp(typename, entitytags[i].name) == 0) + return i + 1; + printf("unknown type '%s'\n", typename); + return 0; +} + +bool +entity_collide(Entity *this, int ox, int oy) +{ + int x0, y0, x1, y1; + _points(this, ox, oy, &x0, &x1, &y0, &y1); + return (map_get_px(x0, y0) == 1 || map_get_px(x0, y1) == 1 || + map_get_px(x1, y0) == 1 || map_get_px(x1, y1) == 1); +} + +bool +entity_meet(Entity *this, Entity *other) +{ + int tx0, ty0, tx1, ty1; + int ox0, oy0, ox1, oy1; + _points(this, 0, 0, &tx0, &tx1, &ty0, &ty1); + _points(other, 0, 0, &ox0, &ox1, &oy0, &oy1); + return (tx0 < ox1 && tx1 > ox0 && ty0 < oy1 && ty1 > oy0); +} + +Entity * +entity_place_meeting(Entity *this, struct Game *g, unsigned int type) +{ + for (__auto_type i = 0; i < MAX_ENTITIES; i++) + if (this != &g->entities[i] && g->entities[i].type == type && + entity_meet(this, &g->entities[i])) + return &g->entities[i]; + return NULL; +} + +void +entity_move(Entity *this, [[maybe_unused]] struct Game *g) +{ + if (this->ignore_solids) { + for (int a = 0; a < 2; a++) { + const double sum = this->vel[a] + this->rem[a]; + int spd = (int)sum; + this->rem[a] = sum - spd; + this->pos[a] += spd; + } + return; + } + if (entity_collide(this, 0, 0)) { + this->vel[0] = 0.0; + this->vel[1] = 0.0; + this->rem[0] = 0.0; + this->rem[1] = 0.0; + return; + } + for (int a = 0; a < 2; a++) { + const double sum = this->vel[a] + this->rem[a]; + int spd = (int)sum; + this->rem[a] = sum - spd; + const int sign = (spd > 0) - (spd < 0); + if (sign == 0) + continue; + while (spd != 0) { + this->pos[a] += sign; + if (entity_collide(this, 0, 0)) { + this->pos[a] -= sign; + this->rem[a] = 0.0; + this->vel[a] = 0.0; + break; + } + spd -= sign; + } + } +} + +Entity * +entity_init(Entity *this, unsigned int type, int x, int y) +{ + entitytags[type - 1].init(this, x, y); + return this; +} diff --git a/src/entity.h b/src/entity.h new file mode 100644 index 0000000..01230d5 --- /dev/null +++ b/src/entity.h @@ -0,0 +1,25 @@ +#pragma once +#include + +struct Game; + +typedef struct Entity Entity; +struct Entity { + unsigned long uuid; + void (*update)(Entity *this, struct Game *g); + void (*draw)(Entity *this, struct Game *g); + unsigned int type; + int pos[2]; + double vel[2]; + double rem[2]; + int width; + int height; + bool ignore_solids; +}; + +unsigned int entity_type(const char *typename); +bool entity_collide(Entity *this, int ox, int oy); +bool entity_meet(Entity *this, Entity *other); +Entity *entity_place_meeting(Entity *this, struct Game *g, unsigned int type); +void entity_move(Entity *this, struct Game *g); +Entity *entity_init(Entity *this, unsigned int type, int x, int y); diff --git a/src/entityimpl.h b/src/entityimpl.h new file mode 100644 index 0000000..afc9dc2 --- /dev/null +++ b/src/entityimpl.h @@ -0,0 +1,30 @@ +#pragma once +#include "game.h" +#include "cfg.h" +#include "lzr.h" +#include "entitytag.h" +#include + +//[[maybe_unused]] static void *_draw; +//[[maybe_unused]] static void *_update; + +#define IMPL(X) static void X(Entity *this, Game *g); \ +static void X([[maybe_unused]] Entity *this, [[maybe_unused]] Game *g) +/*__attribute__((constructor)) static void init_##X() { _##X = X; } \ */ + +#define IMPL_INIT(X) static void init(Entity *this, int x, int y); \ +__attribute__((constructor)) static void init_tag() { \ + entitytags[num_entitytags].init = init; \ + entitytags[num_entitytags++].name = #X; \ +} \ +static void _init(Entity *this); \ +static void init(Entity *this, int x, int y) { \ + memset(this, 0, sizeof(*this)); \ + this->update = update; \ + this->draw = draw; \ + this->pos[0] = x; \ + this->pos[1] = y; \ + this->type = entity_type(#X); \ + _init(this); \ +} \ +static void _init([[maybe_unused]] Entity *this) diff --git a/src/entitytag.c b/src/entitytag.c new file mode 100644 index 0000000..de56455 --- /dev/null +++ b/src/entitytag.c @@ -0,0 +1,4 @@ +#include "entitytag.h" + +unsigned int num_entitytags = 0; +EntityTag entitytags[256] = {}; diff --git a/src/entitytag.h b/src/entitytag.h new file mode 100644 index 0000000..154023c --- /dev/null +++ b/src/entitytag.h @@ -0,0 +1,10 @@ +#pragma once +#include "entity.h" + +typedef struct { + const char *name; + void (*init)(Entity *this, int x, int y); +} EntityTag; + +extern unsigned int num_entitytags; +extern EntityTag entitytags[256]; diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..4cd9728 --- /dev/null +++ b/src/game.c @@ -0,0 +1,94 @@ +#include "game.h" +#include "cfg.h" +#include "entity.h" +#include "map.h" +#include +#include + +void +game_init(Game *this) +{ + memset(this, 0, sizeof(*this)); + game_restart_scene(this); +} + +void +game_update(Game *this) +{ + if (this->queue_next_scene) { + if (--this->queue_next_scene == 0) { + this->queue_restart_scene = 0; + map_next(); + game_restart_scene(this); + return; + } + } + if (this->queue_restart_scene) { + if (--this->queue_restart_scene == 0) + game_restart_scene(this); + } + for (__auto_type i = 0; i < MAX_ENTITIES; i++) { + const __auto_type e = &this->entities[i]; + if (e->type != 0 && e->update != NULL) + e->update(e, this); + } +} + +void +game_draw(Game *this) +{ + map_draw(); + for (int i = 0; i < MAX_ENTITIES; i++) { + const __auto_type e = &this->entities[i]; + if (e->type != 0 && e->draw != NULL) + e->draw(e, this); + } +} + +void +game_restart_scene(Game *this) +{ + memset(this->entities, 0, sizeof(this->entities)); + unsigned int size; + const __auto_type objects = map_objects(&size); + for (__auto_type i = 0u; i < size; i++) { + const __auto_type object = &objects[i]; + const __auto_type type = entity_type(object->name); + if (type == 0) + continue; + const __auto_type x = object->x + object->width / 2; + const __auto_type y = object->y + object->height / 2; + entity_init(game_create_entity(this), type, x, y); + } +} + +Entity * +game_create_entity(Game *this) +{ + __auto_type e = &this->entities[MAX_ENTITIES - 1]; + for (__auto_type i = 0; i < MAX_ENTITIES; i++) + if (this->entities[i].type == 0) { + e = &this->entities[i]; + break; + } + e->uuid = this->uuid++; + return e; +} + +int +game_entity_count(Game *this, unsigned int type) +{ + int count = 0; + for (__auto_type i = 0; i < MAX_ENTITIES; i++) + count += (this->entities[i].type == type); + return count; +} + +Entity * +game_get_entity(Game *this, unsigned int type) +{ + for (__auto_type i = 0; i < MAX_ENTITIES; i++) + if (this->entities[i].type == type) + return &this->entities[i]; + return NULL; +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..2d563f0 --- /dev/null +++ b/src/game.h @@ -0,0 +1,19 @@ +#pragma once +#include "entity.h" + +enum { MAX_ENTITIES = 128 }; + +typedef struct Game { + unsigned long uuid; + int queue_next_scene; + int queue_restart_scene; + Entity entities[MAX_ENTITIES]; +} Game; + +void game_init(Game *this); +void game_update(Game *this); +void game_draw(Game *this); +void game_restart_scene(Game *this); +Entity *game_create_entity(Game *this); +int game_entity_count(Game *this, unsigned int type); +Entity *game_get_entity(Game *this, unsigned int type); diff --git a/src/main.c b/src/main.c index a8db2b1..ff51bb3 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,13 @@ #include "lzr.h" #include "cfg.h" +#include "game.h" +#include #include +static Game *game = NULL; + +static void deinit(void); + int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) { @@ -10,18 +16,35 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) return 1; } - if (atexit(LZR_Quit)) { - LZR_Quit(); + if (atexit(deinit)) { + perror("main:atexit"); + deinit(); return 1; } + if ((game = malloc(sizeof(*game))) == NULL) { + perror("main:malloc"); + return 1; + } + game_init(game); + while (!LZR_ShouldQuit()) { LZR_CycleEvents(); + game_update(game); LZR_DrawBegin(); LZR_DrawSetColor(0, 0, 0, 0); LZR_DrawClear(); + game_draw(game); LZR_DrawEnd(); } return 0; } + +static void +deinit(void) +{ + if (game != NULL) + free(game); + LZR_Quit(); +} diff --git a/src/map.c b/src/map.c new file mode 100644 index 0000000..e3fc6d2 --- /dev/null +++ b/src/map.c @@ -0,0 +1,54 @@ +#include "map.h" +#include "cfg.h" +#include "../map/maps.h" +#include + +static unsigned int map_id = 0; + +void +map_next(void) +{ + if (tmj2c_maps[map_id + 1] != NULL) + map_id += 1; +} + +int +map_width(void) +{ + return tmj2c_maps[map_id]->width; +} + +int +map_height(void) +{ + return tmj2c_maps[map_id]->height; +} + +int +map_get(int x, int y) +{ + if (x < 0 || y < 0 || x >= map_width() || y >= map_height()) + return 1; + return tmj2c_maps[map_id]->layers[0].data[x + y * map_width()]; +} + +int +map_get_px(int x, int y) +{ + if (x < 0 || y < 0) + return 1; + return map_get(x / TSIZE, y / TSIZE); +} + +void +map_draw(void) +{ +} + +const Tmj2cObject * +map_objects(unsigned int *size) +{ + if (size != NULL) + *size = tmj2c_maps[map_id]->numobjects; + return tmj2c_maps[map_id]->objects; +} diff --git a/src/map.h b/src/map.h new file mode 100644 index 0000000..325b3bc --- /dev/null +++ b/src/map.h @@ -0,0 +1,10 @@ +#pragma once +#include "../map/tmj2c.h" + +void map_next(void); +int map_width(void); +int map_height(void); +int map_get(int x, int y); +int map_get_px(int x, int y); +void map_draw(void); +const Tmj2cObject *map_objects(unsigned int *size); diff --git a/src/player.c b/src/player.c new file mode 100644 index 0000000..b51572b --- /dev/null +++ b/src/player.c @@ -0,0 +1,17 @@ +#include "entityimpl.h" + +IMPL(draw) { + LZR_DrawSetColor(1, 1, 1, 1); + LZR_DrawRectangle(true, this->pos[0] - this->width / 2, + this->pos[1] - this->height / 2, + this->width, this->height); +} + +IMPL(update) { + this->vel[0] = LZR_BUTTON(RIGHT) - LZR_BUTTON(LEFT); + entity_move(this, g); +} + +IMPL_INIT(player) { + this->height = this->width = 12; +} -- cgit v1.2.3