Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Enno Rehling 2017-05-28 12:50:51 +02:00
commit 8052ff8b23
32 changed files with 623 additions and 477 deletions

View File

@ -56,9 +56,98 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <util/attrib.h> #include <util/attrib.h>
#include <util/event.h> #include <util/event.h>
#include <util/gamedata.h> #include <util/gamedata.h>
#include <util/resolve.h>
#include <storage.h> #include <storage.h>
#include <stdlib.h>
typedef struct obs_data {
faction *f;
int skill;
int timer;
} obs_data;
static void obs_init(struct attrib *a)
{
a->data.v = malloc(sizeof(obs_data));
}
static void obs_done(struct attrib *a)
{
free(a->data.v);
}
static int obs_age(struct attrib *a, void *owner)
{
obs_data *od = (obs_data *)a->data.v;
update_interval(od->f, (region *)owner);
return --od->timer;
}
static void obs_write(const struct attrib *a, const void *owner, struct storage *store)
{
obs_data *od = (obs_data *)a->data.v;
write_faction_reference(od->f, store);
WRITE_INT(store, od->skill);
WRITE_INT(store, od->timer);
}
static int obs_read(struct attrib *a, void *owner, struct gamedata *data)
{
obs_data *od = (obs_data *)a->data.v;
read_reference(&od->f, data, read_faction_reference, resolve_faction);
READ_INT(data->store, &od->skill);
READ_INT(data->store, &od->timer);
return AT_READ_OK;
}
attrib_type at_observer = { "observer", obs_init, obs_done, obs_age, obs_write, obs_read };
static attrib *make_observer(faction *f, int perception)
{
attrib * a = a_new(&at_observer);
obs_data *od = (obs_data *)a->data.v;
od->f = f;
od->skill = perception;
od->timer = 2;
return a;
}
int get_observer(region *r, faction *f) {
if (fval(r, RF_OBSERVER)) {
attrib *a = a_find(r->attribs, &at_observer);
while (a && a->type == &at_observer) {
obs_data *od = (obs_data *)a->data.v;
if (od->f == f) {
return od->skill;
}
a = a->next;
}
}
return -1;
}
void set_observer(region *r, faction *f, int skill, int turns)
{
update_interval(f, r);
if (fval(r, RF_OBSERVER)) {
attrib *a = a_find(r->attribs, &at_observer);
while (a && a->type == &at_observer) {
obs_data *od = (obs_data *)a->data.v;
if (od->f == f && od->skill < skill) {
od->skill = skill;
od->timer = turns;
return;
}
a = a->nexttype;
}
}
else {
fset(r, RF_OBSERVER);
}
a_add(&r->attribs, make_observer(f, skill));
}
attrib_type at_unitdissolve = { attrib_type at_unitdissolve = {
"unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars
@ -123,6 +212,7 @@ void register_attributes(void)
at_register(&at_stealth); at_register(&at_stealth);
at_register(&at_dict); at_register(&at_dict);
at_register(&at_unitdissolve); at_register(&at_unitdissolve);
at_register(&at_observer);
at_register(&at_overrideroads); at_register(&at_overrideroads);
at_register(&at_raceprefix); at_register(&at_raceprefix);
at_register(&at_iceberg); at_register(&at_iceberg);

View File

@ -23,8 +23,14 @@ extern "C" {
#endif #endif
struct attrib_type; struct attrib_type;
struct region;
struct faction;
extern void register_attributes(void); extern void register_attributes(void);
void set_observer(struct region *r, struct faction *f, int perception, int turns);
int get_observer(struct region *r, struct faction *f);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -439,7 +439,7 @@ static int get_row(const side * s, int row, const side * vs)
return result; return result;
} }
static int get_unitrow(const fighter * af, const side * vs) int get_unitrow(const fighter * af, const side * vs)
{ {
int row = statusrow(af->status); int row = statusrow(af->status);
if (vs == NULL) { if (vs == NULL) {
@ -1589,8 +1589,7 @@ static troop select_opponent(battle * b, troop at, int mindist, int maxdist)
return dt; return dt;
} }
selist *fighters(battle * b, const side * vs, int minrow, int maxrow, selist *select_fighters(battle * b, const side * vs, int mask, select_fun cb, void *cbdata)
int mask)
{ {
side *s; side *s;
selist *fightervp = 0; selist *fightervp = 0;
@ -1613,8 +1612,7 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow,
assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state"); assert(mask == (FS_HELP | FS_ENEMY) || !"invalid alliance state");
} }
for (fig = s->fighters; fig; fig = fig->next) { for (fig = s->fighters; fig; fig = fig->next) {
int row = get_unitrow(fig, vs); if (cb(vs, fig, cbdata)) {
if (row >= minrow && row <= maxrow) {
selist_push(&fightervp, fig); selist_push(&fightervp, fig);
} }
} }
@ -1623,6 +1621,26 @@ selist *fighters(battle * b, const side * vs, int minrow, int maxrow,
return fightervp; return fightervp;
} }
struct selector {
int minrow;
int maxrow;
};
static bool select_row(const side *vs, const fighter *fig, void *cbdata)
{
struct selector *sel = (struct selector *)cbdata;
int row = get_unitrow(fig, vs);
return (row >= sel->minrow && row <= sel->maxrow);
}
selist *fighters(battle * b, const side * vs, int minrow, int maxrow, int mask)
{
struct selector sel;
sel.maxrow = maxrow;
sel.minrow = minrow;
return select_fighters(b, vs, mask, select_row, &sel);
}
static void report_failed_spell(struct battle * b, struct unit * mage, const struct spell *sp) static void report_failed_spell(struct battle * b, struct unit * mage, const struct spell *sp)
{ {
message *m = msg_message("battle::spell_failed", "unit spell", mage, sp); message *m = msg_message("battle::spell_failed", "unit spell", mage, sp);
@ -1636,18 +1654,11 @@ static castorder * create_castorder_combat(castorder *co, fighter *fig, const sp
return co; return co;
} }
void do_combatmagic(battle * b, combatmagic_t was)
{
side *s;
region *r = b->region;
castorder *co;
int level, rank, sl;
spellrank spellranks[MAX_SPELLRANK];
memset(spellranks, 0, sizeof(spellranks));
#ifdef FFL_CURSED #ifdef FFL_CURSED
if (was == DO_PRECOMBATSPELL) { static void summon_igjarjuk(battle *b, spellrank spellranks[]) {
side *s;
castorder *co;
for (s = b->sides; s != b->sides + b->nsides; ++s) { for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fig = 0; fighter *fig = 0;
if (s->bf->attacker && fval(s->faction, FFL_CURSED)) { if (s->bf->attacker && fval(s->faction, FFL_CURSED)) {
@ -1681,6 +1692,22 @@ void do_combatmagic(battle * b, combatmagic_t was)
} }
} }
} }
#endif
void do_combatmagic(battle * b, combatmagic_t was)
{
side *s;
castorder *co;
region *r = b->region;
int level, rank, sl;
spellrank spellranks[MAX_SPELLRANK];
memset(spellranks, 0, sizeof(spellranks));
#ifdef FFL_CURSED
if (was == DO_PRECOMBATSPELL) {
summon_igjarjuk(b, spellranks);
}
#endif #endif
for (s = b->sides; s != b->sides + b->nsides; ++s) { for (s = b->sides; s != b->sides + b->nsides; ++s) {
fighter *fig; fighter *fig;

View File

@ -229,15 +229,17 @@ extern "C" {
fighter * get_fighter(battle * b, const struct unit * u); fighter * get_fighter(battle * b, const struct unit * u);
/* END battle interface */ /* END battle interface */
extern void do_battles(void); void do_battles(void);
/* for combat spells and special attacks */ /* for combat spells and special attacks */
enum { SELECT_ADVANCE = 0x1, SELECT_DISTANCE = 0x2, SELECT_FIND = 0x4 }; enum { SELECT_ADVANCE = 0x1, SELECT_DISTANCE = 0x2, SELECT_FIND = 0x4 };
enum { ALLY_SELF, ALLY_ANY }; enum { ALLY_SELF, ALLY_ANY };
extern troop select_enemy(struct fighter *af, int minrow, int maxrow, int get_unitrow(const fighter * af, const side * vs);
troop select_enemy(struct fighter *af, int minrow, int maxrow,
int select); int select);
extern troop select_ally(struct fighter *af, int minrow, int maxrow, troop select_ally(struct fighter *af, int minrow, int maxrow,
int allytype); int allytype);
int count_enemies(struct battle *b, const struct fighter *af, int count_enemies(struct battle *b, const struct fighter *af,
@ -246,21 +248,25 @@ extern "C" {
int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, union variant *magres); int calculate_armor(troop dt, const struct weapon_type *dwtype, const struct weapon_type *awtype, union variant *magres);
bool terminate(troop dt, troop at, int type, const char *damage, bool terminate(troop dt, troop at, int type, const char *damage,
bool missile); bool missile);
extern void message_all(battle * b, struct message *m); void message_all(battle * b, struct message *m);
extern int hits(troop at, troop dt, weapon * awp); int hits(troop at, troop dt, weapon * awp);
extern void damage_building(struct battle *b, struct building *bldg, void damage_building(struct battle *b, struct building *bldg,
int damage_abs); int damage_abs);
typedef bool(*select_fun)(const struct side *vs, const struct fighter *fig, void *cbdata);
struct selist *select_fighters(struct battle *b, const struct side *vs, int mask, select_fun cb, void *cbdata);
struct selist *fighters(struct battle *b, const struct side *vs, struct selist *fighters(struct battle *b, const struct side *vs,
int minrow, int maxrow, int mask); int minrow, int maxrow, int mask);
int count_allies(const struct side *as, int minrow, int maxrow, int count_allies(const struct side *as, int minrow, int maxrow,
int select, int allytype); int select, int allytype);
extern bool helping(const struct side *as, const struct side *ds); bool helping(const struct side *as, const struct side *ds);
extern void rmfighter(fighter * df, int i); void rmfighter(fighter * df, int i);
extern struct fighter *select_corpse(struct battle *b, struct fighter *af); struct fighter *select_corpse(struct battle *b, struct fighter *af);
extern int statusrow(int status); int statusrow(int status);
extern void drain_exp(struct unit *u, int d); void drain_exp(struct unit *u, int d);
extern void kill_troop(troop dt); void kill_troop(troop dt);
extern void remove_troop(troop dt); /* not the same as the badly named rmtroop */ void remove_troop(troop dt); /* not the same as the badly named rmtroop */
bool is_attacker(const fighter * fig); bool is_attacker(const fighter * fig);
struct battle *make_battle(struct region * r); struct battle *make_battle(struct region * r);

View File

@ -112,10 +112,8 @@ static unit *random_unit(const region * r)
unit *u; unit *u;
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
if (u_race(u) != get_race(RC_SPELL)) {
c += u->number; c += u->number;
} }
}
if (c == 0) { if (c == 0) {
return NULL; return NULL;
@ -125,9 +123,7 @@ static unit *random_unit(const region * r)
u = r->units; u = r->units;
while (u && c < n) { while (u && c < n) {
if (u_race(u) != get_race(RC_SPELL)) {
c += u->number; c += u->number;
}
u = u->next; u = u->next;
} }
@ -203,7 +199,7 @@ static void chaos(region * r)
for (up = &r->units; *up;) { for (up = &r->units; *up;) {
unit *u = *up; unit *u = *up;
if (u_race(u) != get_race(RC_SPELL) && u->ship == 0 && !canfly(u)) { if (u->ship == 0 && !canfly(u)) {
ADDMSG(&u->faction->msgs, msg_message("tidalwave_kill", ADDMSG(&u->faction->msgs, msg_message("tidalwave_kill",
"region unit", r, u)); "region unit", r, u));
remove_unit(up, u); remove_unit(up, u);

View File

@ -1418,7 +1418,7 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
} }
cr_output_curses_compat(F, f, r, TYP_REGION); cr_output_curses_compat(F, f, r, TYP_REGION);
cr_borders(r, f, r->seen.mode, F); cr_borders(r, f, r->seen.mode, F);
if (r->seen.mode == seen_unit && is_astral(r) if (r->seen.mode >= seen_unit && is_astral(r)
&& !is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { && !is_cursed(r->attribs, C_ASTRALBLOCK, 0)) {
/* Sonderbehandlung Teleport-Ebene */ /* Sonderbehandlung Teleport-Ebene */
region_list *rl = astralregions(r, inhabitable); region_list *rl = astralregions(r, inhabitable);
@ -1441,7 +1441,7 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
} }
cr_output_travelthru(F, r, f); cr_output_travelthru(F, r, f);
if (r->seen.mode == seen_unit || r->seen.mode == seen_travel) { if (r->seen.mode >= seen_travel) {
message_list *mlist = r_getmessages(r, f); message_list *mlist = r_getmessages(r, f);
cr_output_messages(F, r->msgs, f); cr_output_messages(F, r->msgs, f);
if (mlist) { if (mlist) {

View File

@ -2978,13 +2978,12 @@ void produce(struct region *r)
static int bt_cache; static int bt_cache;
static const struct building_type *caravan_bt; static const struct building_type *caravan_bt;
static int rc_cache; static int rc_cache;
static const race *rc_spell, *rc_insect, *rc_aquarian; static const race *rc_insect, *rc_aquarian;
if (bt_changed(&bt_cache)) { if (bt_changed(&bt_cache)) {
caravan_bt = bt_find("caravan"); caravan_bt = bt_find("caravan");
} }
if (rc_changed(&rc_cache)) { if (rc_changed(&rc_cache)) {
rc_spell = get_race(RC_SPELL);
rc_insect = get_race(RC_INSECT); rc_insect = get_race(RC_INSECT);
rc_aquarian = get_race(RC_AQUARIAN); rc_aquarian = get_race(RC_AQUARIAN);
} }
@ -3022,7 +3021,7 @@ void produce(struct region *r)
bool trader = false; bool trader = false;
keyword_t todo; keyword_t todo;
if (u_race(u) == rc_spell || fval(u, UFL_LONGACTION)) if (fval(u, UFL_LONGACTION))
continue; continue;
if (u_race(u) == rc_insect && r_insectstalled(r) && if (u_race(u) == rc_insect && r_insectstalled(r) &&

View File

@ -667,12 +667,7 @@ void give_cmd(unit * u, order * ord)
return; return;
} }
if (u2 && u_race(u2) == get_race(RC_SPELL)) { if (u2 && !alliedunit(u2, u->faction, HELP_GIVE) && !ucontact(u2, u)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord,
"feedback_unit_not_found", ""));
return;
}
else if (u2 && !alliedunit(u2, u->faction, HELP_GIVE) && !ucontact(u2, u)) {
cmistake(u, ord, 40, MSG_COMMERCE); cmistake(u, ord, 40, MSG_COMMERCE);
return; return;
} }

View File

@ -69,7 +69,7 @@ const char *racenames[MAXRACES] = {
"dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect", "dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect",
"halfling", "cat", "aquarian", "orc", "snotling", "undead", NULL, "halfling", "cat", "aquarian", "orc", "snotling", "undead", NULL,
"youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid", "youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid",
NULL, "spell", "irongolem", "stonegolem", "shadowdemon", NULL, NULL, "irongolem", "stonegolem", "shadowdemon",
"shadowmaster", "mountainguard", "alp", "toad", "braineater", "peasant", "shadowmaster", "mountainguard", "alp", "toad", "braineater", "peasant",
"wolf", NULL, NULL, NULL, NULL, "songdragon", NULL, "wolf", NULL, NULL, NULL, NULL, "songdragon", NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,

View File

@ -72,8 +72,7 @@ extern "C" {
RC_BIRTHDAYDRAGON, RC_BIRTHDAYDRAGON,
RC_DRACOID, RC_DRACOID,
RC_SPELL = 22, RC_IRONGOLEM = 23,
RC_IRONGOLEM,
RC_STONEGOLEM, RC_STONEGOLEM,
RC_SHADOW, RC_SHADOW,
RC_SHADOWLORD, RC_SHADOWLORD,

View File

@ -35,7 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define RF_MALLORN (1<<1) /* persistent */ #define RF_MALLORN (1<<1) /* persistent */
#define RF_BLOCKED (1<<2) /* persistent */ #define RF_BLOCKED (1<<2) /* persistent */
#define RF_UNUSED_3 (1<<3) #define RF_OBSERVER (1<<3) /* persistent */
#define RF_UNUSED_4 (1<<4) #define RF_UNUSED_4 (1<<4)
#define RF_UNUSED_5 (1<<5) #define RF_UNUSED_5 (1<<5)
#define RF_UNUSED_6 (1<<6) #define RF_UNUSED_6 (1<<6)

View File

@ -48,6 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "lighthouse.h" #include "lighthouse.h"
/* attributes includes */ /* attributes includes */
#include <attributes/attributes.h>
#include <attributes/key.h> #include <attributes/key.h>
#include <triggers/timeout.h> #include <triggers/timeout.h>
@ -131,8 +132,6 @@ static unit *unitorders(FILE * F, int enc, struct faction *f)
i = getid(); i = getid();
u = findunitg(i, NULL); u = findunitg(i, NULL);
if (u && u_race(u) == get_race(RC_SPELL))
return NULL;
if (u && u->faction == f) { if (u && u->faction == f) {
order **ordp; order **ordp;
@ -851,7 +850,7 @@ void write_unit(struct gamedata *data, const unit * u)
WRITE_SECTION(data->store); WRITE_SECTION(data->store);
write_items(data->store, u->items); write_items(data->store, u->items);
WRITE_SECTION(data->store); WRITE_SECTION(data->store);
if (u->hp == 0 && u_race(u)!= get_race(RC_SPELL)) { if (u->hp == 0 && data->version < NORCSPELL_VERSION) {
log_error("unit %s has 0 hitpoints, adjusting.", itoa36(u->no)); log_error("unit %s has 0 hitpoints, adjusting.", itoa36(u->no));
((unit *)u)->hp = u->number; ((unit *)u)->hp = u->number;
} }
@ -1621,6 +1620,7 @@ int read_game(gamedata *data) {
int rmax = maxregions; int rmax = maxregions;
storage * store = data->store; storage * store = data->store;
const struct building_type *bt_lighthouse = bt_find("lighthouse"); const struct building_type *bt_lighthouse = bt_find("lighthouse");
const struct race *rc_spell = rc_find("spell");
if (data->version >= SAVEGAMEID_VERSION) { if (data->version >= SAVEGAMEID_VERSION) {
int gameid; int gameid;
@ -1705,6 +1705,11 @@ int read_game(gamedata *data) {
while (--p >= 0) { while (--p >= 0) {
unit *u = read_unit(data); unit *u = read_unit(data);
if (data->version < NORCSPELL_VERSION && u_race(u) == rc_spell) {
set_observer(r, u->faction, get_level(u, SK_PERCEPTION), u->age);
free_unit(u);
}
else {
if (data->version < JSON_REPORT_VERSION) { if (data->version < JSON_REPORT_VERSION) {
if (u->_name && fval(u->faction, FFL_NPC)) { if (u->_name && fval(u->faction, FFL_NPC)) {
if (!u->_name[0] || unit_name_equals_race(u)) { if (!u->_name[0] || unit_name_equals_race(u)) {
@ -1716,9 +1721,9 @@ int read_game(gamedata *data) {
u->region = r; u->region = r;
*up = u; *up = u;
up = &u->next; up = &u->next;
update_interval(u->faction, r); update_interval(u->faction, r);
} }
}
if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */ if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */
log_debug(" - Einzulesende Regionen: %d/%d * %d,%d \r", rmax, nread, r->x, r->y); log_debug(" - Einzulesende Regionen: %d/%d * %d,%d \r", rmax, nread, r->x, r->y);

View File

@ -71,8 +71,8 @@ typedef enum {
seen_neighbour, seen_neighbour,
seen_lighthouse, seen_lighthouse,
seen_travel, seen_travel,
seen_far,
seen_unit, seen_unit,
seen_spell,
seen_battle seen_battle
} seen_mode; } seen_mode;

View File

@ -981,7 +981,7 @@ void move_unit(unit * u, region * r, unit ** ulist)
/* ist mist, aber wegen nicht skalierender attribute notwendig: */ /* ist mist, aber wegen nicht skalierender attribute notwendig: */
#include "alchemy.h" #include "alchemy.h"
void transfermen(unit * u, unit * dst, int n) void clone_men(unit * u, unit * dst, int n)
{ {
const attrib *a; const attrib *a;
int hp = u->hp; int hp = u->hp;
@ -1074,7 +1074,6 @@ void transfermen(unit * u, unit * dst, int n)
transfer_curse(u, dst, n); transfer_curse(u, dst, n);
} }
} }
scale_number(u, u->number - n);
if (dst) { if (dst) {
set_number(dst, dst->number + n); set_number(dst, dst->number + n);
hp -= u->hp; hp -= u->hp;
@ -1100,6 +1099,12 @@ void transfermen(unit * u, unit * dst, int n)
} }
} }
void transfermen(unit * u, unit * dst, int n)
{
clone_men(u, dst, n);
scale_number(u, u->number - n);
}
struct building *inside_building(const struct unit *u) struct building *inside_building(const struct unit *u)
{ {
if (!u->building) { if (!u->building) {
@ -1891,8 +1896,7 @@ void remove_empty_units_in_region(region * r)
set_number(u, 0); set_number(u, 0);
} }
} }
if ((u->number == 0 && u_race(u) != get_race(RC_SPELL)) || (u->age <= 0 if (u->number == 0) {
&& u_race(u) == get_race(RC_SPELL))) {
remove_unit(up, u); remove_unit(up, u);
} }
if (*up == u) if (*up == u)

View File

@ -165,7 +165,8 @@ extern "C" {
void set_level(struct unit *u, skill_t id, int level); void set_level(struct unit *u, skill_t id, int level);
int get_level(const struct unit *u, skill_t id); int get_level(const struct unit *u, skill_t id);
extern void transfermen(struct unit *src, struct unit *dst, int n); void transfermen(struct unit *src, struct unit *dst, int n);
void clone_men(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 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, const struct region *r);

View File

@ -97,26 +97,6 @@ static void test_remove_units_with_dead_faction(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static void test_remove_units_ignores_spells(CuTest *tc) {
unit *u;
int uid;
test_cleanup();
test_create_world();
u = create_unit(findregion(0, 0), test_create_faction(test_create_race("human")), 1, test_create_race("spell"), 0, 0, 0);
uid = u->no;
u->number = 0;
u->age = 1;
remove_empty_units_in_region(u->region);
CuAssertPtrNotNull(tc, findunit(uid));
CuAssertPtrNotNull(tc, u->region);
u->age = 0;
remove_empty_units_in_region(u->region);
CuAssertPtrEquals(tc, 0, findunit(uid));
test_cleanup();
}
static void test_scale_number(CuTest *tc) { static void test_scale_number(CuTest *tc) {
unit *u; unit *u;
const struct potion_type *ptype; const struct potion_type *ptype;
@ -555,7 +535,6 @@ CuSuite *get_unit_suite(void)
SUITE_ADD_TEST(suite, test_update_monster_name); SUITE_ADD_TEST(suite, test_update_monster_name);
SUITE_ADD_TEST(suite, test_remove_unit); SUITE_ADD_TEST(suite, test_remove_unit);
SUITE_ADD_TEST(suite, test_remove_empty_units); SUITE_ADD_TEST(suite, test_remove_empty_units);
SUITE_ADD_TEST(suite, test_remove_units_ignores_spells);
SUITE_ADD_TEST(suite, test_remove_units_without_faction); SUITE_ADD_TEST(suite, test_remove_units_without_faction);
SUITE_ADD_TEST(suite, test_remove_units_with_dead_faction); SUITE_ADD_TEST(suite, test_remove_units_with_dead_faction);
SUITE_ADD_TEST(suite, test_remove_empty_units_in_region); SUITE_ADD_TEST(suite, test_remove_empty_units_in_region);

View File

@ -153,24 +153,12 @@ static bool RemoveNMRNewbie(void)
static void age_unit(region * r, unit * u) static void age_unit(region * r, unit * u)
{ {
static int rc_cache;
static const race *rc_spell;
if (rc_changed(&rc_cache)) {
rc_spell = get_race(RC_SPELL);
}
if (u_race(u) == rc_spell) {
if (--u->age <= 0) {
remove_unit(&r->units, u);
}
}
else {
const race *rc = u_race(u); const race *rc = u_race(u);
++u->age; ++u->age;
if (u->number > 0 && rc->age_unit) { if (u->number > 0 && rc->age_unit) {
rc->age_unit(u); rc->age_unit(u);
} }
}
if (u->region && is_astral(u->region)) { if (u->region && is_astral(u->region)) {
item **itemp = &u->items; item **itemp = &u->items;
while (*itemp) { while (*itemp) {
@ -2676,8 +2664,7 @@ int guard_on_cmd(unit * u, struct order *ord)
if (fval(u, UFL_MOVED)) { if (fval(u, UFL_MOVED)) {
cmistake(u, ord, 187, MSG_EVENT); cmistake(u, ord, 187, MSG_EVENT);
} }
else if (fval(u_race(u), RCF_ILLUSIONARY) else if (fval(u_race(u), RCF_ILLUSIONARY)) {
|| u_race(u) == get_race(RC_SPELL)) {
cmistake(u, ord, 95, MSG_EVENT); cmistake(u, ord, 95, MSG_EVENT);
} }
else { else {

View File

@ -63,42 +63,21 @@ void update_lighthouse(building * lh)
} }
} }
int lighthouse_range(const building * b, const faction * f) int lighthouse_range(const building * b, const faction * f, const unit *u)
{ {
int d = 0;
if (fval(b, BLD_MAINTAINED) && b->size >= 10) { if (fval(b, BLD_MAINTAINED) && b->size >= 10) {
int maxd = (int)log10(b->size) + 1; int maxd = (int)log10(b->size) + 1;
if (skill_enabled(SK_PERCEPTION)) { if (u && skill_enabled(SK_PERCEPTION)) {
region *r = b->region;
int c = 0;
int cap = buildingcapacity(b);
unit *u, *uown = building_owner(b);
for (u = r->units; u; u = u->next) {
if (u->building == b || u == uown) {
c += u->number;
if (c > cap) {
break;
}
else if (f == NULL || u->faction == f) {
int sk = effskill(u, SK_PERCEPTION, 0) / 3; int sk = effskill(u, SK_PERCEPTION, 0) / 3;
d = MAX(d, sk); assert(u->building == b);
d = MIN(maxd, d); assert(u->faction == f);
if (d == maxd) maxd = MIN(maxd, sk);
break;
} }
}
else if (c)
break; /* first unit that's no longer in the house ends the search */
}
}
else {
/* E3A rule: no perception req'd */ /* E3A rule: no perception req'd */
return maxd; return maxd;
} }
} return 0;
return d;
} }
bool check_leuchtturm(region * r, faction * f) bool check_leuchtturm(region * r, faction * f)

View File

@ -29,14 +29,15 @@ extern "C" {
struct faction; struct faction;
struct region; struct region;
struct building; struct building;
struct unit;
struct attrib; struct attrib;
extern struct attrib_type at_lighthouse; extern struct attrib_type at_lighthouse;
/* leuchtturm */ /* leuchtturm */
bool check_leuchtturm(struct region *r, struct faction *f); bool check_leuchtturm(struct region *r, struct faction *f);
void update_lighthouse(struct building *lh); void update_lighthouse(struct building *b);
int lighthouse_range(const struct building *b, int lighthouse_range(const struct building *b, const struct faction *f,
const struct faction *f); const struct unit *u);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -26,29 +26,29 @@ static void test_lighthouse_range(CuTest * tc)
u1 = test_create_unit(test_create_faction(0), r); u1 = test_create_unit(test_create_faction(0), r);
u2 = test_create_unit(test_create_faction(0), r); u2 = test_create_unit(test_create_faction(0), r);
b = test_create_building(r, test_create_buildingtype("lighthouse")); b = test_create_building(r, test_create_buildingtype("lighthouse"));
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL));
CuAssertIntEquals(tc, 0, lighthouse_range(b, u1->faction)); CuAssertIntEquals(tc, 0, lighthouse_range(b, u1->faction, NULL));
b->size = 10; b->size = 10;
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL));
u1->building = b; u1->building = b;
u2->building = b; u2->building = b;
u1->number = 10; u1->number = 10;
set_level(u1, SK_PERCEPTION, 3); set_level(u1, SK_PERCEPTION, 3);
set_level(u2, SK_PERCEPTION, 3); set_level(u2, SK_PERCEPTION, 3);
CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL));
b->flags |= BLD_MAINTAINED; b->flags |= BLD_MAINTAINED;
CuAssertIntEquals(tc, 1, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction, u1));
set_level(u1, SK_PERCEPTION, 6); set_level(u1, SK_PERCEPTION, 6);
CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction)); CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction, u1));
CuAssertIntEquals(tc, 0, lighthouse_range(b, u2->faction)); /* lighthouse_range does not check inside_building */
CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2));
b->size = 100; b->size = 100;
update_lighthouse(b); update_lighthouse(b);
CuAssertIntEquals(tc, 2, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 3, lighthouse_range(b, NULL, NULL));
CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction, u1));
set_level(u1, SK_PERCEPTION, 9); set_level(u1, SK_PERCEPTION, 9);
CuAssertIntEquals(tc, 3, lighthouse_range(b, NULL)); CuAssertIntEquals(tc, 3, lighthouse_range(b, u1->faction, u1));
CuAssertIntEquals(tc, 3, lighthouse_range(b, u1->faction)); CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2));
CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction));
CuAssertIntEquals(tc, 0, lighthouse_range(b, test_create_faction(0)));
test_cleanup(); test_cleanup();
} }

View File

@ -1209,9 +1209,6 @@ target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
skill *sv; skill *sv;
unit *u = (unit *)obj; unit *u = (unit *)obj;
if (u_race(u)==get_race(RC_SPELL)) {
return true;
}
at = effskill(magician, SK_MAGIC, 0); at = effskill(magician, SK_MAGIC, 0);
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
@ -2780,7 +2777,6 @@ void magic(void)
int rank; int rank;
castorder *co; castorder *co;
spellrank spellranks[MAX_SPELLRANK]; spellrank spellranks[MAX_SPELLRANK];
const race *rc_spell = get_race(RC_SPELL);
const race *rc_insect = get_race(RC_INSECT); const race *rc_insect = get_race(RC_INSECT);
memset(spellranks, 0, sizeof(spellranks)); memset(spellranks, 0, sizeof(spellranks));
@ -2790,7 +2786,7 @@ void magic(void)
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
order *ord; order *ord;
if (u->number <= 0 || u_race(u) == rc_spell) if (u->number <= 0)
continue; continue;
if (u_race(u) == rc_insect && r_insectstalled(r) && if (u_race(u) == rc_insect && r_insectstalled(r) &&

View File

@ -418,7 +418,7 @@ void drown(region * r)
while (*up) { while (*up) {
unit *u = *up; unit *u = *up;
if (!(u->ship || u_race(u) == get_race(RC_SPELL) || u->number == 0 || canswim(u) || canfly(u))) { if (!(u->ship || u->number == 0 || canswim(u) || canfly(u))) {
scale_number(u, 0); scale_number(u, 0);
ADDMSG(&u->faction->msgs, msg_message("drown", "unit region", u, r)); ADDMSG(&u->faction->msgs, msg_message("drown", "unit region", u, r));
} }

View File

@ -997,7 +997,7 @@ void report_region(struct stream *out, const region * r, faction * f)
} }
/* iron & stone */ /* iron & stone */
if (r->seen.mode == seen_unit) { if (r->seen.mode >= seen_unit) {
resource_report result[MAX_RAWMATERIALS]; resource_report result[MAX_RAWMATERIALS];
int n, numresults = report_resources(r, result, MAX_RAWMATERIALS, f, true); int n, numresults = report_resources(r, result, MAX_RAWMATERIALS, f, true);
@ -1184,7 +1184,7 @@ void report_region(struct stream *out, const region * r, faction * f)
*bufp = 0; *bufp = 0;
paragraph(out, buf, 0, 0, 0); paragraph(out, buf, 0, 0, 0);
if (r->seen.mode == seen_unit && is_astral(r) && if (r->seen.mode >= seen_unit && is_astral(r) &&
!is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { !is_cursed(r->attribs, C_ASTRALBLOCK, 0)) {
/* Sonderbehandlung Teleport-Ebene */ /* Sonderbehandlung Teleport-Ebene */
region_list *rl = astralregions(r, inhabitable); region_list *rl = astralregions(r, inhabitable);
@ -2272,7 +2272,7 @@ report_plaintext(const char *filename, report_context * ctx,
continue; continue;
/* Beschreibung */ /* Beschreibung */
if (r->seen.mode == seen_unit) { if (r->seen.mode >= seen_unit) {
anyunits = 1; anyunits = 1;
newline(out); newline(out);
report_region(out, r, f); report_region(out, r, f);
@ -2305,24 +2305,13 @@ report_plaintext(const char *filename, report_context * ctx,
report_travelthru(out, r, f); report_travelthru(out, r, f);
} }
else { else {
if (r->seen.mode == seen_far) {
newline(out);
report_region(out, r, f);
newline(out);
guards(out, r, f);
newline(out);
report_travelthru(out, r, f);
}
else {
newline(out);
report_region(out, r, f); report_region(out, r, f);
newline(out); newline(out);
report_travelthru(out, r, f); report_travelthru(out, r, f);
} }
}
/* Statistik */ /* Statistik */
if (wants_stats && r->seen.mode == seen_unit) if (wants_stats && r->seen.mode >= seen_unit)
statistics(out, r, f); statistics(out, r, f);
/* Nachrichten an REGION in der Region */ /* Nachrichten an REGION in der Region */

View File

@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "reports.h" #include "reports.h"
#include "guard.h" #include "guard.h"
#include "laws.h" #include "laws.h"
#include "spells.h"
#include "travelthru.h" #include "travelthru.h"
#include "lighthouse.h" #include "lighthouse.h"
#include "donations.h" #include "donations.h"
@ -69,6 +70,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <time.h> #include <time.h>
/* attributes includes */ /* attributes includes */
#include <attributes/attributes.h>
#include <attributes/follow.h> #include <attributes/follow.h>
#include <attributes/otherfaction.h> #include <attributes/otherfaction.h>
#include <attributes/racename.h> #include <attributes/racename.h>
@ -93,6 +95,7 @@ const char *visibility[] = {
"travel", "travel",
"far", "far",
"unit", "unit",
"spell",
"battle" "battle"
}; };
@ -966,9 +969,9 @@ const struct unit *ucansee(const struct faction *f, const struct unit *u,
int stealth_modifier(seen_mode mode) int stealth_modifier(seen_mode mode)
{ {
switch (mode) { switch (mode) {
case seen_spell:
case seen_unit: case seen_unit:
return 0; return 0;
case seen_far:
case seen_lighthouse: case seen_lighthouse:
return -2; return -2;
case seen_travel: case seen_travel:
@ -1422,28 +1425,57 @@ void prepare_report(report_context *ctx, faction *f)
ctx->last = lastregion(f); ctx->last = lastregion(f);
for (r = ctx->first; r!=ctx->last; r = r->next) { for (r = ctx->first; r!=ctx->last; r = r->next) {
int range = 0;
unit *u; unit *u;
if (fval(r, RF_LIGHTHOUSE) && bt_lighthouse) {
if (rule_region_owners && f == region_get_owner(r)) {
/* region owners get the report from lighthouses */
building *b; building *b;
int br = 0, c = 0, range = 0;
for (b = r->buildings; b; b = b->next) { if (fval(r, RF_OBSERVER)) {
if (b->type == bt_lighthouse) { int skill = get_observer(r, f);
int br = lighthouse_range(b, NULL); if (skill >= 0) {
add_seen_nb(f, r, seen_spell);
}
}
if (fval(r, RF_LIGHTHOUSE)) {
/* region owners get the report from lighthouses */
if (rule_region_owners && f == region_get_owner(r)) {
for (b = rbuildings(r); b; b = b->next) {
if (b && b->type == bt_lighthouse) {
/* region owners get maximm range */
int br = lighthouse_range(b, NULL, NULL);
if (br > range) range = br; if (br > range) range = br;
} }
} }
} }
} }
b = NULL;
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
/* if we have any unit in this region, then we get seen_unit access */
if (u->faction == f) { if (u->faction == f) {
add_seen_nb(f, r, seen_unit); add_seen_nb(f, r, seen_unit);
if (fval(r, RF_LIGHTHOUSE) && bt_lighthouse) { /* units inside the lighthouse get range based on their perception
if (u->building && u->building->type == bt_lighthouse && inside_building(u)) { * or the size, if perception is not a skill
int br = lighthouse_range(u->building, f); */
if (br > range) range = br; if (!fval(r, RF_LIGHTHOUSE)) {
/* it's enough to add the region once, and if there are
* no lighthouses, there is no need to look at more units */
break;
}
}
if (range == 0 && u->building && u->building->type == bt_lighthouse) {
if (u->building && b != u->building) {
b = u->building;
c = buildingcapacity(b);
br = 0;
}
c -= u->number;
if (u->faction == f && c >= 0) {
/* unit is one of ours, and inside the current lighthouse */
if (br == 0) {
/* lazy-calculate the range */
br = lighthouse_range(u->building, f, u);
}
if (br > range) {
range = br;
} }
} }
} }

View File

@ -6,6 +6,7 @@
#include "lighthouse.h" #include "lighthouse.h"
#include "travelthru.h" #include "travelthru.h"
#include "keyword.h" #include "keyword.h"
#include "spells.h"
#include <kernel/ally.h> #include <kernel/ally.h>
#include <kernel/config.h> #include <kernel/config.h>
@ -26,6 +27,7 @@
#include <util/lists.h> #include <util/lists.h>
#include <util/message.h> #include <util/message.h>
#include <attributes/attributes.h>
#include <attributes/key.h> #include <attributes/key.h>
#include <attributes/otherfaction.h> #include <attributes/otherfaction.h>
@ -474,12 +476,11 @@ void test_prepare_lighthouse_capacity(CuTest *tc) {
u1->number = 4; u1->number = 4;
u1->building = b; u1->building = b;
set_level(u1, SK_PERCEPTION, 3); set_level(u1, SK_PERCEPTION, 3);
CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction)); CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction, u1));
CuAssertPtrEquals(tc, b, inside_building(u1)); CuAssertPtrEquals(tc, b, inside_building(u1));
u2 = test_create_unit(f, r1); u2 = test_create_unit(f, r1);
u2->building = b; u2->building = b;
set_level(u2, SK_PERCEPTION, 3); set_level(u2, SK_PERCEPTION, 3);
CuAssertIntEquals(tc, 0, lighthouse_range(b, u2->faction));
CuAssertPtrEquals(tc, NULL, inside_building(u2)); CuAssertPtrEquals(tc, NULL, inside_building(u2));
prepare_report(&ctx, u1->faction); prepare_report(&ctx, u1->faction);
CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, r1, ctx.first);
@ -532,7 +533,12 @@ static void test_prepare_lighthouse(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static void test_prepare_lighthouse_owners(CuTest *tc) { /**
* In E3, region owners get the view range benefit of
* any lighthouse in the region.
*/
static void test_prepare_lighthouse_owners(CuTest *tc)
{
report_context ctx; report_context ctx;
faction *f; faction *f;
region *r1, *r2, *r3; region *r1, *r2, *r3;
@ -542,6 +548,7 @@ static void test_prepare_lighthouse_owners(CuTest *tc) {
const struct terrain_type *t_ocean, *t_plain; const struct terrain_type *t_ocean, *t_plain;
test_setup(); test_setup();
enable_skill(SK_PERCEPTION, false);
config_set("rules.region_owner_pay_building", "lighthouse"); config_set("rules.region_owner_pay_building", "lighthouse");
config_set("rules.region_owners", "1"); config_set("rules.region_owners", "1");
t_ocean = test_create_terrain("ocean", SEA_REGION); t_ocean = test_create_terrain("ocean", SEA_REGION);
@ -550,6 +557,7 @@ static void test_prepare_lighthouse_owners(CuTest *tc) {
r1 = test_create_region(0, 0, t_plain); r1 = test_create_region(0, 0, t_plain);
r2 = test_create_region(1, 0, t_ocean); r2 = test_create_region(1, 0, t_ocean);
r3 = test_create_region(2, 0, t_ocean); r3 = test_create_region(2, 0, t_ocean);
r3 = test_create_region(3, 0, t_ocean);
btype = test_create_buildingtype("lighthouse"); btype = test_create_buildingtype("lighthouse");
b = test_create_building(r1, btype); b = test_create_building(r1, btype);
b->flags |= BLD_MAINTAINED; b->flags |= BLD_MAINTAINED;
@ -558,8 +566,8 @@ static void test_prepare_lighthouse_owners(CuTest *tc) {
u = test_create_unit(f, r1); u = test_create_unit(f, r1);
u = test_create_unit(test_create_faction(0), r1); u = test_create_unit(test_create_faction(0), r1);
u->building = b; u->building = b;
set_level(u, SK_PERCEPTION, 3);
region_set_owner(b->region, f, 0); region_set_owner(b->region, f, 0);
CuAssertIntEquals(tc, 2, lighthouse_range(b, NULL, NULL));
prepare_report(&ctx, f); prepare_report(&ctx, f);
CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, r1, ctx.first);
CuAssertPtrEquals(tc, NULL, ctx.last); CuAssertPtrEquals(tc, NULL, ctx.last);
@ -707,6 +715,27 @@ static void test_region_distance_ql(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static void test_report_far_vision(CuTest *tc) {
faction *f;
region *r1, *r2;
test_setup();
f = test_create_faction(0);
r1 = test_create_region(0, 0, 0);
test_create_unit(f, r1);
r2 = test_create_region(10, 0, 0);
set_observer(r2, f, 10, 2);
CuAssertPtrEquals(tc, r1, f->first);
CuAssertPtrEquals(tc, r2, f->last);
report_context ctx;
prepare_report(&ctx, f);
CuAssertPtrEquals(tc, r1, ctx.first);
CuAssertPtrEquals(tc, 0, ctx.last);
CuAssertIntEquals(tc, seen_unit, r1->seen.mode);
CuAssertIntEquals(tc, seen_spell, r2->seen.mode);
finish_reports(&ctx);
test_cleanup();
}
CuSuite *get_reports_suite(void) CuSuite *get_reports_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
@ -724,6 +753,7 @@ CuSuite *get_reports_suite(void)
SUITE_ADD_TEST(suite, test_get_addresses); SUITE_ADD_TEST(suite, test_get_addresses);
SUITE_ADD_TEST(suite, test_get_addresses_fstealth); SUITE_ADD_TEST(suite, test_get_addresses_fstealth);
SUITE_ADD_TEST(suite, test_get_addresses_travelthru); SUITE_ADD_TEST(suite, test_get_addresses_travelthru);
SUITE_ADD_TEST(suite, test_report_far_vision);
SUITE_ADD_TEST(suite, test_reorder_units); SUITE_ADD_TEST(suite, test_reorder_units);
SUITE_ADD_TEST(suite, test_seen_faction); SUITE_ADD_TEST(suite, test_seen_faction);
SUITE_ADD_TEST(suite, test_regionid); SUITE_ADD_TEST(suite, test_regionid);

View File

@ -25,6 +25,8 @@
#include "monsters.h" #include "monsters.h"
#include "teleport.h" #include "teleport.h"
#include <attributes/attributes.h>
#include <spells/borders.h> #include <spells/borders.h>
#include <spells/buildingcurse.h> #include <spells/buildingcurse.h>
#include <spells/regioncurse.h> #include <spells/regioncurse.h>
@ -447,7 +449,7 @@ report_effect(region * r, unit * mage, message * seen, message * unseen)
* Vertrauten sehen, und durch den Vertrauten zaubern, allerdings nur * Vertrauten sehen, und durch den Vertrauten zaubern, allerdings nur
* mit seiner halben Stufe. Je nach Vertrautem erhaelt der Magier * mit seiner halben Stufe. Je nach Vertrautem erhaelt der Magier
* evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist
* zufaellig bestimmt, wird aber durch Magiegebiet und Rasse beeinflu<EFBFBD>t. * zufaellig bestimmt, wird aber durch Magiegebiet und Rasse beeinflußt.
* "Tierische" Vertraute brauchen keinen Unterhalt. * "Tierische" Vertraute brauchen keinen Unterhalt.
* *
* Ein paar Moeglichkeiten: * Ein paar Moeglichkeiten:
@ -1692,7 +1694,7 @@ static int sp_great_drought(castorder * co)
rsetterrain(r, T_OCEAN); rsetterrain(r, T_OCEAN);
/* Einheiten duerfen hier auf keinen Fall geloescht werden! */ /* Einheiten duerfen hier auf keinen Fall geloescht werden! */
for (u = r->units; u; u = u->next) { for (u = r->units; u; u = u->next) {
if (u_race(u) != get_race(RC_SPELL) && u->ship == 0) { if (!u->ship) {
set_number(u, 0); set_number(u, 0);
} }
} }
@ -4171,12 +4173,7 @@ static int sp_pump(castorder * co)
return cast_level / 2; return cast_level / 2;
} }
u = set_observer(rt, mage->faction, effskill(target, SK_PERCEPTION, 0), 2);
create_unit(rt, mage->faction, 1, get_race(RC_SPELL), 0,
"spell/pump", NULL);
u->age = 2;
set_level(u, SK_PERCEPTION, effskill(target, SK_PERCEPTION, 0));
return cast_level; return cast_level;
} }
@ -4776,7 +4773,7 @@ int sp_clonecopy(castorder * co)
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
int sp_dreamreading(castorder * co) int sp_dreamreading(castorder * co)
{ {
unit *u, *u2; unit *u;
region *r = co_get_region(co); region *r = co_get_region(co);
unit *mage = co->magician.u; unit *mage = co->magician.u;
int cast_level = co->level; int cast_level = co->level;
@ -4809,12 +4806,7 @@ int sp_dreamreading(castorder * co)
return 0; return 0;
} }
u2 = set_observer(u->region, mage->faction, effskill(u, SK_PERCEPTION, u->region), 2);
create_unit(u->region, mage->faction, 1, get_race(RC_SPELL), 0,
"spell/dreamreading", NULL);
set_number(u2, 1);
u2->age = 2; /* Nur fuer diese Runde. */
set_level(u2, SK_PERCEPTION, effskill(u, SK_PERCEPTION, u2->region));
msg = msg =
msg_message("sp_dreamreading_effect", "mage unit region", mage, u, msg_message("sp_dreamreading_effect", "mage unit region", mage, u,
@ -5609,7 +5601,6 @@ int sp_showastral(castorder * co)
region *r2 = rl2->data; region *r2 = rl2->data;
if (!is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) { if (!is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) {
for (u = r2->units; u; u = u->next) { for (u = r2->units; u; u = u->next) {
if (u_race(u) != get_race(RC_SPECIAL) && u_race(u) != get_race(RC_SPELL))
n++; n++;
} }
} }
@ -5619,7 +5610,8 @@ int sp_showastral(castorder * co)
/* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.", /* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.",
unitname(mage)); */ unitname(mage)); */
cmistake(mage, co->order, 220, MSG_MAGIC); cmistake(mage, co->order, 220, MSG_MAGIC);
} else { }
else {
/* Ausgeben */ /* Ausgeben */
@ -5629,7 +5621,6 @@ int sp_showastral(castorder * co)
for (rl2 = rl; rl2; rl2 = rl2->next) { for (rl2 = rl; rl2; rl2 = rl2->next) {
if (!is_cursed(rl2->data->attribs, C_ASTRALBLOCK, 0)) { if (!is_cursed(rl2->data->attribs, C_ASTRALBLOCK, 0)) {
for (u = rl2->data->units; u; u = u->next) { for (u = rl2->data->units; u; u = u->next) {
if (u_race(u) != get_race(RC_SPECIAL) && u_race(u) != get_race(RC_SPELL)) {
c++; c++;
scat(unitname(u)); scat(unitname(u));
scat(" ("); scat(" (");
@ -5645,9 +5636,9 @@ int sp_showastral(castorder * co)
scat(")"); scat(")");
if (c == n - 1) { if (c == n - 1) {
scat(" und "); scat(" und ");
} else if (c < n - 1) {
scat(", ");
} }
else if (c < n - 1) {
scat(", ");
} }
} }
} }
@ -5667,7 +5658,6 @@ int sp_showastral(castorder * co)
int sp_viewreality(castorder * co) int sp_viewreality(castorder * co)
{ {
region_list *rl, *rl2; region_list *rl, *rl2;
unit *u;
region *r = co_get_region(co); region *r = co_get_region(co);
unit *mage = co->magician.u; unit *mage = co->magician.u;
int cast_level = co->level; int cast_level = co->level;
@ -5686,11 +5676,7 @@ int sp_viewreality(castorder * co)
for (rl2 = rl; rl2; rl2 = rl2->next) { for (rl2 = rl; rl2; rl2 = rl2->next) {
region *rt = rl2->data; region *rt = rl2->data;
if (!is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { if (!is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) {
u = set_observer(rt, mage->faction, co->level / 2, 2);
create_unit(rt, mage->faction, 1, get_race(RC_SPELL), 0,
"spell/viewreality", NULL);
set_level(u, SK_PERCEPTION, co->level / 2);
u->age = 2;
} }
} }
@ -5769,7 +5755,6 @@ int sp_disruptastral(castorder * co)
if (trl != NULL) { if (trl != NULL) {
for (u = r2->units; u; u = u->next) { for (u = r2->units; u; u = u->next) {
if (u_race(u) != get_race(RC_SPELL)) {
region_list *trl2 = trl; region_list *trl2 = trl;
region *tr; region *tr;
int c = rng_int() % inhab_regions; int c = rng_int() % inhab_regions;
@ -5788,7 +5773,6 @@ int sp_disruptastral(castorder * co)
move_unit(u, tr, NULL); move_unit(u, tr, NULL);
} }
} }
}
free_regionlist(trl); free_regionlist(trl);
} }

View File

@ -25,11 +25,12 @@ extern "C" {
struct ship; struct ship;
struct curse; struct curse;
struct unit; struct unit;
struct faction;
struct region;
struct message; struct message;
void register_magicresistance(void); void register_magicresistance(void);
void register_spells(void); void register_spells(void);
void set_spelldata(struct spell *sp);
int sp_baddreams(castorder * co); int sp_baddreams(castorder * co);
int sp_gooddreams(castorder * co); int sp_gooddreams(castorder * co);

View File

@ -1,15 +1,20 @@
#include <platform.h> #include <platform.h>
#include "spells.h"
#include <kernel/config.h> #include <kernel/config.h>
#include <kernel/curse.h> #include <kernel/curse.h>
#include <kernel/faction.h> #include <kernel/faction.h>
#include <kernel/order.h> #include <kernel/order.h>
#include <kernel/race.h>
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/spell.h> #include <kernel/spell.h>
#include <kernel/unit.h> #include <kernel/unit.h>
#include <util/language.h> #include <util/language.h>
#include <util/attrib.h> #include <util/attrib.h>
#include <spells/regioncurse.h> #include <spells/regioncurse.h>
#include "spells.h"
#include <attributes/attributes.h>
#include <CuTest.h> #include <CuTest.h>
#include <tests.h> #include <tests.h>
@ -108,9 +113,26 @@ static void test_bad_dreams(CuTest *tc) {
test_cleanup(); test_cleanup();
} }
static void test_watch_region(CuTest *tc) {
region *r;
faction *f;
test_setup();
r = test_create_region(0, 0, 0);
f = test_create_faction(0);
CuAssertIntEquals(tc, -1, get_observer(r, f));
set_observer(r, f, 0, 2);
CuAssertIntEquals(tc, 0, get_observer(r, f));
set_observer(r, f, 10, 2);
CuAssertIntEquals(tc, 10, get_observer(r, f));
CuAssertIntEquals(tc, RF_OBSERVER, fval(r, RF_OBSERVER));
CuAssertPtrNotNull(tc, r->attribs);
test_cleanup();
}
CuSuite *get_spells_suite(void) CuSuite *get_spells_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_watch_region);
SUITE_ADD_TEST(suite, test_good_dreams); SUITE_ADD_TEST(suite, test_good_dreams);
SUITE_ADD_TEST(suite, test_bad_dreams); SUITE_ADD_TEST(suite, test_bad_dreams);
SUITE_ADD_TEST(suite, test_dreams); SUITE_ADD_TEST(suite, test_dreams);

View File

@ -256,6 +256,17 @@ static void scramble_fighters(selist * ql)
} }
} }
static bool select_armed(const side *vs, const fighter *fig, void *cbdata)
{
int row = get_unitrow(fig, vs);
UNUSED_ARG(cbdata);
if (row >= FIGHT_ROW && row < BEHIND_ROW) {
return fig->alive > 0 && fig->weapons;
}
return false;
}
/* Rosthauch */ /* Rosthauch */
int sp_combatrosthauch(struct castorder * co) int sp_combatrosthauch(struct castorder * co)
{ {
@ -275,22 +286,13 @@ int sp_combatrosthauch(struct castorder * co)
return 0; return 0;
} }
fgs = fighters(b, fi->side, FIGHT_ROW, BEHIND_ROW - 1, FS_ENEMY); fgs = select_fighters(b, fi->side, FS_ENEMY, select_armed, NULL);
scramble_fighters(fgs); scramble_fighters(fgs);
for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = fgs; force>0 && ql; selist_advance(&ql, &qi, 1)) {
fighter *df = (fighter *)selist_get(ql, qi); fighter *df = (fighter *)selist_get(ql, qi);
if (df->alive == 0)
continue;
if (force <= 0)
break;
/* da n MIN(force, x), sollte force maximal auf 0 sinken */
assert(force >= 0);
if (df->weapons) {
int w; int w;
for (w = 0; df->weapons[w].type != NULL; ++w) { for (w = 0; df->weapons[w].type != NULL; ++w) {
weapon *wp = df->weapons; weapon *wp = df->weapons;
int n = MIN(force, wp->used); int n = MIN(force, wp->used);
@ -326,7 +328,6 @@ int sp_combatrosthauch(struct castorder * co)
} }
} }
} }
}
selist_free(fgs); selist_free(fgs);
if (k == 0) { if (k == 0) {
@ -889,6 +890,13 @@ int sp_strong_wall(struct castorder * co)
return level; return level;
} }
static bool select_alive(const side *vs, const fighter *fig, void *cbdata)
{
UNUSED_ARG(vs);
UNUSED_ARG(cbdata);
return fig->alive > 0;
}
/** Spells: chaosrow / song of confusion. /** Spells: chaosrow / song of confusion.
* German Title: 'Gesang der Verwirrung' * German Title: 'Gesang der Verwirrung'
*/ */
@ -915,7 +923,7 @@ int sp_chaosrow(struct castorder * co)
power = chaosrow ? (power * 40) : get_force(power, 5); power = chaosrow ? (power * 40) : get_force(power, 5);
fgs = fighters(b, fi->side, FIGHT_ROW, NUMROWS, FS_ENEMY); fgs = select_fighters(b, fi->side, FS_ENEMY, select_alive, NULL);
scramble_fighters(fgs); scramble_fighters(fgs);
for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) {
@ -977,9 +985,18 @@ int sp_chaosrow(struct castorder * co)
return level; return level;
} }
static bool select_afraid(const side *vs, const fighter *fig, void *cbdata)
{
int row = get_unitrow(fig, vs);
UNUSED_ARG(cbdata);
if (row >= FIGHT_ROW && row <= AVOID_ROW) {
return fig->alive + fig->run.number < fig->unit->number;
}
return false;
}
/* Gesang der Furcht (Kampfzauber) */ /* Gesang der Furcht (Kampfzauber) */
/* Panik (Pr<50>kampfzauber) */ /* Panik (Pr<50>kampfzauber) */
int flee_spell(struct castorder * co, int strength) int flee_spell(struct castorder * co, int strength)
{ {
fighter * fi = co->magician.fig; fighter * fi = co->magician.fig;
@ -994,23 +1011,20 @@ int flee_spell(struct castorder * co, int strength)
int force; int force;
force = (int)get_force(power, strength); force = (int)get_force(power, strength);
if (!count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) { if (force<=0 || !count_enemies(b, fi, FIGHT_ROW, AVOID_ROW, SELECT_ADVANCE | SELECT_FIND)) {
msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp); msg = msg_message("sp_flee_effect_0", "mage spell", mage, sp);
message_all(b, msg); message_all(b, msg);
msg_release(msg); msg_release(msg);
return 0; return 0;
} }
fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY); fgs = select_fighters(b, fi->side, FS_ENEMY, select_afraid, NULL);
scramble_fighters(fgs); scramble_fighters(fgs);
for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = fgs; force > 0 && ql; selist_advance(&ql, &qi, 1)) {
fighter *df = (fighter *)selist_get(ql, qi); fighter *df = (fighter *)selist_get(ql, qi);
for (n = 0; n != df->alive; ++n) { for (n = 0; force > 0 && n != df->alive; ++n) {
if (force < 0)
break;
if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR m<>glich */ if (df->person[n].flags & FL_PANICED) { /* bei SPL_SONG_OF_FEAR m<>glich */
df->person[n].attack -= 1; df->person[n].attack -= 1;
--force; --force;
@ -1556,8 +1570,8 @@ int sp_healing(struct castorder * co)
message *msg; message *msg;
bool use_item = has_ao_healing(mage); bool use_item = has_ao_healing(mage);
/* bis zu 11 Personen pro Stufe (einen HP m<EFBFBD>ssen sie ja noch /* bis zu 11 Personen pro Stufe (einen HP muessen sie ja noch
* haben, sonst w<EFBFBD>ren sie tot) k<EFBFBD>nnen geheilt werden */ * haben, sonst waeren sie tot) koennen geheilt werden */
if (use_item) { if (use_item) {
healhp *= 2; healhp *= 2;
@ -1589,6 +1603,19 @@ int sp_healing(struct castorder * co)
return level; return level;
} }
static bool select_hero(const side *vs, const fighter *fig, void *cbdata)
{
UNUSED_ARG(cbdata);
if (playerrace(u_race(fig->unit))) {
int row = get_unitrow(fig, vs);
if (row >= FIGHT_ROW && row <= AVOID_ROW) {
return fig->alive + fig->run.number < fig->unit->number;
}
}
return false;
}
int sp_undeadhero(struct castorder * co) int sp_undeadhero(struct castorder * co)
{ {
fighter * fi = co->magician.fig; fighter * fi = co->magician.fig;
@ -1603,30 +1630,20 @@ int sp_undeadhero(struct castorder * co)
int force = (int)get_force(power, 0); int force = (int)get_force(power, 0);
double c = 0.50 + 0.02 * power; double c = 0.50 + 0.02 * power;
/* Liste aus allen K<EFBFBD>mpfern */ /* Liste aus allen Kaempfern */
fgs = fighters(b, fi->side, FIGHT_ROW, AVOID_ROW, FS_ENEMY | FS_HELP); fgs = select_fighters(b, fi->side, FS_ENEMY | FS_HELP, select_hero, NULL);
scramble_fighters(fgs); scramble_fighters(fgs);
for (qi = 0, ql = fgs; ql; selist_advance(&ql, &qi, 1)) { for (qi = 0, ql = fgs; ql && force>0; selist_advance(&ql, &qi, 1)) {
fighter *df = (fighter *)selist_get(ql, qi); fighter *df = (fighter *)selist_get(ql, qi);
unit *du = df->unit; unit *du = df->unit;
if (force <= 0)
break;
/* keine Monster */
if (!playerrace(u_race(du)))
continue;
if (df->alive + df->run.number < du->number) {
int j = 0; int j = 0;
/* Wieviele Untote k<>nnen wir aus dieser Einheit wecken? */ /* Wieviele Untote koennen wir aus dieser Einheit wecken? */
for (n = df->alive + df->run.number; n != du->number; n++) { for (n = df->alive + df->run.number; force>0 && n != du->number; n++) {
if (chance(c)) { if (chance(c)) {
++j; ++j;
if (--force <= 0) --force;
break;
} }
} }
@ -1678,7 +1695,6 @@ int sp_undeadhero(struct castorder * co)
undead += j; undead += j;
} }
} }
}
selist_free(fgs); selist_free(fgs);
level = MIN(level, undead); level = MIN(level, undead);

View File

@ -34,10 +34,11 @@
#define NOWATCH_VERSION 354 /* plane->watchers is gone */ #define NOWATCH_VERSION 354 /* plane->watchers is gone */
#define KEYVAL_VERSION 355 /* at_keys has values */ #define KEYVAL_VERSION 355 /* at_keys has values */
#define NOLANDITEM_VERSION 356 /* land_region has no items */ #define NOLANDITEM_VERSION 356 /* land_region has no items */
#define NORCSPELL_VERSION 357 /* data contains no RC_SPELL units */
/* unfinished: */ /* unfinished: */
#define CRYPT_VERSION 400 /* passwords are encrypted */ #define CRYPT_VERSION 400 /* passwords are encrypted */
#define RELEASE_VERSION NOLANDITEM_VERSION /* current datafile */ #define RELEASE_VERSION NORCSPELL_VERSION /* current datafile */
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */

View File

@ -82,7 +82,7 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic)
int i, dead = 0, hp_rem = 0, heiltrank; int i, dead = 0, hp_rem = 0, heiltrank;
assert(u->number); assert(u->number);
if (fval(u_race(u), RCF_ILLUSIONARY) || u_race(u) == get_race(RC_SPELL)) { if (fval(u_race(u), RCF_ILLUSIONARY)) {
return 0; return 0;
} }

View File

@ -3,6 +3,7 @@ IF EXIST ..\build-vs10 SET BUILD=..\build-vs10\eressea\Debug
IF EXIST ..\build-vs11 SET BUILD=..\build-vs11\eressea\Debug IF EXIST ..\build-vs11 SET BUILD=..\build-vs11\eressea\Debug
IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug IF EXIST ..\build-vs12 SET BUILD=..\build-vs12\eressea\Debug
IF EXIST ..\build-vs14 SET BUILD=..\build-vs14\eressea\Debug IF EXIST ..\build-vs14 SET BUILD=..\build-vs14\eressea\Debug
REM IF EXIST ..\build-vs15 SET BUILD=..\build-vs15\eressea\Debug
SET SERVER=%BUILD%\eressea.exe SET SERVER=%BUILD%\eressea.exe
%BUILD%\test_eressea.exe %BUILD%\test_eressea.exe
%SERVER% ..\scripts\run-tests.lua %SERVER% ..\scripts\run-tests.lua