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 #if defined(ENABLE_EOB) || defined(ENABLE_LOL)
24
25 #include "kyra/engine/kyra_rpg.h"
26 #include "kyra/sound/sound.h"
27
28 #include "common/system.h"
29
30 namespace Kyra {
31
KyraRpgEngine(OSystem * system,const GameFlags & flags)32 KyraRpgEngine::KyraRpgEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(system, flags), _numFlyingObjects(_flags.gameID == GI_LOL ? 8 : 10) {
33 _txt = 0;
34 _mouseClick = 0;
35 _preserveEvents = _buttonListChanged = false;
36
37 _sceneXoffset = 0;
38 _sceneShpDim = 5;
39
40 _activeButtons = 0;
41
42 _currentLevel = 0;
43
44 _vcnBlocks = 0;
45 _vcfBlocks = 0;
46 _vcnTransitionMask = _vcnMaskTbl = 0;
47 _vcnShift = 0;
48 _vcnColTable = 0;
49 _vcnShiftVal = 0;
50 _vcnBpp = flags.useHiColorMode ? 2 : 1;
51 _vcnSrcBitsPerPixel = (flags.platform == Common::kPlatformAmiga) ? 5 : (_vcnBpp == 2 ? 8 : 4);
52 _vcnDrawLine = 0;
53
54 _vmpPtr = 0;
55 _blockBrightness = _wllVcnOffset = _wllVcnOffset2 = _wllVcnRmdOffset = 0;
56 _blockDrawingBuffer = 0;
57 _sceneWindowBuffer = 0;
58 _monsterShapes = _monsterPalettes = 0;
59
60 _doorShapes = 0;
61
62 _levelDecorationProperties = 0;
63 _levelDecorationData = 0;
64 _levelDecorationShapes = 0;
65 _decorationCount = 0;
66 _mappedDecorationsCount = 0;
67 memset(_visibleBlockIndex, 0, sizeof(_visibleBlockIndex));
68
69 _lvlShapeTop = _lvlShapeBottom = _lvlShapeLeftRight = 0;
70 _levelBlockProperties = 0;
71 _hasTempDataFlags = 0;
72
73 _wllVmpMap = _specialWallTypes = _wllWallFlags = 0;
74 _wllShapeMap = 0;
75
76 _sceneDrawVarDown = _sceneDrawVarRight = _sceneDrawVarLeft = _wllProcessFlag = 0;
77
78 _currentBlock = 0;
79 _currentDirection = 0;
80 _compassDirection = -1;
81 _updateFlags = _clickedSpecialFlag = 0;
82 _sceneDefaultUpdate = 0;
83 _sceneUpdateRequired = false;
84
85 _flyingObjectsPtr = 0;
86 _flyingObjectStructSize = sizeof(EoBFlyingObject);
87
88 _clickedShapeXOffs = _clickedShapeYOffs = 0;
89
90 _dscShapeX = 0;
91 _dscTileIndex = 0;
92 _dscDoorScaleOffs = 0;
93 _dscDim1 = 0;
94 _dscDim2 = 0;
95 _dscBlockMap = 0;
96 _dscBlockIndex = 0;
97 _dscShapeIndex = 0;
98 _dscDimMap = 0;
99 _dscDoorShpIndex = 0;
100 _dscDoorY2 = 0;
101 _dscDoorFrameY1 = 0;
102 _dscDoorFrameY2 = 0;
103 _dscDoorFrameIndex1 = 0;
104 _dscDoorFrameIndex2 = 0;
105
106 _shpDmX1 = _shpDmX2 = 0;
107
108 memset(_openDoorState, 0, sizeof(_openDoorState));
109 memset(_dialogueButtonString, 0, 3 * sizeof(const char *));
110 _dialogueButtonPosX = 0;
111 _dialogueButtonPosY = 0;
112 _dialogueNumButtons = _dialogueButtonYoffs = _dialogueHighlightedButton = 0;
113 _currentControlMode = 0;
114 _specialSceneFlag = 0;
115 _updateCharNum = -1;
116 _activeVoiceFileTotalTime = 0;
117 _updatePortraitSpeechAnimDuration = _resetPortraitAfterSpeechAnim = _needSceneRestore = 0;
118 _fadeText = false;
119
120 memset(_lvlTempData, 0, sizeof(_lvlTempData));
121
122 _dialogueField = false;
123 _dialogueFieldAmiga = true;
124
125 _environmentSfx = _environmentSfxVol = _envSfxDistThreshold = 0;
126 _monsterStepCounter = _monsterStepMode = 0;
127 }
128
~KyraRpgEngine()129 KyraRpgEngine::~KyraRpgEngine() {
130 delete[] _wllVmpMap;
131 delete[] _wllShapeMap;
132 delete[] _specialWallTypes;
133 delete[] _wllWallFlags;
134
135 delete[] _vmpPtr;
136 delete[] _vcnColTable;
137 delete[] _vcnBlocks;
138 delete[] _vcfBlocks;
139 delete[] _vcnTransitionMask;
140 delete[] _vcnShift;
141 delete[] _blockDrawingBuffer;
142 delete[] _sceneWindowBuffer;
143 delete _vcnDrawLine;
144
145 delete[] _lvlShapeTop;
146 delete[] _lvlShapeBottom;
147 delete[] _lvlShapeLeftRight;
148
149 delete[] _doorShapes;
150
151 delete[] _levelDecorationShapes;
152 delete[] _levelDecorationData;
153 delete[] _levelDecorationProperties;
154 delete[] _levelBlockProperties;
155 }
156
init()157 Common::Error KyraRpgEngine::init() {
158 gui_resetButtonList();
159
160 _levelDecorationProperties = new LevelDecorationProperty[100];
161 memset(_levelDecorationProperties, 0, 100 * sizeof(LevelDecorationProperty));
162 _levelDecorationShapes = new uint8*[400];
163 memset(_levelDecorationShapes, 0, 400 * sizeof(uint8 *));
164 _levelBlockProperties = new LevelBlockProperty[1025];
165 memset(_levelBlockProperties, 0, 1025 * sizeof(LevelBlockProperty));
166
167 _wllVmpMap = new uint8[256];
168 memset(_wllVmpMap, 0, 256);
169 _wllShapeMap = new int8[256];
170 memset(_wllShapeMap, 0, 256);
171 _specialWallTypes = new uint8[256];
172 memset(_specialWallTypes, 0, 256);
173 _wllWallFlags = new uint8[256];
174 memset(_wllWallFlags, 0, 256);
175
176 _blockDrawingBuffer = new uint16[1320];
177 memset(_blockDrawingBuffer, 0, 1320 * sizeof(uint16));
178 int windowBufferSize = _flags.useHiColorMode ? 42240 : 21120;
179 _sceneWindowBuffer = new uint8[windowBufferSize];
180 memset(_sceneWindowBuffer, 0, windowBufferSize);
181
182 _lvlShapeTop = new int16[18];
183 memset(_lvlShapeTop, 0, 18 * sizeof(int16));
184 _lvlShapeBottom = new int16[18];
185 memset(_lvlShapeBottom, 0, 18 * sizeof(int16));
186 _lvlShapeLeftRight = new int16[36];
187 memset(_lvlShapeLeftRight, 0, 36 * sizeof(int16));
188
189 _vcnColTable = new uint8[128];
190 for (int i = 0; i < 128; i++)
191 _vcnColTable[i] = i & 0x0F;
192
193 if (_vcnBpp == 2)
194 _vcnDrawLine = new VcnLineDrawingMethods(new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_hiCol), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_hiCol),
195 new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_trans_hiCol), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_trans_hiCol));
196 else if (_flags.platform == Common::kPlatformAmiga)
197 _vcnDrawLine = new VcnLineDrawingMethods(new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_Amiga), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_Amiga),
198 new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_trans_Amiga), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_trans_Amiga));
199 else
200 _vcnDrawLine = new VcnLineDrawingMethods(new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_4bit), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_4bit),
201 new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_fw_trans_4bit), new VcnDrawProc(this, &KyraRpgEngine::vcnDraw_bw_trans_4bit));
202
203 _doorShapes = new uint8*[6];
204 memset(_doorShapes, 0, 6 * sizeof(uint8 *));
205
206 initStaticResource();
207
208 _envSfxDistThreshold = ((_flags.gameID == GI_EOB2 && _sound->getSfxType() == Sound::kTowns) || _sound->getSfxType() == Sound::kAdLib || _sound->getSfxType() == Sound::kPCSpkr) ? 15 : (_sound->getSfxType() == Sound::kAmiga ? 4 : 3);
209
210 _dialogueButtonLabelColor1 = guiSettings()->buttons.labelColor1;
211 _dialogueButtonLabelColor2 = guiSettings()->buttons.labelColor2;
212 _dialogueButtonWidth = guiSettings()->buttons.width;
213
214 return Common::kNoError;
215 }
216
posWithinRect(int posX,int posY,int x1,int y1,int x2,int y2)217 bool KyraRpgEngine::posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2) {
218 if (posX < x1 || posX > x2 || posY < y1 || posY > y2)
219 return false;
220 return true;
221 }
222
drawDialogueButtons()223 void KyraRpgEngine::drawDialogueButtons() {
224 int cp = screen()->setCurPage(0);
225 Screen::FontId of = screen()->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : ((_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns) ? Screen::FID_8_FNT : Screen::FID_6_FNT));
226
227 for (int i = 0; i < _dialogueNumButtons; i++) {
228 int x = _dialogueButtonPosX[i];
229 if (_flags.lang == Common::JA_JPN && _flags.use16ColorMode) {
230 gui_drawBox(x, ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1, 74, 10, 0xEE, 0xCC, -1);
231 screen()->printText(_dialogueButtonString[i], (x + 37 - (screen()->getTextWidth(_dialogueButtonString[i])) / 2) & ~3,
232 ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2) & ~7, _dialogueHighlightedButton == i ? 0xC1 : 0xE1, 0);
233 } else {
234 int sjisYOffset = (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns) ? 1 : ((_flags.lang == Common::JA_JPN && (_dialogueButtonString[i][0] & 0x80)) ? 2 : 0);
235 screen()->set16bitShadingLevel(4);
236 gui_drawBox(x, (_dialogueButtonYoffs + _dialogueButtonPosY[i]), _dialogueButtonWidth, guiSettings()->buttons.height, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
237 screen()->set16bitShadingLevel(0);
238 screen()->printText(_dialogueButtonString[i], x + (_dialogueButtonWidth >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2,
239 (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2 - sjisYOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0);
240 }
241 }
242 screen()->setFont(of);
243 screen()->setCurPage(cp);
244 }
245
processDialogue()246 uint16 KyraRpgEngine::processDialogue() {
247 int df = _dialogueHighlightedButton;
248 int res = 0;
249
250 for (int i = 0; i < _dialogueNumButtons; i++) {
251 int x = _dialogueButtonPosX[i];
252 int y = ((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i]));
253 Common::Point p = getMousePos();
254 if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonWidth, y + guiSettings()->buttons.height)) {
255 _dialogueHighlightedButton = i;
256 break;
257 }
258 }
259
260 if (_dialogueNumButtons == 0) {
261 int e = checkInput(0, false) & 0xFF;
262 removeInputTop();
263
264 if (e) {
265 gui_notifyButtonListChanged();
266
267 if (e == _keyMap[Common::KEYCODE_SPACE] || e == _keyMap[Common::KEYCODE_RETURN]) {
268 snd_stopSpeech(true);
269 }
270 }
271
272 if (snd_updateCharacterSpeech() != 2) {
273 res = 1;
274 if (!shouldQuit()) {
275 removeInputTop();
276 gui_notifyButtonListChanged();
277 }
278 }
279 } else {
280 int e = checkInput(0, false, 0) & 0xFF;
281 removeInputTop();
282 if (e)
283 gui_notifyButtonListChanged();
284
285 if ((_flags.gameID == GI_LOL && (e == 200 || e == 202)) || (_flags.gameID != GI_LOL && (e == 199 || e == 201))) {
286 for (int i = 0; i < _dialogueNumButtons; i++) {
287 int x = _dialogueButtonPosX[i];
288 int y = (gameFlags().use16ColorMode ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i]));
289 Common::Point p = getMousePos();
290 if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonWidth, y + guiSettings()->buttons.height)) {
291 _dialogueHighlightedButton = i;
292 res = _dialogueHighlightedButton + 1;
293 break;
294 }
295 }
296 } else if (e == _keyMap[Common::KEYCODE_SPACE] || e == _keyMap[Common::KEYCODE_RETURN]) {
297 snd_stopSpeech(true);
298 res = _dialogueHighlightedButton + 1;
299 } else if (e == _keyMap[Common::KEYCODE_LEFT] || e == _keyMap[Common::KEYCODE_DOWN]) {
300 if (_dialogueNumButtons > 1 && _dialogueHighlightedButton > 0)
301 _dialogueHighlightedButton--;
302 } else if (e == _keyMap[Common::KEYCODE_RIGHT] || e == _keyMap[Common::KEYCODE_UP]) {
303 if (_dialogueNumButtons > 1 && _dialogueHighlightedButton < (_dialogueNumButtons - 1))
304 _dialogueHighlightedButton++;
305 }
306 }
307
308 if (df != _dialogueHighlightedButton)
309 drawDialogueButtons();
310
311 screen()->updateScreen();
312
313 if (res == 0)
314 return 0;
315
316 stopPortraitSpeechAnim();
317
318 if (game() == GI_LOL) {
319 if (!textEnabled() && _currentControlMode) {
320 screen()->setScreenDim(5);
321 const ScreenDim *d = screen()->getScreenDim(5);
322 screen()->fillRect(d->sx, d->sy + d->h - 9, d->sx + d->w - 1, d->sy + d->h - 1, d->unkA);
323 } else {
324 const ScreenDim *d = screen()->_curDim;
325 if (gameFlags().use16ColorMode)
326 screen()->fillRect(d->sx, d->sy, d->sx + d->w - 3, d->sy + d->h - 2, d->unkA);
327 else
328 screen()->fillRect(d->sx, d->sy, d->sx + d->w - 2, d->sy + d->h - 1, d->unkA);
329 txt()->clearDim(4);
330 txt()->resetDimTextPositions(4);
331 }
332 }
333
334 return res;
335 }
336
delayUntil(uint32 time,bool,bool doUpdate,bool isMainLoop)337 void KyraRpgEngine::delayUntil(uint32 time, bool, bool doUpdate, bool isMainLoop) {
338 uint32 curTime = _system->getMillis();
339 if (time > curTime)
340 delay(time - curTime, doUpdate, isMainLoop);
341 }
342
rollDice(int times,int pips,int inc)343 int KyraRpgEngine::rollDice(int times, int pips, int inc) {
344 if (times <= 0 || pips <= 0)
345 return inc;
346
347 int res = 0;
348 while (times--)
349 res += _rnd.getRandomNumberRng(1, pips);
350
351 return res + inc;
352 }
353
snd_processEnvironmentalSoundEffect(int soundId,int block)354 bool KyraRpgEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) {
355 if (!_sound->sfxEnabled() || shouldQuit())
356 return false;
357
358 if (_environmentSfx)
359 snd_playSoundEffect(_environmentSfx, _environmentSfxVol);
360
361 int dist = 0;
362 if (block) {
363 dist = getBlockDistance(_currentBlock, block);
364 if (dist > _envSfxDistThreshold) {
365 _environmentSfx = 0;
366 return false;
367 }
368 }
369
370 _environmentSfx = soundId;
371
372 if (_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns)
373 _environmentSfxVol = dist ? (16 - dist) * 8 - 1 : 127;
374 else if (_flags.platform == Common::kPlatformAmiga)
375 _environmentSfxVol = dist ? (soundId != 13 ? dist : (dist >= 4) ? 4 : dist) : 1;
376 else
377 _environmentSfxVol = (15 - ((block || (_flags.gameID == GI_LOL && dist < 2)) ? dist : 0)) << 4;
378
379 return true;
380 }
381
updateEnvironmentalSfx(int soundId)382 void KyraRpgEngine::updateEnvironmentalSfx(int soundId) {
383 snd_processEnvironmentalSoundEffect(soundId, _currentBlock);
384 }
385
386 } // End of namespace Kyra
387
388 #endif // ENABLE_EOB || ENABLE_LOL
389