forked from github/server
Merge pull request #695 from ennorehling/feature/refactor-rcspell
refactor observers, lighthouses
This commit is contained in:
commit
3306488641
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
75
src/battle.c
75
src/battle.c
|
@ -1654,11 +1654,51 @@ static castorder * create_castorder_combat(castorder *co, fighter *fig, const sp
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FFL_CURSED
|
||||||
|
static void summon_igjarjuk(battle *b, spellrank spellranks[]) {
|
||||||
|
side *s;
|
||||||
|
castorder *co;
|
||||||
|
|
||||||
|
for (s = b->sides; s != b->sides + b->nsides; ++s) {
|
||||||
|
fighter *fig = 0;
|
||||||
|
if (s->bf->attacker && fval(s->faction, FFL_CURSED)) {
|
||||||
|
spell *sp = find_spell("igjarjuk");
|
||||||
|
if (sp) {
|
||||||
|
int si;
|
||||||
|
for (si = 0; s->enemies[si]; ++si) {
|
||||||
|
side *se = s->enemies[si];
|
||||||
|
if (se && !fval(se->faction, FFL_NPC)) {
|
||||||
|
fighter *fi;
|
||||||
|
for (fi = se->fighters; fi; fi = fi->next) {
|
||||||
|
if (fi && (!fig || fig->unit->number > fi->unit->number)) {
|
||||||
|
fig = fi;
|
||||||
|
if (fig->unit->number == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fig && fig->unit->number == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fig) {
|
||||||
|
co = create_castorder_combat(0, fig, sp, 10, 10);
|
||||||
|
co->magician.fig = fig;
|
||||||
|
add_castorder(&spellranks[sp->rank], co);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void do_combatmagic(battle * b, combatmagic_t was)
|
void do_combatmagic(battle * b, combatmagic_t was)
|
||||||
{
|
{
|
||||||
side *s;
|
side *s;
|
||||||
region *r = b->region;
|
|
||||||
castorder *co;
|
castorder *co;
|
||||||
|
region *r = b->region;
|
||||||
int level, rank, sl;
|
int level, rank, sl;
|
||||||
spellrank spellranks[MAX_SPELLRANK];
|
spellrank spellranks[MAX_SPELLRANK];
|
||||||
|
|
||||||
|
@ -1666,38 +1706,7 @@ void do_combatmagic(battle * b, combatmagic_t was)
|
||||||
|
|
||||||
#ifdef FFL_CURSED
|
#ifdef FFL_CURSED
|
||||||
if (was == DO_PRECOMBATSPELL) {
|
if (was == DO_PRECOMBATSPELL) {
|
||||||
for (s = b->sides; s != b->sides + b->nsides; ++s) {
|
summon_igjarjuk(b, spellranks);
|
||||||
fighter *fig = 0;
|
|
||||||
if (s->bf->attacker && fval(s->faction, FFL_CURSED)) {
|
|
||||||
spell *sp = find_spell("igjarjuk");
|
|
||||||
if (sp) {
|
|
||||||
int si;
|
|
||||||
for (si = 0; s->enemies[si]; ++si) {
|
|
||||||
side *se = s->enemies[si];
|
|
||||||
if (se && !fval(se->faction, FFL_NPC)) {
|
|
||||||
fighter *fi;
|
|
||||||
for (fi = se->fighters; fi; fi = fi->next) {
|
|
||||||
if (fi && (!fig || fig->unit->number > fi->unit->number)) {
|
|
||||||
fig = fi;
|
|
||||||
if (fig->unit->number == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fig && fig->unit->number == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fig) {
|
|
||||||
co = create_castorder_combat(0, fig, sp, 10, 10);
|
|
||||||
co->magician.fig = fig;
|
|
||||||
add_castorder(&spellranks[sp->rank], co);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (s = b->sides; s != b->sides + b->nsides; ++s) {
|
for (s = b->sides; s != b->sides + b->nsides; ++s) {
|
||||||
|
|
10
src/chaos.c
10
src/chaos.c
|
@ -112,9 +112,7 @@ 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) {
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) &&
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,19 +1705,24 @@ int read_game(gamedata *data) {
|
||||||
while (--p >= 0) {
|
while (--p >= 0) {
|
||||||
unit *u = read_unit(data);
|
unit *u = read_unit(data);
|
||||||
|
|
||||||
if (data->version < JSON_REPORT_VERSION) {
|
if (data->version < NORCSPELL_VERSION && u_race(u) == rc_spell) {
|
||||||
if (u->_name && fval(u->faction, FFL_NPC)) {
|
set_observer(r, u->faction, get_level(u, SK_PERCEPTION), u->age);
|
||||||
if (!u->_name[0] || unit_name_equals_race(u)) {
|
free_unit(u);
|
||||||
unit_setname(u, NULL);
|
}
|
||||||
|
else {
|
||||||
|
if (data->version < JSON_REPORT_VERSION) {
|
||||||
|
if (u->_name && fval(u->faction, FFL_NPC)) {
|
||||||
|
if (!u->_name[0] || unit_name_equals_race(u)) {
|
||||||
|
unit_setname(u, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(u->region == NULL);
|
||||||
|
u->region = r;
|
||||||
|
*up = u;
|
||||||
|
up = &u->next;
|
||||||
|
update_interval(u->faction, r);
|
||||||
}
|
}
|
||||||
assert(u->region == NULL);
|
|
||||||
u->region = r;
|
|
||||||
*up = u;
|
|
||||||
up = &u->next;
|
|
||||||
|
|
||||||
update_interval(u->faction, r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */
|
if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -1896,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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
23
src/laws.c
23
src/laws.c
|
@ -153,23 +153,11 @@ static bool RemoveNMRNewbie(void)
|
||||||
|
|
||||||
static void age_unit(region * r, unit * u)
|
static void age_unit(region * r, unit * u)
|
||||||
{
|
{
|
||||||
static int rc_cache;
|
const race *rc = u_race(u);
|
||||||
static const race *rc_spell;
|
|
||||||
|
|
||||||
if (rc_changed(&rc_cache)) {
|
++u->age;
|
||||||
rc_spell = get_race(RC_SPELL);
|
if (u->number > 0 && rc->age_unit) {
|
||||||
}
|
rc->age_unit(u);
|
||||||
if (u_race(u) == rc_spell) {
|
|
||||||
if (--u->age <= 0) {
|
|
||||||
remove_unit(&r->units, u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const race *rc = u_race(u);
|
|
||||||
++u->age;
|
|
||||||
if (u->number > 0 && rc->age_unit) {
|
|
||||||
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;
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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 sk = effskill(u, SK_PERCEPTION, 0) / 3;
|
||||||
int c = 0;
|
assert(u->building == b);
|
||||||
int cap = buildingcapacity(b);
|
assert(u->faction == f);
|
||||||
unit *u, *uown = building_owner(b);
|
maxd = MIN(maxd, sk);
|
||||||
|
|
||||||
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;
|
|
||||||
d = MAX(d, sk);
|
|
||||||
d = MIN(maxd, d);
|
|
||||||
if (d == maxd)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (c)
|
|
||||||
break; /* first unit that's no longer in the house ends the search */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* E3A rule: no perception req'd */
|
|
||||||
return maxd;
|
|
||||||
}
|
}
|
||||||
|
/* E3A rule: no perception req'd */
|
||||||
|
return maxd;
|
||||||
}
|
}
|
||||||
return d;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_leuchtturm(region * r, faction * f)
|
bool check_leuchtturm(region * r, faction * f)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) &&
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
25
src/report.c
25
src/report.c
|
@ -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) {
|
report_region(out, r, f);
|
||||||
newline(out);
|
newline(out);
|
||||||
report_region(out, r, f);
|
report_travelthru(out, r, f);
|
||||||
newline(out);
|
|
||||||
guards(out, r, f);
|
|
||||||
newline(out);
|
|
||||||
report_travelthru(out, r, f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newline(out);
|
|
||||||
report_region(out, r, f);
|
|
||||||
newline(out);
|
|
||||||
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 */
|
||||||
|
|
|
@ -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) {
|
building *b;
|
||||||
|
int br = 0, c = 0, range = 0;
|
||||||
|
if (fval(r, RF_OBSERVER)) {
|
||||||
|
int skill = get_observer(r, f);
|
||||||
|
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)) {
|
if (rule_region_owners && f == region_get_owner(r)) {
|
||||||
/* region owners get the report from lighthouses */
|
for (b = rbuildings(r); b; b = b->next) {
|
||||||
building *b;
|
if (b && b->type == bt_lighthouse) {
|
||||||
|
/* region owners get maximm range */
|
||||||
for (b = r->buildings; b; b = b->next) {
|
int br = lighthouse_range(b, NULL, NULL);
|
||||||
if (b->type == bt_lighthouse) {
|
|
||||||
int br = lighthouse_range(b, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
260
src/spells.c
260
src/spells.c
|
@ -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>
|
||||||
|
@ -437,40 +439,40 @@ report_effect(region * r, unit * mage, message * seen, message * unseen)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ------------------------------------------------------------- */
|
/* ------------------------------------------------------------- */
|
||||||
/* Name: Vertrauter
|
/* Name: Vertrauter
|
||||||
* Stufe: 10
|
* Stufe: 10
|
||||||
*
|
*
|
||||||
* Wirkung:
|
* Wirkung:
|
||||||
* Der Magier beschwoert einen Vertrauten, ein kleines Tier, welches
|
* Der Magier beschwoert einen Vertrauten, ein kleines Tier, welches
|
||||||
* dem Magier zu Diensten ist. Der Magier kann durch die Augen des
|
* dem Magier zu Diensten ist. Der Magier kann durch die Augen des
|
||||||
* 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:
|
||||||
* Magieg. Rasse Besonderheiten
|
* Magieg. Rasse Besonderheiten
|
||||||
* Eule Tybied -/- fliegt, Auraregeneration
|
* Eule Tybied -/- fliegt, Auraregeneration
|
||||||
* Rabe Ilaun -/- fliegt
|
* Rabe Ilaun -/- fliegt
|
||||||
* Imp Draig -/- Magieresistenz?
|
* Imp Draig -/- Magieresistenz?
|
||||||
* Fuchs Gwyrrd -/- Wahrnehmung
|
* Fuchs Gwyrrd -/- Wahrnehmung
|
||||||
* ???? Cerddor -/- ???? (Singvogel?, Papagei?)
|
* ???? Cerddor -/- ???? (Singvogel?, Papagei?)
|
||||||
* Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch?
|
* Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch?
|
||||||
* Kraehe -/- -/- fliegt, +Tarnung (weil unauffaellig)
|
* Kraehe -/- -/- fliegt, +Tarnung (weil unauffaellig)
|
||||||
* Delphin -/- Meerm. schwimmt
|
* Delphin -/- Meerm. schwimmt
|
||||||
* Wolf -/- Ork
|
* Wolf -/- Ork
|
||||||
* Hund -/- Mensch kann evtl BEWACHE ausfuehren
|
* Hund -/- Mensch kann evtl BEWACHE ausfuehren
|
||||||
* Ratte -/- Goblin
|
* Ratte -/- Goblin
|
||||||
* Albatros -/- -/- fliegt, kann auf Ozean "landen"
|
* Albatros -/- -/- fliegt, kann auf Ozean "landen"
|
||||||
* Affe -/- -/- kann evtl BEKLAUE ausfuehren
|
* Affe -/- -/- kann evtl BEKLAUE ausfuehren
|
||||||
* Goblin -/- !Goblin normale Einheit
|
* Goblin -/- !Goblin normale Einheit
|
||||||
* Katze -/- !Katze normale Einheit
|
* Katze -/- !Katze normale Einheit
|
||||||
* Daemon -/- !Daemon normale Einheit
|
* Daemon -/- !Daemon normale Einheit
|
||||||
*
|
*
|
||||||
* Spezielle V. fuer Katzen, Trolle, Elfen, Daemonen, Insekten, Zwerge?
|
* Spezielle V. fuer Katzen, Trolle, Elfen, Daemonen, Insekten, Zwerge?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const race *select_familiar(const race * magerace, magic_t magiegebiet)
|
static const race *select_familiar(const race * magerace, magic_t magiegebiet)
|
||||||
{
|
{
|
||||||
|
@ -606,7 +608,7 @@ static int sp_summon_familiar(castorder * co)
|
||||||
if (dh == 0) {
|
if (dh == 0) {
|
||||||
bytes =
|
bytes =
|
||||||
strlcpy(bufp, (const char *)LOC(mage->faction->locale,
|
strlcpy(bufp, (const char *)LOC(mage->faction->locale,
|
||||||
"list_and"), size);
|
"list_and"), size);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bytes = strlcpy(bufp, (const char *)", ", size);
|
bytes = strlcpy(bufp, (const char *)", ", size);
|
||||||
|
@ -617,7 +619,7 @@ static int sp_summon_familiar(castorder * co)
|
||||||
}
|
}
|
||||||
bytes =
|
bytes =
|
||||||
strlcpy(bufp, (const char *)skillname((skill_t)sk, mage->faction->locale),
|
strlcpy(bufp, (const char *)skillname((skill_t)sk, mage->faction->locale),
|
||||||
size);
|
size);
|
||||||
assert(bytes <= INT_MAX);
|
assert(bytes <= INT_MAX);
|
||||||
if (wrptr(&bufp, &size, (int)bytes) != 0)
|
if (wrptr(&bufp, &size, (int)bytes) != 0)
|
||||||
WARN_STATIC_BUFFER();
|
WARN_STATIC_BUFFER();
|
||||||
|
@ -1249,7 +1251,7 @@ static void fumble_ents(const castorder * co)
|
||||||
* Flag:
|
* Flag:
|
||||||
* (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE)
|
* (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE)
|
||||||
*/
|
*/
|
||||||
/* Syntax: ZAUBER [REGION x y] [STUFE 2] "Rosthauch" 1111 2222 3333 */
|
/* Syntax: ZAUBER [REGION x y] [STUFE 2] "Rosthauch" 1111 2222 3333 */
|
||||||
|
|
||||||
typedef struct iron_weapon {
|
typedef struct iron_weapon {
|
||||||
const struct item_type *type;
|
const struct item_type *type;
|
||||||
|
@ -1262,7 +1264,7 @@ static iron_weapon *ironweapons = NULL;
|
||||||
|
|
||||||
void
|
void
|
||||||
add_ironweapon(const struct item_type *type, const struct item_type *rusty,
|
add_ironweapon(const struct item_type *type, const struct item_type *rusty,
|
||||||
float chance)
|
float chance)
|
||||||
{
|
{
|
||||||
iron_weapon *iweapon = malloc(sizeof(iron_weapon));
|
iron_weapon *iweapon = malloc(sizeof(iron_weapon));
|
||||||
assert_alloc(iweapon);
|
assert_alloc(iweapon);
|
||||||
|
@ -1369,7 +1371,7 @@ static int sp_rosthauch(castorder * co)
|
||||||
* Flag:
|
* Flag:
|
||||||
* (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE)
|
* (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE)
|
||||||
*/
|
*/
|
||||||
/* Syntax: ZAUBER [STUFE n] "Kaelteschutz" eh1 [eh2 [eh3 [...]]] */
|
/* Syntax: ZAUBER [STUFE n] "Kaelteschutz" eh1 [eh2 [eh3 [...]]] */
|
||||||
|
|
||||||
static int sp_kaelteschutz(castorder * co)
|
static int sp_kaelteschutz(castorder * co)
|
||||||
{
|
{
|
||||||
|
@ -1413,7 +1415,7 @@ static int sp_kaelteschutz(castorder * co)
|
||||||
u));
|
u));
|
||||||
if (u->faction != mage->faction)
|
if (u->faction != mage->faction)
|
||||||
ADDMSG(&u->faction->msgs, msg_message("heat_effect", "mage target",
|
ADDMSG(&u->faction->msgs, msg_message("heat_effect", "mage target",
|
||||||
cansee(u->faction, r, mage, 0) ? mage : NULL, u));
|
cansee(u->faction, r, mage, 0) ? mage : NULL, u));
|
||||||
i = cast_level;
|
i = cast_level;
|
||||||
}
|
}
|
||||||
/* Erstattung? */
|
/* Erstattung? */
|
||||||
|
@ -1433,7 +1435,7 @@ static int sp_kaelteschutz(castorder * co)
|
||||||
* Flag:
|
* Flag:
|
||||||
* (UNITSPELL | TESTCANSEE | SPELLLEVEL)
|
* (UNITSPELL | TESTCANSEE | SPELLLEVEL)
|
||||||
*/
|
*/
|
||||||
/* Syntax: ZAUBER "Funkenregen" eh1 */
|
/* Syntax: ZAUBER "Funkenregen" eh1 */
|
||||||
|
|
||||||
static int sp_sparkle(castorder * co)
|
static int sp_sparkle(castorder * co)
|
||||||
{
|
{
|
||||||
|
@ -1532,8 +1534,8 @@ static int sp_create_irongolem(castorder * co)
|
||||||
|
|
||||||
ADDMSG(&mage->faction->msgs,
|
ADDMSG(&mage->faction->msgs,
|
||||||
msg_message("magiccreate_effect", "region command unit amount object",
|
msg_message("magiccreate_effect", "region command unit amount object",
|
||||||
mage->region, co->order, mage, number,
|
mage->region, co->order, mage, number,
|
||||||
LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
|
LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
|
||||||
|
|
||||||
return cast_level;
|
return cast_level;
|
||||||
}
|
}
|
||||||
|
@ -1599,8 +1601,8 @@ static int sp_create_stonegolem(castorder * co)
|
||||||
|
|
||||||
ADDMSG(&mage->faction->msgs,
|
ADDMSG(&mage->faction->msgs,
|
||||||
msg_message("magiccreate_effect", "region command unit amount object",
|
msg_message("magiccreate_effect", "region command unit amount object",
|
||||||
mage->region, co->order, mage, number,
|
mage->region, co->order, mage, number,
|
||||||
LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
|
LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
|
||||||
|
|
||||||
return cast_level;
|
return cast_level;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2206,17 +2208,17 @@ static int sp_ironkeeper(castorder * co)
|
||||||
fset(keeper, UFL_ANON_FACTION);
|
fset(keeper, UFL_ANON_FACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
trigger *tkill = trigger_killunit(keeper);
|
trigger *tkill = trigger_killunit(keeper);
|
||||||
add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level + 2,
|
add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level + 2,
|
||||||
tkill));
|
tkill));
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = msg_message("summon_effect", "mage amount race", mage, 1, u_race(keeper));
|
msg = msg_message("summon_effect", "mage amount race", mage, 1, u_race(keeper));
|
||||||
r_addmessage(r, NULL, msg);
|
r_addmessage(r, NULL, msg);
|
||||||
msg_release(msg);
|
msg_release(msg);
|
||||||
|
|
||||||
return cast_level;
|
return cast_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------- */
|
/* ------------------------------------------------------------- */
|
||||||
|
@ -2387,7 +2389,7 @@ void patzer_peasantmob(const castorder * co)
|
||||||
|
|
||||||
u =
|
u =
|
||||||
create_unit(r, f, n, get_race(RC_PEASANT), 0, LOC(f->locale, "angry_mob"),
|
create_unit(r, f, n, get_race(RC_PEASANT), 0, LOC(f->locale, "angry_mob"),
|
||||||
NULL);
|
NULL);
|
||||||
fset(u, UFL_ISNEW);
|
fset(u, UFL_ISNEW);
|
||||||
addlist(&u->orders, create_order(K_GUARD, lang, NULL));
|
addlist(&u->orders, create_order(K_GUARD, lang, NULL));
|
||||||
set_order(&u->thisorder, default_order(lang));
|
set_order(&u->thisorder, default_order(lang));
|
||||||
|
@ -2459,7 +2461,7 @@ static int sp_forest_fire(castorder * co)
|
||||||
freset(u->faction, FFL_SELECT);
|
freset(u->faction, FFL_SELECT);
|
||||||
msg =
|
msg =
|
||||||
msg_message("forestfire_effect", "mage region amount", mage, r,
|
msg_message("forestfire_effect", "mage region amount", mage, r,
|
||||||
destroyed + vernichtet_schoesslinge);
|
destroyed + vernichtet_schoesslinge);
|
||||||
r_addmessage(r, NULL, msg);
|
r_addmessage(r, NULL, msg);
|
||||||
add_message(&mage->faction->msgs, msg);
|
add_message(&mage->faction->msgs, msg);
|
||||||
msg_release(msg);
|
msg_release(msg);
|
||||||
|
@ -2629,10 +2631,10 @@ static int sp_summondragon(castorder * co)
|
||||||
number = 6;
|
number = 6;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
trigger *tsummon = trigger_createunit(r, f, race, number);
|
trigger *tsummon = trigger_createunit(r, f, race, number);
|
||||||
add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon));
|
add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2707,15 +2709,15 @@ static int sp_firewall(castorder * co)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* melden, 1x pro Partei */
|
/* melden, 1x pro Partei */
|
||||||
{
|
{
|
||||||
message *seen = msg_message("firewall_effect", "mage region", mage, r);
|
message *seen = msg_message("firewall_effect", "mage region", mage, r);
|
||||||
message *unseen = msg_message("firewall_effect", "mage region", NULL, r);
|
message *unseen = msg_message("firewall_effect", "mage region", NULL, r);
|
||||||
report_effect(r, mage, seen, unseen);
|
report_effect(r, mage, seen, unseen);
|
||||||
msg_release(seen);
|
msg_release(seen);
|
||||||
msg_release(unseen);
|
msg_release(unseen);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cast_level;
|
return cast_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------- */
|
/* ------------------------------------------------------------- */
|
||||||
|
@ -2866,7 +2868,7 @@ static int dc_age(struct curse *c)
|
||||||
damage /= dmg.sa[1];
|
damage /= dmg.sa[1];
|
||||||
hp = change_hitpoints(u, -(int)damage);
|
hp = change_hitpoints(u, -(int)damage);
|
||||||
|
|
||||||
ADDMSG(&u->faction->msgs, msg_message((hp>0)?"poison_damage":"poison_death", "region unit", r, u));
|
ADDMSG(&u->faction->msgs, msg_message((hp > 0) ? "poison_damage" : "poison_death", "region unit", r, u));
|
||||||
if (*up == u)
|
if (*up == u)
|
||||||
up = &u->next;
|
up = &u->next;
|
||||||
}
|
}
|
||||||
|
@ -2923,7 +2925,7 @@ static int dc_read_compat(struct attrib *a, void *target, gamedata *data)
|
||||||
effect = strength;
|
effect = strength;
|
||||||
c =
|
c =
|
||||||
create_curse(u, &r->attribs, &ct_deathcloud, strength * 2, duration,
|
create_curse(u, &r->attribs, &ct_deathcloud, strength * 2, duration,
|
||||||
effect, 0);
|
effect, 0);
|
||||||
c->data.v = r;
|
c->data.v = r;
|
||||||
if (u == NULL) {
|
if (u == NULL) {
|
||||||
ur_add(var, &c->magician, resolve_unit);
|
ur_add(var, &c->magician, resolve_unit);
|
||||||
|
@ -3321,7 +3323,7 @@ static int sp_bloodsacrifice(castorder * co)
|
||||||
change_spellpoints(mage, aura);
|
change_spellpoints(mage, aura);
|
||||||
ADDMSG(&mage->faction->msgs,
|
ADDMSG(&mage->faction->msgs,
|
||||||
msg_message("sp_bloodsacrifice_effect",
|
msg_message("sp_bloodsacrifice_effect",
|
||||||
"unit region command amount", mage, mage->region, co->order, aura));
|
"unit region command amount", mage, mage->region, co->order, aura));
|
||||||
return cast_level;
|
return cast_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3815,7 +3817,7 @@ static int sp_raisepeasantmob(castorder * co)
|
||||||
|
|
||||||
u =
|
u =
|
||||||
create_unit(r, monsters, n, get_race(RC_PEASANT), 0, LOC(monsters->locale,
|
create_unit(r, monsters, n, get_race(RC_PEASANT), 0, LOC(monsters->locale,
|
||||||
"furious_mob"), NULL);
|
"furious_mob"), NULL);
|
||||||
fset(u, UFL_ISNEW);
|
fset(u, UFL_ISNEW);
|
||||||
setguard(u, true);
|
setguard(u, true);
|
||||||
a = a_new(&at_unitdissolve);
|
a = a_new(&at_unitdissolve);
|
||||||
|
@ -4128,7 +4130,7 @@ static int sp_bigrecruit(castorder * co)
|
||||||
* (UNITSPELL | TESTCANSEE)
|
* (UNITSPELL | TESTCANSEE)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* restistenz der einheit pruefen */
|
/* restistenz der einheit pruefen */
|
||||||
static int sp_pump(castorder * co)
|
static int sp_pump(castorder * co)
|
||||||
{
|
{
|
||||||
unit *u, *target;
|
unit *u, *target;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4400,7 +4397,7 @@ static int sp_raisepeasants(castorder * co)
|
||||||
|
|
||||||
u2 =
|
u2 =
|
||||||
create_unit(r, mage->faction, bauern, get_race(RC_PEASANT), 0,
|
create_unit(r, mage->faction, bauern, get_race(RC_PEASANT), 0,
|
||||||
LOC(mage->faction->locale, "furious_mob"), mage);
|
LOC(mage->faction->locale, "furious_mob"), mage);
|
||||||
|
|
||||||
fset(u2, UFL_LOCKED);
|
fset(u2, UFL_LOCKED);
|
||||||
if (rule_stealth_anon()) {
|
if (rule_stealth_anon()) {
|
||||||
|
@ -4414,7 +4411,7 @@ static int sp_raisepeasants(castorder * co)
|
||||||
|
|
||||||
msg =
|
msg =
|
||||||
msg_message("sp_raisepeasants_effect", "mage region amount", mage, r,
|
msg_message("sp_raisepeasants_effect", "mage region amount", mage, r,
|
||||||
u2->number);
|
u2->number);
|
||||||
r_addmessage(r, NULL, msg);
|
r_addmessage(r, NULL, msg);
|
||||||
if (mage->region != r) {
|
if (mage->region != r) {
|
||||||
add_message(&mage->faction->msgs, msg);
|
add_message(&mage->faction->msgs, msg);
|
||||||
|
@ -4527,7 +4524,7 @@ int sp_icastle(castorder * co)
|
||||||
if (type == bt_illusion) {
|
if (type == bt_illusion) {
|
||||||
b->size = (rng_int() % (int)((power * power) + 1) * 10);
|
b->size = (rng_int() % (int)((power * power) + 1) * 10);
|
||||||
}
|
}
|
||||||
else if (type->maxsize >0) {
|
else if (type->maxsize > 0) {
|
||||||
b->size = type->maxsize;
|
b->size = type->maxsize;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -4760,7 +4757,7 @@ int sp_clonecopy(castorder * co)
|
||||||
"clone_of"), unitname(mage));
|
"clone_of"), unitname(mage));
|
||||||
clone =
|
clone =
|
||||||
create_unit(target_region, mage->faction, 1, get_race(RC_CLONE), 0, name,
|
create_unit(target_region, mage->faction, 1, get_race(RC_CLONE), 0, name,
|
||||||
mage);
|
mage);
|
||||||
setstatus(clone, ST_FLEE);
|
setstatus(clone, ST_FLEE);
|
||||||
fset(clone, UFL_LOCKED);
|
fset(clone, UFL_LOCKED);
|
||||||
|
|
||||||
|
@ -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,16 +4806,11 @@ 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,
|
||||||
u->region);
|
u->region);
|
||||||
r_addmessage(r, mage->faction, msg);
|
r_addmessage(r, mage->faction, msg);
|
||||||
msg_release(msg);
|
msg_release(msg);
|
||||||
return cast_level;
|
return cast_level;
|
||||||
|
@ -5609,8 +5601,7 @@ 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,25 +5621,24 @@ 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(" (");
|
if (!fval(u, UFL_ANON_FACTION)) {
|
||||||
if (!fval(u, UFL_ANON_FACTION)) {
|
scat(factionname(u->faction));
|
||||||
scat(factionname(u->faction));
|
scat(", ");
|
||||||
scat(", ");
|
}
|
||||||
}
|
icat(u->number);
|
||||||
icat(u->number);
|
scat(" ");
|
||||||
scat(" ");
|
scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL)));
|
||||||
scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number==1) ? NAME_SINGULAR:NAME_PLURAL)));
|
scat(", Entfernung ");
|
||||||
scat(", Entfernung ");
|
icat(distance(rl2->data, rt));
|
||||||
icat(distance(rl2->data, rt));
|
scat(")");
|
||||||
scat(")");
|
if (c == n - 1) {
|
||||||
if (c == n - 1) {
|
scat(" und ");
|
||||||
scat(" und ");
|
}
|
||||||
} else if (c < n - 1) {
|
else if (c < n - 1) {
|
||||||
scat(", ");
|
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,24 +5755,22 @@ 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;
|
|
||||||
|
|
||||||
/* Zufaellige Zielregion suchen */
|
/* Zufaellige Zielregion suchen */
|
||||||
while (c-- != 0)
|
while (c-- != 0)
|
||||||
trl2 = trl2->next;
|
trl2 = trl2->next;
|
||||||
tr = trl2->data;
|
tr = trl2->data;
|
||||||
|
|
||||||
if (!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) {
|
if (!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) {
|
||||||
message *msg = msg_message("disrupt_astral", "unit region", u, tr);
|
message *msg = msg_message("disrupt_astral", "unit region", u, tr);
|
||||||
add_message(&u->faction->msgs, msg);
|
add_message(&u->faction->msgs, msg);
|
||||||
add_message(&tr->msgs, msg);
|
add_message(&tr->msgs, msg);
|
||||||
msg_release(msg);
|
msg_release(msg);
|
||||||
|
|
||||||
move_unit(u, tr, NULL);
|
move_unit(u, tr, NULL);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free_regionlist(trl);
|
free_regionlist(trl);
|
||||||
|
@ -6264,8 +6248,8 @@ int sp_speed2(castorder * co)
|
||||||
* Flags:
|
* Flags:
|
||||||
* (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE)
|
* (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE)
|
||||||
*/
|
*/
|
||||||
/* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft
|
/* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft
|
||||||
* (force) */
|
* (force) */
|
||||||
int sp_q_antimagie(castorder * co)
|
int sp_q_antimagie(castorder * co)
|
||||||
{
|
{
|
||||||
attrib **ap;
|
attrib **ap;
|
||||||
|
@ -6413,7 +6397,7 @@ int sp_break_curse(castorder * co)
|
||||||
/* Es wurde kein Ziel gefunden */
|
/* Es wurde kein Ziel gefunden */
|
||||||
ADDMSG(&mage->faction->msgs,
|
ADDMSG(&mage->faction->msgs,
|
||||||
msg_message("spelltargetnotfound", "unit region command",
|
msg_message("spelltargetnotfound", "unit region command",
|
||||||
mage, mage->region, co->order));
|
mage, mage->region, co->order));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* curse aufloesen, wenn zauber staerker (force > vigour) */
|
/* curse aufloesen, wenn zauber staerker (force > vigour) */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue