diff --git a/src/battle.c b/src/battle.c index 19f30e59c..e3bc281a0 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1636,11 +1636,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]; @@ -1648,38 +1688,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/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/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/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/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/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 9af18c1d2..146a8529f 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" @@ -93,6 +94,7 @@ const char *visibility[] = { "travel", "far", "unit", + "spell", "battle" }; @@ -966,9 +968,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,29 +1424,42 @@ 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) { + int 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; } } } } 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 mark 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 && inside_building(u)) { + int 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..dd27ee360 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 @@ -474,12 +475,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 +532,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; @@ -558,8 +563,9 @@ 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); + set_level(u, SK_PERCEPTION, 3); + CuAssertIntEquals(tc, 2, lighthouse_range(b, f, NULL)); prepare_report(&ctx, f); CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, NULL, ctx.last); @@ -707,6 +713,30 @@ static void test_region_distance_ql(CuTest *tc) { test_cleanup(); } +static void test_report_far_vision(CuTest *tc) { + unit *u1; + faction *f; + region *r1, *r2; + const race *rc; + test_setup(); + f = test_create_faction(0); + r1 = test_create_region(0, 0, 0); + u1 = test_create_unit(f, r1); + r2 = test_create_region(10, 0, 0); + rc = test_create_race("spell"); + set_observer(r2, f, 10); + 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 +754,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..fa3d9a368 100644 --- a/src/spells.c +++ b/src/spells.c @@ -1,4 +1,4 @@ -/* +/* * * * Eressea PB(E)M host Copyright (C) 1998-2015 @@ -437,40 +437,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 +606,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 +617,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 +1249,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 +1262,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 +1369,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 +1413,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 +1433,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 +1506,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 +1532,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 +1575,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 +1599,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; } @@ -2206,17 +2206,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 +2387,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 +2459,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 +2629,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 +2707,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 +2866,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 +2923,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 +3321,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 +3815,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); @@ -4115,6 +4115,94 @@ static int sp_bigrecruit(castorder * co) return cast_level; } +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) +{ + 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 = 2; + return; + } + a = a->nexttype; + } + } + else { + fset(r, RF_OBSERVER); + } + a_add(&r->attribs, make_observer(f, skill)); +} + /* ------------------------------------------------------------- */ /* Name: Aushorchen * Stufe: 7 @@ -4128,7 +4216,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 +4259,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)); return cast_level; } @@ -4400,7 +4483,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 +4497,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 +4610,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 +4843,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 +4859,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 +4892,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)); 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,7 +5687,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)) + if (u_race(u) != get_race(RC_SPELL)) n++; } } @@ -5619,7 +5697,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,7 +5708,7 @@ 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)) { + if (u_race(u) != get_race(RC_SPELL)) { c++; scat(unitname(u)); scat(" ("); @@ -5639,13 +5718,14 @@ int sp_showastral(castorder * co) } icat(u->number); 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 "); icat(distance(rl2->data, rt)); scat(")"); if (c == n - 1) { scat(" und "); - } else if (c < n - 1) { + } + else if (c < n - 1) { scat(", "); } } @@ -5667,7 +5747,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 +5765,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); } } @@ -6264,8 +6339,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 +6488,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) */ diff --git a/src/spells.h b/src/spells.h index 275f41f7a..d45f04d68 100644 --- a/src/spells.h +++ b/src/spells.h @@ -25,11 +25,15 @@ 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); + + void set_observer(struct region *r, struct faction *f, int perception); + int get_observer(struct region *r, struct faction *f); int sp_baddreams(castorder * co); int sp_gooddreams(castorder * co); diff --git a/src/spells.test.c b/src/spells.test.c index 07a980e03..b6d3a2ab7 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -108,9 +109,28 @@ static void test_bad_dreams(CuTest *tc) { test_cleanup(); } +static void test_watch_region(CuTest *tc) { + region *r; + faction *f; + test_setup(); + test_create_race("spell"); + CuAssertPtrNotNull(tc, get_race(RC_SPELL)); + r = test_create_region(0, 0, 0); + f = test_create_faction(0); + CuAssertIntEquals(tc, -1, get_observer(r, f)); + set_observer(r, f, 0); + CuAssertIntEquals(tc, 0, get_observer(r, f)); + set_observer(r, f, 10); + 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);