1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "xeen/spells.h"
24 #include "xeen/dialogs/dialogs_items.h"
25 #include "xeen/dialogs/dialogs_spells.h"
26 #include "xeen/files.h"
27 #include "xeen/resources.h"
28 #include "xeen/xeen.h"
29 
30 namespace Xeen {
31 
Spells(XeenEngine * vm)32 Spells::Spells(XeenEngine *vm) : _vm(vm) {
33 	_lastCaster = 0;
34 
35 	load();
36 }
37 
load()38 void Spells::load() {
39 	File f1((g_vm->getGameID() == GType_Clouds) ? "spells.cld" : "spells.xen", 1);
40 	while (f1.pos() < f1.size())
41 		_spellNames.push_back(f1.readString());
42 	f1.close();
43 }
44 
calcSpellCost(int spellId,int expenseFactor) const45 int Spells::calcSpellCost(int spellId, int expenseFactor) const {
46 	int amount = Res.SPELL_COSTS[spellId];
47 	return (amount >= 0) ? (amount * 100) << expenseFactor :
48 		(amount * -500) << expenseFactor;
49 }
50 
calcSpellPoints(int spellId,int expenseFactor) const51 int Spells::calcSpellPoints(int spellId, int expenseFactor) const {
52 	int amount = Res.SPELL_COSTS[spellId];
53 	return (amount >= 0) ? amount : amount * -1 * expenseFactor;
54 }
55 
56 typedef void(Spells::*SpellMethodPtr)();
57 
executeSpell(MagicSpell spellId)58 void Spells::executeSpell(MagicSpell spellId) {
59 	static const SpellMethodPtr SPELL_LIST[76] = {
60 		&Spells::acidSpray, &Spells::awaken, &Spells::beastMaster,
61 		&Spells::bless, &Spells::clairvoyance, &Spells::coldRay,
62 		&Spells::createFood, &Spells::cureDisease, &Spells::cureParalysis,
63 		&Spells::curePoison, &Spells::cureWounds, &Spells::dancingSword,
64 		&Spells::dayOfProtection, &Spells::dayOfSorcery, &Spells::deadlySwarm,
65 		&Spells::detectMonster, &Spells::divineIntervention, &Spells::dragonSleep,
66 		&Spells::elementalStorm, &Spells::enchantItem, &Spells::energyBlast,
67 		&Spells::etherialize, &Spells::fantasticFreeze, &Spells::fieryFlail,
68 		&Spells::fingerOfDeath, &Spells::fireball, &Spells::firstAid,
69 		&Spells::flyingFist, &Spells::frostbite, &Spells::golemStopper,
70 		&Spells::heroism, &Spells::holyBonus, &Spells::holyWord,
71 		&Spells::hypnotize, &Spells::identifyMonster, &Spells::implosion,
72 		&Spells::incinerate, &Spells::inferno, &Spells::insectSpray,
73 		&Spells::itemToGold, &Spells::jump, &Spells::levitate,
74 		&Spells::light, &Spells::lightningBolt, &Spells::lloydsBeacon,
75 		&Spells::magicArrow, &Spells::massDistortion, &Spells::megaVolts,
76 		&Spells::moonRay, &Spells::naturesCure, &Spells::pain,
77 		&Spells::poisonVolley, &Spells::powerCure, &Spells::powerShield,
78 		&Spells::prismaticLight, &Spells::protectionFromElements, &Spells::raiseDead,
79 		&Spells::rechargeItem, &Spells::resurrection, &Spells::revitalize,
80 		&Spells::shrapMetal, &Spells::sleep, &Spells::sparks,
81 		&Spells::starBurst, &Spells::stoneToFlesh, &Spells::sunRay,
82 		&Spells::superShelter, &Spells::suppressDisease, &Spells::suppressPoison,
83 		&Spells::teleport, &Spells::timeDistortion, &Spells::townPortal,
84 		&Spells::toxicCloud, &Spells::turnUndead, &Spells::walkOnWater,
85 		&Spells::wizardEye
86 	};
87 
88 	(this->*SPELL_LIST[spellId])();
89 }
90 
spellFailed()91 void Spells::spellFailed() {
92 	ErrorScroll::show(_vm, Res.SPELL_FAILED, WT_NONFREEZED_WAIT);
93 }
94 
castItemSpell(int itemSpellId)95 void Spells::castItemSpell(int itemSpellId) {
96 	assert(itemSpellId != 0);
97 
98 	switch (itemSpellId) {
99 	case 16:
100 		if (_vm->_mode == MODE_COMBAT) {
101 			NotWhileEngaged::show(_vm, MS_Jump);
102 			return;
103 		}
104 		break;
105 	case 21:
106 		if (_vm->_mode == MODE_COMBAT) {
107 			NotWhileEngaged::show(_vm, MS_WizardEye);
108 			return;
109 		}
110 		break;
111 	case 28:
112 		if (_vm->_mode == MODE_COMBAT) {
113 			NotWhileEngaged::show(_vm, MS_LloydsBeacon);
114 			return;
115 		}
116 		break;
117 	case 33:
118 		frostbite2();
119 		break;
120 	case 42:
121 		if (_vm->_mode == MODE_COMBAT) {
122 			NotWhileEngaged::show(_vm, MS_Teleport);
123 			return;
124 		}
125 		break;
126 	case 48:
127 		if (_vm->_mode == MODE_COMBAT) {
128 			NotWhileEngaged::show(_vm, MS_SuperShelter);
129 			return;
130 		}
131 		break;
132 	case 55:
133 		if (_vm->_mode == MODE_COMBAT) {
134 			NotWhileEngaged::show(_vm, MS_TownPortal);
135 			return;
136 		}
137 		break;
138 	case 58:
139 		if (_vm->_mode == MODE_COMBAT) {
140 			NotWhileEngaged::show(_vm, MS_Etheralize);
141 			return;
142 		}
143 		break;
144 	default:
145 		break;
146 	}
147 
148 	static const MagicSpell spells[74] = {
149 		NO_SPELL, MS_Light, MS_Awaken, MS_MagicArrow, MS_FirstAid, MS_FlyingFist,
150 		MS_EnergyBlast, MS_Sleep, MS_Revitalize, MS_CureWounds, MS_Sparks,
151 		MS_Shrapmetal, MS_InsectSpray, MS_ToxicCloud, MS_ProtFromElements, MS_Pain,
152 		MS_Jump, MS_BeastMaster, MS_Clairvoyance, MS_TurnUndead, MS_Levitate,
153 		MS_WizardEye, MS_Bless, MS_IdentifyMonster, MS_LightningBolt, MS_HolyBonus,
154 		MS_PowerCure, MS_NaturesCure, MS_LloydsBeacon, MS_PowerShield, MS_Heroism,
155 		MS_Hynotize, MS_WalkOnWater, NO_SPELL, MS_DetectMonster, MS_Fireball,
156 		MS_ColdRay, MS_CurePoison, MS_AcidSpray, MS_TimeDistortion, MS_DragonSleep,
157 		MS_CureDisease, MS_Teleport, MS_FingerOfDeath, MS_CureParalysis, MS_GolemStopper,
158 		MS_PoisonVolley, MS_DeadlySwarm, MS_SuperShelter, MS_DayOfProtection, MS_DayOfProtection,
159 		MS_CreateFood, MS_FieryFlail, MS_RechargeItem, MS_FantasticFreeze, MS_TownPortal,
160 		MS_StoneToFlesh, MS_RaiseDead, MS_Etheralize, MS_DancingSword, MS_MoonRay,
161 		MS_MassDistortion, MS_PrismaticLight, MS_EnchantItem, MS_Incinerate, MS_HolyWord,
162 		MS_Resurrection, MS_ElementalStorm, MS_MegaVolts, MS_Inferno, MS_SunRay,
163 		MS_Implosion, MS_StarBurst, MS_DivineIntervention
164 	};
165 
166 	executeSpell(spells[itemSpellId]);
167 }
168 
castSpell(Character * c,MagicSpell spellId)169 int Spells::castSpell(Character *c, MagicSpell spellId) {
170 	Combat &combat = *_vm->_combat;
171 	Interface &intf = *_vm->_interface;
172 	int oldTillMove = intf._tillMove;
173 	int result = 1;
174 	combat._oldCharacter = c;
175 
176 	// Try and subtract the SP and gem requirements for the spell
177 	int resultError = subSpellCost(*c, spellId);
178 	if (resultError) {
179 		CantCast::show(_vm, spellId, resultError);
180 		result = -1;
181 	} else {
182 		// Some spells have special handling
183 		switch (spellId) {
184 		case MS_EnchantItem:
185 		case MS_Etheralize:
186 		case MS_Jump:
187 		case MS_LloydsBeacon:
188 		case MS_SuperShelter:
189 		case MS_Teleport:
190 		case MS_TownPortal:
191 		case MS_WizardEye:
192 			if (_vm->_mode != MODE_COMBAT) {
193 				executeSpell(spellId);
194 			} else {
195 				// Return the spell costs and flag that another spell can be selected
196 				addSpellCost(*c, spellId);
197 				NotWhileEngaged::show(_vm, spellId);
198 				result = -1;
199 			}
200 			break;
201 
202 		default:
203 			executeSpell(spellId);
204 			break;
205 		}
206 	}
207 
208 	combat._moveMonsters = 1;
209 	intf._tillMove = oldTillMove;
210 	return result;
211 }
212 
subSpellCost(Character & c,int spellId)213 int Spells::subSpellCost(Character &c, int spellId) {
214 	Party &party = *_vm->_party;
215 	int gemCost = Res.SPELL_GEM_COST[spellId];
216 	int spCost = Res.SPELL_COSTS[spellId];
217 
218 	// Negative SP costs indicate it's dependent on the character's level
219 	if (spCost <= 0) {
220 		spCost = c.getCurrentLevel() * (spCost * -1);
221 	}
222 
223 	if (spCost > c._currentSp)
224 		// Not enough SP
225 		return 1;
226 	if (gemCost > (int)party._gems)
227 		// Not enough gems
228 		return 2;
229 
230 	c._currentSp -= spCost;
231 	party._gems -= gemCost;
232 	return 0;
233 }
234 
addSpellCost(Character & c,int spellId)235 void Spells::addSpellCost(Character &c, int spellId) {
236 	Party &party = *_vm->_party;
237 	int gemCost = Res.SPELL_GEM_COST[spellId];
238 	int spCost = Res.SPELL_COSTS[spellId];
239 
240 	if (spCost < 1)
241 		spCost *= -1 * c.getCurrentLevel();
242 
243 	c._currentSp += spCost;
244 	party._gems += gemCost;
245 }
246 
acidSpray()247 void Spells::acidSpray() {
248 	Combat &combat = *_vm->_combat;
249 	Sound &sound = *_vm->_sound;
250 
251 	combat._monsterDamage = 15;
252 	combat._damageType = DT_POISON;
253 	combat._rangeType = RT_ALL;
254 	sound.playFX(17);
255 	combat.rangedAttack(POW_SPRAY);
256 }
257 
awaken()258 void Spells::awaken() {
259 	Interface &intf = *_vm->_interface;
260 	Party &party = *_vm->_party;
261 	Sound &sound = *_vm->_sound;
262 
263 	for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
264 		Character &c = party._activeParty[idx];
265 		c._conditions[ASLEEP] = 0;
266 		if (c._currentHp > 0)
267 			c._conditions[UNCONSCIOUS] = 0;
268 	}
269 
270 	intf.drawParty(true);
271 	sound.playFX(30);
272 }
273 
beastMaster()274 void Spells::beastMaster() {
275 	Combat &combat = *_vm->_combat;
276 	Sound &sound = *_vm->_sound;
277 
278 	combat._monsterDamage = 0;
279 	combat._damageType = DT_BEASTMASTER;
280 	combat._rangeType = RT_GROUP;
281 	sound.playFX(18);
282 	combat.rangedAttack(POW_MAGIC_ORB);
283 }
284 
bless()285 void Spells::bless() {
286 	Combat &combat = *_vm->_combat;
287 	Party &party = *_vm->_party;
288 	Sound &sound = *_vm->_sound;
289 
290 	sound.playFX(30);
291 	party._blessed = combat._oldCharacter->getCurrentLevel();
292 }
293 
clairvoyance()294 void Spells::clairvoyance() {
295 	_vm->_party->_clairvoyanceActive = true;
296 	_vm->_sound->playFX(20);
297 }
298 
coldRay()299 void Spells::coldRay() {
300 	Combat &combat = *_vm->_combat;
301 	Sound &sound = *_vm->_sound;
302 
303 	combat._monsterDamage = _vm->getRandomNumber(2, 4) * combat._oldCharacter->getCurrentLevel();
304 	combat._damageType = DT_COLD;
305 	combat._rangeType = RT_ALL;
306 	sound.playFX(15);
307 	combat.rangedAttack(POW_COLD_RAY);
308 }
309 
createFood()310 void Spells::createFood() {
311 	Party &party = *_vm->_party;
312 	Sound &sound = *_vm->_sound;
313 
314 	party._food += party._activeParty.size();
315 	sound.playFX(20);
316 }
317 
cureDisease()318 void Spells::cureDisease() {
319 	Interface &intf = *_vm->_interface;
320 	Sound &sound = *_vm->_sound;
321 
322 	Character *c = SpellOnWho::show(_vm, MS_CureDisease);
323 	if (!c)
324 		return;
325 
326 	sound.playFX(30);
327 	c->addHitPoints(0);
328 	c->_conditions[DISEASED] = 0;
329 	intf.drawParty(true);
330 }
331 
cureParalysis()332 void Spells::cureParalysis() {
333 	Interface &intf = *_vm->_interface;
334 	Sound &sound = *_vm->_sound;
335 
336 	Character *c = SpellOnWho::show(_vm, MS_CureParalysis);
337 	if (!c)
338 		return;
339 
340 	sound.playFX(30);
341 	c->addHitPoints(0);
342 	c->_conditions[PARALYZED] = 0;
343 	intf.drawParty(true);
344 }
345 
curePoison()346 void Spells::curePoison() {
347 	Interface &intf = *_vm->_interface;
348 	Sound &sound = *_vm->_sound;
349 
350 	Character *c = SpellOnWho::show(_vm, MS_CurePoison);
351 	if (!c)
352 		return;
353 
354 	sound.playFX(30);
355 	c->addHitPoints(0);
356 	c->_conditions[POISONED] = 0;
357 	intf.drawParty(true);
358 }
359 
cureWounds()360 void Spells::cureWounds() {
361 	Sound &sound = *_vm->_sound;
362 
363 	Character *c = SpellOnWho::show(_vm, MS_CureWounds);
364 	if (!c)
365 		return;
366 
367 	if (c->isDead()) {
368 		spellFailed();
369 	} else {
370 		sound.playFX(30);
371 		c->addHitPoints(15);
372 	}
373 }
374 
dancingSword()375 void Spells::dancingSword() {
376 	Combat &combat = *_vm->_combat;
377 	Sound &sound = *_vm->_sound;
378 
379 	combat._monsterDamage = _vm->getRandomNumber(6, 14) * combat._oldCharacter->getCurrentLevel();
380 	combat._damageType = DT_PHYSICAL;
381 	combat._rangeType = RT_GROUP;
382 	sound.playFX(18);
383 	combat.rangedAttack(POW_SPARKLES);
384 }
385 
dayOfProtection()386 void Spells::dayOfProtection() {
387 	Combat &combat = *_vm->_combat;
388 	Party &party = *_vm->_party;
389 	Sound &sound = *_vm->_sound;
390 
391 	int lvl = combat._oldCharacter->getCurrentLevel();
392 	party._walkOnWaterActive = true;
393 	party._heroism = lvl;
394 	party._holyBonus = lvl;
395 	party._blessed = lvl;
396 	party._poisonResistence = lvl;
397 	party._coldResistence = lvl;
398 	party._electricityResistence = lvl;
399 	party._fireResistence = lvl;
400 	party._lightCount = lvl;
401 	sound.playFX(20);
402 }
403 
dayOfSorcery()404 void Spells::dayOfSorcery() {
405 	Combat &combat = *_vm->_combat;
406 	Party &party = *_vm->_party;
407 	Sound &sound = *_vm->_sound;
408 
409 	int lvl = combat._oldCharacter->getCurrentLevel();
410 	party._powerShield = lvl;
411 	party._clairvoyanceActive = true;
412 	party._wizardEyeActive = true;
413 	party._levitateCount = 1;
414 	party._lightCount = lvl;
415 	party._automapOn = false;
416 	sound.playFX(20);
417 }
418 
deadlySwarm()419 void Spells::deadlySwarm() {
420 	Combat &combat = *_vm->_combat;
421 	Sound &sound = *_vm->_sound;
422 
423 	combat._monsterDamage = 40;
424 	combat._damageType = DT_PHYSICAL;
425 	combat._rangeType = RT_GROUP;
426 	sound.playFX(13);
427 	combat.rangedAttack(POW_DEADLY_SWARM);
428 }
429 
detectMonster()430 void Spells::detectMonster() {
431 	DetectMonsters::show(_vm);
432 }
433 
divineIntervention()434 void Spells::divineIntervention() {
435 	Combat &combat = *_vm->_combat;
436 	Interface &intf = *_vm->_interface;
437 	Party &party = *_vm->_party;
438 	Sound &sound = *_vm->_sound;
439 	Character &castChar = *combat._oldCharacter;
440 
441 	if ((castChar._tempAge + 5) > 250) {
442 		castChar._tempAge = 250;
443 	} else {
444 		castChar._tempAge += 5;
445 	}
446 
447 	for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
448 		Character &c = party._activeParty[idx];
449 		Common::fill(&c._conditions[CURSED], &c._conditions[ERADICATED], 0);
450 		if (!c._conditions[ERADICATED])
451 			c._currentHp = c.getMaxHP();
452 	}
453 
454 	sound.playFX(20);
455 	intf.drawParty(true);
456 }
457 
dragonSleep()458 void Spells::dragonSleep() {
459 	Combat &combat = *_vm->_combat;
460 	Sound &sound = *_vm->_sound;
461 
462 	combat._monsterDamage = 0;
463 	combat._damageType = DT_DRAGONSLEEP;
464 	combat._rangeType = RT_SINGLE;
465 	sound.playFX(18);
466 	combat.rangedAttack(POW_MAGIC_ORB);
467 }
468 
elementalStorm()469 void Spells::elementalStorm() {
470 	Combat &combat = *_vm->_combat;
471 	Sound &sound = *_vm->_sound;
472 	static const int STORM_FX_LIST[4] = { 13, 14, 15, 17 };
473 	static const PowType STORM_MA_LIST[4] = {
474 		POW_FIREBALL, POW_SPARKS, POW_FROST_WAVE, POW_SPRAY
475 	};
476 
477 	combat._monsterDamage = 150;
478 	combat._damageType = (DamageType)_vm->getRandomNumber(DT_FIRE, DT_POISON);
479 	combat._rangeType = RT_ALL;
480 	sound.playFX(STORM_FX_LIST[combat._damageType - DT_FIRE]);
481 	combat.rangedAttack(STORM_MA_LIST[combat._damageType - DT_FIRE]);
482 }
483 
enchantItem()484 void Spells::enchantItem() {
485 	Mode oldMode = _vm->_mode;
486 
487 	Character *c = SpellOnWho::show(_vm, MS_EnchantItem);
488 	if (!c)
489 		return;
490 
491 	ItemsDialog::show(_vm, c, ITEMMODE_ENCHANT);
492 
493 	_vm->_mode = oldMode;
494 }
495 
energyBlast()496 void Spells::energyBlast() {
497 	Combat &combat = *_vm->_combat;
498 	Sound &sound = *_vm->_sound;
499 
500 	combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
501 	combat._damageType = DT_ENERGY;
502 	combat._rangeType = RT_SINGLE;
503 	sound.playFX(16);
504 	combat.rangedAttack(POW_ENERGY_BLAST);
505 }
506 
etherialize()507 void Spells::etherialize() {
508 	Map &map = *_vm->_map;
509 	Party &party = *_vm->_party;
510 	Sound &sound = *_vm->_sound;
511 	Common::Point pt = party._mazePosition + Common::Point(
512 		Res.SCREEN_POSITIONING_X[party._mazeDirection][7],
513 		Res.SCREEN_POSITIONING_Y[party._mazeDirection][7]
514 	);
515 
516 	if ((map.mazeData()._mazeFlags & RESTRICTION_ETHERIALIZE) ||
517 			map.mazeLookup(pt, 0, 0xffff) == INVALID_CELL) {
518 		spellFailed();
519 	} else {
520 		party._mazePosition = pt;
521 		sound.playFX(51);
522 	}
523 }
524 
fantasticFreeze()525 void Spells::fantasticFreeze() {
526 	Combat &combat = *_vm->_combat;
527 	Sound &sound = *_vm->_sound;
528 
529 	combat._monsterDamage = 40;
530 	combat._damageType = DT_COLD;
531 	combat._rangeType = RT_GROUP;
532 	sound.playFX(15);
533 	combat.rangedAttack(POW_COLD_RAY);
534 }
535 
fieryFlail()536 void Spells::fieryFlail() {
537 	Combat &combat = *_vm->_combat;
538 	Sound &sound = *_vm->_sound;
539 
540 	combat._monsterDamage = 100;
541 	combat._damageType = DT_FIRE;
542 	combat._rangeType = RT_SINGLE;
543 	sound.playFX(13);
544 	combat.rangedAttack(POW_FIERY_FLAIL);
545 }
546 
fingerOfDeath()547 void Spells::fingerOfDeath() {
548 	Combat &combat = *_vm->_combat;
549 	Sound &sound = *_vm->_sound;
550 
551 	combat._monsterDamage = 0;
552 	combat._damageType = DT_FINGEROFDEATH;
553 	combat._rangeType = RT_GROUP;
554 	sound.playFX(18);
555 	combat.rangedAttack(POW_SPARKLES);
556 }
557 
fireball()558 void Spells::fireball() {
559 	Combat &combat = *_vm->_combat;
560 	Sound &sound = *_vm->_sound;
561 
562 	combat._monsterDamage = _vm->getRandomNumber(3, 7) * combat._oldCharacter->getCurrentLevel();
563 	combat._damageType = DT_FIRE;
564 	combat._rangeType = RT_GROUP;
565 	sound.playFX(13);
566 	combat.rangedAttack(POW_FIREBALL);
567 }
568 
firstAid()569 void Spells::firstAid() {
570 	Sound &sound = *_vm->_sound;
571 
572 	Character *c = SpellOnWho::show(_vm, MS_FirstAid);
573 	if (!c)
574 		return;
575 
576 	if (c->isDead()) {
577 		spellFailed();
578 	} else {
579 		sound.playFX(30);
580 		c->addHitPoints(6);
581 	}
582 }
583 
flyingFist()584 void Spells::flyingFist() {
585 	Combat &combat = *_vm->_combat;
586 	Sound &sound = *_vm->_sound;
587 
588 	combat._monsterDamage = 6;
589 	combat._damageType = DT_PHYSICAL;
590 	combat._rangeType = RT_SINGLE;
591 	sound.playFX(18);
592 	combat.rangedAttack(POW_SPARKLES);
593 }
594 
frostbite()595 void Spells::frostbite() {
596 	Combat &combat = *_vm->_combat;
597 	Sound &sound = *_vm->_sound;
598 
599 	combat._monsterDamage = 35;
600 	combat._damageType = DT_COLD;
601 	combat._rangeType = RT_SINGLE;
602 	sound.playFX(8);
603 	combat.rangedAttack(POW_COLD_RAY);
604 }
605 
golemStopper()606 void Spells::golemStopper() {
607 	Combat &combat = *_vm->_combat;
608 	Sound &sound = *_vm->_sound;
609 
610 	combat._monsterDamage = 0;
611 	combat._damageType = DT_GOLEMSTOPPER;
612 	combat._rangeType = RT_SINGLE;
613 	sound.playFX(16);
614 	combat.rangedAttack(POW_STOPPER);
615 }
616 
heroism()617 void Spells::heroism() {
618 	Combat &combat = *_vm->_combat;
619 	Party &party = *_vm->_party;
620 	Sound &sound = *_vm->_sound;
621 
622 	sound.playFX(30);
623 	party._heroism = combat._oldCharacter->getCurrentLevel();
624 }
625 
holyBonus()626 void Spells::holyBonus() {
627 	Combat &combat = *_vm->_combat;
628 	Party &party = *_vm->_party;
629 	Sound &sound = *_vm->_sound;
630 
631 	sound.playFX(30);
632 	party._holyBonus = combat._oldCharacter->getCurrentLevel();
633 }
634 
holyWord()635 void Spells::holyWord() {
636 	Combat &combat = *_vm->_combat;
637 	Sound &sound = *_vm->_sound;
638 
639 	combat._monsterDamage = 0;
640 	combat._damageType = DT_HOLYWORD;
641 	combat._rangeType = RT_GROUP;
642 	sound.playFX(18);
643 	combat.rangedAttack(POW_ENERGY_BLAST);
644 }
645 
hypnotize()646 void Spells::hypnotize() {
647 	Combat &combat = *_vm->_combat;
648 	Sound &sound = *_vm->_sound;
649 
650 	combat._monsterDamage = 0;
651 	combat._damageType = DT_HYPNOTIZE;
652 	combat._rangeType = RT_GROUP;
653 	sound.playFX(18);
654 	combat.rangedAttack(POW_MAGIC_ORB);
655 }
656 
identifyMonster()657 void Spells::identifyMonster() {
658 	Combat &combat = *_vm->_combat;
659 
660 	if (combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1
661 		&& combat._attackMonsters[2] == -1) {
662 		spellFailed();
663 	} else {
664 		IdentifyMonster::show(_vm);
665 	}
666 }
667 
implosion()668 void Spells::implosion() {
669 	Combat &combat = *_vm->_combat;
670 	Sound &sound = *_vm->_sound;
671 
672 	combat._monsterDamage = 1000;
673 	combat._damageType = DT_ENERGY;
674 	combat._rangeType = RT_SINGLE;
675 	sound.playFX(18);
676 	combat.rangedAttack(POW_STOPPER);
677 }
678 
incinerate()679 void Spells::incinerate() {
680 	Combat &combat = *_vm->_combat;
681 	Sound &sound = *_vm->_sound;
682 
683 	combat._monsterDamage = 250;
684 	combat._damageType = DT_FIRE;
685 	combat._rangeType = RT_SINGLE;
686 	sound.playFX(22);
687 	combat.rangedAttack(POW_INCINERATE);
688 }
689 
inferno()690 void Spells::inferno() {
691 	Combat &combat = *_vm->_combat;
692 	Sound &sound = *_vm->_sound;
693 
694 	combat._monsterDamage = 250;
695 	combat._damageType = DT_FIRE;
696 	combat._rangeType = RT_GROUP;
697 	sound.playFX(13);
698 	combat.rangedAttack(POW_INCINERATE);
699 }
700 
insectSpray()701 void Spells::insectSpray() {
702 	Combat &combat = *_vm->_combat;
703 	Sound &sound = *_vm->_sound;
704 
705 	combat._monsterDamage = 0;
706 	combat._damageType = DT_INSECT_SPRAY;
707 	combat._rangeType = RT_GROUP;
708 	sound.playFX(17);
709 	combat.rangedAttack(POW_SPRAY);
710 }
711 
itemToGold()712 void Spells::itemToGold() {
713 	Windows &windows = *_vm->_windows;
714 	Character *c = SpellOnWho::show(_vm, MS_ItemToGold);
715 	if (!c)
716 		return;
717 
718 	Mode oldMode = _vm->_mode;
719 	_vm->_mode = MODE_FF;
720 
721 	windows[11].close();
722 	ItemsDialog::show(_vm, c, ITEMMODE_TO_GOLD);
723 
724 	_vm->_mode = oldMode;
725 }
726 
jump()727 void Spells::jump() {
728 	Map &map = *_vm->_map;
729 	Party &party = *_vm->_party;
730 	Sound &sound = *_vm->_sound;
731 
732 	if (map._isOutdoors) {
733 		map.getCell(7);
734 		if (map._currentWall != 1) {
735 			map.getCell(14);
736 			if (map._currentSurfaceId != 0 && map._currentWall != 1) {
737 				party._mazePosition += Common::Point(
738 					Res.SCREEN_POSITIONING_X[party._mazeDirection][14],
739 					Res.SCREEN_POSITIONING_Y[party._mazeDirection][14]
740 					);
741 				sound.playFX(51);
742 				party._stepped = true;
743 				return;
744 			}
745 		}
746 	} else {
747 		Common::Point pt = party._mazePosition + Common::Point(
748 			Res.SCREEN_POSITIONING_X[party._mazeDirection][7],
749 			Res.SCREEN_POSITIONING_Y[party._mazeDirection][7]);
750 		if (!map.mazeLookup(party._mazePosition, Res.MONSTER_GRID_BITMASK[party._mazeDirection]) &&
751 			!map.mazeLookup(pt, Res.MONSTER_GRID_BITMASK[party._mazeDirection])) {
752 			party._mazePosition += Common::Point(
753 				Res.SCREEN_POSITIONING_X[party._mazeDirection][14],
754 				Res.SCREEN_POSITIONING_Y[party._mazeDirection][14]
755 			);
756 			sound.playFX(51);
757 			party._stepped = true;
758 			return;
759 		}
760 	}
761 
762 	spellFailed();
763 }
764 
levitate()765 void Spells::levitate() {
766 	_vm->_party->_levitateCount = 1;
767 	_vm->_sound->playFX(20);
768 }
769 
light()770 void Spells::light() {
771 	Interface &intf = *_vm->_interface;
772 	Party &party = *_vm->_party;
773 	Sound &sound = *_vm->_sound;
774 
775 	++party._lightCount;
776 	if (intf._obscurity != OBSCURITY_BLACK)
777 		party._stepped = true;
778 	sound.playFX(39);
779 }
780 
lightningBolt()781 void Spells::lightningBolt() {
782 	Combat &combat = *_vm->_combat;
783 	Sound &sound = *_vm->_sound;
784 
785 	combat._monsterDamage = _vm->getRandomNumber(4, 6) * combat._oldCharacter->getCurrentLevel();
786 	combat._damageType = DT_ELECTRICAL;
787 	combat._rangeType = RT_GROUP;
788 	sound.playFX(14);
789 	combat.rangedAttack(POW_LIGHTNING);
790 }
791 
lloydsBeacon()792 void Spells::lloydsBeacon() {
793 	if (_vm->_map->mazeData()._mazeFlags & RESTRICTION_LLOYDS_BEACON) {
794 		spellFailed();
795 	} else {
796 		if (!LloydsBeacon::show(_vm))
797 			spellFailed();
798 	}
799 }
800 
magicArrow()801 void Spells::magicArrow() {
802 	Combat &combat = *_vm->_combat;
803 	combat._monsterDamage = 0;
804 	combat._damageType = DT_MAGIC_ARROW;
805 	combat._rangeType = RT_SINGLE;
806 	combat.rangedAttack(POW_ARROW);
807 }
808 
massDistortion()809 void Spells::massDistortion() {
810 	Combat &combat = *_vm->_combat;
811 	Sound &sound = *_vm->_sound;
812 
813 	combat._monsterDamage = 0;
814 	combat._damageType = DT_MASS_DISTORTION;
815 	combat._rangeType = RT_GROUP;
816 	sound.playFX(18);
817 	combat.rangedAttack(POW_STOPPER);
818 }
819 
megaVolts()820 void Spells::megaVolts() {
821 	Combat &combat = *_vm->_combat;
822 	Sound &sound = *_vm->_sound;
823 
824 	combat._monsterDamage = 150;
825 	combat._damageType = DT_ELECTRICAL;
826 	combat._rangeType = RT_GROUP;
827 	sound.playFX(14);
828 	combat.rangedAttack(POW_MEGAVOLTS);
829 }
830 
moonRay()831 void Spells::moonRay() {
832 	Combat &combat = *_vm->_combat;
833 	Interface &intf = *_vm->_interface;
834 	Party &party = *_vm->_party;
835 	Sound &sound = *_vm->_sound;
836 
837 	combat._monsterDamage = 30;
838 	combat._damageType = DT_ENERGY;
839 	combat._rangeType = RT_ALL;
840 	sound.playFX(16);
841 	combat.rangedAttack(POW_ENERGY_BLAST);
842 
843 	for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
844 		sound.playFX(30);
845 		party._activeParty[idx].addHitPoints(_vm->getRandomNumber(1, 30));
846 	}
847 
848 	intf.drawParty(true);
849 }
850 
naturesCure()851 void Spells::naturesCure() {
852 	Sound &sound = *_vm->_sound;
853 
854 	Character *c = SpellOnWho::show(_vm, MS_NaturesCure);
855 	if (!c)
856 		return;
857 
858 	if (c->isDead()) {
859 		spellFailed();
860 	} else {
861 		sound.playFX(30);
862 		c->addHitPoints(25);
863 	}
864 }
865 
pain()866 void Spells::pain() {
867 	Combat &combat = *_vm->_combat;
868 	Sound &sound = *_vm->_sound;
869 
870 	combat._monsterDamage = 0;
871 	combat._damageType = DT_PHYSICAL;
872 	combat._rangeType = RT_GROUP;
873 	sound.playFX(18);
874 	combat.rangedAttack(POW_SPARKLES);
875 }
876 
poisonVolley()877 void Spells::poisonVolley() {
878 	Combat &combat = *_vm->_combat;
879 	Sound &sound = *_vm->_sound;
880 
881 	combat._monsterDamage = 10;
882 	combat._damageType = DT_POISON_VOLLEY;
883 	combat._rangeType = RT_ALL;
884 	sound.playFX(49);
885 	combat.rangedAttack(POW_ARROW);
886 }
887 
powerCure()888 void Spells::powerCure() {
889 	Sound &sound = *_vm->_sound;
890 
891 	Character *c = SpellOnWho::show(_vm, MS_PowerCure);
892 	if (!c)
893 		return;
894 
895 	if (c->isDead()) {
896 		spellFailed();
897 	} else {
898 		sound.playFX(30);
899 		c->addHitPoints(_vm->getRandomNumber(2, 12) * _vm->_combat->_oldCharacter->getCurrentLevel());
900 	}
901 }
902 
powerShield()903 void Spells::powerShield() {
904 	Combat &combat = *_vm->_combat;
905 	Party &party = *_vm->_party;
906 	Sound &sound = *_vm->_sound;
907 
908 	sound.playFX(20);
909 	party._powerShield = combat._oldCharacter->getCurrentLevel();
910 }
911 
prismaticLight()912 void Spells::prismaticLight() {
913 	Combat &combat = *_vm->_combat;
914 	Sound &sound = *_vm->_sound;
915 
916 	combat._monsterDamage = 80;
917 	combat._damageType = (DamageType)_vm->getRandomNumber(DT_PHYSICAL, DT_ENERGY);
918 	combat._rangeType = RT_ALL;
919 	sound.playFX(18);
920 	combat.rangedAttack(POW_SPARKLES);
921 }
922 
protectionFromElements()923 void Spells::protectionFromElements() {
924 	Combat &combat = *_vm->_combat;
925 	Interface &intf = *_vm->_interface;
926 	Party &party = *_vm->_party;
927 	Sound &sound = *_vm->_sound;
928 	Character &c = *combat._oldCharacter;
929 	int resist = MIN(c.getCurrentLevel() * 2 + 5, (uint)200);
930 
931 	int elementType = SelectElement::show(_vm, MS_ProtFromElements);
932 	if (elementType != -1) {
933 		switch (elementType) {
934 		case DT_FIRE:
935 			party._fireResistence = resist;
936 			break;
937 		case DT_ELECTRICAL:
938 			party._fireResistence = resist;
939 			break;
940 		case DT_COLD:
941 			party._coldResistence = resist;
942 			break;
943 		case DT_POISON:
944 			party._poisonResistence = resist;
945 			break;
946 		default:
947 			break;
948 		}
949 
950 		sound.playFX(20);
951 		intf.drawParty(true);
952 	}
953 }
954 
raiseDead()955 void Spells::raiseDead() {
956 	Interface &intf = *_vm->_interface;
957 	Sound &sound = *_vm->_sound;
958 
959 	Character *c = SpellOnWho::show(_vm, MS_RaiseDead);
960 	if (!c)
961 		return;
962 
963 	if (!c->_conditions[DEAD]) {
964 		spellFailed();
965 	} else {
966 		c->_conditions[DEAD] = 0;
967 		c->_conditions[UNCONSCIOUS] = 0;
968 		c->_currentHp = 0;
969 		sound.playFX(30);
970 		c->addHitPoints(1);
971 		if (--c->_endurance._permanent < 1)
972 			c->_endurance._permanent = 1;
973 
974 		intf.drawParty(true);
975 	}
976 }
977 
rechargeItem()978 void Spells::rechargeItem() {
979 	Mode oldMode = _vm->_mode;
980 
981 	Character *c = SpellOnWho::show(_vm, MS_RechargeItem);
982 	if (!c)
983 		return;
984 
985 	ItemsDialog::show(_vm, c, ITEMMODE_RECHARGE);
986 	_vm->_mode = oldMode;
987 }
988 
resurrection()989 void Spells::resurrection() {
990 	Interface &intf = *_vm->_interface;
991 	Sound &sound = *_vm->_sound;
992 
993 	Character *c = SpellOnWho::show(_vm, MS_RaiseDead);
994 	if (!c)
995 		return;
996 
997 	if (!c->_conditions[ERADICATED]) {
998 		spellFailed();
999 		sound.playFX(30);
1000 	} else {
1001 		sound.playFX(30);
1002 		c->addHitPoints(0);
1003 		c->_conditions[ERADICATED] = 0;
1004 
1005 		if (--c->_endurance._permanent < 1)
1006 			c->_endurance._permanent = 1;
1007 		if ((c->_tempAge + 5) >= 250)
1008 			c->_tempAge = 250;
1009 		else
1010 			c->_tempAge += 5;
1011 
1012 		intf.drawParty(true);
1013 	}
1014 }
1015 
revitalize()1016 void Spells::revitalize() {
1017 	Interface &intf = *_vm->_interface;
1018 	Sound &sound = *_vm->_sound;
1019 
1020 	Character *c = SpellOnWho::show(_vm, MS_Revitalize);
1021 	if (!c)
1022 		return;
1023 
1024 	sound.playFX(30);
1025 	c->addHitPoints(0);
1026 	c->_conditions[WEAK] = 0;
1027 	intf.drawParty(true);
1028 }
1029 
shrapMetal()1030 void Spells::shrapMetal() {
1031 	Combat &combat = *_vm->_combat;
1032 	Sound &sound = *_vm->_sound;
1033 
1034 	combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
1035 	combat._damageType = DT_PHYSICAL;
1036 	combat._rangeType = RT_GROUP;
1037 	sound.playFX(16);
1038 	combat.rangedAttack(POW_DEADLY_SWARM);
1039 }
1040 
sleep()1041 void Spells::sleep() {
1042 	Combat &combat = *_vm->_combat;
1043 	Sound &sound = *_vm->_sound;
1044 
1045 	combat._monsterDamage = 0;
1046 	combat._damageType = DT_SLEEP;
1047 	combat._rangeType = RT_GROUP;
1048 	sound.playFX(18);
1049 	combat.rangedAttack(POW_MAGIC_ORB);
1050 }
1051 
sparks()1052 void Spells::sparks() {
1053 	Combat &combat = *_vm->_combat;
1054 	Sound &sound = *_vm->_sound;
1055 
1056 	combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
1057 	combat._damageType = DT_ELECTRICAL;
1058 	combat._rangeType = RT_GROUP;
1059 	sound.playFX(14);
1060 	combat.rangedAttack(POW_SPARKS);
1061 }
1062 
starBurst()1063 void Spells::starBurst() {
1064 	Combat &combat = *_vm->_combat;
1065 	Sound &sound = *_vm->_sound;
1066 
1067 	combat._monsterDamage = 500;
1068 	combat._damageType = DT_FIRE;
1069 	combat._rangeType = RT_ALL;
1070 	sound.playFX(13);
1071 	combat.rangedAttack(POW_DEADLY_SWARM);
1072 }
1073 
stoneToFlesh()1074 void Spells::stoneToFlesh() {
1075 	Interface &intf = *_vm->_interface;
1076 	Sound &sound = *_vm->_sound;
1077 
1078 	Character *c = SpellOnWho::show(_vm, MS_StoneToFlesh);
1079 	if (!c)
1080 		return;
1081 
1082 	sound.playFX(30);
1083 	c->addHitPoints(0);
1084 	c->_conditions[STONED] = 0;
1085 	intf.drawParty(true);
1086 }
1087 
sunRay()1088 void Spells::sunRay() {
1089 	Combat &combat = *_vm->_combat;
1090 	Sound &sound = *_vm->_sound;
1091 
1092 	combat._monsterDamage = 200;
1093 	combat._damageType = DT_ENERGY;
1094 	combat._rangeType = RT_ALL;
1095 	sound.playFX(16);
1096 	combat.rangedAttack(POW_ENERGY_BLAST);
1097 }
1098 
superShelter()1099 void Spells::superShelter() {
1100 	Interface &intf = *_vm->_interface;
1101 	Map &map = *_vm->_map;
1102 	Sound &sound = *_vm->_sound;
1103 
1104 	if (map.mazeData()._mazeFlags & RESTRICTION_SUPER_SHELTER) {
1105 		spellFailed();
1106 	} else {
1107 		Mode oldMode = _vm->_mode;
1108 		_vm->_mode = MODE_INTERACTIVE2;
1109 		sound.playFX(30);
1110 		intf.rest();
1111 		_vm->_mode = oldMode;
1112 	}
1113 }
1114 
suppressDisease()1115 void Spells::suppressDisease() {
1116 	Interface &intf = *_vm->_interface;
1117 	Sound &sound = *_vm->_sound;
1118 
1119 	Character *c = SpellOnWho::show(_vm, MS_SuppressDisease);
1120 	if (!c)
1121 		return;
1122 
1123 	if (c->_conditions[DISEASED]) {
1124 		if (c->_conditions[DISEASED] >= 4)
1125 			c->_conditions[DISEASED] -= 3;
1126 		else
1127 			c->_conditions[DISEASED] = 1;
1128 
1129 		sound.playFX(20);
1130 		c->addHitPoints(0);
1131 		intf.drawParty(true);
1132 	}
1133 }
1134 
suppressPoison()1135 void Spells::suppressPoison() {
1136 	Interface &intf = *_vm->_interface;
1137 	Sound &sound = *_vm->_sound;
1138 
1139 	Character *c = SpellOnWho::show(_vm, MS_FirstAid);
1140 	if (!c)
1141 		return;
1142 
1143 	if (c->_conditions[POISONED]) {
1144 		if (c->_conditions[POISONED] >= 4) {
1145 			c->_conditions[POISONED] -= 2;
1146 		} else {
1147 			c->_conditions[POISONED] = 1;
1148 		}
1149 	}
1150 
1151 	sound.playFX(20);
1152 	c->addHitPoints(0);
1153 	intf.drawParty(1);
1154 }
1155 
teleport()1156 void Spells::teleport() {
1157 	Map &map = *_vm->_map;
1158 	Sound &sound = *_vm->_sound;
1159 
1160 	if (map.mazeData()._mazeFlags & RESTRICTION_TELPORT) {
1161 		spellFailed();
1162 	} else {
1163 		switch (Teleport::show(_vm)) {
1164 		case 0:
1165 			spellFailed();
1166 			break;
1167 		case 1:
1168 			sound.playFX(51);
1169 			break;
1170 		default:
1171 			break;
1172 		}
1173 	}
1174 }
1175 
timeDistortion()1176 void Spells::timeDistortion() {
1177 	Interface &intf = *_vm->_interface;
1178 	Map &map = *_vm->_map;
1179 	Party &party = *_vm->_party;
1180 	Sound &sound = *_vm->_sound;
1181 
1182 	if (map.mazeData()._mazeFlags & RESTRICTION_TIME_DISTORTION) {
1183 		spellFailed();
1184 	} else {
1185 		party.moveToRunLocation();
1186 		sound.playFX(51);
1187 		intf.draw3d(true);
1188 	}
1189 }
1190 
townPortal()1191 void Spells::townPortal() {
1192 	Map &map = *_vm->_map;
1193 	Party &party = *_vm->_party;
1194 	Sound &sound = *_vm->_sound;
1195 
1196 	if (map.mazeData()._mazeFlags & RESTRICTION_TOWN_PORTAL) {
1197 		spellFailed();
1198 		return;
1199 	}
1200 
1201 	int townNumber = TownPortal::show(_vm);
1202 	if (!townNumber)
1203 		return;
1204 
1205 	sound.playFX(51);
1206 	map._loadCcNum = map._sideTownPortal;
1207 	_vm->_files->_ccNum = map._sideTownPortal > 0;
1208 
1209 	int arrIndex = _vm->getGameID() == GType_Swords ? 2 : map._sideTownPortal;
1210 	map.load(Res.TOWN_MAP_NUMBERS[arrIndex][townNumber - 1]);
1211 
1212 	if (_vm->getGameID() == GType_Swords) {
1213 		party._mazePosition = Common::Point(8, 3);
1214 		party._mazeDirection = DIR_NORTH;
1215 	} else if (!_vm->_files->_ccNum) {
1216 		party.moveToRunLocation();
1217 	} else {
1218 		switch (townNumber) {
1219 		case 1:
1220 			party._mazePosition = Common::Point(14, 11);
1221 			party._mazeDirection = DIR_SOUTH;
1222 			break;
1223 		case 2:
1224 			party._mazePosition = Common::Point(5, 12);
1225 			party._mazeDirection = DIR_WEST;
1226 			break;
1227 		case 3:
1228 			party._mazePosition = Common::Point(2, 15);
1229 			party._mazeDirection = DIR_EAST;
1230 			break;
1231 		case 4:
1232 			party._mazePosition = Common::Point(8, 11);
1233 			party._mazeDirection = DIR_NORTH;
1234 			break;
1235 		case 5:
1236 			party._mazePosition = Common::Point(2, 6);
1237 			party._mazeDirection = DIR_NORTH;
1238 			break;
1239 		default:
1240 			break;
1241 		}
1242 	}
1243 }
1244 
toxicCloud()1245 void Spells::toxicCloud() {
1246 	Combat &combat = *_vm->_combat;
1247 	Sound &sound = *_vm->_sound;
1248 
1249 	combat._monsterDamage = 10;
1250 	combat._damageType = DT_POISON;
1251 	combat._rangeType = RT_GROUP;
1252 	sound.playFX(17);
1253 	combat.rangedAttack(POW_SPRAY);
1254 }
1255 
turnUndead()1256 void Spells::turnUndead() {
1257 	Combat &combat = *_vm->_combat;
1258 	Sound &sound = *_vm->_sound;
1259 
1260 	combat._monsterDamage = 0;
1261 	combat._damageType = DT_UNDEAD;
1262 	combat._rangeType = RT_GROUP;
1263 	sound.playFX(18);
1264 	combat.rangedAttack(POW_ENERGY_BLAST);
1265 }
1266 
walkOnWater()1267 void Spells::walkOnWater() {
1268 	Party &party = *_vm->_party;
1269 	Sound &sound = *_vm->_sound;
1270 
1271 	party._walkOnWaterActive = true;
1272 	sound.playFX(20);
1273 }
1274 
wizardEye()1275 void Spells::wizardEye() {
1276 	Party &party = *_vm->_party;
1277 	Sound &sound = *_vm->_sound;
1278 
1279 	party._wizardEyeActive = true;
1280 	party._automapOn = false;
1281 	sound.playFX(20);
1282 }
1283 
frostbite2()1284 void Spells::frostbite2() {
1285 	Combat &combat = *_vm->_combat;
1286 	Sound &sound = *_vm->_sound;
1287 
1288 	combat._monsterDamage = 35;
1289 	combat._damageType = DT_COLD;
1290 	combat._rangeType = RT_SINGLE;
1291 	sound.playFX(15);
1292 	combat.rangedAttack(POW_FROST_WAVE);
1293 }
1294 
1295 } // End of namespace Xeen
1296