forked from github/server
Merge branch 'monster_leftovers' of https://github.com/stm2/server into stm2-monster_leftovers
Conflicts: src/kernel/unit.c src/laws.test.c src/monsters.c
This commit is contained in:
commit
c85e489f4f
|
@ -883,7 +883,7 @@
|
||||||
</race>
|
</race>
|
||||||
|
|
||||||
<race name="seaserpent" magres="0.500000" maxaura="1.0" regaura="1.0" weight="20000" capacity="5000" speed="1.0" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes">
|
<race name="seaserpent" magres="0.500000" maxaura="1.0" regaura="1.0" weight="20000" capacity="5000" speed="1.0" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes">
|
||||||
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes"/>
|
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes" moveattack="yes"/>
|
||||||
<function name="name" value="namegeneric"/>
|
<function name="name" value="namegeneric"/>
|
||||||
<function name="move" value="moveswimming"/>
|
<function name="move" value="moveswimming"/>
|
||||||
<skill name="tactics" modifier="4"/>
|
<skill name="tactics" modifier="4"/>
|
||||||
|
|
|
@ -1172,7 +1172,7 @@
|
||||||
<attack type="1" damage="1d1"/>
|
<attack type="1" damage="1d1"/>
|
||||||
</race>
|
</race>
|
||||||
<race name="seaserpent" magres="0.500000" maxaura="1.000000" regaura="1.000000" recruitcost="5000" weight="20000" capacity="5000" speed="1.000000" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes" unarmedguard="yes">
|
<race name="seaserpent" magres="0.500000" maxaura="1.000000" regaura="1.000000" recruitcost="5000" weight="20000" capacity="5000" speed="1.000000" hp="600" ac="3" damage="2d15" unarmedattack="0" unarmeddefense="0" attackmodifier="4" defensemodifier="4" scarepeasants="yes" swim="yes" teach="no" getitem="yes" resistbash="yes" unarmedguard="yes">
|
||||||
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes"/>
|
<ai splitsize="6" killpeasants="yes" moverandom="yes" learn="yes" moveattack="yes"/>
|
||||||
<function name="name" value="namegeneric"/>
|
<function name="name" value="namegeneric"/>
|
||||||
<function name="move" value="moveswimming"/>
|
<function name="move" value="moveswimming"/>
|
||||||
<skill name="tactics" modifier="4"/>
|
<skill name="tactics" modifier="4"/>
|
||||||
|
|
|
@ -199,6 +199,7 @@ set(TESTS_SRC
|
||||||
laws.test.c
|
laws.test.c
|
||||||
magic.test.c
|
magic.test.c
|
||||||
market.test.c
|
market.test.c
|
||||||
|
monsters.test.c
|
||||||
move.test.c
|
move.test.c
|
||||||
piracy.test.c
|
piracy.test.c
|
||||||
prefix.test.c
|
prefix.test.c
|
||||||
|
|
|
@ -214,6 +214,7 @@ extern "C" {
|
||||||
#define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */
|
#define RCF_SHIPSPEED (1<<26) /* race gets +1 on shipspeed */
|
||||||
#define RCF_STONEGOLEM (1<<27) /* race gets stonegolem properties */
|
#define RCF_STONEGOLEM (1<<27) /* race gets stonegolem properties */
|
||||||
#define RCF_IRONGOLEM (1<<28) /* race gets irongolem properties */
|
#define RCF_IRONGOLEM (1<<28) /* race gets irongolem properties */
|
||||||
|
#define RCF_ATTACK_MOVED (1<<29) /* may attack if it has moved */
|
||||||
|
|
||||||
/* Economic flags */
|
/* Economic flags */
|
||||||
#define ECF_KEEP_ITEM (1<<1) /* gibt Gegenstände weg */
|
#define ECF_KEEP_ITEM (1<<1) /* gibt Gegenstände weg */
|
||||||
|
|
|
@ -28,6 +28,7 @@ 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"
|
||||||
|
@ -56,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
#include <util/lists.h>
|
#include <util/lists.h>
|
||||||
#include <util/log.h>
|
#include <util/log.h>
|
||||||
#include <util/parser.h>
|
#include <util/parser.h>
|
||||||
|
#include <util/rand.h>
|
||||||
#include <util/resolve.h>
|
#include <util/resolve.h>
|
||||||
#include <util/rng.h>
|
#include <util/rng.h>
|
||||||
#include <util/variant.h>
|
#include <util/variant.h>
|
||||||
|
@ -1125,10 +1127,11 @@ 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 chance)
|
bool learn_skill(unit * u, skill_t sk, double learn_chance)
|
||||||
{
|
{
|
||||||
skill *sv = u->skills;
|
skill *sv = u->skills;
|
||||||
if (chance < 1.0 && rng_int() % 10000 >= chance * 10000)
|
if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000)
|
||||||
|
if (!chance(learn_chance))
|
||||||
return false;
|
return false;
|
||||||
while (sv != u->skills + u->skill_size) {
|
while (sv != u->skills + u->skill_size) {
|
||||||
assert(sv->weeks > 0);
|
assert(sv->weeks > 0);
|
||||||
|
@ -1891,7 +1894,7 @@ static double produceexp_chance(void) {
|
||||||
|
|
||||||
void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double))
|
void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double))
|
||||||
{
|
{
|
||||||
if (n != 0 && playerrace(u_race(u))) {
|
if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) {
|
||||||
double chance = produceexp_chance();
|
double chance = produceexp_chance();
|
||||||
if (chance > 0.0F) {
|
if (chance > 0.0F) {
|
||||||
learn(u, sk, (n * chance) / u->number);
|
learn(u, sk, (n * chance) / u->number);
|
||||||
|
|
|
@ -1604,6 +1604,8 @@ static void parse_ai(race * rc, xmlNodePtr node)
|
||||||
rc->flags |= RCF_MOVERANDOM;
|
rc->flags |= RCF_MOVERANDOM;
|
||||||
if (xml_bvalue(node, "learn", false))
|
if (xml_bvalue(node, "learn", false))
|
||||||
rc->flags |= RCF_LEARN;
|
rc->flags |= RCF_LEARN;
|
||||||
|
if (xml_bvalue(node, "moveattack", false))
|
||||||
|
rc->flags |= RCF_ATTACK_MOVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_races(xmlDocPtr doc)
|
static int parse_races(xmlDocPtr doc)
|
||||||
|
|
|
@ -657,7 +657,7 @@ static void test_unarmed_races_can_guard(CuTest *tc) {
|
||||||
|
|
||||||
setup_guard(&fix, false);
|
setup_guard(&fix, false);
|
||||||
rc = rc_get_or_create(fix.u->_race->_name);
|
rc = rc_get_or_create(fix.u->_race->_name);
|
||||||
rc->flags |= RCF_UNARMEDGUARD;
|
fset(rc, RCF_UNARMEDGUARD);
|
||||||
CuAssertIntEquals(tc, E_GUARD_OK, can_start_guarding(fix.u));
|
CuAssertIntEquals(tc, E_GUARD_OK, can_start_guarding(fix.u));
|
||||||
update_guards();
|
update_guards();
|
||||||
CuAssertTrue(tc, fval(fix.u, UFL_GUARD));
|
CuAssertTrue(tc, fval(fix.u, UFL_GUARD));
|
||||||
|
|
|
@ -69,7 +69,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
bool monster_is_waiting(const unit * u)
|
bool monster_is_waiting(const unit * u)
|
||||||
{
|
{
|
||||||
if (fval(u, UFL_ISNEW | UFL_MOVED))
|
int test = fval(u_race(u), RCF_ATTACK_MOVED) ? UFL_ISNEW : UFL_ISNEW | UFL_MOVED;
|
||||||
|
if (fval(u, test))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define MOVECHANCE 25 /* chance fuer bewegung */
|
#define MOVECHANCE .25 /* chance fuer bewegung */
|
||||||
#define DRAGON_RANGE 20 /* Max. Distanz zum nächsten Drachenziel */
|
#define DRAGON_RANGE 20 /* Max. Distanz zum nächsten Drachenziel */
|
||||||
#define MAXILLUSION_TEXTS 3
|
#define MAXILLUSION_TEXTS 3
|
||||||
|
|
||||||
|
@ -83,6 +83,10 @@ static void give_peasants(unit *u, const item_type *itype, int reduce) {
|
||||||
unit_addorder(u, parse_order(buf, u->faction->locale));
|
unit_addorder(u, parse_order(buf, u->faction->locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double random_move_chance(void) {
|
||||||
|
return config_get_flt("rules.monsters.random_move_chance", MOVECHANCE);
|
||||||
|
}
|
||||||
|
|
||||||
static void reduce_weight(unit * u)
|
static void reduce_weight(unit * u)
|
||||||
{
|
{
|
||||||
int capacity, weight = 0;
|
int capacity, weight = 0;
|
||||||
|
@ -155,9 +159,30 @@ static order *monster_attack(unit * u, const unit * target)
|
||||||
return create_order(K_ATTACK, u->faction->locale, "%i", target->no);
|
return create_order(K_ATTACK, u->faction->locale, "%i", target->no);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int monster_attacks(unit * monster, bool respect_buildings, bool rich_only)
|
||||||
|
{
|
||||||
|
region *r = monster->region;
|
||||||
|
unit *u2;
|
||||||
|
int money = 0;
|
||||||
|
|
||||||
|
for (u2 = r->units; u2; u2 = u2->next) {
|
||||||
|
if (u2->faction != monster->faction && cansee(monster->faction, r, u2, 0) && !in_safe_building(u2, monster)) {
|
||||||
|
int m = get_money(u2);
|
||||||
|
if (!rich_only || m > 0) {
|
||||||
|
order *ord = monster_attack(monster, u2);
|
||||||
|
if (ord) {
|
||||||
|
addlist(&monster->orders, ord);
|
||||||
|
money += m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return money;
|
||||||
|
}
|
||||||
|
|
||||||
static order *get_money_for_dragon(region * r, unit * udragon, int wanted)
|
static order *get_money_for_dragon(region * r, unit * udragon, int wanted)
|
||||||
{
|
{
|
||||||
int n;
|
int money;
|
||||||
bool attacks = attack_chance > 0.0;
|
bool attacks = attack_chance > 0.0;
|
||||||
|
|
||||||
/* falls genug geld in der region ist, treiben wir steuern ein. */
|
/* falls genug geld in der region ist, treiben wir steuern ein. */
|
||||||
|
@ -171,26 +196,14 @@ static order *get_money_for_dragon(region * r, unit * udragon, int wanted)
|
||||||
|
|
||||||
/* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an
|
/* falls der drache launisch ist, oder das regionssilber knapp, greift er alle an
|
||||||
* und holt sich Silber von Einheiten, vorausgesetzt er bewacht bereits */
|
* und holt sich Silber von Einheiten, vorausgesetzt er bewacht bereits */
|
||||||
n = 0;
|
money = 0;
|
||||||
if (attacks && is_guard(udragon, GUARD_TAX)) {
|
if (attacks && is_guard(udragon, GUARD_TAX)) {
|
||||||
unit *u;
|
money += monster_attacks(udragon, true, true);
|
||||||
for (u = r->units; u; u = u->next) {
|
|
||||||
if (u->faction != udragon->faction && cansee(udragon->faction, r, u, 0) && !in_safe_building(u, udragon)) {
|
|
||||||
int m = get_money(u);
|
|
||||||
if (m != 0) {
|
|
||||||
order *ord = monster_attack(udragon, u);
|
|
||||||
if (ord) {
|
|
||||||
addlist(&udragon->orders, ord);
|
|
||||||
n += m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* falls die einnahmen erreicht werden, bleibt das monster noch eine */
|
/* falls die einnahmen erreicht werden, bleibt das monster noch eine */
|
||||||
/* runde hier. */
|
/* runde hier. */
|
||||||
if (n + rmoney(r) >= wanted) {
|
if (money + rmoney(r) >= wanted) {
|
||||||
return create_order(K_LOOT, default_locale, NULL);
|
return create_order(K_LOOT, default_locale, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,10 +410,10 @@ static int dragon_affinity_value(region * r, unit * u)
|
||||||
int m = all_money(r, u->faction);
|
int m = all_money(r, u->faction);
|
||||||
|
|
||||||
if (u_race(u) == get_race(RC_FIREDRAGON)) {
|
if (u_race(u) == get_race(RC_FIREDRAGON)) {
|
||||||
return (int)(normalvariate(m, m / 2));
|
return dice(4, m / 2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (int)(normalvariate(m, m / 4));
|
return dice(6, m / 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,21 +549,6 @@ static order *monster_seeks_target(region * r, unit * u)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void monster_attacks(unit * monster)
|
|
||||||
{
|
|
||||||
region *r = monster->region;
|
|
||||||
unit *u;
|
|
||||||
|
|
||||||
for (u = r->units; u; u = u->next) {
|
|
||||||
if (u->faction != monster->faction && cansee(monster->faction, r, u, 0) && !in_safe_building(u, monster)) {
|
|
||||||
order *ord = monster_attack(monster, u);
|
|
||||||
if (ord) {
|
|
||||||
addlist(&monster->orders, ord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *random_growl(void)
|
static const char *random_growl(void)
|
||||||
{
|
{
|
||||||
switch (rng_int() % 5) {
|
switch (rng_int() % 5) {
|
||||||
|
@ -742,9 +740,10 @@ static order *plan_dragon(unit * u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (long_order == NULL) {
|
if (long_order == NULL) {
|
||||||
|
int attempts = 0;
|
||||||
skill_t sk = SK_PERCEPTION;
|
skill_t sk = SK_PERCEPTION;
|
||||||
/* study perception (or a random useful skill) */
|
/* study perception (or a random useful skill) */
|
||||||
while (!skill_enabled(sk) || u_race(u)->bonus[sk] < -5) {
|
while ((!skill_enabled(sk) || (attempts < MAXSKILLS && u_race(u)->bonus[sk] < (++attempts < 10?1:-5 )))) {
|
||||||
sk = (skill_t)(rng_int() % MAXSKILLS);
|
sk = (skill_t)(rng_int() % MAXSKILLS);
|
||||||
}
|
}
|
||||||
long_order = create_order(K_STUDY, u->faction->locale, "'%s'",
|
long_order = create_order(K_STUDY, u->faction->locale, "'%s'",
|
||||||
|
@ -763,7 +762,7 @@ void plan_monsters(faction * f)
|
||||||
|
|
||||||
for (r = regions; r; r = r->next) {
|
for (r = regions; r; r = r->next) {
|
||||||
unit *u;
|
unit *u;
|
||||||
bool attacking = false;
|
bool attacking = chance(attack_chance);
|
||||||
|
|
||||||
for (u = r->units; u; u = u->next) {
|
for (u = r->units; u; u = u->next) {
|
||||||
attrib *ta;
|
attrib *ta;
|
||||||
|
@ -777,20 +776,17 @@ void plan_monsters(faction * f)
|
||||||
free_orders(&u->orders);
|
free_orders(&u->orders);
|
||||||
if (skill_enabled(SK_PERCEPTION)) {
|
if (skill_enabled(SK_PERCEPTION)) {
|
||||||
/* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */
|
/* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */
|
||||||
/* TODO: this only works for playerrace */
|
|
||||||
produceexp(u, SK_PERCEPTION, u->number);
|
produceexp(u, SK_PERCEPTION, u->number);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attacking) {
|
|
||||||
if (chance(attack_chance)) attacking = true;
|
|
||||||
}
|
|
||||||
if (u->status > ST_BEHIND) {
|
if (u->status > ST_BEHIND) {
|
||||||
setstatus(u, ST_FIGHT);
|
setstatus(u, ST_FIGHT);
|
||||||
/* all monsters fight */
|
/* all monsters fight */
|
||||||
}
|
}
|
||||||
if (attacking && is_guard(u, GUARD_TAX)) {
|
if (attacking && (!r->land || is_guard(u, GUARD_TAX))) {
|
||||||
monster_attacks(u);
|
monster_attacks(u, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* units with a plan to kill get ATTACK orders: */
|
/* units with a plan to kill get ATTACK orders: */
|
||||||
ta = a_find(u->attribs, &at_hate);
|
ta = a_find(u->attribs, &at_hate);
|
||||||
if (ta && !monster_is_waiting(u)) {
|
if (ta && !monster_is_waiting(u)) {
|
||||||
|
@ -825,7 +821,7 @@ void plan_monsters(faction * f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (u_race(u)->flags & RCF_MOVERANDOM) {
|
else if (u_race(u)->flags & RCF_MOVERANDOM) {
|
||||||
if (rng_int() % 100 < MOVECHANCE || check_overpopulated(u)) {
|
if (chance(random_move_chance()) || check_overpopulated(u)) {
|
||||||
long_order = monster_move(r, u);
|
long_order = monster_move(r, u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
#include <platform.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <kernel/config.h>
|
||||||
|
#include <kernel/faction.h>
|
||||||
|
#include <kernel/item.h>
|
||||||
|
#include <kernel/order.h>
|
||||||
|
#include <kernel/race.h>
|
||||||
|
#include <kernel/region.h>
|
||||||
|
#include <kernel/terrain.h>
|
||||||
|
#include <kernel/unit.h>
|
||||||
|
|
||||||
|
#include "monster.h"
|
||||||
|
#include "guard.h"
|
||||||
|
#include "skill.h"
|
||||||
|
|
||||||
|
#include <util/language.h>
|
||||||
|
#include <util/language_struct.h>
|
||||||
|
|
||||||
|
#include <CuTest.h>
|
||||||
|
#include <tests.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern void plan_monsters(struct faction *f);
|
||||||
|
extern int monster_attacks(unit * monster, bool respect_buildings, bool rich_only);
|
||||||
|
|
||||||
|
static void init_language(void)
|
||||||
|
{
|
||||||
|
locale* lang;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lang = get_or_create_locale("de");
|
||||||
|
locale_setstring(lang, "skill::unarmed", "Waffenloser Kampf");
|
||||||
|
locale_setstring(lang, "keyword::attack", "ATTACKIERE");
|
||||||
|
locale_setstring(lang, "keyword::study", "LERNE");
|
||||||
|
locale_setstring(lang, "keyword::tax", "TREIBE");
|
||||||
|
locale_setstring(lang, "keyword::loot", "PLUENDERE");
|
||||||
|
locale_setstring(lang, "keyword::guard", "BEWACHE");
|
||||||
|
locale_setstring(lang, "keyword::move", "NACH");
|
||||||
|
locale_setstring(lang, "keyword::message", "BOTSCHAFT");
|
||||||
|
locale_setstring(lang, "REGION", "REGION");
|
||||||
|
locale_setstring(lang, "east", "O");
|
||||||
|
|
||||||
|
for (i = 0; i < MAXKEYWORDS; ++i) {
|
||||||
|
if (!locale_getstring(lang, mkname("keyword", keywords[i])))
|
||||||
|
locale_setstring(lang, mkname("keyword", keywords[i]), keywords[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAXSKILLS; ++i) {
|
||||||
|
if (!locale_getstring(lang, mkname("skill", skillnames[i])))
|
||||||
|
locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]);
|
||||||
|
}
|
||||||
|
init_keywords(lang);
|
||||||
|
init_skills(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
static order *find_order(const char *expected, const unit *unit)
|
||||||
|
{
|
||||||
|
char cmd[32];
|
||||||
|
order *order;
|
||||||
|
for (order = unit->orders; order; order = order->next) {
|
||||||
|
if (strcmp(expected, get_command(order, cmd, sizeof(cmd))) == 0) {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_monsters(faction **player, faction **monsters, region **r, unit **u, unit **m) {
|
||||||
|
race* rc;
|
||||||
|
|
||||||
|
test_cleanup();
|
||||||
|
|
||||||
|
init_language();
|
||||||
|
|
||||||
|
test_create_world();
|
||||||
|
*player = test_create_faction(NULL);
|
||||||
|
*monsters = get_or_create_monsters();
|
||||||
|
assert(rc_find((*monsters)->race->_name));
|
||||||
|
rc = rc_get_or_create((*monsters)->race->_name);
|
||||||
|
fset(rc, RCF_UNARMEDGUARD);
|
||||||
|
fset(rc, RCF_NPC);
|
||||||
|
fset(*monsters, FFL_NOIDLEOUT);
|
||||||
|
assert(fval(*monsters, FFL_NPC) && fval((*monsters)->race, RCF_UNARMEDGUARD) && fval((*monsters)->race, RCF_NPC) && fval(*monsters, FFL_NOIDLEOUT));
|
||||||
|
(*monsters)->locale = default_locale;
|
||||||
|
|
||||||
|
*r = findregion(0, 0);
|
||||||
|
|
||||||
|
*u = test_create_unit(*player, *r);
|
||||||
|
unit_setid(*u, 1);
|
||||||
|
*m = test_create_unit(*monsters, *r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_monsters_attack(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
|
||||||
|
guard(m, GUARD_TAX);
|
||||||
|
|
||||||
|
config_set("rules.monsters.attack_chance", "1");
|
||||||
|
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_monsters_attack_ocean(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
r = findregion(-1, 0);
|
||||||
|
u = test_create_unit(u->faction, r);
|
||||||
|
unit_setid(u, 2);
|
||||||
|
m = test_create_unit(m->faction, r);
|
||||||
|
assert(!m->region->land);
|
||||||
|
|
||||||
|
config_set("rules.monsters.attack_chance", "1");
|
||||||
|
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_monsters_waiting(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
guard(m, GUARD_TAX);
|
||||||
|
fset(m, UFL_ISNEW);
|
||||||
|
monster_attacks(m, false, false);
|
||||||
|
CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_seaserpent_attack(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
race *rc;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
r = findregion(-1, 0);
|
||||||
|
u = test_create_unit(u->faction, r);
|
||||||
|
unit_setid(u, 2);
|
||||||
|
m = test_create_unit(m->faction, r);
|
||||||
|
u_setrace(m, rc = test_create_race("seaserpent"));
|
||||||
|
assert(!m->region->land);
|
||||||
|
fset(m, UFL_MOVED);
|
||||||
|
fset(rc, RCF_ATTACK_MOVED);
|
||||||
|
|
||||||
|
config_set("rules.monsters.attack_chance", "1");
|
||||||
|
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, find_order("ATTACKIERE 2", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_monsters_attack_not(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
|
||||||
|
guard(m, GUARD_TAX);
|
||||||
|
guard(u, GUARD_TAX);
|
||||||
|
|
||||||
|
config_set("rules.monsters.attack_chance", "0");
|
||||||
|
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrEquals(tc, 0, find_order("ATTACKIERE 1", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dragon_attacks_the_rich(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
const item_type *i_silver;
|
||||||
|
|
||||||
|
init_language();
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
|
||||||
|
guard(m, GUARD_TAX);
|
||||||
|
set_level(m, SK_WEAPONLESS, 10);
|
||||||
|
|
||||||
|
rsetmoney(r, 1);
|
||||||
|
rsetmoney(findregion(1, 0), 0);
|
||||||
|
i_silver = it_find("money");
|
||||||
|
assert(i_silver);
|
||||||
|
i_change(&u->items, i_silver, 5000);
|
||||||
|
|
||||||
|
config_set("rules.monsters.attack_chance", "0.00001");
|
||||||
|
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, find_order("ATTACKIERE 1", m));
|
||||||
|
CuAssertPtrNotNull(tc, find_order("PLUENDERE", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dragon_moves(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
rsetpeasants(r, 0);
|
||||||
|
rsetmoney(r, 0);
|
||||||
|
rsetmoney(findregion(1, 0), 1000);
|
||||||
|
|
||||||
|
set_level(m, SK_WEAPONLESS, 10);
|
||||||
|
config_set("rules.monsters.attack_chance", ".0");
|
||||||
|
plan_monsters(f2);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, find_order("NACH O", m));
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_monsters_learn_exp(CuTest * tc)
|
||||||
|
{
|
||||||
|
faction *f, *f2;
|
||||||
|
region *r;
|
||||||
|
unit *u, *m;
|
||||||
|
skill* sk;
|
||||||
|
|
||||||
|
create_monsters(&f, &f2, &r, &u, &m);
|
||||||
|
config_set("study.from_use", "1");
|
||||||
|
|
||||||
|
u_setrace(u, u_race(m));
|
||||||
|
produceexp(u, SK_MELEE, u->number);
|
||||||
|
sk = unit_skill(u, SK_MELEE);
|
||||||
|
CuAssertTrue(tc, !sk);
|
||||||
|
|
||||||
|
produceexp(m, SK_MELEE, u->number);
|
||||||
|
sk = unit_skill(m, SK_MELEE);
|
||||||
|
CuAssertTrue(tc, sk && (sk->level > 0 || (sk->level == 0 && sk->weeks > 0)));
|
||||||
|
|
||||||
|
test_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
CuSuite *get_monsters_suite(void)
|
||||||
|
{
|
||||||
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
SUITE_ADD_TEST(suite, test_monsters_attack);
|
||||||
|
SUITE_ADD_TEST(suite, test_monsters_attack_ocean);
|
||||||
|
SUITE_ADD_TEST(suite, test_seaserpent_attack);
|
||||||
|
SUITE_ADD_TEST(suite, test_monsters_waiting);
|
||||||
|
SUITE_ADD_TEST(suite, test_monsters_attack_not);
|
||||||
|
SUITE_ADD_TEST(suite, test_dragon_attacks_the_rich);
|
||||||
|
SUITE_ADD_TEST(suite, test_dragon_moves);
|
||||||
|
SUITE_ADD_TEST(suite, test_monsters_learn_exp);
|
||||||
|
return suite;
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
#include <kernel/faction.h>
|
#include <kernel/faction.h>
|
||||||
#include <kernel/messages.h>
|
#include <kernel/messages.h>
|
||||||
#include <kernel/order.h>
|
#include <kernel/order.h>
|
||||||
|
#include <kernel/race.h>
|
||||||
#include <kernel/region.h>
|
#include <kernel/region.h>
|
||||||
#include <kernel/ship.h>
|
#include <kernel/ship.h>
|
||||||
#include <kernel/terrain.h>
|
#include <kernel/terrain.h>
|
||||||
|
@ -65,6 +66,8 @@ static attrib *mk_piracy(const faction * pirate, const faction * target,
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool validate_pirate(unit *u, order *ord) {
|
static bool validate_pirate(unit *u, order *ord) {
|
||||||
|
if (fval(u_race(u), RCF_SWIM | RCF_FLY))
|
||||||
|
return true;
|
||||||
if (!u->ship) {
|
if (!u->ship) {
|
||||||
cmistake(u, ord, 144, MSG_MOVE);
|
cmistake(u, ord, 144, MSG_MOVE);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <kernel/terrain.h>
|
#include <kernel/terrain.h>
|
||||||
#include <kernel/faction.h>
|
#include <kernel/faction.h>
|
||||||
#include <kernel/order.h>
|
#include <kernel/order.h>
|
||||||
|
#include <kernel/race.h>
|
||||||
|
|
||||||
#include <util/base36.h>
|
#include <util/base36.h>
|
||||||
#include <util/language.h>
|
#include <util/language.h>
|
||||||
|
@ -66,6 +67,7 @@ static void test_piracy_cmd(CuTest * tc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_piracy_cmd_errors(CuTest * tc) {
|
static void test_piracy_cmd_errors(CuTest * tc) {
|
||||||
|
race *r;
|
||||||
faction *f;
|
faction *f;
|
||||||
unit *u, *u2;
|
unit *u, *u2;
|
||||||
order *ord;
|
order *ord;
|
||||||
|
@ -73,7 +75,8 @@ static void test_piracy_cmd_errors(CuTest * tc) {
|
||||||
|
|
||||||
setup_piracy();
|
setup_piracy();
|
||||||
st_boat = st_get_or_create("boat");
|
st_boat = st_get_or_create("boat");
|
||||||
u = test_create_unit(f = test_create_faction(0), test_create_region(0, 0, get_or_create_terrain("ocean")));
|
r = test_create_race("pirates");
|
||||||
|
u = test_create_unit(f = test_create_faction(r), test_create_region(0, 0, get_or_create_terrain("ocean")));
|
||||||
f->locale = get_or_create_locale("de");
|
f->locale = get_or_create_locale("de");
|
||||||
ord = create_order(K_PIRACY, f->locale, "");
|
ord = create_order(K_PIRACY, f->locale, "");
|
||||||
assert(u && ord);
|
assert(u && ord);
|
||||||
|
@ -81,6 +84,17 @@ static void test_piracy_cmd_errors(CuTest * tc) {
|
||||||
piracy_cmd(u, ord);
|
piracy_cmd(u, ord);
|
||||||
CuAssertPtrNotNullMsg(tc, "must be on a ship for PIRACY", test_find_messagetype(f->msgs, "error144"));
|
CuAssertPtrNotNullMsg(tc, "must be on a ship for PIRACY", test_find_messagetype(f->msgs, "error144"));
|
||||||
|
|
||||||
|
test_clear_messages(f);
|
||||||
|
fset(r, RCF_SWIM);
|
||||||
|
piracy_cmd(u, ord);
|
||||||
|
CuAssertPtrEquals_Msg(tc, "swimmers are pirates", 0, test_find_messagetype(f->msgs, "error144"));
|
||||||
|
CuAssertPtrEquals_Msg(tc, "swimmers are pirates", 0, test_find_messagetype(f->msgs, "error146"));
|
||||||
|
freset(r, RCF_SWIM);
|
||||||
|
fset(r, RCF_FLY);
|
||||||
|
CuAssertPtrEquals_Msg(tc, "flyers are pirates", 0, test_find_messagetype(f->msgs, "error144"));
|
||||||
|
freset(r, RCF_FLY);
|
||||||
|
test_clear_messages(f);
|
||||||
|
|
||||||
u_set_ship(u, test_create_ship(u->region, st_boat));
|
u_set_ship(u, test_create_ship(u->region, st_boat));
|
||||||
|
|
||||||
u2 = test_create_unit(u->faction, u->region);
|
u2 = test_create_unit(u->faction, u->region);
|
||||||
|
|
|
@ -118,6 +118,7 @@ int RunAllTests(int argc, char *argv[])
|
||||||
ADD_SUITE(give);
|
ADD_SUITE(give);
|
||||||
ADD_SUITE(laws);
|
ADD_SUITE(laws);
|
||||||
ADD_SUITE(market);
|
ADD_SUITE(market);
|
||||||
|
ADD_SUITE(monsters);
|
||||||
ADD_SUITE(move);
|
ADD_SUITE(move);
|
||||||
ADD_SUITE(piracy);
|
ADD_SUITE(piracy);
|
||||||
ADD_SUITE(stealth);
|
ADD_SUITE(stealth);
|
||||||
|
|
|
@ -236,10 +236,10 @@ void test_create_world(void)
|
||||||
test_create_itemtype("iron");
|
test_create_itemtype("iron");
|
||||||
test_create_itemtype("stone");
|
test_create_itemtype("stone");
|
||||||
|
|
||||||
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION);
|
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | FLY_INTO);
|
||||||
t_plain->size = 1000;
|
t_plain->size = 1000;
|
||||||
t_plain->max_road = 100;
|
t_plain->max_road = 100;
|
||||||
t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO);
|
t_ocean = test_create_terrain("ocean", SEA_REGION | SAIL_INTO | SWIM_INTO | FLY_INTO);
|
||||||
t_ocean->size = 0;
|
t_ocean->size = 0;
|
||||||
|
|
||||||
island[0] = test_create_region(0, 0, t_plain);
|
island[0] = test_create_region(0, 0, t_plain);
|
||||||
|
|
Loading…
Reference in New Issue