game: map units combat and SDL2 render slice
This commit is contained in:
parent
87d99f7007
commit
4dedf5943b
5 changed files with 228 additions and 0 deletions
57
src/game.c
Normal file
57
src/game.c
Normal 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
44
src/main.c
Normal 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
19
src/map.c
Normal 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
71
src/render.c
Normal 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
37
src/unit.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue