Merge pull request #797 from ennorehling/automate

LERNE AUTO
This commit is contained in:
Enno Rehling 2018-08-18 12:44:24 +02:00 committed by GitHub
commit 34abedd642
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 491 additions and 88 deletions

2
clibs

@ -1 +1 @@
Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871
Subproject commit 66a891b383f1a51bb0d4e5cf002530f7f70bf7f4

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -86,6 +86,7 @@ ENDIF()
set (ERESSEA_SRC
vortex.c
automate.c
move.c
piracy.c
spells.c
@ -187,6 +188,7 @@ set(TESTS_SRC
tests.c
academy.test.c
alchemy.test.c
automate.test.c
battle.test.c
creport.test.c
direction.test.c

View File

@ -33,13 +33,13 @@ void academy_teaching_bonus(struct unit *u, skill_t sk, int students) {
}
}
bool academy_can_teach(unit *teacher, unit *student, skill_t sk) {
bool academy_can_teach(unit *teacher, unit *scholar, 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) * 2;
if (active_building(teacher, btype) && active_building(scholar, btype)) {
int j = study_cost(scholar, sk) * 2;
if (j < 50) j = 50;
/* kann Einheit das zahlen? */
return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j;
return get_pooled(scholar, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j;
/* sonst nehmen sie nicht am Unterricht teil */
}
return false;

View File

@ -9,7 +9,7 @@ extern "C" {
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);
bool academy_can_teach(struct unit *teacher, struct unit *scholar, skill_t sk);
#ifdef __cplusplus
}
#endif

161
src/automate.c Normal file
View File

@ -0,0 +1,161 @@
#include <platform.h>
#include "kernel/faction.h"
#include "kernel/messages.h"
#include "kernel/order.h"
#include "kernel/region.h"
#include "kernel/unit.h"
#include "util/log.h"
#include "automate.h"
#include "keyword.h"
#include "laws.h"
#include "study.h"
#include <stdlib.h>
#include <assert.h>
static int cmp_scholars(const void *lhs, const void *rhs)
{
const scholar *a = (const scholar *)lhs;
const scholar *b = (const scholar *)rhs;
if (a->sk == b->sk) {
/* sort by level, descending: */
return b->level - a->level;
}
/* order by skill */
return (int)a->sk - (int)b->sk;
}
int autostudy_init(scholar scholars[], int max_scholars, region *r)
{
unit *u;
int nscholars = 0;
for (u = r->units; u; u = u->next) {
keyword_t kwd = getkeyword(u->thisorder);
if (kwd == K_AUTOSTUDY) {
if (long_order_allowed(u) && unit_can_study(u)) {
scholar * st = scholars + nscholars;
if (++nscholars == max_scholars) {
log_fatal("you must increase MAXSCHOLARS");
}
st->u = u;
init_order(u->thisorder, u->faction->locale);
st->sk = getskill(u->faction->locale);
st->level = effskill_study(u, st->sk);
st->learn = 0;
}
else {
ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race",
u_race(u)));
}
}
}
if (nscholars > 0) {
qsort(scholars, nscholars, sizeof(scholar), cmp_scholars);
}
return nscholars;
}
static void teaching(scholar *s, int n) {
assert(n <= s->u->number);
s->learn += n;
s->u->flags |= UFL_LONGACTION;
}
static void learning(scholar *s, int n) {
assert(n <= s->u->number);
s->learn += n;
s->u->flags |= UFL_LONGACTION;
}
void autostudy_run(scholar scholars[], int nscholars)
{
int ti = 0;
while (ti != nscholars) {
skill_t sk = scholars[ti].sk;
int t, s, se, ts = 0, tt = 0, si = ti;
for (se = ti; se != nscholars && scholars[se].sk == sk; ++se) {
int mint;
ts += scholars[se].u->number; /* count total scholars */
mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */
for (; mint > tt && si != nscholars; ++si) {
tt += scholars[si].u->number;
}
}
/* now si splits the teachers and students 1:10 */
/* first student must be 2 levels below first teacher: */
for (; si != se && scholars[ti].level - TEACHDIFFERENCE > scholars[si].level; ++si) {
tt += scholars[si].u->number;
}
if (si == se) {
/* there are no students, so standard learning only */
for (t = ti; t != se; ++t) {
learning(scholars + t, scholars[t].u->number);
}
}
else {
/* invariant: unit ti can still teach i students */
int i = scholars[ti].u->number * STUDENTS_PER_TEACHER;
/* invariant: unit si has n students that can still be taught */
int n = scholars[si].u->number;
for (t = ti, s = si; t != si && s != se; ) {
if (i > n) {
/* t has more than enough teaching capacity for s */
i -= n;
teaching(scholars + s, n);
learning(scholars + s, scholars[s].u->number);
/* next student, please: */
if (++s == se) {
continue;
}
n = scholars[s].u->number;
}
else {
/* s gets partial credit and we need a new teacher */
teaching(scholars + s, i);
/* we are done with this teacher. any remaining people are regular learners: */
if (scholars[t].u->number > 1) {
/* remain = number - ceil(taught/10); */
int remain = (STUDENTS_PER_TEACHER * scholars[t].u->number - i + STUDENTS_PER_TEACHER - 1) / STUDENTS_PER_TEACHER;
learning(scholars + t, remain);
}
/* we want a new teacher for s. if any exists, it's next in the sequence. */
if (++t == si) {
continue;
}
if (scholars[t].level - TEACHDIFFERENCE < scholars[s].level) {
/* next teacher cannot teach, we must skip students. */
do {
learning(scholars + s, (n - i));
i = 0;
if (++s == se) {
continue;
}
n = scholars[s].u->number;
} while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level);
}
i = scholars[t].u->number * STUDENTS_PER_TEACHER;
}
}
}
ti = se;
}
}
#define MAXSCHOLARS 128
void do_autostudy(region *r)
{
scholar scholars[MAXSCHOLARS];
int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, r);
autostudy_run(scholars, nscholars);
for (i = 0; i != nscholars; ++i) {
int days = STUDYDAYS * scholars[i].learn;
learn_skill(scholars[i].u, scholars[i].sk, days);
}
}

