From cc4ea47590ba35aa58e206a872ccad2a415225a4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 28 May 2026 09:13:09 +0200 Subject: [PATCH] additional building protection from monsters --- src/monsters.c | 25 ++++++++++++++++--- src/monsters.test.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/monsters.c b/src/monsters.c index c04b198de..c30798e3a 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -220,23 +220,42 @@ void monsters_desert(struct faction *monsters) int monster_attacks(unit * monster, bool rich_only) { - const race *rc_serpent = get_race(RC_SEASERPENT); int result = -1; if (monster_can_attack(monster)) { + const race *rc_serpent = get_race(RC_SEASERPENT); region *r = monster->region; unit *u2; int money = 0; + building *b; + int protect = 0; + for (b = r->buildings; b; b = b->next) { + if (b->type->flags & BTF_FORTIFICATION) { + protect += b->size; + } + } for (u2 = r->units; u2; u2 = u2->next) { - if (u2->faction != monster->faction && cansee(monster->faction, r, u2, 0) && !in_safe_building(u2, monster)) { - int m = get_money(u2); + if (u2->faction != monster->faction && cansee(monster->faction, r, u2, 0)) { + int m; + b = inside_building(u2); + + if (b && b != inside_building(monster)) { + if (b->type->flags & BTF_FORTIFICATION) { + continue; + } + else if (protect >= u2->number) { + protect -= u2->number; + continue; + } + } if (u_race(monster) == rc_serpent) { /* attack bigger ships only */ if (!u2->ship || u2->ship->type->cargo <= 50000) { continue; } } + m = get_money(u2); if (!rich_only || m > 0) { order *ord = monster_attack(monster, u2); if (ord) { diff --git a/src/monsters.test.c b/src/monsters.test.c index 8a377eb6d..dd455c284 100644 --- a/src/monsters.test.c +++ b/src/monsters.test.c @@ -4,6 +4,7 @@ #include "kernel/attrib.h" #include "kernel/config.h" +#include "kernel/building.h" #include "kernel/faction.h" #include "kernel/group.h" #include "kernel/item.h" @@ -123,6 +124,62 @@ static void test_monsters_attack_ocean(CuTest * tc) test_teardown(); } +static void test_monsters_dont_attack_buildings(CuTest *tc) +{ + region *r; + unit *u, *m; + building_type *btype; + building *b; + + test_setup(); + btype = test_create_buildingtype("castle"); + btype->flags |= BTF_FORTIFICATION; + config_set("rules.monsters.attack_chance", "1"); + create_monsters(&u, &m); + r = findregion(0, 0); + move_unit(m, r, NULL); + m->flags = UFL_GUARD; + unit_setid(u, 2); + + b = test_create_building(r, btype); + b->size = u->number = 5; + u_set_building(u, b); + + plan_monsters(m->faction); + CuAssertPtrEquals(tc, NULL, find_order("attack 2", m)); + + /** second unit, does not fit into the building, can be attacked */ + u = test_create_unit(u->faction, r); + unit_setid(u, 3); + u_set_building(u, b); + plan_monsters(m->faction); + CuAssertPtrEquals(tc, NULL, find_order("attack 2", m)); + CuAssertPtrNotNull(tc, find_order("attack 3", m)); + + /** second unit is fully inside a non-fortified building that gets protected */ + u->number = b->size; + u_set_building(u, NULL); + b = test_create_building(r, NULL); + b->size = u->number; + u_set_building(u, b); + plan_monsters(m->faction); + CuAssertPtrEquals(tc, NULL, find_order("attack 2", m)); + CuAssertPtrEquals(tc, NULL, find_order("attack 3", m)); + + /** second unit does not fit into the protected building */ + ++u->number; + plan_monsters(m->faction); + CuAssertPtrEquals(tc, NULL, find_order("attack 2", m)); + CuAssertPtrNotNull(tc, find_order("attack 3", m)); + + /** second unit is fully inside protected building that is too big */ + b->size = u->number; + plan_monsters(m->faction); + CuAssertPtrEquals(tc, NULL, find_order("attack 2", m)); + CuAssertPtrNotNull(tc, find_order("attack 3", m)); + + test_teardown(); +} static void test_monsters_waiting(CuTest * tc) { unit *u, *m; @@ -336,6 +393,7 @@ CuSuite *get_monsters_suite(void) SUITE_ADD_TEST(suite, test_monsters_hate); SUITE_ADD_TEST(suite, test_spawn_seaserpent); SUITE_ADD_TEST(suite, test_monsters_attack_ocean); + SUITE_ADD_TEST(suite, test_monsters_dont_attack_buildings); SUITE_ADD_TEST(suite, test_seaserpent_piracy); SUITE_ADD_TEST(suite, test_monsters_waiting); SUITE_ADD_TEST(suite, test_monsters_attack_not);