diff --git a/src/common/gamecode/creport.c b/src/common/gamecode/creport.c index 459ff636f..23a6629cd 100644 --- a/src/common/gamecode/creport.c +++ b/src/common/gamecode/creport.c @@ -1131,7 +1131,7 @@ report_computer(FILE * F, faction * f, struct seen_region ** seen, const faction } fprintf(F, "%d;Rekrutierungskosten\n", f->race->recruitcost); fprintf(F, "%d;Anzahl Personen\n", count_all(f)); - fprintf(F, "\"%s\";Magiegebiet\n", neue_gebiete[f->magiegebiet]); + fprintf(F, "\"%s\";Magiegebiet\n", magietypen[f->magiegebiet]); if (f->race == new_race[RC_HUMAN]) { fprintf(F, "%d;Anzahl Immigranten\n", count_migrants(f)); diff --git a/src/common/gamecode/items.c b/src/common/gamecode/items.c index fb39167e6..8870a1f2a 100644 --- a/src/common/gamecode/items.c +++ b/src/common/gamecode/items.c @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -90,6 +92,65 @@ use_speedsail(struct unit * u, const struct item_type * itype, int amount, struc } /* END speedsail */ +/* ------------------------------------------------------------- */ +/* Kann auch von Nichtmagiern benutzt werden, erzeugt eine +* Antimagiezone, die zwei Runden bestehen bleibt */ +static void +use_antimagiccrystal(region * r, unit * mage, int amount, struct order * ord) +{ + int i; + for (i=0;i!=amount;++i) { + int effect, duration = 2; + double force; + spell *sp = find_spell(M_GRAU, "antimagiczone"); + attrib ** ap = &r->attribs; + unused(ord); + assert(sp); + + /* Reduziert die Stärke jedes Spruchs um effect */ + effect = sp->level; + + /* Hält Sprüche bis zu einem summierten Gesamtlevel von power aus. + * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone + * um seine Stufe */ + force = sp->level * 20; /* Stufe 5 =~ 100 */ + + /* Regionszauber auflösen */ + while (*ap && force > 0) { + curse * c; + attrib * a = *ap; + if (!fval(a->type, ATF_CURSE)) { + do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); + continue; + } + c = (curse*)a->data.v; + + /* Immunität prüfen */ + if (c->flag & CURSE_IMMUNE) { + do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); + continue; + } + + force = destr_curse(c, effect, force); + if(c->vigour <= 0) { + a_remove(&r->attribs, a); + } + if(*ap) ap = &(*ap)->next; + } + + if(force > 0) { + variant var ; + var.i = effect; + create_curse(mage, &r->attribs, ct_find("antimagiczone"), force, duration, var, 0); + } + + } + use_pooled(mage, mage->region, R_ANTIMAGICCRYSTAL, amount); + ADDMSG(&mage->faction->msgs, msg_message("use_antimagiccrystal", + "unit region", mage, r)); + return; +} + static int use_instantartsculpture(struct unit * u, const struct item_type * itype, int amount, struct order * ord) @@ -193,6 +254,7 @@ use_aurapotion50(struct unit * u, const struct item_type * itype, void register_itemimplementations(void) { + register_function((pf_generic)use_antimagiccrystal, "use_antimagiccrystal"); register_function((pf_generic)use_instantartsculpture, "use_instantartsculpture"); register_function((pf_generic)use_studypotion, "use_studypotion"); register_function((pf_generic)use_speedsail, "use_speedsail"); diff --git a/src/common/gamecode/report.c b/src/common/gamecode/report.c index 2569efce4..d4b1b15c1 100644 --- a/src/common/gamecode/report.c +++ b/src/common/gamecode/report.c @@ -1933,7 +1933,7 @@ report(FILE *F, faction * f, struct seen_region ** seen, const faction_list * ad rnl(F); sprintf(buf, "%s, %s/%s (%s)", factionname(f), LOC(f->locale, rc_name(f->race, 1)), - LOC(f->locale, mkname("school", neue_gebiete[f->magiegebiet])), + LOC(f->locale, mkname("school", magietypen[f->magiegebiet])), f->email); centre(F, buf, true); if (f->alliance!=NULL) { @@ -3338,13 +3338,13 @@ out_faction(FILE *file, faction *f) if (alliances!=NULL) { fprintf(file, "%s (%s/%d) (%.3s/%.3s), %d Einh., %d Pers., $%d, %d %s\n", f->name, itoa36(f->no), f->alliance?f->alliance->id:0, - LOC(default_locale, rc_name(f->race, 0)), neue_gebiete[f->magiegebiet], + LOC(default_locale, rc_name(f->race, 0)), magietypen[f->magiegebiet], f->no_units, f->num_total, f->money, turn - f->lastorders, turn - f->lastorders != 1 ? "NMRs" : "NMR "); } else { fprintf(file, "%s (%.3s/%.3s), %d Einh., %d Pers., $%d, %d %s\n", factionname(f), LOC(default_locale, rc_name(f->race, 0)), - neue_gebiete[f->magiegebiet], f->no_units, f->num_total, f->money, + magietypen[f->magiegebiet], f->no_units, f->num_total, f->money, turn - f->lastorders, turn - f->lastorders != 1 ? "NMRs" : "NMR "); } } diff --git a/src/common/gamecode/spy.c b/src/common/gamecode/spy.c index b4608341b..f71038863 100644 --- a/src/common/gamecode/spy.c +++ b/src/common/gamecode/spy.c @@ -186,9 +186,9 @@ setstealth_cmd(unit * u, struct order * ord) return 0; } - /* Pseudodrachen können sich nur als Drachen tarnen */ - if (u->race == new_race[RC_PSEUDODRAGON] || u->race == new_race[RC_BIRTHDAYDRAGON]) { - if (trace==new_race[RC_PSEUDODRAGON]||trace==new_race[RC_FIREDRAGON]||trace==new_race[RC_DRAGON]||trace==new_race[RC_WYRM]) { + /* Singdrachen können sich nur als Drachen tarnen */ + if (u->race == new_race[RC_SONGDRAGON] || u->race == new_race[RC_BIRTHDAYDRAGON]) { + if (trace==new_race[RC_SONGDRAGON]||trace==new_race[RC_FIREDRAGON]||trace==new_race[RC_DRAGON]||trace==new_race[RC_WYRM]) { u->irace = trace; if (u->race->flags & RCF_SHAPESHIFTANY && get_racename(u->attribs)) set_racename(&u->attribs, NULL); diff --git a/src/common/kernel/combatspells.c b/src/common/kernel/combatspells.c index 5086808c6..cce9e7ccf 100644 --- a/src/common/kernel/combatspells.c +++ b/src/common/kernel/combatspells.c @@ -11,6 +11,7 @@ */ #include #include "eressea.h" +#include "combatspells.h" /* kernel includes */ #include "battle.h" @@ -25,6 +26,7 @@ #include "unit.h" #include "movement.h" #include "spell.h" +#include "spellid.h" #include "race.h" #include "skill.h" diff --git a/src/common/kernel/combatspells.h b/src/common/kernel/combatspells.h new file mode 100644 index 000000000..4e72d3636 --- /dev/null +++ b/src/common/kernel/combatspells.h @@ -0,0 +1,56 @@ +/* vi: set ts=2: + +-------------------+ Christian Schlittchen + | | Enno Rehling + | Eressea PBEM host | Katja Zedel + | (c) 1998 - 2003 | Henning Peters + | | Ingo Wilken + +-------------------+ Stefan Reich + + This program may not be used, modified or distributed + without prior permission by the authors of Eressea. +*/ + +#ifndef H_GC_COMBATSPELLS +#define H_GC_COMBATSPELLS + +#ifdef __cplusplus +extern "C" { +#endif + + struct fighter; + + /* Kampfzauber */ + extern int sp_fumbleshield(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_shadowknights(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_combatrosthauch(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_kampfzauber(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_healing(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_keeploot(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_reanimate(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_chaosrow(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_flee(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_berserk(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_tiredsoldiers(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_reeling_arrows(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_denyattack(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_sleep(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_windshield(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_strong_wall(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_versteinern(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_hero(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_frighten(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_mindblast(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_speed(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_wolfhowl(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_dragonodem(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_reduceshield(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_armorshield(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_stun(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_undeadhero(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_shadowcall(struct fighter * fi, int level, double power, struct spell * sp); + extern int sp_immolation(struct fighter * fi, int level, double power, struct spell * sp); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/common/kernel/equipment.c b/src/common/kernel/equipment.c index 5ef68c52d..626c5aba1 100644 --- a/src/common/kernel/equipment.c +++ b/src/common/kernel/equipment.c @@ -82,6 +82,14 @@ equipment_setskill(equipment * eq, skill_t sk, const char * value) } } +void +equipment_addspell(equipment * eq, spell * sp) +{ + if (eq!=NULL) { + spelllist_add(&eq->spells, sp); + } +} + void equipment_setitem(equipment * eq, const item_type * itype, const char * value) { @@ -108,6 +116,14 @@ equip_unit(struct unit * u, const struct equipment * eq) if (eq) { skill_t sk; itemdata * idata; + sc_mage * m = get_mage(u); + if (m!=NULL) { + spell_list * sp = eq->spells; + while (sp) { + add_spell(m, sp->data); + sp = sp->next; + } + } for (sk=0;sk!=MAXSKILLS;++sk) { if (eq->skills[sk]!=NULL) { int i = dice_rand(eq->skills[sk]); diff --git a/src/common/kernel/equipment.h b/src/common/kernel/equipment.h index 41e14f52b..a88db9d6b 100644 --- a/src/common/kernel/equipment.h +++ b/src/common/kernel/equipment.h @@ -23,6 +23,7 @@ #ifdef __cplusplus extern "C" { #endif + struct spell; typedef struct itemdata { const struct item_type * itype; @@ -44,6 +45,7 @@ extern "C" { char * name; struct itemdata * items; char * skills[MAXSKILLS]; + struct spell_list * spells; struct subset * subsets; struct equipment * next; } equipment; @@ -54,6 +56,7 @@ extern "C" { extern void equipment_setitem(struct equipment * eq, const struct item_type * itype, const char * value); extern void equipment_setskill(struct equipment * eq, skill_t sk, const char * value); + extern void equipment_addspell(struct equipment * eq, struct spell * sp); extern void equip_unit(struct unit * u, const struct equipment * eq); diff --git a/src/common/kernel/eressea.h b/src/common/kernel/eressea.h index a5a01ef66..ef3cfb54e 100644 --- a/src/common/kernel/eressea.h +++ b/src/common/kernel/eressea.h @@ -57,6 +57,7 @@ struct plane; struct order; struct spell; struct region; +struct fighter; struct region_list; struct race; struct ship; @@ -653,18 +654,18 @@ enum { RC_PEASANT, RC_WOLF, /* 31 */ - RC_HOUSECAT, + RC_LYNX, RC_TUNNELWORM, RC_EAGLE, RC_RAT, - RC_PSEUDODRAGON, + RC_SONGDRAGON, RC_NYMPH, RC_UNICORN, - RC_WARG, - RC_WRAITH, + RC_DIREWOLF, + RC_GHOST, RC_IMP, /* 41 */ RC_DREAMCAT, - RC_FEY, + RC_FAIRY, RC_OWL, RC_HELLCAT, RC_TIGER, diff --git a/src/common/kernel/item.c b/src/common/kernel/item.c index 8f6001d72..522e8ebc2 100644 --- a/src/common/kernel/item.c +++ b/src/common/kernel/item.c @@ -28,7 +28,6 @@ #include "pool.h" #include "race.h" #include "region.h" -#include "spell.h" #include "save.h" #include "skill.h" #include "unit.h" @@ -781,64 +780,6 @@ destroy_curse_crystal(attrib **alist, int cast_level, int force) } #endif -/* ------------------------------------------------------------- */ -/* Kann auch von Nichtmagiern benutzt werden, erzeugt eine - * Antimagiezone, die zwei Runden bestehen bleibt */ -static void -use_antimagiccrystal(region * r, unit * mage, int amount, struct order * ord) -{ - int i; - for (i=0;i!=amount;++i) { - int effect, duration = 2; - double force; - spell *sp = find_spellbyid(SPL_ANTIMAGICZONE); - attrib ** ap = &r->attribs; - unused(ord); - - /* Reduziert die Stärke jedes Spruchs um effect */ - effect = sp->level; - - /* Hält Sprüche bis zu einem summierten Gesamtlevel von power aus. - * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone - * um seine Stufe */ - force = sp->level * 20; /* Stufe 5 =~ 100 */ - - /* Regionszauber auflösen */ - while (*ap && force > 0) { - curse * c; - attrib * a = *ap; - if (!fval(a->type, ATF_CURSE)) { - do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); - continue; - } - c = (curse*)a->data.v; - - /* Immunität prüfen */ - if (c->flag & CURSE_IMMUNE) { - do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); - continue; - } - - force = destr_curse(c, effect, force); - if(c->vigour <= 0) { - a_remove(&r->attribs, a); - } - if(*ap) ap = &(*ap)->next; - } - - if(force > 0) { - variant var ; - var.i = effect; - create_curse(mage, &r->attribs, ct_find("antimagiczone"), force, duration, var, 0); - } - - } - use_pooled(mage, mage->region, R_ANTIMAGICCRYSTAL, amount); - add_message(&mage->faction->msgs, - new_message(mage->faction, "use_antimagiccrystal%u:unit%r:region", mage, r)); - return; -} - /* ------------------------------------------------------------- */ /* Kann auch von Nichtmagier benutzt werden, modifiziert Taktik für diese * Runde um -1 - 4 Punkte. */ @@ -1012,10 +953,6 @@ static t_item itemdata[MAXITEMS] = { {"Delphin", "Delphine", "Delphin", "Delphine"}, IS_MAGIC, 0, 0, {0, 0, 0, 0, 0, 0}, 5000, 0, FL_ITEM_ANIMAL | FL_ITEM_NOTINBAG | FL_ITEM_NOTLOST, NULL }, - { /* I_ANTIMAGICCRYSTAL 63 */ - {"Antimagiekristall", "Antimagiekristalle", "Amulett", "Amulette"}, - IS_MAGIC, 0, 0, {0, 0, 0, 0, 0, 0}, 0, 0, 0, &use_antimagiccrystal - }, { /* I_RING_OF_NIMBLEFINGER 64 */ {"Ring der flinken Finger", "Ringe der flinken Finger", "", ""}, IS_MAGIC, 0, 0, {0, 0, 0, 0, 0, 0}, 0, 0, 0, NULL @@ -2030,8 +1967,6 @@ item_score(item_t i) case I_RING_OF_NIMBLEFINGER: case I_FEENSTIEFEL: return 6000; - case I_ANTIMAGICCRYSTAL: - return 2000; } return 0; } @@ -2284,7 +2219,6 @@ register_resources(void) register_function((pf_generic)use_potion, "usepotion"); register_function((pf_generic)use_tacticcrystal, "usetacticcrystal"); register_function((pf_generic)use_birthdayamulet, "usebirthdayamulet"); - register_function((pf_generic)use_antimagiccrystal, "useantimagiccrystal"); register_function((pf_generic)use_warmthpotion, "usewarmthpotion"); register_function((pf_generic)use_bloodpotion, "usebloodpotion"); register_function((pf_generic)use_foolpotion, "usefoolpotion"); diff --git a/src/common/kernel/item.h b/src/common/kernel/item.h index 4f05049b0..59a185293 100644 --- a/src/common/kernel/item.h +++ b/src/common/kernel/item.h @@ -312,7 +312,6 @@ enum { I_PEGASUS, I_UNICORN, I_DOLPHIN, - I_ANTIMAGICCRYSTAL, I_RING_OF_NIMBLEFINGER, I_TROLLBELT, I_PRESSCARD, diff --git a/src/common/kernel/magic.c b/src/common/kernel/magic.c index 3ecf31f8d..af5c5c919 100644 --- a/src/common/kernel/magic.c +++ b/src/common/kernel/magic.c @@ -39,6 +39,7 @@ #include "ship.h" #include "skill.h" #include "spell.h" +#include "spellid.h" #include "unit.h" #include @@ -1243,6 +1244,18 @@ fumble(region * r, unit * u, const spell * sp, int cast_grade) return true; } +/* ------------------------------------------------------------- */ +/* Dummy-Zauberpatzer, Platzhalter für speziel auf die Sprüche +* zugeschnittene Patzer */ +static void +patzer(castorder *co) +{ + unit *mage = (unit *)co->magician; + + cmistake(mage, co->order, 180, MSG_MAGIC); + + return; +} /* Die normalen Spruchkosten müssen immer bezahlt werden, hier noch * alle weiteren Folgen eines Patzers */ diff --git a/src/common/kernel/movement.c b/src/common/kernel/movement.c index 750cf5f79..b3001b88c 100644 --- a/src/common/kernel/movement.c +++ b/src/common/kernel/movement.c @@ -62,7 +62,6 @@ #include #include -extern border_type bt_wisps; int * storms; typedef struct traveldir { @@ -1054,7 +1053,7 @@ next_region(unit * u, region * current, region * next) if (b->type->move) { region * rto = b->type->move(b, u, current, next, true); if (rto!=next) { - /* the target region was changed (bt_wisps, for example). check the + /* the target region was changed (wisps, for example). check the * new target region for borders */ next = rto; b = get_borders(current, next); @@ -1146,7 +1145,7 @@ movement_speed(unit * u) case RC_WYRM: case RC_FIREDRAGON: case RC_BIRTHDAYDRAGON: - case RC_PSEUDODRAGON: + case RC_SONGDRAGON: return BP_DRAGON; } diff --git a/src/common/kernel/race.c b/src/common/kernel/race.c index 2410350d4..0fd3e489e 100644 --- a/src/common/kernel/race.c +++ b/src/common/kernel/race.c @@ -28,11 +28,11 @@ #include "alchemy.h" #include "build.h" #include "building.h" +#include "equipment.h" #include "faction.h" #include "item.h" #include "magic.h" #include "region.h" -#include "spell.h" #include "unit.h" #include "names.h" #include "pathfinder.h" @@ -301,133 +301,13 @@ racename(const struct locale *loc, const unit *u, const race * rc) } static void -oldfamiliars(unit * familiar) +oldfamiliars(unit * u) { - sc_mage * m = NULL; - race_t frt = old_race(familiar->race); - - switch(frt) { - case RC_HOUSECAT: - /* Kräu+1, Mag, Pfer+1, Spi+3, Tar+3, Wahr+4, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_TUNNELWORM: - /* Ber+50,Hol+50,Sbau+50,Aus+2*/ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_MINING, 1); - set_level(familiar, SK_LUMBERJACK, 1); - set_level(familiar, SK_AUSDAUER, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_EAGLE: - /* Spi, Wahr+2, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_RAT: - /* Spionage+5, Tarnung+4, Wahrnehmung+2, Ausdauer */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - set_level(familiar, SK_AUSDAUER, 1+rand()%8); - /* set_number(familiar, 50+rand()%500+rand()%500); */ - m = create_mage(familiar, M_GRAU); - break; - case RC_PSEUDODRAGON: - /* Magie+1, Spionage, Tarnung, Wahrnehmung, Ausdauer */ - m = create_mage(familiar, M_GRAU); - set_level(familiar, SK_MAGIC, 1); - break; - case RC_NYMPH: - /* Alc, Arm, Bog+2, Han-2, Kräu+4, Mag+1, Pfer+5, Rei+5, - * Rüs-2, Sbau, Seg-2, Sta, Spi+2, Tak-2, Tar+3, Unt+10, - * Waf-2, Wag-2, Wahr+2, Steu-2, Aus-1 */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_LONGBOW, 1); - set_level(familiar, SK_HERBALISM, 1); - set_level(familiar, SK_HORSE_TRAINING, 1); - set_level(familiar, SK_RIDING, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_ENTERTAINMENT, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_UNICORN: - /* Mag+2, Spi, Tak, Tar+4, Wahr+4, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_WARG: - /* Spi, Tak, Tar, Wahri+2, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_WRAITH: - /* Mag+1, Rei-2, Hie, Sta, Spi, Tar, Wahr, Aus */ - set_level(familiar, SK_MAGIC, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_IMP: - /* Mag+1,Rei-1,Hie,Sta,Spi+1,Tar+1,Wahr+1,Steu+1,Aus*/ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - set_level(familiar, SK_TAXING, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_DREAMCAT: - /* Mag+1,Hie,Sta,Spi+1,Tar+1,Wahr+1,Steu+1,Aus*/ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - set_level(familiar, SK_TAXING, 1); - m = create_mage(familiar, M_GRAU); - break; - case RC_FEY: - /* Mag+1,Rei-1,Hie-1,Sta-1,Spi+2,Tar+5,Wahr+2,Aus */ - set_level(familiar, SK_MAGIC, 1); - m = create_mage(familiar,M_GRAU); - break; - case RC_OWL: - /* Spi+1,Tar+1,Wahr+5,Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_SPY, 1); - set_level(familiar, SK_STEALTH, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar,M_GRAU); - break; - case RC_HELLCAT: - /* Spi, Tak, Tar, Wahr+1, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar,M_GRAU); - break; - case RC_TIGER: - /* Spi, Tak, Tar, Wahr+1, Aus */ - set_level(familiar, SK_MAGIC, 1); - set_level(familiar, SK_OBSERVATION, 1); - m = create_mage(familiar,M_GRAU); - break; - } - if (m!=NULL) { - spell_list * fspells = familiarspells(familiar->race); - while (fspells!=NULL) { - add_spell(m, fspells->data); - fspells=fspells->next; - } - } + char fname[64]; + /* these familiars have no special skills. + */ + snprintf(fname, sizeof(fname), "%s_familiar", u->race->_name[0]); + equip_unit(u, get_equipment(fname)); } static item * @@ -592,89 +472,7 @@ register_races(void) sprintf(zBuffer, "%s/races.xml", resourcepath()); } -/** familiars **/ -typedef struct familiar_spells { - struct familiar_spells * next; - spell_list * spells; - const race * familiar_race; -} familiar_spells; - -static familiar_spells * racespells; - -spell_list * -familiarspells(const race * rc) -{ - familiar_spells * fspells = racespells; - while (fspells && rc!=fspells->familiar_race) { - fspells = fspells->next; - } - if (fspells!=NULL) return fspells->spells; - return NULL; -} - -familiar_spells * -mkspells(const race * rc) -{ - familiar_spells * fspells; - - fspells = malloc(sizeof(familiar_spells)); - fspells->next = racespells; - racespells = fspells; - fspells->familiar_race = rc; - fspells->spells = NULL; - return fspells; -} - -void -init_familiarspells(void) -{ - familiar_spells * fspells; - - fspells = mkspells(new_race[RC_PSEUDODRAGON]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_FLEE)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SLEEP)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_FRIGHTEN)); - - fspells = mkspells(new_race[RC_NYMPH]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); - - fspells = mkspells(new_race[RC_NYMPH]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_CONFUSION)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); - - fspells = mkspells(new_race[RC_UNICORN]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_RESISTMAGICBONUS)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SONG_OF_PEACE)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_HERO)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_HEALINGSONG)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); - - fspells = mkspells(new_race[RC_WRAITH]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_STEALAURA)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_FRIGHTEN)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SUMMONUNDEAD)); - - fspells = mkspells(new_race[RC_IMP]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_STEALAURA)); - - fspells = mkspells(new_race[RC_DREAMCAT]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_ILL_SHAPESHIFT)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_TRANSFERAURA_TRAUM)); - - fspells = mkspells(new_race[RC_FEY]); - spelllist_add(&fspells->spells, find_spellbyid(SPL_DENYATTACK)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_CALM_MONSTER)); - spelllist_add(&fspells->spells, find_spellbyid(SPL_SEDUCE)); -} - void init_races(void) { - init_familiarspells(); } diff --git a/src/common/kernel/race.h b/src/common/kernel/race.h index a358c7a8a..8b3c4fefd 100644 --- a/src/common/kernel/race.h +++ b/src/common/kernel/race.h @@ -169,8 +169,6 @@ extern boolean allowed_dragon(const struct region * src, const struct region * t extern void register_races(void); extern void init_races(void); extern boolean r_insectstalled(const struct region *r); -extern spell_list * familiarspells(const struct race * rc); - extern void add_raceprefix(const char *); extern char ** race_prefixes; diff --git a/src/common/kernel/reports.c b/src/common/kernel/reports.c index c18c74ce6..6c89869af 100644 --- a/src/common/kernel/reports.c +++ b/src/common/kernel/reports.c @@ -58,15 +58,6 @@ const char * g_reportdir; -const char *neue_gebiete[] = { - "none", - "illaun", - "tybied", - "cerddor", - "gwyrrd", - "draig" -}; - const char *coasts[MAXDIRECTIONS] = { "coast::nw", diff --git a/src/common/kernel/reports.h b/src/common/kernel/reports.h index 587bbae86..881bf6550 100644 --- a/src/common/kernel/reports.h +++ b/src/common/kernel/reports.h @@ -31,7 +31,6 @@ extern "C" { #define REPORTWIDTH 78 extern const char *directions[]; -extern const char *neue_gebiete[]; extern const char *coasts[]; /* kann_finden speedups */ diff --git a/src/common/kernel/spell.c b/src/common/kernel/spell.c index f19bb1e19..cd555fe9b 100644 --- a/src/common/kernel/spell.c +++ b/src/common/kernel/spell.c @@ -24,7268 +24,17 @@ #include "spell.h" /* kernel includes */ -/* FIXME: brauchen wir die wirklich alle? */ -#include "battle.h" /* für lovar */ -#include "border.h" -#include "building.h" -#include "curse.h" -#include "faction.h" -#include "goodies.h" -#include "item.h" -#include "karma.h" #include "magic.h" -#include "message.h" -#include "objtypes.h" -#include "order.h" -#include "plane.h" -#include "pool.h" -#include "race.h" -#include "region.h" -#include "resolve.h" -#include "ship.h" -#include "skill.h" -#include "spy.h" -#include "teleport.h" -#include "terrain.h" +#include "spellid.h" #include "unit.h" -/* spells includes */ -#include - -/* util includes */ -#include -#include -#include -#include -#include -#include -#include - /* libc includes */ #include -#include -#include -#include -#include #include -#include -/* triggers includes */ -#include -#include -#include -#include -#include -#include -#include - -/* attributes includes */ -#include -#include -/* ----------------------------------------------------------------------- */ - -static variant zero_effect = { 0 }; - -attrib_type at_unitdissolve = { - "unitdissolve", NULL, NULL, NULL, a_writedefault, a_readdefault -}; - -#ifdef WDW_PYRAMIDSPELL -attrib_type at_wdwpyramid = { - "wdwpyramid", NULL, NULL, NULL, a_writedefault, a_readdefault -}; -#endif - -/* ----------------------------------------------------------------------- */ - -static void -report_failure(unit * mage, struct order * ord) { - /* Fehler: "Der Zauber schlägt fehl" */ - cmistake(mage, ord, 180, MSG_MAGIC); -} - -/* ------------------------------------------------------------- */ -/* do_shock - Schockt die Einheit, z.B. bei Verlust eines */ -/* Vertrauten. */ -/* ------------------------------------------------------------- */ - -void -do_shock(unit *u, const char *reason) -{ - int i; - if(u->number == 0) return; - - /* HP - Verlust */ - u->hp = (unit_max_hp(u) * u->number)/10; - u->hp = max(1, u->hp); - /* Aura - Verlust */ - if(is_mage(u)) { - set_spellpoints(u, max_spellpoints(u->region,u)/10); - } - - /* Evt. Talenttageverlust */ - for (i=0;i!=u->skill_size;++i) if (rand()%5==0) { - skill * sv = u->skills+i; - int weeks = (sv->level * sv->level - sv->level) / 2; - int change = (weeks+9) / 10; - reduce_skill(u, sv, change); - } - - /* Dies ist ein Hack, um das skillmod und familiar-Attribut beim Mage - * zu löschen wenn der Familiar getötet wird. Da sollten wir über eine - * saubere Implementation nachdenken. */ - - if(!strcmp(reason, "trigger")) { - remove_familiar(u); - } - - ADDMSG(&u->faction->msgs, msg_message("shock", - "mage reason", u, strdup(reason))); -} - -/* ------------------------------------------------------------- */ -/* Spruchanalyse - Ausgabe von curse->info und curse->name */ -/* ------------------------------------------------------------- */ - -static double -curse_chance(const struct curse * c, double force) -{ - return 1.0 + (force - c->vigour) * 0.1; -} - -static void -magicanalyse_region(region *r, unit *mage, double force) -{ - attrib *a; - boolean found = false; - const struct locale * lang = mage->faction->locale; - - for (a=r->attribs;a;a=a->next) { - curse * c = (curse*)a->data.v; - double probability; - int mon; - - if (!fval(a->type, ATF_CURSE)) continue; - - /* ist der curse schwächer als der Analysezauber, so ergibt sich - * mehr als 100% probability und damit immer ein Erfolg. */ - probability = curse_chance(c, force); - mon = c->duration + (rand()%10) - 5; - mon = max(1, mon); - found = true; - - if (chance(probability)) { /* Analyse geglückt */ - if(c->flag & CURSE_NOAGE) { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_region_noage", "mage region curse", - mage, r, LOC(lang, mkname("spell", c->type->cname)))); - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_region_age", "mage region curse months", - mage, r, LOC(lang, mkname("spell", c->type->cname)), mon)); - } - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_region_fail", "mage region", mage, r)); - } - } - if (!found) { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_region_nospell", "mage region", mage, r)); - } -} - -static void -magicanalyse_unit(unit *u, unit *mage, double force) -{ - attrib *a; - boolean found = false; - const struct locale * lang = mage->faction->locale; - - for (a=u->attribs;a;a=a->next) { - curse * c; - double probability; - int mon; - if (!fval(a->type, ATF_CURSE)) continue; - - c = (curse*)a->data.v; - /* ist der curse schwächer als der Analysezauber, so ergibt sich - * mehr als 100% probability und damit immer ein Erfolg. */ - probability = curse_chance(c, force); - mon = c->duration + (rand()%10) - 5; - mon = max(1,mon); - - if (chance(probability)) { /* Analyse geglückt */ - if(c->flag & CURSE_NOAGE){ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_unit_noage", "mage unit curse", - mage, u, LOC(lang, mkname("spell", c->type->cname)))); - }else{ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_unit_age", "mage unit curse months", - mage, u, LOC(lang, mkname("spell", c->type->cname)), mon)); - } - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_unit_fail", "mage unit", mage, u)); - } - } - if (!found) { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_unit_nospell", "mage target", mage, u)); - } -} - -static void -magicanalyse_building(building *b, unit *mage, double force) -{ - attrib *a; - boolean found = false; - const struct locale * lang = mage->faction->locale; - - for (a=b->attribs;a;a=a->next) { - curse * c; - double probability; - int mon; - - if (!fval(a->type, ATF_CURSE)) continue; - - c = (curse*)a->data.v; - /* ist der curse schwächer als der Analysezauber, so ergibt sich - * mehr als 100% probability und damit immer ein Erfolg. */ - probability = curse_chance(c, force); - mon = c->duration + (rand()%10) - 5; - mon = max(1,mon); - - if (chance(probability)) { /* Analyse geglückt */ - if(c->flag & CURSE_NOAGE){ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_building_age", "mage building curse", - mage, b, LOC(lang, mkname("spell", c->type->cname)))); - }else{ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_building_age", "mage building curse months", - mage, b, LOC(lang, mkname("spell", c->type->cname)), mon)); - } - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_building_fail", "mage building", mage, b)); - } - } - if (!found) { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_building_nospell", "mage building", mage, b)); - } - -} - -static void -magicanalyse_ship(ship *sh, unit *mage, double force) -{ - attrib *a; - boolean found = false; - const struct locale * lang = mage->faction->locale; - - for (a=sh->attribs;a;a=a->next) { - curse * c; - double probability; - int mon; - if (!fval(a->type, ATF_CURSE)) continue; - - c = (curse*)a->data.v; - /* ist der curse schwächer als der Analysezauber, so ergibt sich - * mehr als 100% probability und damit immer ein Erfolg. */ - probability = curse_chance(c, force); - mon = c->duration + (rand()%10) - 5; - mon = max(1,mon); - - if (chance(probability)) { /* Analyse geglückt */ - if(c->flag & CURSE_NOAGE){ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_ship_noage", "mage ship curse", - mage, sh, LOC(lang, mkname("spell", c->type->cname)))); - }else{ - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_ship_age", "mage ship curse months", - mage, sh, LOC(lang, mkname("spell", c->type->cname)), mon)); - } - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_ship_fail", "mage ship", mage, sh)); - - } - } - if (!found) { - ADDMSG(&mage->faction->msgs, msg_message( - "analyse_ship_nospell", "mage ship", mage, sh)); - } - -} - -/* ------------------------------------------------------------- */ -/* Antimagie - curse auflösen */ -/* ------------------------------------------------------------- */ - -/* Wenn der Curse schwächer ist als der cast_level, dann wird er - * aufgelöst, bzw seine Kraft (vigour) auf 0 gesetzt. - * Ist der cast_level zu gering, hat die Antimagie nur mit einer Chance - * von 100-20*Stufenunterschied % eine Wirkung auf den Curse. Dann wird - * die Kraft des Curse um die halbe Stärke der Antimagie reduziert. - * Zurückgegeben wird der noch unverbrauchte Rest von force. - */ -double -destr_curse(curse* c, int cast_level, double force) -{ - if (cast_level < c->vigour) { /* Zauber ist nicht stark genug */ - double probability = 0.1 + (cast_level - c->vigour)*0.2; - /* pro Stufe Unterschied -20% */ - if (chance(probability)) { - force -= c->vigour; - if (c->type->change_vigour){ - c->type->change_vigour(c, -(cast_level+1/2)); - } else { - c->vigour -= cast_level+1/2; - } - } - } else { /* Zauber ist stärker als curse */ - if (force >= c->vigour){ /* reicht die Kraft noch aus? */ - force -= c->vigour; - if (c->type->change_vigour){ - c->type->change_vigour(c, -c->vigour); - } else { - c->vigour = 0; - } - - } - } - return force; -} - -int -destroy_curse(attrib **alist, int cast_level, double force, curse * c) -{ - int succ = 0; -/* attrib **a = a_find(*ap, &at_curse); */ - attrib ** ap = alist; - - while (*ap && force > 0) { - curse * c1; - attrib * a = *ap; - if (!fval(a->type, ATF_CURSE)) { - do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); - continue; - } - c1 = (curse*)a->data.v; - - /* Immunität prüfen */ - if (c1->flag & CURSE_IMMUNE) { - do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); - continue; - } - - /* Wenn kein spezieller cursetyp angegeben ist, soll die Antimagie - * auf alle Verzauberungen wirken. Ansonsten prüfe, ob der Curse vom - * richtigen Typ ist. */ - if(!c || c==c1) { - double remain = destr_curse(c1, cast_level, force); - if (remain < force) { - succ = cast_level; - force = remain; - } - if (c1->vigour <= 0) { - a_remove(alist, a); - } - } - if (*ap==a) ap = &a->next; - } - return succ; -} - -/* ------------------------------------------------------------- */ -/* Report a spell's effect to the units in the region. -*/ -static void -report_effect(region * r, unit * mage, message * seen, message * unseen) -{ - unit * u; - - /* melden, 1x pro Partei */ - freset(mage->faction, FL_DH); - for (u = r->units; u; u = u->next ) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - - /* Bei Fernzaubern sieht nur die eigene Partei den Magier */ - if (u->faction != mage->faction){ - if (r == mage->region){ - /* kein Fernzauber, prüfe, ob der Magier überhaupt gesehen - * wird */ - if (cansee(u->faction, r, mage, 0)) { - r_addmessage(r, u->faction, seen); - } else { - r_addmessage(r, u->faction, unseen); - } - } else { /* Fernzauber, fremde Partei sieht den Magier niemals */ - r_addmessage(r, u->faction, unseen); - } - } else { /* Partei des Magiers, sieht diesen immer */ - r_addmessage(r, u->faction, seen); - } - } - } - /* Ist niemand von der Partei des Magiers in der Region, dem Magier - * nochmal gesondert melden */ - if (!fval(mage->faction, FL_DH)) { - add_message(&mage->faction->msgs, seen); - } -} - -/* ------------------------------------------------------------- */ -/* Die Spruchfunktionen */ -/* ------------------------------------------------------------- */ -/* Meldungen: - * - * Fehlermeldungen sollten als MSG_MAGIC, level ML_MISTAKE oder - * ML_WARN ausgegeben werden. (stehen im Kopf der Auswertung unter - * Zauberwirkungen) - - sprintf(buf, "%s in %s: 'ZAUBER %s': [hier die Fehlermeldung].", - unitname(mage), regionname(mage->region, mage->faction), sa->strings[0]); - add_message(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - - * Allgemein sichtbare Auswirkungen in der Region sollten als - * Regionsereignisse auch dort auftauchen. - - { - message * seen = msg_message("harvest_effect", "mage", mage); - message * unseen = msg_message("harvest_effect", "mage", NULL); - report_effect(r, mage, seen, unseen); - } - - * Meldungen an den Magier über Erfolg sollten, wenn sie nicht als - * Regionsereigniss auftauchen, als MSG_MAGIC level ML_INFO unter - * Zauberwirkungen gemeldet werden. Direkt dem Magier zuordnen (wie - * Botschaft an Einheit) ist derzeit nicht möglich. - * ACHTUNG! r muss nicht die Region des Magier sein! (FARCASTING) - * - * Parameter: - * die Struct castorder *co ist in magic.h deklariert - * die Parameterliste spellparameter *pa = co->par steht dort auch. - * - */ - -/* ------------------------------------------------------------- */ -/* Name: Vertrauter - * Stufe: 10 - * - * Wirkung: - * Der Magier beschwört 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 erhält der Magier - * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist - * zufällig bestimmt, wird aber durch Magiegebiet und Rasse beeinflußt. - * "Tierische" Vertraute brauchen keinen Unterhalt. - * - * Ein paar Möglichkeiten: - * Magieg. Rasse Besonderheiten - * Eule Tybied -/- fliegt, Auraregeneration - * Rabe Ilaun -/- fliegt - * Imp Draig -/- Magieresistenz? - * Fuchs Gwyrrd -/- Wahrnehmung - * ???? Cerddor -/- ???? (Singvogel?, Papagei?) - * Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch? - * Krähe -/- -/- fliegt, +Tarnung (weil unauffällig) - * Delphin -/- Meerm. schwimmt - * Wolf -/- Ork - * Hund -/- Mensch kann evtl BEWACHE ausführen - * Ratte -/- Goblin - * Albatros -/- -/- fliegt, kann auf Ozean "landen" - * Affe -/- -/- kann evtl BEKLAUE ausführen - * Goblin -/- !Goblin normale Einheit - * Katze -/- !Katze normale Einheit - * Dämon -/- !Dämon normale Einheit - * - * Spezielle V. für Katzen, Trolle, Elfen, Dämonen, Insekten, Zwerge? - */ - -static const race * -select_familiar(const race * magerace, magic_t magiegebiet) -{ - const race * retval = NULL; - int rnd = rand()%100; - assert(magerace->familiars[0]); - - do { - if (rnd < 3) { - /* RC_KRAKEN muß letzter Vertraute sein */ - int rc = RC_HOUSECAT + rand()%(RC_KRAKEN+1-RC_HOUSECAT); - retval = new_race[rc]; - } else if (rnd < 80) { - retval = magerace->familiars[0]; - } - retval = magerace->familiars[magiegebiet]; - } - while (retval->init_familiar==NULL); - return retval; -} - -/* ------------------------------------------------------------- */ -/* der Vertraue des Magiers */ - -boolean -is_familiar(const unit *u) -{ - attrib * a = a_find(u->attribs, &at_familiarmage); - return i2b(a!=NULL); -} - -static void -make_familiar(unit *familiar, unit *mage) -{ - /* skills and spells: */ - familiar->race->init_familiar(familiar); - - /* triggers: */ - create_newfamiliar(mage, familiar); - - /* Hitpoints nach Talenten korrigieren, sonst starten vertraute - * mit Ausdauerbonus verwundet */ - familiar->hp = unit_max_hp(familiar); -} - -static int -sp_summon_familiar(castorder *co) -{ - unit *familiar; - region *r = co->rt; - region *target_region = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - const race * rc; - skill_t sk; - int dh, dh1; - direction_t d; - if (get_familiar(mage) != NULL ) { - cmistake(mage, co->order, 199, MSG_MAGIC); - return 0; - } - rc = select_familiar(mage->faction->race, mage->faction->magiegebiet); - - if (fval(rc, RCF_SWIM) && !fval(rc, RCF_WALK)) { - int coasts = is_coastregion(r); - - if (coasts == 0) { - cmistake(mage, co->order, 229, MSG_MAGIC); - return 0; - } - - /* In welcher benachbarten Ozeanregion soll der Familiar erscheinen? */ - coasts = rand()%coasts; - dh = -1; - for(d=0; dterrain == T_OCEAN) { - dh++; - if(dh == coasts) break; - } - } - target_region = rconnect(r,d); - } - - familiar = create_unit(target_region, mage->faction, 1, rc, 0, NULL, mage); - if (target_region==mage->region) { - familiar->building = mage->building; - familiar->ship = mage->ship; - } - familiar->status = ST_FLEE; /* flieht */ - sprintf(buf, "Vertrauter von %s", unitname(mage)); - set_string(&familiar->name, buf); - if (fval(mage, UFL_PARTEITARNUNG)) fset(familiar, UFL_PARTEITARNUNG); - fset(familiar, UFL_LOCKED); - make_familiar(familiar, mage); - - dh = 0; - dh1 = 0; - sprintf(buf, "%s ruft einen Vertrauten. %s können ", - unitname(mage), LOC(mage->faction->locale, rc_name(rc, 1))); - for(sk=0;skbonus[sk] > -5) dh++; - } - for(sk=0;skbonus[sk] > -5){ - dh--; - if (dh1 == 0){ - dh1 = 1; - } else { - if (dh == 0){ - scat(" und "); - } else { - scat(", "); - } - } - scat(skillname(sk, mage->faction->locale)); - } - } - scat(" lernen."); - scat(" "); - scat("Der Vertraute verleiht dem Magier einen Bonus auf jedes Talent "); - scat("(ausgenommen Magie), welches der Vertraute beherrscht."); - scat(" "); - scat("Das spezielle Band zu seinem Vertrauten ermöglicht dem Magier "); - scat("auch, Sprüche durch diesen zu wirken. So gezauberte Sprüche "); - scat("wirken auf die Region des Vertrauten und brauchen keine Fernzauber "); - scat("zu sein. Die maximale Entfernung dafür entspricht dem Talent des "); - scat("Magiers. Einen Spruch durch das Vertrautenband zu richten ist "); - scat("jedoch gewissen Einschränkungen unterworfen. Die Stufe des Zaubers "); - scat("kann nicht größer als das Magietalent des Vertrauten oder das halbe "); - scat("Talent des Magiers sein. Auch verdoppeln sich die Kosten für den "); - scat("Spruch. (Um einen Zauber durch den Vertrauten zu wirken, gibt "); - scat("man statt dem Magier dem Vertrauten den Befehl ZAUBERE.)"); - - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Zerstöre Magie - * Wirkung: - * Zerstört alle Zauberwirkungen auf dem Objekt. Jeder gebrochene - * Zauber verbraucht c->vigour an Zauberkraft. Wird der Spruch auf - * einer geringeren Stufe gezaubert, als der Zielzauber an c->vigour - * hat, so schlägt die Auflösung mit einer von der Differenz abhängigen - * Chance fehl. Auch dann wird force verbraucht, der Zauber jedoch nur - * abgeschwächt. - * - * Flag: - * (FARCASTING|SPELLLEVEL|ONSHIPCAST|TESTCANSEE) - * */ -static int -sp_destroy_magic(castorder *co) -{ - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - curse * c = NULL; - char ts[80]; - attrib **ap; - int obj; - int succ; - - /* da jeder Zauber force verbraucht und der Zauber auf alles und nicht - * nur einen Spruch wirken soll, wird die Wirkung hier verstärkt */ - force *= 4; - - /* Objekt ermitteln */ - obj = pa->param[0]->typ; - - switch(obj) { - case SPP_REGION: - { - region *tr = pa->param[0]->data.r; - ap = &tr->attribs; - strcpy(ts, regionname(tr, mage->faction)); - break; - } - case SPP_TEMP: - case SPP_UNIT: - { - unit *u; - u = pa->param[0]->data.u; - ap = &u->attribs; - strcpy(ts, unitname(u)); - break; - } - case SPP_BUILDING: - { - building *b; - b = pa->param[0]->data.b; - ap = &b->attribs; - strcpy(ts, buildingname(b)); - break; - } - case SPP_SHIP: - { - ship *sh; - sh = pa->param[0]->data.sh; - ap = &sh->attribs; - strcpy(ts, shipname(sh)); - break; - } - default: - return 0; - } - - succ = destroy_curse(ap, cast_level, force, c); - - if (succ) { - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_effect", "unit region command succ target", - mage, mage->region, co->order, succ, strdup(ts))); - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_noeffect", "unit region command", - mage, mage->region, co->order)); - } - - return max(succ, 1); -} - -/* ------------------------------------------------------------- */ -/* Name: Transferiere Aura - * Stufe: variabel - * Gebiet: alle - * Kategorie: Einheit, positiv - * Wirkung: - * Mit Hilfe dieses Zauber kann der Magier eigene Aura im Verhältnis - * 2:1 auf einen anderen Magier des gleichen Magiegebietes oder (nur - * bei Tybied) im Verhältnis 3:1 auf einen Magier eines anderen - * Magiegebietes übertragen. - * - * Syntax: - * "ZAUBERE " - * "ui" - * Flags: - * (UNITSPELL|ONSHIPCAST|ONETARGET) - * */ - -static int -sp_transferaura(castorder *co) -{ - int aura, gain, multi = 2; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - unit * u; - sc_mage * scm_dst, * scm_src = get_mage(mage); - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - /* Wieviel Transferieren? */ - aura = pa->param[1]->data.i; - u = pa->param[0]->data.u; - scm_dst = get_mage(u); - - if (scm_dst==NULL) { - /* "Zu dieser Einheit kann ich keine Aura übertragen." */ - cmistake(mage, co->order, 207, MSG_MAGIC); - return 0; - } else if (scm_src->magietyp==M_ASTRAL) { - if (scm_src->magietyp != scm_dst->magietyp) multi = 3; - } else if (scm_src->magietyp==M_GRAU) { - if (scm_src->magietyp != scm_dst->magietyp) multi = 4; - } else if (scm_dst->magietyp!=scm_src->magietyp) { - /* "Zu dieser Einheit kann ich keine Aura übertragen." */ - cmistake(mage, co->order, 207, MSG_MAGIC); - return 0; - } - - if (aura < multi) { - /* "Auraangabe fehlerhaft." */ - cmistake(mage, co->order, 208, MSG_MAGIC); - return 0; - } - - gain = min(aura, scm_src->spellpoints) / multi; - scm_src->spellpoints -= gain*multi; - scm_dst->spellpoints += gain; - -/* sprintf(buf, "%s transferiert %d Aura auf %s", unitname(mage), - gain, unitname(u)); */ - ADDMSG(&mage->faction->msgs, msg_message( - "auratransfer_success", "unit target aura", mage, u, gain)); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* DRUIDE */ -/* ------------------------------------------------------------- */ -/* Name: Günstige Winde - * Stufe: 4 - * Gebiet: Gwyrrd - * Wirkung: - * Schiffsbewegung +1, kein Abtreiben. Hält (Stufe) Runden an. - * Kombinierbar mit "Sturmwind" (das +1 wird dadurch aber nicht - * verdoppelt), und "Luftschiff". - * - * Flags: - * (SHIPSPELL|ONSHIPCAST|SPELLLEVEL|ONETARGET|TESTRESISTANCE) - */ - -static int -sp_goodwinds(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = cast_level+1; - spellparameter *pa = co->par; - ship *sh; - unit *u; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - sh = pa->param[0]->data.sh; - - /* keine Probleme mit C_SHIP_SPEEDUP und C_SHIP_FLYING */ - /* NODRIFT bewirkt auch +1 Geschwindigkeit */ - create_curse(mage, &sh->attribs, ct_find("nodrift"), power, duration, zero_effect, 0); - - /* melden, 1x pro Partei */ - freset(mage->faction, FL_DH); - for(u = r->units; u; u = u->next ) freset(u->faction, FL_DH); - for(u = r->units; u; u = u->next ) { - if(u->ship != sh ) /* nur den Schiffsbesatzungen! */ - continue; - if(!fval(u->faction, FL_DH) ) { - message * m = msg_message("wind_effect", "mage ship", cansee(u->faction, r, mage, 0) ? mage:NULL, sh); - r_addmessage(r, u->faction, m); - msg_release(m); - fset(u->faction, FL_DH); - } - } - if (!fval(mage->faction, FL_DH)) { - message * m = msg_message("wind_effect", "mage ship", mage, sh); - r_addmessage(r, mage->faction, m); - msg_release(m); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Magischer Pfad - * Stufe: 4 - * Gebiet: Gwyrrd - * Wirkung: - * für Stufe Runden wird eine (magische) Strasse erzeugt, die wie eine - * normale Strasse wirkt. - * Im Ozean schlägt der Spruch fehl - * - * Flags: - * (FARCASTING|SPELLLEVEL|REGIONSPELL|ONSHIPCAST|TESTRESISTANCE) - */ -static int -sp_magicstreet(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - - if (!landregion(rterrain(r))) { - cmistake(mage, co->order, 186, MSG_MAGIC); - return 0; - } - - /* wirkt schon in der Zauberrunde! */ - create_curse(mage, &r->attribs, ct_find("magicstreet"), co->force, co->level+1, zero_effect, 0); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("path_effect", "mage region", mage, r); - message * unseen = msg_message("path_effect", "mage region", NULL, r); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return co->level; -} - -/* ------------------------------------------------------------- */ -/* Name: Erwecke Ents - * Stufe: 10 - * Kategorie: Beschwörung, positiv - * Gebiet: Gwyrrd - * Wirkung: - * Verwandelt (Stufe) Bäume in eine Gruppe von Ents, die sich für Stufe - * Runden der Partei des Druiden anschliessen und danach wieder zu - * Bäumen werden - * Patzer: - * Monster-Ents entstehen - * - * Flags: - * (SPELLLEVEL) - */ -static int -sp_summonent(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - unit *u; - attrib *a; - int ents; - - if (rtrees(r,2) == 0) { - cmistake(mage, co->order, 204, MSG_EVENT); - /* nicht ohne bäume */ - return 0; - } - - ents = (int)min(power*power, rtrees(r,2)); - - u = create_unit(r, mage->faction, ents, new_race[RC_TREEMAN], 0, LOC(mage->faction->locale, rc_name(new_race[RC_TREEMAN], ents!=1)), mage); - - a = a_new(&at_unitdissolve); - a->data.ca[0] = 2; /* An r->trees. */ - a->data.ca[1] = 5; /* 5% */ - a_add(&u->attribs, a); - fset(u, UFL_LOCKED); - - rsettrees(r, 2, rtrees(r,2) - ents); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("ent_effect", "mage amount", mage, ents); - message * unseen = msg_message("ent_effect", "mage amount", NULL, ents); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Segne Steinkreis - * Stufe: 11 - * Kategorie: Artefakt - * Gebiet: Gwyrrd - * Wirkung: - * Es werden zwei neue Gebäude eingeführt: Steinkreis und Steinkreis - * (gesegnet). Ersteres kann man bauen, letzteres wird aus einem - * fertigen Steinkreis mittels des Zaubers erschaffen. - * - * Flags: - * (BUILDINGSPELL | ONETARGET) - * - */ -static int -sp_blessstonecircle(castorder *co) -{ - building *b; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *p = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(p->param[0]->flag == TARGET_NOTFOUND) return 0; - - b = p->param[0]->data.b; - - if(b->type != bt_find("stonecircle")) { - sprintf(buf, "%s ist kein Steinkreis.", buildingname(b)); - mistake(mage, co->order, buf, MSG_MAGIC); - return 0; - } - - if(b->size < b->type->maxsize) { - sprintf(buf, "%s muss vor der Weihe fertiggestellt sein.", buildingname(b)); - mistake(mage, co->order, buf, MSG_MAGIC); - return 0; - } - - b->type = bt_find("blessedstonecircle"); - - sprintf(buf, "%s weiht %s.", unitname(mage), buildingname(b)); - addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Mahlstrom - * Stufe: 15 - * Kategorie: Region, negativ - * Gebiet: Gwyrrd - * Wirkung: - * Erzeugt auf See einen Mahlstrom für Stufe-Wochen. Jedes Schiff, das - * durch den Mahlstrom segelt, nimmt 0-150% Schaden. (D.h. es hat auch - * eine 1/3-Chance, ohne Federlesens zu sinken. Der Mahlstrom sollte - * aus den Nachbarregionen sichtbar sein. - * - * Flags: - * (OCEANCASTABLE | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_maelstrom(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - curse * c; - double power = co->force; - variant effect; - int duration = (int)power+1; - - if(rterrain(r) != T_OCEAN) { - cmistake(mage, co->order, 205, MSG_MAGIC); - /* nur auf ozean */ - return 0; - } - - /* Attribut auf Region. - * Existiert schon ein curse, so wird dieser verstärkt - * (Max(Dauer), Max(Stärke))*/ - effect.i = (int)power; - c = create_curse(mage, &mage->attribs, ct_find("maelstrom"), power, duration, effect, 0); - curse_setflag(c, CURSE_ISNEW); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("maelstrom_effect", "mage", mage); - message * unseen = msg_message("maelstrom_effect", "mage", NULL); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Wurzeln der Magie - * Stufe: 16 - * Kategorie: Region, neutral - * Gebiet: Gwyrrd - * Wirkung: - * Wandelt einen Wald permanent in eine Mallornregion - * - * Flags: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_mallorn(castorder *co) -{ - region *r = co->rt; - int cast_level = co->level; - unit *mage = (unit *)co->magician; - - if(!landregion(rterrain(r))) { - cmistake(mage, co->order, 290, MSG_MAGIC); - return 0; - } - if(fval(r, RF_MALLORN)) { - cmistake(mage, co->order, 291, MSG_MAGIC); - return 0; - } - - /* half the trees will die */ - rsettrees(r, 2, rtrees(r,2)/2); - rsettrees(r, 1, rtrees(r,1)/2); - rsettrees(r, 0, rtrees(r,0)/2); - fset(r, RF_MALLORN); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("mallorn_effect", "mage", mage); - message * unseen = msg_message("mallorn_effect", "mage", NULL); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Segen der Erde - * Stufe: 1 - * Kategorie: Region, positiv - * Gebiet: Gwyrrd - * - * Wirkung: - * Alle Bauern verdienen Stufe-Wochen 1 Silber mehr. - * - * Flags: - * (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL) - */ -static int -sp_blessedharvest(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = (int)power+1; - variant effect; - - /* Attribut auf Region. - * Existiert schon ein curse, so wird dieser verstärkt - * (Max(Dauer), Max(Stärke))*/ - effect.i = 1; - create_curse(mage,&r->attribs, ct_find("blessedharvest"), power, duration, effect, 0); - { - message * seen = msg_message("harvest_effect", "mage", mage); - message * unseen = msg_message("harvest_effect", "mage", NULL); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Hainzauber - * Stufe: 2 - * Kategorie: Region, positiv - * Gebiet: Gwyrrd - * Syntax: ZAUBER [REGION x y] [STUFE 2] "Hain" - * Wirkung: - * Erschafft Stufe-10*Stufe Jungbäume - * - * Flag: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ - -static int -sp_hain(castorder *co) -{ - int trees; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - - if(!r->land) { - cmistake(mage, co->order, 296, MSG_MAGIC); - return 0; - } - if (fval(r, RF_MALLORN)) { - cmistake(mage, co->order, 92, MSG_MAGIC); - return 0; - } - - trees = lovar((int)(force * 10 * RESOURCE_QUANTITY)) + (int)force; - rsettrees(r, 1, rtrees(r,1) + trees); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("growtree_effect", "mage amount", mage, trees); - message * unseen = msg_message("growtree_effect", "mage amount", NULL, trees); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} -/* ------------------------------------------------------------- */ -/* Name: Segne Mallornstecken - Mallorn Hainzauber - * Stufe: 4 - * Kategorie: Region, positiv - * Gebiet: Gwyrrd - * Syntax: ZAUBER [REGION x y] [STUFE 4] "Segne Mallornstecken" - * Wirkung: - * Erschafft Stufe-10*Stufe Jungbäume - * - * Flag: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ - -static int -sp_mallornhain(castorder *co) -{ - int trees; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - - if(!r->land) { - cmistake(mage, co->order, 296, MSG_MAGIC); - return 0; - } - if (!fval(r, RF_MALLORN)) { - cmistake(mage, co->order, 91, MSG_MAGIC); - return 0; - } - - trees = lovar((int)(force * 10 * RESOURCE_QUANTITY)) + (int)force; - rsettrees(r, 1, rtrees(r,1) + trees); - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("growtree_effect", "mage amount", mage, trees); - message * unseen = msg_message("growtree_effect", "mage amount", NULL, trees); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - -void -patzer_ents(castorder *co) -{ - int ents; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - /* int cast_level = co->level; */ - double force = co->force; - - if(!r->land) { - cmistake(mage, co->order, 296, MSG_MAGIC); - return; - } - - ents = (int)(force*10); - u = create_unit(r, findfaction(MONSTER_FACTION), ents, new_race[RC_TREEMAN], 0, - LOC(default_locale, rc_name(new_race[RC_TREEMAN], ents!=1)), NULL); - - /* 'Erfolg' melden */ - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_patzer", "unit region command", mage, - mage->region, co->order)); - - /* melden, 1x pro Partei */ - { - message * unseen = msg_message("entrise", "region", r); - report_effect(r, mage, unseen, unseen); - msg_release(unseen); - } -} - -/* ------------------------------------------------------------- */ -/* Name: Rosthauch - * Stufe: 3 - * Kategorie: Einheit, negativ - * Gebiet: Gwyrrd - * Wirkung: - * Zerstört zwischen Stufe und Stufe*10 Eisenwaffen - * - * Flag: - * (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE) - */ -/* Syntax: ZAUBER [REGION x y] [STUFE 2] "Rosthauch" 1111 2222 3333 */ - -typedef struct iron_weapon { - const struct item_type * type; - const struct item_type * rusty; - float chance; - struct iron_weapon * next; -} iron_weapon; - -static iron_weapon * ironweapons = NULL; - -void -add_ironweapon(const struct item_type * type, const struct item_type * rusty, float chance) -{ - iron_weapon * iweapon = malloc(sizeof(iron_weapon)); - iweapon->type = type; - iweapon->rusty = rusty; - iweapon->chance = chance; - iweapon->next = ironweapons; - ironweapons = iweapon; -} - -static int -sp_rosthauch(castorder *co) -{ - int n; - int success = 0; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - int force = (int)co->force; - spellparameter *pa = co->par; - - if (ironweapons==NULL) { - add_ironweapon(it_find("sword"), it_find("rustysword"), 1.0); - add_ironweapon(it_find("axe"), it_find("rustyaxe"), 1.0); - add_ironweapon(it_find("greatsword"), it_find("rustygreatsword"), 1.0); - add_ironweapon(it_find("halberd"), it_find("rustyhalberd"), 0.5f); -#ifndef NO_RUSTY_ARMOR - add_ironweapon(it_find("shield"), it_find("rustyshield"), 0.5f); - add_ironweapon(it_find("chainmail"), it_find("rustychainmail"), 0.2f); -#endif - } - - force = rand()%((int)(force * 10)) + force; - - /* fuer jede Einheit */ - for (n = 0; n < pa->length; n++) { - unit *u = pa->param[n]->data.u; - int ironweapon = 0; - iron_weapon * iweapon = ironweapons; - - if (force<=0) break; - if (pa->param[n]->flag & (TARGET_RESISTS|TARGET_NOTFOUND)) continue; - - for (;iweapon!=NULL;iweapon=iweapon->next) { - item ** ip = i_find(&u->items, iweapon->type); - if (*ip) { - int i = min((*ip)->number, force); - if (iweapon->chance<1.0) { - i = (int)(i*iweapon->chance); - } - if (i>0) { - force -= i; - ironweapon += i; - i_change(ip, iweapon->type, -i); - if (iweapon->rusty) { - i_change(&u->items, iweapon->rusty, i); - } - } - } - if (force<=0) break; - } - - if (ironweapon>0) { - /* {$mage mage} legt einen Rosthauch auf {target}. {amount} Waffen - * wurden vom Rost zerfressen */ - ADDMSG(&mage->faction->msgs, msg_message("rust_effect", - "mage target amount", mage, u, ironweapon)); - ADDMSG(&u->faction->msgs, msg_message("rust_effect", - "mage target amount", - cansee(u->faction, r, mage, 0) ? mage:NULL, u, ironweapon)); - success += ironweapon; - } else { - /* {$mage mage} legt einen Rosthauch auf {target}, doch der - * Rosthauch fand keine Nahrung */ - ADDMSG(&mage->faction->msgs, msg_message( - "rust_fail", "mage target", mage, u)); - } - } - /* in success stehen nun die insgesamt zerstörten Waffen. Im - * ungünstigsten Fall kann pro Stufe nur eine Waffe verzaubert werden, - * darum wird hier nur für alle Fälle in denen noch weniger Waffen - * betroffen wurden ein Kostennachlass gegeben */ - return min(success, cast_level); -} - - -/* ------------------------------------------------------------- */ -/* Name: Kälteschutz - * Stufe: 3 - * Kategorie: Einheit, positiv - * Gebiet: Gwyrrd - * - * Wirkung: - * schützt ein bis mehrere Einheiten mit bis zu Stufe*10 Insekten vor - * den Auswirkungen der Kälte. Sie können Gletscher betreten und dort - * ganz normal alles machen. Die Wirkung hält Stufe Wochen an - * Insekten haben in Gletschern den selben Malus wie in Bergen. Zu - * lange drin, nicht mehr ändern - * - * Flag: - * (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) - */ -/* Syntax: ZAUBER [STUFE n] "Kälteschutz" eh1 [eh2 [eh3 [...]]] */ - -static int -sp_kaelteschutz(castorder *co) -{ - unit *u; - int n, i = 0; - int men; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = max(cast_level, (int)force) + 1; - spellparameter *pa = co->par; - variant effect; - - force*=10; /* 10 Personen pro Force-Punkt */ - - /* für jede Einheit in der Kommandozeile */ - for (n = 0; n < pa->length; n++) { - if (force < 1) - break; - - if(pa->param[n]->flag == TARGET_RESISTS - || pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - u = pa->param[n]->data.u; - - if (force < u->number){ - men = (int)force; - } else { - men = u->number; - } - - effect.i = 1; - create_curse(mage, &u->attribs, ct_find("insectfur"), cast_level, - duration, effect, men); - - force -= u->number; - ADDMSG(&mage->faction->msgs, msg_message( - "heat_effect", "mage target", mage, 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)); - i = cast_level; - } - /* Erstattung? */ - return i; -} - -/* ------------------------------------------------------------- */ -/* Name: Verwünschung, Funkenregen, Naturfreund, ... - * Stufe: 1 - * Kategorie: Einheit, rein visuell - * Gebiet: Alle - * - * Wirkung: - * Die Einheit wird von einem magischen Effekt heimgesucht, der in ihrer - * Beschreibung auftaucht, aber nur visuellen Effekt hat. - * - * Flag: - * (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET) - */ -/* Syntax: ZAUBER "Funkenregen" eh1 */ - -static int -sp_sparkle(castorder *co) -{ - unit *u; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - int duration = cast_level+1; - variant effect; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - u = pa->param[0]->data.u; - effect.i = rand(); - create_curse(mage, &u->attribs, ct_find("sparkle"), cast_level, - duration, effect, u->number); - - ADDMSG(&mage->faction->msgs, msg_message( - "sparkle_effect", "mage target", mage, u)); - if (u->faction!=mage->faction) - ADDMSG(&u->faction->msgs, msg_message( - "sparkle_effect", "mage target", mage, u)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Eisengolem - * Stufe: 2 - * Kategorie: Beschwörung, positiv - * Gebiet: Gwyrrd - * Wirkung: - * Erschafft eine Einheit Eisengolems mit Stufe*8 Golems. Jeder Golem - * hat jede Runde eine Chance von 15% zu Staub zu zerfallen. Gibt man - * den Golems den Befehl 'mache Schwert/Bihänder' oder 'mache - * Schild/Kettenhemd/Plattenpanzer', so werden pro Golem 5 Eisenbarren - * verbaut und der Golem löst sich auf. - * - * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. - * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufällig - * treffen, so ist der Schaden fast immer tödlich. (Eisengolem: HP - * 50, AT 4, PA 2, Rüstung 2(KH), 2d10+4 TP, Magieresistenz 0.25) - * - * Golems nehmen nix an und geben nix. Sie bewegen sich immer nur 1 - * Region weit und ziehen aus Strassen keinen Nutzen. Ein Golem wiegt - * soviel wie ein Stein. Kann nicht im Sumpf gezaubert werden - * - * Flag: - * (SPELLLEVEL) - * - * #define GOLEM_IRON 4 - */ - -static int -sp_create_irongolem(castorder *co) -{ - unit *u2; - attrib *a; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int number = lovar(force*8*RESOURCE_QUANTITY); - if (number<1) number = 1; - - if (rterrain(r) == T_SWAMP){ - cmistake(mage, co->order, 188, MSG_MAGIC); - return 0; - } - - u2 = create_unit(r, mage->faction, number, new_race[RC_IRONGOLEM], 0, - LOC(mage->faction->locale, rc_name(new_race[RC_IRONGOLEM], 1)), mage); - - set_level(u2, SK_ARMORER, 1); - set_level(u2, SK_WEAPONSMITH, 1); - - a = a_new(&at_unitdissolve); - a->data.ca[0] = 0; - a->data.ca[1] = IRONGOLEM_CRUMBLE; - a_add(&u2->attribs, a); - - 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(new_race[RC_IRONGOLEM], 1)))); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Steingolem - * Stufe: 1 - * Kategorie: Beschwörung, positiv - * Gebiet: Gwyrrd - * Wirkung: - * Erschafft eine Einheit Steingolems mit Stufe*5 Golems. Jeder Golem - * hat jede Runde eine Chance von 10% zu Staub zu zerfallen. Gibt man - * den Golems den Befehl 'mache Burg' oder 'mache Strasse', so werden - * pro Golem 10 Steine verbaut und der Golem löst sich auf. - * - * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. - * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufällig - * treffen, so ist der Schaden fast immer tödlich. (Steingolem: HP 60, - * AT 4, PA 2, Rüstung 4(PP), 2d12+6 TP) - * - * Golems nehmen nix an und geben nix. Sie bewegen sich immer nur 1 - * Region weit und ziehen aus Strassen keinen Nutzen. Ein Golem wiegt - * soviel wie ein Stein. - * - * Kann nicht im Sumpf gezaubert werden - * - * Flag: - * (SPELLLEVEL) - * - * #define GOLEM_STONE 4 - */ -static int -sp_create_stonegolem(castorder *co) -{ - unit *u2; - attrib *a; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - int number = lovar(co->force*5*RESOURCE_QUANTITY); - if (number<1) number = 1; - - if (rterrain(r) == T_SWAMP){ - cmistake(mage, co->order, 188, MSG_MAGIC); - return 0; - } - - u2 = create_unit(r, mage->faction, number, new_race[RC_STONEGOLEM], 0, - LOC(mage->faction->locale, rc_name(new_race[RC_STONEGOLEM], 1)), mage); - set_level(u2, SK_ROAD_BUILDING, 1); - set_level(u2, SK_BUILDING, 1); - - a = a_new(&at_unitdissolve); - a->data.ca[0] = 0; - a->data.ca[1] = STONEGOLEM_CRUMBLE; - a_add(&u2->attribs, a); - - 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(new_race[RC_STONEGOLEM], 1)))); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Große Dürre - * Stufe: 17 - * Kategorie: Region, negativ - * Gebiet: Gwyrrd - * - * Wirkung: - * 50% alle Bauern, Pferde, Bäume sterben. - * Zu 25% terraform: Gletscher wird mit 50% zu Sumpf, sonst Ozean, - * Sumpf wird zu Steppe, Ebene zur Steppe, Steppe zur Wüste. - * Besonderheiten: - * neuer Terraintyp Steppe: - * 5000 Felder, 500 Bäume, Strasse: 250 Steine. Anlegen wie in Ebene - * möglich - * - * Flags: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ - -static void -destroy_all_roads(region *r) -{ - int i; - - for(i = 0; i < MAXDIRECTIONS; i++){ - rsetroad(r,(direction_t)i, 0); - } -} - -static int -sp_great_drought(castorder *co) -{ - building *b, *b2; - unit *u; - boolean terraform = false; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = 2; - variant effect; - - if(rterrain(r) == T_OCEAN ) { - cmistake(mage, co->order, 189, MSG_MAGIC); - /* TODO: vielleicht einen netten Patzer hier? */ - return 0; - } - - /* sterben */ - rsetpeasants(r, rpeasants(r)/2); /* evtl wuerfeln */ - rsettrees(r, 2, rtrees(r,2)/2); - rsettrees(r, 1, rtrees(r,1)/2); - rsettrees(r, 0, rtrees(r,0)/2); - rsethorses(r, rhorses(r)/2); - - /* Arbeitslohn = 1/4 */ - effect.i = 4; - create_curse(mage, &r->attribs, ct_find("drought"), force, duration, effect, 0); - - /* terraforming */ - if (rand() % 100 < 25){ - terraform = true; - - switch(rterrain(r)){ - case T_PLAIN: - rsetterrain(r, T_GRASSLAND); - destroy_all_roads(r); - break; - - case T_SWAMP: - rsetterrain(r, T_GRASSLAND); - destroy_all_roads(r); - break; - - case T_GRASSLAND: - rsetterrain(r, T_DESERT); - destroy_all_roads(r); - break; - - case T_GLACIER: -#if NEW_RESOURCEGROWTH == 0 - rsetiron(r, 0); - rsetlaen(r, -1); -#endif - if (rand() % 100 < 50){ - rsetterrain(r, T_SWAMP); - destroy_all_roads(r); - } else { /* Ozean */ - destroy_all_roads(r); - rsetterrain(r, T_OCEAN); - /* Einheiten dürfen hier auf keinen Fall gelöscht werden! */ - for (u = r->units; u; u = u->next) { - if (u->race != new_race[RC_SPELL] && u->ship == 0) { - set_number(u, 0); - } - } - for (b = r->buildings; b;){ - b2 = b->next; - destroy_building(b); - b = b2; - } - } - break; - - default: - terraform = false; - break; - } - } - - /* melden, 1x pro partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - - for (u = r->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - sprintf(buf, "%s ruft das Feuer der Sonne auf %s hinab.", - cansee(u->faction, r, mage, 0)? unitname(mage) : "Jemand", - regionname(r, u->faction)); - if (rterrain(r) != T_OCEAN){ - if(rterrain(r) == T_SWAMP && terraform){ - scat(" Eis schmilzt und verwandelt sich in Morast. Reißende " - "Ströme spülen die mageren Felder weg und ersäufen " - "Mensch und Tier. Was an Bauten nicht den Fluten zum Opfer " - "fiel, verschlingt der Morast. Die sengende Hitze verändert " - "die Region für immer."); - } else { - scat(" Die Felder verdorren und Pferde verdursten. Die Hungersnot " - "kostet vielen Bauern das Leben. Vertrocknete Bäume recken " - "ihre kahlen Zweige in den blauen Himmel, von dem " - "erbarmungslos die sengende Sonne brennt."); - if(terraform){ - scat(" Die Dürre veränderte die Region für immer."); - } - } - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - } else { /* ist Ozean */ - scat(" Das Eis zerbricht und eine gewaltige Flutwelle verschlingt" - "die Region."); - /* es kann gut sein, das in der Region niemand überlebt, also - * besser eine Globalmeldung */ - addmessage(0, u->faction, buf, MSG_EVENT, ML_IMPORTANT); - } - } - } - if (!fval(mage->faction, FL_DH)){ - ADDMSG(&mage->faction->msgs, msg_message( - "drought_effect", "mage region", mage, r)); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: 'Weg der Bäume' - * Stufe: 9 - * Kategorie: Teleport - * Gebiet: Gwyrrd - * Wirkung: - * Der Druide kann 5*Stufe GE in die astrale Ebene schicken. - * Der Druide wird nicht mitteleportiert, es sei denn, er gibt sich - * selbst mit an. - * Der Zauber funktioniert nur in Wäldern. - * - * Syntax: Zauber "Weg der Bäume" ... - * - * Flags: - * (UNITSPELL | SPELLLEVEL | TESTCANSEE) - */ -static int -sp_treewalkenter(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - spellparameter *pa = co->par; - double power = co->force; - int cast_level = co->level; - region *rt; - int remaining_cap; - int n; - int erfolg = 0; - - if (getplane(r) != 0) { - cmistake(mage, co->order, 190, MSG_MAGIC); - return 0; - } - - if (!r_isforest(r)) { - cmistake(mage, co->order, 191, MSG_MAGIC); - return 0; - } - - rt = r_standard_to_astral(r); - if (rt==NULL || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - cmistake(mage, co->order, 192, MSG_MAGIC); - return 0; - } - - remaining_cap = (int)(power * 500); - - /* fuer jede Einheit */ - for (n = 0; n < pa->length; n++) { - unit * u = pa->param[n]->data.u; - spllprm * param = pa->param[n]; - - if (param->flag & (TARGET_RESISTS|TARGET_NOTFOUND)) { - continue; - } - - if (!ucontact(u, mage)) { - cmistake(mage, co->order, 73, MSG_MAGIC); - } else { - int w; - - if (!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - continue; - } - - w = weight(u); - if (remaining_cap - w < 0) { - ADDMSG(&mage->faction->msgs, msg_message("fail_tooheavy", - "command region unit target", co->order, r, mage, u)); - continue; - } - remaining_cap = remaining_cap - w; - move_unit(u, rt, NULL); - erfolg = cast_level; - - /* Meldungen in der Ausgangsregion */ - ADDMSG(&r->msgs, msg_message("astral_disappear", "unit", u)); - - /* Meldungen in der Zielregion */ - ADDMSG(&rt->msgs, msg_message("astral_appear", "unit", u)); - } - } - return erfolg; -} - -/* ------------------------------------------------------------- */ -/* Name: 'Sog des Lebens' - * Stufe: 9 - * Kategorie: Teleport - * Gebiet: Gwyrrd - * Wirkung: - * Der Druide kann 5*Stufe GE aus die astrale Ebene schicken. Der - * Druide wird nicht mitteleportiert, es sei denn, er gibt sich selbst - * mit an. - * Der Zauber funktioniert nur, wenn die Zielregion ein Wald ist. - * - * Syntax: Zauber "Sog des Lebens" ... - * - * Flags: - * (UNITSPELL|SPELLLEVEL) - */ -static int -sp_treewalkexit(castorder *co) -{ - region *rt; - region_list *rl, *rl2; - int tax, tay; - unit *u, *u2; - int remaining_cap; - int n; - int erfolg = 0; - region *r = co->rt; - unit *mage = (unit *)co->magician; - double power = co->force; - spellparameter *pa = co->par; - int cast_level = co->level; - - if(getplane(r) != get_astralplane()) { - cmistake(mage, co->order, 193, MSG_MAGIC); - return 0; - } - if(is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { - cmistake(mage, co->order, 192, MSG_MAGIC); - return 0; - } - - remaining_cap = (int)(power * 500); - - if(pa->param[0]->typ != SPP_REGION){ - report_failure(mage, co->order); - return 0; - } - - /* Koordinaten setzen und Region löschen für Überprüfung auf - * Gültigkeit */ - rt = pa->param[0]->data.r; - tax = rt->x; - tay = rt->y; - rt = NULL; - - rl = astralregions(r, inhabitable); - rt = 0; - - rl2 = rl; - while(rl2) { - if(rl2->data->x == tax && rl2->data->y == tay) { - rt = rl2->data; - break; - } - rl2 = rl2->next; - } - free_regionlist(rl); - - if(!rt) { - cmistake(mage, co->order, 195, MSG_MAGIC); - return 0; - } - - if (!r_isforest(rt)) { - cmistake(mage, co->order, 196, MSG_MAGIC); - return 0; - } - - /* für jede Einheit in der Kommandozeile */ - for (n = 1; n < pa->length; n++) { - if(pa->param[n]->flag == TARGET_RESISTS - || pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - u = pa->param[n]->data.u; - - if (!ucontact(u, mage)) { - sprintf(buf, "%s hat uns nicht kontaktiert.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - } else { - int w = weight(u); - if (!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - } else if(remaining_cap - w < 0) { - sprintf(buf, "%s ist zu schwer.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - } else { - remaining_cap = remaining_cap - w; - move_unit(u, rt, NULL); - erfolg = cast_level; - - /* Meldungen in der Ausgangsregion */ - - for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = r->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, r, u, 0)) { - sprintf(buf, "%s wird durchscheinend und verschwindet.", - unitname(u)); - addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - - /* Meldungen in der Zielregion */ - - for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = rt->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, rt, u, 0)) { - sprintf(buf, "%s erscheint plötzlich.", unitname(u)); - addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - } - } - } - return erfolg; -} - -/* ------------------------------------------------------------- */ -/* Name: Heiliger Boden - * Stufe: 9 - * Kategorie: perm. Regionszauber - * Gebiet: Gwyrrd - * Wirkung: - * Es entstehen keine Untoten mehr, Untote betreten die Region - * nicht mehr. - * - * ZAUBER "Heiliger Boden" - * Flags: (0) - */ -static int -sp_holyground(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - curse * c; - message * msg = r_addmessage(r, mage->faction, msg_message("holyground", "mage", mage)); - msg_release(msg); - - c = create_curse(mage, &r->attribs, ct_find("holyground"), - power*power, 1, zero_effect, 0); - - curse_setflag(c, CURSE_NOAGE); - - a_removeall(&r->attribs, &at_deathcount); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Heimstein - * Stufe: 7 - * Kategorie: Artefakt - * Gebiet: Gwyrrd - * Wirkung: - * Die Burg kann nicht mehr durch Donnerbeben oder andere - * Gebäudezerstörenden Sprüche kaputt gemacht werden. Auch - * schützt der Zauber vor Belagerungskatapulten. - * - * ZAUBER Heimstein - * Flags: (0) - */ -static int -sp_homestone(castorder *co) -{ - unit *u; - curse * c; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - variant effect; - - if(!mage->building || mage->building->type != bt_find("castle")){ - cmistake(mage, co->order, 197, MSG_MAGIC); - return 0; - } - - c = create_curse(mage, &mage->building->attribs, ct_find("magicwalls"), - force*force, 1, zero_effect, 0); - - if (c==NULL) { - cmistake(mage, co->order, 206, MSG_MAGIC); - return 0; - } - curse_setflag(c, CURSE_NOAGE|CURSE_ONLYONE); - - /* Magieresistenz der Burg erhöht sich um 50% */ - effect.i = 50; - c = create_curse(mage, &mage->building->attribs, - ct_find("magicresistance"), force*force, 1, effect, 0); - curse_setflag(c, CURSE_NOAGE); - - /* melden, 1x pro Partei in der Burg */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - if (u->building == mage->building) { - sprintf(buf, "Mit einem Ritual bindet %s die magischen Kräfte " - "der Erde in die Mauern von %s", unitname(mage), - buildingname(mage->building)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - return cast_level; -} - - - -/* ------------------------------------------------------------- */ -/* Name: Dürre - * Stufe: 13 - * Kategorie: Region, negativ - * Gebiet: Gwyrrd - * Wirkung: - * temporär verändert sich das Baummaximum und die maximalen Felder in - * einer Region auf die Hälfte des normalen. - * Die Hälfte der Bäume verdorren und Pferde verdursten. - * Arbeiten bringt nur noch 1/4 des normalen Verdienstes - * - * Flags: - * (FARCASTING|REGIONSPELL|TESTRESISTANCE), - */ -static int -sp_drought(castorder *co) -{ - curse *c; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = (int)power+1; - - if(rterrain(r) == T_OCEAN ) { - cmistake(mage, co->order, 189, MSG_MAGIC); - /* TODO: vielleicht einen netten Patzer hier? */ - return 0; - } - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for(u = r->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s verflucht das Land, und eine Dürreperiode beginnt.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - if(!fval(mage->faction, FL_DH)){ - sprintf(buf, "%s verflucht das Land, und eine Dürreperiode beginnt.", - unitname(mage)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - - /* Wenn schon Duerre herrscht, dann setzen wir nur den Power-Level - * hoch (evtl dauert dann die Duerre laenger). Ansonsten volle - * Auswirkungen. - */ - c = get_curse(r->attribs, ct_find("drought")); - if (c) { - c->vigour = max(c->vigour, power); - c->duration = max(c->duration, (int)power); - } else { - variant effect; - /* Baeume und Pferde sterben */ - rsettrees(r, 2, rtrees(r,2)/2); - rsettrees(r, 1, rtrees(r,1)/2); - rsettrees(r, 0, rtrees(r,0)/2); - rsethorses(r, rhorses(r)/2); - - effect.i = 4; - create_curse(mage, &r->attribs, ct_find("drought"), power, duration, effect, 0); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Nebel der Verwirrung - * Stufe: 14 - * Kategorie: Region, negativ - * Gebiet: Gwyrrd - * Wirkung: - * Alle Regionen innerhalb eines Radius von ((Stufe-15)/2 aufgerundet) - * werden von einem verwirrenden Nebel bedeckt. Innerhalb des Nebels - * können keine Himmelsrichtungen mehr erkannt werden, alle Bewegungen - * erfolgen in eine zufällige Richtung. - * Die Gwyrrd-Variante wirkt nur auf Wälder und Ozeanregionen - * Flags: - * (FARCASTING | SPELLLEVEL) - * */ -static int -sp_fog_of_confusion(castorder *co) -{ - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - double range; - int duration; - region_list *rl,*rl2; - - range = (power-11)/3-1; - duration = (int)((power-11)/1.5)+1; - - rl = all_in_range(r, (short)range, NULL); - - for(rl2 = rl; rl2; rl2 = rl2->next) { - curse * c; - variant effect; - - if(rterrain(rl2->data) != T_OCEAN - && !r_isforest(rl2->data)) continue; - - /* Magieresistenz jeder Region prüfen */ - if (target_resists_magic(mage, r, TYP_REGION, 0)){ - report_failure(mage, co->order); - continue; - } - - effect.i = cast_level*5; - c = create_curse(mage, &rl2->data->attribs, - ct_find("disorientationzone"), power, duration, effect, 0); - /* Soll der schon in der Zauberrunde wirken? */ - curse_setflag(c, CURSE_ISNEW); - - for (u = rl2->data->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = rl2->data->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(rl2->data, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - if(!fval(mage->faction, FL_DH)){ - sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", - unitname(mage)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - } - free_regionlist(rl); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Bergwächter - * Stufe: 9 - * Gebiet: Gwyrrd - * Kategorie: Beschwörung, negativ - * - * Wirkung: - * Erschafft in Bergen oder Gletschern einen Wächter, der durch bewachen - * den Eisen/Laen-Abbau für nicht-Allierte verhindert. Bergwächter - * verhindern auch Abbau durch getarnte/unsichtbare Einheiten und lassen - * sich auch durch Belagerungen nicht aufhalten. - * - * (Ansonsten in economic.c:manufacture() entsprechend anpassen). - * - * Fähigkeiten (factypes.c): 50% Magieresistenz, 25 HP, 4d4 Schaden, - * 4 Rüstung (=PP) - * Flags: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_ironkeeper(castorder *co) -{ - unit *keeper; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - if (rterrain(r) != T_MOUNTAIN && rterrain(r) != T_GLACIER) { - report_failure(mage, co->order); - return 0; - } - - keeper = create_unit(r, mage->faction, 1, new_race[RC_IRONKEEPER], 0, "Bergwächter", mage); - - /*keeper->age = cast_level + 2;*/ - guard(keeper, GUARD_MINING); - fset(keeper, UFL_ISNEW); - keeper->status = ST_AVOID; /* kaempft nicht */ - /* Parteitarnen, damit man nicht sofort weiß, wer dahinter steckt */ - fset(keeper, UFL_PARTEITARNUNG); - { - trigger * tkill = trigger_killunit(keeper); - add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level+2, tkill)); - } - - sprintf(buf, "%s beschwört einen Bergwächter.", unitname(mage)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Sturmwind - Beschwöre einen Sturmelementar - * Stufe: 6 - * Gebiet: Gwyrrd - * - * Wirkung: - * Verdoppelt Geschwindigkeit aller angegebener Schiffe fuer diese - * Runde. Kombinierbar mit "Günstige Winde", aber nicht mit - * "Luftschiff". - * - * Anstelle des alten ship->enchanted benutzen wir einen kurzfristigen - * Curse. Das ist zwar ein wenig aufwendiger, aber weitaus flexibler - * und erlaubt es zB, die Dauer später problemlos zu verändern. - * - * Flags: - * (SHIPSPELL|ONSHIPCAST|OCEANCASTABLE|TESTRESISTANCE) - */ - -static int -sp_stormwinds(castorder *co) -{ - faction *f; - ship *sh; - unit *u; - int erfolg = 0; - region *r = co->rt; - unit *mage = (unit *)co->magician; - double power = co->force; - spellparameter *pa = co->par; - int n, force = (int)power; - - /* melden vorbereiten */ - for(f = factions; f; f = f->next ) freset(f, FL_DH); - - for (n = 0; n < pa->length; n++) { - if (force<=0) break; - - if(pa->param[n]->flag == TARGET_RESISTS - || pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - sh = pa->param[n]->data.sh; - - /* mit C_SHIP_NODRIFT haben wir kein Problem */ - if(is_cursed(sh->attribs, C_SHIP_FLYING, 0) ) { - sprintf(buf, "Es ist zu gefährlich, diesen Zauber auf ein " - "fliegendes Schiff zu legen."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - continue; - } - if(is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { - sprintf(buf, "Auf %s befindet sich bereits ein Zauber", shipname(sh)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - continue; - } - - /* Duration = 1, nur diese Runde */ - create_curse(mage, &sh->attribs, ct_find("stormwind"), power, 1, zero_effect, 0); - /* Da der Spruch nur diese Runde wirkt wird er nie im Report - * erscheinen */ - erfolg++; - force--; - - /* melden vorbereiten: */ - for(u = r->units; u; u = u->next ) { - if(u->ship != sh ) /* nur den Schiffsbesatzungen! */ - continue; - - fset(u->faction, FL_DH); - } - - } - /* melden, 1x pro Partei auf Schiff und für den Magier */ - fset(mage->faction, FL_DH); - for(u = r->units; u; u = u->next ) { - if(fval(u->faction, FL_DH)) { - freset(u->faction, FL_DH); - if (erfolg > 0){ - sprintf(buf, "%s beschwört einen magischen Wind, der die Schiffe " - "über das Wasser treibt.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - return erfolg; -} - - -/* ------------------------------------------------------------- */ -/* Name: Donnerbeben - * Stufe: 6 - * Gebiet: Gwyrrd - * - * Wirkung: - * Zerstört Stufe*10 "Steineinheiten" aller Gebäude der Region, aber nie - * mehr als 25% des gesamten Gebäudes (aber natürlich mindestens ein - * Stein). - * - * Flags: - * (FARCASTING|REGIONSPELL|TESTRESISTANCE) - */ -static int -sp_earthquake(castorder *co) -{ - int kaputt; - building *burg; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - for (burg = r->buildings; burg; burg = burg->next){ - if(burg->size == 0 ) - continue; - - /* Schutzzauber */ - if(is_cursed(burg->attribs, C_MAGICWALLS, 0)) - continue; - - /* Magieresistenz */ - if (target_resists_magic(mage, burg, TYP_BUILDING, 0)) - continue; - - kaputt = min(10 * cast_level, burg->size / 4); - kaputt = max(kaputt, 1); - burg->size -= kaputt; - if(burg->size == 0 ) { - /* alle Einheiten hinausbefördern */ - for(u = r->units; u; u = u->next ) { - if(u->building == burg ) { - u->building = 0; - freset(u, UFL_OWNER); - } - } - /* TODO: sollten die Insassen nicht Schaden nehmen? */ - destroy_building(burg); - } - } - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s läßt die Erde in %s erzittern.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", - regionname(r, u->faction)); - - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - return cast_level; -} - - -/* ------------------------------------------------------------- */ -/* CHAOS / M_CHAOS / Draig */ -/* ------------------------------------------------------------- */ -void -patzer_peasantmob(castorder *co) -{ - int anteil = 6, n; - unit *u; - attrib *a; - region *r; - unit *mage = (unit *)co->magician; - - if (mage->region->land){ - r = mage->region; - } else { - r = co->rt; - } - - if (r->land) { - faction * f = findfaction(MONSTER_FACTION); - const struct locale * lang = f->locale; - - anteil += rand() % 4; - n = rpeasants(r) * anteil / 10; - rsetpeasants(r, rpeasants(r) - n); - assert(rpeasants(r) >= 0); - - u = createunit(r, f, n, new_race[RC_PEASANT]); - fset(u, UFL_ISNEW); - set_string(&u->name, "Bauernmob"); - /* guard(u, GUARD_ALL); hier zu früh! Befehl BEWACHE setzten */ - addlist(&u->orders, parse_order(LOC(lang, keywords[K_GUARD]), lang)); - set_order(&u->thisorder, default_order(lang)); - a = a_new(&at_unitdissolve); - a->data.ca[0] = 1; /* An rpeasants(r). */ - a->data.ca[1] = 10; /* 10% */ - a_add(&u->attribs, a); - a_add(&u->attribs, make_hate(mage)); - - sprintf(buf, "Ein Bauernmob erhebt sich und macht Jagd auf Schwarzmagier."); - addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); - } - return; -} - - -/* ------------------------------------------------------------- */ -/* Name: Waldbrand - * Stufe: 10 - * Kategorie: Region, negativ - * Gebiet: Draig - * Wirkung: - * Vernichtet 10-80% aller Baeume in der Region. Kann sich auf benachbarte - * Regionen ausbreiten, wenn diese (stark) bewaldet sind. Für jeweils - * 10 verbrannte Baeume in der Startregion gibts es eine 1%-Chance, dass - * sich das Feuer auf stark bewaldete Nachbarregionen ausdehnt, auf - * bewaldeten mit halb so hoher Wahrscheinlichkeit. Dort verbrennen - * dann prozentual halbsoviele bzw ein viertel soviele Baeume wie in der - * Startregion. - * - * Im Extremfall: 1250 Baeume in Region, 80% davon verbrennen (1000). - * Dann breitet es sich mit 100% Chance in stark bewaldete Regionen - * aus, mit 50% in bewaldete. Dort verbrennen dann 40% bzw 20% der Baeume. - * Weiter als eine Nachbarregion breitet sich dass Feuer nicht aus. - * - * Sinn: Ein Feuer in einer "stark bewaldeten" Wueste hat so trotzdem kaum - * eine Chance, sich weiter auszubreiten, waehrend ein Brand in einem Wald - * sich fast mit Sicherheit weiter ausbreitet. - * - * Flags: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_forest_fire(castorder *co) -{ - unit *u; - region *nr; - direction_t i; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double probability; - double percentage = (rand() % 8 + 1) * 0.1; /* 10 - 80% */ - - int vernichtet_schoesslinge = (int)(rtrees(r, 1) * percentage); - int destroyed = (int)(rtrees(r, 2) * percentage); - - if (destroyed<1) { - cmistake(mage, co->order, 198, MSG_MAGIC); - return 0; - } - - rsettrees(r, 2, rtrees(r,2) - destroyed); - rsettrees(r, 1, rtrees(r,1) - vernichtet_schoesslinge); - probability = destroyed * 0.001; /* Chance, dass es sich ausbreitet */ - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - - for(u = r->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s erzeugt eine verheerende Feuersbrunst. %d %s " - "den Flammen zum Opfer.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", - destroyed, - destroyed == 1 ? "Baum fiel" : "Bäume fielen"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - if(!fval(mage->faction, FL_DH)){ - sprintf(buf, "%s erzeugt eine verheerende Feuersbrunst. %d %s " - "den Flammen zum Opfer.", unitname(mage), destroyed+vernichtet_schoesslinge, - destroyed+vernichtet_schoesslinge == 1 ? "Baum fiel" : "Bäume fielen"); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - - for(i = 0; i < MAXDIRECTIONS; i++ ) { - nr = rconnect(r, i); - assert(nr); - destroyed = 0; - vernichtet_schoesslinge = 0; - - if(rtrees(nr,2) + rtrees(nr,1) >= 800) { - if (chance(probability)) { - destroyed = (int)(rtrees(nr,2) * percentage/2); - vernichtet_schoesslinge = (int)(rtrees(nr,1) * percentage/2); - } - } else if (rtrees(nr,2) + rtrees(nr,1) >= 600) { - if (chance(probability/2)) { - destroyed = (int)(rtrees(nr,2) * percentage/4); - vernichtet_schoesslinge = (int)(rtrees(nr,1) * percentage/4); - } - } - - if (destroyed > 0 || vernichtet_schoesslinge > 0) { - message * m = msg_message("forestfire_spread", "region next trees", - r, nr, destroyed+vernichtet_schoesslinge); - - add_message(&r->msgs, m); - add_message(&mage->faction->msgs, m); - msg_release(m); - - rsettrees(nr, 2, rtrees(nr,2) - destroyed); - rsettrees(nr, 1, rtrees(nr,1) - vernichtet_schoesslinge); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Chaosfluch - * Stufe: 5 - * Gebiet: Draig - * Kategorie: (Antimagie) Kraftreduzierer, Einheit, negativ - * Wirkung: - * Auf einen Magier gezaubert verhindert/erschwert dieser Chaosfluch - * das Zaubern. Patzer werden warscheinlicher. - * Jeder Zauber muss erst gegen den Wiederstand des Fluchs gezaubert - * werden und schwächt dessen Antimagiewiederstand um 1. - * Wirkt max(Stufe(Magier) - Stufe(Ziel), rand(3)) Wochen - * Patzer: - * Magier wird selbst betroffen - * - * Flags: - * (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE | TESTRESISTANCE) - * - */ -static int -sp_fumblecurse(castorder *co) -{ - unit *target; - int rx, sx; - int duration; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - variant effect; - curse * c; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; - - rx = rand()%3; - sx = cast_level - effskill(target, SK_MAGIC); - duration = max(sx, rx) + 1; - - effect.i = (int)(force/2); - c = create_curse(mage, &target->attribs, ct_find("fumble"), - force, duration, effect, 0); - if (c == NULL) { - report_failure(mage, co->order); - return 0; - } - - curse_setflag(c, CURSE_ONLYONE); - ADDMSG(&target->faction->msgs, msg_message( - "fumblecurse", "unit region", target, target->region)); - - return cast_level; -} - -void -patzer_fumblecurse(castorder *co) -{ - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (cast_level/2)+1; - variant effect; - curse * c; - - effect.i = (int)(force/2); - c = create_curse(mage, &mage->attribs, ct_find("fumble"), force, - duration, effect, 0); - if (c!=NULL) { - ADDMSG(&mage->faction->msgs, msg_message( - "magic_fumble", "unit region command", - mage, mage->region, co->order)); - curse_setflag(c, CURSE_ONLYONE); - } - return; -} - -/* ------------------------------------------------------------- */ -/* Name: Drachenruf - * Stufe: 11 - * Gebiet: Draig - * Kategorie: Monster, Beschwörung, negativ - * - * Wirkung: - * In einer Wüste, Sumpf oder Gletscher gezaubert kann innerhalb der - * nächsten 6 Runden ein bis 6 Dracheneinheiten bis Größe Wyrm - * entstehen. - * - * Mit Stufe 12-15 erscheinen Jung- oder normaler Drachen, mit Stufe - * 16+ erscheinen normale Drachen oder Wyrme. - * - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ - -static int -sp_summondragon(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - unit *u; - int cast_level = co->level; - double power = co->force; - region_list *rl,*rl2; - faction *f; - int time; - int number; - const race * race; - - f = findfaction(MONSTER_FACTION); - - if(rterrain(r) != T_SWAMP && rterrain(r) != T_DESERT - && rterrain(r) != T_GLACIER){ - report_failure(mage, co->order); - return 0; - } - - for(time = 1; time < 7; time++){ - if (rand()%100 < 25){ - switch(rand()%3){ - case 0: - race = new_race[RC_WYRM]; - number = 1; - break; - - case 1: - race = new_race[RC_DRAGON]; - number = 2; - break; - - case 2: - default: - race = new_race[RC_FIREDRAGON]; - number = 6; - break; - } - { - trigger * tsummon = trigger_createunit(r, f, race, number); - add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon)); - } - } - } - - rl = all_in_range(r, (short)power, NULL); - - for(rl2 = rl; rl2; rl2 = rl2->next) { - for(u = rl2->data->units; u; u = u->next) { - if (u->race == new_race[RC_WYRM] || u->race == new_race[RC_DRAGON]) { - attrib * a = a_find(u->attribs, &at_targetregion); - if (!a) { - a = a_add(&u->attribs, make_targetregion(co->rt)); - } else { - a->data.v = co->rt; - } - sprintf(buf, "Kommt aus: %s, Will nach: %s", regionname(rl2->data, u->faction), regionname(co->rt, u->faction)); - usetprivate(u, buf); - } - } - } - - ADDMSG(&mage->faction->msgs, msg_message( - "summondragon", "unit region command region", - mage, mage->region, co->order, co->rt)); - - free_regionlist(rl); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Feuerwand - * Stufe: - * Gebiet: Draig - * Kategorie: Region, negativ - * Flag: - * Kosten: SPC_LINEAR - * Aura: - * Komponenten: - * - * Wirkung: - * eine Wand aus Feuer entsteht in der angegebenen Richtung - * - * Was für eine Wirkung hat die? - */ - -typedef struct wallcurse { - curse * buddy; - border * wall; -} wallcurse; - -void -wall_vigour(curse* c, double delta) -{ - wallcurse * wc = (wallcurse*)c->data.v; - assert(wc->buddy->vigour==c->vigour); - wc->buddy->vigour += delta; - if (wc->buddy->vigour<=0) { - erase_border(wc->wall); - wc->wall = NULL; - ((wallcurse*)wc->buddy->data.v)->wall = NULL; - } -} - -const curse_type ct_firewall = { - "Feuerwand", - CURSETYP_NORM, 0, (M_DURATION | M_VIGOUR | NO_MERGE), - "Eine Feuerwand blockiert die Ein- und Ausreise", - NULL, /* curseinfo */ - wall_vigour /* change_vigour */ -}; - -void -cw_init(attrib * a) { - curse * c; - curse_init(a); - c = (curse*)a->data.v; - c->data.v = calloc(sizeof(wallcurse), 1); -} - -void -cw_write(const attrib * a, FILE * f) { - border * b = ((wallcurse*)((curse*)a->data.v)->data.v)->wall; - curse_write(a, f); - fprintf(f, "%d ", b->id); -} - -typedef struct bresvole { - unsigned int id; - curse * self; -} bresolve; - -static void * -resolve_buddy(variant data) { - bresolve * br = (bresolve*)data.v; - border * b = find_border(br->id); - if (b && b->from && b->to) { - attrib * a = a_find(b->from->attribs, &at_cursewall); - while (a && a->data.v!=br->self) { - curse * c = (curse*)a->data.v; - wallcurse * wc = (wallcurse*)c->data.v; - if (wc->wall->id==br->id) break; - a = a->nexttype; - } - if (!a) { - a = a_find(b->to->attribs, &at_cursewall); - while (a && a->data.v!=br->self) { - curse * c = (curse*)a->data.v; - wallcurse * wc = (wallcurse*)c->data.v; - if (wc->wall->id==br->id) break; - a = a->nexttype; - } - } - if (a) { - curse * c = (curse*)a->data.v; - free(br); - return c; - } - } - return NULL; -} - -int -cw_read(attrib * a, FILE * f) -{ - bresolve * br = calloc(sizeof(bresolve), 1); - curse * c = (curse*)a->data.v; - wallcurse * wc = (wallcurse*)c->data.v; - variant var; - - curse_read(a, f); - br->self = c; - fscanf(f, "%u ", &br->id); - - var.i = br->id; - ur_add(var, (void**)&wc->wall, resolve_borderid); - - var.v = br; - ur_add(var, (void**)&wc->buddy, resolve_buddy); - return AT_READ_OK; -} - -attrib_type at_cursewall = -{ - "cursewall", - cw_init, - curse_done, - curse_age, - cw_write, - cw_read, - ATF_CURSE -}; - -static const char * -fire_name(const border * b, const region * r, const faction * f, int gflags) -{ - unused(f); - unused(r); - unused(b); - if (gflags & GF_ARTICLE) - return "eine Feuerwand"; - else - return "Feuerwand"; -} - -static void -wall_init(border * b) -{ - b->data.v = calloc(sizeof(wall_data), 1); -} - -static void -wall_destroy(border * b) -{ - free(b->data.v); -} - -static void -wall_read(border * b, FILE * f) -{ - wall_data * fd = (wall_data*)b->data.v; - variant mno; - assert(fd); - fscanf(f, "%d %d ", &mno.i, &fd->force); - fd->mage = findunitg(mno.i, NULL); - fd->active = true; - if (!fd->mage) { - ur_add(mno, (void**)&fd->mage, resolve_unit); - } -} - -static void -wall_write(const border * b, FILE * f) -{ - wall_data * fd = (wall_data*)b->data.v; - fprintf(f, "%d %d ", fd->mage?fd->mage->no:0, fd->force); -} - -static region * -wall_move(const border * b, struct unit * u, struct region * from, struct region * to, boolean routing) -{ - wall_data * fd = (wall_data*)b->data.v; - if (!routing && fd->active) { - int hp = dice(3, fd->force) * u->number; - hp = min (u->hp, hp); - u->hp -= hp; - if (u->hp) { - ADDMSG(&u->faction->msgs, msg_message("firewall_damage", - "region unit", from, u)); - } - else ADDMSG(&u->faction->msgs, msg_message("firewall_death", "region unit", from, u)); - if (u->number>u->hp) { - scale_number(u, u->hp); - u->hp = u->number; - } - } - return to; -} - -border_type bt_firewall = { - "firewall", VAR_VOIDPTR, - b_transparent, /* transparent */ - wall_init, /* init */ - wall_destroy, /* destroy */ - wall_read, /* read */ - wall_write, /* write */ - b_blocknone, /* block */ - fire_name, /* name */ - b_rvisible, /* rvisible */ - b_finvisible, /* fvisible */ - b_uinvisible, /* uvisible */ - NULL, - wall_move -}; - -static int -sp_firewall(castorder *co) -{ - border * b; - wall_data * fd; - attrib * a; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - direction_t dir; - region * r2; - - dir = finddirection(pa->param[0]->data.s, mage->faction->locale); - if (dirorder); - return 0; - } - - if (!r2 || r2==r) { - report_failure(mage, co->order); - return 0; - } - - b = get_borders(r, r2); - while (b!=NULL) { - if (b->type == &bt_firewall) break; - b = b->next; - } - if (b==NULL) { - b = new_border(&bt_firewall, r, r2); - fd = (wall_data*)b->data.v; - fd->force = (int)(force/2+0.5); - fd->mage = mage; - fd->active = false; - } else { - fd = (wall_data*)b->data.v; - fd->force = (int)max(fd->force, force/2+0.5); - } - - a = a_find(b->attribs, &at_countdown); - if (a==NULL) { - a = a_add(&b->attribs, a_new(&at_countdown)); - a->data.i = cast_level; - } else { - a->data.i = max(a->data.i, cast_level); - } - - /* 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); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ - -static const char * -wisps_name(const border * b, const region * r, const faction * f, int gflags) -{ - unused(f); - unused(r); - unused(b); - if (gflags & GF_ARTICLE) - return "eine Gruppe von Irrlichtern"; - else - return "Irrlichter"; -} - -static region * -wisps_move(const border * b, struct unit * u, struct region * from, struct region * next, boolean routing) -{ - direction_t reldir = reldirection(from, next); - wall_data * wd = (wall_data*)b->data.v; - assert(reldir!=D_SPECIAL); - - if (routing && wd->active) { - /* pick left and right region: */ - region * rl = rconnect(from, (direction_t)((reldir+MAXDIRECTIONS-1)%MAXDIRECTIONS)); - region * rr = rconnect(from, (direction_t)((reldir+1)%MAXDIRECTIONS)); - int j = rand() % 3; - if (j==1 && rl && landregion(rterrain(rl))==landregion(rterrain(next))) return rl; - if (j==2 && rr && landregion(rterrain(rr))==landregion(rterrain(next))) return rr; - } - return next; -} - -border_type bt_wisps = { - "wisps", VAR_VOIDPTR, - b_transparent, /* transparent */ - wall_init, /* init */ - wall_destroy, /* destroy */ - wall_read, /* read */ - wall_write, /* write */ - b_blocknone, /* block */ - wisps_name, /* name */ - b_rvisible, /* rvisible */ - b_fvisible, /* fvisible */ - b_uvisible, /* uvisible */ - NULL, /* visible */ - wisps_move -}; - -static int -sp_wisps(castorder *co) -{ - border * b; - wall_data * fd; - region * r2; - direction_t dir; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - - dir = finddirection(pa->param[0]->data.s, mage->faction->locale); - r2 = rconnect(r, dir); - - if (!r2) { - report_failure(mage, co->order); - return 0; - } - - b = new_border(&bt_wisps, r, r2); - fd = (wall_data*)b->data.v; - fd->force = (int)(force/2+0.5); - fd->mage = mage; - fd->active = false; - - a_add(&b->attribs, a_new(&at_countdown))->data.i = cast_level; - - /* melden, 1x pro Partei */ - { - message * seen = msg_message("wisps_effect", "mage region", mage, r); - message * unseen = msg_message("wisps_effect", "mage region", NULL, r); - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Unheilige Kraft - * Stufe: 10 - * Gebiet: Draig - * Kategorie: Untote Einheit, positiv - * - * Wirkung: - * transformiert (Stufe)W10 Untote in ihre stärkere Form - * - * - * Flag: - * (SPELLLEVEL | TESTCANSEE) - */ - -static int -sp_unholypower(castorder *co) -{ - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - int i; - int n; - int wounds; - - n = dice((int)co->force, 10); - - for (i = 0; i < pa->length && n > 0; i++) { - const race * target_race; - unit *u; - - if(pa->param[i]->flag == TARGET_RESISTS - || pa->param[i]->flag == TARGET_NOTFOUND) - continue; - - u = pa->param[i]->data.u; - - switch (old_race(u->race)) { - case RC_SKELETON: - target_race = new_race[RC_SKELETON_LORD]; - break; - case RC_ZOMBIE: - target_race = new_race[RC_ZOMBIE_LORD]; - break; - case RC_GHOUL: - target_race = new_race[RC_GHOUL_LORD]; - break; - default: - cmistake(mage, co->order, 284, MSG_MAGIC); - continue; - } - /* Untote heilen nicht, darum den neuen Untoten maximale hp geben - * und vorhandene Wunden abziehen */ - wounds = unit_max_hp(u)*u->number - u->hp; - - if(u->number <= n) { - n -= u->number; - u->race = u->irace = target_race; - u->hp = unit_max_hp(u)*u->number - wounds; - ADDMSG(&co->rt->msgs, msg_message("unholypower_effect", - "mage target race", mage, u, target_race)); - } else { - unit *un; - - /* Wird hoffentlich niemals vorkommen. Es gibt im Source - * vermutlich eine ganze Reihe von Stellen, wo das nicht - * korrekt abgefangen wird. Besser (aber nicht gerade einfach) - * wäre es, eine solche Konstruktion irgendwie zu kapseln. */ - if(fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER) - || is_cursed(u->attribs, C_SLAVE, 0)) { - cmistake(mage, co->order, 74, MSG_MAGIC); - continue; - } - /* Verletzungsanteil der transferierten Personen berechnen */ - wounds = wounds*n/u->number; - - un = create_unit(co->rt, u->faction, 0, target_race, 0, NULL, u); - transfermen(u, un, n); - un->hp = unit_max_hp(un)*n - wounds; - ADDMSG(&co->rt->msgs, msg_message("unholypower_limitedeffect", - "mage target race amount", - mage, u, target_race, n)); - n = 0; - } - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Todeswolke - * Stufe: 11 - * Gebiet: Draig - * Kategorie: Region, negativ - * - * Wirkung: - * Personen in der Region verlieren stufe/2 Trefferpunkte pro Runde. - * Dauer force/2 - * Wirkt gegen MR - * Rüstung wirkt nicht - * Patzer: - * Magier gerät in den Staub und verliert zufällige Zahl von HP bis - * auf max(hp,2) - * Besonderheiten: - * Nicht als curse implementiert, was schlecht ist - man kann dadurch - * kein dispell machen. Wegen fix unter Zeitdruck erstmal nicht zu - * ändern... - * Missbrauchsmöglichkeit: - * Hat der Magier mehr HP als Rasse des Feindes (extrem: Dämon/Goblin) - * so kann er per Farcasting durch mehrmaliges Zaubern eine - * Nachbarregion auslöschen. Darum sollte dieser Spruch nur einmal auf - * eine Region gelegt werden können. - * - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ - -typedef struct dc_data { - region * r; - unit * mage; - double strength; - int countdown; - boolean active; -} dc_data; - -static void -dc_initialize(struct attrib *a) -{ - dc_data * data = (dc_data *)malloc(sizeof(dc_data)); - a->data.v = data; - data->active = true; -} - -static void -dc_finalize(struct attrib * a) -{ - free(a->data.v); -} - -static int -dc_age(struct attrib * a) -/* age returns 0 if the attribute needs to be removed, !=0 otherwise */ -{ - dc_data * data = (dc_data *)a->data.v; - region * r = data->r; - unit ** up = &r->units; - unit * mage = data->mage; - unit * u; - - if (mage==NULL || mage->number==0) { - /* if the mage disappears, so does the spell. */ - return 0; - } - - if (data->active) while (*up!=NULL) { - unit * u = *up; - double damage = data->strength * u->number; - - freset(u->faction, FL_DH); - if (target_resists_magic(mage, u, TYP_UNIT, 0)){ - continue; - } - - /* Reduziert durch Magieresistenz */ - damage *= (1.0 - magic_resistance(u)); - change_hitpoints(u, -(int)damage); - - if (*up==u) up=&u->next; - } - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - ADDMSG(&u->faction->msgs, msg_message("deathcloud_effect", - "mage region", cansee(u->faction, r, mage, 0) ? mage : NULL, r)); - } - } - - if (!fval(mage->faction, FL_DH)){ - ADDMSG(&mage->faction->msgs, msg_message("deathcloud_effect", - "mage region", mage, r)); - } - - return --data->countdown; -} - -static void -dc_write(const struct attrib * a, FILE* F) -{ - const dc_data * data = (const dc_data *)a->data.v; - fprintf(F, "%d %lf ", data->countdown, data->strength); - write_unit_reference(data->mage, F); - write_region_reference(data->r, F); -} - -static int -dc_read(struct attrib * a, FILE* F) -/* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ -{ - dc_data * data = (dc_data *)a->data.v; - fscanf(F, "%d %lf ", &data->countdown, &data->strength); - read_unit_reference(&data->mage, F); - return read_region_reference(&data->r, F); -} - -attrib_type at_deathcloud = { - "zauber_todeswolke", dc_initialize, dc_finalize, dc_age, dc_write, dc_read -}; - -static attrib * -mk_deathcloud(unit * mage, region * r, double strength, int duration) -{ - attrib * a = a_new(&at_deathcloud); - dc_data * data = (dc_data *)a->data.v; - - data->countdown = duration; - data->r = r; - data->mage = mage; - data->strength = strength; - data->active = false; - return a; -} - -static int -sp_deathcloud(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - - attrib *a = a_find(r->attribs, &at_deathcloud); - - if (a!=NULL) { - report_failure(mage, co->order); - return 0; - } - - a_add(&r->attribs, mk_deathcloud(mage, r, co->force/2, co->level)); - - return co->level; -} - -void -patzer_deathcloud(castorder *co) -{ - unit *mage = (unit *)co->magician; - int hp = (mage->hp - 2); - - change_hitpoints(mage, -rand()%hp); - - ADDMSG(&mage->faction->msgs, msg_message( - "magic_fumble", "unit region command", - mage, mage->region, co->order)); - - return; -} - -/* ------------------------------------------------------------- */ -/* Name: Pest - * Stufe: 7 - * Gebiet: Draig - * Wirkung: - * ruft eine Pest in der Region hervor. - * Flags: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - * Syntax: ZAUBER [REGION x y] "Pest" - */ -static int -sp_plague(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - plagues(r, true); - - ADDMSG(&mage->faction->msgs, msg_message("plague_spell", - "region mage", r, mage)); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Beschwöre Schattendämon - * Stufe: 8 - * Gebiet: Draig - * Kategorie: Beschwörung, positiv - * Wirkung: - * Der Magier beschwört Stufe^2 Schattendämonen. - * Schattendämonen haben Tarnung = (Magie_Magier+ Tarnung_Magier)/2 und - * Wahrnehmung 1. Sie haben einen Attacke-Bonus von 8, einen - * Verteidigungsbonus von 11 und machen 2d3 Schaden. Sie entziehen bei - * einem Treffer dem Getroffenen einen Attacke- oder - * Verteidigungspunkt. (50% Chance.) Sie haben 25 Hitpoints und - * Rüstungsschutz 3. - * Flag: - * (SPELLLEVEL) - */ -static int -sp_summonshadow(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - unit *u; - int val; - - u = create_unit(r, mage->faction, (int)(force*force), new_race[RC_SHADOW], 0, NULL, mage); - - /* Bekommen Tarnung = (Magie+Tarnung)/2 und Wahrnehmung 1. */ - val = get_level(mage, SK_MAGIC) + get_level(mage, SK_STEALTH); - - set_level(u, SK_STEALTH, val); - set_level(u, SK_OBSERVATION, 1); - - sprintf(buf, "%s beschwört %d Dämonen aus dem Reich der Schatten.", - unitname(mage), (int)(force*force)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Beschwöre Schattenmeister - * Stufe: 12 - * Gebiet: Draig - * Kategorie: Beschwörung, positiv - * Wirkung: - * Diese höheren Schattendämonen sind erheblich gefährlicher als die - * einfachen Schattendämonen. Sie haben Tarnung entsprechend dem - * Magietalent des Beschwörer-1 und Wahrnehmung 5, 75 HP, - * Rüstungsschutz 4, Attacke-Bonus 11 und Verteidigungsbonus 13, machen - * bei einem Treffer 2d4 Schaden, entziehen einen Stärkepunkt und - * entziehen 5 Talenttage in einem zufälligen Talent. - * Stufe^2 Dämonen. - * - * Flag: - * (SPELLLEVEL) - * */ -static int -sp_summonshadowlords(castorder *co) -{ - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - - u = create_unit(r, mage->faction, (int)(force*force), new_race[RC_SHADOWLORD], 0, NULL, mage); - - /* Bekommen Tarnung = Magie und Wahrnehmung 5. */ - set_level(u, SK_STEALTH, get_level(mage, SK_MAGIC)); - set_level(u, SK_OBSERVATION, 5); - sprintf(buf, "%s beschwört %d Schattenmeister.", - unitname(mage), (int)(force*force)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Chaossog - * Stufe: 14 - * Gebiet: Draig - * Kategorie: Teleport - * Wirkung: - * Durch das Opfern von 200 Bauern kann der Chaosmagier ein Tor zur - * astralen Welt öffnen. Das Tor kann im Folgemonat verwendet werden, - * es löst sich am Ende des Folgemonats auf. - * - * Flag: (0) - */ -static int -sp_chaossuction(castorder *co) -{ - unit *u; - region *rt; - faction *f; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - if (getplane(r)!=get_normalplane()) { - /* Der Zauber funktioniert nur in der materiellen Welt. */ - cmistake(mage, co->order, 190, MSG_MAGIC); - return 0; - } - - rt = r_standard_to_astral(r); - - if (rt==NULL || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - /* Hier gibt es keine Verbindung zur astralen Welt.*/ - cmistake(mage, co->order, 216, MSG_MAGIC); - return 0; - } - - create_special_direction(r, rt, 2, - "Ein Wirbel aus reinem Chaos zieht über die Region", - "Wirbel"); - create_special_direction(rt, r, 2, - "Ein Wirbel aus reinem Chaos zieht über die Region", - "Wirbel"); - new_border(&bt_chaosgate, r, rt); - - for (f = factions; f; f = f->next) freset(f, FL_DH); - for (u = r->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - sprintf(buf, "%s öffnete ein Chaostor.", - cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - for (u = rt->units; u; u = u->next) freset(u->faction, FL_DH); - - for (u = rt->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - addmessage(r, u->faction, "Ein Wirbel aus blendendem Licht erscheint.", - MSG_EVENT, ML_INFO); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Magic Boost - Gabe des Chaos - * Stufe: 3 - * Gebiet: Draig - * Kategorie: Einheit, positiv - * - * Wirkung: - * Erhöht die maximalen Magiepunkte und die monatliche Regeneration auf - * das doppelte. Dauer: 4 Wochen Danach sinkt beides auf die Hälfte des - * normalen ab. - * Dauer: 6 Wochen - * Patzer: - * permanenter Stufen- (Talenttage), Regenerations- oder maxMP-Verlust - * Besonderheiten: - * Patzer können während der Zauberdauer häufiger auftreten derzeit - * +10% - * - * Flag: - * (ONSHIPCAST) - */ - -static int -sp_magicboost(castorder *co) -{ - curse * c; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - variant effect; - trigger * tsummon; - static const curse_type * ct_auraboost; - static const curse_type * ct_magicboost; - - if (!ct_auraboost) { - ct_auraboost = ct_find("auraboost"); - ct_magicboost = ct_find("magicboost"); - assert(ct_auraboost!=NULL); - assert(ct_magicboost!=NULL); - } - /* fehler, wenn schon ein boost */ - if(is_cursed(mage->attribs, C_MBOOST, 0) == true){ - report_failure(mage, co->order); - return 0; - } - - effect.i = 6; - c = create_curse(mage, &mage->attribs, ct_magicboost, power, 10, effect, 1); - /* kann nicht durch Antimagie beeinflusst werden */ - curse_setflag(c, CURSE_IMMUNE); - - /* one aura boost with 200% aura now: */ - effect.i = 200; - c = create_curse(mage, &mage->attribs, ct_auraboost, power, 4, effect, 1); - - /* and one aura boost with 50% aura in 5 weeks: */ - tsummon = trigger_createcurse(mage, mage, ct_auraboost, power, 6, 50, 1); - add_trigger(&mage->attribs, "timer", trigger_timeout(5, tsummon)); - - ADDMSG(&mage->faction->msgs, msg_message("magicboost_effect", - "unit region command", mage, mage->region, co->order)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: kleines Blutopfer - * Stufe: 4 - * Gebiet: Draig - * Kategorie: Einheit, positiv - * - * Wirkung: - * Hitpoints to Aura: - * skill < 8 = 4:1 - * skill < 12 = 3:1 - * skill < 15 = 2:1 - * skill < 18 = 1:2 - * skill > = 2:1 - * Patzer: - * permanenter HP verlust - * - * Flag: - * (ONSHIPCAST) - */ -static int -sp_bloodsacrifice(castorder *co) -{ - unit *mage = (unit *)co->magician; - int cast_level = co->level; - int aura; - int skill = eff_skill(mage, SK_MAGIC, mage->region); - int hp = (int)(co->force*8); - - if (hp <= 0) { - report_failure(mage, co->order); - return 0; - } - - aura = lovar(hp); - - if (skill < 8) { - aura /= 4; - } else if (skill < 12){ - aura /= 3; - } else if (skill < 15){ - aura /= 2; - /* von 15 bis 17 ist hp = aura */ - } else if (skill > 17){ - aura *= 2; - } - - if (aura <= 0){ - report_failure(mage, co->order); - return 0; - } - - /* sicherheitshalber gibs hier einen HP gratis. sonst schaffen es - * garantiert ne ganze reihe von leuten ihren Magier damit umzubringen */ - mage->hp++; - change_spellpoints(mage, aura); - ADDMSG(&mage->faction->msgs, - msg_message("sp_bloodsacrifice_effect", - "unit region command amount", - mage, mage->region, co->order, aura)); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Totenruf - Mächte des Todes - * Stufe: 6 - * Gebiet: Draig - * Kategorie: Beschwörung, positiv - * Flag: FARCASTING - * Wirkung: - * Untote aus deathcounther ziehen, bis Stufe*10 Stück - * - * Patzer: - * Erzeugt Monsteruntote - */ -static int -sp_summonundead(castorder *co) -{ - int undead; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - int force = (int)(co->force*10); - const race * race = new_race[RC_SKELETON]; - - if (!r->land || deathcount(r) == 0) { - sprintf(buf, "%s in %s: In %s sind keine Gräber.", unitname(mage), - regionname(mage->region, mage->faction), regionname(r, mage->faction)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - undead = min(deathcount(r), 2 + lovar(force)); - - if(cast_level <= 8) { - race = new_race[RC_SKELETON]; - } else if(cast_level <= 12) { - race = new_race[RC_ZOMBIE]; - } else { - race = new_race[RC_GHOUL]; - } - - u = create_unit(r, mage->faction, undead, race, 0, NULL, mage); - make_undead_unit(u); - - sprintf(buf, "%s erweckt %d Untote aus ihren Gräbern.", - unitname(mage), undead); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - - for(u = r->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s stört die Ruhe der Toten", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Astraler Sog - * Stufe: 9 - * Gebiet: Draig - * Kategorie: Region, negativ - * Wirkung: - * Allen Magier in der betroffenen Region wird eine Teil ihrer - * Magischen Kraft in die Gefilde des Chaos entzogen Jeder Magier im - * Einflussbereich verliert Stufe(Zaubernden)*5% seiner Magiepunkte. - * Keine Regeneration in der Woche (fehlt noch) - * - * Flag: - * (REGIONSPELL | TESTRESISTANCE) - */ - -static int -sp_auraleak(castorder *co) -{ - int lost_aura; - double lost; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - lost = min(0.95, cast_level * 0.05); - - for(u = r->units; u; u = u->next) { - if (is_mage(u)){ - /* Magieresistenz Einheit? Bei gegenerischen Magiern nur sehr - * geringe Chance auf Erfolg wg erhöhter MR, würde Spruch sinnlos - * machen */ - lost_aura = (int)(get_spellpoints(u)*lost); - change_spellpoints(u, -lost_aura); - } - freset(u->faction, FL_DH); - } - for (u = r->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - if (cansee(u->faction, r, mage, 0)) { - sprintf(buf, "%s rief in %s einen Riss in dem Gefüge der Magie " - "hervor, der alle magische Kraft aus der Region riss.", - unitname(mage), regionname(r, u->faction)); - } else { - sprintf(buf, "In %s entstand ein Riss in dem Gefüge der Magie, " - "der alle magische Kraft aus der Region riss.", - regionname(r, u->faction)); - } - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* BARDE - CERDDOR*/ -/* ------------------------------------------------------------- */ -/* Name: Plappermaul - * Stufe: 4 - * Gebiet: Cerddor - * Kategorie: Einheit - * - * Wirkung: - * Einheit ausspionieren. Gibt auch Zauber und Kampfstatus aus. Wirkt - * gegen Magieresistenz. Ist diese zu hoch, so wird der Zauber entdeckt - * (Meldung) und der Zauberer erhält nur die Talente, nicht die Werte - * der Einheit und auch keine Zauber. - * - * Flag: - * (UNITSPELL | ONETARGET | TESTCANSEE) - */ -static int -sp_babbler(castorder *co) -{ - unit *target; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; - - if (target->faction == mage->faction){ - /* Die Einheit ist eine der unsrigen */ - cmistake(mage, co->order, 45, MSG_MAGIC); - } - - /* Magieresistenz Unit */ - if (target_resists_magic(mage, target, TYP_UNIT, 0)){ - spy_message(5, mage, target); - sprintf(buf, "%s hat einen feuchtfröhlichen Abend in der Taverne " - "verbracht. Ausser einem fürchterlichen Brummschädel ist da auch " - "noch das dumme Gefühl %s seine ganze Lebensgeschichte " - "erzählt zu haben.", unitname(target), - cansee(target->faction, r, mage, 0)? "irgendjemanden":unitname(mage)); - addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); - - } else { - spy_message(100, mage, target); - sprintf(buf, "%s hat einen feuchtfröhlichen Abend in der Taverne " - "verbracht. Ausser einem fürchterlichen Brummschädel ist da auch " - "noch das dumme Gefühl die ganze Taverne mit seiner Lebensgeschichte " - "unterhalten zu haben.", unitname(target)); - addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Magie analysieren - Gebäude, Schiffe, Region - * Name: Lied des Ortes analysieren - * Stufe: 8 - * Gebiet: Cerddor - * - * Wirkung: - * Zeigt die Verzauberungen eines Objekts an (curse->name, - * curse::info). Aus der Differenz Spruchstärke und Curse->vigour - * ergibt sich die Chance den Spruch zu identifizieren ((force - - * c->vigour)*10 + 100 %). - * - * Flag: - * (SPELLLEVEL|ONSHIPCAST) - */ -static int -sp_analysesong_obj(castorder *co) -{ - int obj; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - - obj = pa->param[0]->typ; - - switch(obj) { - case SPP_REGION: - magicanalyse_region(r, mage, force); - break; - - case SPP_BUILDING: - { - building *b = pa->param[0]->data.b; - magicanalyse_building(b, mage, force); - break; - } - case SPP_SHIP: - { - ship * sh = pa->param[0]->data.sh; - magicanalyse_ship(sh, mage, force); - break; - } - default: - /* Syntax fehlerhaft */ - return 0; - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Gesang des Lebens analysieren - * Name: Magie analysieren - Unit - * Stufe: 5 - * Gebiet: Cerddor - * Wirkung: - * Zeigt die Verzauberungen eines Objekts an (curse->name, - * curse::info). Aus der Differenz Spruchstärke und Curse->vigour - * ergibt sich die Chance den Spruch zu identifizieren ((force - - * c->vigour)*10 + 100 %). - * - * Flag: - * (UNITSPELL|ONSHIPCAST|ONETARGET|TESTCANSEE) - */ -static int -sp_analysesong_unit(castorder *co) -{ - unit *u; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - u = pa->param[0]->data.u; - - magicanalyse_unit(u, mage, force); - - return cast_level; -} -/* ------------------------------------------------------------- */ -/* Name: Charming - * Stufe: 13 - * Gebiet: Cerddor - * Flag: UNITSPELL - * Wirkung: - * bezauberte Einheit wechselt 'virtuell' die Partei und führt fremde - * Befehle aus. - * Dauer: 3 - force+2 Wochen - * Wirkt gegen Magieresistenz - * - * wirkt auf eine Einheit mit maximal Talent Personen normal. Für jede - * zusätzliche Person gibt es einen Bonus auf Magieresistenz, also auf - * nichtgelingen, von 10%. - * - * Das höchste Talent der Einheit darf maximal so hoch sein wie das - * Magietalent des Magiers. Für jeden Talentpunkt mehr gibt es einen - * Bonus auf Magieresistenz von 15%, was dazu führt, das bei +2 Stufen - * die Magiersistenz bei 90% liegt. - * - * Migrantenzählung muss Einheit überspringen - * - * Attackiere verbieten - * Flags: - * (UNITSPELL | ONETARGET | TESTCANSEE) - */ -static int -sp_charmingsong(castorder *co) -{ - unit *target; - int duration; - skill_t i; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - int resist_bonus = 0; - int tb = 0; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; - - /* Auf eigene Einheiten versucht zu zaubern? Garantiert Tippfehler */ - if (target->faction == mage->faction){ - /* Die Einheit ist eine der unsrigen */ - cmistake(mage, co->order, 45, MSG_MAGIC); - } - - /* Magieresistensbonus für mehr als Stufe Personen */ - if (target->number > force) { - resist_bonus += (int)((target->number - force) * 10); - } - /* Magieresistensbonus für höhere Talentwerte */ - for(i = 0; i < MAXSKILLS; i++){ - int sk = effskill(target, i); - if (tb < sk) tb = sk; - } - tb -= effskill(mage, SK_MAGIC); - if(tb > 0){ - resist_bonus += tb * 15; - } - /* Magieresistenz */ - if (target_resists_magic(mage, target, TYP_UNIT, resist_bonus)) { - report_failure(mage, co->order); - sprintf(buf, "%s fühlt sich einen Moment lang benommen und desorientiert.", - unitname(target)); - addmessage(target->region, target->faction, buf, MSG_EVENT, ML_WARN); - return 0; - } - - duration = 3 + rand()%(int)force; - { - trigger * trestore = trigger_changefaction(target, target->faction); - /* läuft die Dauer ab, setze Partei zurück */ - add_trigger(&target->attribs, "timer", trigger_timeout(duration, trestore)); - /* wird die alte Partei von Target aufgelöst, dann auch diese Einheit */ - add_trigger(&target->faction->attribs, "destroy", trigger_killunit(target)); - /* wird die neue Partei von Target aufgelöst, dann auch diese Einheit */ - add_trigger(&mage->faction->attribs, "destroy", trigger_killunit(target)); - } - /* sperre ATTACKIERE, GIB PERSON und überspringe Migranten */ - create_curse(mage, &target->attribs, ct_find("slavery"), force, duration, zero_effect, 0); - - /* setze Partei um und lösche langen Befehl aus Sicherheitsgründen */ - u_setfaction(target,mage->faction); - set_order(&target->thisorder, NULL); - - /* setze Parteitarnung, damit nicht sofort klar ist, wer dahinter - * steckt */ - fset(target, UFL_PARTEITARNUNG); - - sprintf(buf, "%s gelingt es %s zu verzaubern. %s wird für etwa %d " - "Wochen unseren Befehlen gehorchen.", unitname(mage), - unitname(target), unitname(target), duration); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Gesang des wachen Geistes - * Stufe: 10 - * Gebiet: Cerddor - * Kosten: SPC_LEVEL - * Wirkung: - * Bringt einmaligen Bonus von +15% auf Magieresistenz. Wirkt auf alle - * Aliierten (HELFE BEWACHE) in der Region. - * Dauert Stufe Wochen an, ist nicht kumulativ. - * Flag: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_song_resistmagic(castorder *co) -{ - variant mr_bonus; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - - mr_bonus.i = 15; - create_curse(mage, &r->attribs, ct_find("goodmagicresistancezone"), - force, duration, mr_bonus, 0); - - /* Erfolg melden */ - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_effect", "unit region command", mage, - mage->region, co->order)); - - return cast_level; -} -/* ------------------------------------------------------------- */ -/* Name: Gesang des schwachen Geistes - * Stufe: 12 - * Gebiet: Cerddor - * Wirkung: - * Bringt einmaligen Malus von -15% auf Magieresistenz. - * Wirkt auf alle Nicht-Aliierten (HELFE BEWACHE) in der Region. - * Dauert Stufe Wochen an, ist nicht kumulativ. - * Flag: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_song_susceptmagic(castorder *co) -{ - variant mr_malus; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - - mr_malus.i = 15; - create_curse(mage, &r->attribs, ct_find("badmagicresistancezone"), - force, duration, mr_malus, 0); - - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_effect", "unit region command", mage, - mage->region, co->order)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Aufruhr beschwichtigen - * Stufe: 15 - * Gebiet: Cerddor - * Flag: FARCASTING - * Wirkung: - * zerstreut einen Monsterbauernmob, Antimagie zu 'Aufruhr - * verursachen' - */ - -static int -sp_rallypeasantmob(castorder *co) -{ - unit *u, *un; - int erfolg = 0; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - /* TODO - remove_allcurse(&r->attribs, C_RIOT, 0); - */ - - for (u = r->units; u; u = un){ - un = u->next; - if (u->faction->no == MONSTER_FACTION && u->race == new_race[RC_PEASANT]){ - rsetpeasants(r, rpeasants(r) + u->number); - rsetmoney(r, rmoney(r) + get_money(u)); - set_money(u, 0); - setguard(u, GUARD_NONE); - set_number(u, 0); - erfolg = cast_level; - } - } - - if (erfolg){ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for(u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s besänftigt den Bauernaufstand in %s.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", - regionname(r, u->faction)); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); - } - } - if (!fval(mage->faction, FL_DH)){ - sprintf(buf, "%s besänftigt den Bauernaufstand in %s.", - unitname(mage), regionname(r, u->faction)); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); - } - } else { - sprintf(buf, "Der Bauernaufstand in %s hatte sich bereits verlaufen.", - regionname(r, u->faction)); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); - } - return erfolg; -} - -/* ------------------------------------------------------------- */ -/* Name: Aufruhr verursachen - * Stufe: 16 - * Gebiet: Cerddor - * Wirkung: - * Wiegelt 60% bis 90% der Bauern einer Region auf. Bauern werden ein - * großer Mob, der zur Monsterpartei gehört und die Region bewacht. - * Regionssilber sollte auch nicht durch Unterhaltung gewonnen werden - * können. - * - * Fehlt: Triggeraktion: löste Bauernmob auf und gib alles an Region, - * dann können die Bauernmobs ihr Silber mitnehmen und bleiben x - * Wochen bestehen - * - * alternativ: Lösen sich langsam wieder auf - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_raisepeasantmob(castorder *co) -{ - unit *u; - attrib *a; - int n; - variant anteil; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - - anteil.i = 6 + (rand()%4); - - n = rpeasants(r) * anteil.i / 10; - n = max(0, n); - n = min(n, rpeasants(r)); - - if(n <= 0){ - report_failure(mage, co->order); - return 0; - } - - rsetpeasants(r, rpeasants(r) - n); - assert(rpeasants(r) >= 0); - - u = createunit(r, findfaction(MONSTER_FACTION), n, new_race[RC_PEASANT]); - fset(u, UFL_ISNEW); - set_string(&u->name, "Aufgebrachte Bauern"); - guard(u, GUARD_ALL); - a = a_new(&at_unitdissolve); - a->data.ca[0] = 1; /* An rpeasants(r). */ - a->data.ca[1] = 15; /* 15% */ - a_add(&u->attribs, a); - - create_curse(mage, &r->attribs, ct_find("riotzone"), cast_level, duration, anteil, 0); - - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - ADDMSG(&u->faction->msgs, msg_message( - "sp_raisepeasantmob_effect", "mage region", - cansee(u->faction, r, mage, 0) ? mage : NULL, r )); - } - } - if (!fval(mage->faction, FL_DH)){ - ADDMSG(&mage->faction->msgs, msg_message( - "sp_raisepeasantmob_effect", "mage region", mage, r)); - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Ritual der Aufnahme / Migrantenwerben - * Stufe: 9 - * Gebiet: Cerddor - * Wirkung: - * Bis zu Stufe Personen fremder Rasse können angeworben werden. Die - * angeworbene Einheit muss kontaktieren. Keine teuren Talente - * - * Flag: - * (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE) - */ -static int -sp_migranten(castorder *co) -{ - unit *target; - order * ord; - int kontaktiert = 0; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - const spell *sp = co->sp; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; /* Zieleinheit */ - - /* Personen unserer Rasse können problemlos normal übergeben werden */ - if (target->race == mage->faction->race){ - /* u ist von unserer Art, das Ritual wäre verschwendete Aura. */ - ADDMSG(&mage->faction->msgs, msg_message( - "sp_migranten_fail1", "unit region command target", mage, - mage->region, co->order, target)); - } - /* Auf eigene Einheiten versucht zu zaubern? Garantiert Tippfehler */ - if (target->faction == mage->faction){ - cmistake(mage, co->order, 45, MSG_MAGIC); - } - - /* Keine Monstereinheiten */ - if (!playerrace(target->race)){ - sprintf(buf, "%s kann nicht auf Monster gezaubert werden.", - spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); - return 0; - } - /* niemand mit teurem Talent */ - if (teure_talente(target)) { - sprintf(buf, "%s hat unaufkündbare Bindungen an seine alte Partei.", - unitname(target)); - addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); - return 0; - } - /* maximal Stufe Personen */ - if (target->number > cast_level - || target->number > max_spellpoints(r, mage)) - { - sprintf(buf, "%s in %s: 'ZAUBER %s': So viele Personen übersteigen " - "meine Kräfte.", unitname(mage), regionname(mage->region, mage->faction), - spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); - } - - /* Kontakt prüfen (aus alter Teleportroutine übernommen) */ - { - /* Nun kommt etwas reichlich krankes, um den - * KONTAKTIERE-Befehl des Ziels zu überprüfen. */ - - for (ord = target->orders; ord; ord = ord->next) { - if (get_keyword(ord) == K_CONTACT) { - const char *c; - /* So weit, so gut. S->s ist also ein KONTAKTIERE. Nun gilt es, - * herauszufinden, wer kontaktiert wird. Das ist nicht trivial. - * Zuerst muß der Parameter herausoperiert werden. */ - /* Leerzeichen finden */ - - init_tokens(ord); - skip_token(); - c = getstrtoken(); - - /* Wenn ein Leerzeichen da ist, ist *c != 0 und zeigt auf das - * Leerzeichen. */ - - if (c!=NULL) { - int kontakt = atoi36(c); - - if (kontakt == mage->no) { - kontaktiert = 1; - break; - } - } - } - } - } - - if (kontaktiert == 0) { - ADDMSG(&mage->faction->msgs, msg_message("spellfail::contact", - "mage region command target", mage, mage->region, co->order, - target)); - return 0; - } - u_setfaction(target,mage->faction); - set_order(&target->thisorder, NULL); - - /* Erfolg melden */ - ADDMSG(&mage->faction->msgs, msg_message("sp_migranten", - "unit region command target", mage, mage->region, co->order, target)); - - return target->number; -} - -/* ------------------------------------------------------------- */ -/* Name: Gesang der Friedfertigkeit - * Stufe: 12 - * Gebiet: Cerddor - * Wirkung: - * verhindert jede Attacke für lovar(Stufe/2) Runden - */ - -static int -sp_song_of_peace(castorder *co) -{ - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = 2 + lovar(force/2); - - create_curse(mage,&r->attribs, ct_find("peacezone"), force, duration, zero_effect, 0); - - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - if (cansee(u->faction, r, mage, 0)){ - sprintf(buf, "%s's Gesangskunst begeistert die Leute. Die " - "friedfertige Stimmung des Lieds überträgt sich auf alle " - "Zuhörer. Einige werfen ihre Waffen weg.", unitname(mage)); - }else{ - sprintf(buf, "In der Luft liegt ein wunderschönes Lied, dessen " - "friedfertiger Stimmung sich niemand entziehen kann. " - "Einige Leute werfen sogar ihre Waffen weg."); - } - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - return cast_level; - -} - -/* ------------------------------------------------------------- */ -/* Name: Hohes Lied der Gaukelei - * Stufe: 2 - * Gebiet: Cerddor - * Wirkung: - * Das Unterhaltungsmaximum steigt von 20% auf 40% des - * Regionsvermögens. Der Spruch hält Stufe Wochen an - */ - -static int -sp_generous(castorder *co) -{ - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - variant effect; - - if(is_cursed(r->attribs, C_DEPRESSION, 0)){ - sprintf(buf, "%s in %s: Die Stimmung in %s ist so schlecht, das " - "niemand auf den Zauber reagiert.", unitname(mage), - regionname(mage->region, mage->faction), regionname(r, mage->faction)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - effect.i = 2; - create_curse(mage,&r->attribs, ct_find("generous"), force, duration, effect, 0); - - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - if (cansee(u->faction, r, mage, 0)){ - sprintf(buf, "%s's Gesangskunst begeistert die Leute. Die " - "fröhliche und ausgelassene Stimmung der Lieder überträgt " - "sich auf alle Zuhörer.", unitname(mage)); - }else{ - sprintf(buf, "Die Darbietungen eines fahrenden Gauklers begeistern " - "die Leute. Die fröhliche und ausgelassene Stimmung seiner " - "Lieder überträgt sich auf alle Zuhörer."); - } - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Anwerbung - * Stufe: 4 - * Gebiet: Cerddor - * Wirkung: - * Bauern schliessen sich der eigenen Partei an - * ist zusätzlich zur Rekrutierungsmenge in der Region - * */ - -static int -sp_recruit(castorder *co) -{ - unit *u; - region *r = co->rt; - int n, maxp = rpeasants(r); - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - faction *f = mage->faction; - - if (maxp == 0) { - report_failure(mage, co->order); - return 0; - } - /* Immer noch zuviel auf niedrigen Stufen. Deshalb die Rekrutierungskosten - * mit einfliessen lassen und dafür den Exponenten etwas größer. - * Wenn die Rekrutierungskosten deutlich höher sind als der Faktor, - * ist das Verhältniss von ausgegebene Aura pro Bauer bei Stufe 2 - * ein mehrfaches von Stufe 1, denn in beiden Fällen gibt es nur 1 - * Bauer, nur die Kosten steigen. */ - n = (int)((pow(force, 1.6) * 100)/f->race->recruitcost); - if (f->race==new_race[RC_URUK]) { - n = min(2*maxp, n); - n = max(n, 1); - rsetpeasants(r, maxp - (n+1) / 2); - } else { - n = min(maxp, n); - n = max(n, 1); - rsetpeasants(r, maxp - n); - } - - u = create_unit(r, f, n, f->race, 0, (n == 1 ? "Bauer" : "Bauern"), mage); - set_order(&u->thisorder, default_order(f->locale)); - - sprintf(buf, "%s konnte %d %s anwerben", unitname(mage), n, - n == 1 ? "Bauer" : "Bauern"); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - if (f->race==new_race[RC_URUK]) n = (n+1) / 2; - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Wanderprediger - Große Anwerbung - * Stufe: 14 - * Gebiet: Cerddor - * Wirkung: - * Bauern schliessen sich der eigenen Partei an - * ist zusätzlich zur Rekrutierungsmenge in der Region - * */ - -static int -sp_bigrecruit(castorder *co) -{ - unit *u; - region *r = co->rt; - int n, maxp = rpeasants(r); - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - faction *f = mage->faction; - - if (maxp <= 0) { - report_failure(mage, co->order); - return 0; - } - /* Für vergleichbare Erfolge bei unterschiedlichen Rassen die - * Rekrutierungskosten mit einfliessen lassen. */ - - n = (int)force + lovar((force * force * 1000)/f->race->recruitcost); - if (f->race==new_race[RC_URUK]) { - n = min(2*maxp, n); - n = max(n, 1); - rsetpeasants(r, maxp - (n+1) / 2); - } else { - n = min(maxp, n); - n = max(n, 1); - rsetpeasants(r, maxp - n); - } - - u = create_unit(r, f, n, f->race, 0, (n == 1 ? "Bauer" : "Bauern"), mage); - set_order(&u->thisorder, default_order(f->locale)); - - sprintf(buf, "%s konnte %d %s anwerben", unitname(mage), n, - n == 1 ? "Bauer" : "Bauern"); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Aushorchen - * Stufe: 7 - * Gebiet: Cerddor - * Wirkung: - * Erliegt die Einheit dem Zauber, so wird sie dem Magier alles - * erzählen, was sie über die gefragte Region weiß. Ist in der Region - * niemand ihrer Partei, so weiß sie nichts zu berichten. Auch kann - * sie nur das erzählen, was sie selber sehen könnte. - * Flags: - * (UNITSPELL | ONETARGET | TESTCANSEE) - */ - -/* restistenz der einheit prüfen */ -static int -sp_pump(castorder *co) -{ - unit *u, *target; - region *rt; - boolean see = false; - region *r = co->rt; - unit *mage = (unit *)co->magician; - spellparameter *pa = co->par; - int cast_level = co->level; - const spell *sp = co->sp; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; /* Zieleinheit */ - - if (fval(target->race, RCF_UNDEAD)) { - sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", - spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); - return 0; - } - if (is_magic_resistant(mage, target, 0) || target->faction->no == MONSTER_FACTION) { - report_failure(mage, co->order); - return 0; - } - - rt = pa->param[1]->data.r; - - for (u = rt->units; u; u = u->next){ - if(u->faction == target->faction) - see = true; - } - - if (see == false){ - sprintf(buf, "%s horcht %s über %s aus, aber %s wusste nichts zu " - "berichten.", unitname(mage), unitname(target), regionname(rt, mage->faction), - unitname(target)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level/2; - } else { - sprintf(buf, "%s horcht %s über %s aus.", unitname(mage), - unitname(target), regionname(rt, mage->faction)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - - u = createunit(rt, mage->faction, RS_FARVISION, new_race[RC_SPELL]); - set_string(&u->name, "Zauber: Aushorchen"); - u->age = 2; - set_level(u, SK_OBSERVATION, eff_skill(target, SK_OBSERVATION, u->region)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Verführung - * Stufe: 6 - * Gebiet: Cerddor - * Wirkung: - * Betört eine Einheit, so das sie ihm den größten Teil ihres Bargelds - * und 50% ihres Besitzes schenkt. Sie behält jedoch immer soviel, wie - * sie zum überleben braucht. Wirkt gegen Magieresistenz. - * min(Stufe*1000$, u->money - maintenace) - * Von jedem Item wird 50% abgerundet ermittelt und übergeben. Dazu - * kommt Itemzahl%2 mit 50% chance - * - * Flags: - * (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE) - */ -static int -sp_seduce(castorder *co) -{ - unit *target; - int loot; - item **itmp; - region *r = co->rt; - unit *mage = (unit *)co->magician; - spellparameter *pa = co->par; - int cast_level = co->level; - const spell *sp = co->sp; - double force = co->force; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; /* Zieleinheit */ - - if (fval(target->race, RCF_UNDEAD)) { - sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", - spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); - return 0; - } - - /* Erfolgsmeldung */ - sprintf(buf, "%s schenkt %s ", unitname(target), unitname(mage)); - - loot = min(cast_level * 1000, get_money(target) - (MAINTENANCE*target->number)); - loot = max(loot, 0); - change_money(mage, loot); - change_money(target, -loot); - - if(loot > 0){ - icat(loot); - } else { - scat("kein"); - } - scat(" Silber"); - itmp=&target->items; - while (*itmp) { - item * itm = *itmp; - loot = itm->number/2; - if (itm->number % 2) { - loot += rand() % 2; - } - if (loot > 0) { - loot = (int)min(loot, force * 5); - scat(", "); - icat(loot); - scat(" "); - scat(locale_string(mage->faction->locale, resourcename(itm->type->rtype, (loot==1)?0:GR_PLURAL))); - i_change(&mage->items, itm->type, loot); - i_change(&target->items, itm->type, -loot); - } - if (*itmp==itm) itmp=&itm->next; - } - scat("."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - sprintf(buf, "%s verfiel dem Glücksspiel und hat fast sein ganzes Hab " - "und Gut verspielt.", unitname(target)); - addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Monster friedlich stimmen - * Stufe: 6 - * Gebiet: Cerddor - * Wirkung: - * verhindert Angriffe des bezauberten Monsters auf die Partei des - * Barden für Stufe Wochen. Nicht übertragbar, dh Verbündete werden vom - * Monster natürlich noch angegriffen. Wirkt nicht gegen Untote - * Jede Einheit kann maximal unter einem Beherrschungszauber dieser Art - * stehen, dh wird auf die selbe Einheit dieser Zauber von einem - * anderen Magier nochmal gezaubert, schlägt der Zauber fehl. - * - * Flags: - * (UNITSPELL | ONSHIPCAST | ONETARGET | TESTRESISTANCE | TESTCANSEE) - */ - -static int -sp_calm_monster(castorder *co) -{ - curse * c; - unit *target; - region *r = co->rt; - unit *mage = (unit *)co->magician; - spellparameter *pa = co->par; - int cast_level = co->level; - double force = co->force; - const spell *sp = co->sp; - variant effect; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; /* Zieleinheit */ - - if (fval(target->race, RCF_UNDEAD)) { - sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", - spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); - return 0; - } - - effect.i = mage->faction->subscription; - c = create_curse(mage, &target->attribs, ct_find("calmmonster"), force, - (int)force, effect, 0); - if (c==NULL) { - report_failure(mage, co->order); - return 0; - } - /* Nur ein Beherrschungszauber pro Unit */ - curse_setflag(c, CURSE_ONLYONE); - - sprintf(buf, "%s besänftigt %s.", unitname(mage), unitname(target)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: schaler Wein - * Stufe: 7 - * Gebiet: Cerddor - * Wirkung: - * wird gegen Magieresistenz gezaubert Das Opfer vergisst bis zu - * Talenttage seines höchsten Talentes und tut die Woche nix. - * Nachfolgende Zauber sind erschwert. - * Wirkt auf bis zu 10 Personen in der Einheit - * - * Flags: - * (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE) - */ - -static int -sp_headache(castorder *co) -{ - skill * smax = NULL; - int i; - unit *target; - region *r = co->rt; - unit *mage = (unit *)co->magician; - spellparameter *pa = co->par; - int cast_level = co->level; - - /* Macht alle nachfolgenden Zauber doppelt so teuer */ - countspells(mage, 1); - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; /* Zieleinheit */ - - /* finde das größte Talent: */ - for (i=0;i!=target->skill_size;++i) { - skill * sv = target->skills+i; - if (smax==NULL || skill_compare(sv, smax)>0) { - smax = sv; - } - } - if (smax!=NULL) { - /* wirkt auf maximal 10 Personen */ - int change = min(10, target->number) * (rand()%2+1) / target->number; - reduce_skill(target, smax, change); - } - set_order(&target->thisorder, NULL); - - sprintf(buf, "%s verschafft %s einige feuchtfröhliche Stunden mit heftigen " - "Nachwirkungen.", unitname(mage), unitname(target)); - addmessage(mage->region, mage->faction, buf, MSG_MAGIC, ML_INFO); - - sprintf(buf, "%s hat höllische Kopfschmerzen und kann sich an die " - "vergangene Woche nicht mehr erinnern. Nur noch daran, wie alles mit " - "einer fröhlichen Feier in irgendeiner Taverne anfing...", unitname(target)); - addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); - - return cast_level; -} - - -/* ------------------------------------------------------------- */ -/* Name: Mob - * Stufe: 10 - * Gebiet: Cerddor - * Wirkung: - * Wiegelt Stufe*250 Bauern zu einem Mob auf, der sich der Partei des - * Magier anschliesst Pro Woche beruhigen sich etwa 15% wieder und - * kehren auf ihre Felder zurück - * - * Flags: - * (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_raisepeasants(castorder *co) -{ - int bauern; - unit *u, *u2; - attrib *a; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - - if(rpeasants(r) == 0) { - addmessage(r, mage->faction, "Hier gibt es keine Bauern.", - MSG_MAGIC, ML_MISTAKE); - return 0; - } - bauern = (int)min(rpeasants(r), power*250); - rsetpeasants(r, rpeasants(r) - bauern); - - u2 = create_unit(r, mage->faction, bauern, new_race[RC_PEASANT], 0, "Wilder Bauernmob", mage); - set_string(&u2->name, "Erzürnte Bauern"); - - fset(u2, UFL_LOCKED); - fset(u2, UFL_PARTEITARNUNG); - - a = a_new(&at_unitdissolve); - a->data.ca[0] = 1; /* An rpeasants(r). */ - a->data.ca[1] = 15; /* 15% */ - a_add(&u2->attribs, a); - - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s wiegelt %d Bauern auf.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", - u2->number); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); - } - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Trübsal - * Stufe: 11 - * Kategorie: Region, negativ - * Wirkung: - * in der Region kann für einige Wochen durch Unterhaltung kein Geld - * mehr verdient werden - * - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -static int -sp_depression(castorder *co) -{ - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - - create_curse(mage,&r->attribs, ct_find("depression"), force, duration, zero_effect, 0); - - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next ) { - if (!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s sorgt für Trübsal unter den Bauern.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); - } - } - return cast_level; -} - -#if 0 -/* ------------------------------------------------------------- */ -/* Name: Hoher Gesang der Drachen - * Stufe: 14 - * Gebiet: Cerddor - * Kategorie: Monster, Beschwörung, positiv - * - * Wirkung: - * Erhöht HP-Regeneration in der Region und lockt drachenartige (Wyrm, - * Drache, Jungdrache, Seeschlange, ...) aus der Umgebung an - * - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -/* TODO zur Aktivierung in Zauberliste aufnehmen*/ -static int -sp_dragonsong(castorder *co) -{ - region *r = co->rt; /* Zauberregion */ - unit *mage = (unit *)co->magician; - unit *u; - int cast_level = co->level; - double power = co->force; - region_list *rl,*rl2; - faction *f; - - /* TODO HP-Effekt */ - - f = findfaction(MONSTER_FACTION); - - rl = all_in_range(r, (int)power); - - for(rl2 = rl; rl2; rl2 = rl2->next) { - for(u = rl2->data->units; u; u = u->next) { - if (u->race->flags & RCF_DRAGON) { - attrib * a = a_find(u->attribs, &at_targetregion); - if (!a) { - a = a_add(&u->attribs, make_targetregion(r)); - } else { - a->data.v = r; - } - sprintf(buf, "Kommt aus: %s, Will nach: %s", regionname(rl2->data, u->faction), regionname(r, u->faction)); - usetprivate(u, buf); - } - } - } - - ADDMSG(&mage->faction->msgs, msg_message( - "summondragon", "unit region command region", - mage, mage->region, co->order, co->rt)); - - free_regionlist(rl); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Hoher Gesang der Verlockung - * Stufe: 17 - * Gebiet: Cerddor - * Kategorie: Monster, Beschwörung, positiv - * - * Wirkung: - * Lockt Bauern aus den umliegenden Regionen her - * - * Flag: - * (FARCASTING | REGIONSPELL | TESTRESISTANCE) - */ -/* TODO zur Aktivierung in Zauberliste aufnehmen*/ - -static int -sp_songofAttraction(castorder *co) -{ - region *r = co->rt; /* Zauberregion */ - unit *mage = (unit *)co->magician; - int cast_level = co->level; - /* double power = co->force; */ - - /* TODO Wander Effekt */ - - ADDMSG(&mage->faction->msgs, msg_message( - "summon", "unit region command region", - mage, mage->region, co->order, r)); - - return cast_level; -} - -#endif - -/* ------------------------------------------------------------- */ -/* TRAUM - Illaun */ -/* ------------------------------------------------------------- */ - -/* Name: Seelenfrieden - * Stufe: 2 - * Kategorie: Region, positiv - * Gebiet: Illaun - * Wirkung: - * Reduziert Untotencounter - * Flag: (0) - */ - -int -sp_puttorest(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int dead = deathcount(r); - int laid_to_rest = dice((int)(co->force * 2), 100); - message * seen = msg_message("puttorest", "mage", mage); - message * unseen = msg_message("puttorest", "mage", NULL); - - laid_to_rest = max(laid_to_rest, dead); - - deathcounts(r, -laid_to_rest); - - report_effect(r, mage, seen, unseen); - msg_release(seen); - msg_release(unseen); - return co->level; -} - -/* Name: Traumschlößchen - * Stufe: 3 - * Kategorie: Region, Gebäude, positiv - * Gebiet: Illaun - * Wirkung: - * Mit Hilfe dieses Zaubers kann der Traumweber die Illusion eines - * beliebigen Gebäudes erzeugen. Die Illusion kann betreten werden, ist - * aber ansonsten funktionslos und benötigt auch keinen Unterhalt - * Flag: (0) - */ - -int -sp_icastle(castorder *co) -{ - building *b; - const building_type * type; - attrib *a; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - icastle_data * data; - - if((type=findbuildingtype(pa->param[0]->data.s, mage->faction->locale)) == NULL) { - type = bt_find("castle"); - } - - b = new_building(bt_find("illusion"), r, mage->faction->locale); - - /* Größe festlegen. */ - if (type == bt_find("illusion")) { - b->size = (rand()%(int)((power*power)+1)*10); - } else if (type->maxsize == -1) { - b->size = ((rand()%(int)(power))+1)*5; - } else { - b->size = type->maxsize; - } - sprintf(buf, "%s %s", LOC(mage->faction->locale, buildingtype(type, b, 0)), buildingid(b)); - set_string(&b->name, buf); - - /* TODO: Auf timeout und action_destroy umstellen */ - a = a_add(&b->attribs, a_new(&at_icastle)); - data = (icastle_data*)a->data.v; - data->type = type; - data->building = b; - data->time = 2+(rand()%(int)(power)+1)*(rand()%(int)(power)+1); - - if(mage->region == r) { - leave(r, mage); - mage->building = b; - } - - ADDMSG(&mage->faction->msgs, msg_message( - "icastle_create", "unit region command", mage, mage->region, - co->order)); - - addmessage(r, 0, - "Verwundert blicken die Bauern auf ein plötzlich erschienenes Gebäude.", - MSG_EVENT, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Gestaltwandlung - * Stufe: 3 - * Gebiet: Illaun - * Wirkung: - * Zieleinheit erscheint für (Stufe) Wochen als eine andere Gestalt - * (wie bei dämonischer Rassetarnung). - * Syntax: ZAUBERE "Gestaltwandlung" - * Flags: - * (UNITSPELL | SPELLLEVEL | ONETARGET) - */ - -int -sp_illusionary_shapeshift(castorder *co) -{ - unit *u; - const race * rc; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - u = pa->param[0]->data.u; - - rc = findrace(pa->param[1]->data.s, mage->faction->locale); - if (rc == NULL) { - cmistake(mage, co->order, 202, MSG_MAGIC); - return 0; - } - - /* ähnlich wie in laws.c:setealth() */ - if (!playerrace(rc)) { - sprintf(buf, "%s %s keine %s-Gestalt annehmen.", - unitname(u), - u->number > 1 ? "können" : "kann", - LOC(u->faction->locale, rc_name(rc, 2))); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - { - trigger * trestore = trigger_changerace(u, NULL, u->irace); - add_trigger(&u->attribs, "timer", trigger_timeout((int)power+2, trestore)); - } - u->irace = rc; - - sprintf(buf, "%s läßt %s als %s erscheinen.", - unitname(mage), unitname(u), LOC(u->faction->locale, rc_name(rc, u->number != 1))); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Traumdeuten - * Stufe: 7 - * Kategorie: Einheit - * - * Wirkung: - * Wirkt gegen Magieresistenz. Spioniert die Einheit aus. Gibt alle - * Gegenstände, Talente mit Stufe, Zauber und Kampfstatus an. - * - * Magieresistenz hier prüfen, wegen Fehlermeldung - * - * Flag: - * (UNITSPELL | ONETARGET) - */ -int -sp_readmind(castorder *co) -{ - unit *target; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - target = pa->param[0]->data.u; - - if (target->faction == mage->faction){ - /* Die Einheit ist eine der unsrigen */ - cmistake(mage, co->order, 45, MSG_MAGIC); - } - - /* Magieresistenz Unit */ - if (target_resists_magic(mage, target, TYP_UNIT, 0)){ - report_failure(mage, co->order); - /* "Fühlt sich beobachtet"*/ - ADDMSG(&target->faction->msgs, msg_message( - "stealdetect", "unit", target)); - return 0; - } - spy_message(2, mage, target); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Regionstraum analysieren - * Stufe: 9 - * Aura: 18 - * Kosten: SPC_FIX - * Wirkung: - * Zeigt die Verzauberungen eines Objekts an (curse->name, - * curse::info). Aus der Differenz Spruchstärke und Curse->vigour - * ergibt sich die Chance den Spruch zu identifizieren ((force - - * c->vigour)*10 + 100 %). - */ -int -sp_analyseregionsdream(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - magicanalyse_region(r, mage, cast_level); - - return cast_level; -} - - -/* ------------------------------------------------------------- */ -/* Name: Traumbilder erkennen - * Stufe: 5 - * Aura: 12 - * Kosten: SPC_FIX - * Wirkung: - * Zeigt die Verzauberungen eines Objekts an (curse->name, - * curse::info). Aus der Differenz Spruchstärke und Curse->vigour - * ergibt sich die Chance den Spruch zu identifizieren ((force - - * c->vigour)*10 + 100 %). - */ -int -sp_analysedream(castorder *co) -{ - unit *u; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - u = pa->param[0]->data.u; - magicanalyse_unit(u, mage, cast_level); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Schlechte Träume - * Stufe: 10 - * Kategorie: Region, negativ - * Wirkung: - * Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller - * nichtaliierten Einheiten (HELFE BEWACHE) in der Region so starkzu - * stören, das sie 1 Talentstufe in allen Talenten - * vorübergehend verlieren. Der Zauber wirkt erst im Folgemonat. - * - * Flags: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - * */ -int -sp_baddreams(castorder *co) -{ - int duration; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - region *r = co->rt; - curse * c; - variant effect; - - /* wirkt erst in der Folgerunde, soll mindestens eine Runde wirken, - * also duration+2 */ - duration = (int)max(1, power/2); /* Stufe 1 macht sonst mist */ - duration = 2 + rand()%duration; - - /* Nichts machen als ein entsprechendes Attribut in die Region legen. */ - effect.i = -1; - c = create_curse(mage, &r->attribs, ct_find("gbdream"), power, duration, effect, 0); - curse_setflag(c, CURSE_ISNEW); - - /* Erfolg melden*/ - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_effect", "unit region command", mage, - mage->region, co->order)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Schöne Träume - * Stufe: 8 - * Kategorie: - * Wirkung: - * Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller aliierten - * Einheiten in der Region so zu beeinflussen, daß sie für einige Zeit - * einen Bonus von 1 Talentstufe in allen Talenten - * bekommen. Der Zauber wirkt erst im Folgemonat. - * Flags: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - */ -int -sp_gooddreams(castorder *co) -{ - int duration; - curse * c; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - variant effect; - - /* wirkt erst in der Folgerunde, soll mindestens eine Runde wirken, - * also duration+2 */ - duration = (int)max(1, power/2); /* Stufe 1 macht sonst mist */ - duration = 2 + rand()%duration; - effect.i = 1; - c = create_curse(mage, &r->attribs, ct_find("gbdream"), power, duration, effect, 0); - curse_setflag(c, CURSE_ISNEW); - - /* Erfolg melden*/ - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_effect", "unit region command", mage, - mage->region, co->order)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: - * Stufe: 9 - * Kategorie: - * Wirkung: - * Es wird eine Kloneinheit erzeugt, die nichts kann. Stirbt der - * Magier, wird er mit einer Wahrscheinlichkeit von 90% in den Klon - * transferiert. - * Flags: - * (NOTFAMILARCAST) - */ -int -sp_clonecopy(castorder *co) -{ - unit *clone; - region *r = co->rt; - region *target_region = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - if (get_clone(mage) != NULL ) { - cmistake(mage, co->order, 298, MSG_MAGIC); - return 0; - } - - sprintf(buf, "Klon von %s", unitname(mage)); - clone = create_unit(target_region, mage->faction, 1, new_race[RC_CLONE], 0, buf, mage); - clone->status = ST_FLEE; - fset(clone, UFL_LOCKED); - - create_newclone(mage, clone); - - sprintf(buf, "%s erschafft einen Klon.", unitname(mage)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -int -sp_dreamreading(castorder *co) -{ - unit *u,*u2; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - double power = co->force; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - u = pa->param[0]->data.u; - - /* Illusionen und Untote abfangen. */ - if (fval(u->race, RCF_UNDEAD|RCF_ILLUSIONARY)) { - ADDMSG(&mage->faction->msgs, msg_message( - "spellunitnotfound", "unit region command id", - mage, mage->region, co->order, strdup(itoa36(u->no)))); - return 0; - } - - /* Entfernung */ - if(distance(mage->region, u->region) > power) { - addmessage(r, mage->faction, "Die Einheit ist zu weit " - "entfernt.", MSG_MAGIC, ML_MISTAKE); - return 0; - } - - u2 = createunit(u->region,mage->faction, RS_FARVISION, new_race[RC_SPELL]); - set_number(u2, 1); - set_string(&u2->name, "sp_dreamreading"); - u2->age = 2; /* Nur für diese Runde. */ - set_level(u2, SK_OBSERVATION, eff_skill(u, SK_OBSERVATION, u2->region)); - - sprintf(buf, "%s verliert sich in die Träume von %s und erhält einen " - "Eindruck von %s.", unitname(mage), unitname(u), regionname(u->region, mage->faction)); - addmessage(r, mage->faction, buf, MSG_EVENT, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Wirkt power/2 Runden auf bis zu power^2 Personen - * mit einer Chance von 5% vermehren sie sich */ -int -sp_sweetdreams(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - int men, n; - int duration = (int)(power/2)+1; - int opfer = (int)(power*power); - - /* Schleife über alle angegebenen Einheiten */ - for (n = 0; n < pa->length; n++) { - curse * c; - unit *u; - variant effect; - /* sollte nie negativ werden */ - if (opfer < 1) break; - - if (pa->param[n]->flag == TARGET_RESISTS || - pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - /* Zieleinheit */ - u = pa->param[n]->data.u; - - if (!ucontact(u, mage)) { - cmistake(mage, co->order, 40, MSG_EVENT); - continue; - } - men = min(opfer, u->number); - opfer -= men; - - /* Nichts machen als ein entsprechendes Attribut an die Einheit legen. */ - effect.i = 5; - c = create_curse(mage,&u->attribs, ct_find("orcish"), power, duration, effect, men); - curse_setflag(c, CURSE_ISNEW); - - sprintf(buf, "%s verschafft %s ein interessanteres Nachtleben.", - unitname(mage), unitname(u)); - addmessage(r, mage->faction, buf, MSG_EVENT, ML_INFO); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -int -sp_disturbingdreams(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = 1 + (int)(power/6); - variant effect; - curse * c; - - effect.i = 10; - c = create_curse(mage, &r->attribs, ct_find("badlearn"), power, duration, effect, 0); - curse_setflag(c, CURSE_ISNEW); - - sprintf(buf, "%s sorgt für schlechten Schlaf in %s.", - unitname(mage), regionname(r, mage->faction)); - addmessage(0, mage->faction, buf, MSG_EVENT, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -int -sp_dream_of_confusion(castorder *co) -{ - unit *u; - region_list *rl,*rl2; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - double range = (power-14)/2-1; - int duration = (int)(power-14)+1; - - rl = all_in_range(r, (short)range, NULL); - - for(rl2 = rl; rl2; rl2 = rl2->next) { - region * r2 = rl2->data; - variant effect; - curse * c; - /* Magieresistenz jeder Region prüfen */ - if (target_resists_magic(mage, r2, TYP_REGION, 0)){ - report_failure(mage, co->order); - continue; - } - - effect.i = cast_level*5; - c = create_curse(mage, &r2->attribs, - ct_find("disorientationzone"), power, duration, effect, 0); - /* soll der Zauber schon in der Zauberrunde wirken? */ - curse_setflag(c, CURSE_ISNEW); - - for (u = r2->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r2->units; u; u = u->next ) { - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); - addmessage(r2, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - if(!fval(mage->faction, FL_DH)){ - sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", - unitname(mage)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - } - free_regionlist(rl); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* ASTRAL / THEORIE / M_ASTRAL */ -/* ------------------------------------------------------------- */ -/* Name: Magie analysieren - * Stufe: 1 - * Aura: 1 - * Kosten: SPC_LINEAR - * Komponenten: - * - * Wirkung: - * Zeigt die Verzauberungen eines Objekts an (curse->name, - * curse::info). Aus der Differenz Spruchstärke und Curse->vigour - * ergibt sich die Chance den Spruch zu identifizieren ((force - - * c->vigour)*10 + 100 %). - * - * Flags: - * UNITSPELL, SHIPSPELL, BUILDINGSPELL - */ - -int -sp_analysemagic(castorder *co) -{ - int obj; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - - /* Objekt ermitteln */ - obj = pa->param[0]->typ; - - switch(obj) { - case SPP_REGION: - { - region *tr = pa->param[0]->data.r; - magicanalyse_region(tr, mage, cast_level); - break; - } - case SPP_TEMP: - case SPP_UNIT: - { - unit *u; - u = pa->param[0]->data.u; - magicanalyse_unit(u, mage, cast_level); - break; - } - case SPP_BUILDING: - { - building *b; - b = pa->param[0]->data.b; - magicanalyse_building(b, mage, cast_level); - break; - } - case SPP_SHIP: - { - ship *sh; - sh = pa->param[0]->data.sh; - magicanalyse_ship(sh, mage, cast_level); - break; - } - default: - /* Fehlerhafter Parameter */ - return 0; - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ - -int -sp_itemcloak(castorder *co) -{ - unit *target; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = (int)power+1; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* Zieleinheit */ - target = pa->param[0]->data.u; - - create_curse(mage,&target->attribs, ct_find("itemcloak"), power, duration, zero_effect, 0); - ADDMSG(&mage->faction->msgs, msg_message( - "itemcloak", "mage target", mage, target)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Magieresistenz erhöhen - * Stufe: 3 - * Aura: 5 MP - * Kosten: SPC_LEVEL - * Komponenten: - * - * Wirkung: - * erhöht die Magierestistenz der Personen um 20 Punkte für 6 Wochen - * Wirkt auf Stufe*5 Personen kann auf mehrere Einheiten gezaubert - * werden, bis die Zahl der möglichen Personen erschöpft ist - * - * Flags: - * UNITSPELL - */ -int -sp_resist_magic_bonus(castorder *co) -{ - unit *u; - int n, m, opfer; - variant resistbonus; - int duration = 6; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - /* Pro Stufe können bis zu 5 Personen verzaubert werden */ - opfer = (int)(power * 5); - - /* Schleife über alle angegebenen Einheiten */ - for (n = 0; n < pa->length; n++) { - /* sollte nie negativ werden */ - if (opfer < 1) - break; - - if(pa->param[n]->flag == TARGET_RESISTS - || pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - u = pa->param[n]->data.u; - - /* Ist die Einheit schon verzaubert, wirkt sich dies nur auf die - * Menge der Verzauberten Personen aus. - if(is_cursed(u->attribs, C_MAGICRESISTANCE, 0)) - continue; - */ - - m = min(u->number,opfer); - opfer -= m; - - resistbonus.i = 20; - create_curse(mage, &u->attribs, ct_find("magicresistance"), - power, duration, resistbonus, m); - - sprintf(buf, "%s wird kurz von einem magischen Licht umhüllt.", - unitname(u)); - addmessage(0, u->faction, buf, MSG_EVENT, ML_IMPORTANT); - - /* und noch einmal dem Magier melden */ - if (u->faction != mage->faction) - addmessage(mage->region, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - /* pro 5 nicht verzauberte Personen kann der Level und damit die - * Kosten des Zaubers um 1 reduziert werden. (die Formel geht von - * immer abrunden da int aus) */ - cast_level -= opfer/5; - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* "ZAUBERE [STUFE n] \"Astraler Weg\" [ ...]", - * - * Parameter: - * pa->param[0]->data.s -*/ -int -sp_enterastral(castorder *co) -{ - region *rt, *ro; - unit *u, *u2; - int remaining_cap; - int n, w; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - const spell *sp = co->sp; - - switch(getplaneid(r)) { - case 0: - rt = r_standard_to_astral(r); - ro = r; - break; - default: - sprintf(buf, "%s in %s: 'ZAUBER %s': Dieser Zauber funktioniert " - "nur in der materiellen Welt.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - if(!rt) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann hier kein Kontakt zur " - "Astralwelt hergestellt werden.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - if(is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || - is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " - "dieser astralen Region hergestellt werden.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - remaining_cap = (int)((power-3) * 1500); - - /* für jede Einheit in der Kommandozeile */ - for (n = 0; n < pa->length; n++) { - if(pa->param[n]->flag == TARGET_NOTFOUND) continue; - u = pa->param[n]->data.u; - - if (!ucontact(u, mage)) { - if (power > 10 && !is_magic_resistant(mage, u, 0) - && can_survive(u, rt)) { - sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " - "Zauber jedoch nicht.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", - unitname(u), - cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_WARN); - } else { - sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " - "Zauber.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", - cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", - unitname(u)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - continue; - } - } - - w = weight(u); - if(!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - } else if(remaining_cap - w < 0) { - addmessage(r, mage->faction, "Die Einheit ist zu schwer.", - MSG_MAGIC, ML_MISTAKE); - } else { - remaining_cap = remaining_cap - w; - move_unit(u, rt, NULL); - - /* Meldungen in der Ausgangsregion */ - - for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = r->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, r, u, 0)) { - sprintf(buf, "%s wird durchscheinend und verschwindet.", - unitname(u)); - addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - - /* Meldungen in der Zielregion */ - - for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for (u2 = rt->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, rt, u, 0)) { - sprintf(buf, "%s erscheint plötzlich.", unitname(u)); - addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - } - } - return cast_level; -} - -int -sp_pullastral(castorder *co) -{ - region *rt, *ro; - unit *u, *u2; - region_list *rl, *rl2; - int remaining_cap; - int n, w; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - const spell *sp = co->sp; - - switch (getplaneid(r)) { - case 1: - rt = r; - ro = pa->param[0]->data.r; - rl = astralregions(r, NULL); - rl2 = rl; - while (rl2!=NULL) { - region * r2 = rl2->data; - if (r2->x == ro->x && r2->y == ro->y) { - ro = r2; - break; - } - rl2 = rl2->next; - } - if(!rl2) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " - "dieser Region hergestellt werden.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - free_regionlist(rl); - return 0; - } - free_regionlist(rl); - break; - default: - sprintf(buf, "%s in %s: 'ZAUBER %s': Dieser Zauber funktioniert " - "nur in der astralen Welt.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - if(is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || - is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " - "dieser Region hergestellt werden.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - remaining_cap = (int)((power-3) * 1500); - - /* für jede Einheit in der Kommandozeile */ - for (n = 1; n < pa->length; n++) { - if(pa->param[n]->flag == TARGET_NOTFOUND) continue; - - u = pa->param[n]->data.u; - - if (!ucontact(u, mage)) { - if(power > 12 && pa->param[n]->flag != TARGET_RESISTS && can_survive(u, rt)) { - sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " - "Zauber jedoch nicht.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", - unitname(u), - cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); - addmessage(r, u->faction, buf, MSG_MAGIC, ML_WARN); - } else { - sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " - "Zauber.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", - cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", - unitname(u)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - continue; - } - } - - w = weight(u); - - if(!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - } else if(remaining_cap - w < 0) { - addmessage(r, mage->faction, "Die Einheit ist zu schwer.", - MSG_MAGIC, ML_MISTAKE); - } else { - remaining_cap = remaining_cap - w; - move_unit(u, rt, NULL); - - /* Meldungen in der Ausgangsregion */ - - for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = r->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, r, u, 0)) { - sprintf(buf, "%s wird durchscheinend und verschwindet.", - unitname(u)); - addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - - /* Meldungen in der Zielregion */ - - for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = rt->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, rt, u, 0)) { - sprintf(buf, "%s erscheint plötzlich.", unitname(u)); - addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - } - } - return cast_level; -} - -int -sp_leaveastral(castorder *co) -{ - region *rt, *ro; - region_list *rl, *rl2; - unit *u, *u2; - int remaining_cap; - int n, w; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - switch(getplaneid(r)) { - case 1: - ro = r; - rt = pa->param[0]->data.r; - if(!rt) { - addmessage(r, mage->faction, "Dorthin führt kein Weg.", - MSG_MAGIC, ML_MISTAKE); - return 0; - } - rl = astralregions(r, inhabitable); - rl2 = rl; - while (rl2!=NULL) { - if (rl2->data == rt) break; - rl2 = rl2->next; - } - if (rl2==NULL) { - addmessage(r, mage->faction, "Dorthin führt kein Weg.", - MSG_MAGIC, ML_MISTAKE); - free_regionlist(rl); - return 0; - } - free_regionlist(rl); - break; - default: - sprintf(buf, "Der Zauber funktioniert nur in der astralen Welt."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - if (ro==NULL || is_cursed(ro->attribs, C_ASTRALBLOCK, 0) || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - sprintf(buf, "Die Wege aus dieser astralen Region sind blockiert."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - remaining_cap = (int)((power-3) * 1500); - - /* für jede Einheit in der Kommandozeile */ - for (n = 1; n < pa->length; n++) { - if(pa->param[n]->flag == TARGET_NOTFOUND) continue; - - u = pa->param[n]->data.u; - - if (!ucontact(u, mage)) { - if (power > 10 && !pa->param[n]->flag == TARGET_RESISTS && can_survive(u, rt)) { - sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " - "Zauber jedoch nicht.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", - unitname(u), - cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - } else { - sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " - "Zauber.", unitname(u)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_WARN); - sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", - cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", - unitname(u)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); - continue; - } - } - - w = weight(u); - - if(!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - } else if(remaining_cap - w < 0) { - addmessage(r, mage->faction, "Die Einheit ist zu schwer.", - MSG_MAGIC, ML_MISTAKE); - } else { - remaining_cap = remaining_cap - w; - move_unit(u, rt, NULL); - - /* Meldungen in der Ausgangsregion */ - - for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for(u2 = r->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, r, u, 0)) { - sprintf(buf, "%s wird durchscheinend und verschwindet.", - unitname(u)); - addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - - /* Meldungen in der Zielregion */ - - for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); - for (u2 = rt->units; u2; u2 = u2->next ) { - if(!fval(u2->faction, FL_DH)) { - fset(u2->faction, FL_DH); - if(cansee(u2->faction, rt, u, 0)) { - sprintf(buf, "%s erscheint plötzlich.", unitname(u)); - addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - } - } - return cast_level; -} - -int -sp_fetchastral(castorder *co) -{ - int n; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - double power = co->force; - int remaining_cap = (int)((power-3) * 1500); - region_list * rtl = NULL; - region * rt = co->rt; /* region to which we are fetching */ - region * ro = NULL; /* region in which the target is */ - - if (rplane(rt)!=get_normalplane()) { - ADDMSG(&mage->faction->msgs, msg_message("error190", - "command region unit", co->order, rt, mage)); - return 0; - } - - if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", - "command region unit", co->order, rt, mage)); - return 0; - } - - /* für jede Einheit in der Kommandozeile */ - for (n=0; n!=pa->length; ++n) { - unit * u = pa->param[n]->data.u; - int w; - - if (pa->param[n]->flag & TARGET_NOTFOUND) continue; - - if (u->region!=ro) { - /* this can happen several times if the units are from different astral - * regions. Only possible on the intersections of schemes */ - region_list * rfind; - if (getplane(u->region) != get_astralplane()) { - cmistake(mage, co->order, 193, MSG_MAGIC); - continue; - } - if (rtl!=NULL) free_regionlist(rtl); - rtl = astralregions(u->region, NULL); - for (rfind=rtl;rfind!=NULL;rfind=rfind->next) { - if (rfind->data==mage->region) break; - } - if (rfind==NULL) { - /* the region r is not in the schemes of rt */ - ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", - "command region unit target", co->order, mage->region, mage, u)); - continue; - } - ro = u->region; - } - - if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { - ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", - "command region unit", co->order, mage->region, mage)); - continue; - } - - if (!can_survive(u, rt)) { - cmistake(mage, co->order, 231, MSG_MAGIC); - continue; - } - - w = weight(u); - if (remaining_cap - w < 0) { - ADDMSG(&mage->faction->msgs, msg_message("fail_tooheavy", - "command region unit target", co->order, mage->region, mage, u)); - continue; - } - - if (!ucontact(u, mage)) { - if (power>12 && !(pa->param[n]->flag & TARGET_RESISTS)) { - sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " - "Zauber jedoch nicht.", unitname(u)); - addmessage(rt, mage->faction, buf, MSG_MAGIC, ML_INFO); - sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", - unitname(u), unitname(mage)); - addmessage(ro, u->faction, buf, MSG_EVENT, ML_WARN); - } else { - sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " - "Zauber.", unitname(u)); - addmessage(rt, mage->faction, buf, MSG_MAGIC, ML_WARN); - sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", - unitname(mage), unitname(u)); - addmessage(ro, u->faction, buf, MSG_EVENT, ML_WARN); - continue; - } - } - - remaining_cap -= w; - move_unit(u, rt, NULL); - - /* Meldungen in der Ausgangsregion */ - ADDMSG(&ro->msgs, msg_message("astral_disappear", "unit", u)); - - /* Meldungen in der Zielregion */ - ADDMSG(&rt->msgs, msg_message("astral_appear", "unit", u)); - } - if (rtl!=NULL) free_regionlist(rtl); - return cast_level; -} - -#ifdef SHOWASTRAL_NOT_BORKED -int -sp_showastral(castorder *co) -{ - unit *u; - region *rt; - int n = 0; - int c = 0; - region_list *rl, *rl2; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - - switch(getplaneid(r)) { - case 0: - rt = r_standard_to_astral(r); - if(!rt) { - /* Hier gibt es keine Verbindung zur astralen Welt */ - cmistake(mage, co->order, 216, MSG_MAGIC); - return 0; - } - break; - case 1: - rt = r; - break; - default: - /* Hier gibt es keine Verbindung zur astralen Welt */ - cmistake(mage, co->order, 216, MSG_MAGIC); - return 0; - } - - rl = all_in_range(rt,power/5); - - /* Erst Einheiten zählen, für die Grammatik. */ - - 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 != new_race[RC_SPECIAL] && u->race != new_race[RC_SPELL]) n++; - } - } - } - - if(n == 0) { - /* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.", - unitname(mage)); */ - cmistake(mage, co->order, 220, MSG_MAGIC); - } else { - - /* Ausgeben */ - - sprintf(buf, "%s hat eine Vision der astralen Ebene. Im astralen " - "Nebel zu erkennen sind ", unitname(mage)); - - 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 != new_race[RC_SPECIAL] && u->race != new_race[RC_SPELL]) { - c++; - scat(unitname(u)); - scat(" ("); - if(!fval(u, UFL_PARTEITARNUNG)) { - scat(factionname(u->faction)); - scat(", "); - } - icat(u->number); - scat(" "); - scat(LOC(mage->faction->locale, rc_name(u->race, u->number!=1))); - scat(", Entfernung "); - icat(distance(rl2->data, rt)); - scat(")"); - if(c == n-1) { - scat(" und "); - } else if(c < n-1) { - scat(", "); - } - } - } - } - } - scat("."); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - } - - free_regionlist(rl); - return cast_level; - unused(co); - return 0; -} -#endif - -/* ------------------------------------------------------------- */ -int -sp_viewreality(castorder *co) -{ - region_list *rl, *rl2; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - if(getplaneid(r) != 1) { - /* sprintf(buf, "Dieser Zauber kann nur im Astralraum gezaubert werden."); */ - cmistake(mage, co->order, 217, MSG_MAGIC); - return 0; - } - - if(is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { - /* sprintf(buf, "Die materielle Welt ist hier nicht sichtbar.");*/ - cmistake(mage, co->order, 218, MSG_MAGIC); - return 0; - } - - rl = astralregions(r, NULL); - - /* Irgendwann mal auf Curses u/o Attribut umstellen. */ - for (rl2=rl; rl2; rl2=rl2->next) { - u = createunit(rl2->data, mage->faction, RS_FARVISION, new_race[RC_SPELL]); - set_level(u, SK_OBSERVATION, co->level/2); - set_string(&u->name, "Zauber: Blick in die Realität"); - u->age = 2; - } - - free_regionlist(rl); - - sprintf(buf, "%s gelingt es, durch die Nebel auf die Realität zu blicken.", - unitname(mage)); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - return cast_level; -} - -/* ------------------------------------------------------------- */ -int -sp_disruptastral(castorder *co) -{ - region_list *rl, *rl2; - region *rt; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - int duration = (int)(power/3)+1; - - switch(getplaneid(r)) { - case 0: - rt = r_standard_to_astral(r); - if(!rt) { - /* "Hier gibt es keine Verbindung zur astralen Welt." */ - cmistake(mage, co->order, 216, MSG_MAGIC); - return 0; - } - break; - case 1: - rt = r; - break; - default: - /* "Von hier aus kann man die astrale Ebene nicht erreichen." */ - cmistake(mage, co->order, 215, MSG_MAGIC); - return 0; - } - - rl = all_in_range(rt, (short)(power/5), NULL); - - for (rl2=rl; rl2!=NULL; rl2=rl2->next) { - attrib *a, *a2; - variant effect; - region * r2 = rl2->data; - spec_direction *sd; - int inhab_regions = 0; - region_list * trl = NULL; - - if (is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) continue; - - if (r2->units!=NULL) { - region_list * trl2; - - trl = astralregions(rl2->data, inhabitable); - for (trl2 = trl; trl2; trl2 = trl2->next) ++inhab_regions; - } - - /* Nicht-Permanente Tore zerstören */ - a = a_find(r->attribs, &at_direction); - - while (a!=NULL) { - a2 = a->nexttype; - sd = (spec_direction *)(a->data.v); - if (sd->duration != -1) a_remove(&r->attribs, a); - a = a2; - } - - /* Einheiten auswerfen */ - - if (trl!=NULL) { - for (u=r2->units;u;u=u->next) { - if (u->race != new_race[RC_SPELL]) { - region_list *trl2 = trl; - region *tr; - int c = rand() % inhab_regions; - - /* Zufällige Zielregion suchen */ - while (c--!=0) trl2 = trl2->next; - tr = trl2->data; - - if(!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) { - move_unit(u, tr, NULL); - sprintf(buf, "%s wird aus der astralen Ebene nach %s geschleudert.", - unitname(u), regionname(tr, u->faction)); - addmessage(0, u->faction, buf, MSG_MAGIC, ML_INFO); - } - } - } - free_regionlist(trl); - } - - /* Kontakt unterbinden */ - effect.i = 100; - create_curse(mage, &rl2->data->attribs, ct_find("astralblock"), - power, duration, effect, 0); - addmessage(r2, 0, "Mächtige Magie verhindert den Kontakt zur Realität.", - MSG_COMMENT, ML_IMPORTANT); - } - - free_regionlist(rl); - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Mauern der Ewigkeit - * Stufe: 7 - * Kategorie: Artefakt - * Gebiet: Tybied - * Wirkung: - * Das Gebäude kostet keinen Unterhalt mehr - * - * ZAUBER "Mauern der Ewigkeit" - * Flags: (0) - */ -static int -sp_eternizewall(castorder *co) -{ - unit *u; - curse * c; - building *b; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - b = pa->param[0]->data.b; - c = create_curse(mage, &b->attribs, ct_find("nocost"), - power*power, 1, zero_effect, 0); - - if(c==NULL) { /* ist bereits verzaubert */ - cmistake(mage, co->order, 206, MSG_MAGIC); - return 0; - } - - curse_setflag(c, CURSE_NOAGE|CURSE_ONLYONE); - - /* melden, 1x pro Partei in der Burg */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for (u = r->units; u; u = u->next) { - if (!fval(u->faction, FL_DH)) { - fset(u->faction, FL_DH); - if (u->building == b) { - sprintf(buf, "Mit einem Ritual bindet %s die magischen Kräfte " - "der Erde in die Mauern von %s", unitname(mage), - buildingname(b)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - } - - return cast_level; -} - - -/* ------------------------------------------------------------- */ -/* Name: Opfere Kraft - * Stufe: 15 - * Gebiet: Tybied - * Kategorie: Einheit, positiv - * Wirkung: - * Mit Hilfe dieses Zaubers kann der Magier einen Teil seiner - * magischen Kraft permanent auf einen anderen Magier übertragen. Auf - * einen Tybied-Magier kann er die Hälfte der eingesetzten Kraft - * übertragen, auf einen Magier eines anderen Gebietes ein Drittel. - * - * Flags: - * (UNITSPELL|ONETARGET) - * - * Syntax: - * ZAUBERE \"Opfere Kraft\" - * "ui" - */ -int -sp_permtransfer(castorder *co) -{ - int aura; - unit *tu; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - const spell *sp = co->sp; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber - * abbrechen aber kosten lassen */ - if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; - - tu = pa->param[0]->data.u; - aura = pa->param[1]->data.i; - - if(!is_mage(tu)) { -/* sprintf(buf, "%s in %s: 'ZAUBER %s': Einheit ist kein Magier." - , unitname(mage), regionname(mage->region, mage->faction),sa->strings[0]); */ - cmistake(mage, co->order, 214, MSG_MAGIC); - return 0; - } - - aura = min(get_spellpoints(mage)-spellcost(mage, sp), aura); - - change_maxspellpoints(mage,-aura); - change_spellpoints(mage,-aura); - - if(get_mage(tu)->magietyp == get_mage(mage)->magietyp) { - change_maxspellpoints(tu, aura/2); - } else { - change_maxspellpoints(tu, aura/3); - } - - sprintf(buf, "%s opfert %s %d Aura.", unitname(mage), unitname(tu), aura); - addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* TODO: specialdirections? */ - -int -sp_movecastle(castorder *co) -{ - building *b; - direction_t dir; - region *target_region; - unit *u, *unext; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - spellparameter *pa = co->par; - const spell *sp = co->sp; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - b = pa->param[0]->data.b; - dir = finddirection(pa->param[1]->data.s, mage->faction->locale); - - if(dir == NODIRECTION) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Ungültige Richtung %s.", - unitname(mage), regionname(mage->region, mage->faction), - spell_name(sp, mage->faction->locale), - pa->param[1]->data.s); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return 0; - } - - if(b->size > (cast_level-12) * 250) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Der Elementar ist " - "zu klein, um das Gebäude zu tragen.", unitname(mage), - regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return cast_level; - } - - target_region = rconnect(r,dir); - - if(!(terrain[target_region->terrain].flags & LAND_REGION)) { - sprintf(buf, "%s in %s: 'ZAUBER %s': Der Erdelementar " - "weigert sich, nach %s zu gehen.", - unitname(mage), regionname(mage->region, mage->faction), - spell_name(sp, mage->faction->locale), - locale_string(mage->faction->locale, directions[dir])); - addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); - return cast_level; - } - - bunhash(b); - translist(&r->buildings, &target_region->buildings, b); - b->region = target_region; - b->size -= b->size/(10-rand()%6); - bhash(b); - - for(u=r->units;u;) { - unext = u->next; - if(u->building == b) { - uunhash(u); - translist(&r->units, &target_region->units, u); - uhash(u); - } - u = unext; - } - - sprintf(buf, "Ein Beben erschüttert %s. Viele kleine Pseudopodien " - "erheben das Gebäude und tragen es in Richtung %s.", - buildingname(b), locale_string(mage->faction->locale, directions[dir])); - - if((b->type==bt_find("caravan") || b->type==bt_find("dam") || b->type==bt_find("tunnel"))) { - boolean damage = false; - direction_t d; - for (d=0;d!=MAXDIRECTIONS;++d) { - if (rroad(r, d)) { - rsetroad(r, d, rroad(r, d)/2); - damage = true; - } - } - if (damage) strcat(buf, " Die Straßen der Region wurden beschädigt."); - } - addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Luftschiff - * Stufe: 6 - * - * Wirkung: - * Läßt ein Schiff eine Runde lang fliegen. Wirkt nur auf Boote und - * Langboote. - * Kombinierbar mit "Günstige Winde", aber nicht mit "Sturmwind". - * - * Flag: - * (ONSHIPCAST | SHIPSPELL | ONETARGET | TESTRESISTANCE) - */ -int -sp_flying_ship(castorder *co) -{ - ship *sh; - unit *u; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - sh = pa->param[0]->data.sh; - - if(is_cursed(sh->attribs, C_SHIP_FLYING, 0) ) { -/* sprintf(buf, "Auf dem Schiff befindet liegt bereits so ein Zauber."); */ - cmistake(mage, co->order, 211, MSG_MAGIC); - return 0; - } - if(is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { -/* sprintf(buf, "Es ist zu gefährlich, ein sturmgepeitschtes Schiff " - "fliegen zu lassen."); */ - cmistake(mage, co->order, 210, MSG_MAGIC); - return 0; - } - /* mit C_SHIP_NODRIFT haben wir kein Problem */ - - /* Duration = 1, nur diese Runde */ - create_curse(mage, &sh->attribs, ct_find("flyingship"), power, 1, zero_effect, 0); - /* Da der Spruch nur diese Runde wirkt, brauchen wir kein - * set_cursedisplay() zu benutzten - es sieht eh niemand... - */ - sh->coast = NODIRECTION; - - /* melden, 1x pro Partei */ - for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); - for(u = r->units; u; u = u->next ) { - /* das sehen natürlich auch die Leute an Land */ - if(!fval(u->faction, FL_DH) ) { - fset(u->faction, FL_DH); - sprintf(buf, "%s beschwört einen Luftgeist, der die %s in " - "die Wolken hebt.", - cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", - shipname(sh)); - addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); - } - } - return cast_level; -} - - -/* ------------------------------------------------------------- */ -/* Name: Stehle Aura - * Stufe: 6 - * Kategorie: Einheit, negativ - * Wirkung: - * Mit Hilfe dieses Zaubers kann der Magier einem anderen Magier - * seine Aura gegen dessen Willen entziehen und sich selber - * zuführen. - * - * Flags: - * (FARCASTING | SPELLLEVEL | UNITSPELL | ONETARGET | TESTRESISTANCE | - * TESTCANSEE) - * */ -int -sp_stealaura(castorder *co) -{ - int taura; - unit *u; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double power = co->force; - spellparameter *pa = co->par; - - /* wenn kein Ziel gefunden, Zauber abbrechen */ - if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; - - /* Zieleinheit */ - u = pa->param[0]->data.u; - - if(!get_mage(u)) { - ADDMSG(&mage->faction->msgs, msg_message( - "stealaura_fail", "unit target", mage, u)); - ADDMSG(&u->faction->msgs, msg_message( - "stealaura_fail_detect", "unit", u)); - return 0; - } - - taura = (get_mage(u)->spellpoints*(rand()%(int)(3*power)+1))/100; - - if(taura > 0) { - get_mage(u)->spellpoints -= taura; - get_mage(mage)->spellpoints += taura; -/* sprintf(buf, "%s entzieht %s %d Aura.", unitname(mage), unitname(u), - taura); */ - ADDMSG(&mage->faction->msgs, msg_message( - "stealaura_success", "mage target aura", mage, u, taura)); -/* sprintf(buf, "%s fühlt seine magischen Kräfte schwinden und verliert %d " - "Aura.", unitname(u), taura); */ - ADDMSG(&u->faction->msgs, msg_message( - "stealaura_detect", "unit aura", u, taura)); - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "stealaura_fail", "unit target", mage, u)); - ADDMSG(&u->faction->msgs, msg_message( - "stealaura_fail_detect", "unit", u)); - } - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Astrale Schwächezone - * Stufe: 5 - * Kategorie: - * Wirkung: - * Reduziert die Stärke jedes Spruch in der Region um Level Hält - * Sprüche bis zu einem Gesammtlevel von Stärke*10 aus, danach ist - * sie verbraucht. - * leibt bis zu Stärke Wochen aktiv. - * Ein Ring der Macht erhöht die Stärke um 1, in einem Magierturm - * gezaubert gibt nochmal +1 auf Stärke. (force) - * - * Beispiel: - * Eine Antimagiezone Stufe 7 hält bis zu 7 Wochen an oder Sprüche mit - * einem Gesammtlevel bis zu 70 auf. Also zB 7 Stufe 10 Sprüche, 10 - * Stufe 7 Sprüche oder 35 Stufe 2 Sprüche. Sie reduziert die Stärke - * (level+boni) jedes Spruchs, der in der Region gezaubert wird, um - * 7. Alle Sprüche mit einer Stärke kleiner als 7 schlagen fehl - * (power = 0). - * - * Flags: - * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) - * - */ -int -sp_antimagiczone(castorder *co) -{ - double power; - variant effect; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - int duration = (int)force+1; - - /* Hält Sprüche bis zu einem summierten Gesamtlevel von power aus. - * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone - * um seine Stufe */ - power = force * 10; - - /* Reduziert die Stärke jedes Spruchs um effect */ - effect.i = cast_level; - - create_curse(mage, &r->attribs, ct_find("antimagiczone"), power, duration, - effect, 0); - - /* Erfolg melden*/ - ADDMSG(&mage->faction->msgs, msg_message( - "regionmagic_effect", "unit region command", mage, - mage->region, co->order)); - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Schutzrunen - * Stufe: 8 - * Kosten: SPC_FIX - * - * Wirkung: - * Gibt Gebäuden einen Bonus auf Magieresistenz von +20%. Der Zauber - * dauert 3+rand()%Level Wochen an, also im Extremfall bis zu 2 Jahre - * bei Stufe 20 - * - * Es können mehrere Zauber übereinander gelegt werden, der Effekt - * summiert sich, jedoch wird die Dauer dadurch nicht verlängert. - * - * oder: - * - * Gibt Schiffen einen Bonus auf Magieresistenz von +20%. Der Zauber - * dauert 3+rand()%Level Wochen an, also im Extremfall bis zu 2 Jahre - * bei Stufe 20 - * - * Es können mehrere Zauber übereinander gelegt werden, der Effekt - * summiert sich, jedoch wird die Dauer dadurch nicht verlängert. - * - * Flags: - * (ONSHIPCAST | TESTRESISTANCE) - * - * Syntax: - * ZAUBERE \"Runen des Schutzes\" GEBÄUDE - * ZAUBERE \"Runen des Schutzes\" SCHIFF - * "kc" - */ - -static int -sp_magicrunes(castorder *co) -{ - int duration; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - variant effect; - - duration = 3 + rand()%cast_level; - effect.i = 20; - - switch(pa->param[0]->typ){ - case SPP_BUILDING: - { - building *b; - b = pa->param[0]->data.b; - - /* Magieresistenz der Burg erhöht sich um 20% */ - create_curse(mage, &b->attribs, ct_find("magicrunes"), force, - duration, effect, 0); - - /* Erfolg melden */ - ADDMSG(&mage->faction->msgs, msg_message( - "objmagic_effect", "unit region command target", mage, - mage->region, co->order, buildingname(b))); - break; - } - case SPP_SHIP: - { - ship *sh; - sh = pa->param[0]->data.sh; - /* Magieresistenz des Schiffs erhöht sich um 20% */ - create_curse(mage, &sh->attribs, ct_find("magicrunes"), force, - duration, effect, 0); - - /* Erfolg melden */ - ADDMSG(&mage->faction->msgs, msg_message( - "objmagic_effect", "unit region command target", mage, - mage->region, co->order, shipname(sh))); - break; - } - default: - /* fehlerhafter Parameter */ - return 0; - } - - return cast_level; -} - -/* ------------------------------------------------------------- */ -/* Name: Zeitdehnung - * - * Flags: - * (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) - * Syntax: - * "u+" - */ - -int -sp_speed2(castorder *co) -{ - int n, maxmen, used = 0, dur, men; - unit *u; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - - maxmen = 2 * cast_level * cast_level; - dur = max(1, cast_level/2); - - for (n = 0; n < pa->length; n++) { - variant effect; - /* sollte nie negativ werden */ - if (maxmen < 1) - break; - - if(pa->param[n]->flag == TARGET_RESISTS - || pa->param[n]->flag == TARGET_NOTFOUND) - continue; - - u = pa->param[n]->data.u; - - men = min(maxmen,u->number); - effect.i = 2; - create_curse(mage, &u->attribs, ct_find("speed"), force, dur, effect, men); - maxmen -= men; - used += men; - } - - /* TODO: Erfolg melden*/ - /* Effektiv benötigten cast_level (mindestens 1) zurückgeben */ - used = (int)sqrt(used/2); - return max(1, used); -} - -/* ------------------------------------------------------------- */ -/* Name: Magiefresser - * Stufe: 7 - * Kosten: SPC_LEVEL - * - * Wirkung: - * Kann eine bestimmte Verzauberung angreifen und auflösen. Die Stärke - * des Zaubers muss stärker sein als die der Verzauberung. - * Syntax: - * ZAUBERE \"Magiefresser\" REGION - * ZAUBERE \"Magiefresser\" EINHEIT - * ZAUBERE \"Magiefresser\" GEBÄUDE - * ZAUBERE \"Magiefresser\" SCHIFF - * - * "kc?c" - * Flags: - * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) - */ -/* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft - * (force) */ -int -sp_q_antimagie(castorder *co) -{ - attrib **ap; - int obj; - curse * c = NULL; - int succ; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - char *ts; - - obj = pa->param[0]->typ; - - switch(obj){ - case SPP_REGION: - ap = &r->attribs; - set_string(&ts, regionname(r, mage->faction)); - break; - - case SPP_TEMP: - case SPP_UNIT: - { - unit *u = pa->param[0]->data.u; - ap = &u->attribs; - set_string(&ts, unitid(u)); - break; - } - case SPP_BUILDING: - { - building *b = pa->param[0]->data.b; - ap = &b->attribs; - set_string(&ts, buildingid(b)); - break; - } - case SPP_SHIP: - { - ship *sh = pa->param[0]->data.sh; - ap = &sh->attribs; - set_string(&ts, shipid(sh)); - break; - } - default: - /* Das Zielobjekt wurde vergessen */ - cmistake(mage, co->order, 203, MSG_MAGIC); - return 0; - } - - succ = destroy_curse(ap, cast_level, force, c); - - if (succ) { - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_effect", "unit region command succ target", - mage, mage->region, co->order, succ, strdup(ts))); - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_noeffect", "unit region command", - mage, mage->region, co->order)); - } - - return max(succ, 1); -} - -/* ------------------------------------------------------------- */ -/* Name: Fluch brechen - * Stufe: 7 - * Kosten: SPC_LEVEL - * - * Wirkung: - * Kann eine bestimmte Verzauberung angreifen und auflösen. Die Stärke - * des Zaubers muss stärker sein als die der Verzauberung. - * Syntax: - * ZAUBERE \"Fluch brechen\" REGION - * ZAUBERE \"Fluch brechen\" EINHEIT - * ZAUBERE \"Fluch brechen\" GEBÄUDE - * ZAUBERE \"Fluch brechen\" SCHIFF - * - * "kcc" - * Flags: - * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) - */ -int -sp_destroy_curse(castorder *co) -{ - attrib **ap; - int obj; - curse * c; - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - double force = co->force; - spellparameter *pa = co->par; - char *ts = NULL; - - if(pa->length < 2){ - /* Das Zielobjekt wurde vergessen */ - cmistake(mage, co->order, 203, MSG_MAGIC); - } - - obj = pa->param[0]->typ; - - c = findcurse(atoi36(pa->param[1]->data.s)); - if (!c){ - /* Es wurde kein Ziel gefunden */ - ADDMSG(&mage->faction->msgs, msg_message( - "spelltargetnotfound", "unit region command", - mage, mage->region, co->order)); - } else { - switch(obj){ - case SPP_REGION: - ap = &r->attribs; - set_string(&ts, regionname(r, mage->faction)); - break; - - case SPP_TEMP: - case SPP_UNIT: - { - unit *u = pa->param[0]->data.u; - ap = &u->attribs; - set_string(&ts, unitid(u)); - break; - } - case SPP_BUILDING: - { - building *b = pa->param[0]->data.b; - ap = &b->attribs; - set_string(&ts, buildingid(b)); - break; - } - case SPP_SHIP: - { - ship *sh = pa->param[0]->data.sh; - ap = &sh->attribs; - set_string(&ts, shipid(sh)); - break; - } - default: - /* Das Zielobjekt wurde vergessen */ - cmistake(mage, co->order, 203, MSG_MAGIC); - return 0; - } - - /* überprüfung, ob curse zu diesem objekt gehört */ - if (!is_cursed_with(*ap, c)){ - /* Es wurde kein Ziel gefunden */ - ADDMSG(&mage->faction->msgs, - msg_message( - "spelltargetnotfound", "unit region command", - mage, mage->region, co->order)); - } - - /* curse auflösen, wenn zauber stärker (force > vigour)*/ - c->vigour -= force; - - if (c->vigour <= 0.0) { - remove_curse(ap, c); - - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_effect", "unit region command id target", - mage, mage->region, co->order, strdup(pa->param[1]->data.s), - strdup(ts))); - } else { - ADDMSG(&mage->faction->msgs, msg_message( - "destroy_magic_noeffect", "unit region command", - mage, mage->region, co->order)); - } - } - if (ts != NULL) free(ts); - - return cast_level; -} - - -/* ------------------------------------------------------------- */ -int -sp_becomewyrm(castorder *co) -{ - unit *u = (unit *)co->magician; - int wyrms_already_created = 0; - int wyrms_allowed; - attrib *a; - - wyrms_allowed = fspecial(u->faction, FS_WYRM); - a = a_find(u->faction->attribs, &at_wyrm); - if(a) wyrms_already_created = a->data.i; - - if(wyrms_already_created >= wyrms_allowed) { - cmistake(u, co->order, 262, MSG_MAGIC); - return 0; - } - - if(!a) { - a_add(&u->faction->attribs, a_new(&at_wyrm)); - a->data.i = 1; - } else { - a->data.i++; - } - - u->race = new_race[RC_WYRM]; - add_spell(get_mage(u), find_spellbyid(SPL_WYRMODEM)); - - ADDMSG(&u->faction->msgs, msg_message("becomewyrm", "u", u)); - - return co->level; -} - -#ifdef WDW_PYRAMIDSPELL -/* ------------------------------------------------------------- */ -/* Name: WDW-Pyramidenfindezauber - * Stufe: unterschiedlich - * Gebiet: alle - * Wirkung: - * gibt die ungefaehre Entfernung zur naechstgelegenen Pyramiden- - * region an. - * - * Flags: - */ -static int -sp_wdwpyramid(castorder *co) -{ - region *r = co->rt; - unit *mage = (unit *)co->magician; - int cast_level = co->level; - - if(a_find(r->attribs, &at_wdwpyramid) != NULL) { - ADDMSG(&mage->faction->msgs, msg_message("wdw_pyramidspell_found", - "unit region command", mage, r, co->order)); - } else { - region *r2; - int mindist = INT_MAX; - int minshowdist; - int maxshowdist; - - for(r2 = regions; r2; r2 = r2->next) { - if(a_find(r2->attribs, &at_wdwpyramid) != NULL) { - int dist = distance(mage->region, r2); - if (dist < mindist) { - mindist = dist; - } - } - } - - assert(mindist >= 1); - - minshowdist = mindist - rand()%5; - maxshowdist = minshowdist + 4; - - ADDMSG(&mage->faction->msgs, msg_message("wdw_pyramidspell_notfound", - "unit region command mindist maxdist", mage, r, co->order, - max(1, minshowdist), maxshowdist)); - } - - return cast_level; -} -#endif - -/* ------------------------------------------------------------- */ -/* Name: Alltagszauber, hat je nach Gebiet anderen Namen - * Stufe: 1 - * Gebiet: alle - * Wirkung: der Magier verdient $50 pro Spruchstufe - * Kosten: 1 SP pro Stufe - */ -#include "../gamecode/economy.h" -/* TODO: das ist scheisse, aber spells gehören eh nicht in den kernel */ -int -sp_earn_silver(castorder *co) -{ - unit *mage = (unit *)co->magician; - double force = co->force; - region *r = co->rt; - int wanted = (int)(force * 50); - int earned = min(rmoney(r), wanted); - - rsetmoney(r, rmoney(r) - earned); - change_money(mage, earned); - /* TODO klären: ist das Silber damit schon reserviert? */ - - add_income(mage, IC_MAGIC, wanted, earned); - return co->level; -} - - -/* ------------------------------------------------------------- */ -/* Dummy-Zauberpatzer, Platzhalter für speziel auf die Sprüche - * zugeschnittene Patzer */ -void -patzer(castorder *co) -{ - unit *mage = (unit *)co->magician; - - report_failure(mage, co->order); - return; -} - -/* ------------------------------------------------------------- */ -/* Erläuterungen zu den Spruchdefinitionen - * - * Spruchstukturdefinition: - * spell{ - * id, name, - * beschreibung, - * syntax, - * parameter, - * magietyp, - * sptyp, - * rank,level, - * costtyp, aura, - * komponenten[5][2][faktorart], - * &funktion, patzer} - * - * id: - * SPL_NOSPELL muss der letzte Spruch in der Liste spelldaten sein, - * denn nicht auf die Reihenfolge in der Liste sondern auf die id wird - * geprüft - * - * sptyp: - * besondere Spruchtypen und Flags - * (Regionszauber, Kampfzauber, Farcastbar, Stufe variable, ..) - * - * rank: - * gibt die Priorität und damit die Reihenfolge an, in der der Spruch - * gezaubert wird. - * 1: Aura übertragen - * 2: Antimagie - * 3: Magierverändernde Sprüche (Magic Boost, ..) - * 4: Monster erschaffen - * 5: Standartlevel - * 7: Teleport - * - * Komponenten[Anzahl mögl. Items][Art:Anzahl:Kostentyp] - * - * R_AURA: - * Grundkosten für einen Zauber. Soviel Mp müssen mindestens investiert - * werden, um den Spruch zu wirken. Zusätzliche Mp können unterschiedliche - * Auswirkungen haben, die in der Spruchfunktionsroutine definiert werden. - * - * R_PERMAURA: - * Kosten an permantenter Aura - * - * Komponenten Kostentyp: - * SPC_LEVEL == Spruch mit Levelabhängigen Magiekosten. Die angegeben - * Kosten müssen für Stufe 1 berechnet sein. - * SPC_FIX == Feste Kosten - * - * Wenn keine spezielle Syntax angegeben ist, wird die - * Syntaxbeschreibung aus sptyp generiert: - * FARCASTING: ZAUBER [REGION x y] - * SPELLLEVEL: ZAUBER [STUFE n] - * UNITSPELL : ZAUBER [ ..] - * SHIPSPELL : ZAUBER [ ..] - * BUILDINGSPELL: ZAUBER [ ..] - * ONETARGET : ZAUBER - * PRECOMBATSPELL : KAMPFZAUBER [STUFE n] - * COMBATSPELL : KAMPFZAUBER [STUFE n] - * POSTCOMBATSPELL: KAMPFZAUBER [STUFE n] - * - * Das Parsing - * - * Der String spell->parameter gibt die Syntax an, nach der die - * Parameter des Spruches in add_spellparameter() geparst werden sollen. - * - * u : eine Einheitennummer - * r : hier kommen zwei Regionskoordinaten x y - * b : Gebäude- oder Burgnummer - * s : Schiffsnummer - * c : String, wird ohne Weiterverarbeitung übergeben - * i : Zahl (int), wird ohne Weiterverarbeitung übergeben - * k : Keywort - dieser String gibt den Paramter an, der folgt. Der - * Parameter wird mit findparam() identifiziert. - * k muss immer von einem c als Platzhalter für das Objekt gefolgt - * werden. - * Ein gutes Beispiel sind hierfür die Sprüche zur Magieanalyse. - * + : gibt an, das der vorherige Parameter mehrfach vorkommen kann. Da - * ein Ende nicht definiert werden kann, muss dies immer am Schluss - * kommen. - * - * Flags für das Parsing: - * TESTRESISTANCE : alle Zielobjekte, also alle Parameter vom Typ Unit, - * Burg, Schiff oder Region, werden auf ihre - * Magieresistenz überprüft - * TESTCANSEE : jedes Objekt vom Typ Einheit wird auf seine - * Sichtbarkeit überprüft - * SEARCHGLOBAL : die Zielobjekte werden global anstelle von regional - * gesucht - * REGIONSPELL : Ziel ist die Region, auch wenn kein Zielobjekt - * angegeben wird. Ist TESTRESISTANCE gesetzt, so wird - * die Magieresistenz der Region überprüft - * - * Bei fehlendem Ziel oder wenn dieses dem Zauber widersteht, wird die - * Spruchfunktion nicht aufgerufen. - * Sind zu wenig Parameter vorhanden, wird der Zauber ebenfalls nicht - * ausgeführt. - * Ist eins von mehreren Zielobjekten resistent, so wird das Flag - * pa->param[n]->flag == TARGET_RESISTS - * Ist eins von mehreren Zielobjekten nicht gefunden worden, so ist - * pa->param[n]->flag == TARGET_NOTFOUND - * - */ -/* ------------------------------------------------------------- */ +/* util includes */ +#include +#include /* Bitte die Sprüche nach Gebieten und Stufe ordnen, denn in derselben * Reihenfolge wie in Spelldaten tauchen sie auch im Report auf @@ -7464,2457 +213,3 @@ find_spellbyid(spellid_t id) return NULL; } -static spell spelldaten[] = -{ - /* M_DRUIDE */ - { - SPL_BLESSEDHARVEST, "blessedharvest", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL), - 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_blessedharvest, - patzer - }, - { - SPL_GWYRRD_EARN_SILVER, "gwyrrdearnsilver", NULL, - NULL, NULL, - M_DRUIDE, - (SPELLLEVEL|ONSHIPCAST), - 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earn_silver, - patzer - }, - { - SPL_STONEGOLEM, "stonegolem", NULL, NULL, NULL, - M_DRUIDE, (SPELLLEVEL), 4, 1, - { - { R_AURA, 2, SPC_LEVEL }, - { R_STONE, 1, SPC_LEVEL }, - { R_TREES, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_create_stonegolem, patzer - }, - { - SPL_IRONGOLEM, "irongolem", NULL, NULL, NULL, - M_DRUIDE, (SPELLLEVEL), 4, 2, - { - { R_AURA, 2, SPC_LEVEL }, - { R_IRON, 1, SPC_LEVEL }, - { R_TREES, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_create_irongolem, patzer - }, - { - SPL_TREEGROW, "treegrow", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 5, 2, - { - { R_AURA, 4, SPC_LEVEL }, - { R_WOOD, 1, SPC_LEVEL }, - { R_TREES, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_hain, patzer_ents - }, - { - SPL_RUSTWEAPON, "rustweapon", NULL, NULL, - "u+", - M_DRUIDE, - (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE), - 5, 3, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_rosthauch, patzer - }, - { - SPL_KAELTESCHUTZ, "cold_protection", NULL, NULL, - "u+", - M_DRUIDE, - (UNITSPELL | SPELLLEVEL | TESTCANSEE | ONSHIPCAST), - 5, 3, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_kaelteschutz, patzer - }, - { - SPL_HAGEL, "hail", NULL, NULL, NULL, - M_DRUIDE, (COMBATSPELL|SPELLLEVEL), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_kampfzauber, patzer - }, - { - SPL_IRONKEEPER, "ironkeeper", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 5, 3, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_ironkeeper, patzer - }, - { - SPL_MAGICSTREET, "magicstreet", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | ONSHIPCAST | TESTRESISTANCE), - 5, 4, - { - { R_AURA, 1, SPC_LEVEL }, - { R_STONE, 1, SPC_FIX }, - { R_WOOD, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_magicstreet, patzer - }, - { - SPL_WINDSHIELD, "windshield", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_windshield, patzer - }, - { - SPL_MALLORNTREEGROW, "mallorntreegrow", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 5, 4, - { - { R_AURA, 6, SPC_LEVEL }, - { R_MALLORN, 1, SPC_LEVEL }, - { R_TREES, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_mallornhain, patzer_ents - }, - { SPL_GOODWINDS, "goodwinds", NULL, NULL, - "s", - M_DRUIDE, - (SHIPSPELL|ONSHIPCAST|SPELLLEVEL|ONETARGET|TESTRESISTANCE), - 5, 4, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_goodwinds, patzer - }, - { - SPL_HEALING, "healing", NULL, NULL, NULL, - M_DRUIDE, (POSTCOMBATSPELL | SPELLLEVEL), 5, 5, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_healing, patzer - }, - { - SPL_REELING_ARROWS, "reelingarrows", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 5, - { - { R_AURA, 15, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_reeling_arrows, patzer - }, - { - SPL_GWYRRD_FUMBLESHIELD, "gwyrrdfumbleshield", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 5, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fumbleshield, patzer - }, - { - SPL_TRANSFERAURA_DRUIDE, "transferauradruide", NULL, - "ZAUBERE \'Meditation\' ", - "ui", - M_DRUIDE, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 6, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_transferaura, patzer - }, - { - SPL_EARTHQUAKE, "earthquake", NULL, NULL, NULL, - M_DRUIDE, (FARCASTING|REGIONSPELL|TESTRESISTANCE), 5, 6, - { - { R_AURA, 25, SPC_FIX }, - { R_EOG, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earthquake, patzer - }, - { - SPL_STORMWINDS, "stormwinds", NULL, NULL, - "s+", - M_DRUIDE, - (SHIPSPELL | ONSHIPCAST | OCEANCASTABLE | TESTRESISTANCE | SPELLLEVEL), - 5, 6, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_stormwinds, patzer - }, - { - SPL_HOMESTONE, "homestone", NULL, NULL, NULL, - M_DRUIDE, (0), 5, 7, - { - { R_AURA, 50, SPC_FIX }, - { R_PERMAURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_homestone, patzer - }, - { - SPL_WOLFHOWL, "wolfhowl", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL ), 5, 7, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wolfhowl, patzer - }, - { - SPL_VERSTEINERN, "versteinern", NULL, NULL, NULL, - M_DRUIDE, (COMBATSPELL | SPELLLEVEL), 5, 8, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_versteinern, patzer - }, - { - SPL_STRONG_WALL, "strongwall", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_strong_wall, patzer - }, - { - SPL_GWYRRD_DESTROY_MAGIC, "gwyrrddestroymagic", NULL, - "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' REGION\n" - "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' SCHIFF ", - "kc?", - M_DRUIDE, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 2, 8, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_magic, patzer - }, - { - SPL_TREEWALKENTER, "treewalkenter", NULL, NULL, - "u+", - M_DRUIDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 7, 9, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_treewalkenter, patzer - }, - { - SPL_TREEWALKEXIT, "treewalkexit", NULL, - "ZAUBERE \'Sog des Lebens\' [ ..]", - "ru+", - M_DRUIDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 7, 9, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_treewalkexit, patzer - }, - { - SPL_HOLYGROUND, "holyground", NULL, NULL, NULL, - M_DRUIDE, (0), 5, 9, - { - { R_AURA, 80, SPC_FIX }, - { R_PERMAURA, 3, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_holyground, patzer - }, - { - SPL_SUMMONENT, "summonent", NULL, NULL, NULL, - M_DRUIDE, (SPELLLEVEL), 5, 10, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summonent, patzer - }, - { - SPL_GWYRRD_FAMILIAR, "gwyrrdfamiliar", NULL, NULL, NULL, - M_DRUIDE, (NOTFAMILIARCAST), 5, 10, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_familiar, patzer - }, - { - SPL_BLESSSTONECIRCLE, "blessstonecircle", NULL, NULL, - "b", - M_DRUIDE, (BUILDINGSPELL | ONETARGET), 5, 11, - { - { R_AURA, 350, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_blessstonecircle, patzer - }, - { - SPL_GWYRRD_ARMORSHIELD, "barkskin", NULL, NULL, NULL, - M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 12, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_armorshield, patzer - }, - { - SPL_DROUGHT, "summonfireelemental", NULL, NULL, NULL, - M_DRUIDE, (FARCASTING|REGIONSPELL|TESTRESISTANCE), 5, 13, - { - { R_AURA, 600, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_drought, patzer - }, - { - SPL_FOG_OF_CONFUSION, "fogofconfusion", NULL, NULL, NULL, - M_DRUIDE, - (FARCASTING|SPELLLEVEL), - 5, 14, - { - { R_AURA, 8, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fog_of_confusion, patzer - }, - { - SPL_MAELSTROM, "maelstrom", - "Dieses Ritual beschört einen großen Wasserelementar aus den " - "Tiefen des Ozeans. Der Elementar erzeugt einen gewaltigen " - "Strudel, einen Mahlstrom, welcher alle Schiffe, die ihn passieren, " - "schwer beschädigen kann.", NULL, NULL, - M_DRUIDE, - (OCEANCASTABLE | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE), - 5, 15, - { - { R_AURA, 200, SPC_FIX }, - { R_SEASERPENTHEAD, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_maelstrom, patzer - }, - { - SPL_MALLORN, "magic_roots", - "Mit Hilfe dieses aufwändigen Rituals läßt der Druide einen Teil seiner " - "dauerhaft in den Boden und die Wälder der Region fliessen. Dadurch wird " - "das Gleichgewicht der Natur in der Region für immer verändert, und in " - "Zukunft werden nur noch die anspruchsvollen, aber kräftigen " - "Mallorngewächse in der Region gedeihen.", NULL, NULL, - M_DRUIDE, - (FARCASTING | REGIONSPELL | TESTRESISTANCE), - 5, 16, - { - { R_AURA, 250, SPC_FIX }, - { R_PERMAURA, 10, SPC_FIX }, - { R_TOADSLIME, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_mallorn, patzer - }, - { - SPL_GREAT_DROUGHT, "great_drought", - "Dieses mächtige Ritual öffnet ein Tor in die Elementarebene der " - "Hitze. Eine grosse Dürre kommt über das Land. Bauern, Tiere und " - "Pflanzen der Region kämpfen um das nackte Überleben, aber eine " - "solche Dürre überlebt wohl nur die Hälfte aller Lebewesen. " - "Der Landstrich kann über Jahre hinaus von den Folgen einer " - "solchen Dürre betroffen sein.", NULL, NULL, - M_DRUIDE, - (FARCASTING | REGIONSPELL | TESTRESISTANCE), - 5, 17, - { - { R_AURA, 800, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_great_drought, patzer - }, - /* M_CHAOS */ - { - SPL_SPARKLE_CHAOS, "sparklechaos", NULL, NULL, - "u", - M_CHAOS, (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_sparkle, patzer - }, - { - SPL_DRAIG_EARN_SILVER, "draigearnsilver", NULL, - NULL, - NULL, - M_CHAOS, (SPELLLEVEL|ONSHIPCAST), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earn_silver, patzer - }, - { - SPL_FIREBALL, "fireball", NULL, NULL, NULL, - M_CHAOS, (COMBATSPELL | SPELLLEVEL), 5, 2, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_kampfzauber, patzer - }, - { - SPL_MAGICBOOST, "magicboost", NULL, NULL, NULL, - M_CHAOS, (ONSHIPCAST), 3, 3, - { - { R_AURA, 2, SPC_LINEAR }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_magicboost, patzer - }, - { - SPL_BLOODSACRIFICE, "bloodsacrifice", NULL, NULL, NULL, - M_CHAOS, (ONSHIPCAST), 1, 4, - { - { R_HITPOINTS, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_bloodsacrifice, patzer - }, - { - SPL_BERSERK, "berserk", NULL, NULL, NULL, - M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 4, 5, - { - { R_AURA, 5, SPC_LEVEL }, - { R_PEASANTS, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_berserk, patzer - }, - { - SPL_FUMBLECURSE, "fumblecurse", NULL, NULL, - "u", - M_CHAOS, - (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE | TESTRESISTANCE), - 4, 5, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fumblecurse, patzer_fumblecurse - }, - { - SPL_SUMMONUNDEAD, "summonundead", NULL, NULL, NULL, - M_CHAOS, (SPELLLEVEL | FARCASTING | ONSHIPCAST), - 5, 6, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summonundead, patzer_peasantmob - }, - { - SPL_COMBATRUST, "combatrust", NULL, NULL, NULL, - M_CHAOS, (COMBATSPELL | SPELLLEVEL), 5, 6, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_combatrosthauch, patzer - }, - { - SPL_TRANSFERAURA_CHAOS, "transferaurachaos", NULL, - "ZAUBERE \'Machtübertragung\' ", - "ui", - M_CHAOS, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 7, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_transferaura, patzer - }, - { - SPL_FIREWALL, "firewall", NULL, - "ZAUBERE \'Feuerwand\' ", - "c", - M_CHAOS, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 4, 7, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_firewall, patzer_peasantmob - }, - { - SPL_PLAGUE, "plague", NULL, NULL, NULL, - M_CHAOS, - (FARCASTING | REGIONSPELL | TESTRESISTANCE), - 5, 7, - { - { R_AURA, 30, SPC_FIX }, - { R_PEASANTS, 50, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_plague, patzer_peasantmob - }, - { - SPL_CHAOSROW, "chaosrow", NULL, NULL, NULL, - M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, - { - { R_AURA, 3, SPC_LEVEL }, - { R_PEASANTS, 10, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_chaosrow, patzer - }, - { - SPL_SUMMONSHADOW, "summonshadow", NULL, NULL, NULL, - M_CHAOS, (SPELLLEVEL), 5, 8, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summonshadow, patzer_peasantmob - }, - { - SPL_UNDEADHERO, "undeadhero", NULL, NULL, NULL, - M_CHAOS, (POSTCOMBATSPELL | SPELLLEVEL), 5, 9, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_undeadhero, patzer - }, - { - SPL_AURALEAK, "auraleak", NULL, NULL, NULL, - M_CHAOS, (REGIONSPELL | TESTRESISTANCE), 3, 9, - { - { R_AURA, 35, SPC_FIX }, - { R_DRACHENBLUT, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_auraleak, patzer - }, - { - SPL_DRAIG_FUMBLESHIELD, "draigfumbleshield", NULL, NULL, NULL, - M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 2, 9, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fumbleshield, patzer - }, - { - SPL_FOREST_FIRE, "forestfire", NULL, NULL, NULL, - M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 10, - { - { R_AURA, 50, SPC_FIX }, - { R_OIL, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_forest_fire, patzer_peasantmob - }, - { - SPL_DRAIG_DESTROY_MAGIC, "draigdestroymagic", NULL, - "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' REGION\n" - "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' SCHIFF ", - "kc?", - M_CHAOS, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 2, 10, - { - { R_AURA, 10, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_magic, patzer - }, - { - SPL_UNHOLYPOWER, "unholypower", NULL, NULL, - "u+", - M_CHAOS, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 5, 14, - { - { R_AURA, 10, SPC_LEVEL }, - { R_PEASANTS, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_unholypower, patzer - }, - { - SPL_DEATHCLOUD, "deathcloud", NULL, NULL, NULL, - M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, - { - { R_AURA, 40, SPC_FIX }, - { R_HITPOINTS, 15, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_deathcloud, patzer_peasantmob - }, - { - SPL_SUMMONDRAGON, "summondragon", NULL, NULL, NULL, - M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, - { - { R_AURA, 80, SPC_FIX }, - { R_DRAGONHEAD, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summondragon, patzer_peasantmob - }, - { - SPL_SUMMONSHADOWLORDS, "summonshadowlords", NULL, NULL, NULL, - M_CHAOS, (SPELLLEVEL), 5, 12, - { - { R_AURA, 7, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summonshadowlords, patzer_peasantmob - }, - { - SPL_DRAIG_FAMILIAR, "draigfamiliar", NULL, NULL, NULL, - M_CHAOS, (NOTFAMILIARCAST), 5, 13, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_familiar, patzer - }, - { - SPL_CHAOSSUCTION, "chaossuction", NULL, NULL, NULL, - M_CHAOS, (0), 5, 14, - { - { R_AURA, 150, SPC_FIX }, - { R_PEASANTS, 200, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_chaossuction, patzer_peasantmob - }, - /* M_TRAUM */ - { - SPL_SPARKLE_DREAM, "sparkledream", NULL, NULL, - "u", - M_TRAUM, - (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET | ONSHIPCAST), - 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_sparkle, patzer - }, - { - SPL_ILLAUN_EARN_SILVER, "illaunearnsilver", NULL, - NULL, - NULL, - M_TRAUM, (SPELLLEVEL|ONSHIPCAST), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earn_silver, patzer - }, - { - SPL_SHADOWKNIGHTS, "shadowknights", NULL, NULL, NULL, - M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 4, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_shadowknights, patzer - }, - { - SPL_FLEE, "flee", NULL, NULL, NULL, - M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 5, 2, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_flee, patzer - }, - { - SPL_PUTTOREST, "puttorest", NULL, NULL, NULL, - M_TRAUM, (SPELLLEVEL), 5, 2, - { - { R_AURA, 3, SPC_LEVEL }, - { R_TREES, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_puttorest, patzer - }, - { - SPL_ICASTLE, "icastle", NULL, - "ZAUBERE \"Traumschlößchen\" ", - "c", - M_TRAUM, (0), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_icastle, patzer - }, - { - SPL_TRANSFERAURA_TRAUM, "transferauratraum", NULL, - "ZAUBERE \'Traum der Magie\' ", - "ui", - M_TRAUM, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 3, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_transferaura, patzer - }, - { - SPL_ILL_SHAPESHIFT, "shapeshift", NULL, - "ZAUBERE [STUFE n] \'Gestaltwandlung\' ", - "uc", - M_TRAUM, (UNITSPELL|SPELLLEVEL|ONETARGET), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_illusionary_shapeshift, patzer - }, - { - SPL_DREAMREADING, "dreamreading", NULL, NULL, - "u", - M_TRAUM, (FARCASTING | UNITSPELL | ONETARGET | TESTRESISTANCE), 5, 4, - { - { R_AURA, 8, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dreamreading, patzer - }, - { - SPL_TIREDSOLDIERS, "tiredsoldiers", NULL, NULL, NULL, - M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_tiredsoldiers, patzer - }, - { - SPL_REANIMATE, "reanimate", NULL, NULL, NULL, - M_TRAUM, (POSTCOMBATSPELL | SPELLLEVEL), 4, 5, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_reanimate, patzer - }, - { - SPL_ANALYSEDREAM, "analysedream", NULL, NULL, - "u", - M_TRAUM, (UNITSPELL | ONSHIPCAST | ONETARGET | TESTCANSEE), 5, 5, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_analysedream, patzer - }, - { - SPL_DISTURBINGDREAMS, "disturbingdreams", NULL, NULL, NULL, - M_TRAUM, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 6, - { - { R_AURA, 18, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_disturbingdreams, patzer - }, - { - SPL_SLEEP, "sleep", NULL, NULL, NULL, - M_TRAUM, (COMBATSPELL | SPELLLEVEL ), 5, 7, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_sleep, patzer - }, - { - SPL_WISPS, "wisps", - "Der Zauberer spricht eine Beschwörung über einen Teil der Region, " - "und in der Folgewoche entstehen dort Irrlichter. " - "Wer durch diese Nebel wandert, wird von Visionen geplagt und " - "in die Irre geleitet.", - "ZAUBERE [REGION x y] [STUFE n] \'Irrlichter\' ", - "c", - M_TRAUM, (SPELLLEVEL | FARCASTING), 5, 7, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wisps, patzer - }, - { - SPL_READMIND, "readmind", NULL, NULL, - "u", - M_TRAUM, (UNITSPELL | ONETARGET), 5, 7, - { - { R_AURA, 20, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_readmind, patzer - }, - { - SPL_GOODDREAMS, "gooddreams", NULL, NULL, NULL, - M_TRAUM, - (FARCASTING | REGIONSPELL | TESTRESISTANCE), - 5, 8, - { - { R_AURA, 80, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_gooddreams, patzer - }, - { - SPL_ILLAUN_DESTROY_MAGIC, "illaundestroymagic", NULL, - "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' REGION\n" - "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' SCHIFF ", - "kc?", - M_TRAUM, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 2, 8, - { - { R_AURA, 6, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_magic, patzer - }, - { - SPL_ILLAUN_FAMILIAR, "illaunfamiliar", NULL, NULL, NULL, - M_TRAUM, (NOTFAMILIARCAST), 5, 9, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_familiar, patzer - }, - { - SPL_CLONECOPY, "clone", - "Dieser mächtige Zauber kann einen Magier vor dem sicheren Tod " - "bewahren. Der Magier erschafft anhand einer kleinen Blutprobe einen " - "Klon von sich, und legt diesen in ein Bad aus Drachenblut und verdünntem " - "Wasser des Lebens. " - "Anschließend transferiert er in einem aufwändigen Ritual einen Teil " - "seiner Seele in den Klon. Stirbt der Magier, reist seine Seele in den " - "Klon und der erschaffene Körper dient nun dem Magier als neues Gefäß. " - "Es besteht allerdings eine geringer Wahrscheinlichkeit, dass die Seele " - "nach dem Tod zu schwach ist, das neue Gefäß zu erreichen.", NULL, NULL, - M_TRAUM, (NOTFAMILIARCAST), 5, 9, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 20, SPC_FIX }, - { R_DRACHENBLUT, 5, SPC_FIX }, - { R_TREES, 5, SPC_FIX }, - { 0, 0, 0 } - }, - (spell_f)sp_clonecopy, patzer - }, - { - SPL_BADDREAMS, "bad_dreams", - "Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller nichtaliierten " - "Einheiten (HELFE BEWACHE) in der Region so stark zu stören, das sie " - "vorübergehend einen Teil ihrer Erinnerungen verlieren.", NULL, NULL, - M_TRAUM, - (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 10, - { - { R_AURA, 90, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_baddreams, patzer - }, - { - SPL_MINDBLAST, "mindblast", NULL, NULL, NULL, - M_TRAUM, (COMBATSPELL | SPELLLEVEL), 5, 11, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_mindblast, patzer - }, - { - SPL_ORKDREAM, "orkdream", NULL, NULL, - "u+", - M_TRAUM, - (UNITSPELL | TESTRESISTANCE | TESTCANSEE | SPELLLEVEL), 5, 12, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_sweetdreams, patzer - }, - { - SPL_SUMMON_ALP, "summon_alp", NULL, NULL, "u", - M_TRAUM, - (UNITSPELL | ONETARGET | SEARCHGLOBAL | TESTRESISTANCE), - 5, 15, - { - { R_AURA, 350, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { R_SWAMP_3, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_alp, patzer - }, - { - SPL_DREAM_OF_CONFUSION, "dream_of_confusion", NULL, NULL, NULL, - M_TRAUM, - (FARCASTING | SPELLLEVEL), - 5, 16, - { - { R_AURA, 7, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dream_of_confusion, patzer - }, - /* M_BARDE */ - { - SPL_DENYATTACK, "appeasement", NULL, NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL ), 5, 1, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_denyattack, patzer - }, - { - SPL_CERDDOR_EARN_SILVER, "jugglery", NULL, NULL, NULL, - M_BARDE, (SPELLLEVEL|ONSHIPCAST), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earn_silver, patzer - }, - { - SPL_HEALINGSONG, "song_of_healing", NULL, NULL, NULL, - M_BARDE, (POSTCOMBATSPELL | SPELLLEVEL), 5, 2, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_healing, patzer - }, - { - SPL_GENEROUS, "generous", NULL, NULL, NULL, - M_BARDE, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE), - 5, 2, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_generous, patzer - }, - { - SPL_RAINDANCE, "raindance", NULL, NULL, NULL, - M_BARDE, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL), - 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_blessedharvest, patzer - }, - { - SPL_SONG_OF_FEAR, "song_of_fear", NULL, NULL, NULL, - M_BARDE, (COMBATSPELL | SPELLLEVEL), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_flee, patzer - }, - { - SPL_RECRUIT, "courting", NULL, NULL, NULL, - M_BARDE, (SPELLLEVEL), 5, 4, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_recruit, patzer - }, - { - SPL_SONG_OF_CONFUSION, "song_of_confusion", NULL, NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_chaosrow, patzer - }, - { - SPL_BABBLER, "blabbermouth", NULL, NULL, "u", - M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 4, - { - { R_AURA, 10, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_babbler, patzer - }, - { - SPL_HERO, "heroic_song", NULL, NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 5, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_hero, patzer - }, - { - SPL_TRANSFERAURA_BARDE, "transfer_aura_song", NULL, NULL, - "ui", - M_BARDE, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 5, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_transferaura, patzer - }, - { - SPL_UNIT_ANALYSESONG, "analysesong_unit", NULL, NULL, - "u", - M_BARDE, - (UNITSPELL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 5, 5, - { - { R_AURA, 10, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_analysesong_unit, patzer - }, - { - SPL_CERRDOR_FUMBLESHIELD, "cerrdorfumbleshield", NULL, NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 5, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fumbleshield, patzer - }, - { SPL_CALM_MONSTER, "calm_monster", - "Dieser einschmeichelnde Gesang kann fast jedes intelligente Monster " - "zähmen. Es wird von Angriffen auf den Magier absehen und auch seine " - "Begleiter nicht anrühren. Doch sollte man sich nicht täuschen, es " - "wird dennoch ein unberechenbares Wesen bleiben.", NULL, - "u", - M_BARDE, - (UNITSPELL | ONSHIPCAST | ONETARGET | TESTRESISTANCE | TESTCANSEE), - 5, 6, - { - { R_AURA, 15, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_calm_monster, patzer - }, - { SPL_SEDUCE, "seduction", - "Mit diesem Lied kann eine Einheit derartig betört werden, so dass " - "sie dem Barden den größten Teil ihres Bargelds und ihres Besitzes " - "schenkt. Sie behält jedoch immer soviel, wie sie zum Überleben " - "braucht.", NULL, - "u", - M_BARDE, - (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), - 5, 6, - { - { R_AURA, 12, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_seduce, patzer - }, - { - SPL_HEADACHE, "headache", - "Aufzeichung des Vortrags von Selen Ard'Ragorn in Bar'Glingal: " - "'Es heiss, dieser Spruch wäre wohl in den Spelunken der Westgassen " - "entstanden, doch es kann genausogut in jedem andern verrufenen " - "Viertel gewesen sein. Seine wichtigste Zutat ist etwa ein Fass " - "schlechtesten Weines, je billiger und ungesunder, desto " - "wirkungsvoller wird die Essenz. Die Kunst, diesen Wein in pure " - "Essenz zu destillieren, die weitaus anspruchsvoller als das einfache " - "Rezeptmischen eines Alchemisten ist, und diese dergestalt zu binden " - "und konservieren, das sie sich nicht gleich wieder verflüchtigt, wie " - "es ihre Natur wäre, ja, dies ist etwas, das nur ein Meister des " - "Cerddor vollbringen kann. Nun besitzt Ihr eine kleine Phiola mit " - "einer rubinrotschimmernden - nun, nicht flüssig, doch auch nicht " - "ganz Dunst - nennen wir es einfach nur Elixier. Doch nicht dies ist " - "die wahre Herausforderung, sodann muss, da sich ihre Wirkung leicht " - "verflüchtigt, diese innerhalb weniger Tage unbemerkt in das Getränkt " - "des Opfers geträufelt werden. Ihr Meister der Betöhrung und " - "Verführung, hier nun könnt Ihr Eure ganze Kunst unter Beweis " - "stellen. Doch gebt Acht, nicht unbedacht selbst von dem Elixier zu " - "kosten, denn wer einmal gekostet hat, der kann vom Weine nicht mehr " - "lassen, und er säuft sicherlich eine volle Woche lang. Jedoch nicht " - "die Verführung zum Trunke ist die wahre Gefahr, die dem Elixier " - "innewohnt, sondern das der Trunkenheit so sicher ein gar " - "fürchterliches Leid des Kopfes folgen wird, wie der Tag auf die " - "Nacht folgt. Und er wird gar sicherlich von seiner besten Fähigkeit " - "einige Tage bis hin zu den Studien zweier Wochen vergessen haben. " - "Noch ein Wort der Warnung: Dieses ist sehr aufwendig, und so Ihr " - "noch weitere Zauber in der selben Woche wirken wollt, so werden sie Euch " - "schwerer fallen.'", NULL, - "u", - M_BARDE, - (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), - 5, 7, - { - { R_AURA, 4, SPC_LINEAR }, - { R_SWAMP_2, 3, SPC_FIX }, - { R_SILVER, 50, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_headache, patzer - }, - { SPL_PUMP, "sound_out", - "Erliegt die Einheit dem Zauber, so wird sie dem Magier alles erzählen, " - "was sie über die gefragte Region weiß. Ist in der Region niemand " - "ihrer Partei, so weiß sie nichts zu berichten. Auch kann sie nur das " - "erzählen, was sie selber sehen könnte.", - "ZAUBERE \'Aushorchen\' ", - "ur", - M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 7, - { - { R_AURA, 4, SPC_FIX }, - { R_SILVER, 100, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_pump, patzer - }, - { - SPL_BLOODTHIRST, "bloodthirst", - "Wie viele magischen Gesänge, so entstammt auch dieser den altem " - "Wissen der Katzen, die schon immer um die machtvolle Wirkung der " - "Stimme wussten. Mit diesem Lied wird die Stimmung der Krieger " - "aufgepeitscht, sie gar in wilde Raserrei und Blutrausch versetzt. " - "Ungeachtet eigener Schmerzen werden sie kämpfen bis zum " - "Tode und niemals fliehen. Während ihre Attacke verstärkt ist " - "achten sie kaum auf sich selbst.", NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 7, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_berserk, patzer - }, - { - SPL_FRIGHTEN, "frighten", - "Dieser Kriegsgesang sät Panik in der Front der Gegner und schwächt " - "so ihre Kampfkraft erheblich. Angst wird ihren Schwertarm schwächen " - "und Furcht ihren Schildarm lähmen.", NULL, NULL, - M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_frighten, patzer - }, - { - SPL_OBJ_ANALYSESONG, "analyse_object", - "Wie Lebewesen, so haben auch Schiffe und Gebäude und sogar Regionen " - "ihr eigenes Lied, wenn auch viel schwächer und schwerer zu hören. " - "Und so, wie wie aus dem Lebenslied einer Person erkannt werden kann, " - "ob diese unter einem Zauber steht, so ist dies auch bei Burgen, " - "Schiffen oder Regionen möglich.", - "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' REGION\n" - "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' GEBÄUDE \n" - "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' SCHIFF ", - "kc?", - M_BARDE, (SPELLLEVEL|ONSHIPCAST), 5, 8, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_analysesong_obj, patzer - }, - { - SPL_CERDDOR_DESTROY_MAGIC, "cerddor_destroymagic", - "Jede Verzauberung beeinflußt das Lebenslied, schwächt und verzerrt es. " - "Der kundige Barde kann versuchen, das Lebenslied aufzufangen und zu " - "verstärken und die Veränderungen aus dem Lied zu tilgen.", - "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' REGION\n" - "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' SCHIFF ", - "kc?", - M_BARDE, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 2, 8, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_magic, patzer - }, - { - SPL_MIGRANT, "migration", - "Dieses Ritual ermöglicht es, eine Einheit, egal welcher Art, in die " - "eigene Partei aufzunehmen. Der um Aufnahme Bittende muss dazu willig " - "und bereit sein, seiner alten Partei abzuschwören. Dies bezeugt er " - "durch KONTAKTIEREn des Magiers. Auch wird er die Woche über " - "ausschliesslich mit Vorbereitungen auf das Ritual beschäftigt sein. " - "Das Ritual wird fehlschlagen, wenn er zu stark an seine alte Partei " - "gebunden ist, dieser etwa Dienst für seine teuere Ausbildung " - "schuldet. Der das Ritual leitende Magier muss für die permanente " - "Bindung des Aufnahmewilligen an seine Partei naturgemäß auch " - "permanente Aura aufwenden. Pro Stufe und pro 1 permanente Aura kann " - "er eine Person aufnehmen.", NULL, - "u", - M_BARDE, (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE), 5, 9, - { - { R_AURA, 3, SPC_LEVEL }, - { R_PERMAURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_migranten, patzer - }, - { - SPL_CERDDOR_FAMILIAR, "summon_familiar", - "Einem erfahrenen Magier wird irgendwann auf seinen Wanderungen ein " - "ungewöhnliches Exemplar einer Gattung begegnen, welches sich dem " - "Magier anschließen wird.", NULL, NULL, - M_BARDE, (NOTFAMILIARCAST), 5, 9, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_familiar, patzer - }, - { - SPL_RAISEPEASANTS, "raise_mob", - "Mit Hilfe dieses magischen Gesangs überzeugt der Magier die Bauern " - "der Region, sich ihm anzuschließen. Die Bauern werden ihre Heimat jedoch " - "nicht verlassen, und keine ihrer Besitztümer fortgeben. Jede Woche " - "werden zudem einige der Bauern den Bann abwerfen und auf ihre Felder " - "zurückkehren. Wie viele Bauern sich dem Magier anschließen hängt von der " - "Kraft seines Gesangs ab.", NULL, NULL, - M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 10, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_raisepeasants, patzer - }, - { - SPL_SONG_RESISTMAGIC, "song_resist_magic", - "Dieses magische Lied wird, einmal mit Inbrunst gesungen, sich in der " - "Region fortpflanzen, von Mund zu Mund springen und eine Zeitlang " - "überall zu vernehmen sein. Nach wie vielen Wochen der Gesang aus dem " - "Gedächnis der Region entschwunden ist, ist von dem Geschick des Barden " - "abhängig. Bis das Lied ganz verklungen ist, wird seine Magie allen " - "Verbündeten des Barden (HELFE BEWACHE), und natürlich auch seinen " - "eigenem Volk, einen einmaligen Bonus von 15% " - "auf die natürliche Widerstandskraft gegen eine Verzauberung " - "verleihen.", NULL, NULL, - M_BARDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 2, 10, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_song_resistmagic, patzer - }, - { - SPL_DEPRESSION, "melancholy", - "Mit diesem Gesang verbreitet der Barde eine melancholische, traurige " - "Stimmung unter den Bauern. Einige Wochen lang werden sie sich in ihre " - "Hütten zurückziehen und kein Silber in den Theatern und Tavernen lassen.", NULL, NULL, - M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, - { - { R_AURA, 40, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_depression, patzer - }, - { - SPL_SONG_SUSCEPTMAGIC, "song_suscept_magic", - "Dieses Lied, das in die magische Essenz der Region gewoben wird, " - "schwächt die natürliche Widerstandskraft gegen eine " - "Verzauberung einmalig um 15%. Nur die Verbündeten des Barden " - "(HELFE BEWACHE) sind gegen die Wirkung des Gesangs gefeit.", NULL, NULL, - M_BARDE, - (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 2, 12, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_song_susceptmagic, patzer - }, - { - SPL_SONG_OF_PEACE, "song_of_peace", - "Dieser mächtige Bann verhindert jegliche Attacken. Niemand in der " - "ganzen Region ist fähig seine Waffe gegen irgendjemanden zu erheben. " - "Die Wirkung kann etliche Wochen andauern", NULL, NULL, - M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 12, - { - { R_AURA, 20, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_song_of_peace, patzer - }, - { - SPL_SONG_OF_ENSLAVE, "song_of_slavery", - "Dieser mächtige Bann raubt dem Opfer seinen freien Willen und " - "unterwirft sie den Befehlen des Barden. Für einige Zeit wird das Opfer " - "sich völlig von seinen eigenen Leuten abwenden und der Partei des Barden " - "zugehörig fühlen.", NULL, - "u", - M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 13, - { - { R_AURA, 40, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_charmingsong, patzer - }, - { - SPL_BIGRECRUIT, "big_recruit", - "Aus 'Wanderungen' von Firudin dem Weisen: " - "'In Weilersweide, nahe dem Wytharhafen, liegt ein kleiner Gasthof, der " - "nur wenig besucht ist. Niemanden bekannt ist, das dieser Hof " - "bis vor einigen Jahren die Bleibe des verbannten Wanderpredigers Grauwolf " - "war. Nachdem er bei einer seiner berüchtigten flammenden Reden fast die " - "gesammte Bauernschaft angeworben hatte, wurde er wegen Aufruhr verurteilt " - "und verbannt. Nur zögerlich war er bereit mir das Geheimniss seiner " - "Überzeugungskraft zu lehren.'", NULL, NULL, - M_BARDE, (SPELLLEVEL), 5, 14, - { - { R_AURA, 20, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_bigrecruit, patzer - }, - { - SPL_RALLYPEASANTMOB, "calm_riot", - "Mit Hilfe dieses magischen Gesangs kann der Magier eine Region in " - "Aufruhr wieder beruhigen. Die Bauernhorden werden sich verlaufen " - "und wieder auf ihre Felder zurückkehren.", NULL, NULL, - M_BARDE, (FARCASTING), 5, 15, - { - { R_AURA, 30, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_rallypeasantmob, patzer - }, - { - SPL_RAISEPEASANTMOB, "incite_riot", - "Mit Hilfe dieses magischen Gesangs versetzt der Magier eine ganze " - "Region in Aufruhr. Rebellierende Bauernhorden machen jedes Besteuern " - "unmöglich, kaum jemand wird mehr für Gaukeleien Geld spenden und " - "es können keine neuen Leute angeworben werden. Nach einigen Wochen " - "beruhigt sich der Mob wieder.", NULL, NULL, - M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 16, - { - { R_AURA, 40, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_raisepeasantmob, patzer - }, - /* M_ASTRAL */ - { - SPL_ANALYSEMAGIC, "analyze_magic", NULL, - "ZAUBERE [STUFE n] \'Magie analysieren\' REGION\n" - "ZAUBERE [STUFE n] \'Magie analysieren\' EINHEIT \n" - "ZAUBERE [STUFE n] \'Magie analysieren\' GEBÄUDE \n" - "ZAUBERE [STUFE n] \'Magie analysieren\' SCHIFF ", - "kc?", - M_ASTRAL, (SPELLLEVEL | UNITSPELL | ONSHIPCAST | TESTCANSEE), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_analysemagic, patzer - }, - { - SPL_ITEMCLOAK, "concealing_aura", NULL, NULL, - "u", - M_ASTRAL, (SPELLLEVEL | UNITSPELL | ONSHIPCAST | ONETARGET), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_itemcloak, patzer - }, - { - SPL_TYBIED_EARN_SILVER, "miracle_doctor", NULL, NULL, NULL, - M_ASTRAL, (SPELLLEVEL|ONSHIPCAST), 5, 1, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_earn_silver, patzer - }, - { - SPL_TYBIED_FUMBLESHIELD, "tybiedfumbleshield", NULL, NULL, NULL, - M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 2, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fumbleshield, patzer - }, -#ifdef SHOWASTRAL_NOT_BORKED - { - SPL_SHOWASTRAL, "show_astral", - "Der Magier kann kurzzeitig in die Astralebene blicken und erfährt " - "so alle Einheiten innerhalb eines astralen Radius von Stufe/5 Regionen.", NULL, NULL, - M_ASTRAL, (SPELLLEVEL), 5, 2, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_showastral, patzer - }, -#endif - { - SPL_RESISTMAGICBONUS, "resist_magic", NULL, NULL, "u+", - M_ASTRAL, - (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), - 2, 3, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_resist_magic_bonus, patzer - }, - { - SPL_KEEPLOOT, "keeploot", NULL, NULL, NULL, - M_ASTRAL, ( POSTCOMBATSPELL | SPELLLEVEL ), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_keeploot, patzer - }, - { - SPL_ENTERASTRAL, "enterastral", 0, 0, "u+", - M_ASTRAL, (UNITSPELL|SPELLLEVEL), 7, 4, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_enterastral, patzer - }, - { - SPL_LEAVEASTRAL, "leaveastral", 0, - "ZAUBER [STUFE n] \'Astraler Ausgang\' " - "[ ...]", - "ru+", - M_ASTRAL, (UNITSPELL |SPELLLEVEL), 7, 4, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_leaveastral, patzer - }, - { - SPL_TRANSFERAURA_ASTRAL, "auratransfer", - "Mit Hilfe dieses Zauber kann der Magier eigene Aura im Verhältnis " - "2:1 auf einen anderen Magier des gleichen Magiegebietes oder im " - "Verhältnis 3:1 auf einen Magier eines anderen Magiegebietes " - "übertragen.", - "ZAUBERE \'Auratransfer\' ", - "ui", - M_ASTRAL, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 5, - { - { R_AURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_transferaura, patzer - }, - { - SPL_SHOCKWAVE, "shockwave", NULL, NULL, NULL, - M_ASTRAL, (COMBATSPELL|SPELLLEVEL), 5, 5, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_stun, patzer - }, - { - SPL_ANTIMAGICZONE, "antimagiczone", NULL, NULL, NULL, - M_ASTRAL, (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), - 2, 5, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_antimagiczone, patzer - }, - { - SPL_TYBIED_DESTROY_MAGIC, "destroy_magic", NULL, - "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' REGION\n" - "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' SCHIFF ", - "kc?", - M_ASTRAL, - (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), - 2, 5, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_magic, patzer - }, - { - SPL_PULLASTRAL, "pull_astral", NULL, - "ZAUBER [STUFE n] \'Astraler Ruf\' " - "[ ...]", - "ru+", - M_ASTRAL, (UNITSPELL | SEARCHGLOBAL | SPELLLEVEL), 7, 6, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_pullastral, patzer - }, - - { - SPL_FETCHASTRAL, "fetch_astral", NULL, NULL, "u+", - M_ASTRAL, (UNITSPELL | SEARCHGLOBAL | SPELLLEVEL), 7, 6, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_fetchastral, patzer - }, - { - SPL_STEALAURA, "steal_aura", NULL, NULL, "u", - M_ASTRAL, - (FARCASTING | SPELLLEVEL | UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), - 3, 6, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_stealaura, patzer - }, - { - SPL_FLYING_SHIP, "airship", NULL, NULL, "s", - M_ASTRAL, (ONSHIPCAST | SHIPSPELL | ONETARGET | TESTRESISTANCE), 5, 6, - { - { R_AURA, 10, SPC_FIX }, - { R_HIGHLAND_1, 1, SPC_FIX }, - { R_GLACIER_3, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_flying_ship, patzer - }, - { - SPL_DESTROY_MAGIC, "break_curse", NULL, - "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' REGION \n" - "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' EINHEIT \n" - "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' GEBÄUDE \n" - "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' SCHIFF ", - "kcc", - M_ASTRAL, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), 3, 7, - { - { R_AURA, 3, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_destroy_curse, patzer - }, - { - SPL_ETERNIZEWALL, "eternal_walls", NULL, - "ZAUBERE \'Mauern der Ewigkeit\' ", - "b", - M_ASTRAL, - (SPELLLEVEL | BUILDINGSPELL | ONETARGET | TESTRESISTANCE | ONSHIPCAST), - 5, 7, - { - { R_AURA, 50, SPC_FIX }, - { R_PERMAURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_eternizewall, patzer - }, - { - SPL_SCHILDRUNEN, "protective_runes", - "Zeichnet man diese Runen auf die Wände eines Gebäudes oder auf die " - "Planken eines Schiffes, so wird es schwerer durch Zauber zu " - "beeinflussen sein. Jedes Ritual erhöht die Widerstandskraft des " - "Gebäudes oder Schiffes gegen Verzauberung um 20%. " - "Werden mehrere Schutzzauber übereinander gelegt, so addiert " - "sich ihre Wirkung, doch ein hundertprozentiger Schutz läßt sich so " - "nicht erreichen. Der Zauber hält mindestens drei Wochen an, je nach " - "Talent des Magiers aber auch viel länger.", - "ZAUBERE \'Runen des Schutzes\' GEBÄUDE | " - "SCHIFF ]", - "kc", - M_ASTRAL, (ONSHIPCAST | TESTRESISTANCE), 2, 8, - { - { R_AURA, 20, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_magicrunes, patzer - }, - - { - SPL_REDUCESHIELD, "fish_shield", - "Dieser Zauber vermag dem Gegner ein geringfügig versetztes Bild der " - "eigenen Truppen vorzuspiegeln, so wie der Fisch im Wasser auch nicht " - "dort ist wo er zu sein scheint. Von jedem Treffer kann so die Hälfte " - "des Schadens unschädlich abgeleitet werden. Doch hält der Schild nur " - "einige Hundert Schwerthiebe aus, danach wird er sich auflösen. " - "Je stärker der Magier, desto mehr Schaden hält der Schild aus.", NULL, NULL, - M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 8, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_reduceshield, patzer - }, - { - SPL_SPEED, "combat_speed", - "Dieser Zauber beschleunigt einige Kämpfer auf der eigenen Seite " - "so, dass sie während des gesamten Kampfes in einer Kampfrunde zweimal " - "angreifen können.", NULL, NULL, - M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 5, 9, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_speed, patzer - }, - { - SPL_VIEWREALITY, "view_reality", - "Der Magier kann mit Hilfe dieses Zaubers aus der Astral- in die " - "materielle Ebene blicken und die Regionen und Einheiten genau " - "erkennen.", NULL, NULL, - M_ASTRAL, (0), 5, 10, - { - { R_AURA, 40, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_viewreality, patzer - }, - { - SPL_SPEED2, "double_time", - "Diese praktische Anwendung des theoretischen Wissens um Raum und Zeit " - "ermöglicht es, den Zeitfluß für einige Personen zu verändern. Auf " - "diese Weise veränderte Personen bekommen für einige Wochen doppelt " - "soviele Bewegungspunkte und doppelt soviele Angriffe pro Runde.", NULL, - "u+", - M_ASTRAL, (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), 5, 11, - { - { R_AURA, 5, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_speed2, patzer - }, - { - SPL_ARMORSHIELD, "armor_shield", - "Diese vor dem Kampf zu zaubernde Ritual gibt den eigenen Truppen " - "einen zusätzlichen Bonus auf ihre Rüstung. Jeder Treffer " - "reduziert die Kraft des Zaubers, so dass der Schild sich irgendwann " - "im Kampf auflösen wird.", NULL, NULL, - M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 12, - { - { R_AURA, 4, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_armorshield, patzer - }, - { - SPL_TYBIED_FAMILIAR, "summon_familiar", - "Einem erfahrenen Magier wird irgendwann auf seinen Wanderungen ein " - "ungewöhnliches Exemplar einer Gattung begegnen, welches sich dem " - "Magier anschließen wird.", NULL, NULL, - M_ASTRAL, (NOTFAMILIARCAST), 5, 12, - { - { R_AURA, 100, SPC_FIX }, - { R_PERMAURA, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_summon_familiar, patzer - }, - { - SPL_MOVECASTLE, "living_rock", - "Dieses kräftezehrende Ritual beschwört mit Hilfe einer Kugel aus " - "konzentriertem Laen einen gewaltigen Erdelementar und bannt ihn " - "in ein Gebäude. Dem Elementar kann dann befohlen werden, das " - "Gebäude mitsamt aller Bewohner in eine Nachbarregion zu tragen. " - "Die Stärke des beschworenen Elementars hängt vom Talent des " - "Magiers ab: Der Elementar kann maximal [Stufe-12]*250 Größeneinheiten " - "große Gebäude versetzen. Das Gebäude wird diese Prozedur nicht " - "unbeschädigt überstehen.", - "ZAUBER [STUFE n] \'Belebtes Gestein\' ", - "bc", - M_ASTRAL, - (SPELLLEVEL | BUILDINGSPELL | ONETARGET | TESTRESISTANCE), - 5, 13, - { - { R_AURA, 10, SPC_LEVEL }, - { R_PERMAURA, 1, SPC_FIX }, - { R_EOG, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_movecastle, patzer - }, - { - SPL_DISRUPTASTRAL, "astral_disruption", - "Dieser Zauber bewirkt eine schwere Störung des Astralraums. Innerhalb " - "eines astralen Radius von Stufe/5 Regionen werden alle Astralwesen, " - "die dem Zauber nicht wiederstehen können, aus der astralen Ebene " - "geschleudert. Der astrale Kontakt mit allen betroffenen Regionen ist " - "für Stufe/3 Wochen gestört.", NULL, NULL, - M_ASTRAL, (REGIONSPELL), 4, 14, - { - { R_AURA, 140, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_disruptastral, patzer - }, - { - SPL_PERMTRANSFER, "sacrifice_strength", - "Mit Hilfe dieses Zaubers kann der Magier einen Teil seiner magischen " - "Kraft permanent auf einen anderen Magier übertragen. Auf einen Tybied-" - "Magier kann er die Hälfte der eingesetzten Kraft übertragen, auf einen " - "Magier eines anderen Gebietes ein Drittel.", - "ZAUBERE \'Opfere Kraft\' ", - "ui", - M_ASTRAL, (UNITSPELL|ONETARGET), 1, 15, - { - { R_AURA, 100, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_permtransfer, patzer - }, - /* M_GRAU */ - /* Definitionen von Create_Artefaktsprüchen */ - { - SPL_METEORRAIN, "meteor_rain", - "Ein Schauer von Meteoren regnet über das Schlachtfeld.", NULL, NULL, - M_GRAU, (COMBATSPELL | SPELLLEVEL), 5, 3, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_kampfzauber, patzer - }, - { - SPL_BECOMEWYRM, "wyrm_transformation", - "Mit Hilfe dieses Zaubers kann sich der Magier permanent in einen " - "mächtigen Wyrm verwandeln. Der Magier behält seine Talente und " - "Möglichkeiten, bekommt jedoch die Kampf- und Bewegungseigenschaften " - "eines Wyrms. Der Odem des Wyrms wird sich mit steigendem Magie-Talent " - "verbessern. Der Zauber ist sehr kraftraubend und der Wyrm wird einige " - "Zeit brauchen, um sich zu erholen.", NULL, NULL, - M_GRAU, 0, 5, 1, - { - { R_AURA, 1, SPC_FIX }, - { R_PERMAURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_becomewyrm, patzer - }, - /* Monstersprüche */ - { SPL_FIREDRAGONODEM, "fiery_dragonbreath", - "Verbrennt die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 3, - { - { R_AURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dragonodem, patzer - }, - { SPL_DRAGONODEM, "icy_dragonbreath", - "Tötet die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 6, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dragonodem, patzer - }, - { SPL_WYRMODEM, "powerful_dragonbreath", - "Verbrennt die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 12, - { - { R_AURA, 3, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dragonodem, patzer - }, - { SPL_DRAINODEM, "drain_skills", - "Entzieht Talentstufen und macht Schaden wie Großer Odem", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 12, - { - { R_AURA, 4, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_dragonodem, patzer - }, - { - SPL_AURA_OF_FEAR, "aura_of_fear", - "Panik", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 12, - { - { R_AURA, 1, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_flee, patzer - }, - { - SPL_SHADOWCALL, "shadowcall", - "Ruft Schattenwesen.", NULL, NULL, - M_GRAU, (PRECOMBATSPELL), 5, 12, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_shadowcall, patzer - }, - { - SPL_IMMOLATION, "immolation", - "Verletzt alle Gegner.", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 12, - { - { R_AURA, 2, SPC_LEVEL }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_immolation, patzer - }, - { SPL_FIREODEM, "firestorm", - "Tötet die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 8, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_immolation, patzer - }, - { SPL_ICEODEM, "coldfront", - "Tötet die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 8, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_immolation, patzer - }, - { SPL_ACIDODEM, "acidrain", - "Tötet die Feinde", NULL, NULL, - M_GRAU, (COMBATSPELL), 5, 8, - { - { R_AURA, 2, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_immolation, patzer - }, - #ifdef WDW_PYRAMIDSPELL - { - SPL_WDWPYRAMID_TRAUM, "wdwpyramid_illaun", - "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " - "Region für den Pyramidenbau geeignet ist.", NULL, NULL, - M_TRAUM, (0), 5, 4, - { - { R_AURA, 2, SPC_FIX }, - { R_PLAIN_3, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wdwpyramid, patzer - }, - { - SPL_WDWPYRAMID_ASTRAL, "wdwpyramid_tybied", - "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " - "Region für den Pyramidenbau geeignet ist.", NULL, NULL, - M_ASTRAL, (0), 5, 3, - { - { R_AURA, 4, SPC_FIX }, - { R_WISE, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wdwpyramid, patzer - }, - - { - SPL_WDWPYRAMID_DRUIDE, "wdwpyramid_gwyrrd", - "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " - "Region für den Pyramidenbau geeignet ist.", NULL, NULL, - M_DRUIDE, (0), 5, 5, - { - { R_AURA, 3, SPC_FIX }, - { R_MALLORN, 5, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wdwpyramid, patzer - }, - { - SPL_WDWPYRAMID_BARDE, "wdwpyramid_cerrdor", - "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " - "Region für den Pyramidenbau geeignet ist.", NULL, NULL, - M_BARDE, (0), 5, 4, - { - { R_AURA, 2, SPC_FIX }, - { R_HIGHLAND_3, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wdwpyramid, patzer - }, - { - SPL_WDWPYRAMID_CHAOS, "wdwpyramid_draig", - "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " - "Region für den Pyramidenbau geeignet ist.", NULL, NULL, - M_CHAOS, (0), 5, 5, - { - { R_AURA, 1, SPC_FIX }, - { R_PERMAURA, 1, SPC_FIX }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - (spell_f)sp_wdwpyramid, patzer - }, - #endif - /* SPL_NOSPELL MUSS der letzte Spruch der Liste sein*/ - { - SPL_NOSPELL, "no spell", NULL, NULL, NULL, 0, 0, 0, 0, - { - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 } - }, - NULL, NULL - } -}; - -void -init_spells(void) -{ - int i; - - /* register all the old spells in the spelldata array */ - for (i=0;spelldaten[i].id!=SPL_NOSPELL;++i) { - register_spell(spelldaten+i); - } - at_register(&at_cursewall); - at_register(&at_unitdissolve); -#ifdef WDW_PYRAMIDSPELL - at_register(&at_wdwpyramid); -#endif - register_bordertype(&bt_firewall); - register_bordertype(&bt_wisps); - register_bordertype(&bt_chaosgate); -} - -static boolean -chaosgate_valid(const border * b) -{ - const attrib * a = a_findc(b->from->attribs, &at_direction); - if (!a) a = a_findc(b->to->attribs, &at_direction); - if (!a) return false; - return true; -} - -struct region * -chaosgate_move(const border * b, struct unit * u, struct region * from, struct region * to, boolean routing) -{ - if (!routing) { - int maxhp = u->hp / 4; - if (maxhpnumber) maxhp = u->number; - u->hp = maxhp; - } - return to; -} - -border_type bt_chaosgate = { - "chaosgate", VAR_NONE, - b_transparent, /* transparent */ - NULL, /* init */ - NULL, /* destroy */ - NULL, /* read */ - NULL, /* write */ - b_blocknone, /* block */ - NULL, /* name */ - b_rinvisible, /* rvisible */ - b_finvisible, /* fvisible */ - b_uinvisible, /* uvisible */ - chaosgate_valid, - chaosgate_move -}; - diff --git a/src/common/kernel/spell.h b/src/common/kernel/spell.h index eb8a7cfa1..20a68fffd 100644 --- a/src/common/kernel/spell.h +++ b/src/common/kernel/spell.h @@ -12,8 +12,8 @@ * prior permission by the authors of Eressea. */ -#ifndef H_KRNL_SPELLS -#define H_KRNL_SPELLS +#ifndef H_KRNL_SPELL +#define H_KRNL_SPELL #ifdef __cplusplus extern "C" { #endif @@ -26,201 +26,6 @@ extern "C" { struct castorder; struct curse; - /* Sprüche. Neue NUR hinten anfügen, oder das Datenfile geht kaputt */ - enum { - SPL_NOSPELL, - LUASPL_ARTEFAKT_OF_POWER, - LUASPL_ARTEFAKT_OF_AURAPOWER, - LUASPL_ARTEFAKT_OF_REGENERATION, - SPL_FIREBALL, - SPL_HAGEL, - SPL_RUSTWEAPON, - SPL_COMBATRUST, - SPL_TREEGROW, - SPL_HEALING, - SPL_HEALINGSONG, - SPL_BADDREAMS, - SPL_GOODDREAMS, - SPL_DREAMREADING, - SPL_SWEETDREAMS, - SPL_TIREDSOLDIERS, - SPL_PLAGUE, - SPL_MAGICBOOST, - SPL_CHAOSROW, - SPL_SONG_OF_CONFUSION, - SPL_FLEE, - SPL_SONG_OF_FEAR, - SPL_BERSERK, - SPL_BLOODTHIRST, - SPL_MAELSTROM, - SPL_BLESSEDHARVEST, - SPL_RAINDANCE, - SPL_TRANSFERAURA_DRUIDE, - SPL_TRANSFERAURA_BARDE, - SPL_TRANSFERAURA_CHAOS, - SPL_TRANSFERAURA_TRAUM, - SPL_TRANSFERAURA_ASTRAL, - SPL_STONEGOLEM, - SPL_IRONGOLEM, - SPL_SUMMONSHADOW, - SPL_SUMMONSHADOWLORDS, - SPL_REELING_ARROWS, - SPL_ANTIMAGICZONE, - LUASPL_CREATE_ANTIMAGICCRYSTAL, - SPL_KAELTESCHUTZ, - SPL_STEALAURA, - SPL_SUMMONUNDEAD, - SPL_AURALEAK, - SPL_GREAT_DROUGHT, - SPL_STRONG_WALL, - SPL_HOMESTONE, - SPL_DROUGHT, - SPL_FOREST_FIRE, - LUASPL_STRENGTH, - SPL_SUMMONENT, - SPL_DISTURBINGDREAMS, - SPL_DENYATTACK, - SPL_SLEEP, - SPL_EARTHQUAKE, - SPL_IRONKEEPER, - SPL_STORMWINDS, - SPL_GOODWINDS, - SPL_FLYING_SHIP, - SPL_SUMMON_ALP, - SPL_WINDSHIELD, - SPL_RAISEPEASANTS, - SPL_DEPRESSION, - SPL_HEADACHE, - LUASPL_ARTEFAKT_NIMBLEFINGERRING, - SPL_ENTERASTRAL, - SPL_LEAVEASTRAL, - SPL_SHOWASTRAL, - SPL_VERSTEINERN, - SPL_TREEWALKENTER, - SPL_TREEWALKEXIT, - SPL_CHAOSSUCTION, - SPL_VIEWREALITY, - SPL_DISRUPTASTRAL, - SPL_SEDUCE, - SPL_PUMP, - SPL_CALM_MONSTER, - SPL_HERO, - SPL_FRIGHTEN, - SPL_MINDBLAST, - SPL_SPEED, - SPL_SPEED2, - SPL_FIREDRAGONODEM, - SPL_DRAGONODEM, - SPL_WYRMODEM, /* 83 */ - SPL_MAGICSTREET, - SPL_REANIMATE, - SPL_RECRUIT, - SPL_GENEROUS, - SPL_PERMTRANSFER, - SPL_SONG_OF_PEACE, - SPL_MIGRANT, - SPL_RALLYPEASANTMOB, - SPL_RAISEPEASANTMOB, - SPL_ILL_SHAPESHIFT, - SPL_WOLFHOWL, - SPL_FOG_OF_CONFUSION, - SPL_DREAM_OF_CONFUSION, - SPL_RESISTMAGICBONUS, - SPL_KEEPLOOT, - SPL_SCHILDRUNEN, - SPL_SONG_RESISTMAGIC, - SPL_SONG_SUSCEPTMAGIC, - SPL_ANALYSEMAGIC, - SPL_ANALYSEDREAM, - SPL_UNIT_ANALYSESONG, - SPL_OBJ_ANALYSESONG, - SPL_TYBIED_DESTROY_MAGIC, - SPL_DESTROY_MAGIC, - SPL_METEORRAIN, - SPL_REDUCESHIELD, - SPL_ARMORSHIELD, - SPL_DEATHCLOUD, - SPL_ORKDREAM, - SPL_SUMMONDRAGON, - SPL_READMIND, - SPL_BABBLER, - SPL_MOVECASTLE, - SPL_BLESSSTONECIRCLE, - SPL_ILLAUN_FAMILIAR, - SPL_GWYRRD_FAMILIAR, - SPL_DRAIG_FAMILIAR, - SPL_CERDDOR_FAMILIAR, - SPL_TYBIED_FAMILIAR, - SPL_SONG_OF_ENSLAVE, - LUASPL_TRUESEEING_GWYRRD, - LUASPL_TRUESEEING_DRAIG, - LUASPL_TRUESEEING_ILLAUN, - LUASPL_TRUESEEING_CERDDOR, - LUASPL_TRUESEEING_TYBIED, - LUASPL_INVISIBILITY_GWYRRD, - LUASPL_INVISIBILITY_DRAIG, - LUASPL_INVISIBILITY_ILLAUN, - LUASPL_INVISIBILITY_CERDDOR, - LUASPL_INVISIBILITY_TYBIED, - LUASPL_ARTEFAKT_CHASTITYBELT, - LUASPL_ARTEFAKT_RUNESWORD, - SPL_FUMBLECURSE, - SPL_ICASTLE, - SPL_GWYRRD_DESTROY_MAGIC, - SPL_DRAIG_DESTROY_MAGIC, - SPL_ILLAUN_DESTROY_MAGIC, - SPL_CERDDOR_DESTROY_MAGIC, - SPL_GWYRRD_ARMORSHIELD, - SPL_DRAIG_FUMBLESHIELD, - SPL_GWYRRD_FUMBLESHIELD, - SPL_CERRDOR_FUMBLESHIELD, - SPL_TYBIED_FUMBLESHIELD, - SPL_SHADOWKNIGHTS, - LUASPL_FIRESWORD, - LUASPL_CREATE_TACTICCRYSTAL, - SPL_ITEMCLOAK, - SPL_FIREWALL, - SPL_WISPS, - SPL_SPARKLE_CHAOS, - SPL_SPARKLE_DREAM, - LUASPL_BAG_OF_HOLDING, - SPL_PULLASTRAL, - SPL_FETCHASTRAL, - SPL_ILLAUN_EARN_SILVER, - SPL_GWYRRD_EARN_SILVER, - SPL_DRAIG_EARN_SILVER, - SPL_TYBIED_EARN_SILVER, - SPL_CERDDOR_EARN_SILVER, - SPL_SHOCKWAVE, - SPL_UNDEADHERO, - LUASPL_ARTEFAKT_SACK_OF_CONSERVATION, - SPL_BECOMEWYRM, - SPL_ETERNIZEWALL, - SPL_PUTTOREST, - SPL_UNHOLYPOWER, - SPL_HOLYGROUND, - SPL_BLOODSACRIFICE, - SPL_MALLORN, - SPL_CLONECOPY, - SPL_DRAINODEM, /* 174? */ - SPL_AURA_OF_FEAR, /* 175? */ - SPL_SHADOWCALL, /* 176? */ - SPL_MALLORNTREEGROW, - LUASPL_INVISIBILITY2_ILLAUN, - SPL_BIGRECRUIT, - SPL_IMMOLATION, - SPL_FIREODEM, /* 181 */ - SPL_ICEODEM, - SPL_ACIDODEM, -#ifdef WDW_PYRAMIDSPELL - SPL_WDWPYRAMID_TRAUM, - SPL_WDWPYRAMID_ASTRAL, - SPL_WDWPYRAMID_DRUIDE, - SPL_WDWPYRAMID_BARDE, - SPL_WDWPYRAMID_CHAOS, -#endif - }; - /* Prototypen */ void do_shock(struct unit *u, const char *reason); @@ -230,55 +35,8 @@ extern "C" { int sp_antimagiczone(struct castorder *co); extern double destr_curse(struct curse* c, int cast_level, double force); - - - /* Kampfzauber */ - extern int sp_fumbleshield(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_shadowknights(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_combatrosthauch(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_kampfzauber(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_healing(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_keeploot(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_reanimate(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_chaosrow(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_flee(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_berserk(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_tiredsoldiers(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_reeling_arrows(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_denyattack(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_sleep(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_windshield(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_strong_wall(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_versteinern(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_hero(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_frighten(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_mindblast(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_speed(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_wolfhowl(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_dragonodem(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_reduceshield(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_armorshield(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_stun(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_undeadhero(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_shadowcall(struct fighter * fi, int level, double power, struct spell * sp); - extern int sp_immolation(struct fighter * fi, int level, double power, struct spell * sp); - /* ------------------------------------------------------------- */ - /* für Feuerwände: in movement muß das noch explizit getestet werden. - * besser wäre eine blcok_type::move() routine, die den effekt - * der Bewegung auf eine struct unit anwendet. - */ - extern struct border_type bt_chaosgate; - extern struct border_type bt_firewall; - extern struct border_type bt_wisps; - typedef struct wall_data { - struct unit * mage; - int force; - boolean active; - } wall_data; - - extern struct attrib_type at_cursewall; extern struct attrib_type at_unitdissolve; #ifdef WDW_PYRAMIDSPELL extern struct attrib_type at_wdwpyramid; @@ -288,9 +46,115 @@ extern "C" { extern void init_spells(void); extern void register_spell(struct spell * sp); extern struct spell * find_spell(magic_t mtype, const char * name); - extern void patzer(struct castorder *co); #ifdef __cplusplus } #endif #endif + +/* ------------------------------------------------------------- */ +/* Erläuterungen zu den Spruchdefinitionen + * + * Spruchstukturdefinition: + * spell{ + * id, name, + * beschreibung, + * syntax, + * parameter, + * magietyp, + * sptyp, + * rank,level, + * costtyp, aura, + * komponenten[5][2][faktorart], + * &funktion, patzer} + * + * id: + * SPL_NOSPELL muss der letzte Spruch in der Liste spelldaten sein, + * denn nicht auf die Reihenfolge in der Liste sondern auf die id wird + * geprüft + * + * sptyp: + * besondere Spruchtypen und Flags + * (Regionszauber, Kampfzauber, Farcastbar, Stufe variable, ..) + * + * rank: + * gibt die Priorität und damit die Reihenfolge an, in der der Spruch + * gezaubert wird. + * 1: Aura übertragen + * 2: Antimagie + * 3: Magierverändernde Sprüche (Magic Boost, ..) + * 4: Monster erschaffen + * 5: Standartlevel + * 7: Teleport + * + * Komponenten[Anzahl mögl. Items][Art:Anzahl:Kostentyp] + * + * R_AURA: + * Grundkosten für einen Zauber. Soviel Mp müssen mindestens investiert + * werden, um den Spruch zu wirken. Zusätzliche Mp können unterschiedliche + * Auswirkungen haben, die in der Spruchfunktionsroutine definiert werden. + * + * R_PERMAURA: + * Kosten an permantenter Aura + * + * Komponenten Kostentyp: + * SPC_LEVEL == Spruch mit Levelabhängigen Magiekosten. Die angegeben + * Kosten müssen für Stufe 1 berechnet sein. + * SPC_FIX == Feste Kosten + * + * Wenn keine spezielle Syntax angegeben ist, wird die + * Syntaxbeschreibung aus sptyp generiert: + * FARCASTING: ZAUBER [REGION x y] + * SPELLLEVEL: ZAUBER [STUFE n] + * UNITSPELL : ZAUBER [ ..] + * SHIPSPELL : ZAUBER [ ..] + * BUILDINGSPELL: ZAUBER [ ..] + * ONETARGET : ZAUBER + * PRECOMBATSPELL : KAMPFZAUBER [STUFE n] + * COMBATSPELL : KAMPFZAUBER [STUFE n] + * POSTCOMBATSPELL: KAMPFZAUBER [STUFE n] + * + * Das Parsing + * + * Der String spell->parameter gibt die Syntax an, nach der die + * Parameter des Spruches in add_spellparameter() geparst werden sollen. + * + * u : eine Einheitennummer + * r : hier kommen zwei Regionskoordinaten x y + * b : Gebäude- oder Burgnummer + * s : Schiffsnummer + * c : String, wird ohne Weiterverarbeitung übergeben + * i : Zahl (int), wird ohne Weiterverarbeitung übergeben + * k : Keywort - dieser String gibt den Paramter an, der folgt. Der + * Parameter wird mit findparam() identifiziert. + * k muss immer von einem c als Platzhalter für das Objekt gefolgt + * werden. + * Ein gutes Beispiel sind hierfür die Sprüche zur Magieanalyse. + * + : gibt an, das der vorherige Parameter mehrfach vorkommen kann. Da + * ein Ende nicht definiert werden kann, muss dies immer am Schluss + * kommen. + * + * Flags für das Parsing: + * TESTRESISTANCE : alle Zielobjekte, also alle Parameter vom Typ Unit, + * Burg, Schiff oder Region, werden auf ihre + * Magieresistenz überprüft + * TESTCANSEE : jedes Objekt vom Typ Einheit wird auf seine + * Sichtbarkeit überprüft + * SEARCHGLOBAL : die Zielobjekte werden global anstelle von regional + * gesucht + * REGIONSPELL : Ziel ist die Region, auch wenn kein Zielobjekt + * angegeben wird. Ist TESTRESISTANCE gesetzt, so wird + * die Magieresistenz der Region überprüft + * + * Bei fehlendem Ziel oder wenn dieses dem Zauber widersteht, wird die + * Spruchfunktion nicht aufgerufen. + * Sind zu wenig Parameter vorhanden, wird der Zauber ebenfalls nicht + * ausgeführt. + * Ist eins von mehreren Zielobjekten resistent, so wird das Flag + * pa->param[n]->flag == TARGET_RESISTS + * Ist eins von mehreren Zielobjekten nicht gefunden worden, so ist + * pa->param[n]->flag == TARGET_NOTFOUND + * + */ +/* ------------------------------------------------------------- */ + diff --git a/src/common/kernel/spellid.h b/src/common/kernel/spellid.h new file mode 100644 index 000000000..f4c163147 --- /dev/null +++ b/src/common/kernel/spellid.h @@ -0,0 +1,209 @@ +/* vi: set ts=2: +* +-------------------+ Christian Schlittchen +* | | Enno Rehling +* | Eressea PBEM host | Katja Zedel +* | (c) 1998 - 2005 | +* | | This program may not be used, modified or distributed +* +-------------------+ without prior permission by the authors of Eressea. +* +*/ + +#ifndef H_KRNL_SPELLID +#define H_KRNL_SPELLID + +/* Sprüche. Neue NUR hinten anfügen, oder das Datenfile geht kaputt */ +enum { + SPL_NOSPELL = 0, + LUASPL_ARTEFAKT_OF_POWER, + LUASPL_ARTEFAKT_OF_AURAPOWER, + LUASPL_ARTEFAKT_OF_REGENERATION, + SPL_FIREBALL = 4, + SPL_HAGEL, + SPL_RUSTWEAPON, + SPL_COMBATRUST, + SPL_TREEGROW, + SPL_HEALING, + SPL_HEALINGSONG, + SPL_BADDREAMS, + SPL_GOODDREAMS, + SPL_DREAMREADING, + SPL_SWEETDREAMS, + SPL_TIREDSOLDIERS, + SPL_PLAGUE, + SPL_MAGICBOOST, + SPL_CHAOSROW, + SPL_SONG_OF_CONFUSION, + SPL_FLEE, + SPL_SONG_OF_FEAR, + SPL_BERSERK, + SPL_BLOODTHIRST, + SPL_MAELSTROM, + SPL_BLESSEDHARVEST, + SPL_RAINDANCE, + SPL_TRANSFERAURA_DRUIDE, + SPL_TRANSFERAURA_BARDE, + SPL_TRANSFERAURA_CHAOS, + SPL_TRANSFERAURA_TRAUM, + SPL_TRANSFERAURA_ASTRAL, + SPL_STONEGOLEM, + SPL_IRONGOLEM, + SPL_SUMMONSHADOW, + SPL_SUMMONSHADOWLORDS, + SPL_REELING_ARROWS, + SPL_ANTIMAGICZONE, + LUASPL_CREATE_ANTIMAGICCRYSTAL, + SPL_KAELTESCHUTZ = 39, + SPL_STEALAURA, + SPL_SUMMONUNDEAD, + SPL_AURALEAK, + SPL_GREAT_DROUGHT, + SPL_STRONG_WALL, + SPL_HOMESTONE, + SPL_DROUGHT, + SPL_FOREST_FIRE, + LUASPL_STRENGTH, + SPL_SUMMONENT = 49, + SPL_DISTURBINGDREAMS, + SPL_DENYATTACK, + SPL_SLEEP, + SPL_EARTHQUAKE, + SPL_IRONKEEPER, + SPL_STORMWINDS, + SPL_GOODWINDS, + SPL_FLYING_SHIP, + SPL_SUMMON_ALP, + SPL_WINDSHIELD, + SPL_RAISEPEASANTS, + SPL_DEPRESSION, + SPL_HEADACHE, + LUASPL_ARTEFAKT_NIMBLEFINGERRING, + SPL_ENTERASTRAL = 64, + SPL_LEAVEASTRAL, + SPL_SHOWASTRAL, + SPL_VERSTEINERN, + SPL_TREEWALKENTER, + SPL_TREEWALKEXIT, + SPL_CHAOSSUCTION, + SPL_VIEWREALITY, + SPL_DISRUPTASTRAL, + SPL_SEDUCE, + SPL_PUMP, + SPL_CALM_MONSTER, + SPL_HERO, + SPL_FRIGHTEN, + SPL_MINDBLAST, + SPL_SPEED, + SPL_SPEED2, + SPL_FIREDRAGONODEM, + SPL_DRAGONODEM, + SPL_WYRMODEM, /* 83 */ + SPL_MAGICSTREET, + SPL_REANIMATE, + SPL_RECRUIT, + SPL_GENEROUS, + SPL_PERMTRANSFER, + SPL_SONG_OF_PEACE, + SPL_MIGRANT, + SPL_RALLYPEASANTMOB, + SPL_RAISEPEASANTMOB, + SPL_ILL_SHAPESHIFT, + SPL_WOLFHOWL, + SPL_FOG_OF_CONFUSION, + SPL_DREAM_OF_CONFUSION, + SPL_RESISTMAGICBONUS, + SPL_KEEPLOOT, + SPL_SCHILDRUNEN, + SPL_SONG_RESISTMAGIC, + SPL_SONG_SUSCEPTMAGIC, + SPL_ANALYSEMAGIC, + SPL_ANALYSEDREAM, + SPL_UNIT_ANALYSESONG, + SPL_OBJ_ANALYSESONG, + SPL_TYBIED_DESTROY_MAGIC, + SPL_DESTROY_MAGIC, + SPL_METEORRAIN, + SPL_REDUCESHIELD, + SPL_ARMORSHIELD, + SPL_DEATHCLOUD, + SPL_ORKDREAM, + SPL_SUMMONDRAGON, + SPL_READMIND, + SPL_BABBLER, + SPL_MOVECASTLE, + SPL_BLESSSTONECIRCLE, + SPL_ILLAUN_FAMILIAR, + SPL_GWYRRD_FAMILIAR, + SPL_DRAIG_FAMILIAR, + SPL_CERDDOR_FAMILIAR, + SPL_TYBIED_FAMILIAR, + SPL_SONG_OF_ENSLAVE, + LUASPL_TRUESEEING_GWYRRD, + LUASPL_TRUESEEING_DRAIG, + LUASPL_TRUESEEING_ILLAUN, + LUASPL_TRUESEEING_CERDDOR, + LUASPL_TRUESEEING_TYBIED, + LUASPL_INVISIBILITY_GWYRRD, + LUASPL_INVISIBILITY_DRAIG, + LUASPL_INVISIBILITY_ILLAUN, + LUASPL_INVISIBILITY_CERDDOR, + LUASPL_INVISIBILITY_TYBIED, + LUASPL_ARTEFAKT_CHASTITYBELT, + LUASPL_ARTEFAKT_RUNESWORD, + SPL_FUMBLECURSE = 136, + SPL_ICASTLE, + SPL_GWYRRD_DESTROY_MAGIC, + SPL_DRAIG_DESTROY_MAGIC, + SPL_ILLAUN_DESTROY_MAGIC, + SPL_CERDDOR_DESTROY_MAGIC, + SPL_GWYRRD_ARMORSHIELD, + SPL_DRAIG_FUMBLESHIELD, + SPL_GWYRRD_FUMBLESHIELD, + SPL_CERRDOR_FUMBLESHIELD, + SPL_TYBIED_FUMBLESHIELD, + SPL_SHADOWKNIGHTS, + LUASPL_FIRESWORD, + LUASPL_CREATE_TACTICCRYSTAL, + SPL_ITEMCLOAK = 150, + SPL_FIREWALL, + SPL_WISPS, + SPL_SPARKLE_CHAOS, + SPL_SPARKLE_DREAM, + LUASPL_BAG_OF_HOLDING, + SPL_PULLASTRAL = 156, + SPL_FETCHASTRAL, + SPL_ILLAUN_EARN_SILVER, + SPL_GWYRRD_EARN_SILVER, + SPL_DRAIG_EARN_SILVER, + SPL_TYBIED_EARN_SILVER, + SPL_CERDDOR_EARN_SILVER, + SPL_SHOCKWAVE, + SPL_UNDEADHERO, + LUASPL_ARTEFAKT_SACK_OF_CONSERVATION, + SPL_BECOMEWYRM = 166, + SPL_ETERNIZEWALL, + SPL_PUTTOREST, + SPL_UNHOLYPOWER, + SPL_HOLYGROUND, + SPL_BLOODSACRIFICE, + SPL_MALLORN, + SPL_CLONECOPY, + SPL_DRAINODEM, /* 174? */ + SPL_AURA_OF_FEAR, /* 175? */ + SPL_SHADOWCALL, /* 176? */ + SPL_MALLORNTREEGROW, + LUASPL_INVISIBILITY2_ILLAUN, + SPL_BIGRECRUIT = 179, + SPL_IMMOLATION, + SPL_FIREODEM, /* 181 */ + SPL_ICEODEM, + SPL_ACIDODEM, +#ifdef WDW_PYRAMIDSPELL + SPL_WDWPYRAMID_TRAUM, + SPL_WDWPYRAMID_ASTRAL, + SPL_WDWPYRAMID_DRUIDE, + SPL_WDWPYRAMID_BARDE, + SPL_WDWPYRAMID_CHAOS, +#endif +}; + +#endif diff --git a/src/common/kernel/xmlreader.c b/src/common/kernel/xmlreader.c index e54deb1e8..e2030aad2 100644 --- a/src/common/kernel/xmlreader.c +++ b/src/common/kernel/xmlreader.c @@ -925,6 +925,38 @@ add_items(equipment * eq, xmlNodeSetPtr nsetItems) } } +static void +add_spells(equipment * eq, xmlNodeSetPtr nsetItems) +{ + if (nsetItems!=NULL && nsetItems->nodeNr>0) { + int i; + for (i=0;i!=nsetItems->nodeNr;++i) { + xmlNodePtr node = nsetItems->nodeTab[i]; + xmlChar * property; + magic_t mtype = M_GRAU; + struct spell * sp; + + property = xmlGetProp(node, BAD_CAST "school"); + if (property!=NULL) { + for (mtype=0;mtype!=MAXMAGIETYP;++mtype) { + if (strcmp((const char*)property, magietypen[mtype])==0) break; + } + assert(mtype!=MAXMAGIETYP); + xmlFree(property); + } + + property = xmlGetProp(node, BAD_CAST "name"); + assert(property!=NULL); + sp = find_spell(mtype, (const char*)property); + assert(sp); + xmlFree(property); + if (sp!=NULL) { + equipment_addspell(eq, sp); + } + } + } +} + static void add_skills(equipment * eq, xmlNodeSetPtr nsetSkills) { @@ -1033,6 +1065,10 @@ parse_equipment(xmlDocPtr doc) add_items(eq, xpathResult->nodesetval); xmlXPathFreeObject(xpathResult); + xpathResult = xmlXPathEvalExpression(BAD_CAST "spell", xpath); + add_spells(eq, xpathResult->nodesetval); + xmlXPathFreeObject(xpathResult); + xpathResult = xmlXPathEvalExpression(BAD_CAST "skill", xpath); add_skills(eq, xpathResult->nodesetval); xmlXPathFreeObject(xpathResult); @@ -1654,8 +1690,8 @@ register_xmlreader(void) xml_register_callback(parse_buildings); /* requires resources */ xml_register_callback(parse_ships); /* requires resources */ - xml_register_callback(parse_equipment); /* requires resources */ xml_register_callback(parse_spells); /* requires resources */ + xml_register_callback(parse_equipment); /* requires spells */ xml_register_callback(parse_races); /* requires spells */ xml_register_callback(parse_calendar); } diff --git a/src/common/modules/arena.c b/src/common/modules/arena.c index 8f2c4518f..63c5631ad 100644 --- a/src/common/modules/arena.c +++ b/src/common/modules/arena.c @@ -316,7 +316,7 @@ tower_init(void) b->size = 10; if (i!=0) { sprintf(buf, "Turm des %s", - LOC(default_locale, mkname("school", neue_gebiete[i]))); + LOC(default_locale, mkname("school", magietypen[i]))); } else sprintf(buf, "Turm der Ahnungslosen"); set_string(&b->name, buf); diff --git a/src/common/spells/spells.c b/src/common/spells/spells.c index f1a855ce1..180a976a8 100644 --- a/src/common/spells/spells.c +++ b/src/common/spells/spells.c @@ -24,6 +24,9615 @@ struct curse_type; extern const struct curse_type ct_firewall; extern void ct_register(const struct curse_type * ct); +/* kernel includes */ +#include /* für lovar */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* spells includes */ +#include + +/* util includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* libc includes */ +#include +#include +#include +#include +#include +#include +#include + +/* triggers includes */ +#include +#include +#include +#include +#include +#include +#include + +/* attributes includes */ +#include +#include +/* ----------------------------------------------------------------------- */ + +static variant zero_effect = { 0 }; + +attrib_type at_unitdissolve = { + "unitdissolve", NULL, NULL, NULL, a_writedefault, a_readdefault +}; + +#ifdef WDW_PYRAMIDSPELL +attrib_type at_wdwpyramid = { + "wdwpyramid", NULL, NULL, NULL, a_writedefault, a_readdefault +}; +#endif + +/* ----------------------------------------------------------------------- */ + +static void +report_failure(unit * mage, struct order * ord) { + /* Fehler: "Der Zauber schlägt fehl" */ + cmistake(mage, ord, 180, MSG_MAGIC); +} + +/* ------------------------------------------------------------- */ +/* do_shock - Schockt die Einheit, z.B. bei Verlust eines */ +/* Vertrauten. */ +/* ------------------------------------------------------------- */ + +void +do_shock(unit *u, const char *reason) +{ + int i; + if(u->number == 0) return; + + /* HP - Verlust */ + u->hp = (unit_max_hp(u) * u->number)/10; + u->hp = max(1, u->hp); + /* Aura - Verlust */ + if(is_mage(u)) { + set_spellpoints(u, max_spellpoints(u->region,u)/10); + } + + /* Evt. Talenttageverlust */ + for (i=0;i!=u->skill_size;++i) if (rand()%5==0) { + skill * sv = u->skills+i; + int weeks = (sv->level * sv->level - sv->level) / 2; + int change = (weeks+9) / 10; + reduce_skill(u, sv, change); + } + + /* Dies ist ein Hack, um das skillmod und familiar-Attribut beim Mage + * zu löschen wenn der Familiar getötet wird. Da sollten wir über eine + * saubere Implementation nachdenken. */ + + if(!strcmp(reason, "trigger")) { + remove_familiar(u); + } + + ADDMSG(&u->faction->msgs, msg_message("shock", + "mage reason", u, strdup(reason))); +} + +/* ------------------------------------------------------------- */ +/* Spruchanalyse - Ausgabe von curse->info und curse->name */ +/* ------------------------------------------------------------- */ + +static double +curse_chance(const struct curse * c, double force) +{ + return 1.0 + (force - c->vigour) * 0.1; +} + +static void +magicanalyse_region(region *r, unit *mage, double force) +{ + attrib *a; + boolean found = false; + const struct locale * lang = mage->faction->locale; + + for (a=r->attribs;a;a=a->next) { + curse * c = (curse*)a->data.v; + double probability; + int mon; + + if (!fval(a->type, ATF_CURSE)) continue; + + /* ist der curse schwächer als der Analysezauber, so ergibt sich + * mehr als 100% probability und damit immer ein Erfolg. */ + probability = curse_chance(c, force); + mon = c->duration + (rand()%10) - 5; + mon = max(1, mon); + found = true; + + if (chance(probability)) { /* Analyse geglückt */ + if(c->flag & CURSE_NOAGE) { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_region_noage", "mage region curse", + mage, r, LOC(lang, mkname("spell", c->type->cname)))); + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_region_age", "mage region curse months", + mage, r, LOC(lang, mkname("spell", c->type->cname)), mon)); + } + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_region_fail", "mage region", mage, r)); + } + } + if (!found) { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_region_nospell", "mage region", mage, r)); + } +} + +static void +magicanalyse_unit(unit *u, unit *mage, double force) +{ + attrib *a; + boolean found = false; + const struct locale * lang = mage->faction->locale; + + for (a=u->attribs;a;a=a->next) { + curse * c; + double probability; + int mon; + if (!fval(a->type, ATF_CURSE)) continue; + + c = (curse*)a->data.v; + /* ist der curse schwächer als der Analysezauber, so ergibt sich + * mehr als 100% probability und damit immer ein Erfolg. */ + probability = curse_chance(c, force); + mon = c->duration + (rand()%10) - 5; + mon = max(1,mon); + + if (chance(probability)) { /* Analyse geglückt */ + if(c->flag & CURSE_NOAGE){ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_unit_noage", "mage unit curse", + mage, u, LOC(lang, mkname("spell", c->type->cname)))); + }else{ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_unit_age", "mage unit curse months", + mage, u, LOC(lang, mkname("spell", c->type->cname)), mon)); + } + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_unit_fail", "mage unit", mage, u)); + } + } + if (!found) { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_unit_nospell", "mage target", mage, u)); + } +} + +static void +magicanalyse_building(building *b, unit *mage, double force) +{ + attrib *a; + boolean found = false; + const struct locale * lang = mage->faction->locale; + + for (a=b->attribs;a;a=a->next) { + curse * c; + double probability; + int mon; + + if (!fval(a->type, ATF_CURSE)) continue; + + c = (curse*)a->data.v; + /* ist der curse schwächer als der Analysezauber, so ergibt sich + * mehr als 100% probability und damit immer ein Erfolg. */ + probability = curse_chance(c, force); + mon = c->duration + (rand()%10) - 5; + mon = max(1,mon); + + if (chance(probability)) { /* Analyse geglückt */ + if(c->flag & CURSE_NOAGE){ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_building_age", "mage building curse", + mage, b, LOC(lang, mkname("spell", c->type->cname)))); + }else{ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_building_age", "mage building curse months", + mage, b, LOC(lang, mkname("spell", c->type->cname)), mon)); + } + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_building_fail", "mage building", mage, b)); + } + } + if (!found) { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_building_nospell", "mage building", mage, b)); + } + +} + +static void +magicanalyse_ship(ship *sh, unit *mage, double force) +{ + attrib *a; + boolean found = false; + const struct locale * lang = mage->faction->locale; + + for (a=sh->attribs;a;a=a->next) { + curse * c; + double probability; + int mon; + if (!fval(a->type, ATF_CURSE)) continue; + + c = (curse*)a->data.v; + /* ist der curse schwächer als der Analysezauber, so ergibt sich + * mehr als 100% probability und damit immer ein Erfolg. */ + probability = curse_chance(c, force); + mon = c->duration + (rand()%10) - 5; + mon = max(1,mon); + + if (chance(probability)) { /* Analyse geglückt */ + if(c->flag & CURSE_NOAGE){ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_ship_noage", "mage ship curse", + mage, sh, LOC(lang, mkname("spell", c->type->cname)))); + }else{ + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_ship_age", "mage ship curse months", + mage, sh, LOC(lang, mkname("spell", c->type->cname)), mon)); + } + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_ship_fail", "mage ship", mage, sh)); + + } + } + if (!found) { + ADDMSG(&mage->faction->msgs, msg_message( + "analyse_ship_nospell", "mage ship", mage, sh)); + } + +} + +/* ------------------------------------------------------------- */ +/* Antimagie - curse auflösen */ +/* ------------------------------------------------------------- */ + +/* Wenn der Curse schwächer ist als der cast_level, dann wird er + * aufgelöst, bzw seine Kraft (vigour) auf 0 gesetzt. + * Ist der cast_level zu gering, hat die Antimagie nur mit einer Chance + * von 100-20*Stufenunterschied % eine Wirkung auf den Curse. Dann wird + * die Kraft des Curse um die halbe Stärke der Antimagie reduziert. + * Zurückgegeben wird der noch unverbrauchte Rest von force. + */ +double +destr_curse(curse* c, int cast_level, double force) +{ + if (cast_level < c->vigour) { /* Zauber ist nicht stark genug */ + double probability = 0.1 + (cast_level - c->vigour)*0.2; + /* pro Stufe Unterschied -20% */ + if (chance(probability)) { + force -= c->vigour; + if (c->type->change_vigour){ + c->type->change_vigour(c, -(cast_level+1/2)); + } else { + c->vigour -= cast_level+1/2; + } + } + } else { /* Zauber ist stärker als curse */ + if (force >= c->vigour){ /* reicht die Kraft noch aus? */ + force -= c->vigour; + if (c->type->change_vigour){ + c->type->change_vigour(c, -c->vigour); + } else { + c->vigour = 0; + } + + } + } + return force; +} + +int +destroy_curse(attrib **alist, int cast_level, double force, curse * c) +{ + int succ = 0; +/* attrib **a = a_find(*ap, &at_curse); */ + attrib ** ap = alist; + + while (*ap && force > 0) { + curse * c1; + attrib * a = *ap; + if (!fval(a->type, ATF_CURSE)) { + do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); + continue; + } + c1 = (curse*)a->data.v; + + /* Immunität prüfen */ + if (c1->flag & CURSE_IMMUNE) { + do { ap = &(*ap)->next; } while (*ap && a->type==(*ap)->type); + continue; + } + + /* Wenn kein spezieller cursetyp angegeben ist, soll die Antimagie + * auf alle Verzauberungen wirken. Ansonsten prüfe, ob der Curse vom + * richtigen Typ ist. */ + if(!c || c==c1) { + double remain = destr_curse(c1, cast_level, force); + if (remain < force) { + succ = cast_level; + force = remain; + } + if (c1->vigour <= 0) { + a_remove(alist, a); + } + } + if (*ap==a) ap = &a->next; + } + return succ; +} + +/* ------------------------------------------------------------- */ +/* Report a spell's effect to the units in the region. +*/ +static void +report_effect(region * r, unit * mage, message * seen, message * unseen) +{ + unit * u; + + /* melden, 1x pro Partei */ + freset(mage->faction, FL_DH); + for (u = r->units; u; u = u->next ) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + + /* Bei Fernzaubern sieht nur die eigene Partei den Magier */ + if (u->faction != mage->faction){ + if (r == mage->region){ + /* kein Fernzauber, prüfe, ob der Magier überhaupt gesehen + * wird */ + if (cansee(u->faction, r, mage, 0)) { + r_addmessage(r, u->faction, seen); + } else { + r_addmessage(r, u->faction, unseen); + } + } else { /* Fernzauber, fremde Partei sieht den Magier niemals */ + r_addmessage(r, u->faction, unseen); + } + } else { /* Partei des Magiers, sieht diesen immer */ + r_addmessage(r, u->faction, seen); + } + } + } + /* Ist niemand von der Partei des Magiers in der Region, dem Magier + * nochmal gesondert melden */ + if (!fval(mage->faction, FL_DH)) { + add_message(&mage->faction->msgs, seen); + } +} + +/* ------------------------------------------------------------- */ +/* Die Spruchfunktionen */ +/* ------------------------------------------------------------- */ +/* Meldungen: + * + * Fehlermeldungen sollten als MSG_MAGIC, level ML_MISTAKE oder + * ML_WARN ausgegeben werden. (stehen im Kopf der Auswertung unter + * Zauberwirkungen) + + sprintf(buf, "%s in %s: 'ZAUBER %s': [hier die Fehlermeldung].", + unitname(mage), regionname(mage->region, mage->faction), sa->strings[0]); + add_message(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + + * Allgemein sichtbare Auswirkungen in der Region sollten als + * Regionsereignisse auch dort auftauchen. + + { + message * seen = msg_message("harvest_effect", "mage", mage); + message * unseen = msg_message("harvest_effect", "mage", NULL); + report_effect(r, mage, seen, unseen); + } + + * Meldungen an den Magier über Erfolg sollten, wenn sie nicht als + * Regionsereigniss auftauchen, als MSG_MAGIC level ML_INFO unter + * Zauberwirkungen gemeldet werden. Direkt dem Magier zuordnen (wie + * Botschaft an Einheit) ist derzeit nicht möglich. + * ACHTUNG! r muss nicht die Region des Magier sein! (FARCASTING) + * + * Parameter: + * die Struct castorder *co ist in magic.h deklariert + * die Parameterliste spellparameter *pa = co->par steht dort auch. + * + */ + +/* ------------------------------------------------------------- */ +/* Name: Vertrauter + * Stufe: 10 + * + * Wirkung: + * Der Magier beschwört 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 erhält der Magier + * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist + * zufällig bestimmt, wird aber durch Magiegebiet und Rasse beeinflußt. + * "Tierische" Vertraute brauchen keinen Unterhalt. + * + * Ein paar Möglichkeiten: + * Magieg. Rasse Besonderheiten + * Eule Tybied -/- fliegt, Auraregeneration + * Rabe Ilaun -/- fliegt + * Imp Draig -/- Magieresistenz? + * Fuchs Gwyrrd -/- Wahrnehmung + * ???? Cerddor -/- ???? (Singvogel?, Papagei?) + * Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch? + * Krähe -/- -/- fliegt, +Tarnung (weil unauffällig) + * Delphin -/- Meerm. schwimmt + * Wolf -/- Ork + * Hund -/- Mensch kann evtl BEWACHE ausführen + * Ratte -/- Goblin + * Albatros -/- -/- fliegt, kann auf Ozean "landen" + * Affe -/- -/- kann evtl BEKLAUE ausführen + * Goblin -/- !Goblin normale Einheit + * Katze -/- !Katze normale Einheit + * Dämon -/- !Dämon normale Einheit + * + * Spezielle V. für Katzen, Trolle, Elfen, Dämonen, Insekten, Zwerge? + */ + +static const race * +select_familiar(const race * magerace, magic_t magiegebiet) +{ + const race * retval = NULL; + int rnd = rand()%100; + assert(magerace->familiars[0]); + + do { + if (rnd < 3) { + /* RC_KRAKEN muß letzter Vertraute sein */ + int rc = RC_LYNX + rand()%(RC_KRAKEN+1-RC_LYNX); + retval = new_race[rc]; + } else if (rnd < 80) { + retval = magerace->familiars[0]; + } + retval = magerace->familiars[magiegebiet]; + } + while (retval->init_familiar==NULL); + return retval; +} + +/* ------------------------------------------------------------- */ +/* der Vertraue des Magiers */ + +boolean +is_familiar(const unit *u) +{ + attrib * a = a_find(u->attribs, &at_familiarmage); + return i2b(a!=NULL); +} + +static void +make_familiar(unit *familiar, unit *mage) +{ + /* skills and spells: */ + familiar->race->init_familiar(familiar); + + /* triggers: */ + create_newfamiliar(mage, familiar); + + /* Hitpoints nach Talenten korrigieren, sonst starten vertraute + * mit Ausdauerbonus verwundet */ + familiar->hp = unit_max_hp(familiar); +} + +static int +sp_summon_familiar(castorder *co) +{ + unit *familiar; + region *r = co->rt; + region *target_region = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + const race * rc; + skill_t sk; + int dh, dh1; + direction_t d; + if (get_familiar(mage) != NULL ) { + cmistake(mage, co->order, 199, MSG_MAGIC); + return 0; + } + rc = select_familiar(mage->faction->race, mage->faction->magiegebiet); + + if (fval(rc, RCF_SWIM) && !fval(rc, RCF_WALK)) { + int coasts = is_coastregion(r); + + if (coasts == 0) { + cmistake(mage, co->order, 229, MSG_MAGIC); + return 0; + } + + /* In welcher benachbarten Ozeanregion soll der Familiar erscheinen? */ + coasts = rand()%coasts; + dh = -1; + for(d=0; dterrain == T_OCEAN) { + dh++; + if(dh == coasts) break; + } + } + target_region = rconnect(r,d); + } + + familiar = create_unit(target_region, mage->faction, 1, rc, 0, NULL, mage); + if (target_region==mage->region) { + familiar->building = mage->building; + familiar->ship = mage->ship; + } + familiar->status = ST_FLEE; /* flieht */ + sprintf(buf, "Vertrauter von %s", unitname(mage)); + set_string(&familiar->name, buf); + if (fval(mage, UFL_PARTEITARNUNG)) fset(familiar, UFL_PARTEITARNUNG); + fset(familiar, UFL_LOCKED); + make_familiar(familiar, mage); + + dh = 0; + dh1 = 0; + sprintf(buf, "%s ruft einen Vertrauten. %s können ", + unitname(mage), LOC(mage->faction->locale, rc_name(rc, 1))); + for(sk=0;skbonus[sk] > -5) dh++; + } + for(sk=0;skbonus[sk] > -5){ + dh--; + if (dh1 == 0){ + dh1 = 1; + } else { + if (dh == 0){ + scat(" und "); + } else { + scat(", "); + } + } + scat(skillname(sk, mage->faction->locale)); + } + } + scat(" lernen."); + scat(" "); + scat("Der Vertraute verleiht dem Magier einen Bonus auf jedes Talent "); + scat("(ausgenommen Magie), welches der Vertraute beherrscht."); + scat(" "); + scat("Das spezielle Band zu seinem Vertrauten ermöglicht dem Magier "); + scat("auch, Sprüche durch diesen zu wirken. So gezauberte Sprüche "); + scat("wirken auf die Region des Vertrauten und brauchen keine Fernzauber "); + scat("zu sein. Die maximale Entfernung dafür entspricht dem Talent des "); + scat("Magiers. Einen Spruch durch das Vertrautenband zu richten ist "); + scat("jedoch gewissen Einschränkungen unterworfen. Die Stufe des Zaubers "); + scat("kann nicht größer als das Magietalent des Vertrauten oder das halbe "); + scat("Talent des Magiers sein. Auch verdoppeln sich die Kosten für den "); + scat("Spruch. (Um einen Zauber durch den Vertrauten zu wirken, gibt "); + scat("man statt dem Magier dem Vertrauten den Befehl ZAUBERE.)"); + + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Zerstöre Magie + * Wirkung: + * Zerstört alle Zauberwirkungen auf dem Objekt. Jeder gebrochene + * Zauber verbraucht c->vigour an Zauberkraft. Wird der Spruch auf + * einer geringeren Stufe gezaubert, als der Zielzauber an c->vigour + * hat, so schlägt die Auflösung mit einer von der Differenz abhängigen + * Chance fehl. Auch dann wird force verbraucht, der Zauber jedoch nur + * abgeschwächt. + * + * Flag: + * (FARCASTING|SPELLLEVEL|ONSHIPCAST|TESTCANSEE) + * */ +static int +sp_destroy_magic(castorder *co) +{ + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + curse * c = NULL; + char ts[80]; + attrib **ap; + int obj; + int succ; + + /* da jeder Zauber force verbraucht und der Zauber auf alles und nicht + * nur einen Spruch wirken soll, wird die Wirkung hier verstärkt */ + force *= 4; + + /* Objekt ermitteln */ + obj = pa->param[0]->typ; + + switch(obj) { + case SPP_REGION: + { + region *tr = pa->param[0]->data.r; + ap = &tr->attribs; + strcpy(ts, regionname(tr, mage->faction)); + break; + } + case SPP_TEMP: + case SPP_UNIT: + { + unit *u; + u = pa->param[0]->data.u; + ap = &u->attribs; + strcpy(ts, unitname(u)); + break; + } + case SPP_BUILDING: + { + building *b; + b = pa->param[0]->data.b; + ap = &b->attribs; + strcpy(ts, buildingname(b)); + break; + } + case SPP_SHIP: + { + ship *sh; + sh = pa->param[0]->data.sh; + ap = &sh->attribs; + strcpy(ts, shipname(sh)); + break; + } + default: + return 0; + } + + succ = destroy_curse(ap, cast_level, force, c); + + if (succ) { + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_effect", "unit region command succ target", + mage, mage->region, co->order, succ, strdup(ts))); + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_noeffect", "unit region command", + mage, mage->region, co->order)); + } + + return max(succ, 1); +} + +/* ------------------------------------------------------------- */ +/* Name: Transferiere Aura + * Stufe: variabel + * Gebiet: alle + * Kategorie: Einheit, positiv + * Wirkung: + * Mit Hilfe dieses Zauber kann der Magier eigene Aura im Verhältnis + * 2:1 auf einen anderen Magier des gleichen Magiegebietes oder (nur + * bei Tybied) im Verhältnis 3:1 auf einen Magier eines anderen + * Magiegebietes übertragen. + * + * Syntax: + * "ZAUBERE " + * "ui" + * Flags: + * (UNITSPELL|ONSHIPCAST|ONETARGET) + * */ + +static int +sp_transferaura(castorder *co) +{ + int aura, gain, multi = 2; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + unit * u; + sc_mage * scm_dst, * scm_src = get_mage(mage); + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + /* Wieviel Transferieren? */ + aura = pa->param[1]->data.i; + u = pa->param[0]->data.u; + scm_dst = get_mage(u); + + if (scm_dst==NULL) { + /* "Zu dieser Einheit kann ich keine Aura übertragen." */ + cmistake(mage, co->order, 207, MSG_MAGIC); + return 0; + } else if (scm_src->magietyp==M_ASTRAL) { + if (scm_src->magietyp != scm_dst->magietyp) multi = 3; + } else if (scm_src->magietyp==M_GRAU) { + if (scm_src->magietyp != scm_dst->magietyp) multi = 4; + } else if (scm_dst->magietyp!=scm_src->magietyp) { + /* "Zu dieser Einheit kann ich keine Aura übertragen." */ + cmistake(mage, co->order, 207, MSG_MAGIC); + return 0; + } + + if (aura < multi) { + /* "Auraangabe fehlerhaft." */ + cmistake(mage, co->order, 208, MSG_MAGIC); + return 0; + } + + gain = min(aura, scm_src->spellpoints) / multi; + scm_src->spellpoints -= gain*multi; + scm_dst->spellpoints += gain; + +/* sprintf(buf, "%s transferiert %d Aura auf %s", unitname(mage), + gain, unitname(u)); */ + ADDMSG(&mage->faction->msgs, msg_message( + "auratransfer_success", "unit target aura", mage, u, gain)); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* DRUIDE */ +/* ------------------------------------------------------------- */ +/* Name: Günstige Winde + * Stufe: 4 + * Gebiet: Gwyrrd + * Wirkung: + * Schiffsbewegung +1, kein Abtreiben. Hält (Stufe) Runden an. + * Kombinierbar mit "Sturmwind" (das +1 wird dadurch aber nicht + * verdoppelt), und "Luftschiff". + * + * Flags: + * (SHIPSPELL|ONSHIPCAST|SPELLLEVEL|ONETARGET|TESTRESISTANCE) + */ + +static int +sp_goodwinds(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = cast_level+1; + spellparameter *pa = co->par; + ship *sh; + unit *u; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + sh = pa->param[0]->data.sh; + + /* keine Probleme mit C_SHIP_SPEEDUP und C_SHIP_FLYING */ + /* NODRIFT bewirkt auch +1 Geschwindigkeit */ + create_curse(mage, &sh->attribs, ct_find("nodrift"), power, duration, zero_effect, 0); + + /* melden, 1x pro Partei */ + freset(mage->faction, FL_DH); + for(u = r->units; u; u = u->next ) freset(u->faction, FL_DH); + for(u = r->units; u; u = u->next ) { + if(u->ship != sh ) /* nur den Schiffsbesatzungen! */ + continue; + if(!fval(u->faction, FL_DH) ) { + message * m = msg_message("wind_effect", "mage ship", cansee(u->faction, r, mage, 0) ? mage:NULL, sh); + r_addmessage(r, u->faction, m); + msg_release(m); + fset(u->faction, FL_DH); + } + } + if (!fval(mage->faction, FL_DH)) { + message * m = msg_message("wind_effect", "mage ship", mage, sh); + r_addmessage(r, mage->faction, m); + msg_release(m); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Magischer Pfad + * Stufe: 4 + * Gebiet: Gwyrrd + * Wirkung: + * für Stufe Runden wird eine (magische) Strasse erzeugt, die wie eine + * normale Strasse wirkt. + * Im Ozean schlägt der Spruch fehl + * + * Flags: + * (FARCASTING|SPELLLEVEL|REGIONSPELL|ONSHIPCAST|TESTRESISTANCE) + */ +static int +sp_magicstreet(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + + if (!landregion(rterrain(r))) { + cmistake(mage, co->order, 186, MSG_MAGIC); + return 0; + } + + /* wirkt schon in der Zauberrunde! */ + create_curse(mage, &r->attribs, ct_find("magicstreet"), co->force, co->level+1, zero_effect, 0); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("path_effect", "mage region", mage, r); + message * unseen = msg_message("path_effect", "mage region", NULL, r); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return co->level; +} + +/* ------------------------------------------------------------- */ +/* Name: Erwecke Ents + * Stufe: 10 + * Kategorie: Beschwörung, positiv + * Gebiet: Gwyrrd + * Wirkung: + * Verwandelt (Stufe) Bäume in eine Gruppe von Ents, die sich für Stufe + * Runden der Partei des Druiden anschliessen und danach wieder zu + * Bäumen werden + * Patzer: + * Monster-Ents entstehen + * + * Flags: + * (SPELLLEVEL) + */ +static int +sp_summonent(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + unit *u; + attrib *a; + int ents; + + if (rtrees(r,2) == 0) { + cmistake(mage, co->order, 204, MSG_EVENT); + /* nicht ohne bäume */ + return 0; + } + + ents = (int)min(power*power, rtrees(r,2)); + + u = create_unit(r, mage->faction, ents, new_race[RC_TREEMAN], 0, LOC(mage->faction->locale, rc_name(new_race[RC_TREEMAN], ents!=1)), mage); + + a = a_new(&at_unitdissolve); + a->data.ca[0] = 2; /* An r->trees. */ + a->data.ca[1] = 5; /* 5% */ + a_add(&u->attribs, a); + fset(u, UFL_LOCKED); + + rsettrees(r, 2, rtrees(r,2) - ents); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("ent_effect", "mage amount", mage, ents); + message * unseen = msg_message("ent_effect", "mage amount", NULL, ents); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Segne Steinkreis + * Stufe: 11 + * Kategorie: Artefakt + * Gebiet: Gwyrrd + * Wirkung: + * Es werden zwei neue Gebäude eingeführt: Steinkreis und Steinkreis + * (gesegnet). Ersteres kann man bauen, letzteres wird aus einem + * fertigen Steinkreis mittels des Zaubers erschaffen. + * + * Flags: + * (BUILDINGSPELL | ONETARGET) + * + */ +static int +sp_blessstonecircle(castorder *co) +{ + building *b; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *p = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(p->param[0]->flag == TARGET_NOTFOUND) return 0; + + b = p->param[0]->data.b; + + if(b->type != bt_find("stonecircle")) { + sprintf(buf, "%s ist kein Steinkreis.", buildingname(b)); + mistake(mage, co->order, buf, MSG_MAGIC); + return 0; + } + + if(b->size < b->type->maxsize) { + sprintf(buf, "%s muss vor der Weihe fertiggestellt sein.", buildingname(b)); + mistake(mage, co->order, buf, MSG_MAGIC); + return 0; + } + + b->type = bt_find("blessedstonecircle"); + + sprintf(buf, "%s weiht %s.", unitname(mage), buildingname(b)); + addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Mahlstrom + * Stufe: 15 + * Kategorie: Region, negativ + * Gebiet: Gwyrrd + * Wirkung: + * Erzeugt auf See einen Mahlstrom für Stufe-Wochen. Jedes Schiff, das + * durch den Mahlstrom segelt, nimmt 0-150% Schaden. (D.h. es hat auch + * eine 1/3-Chance, ohne Federlesens zu sinken. Der Mahlstrom sollte + * aus den Nachbarregionen sichtbar sein. + * + * Flags: + * (OCEANCASTABLE | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_maelstrom(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + curse * c; + double power = co->force; + variant effect; + int duration = (int)power+1; + + if(rterrain(r) != T_OCEAN) { + cmistake(mage, co->order, 205, MSG_MAGIC); + /* nur auf ozean */ + return 0; + } + + /* Attribut auf Region. + * Existiert schon ein curse, so wird dieser verstärkt + * (Max(Dauer), Max(Stärke))*/ + effect.i = (int)power; + c = create_curse(mage, &mage->attribs, ct_find("maelstrom"), power, duration, effect, 0); + curse_setflag(c, CURSE_ISNEW); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("maelstrom_effect", "mage", mage); + message * unseen = msg_message("maelstrom_effect", "mage", NULL); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Wurzeln der Magie + * Stufe: 16 + * Kategorie: Region, neutral + * Gebiet: Gwyrrd + * Wirkung: + * Wandelt einen Wald permanent in eine Mallornregion + * + * Flags: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_mallorn(castorder *co) +{ + region *r = co->rt; + int cast_level = co->level; + unit *mage = (unit *)co->magician; + + if(!landregion(rterrain(r))) { + cmistake(mage, co->order, 290, MSG_MAGIC); + return 0; + } + if(fval(r, RF_MALLORN)) { + cmistake(mage, co->order, 291, MSG_MAGIC); + return 0; + } + + /* half the trees will die */ + rsettrees(r, 2, rtrees(r,2)/2); + rsettrees(r, 1, rtrees(r,1)/2); + rsettrees(r, 0, rtrees(r,0)/2); + fset(r, RF_MALLORN); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("mallorn_effect", "mage", mage); + message * unseen = msg_message("mallorn_effect", "mage", NULL); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Segen der Erde + * Stufe: 1 + * Kategorie: Region, positiv + * Gebiet: Gwyrrd + * + * Wirkung: + * Alle Bauern verdienen Stufe-Wochen 1 Silber mehr. + * + * Flags: + * (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL) + */ +static int +sp_blessedharvest(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = (int)power+1; + variant effect; + + /* Attribut auf Region. + * Existiert schon ein curse, so wird dieser verstärkt + * (Max(Dauer), Max(Stärke))*/ + effect.i = 1; + create_curse(mage,&r->attribs, ct_find("blessedharvest"), power, duration, effect, 0); + { + message * seen = msg_message("harvest_effect", "mage", mage); + message * unseen = msg_message("harvest_effect", "mage", NULL); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Hainzauber + * Stufe: 2 + * Kategorie: Region, positiv + * Gebiet: Gwyrrd + * Syntax: ZAUBER [REGION x y] [STUFE 2] "Hain" + * Wirkung: + * Erschafft Stufe-10*Stufe Jungbäume + * + * Flag: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ + +static int +sp_hain(castorder *co) +{ + int trees; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + + if(!r->land) { + cmistake(mage, co->order, 296, MSG_MAGIC); + return 0; + } + if (fval(r, RF_MALLORN)) { + cmistake(mage, co->order, 92, MSG_MAGIC); + return 0; + } + + trees = lovar((int)(force * 10 * RESOURCE_QUANTITY)) + (int)force; + rsettrees(r, 1, rtrees(r,1) + trees); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("growtree_effect", "mage amount", mage, trees); + message * unseen = msg_message("growtree_effect", "mage amount", NULL, trees); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} +/* ------------------------------------------------------------- */ +/* Name: Segne Mallornstecken - Mallorn Hainzauber + * Stufe: 4 + * Kategorie: Region, positiv + * Gebiet: Gwyrrd + * Syntax: ZAUBER [REGION x y] [STUFE 4] "Segne Mallornstecken" + * Wirkung: + * Erschafft Stufe-10*Stufe Jungbäume + * + * Flag: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ + +static int +sp_mallornhain(castorder *co) +{ + int trees; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + + if(!r->land) { + cmistake(mage, co->order, 296, MSG_MAGIC); + return 0; + } + if (!fval(r, RF_MALLORN)) { + cmistake(mage, co->order, 91, MSG_MAGIC); + return 0; + } + + trees = lovar((int)(force * 10 * RESOURCE_QUANTITY)) + (int)force; + rsettrees(r, 1, rtrees(r,1) + trees); + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("growtree_effect", "mage amount", mage, trees); + message * unseen = msg_message("growtree_effect", "mage amount", NULL, trees); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + +void +patzer_ents(castorder *co) +{ + int ents; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + /* int cast_level = co->level; */ + double force = co->force; + + if(!r->land) { + cmistake(mage, co->order, 296, MSG_MAGIC); + return; + } + + ents = (int)(force*10); + u = create_unit(r, findfaction(MONSTER_FACTION), ents, new_race[RC_TREEMAN], 0, + LOC(default_locale, rc_name(new_race[RC_TREEMAN], ents!=1)), NULL); + + /* 'Erfolg' melden */ + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_patzer", "unit region command", mage, + mage->region, co->order)); + + /* melden, 1x pro Partei */ + { + message * unseen = msg_message("entrise", "region", r); + report_effect(r, mage, unseen, unseen); + msg_release(unseen); + } +} + +/* ------------------------------------------------------------- */ +/* Name: Rosthauch + * Stufe: 3 + * Kategorie: Einheit, negativ + * Gebiet: Gwyrrd + * Wirkung: + * Zerstört zwischen Stufe und Stufe*10 Eisenwaffen + * + * Flag: + * (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE) + */ +/* Syntax: ZAUBER [REGION x y] [STUFE 2] "Rosthauch" 1111 2222 3333 */ + +typedef struct iron_weapon { + const struct item_type * type; + const struct item_type * rusty; + float chance; + struct iron_weapon * next; +} iron_weapon; + +static iron_weapon * ironweapons = NULL; + +void +add_ironweapon(const struct item_type * type, const struct item_type * rusty, float chance) +{ + iron_weapon * iweapon = malloc(sizeof(iron_weapon)); + iweapon->type = type; + iweapon->rusty = rusty; + iweapon->chance = chance; + iweapon->next = ironweapons; + ironweapons = iweapon; +} + +static int +sp_rosthauch(castorder *co) +{ + int n; + int success = 0; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + int force = (int)co->force; + spellparameter *pa = co->par; + + if (ironweapons==NULL) { + add_ironweapon(it_find("sword"), it_find("rustysword"), 1.0); + add_ironweapon(it_find("axe"), it_find("rustyaxe"), 1.0); + add_ironweapon(it_find("greatsword"), it_find("rustygreatsword"), 1.0); + add_ironweapon(it_find("halberd"), it_find("rustyhalberd"), 0.5f); +#ifndef NO_RUSTY_ARMOR + add_ironweapon(it_find("shield"), it_find("rustyshield"), 0.5f); + add_ironweapon(it_find("chainmail"), it_find("rustychainmail"), 0.2f); +#endif + } + + force = rand()%((int)(force * 10)) + force; + + /* fuer jede Einheit */ + for (n = 0; n < pa->length; n++) { + unit *u = pa->param[n]->data.u; + int ironweapon = 0; + iron_weapon * iweapon = ironweapons; + + if (force<=0) break; + if (pa->param[n]->flag & (TARGET_RESISTS|TARGET_NOTFOUND)) continue; + + for (;iweapon!=NULL;iweapon=iweapon->next) { + item ** ip = i_find(&u->items, iweapon->type); + if (*ip) { + int i = min((*ip)->number, force); + if (iweapon->chance<1.0) { + i = (int)(i*iweapon->chance); + } + if (i>0) { + force -= i; + ironweapon += i; + i_change(ip, iweapon->type, -i); + if (iweapon->rusty) { + i_change(&u->items, iweapon->rusty, i); + } + } + } + if (force<=0) break; + } + + if (ironweapon>0) { + /* {$mage mage} legt einen Rosthauch auf {target}. {amount} Waffen + * wurden vom Rost zerfressen */ + ADDMSG(&mage->faction->msgs, msg_message("rust_effect", + "mage target amount", mage, u, ironweapon)); + ADDMSG(&u->faction->msgs, msg_message("rust_effect", + "mage target amount", + cansee(u->faction, r, mage, 0) ? mage:NULL, u, ironweapon)); + success += ironweapon; + } else { + /* {$mage mage} legt einen Rosthauch auf {target}, doch der + * Rosthauch fand keine Nahrung */ + ADDMSG(&mage->faction->msgs, msg_message( + "rust_fail", "mage target", mage, u)); + } + } + /* in success stehen nun die insgesamt zerstörten Waffen. Im + * ungünstigsten Fall kann pro Stufe nur eine Waffe verzaubert werden, + * darum wird hier nur für alle Fälle in denen noch weniger Waffen + * betroffen wurden ein Kostennachlass gegeben */ + return min(success, cast_level); +} + + +/* ------------------------------------------------------------- */ +/* Name: Kälteschutz + * Stufe: 3 + * Kategorie: Einheit, positiv + * Gebiet: Gwyrrd + * + * Wirkung: + * schützt ein bis mehrere Einheiten mit bis zu Stufe*10 Insekten vor + * den Auswirkungen der Kälte. Sie können Gletscher betreten und dort + * ganz normal alles machen. Die Wirkung hält Stufe Wochen an + * Insekten haben in Gletschern den selben Malus wie in Bergen. Zu + * lange drin, nicht mehr ändern + * + * Flag: + * (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) + */ +/* Syntax: ZAUBER [STUFE n] "Kälteschutz" eh1 [eh2 [eh3 [...]]] */ + +static int +sp_kaelteschutz(castorder *co) +{ + unit *u; + int n, i = 0; + int men; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = max(cast_level, (int)force) + 1; + spellparameter *pa = co->par; + variant effect; + + force*=10; /* 10 Personen pro Force-Punkt */ + + /* für jede Einheit in der Kommandozeile */ + for (n = 0; n < pa->length; n++) { + if (force < 1) + break; + + if(pa->param[n]->flag == TARGET_RESISTS + || pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + u = pa->param[n]->data.u; + + if (force < u->number){ + men = (int)force; + } else { + men = u->number; + } + + effect.i = 1; + create_curse(mage, &u->attribs, ct_find("insectfur"), cast_level, + duration, effect, men); + + force -= u->number; + ADDMSG(&mage->faction->msgs, msg_message( + "heat_effect", "mage target", mage, 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)); + i = cast_level; + } + /* Erstattung? */ + return i; +} + +/* ------------------------------------------------------------- */ +/* Name: Verwünschung, Funkenregen, Naturfreund, ... + * Stufe: 1 + * Kategorie: Einheit, rein visuell + * Gebiet: Alle + * + * Wirkung: + * Die Einheit wird von einem magischen Effekt heimgesucht, der in ihrer + * Beschreibung auftaucht, aber nur visuellen Effekt hat. + * + * Flag: + * (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET) + */ +/* Syntax: ZAUBER "Funkenregen" eh1 */ + +static int +sp_sparkle(castorder *co) +{ + unit *u; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + int duration = cast_level+1; + variant effect; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + u = pa->param[0]->data.u; + effect.i = rand(); + create_curse(mage, &u->attribs, ct_find("sparkle"), cast_level, + duration, effect, u->number); + + ADDMSG(&mage->faction->msgs, msg_message( + "sparkle_effect", "mage target", mage, u)); + if (u->faction!=mage->faction) + ADDMSG(&u->faction->msgs, msg_message( + "sparkle_effect", "mage target", mage, u)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Eisengolem + * Stufe: 2 + * Kategorie: Beschwörung, positiv + * Gebiet: Gwyrrd + * Wirkung: + * Erschafft eine Einheit Eisengolems mit Stufe*8 Golems. Jeder Golem + * hat jede Runde eine Chance von 15% zu Staub zu zerfallen. Gibt man + * den Golems den Befehl 'mache Schwert/Bihänder' oder 'mache + * Schild/Kettenhemd/Plattenpanzer', so werden pro Golem 5 Eisenbarren + * verbaut und der Golem löst sich auf. + * + * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. + * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufällig + * treffen, so ist der Schaden fast immer tödlich. (Eisengolem: HP + * 50, AT 4, PA 2, Rüstung 2(KH), 2d10+4 TP, Magieresistenz 0.25) + * + * Golems nehmen nix an und geben nix. Sie bewegen sich immer nur 1 + * Region weit und ziehen aus Strassen keinen Nutzen. Ein Golem wiegt + * soviel wie ein Stein. Kann nicht im Sumpf gezaubert werden + * + * Flag: + * (SPELLLEVEL) + * + * #define GOLEM_IRON 4 + */ + +static int +sp_create_irongolem(castorder *co) +{ + unit *u2; + attrib *a; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int number = lovar(force*8*RESOURCE_QUANTITY); + if (number<1) number = 1; + + if (rterrain(r) == T_SWAMP){ + cmistake(mage, co->order, 188, MSG_MAGIC); + return 0; + } + + u2 = create_unit(r, mage->faction, number, new_race[RC_IRONGOLEM], 0, + LOC(mage->faction->locale, rc_name(new_race[RC_IRONGOLEM], 1)), mage); + + set_level(u2, SK_ARMORER, 1); + set_level(u2, SK_WEAPONSMITH, 1); + + a = a_new(&at_unitdissolve); + a->data.ca[0] = 0; + a->data.ca[1] = IRONGOLEM_CRUMBLE; + a_add(&u2->attribs, a); + + 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(new_race[RC_IRONGOLEM], 1)))); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Steingolem + * Stufe: 1 + * Kategorie: Beschwörung, positiv + * Gebiet: Gwyrrd + * Wirkung: + * Erschafft eine Einheit Steingolems mit Stufe*5 Golems. Jeder Golem + * hat jede Runde eine Chance von 10% zu Staub zu zerfallen. Gibt man + * den Golems den Befehl 'mache Burg' oder 'mache Strasse', so werden + * pro Golem 10 Steine verbaut und der Golem löst sich auf. + * + * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. + * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufällig + * treffen, so ist der Schaden fast immer tödlich. (Steingolem: HP 60, + * AT 4, PA 2, Rüstung 4(PP), 2d12+6 TP) + * + * Golems nehmen nix an und geben nix. Sie bewegen sich immer nur 1 + * Region weit und ziehen aus Strassen keinen Nutzen. Ein Golem wiegt + * soviel wie ein Stein. + * + * Kann nicht im Sumpf gezaubert werden + * + * Flag: + * (SPELLLEVEL) + * + * #define GOLEM_STONE 4 + */ +static int +sp_create_stonegolem(castorder *co) +{ + unit *u2; + attrib *a; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + int number = lovar(co->force*5*RESOURCE_QUANTITY); + if (number<1) number = 1; + + if (rterrain(r) == T_SWAMP){ + cmistake(mage, co->order, 188, MSG_MAGIC); + return 0; + } + + u2 = create_unit(r, mage->faction, number, new_race[RC_STONEGOLEM], 0, + LOC(mage->faction->locale, rc_name(new_race[RC_STONEGOLEM], 1)), mage); + set_level(u2, SK_ROAD_BUILDING, 1); + set_level(u2, SK_BUILDING, 1); + + a = a_new(&at_unitdissolve); + a->data.ca[0] = 0; + a->data.ca[1] = STONEGOLEM_CRUMBLE; + a_add(&u2->attribs, a); + + 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(new_race[RC_STONEGOLEM], 1)))); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Große Dürre + * Stufe: 17 + * Kategorie: Region, negativ + * Gebiet: Gwyrrd + * + * Wirkung: + * 50% alle Bauern, Pferde, Bäume sterben. + * Zu 25% terraform: Gletscher wird mit 50% zu Sumpf, sonst Ozean, + * Sumpf wird zu Steppe, Ebene zur Steppe, Steppe zur Wüste. + * Besonderheiten: + * neuer Terraintyp Steppe: + * 5000 Felder, 500 Bäume, Strasse: 250 Steine. Anlegen wie in Ebene + * möglich + * + * Flags: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ + +static void +destroy_all_roads(region *r) +{ + int i; + + for(i = 0; i < MAXDIRECTIONS; i++){ + rsetroad(r,(direction_t)i, 0); + } +} + +static int +sp_great_drought(castorder *co) +{ + building *b, *b2; + unit *u; + boolean terraform = false; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = 2; + variant effect; + + if(rterrain(r) == T_OCEAN ) { + cmistake(mage, co->order, 189, MSG_MAGIC); + /* TODO: vielleicht einen netten Patzer hier? */ + return 0; + } + + /* sterben */ + rsetpeasants(r, rpeasants(r)/2); /* evtl wuerfeln */ + rsettrees(r, 2, rtrees(r,2)/2); + rsettrees(r, 1, rtrees(r,1)/2); + rsettrees(r, 0, rtrees(r,0)/2); + rsethorses(r, rhorses(r)/2); + + /* Arbeitslohn = 1/4 */ + effect.i = 4; + create_curse(mage, &r->attribs, ct_find("drought"), force, duration, effect, 0); + + /* terraforming */ + if (rand() % 100 < 25){ + terraform = true; + + switch(rterrain(r)){ + case T_PLAIN: + rsetterrain(r, T_GRASSLAND); + destroy_all_roads(r); + break; + + case T_SWAMP: + rsetterrain(r, T_GRASSLAND); + destroy_all_roads(r); + break; + + case T_GRASSLAND: + rsetterrain(r, T_DESERT); + destroy_all_roads(r); + break; + + case T_GLACIER: +#if NEW_RESOURCEGROWTH == 0 + rsetiron(r, 0); + rsetlaen(r, -1); +#endif + if (rand() % 100 < 50){ + rsetterrain(r, T_SWAMP); + destroy_all_roads(r); + } else { /* Ozean */ + destroy_all_roads(r); + rsetterrain(r, T_OCEAN); + /* Einheiten dürfen hier auf keinen Fall gelöscht werden! */ + for (u = r->units; u; u = u->next) { + if (u->race != new_race[RC_SPELL] && u->ship == 0) { + set_number(u, 0); + } + } + for (b = r->buildings; b;){ + b2 = b->next; + destroy_building(b); + b = b2; + } + } + break; + + default: + terraform = false; + break; + } + } + + /* melden, 1x pro partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + + for (u = r->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + sprintf(buf, "%s ruft das Feuer der Sonne auf %s hinab.", + cansee(u->faction, r, mage, 0)? unitname(mage) : "Jemand", + regionname(r, u->faction)); + if (rterrain(r) != T_OCEAN){ + if(rterrain(r) == T_SWAMP && terraform){ + scat(" Eis schmilzt und verwandelt sich in Morast. Reißende " + "Ströme spülen die mageren Felder weg und ersäufen " + "Mensch und Tier. Was an Bauten nicht den Fluten zum Opfer " + "fiel, verschlingt der Morast. Die sengende Hitze verändert " + "die Region für immer."); + } else { + scat(" Die Felder verdorren und Pferde verdursten. Die Hungersnot " + "kostet vielen Bauern das Leben. Vertrocknete Bäume recken " + "ihre kahlen Zweige in den blauen Himmel, von dem " + "erbarmungslos die sengende Sonne brennt."); + if(terraform){ + scat(" Die Dürre veränderte die Region für immer."); + } + } + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + } else { /* ist Ozean */ + scat(" Das Eis zerbricht und eine gewaltige Flutwelle verschlingt" + "die Region."); + /* es kann gut sein, das in der Region niemand überlebt, also + * besser eine Globalmeldung */ + addmessage(0, u->faction, buf, MSG_EVENT, ML_IMPORTANT); + } + } + } + if (!fval(mage->faction, FL_DH)){ + ADDMSG(&mage->faction->msgs, msg_message( + "drought_effect", "mage region", mage, r)); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: 'Weg der Bäume' + * Stufe: 9 + * Kategorie: Teleport + * Gebiet: Gwyrrd + * Wirkung: + * Der Druide kann 5*Stufe GE in die astrale Ebene schicken. + * Der Druide wird nicht mitteleportiert, es sei denn, er gibt sich + * selbst mit an. + * Der Zauber funktioniert nur in Wäldern. + * + * Syntax: Zauber "Weg der Bäume" ... + * + * Flags: + * (UNITSPELL | SPELLLEVEL | TESTCANSEE) + */ +static int +sp_treewalkenter(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + spellparameter *pa = co->par; + double power = co->force; + int cast_level = co->level; + region *rt; + int remaining_cap; + int n; + int erfolg = 0; + + if (getplane(r) != 0) { + cmistake(mage, co->order, 190, MSG_MAGIC); + return 0; + } + + if (!r_isforest(r)) { + cmistake(mage, co->order, 191, MSG_MAGIC); + return 0; + } + + rt = r_standard_to_astral(r); + if (rt==NULL || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { + cmistake(mage, co->order, 192, MSG_MAGIC); + return 0; + } + + remaining_cap = (int)(power * 500); + + /* fuer jede Einheit */ + for (n = 0; n < pa->length; n++) { + unit * u = pa->param[n]->data.u; + spllprm * param = pa->param[n]; + + if (param->flag & (TARGET_RESISTS|TARGET_NOTFOUND)) { + continue; + } + + if (!ucontact(u, mage)) { + cmistake(mage, co->order, 73, MSG_MAGIC); + } else { + int w; + + if (!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + continue; + } + + w = weight(u); + if (remaining_cap - w < 0) { + ADDMSG(&mage->faction->msgs, msg_message("fail_tooheavy", + "command region unit target", co->order, r, mage, u)); + continue; + } + remaining_cap = remaining_cap - w; + move_unit(u, rt, NULL); + erfolg = cast_level; + + /* Meldungen in der Ausgangsregion */ + ADDMSG(&r->msgs, msg_message("astral_disappear", "unit", u)); + + /* Meldungen in der Zielregion */ + ADDMSG(&rt->msgs, msg_message("astral_appear", "unit", u)); + } + } + return erfolg; +} + +/* ------------------------------------------------------------- */ +/* Name: 'Sog des Lebens' + * Stufe: 9 + * Kategorie: Teleport + * Gebiet: Gwyrrd + * Wirkung: + * Der Druide kann 5*Stufe GE aus die astrale Ebene schicken. Der + * Druide wird nicht mitteleportiert, es sei denn, er gibt sich selbst + * mit an. + * Der Zauber funktioniert nur, wenn die Zielregion ein Wald ist. + * + * Syntax: Zauber "Sog des Lebens" ... + * + * Flags: + * (UNITSPELL|SPELLLEVEL) + */ +static int +sp_treewalkexit(castorder *co) +{ + region *rt; + region_list *rl, *rl2; + int tax, tay; + unit *u, *u2; + int remaining_cap; + int n; + int erfolg = 0; + region *r = co->rt; + unit *mage = (unit *)co->magician; + double power = co->force; + spellparameter *pa = co->par; + int cast_level = co->level; + + if(getplane(r) != get_astralplane()) { + cmistake(mage, co->order, 193, MSG_MAGIC); + return 0; + } + if(is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { + cmistake(mage, co->order, 192, MSG_MAGIC); + return 0; + } + + remaining_cap = (int)(power * 500); + + if(pa->param[0]->typ != SPP_REGION){ + report_failure(mage, co->order); + return 0; + } + + /* Koordinaten setzen und Region löschen für Überprüfung auf + * Gültigkeit */ + rt = pa->param[0]->data.r; + tax = rt->x; + tay = rt->y; + rt = NULL; + + rl = astralregions(r, inhabitable); + rt = 0; + + rl2 = rl; + while(rl2) { + if(rl2->data->x == tax && rl2->data->y == tay) { + rt = rl2->data; + break; + } + rl2 = rl2->next; + } + free_regionlist(rl); + + if(!rt) { + cmistake(mage, co->order, 195, MSG_MAGIC); + return 0; + } + + if (!r_isforest(rt)) { + cmistake(mage, co->order, 196, MSG_MAGIC); + return 0; + } + + /* für jede Einheit in der Kommandozeile */ + for (n = 1; n < pa->length; n++) { + if(pa->param[n]->flag == TARGET_RESISTS + || pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + u = pa->param[n]->data.u; + + if (!ucontact(u, mage)) { + sprintf(buf, "%s hat uns nicht kontaktiert.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + } else { + int w = weight(u); + if (!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + } else if(remaining_cap - w < 0) { + sprintf(buf, "%s ist zu schwer.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + } else { + remaining_cap = remaining_cap - w; + move_unit(u, rt, NULL); + erfolg = cast_level; + + /* Meldungen in der Ausgangsregion */ + + for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = r->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, r, u, 0)) { + sprintf(buf, "%s wird durchscheinend und verschwindet.", + unitname(u)); + addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + + /* Meldungen in der Zielregion */ + + for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = rt->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, rt, u, 0)) { + sprintf(buf, "%s erscheint plötzlich.", unitname(u)); + addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + } + } + } + return erfolg; +} + +static void +creation_message(unit * mage, const item_type * itype) +{ + ADDMSG(&mage->faction->msgs, msg_message("item_create_spell", + "mage number item", mage, 1, itype->rtype)); +} + +/* ------------------------------------------------------------- */ +/* Name: Heiliger Boden + * Stufe: 9 + * Kategorie: perm. Regionszauber + * Gebiet: Gwyrrd + * Wirkung: + * Es entstehen keine Untoten mehr, Untote betreten die Region + * nicht mehr. + * + * ZAUBER "Heiliger Boden" + * Flags: (0) + */ +static int +sp_holyground(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + curse * c; + message * msg = r_addmessage(r, mage->faction, msg_message("holyground", "mage", mage)); + msg_release(msg); + + c = create_curse(mage, &r->attribs, ct_find("holyground"), + power*power, 1, zero_effect, 0); + + curse_setflag(c, CURSE_NOAGE); + + a_removeall(&r->attribs, &at_deathcount); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Heimstein + * Stufe: 7 + * Kategorie: Artefakt + * Gebiet: Gwyrrd + * Wirkung: + * Die Burg kann nicht mehr durch Donnerbeben oder andere + * Gebäudezerstörenden Sprüche kaputt gemacht werden. Auch + * schützt der Zauber vor Belagerungskatapulten. + * + * ZAUBER Heimstein + * Flags: (0) + */ +static int +sp_homestone(castorder *co) +{ + unit *u; + curse * c; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + variant effect; + + if(!mage->building || mage->building->type != bt_find("castle")){ + cmistake(mage, co->order, 197, MSG_MAGIC); + return 0; + } + + c = create_curse(mage, &mage->building->attribs, ct_find("magicwalls"), + force*force, 1, zero_effect, 0); + + if (c==NULL) { + cmistake(mage, co->order, 206, MSG_MAGIC); + return 0; + } + curse_setflag(c, CURSE_NOAGE|CURSE_ONLYONE); + + /* Magieresistenz der Burg erhöht sich um 50% */ + effect.i = 50; + c = create_curse(mage, &mage->building->attribs, + ct_find("magicresistance"), force*force, 1, effect, 0); + curse_setflag(c, CURSE_NOAGE); + + /* melden, 1x pro Partei in der Burg */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + if (u->building == mage->building) { + sprintf(buf, "Mit einem Ritual bindet %s die magischen Kräfte " + "der Erde in die Mauern von %s", unitname(mage), + buildingname(mage->building)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + return cast_level; +} + + + +/* ------------------------------------------------------------- */ +/* Name: Dürre + * Stufe: 13 + * Kategorie: Region, negativ + * Gebiet: Gwyrrd + * Wirkung: + * temporär verändert sich das Baummaximum und die maximalen Felder in + * einer Region auf die Hälfte des normalen. + * Die Hälfte der Bäume verdorren und Pferde verdursten. + * Arbeiten bringt nur noch 1/4 des normalen Verdienstes + * + * Flags: + * (FARCASTING|REGIONSPELL|TESTRESISTANCE), + */ +static int +sp_drought(castorder *co) +{ + curse *c; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = (int)power+1; + + if(rterrain(r) == T_OCEAN ) { + cmistake(mage, co->order, 189, MSG_MAGIC); + /* TODO: vielleicht einen netten Patzer hier? */ + return 0; + } + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for(u = r->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s verflucht das Land, und eine Dürreperiode beginnt.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + if(!fval(mage->faction, FL_DH)){ + sprintf(buf, "%s verflucht das Land, und eine Dürreperiode beginnt.", + unitname(mage)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + + /* Wenn schon Duerre herrscht, dann setzen wir nur den Power-Level + * hoch (evtl dauert dann die Duerre laenger). Ansonsten volle + * Auswirkungen. + */ + c = get_curse(r->attribs, ct_find("drought")); + if (c) { + c->vigour = max(c->vigour, power); + c->duration = max(c->duration, (int)power); + } else { + variant effect; + /* Baeume und Pferde sterben */ + rsettrees(r, 2, rtrees(r,2)/2); + rsettrees(r, 1, rtrees(r,1)/2); + rsettrees(r, 0, rtrees(r,0)/2); + rsethorses(r, rhorses(r)/2); + + effect.i = 4; + create_curse(mage, &r->attribs, ct_find("drought"), power, duration, effect, 0); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Nebel der Verwirrung + * Stufe: 14 + * Kategorie: Region, negativ + * Gebiet: Gwyrrd + * Wirkung: + * Alle Regionen innerhalb eines Radius von ((Stufe-15)/2 aufgerundet) + * werden von einem verwirrenden Nebel bedeckt. Innerhalb des Nebels + * können keine Himmelsrichtungen mehr erkannt werden, alle Bewegungen + * erfolgen in eine zufällige Richtung. + * Die Gwyrrd-Variante wirkt nur auf Wälder und Ozeanregionen + * Flags: + * (FARCASTING | SPELLLEVEL) + * */ +static int +sp_fog_of_confusion(castorder *co) +{ + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + double range; + int duration; + region_list *rl,*rl2; + + range = (power-11)/3-1; + duration = (int)((power-11)/1.5)+1; + + rl = all_in_range(r, (short)range, NULL); + + for(rl2 = rl; rl2; rl2 = rl2->next) { + curse * c; + variant effect; + + if(rterrain(rl2->data) != T_OCEAN + && !r_isforest(rl2->data)) continue; + + /* Magieresistenz jeder Region prüfen */ + if (target_resists_magic(mage, r, TYP_REGION, 0)){ + report_failure(mage, co->order); + continue; + } + + effect.i = cast_level*5; + c = create_curse(mage, &rl2->data->attribs, + ct_find("disorientationzone"), power, duration, effect, 0); + /* Soll der schon in der Zauberrunde wirken? */ + curse_setflag(c, CURSE_ISNEW); + + for (u = rl2->data->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = rl2->data->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(rl2->data, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + if(!fval(mage->faction, FL_DH)){ + sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", + unitname(mage)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + } + free_regionlist(rl); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Bergwächter + * Stufe: 9 + * Gebiet: Gwyrrd + * Kategorie: Beschwörung, negativ + * + * Wirkung: + * Erschafft in Bergen oder Gletschern einen Wächter, der durch bewachen + * den Eisen/Laen-Abbau für nicht-Allierte verhindert. Bergwächter + * verhindern auch Abbau durch getarnte/unsichtbare Einheiten und lassen + * sich auch durch Belagerungen nicht aufhalten. + * + * (Ansonsten in economic.c:manufacture() entsprechend anpassen). + * + * Fähigkeiten (factypes.c): 50% Magieresistenz, 25 HP, 4d4 Schaden, + * 4 Rüstung (=PP) + * Flags: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_ironkeeper(castorder *co) +{ + unit *keeper; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + if (rterrain(r) != T_MOUNTAIN && rterrain(r) != T_GLACIER) { + report_failure(mage, co->order); + return 0; + } + + keeper = create_unit(r, mage->faction, 1, new_race[RC_IRONKEEPER], 0, "Bergwächter", mage); + + /*keeper->age = cast_level + 2;*/ + guard(keeper, GUARD_MINING); + fset(keeper, UFL_ISNEW); + keeper->status = ST_AVOID; /* kaempft nicht */ + /* Parteitarnen, damit man nicht sofort weiß, wer dahinter steckt */ + fset(keeper, UFL_PARTEITARNUNG); + { + trigger * tkill = trigger_killunit(keeper); + add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level+2, tkill)); + } + + sprintf(buf, "%s beschwört einen Bergwächter.", unitname(mage)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Sturmwind - Beschwöre einen Sturmelementar + * Stufe: 6 + * Gebiet: Gwyrrd + * + * Wirkung: + * Verdoppelt Geschwindigkeit aller angegebener Schiffe fuer diese + * Runde. Kombinierbar mit "Günstige Winde", aber nicht mit + * "Luftschiff". + * + * Anstelle des alten ship->enchanted benutzen wir einen kurzfristigen + * Curse. Das ist zwar ein wenig aufwendiger, aber weitaus flexibler + * und erlaubt es zB, die Dauer später problemlos zu verändern. + * + * Flags: + * (SHIPSPELL|ONSHIPCAST|OCEANCASTABLE|TESTRESISTANCE) + */ + +static int +sp_stormwinds(castorder *co) +{ + faction *f; + ship *sh; + unit *u; + int erfolg = 0; + region *r = co->rt; + unit *mage = (unit *)co->magician; + double power = co->force; + spellparameter *pa = co->par; + int n, force = (int)power; + + /* melden vorbereiten */ + for(f = factions; f; f = f->next ) freset(f, FL_DH); + + for (n = 0; n < pa->length; n++) { + if (force<=0) break; + + if(pa->param[n]->flag == TARGET_RESISTS + || pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + sh = pa->param[n]->data.sh; + + /* mit C_SHIP_NODRIFT haben wir kein Problem */ + if(is_cursed(sh->attribs, C_SHIP_FLYING, 0) ) { + sprintf(buf, "Es ist zu gefährlich, diesen Zauber auf ein " + "fliegendes Schiff zu legen."); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + continue; + } + if(is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { + sprintf(buf, "Auf %s befindet sich bereits ein Zauber", shipname(sh)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + continue; + } + + /* Duration = 1, nur diese Runde */ + create_curse(mage, &sh->attribs, ct_find("stormwind"), power, 1, zero_effect, 0); + /* Da der Spruch nur diese Runde wirkt wird er nie im Report + * erscheinen */ + erfolg++; + force--; + + /* melden vorbereiten: */ + for(u = r->units; u; u = u->next ) { + if(u->ship != sh ) /* nur den Schiffsbesatzungen! */ + continue; + + fset(u->faction, FL_DH); + } + + } + /* melden, 1x pro Partei auf Schiff und für den Magier */ + fset(mage->faction, FL_DH); + for(u = r->units; u; u = u->next ) { + if(fval(u->faction, FL_DH)) { + freset(u->faction, FL_DH); + if (erfolg > 0){ + sprintf(buf, "%s beschwört einen magischen Wind, der die Schiffe " + "über das Wasser treibt.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + return erfolg; +} + + +/* ------------------------------------------------------------- */ +/* Name: Donnerbeben + * Stufe: 6 + * Gebiet: Gwyrrd + * + * Wirkung: + * Zerstört Stufe*10 "Steineinheiten" aller Gebäude der Region, aber nie + * mehr als 25% des gesamten Gebäudes (aber natürlich mindestens ein + * Stein). + * + * Flags: + * (FARCASTING|REGIONSPELL|TESTRESISTANCE) + */ +static int +sp_earthquake(castorder *co) +{ + int kaputt; + building *burg; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + for (burg = r->buildings; burg; burg = burg->next){ + if(burg->size == 0 ) + continue; + + /* Schutzzauber */ + if(is_cursed(burg->attribs, C_MAGICWALLS, 0)) + continue; + + /* Magieresistenz */ + if (target_resists_magic(mage, burg, TYP_BUILDING, 0)) + continue; + + kaputt = min(10 * cast_level, burg->size / 4); + kaputt = max(kaputt, 1); + burg->size -= kaputt; + if(burg->size == 0 ) { + /* alle Einheiten hinausbefördern */ + for(u = r->units; u; u = u->next ) { + if(u->building == burg ) { + u->building = 0; + freset(u, UFL_OWNER); + } + } + /* TODO: sollten die Insassen nicht Schaden nehmen? */ + destroy_building(burg); + } + } + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s läßt die Erde in %s erzittern.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", + regionname(r, u->faction)); + + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + return cast_level; +} + + +/* ------------------------------------------------------------- */ +/* CHAOS / M_CHAOS / Draig */ +/* ------------------------------------------------------------- */ +void +patzer_peasantmob(castorder *co) +{ + int anteil = 6, n; + unit *u; + attrib *a; + region *r; + unit *mage = (unit *)co->magician; + + if (mage->region->land){ + r = mage->region; + } else { + r = co->rt; + } + + if (r->land) { + faction * f = findfaction(MONSTER_FACTION); + const struct locale * lang = f->locale; + + anteil += rand() % 4; + n = rpeasants(r) * anteil / 10; + rsetpeasants(r, rpeasants(r) - n); + assert(rpeasants(r) >= 0); + + u = createunit(r, f, n, new_race[RC_PEASANT]); + fset(u, UFL_ISNEW); + set_string(&u->name, "Bauernmob"); + /* guard(u, GUARD_ALL); hier zu früh! Befehl BEWACHE setzten */ + addlist(&u->orders, parse_order(LOC(lang, keywords[K_GUARD]), lang)); + set_order(&u->thisorder, default_order(lang)); + a = a_new(&at_unitdissolve); + a->data.ca[0] = 1; /* An rpeasants(r). */ + a->data.ca[1] = 10; /* 10% */ + a_add(&u->attribs, a); + a_add(&u->attribs, make_hate(mage)); + + sprintf(buf, "Ein Bauernmob erhebt sich und macht Jagd auf Schwarzmagier."); + addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); + } + return; +} + + +/* ------------------------------------------------------------- */ +/* Name: Waldbrand + * Stufe: 10 + * Kategorie: Region, negativ + * Gebiet: Draig + * Wirkung: + * Vernichtet 10-80% aller Baeume in der Region. Kann sich auf benachbarte + * Regionen ausbreiten, wenn diese (stark) bewaldet sind. Für jeweils + * 10 verbrannte Baeume in der Startregion gibts es eine 1%-Chance, dass + * sich das Feuer auf stark bewaldete Nachbarregionen ausdehnt, auf + * bewaldeten mit halb so hoher Wahrscheinlichkeit. Dort verbrennen + * dann prozentual halbsoviele bzw ein viertel soviele Baeume wie in der + * Startregion. + * + * Im Extremfall: 1250 Baeume in Region, 80% davon verbrennen (1000). + * Dann breitet es sich mit 100% Chance in stark bewaldete Regionen + * aus, mit 50% in bewaldete. Dort verbrennen dann 40% bzw 20% der Baeume. + * Weiter als eine Nachbarregion breitet sich dass Feuer nicht aus. + * + * Sinn: Ein Feuer in einer "stark bewaldeten" Wueste hat so trotzdem kaum + * eine Chance, sich weiter auszubreiten, waehrend ein Brand in einem Wald + * sich fast mit Sicherheit weiter ausbreitet. + * + * Flags: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_forest_fire(castorder *co) +{ + unit *u; + region *nr; + direction_t i; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double probability; + double percentage = (rand() % 8 + 1) * 0.1; /* 10 - 80% */ + + int vernichtet_schoesslinge = (int)(rtrees(r, 1) * percentage); + int destroyed = (int)(rtrees(r, 2) * percentage); + + if (destroyed<1) { + cmistake(mage, co->order, 198, MSG_MAGIC); + return 0; + } + + rsettrees(r, 2, rtrees(r,2) - destroyed); + rsettrees(r, 1, rtrees(r,1) - vernichtet_schoesslinge); + probability = destroyed * 0.001; /* Chance, dass es sich ausbreitet */ + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + + for(u = r->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s erzeugt eine verheerende Feuersbrunst. %d %s " + "den Flammen zum Opfer.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", + destroyed, + destroyed == 1 ? "Baum fiel" : "Bäume fielen"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + if(!fval(mage->faction, FL_DH)){ + sprintf(buf, "%s erzeugt eine verheerende Feuersbrunst. %d %s " + "den Flammen zum Opfer.", unitname(mage), destroyed+vernichtet_schoesslinge, + destroyed+vernichtet_schoesslinge == 1 ? "Baum fiel" : "Bäume fielen"); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + + for(i = 0; i < MAXDIRECTIONS; i++ ) { + nr = rconnect(r, i); + assert(nr); + destroyed = 0; + vernichtet_schoesslinge = 0; + + if(rtrees(nr,2) + rtrees(nr,1) >= 800) { + if (chance(probability)) { + destroyed = (int)(rtrees(nr,2) * percentage/2); + vernichtet_schoesslinge = (int)(rtrees(nr,1) * percentage/2); + } + } else if (rtrees(nr,2) + rtrees(nr,1) >= 600) { + if (chance(probability/2)) { + destroyed = (int)(rtrees(nr,2) * percentage/4); + vernichtet_schoesslinge = (int)(rtrees(nr,1) * percentage/4); + } + } + + if (destroyed > 0 || vernichtet_schoesslinge > 0) { + message * m = msg_message("forestfire_spread", "region next trees", + r, nr, destroyed+vernichtet_schoesslinge); + + add_message(&r->msgs, m); + add_message(&mage->faction->msgs, m); + msg_release(m); + + rsettrees(nr, 2, rtrees(nr,2) - destroyed); + rsettrees(nr, 1, rtrees(nr,1) - vernichtet_schoesslinge); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Chaosfluch + * Stufe: 5 + * Gebiet: Draig + * Kategorie: (Antimagie) Kraftreduzierer, Einheit, negativ + * Wirkung: + * Auf einen Magier gezaubert verhindert/erschwert dieser Chaosfluch + * das Zaubern. Patzer werden warscheinlicher. + * Jeder Zauber muss erst gegen den Wiederstand des Fluchs gezaubert + * werden und schwächt dessen Antimagiewiederstand um 1. + * Wirkt max(Stufe(Magier) - Stufe(Ziel), rand(3)) Wochen + * Patzer: + * Magier wird selbst betroffen + * + * Flags: + * (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE | TESTRESISTANCE) + * + */ +static int +sp_fumblecurse(castorder *co) +{ + unit *target; + int rx, sx; + int duration; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + variant effect; + curse * c; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; + + rx = rand()%3; + sx = cast_level - effskill(target, SK_MAGIC); + duration = max(sx, rx) + 1; + + effect.i = (int)(force/2); + c = create_curse(mage, &target->attribs, ct_find("fumble"), + force, duration, effect, 0); + if (c == NULL) { + report_failure(mage, co->order); + return 0; + } + + curse_setflag(c, CURSE_ONLYONE); + ADDMSG(&target->faction->msgs, msg_message( + "fumblecurse", "unit region", target, target->region)); + + return cast_level; +} + +void +patzer_fumblecurse(castorder *co) +{ + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (cast_level/2)+1; + variant effect; + curse * c; + + effect.i = (int)(force/2); + c = create_curse(mage, &mage->attribs, ct_find("fumble"), force, + duration, effect, 0); + if (c!=NULL) { + ADDMSG(&mage->faction->msgs, msg_message( + "magic_fumble", "unit region command", + mage, mage->region, co->order)); + curse_setflag(c, CURSE_ONLYONE); + } + return; +} + +/* ------------------------------------------------------------- */ +/* Name: Drachenruf + * Stufe: 11 + * Gebiet: Draig + * Kategorie: Monster, Beschwörung, negativ + * + * Wirkung: + * In einer Wüste, Sumpf oder Gletscher gezaubert kann innerhalb der + * nächsten 6 Runden ein bis 6 Dracheneinheiten bis Größe Wyrm + * entstehen. + * + * Mit Stufe 12-15 erscheinen Jung- oder normaler Drachen, mit Stufe + * 16+ erscheinen normale Drachen oder Wyrme. + * + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ + +static int +sp_summondragon(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + unit *u; + int cast_level = co->level; + double power = co->force; + region_list *rl,*rl2; + faction *f; + int time; + int number; + const race * race; + + f = findfaction(MONSTER_FACTION); + + if(rterrain(r) != T_SWAMP && rterrain(r) != T_DESERT + && rterrain(r) != T_GLACIER){ + report_failure(mage, co->order); + return 0; + } + + for(time = 1; time < 7; time++){ + if (rand()%100 < 25){ + switch(rand()%3){ + case 0: + race = new_race[RC_WYRM]; + number = 1; + break; + + case 1: + race = new_race[RC_DRAGON]; + number = 2; + break; + + case 2: + default: + race = new_race[RC_FIREDRAGON]; + number = 6; + break; + } + { + trigger * tsummon = trigger_createunit(r, f, race, number); + add_trigger(&r->attribs, "timer", trigger_timeout(time, tsummon)); + } + } + } + + rl = all_in_range(r, (short)power, NULL); + + for(rl2 = rl; rl2; rl2 = rl2->next) { + for(u = rl2->data->units; u; u = u->next) { + if (u->race == new_race[RC_WYRM] || u->race == new_race[RC_DRAGON]) { + attrib * a = a_find(u->attribs, &at_targetregion); + if (!a) { + a = a_add(&u->attribs, make_targetregion(co->rt)); + } else { + a->data.v = co->rt; + } + sprintf(buf, "Kommt aus: %s, Will nach: %s", regionname(rl2->data, u->faction), regionname(co->rt, u->faction)); + usetprivate(u, buf); + } + } + } + + ADDMSG(&mage->faction->msgs, msg_message( + "summondragon", "unit region command region", + mage, mage->region, co->order, co->rt)); + + free_regionlist(rl); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Feuerwand + * Stufe: + * Gebiet: Draig + * Kategorie: Region, negativ + * Flag: + * Kosten: SPC_LINEAR + * Aura: + * Komponenten: + * + * Wirkung: + * eine Wand aus Feuer entsteht in der angegebenen Richtung + * + * Was für eine Wirkung hat die? + */ + +typedef struct wallcurse { + curse * buddy; + border * wall; +} wallcurse; + +void +wall_vigour(curse* c, double delta) +{ + wallcurse * wc = (wallcurse*)c->data.v; + assert(wc->buddy->vigour==c->vigour); + wc->buddy->vigour += delta; + if (wc->buddy->vigour<=0) { + erase_border(wc->wall); + wc->wall = NULL; + ((wallcurse*)wc->buddy->data.v)->wall = NULL; + } +} + +const curse_type ct_firewall = { + "Feuerwand", + CURSETYP_NORM, 0, (M_DURATION | M_VIGOUR | NO_MERGE), + "Eine Feuerwand blockiert die Ein- und Ausreise", + NULL, /* curseinfo */ + wall_vigour /* change_vigour */ +}; + +void +cw_init(attrib * a) { + curse * c; + curse_init(a); + c = (curse*)a->data.v; + c->data.v = calloc(sizeof(wallcurse), 1); +} + +void +cw_write(const attrib * a, FILE * f) { + border * b = ((wallcurse*)((curse*)a->data.v)->data.v)->wall; + curse_write(a, f); + fprintf(f, "%d ", b->id); +} + +typedef struct bresvole { + unsigned int id; + curse * self; +} bresolve; + +static void * resolve_buddy(variant data); + +static int +cw_read(attrib * a, FILE * f) +{ + bresolve * br = calloc(sizeof(bresolve), 1); + curse * c = (curse*)a->data.v; + wallcurse * wc = (wallcurse*)c->data.v; + variant var; + + curse_read(a, f); + br->self = c; + fscanf(f, "%u ", &br->id); + + var.i = br->id; + ur_add(var, (void**)&wc->wall, resolve_borderid); + + var.v = br; + ur_add(var, (void**)&wc->buddy, resolve_buddy); + return AT_READ_OK; +} + +attrib_type at_cursewall = +{ + "cursewall", + cw_init, + curse_done, + curse_age, + cw_write, + cw_read, + ATF_CURSE +}; + +static void * +resolve_buddy(variant data) +{ + bresolve * br = (bresolve*)data.v; + border * b = find_border(br->id); + if (b && b->from && b->to) { + attrib * a = a_find(b->from->attribs, &at_cursewall); + while (a && a->data.v!=br->self) { + curse * c = (curse*)a->data.v; + wallcurse * wc = (wallcurse*)c->data.v; + if (wc->wall->id==br->id) break; + a = a->nexttype; + } + if (!a) { + a = a_find(b->to->attribs, &at_cursewall); + while (a && a->data.v!=br->self) { + curse * c = (curse*)a->data.v; + wallcurse * wc = (wallcurse*)c->data.v; + if (wc->wall->id==br->id) break; + a = a->nexttype; + } + } + if (a) { + curse * c = (curse*)a->data.v; + free(br); + return c; + } + } + return NULL; +} + + +static const char * +fire_name(const border * b, const region * r, const faction * f, int gflags) +{ + unused(f); + unused(r); + unused(b); + if (gflags & GF_ARTICLE) + return "eine Feuerwand"; + else + return "Feuerwand"; +} + +static void +wall_init(border * b) +{ + b->data.v = calloc(sizeof(wall_data), 1); +} + +static void +wall_destroy(border * b) +{ + free(b->data.v); +} + +static void +wall_read(border * b, FILE * f) +{ + wall_data * fd = (wall_data*)b->data.v; + variant mno; + assert(fd); + fscanf(f, "%d %d ", &mno.i, &fd->force); + fd->mage = findunitg(mno.i, NULL); + fd->active = true; + if (!fd->mage) { + ur_add(mno, (void**)&fd->mage, resolve_unit); + } +} + +static void +wall_write(const border * b, FILE * f) +{ + wall_data * fd = (wall_data*)b->data.v; + fprintf(f, "%d %d ", fd->mage?fd->mage->no:0, fd->force); +} + +static region * +wall_move(const border * b, struct unit * u, struct region * from, struct region * to, boolean routing) +{ + wall_data * fd = (wall_data*)b->data.v; + if (!routing && fd->active) { + int hp = dice(3, fd->force) * u->number; + hp = min (u->hp, hp); + u->hp -= hp; + if (u->hp) { + ADDMSG(&u->faction->msgs, msg_message("firewall_damage", + "region unit", from, u)); + } + else ADDMSG(&u->faction->msgs, msg_message("firewall_death", "region unit", from, u)); + if (u->number>u->hp) { + scale_number(u, u->hp); + u->hp = u->number; + } + } + return to; +} + +border_type bt_firewall = { + "firewall", VAR_VOIDPTR, + b_transparent, /* transparent */ + wall_init, /* init */ + wall_destroy, /* destroy */ + wall_read, /* read */ + wall_write, /* write */ + b_blocknone, /* block */ + fire_name, /* name */ + b_rvisible, /* rvisible */ + b_finvisible, /* fvisible */ + b_uinvisible, /* uvisible */ + NULL, + wall_move +}; + +static int +sp_firewall(castorder *co) +{ + border * b; + wall_data * fd; + attrib * a; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + direction_t dir; + region * r2; + + dir = finddirection(pa->param[0]->data.s, mage->faction->locale); + if (dirorder); + return 0; + } + + if (!r2 || r2==r) { + report_failure(mage, co->order); + return 0; + } + + b = get_borders(r, r2); + while (b!=NULL) { + if (b->type == &bt_firewall) break; + b = b->next; + } + if (b==NULL) { + b = new_border(&bt_firewall, r, r2); + fd = (wall_data*)b->data.v; + fd->force = (int)(force/2+0.5); + fd->mage = mage; + fd->active = false; + } else { + fd = (wall_data*)b->data.v; + fd->force = (int)max(fd->force, force/2+0.5); + } + + a = a_find(b->attribs, &at_countdown); + if (a==NULL) { + a = a_add(&b->attribs, a_new(&at_countdown)); + a->data.i = cast_level; + } else { + a->data.i = max(a->data.i, cast_level); + } + + /* 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); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ + +static const char * +wisps_name(const border * b, const region * r, const faction * f, int gflags) +{ + unused(f); + unused(r); + unused(b); + if (gflags & GF_ARTICLE) + return "eine Gruppe von Irrlichtern"; + else + return "Irrlichter"; +} + +static region * +wisps_move(const border * b, struct unit * u, struct region * from, struct region * next, boolean routing) +{ + direction_t reldir = reldirection(from, next); + wall_data * wd = (wall_data*)b->data.v; + assert(reldir!=D_SPECIAL); + + if (routing && wd->active) { + /* pick left and right region: */ + region * rl = rconnect(from, (direction_t)((reldir+MAXDIRECTIONS-1)%MAXDIRECTIONS)); + region * rr = rconnect(from, (direction_t)((reldir+1)%MAXDIRECTIONS)); + int j = rand() % 3; + if (j==1 && rl && landregion(rterrain(rl))==landregion(rterrain(next))) return rl; + if (j==2 && rr && landregion(rterrain(rr))==landregion(rterrain(next))) return rr; + } + return next; +} + +border_type bt_wisps = { + "wisps", VAR_VOIDPTR, + b_transparent, /* transparent */ + wall_init, /* init */ + wall_destroy, /* destroy */ + wall_read, /* read */ + wall_write, /* write */ + b_blocknone, /* block */ + wisps_name, /* name */ + b_rvisible, /* rvisible */ + b_fvisible, /* fvisible */ + b_uvisible, /* uvisible */ + NULL, /* visible */ + wisps_move +}; + +static int +sp_wisps(castorder *co) +{ + border * b; + wall_data * fd; + region * r2; + direction_t dir; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + + dir = finddirection(pa->param[0]->data.s, mage->faction->locale); + r2 = rconnect(r, dir); + + if (!r2) { + report_failure(mage, co->order); + return 0; + } + + b = new_border(&bt_wisps, r, r2); + fd = (wall_data*)b->data.v; + fd->force = (int)(force/2+0.5); + fd->mage = mage; + fd->active = false; + + a_add(&b->attribs, a_new(&at_countdown))->data.i = cast_level; + + /* melden, 1x pro Partei */ + { + message * seen = msg_message("wisps_effect", "mage region", mage, r); + message * unseen = msg_message("wisps_effect", "mage region", NULL, r); + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Unheilige Kraft + * Stufe: 10 + * Gebiet: Draig + * Kategorie: Untote Einheit, positiv + * + * Wirkung: + * transformiert (Stufe)W10 Untote in ihre stärkere Form + * + * + * Flag: + * (SPELLLEVEL | TESTCANSEE) + */ + +static int +sp_unholypower(castorder *co) +{ + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + int i; + int n; + int wounds; + + n = dice((int)co->force, 10); + + for (i = 0; i < pa->length && n > 0; i++) { + const race * target_race; + unit *u; + + if(pa->param[i]->flag == TARGET_RESISTS + || pa->param[i]->flag == TARGET_NOTFOUND) + continue; + + u = pa->param[i]->data.u; + + switch (old_race(u->race)) { + case RC_SKELETON: + target_race = new_race[RC_SKELETON_LORD]; + break; + case RC_ZOMBIE: + target_race = new_race[RC_ZOMBIE_LORD]; + break; + case RC_GHOUL: + target_race = new_race[RC_GHOUL_LORD]; + break; + default: + cmistake(mage, co->order, 284, MSG_MAGIC); + continue; + } + /* Untote heilen nicht, darum den neuen Untoten maximale hp geben + * und vorhandene Wunden abziehen */ + wounds = unit_max_hp(u)*u->number - u->hp; + + if(u->number <= n) { + n -= u->number; + u->race = u->irace = target_race; + u->hp = unit_max_hp(u)*u->number - wounds; + ADDMSG(&co->rt->msgs, msg_message("unholypower_effect", + "mage target race", mage, u, target_race)); + } else { + unit *un; + + /* Wird hoffentlich niemals vorkommen. Es gibt im Source + * vermutlich eine ganze Reihe von Stellen, wo das nicht + * korrekt abgefangen wird. Besser (aber nicht gerade einfach) + * wäre es, eine solche Konstruktion irgendwie zu kapseln. */ + if(fval(u, UFL_LOCKED) || fval(u, UFL_HUNGER) + || is_cursed(u->attribs, C_SLAVE, 0)) { + cmistake(mage, co->order, 74, MSG_MAGIC); + continue; + } + /* Verletzungsanteil der transferierten Personen berechnen */ + wounds = wounds*n/u->number; + + un = create_unit(co->rt, u->faction, 0, target_race, 0, NULL, u); + transfermen(u, un, n); + un->hp = unit_max_hp(un)*n - wounds; + ADDMSG(&co->rt->msgs, msg_message("unholypower_limitedeffect", + "mage target race amount", + mage, u, target_race, n)); + n = 0; + } + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Todeswolke + * Stufe: 11 + * Gebiet: Draig + * Kategorie: Region, negativ + * + * Wirkung: + * Personen in der Region verlieren stufe/2 Trefferpunkte pro Runde. + * Dauer force/2 + * Wirkt gegen MR + * Rüstung wirkt nicht + * Patzer: + * Magier gerät in den Staub und verliert zufällige Zahl von HP bis + * auf max(hp,2) + * Besonderheiten: + * Nicht als curse implementiert, was schlecht ist - man kann dadurch + * kein dispell machen. Wegen fix unter Zeitdruck erstmal nicht zu + * ändern... + * Missbrauchsmöglichkeit: + * Hat der Magier mehr HP als Rasse des Feindes (extrem: Dämon/Goblin) + * so kann er per Farcasting durch mehrmaliges Zaubern eine + * Nachbarregion auslöschen. Darum sollte dieser Spruch nur einmal auf + * eine Region gelegt werden können. + * + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ + +typedef struct dc_data { + region * r; + unit * mage; + double strength; + int countdown; + boolean active; +} dc_data; + +static void +dc_initialize(struct attrib *a) +{ + dc_data * data = (dc_data *)malloc(sizeof(dc_data)); + a->data.v = data; + data->active = true; +} + +static void +dc_finalize(struct attrib * a) +{ + free(a->data.v); +} + +static int +dc_age(struct attrib * a) +/* age returns 0 if the attribute needs to be removed, !=0 otherwise */ +{ + dc_data * data = (dc_data *)a->data.v; + region * r = data->r; + unit ** up = &r->units; + unit * mage = data->mage; + unit * u; + + if (mage==NULL || mage->number==0) { + /* if the mage disappears, so does the spell. */ + return 0; + } + + if (data->active) while (*up!=NULL) { + unit * u = *up; + double damage = data->strength * u->number; + + freset(u->faction, FL_DH); + if (target_resists_magic(mage, u, TYP_UNIT, 0)){ + continue; + } + + /* Reduziert durch Magieresistenz */ + damage *= (1.0 - magic_resistance(u)); + change_hitpoints(u, -(int)damage); + + if (*up==u) up=&u->next; + } + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + ADDMSG(&u->faction->msgs, msg_message("deathcloud_effect", + "mage region", cansee(u->faction, r, mage, 0) ? mage : NULL, r)); + } + } + + if (!fval(mage->faction, FL_DH)){ + ADDMSG(&mage->faction->msgs, msg_message("deathcloud_effect", + "mage region", mage, r)); + } + + return --data->countdown; +} + +static void +dc_write(const struct attrib * a, FILE* F) +{ + const dc_data * data = (const dc_data *)a->data.v; + fprintf(F, "%d %lf ", data->countdown, data->strength); + write_unit_reference(data->mage, F); + write_region_reference(data->r, F); +} + +static int +dc_read(struct attrib * a, FILE* F) +/* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ +{ + dc_data * data = (dc_data *)a->data.v; + fscanf(F, "%d %lf ", &data->countdown, &data->strength); + read_unit_reference(&data->mage, F); + return read_region_reference(&data->r, F); +} + +attrib_type at_deathcloud = { + "zauber_todeswolke", dc_initialize, dc_finalize, dc_age, dc_write, dc_read +}; + +static attrib * +mk_deathcloud(unit * mage, region * r, double strength, int duration) +{ + attrib * a = a_new(&at_deathcloud); + dc_data * data = (dc_data *)a->data.v; + + data->countdown = duration; + data->r = r; + data->mage = mage; + data->strength = strength; + data->active = false; + return a; +} + +static int +sp_deathcloud(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + + attrib *a = a_find(r->attribs, &at_deathcloud); + + if (a!=NULL) { + report_failure(mage, co->order); + return 0; + } + + a_add(&r->attribs, mk_deathcloud(mage, r, co->force/2, co->level)); + + return co->level; +} + +void +patzer_deathcloud(castorder *co) +{ + unit *mage = (unit *)co->magician; + int hp = (mage->hp - 2); + + change_hitpoints(mage, -rand()%hp); + + ADDMSG(&mage->faction->msgs, msg_message( + "magic_fumble", "unit region command", + mage, mage->region, co->order)); + + return; +} + +/* ------------------------------------------------------------- */ +/* Name: Pest + * Stufe: 7 + * Gebiet: Draig + * Wirkung: + * ruft eine Pest in der Region hervor. + * Flags: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + * Syntax: ZAUBER [REGION x y] "Pest" + */ +static int +sp_plague(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + plagues(r, true); + + ADDMSG(&mage->faction->msgs, msg_message("plague_spell", + "region mage", r, mage)); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Beschwöre Schattendämon + * Stufe: 8 + * Gebiet: Draig + * Kategorie: Beschwörung, positiv + * Wirkung: + * Der Magier beschwört Stufe^2 Schattendämonen. + * Schattendämonen haben Tarnung = (Magie_Magier+ Tarnung_Magier)/2 und + * Wahrnehmung 1. Sie haben einen Attacke-Bonus von 8, einen + * Verteidigungsbonus von 11 und machen 2d3 Schaden. Sie entziehen bei + * einem Treffer dem Getroffenen einen Attacke- oder + * Verteidigungspunkt. (50% Chance.) Sie haben 25 Hitpoints und + * Rüstungsschutz 3. + * Flag: + * (SPELLLEVEL) + */ +static int +sp_summonshadow(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + unit *u; + int val; + + u = create_unit(r, mage->faction, (int)(force*force), new_race[RC_SHADOW], 0, NULL, mage); + + /* Bekommen Tarnung = (Magie+Tarnung)/2 und Wahrnehmung 1. */ + val = get_level(mage, SK_MAGIC) + get_level(mage, SK_STEALTH); + + set_level(u, SK_STEALTH, val); + set_level(u, SK_OBSERVATION, 1); + + sprintf(buf, "%s beschwört %d Dämonen aus dem Reich der Schatten.", + unitname(mage), (int)(force*force)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Beschwöre Schattenmeister + * Stufe: 12 + * Gebiet: Draig + * Kategorie: Beschwörung, positiv + * Wirkung: + * Diese höheren Schattendämonen sind erheblich gefährlicher als die + * einfachen Schattendämonen. Sie haben Tarnung entsprechend dem + * Magietalent des Beschwörer-1 und Wahrnehmung 5, 75 HP, + * Rüstungsschutz 4, Attacke-Bonus 11 und Verteidigungsbonus 13, machen + * bei einem Treffer 2d4 Schaden, entziehen einen Stärkepunkt und + * entziehen 5 Talenttage in einem zufälligen Talent. + * Stufe^2 Dämonen. + * + * Flag: + * (SPELLLEVEL) + * */ +static int +sp_summonshadowlords(castorder *co) +{ + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + + u = create_unit(r, mage->faction, (int)(force*force), new_race[RC_SHADOWLORD], 0, NULL, mage); + + /* Bekommen Tarnung = Magie und Wahrnehmung 5. */ + set_level(u, SK_STEALTH, get_level(mage, SK_MAGIC)); + set_level(u, SK_OBSERVATION, 5); + sprintf(buf, "%s beschwört %d Schattenmeister.", + unitname(mage), (int)(force*force)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Chaossog + * Stufe: 14 + * Gebiet: Draig + * Kategorie: Teleport + * Wirkung: + * Durch das Opfern von 200 Bauern kann der Chaosmagier ein Tor zur + * astralen Welt öffnen. Das Tor kann im Folgemonat verwendet werden, + * es löst sich am Ende des Folgemonats auf. + * + * Flag: (0) + */ +static int +sp_chaossuction(castorder *co) +{ + unit *u; + region *rt; + faction *f; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + if (getplane(r)!=get_normalplane()) { + /* Der Zauber funktioniert nur in der materiellen Welt. */ + cmistake(mage, co->order, 190, MSG_MAGIC); + return 0; + } + + rt = r_standard_to_astral(r); + + if (rt==NULL || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { + /* Hier gibt es keine Verbindung zur astralen Welt.*/ + cmistake(mage, co->order, 216, MSG_MAGIC); + return 0; + } + + create_special_direction(r, rt, 2, + "Ein Wirbel aus reinem Chaos zieht über die Region", + "Wirbel"); + create_special_direction(rt, r, 2, + "Ein Wirbel aus reinem Chaos zieht über die Region", + "Wirbel"); + new_border(&bt_chaosgate, r, rt); + + for (f = factions; f; f = f->next) freset(f, FL_DH); + for (u = r->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + sprintf(buf, "%s öffnete ein Chaostor.", + cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + for (u = rt->units; u; u = u->next) freset(u->faction, FL_DH); + + for (u = rt->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + addmessage(r, u->faction, "Ein Wirbel aus blendendem Licht erscheint.", + MSG_EVENT, ML_INFO); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Magic Boost - Gabe des Chaos + * Stufe: 3 + * Gebiet: Draig + * Kategorie: Einheit, positiv + * + * Wirkung: + * Erhöht die maximalen Magiepunkte und die monatliche Regeneration auf + * das doppelte. Dauer: 4 Wochen Danach sinkt beides auf die Hälfte des + * normalen ab. + * Dauer: 6 Wochen + * Patzer: + * permanenter Stufen- (Talenttage), Regenerations- oder maxMP-Verlust + * Besonderheiten: + * Patzer können während der Zauberdauer häufiger auftreten derzeit + * +10% + * + * Flag: + * (ONSHIPCAST) + */ + +static int +sp_magicboost(castorder *co) +{ + curse * c; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + variant effect; + trigger * tsummon; + static const curse_type * ct_auraboost; + static const curse_type * ct_magicboost; + + if (!ct_auraboost) { + ct_auraboost = ct_find("auraboost"); + ct_magicboost = ct_find("magicboost"); + assert(ct_auraboost!=NULL); + assert(ct_magicboost!=NULL); + } + /* fehler, wenn schon ein boost */ + if(is_cursed(mage->attribs, C_MBOOST, 0) == true){ + report_failure(mage, co->order); + return 0; + } + + effect.i = 6; + c = create_curse(mage, &mage->attribs, ct_magicboost, power, 10, effect, 1); + /* kann nicht durch Antimagie beeinflusst werden */ + curse_setflag(c, CURSE_IMMUNE); + + /* one aura boost with 200% aura now: */ + effect.i = 200; + c = create_curse(mage, &mage->attribs, ct_auraboost, power, 4, effect, 1); + + /* and one aura boost with 50% aura in 5 weeks: */ + tsummon = trigger_createcurse(mage, mage, ct_auraboost, power, 6, 50, 1); + add_trigger(&mage->attribs, "timer", trigger_timeout(5, tsummon)); + + ADDMSG(&mage->faction->msgs, msg_message("magicboost_effect", + "unit region command", mage, mage->region, co->order)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: kleines Blutopfer + * Stufe: 4 + * Gebiet: Draig + * Kategorie: Einheit, positiv + * + * Wirkung: + * Hitpoints to Aura: + * skill < 8 = 4:1 + * skill < 12 = 3:1 + * skill < 15 = 2:1 + * skill < 18 = 1:2 + * skill > = 2:1 + * Patzer: + * permanenter HP verlust + * + * Flag: + * (ONSHIPCAST) + */ +static int +sp_bloodsacrifice(castorder *co) +{ + unit *mage = (unit *)co->magician; + int cast_level = co->level; + int aura; + int skill = eff_skill(mage, SK_MAGIC, mage->region); + int hp = (int)(co->force*8); + + if (hp <= 0) { + report_failure(mage, co->order); + return 0; + } + + aura = lovar(hp); + + if (skill < 8) { + aura /= 4; + } else if (skill < 12){ + aura /= 3; + } else if (skill < 15){ + aura /= 2; + /* von 15 bis 17 ist hp = aura */ + } else if (skill > 17){ + aura *= 2; + } + + if (aura <= 0){ + report_failure(mage, co->order); + return 0; + } + + /* sicherheitshalber gibs hier einen HP gratis. sonst schaffen es + * garantiert ne ganze reihe von leuten ihren Magier damit umzubringen */ + mage->hp++; + change_spellpoints(mage, aura); + ADDMSG(&mage->faction->msgs, + msg_message("sp_bloodsacrifice_effect", + "unit region command amount", + mage, mage->region, co->order, aura)); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Totenruf - Mächte des Todes + * Stufe: 6 + * Gebiet: Draig + * Kategorie: Beschwörung, positiv + * Flag: FARCASTING + * Wirkung: + * Untote aus deathcounther ziehen, bis Stufe*10 Stück + * + * Patzer: + * Erzeugt Monsteruntote + */ +static int +sp_summonundead(castorder *co) +{ + int undead; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + int force = (int)(co->force*10); + const race * race = new_race[RC_SKELETON]; + + if (!r->land || deathcount(r) == 0) { + sprintf(buf, "%s in %s: In %s sind keine Gräber.", unitname(mage), + regionname(mage->region, mage->faction), regionname(r, mage->faction)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + undead = min(deathcount(r), 2 + lovar(force)); + + if(cast_level <= 8) { + race = new_race[RC_SKELETON]; + } else if(cast_level <= 12) { + race = new_race[RC_ZOMBIE]; + } else { + race = new_race[RC_GHOUL]; + } + + u = create_unit(r, mage->faction, undead, race, 0, NULL, mage); + make_undead_unit(u); + + sprintf(buf, "%s erweckt %d Untote aus ihren Gräbern.", + unitname(mage), undead); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + + for(u = r->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s stört die Ruhe der Toten", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Astraler Sog + * Stufe: 9 + * Gebiet: Draig + * Kategorie: Region, negativ + * Wirkung: + * Allen Magier in der betroffenen Region wird eine Teil ihrer + * Magischen Kraft in die Gefilde des Chaos entzogen Jeder Magier im + * Einflussbereich verliert Stufe(Zaubernden)*5% seiner Magiepunkte. + * Keine Regeneration in der Woche (fehlt noch) + * + * Flag: + * (REGIONSPELL | TESTRESISTANCE) + */ + +static int +sp_auraleak(castorder *co) +{ + int lost_aura; + double lost; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + lost = min(0.95, cast_level * 0.05); + + for(u = r->units; u; u = u->next) { + if (is_mage(u)){ + /* Magieresistenz Einheit? Bei gegenerischen Magiern nur sehr + * geringe Chance auf Erfolg wg erhöhter MR, würde Spruch sinnlos + * machen */ + lost_aura = (int)(get_spellpoints(u)*lost); + change_spellpoints(u, -lost_aura); + } + freset(u->faction, FL_DH); + } + for (u = r->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + if (cansee(u->faction, r, mage, 0)) { + sprintf(buf, "%s rief in %s einen Riss in dem Gefüge der Magie " + "hervor, der alle magische Kraft aus der Region riss.", + unitname(mage), regionname(r, u->faction)); + } else { + sprintf(buf, "In %s entstand ein Riss in dem Gefüge der Magie, " + "der alle magische Kraft aus der Region riss.", + regionname(r, u->faction)); + } + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* BARDE - CERDDOR*/ +/* ------------------------------------------------------------- */ +/* Name: Plappermaul + * Stufe: 4 + * Gebiet: Cerddor + * Kategorie: Einheit + * + * Wirkung: + * Einheit ausspionieren. Gibt auch Zauber und Kampfstatus aus. Wirkt + * gegen Magieresistenz. Ist diese zu hoch, so wird der Zauber entdeckt + * (Meldung) und der Zauberer erhält nur die Talente, nicht die Werte + * der Einheit und auch keine Zauber. + * + * Flag: + * (UNITSPELL | ONETARGET | TESTCANSEE) + */ +static int +sp_babbler(castorder *co) +{ + unit *target; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; + + if (target->faction == mage->faction){ + /* Die Einheit ist eine der unsrigen */ + cmistake(mage, co->order, 45, MSG_MAGIC); + } + + /* Magieresistenz Unit */ + if (target_resists_magic(mage, target, TYP_UNIT, 0)){ + spy_message(5, mage, target); + sprintf(buf, "%s hat einen feuchtfröhlichen Abend in der Taverne " + "verbracht. Ausser einem fürchterlichen Brummschädel ist da auch " + "noch das dumme Gefühl %s seine ganze Lebensgeschichte " + "erzählt zu haben.", unitname(target), + cansee(target->faction, r, mage, 0)? "irgendjemanden":unitname(mage)); + addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); + + } else { + spy_message(100, mage, target); + sprintf(buf, "%s hat einen feuchtfröhlichen Abend in der Taverne " + "verbracht. Ausser einem fürchterlichen Brummschädel ist da auch " + "noch das dumme Gefühl die ganze Taverne mit seiner Lebensgeschichte " + "unterhalten zu haben.", unitname(target)); + addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Magie analysieren - Gebäude, Schiffe, Region + * Name: Lied des Ortes analysieren + * Stufe: 8 + * Gebiet: Cerddor + * + * Wirkung: + * Zeigt die Verzauberungen eines Objekts an (curse->name, + * curse::info). Aus der Differenz Spruchstärke und Curse->vigour + * ergibt sich die Chance den Spruch zu identifizieren ((force - + * c->vigour)*10 + 100 %). + * + * Flag: + * (SPELLLEVEL|ONSHIPCAST) + */ +static int +sp_analysesong_obj(castorder *co) +{ + int obj; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + + obj = pa->param[0]->typ; + + switch(obj) { + case SPP_REGION: + magicanalyse_region(r, mage, force); + break; + + case SPP_BUILDING: + { + building *b = pa->param[0]->data.b; + magicanalyse_building(b, mage, force); + break; + } + case SPP_SHIP: + { + ship * sh = pa->param[0]->data.sh; + magicanalyse_ship(sh, mage, force); + break; + } + default: + /* Syntax fehlerhaft */ + return 0; + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Gesang des Lebens analysieren + * Name: Magie analysieren - Unit + * Stufe: 5 + * Gebiet: Cerddor + * Wirkung: + * Zeigt die Verzauberungen eines Objekts an (curse->name, + * curse::info). Aus der Differenz Spruchstärke und Curse->vigour + * ergibt sich die Chance den Spruch zu identifizieren ((force - + * c->vigour)*10 + 100 %). + * + * Flag: + * (UNITSPELL|ONSHIPCAST|ONETARGET|TESTCANSEE) + */ +static int +sp_analysesong_unit(castorder *co) +{ + unit *u; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + u = pa->param[0]->data.u; + + magicanalyse_unit(u, mage, force); + + return cast_level; +} +/* ------------------------------------------------------------- */ +/* Name: Charming + * Stufe: 13 + * Gebiet: Cerddor + * Flag: UNITSPELL + * Wirkung: + * bezauberte Einheit wechselt 'virtuell' die Partei und führt fremde + * Befehle aus. + * Dauer: 3 - force+2 Wochen + * Wirkt gegen Magieresistenz + * + * wirkt auf eine Einheit mit maximal Talent Personen normal. Für jede + * zusätzliche Person gibt es einen Bonus auf Magieresistenz, also auf + * nichtgelingen, von 10%. + * + * Das höchste Talent der Einheit darf maximal so hoch sein wie das + * Magietalent des Magiers. Für jeden Talentpunkt mehr gibt es einen + * Bonus auf Magieresistenz von 15%, was dazu führt, das bei +2 Stufen + * die Magiersistenz bei 90% liegt. + * + * Migrantenzählung muss Einheit überspringen + * + * Attackiere verbieten + * Flags: + * (UNITSPELL | ONETARGET | TESTCANSEE) + */ +static int +sp_charmingsong(castorder *co) +{ + unit *target; + int duration; + skill_t i; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + int resist_bonus = 0; + int tb = 0; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; + + /* Auf eigene Einheiten versucht zu zaubern? Garantiert Tippfehler */ + if (target->faction == mage->faction){ + /* Die Einheit ist eine der unsrigen */ + cmistake(mage, co->order, 45, MSG_MAGIC); + } + + /* Magieresistensbonus für mehr als Stufe Personen */ + if (target->number > force) { + resist_bonus += (int)((target->number - force) * 10); + } + /* Magieresistensbonus für höhere Talentwerte */ + for(i = 0; i < MAXSKILLS; i++){ + int sk = effskill(target, i); + if (tb < sk) tb = sk; + } + tb -= effskill(mage, SK_MAGIC); + if(tb > 0){ + resist_bonus += tb * 15; + } + /* Magieresistenz */ + if (target_resists_magic(mage, target, TYP_UNIT, resist_bonus)) { + report_failure(mage, co->order); + sprintf(buf, "%s fühlt sich einen Moment lang benommen und desorientiert.", + unitname(target)); + addmessage(target->region, target->faction, buf, MSG_EVENT, ML_WARN); + return 0; + } + + duration = 3 + rand()%(int)force; + { + trigger * trestore = trigger_changefaction(target, target->faction); + /* läuft die Dauer ab, setze Partei zurück */ + add_trigger(&target->attribs, "timer", trigger_timeout(duration, trestore)); + /* wird die alte Partei von Target aufgelöst, dann auch diese Einheit */ + add_trigger(&target->faction->attribs, "destroy", trigger_killunit(target)); + /* wird die neue Partei von Target aufgelöst, dann auch diese Einheit */ + add_trigger(&mage->faction->attribs, "destroy", trigger_killunit(target)); + } + /* sperre ATTACKIERE, GIB PERSON und überspringe Migranten */ + create_curse(mage, &target->attribs, ct_find("slavery"), force, duration, zero_effect, 0); + + /* setze Partei um und lösche langen Befehl aus Sicherheitsgründen */ + u_setfaction(target,mage->faction); + set_order(&target->thisorder, NULL); + + /* setze Parteitarnung, damit nicht sofort klar ist, wer dahinter + * steckt */ + fset(target, UFL_PARTEITARNUNG); + + sprintf(buf, "%s gelingt es %s zu verzaubern. %s wird für etwa %d " + "Wochen unseren Befehlen gehorchen.", unitname(mage), + unitname(target), unitname(target), duration); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Gesang des wachen Geistes + * Stufe: 10 + * Gebiet: Cerddor + * Kosten: SPC_LEVEL + * Wirkung: + * Bringt einmaligen Bonus von +15% auf Magieresistenz. Wirkt auf alle + * Aliierten (HELFE BEWACHE) in der Region. + * Dauert Stufe Wochen an, ist nicht kumulativ. + * Flag: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_song_resistmagic(castorder *co) +{ + variant mr_bonus; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + + mr_bonus.i = 15; + create_curse(mage, &r->attribs, ct_find("goodmagicresistancezone"), + force, duration, mr_bonus, 0); + + /* Erfolg melden */ + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_effect", "unit region command", mage, + mage->region, co->order)); + + return cast_level; +} +/* ------------------------------------------------------------- */ +/* Name: Gesang des schwachen Geistes + * Stufe: 12 + * Gebiet: Cerddor + * Wirkung: + * Bringt einmaligen Malus von -15% auf Magieresistenz. + * Wirkt auf alle Nicht-Aliierten (HELFE BEWACHE) in der Region. + * Dauert Stufe Wochen an, ist nicht kumulativ. + * Flag: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_song_susceptmagic(castorder *co) +{ + variant mr_malus; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + + mr_malus.i = 15; + create_curse(mage, &r->attribs, ct_find("badmagicresistancezone"), + force, duration, mr_malus, 0); + + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_effect", "unit region command", mage, + mage->region, co->order)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Aufruhr beschwichtigen + * Stufe: 15 + * Gebiet: Cerddor + * Flag: FARCASTING + * Wirkung: + * zerstreut einen Monsterbauernmob, Antimagie zu 'Aufruhr + * verursachen' + */ + +static int +sp_rallypeasantmob(castorder *co) +{ + unit *u, *un; + int erfolg = 0; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + /* TODO + remove_allcurse(&r->attribs, C_RIOT, 0); + */ + + for (u = r->units; u; u = un){ + un = u->next; + if (u->faction->no == MONSTER_FACTION && u->race == new_race[RC_PEASANT]){ + rsetpeasants(r, rpeasants(r) + u->number); + rsetmoney(r, rmoney(r) + get_money(u)); + set_money(u, 0); + setguard(u, GUARD_NONE); + set_number(u, 0); + erfolg = cast_level; + } + } + + if (erfolg){ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for(u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s besänftigt den Bauernaufstand in %s.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", + regionname(r, u->faction)); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); + } + } + if (!fval(mage->faction, FL_DH)){ + sprintf(buf, "%s besänftigt den Bauernaufstand in %s.", + unitname(mage), regionname(r, u->faction)); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); + } + } else { + sprintf(buf, "Der Bauernaufstand in %s hatte sich bereits verlaufen.", + regionname(r, u->faction)); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); + } + return erfolg; +} + +/* ------------------------------------------------------------- */ +/* Name: Aufruhr verursachen + * Stufe: 16 + * Gebiet: Cerddor + * Wirkung: + * Wiegelt 60% bis 90% der Bauern einer Region auf. Bauern werden ein + * großer Mob, der zur Monsterpartei gehört und die Region bewacht. + * Regionssilber sollte auch nicht durch Unterhaltung gewonnen werden + * können. + * + * Fehlt: Triggeraktion: löste Bauernmob auf und gib alles an Region, + * dann können die Bauernmobs ihr Silber mitnehmen und bleiben x + * Wochen bestehen + * + * alternativ: Lösen sich langsam wieder auf + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_raisepeasantmob(castorder *co) +{ + unit *u; + attrib *a; + int n; + variant anteil; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + + anteil.i = 6 + (rand()%4); + + n = rpeasants(r) * anteil.i / 10; + n = max(0, n); + n = min(n, rpeasants(r)); + + if(n <= 0){ + report_failure(mage, co->order); + return 0; + } + + rsetpeasants(r, rpeasants(r) - n); + assert(rpeasants(r) >= 0); + + u = createunit(r, findfaction(MONSTER_FACTION), n, new_race[RC_PEASANT]); + fset(u, UFL_ISNEW); + set_string(&u->name, "Aufgebrachte Bauern"); + guard(u, GUARD_ALL); + a = a_new(&at_unitdissolve); + a->data.ca[0] = 1; /* An rpeasants(r). */ + a->data.ca[1] = 15; /* 15% */ + a_add(&u->attribs, a); + + create_curse(mage, &r->attribs, ct_find("riotzone"), cast_level, duration, anteil, 0); + + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + ADDMSG(&u->faction->msgs, msg_message( + "sp_raisepeasantmob_effect", "mage region", + cansee(u->faction, r, mage, 0) ? mage : NULL, r )); + } + } + if (!fval(mage->faction, FL_DH)){ + ADDMSG(&mage->faction->msgs, msg_message( + "sp_raisepeasantmob_effect", "mage region", mage, r)); + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Ritual der Aufnahme / Migrantenwerben + * Stufe: 9 + * Gebiet: Cerddor + * Wirkung: + * Bis zu Stufe Personen fremder Rasse können angeworben werden. Die + * angeworbene Einheit muss kontaktieren. Keine teuren Talente + * + * Flag: + * (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE) + */ +static int +sp_migranten(castorder *co) +{ + unit *target; + order * ord; + int kontaktiert = 0; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + const spell *sp = co->sp; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; /* Zieleinheit */ + + /* Personen unserer Rasse können problemlos normal übergeben werden */ + if (target->race == mage->faction->race){ + /* u ist von unserer Art, das Ritual wäre verschwendete Aura. */ + ADDMSG(&mage->faction->msgs, msg_message( + "sp_migranten_fail1", "unit region command target", mage, + mage->region, co->order, target)); + } + /* Auf eigene Einheiten versucht zu zaubern? Garantiert Tippfehler */ + if (target->faction == mage->faction){ + cmistake(mage, co->order, 45, MSG_MAGIC); + } + + /* Keine Monstereinheiten */ + if (!playerrace(target->race)){ + sprintf(buf, "%s kann nicht auf Monster gezaubert werden.", + spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); + return 0; + } + /* niemand mit teurem Talent */ + if (teure_talente(target)) { + sprintf(buf, "%s hat unaufkündbare Bindungen an seine alte Partei.", + unitname(target)); + addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); + return 0; + } + /* maximal Stufe Personen */ + if (target->number > cast_level + || target->number > max_spellpoints(r, mage)) + { + sprintf(buf, "%s in %s: 'ZAUBER %s': So viele Personen übersteigen " + "meine Kräfte.", unitname(mage), regionname(mage->region, mage->faction), + spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); + } + + /* Kontakt prüfen (aus alter Teleportroutine übernommen) */ + { + /* Nun kommt etwas reichlich krankes, um den + * KONTAKTIERE-Befehl des Ziels zu überprüfen. */ + + for (ord = target->orders; ord; ord = ord->next) { + if (get_keyword(ord) == K_CONTACT) { + const char *c; + /* So weit, so gut. S->s ist also ein KONTAKTIERE. Nun gilt es, + * herauszufinden, wer kontaktiert wird. Das ist nicht trivial. + * Zuerst muß der Parameter herausoperiert werden. */ + /* Leerzeichen finden */ + + init_tokens(ord); + skip_token(); + c = getstrtoken(); + + /* Wenn ein Leerzeichen da ist, ist *c != 0 und zeigt auf das + * Leerzeichen. */ + + if (c!=NULL) { + int kontakt = atoi36(c); + + if (kontakt == mage->no) { + kontaktiert = 1; + break; + } + } + } + } + } + + if (kontaktiert == 0) { + ADDMSG(&mage->faction->msgs, msg_message("spellfail::contact", + "mage region command target", mage, mage->region, co->order, + target)); + return 0; + } + u_setfaction(target,mage->faction); + set_order(&target->thisorder, NULL); + + /* Erfolg melden */ + ADDMSG(&mage->faction->msgs, msg_message("sp_migranten", + "unit region command target", mage, mage->region, co->order, target)); + + return target->number; +} + +/* ------------------------------------------------------------- */ +/* Name: Gesang der Friedfertigkeit + * Stufe: 12 + * Gebiet: Cerddor + * Wirkung: + * verhindert jede Attacke für lovar(Stufe/2) Runden + */ + +static int +sp_song_of_peace(castorder *co) +{ + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = 2 + lovar(force/2); + + create_curse(mage,&r->attribs, ct_find("peacezone"), force, duration, zero_effect, 0); + + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + if (cansee(u->faction, r, mage, 0)){ + sprintf(buf, "%s's Gesangskunst begeistert die Leute. Die " + "friedfertige Stimmung des Lieds überträgt sich auf alle " + "Zuhörer. Einige werfen ihre Waffen weg.", unitname(mage)); + }else{ + sprintf(buf, "In der Luft liegt ein wunderschönes Lied, dessen " + "friedfertiger Stimmung sich niemand entziehen kann. " + "Einige Leute werfen sogar ihre Waffen weg."); + } + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + return cast_level; + +} + +/* ------------------------------------------------------------- */ +/* Name: Hohes Lied der Gaukelei + * Stufe: 2 + * Gebiet: Cerddor + * Wirkung: + * Das Unterhaltungsmaximum steigt von 20% auf 40% des + * Regionsvermögens. Der Spruch hält Stufe Wochen an + */ + +static int +sp_generous(castorder *co) +{ + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + variant effect; + + if(is_cursed(r->attribs, C_DEPRESSION, 0)){ + sprintf(buf, "%s in %s: Die Stimmung in %s ist so schlecht, das " + "niemand auf den Zauber reagiert.", unitname(mage), + regionname(mage->region, mage->faction), regionname(r, mage->faction)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + effect.i = 2; + create_curse(mage,&r->attribs, ct_find("generous"), force, duration, effect, 0); + + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + if (cansee(u->faction, r, mage, 0)){ + sprintf(buf, "%s's Gesangskunst begeistert die Leute. Die " + "fröhliche und ausgelassene Stimmung der Lieder überträgt " + "sich auf alle Zuhörer.", unitname(mage)); + }else{ + sprintf(buf, "Die Darbietungen eines fahrenden Gauklers begeistern " + "die Leute. Die fröhliche und ausgelassene Stimmung seiner " + "Lieder überträgt sich auf alle Zuhörer."); + } + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Anwerbung + * Stufe: 4 + * Gebiet: Cerddor + * Wirkung: + * Bauern schliessen sich der eigenen Partei an + * ist zusätzlich zur Rekrutierungsmenge in der Region + * */ + +static int +sp_recruit(castorder *co) +{ + unit *u; + region *r = co->rt; + int n, maxp = rpeasants(r); + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + faction *f = mage->faction; + + if (maxp == 0) { + report_failure(mage, co->order); + return 0; + } + /* Immer noch zuviel auf niedrigen Stufen. Deshalb die Rekrutierungskosten + * mit einfliessen lassen und dafür den Exponenten etwas größer. + * Wenn die Rekrutierungskosten deutlich höher sind als der Faktor, + * ist das Verhältniss von ausgegebene Aura pro Bauer bei Stufe 2 + * ein mehrfaches von Stufe 1, denn in beiden Fällen gibt es nur 1 + * Bauer, nur die Kosten steigen. */ + n = (int)((pow(force, 1.6) * 100)/f->race->recruitcost); + if (f->race==new_race[RC_URUK]) { + n = min(2*maxp, n); + n = max(n, 1); + rsetpeasants(r, maxp - (n+1) / 2); + } else { + n = min(maxp, n); + n = max(n, 1); + rsetpeasants(r, maxp - n); + } + + u = create_unit(r, f, n, f->race, 0, (n == 1 ? "Bauer" : "Bauern"), mage); + set_order(&u->thisorder, default_order(f->locale)); + + sprintf(buf, "%s konnte %d %s anwerben", unitname(mage), n, + n == 1 ? "Bauer" : "Bauern"); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + if (f->race==new_race[RC_URUK]) n = (n+1) / 2; + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Wanderprediger - Große Anwerbung + * Stufe: 14 + * Gebiet: Cerddor + * Wirkung: + * Bauern schliessen sich der eigenen Partei an + * ist zusätzlich zur Rekrutierungsmenge in der Region + * */ + +static int +sp_bigrecruit(castorder *co) +{ + unit *u; + region *r = co->rt; + int n, maxp = rpeasants(r); + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + faction *f = mage->faction; + + if (maxp <= 0) { + report_failure(mage, co->order); + return 0; + } + /* Für vergleichbare Erfolge bei unterschiedlichen Rassen die + * Rekrutierungskosten mit einfliessen lassen. */ + + n = (int)force + lovar((force * force * 1000)/f->race->recruitcost); + if (f->race==new_race[RC_URUK]) { + n = min(2*maxp, n); + n = max(n, 1); + rsetpeasants(r, maxp - (n+1) / 2); + } else { + n = min(maxp, n); + n = max(n, 1); + rsetpeasants(r, maxp - n); + } + + u = create_unit(r, f, n, f->race, 0, (n == 1 ? "Bauer" : "Bauern"), mage); + set_order(&u->thisorder, default_order(f->locale)); + + sprintf(buf, "%s konnte %d %s anwerben", unitname(mage), n, + n == 1 ? "Bauer" : "Bauern"); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Aushorchen + * Stufe: 7 + * Gebiet: Cerddor + * Wirkung: + * Erliegt die Einheit dem Zauber, so wird sie dem Magier alles + * erzählen, was sie über die gefragte Region weiß. Ist in der Region + * niemand ihrer Partei, so weiß sie nichts zu berichten. Auch kann + * sie nur das erzählen, was sie selber sehen könnte. + * Flags: + * (UNITSPELL | ONETARGET | TESTCANSEE) + */ + +/* restistenz der einheit prüfen */ +static int +sp_pump(castorder *co) +{ + unit *u, *target; + region *rt; + boolean see = false; + region *r = co->rt; + unit *mage = (unit *)co->magician; + spellparameter *pa = co->par; + int cast_level = co->level; + const spell *sp = co->sp; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; /* Zieleinheit */ + + if (fval(target->race, RCF_UNDEAD)) { + sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", + spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_EVENT, ML_WARN); + return 0; + } + if (is_magic_resistant(mage, target, 0) || target->faction->no == MONSTER_FACTION) { + report_failure(mage, co->order); + return 0; + } + + rt = pa->param[1]->data.r; + + for (u = rt->units; u; u = u->next){ + if(u->faction == target->faction) + see = true; + } + + if (see == false){ + sprintf(buf, "%s horcht %s über %s aus, aber %s wusste nichts zu " + "berichten.", unitname(mage), unitname(target), regionname(rt, mage->faction), + unitname(target)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level/2; + } else { + sprintf(buf, "%s horcht %s über %s aus.", unitname(mage), + unitname(target), regionname(rt, mage->faction)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + + u = createunit(rt, mage->faction, RS_FARVISION, new_race[RC_SPELL]); + set_string(&u->name, "Zauber: Aushorchen"); + u->age = 2; + set_level(u, SK_OBSERVATION, eff_skill(target, SK_OBSERVATION, u->region)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Verführung + * Stufe: 6 + * Gebiet: Cerddor + * Wirkung: + * Betört eine Einheit, so das sie ihm den größten Teil ihres Bargelds + * und 50% ihres Besitzes schenkt. Sie behält jedoch immer soviel, wie + * sie zum überleben braucht. Wirkt gegen Magieresistenz. + * min(Stufe*1000$, u->money - maintenace) + * Von jedem Item wird 50% abgerundet ermittelt und übergeben. Dazu + * kommt Itemzahl%2 mit 50% chance + * + * Flags: + * (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE) + */ +static int +sp_seduce(castorder *co) +{ + unit *target; + int loot; + item **itmp; + region *r = co->rt; + unit *mage = (unit *)co->magician; + spellparameter *pa = co->par; + int cast_level = co->level; + const spell *sp = co->sp; + double force = co->force; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; /* Zieleinheit */ + + if (fval(target->race, RCF_UNDEAD)) { + sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", + spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); + return 0; + } + + /* Erfolgsmeldung */ + sprintf(buf, "%s schenkt %s ", unitname(target), unitname(mage)); + + loot = min(cast_level * 1000, get_money(target) - (MAINTENANCE*target->number)); + loot = max(loot, 0); + change_money(mage, loot); + change_money(target, -loot); + + if(loot > 0){ + icat(loot); + } else { + scat("kein"); + } + scat(" Silber"); + itmp=&target->items; + while (*itmp) { + item * itm = *itmp; + loot = itm->number/2; + if (itm->number % 2) { + loot += rand() % 2; + } + if (loot > 0) { + loot = (int)min(loot, force * 5); + scat(", "); + icat(loot); + scat(" "); + scat(locale_string(mage->faction->locale, resourcename(itm->type->rtype, (loot==1)?0:GR_PLURAL))); + i_change(&mage->items, itm->type, loot); + i_change(&target->items, itm->type, -loot); + } + if (*itmp==itm) itmp=&itm->next; + } + scat("."); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + sprintf(buf, "%s verfiel dem Glücksspiel und hat fast sein ganzes Hab " + "und Gut verspielt.", unitname(target)); + addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Monster friedlich stimmen + * Stufe: 6 + * Gebiet: Cerddor + * Wirkung: + * verhindert Angriffe des bezauberten Monsters auf die Partei des + * Barden für Stufe Wochen. Nicht übertragbar, dh Verbündete werden vom + * Monster natürlich noch angegriffen. Wirkt nicht gegen Untote + * Jede Einheit kann maximal unter einem Beherrschungszauber dieser Art + * stehen, dh wird auf die selbe Einheit dieser Zauber von einem + * anderen Magier nochmal gezaubert, schlägt der Zauber fehl. + * + * Flags: + * (UNITSPELL | ONSHIPCAST | ONETARGET | TESTRESISTANCE | TESTCANSEE) + */ + +static int +sp_calm_monster(castorder *co) +{ + curse * c; + unit *target; + region *r = co->rt; + unit *mage = (unit *)co->magician; + spellparameter *pa = co->par; + int cast_level = co->level; + double force = co->force; + const spell *sp = co->sp; + variant effect; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; /* Zieleinheit */ + + if (fval(target->race, RCF_UNDEAD)) { + sprintf(buf, "%s kann nicht auf Untote gezaubert werden.", + spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_WARN); + return 0; + } + + effect.i = mage->faction->subscription; + c = create_curse(mage, &target->attribs, ct_find("calmmonster"), force, + (int)force, effect, 0); + if (c==NULL) { + report_failure(mage, co->order); + return 0; + } + /* Nur ein Beherrschungszauber pro Unit */ + curse_setflag(c, CURSE_ONLYONE); + + sprintf(buf, "%s besänftigt %s.", unitname(mage), unitname(target)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: schaler Wein + * Stufe: 7 + * Gebiet: Cerddor + * Wirkung: + * wird gegen Magieresistenz gezaubert Das Opfer vergisst bis zu + * Talenttage seines höchsten Talentes und tut die Woche nix. + * Nachfolgende Zauber sind erschwert. + * Wirkt auf bis zu 10 Personen in der Einheit + * + * Flags: + * (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE) + */ + +static int +sp_headache(castorder *co) +{ + skill * smax = NULL; + int i; + unit *target; + region *r = co->rt; + unit *mage = (unit *)co->magician; + spellparameter *pa = co->par; + int cast_level = co->level; + + /* Macht alle nachfolgenden Zauber doppelt so teuer */ + countspells(mage, 1); + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; /* Zieleinheit */ + + /* finde das größte Talent: */ + for (i=0;i!=target->skill_size;++i) { + skill * sv = target->skills+i; + if (smax==NULL || skill_compare(sv, smax)>0) { + smax = sv; + } + } + if (smax!=NULL) { + /* wirkt auf maximal 10 Personen */ + int change = min(10, target->number) * (rand()%2+1) / target->number; + reduce_skill(target, smax, change); + } + set_order(&target->thisorder, NULL); + + sprintf(buf, "%s verschafft %s einige feuchtfröhliche Stunden mit heftigen " + "Nachwirkungen.", unitname(mage), unitname(target)); + addmessage(mage->region, mage->faction, buf, MSG_MAGIC, ML_INFO); + + sprintf(buf, "%s hat höllische Kopfschmerzen und kann sich an die " + "vergangene Woche nicht mehr erinnern. Nur noch daran, wie alles mit " + "einer fröhlichen Feier in irgendeiner Taverne anfing...", unitname(target)); + addmessage(r, target->faction, buf, MSG_EVENT, ML_WARN); + + return cast_level; +} + + +/* ------------------------------------------------------------- */ +/* Name: Mob + * Stufe: 10 + * Gebiet: Cerddor + * Wirkung: + * Wiegelt Stufe*250 Bauern zu einem Mob auf, der sich der Partei des + * Magier anschliesst Pro Woche beruhigen sich etwa 15% wieder und + * kehren auf ihre Felder zurück + * + * Flags: + * (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_raisepeasants(castorder *co) +{ + int bauern; + unit *u, *u2; + attrib *a; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + + if(rpeasants(r) == 0) { + addmessage(r, mage->faction, "Hier gibt es keine Bauern.", + MSG_MAGIC, ML_MISTAKE); + return 0; + } + bauern = (int)min(rpeasants(r), power*250); + rsetpeasants(r, rpeasants(r) - bauern); + + u2 = create_unit(r, mage->faction, bauern, new_race[RC_PEASANT], 0, "Wilder Bauernmob", mage); + set_string(&u2->name, "Erzürnte Bauern"); + + fset(u2, UFL_LOCKED); + fset(u2, UFL_PARTEITARNUNG); + + a = a_new(&at_unitdissolve); + a->data.ca[0] = 1; /* An rpeasants(r). */ + a->data.ca[1] = 15; /* 15% */ + a_add(&u2->attribs, a); + + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s wiegelt %d Bauern auf.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", + u2->number); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); + } + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Trübsal + * Stufe: 11 + * Kategorie: Region, negativ + * Wirkung: + * in der Region kann für einige Wochen durch Unterhaltung kein Geld + * mehr verdient werden + * + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +static int +sp_depression(castorder *co) +{ + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + + create_curse(mage,&r->attribs, ct_find("depression"), force, duration, zero_effect, 0); + + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next ) { + if (!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s sorgt für Trübsal unter den Bauern.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_INFO); + } + } + return cast_level; +} + +#if 0 +/* ------------------------------------------------------------- */ +/* Name: Hoher Gesang der Drachen + * Stufe: 14 + * Gebiet: Cerddor + * Kategorie: Monster, Beschwörung, positiv + * + * Wirkung: + * Erhöht HP-Regeneration in der Region und lockt drachenartige (Wyrm, + * Drache, Jungdrache, Seeschlange, ...) aus der Umgebung an + * + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +/* TODO zur Aktivierung in Zauberliste aufnehmen*/ +static int +sp_dragonsong(castorder *co) +{ + region *r = co->rt; /* Zauberregion */ + unit *mage = (unit *)co->magician; + unit *u; + int cast_level = co->level; + double power = co->force; + region_list *rl,*rl2; + faction *f; + + /* TODO HP-Effekt */ + + f = findfaction(MONSTER_FACTION); + + rl = all_in_range(r, (int)power); + + for(rl2 = rl; rl2; rl2 = rl2->next) { + for(u = rl2->data->units; u; u = u->next) { + if (u->race->flags & RCF_DRAGON) { + attrib * a = a_find(u->attribs, &at_targetregion); + if (!a) { + a = a_add(&u->attribs, make_targetregion(r)); + } else { + a->data.v = r; + } + sprintf(buf, "Kommt aus: %s, Will nach: %s", regionname(rl2->data, u->faction), regionname(r, u->faction)); + usetprivate(u, buf); + } + } + } + + ADDMSG(&mage->faction->msgs, msg_message( + "summondragon", "unit region command region", + mage, mage->region, co->order, co->rt)); + + free_regionlist(rl); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Hoher Gesang der Verlockung + * Stufe: 17 + * Gebiet: Cerddor + * Kategorie: Monster, Beschwörung, positiv + * + * Wirkung: + * Lockt Bauern aus den umliegenden Regionen her + * + * Flag: + * (FARCASTING | REGIONSPELL | TESTRESISTANCE) + */ +/* TODO zur Aktivierung in Zauberliste aufnehmen*/ + +static int +sp_songofAttraction(castorder *co) +{ + region *r = co->rt; /* Zauberregion */ + unit *mage = (unit *)co->magician; + int cast_level = co->level; + /* double power = co->force; */ + + /* TODO Wander Effekt */ + + ADDMSG(&mage->faction->msgs, msg_message( + "summon", "unit region command region", + mage, mage->region, co->order, r)); + + return cast_level; +} + +#endif + +/* ------------------------------------------------------------- */ +/* TRAUM - Illaun */ +/* ------------------------------------------------------------- */ + +/* Name: Seelenfrieden + * Stufe: 2 + * Kategorie: Region, positiv + * Gebiet: Illaun + * Wirkung: + * Reduziert Untotencounter + * Flag: (0) + */ + +int +sp_puttorest(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int dead = deathcount(r); + int laid_to_rest = dice((int)(co->force * 2), 100); + message * seen = msg_message("puttorest", "mage", mage); + message * unseen = msg_message("puttorest", "mage", NULL); + + laid_to_rest = max(laid_to_rest, dead); + + deathcounts(r, -laid_to_rest); + + report_effect(r, mage, seen, unseen); + msg_release(seen); + msg_release(unseen); + return co->level; +} + +/* Name: Traumschlößchen + * Stufe: 3 + * Kategorie: Region, Gebäude, positiv + * Gebiet: Illaun + * Wirkung: + * Mit Hilfe dieses Zaubers kann der Traumweber die Illusion eines + * beliebigen Gebäudes erzeugen. Die Illusion kann betreten werden, ist + * aber ansonsten funktionslos und benötigt auch keinen Unterhalt + * Flag: (0) + */ + +int +sp_icastle(castorder *co) +{ + building *b; + const building_type * type; + attrib *a; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + icastle_data * data; + + if((type=findbuildingtype(pa->param[0]->data.s, mage->faction->locale)) == NULL) { + type = bt_find("castle"); + } + + b = new_building(bt_find("illusion"), r, mage->faction->locale); + + /* Größe festlegen. */ + if (type == bt_find("illusion")) { + b->size = (rand()%(int)((power*power)+1)*10); + } else if (type->maxsize == -1) { + b->size = ((rand()%(int)(power))+1)*5; + } else { + b->size = type->maxsize; + } + sprintf(buf, "%s %s", LOC(mage->faction->locale, buildingtype(type, b, 0)), buildingid(b)); + set_string(&b->name, buf); + + /* TODO: Auf timeout und action_destroy umstellen */ + a = a_add(&b->attribs, a_new(&at_icastle)); + data = (icastle_data*)a->data.v; + data->type = type; + data->building = b; + data->time = 2+(rand()%(int)(power)+1)*(rand()%(int)(power)+1); + + if(mage->region == r) { + leave(r, mage); + mage->building = b; + } + + ADDMSG(&mage->faction->msgs, msg_message( + "icastle_create", "unit region command", mage, mage->region, + co->order)); + + addmessage(r, 0, + "Verwundert blicken die Bauern auf ein plötzlich erschienenes Gebäude.", + MSG_EVENT, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Gestaltwandlung + * Stufe: 3 + * Gebiet: Illaun + * Wirkung: + * Zieleinheit erscheint für (Stufe) Wochen als eine andere Gestalt + * (wie bei dämonischer Rassetarnung). + * Syntax: ZAUBERE "Gestaltwandlung" + * Flags: + * (UNITSPELL | SPELLLEVEL | ONETARGET) + */ + +int +sp_illusionary_shapeshift(castorder *co) +{ + unit *u; + const race * rc; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + u = pa->param[0]->data.u; + + rc = findrace(pa->param[1]->data.s, mage->faction->locale); + if (rc == NULL) { + cmistake(mage, co->order, 202, MSG_MAGIC); + return 0; + } + + /* ähnlich wie in laws.c:setealth() */ + if (!playerrace(rc)) { + sprintf(buf, "%s %s keine %s-Gestalt annehmen.", + unitname(u), + u->number > 1 ? "können" : "kann", + LOC(u->faction->locale, rc_name(rc, 2))); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + { + trigger * trestore = trigger_changerace(u, NULL, u->irace); + add_trigger(&u->attribs, "timer", trigger_timeout((int)power+2, trestore)); + } + u->irace = rc; + + sprintf(buf, "%s läßt %s als %s erscheinen.", + unitname(mage), unitname(u), LOC(u->faction->locale, rc_name(rc, u->number != 1))); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Traumdeuten + * Stufe: 7 + * Kategorie: Einheit + * + * Wirkung: + * Wirkt gegen Magieresistenz. Spioniert die Einheit aus. Gibt alle + * Gegenstände, Talente mit Stufe, Zauber und Kampfstatus an. + * + * Magieresistenz hier prüfen, wegen Fehlermeldung + * + * Flag: + * (UNITSPELL | ONETARGET) + */ +int +sp_readmind(castorder *co) +{ + unit *target; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + target = pa->param[0]->data.u; + + if (target->faction == mage->faction){ + /* Die Einheit ist eine der unsrigen */ + cmistake(mage, co->order, 45, MSG_MAGIC); + } + + /* Magieresistenz Unit */ + if (target_resists_magic(mage, target, TYP_UNIT, 0)){ + report_failure(mage, co->order); + /* "Fühlt sich beobachtet"*/ + ADDMSG(&target->faction->msgs, msg_message( + "stealdetect", "unit", target)); + return 0; + } + spy_message(2, mage, target); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Regionstraum analysieren + * Stufe: 9 + * Aura: 18 + * Kosten: SPC_FIX + * Wirkung: + * Zeigt die Verzauberungen eines Objekts an (curse->name, + * curse::info). Aus der Differenz Spruchstärke und Curse->vigour + * ergibt sich die Chance den Spruch zu identifizieren ((force - + * c->vigour)*10 + 100 %). + */ +int +sp_analyseregionsdream(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + magicanalyse_region(r, mage, cast_level); + + return cast_level; +} + + +/* ------------------------------------------------------------- */ +/* Name: Traumbilder erkennen + * Stufe: 5 + * Aura: 12 + * Kosten: SPC_FIX + * Wirkung: + * Zeigt die Verzauberungen eines Objekts an (curse->name, + * curse::info). Aus der Differenz Spruchstärke und Curse->vigour + * ergibt sich die Chance den Spruch zu identifizieren ((force - + * c->vigour)*10 + 100 %). + */ +int +sp_analysedream(castorder *co) +{ + unit *u; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + u = pa->param[0]->data.u; + magicanalyse_unit(u, mage, cast_level); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Schlechte Träume + * Stufe: 10 + * Kategorie: Region, negativ + * Wirkung: + * Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller + * nichtaliierten Einheiten (HELFE BEWACHE) in der Region so starkzu + * stören, das sie 1 Talentstufe in allen Talenten + * vorübergehend verlieren. Der Zauber wirkt erst im Folgemonat. + * + * Flags: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + * */ +int +sp_baddreams(castorder *co) +{ + int duration; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + region *r = co->rt; + curse * c; + variant effect; + + /* wirkt erst in der Folgerunde, soll mindestens eine Runde wirken, + * also duration+2 */ + duration = (int)max(1, power/2); /* Stufe 1 macht sonst mist */ + duration = 2 + rand()%duration; + + /* Nichts machen als ein entsprechendes Attribut in die Region legen. */ + effect.i = -1; + c = create_curse(mage, &r->attribs, ct_find("gbdream"), power, duration, effect, 0); + curse_setflag(c, CURSE_ISNEW); + + /* Erfolg melden*/ + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_effect", "unit region command", mage, + mage->region, co->order)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Schöne Träume + * Stufe: 8 + * Kategorie: + * Wirkung: + * Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller aliierten + * Einheiten in der Region so zu beeinflussen, daß sie für einige Zeit + * einen Bonus von 1 Talentstufe in allen Talenten + * bekommen. Der Zauber wirkt erst im Folgemonat. + * Flags: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + */ +int +sp_gooddreams(castorder *co) +{ + int duration; + curse * c; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + variant effect; + + /* wirkt erst in der Folgerunde, soll mindestens eine Runde wirken, + * also duration+2 */ + duration = (int)max(1, power/2); /* Stufe 1 macht sonst mist */ + duration = 2 + rand()%duration; + effect.i = 1; + c = create_curse(mage, &r->attribs, ct_find("gbdream"), power, duration, effect, 0); + curse_setflag(c, CURSE_ISNEW); + + /* Erfolg melden*/ + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_effect", "unit region command", mage, + mage->region, co->order)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: + * Stufe: 9 + * Kategorie: + * Wirkung: + * Es wird eine Kloneinheit erzeugt, die nichts kann. Stirbt der + * Magier, wird er mit einer Wahrscheinlichkeit von 90% in den Klon + * transferiert. + * Flags: + * (NOTFAMILARCAST) + */ +int +sp_clonecopy(castorder *co) +{ + unit *clone; + region *r = co->rt; + region *target_region = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + if (get_clone(mage) != NULL ) { + cmistake(mage, co->order, 298, MSG_MAGIC); + return 0; + } + + sprintf(buf, "Klon von %s", unitname(mage)); + clone = create_unit(target_region, mage->faction, 1, new_race[RC_CLONE], 0, buf, mage); + clone->status = ST_FLEE; + fset(clone, UFL_LOCKED); + + create_newclone(mage, clone); + + sprintf(buf, "%s erschafft einen Klon.", unitname(mage)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +int +sp_dreamreading(castorder *co) +{ + unit *u,*u2; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + double power = co->force; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + u = pa->param[0]->data.u; + + /* Illusionen und Untote abfangen. */ + if (fval(u->race, RCF_UNDEAD|RCF_ILLUSIONARY)) { + ADDMSG(&mage->faction->msgs, msg_message( + "spellunitnotfound", "unit region command id", + mage, mage->region, co->order, strdup(itoa36(u->no)))); + return 0; + } + + /* Entfernung */ + if(distance(mage->region, u->region) > power) { + addmessage(r, mage->faction, "Die Einheit ist zu weit " + "entfernt.", MSG_MAGIC, ML_MISTAKE); + return 0; + } + + u2 = createunit(u->region,mage->faction, RS_FARVISION, new_race[RC_SPELL]); + set_number(u2, 1); + set_string(&u2->name, "sp_dreamreading"); + u2->age = 2; /* Nur für diese Runde. */ + set_level(u2, SK_OBSERVATION, eff_skill(u, SK_OBSERVATION, u2->region)); + + sprintf(buf, "%s verliert sich in die Träume von %s und erhält einen " + "Eindruck von %s.", unitname(mage), unitname(u), regionname(u->region, mage->faction)); + addmessage(r, mage->faction, buf, MSG_EVENT, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Wirkt power/2 Runden auf bis zu power^2 Personen + * mit einer Chance von 5% vermehren sie sich */ +int +sp_sweetdreams(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + int men, n; + int duration = (int)(power/2)+1; + int opfer = (int)(power*power); + + /* Schleife über alle angegebenen Einheiten */ + for (n = 0; n < pa->length; n++) { + curse * c; + unit *u; + variant effect; + /* sollte nie negativ werden */ + if (opfer < 1) break; + + if (pa->param[n]->flag == TARGET_RESISTS || + pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + /* Zieleinheit */ + u = pa->param[n]->data.u; + + if (!ucontact(u, mage)) { + cmistake(mage, co->order, 40, MSG_EVENT); + continue; + } + men = min(opfer, u->number); + opfer -= men; + + /* Nichts machen als ein entsprechendes Attribut an die Einheit legen. */ + effect.i = 5; + c = create_curse(mage,&u->attribs, ct_find("orcish"), power, duration, effect, men); + curse_setflag(c, CURSE_ISNEW); + + sprintf(buf, "%s verschafft %s ein interessanteres Nachtleben.", + unitname(mage), unitname(u)); + addmessage(r, mage->faction, buf, MSG_EVENT, ML_INFO); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +int +sp_disturbingdreams(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = 1 + (int)(power/6); + variant effect; + curse * c; + + effect.i = 10; + c = create_curse(mage, &r->attribs, ct_find("badlearn"), power, duration, effect, 0); + curse_setflag(c, CURSE_ISNEW); + + sprintf(buf, "%s sorgt für schlechten Schlaf in %s.", + unitname(mage), regionname(r, mage->faction)); + addmessage(0, mage->faction, buf, MSG_EVENT, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +int +sp_dream_of_confusion(castorder *co) +{ + unit *u; + region_list *rl,*rl2; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + double range = (power-14)/2-1; + int duration = (int)(power-14)+1; + + rl = all_in_range(r, (short)range, NULL); + + for(rl2 = rl; rl2; rl2 = rl2->next) { + region * r2 = rl2->data; + variant effect; + curse * c; + /* Magieresistenz jeder Region prüfen */ + if (target_resists_magic(mage, r2, TYP_REGION, 0)){ + report_failure(mage, co->order); + continue; + } + + effect.i = cast_level*5; + c = create_curse(mage, &r2->attribs, + ct_find("disorientationzone"), power, duration, effect, 0); + /* soll der Zauber schon in der Zauberrunde wirken? */ + curse_setflag(c, CURSE_ISNEW); + + for (u = r2->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r2->units; u; u = u->next ) { + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand"); + addmessage(r2, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + if(!fval(mage->faction, FL_DH)){ + sprintf(buf, "%s beschwört einen Schleier der Verwirrung.", + unitname(mage)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + } + free_regionlist(rl); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* ASTRAL / THEORIE / M_ASTRAL */ +/* ------------------------------------------------------------- */ +/* Name: Magie analysieren + * Stufe: 1 + * Aura: 1 + * Kosten: SPC_LINEAR + * Komponenten: + * + * Wirkung: + * Zeigt die Verzauberungen eines Objekts an (curse->name, + * curse::info). Aus der Differenz Spruchstärke und Curse->vigour + * ergibt sich die Chance den Spruch zu identifizieren ((force - + * c->vigour)*10 + 100 %). + * + * Flags: + * UNITSPELL, SHIPSPELL, BUILDINGSPELL + */ + +int +sp_analysemagic(castorder *co) +{ + int obj; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + + /* Objekt ermitteln */ + obj = pa->param[0]->typ; + + switch(obj) { + case SPP_REGION: + { + region *tr = pa->param[0]->data.r; + magicanalyse_region(tr, mage, cast_level); + break; + } + case SPP_TEMP: + case SPP_UNIT: + { + unit *u; + u = pa->param[0]->data.u; + magicanalyse_unit(u, mage, cast_level); + break; + } + case SPP_BUILDING: + { + building *b; + b = pa->param[0]->data.b; + magicanalyse_building(b, mage, cast_level); + break; + } + case SPP_SHIP: + { + ship *sh; + sh = pa->param[0]->data.sh; + magicanalyse_ship(sh, mage, cast_level); + break; + } + default: + /* Fehlerhafter Parameter */ + return 0; + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ + +int +sp_itemcloak(castorder *co) +{ + unit *target; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = (int)power+1; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* Zieleinheit */ + target = pa->param[0]->data.u; + + create_curse(mage,&target->attribs, ct_find("itemcloak"), power, duration, zero_effect, 0); + ADDMSG(&mage->faction->msgs, msg_message( + "itemcloak", "mage target", mage, target)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Magieresistenz erhöhen + * Stufe: 3 + * Aura: 5 MP + * Kosten: SPC_LEVEL + * Komponenten: + * + * Wirkung: + * erhöht die Magierestistenz der Personen um 20 Punkte für 6 Wochen + * Wirkt auf Stufe*5 Personen kann auf mehrere Einheiten gezaubert + * werden, bis die Zahl der möglichen Personen erschöpft ist + * + * Flags: + * UNITSPELL + */ +int +sp_resist_magic_bonus(castorder *co) +{ + unit *u; + int n, m, opfer; + variant resistbonus; + int duration = 6; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + /* Pro Stufe können bis zu 5 Personen verzaubert werden */ + opfer = (int)(power * 5); + + /* Schleife über alle angegebenen Einheiten */ + for (n = 0; n < pa->length; n++) { + /* sollte nie negativ werden */ + if (opfer < 1) + break; + + if(pa->param[n]->flag == TARGET_RESISTS + || pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + u = pa->param[n]->data.u; + + /* Ist die Einheit schon verzaubert, wirkt sich dies nur auf die + * Menge der Verzauberten Personen aus. + if(is_cursed(u->attribs, C_MAGICRESISTANCE, 0)) + continue; + */ + + m = min(u->number,opfer); + opfer -= m; + + resistbonus.i = 20; + create_curse(mage, &u->attribs, ct_find("magicresistance"), + power, duration, resistbonus, m); + + sprintf(buf, "%s wird kurz von einem magischen Licht umhüllt.", + unitname(u)); + addmessage(0, u->faction, buf, MSG_EVENT, ML_IMPORTANT); + + /* und noch einmal dem Magier melden */ + if (u->faction != mage->faction) + addmessage(mage->region, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + /* pro 5 nicht verzauberte Personen kann der Level und damit die + * Kosten des Zaubers um 1 reduziert werden. (die Formel geht von + * immer abrunden da int aus) */ + cast_level -= opfer/5; + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* "ZAUBERE [STUFE n] \"Astraler Weg\" [ ...]", + * + * Parameter: + * pa->param[0]->data.s +*/ +int +sp_enterastral(castorder *co) +{ + region *rt, *ro; + unit *u, *u2; + int remaining_cap; + int n, w; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + const spell *sp = co->sp; + + switch(getplaneid(r)) { + case 0: + rt = r_standard_to_astral(r); + ro = r; + break; + default: + sprintf(buf, "%s in %s: 'ZAUBER %s': Dieser Zauber funktioniert " + "nur in der materiellen Welt.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + if(!rt) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann hier kein Kontakt zur " + "Astralwelt hergestellt werden.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + if(is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || + is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " + "dieser astralen Region hergestellt werden.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + remaining_cap = (int)((power-3) * 1500); + + /* für jede Einheit in der Kommandozeile */ + for (n = 0; n < pa->length; n++) { + if(pa->param[n]->flag == TARGET_NOTFOUND) continue; + u = pa->param[n]->data.u; + + if (!ucontact(u, mage)) { + if (power > 10 && !is_magic_resistant(mage, u, 0) + && can_survive(u, rt)) { + sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " + "Zauber jedoch nicht.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", + unitname(u), + cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_WARN); + } else { + sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " + "Zauber.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", + cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", + unitname(u)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + continue; + } + } + + w = weight(u); + if(!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + } else if(remaining_cap - w < 0) { + addmessage(r, mage->faction, "Die Einheit ist zu schwer.", + MSG_MAGIC, ML_MISTAKE); + } else { + remaining_cap = remaining_cap - w; + move_unit(u, rt, NULL); + + /* Meldungen in der Ausgangsregion */ + + for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = r->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, r, u, 0)) { + sprintf(buf, "%s wird durchscheinend und verschwindet.", + unitname(u)); + addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + + /* Meldungen in der Zielregion */ + + for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for (u2 = rt->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, rt, u, 0)) { + sprintf(buf, "%s erscheint plötzlich.", unitname(u)); + addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + } + } + return cast_level; +} + +int +sp_pullastral(castorder *co) +{ + region *rt, *ro; + unit *u, *u2; + region_list *rl, *rl2; + int remaining_cap; + int n, w; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + const spell *sp = co->sp; + + switch (getplaneid(r)) { + case 1: + rt = r; + ro = pa->param[0]->data.r; + rl = astralregions(r, NULL); + rl2 = rl; + while (rl2!=NULL) { + region * r2 = rl2->data; + if (r2->x == ro->x && r2->y == ro->y) { + ro = r2; + break; + } + rl2 = rl2->next; + } + if(!rl2) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " + "dieser Region hergestellt werden.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + free_regionlist(rl); + return 0; + } + free_regionlist(rl); + break; + default: + sprintf(buf, "%s in %s: 'ZAUBER %s': Dieser Zauber funktioniert " + "nur in der astralen Welt.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + if(is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || + is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Es kann kein Kontakt zu " + "dieser Region hergestellt werden.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + remaining_cap = (int)((power-3) * 1500); + + /* für jede Einheit in der Kommandozeile */ + for (n = 1; n < pa->length; n++) { + if(pa->param[n]->flag == TARGET_NOTFOUND) continue; + + u = pa->param[n]->data.u; + + if (!ucontact(u, mage)) { + if(power > 12 && pa->param[n]->flag != TARGET_RESISTS && can_survive(u, rt)) { + sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " + "Zauber jedoch nicht.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", + unitname(u), + cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); + addmessage(r, u->faction, buf, MSG_MAGIC, ML_WARN); + } else { + sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " + "Zauber.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", + cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", + unitname(u)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + continue; + } + } + + w = weight(u); + + if(!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + } else if(remaining_cap - w < 0) { + addmessage(r, mage->faction, "Die Einheit ist zu schwer.", + MSG_MAGIC, ML_MISTAKE); + } else { + remaining_cap = remaining_cap - w; + move_unit(u, rt, NULL); + + /* Meldungen in der Ausgangsregion */ + + for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = r->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, r, u, 0)) { + sprintf(buf, "%s wird durchscheinend und verschwindet.", + unitname(u)); + addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + + /* Meldungen in der Zielregion */ + + for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = rt->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, rt, u, 0)) { + sprintf(buf, "%s erscheint plötzlich.", unitname(u)); + addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + } + } + return cast_level; +} + +int +sp_leaveastral(castorder *co) +{ + region *rt, *ro; + region_list *rl, *rl2; + unit *u, *u2; + int remaining_cap; + int n, w; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + switch(getplaneid(r)) { + case 1: + ro = r; + rt = pa->param[0]->data.r; + if(!rt) { + addmessage(r, mage->faction, "Dorthin führt kein Weg.", + MSG_MAGIC, ML_MISTAKE); + return 0; + } + rl = astralregions(r, inhabitable); + rl2 = rl; + while (rl2!=NULL) { + if (rl2->data == rt) break; + rl2 = rl2->next; + } + if (rl2==NULL) { + addmessage(r, mage->faction, "Dorthin führt kein Weg.", + MSG_MAGIC, ML_MISTAKE); + free_regionlist(rl); + return 0; + } + free_regionlist(rl); + break; + default: + sprintf(buf, "Der Zauber funktioniert nur in der astralen Welt."); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + if (ro==NULL || is_cursed(ro->attribs, C_ASTRALBLOCK, 0) || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { + sprintf(buf, "Die Wege aus dieser astralen Region sind blockiert."); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + remaining_cap = (int)((power-3) * 1500); + + /* für jede Einheit in der Kommandozeile */ + for (n = 1; n < pa->length; n++) { + if(pa->param[n]->flag == TARGET_NOTFOUND) continue; + + u = pa->param[n]->data.u; + + if (!ucontact(u, mage)) { + if (power > 10 && !pa->param[n]->flag == TARGET_RESISTS && can_survive(u, rt)) { + sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " + "Zauber jedoch nicht.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", + unitname(u), + cansee(u->faction, r, mage, 0)?unitname(mage):"jemandem"); + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + } else { + sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " + "Zauber.", unitname(u)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_WARN); + sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", + cansee(u->faction, r, mage, 0)?unitname(mage):"Jemand", + unitname(u)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_WARN); + continue; + } + } + + w = weight(u); + + if(!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + } else if(remaining_cap - w < 0) { + addmessage(r, mage->faction, "Die Einheit ist zu schwer.", + MSG_MAGIC, ML_MISTAKE); + } else { + remaining_cap = remaining_cap - w; + move_unit(u, rt, NULL); + + /* Meldungen in der Ausgangsregion */ + + for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for(u2 = r->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, r, u, 0)) { + sprintf(buf, "%s wird durchscheinend und verschwindet.", + unitname(u)); + addmessage(r, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + + /* Meldungen in der Zielregion */ + + for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FL_DH); + for (u2 = rt->units; u2; u2 = u2->next ) { + if(!fval(u2->faction, FL_DH)) { + fset(u2->faction, FL_DH); + if(cansee(u2->faction, rt, u, 0)) { + sprintf(buf, "%s erscheint plötzlich.", unitname(u)); + addmessage(rt, u2->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + } + } + return cast_level; +} + +int +sp_fetchastral(castorder *co) +{ + int n; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + double power = co->force; + int remaining_cap = (int)((power-3) * 1500); + region_list * rtl = NULL; + region * rt = co->rt; /* region to which we are fetching */ + region * ro = NULL; /* region in which the target is */ + + if (rplane(rt)!=get_normalplane()) { + ADDMSG(&mage->faction->msgs, msg_message("error190", + "command region unit", co->order, rt, mage)); + return 0; + } + + if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { + ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", + "command region unit", co->order, rt, mage)); + return 0; + } + + /* für jede Einheit in der Kommandozeile */ + for (n=0; n!=pa->length; ++n) { + unit * u = pa->param[n]->data.u; + int w; + + if (pa->param[n]->flag & TARGET_NOTFOUND) continue; + + if (u->region!=ro) { + /* this can happen several times if the units are from different astral + * regions. Only possible on the intersections of schemes */ + region_list * rfind; + if (getplane(u->region) != get_astralplane()) { + cmistake(mage, co->order, 193, MSG_MAGIC); + continue; + } + if (rtl!=NULL) free_regionlist(rtl); + rtl = astralregions(u->region, NULL); + for (rfind=rtl;rfind!=NULL;rfind=rfind->next) { + if (rfind->data==mage->region) break; + } + if (rfind==NULL) { + /* the region r is not in the schemes of rt */ + ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", + "command region unit target", co->order, mage->region, mage, u)); + continue; + } + ro = u->region; + } + + if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { + ADDMSG(&mage->faction->msgs, msg_message("spellfail_distance", + "command region unit", co->order, mage->region, mage)); + continue; + } + + if (!can_survive(u, rt)) { + cmistake(mage, co->order, 231, MSG_MAGIC); + continue; + } + + w = weight(u); + if (remaining_cap - w < 0) { + ADDMSG(&mage->faction->msgs, msg_message("fail_tooheavy", + "command region unit target", co->order, mage->region, mage, u)); + continue; + } + + if (!ucontact(u, mage)) { + if (power>12 && !(pa->param[n]->flag & TARGET_RESISTS)) { + sprintf(buf, "%s hat uns nicht kontaktiert, widersteht dem " + "Zauber jedoch nicht.", unitname(u)); + addmessage(rt, mage->faction, buf, MSG_MAGIC, ML_INFO); + sprintf(buf, "%s wird von %s in eine andere Welt geschleudert.", + unitname(u), unitname(mage)); + addmessage(ro, u->faction, buf, MSG_EVENT, ML_WARN); + } else { + sprintf(buf, "%s hat uns nicht kontaktiert und widersteht dem " + "Zauber.", unitname(u)); + addmessage(rt, mage->faction, buf, MSG_MAGIC, ML_WARN); + sprintf(buf, "%s versucht, %s in eine andere Welt zu schleudern.", + unitname(mage), unitname(u)); + addmessage(ro, u->faction, buf, MSG_EVENT, ML_WARN); + continue; + } + } + + remaining_cap -= w; + move_unit(u, rt, NULL); + + /* Meldungen in der Ausgangsregion */ + ADDMSG(&ro->msgs, msg_message("astral_disappear", "unit", u)); + + /* Meldungen in der Zielregion */ + ADDMSG(&rt->msgs, msg_message("astral_appear", "unit", u)); + } + if (rtl!=NULL) free_regionlist(rtl); + return cast_level; +} + +#ifdef SHOWASTRAL_NOT_BORKED +int +sp_showastral(castorder *co) +{ + unit *u; + region *rt; + int n = 0; + int c = 0; + region_list *rl, *rl2; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + + switch(getplaneid(r)) { + case 0: + rt = r_standard_to_astral(r); + if(!rt) { + /* Hier gibt es keine Verbindung zur astralen Welt */ + cmistake(mage, co->order, 216, MSG_MAGIC); + return 0; + } + break; + case 1: + rt = r; + break; + default: + /* Hier gibt es keine Verbindung zur astralen Welt */ + cmistake(mage, co->order, 216, MSG_MAGIC); + return 0; + } + + rl = all_in_range(rt,power/5); + + /* Erst Einheiten zählen, für die Grammatik. */ + + 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 != new_race[RC_SPECIAL] && u->race != new_race[RC_SPELL]) n++; + } + } + } + + if(n == 0) { + /* sprintf(buf, "%s kann niemanden im astralen Nebel entdecken.", + unitname(mage)); */ + cmistake(mage, co->order, 220, MSG_MAGIC); + } else { + + /* Ausgeben */ + + sprintf(buf, "%s hat eine Vision der astralen Ebene. Im astralen " + "Nebel zu erkennen sind ", unitname(mage)); + + 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 != new_race[RC_SPECIAL] && u->race != new_race[RC_SPELL]) { + c++; + scat(unitname(u)); + scat(" ("); + if(!fval(u, UFL_PARTEITARNUNG)) { + scat(factionname(u->faction)); + scat(", "); + } + icat(u->number); + scat(" "); + scat(LOC(mage->faction->locale, rc_name(u->race, u->number!=1))); + scat(", Entfernung "); + icat(distance(rl2->data, rt)); + scat(")"); + if(c == n-1) { + scat(" und "); + } else if(c < n-1) { + scat(", "); + } + } + } + } + } + scat("."); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + } + + free_regionlist(rl); + return cast_level; + unused(co); + return 0; +} +#endif + +/* ------------------------------------------------------------- */ +int +sp_viewreality(castorder *co) +{ + region_list *rl, *rl2; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + if(getplaneid(r) != 1) { + /* sprintf(buf, "Dieser Zauber kann nur im Astralraum gezaubert werden."); */ + cmistake(mage, co->order, 217, MSG_MAGIC); + return 0; + } + + if(is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { + /* sprintf(buf, "Die materielle Welt ist hier nicht sichtbar.");*/ + cmistake(mage, co->order, 218, MSG_MAGIC); + return 0; + } + + rl = astralregions(r, NULL); + + /* Irgendwann mal auf Curses u/o Attribut umstellen. */ + for (rl2=rl; rl2; rl2=rl2->next) { + u = createunit(rl2->data, mage->faction, RS_FARVISION, new_race[RC_SPELL]); + set_level(u, SK_OBSERVATION, co->level/2); + set_string(&u->name, "Zauber: Blick in die Realität"); + u->age = 2; + } + + free_regionlist(rl); + + sprintf(buf, "%s gelingt es, durch die Nebel auf die Realität zu blicken.", + unitname(mage)); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + return cast_level; +} + +/* ------------------------------------------------------------- */ +int +sp_disruptastral(castorder *co) +{ + region_list *rl, *rl2; + region *rt; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + int duration = (int)(power/3)+1; + + switch(getplaneid(r)) { + case 0: + rt = r_standard_to_astral(r); + if(!rt) { + /* "Hier gibt es keine Verbindung zur astralen Welt." */ + cmistake(mage, co->order, 216, MSG_MAGIC); + return 0; + } + break; + case 1: + rt = r; + break; + default: + /* "Von hier aus kann man die astrale Ebene nicht erreichen." */ + cmistake(mage, co->order, 215, MSG_MAGIC); + return 0; + } + + rl = all_in_range(rt, (short)(power/5), NULL); + + for (rl2=rl; rl2!=NULL; rl2=rl2->next) { + attrib *a, *a2; + variant effect; + region * r2 = rl2->data; + spec_direction *sd; + int inhab_regions = 0; + region_list * trl = NULL; + + if (is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) continue; + + if (r2->units!=NULL) { + region_list * trl2; + + trl = astralregions(rl2->data, inhabitable); + for (trl2 = trl; trl2; trl2 = trl2->next) ++inhab_regions; + } + + /* Nicht-Permanente Tore zerstören */ + a = a_find(r->attribs, &at_direction); + + while (a!=NULL) { + a2 = a->nexttype; + sd = (spec_direction *)(a->data.v); + if (sd->duration != -1) a_remove(&r->attribs, a); + a = a2; + } + + /* Einheiten auswerfen */ + + if (trl!=NULL) { + for (u=r2->units;u;u=u->next) { + if (u->race != new_race[RC_SPELL]) { + region_list *trl2 = trl; + region *tr; + int c = rand() % inhab_regions; + + /* Zufällige Zielregion suchen */ + while (c--!=0) trl2 = trl2->next; + tr = trl2->data; + + if(!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) { + move_unit(u, tr, NULL); + sprintf(buf, "%s wird aus der astralen Ebene nach %s geschleudert.", + unitname(u), regionname(tr, u->faction)); + addmessage(0, u->faction, buf, MSG_MAGIC, ML_INFO); + } + } + } + free_regionlist(trl); + } + + /* Kontakt unterbinden */ + effect.i = 100; + create_curse(mage, &rl2->data->attribs, ct_find("astralblock"), + power, duration, effect, 0); + addmessage(r2, 0, "Mächtige Magie verhindert den Kontakt zur Realität.", + MSG_COMMENT, ML_IMPORTANT); + } + + free_regionlist(rl); + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Mauern der Ewigkeit + * Stufe: 7 + * Kategorie: Artefakt + * Gebiet: Tybied + * Wirkung: + * Das Gebäude kostet keinen Unterhalt mehr + * + * ZAUBER "Mauern der Ewigkeit" + * Flags: (0) + */ +static int +sp_eternizewall(castorder *co) +{ + unit *u; + curse * c; + building *b; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + b = pa->param[0]->data.b; + c = create_curse(mage, &b->attribs, ct_find("nocost"), + power*power, 1, zero_effect, 0); + + if(c==NULL) { /* ist bereits verzaubert */ + cmistake(mage, co->order, 206, MSG_MAGIC); + return 0; + } + + curse_setflag(c, CURSE_NOAGE|CURSE_ONLYONE); + + /* melden, 1x pro Partei in der Burg */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for (u = r->units; u; u = u->next) { + if (!fval(u->faction, FL_DH)) { + fset(u->faction, FL_DH); + if (u->building == b) { + sprintf(buf, "Mit einem Ritual bindet %s die magischen Kräfte " + "der Erde in die Mauern von %s", unitname(mage), + buildingname(b)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + } + + return cast_level; +} + + +/* ------------------------------------------------------------- */ +/* Name: Opfere Kraft + * Stufe: 15 + * Gebiet: Tybied + * Kategorie: Einheit, positiv + * Wirkung: + * Mit Hilfe dieses Zaubers kann der Magier einen Teil seiner + * magischen Kraft permanent auf einen anderen Magier übertragen. Auf + * einen Tybied-Magier kann er die Hälfte der eingesetzten Kraft + * übertragen, auf einen Magier eines anderen Gebietes ein Drittel. + * + * Flags: + * (UNITSPELL|ONETARGET) + * + * Syntax: + * ZAUBERE \"Opfere Kraft\" + * "ui" + */ +int +sp_permtransfer(castorder *co) +{ + int aura; + unit *tu; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + const spell *sp = co->sp; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber + * abbrechen aber kosten lassen */ + if(pa->param[0]->flag == TARGET_RESISTS) return cast_level; + + tu = pa->param[0]->data.u; + aura = pa->param[1]->data.i; + + if(!is_mage(tu)) { +/* sprintf(buf, "%s in %s: 'ZAUBER %s': Einheit ist kein Magier." + , unitname(mage), regionname(mage->region, mage->faction),sa->strings[0]); */ + cmistake(mage, co->order, 214, MSG_MAGIC); + return 0; + } + + aura = min(get_spellpoints(mage)-spellcost(mage, sp), aura); + + change_maxspellpoints(mage,-aura); + change_spellpoints(mage,-aura); + + if(get_mage(tu)->magietyp == get_mage(mage)->magietyp) { + change_maxspellpoints(tu, aura/2); + } else { + change_maxspellpoints(tu, aura/3); + } + + sprintf(buf, "%s opfert %s %d Aura.", unitname(mage), unitname(tu), aura); + addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* TODO: specialdirections? */ + +int +sp_movecastle(castorder *co) +{ + building *b; + direction_t dir; + region *target_region; + unit *u, *unext; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + spellparameter *pa = co->par; + const spell *sp = co->sp; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + b = pa->param[0]->data.b; + dir = finddirection(pa->param[1]->data.s, mage->faction->locale); + + if(dir == NODIRECTION) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Ungültige Richtung %s.", + unitname(mage), regionname(mage->region, mage->faction), + spell_name(sp, mage->faction->locale), + pa->param[1]->data.s); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return 0; + } + + if(b->size > (cast_level-12) * 250) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Der Elementar ist " + "zu klein, um das Gebäude zu tragen.", unitname(mage), + regionname(mage->region, mage->faction), spell_name(sp, mage->faction->locale)); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return cast_level; + } + + target_region = rconnect(r,dir); + + if(!(terrain[target_region->terrain].flags & LAND_REGION)) { + sprintf(buf, "%s in %s: 'ZAUBER %s': Der Erdelementar " + "weigert sich, nach %s zu gehen.", + unitname(mage), regionname(mage->region, mage->faction), + spell_name(sp, mage->faction->locale), + locale_string(mage->faction->locale, directions[dir])); + addmessage(0, mage->faction, buf, MSG_MAGIC, ML_MISTAKE); + return cast_level; + } + + bunhash(b); + translist(&r->buildings, &target_region->buildings, b); + b->region = target_region; + b->size -= b->size/(10-rand()%6); + bhash(b); + + for(u=r->units;u;) { + unext = u->next; + if(u->building == b) { + uunhash(u); + translist(&r->units, &target_region->units, u); + uhash(u); + } + u = unext; + } + + sprintf(buf, "Ein Beben erschüttert %s. Viele kleine Pseudopodien " + "erheben das Gebäude und tragen es in Richtung %s.", + buildingname(b), locale_string(mage->faction->locale, directions[dir])); + + if((b->type==bt_find("caravan") || b->type==bt_find("dam") || b->type==bt_find("tunnel"))) { + boolean damage = false; + direction_t d; + for (d=0;d!=MAXDIRECTIONS;++d) { + if (rroad(r, d)) { + rsetroad(r, d, rroad(r, d)/2); + damage = true; + } + } + if (damage) strcat(buf, " Die Straßen der Region wurden beschädigt."); + } + addmessage(r, 0, buf, MSG_MAGIC, ML_INFO); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Luftschiff + * Stufe: 6 + * + * Wirkung: + * Läßt ein Schiff eine Runde lang fliegen. Wirkt nur auf Boote und + * Langboote. + * Kombinierbar mit "Günstige Winde", aber nicht mit "Sturmwind". + * + * Flag: + * (ONSHIPCAST | SHIPSPELL | ONETARGET | TESTRESISTANCE) + */ +int +sp_flying_ship(castorder *co) +{ + ship *sh; + unit *u; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + sh = pa->param[0]->data.sh; + + if(is_cursed(sh->attribs, C_SHIP_FLYING, 0) ) { +/* sprintf(buf, "Auf dem Schiff befindet liegt bereits so ein Zauber."); */ + cmistake(mage, co->order, 211, MSG_MAGIC); + return 0; + } + if(is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { +/* sprintf(buf, "Es ist zu gefährlich, ein sturmgepeitschtes Schiff " + "fliegen zu lassen."); */ + cmistake(mage, co->order, 210, MSG_MAGIC); + return 0; + } + /* mit C_SHIP_NODRIFT haben wir kein Problem */ + + /* Duration = 1, nur diese Runde */ + create_curse(mage, &sh->attribs, ct_find("flyingship"), power, 1, zero_effect, 0); + /* Da der Spruch nur diese Runde wirkt, brauchen wir kein + * set_cursedisplay() zu benutzten - es sieht eh niemand... + */ + sh->coast = NODIRECTION; + + /* melden, 1x pro Partei */ + for (u = r->units; u; u = u->next) freset(u->faction, FL_DH); + for(u = r->units; u; u = u->next ) { + /* das sehen natürlich auch die Leute an Land */ + if(!fval(u->faction, FL_DH) ) { + fset(u->faction, FL_DH); + sprintf(buf, "%s beschwört einen Luftgeist, der die %s in " + "die Wolken hebt.", + cansee(u->faction, r, mage, 0) ? unitname(mage) : "Jemand", + shipname(sh)); + addmessage(r, u->faction, buf, MSG_EVENT, ML_INFO); + } + } + return cast_level; +} + + +/* ------------------------------------------------------------- */ +/* Name: Stehle Aura + * Stufe: 6 + * Kategorie: Einheit, negativ + * Wirkung: + * Mit Hilfe dieses Zaubers kann der Magier einem anderen Magier + * seine Aura gegen dessen Willen entziehen und sich selber + * zuführen. + * + * Flags: + * (FARCASTING | SPELLLEVEL | UNITSPELL | ONETARGET | TESTRESISTANCE | + * TESTCANSEE) + * */ +int +sp_stealaura(castorder *co) +{ + int taura; + unit *u; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double power = co->force; + spellparameter *pa = co->par; + + /* wenn kein Ziel gefunden, Zauber abbrechen */ + if(pa->param[0]->flag == TARGET_NOTFOUND) return 0; + + /* Zieleinheit */ + u = pa->param[0]->data.u; + + if(!get_mage(u)) { + ADDMSG(&mage->faction->msgs, msg_message( + "stealaura_fail", "unit target", mage, u)); + ADDMSG(&u->faction->msgs, msg_message( + "stealaura_fail_detect", "unit", u)); + return 0; + } + + taura = (get_mage(u)->spellpoints*(rand()%(int)(3*power)+1))/100; + + if(taura > 0) { + get_mage(u)->spellpoints -= taura; + get_mage(mage)->spellpoints += taura; +/* sprintf(buf, "%s entzieht %s %d Aura.", unitname(mage), unitname(u), + taura); */ + ADDMSG(&mage->faction->msgs, msg_message( + "stealaura_success", "mage target aura", mage, u, taura)); +/* sprintf(buf, "%s fühlt seine magischen Kräfte schwinden und verliert %d " + "Aura.", unitname(u), taura); */ + ADDMSG(&u->faction->msgs, msg_message( + "stealaura_detect", "unit aura", u, taura)); + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "stealaura_fail", "unit target", mage, u)); + ADDMSG(&u->faction->msgs, msg_message( + "stealaura_fail_detect", "unit", u)); + } + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Astrale Schwächezone + * Stufe: 5 + * Kategorie: + * Wirkung: + * Reduziert die Stärke jedes Spruch in der Region um Level Hält + * Sprüche bis zu einem Gesammtlevel von Stärke*10 aus, danach ist + * sie verbraucht. + * leibt bis zu Stärke Wochen aktiv. + * Ein Ring der Macht erhöht die Stärke um 1, in einem Magierturm + * gezaubert gibt nochmal +1 auf Stärke. (force) + * + * Beispiel: + * Eine Antimagiezone Stufe 7 hält bis zu 7 Wochen an oder Sprüche mit + * einem Gesammtlevel bis zu 70 auf. Also zB 7 Stufe 10 Sprüche, 10 + * Stufe 7 Sprüche oder 35 Stufe 2 Sprüche. Sie reduziert die Stärke + * (level+boni) jedes Spruchs, der in der Region gezaubert wird, um + * 7. Alle Sprüche mit einer Stärke kleiner als 7 schlagen fehl + * (power = 0). + * + * Flags: + * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) + * + */ +int +sp_antimagiczone(castorder *co) +{ + double power; + variant effect; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + int duration = (int)force+1; + + /* Hält Sprüche bis zu einem summierten Gesamtlevel von power aus. + * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone + * um seine Stufe */ + power = force * 10; + + /* Reduziert die Stärke jedes Spruchs um effect */ + effect.i = cast_level; + + create_curse(mage, &r->attribs, ct_find("antimagiczone"), power, duration, + effect, 0); + + /* Erfolg melden*/ + ADDMSG(&mage->faction->msgs, msg_message( + "regionmagic_effect", "unit region command", mage, + mage->region, co->order)); + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Schutzrunen + * Stufe: 8 + * Kosten: SPC_FIX + * + * Wirkung: + * Gibt Gebäuden einen Bonus auf Magieresistenz von +20%. Der Zauber + * dauert 3+rand()%Level Wochen an, also im Extremfall bis zu 2 Jahre + * bei Stufe 20 + * + * Es können mehrere Zauber übereinander gelegt werden, der Effekt + * summiert sich, jedoch wird die Dauer dadurch nicht verlängert. + * + * oder: + * + * Gibt Schiffen einen Bonus auf Magieresistenz von +20%. Der Zauber + * dauert 3+rand()%Level Wochen an, also im Extremfall bis zu 2 Jahre + * bei Stufe 20 + * + * Es können mehrere Zauber übereinander gelegt werden, der Effekt + * summiert sich, jedoch wird die Dauer dadurch nicht verlängert. + * + * Flags: + * (ONSHIPCAST | TESTRESISTANCE) + * + * Syntax: + * ZAUBERE \"Runen des Schutzes\" GEBÄUDE + * ZAUBERE \"Runen des Schutzes\" SCHIFF + * "kc" + */ + +static int +sp_magicrunes(castorder *co) +{ + int duration; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + variant effect; + + duration = 3 + rand()%cast_level; + effect.i = 20; + + switch(pa->param[0]->typ){ + case SPP_BUILDING: + { + building *b; + b = pa->param[0]->data.b; + + /* Magieresistenz der Burg erhöht sich um 20% */ + create_curse(mage, &b->attribs, ct_find("magicrunes"), force, + duration, effect, 0); + + /* Erfolg melden */ + ADDMSG(&mage->faction->msgs, msg_message( + "objmagic_effect", "unit region command target", mage, + mage->region, co->order, buildingname(b))); + break; + } + case SPP_SHIP: + { + ship *sh; + sh = pa->param[0]->data.sh; + /* Magieresistenz des Schiffs erhöht sich um 20% */ + create_curse(mage, &sh->attribs, ct_find("magicrunes"), force, + duration, effect, 0); + + /* Erfolg melden */ + ADDMSG(&mage->faction->msgs, msg_message( + "objmagic_effect", "unit region command target", mage, + mage->region, co->order, shipname(sh))); + break; + } + default: + /* fehlerhafter Parameter */ + return 0; + } + + return cast_level; +} + +/* ------------------------------------------------------------- */ +/* Name: Zeitdehnung + * + * Flags: + * (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) + * Syntax: + * "u+" + */ + +int +sp_speed2(castorder *co) +{ + int n, maxmen, used = 0, dur, men; + unit *u; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + + maxmen = 2 * cast_level * cast_level; + dur = max(1, cast_level/2); + + for (n = 0; n < pa->length; n++) { + variant effect; + /* sollte nie negativ werden */ + if (maxmen < 1) + break; + + if(pa->param[n]->flag == TARGET_RESISTS + || pa->param[n]->flag == TARGET_NOTFOUND) + continue; + + u = pa->param[n]->data.u; + + men = min(maxmen,u->number); + effect.i = 2; + create_curse(mage, &u->attribs, ct_find("speed"), force, dur, effect, men); + maxmen -= men; + used += men; + } + + /* TODO: Erfolg melden*/ + /* Effektiv benötigten cast_level (mindestens 1) zurückgeben */ + used = (int)sqrt(used/2); + return max(1, used); +} + +/* ------------------------------------------------------------- */ +/* Name: Magiefresser + * Stufe: 7 + * Kosten: SPC_LEVEL + * + * Wirkung: + * Kann eine bestimmte Verzauberung angreifen und auflösen. Die Stärke + * des Zaubers muss stärker sein als die der Verzauberung. + * Syntax: + * ZAUBERE \"Magiefresser\" REGION + * ZAUBERE \"Magiefresser\" EINHEIT + * ZAUBERE \"Magiefresser\" GEBÄUDE + * ZAUBERE \"Magiefresser\" SCHIFF + * + * "kc?c" + * Flags: + * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) + */ +/* Jeder gebrochene Zauber verbraucht c->vigour an Zauberkraft + * (force) */ +int +sp_q_antimagie(castorder *co) +{ + attrib **ap; + int obj; + curse * c = NULL; + int succ; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + char *ts; + + obj = pa->param[0]->typ; + + switch(obj){ + case SPP_REGION: + ap = &r->attribs; + set_string(&ts, regionname(r, mage->faction)); + break; + + case SPP_TEMP: + case SPP_UNIT: + { + unit *u = pa->param[0]->data.u; + ap = &u->attribs; + set_string(&ts, unitid(u)); + break; + } + case SPP_BUILDING: + { + building *b = pa->param[0]->data.b; + ap = &b->attribs; + set_string(&ts, buildingid(b)); + break; + } + case SPP_SHIP: + { + ship *sh = pa->param[0]->data.sh; + ap = &sh->attribs; + set_string(&ts, shipid(sh)); + break; + } + default: + /* Das Zielobjekt wurde vergessen */ + cmistake(mage, co->order, 203, MSG_MAGIC); + return 0; + } + + succ = destroy_curse(ap, cast_level, force, c); + + if (succ) { + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_effect", "unit region command succ target", + mage, mage->region, co->order, succ, strdup(ts))); + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_noeffect", "unit region command", + mage, mage->region, co->order)); + } + + return max(succ, 1); +} + +/* ------------------------------------------------------------- */ +/* Name: Fluch brechen + * Stufe: 7 + * Kosten: SPC_LEVEL + * + * Wirkung: + * Kann eine bestimmte Verzauberung angreifen und auflösen. Die Stärke + * des Zaubers muss stärker sein als die der Verzauberung. + * Syntax: + * ZAUBERE \"Fluch brechen\" REGION + * ZAUBERE \"Fluch brechen\" EINHEIT + * ZAUBERE \"Fluch brechen\" GEBÄUDE + * ZAUBERE \"Fluch brechen\" SCHIFF + * + * "kcc" + * Flags: + * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) + */ +int +sp_destroy_curse(castorder *co) +{ + attrib **ap; + int obj; + curse * c; + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + double force = co->force; + spellparameter *pa = co->par; + char *ts = NULL; + + if(pa->length < 2){ + /* Das Zielobjekt wurde vergessen */ + cmistake(mage, co->order, 203, MSG_MAGIC); + } + + obj = pa->param[0]->typ; + + c = findcurse(atoi36(pa->param[1]->data.s)); + if (!c){ + /* Es wurde kein Ziel gefunden */ + ADDMSG(&mage->faction->msgs, msg_message( + "spelltargetnotfound", "unit region command", + mage, mage->region, co->order)); + } else { + switch(obj){ + case SPP_REGION: + ap = &r->attribs; + set_string(&ts, regionname(r, mage->faction)); + break; + + case SPP_TEMP: + case SPP_UNIT: + { + unit *u = pa->param[0]->data.u; + ap = &u->attribs; + set_string(&ts, unitid(u)); + break; + } + case SPP_BUILDING: + { + building *b = pa->param[0]->data.b; + ap = &b->attribs; + set_string(&ts, buildingid(b)); + break; + } + case SPP_SHIP: + { + ship *sh = pa->param[0]->data.sh; + ap = &sh->attribs; + set_string(&ts, shipid(sh)); + break; + } + default: + /* Das Zielobjekt wurde vergessen */ + cmistake(mage, co->order, 203, MSG_MAGIC); + return 0; + } + + /* überprüfung, ob curse zu diesem objekt gehört */ + if (!is_cursed_with(*ap, c)){ + /* Es wurde kein Ziel gefunden */ + ADDMSG(&mage->faction->msgs, + msg_message( + "spelltargetnotfound", "unit region command", + mage, mage->region, co->order)); + } + + /* curse auflösen, wenn zauber stärker (force > vigour)*/ + c->vigour -= force; + + if (c->vigour <= 0.0) { + remove_curse(ap, c); + + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_effect", "unit region command id target", + mage, mage->region, co->order, strdup(pa->param[1]->data.s), + strdup(ts))); + } else { + ADDMSG(&mage->faction->msgs, msg_message( + "destroy_magic_noeffect", "unit region command", + mage, mage->region, co->order)); + } + } + if (ts != NULL) free(ts); + + return cast_level; +} + + +/* ------------------------------------------------------------- */ +int +sp_becomewyrm(castorder *co) +{ + unit *u = (unit *)co->magician; + int wyrms_already_created = 0; + int wyrms_allowed; + attrib *a; + + wyrms_allowed = fspecial(u->faction, FS_WYRM); + a = a_find(u->faction->attribs, &at_wyrm); + if(a) wyrms_already_created = a->data.i; + + if(wyrms_already_created >= wyrms_allowed) { + cmistake(u, co->order, 262, MSG_MAGIC); + return 0; + } + + if(!a) { + a_add(&u->faction->attribs, a_new(&at_wyrm)); + a->data.i = 1; + } else { + a->data.i++; + } + + u->race = new_race[RC_WYRM]; + add_spell(get_mage(u), find_spellbyid(SPL_WYRMODEM)); + + ADDMSG(&u->faction->msgs, msg_message("becomewyrm", "u", u)); + + return co->level; +} + +#ifdef WDW_PYRAMIDSPELL +/* ------------------------------------------------------------- */ +/* Name: WDW-Pyramidenfindezauber + * Stufe: unterschiedlich + * Gebiet: alle + * Wirkung: + * gibt die ungefaehre Entfernung zur naechstgelegenen Pyramiden- + * region an. + * + * Flags: + */ +static int +sp_wdwpyramid(castorder *co) +{ + region *r = co->rt; + unit *mage = (unit *)co->magician; + int cast_level = co->level; + + if(a_find(r->attribs, &at_wdwpyramid) != NULL) { + ADDMSG(&mage->faction->msgs, msg_message("wdw_pyramidspell_found", + "unit region command", mage, r, co->order)); + } else { + region *r2; + int mindist = INT_MAX; + int minshowdist; + int maxshowdist; + + for(r2 = regions; r2; r2 = r2->next) { + if(a_find(r2->attribs, &at_wdwpyramid) != NULL) { + int dist = distance(mage->region, r2); + if (dist < mindist) { + mindist = dist; + } + } + } + + assert(mindist >= 1); + + minshowdist = mindist - rand()%5; + maxshowdist = minshowdist + 4; + + ADDMSG(&mage->faction->msgs, msg_message("wdw_pyramidspell_notfound", + "unit region command mindist maxdist", mage, r, co->order, + max(1, minshowdist), maxshowdist)); + } + + return cast_level; +} +#endif + +/* ------------------------------------------------------------- */ +/* Name: Alltagszauber, hat je nach Gebiet anderen Namen + * Stufe: 1 + * Gebiet: alle + * Wirkung: der Magier verdient $50 pro Spruchstufe + * Kosten: 1 SP pro Stufe + */ +#include "../gamecode/economy.h" +/* TODO: das ist scheisse, aber spells gehören eh nicht in den kernel */ +int +sp_earn_silver(castorder *co) +{ + unit *mage = (unit *)co->magician; + double force = co->force; + region *r = co->rt; + int wanted = (int)(force * 50); + int earned = min(rmoney(r), wanted); + + rsetmoney(r, rmoney(r) - earned); + change_money(mage, earned); + /* TODO klären: ist das Silber damit schon reserviert? */ + + add_income(mage, IC_MAGIC, wanted, earned); + return co->level; +} + +static spell spelldaten[] = +{ + /* M_DRUIDE */ + { + SPL_BLESSEDHARVEST, "blessedharvest", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL), + 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_blessedharvest, NULL + }, + { + SPL_GWYRRD_EARN_SILVER, "gwyrrdearnsilver", NULL, + NULL, NULL, + M_DRUIDE, + (SPELLLEVEL|ONSHIPCAST), + 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earn_silver, NULL + }, + { + SPL_STONEGOLEM, "stonegolem", NULL, NULL, NULL, + M_DRUIDE, (SPELLLEVEL), 4, 1, + { + { R_AURA, 2, SPC_LEVEL }, + { R_STONE, 1, SPC_LEVEL }, + { R_TREES, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_create_stonegolem, NULL + }, + { + SPL_IRONGOLEM, "irongolem", NULL, NULL, NULL, + M_DRUIDE, (SPELLLEVEL), 4, 2, + { + { R_AURA, 2, SPC_LEVEL }, + { R_IRON, 1, SPC_LEVEL }, + { R_TREES, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_create_irongolem, NULL + }, + { + SPL_TREEGROW, "treegrow", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 5, 2, + { + { R_AURA, 4, SPC_LEVEL }, + { R_WOOD, 1, SPC_LEVEL }, + { R_TREES, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_hain, patzer_ents + }, + { + SPL_RUSTWEAPON, "rustweapon", NULL, NULL, + "u+", + M_DRUIDE, + (FARCASTING | SPELLLEVEL | UNITSPELL | TESTCANSEE | TESTRESISTANCE), + 5, 3, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_rosthauch, NULL + }, + { + SPL_KAELTESCHUTZ, "cold_protection", NULL, NULL, + "u+", + M_DRUIDE, + (UNITSPELL | SPELLLEVEL | TESTCANSEE | ONSHIPCAST), + 5, 3, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_kaelteschutz, NULL + }, + { + SPL_HAGEL, "hail", NULL, NULL, NULL, + M_DRUIDE, (COMBATSPELL|SPELLLEVEL), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_kampfzauber, NULL + }, + { + SPL_IRONKEEPER, "ironkeeper", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 5, 3, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_ironkeeper, NULL + }, + { + SPL_MAGICSTREET, "magicstreet", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | ONSHIPCAST | TESTRESISTANCE), + 5, 4, + { + { R_AURA, 1, SPC_LEVEL }, + { R_STONE, 1, SPC_FIX }, + { R_WOOD, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_magicstreet, NULL + }, + { + SPL_WINDSHIELD, "windshield", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_windshield, NULL + }, + { + SPL_MALLORNTREEGROW, "mallorntreegrow", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 5, 4, + { + { R_AURA, 6, SPC_LEVEL }, + { R_MALLORN, 1, SPC_LEVEL }, + { R_TREES, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_mallornhain, patzer_ents + }, + { SPL_GOODWINDS, "goodwinds", NULL, NULL, + "s", + M_DRUIDE, + (SHIPSPELL|ONSHIPCAST|SPELLLEVEL|ONETARGET|TESTRESISTANCE), + 5, 4, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_goodwinds, NULL + }, + { + SPL_HEALING, "healing", NULL, NULL, NULL, + M_DRUIDE, (POSTCOMBATSPELL | SPELLLEVEL), 5, 5, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_healing, NULL + }, + { + SPL_REELING_ARROWS, "reelingarrows", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 5, + { + { R_AURA, 15, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_reeling_arrows, NULL + }, + { + SPL_GWYRRD_FUMBLESHIELD, "gwyrrdfumbleshield", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 5, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fumbleshield, NULL + }, + { + SPL_TRANSFERAURA_DRUIDE, "transferauradruide", NULL, + "ZAUBERE \'Meditation\' ", + "ui", + M_DRUIDE, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 6, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_transferaura, NULL + }, + { + SPL_EARTHQUAKE, "earthquake", NULL, NULL, NULL, + M_DRUIDE, (FARCASTING|REGIONSPELL|TESTRESISTANCE), 5, 6, + { + { R_AURA, 25, SPC_FIX }, + { R_EOG, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earthquake, NULL + }, + { + SPL_STORMWINDS, "stormwinds", NULL, NULL, + "s+", + M_DRUIDE, + (SHIPSPELL | ONSHIPCAST | OCEANCASTABLE | TESTRESISTANCE | SPELLLEVEL), + 5, 6, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_stormwinds, NULL + }, + { + SPL_HOMESTONE, "homestone", NULL, NULL, NULL, + M_DRUIDE, (0), 5, 7, + { + { R_AURA, 50, SPC_FIX }, + { R_PERMAURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_homestone, NULL + }, + { + SPL_WOLFHOWL, "wolfhowl", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL ), 5, 7, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wolfhowl, NULL + }, + { + SPL_VERSTEINERN, "versteinern", NULL, NULL, NULL, + M_DRUIDE, (COMBATSPELL | SPELLLEVEL), 5, 8, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_versteinern, NULL + }, + { + SPL_STRONG_WALL, "strongwall", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_strong_wall, NULL + }, + { + SPL_GWYRRD_DESTROY_MAGIC, "gwyrrddestroymagic", NULL, + "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' REGION\n" + "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Geister bannen\' SCHIFF ", + "kc?", + M_DRUIDE, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 2, 8, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_magic, NULL + }, + { + SPL_TREEWALKENTER, "treewalkenter", NULL, NULL, + "u+", + M_DRUIDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 7, 9, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_treewalkenter, NULL + }, + { + SPL_TREEWALKEXIT, "treewalkexit", NULL, + "ZAUBERE \'Sog des Lebens\' [ ..]", + "ru+", + M_DRUIDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 7, 9, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_treewalkexit, NULL + }, + { + SPL_HOLYGROUND, "holyground", NULL, NULL, NULL, + M_DRUIDE, (0), 5, 9, + { + { R_AURA, 80, SPC_FIX }, + { R_PERMAURA, 3, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_holyground, NULL + }, + { + SPL_SUMMONENT, "summonent", NULL, NULL, NULL, + M_DRUIDE, (SPELLLEVEL), 5, 10, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summonent, NULL + }, + { + SPL_GWYRRD_FAMILIAR, "gwyrrdfamiliar", NULL, NULL, NULL, + M_DRUIDE, (NOTFAMILIARCAST), 5, 10, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_familiar, NULL + }, + { + SPL_BLESSSTONECIRCLE, "blessstonecircle", NULL, NULL, + "b", + M_DRUIDE, (BUILDINGSPELL | ONETARGET), 5, 11, + { + { R_AURA, 350, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_blessstonecircle, NULL + }, + { + SPL_GWYRRD_ARMORSHIELD, "barkskin", NULL, NULL, NULL, + M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 12, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_armorshield, NULL + }, + { + SPL_DROUGHT, "summonfireelemental", NULL, NULL, NULL, + M_DRUIDE, (FARCASTING|REGIONSPELL|TESTRESISTANCE), 5, 13, + { + { R_AURA, 600, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_drought, NULL + }, + { + SPL_FOG_OF_CONFUSION, "fogofconfusion", NULL, NULL, NULL, + M_DRUIDE, + (FARCASTING|SPELLLEVEL), + 5, 14, + { + { R_AURA, 8, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fog_of_confusion, NULL + }, + { + SPL_MAELSTROM, "maelstrom", + "Dieses Ritual beschört einen großen Wasserelementar aus den " + "Tiefen des Ozeans. Der Elementar erzeugt einen gewaltigen " + "Strudel, einen Mahlstrom, welcher alle Schiffe, die ihn passieren, " + "schwer beschädigen kann.", NULL, NULL, + M_DRUIDE, + (OCEANCASTABLE | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE), + 5, 15, + { + { R_AURA, 200, SPC_FIX }, + { R_SEASERPENTHEAD, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_maelstrom, NULL + }, + { + SPL_MALLORN, "magic_roots", + "Mit Hilfe dieses aufwändigen Rituals läßt der Druide einen Teil seiner " + "dauerhaft in den Boden und die Wälder der Region fliessen. Dadurch wird " + "das Gleichgewicht der Natur in der Region für immer verändert, und in " + "Zukunft werden nur noch die anspruchsvollen, aber kräftigen " + "Mallorngewächse in der Region gedeihen.", NULL, NULL, + M_DRUIDE, + (FARCASTING | REGIONSPELL | TESTRESISTANCE), + 5, 16, + { + { R_AURA, 250, SPC_FIX }, + { R_PERMAURA, 10, SPC_FIX }, + { R_TOADSLIME, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_mallorn, NULL + }, + { + SPL_GREAT_DROUGHT, "great_drought", + "Dieses mächtige Ritual öffnet ein Tor in die Elementarebene der " + "Hitze. Eine grosse Dürre kommt über das Land. Bauern, Tiere und " + "Pflanzen der Region kämpfen um das nackte Überleben, aber eine " + "solche Dürre überlebt wohl nur die Hälfte aller Lebewesen. " + "Der Landstrich kann über Jahre hinaus von den Folgen einer " + "solchen Dürre betroffen sein.", NULL, NULL, + M_DRUIDE, + (FARCASTING | REGIONSPELL | TESTRESISTANCE), + 5, 17, + { + { R_AURA, 800, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_great_drought, NULL + }, + /* M_CHAOS */ + { + SPL_SPARKLE_CHAOS, "sparklechaos", NULL, NULL, + "u", + M_CHAOS, (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_sparkle, NULL + }, + { + SPL_DRAIG_EARN_SILVER, "draigearnsilver", NULL, + NULL, + NULL, + M_CHAOS, (SPELLLEVEL|ONSHIPCAST), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earn_silver, NULL + }, + { + SPL_FIREBALL, "fireball", NULL, NULL, NULL, + M_CHAOS, (COMBATSPELL | SPELLLEVEL), 5, 2, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_kampfzauber, NULL + }, + { + SPL_MAGICBOOST, "magicboost", NULL, NULL, NULL, + M_CHAOS, (ONSHIPCAST), 3, 3, + { + { R_AURA, 2, SPC_LINEAR }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_magicboost, NULL + }, + { + SPL_BLOODSACRIFICE, "bloodsacrifice", NULL, NULL, NULL, + M_CHAOS, (ONSHIPCAST), 1, 4, + { + { R_HITPOINTS, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_bloodsacrifice, NULL + }, + { + SPL_BERSERK, "berserk", NULL, NULL, NULL, + M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 4, 5, + { + { R_AURA, 5, SPC_LEVEL }, + { R_PEASANTS, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_berserk, NULL + }, + { + SPL_FUMBLECURSE, "fumblecurse", NULL, NULL, + "u", + M_CHAOS, + (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE | TESTRESISTANCE), + 4, 5, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fumblecurse, patzer_fumblecurse + }, + { + SPL_SUMMONUNDEAD, "summonundead", NULL, NULL, NULL, + M_CHAOS, (SPELLLEVEL | FARCASTING | ONSHIPCAST), + 5, 6, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summonundead, patzer_peasantmob + }, + { + SPL_COMBATRUST, "combatrust", NULL, NULL, NULL, + M_CHAOS, (COMBATSPELL | SPELLLEVEL), 5, 6, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_combatrosthauch, NULL + }, + { + SPL_TRANSFERAURA_CHAOS, "transferaurachaos", NULL, + "ZAUBERE \'Machtübertragung\' ", + "ui", + M_CHAOS, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 7, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_transferaura, NULL + }, + { + SPL_FIREWALL, "firewall", NULL, + "ZAUBERE \'Feuerwand\' ", + "c", + M_CHAOS, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 4, 7, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_firewall, patzer_peasantmob + }, + { + SPL_PLAGUE, "plague", NULL, NULL, NULL, + M_CHAOS, + (FARCASTING | REGIONSPELL | TESTRESISTANCE), + 5, 7, + { + { R_AURA, 30, SPC_FIX }, + { R_PEASANTS, 50, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_plague, patzer_peasantmob + }, + { + SPL_CHAOSROW, "chaosrow", NULL, NULL, NULL, + M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, + { + { R_AURA, 3, SPC_LEVEL }, + { R_PEASANTS, 10, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_chaosrow, NULL + }, + { + SPL_SUMMONSHADOW, "summonshadow", NULL, NULL, NULL, + M_CHAOS, (SPELLLEVEL), 5, 8, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summonshadow, patzer_peasantmob + }, + { + SPL_UNDEADHERO, "undeadhero", NULL, NULL, NULL, + M_CHAOS, (POSTCOMBATSPELL | SPELLLEVEL), 5, 9, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_undeadhero, NULL + }, + { + SPL_AURALEAK, "auraleak", NULL, NULL, NULL, + M_CHAOS, (REGIONSPELL | TESTRESISTANCE), 3, 9, + { + { R_AURA, 35, SPC_FIX }, + { R_DRACHENBLUT, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_auraleak, NULL + }, + { + SPL_DRAIG_FUMBLESHIELD, "draigfumbleshield", NULL, NULL, NULL, + M_CHAOS, (PRECOMBATSPELL | SPELLLEVEL), 2, 9, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fumbleshield, NULL + }, + { + SPL_FOREST_FIRE, "forestfire", NULL, NULL, NULL, + M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 10, + { + { R_AURA, 50, SPC_FIX }, + { R_OIL, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_forest_fire, patzer_peasantmob + }, + { + SPL_DRAIG_DESTROY_MAGIC, "draigdestroymagic", NULL, + "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' REGION\n" + "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Pentagramm\' SCHIFF ", + "kc?", + M_CHAOS, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 2, 10, + { + { R_AURA, 10, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_magic, NULL + }, + { + SPL_UNHOLYPOWER, "unholypower", NULL, NULL, + "u+", + M_CHAOS, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 5, 14, + { + { R_AURA, 10, SPC_LEVEL }, + { R_PEASANTS, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_unholypower, NULL + }, + { + SPL_DEATHCLOUD, "deathcloud", NULL, NULL, NULL, + M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, + { + { R_AURA, 40, SPC_FIX }, + { R_HITPOINTS, 15, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_deathcloud, patzer_peasantmob + }, + { + SPL_SUMMONDRAGON, "summondragon", NULL, NULL, NULL, + M_CHAOS, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, + { + { R_AURA, 80, SPC_FIX }, + { R_DRAGONHEAD, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summondragon, patzer_peasantmob + }, + { + SPL_SUMMONSHADOWLORDS, "summonshadowlords", NULL, NULL, NULL, + M_CHAOS, (SPELLLEVEL), 5, 12, + { + { R_AURA, 7, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summonshadowlords, patzer_peasantmob + }, + { + SPL_DRAIG_FAMILIAR, "draigfamiliar", NULL, NULL, NULL, + M_CHAOS, (NOTFAMILIARCAST), 5, 13, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_familiar, NULL + }, + { + SPL_CHAOSSUCTION, "chaossuction", NULL, NULL, NULL, + M_CHAOS, (0), 5, 14, + { + { R_AURA, 150, SPC_FIX }, + { R_PEASANTS, 200, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_chaossuction, patzer_peasantmob + }, + /* M_TRAUM */ + { + SPL_SPARKLE_DREAM, "sparkledream", NULL, NULL, + "u", + M_TRAUM, + (UNITSPELL | TESTCANSEE | SPELLLEVEL | ONETARGET | ONSHIPCAST), + 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_sparkle, NULL + }, + { + SPL_ILLAUN_EARN_SILVER, "illaunearnsilver", NULL, + NULL, + NULL, + M_TRAUM, (SPELLLEVEL|ONSHIPCAST), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earn_silver, NULL + }, + { + SPL_SHADOWKNIGHTS, "shadowknights", NULL, NULL, NULL, + M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 4, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_shadowknights, NULL + }, + { + SPL_FLEE, "flee", NULL, NULL, NULL, + M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 5, 2, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_flee, NULL + }, + { + SPL_PUTTOREST, "puttorest", NULL, NULL, NULL, + M_TRAUM, (SPELLLEVEL), 5, 2, + { + { R_AURA, 3, SPC_LEVEL }, + { R_TREES, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_puttorest, NULL + }, + { + SPL_ICASTLE, "icastle", NULL, + "ZAUBERE \"Traumschlößchen\" ", + "c", + M_TRAUM, (0), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_icastle, NULL + }, + { + SPL_TRANSFERAURA_TRAUM, "transferauratraum", NULL, + "ZAUBERE \'Traum der Magie\' ", + "ui", + M_TRAUM, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 3, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_transferaura, NULL + }, + { + SPL_ILL_SHAPESHIFT, "shapeshift", NULL, + "ZAUBERE [STUFE n] \'Gestaltwandlung\' ", + "uc", + M_TRAUM, (UNITSPELL|SPELLLEVEL|ONETARGET), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_illusionary_shapeshift, NULL + }, + { + SPL_DREAMREADING, "dreamreading", NULL, NULL, + "u", + M_TRAUM, (FARCASTING | UNITSPELL | ONETARGET | TESTRESISTANCE), 5, 4, + { + { R_AURA, 8, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dreamreading, NULL + }, + { + SPL_TIREDSOLDIERS, "tiredsoldiers", NULL, NULL, NULL, + M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_tiredsoldiers, NULL + }, + { + SPL_REANIMATE, "reanimate", NULL, NULL, NULL, + M_TRAUM, (POSTCOMBATSPELL | SPELLLEVEL), 4, 5, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_reanimate, NULL + }, + { + SPL_ANALYSEDREAM, "analysedream", NULL, NULL, + "u", + M_TRAUM, (UNITSPELL | ONSHIPCAST | ONETARGET | TESTCANSEE), 5, 5, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_analysedream, NULL + }, + { + SPL_DISTURBINGDREAMS, "disturbingdreams", NULL, NULL, NULL, + M_TRAUM, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 6, + { + { R_AURA, 18, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_disturbingdreams, NULL + }, + { + SPL_SLEEP, "sleep", NULL, NULL, NULL, + M_TRAUM, (COMBATSPELL | SPELLLEVEL ), 5, 7, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_sleep, NULL + }, + { + SPL_WISPS, "wisps", + "Der Zauberer spricht eine Beschwörung über einen Teil der Region, " + "und in der Folgewoche entstehen dort Irrlichter. " + "Wer durch diese Nebel wandert, wird von Visionen geplagt und " + "in die Irre geleitet.", + "ZAUBERE [REGION x y] [STUFE n] \'Irrlichter\' ", + "c", + M_TRAUM, (SPELLLEVEL | FARCASTING), 5, 7, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wisps, NULL + }, + { + SPL_READMIND, "readmind", NULL, NULL, + "u", + M_TRAUM, (UNITSPELL | ONETARGET), 5, 7, + { + { R_AURA, 20, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_readmind, NULL + }, + { + SPL_GOODDREAMS, "gooddreams", NULL, NULL, NULL, + M_TRAUM, + (FARCASTING | REGIONSPELL | TESTRESISTANCE), + 5, 8, + { + { R_AURA, 80, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_gooddreams, NULL + }, + { + SPL_ILLAUN_DESTROY_MAGIC, "illaundestroymagic", NULL, + "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' REGION\n" + "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Traumbilder entwirren\' SCHIFF ", + "kc?", + M_TRAUM, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 2, 8, + { + { R_AURA, 6, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_magic, NULL + }, + { + SPL_ILLAUN_FAMILIAR, "illaunfamiliar", NULL, NULL, NULL, + M_TRAUM, (NOTFAMILIARCAST), 5, 9, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_familiar, NULL + }, + { + SPL_CLONECOPY, "clone", + "Dieser mächtige Zauber kann einen Magier vor dem sicheren Tod " + "bewahren. Der Magier erschafft anhand einer kleinen Blutprobe einen " + "Klon von sich, und legt diesen in ein Bad aus Drachenblut und verdünntem " + "Wasser des Lebens. " + "Anschließend transferiert er in einem aufwändigen Ritual einen Teil " + "seiner Seele in den Klon. Stirbt der Magier, reist seine Seele in den " + "Klon und der erschaffene Körper dient nun dem Magier als neues Gefäß. " + "Es besteht allerdings eine geringer Wahrscheinlichkeit, dass die Seele " + "nach dem Tod zu schwach ist, das neue Gefäß zu erreichen.", NULL, NULL, + M_TRAUM, (NOTFAMILIARCAST), 5, 9, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 20, SPC_FIX }, + { R_DRACHENBLUT, 5, SPC_FIX }, + { R_TREES, 5, SPC_FIX }, + { 0, 0, 0 } + }, + (spell_f)sp_clonecopy, NULL + }, + { + SPL_BADDREAMS, "bad_dreams", + "Dieser Zauber ermöglicht es dem Träumer, den Schlaf aller nichtaliierten " + "Einheiten (HELFE BEWACHE) in der Region so stark zu stören, das sie " + "vorübergehend einen Teil ihrer Erinnerungen verlieren.", NULL, NULL, + M_TRAUM, + (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 10, + { + { R_AURA, 90, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_baddreams, NULL + }, + { + SPL_MINDBLAST, "mindblast", NULL, NULL, NULL, + M_TRAUM, (COMBATSPELL | SPELLLEVEL), 5, 11, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_mindblast, NULL + }, + { + SPL_ORKDREAM, "orkdream", NULL, NULL, + "u+", + M_TRAUM, + (UNITSPELL | TESTRESISTANCE | TESTCANSEE | SPELLLEVEL), 5, 12, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_sweetdreams, NULL + }, + { + SPL_SUMMON_ALP, "summon_alp", NULL, NULL, "u", + M_TRAUM, + (UNITSPELL | ONETARGET | SEARCHGLOBAL | TESTRESISTANCE), + 5, 15, + { + { R_AURA, 350, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { R_SWAMP_3, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_alp, NULL + }, + { + SPL_DREAM_OF_CONFUSION, "dream_of_confusion", NULL, NULL, NULL, + M_TRAUM, + (FARCASTING | SPELLLEVEL), + 5, 16, + { + { R_AURA, 7, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dream_of_confusion, NULL + }, + /* M_BARDE */ + { + SPL_DENYATTACK, "appeasement", NULL, NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL ), 5, 1, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_denyattack, NULL + }, + { + SPL_CERDDOR_EARN_SILVER, "jugglery", NULL, NULL, NULL, + M_BARDE, (SPELLLEVEL|ONSHIPCAST), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earn_silver, NULL + }, + { + SPL_HEALINGSONG, "song_of_healing", NULL, NULL, NULL, + M_BARDE, (POSTCOMBATSPELL | SPELLLEVEL), 5, 2, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_healing, NULL + }, + { + SPL_GENEROUS, "generous", NULL, NULL, NULL, + M_BARDE, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE), + 5, 2, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_generous, NULL + }, + { + SPL_RAINDANCE, "raindance", NULL, NULL, NULL, + M_BARDE, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL), + 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_blessedharvest, NULL + }, + { + SPL_SONG_OF_FEAR, "song_of_fear", NULL, NULL, NULL, + M_BARDE, (COMBATSPELL | SPELLLEVEL), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_flee, NULL + }, + { + SPL_RECRUIT, "courting", NULL, NULL, NULL, + M_BARDE, (SPELLLEVEL), 5, 4, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_recruit, NULL + }, + { + SPL_SONG_OF_CONFUSION, "song_of_confusion", NULL, NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 4, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_chaosrow, NULL + }, + { + SPL_BABBLER, "blabbermouth", NULL, NULL, "u", + M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 4, + { + { R_AURA, 10, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_babbler, NULL + }, + { + SPL_HERO, "heroic_song", NULL, NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 5, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_hero, NULL + }, + { + SPL_TRANSFERAURA_BARDE, "transfer_aura_song", NULL, NULL, + "ui", + M_BARDE, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 5, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_transferaura, NULL + }, + { + SPL_UNIT_ANALYSESONG, "analysesong_unit", NULL, NULL, + "u", + M_BARDE, + (UNITSPELL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 5, 5, + { + { R_AURA, 10, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_analysesong_unit, NULL + }, + { + SPL_CERRDOR_FUMBLESHIELD, "cerrdorfumbleshield", NULL, NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 2, 5, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fumbleshield, NULL + }, + { SPL_CALM_MONSTER, "calm_monster", + "Dieser einschmeichelnde Gesang kann fast jedes intelligente Monster " + "zähmen. Es wird von Angriffen auf den Magier absehen und auch seine " + "Begleiter nicht anrühren. Doch sollte man sich nicht täuschen, es " + "wird dennoch ein unberechenbares Wesen bleiben.", NULL, + "u", + M_BARDE, + (UNITSPELL | ONSHIPCAST | ONETARGET | TESTRESISTANCE | TESTCANSEE), + 5, 6, + { + { R_AURA, 15, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_calm_monster, NULL + }, + { SPL_SEDUCE, "seduction", + "Mit diesem Lied kann eine Einheit derartig betört werden, so dass " + "sie dem Barden den größten Teil ihres Bargelds und ihres Besitzes " + "schenkt. Sie behält jedoch immer soviel, wie sie zum Überleben " + "braucht.", NULL, + "u", + M_BARDE, + (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), + 5, 6, + { + { R_AURA, 12, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_seduce, NULL + }, + { + SPL_HEADACHE, "headache", + "Aufzeichung des Vortrags von Selen Ard'Ragorn in Bar'Glingal: " + "'Es heiss, dieser Spruch wäre wohl in den Spelunken der Westgassen " + "entstanden, doch es kann genausogut in jedem andern verrufenen " + "Viertel gewesen sein. Seine wichtigste Zutat ist etwa ein Fass " + "schlechtesten Weines, je billiger und ungesunder, desto " + "wirkungsvoller wird die Essenz. Die Kunst, diesen Wein in pure " + "Essenz zu destillieren, die weitaus anspruchsvoller als das einfache " + "Rezeptmischen eines Alchemisten ist, und diese dergestalt zu binden " + "und konservieren, das sie sich nicht gleich wieder verflüchtigt, wie " + "es ihre Natur wäre, ja, dies ist etwas, das nur ein Meister des " + "Cerddor vollbringen kann. Nun besitzt Ihr eine kleine Phiola mit " + "einer rubinrotschimmernden - nun, nicht flüssig, doch auch nicht " + "ganz Dunst - nennen wir es einfach nur Elixier. Doch nicht dies ist " + "die wahre Herausforderung, sodann muss, da sich ihre Wirkung leicht " + "verflüchtigt, diese innerhalb weniger Tage unbemerkt in das Getränkt " + "des Opfers geträufelt werden. Ihr Meister der Betöhrung und " + "Verführung, hier nun könnt Ihr Eure ganze Kunst unter Beweis " + "stellen. Doch gebt Acht, nicht unbedacht selbst von dem Elixier zu " + "kosten, denn wer einmal gekostet hat, der kann vom Weine nicht mehr " + "lassen, und er säuft sicherlich eine volle Woche lang. Jedoch nicht " + "die Verführung zum Trunke ist die wahre Gefahr, die dem Elixier " + "innewohnt, sondern das der Trunkenheit so sicher ein gar " + "fürchterliches Leid des Kopfes folgen wird, wie der Tag auf die " + "Nacht folgt. Und er wird gar sicherlich von seiner besten Fähigkeit " + "einige Tage bis hin zu den Studien zweier Wochen vergessen haben. " + "Noch ein Wort der Warnung: Dieses ist sehr aufwendig, und so Ihr " + "noch weitere Zauber in der selben Woche wirken wollt, so werden sie Euch " + "schwerer fallen.'", NULL, + "u", + M_BARDE, + (UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), + 5, 7, + { + { R_AURA, 4, SPC_LINEAR }, + { R_SWAMP_2, 3, SPC_FIX }, + { R_SILVER, 50, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_headache, NULL + }, + { SPL_PUMP, "sound_out", + "Erliegt die Einheit dem Zauber, so wird sie dem Magier alles erzählen, " + "was sie über die gefragte Region weiß. Ist in der Region niemand " + "ihrer Partei, so weiß sie nichts zu berichten. Auch kann sie nur das " + "erzählen, was sie selber sehen könnte.", + "ZAUBERE \'Aushorchen\' ", + "ur", + M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 7, + { + { R_AURA, 4, SPC_FIX }, + { R_SILVER, 100, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_pump, NULL + }, + { + SPL_BLOODTHIRST, "bloodthirst", + "Wie viele magischen Gesänge, so entstammt auch dieser den altem " + "Wissen der Katzen, die schon immer um die machtvolle Wirkung der " + "Stimme wussten. Mit diesem Lied wird die Stimmung der Krieger " + "aufgepeitscht, sie gar in wilde Raserrei und Blutrausch versetzt. " + "Ungeachtet eigener Schmerzen werden sie kämpfen bis zum " + "Tode und niemals fliehen. Während ihre Attacke verstärkt ist " + "achten sie kaum auf sich selbst.", NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 7, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_berserk, NULL + }, + { + SPL_FRIGHTEN, "frighten", + "Dieser Kriegsgesang sät Panik in der Front der Gegner und schwächt " + "so ihre Kampfkraft erheblich. Angst wird ihren Schwertarm schwächen " + "und Furcht ihren Schildarm lähmen.", NULL, NULL, + M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_frighten, NULL + }, + { + SPL_OBJ_ANALYSESONG, "analyse_object", + "Wie Lebewesen, so haben auch Schiffe und Gebäude und sogar Regionen " + "ihr eigenes Lied, wenn auch viel schwächer und schwerer zu hören. " + "Und so, wie wie aus dem Lebenslied einer Person erkannt werden kann, " + "ob diese unter einem Zauber steht, so ist dies auch bei Burgen, " + "Schiffen oder Regionen möglich.", + "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' REGION\n" + "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' GEBÄUDE \n" + "ZAUBERE [STUFE n] \'Lied des Ortes analysieren\' SCHIFF ", + "kc?", + M_BARDE, (SPELLLEVEL|ONSHIPCAST), 5, 8, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_analysesong_obj, NULL + }, + { + SPL_CERDDOR_DESTROY_MAGIC, "cerddor_destroymagic", + "Jede Verzauberung beeinflußt das Lebenslied, schwächt und verzerrt es. " + "Der kundige Barde kann versuchen, das Lebenslied aufzufangen und zu " + "verstärken und die Veränderungen aus dem Lied zu tilgen.", + "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' REGION\n" + "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Lebenslied festigen\' SCHIFF ", + "kc?", + M_BARDE, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 2, 8, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_magic, NULL + }, + { + SPL_MIGRANT, "migration", + "Dieses Ritual ermöglicht es, eine Einheit, egal welcher Art, in die " + "eigene Partei aufzunehmen. Der um Aufnahme Bittende muss dazu willig " + "und bereit sein, seiner alten Partei abzuschwören. Dies bezeugt er " + "durch KONTAKTIEREn des Magiers. Auch wird er die Woche über " + "ausschliesslich mit Vorbereitungen auf das Ritual beschäftigt sein. " + "Das Ritual wird fehlschlagen, wenn er zu stark an seine alte Partei " + "gebunden ist, dieser etwa Dienst für seine teuere Ausbildung " + "schuldet. Der das Ritual leitende Magier muss für die permanente " + "Bindung des Aufnahmewilligen an seine Partei naturgemäß auch " + "permanente Aura aufwenden. Pro Stufe und pro 1 permanente Aura kann " + "er eine Person aufnehmen.", NULL, + "u", + M_BARDE, (UNITSPELL | SPELLLEVEL | ONETARGET | TESTCANSEE), 5, 9, + { + { R_AURA, 3, SPC_LEVEL }, + { R_PERMAURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_migranten, NULL + }, + { + SPL_CERDDOR_FAMILIAR, "summon_familiar", + "Einem erfahrenen Magier wird irgendwann auf seinen Wanderungen ein " + "ungewöhnliches Exemplar einer Gattung begegnen, welches sich dem " + "Magier anschließen wird.", NULL, NULL, + M_BARDE, (NOTFAMILIARCAST), 5, 9, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_familiar, NULL + }, + { + SPL_RAISEPEASANTS, "raise_mob", + "Mit Hilfe dieses magischen Gesangs überzeugt der Magier die Bauern " + "der Region, sich ihm anzuschließen. Die Bauern werden ihre Heimat jedoch " + "nicht verlassen, und keine ihrer Besitztümer fortgeben. Jede Woche " + "werden zudem einige der Bauern den Bann abwerfen und auf ihre Felder " + "zurückkehren. Wie viele Bauern sich dem Magier anschließen hängt von der " + "Kraft seines Gesangs ab.", NULL, NULL, + M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 10, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_raisepeasants, NULL + }, + { + SPL_SONG_RESISTMAGIC, "song_resist_magic", + "Dieses magische Lied wird, einmal mit Inbrunst gesungen, sich in der " + "Region fortpflanzen, von Mund zu Mund springen und eine Zeitlang " + "überall zu vernehmen sein. Nach wie vielen Wochen der Gesang aus dem " + "Gedächnis der Region entschwunden ist, ist von dem Geschick des Barden " + "abhängig. Bis das Lied ganz verklungen ist, wird seine Magie allen " + "Verbündeten des Barden (HELFE BEWACHE), und natürlich auch seinen " + "eigenem Volk, einen einmaligen Bonus von 15% " + "auf die natürliche Widerstandskraft gegen eine Verzauberung " + "verleihen.", NULL, NULL, + M_BARDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 2, 10, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_song_resistmagic, NULL + }, + { + SPL_DEPRESSION, "melancholy", + "Mit diesem Gesang verbreitet der Barde eine melancholische, traurige " + "Stimmung unter den Bauern. Einige Wochen lang werden sie sich in ihre " + "Hütten zurückziehen und kein Silber in den Theatern und Tavernen lassen.", NULL, NULL, + M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, + { + { R_AURA, 40, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_depression, NULL + }, + { + SPL_SONG_SUSCEPTMAGIC, "song_suscept_magic", + "Dieses Lied, das in die magische Essenz der Region gewoben wird, " + "schwächt die natürliche Widerstandskraft gegen eine " + "Verzauberung einmalig um 15%. Nur die Verbündeten des Barden " + "(HELFE BEWACHE) sind gegen die Wirkung des Gesangs gefeit.", NULL, NULL, + M_BARDE, + (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 2, 12, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_song_susceptmagic, NULL + }, + { + SPL_SONG_OF_PEACE, "song_of_peace", + "Dieser mächtige Bann verhindert jegliche Attacken. Niemand in der " + "ganzen Region ist fähig seine Waffe gegen irgendjemanden zu erheben. " + "Die Wirkung kann etliche Wochen andauern", NULL, NULL, + M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 12, + { + { R_AURA, 20, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_song_of_peace, NULL + }, + { + SPL_SONG_OF_ENSLAVE, "song_of_slavery", + "Dieser mächtige Bann raubt dem Opfer seinen freien Willen und " + "unterwirft sie den Befehlen des Barden. Für einige Zeit wird das Opfer " + "sich völlig von seinen eigenen Leuten abwenden und der Partei des Barden " + "zugehörig fühlen.", NULL, + "u", + M_BARDE, (UNITSPELL | ONETARGET | TESTCANSEE), 5, 13, + { + { R_AURA, 40, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_charmingsong, NULL + }, + { + SPL_BIGRECRUIT, "big_recruit", + "Aus 'Wanderungen' von Firudin dem Weisen: " + "'In Weilersweide, nahe dem Wytharhafen, liegt ein kleiner Gasthof, der " + "nur wenig besucht ist. Niemanden bekannt ist, das dieser Hof " + "bis vor einigen Jahren die Bleibe des verbannten Wanderpredigers Grauwolf " + "war. Nachdem er bei einer seiner berüchtigten flammenden Reden fast die " + "gesammte Bauernschaft angeworben hatte, wurde er wegen Aufruhr verurteilt " + "und verbannt. Nur zögerlich war er bereit mir das Geheimniss seiner " + "Überzeugungskraft zu lehren.'", NULL, NULL, + M_BARDE, (SPELLLEVEL), 5, 14, + { + { R_AURA, 20, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_bigrecruit, NULL + }, + { + SPL_RALLYPEASANTMOB, "calm_riot", + "Mit Hilfe dieses magischen Gesangs kann der Magier eine Region in " + "Aufruhr wieder beruhigen. Die Bauernhorden werden sich verlaufen " + "und wieder auf ihre Felder zurückkehren.", NULL, NULL, + M_BARDE, (FARCASTING), 5, 15, + { + { R_AURA, 30, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_rallypeasantmob, NULL + }, + { + SPL_RAISEPEASANTMOB, "incite_riot", + "Mit Hilfe dieses magischen Gesangs versetzt der Magier eine ganze " + "Region in Aufruhr. Rebellierende Bauernhorden machen jedes Besteuern " + "unmöglich, kaum jemand wird mehr für Gaukeleien Geld spenden und " + "es können keine neuen Leute angeworben werden. Nach einigen Wochen " + "beruhigt sich der Mob wieder.", NULL, NULL, + M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 16, + { + { R_AURA, 40, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_raisepeasantmob, NULL + }, + /* M_ASTRAL */ + { + SPL_ANALYSEMAGIC, "analyze_magic", NULL, + "ZAUBERE [STUFE n] \'Magie analysieren\' REGION\n" + "ZAUBERE [STUFE n] \'Magie analysieren\' EINHEIT \n" + "ZAUBERE [STUFE n] \'Magie analysieren\' GEBÄUDE \n" + "ZAUBERE [STUFE n] \'Magie analysieren\' SCHIFF ", + "kc?", + M_ASTRAL, (SPELLLEVEL | UNITSPELL | ONSHIPCAST | TESTCANSEE), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_analysemagic, NULL + }, + { + SPL_ITEMCLOAK, "concealing_aura", NULL, NULL, + "u", + M_ASTRAL, (SPELLLEVEL | UNITSPELL | ONSHIPCAST | ONETARGET), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_itemcloak, NULL + }, + { + SPL_TYBIED_EARN_SILVER, "miracle_doctor", NULL, NULL, NULL, + M_ASTRAL, (SPELLLEVEL|ONSHIPCAST), 5, 1, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_earn_silver, NULL + }, + { + SPL_TYBIED_FUMBLESHIELD, "tybiedfumbleshield", NULL, NULL, NULL, + M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 2, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fumbleshield, NULL + }, +#ifdef SHOWASTRAL_NOT_BORKED + { + SPL_SHOWASTRAL, "show_astral", + "Der Magier kann kurzzeitig in die Astralebene blicken und erfährt " + "so alle Einheiten innerhalb eines astralen Radius von Stufe/5 Regionen.", NULL, NULL, + M_ASTRAL, (SPELLLEVEL), 5, 2, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_showastral, NULL + }, +#endif + { + SPL_RESISTMAGICBONUS, "resist_magic", NULL, NULL, "u+", + M_ASTRAL, + (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), + 2, 3, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_resist_magic_bonus, NULL + }, + { + SPL_KEEPLOOT, "keeploot", NULL, NULL, NULL, + M_ASTRAL, ( POSTCOMBATSPELL | SPELLLEVEL ), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_keeploot, NULL + }, + { + SPL_ENTERASTRAL, "enterastral", 0, 0, "u+", + M_ASTRAL, (UNITSPELL|SPELLLEVEL), 7, 4, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_enterastral, NULL + }, + { + SPL_LEAVEASTRAL, "leaveastral", 0, + "ZAUBER [STUFE n] \'Astraler Ausgang\' " + "[ ...]", + "ru+", + M_ASTRAL, (UNITSPELL |SPELLLEVEL), 7, 4, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_leaveastral, NULL + }, + { + SPL_TRANSFERAURA_ASTRAL, "auratransfer", + "Mit Hilfe dieses Zauber kann der Magier eigene Aura im Verhältnis " + "2:1 auf einen anderen Magier des gleichen Magiegebietes oder im " + "Verhältnis 3:1 auf einen Magier eines anderen Magiegebietes " + "übertragen.", + "ZAUBERE \'Auratransfer\' ", + "ui", + M_ASTRAL, (UNITSPELL|ONSHIPCAST|ONETARGET), 1, 5, + { + { R_AURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_transferaura, NULL + }, + { + SPL_SHOCKWAVE, "shockwave", NULL, NULL, NULL, + M_ASTRAL, (COMBATSPELL|SPELLLEVEL), 5, 5, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_stun, NULL + }, + { + SPL_ANTIMAGICZONE, "antimagiczone", NULL, NULL, NULL, + M_ASTRAL, (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), + 2, 5, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_antimagiczone, NULL + }, + { + SPL_TYBIED_DESTROY_MAGIC, "destroy_magic", NULL, + "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' REGION\n" + "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Magiefresser\' SCHIFF ", + "kc?", + M_ASTRAL, + (FARCASTING | SPELLLEVEL | ONSHIPCAST | ONETARGET | TESTCANSEE), + 2, 5, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_magic, NULL + }, + { + SPL_PULLASTRAL, "pull_astral", NULL, + "ZAUBER [STUFE n] \'Astraler Ruf\' " + "[ ...]", + "ru+", + M_ASTRAL, (UNITSPELL | SEARCHGLOBAL | SPELLLEVEL), 7, 6, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_pullastral, NULL + }, + + { + SPL_FETCHASTRAL, "fetch_astral", NULL, NULL, "u+", + M_ASTRAL, (UNITSPELL | SEARCHGLOBAL | SPELLLEVEL), 7, 6, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_fetchastral, NULL + }, + { + SPL_STEALAURA, "steal_aura", NULL, NULL, "u", + M_ASTRAL, + (FARCASTING | SPELLLEVEL | UNITSPELL | ONETARGET | TESTRESISTANCE | TESTCANSEE), + 3, 6, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_stealaura, NULL + }, + { + SPL_FLYING_SHIP, "airship", NULL, NULL, "s", + M_ASTRAL, (ONSHIPCAST | SHIPSPELL | ONETARGET | TESTRESISTANCE), 5, 6, + { + { R_AURA, 10, SPC_FIX }, + { R_HIGHLAND_1, 1, SPC_FIX }, + { R_GLACIER_3, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_flying_ship, NULL + }, + { + SPL_DESTROY_MAGIC, "break_curse", NULL, + "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' REGION \n" + "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' EINHEIT \n" + "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' GEBÄUDE \n" + "ZAUBERE [REGION x y] [STUFE n] \'Fluch brechen\' SCHIFF ", + "kcc", + M_ASTRAL, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), 3, 7, + { + { R_AURA, 3, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_destroy_curse, NULL + }, + { + SPL_ETERNIZEWALL, "eternal_walls", NULL, + "ZAUBERE \'Mauern der Ewigkeit\' ", + "b", + M_ASTRAL, + (SPELLLEVEL | BUILDINGSPELL | ONETARGET | TESTRESISTANCE | ONSHIPCAST), + 5, 7, + { + { R_AURA, 50, SPC_FIX }, + { R_PERMAURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_eternizewall, NULL + }, + { + SPL_SCHILDRUNEN, "protective_runes", + "Zeichnet man diese Runen auf die Wände eines Gebäudes oder auf die " + "Planken eines Schiffes, so wird es schwerer durch Zauber zu " + "beeinflussen sein. Jedes Ritual erhöht die Widerstandskraft des " + "Gebäudes oder Schiffes gegen Verzauberung um 20%. " + "Werden mehrere Schutzzauber übereinander gelegt, so addiert " + "sich ihre Wirkung, doch ein hundertprozentiger Schutz läßt sich so " + "nicht erreichen. Der Zauber hält mindestens drei Wochen an, je nach " + "Talent des Magiers aber auch viel länger.", + "ZAUBERE \'Runen des Schutzes\' GEBÄUDE | " + "SCHIFF ]", + "kc", + M_ASTRAL, (ONSHIPCAST | TESTRESISTANCE), 2, 8, + { + { R_AURA, 20, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_magicrunes, NULL + }, + + { + SPL_REDUCESHIELD, "fish_shield", + "Dieser Zauber vermag dem Gegner ein geringfügig versetztes Bild der " + "eigenen Truppen vorzuspiegeln, so wie der Fisch im Wasser auch nicht " + "dort ist wo er zu sein scheint. Von jedem Treffer kann so die Hälfte " + "des Schadens unschädlich abgeleitet werden. Doch hält der Schild nur " + "einige Hundert Schwerthiebe aus, danach wird er sich auflösen. " + "Je stärker der Magier, desto mehr Schaden hält der Schild aus.", NULL, NULL, + M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 8, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_reduceshield, NULL + }, + { + SPL_SPEED, "combat_speed", + "Dieser Zauber beschleunigt einige Kämpfer auf der eigenen Seite " + "so, dass sie während des gesamten Kampfes in einer Kampfrunde zweimal " + "angreifen können.", NULL, NULL, + M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 5, 9, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_speed, NULL + }, + { + SPL_VIEWREALITY, "view_reality", + "Der Magier kann mit Hilfe dieses Zaubers aus der Astral- in die " + "materielle Ebene blicken und die Regionen und Einheiten genau " + "erkennen.", NULL, NULL, + M_ASTRAL, (0), 5, 10, + { + { R_AURA, 40, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_viewreality, NULL + }, + { + SPL_SPEED2, "double_time", + "Diese praktische Anwendung des theoretischen Wissens um Raum und Zeit " + "ermöglicht es, den Zeitfluß für einige Personen zu verändern. Auf " + "diese Weise veränderte Personen bekommen für einige Wochen doppelt " + "soviele Bewegungspunkte und doppelt soviele Angriffe pro Runde.", NULL, + "u+", + M_ASTRAL, (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), 5, 11, + { + { R_AURA, 5, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_speed2, NULL + }, + { + SPL_ARMORSHIELD, "armor_shield", + "Diese vor dem Kampf zu zaubernde Ritual gibt den eigenen Truppen " + "einen zusätzlichen Bonus auf ihre Rüstung. Jeder Treffer " + "reduziert die Kraft des Zaubers, so dass der Schild sich irgendwann " + "im Kampf auflösen wird.", NULL, NULL, + M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 12, + { + { R_AURA, 4, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_armorshield, NULL + }, + { + SPL_TYBIED_FAMILIAR, "summon_familiar", + "Einem erfahrenen Magier wird irgendwann auf seinen Wanderungen ein " + "ungewöhnliches Exemplar einer Gattung begegnen, welches sich dem " + "Magier anschließen wird.", NULL, NULL, + M_ASTRAL, (NOTFAMILIARCAST), 5, 12, + { + { R_AURA, 100, SPC_FIX }, + { R_PERMAURA, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_summon_familiar, NULL + }, + { + SPL_MOVECASTLE, "living_rock", + "Dieses kräftezehrende Ritual beschwört mit Hilfe einer Kugel aus " + "konzentriertem Laen einen gewaltigen Erdelementar und bannt ihn " + "in ein Gebäude. Dem Elementar kann dann befohlen werden, das " + "Gebäude mitsamt aller Bewohner in eine Nachbarregion zu tragen. " + "Die Stärke des beschworenen Elementars hängt vom Talent des " + "Magiers ab: Der Elementar kann maximal [Stufe-12]*250 Größeneinheiten " + "große Gebäude versetzen. Das Gebäude wird diese Prozedur nicht " + "unbeschädigt überstehen.", + "ZAUBER [STUFE n] \'Belebtes Gestein\' ", + "bc", + M_ASTRAL, + (SPELLLEVEL | BUILDINGSPELL | ONETARGET | TESTRESISTANCE), + 5, 13, + { + { R_AURA, 10, SPC_LEVEL }, + { R_PERMAURA, 1, SPC_FIX }, + { R_EOG, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_movecastle, NULL + }, + { + SPL_DISRUPTASTRAL, "astral_disruption", + "Dieser Zauber bewirkt eine schwere Störung des Astralraums. Innerhalb " + "eines astralen Radius von Stufe/5 Regionen werden alle Astralwesen, " + "die dem Zauber nicht wiederstehen können, aus der astralen Ebene " + "geschleudert. Der astrale Kontakt mit allen betroffenen Regionen ist " + "für Stufe/3 Wochen gestört.", NULL, NULL, + M_ASTRAL, (REGIONSPELL), 4, 14, + { + { R_AURA, 140, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_disruptastral, NULL + }, + { + SPL_PERMTRANSFER, "sacrifice_strength", + "Mit Hilfe dieses Zaubers kann der Magier einen Teil seiner magischen " + "Kraft permanent auf einen anderen Magier übertragen. Auf einen Tybied-" + "Magier kann er die Hälfte der eingesetzten Kraft übertragen, auf einen " + "Magier eines anderen Gebietes ein Drittel.", + "ZAUBERE \'Opfere Kraft\' ", + "ui", + M_ASTRAL, (UNITSPELL|ONETARGET), 1, 15, + { + { R_AURA, 100, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_permtransfer, NULL + }, + /* M_GRAU */ + /* Definitionen von Create_Artefaktsprüchen */ + { + SPL_METEORRAIN, "meteor_rain", + "Ein Schauer von Meteoren regnet über das Schlachtfeld.", NULL, NULL, + M_GRAU, (COMBATSPELL | SPELLLEVEL), 5, 3, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_kampfzauber, NULL + }, + { + SPL_BECOMEWYRM, "wyrm_transformation", + "Mit Hilfe dieses Zaubers kann sich der Magier permanent in einen " + "mächtigen Wyrm verwandeln. Der Magier behält seine Talente und " + "Möglichkeiten, bekommt jedoch die Kampf- und Bewegungseigenschaften " + "eines Wyrms. Der Odem des Wyrms wird sich mit steigendem Magie-Talent " + "verbessern. Der Zauber ist sehr kraftraubend und der Wyrm wird einige " + "Zeit brauchen, um sich zu erholen.", NULL, NULL, + M_GRAU, 0, 5, 1, + { + { R_AURA, 1, SPC_FIX }, + { R_PERMAURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_becomewyrm, NULL + }, + /* Monstersprüche */ + { SPL_FIREDRAGONODEM, "fiery_dragonbreath", + "Verbrennt die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 3, + { + { R_AURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dragonodem, NULL + }, + { SPL_DRAGONODEM, "icy_dragonbreath", + "Tötet die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 6, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dragonodem, NULL + }, + { SPL_WYRMODEM, "powerful_dragonbreath", + "Verbrennt die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + { R_AURA, 3, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dragonodem, NULL + }, + { SPL_DRAINODEM, "drain_skills", + "Entzieht Talentstufen und macht Schaden wie Großer Odem", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + { R_AURA, 4, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_dragonodem, NULL + }, + { + SPL_AURA_OF_FEAR, "aura_of_fear", + "Panik", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + { R_AURA, 1, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_flee, NULL + }, + { + SPL_SHADOWCALL, "shadowcall", + "Ruft Schattenwesen.", NULL, NULL, + M_GRAU, (PRECOMBATSPELL), 5, 12, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_shadowcall, NULL + }, + { + SPL_IMMOLATION, "immolation", + "Verletzt alle Gegner.", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 12, + { + { R_AURA, 2, SPC_LEVEL }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_immolation, NULL + }, + { SPL_FIREODEM, "firestorm", + "Tötet die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 8, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_immolation, NULL + }, + { SPL_ICEODEM, "coldfront", + "Tötet die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 8, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_immolation, NULL + }, + { SPL_ACIDODEM, "acidrain", + "Tötet die Feinde", NULL, NULL, + M_GRAU, (COMBATSPELL), 5, 8, + { + { R_AURA, 2, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_immolation, NULL + }, + #ifdef WDW_PYRAMIDSPELL + { + SPL_WDWPYRAMID_TRAUM, "wdwpyramid_illaun", + "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " + "Region für den Pyramidenbau geeignet ist.", NULL, NULL, + M_TRAUM, (0), 5, 4, + { + { R_AURA, 2, SPC_FIX }, + { R_PLAIN_3, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wdwpyramid, NULL + }, + { + SPL_WDWPYRAMID_ASTRAL, "wdwpyramid_tybied", + "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " + "Region für den Pyramidenbau geeignet ist.", NULL, NULL, + M_ASTRAL, (0), 5, 3, + { + { R_AURA, 4, SPC_FIX }, + { R_WISE, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wdwpyramid, NULL + }, + + { + SPL_WDWPYRAMID_DRUIDE, "wdwpyramid_gwyrrd", + "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " + "Region für den Pyramidenbau geeignet ist.", NULL, NULL, + M_DRUIDE, (0), 5, 5, + { + { R_AURA, 3, SPC_FIX }, + { R_MALLORN, 5, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wdwpyramid, NULL + }, + { + SPL_WDWPYRAMID_BARDE, "wdwpyramid_cerrdor", + "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " + "Region für den Pyramidenbau geeignet ist.", NULL, NULL, + M_BARDE, (0), 5, 4, + { + { R_AURA, 2, SPC_FIX }, + { R_HIGHLAND_3, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wdwpyramid, NULL + }, + { + SPL_WDWPYRAMID_CHAOS, "wdwpyramid_draig", + "Mit Hilfe dieses Zaubers kann der Magier erkennen, ob eine " + "Region für den Pyramidenbau geeignet ist.", NULL, NULL, + M_CHAOS, (0), 5, 5, + { + { R_AURA, 1, SPC_FIX }, + { R_PERMAURA, 1, SPC_FIX }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + (spell_f)sp_wdwpyramid, NULL + }, + #endif + /* SPL_NOSPELL MUSS der letzte Spruch der Liste sein*/ + { + SPL_NOSPELL, "no spell", NULL, NULL, NULL, 0, 0, 0, 0, + { + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } + }, + NULL, NULL + } +}; + +static boolean +chaosgate_valid(const border * b) +{ + const attrib * a = a_findc(b->from->attribs, &at_direction); + if (!a) a = a_findc(b->to->attribs, &at_direction); + if (!a) return false; + return true; +} + +struct region * +chaosgate_move(const border * b, struct unit * u, struct region * from, struct region * to, boolean routing) +{ + if (!routing) { + int maxhp = u->hp / 4; + if (maxhpnumber) maxhp = u->number; + u->hp = maxhp; + } + return to; +} + +border_type bt_chaosgate = { + "chaosgate", VAR_NONE, + b_transparent, /* transparent */ + NULL, /* init */ + NULL, /* destroy */ + NULL, /* read */ + NULL, /* write */ + b_blocknone, /* block */ + NULL, /* name */ + b_rinvisible, /* rvisible */ + b_finvisible, /* fvisible */ + b_uinvisible, /* uvisible */ + chaosgate_valid, + chaosgate_move +}; + +void +init_spells(void) +{ + int i; + + /* register all the old spells in the spelldata array */ + for (i=0;spelldaten[i].id!=SPL_NOSPELL;++i) { + register_spell(spelldaten+i); + } + at_register(&at_cursewall); + at_register(&at_unitdissolve); +#ifdef WDW_PYRAMIDSPELL + at_register(&at_wdwpyramid); +#endif + register_bordertype(&bt_firewall); + register_bordertype(&bt_wisps); + register_bordertype(&bt_chaosgate); +} + void register_spells(void) { diff --git a/src/common/spells/spells.h b/src/common/spells/spells.h index 34a64b061..0a288b1bd 100644 --- a/src/common/spells/spells.h +++ b/src/common/spells/spells.h @@ -20,6 +20,18 @@ extern "C" { extern void register_spells(void); + /* für Feuerwände: in movement muß das noch explizit getestet werden. + * besser wäre eine blcok_type::move() routine, die den effekt + * der Bewegung auf eine struct unit anwendet. + */ + extern struct border_type bt_chaosgate; + extern struct border_type bt_firewall; + typedef struct wall_data { + struct unit * mage; + int force; + boolean active; + } wall_data; + #ifdef __cplusplus } #endif diff --git a/src/eressea/korrektur.c b/src/eressea/korrektur.c index 07209d2e8..e8eab4a1c 100644 --- a/src/eressea/korrektur.c +++ b/src/eressea/korrektur.c @@ -27,6 +27,7 @@ #include #include #include +#include /* gamecode includes */ #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include #include diff --git a/src/eressea/lua/spell.cpp b/src/eressea/lua/spell.cpp index 43f5a0ba3..2497e6bfc 100644 --- a/src/eressea/lua/spell.cpp +++ b/src/eressea/lua/spell.cpp @@ -3,7 +3,9 @@ #include "list.h" // kernel includes +#include #include +#include #include #include #include @@ -31,13 +33,14 @@ call_spell(castorder *co) { const char * fname = co->sp->sname; unit * mage = (unit*)co->magician; + int retval = -1; if (co->familiar) { mage = co->familiar; } try { - return luabind::call_function(luaState, fname, co->rt, mage, co->level, co->force); + retval = luabind::call_function(luaState, fname, co->rt, mage, co->level, co->force); } catch (luabind::error& e) { lua_State* L = e.state(); @@ -47,9 +50,38 @@ call_spell(castorder *co) lua_pop(L, 1); std::terminate(); } - return -1; + return retval; } +static void +init_familiar(unit * u) +{ + char fname[64]; + snprintf(fname, sizeof(fname), "initfamiliar_%s", u->race->_name[0]); + + luabind::object globals = luabind::get_globals(luaState); + luabind::object fun = globals.at(fname); + if (fun.is_valid()) { + if (fun.type()!=LUA_TFUNCTION) { + log_error(("Lua global object %s is not a function, type is %u\n", fname, fun.type())); + } else { + try { + luabind::call_function(luaState, fname, u); + } + catch (luabind::error& e) { + lua_State* L = e.state(); + const char* error = lua_tostring(L, -1); + log_error(("An exception occured while %s tried to call '%s': %s.\n", + unitname(u), fname, error)); + lua_pop(L, 1); + std::terminate(); + } + } + } + + snprintf(fname, sizeof(fname), "%s_familiar", u->race->_name[0]); + equip_unit(u, get_equipment(fname)); +} void bind_spell(lua_State * L) @@ -62,4 +94,5 @@ bind_spell(lua_State * L) .property("school", &spell_getschool) ]; register_function((pf_generic)&call_spell, "luaspell"); + register_function((pf_generic)&init_familiar, "luafamiliar"); } diff --git a/src/eressea/lua/unit.cpp b/src/eressea/lua/unit.cpp index d08a5f5f0..94bc2c0e0 100644 --- a/src/eressea/lua/unit.cpp +++ b/src/eressea/lua/unit.cpp @@ -58,12 +58,6 @@ public: static spell * value(spell_list * node) { return node->data; } }; -static eressea::list -unit_familiarspells(const unit& u) { - spell_list * spells = familiarspells(u.race); - return eressea::list(spells); -} - class bind_orders { public: static order * next(order * node) { return node->next; } @@ -581,7 +575,6 @@ bind_unit(lua_State * L) .property("region", &unit_getregion, &unit_setregion) .property("is_familiar", &unit_isfamiliar) .property("spells", &unit_spells, return_stl_iterator) - .property("familiarspells", &unit_familiarspells, return_stl_iterator) .property("number", &unit_getnumber, &unit_setnumber) .property("race", &unit_getrace, &unit_setrace) .property("hp_max", &unit_hpmax) diff --git a/src/res/equipment.xml b/src/res/equipment.xml index 0381fe0e8..afb916065 100644 --- a/src/res/equipment.xml +++ b/src/res/equipment.xml @@ -1,6 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/res/races.xml b/src/res/races.xml index e70d0424a..e341cad0c 100644 --- a/src/res/races.xml +++ b/src/res/races.xml @@ -93,7 +93,7 @@ - + @@ -123,7 +123,7 @@ - + @@ -153,7 +153,7 @@ - + @@ -184,7 +184,7 @@ - + @@ -219,7 +219,7 @@ - + @@ -250,7 +250,7 @@ - + @@ -283,7 +283,7 @@ - + @@ -344,7 +344,7 @@ - + @@ -375,7 +375,7 @@ - + @@ -405,7 +405,7 @@ - + @@ -437,7 +437,7 @@ - + @@ -469,7 +469,7 @@ - + @@ -499,7 +499,7 @@ - + @@ -532,7 +532,7 @@ - + @@ -562,6 +562,7 @@ + diff --git a/src/res/resources.xml b/src/res/resources.xml index 13c47d66c..c63af3bf7 100644 --- a/src/res/resources.xml +++ b/src/res/resources.xml @@ -7,8 +7,14 @@ + + + + + + - + diff --git a/src/scripts/eressea.lua b/src/scripts/eressea.lua index 313e2b6d7..31d453254 100644 --- a/src/scripts/eressea.lua +++ b/src/scripts/eressea.lua @@ -24,6 +24,7 @@ end function run_scripts() scripts = { "spells.lua", + "familiars.lua", "eternath.lua", "wedding-jadee.lua", "ponnuki.lua" diff --git a/src/scripts/familiars.lua b/src/scripts/familiars.lua new file mode 100644 index 000000000..9b1d49123 --- /dev/null +++ b/src/scripts/familiars.lua @@ -0,0 +1,3 @@ +function initfamiliar_lynx(u) + print("a lynx is born :-)") +end diff --git a/src/scripts/hse3-run.lua b/src/scripts/hse3-run.lua index 16017aeb5..82c489b41 100644 --- a/src/scripts/hse3-run.lua +++ b/src/scripts/hse3-run.lua @@ -1,6 +1,7 @@ function run_scripts() scripts = { "spells.lua", + "familiars.lua", "hse-portals.lua", "hse-stats.lua" } diff --git a/src/scripts/hse4-run.lua b/src/scripts/hse4-run.lua index b8039b8c3..583fabcc7 100644 --- a/src/scripts/hse4-run.lua +++ b/src/scripts/hse4-run.lua @@ -1,6 +1,7 @@ function run_scripts() scripts = { "spells.lua", + "familiars.lua", "hse-portals.lua", "hse-stats.lua" } diff --git a/src/scripts/wdw-run.lua b/src/scripts/wdw-run.lua index a8f0bb87c..edd65ad4e 100644 --- a/src/scripts/wdw-run.lua +++ b/src/scripts/wdw-run.lua @@ -42,6 +42,7 @@ print("- Running wdw-run.lua") scripts = { "spells.lua", + "familiars.lua", "wdw-sphinx.lua", "wdw-standings.lua" }