43
src/automate.h Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright (c) 1998-2018, 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.
**/
#pragma once
#ifndef H_GC_AUTOMATE
#define H_GC_AUTOMATE
#include "skill.h"
struct region;
struct unit;
typedef struct scholar {
struct unit *u;
skill_t sk;
int level;
int learn;
} scholar;
#define STUDENTS_PER_TEACHER 10
void do_autostudy(struct region *r);
int autostudy_init(scholar scholars[], int max_scholars, struct region *r);
void autostudy_run(scholar scholars[], int nscholars);
#endif

114
src/automate.test.c Normal file
View File

@ -0,0 +1,114 @@
#ifdef _MSC_VER
#include <platform.h>
#endif
#include "automate.h"
#include "kernel/faction.h"
#include "kernel/order.h"
#include "kernel/region.h"
#include "kernel/unit.h"
#include "tests.h"
#include <CuTest.h>
static void test_autostudy_init(CuTest *tc) {
scholar scholars[4];
unit *u1, *u2, *u3;
faction *f;
region *r;
test_setup();
r = test_create_plain(0, 0);
f = test_create_faction(NULL);
u1 = test_create_unit(f, r);
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
test_create_unit(f, r);
u2 = test_create_unit(f, r);
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_level(u2, SK_ENTERTAINMENT, 2);
u3 = test_create_unit(f, r);
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
scholars[3].u = NULL;
CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r));
CuAssertPtrEquals(tc, u2, scholars[0].u);
CuAssertIntEquals(tc, 2, scholars[0].level);
CuAssertIntEquals(tc, 0, scholars[0].learn);
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk);
CuAssertPtrEquals(tc, u1, scholars[1].u);
CuAssertIntEquals(tc, 0, scholars[1].level);
CuAssertIntEquals(tc, 0, scholars[1].learn);
CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk);
CuAssertPtrEquals(tc, u3, scholars[2].u);
CuAssertIntEquals(tc, 0, scholars[2].level);
CuAssertIntEquals(tc, 0, scholars[2].learn);
CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk);
CuAssertPtrEquals(tc, NULL, scholars[3].u);
test_teardown();
}
static void test_autostudy_run(CuTest *tc) {
scholar scholars[4];
unit *u1, *u2, *u3;
faction *f;
region *r;
test_setup();
r = test_create_plain(0, 0);
f = test_create_faction(NULL);
u1 = test_create_unit(f, r);
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_number(u1, 2);
set_level(u1, SK_ENTERTAINMENT, 2);
u2 = test_create_unit(f, r);
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_number(u2, 10);
u3 = test_create_unit(f, r);
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
set_number(u3, 15);
scholars[3].u = NULL;
CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r));
autostudy_run(scholars, 3);
CuAssertIntEquals(tc, 0, scholars[0].learn);
CuAssertIntEquals(tc, 20, scholars[1].learn);
CuAssertIntEquals(tc, 15, scholars[2].learn);
test_teardown();
}
static void test_autostudy_run_noteachers(CuTest *tc) {
scholar scholars[4];
unit *u1, *u2, *u3;
faction *f;
region *r;
test_setup();
r = test_create_plain(0, 0);
f = test_create_faction(NULL);
u1 = test_create_unit(f, r);
u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]);
set_number(u1, 2);
set_level(u1, SK_ENTERTAINMENT, 2);
u2 = test_create_unit(f, r);
u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]);
set_number(u2, 10);
u3 = test_create_unit(f, r);
u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]);
set_number(u3, 15);
scholars[3].u = NULL;
CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r));
autostudy_run(scholars, 3);
CuAssertIntEquals(tc, 2, scholars[0].learn);
CuAssertIntEquals(tc, 10, scholars[1].learn);
CuAssertIntEquals(tc, 15, scholars[2].learn);
test_teardown();
}
CuSuite *get_automate_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_autostudy_init);
SUITE_ADD_TEST(suite, test_autostudy_run);
SUITE_ADD_TEST(suite, test_autostudy_run_noteachers);
return suite;
}

