server/src/kernel/magic.c

2986 lines
85 KiB
C
Raw Normal View History

2010-08-08 10:06:34 +02:00
/*
2014-08-08 01:03:46 +02:00
Copyright (c) 1998-2014,
Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
2010-08-08 10:06:34 +02:00
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
#include <platform.h>
#include <kernel/config.h>
#include <kernel/types.h>
2010-08-08 10:06:34 +02:00
#include "magic.h"
#include "building.h"
#include "curse.h"
#include "faction.h"
#include "item.h"
#include "messages.h"
2010-08-08 10:06:34 +02:00
#include "objtypes.h"
#include "order.h"
#include "pathfinder.h"
#include "plane.h"
#include "pool.h"
#include "race.h"
#include "region.h"
2012-05-26 17:20:26 +02:00
#include "save.h"
2010-08-08 10:06:34 +02:00
#include "ship.h"
#include "skill.h"
#include "spell.h"
#include "spellbook.h"
2010-08-08 10:06:34 +02:00
#include "teleport.h"
#include "terrain.h"
#include "unit.h"
#include "version.h"
#include <triggers/timeout.h>
#include <triggers/shock.h>
#include <triggers/killunit.h>
#include <triggers/giveitem.h>
#include <triggers/changerace.h>
#include <triggers/clonedied.h>
/* util includes */
#include <util/attrib.h>
#include <critbit.h>
2010-08-08 10:06:34 +02:00
#include <util/language.h>
#include <util/lists.h>
#include <util/log.h>
#include <util/parser.h>
#include <quicklist.h>
2010-08-08 10:06:34 +02:00
#include <util/resolve.h>
#include <util/rand.h>
#include <util/rng.h>
#include <util/umlaut.h>
2010-08-08 10:06:34 +02:00
#include <util/base36.h>
#include <util/event.h>
#include <storage.h>
2010-08-08 10:06:34 +02:00
/* libc includes */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include <math.h>
2011-03-07 08:02:35 +01:00
const char *magic_school[MAXMAGIETYP] = {
2014-08-08 01:03:46 +02:00
"gray",
"illaun",
"tybied",
"cerddor",
"gwyrrd",
"draig",
"common"
2010-08-08 10:06:34 +02:00
};
static void a_init_reportspell(struct attrib *a) {
2014-08-08 01:03:46 +02:00
a->data.v = calloc(1, sizeof(spellbook_entry));
}
static void a_finalize_reportspell(struct attrib *a) {
2014-08-08 01:03:46 +02:00
free(a->data.v);
}
2010-08-08 10:06:34 +02:00
attrib_type at_reportspell = {
2014-08-08 01:03:46 +02:00
"reportspell",
a_init_reportspell,
a_finalize_reportspell,
0, NO_WRITE, NO_READ
2010-08-08 10:06:34 +02:00
};
/**
** at_icastle
** TODO: separate castle-appearance from illusion-effects
**/
static float MagicRegeneration(void)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
static float value = -1.0;
if (value < 0) {
const char *str = get_param(global.parameters, "magic.regeneration");
value = str ? (float)atof(str) : 1.0F;
}
return value;
2010-08-08 10:06:34 +02:00
}
float MagicPower(void)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
static float value = -1.0;
if (value < 0) {
const char *str = get_param(global.parameters, "magic.power");
value = str ? (float)atof(str) : 1.0f;
}
return value;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int a_readicastle(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
icastle_data *data = (icastle_data *)a->data.v;
variant bno;
char token[32];
READ_TOK(store, token, sizeof(token));
READ_INT(store, &bno.i);
READ_INT(store, &data->time);
data->building = findbuilding(bno.i);
if (!data->building) {
/* this shouldn't happen, but just in case it does: */
ur_add(bno, &data->building, resolve_building);
}
data->type = bt_find(token);
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
static void
2011-03-07 08:02:35 +01:00
a_writeicastle(const attrib * a, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
icastle_data *data = (icastle_data *)a->data.v;
WRITE_TOK(store, data->type->_name);
WRITE_INT(store, data->building->no);
WRITE_INT(store, data->time);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int a_ageicastle(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
icastle_data *data = (icastle_data *)a->data.v;
if (data->time <= 0) {
building *b = data->building;
region *r = b->region;
ADDMSG(&r->msgs, msg_message("icastle_dissolve", "building", b));
/* remove_building lets units leave the building */
remove_building(&r->buildings, b);
return AT_AGE_REMOVE;
}
else
data->time--;
return AT_AGE_KEEP;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void a_initicastle(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
a->data.v = calloc(sizeof(icastle_data), 1);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void a_finalizeicastle(struct attrib *a)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
free(a->data.v);
2010-08-08 10:06:34 +02:00
}
attrib_type at_icastle = {
2014-08-08 01:03:46 +02:00
"zauber_icastle",
a_initicastle,
a_finalizeicastle,
a_ageicastle,
a_writeicastle,
a_readicastle
2010-08-08 10:06:34 +02:00
};
/* ------------------------------------------------------------- */
extern int dice(int count, int value);
/* ------------------------------------------------------------- */
/* aus dem alten System <20>briggebliegene Funktionen, die bei der
* Umwandlung von alt nach neu gebraucht werden */
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static void init_mage(attrib * a)
{
2014-08-08 01:03:46 +02:00
a->data.v = calloc(sizeof(sc_mage), 1);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void free_mage(attrib * a)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *mage = (sc_mage *)a->data.v;
if (mage->spellbook) {
spellbook_clear(mage->spellbook);
free(mage->spellbook);
}
free(mage);
2010-08-08 10:06:34 +02:00
}
int FactionSpells(void)
{
2014-08-08 01:03:46 +02:00
static int rules_factionspells = -1;
if (rules_factionspells < 0) {
rules_factionspells =
get_param_int(global.parameters, "rules.magic.factionlist", 0);
}
return rules_factionspells;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void read_spells(struct quicklist **slistp, magic_t mtype,
2014-08-08 01:03:46 +02:00
struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
for (;;) {
spell *sp;
char spname[64];
if (global.data_version < SPELLNAME_VERSION) {
int i;
READ_INT(store, &i);
if (i < 0)
break;
sp = find_spellbyid((unsigned int)i);
}
else {
READ_TOK(store, spname, sizeof(spname));
if (strcmp(spname, "end") == 0)
break;
sp = find_spell(spname);
if (!sp) {
log_error("read_spells: could not find spell '%s' in school '%s'\n", spname, magic_school[mtype]);
}
}
if (sp) {
add_spell(slistp, sp);
}
2010-08-08 10:06:34 +02:00
}
}
int get_spell_level_mage(const spell * sp, void * cbdata)
{
2014-08-08 01:03:46 +02:00
sc_mage *mage = (sc_mage *)cbdata;
spellbook *book = get_spellbook(magic_school[mage->magietyp]);
spellbook_entry *sbe = spellbook_get(book, sp);
return sbe ? sbe->level : 0;
}
2011-03-07 08:02:35 +01:00
static int read_mage(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int i, mtype;
sc_mage *mage = (sc_mage *)a->data.v;
char spname[64];
READ_INT(store, &mtype);
mage->magietyp = (magic_t)mtype;
READ_INT(store, &mage->spellpoints);
READ_INT(store, &mage->spchange);
for (i = 0; i != MAXCOMBATSPELLS; ++i) {
spell *sp = NULL;
int level = 0;
if (global.data_version < SPELLNAME_VERSION) {
int spid;
READ_INT(store, &spid);
READ_INT(store, &level);
if (spid >= 0) {
sp = find_spellbyid((unsigned int)spid);
}
}
else {
READ_TOK(store, spname, sizeof(spname));
READ_INT(store, &level);
if (strcmp("none", spname) != 0) {
sp = find_spell(spname);
if (!sp) {
log_error("read_mage: could not find combat spell '%s' in school '%s'\n", spname, magic_school[mage->magietyp]);
}
}
}
if (sp && level >= 0) {
int slot = -1;
if (sp->sptyp & PRECOMBATSPELL)
slot = 0;
else if (sp->sptyp & COMBATSPELL)
slot = 1;
else if (sp->sptyp & POSTCOMBATSPELL)
slot = 2;
if (slot >= 0) {
mage->combatspells[slot].level = level;
mage->combatspells[slot].sp = sp;
}
}
}
if (mage->magietyp == M_GRAY) {
read_spellbook(&mage->spellbook, store, get_spell_level_mage, mage);
}
else {
read_spellbook(0, store, 0, mage);
}
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void write_spells(struct quicklist *slist, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
quicklist *ql;
int qi;
2014-08-08 01:03:46 +02:00
for (ql = slist, qi = 0; ql; ql_advance(&ql, &qi, 1)) {
spell *sp = (spell *)ql_get(ql, qi);
WRITE_TOK(store, sp->sname);
}
WRITE_TOK(store, "end");
2010-08-08 10:06:34 +02:00
}
static void
2011-03-07 08:02:35 +01:00
write_mage(const attrib * a, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int i;
sc_mage *mage = (sc_mage *)a->data.v;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
WRITE_INT(store, mage->magietyp);
WRITE_INT(store, mage->spellpoints);
WRITE_INT(store, mage->spchange);
for (i = 0; i != MAXCOMBATSPELLS; ++i) {
WRITE_TOK(store,
mage->combatspells[i].sp ? mage->combatspells[i].sp->sname : "none");
WRITE_INT(store, mage->combatspells[i].level);
}
write_spellbook(mage->spellbook, store);
2010-08-08 10:06:34 +02:00
}
attrib_type at_mage = {
2014-08-08 01:03:46 +02:00
"mage",
init_mage,
free_mage,
NULL,
write_mage,
read_mage,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
bool is_mage(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
return i2b(get_mage(u) != NULL);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
sc_mage *get_mage(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
if (has_skill(u, SK_MAGIC)) {
attrib *a = a_find(u->attribs, &at_mage);
if (a)
return a->data.v;
}
return (sc_mage *)NULL;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Ausgabe der Spruchbeschreibungen
* Anzeige des Spruchs nur, wenn die Stufe des besten Magiers vorher
* kleiner war (u->faction->seenspells). Ansonsten muss nur gepr<EFBFBD>ft
* werden, ob dieser Magier den Spruch schon kennt, und andernfalls der
* Spruch zu seiner List-of-known-spells hinzugef<EFBFBD>gt werden.
*/
2011-03-07 08:02:35 +01:00
static int read_seenspell(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int i;
spell *sp = 0;
char token[32];
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
READ_TOK(store, token, sizeof(token));
i = atoi(token);
if (i != 0) {
sp = find_spellbyid((unsigned int)i);
}
else {
if (global.data_version < UNIQUE_SPELLS_VERSION) {
READ_INT(store, 0); /* ignore mtype */
}
sp = find_spell(token);
if (!sp) {
log_warning("read_seenspell: could not find spell '%s'\n", token);
}
}
if (!sp) {
2014-08-08 01:03:46 +02:00
return AT_READ_FAIL;
}
2014-08-08 01:03:46 +02:00
a->data.v = sp;
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
static void
2011-03-07 08:02:35 +01:00
write_seenspell(const attrib * a, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
const spell *sp = (const spell *)a->data.v;
WRITE_TOK(store, sp->sname);
2010-08-08 10:06:34 +02:00
}
attrib_type at_seenspell = {
2014-08-08 01:03:46 +02:00
"seenspell", NULL, NULL, NULL, write_seenspell, read_seenspell
2010-08-08 10:06:34 +02:00
};
#define MAXSPELLS 256
static bool already_seen(const faction * f, const spell * sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
for (a = a_find(f->attribs, &at_seenspell); a && a->type == &at_seenspell;
a = a->next) {
if (a->data.v == sp)
return true;
}
return false;
2010-08-08 10:06:34 +02:00
}
void show_new_spells(faction * f, int level, const spellbook *book)
{
2014-08-08 01:03:46 +02:00
if (book) {
quicklist *ql = book->spells;
int qi;
for (qi = 0; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *)ql_get(ql, qi);
if (sbe->level <= level) {
if (!already_seen(f, sbe->sp)) {
attrib * a = a_new(&at_reportspell);
spellbook_entry * entry = (spellbook_entry *)a->data.v;
entry->level = sbe->level;
entry->sp = sbe->sp;
a_add(&f->attribs, a);
a_add(&f->attribs, a_new(&at_seenspell))->data.v = sbe->sp;
}
}
}
2010-08-08 10:06:34 +02:00
}
}
/** update the spellbook with a new level
* Written for E3
2010-08-08 10:06:34 +02:00
*/
void pick_random_spells(faction * f, int level, spellbook * book, int num_spells)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spellbook_entry *commonspells[MAXSPELLS];
int qi, numspells = 0;
quicklist *ql;
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
if (level <= f->max_spelllevel) {
return;
}
2014-08-08 01:03:46 +02:00
for (qi = 0, ql = book->spells; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry * sbe = (spellbook_entry *)ql_get(ql, qi);
if (sbe->level <= level) {
commonspells[numspells++] = sbe;
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
while (numspells > 0 && level > f->max_spelllevel) {
int i;
++f->max_spelllevel;
for (i = 0; i < num_spells; ++i) {
int maxspell = numspells;
int spellno = -1;
spellbook_entry *sbe = 0;
while (!sbe && maxspell>0) {
spellno = rng_int() % maxspell;
sbe = commonspells[spellno];
if (sbe->level > f->max_spelllevel) {
commonspells[spellno] = commonspells[--maxspell];
commonspells[maxspell] = sbe;
sbe = 0;
}
else if (f->spellbook && spellbook_get(f->spellbook, sbe->sp)) {
commonspells[spellno] = commonspells[--numspells];
if (maxspell > numspells) {
maxspell = numspells;
}
sbe = 0;
}
}
2014-08-08 01:03:46 +02:00
if (spellno < maxspell) {
if (!f->spellbook) {
f->spellbook = create_spellbook(0);
}
spellbook_add(f->spellbook, sbe->sp, sbe->level);
commonspells[spellno] = commonspells[--numspells];
}
}
}
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Erzeugen eines neuen Magiers */
2011-03-07 08:02:35 +01:00
sc_mage *create_mage(unit * u, magic_t mtyp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *mage;
attrib *a;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
a = a_find(u->attribs, &at_mage);
if (a != NULL) {
a_remove(&u->attribs, a);
}
a = a_add(&u->attribs, a_new(&at_mage));
mage = a->data.v;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
mage->magietyp = mtyp;
return mage;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Funktionen f<>r die Bearbeitung der List-of-known-spells */
2012-05-26 17:20:26 +02:00
int u_hasspell(const unit *u, const struct spell *sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spellbook * book = unit_get_spellbook(u);
spellbook_entry * sbe = book ? spellbook_get(book, sp) : 0;
if (sbe) {
return sbe->level <= effskill(u, SK_MAGIC);
}
return 0;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Eingestellte Kampfzauberstufe ermitteln */
2011-03-07 08:02:35 +01:00
int get_combatspelllevel(const unit * u, int nr)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m = get_mage(u);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
assert(nr < MAXCOMBATSPELLS);
if (m) {
int level = eff_skill(u, SK_MAGIC, u->region);
return _min(m->combatspells[nr].level, level);
}
return -1;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Kampfzauber ermitteln, setzen oder l<>schen */
2011-03-07 08:02:35 +01:00
const spell *get_combatspell(const unit * u, int nr)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
assert(nr < MAXCOMBATSPELLS);
m = get_mage(u);
if (m) {
return m->combatspells[nr].sp;
}
else if (u_race(u)->precombatspell != NULL) {
return u_race(u)->precombatspell;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void set_combatspell(unit * u, spell * sp, struct order *ord, int level)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *mage = get_mage(u);
int i = -1;
assert(mage || !"trying to set a combat spell for non-mage");
/* knowsspell pr<70>ft auf ist_magier, ist_spruch, kennt_spruch */
if (!knowsspell(u->region, u, sp)) {
/* Fehler 'Spell not found' */
cmistake(u, ord, 173, MSG_MAGIC);
return;
}
if (!u_hasspell(u, sp)) {
/* Diesen Zauber kennt die Einheit nicht */
cmistake(u, ord, 169, MSG_MAGIC);
return;
}
if (!(sp->sptyp & ISCOMBATSPELL)) {
/* Diesen Kampfzauber gibt es nicht */
cmistake(u, ord, 171, MSG_MAGIC);
return;
}
if (sp->sptyp & PRECOMBATSPELL)
i = 0;
else if (sp->sptyp & COMBATSPELL)
i = 1;
else if (sp->sptyp & POSTCOMBATSPELL)
i = 2;
assert(i >= 0);
mage->combatspells[i].sp = sp;
mage->combatspells[i].level = level;
2010-08-08 10:06:34 +02:00
return;
}
2011-03-07 08:02:35 +01:00
void unset_combatspell(unit * u, spell * sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
int nr = 0;
int i;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m)
return;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (!sp) {
for (i = 0; i < MAXCOMBATSPELLS; i++) {
m->combatspells[i].sp = NULL;
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
else if (sp->sptyp & PRECOMBATSPELL) {
if (sp != get_combatspell(u, 0))
return;
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
else if (sp->sptyp & COMBATSPELL) {
if (sp != get_combatspell(u, 1)) {
return;
}
nr = 1;
}
else if (sp->sptyp & POSTCOMBATSPELL) {
if (sp != get_combatspell(u, 2)) {
return;
}
nr = 2;
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
m->combatspells[nr].sp = NULL;
m->combatspells[nr].level = 0;
return;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Gibt die aktuelle Anzahl der Magiepunkte der Einheit zur<75>ck */
2011-03-07 08:02:35 +01:00
int get_spellpoints(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m)
return 0;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return m->spellpoints;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void set_spellpoints(unit * u, int sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m)
return;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m->spellpoints = sp;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return;
2010-08-08 10:06:34 +02:00
}
/*
* ver<EFBFBD>ndert die Anzahl der Magiepunkte der Einheit um +mp
*/
2011-03-07 08:02:35 +01:00
int change_spellpoints(unit * u, int mp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
int sp;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m) {
return 0;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* verhindere negative Magiepunkte */
sp = _max(m->spellpoints + mp, 0);
m->spellpoints = sp;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return sp;
2010-08-08 10:06:34 +02:00
}
/* bietet die M<>glichkeit, die maximale Anzahl der Magiepunkte mit
* Regionszaubern oder Attributen zu beinflussen
*/
2011-03-07 08:02:35 +01:00
static int get_spchange(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m)
return 0;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return m->spchange;
2010-08-08 10:06:34 +02:00
}
/* ein Magier kann normalerweise maximal Stufe^2.1/1.2+1 Magiepunkte
* haben.
* Manche Rassen haben einen zus<EFBFBD>tzlichen Multiplikator
* Durch Talentverlust (zB Insekten im Berg) k<EFBFBD>nnen negative Werte
* entstehen
*/
/* Artefakt der St<53>rke
* Erm<EFBFBD>glicht dem Magier mehr Magiepunkte zu 'speichern'
*/
/** TODO: at_skillmod daraus machen */
2011-03-07 08:02:35 +01:00
static int use_item_aura(const region * r, const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int sk, n;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
sk = eff_skill(u, SK_MAGIC, r);
n = (int)(sk * sk * u_race(u)->maxaura / 4);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return n;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int max_spellpoints(const region * r, const unit * u)
2010-08-08 10:06:34 +02:00
{
int sk;
double n, msp;
double potenz = 2.1;
double divisor = 1.2;
const struct resource_type *rtype;
sk = eff_skill(u, SK_MAGIC, r);
msp = u_race(u)->maxaura * (pow(sk, potenz) / divisor + 1) + get_spchange(u);
2014-08-08 01:03:46 +02:00
rtype = rt_find("aurafocus");
if (rtype && i_get(u->items, rtype->itype) > 0) {
msp += use_item_aura(r, u);
}
n = get_curseeffect(u->attribs, C_AURA, 0);
if (n > 0) {
msp = (msp * n) / 100;
}
return _max((int)msp, 0);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
int change_maxspellpoints(unit * u, int csp)
2010-08-08 10:06:34 +02:00
{
sc_mage *m;
2010-08-08 10:06:34 +02:00
m = get_mage(u);
if (!m) {
return 0;
}
m->spchange += csp;
return max_spellpoints(u->region, u);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Counter f<>r die bereits gezauberte Anzahl Spr<70>che pro Runde.
* Um nur die Zahl der bereits gezauberten Spr<EFBFBD>che zu ermitteln mit
* step = 0 aufrufen.
*/
2011-03-07 08:02:35 +01:00
int countspells(unit * u, int step)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
sc_mage *m;
int count;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
m = get_mage(u);
if (!m)
return 0;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (step == 0)
return m->spellcount;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
count = m->spellcount + step;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* negative Werte abfangen. */
m->spellcount = _max(0, count);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return m->spellcount;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Die f<>r den Spruch ben<65>tigte Aura pro Stufe.
* Die Grundkosten pro Stufe werden hier um 2^count erh<EFBFBD>ht. Der
* Parameter count ist dabei die Anzahl der bereits gezauberten Spr<EFBFBD>che
*/
2011-03-07 08:02:35 +01:00
int spellcost(unit * u, const spell * sp)
2010-08-08 10:06:34 +02:00
{
int k, aura = 0;
int count = countspells(u, 0);
const resource_type *r_aura = get_resourcetype(R_AURA);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
for (k = 0; sp->components[k].type; k++) {
if (sp->components[k].type == r_aura) {
aura = sp->components[k].amount;
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
aura *= (1 << count);
return aura;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* SPC_LINEAR ist am h<>chstwertigen, dann m<>ssen Komponenten f<>r die
* Stufe des Magiers vorhanden sein.
* SPC_LINEAR hat die gew<EFBFBD>nschte Stufe als multiplikator,
* nur SPC_FIX muss nur einmal vorhanden sein, ist also am
* niedrigstwertigen und sollte von den beiden anderen Typen
* <EFBFBD>berschrieben werden */
2011-03-07 08:02:35 +01:00
static int spl_costtyp(const spell * sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int k;
int costtyp = SPC_FIX;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
for (k = 0; sp->components[k].type; k++) {
if (costtyp == SPC_LINEAR)
return SPC_LINEAR;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (sp->components[k].cost == SPC_LINEAR) {
return SPC_LINEAR;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* wenn keine Fixkosten, Typ <20>bernehmen */
if (sp->components[k].cost != SPC_FIX) {
costtyp = sp->components[k].cost;
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
return costtyp;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* durch Komponenten und cast_level begrenzter maximal m<>glicher
* Level
* Da die Funktion nicht alle Komponenten durchprobiert sondern beim
* ersten Fehler abbricht, muss die Fehlermeldung sp<EFBFBD>ter mit cancast()
* generiert werden.
* */
2011-03-07 08:02:35 +01:00
int eff_spelllevel(unit * u, const spell * sp, int cast_level, int range)
2010-08-08 10:06:34 +02:00
{
const resource_type *r_aura = get_resourcetype(R_AURA);
int k, maxlevel, needplevel;
int costtyp = SPC_FIX;
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
for (k = 0; sp->components[k].type; k++) {
if (cast_level == 0)
return 0;
if (sp->components[k].amount > 0) {
/* Die Kosten f<>r Aura sind auch von der Zahl der bereits
* gezauberten Spr<EFBFBD>che abh<EFBFBD>ngig */
if (sp->components[k].type == r_aura) {
needplevel = spellcost(u, sp) * range;
}
else {
needplevel = sp->components[k].amount * range;
}
maxlevel =
get_pooled(u, sp->components[k].type, GET_DEFAULT,
needplevel * cast_level) / needplevel;
/* sind die Kosten fix, so muss die Komponente nur einmal vorhanden
* sein und der cast_level <EFBFBD>ndert sich nicht */
if (sp->components[k].cost == SPC_FIX) {
if (maxlevel < 1)
cast_level = 0;
/* ansonsten wird das Minimum aus maximal m<>glicher Stufe und der
* gew<EFBFBD>nschten gebildet */
}
else if (sp->components[k].cost == SPC_LEVEL) {
costtyp = SPC_LEVEL;
cast_level = _min(cast_level, maxlevel);
/* bei Typ Linear m<>ssen die Kosten in H<>he der Stufe vorhanden
* sein, ansonsten schl<EFBFBD>gt der Spruch fehl */
}
else if (sp->components[k].cost == SPC_LINEAR) {
costtyp = SPC_LINEAR;
if (maxlevel < cast_level)
cast_level = 0;
}
}
}
/* Ein Spruch mit Fixkosten wird immer mit der Stufe des Spruchs und
* nicht auf der Stufe des Magiers gezaubert */
if (costtyp == SPC_FIX) {
spellbook * spells = unit_get_spellbook(u);
if (spells) {
spellbook_entry * sbe = spellbook_get(spells, sp);
if (sbe) {
return _min(cast_level, sbe->level);
}
}
log_error("spell %s is not in the spellbook for %s\n", sp->sname, unitname(u));
}
return cast_level;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Die Spruchgrundkosten werden mit der Entfernung (Farcasting)
* multipliziert, wobei die Aurakosten ein Sonderfall sind, da sie sich
* auch durch die Menge der bereits gezauberten Spr<EFBFBD>che erh<EFBFBD>ht.
* Je nach Kostenart werden dann die Komponenten noch mit cast_level
* multipliziert.
*/
2011-03-07 08:02:35 +01:00
void pay_spell(unit * u, const spell * sp, int cast_level, int range)
2010-08-08 10:06:34 +02:00
{
const resource_type *r_aura = get_resourcetype(R_AURA);
int k;
int resuse;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
assert(cast_level > 0);
for (k = 0; sp->components[k].type; k++) {
if (sp->components[k].type == r_aura) {
resuse = spellcost(u, sp) * range;
}
else {
resuse = sp->components[k].amount * range;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (sp->components[k].cost == SPC_LINEAR
|| sp->components[k].cost == SPC_LEVEL) {
resuse *= cast_level;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
use_pooled(u, sp->components[k].type, GET_DEFAULT, resuse);
}
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Ein Magier kennt den Spruch und kann sich die Beschreibung anzeigen
* lassen, wenn diese in seiner Spruchliste steht. Zaubern muss er ihn
* aber dann immer noch nicht k<EFBFBD>nnen, vieleicht ist seine Stufe derzeit
* nicht ausreichend oder die Komponenten fehlen.
*/
bool knowsspell(const region * r, const unit * u, const spell * sp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
/* Ist <20>berhaupt ein g<>ltiger Spruch angegeben? */
if (!sp || sp->id == 0) {
return false;
}
/* steht der Spruch in der Spruchliste? */
return u_hasspell(u, sp) != 0;
2010-08-08 10:06:34 +02:00
}
/* Um einen Spruch zu beherrschen, muss der Magier die Stufe des
* Spruchs besitzen, nicht nur wissen, das es ihn gibt (also den Spruch
* in seiner Spruchliste haben).
* Kosten f<EFBFBD>r einen Spruch k<EFBFBD>nnen Magiepunkte, Silber, Kraeuter
* und sonstige Gegenstaende sein.
*/
bool
2010-08-08 10:06:34 +02:00
cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
{
const resource_type *r_aura = get_resourcetype(R_AURA);
int k;
int itemanz;
resource *reslist = NULL;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (!knowsspell(u->region, u, sp)) {
/* Diesen Zauber kennt die Einheit nicht */
cmistake(u, ord, 173, MSG_MAGIC);
return false;
}
/* reicht die Stufe aus? */
if (eff_skill(u, SK_MAGIC, u->region) < level) {
/* die Einheit ist nicht erfahren genug f<>r diesen Zauber */
cmistake(u, ord, 169, MSG_MAGIC);
return false;
}
for (k = 0; sp->components[k].type; ++k) {
if (sp->components[k].amount > 0) {
const resource_type *rtype = sp->components[k].type;
int itemhave;
/* Die Kosten f<>r Aura sind auch von der Zahl der bereits
* gezauberten Spr<EFBFBD>che abh<EFBFBD>ngig */
if (rtype == r_aura) {
itemanz = spellcost(u, sp) * range;
}
else {
itemanz = sp->components[k].amount * range;
}
/* sind die Kosten stufenabh<62>ngig, so muss itemanz noch mit dem
* level multipliziert werden */
switch (sp->components[k].cost) {
case SPC_LEVEL:
case SPC_LINEAR:
itemanz *= level;
break;
case SPC_FIX:
default:
break;
}
itemhave = get_pooled(u, rtype, GET_DEFAULT, itemanz);
if (itemhave < itemanz) {
resource *res = malloc(sizeof(resource));
res->number = itemanz - itemhave;
res->type = rtype;
res->next = reslist;
reslist = res;
}
}
}
if (reslist != NULL) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "missing_components_list",
"list", reslist));
return false;
}
return true;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* generische Spruchstaerke,
*
* setzt sich derzeit aus der Stufe des Magiers, Magieturmeffekt,
* Spruchitems und Antimagiefeldern zusammen. Es koennen noch die
* Stufe des Spruchs und Magiekosten mit einfliessen.
*
* Die effektive Spruchst<EFBFBD>rke und ihre Auswirkungen werden in der
* Spruchfunktionsroutine ermittelt.
*/
float
spellpower(region * r, unit * u, const spell * sp, int cast_level, struct order *ord)
2010-08-08 10:06:34 +02:00
{
curse *c;
float force = (float)cast_level;
int elf_power;
const struct resource_type *rtype;
2010-08-08 10:06:34 +02:00
if (sp == NULL) {
return 0;
2014-08-08 01:03:46 +02:00
}
else {
/* Bonus durch Magieturm und gesegneten Steinkreis */
struct building *b = inside_building(u);
const struct building_type *btype = b ? b->type : NULL;
if (btype && btype->flags & BTF_MAGIC) ++force;
}
2010-08-08 10:06:34 +02:00
elf_power = get_param_int(global.parameters, "rules.magic.elfpower", 0);
if (elf_power && u_race(u) == get_race(RC_ELF) && r_isforest(r)) {
++force;
}
rtype = rt_find("rop");
if (rtype && i_get(u->items, rtype->itype) > 0) {
++force;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Antimagie in der Zielregion */
c = get_curse(r->attribs, ct_find("antimagiczone"));
if (curse_active(c)) {
unit *mage = c->magician;
force -= curse_geteffect(c);
curse_changevigour(&r->attribs, c, (float)-cast_level);
cmistake(u, ord, 185, MSG_MAGIC);
if (mage != NULL && mage->faction != NULL) {
if (force > 0) {
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
"self mage region", mage, u, r));
}
else {
ADDMSG(&mage->faction->msgs, msg_message("block_spell",
"self mage region", mage, u, r));
}
}
}
/* Patzerfluch-Effekt: */
c = get_curse(r->attribs, ct_find("fumble"));
if (curse_active(c)) {
unit *mage = c->magician;
force -= curse_geteffect(c);
curse_changevigour(&u->attribs, c, -1);
cmistake(u, ord, 185, MSG_MAGIC);
if (mage != NULL && mage->faction != NULL) {
if (force > 0) {
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
"self mage region", mage, u, r));
}
else {
ADDMSG(&mage->faction->msgs, msg_message("block_spell",
"self mage region", mage, u, r));
}
}
}
force = force * MagicPower();
return _max(force, 0);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* farcasting() == 1 -> gleiche Region, da man mit Null nicht vern<72>nfigt
* rechnen kann */
2011-03-07 08:02:35 +01:00
static int farcasting(unit * magician, region * r)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int dist;
int mult;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (!r) {
return INT_MAX;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
dist = koor_distance(r->x, r->y, magician->region->x, magician->region->y);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (dist > 24)
return INT_MAX;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
mult = 1 << dist;
if (dist > 1) {
if (!path_exists(magician->region, r, dist * 2, allowed_fly))
mult = INT_MAX;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return mult;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Antimagie - Magieresistenz */
/* ------------------------------------------------------------- */
/* allgemeine Magieresistenz einer Einheit,
* reduziert magischen Schaden */
2011-03-07 08:02:35 +01:00
double magic_resistance(unit * target)
2010-08-08 10:06:34 +02:00
{
attrib *a;
curse *c;
int n;
2014-08-08 01:03:46 +02:00
const curse_type * ct_goodresist = 0, *ct_badresist = 0;
const resource_type *rtype;
double probability = u_race(target)->magres;
assert(target->number > 0);
/* Magier haben einen Resistenzbonus vom Magietalent * 5% */
probability += effskill(target, SK_MAGIC) * 0.05;
/* Auswirkungen von Zaubern auf der Einheit */
c = get_curse(target->attribs, ct_find("magicresistance"));
if (c) {
probability += 0.01 * curse_geteffect(c) * get_cursedmen(target, c);
}
2010-08-08 10:06:34 +02:00
/* Unicorn +10 */
rtype = get_resourcetype(R_UNICORN);
n = i_get(target->items, rtype->itype);
if (n) {
probability += n * 0.1 / target->number;
}
2014-08-08 01:03:46 +02:00
/* Auswirkungen von Zaubern auf der Region */
a = a_find(target->region->attribs, &at_curse);
if (a) {
ct_badresist = ct_find("badmagicresistancezone");
ct_goodresist = ct_find("goodmagicresistancezone");
}
while (a && a->type == &at_curse) {
2014-08-08 01:03:46 +02:00
curse *c = (curse *)a->data.v;
unit *mage = c->magician;
2014-08-08 01:03:46 +02:00
if (mage != NULL) {
if (ct_goodresist && c->type == ct_goodresist) {
if (alliedunit(mage, target->faction, HELP_GUARD)) {
probability += curse_geteffect(c) * 0.01;
ct_goodresist = 0; /* only one effect per region */
}
2014-08-08 01:03:46 +02:00
}
else if (ct_badresist && c->type == ct_badresist) {
if (!alliedunit(mage, target->faction, HELP_GUARD)) {
probability -= curse_geteffect(c) * 0.01;
ct_badresist = 0; /* only one effect per region */
}
}
2010-08-08 10:06:34 +02:00
}
a = a->next;
2010-08-08 10:06:34 +02:00
}
/* Bonus durch Artefakte */
/* TODO (noch gibs keine) */
2014-08-08 01:03:46 +02:00
/* Bonus durch Geb<65>ude */
{
struct building *b = inside_building(target);
const struct building_type *btype = b ? b->type : NULL;
2014-08-08 01:03:46 +02:00
/* gesegneter Steinkreis gibt 30% dazu */
if (btype)
probability += btype->magresbonus * 0.01;
}
return probability;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Pr<50>ft, ob das Objekt dem Zauber widerstehen kann.
* Objekte k<EFBFBD>nnen Regionen, Units, Geb<EFBFBD>ude oder Schiffe sein.
* TYP_UNIT:
* Das h<EFBFBD>chste Talent des Ziels ist sein 'Magieresistenz-Talent', Magier
* bekommen einen Bonus. Grundchance ist 50%, f<EFBFBD>r jede Stufe
* Unterschied gibt es 5%, minimalchance ist 5% f<EFBFBD>r jeden (5-95%)
* Scheitert der Spruch an der Magieresistenz, so gibt die Funktion
* true zur<EFBFBD>ck
*/
bool
2011-03-07 08:02:35 +01:00
target_resists_magic(unit * magician, void *obj, int objtyp, int t_bonus)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
double probability = 0.0;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (magician == NULL)
return true;
if (obj == NULL)
return true;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
switch (objtyp) {
case TYP_UNIT:
{
int at, pa = 0;
skill *sv;
unit *u = (unit *)obj;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
at = effskill(magician, SK_MAGIC);
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
int sk = effskill(u, sv->id);
if (pa < sk)
pa = sk;
}
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
/* Contest */
probability = 0.05 * (10 + pa - at);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
probability += magic_resistance((unit *)obj);
break;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
case TYP_REGION:
/* Bonus durch Zauber */
probability +=
0.01 * get_curseeffect(((region *)obj)->attribs, C_RESIST_MAGIC, 0);
break;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
case TYP_BUILDING:
/* Bonus durch Zauber */
probability +=
0.01 * get_curseeffect(((building *)obj)->attribs, C_RESIST_MAGIC, 0);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Bonus durch Typ */
probability += 0.01 * ((building *)obj)->type->magres;
2011-03-08 08:44:20 +01:00
2014-08-08 01:03:46 +02:00
break;
2011-03-08 08:44:20 +01:00
2014-08-08 01:03:46 +02:00
case TYP_SHIP:
/* Bonus durch Zauber */
probability +=
0.01 * get_curseeffect(((ship *)obj)->attribs, C_RESIST_MAGIC, 0);
break;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
probability = _max(0.02, probability + t_bonus * 0.01);
probability = _min(0.98, probability);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* gibt true, wenn die Zufallszahl kleiner als die chance ist und
* false, wenn sie gleich oder gr<EFBFBD><EFBFBD>er ist, dh je gr<EFBFBD><EFBFBD>er die
* Magieresistenz (chance) desto eher gibt die Funktion true zur<EFBFBD>ck */
return chance(probability);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
bool is_magic_resistant(unit * magician, unit * target, int resist_bonus)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
return (bool)target_resists_magic(magician, target, TYP_UNIT,
resist_bonus);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Patzer */
/* ------------------------------------------------------------- */
/* allgemeine Zauberpatzer:
* Treten auf, wenn die Stufe des Magieres zu niedrig ist.
* Abhaengig von Staerke des Spruchs.
*
* Die Warscheinlichkeit reicht von 20% (beherrscht den Spruch gerade
* eben) bis zu etwa 1% bei doppelt so gut wie notwendig
*/
bool fumble(region * r, unit * u, const spell * sp, int cast_grade)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
/* X ergibt Zahl zwischen 1 und 0, je kleiner, desto besser der Magier.
* 0,5*40-20=0, dh wenn der Magier doppelt so gut ist, wie der Spruch
* ben<EFBFBD>tigt, gelingt er immer, ist er gleich gut, gelingt der Spruch mit
* 20% Warscheinlichkeit nicht
* */
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
int rnd = 0;
double x = (double)cast_grade / (double)eff_skill(u, SK_MAGIC, r);
int fumble_chance = (int)(((double)x * 40.0) - 20.0);
struct building *b = inside_building(u);
const struct building_type *btype = b ? b->type : NULL;
int fumble_enabled = get_param_int(global.parameters, "magic.fumble.enable", 1);
sc_mage * mage;
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
if (!fumble_enabled) {
return false;
}
if (btype)
fumble_chance -= btype->fumblebonus;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */
mage = get_mage(u);
if (mage->magietyp == M_DRAIG) {
fumble_chance += CHAOSPATZERCHANCE;
}
if (is_cursed(u->attribs, C_MBOOST, 0)) {
fumble_chance += CHAOSPATZERCHANCE;
}
if (is_cursed(u->attribs, C_FUMBLE, 0)) {
fumble_chance += CHAOSPATZERCHANCE;
}
/* wenn die Chance kleiner als 0 ist, k<>nnen wir gleich false
* zur<EFBFBD>ckgeben */
if (fumble_chance <= 0) {
return false;
}
rnd = rng_int() % 100;
return (rnd <= fumble_chance);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Dummy-Zauberpatzer, Platzhalter f<>r speziel auf die Spr<70>che
* zugeschnittene Patzer */
static void fumble_default(castorder * co)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *mage = co->magician.u;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
cmistake(mage, co->order, 180, MSG_MAGIC);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* Die normalen Spruchkosten m<>ssen immer bezahlt werden, hier noch
* alle weiteren Folgen eines Patzers
*/
2011-03-07 08:02:35 +01:00
static void do_fumble(castorder * co)
2010-08-08 10:06:34 +02:00
{
curse *c;
region *r = co_get_region(co);
unit *u = co->magician.u;
const spell *sp = co->sp;
int level = co->level;
int duration;
float effect;
2014-08-08 01:03:46 +02:00
ADDMSG(&u->faction->msgs,
msg_message("patzer", "unit region spell", u, r, sp));
switch (rng_int() % 10) {
case 0:
/* wenn vorhanden spezieller Patzer, ansonsten nix */
if (sp->fumble) {
sp->fumble(co);
2014-08-08 01:03:46 +02:00
}
else {
fumble_default(co);
}
break;
2014-08-08 01:03:46 +02:00
case 1: /* toad */
2014-08-08 01:03:46 +02:00
{
/* one or two things will happen: the toad changes her race back,
* and may or may not get toadslime.
* The list of things to happen are attached to a timeout
* trigger and that's added to the triggerlit of the mage gone toad.
*/
trigger *trestore = trigger_changerace(u, u_race(u), u->irace);
if (chance(0.7)) {
const resource_type *rtype = rt_find("toadslime");
if (rtype) {
t_add(&trestore, trigger_giveitem(u, rtype->itype, 1));
}
}
2014-08-08 01:03:46 +02:00
duration = rng_int() % level / 2;
if (duration < 2) duration = 2;
add_trigger(&u->attribs, "timer", trigger_timeout(duration, trestore));
u_setrace(u, get_race(RC_TOAD));
u->irace = NULL;
ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", u, r, sp));
break;
}
/* fall-through is intentional! */
2014-08-08 01:03:46 +02:00
case 2:
/* temporary skill loss */
duration = _max(rng_int() % level / 2, 2);
2014-08-08 01:03:46 +02:00
effect = -(float)level / 2;
c = create_curse(u, &u->attribs, ct_find("skillmod"), (float)level,
2014-08-08 01:03:46 +02:00
duration, effect, 1);
c->data.i = SK_MAGIC;
ADDMSG(&u->faction->msgs, msg_message("patzer2", "unit region", u, r));
break;
case 3:
case 4:
/* Spruch schl<68>gt fehl, alle Magiepunkte weg */
set_spellpoints(u, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer3", "unit region spell",
2014-08-08 01:03:46 +02:00
u, r, sp));
break;
2014-08-08 01:03:46 +02:00
case 5:
case 6:
/* Spruch gelingt, aber alle Magiepunkte weg */
co->level = sp->cast(co);
set_spellpoints(u, 0);
ADDMSG(&u->faction->msgs, msg_message("patzer4", "unit region spell",
2014-08-08 01:03:46 +02:00
u, r, sp));
break;
2014-08-08 01:03:46 +02:00
case 7:
case 8:
case 9:
default:
/* Spruch gelingt, alle nachfolgenden Spr<70>che werden 2^4 so teuer */
co->level = sp->cast(co);
ADDMSG(&u->faction->msgs, msg_message("patzer5", "unit region spell",
2014-08-08 01:03:46 +02:00
u, r, sp));
countspells(u, 3);
2010-08-08 10:06:34 +02:00
}
}
/* ------------------------------------------------------------- */
/* Regeneration von Aura */
/* ------------------------------------------------------------- */
/* Ein Magier regeneriert pro Woche W(Stufe^1.5/2+1), mindestens 1
* Zwerge nur die H<EFBFBD>lfte
*/
2011-03-07 08:02:35 +01:00
static double regeneration(unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int sk;
double aura, d;
double potenz = 1.5;
double divisor = 2.0;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
sk = effskill(u, SK_MAGIC);
/* Rassenbonus/-malus */
d = pow(sk, potenz) * u_race(u)->regaura / divisor;
d++;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Einfluss von Artefakten */
/* TODO (noch gibs keine) */
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* W<>rfeln */
aura = (rng_double() * d + rng_double() * d) / 2 + 1;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
aura *= MagicRegeneration();
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return aura;
2010-08-08 10:06:34 +02:00
}
void regenerate_aura(void)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
region *r;
unit *u;
int aura, auramax;
double reg_aura;
int regen;
double mod;
int regen_enabled = get_param_int(global.parameters, "magic.regeneration.enable", 1);
if (!regen_enabled) return;
for (r = regions; r; r = r->next) {
for (u = r->units; u; u = u->next) {
if (u->number && is_mage(u)) {
aura = get_spellpoints(u);
auramax = max_spellpoints(r, u);
if (aura < auramax) {
struct building *b = inside_building(u);
const struct building_type *btype = b ? b->type : NULL;
reg_aura = regeneration(u);
/* Magierturm erh<72>ht die Regeneration um 75% */
/* Steinkreis erh<72>ht die Regeneration um 50% */
if (btype)
reg_aura *= btype->auraregen;
/* Bonus/Malus durch Zauber */
mod = get_curseeffect(u->attribs, C_AURA, 0);
if (mod > 0) {
reg_aura = (reg_aura * mod) / 100.0;
}
/* Einfluss von Artefakten */
/* TODO (noch gibs keine) */
/* maximal Differenz bis Maximale-Aura regenerieren
* mindestens 1 Aura pro Monat */
regen = (int)reg_aura;
reg_aura -= regen;
if (chance(reg_aura))
++regen;
regen = _max(1, regen);
regen = _min((auramax - aura), regen);
aura += regen;
ADDMSG(&u->faction->msgs, msg_message("regenaura",
"unit region amount", u, r, regen));
}
set_spellpoints(u, _min(aura, auramax));
}
}
}
2010-08-08 10:06:34 +02:00
}
static bool
2011-03-07 08:02:35 +01:00
verify_ship(region * r, unit * mage, const spell * sp, spllprm * spobj,
2014-08-08 01:03:46 +02:00
order * ord)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
ship *sh = findship(spobj->data.i);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (sh != NULL && sh->region != r && (sp->sptyp & SEARCHLOCAL)) {
/* Burg muss in gleicher Region sein */
sh = NULL;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (sh == NULL) {
/* Burg nicht gefunden */
spobj->flag = TARGET_NOTFOUND;
ADDMSG(&mage->faction->msgs, msg_message("spellshipnotfound",
"unit region command id", mage, mage->region, ord, spobj->data.i));
return false;
}
spobj->flag = 0;
spobj->data.sh = sh;
return true;
2010-08-08 10:06:34 +02:00
}
static bool
2011-03-07 08:02:35 +01:00
verify_building(region * r, unit * mage, const spell * sp, spllprm * spobj,
2014-08-08 01:03:46 +02:00
order * ord)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
building *b = findbuilding(spobj->data.i);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (b != NULL && b->region != r && (sp->sptyp & SEARCHLOCAL)) {
/* Burg muss in gleicher Region sein */
b = NULL;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (b == NULL) {
/* Burg nicht gefunden */
spobj->flag = TARGET_NOTFOUND;
ADDMSG(&mage->faction->msgs, msg_message("spellbuildingnotfound",
"unit region command id", mage, mage->region, ord, spobj->data.i));
return false;
}
spobj->flag = 0;
spobj->data.b = b;
return true;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
message *msg_unitnotfound(const struct unit * mage, struct order * ord,
2014-08-08 01:03:46 +02:00
const struct spllprm * spobj)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
/* Einheit nicht gefunden */
char tbuf[20];
const char *uid = 0;
2011-03-07 08:02:35 +01:00
if (spobj->typ == SPP_TEMP) {
2014-08-08 01:03:46 +02:00
sprintf(tbuf, "%s %s", LOC(mage->faction->locale,
parameters[P_TEMP]), itoa36(spobj->data.i));
uid = tbuf;
}
else if (spobj->typ == SPP_UNIT) {
uid = itoa36(spobj->data.i);
}
assert(uid);
2014-08-08 01:03:46 +02:00
return msg_message("unitnotfound_id",
"unit region command id", mage, mage->region, ord, uid);
2010-08-08 10:06:34 +02:00
}
static bool
2011-03-07 08:02:35 +01:00
verify_unit(region * r, unit * mage, const spell * sp, spllprm * spobj,
2014-08-08 01:03:46 +02:00
order * ord)
{
unit *u = NULL;
switch (spobj->typ) {
case SPP_UNIT:
u = findunit(spobj->data.i);
break;
case SPP_TEMP:
u = findnewunit(r, mage->faction, spobj->data.i);
if (u == NULL)
u = findnewunit(mage->region, mage->faction, spobj->data.i);
break;
default:
assert(!"shouldn't happen, this");
}
if (u != NULL && (sp->sptyp & SEARCHLOCAL)) {
if (u->region != r)
u = NULL;
else if (sp->sptyp & TESTCANSEE) {
if (!cansee(mage->faction, r, u, 0) && !ucontact(u, mage)) {
u = NULL;
}
}
}
if (u == NULL) {
/* Einheit nicht gefunden */
spobj->flag = TARGET_NOTFOUND;
ADDMSG(&mage->faction->msgs, msg_unitnotfound(mage, ord, spobj));
return false;
}
/* Einheit wurde gefunden, pointer setzen */
spobj->flag = 0;
spobj->data.u = u;
return true;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Zuerst wird versucht alle noch nicht gefundenen Objekte zu finden
* oder zu pr<EFBFBD>fen, ob das gefundene Objekt wirklich h<EFBFBD>tte gefunden
* werden d<EFBFBD>rfen (nicht alle Zauber wirken global). Dabei z<EFBFBD>hlen wir die
* Misserfolge (failed).
* Dann folgen die Tests der gefundenen Objekte auf Magieresistenz und
* Sichtbarkeit. Dabei z<EFBFBD>hlen wir die magieresistenten (resists)
* Objekte. Alle anderen werten wir als Erfolge (success) */
/* gibt bei Misserfolg 0 zur<75>ck, bei Magieresistenz zumindeste eines
* Objektes 1 und bei Erfolg auf ganzer Linie 2 */
static void
2011-03-07 08:02:35 +01:00
verify_targets(castorder * co, int *invalid, int *resist, int *success)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *mage = co->magician.u;
const spell *sp = co->sp;
region *target_r = co_get_region(co);
spellparameter *sa = co->par;
int i;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
*invalid = 0;
*resist = 0;
*success = 0;
if (sa && sa->length) {
/* zuerst versuchen wir vorher nicht gefundene Objekte zu finden.
* Wurde ein Objekt durch globalsuche gefunden, obwohl der Zauber
* gar nicht global h<EFBFBD>tte suchen d<EFBFBD>rften, setzen wir das Objekt
* zur<EFBFBD>ck. */
for (i = 0; i < sa->length; i++) {
spllprm *spobj = sa->param[i];
switch (spobj->typ) {
case SPP_TEMP:
case SPP_UNIT:
if (!verify_unit(target_r, mage, sp, spobj, co->order))
++ * invalid;
break;
case SPP_BUILDING:
if (!verify_building(target_r, mage, sp, spobj, co->order))
++ * invalid;
break;
case SPP_SHIP:
if (!verify_ship(target_r, mage, sp, spobj, co->order))
++ * invalid;
break;
default:
break;
}
2011-03-08 08:44:20 +01:00
}
2014-08-08 01:03:46 +02:00
/* Nun folgen die Tests auf cansee und Magieresistenz */
for (i = 0; i < sa->length; i++) {
spllprm *spobj = sa->param[i];
unit *u;
building *b;
ship *sh;
region *tr;
if (spobj->flag == TARGET_NOTFOUND)
continue;
switch (spobj->typ) {
case SPP_TEMP:
case SPP_UNIT:
u = spobj->data.u;
if ((sp->sptyp & TESTRESISTANCE)
&& target_resists_magic(mage, u, TYP_UNIT, 0)) {
/* Fehlermeldung */
spobj->data.i = u->no;
spobj->flag = TARGET_RESISTS;
++*resist;
ADDMSG(&mage->faction->msgs, msg_message("spellunitresists",
"unit region command target", mage, mage->region, co->order, u));
break;
}
/* TODO: Test auf Parteieigenschaft Magieresistsenz */
++*success;
break;
case SPP_BUILDING:
b = spobj->data.b;
if ((sp->sptyp & TESTRESISTANCE)
&& target_resists_magic(mage, b, TYP_BUILDING, 0)) { /* Fehlermeldung */
spobj->data.i = b->no;
spobj->flag = TARGET_RESISTS;
++*resist;
ADDMSG(&mage->faction->msgs, msg_message("spellbuildingresists",
"unit region command id",
mage, mage->region, co->order, spobj->data.i));
break;
}
++*success;
break;
case SPP_SHIP:
sh = spobj->data.sh;
if ((sp->sptyp & TESTRESISTANCE)
&& target_resists_magic(mage, sh, TYP_SHIP, 0)) { /* Fehlermeldung */
spobj->data.i = sh->no;
spobj->flag = TARGET_RESISTS;
++*resist;
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
"spellshipresists", "ship", sh));
break;
}
++*success;
break;
case SPP_REGION:
/* haben wir ein Regionsobjekt, dann wird auch dieses und
nicht target_r <EFBFBD>berpr<EFBFBD>ft. */
tr = spobj->data.r;
if ((sp->sptyp & TESTRESISTANCE)
&& target_resists_magic(mage, tr, TYP_REGION, 0)) { /* Fehlermeldung */
spobj->flag = TARGET_RESISTS;
++*resist;
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
"unit region command", mage, mage->region, co->order));
break;
}
++*success;
break;
case SPP_INT:
case SPP_STRING:
++*success;
break;
default:
break;
}
}
}
else {
/* der Zauber hat keine expliziten Parameter/Ziele, es kann sich
* aber um einen Regionszauber handeln. Wenn notwendig hier die
* Magieresistenz der Region pr<EFBFBD>fen. */
if ((sp->sptyp & REGIONSPELL)) {
/* Zielobjekt Region anlegen */
spllprm *spobj = (spllprm *)malloc(sizeof(spllprm));
spobj->flag = 0;
spobj->typ = SPP_REGION;
spobj->data.r = target_r;
sa = calloc(1, sizeof(spellparameter));
sa->length = 1;
sa->param = calloc(sa->length, sizeof(spllprm *));
sa->param[0] = spobj;
co->par = sa;
if ((sp->sptyp & TESTRESISTANCE)) {
if (target_resists_magic(mage, target_r, TYP_REGION, 0)) {
/* Fehlermeldung */
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
"unit region command", mage, mage->region, co->order));
spobj->flag = TARGET_RESISTS;
++*resist;
}
else {
++*success;
}
}
else {
++*success;
}
}
else {
++*success;
}
}
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Hilfsstrukturen f<>r ZAUBERE */
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static void free_spellparameter(spellparameter * pa)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int i;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Elemente free'en */
for (i = 0; i < pa->length; i++) {
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
switch (pa->param[i]->typ) {
case SPP_STRING:
free(pa->param[i]->data.s);
break;
default:
break;
}
free(pa->param[i]);
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
if (pa->param)
free(pa->param);
/* struct free'en */
free(pa);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int addparam_string(const char *const param[], spllprm ** spobjp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
assert(param[0]);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
spobj->flag = 0;
spobj->typ = SPP_STRING;
spobj->data.xs = _strdup(param[0]);
return 1;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int addparam_int(const char *const param[], spllprm ** spobjp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
assert(param[0]);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
spobj->flag = 0;
spobj->typ = SPP_INT;
spobj->data.i = atoi((char *)param[0]);
return 1;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int addparam_ship(const char *const param[], spllprm ** spobjp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
int id = atoi36((const char *)param[0]);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
spobj->flag = 0;
spobj->typ = SPP_SHIP;
spobj->data.i = id;
return 1;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int addparam_building(const char *const param[], spllprm ** spobjp)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
int id = atoi36((const char *)param[0]);
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
spobj->flag = 0;
spobj->typ = SPP_BUILDING;
spobj->data.i = id;
return 1;
2010-08-08 10:06:34 +02:00
}
static int
2011-03-07 08:02:35 +01:00
addparam_region(const char *const param[], spllprm ** spobjp, const unit * u,
2014-08-08 01:03:46 +02:00
order * ord, plane * pl)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
assert(param[0]);
if (param[1] == 0) {
/* Fehler: Zielregion vergessen */
cmistake(u, ord, 194, MSG_MAGIC);
return -1;
}
else {
int tx = atoi((const char *)param[0]), ty = atoi((const char *)param[1]);
int x = rel_to_abs(pl, u->faction, tx, 0);
int y = rel_to_abs(pl, u->faction, ty, 1);
region *rt;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
pnormalize(&x, &y, pl);
rt = findregion(x, y);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (rt != NULL) {
spllprm *spobj = *spobjp = (spllprm *)malloc(sizeof(spllprm));
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
spobj->flag = 0;
spobj->typ = SPP_REGION;
spobj->data.r = rt;
}
else {
/* Fehler: Zielregion vergessen */
cmistake(u, ord, 194, MSG_MAGIC);
return -1;
}
return 2;
2010-08-08 10:06:34 +02:00
}
}
static int
2011-03-07 08:02:35 +01:00
addparam_unit(const char *const param[], spllprm ** spobjp, const unit * u,
2014-08-08 01:03:46 +02:00
order * ord)
{
spllprm *spobj;
int i = 0;
sppobj_t otype = SPP_UNIT;
*spobjp = NULL;
if (isparam(param[0], u->faction->locale, P_TEMP)) {
if (param[1] == NULL) {
/* Fehler: Ziel vergessen */
cmistake(u, ord, 203, MSG_MAGIC);
return -1;
}
++i;
otype = SPP_TEMP;
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
spobj = *spobjp = malloc(sizeof(spllprm));
spobj->flag = 0;
spobj->typ = otype;
spobj->data.i = atoi36((const char *)param[i]);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return i + 1;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static spellparameter *add_spellparameter(region * target_r, unit * u,
2014-08-08 01:03:46 +02:00
const char *syntax, const char *const param[], int size, struct order *ord)
{
bool fail = false;
int i = 0;
int p = 0;
const char *c;
spellparameter *par;
int minlen = 0;
for (c = syntax; *c != 0; ++c) {
/* this makes sure that:
* minlen("kc?") = 0
* minlen("kc+") = 1
* minlen("cccc+c?") = 4
*/
if (*c == '?')
--minlen;
else if (*c != '+' && *c != 'k')
++minlen;
}
c = syntax;
/* mindestens ein Ziel (Ziellose Zauber werden nicht
* geparst) */
if (minlen && size == 0) {
/* Fehler: Ziel vergessen */
cmistake(u, ord, 203, MSG_MAGIC);
return 0;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
par = malloc(sizeof(spellparameter));
par->length = size;
if (!size) {
par->param = NULL;
return par;
}
par->param = malloc(size * sizeof(spllprm *));
while (!fail && *c && i < size && param[i] != NULL) {
spllprm *spobj = NULL;
int j = -1;
switch (*c) {
case '?':
/* tja. das sollte moeglichst nur am Ende passieren,
* weil sonst die kacke dampft. */
j = 0;
++c;
assert(*c == 0);
break;
case '+':
/* das vorhergehende Element kommt ein oder mehrmals vor, wir
* springen zum key zur<EFBFBD>ck */
j = 0;
--c;
break;
case 'u':
/* Parameter ist eine Einheit, evtl. TEMP */
j = addparam_unit(param + i, &spobj, u, ord);
++c;
break;
case 'r':
/* Parameter sind zwei Regionskoordinaten */
/* this silly thing only works in the normal plane! */
j = addparam_region(param + i, &spobj, u, ord, get_normalplane());
++c;
break;
case 'b':
/* Parameter ist eine Burgnummer */
j = addparam_building(param + i, &spobj);
++c;
break;
case 's':
j = addparam_ship(param + i, &spobj);
++c;
break;
case 'c':
/* Text, wird im Spruch ausgewertet */
j = addparam_string(param + i, &spobj);
++c;
break;
case 'i': /* Zahl */
j = addparam_int(param + i, &spobj);
++c;
break;
case 'k':
++c;
switch (findparam_ex(param[i++], u->faction->locale)) {
case P_REGION:
spobj = (spllprm *)malloc(sizeof(spllprm));
spobj->flag = 0;
spobj->typ = SPP_REGION;
spobj->data.r = target_r;
j = 0;
++c;
break;
case P_UNIT:
if (i < size) {
j = addparam_unit(param + i, &spobj, u, ord);
++c;
}
break;
case P_BUILDING:
case P_GEBAEUDE:
if (i < size) {
j = addparam_building(param + i, &spobj);
++c;
}
break;
case P_SHIP:
if (i < size) {
j = addparam_ship(param + i, &spobj);
++c;
}
break;
default:
j = -1;
break;
}
break;
default:
j = -1;
break;
2011-03-08 08:44:20 +01:00
}
2014-08-08 01:03:46 +02:00
if (j < 0)
fail = true;
else {
if (spobj != NULL)
par->param[p++] = spobj;
i += j;
2010-08-08 10:06:34 +02:00
}
}
2014-08-08 01:03:46 +02:00
/* im Endeffekt waren es evtl. nur p parameter (wegen TEMP) */
par->length = p;
if (fail || par->length < minlen) {
cmistake(u, ord, 209, MSG_MAGIC);
free_spellparameter(par);
return NULL;
}
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return par;
2010-08-08 10:06:34 +02:00
}
struct unit * co_get_caster(const struct castorder * co) {
2014-08-08 01:03:46 +02:00
return co->_familiar ? co->_familiar : co->magician.u;
}
struct region * co_get_region(const struct castorder * co) {
2014-08-08 01:03:46 +02:00
return co->_rtarget;
}
2010-08-08 10:06:34 +02:00
castorder *create_castorder(castorder * co, unit *caster, unit * familiar, const spell * sp, region * r,
2014-08-08 01:03:46 +02:00
int lev, float force, int range, struct order * ord, spellparameter * p)
{
2014-08-08 01:03:46 +02:00
if (!co) co = (castorder*)calloc(1, sizeof(castorder));
2014-08-08 01:03:46 +02:00
co->magician.u = caster;
co->_familiar = familiar;
co->sp = sp;
co->level = lev;
co->force = force;
co->_rtarget = r ? r : (familiar ? familiar->region : (caster ? caster->region : 0));
co->distance = range;
co->order = copy_order(ord);
co->par = p;
2014-08-08 01:03:46 +02:00
return co;
}
void free_castorder(struct castorder *co)
{
2014-08-08 01:03:46 +02:00
if (co->par) free_spellparameter(co->par);
if (co->order) free_order(co->order);
}
2010-08-08 10:06:34 +02:00
/* H<>nge c-order co an die letze c-order von cll an */
2011-03-07 08:02:35 +01:00
void add_castorder(spellrank * cll, castorder * co)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
if (cll->begin == NULL) {
cll->end = &cll->begin;
}
2011-03-07 08:02:35 +01:00
2014-08-08 01:03:46 +02:00
*cll->end = co;
cll->end = &co->next;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
return;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void free_castorders(castorder * co)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
castorder *co2;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
while (co) {
co2 = co;
co = co->next;
free_castorder(co2);
free(co2);
}
return;
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/***
** at_familiarmage
**/
typedef struct familiar_data {
2014-08-08 01:03:46 +02:00
unit *mage;
unit *familiar;
2010-08-08 10:06:34 +02:00
} famililar_data;
bool is_familiar(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(u->attribs, &at_familiarmage);
return i2b(a != NULL);
2010-08-08 10:06:34 +02:00
}
static void
2011-03-07 08:02:35 +01:00
a_write_unit(const attrib * a, const void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *u = (unit *)a->data.v;
write_unit_reference(u, store);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int sm_familiar(const unit * u, const region * r, skill_t sk, int value)
{ /* skillmod */
2014-08-08 01:03:46 +02:00
if (sk == SK_MAGIC)
return value;
else {
int mod;
unit *familiar = get_familiar(u);
if (familiar == NULL) {
/* the familiar is dead */
return value;
}
mod = eff_skill(familiar, sk, r) / 2;
if (r != familiar->region) {
mod /= distance(r, familiar->region);
}
return value + mod;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
static void set_familiar(unit * mage, unit * familiar)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
/* if the skill modifier for the mage does not yet exist, add it */
attrib *a = a_find(mage->attribs, &at_skillmod);
while (a && a->type == &at_skillmod) {
skillmod_data *smd = (skillmod_data *)a->data.v;
if (smd->special == sm_familiar)
break;
a = a->next;
}
if (a == NULL) {
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
skillmod_data *smd = (skillmod_data *)an->data.v;
smd->special = sm_familiar;
smd->skill = NOSKILL;
}
a = a_find(mage->attribs, &at_familiar);
if (a == NULL) {
a = a_add(&mage->attribs, a_new(&at_familiar));
a->data.v = familiar;
}
else
assert(!a->data.v || a->data.v == familiar);
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
a = a_find(familiar->attribs, &at_familiarmage);
if (a == NULL) {
a = a_add(&familiar->attribs, a_new(&at_familiarmage));
a->data.v = mage;
}
else
assert(!a->data.v || a->data.v == mage);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
void remove_familiar(unit * mage)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(mage->attribs, &at_familiar);
attrib *an;
skillmod_data *smd;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (a != NULL) {
a_remove(&mage->attribs, a);
}
a = a_find(mage->attribs, &at_skillmod);
while (a && a->type == &at_skillmod) {
an = a->next;
smd = (skillmod_data *)a->data.v;
if (smd->special == sm_familiar)
a_remove(&mage->attribs, a);
a = an;
}
2010-08-08 10:06:34 +02:00
}
bool create_newfamiliar(unit * mage, unit * familiar)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
/* if the skill modifier for the mage does not yet exist, add it */
attrib *a;
attrib *afam = a_find(mage->attribs, &at_familiar);
attrib *amage = a_find(familiar->attribs, &at_familiarmage);
if (afam == NULL) {
afam = a_add(&mage->attribs, a_new(&at_familiar));
}
afam->data.v = familiar;
if (amage == NULL) {
amage = a_add(&familiar->attribs, a_new(&at_familiarmage));
}
amage->data.v = mage;
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
/* Wenn der Magier stirbt, dann auch der Vertraute */
add_trigger(&mage->attribs, "destroy", trigger_killunit(familiar));
/* Wenn der Vertraute stirbt, dann bekommt der Magier einen Schock */
add_trigger(&familiar->attribs, "destroy", trigger_shock(mage));
a = a_find(mage->attribs, &at_skillmod);
while (a && a->type == &at_skillmod) {
skillmod_data *smd = (skillmod_data *)a->data.v;
if (smd->special == sm_familiar)
break;
a = a->next;
}
if (a == NULL) {
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
skillmod_data *smd = (skillmod_data *)an->data.v;
smd->special = sm_familiar;
smd->skill = NOSKILL;
}
return true;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int resolve_familiar(variant data, void *addr)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *familiar;
int result = resolve_unit(data, &familiar);
if (result == 0 && familiar) {
attrib *a = a_find(familiar->attribs, &at_familiarmage);
if (a != NULL && a->data.v) {
unit *mage = (unit *)a->data.v;
set_familiar(mage, familiar);
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
*(unit **)addr = familiar;
return result;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int read_familiar(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int result =
read_reference(&a->data.v, store, read_unit_reference, resolve_familiar);
if (result == 0 && a->data.v == NULL) {
return AT_READ_FAIL;
}
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
/* clones */
2011-03-07 08:02:35 +01:00
void create_newclone(unit * mage, unit * clone)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
a = a_find(mage->attribs, &at_clone);
if (a == NULL) {
a = a_add(&mage->attribs, a_new(&at_clone));
a->data.v = clone;
}
else
assert(!a->data.v || a->data.v == clone);
/* TODO: Diese Attribute beim Tod des Klons entfernen: */
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
a = a_find(clone->attribs, &at_clonemage);
if (a == NULL) {
a = a_add(&clone->attribs, a_new(&at_clonemage));
a->data.v = mage;
}
else
assert(!a->data.v || a->data.v == mage);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Wenn der Magier stirbt, wird das in destroy_unit abgefangen.
* Kein Trigger, zu kompliziert. */
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
/* Wenn der Klon stirbt, dann bekommt der Magier einen Schock */
add_trigger(&clone->attribs, "destroy", trigger_clonedied(mage));
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void set_clone(unit * mage, unit * clone)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a;
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
a = a_find(mage->attribs, &at_clone);
if (a == NULL) {
a = a_add(&mage->attribs, a_new(&at_clone));
a->data.v = clone;
}
else
assert(!a->data.v || a->data.v == clone);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
a = a_find(clone->attribs, &at_clonemage);
if (a == NULL) {
a = a_add(&clone->attribs, a_new(&at_clonemage));
a->data.v = mage;
}
else
assert(!a->data.v || a->data.v == mage);
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
unit *has_clone(unit * mage)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(mage->attribs, &at_clone);
if (a)
return (unit *)a->data.v;
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int resolve_clone(variant data, void *addr)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *clone;
int result = resolve_unit(data, &clone);
if (result == 0 && clone) {
attrib *a = a_find(clone->attribs, &at_clonemage);
if (a != NULL && a->data.v) {
unit *mage = (unit *)a->data.v;
set_clone(mage, clone);
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
*(unit **)addr = clone;
return result;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int read_clone(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int result =
read_reference(&a->data.v, store, read_unit_reference, resolve_clone);
if (result == 0 && a->data.v == NULL) {
return AT_READ_FAIL;
}
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
/* mages */
2011-03-07 08:02:35 +01:00
static int resolve_mage(variant data, void *addr)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
unit *mage;
int result = resolve_unit(data, &mage);
if (result == 0 && mage) {
attrib *a = a_find(mage->attribs, &at_familiar);
if (a != NULL && a->data.v) {
unit *familiar = (unit *)a->data.v;
set_familiar(mage, familiar);
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
*(unit **)addr = mage;
return result;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int read_magician(attrib * a, void *owner, struct storage *store)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
int result =
read_reference(&a->data.v, store, read_unit_reference, resolve_mage);
if (result == 0 && a->data.v == NULL) {
return AT_READ_FAIL;
}
return AT_READ_OK;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static int age_unit(attrib * a)
2010-08-08 10:06:34 +02:00
/* if unit is gone or dead, remove the attribute */
{
2014-08-08 01:03:46 +02:00
unit *u = (unit *)a->data.v;
return (u != NULL && u->number > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE;
2010-08-08 10:06:34 +02:00
}
attrib_type at_familiarmage = {
2014-08-08 01:03:46 +02:00
"familiarmage",
NULL,
NULL,
age_unit,
a_write_unit,
read_magician,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
attrib_type at_familiar = {
2014-08-08 01:03:46 +02:00
"familiar",
NULL,
NULL,
age_unit,
a_write_unit,
read_familiar,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
attrib_type at_clonemage = {
2014-08-08 01:03:46 +02:00
"clonemage",
NULL,
NULL,
age_unit,
a_write_unit,
read_magician,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
attrib_type at_clone = {
2014-08-08 01:03:46 +02:00
"clone",
NULL,
NULL,
age_unit,
a_write_unit,
read_clone,
ATF_UNIQUE
2010-08-08 10:06:34 +02:00
};
2011-03-07 08:02:35 +01:00
unit *get_familiar(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(u->attribs, &at_familiar);
if (a != NULL) {
unit *u = (unit *)a->data.v;
if (u->number > 0)
return u;
}
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
unit *get_familiar_mage(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(u->attribs, &at_familiarmage);
if (a != NULL) {
unit *u = (unit *)a->data.v;
if (u->number > 0)
return u;
}
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
unit *get_clone(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(u->attribs, &at_clone);
if (a != NULL) {
unit *u = (unit *)a->data.v;
if (u->number > 0)
return u;
}
return NULL;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
unit *get_clone_mage(const unit * u)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
attrib *a = a_find(u->attribs, &at_clonemage);
if (a != NULL) {
unit *u = (unit *)a->data.v;
if (u->number > 0)
return u;
}
return NULL;
2010-08-08 10:06:34 +02:00
}
static bool is_moving_ship(const region * r, ship * sh)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
const unit *u = ship_owner(sh);
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
if (u)
switch (getkeyword(u->thisorder)) {
case K_ROUTE:
case K_MOVE:
case K_FOLLOW:
return true;
default:
return false;
2011-03-07 08:02:35 +01:00
}
2014-08-08 01:03:46 +02:00
return false;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static castorder *cast_cmd(unit * u, order * ord)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
region *r = u->region;
region *target_r = r;
int level, range;
unit *familiar = NULL;
const char *s;
spell *sp = 0;
plane *pl;
spellparameter *args = NULL;
unit * caster = u;
param_t param;
if (LongHunger(u)) {
cmistake(u, ord, 224, MSG_MAGIC);
return 0;
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
pl = rplane(r);
if (pl && fval(pl, PFL_NOMAGIC)) {
cmistake(u, ord, 269, MSG_MAGIC);
return 0;
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
level = eff_skill(u, SK_MAGIC, r);
init_order(ord);
2010-08-08 10:06:34 +02:00
s = getstrtoken();
2012-05-26 19:16:39 +02:00
param = findparam(s, u->faction->locale);
2014-08-08 01:03:46 +02:00
/* f<>r Syntax ' STUFE x REGION y z ' */
if (param == P_LEVEL) {
int p = getint();
level = _min(p, level);
if (level < 1) {
/* Fehler "Das macht wenig Sinn" */
cmistake(u, ord, 10, MSG_MAGIC);
return 0;
}
s = getstrtoken();
param = findparam(s, u->faction->locale);
}
if (param == P_REGION) {
int t_x = getint();
int t_y = getint();
plane *pl = getplane(u->region);
t_x = rel_to_abs(pl, u->faction, t_x, 0);
t_y = rel_to_abs(pl, u->faction, t_y, 1);
pnormalize(&t_x, &t_y, pl);
target_r = findregion(t_x, t_y);
if (!target_r) {
/* Fehler "Die Region konnte nicht verzaubert werden" */
ADDMSG(&u->faction->msgs, msg_message("spellregionresists",
"unit region command", u, u->region, ord));
return 0;
}
s = getstrtoken();
param = findparam(s, u->faction->locale);
}
/* f<>r Syntax ' REGION x y STUFE z '
* hier nach REGION nochmal auf STUFE pr<EFBFBD>fen */
if (param == P_LEVEL) {
int p = getint();
level = _min(p, level);
if (level < 1) {
/* Fehler "Das macht wenig Sinn" */
cmistake(u, ord, 10, MSG_MAGIC);
return 0;
}
s = getstrtoken();
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
if (!s[0] || strlen(s) == 0) {
/* Fehler "Es wurde kein Zauber angegeben" */
cmistake(u, ord, 172, MSG_MAGIC);
2010-08-08 10:06:34 +02:00
return 0;
2014-08-08 01:03:46 +02:00
}
sp = unit_getspell(u, s, u->faction->locale);
/* Vertraute k<>nnen auch Zauber sprechen, die sie selbst nicht
* k<EFBFBD>nnen. unit_getspell findet aber nur jene Spr<EFBFBD>che, die
* die Einheit beherrscht. */
if (!sp && is_familiar(u)) {
caster = get_familiar_mage(u);
if (caster) {
familiar = u;
sp = unit_getspell(caster, s, caster->faction->locale);
}
else {
/* somehow, this familiar has no mage! */
log_error("cast_cmd: familiar %s is without a mage?\n", unitname(u));
caster = u;
}
}
if (!sp) {
/* Fehler 'Spell not found' */
cmistake(u, ord, 173, MSG_MAGIC);
2010-08-08 10:06:34 +02:00
return 0;
2014-08-08 01:03:46 +02:00
}
/* um testen auf spruchnamen zu unterbinden sollte vor allen
* fehlermeldungen die anzeigen das der magier diesen Spruch
* nur in diese Situation nicht anwenden kann, noch eine
* einfache Sicherheitspr<EFBFBD>fung kommen */
if (!knowsspell(r, u, sp)) {
/* vorsicht! u kann der familiar sein */
if (!familiar) {
cmistake(u, ord, 173, MSG_MAGIC);
return 0;
}
}
if (sp->sptyp & ISCOMBATSPELL) {
/* Fehler: "Dieser Zauber ist nur im Kampf sinnvoll" */
cmistake(u, ord, 174, MSG_MAGIC);
2010-08-08 10:06:34 +02:00
return 0;
2014-08-08 01:03:46 +02:00
}
/* Auf dem Ozean Zaubern als quasi-langer Befehl k<>nnen
* normalerweise nur Meermenschen, ausgenommen explizit als
* OCEANCASTABLE deklarierte Spr<EFBFBD>che */
if (fval(r->terrain, SEA_REGION)) {
if (u_race(u) != get_race(RC_AQUARIAN)
&& !fval(u_race(u), RCF_SWIM)
&& !(sp->sptyp & OCEANCASTABLE)) {
/* Fehlermeldung */
ADDMSG(&u->faction->msgs, msg_message("spellfail_onocean",
"unit region command", u, u->region, ord));
return 0;
}
/* Auf bewegenden Schiffen kann man nur explizit als
* ONSHIPCAST deklarierte Zauber sprechen */
}
else if (u->ship) {
if (is_moving_ship(r, u->ship)) {
if (!(sp->sptyp & ONSHIPCAST)) {
/* Fehler: "Diesen Spruch kann man nicht auf einem sich
* bewegenden Schiff stehend zaubern" */
cmistake(u, ord, 175, MSG_MAGIC);
return 0;
}
}
}
/* Farcasting bei nicht farcastbaren Spr<70>chen abfangen */
range = farcasting(u, target_r);
if (range > 1) {
if (!(sp->sptyp & FARCASTING)) {
/* Fehler "Diesen Spruch kann man nicht in die Ferne
* richten" */
cmistake(u, ord, 176, MSG_MAGIC);
return 0;
}
if (range > 1024) { /* (2^10) weiter als 10 Regionen entfernt */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "spellfail::nocontact",
"target", target_r));
return 0;
}
}
/* Stufenangabe bei nicht Stufenvariierbaren Spr<70>chen abfangen */
if (!(sp->sptyp & SPELLLEVEL)) {
int ilevel = eff_skill(u, SK_MAGIC, u->region);
if (ilevel != level) {
level = ilevel;
ADDMSG(&u->faction->msgs, msg_message("spellfail::nolevel",
"mage region command", u, u->region, ord));
}
}
/* Vertrautenmagie */
/* Kennt der Vertraute den Spruch, so zaubert er ganz normal.
* Ansonsten zaubert der Magier durch seinen Vertrauten, dh
* zahlt Komponenten und Aura. Dabei ist die maximale Stufe
* die des Vertrauten!
* Der Spruch wirkt dann auf die Region des Vertrauten und
* gilt nicht als Farcasting. */
if (familiar) {
if ((sp->sptyp & NOTFAMILIARCAST)) {
/* Fehler: "Diesen Spruch kann der Vertraute nicht zaubern" */
cmistake(u, ord, 177, MSG_MAGIC);
return 0;
}
if (caster != familiar) { /* Magier zaubert durch Vertrauten */
if (range > 1) { /* Fehler! Versucht zu Farcasten */
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
"mage", caster));
return 0;
}
if (distance(caster->region, r) > eff_skill(caster, SK_MAGIC, caster->region)) {
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar",
"mage", caster));
return 0;
}
/* mage auf magier setzen, level anpassen, range f<>r Erh<72>hung
* der Spruchkosten nutzen, langen Befehl des Magiers
* l<EFBFBD>schen, zaubern kann er noch */
range *= 2;
set_order(&caster->thisorder, NULL);
level = _min(level, eff_skill(caster, SK_MAGIC, caster->region) / 2);
}
}
/* Weitere Argumente zusammenbasteln */
if (sp->parameter) {
char **params = (char**)malloc(2 * sizeof(char *));
int p = 0, size = 2;
for (;;) {
s = getstrtoken();
if (!s || *s == 0)
2014-08-08 01:03:46 +02:00
break;
if (p + 1 >= size) {
size *= 2;
params = (char**)realloc(params, sizeof(char *) * size);
}
params[p++] = _strdup(s);
}
params[p] = 0;
args =
add_spellparameter(target_r, caster, sp->parameter,
(const char *const *)params, p, ord);
for (p = 0; params[p]; ++p)
free(params[p]);
free(params);
if (args == NULL) {
/* Syntax war falsch */
return 0;
}
}
return create_castorder(0, caster, familiar, sp, target_r, level, 0, range, ord,
args);
2010-08-08 10:06:34 +02:00
}
/* ------------------------------------------------------------- */
/* Damit man keine Rituale in fremden Gebiet machen kann, diese vor
* Bewegung zaubern. Magier sind also in einem fremden Gebiet eine Runde
* lang verletzlich, da sie es betreten, und angegriffen werden k<EFBFBD>nnen,
* bevor sie ein Ritual machen k<EFBFBD>nnen.
*
* Syntax: ZAUBER [REGION X Y] [STUFE <stufe>] "Spruchname" [Einheit-1
* Einheit-2 ..]
*
* Nach Priorit<EFBFBD>t geordnet die Zauber global auswerten.
*
* Die Kosten f<EFBFBD>r Farcasting multiplizieren sich mit der Entfernung,
* cast_level gibt die virtuelle Stufe an, die den durch das Farcasten
* entstandenen Spruchkosten entspricht. Sind die Spruchkosten nicht
* levelabh<EFBFBD>ngig, so sind die Kosten nur von der Entfernung bestimmt,
* die St<EFBFBD>rke/Level durch den realen Skill des Magiers
*/
2011-03-07 08:02:35 +01:00
void magic(void)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
region *r;
int rank;
castorder *co;
spellrank spellranks[MAX_SPELLRANK];
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
memset(spellranks, 0, sizeof(spellranks));
2010-08-08 10:06:34 +02:00
2014-08-08 01:03:46 +02:00
for (r = regions; r; r = r->next) {
unit *u;
for (u = r->units; u; u = u->next) {
order *ord;
if (u->number <= 0 || u_race(u) == get_race(RC_SPELL))
continue;
if (u_race(u) == get_race(RC_INSECT) && r_insectstalled(r) &&
!is_cursed(u->attribs, C_KAELTESCHUTZ, 0))
continue;
if (fval(u, UFL_WERE | UFL_LONGACTION)) {
continue;
}
2014-08-08 01:03:46 +02:00
if (u->thisorder != NULL) {
for (ord = u->orders; ord; ord = ord->next) {
if (getkeyword(ord) == K_CAST) {
castorder *co = cast_cmd(u, ord);
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
if (co) {
const spell *sp = co->sp;
add_castorder(&spellranks[sp->rank], co);
}
}
}
}
}
2010-08-08 10:06:34 +02:00
}
2014-08-08 01:03:46 +02:00
/* Da sich die Aura und Komponenten in der Zwischenzeit ver<65>ndert
* haben k<EFBFBD>nnen und sich durch vorherige Spr<EFBFBD>che das Zaubern
* erschwert haben kann, muss beim zaubern erneut gepr<EFBFBD>ft werden, ob der
* Spruch <EFBFBD>berhaupt gezaubert werden kann.
* (level) die effektive St<EFBFBD>rke des Spruchs (= Stufe, auf der der
* Spruch gezaubert wird) */
for (rank = 0; rank < MAX_SPELLRANK; rank++) {
for (co = spellranks[rank].begin; co; co = co->next) {
order *ord = co->order;
int invalid, resist, success, cast_level = co->level;
bool fumbled = false;
unit *u = co->magician.u;
const spell *sp = co->sp;
region *target_r = co_get_region(co);
/* reichen die Komponenten nicht, wird der Level reduziert. */
co->level = eff_spelllevel(u, sp, cast_level, co->distance);
if (co->level < 1) {
/* Fehlermeldung mit Komponenten generieren */
cancast(u, sp, cast_level, co->distance, ord);
continue;
}
if (cast_level > co->level) {
/* Spr<70>che mit Fixkosten werden immer auf Stufe des Spruchs
* gezaubert, co->level ist aber defaultm<EFBFBD><EFBFBD>ig Stufe des Magiers */
if (spl_costtyp(sp) != SPC_FIX) {
ADDMSG(&u->faction->msgs, msg_message("missing_components",
"unit spell level", u, sp, cast_level));
}
}
/* Pr<50>fen, ob die realen Kosten f<>r die gew<65>nschten Stufe bezahlt
* werden k<EFBFBD>nnen */
if (!cancast(u, sp, co->level, co->distance, ord)) {
/* die Fehlermeldung wird in cancast generiert */
continue;
}
co->force = spellpower(target_r, u, sp, co->level, ord);
/* die St<53>rke kann durch Antimagie auf 0 sinken */
if (co->force <= 0) {
co->force = 0;
ADDMSG(&u->faction->msgs, msg_message("missing_force",
"unit spell level", u, sp, co->level));
}
/* Ziele auf Existenz pr<70>fen und Magieresistenz feststellen. Wurde
* kein Ziel gefunden, so ist verify_targets=0. Scheitert der
* Spruch an der Magieresistenz, so ist verify_targets = 1, bei
* Erfolg auf ganzer Linie ist verify_targets= 2
*/
verify_targets(co, &invalid, &resist, &success);
if (success + resist == 0) {
/* kein Ziel gefunden, Fehlermeldungen sind in verify_targets */
/* keine kosten f<>r den zauber */
continue; /* <20>u<EFBFBD>ere Schleife, n<>chster Zauberer */
}
else if (co->force > 0 && resist > 0) {
/* einige oder alle Ziele waren magieresistent */
if (success == 0) {
co->force = 0;
/* zwar wurde mindestens ein Ziel gefunden, das widerstand
* jedoch dem Zauber. Kosten abziehen und abbrechen. */
ADDMSG(&u->faction->msgs, msg_message("spell_resist",
"unit region spell", u, r, sp));
}
}
/* Auch f<>r Patzer gibt es Erfahrung, m<>ssen die Spruchkosten
* bezahlt werden und die nachfolgenden Spr<EFBFBD>che werden teurer */
if (co->force > 0) {
if (fumble(target_r, u, sp, co->level)) {
/* zuerst bezahlen, dann evt in do_fumble alle Aura verlieren */
fumbled = true;
}
else {
co->level = sp->cast(co);
if (co->level <= 0) {
/* Kosten nur f<>r real ben<65>tige Stufe berechnen */
continue;
}
}
}
/* erst bezahlen, dann Kostenz<6E>hler erh<72>hen */
if (co->level > 0) {
pay_spell(u, sp, co->level, co->distance);
}
if (fumbled) {
do_fumble(co);
}
countspells(u, 1);
}
}
/* Sind alle Zauber gesprochen gibts Erfahrung */
for (r = regions; r; r = r->next) {
unit *u;
for (u = r->units; u; u = u->next) {
if (is_mage(u) && countspells(u, 0) > 0) {
produceexp(u, SK_MAGIC, u->number);
/* Spruchlistenaktualiesierung ist in Regeneration */
}
}
}
for (rank = 0; rank < MAX_SPELLRANK; rank++) {
free_castorders(spellranks[rank].begin);
}
remove_empty_units();
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *spell_info(const spell * sp, const struct locale *lang)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
return LOC(lang, mkname("spellinfo", sp->sname));
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *spell_name(const spell * sp, const struct locale *lang)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
return LOC(lang, mkname("spell", sp->sname));
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
const char *curse_name(const curse_type * ctype, const struct locale *lang)
2010-08-08 10:06:34 +02:00
{
2014-08-08 01:03:46 +02:00
return LOC(lang, mkname("spell", ctype->cname));
2010-08-08 10:06:34 +02:00
}
2012-05-26 17:20:26 +02:00
spell *unit_getspell(struct unit *u, const char *name, const struct locale * lang)
{
2014-08-08 01:03:46 +02:00
sc_mage * mage = get_mage(u);
if (mage) {
variant token;
void * tokens = 0;
spellbook *sb = unit_get_spellbook(u);
if (sb) {
quicklist * ql;
int qi;
for (qi = 0, ql = sb->spells; ql; ql_advance(&ql, &qi, 1)) {
spellbook_entry *sbe = (spellbook_entry *)ql_get(ql, qi);
spell *sp = sbe->sp;
const char *n = spell_name(sp, lang);
if (!n) {
log_error("no translation in locale %s for spell %s\n", locale_name(lang), sp->sname);
}
else {
token.v = sp;
addtoken(&tokens, n, token);
}
}
}
2014-08-08 01:03:46 +02:00
if (tokens) {
if (findtoken(tokens, name, &token) != E_TOK_NOMATCH) {
freetokens(tokens);
return (spell *)token.v;
}
freetokens(tokens);
}
}
2014-08-08 01:03:46 +02:00
return 0;
}
static critbit_tree cb_spellbooks;
spellbook * get_spellbook(const char * name)
{
2014-08-08 01:03:46 +02:00
char buffer[64];
spellbook * result;
const void * match;
2014-08-08 01:03:46 +02:00
if (cb_find_prefix(&cb_spellbooks, name, strlen(name), &match, 1, 0)) {
cb_get_kv(match, &result, sizeof(result));
}
else {
size_t len = strlen(name);
result = create_spellbook(name);
assert(strlen(name) + sizeof(result) < sizeof(buffer));
len = cb_new_kv(name, len, &result, sizeof(result), buffer);
cb_insert(&cb_spellbooks, buffer, len);
}
return result;
}
void free_spellbooks(void)
{
2014-08-08 01:03:46 +02:00
cb_clear(&cb_spellbooks);
}