kitchen: README + MISSIONS/engine-c keep-drop notes
This commit is contained in:
parent
1d351918da
commit
575d7d29b8
130 changed files with 9817 additions and 1 deletions
214
engine-c/src/buildings.c
Normal file
214
engine-c/src/buildings.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#include "kknd.h"
|
||||
|
||||
/* =========================================================================
|
||||
* buildings.c - Structures, placement, production and defensive fire.
|
||||
* ========================================================================= */
|
||||
|
||||
Building *bld_by_id(Game *g, int id) {
|
||||
for (int i = 0; i < g->bld_count; i++)
|
||||
if (!g->buildings[i].dead && g->buildings[i].id == id)
|
||||
return &g->buildings[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bld_can_place(Game *g, BuildingType t, int tx, int ty) {
|
||||
const BldDef *d = bld_def(t);
|
||||
for (int y = ty; y < ty + d->h; y++)
|
||||
for (int x = tx; x < tx + d->w; x++) {
|
||||
if (!map_in_bounds(&g->map, x, y)) return 0;
|
||||
Tile *tl = map_tile(&g->map, x, y);
|
||||
if (!tl->walkable || tl->occupied) return 0;
|
||||
if (tl->terrain == T_WATER || tl->terrain == T_ROCK) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bld_spawn(Game *g, BuildingType t, Faction f, int tx, int ty) {
|
||||
if (g->bld_count >= MAX_BUILDINGS) return -1;
|
||||
if (!bld_can_place(g, t, tx, ty)) return -2;
|
||||
const BldDef *d = bld_def(t);
|
||||
Building *b = &g->buildings[g->bld_count++];
|
||||
memset(b, 0, sizeof(Building));
|
||||
b->id = g->bld_next_id++;
|
||||
b->type = t; b->faction = f;
|
||||
b->tx = tx; b->ty = ty; b->w = d->w; b->h = d->h;
|
||||
b->max_hp = (float)d->hp;
|
||||
b->build = 0; /* starts under construction */
|
||||
b->hp = (float)d->hp * 0.1f;
|
||||
b->producing = 0; b->prod_timer = 0; b->prod_total = 0;
|
||||
b->queue_len = 0;
|
||||
b->rally_x = (float)((tx + d->w/2) * TILE + TILE*2);
|
||||
b->rally_y = (float)((ty + d->h/2) * TILE);
|
||||
b->selected = 0; b->dead = 0; b->atk_cd = 0;
|
||||
for (int y = ty; y < ty + d->h; y++)
|
||||
for (int x = tx; x < tx + d->w; x++)
|
||||
map_tile(&g->map, x, y)->occupied = 1;
|
||||
return g->bld_count - 1;
|
||||
}
|
||||
|
||||
void bld_remove(Game *g, int idx) {
|
||||
if (idx < 0 || idx >= g->bld_count) return;
|
||||
Building *b = &g->buildings[idx];
|
||||
for (int y = b->ty; y < b->ty + b->h; y++)
|
||||
for (int x = b->tx; x < b->tx + b->w; x++) {
|
||||
Tile *t = map_tile(&g->map, x, y);
|
||||
if (t) t->occupied = 0;
|
||||
}
|
||||
if (b->selected) {
|
||||
for (int i = 0; i < g->sel_count; i++)
|
||||
if (g->selection[i] == b->id) {
|
||||
g->selection[i] = g->selection[g->sel_count-1];
|
||||
g->sel_count--; i--;
|
||||
}
|
||||
}
|
||||
g->buildings[idx] = g->buildings[g->bld_count - 1];
|
||||
g->bld_count--;
|
||||
}
|
||||
|
||||
void bld_apply_damage(Game *g, Building *b, float dmg) {
|
||||
float eff = dmg - 3.0f;
|
||||
if (eff < 1) eff = 1;
|
||||
b->hp -= eff;
|
||||
particle_burst(g, (float)(b->tx*TILE+b->w*TILE/2),
|
||||
(float)(b->ty*TILE+b->h*TILE/2), 2, 2, 255, 200, 80);
|
||||
if (b->hp <= 0 && !b->dead) {
|
||||
b->dead = 1;
|
||||
particle_burst(g, (float)(b->tx*TILE+b->w*TILE/2),
|
||||
(float)(b->ty*TILE+b->h*TILE/2), 20, 1, 255, 140, 40);
|
||||
particle_burst(g, (float)(b->tx*TILE+b->w*TILE/2),
|
||||
(float)(b->ty*TILE+b->h*TILE/2), 12, 3, 90, 80, 70);
|
||||
audio_sfx(2);
|
||||
if (g->cam.shake < 9) g->cam.shake = 9;
|
||||
if (b->faction == g->human)
|
||||
game_log(g, "Structure lost: %s", bld_def(b->type)->name);
|
||||
}
|
||||
}
|
||||
|
||||
void bld_queue_unit(Game *g, int idx, UnitType t) {
|
||||
Building *b = &g->buildings[idx];
|
||||
const BldDef *d = bld_def(b->type);
|
||||
int allowed = 0;
|
||||
for (int i = 0; i < d->produces_count; i++)
|
||||
if (d->produces[i] == t) { allowed = 1; break; }
|
||||
if (!allowed) { audio_sfx(5); return; }
|
||||
if (b->queue_len >= MAX_SELECTION) { audio_sfx(5); return; }
|
||||
const UnitDef *ud = unit_def(t, b->faction);
|
||||
if (g->scrap[b->faction] < ud->cost) { audio_sfx(5); return; }
|
||||
b->queue[b->queue_len++] = t;
|
||||
g->scrap[b->faction] -= ud->cost;
|
||||
audio_sfx(3);
|
||||
}
|
||||
|
||||
void bld_update(Game *g, float dt) {
|
||||
for (int f = 0; f < MAX_FACTIONS; f++) g->research_lab[f] = 0;
|
||||
|
||||
for (int i = 0; i < g->bld_count; i++) {
|
||||
Building *b = &g->buildings[i];
|
||||
if (b->dead) continue;
|
||||
const BldDef *d = bld_def(b->type);
|
||||
if (d->is_research && b->build >= 1.0f) g->research_lab[b->faction]++;
|
||||
|
||||
/* construction progress (faster with Logistics upgrade) */
|
||||
if (b->build < 1.0f) {
|
||||
float rate = (float)d->build_time;
|
||||
rate /= (1.0f + 0.12f * (float)g->upg[b->faction].lvl[UP_BUILD]);
|
||||
b->build += dt / rate;
|
||||
if (b->build >= 1.0f) { b->build = 1.0f; b->hp = b->max_hp; audio_sfx(3); }
|
||||
else b->hp = b->max_hp * (0.1f + 0.9f * b->build);
|
||||
}
|
||||
|
||||
if (b->atk_cd > 0) b->atk_cd -= dt;
|
||||
|
||||
/* turret auto-fire */
|
||||
if (d->is_turret && b->build >= 1.0f && b->atk_cd <= 0) {
|
||||
float bx = (float)(b->tx*TILE + b->w*TILE/2);
|
||||
float by = (float)(b->ty*TILE + b->h*TILE/2);
|
||||
int best = -1; float bestd = d->range * d->range;
|
||||
for (int j = 0; j < g->unit_count; j++) {
|
||||
Unit *u = &g->units[j];
|
||||
if (u->dead || u->faction == b->faction) continue;
|
||||
float dd = kknd_dist2(bx, by, u->x, u->y);
|
||||
if (dd < bestd) { bestd = dd; best = u->id; }
|
||||
}
|
||||
if (best >= 0) {
|
||||
bullet_spawn(g, bx, by, best, -1, d->atk, b->faction, 2);
|
||||
b->atk_cd = 0.6f;
|
||||
audio_sfx(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* production (faster with Logistics upgrade) */
|
||||
if (b->build >= 1.0f && b->producing == 0 && b->queue_len > 0) {
|
||||
UnitType t = (UnitType)b->queue[0];
|
||||
const UnitDef *ud = unit_def(t, b->faction);
|
||||
b->producing = t;
|
||||
b->prod_total = (float)ud->build_time;
|
||||
b->prod_timer = 0;
|
||||
for (int k = 0; k < b->queue_len - 1; k++) b->queue[k] = b->queue[k+1];
|
||||
b->queue_len--;
|
||||
}
|
||||
if (b->producing != 0) {
|
||||
const UnitDef *ud = unit_def((UnitType)b->producing, b->faction);
|
||||
float total = (float)ud->build_time;
|
||||
total /= (1.0f + 0.12f * (float)g->upg[b->faction].lvl[UP_BUILD]);
|
||||
b->prod_timer += dt;
|
||||
if (b->prod_timer >= total) {
|
||||
unit_spawn(g, (UnitType)b->producing, b->faction, b->rally_x, b->rally_y);
|
||||
b->producing = 0; b->prod_timer = 0;
|
||||
audio_sfx(3);
|
||||
}
|
||||
}
|
||||
|
||||
/* repair depot heals nearby units */
|
||||
if (d->type == BT_REPAIR && b->build >= 1.0f) {
|
||||
float bx = (float)(b->tx*TILE + b->w*TILE/2);
|
||||
float by = (float)(b->ty*TILE + b->h*TILE/2);
|
||||
for (int j = 0; j < g->unit_count; j++) {
|
||||
Unit *u = &g->units[j];
|
||||
if (u->dead || u->faction != b->faction) continue;
|
||||
if (kknd_dist2(bx, by, u->x, u->y) < (TILE*5)*(TILE*5) &&
|
||||
u->hp < u->max_hp) {
|
||||
u->hp += dt * 15.0f;
|
||||
if (u->hp > u->max_hp) u->hp = u->max_hp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* research progression */
|
||||
for (int f = 0; f < MAX_FACTIONS; f++) {
|
||||
if (g->research_lab[f] <= 0) { g->research_cur[f] = -1; g->research_prog[f] = 0; continue; }
|
||||
int up = g->research_cur[f];
|
||||
if (up < 0) continue;
|
||||
int lvl = g->upg[f].lvl[up];
|
||||
if (lvl >= UP_MAX_LVL) { g->research_cur[f] = -1; g->research_prog[f] = 0; continue; }
|
||||
g->research_prog[f] += dt * (float)g->research_lab[f] / 18.0f;
|
||||
if (g->research_prog[f] >= 1.0f) {
|
||||
g->upg[f].lvl[up] = lvl + 1;
|
||||
g->research_prog[f] = 0;
|
||||
g->research_cur[f] = -1;
|
||||
audio_sfx(3);
|
||||
engine_log("%s researched %s (lvl %d)", faction_name((Faction)f),
|
||||
research_name(up), lvl + 1);
|
||||
if ((Faction)f == g->human)
|
||||
game_log(g, "Research complete: %s (lvl %d)", research_name(up), lvl + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = g->bld_count - 1; i >= 0; i--)
|
||||
if (g->buildings[i].dead) bld_remove(g, i);
|
||||
}
|
||||
|
||||
int bld_research(Game *g, Faction f, int up) {
|
||||
if (up < 0 || up >= UP_COUNT) return 0;
|
||||
if (g->research_lab[f] <= 0) return 0;
|
||||
if (g->research_cur[f] >= 0) return 0;
|
||||
if (g->upg[f].lvl[up] >= UP_MAX_LVL) return 0;
|
||||
int cost = research_cost(up, g->upg[f].lvl[up]);
|
||||
if (g->scrap[f] < cost) { audio_sfx(5); return 0; }
|
||||
g->scrap[f] -= cost;
|
||||
g->research_cur[f] = up;
|
||||
g->research_prog[f] = 0;
|
||||
audio_sfx(3);
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue