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 #ifdef ENABLE_EOB
24 
25 #include "kyra/engine/kyra_rpg.h"
26 #include "kyra/resource/resource.h"
27 #include "kyra/sound/sound_intern.h"
28 #include "kyra/sound/sound_adlib.h"
29 #include "kyra/script/script_eob.h"
30 #include "kyra/engine/timer.h"
31 #include "kyra/gui/debugger.h"
32 
33 #include "common/config-manager.h"
34 #include "common/translation.h"
35 
36 #include "gui/error.h"
37 
38 #include "backends/keymapper/keymapper.h"
39 
40 namespace Kyra {
41 
42 const char *const EoBCoreEngine::kKeymapName = "eob";
43 
EoBCoreEngine(OSystem * system,const GameFlags & flags)44 EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgEngine(system, flags), _numLargeItemShapes(flags.gameID == GI_EOB1 ? 14 : 11),
45 	_numSmallItemShapes(flags.gameID == GI_EOB1 ? 23 : 26),	_numThrownItemShapes(flags.gameID == GI_EOB1 ? 12 : 9),
46 	_numItemIconShapes(flags.gameID == GI_EOB1 ? 89 : 112),	_teleporterWallId(flags.gameID == GI_EOB1 ? 52 : 44) {
47 
48 	_screen = 0;
49 	_gui = 0;
50 	_debugger = 0;
51 
52 	_playFinale = false;
53 	_runFlag = true;
54 	_configMouse = _config2431 = true;
55 	_loading = false;
56 
57 	_enableHiResDithering = false;
58 
59 	_envAudioTimer = 0;
60 	_flashShapeTimer = 0;
61 	_drawSceneTimer = 0;
62 
63 	_largeItemShapes = _smallItemShapes = _thrownItemShapes = _spellShapes = _firebeamShapes = 0;
64 	_itemIconShapes = _amigaBlueItemIconShapes = _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0;
65 	_redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = 0;
66 	_blackBoxSmallGrid = _weaponSlotGrid = _blackBoxWideGrid = _lightningColumnShape = 0;
67 
68 	memset(_largeItemShapesScl, 0, sizeof(_largeItemShapesScl));
69 	memset(_smallItemShapesScl, 0, sizeof(_smallItemShapesScl));
70 	memset(_thrownItemShapesScl, 0, sizeof(_thrownItemShapesScl));
71 
72 	_monsterAcHitChanceTable1 = _monsterAcHitChanceTable2 = 0;
73 
74 	_monsterDustStrings = 0;
75 	_enemyMageSpellList = 0;
76 	_enemyMageSfx = 0;
77 	_beholderSpellList = 0;
78 	_beholderSfx = 0;
79 
80 	_faceShapes = 0;
81 	_characters = 0;
82 	_items = 0;
83 	_itemTypes = 0;
84 	_itemNames = 0;
85 	_itemInHand = -1;
86 	_numItems = _numItemNames = 0;
87 
88 	_castScrollSlot = 0;
89 	_currentSub = 0;
90 
91 	_itemsOverlay = 0;
92 
93 	_partyEffectFlags = 0;
94 	_lastUsedItem = 0;
95 
96 	_levelDecorationRects = 0;
97 	_doorSwitches = 0;
98 	_monsterProps = 0;
99 	_monsterDecorations = 0;
100 	_monsterFlashOverlay = _monsterStoneOverlay = 0;
101 	_monsters = 0;
102 	_dstMonsterIndex = 0;
103 	_preventMonsterFlash = false;
104 
105 	_teleporterPulse = 0;
106 
107 	_dscShapeCoords = 0;
108 	_dscItemPosIndex = 0;
109 	_dscItemShpX = 0;
110 	_dscItemScaleIndex = 0;
111 	_dscItemTileIndex = 0;
112 	_dscItemShapeMap = 0;
113 	_dscDoorScaleOffs = 0;
114 	_dscDoorScaleMult1 = 0;
115 	_dscDoorScaleMult2 = 0;
116 	_dscDoorScaleMult3 = 0;
117 	_dscDoorY1 = 0;
118 	_dscDoorXE = 0;
119 
120 	_greenFadingTable = _blueFadingTable = _lightBlueFadingTable = _blackFadingTable = _greyFadingTable = 0;
121 
122 	_menuDefs = 0;
123 
124 	_exchangeCharacterId = -1;
125 	_charExchangeSwap = 0;
126 	_configHpBarGraphs = true;
127 	_configMouseBtSwap = false;
128 
129 	memset(_dialogueLastBitmap, 0, 13);
130 	_npcSequenceSub = 0;
131 	_moveCounter = 0;
132 	_partyResting = false;
133 
134 	_flyingObjects = 0;
135 
136 	_inf = 0;
137 	_stepCounter = 0;
138 	_stepsUntilScriptCall = 0;
139 	_scriptTimersMode = 3;
140 	_currentDirection = 0;
141 
142 	_openBookSpellLevel = 0;
143 	_openBookSpellSelectedItem = 0;
144 	_openBookSpellListOffset = 0;
145 	_openBookChar = _openBookCharBackup = _openBookCasterLevel = 0;
146 	_openBookType = _openBookTypeBackup = 0;
147 	_openBookSpellList = 0;
148 	_openBookAvailableSpells = 0;
149 	_activeSpellCharId = 0;
150 	_activeSpellCharacterPos = 0;
151 	_activeSpell = 0;
152 	_characterSpellTarget = 0;
153 	_returnAfterSpellCallback = false;
154 	_spells = 0;
155 	_spellAnimBuffer = 0;
156 	_clericSpellOffset = 0;
157 	_restPartyElapsedTime = _disableElapsedTime = 0;
158 	_allowSkip = false;
159 	_allowImport = false;
160 
161 	_wallsOfForce = 0;
162 
163 	_rrCount = 0;
164 	memset(_rrNames, 0, 10 * sizeof(const char *));
165 	memset(_rrId, 0, 10 * sizeof(int8));
166 
167 	_mainMenuStrings = _levelGainStrings = _monsterSpecAttStrings = _characterGuiStringsHp = 0;
168 	_characterGuiStringsWp = _characterGuiStringsWr = _characterGuiStringsSt = 0;
169 	_characterGuiStringsIn = _characterStatusStrings7 = _characterStatusStrings8 = 0;
170 	_characterStatusStrings9 = _characterStatusStrings12 = _characterStatusStrings13 = 0;
171 	_classModifierFlags = _saveThrowLevelIndex = _saveThrowModDiv = _saveThrowModExt = 0;
172 	_wandTypes = _drawObjPosIndex = _flightObjFlipIndex = _expObjectTblIndex = 0;
173 	_expObjectShpStart = _expObjectTlMode = _expObjectAnimTbl1 = _expObjectAnimTbl2 = _expObjectAnimTbl3 = 0;
174 	_monsterStepTable0 = _monsterStepTable1 = _monsterStepTable2 = _monsterStepTable3 = 0;
175 	_projectileWeaponAmmoTypes = _flightObjShpMap = _flightObjSclIndex = 0;
176 	_monsterCloseAttPosTable1 = _monsterCloseAttPosTable2 = _monsterCloseAttChkTable1 = 0;
177 	_monsterCloseAttChkTable2 = _monsterCloseAttDstTable1 = _monsterCloseAttDstTable2 = 0;
178 	_monsterProximityTable = _findBlockMonstersTable = _wallOfForceDsY = _wallOfForceDsNumW = 0;
179 	_wallOfForceDsNumH = _wallOfForceShpId = _wllFlagPreset = _teleporterShapeCoords = 0;
180 	_monsterCloseAttUnkTable = _monsterFrmOffsTable1 = _monsterFrmOffsTable2 = 0;
181 	_monsterDirChangeTable = _portalSeq = 0;
182 	_wallOfForceDsX = 0;
183 	_expObjectAnimTbl1Size = _expObjectAnimTbl2Size = _expObjectAnimTbl3Size = 0;
184 	_wllFlagPresetSize = _scriptTimersCount = _buttonList1Size = _buttonList2Size = 0;
185 	_buttonList3Size = _buttonList4Size = _buttonList5Size = _buttonList6Size = 0;
186 	_buttonList7Size = _buttonList8Size = 0;
187 	_inventorySlotsY = _mnDef = 0;
188 	_transferStringsScummVM = 0;
189 	_buttonDefs = 0;
190 	_npcPreset = 0;
191 	_chargenStatStrings = _chargenRaceSexStrings = _chargenClassStrings = 0;
192 	_chargenAlignmentStrings = _pryDoorStrings = _warningStrings = _ripItemStrings = 0;
193 	_cursedString = _enchantedString = _magicObjectStrings = _magicObjectString5 = 0;
194 	_patternSuffix = _patternGrFix1 = _patternGrFix2 = _validateArmorString = 0;
195 	_validateCursedString = _validateNoDropString = _potionStrings = _wandStrings = 0;
196 	_itemMisuseStrings = _suffixStringsRings = _suffixStringsPotions = 0;
197 	_suffixStringsWands = _takenStrings = _potionEffectStrings = _yesNoStrings = 0;
198 	_npcMaxStrings = _okStrings = _npcJoinStrings = _cancelStrings = 0;
199 	_abortStrings = _saveLoadStrings = _mnWord = _mnPrompt = _bookNumbers = 0;
200 	_mageSpellList = _clericSpellList = _spellNames = _magicStrings1 = 0;
201 	_magicStrings2 = _magicStrings3 = _magicStrings4 = _magicStrings6 = 0;
202 	_magicStrings7 = _magicStrings8 = _magicStrings9 = _saveNamePatterns = 0;
203 	_spellAnimBuffer = 0;
204 	_sparkEffectDefSteps = _sparkEffectDefSubSteps = _sparkEffectDefShift = 0;
205 	_sparkEffectDefAdd = _sparkEffectDefX = _sparkEffectDefY = _sparkEffectOfShift = 0;
206 	_sparkEffectOfX = _sparkEffectOfY = _magicFlightObjectProperties = 0;
207 	_turnUndeadEffect = _burningHandsDest = _coneOfColdGfxTbl = 0;
208 	_sparkEffectOfFlags1 = _sparkEffectOfFlags2 = 0;
209 	_coneOfColdDest1 = _coneOfColdDest2 = _coneOfColdDest3 = _coneOfColdDest4 = 0;
210 	_coneOfColdGfxTblSize = 0;
211 	_menuButtonDefs = 0;
212 	_updateCharNum = 0;
213 	_menuStringsMain = _menuStringsSaveLoad = _menuStringsOnOff = _menuStringsSpells = 0;
214 	_menuStringsRest = _menuStringsDrop = _menuStringsExit = _menuStringsStarve = 0;
215 	_menuStringsScribe = _menuStringsDrop2 = _menuStringsHead = _menuStringsPoison = 0;
216 	_menuStringsMgc = _menuStringsPrefs = _menuStringsRest2 = _menuStringsRest3 = 0;
217 	_menuStringsRest4 = _menuStringsDefeat = _menuStringsTransfer = _menuStringsSpec = 0;
218 	_menuStringsSpellNo = _menuYesNoStrings = _2431Strings = _katakanaLines = _katakanaSelectStrings = 0;
219 	_errorSlotEmptyString = _errorSlotNoNameString = _menuOkString = 0;
220 	_spellLevelsMage = _spellLevelsCleric = _numSpellsCleric = _numSpellsWisAdj = _numSpellsPal = _numSpellsMage = 0;
221 	_mnNumWord = _numSpells = _mageSpellListSize = _spellLevelsMageSize = _spellLevelsClericSize = 0;
222 	_inventorySlotsX = _slotValidationFlags = _encodeMonsterShpTable = 0;
223 	_cgaMappingDefault = _cgaMappingAlt = _cgaMappingInv = _cgaLevelMappingIndex = _cgaMappingItemsL = _cgaMappingItemsS = _cgaMappingThrown = _cgaMappingIcons = _cgaMappingDeco = 0;
224 	_amigaLevelSoundList1 = _amigaLevelSoundList2 = 0;
225 	_amigaSoundMap = 0;
226 	_amigaCurSoundFile = -1;
227 	memset(_cgaMappingLevel, 0, sizeof(_cgaMappingLevel));
228 	memset(_expRequirementTables, 0, sizeof(_expRequirementTables));
229 	memset(_saveThrowTables, 0, sizeof(_saveThrowTables));
230 	memset(_doorType, 0, sizeof(_doorType));
231 	memset(_noDoorSwitch, 0, sizeof(_noDoorSwitch));
232 	memset(_scriptTimers, 0, sizeof(_scriptTimers));
233 	memset(_monsterBlockPosArray, 0, sizeof(_monsterBlockPosArray));
234 	memset(_foundMonstersArray, 0, sizeof(_foundMonstersArray));
235 
236 #define DWM0 _dscWallMapping.push_back(0)
237 #define DWM(x) _dscWallMapping.push_back(&_sceneDrawVar##x)
238 	DWM0;       DWM0;       DWM(Down);  DWM(Right);
239 	DWM(Down);  DWM(Right); DWM(Down);  DWM0;
240 	DWM(Down);  DWM(Left);  DWM(Down);  DWM(Left);
241 	DWM0;       DWM0;       DWM(Down);  DWM(Right);
242 	DWM(Down);  DWM(Right); DWM(Down);  DWM0;
243 	DWM(Down);  DWM(Left);  DWM(Down);  DWM(Left);
244 	DWM(Down);  DWM(Right); DWM(Down);  DWM0;
245 	DWM(Down);  DWM(Left);  DWM0;       DWM(Right);
246 	DWM(Down);  DWM0;       DWM0;       DWM(Left);
247 #undef DWM
248 #undef DWM0
249 }
250 
~EoBCoreEngine()251 EoBCoreEngine::~EoBCoreEngine() {
252 	releaseItemsAndDecorationsShapes();
253 	releaseTempData();
254 
255 	if (_faceShapes) {
256 		for (int i = 0; i < 44; i++) {
257 			if (_characters) {
258 				for (int ii = 0; ii < 6; ii++) {
259 					if (_characters[ii].faceShape == _faceShapes[i])
260 						_characters[ii].faceShape = 0;
261 				}
262 			}
263 			delete[] _faceShapes[i];
264 			_faceShapes[i] = 0;
265 		}
266 		delete[] _faceShapes;
267 	}
268 
269 	if (_characters) {
270 		for (int i = 0; i < 6; i++)
271 			delete[] _characters[i].faceShape;
272 	}
273 
274 	delete[] _characters;
275 	delete[] _items;
276 	delete[] _itemTypes;
277 	if (_itemNames) {
278 		for (int i = 0; i < 130; i++)
279 			delete[] _itemNames[i];
280 	}
281 	delete[] _itemNames;
282 	delete[] _flyingObjects;
283 
284 	delete[] _monsterFlashOverlay;
285 	delete[] _monsterStoneOverlay;
286 	delete[] _monsters;
287 
288 	if (_monsterDecorations) {
289 		releaseMonsterShapes(0, 36);
290 		delete[] _monsterShapes;
291 		delete[] _monsterDecorations;
292 
293 		for (int i = 0; i < 24; i++)
294 			delete[] _monsterPalettes[i];
295 		delete[] _monsterPalettes;
296 	}
297 
298 	delete[] _monsterProps;
299 
300 	if (_doorSwitches) {
301 		releaseDoorShapes();
302 		delete[] _doorSwitches;
303 	}
304 
305 	releaseDecorations();
306 	delete[] _levelDecorationRects;
307 	_dscWallMapping.clear();
308 
309 	delete[] _greenFadingTable;
310 	delete[] _blueFadingTable;
311 	delete[] _lightBlueFadingTable;
312 	delete[] _blackFadingTable;
313 	delete[] _greyFadingTable;
314 
315 	delete[] _spells;
316 	delete[] _spellAnimBuffer;
317 	delete[] _wallsOfForce;
318 	delete[] _buttonDefs;
319 
320 	delete _gui;
321 	_gui = 0;
322 	delete _screen;
323 	_screen = 0;
324 
325 	delete[] _menuDefs;
326 	_menuDefs = 0;
327 
328 	delete[] _amigaSoundMap;
329 	_amigaSoundMap = 0;
330 
331 	delete _inf;
332 	_inf = 0;
333 	delete _timer;
334 	_timer = 0;
335 	delete _debugger;
336 	_debugger = 0;
337 	delete _txt;
338 	_txt = 0;
339 }
340 
initKeymap()341 void EoBCoreEngine::initKeymap() {
342 #ifdef ENABLE_KEYMAPPER
343 	Common::Keymapper *const mapper = _eventMan->getKeymapper();
344 
345 	// Do not try to recreate same keymap over again
346 	if (mapper->getKeymap(kKeymapName) != 0)
347 		return;
348 
349 	Common::Keymap *const engineKeyMap = new Common::Keymap(kKeymapName);
350 
351 	const Common::KeyActionEntry keyActionEntries[] = {
352 		{ Common::KeyState(Common::KEYCODE_UP), "MVF", _("Move Forward") },
353 		{ Common::KeyState(Common::KEYCODE_DOWN), "MVB", _("Move Back") },
354 		{ Common::KeyState(Common::KEYCODE_LEFT), "MVL", _("Move Left") },
355 		{ Common::KeyState(Common::KEYCODE_RIGHT), "MVR", _("Move Right") },
356 		{ Common::KeyState(Common::KEYCODE_HOME), "TL", _("Turn Left") },
357 		{ Common::KeyState(Common::KEYCODE_PAGEUP), "TR", _("Turn Right") },
358 		{ Common::KeyState(Common::KEYCODE_i), "INV", _("Open/Close Inventory") },
359 		{ Common::KeyState(Common::KEYCODE_p), "SCE", _("Switch Inventory/Character screen") },
360 		{ Common::KeyState(Common::KEYCODE_c), "CMP", _("Camp") },
361 		{ Common::KeyState(Common::KEYCODE_SPACE), "CSP", _("Cast Spell") },
362 		// TODO: Spell cursor, but this needs more thought, since different
363 		// game versions use different keycodes.
364 		{ Common::KeyState(Common::KEYCODE_1), "SL1", _("Spell Level 1") },
365 		{ Common::KeyState(Common::KEYCODE_2), "SL2", _("Spell Level 2") },
366 		{ Common::KeyState(Common::KEYCODE_3), "SL3", _("Spell Level 3") },
367 		{ Common::KeyState(Common::KEYCODE_4), "SL4", _("Spell Level 4") },
368 		{ Common::KeyState(Common::KEYCODE_5), "SL5", _("Spell Level 5") }
369 	};
370 
371 	for (uint i = 0; i < ARRAYSIZE(keyActionEntries); ++i) {
372 		Common::Action *const act = new Common::Action(engineKeyMap, keyActionEntries[i].id, keyActionEntries[i].description);
373 		act->addKeyEvent(keyActionEntries[i].ks);
374 	}
375 
376 	if (_flags.gameID == GI_EOB2) {
377 		Common::Action *const act = new Common::Action(engineKeyMap, "SL6", _("Spell Level 6"));
378 		act->addKeyEvent(Common::KeyState(Common::KEYCODE_6));
379 	}
380 
381 	mapper->addGameKeymap(engineKeyMap);
382 #endif
383 }
384 
init()385 Common::Error EoBCoreEngine::init() {
386 	// In EOB the timer proc is directly invoked via interrupt 0x1C, 18.2 times per second.
387 	// This makes a tick length of 54.94.
388 	_tickLength = 55;
389 
390 	if (ConfMan.hasKey("render_mode"))
391 		_configRenderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
392 
393 	_enableHiResDithering = (_configRenderMode == Common::kRenderEGA && _flags.useHiRes);
394 
395 	_screen = new Screen_EoB(this, _system);
396 	assert(_screen);
397 	_screen->setResolution();
398 
399 	_res = new Resource(this);
400 	assert(_res);
401 	_res->reset();
402 
403 	_staticres = new StaticResource(this);
404 	assert(_staticres);
405 	if (!_staticres->init())
406 		error("_staticres->init() failed");
407 
408 	// SoundTowns_Darkmoon requires initialized _staticres
409 	if (_flags.platform == Common::kPlatformDOS) {
410 		//MidiDriverType midiDriver = MidiDriver::detectDevice(MDT_PCSPK | MDT_ADLIB);
411 		_sound = new SoundAdLibPC(this, _mixer);
412 	} else if (_flags.platform == Common::kPlatformFMTowns) {
413 		_sound = new SoundTowns_Darkmoon(this, _mixer);
414 	} else if (_flags.platform == Common::kPlatformPC98) {
415 
416 	} else if (_flags.platform == Common::kPlatformAmiga) {
417 		_sound = new SoundAmiga_EoB(this, _mixer);
418 	}
419 
420 	assert(_sound);
421 	_sound->init();
422 
423 	// Setup volume settings (and read in all ConfigManager settings)
424 	syncSoundSettings();
425 
426 	if (!_screen->init())
427 		error("screen()->init() failed");
428 
429 	if (ConfMan.hasKey("save_slot")) {
430 		_gameToLoad = ConfMan.getInt("save_slot");
431 		if (!saveFileLoadable(_gameToLoad))
432 			_gameToLoad = -1;
433 	}
434 
435 	setupKeyMap();
436 
437 	_gui = new GUI_EoB(this);
438 	assert(_gui);
439 	_txt = new TextDisplayer_rpg(this, _screen);
440 	assert(_txt);
441 	_inf = new EoBInfProcessor(this, _screen);
442 	assert(_inf);
443 	_debugger = new Debugger_EoB(this);
444 	assert(_debugger);
445 
446 	if (_flags.platform == Common::kPlatformAmiga) {
447 		if (_res->exists("EOBF6.FONT"))
448 			_screen->loadFont(Screen::FID_6_FNT, "EOBF6.FONT");
449 		else if (_res->exists("FONTS/EOBF6.FONT"))
450 			_screen->loadFont(Screen::FID_6_FNT, "FONTS/EOBF6.FONT");
451 		else
452 			AmigaDOSFont::errorDialog(0);
453 
454 		if (_res->exists("EOBF8.FONT"))
455 			_screen->loadFont(Screen::FID_8_FNT, "EOBF8.FONT");
456 		else if (_res->exists("FONTS/EOBF8.FONT"))
457 			_screen->loadFont(Screen::FID_8_FNT, "FONTS/EOBF8.FONT");
458 		else
459 			AmigaDOSFont::errorDialog(0);
460 
461 	} else {
462 		_screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT");
463 		_screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT");
464 	}
465 
466 	Common::Error err = KyraRpgEngine::init();
467 	if (err.getCode() != Common::kNoError)
468 		return err;
469 
470 	initButtonData();
471 	initMenus();
472 	initStaticResource();
473 	initSpells();
474 
475 	_timer = new TimerManager(this, _system);
476 	assert(_timer);
477 	setupTimers();
478 
479 	_wllVmpMap[1] = 1;
480 	_wllVmpMap[2] = 2;
481 	memset(&_wllVmpMap[3], 3, 20);
482 	_wllVmpMap[23] = 4;
483 	_wllVmpMap[24] = 5;
484 
485 	memcpy(_wllWallFlags, _wllFlagPreset, _wllFlagPresetSize);
486 
487 	memset(&_specialWallTypes[3], 1, 5);
488 	memset(&_specialWallTypes[13], 1, 5);
489 	_specialWallTypes[8] = _specialWallTypes[18] = 6;
490 
491 	memset(&_wllShapeMap[3], -1, 5);
492 	memset(&_wllShapeMap[13], -1, 5);
493 
494 	_wllVcnOffset = (_flags.platform == Common::kPlatformFMTowns) ? 0 : 16;
495 	int bpp = (_flags.platform == Common::kPlatformFMTowns) ? 2 : 1;
496 
497 	_greenFadingTable = new uint8[256 * bpp];
498 	_blueFadingTable = new uint8[256 * bpp];
499 	_lightBlueFadingTable = new uint8[256 * bpp];
500 	_blackFadingTable = new uint8[256 * bpp];
501 	_greyFadingTable = new uint8[256 * bpp];
502 
503 	_monsters = new EoBMonsterInPlay[30];
504 	memset(_monsters, 0, 30 * sizeof(EoBMonsterInPlay));
505 
506 	_characters = new EoBCharacter[6];
507 	memset(_characters, 0, sizeof(EoBCharacter) * 6);
508 
509 	_items = new EoBItem[600];
510 	memset(_items, 0, sizeof(EoBItem) * 600);
511 
512 	_itemNames = new char*[130];
513 	for (int i = 0; i < 130; i++) {
514 		_itemNames[i] = new char[35];
515 		memset(_itemNames[i], 0, 35);
516 	}
517 
518 	_flyingObjects = new EoBFlyingObject[_numFlyingObjects];
519 	_flyingObjectsPtr = _flyingObjects;
520 	memset(_flyingObjects, 0, _numFlyingObjects * sizeof(EoBFlyingObject));
521 
522 	int bufferSize = _flags.useHiColorMode ? 8192 : 4096;
523 	_spellAnimBuffer = new uint8[bufferSize];
524 	memset(_spellAnimBuffer, 0, bufferSize);
525 
526 	_wallsOfForce = new WallOfForce[5];
527 	memset(_wallsOfForce, 0, 5 * sizeof(WallOfForce));
528 
529 	memset(_doorType, 0, sizeof(_doorType));
530 	memset(_noDoorSwitch, 0, sizeof(_noDoorSwitch));
531 
532 	_monsterShapes = new uint8*[36];
533 	memset(_monsterShapes, 0, 36 * sizeof(uint8 *));
534 	_monsterDecorations = new SpriteDecoration[36];
535 	memset(_monsterDecorations, 0, 36 * sizeof(SpriteDecoration));
536 	_monsterPalettes = new uint8*[24];
537 	for (int i = 0; i < 24; i++)
538 		_monsterPalettes[i] = new uint8[16];
539 
540 	_doorSwitches = new SpriteDecoration[6];
541 	memset(_doorSwitches, 0, 6 * sizeof(SpriteDecoration));
542 
543 	_monsterFlashOverlay = new uint8[16];
544 	_monsterStoneOverlay = new uint8[16];
545 	memset(_monsterFlashOverlay, (_configRenderMode == Common::kRenderCGA) ? 0xFF : guiSettings()->colors.guiColorWhite, 16 * sizeof(uint8));
546 	memset(_monsterStoneOverlay, (_flags.platform == Common::kPlatformAmiga) ? guiSettings()->colors.guiColorWhite : 0x0D, 16 * sizeof(uint8));
547 	_monsterFlashOverlay[0] = _monsterStoneOverlay[0] = 0;
548 
549 	// Prevent autosave on game startup
550 	_lastAutosave = _system->getMillis();
551 
552 #ifdef ENABLE_KEYMAPPER
553 	_eventMan->getKeymapper()->pushKeymap(kKeymapName, true);
554 #endif
555 
556 	return Common::kNoError;
557 }
558 
go()559 Common::Error EoBCoreEngine::go() {
560 	_debugger->initialize();
561 	_txt->removePageBreakFlag();
562 	_screen->setFont(Screen::FID_8_FNT);
563 	loadItemsAndDecorationsShapes();
564 	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
565 
566 	// Import original save game files (especially the "Quick Start Party")
567 	if (ConfMan.getBool("importOrigSaves")) {
568 		importOriginalSaveFile(-1);
569 		ConfMan.setBool("importOrigSaves", false);
570 		ConfMan.flushToDisk();
571 	}
572 
573 	loadItemDefs();
574 	int action = 0;
575 
576 	for (bool repeatLoop = true; repeatLoop; repeatLoop ^= true) {
577 		action = 0;
578 
579 		if (_gameToLoad != -1) {
580 			startupLoad();
581 			if (loadGameState(_gameToLoad).getCode() != Common::kNoError)
582 				error("Couldn't load game slot %d on startup", _gameToLoad);
583 			_gameToLoad = -1;
584 		} else {
585 			_screen->showMouse();
586 			action = mainMenu();
587 		}
588 
589 		if (action == -1) {
590 			// load game
591 			startupLoad();
592 			repeatLoop = _gui->runLoadMenu(72, 14, true);
593 
594 		} else if (action == -2) {
595 			// new game
596 			repeatLoop = startCharacterGeneration();
597 			if (repeatLoop && !shouldQuit())
598 				startupNew();
599 		} else if (action == -3) {
600 			// transfer party
601 			repeatLoop = startPartyTransfer();
602 			if (repeatLoop && !shouldQuit())
603 				startupNew();
604 		}
605 	}
606 
607 	if (!shouldQuit() && action >= -3) {
608 		runLoop();
609 
610 		if (_playFinale) {
611 			// make final save for party transfer
612 			saveGameStateIntern(-1, 0, 0);
613 			_sound->selectAudioResourceSet(kMusicFinale);
614 			seq_playFinale();
615 		}
616 	}
617 
618 	return Common::kNoError;
619 }
620 
registerDefaultSettings()621 void EoBCoreEngine::registerDefaultSettings() {
622 	KyraEngine_v1::registerDefaultSettings();
623 	ConfMan.registerDefault("hpbargraphs", true);
624 	ConfMan.registerDefault("mousebtswap", false);
625 	ConfMan.registerDefault("importOrigSaves", true);
626 }
627 
readSettings()628 void EoBCoreEngine::readSettings() {
629 	_configHpBarGraphs = ConfMan.getBool("hpbargraphs");
630 	_configMouseBtSwap = ConfMan.getBool("mousebtswap");
631 	_configSounds = ConfMan.getBool("sfx_mute") ? 0 : 1;
632 	_configMusic = _configSounds ? 1 : 0;
633 
634 	if (_sound) {
635 		_sound->enableMusic(_configSounds ? 1 : 0);
636 		_sound->enableSFX(_configSounds);
637 	}
638 }
639 
writeSettings()640 void EoBCoreEngine::writeSettings() {
641 	ConfMan.setBool("hpbargraphs", _configHpBarGraphs);
642 	ConfMan.setBool("mousebtswap", _configMouseBtSwap);
643 	ConfMan.setBool("sfx_mute", _configSounds == 0);
644 
645 	if (_sound) {
646 		if (!_configSounds)
647 			_sound->haltTrack();
648 		_sound->enableMusic(_configSounds ? 1 : 0);
649 		_sound->enableSFX(_configSounds);
650 	}
651 
652 	ConfMan.flushToDisk();
653 }
654 
startupNew()655 void EoBCoreEngine::startupNew() {
656 	gui_setPlayFieldButtons();
657 	_screen->_curPage = 0;
658 	gui_drawPlayField(false);
659 	_screen->_curPage = 0;
660 	gui_drawAllCharPortraitsWithStats();
661 	drawScene(1);
662 	_updateFlags = 0;
663 	_updateCharNum = 0;
664 }
665 
runLoop()666 void EoBCoreEngine::runLoop() {
667 	_envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength);
668 	_flashShapeTimer = 0;
669 	_drawSceneTimer = _system->getMillis();
670 
671 	_screen->setFont(Screen::FID_6_FNT);
672 	_screen->setScreenDim(7);
673 
674 	_runFlag = true;
675 
676 	while (!shouldQuit() && _runFlag) {
677 		checkPartyStatus(true);
678 		checkInput(_activeButtons, true, 0);
679 		removeInputTop();
680 
681 		if (!_runFlag)
682 			break;
683 
684 		_timer->update();
685 		updateScriptTimers();
686 		updateWallOfForceTimers();
687 
688 		if (_sceneUpdateRequired)
689 			drawScene(1);
690 
691 		if (_envAudioTimer < _system->getMillis() && !(_flags.gameID == GI_EOB1 && (_flags.platform == Common::kPlatformAmiga || _currentLevel == 0 || _currentLevel > 3))) {
692 			_envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength);
693 			snd_processEnvironmentalSoundEffect(_flags.gameID == GI_EOB1 ? 30 : (rollDice(1, 2, -1) ? 27 : 28), _currentBlock + rollDice(1, 12, -1));
694 		}
695 
696 		updateEnvironmentalSfx(0);
697 		turnUndeadAuto();
698 	}
699 }
700 
checkPartyStatus(bool handleDeath)701 bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
702 	int numChars = 0;
703 	for (int i = 0; i < 6; i++)
704 		numChars += testCharacter(i, 13);
705 
706 	if (numChars)
707 		return false;
708 
709 	if (!handleDeath)
710 		return true;
711 
712 	gui_drawAllCharPortraitsWithStats();
713 
714 	if (checkPartyStatusExtra()) {
715 		_screen->setFont(Screen::FID_8_FNT);
716 		gui_updateControls();
717 		if (_gui->runLoadMenu(0, 0)) {
718 			_screen->setFont(Screen::FID_6_FNT);
719 			return true;
720 		}
721 	}
722 
723 	quitGame();
724 	return false;
725 }
726 
loadItemsAndDecorationsShapes()727 void EoBCoreEngine::loadItemsAndDecorationsShapes() {
728 	releaseItemsAndDecorationsShapes();
729 	int div = (_flags.gameID == GI_EOB1) ? 3 : 8;
730 	int mul = (_flags.gameID == GI_EOB1) ? 64 : 24;
731 	int size = 0;
732 
733 	_largeItemShapes = new const uint8*[_numLargeItemShapes];
734 	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
735 		for (int i = 0; i < _numLargeItemShapes; i++)
736 			_largeItemShapes[i] = _staticres->loadRawData(kEoB2LargeItemsShapeData00 + i, size);
737 	} else {
738 		_screen->loadShapeSetBitmap("ITEML1", 5, 3);
739 		for (int i = 0; i < _numLargeItemShapes; i++)
740 			_largeItemShapes[i] = _screen->encodeShape((i / div) << 3, (i % div) * mul, 8, 24, false, _cgaMappingItemsL);
741 
742 		if (_flags.gameID == GI_EOB1) {
743 			for (int c = 0; c < 3; ++c) {
744 				_largeItemShapesScl[c] = new const uint8*[_numLargeItemShapes];
745 				for (int i = 0; i < _numLargeItemShapes; i++)
746 					_largeItemShapesScl[c][i] = _screen->encodeShape((i / div) << 3, (i % div) * mul + 24 + (c << 4), 6 - 2 * c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsL);
747 			}
748 		}
749 	}
750 
751 	_smallItemShapes = new const uint8*[_numSmallItemShapes];
752 	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
753 		for (int i = 0; i < _numSmallItemShapes; i++)
754 			_smallItemShapes[i] = _staticres->loadRawData(kEoB2SmallItemsShapeData00 + i, size);
755 	} else {
756 		_screen->loadShapeSetBitmap("ITEMS1", 5, 3);
757 		for (int i = 0; i < _numSmallItemShapes; i++)
758 			_smallItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingItemsS);
759 
760 		if (_flags.gameID == GI_EOB1) {
761 			for (int c = 0; c < 3; ++c) {
762 				_smallItemShapesScl[c] = new const uint8*[_numSmallItemShapes];
763 				for (int i = 0; i < _numSmallItemShapes; i++)
764 					_smallItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsS);
765 			}
766 		}
767 	}
768 
769 	_thrownItemShapes = new const uint8*[_numThrownItemShapes];
770 	if (_flags.gameID == GI_EOB2)
771 		_spellShapes = new const uint8*[4];
772 	_firebeamShapes = new const uint8*[3];
773 
774 	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
775 		for (int i = 0; i < _numThrownItemShapes; i++)
776 			_thrownItemShapes[i] = _staticres->loadRawData(kEoB2ThrownShapeData00 + i, size);
777 		for (int i = 0; i < 4; i++)
778 			_spellShapes[i] = _staticres->loadRawData(kEoB2SpellShapeData00 + i, size);
779 		for (int i = 0; i < 3; i++)
780 			_firebeamShapes[i] = _staticres->loadRawData(kEoB2FirebeamShapeData00 + i, size);
781 		_redSplatShape = _staticres->loadRawData(kEoB2RedSplatShapeData, size);
782 		_greenSplatShape = _staticres->loadRawData(kEoB2GreenSplatShapeData, size);
783 	} else {
784 		_screen->loadShapeSetBitmap("THROWN", 5, 3);
785 		for (int i = 0; i < _numThrownItemShapes; i++)
786 			_thrownItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingThrown);
787 
788 		if (_flags.gameID == GI_EOB1) {
789 			for (int c = 0; c < 3; ++c) {
790 				_thrownItemShapesScl[c] = new const uint8*[_numThrownItemShapes];
791 				for (int i = 0; i < _numThrownItemShapes; i++)
792 					_thrownItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingThrown);
793 			}
794 		} else {
795 			for (int i = 0; i < 4; i++)
796 				_spellShapes[i] = _screen->encodeShape(8, i << 5, 6, 32, false, _cgaMappingThrown);
797 		}
798 
799 		_firebeamShapes[0] = _screen->encodeShape(16, 0, 4, 24, false, _cgaMappingThrown);
800 		_firebeamShapes[1] = _screen->encodeShape(16, 24, 4, 24, false, _cgaMappingThrown);
801 		_firebeamShapes[2] = _screen->encodeShape(16, 48, 3, 24, false, _cgaMappingThrown);
802 		_redSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 144 : 72, 5, 24, false, _cgaMappingThrown);
803 		_greenSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 168 : 96, 5, 16, false, _cgaMappingThrown);
804 	}
805 
806 	_itemIconShapes = new const uint8*[_numItemIconShapes];
807 	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
808 		for (int i = 0; i < _numItemIconShapes; i++)
809 			_itemIconShapes[i] = _staticres->loadRawData(kEoB2ItemIconShapeData00 + i, size);
810 	} else {
811 		_screen->loadShapeSetBitmap("ITEMICN", 5, 3);
812 		for (int i = 0; i < _numItemIconShapes; i++)
813 			_itemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10, false, _cgaMappingIcons);
814 
815 		if (_flags.platform == Common::kPlatformAmiga) {
816 			const uint8 offsY = (_flags.gameID == GI_EOB1) ? 80 : 96;
817 			_amigaBlueItemIconShapes = new const uint8*[_numItemIconShapes];
818 			for (int i = 0; i < _numItemIconShapes; i++) {
819 				int bx = (i % 0x14) << 1;
820 				int by = (i / 0x14) << 4;
821 				_amigaBlueItemIconShapes[i] = _screen->getPagePixel(2, (bx << 3) + 8, by + offsY + 8) ? _screen->encodeShape(bx, by + offsY, 2, 0x10, false, 0) : _screen->encodeShape(bx, by, 2, 0x10, false, 0);
822 			}
823 		}
824 	}
825 
826 	_teleporterShapes = new const uint8*[6];
827 	_sparkShapes = new const uint8*[3];
828 	_compassShapes = new const uint8*[12];
829 	if (_flags.gameID == GI_EOB2)
830 		_wallOfForceShapes = new const uint8*[6];
831 
832 	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
833 		_lightningColumnShape = _staticres->loadRawData(kEoB2LightningColumnShapeData, size);
834 		for (int i = 0; i < 6; i++)
835 			_wallOfForceShapes[i] = _staticres->loadRawData(kEoB2WallOfForceShapeData00 + i, size);
836 		for (int i = 0; i < 6; i++)
837 			_teleporterShapes[i] = _staticres->loadRawData(kEoB2TeleporterShapeData00 + i, size);
838 		for (int i = 0; i < 3; i++)
839 			_sparkShapes[i] = _staticres->loadRawData(kEoB2SparkShapeData00 + i, size);
840 		for (int i = 0; i < 12; i++)
841 			_compassShapes[i] = _staticres->loadRawData(kEoB2CompassShapeData00 + i, size);
842 
843 		_deadCharShape = _staticres->loadRawData(kEoB2DeadCharShapeData, size);
844 		_disabledCharGrid = _staticres->loadRawData(kEoB2DisabledCharGridShapeData, size);
845 		_blackBoxSmallGrid = _staticres->loadRawData(kEoB2SmallGridShapeData, size);
846 		_weaponSlotGrid = _staticres->loadRawData(kEoB2WeaponSlotGridShapeData, size);
847 		_blackBoxWideGrid = _staticres->loadRawData(kEoB2WideGridShapeData, size);
848 
849 	} else {
850 		_screen->loadShapeSetBitmap("DECORATE", 5, 3);
851 		if (_flags.gameID == GI_EOB2) {
852 			_lightningColumnShape = _screen->encodeShape(18, 88, 4, 64);
853 			for (int i = 0; i < 6; i++)
854 				_wallOfForceShapes[i] = _screen->encodeShape(_wallOfForceShapeDefs[(i << 2)], _wallOfForceShapeDefs[(i << 2) + 1], _wallOfForceShapeDefs[(i << 2) + 2], _wallOfForceShapeDefs[(i << 2) + 3]);
855 		}
856 
857 		for (int i = 0; i < 6; i++)
858 			_teleporterShapes[i] = _screen->encodeShape(_teleporterShapeDefs[(i << 2)], _teleporterShapeDefs[(i << 2) + 1], _teleporterShapeDefs[(i << 2) + 2], _teleporterShapeDefs[(i << 2) + 3], false, _cgaMappingDefault);
859 
860 		_sparkShapes[0] = _screen->encodeShape(29, 0, 2, 16, false, _cgaMappingDeco);
861 		_sparkShapes[1] = _screen->encodeShape(31, 0, 2, 16, false, _cgaMappingDeco);
862 		_sparkShapes[2] = _screen->encodeShape(33, 0, 2, 16, false, _cgaMappingDeco);
863 		_deadCharShape = _screen->encodeShape(0, 88, 4, 32, false, _cgaMappingDeco);
864 		_disabledCharGrid = _screen->encodeShape(4, 88, 4, 32, false, _cgaMappingDeco);
865 		_blackBoxSmallGrid = _screen->encodeShape(9, 88, 2, 8, false, _cgaMappingDeco);
866 		_weaponSlotGrid = _screen->encodeShape(8, 88, 4, 16, false, _cgaMappingDeco);
867 		_blackBoxWideGrid = _screen->encodeShape(8, 104, 4, 8, false, _cgaMappingDeco);
868 
869 		static const uint8 dHeight[] = { 17, 10, 10 };
870 		static const uint8 dY[] = { 120, 137, 147 };
871 
872 		for (int y = 0; y < 3; y++) {
873 			for (int x = 0; x < 4; x++)
874 				_compassShapes[(y << 2) + x] = _screen->encodeShape(x * 3, dY[y], 3, dHeight[y], false, _cgaMappingDeco);
875 		}
876 	}
877 }
878 
releaseItemsAndDecorationsShapes()879 void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
880 	if (_flags.platform != Common::kPlatformFMTowns || _flags.gameID != GI_EOB2) {
881 		if (_largeItemShapes) {
882 			for (int i = 0; i < _numLargeItemShapes; i++) {
883 				if (_largeItemShapes[i])
884 					delete[] _largeItemShapes[i];
885 			}
886 		}
887 
888 		if (_smallItemShapes) {
889 			for (int i = 0; i < _numSmallItemShapes; i++) {
890 				if (_smallItemShapes[i])
891 					delete[] _smallItemShapes[i];
892 			}
893 		}
894 
895 		if (_thrownItemShapes) {
896 			for (int i = 0; i < _numThrownItemShapes; i++) {
897 				if (_thrownItemShapes[i])
898 					delete[] _thrownItemShapes[i];
899 			}
900 		}
901 
902 		if (_spellShapes) {
903 			for (int i = 0; i < 4; i++) {
904 				if (_spellShapes[i])
905 					delete[] _spellShapes[i];
906 			}
907 		}
908 
909 		if (_itemIconShapes) {
910 			for (int i = 0; i < _numItemIconShapes; i++) {
911 				if (_itemIconShapes[i])
912 					delete[] _itemIconShapes[i];
913 			}
914 		}
915 
916 		if (_amigaBlueItemIconShapes) {
917 			for (int i = 0; i < _numItemIconShapes; i++) {
918 				if (_amigaBlueItemIconShapes[i])
919 					delete[] _amigaBlueItemIconShapes[i];
920 			}
921 		}
922 
923 		if (_sparkShapes) {
924 			for (int i = 0; i < 3; i++) {
925 				if (_sparkShapes[i])
926 					delete[] _sparkShapes[i];
927 			}
928 		}
929 
930 		if (_wallOfForceShapes) {
931 			for (int i = 0; i < 6; i++) {
932 				if (_wallOfForceShapes[i])
933 					delete[] _wallOfForceShapes[i];
934 			}
935 		}
936 
937 		if (_teleporterShapes) {
938 			for (int i = 0; i < 6; i++) {
939 				if (_teleporterShapes[i])
940 					delete[] _teleporterShapes[i];
941 			}
942 		}
943 
944 		if (_compassShapes) {
945 			for (int i = 0; i < 12; i++) {
946 				if (_compassShapes[i])
947 					delete[] _compassShapes[i];
948 			}
949 		}
950 
951 		if (_firebeamShapes) {
952 			for (int i = 0; i < 3; i++) {
953 				if (_firebeamShapes[i])
954 					delete[] _firebeamShapes[i];
955 			}
956 		}
957 
958 		delete[] _redSplatShape;
959 		delete[] _greenSplatShape;
960 		delete[] _deadCharShape;
961 		delete[] _disabledCharGrid;
962 		delete[] _blackBoxSmallGrid;
963 		delete[] _weaponSlotGrid;
964 		delete[] _blackBoxWideGrid;
965 		delete[] _lightningColumnShape;
966 	}
967 
968 	delete[] _largeItemShapes;
969 	delete[] _smallItemShapes;
970 	delete[] _thrownItemShapes;
971 	delete[] _spellShapes;
972 	delete[] _itemIconShapes;
973 	delete[] _amigaBlueItemIconShapes;
974 	delete[] _sparkShapes;
975 	delete[] _wallOfForceShapes;
976 	delete[] _teleporterShapes;
977 	delete[] _compassShapes;
978 	delete[] _firebeamShapes;
979 
980 	for (int i = 0; i < 3; ++i) {
981 		if (_largeItemShapesScl[i]) {
982 			for (int ii = 0; ii < _numLargeItemShapes; ++ii) {
983 				if (_largeItemShapesScl[i][ii])
984 					delete[] _largeItemShapesScl[i][ii];
985 			}
986 		}
987 
988 		if (_smallItemShapesScl[i]) {
989 			for (int ii = 0; ii < _numSmallItemShapes; ++ii) {
990 				if (_smallItemShapesScl[i][ii])
991 					delete[] _smallItemShapesScl[i][ii];
992 			}
993 		}
994 
995 		if (_thrownItemShapesScl[i]) {
996 			for (int ii = 0; ii < _numThrownItemShapes; ++ii) {
997 				if (_thrownItemShapesScl[i][ii])
998 					delete[] _thrownItemShapesScl[i][ii];
999 			}
1000 		}
1001 
1002 		delete[] _largeItemShapesScl[i];
1003 		delete[] _smallItemShapesScl[i];
1004 		delete[] _thrownItemShapesScl[i];
1005 	}
1006 }
1007 
setHandItem(Item itemIndex)1008 void EoBCoreEngine::setHandItem(Item itemIndex) {
1009 	if (itemIndex == -1) {
1010 		if (_flags.platform == Common::kPlatformFMTowns)
1011 			_screen->setMouseCursor(8, 8, _itemIconShapes[37], 0);
1012 		return;
1013 	}
1014 
1015 	if (_screen->curDimIndex() == 7 && itemIndex) {
1016 		printFullItemName(itemIndex);
1017 		_txt->printMessage(_takenStrings[0]);
1018 	}
1019 
1020 	_itemInHand = itemIndex;
1021 	int icon = _items[_itemInHand].icon;
1022 	const uint8 *shp = _itemIconShapes[icon];
1023 	const uint8 *ovl = 0;
1024 
1025 	if (icon && (_items[_itemInHand].flags & 0x80) && (_partyEffectFlags & 2)) {
1026 		if (_amigaBlueItemIconShapes)
1027 			shp = _amigaBlueItemIconShapes[icon];
1028 		else
1029 			ovl = _flags.gameID == GI_EOB1 ? ((_configRenderMode == Common::kRenderCGA) ? _itemsOverlayCGA : &_itemsOverlay[icon << 4]) : _screen->generateShapeOverlay(shp, _lightBlueFadingTable);
1030 	}
1031 
1032 	int mouseOffs = itemIndex ? 8 : 0;
1033 	_screen->setMouseCursor(mouseOffs, mouseOffs, shp, ovl);
1034 
1035 	if (_flags.useHiColorMode) {
1036 		_screen->setFadeTable(_greyFadingTable);
1037 		_screen->setShapeFadingLevel(0);
1038 	}
1039 }
1040 
getDexterityArmorClassModifier(int dexterity)1041 int EoBCoreEngine::getDexterityArmorClassModifier(int dexterity) {
1042 	static const int8 mod[] = { 5, 5, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -2, -3, -4, -4, -5, -5, -5, -6, -6 };
1043 	return mod[dexterity];
1044 }
1045 
generateCharacterHitpointsByLevel(int charIndex,int levelIndex)1046 int EoBCoreEngine::generateCharacterHitpointsByLevel(int charIndex, int levelIndex) {
1047 	EoBCharacter *c = &_characters[charIndex];
1048 	int m = getClassAndConstHitpointsModifier(c->cClass, c->constitutionCur);
1049 
1050 	int h = 0;
1051 
1052 	for (int i = 0; i < 3; i++) {
1053 		if (!(levelIndex & (1 << i)))
1054 			continue;
1055 
1056 		int d = getCharacterClassType(c->cClass, i);
1057 
1058 		if (c->level[i] <= _hpIncrPerLevel[6 + i])
1059 			h += rollDice(1, (d >= 0) ? _hpIncrPerLevel[d] : 0);
1060 		else
1061 			h += _hpIncrPerLevel[12 + i];
1062 
1063 		h += m;
1064 	}
1065 
1066 	h /= _numLevelsPerClass[c->cClass];
1067 
1068 	if (h < 1)
1069 		h = 1;
1070 
1071 	return h;
1072 }
1073 
getClassAndConstHitpointsModifier(int cclass,int constitution)1074 int EoBCoreEngine::getClassAndConstHitpointsModifier(int cclass, int constitution) {
1075 	int res = _hpConstModifiers[constitution];
1076 	// This also applies to EOB1 despite being coded differently there
1077 	if (res <= 2 || (_classModifierFlags[cclass] & 0x31))
1078 		return res;
1079 
1080 	return 2;
1081 }
1082 
getCharacterClassType(int cclass,int levelIndex)1083 int EoBCoreEngine::getCharacterClassType(int cclass, int levelIndex) {
1084 	return _characterClassType[cclass * 3 + levelIndex];
1085 }
1086 
getModifiedHpLimits(int hpModifier,int constModifier,int level,bool mode)1087 int EoBCoreEngine::getModifiedHpLimits(int hpModifier, int constModifier, int level, bool mode) {
1088 	int s = _hpIncrPerLevel[6 + hpModifier] > level ? level : _hpIncrPerLevel[6 + hpModifier];
1089 	int res = s;
1090 
1091 	if (!mode)
1092 		res *= (hpModifier >= 0 ? _hpIncrPerLevel[hpModifier] : 0);
1093 
1094 	if (level > s) {
1095 		s = level - s;
1096 		res += (s * _hpIncrPerLevel[12 + hpModifier]);
1097 	}
1098 
1099 	if (!mode || (constModifier > 0))
1100 		res += (level * constModifier);
1101 
1102 	return res;
1103 }
1104 
getCharStrength(int str,int strExt)1105 Common::String EoBCoreEngine::getCharStrength(int str, int strExt) {
1106 	if (strExt) {
1107 		if (strExt == 100)
1108 			strExt = 0;
1109 		_strenghtStr = Common::String::format("%d/%02d", str, strExt);
1110 	} else {
1111 		_strenghtStr = Common::String::format("%d", str);
1112 	}
1113 
1114 	return _strenghtStr;
1115 }
1116 
testCharacter(int16 index,int flags)1117 int EoBCoreEngine::testCharacter(int16 index, int flags) {
1118 	if (index == -1)
1119 		return 0;
1120 
1121 	EoBCharacter *c = &_characters[index];
1122 	int res = 1;
1123 
1124 	if (flags & 1)
1125 		res &= (c->flags & 1);
1126 
1127 	if (flags & 2)
1128 		res &= ((c->hitPointsCur <= -10) || (c->flags & 8)) ? 0 : 1;
1129 
1130 	if (flags & 4)
1131 		res &= ((c->hitPointsCur <= 0) || (c->flags & 8)) ? 0 : 1;
1132 
1133 	if (flags & 8)
1134 		res &= (c->flags & 12) ? 0 : 1;
1135 
1136 	if (flags & 0x20)
1137 		res &= (c->flags & 4) ? 0 : 1;
1138 
1139 	if (flags & 0x10)
1140 		res &= (c->flags & 2) ? 0 : 1;
1141 
1142 	if (flags & 0x40)
1143 		res &= (c->food <= 0) ? 0 : 1;
1144 
1145 	return res;
1146 }
1147 
getNextValidCharIndex(int curCharIndex,int searchStep)1148 int EoBCoreEngine::getNextValidCharIndex(int curCharIndex, int searchStep) {
1149 	do {
1150 		curCharIndex += searchStep;
1151 		if (curCharIndex < 0)
1152 			curCharIndex = 5;
1153 		if (curCharIndex > 5)
1154 			curCharIndex = 0;
1155 	} while (!testCharacter(curCharIndex, 1));
1156 
1157 	return curCharIndex;
1158 }
1159 
recalcArmorClass(int index)1160 void EoBCoreEngine::recalcArmorClass(int index) {
1161 	EoBCharacter *c = &_characters[index];
1162 	int acm = getDexterityArmorClassModifier(c->dexterityCur);
1163 	c->armorClass = 10 + acm;
1164 
1165 	static uint8 slot[] = { 17, 0, 1, 18 };
1166 	for (int i = 0; i < 4; i++) {
1167 		int itm = c->inventory[slot[i]];
1168 		if (!itm)
1169 			continue;
1170 
1171 		if (i == 2) {
1172 			if (!validateWeaponSlotItem(index, 1))
1173 				continue;
1174 		}
1175 
1176 		int tp = _items[itm].type;
1177 
1178 		if (!(_itemTypes[tp].allowedClasses & _classModifierFlags[c->cClass]) || (_itemTypes[tp].extraProperties & 0x7F) || (i >= 1 && i <= 2 && tp != 27 && !(_flags.gameID == GI_EOB2 && tp == 57)))
1179 			continue;
1180 
1181 		c->armorClass += _itemTypes[tp].armorClass;
1182 		c->armorClass -= _items[itm].value;
1183 	}
1184 
1185 	if (!_items[c->inventory[17]].value) {
1186 		int8 m1 = 0;
1187 		int8 m2 = 0;
1188 
1189 		if (c->inventory[25]) {
1190 			if (!(_itemTypes[_items[c->inventory[25]].type].extraProperties & 0x7F))
1191 				m1 = _items[c->inventory[25]].value;
1192 		}
1193 
1194 		if (c->inventory[26]) {
1195 			if (!(_itemTypes[_items[c->inventory[26]].type].extraProperties & 0x7F))
1196 				m2 = _items[c->inventory[26]].value;
1197 		}
1198 
1199 		c->armorClass -= MAX(m1, m2);
1200 	}
1201 
1202 	if (c->effectsRemainder[0] > 0) {
1203 		if (c->armorClass <= (acm + 6))
1204 			c->effectsRemainder[0] = 0;
1205 		else
1206 			c->armorClass = (acm + 6);
1207 	}
1208 
1209 	// shield
1210 	if ((c->effectFlags & 8) && (c->armorClass > 4))
1211 		c->armorClass = 4;
1212 
1213 	// magical vestment
1214 	if (c->effectFlags & 0x4000) {
1215 		int8 m1 = 5;
1216 
1217 		if (getClericPaladinLevel(index) > 5)
1218 			m1 += ((getClericPaladinLevel(index) - 5) / 3);
1219 
1220 		if (c->armorClass > m1)
1221 			c->armorClass = m1;
1222 	}
1223 
1224 	if (c->armorClass < -10)
1225 		c->armorClass = -10;
1226 }
1227 
validateWeaponSlotItem(int index,int slot)1228 int EoBCoreEngine::validateWeaponSlotItem(int index, int slot) {
1229 	EoBCharacter *c = &_characters[index];
1230 	int itm1 = c->inventory[0];
1231 	int r = itemUsableByCharacter(index, itm1);
1232 	int tp1 = _items[itm1].type;
1233 
1234 	if (!slot)
1235 		return (!itm1 || r) ? 1 : 0;
1236 
1237 	int itm2 = c->inventory[1];
1238 	r = itemUsableByCharacter(index, itm2);
1239 	int tp2 = _items[itm2].type;
1240 
1241 	if (itm1 && _itemTypes[tp1].requiredHands == 2)
1242 		return 0;
1243 
1244 	if (!itm2)
1245 		return 1;
1246 
1247 	int f = (_itemTypes[tp2].extraProperties & 0x7F);
1248 	if (f <= 0 || f > 3)
1249 		return r;
1250 
1251 	if (_itemTypes[tp2].requiredHands)
1252 		return 0;
1253 
1254 	return r;
1255 }
1256 
getClericPaladinLevel(int index)1257 int EoBCoreEngine::getClericPaladinLevel(int index) {
1258 	if (_castScrollSlot)
1259 		return 9;
1260 
1261 	if (index == -1)
1262 		return (_currentLevel < 7) ? 5 : 9;
1263 
1264 	int l = getCharacterLevelIndex(2, _characters[index].cClass);
1265 	if (l > -1)
1266 		return _characters[index].level[l];
1267 
1268 	l = getCharacterLevelIndex(4, _characters[index].cClass);
1269 	if (l > -1) {
1270 		if (_characters[index].level[l] > 8)
1271 			return _characters[index].level[l] - 8;
1272 	}
1273 
1274 	return 1;
1275 }
1276 
getMageLevel(int index)1277 int EoBCoreEngine::getMageLevel(int index) {
1278 	if (_castScrollSlot)
1279 		return 9;
1280 
1281 	if (index == -1)
1282 		return (_currentLevel < 7) ? 5 : 9;
1283 
1284 	int l = getCharacterLevelIndex(1, _characters[index].cClass);
1285 	return (l > -1) ? _characters[index].level[l] : 1;
1286 }
1287 
getCharacterLevelIndex(int type,int cClass)1288 int EoBCoreEngine::getCharacterLevelIndex(int type, int cClass) {
1289 	if (getCharacterClassType(cClass, 0) == type)
1290 		return 0;
1291 
1292 	if (getCharacterClassType(cClass, 1) == type)
1293 		return 1;
1294 
1295 	if (getCharacterClassType(cClass, 2) == type)
1296 		return 2;
1297 
1298 	return -1;
1299 }
1300 
countCharactersWithSpecificItems(int16 itemType,int16 itemValue)1301 int EoBCoreEngine::countCharactersWithSpecificItems(int16 itemType, int16 itemValue) {
1302 	int res = 0;
1303 	for (int i = 0; i < 6; i++) {
1304 		if (!testCharacter(i, 1))
1305 			continue;
1306 		if (checkInventoryForItem(i, itemType, itemValue) != -1)
1307 			res++;
1308 	}
1309 	return res;
1310 }
1311 
checkInventoryForItem(int character,int16 itemType,int16 itemValue)1312 int EoBCoreEngine::checkInventoryForItem(int character, int16 itemType, int16 itemValue) {
1313 	if (character < 0)
1314 		return -1;
1315 
1316 	for (int i = 0; i < 27; i++) {
1317 		uint16 inv = _characters[character].inventory[i];
1318 		if (!inv)
1319 			continue;
1320 		if (_items[inv].type != itemType && itemType != -1)
1321 			continue;
1322 		if (_items[inv].value == itemValue || itemValue == -1)
1323 			return i;
1324 	}
1325 	return -1;
1326 }
1327 
modifyCharacterHitpoints(int character,int16 points)1328 void EoBCoreEngine::modifyCharacterHitpoints(int character, int16 points) {
1329 	if (!testCharacter(character, 3))
1330 		return;
1331 
1332 	EoBCharacter *c = &_characters[character];
1333 	c->hitPointsCur += points;
1334 	if (c->hitPointsCur > c->hitPointsMax)
1335 		c->hitPointsCur = c->hitPointsMax;
1336 
1337 	gui_drawHitpoints(character);
1338 	gui_drawCharPortraitWithStats(character);
1339 }
1340 
neutralizePoison(int character)1341 void EoBCoreEngine::neutralizePoison(int character) {
1342 	_characters[character].flags &= ~2;
1343 	_characters[character].effectFlags &= ~0x2000;
1344 	deleteCharEventTimer(character, -34);
1345 	gui_drawCharPortraitWithStats(character);
1346 }
1347 
npcSequence(int npcIndex)1348 void EoBCoreEngine::npcSequence(int npcIndex) {
1349 	_screen->loadShapeSetBitmap("OUTTAKE", 5, 3);
1350 	_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK);
1351 
1352 	drawNpcScene(npcIndex);
1353 
1354 	Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
1355 	if (s) {
1356 		_screen->loadFileDataToPage(s, 5, 32000);
1357 	} else {
1358 		s = _res->createReadStream("TEXT.CPS");
1359 		if (s->readSint32BE() + 12 == s->size())
1360 			_screen->loadSpecialAmigaCPS("TEXT.CPS", 5, false);
1361 		else
1362 			_screen->loadBitmap("TEXT.CPS", 5, 5, 0, true);
1363 	}
1364 	delete s;
1365 
1366 	gui_drawBox(0, 121, 320, 79, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
1367 	_txt->setupField(9, true);
1368 	_txt->resetPageBreakString();
1369 
1370 	runNpcDialogue(npcIndex);
1371 
1372 	_txt->removePageBreakFlag();
1373 	gui_restorePlayField();
1374 }
1375 
initNpc(int npcIndex)1376 void EoBCoreEngine::initNpc(int npcIndex) {
1377 	EoBCharacter *c = _characters;
1378 	int i = 0;
1379 	for (; i < 6; i++) {
1380 		if (!(_characters[i].flags & 1)) {
1381 			c = &_characters[i];
1382 			break;
1383 		}
1384 	}
1385 
1386 	delete[] c->faceShape;
1387 	memcpy(c, &_npcPreset[npcIndex], sizeof(EoBCharacter));
1388 	recalcArmorClass(i);
1389 
1390 	for (i = 0; i < 25; i++) {
1391 		if (!c->inventory[i])
1392 			continue;
1393 		c->inventory[i] = duplicateItem(c->inventory[i]);
1394 	}
1395 
1396 	_screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
1397 	_screen->_curPage = 2;
1398 	c->faceShape = _screen->encodeShape(npcIndex << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true, _cgaMappingDefault);
1399 	_screen->_curPage = 0;
1400 }
1401 
npcJoinDialogue(int npcIndex,int queryJoinTextId,int confirmJoinTextId,int noJoinTextId)1402 int EoBCoreEngine::npcJoinDialogue(int npcIndex, int queryJoinTextId, int confirmJoinTextId, int noJoinTextId) {
1403 	gui_drawDialogueBox();
1404 	_txt->printDialogueText(queryJoinTextId, 0);
1405 
1406 	int r = runDialogue(-1, 2, _yesNoStrings[0], _yesNoStrings[1]) - 1;
1407 	if (r == 0) {
1408 		if (confirmJoinTextId == -1) {
1409 			Common::String tmp = Common::String::format(_npcJoinStrings[0], _npcPreset[npcIndex].name);
1410 			_txt->printDialogueText(tmp.c_str(), true);
1411 		} else {
1412 			_txt->printDialogueText(confirmJoinTextId, _okStrings[0]);
1413 		}
1414 
1415 		if (prepareForNewPartyMember(33, npcIndex + 1))
1416 			initNpc(npcIndex);
1417 
1418 	} else if (r == 1) {
1419 		_txt->printDialogueText(noJoinTextId, _okStrings[0]);
1420 	}
1421 
1422 	return r ^ 1;
1423 }
1424 
prepareForNewPartyMember(int16 itemType,int16 itemValue)1425 int EoBCoreEngine::prepareForNewPartyMember(int16 itemType, int16 itemValue) {
1426 	int numChars = 0;
1427 	for (int i = 0; i < 6; i++)
1428 		numChars += (_characters[i].flags & 1);
1429 
1430 	if (numChars < 6) {
1431 		deletePartyItems(itemType, itemValue);
1432 	} else {
1433 		gui_drawDialogueBox();
1434 		_screen->set16bitShadingLevel(4);
1435 		_txt->printDialogueText(_npcMaxStrings[0]);
1436 		_screen->set16bitShadingLevel(0);
1437 		int r = runDialogue(-1, 7, _characters[0].name, _characters[1].name, _characters[2].name, _characters[3].name,
1438 		                    _characters[4].name, _characters[5].name, _abortStrings[0]) - 1;
1439 
1440 		if (r == 6)
1441 			return 0;
1442 
1443 		deletePartyItems(itemType, itemValue);
1444 		removeCharacterFromParty(r);
1445 	}
1446 
1447 	return 1;
1448 }
1449 
dropCharacter(int charIndex)1450 void EoBCoreEngine::dropCharacter(int charIndex) {
1451 	if (!testCharacter(charIndex, 1))
1452 		return;
1453 
1454 	removeCharacterFromParty(charIndex);
1455 
1456 	if (charIndex < 5)
1457 		exchangeCharacters(charIndex, testCharacter(5, 1) ? 5 : 4);
1458 
1459 	gui_processCharPortraitClick(0);
1460 	gui_setPlayFieldButtons();
1461 	setupCharacterTimers();
1462 }
1463 
removeCharacterFromParty(int charIndex)1464 void EoBCoreEngine::removeCharacterFromParty(int charIndex) {
1465 	EoBCharacter *c = &_characters[charIndex];
1466 	c->flags = 0;
1467 
1468 	for (int i = 0; i < 27; i++) {
1469 		if (i == 16 || !c->inventory[i])
1470 			continue;
1471 
1472 		setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3FF].drawObjects, _currentBlock, c->inventory[i], _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]);
1473 		c->inventory[i] = 0;
1474 	}
1475 
1476 	while (c->inventory[16])
1477 		setItemPosition((Item *)&_levelBlockProperties[_currentBlock & 0x3FF].drawObjects, _currentBlock, getQueuedItem(&c->inventory[16], 0, -1), _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]);
1478 
1479 	c->inventory[16] = 0;
1480 
1481 	if (_updateCharNum == charIndex)
1482 		_updateCharNum = 0;
1483 
1484 	setupCharacterTimers();
1485 }
1486 
exchangeCharacters(int charIndex1,int charIndex2)1487 void EoBCoreEngine::exchangeCharacters(int charIndex1, int charIndex2) {
1488 	EoBCharacter temp;
1489 	memcpy(&temp, &_characters[charIndex1], sizeof(EoBCharacter));
1490 	memcpy(&_characters[charIndex1], &_characters[charIndex2], sizeof(EoBCharacter));
1491 	memcpy(&_characters[charIndex2], &temp, sizeof(EoBCharacter));
1492 }
1493 
increasePartyExperience(int16 points)1494 void EoBCoreEngine::increasePartyExperience(int16 points) {
1495 	int cnt = 0;
1496 	for (int i = 0; i < 6; i++) {
1497 		if (testCharacter(i, 3))
1498 			cnt++;
1499 	}
1500 
1501 	if (cnt <= 0)
1502 		return;
1503 
1504 	points /= cnt;
1505 
1506 	for (int i = 0; i < 6; i++) {
1507 		if (!testCharacter(i, 3))
1508 			continue;
1509 		increaseCharacterExperience(i, points);
1510 	}
1511 }
1512 
increaseCharacterExperience(int charIndex,int32 points)1513 void EoBCoreEngine::increaseCharacterExperience(int charIndex, int32 points) {
1514 	int cl = _characters[charIndex].cClass;
1515 	points /= _numLevelsPerClass[cl];
1516 
1517 	for (int i = 0; i < 3; i++) {
1518 		if (getCharacterClassType(cl, i) == -1)
1519 			continue;
1520 		_characters[charIndex].experience[i] += points;
1521 
1522 		uint32 er = getRequiredExperience(cl, i, _characters[charIndex].level[i] + 1);
1523 		if (er == 0xFFFFFFFF)
1524 			continue;
1525 
1526 		if (_characters[charIndex].experience[i] >= er)
1527 			increaseCharacterLevel(charIndex, i);
1528 	}
1529 }
1530 
getRequiredExperience(int cClass,int levelIndex,int level)1531 uint32 EoBCoreEngine::getRequiredExperience(int cClass, int levelIndex, int level) {
1532 	cClass = getCharacterClassType(cClass, levelIndex);
1533 	if (cClass == -1)
1534 		return 0xFFFFFFFF;
1535 
1536 	const uint32 *tbl = _expRequirementTables[cClass];
1537 	return tbl[level - 1];
1538 }
1539 
increaseCharacterLevel(int charIndex,int levelIndex)1540 void EoBCoreEngine::increaseCharacterLevel(int charIndex, int levelIndex) {
1541 	_characters[charIndex].level[levelIndex]++;
1542 	int hpInc = generateCharacterHitpointsByLevel(charIndex, 1 << levelIndex);
1543 	_characters[charIndex].hitPointsCur += hpInc;
1544 	_characters[charIndex].hitPointsMax += hpInc;
1545 
1546 	gui_drawCharPortraitWithStats(charIndex);
1547 	_txt->printMessage(_levelGainStrings[0], -1, _characters[charIndex].name);
1548 	snd_playSoundEffect(23);
1549 }
1550 
setWeaponSlotStatus(int charIndex,int mode,int slot)1551 void EoBCoreEngine::setWeaponSlotStatus(int charIndex, int mode, int slot) {
1552 	if (mode == 0 || mode == 2)
1553 		_characters[charIndex].disabledSlots ^= (1 << slot);
1554 	else if (mode != 1)
1555 		return;
1556 
1557 	_characters[charIndex].slotStatus[slot] = 0;
1558 	gui_drawCharPortraitWithStats(charIndex);
1559 }
1560 
setupDialogueButtons(int presetfirst,int numStr,va_list & args)1561 void EoBCoreEngine::setupDialogueButtons(int presetfirst, int numStr, va_list &args) {
1562 	_dialogueNumButtons = numStr;
1563 	_dialogueHighlightedButton = 0;
1564 
1565 	Screen::FontId of = _screen->setFont((_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns) ? Screen::FID_8_FNT : _screen->_currentFont);
1566 
1567 	for (int i = 0; i < numStr; i++) {
1568 		const char *s = va_arg(args, const char *);
1569 		if (s)
1570 			_dialogueButtonString[i] = s;
1571 		else
1572 			_dialogueNumButtons = numStr = i;
1573 	}
1574 
1575 	static const uint16 prsX[] = { 59, 166, 4, 112, 220, 4, 112, 220, 4, 112, 220, 4, 112, 220 };
1576 	static const uint8 prsY[] = { 0, 0, 0, 0, 0, 12, 12, 12, 24, 24, 24, 36, 36, 36 };
1577 
1578 	const ScreenDim *dm = screen()->_curDim;
1579 	int yOffs = (_txt->lineCount() + 1) * _screen->getFontHeight() + dm->sy + 4;
1580 
1581 	_dialogueButtonPosX = &prsX[presetfirst];
1582 	_dialogueButtonPosY = &prsY[presetfirst];
1583 	_dialogueButtonYoffs = yOffs;
1584 
1585 	drawDialogueButtons();
1586 
1587 	_screen->setFont(of);
1588 
1589 	if (!shouldQuit())
1590 		removeInputTop();
1591 }
1592 
initDialogueSequence()1593 void EoBCoreEngine::initDialogueSequence() {
1594 	_npcSequenceSub = -1;
1595 	_txt->setWaitButtonMode(0);
1596 	_dialogueField = true;
1597 	_dialogueLastBitmap[0] = 0;
1598 
1599 	_txt->resetPageBreakString();
1600 	gui_updateControls();
1601 	//_allowSkip = true;
1602 
1603 	// WORKAROUND for bug in the original code (all platforms). Sequence sound would be terminated prematurely.
1604 	if (_flags.gameID == GI_EOB2 && _currentLevel == 2 && _currentBlock == 654)
1605 		_sound->stopAllSoundEffects();
1606 	else
1607 		snd_stopSound();
1608 
1609 	Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
1610 	if (s) {
1611 		_screen->loadFileDataToPage(s, 5, 32000);
1612 	} else {
1613 		s = _res->createReadStream("TEXT.CPS");
1614 		if (s->readSint32BE() + 12 == s->size())
1615 			_screen->loadSpecialAmigaCPS("TEXT.CPS", 5, false);
1616 		else
1617 			_screen->loadBitmap("TEXT.CPS", 5, 5, 0, true);
1618 	}
1619 	delete s;
1620 
1621 	_txt->setupField(9, 0);
1622 }
1623 
restoreAfterDialogueSequence()1624 void EoBCoreEngine::restoreAfterDialogueSequence() {
1625 	_txt->allowPageBreak(false);
1626 	_dialogueField = _dialogueFieldAmiga = false;
1627 
1628 	_dialogueLastBitmap[0] = 0;
1629 
1630 	gui_restorePlayField();
1631 	//_allowSkip = false;
1632 	_screen->setScreenDim(7);
1633 
1634 	if (_flags.gameID == GI_EOB2)
1635 		snd_playSoundEffect(2);
1636 
1637 	_sceneUpdateRequired = true;
1638 }
1639 
drawSequenceBitmap(const char * file,int destRect,int x1,int y1,int flags)1640 void EoBCoreEngine::drawSequenceBitmap(const char *file, int destRect, int x1, int y1, int flags) {
1641 	static const uint8 frameX[] = { 1, 0 };
1642 	static const uint8 frameY[] = { 8, 0 };
1643 	static const uint8 frameW[] = { 20, 40 };
1644 	static const uint8 frameH[] = { 96, 121 };
1645 
1646 	int page = ((flags & 2) || destRect) ? 0 : 6;
1647 	int amigaPalIndex = (x1 ? 1 : 0) + (y1 ? 2 : 0) + 1;
1648 
1649 	if (scumm_stricmp(_dialogueLastBitmap, file)) {
1650 		_screen->clearPage(2);
1651 		if (!destRect) {
1652 			if (!(flags & 1)) {
1653 				_screen->loadEoBBitmap("BORDER", 0, 3, 3, 2);
1654 				if (_flags.platform == Common::kPlatformAmiga)
1655 					_screen->copyRegion(0, 0, 0, 0, 320, 122, 2, 0, Screen::CR_NO_P_CHECK);
1656 				_screen->copyRegion(0, 0, 0, 0, 184, 121, 2, page, Screen::CR_NO_P_CHECK);
1657 			} else {
1658 				_screen->copyRegion(0, 0, 0, 0, 184, 121, 0, page, Screen::CR_NO_P_CHECK);
1659 			}
1660 
1661 			if (!page)
1662 				_screen->copyRegion(0, 0, 0, 0, 184, 121, 2, 6, Screen::CR_NO_P_CHECK);
1663 		}
1664 
1665 		_screen->loadEoBBitmap(file, 0, 3, 3, 2);
1666 		strcpy(_dialogueLastBitmap, file);
1667 	}
1668 
1669 	if (_flags.platform == Common::kPlatformAmiga) {
1670 		int cp = _screen->setCurPage(0);
1671 		if (!_dialogueFieldAmiga)
1672 			gui_drawDialogueBox();
1673 		_screen->drawClippedLine(0, 120, 319, 120, 9);
1674 		_screen->drawClippedLine(0, 121, 319, 121, guiSettings()->colors.fill);
1675 		_screen->setPagePixel(0, 319, 121, 9);
1676 		_screen->setCurPage(cp);
1677 		_screen->setupDualPalettesSplitScreen(_screen->getPalette(amigaPalIndex), _screen->getPalette(7));
1678 		_dialogueFieldAmiga = true;
1679 	}
1680 
1681 	if (flags & 2)
1682 		_screen->crossFadeRegion(x1 << 3, y1, frameX[destRect] << 3, frameY[destRect], frameW[destRect] << 3, frameH[destRect], 2, page);
1683 	else
1684 		_screen->copyRegion(x1 << 3, y1, frameX[destRect] << 3, frameY[destRect], frameW[destRect] << 3, frameH[destRect], 2, page, Screen::CR_NO_P_CHECK);
1685 
1686 	if (page == 6)
1687 		_screen->copyRegion(0, 0, 0, 0, 184, (_flags.platform == Common::kPlatformAmiga) ? 110 : 121, 6, 0, Screen::CR_NO_P_CHECK);
1688 
1689 	_screen->updateScreen();
1690 }
1691 
runDialogue(int dialogueTextId,int numStr,...)1692 int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, ...) {
1693 	if (dialogueTextId != -1)
1694 		txt()->printDialogueText(dialogueTextId, 0);
1695 
1696 	va_list args;
1697 	va_start(args, numStr);
1698 	if (numStr > 2)
1699 		setupDialogueButtons(2, numStr, args);
1700 	else
1701 		setupDialogueButtons(0, numStr, args);
1702 	va_end(args);
1703 
1704 	int res = 0;
1705 	while (res == 0 && !shouldQuit())
1706 		res = processDialogue();
1707 
1708 	gui_drawDialogueBox();
1709 
1710 	return res;
1711 }
1712 
restParty_displayWarning(const char * str)1713 void EoBCoreEngine::restParty_displayWarning(const char *str) {
1714 	int od = _screen->curDimIndex();
1715 	_screen->setScreenDim(7);
1716 	Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
1717 	_screen->setCurPage(0);
1718 
1719 	_txt->printMessage(Common::String::format("\r%s\r", str).c_str());
1720 
1721 	_screen->setFont(of);
1722 	_screen->setScreenDim(od);
1723 }
1724 
restParty_updateMonsters()1725 bool EoBCoreEngine::restParty_updateMonsters() {
1726 	bool sfxEnabled = _sound->sfxEnabled();
1727 	bool musicEnabled = _sound->musicEnabled();
1728 	_sound->enableSFX(false);
1729 	_sound->enableMusic(false);
1730 
1731 	for (int i = 0; i < 5; i++) {
1732 		_partyResting = true;
1733 		Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
1734 		int od = _screen->curDimIndex();
1735 		_screen->setScreenDim(7);
1736 		updateMonsters(0);
1737 		updateMonsters(1);
1738 		timerProcessFlyingObjects(0);
1739 		_screen->setScreenDim(od);
1740 		_screen->setFont(of);
1741 		_partyResting = false;
1742 
1743 		for (int ii = 0; ii < 30; ii++) {
1744 			if (_monsters[ii].mode == 8)
1745 				continue;
1746 			if (getBlockDistance(_currentBlock, _monsters[ii].block) >= 2)
1747 				continue;
1748 
1749 			restParty_displayWarning(_menuStringsRest4[0]);
1750 			_sound->enableSFX(sfxEnabled);
1751 			_sound->enableMusic(musicEnabled);
1752 			return true;
1753 		}
1754 	}
1755 
1756 	_sound->enableSFX(sfxEnabled);
1757 	_sound->enableMusic(musicEnabled);
1758 	return false;
1759 }
1760 
restParty_getCharacterWithLowestHp()1761 int EoBCoreEngine::restParty_getCharacterWithLowestHp() {
1762 	int lhp = 900;
1763 	int res = -1;
1764 
1765 	for (int i = 0; i < 6; i++) {
1766 		if (!testCharacter(i, 3))
1767 			continue;
1768 		if (_characters[i].hitPointsCur >= _characters[i].hitPointsMax)
1769 			continue;
1770 		if (_characters[i].hitPointsCur < lhp) {
1771 			lhp = _characters[i].hitPointsCur;
1772 			res = i;
1773 		}
1774 	}
1775 
1776 	return res + 1;
1777 }
1778 
restParty_checkHealSpells(int charIndex)1779 bool EoBCoreEngine::restParty_checkHealSpells(int charIndex) {
1780 	static const uint8 eob1healSpells[] = { 2, 15, 20 };
1781 	static const uint8 eob2healSpells[] = { 3, 16, 20 };
1782 	const uint8 *spells = _flags.gameID == GI_EOB1 ? eob1healSpells : eob2healSpells;
1783 	const int8 *list = _characters[charIndex].clericSpells;
1784 
1785 	for (int i = 0; i < 80; i++) {
1786 		int s = list[i] < 0 ? -list[i] : list[i];
1787 		if (s == spells[0] || s == spells[1] || s == spells[2])
1788 			return true;
1789 	}
1790 
1791 	return false;
1792 }
1793 
restParty_checkSpellsToLearn()1794 bool EoBCoreEngine::restParty_checkSpellsToLearn() {
1795 	for (int i = 0; i < 6; i++) {
1796 		if (!testCharacter(i, 0x43))
1797 			continue;
1798 
1799 		if ((getCharacterLevelIndex(2, _characters[i].cClass) != -1 || getCharacterLevelIndex(4, _characters[i].cClass) != -1) && (checkInventoryForItem(i, 30, -1) != -1)) {
1800 			for (int ii = 0; ii < 80; ii++) {
1801 				if (_characters[i].clericSpells[ii] < 0)
1802 					return true;
1803 			}
1804 		}
1805 
1806 		if ((getCharacterLevelIndex(1, _characters[i].cClass) != -1) && (checkInventoryForItem(i, 29, -1) != -1)) {
1807 			for (int ii = 0; ii < 80; ii++) {
1808 				if (_characters[i].mageSpells[ii] < 0)
1809 					return true;
1810 			}
1811 		}
1812 	}
1813 
1814 	return false;
1815 }
1816 
restParty_extraAbortCondition()1817 bool EoBCoreEngine::restParty_extraAbortCondition() {
1818 	return false;
1819 }
1820 
delay(uint32 millis,bool,bool)1821 void EoBCoreEngine::delay(uint32 millis, bool, bool) {
1822 	while (millis && !shouldQuit() && !(_allowSkip && skipFlag())) {
1823 		updateInput();
1824 		uint32 step = MIN<uint32>(millis, (_tickLength / 5));
1825 		_system->delayMillis(step);
1826 		millis -= step;
1827 	}
1828 }
1829 
displayParchment(int id)1830 void EoBCoreEngine::displayParchment(int id) {
1831 	_txt->setWaitButtonMode(1);
1832 	_txt->resetPageBreakString();
1833 	gui_updateControls();
1834 
1835 	if (id >= 0) {
1836 		// display text
1837 		Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
1838 		if (s) {
1839 			_screen->loadFileDataToPage(s, 5, 32000);
1840 		} else {
1841 			s = _res->createReadStream("TEXT.CPS");
1842 			if (s->readSint32BE() + 12 == s->size())
1843 				_screen->loadSpecialAmigaCPS("TEXT.CPS", 5, false);
1844 			else
1845 				_screen->loadBitmap("TEXT.CPS", 5, 5, 0, true);
1846 		}
1847 		delete s;
1848 		_screen->set16bitShadingLevel(4);
1849 		gui_drawBox(0, 0, 176, 175, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
1850 		_screen->set16bitShadingLevel(0);
1851 		_txt->setupField(12, 1);
1852 		if (_flags.gameID == GI_EOB2)
1853 			id++;
1854 		_txt->printDialogueText(id, _okStrings[0]);
1855 
1856 	} else {
1857 		// display bitmap
1858 		static const uint8 x[] = { 0, 20, 0 };
1859 		static const uint8 y[] = { 0, 0, 96 };
1860 		id = -id - 1;
1861 
1862 		if (_flags.platform == Common::kPlatformAmiga)
1863 			_txt->setupField(9, 0);
1864 
1865 		drawSequenceBitmap("MAP", 0, x[id], y[id], 0);
1866 
1867 		removeInputTop();
1868 		while (!shouldQuit()) {
1869 			delay(_tickLength);
1870 			if (checkInput(0, false, 0) & 0xFF)
1871 				break;
1872 			removeInputTop();
1873 		}
1874 		removeInputTop();
1875 	}
1876 
1877 	restoreAfterDialogueSequence();
1878 }
1879 
countResurrectionCandidates()1880 int EoBCoreEngine::countResurrectionCandidates() {
1881 	_rrCount = 0;
1882 	memset(_rrNames, 0, 10 * sizeof(const char *));
1883 
1884 	for (int i = 0; i < 6; i++) {
1885 		if (!testCharacter(i, 1))
1886 			continue;
1887 		if (_characters[i].hitPointsCur != -10)
1888 			continue;
1889 
1890 		_rrNames[_rrCount] = _characters[i].name;
1891 		_rrId[_rrCount++] = i;
1892 	}
1893 
1894 	for (int i = 0; i < 6; i++) {
1895 		if (!testCharacter(i, 1))
1896 			continue;
1897 
1898 		for (int ii = 0; ii < 27; ii++) {
1899 			uint16 inv = _characters[i].inventory[ii];
1900 			if (!inv)
1901 				continue;
1902 
1903 			if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[inv].type].extraProperties & 0x7F) != 8)) || (_flags.gameID == GI_EOB2 && _items[inv].type != 33))
1904 				continue;
1905 
1906 			_rrNames[_rrCount] = _npcPreset[_items[inv].value - 1].name;
1907 			_rrId[_rrCount++] = -_items[inv].value;
1908 		}
1909 	}
1910 
1911 	if (_itemInHand > 0) {
1912 		if ((_flags.gameID == GI_EOB1 && ((_itemTypes[_items[_itemInHand].type].extraProperties & 0x7F) == 8)) || (_flags.gameID == GI_EOB2 && _items[_itemInHand].type == 33)) {
1913 			_rrNames[_rrCount] = _npcPreset[_items[_itemInHand].value - 1].name;
1914 			_rrId[_rrCount++] = -_items[_itemInHand].value;
1915 		}
1916 	}
1917 
1918 	return _rrCount;
1919 }
1920 
seq_portal()1921 void EoBCoreEngine::seq_portal() {
1922 	uint8 *shapes1[5];
1923 	uint8 *shapes2[5];
1924 	uint8 *shapes3[5];
1925 	uint8 *shape0;
1926 
1927 	_screen->loadShapeSetBitmap("PORTALA", 5, 3);
1928 
1929 	for (int i = 0; i < 5; i++) {
1930 		shapes1[i] = _screen->encodeShape(i * 3, 0, 3, 75, false, _cgaMappingDefault);
1931 		shapes2[i] = _screen->encodeShape(i * 3, 80, 3, 75, false, _cgaMappingDefault);
1932 		shapes3[i] = _screen->encodeShape(15, i * 18, 15, 18, false, _cgaMappingDefault);
1933 	}
1934 
1935 	shape0 = _screen->encodeShape(30, 0, 8, 77, false, _cgaMappingDefault);
1936 	_screen->loadEoBBitmap("PORTALB", _cgaMappingDefault, 5, 3, 2);
1937 
1938 	snd_playSoundEffect(33);
1939 	snd_playSoundEffect(19);
1940 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 2, 5, Screen::CR_NO_P_CHECK);
1941 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 0, 2, Screen::CR_NO_P_CHECK);
1942 	_screen->drawShape(2, shapes3[0], 28, 9, 0);
1943 	_screen->drawShape(2, shapes1[0], 34, 28, 0);
1944 	_screen->drawShape(2, shapes2[0], 120, 28, 0);
1945 	_screen->drawShape(2, shape0, 56, 27, 0);
1946 	_screen->crossFadeRegion(24, 0, 24, 0, 144, 104, 2, 0);
1947 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 5, 2, Screen::CR_NO_P_CHECK);
1948 	delay(30 * _tickLength);
1949 
1950 	for (const int8 *pos = _portalSeq; *pos > -1 && !shouldQuit();) {
1951 		int s = *pos++;
1952 		_screen->drawShape(0, shapes3[s], 28, 9, 0);
1953 		_screen->drawShape(0, shapes1[s], 34, 28, 0);
1954 		_screen->drawShape(0, shapes2[s], 120, 28, 0);
1955 
1956 		if ((s == 1) && (pos >= _portalSeq + 3)) {
1957 			if (*(pos - 3) == 0) {
1958 				snd_playSoundEffect(24);
1959 				snd_playSoundEffect(86);
1960 			}
1961 		}
1962 
1963 		s = *pos++;
1964 		if (s == 0) {
1965 			_screen->drawShape(0, shape0, 56, 27, 0);
1966 		} else {
1967 			s--;
1968 			_screen->copyRegion((s % 5) << 6, s / 5 * 77, 56, 27, 64, 77, 2, 0, Screen::CR_NO_P_CHECK);
1969 		}
1970 
1971 		if (s == 1)
1972 			snd_playSoundEffect(31);
1973 		else if (s == 3) {
1974 			if (*(pos - 2) == 3)
1975 				snd_playSoundEffect(90);
1976 		}
1977 
1978 		_screen->updateScreen();
1979 		delay(2 * _tickLength);
1980 	}
1981 
1982 	delete[] shape0;
1983 	for (int i = 0; i < 5; i++) {
1984 		delete[] shapes1[i];
1985 		delete[] shapes2[i];
1986 		delete[] shapes3[i];
1987 	}
1988 }
1989 
checkPassword()1990 bool EoBCoreEngine::checkPassword() {
1991 	char answ[20];
1992 	Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
1993 	_screen->copyPage(0, 10);
1994 
1995 	_screen->setScreenDim(13);
1996 	gui_drawBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, guiSettings()->colors.frame1, guiSettings()->colors.frame2, -1);
1997 	gui_drawBox((_screen->_curDim->sx << 3) + 1, _screen->_curDim->sy + 1, (_screen->_curDim->w << 3) - 2, _screen->_curDim->h - 2, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
1998 	_screen->modifyScreenDim(13, _screen->_curDim->sx + 1, _screen->_curDim->sy + 2, _screen->_curDim->w - 2, _screen->_curDim->h - 16);
1999 
2000 	for (int i = 0; i < 3; i++) {
2001 		_screen->fillRect(_screen->_curDim->sx << 3, _screen->_curDim->sy, ((_screen->_curDim->sx + _screen->_curDim->w) << 3) - 1, (_screen->_curDim->sy + _screen->_curDim->h) - 1, guiSettings()->colors.fill);
2002 		int c = rollDice(1, _mnNumWord - 1, -1);
2003 		const uint8 *shp = (_mnDef[c << 2] < _numLargeItemShapes) ? _largeItemShapes[_mnDef[c << 2]] : (_mnDef[c << 2] < 15 ? 0 : _smallItemShapes[_mnDef[c << 2] - 15]);
2004 		assert(shp);
2005 		_screen->drawShape(0, shp, 100, 2, 13);
2006 		_screen->printShadedText(Common::String::format(_mnPrompt[0], _mnDef[(c << 2) + 1], _mnDef[(c << 2) + 2]).c_str(), (_screen->_curDim->sx + 1) << 3, _screen->_curDim->sy, guiSettings()->colors.guiColorWhite, guiSettings()->colors.fill, guiSettings()->colors.guiColorBlack);
2007 		memset(answ, 0, 20);
2008 		gui_drawBox(76, 100, 133, 14, guiSettings()->colors.frame2, guiSettings()->colors.frame1, -1);
2009 		gui_drawBox(77, 101, 131, 12, guiSettings()->colors.frame2, guiSettings()->colors.frame1, -1);
2010 		if (_gui->getTextInput(answ, 10, 103, 15, guiSettings()->colors.guiColorWhite, guiSettings()->colors.fill, guiSettings()->colors.guiColorDarkRed) < 0)
2011 			i = 3;
2012 		if (!scumm_stricmp(_mnWord[c], answ))
2013 			break;
2014 		else if (i == 2)
2015 			return false;
2016 	}
2017 
2018 	_screen->modifyScreenDim(13, _screen->_curDim->sx - 1, _screen->_curDim->sy - 2, _screen->_curDim->w + 2, _screen->_curDim->h + 16);
2019 	_screen->setFont(of);
2020 	_screen->copyPage(10, 0);
2021 	return true;
2022 }
2023 
convertAsciiToSjis(Common::String str)2024 Common::String EoBCoreEngine::convertAsciiToSjis(Common::String str) {
2025 	if (_flags.platform != Common::kPlatformFMTowns)
2026 		return str;
2027 
2028 	Common::String n;
2029 	const char *src = str.c_str();
2030 	int pos = 0;
2031 	for (uint32 i = 0; i < str.size(); ++i) {
2032 		if (src[i] & 0x80) {
2033 			n.insertChar(src[i++], pos++);
2034 			n.insertChar(src[i], pos++);
2035 		} else if (src[i] >= 32 && src[i] <= 64) {
2036 			n.insertChar(_ascii2SjisTables[1][(src[i] - 32) * 2], pos++);
2037 			n.insertChar(_ascii2SjisTables[1][(src[i] - 32) * 2 + 1], pos++);
2038 		} else if ((src[i] >= 97 && src[i] <= 122) || (src[i] >= 65 && src[i] <= 90)) {
2039 			char c = (src[i] >= 97) ? src[i] - 97 : src[i] - 65;
2040 			n.insertChar(_ascii2SjisTables2[0][c * 2], pos++);
2041 			n.insertChar(_ascii2SjisTables2[0][c * 2 + 1], pos++);
2042 		}
2043 	}
2044 
2045 	return n;
2046 }
2047 
useSlotWeapon(int charIndex,int slotIndex,Item item)2048 void EoBCoreEngine::useSlotWeapon(int charIndex, int slotIndex, Item item) {
2049 	EoBCharacter *c = &_characters[charIndex];
2050 	int tp = item ? _items[item].type : 0;
2051 
2052 	if (c->effectFlags & 0x40)
2053 		removeCharacterEffect(_flags.gameID == GI_EOB1 ? 8 : 10, charIndex, 1); // remove invisibility effect
2054 
2055 	int ep = _itemTypes[tp].extraProperties & 0x7F;
2056 	int8 inflict = 0;
2057 
2058 	if (ep == 1) {
2059 		inflict = closeDistanceAttack(charIndex, item);
2060 		if (!inflict)
2061 			inflict = -1;
2062 		snd_playSoundEffect(32);
2063 	} else if (ep == 2) {
2064 		inflict = thrownAttack(charIndex, slotIndex, item);
2065 	} else if (ep == 3) {
2066 		inflict = projectileWeaponAttack(charIndex, item);
2067 		gui_drawCharPortraitWithStats(charIndex);
2068 	}
2069 
2070 	if (inflict > 0) {
2071 		if (_items[item].flags & 8) {
2072 			c->hitPointsCur += inflict;
2073 			gui_drawCharPortraitWithStats(charIndex);
2074 		}
2075 
2076 		if (_items[item].flags & 0x10)
2077 			c->inventory[slotIndex] = 0;
2078 
2079 		inflictMonsterDamage(&_monsters[_dstMonsterIndex], inflict, true);
2080 	}
2081 
2082 	c->disabledSlots ^= (1 << slotIndex);
2083 	c->slotStatus[slotIndex] = inflict;
2084 
2085 	gui_drawCharPortraitWithStats(charIndex);
2086 	setCharEventTimer(charIndex, 18, inflict >= -2 ? slotIndex + 2 : slotIndex, 1);
2087 }
2088 
closeDistanceAttack(int charIndex,Item item)2089 int EoBCoreEngine::closeDistanceAttack(int charIndex, Item item) {
2090 	if (charIndex > 1)
2091 		return -3;
2092 
2093 	uint16 d = calcNewBlockPosition(_currentBlock, _currentDirection);
2094 	int r = getClosestMonster(charIndex, d);
2095 
2096 	if (r == -1) {
2097 		uint8 w = _specialWallTypes[_levelBlockProperties[d].walls[_sceneDrawVarDown]];
2098 		if (w == 0xFF) {
2099 			if (_flags.gameID == GI_EOB1) {
2100 				_levelBlockProperties[d].walls[_sceneDrawVarDown]++;
2101 				_levelBlockProperties[d].walls[_sceneDrawVarDown ^ 2]++;
2102 
2103 			} else {
2104 				for (int i = 0; i < 4; i++) {
2105 					if (_specialWallTypes[_levelBlockProperties[d].walls[i]] == 0xFF)
2106 						_levelBlockProperties[d].walls[i]++;
2107 				}
2108 			}
2109 			_sceneUpdateRequired = true;
2110 
2111 		} else if ((_flags.gameID == GI_EOB1) || (_flags.gameID == GI_EOB2 && w != 8 && w != 9)) {
2112 			return -1;
2113 		}
2114 
2115 		return (_flags.gameID == GI_EOB2 && ((_itemTypes[_items[item].type].allowedClasses & 4) || !item)) ? -5 : -2;
2116 
2117 	} else {
2118 		if (_monsters[r].flags & 0x20) {
2119 			killMonster(&_monsters[r], 1);
2120 			_txt->printMessage(_monsterDustStrings[0]);
2121 			return -2;
2122 		}
2123 
2124 		if (!characterAttackHitTest(charIndex, r, item, 1))
2125 			return -1;
2126 
2127 		uint16 flg = 0x100;
2128 
2129 		if (isMagicEffectItem(item))
2130 			flg |= 1;
2131 
2132 		_dstMonsterIndex = r;
2133 		return calcMonsterDamage(&_monsters[r], charIndex, item, 1, flg, 5, 3);
2134 	}
2135 
2136 	return 0;
2137 }
2138 
thrownAttack(int charIndex,int slotIndex,Item item)2139 int EoBCoreEngine::thrownAttack(int charIndex, int slotIndex, Item item) {
2140 	int d = charIndex > 3 ? charIndex - 2 : charIndex;
2141 	if (!launchObject(charIndex, item, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + d], _currentDirection, _items[item].type))
2142 		return 0;
2143 
2144 	snd_playSoundEffect(11);
2145 	_characters[charIndex].inventory[slotIndex] = 0;
2146 	reloadWeaponSlot(charIndex, slotIndex, -1, 0);
2147 	_sceneUpdateRequired = true;
2148 	return 0;
2149 }
2150 
projectileWeaponAttack(int charIndex,Item item)2151 int EoBCoreEngine::projectileWeaponAttack(int charIndex, Item item) {
2152 	int tp = _items[item].type;
2153 
2154 	if (_flags.gameID == GI_EOB1)
2155 		assert(tp >= 7);
2156 
2157 	int t = _projectileWeaponAmmoTypes[_flags.gameID == GI_EOB1 ? tp - 7 : tp];
2158 	Item ammoItem = 0;
2159 
2160 	if (t == 16) {
2161 		if (_characters[charIndex].inventory[0] && _items[_characters[charIndex].inventory[0]].type == 16)
2162 			SWAP(ammoItem, _characters[charIndex].inventory[0]);
2163 		else if (_characters[charIndex].inventory[1] && _items[_characters[charIndex].inventory[1]].type == 16)
2164 			SWAP(ammoItem, _characters[charIndex].inventory[1]);
2165 		else if (_characters[charIndex].inventory[16])
2166 			ammoItem = getQueuedItem(&_characters[charIndex].inventory[16], 0, -1);
2167 
2168 	} else {
2169 		for (int i = 0; i < 27; i++) {
2170 			if (_items[_characters[charIndex].inventory[i]].type == t) {
2171 				SWAP(ammoItem, _characters[charIndex].inventory[i]);
2172 				if (i < 2)
2173 					gui_drawCharPortraitWithStats(charIndex);
2174 				break;
2175 			}
2176 		}
2177 	}
2178 
2179 	if (!ammoItem)
2180 		return -4;
2181 
2182 	int c = charIndex;
2183 	if (c > 3)
2184 		c -= 2;
2185 
2186 	if (launchObject(charIndex, ammoItem, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + c], _currentDirection, tp)) {
2187 		snd_playSoundEffect(tp == 7 ? 26 : 11);
2188 		_sceneUpdateRequired = true;
2189 	}
2190 
2191 	return 0;
2192 }
2193 
inflictMonsterDamage(EoBMonsterInPlay * m,int damage,bool giveExperience)2194 void EoBCoreEngine::inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience) {
2195 	m->hitPointsCur -= damage;
2196 	m->flags = (m->flags & 0xF7) | 1;
2197 
2198 	if (_monsterProps[m->type].capsFlags & 0x2000) {
2199 		explodeMonster(m);
2200 		checkSceneUpdateNeed(m->block);
2201 		m->hitPointsCur = 0;
2202 	} else {
2203 		if (checkSceneUpdateNeed(m->block)) {
2204 			m->flags |= 2;
2205 			if (_preventMonsterFlash)
2206 				return;
2207 			flashMonsterShape(m);
2208 		}
2209 	}
2210 
2211 	if (m->hitPointsCur <= 0)
2212 		killMonster(m, giveExperience);
2213 	else if (getBlockDistance(m->block, _currentBlock) < 4)
2214 		m->dest = _currentBlock;
2215 }
2216 
calcAndInflictMonsterDamage(EoBMonsterInPlay * m,int times,int pips,int offs,int flags,int savingThrowType,int savingThrowEffect)2217 void EoBCoreEngine::calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect) {
2218 	int dmg = calcMonsterDamage(m, times, pips, offs, flags, savingThrowType, savingThrowEffect);
2219 	if (dmg > 0)
2220 		inflictMonsterDamage(m, dmg, flags & 0x800 ? true : false);
2221 }
2222 
calcAndInflictCharacterDamage(int charIndex,int times,int itemOrPips,int useStrModifierOrBase,int flags,int savingThrowType,int savingThrowEffect)2223 void EoBCoreEngine::calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect) {
2224 	int dmg = calcCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flags, savingThrowType, savingThrowEffect);
2225 	if (dmg)
2226 		inflictCharacterDamage(charIndex, dmg);
2227 }
2228 
calcCharacterDamage(int charIndex,int times,int itemOrPips,int useStrModifierOrBase,int flags,int savingThrowType,int savingThrowEffect)2229 int EoBCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flags, int savingThrowType, int savingThrowEffect) {
2230 	int s = (flags & 0x100) ? calcDamageModifers(times, 0, itemOrPips, _items[itemOrPips].type, useStrModifierOrBase) : rollDice(times, itemOrPips, useStrModifierOrBase);
2231 	EoBCharacter *c = &_characters[charIndex];
2232 
2233 	if (savingThrowType != 5) {
2234 		if (trySavingThrow(c, _charClassModifier[c->cClass], c->level[0], savingThrowType, c->raceSex >> 1 /*fix bug in original code by adding a right shift*/))
2235 			s = savingThrowReduceDamage(savingThrowEffect, s);
2236 	}
2237 
2238 	if ((flags & 0x110) == 0x110) {
2239 		if (!calcDamageCheckItemType(_items[itemOrPips].type))
2240 			s = 1;
2241 	}
2242 
2243 	if (flags & 4) {
2244 		if (checkInventoryForRings(charIndex, 3))
2245 			s = 0;
2246 	}
2247 
2248 	if (flags & 0x400) {
2249 		if (c->effectFlags & 0x2000)
2250 			s = 0;
2251 		else
2252 			_txt->printMessage(_characterStatusStrings8[0], -1, c->name);
2253 	}
2254 
2255 	return s;
2256 }
2257 
inflictCharacterDamage(int charIndex,int damage)2258 void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
2259 	EoBCharacter *c = &_characters[charIndex];
2260 	if (!testCharacter(charIndex, 3))
2261 		return;
2262 
2263 	if (c->effectsRemainder[3])
2264 		c->effectsRemainder[3] = (damage < c->effectsRemainder[3]) ? (c->effectsRemainder[3] - damage) : 0;
2265 
2266 	c->hitPointsCur -= damage;
2267 	c->damageTaken = damage;
2268 
2269 	if (c->hitPointsCur > -10) {
2270 		snd_playSoundEffect(21);
2271 	} else {
2272 		c->hitPointsCur = -10;
2273 		c->flags &= 1;
2274 		c->food = 0;
2275 		removeAllCharacterEffects(charIndex);
2276 		snd_playSoundEffect(22);
2277 	}
2278 
2279 	if (c->effectsRemainder[0]) {
2280 		c->effectsRemainder[0] = (damage < c->effectsRemainder[0]) ? (c->effectsRemainder[0] - damage) : 0;
2281 		if (!c->effectsRemainder[0])
2282 			removeCharacterEffect(1, charIndex, 1);
2283 	}
2284 
2285 	if (_currentControlMode)
2286 		gui_drawFaceShape(charIndex);
2287 	else
2288 		gui_drawCharPortraitWithStats(charIndex);
2289 
2290 	if (c->hitPointsCur <= 0 && _updateFlags == 1 && charIndex == _openBookChar) {
2291 		Button b;
2292 		clickedSpellbookAbort(&b);
2293 	}
2294 
2295 	setCharEventTimer(charIndex, 18, 6, 1);
2296 }
2297 
characterAttackHitTest(int charIndex,int monsterIndex,int item,int attackType)2298 bool EoBCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType) {
2299 	if (charIndex < 0)
2300 		return true;
2301 
2302 	int p = item ? (_flags.gameID == GI_EOB1 ? _items[item].type : (_itemTypes[_items[item].type].extraProperties & 0x7F)) : 0;
2303 
2304 	if (_monsters[monsterIndex].flags & 0x20)
2305 		return true;// EOB 2 only ?
2306 
2307 	int t = _monsters[monsterIndex].type;
2308 	int d = (p < 1 || p > 3) ? 0 : _items[item].value;
2309 
2310 	if (_flags.gameID == GI_EOB2) {
2311 		if ((p > 0 && p < 4) || !item) {
2312 			if (((_monsterProps[t].immunityFlags & 0x200) && (d <= 0)) || ((_monsterProps[t].immunityFlags & 0x1000) && (d <= 1)))
2313 				return false;
2314 		}
2315 	}
2316 
2317 	d += (attackType ? getStrHitChanceModifier(charIndex) : getDexHitChanceModifier(charIndex));
2318 
2319 	int m = getMonsterAcHitChanceModifier(charIndex, _monsterProps[t].armorClass) - d;
2320 	int s = rollDice(1, 20);
2321 
2322 	_monsters[monsterIndex].flags |= 1;
2323 
2324 	if (_flags.gameID == GI_EOB1) {
2325 		if (_partyEffectFlags & 0x30)
2326 			s++;
2327 		if (_characters[charIndex].effectFlags & 0x40)
2328 			s++;
2329 	} else if ((_partyEffectFlags & 0x8400) || (_characters[charIndex].effectFlags & 0x1000)) {
2330 		s++;
2331 	}
2332 
2333 	s = CLIP(s, 1, 20);
2334 
2335 	return s >= m;
2336 }
2337 
monsterAttackHitTest(EoBMonsterInPlay * m,int charIndex)2338 bool EoBCoreEngine::monsterAttackHitTest(EoBMonsterInPlay *m, int charIndex) {
2339 	int tp = m->type;
2340 	EoBMonsterProperty *p = &_monsterProps[tp];
2341 
2342 	int r = rollDice(1, 20);
2343 	if (r != 20) {
2344 		// Prot from evil
2345 		if (_characters[charIndex].effectFlags & 0x800)
2346 			r -= 2;
2347 		// blur
2348 		if (_characters[charIndex].effectFlags & 0x10)
2349 			r -= 2;
2350 		// prayer
2351 		if (_partyEffectFlags & 0x8000)
2352 			r--;
2353 	}
2354 
2355 	return ((r == 20) || (r >= (p->hitChance - _characters[charIndex].armorClass)));
2356 }
2357 
flyingObjectMonsterHit(EoBFlyingObject * fo,int monsterIndex)2358 bool EoBCoreEngine::flyingObjectMonsterHit(EoBFlyingObject *fo, int monsterIndex) {
2359 	if (fo->attackerId != -1) {
2360 		if (!characterAttackHitTest(fo->attackerId, monsterIndex, fo->item, 0))
2361 			return false;
2362 	}
2363 	calcAndInflictMonsterDamage(&_monsters[monsterIndex], fo->attackerId, fo->item, 0, (fo->attackerId == -1) ? 0x110 : 0x910, 5, 3);
2364 	return true;
2365 }
2366 
flyingObjectPartyHit(EoBFlyingObject * fo)2367 bool EoBCoreEngine::flyingObjectPartyHit(EoBFlyingObject *fo) {
2368 	int ps = _dscItemPosIndex[(_currentDirection << 2) + (_items[fo->item].pos & 3)];
2369 	bool res = false;
2370 
2371 	bool b = ((_currentDirection == fo->direction || _currentDirection == (fo->direction ^ 2)) && ps > 2);
2372 	int s = ps << 1;
2373 	if (ps > 2)
2374 		s += rollDice(1, 2, -1);
2375 
2376 	static const int8 charId[] = { 0, -1, 1, -1, 2, 4, 3, 5 };
2377 
2378 	for (int i = 0; i < 2; i++) {
2379 		int c = charId[s];
2380 		s ^= 1;
2381 		if (!testCharacter(c, 3))
2382 			continue;
2383 		calcAndInflictCharacterDamage(c, -1, fo->item, 0, 0x110, 5, 3);
2384 		res = true;
2385 		if (ps < 2 || b == 0)
2386 			break;
2387 	}
2388 
2389 	return res;
2390 }
2391 
monsterCloseAttack(EoBMonsterInPlay * m)2392 void EoBCoreEngine::monsterCloseAttack(EoBMonsterInPlay *m) {
2393 	int first = _monsterCloseAttDstTable1[(_currentDirection << 2) + m->dir] * 12;
2394 	int v = (m->pos == 4) ? rollDice(1, 2, -1) : _monsterCloseAttChkTable2[(m->dir << 2) + m->pos];
2395 	if (!v)
2396 		first += 6;
2397 
2398 	int last = first + 6;
2399 	for (int i = first; i < last; i++) {
2400 		int c = _monsterCloseAttDstTable2[i];
2401 		if (!testCharacter(c, 3))
2402 			continue;
2403 
2404 		// Character Invisibility
2405 		if ((_characters[c].effectFlags & 0x140) && (rollDice(1, 20) >= 5))
2406 			continue;
2407 
2408 		int dmg = 0;
2409 		for (int ii = 0; ii < _monsterProps[m->type].attacksPerRound; ii++) {
2410 			if (!monsterAttackHitTest(m, c))
2411 				continue;
2412 			dmg += rollDice(_monsterProps[m->type].dmgDc[ii].times, _monsterProps[m->type].dmgDc[ii].pips, _monsterProps[m->type].dmgDc[ii].base);
2413 			if (_characters[c].effectsRemainder[1]) {
2414 				if (--_characters[c].effectsRemainder[1])
2415 					dmg = 0;
2416 			}
2417 		}
2418 
2419 		if (dmg > 0) {
2420 			if ((_monsterProps[m->type].capsFlags & 0x80) && rollDice(1, 4, -1) != 3) {
2421 				int slot = rollDice(1, 27, -1);
2422 				for (int iii = 0; iii < 27; iii++) {
2423 					Item itm = _characters[c].inventory[slot];
2424 					if (!itm || !(_itemTypes[_items[itm].type].extraProperties & 0x80)) {
2425 						if (++slot == 27)
2426 							slot = 0;
2427 						continue;
2428 					}
2429 
2430 					_characters[c].inventory[slot] = 0;
2431 					_txt->printMessage(_ripItemStrings[(_characters[c].raceSex & 1) ^ 1], -1, _characters[c].name);
2432 					printFullItemName(itm);
2433 					_txt->printMessage(_ripItemStrings[2]);
2434 					break;
2435 				}
2436 				gui_drawCharPortraitWithStats(c);
2437 			}
2438 
2439 			inflictCharacterDamage(c, dmg);
2440 
2441 			if (_monsterProps[m->type].capsFlags & 0x10) {
2442 				statusAttack(c, 2, _monsterSpecAttStrings[_flags.gameID == GI_EOB1 ? 3 : 2], 0, 1, 8, 1);
2443 				_characters[c].effectFlags &= ~0x2000;
2444 			}
2445 
2446 			if (_monsterProps[m->type].capsFlags & 0x20)
2447 				statusAttack(c, 4, _monsterSpecAttStrings[_flags.gameID == GI_EOB1 ? 4 : 3], 2, 5, 9, 1);
2448 
2449 			if (_monsterProps[m->type].capsFlags & 0x8000)
2450 				statusAttack(c, 8, _monsterSpecAttStrings[4], 2, 0, 0, 1);
2451 
2452 		}
2453 
2454 		if (!(_monsterProps[m->type].capsFlags & 0x4000))
2455 			return;
2456 	}
2457 }
2458 
monsterSpellCast(EoBMonsterInPlay * m,int type)2459 void EoBCoreEngine::monsterSpellCast(EoBMonsterInPlay *m, int type) {
2460 	launchMagicObject(-1, type, m->block, m->pos, m->dir);
2461 	snd_processEnvironmentalSoundEffect(_spells[_magicFlightObjectProperties[type << 2]].sound, m->block);
2462 }
2463 
statusAttack(int charIndex,int attackStatusFlags,const char * attackStatusString,int savingThrowType,uint32 effectDuration,int restoreEvent,int noRefresh)2464 void EoBCoreEngine::statusAttack(int charIndex, int attackStatusFlags, const char *attackStatusString, int savingThrowType, uint32 effectDuration, int restoreEvent, int noRefresh) {
2465 	EoBCharacter *c = &_characters[charIndex];
2466 	if ((c->flags & attackStatusFlags) && noRefresh)
2467 		return;
2468 	if (!testCharacter(charIndex, 3))
2469 		return;
2470 
2471 	if (savingThrowType != 5 && specialAttackSavingThrow(charIndex, savingThrowType))
2472 		return;
2473 
2474 	if (attackStatusFlags & 8) {
2475 		removeAllCharacterEffects(charIndex);
2476 		c->flags = (c->flags & 1) | 8;
2477 	} else {
2478 		c->flags |= attackStatusFlags;
2479 	}
2480 
2481 	if ((attackStatusFlags & 0x0C) && (_openBookChar == charIndex) && _updateFlags) {
2482 		Button b;
2483 		clickedSpellbookAbort(&b);
2484 	}
2485 
2486 	if (effectDuration)
2487 		setCharEventTimer(charIndex, effectDuration * 546, restoreEvent, 1);
2488 
2489 	gui_drawCharPortraitWithStats(charIndex);
2490 	_txt->printMessage(_characterStatusStrings13[0], -1, c->name, attackStatusString);
2491 }
2492 
calcMonsterDamage(EoBMonsterInPlay * m,int times,int pips,int offs,int flags,int savingThrowType,int savingThrowEffect)2493 int EoBCoreEngine::calcMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect) {
2494 	int s = flags & 0x100 ? calcDamageModifers(times, m, pips, _items[pips].type, offs) : rollDice(times, pips, offs);
2495 	EoBMonsterProperty *p = &_monsterProps[m->type];
2496 
2497 	if (savingThrowType != 5) {
2498 		if (trySavingThrow(m, 0, p->level, savingThrowType, 6))
2499 			s = savingThrowReduceDamage(savingThrowEffect, s);
2500 	}
2501 
2502 	if ((flags & 0x110) == 0x110) {
2503 		if (!calcDamageCheckItemType(_items[pips].type))
2504 			s = 1;
2505 	}
2506 
2507 	if ((flags & 0x100) && (!(_itemTypes[_items[pips].type].allowedClasses & 4 /* bug in original code ??*/))
2508 	    && ((_flags.gameID == GI_EOB2 && (p->immunityFlags & 0x100)) || (_flags.gameID == GI_EOB1 && (p->capsFlags & 4))))
2509 		s >>= 1;
2510 
2511 	if (p->immunityFlags & 0x2000) {
2512 		if (flags & 0x100) {
2513 			if (_items[pips].value < 3)
2514 				s >>= 2;
2515 			if (_items[pips].value == 3)
2516 				s >>= 1;
2517 			if (s == 0)
2518 				s = _items[pips].value;
2519 
2520 		} else {
2521 			s >>= 1;
2522 		}
2523 	}
2524 
2525 	if (flags & 1) {
2526 		if (tryMonsterAttackEvasion(m))
2527 			s = 0;
2528 	}
2529 
2530 	if (_flags.gameID == GI_EOB1)
2531 		return s;
2532 
2533 	static const uint16 damageImmunityFlags[] = { 0x01, 0x10, 0x02, 0x20, 0x80, 0x400, 0x20, 0x800, 0x40, 0x80, 0x400, 0x40 };
2534 	for (int i = 0; i < 12; i += 2) {
2535 		if ((flags & damageImmunityFlags[i]) && (p->immunityFlags & damageImmunityFlags[i + 1]))
2536 			s = 0;
2537 	}
2538 
2539 	return s;
2540 }
2541 
calcDamageModifers(int charIndex,EoBMonsterInPlay * m,int item,int itemType,int useStrModifier)2542 int EoBCoreEngine::calcDamageModifers(int charIndex, EoBMonsterInPlay *m, int item, int itemType, int useStrModifier) {
2543 	int s = (useStrModifier && (charIndex != -1)) ? getStrDamageModifier(charIndex) : 0;
2544 	if (item) {
2545 		EoBItemType *p = &_itemTypes[itemType];
2546 		int t = m ? m->type : 0;
2547 		s += ((m && (_monsterProps[t].capsFlags & 1)) ? rollDice(p->dmgNumDiceL, p->dmgNumPipsL, p->dmgIncS /* bug in original code ? */) :
2548 		      rollDice(p->dmgNumDiceS, p->dmgNumPipsS, p->dmgIncS));
2549 		s += _items[item].value;
2550 	} else {
2551 		s += rollDice(1, 2);
2552 	}
2553 
2554 	return (s < 0) ? 0 : s;
2555 }
2556 
trySavingThrow(void * target,int hpModifier,int level,int type,int race)2557 bool EoBCoreEngine::trySavingThrow(void *target, int hpModifier, int level, int type, int race) {
2558 	static const int8 constMod[] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5 };
2559 
2560 	if (type == 5)
2561 		return false;
2562 
2563 	int s = getSaveThrowModifier(hpModifier, level, type);
2564 	if (((race == 3 || race == 5) && (type == 4 || type == 1 || type == 0)) || (race == 4 && (type == 4 || type == 1))) {
2565 		EoBCharacter *c = (EoBCharacter *)target;
2566 		s -= constMod[c->constitutionCur];
2567 	}
2568 
2569 	return rollDice(1, 20) >= s;
2570 }
2571 
specialAttackSavingThrow(int charIndex,int type)2572 bool EoBCoreEngine::specialAttackSavingThrow(int charIndex, int type) {
2573 	return trySavingThrow(&_characters[charIndex], _charClassModifier[_characters[charIndex].cClass], _characters[charIndex].level[0], type, _characters[charIndex].raceSex >> 1);
2574 }
2575 
getSaveThrowModifier(int hpModifier,int level,int type)2576 int EoBCoreEngine::getSaveThrowModifier(int hpModifier, int level, int type) {
2577 	const uint8 *tbl = _saveThrowTables[hpModifier];
2578 	if (_saveThrowLevelIndex[hpModifier] < level)
2579 		level = _saveThrowLevelIndex[hpModifier];
2580 	level /= _saveThrowModDiv[hpModifier];
2581 	level += (_saveThrowModExt[hpModifier] * type);
2582 
2583 	return tbl[level];
2584 }
2585 
calcDamageCheckItemType(int itemType)2586 bool EoBCoreEngine::calcDamageCheckItemType(int itemType) {
2587 	itemType = _itemTypes[itemType].extraProperties & 0x7F;
2588 	return (itemType == 2 || itemType == 3) ? true : false;
2589 }
2590 
savingThrowReduceDamage(int savingThrowEffect,int damage)2591 int EoBCoreEngine::savingThrowReduceDamage(int savingThrowEffect, int damage) {
2592 	if (savingThrowEffect == 3)
2593 		return 0;
2594 
2595 	if (savingThrowEffect == 0 || savingThrowEffect == 1)
2596 		return damage >> 1;
2597 
2598 	return damage;
2599 }
2600 
tryMonsterAttackEvasion(EoBMonsterInPlay * m)2601 bool EoBCoreEngine::tryMonsterAttackEvasion(EoBMonsterInPlay *m) {
2602 	return rollDice(1, 100) < _monsterProps[m->type].dmgModifierEvade ? true : false;
2603 }
2604 
getStrHitChanceModifier(int charIndex)2605 int EoBCoreEngine::getStrHitChanceModifier(int charIndex) {
2606 	static const int8 strExtLimit[] = { 1, 51, 76, 91, 100 };
2607 	static const int8 strExtMod[] = { 1, 2, 2, 2, 3 };
2608 	static const int8 strMod[] = { -4, -3, -3, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 4, 4, 5, 6, 7 };
2609 
2610 	int r = strMod[_characters[charIndex].strengthCur - 1];
2611 	if (_characters[charIndex].strengthExtCur) {
2612 		for (int i = 0; i < 5; i++) {
2613 			if (_characters[charIndex].strengthExtCur >= strExtLimit[i])
2614 				r = strExtMod[i];
2615 		}
2616 	}
2617 
2618 	return r;
2619 }
2620 
getStrDamageModifier(int charIndex)2621 int EoBCoreEngine::getStrDamageModifier(int charIndex) {
2622 	static const int8 strExtLimit[] = { 1, 51, 76, 91, 100 };
2623 	static const int8 strExtMod[] = { 3, 3, 4, 5, 6 };
2624 	static const int8 strMod[] = { -3, -2, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 7, 8, 9, 10, 11, 12, 14 };
2625 
2626 	int r = strMod[_characters[charIndex].strengthCur - 1];
2627 	if (_characters[charIndex].strengthExtCur) {
2628 		for (int i = 0; i < 5; i++) {
2629 			if (_characters[charIndex].strengthExtCur >= strExtLimit[i])
2630 				r = strExtMod[i];
2631 		}
2632 	}
2633 
2634 	return r;
2635 }
2636 
getDexHitChanceModifier(int charIndex)2637 int EoBCoreEngine::getDexHitChanceModifier(int charIndex) {
2638 	static const int8 dexMod[] = { -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 3, 4, 4, 4 };
2639 	return dexMod[_characters[charIndex].dexterityCur - 1];
2640 }
2641 
getMonsterAcHitChanceModifier(int charIndex,int monsterAc)2642 int EoBCoreEngine::getMonsterAcHitChanceModifier(int charIndex, int monsterAc) {
2643 	int l = _characters[charIndex].level[0] - 1;
2644 	int cm = _charClassModifier[_characters[charIndex].cClass];
2645 
2646 	return (20 - ((l / _monsterAcHitChanceTable1[cm]) * _monsterAcHitChanceTable2[cm])) - monsterAc;
2647 }
2648 
explodeMonster(EoBMonsterInPlay * m)2649 void EoBCoreEngine::explodeMonster(EoBMonsterInPlay *m) {
2650 	m->flags |= 2;
2651 	if (getBlockDistance(m->block, _currentBlock) < 2) {
2652 		explodeObject(0, _currentBlock, 2);
2653 		for (int i = 0; i < 6; i++)
2654 			calcAndInflictCharacterDamage(i, 6, 6, 0, 8, 1, 0);
2655 	} else {
2656 		explodeObject(0, m->block, 2);
2657 	}
2658 	m->flags &= ~2;
2659 }
2660 
snd_playSong(int track)2661 void EoBCoreEngine::snd_playSong(int track) {
2662 	_sound->playTrack(track);
2663 }
2664 
snd_playSoundEffect(int track,int volume)2665 void EoBCoreEngine::snd_playSoundEffect(int track, int volume) {
2666 	if ((track < 1) || (_flags.gameID == GI_EOB2 && track > 119) || shouldQuit())
2667 		return;
2668 
2669 	_sound->playSoundEffect(track, volume);
2670 }
2671 
snd_stopSound()2672 void EoBCoreEngine::snd_stopSound() {
2673 	_sound->haltTrack();
2674 	_sound->stopAllSoundEffects();
2675 }
2676 
snd_fadeOut(int del)2677 void EoBCoreEngine::snd_fadeOut(int del) {
2678 	_sound->beginFadeOut(del);
2679 }
2680 
2681 } // End of namespace Kyra
2682 
2683 #endif // ENABLE_EOB
2684