1 /***************************************************************************
2 spell.cpp - Spell and magic school classes
3 -------------------
4 begin : Sun Sep 28 2003
5 copyright : (C) 2003 by Gabor Torok
6 email : cctorok@yahoo.com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17 #include "../common/constants.h"
18 #include "spell.h"
19 #include "rpg.h"
20
21 // ###### MS Visual C++ specific ######
22 #if defined(_MSC_VER) && defined(_DEBUG)
23 # define new DEBUG_NEW
24 # undef THIS_FILE
25 static char THIS_FILE[] = __FILE__;
26 #endif
27
28 using namespace std;
29
30 MagicSchool *MagicSchool::schools[10];
31 int MagicSchool::schoolCount = 0;
32 map<string, Spell*> Spell::spellMap;
33 map<string, MagicSchool*> MagicSchool::schoolMap;
34
35
36 // FIXME: move this to some other location if needed elsewhere (rpg/dice.cpp?)
Dice(char const * s)37 Dice::Dice( char const* s ) {
38 strcpy( this->s, s );
39
40 char tmp[ S_SIZE ];
41 strcpy( tmp, s );
42 if ( strchr( tmp, 'd' ) ) {
43 count = atoi( strtok( tmp, "d" ) );
44 sides = atoi( strtok( NULL, "+" ) );
45 char *p = strtok( NULL, "+" );
46 if ( p ) mod = atoi( p );
47 else mod = 0;
48 } else {
49 count = 0;
50 sides = 0;
51 mod = atoi( s );
52 }
53
54 // cerr << "DICE: count=" << count << " sides=" << sides << " mod=" << mod << " str=" << this->s << endl;
55 // cerr << "\troll 1:" << roll() << endl;
56 // cerr << "\troll 2:" << roll() << endl;
57 // cerr << "\troll 3:" << roll() << endl;
58 }
59
Dice(int count,int sides,int mod)60 Dice::Dice( int count, int sides, int mod ) {
61 this->count = count;
62 this->sides = sides;
63 this->mod = mod;
64 if ( mod ) {
65 snprintf( s, S_SIZE, "%dd%d+%d", count, sides, mod );
66 } else {
67 snprintf( s, S_SIZE, "%dd%d", count, sides );
68 }
69 }
70
~Dice()71 Dice::~Dice() {
72 }
73
74
75
76
MagicSchool(char const * name,char const * displayName,char const * deity,int skill,int resistSkill,float alignment,float red,float green,float blue,char const * symbol)77 MagicSchool::MagicSchool( char const* name, char const* displayName, char const* deity, int skill, int resistSkill, float alignment, float red, float green, float blue, char const* symbol ) {
78 this->name = name;
79 this->displayName = displayName;
80 this->shortName = this->name.substr(0, this->name.find(' '));
81 this->deity = deity;
82 strcpy( this->deityDescription, "" );
83 this->skill = skill;
84 this->resistSkill = resistSkill;
85 this->red = red;
86 this->green = green;
87 this->blue = blue;
88 this->baseAlignment = alignment;
89 this->symbol = symbol;
90 }
91
~MagicSchool()92 MagicSchool::~MagicSchool() {
93 for ( size_t i = 0; i < spells.size(); ++i ) {
94 delete spells[i];
95 }
96 spells.clear();
97 }
98
99 #define UPDATE_MESSAGE N_("Loading Spells")
100
initMagic()101 void MagicSchool::initMagic() {
102 ConfigLang *config = ConfigLang::load( "config/spell.cfg" );
103 vector<ConfigNode*> *v = config->getDocument()->
104 getChildrenByName( "magic_school" );
105
106 MagicSchool *current = NULL;
107 Spell *currentSpell = NULL;
108 char name[255], displayName[255], notes[255], dice[255];
109 char line[2000], symbol[255], targetTypeStr[255], align[255] /*, prereqName[255]*/;
110 float alignment;
111
112 for ( unsigned int i = 0; i < v->size(); i++ ) {
113 ConfigNode *node = ( *v )[i];
114
115 config->setUpdate( _( UPDATE_MESSAGE ), i, v->size() );
116
117 strcpy( name, node->getValueAsString( "name" ) );
118 strcpy( displayName, node->getValueAsString( "display_name" ) );
119 strcpy( notes, node->getValueAsString( "deity" ) );
120 int skill = Skill::getSkillIndexByName( node->getValueAsString( "skill" ) );
121 int resistSkill = Skill::getSkillIndexByName( node->getValueAsString( "resist_skill" ) );
122 strcpy( line, node->getValueAsString( "rgb" ) );
123 strcpy( align, node->getValueAsString( "base_alignment" ) );
124 if ( strcmp( align, "chaotic" ) == 0 ) {
125 alignment = ALIGNMENT_CHAOTIC;
126 } else if ( strcmp( align, "neutral" ) == 0 ) {
127 alignment = ALIGNMENT_NEUTRAL;
128 } else if ( strcmp( align, "lawful" ) == 0 ) {
129 alignment = ALIGNMENT_LAWFUL;
130 }
131 float red = static_cast<float>( strtod( strtok( line, "," ), NULL ) );
132 float green = static_cast<float>( strtod( strtok( NULL, "," ), NULL ) );
133 float blue = static_cast<float>( strtod( strtok( NULL, "," ), NULL ) );
134 strcpy( symbol, node->getValueAsString( "symbol" ) );
135
136 current = new MagicSchool( name, displayName, notes, skill, resistSkill, alignment, red, green, blue, symbol );
137
138 strcpy( line, node->getValueAsString( "deity_description" ) );
139 if ( strlen( line ) ) current->addToDeityDescription( line );
140
141 schools[schoolCount++] = current;
142 string nameStr = name;
143 schoolMap[nameStr] = current;
144
145 vector<ConfigNode*> *vv = node->getChildrenByName( "low_donate" );
146 for ( unsigned int i = 0; vv && i < vv->size(); i++ ) {
147 ConfigNode *node2 = ( *vv )[i];
148 current->lowDonate.push_back( node2->getValueAsString( "text" ) );
149 }
150 vv = node->getChildrenByName( "neutral_donate" );
151 for ( unsigned int i = 0; vv && i < vv->size(); i++ ) {
152 ConfigNode *node2 = ( *vv )[i];
153 current->neutralDonate.push_back( node2->getValueAsString( "text" ) );
154 }
155 vv = node->getChildrenByName( "high_donate" );
156 for ( unsigned int i = 0; vv && i < vv->size(); i++ ) {
157 ConfigNode *node2 = ( *vv )[i];
158 current->highDonate.push_back( node2->getValueAsString( "text" ) );
159 }
160
161 vv = node->getChildrenByName( "spell" );
162 for ( unsigned int i = 0; vv && i < vv->size(); i++ ) {
163 ConfigNode *node2 = ( *vv )[i];
164
165 strcpy( name, node2->getValueAsString( "name" ) );
166 strcpy( displayName, node2->getValueAsString( "display_name" ) );
167 strcpy( symbol, node2->getValueAsString( "symbol" ) );
168 int level = node2->getValueAsInt( "level" );
169 int mp = node2->getValueAsInt( "mp" );
170 int exp = node2->getValueAsInt( "mp" );
171 int failureRate = node2->getValueAsInt( "failureRate" );
172 strcpy( dice, node2->getValueAsString( "action" ) );
173
174 int distance = node2->getValueAsInt( "distance" );
175 if ( distance < static_cast<int>( MIN_DISTANCE ) )
176 distance = static_cast<int>( MIN_DISTANCE );
177 int targetType = ( !strcmp( node2->getValueAsString( "targetType" ), "single" ) ? SINGLE_TARGET : GROUP_TARGET );
178 int speed = node2->getValueAsInt( "speed" );
179 int effect = Constants::getEffectByName( node2->getValueAsString( "effect" ) );
180 strcpy( targetTypeStr, node2->getValueAsString( "target" ) );
181 bool creatureTarget = ( strchr( targetTypeStr, 'C' ) != NULL );
182 bool locationTarget = ( strchr( targetTypeStr, 'L' ) != NULL );
183 bool itemTarget = ( strchr( targetTypeStr, 'I' ) != NULL );
184 bool partyTarget = ( strchr( targetTypeStr, 'P' ) != NULL );
185 bool doorTarget = ( strchr( targetTypeStr, 'D' ) != NULL );
186 strcpy( line, node2->getValueAsString( "icon" ) );
187 int iconTileX = atoi( strtok( line, "," ) ) - 1;
188 int iconTileY = atoi( strtok( NULL, "," ) ) - 1;
189
190 // Friendly/Hostile marker
191 bool friendly = node2->getValueAsBool( "friendly" );
192
193 int stateModPrereq = -1;
194 strcpy( line, node2->getValueAsString( "prerequisite" ) );
195 if ( strlen( line ) ) {
196 // is it a potion state mod?
197 int n = Constants::getPotionSkillByName( line );
198 if ( n == -1 ) {
199 StateMod *mod = StateMod::getStateModByName( line );
200 if ( !mod ) {
201 cerr << "Can't find state mod: >" << line << "<" << endl;
202 exit( 1 );
203 }
204 n = mod->getIndex();
205 }
206 stateModPrereq = n;
207 if ( stateModPrereq == -1 ) {
208 cerr << "Error: spell=" << name << endl;
209 cerr << "\tCan't understand prereq for spell: " << line << endl;
210 }
211 }
212
213 currentSpell = new Spell( name, displayName, symbol, level, mp, exp, failureRate,
214 dice, distance, targetType, speed, effect,
215 creatureTarget, locationTarget, itemTarget, partyTarget, doorTarget,
216 current, iconTileX, iconTileY,
217 friendly, stateModPrereq );
218 current->addSpell( currentSpell );
219
220 strcpy( line, node2->getValueAsString( "sound" ) );
221 if ( strlen( line ) ) currentSpell->setSound( line );
222
223 strcpy( line, node2->getValueAsString( "notes" ) );
224 if ( strlen( notes ) ) currentSpell->addNotes( line );
225 }
226 }
227
228 delete config;
229 }
230
231
unInitMagic()232 void MagicSchool::unInitMagic() {
233 for ( int i = 0; i < schoolCount; ++i ) {
234 delete schools[i];
235 schools[i] = NULL;
236 }
237 schoolCount = 0;
238
239 }
240
getRandomString(vector<string> * v)241 const char *MagicSchool::getRandomString( vector<string> *v ) {
242 return ( *v )[ Util::dice( v->size() ) ].c_str();
243 }
244
245 /// Message when you donated a small sum to the school's deity.
246
getLowDonateMessage()247 const char *MagicSchool::getLowDonateMessage() {
248 return getRandomString( &lowDonate );
249 }
250
251 /// Message when you donated a moderate sum to the school's deity.
252
getNeutralDonateMessage()253 const char *MagicSchool::getNeutralDonateMessage() {
254 return getRandomString( &neutralDonate );
255 }
256
257 /// Message when you donated a high sum to the school's deity.
258
getHighDonateMessage()259 const char *MagicSchool::getHighDonateMessage() {
260 return getRandomString( &highDonate );
261 }
262
263 /// Returns a random spell from a random school.
264
getRandomSpell(int level)265 Spell *MagicSchool::getRandomSpell( int level ) {
266 int n = Util::dice( getMagicSchoolCount() );
267 int c = getMagicSchool( n )->getSpellCount();
268 if ( c > 0 ) {
269 return getMagicSchool( n )->getSpell( Util::dice( c ) );
270 } else {
271 return NULL;
272 }
273 }
274
275
276
Spell(char const * name,char const * displayName,char const * symbol,int level,int mp,int exp,int failureRate,char const * action,int distance,int targetType,int speed,int effect,bool creatureTarget,bool locationTarget,bool itemTarget,bool partyTarget,bool doorTarget,MagicSchool * school,int iconTileX,int iconTileY,bool friendly,int stateModPrereq)277 Spell::Spell( char const* name, char const* displayName, char const* symbol, int level, int mp, int exp, int failureRate, char const* action,
278 int distance, int targetType, int speed, int effect, bool creatureTarget,
279 bool locationTarget, bool itemTarget, bool partyTarget, bool doorTarget,
280 MagicSchool *school,
281 int iconTileX, int iconTileY, bool friendly, int stateModPrereq )
282 : action( action )
283 , sound() {
284 this->name = name;
285 this->displayName = displayName;
286 this->symbol = symbol;
287 this->level = level;
288 this->mp = mp;
289 this->exp = exp;
290 this->failureRate = failureRate;
291 this->distance = distance;
292 this->targetType = targetType;
293 this->speed = speed;
294 this->effect = effect;
295 this->creatureTarget = creatureTarget;
296 this->locationTarget = locationTarget;
297 this->itemTarget = itemTarget;
298 this->partyTarget = partyTarget;
299 this->doorTarget = doorTarget;
300 this->school = school;
301 this->iconTileX = iconTileX;
302 this->iconTileY = iconTileY;
303 this->friendly = friendly;
304 this->stateModPrereq = stateModPrereq;
305
306 strcpy( this->notes, "" );
307
308 string s = name;
309 spellMap[s] = this;
310 }
311
~Spell()312 Spell::~Spell() {
313 }
314
315 /// Returns a spell by its name.
316
getSpellByName(char * name)317 Spell *Spell::getSpellByName( char *name ) {
318 string s = name;
319 if ( spellMap.find( s ) == spellMap.end() ) {
320 cerr << "Warning: can't find spell: " << name << endl;
321 return NULL;
322 }
323 return spellMap[s];
324 }
325
326 /// Min and max damage the spell does.
327
getAttack(int casterLevel,float * maxP,float * minP)328 void Spell::getAttack( int casterLevel, float *maxP, float *minP ) {
329 *minP = 0; *maxP = 0;
330 for ( int i = 0; i <= ( casterLevel / 2 ); i++ ) {
331 *minP += action.getMin();
332 *maxP += action.getMax();
333 }
334 }
335