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