forked from github/server
Merge branch 'master' of github.com:eressea/server
This commit is contained in:
commit
a5961d069c
|
@ -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}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
223
src/laws.c
223
src/laws.c
|
@ -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)) {
|
||||
|
|
37
src/move.c
37
src/move.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue