game: map units combat and SDL2 render slice

This commit is contained in:
Hernâni Marques 2026-07-11 23:31:09 +02:00
parent 87d99f7007
commit 4dedf5943b
5 changed files with 228 additions and 0 deletions

57
src/game.c Normal file
View file

@ -0,0 +1,57 @@
#include "ok_game.h"
#include "ok_map.h"
#include "ok_unit.h"
#include <string.h>
void ok_game_spawn(OkGame *g, OkFaction f, OkUnitKind k, int x, int y) {
if (g->unit_count >= OK_MAX_UNITS) return;
if (!ok_map_walkable(&g->map, x, y)) return;
OkUnit *u = &g->units[g->unit_count++];
memset(u, 0, sizeof(*u));
u->x = x;
u->y = y;
u->faction = f;
u->kind = k;
u->hp_max = (k == OK_UNIT_VEHICLE) ? 80 : 30;
u->hp = u->hp_max;
u->target = -1;
u->alive = true;
}
void ok_game_init(OkGame *g) {
memset(g, 0, sizeof(*g));
ok_map_generate(&g->map);
g->selected = -1;
g->running = true;
/* two camps */
ok_game_spawn(g, OK_FACTION_SURVIVORS, OK_UNIT_INFANTRY, 6, 6);
ok_game_spawn(g, OK_FACTION_SURVIVORS, OK_UNIT_VEHICLE, 8, 6);
ok_game_spawn(g, OK_FACTION_SCAVENGERS, OK_UNIT_INFANTRY, OK_MAP_W - 8, OK_MAP_H - 8);
ok_game_spawn(g, OK_FACTION_SCAVENGERS, OK_UNIT_VEHICLE, OK_MAP_W - 10, OK_MAP_H - 8);
/* auto-aggro first pair */
if (g->unit_count >= 4) {
g->units[0].target = 2;
g->units[1].target = 3;
g->units[2].target = 0;
g->units[3].target = 1;
}
}
void ok_game_update(OkGame *g, float dt) {
if (!g->running) return;
g->tick++;
ok_units_update(g, dt);
int s_alive = 0, c_alive = 0;
for (int i = 0; i < g->unit_count; i++) {
if (!g->units[i].alive) continue;
if (g->units[i].faction == OK_FACTION_SURVIVORS) s_alive++;
else c_alive++;
}
if (s_alive == 0) {
g->scavengers_win = true;
g->running = false;
} else if (c_alive == 0) {
g->survivors_win = true;
g->running = false;
}
}

44
src/main.c Normal file
View file

@ -0,0 +1,44 @@
#include "ok_game.h"
#include "ok_render.h"
#include <stdio.h>
#include <SDL.h>
#ifndef OPENKKND_VERSION
#define OPENKKND_VERSION "dev"
#endif
int main(int argc, char **argv) {
(void)argc;
(void)argv;
printf("OpenKKND %s — original genre RTS (MIT). No commercial IP assets.\n", OPENKKND_VERSION);
const int tw = 12;
if (!ok_render_init(OK_MAP_W * tw, OK_MAP_H * tw)) {
fprintf(stderr, "render init failed\n");
return 1;
}
OkGame game;
ok_game_init(&game);
Uint64 prev = SDL_GetPerformanceCounter();
const Uint64 freq = SDL_GetPerformanceFrequency();
while (game.running || true) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) goto done;
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) goto done;
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_r) ok_game_init(&game);
}
Uint64 now = SDL_GetPerformanceCounter();
float dt = (float)(now - prev) / (float)freq;
prev = now;
if (dt > 0.05f) dt = 0.05f;
if (game.running) ok_game_update(&game, dt);
ok_render_draw(&game);
}
done:
ok_render_shutdown();
return 0;
}

19
src/map.c Normal file
View file

@ -0,0 +1,19 @@
#include "ok_map.h"
#include <stdlib.h>
void ok_map_generate(OkMap *m) {
for (int y = 0; y < OK_MAP_H; y++) {
for (int x = 0; x < OK_MAP_W; x++) {
m->tile[y][x] = 0;
if (x == 0 || y == 0 || x == OK_MAP_W - 1 || y == OK_MAP_H - 1)
m->tile[y][x] = 1;
else if ((x * 17 + y * 31) % 47 == 0)
m->tile[y][x] = 2; /* scrap pile */
}
}
}
bool ok_map_walkable(const OkMap *m, int x, int y) {
if (x < 0 || y < 0 || x >= OK_MAP_W || y >= OK_MAP_H) return false;
return m->tile[y][x] != 1;
}

