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