/* vi: set ts=2: * * * Eressea PB(E)M host Copyright (C) 1998-2003 * Christian Schlittchen (corwin@amber.kn-bremen.de) * Katja Zedel (katze@felidae.kn-bremen.de) * Henning Peters (faroul@beyond.kn-bremen.de) * Enno Rehling (enno@eressea.de) * Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) * * This program may not be used, modified or distributed without * prior permission by the authors of Eressea. */ #include #include #include "spells.h" #include "buildingcurse.h" #include "regioncurse.h" #include "unitcurse.h" #include "shipcurse.h" #include "alp.h" #include "combatspells.h" /* kernel includes */ #include #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 #include #include #include #include #include #include #include #include /* libc includes */ #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_writechars, a_readchars }; attrib_type at_wdwpyramid = { "wdwpyramid", NULL, NULL, NULL, a_writevoid, a_readvoid }; /* ----------------------------------------------------------------------- */ static void report_spell(unit * mage, region * r, message * msg) { r_addmessage(r, NULL, msg); if (mage && mage->region!=r) { add_message(&mage->faction->msgs, msg); } } static void report_failure(unit * mage, struct order * ord) { /* Fehler: "Der Zauber schlägt fehl" */ cmistake(mage, ord, 180, MSG_MAGIC); } /* ------------------------------------------------------------- */ /* 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; 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 + (rng_int()%10) - 5; mon = max(1, mon); found = true; if (chance(probability)) { /* Analyse geglückt */ if (c_flags(c) & CURSE_NOAGE) { ADDMSG(&mage->faction->msgs, msg_message( "analyse_region_noage", "mage region curse", mage, r, c->type)); } else { ADDMSG(&mage->faction->msgs, msg_message( "analyse_region_age", "mage region curse months", mage, r, c->type, 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; 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 + (rng_int()%10) - 5; mon = max(1,mon); if (chance(probability)) { /* Analyse geglückt */ if (c_flags(c) & CURSE_NOAGE) { ADDMSG(&mage->faction->msgs, msg_message( "analyse_unit_noage", "mage unit curse", mage, u, c->type)); }else{ ADDMSG(&mage->faction->msgs, msg_message( "analyse_unit_age", "mage unit curse months", mage, u, c->type, 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; 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 + (rng_int()%10) - 5; mon = max(1,mon); if (chance(probability)) { /* Analyse geglückt */ if (c_flags(c) & CURSE_NOAGE) { ADDMSG(&mage->faction->msgs, msg_message( "analyse_building_age", "mage building curse", mage, b, c->type)); }else{ ADDMSG(&mage->faction->msgs, msg_message( "analyse_building_age", "mage building curse months", mage, b, c->type, 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; 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 + (rng_int()%10) - 5; mon = max(1,mon); if (chance(probability)) { /* Analyse geglückt */ if (c_flags(c) & CURSE_NOAGE) { ADDMSG(&mage->faction->msgs, msg_message( "analyse_ship_noage", "mage ship curse", mage, sh, c->type)); }else{ ADDMSG(&mage->faction->msgs, msg_message( "analyse_ship_age", "mage ship curse months", mage, sh, c->type, 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 break_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 (c_flags(c1) & 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, FFL_SELECT); for (u = r->units; u; u = u->next ) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { fset(u->faction, FFL_SELECT); /* 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, FFL_SELECT)) { 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 = rng_int()%100; assert(magerace->familiars[0]); if (rnd < 3) { race_list * familiarraces = get_familiarraces(); unsigned int maxlen = listlen(familiarraces); if (maxlen>0) { race_list * rclist = familiarraces; int index = rng_int()%maxlen; while (index-->0) { rclist = rclist->next; } retval = rclist->data; } } else if (rnd < 70) { retval = magerace->familiars[0]; } else { retval = magerace->familiars[magiegebiet]; } return retval; } /* ------------------------------------------------------------- */ /* der Vertraue des Magiers */ static void make_familiar(unit *familiar, unit *mage) { /* skills and spells: */ if (familiar->race->init_familiar!=NULL) { familiar->race->init_familiar(familiar); } else { log_error(("could not perform initialization for familiar %s.\n", familiar->faction->race->_name[0])); } /* 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 = co->magician.u; int cast_level = co->level; const race * rc; skill_t sk; int dh, dh1, bytes; direction_t d; message * msg; char zText[NAMESIZE], * bufp = zText; size_t size = sizeof(zText) - 1; if (get_familiar(mage) != NULL ) { cmistake(mage, co->order, 199, MSG_MAGIC); return 0; } rc = select_familiar(mage->faction->race, mage->faction->magiegebiet); if (rc==NULL) { log_error(("could not find suitable familiar for %s.\n", mage->faction->race->_name[0])); return 0; } 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 = rng_int()%coasts; dh = -1; for(d=0; dterrain, SEA_REGION)) { dh++; if (dh == coasts) break; } } target_region = rconnect(r,d); } msg = msg_message("familiar_name", "unit", mage); nr_render(msg, mage->faction->locale, zText, sizeof(zText), mage->faction); msg_release(msg); familiar = create_unit(target_region, mage->faction, 1, rc, 0, zText, mage); setstatus(familiar, ST_FLEE); fset(familiar, UFL_LOCKED); make_familiar(familiar, mage); dh = 0; dh1 = 0; for (sk=0;skbonus[sk] > -5) dh++; } for (sk=0;skbonus[sk] > -5) { dh--; if (dh1 == 0) { dh1 = 1; } else { if (dh == 0) { bytes = (int)strlcpy(bufp, (const char*)LOC(mage->faction->locale, "list_and"), size); } else { bytes = (int)strlcpy(bufp, (const char*)", ", size); } if (wrptr(&bufp, &size, bytes)!=0) WARN_STATIC_BUFFER(); } bytes = (int)strlcpy(bufp, (const char*)skillname(sk, mage->faction->locale), size); if (wrptr(&bufp, &size, bytes)!=0) WARN_STATIC_BUFFER(); } } ADDMSG(&mage->faction->msgs, msg_message("familiar_describe", "mage race skills", mage, rc, zText)); 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 = co->magician.u; 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; -- farcasting! */ region *tr = co->rt; ap = &tr->attribs; write_regionname(tr, mage->faction, ts, sizeof(ts)); break; } case SPP_TEMP: case SPP_UNIT: { unit *u; u = pa->param[0]->data.u; ap = &u->attribs; write_unitname(u, ts, sizeof(ts)); break; } case SPP_BUILDING: { building *b; b = pa->param[0]->data.b; ap = &b->attribs; write_buildingname(b, ts, sizeof(ts)); break; } case SPP_SHIP: { ship *sh; sh = pa->param[0]->data.sh; ap = &sh->attribs; write_shipname(sh, ts, sizeof(ts)); break; } default: return 0; } succ = break_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, 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) * */ static int sp_transferaura(castorder *co) { int aura, gain, multi = 2; unit *mage = co->magician.u; 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|TESTRESISTANCE) */ static int sp_goodwinds(castorder *co) { region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; int duration = cast_level+1; spellparameter *pa = co->par; message * m; 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, FFL_SELECT); for(u = r->units; u; u = u->next ) freset(u->faction, FFL_SELECT); m = msg_message("wind_effect", "mage ship", mage, sh); for(u = r->units; u; u = u->next ) { if (u->ship != sh ) /* nur den Schiffsbesatzungen! */ continue; if (!fval(u->faction, FFL_SELECT) ) { r_addmessage(r, u->faction, m); fset(u->faction, FFL_SELECT); } } if (!fval(mage->faction, FFL_SELECT)) { 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 = co->magician.u; if (!fval(r->terrain, LAND_REGION)) { 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 = co->magician.u; 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, NULL, 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(unseen); msg_release(seen); } 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) * */ static int sp_blessstonecircle(castorder *co) { building *b; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; spellparameter *p = co->par; message * msg; /* 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")) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_notstonecircle", "building", b)); return 0; } if (b->size < b->type->maxsize) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_notcomplete", "building", b)); return 0; } b->type = bt_find("blessedstonecircle"); msg = msg_message("blessedstonecircle_effect", "mage building", mage, b); add_message(&r->msgs, msg); msg_release(msg); 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 = co->magician.u; int cast_level = co->level; curse * c; double power = co->force; variant effect; int duration = (int)power+1; if (!fval(r->terrain, SEA_REGION)) { 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, &r->attribs, ct_find("maelstrom"), power, duration, effect, 0); /* 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 = co->magician.u; if (!fval(r->terrain, LAND_REGION)) { 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; /* 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, NULL, 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 = co->magician.u; 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 = rng_int()%((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 = co->magician.u; 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) */ /* Syntax: ZAUBER "Funkenregen" eh1 */ static int sp_sparkle(castorder *co) { unit *u; unit *mage = co->magician.u; 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 = rng_int(); 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 = co->magician.u; int cast_level = co->level; double force = co->force; int number = lovar(force*8*RESOURCE_QUANTITY); if (number<1) number = 1; if (r->terrain == newterrain(T_SWAMP)) { cmistake(mage, co->order, 188, MSG_MAGIC); return 0; } u2 = create_unit(r, mage->faction, number, new_race[RC_IRONGOLEM], 0, NULL, 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 = co->magician.u; int cast_level = co->level; int number = lovar(co->force*5*RESOURCE_QUANTITY); if (number<1) number = 1; if (r->terrain == newterrain(T_SWAMP)) { cmistake(mage, co->order, 188, MSG_MAGIC); return 0; } u2 = create_unit(r, mage->faction, number, new_race[RC_STONEGOLEM], 0, NULL, 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 = co->magician.u; int cast_level = co->level; double force = co->force; int duration = 2; variant effect; if (fval(r->terrain, SEA_REGION) ) { 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 (rng_int() % 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 (rng_int() % 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; } } if (!fval(r->terrain, SEA_REGION)) { /* not destroying the region, so it should be safe to make this a local * message */ message * msg; const char * mtype; if (r->terrain == newterrain(T_SWAMP) && terraform) { mtype = "drought_effect_1"; } else if (!terraform) { mtype = "drought_effect_2"; } else { mtype = "drought_effect_3"; } msg = msg_message(mtype, "mage region", mage, r); add_message(&r->msgs, msg); msg_release(msg); } else { /* possible that all units here get killed so better to inform with a global * message */ message * msg = msg_message("drought_effect_4", "mage region", mage, r); for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) { if (!fval(u->faction, FFL_SELECT)) { fset(u->faction, FFL_SELECT); add_message(&u->faction->msgs, msg); } } if (!fval(mage->faction, FFL_SELECT)) { add_message(&mage->faction->msgs, msg); } msg_release(msg); } 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 = co->magician.u; 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact", "target", u)); } else { int w; message * m; unit * u2; 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 */ for (u2 = r->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = r->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, r, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(r, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } } 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 = co->magician.u; double power = co->force; spellparameter *pa = co->par; int cast_level = co->level; if (getplane(r) != get_astralplane()) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralonly", "")); return 0; } if (is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact", "target", u)); } else { int w = weight(u); if (!can_survive(u, rt)) { cmistake(mage, co->order, 231, MSG_MAGIC); } else if (remaining_cap - w < 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "fail_tooheavy", "target", u)); } else { message * m; 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, FFL_SELECT); m = NULL; for(u2 = r->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, r, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(r, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } } } 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) { static const curse_type * ctype = NULL; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; curse * c; message * msg = msg_message("sp_holyground_effect", "mage region", mage, r); report_spell(mage, r, msg); msg_release(msg); if (!ctype) ctype = ct_find("holyground"); c = create_curse(mage, &r->attribs, ctype, power*power, 1, zero_effect, 0); 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 = co->magician.u; int cast_level = co->level; double force = co->force; variant effect; message * msg; 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; } c_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); c_setflag(c, CURSE_NOAGE); /* melden, 1x pro Partei in der Burg */ for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); msg = msg_message("homestone_effect", "mage building", mage, mage->building); for (u = r->units; u; u = u->next) { if (!fval(u->faction, FFL_SELECT)) { fset(u->faction, FFL_SELECT); if (u->building == mage->building) { r_addmessage(r, u->faction, msg); } } } msg_release(msg); 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; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; int duration = (int)power+1; message * msg; if (fval(r->terrain, SEA_REGION) ) { cmistake(mage, co->order, 189, MSG_MAGIC); /* TODO: vielleicht einen netten Patzer hier? */ return 0; } /* melden, 1x pro Partei */ msg = msg_message("sp_drought_effect", "mage region", mage, r); report_spell(mage, r, msg); msg_release(msg); /* 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 *siehe code* * 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 = co->magician.u; int cast_level = co->level; double power = co->force; region_list *rl, *rl2; int range = 1+(int)(power/8); int duration = 2+(int)(power/6); rl = all_in_range(r, (short)range, NULL); for (rl2 = rl; rl2; rl2 = rl2->next) { static const curse_type * ctype = NULL; region * r2 = rl2->data; curse * c; variant effect; message * m = NULL; if (!fval(r2->terrain, SEA_REGION) && !r_isforest(r2)) 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; if (!ctype) ctype = ct_find("disorientationzone"); c = create_curse(mage, &r2->attribs, ctype, power, duration, effect, 0); for (u = r2->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r2->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { fset(u->faction, FFL_SELECT); if (!m) m = msg_message("confusion_result", "mage", mage); add_message(&u->faction->msgs, m); } } if (!fval(mage->faction, FFL_SELECT)) { if (!m) m = msg_message("confusion_result", "mage", mage); add_message(&mage->faction->msgs, m); } if (m) msg_release(m); } 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 = co->magician.u; int cast_level = co->level; message * msg; 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, NULL, mage); /*keeper->age = cast_level + 2;*/ guard(keeper, GUARD_MINING); fset(keeper, UFL_ISNEW); setstatus(keeper, 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)); } msg = msg_message("summon_effect", "mage amount race", mage, 1, keeper->race); r_addmessage(r, NULL, msg); msg_release(msg); 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) { ship *sh; unit *u; int erfolg = 0; region *r = co->rt; unit *mage = co->magician.u; double power = co->force; spellparameter *pa = co->par; int n, force = (int)power; message * m = NULL; /* melden vorbereiten */ freset(mage->faction, FFL_SELECT); for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); 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) ) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_spell_on_flying_ship", "ship", sh)) continue; } if (is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_spell_on_ship_already", "ship", sh)) 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! */ fset(u->faction, FFL_SELECT); } } } /* melden, 1x pro Partei auf Schiff und für den Magier */ fset(mage->faction, FFL_SELECT); for (u = r->units; u; u = u->next ) { if (fval(u->faction, FFL_SELECT)) { freset(u->faction, FFL_SELECT); if (erfolg > 0) { if (!m) { m = msg_message("stormwinds_effect", "unit", mage); } r_addmessage(r, u->faction, m); } } } if (m) msg_release(m); 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 = co->magician.u; int cast_level = co->level; message * msg; 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 */ msg = msg_message("earthquake_effect", "mage region", mage, r); r_addmessage(r, NULL, msg); msg_release(msg); return cast_level; } /* ------------------------------------------------------------- */ /* CHAOS / M_CHAOS / Draig */ /* ------------------------------------------------------------- */ void patzer_peasantmob(castorder *co) { int anteil = 6, n; unit *u; attrib *a; region *r; unit *mage = co->magician.u; 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; message * msg; anteil += rng_int() % 4; n = rpeasants(r) * anteil / 10; rsetpeasants(r, rpeasants(r) - n); assert(rpeasants(r) >= 0); u = create_unit(r, f, n, new_race[RC_PEASANT], 0, LOC(f->locale, "angry_mob"), NULL); fset(u, UFL_ISNEW); /* guard(u, GUARD_ALL); hier zu früh! Befehl BEWACHE setzten */ addlist(&u->orders, create_order(K_GUARD, lang, NULL)); 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)); msg = msg_message("mob_warning", ""); r_addmessage(r, NULL, msg); msg_release(msg); } 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 = co->magician.u; int cast_level = co->level; double probability; double percentage = (rng_int() % 8 + 1) * 0.1; /* 10 - 80% */ message * msg; 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, FFL_SELECT); msg = msg_message("forestfire_effect", "mage region amount", mage, r, destroyed+vernichtet_schoesslinge); r_addmessage(r, NULL, msg); add_message(&mage->faction->msgs, msg); msg_release(msg); 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 | TESTCANSEE | TESTRESISTANCE) * */ static int sp_fumblecurse(castorder *co) { unit *target; int rx, sx; int duration; unit *mage = co->magician.u; 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 = rng_int()%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; } ADDMSG(&target->faction->msgs, msg_message( "fumblecurse", "unit region", target, target->region)); return cast_level; } void patzer_fumblecurse(castorder *co) { unit *mage = co->magician.u; 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)); } 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 = co->magician.u; 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 (r->terrain != newterrain(T_SWAMP) && rterrain(r) != T_DESERT && rterrain(r) != T_GLACIER) { report_failure(mage, co->order); return 0; } for(time = 1; time < 7; time++) { if (rng_int()%100 < 25) { switch(rng_int()%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) { region * r2 = rl2->data; for(u = r2->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; } #if 0 sprintf(buf, "Kommt aus: %s, Will nach: %s", regionname(r2, u->faction), regionname(co->rt, u->faction)); usetprivate(u, buf); #endif } } } ADDMSG(&mage->faction->msgs, msg_message( "summondragon", "unit region command target", 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), 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->next; } if (!a || a->type!=&at_cursewall) { a = a_find(b->to->attribs, &at_cursewall); while (a && a->type==&at_cursewall && 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->next; } } if (a && a->type==&at_cursewall) { 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 = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; direction_t dir; region * r2; dir = finddirection(pa->param[0]->data.xs, 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+1; } else { a->data.i = max(a->data.i, cast_level+1); } /* 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"; } typedef struct wisps_data { wall_data wall; int rnd; } wisps_data; static region * wisps_move(const border * b, struct unit * u, struct region * from, struct region * next, boolean routing) { direction_t reldir = reldirection(from, next); wisps_data * wd = (wisps_data*)b->data.v; assert(reldir!=D_SPECIAL); if (routing && wd->wall.active) { region * rl = rconnect(from, (direction_t)((reldir+MAXDIRECTIONS-1)%MAXDIRECTIONS)); region * rr = rconnect(from, (direction_t)((reldir+1)%MAXDIRECTIONS)); /* pick left and right region: */ if (wd->rnd<0) { wd->rnd = rng_int() % 3; } if (wd->rnd == 1 && rl && fval(rl->terrain, LAND_REGION)==fval(next, LAND_REGION)) return rl; if (wd->rnd == 2 && rr && fval(rr->terrain, LAND_REGION)==fval(next, LAND_REGION)) return rr; } return next; } static void wisps_init(border * b) { wisps_data * wd = (wisps_data*)calloc(sizeof(wisps_data), 1); b->data.v = wd; wd->rnd = -1; } border_type bt_wisps = { "wisps", VAR_VOIDPTR, b_transparent, /* transparent */ wisps_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 = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; dir = finddirection(pa->param[0]->data.xs, 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 = co->magician.u; 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) */ static int dc_age(struct curse * c) /* age returns 0 if the attribute needs to be removed, !=0 otherwise */ { region * r = (region*)c->data.v; unit **up; unit * mage = c->magician; if (r==NULL || mage==NULL || mage->number==0) { /* if the mage disappears, so does the spell. */ return -1; } up = &r->units; if (curse_active(c)) while (*up!=NULL) { unit * u = *up; double damage = c->effect.f * u->number; freset(u->faction, FFL_SELECT); if (u->number<=0 || target_resists_magic(mage, u, TYP_UNIT, 0)) { up=&u->next; continue; } /* Reduziert durch Magieresistenz */ damage *= (1.0 - magic_resistance(u)); change_hitpoints(u, -(int)damage); if (*up==u) up=&u->next; } return 0; } static struct curse_type ct_deathcloud = { "deathcloud", CURSETYP_REGION, 0, NO_MERGE, cinfo_simple, NULL, NULL, NULL, NULL, dc_age }; static curse * mk_deathcloud(unit * mage, region * r, double force, int duration) { variant effect; curse * c; effect.f = (float)force/2; c = create_curse(mage, &r->attribs, &ct_deathcloud, force, duration, effect, 0); c->data.v = r; return c; } #define COMPAT_DEATHCLOUD #ifdef COMPAT_DEATHCLOUD static int dc_read_compat(struct attrib * a, FILE* F) /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ { char zId[16]; region * r = NULL; unit * u; variant var; int duration; double strength; fscanf(F, "%d %lf %s", &duration, &strength, zId); var.i = atoi36(zId); u = findunit(var.i); read_region_reference(&r, F); if (r!=NULL) { variant effect; curse * c; effect.f = (float)strength; c = create_curse(u, &r->attribs, &ct_deathcloud, strength * 2, duration, effect, 0); c->data.v = r; if (u==NULL) { ur_add(var, (void**)&c->magician, resolve_unit); } } return AT_READ_FAIL; /* we don't care for the attribute. */ } attrib_type at_deathcloud_compat = { "zauber_todeswolke", NULL, NULL, NULL, NULL, dc_read_compat }; #endif static int sp_deathcloud(castorder *co) { region *r = co->rt; unit *mage = co->magician.u; attrib *a = r->attribs; unit * u; while (a) { if ((a->type->flags & ATF_CURSE)) { curse * c = a->data.v; if (c->type==&ct_deathcloud) { report_failure(mage, co->order); return 0; } a = a->next; } else a = a->nexttype; } mk_deathcloud(mage, r, co->force, co->level); /* melden, 1x pro Partei */ for (u = r->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { fset(u->faction, FFL_SELECT); ADDMSG(&u->faction->msgs, msg_message("deathcloud_effect", "mage region", cansee(u->faction, r, mage, 0) ? mage : NULL, r)); } } if (!fval(mage->faction, FFL_SELECT)) { ADDMSG(&mage->faction->msgs, msg_message("deathcloud_effect", "mage region", mage, r)); } return co->level; } void patzer_deathcloud(castorder *co) { unit *mage = co->magician.u; int hp = (mage->hp - 2); change_hitpoints(mage, -rng_int()%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 = co->magician.u; 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 = co->magician.u; int cast_level = co->level; double force = co->force; unit *u; int val, number = (int)(force*force); u = create_unit(r, mage->faction, number, 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); ADDMSG(&mage->faction->msgs, msg_message("summonshadow_effect", "mage number", mage, number)); 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 = co->magician.u; 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); ADDMSG(&mage->faction->msgs, msg_message("summon_effect", "mage amount race", mage, 1, u->race)); 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) { region *rt; region *r = co->rt; unit *mage = co->magician.u; 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) { /* Hier gibt es keine Verbindung zur astralen Welt.*/ cmistake(mage, co->order, 216, MSG_MAGIC); return 0; } else if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); return 0; } create_special_direction(r, rt, 2, "vortex_desc", "vortex"); create_special_direction(rt, r, 2, "vortex_desc", "vortex"); new_border(&bt_chaosgate, r, rt); add_message(&r->msgs, msg_message("chaosgate_effect_1", "mage", mage)); add_message(&rt->msgs, msg_message("chaosgate_effect_2", "")); 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 = co->magician.u; 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); /* 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 = co->magician.u; 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; } /** gives a summoned undead unit some base skills. */ static void skill_summoned(unit * u, int level) { if (level>0) { const race * rc = u->race; skill_t sk; for (sk=0;sk!=MAXSKILLS;++sk) { if (rc->bonus[sk>0]) { set_level(u, sk, 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 = co->magician.u; int cast_level = co->level; int force = (int)(co->force*10); const race * race = new_race[RC_SKELETON]; if (!r->land || deathcount(r) == 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_nograves", "target", r)); 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); skill_summoned(u, cast_level/2); ADDMSG(&mage->faction->msgs, msg_message("summonundead_effect_1", "mage region amount", mage, r, undead)); ADDMSG(&r->msgs, msg_message("summonundead_effect_2", "mage region", mage, r)); 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 = co->magician.u; int cast_level = co->level; message * msg; 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, FFL_SELECT); } msg = msg_message("cast_auraleak_effect", "mage region", mage, r); r_addmessage(r, NULL, msg); msg_release(msg); return cast_level; } /* ------------------------------------------------------------- */ /* BARDE - CERDDOR*/ /* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */ /* 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 = co->magician.u; 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|TESTCANSEE) */ static int sp_analysesong_unit(castorder *co) { unit *u; unit *mage = co->magician.u; 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 | TESTCANSEE) */ static int sp_charmingsong(castorder *co) { unit *target; int duration; skill_t i; unit *mage = co->magician.u; 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); #if 0 sprintf(buf, "%s fühlt sich einen Moment lang benommen und desorientiert.", unitname(target)); addmessage(target->region, target->faction, buf, MSG_EVENT, ML_WARN); #endif return 0; } duration = 3 + rng_int()%(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); ADDMSG(&mage->faction->msgs, msg_message("charming_effect", "mage unit duration", mage, target, duration)); 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; int cast_level = co->level; message * msg; curse * c; 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; } } c = get_curse(r->attribs, ct_find(oldcursename(C_RIOT))); if (c!=NULL) { remove_curse(&r->attribs, c); } msg = msg_message("cast_rally_effect", "mage region", mage, r); r_addmessage(r, NULL, msg); msg_release(msg); 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 = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force+1; faction * monsters = findfaction(MONSTER_FACTION); message * msg; anteil.i = 6 + (rng_int()%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 = create_unit(r, monsters, n, new_race[RC_PEASANT], 0, LOC(monsters->locale, "furious_mob"), NULL); fset(u, UFL_ISNEW); 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); msg = msg_message("sp_raisepeasantmob_effect", "mage region", mage, r); report_spell(mage, r, msg); msg_release(msg); 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 | TESTCANSEE) */ static int sp_migranten(castorder *co) { unit *target; region *r = co->rt; unit *mage = co->magician.u; 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; /* 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_nomonsters", "")); return 0; } /* niemand mit teurem Talent */ if (teure_talente(target)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_noexpensives", "target", target)); return 0; } /* maximal Stufe Personen */ if (target->number > cast_level || target->number > max_spellpoints(r, mage)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_toomanytargets", "")); return 0; } /* Kontakt prüfen (aus alter Teleportroutine übernommen) */ if (!ucontact(target, mage)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail::contact", "target", target)); return 0; } u_setfaction(target,mage->faction); set_order(&target->thisorder, NULL); /* Erfolg melden */ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "sp_migranten", "target", 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 = co->magician.u; int cast_level = co->level; double force = co->force; int duration = 2 + lovar(force/2); message * msg[2] = { NULL, NULL }; create_curse(mage, &r->attribs, ct_find("peacezone"), force, duration, zero_effect, 0); for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { message * m = NULL; fset(u->faction, FFL_SELECT); if (cansee(u->faction, r, mage, 0)) { if (msg[0]==NULL) msg[0] = msg_message("song_of_peace_effect_0", "mage", mage); m = msg[0]; } else { if (msg[1]==NULL) msg[1] = msg_message("song_of_peace_effect_1", ""); m = msg[1]; } r_addmessage(r, u->faction, m); } } if (msg[0]) msg_release(msg[0]); if (msg[1]) msg_release(msg[1]); 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 = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force+1; variant effect; message * msg[2] = { NULL, NULL }; if (is_cursed(r->attribs, C_DEPRESSION, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_generous", "")); 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, FFL_SELECT); for (u = r->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { message * m = NULL; fset(u->faction, FFL_SELECT); if (cansee(u->faction, r, mage, 0)) { if (msg[0]==NULL) msg[0] = msg_message("song_of_peace_effect_0", "mage", mage); m = msg[0]; } else { if (msg[1]==NULL) msg[1] = msg_message("song_of_peace_effect_1", ""); m = msg[1]; } r_addmessage(r, u->faction, m); } } if (msg[0]) msg_release(msg[0]); if (msg[1]) msg_release(msg[1]); 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 = co->magician.u; 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, LOC(f->locale,(n == 1 ? "peasant" : "peasant_p")), mage); set_order(&u->thisorder, default_order(f->locale)); ADDMSG(&mage->faction->msgs, msg_message("recruit_effect", "mage amount", mage, n)); 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 = co->magician.u; int cast_level = co->level; double force = co->force; faction *f = mage->faction; message * msg; 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, LOC(f->locale,(n == 1 ? "peasant" : "peasant_p")), mage); set_order(&u->thisorder, default_order(f->locale)); msg = msg_message("recruit_effect", "mage amount", mage, n); r_addmessage(r, mage->faction, msg); msg_release(msg); 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 | TESTCANSEE) */ /* restistenz der einheit prüfen */ static int sp_pump(castorder *co) { unit *u, *target; region *rt; boolean see = false; unit *mage = co->magician.u; spellparameter *pa = co->par; int cast_level = co->level; /* 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_not_on_undead", "")); 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) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_pump", "target tregion", target, rt)); return cast_level/2; } else { ADDMSG(&mage->faction->msgs, msg_message("pump_effect", "mage unit tregion", mage, target, rt)); } u = create_unit(rt, mage->faction, RS_FARVISION, new_race[RC_SPELL], 0, "spell/pump", NULL); 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 | TESTRESISTANCE | TESTCANSEE) */ static int sp_seduce(castorder *co) { item *items = NULL; unit *target; item **itmp; unit *mage = co->magician.u; spellparameter *pa = co->par; int cast_level = co->level; 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_noundead", "")); return 0; } /* Erfolgsmeldung */ itmp=&target->items; while (*itmp) { item * itm = *itmp; int loot; if (itm->type==i_silver) { loot = min(cast_level * 1000, get_money(target) - (maintenance_cost(target))); loot = max(loot, 0); } else { loot = itm->number/2; if (itm->number % 2) { loot += rng_int() % 2; } if (loot > 0) { loot = (int)min(loot, force * 5); } } if (loot>0) { i_change(&mage->items, itm->type, loot); i_change(&items, itm->type, loot); i_change(itmp, itm->type, -loot); } if (*itmp==itm) itmp=&itm->next; } ADDMSG(&mage->faction->msgs, msg_message("seduce_effect_0", "mage unit items", mage, target, items)); i_freeall(&items); ADDMSG(&target->faction->msgs, msg_message("seduce_effect_1", "unit", target)); 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 | TESTRESISTANCE | TESTCANSEE) */ static int sp_calm_monster(castorder *co) { curse * c; unit *target; unit *mage = co->magician.u; spellparameter *pa = co->par; int cast_level = co->level; double force = co->force; variant effect; message * msg; /* 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_noundead", "")); 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; } msg = msg_message("calm_effect", "mage unit", mage, target); r_addmessage(mage->region, mage->faction, msg); msg_release(msg); 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 | TESTRESISTANCE | TESTCANSEE) */ static int sp_headache(castorder *co) { skill * smax = NULL; int i; unit *target; unit *mage = co->magician.u; spellparameter *pa = co->par; int cast_level = co->level; message * msg; /* Macht alle nachfolgenden Zauber doppelt so teuer */ countspells(mage, 1); target = pa->param[0]->data.u; /* Zieleinheit */ /* wenn kein Ziel gefunden, Zauber abbrechen */ if (target->number==0 || pa->param[0]->flag == TARGET_NOTFOUND) return 0; /* 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) * (rng_int()%2+1) / target->number; reduce_skill(target, smax, change); } set_order(&target->thisorder, NULL); msg = msg_message("hangover_effect_0", "mage unit", mage, target); r_addmessage(mage->region, mage->faction, msg); msg_release(msg); msg = msg_message("hangover_effect_1", "unit", target); r_addmessage(target->region, target->faction, msg); msg_release(msg); 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 *u2; attrib *a; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; message * msg; if (rpeasants(r) == 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_nopeasants", "")); 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, LOC(mage->faction->locale, "furious_mob"), mage); 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); msg = msg_message("sp_raisepeasants_effect", "mage region amount", mage, r, u2->number); r_addmessage(r, NULL, msg); if (mage->region!=r) { add_message(&mage->faction->msgs, msg); } msg_release(msg); 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) { region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force+1; message * msg; create_curse(mage, &r->attribs, ct_find("depression"), force, duration, zero_effect, 0); msg = msg_message("sp_depression_effect", "mage region", mage, r); r_addmessage(r, NULL, msg); if (mage->region!=r) { add_message(&mage->faction->msgs, msg); } msg_release(msg); 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 = co->magician.u; 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) { region * r2 = rl2->data; for(u = r2->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(r2, u->faction), regionname(r, u->faction)); usetprivate(u, buf); } } } ADDMSG(&mage->faction->msgs, msg_message( "summondragon", "unit region command target", 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; icastle_data * data; const char * bname; message * msg; static const building_type * bt_illusion; if (bt_illusion==NULL) bt_illusion = bt_find("illusioncastle"); if (bt_illusion==NULL) { return 0; } if ((type=findbuildingtype(pa->param[0]->data.xs, mage->faction->locale)) == NULL) { type = bt_find("castle"); } b = new_building(bt_illusion, r, mage->faction->locale); /* Größe festlegen. */ if (type == bt_illusion) { b->size = (rng_int()%(int)((power*power)+1)*10); } else if (type->maxsize == -1) { b->size = ((rng_int()%(int)(power))+1)*5; } else { b->size = type->maxsize; } if (type->name==NULL) { bname = LOC(mage->faction->locale, type->_name); } else { bname = LOC(mage->faction->locale, buildingtype(type, b, 0)); } free(b->name); b->name = strdup(bname); /* 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+(rng_int()%(int)(power)+1)*(rng_int()%(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)); msg = msg_message("sp_icastle_effect", "region", r); report_spell(mage, r, msg); msg_release(msg); 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) */ int sp_illusionary_shapeshift(castorder *co) { unit *u; const race * rc; unit *mage = co->magician.u; 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.xs, mage->faction->locale); if (rc == NULL) { cmistake(mage, co->order, 202, MSG_MAGIC); return 0; } /* ähnlich wie in laws.c:setealth() */ if (!playerrace(rc)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "sp_shapeshift_fail", "target race", u, rc)); return 0; } if (u->irace == u->race) { trigger * trestore = trigger_changerace(u, NULL, u->irace); add_trigger(&u->attribs, "timer", trigger_timeout((int)power+2, trestore)); u->irace = rc; } ADDMSG(&mage->faction->msgs, msg_message("shapeshift_effect", "mage target race", mage, u, rc)); 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; 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 + rng_int()%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); /* 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 = co->magician.u; 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 + rng_int()%duration; effect.i = 1; c = create_curse(mage, &r->attribs, ct_find("gbdream"), 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: * 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 = co->magician.u; int cast_level = co->level; message * msg; char name[NAMESIZE]; if (get_clone(mage) != NULL ) { cmistake(mage, co->order, 298, MSG_MAGIC); return 0; } snprintf(name, sizeof(name), (const char*)LOC(mage->faction->locale, "clone_of"), unitname(mage)); clone = create_unit(target_region, mage->faction, 1, new_race[RC_CLONE], 0, name, mage); setstatus(clone, ST_FLEE); fset(clone, UFL_LOCKED); create_newclone(mage, clone); msg = msg_message("sp_clone_effet", "mage", mage); r_addmessage(r, mage->faction, msg); msg_release(msg); return cast_level; } /* ------------------------------------------------------------- */ int sp_dreamreading(castorder *co) { unit *u,*u2; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; spellparameter *pa = co->par; double power = co->force; message * msg; /* 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_unitnotfound(mage, co->order, pa->param[0])); return 0; } /* Entfernung */ if (distance(mage->region, u->region) > power) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_distance", "")); return 0; } u2 = create_unit(u->region,mage->faction, RS_FARVISION, new_race[RC_SPELL], 0, "spell/dreamreading", NULL); set_number(u2, 1); u2->age = 2; /* Nur für diese Runde. */ set_level(u2, SK_OBSERVATION, eff_skill(u, SK_OBSERVATION, u2->region)); msg = msg_message("sp_dreamreading_effect", "mage unit region", mage, u, u->region); r_addmessage(r, mage->faction, msg); msg_release(msg); 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 = co->magician.u; 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; message * msg; /* 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); msg = msg_message("sp_sweetdreams_effect", "mage unit region", mage, u, r); r_addmessage(r, mage->faction, msg); if (u->faction!=mage->faction) { r_addmessage(r, u->faction, msg); } msg_release(msg); } return cast_level; } /* ------------------------------------------------------------- */ int sp_disturbingdreams(castorder *co) { region *r = co->rt; unit *mage = co->magician.u; 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); ADDMSG(&mage->faction->msgs, msg_message("sp_disturbingdreams_effect", "mage region", mage, r)); return cast_level; } /* ------------------------------------------------------------- */ int sp_dream_of_confusion(castorder *co) { unit *u; region_list *rl,*rl2; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; int range = 1+(int)(power/8); int duration = 2+(int)(power/6); rl = all_in_range(r, (short)range, NULL); for(rl2 = rl; rl2; rl2 = rl2->next) { region * r2 = rl2->data; variant effect; curse * c; message * m = NULL; /* 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); for (u = r2->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r2->units; u; u = u->next ) { if (!fval(u->faction, FFL_SELECT) ) { fset(u->faction, FFL_SELECT); if (!m) m = msg_message("confusion_result", "mage", mage); add_message(&u->faction->msgs, m); } } if (!fval(mage->faction, FFL_SELECT)) { if (!m) m = msg_message("confusion_result", "mage", mage); add_message(&u->faction->msgs, m); } if (m) msg_release(m); } 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 = co->magician.u; 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 = co->magician.u; 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 = co->magician.u; 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++) { message * msg; /* 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); msg = msg_message("magicresistance_effect", "unit", u); add_message(&u->faction->msgs, msg); /* und noch einmal dem Magier melden */ if (u->faction != mage->faction) { add_message(&mage->faction->msgs, msg); } msg_release(msg); } /* 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; } /** spell 'Astraler Weg'. * Syntax "ZAUBERE [STUFE n] 'Astraler Weg' [ ...]", * * Parameter: * pa->param[0]->data.xs */ int sp_enterastral(castorder *co) { region *rt, *ro; unit *u, *u2; int remaining_cap; int n, w; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; switch(getplaneid(r)) { case 0: rt = r_standard_to_astral(r); ro = r; break; default: cmistake(mage, co->order, 190, MSG_MAGIC); return 0; } if (!rt) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_astralregion", "")); return 0; } if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_no_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("send_astral", "unit target", mage, u)); } else { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("try_astral", "unit target", mage, u)); continue; } } w = weight(u); if (!can_survive(u, rt)) { cmistake(mage, co->order, 231, MSG_MAGIC); } else if (remaining_cap - w < 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "fail_tooheavy", "target", u)); } else { message * m; 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, FFL_SELECT); m = NULL; for (u2 = r->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, r, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(r, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } } return cast_level; } /** Spell 'Astraler Ruf' / 'Astral Call'. */ 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 = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; 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) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail::nocontact", "target", rt)); free_regionlist(rl); return 0; } free_regionlist(rl); break; default: ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralonly", "")); return 0; } if (is_cursed(rt->attribs, C_ASTRALBLOCK, 0) || is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); return 0; } remaining_cap = (int)((power-3) * 1500); /* für jede Einheit in der Kommandozeile */ for (n = 1; n < pa->length; n++) { spllprm * spobj = pa->param[n]; if (spobj->flag == TARGET_NOTFOUND) continue; u = spobj->data.u; if (u->region!=ro) { /* Report this as unit not found */ if (spobj->typ == SPP_UNIT) { spobj->data.i = u->no; } else { spobj->data.i = ualias(u); } spobj->flag = TARGET_NOTFOUND; ADDMSG(&mage->faction->msgs, msg_unitnotfound(mage, co->order, spobj)); return false; } if (!ucontact(u, mage)) { if (power > 12 && spobj->flag != TARGET_RESISTS && can_survive(u, rt)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_no_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("send_astral", "unit target", mage, u)); } else { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("try_astral", "unit target", mage, u)); continue; } } w = weight(u); if (!can_survive(u, rt)) { cmistake(mage, co->order, 231, MSG_MAGIC); } else if (remaining_cap - w < 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "fail_tooheavy", "target", u)); } else { message * m; 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, FFL_SELECT); m = NULL; for (u2 = r->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, r, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(r, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } } 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 = co->magician.u; 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) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail::noway", "")); return 0; } rl = astralregions(r, inhabitable); rl2 = rl; while (rl2!=NULL) { if (rl2->data == rt) break; rl2 = rl2->next; } if (rl2==NULL) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail::noway", "")); free_regionlist(rl); return 0; } free_regionlist(rl); break; default: ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spell_astral_only", "")); return 0; } if (ro==NULL || is_cursed(ro->attribs, C_ASTRALBLOCK, 0) || is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_no_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("send_astral", "unit target", mage, u)); } else { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("try_astral", "unit target", mage, u)); continue; } } w = weight(u); if (!can_survive(u, rt)) { cmistake(mage, co->order, 231, MSG_MAGIC); } else if (remaining_cap - w < 0) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "fail_tooheavy", "target", u)); } else { message * m; 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, FFL_SELECT); m = NULL; for (u2 = r->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, r, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(r, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } } return cast_level; } int sp_fetchastral(castorder *co) { int n; unit *mage = co->magician.u; 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_feedback(mage, co->order, "spellfail_distance", "")); return 0; } /* für jede Einheit in der Kommandozeile */ for (n=0; n!=pa->length; ++n) { unit * u2, * u = pa->param[n]->data.u; int w; message * m; 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()) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralonly", "")); 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_feedback(mage, co->order, "spellfail_distance", "")); 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)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_no_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("send_astral", "unit target", mage, u)); } else { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "feedback_no_contact_resist", "target", u)); ADDMSG(&u->faction->msgs, msg_message("try_astral", "unit target", mage, u)); continue; } } remaining_cap -= w; move_unit(u, rt, NULL); /* Meldungen in der Ausgangsregion */ for (u2 = ro->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = ro->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, ro, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_disappear", "unit", u); r_addmessage(ro, u2->faction, m); } } } if (m) msg_release(m); /* Meldungen in der Zielregion */ for (u2 = rt->units; u2; u2 = u2->next) freset(u2->faction, FFL_SELECT); m = NULL; for (u2 = rt->units; u2; u2 = u2->next ) { if (!fval(u2->faction, FFL_SELECT)) { if (cansee(u2->faction, rt, u, 0)) { fset(u2->faction, FFL_SELECT); if (!m) m = msg_message("astral_appear", "unit", u); r_addmessage(rt, u2->faction, m); } } } if (m) msg_release(m); } 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 = co->magician.u; 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) { region * r2 = rl2->data; if (!is_cursed(r2->attribs, C_ASTRALBLOCK, 0)) { for(u = r2->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 = co->magician.u; int cast_level = co->level; message * m; if (getplaneid(r) != 1) { /* sprintf(buf, "Dieser Zauber kann nur im Astralraum gezaubert werden."); */ ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spell_astral_only", "")); return 0; } if (is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); return 0; } rl = astralregions(r, NULL); /* Irgendwann mal auf Curses u/o Attribut umstellen. */ for (rl2=rl; rl2; rl2=rl2->next) { u = create_unit(rl2->data, mage->faction, RS_FARVISION, new_race[RC_SPELL], 0, "spell/viewreality", NULL); set_level(u, SK_OBSERVATION, co->level/2); u->age = 2; } free_regionlist(rl); m = msg_message("viewreality_effect", "unit", mage); r_addmessage(r, mage->faction, m); msg_release(m); return cast_level; } /* ------------------------------------------------------------- */ int sp_disruptastral(castorder *co) { region_list *rl, *rl2; region *rt; unit *u; region *r = co->rt; unit *mage = co->magician.u; 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; 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 && a->type==&at_direction) { attrib * a2 = a->next; 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 = rng_int() % 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)) { message * msg = msg_message("disrupt_astral", "unit region", u, tr); add_message(&u->faction->msgs, msg); add_message(&tr->msgs, msg); msg_release(msg); move_unit(u, tr, NULL); } } } free_regionlist(trl); } /* Kontakt unterbinden */ effect.i = 100; create_curse(mage, &rl2->data->attribs, ct_find("astralblock"), power, duration, effect, 0); } 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 = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; message * msg; /* 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("nocostbuilding"), power*power, 1, zero_effect, 0); if (c==NULL) { /* ist bereits verzaubert */ cmistake(mage, co->order, 206, MSG_MAGIC); return 0; } /* melden, 1x pro Partei in der Burg */ for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); msg = msg_message("sp_eternizewall_effect", "mage building region", mage, b, r); for (u = r->units; u; u = u->next) { if (!fval(u->faction, FFL_SELECT)) { fset(u->faction, FFL_SELECT); if (u->building == b) { r_addmessage(r, u->faction, msg); } } } if (r!=mage->region) { add_message(&mage->faction->msgs, msg); } else if (mage->building!=b) { r_addmessage(r, mage->faction, msg); } msg_release(msg); 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) * * Syntax: * ZAUBERE \"Opfere Kraft\" * "ui" */ int sp_permtransfer(castorder *co) { int aura; unit *tu; unit *mage = co->magician.u; int cast_level = co->level; spellparameter *pa = co->par; const spell *sp = co->sp; message * msg; /* 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); } msg = msg_message("sp_permtransfer_effect", "mage target amount", mage, tu, aura); add_message(&mage->faction->msgs, msg); if (tu->faction!=mage->faction) { add_message(&tu->faction->msgs, msg); } msg_release(msg); 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 = co->magician.u; int cast_level = co->level; spellparameter *pa = co->par; message* msg; /* 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.xs, mage->faction->locale); if (dir == NODIRECTION) { /* Die Richtung wurde nicht erkannt */ cmistake(mage, co->order, 71, MSG_PRODUCE); return 0; } if (b->size > (cast_level-12) * 250) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "sp_movecastle_fail_0", "")); return cast_level; } target_region = rconnect(r,dir); if (!(target_region->terrain->flags & LAND_REGION)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "sp_movecastle_fail_1", "direction", dir)); return cast_level; } bunhash(b); translist(&r->buildings, &target_region->buildings, b); b->region = target_region; b->size -= b->size/(10-rng_int()%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; } 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."); */ } msg = msg_message("sp_movecastle_effect", "building direction", b, dir); r_addmessage(r, NULL, msg); msg_release(msg); return cast_level; } curse * shipcurse_flyingship(ship* sh, unit * mage, double power, int duration) { static const curse_type * ct_flyingship = NULL; if (!ct_flyingship) { ct_flyingship = ct_find("flyingship"); assert(ct_flyingship); } if (curse_active(get_curse(sh->attribs, ct_flyingship))) { return NULL; } else if (is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0)) { return NULL; } else { /* mit C_SHIP_NODRIFT haben wir kein Problem */ return create_curse(mage, &sh->attribs, ct_flyingship, power, duration, zero_effect, 0); } } /* ------------------------------------------------------------- */ /* 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 | TESTRESISTANCE) */ int sp_flying_ship(castorder *co) { ship *sh; unit *u; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; message * m = NULL; curse * c; /* wenn kein Ziel gefunden, Zauber abbrechen */ if (pa->param[0]->flag == TARGET_NOTFOUND) return 0; sh = pa->param[0]->data.sh; if (sh->type->construction->maxsize>50) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_flying_ship_too_big", "ship", sh)); return 0; } /* Duration = 1, nur diese Runde */ c = shipcurse_flyingship(sh, mage, power, 1); if (c==NULL) { if (is_cursed(sh->attribs, C_SHIP_FLYING, 0) ) { /* Auf dem Schiff befindet liegt bereits so ein Zauber. */ cmistake(mage, co->order, 211, MSG_MAGIC); } else if (is_cursed(sh->attribs, C_SHIP_SPEEDUP, 0) ) { /* Es ist zu gefährlich, ein sturmgepeitschtes Schiff fliegen zu lassen. */ cmistake(mage, co->order, 210, MSG_MAGIC); } return 0; } sh->coast = NODIRECTION; /* melden, 1x pro Partei */ for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for(u = r->units; u; u = u->next ) { /* das sehen natürlich auch die Leute an Land */ if (!fval(u->faction, FFL_SELECT) ) { fset(u->faction, FFL_SELECT); if (!m) m = msg_message("flying_ship_result", "mage ship", mage, sh); add_message(&u->faction->msgs, m); } } if (m) msg_release(m); 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 | TESTRESISTANCE | * TESTCANSEE) * */ int sp_stealaura(castorder *co) { int taura; unit *u; unit *mage = co->magician.u; 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*(rng_int()%(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 = co->magician.u; 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+rng_int()%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+rng_int()%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\" GEBAEUDE * ZAUBERE \"Runen des Schutzes\" SCHIFF * "kc" */ static int sp_magicrunes(castorder *co) { int duration; unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; variant effect; duration = 3 + rng_int()%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 = co->magician.u; 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\" GEBAEUDE * 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 = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; const char *ts = NULL; obj = pa->param[0]->typ; switch(obj) { case SPP_REGION: ap = &r->attribs; ts = regionname(r, mage->faction); break; case SPP_TEMP: case SPP_UNIT: { unit *u = pa->param[0]->data.u; ap = &u->attribs; ts = unitid(u); break; } case SPP_BUILDING: { building *b = pa->param[0]->data.b; ap = &b->attribs; ts = buildingid(b); break; } case SPP_SHIP: { ship *sh = pa->param[0]->data.sh; ap = &sh->attribs; ts = shipid(sh); break; } default: /* Das Zielobjekt wurde vergessen */ cmistake(mage, co->order, 203, MSG_MAGIC); return 0; } succ = break_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, 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\" GEBAEUDE * ZAUBERE \"Fluch brechen\" SCHIFF * * "kcc" * Flags: * (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) */ int sp_break_curse(castorder *co) { attrib **ap; int obj; curse * c; region *r = co->rt; unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; const 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; ts = regionname(r, mage->faction); break; case SPP_TEMP: case SPP_UNIT: { unit *u = pa->param[0]->data.u; ap = &u->attribs; ts = unitid(u); break; } case SPP_BUILDING: { building *b = pa->param[0]->data.b; ap = &b->attribs; ts = buildingid(b); break; } case SPP_SHIP: { ship *sh = pa->param[0]->data.sh; ap = &sh->attribs; 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_curse_effect", "unit region command id target", mage, mage->region, co->order, pa->param[1]->data.xs, ts)); } else { ADDMSG(&mage->faction->msgs, msg_message( "destroy_curse_noeffect", "unit region command", mage, mage->region, co->order)); } } return cast_level; } /* ------------------------------------------------------------- */ int sp_becomewyrm(castorder *co) { #ifdef KARMA_MODULE unit *u = co->magician.u; int wyrms_already_created = 0; int wyrms_allowed = 0; 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(M_GRAU, SPL_WYRMODEM)); ADDMSG(&u->faction->msgs, msg_message("becomewyrm", "u", u)); return co->level; #else return 0; #endif /* KARMA_MODULE */ } /* ------------------------------------------------------------- */ /* 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 = co->magician.u; 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 - rng_int()%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; } typedef struct spelldata { spellid_t id; const char *sname; const char *info; const char *syntax; const char *parameter; magic_t magietyp; int sptyp; char rank; /* Reihenfolge der Zauber */ int level; /* Stufe des Zaubers */ struct component { const char * name; int amount; int flags; } components[5]; spell_f sp_function; void (*patzer) (castorder*); } spelldata; static spelldata spelldaten[] = { /* M_DRUIDE */ { SPL_BLESSEDHARVEST, "blessedharvest", NULL, NULL, NULL, M_DRUIDE, (FARCASTING | SPELLLEVEL | ONSHIPCAST | REGIONSPELL), 5, 1, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_blessedharvest, NULL }, { SPL_STONEGOLEM, "stonegolem", NULL, NULL, NULL, M_DRUIDE, (SPELLLEVEL), 4, 1, { { "aura", 2, SPC_LEVEL }, { "stone", 1, SPC_LEVEL }, { "p2", 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, { { "aura", 2, SPC_LEVEL }, { "iron", 1, SPC_LEVEL }, { "p2", 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, { { "aura", 4, SPC_LEVEL }, { "log", 1, SPC_LEVEL }, { "p2", 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, { { "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, { { "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, { { "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, { { "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, { { "aura", 1, SPC_LEVEL }, { "stone", 1, SPC_FIX }, { "log", 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, { { "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, { { "aura", 6, SPC_LEVEL }, { "mallorn", 1, SPC_LEVEL }, { "p2", 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|TESTRESISTANCE), 5, 4, { { "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, { { "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, { { "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, { { "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, "aura", "ui", M_DRUIDE, (UNITSPELL|ONSHIPCAST), 1, 6, { { "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, { { "aura", 25, SPC_FIX }, { "laen", 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, { { "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, { { "aura", 50, SPC_FIX }, { "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, { { "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, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_petrify, NULL }, { SPL_STRONG_WALL, "strongwall", NULL, NULL, NULL, M_DRUIDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, { { "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, NULL, "kc?", M_DRUIDE, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 2, 8, { { "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, { { "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, NULL, "ru+", M_DRUIDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 7, 9, { { "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, { { "aura", 80, SPC_FIX }, { "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, { { "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, { { "aura", 100, SPC_FIX }, { "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), 5, 11, { { "aura", 350, SPC_FIX }, { "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, { { "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, { { "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, { { "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", NULL, NULL, NULL, M_DRUIDE, (OCEANCASTABLE | ONSHIPCAST | REGIONSPELL | TESTRESISTANCE), 5, 15, { { "aura", 200, SPC_FIX }, { "seaserpenthead", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_maelstrom, NULL }, { SPL_MALLORN, "magic_roots", NULL, NULL, NULL, M_DRUIDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 16, { { "aura", 250, SPC_FIX }, { "permaura", 10, SPC_FIX }, { "toadslime", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_mallorn, NULL }, { SPL_GREAT_DROUGHT, "great_drought", NULL, NULL, NULL, M_DRUIDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 17, { { "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), 5, 1, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_sparkle, NULL }, { SPL_FIREBALL, "fireball", NULL, NULL, NULL, M_CHAOS, (COMBATSPELL | SPELLLEVEL), 5, 2, { { "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, { { "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, { { "hp", 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, { { "aura", 5, SPC_LEVEL }, { "peasant", 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 | TESTCANSEE | TESTRESISTANCE), 4, 5, { { "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, { { "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, { { "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, "aura", "ui", M_CHAOS, (UNITSPELL|ONSHIPCAST), 1, 7, { { "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, "direction", "c", M_CHAOS, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 4, 7, { { "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, { { "aura", 30, SPC_FIX }, { "peasant", 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, { { "aura", 3, SPC_LEVEL }, { "peasant", 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, { { "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, { { "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, { { "aura", 35, SPC_FIX }, { "dragonblood", 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, { { "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, { { "aura", 50, SPC_FIX }, { "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, NULL, "kc?", M_CHAOS, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 2, 10, { { "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, { { "aura", 10, SPC_LEVEL }, { "peasant", 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, { { "aura", 40, SPC_FIX }, { "hp", 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, { { "aura", 80, SPC_FIX }, { "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, { { "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, { { "aura", 100, SPC_FIX }, { "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, { { "aura", 150, SPC_FIX }, { "peasant", 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 | ONSHIPCAST), 5, 1, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_sparkle, NULL }, { SPL_SHADOWKNIGHTS, "shadowknights", NULL, NULL, NULL, M_TRAUM, (PRECOMBATSPELL | SPELLLEVEL), 4, 1, { { "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, { { "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, { { "aura", 3, SPC_LEVEL }, { "p2", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_puttorest, NULL }, { SPL_ICASTLE, "icastle", NULL, "buildingtype", "c", M_TRAUM, (0), 5, 3, { { "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, "aura", "ui", M_TRAUM, (UNITSPELL|ONSHIPCAST), 1, 3, { { "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, "race", "uc", M_TRAUM, (UNITSPELL|SPELLLEVEL), 5, 3, { { "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 | TESTRESISTANCE), 5, 4, { { "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, { { "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, { { "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 | TESTCANSEE), 5, 5, { { "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, { { "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, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_sleep, NULL }, { SPL_WISPS, "wisps", NULL, "direction", "c", M_TRAUM, (SPELLLEVEL | FARCASTING), 5, 7, { { "aura", 2, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_wisps, NULL }, { SPL_GOODDREAMS, "gooddreams", NULL, NULL, NULL, M_TRAUM, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 8, { { "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, NULL, "kc?", M_TRAUM, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 2, 8, { { "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, { { "aura", 100, SPC_FIX }, { "permaura", 5, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_summon_familiar, NULL }, { SPL_CLONECOPY, "clone", NULL, NULL, NULL, M_TRAUM, (NOTFAMILIARCAST), 5, 9, { { "aura", 100, SPC_FIX }, { "permaura", 20, SPC_FIX }, { "dragonblood", 5, SPC_FIX }, { "p2", 5, SPC_FIX }, { 0, 0, 0 } }, (spell_f)sp_clonecopy, NULL }, { SPL_BADDREAMS, "bad_dreams", NULL, NULL, NULL, M_TRAUM, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 10, { { "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, { { "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, { { "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 | SEARCHGLOBAL | TESTRESISTANCE), 5, 15, { { "aura", 350, SPC_FIX }, { "permaura", 5, SPC_FIX }, { "h8", 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, { { "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, { { "aura", 2, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_denyattack, NULL }, { SPL_HEALINGSONG, "song_of_healing", NULL, NULL, NULL, M_BARDE, (POSTCOMBATSPELL | SPELLLEVEL), 5, 2, { { "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, { { "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, { { "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, { { "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, { { "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, { { "aura", 2, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_chaosrow, NULL }, { SPL_HERO, "heroic_song", NULL, NULL, NULL, M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 5, { { "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, "aura", "ui", M_BARDE, (UNITSPELL|ONSHIPCAST), 1, 5, { { "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 | TESTCANSEE), 5, 5, { { "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, { { "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", NULL, NULL, "u", M_BARDE, (UNITSPELL | ONSHIPCAST | TESTRESISTANCE | TESTCANSEE), 5, 6, { { "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", NULL, NULL, "u", M_BARDE, (UNITSPELL | TESTRESISTANCE | TESTCANSEE), 5, 6, { { "aura", 12, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_seduce, NULL }, { SPL_HEADACHE, "headache", NULL, NULL, "u", M_BARDE, (UNITSPELL | TESTRESISTANCE | TESTCANSEE), 5, 7, { { "aura", 4, SPC_LINEAR }, { "h7", 3, SPC_FIX }, { "money", 50, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_headache, NULL }, { SPL_PUMP, "sound_out", NULL, NULL, "ur", M_BARDE, (UNITSPELL | TESTCANSEE), 5, 7, { { "aura", 4, SPC_FIX }, { "money", 100, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_pump, NULL }, { SPL_BLOODTHIRST, "bloodthirst", NULL, NULL, NULL, M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 4, 7, { { "aura", 5, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_berserk, NULL }, { SPL_FRIGHTEN, "frighten", NULL, NULL, NULL, M_BARDE, (PRECOMBATSPELL | SPELLLEVEL), 5, 8, { { "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", NULL, NULL, "kc?", M_BARDE, (SPELLLEVEL | ONSHIPCAST | REGIONSPELL | BUILDINGSPELL | SHIPSPELL), 5, 8, { { "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", NULL, NULL, "kc?", M_BARDE, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 2, 8, { { "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", NULL, NULL, "u", M_BARDE, (UNITSPELL | SPELLLEVEL | TESTCANSEE), 5, 9, { { "aura", 3, SPC_LEVEL }, { "permaura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_migranten, NULL }, { SPL_CERDDOR_FAMILIAR, "summon_familiar", NULL, NULL, NULL, M_BARDE, (NOTFAMILIARCAST), 5, 9, { { "aura", 100, SPC_FIX }, { "permaura", 5, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_summon_familiar, NULL }, { SPL_RAISEPEASANTS, "raise_mob", NULL, NULL, NULL, M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 10, { { "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", NULL, NULL, NULL, M_BARDE, (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 2, 10, { { "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", NULL, NULL, NULL, M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 11, { { "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", NULL, NULL, NULL, M_BARDE, (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 2, 12, { { "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", NULL, NULL, NULL, M_BARDE, (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE), 5, 12, { { "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", NULL, NULL, "u", M_BARDE, (UNITSPELL | TESTCANSEE), 5, 13, { { "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", NULL, NULL, NULL, M_BARDE, (SPELLLEVEL), 5, 14, { { "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", NULL, NULL, NULL, M_BARDE, (FARCASTING), 5, 15, { { "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", NULL, NULL, NULL, M_BARDE, (FARCASTING | REGIONSPELL | TESTRESISTANCE), 5, 16, { { "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, NULL, "kc?", M_ASTRAL, (SPELLLEVEL | UNITSPELL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 5, 1, { { "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), 5, 1, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_itemcloak, NULL }, { SPL_TYBIED_FUMBLESHIELD, "tybiedfumbleshield", NULL, NULL, NULL, M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 2, { { "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", NULL, NULL, NULL, M_ASTRAL, (SPELLLEVEL), 5, 2, { { "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, { { "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, { { "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, { { "aura", 2, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_enterastral, NULL }, { SPL_LEAVEASTRAL, "leaveastral", NULL, NULL, "ru+", M_ASTRAL, (UNITSPELL |SPELLLEVEL), 7, 4, { { "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", NULL, "aura", "ui", M_ASTRAL, (UNITSPELL|ONSHIPCAST), 1, 5, { { "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, { { "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, { { "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, NULL, "kc?", M_ASTRAL, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 2, 5, { { "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, NULL, "ru+", M_ASTRAL, (UNITSPELL | SEARCHGLOBAL | SPELLLEVEL), 7, 6, { { "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, { { "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 | TESTRESISTANCE | TESTCANSEE), 3, 6, { { "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 | TESTRESISTANCE), 5, 6, { { "aura", 10, SPC_FIX }, { "h12", 1, SPC_FIX }, { "h20", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_flying_ship, NULL }, { SPL_DESTROY_MAGIC, "break_curse", NULL, "spellid", "kcc?", M_ASTRAL, (FARCASTING | SPELLLEVEL | ONSHIPCAST | TESTCANSEE | ANYTARGET), 3, 7, { { "aura", 3, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_break_curse, NULL }, { SPL_ETERNIZEWALL, "eternal_walls", NULL, NULL, "b", M_ASTRAL, (SPELLLEVEL | BUILDINGSPELL | TESTRESISTANCE | ONSHIPCAST), 5, 7, { { "aura", 50, SPC_FIX }, { "permaura", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_eternizewall, NULL }, { SPL_SCHILDRUNEN, "protective_runes", NULL, NULL, "kc", M_ASTRAL, (ONSHIPCAST | TESTRESISTANCE | BUILDINGSPELL | SHIPSPELL), 2, 8, { { "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", NULL, NULL, NULL, M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 8, { { "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", NULL, NULL, NULL, M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 5, 9, { { "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", NULL, NULL, NULL, M_ASTRAL, (0), 5, 10, { { "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", NULL, NULL, "u+", M_ASTRAL, (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE), 5, 11, { { "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", NULL, NULL, NULL, M_ASTRAL, (PRECOMBATSPELL | SPELLLEVEL), 2, 12, { { "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", NULL, NULL, NULL, M_ASTRAL, (NOTFAMILIARCAST), 5, 12, { { "aura", 100, SPC_FIX }, { "permaura", 5, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_summon_familiar, NULL }, { SPL_MOVECASTLE, "living_rock", NULL, "direction", "bc", M_ASTRAL, (SPELLLEVEL | BUILDINGSPELL | TESTRESISTANCE), 5, 13, { { "aura", 10, SPC_LEVEL }, { "permaura", 1, SPC_FIX }, { "laen", 5, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_movecastle, NULL }, { SPL_DISRUPTASTRAL, "astral_disruption", NULL, NULL, NULL, M_ASTRAL, (REGIONSPELL), 4, 14, { { "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", NULL, "aura", "ui", M_ASTRAL, (UNITSPELL), 1, 15, { { "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", NULL, NULL, NULL, M_GRAU, (COMBATSPELL | SPELLLEVEL), 5, 3, { { "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", NULL, NULL, NULL, M_GRAU, 0, 5, 1, { { "aura", 1, SPC_FIX }, { "permaura", 1, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_becomewyrm, NULL }, /* Monstersprüche */ { SPL_FIREDRAGONODEM, "fiery_dragonbreath", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 3, { { "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", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 6, { { "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", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 12, { { "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", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 12, { { "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", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 12, { { "aura", 1, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_flee, NULL }, { SPL_SHADOWCALL, "shadowcall", NULL, NULL, NULL, M_GRAU, (PRECOMBATSPELL), 5, 12, { { "aura", 2, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_shadowcall, NULL }, { SPL_IMMOLATION, "immolation", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 12, { { "aura", 2, SPC_LEVEL }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_immolation, NULL }, { SPL_FIREODEM, "firestorm", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 8, { { "aura", 2, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_immolation, NULL }, { SPL_ICEODEM, "coldfront", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 8, { { "aura", 2, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_immolation, NULL }, { SPL_ACIDODEM, "acidrain", NULL, NULL, NULL, M_GRAU, (COMBATSPELL), 5, 8, { { "aura", 2, SPC_FIX }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }, (spell_f)sp_immolation, NULL }, /* 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) { spelldata * data = spelldaten+i; spell * sp = malloc(sizeof(spell)); int n; sp->id = data->id; sp->sname = strdup(data->sname); if (data->info) { locale_setstring(default_locale, mkname("spellinfo", data->sname), data->info); } sp->parameter = data->parameter?strdup(data->parameter):0; sp->syntax = data->syntax?strdup(data->syntax):0; sp->magietyp = data->magietyp; sp->sptyp = data->sptyp; sp->rank = data->rank; sp->level = data->level; for (n=0;n!=5 && data->components[n].name;++n); sp->components = malloc(sizeof(spell_component) *(n+1)); sp->components[n].type = NULL; while (n-->0) { sp->components[n].type = rt_find(data->components[n].name); sp->components[n].amount = data->components[n].amount; sp->components[n].cost = data->components[n].flags; } sp->sp_function = data->sp_function; sp->patzer = data->patzer; register_spell(sp); } at_register(&at_cursewall); at_register(&at_unitdissolve); at_register(&at_wdwpyramid); register_bordertype(&bt_firewall); register_bordertype(&bt_wisps); register_bordertype(&bt_chaosgate); } void register_spells(void) { /* sp_summon_alp */ register_alp(); /* init_firewall(); */ ct_register(&ct_firewall); ct_register(&ct_deathcloud); at_register(&at_deathcloud_compat); register_unitcurse(); register_regioncurse(); register_shipcurse(); register_buildingcurse(); register_function((pf_generic)&sp_wdwpyramid, "wdwpyramid"); }