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/error.h"
25 #include "common/system.h"
26 #include "common/events.h"
27 #include "common/archive.h"
28 #include "common/config-manager.h"
29 #include "common/fs.h"
30 #include "common/str.h"
31
32 #include "trecision/anim.h"
33 #include "trecision/actor.h"
34 #include "trecision/console.h"
35 #include "trecision/defines.h"
36 #include "trecision/dialog.h"
37 #include "trecision/graphics.h"
38 #include "trecision/pathfinding3d.h"
39 #include "trecision/renderer3d.h"
40 #include "trecision/logic.h"
41 #include "trecision/scheduler.h"
42 #include "trecision/sound.h"
43 #include "trecision/trecision.h"
44 #include "trecision/text.h"
45 #include "trecision/video.h"
46
47 namespace Common {
48 class File;
49 }
50
51 namespace Trecision {
52
TrecisionEngine(OSystem * syst,const ADGameDescription * desc)53 TrecisionEngine::TrecisionEngine(OSystem *syst, const ADGameDescription *desc) : Engine(syst), _gameDescription(desc) {
54 const Common::FSNode gameDataDir(ConfMan.get("path"));
55 SearchMan.addSubDirectoryMatching(gameDataDir, "AUTORUN");
56 SearchMan.addSubDirectoryMatching(gameDataDir, "DATA");
57 SearchMan.addSubDirectoryMatching(gameDataDir, "FMV");
58 // Amiga version loads data files from directories
59 if (isAmiga()) {
60 SearchMan.addSubDirectoryMatching(gameDataDir, "NLDATA.CD0");
61 SearchMan.addSubDirectoryMatching(gameDataDir, "NLSPEECH.CD0");
62 SearchMan.addSubDirectoryMatching(gameDataDir, "NLANIM.CDX");
63 }
64
65 _curRoom = 0;
66 _oldRoom = 0;
67
68 _curInventory = 0;
69
70 for (int i = 0; i < 10; ++i)
71 _curScriptFrame[i] = 0;
72
73 _iconBase = 0;
74 _inventoryRefreshStartIcon = 0;
75 _curObj = 1;
76 _inventoryRefreshStartLine = INVENTORY_HIDE;
77 _lightIcon = 0xFF;
78 _inventoryStatus = INV_OFF;
79 _inventoryCounter = INVENTORY_HIDE;
80 _flagInventoryLocked = false;
81 _inventorySpeedIndex = 0;
82 _inventoryScrollTime = 0;
83
84 _fastWalk = false;
85
86 // Use With
87 _useWith[0] = _useWith[1] = 0;
88 _useWithInv[0] = _useWithInv[1] = false;
89
90 // Messages
91 for (int i = 0; i < MAXOBJNAME; ++i)
92 _objName[i] = nullptr;
93
94 for (int i = 0; i < MAXSENTENCE; ++i)
95 _sentence[i] = nullptr;
96
97 for (int i = 0; i < MAXSYSTEXT; ++i)
98 _sysText[i] = nullptr;
99
100 _curMessage = nullptr;
101 _animMgr = nullptr;
102 _dialogMgr = nullptr;
103 _graphicsMgr = nullptr;
104 _logicMgr = nullptr;
105 _soundMgr = nullptr;
106 _renderer = nullptr;
107 _pathFind = nullptr;
108 _textMgr = nullptr;
109 _animTypeMgr = nullptr;
110 _nextRefresh = 0;
111
112 _curKey = Common::KEYCODE_INVALID;
113 _curAscii = 0;
114 _mousePos = Common::Point(0, 0);
115 _mouseMoved = _mouseLeftBtn = _mouseRightBtn = false;
116 _keybInput = false;
117
118 _gamePaused = false;
119
120 _textPtr = nullptr;
121 _lastInv = 0;
122 _lastObj = 0;
123
124 _curStack = 0;
125
126 _flagScriptActive = false;
127
128 _actor = nullptr;
129
130 _flagDialogActive = false;
131 _flagDialogMenuActive = false;
132 _flagSkipTalk = false;
133 _flagPaintCharacter = false;
134 _flagShowCharacter = true;
135 _flagSomeoneSpeaks = false;
136 _flagCharacterSpeak = false;
137 _flagUseWithStarted = false;
138 _flagNoPaintScreen = false;
139 _flagWaitRegen = false;
140
141 for (int i = 0; i < MAXOBJINROOM; ++i) {
142 _objectGraphics[i].buf = nullptr;
143 _objectGraphics[i].mask = nullptr;
144 }
145
146 _curTime = 0;
147 _characterSpeakTime = 0;
148 _pauseStartTime = 0;
149 _textStatus = TEXT_OFF;
150
151 _cx = _cy = 0;
152
153 _textArea = nullptr;
154 Message msg = { MC_IDLE, 0, MP_DEFAULT, 0, 0, 0, 0 };
155 _snake52 = msg;
156 for (int i = 0; i < 50; ++i)
157 _scriptFrame[i].clear();
158
159 _scheduler = nullptr;
160 }
161
~TrecisionEngine()162 TrecisionEngine::~TrecisionEngine() {
163 if (_animMgr)
164 _animMgr->stopAllSmkAnims();
165
166 _dataFile.close();
167 _thumbnail.free();
168
169 delete _animMgr;
170 delete _dialogMgr;
171 delete _graphicsMgr;
172 delete _logicMgr;
173 delete _soundMgr;
174 delete _renderer;
175 delete _pathFind;
176 delete _textMgr;
177 delete _scheduler;
178 delete _animTypeMgr;
179
180 delete _actor;
181 delete[] _textArea;
182
183 for (int i = 0; i < MAXOBJINROOM; ++i) {
184 delete[] _objectGraphics[i].buf;
185 delete[] _objectGraphics[i].mask;
186 }
187 }
188
run()189 Common::Error TrecisionEngine::run() {
190 syncSoundSettings();
191
192 if (!_dataFile.open(this, "nldata.cd0"))
193 error("Error opening nldata.cd0");
194
195 _graphicsMgr = new GraphicsManager(this);
196 if (!_graphicsMgr->init())
197 return Common::kUnsupportedColorMode;
198 _animMgr = new AnimManager(this);
199 _dialogMgr = new DialogManager(this);
200 _logicMgr = new LogicManager(this);
201 _soundMgr = new SoundManager(this);
202 _pathFind = new PathFinding3D(this);
203 _renderer = new Renderer3D(this);
204 _textMgr = new TextManager(this);
205 _scheduler = new Scheduler(this);
206 _animTypeMgr = new AnimTypeManager(this);
207 _actor = new Actor(this);
208
209 setDebugger(new Console(this));
210
211 initMain();
212
213 while (!shouldQuit()) {
214 eventLoop();
215 if (!_flagNoPaintScreen)
216 processTime();
217
218 processMouse();
219 _scheduler->process();
220
221 _animTypeMgr->handler(kAnimTypeBackground);
222
223 processCurrentMessage();
224
225 if (_flagScriptActive)
226 evalScript();
227 }
228
229 if (isDemo())
230 _graphicsMgr->showDemoPic();
231
232 return Common::kNoError;
233 }
234
eventLoop()235 void TrecisionEngine::eventLoop() {
236 Common::Event event;
237 while (g_system->getEventManager()->pollEvent(event)) {
238 switch (event.type) {
239 case Common::EVENT_MOUSEMOVE:
240 _mouseMoved = true;
241 _mousePos = event.mouse;
242 break;
243
244 case Common::EVENT_LBUTTONUP:
245 _mouseLeftBtn = true;
246 break;
247
248 case Common::EVENT_RBUTTONUP:
249 _mouseRightBtn = true;
250 break;
251
252 case Common::EVENT_KEYUP:
253 _curKey = event.kbd.keycode;
254 _curAscii = event.kbd.ascii;
255 switch (event.kbd.keycode) {
256 case Common::KEYCODE_p:
257 if (!_gamePaused && !_keybInput) {
258 _curKey = Common::KEYCODE_INVALID;
259 _gamePaused = true;
260 waitKey();
261 }
262 _gamePaused = false;
263 break;
264
265 case Common::KEYCODE_CAPSLOCK:
266 _fastWalk ^= true;
267 break;
268 default:
269 break;
270 }
271 break;
272
273 default:
274 break;
275 }
276 }
277 g_system->delayMillis(10);
278 g_system->updateScreen();
279 }
280
initMain()281 void TrecisionEngine::initMain() {
282 for (int c = 0; c < MAXOBJ; c++)
283 _obj[c]._position = -1;
284
285 _curRoom = kRoomIntro;
286 _scheduler->init();
287
288 loadAll();
289 processTime();
290
291 // Check if a saved game is to be loaded from the launcher
292 if (ConfMan.hasKey("save_slot"))
293 loadGameState(ConfMan.getInt("save_slot"));
294 else
295 changeRoom(_curRoom);
296 }
297
checkSystem()298 void TrecisionEngine::checkSystem() {
299 eventLoop();
300 }
301
startCharacterAction(uint16 action,uint16 newRoom,uint8 newPos,uint16 textId)302 void TrecisionEngine::startCharacterAction(uint16 action, uint16 newRoom, uint8 newPos, uint16 textId) {
303 _scheduler->initCharacterQueue();
304
305 _flagInventoryLocked = false;
306 if (action > hLAST) {
307 _animMgr->startSmkAnim(action);
308 _animTypeMgr->init(action, _curObj);
309 _graphicsMgr->hideCursor();
310 _flagShowCharacter = false;
311 _scheduler->doEvent(MC_CHARACTER, ME_CHARACTERCONTINUEACTION, MP_DEFAULT, action, newRoom, newPos, _curObj);
312 } else {
313 if ((action == aWALKIN) || (action == aWALKOUT))
314 _curObj = 0;
315 _graphicsMgr->hideCursor();
316 _actor->actorDoAction(action);
317 _pathFind->nextStep();
318 }
319
320 if (textId)
321 _textMgr->characterSayInAction(textId);
322 else
323 _textMgr->clearLastText();
324 }
325
canPlayerInteract()326 bool TrecisionEngine::canPlayerInteract() {
327 return (!_flagSomeoneSpeaks &&
328 !_flagScriptActive &&
329 !_flagDialogActive &&
330 !_flagDialogMenuActive &&
331 (_actor->_curAction < hWALKIN) &&
332 !_flagUseWithStarted &&
333 _flagShowCharacter &&
334 !_animMgr->isActionActive());
335 }
336
setObjectVisible(uint16 objectId,bool visible)337 void TrecisionEngine::setObjectVisible(uint16 objectId, bool visible) {
338 _obj[objectId].setModeStatus(visible);
339 refreshObject(objectId);
340 }
341
refreshObject(uint16 objectId)342 void TrecisionEngine::refreshObject(uint16 objectId) {
343 for (int i = 0; i < MAXOBJINROOM; ++i) {
344 if (!_room[_curRoom]._object[i])
345 return; // reached the end of the list, object not found
346
347 if (objectId == _room[_curRoom]._object[i]) {
348 break; // object found in room objects, continue
349 }
350 }
351
352 if (_obj[objectId].isModeMask() || _obj[objectId].isModeFull()) {
353 SSortTable entry;
354 entry._objectId = objectId;
355 entry._remove = !isObjectVisible(objectId);
356 _sortTable.push_back(entry);
357
358 // Remove previous instances
359 for (Common::List<SSortTable>::iterator it = _sortTableReplay.begin(); it != _sortTableReplay.end(); ++it) {
360 if (it->_objectId == objectId) {
361 _sortTableReplay.erase(it);
362 break;
363 }
364 }
365
366 _sortTableReplay.push_back(entry);
367 }
368 }
369
isObjectVisible(uint16 objectId)370 bool TrecisionEngine::isObjectVisible(uint16 objectId) {
371 return _obj[objectId].isModeStatus();
372 }
373
setObjectAnim(uint16 objectId,uint16 animId)374 void TrecisionEngine::setObjectAnim(uint16 objectId, uint16 animId) {
375 _obj[objectId]._anim = animId;
376 }
377
reEvent()378 void TrecisionEngine::reEvent() {
379 _scheduler->doEvent(_curMessage->_class, _curMessage->_event, _curMessage->_priority, _curMessage->_u16Param1, _curMessage->_u16Param2, _curMessage->_u8Param, _curMessage->_u32Param);
380 }
381
readLoc()382 void TrecisionEngine::readLoc() {
383 _soundMgr->stopAllExceptMusic();
384
385 _graphicsMgr->clearScreenBufferTop();
386
387 Common::String filename;
388 Common::SeekableReadStreamEndian *picFile;
389 if (isAmiga()) {
390 filename = Common::String::format("%s.bm", _room[_curRoom]._baseName);
391 picFile = readEndian(_dataFile.createReadStreamForMember(filename));
392 } else {
393 filename = Common::String::format("%s.cr", _room[_curRoom]._baseName);
394 picFile = readEndian(_dataFile.createReadStreamForCompressedMember(filename));
395 }
396
397 SObject bgInfo;
398 bgInfo.readRect(picFile);
399
400 _graphicsMgr->loadBackground(picFile, bgInfo._rect.width(), bgInfo._rect.height());
401 _sortTable.clear();
402 _sortTableReplay.clear();
403 readObj(picFile);
404
405 _soundMgr->stopAll();
406
407 if (_room[_curRoom]._sounds[0] != 0)
408 _soundMgr->loadRoomSounds();
409
410 Common::String fname = Common::String::format("%s.3d", _room[_curRoom]._baseName);
411 read3D(fname);
412
413 if (_room[_curRoom]._bkgAnim) {
414 _animMgr->startSmkAnim(_room[_curRoom]._bkgAnim);
415 } else
416 _animMgr->smkStop(kSmackerBackground);
417
418 _animTypeMgr->init(_room[_curRoom]._bkgAnim, 0);
419 }
420
redrawRoom()421 void TrecisionEngine::redrawRoom() {
422 const uint16 curDialog = _dialogMgr->getCurDialog();
423 const uint16 curChoice = _dialogMgr->getCurChoice();
424 const uint16 bgAnim = _room[_curRoom]._bkgAnim;
425 static const ElevatorAction elevatorActions[6] = {
426 {dASCENSORE12, 3, a129PARLACOMPUTERESCENDE, kRoom13},
427 {dASCENSORE12, 4, a129PARLACOMPUTERESCENDE, kRoom16},
428 {dASCENSORE13, 17, a139CHIUDONOPORTESU, kRoom12},
429 {dASCENSORE13, 18, a1316CHIUDONOPORTEGIU, kRoom16},
430 {dASCENSORE16, 32, a1616SALECONASCENSORE, kRoom12},
431 {dASCENSORE16, 33, a1616SALECONASCENSORE, kRoom13},
432 };
433
434 _flagShowCharacter = _dialogMgr->showCharacterAfterDialog();
435 _flagPaintCharacter = true;
436 _textStatus = TEXT_OFF;
437
438 for (int i = 0; i < 6; ++i) {
439 if (curDialog == elevatorActions[i].dialog && curChoice == elevatorActions[i].choice) {
440 startCharacterAction(elevatorActions[i].action, elevatorActions[i].newRoom, 20, 0);
441 break;
442 }
443 }
444
445 Common::String filename;
446 Common::SeekableReadStreamEndian *picFile;
447 if (isAmiga()) {
448 filename = Common::String::format("%s.bm", _room[_curRoom]._baseName);
449 picFile = readEndian(_dataFile.createReadStreamForMember(filename));
450 } else {
451 filename = Common::String::format("%s.cr", _room[_curRoom]._baseName);
452 picFile = readEndian(_dataFile.createReadStreamForCompressedMember(filename));
453 }
454
455 SObject bgInfo;
456 bgInfo.readRect(picFile);
457
458 _graphicsMgr->loadBackground(picFile, bgInfo._rect.width(), bgInfo._rect.height());
459 _sortTable.clear();
460 _sortTable = _sortTableReplay;
461
462 if (bgAnim)
463 _animMgr->startSmkAnim(bgAnim);
464
465 if (_curRoom == kRoom4P && curDialog == dF4PI)
466 _animMgr->smkGoto(kSmackerBackground, 21);
467
468 _graphicsMgr->paintScreen(true);
469 }
470
tendIn()471 void TrecisionEngine::tendIn() {
472 _textStatus = TEXT_OFF;
473
474 _flagPaintCharacter = true;
475 _graphicsMgr->paintScreen(true);
476
477 _graphicsMgr->copyToScreen(0, 0, MAXX, MAXY);
478 }
479
480 } // End of namespace Trecision
481