forked from github/server
Merge pull request #499 from ennorehling/feature/bug-2194-academy-refactoring
academy refactoring for bug 2194
This commit is contained in:
commit
95b0a0ad9c
2
critbit
2
critbit
|
@ -1 +1 @@
|
|||
Subproject commit 934c2dd94d41da19637a76a1a8b3dfeb7aa8524d
|
||||
Subproject commit ee7a32b9b8986bf2ea6b287975f9ef0ec997b7a3
|
|
@ -83,6 +83,7 @@ set (ERESSEA_SRC
|
|||
spells.c
|
||||
battle.c
|
||||
alchemy.c
|
||||
academy.c
|
||||
upkeep.c
|
||||
vortex.c
|
||||
names.c
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "alchemy.h"
|
||||
#include "move.h"
|
||||
#include "skill.h"
|
||||
#include "study.h"
|
||||
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/faction.h>
|
||||
|
|
|
@ -53,10 +53,14 @@ static int a_readkey(attrib *a, void *owner, struct gamedata *data) {
|
|||
return (res != AT_READ_FAIL) ? AT_READ_DEPR : res;
|
||||
}
|
||||
|
||||
static void a_freekeys(attrib *a) {
|
||||
free(a->data.v);
|
||||
}
|
||||
|
||||
attrib_type at_keys = {
|
||||
"keys",
|
||||
NULL,
|
||||
NULL,
|
||||
a_freekeys,
|
||||
NULL,
|
||||
a_writekeys,
|
||||
a_readkeys,
|
||||
|
|
|
@ -39,6 +39,7 @@ static void test_upgrade(CuTest *tc) {
|
|||
CuAssertTrue(tc, key_get(alist, 42));
|
||||
CuAssertTrue(tc, key_get(alist, 43));
|
||||
CuAssertTrue(tc, key_get(alist, 44));
|
||||
a_removeall(&alist, NULL);
|
||||
}
|
||||
|
||||
CuSuite *get_key_suite(void)
|
||||
|
|
|
@ -27,6 +27,7 @@ without prior permission by the authors of Eressea.
|
|||
#include "console.h"
|
||||
#include "reports.h"
|
||||
#include "seen.h"
|
||||
#include "study.h"
|
||||
#include "calendar.h"
|
||||
|
||||
#include <kernel/config.h>
|
||||
|
|
|
@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "laws.h"
|
||||
#include "randenc.h"
|
||||
#include "spy.h"
|
||||
#include "study.h"
|
||||
#include "move.h"
|
||||
#include "monster.h"
|
||||
#include "morale.h"
|
||||
|
|
|
@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "xerewards.h"
|
||||
|
||||
#include "magic.h"
|
||||
#include "study.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/item.h>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <platform.h>
|
||||
|
||||
#include "xerewards.h"
|
||||
#include "study.h"
|
||||
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/item.h>
|
||||
|
|
|
@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "alchemy.h"
|
||||
#include "direction.h"
|
||||
#include "move.h"
|
||||
#include "study.h"
|
||||
#include "laws.h"
|
||||
#include "skill.h"
|
||||
#include "lighthouse.h"
|
||||
|
|
|
@ -318,7 +318,7 @@ bool checkpasswd(const faction * f, const char *passwd)
|
|||
{
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ static faction *dead_factions;
|
|||
|
||||
void free_flist(faction **fp) {
|
||||
faction * flist = *fp;
|
||||
for (flist = factions; flist;) {
|
||||
while (flist) {
|
||||
faction *f = flist;
|
||||
flist = f->next;
|
||||
free_faction(f);
|
||||
|
@ -681,8 +681,6 @@ void remove_empty_factions(void)
|
|||
if (!(f->_alive && f->units!=NULL) && !fval(f, FFL_NOIDLEOUT)) {
|
||||
log_debug("dead: %s", factionname(f));
|
||||
destroyfaction(fp);
|
||||
free_faction(f);
|
||||
free(f);
|
||||
}
|
||||
else {
|
||||
fp = &(*fp)->next;
|
||||
|
|
|
@ -132,6 +132,9 @@ void free_group(group * g)
|
|||
assert(*g_ptr == g);
|
||||
*g_ptr = g->nexthash;
|
||||
|
||||
if (g->attribs) {
|
||||
a_removeall(&g->attribs, NULL);
|
||||
}
|
||||
while (g->allies) {
|
||||
ally *a = g->allies;
|
||||
g->allies = a->next;
|
||||
|
|
|
@ -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) {
|
||||
resource_type *rtype;
|
||||
cb_get_kv(match, &rtype, sizeof(rtype));
|
||||
free(rtype->_name);
|
||||
if (rtype->itype) {
|
||||
free_itype(rtype->itype);
|
||||
}
|
||||
if (rtype->wtype) {
|
||||
free_wtype(rtype->wtype);
|
||||
}
|
||||
if (rtype->atype) {
|
||||
free(rtype->atype);
|
||||
}
|
||||
if (rtype->itype) {
|
||||
free_itype(rtype->itype);
|
||||
}
|
||||
free(rtype->_name);
|
||||
free(rtype);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -141,6 +141,7 @@ void racelist_insert(struct race_list **rl, const struct race *r)
|
|||
void free_races(void) {
|
||||
while (races) {
|
||||
race * rc = races->next;
|
||||
free_params(&races->parameters);
|
||||
free(races->_name);
|
||||
free(races->def_damage);
|
||||
free(races);
|
||||
|
|
|
@ -296,6 +296,7 @@ static void test_read_password_external(CuTest *tc) {
|
|||
fclose(F);
|
||||
_test_read_password(&data, f);
|
||||
CuAssertPtrNotNull(tc, f->_password);
|
||||
mstream_done(&data.strm);
|
||||
gamedata_done(&data);
|
||||
CuAssertTrue(tc, checkpasswd(f, "secret"));
|
||||
CuAssertIntEquals(tc, 0, remove(pwfile));
|
||||
|
|
|
@ -28,7 +28,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "curse.h"
|
||||
#include "item.h"
|
||||
#include "move.h"
|
||||
#include "monster.h"
|
||||
#include "order.h"
|
||||
#include "plane.h"
|
||||
#include "race.h"
|
||||
|
@ -1130,30 +1129,6 @@ void set_number(unit * u, int 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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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 */
|
||||
int newunitid(void)
|
||||
{
|
||||
|
|
|
@ -162,9 +162,6 @@ extern "C" {
|
|||
struct skill *unit_skill(const struct unit *u, skill_t id);
|
||||
bool has_skill(const unit * u, skill_t sk);
|
||||
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);
|
||||
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 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);
|
||||
void free_unit(struct unit *u);
|
||||
|
||||
|
|
|
@ -341,32 +341,6 @@ static void test_age_familiar(CuTest *tc) {
|
|||
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) {
|
||||
unit *u;
|
||||
building *b;
|
||||
|
@ -433,7 +407,6 @@ CuSuite *get_unit_suite(void)
|
|||
SUITE_ADD_TEST(suite, test_skill_familiar);
|
||||
SUITE_ADD_TEST(suite, test_age_familiar);
|
||||
SUITE_ADD_TEST(suite, test_inside_building);
|
||||
SUITE_ADD_TEST(suite, test_produceexp);
|
||||
SUITE_ADD_TEST(suite, test_limited_skills);
|
||||
return suite;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#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 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 ATHASH_VERSION 352 /* attribute-type hash, not name */
|
||||
#define NOWATCH_VERSION 353 /* plane->watchers is gone */
|
||||
|
|
|
@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "magic.h"
|
||||
|
||||
#include "skill.h"
|
||||
#include "study.h"
|
||||
#include "laws.h"
|
||||
|
||||
#include <kernel/ally.h>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "monster.h"
|
||||
#include "laws.h"
|
||||
#include "keyword.h"
|
||||
#include "study.h"
|
||||
|
||||
/* triggers includes */
|
||||
#include <triggers/removecurse.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "monster.h"
|
||||
#include "guard.h"
|
||||
#include "skill.h"
|
||||
#include "study.h"
|
||||
|
||||
#include <util/language.h>
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "move.h"
|
||||
#include "laws.h"
|
||||
#include "reports.h"
|
||||
#include "study.h"
|
||||
#include "alchemy.h"
|
||||
#include "travelthru.h"
|
||||
#include "vortex.h"
|
||||
|
|
|
@ -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);
|
||||
otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO);
|
||||
stype = test_create_shiptype("derp");
|
||||
free(stype->coasts);
|
||||
stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *));
|
||||
|
||||
r1 = test_create_region(0, 0, ttype);
|
||||
|
|
|
@ -47,6 +47,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha
|
|||
if (v_shiptype) {
|
||||
st_boat = st_get_or_create(v_shiptype);
|
||||
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[0] = vterrain;
|
||||
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) {
|
||||
st_boat = st_get_or_create(p_shiptype);
|
||||
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[0] = vterrain;
|
||||
st_boat->coasts[1] = 0;
|
||||
|
|
|
@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "move.h"
|
||||
#include "alchemy.h"
|
||||
#include "chaos.h"
|
||||
#include "study.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/building.h>
|
||||
|
|
|
@ -405,7 +405,8 @@ static void test_write_spell_syntax(CuTest *tc) {
|
|||
set_parameter(spell, "kc+");
|
||||
check_spell_syntax(tc, "kc+", &spell,
|
||||
" ZAUBERE \"Testzauber\" ( REGION | EINHEIT <enr> [<enr> ...] | SCHIFF <snr>\n [<snr> ...] | BURG <bnr> [<bnr> ...] )");
|
||||
|
||||
spellbook_clear(spell.spb);
|
||||
free(spell.spb);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
|
|
@ -44,19 +44,23 @@ static void test_flyingship(CuTest * tc)
|
|||
|
||||
sh1 = test_create_ship(r, shipType1);
|
||||
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));
|
||||
CuAssertIntEquals(tc, 10, sp_flying_ship(&co));
|
||||
CuAssertTrue(tc, flying_ship(sh1));
|
||||
co.par = 0;
|
||||
free_castorder(&co);
|
||||
|
||||
sh2 = test_create_ship(r, shipType2);
|
||||
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));
|
||||
CuAssertIntEquals(tc, 0, sp_flying_ship(&co));
|
||||
CuAssertTrue(tc, !flying_ship(sh2));
|
||||
co.par = 0;
|
||||
free_castorder(&co);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include "laws.h"
|
||||
#include "move.h"
|
||||
#include "reports.h"
|
||||
#include "study.h"
|
||||
|
||||
/* kernel includes */
|
||||
#include <kernel/item.h>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <platform.h>
|
||||
|
||||
#include <magic.h>
|
||||
#include <kernel/config.h>
|
||||
#include <kernel/types.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/unit.h>
|
||||
|
@ -90,7 +91,8 @@ static void setup_sabotage(void) {
|
|||
test_cleanup();
|
||||
lang = get_or_create_locale("de");
|
||||
locale_setstring(lang, parameters[P_SHIP], "SCHIFF");
|
||||
test_create_world();
|
||||
locale_setstring(lang, parameters[P_ANY], "ALLE");
|
||||
init_parameters(lang);
|
||||
init_locales();
|
||||
}
|
||||
|
||||
|
@ -100,7 +102,7 @@ static void test_sabotage_self(CuTest *tc) {
|
|||
order *ord;
|
||||
|
||||
setup_sabotage();
|
||||
r = findregion(0, 0);
|
||||
r = test_create_region(0, 0, 0);
|
||||
assert(r);
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
assert(u && u->faction && u->region == r);
|
||||
|
@ -122,7 +124,7 @@ static void test_sabotage_other_fail(CuTest *tc) {
|
|||
message *msg;
|
||||
|
||||
setup_sabotage();
|
||||
r = findregion(0, 0);
|
||||
r = test_create_region(0, 0, 0);
|
||||
assert(r);
|
||||
u = 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;
|
||||
|
||||
setup_sabotage();
|
||||
r = findregion(0, 0);
|
||||
r = test_create_region(0, 0, 0);
|
||||
assert(r);
|
||||
u = test_create_unit(test_create_faction(NULL), r);
|
||||
u2 = test_create_unit(test_create_faction(NULL), r);
|
||||
|
|
86
src/study.c
86
src/study.c
|
@ -23,7 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <kernel/config.h>
|
||||
#include "study.h"
|
||||
#include "move.h"
|
||||
#include "monster.h"
|
||||
#include "alchemy.h"
|
||||
#include "academy.h"
|
||||
|
||||
#include <kernel/ally.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/parser.h>
|
||||
#include <util/rand.h>
|
||||
#include <util/rng.h>
|
||||
#include <util/umlaut.h>
|
||||
|
||||
/* libc includes */
|
||||
|
@ -57,8 +60,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define TEACHNUMBER 10
|
||||
|
||||
static skill_t getskill(const struct locale *lang)
|
||||
{
|
||||
char token[128];
|
||||
|
@ -206,7 +207,6 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
|
|||
n = _min(n, nteaching);
|
||||
|
||||
if (n != 0) {
|
||||
const struct building_type *btype = bt_find("academy");
|
||||
int index = 0;
|
||||
|
||||
if (teach == NULL) {
|
||||
|
@ -227,21 +227,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
|
|||
}
|
||||
teach->value += n;
|
||||
|
||||
/* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und
|
||||
* Student auch in unterschiedlichen Gebaeuden stehen duerfen */
|
||||
if (active_building(teacher, btype) && active_building(student, btype)) {
|
||||
int j = study_cost(student, sk);
|
||||
j = _max(50, j * 2);
|
||||
/* kann Einheit das zahlen? */
|
||||
if (get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j) {
|
||||
if (student->building && teacher->building == student->building) {
|
||||
/* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und
|
||||
* Student auch in unterschiedlichen Gebaeuden stehen duerfen */
|
||||
if (academy_can_teach(teacher, student, sk)) {
|
||||
/* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */
|
||||
teach->value += (n / 30) * 10; /* learning erhoehen */
|
||||
/* Lehrer zusaetzlich +1 Tag pro Schueler. */
|
||||
if (academy)
|
||||
/* Lehrer zusaetzlich +1 Tag pro Schueler. */
|
||||
if (academy) {
|
||||
*academy += n;
|
||||
} /* sonst nehmen sie nicht am Unterricht teil */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da
|
||||
* hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die
|
||||
* effektiv gelernten Tage. -> FALSCH ? (ENNO)
|
||||
|
@ -482,9 +479,8 @@ int teach_cmd(unit * u, struct order *ord)
|
|||
replace_order(&u->orders, ord, new_order);
|
||||
free_order(new_order); /* parse_order & set_order have each increased the refcount */
|
||||
}
|
||||
if (academy && sk != NOSKILL) {
|
||||
academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */
|
||||
learn_skill(u, sk, academy / 30.0 / TEACHNUMBER);
|
||||
if (academy) {
|
||||
academy_teaching_bonus(u, sk, academy);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -803,3 +799,59 @@ int study_cmd(unit * u, order * ord)
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
22
src/study.h
22
src/study.h
|
@ -26,14 +26,25 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int teach_cmd(struct unit *u, struct order *ord);
|
||||
extern int study_cmd(struct unit *u, struct order *ord);
|
||||
struct unit;
|
||||
|
||||
extern magic_t getmagicskill(const struct locale *lang);
|
||||
extern bool is_migrant(struct unit *u);
|
||||
extern int study_cost(struct unit *u, skill_t talent);
|
||||
int teach_cmd(struct unit *u, struct order *ord);
|
||||
int study_cmd(struct unit *u, struct order *ord);
|
||||
|
||||
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 TEACHNUMBER 10
|
||||
typedef struct teaching_info {
|
||||
struct unit *teachers[MAXTEACHERS];
|
||||
int value;
|
||||
|
@ -41,6 +52,7 @@ extern "C" {
|
|||
|
||||
extern const struct attrib_type at_learning;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
149
src/study.test.c
149
src/study.test.c
|
@ -5,8 +5,11 @@
|
|||
#include <kernel/config.h>
|
||||
#include <kernel/unit.h>
|
||||
#include <kernel/faction.h>
|
||||
#include <kernel/item.h>
|
||||
#include <kernel/order.h>
|
||||
#include <kernel/region.h>
|
||||
#include <kernel/building.h>
|
||||
#include <util/rand.h>
|
||||
#include <util/message.h>
|
||||
#include <util/language.h>
|
||||
#include <util/base36.h>
|
||||
|
@ -21,6 +24,17 @@ typedef struct {
|
|||
unit *teachers[2];
|
||||
} 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) {
|
||||
struct region * r;
|
||||
struct faction *f;
|
||||
|
@ -33,8 +47,7 @@ static void setup_study(study_fixture *fix, skill_t sk) {
|
|||
r = findregion(0, 0);
|
||||
f = test_create_faction(0);
|
||||
lang = get_or_create_locale(locale_name(f->locale));
|
||||
locale_setstring(lang, mkname("skill", skillnames[sk]), skillnames[sk]);
|
||||
init_skills(lang);
|
||||
setup_locale(lang);
|
||||
fix->u = test_create_unit(f, r);
|
||||
assert(fix->u);
|
||||
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) {
|
||||
study_fixture fix;
|
||||
skill *sv;
|
||||
message *msg;
|
||||
|
||||
setup_study(&fix, SK_CROSSBOW);
|
||||
teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder);
|
||||
CuAssertPtrNotNull(tc, msg = test_get_last_message(fix.u->faction->msgs));
|
||||
CuAssertStrEquals(tc, "teach_asgood", test_get_messagetype(msg));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(fix.u->faction->msgs, "teach_asgood"));
|
||||
study_cmd(fix.u, fix.u->thisorder);
|
||||
CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW));
|
||||
CuAssertIntEquals(tc, 1, sv->level);
|
||||
|
@ -93,11 +104,139 @@ static void test_study_with_bad_teacher(CuTest *tc) {
|
|||
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 *suite = CuSuiteNew();
|
||||
SUITE_ADD_TEST(suite, test_study_no_teacher);
|
||||
SUITE_ADD_TEST(suite, test_study_with_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;
|
||||
}
|
||||
|
|
|
@ -154,11 +154,13 @@ ship_type * test_create_shiptype(const char * name)
|
|||
stype->construction->skill = SK_SHIPBUILDING;
|
||||
}
|
||||
|
||||
if (stype->coasts) {
|
||||
free(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[1] = NULL;
|
||||
|
||||
if (default_locale) {
|
||||
locale_setstring(default_locale, name, name);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ static void test_shock(CuTest *tc) {
|
|||
CuAssertIntEquals(tc, 2, u->hp);
|
||||
CuAssertIntEquals(tc, 2, get_spellpoints(u));
|
||||
CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "shock"));
|
||||
t_free(tt);
|
||||
free(tt);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
@ -41,6 +43,8 @@ static void test_shock_low(CuTest *tc) {
|
|||
tt->type->handle(tt, u);
|
||||
CuAssertIntEquals(tc, 1, u->hp);
|
||||
CuAssertIntEquals(tc, 1, get_spellpoints(u));
|
||||
t_free(tt);
|
||||
free(tt);
|
||||
test_cleanup();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ static void test_attrib_remove_self(CuTest * tc) {
|
|||
CuAssertPtrEquals(tc, 0, alist->nexttype);
|
||||
CuAssertIntEquals(tc, 1, a_remove(&alist, alist));
|
||||
CuAssertPtrEquals(tc, a, alist);
|
||||
a_removeall(&alist, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ int read_triggers(struct gamedata *data, trigger ** tp)
|
|||
break;
|
||||
case AT_READ_FAIL:
|
||||
t_free(*tp);
|
||||
free(*tp);
|
||||
*tp = NULL;
|
||||
break;
|
||||
default:
|
||||
|
@ -107,6 +108,7 @@ int handle_triggers(trigger ** triggers, void *param)
|
|||
if (t->type->handle(t, param) != 0) {
|
||||
*tp = t->next;
|
||||
t_free(t);
|
||||
free(t);
|
||||
}
|
||||
else
|
||||
tp = &t->next;
|
||||
|
@ -260,6 +262,7 @@ const trigger_type * tt)
|
|||
if (t->type == tt) {
|
||||
*tp = t->next;
|
||||
t_free(t);
|
||||
free(t);
|
||||
}
|
||||
else
|
||||
tp = &t->next;
|
||||
|
|
|
@ -23,11 +23,6 @@ void gamedata_init(gamedata *data, storage *store, int version) {
|
|||
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) {
|
||||
FILE *F = fopen(filename, mode);
|
||||
if (F) {
|
||||
|
@ -70,3 +65,9 @@ gamedata *gamedata_open(const char *filename, const char *mode, int version) {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void gamedata_close(gamedata *data) {
|
||||
gamedata_done(data);
|
||||
fstream_done(&data->strm);
|
||||
free(data->store);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,11 @@ static void test_gamedata(CuTest * tc)
|
|||
data = gamedata_open("test.dat", "wb", 0);
|
||||
CuAssertPtrNotNull(tc, data);
|
||||
gamedata_close(data);
|
||||
free(data);
|
||||
data = gamedata_open("test.dat", "rb", 0);
|
||||
CuAssertPtrNotNull(tc, data);
|
||||
gamedata_close(data);
|
||||
free(data);
|
||||
CuAssertIntEquals(tc, 0, remove("test.dat"));
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,6 @@ void addtoken(void ** root, const char *str, variant id)
|
|||
|
||||
ref = (tref *)malloc(sizeof(tref));
|
||||
ref->ucs = ucs;
|
||||
ref->node = 0;
|
||||
ref->node = node;
|
||||
ref->nexthash = tk->next[index];
|
||||
tk->next[index] = ref;
|
||||
|
|
Loading…
Reference in New Issue