Bug 2633: implement combined trade limits.

This commit is contained in:
Enno Rehling 2020-01-14 20:57:29 +01:00
parent e02c1e9d00
commit 6626cfe297
2 changed files with 120 additions and 93 deletions

View File

@ -1052,6 +1052,36 @@ const attrib_type at_luxuries = {
"luxuries", NULL, free_luxuries, NULL, NULL, NULL "luxuries", NULL, free_luxuries, NULL, NULL, NULL
}; };
static const attrib_type at_trades = {
"trades",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ
};
static int trade_add(unit *u, int n) {
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */
int k = u->number * 10 * effskill(u, SK_TRADE, NULL);
attrib *a = a_find(u->attribs, &at_trades);
/* hat der Haendler bereits gehandelt, muss die Menge der bereits
* verkauften/gekauften Gueter abgezogen werden */
if (!a) {
a = a_add(&u->attribs, a_new(&at_trades));
}
else {
k -= a->data.i;
}
if (n > k) n = k;
/* die Menge der verkauften Gueter merken */
a->data.i += n;
return n;
}
static void expandbuying(region * r, econ_request * buyorders) static void expandbuying(region * r, econ_request * buyorders)
{ {
const resource_type *rsilver = get_resourcetype(R_SILVER); const resource_type *rsilver = get_resourcetype(R_SILVER);
@ -1120,15 +1150,16 @@ static void expandbuying(region * r, econ_request * buyorders)
a->data.v = items; a->data.v = items;
i_change(&g_requests[j]->unit->items, ltype->itype, 1); i_change(&g_requests[j]->unit->items, ltype->itype, 1);
use_pooled(u, rsilver, GET_DEFAULT, price); use_pooled(u, rsilver, GET_DEFAULT, price);
if (u->n < 0) if (u->n < 0) {
u->n = 0; u->n = 0;
}
u->n += price; u->n += price;
rsetmoney(r, rmoney(r) + price); rsetmoney(r, rmoney(r) + price);
/* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt /* Falls mehr als max_products Bauern ein Produkt verkauft haben, steigt
* der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler * der Preis Multiplikator fuer das Produkt um den Faktor 1. Der Zaehler
* wird wieder auf 0 gesetzt. */ * wird wieder auf 0 gesetzt. */
if (++trade->number == max_products) { if (++trade->number == max_products) {
trade->number = 0; trade->number = 0;
++trade->multi; ++trade->multi;
@ -1158,15 +1189,6 @@ static void expandbuying(region * r, econ_request * buyorders)
} }
} }
attrib_type at_trades = {
"trades",
DEFAULT_INIT,
DEFAULT_FINALIZE,
DEFAULT_AGE,
NO_WRITE,
NO_READ
};
bool trade_needs_castle(const terrain_type *terrain, const race *rc) bool trade_needs_castle(const terrain_type *terrain, const race *rc)
{ {
static int rc_change, terrain_change; static int rc_change, terrain_change;
@ -1198,9 +1220,8 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
{ {
char token[128]; char token[128];
region *r = u->region; region *r = u->region;
int n, k; int n;
econ_request *o; econ_request *o;
attrib *a;
const item_type *itype = NULL; const item_type *itype = NULL;
const luxury_type *ltype = NULL; const luxury_type *ltype = NULL;
keyword_t kwd; keyword_t kwd;
@ -1239,30 +1260,12 @@ static void buy(unit * u, econ_request ** buyorders, struct order *ord)
} }
} }
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ n = trade_add(u, n);
k = u->number * 10 * effskill(u, SK_TRADE, NULL); if (n <= 0) {
/* hat der Haendler bereits gehandelt, muss die Menge der bereits
* verkauften/gekauften Gueter abgezogen werden */
a = a_find(u->attribs, &at_trades);
if (!a) {
a = a_add(&u->attribs, a_new(&at_trades));
}
else {
k -= a->data.i;
}
if (n > k) n = k;
if (!n) {
cmistake(u, ord, 102, MSG_COMMERCE); cmistake(u, ord, 102, MSG_COMMERCE);
return; return;
} }
assert(n >= 0);
/* die Menge der verkauften Gueter merken */
a->data.i += n;
s = gettoken(token, sizeof(token)); s = gettoken(token, sizeof(token));
itype = s ? finditemtype(s, u->faction->locale) : 0; itype = s ? finditemtype(s, u->faction->locale) : 0;
if (itype != NULL) { if (itype != NULL) {
@ -1309,8 +1312,7 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
unit *maxowner = (unit *)NULL; unit *maxowner = (unit *)NULL;
building *maxb = (building *)NULL; building *maxb = (building *)NULL;
building *b; building *b;
unit *u; unit *hafenowner, *u;
unit *hafenowner;
static int counter[MAXLUXURIES]; static int counter[MAXLUXURIES];
static int ncounter = 0; static int ncounter = 0;
static int bt_cache; static int bt_cache;
@ -1386,6 +1388,8 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
int multi = r_demand(r, ltype); int multi = r_demand(r, ltype);
int i, price; int i, price;
int use = 0; int use = 0;
u = g_requests[j]->unit;
for (i = 0, search = luxurytypes; search != ltype; search = search->next) { for (i = 0, search = luxurytypes; search != ltype; search = search->next) {
/* TODO: this is slow and lame! */ /* TODO: this is slow and lame! */
++i; ++i;
@ -1397,58 +1401,62 @@ static void expandselling(region * r, econ_request * sellorders, int limit)
price = ltype->price * multi; price = ltype->price * multi;
if (money >= price) { if (money >= price) {
item *itm; if (trade_add(u, 1) != 1) {
attrib *a; break;
u = g_requests[j]->unit;
a = a_find(u->attribs, &at_luxuries);
if (!a) {
a = a_add(&u->attribs, a_new(&at_luxuries));
} }
itm = (item *)a->data.v; else {
i_change(&itm, ltype->itype, 1); item *itm;
a->data.v = itm; attrib *a;
++use; a = a_find(u->attribs, &at_luxuries);
if (u->n < 0) { if (!a) {
u->n = 0; a = a_add(&u->attribs, a_new(&at_luxuries));
}
if (hafenowner) {
if (hafenowner->faction != u->faction) {
int abgezogenhafen = price / 10;
hafencollected += abgezogenhafen;
price -= abgezogenhafen;
money -= abgezogenhafen;
} }
} itm = (item *)a->data.v;
if (maxb) { i_change(&itm, ltype->itype, 1);
if (maxowner->faction != u->faction) { a->data.v = itm;
int abgezogensteuer = price * tax_per_size[maxeffsize] / 100; ++use;
taxcollected += abgezogensteuer; if (u->n < 0) {
price -= abgezogensteuer; u->n = 0;
money -= abgezogensteuer;
} }
}
u->n += price;
change_money(u, price);
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
/* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die if (hafenowner) {
* Produkte koennen auch als Steuern eingetrieben werden. In der Region if (hafenowner->faction != u->faction) {
* wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht! int abgezogenhafen = price / 10;
* Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt hafencollected += abgezogenhafen;
* die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0 price -= abgezogenhafen;
* gesetzt. */ money -= abgezogenhafen;
}
if (++counter[i] > max_products) { }
int d = r_demand(r, ltype); if (maxb) {
if (d > 1) { if (maxowner->faction != u->faction) {
r_setdemand(r, ltype, d - 1); int abgezogensteuer = price * tax_per_size[maxeffsize] / 100;
taxcollected += abgezogensteuer;
price -= abgezogensteuer;
money -= abgezogensteuer;
}
}
u->n += price;
change_money(u, price);
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
/* r->money -= price; --- dies wird eben nicht ausgefuehrt, denn die
* Produkte koennen auch als Steuern eingetrieben werden. In der Region
* wurden Silberstuecke gegen Luxusgueter des selben Wertes eingetauscht!
* Falls mehr als max_products Kunden ein Produkt gekauft haben, sinkt
* die Nachfrage fuer das Produkt um 1. Der Zaehler wird wieder auf 0
* gesetzt. */
if (++counter[i] > max_products) {
int d = r_demand(r, ltype);
if (d > 1) {
r_setdemand(r, ltype, d - 1);
}
counter[i] = 0;
} }
counter[i] = 0;
} }
} }
if (use > 0) { if (use > 0) {
use_pooled(g_requests[j]->unit, ltype->itype->rtype, GET_DEFAULT, use); use_pooled(u, ltype->itype->rtype, GET_DEFAULT, use);
} }
} }
} }
@ -1561,7 +1569,6 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
return false; return false;
} }
else { else {
attrib *a;
econ_request *o; econ_request *o;
int k, available; int k, available;
@ -1597,20 +1604,9 @@ static bool sell(unit * u, econ_request ** sellorders, struct order *ord)
/* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */ /* Ein Haendler kann nur 10 Gueter pro Talentpunkt handeln. */
k = u->number * 10 * effskill(u, SK_TRADE, NULL); k = u->number * 10 * effskill(u, SK_TRADE, NULL);
/* hat der Haendler bereits gehandelt, muss die Menge der bereits
* verkauften/gekauften Gueter abgezogen werden */
a = a_find(u->attribs, &at_trades);
if (!a) {
a = a_add(&u->attribs, a_new(&at_trades));
}
else {
k -= a->data.i;
}
if (n > k) n = k; if (n > k) n = k;
assert(n >= 0); assert(n >= 0);
/* die Menge der verkauften Gueter merken */ /* die Menge der verkauften Gueter merken */
a->data.i += n;
o = (econ_request *)calloc(1, sizeof(econ_request)); o = (econ_request *)calloc(1, sizeof(econ_request));
if (!o) abort(); if (!o) abort();
o->unit = u; o->unit = u;

View File

@ -209,7 +209,7 @@ static void setup_terrains(CuTest *tc) {
static region *setup_trade_region(CuTest *tc, const struct terrain_type *terrain) { static region *setup_trade_region(CuTest *tc, const struct terrain_type *terrain) {
region *r; region *r;
item_type *it_luxury; item_type *it_luxury;
struct locale * lang = default_locale; struct locale * lang = test_create_locale();
new_luxurytype(it_luxury = test_create_itemtype("balm"), 5); new_luxurytype(it_luxury = test_create_itemtype("balm"), 5);
locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name); locale_setstring(lang, it_luxury->rtype->_name, it_luxury->rtype->_name);
@ -233,6 +233,36 @@ static unit *setup_trade_unit(CuTest *tc, region *r, const struct race *rc) {
return u; return u;
} }
static void test_trade_limits(CuTest *tc) {
region *r;
unit *u;
building *b;
const item_type *it_jewel, *it_balm;
test_setup();
setup_production();
setup_terrains(tc);
init_terrains();
r = setup_trade_region(tc, NULL);
b = test_create_building(r, test_create_buildingtype("castle"));
b->size = 2;
rsetpeasants(r, TRADE_FRACTION * 20);
it_jewel = it_find("jewel");
u = test_create_unit(test_create_faction(NULL), r);
set_level(u, SK_TRADE, 1);
i_change(&u->items, it_find("money"), 500);
unit_addorder(u, create_order(K_BUY, u->faction->locale, "5 %s",
LOC(u->faction->locale, resourcename(it_jewel->rtype, 0))));
it_balm = it_find("balm");
i_change(&u->items, it_balm, 10);
unit_addorder(u, create_order(K_SELL, u->faction->locale, "10 %s",
LOC(u->faction->locale, resourcename(it_balm->rtype, 0))));
produce(r);
CuAssertIntEquals(tc, 5, i_get(u->items, it_jewel));
CuAssertIntEquals(tc, 5, i_get(u->items, it_balm));
test_teardown();
}
static void test_trade_needs_castle(CuTest *tc) { static void test_trade_needs_castle(CuTest *tc) {
/* Handeln ist nur in Regionen mit Burgen möglich. */ /* Handeln ist nur in Regionen mit Burgen möglich. */
race *rc; race *rc;
@ -837,6 +867,7 @@ CuSuite *get_economy_suite(void)
SUITE_ADD_TEST(suite, test_heroes_dont_recruit); SUITE_ADD_TEST(suite, test_heroes_dont_recruit);
SUITE_ADD_TEST(suite, test_tax_cmd); SUITE_ADD_TEST(suite, test_tax_cmd);
SUITE_ADD_TEST(suite, test_buy_cmd); SUITE_ADD_TEST(suite, test_buy_cmd);
SUITE_ADD_TEST(suite, test_trade_limits);
SUITE_ADD_TEST(suite, test_trade_needs_castle); SUITE_ADD_TEST(suite, test_trade_needs_castle);
SUITE_ADD_TEST(suite, test_trade_insect); SUITE_ADD_TEST(suite, test_trade_insect);
SUITE_ADD_TEST(suite, test_maintain_buildings); SUITE_ADD_TEST(suite, test_maintain_buildings);