Merge pull request #50 from badgerman/master

Bugfixes pre-release turn 901, new storage library.
This commit is contained in:
Enno Rehling 2014-11-03 22:37:14 +01:00
commit 7b47f5fa78
19 changed files with 642 additions and 434 deletions

33
scripts/fix1984.lua Normal file
View File

@ -0,0 +1,33 @@
function recover(turn)
eressea.free_game()
eressea.read_game(turn .. ".dat")
ships = {}
bldgs = {}
for r in regions() do
for b in r.buildings do
if b.info~=nil and (string.len(b.info)>120) then
bldgs[b.id] = b.info
end
end
for b in r.ships do
if b.info~=nil and (string.len(b.info)>120) then
ships[b.id] = b.info
end
end
end
eressea.free_game()
eressea.read_game((turn+1) .. ".dat")
for k, v in pairs(bldgs) do
b = get_building(k)
if b~=nil then
b.info = v
end
end
for k, v in pairs(ships) do
b = get_ship(k)
if b~=nil then
b.info = v
end
end
eressea.write_game((turn+1) .. ".fixed")
end

View File

@ -166,6 +166,7 @@ set(TESTS_SRC
economy.test.c
json.test.c
keyword.test.c
give.test.c
laws.test.c
magic.test.c
market.test.c

View File

@ -18,6 +18,8 @@ without prior permission by the authors of Eressea.
#include <kernel/version.h>
#include <storage.h>
#include <stream.h>
#include <filestream.h>
#include <binarystore.h>
#include <math.h>
@ -29,36 +31,37 @@ without prior permission by the authors of Eressea.
static int tolua_storage_create(lua_State * L)
{
const char *filename = tolua_tostring(L, 1, 0);
const char *type = tolua_tostring(L, 2, "rb");
gamedata *data = (gamedata *)calloc(1, sizeof(gamedata));
storage *store = (storage *)calloc(1, sizeof(storage));
FILE * F;
const char *filename = tolua_tostring(L, 1, 0);
const char *type = tolua_tostring(L, 2, "rb");
gamedata *data = (gamedata *)calloc(1, sizeof(gamedata));
storage *store = (storage *)calloc(1, sizeof(storage));
FILE * F;
data->store = store;
data->store = store;
F = fopen(filename, type);
if (strchr(type, 'r')) {
fread(&data->version, sizeof(int), 1, F);
fseek(F, sizeof(int), SEEK_CUR);
}
else if (strchr(type, 'w')) {
int n = STREAM_VERSION;
data->version = RELEASE_VERSION;
fwrite(&data->version, sizeof(int), 1, F);
fwrite(&n, sizeof(int), 1, F);
}
binstore_init(store, F);
tolua_pushusertype(L, (void *)data, TOLUA_CAST "storage");
return 1;
F = fopen(filename, type);
if (strchr(type, 'r')) {
fread(&data->version, sizeof(int), 1, F);
fseek(F, sizeof(int), SEEK_CUR);
}
else if (strchr(type, 'w')) {
int n = STREAM_VERSION;
data->version = RELEASE_VERSION;
fwrite(&data->version, sizeof(int), 1, F);
fwrite(&n, sizeof(int), 1, F);
}
fstream_init(&data->strm, F);
binstore_init(store, &data->strm);
tolua_pushusertype(L, (void *)data, TOLUA_CAST "storage");
return 1;
}
static int tolua_storage_read_unit(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
struct unit *u = read_unit(data);
tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit");
return 1;
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
struct unit *u = read_unit(data);
tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit");
return 1;
}
static int tolua_storage_write_unit(lua_State * L)
@ -71,76 +74,78 @@ static int tolua_storage_write_unit(lua_State * L)
static int tolua_storage_read_float(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
float num;
READ_FLT(data->store, &num);
tolua_pushnumber(L, (lua_Number) num);
return 1;
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
float num;
READ_FLT(data->store, &num);
tolua_pushnumber(L, (lua_Number)num);
return 1;
}
static int tolua_storage_read_int(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
int num;
READ_INT(data->store, &num);
tolua_pushnumber(L, (lua_Number) num);
return 1;
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
int num;
READ_INT(data->store, &num);
tolua_pushnumber(L, (lua_Number)num);
return 1;
}
static int tolua_storage_write(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
if (data->version && tolua_isnumber(L, 2, 0, 0)) {
lua_Number num = tolua_tonumber(L, 2, 0);
double n;
if (modf(num, &n) == 0.0) {
WRITE_INT(data->store, (int)num);
} else {
WRITE_FLT(data->store, (float)num);
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
if (data->version && tolua_isnumber(L, 2, 0, 0)) {
lua_Number num = tolua_tonumber(L, 2, 0);
double n;
if (modf(num, &n) == 0.0) {
WRITE_INT(data->store, (int)num);
}
else {
WRITE_FLT(data->store, (float)num);
}
}
}
return 0;
return 0;
}
static int tolua_storage_tostring(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
char name[64];
_snprintf(name, sizeof(name), "<storage enc=%d ver=%d>", data->encoding,
data->version);
lua_pushstring(L, name);
return 1;
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
char name[64];
_snprintf(name, sizeof(name), "<storage enc=%d ver=%d>", data->encoding,
data->version);
lua_pushstring(L, name);
return 1;
}
static int tolua_storage_close(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
binstore_done(data->store);
return 0;
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
binstore_done(data->store);
fstream_done(&data->strm);
return 0;
}
void tolua_storage_open(lua_State * L)
{
/* register user types */
tolua_usertype(L, TOLUA_CAST "storage");
/* register user types */
tolua_usertype(L, TOLUA_CAST "storage");
tolua_module(L, NULL, 0);
tolua_beginmodule(L, NULL);
{
tolua_cclass(L, TOLUA_CAST "storage", TOLUA_CAST "storage", TOLUA_CAST "",
NULL);
tolua_beginmodule(L, TOLUA_CAST "storage");
tolua_module(L, NULL, 0);
tolua_beginmodule(L, NULL);
{
tolua_function(L, TOLUA_CAST "__tostring", tolua_storage_tostring);
tolua_function(L, TOLUA_CAST "write", tolua_storage_write);
tolua_function(L, TOLUA_CAST "read_int", tolua_storage_read_int);
tolua_function(L, TOLUA_CAST "read_float", tolua_storage_read_float);
tolua_function(L, TOLUA_CAST "write_unit", tolua_storage_write_unit);
tolua_function(L, TOLUA_CAST "read_unit", tolua_storage_read_unit);
tolua_function(L, TOLUA_CAST "close", tolua_storage_close);
tolua_function(L, TOLUA_CAST "create", tolua_storage_create);
tolua_cclass(L, TOLUA_CAST "storage", TOLUA_CAST "storage", TOLUA_CAST "",
NULL);
tolua_beginmodule(L, TOLUA_CAST "storage");
{
tolua_function(L, TOLUA_CAST "__tostring", tolua_storage_tostring);
tolua_function(L, TOLUA_CAST "write", tolua_storage_write);
tolua_function(L, TOLUA_CAST "read_int", tolua_storage_read_int);
tolua_function(L, TOLUA_CAST "read_float", tolua_storage_read_float);
tolua_function(L, TOLUA_CAST "write_unit", tolua_storage_write_unit);
tolua_function(L, TOLUA_CAST "read_unit", tolua_storage_read_unit);
tolua_function(L, TOLUA_CAST "close", tolua_storage_close);
tolua_function(L, TOLUA_CAST "create", tolua_storage_create);
}
tolua_endmodule(L);
}
tolua_endmodule(L);
}
tolua_endmodule(L);
}

View File

@ -388,36 +388,6 @@ static int do_recruiting(recruitment * recruits, int available)
return recruited;
}
static void feedback_give_not_allowed(unit * u, order * ord)
{
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_give_forbidden",
""));
}
static bool can_give(const unit * u, const unit * u2, const item_type * itype, int mask)
{
if (u2) {
if (u->faction != u2->faction) {
int rule = rule_give();
if (itype) {
assert(mask == 0);
if (itype->rtype->ltype)
mask |= GIVE_LUXURIES;
else if (fval(itype, ITF_HERB))
mask |= GIVE_HERBS;
else
mask |= GIVE_GOODS;
}
return (rule & mask) != 0;
}
}
else {
int rule = rule_give();
return (rule & GIVE_PEASANTS) != 0;
}
return true;
}
void free_recruitments(recruitment * recruits)
{
while (recruits) {
@ -708,273 +678,6 @@ int give_control_cmd(unit * u, order * ord)
return 0;
}
message *check_give(const unit *u, const unit *u2, order * ord) {
if (!can_give(u, u2, NULL, GIVE_ALLITEMS)) {
return msg_feedback(u, ord, "feedback_give_forbidden", "");
}
return 0;
}
static void give_cmd(unit * u, order * ord)
{
region *r = u->region;
unit *u2;
const char *s;
int n;
const item_type *itype;
param_t p;
plane *pl;
message *msg;
init_order(ord);
u2 = getunit(r, u->faction);
s = getstrtoken();
n = s ? atoip(s) : 0;
p = (n > 0) ? NOPARAM : findparam(s, u->faction->locale);
/* first, do all the ones that do not require HELP_GIVE or CONTACT */
if (p == P_CONTROL) {
/* handled in give_control_cmd */
return;
}
if (!u2 && !getunitpeasants) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
msg = check_give(u, u2, ord);
if (msg) {
ADDMSG(&u->faction->msgs, msg);
return;
}
/* Damit Tarner nicht durch die Fehlermeldung enttarnt werden können */
if (u2 && !alliedunit(u2, u->faction, HELP_GIVE)
&& !cansee(u->faction, r, u2, 0) && !ucontact(u2, u)
&& !fval(u2, UFL_TAKEALL)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
if (u == u2) {
cmistake(u, ord, 8, MSG_COMMERCE);
return;
}
/* UFL_TAKEALL ist ein grober Hack. Generalisierung tut not, ist aber nicht
* wirklich einfach. */
pl = rplane(r);
if (pl && fval(pl, PFL_NOGIVE) && (!u2 || !fval(u2, UFL_TAKEALL))) {
cmistake(u, ord, 268, MSG_COMMERCE);
return;
}
if (u2 && u_race(u2) == get_race(RC_SPELL)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
else if (u2 && !alliedunit(u2, u->faction, HELP_GIVE) && !ucontact(u2, u)) {
cmistake(u, ord, 40, MSG_COMMERCE);
return;
}
else if (p == P_HERBS) {
bool given = false;
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (!can_give(u, u2, NULL, GIVE_HERBS)) {
feedback_give_not_allowed(u, ord);
return;
}
if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
if (!u2) {
if (!getunitpeasants) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
"feedback_unit_not_found", ""));
return;
}
}
if (u->items) {
item **itmp = &u->items;
while (*itmp) {
item *itm = *itmp;
const item_type *itype = itm->type;
if (fval(itype, ITF_HERB) && itm->number > 0) {
/* give_item ändert im fall,das man alles übergibt, die
* item-liste der unit, darum continue vor pointerumsetzten */
if (give_item(itm->number, itm->type, u, u2, ord) == 0) {
given = true;
if (*itmp != itm)
continue;
continue;
}
}
itmp = &itm->next;
}
}
if (!given)
cmistake(u, ord, 38, MSG_COMMERCE);
return;
}
else if (p == P_ZAUBER) {
cmistake(u, ord, 7, MSG_COMMERCE);
/* geht nimmer */
return;
}
else if (p == P_UNIT) { /* Einheiten uebergeben */
if (!(u_race(u)->ec_flags & GIVEUNIT)) {
cmistake(u, ord, 167, MSG_COMMERCE);
return;
}
give_unit(u, u2, ord);
return;
}
else if (p == P_ANY) {
const char *s;
if (!can_give(u, u2, NULL, GIVE_ALLITEMS)) {
feedback_give_not_allowed(u, ord);
return;
}
s = getstrtoken();
if (!s || *s == 0) { /* GIVE ALL items that you have */
/* do these checks once, not for each item we have: */
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
/* für alle items einmal prüfen, ob wir mehr als von diesem Typ
* reserviert ist besitzen und diesen Teil dann übergeben */
if (u->items) {
item **itmp = &u->items;
while (*itmp) {
item *itm = *itmp;
const item_type *itype = itm->type;
if (itm->number > 0
&& itm->number - get_reservation(u, itype->rtype) > 0) {
n = itm->number - get_reservation(u, itype->rtype);
if (give_item(n, itype, u, u2, ord) == 0) {
if (*itmp != itm)
continue;
}
}
itmp = &itm->next;
}
}
}
else {
if (isparam(s, u->faction->locale, P_PERSON)) {
if (!(u_race(u)->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_noregroup", "race", u_race(u)));
}
else {
n = u->number;
give_men(n, u, u2, ord);
}
}
else if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
}
else if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
}
else {
itype = finditemtype(s, u->faction->locale);
if (itype != NULL) {
item *i = *i_find(&u->items, itype);
if (i != NULL) {
if (can_give(u, u2, itype, 0)) {
n = i->number - get_reservation(u, itype->rtype);
give_item(n, itype, u, u2, ord);
}
else {
feedback_give_not_allowed(u, ord);
}
}
}
}
}
return;
}
else if (p == P_EACH) {
if (u2 == NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "peasants_give_invalid", ""));
return;
}
s = getstrtoken(); /* skip one ahead to get the amount. */
n = atoip(s); /* n: Anzahl */
n *= u2->number;
}
s = getstrtoken();
if (s == NULL) {
cmistake(u, ord, 113, MSG_COMMERCE);
return;
}
if (isparam(s, u->faction->locale, P_PERSON)) {
if (!(u_race(u)->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_noregroup", "race", u_race(u)));
return;
}
give_men(n, u, u2, ord);
return;
}
if (u2 != NULL) {
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (!(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
}
itype = finditemtype(s, u->faction->locale);
if (itype != NULL) {
if (can_give(u, u2, itype, 0)) {
give_item(n, itype, u, u2, ord);
}
else {
feedback_give_not_allowed(u, ord);
}
return;
}
cmistake(u, ord, 123, MSG_COMMERCE);
}
static int forget_cmd(unit * u, order * ord)
{
skill_t sk;

View File

@ -37,11 +37,17 @@ extern "C" {
#define TRADE_FRACTION 100
extern int income(const struct unit *u);
/* Wieviel Fremde eine Partei pro Woche aufnehmen kann */
#define MAXNEWBIES 5
struct unit;
struct region;
struct faction;
struct order;
struct message;
int income(const struct unit *u);
void economics(struct region *r);
void produce(struct region *r);
void auto_work(struct region *r);
@ -56,7 +62,6 @@ extern "C" {
extern void give_control(struct unit * u, struct unit * u2);
struct message * check_steal(const struct unit * u, struct order *ord);
struct message * check_give(const struct unit * u, const struct unit * u2, struct order *ord);
#ifdef __cplusplus
}

View File

@ -63,7 +63,7 @@ struct steal {
struct faction *f;
};
static void setup_steal(struct steal *env, terrain_type *ter, race *rc) {
static void setup_steal(struct steal *env, struct terrain_type *ter, struct race *rc) {
env->r = test_create_region(0, 0, ter);
env->f = test_create_faction(rc);
env->u = test_create_unit(env->f, env->r);
@ -72,7 +72,7 @@ static void setup_steal(struct steal *env, terrain_type *ter, race *rc) {
static void test_steal_okay(CuTest * tc) {
struct steal env;
race *rc;
terrain_type *ter;
struct terrain_type *ter;
test_cleanup();
ter = test_create_terrain("plain", LAND_REGION);
@ -114,50 +114,6 @@ static void test_steal_ocean(CuTest * tc) {
test_cleanup();
}
struct give {
struct unit *src, *dst;
struct region *r;
struct faction *f1, *f2;
};
static void setup_give(struct give *env) {
terrain_type *ter = test_create_terrain("plain", LAND_REGION);
env->r = test_create_region(0, 0, ter);
env->src = test_create_unit(env->f1, env->r);
env->dst = test_create_unit(env->f2, env->r);
}
static void test_give_okay(CuTest * tc) {
struct give env;
struct race * rc;
test_cleanup();
rc = test_create_race("human");
env.f2 = env.f1 = test_create_faction(rc);
setup_give(&env);
set_param(&global.parameters, "rules.give", "0");
CuAssertPtrEquals(tc, 0, check_give(env.src, env.dst, 0));
test_cleanup();
}
static void test_give_denied_by_rules(CuTest * tc) {
struct give env;
struct race * rc;
struct message *msg;
test_cleanup();
rc = test_create_race("human");
env.f1 = test_create_faction(rc);
env.f2 = test_create_faction(rc);
setup_give(&env);
set_param(&global.parameters, "rules.give", "0");
CuAssertPtrNotNull(tc, msg=check_give(env.src, env.dst, 0));
msg_release(msg);
test_cleanup();
}
CuSuite *get_economy_suite(void)
{
CuSuite *suite = CuSuiteNew();
@ -166,7 +122,5 @@ CuSuite *get_economy_suite(void)
SUITE_ADD_TEST(suite, test_steal_okay);
SUITE_ADD_TEST(suite, test_steal_ocean);
SUITE_ADD_TEST(suite, test_steal_nosteal);
SUITE_ADD_TEST(suite, test_give_okay);
SUITE_ADD_TEST(suite, test_give_denied_by_rules);
return suite;
}

View File

@ -15,6 +15,7 @@
#include "give.h"
#include "economy.h"
#include "laws.h"
/* kernel includes */
#include <kernel/curse.h>
@ -22,6 +23,7 @@
#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>
@ -38,6 +40,7 @@
#include <util/base36.h>
#include <util/event.h>
#include <util/log.h>
#include <util/parser.h>
/* libc includes */
#include <assert.h>
@ -59,6 +62,36 @@ static int GiveRestriction(void)
return value;
}
static void feedback_give_not_allowed(unit * u, order * ord)
{
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_give_forbidden",
""));
}
static bool can_give(const unit * u, const unit * u2, const item_type * itype, int mask)
{
if (u2) {
if (u->faction != u2->faction) {
int rule = rule_give();
if (itype) {
assert(mask == 0);
if (itype->rtype->ltype)
mask |= GIVE_LUXURIES;
else if (fval(itype, ITF_HERB))
mask |= GIVE_HERBS;
else
mask |= GIVE_GOODS;
}
return (rule & mask) != 0;
}
}
else {
int rule = rule_give();
return (rule & GIVE_PEASANTS) != 0;
}
return true;
}
static void
add_give(unit * u, unit * u2, int given, int received,
const resource_type * rtype, struct order *ord, int error)
@ -456,3 +489,273 @@ void give_unit(unit * u, unit * u2, order * ord)
u_setfaction(u, u2->faction);
u2->faction->newbies += n;
}
void give_cmd(unit * u, order * ord)
{
region *r = u->region;
unit *u2;
const char *s;
int n;
const item_type *itype;
param_t p;
plane *pl;
message *msg;
keyword_t kwd;
kwd = init_order(ord);
assert(kwd == K_GIVE);
u2 = getunit(r, u->faction);
s = getstrtoken();
n = s ? atoip(s) : 0;
p = (n > 0) ? NOPARAM : findparam(s, u->faction->locale);
/* first, do all the ones that do not require HELP_GIVE or CONTACT */
if (p == P_CONTROL) {
/* handled in give_control_cmd */
return;
}
if (!u2 && !getunitpeasants) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
msg = check_give(u, u2, ord);
if (msg) {
ADDMSG(&u->faction->msgs, msg);
return;
}
/* Damit Tarner nicht durch die Fehlermeldung enttarnt werden können */
if (u2 && !alliedunit(u2, u->faction, HELP_GIVE)
&& !cansee(u->faction, r, u2, 0) && !ucontact(u2, u)
&& !fval(u2, UFL_TAKEALL)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
if (u == u2) {
cmistake(u, ord, 8, MSG_COMMERCE);
return;
}
/* UFL_TAKEALL ist ein grober Hack. Generalisierung tut not, ist aber nicht
* wirklich einfach. */
pl = rplane(r);
if (pl && fval(pl, PFL_NOGIVE) && (!u2 || !fval(u2, UFL_TAKEALL))) {
cmistake(u, ord, 268, MSG_COMMERCE);
return;
}
if (u2 && u_race(u2) == get_race(RC_SPELL)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found",
""));
return;
}
else if (u2 && !alliedunit(u2, u->faction, HELP_GIVE) && !ucontact(u2, u)) {
cmistake(u, ord, 40, MSG_COMMERCE);
return;
}
else if (p == P_HERBS) {
bool given = false;
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (!can_give(u, u2, NULL, GIVE_HERBS)) {
feedback_give_not_allowed(u, ord);
return;
}
if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
if (!u2) {
if (!getunitpeasants) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
"feedback_unit_not_found", ""));
return;
}
}
if (u->items) {
item **itmp = &u->items;
while (*itmp) {
item *itm = *itmp;
const item_type *itype = itm->type;
if (fval(itype, ITF_HERB) && itm->number > 0) {
/* give_item ändert im fall,das man alles übergibt, die
* item-liste der unit, darum continue vor pointerumsetzten */
if (give_item(itm->number, itm->type, u, u2, ord) == 0) {
given = true;
if (*itmp != itm)
continue;
continue;
}
}
itmp = &itm->next;
}
}
if (!given)
cmistake(u, ord, 38, MSG_COMMERCE);
return;
}
else if (p == P_ZAUBER) {
cmistake(u, ord, 7, MSG_COMMERCE);
/* geht nimmer */
return;
}
else if (p == P_UNIT) { /* Einheiten uebergeben */
if (!(u_race(u)->ec_flags & GIVEUNIT)) {
cmistake(u, ord, 167, MSG_COMMERCE);
return;
}
give_unit(u, u2, ord);
return;
}
else if (p == P_ANY) {
const char *s;
if (!can_give(u, u2, NULL, GIVE_ALLITEMS)) {
feedback_give_not_allowed(u, ord);
return;
}
s = getstrtoken();
if (!s || *s == 0) { /* GIVE ALL items that you have */
/* do these checks once, not for each item we have: */
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
/* für alle items einmal prüfen, ob wir mehr als von diesem Typ
* reserviert ist besitzen und diesen Teil dann übergeben */
if (u->items) {
item **itmp = &u->items;
while (*itmp) {
item *itm = *itmp;
const item_type *itype = itm->type;
if (itm->number > 0
&& itm->number - get_reservation(u, itype->rtype) > 0) {
n = itm->number - get_reservation(u, itype->rtype);
if (give_item(n, itype, u, u2, ord) == 0) {
if (*itmp != itm)
continue;
}
}
itmp = &itm->next;
}
}
}
else {
if (isparam(s, u->faction->locale, P_PERSON)) {
if (!(u_race(u)->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_noregroup", "race", u_race(u)));
}
else {
n = u->number;
give_men(n, u, u2, ord);
}
}
else if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
}
else if (u2 && !(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
}
else {
itype = finditemtype(s, u->faction->locale);
if (itype != NULL) {
item *i = *i_find(&u->items, itype);
if (i != NULL) {
if (can_give(u, u2, itype, 0)) {
n = i->number - get_reservation(u, itype->rtype);
give_item(n, itype, u, u2, ord);
}
else {
feedback_give_not_allowed(u, ord);
}
}
}
}
}
return;
}
else if (p == P_EACH) {
if (u2 == NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "peasants_give_invalid", ""));
return;
}
s = getstrtoken(); /* skip one ahead to get the amount. */
n = atoip(s); /* n: Anzahl */
n *= u2->number;
}
s = getstrtoken();
if (s == NULL) {
cmistake(u, ord, 113, MSG_COMMERCE);
return;
}
if (isparam(s, u->faction->locale, P_PERSON)) {
if (!(u_race(u)->ec_flags & GIVEPERSON)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_noregroup", "race", u_race(u)));
return;
}
give_men(n, u, u2, ord);
return;
}
if (u2 != NULL) {
if (!(u_race(u)->ec_flags & GIVEITEM) && u2 != NULL) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_nogive", "race", u_race(u)));
return;
}
if (!(u_race(u2)->ec_flags & GETITEM)) {
ADDMSG(&u->faction->msgs,
msg_feedback(u, ord, "race_notake", "race", u_race(u2)));
return;
}
}
itype = finditemtype(s, u->faction->locale);
if (itype != NULL) {
if (can_give(u, u2, itype, 0)) {
give_item(n, itype, u, u2, ord);
}
else {
feedback_give_not_allowed(u, ord);
}
return;
}
cmistake(u, ord, 123, MSG_COMMERCE);
}
message *check_give(const unit *u, const unit *u2, order * ord) {
if (!can_give(u, u2, NULL, GIVE_ALLITEMS)) {
return msg_feedback(u, ord, "feedback_give_forbidden", "");
}
return 0;
}

View File

@ -16,11 +16,17 @@
extern "C" {
#endif
extern int give_item(int want, const struct item_type *itype,
struct item_type;
struct order;
struct unit;
int give_item(int want, const struct item_type *itype,
struct unit *src, struct unit *dest, struct order *ord);
extern void give_men(int n, struct unit *u, struct unit *u2,
void give_men(int n, struct unit *u, struct unit *u2,
struct order *ord);
extern void give_unit(struct unit *u, struct unit *u2, struct order *ord);
void give_unit(struct unit *u, struct unit *u2, struct order *ord);
void give_cmd(struct unit * u, struct order * ord);
struct message * check_give(const struct unit * u, const struct unit * u2, struct order *ord);
#ifdef __cplusplus
}

117
src/give.test.c Normal file
View File

@ -0,0 +1,117 @@
#include <platform.h>
#include "give.h"
#include "economy.h"
#include <kernel/config.h>
#include <kernel/item.h>
#include <kernel/terrain.h>
#include <kernel/order.h>
#include <kernel/unit.h>
#include <kernel/faction.h>
#include <util/base36.h>
#include <util/language.h>
#include <util/message.h>
#include <CuTest.h>
#include <tests.h>
#include <stdio.h>
#include <assert.h>
struct give {
struct unit *src, *dst;
struct region *r;
struct faction *f1, *f2;
struct item_type * itype;
};
static void setup_give(struct give *env) {
struct terrain_type *ter = test_create_terrain("plain", LAND_REGION);
env->r = test_create_region(0, 0, ter);
env->src = test_create_unit(env->f1, env->r);
env->dst = test_create_unit(env->f2, env->r);
env->itype = it_get_or_create(rt_get_or_create("money"));
env->itype->flags |= ITF_HERB;
}
static void test_give(CuTest * tc) {
struct give env;
test_cleanup();
env.f2 = env.f1 = test_create_faction(0);
setup_give(&env);
i_change(&env.src->items, env.itype, 10);
CuAssertIntEquals(tc, 0, give_item(10, env.itype, env.src, env.dst, 0));
CuAssertIntEquals(tc, 0, i_get(env.src->items, env.itype));
CuAssertIntEquals(tc, 10, i_get(env.dst->items, env.itype));
CuAssertIntEquals(tc, -1, give_item(10, env.itype, env.src, env.dst, 0));
CuAssertIntEquals(tc, 0, i_get(env.src->items, env.itype));
CuAssertIntEquals(tc, 10, i_get(env.dst->items, env.itype));
test_cleanup();
}
static void test_give_herbs(CuTest * tc) {
struct give env;
struct order *ord;
struct locale * lang;
char cmd[32];
test_cleanup();
test_create_world();
env.f2 = env.f1 = test_create_faction(0);
setup_give(&env);
i_change(&env.src->items, env.itype, 10);
lang = get_or_create_locale("test");
env.f1->locale = lang;
locale_setstring(lang, "KRAEUTER", "HERBS");
init_locale(lang);
_snprintf(cmd, sizeof(cmd), "%s HERBS", itoa36(env.dst->no));
ord = create_order(K_GIVE, lang, cmd);
assert(ord);
give_cmd(env.src, ord);
CuAssertIntEquals(tc, 0, i_get(env.src->items, env.itype));
CuAssertIntEquals(tc, 10, i_get(env.dst->items, env.itype));
test_cleanup();
}
static void test_give_okay(CuTest * tc) {
struct give env;
test_cleanup();
env.f2 = env.f1 = test_create_faction(0);
setup_give(&env);
set_param(&global.parameters, "rules.give", "0");
CuAssertPtrEquals(tc, 0, check_give(env.src, env.dst, 0));
test_cleanup();
}
static void test_give_denied_by_rules(CuTest * tc) {
struct give env;
struct message *msg;
test_cleanup();
env.f1 = test_create_faction(0);
env.f2 = test_create_faction(0);
setup_give(&env);
set_param(&global.parameters, "rules.give", "0");
CuAssertPtrNotNull(tc, msg = check_give(env.src, env.dst, 0));
msg_release(msg);
test_cleanup();
}
CuSuite *get_give_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_give);
SUITE_ADD_TEST(suite, test_give_herbs);
SUITE_ADD_TEST(suite, test_give_okay);
SUITE_ADD_TEST(suite, test_give_denied_by_rules);
return suite;
}

View File

@ -48,7 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
static group *ghash[GMAXHASH];
static int maxgid;
static group *new_group(faction * f, const char *name, int gid)
group *new_group(faction * f, const char *name, int gid)
{
group **gp = &f->groups;
int index = gid % GMAXHASH;
@ -195,9 +195,10 @@ bool join_group(unit * u, const char *name)
return true;
}
void write_groups(struct storage *store, group * g)
void write_groups(struct storage *store, const faction * f)
{
while (g) {
group *g;
for (g = f->groups; g; g=g->next) {
ally *a;
WRITE_INT(store, g->gid);
WRITE_STR(store, g->name);
@ -210,7 +211,6 @@ void write_groups(struct storage *store, group * g)
WRITE_INT(store, 0);
a_write(store, g->attribs, g);
WRITE_SECTION(store);
g = g->next;
}
WRITE_INT(store, 0);
}
@ -243,7 +243,6 @@ void read_groups(struct storage *store, faction * f)
if (!a->faction)
ur_add(fid, &a->faction, resolve_faction);
}
*pa = 0;
a_read(store, &g->attribs, g);
}
}

View File

@ -40,8 +40,9 @@ 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);
extern void write_groups(struct storage *data, struct group *g);
extern void write_groups(struct storage *data, const struct faction *f);
extern void read_groups(struct storage *data, struct faction *f);
#ifdef __cplusplus

View File

@ -1,14 +1,58 @@
#include <platform.h>
#include "types.h"
#include "ally.h"
#include "group.h"
#include "faction.h"
#include "unit.h"
#include "region.h"
#include <stream.h>
#include <filestream.h>
#include <storage.h>
#include <binarystore.h>
#include <CuTest.h>
#include <tests.h>
#include <assert.h>
static void test_group_readwrite(CuTest * tc)
{
faction * f;
group *g;
ally *al;
storage store;
FILE *F;
stream strm;
F = fopen("test.dat", "wb");
fstream_init(&strm, F);
binstore_init(&store, &strm);
test_cleanup();
test_create_world();
f = test_create_faction(0);
g = new_group(f, "test", 42);
al = ally_add(&g->allies, f);
al->status = HELP_GIVE;
write_groups(&store, f);
binstore_done(&store);
fstream_done(&strm);
F = fopen("test.dat", "rb");
fstream_init(&strm, F);
binstore_init(&store, &strm);
f->groups = 0;
free_group(g);
read_groups(&store, f);
binstore_done(&store);
fstream_done(&strm);
CuAssertPtrNotNull(tc, f->groups);
CuAssertPtrNotNull(tc, f->groups->allies);
CuAssertPtrEquals(tc, 0, f->groups->allies->next);
CuAssertPtrEquals(tc, f, f->groups->allies->faction);
CuAssertIntEquals(tc, HELP_GIVE, f->groups->allies->status);
test_cleanup();
}
static void test_group(CuTest * tc)
{
unit *u;
@ -40,6 +84,7 @@ CuSuite *get_group_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_group);
SUITE_ADD_TEST(suite, test_group_readwrite);
return suite;
}

View File

@ -176,7 +176,7 @@ int count)
}
if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) {
for (v = r->units; v && use < count; v = v->next)
if (u != v && (u->items || rtype->uget)) {
if (u != v && (v->items || rtype->uget)) {
int mask;
if ((urace(v)->ec_flags & GIVEITEM) == 0)

View File

@ -87,6 +87,27 @@ void test_pool(CuTest *tc) {
CuAssertIntEquals(tc, 300, get_pooled(u1, rtype, GET_ALL, INT_MAX));
}
void test_pool_bug_2042(CuTest *tc) {
unit *u1, *u2;
faction *f;
region *r;
struct resource_type *rtype;
test_cleanup();
test_create_world();
rtype = rt_get_or_create("money");
it_get_or_create(rtype);
f = test_create_faction(0);
r = findregion(0, 0);
assert(r && f && rtype && rtype->itype);
u1 = test_create_unit(f, r);
u2 = test_create_unit(f, r);
assert(u1 && u2);
i_change(&u2->items, rtype->itype, 100);
CuAssertIntEquals(tc, 100, get_pooled(u1, rtype, GET_SLACK | GET_POOLED_SLACK, 100));
}
void test_pool_use(CuTest *tc) {
unit *u1, *u2, *u3;
faction *f;
@ -165,6 +186,7 @@ CuSuite *get_pool_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_reservation);
SUITE_ADD_TEST(suite, test_pool);
SUITE_ADD_TEST(suite, test_pool_bug_2042);
SUITE_ADD_TEST(suite, test_pool_use);
SUITE_ADD_TEST(suite, test_change_resource);
return suite;

View File

@ -70,6 +70,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/umlaut.h>
#include <util/unicode.h>
#include <stream.h>
#include <filestream.h>
#include <storage.h>
#include <binarystore.h>
@ -1439,7 +1441,7 @@ void writefaction(struct gamedata *data, const faction * f)
}
WRITE_INT(data->store, 0);
WRITE_SECTION(data->store);
write_groups(data->store, f->groups);
write_groups(data->store, f);
write_spellbook(f->spellbook, data->store);
}
@ -1457,6 +1459,7 @@ int readgame(const char *filename, int backup)
const struct building_type *bt_lighthouse = bt_find("lighthouse");
gamedata gdata = { 0 };
storage store;
stream strm;
FILE *F;
init_locales();
@ -1482,7 +1485,8 @@ int readgame(const char *filename, int backup)
assert(gdata.version <= RELEASE_VERSION || !"unsupported data format");
gdata.encoding = enc_gamedata;
binstore_init(&store, F);
fstream_init(&strm, F);
binstore_init(&store, &strm);
gdata.store = &store;
global.data_version = gdata.version; /* HACK: attribute::read does not have access to gamedata, only storage */
@ -1733,7 +1737,7 @@ int readgame(const char *filename, int backup)
read_borders(&store);
binstore_done(&store);
fstream_done(&strm);
/* Unaufgeloeste Zeiger initialisieren */
log_printf(stdout, "fixing unresolved references.\n");
resolve();
@ -1797,6 +1801,7 @@ int writegame(const char *filename)
char path[MAX_PATH];
gamedata gdata;
storage store;
stream strm;
FILE *F;
clear_monster_orders();
@ -1826,7 +1831,8 @@ int writegame(const char *filename)
fwrite(&gdata.version, sizeof(int), 1, F);
fwrite(&n, sizeof(int), 1, F);
binstore_init(&store, F);
fstream_init(&strm, F);
binstore_init(&store, &strm);
/* globale Variablen */
@ -1942,6 +1948,7 @@ int writegame(const char *filename)
WRITE_SECTION(&store);
binstore_done(&store);
fstream_done(&strm);
log_printf(stdout, "\nOk.\n");
return 0;

View File

@ -18,6 +18,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef H_KRNL_SAVE
#define H_KRNL_SAVE
#include <stream.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -31,6 +33,7 @@ extern "C" {
typedef struct gamedata {
struct storage *store;
stream strm;
int version;
int encoding;
} gamedata;

View File

@ -18,6 +18,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef H_GC_LAWS
#define H_GC_LAWS
#include <kernel/types.h>
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -57,6 +57,7 @@ int RunAllTests(void)
/* gamecode */
ADD_TESTS(suite, battle);
ADD_TESTS(suite, economy);
ADD_TESTS(suite, give);
ADD_TESTS(suite, laws);
ADD_TESTS(suite, market);
ADD_TESTS(suite, move);

@ -1 +1 @@
Subproject commit c6103e59c0938b173c0e08a852ff1cbbc4e284e3
Subproject commit 5f0b7095d42209762c9eac73c866c614018b440d