Merge branch 'develop' of github.com:ennorehling/eressea into develop

This commit is contained in:
Enno Rehling 2018-08-05 06:40:44 +02:00
commit 0eb030194c
45 changed files with 640 additions and 563 deletions

2
clibs

@ -1 +1 @@
Subproject commit 9b6e34959f77d7ca3a4ce3826cb487487f557441 Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871

View file

@ -868,21 +868,6 @@
</type> </type>
</message> </message>
<message name="sink_lost_msg" section="events">
<type>
<arg name="dead" type="int"/>
<arg name="region" type="region"/>
<arg name="unit" type="unit"/>
</type>
</message>
<message name="sink_saved_msg" section="events">
<type>
<arg name="region" type="region"/>
<arg name="unit" type="unit"/>
</type>
</message>
<message name="sink_msg" section="events"> <message name="sink_msg" section="events">
<type> <type>
<arg name="ship" type="ship"/> <arg name="ship" type="ship"/>

View file

@ -1773,7 +1773,7 @@ msgid "storm"
msgstr "\"Die $ship($ship) wird in $region($region) von Stürmen abgetrieben$if($sink,\" und sinkt\",\"\").\"" msgstr "\"Die $ship($ship) wird in $region($region) von Stürmen abgetrieben$if($sink,\" und sinkt\",\"\").\""
msgid "nr_insectwinter" 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" msgid "spellfail_nomonsters"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nicht auf Monster gezaubert werden.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Dieser Zauber kann nicht auf Monster gezaubert werden.\""
@ -2121,7 +2121,7 @@ msgid "missing_components"
msgstr "\"$unit($unit) hat nicht genügend Komponenten um $spell($spell) auf Stufe $int($level) zu zaubern.\"" msgstr "\"$unit($unit) hat nicht genügend Komponenten um $spell($spell) auf Stufe $int($level) zu zaubern.\""
msgid "seduce_effect_1" msgid "seduce_effect_1"
msgstr "\"$unit($unit) verfiel dem Glücksspiel und hat fast sein ganzes Hab und gut verspielt.\"" msgstr "\"$unit($unit) verfiel dem Glücksspiel und hat fast sein ganzes Hab und Gut verspielt.\""
msgid "xmastree_effect" msgid "xmastree_effect"
msgstr "\"In der Region erstrahlen des Nachts bunte Lichter, Gloeckchen klingeln und frohes Kindergelaechter klingt durch den Wald.\"" msgstr "\"In der Region erstrahlen des Nachts bunte Lichter, Gloeckchen klingeln und frohes Kindergelaechter klingt durch den Wald.\""
@ -2303,9 +2303,6 @@ msgstr "\"$unit($mage) erleidet durch den Tod seines Vertrauten einen Schock.\""
msgid "error269" msgid "error269"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Hier kann man nicht zaubern.\"" 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" msgid "race_noregroup"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $race($race,0) können nicht neu gruppiert werden.\"" 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" 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.\"" 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" msgid "error130"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Syntax: MAGIEGEBIET [1-5].\"" 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.\"" msgstr "\"Aus dem Vulkankrater von $region($region) steigt plötzlich Rauch.\""
msgid "nr_insectfall" 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" msgid "error296"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Hier werden niemals Bäume wachsen.\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Hier werden niemals Bäume wachsen.\""

View file

@ -2303,9 +2303,6 @@ msgstr "\"$unit($mage) receives a shock when his familiar dies.\""
msgid "error269" msgid "error269"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - You cannot cast spells here.\"" 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" msgid "race_noregroup"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - $race($race,0) cannot be regrouped.\"" 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" msgid "analyse_building_noage"
msgstr "\"$unit($mage) discovers that $building($building) is charmed with '$curse($curse)', which will last for centuries.\"" 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" msgid "error130"
msgstr "\"$unit($unit) in $region($region): '$order($command)' - Syntax: MAGIC SPHERE [1-5].\"" msgstr "\"$unit($unit) in $region($region): '$order($command)' - Syntax: MAGIC SPHERE [1-5].\""

View file

@ -2,7 +2,6 @@
local self = {} local self = {}
local function equip_first(u) local function equip_first(u)
equip_newunits(u)
name = 'seed_' .. u.race name = 'seed_' .. u.race
equip_unit(u, name, 255) equip_unit(u, name, 255)
end end

View file

@ -99,8 +99,6 @@ function self.update()
r:set_resource("tree", trees * 1.1) r:set_resource("tree", trees * 1.1)
msg:send_region(r) msg:send_region(r)
end end
if clear then
end
end end
end end
else else

View file

@ -338,45 +338,6 @@ function test_message()
return msg return msg
end 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() function test_renumber_ship()
local r = region.create(0, 0, "plain") local r = region.create(0, 0, "plain")
local f = create_faction('human') local f = create_faction('human')

View file

@ -30,6 +30,26 @@ function test_first_troll()
assert_equal(2, u:eff_skill('perception')) assert_equal(2, u:eff_skill('perception'))
end end
function test_first_human()
local f = faction.create('human')
local r = region.create(0, 0, "plain")
local u = unit.create(f, r, 1)
u:equip('first_unit')
assert_not_nil(u.building)
assert_equal('castle', u.building.type)
assert_equal(10, u.building.size)
end
function test_first_aquarian()
local f = faction.create('aquarian')
local r = region.create(0, 0, "plain")
local u = unit.create(f, r, 1)
u:equip('first_unit')
assert_not_nil(u.ship)
assert_equal('boat', u.ship.type)
assert_equal(1, u:get_skill('sailing'))
end
function test_seed_unit() function test_seed_unit()
local r = region.create(0, 0, "plain") local r = region.create(0, 0, "plain")
local f = faction.create('human') local f = faction.create('human')
@ -53,3 +73,4 @@ function test_seed_elf()
assert_equal('castle', u.building.type) assert_equal('castle', u.building.type)
assert_equal(10, u.building.size) assert_equal(10, u.building.size)
end end

View file

@ -517,3 +517,16 @@ function test_buy_sell()
assert_equal(4, u:get_item(item)) assert_equal(4, u:get_item(item))
assert_not_equal(0, u:get_item('money')) assert_not_equal(0, u:get_item('money'))
end 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

View file

@ -93,11 +93,11 @@ end
function test_lighthouse() function test_lighthouse()
eressea.free_game() eressea.free_game()
local r = region.create(0, 0, "mountain") local r = region.create(0, 0, "mountain")
local f = faction.create("human", "noreply@eressea.de", "de") local f = faction.create("human", "human@example.com")
region.create(1, 0, "mountain") region.create(1, 0, "mountain")
region.create(2, 0, "ocean") region.create(2, 0, "ocean")
region.create(0, 1, "firewall") region.create(0, 1, "firewall")
region.create(3, 0, "mountain") region.create(3, 0, "ocean")
region.create(4, 0, "plain") region.create(4, 0, "plain")
local u = unit.create(f, r, 1) local u = unit.create(f, r, 1)
local b = building.create(r, "lighthouse") local b = building.create(r, "lighthouse")
@ -110,7 +110,7 @@ function test_lighthouse()
init_reports() init_reports()
write_report(f) write_report(f)
assert_true(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)")) assert_false(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
assert_true(find_in_report(f, " %(2,0%) %(vom Turm erblickt%)")) assert_true(find_in_report(f, " %(2,0%) %(vom Turm erblickt%)"))
assert_true(find_in_report(f, " %(3,0%) %(vom Turm erblickt%)")) assert_true(find_in_report(f, " %(3,0%) %(vom Turm erblickt%)"))

View file

@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "move.h" #include "move.h"
#include "skill.h" #include "skill.h"
#include "study.h" #include "study.h"
#include "spy.h"
#include <spells/buildingcurse.h> #include <spells/buildingcurse.h>
#include <spells/regioncurse.h> #include <spells/regioncurse.h>
@ -2787,10 +2788,12 @@ static void aftermath(battle * b)
ship *sh = *sp; ship *sh = *sp;
freset(sh, SF_DAMAGED); freset(sh, SF_DAMAGED);
if (sh->damage >= sh->size * DAMAGE_SCALE) { if (sh->damage >= sh->size * DAMAGE_SCALE) {
sink_ship(sh);
remove_ship(sp, sh); remove_ship(sp, sh);
} }
if (*sp == sh) else {
sp = &sh->next; sp = &sh->next;
}
} }
} }

View file

@ -455,48 +455,6 @@ int fctr_handle(struct trigger *tp, void *data)
return 0; 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) static int tolua_unit_addnotice(lua_State * L)
{ {
unit *self = (unit *)tolua_tousertype(L, 1, 0); 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); faction *f = (faction *)tolua_tousertype(L, 1, 0);
region *r = (region *)tolua_tousertype(L, 2, 0); region *r = (region *)tolua_tousertype(L, 2, 0);
unit *u; unit *u;
const char *rcname = tolua_tostring(L, 4, NULL);
int num = (int)tolua_tonumber(L, 3, 1); int num = (int)tolua_tonumber(L, 3, 1);
const char *rcname = tolua_tostring(L, 4, NULL);
const race *rc; const race *rc;
assert(f && r); assert(f && r);
@ -1046,9 +1004,6 @@ void tolua_unit_open(lua_State * L)
tolua_function(L, TOLUA_CAST "add_notice", tolua_unit_addnotice); 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_variable(L, TOLUA_CAST "race_name", tolua_unit_get_racename,
tolua_unit_set_racename); tolua_unit_set_racename);
tolua_function(L, TOLUA_CAST "add_spell", tolua_unit_addspell); tolua_function(L, TOLUA_CAST "add_spell", tolua_unit_addspell);

View file

@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "chaos.h" #include "chaos.h"
#include "monsters.h" #include "monsters.h"
#include "move.h" #include "move.h"
#include "spy.h"
#include <kernel/building.h> #include <kernel/building.h>
#include <kernel/faction.h> #include <kernel/faction.h>
@ -144,19 +145,20 @@ static void chaos(region * r)
break; break;
} }
if (dir != MAXDIRECTIONS) { if (dir != MAXDIRECTIONS) {
ship *sh = r->ships; ship **slist = &r->ships;
unit **up; unit **up;
while (sh) { while (*slist) {
ship *nsh = sh->next; ship *sh = *slist;
double dmg =
config_get_flt("rules.ship.damage.atlantis", damage_ship(sh, 0.5);
0.50);
damage_ship(sh, dmg);
if (sh->damage >= sh->size * DAMAGE_SCALE) { 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;) { for (up = &r->units; *up;) {

View file

@ -22,8 +22,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" { extern "C" {
#endif #endif
struct region;
void chaos_update(void); void chaos_update(void);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -1529,6 +1529,7 @@ static void report_itemtype(FILE *F, faction *f, const item_type *itype) {
fprintf(F, "\"%s\"\n", translate(ch, LOC(f->locale, ch))); fprintf(F, "\"%s\"\n", translate(ch, LOC(f->locale, ch)));
m++; m++;
} }
assert(!m->rtype);
} }
} }

View file

@ -426,6 +426,59 @@ static int recruit_cost(const faction * f, const race * rc)
return -1; 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<EFBFBD>tzlich nur alleine! */
return msg_error(u, ord, 158);
}
return NULL;
}
static void recruit(unit * u, struct order *ord, econ_request ** recruitorders) static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
{ {
region *r = u->region; 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 faction *f = u->faction;
const struct race *rc = u_race(u); const struct race *rc = u_race(u);
int n; int n;
message *msg;
init_order_depr(ord); init_order_depr(ord);
n = getint(); n = getint();
@ -456,6 +510,7 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
} }
} }
} }
if (recruitcost < 0) { if (recruitcost < 0) {
rc = u_race(u); rc = u_race(u);
recruitcost = recruit_cost(f, rc); recruitcost = recruit_cost(f, rc);
@ -463,95 +518,46 @@ static void recruit(unit * u, struct order *ord, econ_request ** recruitorders)
recruitcost = INT_MAX; recruitcost = INT_MAX;
} }
} }
assert(rc);
u_setrace(u, rc);
/* this is a very special case because the recruiting unit may be empty if (recruitcost > 0) {
* at this point and we have to look at the creating unit instead. This int pool;
* 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) {
plane *pl = getplane(r); 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", "")); ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
return; return;
} }
if (get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, pool = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT, recruitcost * n);
recruitcost) < recruitcost) { if (pool < recruitcost) {
cmistake(u, ord, 142, MSG_EVENT); cmistake(u, ord, 142, MSG_EVENT);
return; 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<EFBFBD>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) { if (!n) {
cmistake(u, ord, 142, MSG_EVENT); cmistake(u, ord, 142, MSG_EVENT);
return; 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 = (econ_request *)calloc(1, sizeof(econ_request));
o->qty = n; o->qty = n;
o->unit = u; o->unit = u;
@ -1252,7 +1258,6 @@ static void create_potion(unit * u, const item_type * itype, int want)
/* something missing from the list of materials */ /* something missing from the list of materials */
ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder, ADDMSG(&u->faction->msgs, msg_materials_required(u, u->thisorder,
itype->construction, want)); itype->construction, want));
return;
break; break;
default: default:
i_change(&u->items, itype, built); i_change(&u->items, itype, built);

View file

@ -44,6 +44,7 @@ extern "C" {
#define MAXNEWBIES 5 #define MAXNEWBIES 5
struct unit; struct unit;
struct race;
struct region; struct region;
struct faction; struct faction;
struct order; struct order;
@ -94,6 +95,7 @@ extern "C" {
void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders); void steal_cmd(struct unit * u, struct order *ord, struct econ_request ** stealorders);
void expandstealing(struct region * r, 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); void add_recruits(struct unit * u, int number, int wanted);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -480,6 +480,35 @@ static void test_recruit(CuTest *tc) {
test_teardown(); 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) static void test_income(CuTest *tc)
{ {
race *rc; race *rc;
@ -764,6 +793,7 @@ CuSuite *get_economy_suite(void)
SUITE_ADD_TEST(suite, test_trade_insect); SUITE_ADD_TEST(suite, test_trade_insect);
SUITE_ADD_TEST(suite, test_maintain_buildings); SUITE_ADD_TEST(suite, test_maintain_buildings);
SUITE_ADD_TEST(suite, test_recruit); SUITE_ADD_TEST(suite, test_recruit);
SUITE_ADD_TEST(suite, test_recruit_insect);
SUITE_ADD_TEST(suite, test_loot); SUITE_ADD_TEST(suite, test_loot);
SUITE_ADD_TEST(suite, test_expand_production); SUITE_ADD_TEST(suite, test_expand_production);
return suite; return suite;

View file

@ -625,6 +625,7 @@ static void handle_requirement(parseinfo *pi, const XML_Char *el, const XML_Char
assert(nreqs < MAX_REQUIREMENTS); assert(nreqs < MAX_REQUIREMENTS);
req = reqs + nreqs; req = reqs + nreqs;
req->number = 1;
for (i = 0; attr[i]; i += 2) { for (i = 0; attr[i]; i += 2) {
if (xml_strcmp(attr[i], "type") == 0) { if (xml_strcmp(attr[i], "type") == 0) {
req->rtype = rt_get_or_create(attr[i + 1]); req->rtype = rt_get_or_create(attr[i + 1]);

View file

@ -530,16 +530,31 @@ static void statusline(WINDOW * win, const char *str)
} }
static void reset_region(region *r) { static void reset_region(region *r) {
unit **up = &r->units;
bool players = false;
r->flags = 0; r->flags = 0;
a_removeall(&r->attribs, NULL); a_removeall(&r->attribs, NULL);
while (r->units) { while (*up) {
remove_unit(&r->units, r->units); unit *u = *up;
if (is_monsters(u->faction)) {
remove_unit(up, u);
}
else {
players = true;
up = &u->next;
}
} }
while (r->ships) { if (!players) {
remove_ship(&r->ships, r->ships); while (r->ships) {
} remove_ship(&r->ships, r->ships);
while (r->buildings) { }
remove_building(&r->buildings, r->buildings); while (r->buildings) {
remove_building(&r->buildings, r->buildings);
}
if (r->land) {
init_region(r);
}
} }
} }

View file

@ -895,7 +895,9 @@ build_building(unit * u, const building_type * btype, int id, int want, order *
} }
fset(b, BLD_EXPANDED); fset(b, BLD_EXPANDED);
update_lighthouse(b); if (is_lighthouse(btype)) {
update_lighthouse(b);
}
return built; return built;
} }

View file

@ -377,7 +377,9 @@ building *new_building(const struct building_type * btype, region * r,
bptr = &(*bptr)->next; bptr = &(*bptr)->next;
*bptr = b; *bptr = b;
update_lighthouse(b); if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
bname = LOC(lang, btype->_name); bname = LOC(lang, btype->_name);
if (!bname) { if (!bname) {
bname = LOC(lang, parameters[P_GEBAEUDE]); bname = LOC(lang, parameters[P_GEBAEUDE]);
@ -399,6 +401,7 @@ static building *deleted_buildings;
void remove_building(building ** blist, building * b) void remove_building(building ** blist, building * b)
{ {
unit *u; unit *u;
region *r = b->region;
static const struct building_type *bt_caravan, *bt_dam, *bt_tunnel; static const struct building_type *bt_caravan, *bt_dam, *bt_tunnel;
static int btypes; static int btypes;
@ -410,18 +413,19 @@ void remove_building(building ** blist, building * b)
bt_tunnel = bt_find("tunnel"); bt_tunnel = bt_find("tunnel");
} }
handle_event(b->attribs, "destroy", b); handle_event(b->attribs, "destroy", b);
for (u = b->region->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
if (u->building == b) leave(u, true); if (u->building == b) leave(u, true);
} }
if (is_lighthouse(b->type)) {
remove_lighthouse(b);
}
b->size = 0; b->size = 0;
update_lighthouse(b);
bunhash(b); bunhash(b);
/* Falls Karawanserei, Damm oder Tunnel einst<73>rzen, wird die schon /* Falls Karawanserei, Damm oder Tunnel einst<73>rzen, wird die schon
* gebaute Strasse zur Haelfte vernichtet */ * gebaute Strasse zur Haelfte vernichtet */
if (b->type == bt_caravan || b->type == bt_dam || b->type == bt_tunnel) { if (b->type == bt_caravan || b->type == bt_dam || b->type == bt_tunnel) {
region *r = b->region;
int d; int d;
for (d = 0; d != MAXDIRECTIONS; ++d) { for (d = 0; d != MAXDIRECTIONS; ++d) {
direction_t dir = (direction_t)d; direction_t dir = (direction_t)d;

View file

@ -1048,6 +1048,43 @@ int fix_demand(region * rd) {
return -1; return -1;
} }
void init_region(region *r)
{
static int changed;
static const terrain_type *t_plain;
const terrain_type * terrain = r->terrain;
int horses = 0, trees = 0;
if (terrain_changed(&changed)) {
t_plain = get_terrain(terrainnames[T_PLAIN]);
}
if (terrain->size>0) {
horses = rng_int() % (terrain->size / 50);
trees = terrain->size * (30 + rng_int() % 40) / 1000;
}
if (t_plain && terrain == t_plain) {
rsethorses(r, horses);
if (chance(0.4)) {
rsettrees(r, 2, trees);
}
}
else if (trees>0 && chance(0.2)) {
rsettrees(r, 2, trees);
}
else {
rsettrees(r, 2, 0);
}
rsettrees(r, 1, rtrees(r, 2) / 4);
rsettrees(r, 0, rtrees(r, 2) / 8);
if (!fval(r, RF_CHAOTIC)) {
int peasants;
peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100;
rsetpeasants(r, MAX(100, peasants));
rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL,
INT_MAX) + 1) + rng_int() % 5));
}
}
void terraform_region(region * r, const terrain_type * terrain) void terraform_region(region * r, const terrain_type * terrain)
{ {
/* Resourcen, die nicht mehr vorkommen können, löschen */ /* Resourcen, die nicht mehr vorkommen können, löschen */
@ -1195,40 +1232,8 @@ void terraform_region(region * r, const terrain_type * terrain)
else else
freset(r, RF_MALLORN); freset(r, RF_MALLORN);
} }
} if (oldterrain == NULL || terrain->size != oldterrain->size) {
init_region(r);
if (oldterrain == NULL || terrain->size != oldterrain->size) {
static int changed;
static const terrain_type *t_plain;
int horses = 0, trees = 0;
if (terrain_changed(&changed)) {
t_plain = get_terrain(terrainnames[T_PLAIN]);
}
if (terrain->size>0) {
horses = rng_int() % (terrain->size / 50);
trees = terrain->size * (30 + rng_int() % 40) / 1000;
}
if (t_plain && terrain == t_plain) {
rsethorses(r, horses);
if (chance(0.4)) {
rsettrees(r, 2, trees);
}
}
else if (trees>0 && chance(0.2)) {
rsettrees(r, 2, trees);
}
else {
rsettrees(r, 2, 0);
}
rsettrees(r, 1, rtrees(r, 2) / 4);
rsettrees(r, 0, rtrees(r, 2) / 8);
if (!fval(r, RF_CHAOTIC)) {
int peasants;
peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100;
rsetpeasants(r, MAX(100, peasants));
rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL,
INT_MAX) + 1) + rng_int() % 5));
} }
} }
} }

View file

@ -228,6 +228,7 @@ extern "C" {
struct region *new_region(int x, int y, struct plane *pl, int uid); struct region *new_region(int x, int y, struct plane *pl, int uid);
void remove_region(region ** rlist, region * r); void remove_region(region ** rlist, region * r);
void terraform_region(struct region *r, const struct terrain_type *terrain); void terraform_region(struct region *r, const struct terrain_type *terrain);
void init_region(struct region *r);
bool pnormalize(int *x, int *y, const struct plane *pl); bool pnormalize(int *x, int *y, const struct plane *pl);
extern const int delta_x[MAXDIRECTIONS]; extern const int delta_x[MAXDIRECTIONS];

View file

@ -1493,7 +1493,9 @@ int read_game(gamedata *data)
if (r->flags & RF_LIGHTHOUSE) { if (r->flags & RF_LIGHTHOUSE) {
building *b; building *b;
for (b = r->buildings; b; b = b->next) { for (b = r->buildings; b; b = b->next) {
update_lighthouse(b); if (is_lighthouse(b->type)) {
update_lighthouse(b);
}
} }
} }
} }

View file

@ -124,9 +124,9 @@ extern "C" {
extern void write_ship_reference(const struct ship *sh, extern void write_ship_reference(const struct ship *sh,
struct storage *store); struct storage *store);
extern void remove_ship(struct ship **slist, struct ship *s); void remove_ship(struct ship **slist, struct ship *s);
extern void free_ship(struct ship *s); void free_ship(struct ship *s);
extern void free_ships(void); void free_ships(void);
const char *ship_getname(const struct ship *sh); const char *ship_getname(const struct ship *sh);
void ship_setname(struct ship *self, const char *name); void ship_setname(struct ship *self, const char *name);

View file

@ -2058,17 +2058,6 @@ int mail_cmd(unit * u, struct order *ord)
break; break;
} }
else { 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); mailunit(r, u, n, ord, s);
} }
return 0; return 0;
@ -2590,6 +2579,7 @@ void sinkships(struct region * r)
} }
} }
if (sh->damage >= sh->size * DAMAGE_SCALE) { if (sh->damage >= sh->size * DAMAGE_SCALE) {
sink_ship(sh);
remove_ship(shp, sh); remove_ship(shp, sh);
} }
if (*shp == sh) if (*shp == sh)

View file

@ -19,72 +19,94 @@ attrib_type at_lighthouse = {
/* Rest ist NULL; tempor<6F>res, nicht alterndes Attribut */ /* Rest ist NULL; tempor<6F>res, nicht alterndes Attribut */
}; };
bool is_lighthouse(const building_type *btype)
{
return is_building_type(btype, "lighthouse");
}
/* update_lighthouse: call this function whenever the size of a lighthouse changes /* update_lighthouse: call this function whenever the size of a lighthouse changes
* it adds temporary markers to the surrounding regions. * it adds temporary markers to the surrounding regions.
* The existence of markers says nothing about the quality of the observer in * The existence of markers says nothing about the quality of the observer in
* the lighthouse, for this may change more frequently. * the lighthouse, since this may change more frequently.
*/ */
void update_lighthouse(building * lh) void update_lighthouse(building * lh)
{ {
if (is_building_type(lh->type, "lighthouse")) { region *r = lh->region;
region *r = lh->region; assert(is_lighthouse(lh->type));
r->flags |= RF_LIGHTHOUSE; r->flags |= RF_LIGHTHOUSE;
if (lh->size > 0) { if (lh->size >= 10) {
int d = (int)log10(lh->size) + 1; int d = lighthouse_range(lh);
int x; int x;
for (x = -d; x <= d; ++x) { for (x = -d; x <= d; ++x) {
int y; int y;
for (y = -d; y <= d; ++y) { for (y = -d; y <= d; ++y) {
attrib *a; attrib *a;
region *r2; region *r2;
int px = r->x + x, py = r->y + y; int px = r->x + x, py = r->y + y;
pnormalize(&px, &py, rplane(r)); pnormalize(&px, &py, rplane(r));
r2 = findregion(px, py); r2 = findregion(px, py);
if (!r2 || !fval(r2->terrain, SEA_REGION)) if (!r2 || !fval(r2->terrain, SEA_REGION))
continue; continue;
if (distance(r, r2) > d) if (distance(r, r2) > d)
continue; continue;
a = a_find(r2->attribs, &at_lighthouse); a = a_find(r2->attribs, &at_lighthouse);
while (a && a->type == &at_lighthouse) { while (a && a->type == &at_lighthouse) {
building *b = (building *)a->data.v; building *b = (building *)a->data.v;
if (b == lh) if (b == lh)
break; break;
a = a->next; a = a->next;
} }
if (!a) { if (!a) {
a = a_add(&r2->attribs, a_new(&at_lighthouse)); a = a_add(&r2->attribs, a_new(&at_lighthouse));
a->data.v = (void *)lh; a->data.v = (void *)lh;
}
} }
} }
} }
} }
} }
int lighthouse_range(const building * b, const faction * f, const unit *u) void remove_lighthouse(const building *lh) {
{ building *b;
if (fval(b, BLD_MAINTAINED) && b->size >= 10) { region * r = lh->region;
int maxd = (int)log10(b->size) + 1;
if (u && skill_enabled(SK_PERCEPTION)) { r->flags &= ~RF_LIGHTHOUSE;
for (b = r->buildings; b; b = b->next) {
if (b != lh && is_lighthouse(b->type)) {
update_lighthouse(b);
}
}
}
int lighthouse_range(const building * b)
{
if (b->size >= 10 && (b->flags & BLD_MAINTAINED)) {
return (int)log10(b->size) + 1;
}
return 0;
}
int lighthouse_view_distance(const building * b, const unit *u)
{
if (b->size >= 10 && (b->flags & BLD_MAINTAINED)) {
int maxd = lighthouse_range(b);
if (maxd > 0 && u && skill_enabled(SK_PERCEPTION)) {
int sk = effskill(u, SK_PERCEPTION, 0) / 3; int sk = effskill(u, SK_PERCEPTION, 0) / 3;
assert(u->building == b); assert(u->building == b);
assert(u->faction == f);
if (maxd > sk) maxd = sk; if (maxd > sk) maxd = sk;
} }
/* E3A rule: no perception req'd */
return maxd; return maxd;
} }
return 0; return 0;
} }
bool check_leuchtturm(region * r, faction * f) bool lighthouse_guarded(const region * r)
{ {
attrib *a; attrib *a;
if (!fval(r->terrain, SEA_REGION)) { if (!r->attribs || !(r->terrain->flags & SEA_REGION)) {
return false; return false;
} }
for (a = a_find(r->attribs, &at_lighthouse); a && a->type == &at_lighthouse; for (a = a_find(r->attribs, &at_lighthouse); a && a->type == &at_lighthouse;
@ -92,37 +114,11 @@ bool check_leuchtturm(region * r, faction * f)
building *b = (building *)a->data.v; building *b = (building *)a->data.v;
assert(is_building_type(b->type, "lighthouse")); assert(is_building_type(b->type, "lighthouse"));
if (fval(b, BLD_MAINTAINED) && b->size >= 10) { if ((b->flags & BLD_MAINTAINED) && b->size >= 10) {
int maxd = (int)log10(b->size) + 1; int maxd = (int)log10(b->size) + 1;
int d = distance(r, b->region);
if (skill_enabled(SK_PERCEPTION) && f) { assert(maxd >= d);
region *r2 = b->region; return true;
unit *u;
int c = 0;
int d = 0;
for (u = r2->units; u; u = u->next) {
if (u->building == b) {
c += u->number;
if (c > buildingcapacity(b))
break;
if (u->faction == f) {
if (!d)
d = distance(r, r2);
if (maxd < d)
break;
if (effskill(u, SK_PERCEPTION, 0) >= d * 3)
return true;
}
}
else if (c)
break; /* first unit that's no longer in the house ends the search */
}
}
else {
/* E3A rule: no perception req'd */
return true;
}
} }
} }

View file

@ -29,15 +29,18 @@ extern "C" {
struct faction; struct faction;
struct region; struct region;
struct building; struct building;
struct building_type;
struct unit; struct unit;
struct attrib; struct attrib;
extern struct attrib_type at_lighthouse; extern struct attrib_type at_lighthouse;
/* leuchtturm */ /* leuchtturm */
bool check_leuchtturm(struct region *r, struct faction *f); bool is_lighthouse(const struct building_type *btype);
bool lighthouse_guarded(const struct region *r);
void update_lighthouse(struct building *b); void update_lighthouse(struct building *b);
int lighthouse_range(const struct building *b, const struct faction *f, void remove_lighthouse(const struct building *lh);
const struct unit *u); int lighthouse_range(const struct building *b);
int lighthouse_view_distance(const struct building *b, const struct unit *u);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -26,35 +26,39 @@ static void test_lighthouse_range(CuTest * tc)
u1 = test_create_unit(test_create_faction(NULL), r); u1 = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r);
b = test_create_building(r, test_create_buildingtype("lighthouse")); b = test_create_building(r, test_create_buildingtype("lighthouse"));
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL)); CuAssertIntEquals(tc, 0, lighthouse_range(b));
CuAssertIntEquals(tc, 0, lighthouse_range(b, u1->faction, NULL)); b->size = 9;
CuAssertIntEquals(tc, 0, lighthouse_range(b));
b->size = 10; b->size = 10;
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL)); CuAssertIntEquals(tc, 0, lighthouse_range(b));
b->flags |= BLD_MAINTAINED;
CuAssertIntEquals(tc, 2, lighthouse_range(b));
u1->building = b; u1->building = b;
u2->building = b; u2->building = b;
u1->number = 10; u1->number = 10;
set_level(u1, SK_PERCEPTION, 3); set_level(u1, SK_PERCEPTION, 3);
set_level(u2, SK_PERCEPTION, 3); set_level(u2, SK_PERCEPTION, 3);
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL));
b->flags |= BLD_MAINTAINED; CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u1));
CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction, u1));
set_level(u1, SK_PERCEPTION, 6); set_level(u1, SK_PERCEPTION, 6);
CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction, u1)); CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u2));
/* lighthouse_range does not check inside_building */ CuAssertIntEquals(tc, 2, lighthouse_view_distance(b, u1));
CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2));
b->size = 100; b->size = 100;
update_lighthouse(b); update_lighthouse(b);
CuAssertIntEquals(tc, 3, lighthouse_range(b, NULL, NULL)); CuAssertIntEquals(tc, 3, lighthouse_range(b));
CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction, u1)); CuAssertIntEquals(tc, 2, lighthouse_view_distance(b, u1));
set_level(u1, SK_PERCEPTION, 9); set_level(u1, SK_PERCEPTION, 9);
CuAssertIntEquals(tc, 3, lighthouse_range(b, u1->faction, u1)); CuAssertIntEquals(tc, 3, lighthouse_view_distance(b, u1));
CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2)); CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u2));
b->size = 99;
CuAssertIntEquals(tc, 2, lighthouse_view_distance(b, u1));
test_teardown(); test_teardown();
} }
static void test_lighthouse_update(CuTest * tc) static void test_lighthouse_update(CuTest * tc)
{ {
region *r1, *r2, *r3; region *r1, *r2, *r3, *r4;
building *b; building *b;
const struct terrain_type *t_ocean, *t_plain; const struct terrain_type *t_ocean, *t_plain;
@ -64,20 +68,23 @@ static void test_lighthouse_update(CuTest * tc)
r1 = test_create_region(0, 0, t_plain); r1 = test_create_region(0, 0, t_plain);
r2 = test_create_region(1, 0, t_ocean); r2 = test_create_region(1, 0, t_ocean);
r3 = test_create_region(2, 0, t_ocean); r3 = test_create_region(2, 0, t_ocean);
r4 = test_create_region(0, 1, t_plain);
b = test_create_building(r1, test_create_buildingtype("lighthouse")); b = test_create_building(r1, test_create_buildingtype("lighthouse"));
b->flags |= BLD_MAINTAINED;
CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE); CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE);
CuAssertPtrEquals(tc, NULL, r1->attribs); CuAssertPtrEquals(tc, NULL, r1->attribs);
CuAssertPtrEquals(tc, NULL, r2->attribs); CuAssertPtrEquals(tc, NULL, r2->attribs);
CuAssertPtrEquals(tc, NULL, r3->attribs); CuAssertPtrEquals(tc, NULL, r3->attribs);
CuAssertPtrEquals(tc, NULL, r4->attribs);
r1->flags = 0; r1->flags = 0;
b->size = 1; b->size = 9; /* minimum size for any effect is 10 */
update_lighthouse(b); update_lighthouse(b);
CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE); CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE);
CuAssertPtrNotNull(tc, r2->attribs);
CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r2->attribs->type);
CuAssertPtrEquals(tc, NULL, r1->attribs); CuAssertPtrEquals(tc, NULL, r1->attribs);
CuAssertPtrEquals(tc, NULL, r2->attribs);
CuAssertPtrEquals(tc, NULL, r3->attribs); CuAssertPtrEquals(tc, NULL, r3->attribs);
CuAssertPtrEquals(tc, NULL, r4->attribs);
a_removeall(&r2->attribs, NULL); a_removeall(&r2->attribs, NULL);
r1->flags = 0; r1->flags = 0;
@ -88,6 +95,39 @@ static void test_lighthouse_update(CuTest * tc)
CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r2->attribs->type); CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r2->attribs->type);
CuAssertPtrNotNull(tc, r3->attribs); CuAssertPtrNotNull(tc, r3->attribs);
CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r3->attribs->type); CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r3->attribs->type);
CuAssertPtrEquals(tc, NULL, r4->attribs);
test_teardown();
}
static void test_lighthouse_guard(CuTest * tc) {
region *r1, *r2, *r3, *r4;
building *b;
const struct terrain_type *t_ocean, *t_plain;
test_setup();
t_ocean = test_create_terrain("ocean", SEA_REGION);
t_plain = test_create_terrain("plain", LAND_REGION);
r1 = test_create_region(0, 0, t_plain);
r2 = test_create_region(1, 0, t_ocean);
r3 = test_create_region(2, 0, t_ocean);
r4 = test_create_region(0, 1, t_plain);
b = test_create_building(r1, test_create_buildingtype("lighthouse"));
b->flags |= BLD_MAINTAINED;
b->size = 10;
CuAssertIntEquals(tc, 2, lighthouse_range(b));
update_lighthouse(b);
CuAssertIntEquals(tc, RF_LIGHTHOUSE, r1->flags&RF_LIGHTHOUSE);
CuAssertPtrEquals(tc, NULL, r1->attribs);
CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r2->attribs->type);
CuAssertPtrEquals(tc, (void *)&at_lighthouse, (void *)r3->attribs->type);
CuAssertPtrEquals(tc, NULL, r4->attribs);
CuAssertIntEquals(tc, false, lighthouse_guarded(r1));
CuAssertIntEquals(tc, true, lighthouse_guarded(r2));
CuAssertIntEquals(tc, true, lighthouse_guarded(r3));
CuAssertIntEquals(tc, false, lighthouse_guarded(r4));
b->size = 1; /* size can go down in destroy_cmd */
CuAssertIntEquals(tc, false, lighthouse_guarded(r2));
CuAssertIntEquals(tc, false, lighthouse_guarded(r3));
test_teardown(); test_teardown();
} }
@ -96,5 +136,6 @@ CuSuite *get_lighthouse_suite(void)
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_lighthouse_range); SUITE_ADD_TEST(suite, test_lighthouse_range);
SUITE_ADD_TEST(suite, test_lighthouse_update); SUITE_ADD_TEST(suite, test_lighthouse_update);
SUITE_ADD_TEST(suite, test_lighthouse_guard);
return suite; return suite;
} }

View file

@ -47,6 +47,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h" #include "laws.h"
#include "reports.h" #include "reports.h"
#include "study.h" #include "study.h"
#include "spy.h"
#include "alchemy.h" #include "alchemy.h"
#include "travelthru.h" #include "travelthru.h"
#include "vortex.h" #include "vortex.h"
@ -541,6 +542,7 @@ static ship *do_maelstrom(region * r, unit * u)
if (sh->damage >= sh->size * DAMAGE_SCALE) { if (sh->damage >= sh->size * DAMAGE_SCALE) {
ADDMSG(&u->faction->msgs, msg_message("entermaelstrom", ADDMSG(&u->faction->msgs, msg_message("entermaelstrom",
"region ship damage sink", r, sh, damage, 1)); "region ship damage sink", r, sh, damage, 1));
sink_ship(sh);
remove_ship(&sh->region->ships, sh); remove_ship(&sh->region->ships, sh);
return NULL; return NULL;
} }
@ -882,12 +884,14 @@ static void drifting_ships(region * r)
} }
if (sh->damage >= sh->size * DAMAGE_SCALE) { if (sh->damage >= sh->size * DAMAGE_SCALE) {
msg_to_ship_inmates(sh, &firstu, &lastu, msg_message("shipsink", "ship", sh)); 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; shp = &sh->next;
}
} }
} }
} }
@ -1779,7 +1783,7 @@ static void sail(unit * u, order * ord, region_list ** routep, bool drifting)
/* storms should be the first thing we do. */ /* storms should be the first thing we do. */
stormchance = stormyness / shipspeed(sh, u); stormchance = stormyness / shipspeed(sh, u);
if (check_leuchtturm(next_point, NULL)) { if (lighthouse_guarded(next_point)) {
if (lighthouse_div > 0) { if (lighthouse_div > 0) {
stormchance /= lighthouse_div; stormchance /= lighthouse_div;
} }
@ -1867,7 +1871,7 @@ static void sail(unit * u, order * ord, region_list ** routep, bool drifting)
if (reason == SA_NO_INSECT) { if (reason == SA_NO_INSECT) {
ADDMSG(&f->msgs, msg_message("detectforbidden", "unit region", u, sh->region)); ADDMSG(&f->msgs, msg_message("detectforbidden", "unit region", u, sh->region));
} }
else if (check_leuchtturm(current_point, NULL)) { else if (lighthouse_guarded(current_point)) {
ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point)); ADDMSG(&f->msgs, msg_message("sailnolandingstorm", "ship region", sh, next_point));
} }
else { else {
@ -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->damage >= sh->size * DAMAGE_SCALE) {
if (sh->region) { if (sh->region) {
ADDMSG(&f->msgs, msg_message("shipsink", "ship", sh)); ADDMSG(&f->msgs, msg_message("shipsink", "ship", sh));
sink_ship(sh);
remove_ship(&sh->region->ships, sh); remove_ship(&sh->region->ships, sh);
} }
sh = NULL; sh = NULL;

View file

@ -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)); u_set_ship(fix->u, fix->sh = test_create_ship(fix->u->region, fix->st_boat));
assert(fix->sh); 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), mt_create_va(mt_new("ship_drift", NULL),
"ship:ship", "dir:int", MT_NEW_END); "ship:ship", "dir:int", MT_NEW_END);
mt_create_va(mt_new("shipsink", NULL), mt_create_va(mt_new("shipsink", NULL),

View file

@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "economy.h" #include "economy.h"
#include "monsters.h" #include "monsters.h"
#include "move.h" #include "move.h"
#include "spy.h"
#include "study.h" #include "study.h"
#include "volcano.h" #include "volcano.h"
@ -295,6 +296,7 @@ static void move_iceberg(region * r)
ADDMSG(&u->faction->msgs, msg_message("overrun_by_iceberg_des", ADDMSG(&u->faction->msgs, msg_message("overrun_by_iceberg_des",
"ship", sh)); "ship", sh));
} }
sink_ship(sh);
remove_ship(&sh->region->ships, sh); remove_ship(&sh->region->ships, sh);
} }
else if (u != NULL) { else if (u != NULL) {
@ -421,6 +423,7 @@ static void godcurse(void)
ADDMSG(&uo->faction->msgs, ADDMSG(&uo->faction->msgs,
msg_message("godcurse_destroy_ship", "ship", sh)); msg_message("godcurse_destroy_ship", "ship", sh));
} }
sink_ship(sh);
remove_ship(&sh->region->ships, sh); remove_ship(&sh->region->ships, sh);
} }
sh = shn; sh = shn;

View file

@ -2191,6 +2191,7 @@ report_plaintext(const char *filename, report_context * ctx,
if (wrptr(&bufp, &size, bytes) != 0) if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER(); WARN_STATIC_BUFFER();
} }
assert(!rm->rtype);
} }
*bufp = 0; *bufp = 0;
centre(out, buf, true); centre(out, buf, true);

View file

@ -1353,6 +1353,13 @@ static void add_seen_nb(faction *f, region *r, seen_mode mode) {
update_interval(f, last); update_interval(f, last);
} }
static void add_seen_lighthouse(region *r, faction *f)
{
if (r->terrain->flags & SEA_REGION) {
add_seen_nb(f, r, seen_lighthouse);
}
}
/** mark all regions seen by the lighthouse. /** mark all regions seen by the lighthouse.
*/ */
static void prepare_lighthouse_ql(faction *f, selist *rlist) { static void prepare_lighthouse_ql(faction *f, selist *rlist) {
@ -1361,9 +1368,7 @@ static void prepare_lighthouse_ql(faction *f, selist *rlist) {
for (ql = rlist, qi = 0; ql; selist_advance(&ql, &qi, 1)) { for (ql = rlist, qi = 0; ql; selist_advance(&ql, &qi, 1)) {
region *rl = (region *)selist_get(ql, qi); region *rl = (region *)selist_get(ql, qi);
if (!fval(rl->terrain, FORBIDDEN_REGION)) { add_seen_lighthouse(rl, f);
add_seen_nb(f, rl, seen_lighthouse);
}
} }
} }
@ -1382,9 +1387,7 @@ static void prepare_lighthouse(faction *f, region *r, int range)
assert(n > 0 && n <= 64); assert(n > 0 && n <= 64);
for (i = 0; i != n; ++i) { for (i = 0; i != n; ++i) {
region *rl = result[i]; region *rl = result[i];
if (!fval(rl->terrain, FORBIDDEN_REGION)) { add_seen_lighthouse(rl, f);
add_seen_nb(f, rl, seen_lighthouse);
}
} }
} }
} }
@ -1514,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()) { if (f->age < NewbieImmunity()) {
ADDMSG(&f->msgs, msg_message("newbieimmunity", "turns", ADDMSG(&f->msgs, msg_message("newbieimmunity", "turns",
NewbieImmunity() - f->age)); NewbieImmunity() - f->age));
} }
if (date) { if (f->race == get_race(RC_INSECT)) {
if (f->race == get_race(RC_INSECT)) { gamedate date;
if (date->season == 0) { get_gamedate(now + 1, &date);
ADDMSG(&f->msgs, msg_message("nr_insectwinter", ""));
} if (date.season == SEASON_WINTER) {
else { ADDMSG(&f->msgs, msg_message("nr_insectwinter", ""));
gamedate next; }
get_gamedate(date->turn + 1, &next); else if (date.season == SEASON_AUTUMN) {
if (next.season == 0) { if (get_gamedate(now + 2 + 2, &date)->season == SEASON_WINTER) {
ADDMSG(&f->msgs, msg_message("nr_insectfall", "")); ADDMSG(&f->msgs, msg_message("nr_insectfall", ""));
}
} }
} }
} }
@ -1549,11 +1551,9 @@ void prepare_report(report_context *ctx, faction *f)
static bool rule_region_owners; static bool rule_region_owners;
static bool rule_lighthouse_units; static bool rule_lighthouse_units;
const struct building_type *bt_lighthouse = bt_find("lighthouse"); const struct building_type *bt_lighthouse = bt_find("lighthouse");
gamedate now;
/* Insekten-Winter-Warnung */ /* Insekten-Winter-Warnung */
get_gamedate(turn, &now); report_warnings(f, turn);
report_warnings(f, &now);
if (bt_lighthouse && config_changed(&config)) { if (bt_lighthouse && config_changed(&config)) {
rule_region_owners = config_token("rules.region_owner_pay_building", bt_lighthouse->_name); rule_region_owners = config_token("rules.region_owner_pay_building", bt_lighthouse->_name);
@ -1592,8 +1592,8 @@ void prepare_report(report_context *ctx, faction *f)
if (rule_region_owners && f == region_get_owner(r)) { if (rule_region_owners && f == region_get_owner(r)) {
for (b = rbuildings(r); b; b = b->next) { for (b = rbuildings(r); b; b = b->next) {
if (b && b->type == bt_lighthouse) { if (b && b->type == bt_lighthouse) {
/* region owners get maximm range */ /* region owners get maximum range */
int lhr = lighthouse_range(b, NULL, NULL); int lhr = lighthouse_view_distance(b, NULL);
if (lhr > range) range = lhr; if (lhr > range) range = lhr;
} }
} }
@ -1610,7 +1610,7 @@ void prepare_report(report_context *ctx, faction *f)
*/ */
if (!fval(r, RF_LIGHTHOUSE)) { if (!fval(r, RF_LIGHTHOUSE)) {
/* it's enough to add the region once, and if there are /* it's enough to add the region once, and if there are
* no lighthouses, there is no need to look at more units */ * no lighthouses here, there is no need to look at more units */
break; break;
} }
} }
@ -1630,10 +1630,10 @@ void prepare_report(report_context *ctx, faction *f)
/* unit is one of ours, and inside the current lighthouse */ /* unit is one of ours, and inside the current lighthouse */
if (br == 0) { if (br == 0) {
/* lazy-calculate the range */ /* lazy-calculate the range */
br = lighthouse_range(u->building, f, u); br = lighthouse_view_distance(b, u);
} if (br > range) {
if (br > range) { range = br;
range = br; }
} }
} }
} }
@ -2446,8 +2446,10 @@ bool visible_unit(const unit *u, const faction *f, int stealthmod, seen_mode mod
return true; return true;
} }
else { else {
if (stealthmod > INT_MIN && (mode == seen_lighthouse || mode >= seen_unit)) { if (stealthmod > INT_MIN && mode >= seen_lighthouse) {
return cansee(f, u->region, u, stealthmod); if (mode != seen_travel || u->building || u->ship || is_guard(u)) {
return cansee(f, u->region, u, stealthmod);
}
} }
} }
return false; return false;

View file

@ -115,7 +115,7 @@ extern "C" {
int size, const struct faction *viewer, bool see_unit); int size, const struct faction *viewer, bool see_unit);
int report_items(const struct unit *u, struct item *result, int size, int report_items(const struct unit *u, struct item *result, int size,
const struct unit *owner, const struct faction *viewer); 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_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_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, void report_item(const struct unit *owner, const struct item *i,

View file

@ -1,7 +1,7 @@
#include <platform.h> #include <platform.h>
#include "reports.h" #include "reports.h"
#include "kernel/calendar.h" #include "guard.h"
#include "keyword.h" #include "keyword.h"
#include "lighthouse.h" #include "lighthouse.h"
#include "laws.h" #include "laws.h"
@ -10,28 +10,29 @@
#include "spy.h" #include "spy.h"
#include "travelthru.h" #include "travelthru.h"
#include <kernel/ally.h> #include "kernel/ally.h"
#include <kernel/config.h> #include "kernel/calendar.h"
#include <kernel/building.h> #include "kernel/config.h"
#include <kernel/faction.h> #include "kernel/building.h"
#include <kernel/item.h> #include "kernel/faction.h"
#include <kernel/race.h> #include "kernel/item.h"
#include <kernel/region.h> #include "kernel/race.h"
#include <kernel/ship.h> #include "kernel/region.h"
#include <kernel/terrain.h> #include "kernel/ship.h"
#include <kernel/unit.h> #include "kernel/terrain.h"
#include <kernel/spell.h> #include "kernel/unit.h"
#include <kernel/spellbook.h> #include "kernel/spell.h"
#include <kernel/terrain.h> #include "kernel/spellbook.h"
#include "kernel/terrain.h"
#include <util/attrib.h> #include "util/attrib.h"
#include <util/language.h> #include "util/language.h"
#include <util/lists.h> #include "util/lists.h"
#include <util/message.h> #include "util/message.h"
#include <attributes/attributes.h> #include "attributes/attributes.h"
#include <attributes/key.h> #include "attributes/key.h"
#include <attributes/otherfaction.h> #include "attributes/otherfaction.h"
#include <selist.h> #include <selist.h>
#include <stream.h> #include <stream.h>
@ -495,7 +496,7 @@ void test_prepare_lighthouse_capacity(CuTest *tc) {
u1->number = 4; u1->number = 4;
u1->building = b; u1->building = b;
set_level(u1, SK_PERCEPTION, 3); set_level(u1, SK_PERCEPTION, 3);
CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction, u1)); CuAssertIntEquals(tc, 1, lighthouse_view_distance(b, u1));
CuAssertPtrEquals(tc, b, inside_building(u1)); CuAssertPtrEquals(tc, b, inside_building(u1));
u2 = test_create_unit(f, r1); u2 = test_create_unit(f, r1);
u2->building = b; u2->building = b;
@ -530,7 +531,7 @@ void test_prepare_lighthouse_capacity(CuTest *tc) {
static void test_prepare_lighthouse(CuTest *tc) { static void test_prepare_lighthouse(CuTest *tc) {
report_context ctx; report_context ctx;
faction *f; faction *f;
region *r1, *r2, *r3; region *r1, *r2, *r3, *r4;
unit *u; unit *u;
building *b; building *b;
building_type *btype; building_type *btype;
@ -543,6 +544,7 @@ static void test_prepare_lighthouse(CuTest *tc) {
r1 = test_create_region(0, 0, t_plain); r1 = test_create_region(0, 0, t_plain);
r2 = test_create_region(1, 0, t_ocean); r2 = test_create_region(1, 0, t_ocean);
r3 = test_create_region(2, 0, t_ocean); r3 = test_create_region(2, 0, t_ocean);
r4 = test_create_region(0, 1, t_plain);
btype = test_create_buildingtype("lighthouse"); btype = test_create_buildingtype("lighthouse");
b = test_create_building(r1, btype); b = test_create_building(r1, btype);
b->flags |= BLD_MAINTAINED; b->flags |= BLD_MAINTAINED;
@ -557,6 +559,7 @@ static void test_prepare_lighthouse(CuTest *tc) {
CuAssertIntEquals(tc, seen_unit, r1->seen.mode); CuAssertIntEquals(tc, seen_unit, r1->seen.mode);
CuAssertIntEquals(tc, seen_lighthouse, r2->seen.mode); CuAssertIntEquals(tc, seen_lighthouse, r2->seen.mode);
CuAssertIntEquals(tc, seen_neighbour, r3->seen.mode); CuAssertIntEquals(tc, seen_neighbour, r3->seen.mode);
CuAssertIntEquals(tc, seen_neighbour, r4->seen.mode);
finish_reports(&ctx); finish_reports(&ctx);
test_teardown(); test_teardown();
} }
@ -595,7 +598,7 @@ static void test_prepare_lighthouse_owners(CuTest *tc)
u = test_create_unit(test_create_faction(NULL), r1); u = test_create_unit(test_create_faction(NULL), r1);
u->building = b; u->building = b;
region_set_owner(b->region, f, 0); region_set_owner(b->region, f, 0);
CuAssertIntEquals(tc, 2, lighthouse_range(b, NULL, NULL)); CuAssertIntEquals(tc, 2, lighthouse_view_distance(b, NULL));
prepare_report(&ctx, f); prepare_report(&ctx, f);
CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, r1, ctx.first);
CuAssertPtrEquals(tc, NULL, ctx.last); CuAssertPtrEquals(tc, NULL, ctx.last);
@ -786,19 +789,29 @@ static void test_insect_warnings(CuTest *tc) {
faction *f; faction *f;
gamedate gd; gamedate gd;
/* OBS: in unit tests, get_gamedate always returns season = 0 */
test_setup(); test_setup();
test_create_calendar();
test_inject_messagetypes(); test_inject_messagetypes();
f = test_create_faction(test_create_race("insect")); f = test_create_faction(test_create_race("insect"));
gd.turn = 0; CuAssertIntEquals(tc, SEASON_AUTUMN, get_gamedate(1083, &gd)->season);
gd.season = 3; report_warnings(f, gd.turn);
report_warnings(f, &gd); CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "nr_insectfall"));
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectfall"));
gd.season = 0;
report_warnings(f, &gd);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "nr_insectwinter")); 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(); test_teardown();
} }
@ -807,16 +820,16 @@ static void test_newbie_warning(CuTest *tc) {
test_setup(); test_setup();
test_inject_messagetypes(); test_inject_messagetypes();
f = test_create_faction(test_create_race("insect")); f = test_create_faction(NULL);
config_set_int("NewbieImmunity", 3); config_set_int("NewbieImmunity", 3);
f->age = 2; f->age = 2;
report_warnings(f, NULL); report_warnings(f, 0);
CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "newbieimmunity")); CuAssertPtrNotNull(tc, test_find_messagetype(f->msgs, "newbieimmunity"));
test_clear_messages(f); test_clear_messages(f);
f->age = 3; f->age = 3;
report_warnings(f, NULL); report_warnings(f, 0);
CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "newbieimmunity")); CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "newbieimmunity"));
test_clear_messages(f); test_clear_messages(f);
@ -824,38 +837,61 @@ static void test_newbie_warning(CuTest *tc) {
} }
static void test_visible_unit(CuTest *tc) { static void test_visible_unit(CuTest *tc) {
unit *u2; unit *u;
faction *f; faction *f;
ship *sh; ship *sh;
building *b;
race *rc;
test_setup(); test_setup();
f = test_create_faction(NULL); f = test_create_faction(NULL);
u2 = test_create_unit(test_create_faction(NULL), test_create_region(0, 0, NULL)); rc = test_create_race("smurf");
sh = test_create_ship(u2->region, NULL); rc->flags |= RCF_UNARMEDGUARD;
u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, NULL));
CuAssertTrue(tc, cansee(f, u2->region, u2, 0)); CuAssertTrue(tc, cansee(f, u->region, u, 0));
CuAssertTrue(tc, visible_unit(u2, f, 0, seen_unit)); CuAssertTrue(tc, visible_unit(u, f, 0, seen_unit));
CuAssertTrue(tc, visible_unit(u2, f, 0, seen_spell)); CuAssertTrue(tc, visible_unit(u, f, 0, seen_spell));
CuAssertTrue(tc, visible_unit(u2, f, 0, seen_battle)); CuAssertTrue(tc, visible_unit(u, f, 0, seen_battle));
CuAssertTrue(tc, !visible_unit(u2, f, 0, seen_travel)); CuAssertTrue(tc, !visible_unit(u, f, 0, seen_travel));
CuAssertTrue(tc, !visible_unit(u2, f, 0, seen_none)); CuAssertTrue(tc, !visible_unit(u, f, 0, seen_none));
CuAssertTrue(tc, !visible_unit(u2, f, 0, seen_neighbour)); CuAssertTrue(tc, !visible_unit(u, f, 0, seen_neighbour));
CuAssertTrue(tc, visible_unit(u2, f, 0, seen_lighthouse)); CuAssertTrue(tc, visible_unit(u, f, 0, seen_lighthouse));
CuAssertTrue(tc, !visible_unit(u2, f, -2, seen_lighthouse)); CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse));
u2->ship = sh;
CuAssertTrue(tc, visible_unit(u2, f, -2, seen_lighthouse));
u2->ship = NULL;
set_level(u2, SK_STEALTH, 1); u->ship = sh = test_create_ship(u->region, NULL);
CuAssertTrue(tc, !cansee(f, u2->region, u2, 0)); CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
CuAssertTrue(tc, cansee(f, u2->region, u2, 1)); CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
u->ship = NULL;
u2->ship = sh; setguard(u, true);
CuAssertTrue(tc, visible_unit(u2, f, -2, seen_lighthouse)); CuAssertTrue(tc, is_guard(u));
u2->ship = NULL; CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
CuAssertTrue(tc, visible_unit(u2, f, 1, seen_spell)); CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
CuAssertTrue(tc, visible_unit(u2, f, 1, seen_battle)); setguard(u, false);
u->building = b = test_create_building(u->region, NULL);
CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
u->building = NULL;
set_level(u, SK_STEALTH, 1);
CuAssertTrue(tc, !cansee(f, u->region, u, 0));
CuAssertTrue(tc, cansee(f, u->region, u, 1));
u->ship = sh;
CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
u->ship = NULL;
u->building = b;
CuAssertTrue(tc, visible_unit(u, f, -2, seen_lighthouse));
CuAssertTrue(tc, visible_unit(u, f, -2, seen_travel));
u->building = NULL;
CuAssertTrue(tc, visible_unit(u, f, 1, seen_spell));
CuAssertTrue(tc, visible_unit(u, f, 1, seen_battle));
test_teardown(); test_teardown();
} }

View file

@ -4072,7 +4072,7 @@ static int sp_pump(castorder * co)
* Betoert eine Einheit, so das sie ihm den groe<EFBFBD>ten Teil ihres Bargelds * Betoert eine Einheit, so das sie ihm den groe<EFBFBD>ten Teil ihres Bargelds
* und 50% ihres Besitzes schenkt. Sie behaelt jedoch immer soviel, wie * und 50% ihres Besitzes schenkt. Sie behaelt jedoch immer soviel, wie
* sie zum ueberleben braucht. Wirkt gegen Magieresistenz. * sie zum ueberleben braucht. Wirkt gegen Magieresistenz.
* MIN(Stufe*1000$, u->money - maintenace) * MIN(Stufe*1000$, u->money - maintenance)
* Von jedem Item wird 50% abgerundet ermittelt und uebergeben. Dazu * Von jedem Item wird 50% abgerundet ermittelt und uebergeben. Dazu
* kommt Itemzahl%2 mit 50% chance * kommt Itemzahl%2 mit 50% chance
* *
@ -4083,15 +4083,16 @@ static int sp_seduce(castorder * co)
{ {
const resource_type *rsilver = get_resourcetype(R_SILVER); const resource_type *rsilver = get_resourcetype(R_SILVER);
unit *target; unit *target;
item **itmp, *items = 0; item **itmp, *items = NULL;
unit *mage = co->magician.u; unit *u, *mage = co->magician.u;
spellparameter *pa = co->par; spellparameter *pa = co->par;
int cast_level = co->level; int cast_level = co->level;
double force = co->force; double force = co->force;
/* wenn kein Ziel gefunden, Zauber abbrechen */ /* wenn kein Ziel gefunden, Zauber abbrechen */
if (pa->param[0]->flag == TARGET_NOTFOUND) if (pa->param[0]->flag == TARGET_NOTFOUND) {
return 0; return 0;
}
target = pa->param[0]->data.u; /* Zieleinheit */ target = pa->param[0]->data.u; /* Zieleinheit */
@ -4101,6 +4102,15 @@ static int sp_seduce(castorder * co)
return 0; return 0;
} }
u = mage;
if (mage->region != target->region) {
for (u = target->region->units; u; u = u->next) {
if (u->faction == mage->faction) {
break;
}
}
}
/* Erfolgsmeldung */ /* Erfolgsmeldung */
itmp = &target->items; itmp = &target->items;
@ -4113,28 +4123,30 @@ static int sp_seduce(castorder * co)
if (loot < 0) loot = 0; if (loot < 0) loot = 0;
} }
else { else {
loot = itm->number / 2; loot = (itm->number + 1) / 2;
if (itm->number % 2) {
loot += rng_int() % 2;
}
if (loot > 0) { if (loot > 0) {
int floot = (int)(5 * force); int floot = (int)(5 * force);
if (loot > floot) loot = floot; if (loot > floot) loot = floot;
} }
} }
if (loot > 0) { if (loot > 0) {
i_change(&mage->items, itm->type, loot); if (u) {
i_change(&items, itm->type, loot); i_change(&u->items, itm->type, loot);
i_change(&items, itm->type, loot);
}
i_change(itmp, itm->type, -loot); i_change(itmp, itm->type, -loot);
} }
if (*itmp == itm) if (*itmp == itm) {
itmp = &itm->next; itmp = &itm->next;
}
} }
if (items) { if (items) {
ADDMSG(&mage->faction->msgs, msg_message("seduce_effect_0", "mage unit items", if (u) {
mage, target, items)); ADDMSG(&mage->faction->msgs, msg_message("seduce_effect_0", "mage unit items",
i_freeall(&items); u, target, items));
i_freeall(&items);
}
ADDMSG(&target->faction->msgs, msg_message("seduce_effect_1", "unit", ADDMSG(&target->faction->msgs, msg_message("seduce_effect_1", "unit",
target)); target));
} }

View file

