Modularize rendering
parent
149848dbfa
commit
76d78db559
|
@ -1,4 +1,4 @@
|
||||||
plugins/apple/apple.so: plugins/apple/apple.c plugins/game/game.h
|
plugins/apple/apple.so: plugins/apple/apple.c plugins/game/game.h plugins/score/score.h
|
||||||
cc -g -shared -fpic -o plugins/apple/apple.so plugins/apple/apple.c
|
cc -g -shared -fpic -o plugins/apple/apple.so plugins/apple/apple.c
|
||||||
|
|
||||||
PLUGINS := ${PLUGINS} plugins/apple/apple.so
|
PLUGINS := ${PLUGINS} plugins/apple/apple.so
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../game/game.h"
|
#include "../game/game.h"
|
||||||
|
#include "../score/score.h"
|
||||||
|
|
||||||
static struct entity apple;
|
static struct entity apple;
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
game
|
game
|
||||||
|
score
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
plugins/fireball/fireball.so: plugins/fireball/fireball.c plugins/game/game.h
|
plugins/fireball/fireball.so: plugins/fireball/fireball.c plugins/game/game.h plugins/movement/movement.h
|
||||||
cc -g -shared -fpic -o plugins/fireball/fireball.so plugins/fireball/fireball.c
|
cc -g -shared -fpic -o plugins/fireball/fireball.so plugins/fireball/fireball.c
|
||||||
|
|
||||||
PLUGINS := ${PLUGINS} plugins/fireball/fireball.so
|
PLUGINS := ${PLUGINS} plugins/fireball/fireball.so
|
||||||
|
|
|
@ -8,15 +8,12 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
|
#define MAKEBUF(type) type *buf = malloc(sizeof(type)); *buf = arg;
|
||||||
|
|
||||||
bool running = true;
|
/* Shared variables */
|
||||||
double damage_overlay = 0.0;
|
|
||||||
|
|
||||||
int score = 0;
|
|
||||||
|
|
||||||
struct color black = {0, 0, 0};
|
struct color black = {0, 0, 0};
|
||||||
|
|
||||||
|
@ -32,16 +29,20 @@ struct list *entities = & (struct list) {
|
||||||
.next = NULL,
|
.next = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Private variables */
|
||||||
|
|
||||||
struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT] = {{NULL}};
|
struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT] = {{NULL}};
|
||||||
|
|
||||||
struct list *air_functions = NULL;
|
static bool running = true;
|
||||||
|
static double damage_overlay = 0.0;
|
||||||
|
static struct color damage_overlay_color;
|
||||||
|
|
||||||
struct input_handler *input_handlers[256] = {NULL};
|
static struct list *air_functions = NULL;
|
||||||
|
static struct input_handler *input_handlers[256] = {NULL};
|
||||||
|
static struct entity *render_entities[LIGHT * 2 + 1][LIGHT * 2 + 1];
|
||||||
|
static struct list *render_components = NULL;
|
||||||
|
|
||||||
void quit()
|
/* Helper functions */
|
||||||
{
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct color get_color(const char *str)
|
struct color get_color(const char *str)
|
||||||
{
|
{
|
||||||
|
@ -50,44 +51,104 @@ struct color get_color(const char *str)
|
||||||
return (struct color) {r, g, b};
|
return (struct color) {r, g, b};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_outside(int x, int y)
|
void set_color(struct color color, bool bg)
|
||||||
{
|
{
|
||||||
return x >= MAP_WIDTH || x < 0 || y >= MAP_HEIGHT || y < 0;
|
printf("\e[%u;2;%u;%u;%um", bg ? 48 : 38, color.r, color.g, color.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void light_color(struct color *color, double light)
|
||||||
|
{
|
||||||
|
color->r *= light;
|
||||||
|
color->g *= light;
|
||||||
|
color->b *= light;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mix_color(struct color *color, struct color other, double ratio)
|
||||||
|
{
|
||||||
|
double ratio_total = ratio + 1;
|
||||||
|
|
||||||
|
color->r = (color->r + other.r * ratio) / ratio_total;
|
||||||
|
color->g = (color->g + other.g * ratio) / ratio_total;
|
||||||
|
color->b = (color->b + other.b * ratio) / ratio_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_to_xy(enum direction dir, int *x, int *y)
|
||||||
|
{
|
||||||
|
switch (dir) {
|
||||||
|
case UP:
|
||||||
|
(*y)--;
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
(*x)--;
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
(*y)++;
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
(*x)++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct list *add_element(struct list *list, void *element)
|
||||||
|
{
|
||||||
|
struct list **ptr;
|
||||||
|
|
||||||
|
for (ptr = &list; *ptr != NULL; ptr = &(*ptr)->next)
|
||||||
|
;
|
||||||
|
|
||||||
|
*ptr = malloc(sizeof(struct list));
|
||||||
|
(*ptr)->element = element;
|
||||||
|
(*ptr)->next = NULL;
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clamp(int v, int min, int max)
|
||||||
|
{
|
||||||
|
return v < min ? min : v > max ? max : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max(int a, int b)
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int min(int a, int b)
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game-related utility functions */
|
||||||
|
|
||||||
|
void quit()
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool player_dead()
|
||||||
|
{
|
||||||
|
return player.health <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Map functions */
|
||||||
|
|
||||||
struct node get_node(int x, int y)
|
struct node get_node(int x, int y)
|
||||||
{
|
{
|
||||||
return is_outside(x, y) ? (struct node) {&outside} : map[x][y];
|
return is_outside(x, y) ? (struct node) {&outside} : map[x][y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_outside(int x, int y)
|
||||||
|
{
|
||||||
|
return x >= MAP_WIDTH || x < 0 || y >= MAP_HEIGHT || y < 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_solid(int x, int y)
|
bool is_solid(int x, int y)
|
||||||
{
|
{
|
||||||
return get_node(x, y).material->solid;
|
return get_node(x, y).material->solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool move(struct entity *entity, int xoff, int yoff)
|
/* Entity functions */
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
|
|
||||||
x = entity->x + xoff;
|
|
||||||
y = entity->y + yoff;
|
|
||||||
|
|
||||||
if (is_solid(x, y)) {
|
|
||||||
if (entity->on_collide)
|
|
||||||
entity->on_collide(entity, x, y);
|
|
||||||
return false;
|
|
||||||
} else if (entity->collide_with_entities && entity_collision_map[x][y]) {
|
|
||||||
if (entity->on_collide_with_entity)
|
|
||||||
entity->on_collide_with_entity(entity, entity_collision_map[x][y]);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
entity_collision_map[entity->x][entity->y] = NULL;
|
|
||||||
entity->x = x;
|
|
||||||
entity->y = y;
|
|
||||||
entity_collision_map[entity->x][entity->y] = entity;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool spawn(struct entity def, int x, int y, void *data)
|
bool spawn(struct entity def, int x, int y, void *data)
|
||||||
{
|
{
|
||||||
|
@ -114,6 +175,30 @@ bool spawn(struct entity def, int x, int y, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool move(struct entity *entity, int xoff, int yoff)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
x = entity->x + xoff;
|
||||||
|
y = entity->y + yoff;
|
||||||
|
|
||||||
|
if (is_solid(x, y)) {
|
||||||
|
if (entity->on_collide)
|
||||||
|
entity->on_collide(entity, x, y);
|
||||||
|
return false;
|
||||||
|
} else if (entity->collide_with_entities && entity_collision_map[x][y]) {
|
||||||
|
if (entity->on_collide_with_entity)
|
||||||
|
entity->on_collide_with_entity(entity, entity_collision_map[x][y]);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
entity_collision_map[entity->x][entity->y] = NULL;
|
||||||
|
entity->x = x;
|
||||||
|
entity->y = y;
|
||||||
|
entity_collision_map[entity->x][entity->y] = entity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void add_health(struct entity *entity, int health)
|
void add_health(struct entity *entity, int health)
|
||||||
{
|
{
|
||||||
bool was_alive = entity->health > 0;
|
bool was_alive = entity->health > 0;
|
||||||
|
@ -129,71 +214,27 @@ void add_health(struct entity *entity, int health)
|
||||||
entity->on_death(entity);
|
entity->on_death(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_score(int s)
|
/* Register callback functions */
|
||||||
|
|
||||||
|
void register_air_function(struct generator_function arg)
|
||||||
{
|
{
|
||||||
score += s;
|
MAKEBUF(struct generator_function);
|
||||||
}
|
|
||||||
|
|
||||||
bool player_dead()
|
|
||||||
{
|
|
||||||
return player.health <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct list *add_element(struct list *list, void *element)
|
|
||||||
{
|
|
||||||
struct list **ptr;
|
|
||||||
|
|
||||||
for (ptr = &list; *ptr != NULL; ptr = &(*ptr)->next)
|
|
||||||
;
|
|
||||||
|
|
||||||
*ptr = malloc(sizeof(struct list));
|
|
||||||
(*ptr)->element = element;
|
|
||||||
(*ptr)->next = NULL;
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_air_function(struct generator_function func)
|
|
||||||
{
|
|
||||||
struct generator_function *buf = malloc(sizeof(struct generator_function));
|
|
||||||
*buf = func;
|
|
||||||
|
|
||||||
air_functions = add_element(air_functions, buf);
|
air_functions = add_element(air_functions, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_input_handler(unsigned char c, struct input_handler handler)
|
void register_input_handler(unsigned char c, struct input_handler arg)
|
||||||
{
|
{
|
||||||
if (input_handlers[c])
|
if (input_handlers[c])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct input_handler *buf = malloc(sizeof(struct input_handler));
|
MAKEBUF(struct input_handler);
|
||||||
*buf = handler;
|
|
||||||
|
|
||||||
input_handlers[c] = buf;
|
input_handlers[c] = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dir_to_xy(enum direction dir, int *x, int *y)
|
void register_render_component(void (*arg)(struct winsize ws))
|
||||||
{
|
{
|
||||||
switch (dir) {
|
render_components = add_element(render_components, arg);
|
||||||
case UP:
|
};
|
||||||
(*y)--;
|
|
||||||
break;
|
|
||||||
case LEFT:
|
|
||||||
(*x)--;
|
|
||||||
break;
|
|
||||||
case DOWN:
|
|
||||||
(*y)++;
|
|
||||||
break;
|
|
||||||
case RIGHT:
|
|
||||||
(*x)++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int clamp(int v, int min, int max)
|
|
||||||
{
|
|
||||||
return v < min ? min : v > max ? max : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Player */
|
/* Player */
|
||||||
|
|
||||||
|
@ -273,27 +314,6 @@ static void generate_corridor_random(int x, int y)
|
||||||
|
|
||||||
/* Rendering */
|
/* Rendering */
|
||||||
|
|
||||||
void set_color(struct color color, bool bg)
|
|
||||||
{
|
|
||||||
printf("\e[%u;2;%u;%u;%um", bg ? 48 : 38, color.r, color.g, color.b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void light_color(struct color *color, double light)
|
|
||||||
{
|
|
||||||
color->r *= light;
|
|
||||||
color->g *= light;
|
|
||||||
color->b *= light;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mix_color(struct color *color, struct color other, double ratio)
|
|
||||||
{
|
|
||||||
double ratio_total = ratio + 1;
|
|
||||||
|
|
||||||
color->r = (color->r + other.r * ratio) / ratio_total;
|
|
||||||
color->g = (color->g + other.g * ratio) / ratio_total;
|
|
||||||
color->b = (color->b + other.b * ratio) / ratio_total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool render_color(struct color color, double light, bool bg)
|
static bool render_color(struct color color, double light, bool bg)
|
||||||
{
|
{
|
||||||
if (light <= 0.0) {
|
if (light <= 0.0) {
|
||||||
|
@ -301,7 +321,7 @@ static bool render_color(struct color color, double light, bool bg)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (damage_overlay > 0.0)
|
if (damage_overlay > 0.0)
|
||||||
mix_color(&color, get_color("#F20000"), damage_overlay * 2.0);
|
mix_color(&color, damage_overlay_color, damage_overlay * 2.0);
|
||||||
|
|
||||||
light_color(&color, light);
|
light_color(&color, light);
|
||||||
|
|
||||||
|
@ -310,21 +330,14 @@ static bool render_color(struct color color, double light, bool bg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render(render_entity_list entity_list)
|
static void render_map(struct winsize ws)
|
||||||
{
|
{
|
||||||
printf("\e[2J\e[0;0H");
|
|
||||||
|
|
||||||
struct winsize ws;
|
|
||||||
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
|
|
||||||
|
|
||||||
int cols = ws.ws_col / 2 - LIGHT * 2;
|
int cols = ws.ws_col / 2 - LIGHT * 2;
|
||||||
int rows = ws.ws_row / 2 - LIGHT;
|
int rows = ws.ws_row / 2 - LIGHT;
|
||||||
|
|
||||||
int cols_left = ws.ws_col - cols - (LIGHT * 2 + 1) * 2;
|
int cols_left = ws.ws_col - cols - (LIGHT * 2 + 1) * 2;
|
||||||
int rows_left = ws.ws_row - rows - (LIGHT * 2 + 1);
|
int rows_left = ws.ws_row - rows - (LIGHT * 2 + 1);
|
||||||
|
|
||||||
set_color(black, true);
|
|
||||||
|
|
||||||
for (int i = 0; i < rows; i++)
|
for (int i = 0; i < rows; i++)
|
||||||
for (int i = 0; i < ws.ws_col; i++)
|
for (int i = 0; i < ws.ws_col; i++)
|
||||||
printf(" ");
|
printf(" ");
|
||||||
|
@ -346,7 +359,8 @@ static void render(render_entity_list entity_list)
|
||||||
|
|
||||||
render_color(node.material->color, light, true);
|
render_color(node.material->color, light, true);
|
||||||
|
|
||||||
struct entity *entity = entity_list[x + LIGHT][y + LIGHT];
|
struct entity *entity = render_entities[x + LIGHT][y + LIGHT];
|
||||||
|
render_entities[x + LIGHT][y + LIGHT] = NULL;
|
||||||
|
|
||||||
if (entity && render_color(entity->color, light, false))
|
if (entity && render_color(entity->color, light, false))
|
||||||
printf("%s", entity->texture);
|
printf("%s", entity->texture);
|
||||||
|
@ -363,42 +377,34 @@ static void render(render_entity_list entity_list)
|
||||||
for (int i = 0; i < rows_left + 1; i++)
|
for (int i = 0; i < rows_left + 1; i++)
|
||||||
for (int i = 0; i < ws.ws_col; i++)
|
for (int i = 0; i < ws.ws_col; i++)
|
||||||
printf(" ");
|
printf(" ");
|
||||||
|
}
|
||||||
|
|
||||||
printf("\e[0;0H\e[39m");
|
static void render()
|
||||||
|
{
|
||||||
|
printf("\e[2J");
|
||||||
|
|
||||||
printf("\e[32m\e[3mScore:\e[23m %d\e[39m", score);
|
struct winsize ws;
|
||||||
|
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
|
||||||
|
|
||||||
printf("\e[0;0");
|
for (struct list *ptr = render_components; ptr != NULL; ptr = ptr->next) {
|
||||||
|
printf("\e[0m\e[0;0H");
|
||||||
|
set_color(black, true);
|
||||||
|
|
||||||
for (int i = 0; i < rows; i++)
|
((void (*)(struct winsize ws)) ptr->element)(ws);
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
printf("\t\e[1mInventory\e[22m\n\n");
|
|
||||||
printf("\t0x\t\e[3mNothing\e[23m\n");
|
|
||||||
|
|
||||||
printf("\e[0;0H");
|
|
||||||
|
|
||||||
for (int i = 0; i < ws.ws_row - 2; i++)
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
int hearts_cols = ws.ws_col / 2 - player.max_health;
|
|
||||||
|
|
||||||
for (int i = 0; i < hearts_cols; i++)
|
|
||||||
printf(" ");
|
|
||||||
|
|
||||||
set_color((struct color) {255, 0, 0}, false);
|
|
||||||
|
|
||||||
for (int i = 0; i < player.max_health; i++) {
|
|
||||||
if (i >= player.health)
|
|
||||||
set_color(get_color("#5A5A5A"), false);
|
|
||||||
printf("\u2665 ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\e[39m\n");
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input */
|
/* Input */
|
||||||
|
|
||||||
|
static void handle_interrupt(int signal)
|
||||||
|
{
|
||||||
|
(void) signal;
|
||||||
|
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_input(unsigned char c)
|
static void handle_input(unsigned char c)
|
||||||
{
|
{
|
||||||
struct input_handler *handler = input_handlers[c];
|
struct input_handler *handler = input_handlers[c];
|
||||||
|
@ -407,15 +413,6 @@ static void handle_input(unsigned char c)
|
||||||
handler->callback();
|
handler->callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Multithreading */
|
|
||||||
|
|
||||||
static void handle_interrupt(int signal)
|
|
||||||
{
|
|
||||||
(void) signal;
|
|
||||||
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *input_thread(void *unused)
|
static void *input_thread(void *unused)
|
||||||
{
|
{
|
||||||
(void) unused;
|
(void) unused;
|
||||||
|
@ -428,6 +425,103 @@ static void *input_thread(void *unused)
|
||||||
|
|
||||||
/* Main Game */
|
/* Main Game */
|
||||||
|
|
||||||
|
void game()
|
||||||
|
{
|
||||||
|
srand(time(0));
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = &handle_interrupt;
|
||||||
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
|
||||||
|
generate_corridor_random(player.x, player.y);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; i++)
|
||||||
|
generate_corridor_random(rand() % MAP_WIDTH, rand() % MAP_HEIGHT);
|
||||||
|
|
||||||
|
struct termios oldtio, newtio;
|
||||||
|
tcgetattr(STDIN_FILENO, &oldtio);
|
||||||
|
newtio = oldtio;
|
||||||
|
newtio.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
|
||||||
|
|
||||||
|
printf("\e[?1049h\e[?25l");
|
||||||
|
|
||||||
|
pthread_t input_thread_id;
|
||||||
|
pthread_create(&input_thread_id, NULL, &input_thread, NULL);
|
||||||
|
|
||||||
|
struct timespec ts, ts_old;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts_old);
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
double dtime = (double) (ts.tv_sec - ts_old.tv_sec) + (double) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
|
||||||
|
ts_old = ts;
|
||||||
|
|
||||||
|
bool dead = player_dead();
|
||||||
|
|
||||||
|
if (! dead && damage_overlay > 0.0) {
|
||||||
|
damage_overlay -= dtime;
|
||||||
|
|
||||||
|
if (damage_overlay < 0.0)
|
||||||
|
damage_overlay = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (struct list **ptr = &entities; *ptr != NULL; ) {
|
||||||
|
struct entity *entity = (*ptr)->element;
|
||||||
|
|
||||||
|
if (entity->remove) {
|
||||||
|
assert(entity != &player);
|
||||||
|
struct list *next = (*ptr)->next;
|
||||||
|
|
||||||
|
if (entity->on_remove)
|
||||||
|
entity->on_remove(entity);
|
||||||
|
|
||||||
|
if (entity->meta)
|
||||||
|
free(entity->meta);
|
||||||
|
|
||||||
|
if (entity->collide_with_entities)
|
||||||
|
entity_collision_map[entity->x][entity->y] = NULL;
|
||||||
|
|
||||||
|
free(entity);
|
||||||
|
free(*ptr);
|
||||||
|
|
||||||
|
*ptr = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dx, dy;
|
||||||
|
|
||||||
|
dx = entity->x - player.x;
|
||||||
|
dy = entity->y - player.y;
|
||||||
|
|
||||||
|
bool visible = abs(dx) <= LIGHT && abs(dy) <= LIGHT;
|
||||||
|
|
||||||
|
if (visible)
|
||||||
|
render_entities[dx + LIGHT][dy + LIGHT] = entity;
|
||||||
|
|
||||||
|
if (! dead && entity->on_step)
|
||||||
|
entity->on_step(entity, (struct entity_step_data) {
|
||||||
|
.dtime = dtime,
|
||||||
|
.visible = visible,
|
||||||
|
.dx = dx,
|
||||||
|
.dy = dy,
|
||||||
|
});
|
||||||
|
|
||||||
|
ptr = &(*ptr)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
|
||||||
|
// there is no such thing as glfwSwapBuffers, so we just wait 1 / 60 seconds to prevent artifacts
|
||||||
|
usleep(1000000 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\e[?1049l\e[?25h");
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializer function */
|
||||||
|
|
||||||
__attribute__ ((constructor)) static void init()
|
__attribute__ ((constructor)) static void init()
|
||||||
{
|
{
|
||||||
wall = (struct material) {
|
wall = (struct material) {
|
||||||
|
@ -476,103 +570,10 @@ __attribute__ ((constructor)) static void init()
|
||||||
.run_if_dead = true,
|
.run_if_dead = true,
|
||||||
.callback = &quit,
|
.callback = &quit,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
void game()
|
register_render_component(&render_map);
|
||||||
{
|
|
||||||
srand(time(0));
|
|
||||||
|
|
||||||
struct sigaction sa;
|
damage_overlay_color = get_color("#F20000");
|
||||||
sa.sa_handler = &handle_interrupt;
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
|
||||||
|
|
||||||
generate_corridor_random(player.x, player.y);
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++)
|
|
||||||
generate_corridor_random(rand() % MAP_WIDTH, rand() % MAP_HEIGHT);
|
|
||||||
|
|
||||||
struct termios oldtio, newtio;
|
|
||||||
tcgetattr(STDIN_FILENO, &oldtio);
|
|
||||||
newtio = oldtio;
|
|
||||||
newtio.c_lflag &= ~(ICANON | ECHO);
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
|
|
||||||
|
|
||||||
printf("\e[?1049h\e[?25l");
|
|
||||||
|
|
||||||
pthread_t input_thread_id;
|
|
||||||
pthread_create(&input_thread_id, NULL, &input_thread, NULL);
|
|
||||||
|
|
||||||
struct timespec ts, ts_old;
|
|
||||||
clock_gettime(CLOCK_REALTIME, &ts_old);
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
clock_gettime(CLOCK_REALTIME, &ts);
|
|
||||||
double dtime = (double) (ts.tv_sec - ts_old.tv_sec) + (double) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
|
|
||||||
ts_old = ts;
|
|
||||||
|
|
||||||
bool dead = player_dead();
|
|
||||||
|
|
||||||
if (! dead && damage_overlay > 0.0) {
|
|
||||||
damage_overlay -= dtime;
|
|
||||||
|
|
||||||
if (damage_overlay < 0.0)
|
|
||||||
damage_overlay = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
render_entity_list render_list = {{NULL}};
|
|
||||||
|
|
||||||
for (struct list **ptr = &entities; *ptr != NULL; ) {
|
|
||||||
struct entity *entity = (*ptr)->element;
|
|
||||||
|
|
||||||
if (entity->remove) {
|
|
||||||
assert(entity != &player);
|
|
||||||
struct list *next = (*ptr)->next;
|
|
||||||
|
|
||||||
if (entity->on_remove)
|
|
||||||
entity->on_remove(entity);
|
|
||||||
|
|
||||||
if (entity->meta)
|
|
||||||
free(entity->meta);
|
|
||||||
|
|
||||||
if (entity->collide_with_entities)
|
|
||||||
entity_collision_map[entity->x][entity->y] = NULL;
|
|
||||||
|
|
||||||
free(entity);
|
|
||||||
free(*ptr);
|
|
||||||
|
|
||||||
*ptr = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dx, dy;
|
|
||||||
|
|
||||||
dx = entity->x - player.x;
|
|
||||||
dy = entity->y - player.y;
|
|
||||||
|
|
||||||
bool visible = abs(dx) <= LIGHT && abs(dy) <= LIGHT;
|
|
||||||
|
|
||||||
if (visible)
|
|
||||||
render_list[dx + LIGHT][dy + LIGHT] = entity;
|
|
||||||
|
|
||||||
if (! dead && entity->on_step)
|
|
||||||
entity->on_step(entity, (struct entity_step_data) {
|
|
||||||
.dtime = dtime,
|
|
||||||
.visible = visible,
|
|
||||||
.dx = dx,
|
|
||||||
.dy = dy,
|
|
||||||
});
|
|
||||||
|
|
||||||
ptr = &(*ptr)->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(render_list);
|
|
||||||
|
|
||||||
// there is no such thing as glfwSwapBuffers, so we just wait 1 / 60 seconds to prevent artifacts
|
|
||||||
usleep(1000000 / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\e[?1049l\e[?25h");
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use later */
|
/* Use later */
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define _GAME_H_
|
#define _GAME_H_
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#define MAP_HEIGHT 1000
|
#define MAP_HEIGHT 1000
|
||||||
#define MAP_WIDTH 1000
|
#define MAP_WIDTH 1000
|
||||||
#define LIGHT 10
|
#define LIGHT 10
|
||||||
|
@ -59,8 +60,6 @@ struct list
|
||||||
struct list *next;
|
struct list *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct entity *render_entity_list[LIGHT * 2 + 1][LIGHT * 2 + 1];
|
|
||||||
|
|
||||||
struct generator_function
|
struct generator_function
|
||||||
{
|
{
|
||||||
int chance;
|
int chance;
|
||||||
|
@ -81,8 +80,6 @@ enum direction
|
||||||
RIGHT,
|
RIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int score;
|
|
||||||
|
|
||||||
extern struct color black;
|
extern struct color black;
|
||||||
|
|
||||||
extern struct material wall;
|
extern struct material wall;
|
||||||
|
@ -96,27 +93,29 @@ extern struct list *entities;
|
||||||
|
|
||||||
extern struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT];
|
extern struct entity *entity_collision_map[MAP_WIDTH][MAP_HEIGHT];
|
||||||
|
|
||||||
extern struct list *air_functions;
|
|
||||||
|
|
||||||
extern struct input_handler *input_handlers[256];
|
|
||||||
|
|
||||||
void quit();
|
|
||||||
struct color get_color(const char *str);
|
struct color get_color(const char *str);
|
||||||
bool is_outside(int x, int y);
|
|
||||||
struct node get_node(int x, int y);
|
|
||||||
bool is_solid(int x, int y);
|
|
||||||
bool move(struct entity *entity, int xoff, int yoff);
|
|
||||||
bool spawn(struct entity def, int x, int y, void *data);
|
|
||||||
void add_health(struct entity *entity, int health);
|
|
||||||
void add_score(int s);
|
|
||||||
bool player_dead();
|
|
||||||
void set_color(struct color color, bool bg);
|
void set_color(struct color color, bool bg);
|
||||||
void light_color(struct color *color, double light);
|
void light_color(struct color *color, double light);
|
||||||
void mix_color(struct color *color, struct color other, double ratio);
|
void mix_color(struct color *color, struct color other, double ratio);
|
||||||
void register_air_function(struct generator_function func);
|
|
||||||
void register_input_handler(unsigned char c, struct input_handler handler);
|
|
||||||
void dir_to_xy(enum direction dir, int *x, int *y);
|
void dir_to_xy(enum direction dir, int *x, int *y);
|
||||||
int clamp(int v, int max, int min);
|
|
||||||
struct list *add_element(struct list *list, void *element);
|
struct list *add_element(struct list *list, void *element);
|
||||||
|
int clamp(int v, int max, int min);
|
||||||
|
int max(int a, int b);
|
||||||
|
int min(int a, int b);
|
||||||
|
|
||||||
|
void quit();
|
||||||
|
bool player_dead();
|
||||||
|
|
||||||
|
struct node get_node(int x, int y);
|
||||||
|
bool is_outside(int x, int y);
|
||||||
|
bool is_solid(int x, int y);
|
||||||
|
|
||||||
|
bool spawn(struct entity def, int x, int y, void *data);
|
||||||
|
bool move(struct entity *entity, int xoff, int yoff);
|
||||||
|
void add_health(struct entity *entity, int health);
|
||||||
|
|
||||||
|
void register_air_function(struct generator_function arg);
|
||||||
|
void register_input_handler(unsigned char c, struct input_handler arg);
|
||||||
|
void register_render_component(void (*arg)(struct winsize ws));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
plugins/healthbar/healthbar.so: plugins/healthbar/healthbar.c plugins/game/game.h
|
||||||
|
cc -g -shared -fpic -o plugins/healthbar/healthbar.so plugins/healthbar/healthbar.c
|
||||||
|
|
||||||
|
PLUGINS := ${PLUGINS} plugins/healthbar/healthbar.so
|
|
@ -0,0 +1 @@
|
||||||
|
game
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "../game/game.h"
|
||||||
|
|
||||||
|
static struct color red = {255, 0, 0};
|
||||||
|
static struct color gray;
|
||||||
|
|
||||||
|
static void render_healthbar(struct winsize ws)
|
||||||
|
{
|
||||||
|
int y = max(ws.ws_row - 2, 0);
|
||||||
|
int x = max(ws.ws_col / 2 - player.max_health, 0);
|
||||||
|
|
||||||
|
printf("\e[%u;%uH", y, x);
|
||||||
|
|
||||||
|
set_color(red, false);
|
||||||
|
|
||||||
|
int health = max(player.health, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < player.max_health; i++) {
|
||||||
|
if (i == health)
|
||||||
|
set_color(gray, false);
|
||||||
|
printf("♥ ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((constructor)) static void init()
|
||||||
|
{
|
||||||
|
gray = get_color("#5A5A5A");
|
||||||
|
|
||||||
|
register_render_component(&render_healthbar);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
plugins/monster/monster.so: plugins/monster/monster.c plugins/game/game.h
|
plugins/monster/monster.so: plugins/monster/monster.c plugins/game/game.h plugins/score/score.h
|
||||||
cc -g -shared -fpic -o plugins/monster/monster.so plugins/monster/monster.c
|
cc -g -shared -fpic -o plugins/monster/monster.so plugins/monster/monster.c
|
||||||
|
|
||||||
PLUGINS := ${PLUGINS} plugins/monster/monster.so
|
PLUGINS := ${PLUGINS} plugins/monster/monster.so
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
game
|
game
|
||||||
|
score
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "../game/game.h"
|
#include "../game/game.h"
|
||||||
|
#include "../score/score.h"
|
||||||
|
|
||||||
static struct entity monster;
|
static struct entity monster;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
plugins/movement/movement.so: plugins/movement/movement.c plugins/game/game.h
|
plugins/movement/movement.so: plugins/movement/movement.c plugins/movement/movement.h plugins/game/game.h
|
||||||
cc -g -shared -fpic -o plugins/movement/movement.so plugins/movement/movement.c
|
cc -g -shared -fpic -o plugins/movement/movement.so plugins/movement/movement.c
|
||||||
|
|
||||||
PLUGINS := ${PLUGINS} plugins/movement/movement.so
|
PLUGINS := ${PLUGINS} plugins/movement/movement.so
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
plugins/score/score.so: plugins/score/score.c plugins/score/score.h plugins/game/game.h
|
||||||
|
cc -g -shared -fpic -o plugins/score/score.so plugins/score/score.c
|
||||||
|
|
||||||
|
PLUGINS := ${PLUGINS} plugins/score/score.so
|
|
@ -0,0 +1 @@
|
||||||
|
game
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "../game/game.h"
|
||||||
|
|
||||||
|
static int score = 0;
|
||||||
|
|
||||||
|
void add_score(int s)
|
||||||
|
{
|
||||||
|
score += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_score()
|
||||||
|
{
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_score(struct winsize ws)
|
||||||
|
{
|
||||||
|
(void) ws;
|
||||||
|
|
||||||
|
printf("\e[32m\e[3mScore:\e[23m %d", score);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((constructor)) static void init()
|
||||||
|
{
|
||||||
|
register_render_component(&render_score);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _SCORE_H_
|
||||||
|
#define _SCORE_H_
|
||||||
|
|
||||||
|
void add_score(int s);
|
||||||
|
int get_score();
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue