forked from github/server
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
e90b7acd4c
69 changed files with 1828 additions and 1163 deletions
iniparser
res
s
scripts
src
CMakeLists.txt
attributes
automate.cautomate.hautomate.test.cbind_region.cbindings.ccreport.ceconomy.ceconomy.heconomy.test.cgmtool.cjsonconf.cjsonconf.test.ckernel
ally.ccalendar.ccalendar.hcalendar.test.cfaction.cfaction.test.cgroup.cgroup.hgroup.test.cpathfinder.cregion.cregion.hsave.cship.cship.test.ctypes.hunit.cunit.test.cversion.c
laws.claws.hlaws.test.cmagic.cmodules
recruit.crecruit.hreport.creport.test.creports.creports.test.csteal.cstudy.csummary.ctests.cutil
wormhole.ctests
|
@ -1 +1 @@
|
|||
Subproject commit e3533ac0a45e43e9716c40f6a874bc6f41ddc96d
|
||||
Subproject commit 22741d9ce9d19bf7b5f5a219b6ed0925259a4d1b
|
|
@ -8,6 +8,13 @@
|
|||
</construction>
|
||||
</ship>
|
||||
|
||||
<ship name="galleon" range="5" storm="1.00" damage="1.00" cargo="2000000" cptskill="5" minskill="2" sumskill="250" opensea="yes">
|
||||
<coast terrain="plain"/>
|
||||
<construction skill="shipcraft" minskill="5" maxsize="2000">
|
||||
<requirement type="log" quantity="1"/>
|
||||
</construction>
|
||||
</ship>
|
||||
|
||||
<ship name="caravel" range="5" storm="1.00" damage="1.00" cargo="300000" cptskill="3" minskill="1" sumskill="30" opensea="yes">
|
||||
<coast terrain="plain"/>
|
||||
<construction skill="shipcraft" minskill="3" maxsize="250">
|
||||
|
|
|
@ -1707,6 +1707,9 @@ msgstr "Ring der Unsichtbarkeit"
|
|||
msgid "caravel_a"
|
||||
msgstr "eine Karavelle"
|
||||
|
||||
msgid "galleon_a"
|
||||
msgstr "eine Galeone"
|
||||
|
||||
msgctxt "keyword"
|
||||
msgid "describe"
|
||||
msgstr "BESCHREIBE"
|
||||
|
@ -2064,6 +2067,9 @@ msgstr "Wir schreiben %s des Monats %s im Jahre %d %s."
|
|||
msgid "caravel"
|
||||
msgstr "Karavelle"
|
||||
|
||||
msgid "galleon"
|
||||
msgstr "Galeone"
|
||||
|
||||
msgid "dragon_postfix_10"
|
||||
msgstr "der Goldene"
|
||||
|
||||
|
|
|
@ -1458,6 +1458,12 @@ msgstr "DESCRIBE"
|
|||
msgid "roi"
|
||||
msgstr "ring of invisibility"
|
||||
|
||||
msgid "galleon_a"
|
||||
msgstr "a galleon"
|
||||
|
||||
msgid "galleon"
|
||||
msgstr "galleon"
|
||||
|
||||
msgid "caravel_a"
|
||||
msgstr "a caravel"
|
||||
|
||||
|
|
9
s/convert.sh
Normal file
9
s/convert.sh
Normal file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
while [ ! -z $1 ] ; do
|
||||
tmpfile=$(mktemp eressea.XXX)
|
||||
iconv -f latin1 -t utf-8 < $1 | \
|
||||
perl -pe 's/ß/ss/; s/ä/ae/; s/ü/ue/; s/ö/oe/;' \
|
||||
> $tmpfile && \mv $tmpfile $1
|
||||
file $1
|
||||
shift 1
|
||||
done
|
|
@ -37,6 +37,7 @@ function use_snowglobe(u, amount, token, ord)
|
|||
local transform = {
|
||||
ocean = "glacier",
|
||||
firewall = "volcano",
|
||||
activevolcano = "volcano",
|
||||
volcano = "mountain",
|
||||
desert = "plain"
|
||||
}
|
||||
|
|
|
@ -37,6 +37,18 @@ function setup()
|
|||
eressea.settings.set("study.random_progress", "0")
|
||||
end
|
||||
|
||||
function test_set_faction()
|
||||
local r = region.create(0, 0, "plain")
|
||||
local f1 = create_faction('elf')
|
||||
local f2 = create_faction('elf')
|
||||
local u = one_unit(r, f1)
|
||||
u.group = 'Experten'
|
||||
assert_equal('Experten', u.group)
|
||||
u.faction = f2
|
||||
assert_equal(f2, u.faction)
|
||||
assert_nil(u.group)
|
||||
end
|
||||
|
||||
function test_locales()
|
||||
assert_equal(2, eressea.locale.direction("de", "Ost"))
|
||||
assert_equal(5, eressea.locale.direction("de", "westen"))
|
||||
|
|
|
@ -12,6 +12,23 @@ function setup()
|
|||
eressea.settings.set("rules.peasants.growth.factor", "0")
|
||||
end
|
||||
|
||||
function test_give_unit()
|
||||
local r = region.create(0, 0, "plain")
|
||||
local f1 = faction.create('elf')
|
||||
local f2 = faction.create('elf')
|
||||
local u1 = unit.create(f1, r)
|
||||
local u2 = unit.create(f2, r)
|
||||
assert_equal(f1, u1.faction)
|
||||
assert_equal(f2, u2.faction)
|
||||
u2.group = 'Experten'
|
||||
assert_equal('Experten', u2.group)
|
||||
u1:add_order("HELFE " .. itoa36(f2.id) .. " GIB")
|
||||
u2:add_order("GIB " .. itoa36(u1.id) .. " EINHEIT")
|
||||
process_orders()
|
||||
assert_equal(f1, u2.faction)
|
||||
assert_nil(u2.group)
|
||||
end
|
||||
|
||||
function test_study_auto()
|
||||
local r = region.create(0, 0, "plain")
|
||||
local f = faction.create("human")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'tests.e2.allies'
|
||||
require 'tests.e2.quit'
|
||||
require 'tests.e2.movement'
|
||||
require 'tests.e2.astral'
|
||||
require 'tests.e2.spells'
|
||||
|
|
|
@ -7,8 +7,8 @@ function test_quit_faction()
|
|||
local f1 = faction.create("human")
|
||||
f1.password = "steamedhams"
|
||||
local f2 = faction.create("human")
|
||||
local u1 = unit.create(f1, r, 10)
|
||||
local u2 = unit.create(f2, r, 10)
|
||||
local u1 = unit.create(f1, r, 8)
|
||||
local u2 = unit.create(f2, r, 9)
|
||||
local u3 = unit.create(f1, r, 10)
|
||||
u1:clear_orders()
|
||||
u2:clear_orders()
|
||||
|
|
|
@ -36,6 +36,20 @@ function teardown()
|
|||
end
|
||||
end
|
||||
|
||||
function test_new_faction_cannot_give_unit()
|
||||
local r = region.create(0, 0, "plain")
|
||||
local f1 = faction.create('elf')
|
||||
local f2 = faction.create('elf')
|
||||
local u1 = unit.create(f1, r)
|
||||
local u2 = unit.create(f2, r)
|
||||
assert_equal(f1, u1.faction)
|
||||
assert_equal(f2, u2.faction)
|
||||
u1:add_order("HELFE " .. itoa36(f2.id) .. " GIB")
|
||||
u2:add_order("GIB " .. itoa36(u1.id) .. " EINHEIT")
|
||||
process_orders()
|
||||
assert_equal(f2, u2.faction)
|
||||
end
|
||||
|
||||
function test_calendar()
|
||||
assert_equal("winter", get_season(396))
|
||||
end
|
||||
|
|
|
@ -70,15 +70,15 @@ end
|
|||
function test_give_temp()
|
||||
u.number = 2
|
||||
u:add_order("GIB TEMP 123 1 PERSON")
|
||||
u:add_order("MACHE TEMP 123 'Herpderp'")
|
||||
u:add_order("MACHE TEMP 123 'Lorax'")
|
||||
u:add_order("ENDE")
|
||||
_G.process_orders()
|
||||
assert_equal(1, u.number)
|
||||
|
||||
for x in f.units do
|
||||
if x.name == 'Herpderp' then u=x end
|
||||
if x.name == 'Lorax' then u=x end
|
||||
end
|
||||
assert_equal('Herpderp', u.name)
|
||||
assert_equal('Lorax', u.name)
|
||||
assert_equal(1, u.number)
|
||||
end
|
||||
|
||||
|
|
|
@ -94,7 +94,9 @@ function test_lighthouse()
|
|||
eressea.free_game()
|
||||
local r = region.create(0, 0, "mountain")
|
||||
local f = faction.create("human", "human@example.com")
|
||||
region.create(1, 0, "mountain")
|
||||
local f2 = faction.create("dwarf")
|
||||
local r2 = region.create(1, 0, "mountain")
|
||||
unit.create(f2, r2, 1).name = 'The Babadook'
|
||||
region.create(2, 0, "ocean")
|
||||
region.create(0, 1, "firewall")
|
||||
region.create(3, 0, "ocean")
|
||||
|
@ -110,12 +112,13 @@ function test_lighthouse()
|
|||
|
||||
init_reports()
|
||||
write_report(f)
|
||||
assert_false(find_in_report(f, " %(1,0%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, "The Babadook"))
|
||||
assert_true(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, " %(3,0%) %(vom Turm erblickt%)"))
|
||||
assert_true(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
|
||||
|
||||
assert_false(find_in_report(f, " %(0,0%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
|
||||
assert_false(find_in_report(f, " %(4,0%) %(vom Turm erblickt%)"))
|
||||
remove_report(f)
|
||||
end
|
||||
|
|
|
@ -101,6 +101,7 @@ set (ERESSEA_SRC
|
|||
creport.c
|
||||
direction.c
|
||||
donations.c
|
||||
recruit.c
|
||||
economy.c
|
||||
eressea.c
|
||||
exparse.c
|
||||
|
|
|
@ -196,7 +196,6 @@ void register_attributes(void)
|
|||
at_register(&at_group);
|
||||
|
||||
at_register(&at_building_generic_type);
|
||||
at_register(&at_npcfaction);
|
||||
|
||||
/* connection-typen */
|
||||
register_bordertype(&bt_noway);
|
||||
|
@ -205,6 +204,7 @@ void register_attributes(void)
|
|||
register_bordertype(&bt_illusionwall);
|
||||
register_bordertype(&bt_road);
|
||||
|
||||
at_deprecate("npcfaction", a_readint);
|
||||
at_deprecate("siege", a_readint);
|
||||
at_deprecate("maxmagicians", a_readint); /* factions with differnt magician limits, probably unused */
|
||||
at_deprecate("hurting", a_readint); /* an old arena attribute */
|
||||
|
|
110
src/automate.c
110
src/automate.c
|
@ -1,5 +1,6 @@
|
|||
#include <platform.h>
|
||||
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/messages.h"
|
||||
#include "kernel/order.h"
|
||||
|
@ -16,48 +17,57 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int cmp_scholars(const void *lhs, const void *rhs)
|
||||
{
|
||||
static int cmp_scholars(const void *lhs, const void *rhs) {
|
||||
const scholar *a = (const scholar *)lhs;
|
||||
const scholar *b = (const scholar *)rhs;
|
||||
if (a->sk == b->sk) {
|
||||
/* sort by level, descending: */
|
||||
return b->level - a->level;
|
||||
}
|
||||
/* order by skill */
|
||||
return (int)a->sk - (int)b->sk;
|
||||
return b->level - a->level;
|
||||
}
|
||||
|
||||
int autostudy_init(scholar scholars[], int max_scholars, unit **units)
|
||||
int autostudy_init(scholar scholars[], int max_scholars, unit **units, skill_t *o_skill)
|
||||
{
|
||||
unit *unext = NULL, *u = *units;
|
||||
faction *f = u->faction;
|
||||
int nscholars = 0;
|
||||
|
||||
skill_t skill = NOSKILL;
|
||||
while (u) {
|
||||
keyword_t kwd = init_order(u->thisorder, u->faction->locale);
|
||||
if (kwd == K_AUTOSTUDY) {
|
||||
if (long_order_allowed(u)) {
|
||||
if (!fval(u, UFL_MARK)) {
|
||||
keyword_t kwd = init_order(u->thisorder, u->faction->locale);
|
||||
if (kwd == K_AUTOSTUDY) {
|
||||
if (f == u->faction) {
|
||||
scholar * st = scholars + nscholars;
|
||||
skill_t sk = getskill(u->faction->locale);
|
||||
if (check_student(u, u->thisorder, sk)) {
|
||||
st->sk = sk;
|
||||
st->level = effskill_study(u, st->sk);
|
||||
st->learn = 0;
|
||||
st->u = u;
|
||||
if (++nscholars > max_scholars) {
|
||||
log_fatal("you must increase MAXSCHOLARS");
|
||||
unext = u->next;
|
||||
if (long_order_allowed(u)) {
|
||||
scholar * st = scholars + nscholars;
|
||||
skill_t sk = getskill(u->faction->locale);
|
||||
if (skill == NOSKILL && sk != NOSKILL) {
|
||||
skill = sk;
|
||||
if (o_skill) {
|
||||
*o_skill = skill;
|
||||
}
|
||||
}
|
||||
if (check_student(u, u->thisorder, sk)) {
|
||||
if (sk == skill) {
|
||||
fset(u, UFL_MARK);
|
||||
st->level = (short)effskill_study(u, sk);
|
||||
st->learn = 0;
|
||||
st->u = u;
|
||||
if (++nscholars >= max_scholars) {
|
||||
log_warning("you must increase MAXSCHOLARS");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
fset(u, UFL_MARK);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!unext) {
|
||||
unext = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
while (unext && unext->faction != f) {
|
||||
unext = unext->next;
|
||||
}
|
||||
*units = unext;
|
||||
if (nscholars > 0) {
|
||||
qsort(scholars, nscholars, sizeof(scholar), cmp_scholars);
|
||||
|
@ -81,26 +91,25 @@ void autostudy_run(scholar scholars[], int nscholars)
|
|||
{
|
||||
int ti = 0;
|
||||
while (ti != nscholars) {
|
||||
skill_t sk = scholars[ti].sk;
|
||||
int t, se, ts = 0, tt = 0, si = ti;
|
||||
for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) {
|
||||
for (se = ti; se != nscholars; ++se) {
|
||||
int mint;
|
||||
ts += scholars[se].u->number; /* count total scholars */
|
||||
mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */
|
||||
for (; mint > tt && si != nscholars && scholars[si].sk == sk; ++si) {
|
||||
for (; mint > tt && si != nscholars; ++si) {
|
||||
tt += scholars[si].u->number;
|
||||
}
|
||||
}
|
||||
/* now si splits the teachers and students 1:10 */
|
||||
/* first student must be 2 levels below first teacher: */
|
||||
for (; si != se && scholars[si].sk == sk; ++si) {
|
||||
for (; si != se; ++si) {
|
||||
if (scholars[si].level + TEACHDIFFERENCE <= scholars[ti].level) {
|
||||
break;
|
||||
}
|
||||
tt += scholars[si].u->number;
|
||||
}
|
||||
/* now si is the first unit we can teach, if we can teach any */
|
||||
if (si == se || scholars[si].sk != sk) {
|
||||
if (si == se) {
|
||||
/* there are no students, so standard learning for everyone */
|
||||
for (t = ti; t != se; ++t) {
|
||||
learning(scholars + t, scholars[t].u->number);
|
||||
|
@ -161,23 +170,38 @@ void autostudy_run(scholar scholars[], int nscholars)
|
|||
}
|
||||
}
|
||||
|
||||
#define MAXSCHOLARS 1024
|
||||
|
||||
void do_autostudy(region *r)
|
||||
{
|
||||
static int config;
|
||||
static int batchsize = MAXSCHOLARS;
|
||||
static int max_scholars;
|
||||
unit *units = r->units;
|
||||
scholar scholars[MAXSCHOLARS];
|
||||
while (units) {
|
||||
int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, &units);
|
||||
if (nscholars > max_scholars) {
|
||||
stats_count("automate.max_scholars", nscholars - max_scholars);
|
||||
max_scholars = nscholars;
|
||||
}
|
||||
autostudy_run(scholars, nscholars);
|
||||
for (i = 0; i != nscholars; ++i) {
|
||||
int days = STUDYDAYS * scholars[i].learn;
|
||||
learn_skill(scholars[i].u, scholars[i].sk, days);
|
||||
unit *u;
|
||||
|
||||
if (config_changed(&config)) {
|
||||
batchsize = config_get_int("automate.batchsize", MAXSCHOLARS);
|
||||
assert(batchsize <= MAXSCHOLARS);
|
||||
}
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (!fval(u, UFL_MARK)) {
|
||||
unit *ulist = u;
|
||||
int sum_scholars = 0;
|
||||
while (ulist) {
|
||||
skill_t skill = NOSKILL;
|
||||
int i, nscholars = autostudy_init(scholars, batchsize, &ulist, &skill);
|
||||
assert(ulist == NULL || ulist->faction == u->faction);
|
||||
sum_scholars += nscholars;
|
||||
if (sum_scholars > max_scholars) {
|
||||
stats_count("automate.max_scholars", sum_scholars - max_scholars);
|
||||
max_scholars = sum_scholars;
|
||||
}
|
||||
autostudy_run(scholars, nscholars);
|
||||
for (i = 0; i != nscholars; ++i) {
|
||||
int days = STUDYDAYS * scholars[i].learn;
|
||||
learn_skill(scholars[i].u, skill, days);
|
||||
}
|
||||
}
|
||||
}
|
||||
freset(u, UFL_MARK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,16 +28,16 @@ struct unit;
|
|||
|
||||
typedef struct scholar {
|
||||
struct unit *u;
|
||||
skill_t sk;
|
||||
int level;
|
||||
int learn;
|
||||
short level;
|
||||
} scholar;
|
||||
|
||||
#define MAXSCHOLARS 128
|
||||
#define STUDENTS_PER_TEACHER 10
|
||||
|
||||
void do_autostudy(struct region *r);
|
||||
|
||||
int autostudy_init(scholar scholars[], int max_scholars, struct unit **units);
|
||||
int autostudy_init(scholar scholars[], int max_scholars, struct unit **units, skill_t *o_skill);
|
||||
void autostudy_run(scholar scholars[], int nscholars);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "automate.h"
|
||||
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/order.h"
|
||||
#include "kernel/region.h"
|
||||
|
@ -20,6 +21,8 @@ static void test_autostudy_init(CuTest *tc) {
|
|||
unit *u1, *u2, *u3, *u4, *u5, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
message *msg;
|
||||
skill_t skill = NOSKILL;
|
||||
|
||||
test_setup();
|
||||
mt_create_error(77);
|
||||
|
@ -33,36 +36,36 @@ static void test_autostudy_init(CuTest *tc) {
|
|||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_level(u2, SK_ENTERTAINMENT, 2);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
u4 = test_create_unit(f, r);
|
||||
u4->thisorder = create_order(K_AUTOSTUDY, f->locale, "Dudelidu");
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
u5 = test_create_unit(test_create_faction(NULL), r);
|
||||
u5->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
scholars[3].u = NULL;
|
||||
scholars[2].u = NULL;
|
||||
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u4->faction->msgs, "error77"));
|
||||
CuAssertIntEquals(tc, 2, autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
|
||||
CuAssertPtrEquals(tc, u2, scholars[0].u);
|
||||
CuAssertIntEquals(tc, 2, scholars[0].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk);
|
||||
CuAssertPtrEquals(tc, u1, scholars[1].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk);
|
||||
CuAssertPtrEquals(tc, u3, scholars[2].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[2].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[2].learn);
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[3].u);
|
||||
CuAssertPtrEquals(tc, u5, ulist);
|
||||
CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertPtrEquals(tc, u5, scholars[0].u);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
|
||||
ulist = u3;
|
||||
CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, skill);
|
||||
CuAssertPtrEquals(tc, u3, scholars[0].u);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].level);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, SK_PERCEPTION, scholars[0].sk);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
|
||||
CuAssertPtrNotNull(tc, msg = test_find_messagetype(f->msgs, "error77"));
|
||||
CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(f->msgs, "error77", msg));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
@ -75,6 +78,7 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
|
|||
unit *u1, *u2, *u3, *u4, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
skill_t skill;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
|
@ -94,9 +98,10 @@ static void test_autostudy_run_twoteachers(CuTest *tc) {
|
|||
set_number(u4, 12);
|
||||
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist, &skill));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 0, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, scholars[2].u->number * 2, scholars[2].learn);
|
||||
|
@ -125,21 +130,34 @@ static void test_autostudy_run(CuTest *tc) {
|
|||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 15);
|
||||
scholars[3].u = NULL;
|
||||
|
||||
scholars[2].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertIntEquals(tc, UFL_MARK, u1->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, UFL_MARK, u2->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 1, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 20, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 15, scholars[2].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
|
||||
scholars[1].u = NULL;
|
||||
ulist = u3;
|
||||
CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 15, scholars[0].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[1].u);
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_autostudy_run_noteachers(CuTest *tc) {
|
||||
scholar scholars[4];
|
||||
int nscholars;
|
||||
unit *u1, *u2, *u3, *ulist;
|
||||
unit *u1, *u2, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
|
@ -147,23 +165,24 @@ static void test_autostudy_run_noteachers(CuTest *tc) {
|
|||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]);
|
||||
set_number(u1, 2);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u1, 5);
|
||||
set_level(u1, SK_ENTERTAINMENT, 2);
|
||||
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 15);
|
||||
scholars[3].u = NULL;
|
||||
set_number(u2, 7);
|
||||
set_level(u2, SK_ENTERTAINMENT, 2);
|
||||
|
||||
scholars[2].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 2, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 10, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 15, scholars[2].learn);
|
||||
/* stupid qsort is unstable: */
|
||||
CuAssertIntEquals(tc, 12, scholars[0].learn + scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 35, scholars[0].learn * scholars[1].learn);
|
||||
CuAssertPtrEquals(tc, NULL, scholars[2].u);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
@ -188,7 +207,7 @@ static void test_autostudy_run_teachers_learn(CuTest *tc) {
|
|||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
set_number(u2, 10);
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 1, scholars[0].learn);
|
||||
|
@ -222,7 +241,7 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
|
|||
set_number(u3, 10);
|
||||
scholars[3].u = NULL;
|
||||
ulist = r->units;
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist));
|
||||
CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, NULL, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
|
@ -231,11 +250,78 @@ static void test_autostudy_run_skilldiff(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_autostudy_batches(CuTest *tc) {
|
||||
scholar scholars[2];
|
||||
int nscholars;
|
||||
unit *u1, *u2, *u3, *ulist;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u1, 1);
|
||||
set_level(u1, SK_PERCEPTION, 2);
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u3, 10);
|
||||
scholars[1].u = NULL;
|
||||
ulist = r->units;
|
||||
config_set("automate.batchsize", "2");
|
||||
CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
|
||||
CuAssertPtrEquals(tc, u3, ulist);
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 0, scholars[0].learn);
|
||||
CuAssertIntEquals(tc, 20, scholars[1].learn);
|
||||
CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 2, &ulist, NULL));
|
||||
autostudy_run(scholars, nscholars);
|
||||
CuAssertIntEquals(tc, 10, scholars[0].learn);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_do_autostudy(CuTest *tc) {
|
||||
unit *u1, *u2, *u3, *u4;
|
||||
faction *f;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
r = test_create_plain(0, 0);
|
||||
f = test_create_faction(NULL);
|
||||
u1 = test_create_unit(f, r);
|
||||
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u1, 1);
|
||||
set_level(u1, SK_PERCEPTION, 2);
|
||||
u2 = test_create_unit(f, r);
|
||||
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
|
||||
set_number(u2, 10);
|
||||
u3 = test_create_unit(f, r);
|
||||
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
u4 = test_create_unit(test_create_faction(NULL), r);
|
||||
u4->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
|
||||
do_autostudy(r);
|
||||
CuAssertIntEquals(tc, 2, get_level(u1, SK_PERCEPTION));
|
||||
/* impossible to say if u2 is T1 or T2 now */
|
||||
CuAssertIntEquals(tc, 1, get_level(u3, SK_ENTERTAINMENT));
|
||||
CuAssertIntEquals(tc, 1, get_level(u4, SK_ENTERTAINMENT));
|
||||
CuAssertIntEquals(tc, 0, u1->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u2->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK);
|
||||
CuAssertIntEquals(tc, 0, u4->flags & UFL_MARK);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_automate_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_autostudy_init);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run);
|
||||
SUITE_ADD_TEST(suite, test_do_autostudy);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_batches);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_noteachers);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_teachers_learn);
|
||||
SUITE_ADD_TEST(suite, test_autostudy_run_twoteachers);
|
||||
|
|
|
@ -527,7 +527,7 @@ static int tolua_region_get_peasants(lua_State * L)
|
|||
region *self = (region *)tolua_tousertype(L, 1, NULL);
|
||||
|
||||
if (self) {
|
||||
lua_pushinteger(L, self->land ? self->land->peasants : 0);
|
||||
lua_pushinteger(L, rpeasants(self));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -538,7 +538,7 @@ static int tolua_region_set_peasants(lua_State * L)
|
|||
region *self = (region *)tolua_tousertype(L, 1, NULL);
|
||||
|
||||
if (self && self->land) {
|
||||
self->land->peasants = lua_tointeger(L, 2);
|
||||
rsetpeasants(self, lua_tointeger(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -259,10 +259,9 @@ static int tolua_dice_rand(lua_State * L)
|
|||
|
||||
static int tolua_get_season(lua_State * L)
|
||||
{
|
||||
int turnno = (int)tolua_tonumber(L, 1, 0);
|
||||
gamedate gd;
|
||||
get_gamedate(turnno, &gd);
|
||||
tolua_pushstring(L, seasonnames[gd.season]);
|
||||
int turn_no = (int)tolua_tonumber(L, 1, 0);
|
||||
season_t season = calendar_season(turn_no);
|
||||
tolua_pushstring(L, seasonnames[season]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -1503,33 +1503,34 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
|
|||
cr_output_messages(F, mlist, f);
|
||||
}
|
||||
}
|
||||
/* buildings */
|
||||
for (b = rbuildings(r); b; b = b->next) {
|
||||
int fno = -1;
|
||||
u = building_owner(b);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
cr_output_building_compat(F, b, u, fno, f);
|
||||
}
|
||||
|
||||
/* ships */
|
||||
for (sh = r->ships; sh; sh = sh->next) {
|
||||
int fno = -1;
|
||||
u = ship_owner(sh);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
/* buildings */
|
||||
for (b = rbuildings(r); b; b = b->next) {
|
||||
int fno = -1;
|
||||
u = building_owner(b);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
cr_output_building_compat(F, b, u, fno, f);
|
||||
}
|
||||
|
||||
cr_output_ship_compat(F, sh, u, fno, f, r);
|
||||
}
|
||||
/* ships */
|
||||
for (sh = r->ships; sh; sh = sh->next) {
|
||||
int fno = -1;
|
||||
u = ship_owner(sh);
|
||||
if (u && !fval(u, UFL_ANON_FACTION)) {
|
||||
const faction *sf = visible_faction(f, u);
|
||||
fno = sf->no;
|
||||
}
|
||||
|
||||
/* visible units */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
cr_output_unit_compat(F, f, u, r->seen.mode);
|
||||
cr_output_ship_compat(F, sh, u, fno, f, r);
|
||||
}
|
||||
/* visible units */
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
cr_output_unit_compat(F, f, u, r->seen.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
657
src/economy.c
657
src/economy.c
|
@ -84,28 +84,28 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
static int working;
|
||||
|
||||
static econ_request entertainers[1024];
|
||||
static econ_request *nextentertainer;
|
||||
static int entertaining;
|
||||
#define MAX_REQUESTS 1024
|
||||
static struct econ_request econ_requests[MAX_REQUESTS];
|
||||
|
||||
static econ_request **g_requests; /* TODO: no need for this to be module-global */
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
#define ENTERTAINFRACTION 20
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
static void add_request(econ_request * req, enum econ_type type, unit *u, order *ord, int want) {
|
||||
req->next = NULL;
|
||||
req->unit = u;
|
||||
req->qty = u->wants = want;
|
||||
req->type = type;
|
||||
}
|
||||
|
||||
#define ENTERTAINFRACTION 20
|
||||
static bool rule_auto_taxation(void)
|
||||
{
|
||||
return config_get_int("rules.economy.taxation", 0) != 0;
|
||||
}
|
||||
|
||||
static bool rule_autowork(void) {
|
||||
return config_get_int("work.auto", 0) != 0;
|
||||
}
|
||||
|
||||
int entertainmoney(const region * r)
|
||||
{
|
||||
|
@ -187,6 +187,10 @@ int expand_production(region * r, econ_request * requests, econ_request ***resul
|
|||
return norders;
|
||||
}
|
||||
|
||||
static int expandorders(region * r, econ_request * requests) {
|
||||
return expand_production(r, requests, &g_requests);
|
||||
}
|
||||
|
||||
static void free_requests(econ_request *requests) {
|
||||
while (requests) {
|
||||
econ_request *req = requests->next;
|
||||
|
@ -195,383 +199,8 @@ static void free_requests(econ_request *requests) {
|
|||
}
|
||||
}
|
||||
|
||||
static int expandorders(region * r, econ_request * requests) {
|
||||
return expand_production(r, requests, &g_requests);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
econ_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(econ_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
econ_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
rec->f = u->faction;
|
||||
rec->total = 0;
|
||||
rec->assigned = 0;
|
||||
rec->requests = NULL;
|
||||
rec->next = recruits;
|
||||
recruits = rec;
|
||||
}
|
||||
*total += qty;
|
||||
rec->total += qty;
|
||||
ro->next = rec->requests;
|
||||
rec->requests = ro;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
if (u->number == 0) {
|
||||
set_number(u, number);
|
||||
u->hp = number * unit_max_hp(u);
|
||||
unew = u;
|
||||
}
|
||||
else {
|
||||
unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
econ_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->type.recruit.ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
while (rec->requests) {
|
||||
econ_request *req = rec->requests;
|
||||
rec->requests = req->next;
|
||||
free(req);
|
||||
}
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, econ_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
int orc_total = 0;
|
||||
|
||||
/* peasant limited: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int orc_recruited, orc_peasants = rpeasants(r) * 2;
|
||||
int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
|
||||
if (orc_total < orc_frac)
|
||||
orc_frac = orc_total;
|
||||
orc_recruited = do_recruiting(recruits, orc_frac);
|
||||
assert(orc_recruited <= orc_frac);
|
||||
rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
/* no limit: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int recruited, peasants = rpeasants(r) * 2;
|
||||
recruited = do_recruiting(recruits, INT_MAX);
|
||||
if (recruited > 0) {
|
||||
rsetpeasants(r, (peasants - recruited) / 2);
|
||||
}
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
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
|
||||
* grundsaetzlich 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;
|
||||
econ_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
if (str && str[0]) {
|
||||
/* Monsters can RECRUIT 15 DRACOID
|
||||
* also: secondary race */
|
||||
rc = findrace(str, f->locale);
|
||||
if (rc != NULL) {
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
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 (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_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));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->type.recruit.ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
static void friendly_takeover(region * r, faction * f)
|
||||
{
|
||||
region_set_owner(r, f, turn);
|
||||
|
@ -761,7 +390,6 @@ void maintain_buildings(region * r)
|
|||
void economics(region * r)
|
||||
{
|
||||
unit *u;
|
||||
econ_request *recruitorders = NULL;
|
||||
|
||||
/* Geben vor Selbstmord (doquit)! Hier alle unmittelbaren Befehle.
|
||||
* Rekrutieren vor allen Einnahmequellen. Bewachen JA vor Steuern
|
||||
|
@ -784,28 +412,10 @@ void economics(region * r)
|
|||
}
|
||||
}
|
||||
}
|
||||
/* RECRUIT orders */
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
||||
for (ord = u->orders; ord; ord = ord->next) {
|
||||
if (getkeyword(ord) == K_RECRUIT) {
|
||||
recruit(u, ord, &recruitorders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
||||
|
||||
void destroy(region *r) {
|
||||
unit *u;
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord = u->thisorder;
|
||||
keyword_t kwd = getkeyword(ord);
|
||||
|
@ -1490,7 +1100,7 @@ static void expandbuying(region * r, econ_request * buyorders)
|
|||
unsigned int j;
|
||||
for (j = 0; j != norders; j++) {
|
||||
int price, multi;
|
||||
ltype = g_requests[j]->type.trade.ltype;
|
||||
ltype = g_requests[j]->data.trade.ltype;
|
||||
trade = trades;
|
||||
while (trade->type && trade->type != ltype)
|
||||
++trade;
|
||||
|
@ -1671,10 +1281,11 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
|
|||
}
|
||||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->type.trade.ltype = ltype; /* sollte immer gleich sein */
|
||||
o->data.trade.ltype = ltype; /* sollte immer gleich sein */
|
||||
|
||||
o->unit = u;
|
||||
o->qty = n;
|
||||
o->type = ECON_BUY;
|
||||
addlist(buyorders, o);
|
||||
}
|
||||
|
||||
|
@ -1774,7 +1385,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
|
|||
int j;
|
||||
for (j = 0; j != norders; j++) {
|
||||
const luxury_type *search = NULL;
|
||||
const luxury_type *ltype = g_requests[j]->type.trade.ltype;
|
||||
const luxury_type *ltype = g_requests[j]->data.trade.ltype;
|
||||
int multi = r_demand(r, ltype);
|
||||
int i, price;
|
||||
int use = 0;
|
||||
|
@ -1981,7 +1592,7 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
|
|||
/* Wenn andere Einheiten das selbe verkaufen, muss ihr Zeug abgezogen
|
||||
* werden damit es nicht zweimal verkauft wird: */
|
||||
for (o = *sellorders; o; o = o->next) {
|
||||
if (o->type.trade.ltype == ltype && o->unit->faction == u->faction) {
|
||||
if (o->data.trade.ltype == ltype && o->unit->faction == u->faction) {
|
||||
int fpool =
|
||||
o->qty - get_pooled(o->unit, itype->rtype, GET_RESERVE, INT_MAX);
|
||||
if (fpool < 0) fpool = 0;
|
||||
|
@ -2022,7 +1633,8 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
|
|||
if (!o) abort();
|
||||
o->unit = u;
|
||||
o->qty = n;
|
||||
o->type.trade.ltype = ltype;
|
||||
o->type = ECON_SELL;
|
||||
o->data.trade.ltype = ltype;
|
||||
addlist(sellorders, o);
|
||||
|
||||
return unlimited;
|
||||
|
@ -2146,12 +1758,8 @@ static void breedtrees(unit * u, int raw)
|
|||
{
|
||||
int n, i, skill, planted = 0;
|
||||
const resource_type *rtype;
|
||||
int current_season;
|
||||
season_t current_season = calendar_season(turn);
|
||||
region *r = u->region;
|
||||
gamedate date;
|
||||
|
||||
get_gamedate(turn, &date);
|
||||
current_season = date.season;
|
||||
|
||||
/* Baeume zuechten geht nur im Fruehling */
|
||||
if (current_season != SEASON_SPRING) {
|
||||
|
@ -2349,36 +1957,39 @@ static void research_cmd(unit * u, struct order *ord)
|
|||
}
|
||||
}
|
||||
|
||||
static void expandentertainment(region * r)
|
||||
static void expandentertainment(region * r, econ_request *ecbegin, econ_request *ecend, long total)
|
||||
{
|
||||
int m = entertainmoney(r);
|
||||
econ_request *o;
|
||||
|
||||
for (o = &entertainers[0]; o != nextentertainer; ++o) {
|
||||
double part = m / (double)entertaining;
|
||||
unit *u = o->unit;
|
||||
for (o = ecbegin; o != ecend; ++o) {
|
||||
if (o->type == ECON_ENTERTAIN) {
|
||||
double part = m / (double)total;
|
||||
unit *u = o->unit;
|
||||
|
||||
if (entertaining <= m)
|
||||
u->n = o->qty;
|
||||
else
|
||||
u->n = (int)(o->qty * part);
|
||||
change_money(u, u->n);
|
||||
rsetmoney(r, rmoney(r) - u->n);
|
||||
m -= u->n;
|
||||
entertaining -= o->qty;
|
||||
if (total <= m)
|
||||
u->n = o->qty;
|
||||
else
|
||||
u->n = (int)(o->qty * part);
|
||||
change_money(u, u->n);
|
||||
rsetmoney(r, rmoney(r) - u->n);
|
||||
m -= u->n;
|
||||
total -= o->qty;
|
||||
|
||||
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
||||
produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
|
||||
add_income(u, IC_ENTERTAIN, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
|
||||
produceexp(u, SK_ENTERTAINMENT, (u->n < u->number) ? u->n : u->number);
|
||||
add_income(u, IC_ENTERTAIN, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
}
|
||||
}
|
||||
assert(total == 0);
|
||||
}
|
||||
|
||||
void entertain_cmd(unit * u, struct order *ord)
|
||||
static int entertain_cmd(unit * u, struct order *ord, econ_request **io_req)
|
||||
{
|
||||
region *r = u->region;
|
||||
int max_e;
|
||||
econ_request *o;
|
||||
int wants, max_e;
|
||||
econ_request *req = *io_req;
|
||||
static int entertainbase = 0;
|
||||
static int entertainperlevel = 0;
|
||||
keyword_t kwd;
|
||||
|
@ -2395,39 +2006,38 @@ void entertain_cmd(unit * u, struct order *ord)
|
|||
}
|
||||
if (fval(u, UFL_WERE)) {
|
||||
cmistake(u, ord, 58, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (!effskill(u, SK_ENTERTAINMENT, NULL)) {
|
||||
cmistake(u, ord, 58, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (u->ship && is_guarded(r, u)) {
|
||||
cmistake(u, ord, 69, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (is_cursed(r->attribs, &ct_depression)) {
|
||||
cmistake(u, ord, 28, MSG_INCOME);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u->wants = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL)
|
||||
* entertainperlevel);
|
||||
|
||||
max_e = getuint();
|
||||
if (max_e != 0) {
|
||||
if (u->wants > max_e) u->wants = max_e;
|
||||
wants = getuint();
|
||||
max_e = u->number * (entertainbase + effskill(u, SK_ENTERTAINMENT, NULL) * entertainperlevel);
|
||||
if (wants > 0 && wants < max_e) {
|
||||
max_e = wants;
|
||||
}
|
||||
o = nextentertainer++;
|
||||
o->unit = u;
|
||||
o->qty = u->wants;
|
||||
entertaining += o->qty;
|
||||
if (max_e > 0) {
|
||||
add_request(req++, ECON_ENTERTAIN, u, ord, max_e);
|
||||
*io_req = req;
|
||||
}
|
||||
return max_e;
|
||||
}
|
||||
|
||||
/**
|
||||
* \return number of working spaces taken by players
|
||||
*/
|
||||
static void
|
||||
expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork)
|
||||
expandwork(region * r, econ_request * work_begin, econ_request * work_end, int maxwork, long total)
|
||||
{
|
||||
int earnings;
|
||||
/* n: verbleibende Einnahmen */
|
||||
|
@ -2435,37 +2045,41 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
|
|||
int jobs = maxwork;
|
||||
int p_wage = wage(r, NULL, NULL, turn);
|
||||
int money = rmoney(r);
|
||||
econ_request *o;
|
||||
if (total > 0 && !rule_autowork()) {
|
||||
econ_request *o;
|
||||
|
||||
for (o = work_begin; o != work_end; ++o) {
|
||||
unit *u = o->unit;
|
||||
int workers;
|
||||
for (o = work_begin; o != work_end; ++o) {
|
||||
if (o->type == ECON_WORK) {
|
||||
unit *u = o->unit;
|
||||
int workers;
|
||||
|
||||
if (u->number == 0)
|
||||
continue;
|
||||
if (u->number == 0)
|
||||
continue;
|
||||
|
||||
if (jobs >= working)
|
||||
workers = u->number;
|
||||
else {
|
||||
int req = (u->number * jobs) % working;
|
||||
workers = u->number * jobs / working;
|
||||
if (req > 0 && rng_int() % working < req)
|
||||
workers++;
|
||||
if (jobs >= total)
|
||||
workers = u->number;
|
||||
else {
|
||||
int req = (u->number * jobs) % total;
|
||||
workers = u->number * jobs / total;
|
||||
if (req > 0 && rng_int() % total < req)
|
||||
workers++;
|
||||
}
|
||||
|
||||
assert(workers >= 0);
|
||||
|
||||
u->n = workers * wage(u->region, u->faction, u_race(u), turn);
|
||||
|
||||
jobs -= workers;
|
||||
assert(jobs >= 0);
|
||||
|
||||
change_money(u, u->n);
|
||||
total -= o->unit->number;
|
||||
add_income(u, IC_WORK, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
}
|
||||
}
|
||||
|
||||
assert(workers >= 0);
|
||||
|
||||
u->n = workers * wage(u->region, u->faction, u_race(u), turn);
|
||||
|
||||
jobs -= workers;
|
||||
assert(jobs >= 0);
|
||||
|
||||
change_money(u, u->n);
|
||||
working -= o->unit->number;
|
||||
add_income(u, IC_WORK, o->qty, u->n);
|
||||
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
||||
assert(total == 0);
|
||||
}
|
||||
|
||||
if (jobs > rpeasants(r)) {
|
||||
jobs = rpeasants(r);
|
||||
}
|
||||
|
@ -2478,34 +2092,35 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
|
|||
rsetmoney(r, money + earnings);
|
||||
}
|
||||
|
||||
static int do_work(unit * u, order * ord, econ_request * o)
|
||||
static int work_cmd(unit * u, order * ord, econ_request ** io_req)
|
||||
{
|
||||
if (playerrace(u_race(u))) {
|
||||
econ_request *req = *io_req;
|
||||
region *r = u->region;
|
||||
int w;
|
||||
|
||||
if (fval(u, UFL_WERE)) {
|
||||
if (ord)
|
||||
if (ord) {
|
||||
cmistake(u, ord, 313, MSG_INCOME);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (u->ship && is_guarded(r, u)) {
|
||||
if (ord)
|
||||
if (ord) {
|
||||
cmistake(u, ord, 69, MSG_INCOME);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
w = wage(r, u->faction, u_race(u), turn);
|
||||
u->wants = u->number * w;
|
||||
o->unit = u;
|
||||
o->qty = u->number * w;
|
||||
working += u->number;
|
||||
return 0;
|
||||
add_request(req++, ECON_WORK, u, ord, w * u->number);
|
||||
*io_req = req;
|
||||
return u->number;
|
||||
}
|
||||
else if (ord && !is_monsters(u->faction)) {
|
||||
ADDMSG(&u->faction->msgs,
|
||||
msg_feedback(u, ord, "race_cantwork", "race", u_race(u)));
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void expandloot(region * r, econ_request * lootorders)
|
||||
|
@ -2637,6 +2252,7 @@ void tax_cmd(unit * u, struct order *ord, econ_request ** taxorders)
|
|||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = u->wants / TAXFRACTION;
|
||||
o->type = ECON_TAX;
|
||||
o->unit = u;
|
||||
addlist(taxorders, o);
|
||||
return;
|
||||
|
@ -2703,30 +2319,30 @@ void loot_cmd(unit * u, struct order *ord, econ_request ** lootorders)
|
|||
o = (econ_request *)calloc(1, sizeof(econ_request));
|
||||
if (!o) abort();
|
||||
o->qty = u->wants / TAXFRACTION;
|
||||
o->type = ECON_LOOT;
|
||||
o->unit = u;
|
||||
addlist(lootorders, o);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define MAX_WORKERS 1024
|
||||
static struct econ_request workers[MAX_WORKERS];
|
||||
|
||||
void auto_work(region * r)
|
||||
{
|
||||
econ_request *nextworker = workers;
|
||||
econ_request *nextrequest = econ_requests;
|
||||
unit *u;
|
||||
long total = 0;
|
||||
|
||||
for (u = r->units; u; u = u->next) {
|
||||
if (!(u->flags & UFL_LONGACTION) && !is_monsters(u->faction)) {
|
||||
if (do_work(u, NULL, nextworker) == 0) {
|
||||
assert(nextworker - workers < MAX_WORKERS);
|
||||
++nextworker;
|
||||
int work = work_cmd(u, NULL, &nextrequest);
|
||||
if (work) {
|
||||
total += work;
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextworker != workers) {
|
||||
expandwork(r, workers, nextworker, region_maxworkers(r));
|
||||
if (nextrequest != econ_requests) {
|
||||
expandwork(r, econ_requests, nextrequest, region_maxworkers(r), total);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2771,21 +2387,13 @@ static void peasant_taxes(region * r)
|
|||
}
|
||||
}
|
||||
|
||||
static bool rule_auto_taxation(void)
|
||||
{
|
||||
return config_get_int("rules.economy.taxation", 0) != 0;
|
||||
}
|
||||
|
||||
static bool rule_autowork(void) {
|
||||
return config_get_int("work.auto", 0) != 0;
|
||||
}
|
||||
|
||||
void produce(struct region *r)
|
||||
{
|
||||
econ_request *taxorders, *lootorders, *sellorders, *stealorders, *buyorders;
|
||||
unit *u;
|
||||
bool limited = true;
|
||||
econ_request *nextworker = workers;
|
||||
long entertaining = 0, working = 0;
|
||||
econ_request *nextrequest = econ_requests;
|
||||
static int bt_cache;
|
||||
static const struct building_type *caravan_bt;
|
||||
static int rc_cache;
|
||||
|
@ -2820,9 +2428,6 @@ void produce(struct region *r)
|
|||
|
||||
buyorders = 0;
|
||||
sellorders = 0;
|
||||
working = 0;
|
||||
nextentertainer = &entertainers[0];
|
||||
entertaining = 0;
|
||||
taxorders = 0;
|
||||
lootorders = 0;
|
||||
stealorders = 0;
|
||||
|
@ -2879,13 +2484,17 @@ void produce(struct region *r)
|
|||
|
||||
switch (todo) {
|
||||
case K_ENTERTAIN:
|
||||
entertain_cmd(u, u->thisorder);
|
||||
entertaining += entertain_cmd(u, u->thisorder, &nextrequest);
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
break;
|
||||
|
||||
case K_WORK:
|
||||
if (!rule_autowork() && do_work(u, u->thisorder, nextworker) == 0) {
|
||||
assert(nextworker - workers < MAX_WORKERS);
|
||||
++nextworker;
|
||||
if (!rule_autowork()) {
|
||||
int work = work_cmd(u, u->thisorder, &nextrequest);
|
||||
if (work != 0) {
|
||||
working += work;
|
||||
assert(nextrequest - econ_requests <= MAX_REQUESTS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2927,11 +2536,11 @@ void produce(struct region *r)
|
|||
* Befehlen, die den Bauern mehr Geld geben, damit man aus den Zahlen der
|
||||
* letzten Runde berechnen kann, wieviel die Bauern fuer Unterhaltung
|
||||
* auszugeben bereit sind. */
|
||||
if (entertaining)
|
||||
expandentertainment(r);
|
||||
if (!rule_autowork()) {
|
||||
expandwork(r, workers, nextworker, region_maxworkers(r));
|
||||
if (entertaining > 0) {
|
||||
expandentertainment(r, econ_requests, nextrequest, entertaining);
|
||||
}
|
||||
expandwork(r, econ_requests, nextrequest, region_maxworkers(r), working);
|
||||
|
||||
if (taxorders) {
|
||||
expandtax(r, taxorders);
|
||||
free_requests(taxorders);
|
||||
|
|
|
@ -55,10 +55,17 @@ extern "C" {
|
|||
struct econ_request *next;
|
||||
struct unit *unit;
|
||||
int qty;
|
||||
enum econ_type {
|
||||
ECON_LIST,
|
||||
ECON_ENTERTAIN,
|
||||
ECON_WORK,
|
||||
ECON_TAX,
|
||||
ECON_LOOT,
|
||||
ECON_BUY,
|
||||
ECON_SELL,
|
||||
ECON_STEAL,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
struct order *ord;
|
||||
} recruit;
|
||||
struct {
|
||||
int no;
|
||||
bool goblin; /* stealing */
|
||||
|
@ -66,18 +73,19 @@ extern "C" {
|
|||
struct {
|
||||
const struct luxury_type *ltype; /* trading */
|
||||
} trade;
|
||||
} type;
|
||||
} data;
|
||||
} econ_request;
|
||||
|
||||
int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
|
||||
|
||||
int income(const struct unit *u);
|
||||
int entertainmoney(const struct region *r);
|
||||
|
||||
void economics(struct region *r);
|
||||
void destroy(struct region *r);
|
||||
void produce(struct region *r);
|
||||
void auto_work(struct region *r);
|
||||
|
||||
int expand_production(struct region * r, struct econ_request * requests, struct econ_request ***results);
|
||||
|
||||
typedef enum income_t { IC_WORK, IC_ENTERTAIN, IC_TAX, IC_TRADE, IC_TRADETAX, IC_STEAL, IC_MAGIC, IC_LOOT } income_t;
|
||||
void add_income(struct unit * u, income_t type, int want, int qty);
|
||||
|
||||
|
@ -95,9 +103,6 @@ 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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
#endif
|
||||
#include <kernel/config.h>
|
||||
#include "economy.h"
|
||||
#include "recruit.h"
|
||||
|
||||
#include <util/message.h>
|
||||
#include <kernel/attrib.h>
|
||||
#include <kernel/building.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/calendar.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/messages.h>
|
||||
#include <kernel/order.h>
|
||||
#include <kernel/pool.h>
|
||||
|
@ -19,9 +21,9 @@
|
|||
#include <kernel/terrainid.h>
|
||||
#include <kernel/unit.h>
|
||||
|
||||
#include <kernel/attrib.h>
|
||||
#include <util/language.h>
|
||||
#include <util/macros.h>
|
||||
#include <util/message.h>
|
||||
|
||||
#include <CuTest.h>
|
||||
#include <tests.h>
|
||||
|
@ -162,7 +164,7 @@ static void test_heroes_dont_recruit(CuTest * tc) {
|
|||
fset(u, UFL_HERO);
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 1, u->number);
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "error_herorecruit"));
|
||||
|
@ -178,7 +180,7 @@ static void test_normals_recruit(CuTest * tc) {
|
|||
u = create_recruiter();
|
||||
unit_addorder(u, create_order(K_RECRUIT, default_locale, "1"));
|
||||
|
||||
economics(u->region);
|
||||
recruit(u->region);
|
||||
|
||||
CuAssertIntEquals(tc, 2, u->number);
|
||||
|
||||
|
@ -493,6 +495,7 @@ static void test_recruit_insect(CuTest *tc) {
|
|||
u = test_create_unit(f, test_create_region(0, 0, NULL));
|
||||
u->thisorder = create_order(K_RECRUIT, f->locale, "%d", 1);
|
||||
|
||||
CuAssertIntEquals(tc, SEASON_AUTUMN, calendar_season(1083));
|
||||
msg = can_recruit(u, f->race, u->thisorder, 1083); /* Autumn */
|
||||
CuAssertPtrEquals(tc, NULL, msg);
|
||||
|
||||
|
|
60
src/gmtool.c
60
src/gmtool.c
|
@ -580,12 +580,49 @@ static void reset_region(region *r) {
|
|||
}
|
||||
}
|
||||
|
||||
static void reset_cursor(state *st) {
|
||||
static region * state_region(state *st) {
|
||||
int nx = st->cursor.x;
|
||||
int ny = st->cursor.y;
|
||||
region *r;
|
||||
pnormalize(&nx, &ny, st->cursor.pl);
|
||||
if ((r = findregion(nx, ny)) != NULL) {
|
||||
return findregion(nx, ny);
|
||||
}
|
||||
|
||||
static void reset_area_cb(void *arg) {
|
||||
region *r = (region *)arg;
|
||||
r->age = 0;
|
||||
freset(r, RF_MARK);
|
||||
}
|
||||
|
||||
static void reset_area(state *st) {
|
||||
region * r = state_region(st);
|
||||
if (r != NULL) {
|
||||
selist * ql = NULL;
|
||||
int qi = 0, qlen = 0;
|
||||
fset(r, RF_MARK);
|
||||
selist_insert(&ql, qlen++, r);
|
||||
while (qi != qlen) {
|
||||
int i;
|
||||
region *adj[MAXDIRECTIONS];
|
||||
r = selist_get(ql, qi++);
|
||||
get_neighbours(r, adj);
|
||||
for (i = 0; i != MAXDIRECTIONS; ++i) {
|
||||
region *rn = adj[i];
|
||||
if (rn && !fval(rn, RF_MARK)) {
|
||||
if ((rn->terrain->flags & FORBIDDEN_REGION) == 0) {
|
||||
fset(rn, RF_MARK);
|
||||
selist_insert(&ql, qlen++, rn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
selist_foreach(ql, reset_area_cb);
|
||||
selist_free(ql);
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_cursor(state *st) {
|
||||
region * r = state_region(st);
|
||||
if (r != NULL) {
|
||||
reset_region(r);
|
||||
}
|
||||
}
|
||||
|
@ -1072,6 +1109,16 @@ static void seed_player(state *st, const newfaction *player) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool confirm(WINDOW * win, const char *q) {
|
||||
int ch;
|
||||
werase(win);
|
||||
mvwaddstr(win, 0, 0, (char *)q);
|
||||
wmove(win, 0, (int)(strlen(q) + 1));
|
||||
ch = wgetch(win);
|
||||
return (ch == 'y') || (ch == 'Y');
|
||||
}
|
||||
|
||||
|
||||
static void handlekey(state * st, int c)
|
||||
{
|
||||
window *wnd;
|
||||
|
@ -1157,6 +1204,13 @@ static void handlekey(state * st, int c)
|
|||
st->wnd_status->update |= 1;
|
||||
st->wnd_map->update |= 1;
|
||||
break;
|
||||
case 'A': /* clear/reset area */
|
||||
if (confirm(st->wnd_status->handle, "Are you sure you want to reset this entire area?")) {
|
||||
reset_area(st);
|
||||
st->modified = 1;
|
||||
st->wnd_map->update |= 1;
|
||||
}
|
||||
break;
|
||||
case 'c': /* clear/reset */
|
||||
reset_cursor(st);
|
||||
st->modified = 1;
|
||||
|
|
|
@ -792,7 +792,7 @@ static void json_calendar(cJSON *json) {
|
|||
for (i = 0, jmonth = child->child; jmonth; jmonth = jmonth->next, ++i) {
|
||||
if (jmonth->type == cJSON_Object) {
|
||||
storms[i] = cJSON_GetObjectItem(jmonth, "storm")->valueint;
|
||||
month_season[i] = cJSON_GetObjectItem(jmonth, "season")->valueint;
|
||||
month_season[i] = (season_t) cJSON_GetObjectItem(jmonth, "season")->valueint;
|
||||
}
|
||||
else {
|
||||
log_error("calendar.months[%d] is not an object: %d", i, json->type);
|
||||
|
|
|
@ -160,8 +160,8 @@ static void test_calendar(CuTest * tc)
|
|||
CuAssertIntEquals(tc, 99, storms[0]);
|
||||
CuAssertIntEquals(tc, 22, storms[1]);
|
||||
CuAssertPtrNotNull(tc, month_season);
|
||||
CuAssertIntEquals(tc, 1, month_season[0]);
|
||||
CuAssertIntEquals(tc, 2, month_season[1]);
|
||||
CuAssertIntEquals(tc, SEASON_SPRING, month_season[0]);
|
||||
CuAssertIntEquals(tc, SEASON_SUMMER, month_season[1]);
|
||||
cJSON_Delete(json);
|
||||
test_teardown();
|
||||
}
|
||||
|
|
|
@ -238,22 +238,6 @@ autoalliance(const faction * sf, const faction * f2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void init_npcfaction(variant *var)
|
||||
{
|
||||
var->i = 1;
|
||||
}
|
||||
|
||||
attrib_type at_npcfaction = {
|
||||
"npcfaction",
|
||||
init_npcfaction,
|
||||
NULL,
|
||||
NULL,
|
||||
a_writeint,
|
||||
a_readint,
|
||||
NULL,
|
||||
ATF_UNIQUE
|
||||
};
|
||||
|
||||
/** Limits the available help modes
|
||||
* The bitfield returned by this function specifies the available help modes
|
||||
* in this game (so you can, for example, disable HELP GIVE globally).
|
||||
|
@ -307,12 +291,6 @@ int alliance_status(const faction *f, const faction *f2, int status) {
|
|||
if (status > 0) {
|
||||
int mask = AllianceRestricted();
|
||||
if (mask) {
|
||||
if (a_find(f->attribs, &at_npcfaction)) {
|
||||
return status;
|
||||
}
|
||||
if (a_find(f2->attribs, &at_npcfaction)) {
|
||||
return status;
|
||||
}
|
||||
if (f->alliance != f2->alliance) {
|
||||
status &= ~mask;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ int months_per_year = 12;
|
|||
const char *seasonnames[CALENDAR_SEASONS] = { "winter", "spring", "summer", "fall" };
|
||||
char **weeknames = NULL;
|
||||
char **weeknames2 = NULL;
|
||||
int *month_season = NULL;
|
||||
season_t *month_season = NULL;
|
||||
|
||||
const char *calendar_month(int index)
|
||||
{
|
||||
|
@ -27,6 +27,13 @@ const char *calendar_month(int index)
|
|||
return result;
|
||||
}
|
||||
|
||||
season_t calendar_season(int now) {
|
||||
gamedate date;
|
||||
get_gamedate(now, &date);
|
||||
|
||||
return date.season;
|
||||
}
|
||||
|
||||
const char *calendar_era(void)
|
||||
{
|
||||
static char result[20];
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
typedef enum season_t {
|
||||
SEASON_WINTER,
|
||||
SEASON_SPRING,
|
||||
SEASON_SUMMER,
|
||||
SEASON_AUTUMN
|
||||
};
|
||||
} season_t;
|
||||
#define CALENDAR_SEASONS 4
|
||||
extern const char *seasonnames[CALENDAR_SEASONS];
|
||||
|
||||
extern int months_per_year;
|
||||
extern int *month_season;
|
||||
extern season_t *month_season;
|
||||
extern int first_month;
|
||||
extern int turn;
|
||||
|
||||
|
@ -26,16 +26,18 @@ extern "C" {
|
|||
typedef struct gamedate {
|
||||
int turn;
|
||||
int year;
|
||||
int season;
|
||||
season_t season;
|
||||
int month;
|
||||
int week;
|
||||
} gamedate;
|
||||
|
||||
const gamedate *get_gamedate(int turn, gamedate * gd);
|
||||
void calendar_cleanup(void);
|
||||
const char *calendar_month(int index);
|
||||
const char *calendar_era(void);
|
||||
int first_turn(void);
|
||||
const gamedate *get_gamedate(int turn, gamedate * gd);
|
||||
season_t calendar_season(int turn);
|
||||
|
||||
void calendar_cleanup(void);
|
||||
const char *calendar_month(int index);
|
||||
const char *calendar_era(void);
|
||||
int first_turn(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -65,26 +65,59 @@ static void test_calendar(CuTest * tc)
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void setup_calendar(void) {
|
||||
int i;
|
||||
|
||||
months_per_year = 4;
|
||||
weeks_per_month = 2;
|
||||
free(month_season);
|
||||
month_season = calloc(months_per_year, sizeof(season_t));
|
||||
for (i = 0; i != 4; ++i) {
|
||||
month_season[i] = (season_t)i;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_calendar_season(CuTest * tc)
|
||||
{
|
||||
test_setup();
|
||||
setup_calendar();
|
||||
|
||||
CuAssertIntEquals(tc, SEASON_WINTER, calendar_season(0));
|
||||
CuAssertIntEquals(tc, SEASON_WINTER, calendar_season(1));
|
||||
CuAssertIntEquals(tc, SEASON_SPRING, calendar_season(2));
|
||||
CuAssertIntEquals(tc, SEASON_SPRING, calendar_season(3));
|
||||
CuAssertIntEquals(tc, SEASON_SUMMER, calendar_season(4));
|
||||
CuAssertIntEquals(tc, SEASON_SUMMER, calendar_season(5));
|
||||
CuAssertIntEquals(tc, SEASON_AUTUMN, calendar_season(6));
|
||||
CuAssertIntEquals(tc, SEASON_AUTUMN, calendar_season(7));
|
||||
CuAssertIntEquals(tc, SEASON_WINTER, calendar_season(8));
|
||||
|
||||
free(month_season);
|
||||
month_season = NULL;
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_gamedate(CuTest * tc)
|
||||
{
|
||||
gamedate gd;
|
||||
|
||||
test_setup();
|
||||
month_season = calloc(months_per_year, sizeof(int));
|
||||
setup_calendar();
|
||||
|
||||
get_gamedate(0, &gd);
|
||||
CuAssertIntEquals(tc, 1, gd.year);
|
||||
CuAssertIntEquals(tc, 0, gd.season);
|
||||
CuAssertIntEquals(tc, SEASON_WINTER, gd.season);
|
||||
CuAssertIntEquals(tc, 0, gd.month);
|
||||
CuAssertIntEquals(tc, 0, gd.week);
|
||||
|
||||
month_season[1] = 1;
|
||||
get_gamedate(weeks_per_month + 1, &gd);
|
||||
CuAssertIntEquals(tc, 1, gd.year);
|
||||
CuAssertIntEquals(tc, 1, gd.season);
|
||||
CuAssertIntEquals(tc, SEASON_SPRING, gd.season);
|
||||
CuAssertIntEquals(tc, 1, gd.month);
|
||||
CuAssertIntEquals(tc, 1, gd.week);
|
||||
|
||||
free(month_season);
|
||||
month_season = NULL;
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
|
@ -94,5 +127,6 @@ CuSuite *get_calendar_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_calendar_config);
|
||||
SUITE_ADD_TEST(suite, test_calendar);
|
||||
SUITE_ADD_TEST(suite, test_calendar_season);
|
||||
SUITE_ADD_TEST(suite, test_gamedate);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ unit *addplayer(region * r, faction * f)
|
|||
} while (rc == NULL || urc == RC_DAEMON || !playerrace(rc));
|
||||
u->irace = rc;
|
||||
}
|
||||
f->lastorders = 0;
|
||||
f->lastorders = turn;
|
||||
return u;
|
||||
}
|
||||
|
||||
|
|
|
@ -344,6 +344,7 @@ static void test_addplayer(CuTest *tc) {
|
|||
CuAssertPtrNotNull(tc, u);
|
||||
CuAssertPtrEquals(tc, r, u->region);
|
||||
CuAssertPtrEquals(tc, f, u->faction);
|
||||
CuAssertIntEquals(tc, turn, u->faction->lastorders);
|
||||
CuAssertIntEquals(tc, i_get(u->items, itype), 10);
|
||||
CuAssertPtrNotNull(tc, u->orders);
|
||||
CuAssertIntEquals(tc, K_WORK, getkeyword(u->orders));
|
||||
|
|
|
@ -49,7 +49,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
static group *ghash[GMAXHASH];
|
||||
static int maxgid;
|
||||
|
||||
group *new_group(faction * f, const char *name, int gid)
|
||||
group *create_group(faction * f, const char *name, int gid)
|
||||
{
|
||||
group **gp = &f->groups;
|
||||
int index = gid % GMAXHASH;
|
||||
|
@ -63,6 +63,7 @@ group *new_group(faction * f, const char *name, int gid)
|
|||
if (gid > maxgid) maxgid = gid;
|
||||
g->name = str_strdup(name);
|
||||
g->gid = gid;
|
||||
g->f = f;
|
||||
|
||||
g->nexthash = ghash[index];
|
||||
return ghash[index] = g;
|
||||
|
@ -93,11 +94,15 @@ static int read_group(variant *var, void *owner, gamedata *data)
|
|||
{
|
||||
struct storage *store = data->store;
|
||||
group *g;
|
||||
unit * u = (unit *)owner;
|
||||
int gid;
|
||||
|
||||
READ_INT(store, &gid);
|
||||
var->v = g = find_group(gid);
|
||||
if (g != 0) {
|
||||
if (g != NULL) {
|
||||
if (g->f != u->faction) {
|
||||
return AT_READ_FAIL;
|
||||
}
|
||||
g->members++;
|
||||
return AT_READ_OK;
|
||||
}
|
||||
|
@ -184,7 +189,7 @@ group *join_group(unit * u, const char *name)
|
|||
if (name && name[0]) {
|
||||
g = find_groupbyname(u->faction->groups, name);
|
||||
if (g == NULL) {
|
||||
g = new_group(u->faction, name, ++maxgid);
|
||||
g = create_group(u->faction, name, ++maxgid);
|
||||
init_group(u->faction, g);
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +224,7 @@ void read_groups(gamedata *data, faction * f)
|
|||
if (gid == 0)
|
||||
break;
|
||||
READ_STR(store, buf, sizeof(buf));
|
||||
g = new_group(f, buf, gid);
|
||||
g = create_group(f, buf, gid);
|
||||
read_allies(data, &g->allies);
|
||||
read_attribs(data, &g->attribs, g);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ extern "C" {
|
|||
extern void set_group(struct unit *u, struct group *g);
|
||||
extern struct group * get_group(const struct unit *u);
|
||||
extern void free_group(struct group *g);
|
||||
struct group *new_group(struct faction * f, const char *name, int gid);
|
||||
struct group *create_group(struct faction * f, const char *name, int gid);
|
||||
|
||||
extern void write_groups(struct gamedata *data, const struct faction *f);
|
||||
extern void read_groups(struct gamedata *data, struct faction *f);
|
||||
|
|
|
@ -80,8 +80,10 @@ static void test_group_readwrite(CuTest * tc)
|
|||
mstream_init(&data.strm);
|
||||
gamedata_init(&data, &store, RELEASE_VERSION);
|
||||
f = test_create_faction(NULL);
|
||||
new_group(f, "NW", 42);
|
||||
g = new_group(f, "Egoisten", 43);
|
||||
create_group(f, "NW", 42);
|
||||
g = create_group(f, "Egoisten", 43);
|
||||
CuAssertPtrEquals(tc, f, g->f);
|
||||
CuAssertStrEquals(tc, "Egoisten", g->name);
|
||||
key_set(&g->attribs, 44, 44);
|
||||
ally_set(&g->allies, f, HELP_GIVE);
|
||||
write_groups(&data, f);
|
||||
|
|
|
@ -141,11 +141,10 @@ static region **internal_path_find(region * handle_start, const region * target,
|
|||
int maxlen, bool(*allowed) (const region *, const region *))
|
||||
{
|
||||
static region *path[MAXDEPTH + 2]; /* STATIC_RETURN: used for return, not across calls */
|
||||
direction_t d;
|
||||
direction_t d = MAXDIRECTIONS;
|
||||
node *root = new_node(handle_start, 0, NULL);
|
||||
node **handle_end = &root->next;
|
||||
node *n = root;
|
||||
bool found = false;
|
||||
assert(maxlen <= MAXDEPTH);
|
||||
fset(handle_start, RF_MARK);
|
||||
|
||||
|
@ -156,36 +155,33 @@ static region **internal_path_find(region * handle_start, const region * target,
|
|||
break;
|
||||
for (d = 0; d != MAXDIRECTIONS; ++d) {
|
||||
region *rn = rconnect(r, d);
|
||||
if (rn == NULL)
|
||||
continue;
|
||||
if (fval(rn, RF_MARK))
|
||||
continue; /* already been there */
|
||||
if (!allowed(r, rn))
|
||||
continue; /* can't go there */
|
||||
if (rn == target) {
|
||||
int i = depth;
|
||||
path[i + 1] = NULL;
|
||||
path[i] = rn;
|
||||
while (n) {
|
||||
path[--i] = n->r;
|
||||
n = n->prev;
|
||||
if (rn && !fval(rn, RF_MARK) && allowed(r, rn)) {
|
||||
if (rn == target) {
|
||||
int i = depth;
|
||||
path[i + 1] = NULL;
|
||||
path[i] = rn;
|
||||
while (n) {
|
||||
path[--i] = n->r;
|
||||
n = n->prev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fset(rn, RF_MARK);
|
||||
*handle_end = new_node(rn, depth, n);
|
||||
handle_end = &(*handle_end)->next;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fset(rn, RF_MARK);
|
||||
*handle_end = new_node(rn, depth, n);
|
||||
handle_end = &(*handle_end)->next;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
if (d != MAXDIRECTIONS) {
|
||||
break;
|
||||
}
|
||||
n = n->next;
|
||||
}
|
||||
free_nodes(root);
|
||||
if (found)
|
||||
if (d != MAXDIRECTIONS) {
|
||||
return path;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -625,7 +625,11 @@ void rsetpeasants(region * r, int value)
|
|||
assert(r->land || value==0);
|
||||
assert(value >= 0);
|
||||
if (r->land) {
|
||||
r->land->peasants = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d peasants.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->peasants = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,7 +643,11 @@ void rsethorses(const region * r, int value)
|
|||
assert(r->land || value==0);
|
||||
assert(value >= 0);
|
||||
if (r->land) {
|
||||
r->land->horses = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d horses.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->horses = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,15 +667,18 @@ void rsetmoney(region * r, int value)
|
|||
|
||||
int rherbs(const region *r)
|
||||
{
|
||||
return r->land?r->land->herbs:0;
|
||||
return r->land ? r->land->herbs : 0;
|
||||
}
|
||||
|
||||
void rsetherbs(region *r, int value)
|
||||
{
|
||||
assert(r->land || value==0);
|
||||
assert(value >= 0 && value<=SHRT_MAX);
|
||||
if (r->land) {
|
||||
r->land->herbs = value;
|
||||
if (value > USHRT_MAX) {
|
||||
log_warning("region %s cannot have %d herbs.", regionname(r, NULL), value);
|
||||
value = USHRT_MAX;
|
||||
}
|
||||
r->land->herbs = (unsigned short)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1478,7 +1489,7 @@ const char *region_getname(const region * r)
|
|||
int region_get_morale(const region * r)
|
||||
{
|
||||
if (r->land) {
|
||||
assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
|
||||
assert(r->land->morale <= MORALE_MAX);
|
||||
return r->land->morale;
|
||||
}
|
||||
return -1;
|
||||
|
@ -1487,11 +1498,11 @@ int region_get_morale(const region * r)
|
|||
void region_set_morale(region * r, int morale, int turn)
|
||||
{
|
||||
if (r->land) {
|
||||
r->land->morale = morale;
|
||||
r->land->morale = (unsigned short)morale;
|
||||
if (turn >= 0 && r->land->ownership) {
|
||||
r->land->ownership->morale_turn = turn;
|
||||
}
|
||||
assert(r->land->morale >= 0 && r->land->morale <= MORALE_MAX);
|
||||
assert(r->land->morale <= MORALE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,12 +97,12 @@ extern "C" {
|
|||
char *display;
|
||||
demand *demands;
|
||||
const struct item_type *herbtype;
|
||||
int herbs;
|
||||
int morale;
|
||||
unsigned short horses;
|
||||
unsigned short herbs;
|
||||
unsigned short peasants;
|
||||
unsigned short morale;
|
||||
short newpeasants;
|
||||
int trees[3]; /* 0 -> seeds, 1 -> shoots, 2 -> trees */
|
||||
int horses;
|
||||
int peasants;
|
||||
int newpeasants;
|
||||
int money;
|
||||
struct region_owner *ownership;
|
||||
} land_region;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "save.h"
|
||||
/*
|
||||
Copyright (c) 1998-2019, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
|
@ -630,7 +631,6 @@ static void fix_resource_levels(region *r) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1474,61 +1474,15 @@ static void fix_familiars(void (*callback)(unit *)) {
|
|||
}
|
||||
}
|
||||
|
||||
int read_game(gamedata *data)
|
||||
{
|
||||
int p, nread;
|
||||
faction *f, **fp;
|
||||
region *r;
|
||||
unit *u;
|
||||
static void read_regions(gamedata *data) {
|
||||
storage * store = data->store;
|
||||
const struct building_type *bt_lighthouse = bt_find("lighthouse");
|
||||
const struct race *rc_spell = rc_find("spell");
|
||||
|
||||
if (data->version >= SAVEGAMEID_VERSION) {
|
||||
int gameid;
|
||||
|
||||
READ_INT(store, &gameid);
|
||||
if (gameid != game_id()) {
|
||||
log_warning("game mismatch: datafile contains game %d, but config is for %d", gameid, game_id());
|
||||
}
|
||||
}
|
||||
else {
|
||||
READ_STR(store, NULL, 0);
|
||||
}
|
||||
|
||||
if (data->version < FIXATKEYS_VERSION) {
|
||||
attrib *a = NULL;
|
||||
read_attribs(data, &a, NULL);
|
||||
a_removeall(&a, NULL);
|
||||
}
|
||||
|
||||
READ_INT(store, &turn);
|
||||
log_debug(" - reading turn %d", turn);
|
||||
rng_init(turn + config_get_int("game.seed", 0));
|
||||
READ_INT(store, NULL); /* max_unique_id = ignore */
|
||||
READ_INT(store, &nextborder);
|
||||
|
||||
read_planes(data);
|
||||
read_alliances(data);
|
||||
READ_INT(store, &nread);
|
||||
log_debug(" - Einzulesende Parteien: %d\n", nread);
|
||||
fp = &factions;
|
||||
while (*fp) {
|
||||
fp = &(*fp)->next;
|
||||
}
|
||||
|
||||
while (--nread >= 0) {
|
||||
faction *f = read_faction(data);
|
||||
|
||||
*fp = f;
|
||||
fp = &f->next;
|
||||
}
|
||||
*fp = 0;
|
||||
|
||||
/* Regionen */
|
||||
region *r;
|
||||
int nread;
|
||||
|
||||
READ_INT(store, &nread);
|
||||
assert(nread < MAXREGIONS && nread >=0);
|
||||
assert(nread < MAXREGIONS && nread >= 0);
|
||||
|
||||
log_debug(" - Einzulesende Regionen: %d", nread);
|
||||
|
||||
|
@ -1536,13 +1490,14 @@ int read_game(gamedata *data)
|
|||
unit **up;
|
||||
building **bp;
|
||||
ship **shp;
|
||||
int p;
|
||||
|
||||
r = read_region(data);
|
||||
|
||||
/* Burgen */
|
||||
READ_INT(store, &p);
|
||||
if (p > 0 && !r->land) {
|
||||
log_debug("%s, uid=%d has %d %s", regionname(r, NULL), r->uid, p, (p==1) ? "building" : "buildings");
|
||||
log_debug("%s, uid=%d has %d %s", regionname(r, NULL), r->uid, p, (p == 1) ? "building" : "buildings");
|
||||
}
|
||||
bp = &r->buildings;
|
||||
|
||||
|
@ -1596,7 +1551,6 @@ int read_game(gamedata *data)
|
|||
}
|
||||
}
|
||||
}
|
||||
read_borders(data);
|
||||
|
||||
log_debug("updating area information for lighthouses.");
|
||||
for (r = regions; r; r = r->next) {
|
||||
|
@ -1609,8 +1563,12 @@ int read_game(gamedata *data)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_factions(int data_version)
|
||||
{
|
||||
log_debug("marking factions as alive.");
|
||||
for (f = factions; f; f = f->next) {
|
||||
for (faction *f = factions; f; f = f->next) {
|
||||
if (f->flags & FFL_NPC) {
|
||||
f->_alive = true;
|
||||
f->magiegebiet = M_GRAY;
|
||||
|
@ -1624,8 +1582,8 @@ int read_game(gamedata *data)
|
|||
}
|
||||
else {
|
||||
assert(f->units);
|
||||
for (u = f->units; u; u = u->nextF) {
|
||||
if (data->version < SPELL_LEVEL_VERSION) {
|
||||
for (unit *u = f->units; u; u = u->nextF) {
|
||||
if (data_version < SPELL_LEVEL_VERSION) {
|
||||
struct sc_mage *mage = get_mage(u);
|
||||
if (mage) {
|
||||
faction *f = u->faction;
|
||||
|
@ -1642,16 +1600,76 @@ int read_game(gamedata *data)
|
|||
}
|
||||
if (u->number > 0) {
|
||||
f->_alive = true;
|
||||
if (data->version >= SPELL_LEVEL_VERSION) {
|
||||
if (data_version >= SPELL_LEVEL_VERSION) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data->version < SPELL_LEVEL_VERSION && f->spellbook) {
|
||||
if (data_version < SPELL_LEVEL_VERSION && f->spellbook) {
|
||||
spellbook_foreach(f->spellbook, cb_sb_maxlevel, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void read_factions(gamedata * data)
|
||||
{
|
||||
storage * store = data->store;
|
||||
int nread;
|
||||
faction **fp;
|
||||
READ_INT(store, &nread);
|
||||
log_debug(" - Einzulesende Parteien: %d\n", nread);
|
||||
fp = &factions;
|
||||
while (*fp) {
|
||||
fp = &(*fp)->next;
|
||||
}
|
||||
|
||||
while (--nread >= 0) {
|
||||
faction *f = read_faction(data);
|
||||
|
||||
*fp = f;
|
||||
fp = &f->next;
|
||||
}
|
||||
}
|
||||
|
||||
int read_game(gamedata *data)
|
||||
{
|
||||
storage * store = data->store;
|
||||
|
||||
if (data->version >= SAVEGAMEID_VERSION) {
|
||||
int gameid;
|
||||
|
||||
READ_INT(store, &gameid);
|
||||
if (gameid != game_id()) {
|
||||
log_warning("game mismatch: datafile contains game %d, but config is for %d", gameid, game_id());
|
||||
}
|
||||
}
|
||||
else {
|
||||
READ_STR(store, NULL, 0);
|
||||
}
|
||||
|
||||
if (data->version < FIXATKEYS_VERSION) {
|
||||
attrib *a = NULL;
|
||||
read_attribs(data, &a, NULL);
|
||||
a_removeall(&a, NULL);
|
||||
}
|
||||
|
||||
READ_INT(store, &turn);
|
||||
log_debug(" - reading turn %d", turn);
|
||||
rng_init(turn + config_get_int("game.seed", 0));
|
||||
READ_INT(store, NULL); /* max_unique_id = ignore */
|
||||
READ_INT(store, &nextborder);
|
||||
|
||||
read_planes(data);
|
||||
read_alliances(data);
|
||||
|
||||
read_factions(data);
|
||||
|
||||
/* Regionen */
|
||||
|
||||
read_regions(data);
|
||||
read_borders(data);
|
||||
init_factions(data->version);
|
||||
if (data->version < FIX_CLONES_VERSION) {
|
||||
fix_clones();
|
||||
}
|
||||
|
|
|
@ -318,7 +318,10 @@ int crew_skill(const ship *sh) {
|
|||
|
||||
for (u = sh->region->units; u; u = u->next) {
|
||||
if (u->ship == sh) {
|
||||
n += effskill(u, SK_SAILING, NULL) * u->number;
|
||||
int es = effskill(u, SK_SAILING, NULL);
|
||||
if (es >= sh->type->minskill) {
|
||||
n += es * u->number;
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
|
|
|
@ -384,27 +384,6 @@ static void test_stype_defaults(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_crew_skill(CuTest *tc) {
|
||||
ship *sh;
|
||||
region *r;
|
||||
struct faction *f;
|
||||
int i;
|
||||
|
||||
test_setup();
|
||||
test_create_world();
|
||||
r = test_create_region(0, 0, NULL);
|
||||
f = test_create_faction(NULL);
|
||||
assert(r && f);
|
||||
sh = test_create_ship(r, st_find("boat"));
|
||||
for (i = 0; i != 4; ++i) {
|
||||
unit * u = test_create_unit(f, r);
|
||||
set_level(u, SK_SAILING, 5);
|
||||
u->ship = sh;
|
||||
}
|
||||
CuAssertIntEquals(tc, 20, crew_skill(sh));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static ship *setup_ship(void) {
|
||||
region *r;
|
||||
ship_type *stype;
|
||||
|
@ -651,6 +630,35 @@ static void test_shipspeed_max_range(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_crew_skill(CuTest *tc) {
|
||||
ship_type *stype;
|
||||
ship * sh;
|
||||
unit *u;
|
||||
region *r;
|
||||
|
||||
test_setup();
|
||||
stype = test_create_shiptype("kayak");
|
||||
CuAssertIntEquals(tc, 1, stype->minskill);
|
||||
r = test_create_ocean(0, 0);
|
||||
sh = test_create_ship(r, stype);
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
set_level(u, SK_SAILING, 1);
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
u_set_ship(u, sh);
|
||||
set_level(u, SK_SAILING, 1);
|
||||
CuAssertIntEquals(tc, 1, crew_skill(sh));
|
||||
set_number(u, 10);
|
||||
CuAssertIntEquals(tc, 10, crew_skill(sh));
|
||||
stype->minskill = 2;
|
||||
CuAssertIntEquals(tc, 0, crew_skill(sh));
|
||||
set_level(u, SK_SAILING, 2);
|
||||
CuAssertIntEquals(tc, 20, crew_skill(sh));
|
||||
set_level(u, SK_SAILING, 3);
|
||||
CuAssertIntEquals(tc, 30, crew_skill(sh));
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_ship_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
|
|
|
@ -59,6 +59,7 @@ struct weapon_type;
|
|||
typedef enum {
|
||||
seen_none,
|
||||
seen_neighbour,
|
||||
seen_lighthouse_land,
|
||||
seen_lighthouse,
|
||||
seen_travel,
|
||||
seen_unit,
|
||||
|
|
|
@ -913,7 +913,7 @@ void u_setfaction(unit * u, faction * f)
|
|||
--u->faction->num_units;
|
||||
u->faction->num_people -= u->number;
|
||||
}
|
||||
join_group(u, NULL);
|
||||
set_group(u, NULL);
|
||||
free_orders(&u->orders);
|
||||
set_order(&u->thisorder, NULL);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <platform.h>
|
||||
#include <kernel/ally.h>
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/curse.h>
|
||||
#include <kernel/item.h>
|
||||
|
@ -652,6 +652,62 @@ static void test_get_modifier(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_gift_items(CuTest *tc) {
|
||||
unit *u, *u1, *u2;
|
||||
region *r;
|
||||
const resource_type *rtype;
|
||||
test_setup();
|
||||
init_resources();
|
||||
r = test_create_plain(0, 0);
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
rtype = get_resourcetype(R_SILVER);
|
||||
region_setresource(r, rtype, 0);
|
||||
i_change(&u->items, rtype->itype, 10);
|
||||
gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS | GIFT_SELF);
|
||||
CuAssertIntEquals(tc, 10, region_getresource(r, rtype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
|
||||
|
||||
region_setresource(r, get_resourcetype(R_HORSE), 0);
|
||||
region_setresource(r, rtype, 0);
|
||||
i_change(&u->items, rtype->itype, 10);
|
||||
i_change(&u->items, get_resourcetype(R_HORSE)->itype, 20);
|
||||
u1 = test_create_unit(test_create_faction(NULL), r);
|
||||
u2 = test_create_unit(u1->faction, r);
|
||||
gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS | GIFT_SELF);
|
||||
CuAssertIntEquals(tc, 20, region_getresource(r, get_resourcetype(R_HORSE)));
|
||||
CuAssertIntEquals(tc, 10, region_getresource(r, rtype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u1->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u2->items, rtype->itype));
|
||||
|
||||
region_setresource(r, rtype, 0);
|
||||
i_change(&u->items, rtype->itype, 10);
|
||||
ally_set(&u->faction->allies, u1->faction, HELP_MONEY);
|
||||
ally_set(&u1->faction->allies, u->faction, HELP_GIVE);
|
||||
gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS | GIFT_SELF);
|
||||
CuAssertIntEquals(tc, 0, region_getresource(r, rtype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 10, i_get(u1->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u2->items, rtype->itype));
|
||||
i_change(&u1->items, rtype->itype, -10);
|
||||
|
||||
set_number(u1, 2);
|
||||
u_setfaction(u2, test_create_faction(NULL));
|
||||
ally_set(&u->faction->allies, u2->faction, HELP_MONEY);
|
||||
ally_set(&u2->faction->allies, u->faction, HELP_GIVE);
|
||||
region_setresource(r, rtype, 0);
|
||||
i_change(&u->items, rtype->itype, 15);
|
||||
ally_set(&u->faction->allies, u1->faction, HELP_MONEY);
|
||||
ally_set(&u1->faction->allies, u->faction, HELP_GIVE);
|
||||
gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS | GIFT_SELF);
|
||||
CuAssertIntEquals(tc, 0, region_getresource(r, rtype));
|
||||
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 10, i_get(u1->items, rtype->itype));
|
||||
CuAssertIntEquals(tc, 5, i_get(u2->items, rtype->itype));
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
CuSuite *get_unit_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
|
@ -683,5 +739,6 @@ CuSuite *get_unit_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_name_unit);
|
||||
SUITE_ADD_TEST(suite, test_heal_factor);
|
||||
SUITE_ADD_TEST(suite, test_get_modifier);
|
||||
SUITE_ADD_TEST(suite, test_gift_items);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#ifndef ERESSEA_VERSION
|
||||
/* the version number, if it was not passed to make with -D */
|
||||
#define ERESSEA_VERSION "3.20.0"
|
||||
#define ERESSEA_VERSION "3.21.0"
|
||||
#endif
|
||||
|
||||
const char *eressea_version(void) {
|
||||
|
|
53
src/laws.c
53
src/laws.c
|
@ -35,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "monsters.h"
|
||||
#include "move.h"
|
||||
#include "randenc.h"
|
||||
#include "recruit.h"
|
||||
#include "renumber.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
|
@ -280,14 +281,13 @@ static void live(region * r)
|
|||
#define MAX_EMIGRATION(p) ((p)/MAXDIRECTIONS)
|
||||
#define MAX_IMMIGRATION(p) ((p)*2/3)
|
||||
|
||||
static void calculate_emigration(region * r)
|
||||
void peasant_migration(region * r)
|
||||
{
|
||||
int i;
|
||||
int maxp = region_maxworkers(r);
|
||||
int rp = rpeasants(r);
|
||||
int max_immigrants = MAX_IMMIGRATION(maxp - rp);
|
||||
|
||||
|
||||
if (volcano_module()) {
|
||||
static int terrain_cache;
|
||||
static const terrain_type *t_volcano;
|
||||
|
@ -313,8 +313,14 @@ static void calculate_emigration(region * r)
|
|||
|
||||
if (max_emigration > 0) {
|
||||
if (max_emigration > max_immigrants) max_emigration = max_immigrants;
|
||||
r->land->newpeasants += max_emigration;
|
||||
rc->land->newpeasants -= max_emigration;
|
||||
if (max_emigration + r->land->newpeasants > USHRT_MAX) {
|
||||
max_emigration = USHRT_MAX - r->land->newpeasants;
|
||||
}
|
||||
if (max_emigration + rc->land->newpeasants > USHRT_MAX) {
|
||||
max_emigration = USHRT_MAX - rc->land->newpeasants;
|
||||
}
|
||||
r->land->newpeasants += (short)max_emigration;
|
||||
rc->land->newpeasants -= (short)max_emigration;
|
||||
max_immigrants -= max_emigration;
|
||||
}
|
||||
}
|
||||
|
@ -607,7 +613,7 @@ growing_trees_e3(region * r, const int current_season,
|
|||
}
|
||||
|
||||
static void
|
||||
growing_trees(region * r, const int current_season, const int last_weeks_season)
|
||||
growing_trees(region * r, const season_t current_season, const season_t last_weeks_season)
|
||||
{
|
||||
int grownup_trees, i, seeds, sprout;
|
||||
attrib *a;
|
||||
|
@ -749,7 +755,7 @@ growing_trees(region * r, const int current_season, const int last_weeks_season)
|
|||
}
|
||||
|
||||
static void
|
||||
growing_herbs(region * r, const int current_season, const int last_weeks_season)
|
||||
growing_herbs(region * r, const int current_season, const season_t last_weeks_season)
|
||||
{
|
||||
/* Jetzt die Kraeutervermehrung. Vermehrt wird logistisch:
|
||||
*
|
||||
|
@ -778,6 +784,7 @@ void immigration(void)
|
|||
/* FIXME: kann ernsthaft abs(newpeasants) > rpeasants(r) sein? */
|
||||
if (rp < 0) rp = 0;
|
||||
rsetpeasants(r, rp);
|
||||
r->land->newpeasants = 0;
|
||||
}
|
||||
/* Genereate some (0-6 depending on the income) peasants out of nothing */
|
||||
/* if less than 50 are in the region and there is space and no monster or demon units in the region */
|
||||
|
@ -842,20 +849,12 @@ void nmr_warnings(void)
|
|||
void demographics(void)
|
||||
{
|
||||
region *r;
|
||||
static int last_weeks_season = -1;
|
||||
static int current_season = -1;
|
||||
int plant_rules = config_get_int("rules.grow.formula", 2);
|
||||
int horse_rules = config_get_int("rules.horses.growth", 1);
|
||||
int peasant_rules = config_get_int("rules.peasants.growth", 1);
|
||||
const struct building_type *bt_harbour = bt_find("harbour");
|
||||
|
||||
if (current_season < 0) {
|
||||
gamedate date;
|
||||
get_gamedate(turn, &date);
|
||||
current_season = date.season;
|
||||
get_gamedate(turn - 1, &date);
|
||||
last_weeks_season = date.season;
|
||||
}
|
||||
season_t current_season = calendar_season(turn);
|
||||
season_t last_weeks_season = calendar_season(turn - 1);
|
||||
|
||||
for (r = regions; r; r = r->next) {
|
||||
++r->age; /* also oceans. no idea why we didn't always do that */
|
||||
|
@ -877,7 +876,7 @@ void demographics(void)
|
|||
/* Seuchen erst nachdem die Bauern sich vermehrt haben
|
||||
* und gewandert sind */
|
||||
|
||||
calculate_emigration(r);
|
||||
peasant_migration(r);
|
||||
peasants(r, peasant_rules);
|
||||
|
||||
if (r->age > 20) {
|
||||
|
@ -1703,7 +1702,7 @@ static int rename_cmd(unit * u, order * ord, char **s, const char *s2)
|
|||
}
|
||||
|
||||
static bool try_rename(unit *u, building *b, order *ord) {
|
||||
unit *owner = b ? building_owner(b) : 0;
|
||||
unit *owner = b ? building_owner(b) : NULL;
|
||||
bool foreign = !(owner && owner->faction == u->faction);
|
||||
|
||||
if (!b) {
|
||||
|
@ -1733,12 +1732,12 @@ static bool try_rename(unit *u, building *b, order *ord) {
|
|||
msg_message("renamed_building_notseen",
|
||||
"building region", b, u->region));
|
||||
}
|
||||
if (owner != u) {
|
||||
cmistake(u, ord, 148, MSG_PRODUCE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (owner && owner->faction != u->faction) {
|
||||
cmistake(u, ord, 148, MSG_PRODUCE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3891,7 +3890,6 @@ void init_processor(void)
|
|||
add_proc_order(p, K_GROUP, group_cmd, 0, NULL);
|
||||
|
||||
p += 10;
|
||||
add_proc_order(p, K_QUIT, quit_cmd, 0, NULL);
|
||||
add_proc_order(p, K_URSPRUNG, origin_cmd, 0, NULL);
|
||||
add_proc_order(p, K_ALLY, ally_cmd, 0, NULL);
|
||||
add_proc_order(p, K_PREFIX, prefix_cmd, 0, NULL);
|
||||
|
@ -3915,6 +3913,7 @@ void init_processor(void)
|
|||
p += 10; /* all claims must be done before we can USE */
|
||||
add_proc_region(p, enter_1, "Betreten (1. Versuch)"); /* for GIVE CONTROL */
|
||||
add_proc_order(p, K_USE, use_cmd, 0, "Benutzen");
|
||||
add_proc_order(p, K_QUIT, quit_cmd, 0, "Stirb");
|
||||
|
||||
p += 10; /* in case it has any effects on alliance victories */
|
||||
add_proc_order(p, K_GIVE, give_control_cmd, 0, "GIB KOMMANDO");
|
||||
|
@ -3942,11 +3941,14 @@ void init_processor(void)
|
|||
if (rule_force_leave(FORCE_LEAVE_ALL)) {
|
||||
add_proc_region(p, do_force_leave, "kick non-allies out of buildings/ships");
|
||||
}
|
||||
add_proc_region(p, economics, "Zerstoeren, Geben, Rekrutieren, Vergessen");
|
||||
add_proc_region(p, economics, "Geben, Vergessen");
|
||||
add_proc_region(p+1, recruit, "Rekrutieren");
|
||||
add_proc_region(p+2, destroy, "Zerstoeren");
|
||||
|
||||
/* all recruitment must be finished before we can calculate
|
||||
* promotion cost of ability */
|
||||
p += 10;
|
||||
add_proc_global(p, quit, "Sterben");
|
||||
add_proc_order(p, K_PROMOTION, promotion_cmd, 0, "Heldenbefoerderung");
|
||||
|
||||
p += 10;
|
||||
|
@ -3955,9 +3957,6 @@ void init_processor(void)
|
|||
}
|
||||
add_proc_postregion(p, maintain_buildings, "Gebaeudeunterhalt");
|
||||
|
||||
p += 10; /* QUIT fuer sich alleine */
|
||||
add_proc_global(p, quit, "Sterben");
|
||||
|
||||
if (!keyword_disabled(K_CAST)) {
|
||||
p += 10;
|
||||
add_proc_global(p, magic, "Zaubern");
|
||||
|
|
|
@ -94,6 +94,7 @@ extern "C" {
|
|||
int reserve_self(struct unit *u, struct order *ord);
|
||||
int claim_cmd(struct unit *u, struct order *ord);
|
||||
void transfer_faction(struct faction *fsrc, struct faction *fdst);
|
||||
void peasant_migration(struct region * r);
|
||||
|
||||
void nmr_warnings(void);
|
||||
bool nmr_death(const struct faction * f, int turn, int timeout);
|
||||
|
@ -120,6 +121,8 @@ extern "C" {
|
|||
|
||||
enum param_t findparam_ex(const char *s, const struct locale * lang);
|
||||
|
||||
#define QUIT_WITH_TRANSFER
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1355,6 +1355,24 @@ static void test_name_cmd(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_name_foreign_cmd(CuTest *tc) {
|
||||
building *b;
|
||||
faction *f;
|
||||
region *r;
|
||||
unit *u;
|
||||
|
||||
test_setup();
|
||||
u = test_create_unit(f = test_create_faction(NULL), r = test_create_region(0, 0, NULL));
|
||||
b = test_create_building(u->region, NULL);
|
||||
u->thisorder = create_order(K_NAME, f->locale, "%s %s %s Hodor",
|
||||
LOC(f->locale, parameters[P_FOREIGN]),
|
||||
LOC(f->locale, parameters[P_BUILDING]),
|
||||
itoa36(b->no));
|
||||
name_cmd(u, u->thisorder);
|
||||
CuAssertStrEquals(tc, "Hodor", b->name);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_name_cmd_2274(CuTest *tc) {
|
||||
unit *u1, *u2, *u3;
|
||||
faction *f;
|
||||
|
@ -1931,6 +1949,53 @@ static void test_long_order_on_ocean(CuTest *tc) {
|
|||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_peasant_migration(CuTest *tc) {
|
||||
region *r1, *r2;
|
||||
int rmax;
|
||||
|
||||
test_setup();
|
||||
config_set("rules.economy.repopulate_maximum", "0");
|
||||
r1 = test_create_plain(0, 0);
|
||||
rsettrees(r1, 0, 0);
|
||||
rsettrees(r1, 1, 0);
|
||||
rsettrees(r1, 2, 0);
|
||||
rmax = region_maxworkers(r1);
|
||||
r2 = test_create_plain(0, 1);
|
||||
rsettrees(r2, 0, 0);
|
||||
rsettrees(r2, 1, 0);
|
||||
rsettrees(r2, 2, 0);
|
||||
|
||||
rsetpeasants(r1, rmax - 90);
|
||||
rsetpeasants(r2, rmax);
|
||||
peasant_migration(r1);
|
||||
immigration();
|
||||
CuAssertIntEquals(tc, rmax - 90, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 90);
|
||||
rsetpeasants(r2, rmax + 60);
|
||||
peasant_migration(r1);
|
||||
immigration();
|
||||
CuAssertIntEquals(tc, rmax - 80, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 50, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
|
||||
rsetpeasants(r2, rmax + 60); /* max 10 emigrants. */
|
||||
peasant_migration(r1);
|
||||
immigration(); /* 4 peasants will move */
|
||||
CuAssertIntEquals(tc, rmax - 2, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 56, rpeasants(r2));
|
||||
|
||||
rsetpeasants(r1, rmax - 6); /* max 4 immigrants. */
|
||||
rsetpeasants(r2, rmax + 6); /* max 1 emigrant. */
|
||||
peasant_migration(r1);
|
||||
immigration(); /* 4 peasants will move */
|
||||
CuAssertIntEquals(tc, rmax - 5, rpeasants(r1));
|
||||
CuAssertIntEquals(tc, rmax + 5, rpeasants(r2));
|
||||
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_quit(CuTest *tc) {
|
||||
faction *f;
|
||||
unit *u;
|
||||
|
@ -2108,6 +2173,7 @@ CuSuite *get_laws_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_nmr_warnings);
|
||||
SUITE_ADD_TEST(suite, test_ally_cmd);
|
||||
SUITE_ADD_TEST(suite, test_name_cmd);
|
||||
SUITE_ADD_TEST(suite, test_name_foreign_cmd);
|
||||
SUITE_ADD_TEST(suite, test_banner_cmd);
|
||||
SUITE_ADD_TEST(suite, test_email_cmd);
|
||||
SUITE_ADD_TEST(suite, test_name_cmd_2274);
|
||||
|
@ -2177,6 +2243,7 @@ CuSuite *get_laws_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_long_orders);
|
||||
SUITE_ADD_TEST(suite, test_long_order_on_ocean);
|
||||
SUITE_ADD_TEST(suite, test_quit);
|
||||
SUITE_ADD_TEST(suite, test_peasant_migration);
|
||||
#ifdef QUIT_WITH_TRANSFER
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer);
|
||||
SUITE_ADD_TEST(suite, test_quit_transfer_limited);
|
||||
|
|
66
src/magic.c
66
src/magic.c
|
@ -1335,8 +1335,8 @@ static void do_fumble(castorder * co)
|
|||
static int rc_cache;
|
||||
fumble_f fun;
|
||||
|
||||
ADDMSG(&mage->faction->msgs,
|
||||
msg_message("patzer", "unit region spell", mage, r, sp));
|
||||
ADDMSG(&caster->faction->msgs,
|
||||
msg_message("patzer", "unit region spell", caster, r, sp));
|
||||
switch (rng_int() % 10) {
|
||||
case 0:
|
||||
/* wenn vorhanden spezieller Patzer, ansonsten nix */
|
||||
|
@ -1385,14 +1385,14 @@ static void do_fumble(castorder * co)
|
|||
c = create_curse(mage, &mage->attribs, &ct_skillmod, level,
|
||||
duration, effect, 1);
|
||||
c->data.i = SK_MAGIC;
|
||||
ADDMSG(&mage->faction->msgs, msg_message("patzer2", "unit region", mage, r));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("patzer2", "unit region", caster, r));
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
/* Spruch schlaegt fehl, alle Magiepunkte weg */
|
||||
set_spellpoints(mage, 0);
|
||||
ADDMSG(&mage->faction->msgs, msg_message("patzer3", "unit region spell",
|
||||
mage, r, sp));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("patzer3", "unit region spell",
|
||||
caster, r, sp));
|
||||
break;
|
||||
|
||||
case 5:
|
||||
|
@ -1400,8 +1400,8 @@ static void do_fumble(castorder * co)
|
|||
/* Spruch gelingt, aber alle Magiepunkte weg */
|
||||
co->level = cast_spell(co);
|
||||
set_spellpoints(mage, 0);
|
||||
ADDMSG(&mage->faction->msgs, msg_message("patzer4", "unit region spell",
|
||||
mage, r, sp));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("patzer4", "unit region spell",
|
||||
caster, r, sp));
|
||||
break;
|
||||
|
||||
case 7:
|
||||
|
@ -1410,8 +1410,8 @@ static void do_fumble(castorder * co)
|
|||
default:
|
||||
/* Spruch gelingt, alle nachfolgenden Sprueche werden 2^4 so teuer */
|
||||
co->level = cast_spell(co);
|
||||
ADDMSG(&mage->faction->msgs, msg_message("patzer5", "unit region spell",
|
||||
mage, r, sp));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("patzer5", "unit region spell",
|
||||
caster, r, sp));
|
||||
countspells(caster, 3);
|
||||
}
|
||||
}
|
||||
|
@ -1626,7 +1626,7 @@ verify_unit(region * r, unit * mage, const spell * sp, spllprm * spobj,
|
|||
static void
|
||||
verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
||||
{
|
||||
unit *mage = co_get_magician(co);
|
||||
unit *caster = co_get_caster(co);
|
||||
const spell *sp = co->sp;
|
||||
region *target_r = co_get_region(co);
|
||||
spellparameter *sa = co->par;
|
||||
|
@ -1647,15 +1647,15 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
switch (spobj->typ) {
|
||||
case SPP_TEMP:
|
||||
case SPP_UNIT:
|
||||
if (!verify_unit(target_r, mage, sp, spobj, co->order))
|
||||
if (!verify_unit(target_r, caster, sp, spobj, co->order))
|
||||
++ * invalid;
|
||||
break;
|
||||
case SPP_BUILDING:
|
||||
if (!verify_building(target_r, mage, sp, spobj, co->order))
|
||||
if (!verify_building(target_r, caster, sp, spobj, co->order))
|
||||
++ * invalid;
|
||||
break;
|
||||
case SPP_SHIP:
|
||||
if (!verify_ship(target_r, mage, sp, spobj, co->order))
|
||||
if (!verify_ship(target_r, caster, sp, spobj, co->order))
|
||||
++ * invalid;
|
||||
break;
|
||||
default:
|
||||
|
@ -1679,13 +1679,13 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
u = spobj->data.u;
|
||||
|
||||
if ((sp->sptyp & TESTRESISTANCE)
|
||||
&& target_resists_magic(mage, u, TYP_UNIT, 0)) {
|
||||
&& target_resists_magic(caster, u, TYP_UNIT, 0)) {
|
||||
/* Fehlermeldung */
|
||||
spobj->data.i = u->no;
|
||||
spobj->flag = TARGET_RESISTS;
|
||||
++*resist;
|
||||
ADDMSG(&mage->faction->msgs, msg_message("spellunitresists",
|
||||
"unit region command target", mage, mage->region, co->order, u));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("spellunitresists",
|
||||
"unit region command target", caster, caster->region, co->order, u));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1696,13 +1696,13 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
b = spobj->data.b;
|
||||
|
||||
if ((sp->sptyp & TESTRESISTANCE)
|
||||
&& target_resists_magic(mage, b, TYP_BUILDING, 0)) { /* Fehlermeldung */
|
||||
&& target_resists_magic(caster, b, TYP_BUILDING, 0)) { /* Fehlermeldung */
|
||||
spobj->data.i = b->no;
|
||||
spobj->flag = TARGET_RESISTS;
|
||||
++*resist;
|
||||
ADDMSG(&mage->faction->msgs, msg_message("spellbuildingresists",
|
||||
ADDMSG(&caster->faction->msgs, msg_message("spellbuildingresists",
|
||||
"unit region command id",
|
||||
mage, mage->region, co->order, spobj->data.i));
|
||||
caster, caster->region, co->order, spobj->data.i));
|
||||
break;
|
||||
}
|
||||
++*success;
|
||||
|
@ -1711,11 +1711,11 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
sh = spobj->data.sh;
|
||||
|
||||
if ((sp->sptyp & TESTRESISTANCE)
|
||||
&& target_resists_magic(mage, sh, TYP_SHIP, 0)) { /* Fehlermeldung */
|
||||
&& target_resists_magic(caster, sh, TYP_SHIP, 0)) { /* Fehlermeldung */
|
||||
spobj->data.i = sh->no;
|
||||
spobj->flag = TARGET_RESISTS;
|
||||
++*resist;
|
||||
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
|
||||
ADDMSG(&caster->faction->msgs, msg_feedback(caster, co->order,
|
||||
"spellshipresists", "ship", sh));
|
||||
break;
|
||||
}
|
||||
|
@ -1728,11 +1728,11 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
tr = spobj->data.r;
|
||||
|
||||
if ((sp->sptyp & TESTRESISTANCE)
|
||||
&& target_resists_magic(mage, tr, TYP_REGION, 0)) { /* Fehlermeldung */
|
||||
&& target_resists_magic(caster, tr, TYP_REGION, 0)) { /* Fehlermeldung */
|
||||
spobj->flag = TARGET_RESISTS;
|
||||
++*resist;
|
||||
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
|
||||
"unit region command", mage, mage->region, co->order));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("spellregionresists",
|
||||
"unit region command", caster, caster->region, co->order));
|
||||
break;
|
||||
}
|
||||
++*success;
|
||||
|
@ -1767,10 +1767,10 @@ verify_targets(castorder * co, int *invalid, int *resist, int *success)
|
|||
co->par = sa;
|
||||
|
||||
if ((sp->sptyp & TESTRESISTANCE)) {
|
||||
if (target_resists_magic(mage, target_r, TYP_REGION, 0)) {
|
||||
if (target_resists_magic(caster, target_r, TYP_REGION, 0)) {
|
||||
/* Fehlermeldung */
|
||||
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
|
||||
"unit region command", mage, mage->region, co->order));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("spellregionresists",
|
||||
"unit region command", caster, caster->region, co->order));
|
||||
spobj->flag = TARGET_RESISTS;
|
||||
++*resist;
|
||||
}
|
||||
|
@ -2848,8 +2848,8 @@ void magic(void)
|
|||
/* Sprueche mit Fixkosten werden immer auf Stufe des Spruchs
|
||||
* gezaubert, co->level ist aber defaultmaessig Stufe des Magiers */
|
||||
if (spl_costtyp(sp) != SPC_FIX) {
|
||||
ADDMSG(&mage->faction->msgs, msg_message("missing_components",
|
||||
"unit spell level", mage, sp, cast_level));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("missing_components",
|
||||
"unit spell level", caster, sp, cast_level));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2864,8 +2864,8 @@ void magic(void)
|
|||
/* die Staerke kann durch Antimagie auf 0 sinken */
|
||||
if (co->force <= 0) {
|
||||
co->force = 0;
|
||||
ADDMSG(&mage->faction->msgs, msg_message("missing_force",
|
||||
"unit spell level", mage, sp, co->level));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("missing_force",
|
||||
"unit spell level", caster, sp, co->level));
|
||||
}
|
||||
|
||||
/* Ziele auf Existenz pruefen und Magieresistenz feststellen. Wurde
|
||||
|
@ -2885,8 +2885,8 @@ void magic(void)
|
|||
co->force = 0;
|
||||
/* zwar wurde mindestens ein Ziel gefunden, das widerstand
|
||||
* jedoch dem Zauber. Kosten abziehen und abbrechen. */
|
||||
ADDMSG(&mage->faction->msgs, msg_message("spell_resist",
|
||||
"unit region spell", mage, r, sp));
|
||||
ADDMSG(&caster->faction->msgs, msg_message("spell_resist",
|
||||
"unit region spell", caster, r, sp));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -565,8 +565,8 @@ int autoseed(newfaction ** players, int nsize, int max_agediff)
|
|||
nfp = &nextf->next;
|
||||
while (*nfp) {
|
||||
newfaction *nf = *nfp;
|
||||
if (strcmp(nextf->email, nf->email) == 0) {
|
||||
log_warning("Duplicate email %s\n", nf->email?nf->email:"");
|
||||
if (nf->email && nextf->email && strcmp(nextf->email, nf->email) == 0) {
|
||||
log_warning("Duplicate email %s\n", nf->email ? nf->email : "");
|
||||
*nfp = nf->next;
|
||||
free_newfaction(nf);
|
||||
}
|
||||
|
|
507
src/recruit.c
Normal file
507
src/recruit.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
Copyright (c) 1998-2019,
|
||||
Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
**/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <platform.h>
|
||||
#endif
|
||||
|
||||
#include "recruit.h"
|
||||
|
||||
#include "alchemy.h"
|
||||
#include "direction.h"
|
||||
#include "donations.h"
|
||||
#include "guard.h"
|
||||
#include "give.h"
|
||||
#include "laws.h"
|
||||
#include "randenc.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
#include "move.h"
|
||||
#include "monsters.h"
|
||||
#include "morale.h"
|
||||
#include "reports.h"
|
||||
|
||||
#include <attributes/reduceproduction.h>
|
||||
#include <attributes/racename.h>
|
||||
#include <spells/buildingcurse.h>
|
||||
#include <spells/regioncurse.h>
|
||||
#include <spells/unitcurse.h>
|
||||
|
||||
/* kernel includes */
|
||||
#include "kernel/ally.h"
|
||||
#include "kernel/attrib.h"
|
||||
#include "kernel/building.h"
|
||||
#include "kernel/calendar.h"
|
||||
#include "kernel/config.h"
|
||||
#include "kernel/curse.h"
|
||||
#include "kernel/equipment.h"
|
||||
#include "kernel/event.h"
|
||||
#include "kernel/faction.h"
|
||||
#include "kernel/item.h"
|
||||
#include "kernel/messages.h"
|
||||
#include "kernel/order.h"
|
||||
#include "kernel/plane.h"
|
||||
#include "kernel/pool.h"
|
||||
#include "kernel/race.h"
|
||||
#include "kernel/region.h"
|
||||
#include "kernel/resources.h"
|
||||
#include "kernel/ship.h"
|
||||
#include "kernel/terrain.h"
|
||||
#include "kernel/terrainid.h"
|
||||
#include "kernel/unit.h"
|
||||
|
||||
/* util includes */
|
||||
#include <util/base36.h>
|
||||
#include <util/goodies.h>
|
||||
#include <util/language.h>
|
||||
#include <util/lists.h>
|
||||
#include <util/log.h>
|
||||
#include "util/param.h"
|
||||
#include <util/parser.h>
|
||||
#include <util/rng.h>
|
||||
|
||||
/* libs includes */
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define RECRUIT_MERGE 1
|
||||
static int rules_recruit = -1;
|
||||
|
||||
typedef struct recruit_request {
|
||||
struct recruit_request *next;
|
||||
struct unit *unit;
|
||||
struct order *ord;
|
||||
int qty;
|
||||
} recruit_request;
|
||||
|
||||
typedef struct recruitment {
|
||||
struct recruitment *next;
|
||||
faction *f;
|
||||
recruit_request *requests;
|
||||
int total, assigned;
|
||||
} recruitment;
|
||||
|
||||
static void recruit_init(void)
|
||||
{
|
||||
if (rules_recruit < 0) {
|
||||
rules_recruit = 0;
|
||||
if (config_get_int("recruit.allow_merge", 1)) {
|
||||
rules_recruit |= RECRUIT_MERGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void free_requests(recruit_request *requests) {
|
||||
while (requests) {
|
||||
recruit_request *req = requests->next;
|
||||
free(requests);
|
||||
requests = req;
|
||||
}
|
||||
}
|
||||
|
||||
void free_recruitments(recruitment * recruits)
|
||||
{
|
||||
while (recruits) {
|
||||
recruitment *rec = recruits;
|
||||
recruits = rec->next;
|
||||
free_requests(rec->requests);
|
||||
free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a list of recruitment structs, one for each faction. Adds every quantifyable production
|
||||
* to the faction's struct and to total.
|
||||
*/
|
||||
static recruitment *select_recruitment(recruit_request ** rop,
|
||||
int(*quantify) (const struct race *, int), int *total)
|
||||
{
|
||||
recruitment *recruits = NULL;
|
||||
|
||||
while (*rop) {
|
||||
recruitment *rec = recruits;
|
||||
recruit_request *ro = *rop;
|
||||
unit *u = ro->unit;
|
||||
const race *rc = u_race(u);
|
||||
int qty = quantify(rc, ro->qty);
|
||||
|
||||
if (qty < 0) {
|
||||
rop = &ro->next; /* skip this one */
|
||||
}
|
||||
else {
|
||||
*rop = ro->next; /* remove this one */
|
||||
while (rec && rec->f != u->faction)
|
||||
rec = rec->next;
|
||||
if (rec == NULL) {
|
||||
rec = (recruitment *)malloc(sizeof(recruitment));
|
||||
if (!rec) abort();
|
||||
rec->f = u->faction;
|
||||
rec->total = 0;
|
||||
rec->assigned = 0;
|
||||
rec->requests = NULL;
|
||||
rec->next = recruits;
|
||||
recruits = rec;
|
||||
}
|
||||
*total += qty;
|
||||
rec->total += qty;
|
||||
ro->next = rec->requests;
|
||||
rec->requests = ro;
|
||||
}
|
||||
}
|
||||
return recruits;
|
||||
}
|
||||
|
||||
void add_recruits(unit * u, int number, int wanted)
|
||||
{
|
||||
region *r = u->region;
|
||||
assert(number <= wanted);
|
||||
if (number > 0) {
|
||||
unit *unew;
|
||||
char equipment[64];
|
||||
int len;
|
||||
|
||||
if (u->number == 0) {
|
||||
set_number(u, number);
|
||||
u->hp = number * unit_max_hp(u);
|
||||
unew = u;
|
||||
}
|
||||
else {
|
||||
unew = create_unit(r, u->faction, number, u_race(u), 0, NULL, u);
|
||||
}
|
||||
|
||||
len = snprintf(equipment, sizeof(equipment), "new_%s", u_race(u)->_name);
|
||||
if (len > 0 && (size_t)len < sizeof(equipment)) {
|
||||
equip_unit(unew, equipment);
|
||||
}
|
||||
if (unew != u) {
|
||||
transfermen(unew, u, unew->number);
|
||||
remove_unit(&r->units, unew);
|
||||
}
|
||||
}
|
||||
if (number < wanted) {
|
||||
ADDMSG(&u->faction->msgs, msg_message("recruit",
|
||||
"unit region amount want", u, r, number, wanted));
|
||||
}
|
||||
}
|
||||
|
||||
static int any_recruiters(const struct race *rc, int qty)
|
||||
{
|
||||
return (int)(qty * 2 * rc->recruit_multi);
|
||||
}
|
||||
|
||||
static int do_recruiting(recruitment * recruits, int available)
|
||||
{
|
||||
recruitment *rec;
|
||||
int recruited = 0;
|
||||
|
||||
/* try to assign recruits to factions fairly */
|
||||
while (available > 0) {
|
||||
int n = 0;
|
||||
int rest, mintotal = INT_MAX;
|
||||
|
||||
/* find smallest production */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
if (want > 0) {
|
||||
if (mintotal > want)
|
||||
mintotal = want;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
if (mintotal * n > available) {
|
||||
mintotal = available / n;
|
||||
}
|
||||
rest = available - mintotal * n;
|
||||
|
||||
/* assign size of smallest production for everyone if possible; in the end roll dice to assign
|
||||
* small rest */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
int want = rec->total - rec->assigned;
|
||||
|
||||
if (want > 0) {
|
||||
int get = mintotal;
|
||||
if (want > mintotal && rest < n && (rng_int() % n) < rest) {
|
||||
--rest;
|
||||
++get;
|
||||
}
|
||||
assert(get <= want);
|
||||
available -= get;
|
||||
rec->assigned += get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* do actual recruiting */
|
||||
for (rec = recruits; rec != NULL; rec = rec->next) {
|
||||
recruit_request *req;
|
||||
int get = rec->assigned;
|
||||
|
||||
for (req = rec->requests; req; req = req->next) {
|
||||
unit *u = req->unit;
|
||||
const race *rc = u_race(u); /* race is set in recruit() */
|
||||
int number;
|
||||
double multi = 2.0 * rc->recruit_multi;
|
||||
|
||||
number = (int)(get / multi);
|
||||
if (number > req->qty) number = req->qty;
|
||||
if (rc->recruitcost) {
|
||||
int afford = get_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
number * rc->recruitcost) / rc->recruitcost;
|
||||
if (number > afford) number = afford;
|
||||
}
|
||||
if (u->number + number > UNIT_MAXSIZE) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, req->ord, "error_unit_size",
|
||||
"maxsize", UNIT_MAXSIZE));
|
||||
number = UNIT_MAXSIZE - u->number;
|
||||
assert(number >= 0);
|
||||
}
|
||||
if (rc->recruitcost) {
|
||||
use_pooled(u, get_resourcetype(R_SILVER), GET_DEFAULT,
|
||||
rc->recruitcost * number);
|
||||
}
|
||||
if (u->number == 0 && fval(u, UFL_DEAD)) {
|
||||
/* unit is empty, dead, and cannot recruit */
|
||||
number = 0;
|
||||
}
|
||||
add_recruits(u, number, req->qty);
|
||||
if (number > 0) {
|
||||
int dec = (int)(number * multi);
|
||||
if ((rc->ec_flags & ECF_REC_ETHEREAL) == 0) {
|
||||
recruited += dec;
|
||||
}
|
||||
|
||||
get -= dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
return recruited;
|
||||
}
|
||||
|
||||
/* Rekrutierung */
|
||||
static void expandrecruit(region * r, recruit_request * recruitorders)
|
||||
{
|
||||
recruitment *recruits;
|
||||
int orc_total = 0;
|
||||
|
||||
/* peasant limited: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int orc_recruited, orc_peasants = rpeasants(r) * 2;
|
||||
int orc_frac = orc_peasants / RECRUITFRACTION; /* anzahl orks. 2 ork = 1 bauer */
|
||||
if (orc_total < orc_frac)
|
||||
orc_frac = orc_total;
|
||||
orc_recruited = do_recruiting(recruits, orc_frac);
|
||||
assert(orc_recruited <= orc_frac);
|
||||
rsetpeasants(r, (orc_peasants - orc_recruited) / 2);
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
/* no limit: */
|
||||
recruits = select_recruitment(&recruitorders, any_recruiters, &orc_total);
|
||||
if (recruits) {
|
||||
int recruited, peasants = rpeasants(r) * 2;
|
||||
recruited = do_recruiting(recruits, INT_MAX);
|
||||
if (recruited > 0) {
|
||||
rsetpeasants(r, (peasants - recruited) / 2);
|
||||
}
|
||||
free_recruitments(recruits);
|
||||
}
|
||||
|
||||
assert(recruitorders == NULL);
|
||||
}
|
||||
|
||||
static int recruit_cost(const faction * f, const race * rc)
|
||||
{
|
||||
if (is_monsters(f) || valid_race(f, rc)) {
|
||||
return rc->recruitcost;
|
||||
}
|
||||
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)) {
|
||||
|
||||
/* in Gletschern, Eisbergen gar nicht rekrutieren */
|
||||
if (r_insectstalled(r)) {
|
||||
return msg_error(u, ord, 97);
|
||||
}
|
||||
|
||||
/* in Wüsten ganzjährig rekrutieren, sonst im Winter nur mit Trank */
|
||||
if (r->terrain != newterrain(T_DESERT) && calendar_season(now) == SEASON_WINTER) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
* grundsaetzlich nur alleine! */
|
||||
return msg_error(u, ord, 158);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recruit_cmd(unit * u, struct order *ord, recruit_request ** recruitorders)
|
||||
{
|
||||
region *r = u->region;
|
||||
recruit_request *o;
|
||||
int recruitcost = -1;
|
||||
const faction *f = u->faction;
|
||||
const struct race *rc = u_race(u);
|
||||
int n;
|
||||
message *msg;
|
||||
|
||||
init_order_depr(ord);
|
||||
n = getint();
|
||||
if (n <= 0) {
|
||||
syntax_error(u, ord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->number == 0) {
|
||||
char token[128];
|
||||
const char *str;
|
||||
|
||||
str = gettoken(token, sizeof(token));
|
||||
if (str && str[0]) {
|
||||
/* Monsters can RECRUIT 15 DRACOID
|
||||
* also: secondary race */
|
||||
rc = findrace(str, f->locale);
|
||||
if (rc != NULL) {
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost < 0) {
|
||||
rc = u_race(u);
|
||||
recruitcost = recruit_cost(f, rc);
|
||||
if (recruitcost < 0) {
|
||||
recruitcost = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitcost > 0) {
|
||||
int pool;
|
||||
plane *pl = getplane(r);
|
||||
|
||||
if (pl && (pl->flags & PFL_NORECRUITS)) {
|
||||
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_pflnorecruit", ""));
|
||||
return;
|
||||
}
|
||||
|
||||
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 (!n) {
|
||||
cmistake(u, ord, 142, MSG_EVENT);
|
||||
return;
|
||||
}
|
||||
if (has_skill(u, SK_ALCHEMY)) {
|
||||
if (faction_count_skill(u->faction, SK_ALCHEMY) + n > faction_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 = (recruit_request *)calloc(1, sizeof(recruit_request));
|
||||
if (!o) abort();
|
||||
o->qty = n;
|
||||
o->unit = u;
|
||||
o->ord = ord;
|
||||
addlist(recruitorders, o);
|
||||
}
|
||||
|
||||
void recruit(region * r)
|
||||
{
|
||||
unit *u;
|
||||
recruit_request *recruitorders = NULL;
|
||||
|
||||
if (rules_recruit < 0)
|
||||
recruit_init();
|
||||
for (u = r->units; u; u = u->next) {
|
||||
order *ord;
|
||||
|
||||
if ((rules_recruit & RECRUIT_MERGE) || u->number == 0) {
|
||||
for (ord = u->orders; ord; ord = ord->next) {
|
||||
if (getkeyword(ord) == K_RECRUIT) {
|
||||
recruit_cmd(u, ord, &recruitorders);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recruitorders) {
|
||||
expandrecruit(r, recruitorders);
|
||||
}
|
||||
remove_empty_units_in_region(r);
|
||||
}
|
40
src/recruit.h
Normal file
40
src/recruit.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
|
||||
Katja Zedel <katze@felidae.kn-bremen.de
|
||||
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
#ifndef H_GC_RECRUIT
|
||||
#define H_GC_RECRUIT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct message;
|
||||
struct order;
|
||||
struct race;
|
||||
struct region;
|
||||
struct unit;
|
||||
|
||||
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 recruit(struct region * r);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
222
src/report.c
222
src/report.c
|
@ -750,7 +750,7 @@ static void append_message(sbstring *sbp, message *m, const faction * f) {
|
|||
sbp->end += size;
|
||||
}
|
||||
|
||||
static void prices(struct stream *out, const region * r, const faction * f)
|
||||
static void report_prices(struct stream *out, const region * r, const faction * f)
|
||||
{
|
||||
const luxury_type *sale = NULL;
|
||||
struct demand *dmd;
|
||||
|
@ -771,6 +771,7 @@ static void prices(struct stream *out, const region * r, const faction * f)
|
|||
|
||||
m = msg_message("nr_market_sale", "product price",
|
||||
sale->itype->rtype, sale->price);
|
||||
newline(out);
|
||||
nr_render(m, f->locale, buf, sizeof(buf), f);
|
||||
msg_release(m);
|
||||
sbs_adopt(&sbs, buf, sizeof(buf));
|
||||
|
@ -867,7 +868,8 @@ static void report_region_description(struct stream *out, const region * r, fact
|
|||
sbs_strcat(&sbs, LOC(f->locale, "see_neighbour"));
|
||||
sbs_strcat(&sbs, ")");
|
||||
}
|
||||
else if (r->seen.mode == seen_lighthouse) {
|
||||
else if ((r->seen.mode == seen_lighthouse)
|
||||
|| (r->seen.mode == seen_lighthouse_land)) {
|
||||
sbs_strcat(&sbs, " (");
|
||||
sbs_strcat(&sbs, LOC(f->locale, "see_lighthouse"));
|
||||
sbs_strcat(&sbs, ")");
|
||||
|
@ -1154,17 +1156,22 @@ void report_region(struct stream *out, const region * r, faction * f)
|
|||
}
|
||||
|
||||
report_region_description(out, r, f, see);
|
||||
report_region_schemes(out, r, f);
|
||||
report_region_edges(out, r, f, edges, ne);
|
||||
if (r->seen.mode >= seen_unit) {
|
||||
report_region_schemes(out, r, f);
|
||||
}
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
report_region_edges(out, r, f, edges, ne);
|
||||
}
|
||||
}
|
||||
|
||||
static void statistics(struct stream *out, const region * r, const faction * f)
|
||||
static void report_statistics(struct stream *out, const region * r, const faction * f)
|
||||
{
|
||||
int p = rpeasants(r);
|
||||
message *m;
|
||||
char buf[4096];
|
||||
|
||||
/* print */
|
||||
newline(out);
|
||||
m = msg_message("nr_stat_header", "region", r);
|
||||
nr_render(m, f->locale, buf, sizeof(buf), f);
|
||||
msg_release(m);
|
||||
|
@ -1592,6 +1599,8 @@ static void allies(struct stream *out, const faction * f)
|
|||
const group *g = f->groups;
|
||||
char prefix[64];
|
||||
|
||||
rpline(out);
|
||||
newline(out);
|
||||
centre(out, LOC(f->locale, "nr_alliances"), false);
|
||||
newline(out);
|
||||
|
||||
|
@ -1609,7 +1618,7 @@ static void allies(struct stream *out, const faction * f)
|
|||
}
|
||||
}
|
||||
|
||||
static void guards(struct stream *out, const region * r, const faction * see)
|
||||
static void report_guards(struct stream *out, const region * r, const faction * see)
|
||||
{
|
||||
/* die Partei see sieht dies; wegen
|
||||
* "unbekannte Partei", wenn man es selbst ist... */
|
||||
|
@ -1678,6 +1687,8 @@ static void list_address(struct stream *out, const faction * uf, selist * seenfa
|
|||
int qi = 0;
|
||||
selist *flist = seenfactions;
|
||||
|
||||
rpline(out);
|
||||
newline(out);
|
||||
centre(out, LOC(uf->locale, "nr_addresses"), false);
|
||||
newline(out);
|
||||
|
||||
|
@ -1806,12 +1817,10 @@ nr_building(struct stream *out, const region *r, const building *b, const factio
|
|||
}
|
||||
paragraph(out, buffer, 2, 0, 0);
|
||||
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
nr_curses(out, 4, f, TYP_BUILDING, b);
|
||||
}
|
||||
nr_curses(out, 4, f, TYP_BUILDING, b);
|
||||
}
|
||||
|
||||
static void nr_paragraph(struct stream *out, message * m, faction * f)
|
||||
static void nr_paragraph(struct stream *out, message * m, const faction * f)
|
||||
{
|
||||
char buf[4096];
|
||||
|
||||
|
@ -1897,26 +1906,44 @@ static void cb_write_travelthru(region *r, unit *u, void *cbdata) {
|
|||
|
||||
void report_travelthru(struct stream *out, region *r, const faction *f)
|
||||
{
|
||||
int maxtravel;
|
||||
|
||||
assert(r);
|
||||
assert(f);
|
||||
if (!fval(r, RF_TRAVELUNIT)) {
|
||||
return;
|
||||
if (fval(r, RF_TRAVELUNIT)) {
|
||||
int maxtravel = count_travelthru(r, f);
|
||||
|
||||
if (maxtravel > 0) {
|
||||
cb_data cbdata;
|
||||
char buf[8192];
|
||||
|
||||
newline(out);
|
||||
init_cb(&cbdata, out, buf, sizeof(buf), f);
|
||||
cbdata.maxtravel = maxtravel;
|
||||
cbdata.writep +=
|
||||
str_strlcpy(buf, LOC(f->locale, "travelthru_header"), sizeof(buf));
|
||||
travelthru_map(r, cb_write_travelthru, &cbdata);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* How many are we listing? For grammar. */
|
||||
maxtravel = count_travelthru(r, f);
|
||||
if (maxtravel > 0) {
|
||||
cb_data cbdata;
|
||||
char buf[8192];
|
||||
static void report_market(stream * out, const region *r, const faction *f) {
|
||||
const item_type *lux = r_luxury(r);
|
||||
const item_type *herb = r->land->herbtype;
|
||||
message * m = NULL;
|
||||
|
||||
init_cb(&cbdata, out, buf, sizeof(buf), f);
|
||||
cbdata.maxtravel = maxtravel;
|
||||
cbdata.writep +=
|
||||
str_strlcpy(buf, LOC(f->locale, "travelthru_header"), sizeof(buf));
|
||||
travelthru_map(r, cb_write_travelthru, &cbdata);
|
||||
return;
|
||||
if (herb && lux) {
|
||||
m = msg_message("nr_market_info_p", "p1 p2",
|
||||
lux->rtype, herb->rtype);
|
||||
}
|
||||
else if (lux) {
|
||||
m = msg_message("nr_market_info_s", "p1", lux->rtype);
|
||||
}
|
||||
else if (herb) {
|
||||
m = msg_message("nr_market_info_s", "p1", herb->rtype);
|
||||
}
|
||||
if (m) {
|
||||
newline(out);
|
||||
nr_paragraph(out, m, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2120,121 +2147,92 @@ report_plaintext(const char *filename, report_context * ctx,
|
|||
|
||||
for (r = ctx->first; r != ctx->last; r = r->next) {
|
||||
int stealthmod = stealth_modifier(r, f, r->seen.mode);
|
||||
building *b = r->buildings;
|
||||
ship *sh = r->ships;
|
||||
|
||||
if (r->seen.mode < seen_lighthouse)
|
||||
continue;
|
||||
/* Beschreibung */
|
||||
if (r->seen.mode >= seen_lighthouse_land) {
|
||||
rpline(out);
|
||||
newline(out);
|
||||
report_region(out, r, f);
|
||||
}
|
||||
|
||||
rpline(out);
|
||||
newline(out);
|
||||
if (r->seen.mode >= seen_unit) {
|
||||
anyunits = 1;
|
||||
report_region(out, r, f);
|
||||
if (markets_module() && r->land) {
|
||||
const item_type *lux = r_luxury(r);
|
||||
const item_type *herb = r->land->herbtype;
|
||||
|
||||
m = NULL;
|
||||
if (herb && lux) {
|
||||
m = msg_message("nr_market_info_p", "p1 p2",
|
||||
lux->rtype, herb->rtype);
|
||||
}
|
||||
else if (lux) {
|
||||
m = msg_message("nr_market_info_s", "p1",lux->rtype);
|
||||
}
|
||||
else if (herb) {
|
||||
m = msg_message("nr_market_info_s", "p1", herb->rtype);
|
||||
}
|
||||
if (m) {
|
||||
newline(out);
|
||||
nr_paragraph(out, m, f);
|
||||
}
|
||||
report_market(out, r, f);
|
||||
}
|
||||
else {
|
||||
if (!fval(r->terrain, SEA_REGION) && rpeasants(r) / TRADE_FRACTION > 0) {
|
||||
newline(out);
|
||||
prices(out, r, f);
|
||||
}
|
||||
else if (!fval(r->terrain, SEA_REGION) && rpeasants(r) / TRADE_FRACTION > 0) {
|
||||
report_prices(out, r, f);
|
||||
}
|
||||
guards(out, r, f);
|
||||
newline(out);
|
||||
report_guards(out, r, f);
|
||||
report_travelthru(out, r, f);
|
||||
}
|
||||
else {
|
||||
report_region(out, r, f);
|
||||
newline(out);
|
||||
report_travelthru(out, r, f);
|
||||
}
|
||||
|
||||
if (wants_stats && r->seen.mode >= seen_travel) {
|
||||
if (r->land || r->seen.mode >= seen_unit) {
|
||||
newline(out);
|
||||
statistics(out, r, f);
|
||||
if (wants_stats) {
|
||||
report_statistics(out, r, f);
|
||||
}
|
||||
}
|
||||
else if (r->seen.mode >= seen_lighthouse) {
|
||||
report_travelthru(out, r, f);
|
||||
}
|
||||
|
||||
/* Nachrichten an REGION in der Region */
|
||||
if (r->seen.mode >= seen_travel) {
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
message_list *mlist = r_getmessages(r, f);
|
||||
newline(out);
|
||||
if (mlist) {
|
||||
struct mlist **split = merge_messages(mlist, r->msgs);
|
||||
newline(out);
|
||||
rp_messages(out, mlist, f, 0, false);
|
||||
split_messages(mlist, split);
|
||||
}
|
||||
else {
|
||||
else if (r->msgs) {
|
||||
newline(out);
|
||||
rp_messages(out, r->msgs, f, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* report all units. they are pre-sorted in an efficient manner */
|
||||
u = r->units;
|
||||
while (b) {
|
||||
while (b && (!u || u->building != b)) {
|
||||
nr_building(out, r, b, f);
|
||||
b = b->next;
|
||||
}
|
||||
if (b) {
|
||||
nr_building(out, r, b, f);
|
||||
while (u && u->building == b) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 6, r->seen.mode);
|
||||
/* report all units. they are pre-sorted in an efficient manner */
|
||||
u = r->units;
|
||||
if (r->seen.mode >= seen_travel) {
|
||||
building *b = r->buildings;
|
||||
while (b) {
|
||||
while (b && (!u || u->building != b)) {
|
||||
nr_building(out, r, b, f);
|
||||
b = b->next;
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
while (u && !u->ship) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 4, r->seen.mode);
|
||||
}
|
||||
assert(!u->building);
|
||||
u = u->next;
|
||||
}
|
||||
while (sh) {
|
||||
while (sh && (!u || u->ship != sh)) {
|
||||
nr_ship(out, r, sh, f, NULL);
|
||||
sh = sh->next;
|
||||
}
|
||||
if (sh) {
|
||||
nr_ship(out, r, sh, f, u);
|
||||
while (u && u->ship == sh) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 6, r->seen.mode);
|
||||
if (b) {
|
||||
nr_building(out, r, b, f);
|
||||
while (u && u->building == b) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 6, r->seen.mode);
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
sh = sh->next;
|
||||
}
|
||||
while (u && !u->ship) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 4, r->seen.mode);
|
||||
}
|
||||
assert(!u->building);
|
||||
u = u->next;
|
||||
}
|
||||
while (sh) {
|
||||
while (sh && (!u || u->ship != sh)) {
|
||||
nr_ship(out, r, sh, f, NULL);
|
||||
sh = sh->next;
|
||||
}
|
||||
if (sh) {
|
||||
nr_ship(out, r, sh, f, u);
|
||||
while (u && u->ship == sh) {
|
||||
if (visible_unit(u, f, stealthmod, r->seen.mode)) {
|
||||
nr_unit(out, f, u, 6, r->seen.mode);
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
sh = sh->next;
|
||||
}
|
||||
}
|
||||
assert(!u);
|
||||
}
|
||||
|
||||
assert(!u);
|
||||
|
||||
newline(out);
|
||||
ERRNO_CHECK();
|
||||
}
|
||||
if (!is_monsters(f)) {
|
||||
|
|
|
@ -260,7 +260,7 @@ static void test_report_travelthru(CuTest *tc) {
|
|||
out.api->rewind(out.handle);
|
||||
len = out.api->read(out.handle, buf, sizeof(buf));
|
||||
buf[len] = '\0';
|
||||
CuAssertStrEquals_Msg(tc, "list one unit", "Durchreise: Hodor (1).\n", buf);
|
||||
CuAssertStrEquals_Msg(tc, "list one unit", "\nDurchreise: Hodor (1).\n", buf);
|
||||
mstream_done(&out);
|
||||
|
||||
mstream_init(&out);
|
||||
|
|
|
@ -102,6 +102,7 @@ const char *visibility[] = {
|
|||
"none",
|
||||
"neighbour",
|
||||
"lighthouse",
|
||||
"lighthouse",
|
||||
"travel",
|
||||
"far",
|
||||
"unit",
|
||||
|
@ -1115,38 +1116,40 @@ void get_addresses(report_context * ctx)
|
|||
}
|
||||
|
||||
for (; r != NULL; r = r->next) {
|
||||
int stealthmod = stealth_modifier(r, ctx->f, r->seen.mode);
|
||||
if (r->seen.mode == seen_lighthouse) {
|
||||
unit *u = r->units;
|
||||
for (; u; u = u->next) {
|
||||
faction *sf = visible_faction(ctx->f, u);
|
||||
if (lastf != sf) {
|
||||
if (u->building || u->ship || (stealthmod > INT_MIN
|
||||
&& cansee(ctx->f, r, u, stealthmod))) {
|
||||
add_seen_faction_i(&flist, sf);
|
||||
lastf = sf;
|
||||
if (r->seen.mode >= seen_lighthouse) {
|
||||
int stealthmod = stealth_modifier(r, ctx->f, r->seen.mode);
|
||||
if (r->seen.mode == seen_lighthouse) {
|
||||
unit *u = r->units;
|
||||
for (; u; u = u->next) {
|
||||
faction *sf = visible_faction(ctx->f, u);
|
||||
if (lastf != sf) {
|
||||
if (u->building || u->ship || (stealthmod > INT_MIN
|
||||
&& cansee(ctx->f, r, u, stealthmod))) {
|
||||
add_seen_faction_i(&flist, sf);
|
||||
lastf = sf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (r->seen.mode == seen_travel) {
|
||||
/* when we travel through a region, then we must add
|
||||
* the factions of any units we saw */
|
||||
add_travelthru_addresses(r, ctx->f, &flist, stealthmod);
|
||||
}
|
||||
else if (r->seen.mode > seen_travel) {
|
||||
const unit *u = r->units;
|
||||
while (u != NULL) {
|
||||
if (u->faction != ctx->f) {
|
||||
faction *sf = visible_faction(ctx->f, u);
|
||||
bool ballied = sf && sf != ctx->f && sf != lastf
|
||||
&& !fval(u, UFL_ANON_FACTION) && cansee(ctx->f, r, u, stealthmod);
|
||||
if (ballied || is_allied(ctx->f, sf)) {
|
||||
add_seen_faction_i(&flist, sf);
|
||||
lastf = sf;
|
||||
else if (r->seen.mode == seen_travel) {
|
||||
/* when we travel through a region, then we must add
|
||||
* the factions of any units we saw */
|
||||
add_travelthru_addresses(r, ctx->f, &flist, stealthmod);
|
||||
}
|
||||
else if (r->seen.mode > seen_travel) {
|
||||
const unit *u = r->units;
|
||||
while (u != NULL) {
|
||||
if (u->faction != ctx->f) {
|
||||
faction *sf = visible_faction(ctx->f, u);
|
||||
bool ballied = sf && sf != ctx->f && sf != lastf
|
||||
&& !fval(u, UFL_ANON_FACTION) && cansee(ctx->f, r, u, stealthmod);
|
||||
if (ballied || is_allied(ctx->f, sf)) {
|
||||
add_seen_faction_i(&flist, sf);
|
||||
lastf = sf;
|
||||
}
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1293,6 +1296,9 @@ static void add_seen_lighthouse(region *r, faction *f)
|
|||
if (r->terrain->flags & SEA_REGION) {
|
||||
add_seen_nb(f, r, seen_lighthouse);
|
||||
}
|
||||
else {
|
||||
add_seen_nb(f, r, seen_lighthouse_land);
|
||||
}
|
||||
}
|
||||
|
||||
/** mark all regions seen by the lighthouse.
|
||||
|
@ -1460,14 +1466,14 @@ void report_warnings(faction *f, int now)
|
|||
}
|
||||
|
||||
if (f->race == get_race(RC_INSECT)) {
|
||||
gamedate date;
|
||||
get_gamedate(now + 1, &date);
|
||||
season_t season = calendar_season(now + 1);
|
||||
|
||||
if (date.season == SEASON_WINTER) {
|
||||
if (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) {
|
||||
else if (season == SEASON_AUTUMN) {
|
||||
/* warning: next turn is the last week of autumn */
|
||||
if (calendar_season(now + 2) == SEASON_WINTER) {
|
||||
ADDMSG(&f->msgs, msg_message("nr_insectfall", ""));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -573,7 +573,7 @@ static void test_prepare_lighthouse(CuTest *tc) {
|
|||
CuAssertIntEquals(tc, seen_unit, r1->seen.mode);
|
||||
CuAssertIntEquals(tc, seen_lighthouse, r2->seen.mode);
|
||||
CuAssertIntEquals(tc, seen_neighbour, r3->seen.mode);
|
||||
CuAssertIntEquals(tc, seen_neighbour, r4->seen.mode);
|
||||
CuAssertIntEquals(tc, seen_lighthouse_land, r4->seen.mode);
|
||||
finish_reports(&ctx);
|
||||
test_teardown();
|
||||
}
|
||||
|
@ -868,6 +868,7 @@ static void test_visible_unit(CuTest *tc) {
|
|||
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_travel));
|
||||
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_none));
|
||||
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_neighbour));
|
||||
CuAssertTrue(tc, !visible_unit(u, f, 0, seen_lighthouse_land));
|
||||
|
||||
CuAssertTrue(tc, visible_unit(u, f, 0, seen_lighthouse));
|
||||
CuAssertTrue(tc, !visible_unit(u, f, -2, seen_lighthouse));
|
||||
|
|
|
@ -70,7 +70,7 @@ void expandstealing(region * r, econ_request * stealorders)
|
|||
break;
|
||||
}
|
||||
|
||||
u = findunit(requests[j]->type.steal.no);
|
||||
u = findunit(requests[j]->data.steal.no);
|
||||
|
||||
if (u && u->region == r) {
|
||||
n = get_pooled(u, rsilver, GET_ALL, INT_MAX);
|
||||
|
@ -233,8 +233,9 @@ void steal_cmd(unit * u, struct order *ord, econ_request ** stealorders)
|
|||
if (!o) abort();
|
||||
o->unit = u;
|
||||
o->qty = 1; /* Betrag steht in u->wants */
|
||||
o->type.steal.no = u2->no;
|
||||
o->type.steal.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */
|
||||
o->type = ECON_STEAL;
|
||||
o->data.steal.no = u2->no;
|
||||
o->data.steal.goblin = goblin; /* Merken, wenn Goblin-Spezialklau */
|
||||
o->next = *stealorders;
|
||||
*stealorders = o;
|
||||
|
||||
|
|
|
@ -860,8 +860,9 @@ void reduce_skill_days(unit *u, skill_t sk, int days) {
|
|||
}
|
||||
}
|
||||
|
||||
/** Talente von Daemonen verschieben sich.
|
||||
*/
|
||||
/**
|
||||
* Talente von Daemonen verschieben sich.
|
||||
*/
|
||||
void demon_skillchange(unit *u)
|
||||
{
|
||||
skill *sv = u->skills;
|
||||
|
|
|
@ -177,11 +177,11 @@ static int count_umlaut(const char *s)
|
|||
int result = 0;
|
||||
const char *cp;
|
||||
for (cp = s; *cp; ++cp) {
|
||||
ucs4_t ucs = *cp;
|
||||
if (ucs & 0x80) {
|
||||
wint_t wc = *cp;
|
||||
if (wc & 0x80) {
|
||||
size_t size;
|
||||
int err;
|
||||
err = unicode_utf8_to_ucs4(&ucs, cp, &size);
|
||||
err = unicode_utf8_decode(&wc, cp, &size);
|
||||
if (err != 0) {
|
||||
log_error("illegal utf8 encoding %s at %s", s, cp);
|
||||
return result;
|
||||
|
|
16
src/tests.c
16
src/tests.c
|
@ -277,7 +277,7 @@ 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 = malloc(sizeof(season_t) * months_per_year);
|
||||
if (!month_season) abort();
|
||||
month_season[0] = SEASON_SUMMER;
|
||||
month_season[1] = SEASON_AUTUMN;
|
||||
|
@ -576,15 +576,11 @@ struct message * test_find_messagetype_ex(struct message_list *msgs, const char
|
|||
struct mlist *ml;
|
||||
if (!msgs) return 0;
|
||||
for (ml = msgs->begin; ml; ml = ml->next) {
|
||||
if (strcmp(name, test_get_messagetype(ml->msg)) == 0) {
|
||||
if (prev) {
|
||||
if (ml->msg == prev) {
|
||||
prev = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ml->msg;
|
||||
}
|
||||
if (prev && ml->msg == prev) {
|
||||
prev = NULL;
|
||||
}
|
||||
else if (strcmp(name, test_get_messagetype(ml->msg)) == 0) {
|
||||
return ml->msg;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -26,12 +26,12 @@ static int eatwhite(const char *ptr, size_t * total_size)
|
|||
*total_size = 0;
|
||||
|
||||
while (*ptr) {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t size = 0;
|
||||
ret = unicode_utf8_to_ucs4(&ucs, ptr, &size);
|
||||
ret = unicode_utf8_decode(&wc, ptr, &size);
|
||||
if (ret != 0)
|
||||
break;
|
||||
if (!iswspace((wint_t)ucs))
|
||||
if (!iswspace(wc))
|
||||
break;
|
||||
*total_size += size;
|
||||
ptr += size;
|
||||
|
@ -86,7 +86,7 @@ static const char *getbuf_utf8(FILE * F)
|
|||
}
|
||||
cont = false;
|
||||
while (*bp && cp < fbuf + MAXLINE) {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
|
@ -119,14 +119,14 @@ static const char *getbuf_utf8(FILE * F)
|
|||
}
|
||||
}
|
||||
|
||||
ret = unicode_utf8_to_ucs4(&ucs, bp, &size);
|
||||
ret = unicode_utf8_decode(&wc, bp, &size);
|
||||
|
||||
if (ret != 0) {
|
||||
unicode_warning(bp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (iswspace((wint_t)ucs)) {
|
||||
if (iswspace(wc)) {
|
||||
if (!quote) {
|
||||
bp += size;
|
||||
ret = eatwhite(bp, &size);
|
||||
|
@ -151,7 +151,7 @@ static const char *getbuf_utf8(FILE * F)
|
|||
bp += size;
|
||||
}
|
||||
}
|
||||
else if (iswcntrl((wint_t)ucs)) {
|
||||
else if (iswcntrl(wc)) {
|
||||
if (!comment && cp < fbuf + MAXLINE) {
|
||||
*cp++ = '?';
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ static parse_state *states;
|
|||
static int eatwhitespace_c(const char **str_p)
|
||||
{
|
||||
int ret = 0;
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t len;
|
||||
const char *str = *str_p;
|
||||
|
||||
|
@ -40,12 +40,12 @@ static int eatwhitespace_c(const char **str_p)
|
|||
++str;
|
||||
}
|
||||
else {
|
||||
ret = unicode_utf8_to_ucs4(&ucs, str, &len);
|
||||
ret = unicode_utf8_decode(&wc, str, &len);
|
||||
if (ret != 0) {
|
||||
log_warning("illegal character sequence in UTF8 string: %s\n", str);
|
||||
break;
|
||||
}
|
||||
if (!iswspace((wint_t)ucs))
|
||||
if (!iswspace(wc))
|
||||
break;
|
||||
str += len;
|
||||
}
|
||||
|
@ -106,16 +106,16 @@ void skip_token(void)
|
|||
eatwhitespace_c(&states->current_token);
|
||||
|
||||
while (*states->current_token) {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t len;
|
||||
|
||||
unsigned char utf8_character = (unsigned char)states->current_token[0];
|
||||
if (~utf8_character & 0x80) {
|
||||
ucs = utf8_character;
|
||||
wc = utf8_character;
|
||||
++states->current_token;
|
||||
}
|
||||
else {
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, states->current_token, &len);
|
||||
int ret = unicode_utf8_decode(&wc, states->current_token, &len);
|
||||
if (ret == 0) {
|
||||
states->current_token += len;
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ void skip_token(void)
|
|||
log_warning("illegal character sequence in UTF8 string: %s\n", states->current_token);
|
||||
}
|
||||
}
|
||||
if (iswspace((wint_t)ucs) && quotechar == 0) {
|
||||
if (iswspace(wc) && quotechar == 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
|
@ -160,17 +160,17 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
|
|||
return 0;
|
||||
}
|
||||
while (*ctoken) {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t len;
|
||||
bool copy = false;
|
||||
|
||||
unsigned char utf8_character = *(unsigned char *)ctoken;
|
||||
if (~utf8_character & 0x80) {
|
||||
ucs = utf8_character;
|
||||
wc = utf8_character;
|
||||
len = 1;
|
||||
}
|
||||
else {
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, ctoken, &len);
|
||||
int ret = unicode_utf8_decode(&wc, ctoken, &len);
|
||||
if (ret != 0) {
|
||||
log_warning("illegal character sequence in UTF8 string: %s\n", ctoken);
|
||||
break;
|
||||
|
@ -180,7 +180,7 @@ char *parse_token(const char **str, char *lbuf, size_t buflen)
|
|||
copy = true;
|
||||
escape = false;
|
||||
}
|
||||
else if (iswspace((wint_t)ucs)) {
|
||||
else if (iswspace(wc)) {
|
||||
if (quotechar == 0)
|
||||
break;
|
||||
copy = true;
|
||||
|
|
|
@ -32,7 +32,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
|
||||
typedef struct tref {
|
||||
struct tref *nexthash;
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
struct tnode *node;
|
||||
} tref;
|
||||
|
||||
|
@ -99,8 +99,8 @@ char * transliterate(char * out, size_t size, const char * in)
|
|||
size -= advance;
|
||||
}
|
||||
else {
|
||||
ucs4_t ucs;
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, src, &len);
|
||||
wint_t wc;
|
||||
int ret = unicode_utf8_decode(&wc, src, &len);
|
||||
if (ret != 0) {
|
||||
/* encoding is broken. yikes */
|
||||
log_error("transliterate | encoding error in '%s'\n", src);
|
||||
|
@ -127,7 +127,7 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
{
|
||||
tnode * tk;
|
||||
static const struct replace {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
const char str[3];
|
||||
} replace[] = {
|
||||
/* match lower-case (!) umlauts and others to transcriptions */
|
||||
|
@ -150,10 +150,10 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
else {
|
||||
tref *next;
|
||||
int ret, index, i = 0;
|
||||
ucs4_t ucs, lcs;
|
||||
wint_t ucs, lcs;
|
||||
size_t len;
|
||||
|
||||
ret = unicode_utf8_to_ucs4(&ucs, str, &len);
|
||||
ret = unicode_utf8_decode(&ucs, str, &len);
|
||||
assert(ret == 0 || !"invalid utf8 string");
|
||||
lcs = ucs;
|
||||
|
||||
|
@ -166,7 +166,7 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
next = tk->next[index];
|
||||
if (!(tk->flags & LEAF))
|
||||
tk->id = id;
|
||||
while (next && next->ucs != ucs)
|
||||
while (next && next->wc != ucs)
|
||||
next = next->nexthash;
|
||||
if (!next) {
|
||||
tref *ref;
|
||||
|
@ -181,7 +181,7 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
|
||||
ref = (tref *)malloc(sizeof(tref));
|
||||
if (!ref) abort();
|
||||
ref->ucs = ucs;
|
||||
ref->wc = ucs;
|
||||
ref->node = node;
|
||||
ref->nexthash = tk->next[index];
|
||||
tk->next[index] = ref;
|
||||
|
@ -195,7 +195,7 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
#endif
|
||||
ref = (tref *)malloc(sizeof(tref));
|
||||
assert_alloc(ref);
|
||||
ref->ucs = lcs;
|
||||
ref->wc = lcs;
|
||||
ref->node = node;
|
||||
++node->refcount;
|
||||
ref->nexthash = tk->next[index];
|
||||
|
@ -211,7 +211,7 @@ void addtoken(tnode ** root, const char *str, variant id)
|
|||
}
|
||||
addtoken(&next->node, str + len, id);
|
||||
while (replace[i].str[0]) {
|
||||
if (lcs == replace[i].ucs) {
|
||||
if (lcs == replace[i].wc) {
|
||||
char zText[1024];
|
||||
memcpy(zText, replace[i].str, 3);
|
||||
str_strlcpy(zText + 2, (const char *)str + len, sizeof(zText)-2);
|
||||
|
@ -255,9 +255,9 @@ int findtoken(const void * root, const char *key, variant * result)
|
|||
do {
|
||||
int index;
|
||||
const tref *ref;
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t len;
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, str, &len);
|
||||
int ret = unicode_utf8_decode(&wc, str, &len);
|
||||
|
||||
if (ret != 0) {
|
||||
/* encoding is broken. youch */
|
||||
|
@ -265,12 +265,12 @@ int findtoken(const void * root, const char *key, variant * result)
|
|||
return E_TOK_NOMATCH;
|
||||
}
|
||||
#if NODEHASHSIZE == 8
|
||||
index = ucs & 7;
|
||||
index = wc & 7;
|
||||
#else
|
||||
index = ucs % NODEHASHSIZE;
|
||||
index = wc % NODEHASHSIZE;
|
||||
#endif
|
||||
ref = tk->next[index];
|
||||
while (ref && ref->ucs != ucs)
|
||||
while (ref && ref->wc != wc)
|
||||
ref = ref->nexthash;
|
||||
str += len;
|
||||
if (!ref) {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <wctype.h>
|
||||
#include <ctype.h>
|
||||
|
@ -33,18 +34,26 @@
|
|||
#define B00000011 0x03
|
||||
#define B00000001 0x01
|
||||
|
||||
int unicode_utf8_trim(utf8_t *buf)
|
||||
static bool char_trimmed(wint_t wc) {
|
||||
if (wc >= 0x2000 && wc <= 0x200f) {
|
||||
/* only weird stuff here */
|
||||
return true;
|
||||
}
|
||||
return iswspace(wc) || iswcntrl(wc);
|
||||
}
|
||||
|
||||
size_t unicode_utf8_trim(char *buf)
|
||||
{
|
||||
int result = 0, ts = 0;
|
||||
utf8_t *op = buf, *ip = buf, *lc = buf;
|
||||
char *op = buf, *ip = buf, *lc = buf;
|
||||
assert(buf);
|
||||
while (*ip) {
|
||||
size_t size = 1;
|
||||
wint_t wc = *ip;
|
||||
wint_t wc = *(unsigned char *)ip;
|
||||
if (wc & 0x80) {
|
||||
ucs4_t ucs = 0;
|
||||
wint_t ucs = 0;
|
||||
if (ip[1]) {
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, ip, &size);
|
||||
int ret = unicode_utf8_decode(&ucs, ip, &size);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -56,22 +65,24 @@ int unicode_utf8_trim(utf8_t *buf)
|
|||
++result;
|
||||
}
|
||||
}
|
||||
if (op == buf && iswspace(wc)) {
|
||||
++result;
|
||||
if (op == buf && char_trimmed(wc)) {
|
||||
result += size;
|
||||
}
|
||||
else if (wc>255 || !iscntrl(wc)) {
|
||||
else if (wc>255 || !iswcntrl(wc)) {
|
||||
if (op != ip) {
|
||||
memmove(op, ip, size);
|
||||
}
|
||||
op += size;
|
||||
if (iswspace(wc)) ++ts;
|
||||
if (char_trimmed(wc)) {
|
||||
ts += size;
|
||||
}
|
||||
else {
|
||||
lc = op;
|
||||
ts = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
++result;
|
||||
result += size;
|
||||
}
|
||||
ip += size;
|
||||
}
|
||||
|
@ -79,15 +90,15 @@ int unicode_utf8_trim(utf8_t *buf)
|
|||
return result + ts;
|
||||
}
|
||||
|
||||
int unicode_utf8_tolower(utf8_t * op, size_t outlen, const utf8_t * ip)
|
||||
int unicode_utf8_tolower(char * op, size_t outlen, const char * ip)
|
||||
{
|
||||
while (*ip) {
|
||||
ucs4_t ucs = *ip;
|
||||
ucs4_t low;
|
||||
wint_t ucs = *ip;
|
||||
wint_t low;
|
||||
size_t size = 1;
|
||||
|
||||
if (ucs & 0x80) {
|
||||
int ret = unicode_utf8_to_ucs4(&ucs, ip, &size);
|
||||
int ret = unicode_utf8_decode(&ucs, ip, &size);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -104,7 +115,7 @@ int unicode_utf8_tolower(utf8_t * op, size_t outlen, const utf8_t * ip)
|
|||
}
|
||||
else {
|
||||
ip += size;
|
||||
unicode_ucs4_to_utf8(op, &size, low);
|
||||
unicode_utf8_encode(op, &size, low);
|
||||
op += size;
|
||||
outlen -= size;
|
||||
}
|
||||
|
@ -114,7 +125,7 @@ int unicode_utf8_tolower(utf8_t * op, size_t outlen, const utf8_t * ip)
|
|||
}
|
||||
|
||||
int
|
||||
unicode_latin1_to_utf8(utf8_t * dst, size_t * outlen, const char *in,
|
||||
unicode_latin1_to_utf8(char * dst, size_t * outlen, const char *in,
|
||||
size_t * inlen)
|
||||
{
|
||||
int is = (int)*inlen;
|
||||
|
@ -148,15 +159,15 @@ unicode_latin1_to_utf8(utf8_t * dst, size_t * outlen, const char *in,
|
|||
return (int)*outlen;
|
||||
}
|
||||
|
||||
int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t *b)
|
||||
int unicode_utf8_strcasecmp(const char * a, const char *b)
|
||||
{
|
||||
while (*a && *b) {
|
||||
int ret;
|
||||
size_t size;
|
||||
ucs4_t ucsa = *a, ucsb = *b;
|
||||
wint_t ucsa = *a, ucsb = *b;
|
||||
|
||||
if (ucsa & 0x80) {
|
||||
ret = unicode_utf8_to_ucs4(&ucsa, a, &size);
|
||||
ret = unicode_utf8_decode(&ucsa, a, &size);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
a += size;
|
||||
|
@ -164,7 +175,7 @@ int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t *b)
|
|||
else
|
||||
++a;
|
||||
if (ucsb & 0x80) {
|
||||
ret = unicode_utf8_to_ucs4(&ucsb, b, &size);
|
||||
ret = unicode_utf8_decode(&ucsb, b, &size);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
b += size;
|
||||
|
@ -188,10 +199,10 @@ int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t *b)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Convert a UCS-4 character to UTF-8. */
|
||||
/* Convert a wide character to UTF-8. */
|
||||
int
|
||||
unicode_ucs4_to_utf8(utf8_t * utf8_character, size_t * size,
|
||||
ucs4_t ucs4_character)
|
||||
unicode_utf8_encode(char * utf8_character, size_t * size,
|
||||
wint_t ucs4_character)
|
||||
{
|
||||
int utf8_bytes;
|
||||
|
||||
|
@ -213,6 +224,7 @@ unicode_ucs4_to_utf8(utf8_t * utf8_character, size_t * size,
|
|||
utf8_character[1] = (char)(((ucs4_character >> 6) & B00111111) | B10000000);
|
||||
utf8_character[2] = (char)((ucs4_character & B00111111) | B10000000);
|
||||
}
|
||||
#if 0
|
||||
else if (ucs4_character <= 0x001FFFFF) {
|
||||
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
|
||||
utf8_bytes = 4;
|
||||
|
@ -246,6 +258,7 @@ unicode_ucs4_to_utf8(utf8_t * utf8_character, size_t * size,
|
|||
utf8_character[4] = (char)(((ucs4_character >> 6) & B00111111) | B10000000);
|
||||
utf8_character[5] = (char)((ucs4_character & B00111111) | B10000000);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return EILSEQ;
|
||||
}
|
||||
|
@ -257,10 +270,10 @@ unicode_ucs4_to_utf8(utf8_t * utf8_character, size_t * size,
|
|||
|
||||
/* Convert a UTF-8 encoded character to UCS-4. */
|
||||
int
|
||||
unicode_utf8_to_ucs4(ucs4_t * ucs4_character, const utf8_t * utf8_string,
|
||||
unicode_utf8_decode(wint_t * ucs4_character, const char * utf8_string,
|
||||
size_t * length)
|
||||
{
|
||||
utf8_t utf8_character = utf8_string[0];
|
||||
char utf8_character = utf8_string[0];
|
||||
|
||||
/* Is the character in the ASCII range? If so, just copy it to the
|
||||
output. */
|
||||
|
@ -361,13 +374,13 @@ unicode_utf8_to_ucs4(ucs4_t * ucs4_character, const utf8_t * utf8_string,
|
|||
|
||||
/** Convert a UTF-8 encoded character to CP437. */
|
||||
int
|
||||
unicode_utf8_to_cp437(unsigned char *cp_character, const utf8_t * utf8_string,
|
||||
unicode_utf8_to_cp437(unsigned char *cp_character, const char * utf8_string,
|
||||
size_t * length)
|
||||
{
|
||||
ucs4_t ucs4_character;
|
||||
wint_t ucs4_character;
|
||||
int result;
|
||||
|
||||
result = unicode_utf8_to_ucs4(&ucs4_character, utf8_string, length);
|
||||
result = unicode_utf8_decode(&ucs4_character, utf8_string, length);
|
||||
if (result != 0) {
|
||||
/* pass decoding characters upstream */
|
||||
return result;
|
||||
|
@ -378,7 +391,7 @@ unicode_utf8_to_cp437(unsigned char *cp_character, const utf8_t * utf8_string,
|
|||
}
|
||||
else {
|
||||
struct {
|
||||
ucs4_t ucs4;
|
||||
wint_t ucs4;
|
||||
unsigned char cp437;
|
||||
} xref[160] = {
|
||||
{ 0x00A0, 255 },
|
||||
|
@ -566,7 +579,7 @@ unicode_utf8_to_cp437(unsigned char *cp_character, const utf8_t * utf8_string,
|
|||
}
|
||||
|
||||
/** Convert a UTF-8 encoded character to ASCII, with '?' replacements. */
|
||||
int unicode_utf8_to_ascii(unsigned char *cp_character, const utf8_t * utf8_string,
|
||||
int unicode_utf8_to_ascii(unsigned char *cp_character, const char * utf8_string,
|
||||
size_t *length)
|
||||
{
|
||||
int result = unicode_utf8_to_cp437(cp_character, utf8_string, length);
|
||||
|
@ -579,13 +592,13 @@ int unicode_utf8_to_ascii(unsigned char *cp_character, const utf8_t * utf8_strin
|
|||
}
|
||||
|
||||
/** Convert a UTF-8 encoded character to CP1252. */
|
||||
int unicode_utf8_to_cp1252(unsigned char *cp_character, const utf8_t * utf8_string,
|
||||
int unicode_utf8_to_cp1252(unsigned char *cp_character, const char * utf8_string,
|
||||
size_t * length)
|
||||
{
|
||||
ucs4_t ucs4_character;
|
||||
wint_t ucs4_character;
|
||||
int result;
|
||||
|
||||
result = unicode_utf8_to_ucs4(&ucs4_character, utf8_string, length);
|
||||
result = unicode_utf8_decode(&ucs4_character, utf8_string, length);
|
||||
if (result != 0) {
|
||||
/* pass decoding characters upstream */
|
||||
return result;
|
||||
|
@ -596,7 +609,7 @@ int unicode_utf8_to_cp1252(unsigned char *cp_character, const utf8_t * utf8_stri
|
|||
}
|
||||
else {
|
||||
struct {
|
||||
ucs4_t ucs4;
|
||||
wint_t ucs4;
|
||||
unsigned char cp;
|
||||
} xref[] = {
|
||||
{ 0x0081, 0x81 },
|
||||
|
|
|
@ -19,30 +19,29 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#ifndef _UNICODE_H
|
||||
#define _UNICODE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <wchar.h>
|
||||
#define USE_UNICODE
|
||||
typedef long ucs4_t;
|
||||
typedef char utf8_t;
|
||||
|
||||
int unicode_utf8_to_cp437(unsigned char *result, const utf8_t * utf8_string,
|
||||
int unicode_utf8_to_cp437(unsigned char *result, const char * utf8_string,
|
||||
size_t * length);
|
||||
int unicode_utf8_to_cp1252(unsigned char *result, const utf8_t * utf8_string,
|
||||
int unicode_utf8_to_cp1252(unsigned char *result, const char * utf8_string,
|
||||
size_t * length);
|
||||
int unicode_utf8_to_ucs4(ucs4_t * result, const utf8_t * utf8_string,
|
||||
int unicode_utf8_decode(wint_t * result, const char * utf8_string,
|
||||
size_t * length);
|
||||
int unicode_ucs4_to_utf8(utf8_t * result, size_t * size,
|
||||
ucs4_t ucs4_character);
|
||||
int unicode_utf8_to_ascii(unsigned char *cp_character, const utf8_t * utf8_string,
|
||||
int unicode_utf8_encode(char * result, size_t * size,
|
||||
wint_t ucs4_character);
|
||||
int unicode_utf8_to_ascii(unsigned char *cp_character, const char * utf8_string,
|
||||
size_t *length);
|
||||
int unicode_utf8_strcasecmp(const utf8_t * a, const utf8_t * b);
|
||||
int unicode_latin1_to_utf8(utf8_t * out, size_t * outlen,
|
||||
int unicode_utf8_strcasecmp(const char * a, const char * b);
|
||||
int unicode_latin1_to_utf8(char * out, size_t * outlen,
|
||||
const char *in, size_t * inlen);
|
||||
int unicode_utf8_tolower(utf8_t *op, size_t outlen, const utf8_t *ip);
|
||||
int unicode_utf8_trim(utf8_t *ip);
|
||||
int unicode_utf8_tolower(char *op, size_t outlen, const char *ip);
|
||||
size_t unicode_utf8_trim(char *ip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#ifdef _MSC_VER
|
||||
#include <platform.h>
|
||||
#include <CuTest.h>
|
||||
#endif
|
||||
|
||||
#include "unicode.h"
|
||||
|
||||
#include <CuTest.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
@ -9,9 +15,33 @@ static void test_unicode_trim(CuTest * tc)
|
|||
{
|
||||
char buffer[32];
|
||||
|
||||
strcpy(buffer, "Hello Word");
|
||||
strcpy(buffer, "Hello World");
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "Hello Word", buffer);
|
||||
CuAssertStrEquals(tc, "Hello World", buffer);
|
||||
|
||||
strcpy(buffer, " Hello World");
|
||||
CuAssertIntEquals(tc, 2, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "Hello World", buffer);
|
||||
|
||||
strcpy(buffer, "Hello World ");
|
||||
CuAssertIntEquals(tc, 2, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "Hello World", buffer);
|
||||
|
||||
strcpy(buffer, " Hello World ");
|
||||
CuAssertIntEquals(tc, 2, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "Hello World", buffer);
|
||||
|
||||
strcpy(buffer, "Hello\t\r\nWorld");
|
||||
CuAssertIntEquals(tc, 3, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "HelloWorld", buffer);
|
||||
|
||||
strcpy(buffer, "LTR");
|
||||
buffer[3] = -30;
|
||||
buffer[4] = -128;
|
||||
buffer[5] = -114;
|
||||
buffer[6] = 0;
|
||||
CuAssertIntEquals(tc, 3, unicode_utf8_trim(buffer));
|
||||
CuAssertStrEquals(tc, "LTR", buffer);
|
||||
|
||||
strcpy(buffer, " Hello Word ");
|
||||
CuAssertIntEquals(tc, 4, unicode_utf8_trim(buffer));
|
||||
|
@ -48,7 +78,7 @@ static void test_unicode_tolower(CuTest * tc)
|
|||
static void test_unicode_utf8_to_other(CuTest *tc)
|
||||
{
|
||||
const unsigned char uchar_str[] = { 0xc3, 0x98, 0xc5, 0xb8, 0xc2, 0x9d, 'l', 0 }; /* ØŸl */
|
||||
utf8_t *utf8_str = (utf8_t *)uchar_str;
|
||||
char *utf8_str = (char *)uchar_str;
|
||||
unsigned char ch;
|
||||
size_t sz;
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_to_cp437(&ch, utf8_str, &sz));
|
||||
|
@ -92,27 +122,27 @@ static void test_unicode_utf8_to_other(CuTest *tc)
|
|||
}
|
||||
|
||||
static void test_unicode_utf8_to_ucs(CuTest *tc) {
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t sz;
|
||||
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_to_ucs4(&ucs, "a", &sz));
|
||||
CuAssertIntEquals(tc, 'a', ucs);
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_decode(&wc, "a", &sz));
|
||||
CuAssertIntEquals(tc, 'a', wc);
|
||||
CuAssertIntEquals(tc, 1, sz);
|
||||
}
|
||||
|
||||
static void test_unicode_bug2262(CuTest *tc) {
|
||||
char name[7];
|
||||
ucs4_t ucs;
|
||||
wint_t wc;
|
||||
size_t sz;
|
||||
|
||||
strcpy(name, "utende");
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_to_ucs4(&ucs, name, &sz));
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_decode(&wc, name, &sz));
|
||||
CuAssertIntEquals(tc, 1, sz);
|
||||
CuAssertIntEquals(tc, 'u', ucs);
|
||||
CuAssertIntEquals(tc, 'u', wc);
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_trim(name));
|
||||
|
||||
name[0] = -4; /* latin1: ü should fail to decode */
|
||||
CuAssertIntEquals(tc, EILSEQ, unicode_utf8_to_ucs4(&ucs, name, &sz));
|
||||
CuAssertIntEquals(tc, EILSEQ, unicode_utf8_decode(&wc, name, &sz));
|
||||
CuAssertIntEquals(tc, EILSEQ, unicode_utf8_trim(name));
|
||||
}
|
||||
|
||||
|
@ -123,26 +153,47 @@ static void test_unicode_compare(CuTest *tc)
|
|||
CuAssertIntEquals(tc, 1, unicode_utf8_strcasecmp("bacdefg123", "ABCDEFG123"));
|
||||
}
|
||||
|
||||
static void test_unicode_farsi_nzwj(CuTest *tc) {
|
||||
const char str[] = { 0xe2, 0x80, 0x8c, 0xd8, 0xa7, 0xd9, 0x84, 0xd8, 0xaf,
|
||||
0xdb, 0x8c, 0xd9, 0x86, 0x20, 0xd9, 0x85, 0xd8, 0xad, 0xd9, 0x85, 0xd8,
|
||||
0xaf, 0x20, 0xd8, 0xb1, 0xd9, 0x88, 0xd9, 0x85, 0xdb, 0x8c, 0xe2, 0x80,
|
||||
0x8e, 0xe2, 0x80, 0x8e, 0x00 };
|
||||
static void test_unicode_trim_zwnj(CuTest *tc) {
|
||||
const char zwnj[] = { 0xe2, 0x80, 0x8c, 0x00 };
|
||||
char name[64];
|
||||
strcpy(name, str);
|
||||
char expect[64];
|
||||
snprintf(name, sizeof(name), "%sA%sB%s ", zwnj, zwnj, zwnj);
|
||||
snprintf(expect, sizeof(expect), "A%sB", zwnj);
|
||||
CuAssertIntEquals(tc, 8, unicode_utf8_trim(name));
|
||||
CuAssertStrEquals(tc, expect, name);
|
||||
}
|
||||
|
||||
static void test_unicode_trim_ltrm(CuTest *tc) {
|
||||
const char ltrm[] = { 0xe2, 0x80, 0x8e, 0x00 };
|
||||
char name[64];
|
||||
char expect[64];
|
||||
snprintf(name, sizeof(name), "%sBrot%szeit%s ", ltrm, ltrm, ltrm);
|
||||
snprintf(expect, sizeof(expect), "Brot%szeit", ltrm);
|
||||
CuAssertIntEquals(tc, 8, unicode_utf8_trim(name));
|
||||
CuAssertStrEquals(tc, expect, name);
|
||||
}
|
||||
|
||||
static void test_unicode_trim_emoji(CuTest *tc) {
|
||||
const char clock[] = { 0xE2, 0x8F, 0xB0, 0x00 };
|
||||
char name[64];
|
||||
char expect[64];
|
||||
snprintf(name, sizeof(name), "%s Alarm%sClock %s", clock, clock, clock);
|
||||
strcpy(expect, name);
|
||||
CuAssertIntEquals(tc, 0, unicode_utf8_trim(name));
|
||||
CuAssertStrEquals(tc, str, name);
|
||||
CuAssertStrEquals(tc, expect, name);
|
||||
}
|
||||
|
||||
CuSuite *get_unicode_suite(void)
|
||||
{
|
||||
CuSuite *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_unicode_bug2262);
|
||||
SUITE_ADD_TEST(suite, test_unicode_tolower);
|
||||
SUITE_ADD_TEST(suite, test_unicode_trim);
|
||||
SUITE_ADD_TEST(suite, test_unicode_trim_zwnj);
|
||||
SUITE_ADD_TEST(suite, test_unicode_trim_ltrm);
|
||||
SUITE_ADD_TEST(suite, test_unicode_trim_emoji);
|
||||
SUITE_ADD_TEST(suite, test_unicode_utf8_to_other);
|
||||
SUITE_ADD_TEST(suite, test_unicode_utf8_to_ucs);
|
||||
SUITE_ADD_TEST(suite, test_unicode_compare);
|
||||
SUITE_ADD_TEST(suite, test_unicode_farsi_nzwj);
|
||||
SUITE_ADD_TEST(suite, test_unicode_bug2262);
|
||||
SUITE_ADD_TEST(suite, test_unicode_tolower);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,10 @@
|
|||
|
||||
static bool good_region(const region * r)
|
||||
{
|
||||
return (!fval(r, RF_CHAOTIC) && r->age > 30 && rplane(r) == NULL
|
||||
&& r->units != NULL && r->land != NULL);
|
||||
if (fval(r, RF_CHAOTIC) || r->age <= 100 || !r->units || !r->land || rplane(r)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cmp_age(const void *v1, const void *v2)
|
||||
|
|
|
@ -54,11 +54,11 @@ assert_grep_count reports/$CRFILE '^EINHEIT' 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 ';Baeume' 2
|
||||
assert_grep_count reports/185-heg.cr '"B.ume";type' 2
|
||||
assert_grep_count reports/185-heg.cr '"Pferde";type' 2
|
||||
assert_grep_count reports/185-heg.nr 'erblickt' 2
|
||||
assert_grep_count reports/185-heg.cr '"lighthouse";visibility' 2
|
||||
assert_grep_count reports/185-heg.cr ';Baeume' 4
|
||||
assert_grep_count reports/185-heg.cr '"B.ume";type' 4
|
||||
assert_grep_count reports/185-heg.cr '"Pferde";type' 6
|
||||
assert_grep_count reports/185-heg.nr 'erblickt' 6
|
||||
assert_grep_count reports/185-heg.cr '"lighthouse";visibility' 6
|
||||
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 '^REGION' 13
|
||||
|
|
Loading…
Add table
Reference in a new issue