@ -392,19 +392,16 @@ static int try_destruction(unit * u, unit * u2, const ship * sh, int skilldiff)
return 1; /* success */ return 1; /* success */
} }
static void sink_ship(region * r, ship * sh, unit * saboteur) void sink_ship(ship * sh)
{ {
unit **ui, *u; unit *u;
region *safety = r; region *r;
int i;
direction_t d;
double probability = 0.0;
message *sink_msg = NULL; message *sink_msg = NULL;
faction *f; faction *f;
assert(r); assert(sh && sh->region);
assert(sh); r = sh->region;
assert(saboteur);
for (f = NULL, u = r->units; u; u = u->next) { for (f = NULL, u = r->units; u; u = u->next) {
/* slight optimization to avoid dereferencing u->faction each time */ /* slight optimization to avoid dereferencing u->faction each time */
if (f != u->faction) { 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: */ for (f = NULL, u = r->units; u; u = u->next) {
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;) {
/* inform this faction about the sinking ship: */ /* 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) { if (u->ship == sh) {
int dead = 0; if (f != u->faction) {
message *msg; f = u->faction;
if (!(f->flags & FFL_SELECT)) {
/* if this fails, I misunderstood something: */ f->flags |= FFL_SELECT;
for (i = 0; i != u->number; ++i) if (sink_msg == NULL) {
if (chance(probability)) sink_msg = msg_message("sink_msg", "ship region", sh, r);
++dead; }
add_message(&f->msgs, sink_msg);
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;
} }
} }
} }
ui = &u->next; else if (f != NULL) {
break;
}
} }
if (sink_msg) if (sink_msg) {
msg_release(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) 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); effskill(u, SK_SPY, 0) - top_skill(u->region, u2->faction, sh, SK_PERCEPTION);
} }
if (try_destruction(u, u2, sh, skdiff)) { 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; break;
default: default:

View file

@ -27,6 +27,7 @@ extern "C" {
struct strlist; struct strlist;
struct order; struct order;
struct faction; struct faction;
struct ship;
int setstealth_cmd(struct unit *u, struct order *ord); int setstealth_cmd(struct unit *u, struct order *ord);
int spy_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, void spy_message(int spy, const struct unit *u,
const struct unit *target); const struct unit *target);
void set_factionstealth(struct unit * u, struct faction * f); void set_factionstealth(struct unit * u, struct faction * f);
void sink_ship(struct ship * sh);
#define OCEAN_SWIMMER_CHANCE 0.1
#define CANAL_SWIMMER_CHANCE 0.9
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -55,10 +55,6 @@ static void setup_spy(spy_fixture *fix) {
"ship:ship", MT_NEW_END); "ship:ship", MT_NEW_END);
mt_create_va(mt_new("sink_msg", NULL), mt_create_va(mt_new("sink_msg", NULL),
"ship:ship", "region:region", MT_NEW_END); "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) { if (fix) {
fix->r = test_create_region(0, 0, NULL); fix->r = test_create_region(0, 0, NULL);
@ -112,6 +108,7 @@ static void test_sabotage_self(CuTest *tc) {
unit *u; unit *u;
region *r; region *r;
order *ord; order *ord;
message *msg;
test_setup(); test_setup();
setup_spy(NULL); setup_spy(NULL);
@ -119,17 +116,49 @@ static void test_sabotage_self(CuTest *tc) {
assert(r); assert(r);
u = test_create_unit(test_create_faction(NULL), r); u = test_create_unit(test_create_faction(NULL), r);
assert(u && u->faction && u->region == 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); assert(u->ship);
ord = create_order(K_SABOTAGE, u->faction->locale, "SCHIFF"); ord = create_order(K_SABOTAGE, u->faction->locale, "SCHIFF");
assert(ord); assert(ord);
CuAssertIntEquals(tc, 0, sabotage_cmd(u, ord)); CuAssertIntEquals(tc, 0, sabotage_cmd(u, ord));
CuAssertPtrEquals(tc, 0, r->ships); CuAssertPtrEquals(tc, NULL, r->ships);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "sink_msg")); 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); free_order(ord);
test_teardown(); 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) { static void test_sabotage_other_fail(CuTest *tc) {
unit *u, *u2; unit *u, *u2;
@ -145,7 +174,7 @@ static void test_sabotage_other_fail(CuTest *tc) {
u = test_create_unit(test_create_faction(NULL), r); u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r);
assert(u && u2); assert(u && u2);
u2->ship = test_create_ship(r, test_create_shiptype("boat")); u2->ship = test_create_ship(r, NULL);
assert(u2->ship); assert(u2->ship);
u->ship = u2->ship; u->ship = u2->ship;
ship_update_owner(u->ship); ship_update_owner(u->ship);
@ -167,7 +196,7 @@ static void test_setstealth_cmd(CuTest *tc) {
const struct locale *lang; const struct locale *lang;
test_setup(); 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; lang = u->faction->locale;
u->flags = UFL_ANON_FACTION | UFL_SIEGE; u->flags = UFL_ANON_FACTION | UFL_SIEGE;
u->thisorder = create_order(K_SETSTEALTH, lang, "%s %s", u->thisorder = create_order(K_SETSTEALTH, lang, "%s %s",
@ -191,7 +220,7 @@ static void test_setstealth_demon(CuTest *tc) {
test_setup(); test_setup();
lang = test_create_locale(); lang = test_create_locale();
rc = test_create_race("demon"); 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"); rc = test_create_race("dwarf");
init_races(lang); init_races(lang);
u->thisorder = create_order(K_SETSTEALTH, lang, racename(lang, u, rc)); 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(); test_setup();
lang = test_create_locale(); lang = test_create_locale();
rc = test_create_race("demon"); 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 = test_create_race("smurf");
rc->flags &= ~RCF_PLAYABLE; 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); u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r);
assert(u && u2); assert(u && u2);
u2->ship = test_create_ship(r, test_create_shiptype("boat")); u2->ship = test_create_ship(r, NULL);
assert(u2->ship); assert(u2->ship);
u->ship = u2->ship; u->ship = u2->ship;
ship_update_owner(u->ship); ship_update_owner(u->ship);
@ -251,6 +280,7 @@ CuSuite *get_spy_suite(void)
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_simple_spy_message); SUITE_ADD_TEST(suite, test_simple_spy_message);
SUITE_ADD_TEST(suite, test_all_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_sabotage_self);
SUITE_ADD_TEST(suite, test_setstealth_cmd); SUITE_ADD_TEST(suite, test_setstealth_cmd);
SUITE_ADD_TEST(suite, test_setstealth_demon); SUITE_ADD_TEST(suite, test_setstealth_demon);

View file

@ -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) void test_inject_messagetypes(void)
{ {
message_handle_missing(MESSAGE_MISSING_REPLACE); message_handle_missing(MESSAGE_MISSING_REPLACE);

View file

@ -40,6 +40,7 @@ extern "C" {
struct log_t * test_log_start(int flags, struct strlist **slist); struct log_t * test_log_start(int flags, struct strlist **slist);
void test_log_stop(struct log_t *log, 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 locale * test_create_locale(void);
struct terrain_type * test_create_terrain(const char * name, int flags); struct terrain_type * test_create_terrain(const char * name, int flags);
struct race *test_create_race(const char *name); struct race *test_create_race(const char *name);

View file

@ -54,11 +54,11 @@ assert_grep_count reports/$CRFILE '^EINHEIT' 2
assert_grep_count reports/$CRFILE '^GEGENSTAENDE' 2 assert_grep_count reports/$CRFILE '^GEGENSTAENDE' 2
assert_grep_count reports/185-heg.cr '185;Runde' 1 assert_grep_count reports/185-heg.cr '185;Runde' 1
assert_grep_count reports/185-heg.cr ';Baeume' 4 assert_grep_count reports/185-heg.cr ';Baeume' 2
assert_grep_count reports/185-heg.cr '"B.ume";type' 4 assert_grep_count reports/185-heg.cr '"B.ume";type' 2
assert_grep_count reports/185-heg.cr '"Pferde";type' 6 assert_grep_count reports/185-heg.cr '"Pferde";type' 2
assert_grep_count reports/185-heg.nr 'erblickt' 6 assert_grep_count reports/185-heg.nr 'erblickt' 2
assert_grep_count reports/185-heg.cr '"lighthouse";visibility' 6 assert_grep_count reports/185-heg.cr '"lighthouse";visibility' 2
assert_grep_count reports/185-heg.cr '"neighbour";visibility' 11 assert_grep_count reports/185-heg.cr '"neighbour";visibility' 11
assert_grep_count reports/185-6rLo.cr '^EINHEIT' 2 assert_grep_count reports/185-6rLo.cr '^EINHEIT' 2
assert_grep_count reports/185-6rLo.cr '^REGION' 13 assert_grep_count reports/185-6rLo.cr '^REGION' 13

View file

@ -19,7 +19,7 @@ done
#set -e #set -e
cd $ROOT/tests cd $ROOT/tests
setup setup
#cleanup cleanup
VALGRIND=`which valgrind` VALGRIND=`which valgrind`
TESTS=../Debug/eressea/test_eressea TESTS=../Debug/eressea/test_eressea
SERVER=../Debug/eressea/eressea SERVER=../Debug/eressea/eressea