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/endian.h"
24 #include "common/str.h"
25 #include "graphics/cursorman.h"
26 
27 #include "gob/gob.h"
28 #include "gob/draw.h"
29 #include "gob/global.h"
30 #include "gob/util.h"
31 #include "gob/game.h"
32 #include "gob/resources.h"
33 #include "gob/hotspots.h"
34 #include "gob/scenery.h"
35 #include "gob/inter.h"
36 #include "gob/sound/sound.h"
37 
38 namespace Gob {
39 
Draw_v1(GobEngine * vm)40 Draw_v1::Draw_v1(GobEngine *vm) : Draw(vm) {
41 }
42 
initScreen()43 void Draw_v1::initScreen() {
44 	_backSurface  = _vm->_video->initSurfDesc(320, 200);
45 	_frontSurface = _vm->_global->_primarySurfDesc;
46 
47 	_cursorSprites = _vm->_video->initSurfDesc(_cursorWidth * 2, _cursorHeight, 2);
48 	_scummvmCursor = _vm->_video->initSurfDesc(_cursorWidth    , _cursorHeight, SCUMMVM_CURSOR);
49 }
50 
closeScreen()51 void Draw_v1::closeScreen() {
52 }
53 
blitCursor()54 void Draw_v1::blitCursor() {
55 	if (_cursorIndex == -1)
56 		return;
57 
58 	if (_showCursor == 2)
59 		_showCursor = 0;
60 }
61 
animateCursor(int16 cursor)62 void Draw_v1::animateCursor(int16 cursor) {
63 	int16 cursorIndex = cursor;
64 	int16 newX = 0, newY = 0;
65 	uint16 hotspotX = 0, hotspotY = 0;
66 
67 	_showCursor = 2;
68 
69 	if (cursorIndex == -1) {
70 		cursorIndex =
71 			_vm->_game->_hotspots->findCursor(_vm->_global->_inter_mouseX,
72 			                                  _vm->_global->_inter_mouseY);
73 
74 		if (_cursorAnimLow[cursorIndex] == -1)
75 			cursorIndex = 1;
76 	}
77 
78 	if (_cursorAnimLow[cursorIndex] != -1) {
79 		if (cursorIndex == _cursorIndex) {
80 			if (_cursorAnimDelays[_cursorIndex] != 0 &&
81 			    _cursorAnimDelays[_cursorIndex] * 10 +
82 			    _cursorTimeKey <= _vm->_util->getTimeKey()) {
83 				_cursorAnim++;
84 				_cursorTimeKey = _vm->_util->getTimeKey();
85 			} else {
86 				if (_noInvalidated && (_vm->_global->_inter_mouseX == _cursorX) &&
87 						(_vm->_global->_inter_mouseY == _cursorY)) {
88 					_vm->_video->waitRetrace();
89 					return;
90 				}
91 			}
92 		} else {
93 			_cursorIndex = cursorIndex;
94 			if (_cursorAnimDelays[_cursorIndex] != 0) {
95 				_cursorAnim =
96 				    _cursorAnimLow[_cursorIndex];
97 				_cursorTimeKey = _vm->_util->getTimeKey();
98 			} else {
99 				_cursorAnim = _cursorIndex;
100 			}
101 		}
102 
103 		if (_cursorAnimDelays[_cursorIndex] != 0 &&
104 		    (_cursorAnimHigh[_cursorIndex] < _cursorAnim ||
105 			_cursorAnimLow[_cursorIndex] >
106 			_cursorAnim)) {
107 			_cursorAnim = _cursorAnimLow[_cursorIndex];
108 		}
109 
110 		newX = _vm->_global->_inter_mouseX;
111 		newY = _vm->_global->_inter_mouseY;
112 		if (_cursorHotspotXVar != -1) {
113 			newX -= hotspotX = (uint16) VAR(_cursorIndex + _cursorHotspotXVar);
114 			newY -= hotspotY = (uint16) VAR(_cursorIndex + _cursorHotspotYVar);
115 		} else if (_cursorHotspotX != -1) {
116 			newX -= hotspotX = _cursorHotspotX;
117 			newY -= hotspotY = _cursorHotspotY;
118 		}
119 
120 		_scummvmCursor->clear();
121 		_scummvmCursor->blit(*_cursorSprites,
122 				cursorIndex * _cursorWidth, 0,
123 				(cursorIndex + 1) * _cursorWidth - 1,
124 				_cursorHeight - 1, 0, 0);
125 		CursorMan.replaceCursor(_scummvmCursor->getData(),
126 				_cursorWidth, _cursorHeight, hotspotX, hotspotY, 0, false, &_vm->getPixelFormat());
127 
128 		if (_frontSurface != _backSurface) {
129 			_showCursor = 3;
130 			if (!_noInvalidated) {
131 				int16 tmp = _cursorIndex;
132 				_cursorIndex = -1;
133 				blitInvalidated();
134 				_cursorIndex = tmp;
135 			} else {
136 				_vm->_video->waitRetrace();
137 				if (MIN(newY, _cursorY) < 50)
138 					_vm->_util->delay(5);
139 				_showCursor = 0;
140 			}
141 		}
142 	} else
143 		blitCursor();
144 
145 	_cursorX = newX;
146 	_cursorY = newY;
147 }
148 
printTotText(int16 id)149 void Draw_v1::printTotText(int16 id) {
150 	byte *dataPtr;
151 	byte *ptr, *ptrEnd;
152 	byte cmd;
153 	int16 destX, destY;
154 	int16 val;
155 	int16 savedFlags;
156 	int16 destSpriteX;
157 	int16 spriteRight, spriteBottom;
158 	char buf[20];
159 
160 	_vm->_sound->cdPlayMultMusic();
161 
162 	TextItem *textItem = _vm->_game->_resources->getTextItem(id);
163 	if (!textItem)
164 		return;
165 
166 	dataPtr = textItem->getData();
167 	ptr     = dataPtr;
168 
169 	destX = READ_LE_UINT16(ptr) & 0x7FFF;
170 	destY = READ_LE_UINT16(ptr + 2);
171 	spriteRight = READ_LE_UINT16(ptr + 4);
172 	spriteBottom = READ_LE_UINT16(ptr + 6);
173 	ptr += 8;
174 
175 	if (_renderFlags & RENDERFLAG_CAPTUREPUSH) {
176 		_vm->_game->capturePush(destX, destY,
177 				spriteRight - destX + 1, spriteBottom - destY + 1);
178 		(*_vm->_scenery->_pCaptureCounter)++;
179 	}
180 
181 	_destSpriteX = destX;
182 	_destSpriteY = destY;
183 	_spriteRight = spriteRight;
184 	_spriteBottom = spriteBottom;
185 	_destSurface = kBackSurface;
186 
187 	_backColor = *ptr++;
188 	_transparency = 1;
189 	spriteOperation(DRAW_CLEARRECT);
190 
191 	_backColor = 0;
192 	savedFlags = _renderFlags;
193 	_renderFlags &= ~RENDERFLAG_NOINVALIDATE;
194 
195 	while ((_destSpriteX = READ_LE_UINT16(ptr)) != -1) {
196 		_destSpriteX += destX;
197 		_destSpriteY = READ_LE_UINT16(ptr + 2) + destY;
198 		_spriteRight = READ_LE_UINT16(ptr + 4) + destX;
199 		_spriteBottom = READ_LE_UINT16(ptr + 6) + destY;
200 		ptr += 8;
201 
202 		cmd = *ptr++;
203 		switch ((cmd & 0xF0) >> 4) {
204 		case 0:
205 			_frontColor = cmd & 0xF;
206 			spriteOperation(DRAW_DRAWLINE);
207 			break;
208 		case 1:
209 			_frontColor = cmd & 0xF;
210 			spriteOperation(DRAW_DRAWBAR);
211 			break;
212 		case 2:
213 			_backColor = cmd & 0xF;
214 			spriteOperation(DRAW_FILLRECTABS);
215 			break;
216 		default:
217 			break;
218 		}
219 	}
220 	ptr += 2;
221 
222 	for (ptrEnd = ptr; *ptrEnd != 1; ptrEnd++) {
223 		if (*ptrEnd == 3)
224 			ptrEnd++;
225 
226 		if (*ptrEnd == 2)
227 			ptrEnd += 4;
228 	}
229 	ptrEnd++;
230 
231 	while (*ptr != 1) {
232 		cmd = *ptr;
233 		if (cmd == 3) {
234 			ptr++;
235 			_fontIndex = (*ptr & 0xF0) >> 4;
236 			_frontColor = *ptr & 0xF;
237 			ptr++;
238 			continue;
239 		} else if (cmd == 2) {
240 			ptr++;
241 			_destSpriteX = destX + READ_LE_UINT16(ptr);
242 			_destSpriteY = destY + READ_LE_UINT16(ptr + 2);
243 			ptr += 4;
244 			continue;
245 		}
246 
247 		if (*ptr != 0xBA) {
248 			_letterToPrint = (char) *ptr;
249 			spriteOperation(DRAW_DRAWLETTER);
250 			_destSpriteX += _fonts[_fontIndex]->getCharWidth();
251 			ptr++;
252 		} else {
253 			cmd = ptrEnd[17] & 0x7F;
254 			if (cmd == 0) {
255 				val = READ_LE_UINT16(ptrEnd + 18) * 4;
256 				sprintf(buf, "%d", (int32)VAR_OFFSET(val));
257 			} else if (cmd == 1) {
258 				val = READ_LE_UINT16(ptrEnd + 18) * 4;
259 
260 				Common::strlcpy(buf, GET_VARO_STR(val), 20);
261 			} else {
262 				val = READ_LE_UINT16(ptrEnd + 18) * 4;
263 
264 				sprintf(buf, "%d", (int32)VAR_OFFSET(val));
265 				if (buf[0] == '-') {
266 					while (strlen(buf) - 1 < (uint32)ptrEnd[17]) {
267 						_vm->_util->insertStr("0", buf, 1);
268 					}
269 				} else {
270 					while (strlen(buf) - 1 < (uint32)ptrEnd[17]) {
271 						_vm->_util->insertStr("0", buf, 0);
272 					}
273 				}
274 
275 				_vm->_util->insertStr(",", buf, strlen(buf) + 1 - ptrEnd[17]);
276 			}
277 
278 			_textToPrint = buf;
279 			destSpriteX = _destSpriteX;
280 			spriteOperation(DRAW_PRINTTEXT);
281 			if (ptrEnd[17] & 0x80) {
282 				if (ptr[1] == ' ') {
283 					_destSpriteX += _fonts[_fontIndex]->getCharWidth();
284 					while (ptr[1] == ' ')
285 						ptr++;
286 					if (ptr[1] == 2) {
287 						if (READ_LE_UINT16(ptr + 4) == _destSpriteY)
288 							ptr += 5;
289 					}
290 				} else if (ptr[1] == 2 && READ_LE_UINT16(ptr + 4) == _destSpriteY) {
291 					ptr += 5;
292 					_destSpriteX += _fonts[_fontIndex]->getCharWidth();
293 				}
294 			} else {
295 				_destSpriteX = destSpriteX + _fonts[_fontIndex]->getCharWidth();
296 			}
297 			ptrEnd += 23;
298 			ptr++;
299 		}
300 	}
301 
302 	delete textItem;
303 	_renderFlags = savedFlags;
304 
305 	if (_renderFlags & RENDERFLAG_COLLISIONS)
306 		_vm->_game->_hotspots->check(0, 0);
307 
308 	if ((_renderFlags & RENDERFLAG_CAPTUREPOP) && *_vm->_scenery->_pCaptureCounter != 0) {
309 		(*_vm->_scenery->_pCaptureCounter)--;
310 		_vm->_game->capturePop(1);
311 	}
312 }
313 
spriteOperation(int16 operation)314 void Draw_v1::spriteOperation(int16 operation) {
315 	int16 len;
316 	int16 x, y;
317 	int16 perLine;
318 	Resource *resource;
319 
320 	operation &= 0x0F;
321 
322 	if (_sourceSurface >= 100)
323 		_sourceSurface -= 80;
324 	if (_destSurface >= 100)
325 		_destSurface -= 80;
326 
327 	if (_renderFlags & RENDERFLAG_USEDELTAS) {
328 		if (_sourceSurface == kBackSurface) {
329 			_spriteLeft += _backDeltaX;
330 			_spriteTop += _backDeltaY;
331 		}
332 
333 		if (_destSurface == kBackSurface) {
334 			_destSpriteX += _backDeltaX;
335 			_destSpriteY += _backDeltaY;
336 			if ((operation == DRAW_DRAWLINE) ||
337 			   ((operation >= DRAW_DRAWBAR) &&
338 			    (operation <= DRAW_FILLRECTABS))) {
339 				_spriteRight += _backDeltaX;
340 				_spriteBottom += _backDeltaY;
341 			}
342 		}
343 	}
344 
345 	Font *font = 0;
346 	switch (operation) {
347 	case DRAW_BLITSURF:
348 		_spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
349 		    _spriteLeft, _spriteTop,
350 		    _spriteLeft + _spriteRight - 1,
351 		    _spriteTop + _spriteBottom - 1,
352 		    _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
353 
354 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
355 				_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
356 		break;
357 
358 	case DRAW_PUTPIXEL:
359 		_spritesArray[_destSurface]->putPixel(_destSpriteX, _destSpriteY, _frontColor);
360 
361 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _destSpriteX, _destSpriteY);
362 		break;
363 
364 	case DRAW_FILLRECT:
365 		_spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
366 		    _destSpriteX + _spriteRight - 1,
367 		    _destSpriteY + _spriteBottom - 1, _backColor);
368 
369 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
370 				_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
371 		break;
372 
373 	case DRAW_DRAWLINE:
374 		_spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
375 		    _spriteRight, _spriteBottom, _frontColor);
376 
377 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
378 		break;
379 
380 	case DRAW_INVALIDATE:
381 		dirtiedRect(_destSurface, _destSpriteX - _spriteRight, _destSpriteY - _spriteBottom,
382 				_destSpriteX + _spriteRight, _destSpriteY + _spriteBottom);
383 		break;
384 
385 	case DRAW_LOADSPRITE:
386 		resource = _vm->_game->_resources->getResource((uint16) _spriteLeft,
387 			                                             &_spriteRight, &_spriteBottom);
388 
389 		if (!resource)
390 			break;
391 
392 		_vm->_video->drawPackedSprite(resource->getData(),
393 				_spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
394 				_transparency, *_spritesArray[_destSurface]);
395 
396 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
397 				_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
398 
399 		delete resource;
400 		break;
401 
402 	case DRAW_PRINTTEXT:
403 		if ((_fontIndex >= kFontCount) || !_fonts[_fontIndex]) {
404 			warning("Trying to print \"%s\" with undefined font %d", _textToPrint, _fontIndex);
405 			break;
406 		}
407 
408 		font = _fonts[_fontIndex];
409 		len = strlen(_textToPrint);
410 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
411 				_destSpriteX + len * font->getCharWidth() - 1,
412 				_destSpriteY + font->getCharHeight() - 1);
413 
414 		for (int i = 0; i < len; i++) {
415 			font->drawLetter(*_spritesArray[_destSurface], _textToPrint[i],
416 					_destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
417 
418 			_destSpriteX += font->getCharWidth();
419 		}
420 		break;
421 
422 	case DRAW_DRAWBAR:
423 		_spritesArray[_destSurface]->drawLine(_destSpriteX, _spriteBottom,
424 		    _spriteRight, _spriteBottom, _frontColor);
425 
426 		_spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
427 		    _destSpriteX, _spriteBottom, _frontColor);
428 
429 		_spritesArray[_destSurface]->drawLine(_spriteRight, _destSpriteY,
430 		    _spriteRight, _spriteBottom, _frontColor);
431 
432 		_spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
433 		    _spriteRight, _destSpriteY, _frontColor);
434 
435 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
436 		break;
437 
438 	case DRAW_CLEARRECT:
439 		if (_backColor < 16) {
440 			_spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
441 			    _spriteRight, _spriteBottom,
442 			    _backColor);
443 		}
444 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
445 		break;
446 
447 	case DRAW_FILLRECTABS:
448 		_spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
449 		    _spriteRight, _spriteBottom, _backColor);
450 
451 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
452 		break;
453 
454 	case DRAW_DRAWLETTER:
455 		if ((_fontIndex >= kFontCount) || !_fonts[_fontIndex]) {
456 			warning("Trying to print \'%c\' with undefined font %d", _letterToPrint, _fontIndex);
457 			break;
458 		}
459 
460 		font = _fonts[_fontIndex];
461 		if (_fontToSprite[_fontIndex].sprite == -1) {
462 			dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
463 					_destSpriteX + font->getCharWidth()  - 1,
464 					_destSpriteY + font->getCharHeight() - 1);
465 			font->drawLetter(*_spritesArray[_destSurface], _letterToPrint,
466 					_destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
467 			break;
468 		}
469 
470 		perLine =
471 			_spritesArray[(int16)_fontToSprite[_fontIndex].sprite]->getWidth() /
472 			_fontToSprite[_fontIndex].width;
473 
474 		y = (_letterToPrint - _fontToSprite[_fontIndex].base) / perLine *
475 			_fontToSprite[_fontIndex].height;
476 
477 		x = (_letterToPrint - _fontToSprite[_fontIndex].base) % perLine *
478 			_fontToSprite[_fontIndex].width;
479 
480 		dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
481 				_destSpriteX + _fontToSprite[_fontIndex].width,
482 				_destSpriteY + _fontToSprite[_fontIndex].height);
483 
484 		_spritesArray[_destSurface]->blit(*_spritesArray[(int16)_fontToSprite[_fontIndex].sprite], x, y,
485 		    x + _fontToSprite[_fontIndex].width,
486 		    y + _fontToSprite[_fontIndex].height,
487 		    _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
488 
489 		break;
490 
491 	default:
492 		break;
493 	}
494 
495 	if (_renderFlags & RENDERFLAG_USEDELTAS) {
496 		if (_sourceSurface == kBackSurface) {
497 			_spriteLeft -= _backDeltaX;
498 			_spriteTop -= _backDeltaY;
499 		}
500 
501 		if (_destSurface == kBackSurface) {
502 			_destSpriteX -= _backDeltaX;
503 			_destSpriteY -= _backDeltaY;
504 		}
505 	}
506 }
507 
508 } // End of namespace Gob
509