View File

@ -135,7 +135,8 @@ const char *parameters[MAXPARAMS] = {
"GRUPPE",
"PARTEITARNUNG",
"BAEUME",
"ALLIANZ"
"ALLIANZ",
"AUTO"
};
int findoption(const char *s, const struct locale *lang)

View File

@ -84,7 +84,7 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si
sbs_strcat(&sbs, str);
if (ord->id < 0) {
skill_t sk = (skill_t)(100+ord->id);
assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS);
assert((kwd == K_STUDY || kwd == K_AUTOSTUDY) && sk != SK_MAGIC && sk < MAXSKILLS);
str = skillname(sk, lang);
if (str) {
if (strchr(str, ' ') == NULL) {
@ -214,12 +214,12 @@ static int create_data(keyword_t kwd, const char *s,
order_data *data;
int id;
assert(kwd!=NOKEYWORD);
assert(kwd != NOKEYWORD);
if (!s || *s == 0) {
return 0;
}
if (kwd==K_STUDY) {
if (kwd == K_STUDY || kwd == K_AUTOSTUDY) {
const char * sptr = s;
skill_t sk = get_skill(parse_token_depr(&sptr), lang);
if (sk != SK_MAGIC && sk != NOSKILL) {
@ -332,6 +332,14 @@ order *parse_order(const char *s, const struct locale * lang)
sptr = sp;
}
}
else if (kwd == K_STUDY) {
const char *sp = sptr;
p = parse_token_depr(&sp);
if (p && isparam(p, lang, P_AUTO)) {
kwd = K_AUTOSTUDY;
sptr = sp;
}
}
if (kwd != NOKEYWORD) {
order *ord = (order *)malloc(sizeof(order));
create_order_i(ord, kwd, sptr, persistent, noerror, lang);
@ -366,6 +374,7 @@ bool is_repeated(keyword_t kwd)
case K_STEAL:
case K_SABOTAGE:
case K_STUDY:
case K_AUTOSTUDY:
case K_TEACH:
case K_GROW:
case K_PLANT:
@ -406,6 +415,7 @@ bool is_exclusive(const order * ord)
case K_STEAL:
case K_SABOTAGE:
case K_STUDY:
case K_AUTOSTUDY:
case K_TEACH:
case K_GROW:
case K_PLANT:
@ -447,6 +457,7 @@ bool is_long(keyword_t kwd)
case K_STEAL:
case K_SABOTAGE:
case K_STUDY:
case K_AUTOSTUDY:
case K_TEACH:
case K_GROW:
case K_PLANT:
@ -541,7 +552,7 @@ keyword_t init_order(const struct order *ord, const struct locale *lang)
assert(sk < MAXSKILLS);
assert(lang);
assert(kwd == K_STUDY);
assert(kwd == K_STUDY || kwd == K_AUTOSTUDY);
str = skillname(sk, lang);
if (strchr(str, ' ') == NULL) {
init_tokens_str(str);
@ -575,7 +586,7 @@ keyword_t init_order_depr(const struct order *ord)
{
if (ord) {
keyword_t kwd = ORD_KEYWORD(ord);
assert(kwd != K_STUDY);
assert(kwd != K_STUDY && kwd != K_AUTOSTUDY);
}
return init_order(ord, NULL);
}

View File

@ -113,6 +113,30 @@ static void test_parse_make(CuTest *tc) {
test_teardown();
}
static void test_parse_autostudy(CuTest *tc) {
char cmd[32];
order *ord;
struct locale * lang;
test_setup();
lang = get_or_create_locale("en");
locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment");
locale_setstring(lang, keyword(K_STUDY), "STUDY");
locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY");
locale_setstring(lang, parameters[P_AUTO], "AUTO");
init_locale(lang);
ord = parse_order("STUDY AUTO Entertainment", lang);
CuAssertPtrNotNull(tc, ord);
CuAssertIntEquals(tc, K_AUTOSTUDY, getkeyword(ord));
CuAssertStrEquals(tc, "AUTOSTUDY Entertainment", get_command(ord, lang, cmd, sizeof(cmd)));
CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang));
CuAssertStrEquals(tc, "Entertainment", getstrtoken());
free_order(ord);
test_teardown();
}
static void test_parse_make_temp(CuTest *tc) {
char cmd[32];
order *ord;
@ -130,7 +154,7 @@ static void test_parse_make_temp(CuTest *tc) {
CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord));
CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd)));
CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord));
CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang));
CuAssertStrEquals(tc, "herp", getstrtoken());
free_order(ord);
test_teardown();
@ -153,7 +177,7 @@ static void test_parse_maketemp(CuTest *tc) {
CuAssertPtrNotNull(tc, ord);
CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd)));
CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord));
CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord));
CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang));
CuAssertStrEquals(tc, "herp", getstrtoken());
free_order(ord);
test_teardown();
@ -167,7 +191,7 @@ static void test_init_order(CuTest *tc) {
lang = get_or_create_locale("en");
ord = create_order(K_MAKETEMP, lang, "hurr durr");
CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord));
CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang));
CuAssertStrEquals(tc, "hurr", getstrtoken());
CuAssertStrEquals(tc, "durr", getstrtoken());
free_order(ord);
@ -548,6 +572,7 @@ CuSuite *get_order_suite(void)
SUITE_ADD_TEST(suite, test_study_order_quoted);
SUITE_ADD_TEST(suite, test_parse_order);
SUITE_ADD_TEST(suite, test_parse_make);
SUITE_ADD_TEST(suite, test_parse_autostudy);
SUITE_ADD_TEST(suite, test_parse_make_temp);
SUITE_ADD_TEST(suite, test_parse_maketemp);
SUITE_ADD_TEST(suite, test_init_order);

View File

@ -130,6 +130,7 @@ typedef enum {
P_FACTIONSTEALTH,
P_TREES,
P_ALLIANCE,
P_AUTO,
MAXPARAMS,
NOPARAM
} param_t;

View File

@ -1310,13 +1310,12 @@ int eff_skill(const unit * u, const skill *sv, const region *r)
return 0;
}
int effskill_study(const unit * u, skill_t sk, const region * r)
int effskill_study(const unit * u, skill_t sk)
{
skill *sv = unit_skill(u, sk);
if (sv && sv->level > 0) {
int mlevel = sv->level;
if (!r) r = u->region;
mlevel += get_modifier(u, sv->id, sv->level, r, true);
mlevel += get_modifier(u, sv->id, sv->level, u->region, true);
if (mlevel > 0)
return mlevel;
}

View File

@ -163,7 +163,7 @@ extern "C" {
void clone_men(const struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */
int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r);
int effskill_study(const struct unit *u, skill_t sk, const struct region *r);
int effskill_study(const struct unit *u, skill_t sk);
int get_modifier(const struct unit *u, skill_t sk, int level,
const struct region *r, bool noitem);

View File

@ -149,5 +149,6 @@ const char *keywords[MAXKEYWORDS] = {
"promote",
"pay",
"loot",
"autostudy",
};

View File

@ -72,6 +72,7 @@ extern "C"
K_PROMOTION,
K_PAY,
K_LOOT,
K_AUTOSTUDY,
MAXKEYWORDS,
NOKEYWORD
} keyword_t;

View File

@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <modules/gmcmd.h>
#include "alchemy.h"
#include "automate.h"
#include "battle.h"
#include "economy.h"
#include "keyword.h"
@ -3636,6 +3637,24 @@ void add_proc_unit(int priority, void(*process) (unit *), const char *name)
}
}
bool long_order_allowed(const unit *u)
{
const region *r = u->region;
if (fval(u, UFL_LONGACTION)) {
/* this message was already given in laws.update_long_order
cmistake(u, ord, 52, MSG_PRODUCE);
*/
return false;
}
else if (fval(r->terrain, SEA_REGION)
&& u_race(u) != get_race(RC_AQUARIAN)
&& !(u_race(u)->flags & RCF_SWIM)) {
/* error message disabled by popular demand */
return false;
}
return true;
}
/* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */
void process(void)
{
@ -3705,16 +3724,7 @@ void process(void)
cmistake(u, ord, 224, MSG_MAGIC);
ord = NULL;
}
else if (fval(u, UFL_LONGACTION)) {
/* this message was already given in laws.update_long_order
cmistake(u, ord, 52, MSG_PRODUCE);
*/
ord = NULL;
}
else if (fval(r->terrain, SEA_REGION)
&& u_race(u) != get_race(RC_AQUARIAN)
&& !(u_race(u)->flags & RCF_SWIM)) {
/* error message disabled by popular demand */
else if (!long_order_allowed(u)) {
ord = NULL;
}
}
@ -4001,6 +4011,7 @@ void init_processor(void)
}
p += 10;
add_proc_region(p, do_autostudy, "study automation");
add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER,
"Lehren");
p += 10;
@ -4155,7 +4166,7 @@ void update_subscriptions(void)
/** determine if unit can be seen by faction
* @param f -- the observiong faction
* @param u -- the unit that is observed
* @param r -- the region that u is obesrved in (see below)
* @param r -- the region that u is obesrved from (see below)
* @param m -- terrain modifier to stealth
*
* r kann != u->region sein, wenn es um Durchreisen geht,

View File

@ -66,6 +66,7 @@ extern "C" {
void update_long_order(struct unit *u);
void sinkships(struct region * r);
void do_enter(struct region *r, bool is_final_attempt);
bool long_order_allowed(const struct unit *u);
int password_cmd(struct unit *u, struct order *ord);
int banner_cmd(struct unit *u, struct order *ord);

View File

@ -1759,6 +1759,34 @@ static void test_nmr_timeout(CuTest *tc) {
test_teardown();
}
static void test_long_orders(CuTest *tc) {
unit *u;
test_setup();
u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0));
CuAssertTrue(tc, long_order_allowed(u));
u->flags |= UFL_LONGACTION;
CuAssertTrue(tc, !long_order_allowed(u));
test_teardown();
}
static void test_long_order_on_ocean(CuTest *tc) {
unit *u;
race * rc;
test_setup();
rc = test_create_race("pikachu");
u = test_create_unit(test_create_faction(rc), test_create_ocean(0, 0));
CuAssertTrue(tc, !long_order_allowed(u));
rc->flags |= RCF_SWIM;
CuAssertTrue(tc, long_order_allowed(u));
rc = test_create_race("aquarian");
u = test_create_unit(test_create_faction(rc), u->region);
CuAssertTrue(tc, long_order_allowed(u));
test_teardown();
}
CuSuite *get_laws_suite(void)
{
CuSuite *suite = CuSuiteNew();
@ -1831,6 +1859,8 @@ CuSuite *get_laws_suite(void)
SUITE_ADD_TEST(suite, test_cansee_ring);
SUITE_ADD_TEST(suite, test_cansee_sphere);
SUITE_ADD_TEST(suite, test_nmr_timeout);
SUITE_ADD_TEST(suite, test_long_orders);
SUITE_ADD_TEST(suite, test_long_order_on_ocean);
return suite;
}

View File

@ -69,7 +69,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define TEACH_ALL 1
#define TEACH_FRIENDS
static skill_t getskill(const struct locale *lang)
skill_t getskill(const struct locale *lang)
{
char token[128];
const char * s = gettoken(token, sizeof(token));
@ -195,37 +195,37 @@ const attrib_type at_learning = {
#define EXPERIENCEDAYS 10
static int study_days(unit * student, skill_t sk)
static int study_days(unit * scholar, skill_t sk)
{
int speed = STUDYDAYS;
if (u_race(student)->study_speed) {
speed += u_race(student)->study_speed[sk];
if (u_race(scholar)->study_speed) {
speed += u_race(scholar)->study_speed[sk];
if (speed < STUDYDAYS) {
skill *sv = unit_skill(student, sk);
skill *sv = unit_skill(scholar, sk);
if (sv == 0) {
speed = STUDYDAYS;
}
}
}
return student->number * speed;
return scholar->number * speed;
}
static int
teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk,
bool report, int *academy_students)
{
teaching_info *teach = NULL;
attrib *a;
int students;
if (magic_lowskill(student)) {
if (magic_lowskill(scholar)) {
cmistake(teacher, teacher->thisorder, 292, MSG_EVENT);
return 0;
}
students = student->number;
students = scholar->number;
/* subtract already taught students */
a = a_find(student->attribs, &at_learning);
a = a_find(scholar->attribs, &at_learning);
if (a != NULL) {
teach = (teaching_info *)a->data.v;
students -= teach->students;
@ -235,18 +235,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk,
if (students > 0) {
if (teach == NULL) {
a = a_add(&student->attribs, a_new(&at_learning));
a = a_add(&scholar->attribs, a_new(&at_learning));
teach = (teaching_info *)a->data.v;
}
selist_push(&teach->teachers, teacher);
teach->days += students * STUDYDAYS;
teach->students += students;
if (student->building && teacher->building == student->building) {
if (scholar->building && teacher->building == scholar->building) {
/* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und
* Student auch in unterschiedlichen Gebaeuden stehen duerfen */
/* FIXME comment contradicts implementation */
if (academy_can_teach(teacher, student, sk)) {
if (academy_can_teach(teacher, scholar, sk)) {
/* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */
teach->days += students * EXPERIENCEDAYS; /* learning erhoehen */
/* Lehrer zusaetzlich +1 Tag pro Schueler. */
@ -304,7 +304,7 @@ int teach_cmd(unit * teacher, struct order *ord)
#if TEACH_ALL
if (getparam(teacher->faction->locale) == P_ANY) {
skill_t sk;
unit *student;
unit *scholar;
skill_t teachskill[MAXSKILLS];
int t = 0;
@ -313,15 +313,15 @@ int teach_cmd(unit * teacher, struct order *ord)
teachskill[t] = getskill(teacher->faction->locale);
} while (sk != NOSKILL);
for (student = r->units; teaching > 0 && student; student = student->next) {
if (LongHunger(student)) {
for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) {
if (LongHunger(scholar)) {
continue;
}
else if (student->faction == teacher->faction) {
if (getkeyword(student->thisorder) == K_STUDY) {
else if (scholar->faction == teacher->faction) {
if (getkeyword(scholar->thisorder) == K_STUDY) {
/* Input ist nun von student->thisorder !! */
init_order(student->thisorder, student->faction->locale);
sk = getskill(student->faction->locale);
init_order(scholar->thisorder, scholar->faction->locale);
sk = getskill(scholar->faction->locale);
if (sk != NOSKILL && teachskill[0] != NOSKILL) {
for (t = 0; teachskill[t] != NOSKILL; ++t) {
if (sk == teachskill[t]) {
@ -331,20 +331,20 @@ int teach_cmd(unit * teacher, struct order *ord)
sk = teachskill[t];
}
if (sk != NOSKILL
&& effskill_study(teacher, sk, 0) - TEACHDIFFERENCE > effskill_study(student, sk, 0)) {
teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students);
&& effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) {
teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students);
}
}
}
#ifdef TEACH_FRIENDS
else if (alliedunit(teacher, student->faction, HELP_GUARD)) {
if (getkeyword(student->thisorder) == K_STUDY) {
else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) {
if (getkeyword(scholar->thisorder) == K_STUDY) {
/* Input ist nun von student->thisorder !! */
init_order(student->thisorder, student->faction->locale);
sk = getskill(student->faction->locale);
init_order(scholar->thisorder, scholar->faction->locale);
sk = getskill(scholar->faction->locale);
if (sk != NOSKILL
&& effskill_study(teacher, sk, 0) - TEACHDIFFERENCE >= effskill(student, sk, 0)) {
teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students);
&& effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) {
teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students);
}
}
}
@ -363,15 +363,15 @@ int teach_cmd(unit * teacher, struct order *ord)
while (!parser_end()) {
skill_t sk;
unit *student;
unit *scholar;
bool feedback;
getunit(r, teacher->faction, &student);
getunit(r, teacher->faction, &scholar);
++count;
/* Falls die Unit nicht gefunden wird, Fehler melden */
if (!student) {
if (!scholar) {
char tbuf[20];
const char *uid;
const char *token;
@ -403,8 +403,8 @@ int teach_cmd(unit * teacher, struct order *ord)
continue;
}
feedback = teacher->faction == student->faction
|| alliedunit(student, teacher->faction, HELP_GUARD);
feedback = teacher->faction == scholar->faction
|| alliedunit(scholar, teacher->faction, HELP_GUARD);
/* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in
* ihre neuen Nummern uebersetzt. */
@ -412,31 +412,31 @@ int teach_cmd(unit * teacher, struct order *ord)
strncat(zOrder, " ", sz - 1);
--sz;
}
sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(student->no), sz);
sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz);
if (getkeyword(student->thisorder) != K_STUDY) {
if (getkeyword(scholar->thisorder) != K_STUDY) {
ADDMSG(&teacher->faction->msgs,
msg_feedback(teacher, ord, "teach_nolearn", "student", student));
msg_feedback(teacher, ord, "teach_nolearn", "student", scholar));
continue;
}
/* Input ist nun von student->thisorder !! */
parser_pushstate();
init_order(student->thisorder, student->faction->locale);
sk = getskill(student->faction->locale);
init_order(scholar->thisorder, scholar->faction->locale);
sk = getskill(scholar->faction->locale);
parser_popstate();
if (sk == NOSKILL) {
ADDMSG(&teacher->faction->msgs,
msg_feedback(teacher, ord, "teach_nolearn", "student", student));
msg_feedback(teacher, ord, "teach_nolearn", "student", scholar));
continue;
}
if (effskill_study(student, sk, 0) > effskill_study(teacher, sk, 0)
if (effskill_study(scholar, sk) > effskill_study(teacher, sk)
- TEACHDIFFERENCE) {
if (feedback) {
ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood",
"student", student));
"student", scholar));
}
continue;
}
@ -444,18 +444,18 @@ int teach_cmd(unit * teacher, struct order *ord)
/* ist der Magier schon spezialisiert, so versteht er nur noch
* Lehrer seines Gebietes */
sc_mage *mage1 = get_mage_depr(teacher);
sc_mage *mage2 = get_mage_depr(student);
sc_mage *mage2 = get_mage_depr(scholar);
if (mage2 && mage1 && mage2->magietyp != M_GRAY
&& mage1->magietyp != mage2->magietyp) {
if (feedback) {
ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord,
"error_different_magic", "target", student));
"error_different_magic", "target", scholar));
}
continue;
}
}
sk_academy = sk;
teaching -= teach_unit(teacher, student, teaching, sk, false, &academy_students);
teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students);
}
new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder);
replace_order(&teacher->orders, ord, new_order);
@ -766,9 +766,6 @@ int study_cmd(unit * u, order * ord)
days *= 2;
}
if (fval(u, UFL_HUNGER))
days /= 2;
learn_skill(u, sk, days);
if (a != NULL) {
if (teach->teachers) {
@ -832,6 +829,9 @@ void learn_skill(unit *u, skill_t sk, int days) {
int leveldays = STUDYDAYS * u->number;
int weeks = 0;
if (fval(u, UFL_HUNGER)) {
days /= 2;
}
assert(sk >= 0 && sk < MAXSKILLS);
if (inject_learn_fun) {
inject_learn_fun(u, sk, days);

View File

@ -33,6 +33,7 @@ extern "C" {
int study_cmd(struct unit *u, struct order *ord);
magic_t getmagicskill(const struct locale *lang);
skill_t getskill(const struct locale *lang);
bool is_migrant(struct unit *u);
int study_cost(struct unit *u, skill_t talent);

View File

@ -89,15 +89,15 @@ static void setup_teacher(study_fixture *fix, skill_t sk) {
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]);
fix->u->thisorder = create_order(K_STUDY, f->locale, skillnames[sk]);
fix->teachers[0] = test_create_unit(f, r);
assert(fix->teachers[0]);
fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no));
fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no));
fix->teachers[1] = test_create_unit(f, r);
assert(fix->teachers[1]);
fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no));
fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no));
test_clear_messages(f);
}
@ -110,7 +110,7 @@ static void test_study_no_teacher(CuTest *tc) {
CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW));
CuAssertIntEquals(tc, 1, sv->level);
CuAssertIntEquals(tc, 2, sv->weeks);
CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs));
CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs));
test_teardown();
}
@ -121,7 +121,7 @@ static void test_study_with_teacher(CuTest *tc) {
setup_teacher(&fix, SK_CROSSBOW);
set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE);
teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder);
CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs));
CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs));
study_cmd(fix.u, fix.u->thisorder);
CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW));
CuAssertIntEquals(tc, 1, sv->level);
@ -288,7 +288,7 @@ static void test_academy_bonus(CuTest *tc) {
u1 = test_create_unit(u->faction, u->region);
u3 = test_create_unit(u->faction, u->region);
u0->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u3->no), itoa36(u1->no));
u->thisorder = create_order(K_TEACH, loc, "%s", itoa36(u1->no));
u->thisorder = create_order(K_TEACH, loc, itoa36(u1->no));
u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]);
u3->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]);
@ -405,7 +405,7 @@ static void test_study_magic(CuTest *tc) {
f = test_create_faction(NULL);
lang = f->locale;
u = test_create_unit(f, test_create_region(0, 0, NULL));
u->thisorder = create_order(K_STUDY, lang, "%s", skillnames[SK_MAGIC]);
u->thisorder = create_order(K_STUDY, lang, skillnames[SK_MAGIC]);
itype = test_create_silver();
CuAssertIntEquals(tc, -1, study_cmd(u, u->thisorder));
@ -423,7 +423,7 @@ static void test_study_magic(CuTest *tc) {
CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet);
CuAssertIntEquals(tc, 0, i_get(u->items, itype));
CuAssertPtrNotNull(tc, get_mage_depr(u));
CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65"));
CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "error65"));
CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp);
test_teardown();
@ -491,12 +491,12 @@ static void test_teach_magic(CuTest *tc) {
f = test_create_faction(NULL);
f->magiegebiet = M_GWYRRD;
u = test_create_unit(f, test_create_region(0, 0, NULL));
u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[SK_MAGIC]);
u->thisorder = create_order(K_STUDY, f->locale, skillnames[SK_MAGIC]);
i_change(&u->items, itype, study_cost(u, SK_MAGIC));
ut = test_create_unit(f, u->region);
set_level(ut, SK_MAGIC, TEACHDIFFERENCE);
create_mage(ut, M_GWYRRD);
ut->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(u->no));
ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no));
learn_inject();
teach_cmd(ut, ut->thisorder);
study_cmd(u, u->thisorder);

View File

@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[])
ADD_SUITE(xerewards);
/* kernel */
ADD_SUITE(academy);
ADD_SUITE(alchemy);
ADD_SUITE(alliance);
ADD_SUITE(ally);
ADD_SUITE(building);
@ -121,6 +120,8 @@ int RunAllTests(int argc, char *argv[])
ADD_SUITE(spells);
ADD_SUITE(unit);
/* gamecode */
ADD_SUITE(alchemy);
ADD_SUITE(automate);
ADD_SUITE(battle);
ADD_SUITE(calendar);
ADD_SUITE(creport);

BIN
src/util/.DS_Store vendored Normal file

Binary file not shown.