/* * * * Eressea PB(E)M host Copyright (C) 1998-2015 * 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 "spy.h" #include "vortex.h" #include "laws.h" #include "spells.h" #include "direction.h" #include "randenc.h" #include "monster.h" #include "teleport.h" #include #include #include #include #include #include #include /* kernel includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* util includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* libc includes */ #include #include #include #include #include #include #include /* triggers includes */ #include #include #include #include #include #include #include /* attributes includes */ #include #include /* ----------------------------------------------------------------------- */ #if defined(_MSC_VER) && _MSC_VER >= 1900 # pragma warning(disable: 4774) // TODO: remove this #endif static double zero_effect = 0.0; 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 schlaegt 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; bool 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 schwaecher 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 geglueckt */ 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; bool 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 schwaecher 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 geglueckt */ 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; bool 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 schwaecher 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 geglueckt */ 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; bool 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 schwaecher 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 geglueckt */ 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)); } } static 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; /* Immunitaet pruefen */ 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 pruefe, 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; } int report_action(region * r, unit * actor, message * msg, int flags) { int result = 0; unit *u; int view = flags & (ACTION_CANSEE | ACTION_CANNOTSEE); /* melden, 1x pro Partei */ if (flags & ACTION_RESET) { freset(actor->faction, FFL_SELECT); for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); } if (view) { for (u = r->units; u; u = u->next) { if (!fval(u->faction, FFL_SELECT)) { bool show = u->faction == actor->faction; fset(u->faction, FFL_SELECT); if (view == ACTION_CANSEE) { /* Bei Fernzaubern sieht nur die eigene Partei den Magier */ show = show || (r == actor->region && cansee(u->faction, r, actor, 0)); } else if (view == ACTION_CANNOTSEE) { show = !show && !(r == actor->region && cansee(u->faction, r, actor, 0)); } else { /* the unliely (or lazy) case */ show = true; } if (show) { r_addmessage(r, u->faction, msg); } else { /* Partei des Magiers, sieht diesen immer */ result = 1; } } } /* Ist niemand von der Partei des Magiers in der Region, dem Magier * nochmal gesondert melden */ if ((flags & ACTION_CANSEE) && !fval(actor->faction, FFL_SELECT)) { add_message(&actor->faction->msgs, msg); } } return result; } /* ------------------------------------------------------------- */ /* Report a spell's effect to the units in the region. */ static void report_effect(region * r, unit * mage, message * seen, message * unseen) { int err = report_action(r, mage, seen, ACTION_RESET | ACTION_CANSEE); if (err) { report_action(r, mage, seen, ACTION_CANNOTSEE); } } /* ------------------------------------------------------------- */ /* 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 ueber 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 moeglich. * 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 beschwoert einen Vertrauten, ein kleines Tier, welches * dem Magier zu Diensten ist. Der Magier kann durch die Augen des * Vertrauten sehen, und durch den Vertrauten zaubern, allerdings nur * mit seiner halben Stufe. Je nach Vertrautem erhaelt der Magier * evtl diverse Skillmodifikationen. Der Typ des Vertrauten ist * zufaellig bestimmt, wird aber durch Magiegebiet und Rasse beeinflußt. * "Tierische" Vertraute brauchen keinen Unterhalt. * * Ein paar Moeglichkeiten: * Magieg. Rasse Besonderheiten * Eule Tybied -/- fliegt, Auraregeneration * Rabe Ilaun -/- fliegt * Imp Draig -/- Magieresistenz? * Fuchs Gwyrrd -/- Wahrnehmung * ???? Cerddor -/- ???? (Singvogel?, Papagei?) * Adler -/- -/- fliegt, +Wahrnehmung, =^=Adlerauge-Spruch? * Kraehe -/- -/- fliegt, +Tarnung (weil unauffaellig) * Delphin -/- Meerm. schwimmt * Wolf -/- Ork * Hund -/- Mensch kann evtl BEWACHE ausfuehren * Ratte -/- Goblin * Albatros -/- -/- fliegt, kann auf Ozean "landen" * Affe -/- -/- kann evtl BEKLAUE ausfuehren * Goblin -/- !Goblin normale Einheit * Katze -/- !Katze normale Einheit * Daemon -/- !Daemon normale Einheit * * Spezielle V. fuer Katzen, Trolle, Elfen, Daemonen, Insekten, Zwerge? */ static const race *select_familiar(const race * magerace, magic_t magiegebiet) { const race *retval = 0; int rnd = rng_int() % 100; assert(magerace->familiars[0]); if (rnd >= 70) { retval = magerace->familiars[magiegebiet]; assert(retval); } else { retval = magerace->familiars[0]; } if (!retval || rnd < 3) { race_list *familiarraces = get_familiarraces(); unsigned int maxlen = listlen(familiarraces); if (maxlen > 0) { race_list *rclist = familiarraces; unsigned int index = rng_uint() % maxlen; while (index-- > 0) { rclist = rclist->next; } retval = rclist->data; } } if (!retval) { retval = magerace->familiars[0]; } if (!retval) { log_error("select_familiar: No familiar (not even a default) defined for %s.\n", magerace->_name); } return retval; } /* ------------------------------------------------------------- */ /* der Vertraue des Magiers */ static void make_familiar(unit * familiar, unit * mage) { /* skills and spells: */ if (u_race(familiar)->init_familiar != NULL) { u_race(familiar)->init_familiar(familiar); } else { log_error("could not perform initialization for familiar %s.\n", familiar->faction->race->_name); } /* 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; const race *rc; int sk; int dh, dh1; size_t bytes; message *msg; char zText[2048], *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->_race, mage->faction->magiegebiet); if (rc == NULL) { log_error("could not find suitable familiar for %s.\n", mage->faction->race->_name); return 0; } if (fval(rc, RCF_SWIM) && !fval(rc, RCF_WALK)) { int coasts = is_coastregion(r); int dir; 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 (dir = 0; dir != MAXDIRECTIONS; ++dir) { region *rn = rconnect(r, dir); if (rn && fval(rn->terrain, SEA_REGION)) { dh++; if (dh == coasts) { r = rconnect(r, dir); break; } } } } msg = msg_message("familiar_name", "unit", mage); nr_render(msg, mage->faction->locale, zText, sizeof(zText), mage->faction); msg_release(msg); familiar = create_unit(r, 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; sk < MAXSKILLS; ++sk) { if (skill_enabled(sk) && rc->bonus[sk] > -5) dh++; } for (sk = 0; sk < MAXSKILLS; sk++) { if (skill_enabled(sk) && rc->bonus[sk] > -5) { dh--; if (dh1 == 0) { dh1 = 1; } else { if (dh == 0) { bytes = strlcpy(bufp, (const char *)LOC(mage->faction->locale, "list_and"), size); } else { bytes = strlcpy(bufp, (const char *)", ", size); } assert(bytes <= INT_MAX); if (wrptr(&bufp, &size, (int)bytes) != 0) WARN_STATIC_BUFFER(); } bytes = strlcpy(bufp, (const char *)skillname((skill_t)sk, mage->faction->locale), size); assert(bytes <= INT_MAX); if (wrptr(&bufp, &size, (int)bytes) != 0) WARN_STATIC_BUFFER(); } } ADDMSG(&mage->faction->msgs, msg_message("familiar_describe", "mage race skills", mage, rc, zText)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Zerstoere Magie * Wirkung: * Zerstoert 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 schlaegt die Aufloesung mit einer von der Differenz abhaengigen * Chance fehl. Auch dann wird force verbraucht, der Zauber jedoch nur * abgeschwaecht. * * 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 verstaerkt */ 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_get_region(co); 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 Verhaeltnis * 2:1 auf einen anderen Magier des gleichen Magiegebietes oder (nur * bei Tybied) im Verhaeltnis 3:1 auf einen Magier eines anderen * Magiegebietes uebertragen. * * 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 uebertragen." */ cmistake(mage, co->order, 207, MSG_MAGIC); return 0; } else if (scm_src->magietyp == M_TYBIED) { if (scm_src->magietyp != scm_dst->magietyp) multi = 3; } else if (scm_src->magietyp == M_GRAY) { if (scm_src->magietyp != scm_dst->magietyp) multi = 4; } else if (scm_dst->magietyp != scm_src->magietyp) { /* "Zu dieser Einheit kann ich keine Aura uebertragen." */ 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: Guenstige Winde * Stufe: 4 * Gebiet: Gwyrrd * Wirkung: * Schiffsbewegung +1, kein Abtreiben. Haelt (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_get_region(co); 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: * fuer Stufe Runden wird eine (magische) Strasse erzeugt, die wie eine * normale Strasse wirkt. * Im Ozean schlaegt der Spruch fehl * * Flags: * (FARCASTING|SPELLLEVEL|REGIONSPELL|ONSHIPCAST|TESTRESISTANCE) */ static int sp_magicstreet(castorder * co) { region *r = co_get_region(co); 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: Beschwoerung, positiv * Gebiet: Gwyrrd * Wirkung: * Verwandelt (Stufe) Baeume in eine Gruppe von Ents, die sich fuer Stufe * Runden der Partei des Druiden anschliessen und danach wieder zu * Baeumen werden * Patzer: * Monster-Ents entstehen * * Flags: * (SPELLLEVEL) */ static int sp_summonent(castorder * co) { region *r = co_get_region(co); 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 baeume */ return 0; } ents = _min((int)(power * power), rtrees(r, 2)); u = create_unit(r, mage->faction, ents, get_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 Gebaeude eingefuehrt: 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_get_region(co); 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 (!building_finished(b)) { 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 fuer 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; curse *c; int duration = (int)co->force + 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 verstaerkt * (Max(Dauer), Max(Staerke))*/ c = create_curse(mage, &r->attribs, ct_find("maelstrom"), co->force, duration, co->force, 0); /* melden, 1x pro Partei */ if (c) { 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_get_region(co); int cast_level = co->level; unit *mage = co->magician.u; if (!fval(r->terrain, LAND_REGION)) { cmistake(mage, co->order, 186, MSG_MAGIC); return 0; } if (fval(r, RF_MALLORN)) { cmistake(mage, co->order, 191, 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 / Regentanz * 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; int duration = (int)co->force + 1; /* Attribut auf Region. * Existiert schon ein curse, so wird dieser verstaerkt * (Max(Dauer), Max(Staerke))*/ if (create_curse(mage, &r->attribs, ct_find("blessedharvest"), co->force, duration, 1.0, 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 Jungbaeume * * Flag: * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) */ static int sp_hain(castorder * co) { int trees; region *r = co_get_region(co); 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 Jungbaeume * * Flag: * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) */ static int sp_mallornhain(castorder * co) { int trees; region *r = co_get_region(co); 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; } static void fumble_ents(const castorder * co) { int ents; unit *u; region *r = co_get_region(co); 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, get_monsters(), ents, get_race(RC_TREEMAN), 0, NULL, NULL); if (u) { message *unseen; /* 'Erfolg' melden */ ADDMSG(&mage->faction->msgs, msg_message("regionmagic_patzer", "unit region command", mage, mage->region, co->order)); /* melden, 1x pro Partei */ 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: * Zerstoert 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_get_region(co); 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 } if (force > 0) { 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) { float chance = (float)_min((*ip)->number, force); if (iweapon->chance < 1.0) { chance *= iweapon->chance; } if (chance > 0) { int ichange = (int)chance; force -= ichange; ironweapon += ichange; i_change(ip, iweapon->type, -ichange); if (iweapon->rusty) { i_change(&u->items, iweapon->rusty, ichange); } } } 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 zerstoerten Waffen. Im * unguenstigsten Fall kann pro Stufe nur eine Waffe verzaubert werden, * darum wird hier nur fuer alle Faelle in denen noch weniger Waffen * betroffen wurden ein Kostennachlass gegeben */ return _min(success, cast_level); } /* ------------------------------------------------------------- */ /* Name: Kaelteschutz * Stufe: 3 * Kategorie: Einheit, positiv * Gebiet: Gwyrrd * * Wirkung: * schuetzt ein bis mehrere Einheiten mit bis zu Stufe*10 Insekten vor * den Auswirkungen der Kaelte. Sie koennen Gletscher betreten und dort * ganz normal alles machen. Die Wirkung haelt Stufe Wochen an * Insekten haben in Gletschern den selben Malus wie in Bergen. Zu * lange drin, nicht mehr aendern * * Flag: * (UNITSPELL | SPELLLEVEL | ONSHIPCAST | TESTCANSEE) */ /* Syntax: ZAUBER [STUFE n] "Kaelteschutz" eh1 [eh2 [eh3 [...]]] */ static int sp_kaelteschutz(castorder * co) { unit *u; int n, i = 0; int men; region *r = co_get_region(co); 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; double effect; force *= 10; /* 10 Personen pro Force-Punkt */ /* fuer 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 = 1; create_curse(mage, &u->attribs, ct_find("insectfur"), (float)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: Verwuenschung, 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; double 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 = (float)(rng_int() % 0xffffff); create_curse(mage, &u->attribs, ct_find("sparkle"), (float)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: Beschwoerung, 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/Bihaender' oder 'mache * Schild/Kettenhemd/Plattenpanzer', so werden pro Golem 5 Eisenbarren * verbaut und der Golem loest sich auf. * * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufaellig * treffen, so ist der Schaden fast immer toedlich. (Eisengolem: HP * 50, AT 4, PA 2, Ruestung 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_get_region(co); 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, rc_find("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_s(rc_find("irongolem"), (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Steingolem * Stufe: 1 * Kategorie: Beschwoerung, 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 loest sich auf. * * Golems sind zu langsam um wirklich im Kampf von Nutzen zu sein. * Jedoch fangen sie eine Menge Schaden auf und sollten sie zufaellig * treffen, so ist der Schaden fast immer toedlich. (Steingolem: HP 60, * AT 4, PA 2, Ruestung 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_get_region(co); 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, rc_find("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_s(rc_find("stonegolem"), (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL)))); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Große Duerre * Stufe: 17 * Kategorie: Region, negativ * Gebiet: Gwyrrd * * Wirkung: * 50% alle Bauern, Pferde, Baeume sterben. * Zu 25% terraform: Gletscher wird mit 50% zu Sumpf, sonst Ozean, * Sumpf wird zu Steppe, Ebene zur Steppe, Steppe zur Wueste. * Besonderheiten: * neuer Terraintyp Steppe: * 5000 Felder, 500 Baeume, Strasse: 250 Steine. Anlegen wie in Ebene * moeglich * * 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) { unit *u; bool terraform = false; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = 2; double 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 = 4.0; /* curses: higher is stronger */ 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 duerfen hier auf keinen Fall geloescht werden! */ for (u = r->units; u; u = u->next) { if (u_race(u) != get_race(RC_SPELL) && u->ship == 0) { set_number(u, 0); } } while (r->buildings) { remove_building(&r->buildings, r->buildings); } } 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 Baeume' * 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 Waeldern. * * Syntax: Zauber "Weg der Baeume" ... * * Flags: * (UNITSPELL | SPELLLEVEL | TESTCANSEE) */ static int sp_treewalkenter(castorder * co) { region *r = co_get_region(co); 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) || fval(rt->terrain, FORBIDDEN_REGION)) { 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_feedback(mage, co->order, "fail_tooheavy", "target", 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_get_region(co); unit *mage = co->magician.u; double power = co->force; spellparameter *pa = co->par; int cast_level = co->level; if (!is_astral(r)) { 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 loeschen fuer Überpruefung auf * Gueltigkeit */ 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; } /* fuer 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; 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"); } 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 * Gebaeudezerstoerenden Sprueche kaputt gemacht werden. Auch * schuetzt der Zauber vor Belagerungskatapulten. * * ZAUBER Heimstein * Flags: (0) */ static int sp_homestone(castorder * co) { unit *u; curse *c; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; double 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 erhoeht sich um 50% */ effect = 50.0F; 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: Duerre * Stufe: 13 * Kategorie: Region, negativ * Gebiet: Gwyrrd * Wirkung: * temporaer veraendert sich das Baummaximum und die maximalen Felder in * einer Region auf die Haelfte des normalen. * Die Haelfte der Baeume 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_get_region(co); 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 { double effect = 4.0; /* 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); create_curse(mage, &r->attribs, ct_find("drought"), power, duration, effect, 0); } return cast_level; } /* ------------------------------------------------------------- */ /* Name: Bergwaechter * Stufe: 9 * Gebiet: Gwyrrd * Kategorie: Beschwoerung, negativ * * Wirkung: * Erschafft in Bergen oder Gletschern einen Waechter, der durch bewachen * den Eisen/Laen-Abbau fuer nicht-Allierte verhindert. Bergwaechter * verhindern auch Abbau durch getarnte/unsichtbare Einheiten und lassen * sich auch durch Belagerungen nicht aufhalten. * * (Ansonsten in economic.c:manufacture() entsprechend anpassen). * * Faehigkeiten (factypes.c): 50% Magieresistenz, 25 HP, 4d4 Schaden, * 4 Ruestung (=PP) * Flags: * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) */ static int sp_ironkeeper(castorder * co) { unit *keeper; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; message *msg; if (r->terrain != newterrain(T_MOUNTAIN) && r->terrain != newterrain(T_GLACIER)) { report_failure(mage, co->order); return 0; } keeper = create_unit(r, mage->faction, 1, get_race(RC_IRONKEEPER), 0, NULL, mage); /*keeper->age = cast_level + 2; */ setstatus(keeper, ST_AVOID); /* kaempft nicht */ guard(keeper, GUARD_MINING); fset(keeper, UFL_ISNEW); /* Parteitarnen, damit man nicht sofort weiß, wer dahinter steckt */ if (rule_stealth_anon()) { fset(keeper, UFL_ANON_FACTION); } { trigger *tkill = trigger_killunit(keeper); add_trigger(&keeper->attribs, "timer", trigger_timeout(cast_level + 2, tkill)); } msg = msg_message("summon_effect", "mage amount race", mage, 1, u_race(keeper)); r_addmessage(r, NULL, msg); msg_release(msg); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Sturmwind - Beschwoere einen Sturmelementar * Stufe: 6 * Gebiet: Gwyrrd * * Wirkung: * Verdoppelt Geschwindigkeit aller angegebener Schiffe fuer diese * Runde. Kombinierbar mit "Guenstige 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 spaeter problemlos zu veraendern. * * Flags: * (SHIPSPELL|ONSHIPCAST|OCEANCASTABLE|TESTRESISTANCE) */ static int sp_stormwinds(castorder * co) { ship *sh; unit *u; int erfolg = 0; region *r = co_get_region(co); 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); } } } if (erfolg < pa->length) { ADDMSG(&mage->faction->msgs, msg_message("stormwinds_reduced", "unit ships maxships", mage, erfolg, pa->length)); } /* melden, 1x pro Partei auf Schiff und fuer 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: * Zerstoert Stufe*10 "Steineinheiten" aller Gebaeude der Region, aber nie * mehr als 25% des gesamten Gebaeudes (aber natuerlich mindestens ein * Stein). * * Flags: * (FARCASTING|REGIONSPELL|TESTRESISTANCE) */ static int sp_earthquake(castorder * co) { int kaputt; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; message *msg; building **blist = &r->buildings; while (*blist) { building *burg = *blist; if (burg->size != 0 && !is_cursed(burg->attribs, C_MAGICWALLS, 0)) { /* Magieresistenz */ if (!target_resists_magic(mage, burg, TYP_BUILDING, 0)) { kaputt = _min(10 * cast_level, burg->size / 4); kaputt = _max(kaputt, 1); burg->size -= kaputt; if (burg->size == 0) { /* TODO: sollten die Insassen nicht Schaden nehmen? */ remove_building(blist, burg); } } } if (*blist == burg) blist = &burg->next; } /* 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_DRAIG / Draig */ /* ------------------------------------------------------------- */ void patzer_peasantmob(const castorder * co) { int anteil = 6, n; unit *u; attrib *a; region *r; unit *mage = co->magician.u; r = mage->region->land ? mage->region : co_get_region(co); if (r->land) { faction *f = get_monsters(); 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, get_race(RC_PEASANT), 0, LOC(f->locale, "angry_mob"), NULL); fset(u, UFL_ISNEW); /* guard(u, GUARD_ALL); hier zu frueh! 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. Fuer 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_get_region(co); 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 schwaecht 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; double 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, 0); duration = _max(sx, rx) + 1; effect = 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(const castorder * co) { unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (cast_level / 2) + 1; double effect; curse *c; effect = 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, Beschwoerung, negativ * * Wirkung: * In einer Wueste, Sumpf oder Gletscher gezaubert kann innerhalb der * naechsten 6 Runden ein bis 6 Dracheneinheiten bis Groeß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_get_region(co); 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 = get_monsters(); if (r->terrain != newterrain(T_SWAMP) && r->terrain != newterrain(T_DESERT) && r->terrain != newterrain(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 = get_race(RC_WYRM); number = 1; break; case 1: race = get_race(RC_DRAGON); number = 2; break; case 2: default: race = get_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(u) == get_race(RC_WYRM) || u_race(u) == get_race(RC_DRAGON)) { attrib *a = a_find(u->attribs, &at_targetregion); if (!a) { a = a_add(&u->attribs, make_targetregion(r)); } else { a->data.v = r; } } } } ADDMSG(&mage->faction->msgs, msg_message("summondragon", "unit region command target", mage, mage->region, co->order, r)); free_regionlist(rl); return cast_level; } static int sp_firewall(castorder * co) { connection *b; wall_data *fd; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; spellparameter *pa = co->par; direction_t dir; region *r2; dir = get_direction(pa->param[0]->data.xs, mage->faction->locale); if (dir < MAXDIRECTIONS && dir != NODIRECTION) { r2 = rconnect(r, dir); } else { report_failure(mage, co->order); 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; fd->countdown = cast_level + 1; } else { fd = (wall_data *)b->data.v; fd->force = (int)_max(fd->force, force / 2 + 0.5); fd->countdown = _max(fd->countdown, 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; } /* ------------------------------------------------------------- */ /* Name: Unheilige Kraft * Stufe: 10 * Gebiet: Draig * Kategorie: Untote Einheit, positiv * * Wirkung: * transformiert (Stufe)W10 Untote in ihre staerkere Form * * * Flag: * (SPELLLEVEL | TESTCANSEE) */ static int sp_unholypower(castorder * co) { region * r = co_get_region(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(u))) { case RC_SKELETON: target_race = get_race(RC_SKELETON_LORD); break; case RC_ZOMBIE: target_race = get_race(RC_ZOMBIE_LORD); break; case RC_GHOUL: target_race = get_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->irace = NULL; u_setrace(u, target_race); u->hp = unit_max_hp(u) * u->number - wounds; ADDMSG(&r->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) * waere 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(r, u->faction, 0, target_race, 0, NULL, u); transfermen(u, un, n); un->hp = unit_max_hp(un) * n - wounds; ADDMSG(&r->msgs, msg_message("unholypower_limitedeffect", "mage target race amount", mage, u, target_race, n)); n = 0; } } return cast_level; } static int change_hitpoints(unit * u, int value) { int hp = u->hp; hp += value; /* Jede Person benötigt mindestens 1 HP */ if (hp < u->number) { if (hp < 0) { /* Einheit tot */ hp = 0; } scale_number(u, hp); } u->hp = hp; return hp; } 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 AT_AGE_REMOVE; } up = &r->units; if (curse_active(c)) while (*up != NULL) { unit *u = *up; int hp; double damage = c->effect * u->number; 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)); hp = change_hitpoints(u, -(int)damage); ADDMSG(&u->faction->msgs, msg_message((hp>0)?"poison_damage":"poison_death", "region unit", r, u)); if (*up == u) up = &u->next; } return AT_AGE_KEEP; } 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) { double effect; curse *c; effect = 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, void *target, gamedata *data) /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ { struct storage *store = data->store; region *r = NULL; unit *u; variant var; int duration; float strength; int rx, ry; READ_INT(store, &duration); READ_FLT(store, &strength); READ_INT(store, &var.i); u = findunit(var.i); /* this only affects really old data. no need to change: */ READ_INT(store, &rx); READ_INT(store, &ry); r = findregion(rx, ry); if (r != NULL) { double effect; curse *c; effect = strength; c = create_curse(u, &r->attribs, &ct_deathcloud, strength * 2, duration, effect, 0); c->data.v = r; if (u == NULL) { ur_add(var, &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 /* ------------------------------------------------------------- */ /* 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 * Ruestung wirkt nicht * Patzer: * Magier geraet in den Staub und verliert zufaellige 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 * aendern... * Missbrauchsmoeglichkeit: * Hat der Magier mehr HP als Rasse des Feindes (extrem: Daemon/Goblin) * so kann er per Farcasting durch mehrmaliges Zaubern eine * Nachbarregion ausloeschen. Darum sollte dieser Spruch nur einmal auf * eine Region gelegt werden koennen. * * Flag: * (FARCASTING | REGIONSPELL | TESTRESISTANCE) */ static int sp_deathcloud(castorder * co) { region *r = co_get_region(co); 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) freset(u->faction, FFL_SELECT); 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; plagues(r); ADDMSG(&mage->faction->msgs, msg_message("plague_spell", "region mage", r, mage)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Beschwoere Schattendaemon * Stufe: 8 * Gebiet: Draig * Kategorie: Beschwoerung, positiv * Wirkung: * Der Magier beschwoert Stufe^2 Schattendaemonen. * Schattendaemonen 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 * Ruestungsschutz 3. * Flag: * (SPELLLEVEL) */ static int sp_summonshadow(castorder * co) { region *r = co_get_region(co); 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, get_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_PERCEPTION, 1); ADDMSG(&mage->faction->msgs, msg_message("summonshadow_effect", "mage number", mage, number)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Beschwoere Schattenmeister * Stufe: 12 * Gebiet: Draig * Kategorie: Beschwoerung, positiv * Wirkung: * Diese hoeheren Schattendaemonen sind erheblich gefaehrlicher als die * einfachen Schattendaemonen. Sie haben Tarnung entsprechend dem * Magietalent des Beschwoerer-1 und Wahrnehmung 5, 75 HP, * Ruestungsschutz 4, Attacke-Bonus 11 und Verteidigungsbonus 13, machen * bei einem Treffer 2d4 Schaden, entziehen einen Staerkepunkt und * entziehen 5 Talenttage in einem zufaelligen Talent. * Stufe^2 Daemonen. * * Flag: * (SPELLLEVEL) * */ static int sp_summonshadowlords(castorder * co) { unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int amount = (int)(force * force); u = create_unit(r, mage->faction, amount, get_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_PERCEPTION, 5); ADDMSG(&mage->faction->msgs, msg_message("summon_effect", "mage amount race", mage, amount, u_race(u))); return cast_level; } static bool chaosgate_valid(const connection * b) { const attrib *a = a_find(b->from->attribs, &at_direction); if (!a) a = a_find(b->to->attribs, &at_direction); if (!a) return false; return true; } static struct region *chaosgate_move(const connection * b, struct unit *u, struct region *from, struct region *to, bool routing) { if (!routing) { int maxhp = u->hp / 4; if (maxhp < u->number) 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 }; /* ------------------------------------------------------------- */ /* Name: Chaossog * Stufe: 14 * Gebiet: Draig * Kategorie: Teleport * Wirkung: * Durch das Opfern von 200 Bauern kann der Chaosmagier ein Tor zur * astralen Welt oeffnen. Das Tor kann im Folgemonat verwendet werden, * es loest sich am Ende des Folgemonats auf. * * Flag: (0) */ static int sp_chaossuction(castorder * co) { region *rt; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; if (rplane(r)) { /* 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 || fval(rt->terrain, FORBIDDEN_REGION)) { /* 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; } /* TODO: implement with a building */ create_special_direction(r, rt, 2, "vortex_desc", "vortex", false); create_special_direction(rt, r, 2, "vortex_desc", "vortex", false); 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: * Erhoeht die maximalen Magiepunkte und die monatliche Regeneration auf * das doppelte. Dauer: 4 Wochen Danach sinkt beides auf die Haelfte des * normalen ab. * Dauer: 6 Wochen * Patzer: * permanenter Stufen- (Talenttage), Regenerations- oder maxMP-Verlust * Besonderheiten: * Patzer koennen waehrend der Zauberdauer haeufiger 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; double 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)) { report_failure(mage, co->order); return 0; } effect = 6; c = create_curse(mage, &mage->attribs, ct_magicboost, power, 10, effect, 1); /* one aura boost with 200% aura now: */ effect = 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", c->magician, 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 = effskill(mage, SK_MAGIC, 0); 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(u); skill_t sk; for (sk = 0; sk != MAXSKILLS; ++sk) { if (rc->bonus[sk] > 0) { set_level(u, sk, level); } } if (rc->bonus[SK_STAMINA]) { u->hp = unit_max_hp(u) * u->number; } } } /* ------------------------------------------------------------- */ /* Name: Totenruf - Maechte des Todes * Stufe: 6 * Gebiet: Draig * Kategorie: Beschwoerung, positiv * Flag: FARCASTING * Wirkung: * Untote aus deathcounther ziehen, bis Stufe*10 Stueck * * Patzer: * Erzeugt Monsteruntote */ static int sp_summonundead(castorder * co) { int undead; unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; int force = (int)(co->force * 10); const race *race = get_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 = get_race(RC_SKELETON); } else if (cast_level <= 12) { race = get_race(RC_ZOMBIE); } else { race = get_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_get_region(co); 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 erhoehter MR, wuerde Spruch sinnlos * machen */ lost_aura = (int)(get_spellpoints(u) * lost); change_spellpoints(u, -lost_aura); } } 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 - Gebaeude, 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 Spruchstaerke 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_get_region(co); 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 Spruchstaerke 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; } static bool can_charm(const unit * u, int maxlevel) { const skill_t expskills[] = { SK_ALCHEMY, SK_HERBALISM, SK_MAGIC, SK_SPY, SK_TACTICS, NOSKILL }; skill *sv = u->skills; if (fval(u, UFL_HERO)) return false; for (; sv != u->skills + u->skill_size; ++sv) { int l = 0, h = 5; skill_t sk = sv->id; assert(expskills[h] == NOSKILL); while (l < h) { int m = (l + h) / 2; if (sk == expskills[m]) { if (skill_limit(u->faction, sk) != INT_MAX) { return false; } else if ((int)sv->level > maxlevel) { return false; } break; } else if (sk > expskills[m]) l = m + 1; else h = m; } } return true; } /* ------------------------------------------------------------- */ /* Name: Charming * Stufe: 13 * Gebiet: Cerddor * Flag: UNITSPELL * Wirkung: * bezauberte Einheit wechselt 'virtuell' die Partei und fuehrt fremde * Befehle aus. * Dauer: 3 - force+2 Wochen * Wirkt gegen Magieresistenz * * wirkt auf eine Einheit mit maximal Talent Personen normal. Fuer jede * zusaetzliche Person gibt es einen Bonus auf Magieresistenz, also auf * nichtgelingen, von 10%. * * Das hoechste Talent der Einheit darf maximal so hoch sein wie das * Magietalent des Magiers. Fuer jeden Talentpunkt mehr gibt es einen * Bonus auf Magieresistenz von 15%, was dazu fuehrt, das bei +2 Stufen * die Magiersistenz bei 90% liegt. * * Migrantenzaehlung muss Einheit ueberspringen * * 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); } /* niemand mit teurem Talent */ if (!can_charm(target, cast_level / 2)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_noexpensives", "target", target)); return 0; } /* Magieresistensbonus fuer mehr als Stufe Personen */ if (target->number > force) { resist_bonus += (int)((target->number - force) * 10); } /* Magieresistensbonus fuer hoehere Talentwerte */ for (i = 0; i < MAXSKILLS; i++) { int sk = effskill(target, i, 0); if (tb < sk) tb = sk; } tb -= effskill(mage, SK_MAGIC, 0); if (tb > 0) { resist_bonus += tb * 15; } /* Magieresistenz */ if (target_resists_magic(mage, target, TYP_UNIT, resist_bonus)) { report_failure(mage, co->order); return 0; } duration = 3 + rng_int() % (int)force; { trigger *trestore = trigger_changefaction(target, target->faction); /* laeuft die Dauer ab, setze Partei zurueck */ add_trigger(&target->attribs, "timer", trigger_timeout(duration, trestore)); /* wird die alte Partei von Target aufgeloest, dann auch diese Einheit */ add_trigger(&target->faction->attribs, "destroy", trigger_killunit(target)); /* wird die neue Partei von Target aufgeloest, dann auch diese Einheit */ add_trigger(&mage->faction->attribs, "destroy", trigger_killunit(target)); } /* sperre ATTACKIERE, GIB PERSON und ueberspringe Migranten */ create_curse(mage, &target->attribs, ct_find("slavery"), force, duration, zero_effect, 0); /* setze Partei um und loesche langen Befehl aus Sicherheitsgruenden */ u_setfaction(target, mage->faction); set_order(&target->thisorder, NULL); /* setze Parteitarnung, damit nicht sofort klar ist, wer dahinter * steckt */ if (rule_stealth_anon()) { fset(target, UFL_ANON_FACTION); } 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) { region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force + 1; create_curse(mage, &r->attribs, ct_find("goodmagicresistancezone"), force, duration, 15, 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) { region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force + 1; create_curse(mage, &r->attribs, ct_find("badmagicresistancezone"), force, duration, 15, 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_get_region(co); 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 (is_monsters(u->faction) && u_race(u) == get_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 gehoert und die Region bewacht. * Regionssilber sollte auch nicht durch Unterhaltung gewonnen werden * koennen. * * Fehlt: Triggeraktion: loeste Bauernmob auf und gib alles an Region, * dann koennen die Bauernmobs ihr Silber mitnehmen und bleiben x * Wochen bestehen * * alternativ: Loesen sich langsam wieder auf * Flag: * (FARCASTING | REGIONSPELL | TESTRESISTANCE) */ static int sp_raisepeasantmob(castorder * co) { unit *u; attrib *a; int n; int anteil; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force + 1; faction *monsters = get_monsters(); message *msg; anteil = 6 + (rng_int() % 4); n = rpeasants(r) * anteil / 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, get_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"), (float)cast_level, duration, (float)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 koennen 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_get_region(co); 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 koennen problemlos normal uebergeben werden */ if (u_race(target) == mage->faction->race) { /* u ist von unserer Art, das Ritual waere 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(u_race(target))) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_nomonsters", "")); return 0; } /* niemand mit teurem Talent */ if (has_limited_skills(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 pruefen (aus alter Teleportroutine uebernommen) */ 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 fuer lovar(Stufe/2) Runden */ static int sp_song_of_peace(castorder * co) { unit *u; region *r = co_get_region(co); 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 * Regionsvermoegens. Der Spruch haelt Stufe Wochen an */ static int sp_generous(castorder * co) { unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force + 1; double 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 = 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("generous_effect_0", "mage", mage); m = msg[0]; } else { if (msg[1] == NULL) msg[1] = msg_message("generous_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 zusaetzlich zur Rekrutierungsmenge in der Region * */ static int sp_recruit(castorder * co) { unit *u; region *r = co_get_region(co); int num, maxp = rpeasants(r); double n; unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; faction *f = mage->faction; const struct race *rc = f->race; if (maxp == 0) { report_failure(mage, co->order); return 0; } /* Immer noch zuviel auf niedrigen Stufen. Deshalb die Rekrutierungskosten * mit einfliessen lassen und dafuer den Exponenten etwas groeßer. * Wenn die Rekrutierungskosten deutlich hoeher sind als der Faktor, * ist das Verhaeltniss von ausgegebene Aura pro Bauer bei Stufe 2 * ein mehrfaches von Stufe 1, denn in beiden Faellen gibt es nur 1 * Bauer, nur die Kosten steigen. */ n = (pow(force, 1.6) * 100) / f->race->recruitcost; if (rc->recruit_multi != 0) { double multp = (double)maxp / rc->recruit_multi; n = _min(multp, n); n = _max(n, 1); rsetpeasants(r, maxp - (int)(n * rc->recruit_multi)); } else { n = _min(maxp, n); n = _max(n, 1); rsetpeasants(r, maxp - (int)n); } num = (int)n; u = create_unit(r, f, num, f->race, 0, LOC(f->locale, (num == 1 ? "peasant" : "peasant_p")), mage); set_order(&u->thisorder, default_order(f->locale)); ADDMSG(&mage->faction->msgs, msg_message("recruit_effect", "mage amount", mage, num)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Wanderprediger - Große Anwerbung * Stufe: 14 * Gebiet: Cerddor * Wirkung: * Bauern schliessen sich der eigenen Partei an * ist zusaetzlich zur Rekrutierungsmenge in der Region * */ static int sp_bigrecruit(castorder * co) { unit *u; region *r = co_get_region(co); 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; } /* Fuer vergleichbare Erfolge bei unterschiedlichen Rassen die * Rekrutierungskosten mit einfliessen lassen. */ n = (int)force + lovar((force * force * 1000) / (float)f->race->recruitcost); if (f->race == get_race(RC_ORC)) { 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 * erzaehlen, was sie ueber die gefragte Region weiß. Ist in der Region * niemand ihrer Partei, so weiß sie nichts zu berichten. Auch kann * sie nur das erzaehlen, was sie selber sehen koennte. * Flags: * (UNITSPELL | TESTCANSEE) */ /* restistenz der einheit pruefen */ static int sp_pump(castorder * co) { unit *u, *target; region *rt; bool 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(u_race(target), RCF_UNDEAD)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error_not_on_undead", "")); return 0; } if (is_magic_resistant(mage, target, 0) || is_monsters(target->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) { ADDMSG(&mage->faction->msgs, msg_message("pump_effect", "mage unit tregion", mage, target, rt)); } else { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_pump", "target tregion", target, rt)); return cast_level / 2; } u = create_unit(rt, mage->faction, RS_FARVISION, get_race(RC_SPELL), 0, "spell/pump", NULL); u->age = 2; set_level(u, SK_PERCEPTION, effskill(target, SK_PERCEPTION, 0)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Verfuehrung * Stufe: 6 * Gebiet: Cerddor * Wirkung: * Betoert eine Einheit, so das sie ihm den groeßten Teil ihres Bargelds * und 50% ihres Besitzes schenkt. Sie behaelt jedoch immer soviel, wie * sie zum ueberleben braucht. Wirkt gegen Magieresistenz. * _min(Stufe*1000$, u->money - maintenace) * Von jedem Item wird 50% abgerundet ermittelt und uebergeben. Dazu * kommt Itemzahl%2 mit 50% chance * * Flags: * (UNITSPELL | TESTRESISTANCE | TESTCANSEE) */ static int sp_seduce(castorder * co) { const resource_type *rsilver = get_resourcetype(R_SILVER); unit *target; item **itmp, *items = 0;; 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(u_race(target), 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->rtype == rsilver) { 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 = _min(loot, (int)(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; } if (items) { 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 fuer Stufe Wochen. Nicht uebertragbar, dh Verbuendete werden vom * Monster natuerlich 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, schlaegt 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; double 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(u_race(target), RCF_UNDEAD)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_noundead", "")); return 0; } effect = 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 hoechsten 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 groeß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 */ unsigned int change = _min(10, target->number) * (rng_uint() % 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 zurueck * * Flags: * (SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) */ static int sp_raisepeasants(castorder * co) { int bauern; unit *u2; attrib *a; region *r = co_get_region(co); 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 = _min(rpeasants(r), (int)(power * 250)); rsetpeasants(r, rpeasants(r) - bauern); u2 = create_unit(r, mage->faction, bauern, get_race(RC_PEASANT), 0, LOC(mage->faction->locale, "furious_mob"), mage); fset(u2, UFL_LOCKED); if (rule_stealth_anon()) { fset(u2, UFL_ANON_FACTION); } 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: Truebsal * Stufe: 11 * Kategorie: Region, negativ * Wirkung: * in der Region kann fuer einige Wochen durch Unterhaltung kein Geld * mehr verdient werden * * Flag: * (FARCASTING | REGIONSPELL | TESTRESISTANCE) */ static int sp_depression(castorder * co) { region *r = co_get_region(co); 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; } /* ------------------------------------------------------------- */ /* TRAUM - Illaun */ /* ------------------------------------------------------------- */ /* Name: Seelenfrieden * Stufe: 2 * Kategorie: Region, positiv * Gebiet: Illaun * Wirkung: * Reduziert Untotencounter * Flag: (0) */ int sp_puttorest(castorder * co) { region *r = co_get_region(co); 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: Traumschloeßchen * Stufe: 3 * Kategorie: Region, Gebaeude, positiv * Gebiet: Illaun * Wirkung: * Mit Hilfe dieses Zaubers kann der Traumweber die Illusion eines * beliebigen Gebaeudes erzeugen. Die Illusion kann betreten werden, ist * aber ansonsten funktionslos und benoetigt auch keinen Unterhalt * Flag: (0) */ int sp_icastle(castorder * co) { building *b; const building_type *type; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; const char *bname; message *msg; const building_type *bt_illusion = bt_find("illusioncastle"); if (!bt_illusion) { 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); /* Groeße festlegen. */ if (type == bt_illusion) { b->size = (rng_int() % (int)((power * power) + 1) * 10); } else if (type->maxsize >0) { b->size = type->maxsize; } else { b->size = ((rng_int() % (int)(power)) + 1) * 5; } if (type->name == NULL) { bname = LOC(mage->faction->locale, type->_name); } else { bname = LOC(mage->faction->locale, buildingtype(type, b, 0)); } building_setname(b, bname); /* TODO: Auf timeout und action_destroy umstellen */ make_icastle(b, type, 2 + (rng_int() % (int)(power)+1) * (rng_int() % (int)(power)+1)); if (mage->region == r) { if (leave(mage, false)) { u_set_building(mage, 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 fuer (Stufe) Wochen als eine andere Gestalt * (wie bei daemonischer 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; const race *irace; /* 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; } /* aehnlich 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; } irace = u_irace(u); if (irace == u_race(u)) { trigger *trestore = trigger_changerace(u, NULL, irace); add_trigger(&u->attribs, "timer", trigger_timeout((int)power + 3, 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 Spruchstaerke und Curse->vigour * ergibt sich die Chance den Spruch zu identifizieren ((force - * c->vigour)*10 + 100 %). */ int sp_analyseregionsdream(castorder * co) { region *r = co_get_region(co); 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 Spruchstaerke 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; } static int sp_gbdreams(castorder * co, const char *curse_name, int effect); /* ------------------------------------------------------------- */ /* Name: Schlechte Traeume * Stufe: 10 * Kategorie: Region, negativ * Wirkung: * Dieser Zauber ermoeglicht es dem Traeumer, den Schlaf aller * nichtaliierten Einheiten (HELFE BEWACHE) in der Region so starkzu * stoeren, das sie 1 Talentstufe in allen Talenten * voruebergehend verlieren. Der Zauber wirkt erst im Folgemonat. * * Flags: * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) * */ int sp_baddreams(castorder * co) { return sp_gbdreams(co, "gbdream", -1); } /* ------------------------------------------------------------- */ /* Name: Schoene Traeume * Stufe: 8 * Kategorie: * Wirkung: * Dieser Zauber ermoeglicht es dem Traeumer, den Schlaf aller aliierten * Einheiten in der Region so zu beeinflussen, daß sie fuer 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) { return sp_gbdreams(co, "gbdream", 1); } static int sp_gbdreams(castorder * co, const char *curse_name, int effect) { int duration; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; region *r = co_get_region(co); curse *c; /* 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. */ c = create_curse(mage, &r->attribs, ct_find(curse_name), power, duration, effect, 0); /* Erfolg melden */ ADDMSG(&mage->faction->msgs, msg_message("regionmagic_effect", "unit region command", c->magician, mage->region, co->order)); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Seelenkopie / Doppelganger * 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_get_region(co); region *target_region = co_get_region(co); 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, get_race(RC_CLONE), 0, name, mage); setstatus(clone, ST_FLEE); fset(clone, UFL_LOCKED); create_newclone(mage, clone); msg = msg_message("sp_clone_effect", "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_get_region(co); 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(u), 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, get_race(RC_SPELL), 0, "spell/dreamreading", NULL); set_number(u2, 1); u2->age = 2; /* Nur fuer diese Runde. */ set_level(u2, SK_PERCEPTION, effskill(u, SK_PERCEPTION, u2->region)); 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_get_region(co); 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 ueber alle angegebenen Einheiten */ for (n = 0; n < pa->length; n++) { curse *c; unit *u; double 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 = 0.05f; c = create_curse(mage, &u->attribs, ct_find("orcish"), power, duration, effect, men); msg = msg_message("sp_sweetdreams_effect", "mage unit region", c->magician, 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_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; int duration = 1 + (int)(power / 6); double effect; curse *c; effect = 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", c->magician, r)); return cast_level; } /* ------------------------------------------------------------- */ /* ASTRAL / THEORIE / M_TYBIED */ /* ------------------------------------------------------------- */ /* 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 Spruchstaerke 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_get_caster(co); int cast_level = co->level; spellparameter *pa = co->par; if (!pa->param) { return 0; } /* 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; spellparameter *pa = co->par; int cast_level = co->level; double power = co->force; int duration = (int)_max(2.0, power + 1); /* works in the report, and ageing this round would kill it if it's <=1 */ /* 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 erhoehen * Stufe: 3 * Aura: 5 MP * Kosten: SPC_LEVEL * Komponenten: * * Wirkung: * erhoeht die Magierestistenz der Personen um 20 Punkte fuer 6 Wochen * Wirkt auf Stufe*5 Personen kann auf mehrere Einheiten gezaubert * werden, bis die Zahl der moeglichen Personen erschoepft ist * * Flags: * UNITSPELL */ int sp_resist_magic_bonus(castorder * co) { unit *u; int n, m; int duration = 6; unit *mage = co->magician.u; int cast_level = co->level; double power = co->force; spellparameter *pa = co->par; /* Pro Stufe koennen bis zu 5 Personen verzaubert werden */ double maxvictims = 5 * power; int victims = (int)maxvictims; /* Schleife ueber alle angegebenen Einheiten */ for (n = 0; n < pa->length; n++) { message *msg; /* sollte nie negativ werden */ if (victims < 1) break; if (pa->param[n]->flag == TARGET_RESISTS || pa->param[n]->flag == TARGET_NOTFOUND) continue; u = pa->param[n]->data.u; m = _min(u->number, victims); victims -= m; create_curse(mage, &u->attribs, ct_find("magicresistance"), power, duration, 20, 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); } cast_level = _min(cast_level, (int)(cast_level * (victims + 4) / maxvictims)); return _max(cast_level, 1); } /** 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_get_region(co); 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 || fval(rt->terrain, FORBIDDEN_REGION)) { 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); /* fuer 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_get_region(co); 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); /* fuer 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)); } 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; ADDMSG(&u->faction->msgs, msg_message("send_astral", "unit target", mage, u)); 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_get_region(co); 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); /* fuer 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_get_region(co); /* region to which we are fetching */ region *ro = NULL; /* region in which the target is */ if (rplane(rt)) { /* Der Zauber funktioniert nur in der materiellen Welt. */ cmistake(mage, co->order, 190, MSG_MAGIC); return 0; } /* fuer 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 (!is_astral(u->region)) { 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_feedback(mage, co->order, "spellfail_distance", "target", u)); continue; } ro = u->region; } if (is_cursed(ro->attribs, C_ASTRALBLOCK, 0)) { ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "spellfail_astralblock", "")); 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_feedback(mage, co->order, "fail_tooheavy", "target", 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_get_region(co); 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 || fval(rt->terrain, FORBIDDEN_REGION)) { /* 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 zaehlen, fuer 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(u) != get_race(RC_SPECIAL) && u_race(u) != get_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(u) != get_race(RC_SPECIAL) && u_race(u) != get_race(RC_SPELL)) { c++; scat(unitname(u)); scat(" ("); if (!fval(u, UFL_ANON_FACTION)) { scat(factionname(u->faction)); scat(", "); } icat(u->number); scat(" "); scat(LOC(mage->faction->locale, rc_name_s(u_race(u), (u->number==1) ? NAME_SINGULAR:NAME_PLURAL))); scat(", Entfernung "); icat(distance(rl2->data, rt)); scat(")"); if (c == n - 1) { scat(" und "); } else if (c < n - 1) { scat(", "); } } } } } scat("."); addmessage(r, mage->faction, buf, MSG_MAGIC, ML_INFO); } free_regionlist(rl); return cast_level; unused_arg(co); return 0; } #endif /* ------------------------------------------------------------- */ int sp_viewreality(castorder * co) { region_list *rl, *rl2; unit *u; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; 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; } rl = astralregions(r, NULL); /* Irgendwann mal auf Curses u/o Attribut umstellen. */ for (rl2 = rl; rl2; rl2 = rl2->next) { region *rt = rl2->data; if (!is_cursed(rt->attribs, C_ASTRALBLOCK, 0)) { u = create_unit(rt, mage->faction, RS_FARVISION, get_race(RC_SPELL), 0, "spell/viewreality", NULL); set_level(u, SK_PERCEPTION, 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_get_region(co); 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 || fval(rt->terrain, FORBIDDEN_REGION)) { /* "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; double 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 zerstoeren */ 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(u) != get_race(RC_SPELL)) { region_list *trl2 = trl; region *tr; int c = rng_int() % inhab_regions; /* Zufaellige Zielregion suchen */ while (c-- != 0) trl2 = trl2->next; tr = trl2->data; if (!is_magic_resistant(mage, u, 0) && can_survive(u, tr)) { message *msg = msg_message("disrupt_astral", "unit region", u, tr); add_message(&u->faction->msgs, msg); add_message(&tr->msgs, msg); msg_release(msg); move_unit(u, tr, NULL); } } } free_regionlist(trl); } /* Kontakt unterbinden */ effect = 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 Gebaeude 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_get_region(co); 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 uebertragen. Auf * einen Tybied-Magier kann er die Haelfte der eingesetzten Kraft * uebertragen, 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_get_region(co); 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 = get_direction(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 || !(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"))) { direction_t d; for (d = 0; d != MAXDIRECTIONS; ++d) { if (rroad(r, d)) { rsetroad(r, d, rroad(r, d) / 2); } } } msg = msg_message("sp_movecastle_effect", "building direction", b, dir); r_addmessage(r, NULL, msg); msg_release(msg); 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 * zufuehren. * * 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 fuehlt seine magischen Kraefte 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 Schwaechezone * Stufe: 5 * Kategorie: * Wirkung: * Reduziert die Staerke jedes Spruch in der Region um Level Haelt * Sprueche bis zu einem Gesammtlevel von Staerke*10 aus, danach ist * sie verbraucht. * leibt bis zu Staerke Wochen aktiv. * Ein Ring der Macht erhoeht die Staerke um 1, in einem Magierturm * gezaubert gibt nochmal +1 auf Staerke. (force) * * Beispiel: * Eine Antimagiezone Stufe 7 haelt bis zu 7 Wochen an oder Sprueche mit * einem Gesammtlevel bis zu 70 auf. Also zB 7 Stufe 10 Sprueche, 10 * Stufe 7 Sprueche oder 35 Stufe 2 Sprueche. Sie reduziert die Staerke * (level+boni) jedes Spruchs, der in der Region gezaubert wird, um * 7. Alle Sprueche mit einer Staerke kleiner als 7 schlagen fehl * (power = 0). * * Flags: * (FARCASTING | SPELLLEVEL | REGIONSPELL | TESTRESISTANCE) * */ int sp_antimagiczone(castorder * co) { double power; double effect; region *r = co_get_region(co); unit *mage = co->magician.u; int cast_level = co->level; double force = co->force; int duration = (int)force + 1; /* Haelt Sprueche bis zu einem summierten Gesamtlevel von power aus. * Jeder Zauber reduziert die 'Lebenskraft' (vigour) der Antimagiezone * um seine Stufe */ power = force * 10; /* Reduziert die Staerke jedes Spruchs um effect */ effect = 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 Gebaeuden 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 koennen mehrere Zauber uebereinander gelegt werden, der Effekt * summiert sich, jedoch wird die Dauer dadurch nicht verlaengert. * * 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 koennen mehrere Zauber uebereinander gelegt werden, der Effekt * summiert sich, jedoch wird die Dauer dadurch nicht verlaengert. * * 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; double effect; duration = 3 + rng_int() % cast_level; effect = 20; switch (pa->param[0]->typ) { case SPP_BUILDING: { building *b; b = pa->param[0]->data.b; /* Magieresistenz der Burg erhoeht 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 erhoeht 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++) { double 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 = 2; create_curse(mage, &u->attribs, ct_find("speed"), force, dur, effect, men); maxmen -= men; used += men; } ADDMSG(&mage->faction->msgs, msg_message("speed_time_effect", "unit region amount", mage, mage->region, used)); /* Effektiv benoetigten cast_level (mindestens 1) zurueckgeben */ used = (int)sqrt(used / 2); return _max(1, used); } /* ------------------------------------------------------------- */ /* Name: Magiefresser * Stufe: 7 * Kosten: SPC_LEVEL * * Wirkung: * Kann eine bestimmte Verzauberung angreifen und aufloesen. Die Staerke * des Zaubers muss staerker 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_get_region(co); 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 aufloesen. Die Staerke * des Zaubers muss staerker 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_get_region(co); 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); return 0; } 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; } /* ueberpruefung, ob curse zu diesem objekt gehoert */ 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 aufloesen, wenn zauber staerker (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 id target", mage, mage->region, co->order, pa->param[1]->data.xs, ts)); } } return cast_level; } /* ------------------------------------------------------------- */ int sp_becomewyrm(castorder * co) { return 0; } typedef struct spelldata { const char *sname; spell_f cast; fumble_f fumble; } spelldata; static spelldata spell_functions[] = { /* M_GWYRRD */ { "stonegolem", sp_create_stonegolem, 0 }, { "irongolem", sp_create_irongolem, 0 }, { "treegrow", sp_hain, fumble_ents }, { "rustweapon", sp_rosthauch, 0 }, { "cold_protection", sp_kaelteschutz, 0 }, { "ironkeeper", sp_ironkeeper, 0 }, { "magicstreet", sp_magicstreet, 0 }, { "windshield", sp_windshield, 0 }, { "mallorntreegrow", sp_mallornhain, fumble_ents }, { "goodwinds", sp_goodwinds, 0 }, { "healing", sp_healing, 0 }, { "reelingarrows", sp_reeling_arrows, 0 }, { "gwyrrdfumbleshield", sp_fumbleshield, 0 }, { "transferauradruide", sp_transferaura, 0 }, { "earthquake", sp_earthquake, 0 }, { "stormwinds", sp_stormwinds, 0 }, { "homestone", sp_homestone, 0 }, { "wolfhowl", sp_wolfhowl, 0 }, { "igjarjuk", sp_igjarjuk, 0 }, { "versteinern", sp_petrify, 0 }, { "strongwall", sp_strong_wall, 0 }, { "gwyrrddestroymagic", sp_destroy_magic, 0 }, { "treewalkenter", sp_treewalkenter, 0 }, { "treewalkexit", sp_treewalkexit, 0 }, { "holyground", sp_holyground, 0 }, { "summonent", sp_summonent, 0 }, { "blessstonecircle", sp_blessstonecircle, 0 }, { "barkskin", sp_armorshield, 0 }, { "summonfireelemental", sp_drought, 0 }, { "maelstrom", sp_maelstrom, 0 }, { "magic_roots", sp_mallorn, 0 }, { "great_drought", sp_great_drought, 0 }, /* M_DRAIG */ { "sparklechaos", sp_sparkle, 0 }, { "magicboost", sp_magicboost, 0 }, { "bloodsacrifice", sp_bloodsacrifice, 0 }, { "berserk", sp_berserk, 0 }, { "fumblecurse", sp_fumblecurse, patzer_fumblecurse }, { "summonundead", sp_summonundead, patzer_peasantmob }, { "combatrust", sp_combatrosthauch, 0 }, { "transferaurachaos", sp_transferaura, 0 }, { "firewall", sp_firewall, patzer_peasantmob }, { "plague", sp_plague, patzer_peasantmob }, { "chaosrow", sp_chaosrow, 0 }, { "summonshadow", sp_summonshadow, patzer_peasantmob }, { "undeadhero", sp_undeadhero, 0 }, { "auraleak", sp_auraleak, 0 }, { "draigfumbleshield", sp_fumbleshield, 0 }, { "forestfire", sp_forest_fire, patzer_peasantmob }, { "draigdestroymagic", sp_destroy_magic, 0 }, { "unholypower", sp_unholypower, 0 }, { "deathcloud", sp_deathcloud, patzer_peasantmob }, { "summondragon", sp_summondragon, patzer_peasantmob }, { "summonshadowlords", sp_summonshadowlords, patzer_peasantmob }, { "chaossuction", sp_chaossuction, patzer_peasantmob }, /* M_ILLAUN */ { "sparkledream", sp_sparkle, 0 }, { "shadowknights", sp_shadowknights, 0 }, { "flee", sp_flee, 0 }, { "puttorest", sp_puttorest, 0 }, { "icastle", sp_icastle, 0 }, { "transferauratraum", sp_transferaura, 0 }, { "shapeshift", sp_illusionary_shapeshift, 0 }, { "dreamreading", sp_dreamreading, 0 }, { "tiredsoldiers", sp_tiredsoldiers, 0 }, { "reanimate", sp_reanimate, 0 }, { "analysedream", sp_analysedream, 0 }, { "disturbingdreams", sp_disturbingdreams, 0 }, { "sleep", sp_sleep, 0 }, { "wisps", 0, 0 }, /* TODO: this spell is gone */ { "gooddreams", sp_gooddreams, 0 }, { "illaundestroymagic", sp_destroy_magic, 0 }, { "clone", sp_clonecopy, 0 }, { "bad_dreams", sp_baddreams, 0 }, { "mindblast", sp_mindblast_temp, 0 }, { "orkdream", sp_sweetdreams, 0 }, /* M_CERDDOR */ { "appeasement", sp_denyattack, 0 }, { "song_of_healing", sp_healing, 0 }, { "generous", sp_generous, 0 }, { "song_of_fear", sp_flee, 0 }, { "courting", sp_recruit, 0 }, { "song_of_confusion", sp_chaosrow, 0 }, { "heroic_song", sp_hero, 0 }, { "transfer_aura_song", sp_transferaura, 0 }, { "analysesong_unit", sp_analysesong_unit, 0 }, { "cerddorfumbleshield", sp_fumbleshield, 0 }, { "calm_monster", sp_calm_monster, 0 }, { "seduction", sp_seduce, 0 }, { "headache", sp_headache, 0 }, { "sound_out", sp_pump, 0 }, { "bloodthirst", sp_berserk, 0 }, { "frighten", sp_frighten, 0 }, { "analyse_object", sp_analysesong_obj, 0 }, { "cerddor_destroymagic", sp_destroy_magic, 0 }, { "migration", sp_migranten, 0 }, { "summon_familiar", sp_summon_familiar, 0 }, { "raise_mob", sp_raisepeasants, 0 }, { "song_resist_magic", sp_song_resistmagic, 0 }, { "melancholy", sp_depression, 0 }, { "song_suscept_magic", sp_song_susceptmagic, 0 }, { "song_of_peace", sp_song_of_peace, 0 }, { "song_of_slavery", sp_charmingsong, 0 }, { "big_recruit", sp_bigrecruit, 0 }, { "calm_riot", sp_rallypeasantmob, 0 }, { "incite_riot", sp_raisepeasantmob, 0 }, /* M_TYBIED */ { "analyze_magic", sp_analysemagic, 0 }, { "concealing_aura", sp_itemcloak, 0 }, { "tybiedfumbleshield", sp_fumbleshield, 0 }, #ifdef SHOWASTRAL_NOT_BORKED { "show_astral", sp_showastral, 0}, #endif { "resist_magic", sp_resist_magic_bonus, 0 }, { "keeploot", sp_keeploot, 0 }, { "enterastral", sp_enterastral, 0 }, { "leaveastral", sp_leaveastral, 0 }, { "auratransfer", sp_transferaura, 0 }, { "shockwave", sp_stun, 0 }, { "antimagiczone", sp_antimagiczone, 0 }, { "destroy_magic", sp_destroy_magic, 0 }, { "pull_astral", sp_pullastral, 0 }, { "fetch_astral", sp_fetchastral, 0 }, { "steal_aura", sp_stealaura, 0 }, { "airship", sp_flying_ship, 0 }, { "break_curse", sp_break_curse, 0 }, { "eternal_walls", sp_eternizewall, 0 }, { "protective_runes", sp_magicrunes, 0 }, { "fish_shield", sp_reduceshield, 0 }, { "combat_speed", sp_speed, 0 }, { "view_reality", sp_viewreality, 0 }, { "double_time", sp_speed2, 0 }, { "armor_shield", sp_armorshield, 0 }, { "living_rock", sp_movecastle, 0 }, { "astral_disruption", sp_disruptastral, 0 }, { "sacrifice_strength", sp_permtransfer, 0 }, /* M_GRAY */ /* Definitionen von Create_Artefaktspruechen */ { "wyrm_transformation", sp_becomewyrm, 0 }, /* Monstersprueche */ { "fiery_dragonbreath", sp_dragonodem, 0 }, { "icy_dragonbreath", sp_dragonodem, 0 }, { "powerful_dragonbreath", sp_dragonodem, 0 }, { "drain_skills", sp_dragonodem, 0 }, { "aura_of_fear", sp_flee, 0 }, { "shadowcall", sp_shadowcall, 0 }, { "immolation", sp_immolation, 0 }, { "firestorm", sp_immolation, 0 }, { "coldfront", sp_immolation, 0 }, { "acidrain", sp_immolation, 0 }, { 0, 0, 0 } }; static void register_spelldata(void) { int i; char zText[32]; strcpy(zText, "fumble_"); for (i = 0; spell_functions[i].sname; ++i) { spelldata *data = spell_functions + i; if (data->cast) { register_function((pf_generic)data->cast, data->sname); } if (data->fumble) { strlcpy(zText + 7, data->sname, sizeof(zText) - 7); register_function((pf_generic)data->fumble, zText); } } } /* ------------------------------------------------------------- */ /* Name: Plappermaul * Stufe: 4 * Gebiet: Cerddor * Kategorie: Einheit * * Wirkung: * Einheit ausspionieren. Gibt auch Zauber und Kampfstatus aus. Wirkt * gegen Magieresistenz. Ist diese zu hoch, so wird der Zauber entdeckt * (Meldung) und der Zauberer erhaelt nur die Talente, nicht die Werte * der Einheit und auch keine Zauber. * * Flag: * (UNITSPELL | TESTCANSEE) */ static int sp_babbler(castorder * co) { unit *target; region *r = co_get_region(co); 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; target = pa->param[0]->data.u; if (target->faction == mage->faction) { /* Die Einheit ist eine der unsrigen */ cmistake(mage, co->order, 45, MSG_MAGIC); } /* Magieresistenz Unit */ if (target_resists_magic(mage, target, TYP_UNIT, 0)) { spy_message(5, mage, target); msg = msg_message("babbler_resist", "unit mage", target, mage); } else { spy_message(100, mage, target); msg = msg_message("babbler_effect", "unit", target); } r_addmessage(r, target->faction, msg); msg_release(msg); return cast_level; } /* ------------------------------------------------------------- */ /* Name: Traumdeuten * Stufe: 7 * Kategorie: Einheit * * Wirkung: * Wirkt gegen Magieresistenz. Spioniert die Einheit aus. Gibt alle * Gegenstaende, Talente mit Stufe, Zauber und Kampfstatus an. * * Magieresistenz hier pruefen, wegen Fehlermeldung * * Flag: * (UNITSPELL) */ static int sp_readmind(castorder * co) { unit *target; 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; if (target->faction == mage->faction) { /* Die Einheit ist eine der unsrigen */ cmistake(mage, co->order, 45, MSG_MAGIC); } /* Magieresistenz Unit */ if (target_resists_magic(mage, target, TYP_UNIT, 0)) { cmistake(mage, co->order, 180, MSG_MAGIC); /* "Fuehlt sich beobachtet" */ ADDMSG(&target->faction->msgs, msg_message("stealdetect", "unit", target)); return 0; } spy_message(2, mage, target); return cast_level; } void register_magicresistance(void); void register_spells(void) { register_borders(); at_register(&at_wdwpyramid); at_register(&at_deathcloud_compat); /* init_firewall(); */ ct_register(&ct_firewall); ct_register(&ct_deathcloud); register_function((pf_generic)sp_blessedharvest, "cast_blessedharvest"); register_function((pf_generic)sp_summon_familiar, "cast_familiar"); register_function((pf_generic)sp_babbler, "cast_babbler"); register_function((pf_generic)sp_readmind, "cast_readmind"); register_function((pf_generic)sp_kampfzauber, "combat_spell"); register_spelldata(); register_unitcurse(); register_regioncurse(); register_shipcurse(); register_buildingcurse(); register_magicresistance(); register_flyingship(); }