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