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 /*
24  * This code is based on original Tony Tough source code
25  *
26  * Copyright (c) 1997-2003 Nayma Software
27  */
28 
29 #include "common/textconsole.h"
30 #include "tony/mpal/mpalutils.h"
31 #include "tony/font.h"
32 #include "tony/input.h"
33 #include "tony/inventory.h"
34 #include "tony/loc.h"
35 #include "tony/tony.h"
36 
37 namespace Tony {
38 
39 /****************************************************************************\
40 *       RMFont Methods
41 \****************************************************************************/
42 
RMFont()43 RMFont::RMFont() {
44 	_letter = NULL;
45 	_nLetters = _fontDimx = _fontDimy = _dimx = _dimy = 0;
46 }
47 
~RMFont()48 RMFont::~RMFont() {
49 	unload();
50 }
51 
load(const byte * buf,int nChars,int dimx,int dimy,uint32 palResID)52 void RMFont::load(const byte *buf, int nChars, int dimx, int dimy, uint32 palResID) {
53 	_letter = new RMGfxSourceBuffer8RLEByte[nChars];
54 
55 	// Initialize the fonts
56 	for (int i = 0; i < nChars; i++) {
57 		// Initialize the buffer with the letters
58 		_letter[i].init(buf + i * (dimx * dimy + 8) + 8, dimx, dimy);
59 		_letter[i].loadPaletteWA(palResID);
60 	}
61 
62 	_fontDimx = dimx;
63 	_fontDimy = dimy;
64 
65 	_nLetters = nChars;
66 }
67 
load(uint32 resID,int nChars,int dimx,int dimy,uint32 palResID)68 void RMFont::load(uint32 resID, int nChars, int dimx, int dimy, uint32 palResID) {
69 	RMRes res(resID);
70 
71 	if ((int)res.size() < nChars * (dimy * dimx + 8))
72 		nChars = res.size() / (dimy * dimx + 8);
73 
74 	load(res, nChars, dimx, dimy, palResID);
75 }
76 
unload()77 void RMFont::unload() {
78 	if (_letter != NULL) {
79 		delete[] _letter;
80 		_letter = NULL;
81 	}
82 }
83 
makeLetterPrimitive(byte bChar,int & nLength)84 RMGfxPrimitive *RMFont::makeLetterPrimitive(byte bChar, int &nLength) {
85 	RMFontPrimitive *prim;
86 
87 	// Convert from character to glyph index
88 	int nLett = convertToLetter(bChar);
89 	assert(nLett < _nLetters);
90 
91 	// Create primitive font
92 	prim = new RMFontPrimitive(this);
93 	prim->_nChar = nLett;
94 
95 	// Get the length of the character in pixels
96 	nLength = letterLength(bChar);
97 
98 	return prim;
99 }
100 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim2)101 void RMFont::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim2) {
102 	CORO_BEGIN_CONTEXT;
103 	CORO_END_CONTEXT(_ctx);
104 
105 	RMFontPrimitive *prim = (RMFontPrimitive *)prim2;
106 
107 	CORO_BEGIN_CODE(_ctx);
108 
109 	// Call the draw method of the letter assigned to the primitive
110 	if (prim->_nChar != -1)
111 		CORO_INVOKE_2(_letter[prim->_nChar].draw, bigBuf, prim);
112 
113 	CORO_END_CODE;
114 }
115 
close()116 void RMFont::close() {
117 	unload();
118 }
119 
stringLen(const Common::String & text)120 int RMFont::stringLen(const Common::String &text) {
121 	if (text.empty())
122 		return letterLength('\0');
123 
124 	uint len = 0;
125 	uint i;
126 	for (i = 0; i < text.size() - 1; i++)
127 		len += letterLength(text[i], text[i + 1]);
128 	len += letterLength(text[i]);
129 
130 	return len;
131 }
132 
stringLen(char bChar,char bNext)133 int RMFont::stringLen(char bChar, char bNext) {
134 	return letterLength(bChar, bNext);
135 }
136 
137 /****************************************************************************\
138 *       RMFontColor Methods
139 \****************************************************************************/
140 
RMFontColor()141 RMFontColor::RMFontColor() {
142 	_fontR = _fontG = _fontB = 255;
143 }
144 
~RMFontColor()145 RMFontColor::~RMFontColor() {
146 }
147 
setBaseColor(byte r1,byte g1,byte b1)148 void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) {
149 	int r = (int)r1 << 16;
150 	int g = (int)g1 << 16;
151 	int b = (int)b1 << 16;
152 
153 	int rstep = r / 14;
154 	int gstep = g / 14;
155 	int bstep = b / 14;
156 
157 	byte pal[768 * 3];
158 
159 	// Check if we are already on the right color
160 	if (_fontR == r1 && _fontG == g1 && _fontB == b1)
161 		return;
162 
163 	_fontR = r1;
164 	_fontG = g1;
165 	_fontB = b1;
166 
167 	// Constructs a new palette for the font
168 	for (int i = 1; i < 16; i++) {
169 		pal[i * 3 + 0] = r >> 16;
170 		pal[i * 3 + 1] = g >> 16;
171 		pal[i * 3 + 2] = b >> 16;
172 
173 		r -= rstep;
174 		g -= gstep;
175 		b -= bstep;
176 	}
177 
178 	pal[15 * 3 + 0] += 8;
179 	pal[15 * 3 + 1] += 8;
180 	pal[15 * 3 + 2] += 8;
181 
182 	// Puts in all the letters
183 	for (int i = 0; i < _nLetters; i++)
184 		_letter[i].loadPaletteWA(pal);
185 }
186 
187 /***************************************************************************\
188 *       RMFontWithTables Methods
189 \****************************************************************************/
convertToLetter(byte nChar)190 int RMFontWithTables::convertToLetter(byte nChar) {
191 	return _cTable[nChar];
192 }
193 
letterLength(int nChar,int nNext)194 int RMFontWithTables::letterLength(int nChar, int nNext) {
195 	return (nChar != -1 ? _lTable[(byte)nChar] + _l2Table[(byte)nChar][(byte)nNext] : _lDefault);
196 }
197 
198 /***************************************************************************\
199 *       RMFontDialog Methods
200 \****************************************************************************/
201 
init()202 void RMFontDialog::init() {
203 	// bernie: Number of characters in the font
204 	int nchars =
205 	    112    // base
206 	    + 18    // polish
207 	    + 66    // russian
208 	    + 30    // czech
209 	    +  8    // french
210 	    +  5;   // deutsch
211 
212 	load(RES_F_PARL, nchars, 20, 20);
213 
214 	// Initialize the font table
215 	_lDefault = 13;
216 	_hDefault = 18;
217 	Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
218 
219 	for (int i = 0; i < 256; i++) {
220 		_cTable[i] = g_vm->_cTableDialog[i];
221 		_lTable[i] = g_vm->_lTableDialog[i];
222 	}
223 }
224 
225 /***************************************************************************\
226 *       RMFontMacc Methods
227 \****************************************************************************/
228 
init()229 void RMFontMacc::init() {
230 	// bernie: Number of characters in the font
231 	int nchars =
232 	    102    // base
233 	    + 18    // polish
234 	    + 66    // russian
235 	    + 30    // czech
236 	    +  8    // francais
237 	    +  5;   // deutsch
238 
239 	load(RES_F_MACC, nchars, 11, 16);
240 
241 	// Default
242 	_lDefault = 10;
243 	_hDefault = 17;
244 	Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
245 
246 	for (int i = 0; i < 256; i++) {
247 		_cTable[i] = g_vm->_cTableMacc[i];
248 		_lTable[i] = g_vm->_lTableMacc[i];
249 	}
250 }
251 
252 /***************************************************************************\
253 *       RMFontCredits Methods
254 \****************************************************************************/
255 
init()256 void RMFontCredits::init() {
257 	// bernie: Number of characters in the font
258 	int nchars =
259 	    112    // base
260 	    + 18    // polish
261 	    + 66    // russian
262 	    + 30    // czech
263 	    +  8    // french
264 	    +  2;   // deutsch
265 
266 	load(RES_F_CREDITS, nchars, 27, 28, RES_F_CPAL);
267 
268 	// Default
269 	_lDefault = 10;
270 	_hDefault = 28;
271 	Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
272 
273 	for (int i = 0; i < 256; i++) {
274 		_cTable[i] = g_vm->_cTableCred[i];
275 		_lTable[i] = g_vm->_lTableCred[i];
276 	}
277 }
278 
279 /***************************************************************************\
280 *       RMFontObj Methods
281 \****************************************************************************/
282 
283 #define TOUPPER(a)  ((a) >= 'a' && (a) <= 'z' ? (a) + 'A' - 'a' : (a))
284 #define TOLOWER(a)  ((a) >= 'A' && (a) <= 'Z' ? (a) + 'a' - 'A' : (a))
285 
setBothCase(int nChar,int nNext,signed char spiazz)286 void RMFontObj::setBothCase(int nChar, int nNext, signed char spiazz) {
287 	_l2Table[TOUPPER(nChar)][TOUPPER(nNext)] = spiazz;
288 	_l2Table[TOUPPER(nChar)][TOLOWER(nNext)] = spiazz;
289 	_l2Table[TOLOWER(nChar)][TOUPPER(nNext)] = spiazz;
290 	_l2Table[TOLOWER(nChar)][TOLOWER(nNext)] = spiazz;
291 }
292 
init()293 void RMFontObj::init() {
294 	//bernie: Number of characters in the font (solo maiuscolo)
295 	int nchars =
296 	    85    // base
297 	    +  9    // polish
298 	    + 33    // russian
299 	    + 15    // czech
300 	    +  0    // francais (no uppercase chars)
301 	    +  1;   // deutsch
302 
303 	load(RES_F_OBJ, nchars, 25, 30);
304 
305 	// Initialize the font table
306 	_lDefault = 26;
307 	_hDefault = 30;
308 	Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0');
309 
310 	for (int i = 0; i < 256; i++) {
311 		_cTable[i] = g_vm->_cTableObj[i];
312 		_lTable[i] = g_vm->_lTableObj[i];
313 	}
314 
315 	// Special case
316 	setBothCase('C', 'C', 2);
317 	setBothCase('A', 'T', -2);
318 	setBothCase('R', 'S', 2);
319 	setBothCase('H', 'I', -2);
320 	setBothCase('T', 'S', 2);
321 	setBothCase('O', 'R', 2);
322 	setBothCase('O', 'L', 2);
323 	setBothCase('O', 'G', 2);
324 	setBothCase('Z', 'A', -1);
325 	setBothCase('R', 'R', 1);
326 	setBothCase('R', 'U', 3);
327 }
328 
329 /****************************************************************************\
330 *       RMText Methods
331 \****************************************************************************/
332 
333 RMFontColor *RMText::_fonts[4] = { NULL, NULL, NULL, NULL };
334 
initStatics()335 void RMText::initStatics() {
336 	Common::fill(&_fonts[0], &_fonts[4], (RMFontColor *)NULL);
337 }
338 
RMText()339 RMText::RMText() {
340 	// Default color: white
341 	_textR = _textG = _textB = 255;
342 
343 	// Default length
344 	_maxLineLength = 350;
345 
346 	_bTrasp0 = true;
347 	_aHorType = HCENTER;
348 	_aVerType = VTOP;
349 	setPriority(150);
350 }
351 
~RMText()352 RMText::~RMText() {
353 }
354 
unload()355 void RMText::unload() {
356 	if (_fonts[0] != NULL) {
357 		delete _fonts[0];
358 		delete _fonts[1];
359 		delete _fonts[2];
360 		delete _fonts[3];
361 		_fonts[0] =  _fonts[1] = _fonts[2] = _fonts[3] = 0;
362 	}
363 }
364 
setMaxLineLength(int max)365 void RMText::setMaxLineLength(int max) {
366 	_maxLineLength = max;
367 }
368 
removeThis(CORO_PARAM,bool & result)369 void RMText::removeThis(CORO_PARAM, bool &result) {
370 	// Here we can do checks on the number of frames, time spent, etc.
371 	result = true;
372 }
373 
writeText(const Common::String & text,int nFont,int * time)374 void RMText::writeText(const Common::String &text, int nFont, int *time) {
375 	// Initializes the font (only once)
376 	if (_fonts[0] == NULL) {
377 		_fonts[0] = new RMFontDialog;
378 		_fonts[0]->init();
379 		_fonts[1] = new RMFontObj;
380 		_fonts[1]->init();
381 		_fonts[2] = new RMFontMacc;
382 		_fonts[2]->init();
383 		_fonts[3] = new RMFontCredits;
384 		_fonts[3]->init();
385 	}
386 
387 	writeText(text, _fonts[nFont], time);
388 }
389 
writeText(Common::String text,RMFontColor * font,int * time)390 void RMText::writeText(Common::String text, RMFontColor *font, int *time) {
391 	RMGfxPrimitive *prim;
392 
393 	// Set the base color
394 	font->setBaseColor(_textR, _textG, _textB);
395 
396 	// Destroy the buffer before starting
397 	destroy();
398 
399 	// If the string is empty, do nothing
400 	if (text.empty())
401 		return;
402 
403 	// Divide the words into lines. In this cycle, X contains the maximum length reached by a line,
404 	// and the number of lines
405 	Common::Array<Common::String> lines;
406 	uint p = 0;
407 	int j = 0;
408 	int x = 0;
409 	while (p < text.size()) {
410 		j += font->stringLen(text[p]);
411 		if (j > (((_aHorType == HLEFTPAR) && (lines.size() > 0)) ? _maxLineLength - 25 : _maxLineLength)) {
412 			j -= font->stringLen(text[p], (p + 1 == text.size()) ? '\0' : text[p + 1]);
413 			if (j > x)
414 				x = j;
415 
416 			// Back to the first usable space
417 			//
418 			// BERNIE: In the original, sentences containing words that exceed the
419 			// width of a line caused discontinuation of the whole sentence.
420 			// This workaround has the partial word broken up so it will still display
421 			//
422 			uint old_p = p;
423 			while (text[p] != ' ' && text[p] != '-' && p > 0)
424 				p--;
425 
426 			if (p == 0)
427 				p = old_p;
428 
429 			// Check if there are any blanks to end
430 			while ((text[p] == ' ' || text[p] == '-') && p + 1 < text.size())
431 				p++;
432 			if (p == text.size())
433 				break;
434 			lines.push_back(Common::String(text.c_str(), p));
435 			if (text[p] == ' ')
436 				p++;
437 			text = text.c_str() + p;
438 			p = 0;
439 			j = 0;
440 			continue;
441 		}
442 		p++;
443 	}
444 
445 	if (j > x)
446 		x = j;
447 
448 	// Add the last line of text.
449 	lines.push_back(text);
450 
451 	x += 8;
452 
453 	// Starting position for the surface: X1, Y
454 	int width = x;
455 	int height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy;
456 
457 	// Create the surface
458 	create(width, height);
459 	Common::fill(_buf, _buf + width * height * 2, 0);
460 
461 	p = 0;
462 
463 	int y = 0;
464 	int numchar = 0;
465 	for (uint i = 0; i < lines.size(); ++i) {
466 		const Common::String &line = lines[i];
467 
468 		// Measure the length of the line
469 		x = 0;
470 		j = font->stringLen(line);
471 
472 		switch (_aHorType) {
473 		case HLEFT:
474 		default:
475 			x = 0;
476 			break;
477 
478 		case HLEFTPAR:
479 			if (i == 0)
480 				x = 0;
481 			else
482 				x = 25;
483 			break;
484 
485 		case HCENTER:
486 			x = width / 2 - j / 2;
487 			break;
488 
489 		case HRIGHT:
490 			x = width - j - 1;
491 			break;
492 		}
493 
494 		p = 0;
495 		while (p < line.size()) {
496 			if (line[p] == ' ') {
497 				x += font->stringLen(line[p]);
498 				p++;
499 				continue;
500 			}
501 
502 			int len;
503 			prim = font->makeLetterPrimitive(line[p], len);
504 			prim->getDst()._x1 = x;
505 			prim->getDst()._y1 = y;
506 			addPrim(prim);
507 
508 			numchar++;
509 
510 			x += font->stringLen(line[p], (p + 1 == line.size()) ? '\0' : line[p + 1]);
511 			p++;
512 		}
513 		p++;
514 		y += font->letterHeight();
515 	}
516 
517 	if (time != NULL)
518 		*time = 1000 + numchar * (11 - GLOBALS._nCfgTextSpeed) * 14;
519 }
520 
clipOnScreen(RMGfxPrimitive * prim)521 void RMText::clipOnScreen(RMGfxPrimitive *prim) {
522 	// Don't let it go outside the screen
523 	if (prim->getDst()._x1 < 5)
524 		prim->getDst()._x1 = 5;
525 	if (prim->getDst()._y1 < 5)
526 		prim->getDst()._y1 = 5;
527 	if (prim->getDst()._x1 + _dimx > 635)
528 		prim->getDst()._x1 = 635 - _dimx;
529 	if (prim->getDst()._y1 + _dimy > 475)
530 		prim->getDst()._y1 = 475 - _dimy;
531 }
532 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)533 void RMText::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
534 	CORO_BEGIN_CONTEXT;
535 	CORO_END_CONTEXT(_ctx);
536 
537 	CORO_BEGIN_CODE(_ctx);
538 	// Horizontally
539 	if (_aHorType == HCENTER)
540 		prim->getDst().topLeft() -= RMPoint(_dimx / 2, 0);
541 	else if (_aHorType == HRIGHT)
542 		prim->getDst().topLeft() -= RMPoint(_dimx, 0);
543 
544 	// Vertically
545 	if (_aVerType == VTOP) {
546 
547 	} else if (_aVerType == VCENTER) {
548 		prim->getDst()._y1 -= _dimy / 2;
549 
550 	} else if (_aVerType == VBOTTOM) {
551 		prim->getDst()._y1 -= _dimy;
552 	}
553 
554 	clipOnScreen(prim);
555 
556 	CORO_INVOKE_2(RMGfxWoodyBuffer::draw, bigBuf, prim);
557 
558 	CORO_END_CODE;
559 }
560 
561 /**
562  * Set the alignment type
563  */
setAlignType(HorAlign aHor,VerAlign aVer)564 void RMText::setAlignType(HorAlign aHor, VerAlign aVer) {
565 	_aHorType = aHor;
566 	_aVerType = aVer;
567 }
568 
569 /**
570  * Set the base color
571  */
setColor(byte r,byte g,byte b)572 void RMText::setColor(byte r, byte g, byte b) {
573 	_textR = r;
574 	_textG = g;
575 	_textB = b;
576 }
577 
578 /****************************************************************************\
579 *       RMTextDialog Methods
580 \****************************************************************************/
581 
RMTextDialog()582 RMTextDialog::RMTextDialog() : RMText() {
583 	_time = _startTime = 0;
584 	_dst = RMPoint(0, 0);
585 
586 	_bSkipStatus = true;
587 	_bShowed = true;
588 	_bForceTime = false;
589 	_bForceNoTime = false;
590 	_bAlwaysDisplay = false;
591 	_bNoTab = false;
592 	_hCustomSkip = CORO_INVALID_PID_VALUE;
593 	_hCustomSkip2 = CORO_INVALID_PID_VALUE;
594 	_input = NULL;
595 
596 	// Create the event for displaying the end
597 	_hEndDisplay = CoroScheduler.createEvent(false, false);
598 }
599 
~RMTextDialog()600 RMTextDialog::~RMTextDialog() {
601 	CoroScheduler.closeEvent(_hEndDisplay);
602 }
603 
show()604 void RMTextDialog::show() {
605 	_bShowed = true;
606 }
607 
hide(CORO_PARAM)608 void RMTextDialog::hide(CORO_PARAM) {
609 	_bShowed = false;
610 }
611 
writeText(const Common::String & text,int font,int * time)612 void RMTextDialog::writeText(const Common::String &text, int font, int *time) {
613 	RMText::writeText(text, font, &_time);
614 
615 	if (time != NULL)
616 		*time = _time;
617 }
618 
writeText(const Common::String & text,RMFontColor * font,int * time)619 void RMTextDialog::writeText(const Common::String &text, RMFontColor *font, int *time) {
620 	RMText::writeText(text, font, &_time);
621 
622 	if (time != NULL)
623 		*time = _time;
624 }
625 
setSkipStatus(bool bEnabled)626 void RMTextDialog::setSkipStatus(bool bEnabled) {
627 	_bSkipStatus = bEnabled;
628 }
629 
forceTime()630 void RMTextDialog::forceTime() {
631 	_bForceTime = true;
632 }
633 
forceNoTime()634 void RMTextDialog::forceNoTime() {
635 	_bForceNoTime = true;
636 }
637 
setNoTab()638 void RMTextDialog::setNoTab() {
639 	_bNoTab = true;
640 }
641 
setForcedTime(uint32 dwTime)642 void RMTextDialog::setForcedTime(uint32 dwTime) {
643 	_time = dwTime;
644 }
645 
setAlwaysDisplay()646 void RMTextDialog::setAlwaysDisplay() {
647 	_bAlwaysDisplay = true;
648 }
649 
removeThis(CORO_PARAM,bool & result)650 void RMTextDialog::removeThis(CORO_PARAM, bool &result) {
651 	CORO_BEGIN_CONTEXT;
652 	bool expired;
653 	CORO_END_CONTEXT(_ctx);
654 
655 	CORO_BEGIN_CODE(_ctx);
656 
657 	// Presume successful result
658 	result = true;
659 
660 	// Don't erase the background
661 	if (_bSkipStatus) {
662 		if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
663 			if (GLOBALS._bCfgTimerizedText) {
664 				if (!_bForceNoTime) {
665 					if (g_vm->getTime() > (uint32)_time + _startTime)
666 						return;
667 				}
668 			}
669 		}
670 
671 		if (!_bNoTab) {
672 			if (g_vm->getEngine()->getInput().getAsyncKeyState(Common::KEYCODE_TAB))
673 				return;
674 		}
675 
676 		if (!_bNoTab) {
677 			if (_input) {
678 				if (_input->mouseLeftClicked() || _input->mouseRightClicked())
679 					return;
680 			}
681 		}
682 	}
683 
684 	// Erase the background
685 	else if (!(GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE)) {
686 		if (!_bForceNoTime) {
687 			if (g_vm->getTime() > (uint32)_time + _startTime)
688 				return;
689 		}
690 	}
691 
692 	// If time is forced
693 	if (_bForceTime) {
694 		if (g_vm->getTime() > (uint32)_time + _startTime)
695 			return;
696 	}
697 
698 	if (_hCustomSkip != CORO_INVALID_PID_VALUE) {
699 		CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip, 0, &_ctx->expired);
700 		// == WAIT_OBJECT_0
701 		if (!_ctx->expired)
702 			return;
703 	}
704 
705 	if (GLOBALS._bCfgDubbing && _hCustomSkip2 != CORO_INVALID_PID_VALUE) {
706 		CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _hCustomSkip2, 0, &_ctx->expired);
707 		// == WAIT_OBJECT_0
708 		if (!_ctx->expired)
709 			return;
710 	}
711 
712 	result = false;
713 
714 	CORO_END_CODE;
715 }
716 
unregister()717 void RMTextDialog::unregister() {
718 	RMGfxTask::unregister();
719 	assert(_nInList == 0);
720 	CoroScheduler.setEvent(_hEndDisplay);
721 }
722 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)723 void RMTextDialog::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
724 	CORO_BEGIN_CONTEXT;
725 	CORO_END_CONTEXT(_ctx);
726 
727 	CORO_BEGIN_CODE(_ctx);
728 
729 	if (_startTime == 0)
730 		_startTime = g_vm->getTime();
731 
732 	if (_bShowed) {
733 		if (GLOBALS._bShowSubtitles || _bAlwaysDisplay) {
734 			prim->getDst().topLeft() = _dst;
735 			CORO_INVOKE_2(RMText::draw, bigBuf, prim);
736 		}
737 	}
738 
739 	CORO_END_CODE;
740 }
741 
setCustomSkipHandle(uint32 hCustom)742 void RMTextDialog::setCustomSkipHandle(uint32 hCustom) {
743 	_hCustomSkip = hCustom;
744 }
745 
setCustomSkipHandle2(uint32 hCustom)746 void RMTextDialog::setCustomSkipHandle2(uint32 hCustom) {
747 	_hCustomSkip2 = hCustom;
748 }
749 
waitForEndDisplay(CORO_PARAM)750 void RMTextDialog::waitForEndDisplay(CORO_PARAM) {
751 	CoroScheduler.waitForSingleObject(coroParam, _hEndDisplay, CORO_INFINITE);
752 }
753 
setInput(RMInput * input)754 void RMTextDialog::setInput(RMInput *input) {
755 	_input = input;
756 }
757 
758 /**
759  * Set the position
760  */
setPosition(const RMPoint & pt)761 void RMTextDialog::setPosition(const RMPoint &pt) {
762 	_dst = pt;
763 }
764 
765 /****************************************************************************\
766 *       RMTextDialogScrolling Methods
767 \****************************************************************************/
768 
RMTextDialogScrolling()769 RMTextDialogScrolling::RMTextDialogScrolling() {
770 	_curLoc = NULL;
771 }
772 
RMTextDialogScrolling(RMLocation * loc)773 RMTextDialogScrolling::RMTextDialogScrolling(RMLocation *loc) {
774 	_curLoc = loc;
775 	_startScroll = loc->scrollPosition();
776 }
777 
~RMTextDialogScrolling()778 RMTextDialogScrolling::~RMTextDialogScrolling() {
779 }
780 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)781 void RMTextDialogScrolling::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
782 	CORO_BEGIN_CONTEXT;
783 	RMPoint curDst;
784 	CORO_END_CONTEXT(_ctx);
785 
786 	CORO_BEGIN_CODE(_ctx);
787 
788 	_ctx->curDst = _dst;
789 
790 	if (_curLoc != NULL)
791 		_dst -= _curLoc->scrollPosition() - _startScroll;
792 
793 	CORO_INVOKE_2(RMTextDialog::draw, bigBuf, prim);
794 
795 	_dst = _ctx->curDst;
796 
797 	CORO_END_CODE;
798 }
799 
clipOnScreen(RMGfxPrimitive * prim)800 void RMTextDialogScrolling::clipOnScreen(RMGfxPrimitive *prim) {
801 	// We must not do anything!
802 }
803 
804 /****************************************************************************\
805 *       RMTextItemName Methods
806 \****************************************************************************/
807 
RMTextItemName()808 RMTextItemName::RMTextItemName() : RMText() {
809 	_item = NULL;
810 	setPriority(220);
811 }
812 
~RMTextItemName()813 RMTextItemName::~RMTextItemName() {
814 }
815 
doFrame(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMLocation & loc,RMPointer & ptr,RMInventory & inv)816 void RMTextItemName::doFrame(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMLocation &loc, RMPointer &ptr, RMInventory &inv) {
817 	CORO_BEGIN_CONTEXT;
818 	RMItem *lastItem;
819 	uint32 hThread;
820 	CORO_END_CONTEXT(_ctx);
821 
822 	Common::String itemName;
823 
824 	CORO_BEGIN_CODE(_ctx);
825 
826 	_ctx->lastItem = _item;
827 
828 	// Adds to the list if there is need
829 	if (!_nInList)
830 		bigBuf.addPrim(new RMGfxPrimitive(this));
831 
832 	// Update the scrolling co-ordinates
833 	_curscroll = loc.scrollPosition();
834 
835 	// Check if we are on the inventory
836 	if (inv.itemInFocus(_mpos))
837 		_item = inv.whichItemIsIn(_mpos);
838 	else
839 		_item = loc.whichItemIsIn(_mpos);
840 
841 	// If there an item, get its name
842 	if (_item != NULL)
843 		_item->getName(itemName);
844 
845 	// Write it
846 	writeText(itemName, 1);
847 
848 	// Handle the change If the selected item is different from the previous one
849 	if (_ctx->lastItem != _item) {
850 		if (_item == NULL)
851 			ptr.setSpecialPointer(RMPointer::PTR_NONE);
852 		else {
853 			_ctx->hThread = mpalQueryDoAction(20, _item->mpalCode(), 0);
854 			if (_ctx->hThread == CORO_INVALID_PID_VALUE)
855 				ptr.setSpecialPointer(RMPointer::PTR_NONE);
856 			else
857 				CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->hThread, CORO_INFINITE);
858 		}
859 	}
860 
861 	CORO_END_CODE;
862 }
863 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)864 void RMTextItemName::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
865 	CORO_BEGIN_CONTEXT;
866 	CORO_END_CONTEXT(_ctx);
867 
868 	CORO_BEGIN_CODE(_ctx);
869 
870 	// If there is no text, it's pointless to continue
871 	if (_buf == NULL)
872 		return;
873 
874 	// Set the destination coordinates of the mouse
875 	prim->getDst().topLeft() = _mpos - RMPoint(0, 30);
876 
877 	CORO_INVOKE_2(RMText::draw, bigBuf, prim);
878 
879 	CORO_END_CODE;
880 }
881 
getHotspot()882 RMPoint RMTextItemName::getHotspot() {
883 	if (_item == NULL)
884 		return _mpos + _curscroll;
885 	else
886 		return _item->getHotspot();
887 }
888 
getSelectedItem()889 RMItem *RMTextItemName::getSelectedItem() {
890 	return _item;
891 }
892 
isItemSelected()893 bool RMTextItemName::isItemSelected() {
894 	return _item != NULL;
895 }
896 
setMouseCoord(const RMPoint & m)897 void RMTextItemName::setMouseCoord(const RMPoint &m) {
898 	_mpos = m;
899 }
900 
removeThis(CORO_PARAM,bool & result)901 void RMTextItemName::removeThis(CORO_PARAM, bool &result) {
902 	result = true;
903 }
904 
905 /****************************************************************************\
906 *       RMDialogChoice Methods
907 \****************************************************************************/
908 
RMDialogChoice()909 RMDialogChoice::RMDialogChoice() {
910 	RMResRaw dlg1(RES_I_DLGTEXT);
911 	RMResRaw dlg2(RES_I_DLGTEXTLINE);
912 	RMRes dlgpal(RES_I_DLGTEXTPAL);
913 
914 	_dlgText.init(dlg1, dlg1.width(), dlg1.height());
915 	_dlgTextLine.init(dlg2, dlg2.width(), dlg2.height());
916 
917 	_dlgText.loadPaletteWA(dlgpal);
918 	_dlgTextLine.loadPaletteWA(dlgpal);
919 
920 	_hUnreg = CoroScheduler.createEvent(false, false);
921 	_bRemoveFromOT = false;
922 
923 	_curAdded = 0;
924 	_bShow = false;
925 
926 	_curSelection = 0;
927 	_numChoices = 0;
928 
929 	_drawedStrings = NULL;
930 	_ptDrawStrings = NULL;
931 }
932 
~RMDialogChoice()933 RMDialogChoice::~RMDialogChoice() {
934 	CoroScheduler.closeEvent(_hUnreg);
935 }
936 
unregister()937 void RMDialogChoice::unregister() {
938 	RMGfxWoodyBuffer::unregister();
939 	assert(!_nInList);
940 	CoroScheduler.pulseEvent(_hUnreg);
941 
942 	_bRemoveFromOT = false;
943 }
944 
init()945 void RMDialogChoice::init() {
946 	_numChoices = 0;
947 	_drawedStrings = NULL;
948 	_ptDrawStrings = NULL;
949 	_curSelection = -1;
950 
951 	create(640, 477);
952 	setPriority(140);
953 }
954 
close()955 void RMDialogChoice::close() {
956 	if (_drawedStrings != NULL) {
957 		delete[] _drawedStrings;
958 		_drawedStrings = NULL;
959 	}
960 
961 	if (_ptDrawStrings != NULL) {
962 		delete[] _ptDrawStrings;
963 		_ptDrawStrings = NULL;
964 	}
965 
966 	destroy();
967 }
968 
setNumChoices(int num)969 void RMDialogChoice::setNumChoices(int num) {
970 	_numChoices = num;
971 	_curAdded = 0;
972 
973 	// Allocate space for drawn strings
974 	_drawedStrings = new RMText[num];
975 	_ptDrawStrings = new RMPoint[num];
976 
977 	// Initialization
978 	for (int i = 0; i < _numChoices; i++) {
979 		_drawedStrings[i].setColor(0, 255, 0);
980 		_drawedStrings[i].setAlignType(RMText::HLEFTPAR, RMText::VTOP);
981 		_drawedStrings[i].setMaxLineLength(600);
982 		_drawedStrings[i].setPriority(10);
983 	}
984 }
985 
addChoice(const Common::String & string)986 void RMDialogChoice::addChoice(const Common::String &string) {
987 	// Draw the string
988 	assert(_curAdded < _numChoices);
989 	_drawedStrings[_curAdded++].writeText(string, 0);
990 }
991 
prepare(CORO_PARAM)992 void RMDialogChoice::prepare(CORO_PARAM) {
993 	CORO_BEGIN_CONTEXT;
994 	int i;
995 	RMPoint ptPos;
996 	CORO_END_CONTEXT(_ctx);
997 
998 	CORO_BEGIN_CODE(_ctx);
999 
1000 	addPrim(new RMGfxPrimitive(&_dlgText, RMPoint(0, 0)));
1001 	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155)));
1002 	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83)));
1003 	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83)));
1004 	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83 + 83)));
1005 
1006 	_ctx->ptPos.set(20, 90);
1007 
1008 	for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
1009 		addPrim(new RMGfxPrimitive(&_drawedStrings[_ctx->i], _ctx->ptPos));
1010 		_ptDrawStrings[_ctx->i] = _ctx->ptPos;
1011 		_ctx->ptPos.offset(0, _drawedStrings[_ctx->i].getDimy() + 15);
1012 	}
1013 
1014 	CORO_INVOKE_0(drawOT);
1015 	clearOT();
1016 
1017 	_ptDrawPos.set(0, 480 - _ctx->ptPos._y);
1018 
1019 	CORO_END_CODE;
1020 }
1021 
setSelected(CORO_PARAM,int pos)1022 void RMDialogChoice::setSelected(CORO_PARAM, int pos) {
1023 	CORO_BEGIN_CONTEXT;
1024 	RMGfxBox box;
1025 	RMRect rc;
1026 	CORO_END_CONTEXT(_ctx);
1027 
1028 	CORO_BEGIN_CODE(_ctx);
1029 
1030 	if (pos == _curSelection)
1031 		return;
1032 
1033 	_ctx->box.setPriority(5);
1034 
1035 	if (_curSelection != -1) {
1036 		_ctx->box.setColor(0xCC, 0xCC, 0xFF);
1037 		_ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[_curSelection]._y);
1038 		_ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[_curSelection].getDimy());
1039 		addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
1040 
1041 		addPrim(new RMGfxPrimitive(&_drawedStrings[_curSelection], _ptDrawStrings[_curSelection]));
1042 		CORO_INVOKE_0(drawOT);
1043 		clearOT();
1044 	}
1045 
1046 	if (pos != -1) {
1047 		_ctx->box.setColor(100, 100, 100);
1048 		_ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[pos]._y);
1049 		_ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[pos].getDimy());
1050 		addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
1051 		addPrim(new RMGfxPrimitive(&_drawedStrings[pos], _ptDrawStrings[pos]));
1052 	}
1053 
1054 	CORO_INVOKE_0(drawOT);
1055 	clearOT();
1056 
1057 	_curSelection = pos;
1058 
1059 	CORO_END_CODE;
1060 }
1061 
show(CORO_PARAM,RMGfxTargetBuffer * bigBuf)1062 void RMDialogChoice::show(CORO_PARAM, RMGfxTargetBuffer *bigBuf) {
1063 	CORO_BEGIN_CONTEXT;
1064 	RMPoint destpt;
1065 	int deltay;
1066 	int starttime;
1067 	int elaps;
1068 	CORO_END_CONTEXT(_ctx);
1069 
1070 	CORO_BEGIN_CODE(_ctx);
1071 
1072 	CORO_INVOKE_0(prepare);
1073 	_bShow = false;
1074 
1075 	if (!_nInList && bigBuf != NULL)
1076 		bigBuf->addPrim(new RMGfxPrimitive(this));
1077 
1078 	if (0) {
1079 		_bShow = true;
1080 	} else {
1081 		_ctx->starttime = g_vm->getTime();
1082 		_ctx->deltay = 480 - _ptDrawPos._y;
1083 		_ctx->destpt = _ptDrawPos;
1084 		_ptDrawPos.set(0, 480);
1085 
1086 		if (!_nInList && bigBuf != NULL)
1087 			bigBuf->addPrim(new RMGfxPrimitive(this));
1088 		_bShow = true;
1089 
1090 		_ctx->elaps = 0;
1091 		while (_ctx->elaps < 700) {
1092 			CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
1093 			_ctx->elaps = g_vm->getTime() - _ctx->starttime;
1094 			_ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * _ctx->elaps) / 100;
1095 		}
1096 
1097 		_ptDrawPos._y = _ctx->destpt._y;
1098 	}
1099 
1100 	CORO_END_CODE;
1101 }
1102 
draw(CORO_PARAM,RMGfxTargetBuffer & bigBuf,RMGfxPrimitive * prim)1103 void RMDialogChoice::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
1104 	CORO_BEGIN_CONTEXT;
1105 	CORO_END_CONTEXT(_ctx);
1106 
1107 	CORO_BEGIN_CODE(_ctx);
1108 
1109 	if (_bShow == false)
1110 		return;
1111 
1112 	prim->setDst(_ptDrawPos);
1113 	CORO_INVOKE_2(RMGfxSourceBuffer16::draw, bigBuf, prim);
1114 
1115 	CORO_END_CODE;
1116 }
1117 
hide(CORO_PARAM)1118 void RMDialogChoice::hide(CORO_PARAM) {
1119 	CORO_BEGIN_CONTEXT;
1120 	int deltay;
1121 	int starttime;
1122 	int elaps;
1123 	CORO_END_CONTEXT(_ctx);
1124 
1125 	CORO_BEGIN_CODE(_ctx);
1126 
1127 	if (1) {
1128 		_ctx->starttime = g_vm->getTime();
1129 
1130 		_ctx->deltay = 480 - _ptDrawPos._y;
1131 		_ctx->elaps = 0;
1132 		while (_ctx->elaps < 700) {
1133 			CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
1134 			_ctx->elaps = g_vm->getTime() - _ctx->starttime;
1135 			_ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * (700 - _ctx->elaps)) / 100;
1136 		}
1137 	}
1138 
1139 	_bShow = false;
1140 	_bRemoveFromOT = true;
1141 	CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hUnreg, CORO_INFINITE);
1142 
1143 	CORO_END_CODE;
1144 }
1145 
removeThis(CORO_PARAM,bool & result)1146 void RMDialogChoice::removeThis(CORO_PARAM, bool &result) {
1147 	result = _bRemoveFromOT;
1148 }
1149 
doFrame(CORO_PARAM,RMPoint ptMousePos)1150 void RMDialogChoice::doFrame(CORO_PARAM, RMPoint ptMousePos) {
1151 	CORO_BEGIN_CONTEXT;
1152 	int i;
1153 	CORO_END_CONTEXT(_ctx);
1154 
1155 	CORO_BEGIN_CODE(_ctx);
1156 
1157 	if (ptMousePos._y > _ptDrawPos._y) {
1158 		for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
1159 			if ((ptMousePos._y >= _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y) && (ptMousePos._y < _ptDrawPos._y + _ptDrawStrings[_ctx->i]._y + _drawedStrings[_ctx->i].getDimy())) {
1160 				CORO_INVOKE_1(setSelected, _ctx->i);
1161 				break;
1162 			}
1163 		}
1164 
1165 		if (_ctx->i == _numChoices)
1166 			CORO_INVOKE_1(setSelected, -1);
1167 	}
1168 
1169 	CORO_END_CODE;
1170 }
1171 
getSelection()1172 int RMDialogChoice::getSelection() {
1173 	return _curSelection;
1174 }
1175 
1176 } // End of namespace Tony
1177