From 87873702ca18de73a3711917ad2b0780469bed09 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 24 Sep 2016 20:31:31 +0200 Subject: [PATCH 1/4] eliminate RS_FARVISION enum, there is no other kind of RC_SPELL. --- src/battle.c | 75 +++++++++++++++++++++++++-------------------- src/kernel/config.h | 1 + src/magic.h | 6 ---- src/main.c | 3 ++ src/reports.c | 19 ++---------- src/reports.test.c | 24 +++++++++++++++ src/spells.c | 7 ++--- 7 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/battle.c b/src/battle.c index 1bbaaadfe..aa0c486eb 100644 --- a/src/battle.c +++ b/src/battle.c @@ -1649,11 +1649,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]; @@ -1661,38 +1701,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/kernel/config.h b/src/kernel/config.h index 74565e25e..c3b758f48 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -159,6 +159,7 @@ struct param; unsigned int data_turn; void *vm_state; int data_version; /* TODO: eliminate in favor of gamedata.version */ + int debug; struct _dictionary_ *inifile; struct global_functions { int(*wage) (const struct region * r, const struct faction * f, diff --git a/src/magic.h b/src/magic.h index 257ba11c4..59ef92475 100644 --- a/src/magic.h +++ b/src/magic.h @@ -184,12 +184,6 @@ extern "C" { SPC_LINEAR /* Komponenten pro Level und müssen vorhanden sein */ }; - enum { - RS_DUMMY, - RS_FARVISION, - MAX_REGIONSPELLS - }; - /* ------------------------------------------------------------- */ /* Prototypen */ diff --git a/src/main.c b/src/main.c index 58a15edc0..086197d29 100644 --- a/src/main.c +++ b/src/main.c @@ -184,6 +184,9 @@ static int parse_args(int argc, char **argv, int *exitcode) else { const char *arg; switch (argi[1]) { + case 'd': // check configuration for consistency + i = get_arg(argc, argv, 2, i, &arg, 0); + global.debug = arg ? atoi(arg) : 0xff; case 'r': i = get_arg(argc, argv, 2, i, &arg, 0); config_set("config.rules", arg); diff --git a/src/reports.c b/src/reports.c index ff076a866..dc0edf1b3 100644 --- a/src/reports.c +++ b/src/reports.c @@ -1308,17 +1308,6 @@ static void cb_add_seen(region *r, unit *u, void *cbdata) { } } -static bool unit_cansee(const unit *u) -{ - const race *rc = u_race(u); - static const race *rc_spell; - static int rc_cache; - if (rc_changed(&rc_cache)) { - rc_spell = get_race(RC_SPELL); - } - return (rc!=rc_spell || u->number == RS_FARVISION); -} - /** set region.seen based on visibility by one faction. * * this function may also update ctx->last and ctx->first for potential @@ -1355,9 +1344,7 @@ void prepare_report(report_context *ctx, faction *f) u = building_owner(b); if (u && u->faction==f) { prepare_lighthouse(b, ctx); - if (unit_cansee(u)) { - add_seen_nb(f, r, seen_unit); - } + add_seen_nb(f, r, seen_unit); } } } @@ -1366,9 +1353,7 @@ void prepare_report(report_context *ctx, faction *f) } for (u = r->units; u; u = u->next) { if (u->faction==f) { - if (unit_cansee(u)) { - add_seen_nb(f, r, seen_unit); - } + add_seen_nb(f, r, seen_unit); if (fval(r, RF_LIGHTHOUSE)) { if (u->building && u->building->type == bt_lighthouse && inside_building(u)) { /* we are in a lighthouse. add the regions we can see from here! */ diff --git a/src/reports.test.c b/src/reports.test.c index d9b427ff2..d5801ebab 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -425,6 +425,29 @@ static void test_seen_travelthru(CuTest *tc) { test_cleanup(); } +static void test_report_far_vision(CuTest *tc) { + unit *u1, *u2; + 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"); + u2 = create_unit(r2, u1->faction, 1, rc, 0, "spell/dreamreading", NULL); + set_number(u2, 1); + 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_unit, r2->seen.mode); + finish_reports(&ctx); + test_cleanup(); +} + CuSuite *get_reports_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -434,6 +457,7 @@ CuSuite *get_reports_suite(void) SUITE_ADD_TEST(suite, test_prepare_lighthouse); SUITE_ADD_TEST(suite, test_prepare_lighthouse_capacity); SUITE_ADD_TEST(suite, test_prepare_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 4f2d49d9d..84700926c 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4168,7 +4168,7 @@ static int sp_pump(castorder * co) } u = - create_unit(rt, mage->faction, RS_FARVISION, get_race(RC_SPELL), 0, + 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)); @@ -4812,9 +4812,8 @@ int sp_dreamreading(castorder * co) } u2 = - create_unit(u->region, mage->faction, RS_FARVISION, get_race(RC_SPELL), 0, + 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)); @@ -5691,7 +5690,7 @@ int sp_viewreality(castorder * co) region *rt = rl2->data; if (!is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { u = - create_unit(rt, mage->faction, RS_FARVISION, get_race(RC_SPELL), 0, + 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; From de58fa6aa08bf69b76e5b07d287b54061d55f550 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 24 Sep 2016 20:50:35 +0200 Subject: [PATCH 2/4] harmless refactoring: create RC_SPELL units from a common function. --- src/spells.c | 34 ++++++++++++++-------------------- src/spells.h | 1 - 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/spells.c b/src/spells.c index 84700926c..ce0a8d980 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4111,6 +4111,14 @@ static int sp_bigrecruit(castorder * co) return cast_level; } +static void watch_region(region *r, faction *f, int perception) { + unit *u; + + u = create_unit(r, f, 1, get_race(RC_SPELL), 0, NULL, NULL); + u->age = 2; + set_level(u, SK_PERCEPTION, perception); +} + /* ------------------------------------------------------------- */ /* Name: Aushorchen * Stufe: 7 @@ -4167,12 +4175,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)); - + watch_region(rt, mage->faction, effskill(target, SK_PERCEPTION, 0)); return cast_level; } @@ -4778,7 +4781,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; @@ -4811,11 +4814,7 @@ int sp_dreamreading(castorder * co) return 0; } - u2 = - create_unit(u->region, mage->faction, 1, get_race(RC_SPELL), 0, - "spell/dreamreading", NULL); - u2->age = 2; /* Nur fuer diese Runde. */ - set_level(u2, SK_PERCEPTION, effskill(u, SK_PERCEPTION, u2->region)); + watch_region(u->region, mage->faction, effskill(u, SK_PERCEPTION, u->region)); msg = msg_message("sp_dreamreading_effect", "mage unit region", mage, u, @@ -5612,7 +5611,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++; } } @@ -5632,7 +5631,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(" ("); @@ -5670,7 +5669,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; @@ -5689,11 +5687,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; + watch_region(rt, mage->faction, co->level / 2); } } diff --git a/src/spells.h b/src/spells.h index 65270a164..1ad68e131 100644 --- a/src/spells.h +++ b/src/spells.h @@ -28,7 +28,6 @@ extern "C" { struct message; void register_spells(void); - void set_spelldata(struct spell *sp); int sp_baddreams(castorder * co); int sp_gooddreams(castorder * co); From 4353773c3ba5b6f1c8b17254815a06ad6e635bc9 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 24 Sep 2016 21:09:15 +0200 Subject: [PATCH 3/4] far_vision test uses watch_region, checkes faction.interval --- src/reports.test.c | 8 +++++--- src/spells.c | 3 ++- src/spells.h | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/reports.test.c b/src/reports.test.c index d5801ebab..91e052683 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 @@ -426,7 +427,7 @@ static void test_seen_travelthru(CuTest *tc) { } static void test_report_far_vision(CuTest *tc) { - unit *u1, *u2; + unit *u1; faction *f; region *r1, *r2; const race *rc; @@ -436,8 +437,9 @@ static void test_report_far_vision(CuTest *tc) { u1 = test_create_unit(f, r1); r2 = test_create_region(10, 0, 0); rc = test_create_race("spell"); - u2 = create_unit(r2, u1->faction, 1, rc, 0, "spell/dreamreading", NULL); - set_number(u2, 1); + watch_region(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); diff --git a/src/spells.c b/src/spells.c index ce0a8d980..b14b588ee 100644 --- a/src/spells.c +++ b/src/spells.c @@ -4111,7 +4111,8 @@ static int sp_bigrecruit(castorder * co) return cast_level; } -static void watch_region(region *r, faction *f, int perception) { +void watch_region(region *r, faction *f, int perception) +{ unit *u; u = create_unit(r, f, 1, get_race(RC_SPELL), 0, NULL, NULL); diff --git a/src/spells.h b/src/spells.h index 1ad68e131..c10ee1c6c 100644 --- a/src/spells.h +++ b/src/spells.h @@ -25,10 +25,14 @@ extern "C" { struct ship; struct curse; struct unit; + struct faction; + struct region; struct message; void register_spells(void); + void watch_region(struct region *r, struct faction *f, int perception); + int sp_baddreams(castorder * co); int sp_gooddreams(castorder * co); From 15702daf995009b97b8dfad6b22347a08ab3f54e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 25 Sep 2016 13:01:51 +0200 Subject: [PATCH 4/4] Add a new attribute and RF_OBSERVER flag. --- src/creport.c | 4 +- src/kernel/region.h | 2 +- src/kernel/types.h | 2 +- src/report.c | 25 ++-- src/reports.c | 10 +- src/reports.test.c | 4 +- src/spells.c | 280 ++++++++++++++++++++++++++++---------------- src/spells.h | 3 +- src/spells.test.c | 20 ++++ 9 files changed, 226 insertions(+), 124 deletions(-) diff --git a/src/creport.c b/src/creport.c index 5e7442030..373cff2dc 100644 --- a/src/creport.c +++ b/src/creport.c @@ -1404,7 +1404,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); @@ -1427,7 +1427,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 6e231529d..f68a58fc5 100644 --- a/src/kernel/region.h +++ b/src/kernel/region.h @@ -34,7 +34,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 e0521635e..99daaf521 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -79,8 +79,8 @@ typedef enum { seen_neighbour, seen_lighthouse, seen_travel, - seen_far, seen_unit, + seen_spell, seen_battle } seen_mode; diff --git a/src/report.c b/src/report.c index c7093da73..ed448190c 100644 --- a/src/report.c +++ b/src/report.c @@ -992,7 +992,7 @@ static void describe(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); @@ -1190,7 +1190,7 @@ static void describe(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); @@ -2285,7 +2285,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; describe(out, r, f); if (markets_module() && r->land) { @@ -2317,27 +2317,18 @@ report_plaintext(const char *filename, report_context * ctx, write_travelthru(out, r, f); } else { - if (r->seen.mode == seen_far) { - describe(out, r, f); - newline(out); - guards(out, r, f); - newline(out); - write_travelthru(out, r, f); - } - else { - describe(out, r, f); - newline(out); - write_travelthru(out, r, f); - } + describe(out, r, f); + newline(out); + write_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 */ - if (r->seen.mode == seen_unit || r->seen.mode == seen_travel) { + if (r->seen.mode >= seen_travel) { // TODO: Bug 2073 message_list *mlist = r_getmessages(r, f); if (mlist) { diff --git a/src/reports.c b/src/reports.c index dc0edf1b3..92955e448 100644 --- a/src/reports.c +++ b/src/reports.c @@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "reports.h" #include "laws.h" +#include "spells.h" #include "travelthru.h" #include "lighthouse.h" #include "donations.h" @@ -92,6 +93,7 @@ const char *visibility[] = { "travel", "far", "unit", + "spell", "battle" }; @@ -943,9 +945,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: @@ -1336,6 +1338,12 @@ void prepare_report(report_context *ctx, faction *f) for (r = ctx->first; r!=ctx->last; r = r->next) { unit *u; + 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 && bt_lighthouse) { diff --git a/src/reports.test.c b/src/reports.test.c index 91e052683..72c8e46eb 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -437,7 +437,7 @@ static void test_report_far_vision(CuTest *tc) { u1 = test_create_unit(f, r1); r2 = test_create_region(10, 0, 0); rc = test_create_race("spell"); - watch_region(r2, f, 10); + set_observer(r2, f, 10); CuAssertPtrEquals(tc, r1, f->first); CuAssertPtrEquals(tc, r2, f->last); report_context ctx; @@ -445,7 +445,7 @@ static void test_report_far_vision(CuTest *tc) { CuAssertPtrEquals(tc, r1, ctx.first); CuAssertPtrEquals(tc, 0, ctx.last); CuAssertIntEquals(tc, seen_unit, r1->seen.mode); - CuAssertIntEquals(tc, seen_unit, r2->seen.mode); + CuAssertIntEquals(tc, seen_spell, r2->seen.mode); finish_reports(&ctx); test_cleanup(); } diff --git a/src/spells.c b/src/spells.c index b14b588ee..8e3887fd9 100644 --- a/src/spells.c +++ b/src/spells.c @@ -1,4 +1,4 @@ -/* +/* * * * Eressea PB(E)M host Copyright (C) 1998-2015 @@ -32,7 +32,7 @@ #include #include -/* kernel includes */ + /* kernel includes */ #include #include #include @@ -440,40 +440,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) { @@ -604,7 +604,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); @@ -615,7 +615,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(); @@ -1246,7 +1246,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; @@ -1259,7 +1259,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); @@ -1366,7 +1366,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) { @@ -1410,7 +1410,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? */ @@ -1430,7 +1430,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) { @@ -1503,11 +1503,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; } @@ -1529,8 +1529,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; } @@ -1572,7 +1572,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"); } @@ -1596,8 +1596,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; } @@ -2203,17 +2203,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; } /* ------------------------------------------------------------- */ @@ -2384,7 +2384,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); /* guard(u, GUARD_ALL); hier zu frueh! Befehl BEWACHE setzten */ addlist(&u->orders, create_order(K_GUARD, lang, NULL)); @@ -2457,7 +2457,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); @@ -2627,10 +2627,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)); + } } } @@ -2705,15 +2705,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; } /* ------------------------------------------------------------- */ @@ -2861,7 +2861,7 @@ static int dc_age(struct curse *c) damage *= (1.0 - magic_resistance(u)); 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; } @@ -2916,7 +2916,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); @@ -3123,7 +3123,7 @@ static bool chaosgate_valid(const connection * b) } static struct region *chaosgate_move(const connection * b, struct unit *u, -struct region *from, struct region *to, bool routing) + struct region *from, struct region *to, bool routing) { if (!routing) { int maxhp = u->hp / 4; @@ -3317,7 +3317,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; } @@ -3811,7 +3811,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); guard(u, GUARD_ALL); a = a_new(&at_unitdissolve); @@ -4111,13 +4111,92 @@ static int sp_bigrecruit(castorder * co) return cast_level; } -void watch_region(region *r, faction *f, int perception) -{ - unit *u; +typedef struct obs_data { + faction *f; + int skill; + int timer; +} obs_data; - u = create_unit(r, f, 1, get_race(RC_SPELL), 0, NULL, NULL); - u->age = 2; - set_level(u, SK_PERCEPTION, perception); +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)); } /* ------------------------------------------------------------- */ @@ -4133,7 +4212,7 @@ void watch_region(region *r, faction *f, int perception) * (UNITSPELL | TESTCANSEE) */ -/* restistenz der einheit pruefen */ + /* restistenz der einheit pruefen */ static int sp_pump(castorder * co) { unit *u, *target; @@ -4176,7 +4255,7 @@ static int sp_pump(castorder * co) return cast_level / 2; } - watch_region(rt, mage->faction, effskill(target, SK_PERCEPTION, 0)); + set_observer(rt, mage->faction, effskill(target, SK_PERCEPTION, 0)); return cast_level; } @@ -4400,7 +4479,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 +4493,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 +4606,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 { @@ -4766,7 +4845,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); @@ -4815,11 +4894,11 @@ int sp_dreamreading(castorder * co) return 0; } - watch_region(u->region, mage->faction, effskill(u, SK_PERCEPTION, u->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; @@ -5622,7 +5701,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 */ @@ -5642,13 +5722,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(", "); } } @@ -5688,7 +5769,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)) { - watch_region(rt, mage->faction, co->level / 2); + set_observer(rt, mage->faction, co->level / 2); } } @@ -6262,8 +6343,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; @@ -6411,7 +6492,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) */ @@ -6718,6 +6799,7 @@ void register_spells(void) register_borders(); at_register(&at_wdwpyramid); + at_register(&at_observer); at_register(&at_deathcloud_compat); /* init_firewall(); */ diff --git a/src/spells.h b/src/spells.h index c10ee1c6c..6e1b194e6 100644 --- a/src/spells.h +++ b/src/spells.h @@ -31,7 +31,8 @@ extern "C" { void register_spells(void); - void watch_region(struct region *r, struct faction *f, int perception); + 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 3c773d489..a3f624d40 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,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);