71
src/render.c Normal file
View file

@ -0,0 +1,71 @@
#include "ok_render.h"
#include <stdio.h>
static SDL_Window *win;
static SDL_Renderer *ren;
static int tile = 12;
bool ok_render_init(int w, int h) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
return false;
}
win = SDL_CreateWindow("OpenKKND " OPENKKND_VERSION,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
w, h, SDL_WINDOW_SHOWN);
if (!win) return false;
ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!ren) ren = SDL_CreateRenderer(win, -1, 0);
return ren != NULL;
}
SDL_Window *ok_render_window(void) { return win; }
void ok_render_draw(const OkGame *g) {
SDL_SetRenderDrawColor(ren, 20, 18, 16, 255);
SDL_RenderClear(ren);
for (int y = 0; y < OK_MAP_H; y++) {
for (int x = 0; x < OK_MAP_W; x++) {
uint8_t t = g->map.tile[y][x];
if (t == 1) SDL_SetRenderDrawColor(ren, 50, 45, 40, 255);
else if (t == 2) SDL_SetRenderDrawColor(ren, 90, 70, 30, 255);
else SDL_SetRenderDrawColor(ren, 35, 40, 32, 255);
SDL_Rect r = {x * tile, y * tile, tile - 1, tile - 1};
SDL_RenderFillRect(ren, &r);
}
}
for (int i = 0; i < g->unit_count; i++) {
const OkUnit *u = &g->units[i];
if (!u->alive) continue;
if (u->faction == OK_FACTION_SURVIVORS)
SDL_SetRenderDrawColor(ren, 60, 160, 220, 255);
else
SDL_SetRenderDrawColor(ren, 220, 90, 50, 255);
int s = (u->kind == OK_UNIT_VEHICLE) ? tile : tile - 3;
SDL_Rect r = {u->x * tile + 1, u->y * tile + 1, s, s};
SDL_RenderFillRect(ren, &r);
/* hp bar */
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
SDL_Rect bg = {r.x, r.y - 3, s, 2};
SDL_RenderFillRect(ren, &bg);
int hw = s * u->hp / (u->hp_max ? u->hp_max : 1);
SDL_SetRenderDrawColor(ren, 40, 200, 60, 255);
SDL_Rect hg = {r.x, r.y - 3, hw, 2};
SDL_RenderFillRect(ren, &hg);
}
if (!g->running) {
if (g->survivors_win) SDL_SetRenderDrawColor(ren, 40, 180, 255, 180);
else SDL_SetRenderDrawColor(ren, 255, 80, 40, 180);
SDL_Rect banner = {0, (OK_MAP_H * tile) / 2 - 20, OK_MAP_W * tile, 40};
SDL_RenderFillRect(ren, &banner);
}
SDL_RenderPresent(ren);
}
void ok_render_shutdown(void) {
if (ren) SDL_DestroyRenderer(ren);
if (win) SDL_DestroyWindow(win);
ren = NULL;
win = NULL;
SDL_Quit();
}

37
src/unit.c Normal file
View file

@ -0,0 +1,37 @@
#include "ok_unit.h"
#include "ok_map.h"
#include <math.h>
static int dist2(int ax, int ay, int bx, int by) {
int dx = ax - bx, dy = ay - by;
return dx * dx + dy * dy;
}
void ok_units_update(OkGame *g, float dt) {
(void)dt;
for (int i = 0; i < g->unit_count; i++) {
OkUnit *u = &g->units[i];
if (!u->alive) continue;
if (u->target < 0 || u->target >= g->unit_count) continue;
OkUnit *t = &g->units[u->target];
if (!t->alive || t->faction == u->faction) {
u->target = -1;
continue;
}
int range = (u->kind == OK_UNIT_VEHICLE) ? 8 : 4;
if (dist2(u->x, u->y, t->x, t->y) <= range * range) {
int dmg = (u->kind == OK_UNIT_VEHICLE) ? 4 : 2;
t->hp -= dmg;
if (t->hp <= 0) {
t->alive = false;
t->hp = 0;
}
} else {
/* crude step toward target */
int nx = u->x + (t->x > u->x) - (t->x < u->x);
int ny = u->y + (t->y > u->y) - (t->y < u->y);
if (ok_map_walkable(&g->map, nx, u->y)) u->x = nx;
if (ok_map_walkable(&g->map, u->x, ny)) u->y = ny;
}
}
}