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/ini-file.h"
24 #include "common/substream.h"
25 #include "common/system.h"
26 
27 #include "graphics/thumbnail.h"
28 
29 #include "petka/petka.h"
30 #include "petka/interfaces/startup.h"
31 #include "petka/interfaces/main.h"
32 #include "petka/interfaces/save_load.h"
33 #include "petka/interfaces/sequence.h"
34 #include "petka/interfaces/panel.h"
35 #include "petka/interfaces/map.h"
36 #include "petka/objects/object_cursor.h"
37 #include "petka/objects/object_case.h"
38 #include "petka/objects/object_star.h"
39 #include "petka/objects/heroes.h"
40 #include "petka/big_dialogue.h"
41 #include "petka/q_system.h"
42 #include "petka/video.h"
43 #include "petka/q_manager.h"
44 #include "petka/flc.h"
45 
46 namespace Petka {
47 
QSystem(PetkaEngine & vm)48 QSystem::QSystem(PetkaEngine &vm)
49 	: _vm(vm), _mainInterface(nullptr), _currInterface(nullptr), _prevInterface(nullptr),
50 	_totalInit(false), _sceneWidth(640), _room(nullptr), _xOffset(0), _reqOffset(0) {}
51 
~QSystem()52 QSystem::~QSystem() {
53 	for (uint i = 0; i < _allObjects.size(); ++i) {
54 		delete _allObjects[i];
55 	}
56 }
57 
init()58 bool QSystem::init() {
59 	Common::ScopedPtr<Common::SeekableReadStream> stream(_vm.openFile("script.dat", true));
60 	if (!stream)
61 		return false;
62 	Common::ScopedPtr<Common::SeekableReadStream> namesStream(_vm.openFile("Names.ini", true));
63 	Common::ScopedPtr<Common::SeekableReadStream> castStream(_vm.openFile("Cast.ini", true));
64 	Common::ScopedPtr<Common::SeekableReadStream> bgsStream(_vm.openFile("BGs.ini", true));
65 
66 	Common::INIFile namesIni;
67 	Common::INIFile castIni;
68 	Common::INIFile bgsIni;
69 
70 	namesIni.allowNonEnglishCharacters();
71 	castIni.allowNonEnglishCharacters();
72 	bgsIni.allowNonEnglishCharacters();
73 
74 	if (namesStream)
75 		namesIni.loadFromStream(*namesStream);
76 	if (castStream)
77 		castIni.loadFromStream(*castStream);
78 	if (bgsStream)
79 		bgsIni.loadFromStream(*bgsStream);
80 
81 	uint32 objsCount = stream->readUint32LE();
82 	uint32 bgsCount = stream->readUint32LE();
83 	_allObjects.reserve(objsCount + bgsCount + 3);
84 	for (uint i = 0; i < objsCount + bgsCount; ++i) {
85 		QMessageObject *obj = nullptr;
86 		if (i == 0) {
87 			obj = new QObjectPetka;
88 		} else if (i == 1) {
89 			obj = new QObjectChapayev;
90 		} else if (i < objsCount) {
91 			obj = new QObject;
92 		} else {
93 			obj = new QObjectBG;
94 		}
95 		obj->readScriptData(*stream);
96 		obj->readInisData(namesIni, castIni, &bgsIni);
97 		_allObjects.push_back(obj);
98 	}
99 
100 	_allObjects.push_back(new QObjectCursor);
101 	_allObjects.push_back(new QObjectCase);
102 	_allObjects.push_back(new QObjectStar);
103 
104 	_mainInterface.reset(new InterfaceMain());
105 	_startupInterface.reset(new InterfaceStartup());
106 	_saveLoadInterface.reset(new InterfaceSaveLoad());
107 	_sequenceInterface.reset(new InterfaceSequence());
108 	_panelInterface.reset(new InterfacePanel());
109 	_mapInterface.reset(new InterfaceMap());
110 
111 	if (_vm.getPart() == 0) {
112 		_prevInterface = _currInterface = _startupInterface.get();
113 	} else {
114 		_prevInterface = _currInterface = _mainInterface.get();
115 	}
116 
117 	_totalInit = true;
118 
119 	addMessageForAllObjects(kTotalInit);
120 	update();
121 
122 	_totalInit = false;
123 
124 	_currInterface->start(0);
125 	return true;
126 }
127 
addMessage(const QMessage & msg)128 void QSystem::addMessage(const QMessage &msg) {
129 	_messages.push_back(msg);
130 }
131 
addMessage(uint16 objId,uint16 opcode,int16 arg1,int16 arg2,int16 arg3,int32 unk,QMessageObject * sender)132 void QSystem::addMessage(uint16 objId, uint16 opcode, int16 arg1, int16 arg2, int16 arg3, int32 unk, QMessageObject *sender) {
133 	_messages.push_back(QMessage(objId, opcode, arg1, arg2, arg3, sender, unk));
134 }
135 
addMessageForAllObjects(uint16 opcode,int16 arg1,int16 arg2,int16 arg3,int32 unk,QMessageObject * sender)136 void QSystem::addMessageForAllObjects(uint16 opcode, int16 arg1, int16 arg2, int16 arg3, int32 unk, QMessageObject *sender) {
137 	for (uint i = 0; i < _allObjects.size(); ++i) {
138 		_messages.push_back(QMessage(_allObjects[i]->_id, opcode, arg1, arg2, arg3, sender, unk));
139 	}
140 }
141 
findObject(int16 id)142 QMessageObject *QSystem::findObject(int16 id) {
143 	for (uint i = 0; i < _allObjects.size(); ++i) {
144 		if (_allObjects[i]->_id == id)
145 			return _allObjects[i];
146 	}
147 	return nullptr;
148 }
149 
findObject(const Common::String & name)150 QMessageObject *QSystem::findObject(const Common::String &name) {
151 	for (uint i = 0; i < _allObjects.size(); ++i) {
152 		if (_allObjects[i]->_name == name)
153 			return _allObjects[i];
154 	}
155 	return nullptr;
156 }
157 
update()158 void QSystem::update() {
159 	for (Common::List<QMessage>::iterator it = _messages.begin(); it != _messages.end();) {
160 		QMessageObject *obj = findObject(it->objId);
161 		if (obj && !obj->_holdMessages) {
162 			obj->processMessage(*it);
163 			it = _messages.erase(it);
164 		} else {
165 			++it;
166 		}
167 	}
168 }
169 
togglePanelInterface()170 void QSystem::togglePanelInterface() {
171 	if (_currInterface != _startupInterface.get() && getStar()->_isActive) {
172 		getCase()->show(0);
173 		if (_currInterface == _panelInterface.get()) {
174 			_currInterface->stop();
175 		} else if (_currInterface == _mainInterface.get()) {
176 			_panelInterface->start(0);
177 		}
178 	}
179 }
180 
toggleMapInterface()181 void QSystem::toggleMapInterface() {
182 	if (_currInterface != _startupInterface.get() && getStar()->_isActive && _room->_showMap) {
183 		getCase()->show(false);
184 		if (_currInterface == _mapInterface.get()) {
185 			_currInterface->stop();
186 		} else if (_currInterface == _mainInterface.get()) {
187 			_currInterface->setText(Common::U32String(), 0, 0);
188 			_mapInterface->start(0);
189 		}
190 	}
191 }
192 
setCursorAction(int action)193 void QSystem::setCursorAction(int action) {
194 	if (getStar()->_isActive && _currInterface == _mainInterface.get()) {
195 		if (action != kActionObjUseChapayev || getChapay()->_isShown) {
196 			getCursor()->setAction(action);
197 
198 			// original bug fix
199 			_mainInterface->onMouseMove(g_system->getEventManager()->getMousePos());
200 		}
201 	}
202 }
203 
readString(Common::ReadStream * s)204 static Common::String readString(Common::ReadStream *s) {
205 	Common::String string;
206 	uint32 len = s->readUint32LE();
207 	char *buffer = (char *)malloc(len);
208 	s->read(buffer, len);
209 	string = Common::String(buffer, len);
210 	free(buffer);
211 	return string;
212 }
213 
writeString(Common::WriteStream * s,const Common::String & string)214 static void writeString(Common::WriteStream *s, const Common::String &string) {
215 	s->writeUint32LE(string.size());
216 	s->write(string.c_str(), string.size());
217 }
218 
load(Common::ReadStream * s)219 void QSystem::load(Common::ReadStream *s) {
220 	uint count = s->readUint32LE();
221 	for (uint i = 0; i < count; ++i) {
222 		QMessageObject *obj = findObject(readString(s));
223 		obj->_holdMessages = s->readUint32LE();
224 		obj->_status = s->readUint32LE();
225 		obj->_resourceId = s->readUint32LE();
226 		/*obj->_z =*/ s->readUint32LE();
227 		obj->_x = s->readUint32LE();
228 		obj->_y = s->readUint32LE();
229 		obj->_isShown = s->readUint32LE();
230 		obj->_isActive = s->readUint32LE();
231 		obj->_animate = s->readUint32LE();
232 	}
233 
234 	uint itemSize = s->readUint32LE();
235 	QObjectCase *objCase = getCase();
236 	objCase->_items.clear();
237 	for (uint i = 0; i < itemSize; ++i) {
238 		objCase->_items.push_back(s->readSint32LE());
239 	}
240 
241 	_room = (QObjectBG *)findObject(readString(s));
242 	if (_room) {
243 		_mainInterface->loadRoom(_room->_id, true);
244 	}
245 
246 	QObjectPetka *petka = getPetka();
247 	QObjectChapayev *chapayev = getChapay();
248 
249 	Common::Point pos;
250 	pos.x = s->readSint32LE();
251 	pos.y = s->readSint32LE();
252 
253 	petka->setPos(pos, false);
254 
255 	_xOffset = CLIP<int>(pos.x - 320, 0, _sceneWidth - 640);
256 
257 	pos.x = s->readSint32LE();
258 	pos.y = s->readSint32LE();
259 
260 	chapayev->setPos(pos, false);
261 
262 	_vm.getBigDialogue()->load(s);
263 
264 	QObjectCursor *cursor = getCursor();
265 	cursor->_resourceId = s->readUint32LE();
266 	cursor->_actionType = s->readUint32LE();
267 	int invObjId = s->readSint32LE();
268 	if (invObjId != -1) {
269 		cursor->_invObj = findObject(invObjId);
270 	} else {
271 		cursor->_invObj = nullptr;
272 	}
273 
274 	int imageId = s->readSint32LE();
275 	if (imageId != -1 && !(imageId % 100)) {
276 		addMessage(petka->_id, kImage, imageId, 1);
277 	}
278 
279 	imageId = s->readSint32LE();
280 	if (imageId != -1 && !(imageId % 100)) {
281 		addMessage(chapayev->_id, kImage, imageId, 1);
282 	}
283 
284 	getStar()->_isActive = true;
285 
286 	_vm.videoSystem()->makeAllDirty();
287 }
288 
save(Common::WriteStream * s)289 void QSystem::save(Common::WriteStream *s) {
290 	s->writeUint32LE(_allObjects.size() - 3);
291 	for (uint i = 0; i < _allObjects.size() - 3; ++i) {
292 		writeString(s, _allObjects[i]->_name);
293 		s->writeUint32LE(_allObjects[i]->_holdMessages);
294 		s->writeUint32LE(_allObjects[i]->_status);
295 		s->writeUint32LE(_allObjects[i]->_resourceId);
296 		s->writeUint32LE(_allObjects[i]->_z);
297 		s->writeUint32LE(_allObjects[i]->_x);
298 		s->writeUint32LE(_allObjects[i]->_y);
299 		s->writeUint32LE(_allObjects[i]->_isShown);
300 		s->writeUint32LE(_allObjects[i]->_isActive);
301 		s->writeUint32LE(_allObjects[i]->_animate);
302 	}
303 
304 	QObjectCase *objCase = getCase();
305 	s->writeUint32LE(objCase->_items.size());
306 	for (uint i = 0; i < objCase->_items.size(); ++i) {
307 		s->writeSint32LE(objCase->_items[i]);
308 	}
309 
310 	writeString(s, _room->_name);
311 
312 	QObjectPetka *petka = getPetka();
313 	QObjectChapayev *chapayev = getChapay();
314 
315 	FlicDecoder *petkaFlc = _vm.resMgr()->getFlic(petka->_resourceId);
316 	FlicDecoder *chapayFlc = _vm.resMgr()->getFlic(chapayev->_resourceId);
317 
318 	s->writeSint32LE(petka->_x - petkaFlc->getCurrentFrame()->w * petka->_k * -0.5);
319 	s->writeSint32LE(petka->_y + petkaFlc->getCurrentFrame()->h * petka->_k);
320 
321 	s->writeSint32LE(chapayev->_x - chapayFlc->getCurrentFrame()->w * chapayev->_k * -0.5);
322 	s->writeSint32LE(chapayev->_y + chapayFlc->getCurrentFrame()->h * chapayev->_k);
323 
324 	_vm.getBigDialogue()->save(s);
325 
326 	QObjectCursor *cursor = getCursor();
327 	s->writeUint32LE(cursor->_resourceId);
328 	s->writeUint32LE(cursor->_actionType);
329 	if (cursor->_invObj) {
330 		s->writeSint32LE(cursor->_invObj->_id);
331 	} else {
332 		s->writeSint32LE(-1);
333 	}
334 
335 	s->writeSint32LE(petka->_imageId);
336 	s->writeSint32LE(chapayev->_imageId);
337 }
338 
getPetka() const339 QObjectPetka *QSystem::getPetka() const {
340 	return (QObjectPetka *)_allObjects[0];
341 }
342 
getChapay() const343 QObjectChapayev *QSystem::getChapay() const {
344 	return (QObjectChapayev *)_allObjects[1];
345 }
346 
getCursor() const347 QObjectCursor *QSystem::getCursor() const {
348 	return (QObjectCursor *)_allObjects[_allObjects.size() - 3];
349 }
350 
getCase() const351 QObjectCase *QSystem::getCase() const {
352 	return (QObjectCase *)_allObjects[_allObjects.size() - 2];
353 }
354 
getStar() const355 QObjectStar *QSystem::getStar() const {
356 	return (QObjectStar *)_allObjects.back();
357 }
358 
onEvent(const Common::Event & event)359 void QSystem::onEvent(const Common::Event &event) {
360 	switch (event.type) {
361 	case Common::EVENT_MOUSEMOVE: {
362 		Common::Point p = event.mouse;
363 		p.x += _xOffset;
364 		_currInterface->onMouseMove(p);
365 		break;
366 	}
367 	case Common::EVENT_LBUTTONDOWN: {
368 		Common::Point p = event.mouse;
369 		p.x += _xOffset;
370 		_currInterface->onLeftButtonDown(p);
371 		break;
372 	}
373 	case Common::EVENT_RBUTTONDOWN: {
374 		Common::Point p = event.mouse;
375 		p.x += _xOffset;
376 		_currInterface->onRightButtonDown(p);
377 		break;
378 	}
379 	case Common::EVENT_KEYDOWN:
380 		switch (event.kbd.keycode) {
381 		case Common::KEYCODE_1:
382 		case Common::KEYCODE_l:
383 			setCursorAction(kActionLook);
384 			break;
385 		case Common::KEYCODE_2:
386 		case Common::KEYCODE_w:
387 			setCursorAction(kActionWalk);
388 			break;
389 		case Common::KEYCODE_3:
390 		case Common::KEYCODE_g:
391 			setCursorAction(kActionTake);
392 			break;
393 		case Common::KEYCODE_4:
394 		case Common::KEYCODE_u:
395 			setCursorAction(kActionUse);
396 			break;
397 		case Common::KEYCODE_5:
398 		case Common::KEYCODE_t:
399 			setCursorAction(kActionTalk);
400 			break;
401 		case Common::KEYCODE_6:
402 		case Common::KEYCODE_c:
403 			setCursorAction(kActionObjUseChapayev);
404 			break;
405 		case Common::KEYCODE_i:
406 			toggleCase();
407 			break;
408 		case Common::KEYCODE_TAB:
409 		case Common::KEYCODE_m:
410 			toggleMapInterface();
411 			break;
412 		case Common::KEYCODE_o:
413 			togglePanelInterface();
414 			break;
415 		case Common::KEYCODE_ESCAPE:
416 			goPrevInterface();
417 			break;
418 		case Common::KEYCODE_F2: {
419 			InterfaceSaveLoad::saveScreen();
420 			startSaveLoad(kSaveMode);
421 			break;
422 		}
423 		case Common::KEYCODE_F3:
424 			startSaveLoad(kLoadMode);
425 			break;
426 		case Common::KEYCODE_r:
427 			if (event.kbd.flags & Common::KBD_ALT) {
428 				_mainInterface->_dialog.fixCursor(); // Buggy in original
429 			}
430 			break;
431 		default:
432 			break;
433 		}
434 	default:
435 		break;
436 	}
437 }
438 
goPrevInterface()439 void QSystem::goPrevInterface() {
440 	getCase()->show(false);
441 	if (_currInterface != _startupInterface.get() && _currInterface != _sequenceInterface.get())
442 		_currInterface->stop();
443 }
444 
toggleCase()445 void QSystem::toggleCase() {
446 	if (_currInterface == _mainInterface.get() && getStar()->_isActive) {
447 		QObjectCase *obj = getCase();
448 		obj->show(obj->_isShown == 0);
449 	}
450 }
451 
startSaveLoad(int id)452 void QSystem::startSaveLoad(int id) {
453 	if (_currInterface == _mainInterface.get() && getStar()->_isActive) {
454 		_saveLoadInterface->start(id);
455 	}
456 }
457 
458 }
459