server/src/spells.c

6748 lines
197 KiB
C
Raw Blame History

/*
*
*
* 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 <platform.h>
#include <kernel/config.h>
#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 <spells/borders.h>
#include <spells/buildingcurse.h>
#include <spells/regioncurse.h>
#include <spells/unitcurse.h>
#include <spells/shipcurse.h>
#include <spells/combatspells.h>
#include <spells/flyingship.h>
/* kernel includes */
#include <kernel/curse.h>
#include <kernel/connection.h>
#include <kernel/building.h>
#include <kernel/curse.h>
#include <kernel/faction.h>
#include <kernel/item.h>
#include <kernel/messages.h>
#include <kernel/objtypes.h>
#include <kernel/order.h>
#include <kernel/plane.h>
#include <kernel/pool.h>
#include <kernel/race.h>
#include <kernel/region.h>
#include <kernel/save.h>
#include <kernel/ship.h>
#include <kernel/spell.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
#include <kernel/unit.h>
#include <kernel/xmlreader.h>
#include <races/races.h>
/* util includes */
#include <util/assert.h>
#include <util/attrib.h>
#include <util/base36.h>
#include <util/event.h>
#include <util/gamedata.h>
#include <util/language.h>
#include <util/message.h>
#include <util/parser.h>
#include <util/umlaut.h>
#include <util/functions.h>
#include <util/lists.h>
#include <util/rand.h>
#include <util/log.h>
#include <util/nrmessage.h>
#include <util/bsdstring.h>
#include <util/variant.h>
#include <util/goodies.h>
#include <util/resolve.h>
#include <util/rng.h>
#include <storage.h>
/* libc includes */
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* triggers includes */
#include <triggers/changefaction.h>
#include <triggers/changerace.h>
#include <triggers/createcurse.h>
#include <triggers/createunit.h>
#include <triggers/killunit.h>
#include <triggers/timeout.h>
#include <triggers/unitmessage.h>
/* attributes includes */
#include <attributes/targetregion.h>
#include <attributes/hate.h>
/* ----------------------------------------------------------------------- */
#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<6C>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 <spruchname> <Einheit-Nr> <investierte Aura>"
* "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 (!is_building_type(b->type, "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));
assert_alloc(iweapon);
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);
static int cache;
static const race * golem_rc;
if (rc_changed(&cache)) {
golem_rc = rc_find("irongolem");
}
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, golem_rc, 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(golem_rc, (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);
static int cache;
static const race * golem_rc;
if (rc_changed(&cache)) {
golem_rc = rc_find("stonegolem");
}
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, golem_rc, 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(golem_rc, (u2->number == 1) ? NAME_SINGULAR : NAME_PLURAL))));
return cast_level;
}
/* ------------------------------------------------------------- */
/* Name: Gro<72>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" <Einheit> ...
*
* 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" <Ziel-X> <Ziel-Y> <Einheit> ...
*
* 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 <20>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)
{
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);
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 || !is_building_type(mage->building->type, "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<65>, 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<6F>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 const race *unholy_race(const race *rc) {
static int cache;
static const race * rc_skeleton, *rc_zombie, *rc_ghoul;
if (rc_changed(&cache)) {
rc_skeleton = get_race(RC_SKELETON);
rc_zombie = get_race(RC_ZOMBIE);
rc_ghoul = get_race(RC_GHOUL);
}
if (rc == rc_skeleton) {
return get_race(RC_SKELETON_LORD);
}
if (rc == rc_zombie) {
return get_race(RC_ZOMBIE_LORD);
}
if (rc == rc_ghoul) {
return get_race(RC_GHOUL_LORD);
}
return NULL;
}
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;
target_race = unholy_race(u_race(u));
if (!target_race) {
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<65>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;
const curse_type *ct_auraboost;
const curse_type *ct_magicboost;
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<72>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<6F>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<72>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<65>. Ist in der Region
* niemand ihrer Partei, so wei<65> 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, 1, 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<6F>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<6F>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<6F>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<6F>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" <einheit> <rasse>
* 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<64> 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, 1, get_race(RC_SPELL), 0,
"spell/dreamreading", NULL);
set_number(u2, 1);
u2->age = 2; /* Nur fuer diese Runde. */
set_level(u2, SK_PERCEPTION, effskill(u, SK_PERCEPTION, u2->region));
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' <Einheit-Nr> [<Einheit-Nr> ...]",
*
* 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, 1, 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" <gebaeude-nummer>
* 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\" <Einheit-Nr> <Aura>
* "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 ((is_building_type(b->type, "caravan") || is_building_type(b->type, "dam")
|| is_building_type(b->type, "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 <Gebaeude-Nr>
* ZAUBERE \"Runen des Schutzes\" SCHIFF <Schiff-Nr>
* "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 <Einheit-Nr>
* ZAUBERE \"Magiefresser\" GEBAEUDE <Gebaeude-Nr>
* ZAUBERE \"Magiefresser\" SCHIFF <Schiff-Nr>
*
* "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 <Zauber-id>
* ZAUBERE \"Fluch brechen\" EINHEIT <Einheit-Nr> <Zauber-id>
* ZAUBERE \"Fluch brechen\" GEBAEUDE <Gebaeude-Nr> <Zauber-id>
* ZAUBERE \"Fluch brechen\" SCHIFF <Schiff-Nr> <Zauber-id>
*
* "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();
}