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/system.h"
24 
25 #include "petka/petka.h"
26 #include "petka/q_manager.h"
27 #include "petka/q_system.h"
28 #include "petka/flc.h"
29 #include "petka/video.h"
30 #include "petka/sound.h"
31 #include "petka/objects/heroes.h"
32 #include "petka/interfaces/panel.h"
33 
34 namespace Petka {
35 
QObjectPetka()36 QObjectPetka::QObjectPetka()
37 	: _walk(nullptr) {
38 	_field7C = 1;
39 	_reaction = nullptr;
40 	_heroReaction = nullptr;
41 	_sender = nullptr;
42 	_isWalking = false;
43 	_x = 574;
44 	_y = 444;
45 	_z = 200;
46 	// _surfId  = -5;
47 	_surfH = 0;
48 	_surfW = 0;
49 	_k = 1.0;
50 }
51 
processMessage(const QMessage & arg)52 void QObjectPetka::processMessage(const QMessage &arg) {
53 	QMessage msg = arg;
54 	if (msg.opcode == kImage) {
55 		msg.opcode = kSet;
56 		_imageId = msg.arg1;
57 
58 		_walk.reset(new Walk(_imageId + 10));
59 
60 		QObjectBG *room = g_vm->getQSystem()->_room;
61 		if (room)
62 			_walk->setBackground(g_vm->resMgr()->findResourceName(room->_resourceId));
63 	}
64 	if (msg.opcode == kSaid || msg.opcode == kStand) {
65 		msg.opcode = kSet;
66 		msg.arg1 = _imageId;
67 		msg.arg2 = 1;
68 	}
69 	if (msg.opcode == kSay) {
70 		msg.opcode = kSet;
71 		msg.arg1 = _imageId + 1;
72 		msg.arg2 = 1;
73 	}
74 	if (msg.opcode == kSet || msg.opcode == kPlay) {
75 		_field7C = msg.arg1 == _imageId || msg.opcode == kPlay;
76 	}
77 	if (msg.opcode != kWalk) {
78 		if (msg.opcode == kWalked && _heroReaction) {
79 			QReaction *reaction = _heroReaction;
80 			_heroReaction = nullptr;
81 			_sender->processReaction(reaction);
82 		}
83 		QMessageObject::processMessage(msg);
84 		if (msg.opcode == kSet || msg.opcode == kPlay) {
85 			initSurface();
86 			if (!g_vm->getQSystem()->_totalInit) {
87 				setPos(Common::Point(_x_, _y_), false);
88 			}
89 		}
90 	}
91 }
92 
initSurface()93 void QObjectPetka::initSurface() {
94 	QManager *resMgr = g_vm->resMgr();
95 	FlicDecoder *flc = resMgr->getFlic(_resourceId);
96 	_surfW = flc->getWidth() * _k;
97 	_surfH = flc->getHeight() * _k;
98 }
99 
walk(int x,int y)100 void QObjectPetka::walk(int x, int y) {
101 	Common::Point walkPos(x, y);
102 	if (!_isShown) {
103 		setPos(walkPos, false);
104 		return;
105 	}
106 
107 
108 	Common::Point currPos;
109 	if (_isWalking) {
110 		currPos = _walk->currPos();
111 	} else {
112 		currPos.x = _x_;
113 		currPos.y = _y_;
114 	}
115 
116 
117 	if (currPos.sqrDist(walkPos) >= 25) {
118 		_walk->init(currPos, walkPos);
119 		_destX = x;
120 		_destY = y;
121 		_resourceId = _imageId + _walk->getSpriteId() + 10;
122 		_isWalking = true;
123 		_animate = true;
124 
125 		initSurface();
126 		FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
127 		flc->setFrame(1);
128 
129 		sub_408940();
130 
131 		g_vm->videoSystem()->makeAllDirty();
132 
133 		_field7C = 0;
134 		_time = 0;
135 		_holdMessages = true;
136 	}
137 }
138 
draw()139 void QObjectPetka::draw() {
140 	if (!_isShown || _resourceId == -1) {
141 		return;
142 	}
143 
144 	if (_animate && _startSound) {
145 		if (_sound) {
146 			_sound->play(_loopedSound);
147 			if (_loopedSound) {
148 				_sound = nullptr;
149 			}
150 		}
151 		_startSound = false;
152 	}
153 
154 	FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
155 	if (!flc) {
156 		return;
157 	}
158 	Graphics::Surface *conv = flc->getCurrentFrame()->convertTo(g_system->getScreenFormat(), flc->getPalette());
159 
160 	Common::Rect srcRect(0, 0, conv->w, conv->h);
161 	Common::Rect dstRect(0, 0, _surfW, _surfH);
162 	dstRect.translate(_x - g_vm->getQSystem()->_xOffset, _y);
163 
164 	g_vm->videoSystem()->transBlitFrom(*conv, srcRect, dstRect, flc->getTransColor(conv->format));
165 	conv->free();
166 	delete conv;
167 }
168 
setPos(Common::Point p,bool)169 void QObjectPetka::setPos(Common::Point p, bool) {
170 	QSystem *sys = g_vm->getQSystem();
171 
172 	int xOff = sys->_xOffset;
173 	Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
174 	g_vm->videoSystem()->addDirtyRect(dirty);
175 
176 	p.y = MIN<int16>(p.y, 480);
177 	FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
178 
179 	_k = calcPerspective(p.y);
180 
181 	_surfH = flc->getHeight() * _k;
182 	_surfW = flc->getWidth() * _k;
183 
184 	_x_ = p.x;
185 	_y_ = p.y;
186 
187 	_x = p.x - _surfW / 2;
188 	_y = p.y - _surfH;
189 
190 	recalcOffset();
191 
192 	g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
193 }
194 
calcPerspective(int y)195 double QObjectPetka::calcPerspective(int y) {
196 	QSystem *qsys = g_vm->getQSystem();
197 
198 	y = MIN(y, 480);
199 
200 	const Perspective &pers = qsys->_room->_persp;
201 	double res = (y - pers.y0) * pers.k / (pers.y1 - pers.y0);
202 	if (res < 0.0)
203 		res = 0.0;
204 
205 	if (res + pers.f0 > pers.f1)
206 		return pers.f1;
207 	return res + pers.f0;
208 }
209 
updateWalk()210 void QObjectPetka::updateWalk() {
211 	if (!_isWalking)
212 		return;
213 
214 	int v = _walk->sub_423350();
215 
216 	switch (v) {
217 	case 0: {
218 		_isWalking = false;
219 		setPos(Common::Point(_walk->destX, _walk->destY), false);
220 
221 		QMessage msg(_id, kSet, (uint16) _imageId, 1, 0, nullptr, 0);
222 		if (_heroReaction) {
223 			uint i;
224 			for (i = 0; i < _heroReaction->messages.size(); ++i) {
225 				if (_heroReaction->messages[i].opcode == kGoTo || _heroReaction->messages[i].opcode == kSetSeq) {
226 					_resourceId = _imageId + _walk->getSpriteId() + 10;
227 
228 					FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
229 					flc->setFrame(1);
230 
231 					initSurface();
232 
233 					processMessage(QMessage(_id, kAnimate, 0, 0, 0, nullptr, 0));
234 
235 					_heroReaction->messages.push_back(msg);
236 					_heroReaction->messages.push_back(QMessage(_id, kAnimate, 1, 0, 0, nullptr, 0));
237 					break;
238 				}
239 			}
240 			if (i == _heroReaction->messages.size())
241 				processMessage(msg);
242 		} else {
243 			processMessage(msg);
244 		}
245 		_holdMessages = false;
246 		g_vm->videoSystem()->makeAllDirty();
247 		break;
248 	}
249 	case 1:
250 		sub_408940();
251 		break;
252 	case 2: {
253 		_resourceId = _walk->getSpriteId() + _imageId + 10;
254 
255 		FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
256 		flc->setFrame(1);
257 
258 		_time = flc->getDelay();
259 
260 		initSurface();
261 		g_vm->videoSystem()->makeAllDirty();
262 		break;
263 	}
264 	default:
265 		break;
266 	}
267 }
268 
setReactionAfterWalk(uint index,QReaction * reaction,QMessageObject * sender,bool deleteReaction)269 void QObjectPetka::setReactionAfterWalk(uint index, QReaction *reaction, QMessageObject *sender, bool deleteReaction) {
270 	_heroReaction = nullptr;
271 
272 	stopWalk();
273 
274 	QMessage msg(_id, kWalked, 0, 0, 0, sender, 0);
275 	g_vm->getQSystem()->addMessage(msg);
276 	_heroReaction = new QReaction();
277 	_sender = sender;
278 
279 	for (uint i = index + 1; i < reaction->messages.size(); ++i) {
280 		_heroReaction->messages.push_back(reaction->messages[i]);
281 	}
282 
283 	if (deleteReaction) {
284 		delete reaction;
285 	}
286 
287 }
288 
stopWalk()289 void QObjectPetka::stopWalk() {
290 	_isWalking = false;
291 	_holdMessages = false;
292 
293 	Common::List<QMessage> &list = g_vm->getQSystem()->_messages;
294 	for (Common::List<QMessage>::iterator it = list.begin(); it != list.end(); ++it) {
295 		if (it->opcode == kWalked && it->objId == _id) {
296 			it->objId = -1;
297 		}
298 
299 	}
300 
301 	delete _heroReaction;
302 	_heroReaction = nullptr;
303 
304 	if (!_field7C) {
305 		Common::Point p = _walk->sub_4234B0();
306 
307 		_x = p.x;
308 		_y = p.y;
309 
310 		QMessage msg(_id, kSet, (uint16)_imageId, 1, 0, nullptr, 0);
311 		processMessage(msg);
312 	}
313 }
314 
update(int time)315 void QObjectPetka::update(int time) {
316 	if (!_animate || !_isShown)
317 		return;
318 	if (_isWalking)
319 		_time += time * (g_vm->getQSystem()->_panelInterface->getHeroSpeed() + 50) / 50;
320 	else
321 		_time += time;
322 	FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
323 	if (flc && flc->getFrameCount() != 1) {
324 		if (_sound) {
325 			Common::Rect bounds = flc->getBounds();
326 			_sound->setBalance(bounds.left + bounds.width() / 2 - g_vm->getQSystem()->_xOffset, 640);
327 		}
328 
329 		while (_time >= (int)flc->getDelay()) {
330 			if (_sound && flc->getCurFrame() == 0) {
331 				_startSound = true;
332 			}
333 			flc->setFrame(-1);
334 			if (flc->getCurFrame() == (int32)flc->getFrameCount() - 1) {
335 				g_vm->getQSystem()->addMessage(_id, kEnd, _resourceId, 0, 0, 0, 0);
336 			}
337 			if (flc->getCurFrame() + 1 == (int32)flc->getFrameCount() / 2) {
338 				g_vm->getQSystem()->addMessage(_id, kHalf, _resourceId, 0, 0, 0, 0);
339 			}
340 
341 			if (_field7C && flc->getCurFrame() == 0)
342 				_time = -10000;
343 
344 			updateWalk();
345 			flc = g_vm->resMgr()->getFlic(_resourceId);
346 
347 			_surfH = flc->getHeight() * _k;
348 			_surfW = flc->getWidth() * _k;
349 
350 			_time -= flc->getDelay();
351 
352 			g_vm->videoSystem()->addDirtyRect(Common::Rect(_x, _y, _surfW + _x, _surfH + _y));
353 		}
354 	}
355 }
356 
isInPoint(Common::Point p)357 bool QObjectPetka::isInPoint(Common::Point p) {
358 	if (!_isActive)
359 		return false;
360 	FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
361 	const Graphics::Surface *flcSurface = flc->getCurrentFrame();
362 	Common::Rect bounds(_surfW, _surfH);
363 	Graphics::ManagedSurface s(_surfW, _surfH, flcSurface->format);
364 	s.transBlitFrom(*flcSurface, Common::Rect(0, 0, flcSurface->w, flcSurface->h), bounds);
365 	p.x -= _x;
366 	p.y -= _y;
367 	if (!bounds.contains(p.x, p.y))
368 		return false;
369 	return *(uint16 *)s.getBasePtr(p.x, p.y) != 0;
370 }
371 
updateZ()372 void QObjectPetka::updateZ() {
373 	if (_animate && _isShown && _updateZ) {
374 		FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
375 		if (_isWalking) {
376 			_z = _walk->currPos().y;
377 		} else {
378 			_z = _y + flc->getHeight() * _k;
379 		}
380 	}
381 }
382 
sub_408940()383 void QObjectPetka::sub_408940() {
384 	FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
385 	QSystem *sys = g_vm->getQSystem();
386 
387 	int xOff = sys->_xOffset;
388 	Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
389 	g_vm->videoSystem()->addDirtyRect(dirty);
390 
391 	Common::Point currPos = _walk->currPos();
392 	_k = calcPerspective(currPos.y);
393 	_surfW = flc->getWidth() * _k;
394 	_surfH = flc->getHeight() * _k;
395 
396 	Common::Point p = _walk->sub_4234B0();
397 	_x = p.x;
398 	_y = p.y;
399 
400 	_x_ = currPos.x;
401 	_y_ = currPos.y;
402 
403 	recalcOffset();
404 
405 	g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
406 }
407 
recalcOffset()408 void QObjectPetka::recalcOffset() {
409 	QSystem *sys = g_vm->getQSystem();
410 	int xOff = sys->_xOffset;
411 
412 	if (_x_ < xOff + 160 || _x_ > xOff + 480) {
413 		sys->_reqOffset = _x_ - 320;
414 	}
415 	sys->_reqOffset = CLIP<int>(sys->_reqOffset, 0, sys->_sceneWidth - 640);
416 }
417 
QObjectChapayev()418 QObjectChapayev::QObjectChapayev() {
419 	_x = 477;
420 	_y = 350;
421 	// _surfId = -6;
422 }
423 
424 }
425