diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c index f699fdb57..610bea684 100644 --- a/src/attributes/attributes.c +++ b/src/attributes/attributes.c @@ -56,9 +56,98 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include +#include +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 = { "unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars @@ -123,6 +212,7 @@ void register_attributes(void) at_register(&at_stealth); at_register(&at_dict); at_register(&at_unitdissolve); + at_register(&at_observer); at_register(&at_overrideroads); at_register(&at_raceprefix); at_register(&at_iceberg); diff --git a/src/attributes/attributes.h b/src/attributes/attributes.h index 4ec3150ec..19b371dbb 100644 --- a/src/attributes/attributes.h +++ b/src/attributes/attributes.h @@ -23,8 +23,14 @@ extern "C" { #endif struct attrib_type; + struct region; + struct faction; + 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 } #endif diff --git a/src/battle.c b/src/battle.c index 5075b247b..343bd754a 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1654,11 +1654,51 @@ static castorder * create_castorder_combat(castorder *co, fighter *fig, const sp 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) { side *s; - region *r = b->region; castorder *co; + region *r = b->region; int level, rank, sl; spellrank spellranks[MAX_SPELLRANK]; @@ -1666,38 +1706,7 @@ void do_combatmagic(battle * b, combatmagic_t was) #ifdef FFL_CURSED if (was == DO_PRECOMBATSPELL) { - 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; - } - } - } - } + summon_igjarjuk(b, spellranks); } #endif for (s = b->sides; s != b->sides + b->nsides; ++s) { diff --git a/src/chaos.c b/src/chaos.c index 58cd7af73..5628b68f4 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -112,9 +112,7 @@ static unit *random_unit(const region * r) unit *u; 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) { @@ -125,9 +123,7 @@ static unit *random_unit(const region * r) u = r->units; while (u && c < n) { - if (u_race(u) != get_race(RC_SPELL)) { - c += u->number; - } + c += u->number; u = u->next; } @@ -203,7 +199,7 @@ static void chaos(region * r) for (up = &r->units; *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", "region unit", r, u)); remove_unit(up, u); diff --git a/src/creport.c b/src/creport.c index 10dc4c323..00d55b6ce 100644 --- a/src/creport.c +++ b/src/creport.c @@ -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_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)) { /* Sonderbehandlung Teleport-Ebene */ 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); - if (r->seen.mode == seen_unit || r->seen.mode == seen_travel) { + if (r->seen.mode >= seen_travel) { message_list *mlist = r_getmessages(r, f); cr_output_messages(F, r->msgs, f); if (mlist) { diff --git a/src/economy.c b/src/economy.c index 0bc056b56..3e13f0643 100644 --- a/src/economy.c +++ b/src/economy.c @@ -2978,13 +2978,12 @@ void produce(struct region *r) static int bt_cache; static const struct building_type *caravan_bt; 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)) { caravan_bt = bt_find("caravan"); } if (rc_changed(&rc_cache)) { - rc_spell = get_race(RC_SPELL); rc_insect = get_race(RC_INSECT); rc_aquarian = get_race(RC_AQUARIAN); } @@ -3022,7 +3021,7 @@ void produce(struct region *r) bool trader = false; keyword_t todo; - if (u_race(u) == rc_spell || fval(u, UFL_LONGACTION)) + if (fval(u, UFL_LONGACTION)) continue; if (u_race(u) == rc_insect && r_insectstalled(r) && diff --git a/src/give.c b/src/give.c index 060ecbbb9..26b193d0d 100644 --- a/src/give.c +++ b/src/give.c @@ -667,12 +667,7 @@ void give_cmd(unit * u, order * ord) return; } - if (u2 && u_race(u2) == get_race(RC_SPELL)) { - 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)) { + if (u2 && !alliedunit(u2, u->faction, HELP_GIVE) && !ucontact(u2, u)) { cmistake(u, ord, 40, MSG_COMMERCE); return; } diff --git a/src/kernel/race.c b/src/kernel/race.c index a2c37d87d..14fb66331 100644 --- a/src/kernel/race.c +++ b/src/kernel/race.c @@ -69,7 +69,7 @@ const char *racenames[MAXRACES] = { "dwarf", "elf", NULL, "goblin", "human", "troll", "demon", "insect", "halfling", "cat", "aquarian", "orc", "snotling", "undead", NULL, "youngdragon", "dragon", "wyrm", "ent", "catdragon", "dracoid", - NULL, "spell", "irongolem", "stonegolem", "shadowdemon", + NULL, NULL, "irongolem", "stonegolem", "shadowdemon", "shadowmaster", "mountainguard", "alp", "toad", "braineater", "peasant", "wolf", NULL, NULL, NULL, NULL, "songdragon", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, diff --git a/src/kernel/race.h b/src/kernel/race.h index 5c320ea05..a8933dae8 100644 --- a/src/kernel/race.h +++ b/src/kernel/race.h @@ -72,8 +72,7 @@ extern "C" { RC_BIRTHDAYDRAGON, RC_DRACOID, - RC_SPELL = 22, - RC_IRONGOLEM, + RC_IRONGOLEM = 23, RC_STONEGOLEM, RC_SHADOW, RC_SHADOWLORD, diff --git a/src/kernel/region.h b/src/kernel/region.h index e3204d129..5117567a6 100644 --- a/src/kernel/region.h +++ b/src/kernel/region.h @@ -35,7 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define RF_MALLORN (1<<1) /* 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_5 (1<<5) #define RF_UNUSED_6 (1<<6) diff --git a/src/kernel/save.c b/src/kernel/save.c index 39f2a129f..7a9e12286 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -48,6 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "lighthouse.h" /* attributes includes */ +#include #include #include @@ -131,8 +132,6 @@ static unit *unitorders(FILE * F, int enc, struct faction *f) i = getid(); u = findunitg(i, NULL); - if (u && u_race(u) == get_race(RC_SPELL)) - return NULL; if (u && u->faction == f) { order **ordp; @@ -851,7 +850,7 @@ void write_unit(struct gamedata *data, const unit * u) WRITE_SECTION(data->store); write_items(data->store, u->items); 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)); ((unit *)u)->hp = u->number; } @@ -1621,6 +1620,7 @@ int read_game(gamedata *data) { int rmax = maxregions; storage * store = data->store; const struct building_type *bt_lighthouse = bt_find("lighthouse"); + const struct race *rc_spell = rc_find("spell"); if (data->version >= SAVEGAMEID_VERSION) { int gameid; @@ -1705,19 +1705,24 @@ int read_game(gamedata *data) { while (--p >= 0) { unit *u = read_unit(data); - 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); + 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 (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 */ diff --git a/src/kernel/types.h b/src/kernel/types.h index b9faefce7..a20e923d6 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -71,8 +71,8 @@ typedef enum { seen_neighbour, seen_lighthouse, seen_travel, - seen_far, seen_unit, + seen_spell, seen_battle } seen_mode; diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 75daf2417..e6b8757ef 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1896,8 +1896,7 @@ void remove_empty_units_in_region(region * r) set_number(u, 0); } } - if ((u->number == 0 && u_race(u) != get_race(RC_SPELL)) || (u->age <= 0 - && u_race(u) == get_race(RC_SPELL))) { + if (u->number == 0) { remove_unit(up, u); } if (*up == u) diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index 096da9d62..54b07b63f 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -97,26 +97,6 @@ static void test_remove_units_with_dead_faction(CuTest *tc) { 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) { unit *u; 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_remove_unit); 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_with_dead_faction); SUITE_ADD_TEST(suite, test_remove_empty_units_in_region); diff --git a/src/laws.c b/src/laws.c index e16cbd583..ba088a8bb 100644 --- a/src/laws.c +++ b/src/laws.c @@ -153,23 +153,11 @@ static bool RemoveNMRNewbie(void) static void age_unit(region * r, unit * u) { - static int rc_cache; - static const race *rc_spell; + const race *rc = u_race(u); - 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); - ++u->age; - if (u->number > 0 && rc->age_unit) { - rc->age_unit(u); - } + ++u->age; + if (u->number > 0 && rc->age_unit) { + rc->age_unit(u); } if (u->region && is_astral(u->region)) { item **itemp = &u->items; @@ -2676,8 +2664,7 @@ int guard_on_cmd(unit * u, struct order *ord) if (fval(u, UFL_MOVED)) { cmistake(u, ord, 187, MSG_EVENT); } - else if (fval(u_race(u), RCF_ILLUSIONARY) - || u_race(u) == get_race(RC_SPELL)) { + else if (fval(u_race(u), RCF_ILLUSIONARY)) { cmistake(u, ord, 95, MSG_EVENT); } else { diff --git a/src/lighthouse.c b/src/lighthouse.c index d29d58b71..ad0cd639a 100644 --- a/src/lighthouse.c +++ b/src/lighthouse.c @@ -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) { int maxd = (int)log10(b->size) + 1; - if (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; - 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; + if (u && skill_enabled(SK_PERCEPTION)) { + int sk = effskill(u, SK_PERCEPTION, 0) / 3; + assert(u->building == b); + assert(u->faction == f); + maxd = MIN(maxd, sk); } + /* E3A rule: no perception req'd */ + return maxd; } - return d; + return 0; } bool check_leuchtturm(region * r, faction * f) diff --git a/src/lighthouse.h b/src/lighthouse.h index f2974ddf1..2eacfb7d8 100644 --- a/src/lighthouse.h +++ b/src/lighthouse.h @@ -29,14 +29,15 @@ extern "C" { struct faction; struct region; struct building; + struct unit; struct attrib; extern struct attrib_type at_lighthouse; /* leuchtturm */ bool check_leuchtturm(struct region *r, struct faction *f); - void update_lighthouse(struct building *lh); - int lighthouse_range(const struct building *b, - const struct faction *f); + void update_lighthouse(struct building *b); + int lighthouse_range(const struct building *b, const struct faction *f, + const struct unit *u); #ifdef __cplusplus diff --git a/src/lighthouse.test.c b/src/lighthouse.test.c index e06941bc1..6105d25b9 100644 --- a/src/lighthouse.test.c +++ b/src/lighthouse.test.c @@ -26,29 +26,29 @@ static void test_lighthouse_range(CuTest * tc) u1 = 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")); - CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL)); - CuAssertIntEquals(tc, 0, lighthouse_range(b, u1->faction)); + CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL)); + CuAssertIntEquals(tc, 0, lighthouse_range(b, u1->faction, NULL)); b->size = 10; - CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL)); + CuAssertIntEquals(tc, 0, lighthouse_range(b, NULL, NULL)); u1->building = b; u2->building = b; u1->number = 10; set_level(u1, 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; - CuAssertIntEquals(tc, 1, lighthouse_range(b, NULL)); + CuAssertIntEquals(tc, 1, lighthouse_range(b, u1->faction, u1)); set_level(u1, SK_PERCEPTION, 6); - CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction)); - CuAssertIntEquals(tc, 0, lighthouse_range(b, u2->faction)); + CuAssertIntEquals(tc, 2, lighthouse_range(b, u1->faction, u1)); + /* lighthouse_range does not check inside_building */ + CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2)); b->size = 100; 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); - CuAssertIntEquals(tc, 3, lighthouse_range(b, NULL)); - CuAssertIntEquals(tc, 3, lighthouse_range(b, u1->faction)); - CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction)); - CuAssertIntEquals(tc, 0, lighthouse_range(b, test_create_faction(0))); + CuAssertIntEquals(tc, 3, lighthouse_range(b, u1->faction, u1)); + CuAssertIntEquals(tc, 1, lighthouse_range(b, u2->faction, u2)); test_cleanup(); } diff --git a/src/magic.c b/src/magic.c index 81a797e1c..3cac1cb52 100644 --- a/src/magic.c +++ b/src/magic.c @@ -1209,9 +1209,6 @@ target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus) skill *sv; unit *u = (unit *)obj; - if (u_race(u)==get_race(RC_SPELL)) { - return true; - } at = effskill(magician, SK_MAGIC, 0); for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) { @@ -2780,7 +2777,6 @@ void magic(void) int rank; castorder *co; spellrank spellranks[MAX_SPELLRANK]; - const race *rc_spell = get_race(RC_SPELL); const race *rc_insect = get_race(RC_INSECT); memset(spellranks, 0, sizeof(spellranks)); @@ -2790,7 +2786,7 @@ void magic(void) for (u = r->units; u; u = u->next) { order *ord; - if (u->number <= 0 || u_race(u) == rc_spell) + if (u->number <= 0) continue; if (u_race(u) == rc_insect && r_insectstalled(r) && diff --git a/src/randenc.c b/src/randenc.c index ab51d64b1..e46d5fa24 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -418,7 +418,7 @@ void drown(region * r) while (*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); ADDMSG(&u->faction->msgs, msg_message("drown", "unit region", u, r)); } diff --git a/src/report.c b/src/report.c index 188d8dfa9..2e11b4e09 100644 --- a/src/report.c +++ b/src/report.c @@ -997,7 +997,7 @@ void report_region(struct stream *out, const region * r, faction * f) } /* iron & stone */ - if (r->seen.mode == seen_unit) { + if (r->seen.mode >= seen_unit) { resource_report result[MAX_RAWMATERIALS]; 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; 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)) { /* Sonderbehandlung Teleport-Ebene */ region_list *rl = astralregions(r, inhabitable); @@ -2272,7 +2272,7 @@ report_plaintext(const char *filename, report_context * ctx, continue; /* Beschreibung */ - if (r->seen.mode == seen_unit) { + if (r->seen.mode >= seen_unit) { anyunits = 1; newline(out); report_region(out, r, f); @@ -2305,24 +2305,13 @@ report_plaintext(const char *filename, report_context * ctx, report_travelthru(out, r, f); } 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); - newline(out); - report_travelthru(out, r, f); - } + report_region(out, r, f); + newline(out); + report_travelthru(out, r, f); } /* Statistik */ - if (wants_stats && r->seen.mode == seen_unit) + if (wants_stats && r->seen.mode >= seen_unit) statistics(out, r, f); /* Nachrichten an REGION in der Region */ diff --git a/src/reports.c b/src/reports.c index ba0302c6c..8287e41f7 100644 --- a/src/reports.c +++ b/src/reports.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "reports.h" #include "guard.h" #include "laws.h" +#include "spells.h" #include "travelthru.h" #include "lighthouse.h" #include "donations.h" @@ -69,6 +70,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include /* attributes includes */ +#include #include #include #include @@ -93,6 +95,7 @@ const char *visibility[] = { "travel", "far", "unit", + "spell", "battle" }; @@ -966,9 +969,9 @@ const struct unit *ucansee(const struct faction *f, const struct unit *u, int stealth_modifier(seen_mode mode) { switch (mode) { + case seen_spell: case seen_unit: return 0; - case seen_far: case seen_lighthouse: return -2; case seen_travel: @@ -1422,28 +1425,57 @@ void prepare_report(report_context *ctx, faction *f) ctx->last = lastregion(f); for (r = ctx->first; r!=ctx->last; r = r->next) { - int range = 0; 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)) { - /* region owners get the report from lighthouses */ - building *b; - - for (b = r->buildings; b; b = b->next) { - if (b->type == bt_lighthouse) { - int br = lighthouse_range(b, NULL); + 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; } } } } + + b = NULL; 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) { add_seen_nb(f, r, seen_unit); - if (fval(r, RF_LIGHTHOUSE) && bt_lighthouse) { - if (u->building && u->building->type == bt_lighthouse && inside_building(u)) { - int br = lighthouse_range(u->building, f); - if (br > range) range = br; + /* units inside the lighthouse get range based on their perception + * or the size, if perception is not a skill + */ + 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; } } } diff --git a/src/reports.test.c b/src/reports.test.c index 9d7944258..74750e9b5 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -6,6 +6,7 @@ #include "lighthouse.h" #include "travelthru.h" #include "keyword.h" +#include "spells.h" #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include @@ -474,12 +476,11 @@ void test_prepare_lighthouse_capacity(CuTest *tc) { u1->number = 4; u1->building = b; 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)); u2 = test_create_unit(f, r1); u2->building = b; set_level(u2, SK_PERCEPTION, 3); - CuAssertIntEquals(tc, 0, lighthouse_range(b, u2->faction)); CuAssertPtrEquals(tc, NULL, inside_building(u2)); prepare_report(&ctx, u1->faction); CuAssertPtrEquals(tc, r1, ctx.first); @@ -532,7 +533,12 @@ static void test_prepare_lighthouse(CuTest *tc) { 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; faction *f; region *r1, *r2, *r3; @@ -542,6 +548,7 @@ static void test_prepare_lighthouse_owners(CuTest *tc) { const struct terrain_type *t_ocean, *t_plain; test_setup(); + enable_skill(SK_PERCEPTION, false); config_set("rules.region_owner_pay_building", "lighthouse"); config_set("rules.region_owners", "1"); 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); r2 = test_create_region(1, 0, t_ocean); r3 = test_create_region(2, 0, t_ocean); + r3 = test_create_region(3, 0, t_ocean); btype = test_create_buildingtype("lighthouse"); b = test_create_building(r1, btype); 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(test_create_faction(0), r1); u->building = b; - set_level(u, SK_PERCEPTION, 3); region_set_owner(b->region, f, 0); + CuAssertIntEquals(tc, 2, lighthouse_range(b, NULL, NULL)); prepare_report(&ctx, f); CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, NULL, ctx.last); @@ -707,6 +715,27 @@ static void test_region_distance_ql(CuTest *tc) { 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 *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_fstealth); 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_seen_faction); SUITE_ADD_TEST(suite, test_regionid); diff --git a/src/spells.c b/src/spells.c index b1e5c0591..fc8c445b7 100644 --- a/src/spells.c +++ b/src/spells.c @@ -1,4 +1,4 @@ -/* +/* * * * Eressea PB(E)M host Copyright (C) 1998-2015 @@ -25,6 +25,8 @@ #include "monsters.h" #include "teleport.h" +#include + #include #include #include @@ -437,40 +439,40 @@ report_effect(region * r, unit * mage, message * seen, message * unseen) * */ -/* ------------------------------------------------------------- */ -/* Name: Vertrauter - * Stufe: 10 - * - * Wirkung: - * Der Magier beschwoert einen Vertrauten, ein kleines Tier, welches - * dem Magier zu Diensten ist. Der Magier kann durch die Augen des - * Vertrauten sehen, und durch den Vertrauten zaubern, allerdings nur - * mit seiner halben Stufe. Je nach Vertrautem erhaelt der Magier - * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist - * zufaellig bestimmt, wird aber durch Magiegebiet und Rasse beeinflu�t. - * "Tierische" Vertraute brauchen keinen Unterhalt. - * - * Ein paar Moeglichkeiten: - * Magieg. Rasse Besonderheiten - * Eule Tybied -/- fliegt, Auraregeneration - * Rabe Ilaun -/- fliegt - * Imp Draig -/- Magieresistenz? - * Fuchs Gwyrrd -/- Wahrnehmung - * ???? Cerddor -/- ???? (Singvogel?, Papagei?) - * Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch? - * Kraehe -/- -/- fliegt, +Tarnung (weil unauffaellig) - * Delphin -/- Meerm. schwimmt - * Wolf -/- Ork - * Hund -/- Mensch kann evtl BEWACHE ausfuehren - * Ratte -/- Goblin - * Albatros -/- -/- fliegt, kann auf Ozean "landen" - * Affe -/- -/- kann evtl BEKLAUE ausfuehren - * Goblin -/- !Goblin normale Einheit - * Katze -/- !Katze normale Einheit - * Daemon -/- !Daemon normale Einheit - * - * Spezielle V. fuer Katzen, Trolle, Elfen, Daemonen, Insekten, Zwerge? - */ + /* ------------------------------------------------------------- */ + /* Name: Vertrauter + * Stufe: 10 + * + * Wirkung: + * Der Magier beschwoert einen Vertrauten, ein kleines Tier, welches + * dem Magier zu Diensten ist. Der Magier kann durch die Augen des + * Vertrauten sehen, und durch den Vertrauten zaubern, allerdings nur + * mit seiner halben Stufe. Je nach Vertrautem erhaelt der Magier + * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist + * zufaellig bestimmt, wird aber durch Magiegebiet und Rasse beeinflußt. + * "Tierische" Vertraute brauchen keinen Unterhalt. + * + * Ein paar Moeglichkeiten: + * Magieg. Rasse Besonderheiten + * Eule Tybied -/- fliegt, Auraregeneration + * Rabe Ilaun -/- fliegt + * Imp Draig -/- Magieresistenz? + * Fuchs Gwyrrd -/- Wahrnehmung + * ???? Cerddor -/- ???? (Singvogel?, Papagei?) + * Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch? + * Kraehe -/- -/- fliegt, +Tarnung (weil unauffaellig) + * Delphin -/- Meerm. schwimmt + * Wolf -/- Ork + * Hund -/- Mensch kann evtl BEWACHE ausfuehren + * Ratte -/- Goblin + * Albatros -/- -/- fliegt, kann auf Ozean "landen" + * Affe -/- -/- kann evtl BEKLAUE ausfuehren + * Goblin -/- !Goblin normale Einheit + * Katze -/- !Katze normale Einheit + * Daemon -/- !Daemon normale Einheit + * + * Spezielle V. fuer Katzen, Trolle, Elfen, Daemonen, Insekten, Zwerge? + */ 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) { bytes = strlcpy(bufp, (const char *)LOC(mage->faction->locale, - "list_and"), size); + "list_and"), size); } else { bytes = strlcpy(bufp, (const char *)", ", size); @@ -617,7 +619,7 @@ static int sp_summon_familiar(castorder * co) } bytes = strlcpy(bufp, (const char *)skillname((skill_t)sk, mage->faction->locale), - size); + size); assert(bytes <= INT_MAX); if (wrptr(&bufp, &size, (int)bytes) != 0) WARN_STATIC_BUFFER(); @@ -1249,7 +1251,7 @@ static void fumble_ents(const castorder * co) * Flag: * (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 { const struct item_type *type; @@ -1262,7 +1264,7 @@ static iron_weapon *ironweapons = NULL; void add_ironweapon(const struct item_type *type, const struct item_type *rusty, -float chance) + float chance) { iron_weapon *iweapon = malloc(sizeof(iron_weapon)); assert_alloc(iweapon); @@ -1369,7 +1371,7 @@ static int sp_rosthauch(castorder * co) * Flag: * (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) { @@ -1413,7 +1415,7 @@ static int sp_kaelteschutz(castorder * co) u)); if (u->faction != mage->faction) 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; } /* Erstattung? */ @@ -1433,7 +1435,7 @@ static int sp_kaelteschutz(castorder * co) * Flag: * (UNITSPELL | TESTCANSEE | SPELLLEVEL) */ -/* Syntax: ZAUBER "Funkenregen" eh1 */ + /* Syntax: ZAUBER "Funkenregen" eh1 */ static int sp_sparkle(castorder * co) { @@ -1506,11 +1508,11 @@ static int sp_create_irongolem(castorder * co) int number = lovar(force * 8 * RESOURCE_QUANTITY); static int cache; static const race * golem_rc; - + if (rc_changed(&cache)) { golem_rc = rc_find("irongolem"); } - + if (number < 1) { number = 1; } @@ -1532,8 +1534,8 @@ static int sp_create_irongolem(castorder * co) ADDMSG(&mage->faction->msgs, msg_message("magiccreate_effect", "region command unit amount object", - mage->region, co->order, mage, number, - LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); + mage->region, co->order, mage, number, + LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); return cast_level; } @@ -1575,7 +1577,7 @@ static int sp_create_stonegolem(castorder * co) int number = lovar(co->force * 5 * RESOURCE_QUANTITY); static int cache; static const race * golem_rc; - + if (rc_changed(&cache)) { golem_rc = rc_find("stonegolem"); } @@ -1599,8 +1601,8 @@ static int sp_create_stonegolem(castorder * co) ADDMSG(&mage->faction->msgs, msg_message("magiccreate_effect", "region command unit amount object", - mage->region, co->order, mage, number, - LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); + mage->region, co->order, mage, number, + LOC(mage->faction->locale, rc_name_s(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); return cast_level; } @@ -1692,7 +1694,7 @@ static int sp_great_drought(castorder * co) rsetterrain(r, T_OCEAN); /* Einheiten duerfen hier auf keinen Fall geloescht werden! */ 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); } } @@ -2206,17 +2208,17 @@ static int sp_ironkeeper(castorder * co) fset(keeper, UFL_ANON_FACTION); } - { - trigger *tkill = trigger_killunit(keeper); - add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level + 2, - tkill)); - } + { + trigger *tkill = trigger_killunit(keeper); + add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level + 2, + tkill)); + } - msg = msg_message("summon_effect", "mage amount race", mage, 1, u_race(keeper)); - r_addmessage(r, NULL, msg); - msg_release(msg); + msg = msg_message("summon_effect", "mage amount race", mage, 1, u_race(keeper)); + r_addmessage(r, NULL, msg); + msg_release(msg); - return cast_level; + return cast_level; } /* ------------------------------------------------------------- */ @@ -2387,7 +2389,7 @@ void patzer_peasantmob(const castorder * co) u = create_unit(r, f, n, get_race(RC_PEASANT), 0, LOC(f->locale, "angry_mob"), - NULL); + NULL); fset(u, UFL_ISNEW); addlist(&u->orders, create_order(K_GUARD, lang, NULL)); set_order(&u->thisorder, default_order(lang)); @@ -2459,7 +2461,7 @@ static int sp_forest_fire(castorder * co) freset(u->faction, FFL_SELECT); msg = msg_message("forestfire_effect", "mage region amount", mage, r, - destroyed + vernichtet_schoesslinge); + destroyed + vernichtet_schoesslinge); r_addmessage(r, NULL, msg); add_message(&mage->faction->msgs, msg); msg_release(msg); @@ -2629,10 +2631,10 @@ static int sp_summondragon(castorder * co) number = 6; break; } - { - trigger *tsummon = trigger_createunit(r, f, race, number); - add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon)); - } + { + trigger *tsummon = trigger_createunit(r, f, race, number); + add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon)); + } } } @@ -2707,15 +2709,15 @@ static int sp_firewall(castorder * co) } /* melden, 1x pro Partei */ - { - message *seen = msg_message("firewall_effect", "mage region", mage, r); - message *unseen = msg_message("firewall_effect", "mage region", NULL, r); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } + { + message *seen = msg_message("firewall_effect", "mage region", mage, r); + message *unseen = msg_message("firewall_effect", "mage region", NULL, r); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } - return cast_level; + return cast_level; } /* ------------------------------------------------------------- */ @@ -2866,7 +2868,7 @@ static int dc_age(struct curse *c) damage /= dmg.sa[1]; 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) up = &u->next; } @@ -2923,7 +2925,7 @@ static int dc_read_compat(struct attrib *a, void *target, gamedata *data) effect = strength; c = create_curse(u, &r->attribs, &ct_deathcloud, strength * 2, duration, - effect, 0); + effect, 0); c->data.v = r; if (u == NULL) { ur_add(var, &c->magician, resolve_unit); @@ -3321,7 +3323,7 @@ static int sp_bloodsacrifice(castorder * co) change_spellpoints(mage, aura); ADDMSG(&mage->faction->msgs, 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; } @@ -3815,7 +3817,7 @@ static int sp_raisepeasantmob(castorder * co) u = create_unit(r, monsters, n, get_race(RC_PEASANT), 0, LOC(monsters->locale, - "furious_mob"), NULL); + "furious_mob"), NULL); fset(u, UFL_ISNEW); setguard(u, true); a = a_new(&at_unitdissolve); @@ -4128,7 +4130,7 @@ static int sp_bigrecruit(castorder * co) * (UNITSPELL | TESTCANSEE) */ -/* restistenz der einheit pruefen */ + /* restistenz der einheit pruefen */ static int sp_pump(castorder * co) { unit *u, *target; @@ -4171,12 +4173,7 @@ static int sp_pump(castorder * co) return cast_level / 2; } - u = - 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)); - + set_observer(rt, mage->faction, effskill(target, SK_PERCEPTION, 0), 2); return cast_level; } @@ -4400,7 +4397,7 @@ static int sp_raisepeasants(castorder * co) u2 = 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); if (rule_stealth_anon()) { @@ -4414,7 +4411,7 @@ static int sp_raisepeasants(castorder * co) msg = msg_message("sp_raisepeasants_effect", "mage region amount", mage, r, - u2->number); + u2->number); r_addmessage(r, NULL, msg); if (mage->region != r) { add_message(&mage->faction->msgs, msg); @@ -4527,7 +4524,7 @@ int sp_icastle(castorder * co) if (type == bt_illusion) { b->size = (rng_int() % (int)((power * power) + 1) * 10); } - else if (type->maxsize >0) { + else if (type->maxsize > 0) { b->size = type->maxsize; } else { @@ -4760,7 +4757,7 @@ int sp_clonecopy(castorder * co) "clone_of"), unitname(mage)); clone = create_unit(target_region, mage->faction, 1, get_race(RC_CLONE), 0, name, - mage); + mage); setstatus(clone, ST_FLEE); fset(clone, UFL_LOCKED); @@ -4776,7 +4773,7 @@ int sp_clonecopy(castorder * co) /* ------------------------------------------------------------- */ int sp_dreamreading(castorder * co) { - unit *u, *u2; + unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; @@ -4809,16 +4806,11 @@ int sp_dreamreading(castorder * co) return 0; } - u2 = - 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)); + set_observer(u->region, mage->faction, effskill(u, SK_PERCEPTION, u->region), 2); msg = msg_message("sp_dreamreading_effect", "mage unit region", mage, u, - u->region); + u->region); r_addmessage(r, mage->faction, msg); msg_release(msg); return cast_level; @@ -5609,8 +5601,7 @@ int sp_showastral(castorder * co) region *r2 = rl2->data; if (!is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) { 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.", unitname(mage)); */ cmistake(mage, co->order, 220, MSG_MAGIC); - } else { + } + else { /* Ausgeben */ @@ -5629,25 +5621,24 @@ int sp_showastral(castorder * co) for (rl2 = rl; rl2; rl2 = rl2->next) { if (!is_cursed(rl2->data->attribs, C_ASTRALBLOCK, 0)) { 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++; - scat(unitname(u)); - scat(" ("); - if (!fval(u, UFL_ANON_FACTION)) { - scat(factionname(u->faction)); - scat(", "); - } - icat(u->number); - scat(" "); - scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number==1) ? NAME_SINGULAR:NAME_PLURAL))); - scat(", Entfernung "); - icat(distance(rl2->data, rt)); - scat(")"); - if (c == n - 1) { - scat(" und "); - } else if (c < n - 1) { - scat(", "); - } + c++; + scat(unitname(u)); + scat(" ("); + if (!fval(u, UFL_ANON_FACTION)) { + scat(factionname(u->faction)); + scat(", "); + } + icat(u->number); + scat(" "); + scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number == 1) ? NAME_SINGULAR : NAME_PLURAL))); + scat(", Entfernung "); + icat(distance(rl2->data, rt)); + scat(")"); + if (c == n - 1) { + scat(" und "); + } + else if (c < n - 1) { + scat(", "); } } } @@ -5667,7 +5658,6 @@ int sp_showastral(castorder * co) int sp_viewreality(castorder * co) { region_list *rl, *rl2; - unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; @@ -5686,11 +5676,7 @@ int sp_viewreality(castorder * co) for (rl2 = rl; rl2; rl2 = rl2->next) { region *rt = rl2->data; if (!is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - u = - 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; + set_observer(rt, mage->faction, co->level / 2, 2); } } @@ -5769,24 +5755,22 @@ int sp_disruptastral(castorder * co) if (trl != NULL) { for (u = r2->units; u; u = u->next) { - if (u_race(u) != get_race(RC_SPELL)) { - region_list *trl2 = trl; - region *tr; - int c = rng_int() % inhab_regions; + region_list *trl2 = trl; + region *tr; + int c = rng_int() % inhab_regions; - /* Zufaellige Zielregion suchen */ - while (c-- != 0) - trl2 = trl2->next; - tr = trl2->data; + /* Zufaellige Zielregion suchen */ + while (c-- != 0) + trl2 = trl2->next; + tr = trl2->data; - if (!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) { - message *msg = msg_message("disrupt_astral", "unit region", u, tr); - add_message(&u->faction->msgs, msg); - add_message(&tr->msgs, msg); - msg_release(msg); + if (!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) { + message *msg = msg_message("disrupt_astral", "unit region", u, tr); + add_message(&u->faction->msgs, msg); + add_message(&tr->msgs, msg); + msg_release(msg); - move_unit(u, tr, NULL); - } + move_unit(u, tr, NULL); } } free_regionlist(trl); @@ -6264,8 +6248,8 @@ int sp_speed2(castorder * co) * Flags: * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) */ -/* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft - * (force) */ + /* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft + * (force) */ int sp_q_antimagie(castorder * co) { attrib **ap; @@ -6413,7 +6397,7 @@ int sp_break_curse(castorder * co) /* Es wurde kein Ziel gefunden */ ADDMSG(&mage->faction->msgs, msg_message("spelltargetnotfound", "unit region command", - mage, mage->region, co->order)); + mage, mage->region, co->order)); } /* curse aufloesen, wenn zauber staerker (force > vigour) */ @@ -6773,7 +6757,7 @@ void register_spells(void) #ifdef COMPAT_DEATHCLOUD at_deprecate("zauber_todeswolke", dc_read_compat); #endif - + /* init_firewall(); */ ct_register(&ct_firewall); ct_register(&ct_deathcloud); diff --git a/src/spells.h b/src/spells.h index 275f41f7a..33a263733 100644 --- a/src/spells.h +++ b/src/spells.h @@ -25,11 +25,12 @@ extern "C" { struct ship; struct curse; struct unit; + struct faction; + struct region; struct message; void register_magicresistance(void); void register_spells(void); - void set_spelldata(struct spell *sp); int sp_baddreams(castorder * co); int sp_gooddreams(castorder * co); diff --git a/src/spells.test.c b/src/spells.test.c index 07a980e03..00a88cbf8 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -1,15 +1,20 @@ #include + +#include "spells.h" + #include #include #include #include +#include #include #include #include #include #include #include -#include "spells.h" + +#include #include #include @@ -108,9 +113,26 @@ static void test_bad_dreams(CuTest *tc) { 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 *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_watch_region); SUITE_ADD_TEST(suite, test_good_dreams); SUITE_ADD_TEST(suite, test_bad_dreams); SUITE_ADD_TEST(suite, test_dreams); diff --git a/src/util/gamedata.h b/src/util/gamedata.h index 452a6e527..f4983a981 100644 --- a/src/util/gamedata.h +++ b/src/util/gamedata.h @@ -34,10 +34,11 @@ #define NOWATCH_VERSION 354 /* plane->watchers is gone */ #define KEYVAL_VERSION 355 /* at_keys has values */ #define NOLANDITEM_VERSION 356 /* land_region has no items */ +#define NORCSPELL_VERSION 357 /* data contains no RC_SPELL units */ /* unfinished: */ #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 MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ diff --git a/src/volcano.c b/src/volcano.c index 9734bb2cd..db7166de0 100644 --- a/src/volcano.c +++ b/src/volcano.c @@ -82,7 +82,7 @@ damage_unit(unit * u, const char *dam, bool physical, bool magic) int i, dead = 0, hp_rem = 0, heiltrank; 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; }