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 "gnap/gamesys.h"
24 #include "gnap/fontdata.h"
25 #include "graphics/fontman.h"
26 #include "graphics/font.h"
27 #include "image/bmp.h"
28 
29 namespace Gnap {
30 
testUpdRect(const Common::Rect & updRect)31 void GfxItem::testUpdRect(const Common::Rect &updRect) {
32 	Common::Rect intersectingRect;
33 	if (!_updFlag && _prevFrame._spriteId != -1 &&
34 		_updRectsCount < 20 && intersectRect(intersectingRect, _prevFrame._rect, updRect))
35 		_updRects[_updRectsCount++] = intersectingRect;
36 }
37 
38 // GameSys
39 
GameSys(GnapEngine * vm)40 GameSys::GameSys(GnapEngine *vm) : _vm(vm) {
41 	_newSpriteDrawItemsCount = 0;
42 	_removeSequenceItemsCount = 0;
43 	_removeSpriteDrawItemsCount = 0;
44 	_grabSpriteId = -1;
45 	_grabSpriteChanged = false;
46 	_reqRemoveSequenceItem = false;
47 	_removeSequenceItemSequenceId = -1;
48 	_removeSequenceItemValue = 0;
49 	_gfxItemsCount = 0;
50 	_animationsCount = 0;
51 	_animationsDone = false;
52 	_backgroundImageValue3 = 0;
53 	_backgroundImageValue1 = 0;
54 	_backgroundImageValue4 = 1000;
55 	_backgroundImageValue2 = 1000;
56 	_gameSysClock = 0;
57 	_lastUpdateClock = 0;
58 	_backgroundSurface = nullptr;
59 	_frontSurface = nullptr;
60 	for (int i = 0; i < kMaxAnimations; ++i) {
61 		_animations[i]._sequenceId = -1;
62 		_animations[i]._id = -1;
63 		_animations[i]._status = 0;
64 	}
65 	_removeSequenceItems->_sequenceId = -1;
66 	_removeSequenceItems->_id = -1;
67 	_removeSequenceItems->_forceFrameReset = false;
68 	_removeSpriteDrawItems->_id = -1;
69 	_removeSpriteDrawItems->_surface = nullptr;
70 
71 	_grabSpriteSurface1 = _grabSpriteSurface2 = nullptr;
72 
73 	_screenRect = Common::Rect(0, 0, 800, 600);
74 }
75 
~GameSys()76 GameSys::~GameSys() {
77 	if (_frontSurface)
78 		_frontSurface->free();
79 	delete _frontSurface;
80 }
81 
insertSequence(int sequenceId,int id,int sequenceId2,int id2,int flags,int totalDuration,int16 x,int16 y)82 void GameSys::insertSequence(int sequenceId, int id, int sequenceId2, int id2, int flags, int totalDuration, int16 x, int16 y) {
83 	debugC(kDebugBasic, "GameSys::insertSequence() [%08X, %d] -> [%08X, %d] (%d, %d)", sequenceId, id, sequenceId2, id2, x, y);
84 	Sequence sequence;
85 	SequenceResource *sequenceResource = _vm->_sequenceCache->get(sequenceId);
86 	sequenceResource->_sequenceId = sequenceId;
87 	sequence._sequenceId = sequenceId;
88 	sequence._id = id != -1 ? id : sequenceResource->_defaultId;
89 	sequence._sequenceId2 = sequenceId2 != (int32)0x80000000 ? sequenceId2 : sequenceResource->_sequenceId2;
90 	sequence._id2 = id2 != -1 ? id2 : sequenceResource->_defaultId2;
91 	sequence._flags = flags != -1 ? flags : sequenceResource->_flags;
92 	sequence._totalDuration = totalDuration != -1 ? totalDuration : sequenceResource->_totalDuration;
93 	sequence._x = (x < 10000 && x > -10000) ? x : sequenceResource->_xOffs;
94 	sequence._y = (y < 10000 && y > -10000) ? y : sequenceResource->_yOffs;
95 	_fatSequenceItems.push_back(sequence);
96 }
97 
insertDirtyRect(const Common::Rect & rect)98 void GameSys::insertDirtyRect(const Common::Rect &rect) {
99 	_dirtyRects.push_back(rect);
100 }
101 
removeSequence(int sequenceId,int id,bool resetFl)102 void GameSys::removeSequence(int sequenceId, int id, bool resetFl) {
103 	//WaitForSingleObject(removeSequence2Mutex, INFINITE);
104 	if (_removeSequenceItemsCount < kMaxSequenceItems) {
105 		_removeSequenceItems[_removeSequenceItemsCount]._sequenceId = sequenceId;
106 		_removeSequenceItems[_removeSequenceItemsCount]._id = id;
107 		_removeSequenceItems[_removeSequenceItemsCount]._forceFrameReset = resetFl;
108 		++_removeSequenceItemsCount;
109 		//ResetEvent(removeSequenceItemsEvent);
110 		//ReleaseMutex(removeSequence2Mutex);
111 		//WaitForSingleObject(removeSequenceItemsEvent, INFINITE);
112 	}
113 }
114 
invalidateGrabCursorSprite(int id,Common::Rect & rect,Graphics::Surface * surface1,Graphics::Surface * surface2)115 void GameSys::invalidateGrabCursorSprite(int id, Common::Rect &rect, Graphics::Surface *surface1, Graphics::Surface *surface2) {
116 	//WaitForSingleObject(grabSpriteMutex, INFINITE);
117 	_grabSpriteId = id;
118 	_grabSpriteRect = rect;
119 	_grabSpriteSurface2 = surface2;
120 	_grabSpriteSurface1 = surface1;
121 	//ResetEvent(grabSpriteEvent);
122 	_grabSpriteChanged = true;
123 	//ReleaseMutex(grabSpriteMutex);
124 	//WaitForSingleObject(grabSpriteEvent, INFINITE);
125 }
126 
requestClear2(bool resetFl)127 void GameSys::requestClear2(bool resetFl) {
128 	_fatSequenceItems.clear();
129 	_seqItems.clear();
130 	for (int i = 0; i < _gfxItemsCount; ++i) {
131 		GfxItem *gfxItem = &_gfxItems[i];
132 		gfxItem->_sequenceId = -1;
133 		gfxItem->_animation = nullptr;
134 		if (resetFl) {
135 			gfxItem->_currFrame._duration = 0;
136 			gfxItem->_currFrame._spriteId = -1;
137 			gfxItem->_currFrame._soundId = -1;
138 			gfxItem->_updFlag = true;
139 		} else {
140 			gfxItem->_updFlag = false;
141 		}
142   	}
143 	_lastUpdateClock = 0;
144 	_gameSysClock = 0;
145 }
146 
requestClear1()147 void GameSys::requestClear1() {
148 	_gfxItemsCount = 0;
149 	_fatSequenceItems.clear();
150 	_seqItems.clear();
151 	_lastUpdateClock = 0;
152 	_gameSysClock = 0;
153 }
154 
requestRemoveSequence(int sequenceId,int id)155 void GameSys::requestRemoveSequence(int sequenceId, int id) {
156 	//WaitForSingleObject(removeSequence2Mutex, INFINITE);
157 	_reqRemoveSequenceItem = true;
158 	_removeSequenceItemSequenceId = sequenceId;
159 	_removeSequenceItemValue = id;
160 
161 	handleReqRemoveSequenceItem(); //CHECKME?
162 
163 	//ResetEvent(reqClearEvent);
164 	//ReleaseMutex(removeSequence2Mutex);
165 	//WaitForSingleObject(reqClearEvent, INFINITE);
166 }
167 
waitForUpdate()168 void GameSys::waitForUpdate() {
169 	//ResetEvent(updateEvent);
170 	//WaitForSingleObject(updateEvent, INFINITE);
171 	while ( !_animationsDone) {
172 		_vm->gameUpdateTick();
173 	}
174 }
175 
isSequenceActive(int sequenceId,int id)176 int GameSys::isSequenceActive(int sequenceId, int id) {
177 	for (uint i = 0; i < _seqItems.size(); ++i)
178 		if (_seqItems[i]._sequenceId == sequenceId && _seqItems[i]._id == id)
179 			return true;
180 	return false;
181 }
182 
setBackgroundSurface(Graphics::Surface * surface,int a4,int a5,int a6,int a7)183 void GameSys::setBackgroundSurface(Graphics::Surface *surface, int a4, int a5, int a6, int a7) {
184 	debugC(kDebugBasic, "GameSys::setBackgroundSurface() Setting background image");
185 
186 	_backgroundSurface = surface;
187 	if (!_backgroundSurface) {
188 		return;
189 	}
190 
191 	if (!_frontSurface || _frontSurface->w != surface->w || _frontSurface->h != surface->h) {
192 		debugC(kDebugBasic, "GameSys::setBackgroundSurface() Creating background working surface");
193 		if (_frontSurface)
194 			_frontSurface->free();
195 		delete _frontSurface;
196 		_frontSurface = new Graphics::Surface();
197 		_frontSurface->create(surface->w, surface->h, surface->format);
198 	}
199 
200 	memcpy(_frontSurface->getPixels(), surface->getPixels(), surface->pitch * surface->h);
201 	_vm->_system->copyRectToScreen(_frontSurface->getPixels(), _frontSurface->pitch, 0, 0, _frontSurface->w, _frontSurface->h);
202 
203 	_backgroundImageValue1 = a4;
204 	_backgroundImageValue3 = a6;
205 	_backgroundImageValue2 = a5;
206 	_backgroundImageValue4 = a7;
207 	_lastUpdateClock = 0;
208 	_gameSysClock = 0;
209 }
210 
setScaleValues(int a1,int a2,int a3,int a4)211 void GameSys::setScaleValues(int a1, int a2, int a3, int a4) {
212 	_backgroundImageValue1 = a1;
213 	_backgroundImageValue3 = a3;
214 	_backgroundImageValue2 = a2;
215 	_backgroundImageValue4 = a4;
216 }
217 
insertSpriteDrawItem(Graphics::Surface * surface,int x,int y,int id)218 void GameSys::insertSpriteDrawItem(Graphics::Surface *surface, int x, int y, int id) {
219 	if (surface && _newSpriteDrawItemsCount < kMaxSpriteDrawItems) {
220 		_newSpriteDrawItems[_newSpriteDrawItemsCount]._id = id;
221 		_newSpriteDrawItems[_newSpriteDrawItemsCount]._rect = Common::Rect(x, y, x + surface->w, y + surface->h);
222 		_newSpriteDrawItems[_newSpriteDrawItemsCount]._surface = surface;
223 		++_newSpriteDrawItemsCount;
224 	}
225 }
226 
removeSpriteDrawItem(Graphics::Surface * surface,int id)227 void GameSys::removeSpriteDrawItem(Graphics::Surface *surface, int id) {
228 	if (surface && _removeSpriteDrawItemsCount < kMaxSpriteDrawItems) {
229 		_removeSpriteDrawItems[_removeSpriteDrawItemsCount]._id = id;
230 		_removeSpriteDrawItems[_removeSpriteDrawItemsCount]._surface = surface;
231 		++_removeSpriteDrawItemsCount;
232 	}
233 }
234 
drawSpriteToBackground(int x,int y,int resourceId)235 void GameSys::drawSpriteToBackground(int x, int y, int resourceId) {
236 	SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
237 	uint32 *sourcePalette = spriteResource->_palette;
238 	byte *sourcePixels = spriteResource->_pixels;
239 	int spriteWidth = spriteResource->_width;
240 	int spriteHeight = spriteResource->_height;
241 	Common::Rect dstRect(0, 0, spriteWidth, spriteHeight);
242 	blitSprite32(_backgroundSurface, x, y, sourcePixels, spriteResource->_width, dstRect, sourcePalette, spriteResource->_transparent);
243 	_vm->_spriteCache->release(resourceId);
244 
245 	// Add dirty rect so the modified background is redrawn
246 	insertDirtyRect(Common::Rect(x, y, x + spriteWidth, y + spriteHeight));
247 }
248 
allocSurface(int width,int height)249 Graphics::Surface *GameSys::allocSurface(int width, int height) {
250 	Graphics::Surface *surface = new Graphics::Surface();
251 	surface->create(width, height, _backgroundSurface->format);
252 	surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0xFFFFFF00);
253 	return surface;
254 }
255 
createSurface(int resourceId)256 Graphics::Surface *GameSys::createSurface(int resourceId) {
257 	debugC(kDebugBasic, "GameSys::createSurface() resourceId: %08X", resourceId);
258 
259 	SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
260 	Graphics::Surface *surface = allocSurface(spriteResource->_width, spriteResource->_height);
261 	_vm->_spriteCache->release(resourceId);
262 
263 	drawSpriteToSurface(surface, 0, 0, resourceId);
264 
265 	return surface;
266 }
267 
drawSpriteToSurface(Graphics::Surface * surface,int x,int y,int resourceId)268 void GameSys::drawSpriteToSurface(Graphics::Surface *surface, int x, int y, int resourceId) {
269 	SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
270 	uint32 *sourcePalette = spriteResource->_palette;
271 	byte *sourcePixels = spriteResource->_pixels;
272 	Common::Rect dstRect(0, 0, spriteResource->_width, spriteResource->_height);
273 	blitSprite32(surface, x, y, sourcePixels, spriteResource->_width, dstRect, sourcePalette, true);
274 	_vm->_spriteCache->release(resourceId);
275 }
276 
drawTextToSurface(Graphics::Surface * surface,int x,int y,byte r,byte g,byte b,const char * text)277 void GameSys::drawTextToSurface(Graphics::Surface *surface, int x, int y, byte r, byte g, byte b, const char *text) {
278 	bool doDirty = false;
279 
280 	if (!surface) {
281 		surface = _backgroundSurface;
282 		doDirty = true;
283 	}
284 
285 	uint32 color = surface->format.RGBToColor(r, g, b);
286 	if (_vm->_font) {
287 		_vm->_font->drawString(surface, text, x, y, _vm->_font->getStringWidth(text), color);
288 
289 		if (doDirty)
290 			insertDirtyRect(Common::Rect(x, y, x + _vm->_font->getStringWidth(text), y + _vm->_font->getFontHeight()));
291 	} else {
292 		for (const char *cp = text; *cp != 0; ++cp) {
293 			byte c = *cp;
294 			if (c < 32 || c >= 127)
295 				c = (byte)'_';
296 			c -= 32;
297 			int w = _dejaVuSans9ptWidth[c];
298 			const byte *data = _dejaVuSans9ptCharBitmaps + _dejaVuSans9ptOffsets[c];
299 			for (int xc = 0; xc < w; ++xc) {
300 				for (int yc = 15; yc >= 0; --yc) {
301 					byte *dst = (byte *)surface->getBasePtr(x + xc, y + yc);
302 					if (data[1 - (yc >> 3)] & (1 << (yc & 7)))
303 						WRITE_LE_UINT32(dst, color);
304 				}
305 				data += 2;
306 			}
307 			x += w + 1;
308 		}
309 
310 		if (doDirty)
311 			insertDirtyRect(Common::Rect(x, y, x + getTextWidth(text), y + 16));
312 	}
313 }
314 
getTextHeight(const char * text)315 int GameSys::getTextHeight(const char *text) {
316 	byte height = 0;
317 	for (const char *cp = text; *cp != 0; ++cp) {
318 		byte c = *cp;
319 		if (c < 32 || c >= 127)
320 			c = (byte)'_';
321 		c -= 32;
322 		height = MAX(height, _dejaVuSans9ptWidth[c]);
323 	}
324 	return height;
325 }
326 
getTextWidth(const char * text)327 int GameSys::getTextWidth(const char *text) {
328 	int width = 0;
329 	for (const char *cp = text; *cp != 0; ++cp) {
330 		byte c = *cp;
331 		if (c < 32 || c >= 127)
332 			c = (byte)'_';
333 		c -= 32;
334 		width += _dejaVuSans9ptWidth[c] + 1;
335 	}
336 	return width;
337 }
338 
fillSurface(Graphics::Surface * surface,int x,int y,int width,int height,byte r,byte g,byte b)339 void GameSys::fillSurface(Graphics::Surface *surface, int x, int y, int width, int height, byte r, byte g, byte b) {
340 	Common::Rect rect(x, y, x + width, y + height);
341 	if (!surface) {
342 		_backgroundSurface->fillRect(rect, _backgroundSurface->format.RGBToColor(r, g, b));
343 		insertDirtyRect(rect);
344 	} else {
345 		surface->fillRect(rect, surface->format.RGBToColor(r, g, b));
346 	}
347 }
348 
setAnimation(int sequenceId,int id,int animationIndex)349 void GameSys::setAnimation(int sequenceId, int id, int animationIndex) {
350 	if (animationIndex < kMaxAnimations) {
351 		_animations[animationIndex]._sequenceId = sequenceId;
352 		_animations[animationIndex]._id = id;
353 		_animations[animationIndex]._status = 0;
354 	}
355 }
356 
getAnimationStatus(int animationIndex)357 int GameSys::getAnimationStatus(int animationIndex) {
358 	int result = -1;
359 	if (animationIndex < kMaxAnimations)
360 		result = _animations[animationIndex]._status;
361 	return result;
362 }
363 
getSpriteWidthById(int resourceId)364 int GameSys::getSpriteWidthById(int resourceId) {
365 	SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
366 	const int width = spriteResource->_width;
367 	_vm->_spriteCache->release(resourceId);
368 	return width;
369 }
370 
getSpriteHeightById(int resourceId)371 int GameSys::getSpriteHeightById(int resourceId) {
372 	SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
373 	const int height = spriteResource->_height;
374 	_vm->_spriteCache->release(resourceId);
375 	return height;
376 }
377 
loadBitmap(int resourceId)378 Graphics::Surface *GameSys::loadBitmap(int resourceId) {
379 	debugC(kDebugBasic, "GameSys::loadBitmap() resourceId: %08X", resourceId);
380 	if (_vm->_dat->getResourceType(resourceId) != 1)
381 		return nullptr;
382 	byte *resourceData = _vm->_dat->loadResource(resourceId);
383 	uint32 resourceSize = _vm->_dat->getResourceSize(resourceId);
384 	Common::MemoryReadStream stream(resourceData, resourceSize, DisposeAfterUse::NO);
385 	Graphics::Surface *bmpSurface;
386 	Image::BitmapDecoder bmp;
387 	if (!bmp.loadStream(stream))
388 		error("GameSys::loadBitmap() Could not load bitmap resource %08X", resourceId);
389 	bmpSurface = bmp.getSurface()->convertTo(_vm->_system->getScreenFormat());
390 	delete[] resourceData;
391 	return bmpSurface;
392 }
393 
drawBitmap(int resourceId)394 void GameSys::drawBitmap(int resourceId) {
395 	assert(_backgroundSurface);
396 
397 	Graphics::Surface *bmpSurface = loadBitmap(resourceId);
398 	if (!bmpSurface)
399 		error("GameSys::drawBitmap() Error loading the bitmap");
400 
401 	if (bmpSurface->format != _backgroundSurface->format
402 		|| bmpSurface->w != _backgroundSurface->w || bmpSurface->h != _backgroundSurface->h)
403 		error("GameSys::drawBitmap() Different bitmap properties than current background");
404 
405 	byte *src = (byte *)bmpSurface->getPixels();
406 	byte *dst = (byte *)_backgroundSurface->getPixels();
407 	const int pitch = bmpSurface->pitch;
408 	int height = bmpSurface->h;
409 	while (height--) {
410 		memcpy(dst, src, pitch);
411 		src += pitch;
412 		dst += pitch;
413 	}
414 
415 	bmpSurface->free();
416 	delete bmpSurface;
417 
418 	insertDirtyRect(Common::Rect(0, 0, 800, 600));
419 }
420 
seqFind(int sequenceId,int id,int * outIndex)421 Sequence *GameSys::seqFind(int sequenceId, int id, int *outIndex) {
422 	for (uint i = 0; i < _seqItems.size(); ++i)
423 		if (_seqItems[i]._sequenceId == sequenceId && _seqItems[i]._id == id) {
424 			if (outIndex)
425 				*outIndex = i;
426 			return &_seqItems[i];
427 		}
428 	return nullptr;
429 }
430 
seqLocateGfx(int sequenceId,int id,int * outGfxIndex)431 int GameSys::seqLocateGfx(int sequenceId, int id, int *outGfxIndex) {
432 	for (int i = 0; i < _gfxItemsCount; ++i) {
433 		GfxItem *gfxItem = &_gfxItems[i];
434 		if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) {
435 			if (outGfxIndex)
436 				*outGfxIndex = i;
437 			return gfxItem->_sequenceId;
438 		}
439 		if (gfxItem->_id > id) {
440 			if (outGfxIndex)
441 				*outGfxIndex = i;
442 			return 0;
443 		}
444 	}
445 	if (outGfxIndex)
446 		*outGfxIndex = _gfxItemsCount;
447 	return 0;
448 }
449 
seqInsertGfx(int index,int duration)450 void GameSys::seqInsertGfx(int index, int duration) {
451 	Sequence *seqItem = &_seqItems[index];
452 	SequenceResource *sequenceResource = _vm->_sequenceCache->get(seqItem->_sequenceId);
453 
454 	if (sequenceResource->_animationsCount > 50 - _gfxItemsCount)
455 		return;
456 
457 	int gfxIndex;
458 	seqLocateGfx(seqItem->_sequenceId, seqItem->_id, &gfxIndex);
459 
460 	if (gfxIndex != _gfxItemsCount)
461 		memmove(&_gfxItems[gfxIndex + sequenceResource->_animationsCount],	&_gfxItems[gfxIndex], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex));
462 	_gfxItemsCount += sequenceResource->_animationsCount;
463 
464 	for (int i = 0; i < sequenceResource->_animationsCount; ++i) {
465 		GfxItem *gfxItem = &_gfxItems[i + gfxIndex];
466 		SequenceAnimation *animation = &sequenceResource->_animations[i];
467 
468 		debugC(kDebugBasic, "GameSys::seqInsertGfx() seqItem->sequenceId: %08X", seqItem->_sequenceId);
469 
470 		gfxItem->_sequenceId = seqItem->_sequenceId;
471 		gfxItem->_id = seqItem->_id;
472 		gfxItem->_animation = animation;
473 		gfxItem->_currFrameNum = 0;
474 		gfxItem->_flags = 0;
475 		gfxItem->_delayTicks = seqItem->_totalDuration + animation->_additionalDelay;
476 		gfxItem->_updFlag = false;
477 		gfxItem->_updRectsCount = 0;
478 		gfxItem->_prevFrame._duration = 0;
479 		gfxItem->_prevFrame._spriteId = -1;
480 		gfxItem->_prevFrame._soundId = -1;
481 		int totalDuration = duration;
482 		if ((seqItem->_flags & kSeqUnk) && totalDuration > 0) {
483 			gfxItem->_prevFrame._duration = 1;
484 			if (gfxItem->_delayTicks <= totalDuration)
485 				gfxItem->_delayTicks = 0;
486 			else
487 				gfxItem->_delayTicks -= totalDuration + 1;
488 			gfxItem->_updFlag = false;
489 		} else if (gfxItem->_delayTicks <= totalDuration) {
490 			int j;
491 			totalDuration -= gfxItem->_delayTicks;
492 			gfxItem->_delayTicks = 0;
493 			for (j = gfxItem->_currFrameNum; j < animation->_framesCount && animation->frames[j]._duration <= totalDuration; ++j) {
494 				if (animation->frames[j]._soundId != -1)
495 					_soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | animation->frames[j]._soundId);
496 				totalDuration -= animation->frames[j]._duration;
497 			}
498 			if (animation->_framesCount > j)
499 				gfxItem->_currFrame = animation->frames[j++];
500 			else
501 				gfxItem->_currFrame = animation->frames[j - 1];
502 			if (gfxItem->_currFrame._spriteId != -1 && (seqItem->_x != 0 || seqItem->_y != 0))
503 				gfxItem->_currFrame._rect.translate(seqItem->_x, seqItem->_y);
504 			// Update sprite scaling
505 			if ((seqItem->_flags & kSeqScale) && gfxItem->_currFrame._rect.bottom >= _backgroundImageValue1 && gfxItem->_currFrame._rect.bottom <= _backgroundImageValue3) {
506 				int scaleValue = _backgroundImageValue2	+ (gfxItem->_currFrame._rect.bottom - _backgroundImageValue1) *
507 					(_backgroundImageValue4 - _backgroundImageValue2) /
508 					(_backgroundImageValue3 - _backgroundImageValue1);
509 				gfxItem->_currFrame._rect.top = gfxItem->_currFrame._rect.bottom - scaleValue * (gfxItem->_currFrame._rect.bottom - gfxItem->_currFrame._rect.top) / 1000;
510 				gfxItem->_currFrame._rect.right = scaleValue * (gfxItem->_currFrame._rect.right - gfxItem->_currFrame._rect.left) / 1000 + gfxItem->_currFrame._rect.left;
511 				gfxItem->_currFrame._isScaled = true;
512 			}
513 			gfxItem->_currFrame._duration -= totalDuration;
514 			if (gfxItem->_currFrame._soundId != -1)
515 				_soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | gfxItem->_currFrame._soundId);
516 			gfxItem->_currFrameNum = j;
517 			gfxItem->_updFlag = true;
518 		} else {
519 			gfxItem->_delayTicks -= totalDuration + 1;
520 			gfxItem->_updFlag = false;
521 		}
522 	}
523 
524 	for (int k = 0; k < kMaxAnimations; ++k) {
525 		if (_animations[k]._sequenceId != -1 && _animations[k]._sequenceId == seqItem->_sequenceId && _animations[k]._id == seqItem->_id) {
526 			_animations[k]._status = 1;
527 			break;
528 		}
529 	}
530 }
531 
seqRemoveGfx(int sequenceId,int id)532 void GameSys::seqRemoveGfx(int sequenceId, int id) {
533 	int gfxIndex;
534 	if (seqLocateGfx(sequenceId, id, &gfxIndex)) {
535 		GfxItem *gfxItem = &_gfxItems[gfxIndex];
536 		while (gfxIndex < _gfxItemsCount && gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) {
537 			if (gfxItem->_prevFrame._spriteId == -1) {
538 				--_gfxItemsCount;
539 				if (gfxIndex != _gfxItemsCount)
540 					memmove(&_gfxItems[gfxIndex], &_gfxItems[gfxIndex + 1], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex));
541 			} else {
542 				gfxItem->_sequenceId = -1;
543 				gfxItem->_animation = nullptr;
544 				gfxItem->_currFrame._duration = 0;
545 				gfxItem->_currFrame._spriteId = -1;
546 				gfxItem->_currFrame._soundId = -1;
547 				gfxItem->_updFlag = true;
548 				++gfxIndex;
549 				gfxItem = &_gfxItems[gfxIndex];
550 			}
551 		}
552 	}
553 }
554 
updateSequenceDuration(int sequenceId,int id,int * outDuration)555 bool GameSys::updateSequenceDuration(int sequenceId, int id, int *outDuration) {
556 	bool found = false;
557 	int duration = 0x7FFFFFFF;
558 	*outDuration = 0;
559 	for (int i = 0; i < _gfxItemsCount; ++i) {
560 		GfxItem *gfxItem = &_gfxItems[i];
561 		if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) {
562 			found = true;
563 			SequenceAnimation *animation = gfxItem->_animation;
564 			if (animation) {
565 				if (gfxItem->_currFrameNum < animation->_framesCount)
566 					return false;
567 				if (gfxItem->_updFlag) {
568 					if (gfxItem->_currFrame._duration > 0)
569 						return false;
570 					if (-gfxItem->_currFrame._duration < duration)
571 						duration = -gfxItem->_currFrame._duration;
572 				} else {
573 					if (gfxItem->_prevFrame._duration > 0)
574 						return false;
575 					if (-gfxItem->_prevFrame._duration < duration)
576 						duration = -gfxItem->_prevFrame._duration;
577 				}
578 			}
579 		}
580 	}
581 
582 	if (found)
583 		*outDuration = duration;
584 
585 	return found;
586 }
587 
updateAnimationsStatus(int sequenceId,int id)588 void GameSys::updateAnimationsStatus(int sequenceId, int id) {
589 	Animation *foundAnimation = nullptr;
590 	for (int animationIndex = 0; animationIndex < kMaxAnimations; ++animationIndex) {
591 		Animation *animation = &_animations[animationIndex];
592 		if (animation->_sequenceId != -1 && animation->_sequenceId == sequenceId && animation->_id == id) {
593 			foundAnimation = animation;
594 			break;
595 		}
596 	}
597 
598 	if (!foundAnimation)
599 		return;
600 
601 	bool foundSequence = false;
602 	for (int i = 0; i < _gfxItemsCount; ++i) {
603 		GfxItem *gfxItem = &_gfxItems[i];
604 		SequenceAnimation *animation = gfxItem->_animation;
605 		if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id && animation) {
606 			foundSequence = true;
607 			if (animation->_framesCount > gfxItem->_currFrameNum ||
608 				(gfxItem->_updFlag && gfxItem->_currFrame._duration > 1) ||
609 				gfxItem->_prevFrame._duration > 1)
610 				foundSequence = false;
611 			break;
612 		}
613 	}
614 
615 	if (foundSequence) {
616 		foundAnimation->_sequenceId = -1;
617 		foundAnimation->_status = 2;
618 	}
619 }
620 
restoreBackgroundRect(const Common::Rect & rect)621 void GameSys::restoreBackgroundRect(const Common::Rect &rect) {
622 	Common::Rect clipRect;
623 	if (!intersectRect(clipRect, rect, _screenRect))
624 		return;
625 	byte *src = (byte *)_backgroundSurface->getBasePtr(clipRect.left, clipRect.top);
626 	byte *dst = (byte *)_frontSurface->getBasePtr(clipRect.left, clipRect.top);
627 	const int bytes = _backgroundSurface->format.bytesPerPixel * clipRect.width();
628 	int height = clipRect.height();
629 	while (height--) {
630 		memcpy(dst, src, bytes);
631 		src += _backgroundSurface->pitch;
632 		dst += _frontSurface->pitch;
633 	}
634 }
635 
blitSurface32(Graphics::Surface * destSurface,int x,int y,Graphics::Surface * sourceSurface,Common::Rect & sourceRect,bool transparent)636 void GameSys::blitSurface32(Graphics::Surface *destSurface, int x, int y, Graphics::Surface *sourceSurface,
637 	Common::Rect &sourceRect, bool transparent) {
638 
639 	const int sourcePitch = sourceSurface->pitch;
640 	byte *dst = (byte *)destSurface->getBasePtr(x, y);
641 	byte *src = (byte *)sourceSurface->getBasePtr(sourceRect.left, sourceRect.top);
642 	int width = sourceRect.width();
643 	int height = sourceRect.height();
644 	while (height--) {
645 		byte *rsrc = src;
646 		byte *rdst = dst;
647 		for (int xc = 0; xc < width; ++xc) {
648 			uint32 pixel = READ_UINT32(rsrc);
649 			if (!transparent || pixel != 0xFFFFFF00)
650 				WRITE_UINT32(rdst, pixel);
651 			rsrc += 4;
652 			rdst += 4;
653 		}
654 		dst += destSurface->pitch;
655 		src += sourcePitch;
656 	}
657 }
658 
blitSprite32(Graphics::Surface * destSurface,int x,int y,byte * sourcePixels,int sourceWidth,Common::Rect & sourceRect,uint32 * sourcePalette,bool transparent)659 void GameSys::blitSprite32(Graphics::Surface *destSurface, int x, int y, byte *sourcePixels,
660 	int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette, bool transparent) {
661 
662 	const int sourcePitch = (sourceWidth + 3) & 0xFFFFFFFC;
663 	byte *dst = (byte *)destSurface->getBasePtr(x, y);
664 	byte *src = sourcePixels + sourceRect.left + sourcePitch * sourceRect.top;
665 	int width = sourceRect.width();
666 	int height = sourceRect.height();
667 	while (height--) {
668 		byte *rdst = dst;
669 		for (int xc = 0; xc < width; ++xc) {
670 			byte srcPixel = src[xc];
671 			if (!transparent || srcPixel) {
672 				uint32 rgb = sourcePalette[srcPixel];
673 				rdst[0] = 0xFF;
674 				rdst[1] = rgb & 0x000000FF;
675 				rdst[2] = (rgb & 0x0000FF00) >> 8;
676 				rdst[3] = (rgb & 0x00FF0000) >> 16;
677 			}
678 			rdst += 4;
679 		}
680 		dst += destSurface->pitch;
681 		src += sourcePitch;
682 	}
683 }
684 
blitSpriteScaled32(Graphics::Surface * destSurface,Common::Rect & frameRect,Common::Rect & destRect,byte * sourcePixels,int sourceWidth,Common::Rect & sourceRect,uint32 * sourcePalette)685 void GameSys::blitSpriteScaled32(Graphics::Surface *destSurface, Common::Rect &frameRect,
686 	Common::Rect &destRect, byte *sourcePixels, int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette) {
687 
688 	if (frameRect.height() <= 0 || frameRect.width() <= 0)
689 		return;
690 
691 	const int ys = ((sourceRect.bottom - sourceRect.top - 1) << 16) / (frameRect.bottom - frameRect.top - 1);
692 	const int xs = ((sourceRect.right - sourceRect.left - 1) << 16) / (frameRect.right - frameRect.left - 1);
693 	const int destPitch = destSurface->pitch;
694 	const int sourcePitch = (sourceWidth + 3) & 0xFFFFFFFC;
695 
696 	if (!frameRect.equals(destRect)) {
697 		byte *dst = (byte *)destSurface->getBasePtr(destRect.left, destRect.top);
698 		byte *src = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left;
699 		const int height = destRect.bottom - destRect.top;
700 		const int width = destRect.right - destRect.left;
701 		int yi = ys * (destRect.top - frameRect.top);
702 		byte *hsrc = src + sourcePitch * ((yi + 0x8000) >> 16);
703 		for (int i = 0; i < height; ++i) {
704 			byte *wdst = dst;
705 			int xi = xs * (destRect.left - frameRect.left);
706 			byte *wsrc = hsrc + ((xi + 0x8000) >> 16);
707 			for (int j = 0; j < width; ++j) {
708 				byte srcPixel = *wsrc;
709 				if (srcPixel) {
710 					uint32 rgb = sourcePalette[srcPixel];
711 					wdst[0] = 0xFF;
712 					wdst[1] = rgb & 0x000000FF;
713 					wdst[2] = (rgb & 0x0000FF00) >> 8;
714 					wdst[3] = (rgb & 0x00FF0000) >> 16;
715 				}
716 				wdst += 4;
717 				xi += xs;
718 				wsrc = hsrc + ((xi + 0x8000) >> 16);
719 			}
720 			dst += destPitch;
721 			yi += ys;
722 			hsrc = src + sourcePitch * ((yi + 0x8000) >> 16);
723 		}
724 	} else {
725 		byte *dst = (byte *)destSurface->getBasePtr(frameRect.left, frameRect.top);
726 		byte *src = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left;
727 		const int height = frameRect.bottom - frameRect.top;
728 		const int width = frameRect.right - frameRect.left;
729 		byte *hsrc = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left;
730 		int yi = 0;
731 		for (int i = 0; i < height; ++i) {
732 			byte *wdst = dst;
733 			byte *wsrc = hsrc;
734 			int xi = 0;
735 			for (int j = 0; j < width; ++j) {
736 				byte srcPixel = *wsrc;
737 				if (srcPixel) {
738 					uint32 rgb = sourcePalette[srcPixel];
739 					wdst[0] = 0xFF;
740 					wdst[1] = rgb & 0x000000FF;
741 					wdst[2] = (rgb & 0x0000FF00) >> 8;
742 					wdst[3] = (rgb & 0x00FF0000) >> 16;
743 				}
744 				wdst += 4;
745 				xi += xs;
746 				wsrc = hsrc + ((xi + 0x8000) >> 16);
747 			}
748 			dst += destPitch;
749 			yi += ys;
750 			hsrc = src + sourcePitch * ((yi + 0x8000) >> 16);
751 		}
752 	}
753 
754 }
755 
seqDrawStaticFrame(Graphics::Surface * surface,SequenceFrame & frame,Common::Rect * subRect)756 void GameSys::seqDrawStaticFrame(Graphics::Surface *surface, SequenceFrame &frame, Common::Rect *subRect) {
757 	debugC(kDebugBasic, "GameSys::seqDrawStaticFrame() rect: (%d, %d, %d, %d)",
758 		frame._rect.left, frame._rect.top, frame._rect.right, frame._rect.bottom);
759 
760 	Common::Rect srcRect = subRect ? *subRect : frame._rect;
761 	Common::Rect clipRect;
762 
763 	if (!intersectRect(clipRect, srcRect, _screenRect)) {
764 		debugC(kDebugBasic, "GameSys::seqDrawStaticFrame() Surface not inside screen");
765 		return;
766 	}
767 
768 	const int x = clipRect.left, y = clipRect.top;
769 
770 	clipRect.translate(-frame._rect.left, -frame._rect.top);
771 
772 	// TODO Save transparent flag somewhere
773 	blitSurface32(_frontSurface, x, y, surface, clipRect, true);
774 }
775 
seqDrawSpriteFrame(SpriteResource * spriteResource,SequenceFrame & frame,Common::Rect * subRect)776 void GameSys::seqDrawSpriteFrame(SpriteResource *spriteResource, SequenceFrame &frame, Common::Rect *subRect) {
777 	debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() spriteId: %04X; rect: (%d, %d, %d, %d)",
778 		frame._spriteId, frame._rect.left, frame._rect.top, frame._rect.right, frame._rect.bottom);
779 
780 	Common::Rect srcRect = subRect ? *subRect : frame._rect;
781 	Common::Rect clipRect;
782 
783 	if (!intersectRect(clipRect, srcRect, _screenRect)) {
784 		debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() Sprite not inside screen");
785 		return;
786 	}
787 
788 	uint32 *sourcePalette = spriteResource->_palette;
789 	byte *sourcePixels = spriteResource->_pixels;
790 
791 	const int x = clipRect.left, y = clipRect.top;
792 
793 	debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() destX: %d; destY: %d; frame.isScaled: %d", x, y, frame._isScaled ? 1 : 0);
794 
795 	// 32bit sprite drawing
796 	if (frame._isScaled) {
797 		Common::Rect sourceRect(0, 0, spriteResource->_width, spriteResource->_height);
798 		blitSpriteScaled32(_frontSurface,	frame._rect, clipRect, sourcePixels, spriteResource->_width, sourceRect, sourcePalette);
799 	} else {
800 		clipRect.translate(-frame._rect.left, -frame._rect.top);
801 		blitSprite32(_frontSurface, x, y, sourcePixels, spriteResource->_width, clipRect, sourcePalette, true);
802 	}
803 }
804 
drawSprites()805 void GameSys::drawSprites() {
806 	debugC(kDebugBasic, "GameSys::drawSprites() _gfxItemsCount: %d", _gfxItemsCount);
807 
808 	// Restore dirty background and collect rects to be redrawn for all sprites
809 	// which aren't marked to be redrawn yet
810 	Common::Rect intersectingRect;
811 	for (uint i = 0; i < _dirtyRects.size(); ++i) {
812 		restoreBackgroundRect(_dirtyRects[i]);
813 		for (int j = 0; j < _gfxItemsCount; ++j)
814 			_gfxItems[j].testUpdRect(_dirtyRects[i]);
815 	}
816 
817 	for (int k = 0; k < _gfxItemsCount; ++k) {
818 		GfxItem *gfxItem2 = &_gfxItems[k];
819 
820 		if (!gfxItem2->_updFlag)
821 			continue;
822 
823 		if (gfxItem2->_prevFrame._spriteId != -1) {
824 			bool transparent = false;
825 			if (gfxItem2->_currFrame._spriteId != -1) {
826 				if (gfxItem2->_flags) {
827 					transparent = true;
828 				} else {
829 					int resourceId = (gfxItem2->_sequenceId & 0xFFFF0000) | gfxItem2->_currFrame._spriteId;
830 					SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
831 					transparent = spriteResource->_transparent;
832 					_vm->_spriteCache->release(resourceId);
833 				}
834 			}
835 			if (gfxItem2->_currFrame._spriteId == -1 || !gfxItem2->_prevFrame._rect.equals(gfxItem2->_currFrame._rect) || !transparent) {
836 				restoreBackgroundRect(gfxItem2->_prevFrame._rect);
837 				for (int l = 0; l < _gfxItemsCount; ++l)
838 					_gfxItems[l].testUpdRect(gfxItem2->_prevFrame._rect);
839 			}
840 		}
841 
842 		if (gfxItem2->_currFrame._spriteId != -1) {
843 			bool transparent = false;
844 			if (gfxItem2->_flags) {
845 				transparent = true;
846 			} else {
847 				int resourceId = (gfxItem2->_sequenceId & 0xFFFF0000) | gfxItem2->_currFrame._spriteId;
848 				SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
849 				transparent = spriteResource->_transparent;
850 				_vm->_spriteCache->release(resourceId);
851 			}
852 			if (gfxItem2->_prevFrame._spriteId == -1 || !gfxItem2->_prevFrame._rect.equals(gfxItem2->_currFrame._rect) || transparent) {
853 				for (int l = k; l < _gfxItemsCount; ++l)
854 					_gfxItems[l].testUpdRect(gfxItem2->_currFrame._rect);
855 			}
856 		}
857 	}
858 
859 	for (int m = 0; m < _gfxItemsCount; ++m) {
860 		GfxItem *gfxItem5 = &_gfxItems[m];
861 
862 		debugC(kDebugBasic, "DrawGfxItem(%d) updFlag: %d; currFrame.spriteId: %04X; updRectsCount: %d; flags: %04X; sequenceId: %08X",
863 			m, gfxItem5->_updFlag, gfxItem5->_currFrame._spriteId, gfxItem5->_updRectsCount, gfxItem5->_flags, gfxItem5->_sequenceId);
864 
865 		if (gfxItem5->_updFlag) {
866 			if (gfxItem5->_currFrame._spriteId != -1) {
867 				if (gfxItem5->_flags) {
868 					seqDrawStaticFrame(gfxItem5->_surface, gfxItem5->_currFrame, nullptr);
869 				} else {
870 					int resourceId = (gfxItem5->_sequenceId & 0xFFFF0000) | gfxItem5->_currFrame._spriteId;
871 					SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
872 					seqDrawSpriteFrame(spriteResource, gfxItem5->_currFrame, nullptr);
873 					_vm->_spriteCache->release(resourceId);
874 				}
875 			}
876 		} else if (gfxItem5->_updRectsCount > 0) {
877 			if (gfxItem5->_flags) {
878 				for (int n = 0; n < gfxItem5->_updRectsCount; ++n)
879 					seqDrawStaticFrame(gfxItem5->_surface, gfxItem5->_prevFrame, &gfxItem5->_updRects[n]);
880 			} else {
881 				int resourceId = (gfxItem5->_sequenceId & 0xFFFF0000) | gfxItem5->_prevFrame._spriteId;
882 				SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId);
883 				for (int n = 0; n < gfxItem5->_updRectsCount; ++n)
884 					seqDrawSpriteFrame(spriteResource, gfxItem5->_prevFrame, &gfxItem5->_updRects[n]);
885 				_vm->_spriteCache->release(resourceId);
886 			}
887 		}
888 	}
889 
890 	debugC(kDebugBasic, "GameSys::drawSprites() OK");
891 }
892 
updateRect(const Common::Rect & r)893 void GameSys::updateRect(const Common::Rect &r) {
894 	debugC(kDebugBasic, "GameSys::updateRect() %d, %d, %d, %d [%d, %d]", r.left, r.top, r.right, r.bottom, r.width(), r.height());
895 	if (r.width() > 0 && r.height() > 0) {
896 		byte *pixels = (byte *)_frontSurface->getBasePtr(r.left, r.top);
897 		_vm->_system->copyRectToScreen(pixels, _frontSurface->pitch, r.left, r.top,
898 			r.width(), r.height());
899 	}
900 }
901 
updateScreen()902 void GameSys::updateScreen() {
903 	debugC(kDebugBasic, "GameSys::updateScreen()");
904 
905 	for (uint i = 0; i < _dirtyRects.size(); ++i)
906 		updateRect(_dirtyRects[i]);
907 
908 	if (_dirtyRects.size() > 0) {
909 		_dirtyRects.clear();
910 		_lastUpdateClock = 0;
911 		_gameSysClock = 0;
912 	}
913 
914 	Common::Rect dstRect, srcRect, rcSrc2;
915 
916 	for (int j = 0; j < _gfxItemsCount; ++j) {
917 
918 		GfxItem *gfxItem = &_gfxItems[j];
919 
920 		if (!gfxItem->_updFlag)
921 			continue;
922 
923 		if (gfxItem->_prevFrame._spriteId == -1 ||
924 			!intersectRect(srcRect, _screenRect, gfxItem->_prevFrame._rect)) {
925 			if (gfxItem->_currFrame._spriteId != -1 && intersectRect(rcSrc2, _screenRect, gfxItem->_currFrame._rect))
926 				updateRect(rcSrc2);
927 		} else if (gfxItem->_currFrame._spriteId != -1 &&
928 			intersectRect(rcSrc2, _screenRect, gfxItem->_currFrame._rect)) {
929 			updateRect(srcRect);
930 			updateRect(rcSrc2);
931 		}
932 		gfxItem->_prevFrame = gfxItem->_currFrame;
933 	}
934 
935 	updateRect(Common::Rect(0, 0, 800, 600));
936 
937 	debugC(kDebugBasic, "GameSys::updateScreen() OK");
938 }
939 
handleReqRemoveSequenceItem()940 void GameSys::handleReqRemoveSequenceItem() {
941 	if (_reqRemoveSequenceItem) {
942 		int gfxIndex2;
943 		_reqRemoveSequenceItem = false;
944 		if (seqFind(_removeSequenceItemSequenceId, _removeSequenceItemValue, &gfxIndex2))
945 			_seqItems.remove_at(gfxIndex2);
946 		if (seqLocateGfx(_removeSequenceItemSequenceId, _removeSequenceItemValue, &gfxIndex2)) {
947 			int gfxIndex2a = gfxIndex2;
948 			for (GfxItem *gfxItem = &_gfxItems[gfxIndex2a];
949 				gfxIndex2a < _gfxItemsCount && gfxItem->_sequenceId == _removeSequenceItemSequenceId && gfxItem->_id == _removeSequenceItemValue;
950 				gfxItem = &_gfxItems[gfxIndex2a])
951 				++gfxIndex2a;
952 			_gfxItemsCount -= gfxIndex2a - gfxIndex2;
953 			if (_gfxItemsCount != gfxIndex2)
954 				memmove(&_gfxItems[gfxIndex2], &_gfxItems[gfxIndex2a], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex2));
955 		}
956 	}
957 }
958 
handleReqRemoveSequenceItems()959 void GameSys::handleReqRemoveSequenceItems() {
960 	if (_removeSequenceItemsCount > 0) {
961 		for (int i = 0; i < _removeSequenceItemsCount; ++i) {
962 			int gfxIndex;
963 			if (seqFind(_removeSequenceItems[i]._sequenceId, _removeSequenceItems[i]._id, &gfxIndex))
964 				_seqItems.remove_at(gfxIndex);
965 			seqLocateGfx(_removeSequenceItems[i]._sequenceId, _removeSequenceItems[i]._id, &gfxIndex);
966 			for (GfxItem *gfxItem = &_gfxItems[gfxIndex];
967 				gfxIndex < _gfxItemsCount && gfxItem->_sequenceId == _removeSequenceItems[i]._sequenceId && gfxItem->_id == _removeSequenceItems[i]._id;
968 				gfxItem = &_gfxItems[gfxIndex]) {
969 				gfxItem->_sequenceId = -1;
970 				gfxItem->_animation = nullptr;
971 				if (_removeSequenceItems[i]._forceFrameReset) {
972 					gfxItem->_currFrame._duration = 0;
973 					gfxItem->_currFrame._spriteId = -1;
974 					gfxItem->_currFrame._soundId = -1;
975 					gfxItem->_updFlag = true;
976 				} else {
977 					gfxItem->_updFlag = false;
978 				}
979 				++gfxIndex;
980 			}
981 		}
982 		_removeSequenceItemsCount = 0;
983 	}
984 }
985 
handleReqRemoveSpriteDrawItems()986 void GameSys::handleReqRemoveSpriteDrawItems() {
987 	if (_removeSpriteDrawItemsCount > 0) {
988 		for (int j = 0; j < _removeSpriteDrawItemsCount; ++j) {
989 			for (int i = 0; i < _gfxItemsCount; ++i) {
990 				GfxItem *gfxItem = &_gfxItems[i];
991 				if (gfxItem->_sequenceId == -1 && !gfxItem->_animation && gfxItem->_flags
992 				 && gfxItem->_id == _removeSpriteDrawItems[j]._id && _removeSpriteDrawItems[j]._surface == gfxItem->_surface) {
993 					gfxItem->_flags = 0;
994 					gfxItem->_currFrame._duration = 0;
995 					gfxItem->_currFrame._spriteId = -1;
996 					gfxItem->_currFrame._soundId = -1;
997 					gfxItem->_updFlag = true;
998 				}
999 			}
1000 		}
1001 		_removeSpriteDrawItemsCount = 0;
1002 	}
1003 }
1004 
fatUpdateFrame()1005 void GameSys::fatUpdateFrame() {
1006 	debugC(kDebugBasic, "GameSys::fatUpdateFrame()");
1007 
1008 	int32 clockDelta = _gameSysClock - _lastUpdateClock;
1009 	_lastUpdateClock = _gameSysClock;
1010 
1011 	debugC(kDebugBasic, "GameSys::fatUpdateFrame() clockDelta: %d", clockDelta);
1012 
1013 	if (clockDelta <= 0)
1014 		return;
1015 
1016 	_animationsDone = true;
1017 
1018 	int duration, currFrameNum;
1019 
1020 	for (int i = 0; i < _gfxItemsCount; ++i) {
1021 		GfxItem *gfxItem = &_gfxItems[i];
1022 		SequenceAnimation *animation = gfxItem->_animation;
1023 		if ((gfxItem->_sequenceId != -1 && animation) || gfxItem->_prevFrame._spriteId != -1 || gfxItem->_prevFrame._duration > 0) {
1024 			if (gfxItem->_sequenceId != -1 && !gfxItem->_updFlag) {
1025 				Sequence *seqItem = seqFind(gfxItem->_sequenceId, gfxItem->_id, nullptr);
1026 				if (!animation) {
1027 					gfxItem->_sequenceId = -1;
1028 					gfxItem->_animation = nullptr;
1029 					gfxItem->_currFrame._duration = 0;
1030 					gfxItem->_currFrame._spriteId = -1;
1031 					gfxItem->_currFrame._soundId = -1;
1032 					gfxItem->_updFlag = true;
1033 				} else if (!seqItem) {
1034 					gfxItem->_animation = nullptr;
1035 					gfxItem->_currFrame._duration = 0;
1036 					gfxItem->_currFrame._spriteId = -1;
1037 					gfxItem->_currFrame._soundId = -1;
1038 					gfxItem->_updFlag = true;
1039 				} else if ((seqItem->_flags & kSeqUnk) && clockDelta > 1) {
1040 					if (gfxItem->_delayTicks < clockDelta) {
1041 						duration = clockDelta - gfxItem->_delayTicks;
1042 						gfxItem->_delayTicks = 0;
1043 						if (gfxItem->_prevFrame._duration <= duration)
1044 							gfxItem->_prevFrame._duration = 1;
1045 						else
1046 							gfxItem->_prevFrame._duration -= duration;
1047 					} else {
1048 						gfxItem->_delayTicks -= clockDelta;
1049 					}
1050 					gfxItem->_updFlag = false;
1051 				} else if (gfxItem->_delayTicks < clockDelta) {
1052 					duration = clockDelta - gfxItem->_delayTicks;
1053 					gfxItem->_delayTicks = 0;
1054 					if (gfxItem->_prevFrame._duration <= duration) {
1055 						bool v20 = false;
1056 						if (gfxItem->_prevFrame._duration > 0) {
1057 							duration -= gfxItem->_prevFrame._duration;
1058 							gfxItem->_prevFrame._duration = -duration;
1059 						} else {
1060 							gfxItem->_prevFrame._duration = 0;
1061 							v20 = true;
1062 						}
1063 						currFrameNum = gfxItem->_currFrameNum;
1064 						if (animation->_framesCount > currFrameNum) {
1065 							while (animation->_framesCount > currFrameNum
1066 								&& animation->frames[currFrameNum]._duration <= duration) {
1067 								if (animation->frames[currFrameNum]._soundId != -1)
1068 									_soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | animation->frames[currFrameNum]._soundId);
1069 								duration -= animation->frames[currFrameNum]._duration;
1070 								++currFrameNum;
1071 							}
1072 							if (animation->_framesCount > currFrameNum)
1073 								gfxItem->_currFrame = animation->frames[currFrameNum++];
1074 							else
1075 								gfxItem->_currFrame = animation->frames[currFrameNum - 1];
1076 							if (gfxItem->_currFrame._spriteId != -1 && (seqItem->_x != 0 || seqItem->_y != 0))
1077 								gfxItem->_currFrame._rect.translate(seqItem->_x, seqItem->_y);
1078 							// Update sprite scaling
1079 							if ((seqItem->_flags & kSeqScale) && gfxItem->_currFrame._rect.bottom >= _backgroundImageValue1 && gfxItem->_currFrame._rect.bottom <= _backgroundImageValue3) {
1080 								int v17 = _backgroundImageValue2 + (gfxItem->_currFrame._rect.bottom - _backgroundImageValue1) *
1081 									(_backgroundImageValue4 - _backgroundImageValue2) /
1082 									(_backgroundImageValue3 - _backgroundImageValue1);
1083 								gfxItem->_currFrame._rect.top = gfxItem->_currFrame._rect.bottom - v17 * (gfxItem->_currFrame._rect.bottom - gfxItem->_currFrame._rect.top) / 1000;
1084 								gfxItem->_currFrame._rect.right = v17 * (gfxItem->_currFrame._rect.right - gfxItem->_currFrame._rect.left) / 1000 + gfxItem->_currFrame._rect.left;
1085 								gfxItem->_currFrame._isScaled = true;
1086 							}
1087 							gfxItem->_currFrame._duration -= duration;
1088 							if (gfxItem->_currFrame._soundId != -1)
1089 								_soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | gfxItem->_currFrame._soundId);
1090 							gfxItem->_currFrameNum = currFrameNum;
1091 							gfxItem->_updFlag = true;
1092 						} else if (v20 && gfxItem->_prevFrame._spriteId == -1) {
1093 							--_gfxItemsCount;
1094 							if (_gfxItemsCount != i)
1095 								memmove(&_gfxItems[i], &_gfxItems[i + 1], sizeof(GfxItem) * (_gfxItemsCount - i));
1096 							--i;
1097 						} else {
1098 							gfxItem->_updFlag = false;
1099 						}
1100 					} else {
1101 						gfxItem->_prevFrame._duration -= duration;
1102 						gfxItem->_updFlag = false;
1103 						_animationsDone = false;
1104 					}
1105 				} else {
1106 					gfxItem->_delayTicks -= clockDelta;
1107 					gfxItem->_updFlag = false;
1108 					_animationsDone = false;
1109 				}
1110 			}
1111 		} else {
1112 			--_gfxItemsCount;
1113 			if (_gfxItemsCount != i)
1114 				memmove(&_gfxItems[i], &_gfxItems[i + 1], sizeof(GfxItem) * (_gfxItemsCount - i));
1115 			--i;
1116 		}
1117 	}
1118 
1119 	if (_newSpriteDrawItemsCount > 0) {
1120 		debugC(kDebugBasic, "_newSpriteDrawItemsCount: %d", _newSpriteDrawItemsCount);
1121 		for (int k = 0; k < _newSpriteDrawItemsCount; ++k) {
1122 			// The original was allowing a buffer overflow.
1123 			// In order to fit in memory, insertIndex + 1 + (_gfxItemsCount - InsertIndex) must be
1124 			// smaller than the size _gfxItems array (50).
1125 			if (_gfxItemsCount + 1 < 50) {
1126 				int insertIndex;
1127 				seqLocateGfx(-1, _newSpriteDrawItems[k]._id, &insertIndex);
1128 				if (_gfxItemsCount != insertIndex)
1129 					memmove(&_gfxItems[insertIndex + 1], &_gfxItems[insertIndex], sizeof(GfxItem) * (_gfxItemsCount - insertIndex));
1130 				++_gfxItemsCount;
1131 				GfxItem *gfxItem = &_gfxItems[insertIndex];
1132 				gfxItem->_sequenceId = -1;
1133 				gfxItem->_id = _newSpriteDrawItems[k]._id;
1134 				gfxItem->_animation = nullptr;
1135 				gfxItem->_currFrameNum = 0;
1136 				gfxItem->_flags = 1;
1137 				gfxItem->_delayTicks = 0;
1138 				gfxItem->_updFlag = true;
1139 				gfxItem->_updRectsCount = 0;
1140 				gfxItem->_surface = _newSpriteDrawItems[k]._surface;
1141 				gfxItem->_prevFrame._duration = 0;
1142 				gfxItem->_prevFrame._spriteId = -1;
1143 				gfxItem->_prevFrame._soundId = -1;
1144 				gfxItem->_currFrame._duration = 0;
1145 				gfxItem->_currFrame._isScaled = false;
1146 				gfxItem->_currFrame._rect = _newSpriteDrawItems[k]._rect;
1147 				gfxItem->_currFrame._spriteId = _newSpriteDrawItems[k]._surface ? (int32)0xCAFEBABE : -1;// TODO
1148 				gfxItem->_currFrame._soundId = -1;
1149 				_animationsDone = false;
1150 			}
1151 		}
1152 		_newSpriteDrawItemsCount = 0;
1153 	}
1154 
1155 	if (_grabSpriteChanged) {
1156 		for (int i = 0; i < _gfxItemsCount; ++i) {
1157 			GfxItem *gfxItem = &_gfxItems[i];
1158 			if (gfxItem->_sequenceId == -1 && !gfxItem->_animation && gfxItem->_flags
1159 			 && gfxItem->_id == _grabSpriteId && gfxItem->_surface == _grabSpriteSurface1) {
1160 				gfxItem->_currFrame._duration = 0;
1161 				gfxItem->_currFrame._isScaled = false;
1162 				gfxItem->_currFrame._rect = _grabSpriteRect;
1163 				gfxItem->_currFrame._spriteId = _grabSpriteSurface2 ? 1 : -1;// TODO
1164 				gfxItem->_currFrame._soundId = -1;
1165 				gfxItem->_updFlag = true;
1166 				gfxItem->_surface = _grabSpriteSurface2;
1167 				_animationsDone = false;
1168 				break;
1169 			}
1170 		}
1171 		_grabSpriteChanged = false;
1172 	}
1173 
1174 	debugC(kDebugBasic, "GameSys::fatUpdateFrame() _fatSequenceItems.size(): %d", _fatSequenceItems.size());
1175 
1176 	for (uint i = 0; i < _fatSequenceItems.size(); ++i) {
1177 		Sequence *seqItem = &_fatSequenceItems[i];
1178 		if (((seqItem->_flags & kSeqSyncWait) || (seqItem->_flags & kSeqSyncExists)) && seqItem->_sequenceId2 != -1) {
1179 			duration = 0;
1180 			if (((seqItem->_flags & kSeqSyncExists) && seqLocateGfx(seqItem->_sequenceId2, seqItem->_id2, nullptr)) ||
1181 				updateSequenceDuration(seqItem->_sequenceId2, seqItem->_id2, &duration)) {
1182 				int index = -1;
1183 				bool found = false;
1184 				if (seqItem->_sequenceId2 == seqItem->_sequenceId	&& seqItem->_id == seqItem->_id2	&&
1185 					seqFind(seqItem->_sequenceId, seqItem->_id, &index)) {
1186 					_seqItems[index] = *seqItem;
1187 					found = true;
1188 				} else if (_seqItems.size() < 50) {
1189 					index = _seqItems.size();
1190 					_seqItems.push_back(*seqItem);
1191 					found = true;
1192 				}
1193 				if (found) {
1194 					_animationsDone = false;
1195 					seqRemoveGfx(seqItem->_sequenceId2, seqItem->_id2);
1196 					seqRemoveGfx(seqItem->_sequenceId, seqItem->_id);
1197 					_fatSequenceItems.remove_at(i);
1198 					--i;
1199 					seqInsertGfx(index, duration);
1200 				}
1201 			}
1202 		} else {
1203 			if (seqItem->_totalDuration < clockDelta) {
1204 				int index;
1205 				bool found = false;
1206 				duration = clockDelta - seqItem->_totalDuration;
1207 				seqItem->_totalDuration = 0;
1208 				if (seqFind(seqItem->_sequenceId, seqItem->_id, &index)) {
1209 					_seqItems[index] = *seqItem;
1210 					found = true;
1211 				} else if (_seqItems.size() < 50) {
1212 					index = _seqItems.size();
1213 					_seqItems.push_back(*seqItem);
1214 					found = true;
1215 				}
1216 				if (found) {
1217 					_animationsDone = false;
1218 					seqRemoveGfx(seqItem->_sequenceId, seqItem->_id);
1219 					_fatSequenceItems.remove_at(i);
1220 					--i;
1221 					seqInsertGfx(index, duration - 1);
1222 				}
1223 			} else {
1224 				seqItem->_totalDuration -= clockDelta;
1225 			}
1226 		}
1227 	}
1228 
1229 	debugC(kDebugBasic, "GameSys::fatUpdateFrame() _seqItems.size(): %d", _seqItems.size());
1230 
1231 	for (uint i = 0; i < _seqItems.size(); ++i) {
1232 		Sequence *seqItem = &_seqItems[i];
1233 		if (seqLocateGfx(seqItem->_sequenceId, seqItem->_id, nullptr)) {
1234 			updateAnimationsStatus(seqItem->_sequenceId, seqItem->_id);
1235 			if (seqItem->_flags & kSeqLoop) {
1236 				int gfxDuration;
1237 				if (updateSequenceDuration(seqItem->_sequenceId, seqItem->_id, &gfxDuration)) {
1238 					seqRemoveGfx(seqItem->_sequenceId, seqItem->_id);
1239 					seqInsertGfx(i, gfxDuration);
1240 				}
1241 				_animationsDone = false;
1242 			}
1243 		} else {
1244 			_seqItems.remove_at(i);
1245 			--i;
1246 		}
1247 	}
1248 }
1249 
fatUpdate()1250 void GameSys::fatUpdate() {
1251 	debugC(kDebugBasic, "GameSys::fatUpdate() _gfxItemsCount: %d", _gfxItemsCount);
1252 
1253 	for (int i = 0; i < _gfxItemsCount; ++i) {
1254 		_gfxItems[i]._updFlag = false;
1255 		_gfxItems[i]._updRectsCount = 0;
1256 	}
1257 
1258 	handleReqRemoveSequenceItem();
1259 	handleReqRemoveSequenceItems();
1260 	handleReqRemoveSpriteDrawItems();
1261 
1262 	fatUpdateFrame();
1263 }
1264 
updatePlaySounds()1265 void GameSys::updatePlaySounds() {
1266 	for (uint i = 0; i < _soundIds.size(); ++i)
1267 		_vm->playSound(_soundIds[i], false);
1268 	_soundIds.clear();
1269 }
1270 
intersectRect(Common::Rect & intersectingRect,const Common::Rect & r1,const Common::Rect & r2)1271 bool intersectRect(Common::Rect &intersectingRect, const Common::Rect &r1, const Common::Rect &r2) {
1272 	if (r1.intersects(r2)) {
1273 		intersectingRect = r1.findIntersectingRect(r2);
1274 		return true;
1275 	} else
1276 		return false;
1277 }
1278 
1279 } // End of namespace Gnap
1280