1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14
15 #include "ac/display.h"
16 #include "ac/common.h"
17 #include "font/agsfontrenderer.h"
18 #include "font/fonts.h"
19 #include "ac/character.h"
20 #include "ac/draw.h"
21 #include "ac/game.h"
22 #include "ac/gamesetupstruct.h"
23 #include "ac/gamestate.h"
24 #include "ac/global_audio.h"
25 #include "ac/global_game.h"
26 #include "ac/gui.h"
27 #include "ac/mouse.h"
28 #include "ac/overlay.h"
29 #include "ac/record.h"
30 #include "ac/screenoverlay.h"
31 #include "ac/speech.h"
32 #include "ac/string.h"
33 #include "ac/system.h"
34 #include "ac/topbarsettings.h"
35 #include "debug/debug_log.h"
36 #include "gui/guibutton.h"
37 #include "gui/guimain.h"
38 #include "main/game_run.h"
39 #include "media/audio/audio.h"
40 #include "platform/base/agsplatformdriver.h"
41 #include "ac/spritecache.h"
42 #include "gfx/gfx_util.h"
43 #include "util/string_utils.h"
44
45 using AGS::Common::Bitmap;
46 namespace BitmapHelper = AGS::Common::BitmapHelper;
47
48 extern GameState play;
49 extern GameSetupStruct game;
50 extern int longestline;
51 extern Bitmap *virtual_screen;
52 extern ScreenOverlay screenover[MAX_SCREEN_OVERLAYS];
53 extern volatile int timerloop;
54 extern AGSPlatformDriver *platform;
55 extern volatile unsigned long globalTimerCounter;
56 extern int time_between_timers;
57 extern int offsetx, offsety;
58 extern int frames_per_second;
59 extern int loops_per_character;
60 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
61 extern SpriteCache spriteset;
62
63 int display_message_aschar=0;
64
65
66 TopBarSettings topBar;
67 struct DisplayVars
68 {
69 int lineheight; // font's height of single line
70 int linespacing; // font's line spacing
71 int fulltxtheight; // total height of all the text
72 } disp;
73
74 // Pass yy = -1 to find Y co-ord automatically
75 // allowShrink = 0 for none, 1 for leftwards, 2 for rightwards
76 // pass blocking=2 to create permanent overlay
_display_main(int xx,int yy,int wii,const char * text,int blocking,int usingfont,int asspch,int isThought,int allowShrink,bool overlayPositionFixed)77 int _display_main(int xx,int yy,int wii,const char*text,int blocking,int usingfont,int asspch, int isThought, int allowShrink, bool overlayPositionFixed)
78 {
79 const bool use_speech_textwindow = (asspch < 0) && (game.options[OPT_SPEECHTYPE] >= 2);
80 const bool use_thought_gui = (isThought) && (game.options[OPT_THOUGHTGUI] > 0);
81
82 bool alphaChannel = false;
83 char todis[STD_BUFFER_SIZE];
84 snprintf(todis, STD_BUFFER_SIZE - 1, "%s", text);
85 int usingGui = -1;
86 if (use_speech_textwindow)
87 usingGui = play.speech_textwindow_gui;
88 else if (use_thought_gui)
89 usingGui = game.options[OPT_THOUGHTGUI];
90
91 int padding = get_textwindow_padding(usingGui);
92 int paddingScaled = get_fixed_pixel_size(padding);
93 int paddingDoubledScaled = get_fixed_pixel_size(padding * 2); // Just in case screen size does is not neatly divisible by 320x200
94
95 ensure_text_valid_for_font(todis, usingfont);
96 break_up_text_into_lines(wii-2*padding,usingfont,todis);
97 disp.lineheight = getfontheight_outlined(usingfont);
98 disp.linespacing= getfontspacing_outlined(usingfont);
99 disp.fulltxtheight = getheightoflines(usingfont, numlines);
100
101 // AGS 2.x: If the screen is faded out, fade in again when displaying a message box.
102 if (!asspch && (loaded_game_file_version <= kGameVersion_272))
103 play.screen_is_faded_out = 0;
104
105 // if it's a normal message box and the game was being skipped,
106 // ensure that the screen is up to date before the message box
107 // is drawn on top of it
108 if ((play.skip_until_char_stops >= 0) && (blocking == 1))
109 render_graphics();
110
111 EndSkippingUntilCharStops();
112
113 if (topBar.wantIt) {
114 // ensure that the window is wide enough to display
115 // any top bar text
116 int topBarWid = wgettextwidth_compensate(topBar.text, topBar.font);
117 topBarWid += multiply_up_coordinate(play.top_bar_borderwidth + 2) * 2;
118 if (longestline < topBarWid)
119 longestline = topBarWid;
120 // the top bar should behave like DisplaySpeech wrt blocking
121 blocking = 0;
122 }
123
124 if (asspch > 0) {
125 // update the all_buttons_disabled variable in advance
126 // of the adjust_x/y_for_guis calls
127 play.disabled_user_interface++;
128 update_gui_disabled_status();
129 play.disabled_user_interface--;
130 }
131
132 if (xx == OVR_AUTOPLACE) ;
133 // centre text in middle of screen
134 else if (yy<0) yy=play.viewport.GetHeight()/2-disp.fulltxtheight/2-padding;
135 // speech, so it wants to be above the character's head
136 else if (asspch > 0) {
137 yy-=disp.fulltxtheight;
138 if (yy < 5) yy=5;
139 yy = adjust_y_for_guis (yy);
140 }
141
142 if (longestline < wii - paddingDoubledScaled) {
143 // shrink the width of the dialog box to fit the text
144 int oldWid = wii;
145 //if ((asspch >= 0) || (allowShrink > 0))
146 // If it's not speech, or a shrink is allowed, then shrink it
147 if ((asspch == 0) || (allowShrink > 0))
148 wii = longestline + paddingDoubledScaled;
149
150 // shift the dialog box right to align it, if necessary
151 if ((allowShrink == 2) && (xx >= 0))
152 xx += (oldWid - wii);
153 }
154
155 if (xx<-1) {
156 xx=(-xx)-wii/2;
157 if (xx < 0)
158 xx = 0;
159
160 xx = adjust_x_for_guis (xx, yy);
161
162 if (xx + wii >= play.viewport.GetWidth())
163 xx = (play.viewport.GetWidth() - wii) - 5;
164 }
165 else if (xx<0) xx=play.viewport.GetWidth()/2-wii/2;
166
167 int ee, extraHeight = paddingDoubledScaled;
168 Bitmap *ds = GetVirtualScreen();
169 color_t text_color = ds->GetCompatibleColor(15);
170 if (blocking < 2)
171 remove_screen_overlay(OVER_TEXTMSG);
172
173 Bitmap *text_window_ds = BitmapHelper::CreateTransparentBitmap((wii > 0) ? wii : 2, disp.fulltxtheight + extraHeight, game.GetColorDepth());
174 SetVirtualScreen(text_window_ds);
175
176 // inform draw_text_window to free the old bitmap
177 const bool wantFreeScreenop = true;
178
179 if ((strlen (todis) < 1) || (strcmp (todis, " ") == 0) || (wii == 0)) ;
180 // if it's an empty speech line, don't draw anything
181 else if (asspch) { //text_color = ds->GetCompatibleColor(12);
182 int ttxleft = 0, ttxtop = paddingScaled, oriwid = wii - padding * 2;
183 int drawBackground = 0;
184
185 if (use_speech_textwindow) {
186 drawBackground = 1;
187 }
188 else if (use_thought_gui) {
189 // make it treat it as drawing inside a window now
190 if (asspch > 0)
191 asspch = -asspch;
192 drawBackground = 1;
193 }
194
195 if (drawBackground)
196 {
197 draw_text_window_and_bar(&text_window_ds, wantFreeScreenop, &ttxleft, &ttxtop, &xx, &yy, &wii, &text_color, 0, usingGui);
198 if (usingGui > 0)
199 {
200 alphaChannel = guis[usingGui].HasAlphaChannel();
201 }
202 }
203 else if ((ShouldAntiAliasText()) && (game.GetColorDepth() >= 24))
204 alphaChannel = true;
205
206 for (ee=0;ee<numlines;ee++) {
207 //int ttxp=wii/2 - wgettextwidth_compensate(lines[ee], usingfont)/2;
208 int ttyp=ttxtop+ee*disp.linespacing;
209 // asspch < 0 means that it's inside a text box so don't
210 // centre the text
211 if (asspch < 0) {
212 if ((usingGui >= 0) &&
213 ((game.options[OPT_SPEECHTYPE] >= 2) || (isThought)))
214 text_color = text_window_ds->GetCompatibleColor(guis[usingGui].FgColor);
215 else
216 text_color = text_window_ds->GetCompatibleColor(-asspch);
217
218 wouttext_aligned(text_window_ds, ttxleft, ttyp, oriwid, usingfont, text_color, lines[ee], play.text_align);
219 }
220 else {
221 text_color = text_window_ds->GetCompatibleColor(asspch);
222 //wouttext_outline(ttxp,ttyp,usingfont,lines[ee]);
223 wouttext_aligned(text_window_ds, ttxleft, ttyp, wii, usingfont, text_color, lines[ee], play.speech_text_align);
224 }
225 }
226 }
227 else {
228
229 int xoffs,yoffs, oriwid = wii - padding * 2;
230 draw_text_window_and_bar(&text_window_ds, wantFreeScreenop, &xoffs,&yoffs,&xx,&yy,&wii,&text_color);
231
232 if (game.options[OPT_TWCUSTOM] > 0)
233 {
234 alphaChannel = guis[game.options[OPT_TWCUSTOM]].HasAlphaChannel();
235 }
236
237 adjust_y_coordinate_for_text(&yoffs, usingfont);
238
239 for (ee=0;ee<numlines;ee++)
240 wouttext_aligned (text_window_ds, xoffs, yoffs + ee * disp.linespacing, oriwid, usingfont, text_color, lines[ee], play.text_align);
241 }
242
243 int ovrtype = OVER_TEXTMSG;
244 if (blocking == 2) ovrtype=OVER_CUSTOM;
245 else if (blocking >= OVER_CUSTOM) ovrtype=blocking;
246
247 int nse = add_screen_overlay(xx, yy, ovrtype, text_window_ds, alphaChannel);
248 // we should not delete text_window_ds here, because it is now owned by Overlay
249
250 ds = SetVirtualScreen(virtual_screen);
251 if (blocking>=2) {
252 return screenover[nse].type;
253 }
254
255 if (blocking) {
256 if (play.fast_forward) {
257 remove_screen_overlay(OVER_TEXTMSG);
258 play.messagetime=-1;
259 return 0;
260 }
261
262 /* wputblock(xx,yy,screenop,1);
263 remove_screen_overlay(OVER_TEXTMSG);*/
264
265 if (!play.mouse_cursor_hidden)
266 domouse(1);
267 // play.skip_display has same values as SetSkipSpeech:
268 // 0 = click mouse or key to skip
269 // 1 = key only
270 // 2 = can't skip at all
271 // 3 = only on keypress, no auto timer
272 // 4 = mouse only
273 int countdown = GetTextDisplayTime (todis);
274 int skip_setting = user_to_internal_skip_speech((SkipSpeechStyle)play.skip_display);
275 while (1) {
276 timerloop = 0;
277 NEXT_ITERATION();
278 /* if (!play.mouse_cursor_hidden)
279 domouse(0);
280 write_screen();*/
281
282 render_graphics();
283
284 update_polled_audio_and_crossfade();
285 if (mgetbutton()>NONE) {
286 // If we're allowed, skip with mouse
287 if (skip_setting & SKIP_MOUSECLICK)
288 break;
289 }
290 int kp;
291 if (run_service_key_controls(kp)) {
292 // let them press ESC to skip the cutscene
293 check_skip_cutscene_keypress (kp);
294 if (play.fast_forward)
295 break;
296
297 if (skip_setting & SKIP_KEYPRESS)
298 break;
299 }
300 update_polled_stuff_if_runtime();
301 if (play.fast_forward == 0) WaitForNextFrame();
302 countdown--;
303
304 if (channels[SCHAN_SPEECH] != NULL) {
305 // extend life of text if the voice hasn't finished yet
306 if ((!rec_isSpeechFinished()) && (play.fast_forward == 0)) {
307 if (countdown <= 1)
308 countdown = 1;
309 }
310 else // if the voice has finished, remove the speech
311 countdown = 0;
312 }
313
314 if ((countdown < 1) && (skip_setting & SKIP_AUTOTIMER))
315 {
316 play.ignore_user_input_until_time = globalTimerCounter + (play.ignore_user_input_after_text_timeout_ms / time_between_timers);
317 break;
318 }
319 // if skipping cutscene, don't get stuck on No Auto Remove
320 // text boxes
321 if ((countdown < 1) && (play.fast_forward))
322 break;
323 }
324 if (!play.mouse_cursor_hidden)
325 domouse(2);
326 remove_screen_overlay(OVER_TEXTMSG);
327
328 construct_virtual_screen(true);
329 }
330 else {
331 // if the speech does not time out, but we are skipping a cutscene,
332 // allow it to time out
333 if ((play.messagetime < 0) && (play.fast_forward))
334 play.messagetime = 2;
335
336 if (!overlayPositionFixed)
337 {
338 screenover[nse].positionRelativeToScreen = false;
339 screenover[nse].x += offsetx;
340 screenover[nse].y += offsety;
341 }
342
343 GameLoopUntilEvent(UNTIL_NOOVERLAY,0);
344 }
345
346 play.messagetime=-1;
347 return 0;
348 }
349
_display_at(int xx,int yy,int wii,const char * todis,int blocking,int asspch,int isThought,int allowShrink,bool overlayPositionFixed)350 void _display_at(int xx,int yy,int wii,const char*todis,int blocking,int asspch, int isThought, int allowShrink, bool overlayPositionFixed) {
351 int usingfont=FONT_NORMAL;
352 if (asspch) usingfont=FONT_SPEECH;
353 int needStopSpeech = 0;
354
355 EndSkippingUntilCharStops();
356
357 if (todis[0]=='&') {
358 // auto-speech
359 int igr=atoi(&todis[1]);
360 while ((todis[0]!=' ') & (todis[0]!=0)) todis++;
361 if (todis[0]==' ') todis++;
362 if (igr <= 0)
363 quit("Display: auto-voice symbol '&' not followed by valid integer");
364 if (play_speech(play.narrator_speech,igr)) {
365 // if Voice Only, then blank out the text
366 if (play.want_speech == 2)
367 todis = " ";
368 }
369 needStopSpeech = 1;
370 }
371 _display_main(xx,yy,wii,todis,blocking,usingfont,asspch, isThought, allowShrink, overlayPositionFixed);
372
373 if (needStopSpeech)
374 stop_speech();
375 }
376
377 // TODO: refactor this global variable out; currently it is set at the every get_translation call.
378 // Be careful: a number of Say/Display functions expect it to be set beforehand.
379 int source_text_length = -1;
380
GetTextDisplayLength(const char * text)381 int GetTextDisplayLength(const char *text)
382 {
383 int len = (int)strlen(text);
384 if ((text[0] == '&') && (play.unfactor_speech_from_textlength != 0))
385 {
386 // if there's an "&12 text" type line, remove "&12 " from the source length
387 size_t j = 0;
388 while ((text[j] != ' ') && (text[j] != 0))
389 j++;
390 j++;
391 len -= j;
392 }
393 return len;
394 }
395
GetTextDisplayTime(const char * text,int canberel)396 int GetTextDisplayTime(const char *text, int canberel) {
397 int uselen = 0;
398 int fpstimer = frames_per_second;
399
400 // if it's background speech, make it stay relative to game speed
401 if ((canberel == 1) && (play.bgspeech_game_speed == 1))
402 fpstimer = 40;
403
404 if (source_text_length >= 0) {
405 // sync to length of original text, to make sure any animations
406 // and music sync up correctly
407 uselen = source_text_length;
408 source_text_length = -1;
409 }
410 else {
411 uselen = GetTextDisplayLength(text);
412 }
413
414 if (uselen <= 0)
415 return 0;
416
417 if (play.text_speed + play.text_speed_modifier <= 0)
418 quit("!Text speed is zero; unable to display text. Check your game.text_speed settings.");
419
420 // Store how many game loops per character of text
421 // This is calculated using a hard-coded 15 for the text speed,
422 // so that it's always the same no matter how fast the user
423 // can read.
424 loops_per_character = (((uselen/play.lipsync_speed)+1) * fpstimer) / uselen;
425
426 int textDisplayTimeInMS = ((uselen / (play.text_speed + play.text_speed_modifier)) + 1) * 1000;
427 if (textDisplayTimeInMS < play.text_min_display_time_ms)
428 textDisplayTimeInMS = play.text_min_display_time_ms;
429
430 return (textDisplayTimeInMS * fpstimer) / 1000;
431 }
432
ShouldAntiAliasText()433 bool ShouldAntiAliasText() {
434 return (game.options[OPT_ANTIALIASFONTS] != 0);
435 }
436
wouttext_outline(Common::Bitmap * ds,int xxp,int yyp,int usingfont,color_t text_color,const char * texx)437 void wouttext_outline(Common::Bitmap *ds, int xxp, int yyp, int usingfont, color_t text_color, const char*texx) {
438
439 color_t outline_color = ds->GetCompatibleColor(play.speech_text_shadow);
440 if (get_font_outline(usingfont) >= 0) {
441 // MACPORT FIX 9/6/5: cast
442 wouttextxy(ds, xxp, yyp, (int)get_font_outline(usingfont), outline_color, texx);
443 }
444 else if (get_font_outline(usingfont) == FONT_OUTLINE_AUTO) {
445 int outlineDist = 1;
446
447 if ((game.options[OPT_NOSCALEFNT] == 0) && (!font_supports_extended_characters(usingfont))) {
448 // if it's a scaled up SCI font, move the outline out more
449 outlineDist = get_fixed_pixel_size(1);
450 }
451
452 // move the text over so that it's still within the bounding rect
453 xxp += outlineDist;
454 yyp += outlineDist;
455
456 wouttextxy(ds, xxp - outlineDist, yyp, usingfont, outline_color, texx);
457 wouttextxy(ds, xxp + outlineDist, yyp, usingfont, outline_color, texx);
458 wouttextxy(ds, xxp, yyp + outlineDist, usingfont, outline_color, texx);
459 wouttextxy(ds, xxp, yyp - outlineDist, usingfont, outline_color, texx);
460 wouttextxy(ds, xxp - outlineDist, yyp - outlineDist, usingfont, outline_color, texx);
461 wouttextxy(ds, xxp - outlineDist, yyp + outlineDist, usingfont, outline_color, texx);
462 wouttextxy(ds, xxp + outlineDist, yyp + outlineDist, usingfont, outline_color, texx);
463 wouttextxy(ds, xxp + outlineDist, yyp - outlineDist, usingfont, outline_color, texx);
464 }
465
466 wouttextxy(ds, xxp, yyp, usingfont, text_color, texx);
467 }
468
wouttext_aligned(Bitmap * ds,int usexp,int yy,int oriwid,int usingfont,color_t text_color,const char * text,int align)469 void wouttext_aligned (Bitmap *ds, int usexp, int yy, int oriwid, int usingfont, color_t text_color, const char *text, int align) {
470
471 if (align == SCALIGN_CENTRE)
472 usexp = usexp + (oriwid / 2) - (wgettextwidth_compensate(text, usingfont) / 2);
473 else if (align == SCALIGN_RIGHT)
474 usexp = usexp + (oriwid - wgettextwidth_compensate(text, usingfont));
475
476 wouttext_outline(ds, usexp, yy, usingfont, text_color, (char *)text);
477 }
478
get_outline_adjustment(int font)479 int get_outline_adjustment(int font)
480 {
481 // automatic outline fonts are 2 pixels taller
482 if (get_font_outline(font) == FONT_OUTLINE_AUTO) {
483 // scaled up SCI font, push outline further out
484 if ((game.options[OPT_NOSCALEFNT] == 0) && (!font_supports_extended_characters(font)))
485 return get_fixed_pixel_size(2);
486 // otherwise, just push outline by 1 pixel
487 else
488 return 2;
489 }
490 return 0;
491 }
492
getfontheight_outlined(int font)493 int getfontheight_outlined(int font)
494 {
495 return getfontheight(font) + get_outline_adjustment(font);
496 }
497
getfontspacing_outlined(int font)498 int getfontspacing_outlined(int font)
499 {
500 return use_default_linespacing(font) ?
501 getfontheight_outlined(font) :
502 getfontlinespacing(font);
503 }
504
getfontlinegap(int font)505 int getfontlinegap(int font)
506 {
507 return getfontspacing_outlined(font) - getfontheight_outlined(font);
508 }
509
getheightoflines(int font,int numlines)510 int getheightoflines(int font, int numlines)
511 {
512 return getfontspacing_outlined(font) * (numlines - 1) + getfontheight_outlined(font);
513 }
514
wgettextwidth_compensate(const char * tex,int font)515 int wgettextwidth_compensate(const char *tex, int font) {
516 int wdof = wgettextwidth(tex, font);
517
518 if (get_font_outline(font) == FONT_OUTLINE_AUTO) {
519 // scaled up SCI font, push outline further out
520 if ((game.options[OPT_NOSCALEFNT] == 0) && (!font_supports_extended_characters(font)))
521 wdof += get_fixed_pixel_size(2);
522 // otherwise, just push outline by 1 pixel
523 else
524 wdof += get_fixed_pixel_size(1);
525 }
526
527 return wdof;
528 }
529
do_corner(Bitmap * ds,int sprn,int x,int y,int offx,int offy)530 void do_corner(Bitmap *ds, int sprn, int x, int y, int offx, int offy) {
531 if (sprn<0) return;
532 if (spriteset[sprn] == NULL)
533 {
534 sprn = 0;
535 }
536
537 x = x + offx * spritewidth[sprn];
538 y = y + offy * spriteheight[sprn];
539 draw_gui_sprite_v330(ds, sprn, x, y);
540 }
541
get_but_pic(GUIMain * guo,int indx)542 int get_but_pic(GUIMain*guo,int indx) {
543 return guibuts[guo->CtrlRefs[indx] & 0x000ffff].pic;
544 }
545
draw_button_background(Bitmap * ds,int xx1,int yy1,int xx2,int yy2,GUIMain * iep)546 void draw_button_background(Bitmap *ds, int xx1,int yy1,int xx2,int yy2,GUIMain*iep) {
547 color_t draw_color;
548 if (iep==NULL) { // standard window
549 draw_color = ds->GetCompatibleColor(15);
550 ds->FillRect(Rect(xx1,yy1,xx2,yy2), draw_color);
551 draw_color = ds->GetCompatibleColor(16);
552 ds->DrawRect(Rect(xx1,yy1,xx2,yy2), draw_color);
553 /* draw_color = ds->GetCompatibleColor(opts.tws.backcol); ds->FillRect(Rect(xx1,yy1,xx2,yy2);
554 draw_color = ds->GetCompatibleColor(opts.tws.ds->GetTextColor()); ds->DrawRect(Rect(xx1+1,yy1+1,xx2-1,yy2-1);*/
555 }
556 else {
557 if (loaded_game_file_version < kGameVersion_262) // < 2.62
558 {
559 // Color 0 wrongly shows as transparent instead of black
560 // From the changelog of 2.62:
561 // - Fixed text windows getting a black background if colour 0 was
562 // specified, rather than being transparent.
563 if (iep->BgColor == 0)
564 iep->BgColor = 16;
565 }
566
567 if (iep->BgColor >= 0) draw_color = ds->GetCompatibleColor(iep->BgColor);
568 else draw_color = ds->GetCompatibleColor(0); // black backrgnd behind picture
569
570 if (iep->BgColor > 0)
571 ds->FillRect(Rect(xx1,yy1,xx2,yy2), draw_color);
572
573 int leftRightWidth = spritewidth[get_but_pic(iep,4)];
574 int topBottomHeight = spriteheight[get_but_pic(iep,6)];
575 if (iep->BgImage>0) {
576 if ((loaded_game_file_version <= kGameVersion_272) // 2.xx
577 && (spriteset[iep->BgImage]->GetWidth() == 1)
578 && (spriteset[iep->BgImage]->GetHeight() == 1)
579 && (*((unsigned int*)spriteset[iep->BgImage]->GetData()) == 0x00FF00FF))
580 {
581 // Don't draw fully transparent dummy GUI backgrounds
582 }
583 else
584 {
585 // offset the background image and clip it so that it is drawn
586 // such that the border graphics can have a transparent outside
587 // edge
588 int bgoffsx = xx1 - leftRightWidth / 2;
589 int bgoffsy = yy1 - topBottomHeight / 2;
590 ds->SetClip(Rect(bgoffsx, bgoffsy, xx2 + leftRightWidth / 2, yy2 + topBottomHeight / 2));
591 int bgfinishx = xx2;
592 int bgfinishy = yy2;
593 int bgoffsyStart = bgoffsy;
594 while (bgoffsx <= bgfinishx)
595 {
596 bgoffsy = bgoffsyStart;
597 while (bgoffsy <= bgfinishy)
598 {
599 draw_gui_sprite_v330(ds, iep->BgImage, bgoffsx, bgoffsy);
600 bgoffsy += spriteheight[iep->BgImage];
601 }
602 bgoffsx += spritewidth[iep->BgImage];
603 }
604 // return to normal clipping rectangle
605 ds->SetClip(Rect(0, 0, ds->GetWidth() - 1, ds->GetHeight() - 1));
606 }
607 }
608 int uu;
609 for (uu=yy1;uu <= yy2;uu+=spriteheight[get_but_pic(iep,4)]) {
610 do_corner(ds, get_but_pic(iep,4),xx1,uu,-1,0); // left side
611 do_corner(ds, get_but_pic(iep,5),xx2+1,uu,0,0); // right side
612 }
613 for (uu=xx1;uu <= xx2;uu+=spritewidth[get_but_pic(iep,6)]) {
614 do_corner(ds, get_but_pic(iep,6),uu,yy1,0,-1); // top side
615 do_corner(ds, get_but_pic(iep,7),uu,yy2+1,0,0); // bottom side
616 }
617 do_corner(ds, get_but_pic(iep,0),xx1,yy1,-1,-1); // top left
618 do_corner(ds, get_but_pic(iep,1),xx1,yy2+1,-1,0); // bottom left
619 do_corner(ds, get_but_pic(iep,2),xx2+1,yy1,0,-1); // top right
620 do_corner(ds, get_but_pic(iep,3),xx2+1,yy2+1,0,0); // bottom right
621 }
622 }
623
624 // Calculate the width that the left and right border of the textwindow
625 // GUI take up
get_textwindow_border_width(int twgui)626 int get_textwindow_border_width (int twgui) {
627 if (twgui < 0)
628 return 0;
629
630 if (!guis[twgui].IsTextWindow())
631 quit("!GUI set as text window but is not actually a text window GUI");
632
633 int borwid = spritewidth[get_but_pic(&guis[twgui], 4)] +
634 spritewidth[get_but_pic(&guis[twgui], 5)];
635
636 return borwid;
637 }
638
639 // get the hegiht of the text window's top border
get_textwindow_top_border_height(int twgui)640 int get_textwindow_top_border_height (int twgui) {
641 if (twgui < 0)
642 return 0;
643
644 if (!guis[twgui].IsTextWindow())
645 quit("!GUI set as text window but is not actually a text window GUI");
646
647 return spriteheight[get_but_pic(&guis[twgui], 6)];
648 }
649
650 // Get the padding for a text window
651 // -1 for the game's custom text window
get_textwindow_padding(int ifnum)652 int get_textwindow_padding(int ifnum) {
653 int result;
654
655 if (ifnum < 0)
656 ifnum = game.options[OPT_TWCUSTOM];
657 if (ifnum > 0 && ifnum < game.numgui)
658 result = guis[ifnum].Padding;
659 else
660 result = TEXTWINDOW_PADDING_DEFAULT;
661
662 return result;
663 }
664
draw_text_window(Bitmap ** text_window_ds,bool should_free_ds,int * xins,int * yins,int * xx,int * yy,int * wii,color_t * set_text_color,int ovrheight,int ifnum)665 void draw_text_window(Bitmap **text_window_ds, bool should_free_ds,
666 int*xins,int*yins,int*xx,int*yy,int*wii, color_t *set_text_color, int ovrheight, int ifnum) {
667
668 Bitmap *ds = *text_window_ds;
669 if (ifnum < 0)
670 ifnum = game.options[OPT_TWCUSTOM];
671
672 if (ifnum <= 0) {
673 if (ovrheight)
674 quit("!Cannot use QFG4 style options without custom text window");
675 draw_button_background(ds, 0,0,ds->GetWidth() - 1,ds->GetHeight() - 1,NULL);
676 if (set_text_color)
677 *set_text_color = ds->GetCompatibleColor(16);
678 xins[0]=3;
679 yins[0]=3;
680 }
681 else {
682 if (ifnum >= game.numgui)
683 quitprintf("!Invalid GUI %d specified as text window (total GUIs: %d)", ifnum, game.numgui);
684 if (!guis[ifnum].IsTextWindow())
685 quit("!GUI set as text window but is not actually a text window GUI");
686
687 int tbnum = get_but_pic(&guis[ifnum], 0);
688
689 wii[0] += get_textwindow_border_width (ifnum);
690 xx[0]-=spritewidth[tbnum];
691 yy[0]-=spriteheight[tbnum];
692 if (ovrheight == 0)
693 ovrheight = disp.fulltxtheight;
694
695 if (should_free_ds)
696 delete *text_window_ds;
697 int padding = get_textwindow_padding(ifnum);
698 *text_window_ds = BitmapHelper::CreateTransparentBitmap(wii[0],ovrheight+(padding*2)+spriteheight[tbnum]*2,game.GetColorDepth());
699 ds = SetVirtualScreen(*text_window_ds);
700 int xoffs=spritewidth[tbnum],yoffs=spriteheight[tbnum];
701 draw_button_background(ds, xoffs,yoffs,(ds->GetWidth() - xoffs) - 1,(ds->GetHeight() - yoffs) - 1,&guis[ifnum]);
702 if (set_text_color)
703 *set_text_color = ds->GetCompatibleColor(guis[ifnum].FgColor);
704 xins[0]=xoffs+padding;
705 yins[0]=yoffs+padding;
706 }
707
708 }
709
draw_text_window_and_bar(Bitmap ** text_window_ds,bool should_free_ds,int * xins,int * yins,int * xx,int * yy,int * wii,color_t * set_text_color,int ovrheight,int ifnum)710 void draw_text_window_and_bar(Bitmap **text_window_ds, bool should_free_ds,
711 int*xins,int*yins,int*xx,int*yy,int*wii,color_t *set_text_color,int ovrheight, int ifnum) {
712
713 draw_text_window(text_window_ds, should_free_ds, xins, yins, xx, yy, wii, set_text_color, ovrheight, ifnum);
714
715 if ((topBar.wantIt) && (text_window_ds && *text_window_ds)) {
716 // top bar on the dialog window with character's name
717 // create an enlarged window, then free the old one
718 Bitmap *ds = *text_window_ds;
719 Bitmap *newScreenop = BitmapHelper::CreateBitmap(ds->GetWidth(), ds->GetHeight() + topBar.height, game.GetColorDepth());
720 newScreenop->Blit(ds, 0, 0, 0, topBar.height, ds->GetWidth(), ds->GetHeight());
721 delete *text_window_ds;
722 *text_window_ds = newScreenop;
723 ds = SetVirtualScreen(*text_window_ds);
724
725 // draw the top bar
726 color_t draw_color = ds->GetCompatibleColor(play.top_bar_backcolor);
727 ds->FillRect(Rect(0, 0, ds->GetWidth() - 1, topBar.height - 1), draw_color);
728 if (play.top_bar_backcolor != play.top_bar_bordercolor) {
729 // draw the border
730 draw_color = ds->GetCompatibleColor(play.top_bar_bordercolor);
731 for (int j = 0; j < multiply_up_coordinate(play.top_bar_borderwidth); j++)
732 ds->DrawRect(Rect(j, j, ds->GetWidth() - (j + 1), topBar.height - (j + 1)), draw_color);
733 }
734
735 // draw the text
736 int textx = (ds->GetWidth() / 2) - wgettextwidth_compensate(topBar.text, topBar.font) / 2;
737 color_t text_color = ds->GetCompatibleColor(play.top_bar_textcolor);
738 wouttext_outline(ds, textx, play.top_bar_borderwidth + get_fixed_pixel_size(1), topBar.font, text_color, topBar.text);
739
740 // don't draw it next time
741 topBar.wantIt = 0;
742 // adjust the text Y position
743 yins[0] += topBar.height;
744 }
745 else if (topBar.wantIt)
746 topBar.wantIt = 0;
747 }
748