Merge pull request #434 from ennorehling/develop

Fixed PR for negative peasant growth, rand injection
This commit is contained in:
Enno Rehling 2015-12-11 18:09:02 +01:00
commit ad28b9ae94
21 changed files with 192 additions and 55 deletions

View File

@ -77,7 +77,7 @@ void herbsearch(unit * u, int max)
herbsfound = ntimespprob(effsk * u->number,
(double)rherbs(r) / 100.0F, -0.01F);
herbsfound = _min(herbsfound, max);
rsetherbs(r, rherbs(r) - herbsfound);
rsetherbs(r, (short) (rherbs(r) - herbsfound));
if (herbsfound) {
produceexp(u, SK_HERBALISM, u->number);

View File

@ -1380,7 +1380,7 @@ static void cr_output_region(FILE * F, report_context * ctx, seen_region * sr)
}
}
if (r->land && r->land->ownership) {
fprintf(F, "%d;morale\n", r->land->morale);
fprintf(F, "%d;morale\n", region_get_morale(r));
}
}

View File

@ -2298,7 +2298,7 @@ static void plant(unit * u, int raw)
/* Alles ok. Abziehen. */
use_pooled(u, rt_water, GET_DEFAULT, 1);
use_pooled(u, itype->rtype, GET_DEFAULT, n);
rsetherbs(r, rherbs(r) + planted);
rsetherbs(r, (short) (rherbs(r) + planted));
ADDMSG(&u->faction->msgs, msg_message("plant", "unit region amount herb",
u, r, planted, itype->rtype));
}

View File

@ -224,7 +224,7 @@ static void test_tax_cmd(CuTest *tc) {
r->land->money = 11;
rsetmoney(r, 11);
expandtax(r, taxorders);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "income"));
/* taxing is in multiples of 10 */
@ -232,7 +232,7 @@ static void test_tax_cmd(CuTest *tc) {
test_clear_messages(u->faction);
i_change(&u->items, silver, -i_get(u->items, silver));
r->land->money = 1000;
rsetmoney(r, 1000);
taxorders = 0;
tax_cmd(u, ord, &taxorders);
expandtax(r, taxorders);

View File

@ -64,7 +64,7 @@ static void test_give_unit_to_peasants(CuTest * tc) {
rsetpeasants(env.r, 0);
give_unit(env.src, NULL, NULL);
CuAssertIntEquals(tc, 0, env.src->number);
CuAssertIntEquals(tc, 1, env.r->land->peasants);
CuAssertIntEquals(tc, 1, rpeasants(env.r));
test_cleanup();
}
@ -251,7 +251,7 @@ static void test_give_peasants(CuTest * tc) {
msg = disband_men(1, env.src, NULL);
CuAssertStrEquals(tc, "give_person_peasants", (const char*)msg->parameters[0].v);
CuAssertIntEquals(tc, 0, env.src->number);
CuAssertIntEquals(tc, 1, env.r->land->peasants);
CuAssertIntEquals(tc, 1, rpeasants(env.r));
msg_release(msg);
test_cleanup();
}

View File

@ -385,9 +385,8 @@ static void paint_info_region(window * wnd, const state * st)
line++;
mvwprintw(win, line++, 1, "%s, age %d", r->terrain->_name, r->age);
if (r->land) {
mvwprintw(win, line++, 1, "$:%6d P:%5d", r->land->money,
r->land->peasants);
mvwprintw(win, line++, 1, "H:%6d %s:%5d", r->land->horses,
mvwprintw(win, line++, 1, "$:%6d P:%5d", rmoney(r), rpeasants(r));
mvwprintw(win, line++, 1, "H:%6d %s:%5d", rhorses(r),
(r->flags & RF_MALLORN) ? "M" : "T",
r->land->trees[1] + r->land->trees[2]);
}

View File

@ -98,8 +98,8 @@ static int res_changehp(unit * u, const resource_type * rtype, int delta)
static int res_changepeasants(unit * u, const resource_type * rtype, int delta)
{
assert(rtype != NULL && u->region->land);
u->region->land->peasants += delta;
return u->region->land->peasants;
rsetpeasants(u->region, rpeasants(u->region) + delta);
return rpeasants(u->region);
}
static int golem_factor(const unit *u, const resource_type *rtype) {

View File

@ -595,26 +595,30 @@ bool is_coastregion(region * r)
int rpeasants(const region * r)
{
return ((r)->land ? (r)->land->peasants : 0);
return r->land ? r->land->peasants : 0;
}
void rsetpeasants(region * r, int value)
{
if (r->land) r->land->peasants = value;
else assert(value>=0);
if (r->land) {
assert(value >= 0);
r->land->peasants = value;
} else
assert(value == 0);
}
int rmoney(const region * r)
{
return ((r)->land ? (r)->land->money : 0);
return r->land ? r->land->money : 0;
}
void rsethorses(const region * r, int value)
{
assert(value >= 0);
if (r->land)
if (r->land) {
assert(value >= 0);
r->land->horses = value;
} else
assert(value == 0);
}
int rhorses(const region * r)
@ -624,10 +628,28 @@ int rhorses(const region * r)
void rsetmoney(region * r, int value)
{
if (r->land) r->land->money = value;
else assert(value >= 0);
if (r->land) {
assert(value >= 0);
r->land->money = value;
} else
assert(value == 0);
}
int rherbs(const struct region *r)
{
return r->land?r->land->herbs:0;
}
void rsetherbs(const struct region *r, int value)
{
if (r->land) {
assert(value >= 0);
r->land->herbs = (short)(value);
} else
assert(value == 0);
}
void r_setdemand(region * r, const luxury_type * ltype, int value)
{
struct demand *d, **dp = &r->land->demands;
@ -1325,7 +1347,7 @@ faction *update_owners(region * r)
else if (f || new_owner->faction != region_get_last_owner(r)) {
alliance *al = region_get_alliance(r);
if (al && new_owner->faction->alliance == al) {
int morale = _max(0, r->land->morale - MORALE_TRANSFER);
int morale = _max(0, region_get_morale(r) - MORALE_TRANSFER);
region_set_morale(r, morale, turn);
}
else {

View File

@ -201,13 +201,14 @@ extern "C" {
int rhorses(const struct region *r);
void rsethorses(const struct region *r, int value);
int rherbs(const struct region *r);
void rsetherbs(const struct region *r, int value);
#define rbuildings(r) ((r)->buildings)
#define rherbtype(r) ((r)->land?(r)->land->herbtype:0)
#define rsetherbtype(r, value) if ((r)->land) (r)->land->herbtype=(value)
#define rherbs(r) ((r)->land?(r)->land->herbs:0)
#define rsetherbs(r, value) if ((r)->land) ((r)->land->herbs=(short)(value))
bool r_isforest(const struct region *r);

View File

@ -973,10 +973,7 @@ static region *readregion(struct gamedata *data, int x, int y)
read_items(data->store, &r->land->items);
if (data->version >= REGIONOWNER_VERSION) {
READ_INT(data->store, &n);
r->land->morale = (short)n;
if (r->land->morale < 0) {
r->land->morale = 0;
}
region_set_morale(r, _max(0, (short) n), -1);
read_owner(data, &r->land->ownership);
}
}
@ -1039,7 +1036,7 @@ void writeregion(struct gamedata *data, const region * r)
write_items(data->store, r->land->items);
WRITE_SECTION(data->store);
#if RELEASE_VERSION>=REGIONOWNER_VERSION
WRITE_INT(data->store, r->land->morale);
WRITE_INT(data->store, region_get_morale(r));
write_owner(data, r->land->ownership);
WRITE_SECTION(data->store);
#endif

View File

@ -707,23 +707,21 @@ void immigration(void)
int rp = rpeasants(r) + r->land->newpeasants;
rsetpeasants(r, _max(0, rp));
}
/* Genereate some (0-2 to 0-6 depending on the income) peasants out of nothing */
/*if less then 50 are in the region and there is space and no monster or deamon units in the region */
/* Genereate some (0-6 depending on the income) peasants out of nothing */
/* if less than 50 are in the region and there is space and no monster or demon units in the region */
int peasants = rpeasants(r);
if (repopulate && r->land && (peasants < repopulate) && maxworkingpeasants(r) >(peasants + 30) * 2)
{
int income = wage(r, NULL, NULL, turn) - maintenance_cost(NULL);
if (repopulate && r->land && (peasants < repopulate) && maxworkingpeasants(r) > (peasants + 30) * 2 && income >= 0) {
int badunit = 0;
unit *u;
for (u = r->units; u; u = u->next) {
if (!playerrace(u_race(u)) || u_race(u) == get_race(RC_DAEMON))
{
if (!playerrace(u_race(u)) || u_race(u) == get_race(RC_DAEMON)) {
badunit = 1;
break;
}
}
if (badunit == 0)
{
peasants += (int)(rng_double()*(wage(r, NULL, NULL, turn) - 9));
if (badunit == 0) {
peasants += (int)(rng_double() * (income + 1));
rsetpeasants(r, peasants);
}
}

View File

@ -1258,6 +1258,43 @@ static void test_show_without_item(CuTest *tc)
test_cleanup();
}
static int low_wage(const region * r, const faction * f, const race * rc, int in_turn) {
return 1;
}
static void test_immigration(CuTest * tc)
{
region *r;
double inject[] = { 1 };
int (*old_wage)(const region*, const faction*, const race*, int) = global.functions.wage;
test_cleanup();
test_create_world();
r = findregion(0, 0);
rsetpeasants(r, 0);
config_set("rules.economy.repopulate_maximum", 0);
random_source_inject_constant(0);
immigration();
CuAssertIntEquals(tc, 0, rpeasants(r));
random_source_inject_constant(1);
immigration();
CuAssertIntEquals(tc, 2, rpeasants(r));
random_source_inject_array(inject, 2);
global.functions.wage = low_wage;
immigration();
CuAssertIntEquals(tc, 2, rpeasants(r));
global.functions.wage = old_wage;
test_cleanup();
}
CuSuite *get_laws_suite(void)
{
CuSuite *suite = CuSuiteNew();
@ -1316,6 +1353,7 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_name_building);
SUITE_ADD_TEST(suite, test_name_ship);
SUITE_ADD_TEST(suite, test_show_without_item);
SUITE_ADD_TEST(suite, test_immigration);
return suite;
}

View File

@ -842,7 +842,7 @@ int region_quality(const region * r, region * rn[])
/* nobody likes volcanoes */
result -= 2000;
}
result += rn[n]->land->peasants;
result += rpeasants(rn[n]);
}
}
return result;
@ -1007,10 +1007,10 @@ int build_island_e3(newfaction ** players, int x, int y, int numfactions, int mi
terraform_region(r, newterrain(T_HIGHLAND));
prepare_starting_region(r);
}
r->land->money = 50000; /* 2% = 1000 silver */
rsetmoney(r, 50000); /* 2% = 1000 silver */
}
else if (r->land) {
r->land->money *= 4;
rsetmoney(r, rmoney(r) *4);
}
}
return nfactions;

View File

@ -657,8 +657,8 @@ static order *plan_dragon(unit * u)
order *long_order = NULL;
if (ta == NULL) {
move |= (r->land == 0 || r->land->peasants == 0); /* when no peasants, move */
move |= (r->land == 0 || r->land->money == 0); /* when no money, move */
move |= (rpeasants(r) == 0); /* when no peasants, move */
move |= (rmoney(r) == 0); /* when no money, move */
}
move |= chance(0.04); /* 4% chance to change your mind */
@ -950,7 +950,7 @@ void spawn_undead(void)
continue;
/* Chance 0.1% * chaosfactor */
if (r->land && unburied > r->land->peasants / 20
if (r->land && unburied > rpeasants(r) / 20
&& rng_int() % 10000 < (100 + 100 * chaosfactor(r))) {
message *msg;
unit *u;

View File

@ -29,6 +29,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/rand.h>
#include <assert.h>
static double rc_popularity(const struct race *rc)
{
int pop = get_param_int(rc->parameters, "morale", MORALE_AVERAGE);
@ -36,6 +38,9 @@ static double rc_popularity(const struct race *rc)
}
void morale_update(region *r) {
int morale = region_get_morale(r);
assert(r->land);
if (r->land->ownership && r->land->ownership->owner) {
int stability = turn - r->land->ownership->morale_turn;
int maxmorale = MORALE_DEFAULT;
@ -44,24 +49,24 @@ void morale_update(region *r) {
int bsize = buildingeffsize(b, false);
maxmorale = (int)(0.5 + b->type->taxes(b, bsize + 1) / MORALE_TAX_FACTOR);
}
if (r->land->morale < maxmorale) {
if (morale < maxmorale) {
if (stability > MORALE_COOLDOWN && r->land->ownership->owner
&& r->land->morale < MORALE_MAX) {
&& morale < MORALE_MAX) {
double ch = rc_popularity(r->land->ownership->owner->race);
if (is_cursed(r->attribs, C_GENEROUS, 0)) {
ch *= 1.2; /* 20% improvement */
}
if (stability >= MORALE_AVERAGE * 2 || chance(ch)) {
region_set_morale(r, r->land->morale + 1, turn);
region_set_morale(r, morale + 1, turn);
}
}
}
else if (r->land->morale > maxmorale) {
region_set_morale(r, r->land->morale - 1, turn);
else if (morale > maxmorale) {
region_set_morale(r, morale - 1, turn);
}
}
else if (r->land->morale > MORALE_DEFAULT) {
region_set_morale(r, r->land->morale - 1, turn);
else if (morale > MORALE_DEFAULT) {
region_set_morale(r, morale - 1, turn);
}
}

View File

@ -604,6 +604,7 @@ volcano_destruction(region * volcano, region * r, const char *damage)
unit *u = *up;
if (u->number) {
int dead = damage_unit(u, damage, true, false);
/* TODO create undead */
if (dead) {
ADDMSG(&u->faction->msgs, msg_message("volcano_dead",
"unit region dead", u, volcano, dead));

View File

@ -1019,7 +1019,7 @@ static void describe(stream *out, const seen_region * sr, faction * f)
if (r->land->ownership) {
const char *str =
LOC(f->locale, mkname("morale", itoa10(r->land->morale)));
LOC(f->locale, mkname("morale", itoa10(region_get_morale(r))));
bytes = _snprintf(bufp, size, " %s", str);
if (wrptr(&bufp, &size, bytes) != 0)
WARN_STATIC_BUFFER();
@ -1354,7 +1354,7 @@ static void statistics(stream *out, const region * r, const faction * f)
}
if (r->land->ownership) {
m = msg_message("nr_stat_morale", "morale", r->land->morale);
m = msg_message("nr_stat_morale", "morale", region_get_morale(r));
nr_render(m, f->locale, buf, sizeof(buf), f);
paragraph(out, buf, 2, 2, 0);
msg_release(m);

View File

@ -23,6 +23,7 @@
#include <util/language.h>
#include <util/message.h>
#include <util/log.h>
#include <util/rand.h>
#include <CuTest.h>
@ -103,6 +104,8 @@ void test_cleanup(void)
errno = 0;
log_error("errno: %d", error);
}
random_source_reset();
}
terrain_type *

View File

@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "rng.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
@ -65,3 +66,66 @@ bool chance(double x)
return true;
return rng_double() < x;
}
extern double genrand_real2(void);
typedef struct random_source {
double (*double_source) (void);
} random_source;
random_source *r_source = 0;
double rng_injectable_double(void) {
if (r_source)
return r_source->double_source();
return genrand_real2();
}
static double constant_value;
static double constant_source (void) {
return constant_value;
}
struct random_source constant_provider = {
constant_source
};
void random_source_inject_constant(double value) {
constant_value = value;
r_source = &constant_provider;
}
static int i = 0;
static double *values;
static int value_size = 0;
static double array_source (void) {
assert(i<value_size);
return values[i++];
}
struct random_source array_provider = {
array_source
};
void random_source_inject_array(double inject[], int size) {
assert(size > 0);
value_size = size;
if (values)
free(values);
values = malloc(sizeof(double) * size);
for (i=0; i < size; ++i) {
values[i] = inject[i];
}
i = 0;
r_source = &array_provider;
}
void random_source_reset(void) {
if (values)
free(values);
values = NULL;
r_source = NULL;
}

View File

@ -31,6 +31,15 @@ extern "C" {
extern int ntimespprob(int n, double p, double mod);
extern bool chance(double x);
/* a random source that generates numbers in [0, 1).
By calling the random_source_inject... functions you can set a special random source,
which is handy for testing */
double rng_injectable_double(void);
void random_source_inject_constant(double value);
void random_source_inject_array(double inject[], int size);
void random_source_reset(void);
#ifdef __cplusplus
}
#endif

View File

@ -23,7 +23,7 @@ extern "C" {
extern unsigned long genrand_int32(void);
/* generates a random number on [0,1)-real-interval */
extern double genrand_real2(void);
extern double rng_injectable_double(void);
/* generates a random number on [0,0x7fffffff]-interval */
long genrand_int31(void);
@ -31,7 +31,7 @@ extern "C" {
# define rng_init(seed) init_genrand(seed)
# define rng_int (int)genrand_int31
# define rng_uint (unsigned int)genrand_int32
# define rng_double genrand_real2
# define rng_double rng_injectable_double
# define RNG_RAND_MAX 0x7fffffff
#else
# include <stdlib.h>