Merge pull request #499 from ennorehling/feature/bug-2194-academy-refactoring

academy refactoring for bug 2194
This commit is contained in:
Enno Rehling 2016-03-11 21:48:56 +01:00
commit 95b0a0ad9c
42 changed files with 363 additions and 129 deletions

@ -1 +1 @@
Subproject commit 934c2dd94d41da19637a76a1a8b3dfeb7aa8524d Subproject commit ee7a32b9b8986bf2ea6b287975f9ef0ec997b7a3

View File

@ -83,6 +83,7 @@ set (ERESSEA_SRC
spells.c spells.c
battle.c battle.c
alchemy.c alchemy.c
academy.c
upkeep.c upkeep.c
vortex.c vortex.c
names.c names.c

45
src/academy.c Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
#include <platform.h>
#include <kernel/config.h>
#include <kernel/unit.h>
#include <kernel/building.h>
#include <kernel/item.h>
#include <kernel/pool.h>
#include "academy.h"
#include "study.h"
void academy_teaching_bonus(struct unit *u, skill_t sk, int academy) {
if (academy && sk != NOSKILL) {
academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */
learn_skill(u, sk, academy / 30.0 / TEACHNUMBER);
}
}
bool academy_can_teach(unit *teacher, unit *student, skill_t sk) {
const struct building_type *btype = bt_find("academy");
if (active_building(teacher, btype) && active_building(student, btype)) {
int j = study_cost(student, sk);
j = _max(50, j * 2);
/* kann Einheit das zahlen? */
return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j;
/* sonst nehmen sie nicht am Unterricht teil */
}
return false;
}

16
src/academy.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef H_ACADEMY
#define H_ACADEMY
#include <skill.h>
#ifdef __cplusplus
extern "C" {
#endif
struct unit;
void academy_teaching_bonus(struct unit *u, skill_t sk, int academy);
bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alchemy.h" #include "alchemy.h"
#include "move.h" #include "move.h"
#include "skill.h" #include "skill.h"
#include "study.h"
#include <kernel/item.h> #include <kernel/item.h>
#include <kernel/faction.h> #include <kernel/faction.h>

View File

@ -53,10 +53,14 @@ static int a_readkey(attrib *a, void *owner, struct gamedata *data) {
return (res != AT_READ_FAIL) ? AT_READ_DEPR : res; return (res != AT_READ_FAIL) ? AT_READ_DEPR : res;
} }
static void a_freekeys(attrib *a) {
free(a->data.v);
}
attrib_type at_keys = { attrib_type at_keys = {
"keys", "keys",
NULL, NULL,
NULL, a_freekeys,
NULL, NULL,
a_writekeys, a_writekeys,
a_readkeys, a_readkeys,

View File

@ -39,6 +39,7 @@ static void test_upgrade(CuTest *tc) {
CuAssertTrue(tc, key_get(alist, 42)); CuAssertTrue(tc, key_get(alist, 42));
CuAssertTrue(tc, key_get(alist, 43)); CuAssertTrue(tc, key_get(alist, 43));
CuAssertTrue(tc, key_get(alist, 44)); CuAssertTrue(tc, key_get(alist, 44));
a_removeall(&alist, NULL);
} }
CuSuite *get_key_suite(void) CuSuite *get_key_suite(void)

View File

@ -27,6 +27,7 @@ without prior permission by the authors of Eressea.
#include "console.h" #include "console.h"
#include "reports.h" #include "reports.h"
#include "seen.h" #include "seen.h"
#include "study.h"
#include "calendar.h" #include "calendar.h"
#include <kernel/config.h> #include <kernel/config.h>

View File

@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h" #include "laws.h"
#include "randenc.h" #include "randenc.h"
#include "spy.h" #include "spy.h"
#include "study.h"
#include "move.h" #include "move.h"
#include "monster.h" #include "monster.h"
#include "morale.h" #include "morale.h"

View File

@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "xerewards.h" #include "xerewards.h"
#include "magic.h" #include "magic.h"
#include "study.h"
/* kernel includes */ /* kernel includes */
#include <kernel/item.h> #include <kernel/item.h>

View File

@ -1,6 +1,7 @@
#include <platform.h> #include <platform.h>
#include "xerewards.h" #include "xerewards.h"
#include "study.h"
#include <kernel/unit.h> #include <kernel/unit.h>
#include <kernel/item.h> #include <kernel/item.h>

View File

@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alchemy.h" #include "alchemy.h"
#include "direction.h" #include "direction.h"
#include "move.h" #include "move.h"
#include "study.h"
#include "laws.h" #include "laws.h"
#include "skill.h" #include "skill.h"
#include "lighthouse.h" #include "lighthouse.h"

View File

@ -318,7 +318,7 @@ bool checkpasswd(const faction * f, const char *passwd)
{ {
if (!passwd) return false; if (!passwd) return false;
if (password_verify(f->_password, passwd) == VERIFY_FAIL) { if (f->_password && password_verify(f->_password, passwd) == VERIFY_FAIL) {
log_warning("password check failed: %s", factionname(f)); log_warning("password check failed: %s", factionname(f));
return false; return false;
} }
@ -342,7 +342,7 @@ static faction *dead_factions;
void free_flist(faction **fp) { void free_flist(faction **fp) {
faction * flist = *fp; faction * flist = *fp;
for (flist = factions; flist;) { while (flist) {
faction *f = flist; faction *f = flist;
flist = f->next; flist = f->next;
free_faction(f); free_faction(f);
@ -681,8 +681,6 @@ void remove_empty_factions(void)
if (!(f->_alive && f->units!=NULL) && !fval(f, FFL_NOIDLEOUT)) { if (!(f->_alive && f->units!=NULL) && !fval(f, FFL_NOIDLEOUT)) {
log_debug("dead: %s", factionname(f)); log_debug("dead: %s", factionname(f));
destroyfaction(fp); destroyfaction(fp);
free_faction(f);
free(f);
} }
else { else {
fp = &(*fp)->next; fp = &(*fp)->next;

View File

@ -132,6 +132,9 @@ void free_group(group * g)
assert(*g_ptr == g); assert(*g_ptr == g);
*g_ptr = g->nexthash; *g_ptr = g->nexthash;
if (g->attribs) {
a_removeall(&g->attribs, NULL);
}
while (g->allies) { while (g->allies) {
ally *a = g->allies; ally *a = g->allies;
g->allies = a->next; g->allies = a->next;

View File

@ -1203,13 +1203,16 @@ static void free_wtype(weapon_type *wtype) {
int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) { int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
resource_type *rtype; resource_type *rtype;
cb_get_kv(match, &rtype, sizeof(rtype)); cb_get_kv(match, &rtype, sizeof(rtype));
free(rtype->_name);
if (rtype->itype) {
free_itype(rtype->itype);
}
if (rtype->wtype) { if (rtype->wtype) {
free_wtype(rtype->wtype); free_wtype(rtype->wtype);
} }
if (rtype->atype) {
free(rtype->atype);
}
if (rtype->itype) {
free_itype(rtype->itype);
}
free(rtype->_name);
free(rtype); free(rtype);
return 0; return 0;
} }

View File

@ -141,6 +141,7 @@ void racelist_insert(struct race_list **rl, const struct race *r)
void free_races(void) { void free_races(void) {
while (races) { while (races) {
race * rc = races->next; race * rc = races->next;
free_params(&races->parameters);
free(races->_name); free(races->_name);
free(races->def_damage); free(races->def_damage);
free(races); free(races);

View File

@ -296,6 +296,7 @@ static void test_read_password_external(CuTest *tc) {
fclose(F); fclose(F);
_test_read_password(&data, f); _test_read_password(&data, f);
CuAssertPtrNotNull(tc, f->_password); CuAssertPtrNotNull(tc, f->_password);
mstream_done(&data.strm);
gamedata_done(&data); gamedata_done(&data);
CuAssertTrue(tc, checkpasswd(f, "secret")); CuAssertTrue(tc, checkpasswd(f, "secret"));
CuAssertIntEquals(tc, 0, remove(pwfile)); CuAssertIntEquals(tc, 0, remove(pwfile));

View File

@ -28,7 +28,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "curse.h" #include "curse.h"
#include "item.h" #include "item.h"
#include "move.h" #include "move.h"
#include "monster.h"
#include "order.h" #include "order.h"
#include "plane.h" #include "plane.h"
#include "race.h" #include "race.h"
@ -1130,30 +1129,6 @@ void set_number(unit * u, int count)
u->number = (unsigned short)count; u->number = (unsigned short)count;
} }
bool learn_skill(unit * u, skill_t sk, double learn_chance)
{
skill *sv = u->skills;
if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000)
if (!chance(learn_chance))
return false;
while (sv != u->skills + u->skill_size) {
assert(sv->weeks > 0);
if (sv->id == sk) {
if (sv->weeks <= 1) {
sk_set(sv, sv->level + 1);
}
else {
sv->weeks--;
}
return true;
}
++sv;
}
sv = add_skill(u, sk);
sk_set(sv, 1);
return true;
}
void remove_skill(unit * u, skill_t sk) void remove_skill(unit * u, skill_t sk)
{ {
skill *sv = u->skills; skill *sv = u->skills;
@ -1892,25 +1867,6 @@ bool unit_can_study(const unit *u) {
return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE)); return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE));
} }
static double produceexp_chance(void) {
return config_get_flt("study.from_use", 1.0 / 3);
}
void produceexp_ex(struct unit *u, skill_t sk, int n, bool(*learn)(unit *, skill_t, double))
{
if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) {
double chance = produceexp_chance();
if (chance > 0.0F) {
learn(u, sk, (n * chance) / u->number);
}
}
}
void produceexp(struct unit *u, skill_t sk, int n)
{
produceexp_ex(u, sk, n, learn_skill);
}
/* ID's für Einheiten und Zauber */ /* ID's für Einheiten und Zauber */
int newunitid(void) int newunitid(void)
{ {

View File

@ -162,9 +162,6 @@ extern "C" {
struct skill *unit_skill(const struct unit *u, skill_t id); struct skill *unit_skill(const struct unit *u, skill_t id);
bool has_skill(const unit * u, skill_t sk); bool has_skill(const unit * u, skill_t sk);
int effskill(const struct unit *u, skill_t sk, const struct region *r); int effskill(const struct unit *u, skill_t sk, const struct region *r);
int SkillCap(skill_t sk);
void produceexp(struct unit *u, skill_t sk, int n);
void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double));
void set_level(struct unit *u, skill_t id, int level); void set_level(struct unit *u, skill_t id, int level);
int get_level(const struct unit *u, skill_t id); int get_level(const struct unit *u, skill_t id);
@ -209,8 +206,6 @@ extern "C" {
void u_setfaction(struct unit *u, struct faction *f); void u_setfaction(struct unit *u, struct faction *f);
void set_number(struct unit *u, int count); void set_number(struct unit *u, int count);
bool learn_skill(struct unit *u, skill_t sk, double chance);
int invisible(const struct unit *target, const struct unit *viewer); int invisible(const struct unit *target, const struct unit *viewer);
void free_unit(struct unit *u); void free_unit(struct unit *u);

View File

@ -341,32 +341,6 @@ static void test_age_familiar(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static CuTest *g_tc;
static bool cb_learn_one(unit *u, skill_t sk, double chance) {
CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01);
return false;
}
static bool cb_learn_two(unit *u, skill_t sk, double chance) {
CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01);
return false;
}
static void test_produceexp(CuTest *tc) {
unit *u;
g_tc = tc;
test_cleanup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
config_set("study.from_use", "0.5");
produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one);
produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two);
test_cleanup();
}
static void test_inside_building(CuTest *tc) { static void test_inside_building(CuTest *tc) {
unit *u; unit *u;
building *b; building *b;
@ -433,7 +407,6 @@ CuSuite *get_unit_suite(void)
SUITE_ADD_TEST(suite, test_skill_familiar); SUITE_ADD_TEST(suite, test_skill_familiar);
SUITE_ADD_TEST(suite, test_age_familiar); SUITE_ADD_TEST(suite, test_age_familiar);
SUITE_ADD_TEST(suite, test_inside_building); SUITE_ADD_TEST(suite, test_inside_building);
SUITE_ADD_TEST(suite, test_produceexp);
SUITE_ADD_TEST(suite, test_limited_skills); SUITE_ADD_TEST(suite, test_limited_skills);
return suite; return suite;
} }

View File

@ -32,7 +32,7 @@
#define EXPLICIT_CURSE_ISNEW_VERSION 347 /* CURSE_ISNEW is not reset in read/write, but in age() */ #define EXPLICIT_CURSE_ISNEW_VERSION 347 /* CURSE_ISNEW is not reset in read/write, but in age() */
#define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */ #define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */
#define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */ #define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */
#define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */ #define ATTRIBOWNER_VERSION 351 /* all attrib_type functions know who owns the attribute */
#define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */ #define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */
#define ATHASH_VERSION 352 /* attribute-type hash, not name */ #define ATHASH_VERSION 352 /* attribute-type hash, not name */
#define NOWATCH_VERSION 353 /* plane->watchers is gone */ #define NOWATCH_VERSION 353 /* plane->watchers is gone */

View File

@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "magic.h" #include "magic.h"
#include "skill.h" #include "skill.h"
#include "study.h"
#include "laws.h" #include "laws.h"
#include <kernel/ally.h> #include <kernel/ally.h>

View File

@ -26,6 +26,7 @@
#include "monster.h" #include "monster.h"
#include "laws.h" #include "laws.h"
#include "keyword.h" #include "keyword.h"
#include "study.h"
/* triggers includes */ /* triggers includes */
#include <triggers/removecurse.h> #include <triggers/removecurse.h>

View File

@ -13,6 +13,7 @@
#include "monster.h" #include "monster.h"
#include "guard.h" #include "guard.h"
#include "skill.h" #include "skill.h"
#include "study.h"
#include <util/language.h> #include <util/language.h>

View File

@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "move.h" #include "move.h"
#include "laws.h" #include "laws.h"
#include "reports.h" #include "reports.h"
#include "study.h"
#include "alchemy.h" #include "alchemy.h"
#include "travelthru.h" #include "travelthru.h"
#include "vortex.h" #include "vortex.h"

View File

@ -38,6 +38,7 @@ static void test_ship_not_allowed_in_coast(CuTest * tc)
ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO); ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO);
otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO); otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO);
stype = test_create_shiptype("derp"); stype = test_create_shiptype("derp");
free(stype->coasts);
stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *));
r1 = test_create_region(0, 0, ttype); r1 = test_create_region(0, 0, ttype);

View File

@ -47,6 +47,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha
if (v_shiptype) { if (v_shiptype) {
st_boat = st_get_or_create(v_shiptype); st_boat = st_get_or_create(v_shiptype);
u_set_ship(*victim, test_create_ship((*victim)->region, st_boat)); u_set_ship(*victim, test_create_ship((*victim)->region, st_boat));
free(st_boat->coasts);
st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *));
st_boat->coasts[0] = vterrain; st_boat->coasts[0] = vterrain;
st_boat->coasts[1] = 0; st_boat->coasts[1] = 0;
@ -59,6 +60,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha
if (p_shiptype) { if (p_shiptype) {
st_boat = st_get_or_create(p_shiptype); st_boat = st_get_or_create(p_shiptype);
u_set_ship(*pirate, test_create_ship((*pirate)->region, st_boat)); u_set_ship(*pirate, test_create_ship((*pirate)->region, st_boat));
free(st_boat->coasts);
st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *));
st_boat->coasts[0] = vterrain; st_boat->coasts[0] = vterrain;
st_boat->coasts[1] = 0; st_boat->coasts[1] = 0;

View File

@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "move.h" #include "move.h"
#include "alchemy.h" #include "alchemy.h"
#include "chaos.h" #include "chaos.h"
#include "study.h"
/* kernel includes */ /* kernel includes */
#include <kernel/building.h> #include <kernel/building.h>

View File

@ -405,7 +405,8 @@ static void test_write_spell_syntax(CuTest *tc) {
set_parameter(spell, "kc+"); set_parameter(spell, "kc+");
check_spell_syntax(tc, "kc+", &spell, check_spell_syntax(tc, "kc+", &spell,
" ZAUBERE \"Testzauber\" ( REGION | EINHEIT <enr> [<enr> ...] | SCHIFF <snr>\n [<snr> ...] | BURG <bnr> [<bnr> ...] )"); " ZAUBERE \"Testzauber\" ( REGION | EINHEIT <enr> [<enr> ...] | SCHIFF <snr>\n [<snr> ...] | BURG <bnr> [<bnr> ...] )");
spellbook_clear(spell.spb);
free(spell.spb);
test_cleanup(); test_cleanup();
} }

View File

@ -44,19 +44,23 @@ static void test_flyingship(CuTest * tc)
sh1 = test_create_ship(r, shipType1); sh1 = test_create_ship(r, shipType1);
par_data.data.sh = sh1; par_data.data.sh = sh1;
test_create_castorder(&co, u, 10, 10.0, 0, &par);
test_create_castorder(&co, u, 10, 10.0, 0, &par);
CuAssertTrue(tc, !flying_ship(sh1)); CuAssertTrue(tc, !flying_ship(sh1));
CuAssertIntEquals(tc, 10, sp_flying_ship(&co)); CuAssertIntEquals(tc, 10, sp_flying_ship(&co));
CuAssertTrue(tc, flying_ship(sh1)); CuAssertTrue(tc, flying_ship(sh1));
co.par = 0;
free_castorder(&co);
sh2 = test_create_ship(r, shipType2); sh2 = test_create_ship(r, shipType2);
par_data.data.sh = sh2; par_data.data.sh = sh2;
test_create_castorder(&co, u, 10, 10.0, 0, &par);
test_create_castorder(&co, u, 10, 10.0, 0, &par);
CuAssertTrue(tc, !flying_ship(sh2)); CuAssertTrue(tc, !flying_ship(sh2));
CuAssertIntEquals(tc, 0, sp_flying_ship(&co)); CuAssertIntEquals(tc, 0, sp_flying_ship(&co));
CuAssertTrue(tc, !flying_ship(sh2)); CuAssertTrue(tc, !flying_ship(sh2));
co.par = 0;
free_castorder(&co);
test_cleanup(); test_cleanup();
} }

