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/str.h"
24 #include "common/system.h"
25 #include "engines/util.h"
26 #include "graphics/cursorman.h"
27 #include "graphics/palette.h"
28 #include "graphics/surface.h"
29 #include "common/config-manager.h"
30
31 #include "supernova/imageid.h"
32 #include "supernova/resman.h"
33 #include "supernova/game-manager.h"
34 #include "supernova/screen.h"
35 #include "supernova/supernova.h"
36
37 #include "supernova/screenstatic.cpp"
38
39 namespace Supernova {
40
ScreenBuffer()41 ScreenBuffer::ScreenBuffer()
42 : _x(0)
43 , _y(0)
44 , _width(0)
45 , _height(0)
46 , _pixels(nullptr) {
47 }
48
ScreenBufferStack()49 ScreenBufferStack::ScreenBufferStack()
50 : _last(_buffer) {
51 }
52
push(int x,int y,int width,int height)53 void ScreenBufferStack::push(int x, int y, int width, int height) {
54 if (_last == ARRAYEND(_buffer))
55 return;
56
57 Graphics::Surface *screenSurface = g_system->lockScreen();
58
59 if (x < 0) {
60 width += x;
61 x = 0;
62 }
63
64 if (x + width > screenSurface->w)
65 width = screenSurface->w - x;
66
67 if (y < 0) {
68 height += y;
69 y = 0;
70 }
71
72 if (y + height > screenSurface->h)
73 height = screenSurface->h - y;
74
75 _last->_pixels = new byte[width * height];
76 byte *pixels = _last->_pixels;
77 const byte *screen = static_cast<const byte *>(screenSurface->getBasePtr(x, y));
78 for (int i = 0; i < height; ++i) {
79 Common::copy(screen, screen + width, pixels);
80 screen += screenSurface->pitch;
81 pixels += width;
82 }
83 g_system->unlockScreen();
84
85 _last->_x = x;
86 _last->_y = y;
87 _last->_width = width;
88 _last->_height = height;
89
90 ++_last;
91 }
92
restore()93 void ScreenBufferStack::restore() {
94 if (_last == _buffer)
95 return;
96
97 --_last;
98 g_system->lockScreen()->copyRectToSurface(_last->_pixels, _last->_width, _last->_x,
99 _last->_y, _last->_width, _last->_height);
100 g_system->unlockScreen();
101
102 delete[] _last->_pixels;
103 }
104
Marquee(Screen * screen,MarqueeId id,const char * text)105 Marquee::Marquee(Screen *screen, MarqueeId id, const char *text)
106 : _text(text)
107 , _textBegin(text)
108 , _delay(0)
109 , _loop(false)
110 , _oldColor(nullptr)
111 , _screen(screen) {
112 if (_screen->_vm->_MSPart == 1) {
113 _color = kColorLightBlue;
114 if (id == kMarqueeIntro) {
115 _y = 191;
116 _loop = true;
117 } else if (id == kMarqueeOutro) {
118 _y = 1;
119 }
120 } else if (_screen->_vm->_MSPart == 2) {
121 byte purple[3] = {0x9b, 0x00, 0xfb};
122 _oldColor = new byte[3];
123 _screen->_vm->_system->getPaletteManager()->grabPalette(_oldColor, kColorPurple, 1);
124 _screen->_vm->_system->getPaletteManager()->setPalette(purple, kColorPurple, 1);
125 _color = kColorPurple;
126 if (id == kMarqueeIntro) {
127 _y = 191;
128 _loop = true;
129 } else if (id == kMarqueeOutro) {
130 _y = 191;
131 }
132 }
133
134 _textWidth = Screen::textWidth(_text);
135 _x = kScreenWidth / 2 - _textWidth / 2;
136 _screen->_textCursorX = _x;
137 _screen->_textCursorY = _y;
138 _screen->_textColor = _color;
139 }
140
~Marquee()141 Marquee::~Marquee() {
142 if (_screen->_vm->_MSPart == 2) {
143 _screen->_vm->_system->getPaletteManager()->setPalette(_oldColor, kColorPurple, 1);
144 delete _oldColor;
145 }
146 }
147
clearText()148 void Marquee::clearText() {
149 _screen->renderBox(_x, _y - 1, _textWidth + 1, 9, kColorBlack);
150 }
151
reset()152 void Marquee::reset() {
153 _text = _textBegin;
154 clearText();
155 _textWidth = Screen::textWidth(_text);
156 _x = kScreenWidth / 2 - _textWidth / 2;
157 _screen->_textCursorX = _x;
158 }
159
renderCharacter()160 bool Marquee::renderCharacter() {
161 if (_delay != 0) {
162 _delay--;
163 return true;
164 }
165
166 switch (*_text) {
167 case '\233':
168 if (_loop) {
169 _loop = false;
170 _text = _textBegin;
171 clearText();
172 _textWidth = Screen::textWidth(_text);
173 _x = kScreenWidth / 2 - _textWidth / 2;
174 _screen->_textCursorX = _x;
175 } else
176 return false;
177 break;
178 case '\1':
179 clearText();
180 _text++;
181 _textWidth = Screen::textWidth(_text);
182 _x = kScreenWidth / 2 - _textWidth / 2;
183 _screen->_textCursorX = _x;
184 if (_screen->_vm->_MSPart == 1) {
185 _color = kColorLightBlue;
186 _screen->_textColor = _color;
187 } else if (_screen->_vm->_MSPart == 2) {
188 _color = kColorPurple;
189 _screen->_textColor = _color;
190 }
191 break;
192 case '^':
193 _color = kColorLightYellow;
194 _screen->_textColor = _color;
195 _text++;
196 break;
197 case '#':
198 _delay = 50;
199 _text++;
200 break;
201 default:
202 _screen->renderText((uint16)*_text++);
203 _delay = 1;
204 break;
205 }
206 return true;
207 }
208
Screen(SupernovaEngine * vm,ResourceManager * resMan)209 Screen::Screen(SupernovaEngine *vm, ResourceManager *resMan)
210 : _vm(vm)
211 , _resMan(resMan)
212 , _currentImage(nullptr)
213 , _viewportBrightness(255)
214 , _guiBrightness(255)
215 , _screenWidth(320)
216 , _screenHeight(200)
217 , _textColor(kColorBlack)
218 , _textCursorX(0)
219 , _textCursorY(0)
220 , _messageShown(false) {
221
222 changeCursor(ResourceManager::kCursorNormal);
223 }
224
getScreenWidth() const225 int Screen::getScreenWidth() const {
226 return _screenWidth;
227 }
228
getScreenHeight() const229 int Screen::getScreenHeight() const {
230 return _screenHeight;
231 }
232
getGuiBrightness() const233 int Screen::getGuiBrightness() const {
234 return _guiBrightness;
235 }
236
setViewportBrightness(int brightness)237 void Screen::setViewportBrightness(int brightness) {
238 _viewportBrightness = brightness;
239 }
240
getViewportBrightness() const241 int Screen::getViewportBrightness() const {
242 return _viewportBrightness;
243 }
244
setGuiBrightness(int brightness)245 void Screen::setGuiBrightness(int brightness) {
246 _guiBrightness = brightness;
247 }
248
getCurrentImage()249 MSNImage *Screen::getCurrentImage() {
250 return _currentImage;
251 }
252
getImageInfo(ImageId id) const253 const Screen::ImageInfo *Screen::getImageInfo(ImageId id) const {
254 return &imageInfo[(int)id];
255 }
256
isMessageShown() const257 bool Screen::isMessageShown() const {
258 return _messageShown;
259 }
260
getTextCursorPos()261 Common::Point Screen::getTextCursorPos() {
262 return Common::Point(_textCursorX, _textCursorY);
263 }
264
setTextCursorPos(int x,int y)265 void Screen::setTextCursorPos(int x, int y) {
266 _textCursorX = x;
267 _textCursorY = y;
268 }
269
getTextCursorColor()270 byte Screen::getTextCursorColor() {
271 return _textColor;
272 }
273
setTextCursorColor(byte color)274 void Screen::setTextCursorColor(byte color) {
275 _textColor = color;
276 }
277
renderMessage(int stringId,MessagePosition position,Common::String var1,Common::String var2)278 void Screen::renderMessage(int stringId, MessagePosition position,
279 Common::String var1, Common::String var2) {
280 Common::String text = _vm->getGameString(stringId);
281
282 if (!var1.empty()) {
283 if (!var2.empty())
284 text = Common::String::format(text.c_str(), var1.c_str(), var2.c_str());
285 else
286 text = Common::String::format(text.c_str(), var1.c_str());
287 }
288
289 renderMessage(text, position);
290 }
291
renderMessage(const Common::String & text,MessagePosition position)292 void Screen::renderMessage(const Common::String &text, MessagePosition position) {
293 if (!text.empty())
294 renderMessage(text.c_str(), position);
295 }
296
renderText(const uint16 character)297 void Screen::renderText(const uint16 character) {
298 char text[2];
299 text[0] = character & 0xFF;
300 text[1] = 0;
301 renderText(text, _textCursorX, _textCursorY, _textColor);
302 }
303
renderText(const char * text)304 void Screen::renderText(const char *text) {
305 renderText(text, _textCursorX, _textCursorY, _textColor);
306 }
307
renderText(int stringId)308 void Screen::renderText(int stringId) {
309 renderText(_vm->getGameString(stringId));
310 }
311
renderText(const Common::String & text)312 void Screen::renderText(const Common::String &text) {
313 if (!text.empty())
314 renderText(text.c_str());
315 }
316
renderText(const GuiElement & guiElement)317 void Screen::renderText(const GuiElement &guiElement) {
318 renderText(guiElement.getText(), guiElement.getTextPos().x,
319 guiElement.getTextPos().y, guiElement.getTextColor());
320 }
321
renderText(const uint16 character,int x,int y,byte color)322 void Screen::renderText(const uint16 character, int x, int y, byte color) {
323 char text[2];
324 text[0] = character & 0xFF;
325 text[1] = 0;
326 renderText(text, x, y, color);
327 }
328
renderText(const char * text,int x,int y,byte color)329 void Screen::renderText(const char *text, int x, int y, byte color) {
330 Graphics::Surface *screen = _vm->_system->lockScreen();
331 byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y));
332 const byte *basePtr = cursor;
333
334 byte c;
335 while ((c = *text++) != '\0') {
336 if (c < 32) {
337 continue;
338 } else if (c == 225) {
339 c = 128;
340 }
341
342 for (uint i = 0; i < 5; ++i) {
343 if (font[c - 32][i] == 0xff) {
344 break;
345 }
346
347 byte *ascentLine = cursor;
348 for (byte j = font[c - 32][i]; j != 0; j >>= 1) {
349 if (j & 1) {
350 *cursor = color;
351 }
352 cursor += kScreenWidth;
353 }
354 cursor = ++ascentLine;
355 }
356 ++cursor;
357 }
358 _vm->_system->unlockScreen();
359
360 uint numChars = cursor - basePtr;
361 uint absPosition = y * kScreenWidth + x + numChars;
362 _textCursorX = absPosition % kScreenWidth;
363 _textCursorY = absPosition / kScreenWidth;
364 _textColor = color;
365 }
366
renderText(const Common::String & text,int x,int y,byte color)367 void Screen::renderText(const Common::String &text, int x, int y, byte color) {
368 if (!text.empty())
369 renderText(text.c_str(), x, y, color);
370 }
371
renderText(int stringId,int x,int y,byte color)372 void Screen::renderText(int stringId, int x, int y, byte color) {
373 renderText(_vm->getGameString(stringId), x, y, color);
374 }
375
renderImageSection(const MSNImage * image,int section,bool invert)376 void Screen::renderImageSection(const MSNImage *image, int section, bool invert) {
377 // Note: inverting means we are removing the section. So we should get the rect for that
378 // section but draw the background (section 0) instead.
379 if (section > image->_numSections - 1)
380 return;
381
382 Common::Rect sectionRect(image->_section[section].x1,
383 image->_section[section].y1,
384 image->_section[section].x2 + 1,
385 image->_section[section].y2 + 1);
386 bool bigImage = false;
387 if (_vm->_MSPart == 1)
388 bigImage = image->_filenumber == 1 || image->_filenumber == 2;
389 else if (_vm->_MSPart == 2)
390 bigImage = image->_filenumber == 38;
391 if (bigImage) {
392 sectionRect.setWidth(640);
393 sectionRect.setHeight(480);
394 if (_screenWidth != 640) {
395 _screenWidth = 640;
396 _screenHeight = 480;
397 initGraphics(_screenWidth, _screenHeight);
398 }
399 } else {
400 if (_screenWidth != 320) {
401 _screenWidth = 320;
402 _screenHeight = 200;
403 initGraphics(_screenWidth, _screenHeight);
404 }
405 }
406
407 uint offset = 0;
408 int pitch = sectionRect.width();
409 if (invert) {
410 pitch = image->_pitch;
411 offset = image->_section[section].y1 * pitch +
412 image->_section[section].x1;
413 section = 0;
414 }
415
416 void *pixels = image->_sectionSurfaces[section]->getPixels();
417 _vm->_system->copyRectToScreen(static_cast<const byte *>(pixels) + offset,
418 pitch, sectionRect.left, sectionRect.top,
419 sectionRect.width(), sectionRect.height());
420 }
421
renderImage(ImageId id,bool removeImage)422 void Screen::renderImage(ImageId id, bool removeImage) {
423 ImageInfo info = imageInfo[id];
424 const MSNImage *image = _resMan->getImage(info.filenumber);
425
426 if (_currentImage != image)
427 setCurrentImage(info.filenumber);
428
429 do {
430 renderImageSection(image, info.section, removeImage);
431 info.section = image->_section[info.section].next;
432 } while (info.section != 0);
433 }
434
renderImage(int section)435 void Screen::renderImage(int section) {
436 bool removeImage = false;
437 if (section > kSectionInvert) {
438 removeImage = true;
439 section -= kSectionInvert;
440 }
441
442 if (!_currentImage || section >= kMaxSection)
443 return;
444
445 do {
446 renderImageSection(_currentImage, section, removeImage);
447 section = _currentImage->_section[section].next;
448 } while (section != 0);
449 }
450
setCurrentImage(int filenumber)451 bool Screen::setCurrentImage(int filenumber) {
452 _currentImage = _resMan->getImage(filenumber);
453 _vm->_system->getPaletteManager()->setPalette(_currentImage->getPalette(), 16, 239);
454 paletteBrightness();
455
456 return true;
457 }
458
saveScreen(int x,int y,int width,int height)459 void Screen::saveScreen(int x, int y, int width, int height) {
460 _screenBuffer.push(x, y, width, height);
461 }
462
saveScreen(const GuiElement & guiElement)463 void Screen::saveScreen(const GuiElement &guiElement) {
464 saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height());
465 }
466
restoreScreen()467 void Screen::restoreScreen() {
468 _screenBuffer.restore();
469 }
470
renderRoom(Room & room)471 void Screen::renderRoom(Room &room) {
472 if (room.getId() == INTRO1 || room.getId() == INTRO2)
473 return;
474
475 if (setCurrentImage(room.getFileNumber())) {
476 for (int i = 0; i < _currentImage->_numSections; ++i) {
477 int section = i;
478 if (room.isSectionVisible(section)) {
479 do {
480 renderImageSection(_currentImage, section, false);
481 section = _currentImage->_section[section].next;
482 } while (section != 0);
483 }
484 }
485 }
486 }
487
textWidth(const uint16 key)488 int Screen::textWidth(const uint16 key) {
489 char text[2];
490 text[0] = key & 0xFF;
491 text[1] = 0;
492 return textWidth(text);
493 }
494
textWidth(const char * text)495 int Screen::textWidth(const char *text) {
496 int charWidth = 0;
497 while (*text != '\0' && *text != '\1') {
498 byte c = *text++;
499 if (c < 32 || c == 155) {
500 // 155 is used for looping in Marquee text and is not used otherwise
501 // (it is beyond the end of the font).
502 continue;
503 } else if (c == 225) {
504 c = 35;
505 }
506
507 for (uint i = 0; i < 5; ++i) {
508 if (font[c - 32][i] == 0xff) {
509 break;
510 }
511 ++charWidth;
512 }
513 ++charWidth;
514 }
515
516 return charWidth;
517 }
518
textWidth(const Common::String & text)519 int Screen::textWidth(const Common::String &text) {
520 return Screen::textWidth(text.c_str());
521 }
522
renderMessage(const char * text,MessagePosition position,int positionX,int positionY)523 void Screen::renderMessage(const char *text, MessagePosition position, int positionX, int positionY) {
524 Common::String t(text);
525 char *row[20];
526 Common::String::iterator p = t.begin();
527 uint numRows = 0;
528 int rowWidthMax = 0;
529 int x = 0;
530 int y = 0;
531 byte textColor = 0;
532
533 while (*p != '\0') {
534 row[numRows] = p;
535 ++numRows;
536 while ((*p != '\0') && (*p != '|')) {
537 ++p;
538 }
539 if (*p == '|') {
540 *p = '\0';
541 ++p;
542 }
543 }
544 for (uint i = 0; i < numRows; ++i) {
545 int rowWidth = textWidth(row[i]);
546 if (rowWidth > rowWidthMax)
547 rowWidthMax = rowWidth;
548 }
549
550 switch (position) {
551 case kMessageNormal:
552 default:
553 x = 160 - rowWidthMax / 2;
554 textColor = kColorWhite99;
555 break;
556 case kMessageTop:
557 x = 160 - rowWidthMax / 2;
558 textColor = kColorLightYellow;
559 break;
560 case kMessageCenter:
561 x = 160 - rowWidthMax / 2;
562 textColor = kColorLightRed;
563 break;
564 case kMessageLeft:
565 x = 3;
566 textColor = kColorLightYellow;
567 break;
568 case kMessageRight:
569 x = 317 - rowWidthMax;
570 textColor = kColorLightGreen;
571 break;
572 }
573
574 if (position == kMessageNormal) {
575 y = 70 - ((numRows * 9) / 2);
576 } else if (position == kMessageTop) {
577 y = 5;
578 } else {
579 y = 142;
580 }
581
582 if (positionX != -1 && positionY != -1) {
583 x = positionX;
584 y = positionY;
585 }
586
587 int message_columns = x - 3;
588 int message_rows = y - 3;
589 int message_width = rowWidthMax + 6;
590 int message_height = numRows * 9 + 5;
591 saveScreen(message_columns, message_rows, message_width, message_height);
592 renderBox(message_columns, message_rows, message_width, message_height, kColorWhite35);
593 for (uint i = 0; i < numRows; ++i) {
594 renderText(row[i], x, y, textColor);
595 y += 9;
596 }
597
598 _messageShown = true;
599 }
600
removeMessage()601 void Screen::removeMessage() {
602 if (_messageShown) {
603 restoreScreen();
604 _messageShown = false;
605 }
606 }
607
renderBox(int x,int y,int width,int height,byte color)608 void Screen::renderBox(int x, int y, int width, int height, byte color) {
609 Graphics::Surface *screen = _vm->_system->lockScreen();
610 screen->fillRect(Common::Rect(x, y, x + width, y + height), color);
611 _vm->_system->unlockScreen();
612 }
613
renderBox(const GuiElement & guiElement)614 void Screen::renderBox(const GuiElement &guiElement) {
615 renderBox(guiElement.left, guiElement.top, guiElement.width(),
616 guiElement.height(), guiElement.getBackgroundColor());
617 }
618
initPalette()619 void Screen::initPalette() {
620 g_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
621 }
622
paletteBrightness()623 void Screen::paletteBrightness() {
624 byte palette[768];
625
626 _vm->_system->getPaletteManager()->grabPalette(palette, 0, 255);
627 for (uint i = 0; i < 48; ++i) {
628 palette[i] = (initVGAPalette[i] * _guiBrightness) >> 8;
629 }
630 for (uint i = 0; i < 717; ++i) {
631 const byte *imagePalette;
632 if (_currentImage && _currentImage->getPalette()) {
633 imagePalette = _currentImage->getPalette();
634 } else {
635 imagePalette = palette + 48;
636 }
637 palette[i + 48] = (imagePalette[i] * _viewportBrightness) >> 8;
638 }
639 _vm->_system->getPaletteManager()->setPalette(palette, 0, 255);
640 }
641
paletteFadeOut(int minBrightness)642 void Screen::paletteFadeOut(int minBrightness) {
643 while (_guiBrightness > minBrightness + 10) {
644 _guiBrightness -= 10;
645 if (_viewportBrightness > _guiBrightness)
646 _viewportBrightness = _guiBrightness;
647 paletteBrightness();
648 _vm->_system->updateScreen();
649 _vm->_system->delayMillis(_vm->_delay);
650 }
651 _guiBrightness = minBrightness;
652 _viewportBrightness = minBrightness;
653 paletteBrightness();
654 _vm->_system->updateScreen();
655 }
656
paletteFadeIn(int maxViewportBrightness)657 void Screen::paletteFadeIn(int maxViewportBrightness) {
658 while (_guiBrightness < 245) {
659 if (_viewportBrightness < maxViewportBrightness)
660 _viewportBrightness += 10;
661 _guiBrightness += 10;
662 paletteBrightness();
663 _vm->_system->updateScreen();
664 _vm->_system->delayMillis(_vm->_delay);
665 }
666 _guiBrightness = 255;
667 _viewportBrightness = maxViewportBrightness;
668 paletteBrightness();
669 _vm->_system->updateScreen();
670 }
671
setColor63(byte value)672 void Screen::setColor63(byte value) {
673 byte color[3] = {value, value, value};
674 _vm->_system->getPaletteManager()->setPalette(color, 63, 1);
675 }
676
changeCursor(ResourceManager::CursorId id)677 void Screen::changeCursor(ResourceManager::CursorId id) {
678 CursorMan.replaceCursor(_resMan->getCursor(id),
679 16, 16, 0, 0, kColorCursorTransparent);
680 CursorMan.replaceCursorPalette(initVGAPalette, 0, 16);
681 CursorMan.showMouse(true);
682 }
683
684
685 }
686