Merge pull request #796 from ennorehling/develop

cleaning up, fixing bugs
This commit is contained in:
Enno Rehling 2018-08-05 06:34:58 +02:00 committed by GitHub
commit 14cfccde0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 276 additions and 331 deletions

View File

@ -868,21 +868,6 @@
</type>
</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">
<type>
<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\",\"\").\""
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.\""

View File

@ -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].\""

View File

@ -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')

View File

@ -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

View File

@ -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 <spells/buildingcurse.h>
#include <spells/regioncurse.h>
@ -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;
}
}
}

View File

@ -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);

View File

@ -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 <kernel/building.h>
#include <kernel/faction.h>
@ -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;) {

View File

@ -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

View File

@ -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<EFBFBD>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<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) {
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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

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));
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),

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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:

View File

@ -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
}

View File

@ -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);

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)
{
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);
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);