View File

@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h" #include "laws.h"
#include "move.h" #include "move.h"
#include "reports.h" #include "reports.h"
#include "study.h"
/* kernel includes */ /* kernel includes */
#include <kernel/item.h> #include <kernel/item.h>

View File

@ -1,6 +1,7 @@
#include <platform.h> #include <platform.h>
#include <magic.h> #include <magic.h>
#include <kernel/config.h>
#include <kernel/types.h> #include <kernel/types.h>
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/unit.h> #include <kernel/unit.h>
@ -90,7 +91,8 @@ static void setup_sabotage(void) {
test_cleanup(); test_cleanup();
lang = get_or_create_locale("de"); lang = get_or_create_locale("de");
locale_setstring(lang, parameters[P_SHIP], "SCHIFF"); locale_setstring(lang, parameters[P_SHIP], "SCHIFF");
test_create_world(); locale_setstring(lang, parameters[P_ANY], "ALLE");
init_parameters(lang);
init_locales(); init_locales();
} }
@ -100,7 +102,7 @@ static void test_sabotage_self(CuTest *tc) {
order *ord; order *ord;
setup_sabotage(); setup_sabotage();
r = findregion(0, 0); r = test_create_region(0, 0, 0);
assert(r); assert(r);
u = test_create_unit(test_create_faction(NULL), r); u = test_create_unit(test_create_faction(NULL), r);
assert(u && u->faction && u->region == r); assert(u && u->faction && u->region == r);
@ -122,7 +124,7 @@ static void test_sabotage_other_fail(CuTest *tc) {
message *msg; message *msg;
setup_sabotage(); setup_sabotage();
r = findregion(0, 0); r = test_create_region(0, 0, 0);
assert(r); assert(r);
u = test_create_unit(test_create_faction(NULL), r); u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r);
@ -151,7 +153,7 @@ static void test_sabotage_other_success(CuTest *tc) {
order *ord; order *ord;
setup_sabotage(); setup_sabotage();
r = findregion(0, 0); r = test_create_region(0, 0, 0);
assert(r); assert(r);
u = test_create_unit(test_create_faction(NULL), r); u = test_create_unit(test_create_faction(NULL), r);
u2 = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r);

View File

@ -23,7 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/config.h> #include <kernel/config.h>
#include "study.h" #include "study.h"
#include "move.h" #include "move.h"
#include "monster.h"
#include "alchemy.h" #include "alchemy.h"
#include "academy.h"
#include <kernel/ally.h> #include <kernel/ally.h>
#include <kernel/building.h> #include <kernel/building.h>
@ -47,6 +49,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/log.h> #include <util/log.h>
#include <util/parser.h> #include <util/parser.h>
#include <util/rand.h> #include <util/rand.h>
#include <util/rng.h>
#include <util/umlaut.h> #include <util/umlaut.h>
/* libc includes */ /* libc includes */
@ -57,8 +60,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#define TEACHNUMBER 10
static skill_t getskill(const struct locale *lang) static skill_t getskill(const struct locale *lang)
{ {
char token[128]; char token[128];
@ -206,7 +207,6 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
n = _min(n, nteaching); n = _min(n, nteaching);
if (n != 0) { if (n != 0) {
const struct building_type *btype = bt_find("academy");
int index = 0; int index = 0;
if (teach == NULL) { if (teach == NULL) {
@ -227,21 +227,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
} }
teach->value += n; teach->value += n;
/* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und if (student->building && teacher->building == student->building) {
* Student auch in unterschiedlichen Gebaeuden stehen duerfen */ /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und
if (active_building(teacher, btype) && active_building(student, btype)) { * Student auch in unterschiedlichen Gebaeuden stehen duerfen */
int j = study_cost(student, sk); if (academy_can_teach(teacher, student, sk)) {
j = _max(50, j * 2);
/* kann Einheit das zahlen? */
if (get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j) {
/* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */
teach->value += (n / 30) * 10; /* learning erhoehen */ teach->value += (n / 30) * 10; /* learning erhoehen */
/* Lehrer zusaetzlich +1 Tag pro Schueler. */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */
if (academy) if (academy) {
*academy += n; *academy += n;
} /* sonst nehmen sie nicht am Unterricht teil */ }
}
} }
/* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da /* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da
* hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die * hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die
* effektiv gelernten Tage. -> FALSCH ? (ENNO) * effektiv gelernten Tage. -> FALSCH ? (ENNO)
@ -482,9 +479,8 @@ int teach_cmd(unit * u, struct order *ord)
replace_order(&u->orders, ord, new_order); replace_order(&u->orders, ord, new_order);
free_order(new_order); /* parse_order & set_order have each increased the refcount */ free_order(new_order); /* parse_order & set_order have each increased the refcount */
} }
if (academy && sk != NOSKILL) { if (academy) {
academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */ academy_teaching_bonus(u, sk, academy);
learn_skill(u, sk, academy / 30.0 / TEACHNUMBER);
} }
return 0; return 0;
} }
@ -803,3 +799,59 @@ int study_cmd(unit * u, order * ord)
return 0; return 0;
} }
static double produceexp_chance(void) {
return config_get_flt("study.from_use", 1.0 / 3);
}
void produceexp_ex(struct unit *u, skill_t sk, int n, bool(*learn)(unit *, skill_t, double))
{
if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) {
double chance = produceexp_chance();
if (chance > 0.0F) {
learn(u, sk, (n * chance) / u->number);
}
}
}
void produceexp(struct unit *u, skill_t sk, int n)
{
produceexp_ex(u, sk, n, learn_skill);
}
#ifndef NO_TESTS
static learn_fun inject_learn_fun = 0;
void inject_learn(learn_fun fun) {
inject_learn_fun = fun;
}
#endif
bool learn_skill(unit * u, skill_t sk, double learn_chance)
{
skill *sv = u->skills;
#ifndef NO_TESTS
if (inject_learn_fun) {
return inject_learn_fun(u, sk, learn_chance);
}
#endif
if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000)
if (!chance(learn_chance))
return false;
while (sv != u->skills + u->skill_size) {
assert(sv->weeks > 0);
if (sv->id == sk) {
if (sv->weeks <= 1) {
sk_set(sv, sv->level + 1);
}
else {
sv->weeks--;
}
return true;
}
++sv;
}
sv = add_skill(u, sk);
sk_set(sv, 1);
return true;
}

