#include "kknd.h" /* ========================================================================= * ai.c - Enemy/human commander. Grows an economy and launches attack waves. * ========================================================================= */ static Building *ai_find_bld(Game *g, Faction f, BuildingType t) { for (int i = 0; i < g->bld_count; i++) { Building *b = &g->buildings[i]; if (b->dead || b->faction != f || b->type != t) continue; if (b->build < 1.0f) continue; return b; } return NULL; } static int ai_count_units(Game *g, Faction f, int combat_only) { int n = 0; for (int i = 0; i < g->unit_count; i++) { Unit *u = &g->units[i]; if (u->dead || u->faction != f) continue; if (combat_only && (u->type == UT_HARVESTER)) continue; n++; } return n; } static int ai_count_harvesters(Game *g, Faction f) { int n = 0; for (int i = 0; i < g->unit_count; i++) { Unit *u = &g->units[i]; if (u->dead || u->faction != f) continue; if (u->type == UT_HARVESTER) n++; } return n; } static void ai_run_faction(Game *g, Faction f) { Building *base = ai_find_bld(g, f, BT_BASE); Building *wf = ai_find_bld(g, f, BT_WARFACTORY); Building *ref = ai_find_bld(g, f, BT_REFINERY); if (!base) return; /* early defensive turret */ if (g->time < 60 && g->scrap[f] > 600) { int bx = base->tx + 3, by = base->ty - 2; if (bld_can_place(g, BT_TURRET, bx, by)) { if (bld_spawn(g, BT_TURRET, f, bx, by) >= 0) g->scrap[f] -= bld_def(BT_TURRET)->cost; } } /* research lab + upgrades */ if (!ai_find_bld(g, f, BT_RESEARCH) && g->scrap[f] > 400) { int bx = base->tx - 3, by = base->ty + 2; if (bld_can_place(g, BT_RESEARCH, bx, by)) { int r = bld_spawn(g, BT_RESEARCH, f, bx, by); if (r >= 0) g->scrap[f] -= bld_def(BT_RESEARCH)->cost; } } if (g->research_lab[f] > 0 && g->research_cur[f] < 0) { int best = -1, bestlv = 99; for (int u = 0; u < UP_COUNT; u++) { if (g->upg[f].lvl[u] >= UP_MAX_LVL) continue; if (g->upg[f].lvl[u] < bestlv) { bestlv = g->upg[f].lvl[u]; best = u; } } if (best >= 0) bld_research(g, f, best); } /* keep harvesters flowing */ if (ref && ai_count_harvesters(g, f) < 4 && g->scrap[f] >= 300) bld_queue_unit(g, (int)(ref - g->buildings), UT_HARVESTER); /* pump combat units */ if (wf) { int combat = ai_count_units(g, f, 1); int cap = (g->diff == DIFF_HARD) ? 40 : (g->diff == DIFF_NORMAL ? 28 : 18); if (combat < cap) { int r = rng_range(&(RNG){0}, 0, 6); UnitType pick = (r == 0) ? UT_BUGGY : (r == 1) ? UT_TANK : (r == 2) ? UT_ARTILLERY : (r == 3) ? UT_JEEP : (r == 4) ? UT_RAVAGER : (r == 5) ? UT_FLAME : UT_MISSILE; bld_queue_unit(g, (int)(wf - g->buildings), pick); } } /* attack wave every ~35s once we have a force */ g->ai_wave[f] += (g->diff == DIFF_HARD ? 4.0f : (g->diff == DIFF_NORMAL ? 6.0f : 9.0f)); if (g->ai_wave[f] >= 35.0f) { g->ai_wave[f] = 0; Faction foe = (f == g->human) ? g->enemy : g->human; int hx = g->map.spawn_x[foe], hy = g->map.spawn_y[foe]; float tx, ty; map_tile_to_world(hx, hy, &tx, &ty); int launched = 0; for (int i = 0; i < g->unit_count; i++) { Unit *u = &g->units[i]; if (u->dead || u->faction != f) continue; if (u->type == UT_HARVESTER) continue; unit_issue_move(g, (int)(u - g->units), tx, ty, 1); launched++; } if (launched > 0) { engine_log("%s warband (%d) advances on the %s!", faction_name(f), launched, faction_name(foe)); if (f == g->human) game_log(g, "Warband (%d units) sent to attack!", launched); } } } void ai_command(Faction f, int diff) { (void)f; (void)diff; } void ai_update(Game *g, float dt) { g->ai_timer -= dt; if (g->ai_timer > 0) return; float interval = (g->diff == DIFF_HARD) ? 4.0f : (g->diff == DIFF_NORMAL ? 6.0f : 9.0f); g->ai_timer = interval; ai_run_faction(g, g->enemy); if (g_auto) ai_run_faction(g, g->human); }