scummvm/engines/xeen/spells.cpp

1296 lines
30 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "xeen/spells.h"
#include "xeen/dialogs/dialogs_items.h"
#include "xeen/dialogs/dialogs_spells.h"
#include "xeen/files.h"
#include "xeen/resources.h"
#include "xeen/xeen.h"
namespace Xeen {
Spells::Spells(XeenEngine *vm) : _vm(vm) {
_lastCaster = 0;
load();
}
void Spells::load() {
File f1((g_vm->getGameID() == GType_Clouds) ? "spells.cld" : "spells.xen", 1);
while (f1.pos() < f1.size())
_spellNames.push_back(f1.readString());
f1.close();
}
int Spells::calcSpellCost(int spellId, int expenseFactor) const {
int amount = Res.SPELL_COSTS[spellId];
return (amount >= 0) ? (amount * 100) << expenseFactor :
(amount * -500) << expenseFactor;
}
int Spells::calcSpellPoints(int spellId, int expenseFactor) const {
int amount = Res.SPELL_COSTS[spellId];
return (amount >= 0) ? amount : amount * -1 * expenseFactor;
}
typedef void(Spells::*SpellMethodPtr)();
void Spells::executeSpell(MagicSpell spellId) {
static const SpellMethodPtr SPELL_LIST[76] = {
&Spells::acidSpray, &Spells::awaken, &Spells::beastMaster,
&Spells::bless, &Spells::clairvoyance, &Spells::coldRay,
&Spells::createFood, &Spells::cureDisease, &Spells::cureParalysis,
&Spells::curePoison, &Spells::cureWounds, &Spells::dancingSword,
&Spells::dayOfProtection, &Spells::dayOfSorcery, &Spells::deadlySwarm,
&Spells::detectMonster, &Spells::divineIntervention, &Spells::dragonSleep,
&Spells::elementalStorm, &Spells::enchantItem, &Spells::energyBlast,
&Spells::etherialize, &Spells::fantasticFreeze, &Spells::fieryFlail,
&Spells::fingerOfDeath, &Spells::fireball, &Spells::firstAid,
&Spells::flyingFist, &Spells::frostbite, &Spells::golemStopper,
&Spells::heroism, &Spells::holyBonus, &Spells::holyWord,
&Spells::hypnotize, &Spells::identifyMonster, &Spells::implosion,
&Spells::incinerate, &Spells::inferno, &Spells::insectSpray,
&Spells::itemToGold, &Spells::jump, &Spells::levitate,
&Spells::light, &Spells::lightningBolt, &Spells::lloydsBeacon,
&Spells::magicArrow, &Spells::massDistortion, &Spells::megaVolts,
&Spells::moonRay, &Spells::naturesCure, &Spells::pain,
&Spells::poisonVolley, &Spells::powerCure, &Spells::powerShield,
&Spells::prismaticLight, &Spells::protectionFromElements, &Spells::raiseDead,
&Spells::rechargeItem, &Spells::resurrection, &Spells::revitalize,
&Spells::shrapMetal, &Spells::sleep, &Spells::sparks,
&Spells::starBurst, &Spells::stoneToFlesh, &Spells::sunRay,
&Spells::superShelter, &Spells::suppressDisease, &Spells::suppressPoison,
&Spells::teleport, &Spells::timeDistortion, &Spells::townPortal,
&Spells::toxicCloud, &Spells::turnUndead, &Spells::walkOnWater,
&Spells::wizardEye
};
(this->*SPELL_LIST[spellId])();
}
void Spells::spellFailed() {
ErrorScroll::show(_vm, Res.SPELL_FAILED, WT_NONFREEZED_WAIT);
}
void Spells::castItemSpell(int itemSpellId) {
assert(itemSpellId != 0);
switch (itemSpellId) {
case 16:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_Jump);
return;
}
break;
case 21:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_WizardEye);
return;
}
break;
case 28:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_LloydsBeacon);
return;
}
break;
case 33:
frostbite2();
break;
case 42:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_Teleport);
return;
}
break;
case 48:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_SuperShelter);
return;
}
break;
case 55:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_TownPortal);
return;
}
break;
case 58:
if (_vm->_mode == MODE_COMBAT) {
NotWhileEngaged::show(_vm, MS_Etheralize);
return;
}
break;
default:
break;
}
static const MagicSpell spells[74] = {
NO_SPELL, MS_Light, MS_Awaken, MS_MagicArrow, MS_FirstAid, MS_FlyingFist,
MS_EnergyBlast, MS_Sleep, MS_Revitalize, MS_CureWounds, MS_Sparks,
MS_Shrapmetal, MS_InsectSpray, MS_ToxicCloud, MS_ProtFromElements, MS_Pain,
MS_Jump, MS_BeastMaster, MS_Clairvoyance, MS_TurnUndead, MS_Levitate,
MS_WizardEye, MS_Bless, MS_IdentifyMonster, MS_LightningBolt, MS_HolyBonus,
MS_PowerCure, MS_NaturesCure, MS_LloydsBeacon, MS_PowerShield, MS_Heroism,
MS_Hynotize, MS_WalkOnWater, NO_SPELL, MS_DetectMonster, MS_Fireball,
MS_ColdRay, MS_CurePoison, MS_AcidSpray, MS_TimeDistortion, MS_DragonSleep,
MS_CureDisease, MS_Teleport, MS_FingerOfDeath, MS_CureParalysis, MS_GolemStopper,
MS_PoisonVolley, MS_DeadlySwarm, MS_SuperShelter, MS_DayOfProtection, MS_DayOfProtection,
MS_CreateFood, MS_FieryFlail, MS_RechargeItem, MS_FantasticFreeze, MS_TownPortal,
MS_StoneToFlesh, MS_RaiseDead, MS_Etheralize, MS_DancingSword, MS_MoonRay,
MS_MassDistortion, MS_PrismaticLight, MS_EnchantItem, MS_Incinerate, MS_HolyWord,
MS_Resurrection, MS_ElementalStorm, MS_MegaVolts, MS_Inferno, MS_SunRay,
MS_Implosion, MS_StarBurst, MS_DivineIntervention
};
executeSpell(spells[itemSpellId]);
}
int Spells::castSpell(Character *c, MagicSpell spellId) {
Combat &combat = *_vm->_combat;
Interface &intf = *_vm->_interface;
int oldTillMove = intf._tillMove;
int result = 1;
combat._oldCharacter = c;
// Try and subtract the SP and gem requirements for the spell
int resultError = subSpellCost(*c, spellId);
if (resultError) {
CantCast::show(_vm, spellId, resultError);
result = -1;
} else {
// Some spells have special handling
switch (spellId) {
case MS_EnchantItem:
case MS_Etheralize:
case MS_Jump:
case MS_LloydsBeacon:
case MS_SuperShelter:
case MS_Teleport:
case MS_TownPortal:
case MS_WizardEye:
if (_vm->_mode != MODE_COMBAT) {
executeSpell(spellId);
} else {
// Return the spell costs and flag that another spell can be selected
addSpellCost(*c, spellId);
NotWhileEngaged::show(_vm, spellId);
result = -1;
}
break;
default:
executeSpell(spellId);
break;
}
}
combat._moveMonsters = 1;
intf._tillMove = oldTillMove;
return result;
}
int Spells::subSpellCost(Character &c, int spellId) {
Party &party = *_vm->_party;
int gemCost = Res.SPELL_GEM_COST[spellId];
int spCost = Res.SPELL_COSTS[spellId];
// Negative SP costs indicate it's dependent on the character's level
if (spCost <= 0) {
spCost = c.getCurrentLevel() * (spCost * -1);
}
if (spCost > c._currentSp)
// Not enough SP
return 1;
if (gemCost > (int)party._gems)
// Not enough gems
return 2;
c._currentSp -= spCost;
party._gems -= gemCost;
return 0;
}
void Spells::addSpellCost(Character &c, int spellId) {
Party &party = *_vm->_party;
int gemCost = Res.SPELL_GEM_COST[spellId];
int spCost = Res.SPELL_COSTS[spellId];
if (spCost < 1)
spCost *= -1 * c.getCurrentLevel();
c._currentSp += spCost;
party._gems += gemCost;
}
void Spells::acidSpray() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 15;
combat._damageType = DT_POISON;
combat._rangeType = RT_ALL;
sound.playFX(17);
combat.rangedAttack(POW_SPRAY);
}
void Spells::awaken() {
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
Character &c = party._activeParty[idx];
c._conditions[ASLEEP] = 0;
if (c._currentHp > 0)
c._conditions[UNCONSCIOUS] = 0;
}
intf.drawParty(true);
sound.playFX(30);
}
void Spells::beastMaster() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_BEASTMASTER;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_MAGIC_ORB);
}
void Spells::bless() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
sound.playFX(30);
party._blessed = combat._oldCharacter->getCurrentLevel();
}
void Spells::clairvoyance() {
_vm->_party->_clairvoyanceActive = true;
_vm->_sound->playFX(20);
}
void Spells::coldRay() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = _vm->getRandomNumber(2, 4) * combat._oldCharacter->getCurrentLevel();
combat._damageType = DT_COLD;
combat._rangeType = RT_ALL;
sound.playFX(15);
combat.rangedAttack(POW_COLD_RAY);
}
void Spells::createFood() {
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
party._food += party._activeParty.size();
sound.playFX(20);
}
void Spells::cureDisease() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_CureDisease);
if (!c)
return;
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[DISEASED] = 0;
intf.drawParty(true);
}
void Spells::cureParalysis() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_CureParalysis);
if (!c)
return;
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[PARALYZED] = 0;
intf.drawParty(true);
}
void Spells::curePoison() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_CurePoison);
if (!c)
return;
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[POISONED] = 0;
intf.drawParty(true);
}
void Spells::cureWounds() {
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_CureWounds);
if (!c)
return;
if (c->isDead()) {
spellFailed();
} else {
sound.playFX(30);
c->addHitPoints(15);
}
}
void Spells::dancingSword() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = _vm->getRandomNumber(6, 14) * combat._oldCharacter->getCurrentLevel();
combat._damageType = DT_PHYSICAL;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_SPARKLES);
}
void Spells::dayOfProtection() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
int lvl = combat._oldCharacter->getCurrentLevel();
party._walkOnWaterActive = true;
party._heroism = lvl;
party._holyBonus = lvl;
party._blessed = lvl;
party._poisonResistence = lvl;
party._coldResistence = lvl;
party._electricityResistence = lvl;
party._fireResistence = lvl;
party._lightCount = lvl;
sound.playFX(20);
}
void Spells::dayOfSorcery() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
int lvl = combat._oldCharacter->getCurrentLevel();
party._powerShield = lvl;
party._clairvoyanceActive = true;
party._wizardEyeActive = true;
party._levitateCount = 1;
party._lightCount = lvl;
party._automapOn = false;
sound.playFX(20);
}
void Spells::deadlySwarm() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 40;
combat._damageType = DT_PHYSICAL;
combat._rangeType = RT_GROUP;
sound.playFX(13);
combat.rangedAttack(POW_DEADLY_SWARM);
}
void Spells::detectMonster() {
DetectMonsters::show(_vm);
}
void Spells::divineIntervention() {
Combat &combat = *_vm->_combat;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
Character &castChar = *combat._oldCharacter;
if ((castChar._tempAge + 5) > 250) {
castChar._tempAge = 250;
} else {
castChar._tempAge += 5;
}
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
Character &c = party._activeParty[idx];
Common::fill(&c._conditions[CURSED], &c._conditions[ERADICATED], 0);
if (!c._conditions[ERADICATED])
c._currentHp = c.getMaxHP();
}
sound.playFX(20);
intf.drawParty(true);
}
void Spells::dragonSleep() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_DRAGONSLEEP;
combat._rangeType = RT_SINGLE;
sound.playFX(18);
combat.rangedAttack(POW_MAGIC_ORB);
}
void Spells::elementalStorm() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
static const int STORM_FX_LIST[4] = { 13, 14, 15, 17 };
static const PowType STORM_MA_LIST[4] = {
POW_FIREBALL, POW_SPARKS, POW_FROST_WAVE, POW_SPRAY
};
combat._monsterDamage = 150;
combat._damageType = (DamageType)_vm->getRandomNumber(DT_FIRE, DT_POISON);
combat._rangeType = RT_ALL;
sound.playFX(STORM_FX_LIST[combat._damageType - DT_FIRE]);
combat.rangedAttack(STORM_MA_LIST[combat._damageType - DT_FIRE]);
}
void Spells::enchantItem() {
Mode oldMode = _vm->_mode;
Character *c = SpellOnWho::show(_vm, MS_EnchantItem);
if (!c)
return;
ItemsDialog::show(_vm, c, ITEMMODE_ENCHANT);
_vm->_mode = oldMode;
}
void Spells::energyBlast() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
combat._damageType = DT_ENERGY;
combat._rangeType = RT_SINGLE;
sound.playFX(16);
combat.rangedAttack(POW_ENERGY_BLAST);
}
void Spells::etherialize() {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
Common::Point pt = party._mazePosition + Common::Point(
Res.SCREEN_POSITIONING_X[party._mazeDirection][7],
Res.SCREEN_POSITIONING_Y[party._mazeDirection][7]
);
if ((map.mazeData()._mazeFlags & RESTRICTION_ETHERIALIZE) ||
map.mazeLookup(pt, 0, 0xffff) == INVALID_CELL) {
spellFailed();
} else {
party._mazePosition = pt;
sound.playFX(51);
}
}
void Spells::fantasticFreeze() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 40;
combat._damageType = DT_COLD;
combat._rangeType = RT_GROUP;
sound.playFX(15);
combat.rangedAttack(POW_COLD_RAY);
}
void Spells::fieryFlail() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 100;
combat._damageType = DT_FIRE;
combat._rangeType = RT_SINGLE;
sound.playFX(13);
combat.rangedAttack(POW_FIERY_FLAIL);
}
void Spells::fingerOfDeath() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_FINGEROFDEATH;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_SPARKLES);
}
void Spells::fireball() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = _vm->getRandomNumber(3, 7) * combat._oldCharacter->getCurrentLevel();
combat._damageType = DT_FIRE;
combat._rangeType = RT_GROUP;
sound.playFX(13);
combat.rangedAttack(POW_FIREBALL);
}
void Spells::firstAid() {
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_FirstAid);
if (!c)
return;
if (c->isDead()) {
spellFailed();
} else {
sound.playFX(30);
c->addHitPoints(6);
}
}
void Spells::flyingFist() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 6;
combat._damageType = DT_PHYSICAL;
combat._rangeType = RT_SINGLE;
sound.playFX(18);
combat.rangedAttack(POW_SPARKLES);
}
void Spells::frostbite() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 35;
combat._damageType = DT_COLD;
combat._rangeType = RT_SINGLE;
sound.playFX(8);
combat.rangedAttack(POW_COLD_RAY);
}
void Spells::golemStopper() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_GOLEMSTOPPER;
combat._rangeType = RT_SINGLE;
sound.playFX(16);
combat.rangedAttack(POW_STOPPER);
}
void Spells::heroism() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
sound.playFX(30);
party._heroism = combat._oldCharacter->getCurrentLevel();
}
void Spells::holyBonus() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
sound.playFX(30);
party._holyBonus = combat._oldCharacter->getCurrentLevel();
}
void Spells::holyWord() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_HOLYWORD;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_ENERGY_BLAST);
}
void Spells::hypnotize() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_HYPNOTIZE;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_MAGIC_ORB);
}
void Spells::identifyMonster() {
Combat &combat = *_vm->_combat;
if (combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1
&& combat._attackMonsters[2] == -1) {
spellFailed();
} else {
IdentifyMonster::show(_vm);
}
}
void Spells::implosion() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 1000;
combat._damageType = DT_ENERGY;
combat._rangeType = RT_SINGLE;
sound.playFX(18);
combat.rangedAttack(POW_STOPPER);
}
void Spells::incinerate() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 250;
combat._damageType = DT_FIRE;
combat._rangeType = RT_SINGLE;
sound.playFX(22);
combat.rangedAttack(POW_INCINERATE);
}
void Spells::inferno() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 250;
combat._damageType = DT_FIRE;
combat._rangeType = RT_GROUP;
sound.playFX(13);
combat.rangedAttack(POW_INCINERATE);
}
void Spells::insectSpray() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_INSECT_SPRAY;
combat._rangeType = RT_GROUP;
sound.playFX(17);
combat.rangedAttack(POW_SPRAY);
}
void Spells::itemToGold() {
Windows &windows = *_vm->_windows;
Character *c = SpellOnWho::show(_vm, MS_ItemToGold);
if (!c)
return;
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_FF;
windows[11].close();
ItemsDialog::show(_vm, c, ITEMMODE_TO_GOLD);
_vm->_mode = oldMode;
}
void Spells::jump() {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
if (map._isOutdoors) {
map.getCell(7);
if (map._currentWall != 1) {
map.getCell(14);
if (map._currentSurfaceId != 0 && map._currentWall != 1) {
party._mazePosition += Common::Point(
Res.SCREEN_POSITIONING_X[party._mazeDirection][14],
Res.SCREEN_POSITIONING_Y[party._mazeDirection][14]
);
sound.playFX(51);
party._stepped = true;
return;
}
}
} else {
Common::Point pt = party._mazePosition + Common::Point(
Res.SCREEN_POSITIONING_X[party._mazeDirection][7],
Res.SCREEN_POSITIONING_Y[party._mazeDirection][7]);
if (!map.mazeLookup(party._mazePosition, Res.MONSTER_GRID_BITMASK[party._mazeDirection]) &&
!map.mazeLookup(pt, Res.MONSTER_GRID_BITMASK[party._mazeDirection])) {
party._mazePosition += Common::Point(
Res.SCREEN_POSITIONING_X[party._mazeDirection][14],
Res.SCREEN_POSITIONING_Y[party._mazeDirection][14]
);
sound.playFX(51);
party._stepped = true;
return;
}
}
spellFailed();
}
void Spells::levitate() {
_vm->_party->_levitateCount = 1;
_vm->_sound->playFX(20);
}
void Spells::light() {
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
++party._lightCount;
if (intf._obscurity != OBSCURITY_BLACK)
party._stepped = true;
sound.playFX(39);
}
void Spells::lightningBolt() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = _vm->getRandomNumber(4, 6) * combat._oldCharacter->getCurrentLevel();
combat._damageType = DT_ELECTRICAL;
combat._rangeType = RT_GROUP;
sound.playFX(14);
combat.rangedAttack(POW_LIGHTNING);
}
void Spells::lloydsBeacon() {
if (_vm->_map->mazeData()._mazeFlags & RESTRICTION_LLOYDS_BEACON) {
spellFailed();
} else {
if (!LloydsBeacon::show(_vm))
spellFailed();
}
}
void Spells::magicArrow() {
Combat &combat = *_vm->_combat;
combat._monsterDamage = 0;
combat._damageType = DT_MAGIC_ARROW;
combat._rangeType = RT_SINGLE;
combat.rangedAttack(POW_ARROW);
}
void Spells::massDistortion() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_MASS_DISTORTION;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_STOPPER);
}
void Spells::megaVolts() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 150;
combat._damageType = DT_ELECTRICAL;
combat._rangeType = RT_GROUP;
sound.playFX(14);
combat.rangedAttack(POW_MEGAVOLTS);
}
void Spells::moonRay() {
Combat &combat = *_vm->_combat;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 30;
combat._damageType = DT_ENERGY;
combat._rangeType = RT_ALL;
sound.playFX(16);
combat.rangedAttack(POW_ENERGY_BLAST);
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
sound.playFX(30);
party._activeParty[idx].addHitPoints(_vm->getRandomNumber(1, 30));
}
intf.drawParty(true);
}
void Spells::naturesCure() {
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_NaturesCure);
if (!c)
return;
if (c->isDead()) {
spellFailed();
} else {
sound.playFX(30);
c->addHitPoints(25);
}
}
void Spells::pain() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_PHYSICAL;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_SPARKLES);
}
void Spells::poisonVolley() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 10;
combat._damageType = DT_POISON_VOLLEY;
combat._rangeType = RT_ALL;
sound.playFX(49);
combat.rangedAttack(POW_ARROW);
}
void Spells::powerCure() {
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_PowerCure);
if (!c)
return;
if (c->isDead()) {
spellFailed();
} else {
sound.playFX(30);
c->addHitPoints(_vm->getRandomNumber(2, 12) * _vm->_combat->_oldCharacter->getCurrentLevel());
}
}
void Spells::powerShield() {
Combat &combat = *_vm->_combat;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
sound.playFX(20);
party._powerShield = combat._oldCharacter->getCurrentLevel();
}
void Spells::prismaticLight() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 80;
combat._damageType = (DamageType)_vm->getRandomNumber(DT_PHYSICAL, DT_ENERGY);
combat._rangeType = RT_ALL;
sound.playFX(18);
combat.rangedAttack(POW_SPARKLES);
}
void Spells::protectionFromElements() {
Combat &combat = *_vm->_combat;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
Character &c = *combat._oldCharacter;
int resist = MIN(c.getCurrentLevel() * 2 + 5, (uint)200);
int elementType = SelectElement::show(_vm, MS_ProtFromElements);
if (elementType != -1) {
switch (elementType) {
case DT_FIRE:
party._fireResistence = resist;
break;
case DT_ELECTRICAL:
party._fireResistence = resist;
break;
case DT_COLD:
party._coldResistence = resist;
break;
case DT_POISON:
party._poisonResistence = resist;
break;
default:
break;
}
sound.playFX(20);
intf.drawParty(true);
}
}
void Spells::raiseDead() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_RaiseDead);
if (!c)
return;
if (!c->_conditions[DEAD]) {
spellFailed();
} else {
c->_conditions[DEAD] = 0;
c->_conditions[UNCONSCIOUS] = 0;
c->_currentHp = 0;
sound.playFX(30);
c->addHitPoints(1);
if (--c->_endurance._permanent < 1)
c->_endurance._permanent = 1;
intf.drawParty(true);
}
}
void Spells::rechargeItem() {
Mode oldMode = _vm->_mode;
Character *c = SpellOnWho::show(_vm, MS_RechargeItem);
if (!c)
return;
ItemsDialog::show(_vm, c, ITEMMODE_RECHARGE);
_vm->_mode = oldMode;
}
void Spells::resurrection() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_RaiseDead);
if (!c)
return;
if (!c->_conditions[ERADICATED]) {
spellFailed();
sound.playFX(30);
} else {
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[ERADICATED] = 0;
if (--c->_endurance._permanent < 1)
c->_endurance._permanent = 1;
if ((c->_tempAge + 5) >= 250)
c->_tempAge = 250;
else
c->_tempAge += 5;
intf.drawParty(true);
}
}
void Spells::revitalize() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_Revitalize);
if (!c)
return;
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[WEAK] = 0;
intf.drawParty(true);
}
void Spells::shrapMetal() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
combat._damageType = DT_PHYSICAL;
combat._rangeType = RT_GROUP;
sound.playFX(16);
combat.rangedAttack(POW_DEADLY_SWARM);
}
void Spells::sleep() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_SLEEP;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_MAGIC_ORB);
}
void Spells::sparks() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = combat._oldCharacter->getCurrentLevel() * 2;
combat._damageType = DT_ELECTRICAL;
combat._rangeType = RT_GROUP;
sound.playFX(14);
combat.rangedAttack(POW_SPARKS);
}
void Spells::starBurst() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 500;
combat._damageType = DT_FIRE;
combat._rangeType = RT_ALL;
sound.playFX(13);
combat.rangedAttack(POW_DEADLY_SWARM);
}
void Spells::stoneToFlesh() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_StoneToFlesh);
if (!c)
return;
sound.playFX(30);
c->addHitPoints(0);
c->_conditions[STONED] = 0;
intf.drawParty(true);
}
void Spells::sunRay() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 200;
combat._damageType = DT_ENERGY;
combat._rangeType = RT_ALL;
sound.playFX(16);
combat.rangedAttack(POW_ENERGY_BLAST);
}
void Spells::superShelter() {
Interface &intf = *_vm->_interface;
Map &map = *_vm->_map;
Sound &sound = *_vm->_sound;
if (map.mazeData()._mazeFlags & RESTRICTION_SUPER_SHELTER) {
spellFailed();
} else {
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_INTERACTIVE2;
sound.playFX(30);
intf.rest();
_vm->_mode = oldMode;
}
}
void Spells::suppressDisease() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_SuppressDisease);
if (!c)
return;
if (c->_conditions[DISEASED]) {
if (c->_conditions[DISEASED] >= 4)
c->_conditions[DISEASED] -= 3;
else
c->_conditions[DISEASED] = 1;
sound.playFX(20);
c->addHitPoints(0);
intf.drawParty(true);
}
}
void Spells::suppressPoison() {
Interface &intf = *_vm->_interface;
Sound &sound = *_vm->_sound;
Character *c = SpellOnWho::show(_vm, MS_FirstAid);
if (!c)
return;
if (c->_conditions[POISONED]) {
if (c->_conditions[POISONED] >= 4) {
c->_conditions[POISONED] -= 2;
} else {
c->_conditions[POISONED] = 1;
}
}
sound.playFX(20);
c->addHitPoints(0);
intf.drawParty(1);
}
void Spells::teleport() {
Map &map = *_vm->_map;
Sound &sound = *_vm->_sound;
if (map.mazeData()._mazeFlags & RESTRICTION_TELPORT) {
spellFailed();
} else {
switch (Teleport::show(_vm)) {
case 0:
spellFailed();
break;
case 1:
sound.playFX(51);
break;
default:
break;
}
}
}
void Spells::timeDistortion() {
Interface &intf = *_vm->_interface;
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
if (map.mazeData()._mazeFlags & RESTRICTION_TIME_DISTORTION) {
spellFailed();
} else {
party.moveToRunLocation();
sound.playFX(51);
intf.draw3d(true);
}
}
void Spells::townPortal() {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
if (map.mazeData()._mazeFlags & RESTRICTION_TOWN_PORTAL) {
spellFailed();
return;
}
int townNumber = TownPortal::show(_vm);
if (!townNumber)
return;
sound.playFX(51);
map._loadCcNum = map._sideTownPortal;
_vm->_files->_ccNum = map._sideTownPortal > 0;
int arrIndex = _vm->getGameID() == GType_Swords ? 2 : map._sideTownPortal;
map.load(Res.TOWN_MAP_NUMBERS[arrIndex][townNumber - 1]);
if (_vm->getGameID() == GType_Swords) {
party._mazePosition = Common::Point(8, 3);
party._mazeDirection = DIR_NORTH;
} else if (!_vm->_files->_ccNum) {
party.moveToRunLocation();
} else {
switch (townNumber) {
case 1:
party._mazePosition = Common::Point(14, 11);
party._mazeDirection = DIR_SOUTH;
break;
case 2:
party._mazePosition = Common::Point(5, 12);
party._mazeDirection = DIR_WEST;
break;
case 3:
party._mazePosition = Common::Point(2, 15);
party._mazeDirection = DIR_EAST;
break;
case 4:
party._mazePosition = Common::Point(8, 11);
party._mazeDirection = DIR_NORTH;
break;
case 5:
party._mazePosition = Common::Point(2, 6);
party._mazeDirection = DIR_NORTH;
break;
default:
break;
}
}
}
void Spells::toxicCloud() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 10;
combat._damageType = DT_POISON;
combat._rangeType = RT_GROUP;
sound.playFX(17);
combat.rangedAttack(POW_SPRAY);
}
void Spells::turnUndead() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 0;
combat._damageType = DT_UNDEAD;
combat._rangeType = RT_GROUP;
sound.playFX(18);
combat.rangedAttack(POW_ENERGY_BLAST);
}
void Spells::walkOnWater() {
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
party._walkOnWaterActive = true;
sound.playFX(20);
}
void Spells::wizardEye() {
Party &party = *_vm->_party;
Sound &sound = *_vm->_sound;
party._wizardEyeActive = true;
party._automapOn = false;
sound.playFX(20);
}
void Spells::frostbite2() {
Combat &combat = *_vm->_combat;
Sound &sound = *_vm->_sound;
combat._monsterDamage = 35;
combat._damageType = DT_COLD;
combat._rangeType = RT_SINGLE;
sound.playFX(15);
combat.rangedAttack(POW_FROST_WAVE);
}
} // End of namespace Xeen