View File

@ -26,14 +26,25 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" { extern "C" {
#endif #endif
extern int teach_cmd(struct unit *u, struct order *ord); struct unit;
extern int study_cmd(struct unit *u, struct order *ord);
extern magic_t getmagicskill(const struct locale *lang); int teach_cmd(struct unit *u, struct order *ord);
extern bool is_migrant(struct unit *u); int study_cmd(struct unit *u, struct order *ord);
extern int study_cost(struct unit *u, skill_t talent);
magic_t getmagicskill(const struct locale *lang);
bool is_migrant(struct unit *u);
int study_cost(struct unit *u, skill_t talent);
#ifndef NO_TESTS
typedef bool(*learn_fun)(struct unit *u, skill_t sk, double ch);
void inject_learn(learn_fun fun);
#endif
bool learn_skill(struct unit *u, skill_t sk, double chance);
void produceexp(struct unit *u, skill_t sk, int n);
void produceexp_ex(struct unit *u, skill_t sk, int n, bool(*learn)(struct unit *, skill_t, double));
#define MAXTEACHERS 20 #define MAXTEACHERS 20
#define TEACHNUMBER 10
typedef struct teaching_info { typedef struct teaching_info {
struct unit *teachers[MAXTEACHERS]; struct unit *teachers[MAXTEACHERS];
int value; int value;
@ -41,6 +52,7 @@ extern "C" {
extern const struct attrib_type at_learning; extern const struct attrib_type at_learning;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -5,8 +5,11 @@
#include <kernel/config.h> #include <kernel/config.h>
#include <kernel/unit.h> #include <kernel/unit.h>
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/item.h>
#include <kernel/order.h> #include <kernel/order.h>
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/building.h>
#include <util/rand.h>
#include <util/message.h> #include <util/message.h>
#include <util/language.h> #include <util/language.h>
#include <util/base36.h> #include <util/base36.h>
@ -21,6 +24,17 @@ typedef struct {
unit *teachers[2]; unit *teachers[2];
} study_fixture; } study_fixture;
static void setup_locale(struct locale *lang) {
int i;
for (i = 0; i < MAXSKILLS; ++i) {
if (!locale_getstring(lang, mkname("skill", skillnames[i])))
locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]);
}
locale_setstring(lang, parameters[P_ANY], "ALLE");
init_parameters(lang);
init_skills(lang);
}
static void setup_study(study_fixture *fix, skill_t sk) { static void setup_study(study_fixture *fix, skill_t sk) {
struct region * r; struct region * r;
struct faction *f; struct faction *f;
@ -33,8 +47,7 @@ static void setup_study(study_fixture *fix, skill_t sk) {
r = findregion(0, 0); r = findregion(0, 0);
f = test_create_faction(0); f = test_create_faction(0);
lang = get_or_create_locale(locale_name(f->locale)); lang = get_or_create_locale(locale_name(f->locale));
locale_setstring(lang, mkname("skill", skillnames[sk]), skillnames[sk]); setup_locale(lang);
init_skills(lang);
fix->u = test_create_unit(f, r); fix->u = test_create_unit(f, r);
assert(fix->u); assert(fix->u);
fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]);
@ -80,12 +93,10 @@ static void test_study_with_teacher(CuTest *tc) {
static void test_study_with_bad_teacher(CuTest *tc) { static void test_study_with_bad_teacher(CuTest *tc) {
study_fixture fix; study_fixture fix;
skill *sv; skill *sv;
message *msg;
setup_study(&fix, SK_CROSSBOW); setup_study(&fix, SK_CROSSBOW);
teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder);
CuAssertPtrNotNull(tc, msg = test_get_last_message(fix.u->faction->msgs)); CuAssertPtrNotNull(tc, test_find_messagetype(fix.u->faction->msgs, "teach_asgood"));
CuAssertStrEquals(tc, "teach_asgood", test_get_messagetype(msg));
study_cmd(fix.u, fix.u->thisorder); study_cmd(fix.u, fix.u->thisorder);
CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW));
CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 1, sv->level);
@ -93,11 +104,139 @@ static void test_study_with_bad_teacher(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static void test_study_bug_2194(CuTest *tc) {
unit *u, *u1, *u2;
struct locale * loc;
building * b;
test_cleanup();
random_source_inject_constant(0.0);
init_resources();
loc = get_or_create_locale("de");
setup_locale(loc);
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
scale_number(u, 2);
set_level(u, SK_CROSSBOW, TEACHDIFFERENCE);
u->faction->locale = loc;
u1 = test_create_unit(u->faction, u->region);
scale_number(u1, 17);
u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]);
u2 = test_create_unit(u->faction, u->region);
scale_number(u2, 3);
u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_MAGIC]);
u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no));
b = test_create_building(u->region, test_create_buildingtype("academy"));
b->size = 22;
u_set_building(u, b);
u_set_building(u1, b);
u_set_building(u2, b);
i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50);
i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50);
b->flags = BLD_WORKING;
teach_cmd(u, u->thisorder);
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood"));
test_cleanup();
}
static CuTest *g_tc;
static bool cb_learn_one(unit *u, skill_t sk, double chance) {
CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01);
return false;
}
static bool cb_learn_two(unit *u, skill_t sk, double chance) {
CuAssertIntEquals(g_tc, SK_ALCHEMY, sk);
CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01);
return false;
}
static void test_produceexp(CuTest *tc) {
unit *u;
g_tc = tc;
test_cleanup();
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
config_set("study.from_use", "0.5");
produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one);
produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two);
test_cleanup();
}
#define MAXLOG 4
typedef struct log_entry {
unit *u;
skill_t sk;
double ch;
} log_entry;
static log_entry log_learners[MAXLOG];
static int log_size;
static bool log_learn(unit *u, skill_t sk, double ch) {
if (log_size < MAXLOG) {
log_entry * entry = &log_learners[log_size++];
entry->u = u;
entry->sk = sk;
entry->ch = ch;
}
return true;
}
static void test_academy_building(CuTest *tc) {
unit *u, *u1, *u2;
struct locale * loc;
building * b;
message * msg;
test_cleanup();
mt_register(mt_new_va("teach_asgood", "unit:unit", "region:region", "command:order", "student:unit", 0));
random_source_inject_constant(0.0);
init_resources();
loc = get_or_create_locale("de");
setup_locale(loc);
u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0));
scale_number(u, 2);
set_level(u, SK_CROSSBOW, TEACHDIFFERENCE);
u->faction->locale = loc;
u1 = test_create_unit(u->faction, u->region);
scale_number(u1, 15);
u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]);
u2 = test_create_unit(u->faction, u->region);
scale_number(u2, 5);
u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]);
set_level(u2, SK_CROSSBOW, 1);
u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no));
b = test_create_building(u->region, test_create_buildingtype("academy"));
b->size = 22;
u_set_building(u, b);
u_set_building(u1, b);
u_set_building(u2, b);
i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50);
i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50);
b->flags = BLD_WORKING;
inject_learn(log_learn);
teach_cmd(u, u->thisorder);
inject_learn(0);
CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood"));
CuAssertPtrEquals(tc, u, (unit *)(msg)->parameters[0].v);
CuAssertPtrEquals(tc, u2, (unit *)(msg)->parameters[3].v);
CuAssertPtrEquals(tc, u, log_learners[0].u);
CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk);
CuAssertDblEquals(tc, 0.05, log_learners[0].ch, 0.001);
test_cleanup();
}
CuSuite *get_study_suite(void) CuSuite *get_study_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_study_no_teacher); SUITE_ADD_TEST(suite, test_study_no_teacher);
SUITE_ADD_TEST(suite, test_study_with_teacher); SUITE_ADD_TEST(suite, test_study_with_teacher);
SUITE_ADD_TEST(suite, test_study_with_bad_teacher); SUITE_ADD_TEST(suite, test_study_with_bad_teacher);
SUITE_ADD_TEST(suite, test_produceexp);
SUITE_ADD_TEST(suite, test_academy_building);
DISABLE_TEST(suite, test_study_bug_2194);
return suite; return suite;
} }

View File

@ -154,11 +154,13 @@ ship_type * test_create_shiptype(const char * name)
stype->construction->skill = SK_SHIPBUILDING; stype->construction->skill = SK_SHIPBUILDING;
} }
if (stype->coasts) {
free(stype->coasts);
}
stype->coasts = stype->coasts =
(terrain_type **)malloc(sizeof(terrain_type *)*2); (terrain_type **)malloc(sizeof(terrain_type *) * 2);
stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO | FLY_INTO); stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO | FLY_INTO);
stype->coasts[1] = NULL; stype->coasts[1] = NULL;
if (default_locale) { if (default_locale) {
locale_setstring(default_locale, name, name); locale_setstring(default_locale, name, name);
} }

View File

@ -24,6 +24,8 @@ static void test_shock(CuTest *tc) {
CuAssertIntEquals(tc, 2, u->hp); CuAssertIntEquals(tc, 2, u->hp);
CuAssertIntEquals(tc, 2, get_spellpoints(u)); CuAssertIntEquals(tc, 2, get_spellpoints(u));
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "shock")); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "shock"));
t_free(tt);
free(tt);
test_cleanup(); test_cleanup();
} }
@ -41,6 +43,8 @@ static void test_shock_low(CuTest *tc) {
tt->type->handle(tt, u); tt->type->handle(tt, u);
CuAssertIntEquals(tc, 1, u->hp); CuAssertIntEquals(tc, 1, u->hp);
CuAssertIntEquals(tc, 1, get_spellpoints(u)); CuAssertIntEquals(tc, 1, get_spellpoints(u));
t_free(tt);
free(tt);
test_cleanup(); test_cleanup();
} }

