diff --git a/res/core/messages.xml b/res/core/messages.xml
index d566c82e6..0cd66ca08 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -868,21 +868,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index b87fe241a..507f0e39d 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -1773,7 +1773,7 @@ msgid "storm"
msgstr "\"Die $ship($ship) wird in $region($region) von Stürmen abgetrieben$if($sink,\" und sinkt\",\"\").\""
msgid "nr_insectwinter"
-msgstr "Es ist Winter, und Insekten können nur in Wüsten oder mit Hilfe des Nestwärme-Tranks Personen rekrutieren."
+msgstr "Insekten können wegen des Winterwetters in der kommenden Woche nur in Wüsten oder mit Hilfe des Nestwärme-Tranks Personen rekrutieren."
msgid "spellfail_nomonsters"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nicht auf Monster gezaubert werden.\""
@@ -2303,9 +2303,6 @@ msgstr "\"$unit($mage) erleidet durch den Tod seines Vertrauten einen Schock.\""
msgid "error269"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Hier kann man nicht zaubern.\""
-msgid "sink_saved_msg"
-msgstr "\"$unit($unit) überlebt unbeschadet und rettet sich nach $region($region).\""
-
msgid "race_noregroup"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $race($race,0) können nicht neu gruppiert werden.\""
@@ -2654,9 +2651,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - Die Einheit kan
msgid "analyse_building_noage"
msgstr "\"$unit($mage) fand heraus, dass auf $building($building) der Zauber '$curse($curse)' liegt, dessen Kraft ausreicht, um noch Jahrhunderte bestehen zu bleiben.\""
-msgid "sink_lost_msg"
-msgstr "\"$int($amount) Personen von $unit($unit) ertrinken.$if($isnull($region),\"\",\" Die Einheit rettet sich nach $region($region).\")\""
-
msgid "error130"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Syntax: MAGIEGEBIET [1-5].\""
@@ -2733,7 +2727,7 @@ msgid "volcanostartsmoke"
msgstr "\"Aus dem Vulkankrater von $region($region) steigt plötzlich Rauch.\""
msgid "nr_insectfall"
-msgstr "Es ist Spätherbst, und diese Woche ist die letzte vor dem Winter, in der Insekten rekrutieren können."
+msgstr "Es ist Spätherbst, und die kommende Woche ist die letzte vor dem Winter, in der Insekten rekrutieren können."
msgid "error296"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Hier werden niemals Bäume wachsen.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index 6eaf6f258..bf9d8e3c9 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -2303,9 +2303,6 @@ msgstr "\"$unit($mage) receives a shock when his familiar dies.\""
msgid "error269"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - You cannot cast spells here.\""
-msgid "sink_saved_msg"
-msgstr "\"$unit($unit) survives unscathed and makes it to $region($region).\""
-
msgid "race_noregroup"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $race($race,0) cannot be regrouped.\""
@@ -2654,9 +2651,6 @@ msgstr "\"$unit($unit) in $region($region): '$order($command)' - The unit cannot
msgid "analyse_building_noage"
msgstr "\"$unit($mage) discovers that $building($building) is charmed with '$curse($curse)', which will last for centuries.\""
-msgid "sink_lost_msg"
-msgstr "\"$int($amount) people of $unit($unit) drown.$if($isnull($region),\"\",\" The unit makes it to $region($region).\")\""
-
msgid "error130"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Syntax: MAGIC SPHERE [1-5].\""
diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index febcc2a3f..403db0a46 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -338,45 +338,6 @@ function test_message()
return msg
end
-function test_events()
- local fail = 1
- local function msg_handler(u, evt)
- str = evt:get(0)
- u2 = evt:get(1)
- assert(u2~=nil)
- assert(str=="Du Elf stinken")
- message_unit(u, u2, "thanks unit, i got your message: " .. str)
- message_faction(u, u2.faction, "thanks faction, i got your message: " .. str)
- message_region(u, "thanks region, i got your message: " .. str)
- fail = 0
- end
-
- plain = region.create(0, 0, "plain")
- skill = 8
-
- f = create_faction('elf')
- f.age = 20
-
- u = unit.create(f, plain)
- u.number = 1
- u:add_item("money", u.number*100)
- u:clear_orders()
- u:add_order("NUMMER PARTEI test")
- u:add_handler("message", msg_handler)
- msg = "BOTSCHAFT EINHEIT " .. itoa36(u.id) .. " Du~Elf~stinken"
- f = create_faction('elf')
- f.age = 20
-
- u = unit.create(f, plain)
- u.number = 1
- u:add_item("money", u.number*100)
- u:clear_orders()
- u:add_order("NUMMER PARTEI eviL")
- u:add_order(msg)
- process_orders()
- assert(fail==0)
-end
-
function test_renumber_ship()
local r = region.create(0, 0, "plain")
local f = create_faction('human')
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index be09ec448..bf90ab6a0 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -517,3 +517,16 @@ function test_buy_sell()
assert_equal(4, u:get_item(item))
assert_not_equal(0, u:get_item('money'))
end
+
+function test_seaserpent_attack()
+ local r = region.create(0, 0, 'ocean')
+ local sh = ship.create(r, 'boat')
+ local us = unit.create(get_monsters(), r, 1, 'seaserpent')
+ local u = unit.create(faction.create('human', 'enno@example.com'), r, 20, 'human')
+ u.ship = sh
+ us:clear_orders()
+ us:add_order('ATTACKIERE ' .. itoa36(u.id))
+ us:set_skill('unarmed', 10)
+ process_orders()
+ write_reports()
+end
diff --git a/src/battle.c b/src/battle.c
index 38590b8a6..9e2f841c0 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "move.h"
#include "skill.h"
#include "study.h"
+#include "spy.h"
#include
#include
@@ -2787,10 +2788,12 @@ static void aftermath(battle * b)
ship *sh = *sp;
freset(sh, SF_DAMAGED);
if (sh->damage >= sh->size * DAMAGE_SCALE) {
+ sink_ship(sh);
remove_ship(sp, sh);
}
- if (*sp == sh)
+ else {
sp = &sh->next;
+ }
}
}
diff --git a/src/bind_unit.c b/src/bind_unit.c
index e0fa9f86b..fddcd5608 100644
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -455,48 +455,6 @@ int fctr_handle(struct trigger *tp, void *data)
return 0;
}
-static void fctr_init(trigger * t)
-{
- t->data.v = calloc(sizeof(fctr_data), 1);
-}
-
-static void fctr_done(trigger * t)
-{
- fctr_data *fd = (fctr_data *)t->data.v;
- lua_State *L = (lua_State *)global.vm_state;
- luaL_unref(L, LUA_REGISTRYINDEX, fd->fhandle);
- free(fd);
-}
-
-static struct trigger_type tt_lua = {
- "lua_event",
- fctr_init,
- fctr_done,
- fctr_handle
-};
-
-static trigger *trigger_lua(struct unit *u, int handle)
-{
- trigger *t = t_new(&tt_lua);
- fctr_data *td = (fctr_data *)t->data.v;
- td->target = u;
- td->fhandle = handle;
- return t;
-}
-
-static int tolua_unit_addhandler(lua_State * L)
-{
- unit *self = (unit *)tolua_tousertype(L, 1, 0);
- const char *ename = tolua_tostring(L, 2, 0);
- int handle;
-
- lua_pushvalue(L, 3);
- handle = luaL_ref(L, LUA_REGISTRYINDEX);
- add_trigger(&self->attribs, ename, trigger_lua(self, handle));
-
- return 0;
-}
-
static int tolua_unit_addnotice(lua_State * L)
{
unit *self = (unit *)tolua_tousertype(L, 1, 0);
@@ -909,8 +867,8 @@ static int tolua_unit_create(lua_State * L)
faction *f = (faction *)tolua_tousertype(L, 1, 0);
region *r = (region *)tolua_tousertype(L, 2, 0);
unit *u;
- const char *rcname = tolua_tostring(L, 4, NULL);
int num = (int)tolua_tonumber(L, 3, 1);
+ const char *rcname = tolua_tostring(L, 4, NULL);
const race *rc;
assert(f && r);
@@ -1046,9 +1004,6 @@ void tolua_unit_open(lua_State * L)
tolua_function(L, TOLUA_CAST "add_notice", tolua_unit_addnotice);
- /* npc logic: */
- tolua_function(L, TOLUA_CAST "add_handler", tolua_unit_addhandler);
-
tolua_variable(L, TOLUA_CAST "race_name", tolua_unit_get_racename,
tolua_unit_set_racename);
tolua_function(L, TOLUA_CAST "add_spell", tolua_unit_addspell);
diff --git a/src/chaos.c b/src/chaos.c
index 21a5a8624..1ec431b7e 100644
--- a/src/chaos.c
+++ b/src/chaos.c
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "chaos.h"
#include "monsters.h"
#include "move.h"
+#include "spy.h"
#include
#include
@@ -144,19 +145,20 @@ static void chaos(region * r)
break;
}
if (dir != MAXDIRECTIONS) {
- ship *sh = r->ships;
+ ship **slist = &r->ships;
unit **up;
- while (sh) {
- ship *nsh = sh->next;
- double dmg =
- config_get_flt("rules.ship.damage.atlantis",
- 0.50);
- damage_ship(sh, dmg);
+ while (*slist) {
+ ship *sh = *slist;
+
+ damage_ship(sh, 0.5);
if (sh->damage >= sh->size * DAMAGE_SCALE) {
- remove_ship(&sh->region->ships, sh);
+ sink_ship(sh);
+ remove_ship(slist, sh);
+ }
+ else {
+ slist = &sh->next;
}
- sh = nsh;
}
for (up = &r->units; *up;) {
diff --git a/src/chaos.h b/src/chaos.h
index 550aa3efb..cb913d1a0 100644
--- a/src/chaos.h
+++ b/src/chaos.h
@@ -22,8 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" {
#endif
- struct region;
-
void chaos_update(void);
#ifdef __cplusplus
diff --git a/src/economy.c b/src/economy.c
index 3bb7b1066..dc7e6148b 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -426,6 +426,59 @@ static int recruit_cost(const faction * f, const race * rc)
return -1;
}
+message *can_recruit(unit *u, const race *rc, order *ord, int now)
+{
+ region *r = u->region;
+
+ /* this is a very special case because the recruiting unit may be empty
+ * at this point and we have to look at the creating unit instead. This
+ * is done in cansee, which is called indirectly by is_guarded(). */
+ if (is_guarded(r, u)) {
+ return msg_error(u, ord, 70);
+ }
+
+ if (rc == get_race(RC_INSECT)) {
+ gamedate date;
+ get_gamedate(now, &date);
+ if (date.season == SEASON_WINTER && r->terrain != newterrain(T_DESERT)) {
+ bool usepotion = false;
+ unit *u2;
+
+ for (u2 = r->units; u2; u2 = u2->next) {
+ if (fval(u2, UFL_WARMTH)) {
+ usepotion = true;
+ break;
+ }
+ }
+ if (!usepotion) {
+ return msg_error(u, ord, 98);
+ }
+ }
+ /* in Gletschern, Eisbergen gar nicht rekrutieren */
+ if (r_insectstalled(r)) {
+ return msg_error(u, ord, 97);
+ }
+ }
+ if (is_cursed(r->attribs, &ct_riotzone)) {
+ /* Die Region befindet sich in Aufruhr */
+ return msg_error(u, ord, 237);
+ }
+
+ if (rc && !playerrace(rc)) {
+ return msg_error(u, ord, 139);
+ }
+
+ if (fval(u, UFL_HERO)) {
+ return msg_feedback(u, ord, "error_herorecruit", "");
+ }
+ if (has_skill(u, SK_MAGIC)) {
+ /* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
+ * grunds�tzlich nur alleine! */
+ return msg_error(u, ord, 158);
+ }
+ return NULL;
+}
+
static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
{
region *r = u->region;
@@ -434,6 +487,7 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
const faction *f = u->faction;
const struct race *rc = u_race(u);
int n;
+ message *msg;
init_order_depr(ord);
n = getint();
@@ -456,6 +510,7 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
}
}
}
+
if (recruitcost < 0) {
rc = u_race(u);
recruitcost = recruit_cost(f, rc);
@@ -463,95 +518,46 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
recruitcost = INT_MAX;
}
}
- assert(rc);
- u_setrace(u, rc);
- /* this is a very special case because the recruiting unit may be empty
- * at this point and we have to look at the creating unit instead. This
- * is done in cansee, which is called indirectly by is_guarded(). */
- if (is_guarded(r, u)) {
- cmistake(u, ord, 70, MSG_EVENT);
- return;
- }
-
- if (rc == get_race(RC_INSECT)) {
- gamedate date;
- get_gamedate(turn, &date);
- if (date.season == 0 && r->terrain != newterrain(T_DESERT)) {
- bool usepotion = false;
- unit *u2;
-
- for (u2 = r->units; u2; u2 = u2->next)
- if (fval(u2, UFL_WARMTH)) {
- usepotion = true;
- break;
- }
- if (!usepotion)
- {
- cmistake(u, ord, 98, MSG_EVENT);
- return;
- }
- }
- /* in Gletschern, Eisbergen gar nicht rekrutieren */
- if (r_insectstalled(r)) {
- cmistake(u, ord, 97, MSG_EVENT);
- return;
- }
- }
- if (is_cursed(r->attribs, &ct_riotzone)) {
- /* Die Region befindet sich in Aufruhr */
- cmistake(u, ord, 237, MSG_EVENT);
- return;
- }
-
- if (recruitcost) {
+ if (recruitcost > 0) {
+ int pool;
plane *pl = getplane(r);
- if (pl && fval(pl, PFL_NORECRUITS)) {
+
+ if (pl && (pl->flags & PFL_NORECRUITS)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
return;
}
- if (get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
- recruitcost) < recruitcost) {
+ pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
+ if (pool < recruitcost) {
cmistake(u, ord, 142, MSG_EVENT);
return;
}
+ pool /= recruitcost;
+ if (n > pool) n = pool;
}
- if (!playerrace(rc)) {
- cmistake(u, ord, 139, MSG_EVENT);
- return;
- }
-
- if (fval(u, UFL_HERO)) {
- ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_herorecruit", ""));
- return;
- }
- if (has_skill(u, SK_MAGIC)) {
- /* error158;de;{unit} in {region}: '{command}' - Magier arbeiten
- * grunds�tzlich nur alleine! */
- cmistake(u, ord, 158, MSG_EVENT);
- return;
- }
- if (has_skill(u, SK_ALCHEMY)
- && count_skill(u->faction, SK_ALCHEMY) + n >
- skill_limit(u->faction, SK_ALCHEMY)) {
- cmistake(u, ord, 156, MSG_EVENT);
- return;
- }
- if (recruitcost > 0) {
- int pooled =
- get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
- int pr = pooled / recruitcost;
- if (n > pr) n = pr;
- }
-
- u->wants = n;
if (!n) {
cmistake(u, ord, 142, MSG_EVENT);
return;
}
+ if (has_skill(u, SK_ALCHEMY)) {
+ if (count_skill(u->faction, SK_ALCHEMY) + n > skill_limit(u->faction, SK_ALCHEMY)) {
+ cmistake(u, ord, 156, MSG_EVENT);
+ return;
+ }
+ }
+ assert(rc);
+ msg = can_recruit(u, rc, ord, turn);
+ if (msg) {
+ add_message(&u->faction->msgs, msg);
+ msg_release(msg);
+ return;
+ }
+
+ u_setrace(u, rc);
+ u->wants = n;
o = (econ_request *)calloc(1, sizeof(econ_request));
o->qty = n;
o->unit = u;
diff --git a/src/economy.h b/src/economy.h
index 67762b513..9456a4c93 100644
--- a/src/economy.h
+++ b/src/economy.h
@@ -44,6 +44,7 @@ extern "C" {
#define MAXNEWBIES 5
struct unit;
+ struct race;
struct region;
struct faction;
struct order;
@@ -94,6 +95,7 @@ extern "C" {
void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders);
void expandstealing(struct region * r, struct econ_request * stealorders);
+ struct message *can_recruit(struct unit *u, const struct race *rc, struct order *ord, int now);
void add_recruits(struct unit * u, int number, int wanted);
#ifdef __cplusplus
diff --git a/src/economy.test.c b/src/economy.test.c
index 91d462770..90a4bd260 100644
--- a/src/economy.test.c
+++ b/src/economy.test.c
@@ -480,6 +480,35 @@ static void test_recruit(CuTest *tc) {
test_teardown();
}
+static void test_recruit_insect(CuTest *tc) {
+ unit *u;
+ faction *f;
+ message * msg;
+
+ test_setup();
+ test_create_calendar();
+ f = test_create_faction(test_create_race("insect"));
+ u = test_create_unit(f, test_create_region(0, 0, NULL));
+ u->thisorder = create_order(K_RECRUIT, f->locale, "%d", 1);
+
+ msg = can_recruit(u, f->race, u->thisorder, 1083); /* Autumn */
+ CuAssertPtrEquals(tc, NULL, msg);
+
+ msg = can_recruit(u, f->race, u->thisorder, 1084); /* Insects, Winter */
+ CuAssertPtrNotNull(tc, msg);
+ msg_release(msg);
+
+ u->flags |= UFL_WARMTH;
+ msg = can_recruit(u, f->race, u->thisorder, 1084); /* Insects, potion, Winter */
+ CuAssertPtrEquals(tc, NULL, msg);
+
+ u->flags = 0;
+ msg = can_recruit(u, NULL, u->thisorder, 1084); /* Other races, Winter */
+ CuAssertPtrEquals(tc, NULL, msg);
+
+ test_teardown();
+}
+
static void test_income(CuTest *tc)
{
race *rc;
@@ -764,6 +793,7 @@ CuSuite *get_economy_suite(void)
SUITE_ADD_TEST(suite, test_trade_insect);
SUITE_ADD_TEST(suite, test_maintain_buildings);
SUITE_ADD_TEST(suite, test_recruit);
+ SUITE_ADD_TEST(suite, test_recruit_insect);
SUITE_ADD_TEST(suite, test_loot);
SUITE_ADD_TEST(suite, test_expand_production);
return suite;
diff --git a/src/kernel/ship.h b/src/kernel/ship.h
index c726b4839..ce3283a6f 100644
--- a/src/kernel/ship.h
+++ b/src/kernel/ship.h
@@ -124,9 +124,9 @@ extern "C" {
extern void write_ship_reference(const struct ship *sh,
struct storage *store);
- extern void remove_ship(struct ship **slist, struct ship *s);
- extern void free_ship(struct ship *s);
- extern void free_ships(void);
+ void remove_ship(struct ship **slist, struct ship *s);
+ void free_ship(struct ship *s);
+ void free_ships(void);
const char *ship_getname(const struct ship *sh);
void ship_setname(struct ship *self, const char *name);
diff --git a/src/laws.c b/src/laws.c
index aa6e237e2..8d75b0897 100644
--- a/src/laws.c
+++ b/src/laws.c
@@ -2058,17 +2058,6 @@ int mail_cmd(unit * u, struct order *ord)
break;
}
else {
- attrib *a = a_find(u2->attribs, &at_eventhandler);
- if (a != NULL) {
- event_arg args[3];
- args[0].data.v = (void *)s;
- args[0].type = "string";
- args[1].data.v = (void *)u;
- args[1].type = "unit";
- args[2].type = NULL;
- handle_event(a, "message", args);
- }
-
mailunit(r, u, n, ord, s);
}
return 0;
@@ -2590,6 +2579,7 @@ void sinkships(struct region * r)
}
}
if (sh->damage >= sh->size * DAMAGE_SCALE) {
+ sink_ship(sh);
remove_ship(shp, sh);
}
if (*shp == sh)
diff --git a/src/move.c b/src/move.c
index 205b52416..b815f5ea5 100644
--- a/src/move.c
+++ b/src/move.c
@@ -47,6 +47,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h"
#include "reports.h"
#include "study.h"
+#include "spy.h"
#include "alchemy.h"
#include "travelthru.h"
#include "vortex.h"
@@ -541,6 +542,7 @@ static ship *do_maelstrom(region * r, unit * u)
if (sh->damage >= sh->size * DAMAGE_SCALE) {
ADDMSG(&u->faction->msgs, msg_message("entermaelstrom",
"region ship damage sink", r, sh, damage, 1));
+ sink_ship(sh);
remove_ship(&sh->region->ships, sh);
return NULL;
}
@@ -882,12 +884,14 @@ static void drifting_ships(region * r)
}
if (sh->damage >= sh->size * DAMAGE_SCALE) {
msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("shipsink", "ship", sh));
- remove_ship(&sh->region->ships, sh);
+ sink_ship(sh);
+ remove_ship(shp, sh);
}
}
- if (*shp == sh)
+ if (*shp == sh) {
shp = &sh->next;
+ }
}
}
}
@@ -1926,6 +1930,7 @@ static void sail(unit * u, order * ord, region_list ** routep, bool drifting)
if (sh->damage >= sh->size * DAMAGE_SCALE) {
if (sh->region) {
ADDMSG(&f->msgs, msg_message("shipsink", "ship", sh));
+ sink_ship(sh);
remove_ship(&sh->region->ships, sh);
}
sh = NULL;
diff --git a/src/move.test.c b/src/move.test.c
index f8c11114f..f01915e2a 100644
--- a/src/move.test.c
+++ b/src/move.test.c
@@ -287,6 +287,9 @@ void setup_drift (struct drift_fixture *fix) {
u_set_ship(fix->u, fix->sh = test_create_ship(fix->u->region, fix->st_boat));
assert(fix->sh);
+ mt_create_va(mt_new("sink_msg", NULL),
+ "ship:ship", "region:region", MT_NEW_END);
+
mt_create_va(mt_new("ship_drift", NULL),
"ship:ship", "dir:int", MT_NEW_END);
mt_create_va(mt_new("shipsink", NULL),
diff --git a/src/randenc.c b/src/randenc.c
index 7de49b9e2..820d3760f 100644
--- a/src/randenc.c
+++ b/src/randenc.c
@@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "economy.h"
#include "monsters.h"
#include "move.h"
+#include "spy.h"
#include "study.h"
#include "volcano.h"
@@ -295,6 +296,7 @@ static void move_iceberg(region * r)
ADDMSG(&u->faction->msgs, msg_message("overrun_by_iceberg_des",
"ship", sh));
}
+ sink_ship(sh);
remove_ship(&sh->region->ships, sh);
}
else if (u != NULL) {
@@ -421,6 +423,7 @@ static void godcurse(void)
ADDMSG(&uo->faction->msgs,
msg_message("godcurse_destroy_ship", "ship", sh));
}
+ sink_ship(sh);
remove_ship(&sh->region->ships, sh);
}
sh = shn;
diff --git a/src/reports.c b/src/reports.c
index ac413bfc6..9d9ab5f6f 100644
--- a/src/reports.c
+++ b/src/reports.c
@@ -1517,24 +1517,23 @@ static void cb_add_seen(region *r, unit *u, void *cbdata) {
}
}
-void report_warnings(faction *f, const gamedate *date)
+void report_warnings(faction *f, int now)
{
if (f->age < NewbieImmunity()) {
ADDMSG(&f->msgs, msg_message("newbieimmunity", "turns",
NewbieImmunity() - f->age));
}
- if (date) {
- if (f->race == get_race(RC_INSECT)) {
- if (date->season == 0) {
- ADDMSG(&f->msgs, msg_message("nr_insectwinter", ""));
- }
- else {
- gamedate next;
- get_gamedate(date->turn + 1, &next);
- if (next.season == 0) {
- ADDMSG(&f->msgs, msg_message("nr_insectfall", ""));
- }
+ if (f->race == get_race(RC_INSECT)) {
+ gamedate date;
+ get_gamedate(now + 1, &date);
+
+ if (date.season == SEASON_WINTER) {
+ ADDMSG(&f->msgs, msg_message("nr_insectwinter", ""));
+ }
+ else if (date.season == SEASON_AUTUMN) {
+ if (get_gamedate(now + 2 + 2, &date)->season == SEASON_WINTER) {
+ ADDMSG(&f->msgs, msg_message("nr_insectfall", ""));
}
}
}
@@ -1552,11 +1551,9 @@ void prepare_report(report_context *ctx, faction *f)
static bool rule_region_owners;
static bool rule_lighthouse_units;
const struct building_type *bt_lighthouse = bt_find("lighthouse");
- gamedate now;
/* Insekten-Winter-Warnung */
- get_gamedate(turn, &now);
- report_warnings(f, &now);
+ report_warnings(f, turn);
if (bt_lighthouse && config_changed(&config)) {
rule_region_owners = config_token("rules.region_owner_pay_building", bt_lighthouse->_name);
diff --git a/src/reports.h b/src/reports.h
index 0bdaf7d8c..a6dddb1b4 100644
--- a/src/reports.h
+++ b/src/reports.h
@@ -115,7 +115,7 @@ extern "C" {
int size, const struct faction *viewer, bool see_unit);
int report_items(const struct unit *u, struct item *result, int size,
const struct unit *owner, const struct faction *viewer);
- void report_warnings(struct faction *f, const struct gamedate *date);
+ void report_warnings(struct faction *f, int now);
void report_raceinfo(const struct race *rc, const struct locale *lang, char *buf, size_t length);
void report_race_skills(const struct race *rc, char *zText, size_t length, const struct locale *lang);
void report_item(const struct unit *owner, const struct item *i,
diff --git a/src/reports.test.c b/src/reports.test.c
index e86080b0e..65b5b06b0 100644
--- a/src/reports.test.c
+++ b/src/reports.test.c
@@ -789,19 +789,29 @@ static void test_insect_warnings(CuTest *tc) {
faction *f;
gamedate gd;
- /* OBS: in unit tests, get_gamedate always returns season = 0 */
test_setup();
+ test_create_calendar();
test_inject_messagetypes();
f = test_create_faction(test_create_race("insect"));
- gd.turn = 0;
- gd.season = 3;
- report_warnings(f, &gd);
- CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectfall"));
-
- gd.season = 0;
- report_warnings(f, &gd);
+ CuAssertIntEquals(tc, SEASON_AUTUMN, get_gamedate(1083, &gd)->season);
+ report_warnings(f, gd.turn);
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "nr_insectfall"));
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectwinter"));
+ test_clear_messages(f);
+
+ CuAssertIntEquals(tc, SEASON_AUTUMN, get_gamedate(1082, &gd)->season);
+ report_warnings(f, gd.turn);
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectfall"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "nr_insectwinter"));
+ test_clear_messages(f);
+
+ CuAssertIntEquals(tc, SEASON_WINTER, get_gamedate(1084, &gd)->season);
+ report_warnings(f, gd.turn);
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "nr_insectfall"));
+ CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectwinter"));
+ test_clear_messages(f);
+
test_teardown();
}
@@ -810,16 +820,16 @@ static void test_newbie_warning(CuTest *tc) {
test_setup();
test_inject_messagetypes();
- f = test_create_faction(test_create_race("insect"));
+ f = test_create_faction(NULL);
config_set_int("NewbieImmunity", 3);
f->age = 2;
- report_warnings(f, NULL);
+ report_warnings(f, 0);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "newbieimmunity"));
test_clear_messages(f);
f->age = 3;
- report_warnings(f, NULL);
+ report_warnings(f, 0);
CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "newbieimmunity"));
test_clear_messages(f);
diff --git a/src/spy.c b/src/spy.c
index 94d6c4e7d..ae70f4db8 100644
--- a/src/spy.c
+++ b/src/spy.c
@@ -392,19 +392,16 @@ static int try_destruction(unit * u, unit * u2, const ship * sh, int skilldiff)
return 1; /* success */
}
-static void sink_ship(region * r, ship * sh, unit * saboteur)
+void sink_ship(ship * sh)
{
- unit **ui, *u;
- region *safety = r;
- int i;
- direction_t d;
- double probability = 0.0;
+ unit *u;
+ region *r;
message *sink_msg = NULL;
faction *f;
- assert(r);
- assert(sh);
- assert(saboteur);
+ assert(sh && sh->region);
+ r = sh->region;
+
for (f = NULL, u = r->units; u; u = u->next) {
/* slight optimization to avoid dereferencing u->faction each time */
if (f != u->faction) {
@@ -413,76 +410,27 @@ static void sink_ship(region * r, ship * sh, unit * saboteur)
}
}
- /* figure out what a unit's chances of survival are: */
- if (!(r->terrain->flags & SEA_REGION)) {
- probability = CANAL_SWIMMER_CHANCE;
- }
- else {
- for (d = 0; d != MAXDIRECTIONS; ++d) {
- region *rn = rconnect(r, d);
- if (rn && !(rn->terrain->flags & SEA_REGION) && !move_blocked(NULL, r, rn)) {
- safety = rn;
- probability = OCEAN_SWIMMER_CHANCE;
- break;
- }
- }
- }
- for (ui = &r->units; *ui;) {
+ for (f = NULL, u = r->units; u; u = u->next) {
/* inform this faction about the sinking ship: */
- u = *ui;
- if (!(u->faction->flags & FFL_SELECT)) {
- fset(u->faction, FFL_SELECT);
- if (sink_msg == NULL) {
- sink_msg = msg_message("sink_msg", "ship region", sh, r);
- }
- add_message(&f->msgs, sink_msg);
- }
-
if (u->ship == sh) {
- int dead = 0;
- message *msg;
-
- /* if this fails, I misunderstood something: */
- for (i = 0; i != u->number; ++i)
- if (chance(probability))
- ++dead;
-
- if (dead != u->number) {
- /* she will live. but her items get stripped */
- if (dead > 0) {
- msg =
- msg_message("sink_lost_msg", "dead region unit", dead, safety, u);
- }
- else {
- msg = msg_message("sink_saved_msg", "region unit", safety, u);
- }
- leave_ship(u);
- if (r != safety) {
- setguard(u, false);
- }
- while (u->items) {
- i_remove(&u->items, u->items);
- }
- move_unit(u, safety, NULL);
- }
- else {
- msg = msg_message("sink_lost_msg", "dead region unit", dead, (region *)NULL, u);
- }
- add_message(&u->faction->msgs, msg);
- msg_release(msg);
- if (dead == u->number) {
- if (remove_unit(ui, u) == 0) {
- /* ui is already pointing at u->next */
- continue;
+ if (f != u->faction) {
+ f = u->faction;
+ if (!(f->flags & FFL_SELECT)) {
+ f->flags |= FFL_SELECT;
+ if (sink_msg == NULL) {
+ sink_msg = msg_message("sink_msg", "ship region", sh, r);
+ }
+ add_message(&f->msgs, sink_msg);
}
}
}
- ui = &u->next;
+ else if (f != NULL) {
+ break;
+ }
}
- if (sink_msg)
+ if (sink_msg) {
msg_release(sink_msg);
- /* finally, get rid of the ship */
- remove_ship(&sh->region->ships, sh);
+ }
}
int sabotage_cmd(unit * u, struct order *ord)
@@ -514,7 +462,9 @@ int sabotage_cmd(unit * u, struct order *ord)
effskill(u, SK_SPY, 0) - top_skill(u->region, u2->faction, sh, SK_PERCEPTION);
}
if (try_destruction(u, u2, sh, skdiff)) {
- sink_ship(u->region, sh, u);
+ sink_ship(sh);
+ /* finally, get rid of the ship */
+ remove_ship(&sh->region->ships, sh);
}
break;
default:
diff --git a/src/spy.h b/src/spy.h
index 665595ef4..b0bd7ae0e 100644
--- a/src/spy.h
+++ b/src/spy.h
@@ -27,6 +27,7 @@ extern "C" {
struct strlist;
struct order;
struct faction;
+ struct ship;
int setstealth_cmd(struct unit *u, struct order *ord);
int spy_cmd(struct unit *u, struct order *ord);
@@ -34,10 +35,7 @@ extern "C" {
void spy_message(int spy, const struct unit *u,
const struct unit *target);
void set_factionstealth(struct unit * u, struct faction * f);
-
-
-#define OCEAN_SWIMMER_CHANCE 0.1
-#define CANAL_SWIMMER_CHANCE 0.9
+ void sink_ship(struct ship * sh);
#ifdef __cplusplus
}
diff --git a/src/spy.test.c b/src/spy.test.c
index ed10c4b7e..0224698a0 100644
--- a/src/spy.test.c
+++ b/src/spy.test.c
@@ -55,10 +55,6 @@ static void setup_spy(spy_fixture *fix) {
"ship:ship", MT_NEW_END);
mt_create_va(mt_new("sink_msg", NULL),
"ship:ship", "region:region", MT_NEW_END);
- mt_create_va(mt_new("sink_lost_msg", NULL),
- "unit:unit", "region:region", "dead:int", MT_NEW_END);
- mt_create_va(mt_new("sink_saved_msg", NULL),
- "unit:unit", "region:region", MT_NEW_END);
if (fix) {
fix->r = test_create_region(0, 0, NULL);
@@ -112,6 +108,7 @@ static void test_sabotage_self(CuTest *tc) {
unit *u;
region *r;
order *ord;
+ message *msg;
test_setup();
setup_spy(NULL);
@@ -119,17 +116,49 @@ static void test_sabotage_self(CuTest *tc) {
assert(r);
u = test_create_unit(test_create_faction(NULL), r);
assert(u && u->faction && u->region == r);
- u->ship = test_create_ship(r, test_create_shiptype("boat"));
+ u->ship = test_create_ship(r, NULL);
assert(u->ship);
ord = create_order(K_SABOTAGE, u->faction->locale, "SCHIFF");
assert(ord);
CuAssertIntEquals(tc, 0, sabotage_cmd(u, ord));
- CuAssertPtrEquals(tc, 0, r->ships);
- CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "sink_msg"));
+ CuAssertPtrEquals(tc, NULL, r->ships);
+ CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "sink_msg"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(u->faction->msgs, "sink_msg", msg));
free_order(ord);
test_teardown();
}
+static void test_sink_ship(CuTest *tc) {
+ ship *sh;
+ unit *u1, *u2, *u3;
+ region *r;
+ message *msg;
+
+ test_setup();
+ setup_spy(NULL);
+ r = test_create_ocean(0, 0);
+ u1 = test_create_unit(test_create_faction(NULL), r);
+ u2 = test_create_unit(u1->faction, r);
+ u3 = test_create_unit(test_create_faction(NULL), r);
+ u1->ship = u2->ship = u3->ship = sh = test_create_ship(r, NULL);
+
+ sink_ship(sh);
+ CuAssertPtrEquals(tc, r, sh->region);
+ CuAssertPtrEquals(tc, sh, r->ships);
+ CuAssertPtrNotNull(tc, msg = test_find_messagetype(u1->faction->msgs, "sink_msg"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(u1->faction->msgs, "sink_msg", msg));
+ CuAssertPtrNotNull(tc, msg = test_find_messagetype(u3->faction->msgs, "sink_msg"));
+ CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(u3->faction->msgs, "sink_msg", msg));
+
+ remove_ship(&r->ships, sh);
+ CuAssertPtrEquals(tc, NULL, sh->region);
+ CuAssertPtrEquals(tc, NULL, r->ships);
+ CuAssertPtrEquals(tc, NULL, u1->ship);
+ CuAssertPtrEquals(tc, NULL, u2->ship);
+ CuAssertPtrEquals(tc, NULL, u3->ship);
+
+ test_teardown();
+}
static void test_sabotage_other_fail(CuTest *tc) {
unit *u, *u2;
@@ -145,7 +174,7 @@ static void test_sabotage_other_fail(CuTest *tc) {
u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r);
assert(u && u2);
- u2->ship = test_create_ship(r, test_create_shiptype("boat"));
+ u2->ship = test_create_ship(r, NULL);
assert(u2->ship);
u->ship = u2->ship;
ship_update_owner(u->ship);
@@ -167,7 +196,7 @@ static void test_setstealth_cmd(CuTest *tc) {
const struct locale *lang;
test_setup();
- u = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL));
+ u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
lang = u->faction->locale;
u->flags = UFL_ANON_FACTION | UFL_SIEGE;
u->thisorder = create_order(K_SETSTEALTH, lang, "%s %s",
@@ -191,7 +220,7 @@ static void test_setstealth_demon(CuTest *tc) {
test_setup();
lang = test_create_locale();
rc = test_create_race("demon");
- u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL));
+ u = test_create_unit(test_create_faction(rc), test_create_plain(0, 0));
rc = test_create_race("dwarf");
init_races(lang);
u->thisorder = create_order(K_SETSTEALTH, lang, racename(lang, u, rc));
@@ -208,7 +237,7 @@ static void test_setstealth_demon_bad(CuTest *tc) {
test_setup();
lang = test_create_locale();
rc = test_create_race("demon");
- u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL));
+ u = test_create_unit(test_create_faction(rc), test_create_plain(0, 0));
rc = test_create_race("smurf");
rc->flags &= ~RCF_PLAYABLE;
@@ -232,7 +261,7 @@ static void test_sabotage_other_success(CuTest *tc) {
u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r);
assert(u && u2);
- u2->ship = test_create_ship(r, test_create_shiptype("boat"));
+ u2->ship = test_create_ship(r, NULL);
assert(u2->ship);
u->ship = u2->ship;
ship_update_owner(u->ship);
@@ -251,6 +280,7 @@ CuSuite *get_spy_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_simple_spy_message);
SUITE_ADD_TEST(suite, test_all_spy_message);
+ SUITE_ADD_TEST(suite, test_sink_ship);
SUITE_ADD_TEST(suite, test_sabotage_self);
SUITE_ADD_TEST(suite, test_setstealth_cmd);
SUITE_ADD_TEST(suite, test_setstealth_demon);
diff --git a/src/tests.c b/src/tests.c
index 09f533a9f..4ee38d195 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -260,6 +260,21 @@ static void test_reset(void) {
}
}
+void test_create_calendar(void) {
+ config_set_int("game.start", 184);
+ months_per_year = 9;
+ month_season = malloc(sizeof(int) * months_per_year);
+ month_season[0] = SEASON_SUMMER;
+ month_season[1] = SEASON_AUTUMN;
+ month_season[2] = SEASON_AUTUMN;
+ month_season[3] = SEASON_WINTER;
+ month_season[4] = SEASON_WINTER;
+ month_season[5] = SEASON_WINTER;
+ month_season[6] = SEASON_SPRING;
+ month_season[7] = SEASON_SPRING;
+ month_season[8] = SEASON_SUMMER;
+}
+
void test_inject_messagetypes(void)
{
message_handle_missing(MESSAGE_MISSING_REPLACE);
diff --git a/src/tests.h b/src/tests.h
index dd8bed3c8..441db9e16 100644
--- a/src/tests.h
+++ b/src/tests.h
@@ -40,6 +40,7 @@ extern "C" {
struct log_t * test_log_start(int flags, struct strlist **slist);
void test_log_stop(struct log_t *log, struct strlist *slist);
+ void test_create_calendar(void);
struct locale * test_create_locale(void);
struct terrain_type * test_create_terrain(const char * name, int flags);
struct race *test_create_race(const char *name);