1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/scummsys.h"
24 #include "common/config-manager.h"
25 #include "common/debug-channels.h"
26 #include "common/debug.h"
27 #include "common/events.h"
28 #include "common/file.h"
29 #include "common/random.h"
30 #include "common/fs.h"
31 #include "common/keyboard.h"
32 #include "common/substream.h"
33 #include "common/str.h"
34
35 #include "graphics/surface.h"
36 #include "graphics/pixelformat.h"
37
38 #include "engines/util.h"
39
40 #include "prince/prince.h"
41 #include "prince/graphics.h"
42 #include "prince/script.h"
43 #include "prince/debugger.h"
44 #include "prince/object.h"
45 #include "prince/mob.h"
46 #include "prince/music.h"
47 #include "prince/variatxt.h"
48 #include "prince/font.h"
49 #include "prince/mhwanh.h"
50 #include "prince/cursor.h"
51 #include "prince/archive.h"
52 #include "prince/hero.h"
53 #include "prince/animation.h"
54 #include "prince/curve_values.h"
55
56 namespace Prince {
57
debugEngine(const char * s,...)58 void PrinceEngine::debugEngine(const char *s, ...) {
59 char buf[STRINGBUFLEN];
60 va_list va;
61
62 va_start(va, s);
63 vsnprintf(buf, STRINGBUFLEN, s, va);
64 va_end(va);
65
66 debug("Prince::Engine %s", buf);
67 }
68
PrinceEngine(OSystem * syst,const PrinceGameDescription * gameDesc)69 PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) :
70 Engine(syst), _gameDescription(gameDesc), _graph(nullptr), _script(nullptr), _interpreter(nullptr), _flags(nullptr),
71 _locationNr(0), _debugger(nullptr), _midiPlayer(nullptr), _room(nullptr),
72 _cursor1(nullptr), _cursor2(nullptr), _cursor3(nullptr), _font(nullptr),
73 _suitcaseBmp(nullptr), _roomBmp(nullptr), _cursorNr(0), _picWindowX(0), _picWindowY(0), _randomSource("prince"),
74 _invLineX(134), _invLineY(176), _invLine(5), _invLines(3), _invLineW(70), _invLineH(76), _maxInvW(72), _maxInvH(76),
75 _invLineSkipX(2), _invLineSkipY(3), _showInventoryFlag(false), _inventoryBackgroundRemember(false),
76 _mst_shadow(0), _mst_shadow2(0), _candleCounter(0), _invX1(53), _invY1(18), _invWidth(536), _invHeight(438),
77 _invCurInside(false), _optionsFlag(false), _optionEnabled(0), _invExamY(120), _invMaxCount(2), _invCounter(0),
78 _optionsMob(-1), _currentPointerNumber(1), _selectedMob(-1), _selectedItem(0), _selectedMode(0),
79 _optionsWidth(210), _optionsHeight(170), _invOptionsWidth(210), _invOptionsHeight(130), _optionsStep(20),
80 _invOptionsStep(20), _optionsNumber(7), _invOptionsNumber(5), _optionsColor1(236), _optionsColor2(252),
81 _dialogWidth(600), _dialogHeight(0), _dialogLineSpace(10), _dialogColor1(220), _dialogColor2(223),
82 _dialogFlag(false), _dialogLines(0), _dialogText(nullptr), _mouseFlag(1),
83 _roomPathBitmap(nullptr), _roomPathBitmapTemp(nullptr), _coordsBufEnd(nullptr), _coordsBuf(nullptr), _coords(nullptr),
84 _traceLineLen(0), _rembBitmapTemp(nullptr), _rembBitmap(nullptr), _rembMask(0), _rembX(0), _rembY(0), _fpX(0), _fpY(0),
85 _checkBitmapTemp(nullptr), _checkBitmap(nullptr), _checkMask(0), _checkX(0), _checkY(0), _traceLineFirstPointFlag(false),
86 _tracePointFirstPointFlag(false), _coordsBuf2(nullptr), _coords2(nullptr), _coordsBuf3(nullptr), _coords3(nullptr),
87 _shanLen(0), _directionTable(nullptr), _currentMidi(0), _lightX(0), _lightY(0), _curveData(nullptr), _curvPos(0),
88 _creditsData(nullptr), _creditsDataSize(0), _currentTime(0), _zoomBitmap(nullptr), _shadowBitmap(nullptr), _transTable(nullptr),
89 _flcFrameSurface(nullptr), _shadScaleValue(0), _shadLineLen(0), _scaleValue(0), _dialogImage(nullptr), _mobTranslationData(nullptr),
90 _mobTranslationSize(0), _missingVoice(false) {
91
92 DebugMan.enableDebugChannel("script");
93
94 memset(_audioStream, 0, sizeof(_audioStream));
95 }
96
~PrinceEngine()97 PrinceEngine::~PrinceEngine() {
98 delete _rnd;
99 delete _cursor1;
100 delete _cursor3;
101 delete _midiPlayer;
102 delete _script;
103 delete _flags;
104 delete _interpreter;
105 delete _font;
106 delete _roomBmp;
107 delete _suitcaseBmp;
108 delete _variaTxt;
109 free(_talkTxt);
110 free(_invTxt);
111 free(_dialogDat);
112 delete _graph;
113 delete _room;
114 //_debugger is deleted by Engine
115
116 if (_cursor2 != nullptr) {
117 _cursor2->free();
118 delete _cursor2;
119 }
120
121 for (uint i = 0; i < _objList.size(); i++) {
122 delete _objList[i];
123 }
124 _objList.clear();
125
126 free(_objSlot);
127
128 for (uint32 i = 0; i < _pscrList.size(); i++) {
129 delete _pscrList[i];
130 }
131 _pscrList.clear();
132
133 for (uint i = 0; i < _maskList.size(); i++) {
134 free(_maskList[i]._data);
135 }
136 _maskList.clear();
137
138 _drawNodeList.clear();
139
140 clearBackAnimList();
141 _backAnimList.clear();
142
143 freeAllNormAnims();
144 _normAnimList.clear();
145
146 for (uint i = 0; i < _allInvList.size(); i++) {
147 _allInvList[i]._surface->free();
148 delete _allInvList[i]._surface;
149 }
150 _allInvList.clear();
151
152 _optionsPic->free();
153 delete _optionsPic;
154
155 _optionsPicInInventory->free();
156 delete _optionsPicInInventory;
157
158 for (uint i = 0; i < _mainHero->_moveSet.size(); i++) {
159 delete _mainHero->_moveSet[i];
160 }
161
162 for (uint i = 0; i < _secondHero->_moveSet.size(); i++) {
163 delete _secondHero->_moveSet[i];
164 }
165
166 delete _mainHero;
167 delete _secondHero;
168
169 free(_roomPathBitmap);
170 free(_roomPathBitmapTemp);
171 free(_coordsBuf);
172
173 _mobPriorityList.clear();
174
175 freeAllSamples();
176
177 free(_zoomBitmap);
178 free(_shadowBitmap);
179 free(_transTable);
180
181 free(_curveData);
182
183 free(_shadowLine);
184
185 free(_creditsData);
186
187 if (_dialogImage != nullptr) {
188 _dialogImage->free();
189 delete _dialogImage;
190 }
191
192 free(_mobTranslationData);
193 }
194
init()195 void PrinceEngine::init() {
196
197 const Common::FSNode gameDataDir(ConfMan.get("path"));
198
199 debugEngine("Adding all path: %s", gameDataDir.getPath().c_str());
200
201 if (!(getFeatures() & GF_EXTRACTED)) {
202 PtcArchive *all = new PtcArchive();
203 if (!all->open("all/databank.ptc"))
204 error("Can't open all/databank.ptc");
205
206 PtcArchive *voices = new PtcArchive();
207
208 if (!(getFeatures() & GF_NOVOICES)) {
209 if (!voices->open("voices/databank.ptc"))
210 error("Can't open voices/databank.ptc");
211 }
212
213 PtcArchive *sound = new PtcArchive();
214 if (!sound->open("sound/databank.ptc"))
215 error("Can't open sound/databank.ptc");
216
217 SearchMan.addSubDirectoryMatching(gameDataDir, "all");
218
219 // Prefix the archive names, so that "all" doesn't conflict with the
220 // "all" directory, if that happens to be named in all lower case.
221 // It isn't on the CD, but we should try to stay case-insensitive.
222 SearchMan.add("_all", all);
223 SearchMan.add("_voices", voices);
224 SearchMan.add("_sound", sound);
225 } else {
226 SearchMan.addSubDirectoryMatching(gameDataDir, "all");
227 SearchMan.addSubDirectoryMatching(gameDataDir, "voices");
228 SearchMan.addSubDirectoryMatching(gameDataDir, "sound");
229 }
230
231 if (getFeatures() & GF_TRANSLATED) {
232 PtcArchive *translation = new PtcArchive();
233 if (getFeatures() & GF_TRANSLATED) {
234 if (!translation->openTranslation("prince_translation.dat"))
235 error("Can't open prince_translation.dat");
236 }
237
238 SearchMan.add("translation", translation);
239 }
240
241 _graph = new GraphicsMan(this);
242
243 _rnd = new Common::RandomSource("prince");
244
245 _midiPlayer = new MusicPlayer(this);
246
247 if (getLanguage() == Common::DE_DEU) {
248 _font = new Font();
249 Resource::loadResource(_font, "font3.raw", true);
250 } else {
251 _font = new Font();
252 Resource::loadResource(_font, "font1.raw", true);
253 }
254
255 _suitcaseBmp = new MhwanhDecoder();
256 Resource::loadResource(_suitcaseBmp, "walizka", true);
257
258 _script = new Script(this);
259 Resource::loadResource(_script, "skrypt.dat", true);
260
261 _flags = new InterpreterFlags();
262 _interpreter = new Interpreter(this, _script, _flags);
263
264 _debugger = new Debugger(this, _flags);
265 setDebugger(_debugger);
266
267 _variaTxt = new VariaTxt();
268 if (getFeatures() & GF_TRANSLATED) {
269 Resource::loadResource(_variaTxt, "variatxt_translate.dat", true);
270 } else {
271 Resource::loadResource(_variaTxt, "variatxt.dat", true);
272 }
273
274 _cursor1 = new Cursor();
275 Resource::loadResource(_cursor1, "mouse1.cur", true);
276
277 _cursor3 = new Cursor();
278 Resource::loadResource(_cursor3, "mouse2.cur", true);
279
280 Common::SeekableReadStream *talkTxtStream;
281 if (getFeatures() & GF_TRANSLATED) {
282 talkTxtStream = SearchMan.createReadStreamForMember("talktxt_translate.dat");
283 } else {
284 talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat");
285 }
286 if (!talkTxtStream) {
287 error("Can't load talkTxtStream");
288 return;
289 }
290 _talkTxtSize = talkTxtStream->size();
291 _talkTxt = (byte *)malloc(_talkTxtSize);
292 talkTxtStream->read(_talkTxt, _talkTxtSize);
293
294 delete talkTxtStream;
295
296 Common::SeekableReadStream *invTxtStream;
297 if (getFeatures() & GF_TRANSLATED) {
298 invTxtStream = SearchMan.createReadStreamForMember("invtxt_translate.dat");
299 } else {
300 invTxtStream = SearchMan.createReadStreamForMember("invtxt.dat");
301 }
302 if (!invTxtStream) {
303 error("Can't load invTxtStream");
304 return;
305 }
306 _invTxtSize = invTxtStream->size();
307 _invTxt = (byte *)malloc(_invTxtSize);
308 invTxtStream->read(_invTxt, _invTxtSize);
309
310 delete invTxtStream;
311
312 loadAllInv();
313
314 Common::SeekableReadStream *dialogDatStream = SearchMan.createReadStreamForMember("dialog.dat");
315 if (!dialogDatStream) {
316 error("Can't load dialogDatStream");
317 return;
318 }
319
320 dialogDatStream = Resource::getDecompressedStream(dialogDatStream);
321
322 _dialogDatSize = dialogDatStream->size();
323 _dialogDat = (byte *)malloc(_dialogDatSize);
324 dialogDatStream->read(_dialogDat, _dialogDatSize);
325
326 delete dialogDatStream;
327
328 _optionsPic = new Graphics::Surface();
329 _optionsPic->create(_optionsWidth, _optionsHeight, Graphics::PixelFormat::createFormatCLUT8());
330 Common::Rect picRect(0, 0, _optionsWidth, _optionsHeight);
331 _optionsPic->fillRect(picRect, _graph->kShadowColor);
332
333 _optionsPicInInventory = new Graphics::Surface();
334 _optionsPicInInventory->create(_invOptionsWidth, _invOptionsHeight, Graphics::PixelFormat::createFormatCLUT8());
335 Common::Rect invPicRect(0, 0, _invOptionsWidth, _invOptionsHeight);
336 _optionsPicInInventory->fillRect(invPicRect, _graph->kShadowColor);
337
338 _roomBmp = new Image::BitmapDecoder();
339
340 _room = new Room();
341
342 _mainHero = new Hero(this, _graph);
343 _secondHero = new Hero(this, _graph);
344 _secondHero->_maxBoredom = 140;
345 _secondHero->loadAnimSet(3);
346
347 _roomPathBitmap = (byte *)malloc(kPathBitmapLen);
348 _roomPathBitmapTemp = (byte *)malloc(kPathBitmapLen);
349 _coordsBuf = (byte *)malloc(kTracePts * 4);
350 _coords = _coordsBuf;
351 _coordsBufEnd = _coordsBuf + kTracePts * 4 - 4;
352
353 BackgroundAnim tempBackAnim;
354 tempBackAnim._seq._currRelative = 0;
355 for (int i = 0; i < kMaxBackAnims; i++) {
356 _backAnimList.push_back(tempBackAnim);
357 }
358
359 Anim tempAnim;
360 tempAnim._animData = nullptr;
361 tempAnim._shadowData = nullptr;
362 for (int i = 0; i < kMaxNormAnims; i++) {
363 _normAnimList.push_back(tempAnim);
364 }
365
366 _objSlot = (uint16 *)malloc(kMaxObjects * sizeof(uint16));
367 for (int i = 0; i < kMaxObjects; i++) {
368 _objSlot[i] = 0xFF;
369 }
370
371 _zoomBitmap = (byte *)malloc(kZoomBitmapLen);
372 _shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize);
373 _transTable = (byte *)malloc(kTransTableSize);
374
375 _curveData = (int16 *)malloc(2 * kCurveLen * sizeof(int16));
376
377 _shadowLine = (byte *)malloc(kShadowLineArraySize);
378
379 Common::SeekableReadStream *creditsDataStream;
380 if (getFeatures() & GF_TRANSLATED) {
381 creditsDataStream = SearchMan.createReadStreamForMember("credits_translate.dat");
382 } else {
383 creditsDataStream = SearchMan.createReadStreamForMember("credits.dat");
384 }
385 if (!creditsDataStream) {
386 error("Can't load creditsDataStream");
387 return;
388 }
389 _creditsDataSize = creditsDataStream->size();
390 _creditsData = (byte *)malloc(_creditsDataSize);
391 creditsDataStream->read(_creditsData, _creditsDataSize);
392 delete creditsDataStream;
393
394 if (getFeatures() & GF_TRANSLATED) {
395 loadMobTranslationTexts();
396 }
397 }
398
showLogo()399 void PrinceEngine::showLogo() {
400 MhwanhDecoder logo;
401 if (Resource::loadResource(&logo, "logo.raw", true)) {
402 loadSample(0, "LOGO.WAV");
403 playSample(0, 0);
404 _graph->draw(_graph->_frontScreen, logo.getSurface());
405 _graph->change();
406 _graph->update(_graph->_frontScreen);
407 setPalette(logo.getPalette());
408
409 uint32 logoStart = _system->getMillis();
410 while (_system->getMillis() < logoStart + 5000) {
411 Common::Event event;
412 Common::EventManager *eventMan = _system->getEventManager();
413 while (eventMan->pollEvent(event)) {
414 switch (event.type) {
415 case Common::EVENT_KEYDOWN:
416 if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
417 stopSample(0);
418 return;
419 }
420 break;
421 case Common::EVENT_LBUTTONDOWN:
422 stopSample(0);
423 return;
424 default:
425 break;
426 }
427 }
428
429 if (shouldQuit()) {
430 return;
431 }
432 }
433 }
434 }
435
run()436 Common::Error PrinceEngine::run() {
437 syncSoundSettings();
438 int startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
439 init();
440 if (startGameSlot == -1) {
441 playVideo("topware.avi");
442 showLogo();
443 } else {
444 loadLocation(59); // load intro location - easiest way to set everything up
445 loadGame(startGameSlot);
446 }
447 mainLoop();
448 return Common::kNoError;
449 }
450
pauseEngineIntern(bool pause)451 void PrinceEngine::pauseEngineIntern(bool pause) {
452 Engine::pauseEngineIntern(pause);
453 if (pause) {
454 _midiPlayer->pause();
455 } else {
456 _midiPlayer->resume();
457 }
458 }
459
setShadowScale(int32 shadowScale)460 void PrinceEngine::setShadowScale(int32 shadowScale) {
461 shadowScale = 100 - shadowScale;
462 if (!shadowScale) {
463 _shadScaleValue = 10000;
464 } else {
465 _shadScaleValue = 10000 / shadowScale;
466 }
467 }
468
plotShadowLinePoint(int x,int y,int color,void * data)469 void PrinceEngine::plotShadowLinePoint(int x, int y, int color, void *data) {
470 PrinceEngine *vm = (PrinceEngine *)data;
471 WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4], x);
472 WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4 + 2], y);
473 vm->_shadLineLen++;
474 }
475
playNextFLCFrame()476 bool PrinceEngine::playNextFLCFrame() {
477 if (!_flicPlayer.isVideoLoaded())
478 return false;
479
480 const Graphics::Surface *s = _flicPlayer.decodeNextFrame();
481 if (s) {
482 _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, s, 255);
483 _graph->change();
484 _flcFrameSurface = s;
485 } else if (_flicLooped) {
486 _flicPlayer.rewind();
487 playNextFLCFrame();
488 } else if (_flcFrameSurface) {
489 _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, _flcFrameSurface, 255);
490 _graph->change();
491 }
492
493 return true;
494 }
495
loadMobTranslationTexts()496 void PrinceEngine::loadMobTranslationTexts() {
497 Common::SeekableReadStream *mobTranslationStream = SearchMan.createReadStreamForMember("mob_translate.dat");
498 if (!mobTranslationStream) {
499 error("Can't load mob_translate.dat");
500 }
501 _mobTranslationSize = mobTranslationStream->size();
502 _mobTranslationData = (byte *)malloc(_mobTranslationSize);
503 mobTranslationStream->read(_mobTranslationData, _mobTranslationSize);
504 delete mobTranslationStream;
505 }
506
setMobTranslationTexts()507 void PrinceEngine::setMobTranslationTexts() {
508 int locationOffset = READ_UINT16(_mobTranslationData + (_locationNr - 1) * 2);
509 if (locationOffset) {
510 byte *locationText = _mobTranslationData + locationOffset;
511 for (uint i = 0; i < _mobList.size(); i++) {
512 byte c;
513 locationText++;
514 _mobList[i]._name.clear();
515 while ((c = *locationText)) {
516 _mobList[i]._name += c;
517 locationText++;
518 }
519 locationText++;
520 _mobList[i]._examText.clear();
521 c = *locationText;
522 locationText++;
523 if (c) {
524 _mobList[i]._examText += c;
525 do {
526 c = *locationText;
527 _mobList[i]._examText += c;
528 locationText++;
529 } while (c != 255);
530 }
531 }
532 }
533 }
534
keyHandler(Common::Event event)535 void PrinceEngine::keyHandler(Common::Event event) {
536 uint16 nChar = event.kbd.keycode;
537 switch (nChar) {
538 case Common::KEYCODE_F1:
539 if (canLoadGameStateCurrently())
540 scummVMSaveLoadDialog(false);
541 break;
542 case Common::KEYCODE_F2:
543 if (canSaveGameStateCurrently())
544 scummVMSaveLoadDialog(true);
545 break;
546 case Common::KEYCODE_z:
547 if (_flags->getFlagValue(Flags::POWERENABLED)) {
548 _flags->setFlagValue(Flags::MBFLAG, 1);
549 }
550 break;
551 case Common::KEYCODE_x:
552 if (_flags->getFlagValue(Flags::POWERENABLED)) {
553 _flags->setFlagValue(Flags::MBFLAG, 2);
554 }
555 break;
556 case Common::KEYCODE_ESCAPE:
557 _flags->setFlagValue(Flags::ESCAPED2, 1);
558 break;
559 default:
560 break;
561 }
562 }
563
printAt(uint32 slot,uint8 color,char * s,uint16 x,uint16 y)564 void PrinceEngine::printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y) {
565 debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s);
566
567 if (getLanguage() == Common::DE_DEU)
568 correctStringDEU(s);
569
570 Text &text = _textSlots[slot];
571 text._str = s;
572 text._x = x;
573 text._y = y;
574 text._color = color;
575 int lines = calcTextLines(s);
576 text._time = calcTextTime(lines);
577 }
578
calcTextLines(const char * s)579 int PrinceEngine::calcTextLines(const char *s) {
580 int lines = 1;
581 while (*s) {
582 if (*s == '\n') {
583 lines++;
584 }
585 s++;
586 }
587 return lines;
588 }
589
calcTextTime(int numberOfLines)590 int PrinceEngine::calcTextTime(int numberOfLines) {
591 return numberOfLines * 30;
592 }
593
correctStringDEU(char * s)594 void PrinceEngine::correctStringDEU(char *s) {
595 while (*s) {
596 switch (*s) {
597 case '\xc4':
598 *s = '\x83';
599 break;
600 case '\xd6':
601 *s = '\x84';
602 break;
603 case '\xdc':
604 *s = '\x85';
605 break;
606 case '\xdf':
607 *s = '\x7f';
608 break;
609 case '\xe4':
610 *s = '\x80';
611 break;
612 case '\xf6':
613 *s = '\x81';
614 break;
615 case '\xfc':
616 *s = '\x82';
617 break;
618 default:
619 break;
620 }
621 s++;
622 }
623 }
624
getTextWidth(const char * s)625 uint32 PrinceEngine::getTextWidth(const char *s) {
626 uint16 textW = 0;
627 while (*s) {
628 textW += _font->getCharWidth(*s) + _font->getKerningOffset(0, 0);
629 s++;
630 }
631 return textW;
632 }
633
showTexts(Graphics::Surface * screen)634 void PrinceEngine::showTexts(Graphics::Surface *screen) {
635 for (uint32 slot = 0; slot < kMaxTexts; slot++) {
636
637 if (_showInventoryFlag && slot) {
638 // only slot 0 for inventory
639 break;
640 }
641
642 Text& text = _textSlots[slot];
643 if (!text._str && !text._time) {
644 continue;
645 }
646
647 int x = text._x;
648 int y = text._y;
649
650 if (!_showInventoryFlag) {
651 x -= _picWindowX;
652 y -= _picWindowY;
653 }
654
655 Common::Array<Common::String> lines;
656 _font->wordWrapText(text._str, _graph->_frontScreen->w, lines);
657
658 int wideLine = 0;
659 for (uint i = 0; i < lines.size(); i++) {
660 int textLen = getTextWidth(lines[i].c_str());
661 if (textLen > wideLine) {
662 wideLine = textLen;
663 }
664 }
665
666 int leftBorderText = 6;
667 if (x + wideLine / 2 > kNormalWidth - leftBorderText) {
668 x = kNormalWidth - leftBorderText - wideLine / 2;
669 }
670
671 if (x - wideLine / 2 < leftBorderText) {
672 x = leftBorderText + wideLine / 2;
673 }
674
675 int textSkip = 2;
676 for (uint i = 0; i < lines.size(); i++) {
677 int drawX = x - getTextWidth(lines[i].c_str()) / 2;
678 int drawY = y - 10 - (lines.size() - i) * (_font->getFontHeight() - textSkip);
679 if (drawX < 0) {
680 drawX = 0;
681 }
682 if (drawY < 0) {
683 drawY = 0;
684 }
685 _font->drawString(screen, lines[i], drawX, drawY, screen->w, text._color);
686 }
687
688 text._time--;
689 if (!text._time) {
690 text._str = nullptr;
691 }
692 }
693 }
694
pausePrinceEngine(int fps)695 void PrinceEngine::pausePrinceEngine(int fps) {
696 int delay = 1000 / fps - int32(_system->getMillis() - _currentTime);
697 delay = delay < 0 ? 0 : delay;
698 _system->delayMillis(delay);
699 _currentTime = _system->getMillis();
700 }
701
leftMouseButton()702 void PrinceEngine::leftMouseButton() {
703 _flags->setFlagValue(Flags::ESCAPED2, 1); // skip intro animation
704 _flags->setFlagValue(Flags::LMOUSE, 1);
705 if (_flags->getFlagValue(Flags::POWERENABLED)) {
706 _flags->setFlagValue(Flags::MBFLAG, 1);
707 }
708 if (_mouseFlag) {
709 int option = 0;
710 int optionEvent = -1;
711
712 if (_optionsFlag) {
713 if (_optionEnabled < _optionsNumber && _optionEnabled != -1) {
714 option = _optionEnabled;
715 _optionsFlag = 0;
716 } else {
717 return;
718 }
719 } else {
720 _optionsMob = _selectedMob;
721 if (_optionsMob == -1) {
722 walkTo();
723 return;
724 }
725 option = 0;
726 }
727 //do_option
728 if (_currentPointerNumber != 2) {
729 //skip_use_code
730 int optionScriptOffset = _room->getOptionOffset(option);
731 if (optionScriptOffset != 0) {
732 optionEvent = _script->scanMobEvents(_optionsMob, optionScriptOffset);
733 }
734 if (optionEvent == -1) {
735 if (!option) {
736 walkTo();
737 return;
738 } else {
739 optionEvent = _script->getOptionStandardOffset(option);
740 }
741 }
742 } else if (_selectedMode) {
743 //give_item
744 if (_room->_itemGive) {
745 optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemGive, _selectedItem);
746 }
747 if (optionEvent == -1) {
748 //standard_giveitem
749 optionEvent = _script->_scriptInfo.stdGiveItem;
750 }
751 } else {
752 if (_room->_itemUse) {
753 optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemUse, _selectedItem);
754 _flags->setFlagValue(Flags::SELITEM, _selectedItem);
755 }
756 if (optionEvent == -1) {
757 //standard_useitem
758 optionEvent = _script->_scriptInfo.stdUseItem;
759 }
760 }
761 _interpreter->storeNewPC(optionEvent);
762 _flags->setFlagValue(Flags::CURRMOB, _selectedMob);
763 _selectedMob = -1;
764 _optionsMob = -1;
765 } else {
766 if (!_flags->getFlagValue(Flags::POWERENABLED)) {
767 if (!_flags->getFlagValue(Flags::NOCLSTEXT)) {
768 for (int slot = 0; slot < kMaxTexts; slot++) {
769 if (slot != 9) {
770 Text& text = _textSlots[slot];
771 if (!text._str) {
772 continue;
773 }
774 text._str = 0;
775 text._time = 0;
776 }
777 }
778 _mainHero->_talkTime = 0;
779 _secondHero->_talkTime = 0;
780 }
781 }
782 }
783 }
784
rightMouseButton()785 void PrinceEngine::rightMouseButton() {
786 if (_flags->getFlagValue(Flags::POWERENABLED)) {
787 _flags->setFlagValue(Flags::MBFLAG, 2);
788 }
789 if (_mouseFlag && _mouseFlag != 3) {
790 _mainHero->freeOldMove();
791 _secondHero->freeOldMove();
792 _interpreter->storeNewPC(0);
793 if (_currentPointerNumber < 2) {
794 enableOptions(true);
795 } else {
796 _currentPointerNumber = 1;
797 changeCursor(1);
798 }
799 }
800 }
801
createDialogBox(int dialogBoxNr)802 void PrinceEngine::createDialogBox(int dialogBoxNr) {
803 _dialogLines = 0;
804 int amountOfDialogOptions = 0;
805 int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
806
807 byte c;
808 int sentenceNumber;
809 _dialogText = _dialogBoxAddr[dialogBoxNr];
810 byte *dialogText = _dialogText;
811
812 while ((sentenceNumber = *dialogText) != 0xFF) {
813 dialogText++;
814 if (!(dialogDataValue & (1 << sentenceNumber))) {
815 _dialogLines += calcTextLines((const char *)dialogText);
816 amountOfDialogOptions++;
817 }
818 do {
819 c = *dialogText;
820 dialogText++;
821 } while (c);
822 }
823
824 _dialogHeight = _font->getFontHeight() * _dialogLines + _dialogLineSpace * (amountOfDialogOptions + 1);
825 _dialogImage = new Graphics::Surface();
826 _dialogImage->create(_dialogWidth, _dialogHeight, Graphics::PixelFormat::createFormatCLUT8());
827 Common::Rect dBoxRect(0, 0, _dialogWidth, _dialogHeight);
828 _dialogImage->fillRect(dBoxRect, _graph->kShadowColor);
829 }
830
dialogRun()831 void PrinceEngine::dialogRun() {
832
833 _dialogFlag = true;
834
835 while (!shouldQuit()) {
836
837 _interpreter->stepBg();
838 drawScreen();
839
840 int dialogX = (640 - _dialogWidth) / 2;
841 int dialogY = 460 - _dialogHeight;
842 _graph->drawAsShadowSurface(_graph->_frontScreen, dialogX, dialogY, _dialogImage, _graph->_shadowTable50);
843
844 int dialogSkipLeft = 14;
845 int dialogSkipUp = 10;
846
847 int dialogTextX = dialogX + dialogSkipLeft;
848 int dialogTextY = dialogY + dialogSkipUp;
849
850 Common::Point mousePos = _system->getEventManager()->getMousePos();
851
852 byte c;
853 int sentenceNumber;
854 byte *dialogText = _dialogText;
855 byte *dialogCurrentText = nullptr;
856 int dialogSelected = -1;
857 int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
858
859 while ((sentenceNumber = *dialogText) != 0xFF) {
860 dialogText++;
861 int actualColor = _dialogColor1;
862
863 if (!(dialogDataValue & (1 << sentenceNumber))) {
864 if (getLanguage() == Common::DE_DEU) {
865 correctStringDEU((char *)dialogText);
866 }
867 Common::Array<Common::String> lines;
868 _font->wordWrapText((const char *)dialogText, _graph->_frontScreen->w, lines);
869
870 Common::Rect dialogOption(dialogTextX, dialogTextY - dialogSkipUp / 2, dialogX + _dialogWidth - dialogSkipLeft, dialogTextY + lines.size() * _font->getFontHeight() + dialogSkipUp / 2 - 1);
871 if (dialogOption.contains(mousePos)) {
872 actualColor = _dialogColor2;
873 dialogSelected = sentenceNumber;
874 dialogCurrentText = dialogText;
875 }
876
877 for (uint j = 0; j < lines.size(); j++) {
878 _font->drawString(_graph->_frontScreen, lines[j], dialogTextX, dialogTextY, _graph->_frontScreen->w, actualColor);
879 dialogTextY += _font->getFontHeight();
880 }
881 dialogTextY += _dialogLineSpace;
882 }
883 do {
884 c = *dialogText;
885 dialogText++;
886 } while (c);
887 }
888
889 Common::Event event;
890 Common::EventManager *eventMan = _system->getEventManager();
891 while (eventMan->pollEvent(event)) {
892 switch (event.type) {
893 case Common::EVENT_KEYDOWN:
894 keyHandler(event);
895 break;
896 case Common::EVENT_LBUTTONDOWN:
897 if (dialogSelected != -1) {
898 dialogLeftMouseButton(dialogCurrentText, dialogSelected);
899 _dialogFlag = false;
900 }
901 break;
902 default:
903 break;
904 }
905 }
906
907 if (shouldQuit()) {
908 return;
909 }
910
911 if (!_dialogFlag) {
912 break;
913 }
914
915
916 _graph->update(_graph->_frontScreen);
917 pausePrinceEngine();
918 }
919 _dialogImage->free();
920 delete _dialogImage;
921 _dialogImage = nullptr;
922 _dialogFlag = false;
923 }
924
dialogLeftMouseButton(byte * string,int dialogSelected)925 void PrinceEngine::dialogLeftMouseButton(byte *string, int dialogSelected) {
926 _interpreter->setString(string);
927 talkHero(0);
928
929 int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
930 dialogDataValue |= (1u << dialogSelected);
931 WRITE_LE_UINT32(_dialogData, dialogDataValue);
932
933 _flags->setFlagValue(Flags::BOXSEL, dialogSelected + 1);
934 setVoice(0, 28, dialogSelected + 1);
935
936 _flags->setFlagValue(Flags::VOICE_H_LINE, _dialogOptLines[dialogSelected * 4]);
937 _flags->setFlagValue(Flags::VOICE_A_LINE, _dialogOptLines[dialogSelected * 4 + 1]);
938 _flags->setFlagValue(Flags::VOICE_B_LINE, _dialogOptLines[dialogSelected * 4 + 2]);
939
940 _interpreter->setString(_dialogOptAddr[dialogSelected]);
941 }
942
talkHero(int slot)943 void PrinceEngine::talkHero(int slot) {
944 // heroSlot = textSlot (slot 0 or 1)
945 Text &text = _textSlots[slot];
946 int lines = calcTextLines((const char *)_interpreter->getString());
947 int time = lines * 30;
948
949 if (slot == 0) {
950 text._color = 220; // TODO - test this
951 _mainHero->_state = Hero::kHeroStateTalk;
952 _mainHero->_talkTime = time;
953 text._x = _mainHero->_middleX;
954 text._y = _mainHero->_middleY - _mainHero->_scaledFrameYSize;
955 } else {
956 text._color = _flags->getFlagValue(Flags::KOLOR); // TODO - test this
957 _secondHero->_state = Hero::kHeroStateTalk;
958 _secondHero->_talkTime = time;
959 text._x = _secondHero->_middleX;
960 text._y = _secondHero->_middleY - _secondHero->_scaledFrameYSize;
961 }
962 text._time = time;
963 if (getLanguage() == Common::DE_DEU) {
964 correctStringDEU((char *)_interpreter->getString());
965 }
966 text._str = (const char *)_interpreter->getString();
967 _interpreter->increaseString();
968 }
969
getCurve()970 void PrinceEngine::getCurve() {
971 _flags->setFlagValue(Flags::TORX1, _curveData[_curvPos]);
972 _flags->setFlagValue(Flags::TORY1, _curveData[_curvPos + 1]);
973 _curvPos += 2;
974 }
975
makeCurve()976 void PrinceEngine::makeCurve() {
977 _curvPos = 0;
978 int x1 = _flags->getFlagValue(Flags::TORX1);
979 int y1 = _flags->getFlagValue(Flags::TORY1);
980 int x2 = _flags->getFlagValue(Flags::TORX2);
981 int y2 = _flags->getFlagValue(Flags::TORY2);
982
983 for (int i = 0; i < kCurveLen; i++) {
984 int sum1 = x1 * curveValues[i][0];
985 sum1 += (x2 + (x1 - x2) / 2) * curveValues[i][1];
986 sum1 += x2 * curveValues[i][2];
987 sum1 += x2 * curveValues[i][3];
988
989 int sum2 = y1 * curveValues[i][0];
990 sum2 += (y2 - 20) * curveValues[i][1];
991 sum2 += (y2 - 10) * curveValues[i][2];
992 sum2 += y2 * curveValues[i][3];
993
994 _curveData[i * 2] = (sum1 >> 15);
995 _curveData[i * 2 + 1] = (sum2 >> 15);
996 }
997 }
998
mouseWeirdo()999 void PrinceEngine::mouseWeirdo() {
1000 if (_mouseFlag == 3) {
1001 int weirdDir = _randomSource.getRandomNumber(3);
1002 Common::Point mousePos = _system->getEventManager()->getMousePos();
1003 switch (weirdDir) {
1004 case 0:
1005 mousePos.x += kCelStep;
1006 break;
1007 case 1:
1008 mousePos.x -= kCelStep;
1009 break;
1010 case 2:
1011 mousePos.y += kCelStep;
1012 break;
1013 case 3:
1014 mousePos.y -= kCelStep;
1015 break;
1016 default:
1017 break;
1018 }
1019 mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639);
1020 _flags->setFlagValue(Flags::MXFLAG, mousePos.x);
1021 mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170);
1022 _flags->setFlagValue(Flags::MYFLAG, mousePos.y);
1023 _system->warpMouse(mousePos.x, mousePos.y);
1024 }
1025 }
1026
showPower()1027 void PrinceEngine::showPower() {
1028 if (_flags->getFlagValue(Flags::POWERENABLED)) {
1029 int power = _flags->getFlagValue(Flags::POWER);
1030
1031 byte *dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarPosY);
1032 for (int y = 0; y < kPowerBarHeight; y++) {
1033 byte *dst2 = dst;
1034 for (int x = 0; x < kPowerBarWidth; x++, dst2++) {
1035 *dst2 = kPowerBarBackgroundColor;
1036 }
1037 dst += _graph->_frontScreen->pitch;
1038 }
1039
1040 if (power) {
1041 dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarGreenPosY);
1042 for (int y = 0; y < kPowerBarGreenHeight; y++) {
1043 byte *dst2 = dst;
1044 for (int x = 0; x < power + 1; x++, dst2++) {
1045 if (x < 58) {
1046 *dst2 = kPowerBarGreenColor1;
1047 } else {
1048 *dst2 = kPowerBarGreenColor2;
1049 }
1050 }
1051 dst += _graph->_frontScreen->pitch;
1052 }
1053 }
1054
1055 _graph->change();
1056 }
1057 }
1058
scrollCredits()1059 void PrinceEngine::scrollCredits() {
1060 byte *scrollAdress = _creditsData;
1061 while (!shouldQuit()) {
1062 for (int scrollPos = 0; scrollPos > -23; scrollPos--) {
1063 const Graphics::Surface *roomSurface = _roomBmp->getSurface();
1064 if (roomSurface) {
1065 _graph->draw(_graph->_frontScreen, roomSurface);
1066 }
1067 char *s = (char *)scrollAdress;
1068 int drawY = scrollPos;
1069 for (int i = 0; i < 22; i++) {
1070 Common::String line;
1071 char *linePos = s;
1072 while ((*linePos != 13)) {
1073 line += *linePos;
1074 linePos++;
1075 }
1076 if (!line.empty()) {
1077 int drawX = (kNormalWidth - getTextWidth(line.c_str())) / 2;
1078 _font->drawString(_graph->_frontScreen, line, drawX, drawY, _graph->_frontScreen->w, 217);
1079 }
1080
1081 char letter1;
1082 bool gotIt1 = false;
1083 do {
1084 letter1 = *s;
1085 s++;
1086 if (letter1 == 13) {
1087 if (*s == 10) {
1088 s++;
1089 }
1090 if (*s != 35) {
1091 gotIt1 = true;
1092 }
1093 break;
1094 }
1095 } while (letter1 != 35);
1096
1097 if (gotIt1) {
1098 drawY += 23;
1099 } else {
1100 break;
1101 }
1102 }
1103 Common::Event event;
1104 Common::EventManager *eventMan = _system->getEventManager();
1105 while (eventMan->pollEvent(event)) {
1106 if (event.type == Common::EVENT_KEYDOWN) {
1107 if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
1108 blackPalette();
1109 return;
1110 }
1111 }
1112 }
1113 if (shouldQuit()) {
1114 return;
1115 }
1116 _graph->change();
1117 _graph->update(_graph->_frontScreen);
1118 pausePrinceEngine(kFPS * 2);
1119 }
1120 char letter2;
1121 byte *scan2 = scrollAdress;
1122 bool gotIt2 = false;
1123 do {
1124 letter2 = *scan2;
1125 scan2++;
1126 if (letter2 == 13) {
1127 if (*scan2 == 10) {
1128 scan2++;
1129 }
1130 if (*scan2 != 35) {
1131 gotIt2 = true;
1132 }
1133 break;
1134 }
1135 } while (letter2 != 35);
1136 if (gotIt2) {
1137 scrollAdress = scan2;
1138 } else {
1139 break;
1140 }
1141 }
1142 blackPalette();
1143 }
1144
mainLoop()1145 void PrinceEngine::mainLoop() {
1146 changeCursor(0);
1147 _currentTime = _system->getMillis();
1148
1149 while (!shouldQuit()) {
1150 Common::Event event;
1151 Common::EventManager *eventMan = _system->getEventManager();
1152 while (eventMan->pollEvent(event)) {
1153 switch (event.type) {
1154 case Common::EVENT_KEYDOWN:
1155 keyHandler(event);
1156 break;
1157 case Common::EVENT_LBUTTONDOWN:
1158 leftMouseButton();
1159 break;
1160 case Common::EVENT_RBUTTONDOWN:
1161 rightMouseButton();
1162 break;
1163 default:
1164 break;
1165 }
1166 }
1167
1168 if (shouldQuit()) {
1169 return;
1170 }
1171
1172 // for "throw a rock" mini-game
1173 mouseWeirdo();
1174
1175 _interpreter->stepBg();
1176 _interpreter->stepFg();
1177
1178 drawScreen();
1179
1180 _graph->update(_graph->_frontScreen);
1181
1182 openInventoryCheck();
1183
1184 pausePrinceEngine();
1185 }
1186 }
1187
1188 } // End of namespace Prince
1189