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