View File

@ -49,6 +49,7 @@ static void test_attrib_remove_self(CuTest * tc) {
CuAssertPtrEquals(tc, 0, alist->nexttype); CuAssertPtrEquals(tc, 0, alist->nexttype);
CuAssertIntEquals(tc, 1, a_remove(&alist, alist)); CuAssertIntEquals(tc, 1, a_remove(&alist, alist));
CuAssertPtrEquals(tc, a, alist); CuAssertPtrEquals(tc, a, alist);
a_removeall(&alist, NULL);
} }

View File

@ -63,6 +63,7 @@ int read_triggers(struct gamedata *data, trigger ** tp)
break; break;
case AT_READ_FAIL: case AT_READ_FAIL:
t_free(*tp); t_free(*tp);
free(*tp);
*tp = NULL; *tp = NULL;
break; break;
default: default:
@ -107,6 +108,7 @@ int handle_triggers(trigger ** triggers, void *param)
if (t->type->handle(t, param) != 0) { if (t->type->handle(t, param) != 0) {
*tp = t->next; *tp = t->next;
t_free(t); t_free(t);
free(t);
} }
else else
tp = &t->next; tp = &t->next;
@ -260,6 +262,7 @@ const trigger_type * tt)
if (t->type == tt) { if (t->type == tt) {
*tp = t->next; *tp = t->next;
t_free(t); t_free(t);
free(t);
} }
else else
tp = &t->next; tp = &t->next;

