diff --git a/.gitignore b/.gitignore index 8e19d4018..24219490f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode/ *.orig eressea.ini Debug diff --git a/res/races/dragon.xml b/res/races/dragon.xml index 907ff90a7..e4a1a7fec 100644 --- a/res/races/dragon.xml +++ b/res/races/dragon.xml @@ -1,7 +1,8 @@ +income="1000" walk="yes" teach="no" getitem="yes" resistbash="yes" +dragon="yes"> diff --git a/res/races/wyrm.xml b/res/races/wyrm.xml index b5d3bd7a2..b9f95d9ef 100644 --- a/res/races/wyrm.xml +++ b/res/races/wyrm.xml @@ -1,4 +1,8 @@ - + diff --git a/res/races/youngdragon.xml b/res/races/youngdragon.xml index d1b3a4752..71bc4214b 100644 --- a/res/races/youngdragon.xml +++ b/res/races/youngdragon.xml @@ -1,4 +1,8 @@ - + diff --git a/s/travis-build b/s/travis-build index 011e9e7de..9293ac12d 100755 --- a/s/travis-build +++ b/s/travis-build @@ -10,7 +10,7 @@ $BUILD/iniparser/inifile eressea.ini add lua:paths lunit:scripts fi } -integraton_tests() { +integration_tests() { cd tests ./write-reports.sh ./run-turn.sh @@ -24,4 +24,4 @@ cd $ROOT inifile s/runtests -integraton_tests +integration_tests diff --git a/src/battle.c b/src/battle.c index 1bbaaadfe..fb411d778 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1886,7 +1886,13 @@ int skilldiff(troop at, troop dt, int dist) unit *au = af->unit, *du = df->unit; int is_protected = 0, skdiff = 0; weapon *awp = select_weapon(at, true, dist > 1); + static int rc_cache; + static const race *rc_halfling, *rc_goblin; + if (rc_changed(&rc_cache)) { + rc_halfling = get_race(RC_HALFLING); + rc_goblin = get_race(RC_GOBLIN); + } skdiff += af->person[at.index].attack; skdiff -= df->person[dt.index].defence; @@ -1894,11 +1900,10 @@ int skilldiff(troop at, troop dt, int dist) skdiff += 2; /* Effekte durch Rassen */ - if (awp != NULL && u_race(au) == get_race(RC_HALFLING) && dragonrace(u_race(du))) { + if (awp != NULL && u_race(au) == rc_halfling && dragonrace(u_race(du))) { skdiff += 5; } - - if (u_race(au) == get_race(RC_GOBLIN)) { + else if (u_race(au) == rc_goblin) { if (af->side->size[SUM_ROW] >= df->side->size[SUM_ROW] * rule_goblin_bonus) { skdiff += 1; } diff --git a/src/economy.c b/src/economy.c index 099e9786d..8f6a1afa1 100644 --- a/src/economy.c +++ b/src/economy.c @@ -114,20 +114,8 @@ static void recruit_init(void) int income(const unit * u) { - // TODO: make this a property, like race.income, no hard-coding of values - if (fval(u_race(u), RCF_DRAGON)) { - switch (old_race(u_race(u))) { - case RC_FIREDRAGON: - return 150 * u->number; - case RC_DRAGON: - return 1000 * u->number; - case RC_WYRM: - return 5000 * u->number; - default: - break; - } - } - return 20 * u->number; + const race *rc = u_race(u); + return rc->income * u->number; } static void scramble(void *data, unsigned int n, size_t width) diff --git a/src/economy.test.c b/src/economy.test.c index 7f48df289..3c4a5f257 100644 --- a/src/economy.test.c +++ b/src/economy.test.c @@ -328,11 +328,25 @@ static void test_recruit(CuTest *tc) { test_cleanup(); } +static void test_income(CuTest *tc) +{ + race *rc; + unit *u; + test_setup(); + rc = test_create_race("nerd"); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); + CuAssertIntEquals(tc, 20, income(u)); + u->number = 5; + CuAssertIntEquals(tc, 100, income(u)); + test_cleanup(); +} + CuSuite *get_economy_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_give_control_building); SUITE_ADD_TEST(suite, test_give_control_ship); + SUITE_ADD_TEST(suite, test_income); SUITE_ADD_TEST(suite, test_steal_okay); SUITE_ADD_TEST(suite, test_steal_ocean); SUITE_ADD_TEST(suite, test_steal_nosteal); diff --git a/src/kernel/config.c b/src/kernel/config.c index 8df618aa5..ebfe13833 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -137,16 +137,6 @@ int NMRTimeout(void) return config_get_int("nmr.timeout", 0); } -race_t old_race(const struct race * rc) -{ - race_t i; - // TODO: this sucks so bad! - for (i = 0; i != MAXRACES; ++i) { - if (get_race(i) == rc) return i; - } - return NORACE; -} - helpmode helpmodes[] = { { "all", HELP_ALL } , diff --git a/src/kernel/jsonconf.c b/src/kernel/jsonconf.c index 9e6001f8d..6e60fc90c 100644 --- a/src/kernel/jsonconf.c +++ b/src/kernel/jsonconf.c @@ -481,6 +481,9 @@ static void json_race(cJSON *json, race *rc) { else if (strcmp(child->string, "capacity") == 0) { rc->capacity = child->valueint; } + else if (strcmp(child->string, "income") == 0) { + rc->income = child->valueint; + } else if (strcmp(child->string, "hp") == 0) { rc->hitpoints = child->valueint; } diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c index 3fac3478a..76784ed56 100644 --- a/src/kernel/jsonconf.test.c +++ b/src/kernel/jsonconf.test.c @@ -141,6 +141,7 @@ static void test_races(CuTest * tc) "\"maintenance\" : 2," "\"weight\" : 3," "\"capacity\" : 4," + "\"income\" : 30," "\"hp\" : 5," "\"ac\" : 6," "\"flags\" : [ \"npc\", \"walk\", \"undead\" ]" @@ -167,6 +168,7 @@ static void test_races(CuTest * tc) CuAssertIntEquals(tc, 2, rc->maintenance); CuAssertIntEquals(tc, 3, rc->weight); CuAssertIntEquals(tc, 4, rc->capacity); + CuAssertIntEquals(tc, 30, rc->income); CuAssertIntEquals(tc, 5, rc->hitpoints); CuAssertIntEquals(tc, 6, rc->armor); cJSON_Delete(json); diff --git a/src/kernel/race.c b/src/kernel/race.c index 4003c701e..5b991eca6 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -86,6 +86,50 @@ const struct race *get_race(race_t rt) { return rc_find(name); } +typedef struct xref { + race_t id; + const race *rc; +} rc_xref; + +int cmp_xref(const void *a, const void *b) +{ + const rc_xref *l = (const rc_xref *)a; + const rc_xref *r = (const rc_xref *)b; + if (l->rcrc) return -1; + if (l->rc>r->rc) return 1; + return 0; +} + +static rc_xref *xrefs; +race_t old_race(const struct race * rc) +{ + static int cache; + int i, l, r; + + if (rc_changed(&cache)) { + if (!xrefs) { + xrefs = malloc(sizeof(rc_xref) * MAXRACES); + } + for (i = 0; i != MAXRACES; ++i) { + xrefs[i].rc = get_race(i); + xrefs[i].id = (race_t)i; + } + qsort(xrefs, MAXRACES, sizeof(rc_xref), cmp_xref); + } + l=0; r=MAXRACES-1; + while (l<=r) { + int m = (l+r)/2; + if (rcxrefs[m].rc) { + l = m+1; + } else { + return (race_t)xrefs[m].id; + } + } + return NORACE; +} + race_list *get_familiarraces(void) { static int init = 0; @@ -126,6 +170,8 @@ void free_races(void) { while (races) { race * rc = races->next; free_params(&races->parameters); + free(xrefs); + xrefs = 0; free(races->_name); free(races->def_damage); free(races); @@ -173,6 +219,7 @@ race *rc_create(const char *zName) rc->hitpoints = 1; rc->weight = PERSON_WEIGHT; rc->capacity = 540; + rc->income = 20; rc->recruit_multi = 1.0F; rc->regaura = 1.0F; rc->speed = 1.0F; diff --git a/src/kernel/race.h b/src/kernel/race.h index 17c2bc61f..214f2738d 100644 --- a/src/kernel/race.h +++ b/src/kernel/race.h @@ -124,6 +124,7 @@ extern "C" { int splitsize; int weight; int capacity; + int income; float speed; float aggression; /* chance that a monster will attack */ int hitpoints; diff --git a/src/kernel/race.test.c b/src/kernel/race.test.c index ec4b40f92..663a538ee 100644 --- a/src/kernel/race.test.c +++ b/src/kernel/race.test.c @@ -32,6 +32,7 @@ static void test_rc_defaults(CuTest *tc) { CuAssertIntEquals(tc, 0, rc->recruitcost); CuAssertIntEquals(tc, 0, rc->maintenance); CuAssertIntEquals(tc, 540, rc->capacity); + CuAssertIntEquals(tc, 20, rc->income); CuAssertIntEquals(tc, 1, rc->hitpoints); CuAssertIntEquals(tc, 0, rc->armor); CuAssertIntEquals(tc, 0, rc->at_bonus); @@ -65,10 +66,26 @@ static void test_race_get(CuTest *tc) { test_cleanup(); } +static void test_old_race(CuTest *tc) +{ + race * rc1, *rc2; + test_setup(); + test_create_race("dwarf"); + rc1 = test_create_race("elf"); + rc2 = test_create_race("onkel"); + CuAssertIntEquals(tc, RC_ELF, old_race(rc1)); + CuAssertIntEquals(tc, NORACE, old_race(rc2)); + rc2 = test_create_race("human"); + CuAssertIntEquals(tc, RC_ELF, old_race(rc1)); + CuAssertIntEquals(tc, RC_HUMAN, old_race(rc2)); + test_cleanup(); +} + CuSuite *get_race_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_race_get); + SUITE_ADD_TEST(suite, test_old_race); SUITE_ADD_TEST(suite, test_rc_name); SUITE_ADD_TEST(suite, test_rc_defaults); SUITE_ADD_TEST(suite, test_rc_find); diff --git a/src/kernel/xmlreader.c b/src/kernel/xmlreader.c index 06363de48..894647f89 100644 --- a/src/kernel/xmlreader.c +++ b/src/kernel/xmlreader.c @@ -1656,6 +1656,7 @@ static int parse_races(xmlDocPtr doc) rc->maintenance = xml_ivalue(node, "maintenance", rc->maintenance); rc->weight = xml_ivalue(node, "weight", rc->weight); rc->capacity = xml_ivalue(node, "capacity", rc->capacity); + rc->income = xml_ivalue(node, "income", rc->income); rc->speed = (float)xml_fvalue(node, "speed", rc->speed); rc->hitpoints = xml_ivalue(node, "hp", rc->hitpoints); rc->armor = (char)xml_ivalue(node, "ac", rc->armor); diff --git a/src/laws.c b/src/laws.c index f854ed7ef..e243378dd 100755 --- a/src/laws.c +++ b/src/laws.c @@ -125,7 +125,13 @@ static bool RemoveNMRNewbie(void) static void age_unit(region * r, unit * u) { - if (u_race(u) == get_race(RC_SPELL)) { + static int rc_cache; + static const race *rc_spell; + + if (rc_changed(&rc_cache)) { + rc_spell = get_race(RC_SPELL); + } + if (u_race(u) == rc_spell) { if (--u->age <= 0) { remove_unit(&r->units, u); } diff --git a/src/magic.c b/src/magic.c index bcf3403aa..0a2c685d4 100644 --- a/src/magic.c +++ b/src/magic.c @@ -1031,9 +1031,8 @@ cancast(unit * u, const spell * sp, int level, int range, struct order * ord) double spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order *ord) { - curse *c; double force = cast_level; - int elf_power; + static int elf_power, config; const struct resource_type *rtype; if (sp == NULL) { @@ -1046,54 +1045,65 @@ spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order if (btype && btype->flags & BTF_MAGIC) ++force; } - elf_power = config_get_int("rules.magic.elfpower", 0); - - if (elf_power && u_race(u) == get_race(RC_ELF) && r_isforest(r)) { - ++force; + if (config_changed(&config)) { + elf_power = config_get_int("rules.magic.elfpower", 0); + } + if (elf_power) { + static int rc_cache; + static const race *rc_elf; + if (rc_changed(&rc_cache)) { + rc_elf = get_race(RC_ELF); + } + if (u_race(u) == rc_elf && r_isforest(r)) { + ++force; + } } rtype = rt_find("rop"); if (rtype && i_get(u->items, rtype->itype) > 0) { ++force; } - /* Antimagie in der Zielregion */ - c = get_curse(r->attribs, ct_find("antimagiczone")); - if (curse_active(c)) { - unit *mage = c->magician; - force -= curse_geteffect(c); - curse_changevigour(&r->attribs, c, -cast_level); - cmistake(u, ord, 185, MSG_MAGIC); - if (mage != NULL && mage->faction != NULL) { - if (force > 0) { - ADDMSG(&mage->faction->msgs, msg_message("reduce_spell", - "self mage region", mage, u, r)); + if (r->attribs) { + curse *c; + + /* Antimagie in der Zielregion */ + c = get_curse(r->attribs, ct_find("antimagiczone")); + if (curse_active(c)) { + unit *mage = c->magician; + force -= curse_geteffect(c); + curse_changevigour(&r->attribs, c, -cast_level); + cmistake(u, ord, 185, MSG_MAGIC); + if (mage != NULL && mage->faction != NULL) { + if (force > 0) { + ADDMSG(&mage->faction->msgs, msg_message("reduce_spell", + "self mage region", mage, u, r)); + } + else { + ADDMSG(&mage->faction->msgs, msg_message("block_spell", + "self mage region", mage, u, r)); + } } - else { - ADDMSG(&mage->faction->msgs, msg_message("block_spell", - "self mage region", mage, u, r)); + } + + /* Patzerfluch-Effekt: */ + c = get_curse(r->attribs, ct_find("fumble")); + if (curse_active(c)) { + unit *mage = c->magician; + force -= curse_geteffect(c); + curse_changevigour(&u->attribs, c, -1); + cmistake(u, ord, 185, MSG_MAGIC); + if (mage != NULL && mage->faction != NULL) { + if (force > 0) { + ADDMSG(&mage->faction->msgs, msg_message("reduce_spell", + "self mage region", mage, u, r)); + } + else { + ADDMSG(&mage->faction->msgs, msg_message("block_spell", + "self mage region", mage, u, r)); + } } } } - - /* Patzerfluch-Effekt: */ - c = get_curse(r->attribs, ct_find("fumble")); - if (curse_active(c)) { - unit *mage = c->magician; - force -= curse_geteffect(c); - curse_changevigour(&u->attribs, c, -1); - cmistake(u, ord, 185, MSG_MAGIC); - if (mage != NULL && mage->faction != NULL) { - if (force > 0) { - ADDMSG(&mage->faction->msgs, msg_message("reduce_spell", - "self mage region", mage, u, r)); - } - else { - ADDMSG(&mage->faction->msgs, msg_message("block_spell", - "self mage region", mage, u, r)); - } - } - } - return _max(force, 0); } @@ -2784,6 +2794,8 @@ void magic(void) int rank; castorder *co; spellrank spellranks[MAX_SPELLRANK]; + const race *rc_spell = get_race(RC_SPELL); + const race *rc_insect = get_race(RC_INSECT); memset(spellranks, 0, sizeof(spellranks)); @@ -2792,10 +2804,10 @@ void magic(void) for (u = r->units; u; u = u->next) { order *ord; - if (u->number <= 0 || u_race(u) == get_race(RC_SPELL)) + if (u->number <= 0 || u_race(u) == rc_spell) continue; - if (u_race(u) == get_race(RC_INSECT) && r_insectstalled(r) && + if (u_race(u) == rc_insect && r_insectstalled(r) && !is_cursed(u->attribs, C_KAELTESCHUTZ, 0)) continue; diff --git a/src/monsters.c b/src/monsters.c index ecf768cab..258175b43 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -269,47 +269,57 @@ static direction_t richest_neighbour(region * r, faction * f, int absolut) static bool room_for_race_in_region(region * r, const race * rc) { - unit *u; - int c = 0; + if (rc->splitsize > 0) { + unit *u; + int c = 0; - for (u = r->units; u; u = u->next) { - if (u_race(u) == rc) - c += u->number; + for (u = r->units; u; u = u->next) { + if (u_race(u) == rc) { + c += u->number; + if (c > rc->splitsize * 2) { + return false; + } + } + } } - - if (c > (rc->splitsize * 2)) - return false; - return true; } static direction_t random_neighbour(region * r, unit * u) { int i; - region *rc; - region * next[MAXDIRECTIONS]; + region *next[MAXDIRECTIONS], *backup[MAXDIRECTIONS]; + region **pick; int rr, c = 0, c2 = 0; + const race *rc = u_race(u); get_neighbours(r, next); /* Nachsehen, wieviele Regionen in Frage kommen */ for (i = 0; i != MAXDIRECTIONS; i++) { - rc = next[i]; - if (rc && can_survive(u, rc)) { - if (room_for_race_in_region(rc, u_race(u))) { + region *rn = next[i]; + if (rn && can_survive(u, rn)) { + if (room_for_race_in_region(rn, rc)) { c++; + } else { + next[i] = NULL; } + backup[i] = rn; c2++; + } else { + next[i] = NULL; + backup[i] = NULL; } } + pick = next; if (c == 0) { if (c2 == 0) { return NODIRECTION; } else { + pick = backup; c = c2; - c2 = 0; /* c2 == 0 -> room_for_race nicht beachten */ } } @@ -319,18 +329,14 @@ static direction_t random_neighbour(region * r, unit * u) /* Durchzählen */ - c = -1; + c = 0; for (i = 0; i != MAXDIRECTIONS; i++) { - rc = next[i]; - if (rc && can_survive(u, rc)) { - if (c2 == 0) { - c++; - } - else if (room_for_race_in_region(rc, u_race(u))) { - c++; - } - if (c == rr) + region *rn = pick[i]; + if (rn) { + if (c == rr) { return (direction_t)i; + } + c++; } } @@ -546,19 +552,21 @@ static order *monster_learn(unit * u) return NULL; } -static bool check_overpopulated(unit * u) +static bool check_overpopulated(const unit * u) { - unit *u2; - int c = 0; + const race *rc = u_race(u); + if (rc->splitsize > 0) { + unit *u2; + int c = 0; - for (u2 = u->region->units; u2; u2 = u2->next) { - if (u_race(u2) == u_race(u) && u != u2) - c += u2->number; + for (u2 = u->region->units; u2; u2 = u2->next) { + if (u != u2 && u_race(u2) == rc) { + c += u2->number; + if (c > rc->splitsize * 2) + return true; + } + } } - - if (c > u_race(u)->splitsize * 2) - return true; - return false; } diff --git a/src/randenc.c b/src/randenc.c index 85c3dd8d3..0fe76e883 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -731,11 +731,16 @@ static void orc_growth(void) static void demon_skillchanges(void) { region *r; + static const race *rc_demon; + static int rc_cache; + if (rc_changed(&rc_cache)) { + rc_demon = get_race(RC_DAEMON); + } for (r = regions; r; r = r->next) { unit *u; for (u = r->units; u; u = u->next) { - if (u_race(u) == get_race(RC_DAEMON)) { + if (u_race(u) == rc_demon) { demon_skillchange(u); } } diff --git a/src/study.c b/src/study.c index 027f67dde..1ddaf8dfe 100644 --- a/src/study.c +++ b/src/study.c @@ -546,6 +546,12 @@ int study_cmd(unit * u, order * ord) int maxalchemy = 0; int speed_rule = (study_rule_t)config_get_int("study.speedup", 0); bool learn_newskills = config_get_int("study.newskills", 1) != 0; + static const race *rc_snotling; + static int rc_cache; + + if (rc_changed(&rc_cache)) { + rc_snotling = get_race(RC_SNOTLING); + } if (!unit_can_study(u)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_race_nolearn", "race", @@ -575,7 +581,7 @@ int study_cmd(unit * u, order * ord) } /* snotlings koennen Talente nur bis T8 lernen */ - if (u_race(u) == get_race(RC_SNOTLING)) { + if (u_race(u) == rc_snotling) { if (get_level(u, sk) >= 8) { cmistake(u, ord, 308, MSG_EVENT); return 0;