Merge branch 'master' of github.com:eressea/server

This commit is contained in:
Enno Rehling 2014-09-21 10:00:02 +02:00
commit a5961d069c
16 changed files with 583 additions and 360 deletions

View File

@ -68,6 +68,7 @@ set (ERESSEA_SRC
battle.c
alchemy.c
stealth.c
upkeep.c
vortex.c
names.c
reports.c
@ -159,15 +160,16 @@ set(TESTS_SRC
tests.test.c
reports.test.c
stealth.test.c
move.test.c
callback.test.c
direction.test.c
keyword.test.c
skill.test.c
json.test.c
economy.test.c
market.test.c
json.test.c
keyword.test.c
laws.test.c
market.test.c
move.test.c
skill.test.c
upkeep.test.c
${UTIL_TESTS}
${KERNEL_TESTS}
${ERESSEA_SRC}

View File

@ -11,11 +11,13 @@ without prior permission by the authors of Eressea.
*/
#include <platform.h>
#include <kernel/types.h>
#include "bind_faction.h"
#include "bind_unit.h"
#include "bindings.h"
#include <kernel/alliance.h>
#include <kernel/faction.h>
#include <kernel/config.h>
#include <kernel/unit.h>
#include <kernel/item.h>
@ -452,8 +454,8 @@ static int tolua_faction_get_alliance(lua_State * L)
static int tolua_faction_set_alliance(lua_State * L)
{
faction *self = (faction *) tolua_tousertype(L, 1, 0);
alliance *alli = (alliance *) tolua_tousertype(L, 2, 0);
struct faction *self = (struct faction *)tolua_tousertype(L, 1, 0);
struct alliance *alli = (struct alliance *) tolua_tousertype(L, 2, 0);
setalliance(self, alli);

View File

@ -2154,6 +2154,13 @@ static void buy(unit * u, request ** buyorders, struct order *ord)
}
/* ------------------------------------------------------------- */
static void add_income(unit * u, int type, int want, int qty)
{
if (want == INT_MAX)
want = qty;
ADDMSG(&u->faction->msgs, msg_message("income",
"unit region mode wanted amount", u, u->region, type, want, qty));
}
/* Steuersätze in % bei Burggröße */
static int tax_per_size[7] = { 0, 6, 12, 18, 24, 30, 36 };

View File

@ -2240,27 +2240,6 @@ int besieged(const unit * u)
&& u->building->besieged >= u->building->size * SIEGEFACTOR);
}
int lifestyle(const unit * u)
{
int need;
plane *pl;
static int gamecookie = -1;
if (gamecookie != global.cookie) {
gamecookie = global.cookie;
}
if (is_monsters(u->faction))
return 0;
need = maintenance_cost(u);
pl = rplane(u->region);
if (pl && fval(pl, PFL_NOFEED))
return 0;
return need;
}
bool has_horses(const struct unit * u)
{
item *itm = u->items;
@ -2271,56 +2250,6 @@ bool has_horses(const struct unit * u)
return false;
}
bool hunger(int number, unit * u)
{
region *r = u->region;
int dead = 0, hpsub = 0;
int hp = u->hp / u->number;
static const char *damage = 0;
static const char *rcdamage = 0;
static const race *rc = 0;
if (!damage) {
damage = get_param(global.parameters, "hunger.damage");
if (damage == NULL)
damage = "1d12+12";
}
if (rc != u_race(u)) {
rcdamage = get_param(u_race(u)->parameters, "hunger.damage");
rc = u_race(u);
}
while (number--) {
int dam = dice_rand(rcdamage ? rcdamage : damage);
if (dam >= hp) {
++dead;
}
else {
hpsub += dam;
}
}
if (dead) {
/* Gestorbene aus der Einheit nehmen,
* Sie bekommen keine Beerdingung. */
ADDMSG(&u->faction->msgs, msg_message("starvation",
"unit region dead live", u, r, dead, u->number - dead));
scale_number(u, u->number - dead);
deathcounts(r, dead);
}
if (hpsub > 0) {
/* Jetzt die Schäden der nicht gestorbenen abziehen. */
u->hp -= hpsub;
/* Meldung nur, wenn noch keine für Tote generiert. */
if (dead == 0) {
/* Durch unzureichende Ernährung wird %s geschwächt */
ADDMSG(&u->faction->msgs, msg_message("malnourish", "unit region", u, r));
}
}
return (dead || hpsub);
}
void plagues(region * r, bool ismagic)
{
int peasants;
@ -2653,51 +2582,6 @@ int maintenance_cost(const struct unit *u)
return u_race(u)->maintenance * u->number;
}
message *movement_error(unit * u, const char *token, order * ord,
int error_code)
{
direction_t d;
switch (error_code) {
case E_MOVE_BLOCKED:
d = get_direction(token, u->faction->locale);
return msg_message("moveblocked", "unit direction", u, d);
case E_MOVE_NOREGION:
return msg_feedback(u, ord, "unknowndirection", "dirname", token);
}
return NULL;
}
bool move_blocked(const unit * u, const region * r, const region * r2)
{
connection *b;
curse *c;
static const curse_type *fogtrap_ct = NULL;
if (r2 == NULL)
return true;
b = get_borders(r, r2);
while (b) {
if (b->type->block && b->type->block(b, u, r))
return true;
b = b->next;
}
if (fogtrap_ct == NULL)
fogtrap_ct = ct_find("fogtrap");
c = get_curse(r->attribs, fogtrap_ct);
if (curse_active(c))
return true;
return false;
}
void add_income(unit * u, int type, int want, int qty)
{
if (want == INT_MAX)
want = qty;
ADDMSG(&u->faction->msgs, msg_message("income",
"unit region mode wanted amount", u, u->region, type, want, qty));
}
int produceexp(struct unit *u, skill_t sk, int n)
{
if (global.producexpchance > 0.0F) {

View File

@ -324,8 +324,6 @@ extern "C" {
*/
unsigned int guard_flags(const struct unit *u);
bool hunger(int number, struct unit *u);
int lifestyle(const struct unit *);
int besieged(const struct unit *u);
int maxworkingpeasants(const struct region *r);
bool has_horses(const struct unit *u);
@ -333,11 +331,6 @@ extern "C" {
int wage(const struct region *r, const struct faction *f,
const struct race *rc, int in_turn);
int maintenance_cost(const struct unit *u);
struct message *movement_error(struct unit *u, const char *token,
struct order *ord, int error_code);
bool move_blocked(const struct unit *u, const struct region *src,
const struct region *dest);
void add_income(struct unit *u, int type, int want, int qty);
const char *datapath(void);
void set_datapath(const char *path);

View File

@ -277,7 +277,7 @@ rel_to_abs(const struct plane *pl, const struct faction *f, int rel,
return (rel + ursprung_y(f, pl, NULL) + plane_center_y(pl));
}
int resolve_plane(variant id, void *addr)
static int resolve_plane(variant id, void *addr)
{
int result = 0;
plane *pl = NULL;

View File

@ -74,7 +74,6 @@ extern "C" {
extern int rel_to_abs(const struct plane *pl, const struct faction *f,
int rel, unsigned char index);
extern bool is_watcher(const struct plane *p, const struct faction *f);
extern int resolve_plane(variant data, void *addr);
extern void write_plane_reference(const plane * p, struct storage *store);
extern int read_plane_reference(plane ** pp, struct storage *store);
extern int plane_width(const plane * pl);

View File

@ -1,9 +1,11 @@
#include <platform.h>
#include "kernel/types.h"
#include "kernel/config.h"
#include "keyword.h"
#include "util/language.h"
#include "tests.h"
#include <critbit.h>
#include <CuTest.h>
static void test_init_keywords(CuTest *tc) {
@ -51,6 +53,24 @@ static void test_get_keyword_default(CuTest *tc) {
CuAssertIntEquals(tc, K_STUDY, get_keyword("study", lang));
}
static void test_get_shortest_match(CuTest *tc) {
struct locale *lang;
critbit_tree ** cb;
test_cleanup();
lang = get_or_create_locale("en");
cb = (critbit_tree **)get_translations(lang, UT_KEYWORDS);
/* note that the english order is FIGHT, not COMBAT, so this is a poor example */
add_translation(cb, "COMBAT", K_STATUS);
add_translation(cb, "COMBATSPELL", K_COMBATSPELL);
CuAssertIntEquals(tc, NOKEYWORD, get_keyword("", lang));
CuAssertIntEquals(tc, K_STATUS, get_keyword("COM", lang));
CuAssertIntEquals(tc, K_STATUS, get_keyword("COMBAT", lang));
CuAssertIntEquals(tc, K_COMBATSPELL, get_keyword("COMBATS", lang));
}
#define SUITE_DISABLE_TEST(suite, test) (void)test
CuSuite *get_keyword_suite(void)
@ -59,6 +79,7 @@ CuSuite *get_keyword_suite(void)
SUITE_ADD_TEST(suite, test_init_keyword);
SUITE_ADD_TEST(suite, test_init_keywords);
SUITE_ADD_TEST(suite, test_findkeyword);
SUITE_ADD_TEST(suite, test_get_shortest_match);
SUITE_DISABLE_TEST(suite, test_get_keyword_default);
return suite;
}

View File

@ -138,229 +138,6 @@ static void checkorders(void)
ADDMSG(&f->msgs, msg_message("turnreminder", ""));
}
static bool help_money(const unit * u)
{
if (u_race(u)->ec_flags & GIVEITEM)
return true;
return false;
}
static void help_feed(unit * donor, unit * u, int *need_p)
{
int need = *need_p;
int give = get_money(donor) - lifestyle(donor);
give = _min(need, give);
if (give > 0) {
change_money(donor, -give);
change_money(u, give);
need -= give;
add_spende(donor->faction, u->faction, give, donor->region);
}
*need_p = need;
}
enum {
FOOD_FROM_PEASANTS = 1,
FOOD_FROM_OWNER = 2,
FOOD_IS_FREE = 4
};
void get_food(region * r)
{
plane *pl = rplane(r);
unit *u;
int peasantfood = rpeasants(r) * 10;
static int food_rules = -1;
static int gamecookie = -1;
if (food_rules < 0 || gamecookie != global.cookie) {
gamecookie = global.cookie;
food_rules = get_param_int(global.parameters, "rules.economy.food", 0);
}
if (food_rules & FOOD_IS_FREE) {
return;
}
/* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber
* wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise
* jede Einheit genug Silber für ihren Unterhalt hat. */
for (u = r->units; u; u = u->next) {
int need = lifestyle(u);
/* Erstmal zurücksetzen */
freset(u, UFL_HUNGER);
if (u->ship && (u->ship->flags & SF_FISHING)) {
unit *v;
int c = 2;
for (v = u; c > 0 && v; v = v->next) {
if (v->ship == u->ship) {
int get = 0;
if (v->number <= c) {
get = lifestyle(v);
}
else {
get = lifestyle(v) * c / v->number;
}
if (get) {
change_money(v, get);
}
}
c -= v->number;
}
u->ship->flags -= SF_FISHING;
}
if (food_rules & FOOD_FROM_PEASANTS) {
faction *owner = region_get_owner(r);
/* if the region is owned, and the owner is nice, then we'll get
* food from the peasants - should not be used with WORK */
if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) {
int rm = rmoney(r);
int use = _min(rm, need);
rsetmoney(r, rm - use);
need -= use;
}
}
need -= get_money(u);
if (need > 0) {
unit *v;
for (v = r->units; need && v; v = v->next) {
if (v->faction == u->faction && help_money(v)) {
int give = get_money(v) - lifestyle(v);
give = _min(need, give);
if (give > 0) {
change_money(v, -give);
change_money(u, give);
need -= give;
}
}
}
}
}
/* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird
* entsprechend verteilt. */
for (u = r->units; u; u = u->next) {
int need = lifestyle(u);
faction *f = u->faction;
need -= _max(0, get_money(u));
if (need > 0) {
unit *v;
if (food_rules & FOOD_FROM_OWNER) {
/* the owner of the region is the first faction to help out when you're hungry */
faction *owner = region_get_owner(r);
if (owner && owner != u->faction) {
for (v = r->units; v; v = v->next) {
if (v->faction == owner && alliedunit(v, f, HELP_MONEY)
&& help_money(v)) {
help_feed(v, u, &need);
break;
}
}
}
}
for (v = r->units; need && v; v = v->next) {
if (v->faction != f && alliedunit(v, f, HELP_MONEY)
&& help_money(v)) {
help_feed(v, u, &need);
}
}
/* Die Einheit hat nicht genug Geld zusammengekratzt und
* nimmt Schaden: */
if (need > 0) {
int lspp = lifestyle(u) / u->number;
if (lspp > 0) {
int number = (need + lspp - 1) / lspp;
if (hunger(number, u))
fset(u, UFL_HUNGER);
}
}
}
}
/* 3. bestimmen, wie viele Bauern gefressen werden.
* bei fehlenden Bauern den Dämon hungern lassen
*/
for (u = r->units; u; u = u->next) {
if (u_race(u) == get_race(RC_DAEMON)) {
int hungry = u->number;
/* use peasantblood before eating the peasants themselves */
const struct potion_type *pt_blood = 0;
const resource_type *rt_blood = rt_find("peasantblood");
if (rt_blood) {
pt_blood = rt_blood->ptype;
}
if (pt_blood) {
/* always start with the unit itself, then the first known unit that may have some blood */
unit *donor = u;
while (donor != NULL && hungry > 0) {
int blut = get_effect(donor, pt_blood);
blut = _min(blut, hungry);
if (blut) {
change_effect(donor, pt_blood, -blut);
hungry -= blut;
}
if (donor == u)
donor = r->units;
while (donor != NULL) {
if (u_race(donor) == get_race(RC_DAEMON) && donor != u) {
if (get_effect(donor, pt_blood)) {
/* if he's in our faction, drain him: */
if (donor->faction == u->faction)
break;
}
}
donor = donor->next;
}
}
}
/* remaining demons feed on peasants */
if (pl == NULL || !fval(pl, PFL_NOFEED)) {
if (peasantfood >= hungry) {
peasantfood -= hungry;
hungry = 0;
}
else {
hungry -= peasantfood;
peasantfood = 0;
}
if (hungry > 0) {
static int demon_hunger = -1;
if (demon_hunger < 0) {
demon_hunger = get_param_int(global.parameters, "hunger.demons", 0);
}
if (demon_hunger == 0) {
/* demons who don't feed are hungry */
if (hunger(hungry, u))
fset(u, UFL_HUNGER);
}
else {
/* no damage, but set the hungry-flag */
fset(u, UFL_HUNGER);
}
}
}
}
}
rsetpeasants(r, peasantfood / 10);
/* 3. Von den überlebenden das Geld abziehen: */
for (u = r->units; u; u = u->next) {
int need = _min(get_money(u), lifestyle(u));
change_money(u, -need);
}
}
static void age_unit(region * r, unit * u)
{
if (u_race(u) == get_race(RC_SPELL)) {

View File

@ -1069,6 +1069,29 @@ unit *is_guarded(region * r, unit * u, unsigned int mask)
return NULL;
}
bool move_blocked(const unit * u, const region * r, const region * r2)
{
connection *b;
curse *c;
static const curse_type *fogtrap_ct = NULL;
if (r2 == NULL)
return true;
b = get_borders(r, r2);
while (b) {
if (b->type->block && b->type->block(b, u, r))
return true;
b = b->next;
}
if (fogtrap_ct == NULL)
fogtrap_ct = ct_find("fogtrap");
c = get_curse(r->attribs, fogtrap_ct);
if (curse_active(c))
return true;
return false;
}
int movewhere(const unit * u, const char *token, region * r, region ** resultp)
{
region *r2;
@ -1389,6 +1412,20 @@ static const region_list *reroute(unit * u, const region_list * route,
return route;
}
static message *movement_error(unit * u, const char *token, order * ord,
int error_code)
{
direction_t d;
switch (error_code) {
case E_MOVE_BLOCKED:
d = get_direction(token, u->faction->locale);
return msg_message("moveblocked", "unit direction", u, d);
case E_MOVE_NOREGION:
return msg_feedback(u, ord, "unknowndirection", "dirname", token);
}
return NULL;
}
static void make_route(unit * u, order * ord, region_list ** routep)
{
region_list **iroute = routep;

View File

@ -79,6 +79,8 @@ extern "C" {
const struct building_type *bt, bool working);
struct unit *owner_buildingtyp(const struct region *r,
const struct building_type *bt);
bool move_blocked(const struct unit *u, const struct region *src,
const struct region *dest);
#define SA_HARBOUR 2
#define SA_COAST 1

View File

@ -32,12 +32,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <attributes/reduceproduction.h>
/* gamecode includes */
#include "creport.h"
#include "economy.h"
#include "monster.h"
#include "laws.h"
#include "move.h"
#include "alchemy.h"
#include "economy.h"
#include "move.h"
#include "upkeep.h"
#include "vortex.h"
/* kernel includes */

View File

@ -58,6 +58,7 @@ int RunAllTests(void)
ADD_TESTS(suite, market);
ADD_TESTS(suite, move);
ADD_TESTS(suite, stealth);
ADD_TESTS(suite, upkeep);
ADD_TESTS(suite, vortex);
CuSuiteRun(suite);

308
src/upkeep.c Normal file
View File

@ -0,0 +1,308 @@
#include <platform.h>
#include "upkeep.h"
#include <kernel/types.h>
#include <kernel/faction.h>
#include <kernel/config.h>
#include <kernel/item.h>
#include <kernel/messages.h>
#include <kernel/plane.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/ship.h>
#include <kernel/unit.h>
#include <util/rand.h>
#include "alchemy.h"
#include "economy.h"
#include <assert.h>
int lifestyle(const unit * u)
{
int need;
plane *pl;
static int gamecookie = -1;
if (gamecookie != global.cookie) {
gamecookie = global.cookie;
}
if (is_monsters(u->faction))
return 0;
need = maintenance_cost(u);
pl = rplane(u->region);
if (pl && fval(pl, PFL_NOFEED))
return 0;
return need;
}
static bool help_money(const unit * u)
{
if (u_race(u)->ec_flags & GIVEITEM)
return true;
return false;
}
static void help_feed(unit * donor, unit * u, int *need_p)
{
int need = *need_p;
int give = get_money(donor) - lifestyle(donor);
give = _min(need, give);
if (give > 0) {
change_money(donor, -give);
change_money(u, give);
need -= give;
add_spende(donor->faction, u->faction, give, donor->region);
}
*need_p = need;
}
static bool hunger(int number, unit * u)
{
region *r = u->region;
int dead = 0, hpsub = 0;
int hp = u->hp / u->number;
static const char *damage = 0;
static const char *rcdamage = 0;
static const race *rc = 0;
if (!damage) {
damage = get_param(global.parameters, "hunger.damage");
if (damage == NULL)
damage = "1d12+12";
}
if (rc != u_race(u)) {
rcdamage = get_param(u_race(u)->parameters, "hunger.damage");
rc = u_race(u);
}
while (number--) {
int dam = dice_rand(rcdamage ? rcdamage : damage);
if (dam >= hp) {
++dead;
}
else {
hpsub += dam;
}
}
if (dead) {
/* Gestorbene aus der Einheit nehmen,
* Sie bekommen keine Beerdingung. */
ADDMSG(&u->faction->msgs, msg_message("starvation",
"unit region dead live", u, r, dead, u->number - dead));
scale_number(u, u->number - dead);
deathcounts(r, dead);
}
if (hpsub > 0) {
/* Jetzt die Schäden der nicht gestorbenen abziehen. */
u->hp -= hpsub;
/* Meldung nur, wenn noch keine für Tote generiert. */
if (dead == 0) {
/* Durch unzureichende Ernährung wird %s geschwächt */
ADDMSG(&u->faction->msgs, msg_message("malnourish", "unit region", u, r));
}
}
return (dead || hpsub);
}
void get_food(region * r)
{
plane *pl = rplane(r);
unit *u;
int peasantfood = rpeasants(r) * 10;
static int food_rules = -1;
static int gamecookie = -1;
if (food_rules < 0 || gamecookie != global.cookie) {
gamecookie = global.cookie;
food_rules = get_param_int(global.parameters, "rules.economy.food", 0);
}
if (food_rules & FOOD_IS_FREE) {
return;
}
/* 1. Versorgung von eigenen Einheiten. Das vorhandene Silber
* wird zunächst so auf die Einheiten aufgeteilt, dass idealerweise
* jede Einheit genug Silber für ihren Unterhalt hat. */
for (u = r->units; u; u = u->next) {
int need = lifestyle(u);
/* Erstmal zurücksetzen */
freset(u, UFL_HUNGER);
if (u->ship && (u->ship->flags & SF_FISHING)) {
unit *v;
int c = 2;
for (v = u; c > 0 && v; v = v->next) {
if (v->ship == u->ship) {
int get = 0;
if (v->number <= c) {
get = lifestyle(v);
}
else {
get = lifestyle(v) * c / v->number;
}
if (get) {
change_money(v, get);
}
}
c -= v->number;
}
u->ship->flags -= SF_FISHING;
}
if (food_rules & FOOD_FROM_PEASANTS) {
struct faction *owner = region_get_owner(r);
/* if the region is owned, and the owner is nice, then we'll get
* food from the peasants - should not be used with WORK */
if (owner != NULL && (get_alliance(owner, u->faction) & HELP_MONEY)) {
int rm = rmoney(r);
int use = _min(rm, need);
rsetmoney(r, rm - use);
need -= use;
}
}
need -= get_money(u);
if (need > 0) {
unit *v;
for (v = r->units; need && v; v = v->next) {
if (v->faction == u->faction && help_money(v)) {
int give = get_money(v) - lifestyle(v);
give = _min(need, give);
if (give > 0) {
change_money(v, -give);
change_money(u, give);
need -= give;
}
}
}
}
}
/* 2. Versorgung durch Fremde. Das Silber alliierter Einheiten wird
* entsprechend verteilt. */
for (u = r->units; u; u = u->next) {
int need = lifestyle(u);
faction *f = u->faction;
need -= _max(0, get_money(u));
if (need > 0) {
unit *v;
if (food_rules & FOOD_FROM_OWNER) {
/* the owner of the region is the first faction to help out when you're hungry */
faction *owner = region_get_owner(r);
if (owner && owner != u->faction) {
for (v = r->units; v; v = v->next) {
if (v->faction == owner && alliedunit(v, f, HELP_MONEY)
&& help_money(v)) {
help_feed(v, u, &need);
break;
}
}
}
}
for (v = r->units; need && v; v = v->next) {
if (v->faction != f && alliedunit(v, f, HELP_MONEY)
&& help_money(v)) {
help_feed(v, u, &need);
}
}
/* Die Einheit hat nicht genug Geld zusammengekratzt und
* nimmt Schaden: */
if (need > 0) {
int lspp = lifestyle(u) / u->number;
if (lspp > 0) {
int number = (need + lspp - 1) / lspp;
if (hunger(number, u))
fset(u, UFL_HUNGER);
}
}
}
}
/* 3. bestimmen, wie viele Bauern gefressen werden.
* bei fehlenden Bauern den Dämon hungern lassen
*/
for (u = r->units; u; u = u->next) {
if (u_race(u) == get_race(RC_DAEMON)) {
int hungry = u->number;
/* use peasantblood before eating the peasants themselves */
const struct potion_type *pt_blood = 0;
const resource_type *rt_blood = rt_find("peasantblood");
if (rt_blood) {
pt_blood = rt_blood->ptype;
}
if (pt_blood) {
/* always start with the unit itself, then the first known unit that may have some blood */
unit *donor = u;
while (donor != NULL && hungry > 0) {
int blut = get_effect(donor, pt_blood);
blut = _min(blut, hungry);
if (blut) {
change_effect(donor, pt_blood, -blut);
hungry -= blut;
}
if (donor == u)
donor = r->units;
while (donor != NULL) {
if (u_race(donor) == get_race(RC_DAEMON) && donor != u) {
if (get_effect(donor, pt_blood)) {
/* if he's in our faction, drain him: */
if (donor->faction == u->faction)
break;
}
}
donor = donor->next;
}
}
}
/* remaining demons feed on peasants */
if (pl == NULL || !fval(pl, PFL_NOFEED)) {
if (peasantfood >= hungry) {
peasantfood -= hungry;
hungry = 0;
}
else {
hungry -= peasantfood;
peasantfood = 0;
}
if (hungry > 0) {
static int demon_hunger = -1;
if (demon_hunger < 0) {
demon_hunger = get_param_int(global.parameters, "hunger.demons", 0);
}
if (demon_hunger == 0) {
/* demons who don't feed are hungry */
if (hunger(hungry, u))
fset(u, UFL_HUNGER);
}
else {
/* no damage, but set the hungry-flag */
fset(u, UFL_HUNGER);
}
}
}
}
}
rsetpeasants(r, peasantfood / 10);
/* 3. Von den überlebenden das Geld abziehen: */
for (u = r->units; u; u = u->next) {
int need = _min(get_money(u), lifestyle(u));
change_money(u, -need);
}
}

22
src/upkeep.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef UPKEEP_H
#define UPKEEP_H
#ifdef __cplusplus
extern "C" {
#endif
struct region;
struct unit;
void get_food(struct region * r);
int lifestyle(const struct unit * u);
enum {
FOOD_FROM_PEASANTS = 1,
FOOD_FROM_OWNER = 2,
FOOD_IS_FREE = 4
};
#ifdef __cplusplus
}
#endif
#endif

170
src/upkeep.test.c Normal file
View File

@ -0,0 +1,170 @@
#include <platform.h>
#include "upkeep.h"
#include <kernel/config.h>
#include <kernel/faction.h>
#include <kernel/region.h>
#include <kernel/unit.h>
#include <kernel/item.h>
#include <CuTest.h>
#include <tests.h>
#include <assert.h>
void test_upkeep_default(CuTest * tc)
{
region *r;
unit *u1, *u2;
faction *f1, *f2;
const item_type *i_silver;
test_cleanup();
test_create_world();
i_silver = it_find("money");
assert(i_silver);
r = findregion(0, 0);
f1 = test_create_faction(test_create_race("human"));
f2 = test_create_faction(test_create_race("human"));
assert(f1 && f2);
u1 = test_create_unit(f1, r);
u2 = test_create_unit(f2, r);
assert(r && u1 && u2);
set_param(&global.parameters, "rules.economy.food", "0");
i_change(&u1->items, i_silver, 20);
get_food(r);
// since u1 and u2 are not allied, u1 should not help u2 with upkeep
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
test_cleanup();
}
void test_upkeep_hunger_damage(CuTest * tc)
{
region *r;
unit *u1;
faction *f1;
const item_type *i_silver;
test_cleanup();
test_create_world();
i_silver = it_find("money");
assert(i_silver);
r = findregion(0, 0);
f1 = test_create_faction(test_create_race("human"));
u1 = test_create_unit(f1, r);
assert(r && u1);
set_param(&global.parameters, "rules.economy.food", "0");
u1->hp = 100;
get_food(r);
// since u1 and u2 are not allied, u1 should not help u2 with upkeep
CuAssertTrue(tc, u1->hp<100);
test_cleanup();
}
void test_upkeep_from_pool(CuTest * tc)
{
region *r;
unit *u1, *u2;
const item_type *i_silver;
test_cleanup();
test_create_world();
i_silver = it_find("money");
assert(i_silver);
r = findregion(0, 0);
u1 = test_create_unit(test_create_faction(test_create_race("human")), r);
u2 = test_create_unit(u1->faction, r);
assert(r && u1 && u2);
set_param(&global.parameters, "rules.economy.food", "0");
i_change(&u1->items, i_silver, 30);
get_food(r);
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
CuAssertIntEquals(tc, 0, fval(u2, UFL_HUNGER));
get_food(r);
CuAssertIntEquals(tc, 0, i_get(u1->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
test_cleanup();
}
void test_upkeep_from_friend(CuTest * tc)
{
region *r;
unit *u1, *u2;
faction *f1, *f2;
const item_type *i_silver;
test_cleanup();
test_create_world();
i_silver = it_find("money");
assert(i_silver);
r = findregion(0, 0);
f1 = test_create_faction(test_create_race("human"));
f2 = test_create_faction(test_create_race("human"));
assert(f1 && f2);
set_alliance(f1, f2, HELP_MONEY);
u1 = test_create_unit(f1, r);
u2 = test_create_unit(f2, r);
assert(r && u1 && u2);
set_param(&global.parameters, "rules.economy.food", "0");
i_change(&u1->items, i_silver, 30);
get_food(r);
CuAssertIntEquals(tc, 10, i_get(u1->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
CuAssertIntEquals(tc, 0, fval(u2, UFL_HUNGER));
get_food(r);
CuAssertIntEquals(tc, 0, i_get(u1->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u1, UFL_HUNGER));
CuAssertIntEquals(tc, UFL_HUNGER, fval(u2, UFL_HUNGER));
test_cleanup();
}
void test_upkeep_free(CuTest * tc)
{
region *r;
unit *u;
const item_type *i_silver;
test_cleanup();
test_create_world();
i_silver = it_find("money");
assert(i_silver);
r = findregion(0, 0);
u = test_create_unit(test_create_faction(test_create_race("human")), r);
assert(r && u);
set_param(&global.parameters, "rules.economy.food", "4"); // FOOD_IS_FREE
get_food(r);
CuAssertIntEquals(tc, 0, i_get(u->items, i_silver));
CuAssertIntEquals(tc, 0, fval(u, UFL_HUNGER));
test_cleanup();
}
CuSuite *get_upkeep_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_upkeep_default);
SUITE_ADD_TEST(suite, test_upkeep_from_pool);
SUITE_ADD_TEST(suite, test_upkeep_from_friend);
SUITE_ADD_TEST(suite, test_upkeep_hunger_damage);
SUITE_ADD_TEST(suite, test_upkeep_free);
return suite;
}