View File

@ -23,11 +23,6 @@ void gamedata_init(gamedata *data, storage *store, int version) {
binstore_init(data->store, &data->strm); binstore_init(data->store, &data->strm);
} }
void gamedata_close(gamedata *data) {
gamedata_done(data);
fstream_done(&data->strm);
}
int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version) { int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version) {
FILE *F = fopen(filename, mode); FILE *F = fopen(filename, mode);
if (F) { if (F) {
@ -70,3 +65,9 @@ gamedata *gamedata_open(const char *filename, const char *mode, int version) {
} }
return data; return data;
} }
void gamedata_close(gamedata *data) {
gamedata_done(data);
fstream_done(&data->strm);
free(data->store);
}

View File

@ -11,9 +11,11 @@ static void test_gamedata(CuTest * tc)
data = gamedata_open("test.dat", "wb", 0); data = gamedata_open("test.dat", "wb", 0);
CuAssertPtrNotNull(tc, data); CuAssertPtrNotNull(tc, data);
gamedata_close(data); gamedata_close(data);
free(data);
data = gamedata_open("test.dat", "rb", 0); data = gamedata_open("test.dat", "rb", 0);
CuAssertPtrNotNull(tc, data); CuAssertPtrNotNull(tc, data);
gamedata_close(data); gamedata_close(data);
free(data);
CuAssertIntEquals(tc, 0, remove("test.dat")); CuAssertIntEquals(tc, 0, remove("test.dat"));
} }

View File

@ -171,7 +171,6 @@ void addtoken(void ** root, const char *str, variant id)
ref = (tref *)malloc(sizeof(tref)); ref = (tref *)malloc(sizeof(tref));
ref->ucs = ucs; ref->ucs = ucs;
ref->node = 0;
ref->node = node; ref->node = node;
ref->nexthash = tk->next[index]; ref->nexthash = tk->next[index];
tk->next[index] = ref; tk->next[index] = ref;