2010-08-08 10:06:34 +02:00
|
|
|
|
/*
|
|
|
|
|
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
|
|
|
|
|
Katja Zedel <katze@felidae.kn-bremen.de
|
|
|
|
|
Christian Schlittchen <corwin@amber.kn-bremen.de>
|
|
|
|
|
|
|
|
|
|
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 "magic.h"
|
|
|
|
|
|
|
|
|
|
#include "building.h"
|
|
|
|
|
#include "curse.h"
|
|
|
|
|
#include "faction.h"
|
|
|
|
|
#include "item.h"
|
|
|
|
|
#include "message.h"
|
|
|
|
|
#include "objtypes.h"
|
|
|
|
|
#include "order.h"
|
|
|
|
|
#include "pathfinder.h"
|
|
|
|
|
#include "plane.h"
|
|
|
|
|
#include "pool.h"
|
|
|
|
|
#include "race.h"
|
|
|
|
|
#include "region.h"
|
|
|
|
|
#include "ship.h"
|
|
|
|
|
#include "skill.h"
|
|
|
|
|
#include "spell.h"
|
|
|
|
|
#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 <util/language.h>
|
|
|
|
|
#include <util/lists.h>
|
|
|
|
|
#include <util/log.h>
|
|
|
|
|
#include <util/parser.h>
|
2011-02-26 09:26:14 +01:00
|
|
|
|
#include <util/quicklist.h>
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#include <util/resolve.h>
|
|
|
|
|
#include <util/rand.h>
|
|
|
|
|
#include <util/rng.h>
|
|
|
|
|
#include <util/storage.h>
|
|
|
|
|
#include <util/base36.h>
|
|
|
|
|
#include <util/event.h>
|
|
|
|
|
|
|
|
|
|
/* 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] = {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
"gray",
|
|
|
|
|
"illaun",
|
|
|
|
|
"tybied",
|
|
|
|
|
"cerddor",
|
|
|
|
|
"gwyrrd",
|
|
|
|
|
"draig",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"common"
|
2010-08-08 10:06:34 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
attrib_type at_reportspell = {
|
|
|
|
|
"reportspell", NULL, NULL, NULL, NO_WRITE, NO_READ
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
** at_icastle
|
|
|
|
|
** TODO: separate castle-appearance from illusion-effects
|
|
|
|
|
**/
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static double MagicRegeneration(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
static double value = -1.0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (value < 0) {
|
|
|
|
|
const char *str = get_param(global.parameters, "magic.regeneration");
|
|
|
|
|
value = str ? atof(str) : 1.0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
double MagicPower(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
static double value = -1.0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (value < 0) {
|
|
|
|
|
const char *str = get_param(global.parameters, "magic.power");
|
|
|
|
|
value = str ? atof(str) : 1.0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
icastle_data *data = (icastle_data *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
variant bno;
|
|
|
|
|
char token[32];
|
|
|
|
|
store->r_tok_buf(store, token, sizeof(token));
|
|
|
|
|
bno.i = store->r_int(store);
|
|
|
|
|
data->time = store->r_int(store);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
icastle_data *data = (icastle_data *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
store->w_tok(store, data->type->_name);
|
|
|
|
|
store->w_int(store, data->building->no);
|
|
|
|
|
store->w_int(store, data->time);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int a_ageicastle(struct attrib *a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
icastle_data *data = (icastle_data *) a->data.v;
|
|
|
|
|
if (data->time <= 0) {
|
|
|
|
|
building *b = data->building;
|
|
|
|
|
region *r = b->region;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else
|
|
|
|
|
data->time--;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return AT_AGE_KEEP;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void a_initicastle(struct attrib *a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
a->data.v = calloc(sizeof(icastle_data), 1);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void a_finalizeicastle(struct attrib *a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
free(a->data.v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_icastle = {
|
|
|
|
|
"zauber_icastle",
|
|
|
|
|
a_initicastle,
|
|
|
|
|
a_finalizeicastle,
|
|
|
|
|
a_ageicastle,
|
|
|
|
|
a_writeicastle,
|
|
|
|
|
a_readicastle
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a->data.v = calloc(sizeof(sc_mage), 1);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void free_mage(attrib * a)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *mage = (sc_mage *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
freelist(mage->spells);
|
|
|
|
|
free(mage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int FactionSpells(void)
|
|
|
|
|
{
|
|
|
|
|
static int rules_factionspells = -1;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (rules_factionspells < 0) {
|
|
|
|
|
rules_factionspells =
|
|
|
|
|
get_param_int(global.parameters, "rules.magic.factionlist", 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return rules_factionspells;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void read_spells(struct quicklist **slistp, magic_t mtype,
|
|
|
|
|
struct storage *store)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
for (;;) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spell *sp;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
char spname[64];
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (store->version < SPELLNAME_VERSION) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int i = store->r_int(store);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (i < 0)
|
|
|
|
|
break;
|
|
|
|
|
sp = find_spellbyid(M_NONE, (spellid_t) i);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} else {
|
|
|
|
|
store->r_tok_buf(store, spname, sizeof(spname));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (strcmp(spname, "end") == 0)
|
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
sp = find_spell(mtype, spname);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp != NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
add_spell(slistp, sp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
int i, mtype;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *mage = (sc_mage *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
char spname[64];
|
|
|
|
|
|
|
|
|
|
mtype = store->r_int(store);
|
|
|
|
|
mage->spellpoints = store->r_int(store);
|
|
|
|
|
mage->spchange = store->r_int(store);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
mage->magietyp = (magic_t) mtype;
|
|
|
|
|
for (i = 0; i != MAXCOMBATSPELLS; ++i) {
|
|
|
|
|
spell *sp = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int level = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (store->version < SPELLNAME_VERSION) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int spid;
|
|
|
|
|
spid = store->r_int(store);
|
|
|
|
|
level = store->r_int(store);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (spid >= 0) {
|
|
|
|
|
sp = find_spellbyid(mage->magietyp, (spellid_t) spid);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
store->r_tok_buf(store, spname, sizeof(spname));
|
|
|
|
|
level = store->r_int(store);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (strcmp("none", spname) != 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
sp = find_spell(mage->magietyp, spname);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp && level >= 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int slot = -1;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp->sptyp & PRECOMBATSPELL)
|
|
|
|
|
slot = 0;
|
|
|
|
|
else if (sp->sptyp & COMBATSPELL)
|
|
|
|
|
slot = 1;
|
|
|
|
|
else if (sp->sptyp & POSTCOMBATSPELL)
|
|
|
|
|
slot = 2;
|
|
|
|
|
if (slot >= 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
mage->combatspells[slot].level = level;
|
|
|
|
|
mage->combatspells[slot].sp = sp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-26 09:26:14 +01:00
|
|
|
|
read_spells(&mage->spells, mage->magietyp, store);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return AT_READ_OK;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
quicklist *ql;
|
2011-02-26 09:26:14 +01:00
|
|
|
|
int qi;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (ql = slist, qi = 0; ql; ql_advance(&ql, &qi, 1)) {
|
|
|
|
|
spell *sp = (spell *) ql_get(ql, qi);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
store->w_tok(store, sp->sname);
|
|
|
|
|
}
|
|
|
|
|
store->w_tok(store, "end");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
int i;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *mage = (sc_mage *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
store->w_int(store, mage->magietyp);
|
|
|
|
|
store->w_int(store, mage->spellpoints);
|
|
|
|
|
store->w_int(store, mage->spchange);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (i = 0; i != MAXCOMBATSPELLS; ++i) {
|
|
|
|
|
store->w_tok(store,
|
|
|
|
|
mage->combatspells[i].sp ? mage->combatspells[i].sp->sname : "none");
|
2010-08-08 10:06:34 +02:00
|
|
|
|
store->w_int(store, mage->combatspells[i].level);
|
|
|
|
|
}
|
2011-02-26 09:26:14 +01:00
|
|
|
|
write_spells(mage->spells, store);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_mage = {
|
|
|
|
|
"mage",
|
|
|
|
|
init_mage,
|
|
|
|
|
free_mage,
|
|
|
|
|
NULL,
|
|
|
|
|
write_mage,
|
|
|
|
|
read_mage,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean is_mage(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
return i2b(get_mage(u) != NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *get_mage(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
if (has_skill(u, SK_MAGIC)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_mage);
|
|
|
|
|
if (a)
|
|
|
|
|
return a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return (sc_mage *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int i;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spell *sp = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
char token[32];
|
|
|
|
|
|
|
|
|
|
store->r_tok_buf(store, token, sizeof(token));
|
|
|
|
|
i = atoi(token);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (i != 0) {
|
|
|
|
|
sp = find_spellbyid(M_NONE, (spellid_t) i);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} else {
|
|
|
|
|
int mtype;
|
|
|
|
|
mtype = store->r_int(store);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sp = find_spell((magic_t) mtype, token);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* log_error(("could not find seenspell '%s'\n", buf)); */
|
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
|
}
|
|
|
|
|
a->data.v = sp;
|
|
|
|
|
return AT_READ_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const spell *sp = (const spell *)a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
store->w_tok(store, sp->sname);
|
|
|
|
|
store->w_int(store, sp->magietyp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_type at_seenspell = {
|
|
|
|
|
"seenspell", NULL, NULL, NULL, write_seenspell, read_seenspell
|
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static boolean already_seen(const faction * f, const spell * sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
attrib *a;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (a = a_find(f->attribs, &at_seenspell); a && a->type == &at_seenspell;
|
|
|
|
|
a = a->next) {
|
|
|
|
|
if (a->data.v == sp)
|
|
|
|
|
return true;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean know_school(const faction * f, magic_t school)
|
|
|
|
|
{
|
|
|
|
|
static int common = MAXMAGIETYP;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (f->magiegebiet == school)
|
|
|
|
|
return true;
|
|
|
|
|
if (common == MAXMAGIETYP) {
|
|
|
|
|
const char *common_school =
|
|
|
|
|
get_param(global.parameters, "rules.magic.common");
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (common_school) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (common = 0; common != MAXMAGIETYP; ++common) {
|
|
|
|
|
if (strcmp(common_school, magic_school[common]) == 0)
|
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (common == MAXMAGIETYP) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
common = M_NONE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return school == common;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
#define COMMONSPELLS 1 /* number of new common spells per level */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
#define MAXSPELLS 256
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static boolean has_spell(quicklist * ql, const spell * sp)
|
2011-02-26 09:26:14 +01:00
|
|
|
|
{
|
|
|
|
|
int qi;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return ql_set_find(&ql, &qi, sp) != 0;
|
2011-02-26 09:26:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/** update the spellbook with a new level
|
|
|
|
|
* Written for Eressea 1.1
|
|
|
|
|
*/
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void update_spellbook(faction * f, int level)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spell *commonspells[MAXSPELLS];
|
2011-02-26 09:26:14 +01:00
|
|
|
|
int qi, numspells = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
quicklist *ql;
|
|
|
|
|
|
|
|
|
|
for (qi = 0, ql = spells; ql; ql_advance(&ql, &qi, 1)) {
|
|
|
|
|
spell *sp = (spell *) ql_get(ql, qi);
|
|
|
|
|
if (sp->magietyp == M_COMMON && level > f->max_spelllevel
|
|
|
|
|
&& sp->level <= level) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
commonspells[numspells++] = sp;
|
|
|
|
|
} else {
|
|
|
|
|
if (know_school(f, sp->magietyp) && sp->level <= level) {
|
2011-03-01 06:28:26 +01:00
|
|
|
|
ql_set_insert(&f->spellbook, sp);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (numspells > 0 && level > f->max_spelllevel) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int i;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (i = 0; i != COMMONSPELLS; ++i) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int maxspell = numspells;
|
|
|
|
|
int spellno = -1;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spell *sp;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
do {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (spellno == maxspell) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
--maxspell;
|
|
|
|
|
}
|
|
|
|
|
spellno = rng_int() % maxspell;
|
|
|
|
|
sp = commonspells[spellno];
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (maxspell > 0 && sp && sp->level <= f->max_spelllevel
|
|
|
|
|
&& !has_spell(f->spellbook, sp));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (sp) {
|
2011-02-26 09:26:14 +01:00
|
|
|
|
ql_set_insert(&f->spellbook, sp);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
commonspells[spellno] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
++f->max_spelllevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void updatespelllist(unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
int sk = eff_skill(u, SK_MAGIC, u->region);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
quicklist *ql = spells;
|
2011-02-26 09:26:14 +01:00
|
|
|
|
int qi;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
struct sc_mage *mage = get_mage(u);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
boolean ismonster = is_monsters(u->faction);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
quicklist **dst;
|
|
|
|
|
|
|
|
|
|
if (mage->magietyp == M_GRAY) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Magier mit M_GRAY bekommen weder Spr<70>che angezeigt noch
|
|
|
|
|
* neue Spr<EFBFBD>che in ihre List-of-known-spells. Das sind zb alle alten
|
|
|
|
|
* Drachen, die noch den Skill Magie haben, und alle familiars */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (FactionSpells()) {
|
2011-02-26 09:26:14 +01:00
|
|
|
|
ql = u->faction->spellbook;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-02-26 09:26:14 +01:00
|
|
|
|
dst = get_spelllist(mage, u->faction);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (qi = 0; ql; ql_advance(&ql, &qi, 1)) {
|
|
|
|
|
spell *sp = (spell *) ql_get(ql, qi);
|
|
|
|
|
if (sp->level <= sk) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
boolean know = u_hasspell(u, sp);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (know || sp->magietyp == M_COMMON
|
|
|
|
|
|| know_school(u->faction, sp->magietyp)) {
|
|
|
|
|
faction *f = u->faction;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!know)
|
|
|
|
|
add_spell(dst, sp);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (!ismonster && !already_seen(u->faction, sp)) {
|
|
|
|
|
a_add(&f->attribs, a_new(&at_reportspell))->data.v = sp;
|
|
|
|
|
a_add(&f->attribs, a_new(&at_seenspell))->data.v = sp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *mage;
|
|
|
|
|
attrib *a;
|
|
|
|
|
|
|
|
|
|
a = a_find(u->attribs, &at_mage);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a != NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a_remove(&u->attribs, a);
|
|
|
|
|
}
|
|
|
|
|
a = a_add(&u->attribs, a_new(&at_mage));
|
|
|
|
|
mage = a->data.v;
|
|
|
|
|
|
|
|
|
|
mage->magietyp = mtyp;
|
|
|
|
|
return mage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* Funktionen f<>r die Bearbeitung der List-of-known-spells */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void add_spell(struct quicklist **slistp, spell * sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (ql_set_insert(slistp, sp) != 0) {
|
|
|
|
|
log_error(("add_spell: the list already contains the spell '%s'.\n",
|
|
|
|
|
sp->sname));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean u_hasspell(const struct unit *u, const struct spell *sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *mage = get_mage(u);
|
|
|
|
|
|
|
|
|
|
return (mage) ? has_spell(mage->spells, sp) : false;
|
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
|
|
|
|
{
|
|
|
|
|
sc_mage *m = get_mage(u);
|
|
|
|
|
|
|
|
|
|
assert(nr < MAXCOMBATSPELLS);
|
|
|
|
|
if (m) {
|
|
|
|
|
int level = eff_skill(u, SK_MAGIC, u->region);
|
|
|
|
|
return MIN(m->combatspells[nr].level, level);
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
|
|
|
|
|
assert(nr < MAXCOMBATSPELLS);
|
|
|
|
|
m = get_mage(u);
|
|
|
|
|
if (m) {
|
|
|
|
|
return m->combatspells[nr].sp;
|
|
|
|
|
} else if (u->race->precombatspell != NULL) {
|
|
|
|
|
return u->race->precombatspell;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
sc_mage *m = get_mage(u);
|
|
|
|
|
int i = -1;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* knowsspell pr<70>ft auf ist_magier, ist_spruch, kennt_spruch */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (knowsspell(u->region, u, sp) == false) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp->sptyp & PRECOMBATSPELL)
|
|
|
|
|
i = 0;
|
|
|
|
|
else if (sp->sptyp & COMBATSPELL)
|
|
|
|
|
i = 1;
|
|
|
|
|
else if (sp->sptyp & POSTCOMBATSPELL)
|
|
|
|
|
i = 2;
|
|
|
|
|
assert(i >= 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
m->combatspells[i].sp = sp;
|
|
|
|
|
m->combatspells[i].level = level;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void unset_combatspell(unit * u, spell * sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
int nr = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (!sp) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (i = 0; i < MAXCOMBATSPELLS; i++) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
m->combatspells[i].sp = NULL;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else if (sp->sptyp & PRECOMBATSPELL) {
|
|
|
|
|
if (sp != get_combatspell(u, 0))
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return;
|
|
|
|
|
} else if (sp->sptyp & COMBATSPELL) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp != get_combatspell(u, 1)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
nr = 1;
|
|
|
|
|
} else if (sp->sptyp & POSTCOMBATSPELL) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp != get_combatspell(u, 2)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
nr = 2;
|
|
|
|
|
}
|
|
|
|
|
m->combatspells[nr].sp = NULL;
|
|
|
|
|
m->combatspells[nr].level = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
return m->spellpoints;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void set_spellpoints(unit * u, int sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
m->spellpoints = sp;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
int sp;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* verhindere negative Magiepunkte */
|
|
|
|
|
sp = MAX(m->spellpoints + mp, 0);
|
|
|
|
|
m->spellpoints = sp;
|
|
|
|
|
|
|
|
|
|
return sp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
return m->spchange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int sk, n;
|
|
|
|
|
|
|
|
|
|
sk = eff_skill(u, SK_MAGIC, r);
|
|
|
|
|
n = (int)(sk * sk * u->race->maxaura / 4);
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
sk = eff_skill(u, SK_MAGIC, r);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
msp = u->race->maxaura * (pow(sk, potenz) / divisor + 1) + get_spchange(u);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (get_item(u, I_AURAKULUM) > 0) {
|
|
|
|
|
msp += use_item_aura(r, u);
|
|
|
|
|
}
|
|
|
|
|
n = get_curseeffect(u->attribs, C_AURA, 0);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (n > 0)
|
|
|
|
|
msp = (msp * n) / 100;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
return MAX((int)msp, 0);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!m)
|
|
|
|
|
return 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
m->spchange += csp;
|
|
|
|
|
return max_spellpoints(u->region, u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
sc_mage *m;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
m = get_mage(u);
|
|
|
|
|
if (!m)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (step == 0)
|
|
|
|
|
return m->spellcount;
|
|
|
|
|
|
|
|
|
|
count = m->spellcount + step;
|
|
|
|
|
|
|
|
|
|
/* negative Werte abfangen. */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
m->spellcount = MAX(0, count);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
return m->spellcount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
for (k = 0; sp->components[k].type; k++) {
|
|
|
|
|
if (sp->components[k].type == r_aura) {
|
|
|
|
|
aura = sp->components[k].amount;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
aura *= (1 << count);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return aura;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int k;
|
|
|
|
|
int costtyp = SPC_FIX;
|
|
|
|
|
|
|
|
|
|
for (k = 0; sp->components[k].type; k++) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (costtyp == SPC_LINEAR)
|
|
|
|
|
return SPC_LINEAR;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (sp->components[k].cost == SPC_LINEAR) {
|
|
|
|
|
return SPC_LINEAR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wenn keine Fixkosten, Typ <20>bernehmen */
|
|
|
|
|
if (sp->components[k].cost != SPC_FIX) {
|
|
|
|
|
costtyp = sp->components[k].cost;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return costtyp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int k, maxlevel, needplevel;
|
|
|
|
|
int costtyp = SPC_FIX;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
for (k = 0; sp->components[k].type; k++) {
|
|
|
|
|
if (cast_level == 0)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
maxlevel =
|
|
|
|
|
get_pooled(u, sp->components[k].type, GET_DEFAULT,
|
|
|
|
|
needplevel * cast_level) / needplevel;
|
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* 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) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (maxlevel < 1)
|
|
|
|
|
cast_level = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* 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;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (maxlevel < cast_level)
|
|
|
|
|
cast_level = 0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Ein Spruch mit Fixkosten wird immer mit der Stufe des Spruchs und
|
|
|
|
|
* nicht auf der Stufe des Magiers gezaubert */
|
|
|
|
|
if (costtyp == SPC_FIX) {
|
|
|
|
|
cast_level = MIN(cast_level, sp->level);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cast_level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int k;
|
|
|
|
|
int resuse;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sp->components[k].cost == SPC_LINEAR
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|| sp->components[k].cost == SPC_LEVEL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
resuse *= cast_level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use_pooled(u, sp->components[k].type, GET_DEFAULT, resuse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean knowsspell(const region * r, const unit * u, const spell * sp)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
sc_mage *mage;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Ist <20>berhaupt ein g<>ltiger Spruch angegeben? */
|
|
|
|
|
if (!sp || sp->id == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
/* Magier? */
|
|
|
|
|
mage = get_mage(u);
|
|
|
|
|
if (mage == NULL) {
|
|
|
|
|
log_warning(("%s ist kein Magier, versucht aber zu zaubern.\n",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unitname(u)));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
/* steht der Spruch in der Spruchliste? */
|
|
|
|
|
if (!u_hasspell(u, sp)) {
|
|
|
|
|
/* ist der Spruch aus einem anderen Magiegebiet? */
|
|
|
|
|
if (know_school(u->faction, sp->magietyp)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (eff_skill(u, SK_MAGIC, u->region) >= sp->level) {
|
|
|
|
|
log_warning(("%s ist hat die erforderliche Stufe, kennt aber %s nicht.\n",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unitname(u), spell_name(sp, default_locale)));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* hier sollten alle potentiellen Fehler abgefangen sein */
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
boolean
|
|
|
|
|
cancast(unit * u, const spell * sp, int level, int range, struct order * ord)
|
|
|
|
|
{
|
|
|
|
|
int k;
|
|
|
|
|
int itemanz;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
resource *reslist = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (knowsspell(u->region, u, sp) == false) {
|
|
|
|
|
/* 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) < sp->level) {
|
|
|
|
|
/* die Einheit ist nicht erfahren genug f<>r diesen Zauber */
|
|
|
|
|
cmistake(u, ord, 169, MSG_MAGIC);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
for (k = 0; sp->components[k].type; ++k) {
|
|
|
|
|
if (sp->components[k].amount > 0) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const resource_type *rtype = sp->components[k].type;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* sind die Kosten stufenabh<62>ngig, so muss itemanz noch mit dem
|
|
|
|
|
* level multipliziert werden */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
switch (sp->components[k].cost) {
|
|
|
|
|
case SPC_LEVEL:
|
|
|
|
|
case SPC_LINEAR:
|
|
|
|
|
itemanz *= level;
|
|
|
|
|
break;
|
|
|
|
|
case SPC_FIX:
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
itemhave = get_pooled(u, rtype, GET_DEFAULT, itemanz);
|
|
|
|
|
if (itemhave < itemanz) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
resource *res = malloc(sizeof(resource));
|
|
|
|
|
res->number = itemanz - itemhave;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
res->type = rtype;
|
|
|
|
|
res->next = reslist;
|
|
|
|
|
reslist = res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (reslist != NULL) {
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "missing_components_list",
|
|
|
|
|
"list", reslist));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
double
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spellpower(region * r, unit * u, const spell * sp, int cast_level,
|
|
|
|
|
struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
curse *c;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
double force = cast_level;
|
|
|
|
|
int elf_power = -1;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
/* Bonus durch Magieturm und gesegneten Steinkreis */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (get_item(u, I_RING_OF_POWER) > 0)
|
|
|
|
|
++force;
|
|
|
|
|
if (elf_power < 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
elf_power = get_param_int(global.parameters, "rules.magic.elfpower", 0);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (elf_power && u->race == new_race[RC_ELF] && r_isforest(r)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
++force;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Antimagie in der Zielregion */
|
|
|
|
|
c = get_curse(r->attribs, ct_find("antimagiczone"));
|
|
|
|
|
if (curse_active(c)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *mage = c->magician;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
force -= curse_geteffect(c);
|
|
|
|
|
curse_changevigour(&r->attribs, c, (float)-cast_level);
|
|
|
|
|
cmistake(u, ord, 185, MSG_MAGIC);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (mage != NULL && mage->faction != NULL) {
|
|
|
|
|
if (force > 0) {
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
|
|
|
|
|
"self mage region", mage, u, r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("block_spell",
|
|
|
|
|
"self mage region", mage, u, r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Patzerfluch-Effekt: */
|
|
|
|
|
c = get_curse(r->attribs, ct_find("fumble"));
|
|
|
|
|
if (curse_active(c)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *mage = c->magician;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
force -= curse_geteffect(c);
|
|
|
|
|
curse_changevigour(&u->attribs, c, -1);
|
|
|
|
|
cmistake(u, ord, 185, MSG_MAGIC);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (mage != NULL && mage->faction != NULL) {
|
|
|
|
|
if (force > 0) {
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("reduce_spell",
|
|
|
|
|
"self mage region", mage, u, r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("block_spell",
|
|
|
|
|
"self mage region", mage, u, r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
force = force * MagicPower();
|
|
|
|
|
|
|
|
|
|
return MAX(force, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int dist;
|
|
|
|
|
int mult;
|
|
|
|
|
|
|
|
|
|
if (!r) {
|
|
|
|
|
return INT_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dist = koor_distance(r->x, r->y, magician->region->x, magician->region->y);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (dist > 24)
|
|
|
|
|
return INT_MAX;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
mult = 1 << dist;
|
|
|
|
|
if (dist > 1) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!path_exists(magician->region, r, dist * 2, allowed_fly))
|
|
|
|
|
mult = INT_MAX;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
curse *c;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
/* Bonus durch Rassenmagieresistenz */
|
|
|
|
|
double probability = target->race->magres;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
assert(target->number > 0);
|
|
|
|
|
|
|
|
|
|
/* Magier haben einen Resistenzbonus vom Magietalent * 5% */
|
|
|
|
|
probability += effskill(target, SK_MAGIC) * 0.05;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unicorn +10 */
|
|
|
|
|
n = get_item(target, I_ELVENHORSE);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (n)
|
|
|
|
|
probability += n * 0.1 / target->number;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* Auswirkungen von Zaubern auf der Region */
|
|
|
|
|
a = a_find(target->region->attribs, &at_curse);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (a && a->type == &at_curse) {
|
|
|
|
|
curse *c = (curse *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
unit *mage = c->magician;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (mage != NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (c->type == ct_find("goodmagicresistancezone")) {
|
|
|
|
|
if (alliedunit(mage, target->faction, HELP_GUARD)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability += curse_geteffect(c) * 0.01;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else if (c->type == ct_find("badmagicresistancezone")) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (alliedunit(mage, target->faction, HELP_GUARD)) {
|
|
|
|
|
/* TODO: hier sollte doch sicher was passieren? */
|
|
|
|
|
a = a->next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
|
|
|
|
/* Bonus durch Artefakte */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* TODO (noch gibs keine) */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* Bonus durch Geb<65>ude */
|
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
struct building *b = inside_building(target);
|
|
|
|
|
const struct building_type *btype = b ? b->type : NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* gesegneter Steinkreis gibt 30% dazu */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (btype)
|
|
|
|
|
probability += btype->magresbonus * 0.01;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return probability;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
boolean
|
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
|
|
|
|
{
|
|
|
|
|
double probability = 0.0;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (magician == NULL)
|
|
|
|
|
return true;
|
|
|
|
|
if (obj == NULL)
|
|
|
|
|
return true;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
switch (objtyp) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
case TYP_UNIT:
|
2011-03-07 08:02:35 +01:00
|
|
|
|
{
|
|
|
|
|
int at, pa = 0;
|
|
|
|
|
skill *sv;
|
|
|
|
|
unit *u = (unit *) obj;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
at = effskill(magician, SK_MAGIC);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (sv = u->skills; sv != u->skills + u->skill_size; ++sv) {
|
|
|
|
|
int sk = effskill(u, sv->id);
|
|
|
|
|
if (pa < sk)
|
|
|
|
|
pa = sk;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* Contest */
|
|
|
|
|
probability = 0.05 * (10 + pa - at);
|
|
|
|
|
|
|
|
|
|
probability += magic_resistance((unit *) obj);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
case TYP_REGION:
|
|
|
|
|
/* Bonus durch Zauber */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability +=
|
|
|
|
|
0.01 * get_curseeffect(((region *) obj)->attribs, C_RESIST_MAGIC, 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYP_BUILDING:
|
|
|
|
|
/* Bonus durch Zauber */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability +=
|
|
|
|
|
0.01 * get_curseeffect(((building *) obj)->attribs, C_RESIST_MAGIC, 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* Bonus durch Typ */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability += 0.01 * ((building *) obj)->type->magres;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TYP_SHIP:
|
|
|
|
|
/* Bonus durch Zauber */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability +=
|
|
|
|
|
0.01 * get_curseeffect(((ship *) obj)->attribs, C_RESIST_MAGIC, 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
probability = MAX(0.02, probability + t_bonus * 0.01);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
probability = MIN(0.98, probability);
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean is_magic_resistant(unit * magician, unit * target, int resist_bonus)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return (boolean) 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
|
|
|
|
|
*/
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean fumble(region * r, unit * u, const spell * sp, int cast_grade)
|
2010-08-08 10:06:34 +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
|
|
|
|
|
* */
|
|
|
|
|
|
|
|
|
|
int rnd = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
double x = (double)cast_grade / (double)eff_skill(u, SK_MAGIC, r);
|
|
|
|
|
int patzer = (int)(((double)x * 40.0) - 20.0);
|
|
|
|
|
struct building *b = inside_building(u);
|
|
|
|
|
const struct building_type *btype = b ? b->type : NULL;
|
|
|
|
|
|
|
|
|
|
if (btype)
|
|
|
|
|
patzer -= btype->fumblebonus;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* CHAOSPATZERCHANCE 10 : +10% Chance zu Patzern */
|
|
|
|
|
if (sp->magietyp == M_DRAIG) {
|
|
|
|
|
patzer += CHAOSPATZERCHANCE;
|
|
|
|
|
}
|
|
|
|
|
if (is_cursed(u->attribs, C_MBOOST, 0) == true) {
|
|
|
|
|
patzer += CHAOSPATZERCHANCE;
|
|
|
|
|
}
|
|
|
|
|
if (is_cursed(u->attribs, C_FUMBLE, 0) == true) {
|
|
|
|
|
patzer += CHAOSPATZERCHANCE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* wenn die Chance kleiner als 0 ist, k<>nnen wir gleich false
|
|
|
|
|
* zur<EFBFBD>ckgeben */
|
|
|
|
|
if (patzer <= 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
rnd = rng_int() % 100;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (rnd > patzer) {
|
|
|
|
|
/* Gl<47>ck gehabt, kein Patzer */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* Dummy-Zauberpatzer, Platzhalter f<>r speziel auf die Spr<70>che
|
|
|
|
|
* zugeschnittene Patzer */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void patzer(castorder * co)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
unit *mage = co->magician.u;
|
|
|
|
|
|
|
|
|
|
cmistake(mage, co->order, 180, MSG_MAGIC);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
curse *c;
|
|
|
|
|
region *r = co->rt;
|
|
|
|
|
unit *u = co->magician.u;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
const spell *sp = co->sp;
|
|
|
|
|
int level = co->level;
|
|
|
|
|
int duration;
|
|
|
|
|
double effect;
|
|
|
|
|
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("patzer", "unit region spell",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
u, r, sp));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
switch (rng_int() % 10) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
case 0:
|
|
|
|
|
/* wenn vorhanden spezieller Patzer, ansonsten nix */
|
|
|
|
|
if (sp->patzer)
|
|
|
|
|
sp->patzer(co);
|
|
|
|
|
else
|
|
|
|
|
patzer(co);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
/* Kr<4B>te */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
/* one or two things will happen: the toad changes her race back,
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* 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->irace);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (chance(0.7)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const item_type *it_toadslime = it_find("toadslime");
|
|
|
|
|
if (it_toadslime != NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
t_add(&trestore, trigger_giveitem(u, it_toadslime, 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
duration = rng_int() % level / 2;
|
|
|
|
|
if (duration < 2)
|
|
|
|
|
duration = 2;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
add_trigger(&u->attribs, "timer", trigger_timeout(duration, trestore));
|
|
|
|
|
u->race = new_race[RC_TOAD];
|
|
|
|
|
u->irace = NULL;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&r->msgs, msg_message("patzer6", "unit region spell", u, r, sp));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* fall-through is intentional! */
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
/* tempor<6F>rer Stufenverlust */
|
|
|
|
|
duration = MAX(rng_int() % level / 2, 2);
|
|
|
|
|
effect = -0.5 * level;
|
|
|
|
|
c =
|
|
|
|
|
create_curse(u, &u->attribs, ct_find("skillmod"), (float)level,
|
|
|
|
|
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",
|
|
|
|
|
u, r, sp));
|
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
case 5:
|
|
|
|
|
case 6:
|
|
|
|
|
/* Spruch gelingt, aber alle Magiepunkte weg */
|
|
|
|
|
if (sp->sp_function == NULL) {
|
|
|
|
|
log_error(("spell '%s' has no function.\n", sp->sname));
|
|
|
|
|
} else {
|
|
|
|
|
((nspell_f) sp->sp_function) (co);
|
|
|
|
|
}
|
|
|
|
|
set_spellpoints(u, 0);
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("patzer4", "unit region spell",
|
|
|
|
|
u, r, sp));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
|
case 8:
|
|
|
|
|
case 9:
|
|
|
|
|
default:
|
|
|
|
|
/* Spruch gelingt, alle nachfolgenden Spr<70>che werden 2^4 so teuer */
|
|
|
|
|
if (sp->sp_function == NULL) {
|
|
|
|
|
log_error(("spell '%s' has no function.\n", sp->sname));
|
|
|
|
|
} else {
|
|
|
|
|
((nspell_f) sp->sp_function) (co);
|
|
|
|
|
}
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("patzer5", "unit region spell",
|
|
|
|
|
u, r, sp));
|
|
|
|
|
countspells(u, 3);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int sk;
|
|
|
|
|
double aura, d;
|
|
|
|
|
double potenz = 1.5;
|
|
|
|
|
double divisor = 2.0;
|
|
|
|
|
|
|
|
|
|
sk = effskill(u, SK_MAGIC);
|
|
|
|
|
/* Rassenbonus/-malus */
|
|
|
|
|
d = pow(sk, potenz) * u->race->regaura / divisor;
|
|
|
|
|
d++;
|
|
|
|
|
|
|
|
|
|
/* Einfluss von Artefakten */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* TODO (noch gibs keine) */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* W<>rfeln */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
aura = (rng_double() * d + rng_double() * d) / 2 + 1;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
aura *= MagicRegeneration();
|
|
|
|
|
|
|
|
|
|
return aura;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void regeneration_magiepunkte(void)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
region *r;
|
|
|
|
|
unit *u;
|
|
|
|
|
int aura, auramax;
|
|
|
|
|
double reg_aura;
|
|
|
|
|
int regen;
|
|
|
|
|
double mod;
|
|
|
|
|
|
|
|
|
|
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) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
struct building *b = inside_building(u);
|
|
|
|
|
const struct building_type *btype = b ? b->type : NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
reg_aura = regeneration(u);
|
|
|
|
|
|
|
|
|
|
/* Magierturm erh<72>ht die Regeneration um 75% */
|
|
|
|
|
/* Steinkreis erh<72>ht die Regeneration um 50% */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (btype)
|
|
|
|
|
reg_aura *= btype->auraregen;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* Bonus/Malus durch Zauber */
|
|
|
|
|
mod = get_curseeffect(u->attribs, C_AURA, 0);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (mod > 0) {
|
|
|
|
|
reg_aura = (reg_aura * mod) / 100.0;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Einfluss von Artefakten */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* TODO (noch gibs keine) */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* maximal Differenz bis Maximale-Aura regenerieren
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* mindestens 1 Aura pro Monat */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
regen = (int)reg_aura;
|
|
|
|
|
reg_aura -= regen;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (chance(reg_aura))
|
|
|
|
|
++regen;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
regen = MAX(1, regen);
|
|
|
|
|
regen = MIN((auramax - aura), regen);
|
|
|
|
|
|
|
|
|
|
aura += regen;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("regenaura",
|
|
|
|
|
"unit region amount", u, r, regen));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
set_spellpoints(u, MIN(aura, auramax));
|
|
|
|
|
|
|
|
|
|
/* Zum letzten Mal Spruchliste aktualisieren */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/*updatespelllist(u); */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean
|
2011-03-07 08:02:35 +01:00
|
|
|
|
verify_ship(region * r, unit * mage, const spell * sp, spllprm * spobj,
|
|
|
|
|
order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
ship *sh = findship(spobj->data.i);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sh != NULL && sh->region != r && (sp->sptyp & SEARCHLOCAL)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Burg muss in gleicher Region sein */
|
|
|
|
|
sh = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sh == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Burg nicht gefunden */
|
|
|
|
|
spobj->flag = TARGET_NOTFOUND;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellshipnotfound",
|
|
|
|
|
"unit region command id", mage, mage->region, ord, spobj->data.i));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->data.sh = sh;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean
|
2011-03-07 08:02:35 +01:00
|
|
|
|
verify_building(region * r, unit * mage, const spell * sp, spllprm * spobj,
|
|
|
|
|
order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
building *b = findbuilding(spobj->data.i);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (b != NULL && b->region != r && (sp->sptyp & SEARCHLOCAL)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Burg muss in gleicher Region sein */
|
|
|
|
|
b = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (b == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Burg nicht gefunden */
|
|
|
|
|
spobj->flag = TARGET_NOTFOUND;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellbuildingnotfound",
|
|
|
|
|
"unit region command id", mage, mage->region, ord, spobj->data.i));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->data.b = b;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
message *msg_unitnotfound(const struct unit * mage, struct order * ord,
|
|
|
|
|
const struct spllprm * spobj)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
/* Einheit nicht gefunden */
|
|
|
|
|
char tbuf[20];
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const char *uid;
|
|
|
|
|
|
|
|
|
|
if (spobj->typ == SPP_UNIT) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
uid = itoa36(spobj->data.i);
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(tbuf, "%s %s", LOC(mage->faction->locale,
|
2011-03-07 08:02:35 +01:00
|
|
|
|
parameters[P_TEMP]), itoa36(spobj->data.i));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
uid = tbuf;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return msg_message("unitnotfound_id",
|
2010-08-08 10:06:34 +02:00
|
|
|
|
"unit region command id", mage, mage->region, ord, uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean
|
2011-03-07 08:02:35 +01:00
|
|
|
|
verify_unit(region * r, unit * mage, const spell * sp, spllprm * spobj,
|
|
|
|
|
order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
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);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (u == NULL)
|
|
|
|
|
u = findnewunit(mage->region, mage->faction, spobj->data.i);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(!"shouldn't happen, this");
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (u != NULL && (sp->sptyp & SEARCHLOCAL)) {
|
|
|
|
|
if (u->region != r)
|
|
|
|
|
u = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
else if (sp->sptyp & TESTCANSEE) {
|
|
|
|
|
if (!cansee(mage->faction, r, u, 0) && !ucontact(u, mage)) {
|
|
|
|
|
u = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (u == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
unit *mage = co->magician.u;
|
|
|
|
|
const spell *sp = co->sp;
|
|
|
|
|
region *target_r = co->rt;
|
|
|
|
|
spellparameter *sa = co->par;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
*invalid = 0;
|
|
|
|
|
*resist = 0;
|
|
|
|
|
*success = 0;
|
|
|
|
|
|
|
|
|
|
if (sa && sa->length) {
|
|
|
|
|
/* zuerst versuchen wir vorher nicht gefundene Objekte zu finden.
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* 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];
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
switch (spobj->typ) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
case SPP_TEMP:
|
|
|
|
|
case SPP_UNIT:
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!verify_unit(target_r, mage, sp, spobj, co->order))
|
|
|
|
|
++ * invalid;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
case SPP_BUILDING:
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!verify_building(target_r, mage, sp, spobj, co->order))
|
|
|
|
|
++ * invalid;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
case SPP_SHIP:
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!verify_ship(target_r, mage, sp, spobj, co->order))
|
|
|
|
|
++ * invalid;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Nun folgen die Tests auf cansee und Magieresistenz */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
case SPP_TEMP:
|
|
|
|
|
case SPP_UNIT:
|
|
|
|
|
u = spobj->data.u;
|
|
|
|
|
|
|
|
|
|
if ((sp->sptyp & TESTRESISTANCE)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
&& target_resists_magic(mage, u, TYP_UNIT, 0)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Fehlermeldung */
|
|
|
|
|
spobj->data.i = u->no;
|
|
|
|
|
spobj->flag = TARGET_RESISTS;
|
|
|
|
|
++*resist;
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellunitresists",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"unit region command target",
|
|
|
|
|
mage, mage->region, co->order, u));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: Test auf Parteieigenschaft Magieresistsenz */
|
|
|
|
|
++*success;
|
|
|
|
|
break;
|
|
|
|
|
case SPP_BUILDING:
|
|
|
|
|
b = spobj->data.b;
|
|
|
|
|
|
|
|
|
|
if ((sp->sptyp & TESTRESISTANCE)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
&& target_resists_magic(mage, b, TYP_BUILDING, 0)) { /* Fehlermeldung */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spobj->data.i = b->no;
|
|
|
|
|
spobj->flag = TARGET_RESISTS;
|
|
|
|
|
++*resist;
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellbuildingresists",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"unit region command id",
|
|
|
|
|
mage, mage->region, co->order, spobj->data.i));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++*success;
|
|
|
|
|
break;
|
|
|
|
|
case SPP_SHIP:
|
|
|
|
|
sh = spobj->data.sh;
|
|
|
|
|
|
|
|
|
|
if ((sp->sptyp & TESTRESISTANCE)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
&& target_resists_magic(mage, sh, TYP_SHIP, 0)) { /* Fehlermeldung */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spobj->data.i = sh->no;
|
|
|
|
|
spobj->flag = TARGET_RESISTS;
|
|
|
|
|
++*resist;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order,
|
|
|
|
|
"spellshipresists", "ship", sh));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++*success;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SPP_REGION:
|
|
|
|
|
/* haben wir ein Regionsobjekt, dann wird auch dieses und
|
2011-03-07 08:02:35 +01:00
|
|
|
|
nicht target_r <EFBFBD>berpr<EFBFBD>ft. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
tr = spobj->data.r;
|
|
|
|
|
|
|
|
|
|
if ((sp->sptyp & TESTRESISTANCE)
|
2011-03-07 08:02:35 +01:00
|
|
|
|
&& target_resists_magic(mage, tr, TYP_REGION, 0)) { /* Fehlermeldung */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spobj->flag = TARGET_RESISTS;
|
|
|
|
|
++*resist;
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"unit region command", mage, mage->region, co->order));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
++*success;
|
|
|
|
|
break;
|
|
|
|
|
case SPP_INT:
|
|
|
|
|
case SPP_STRING:
|
|
|
|
|
++*success;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* der Zauber hat keine expliziten Parameter/Ziele, es kann sich
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* aber um einen Regionszauber handeln. Wenn notwendig hier die
|
|
|
|
|
* Magieresistenz der Region pr<EFBFBD>fen. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if ((sp->sptyp & REGIONSPELL)) {
|
|
|
|
|
/* Zielobjekt Region anlegen */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spllprm *spobj = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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 */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
|
|
|
|
|
"unit region command", mage, mage->region, co->order));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spobj->flag = TARGET_RESISTS;
|
|
|
|
|
++*resist;
|
|
|
|
|
} else {
|
|
|
|
|
++*success;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
++*success;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
++*success;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/* 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
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Elemente free'en */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (i = 0; i < pa->length; i++) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
switch (pa->param[i]->typ) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
case SPP_STRING:
|
|
|
|
|
free(pa->param[i]->data.s);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
free(pa->param[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (pa->param)
|
|
|
|
|
free(pa->param);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* struct free'en */
|
|
|
|
|
free(pa);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
assert(param[0]);
|
|
|
|
|
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = SPP_STRING;
|
|
|
|
|
spobj->data.xs = strdup(param[0]);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
assert(param[0]);
|
|
|
|
|
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = SPP_INT;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spobj->data.i = atoi((char *)param[0]);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int id = atoi36((const char *)param[0]);
|
|
|
|
|
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = SPP_SHIP;
|
|
|
|
|
spobj->data.i = id;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int id = atoi36((const char *)param[0]);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = SPP_BUILDING;
|
|
|
|
|
spobj->data.i = id;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2011-03-07 08:02:35 +01:00
|
|
|
|
addparam_region(const char *const param[], spllprm ** spobjp, const unit * u,
|
|
|
|
|
order * ord, plane * pl)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
assert(param[0]);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (param[1] == 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Fehler: Zielregion vergessen */
|
|
|
|
|
cmistake(u, ord, 194, MSG_MAGIC);
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int tx = atoi((const char *)param[0]), ty = atoi((const char *)param[1]);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int x = rel_to_abs(pl, u->faction, tx, 0);
|
|
|
|
|
int y = rel_to_abs(pl, u->faction, ty, 1);
|
|
|
|
|
region *rt;
|
|
|
|
|
|
|
|
|
|
pnormalize(&x, &y, pl);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
rt = findregion(x, y);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (rt != NULL) {
|
|
|
|
|
spllprm *spobj = *spobjp = malloc(sizeof(spllprm));
|
2010-08-08 10:06:34 +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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2011-03-07 08:02:35 +01:00
|
|
|
|
addparam_unit(const char *const param[], spllprm ** spobjp, const unit * u,
|
|
|
|
|
order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
spllprm *spobj;
|
|
|
|
|
int i = 0;
|
|
|
|
|
sppobj_t otype = SPP_UNIT;
|
|
|
|
|
|
|
|
|
|
*spobjp = NULL;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (findparam(param[0], u->faction->locale) == P_TEMP) {
|
|
|
|
|
if (param[1] == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Fehler: Ziel vergessen */
|
|
|
|
|
cmistake(u, ord, 203, MSG_MAGIC);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
++i;
|
|
|
|
|
otype = SPP_TEMP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spobj = *spobjp = malloc(sizeof(spllprm));
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = otype;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
spobj->data.i = atoi36((const char *)param[i]);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01: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,
|
|
|
|
|
const char *syntax, const char *const param[], int size, struct order *ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
boolean fail = false;
|
|
|
|
|
int i = 0;
|
|
|
|
|
int p = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const char *c;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spellparameter *par;
|
|
|
|
|
int minlen = 0;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
for (c = syntax; *c != 0; ++c) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* this makes sure that:
|
|
|
|
|
* minlen("kc?") = 0
|
|
|
|
|
* minlen("kc+") = 1
|
|
|
|
|
* minlen("cccc+c?") = 4
|
|
|
|
|
*/
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (*c == '?')
|
|
|
|
|
--minlen;
|
|
|
|
|
else if (*c != '+' && *c != 'k')
|
|
|
|
|
++minlen;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
c = syntax;
|
|
|
|
|
|
|
|
|
|
/* mindestens ein Ziel (Ziellose Zauber werden nicht
|
|
|
|
|
* geparst) */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (minlen && size == 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Fehler: Ziel vergessen */
|
|
|
|
|
cmistake(u, ord, 203, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
par = malloc(sizeof(spellparameter));
|
|
|
|
|
par->length = size;
|
|
|
|
|
if (!size) {
|
|
|
|
|
par->param = NULL;
|
|
|
|
|
return par;
|
|
|
|
|
}
|
|
|
|
|
par->param = malloc(size * sizeof(spllprm *));
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (!fail && *c && i < size && param[i] != NULL) {
|
|
|
|
|
spllprm *spobj = NULL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
param_t pword;
|
|
|
|
|
int j = -1;
|
|
|
|
|
switch (*c) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
case '?':
|
|
|
|
|
/* tja. das sollte moeglichst nur am Ende passieren,
|
|
|
|
|
* weil sonst die kacke dampft. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
j = 0;
|
|
|
|
|
++c;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
assert(*c == 0);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
case '+':
|
|
|
|
|
/* das vorhergehende Element kommt ein oder mehrmals vor, wir
|
|
|
|
|
* springen zum key zur<EFBFBD>ck */
|
|
|
|
|
j = 0;
|
|
|
|
|
--c;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
break;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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;
|
|
|
|
|
pword = findparam(param[i++], u->faction->locale);
|
|
|
|
|
switch (pword) {
|
|
|
|
|
case P_REGION:
|
|
|
|
|
spobj = malloc(sizeof(spllprm));
|
|
|
|
|
spobj->flag = 0;
|
|
|
|
|
spobj->typ = SPP_REGION;
|
|
|
|
|
spobj->data.r = u->region;
|
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
j = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (j < 0)
|
|
|
|
|
fail = true;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (spobj != NULL)
|
|
|
|
|
par->param[p++] = spobj;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
i += j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* im Endeffekt waren es evtl. nur p parameter (wegen TEMP) */
|
|
|
|
|
par->length = p;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (fail || par->length < minlen) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
cmistake(u, ord, 209, MSG_MAGIC);
|
|
|
|
|
free_spellparameter(par);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return par;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
castorder *new_castorder(void *u, unit * u2, const spell * sp, region * r,
|
|
|
|
|
int lev, double force, int range, struct order * ord, spellparameter * p)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
castorder *corder;
|
|
|
|
|
|
|
|
|
|
corder = calloc(1, sizeof(castorder));
|
|
|
|
|
corder->magician.u = u;
|
|
|
|
|
corder->familiar = u2;
|
|
|
|
|
corder->sp = sp;
|
|
|
|
|
corder->level = lev;
|
|
|
|
|
corder->force = force;
|
|
|
|
|
corder->rt = r;
|
|
|
|
|
corder->distance = range;
|
|
|
|
|
corder->order = copy_order(ord);
|
|
|
|
|
corder->par = p;
|
|
|
|
|
|
|
|
|
|
return corder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (cll->begin == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
cll->end = &cll->begin;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
*cll->end = co;
|
|
|
|
|
cll->end = &co->next;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void free_castorders(castorder * co)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
castorder *co2;
|
|
|
|
|
|
|
|
|
|
while (co) {
|
|
|
|
|
co2 = co;
|
|
|
|
|
co = co->next;
|
|
|
|
|
if (co2->par) {
|
|
|
|
|
free_spellparameter(co2->par);
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (co2->order)
|
|
|
|
|
free_order(co2->order);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
free(co2);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
/***
|
|
|
|
|
** at_familiarmage
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
typedef struct familiar_data {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *mage;
|
|
|
|
|
unit *familiar;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} famililar_data;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean is_familiar(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01: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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *u = (unit *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
write_unit_reference(u, store);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int sm_familiar(const unit * u, const region * r, skill_t sk, int value)
|
|
|
|
|
{ /* skillmod */
|
|
|
|
|
if (sk == SK_MAGIC)
|
|
|
|
|
return value;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
else {
|
|
|
|
|
int mod;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *familiar = get_familiar(u);
|
|
|
|
|
if (familiar == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* the familiar is dead */
|
|
|
|
|
return value;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
mod = eff_skill(familiar, sk, r) / 2;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (r != familiar->region) {
|
|
|
|
|
mod /= distance(r, familiar->region);
|
|
|
|
|
}
|
|
|
|
|
return value + mod;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void set_familiar(unit * mage, unit * familiar)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
/* if the skill modifier for the mage does not yet exist, add it */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
|
|
|
|
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
|
|
|
|
|
skillmod_data *smd = (skillmod_data *) an->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
smd->special = sm_familiar;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
smd->skill = NOSKILL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = a_find(mage->attribs, &at_familiar);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a_add(&mage->attribs, a_new(&at_familiar));
|
|
|
|
|
a->data.v = familiar;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else
|
|
|
|
|
assert(!a->data.v || a->data.v == familiar);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* TODO: Diese Attribute beim Tod des Familiars entfernen: */
|
|
|
|
|
|
|
|
|
|
a = a_find(familiar->attribs, &at_familiarmage);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a_add(&familiar->attribs, a_new(&at_familiarmage));
|
|
|
|
|
a->data.v = mage;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} 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
|
|
|
|
{
|
|
|
|
|
attrib *a = a_find(mage->attribs, &at_familiar);
|
|
|
|
|
attrib *an;
|
|
|
|
|
skillmod_data *smd;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a != NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a_remove(&mage->attribs, a);
|
|
|
|
|
}
|
|
|
|
|
a = a_find(mage->attribs, &at_skillmod);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (a && a->type == &at_skillmod) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
an = a->next;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
smd = (skillmod_data *) a->data.v;
|
|
|
|
|
if (smd->special == sm_familiar)
|
|
|
|
|
a_remove(&mage->attribs, a);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = an;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
boolean create_newfamiliar(unit * mage, unit * familiar)
|
2010-08-08 10:06:34 +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);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
|
|
|
|
if (afam == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
afam = a_add(&mage->attribs, a_new(&at_familiar));
|
|
|
|
|
}
|
|
|
|
|
afam->data.v = familiar;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (amage == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a_find(mage->attribs, &at_skillmod);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
while (a && a->type == &at_skillmod) {
|
|
|
|
|
skillmod_data *smd = (skillmod_data *) a->data.v;
|
|
|
|
|
if (smd->special == sm_familiar)
|
|
|
|
|
break;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a->next;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
|
|
|
|
attrib *an = a_add(&mage->attribs, a_new(&at_skillmod));
|
|
|
|
|
skillmod_data *smd = (skillmod_data *) an->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
smd->special = sm_familiar;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
smd->skill = NOSKILL;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int resolve_familiar(variant data, void *addr)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *familiar;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int result = resolve_unit(data, &familiar);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (result == 0 && familiar) {
|
|
|
|
|
attrib *a = a_find(familiar->attribs, &at_familiarmage);
|
|
|
|
|
if (a != NULL && a->data.v) {
|
|
|
|
|
unit *mage = (unit *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
set_familiar(mage, familiar);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
*(unit **) addr = familiar;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int result =
|
|
|
|
|
read_reference(&a->data.v, store, read_unit_reference, resolve_familiar);
|
|
|
|
|
if (result == 0 && a->data.v == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
|
}
|
|
|
|
|
return AT_READ_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* clones */
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
void create_newclone(unit * mage, unit * clone)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
attrib *a;
|
|
|
|
|
|
|
|
|
|
a = a_find(mage->attribs, &at_clone);
|
|
|
|
|
if (a == NULL) {
|
|
|
|
|
a = a_add(&mage->attribs, a_new(&at_clone));
|
|
|
|
|
a->data.v = clone;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else
|
|
|
|
|
assert(!a->data.v || a->data.v == clone);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* TODO: Diese Attribute beim Tod des Klons entfernen: */
|
|
|
|
|
|
|
|
|
|
a = a_find(clone->attribs, &at_clonemage);
|
|
|
|
|
if (a == NULL) {
|
|
|
|
|
a = a_add(&clone->attribs, a_new(&at_clonemage));
|
|
|
|
|
a->data.v = mage;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else
|
|
|
|
|
assert(!a->data.v || a->data.v == mage);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* Wenn der Magier stirbt, wird das in destroy_unit abgefangen.
|
|
|
|
|
* Kein Trigger, zu kompliziert. */
|
|
|
|
|
|
|
|
|
|
/* Wenn der Klon stirbt, dann bekommt der Magier einen Schock */
|
|
|
|
|
add_trigger(&clone->attribs, "destroy", trigger_clonedied(mage));
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static void set_clone(unit * mage, unit * clone)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
attrib *a;
|
|
|
|
|
|
|
|
|
|
a = a_find(mage->attribs, &at_clone);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a_add(&mage->attribs, a_new(&at_clone));
|
|
|
|
|
a->data.v = clone;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} else
|
|
|
|
|
assert(!a->data.v || a->data.v == clone);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
a = a_find(clone->attribs, &at_clonemage);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
a = a_add(&clone->attribs, a_new(&at_clonemage));
|
|
|
|
|
a->data.v = mage;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
} 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
|
|
|
|
{
|
|
|
|
|
attrib *a = a_find(mage->attribs, &at_clone);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (a)
|
|
|
|
|
return (unit *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static int resolve_clone(variant data, void *addr)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *clone;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int result = resolve_unit(data, &clone);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (result == 0 && clone) {
|
|
|
|
|
attrib *a = a_find(clone->attribs, &at_clonemage);
|
|
|
|
|
if (a != NULL && a->data.v) {
|
|
|
|
|
unit *mage = (unit *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
set_clone(mage, clone);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
*(unit **) addr = clone;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int result =
|
|
|
|
|
read_reference(&a->data.v, store, read_unit_reference, resolve_clone);
|
|
|
|
|
if (result == 0 && a->data.v == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
|
}
|
|
|
|
|
return AT_READ_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *mage;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int result = resolve_unit(data, &mage);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (result == 0 && mage) {
|
|
|
|
|
attrib *a = a_find(mage->attribs, &at_familiar);
|
|
|
|
|
if (a != NULL && a->data.v) {
|
|
|
|
|
unit *familiar = (unit *) a->data.v;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
set_familiar(mage, familiar);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
*(unit **) addr = mage;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
int result =
|
|
|
|
|
read_reference(&a->data.v, store, read_unit_reference, resolve_mage);
|
|
|
|
|
if (result == 0 && a->data.v == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return AT_READ_FAIL;
|
|
|
|
|
}
|
|
|
|
|
return AT_READ_OK;
|
|
|
|
|
}
|
|
|
|
|
|
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 */
|
|
|
|
|
{
|
2011-03-07 08:02:35 +01: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 = {
|
|
|
|
|
"familiarmage",
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
age_unit,
|
|
|
|
|
a_write_unit,
|
|
|
|
|
read_magician,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
attrib_type at_familiar = {
|
|
|
|
|
"familiar",
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
age_unit,
|
|
|
|
|
a_write_unit,
|
|
|
|
|
read_familiar,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
attrib_type at_clonemage = {
|
|
|
|
|
"clonemage",
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
age_unit,
|
|
|
|
|
a_write_unit,
|
|
|
|
|
read_magician,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
attrib_type at_clone = {
|
|
|
|
|
"clone",
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
age_unit,
|
|
|
|
|
a_write_unit,
|
|
|
|
|
read_clone,
|
|
|
|
|
ATF_UNIQUE
|
|
|
|
|
};
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *get_familiar(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_familiar);
|
|
|
|
|
if (a != NULL) {
|
|
|
|
|
unit *u = (unit *) a->data.v;
|
|
|
|
|
if (u->number > 0)
|
|
|
|
|
return u;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *get_familiar_mage(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_familiarmage);
|
|
|
|
|
if (a != NULL) {
|
|
|
|
|
unit *u = (unit *) a->data.v;
|
|
|
|
|
if (u->number > 0)
|
|
|
|
|
return u;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *get_clone(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_clone);
|
|
|
|
|
if (a != NULL) {
|
|
|
|
|
unit *u = (unit *) a->data.v;
|
|
|
|
|
if (u->number > 0)
|
|
|
|
|
return u;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *get_clone_mage(const unit * u)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
attrib *a = a_find(u->attribs, &at_clonemage);
|
|
|
|
|
if (a != NULL) {
|
|
|
|
|
unit *u = (unit *) a->data.v;
|
|
|
|
|
if (u->number > 0)
|
|
|
|
|
return u;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static boolean is_moving_ship(const region * r, const ship * sh)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
const unit *u = shipowner(sh);
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (u)
|
|
|
|
|
switch (get_keyword(u->thisorder)) {
|
|
|
|
|
case K_ROUTE:
|
|
|
|
|
case K_MOVE:
|
|
|
|
|
case K_FOLLOW:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
static castorder *cast_cmd(unit * u, order * ord)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
2011-03-07 08:02:35 +01:00
|
|
|
|
region *r = u->region;
|
|
|
|
|
region *target_r = r;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int level, range;
|
|
|
|
|
unit *familiar = NULL, *mage = u;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const char *s;
|
|
|
|
|
spell *sp;
|
|
|
|
|
plane *pl;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
spellparameter *args = NULL;
|
|
|
|
|
|
|
|
|
|
if (LongHunger(u)) {
|
|
|
|
|
cmistake(u, ord, 224, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
pl = rplane(r);
|
|
|
|
|
if (pl && fval(pl, PFL_NOMAGIC)) {
|
|
|
|
|
cmistake(u, ord, 269, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
level = eff_skill(u, SK_MAGIC, r);
|
|
|
|
|
|
|
|
|
|
init_tokens(ord);
|
|
|
|
|
skip_token();
|
|
|
|
|
s = getstrtoken();
|
|
|
|
|
/* f<>r Syntax ' STUFE x REGION y z ' */
|
|
|
|
|
if (findparam(s, u->faction->locale) == 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();
|
|
|
|
|
}
|
|
|
|
|
if (findparam(s, u->faction->locale) == P_REGION) {
|
|
|
|
|
int t_x = getint();
|
|
|
|
|
int t_y = getint();
|
2011-03-07 08:02:35 +01:00
|
|
|
|
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);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
pnormalize(&t_x, &t_y, pl);
|
|
|
|
|
target_r = findregion(t_x, t_y);
|
|
|
|
|
if (!target_r) {
|
|
|
|
|
/* Fehler "Die Region konnte nicht verzaubert werden" */
|
|
|
|
|
ADDMSG(&mage->faction->msgs, msg_message("spellregionresists",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"unit region command", mage, mage->region, ord));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
s = getstrtoken();
|
|
|
|
|
}
|
|
|
|
|
/* f<>r Syntax ' REGION x y STUFE z '
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* hier nach REGION nochmal auf STUFE pr<EFBFBD>fen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (findparam(s, u->faction->locale) == 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();
|
|
|
|
|
}
|
|
|
|
|
if (!s[0] || strlen(s) == 0) {
|
|
|
|
|
/* Fehler "Es wurde kein Zauber angegeben" */
|
|
|
|
|
cmistake(u, ord, 172, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
sp = get_spellfromtoken(u, s, u->faction->locale);
|
|
|
|
|
|
|
|
|
|
/* Vertraute k<>nnen auch Zauber sprechen, die sie selbst nicht
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* k<EFBFBD>nnen. get_spellfromtoken findet aber nur jene Spr<EFBFBD>che, die
|
|
|
|
|
* die Einheit beherrscht. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (sp == NULL && is_familiar(u)) {
|
|
|
|
|
familiar = u;
|
|
|
|
|
mage = get_familiar_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (mage != NULL)
|
|
|
|
|
sp = get_spellfromtoken(mage, s, mage->faction->locale);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sp == NULL) {
|
|
|
|
|
/* Fehler 'Spell not found' */
|
|
|
|
|
cmistake(u, ord, 173, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* um testen auf spruchnamen zu unterbinden sollte vor allen
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* fehlermeldungen die anzeigen das der magier diesen Spruch
|
|
|
|
|
* nur in diese Situation nicht anwenden kann, noch eine
|
|
|
|
|
* einfache Sicherheitspr<EFBFBD>fung kommen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* Auf dem Ozean Zaubern als quasi-langer Befehl k<>nnen
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* normalerweise nur Meermenschen, ausgenommen explizit als
|
|
|
|
|
* OCEANCASTABLE deklarierte Spr<EFBFBD>che */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (fval(r->terrain, SEA_REGION)) {
|
|
|
|
|
if (u->race != new_race[RC_AQUARIAN]
|
2011-03-07 08:02:35 +01:00
|
|
|
|
&& !fval(u->race, RCF_SWIM)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
&& !(sp->sptyp & OCEANCASTABLE)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
/* Fehlermeldung */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("spellfail_onocean",
|
2010-08-08 10:06:34 +02:00
|
|
|
|
"unit region command", u, u->region, ord));
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* Auf bewegenden Schiffen kann man nur explizit als
|
|
|
|
|
* ONSHIPCAST deklarierte Zauber sprechen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
} else if (u->ship) {
|
|
|
|
|
if (is_moving_ship(r, u->ship)) {
|
|
|
|
|
if (!(sp->sptyp & ONSHIPCAST)) {
|
|
|
|
|
/* Fehler: "Diesen Spruch kann man nicht auf einem sich
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* bewegenden Schiff stehend zaubern" */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* richten" */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
cmistake(u, ord, 176, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (range > 1024) { /* (2^10) weiter als 10 Regionen entfernt */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "spellfail::nocontact",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"target", target_r));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Stufenangabe bei nicht Stufenvariierbaren Spr<70>chen abfangen */
|
|
|
|
|
if (!(sp->sptyp & SPELLLEVEL)) {
|
|
|
|
|
int ilevel = eff_skill(u, SK_MAGIC, u->region);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (ilevel != level) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
level = ilevel;
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("spellfail::nolevel",
|
2011-03-07 08:02:35 +01:00
|
|
|
|
"mage region command", u, u->region, ord));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Vertrautenmagie */
|
|
|
|
|
/* Kennt der Vertraute den Spruch, so zaubert er ganz normal.
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* 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. */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (familiar || is_familiar(u)) {
|
|
|
|
|
if ((sp->sptyp & NOTFAMILIARCAST)) {
|
|
|
|
|
/* Fehler: "Diesen Spruch kann der Vertraute nicht zaubern" */
|
|
|
|
|
cmistake(u, ord, 177, MSG_MAGIC);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (!knowsspell(r, u, sp)) { /* Magier zaubert durch Vertrauten */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
mage = get_familiar_mage(u);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (range > 1) { /* Fehler! Versucht zu Farcasten */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_farcast",
|
|
|
|
|
"mage", mage));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (distance(mage->region, r) > eff_skill(mage, SK_MAGIC, mage->region)) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "familiar_toofar",
|
|
|
|
|
"mage", mage));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* mage auf magier setzen, level anpassen, range f<>r Erh<72>hung
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* der Spruchkosten nutzen, langen Befehl des Magiers
|
|
|
|
|
* l<EFBFBD>schen, zaubern kann er noch */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
range *= 2;
|
|
|
|
|
set_order(&mage->thisorder, NULL);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
level = MIN(level, eff_skill(mage, SK_MAGIC, mage->region) / 2);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
familiar = u;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Weitere Argumente zusammenbasteln */
|
|
|
|
|
if (sp->parameter) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
char **params = malloc(2 * sizeof(char *));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int p = 0, size = 2;
|
|
|
|
|
for (;;) {
|
|
|
|
|
s = getstrtoken();
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (*s == 0)
|
|
|
|
|
break;
|
|
|
|
|
if (p + 1 >= size) {
|
|
|
|
|
size *= 2;
|
|
|
|
|
params = realloc(params, sizeof(char *) * size);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
params[p++] = strdup(s);
|
|
|
|
|
}
|
|
|
|
|
params[p] = 0;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
args =
|
|
|
|
|
add_spellparameter(target_r, mage, sp->parameter,
|
|
|
|
|
(const char *const *)params, p, ord);
|
|
|
|
|
for (p = 0; params[p]; ++p)
|
|
|
|
|
free(params[p]);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
free(params);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (args == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* Syntax war falsch */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-07 08:02:35 +01:00
|
|
|
|
return new_castorder(mage, 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
|
|
|
|
{
|
|
|
|
|
region *r;
|
|
|
|
|
int rank;
|
|
|
|
|
castorder *co;
|
|
|
|
|
spellrank spellranks[MAX_SPELLRANK];
|
|
|
|
|
|
|
|
|
|
memset(spellranks, 0, sizeof(spellranks));
|
|
|
|
|
|
|
|
|
|
for (r = regions; r; r = r->next) {
|
|
|
|
|
unit *u;
|
|
|
|
|
for (u = r->units; u; u = u->next) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
order *ord;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (u->number <= 0 || u->race == new_race[RC_SPELL])
|
2010-08-08 10:06:34 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (u->race == new_race[RC_INSECT] && r_insectstalled(r) &&
|
2011-03-07 08:02:35 +01:00
|
|
|
|
!is_cursed(u->attribs, C_KAELTESCHUTZ, 0))
|
2010-08-08 10:06:34 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (fval(u, UFL_WERE | UFL_LONGACTION)) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ord = u->orders; ord; ord = ord->next) {
|
|
|
|
|
if (get_keyword(ord) == K_CAST) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
castorder *co = cast_cmd(u, ord);
|
|
|
|
|
fset(u, UFL_LONGACTION | UFL_NOTMOVING);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (co) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
const spell *sp = co->sp;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
add_castorder(&spellranks[sp->rank], co);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
order *ord = co->order;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
int invalid, resist, success, cast_level = co->level;
|
|
|
|
|
boolean fumbled = false;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
unit *u = co->magician.u;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
const spell *sp = co->sp;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
region *target_r = co->rt;
|
2010-08-08 10:06:34 +02:00
|
|
|
|
|
|
|
|
|
/* 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
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* gezaubert, co->level ist aber defaultm<EFBFBD><EFBFBD>ig Stufe des Magiers */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (spl_costtyp(sp) != SPC_FIX) {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("missing_components",
|
|
|
|
|
"unit spell level", u, sp, cast_level));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pr<50>fen, ob die realen Kosten f<>r die gew<65>nschten Stufe bezahlt
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* werden k<EFBFBD>nnen */
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (cancast(u, sp, co->level, co->distance, ord) == false) {
|
|
|
|
|
/* 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;
|
2011-03-07 08:02:35 +01:00
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("missing_force",
|
|
|
|
|
"unit spell level", u, sp, co->level));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ziele auf Existenz pr<70>fen und Magieresistenz feststellen. Wurde
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* 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
|
|
|
|
|
*/
|
2010-08-08 10:06:34 +02:00
|
|
|
|
verify_targets(co, &invalid, &resist, &success);
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (success + resist == 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* kein Ziel gefunden, Fehlermeldungen sind in verify_targets */
|
|
|
|
|
/* keine kosten f<>r den zauber */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
continue; /* <20>u<EFBFBD>ere Schleife, n<>chster Zauberer */
|
|
|
|
|
} else if (co->force > 0 && resist > 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
/* einige oder alle Ziele waren magieresistent */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (success == 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
co->force = 0;
|
|
|
|
|
/* zwar wurde mindestens ein Ziel gefunden, das widerstand
|
2011-03-07 08:02:35 +01:00
|
|
|
|
* jedoch dem Zauber. Kosten abziehen und abbrechen. */
|
|
|
|
|
ADDMSG(&u->faction->msgs, msg_message("spell_resist",
|
|
|
|
|
"unit region spell", u, r, sp));
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Auch f<>r Patzer gibt es Erfahrung, m<>ssen die Spruchkosten
|
|
|
|
|
* bezahlt werden und die nachfolgenden Spr<EFBFBD>che werden teurer */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (co->force > 0) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
if (fumble(target_r, u, sp, co->level)) {
|
|
|
|
|
/* zuerst bezahlen, dann evt in do_fumble alle Aura verlieren */
|
|
|
|
|
fumbled = true;
|
|
|
|
|
} else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (sp->sp_function == NULL) {
|
2010-08-08 10:06:34 +02:00
|
|
|
|
log_error(("spell '%s' has no function.\n", sp->sname));
|
|
|
|
|
co->level = 0;
|
|
|
|
|
} else {
|
2011-03-07 08:02:35 +01:00
|
|
|
|
co->level = ((nspell_f) sp->sp_function) (co);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
if (co->level <= 0) {
|
|
|
|
|
/* Kosten nur f<>r real ben<65>tige Stufe berechnen */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pay_spell(u, sp, co->level, co->distance);
|
|
|
|
|
/* erst bezahlen, dann Kostenz<6E>hler erh<72>hen */
|
2011-03-07 08:02:35 +01:00
|
|
|
|
if (fumbled)
|
|
|
|
|
do_fumble(co);
|
2010-08-08 10:06:34 +02:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
return LOC(lang, mkname("spellinfo", sp->sname));
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
return LOC(lang, mkname("spell", sp->sname));
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
return LOC(lang, mkname("spell", ctype->cname));
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-07 08:02:35 +01:00
|
|
|
|
struct quicklist **get_spelllist(struct sc_mage *mage, struct faction *f)
|
2010-08-08 10:06:34 +02:00
|
|
|
|
{
|
|
|
|
|
if (mage) {
|
|
|
|
|
return &mage->spells;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|