aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/engine/magic_eob.cpp
diff options
context:
space:
mode:
authorathrxx2019-01-26 01:31:34 +0100
committerathrxx2019-03-06 20:48:15 +0100
commit1dfdcc7252ac83643cae7a7447c025da2af63843 (patch)
treeb6736d006bf67d5264dd171c336f0915695d1f88 /engines/kyra/engine/magic_eob.cpp
parent8b53d20b51771680c3d31aa02c0285b7a8be4e85 (diff)
downloadscummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.gz
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.bz2
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.zip
KYRA: cleanup dir
Reorganize all files in sub directories. The file placement isn't as intuitive as it might be for other engines, which is probably the reason why this hasn't been done before.
Diffstat (limited to 'engines/kyra/engine/magic_eob.cpp')
-rw-r--r--engines/kyra/engine/magic_eob.cpp1381
1 files changed, 1381 insertions, 0 deletions
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
new file mode 100644
index 0000000000..d443b85c18
--- /dev/null
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -0,0 +1,1381 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/engine/eobcommon.h"
+#include "kyra/resource/resource.h"
+#include "common/system.h"
+
+namespace Kyra {
+
+void EoBCoreEngine::useMagicBookOrSymbol(int charIndex, int type) {
+ EoBCharacter *c = &_characters[charIndex];
+ _openBookSpellLevel = c->slotStatus[3];
+ _openBookSpellSelectedItem = c->slotStatus[2];
+ _openBookSpellListOffset = c->slotStatus[4];
+ _openBookChar = charIndex;
+ _openBookType = type;
+ _openBookSpellList = (type == 1) ? _clericSpellList : _mageSpellList;
+ _openBookAvailableSpells = (type == 1) ? c->clericSpells : c->mageSpells;
+ int8 *tmp = _openBookAvailableSpells + _openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem;
+
+ if (*tmp <= 0) {
+ for (bool loop = true; loop && _openBookSpellSelectedItem < 10;) {
+ tmp = _openBookAvailableSpells + _openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem;
+ if (*tmp > 0) {
+ if (_openBookSpellSelectedItem > 5) {
+ _openBookSpellListOffset = 6;
+ _openBookSpellSelectedItem -= 6;
+ }
+ loop = false;
+ } else {
+ _openBookSpellSelectedItem++;
+ }
+ }
+
+ if (_openBookSpellSelectedItem == 10) {
+ _openBookSpellListOffset = 0;
+ _openBookSpellSelectedItem = 6;
+ }
+ }
+
+ if (!_updateFlags)
+ _screen->copyRegion(64, 121, 0, 0, 112, 56, 0, 10, Screen::CR_NO_P_CHECK);
+ _updateFlags = 1;
+ gui_setPlayFieldButtons();
+ gui_drawSpellbook();
+}
+
+void EoBCoreEngine::useMagicScroll(int charIndex, int type, int weaponSlot) {
+ _openBookCharBackup = _openBookChar;
+ _openBookTypeBackup = _openBookType;
+ _castScrollSlot = weaponSlot + 1;
+ _openBookChar = charIndex;
+ _openBookType = type <= _clericSpellOffset ? 0 : 1;
+ castSpell(type, weaponSlot);
+}
+
+void EoBCoreEngine::usePotion(int charIndex, int weaponSlot) {
+ EoBCharacter *c = &_characters[charIndex];
+
+ int val = deleteInventoryItem(charIndex, weaponSlot);
+ snd_playSoundEffect(10);
+
+ if (_flags.gameID == GI_EOB1)
+ val--;
+
+ switch (val) {
+ case 0:
+ sparkEffectDefensive(charIndex);
+ c->strengthCur = 22;
+ c->strengthExtCur = 0;
+ setCharEventTimer(charIndex, 546 * rollDice(1, 4, 4), 7, 1);
+ break;
+
+ case 1:
+ sparkEffectDefensive(charIndex);
+ modifyCharacterHitpoints(charIndex, rollDice(2, 4, 2));
+ break;
+
+ case 2:
+ sparkEffectDefensive(charIndex);
+ modifyCharacterHitpoints(charIndex, rollDice(3, 8, 3));
+ break;
+
+ case 3:
+ statusAttack(charIndex, 2, _potionStrings[0], 0, 1, 8, 1);
+ c->effectFlags &= ~0x2000;
+ if (c->flags & 2)
+ return;
+ break;
+
+ case 4:
+ sparkEffectDefensive(charIndex);
+ c->food = 100;
+ if (_currentControlMode)
+ gui_drawCharPortraitWithStats(charIndex);
+ break;
+
+ case 5:
+ sparkEffectDefensive(charIndex);
+ c->effectFlags |= 0x10000;
+ setCharEventTimer(charIndex, 546 * rollDice(1, 4, 4), 12, 1);
+ snd_playSoundEffect(100);
+ gui_drawCharPortraitWithStats(charIndex);
+ break;
+
+ case 6:
+ sparkEffectDefensive(charIndex);
+ c->effectFlags |= 0x40;
+ gui_drawCharPortraitWithStats(charIndex);
+ break;
+
+ case 7:
+ sparkEffectDefensive(charIndex);
+ neutralizePoison(charIndex);
+ break;
+
+ default:
+ break;
+ }
+
+ _txt->printMessage(_potionStrings[1], -1, c->name, _potionEffectStrings[val]);
+}
+
+void EoBCoreEngine::useWand(int charIndex, int weaponSlot) {
+ int v = _items[_characters[charIndex].inventory[weaponSlot]].value;
+ if (!v) {
+ _txt->printMessage(_wandStrings[0]);
+ return;
+ }
+
+ if (v != 5)
+ useMagicScroll(charIndex, _wandTypes[v], weaponSlot);
+ else if (_flags.gameID == GI_EOB2)
+ useMagicScroll(charIndex, 64, weaponSlot);
+ else {
+ uint16 bl1 = calcNewBlockPosition(_currentBlock, _currentDirection);
+ uint16 bl2 = calcNewBlockPosition(bl1, _currentDirection);
+ snd_playSoundEffect(98);
+ sparkEffectOffensive();
+
+ if ((_wllWallFlags[_levelBlockProperties[bl2].walls[_currentDirection ^ 2]] & 4) && !(_levelBlockProperties[bl2].flags & 7) && (_levelBlockProperties[bl1].flags & 7)) {
+ for (int i = 0; i < 30; i++) {
+ if (_monsters[i].block != bl1)
+ continue;
+ placeMonster(&_monsters[i], bl2, -1);
+ _sceneUpdateRequired = true;
+ }
+ } else {
+ _txt->printMessage(_wandStrings[1]);
+ }
+ }
+}
+
+void EoBCoreEngine::castSpell(int spell, int weaponSlot) {
+ EoBSpell *s = &_spells[spell];
+ EoBCharacter *c = &_characters[_openBookChar];
+ _activeSpell = spell;
+
+ if ((s->flags & 0x100) && (c->effectFlags & 0x40))
+ // remove invisibility effect
+ removeCharacterEffect(_flags.gameID == GI_EOB1 ? 8 : 10, _openBookChar, 1);
+
+ int ci = _openBookChar;
+ if (ci > 3)
+ ci -= 2;
+
+ _activeSpellCharacterPos = _dropItemDirIndex[(_currentDirection << 2) + ci];
+
+ if (s->flags & 0x400) {
+ if (c->inventory[0] && c->inventory[1]) {
+ printWarning(_magicStrings1[2]);
+ return;
+ }
+
+ if (isMagicEffectItem(c->inventory[0]) || isMagicEffectItem(c->inventory[1])) {
+ printWarning(_magicStrings1[3]);
+ return;
+ }
+ }
+
+ if (!(_flags.gameID == GI_EOB2 && _activeSpell == 62)) {
+ if (!_castScrollSlot) {
+ int8 tmp = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem];
+ if (_openBookSpellListOffset + _openBookSpellSelectedItem < 8)
+ memmove(&_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem], &_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem + 1], 8 - (_openBookSpellListOffset + _openBookSpellSelectedItem));
+ _openBookAvailableSpells[_openBookSpellLevel * 10 + 8] = -tmp;
+ if (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem] < 0) {
+ if (--_openBookSpellSelectedItem == -1) {
+ if (_openBookSpellListOffset) {
+ _openBookSpellListOffset = 0;
+ _openBookSpellSelectedItem = 5;
+ } else {
+ _openBookSpellSelectedItem = 6;
+ }
+ }
+ }
+ } else if (weaponSlot != -1) {
+ updateUsedCharacterHandItem(_openBookChar, weaponSlot);
+ }
+ }
+
+ _txt->printMessage(_magicStrings1[4], -1, c->name, s->name);
+
+ if (s->flags & 0x20) {
+ castOnWhomDialogue();
+ return;
+ }
+
+ _activeSpellCharId = _openBookChar;
+ startSpell(spell);
+}
+
+void EoBCoreEngine::removeCharacterEffect(int spell, int charIndex, int showWarning) {
+ assert(spell >= 0);
+ EoBCharacter *c = &_characters[charIndex];
+ EoBSpell *s = &_spells[spell];
+
+ if (showWarning) {
+ int od = _screen->curDimIndex();
+ Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
+ _screen->setScreenDim(7);
+ printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 3 : 2], c->name, s->name).c_str());
+ _screen->setScreenDim(od);
+ _screen->setFont(of);
+ }
+
+ if (s->endCallback)
+ (this->*s->endCallback)(c);
+
+ if (s->flags & 1)
+ c->effectFlags &= ~s->effectFlags;
+
+ if (s->flags & 4)
+ _partyEffectFlags &= ~s->effectFlags;
+
+ if (s->flags & 0x200) {
+ for (int i = 0; i < 6; i++) {
+ if (!testCharacter(i, 1))
+ continue;
+ if (!testCharacter(i, 2) && !(s->flags & 0x800))
+ continue;
+ _characters[i].effectFlags &= ~s->effectFlags;
+ }
+ }
+
+ if (s->flags & 0x2)
+ recalcArmorClass(_activeSpellCharId);
+
+ if (showWarning) {
+ if (s->flags & 0x20A0)
+ gui_drawCharPortraitWithStats(charIndex);
+ else if (s->flags & 0x40)
+ gui_drawAllCharPortraitsWithStats();
+ }
+}
+
+void EoBCoreEngine::removeAllCharacterEffects(int charIndex) {
+ EoBCharacter *c = &_characters[charIndex];
+ c->effectFlags = 0;
+ memset(c->effectsRemainder, 0, 4);
+
+ for (int i = 0; i < 10; i++) {
+ if (c->events[i] < 0)
+ removeCharacterEffect(-c->events[i], charIndex, 0);
+ c->timers[i] = 0;
+ c->events[i] = 0;
+ }
+
+ setupCharacterTimers();
+ recalcArmorClass(charIndex);
+ c->disabledSlots = 0;
+ c->slotStatus[0] = c->slotStatus[1] = 0;
+ c->damageTaken = 0;
+ c->strengthCur = c->strengthMax;
+ c->strengthExtCur = c->strengthExtMax;
+ gui_drawAllCharPortraitsWithStats();
+}
+
+void EoBCoreEngine::castOnWhomDialogue() {
+ printWarning(_magicStrings3[0]);
+ gui_setCastOnWhomButtons();
+}
+
+void EoBCoreEngine::startSpell(int spell) {
+ EoBSpell *s = &_spells[spell];
+ EoBCharacter *c = &_characters[_activeSpellCharId];
+ snd_playSoundEffect(s->sound);
+
+ if (s->flags & 0xA0)
+ sparkEffectDefensive(_activeSpellCharId);
+ else if (s->flags & 0x40)
+ sparkEffectDefensive(-1);
+ else if (s->flags & 0x1000)
+ sparkEffectOffensive();
+
+ if (s->flags & 0x20) {
+ _txt->printMessage(c->name);
+ _txt->printMessage(_flags.gameID == GI_EOB1 ? _magicStrings3[1] : _magicStrings1[5]);
+ }
+
+ if ((s->flags & 0x30) && (s->effectFlags & c->effectFlags)) {
+ if (_flags.gameID == GI_EOB2)
+ printWarning(Common::String::format(_magicStrings7[0], c->name, s->name).c_str());
+ } else if ((s->flags & 0x50) && (s->effectFlags & _partyEffectFlags)) {
+ if (_flags.gameID == GI_EOB1 && s->effectFlags == 0x400)
+ // EOB 1 only warns in case of a bless spell
+ printWarning(_magicStrings8[1]);
+ else
+ printWarning(Common::String::format(_magicStrings7[1], s->name).c_str());
+ } else {
+ if (s->flags & 8)
+ setSpellEventTimer(spell, s->timingPara[0], s->timingPara[1], s->timingPara[2], s->timingPara[3]);
+
+ _returnAfterSpellCallback = false;
+ if (s->startCallback)
+ (this->*s->startCallback)();
+ if (_returnAfterSpellCallback)
+ return;
+
+ if (s->flags & 1)
+ c->effectFlags |= s->effectFlags;
+ if (s->flags & 4)
+ _partyEffectFlags |= s->effectFlags;
+
+ if (s->flags & 0x200) {
+ for (int i = 0; i < 6; i++) {
+ if (!testCharacter(i, 1))
+ continue;
+ if (!testCharacter(i, 2) && !(s->flags & 0x800))
+ continue;
+ _characters[i].effectFlags |= s->effectFlags;
+ }
+ }
+
+ if (s->flags & 2)
+ recalcArmorClass(_activeSpellCharId);
+
+ if (s->flags & 0x20A0)
+ gui_drawCharPortraitWithStats(_activeSpellCharId);
+ if (s->flags & 0x40)
+ gui_drawAllCharPortraitsWithStats();
+ }
+
+ if (_castScrollSlot) {
+ gui_updateSlotAfterScrollUse();
+ } else {
+ _characters[_openBookChar].disabledSlots |= 4;
+ setCharEventTimer(_openBookChar, 72, 11, 1);
+ gui_toggleButtons();
+ gui_drawSpellbook();
+ }
+
+ if (_flags.gameID == GI_EOB2) {
+ //_castSpellWd1 = spell;
+ runLevelScript(_currentBlock, 0x800);
+ //_castSpellWd1 = 0;
+ }
+}
+
+void EoBCoreEngine::sparkEffectDefensive(int charIndex) {
+ int first = charIndex;
+ int last = charIndex;
+ if (charIndex == -1) {
+ first = 0;
+ last = 5;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ for (int ii = first; ii <= last; ii++) {
+ if (!testCharacter(ii, 1) || (_currentControlMode && ii != _updateCharNum))
+ continue;
+
+ gui_drawCharPortraitWithStats(ii);
+
+ for (int iii = 0; iii < 4; iii++) {
+ int shpIndex = ((_sparkEffectDefSteps[i] & _sparkEffectDefSubSteps[iii]) >> _sparkEffectDefShift[iii]);
+ if (!shpIndex)
+ continue;
+ int x = _sparkEffectDefAdd[iii * 2] - 8;
+ int y = _sparkEffectDefAdd[iii * 2 + 1];
+ if (_currentControlMode) {
+ x += 181;
+ y += 3;
+ } else {
+ x += (_sparkEffectDefX[ii] << 3);
+ y += _sparkEffectDefY[ii];
+ }
+ _screen->drawShape(0, _sparkShapes[shpIndex - 1], x, y, 0);
+ _screen->updateScreen();
+ }
+ }
+ delay(2 * _tickLength);
+ }
+
+ for (int i = first; i < last; i++)
+ gui_drawCharPortraitWithStats(i);
+}
+
+void EoBCoreEngine::sparkEffectOffensive() {
+ disableSysTimer(2);
+ _screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 2, Screen::CR_NO_P_CHECK);
+ int sh = _flags.useHiColorMode ? 9 : 8;
+
+ for (int i = 0; i < 16; i++)
+ _screen->copyRegionToBuffer(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << sh]);
+
+ for (int i = 0; i < 11; i++) {
+ for (int ii = 0; ii < 16; ii++)
+ _screen->copyBlockToPage(2, _sparkEffectOfX[ii], _sparkEffectOfY[ii], 16, 16, &_spellAnimBuffer[ii << sh]);
+
+ for (int ii = 0; ii < 16; ii++) {
+ int shpIndex = (_sparkEffectOfFlags1[i] & _sparkEffectOfFlags2[ii]) >> _sparkEffectOfShift[ii];
+ if (shpIndex)
+ _screen->drawShape(2, _sparkShapes[shpIndex - 1], _sparkEffectOfX[ii], _sparkEffectOfY[ii], 0);
+ }
+ delay(2 * _tickLength);
+ _screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ }
+
+ for (int i = 0; i < 16; i++)
+ _screen->copyBlockToPage(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << sh]);
+
+ _screen->updateScreen();
+ enableSysTimer(2);
+}
+
+void EoBCoreEngine::setSpellEventTimer(int spell, int timerBaseFactor, int timerLength, int timerLevelFactor, int updateExistingTimer) {
+ assert(spell >= 0);
+ int l = _openBookType == 1 ? getClericPaladinLevel(_openBookChar) : getMageLevel(_openBookChar);
+ uint32 countdown = timerLength * timerBaseFactor + timerLength * l * timerLevelFactor;
+ setCharEventTimer(_activeSpellCharId, countdown, -spell, updateExistingTimer);
+}
+
+void EoBCoreEngine::sortCharacterSpellList(int charIndex) {
+ int8 *list = _characters[charIndex].mageSpells;
+
+ for (int i = 0; i < 16;) {
+ bool p = false;
+ for (int ii = 0; ii < 9; ii++) {
+ int8 *pos = &list[ii];
+
+ int s1 = pos[0];
+ int s2 = pos[1];
+
+ if (s1 == 0)
+ s1 = 80;
+ else if (s1 < 0)
+ s1 = s1 * -1 + 40;
+
+ if (s2 == 0)
+ s2 = 80;
+ else if (s2 < 0)
+ s2 = s2 * -1 + 40;
+
+ if (s1 > s2) {
+ SWAP(pos[0], pos[1]);
+ p = true;
+ }
+ }
+
+ if (p)
+ continue;
+
+ list += 10;
+ if (++i == 8)
+ list = _characters[charIndex].clericSpells;
+ }
+}
+
+bool EoBCoreEngine::magicObjectDamageHit(EoBFlyingObject *fo, int dcTimes, int dcPips, int dcOffs, int level) {
+ int ignoreAttackerId = fo->flags & 0x10;
+ int singleTargetCheckAdjacent = fo->flags & 1;
+ int blockDamage = fo->flags & 2;
+ int hitTest = fo->flags & 4;
+
+ int savingThrowType = 5;
+ int savingThrowEffect = 3;
+ if (fo->flags & 8) {
+ savingThrowType = 4;
+ savingThrowEffect = 0;
+ }
+
+ int dmgFlag = _spells[fo->callBackIndex].damageFlags;
+ if (fo->attackerId >= 0)
+ dmgFlag |= 0x800;
+
+ bool res = false;
+ if (!level)
+ level = 1;
+
+ if ((_levelBlockProperties[fo->curBlock].flags & 7) && (fo->attackerId >= 0 || ignoreAttackerId)) {
+ _preventMonsterFlash = true;
+
+ for (const int16 *m = findBlockMonsters(fo->curBlock, fo->curPos, fo->direction, blockDamage, singleTargetCheckAdjacent); *m != -1; m++) {
+ int dmg = rollDice(dcTimes, dcPips, dcOffs) * level;
+
+ if (hitTest) {
+ if (!characterAttackHitTest(fo->attackerId, *m, 0, 0))
+ continue;
+ }
+
+ calcAndInflictMonsterDamage(&_monsters[*m], 0, 0, dmg, dmgFlag, savingThrowType, savingThrowEffect);
+ res = true;
+ }
+ updateAllMonsterShapes();
+
+ } else if (fo->curBlock == _currentBlock && (fo->attackerId < 0 || ignoreAttackerId)) {
+ if (blockDamage) {
+ for (int i = 0; i < 6; i++) {
+ if (!testCharacter(i, 1))
+ continue;
+ if (hitTest && !monsterAttackHitTest(&_monsters[0], i))
+ continue;
+
+ int dmg = rollDice(dcTimes, dcPips, dcOffs) * level;
+ res = true;
+
+ calcAndInflictCharacterDamage(i, 0, 0, dmg, dmgFlag, savingThrowType, savingThrowEffect);
+ }
+ } else {
+ int c = _dscItemPosIndex[(_currentDirection << 2) + (fo->curPos & 3)];
+ if ((c > 2) && (testCharacter(4, 1) || testCharacter(5, 1)) && rollDice(1, 2, -1))
+ c += 2;
+
+ if (!fo->item && (_characters[c].effectFlags & 8)) {
+ res = true;
+ } else {
+ if ((_characters[c].flags & 1) && (!hitTest || monsterAttackHitTest(&_monsters[0], c))) {
+ int dmg = rollDice(dcTimes, dcPips, dcOffs) * level;
+ res = true;
+ calcAndInflictCharacterDamage(c, 0, 0, dmg, dmgFlag, savingThrowType, savingThrowEffect);
+ }
+ }
+ }
+ }
+
+ if (res && (fo->flags & 0x40))
+ explodeObject(fo, fo->curBlock, fo->item);
+ else if ((_flags.gameID == GI_EOB1 && fo->item == 5) || (_flags.gameID == GI_EOB2 && fo->item == 4))
+ res = false;
+
+ return res;
+}
+
+bool EoBCoreEngine::magicObjectStatusHit(EoBMonsterInPlay *m, int type, bool tryEvade, int mod) {
+ EoBMonsterProperty *p = &_monsterProps[m->type];
+ if (tryEvade) {
+ if (tryMonsterAttackEvasion(m) || (p->immunityFlags & 0x10))
+ return true;
+ }
+
+ if (trySavingThrow(m, 0, p->level, mod, 6))
+ return false;
+
+ int para = 0;
+
+ switch (type) {
+ case 0:
+ case 1:
+ case 2:
+ para = (type == 0) ? ((p->typeFlags & 1) ? 1 : 0) : ((type == 1) ? ((p->typeFlags & 2) ? 1 : 0) : 1);
+ if (para && !(p->immunityFlags & 2)) {
+ m->mode = 10;
+ m->spellStatusLeft = 15;
+ }
+
+ break;
+
+ case 3:
+ if (!(p->immunityFlags & 8))
+ inflictMonsterDamage(m, 1000, true);
+ break;
+
+ case 4:
+ inflictMonsterDamage(m, 1000, true);
+ break;
+
+ case 5:
+ m->flags |= 0x20;
+ _sceneUpdateRequired = true;
+ break;
+
+ case 6:
+ if (!(_flags.gameID == GI_EOB1 && !(p->typeFlags & 3)) && !(p->immunityFlags & 4) && m->mode != 7 && m->mode != 8 && m->mode != 10) {
+ m->mode = 0;
+ m->spellStatusLeft = 20;
+ m->flags |= 8;
+ walkMonsterNextStep(m, -1, (getNextMonsterDirection(m->block, _currentBlock) ^ 4) >> 1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool EoBCoreEngine::turnUndeadHit(EoBMonsterInPlay *m, int hitChance, int casterLevel) {
+ assert(_monsterProps[m->type].tuResist > 0);
+ uint8 e = _turnUndeadEffect[_monsterProps[m->type].tuResist * 14 + MIN(casterLevel, 14)];
+
+ if (e == 0xFF) {
+ calcAndInflictMonsterDamage(m, 0, 0, 500, 0x200, 5, 3);
+ } else if (hitChance < e) {
+ return false;
+ } else {
+ m->mode = 0;
+ m->flags |= 8;
+ m->spellStatusLeft = 40;
+ m->dir = (getNextMonsterDirection(m->block, _currentBlock) ^ 4) >> 1;
+ }
+
+ return true;
+}
+
+int EoBCoreEngine::getMagicWeaponSlot(int charIndex) {
+ return _characters[charIndex].inventory[1] ? 0 : 1;
+}
+
+void EoBCoreEngine::causeWounds(int dcTimes, int dcPips, int dcOffs) {
+ if (_openBookChar == 0 || _openBookChar == 1) {
+ int d = getClosestMonster(_openBookChar, calcNewBlockPosition(_currentBlock, _currentDirection));
+ if (d != -1) {
+ if (!characterAttackHitTest(_openBookChar, d, 0, 1))
+ return;
+
+ if (dcTimes == -1) {
+ dcOffs = _monsters[d].hitPointsMax - rollDice(1, 4);
+ dcTimes = dcPips = 0;
+ }
+ calcAndInflictMonsterDamage(&_monsters[d], dcTimes, dcPips, dcOffs, 0x801, 4, 2);
+ } else {
+ printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 4 : 3], _characters[_openBookChar].name).c_str());
+ }
+ } else {
+ printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 5 : 4], _characters[_openBookChar].name).c_str());
+ }
+}
+
+int EoBCoreEngine::createMagicWeaponType(int invFlags, int handFlags, int armorClass, int allowedClasses, int dmgNumDice, int dmgPips, int dmgInc, int extraProps) {
+ int i = 51;
+ for (; i < 57; i++) {
+ if (_itemTypes[i].armorClass == -30)
+ break;
+ }
+
+ if (i == 57)
+ return -1;
+
+ EoBItemType *tp = &_itemTypes[i];
+ tp->invFlags = invFlags;
+ tp->requiredHands = 0;
+ tp->handFlags = handFlags;
+ tp->armorClass = armorClass;
+ tp->allowedClasses = allowedClasses;
+ tp->dmgNumDiceL = tp->dmgNumDiceS = dmgNumDice;
+ tp->dmgNumPipsL = tp->dmgNumPipsS = dmgPips;
+ tp->dmgIncL = tp->dmgIncS = dmgInc;
+ tp->extraProperties = extraProps;
+
+ return i;
+}
+
+Item EoBCoreEngine::createMagicWeaponItem(int flags, int icon, int value, int type) {
+ Item i = 11;
+ for (; i < 17; i++) {
+ if (_items[i].block == -2)
+ break;
+ }
+
+ if (i == 17)
+ return -1;
+
+ EoBItem *itm = &_items[i];
+ itm->flags = 0x20 | flags;
+ itm->icon = icon;
+ itm->value = value;
+ itm->type = type;
+ itm->pos = 0;
+ itm->block = 0;
+ itm->nameId = itm->nameUnid = 0;
+ itm->prev = itm->next = 0;
+
+ return i;
+}
+
+void EoBCoreEngine::removeMagicWeaponItem(Item item) {
+ _itemTypes[_items[item].type].armorClass = -30;
+ _items[item].block = -2;
+ _items[item].level = 0xFF;
+}
+
+void EoBCoreEngine::updateWallOfForceTimers() {
+ uint32 ct = _system->getMillis();
+ for (int i = 0; i < 5; i++) {
+ if (!_wallsOfForce[i].block)
+ continue;
+ if (_wallsOfForce[i].duration < ct)
+ destroyWallOfForce(i);
+ }
+}
+
+void EoBCoreEngine::destroyWallOfForce(int index) {
+ memset(_levelBlockProperties[_wallsOfForce[index].block].walls, 0, 4);
+ _wallsOfForce[index].block = 0;
+ _sceneUpdateRequired = true;
+}
+
+int EoBCoreEngine::findSingleSpellTarget(int dist) {
+ uint16 bl = _currentBlock;
+ int res = -1;
+
+ for (int i = 0; i < dist && res == -1; i++) {
+ bl = calcNewBlockPosition(bl, _currentDirection);
+ res = getClosestMonster(_openBookChar, bl);
+ if (!(_wllWallFlags[_levelBlockProperties[bl].walls[_sceneDrawVarDown]] & 1)) {
+ i = dist;
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+int EoBCoreEngine::findFirstCharacterSpellTarget() {
+ int curCharIndex = rollDice(1, 6, -1);
+ for (_characterSpellTarget = 0; _characterSpellTarget < 6; _characterSpellTarget++) {
+ if (testCharacter(curCharIndex, 3))
+ return curCharIndex;
+ if (++curCharIndex == 6)
+ curCharIndex = 0;
+ }
+ return -1;
+}
+
+int EoBCoreEngine::findNextCharacterSpellTarget(int curCharIndex) {
+ for (_characterSpellTarget++; _characterSpellTarget < 6;) {
+ if (++curCharIndex == 6)
+ curCharIndex = 0;
+ if (testCharacter(curCharIndex, 3))
+ return curCharIndex;
+ }
+ return -1;
+}
+
+int EoBCoreEngine::charDeathSavingThrow(int charIndex, int div) {
+ bool _beholderOrgBhv = true;
+ // Due to a bug in the original code the saving throw result is completely ignored
+ // here. The Beholders' disintegrate spell will alway succeed while their flesh to
+ // stone spell will always fail.
+ if (_beholderOrgBhv)
+ div >>= 1;
+ else
+ div = specialAttackSavingThrow(charIndex, 4) ? 1 : 0;
+ return div;
+}
+
+void EoBCoreEngine::printWarning(const char *str) {
+ _txt->printMessage(str);
+ snd_playSoundEffect(79);
+}
+
+void EoBCoreEngine::printNoEffectWarning() {
+ printWarning(_magicStrings4[0]);
+}
+
+void EoBCoreEngine::spellCallback_start_armor() {
+ _characters[_activeSpellCharId].effectsRemainder[0] = getMageLevel(_openBookChar) + 8;
+ if ((getDexterityArmorClassModifier(_characters[_activeSpellCharId].dexterityCur) + 6) >= _characters[_activeSpellCharId].armorClass)
+ printWarning(Common::String::format(_magicStrings6[0], _characters[_activeSpellCharId].name).c_str());
+}
+
+void EoBCoreEngine::spellCallback_start_burningHands() {
+ static const int16 bX[] = { 0, 152, 24, 120, 56, 88 };
+ static const int8 bY[] = { 64, 64, 56, 56, 56, 56 };
+
+ for (int i = 0; i < 6; i++)
+ drawBlockObject(i & 1, 0, _firebeamShapes[(5 - i) >> 1], bX[i], bY[i], 0);
+ _screen->updateScreen();
+ delay(2 * _tickLength);
+
+ int cl = getMageLevel(_openBookChar);
+ int bl = calcNewBlockPosition(_currentBlock, _currentDirection);
+
+ const int8 *pos = getMonstersOnBlockPositions(bl);
+ _preventMonsterFlash = true;
+
+ int numDest = (_flags.gameID == GI_EOB1) ? 2 : 6;
+ const uint8 *d = &_burningHandsDest[_currentDirection * (_flags.gameID == GI_EOB1 ? 2 : 8)];
+
+ for (int i = 0; i < numDest; i++, d++) {
+ if (pos[*d] == -1)
+ continue;
+ calcAndInflictMonsterDamage(&_monsters[pos[*d]], 1, 3, cl << 1, 0x21, 4, 0);
+ }
+
+ updateAllMonsterShapes();
+ _sceneUpdateRequired = true;
+}
+
+void EoBCoreEngine::spellCallback_start_detectMagic() {
+ setHandItem(_itemInHand);
+}
+
+bool EoBCoreEngine::spellCallback_end_detectMagic(void *) {
+ setHandItem(_itemInHand);
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_magicMissile() {
+ launchMagicObject(_openBookChar, 0, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_magicMissile(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 1, 4, 1, (getMageLevel(fo->attackerId) - 1) >> 1);
+}
+
+void EoBCoreEngine::spellCallback_start_shockingGrasp() {
+ int t = createMagicWeaponType(0, 0, 0, 0x0F, 1, 8, getMageLevel(_openBookChar), 1);
+ Item i = (t != -1) ? createMagicWeaponItem(0x10, 82, 0, t) : -1;
+ if (t == -1 || i == -1) {
+ if (_flags.gameID == GI_EOB2)
+ printWarning(_magicStrings8[0]);
+ removeCharacterEffect(_activeSpell, _activeSpellCharId, 0);
+ deleteCharEventTimer(_activeSpellCharId, -_activeSpell);
+ _returnAfterSpellCallback = true;
+ } else {
+ _characters[_activeSpellCharId].inventory[getMagicWeaponSlot(_activeSpellCharId)] = i;
+ }
+}
+
+bool EoBCoreEngine::spellCallback_end_shockingGraspFlameBlade(void *obj) {
+ EoBCharacter *c = (EoBCharacter *)obj;
+ for (int i = 0; i < 2; i++) {
+ if (isMagicEffectItem(c->inventory[i])) {
+ removeMagicWeaponItem(c->inventory[i]);
+ c->inventory[i] = 0;
+ }
+ }
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_improvedIdentify() {
+ for (int i = 0; i < 2; i++) {
+ Item itm = _characters[_activeSpellCharId].inventory[i];
+ if (itm)
+ _items[itm].flags |= 0x40;
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_melfsAcidArrow() {
+ launchMagicObject(_openBookChar, 1, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_melfsAcidArrow(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ assert(fo);
+ return magicObjectDamageHit(fo, 2, 4, 0, getMageLevel(fo->attackerId) / 3);
+}
+
+void EoBCoreEngine::spellCallback_start_dispelMagic() {
+ for (int i = 0; i < 6; i++) {
+ if (testCharacter(i, 1))
+ removeAllCharacterEffects(i);
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_fireball() {
+ launchMagicObject(_openBookChar, 2, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_fireball(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 1, 6, 0, getMageLevel(fo->attackerId));
+}
+
+void EoBCoreEngine::spellCallback_start_flameArrow() {
+ launchMagicObject(_openBookChar, 3, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_flameArrow(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 5, 6, 0, getMageLevel(fo->attackerId));
+}
+
+void EoBCoreEngine::spellCallback_start_holdPerson() {
+ launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 4 : 3, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_holdPerson(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ bool res = false;
+
+ if (_flags.gameID == GI_EOB2 && fo->curBlock == _currentBlock) {
+ // party hit
+ int numChar = rollDice(1, 4, 0);
+ int charIndex = rollDice(1, 6, -1);
+ for (int i = 0; i < 6 && numChar; i++) {
+ if (testCharacter(charIndex, 3)) {
+ statusAttack(charIndex, 4, _magicStrings8[1], 4, 5, 9, 1);
+ numChar--;
+ }
+ charIndex = (charIndex + 1) % 6;
+ }
+ res = true;
+
+ } else {
+ // monster hit
+ for (const int16 *m = findBlockMonsters(fo->curBlock, fo->curPos, fo->direction, 1, 1); *m != -1; m++)
+ res |= magicObjectStatusHit(&_monsters[*m], 0, true, 4);
+ }
+
+ return res;
+}
+
+void EoBCoreEngine::spellCallback_start_lightningBolt() {
+ launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 5 : 4, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_lightningBolt(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 1, 6, 0, getMageLevel(fo->attackerId));
+}
+
+void EoBCoreEngine::spellCallback_start_vampiricTouch() {
+ int t = createMagicWeaponType(0, 0, 0, 0x0F, getMageLevel(_openBookChar) >> 1, 6, 0, 1);
+ Item i = (t != -1) ? createMagicWeaponItem(0x18, 83, 0, t) : -1;
+ if (t == -1 || i == -1) {
+ if (_flags.gameID == GI_EOB2)
+ printWarning(_magicStrings8[2]);
+ removeCharacterEffect(_activeSpell, _activeSpellCharId, 0);
+ deleteCharEventTimer(_activeSpellCharId, -_activeSpell);
+ _returnAfterSpellCallback = true;
+ } else {
+ _characters[_activeSpellCharId].inventory[getMagicWeaponSlot(_activeSpellCharId)] = i;
+ }
+}
+
+bool EoBCoreEngine::spellCallback_end_vampiricTouch(void *obj) {
+ EoBCharacter *c = (EoBCharacter *)obj;
+ if (c->hitPointsCur > c->hitPointsMax)
+ c->hitPointsCur = c->hitPointsMax;
+ spellCallback_end_shockingGraspFlameBlade(obj);
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_fear() {
+ sparkEffectOffensive();
+ uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
+ for (int i = 0; i < 30; i++) {
+ if (_monsters[i].block == bl)
+ magicObjectStatusHit(&_monsters[i], 6, true, 4);
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_iceStorm() {
+ launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 6 : 5, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_iceStorm(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ static int8 blockAdv[] = { -32, 32, 1, -1 };
+ bool res = magicObjectDamageHit(fo, 1, 6, 0, getMageLevel(fo->attackerId));
+ if (res) {
+ for (int i = 0; i < 4; i++) {
+ uint16 bl = fo->curBlock;
+ fo->curBlock = (fo->curBlock + blockAdv[i]) & 0x3FF;
+ magicObjectDamageHit(fo, 1, 6, 0, getMageLevel(fo->attackerId));
+ fo->curBlock = bl;
+ }
+ }
+ return res;
+}
+
+void EoBCoreEngine::spellCallback_start_stoneSkin() {
+ _characters[_activeSpellCharId].effectsRemainder[1] = (getMageLevel(_openBookChar) >> 1) + rollDice(1, 4);
+}
+
+void EoBCoreEngine::spellCallback_start_removeCurse() {
+ for (int i = 0; i < 27; i++) {
+ Item itm = _characters[_activeSpellCharId].inventory[i];
+ if (itm && (_items[itm].flags & 0x20) && !isMagicEffectItem(itm))
+ _items[itm].flags = (_items[itm].flags & ~0x20) | 0x40;
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_coneOfCold() {
+ const int8 *dirTables[] = { _coneOfColdDest1, _coneOfColdDest2, _coneOfColdDest3, _coneOfColdDest4 };
+
+ int cl = getMageLevel(_openBookChar);
+
+ _screen->setCurPage(2);
+ _screen->fillRect(0, 0, 176, 120, 0);
+ _screen->setGfxParameters(0, 0, _screen->getPagePixel(2, 0, 0));
+ drawSceneShapes(7);
+ _screen->setCurPage(0);
+ disableSysTimer(2);
+ _screen->drawVortex(150, 50, 10, 1, 100, _coneOfColdGfxTbl, _coneOfColdGfxTblSize);
+ enableSysTimer(2);
+
+ const int8 *tbl = dirTables[_currentDirection];
+ _preventMonsterFlash = true;
+
+ for (int i = 0; i < 7; i++) {
+ for (const int16 *m = findBlockMonsters((_currentBlock + tbl[i]) & 0x3FF, 4, _currentDirection, 1, 1); *m != -1; m++)
+ calcAndInflictMonsterDamage(&_monsters[*m], cl, 4, cl, 0x41, 5, 0);
+ }
+
+ updateAllMonsterShapes();
+}
+
+void EoBCoreEngine::spellCallback_start_holdMonster() {
+ launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 7 : 6, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_holdMonster(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ bool res = false;
+ for (const int16 *m = findBlockMonsters(fo->curBlock, fo->curPos, fo->direction, 1, 1); *m != -1; m++)
+ res |= magicObjectStatusHit(&_monsters[*m], 1, true, 4);
+ return res;
+}
+
+void EoBCoreEngine::spellCallback_start_wallOfForce() {
+ uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
+ LevelBlockProperty *l = &_levelBlockProperties[bl];
+ if (l->walls[0] || l->walls[1] || l->walls[2] || l->walls[3] || (l->flags & 7)) {
+ printWarning(_magicStrings8[3]);
+ return;
+ }
+
+ uint32 dur = 0xFFFFFFFF;
+ int s = 0;
+ int i = 0;
+
+ for (; i < 5; i++) {
+ if (!_wallsOfForce[i].block)
+ break;
+ if (_wallsOfForce[i].duration < dur) {
+ dur = _wallsOfForce[i].duration;
+ s = i;
+ }
+ }
+
+ if (i == 5)
+ destroyWallOfForce(s);
+
+ memset(_levelBlockProperties[bl].walls, 74, 4);
+ _wallsOfForce[s].block = bl;
+ _wallsOfForce[s].duration = _system->getMillis() + (((getMageLevel(_openBookChar) * 546) >> 1) + 546) * _tickLength;
+ _sceneUpdateRequired = true;
+}
+
+void EoBCoreEngine::spellCallback_start_disintegrate() {
+ int d = findSingleSpellTarget(1);
+ if (d != -1)
+ magicObjectStatusHit(&_monsters[d], 4, true, 4);
+ memset(_visibleBlocks[13]->walls, 0, 4);
+ _sceneUpdateRequired = true;
+}
+
+void EoBCoreEngine::spellCallback_start_fleshToStone() {
+ sparkEffectOffensive();
+ int t = getClosestMonster(_openBookChar, calcNewBlockPosition(_currentBlock, _currentDirection));
+ if (t != -1)
+ magicObjectStatusHit(&_monsters[t], 5, true, 4);
+ else
+ printWarning(_magicStrings8[4]);
+}
+
+void EoBCoreEngine::spellCallback_start_stoneToFlesh() {
+ if (_characters[_activeSpellCharId].flags & 8)
+ _characters[_activeSpellCharId].flags &= ~8;
+ else
+ printNoEffectWarning();
+}
+
+void EoBCoreEngine::spellCallback_start_trueSeeing() {
+ _wllVmpMap[46] = 0;
+}
+
+bool EoBCoreEngine::spellCallback_end_trueSeeing(void *) {
+ _wllVmpMap[46] = 1;
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_slayLiving() {
+ int d = findSingleSpellTarget(2);
+ if (d != -1) {
+ if (!magicObjectStatusHit(&_monsters[d], 3, true, 4))
+ inflictMonsterDamage(&_monsters[d], rollDice(2, 8, 1), true);
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_powerWordStun() {
+ int d = findSingleSpellTarget(2);
+ if (d != -1) {
+ if (_monsters[d].hitPointsCur < 90)
+ magicObjectStatusHit(&_monsters[d], 5, true, 4);
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_causeLightWounds() {
+ causeWounds(1, 8, 0);
+}
+
+void EoBCoreEngine::spellCallback_start_cureLightWounds() {
+ modifyCharacterHitpoints(_activeSpellCharId, rollDice(1, 8));
+}
+
+void EoBCoreEngine::spellCallback_start_aid() {
+ if (!testCharacter(_activeSpellCharId, 3)) {
+ printNoEffectWarning();
+ } else if (_characters[_activeSpellCharId].effectsRemainder[3]) {
+ printWarning(Common::String::format(_magicStrings8[(_flags.gameID == GI_EOB1) ? 2 : 5], _characters[_activeSpellCharId].name).c_str());
+ } else {
+ _characters[_activeSpellCharId].effectsRemainder[3] = rollDice(1, 8);
+ _characters[_activeSpellCharId].hitPointsCur += _characters[_activeSpellCharId].effectsRemainder[3];
+ _characters[_activeSpellCharId].effectFlags |= 0x1000;
+ return;
+ }
+
+ removeCharacterEffect(_activeSpell, _activeSpellCharId, 0);
+ deleteCharEventTimer(_activeSpellCharId, -_activeSpell);
+}
+
+bool EoBCoreEngine::spellCallback_end_aid(void *obj) {
+ EoBCharacter *c = (EoBCharacter *)obj;
+ c->hitPointsCur -= c->effectsRemainder[3];
+ c->effectsRemainder[3] = 0;
+ c->effectFlags &= ~0x1000;
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_flameBlade() {
+ int t = createMagicWeaponType(0, 0, 0, 0x0F, 1, 4, 4, 1);
+ Item i = (t != -1) ? createMagicWeaponItem(0, 84, 0, t) : -1;
+ if (t == -1 || i == -1) {
+ if (_flags.gameID == GI_EOB2)
+ printWarning(_magicStrings8[0]);
+ removeCharacterEffect(_activeSpell, _activeSpellCharId, 0);
+ deleteCharEventTimer(_activeSpellCharId, -_activeSpell);
+ _returnAfterSpellCallback = true;
+ } else {
+ _characters[_activeSpellCharId].inventory[getMagicWeaponSlot(_activeSpellCharId)] = i;
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_slowPoison() {
+ if (_characters[_activeSpellCharId].flags & 2) {
+ _characters[_activeSpellCharId].effectFlags |= 0x2000;
+ setSpellEventTimer(_activeSpell, 1, 32760, 1, 1);
+ } else {
+ printNoEffectWarning();
+ }
+}
+
+bool EoBCoreEngine::spellCallback_end_slowPoison(void *obj) {
+ EoBCharacter *c = (EoBCharacter *)obj;
+ c->effectFlags &= ~0x2000;
+ return true;
+}
+
+void EoBCoreEngine::spellCallback_start_createFood() {
+ for (int i = 0; i < 6; i++) {
+ if (!testCharacter(i, 3))
+ continue;
+ _characters[i].food = 100;
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_removeParalysis() {
+ int numChar = 4;
+ for (int i = 0; i < 6; i++) {
+ if (!(_characters[i].flags & 4) || !numChar)
+ continue;
+ _characters[i].flags &= ~4;
+ numChar--;
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_causeSeriousWounds() {
+ causeWounds(2, 8, 1);
+}
+
+void EoBCoreEngine::spellCallback_start_cureSeriousWounds() {
+ modifyCharacterHitpoints(_activeSpellCharId, rollDice(2, 8, 1));
+}
+
+void EoBCoreEngine::spellCallback_start_neutralizePoison() {
+ if (_characters[_activeSpellCharId].flags & 2)
+ neutralizePoison(_activeSpellCharId);
+ else
+ printNoEffectWarning();
+}
+
+void EoBCoreEngine::spellCallback_start_causeCriticalWounds() {
+ causeWounds(3, 8, 3);
+}
+
+void EoBCoreEngine::spellCallback_start_cureCriticalWounds() {
+ modifyCharacterHitpoints(_activeSpellCharId, rollDice(3, 8, 3));
+}
+
+void EoBCoreEngine::spellCallback_start_flameStrike() {
+ launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 8 : 7, _currentBlock, _activeSpellCharacterPos, _currentDirection);
+}
+
+bool EoBCoreEngine::spellCallback_end_flameStrike(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 6, 8, 0, 0);
+}
+
+void EoBCoreEngine::spellCallback_start_raiseDead() {
+ if (_characters[_activeSpellCharId].hitPointsCur == -10 && ((_characters[_activeSpellCharId].raceSex >> 1) != 1)) {
+ _characters[_activeSpellCharId].hitPointsCur = 1;
+ gui_drawCharPortraitWithStats(_activeSpellCharId);
+ } else {
+ printNoEffectWarning();
+ }
+}
+
+void EoBCoreEngine::spellCallback_start_harm() {
+ causeWounds(-1, -1, -1);
+}
+
+void EoBCoreEngine::spellCallback_start_heal() {
+ EoBCharacter *c = &_characters[_activeSpellCharId];
+ if (c->hitPointsMax <= c->hitPointsCur)
+ printWarning(_magicStrings4[0]);
+ else
+ modifyCharacterHitpoints(_activeSpellCharId, c->hitPointsMax - c->hitPointsCur);
+}
+
+void EoBCoreEngine::spellCallback_start_layOnHands() {
+ modifyCharacterHitpoints(_activeSpellCharId, _characters[_openBookChar].level[0] << 1);
+}
+
+void EoBCoreEngine::spellCallback_start_turnUndead() {
+ uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
+ if (!(_levelBlockProperties[bl].flags & 7))
+ return;
+
+ int cl = _openBookCasterLevel ? _openBookCasterLevel : getClericPaladinLevel(_openBookChar);
+ int r = rollDice(1, 20);
+ bool hit = false;
+
+ for (const int16 *m = findBlockMonsters(bl, 4, 4, 1, 1); *m != -1; m++) {
+ if ((_monsterProps[_monsters[*m].type].typeFlags & 4) && !(_monsters[*m].flags & 0x10)) {
+ _preventMonsterFlash = true;
+ _monsters[*m].flags |= 0x10;
+ hit |= turnUndeadHit(&_monsters[*m], r, cl);
+ }
+ }
+
+ if (hit) {
+ turnUndeadAutoHit();
+ snd_playSoundEffect(95);
+ updateAllMonsterShapes();
+ }
+
+ _preventMonsterFlash = false;
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_lightningBolt(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 0, 0, 12, 1);
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_fireball1(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ bool res = false;
+ if (_partyEffectFlags & 0x20000) {
+ res = magicObjectDamageHit(fo, 4, 10, 6, 0);
+ if (res) {
+ gui_drawAllCharPortraitsWithStats();
+ _partyEffectFlags &= ~0x20000;
+ }
+ } else {
+ res = magicObjectDamageHit(fo, 12, 10, 6, 0);
+ }
+ return res;
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_fireball2(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ return magicObjectDamageHit(fo, 0, 0, 18, 0);
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_deathSpell(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ if (fo->curBlock != _currentBlock)
+ return false;
+
+ int numDest = rollDice(1, 4);
+ _txt->printMessage(_magicStrings2[2]);
+ for (int d = findFirstCharacterSpellTarget(); d != -1 && numDest; d = findNextCharacterSpellTarget(d)) {
+ if (_characters[d].level[0] < 8) {
+ inflictCharacterDamage(d, 300);
+ numDest--;
+ }
+ }
+
+ return true;
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_disintegrate(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ if (fo->curBlock != _currentBlock)
+ return false;
+
+ int d = findFirstCharacterSpellTarget();
+ if (d != -1) {
+ if (!charDeathSavingThrow(d, 1)) {
+ inflictCharacterDamage(d, 300);
+ _txt->printMessage(_magicStrings2[1], -1, _characters[d].name);
+ }
+ }
+
+ return true;
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_causeCriticalWounds(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ if (fo->curBlock != _currentBlock)
+ return false;
+
+ int d = findFirstCharacterSpellTarget();
+ if (d != -1) {
+ _txt->printMessage(_magicStrings2[3], -1, _characters[d].name);
+ inflictCharacterDamage(d, rollDice(3, 8, 3));
+ }
+
+ return true;
+}
+
+bool EoBCoreEngine::spellCallback_end_monster_fleshToStone(void *obj) {
+ EoBFlyingObject *fo = (EoBFlyingObject *)obj;
+ if (fo->curBlock != _currentBlock)
+ return false;
+
+ int d = findFirstCharacterSpellTarget();
+ while (d != -1) {
+ if (!charDeathSavingThrow(d, 2)) {
+ statusAttack(d, 8, _magicStrings2[4], 5, 0, 0, 1);
+ d = -1;
+ } else {
+ d = findNextCharacterSpellTarget(d);
+ }
+ }
+
+ return true;
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB