1 /*
2  *  Copyright (C) 2000-2013  The Exult Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #	include <config.h>
21 #endif
22 
23 #include "bggame.h"
24 
25 #include "Audio.h"
26 #include "AudioMixer.h"
27 #include "Configuration.h"
28 #include "array_size.h"
29 #include "data/bg/introsfx_mt32_flx.h"
30 #include "data/exult_bg_flx.h"
31 #include "databuf.h"
32 #include "exult.h"
33 #include "exult_constants.h"
34 #include "files/U7file.h"
35 #include "files/utils.h"
36 #include "flic/playfli.h"
37 #include "fnames.h"
38 #include "font.h"
39 #include "gamewin.h"
40 #include "gump_utils.h"
41 #include "imagewin/ArbScaler.h"
42 #include "imagewin/imagewin.h"
43 #include "items.h"
44 #include "mappatch.h"
45 #include "miscinf.h"
46 #include "modmgr.h"
47 #include "palette.h"
48 #include "shapeid.h"
49 #include "sigame.h"
50 #include "touchui.h"
51 #include "txtscroll.h"
52 
53 #include <SDL.h>
54 #include <SDL_events.h>
55 
56 #include <cctype>
57 #include <cstring>
58 #include <string>
59 #include <typeinfo>
60 #include <utility>
61 #include <vector>
62 #include <memory>
63 
64 using std::abs;
65 using std::make_unique;
66 using std::rand;
67 using std::strchr;
68 using std::strlen;
69 using std::unique_ptr;
70 
71 enum {
72     ultima_text_shp = 0x0D,
73     butterfly_shp = 0x0E,
74     lord_british_shp = 0x11,
75     trees_shp = 0x12,
76 
77     guardian_mouth_shp = 0x1E,
78     guardian_forehead_shp = 0x1F,
79     guardian_eyes_shp = 0x20
80 };
81 
82 enum {
83     bird_song_midi = 0,
84     home_song_midi = 1,
85     guardian_midi = 2,
86     menu_midi = 3,
87     credits_midi = 4,
88     quotes_midi = 5
89 };
90 
BG_Game()91 BG_Game::BG_Game()
92 	: shapes(ENDSHAPE_FLX, -1, PATCH_ENDSHAPE) {
93 	if (!read_game_xml()) {
94 		add_shape("gumps/check", 2);
95 		add_shape("gumps/fileio", 3);
96 		add_shape("gumps/fntext", 4);
97 		add_shape("gumps/loadbtn", 5);
98 		add_shape("gumps/savebtn", 6);
99 		add_shape("gumps/halo", 7);
100 		add_shape("gumps/disk", 24);
101 		add_shape("gumps/heart", 25);
102 		add_shape("gumps/statatts", 28);
103 		add_shape("gumps/musicbtn", 29);
104 		add_shape("gumps/speechbtn", 30);
105 		add_shape("gumps/soundbtn", 31);
106 		add_shape("gumps/spellbook", 43);
107 		add_shape("gumps/statsdisplay", 47);
108 		add_shape("gumps/combat", 46);
109 		add_shape("gumps/quitbtn", 56);
110 		add_shape("gumps/yesnobox", 69);
111 		add_shape("gumps/yesbtn", 70);
112 		add_shape("gumps/nobtn", 71);
113 		add_shape("gumps/book", 32);
114 		add_shape("gumps/scroll", 55);
115 		add_shape("gumps/combatmode", 12);
116 		add_shape("gumps/slider", 14);
117 		add_shape("gumps/slider_diamond", 15);
118 		add_shape("gumps/slider_right", 16);
119 		add_shape("gumps/slider_left", 17);
120 
121 		add_shape("gumps/box", 0);
122 		add_shape("gumps/crate", 1);
123 		add_shape("gumps/barrel", 8);
124 		add_shape("gumps/bag", 9);
125 		add_shape("gumps/backpack", 10);
126 		add_shape("gumps/basket", 11);
127 		add_shape("gumps/chest", 22);
128 		add_shape("gumps/shipshold", 26);
129 		add_shape("gumps/drawer", 27);
130 		add_shape("gumps/woodsign", 49);
131 		add_shape("gumps/tombstone", 50);
132 		add_shape("gumps/goldsign", 51);
133 		add_shape("gumps/body", 53);
134 
135 		add_shape("gumps/scroll_spells", 0xfff);
136 		add_shape("gumps/spell_scroll", 0xfff);
137 		add_shape("gumps/jawbone", 0xfff);
138 		add_shape("gumps/tooth", 0xfff);
139 
140 		add_shape("sprites/map", 22);
141 		add_shape("sprites/cheatmap", EXULT_BG_FLX_BGMAP_SHP);
142 
143 		const char *exultflx = BUNDLE_CHECK(BUNDLE_EXULT_FLX, EXULT_FLX);
144 		const char *gameflx = BUNDLE_CHECK(BUNDLE_EXULT_BG_FLX, EXULT_BG_FLX);
145 
146 		add_resource("files/shapes/count", nullptr, 9);
147 		add_resource("files/shapes/0", SHAPES_VGA, 0);
148 		add_resource("files/shapes/1", FACES_VGA, 0);
149 		add_resource("files/shapes/2", GUMPS_VGA, 0);
150 		add_resource("files/shapes/3", SPRITES_VGA, 0);
151 		add_resource("files/shapes/4", MAINSHP_FLX, 0);
152 		add_resource("files/shapes/5", ENDSHAPE_FLX, 0);
153 		add_resource("files/shapes/6", FONTS_VGA, 0);
154 		add_resource("files/shapes/7", exultflx, 0);
155 		add_resource("files/shapes/8", gameflx, 0);
156 
157 		add_resource("files/gameflx", gameflx, 0);
158 
159 		add_resource("files/paperdolvga", gameflx, EXULT_BG_FLX_BG_PAPERDOL_VGA);
160 		add_resource("files/mrfacesvga", gameflx, EXULT_BG_FLX_BG_MR_FACES_VGA);
161 		add_resource("config/defaultkeys", gameflx, EXULT_BG_FLX_DEFAULTKEYS_TXT);
162 		add_resource("config/bodies", gameflx, EXULT_BG_FLX_BODIES_TXT);
163 		add_resource("config/paperdol_info", gameflx, EXULT_BG_FLX_PAPERDOL_INFO_TXT);
164 		add_resource("config/shape_info", gameflx, EXULT_BG_FLX_SHAPE_INFO_TXT);
165 		add_resource("config/shape_files", gameflx, EXULT_BG_FLX_SHAPE_FILES_TXT);
166 		add_resource("config/avatar_data", gameflx, EXULT_BG_FLX_AVATAR_DATA_TXT);
167 		add_resource("config/autonotes", gameflx, EXULT_BG_FLX_AUTONOTES_TXT);
168 		add_resource("files/intro_hand", gameflx, EXULT_BG_FLX_INTRO_HAND_SHP);
169 
170 		add_resource("palettes/count", nullptr, 18);
171 		add_resource("palettes/0", PALETTES_FLX, 0);
172 		add_resource("palettes/1", PALETTES_FLX, 1);
173 		add_resource("palettes/2", PALETTES_FLX, 2);
174 		add_resource("palettes/3", PALETTES_FLX, 3);
175 		add_resource("palettes/4", PALETTES_FLX, 4);
176 		add_resource("palettes/5", PALETTES_FLX, 5);
177 		add_resource("palettes/6", PALETTES_FLX, 6);
178 		add_resource("palettes/7", PALETTES_FLX, 7);
179 		add_resource("palettes/8", PALETTES_FLX, 8);
180 		add_resource("palettes/9", PALETTES_FLX, 10);
181 		add_resource("palettes/10", PALETTES_FLX, 11);
182 		add_resource("palettes/11", PALETTES_FLX, 12);
183 		add_resource("palettes/12", INTROPAL_DAT, 0);
184 		add_resource("palettes/13", INTROPAL_DAT, 1);
185 		add_resource("palettes/14", INTROPAL_DAT, 2);
186 		add_resource("palettes/15", INTROPAL_DAT, 3);
187 		add_resource("palettes/16", INTROPAL_DAT, 4);
188 		add_resource("palettes/17", INTROPAL_DAT, 5);
189 
190 		add_resource("palettes/patch/0", PATCH_PALETTES, 0);
191 		add_resource("palettes/patch/1", PATCH_PALETTES, 1);
192 		add_resource("palettes/patch/2", PATCH_PALETTES, 2);
193 		add_resource("palettes/patch/3", PATCH_PALETTES, 3);
194 		add_resource("palettes/patch/4", PATCH_PALETTES, 4);
195 		add_resource("palettes/patch/5", PATCH_PALETTES, 5);
196 		add_resource("palettes/patch/6", PATCH_PALETTES, 6);
197 		add_resource("palettes/patch/7", PATCH_PALETTES, 7);
198 		add_resource("palettes/patch/8", PATCH_PALETTES, 8);
199 		add_resource("palettes/patch/9", PATCH_PALETTES, 10);
200 		add_resource("palettes/patch/10", PATCH_PALETTES, 11);
201 		add_resource("palettes/patch/11", PATCH_PALETTES, 12);
202 		add_resource("palettes/patch/12", PATCH_INTROPAL, 0);
203 		add_resource("palettes/patch/13", PATCH_INTROPAL, 1);
204 		add_resource("palettes/patch/14", PATCH_INTROPAL, 2);
205 		add_resource("palettes/patch/15", PATCH_INTROPAL, 3);
206 		add_resource("palettes/patch/16", PATCH_INTROPAL, 4);
207 		add_resource("palettes/patch/17", PATCH_INTROPAL, 5);
208 
209 		add_resource("xforms/count", nullptr, 20);
210 		add_resource("xforms/0", XFORMTBL, 0);
211 		add_resource("xforms/1", XFORMTBL, 1);
212 		add_resource("xforms/2", XFORMTBL, 2);
213 		add_resource("xforms/3", XFORMTBL, 3);
214 		add_resource("xforms/4", XFORMTBL, 4);
215 		add_resource("xforms/5", XFORMTBL, 5);
216 		add_resource("xforms/6", XFORMTBL, 6);
217 		add_resource("xforms/7", XFORMTBL, 7);
218 		add_resource("xforms/8", XFORMTBL, 8);
219 		add_resource("xforms/9", XFORMTBL, 9);
220 		add_resource("xforms/10", XFORMTBL, 10);
221 		add_resource("xforms/11", XFORMTBL, 11);
222 		add_resource("xforms/12", XFORMTBL, 12);
223 		add_resource("xforms/13", XFORMTBL, 13);
224 		add_resource("xforms/14", XFORMTBL, 14);
225 		add_resource("xforms/15", XFORMTBL, 15);
226 		add_resource("xforms/16", XFORMTBL, 16);
227 		add_resource("xforms/17", XFORMTBL, 17);
228 		add_resource("xforms/18", XFORMTBL, 18);
229 		add_resource("xforms/19", XFORMTBL, 19);
230 	}
231 	fontManager.add_font("MENU_FONT", MAINSHP_FLX, PATCH_MAINSHP, 9, 1);
232 	fontManager.add_font("END2_FONT", ENDGAME, PATCH_ENDGAME, 4, -1);
233 	fontManager.add_font("END3_FONT", ENDGAME, PATCH_ENDGAME, 5, -2);
234 	fontManager.add_font("NORMAL_FONT", FONTS_VGA, PATCH_FONTS, 0, -1);
235 	fontManager.add_font("SMALL_BLACK_FONT", FONTS_VGA, PATCH_FONTS, 2, 0);
236 	fontManager.add_font("TINY_BLACK_FONT", FONTS_VGA, PATCH_FONTS, 4, 0);
237 	fontManager.add_font("GUARDIAN_FONT", MAINSHP_FLX, PATCH_MAINSHP, 3, -2);
238 	auto& mp = gwin->get_map_patches();
239 			// Sawdust in Iolo's hut is at lift 2, should be 0
240 			// FIXME - the original had some way to deal with this
241 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
242 			                                 Tile_coord(481, 599, 2), 224, 7, 0),
243 			                             Object_spec(
244 			                                 Tile_coord(480, 598, 0), 224, 7, 0)));
245 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
246 			                                 Tile_coord(482, 601, 2), 224, 7, 0),
247 			                             Object_spec(
248 			                                 Tile_coord(481, 600, 0), 224, 7, 0)));
249 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
250 			                                 Tile_coord(482, 600, 2), 224, 7, 0),
251 			                             Object_spec(
252 			                                 Tile_coord(481, 599, 0), 224, 7, 0)));
253 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
254 			                                 Tile_coord(481, 602, 2), 224, 7, 0),
255 			                             Object_spec(
256 			                                 Tile_coord(480, 601, 0), 224, 7, 0)));
257 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
258 			                                 Tile_coord(479, 599, 2), 224, 7, 0),
259 			                             Object_spec(
260 			                                 Tile_coord(478, 598, 0), 224, 7, 0)));
261 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
262 			                                 Tile_coord(477, 597, 2), 224, 7, 0),
263 			                             Object_spec(
264 			                                 Tile_coord(476, 596, 0), 224, 7, 0)));
265 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
266 			                                 Tile_coord(476, 597, 2), 224, 7, 0),
267 			                             Object_spec(
268 			                                 Tile_coord(475, 596, 0), 224, 7, 0)));
269 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
270 			                                 Tile_coord(472, 595, 2), 224, 7, 0),
271 			                             Object_spec(
272 			                                 Tile_coord(471, 594, 0), 224, 7, 0)));
273 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
274 			                                 Tile_coord(473, 598, 2), 224, 7, 0),
275 			                             Object_spec(
276 			                                 Tile_coord(472, 597, 0), 224, 7, 0)));
277 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
278 			                                 Tile_coord(472, 600, 2), 224, 7, 0),
279 			                             Object_spec(
280 			                                 Tile_coord(471, 599, 0), 224, 7, 0)));
281 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
282 			                                 Tile_coord(470, 597, 2), 224, 7, 0),
283 			                             Object_spec(
284 			                                 Tile_coord(469, 596, 0), 224, 7, 0)));
285 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
286 			                                 Tile_coord(469, 597, 2), 224, 7, 0),
287 			                             Object_spec(
288 			                                 Tile_coord(468, 596, 0), 224, 7, 0)));
289 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
290 			                                 Tile_coord(467, 599, 2), 224, 7, 0),
291 			                             Object_spec(
292 			                                 Tile_coord(466, 598, 0), 224, 7, 0)));
293 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
294 			                                 Tile_coord(468, 600, 2), 224, 7, 0),
295 			                             Object_spec(
296 			                                 Tile_coord(467, 599, 0), 224, 7, 0)));
297 			mp.add(std::make_unique<Map_patch_modify>(Object_spec(
298 			                                 Tile_coord(467, 601, 2), 224, 7, 0),
299 			                             Object_spec(
300 			                                 Tile_coord(466, 600, 0), 224, 7, 0)));
301 			// pure BG shape 874 only has an empty frame. For FoV's Test of Truth
302 			// it was changed for creating a dungeon chasm while obviously forgetting
303 			// that frame #0 was used in two occassions:
304 			//       hole in Despise's rebel base to invisible stairway
305 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
306 		                                     Tile_coord(681, 1192, 5), 874, 0, 0)));
307 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
308 		                                     Tile_coord(682, 1192, 5), 874, 0, 0)));
309 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
310 		                                     Tile_coord(681, 1195, 5), 874, 0, 0)));
311 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
312 		                                     Tile_coord(682, 1195, 5), 874, 0, 0)));
313 			//       stairways hole in Shame's 2nd floor
314 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
315 		                                     Tile_coord(807, 951, 5), 874, 0, 0)));
316 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
317 		                                     Tile_coord(811, 951, 5), 874, 0, 0)));
318 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
319 		                                     Tile_coord(807, 955, 5), 874, 0, 0)));
320 			mp.add(std::make_unique<Map_patch_remove>(Object_spec(
321 		                                     Tile_coord(811, 955, 5), 874, 0, 0)));
322 }
323 
324 class UserSkipException : public UserBreakException {
325 };
326 
327 
328 #define WAITDELAY(x) switch(wait_delay(x)) { \
329 	case 1: throw UserBreakException(); break; \
330 	case 2: throw UserSkipException(); break; \
331 	}
332 
333 #define WAITDELAYCYCLE1(x) switch (wait_delay((x), 16, 78, 50)) { \
334 	case 1: throw UserBreakException(); break; \
335 	case 2: throw UserSkipException(); break; \
336 	}
337 
338 #define WAITDELAYCYCLE2(x) switch (wait_delay((x), 250, 5)) { \
339 	case 1: throw UserBreakException(); break; \
340 	case 2: throw UserSkipException(); break; \
341 	}
342 
343 #define WAITDELAYCYCLE3(x) switch (wait_delay((x), 240, 15)) { \
344 	case 1: throw UserBreakException(); break; \
345 	case 2: throw UserSkipException(); break; \
346 	}
347 
348 #define WAITDELAYCYCLE4(x) switch (wait_delay((x), 16, 78, 15)) { \
349 	case 1: throw UserBreakException(); break; \
350 	case 2: throw UserSkipException(); break; \
351 	}
352 
353 #define WAITDELAYCYCLE5(x) switch (wait_delay((x), 16, -78, 50)) { \
354 	case 1: throw UserBreakException(); break; \
355 	case 2: throw UserSkipException(); break; \
356 	}
357 
358 #define WAITDELAYCYCLE6(x) switch (wait_delay((x), 16, -78, 15)) { \
359 	case 1: throw UserBreakException(); break; \
360 	case 2: throw UserSkipException(); break; \
361 	}
362 
play_intro()363 void BG_Game::play_intro() {
364 	Audio *audio = Audio::get_ptr();
365 	audio->stop_music();
366 	MyMidiPlayer *midi = audio->get_midi();
367 	if (midi) midi->set_timbre_lib(MyMidiPlayer::TIMBRE_LIB_INTRO);
368 
369 	gwin->clear_screen(true);
370 
371 	// TODO: check/fix other resolutions
372 
373 	try {
374 		/********************************************************************
375 		 Lord British Presents
376 		********************************************************************/
377 
378 		scene_lord_british();
379 
380 		/********************************************************************
381 		 Ultima VII logo w/Trees
382 		********************************************************************/
383 
384 		scene_butterfly();
385 
386 		/********************************************************************
387 		 Enter guardian
388 		********************************************************************/
389 
390 		scene_guardian();
391 
392 		/********************************************************************
393 		 PC screen
394 		********************************************************************/
395 
396 		scene_desk();
397 
398 		/********************************************************************
399 		 The Moongate
400 		********************************************************************/
401 
402 		scene_moongate();
403 	} catch (const UserBreakException &/*x*/) {
404 	}
405 
406 	// Fade out the palette...
407 	pal->fade_out(c_fade_out_time);
408 
409 	// ... and clean the screen.
410 	gwin->clear_screen(true);
411 
412 	// Stop all audio output
413 	audio->cancel_streams();
414 }
415 
get_sfx_subflex()416 File_spec BG_Game::get_sfx_subflex() {
417 	if (Audio::have_sblaster_sfx(BLACK_GATE)) {
418 		return File_spec{game->get_resource("files/gameflx").str, EXULT_BG_FLX_INTROSFX_SB_FLX};
419 	}
420 	// TODO: MIDI SFX
421 	// if (audio->have_midi_sfx()) {
422 	// 	return File_spec{game->get_resource("files/gameflx").str, EXULT_BG_FLX_INTROSFX_MTIDI_FLX};
423 	// }
424 	return File_spec{game->get_resource("files/gameflx").str, EXULT_BG_FLX_INTROSFX_MT32_FLX};
425 }
426 
scene_lord_british()427 void BG_Game::scene_lord_british() {
428 	/*
429 	 *  Enhancements to lip-syncing contributed by
430 	 *  Eric Wasylishen, Jun. 19, 2006.
431 	 */
432 
433 	try {
434 		WAITDELAY(1500); // - give a little space between exult title music
435 		//     and LB presents screen
436 
437 
438 		// Lord British presents...  (sh. 0x11)
439 		pal->load(INTROPAL_DAT, PATCH_INTROPAL, 3);
440 		sman->paint_shape(topx, topy, shapes.get_shape(lord_british_shp, 0));
441 
442 		pal->fade_in(c_fade_in_time);
443 		if (1 == wait_delay(2000))
444 			throw UserBreakException();
445 		pal->fade_out(c_fade_out_time);
446 	} catch (const UserSkipException &/*x*/) {
447 	}
448 	gwin->clear_screen(true);
449 }
450 
scene_butterfly()451 void BG_Game::scene_butterfly() {
452 	constexpr static const int frame_duration = 23; // - used to be 16.. too fast.
453 	constexpr static const int frame_count = 3;
454 
455 	constexpr static int butterfly_x[] = {
456 		  6,  18,  30,  41,  52,  62,  70,  78,  86,  95,
457 		104, 113, 122, 132, 139, 146, 151, 155, 157, 158,
458 		157, 155, 151, 146, 139, 132, 124, 116, 108, 102,
459 		 96,  93,  93,  93,  95,  99, 109, 111, 118, 125,
460 		132, 140, 148, 157, 164, 171, 178, 184, 190, 196,
461 		203, 211, 219, 228, 237, 246, 254, 259, 262, 264,
462 		265, 265, 263, 260, 256, 251, 245, 239, 232, 226,
463 		219, 212, 208, 206, 206, 209, 212, 216, 220, 224,
464 		227, 234, 231, 232, 233, 233, 233, 233, 234, 236,
465 		239, 243, 247, 250, 258, 265
466 	};
467 
468 	constexpr static int butterfly_y[] = {
469 		155, 153, 151, 150, 149, 148, 148, 148, 148, 149,
470 		150, 150, 150, 149, 147, 142, 137, 131, 125, 118,
471 		110, 103,  98,  94,  92,  91,  91,  91,  92,  95,
472 		 99, 104, 110, 117, 123, 127, 131, 134, 135, 135,
473 		135, 135, 135, 134, 132, 129, 127, 123, 119, 115,
474 		112, 109, 104, 102, 101, 102, 109, 109, 114, 119,
475 		125, 131, 138, 144, 149, 152, 156, 158, 159, 159,
476 		158, 155, 150, 144, 137, 130, 124, 118, 112, 105,
477 		 99,  93,  86,  80,  73,  66,  59,  53,  47,  42,
478 		 38,  35,  32,  29,  26,  25
479 	};
480 
481 	constexpr static const int butterfly_num_coords = array_size(butterfly_x);
482 
483 	constexpr static const int butterfly_end_frames[] = {
484 		  3,   4,   3,   4,   3,   2,   1,   0
485 	};
486 	constexpr static const int butterfly_end_delay[] = {
487 		167, 416, 250, 416, 416, 416, 416, 333
488 	};
489 
490 	int frame;
491 
492 	auto DrawButterfly = [&](int x, int y, int frame, int delay, Image_buffer *backup, Shape_frame *butterfly) {
493 		// Draw butterfly
494 		win->get(backup, topx + x - butterfly->get_xleft(),
495 				topy + y - butterfly->get_yabove());
496 		sman->paint_shape(topx + x, topy + y, shapes.get_shape(butterfly_shp, frame));
497 		win->show();
498 		WAITDELAY(delay);
499 		win->put(backup, topx + x - butterfly->get_xleft(),
500 				topy + y - butterfly->get_yabove());
501 	};
502 
503 	int dir = 0;
504 	auto ButterflyFlap = [&]() {
505 		if ((rand() % 5)<4) {
506 			if (frame == 3) {
507 				dir = -1;
508 			} else if (frame == 0) {
509 				dir = +1;
510 			}
511 			frame += dir;
512 		}
513 	};
514 
515 	try {
516 		pal->load(INTROPAL_DAT, PATCH_INTROPAL, 4);
517 
518 		// Load the butterfly shape
519 		Shape_frame *butterfly(shapes.get_shape(butterfly_shp, 0));
520 		unique_ptr<Image_buffer> backup(win->create_buffer(butterfly->get_width(), butterfly->get_height()));
521 
522 		// Start playing the birdsongs while still faded out
523 		Audio::get_ptr()->start_music(bird_song_midi, false, INTROMUS);
524 
525 		// trees with "Ultima VII" on top of 'em
526 		sman->paint_shape(topx, topy, shapes.get_shape(trees_shp, 0));
527 		sman->paint_shape(topx + 160, topy + 50, shapes.get_shape(ultima_text_shp, 0));
528 
529 
530 		// Keep it dark for some more time, playing the music
531 		WAITDELAY(4500); //  - was WAITDELAY(3500);
532 
533 		// Finally fade in
534 		pal->fade_in(c_fade_in_time);
535 
536 		WAITDELAY(4000);
537 
538 		win->show();
539 
540 		WAITDELAY(7100);
541 
542 		//
543 		// Move the butterfly along its path
544 		//
545 		frame = 0;
546 		Sint32 delay = frame_duration;
547 		for (int i = 0; i < butterfly_num_coords - 1; ++i) {
548 			for (int j = 0; j < frame_count; ++j) {
549 
550 				Sint32 ticks = SDL_GetTicks();
551 				int x = butterfly_x[i] + j * (butterfly_x[i + 1] - butterfly_x[i]) / frame_count;
552 				int y = butterfly_y[i] + j * (butterfly_y[i + 1] - butterfly_y[i]) / frame_count;
553 				DrawButterfly(x, y, frame, delay, backup.get(), butterfly);
554 
555 				// Flap the wings; but not always, so that the butterfly "glides" from time to time
556 				ButterflyFlap();
557 
558 				// Calculate the difference between the time we wanted to spent and the time
559 				// we actually spent; then adjust 'delay' accordingly
560 				ticks = SDL_GetTicks() - ticks;
561 				delay = (delay + (2 * frame_duration - ticks)) / 2;
562 
563 				// ... but maybe we also have to skip frames..
564 				if (delay < 0) {
565 					// Calculate how many frames we should skip
566 					int frames_to_skip = (-delay) / frame_duration + 1;
567 					int new_index = i * frame_count + j + frames_to_skip;
568 					i = new_index / frame_count;
569 					j = new_index % frame_count;
570 
571 					// Did we skip over the end?
572 					if (i >= butterfly_num_coords - 1)
573 						break;
574 
575 					while (frames_to_skip--)
576 						ButterflyFlap();
577 
578 					delay = 0;
579 				}
580 			}
581 		}
582 
583 		// Finally, let it flutter a bit on the end spot
584 		for (int i = 0; i < 8; i++) {
585 			DrawButterfly(butterfly_x[butterfly_num_coords - 1],
586 			              butterfly_y[butterfly_num_coords - 1],
587 			              butterfly_end_frames[i],
588 			              butterfly_end_delay[i],
589 						  backup.get(), butterfly);
590 		}
591 
592 		WAITDELAY(1000);
593 
594 		// Wait till the music finished playing
595 		while (Audio::get_ptr()->is_track_playing(bird_song_midi))
596 			WAITDELAY(20);
597 	} catch (const UserSkipException &/*x*/) {
598 	}
599 }
600 
601 #define FLASH_SHAPE1(x,y,shape,frame,delay)  do {    \
602 		sman->paint_shape((x),(y),shapes.get_shape((shape),(frame)));   \
603 		win->show();  \
604 		WAITDELAYCYCLE1((delay));    \
605 		win->put(backup.get(),(x)-s->get_xleft(),(y)-s->get_yabove());    \
606 	} while(0)
607 
608 #define FLASH_SHAPE2(x,y,shape,frame,delay)  do {    \
609 		sman->paint_shape((x),(y),shapes.get_shape((shape),(frame)));   \
610 		win->show();  \
611 		WAITDELAYCYCLE4((delay));    \
612 		win->put(backup.get(),(x)-s->get_xleft(),(y)-s->get_yabove());    \
613 	} while(0)
614 
615 
616 class LipSynchReader {
617 	std::unique_ptr<IBufferDataView> data;
618 
619 public:
LipSynchReader()620 	LipSynchReader()
621 		: data(std::make_unique<IExultDataSource>(MAINSHP_FLX, PATCH_MAINSHP, 0x0F)) {}
LipSynchReader(char const * pp,int len)622 	LipSynchReader(char const *pp, int len)
623 		: data(std::make_unique<IBufferDataView>(pp, len)) {}
have_events() const624 	bool have_events() const {
625 		return data->getAvail() > 0;
626 	}
get_event(int & time,int & code)627 	void get_event(int &time, int &code) {
628 		if (have_events()) {
629 			int curr_time = data->read2();
630 			time = curr_time * 1000.0 / 60.0;
631 			code = data->read1();
632 		} else {
633 			time = 0;
634 			code = 0;
635 		}
636 	}
translate_code(int code,int & mouth,int & eyes,int & lasteyes)637 	void translate_code(int code, int &mouth, int &eyes, int &lasteyes) {
638 		// Lookup table for translating lipsynch data. The table is indexed by the
639 		// current frame-1, then by read lipsynch data-1.
640 		constexpr static const int eye_frame_LUT[6][9] = {
641 		//   1, 2, 3, 4, 5, 6, 7, 8, 9		// Last eye frame (or the one before, if it was 10)
642 			{1, 2, 3, 1, 2, 3, 1, 2, 3},	// 1: Change eyebrows to angry
643 			{4, 5, 6, 4, 5, 6, 4, 5, 6},	// 2: Change eyebrows to neutral
644 			{7, 8, 9, 7, 8, 9, 7, 8, 9},	// 3: Change eyebrows to raised
645 			{1, 1, 1, 4, 4, 4, 7, 7, 7},	// 4: Change eyes to fully open
646 			{2, 2, 2, 5, 5, 5, 8, 8, 8},	// 5: Change eyes to half-open
647 			{3, 3, 3, 6, 6, 6, 9, 9, 9},	// 6: Change eyes to fully closed
648 		};
649 		// Set based on code read
650 		if (code >= 8 && code < 23) {
651 			// This changes mouth frame
652 			mouth = code - 8;
653 		} else if (code == 7) {
654 			// This sets eyes to looking down
655 			// Don't update last eye frame
656 			eyes = 10;
657 		} else if (code > 0 && code < 7) {
658 			// This changes eyes and eyebrows
659 			eyes = eye_frame_LUT[code-1][lasteyes-1];
660 			lasteyes = eyes;
661 		}
662 	}
663 };
664 
665 // Simple RAII wrapper for SDL_FreeSurface
666 class SDL_SurfaceOwner {
667 	SDL_Surface* surf;
668 public:
SDL_SurfaceOwner(Image_buffer * src,SDL_Surface * draw)669 	SDL_SurfaceOwner(Image_buffer *src, SDL_Surface *draw)
670 		: surf(SDL_CreateRGBSurfaceFrom(src->get_bits(),
671 										src->get_height(), src->get_width(),
672 										draw->format->BitsPerPixel,
673 										src->get_line_width(),
674 										draw->format->Rmask,
675 										draw->format->Gmask,
676 										draw->format->Bmask,
677 										draw->format->Amask)) {}
~SDL_SurfaceOwner()678 	~SDL_SurfaceOwner() noexcept {
679 		SDL_FreeSurface(surf);
680 	}
get()681 	SDL_Surface* get() noexcept {
682 		return surf;
683 	}
684 };
685 
686 // Simple RAII wrapper for managing window clip
687 class WinClip {
688 	Image_window8 *window;
689 public:
WinClip(Image_window8 * win,int cx,int cy,int wid,int hgt)690 	WinClip(Image_window8 *win, int cx, int cy, int wid, int hgt)
691 		: window(win) {
692 		window->set_clip(cx, cy, wid, hgt);
693 	}
~WinClip()694 	~WinClip() noexcept {
695 		window->clear_clip();
696 	}
697 };
698 
699 // Simple RAII wrapper for stopping speech
700 struct SpeechManager {
SpeechManagerSpeechManager701 	SpeechManager(const char *fname, const char *fpatch, bool wait) {
702 		if (Audio::get_ptr()->is_audio_enabled() &&
703 					Audio::get_ptr()->is_speech_enabled()) {
704 			Audio::get_ptr()->playfile(fname, fpatch, wait);
705 		}
706 	}
~SpeechManagerSpeechManager707 	~SpeechManager() noexcept {
708 		Audio::get_ptr()->stop_speech();
709 	}
710 };
711 
scene_guardian()712 void BG_Game::scene_guardian() {
713 	constexpr static const int Eyes_Dist = 12;
714 	constexpr static const int Forehead_Dist = 49;
715 
716 	constexpr static const int text_times[] = {
717 		250, 1700, 5250, 8200, 10550, 12900, 16000, 19950, 22700, 27200, 32400,
718 		36400, 39650, 42400
719 	};
720 
721 	constexpr static const int text_num_frames = array_size(text_times);
722 
723 	constexpr static const char surfacing_data[] = {
724 		0x03, 0x00, 0x02,	// Eyebrows -> neutral
725 		0x03, 0x00, 0x05,	// Eyes -> half-open
726 		0x15, 0x00, 0x04,	// Eyes -> fully open
727 		0x33, 0x00, 0x07,	// Eyes -> Look down
728 		0x54, 0x00, 0x04,	// Eyes -> fully open
729 		0x77, 0x00, 0x00
730 	};
731 
732 	try {
733 		Uint32 ticks;
734 		const File_spec sfxfile = get_sfx_subflex();
735 		{
736 			// create buffer containing a blue 'plasma' screen
737 			unique_ptr<Image_buffer> plasma(win->create_buffer(win->get_full_width(),
738 		                            win->get_full_height()));
739 			gwin->plasma(win->get_full_width(), win->get_full_height(), win->get_start_x(), win->get_start_y(), 16, 16 + 76);
740 			win->get(plasma.get(), win->get_start_x(), win->get_start_y());
741 
742 			pal->load(INTROPAL_DAT, PATCH_INTROPAL, 2);
743 			pal->set_color(1, 0, 0, 0); //UGLY hack... set font background to black
744 			pal->apply();
745 
746 			//play static SFX
747 			Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_STATIC1_WAV);
748 
749 			//
750 			// Show some "static" alternating with the blue plasma
751 			//
752 			// TODO: Is this the right kind of static? Dosbox shows a mostly black
753 			//      with an ocassional white pixel static - but is this what it
754 			//      was really like?
755 
756 			ticks = SDL_GetTicks();
757 			while (true) {
758 				win->get_ibuf()->fill_static(0, 7, 15);
759 				win->show();
760 				WAITDELAYCYCLE1(2);
761 				if (SDL_GetTicks() > ticks + 400)//400)
762 					break;
763 			}
764 
765 			Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_STATIC2_WAV);
766 
767 			win->put(plasma.get(), win->get_start_x(), win->get_start_y());
768 			win->show();
769 			WAITDELAYCYCLE1(200);
770 
771 			ticks = SDL_GetTicks();
772 			while (true) {
773 				win->get_ibuf()->fill_static(0, 7, 15);
774 				win->show();
775 				WAITDELAYCYCLE1(2);
776 				if (SDL_GetTicks() > ticks + 200)
777 					break;
778 			}
779 
780 			Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_STATIC3_WAV);
781 
782 			win->put(plasma.get(), win->get_start_x(), win->get_start_y());
783 			win->show();
784 			WAITDELAYCYCLE1(200);
785 
786 			ticks = SDL_GetTicks();
787 			while (true) {
788 				win->get_ibuf()->fill_static(0, 7, 15);
789 				win->show();
790 				WAITDELAYCYCLE1(2);
791 				if (SDL_GetTicks() > ticks + 100)
792 					break;
793 			}
794 
795 			win->put(plasma.get(), win->get_start_x(), win->get_start_y());
796 			win->show();
797 		}
798 
799 		//
800 		// Start background music
801 		//
802 		Audio::get_ptr()->start_music(guardian_midi, false, INTROMUS);
803 
804 		WAITDELAYCYCLE1(3800);
805 
806 		//
807 		// First 'popup' (sh. 0x21)
808 		//
809 
810 		Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_GUARDIAN1_WAV);
811 
812 		{
813 			Shape_frame *s = shapes.get_shape(0x21, 0);
814 			unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width(), s->get_height()));
815 			win->get(backup.get(), centerx - 53 - s->get_xleft(), centery - 68 - s->get_yabove());
816 			for (int i = 8; i >= -8; i--)
817 				FLASH_SHAPE2(centerx - 53, centery - 68, 0x21, 1 + abs(i), 80);
818 		}
819 		WAITDELAYCYCLE1(2000);
820 
821 		//
822 		// Second 'popup' (sh. 0x22)
823 		//
824 		Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_GUARDIAN2_WAV);
825 
826 		{
827 			Shape_frame *s = shapes.get_shape(0x22, 0);
828 			unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width(), s->get_height()));
829 			win->get(backup.get(), centerx - s->get_xleft(), centery - 45 - s->get_yabove());
830 			for (int i = 9; i >= -9; i--)
831 				FLASH_SHAPE2(centerx, centery - 45, 0x22, 9 - abs(i), 80);
832 		}
833 		WAITDELAYCYCLE1(2000);
834 
835 		//
836 		// Successful 'popup' (sh. 0x23)
837 		//
838 		Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_GUARDIAN3_WAV);
839 
840 		{
841 			Shape_frame *s = shapes.get_shape(0x23, 0);
842 			unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width(), s->get_height()));
843 			unique_ptr<Image_buffer> cbackup(win->create_buffer(s->get_width(), s->get_height()));
844 
845 			win->get(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
846 			sman->paint_shape(centerx, centery, s); // frame 0 is static background
847 			win->get(backup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
848 			for (int i = 1; i < 16; i++)
849 				FLASH_SHAPE2(centerx, centery, 0x23, i, 70);
850 
851 			sman->paint_shape(centerx, centery, shapes.get_shape(0x23, 15));
852 			win->show();
853 
854 			WAITDELAYCYCLE1(500);    // - show his face for half a second
855 			// before he opens his eyes.
856 
857 			win->put(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
858 		}
859 		//
860 		// Actual appearance
861 		//
862 
863 		// mouth
864 		{
865 			Shape_frame *s = shapes.get_shape(guardian_mouth_shp, 0);
866 			unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width(), s->get_height()));
867 			unique_ptr<Image_buffer> cbackup(win->create_buffer(s->get_width(), s->get_height()));
868 			win->get(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
869 			sman->paint_shape(centerx, centery, s); // frame 0 is background
870 			win->get(backup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
871 			// eyes
872 			Shape_frame *s2 = shapes.get_shape(guardian_eyes_shp, 0);
873 			unique_ptr<Image_buffer> backup2(win->create_buffer(s2->get_width(), s2->get_height()));
874 			unique_ptr<Image_buffer> cbackup2(win->create_buffer(s2->get_width(), s2->get_height()));
875 			win->get(cbackup2.get(), centerx - s2->get_xleft(),
876 					centery - Eyes_Dist - s2->get_yabove());
877 			sman->paint_shape(centerx, centery - Eyes_Dist, s2); // frame 0 is background
878 			win->get(backup2.get(), centerx - s2->get_xleft(),
879 					centery - Eyes_Dist - s2->get_yabove());
880 			// forehead
881 			Shape_frame *s3 = shapes.get_shape(guardian_forehead_shp, 0);
882 			unique_ptr<Image_buffer> cbackup3(win->create_buffer(s3->get_width(), s3->get_height()));
883 			win->get(cbackup3.get(), centerx - s3->get_xleft(),
884 					centery - Forehead_Dist - s3->get_yabove());
885 			sman->paint_shape(centerx, centery - Forehead_Dist, s3); // forehead isn't animated
886 
887 			// prepare Guardian speech
888 			{
889 				Font *font = fontManager.get_font("GUARDIAN_FONT");
890 				U7multiobject textobj(MAINSHP_FLX, PATCH_MAINSHP, 0x0D);
891 				size_t txt_len;
892 				auto txt = textobj.retrieve(txt_len);
893 				char *txt_ptr;
894 				char *txt_end;
895 				char *next_txt;
896 				next_txt = txt_ptr = reinterpret_cast<char*>(txt.get());
897 
898 				int txt_height = font->get_text_height();
899 				int txt_ypos = gwin->get_height() - txt_height - 16;
900 
901 				// backup text area
902 				unique_ptr<Image_buffer> backup3(win->create_buffer(win->get_full_width(), txt_height));
903 				win->get(backup3.get(), win->get_start_x(), txt_ypos);
904 
905 				// Lipsynching
906 				int eye_frame = 3;
907 				int last_eye_frame = 3;
908 				int mouth_frame = 1;
909 				int text_index = -1;
910 				int next_time;
911 				int next_code;
912 				LipSynchReader lipsync;
913 				LipSynchReader surfacing(surfacing_data, sizeof(surfacing_data));
914 
915 				int time = 0;
916 				unsigned long start = SDL_GetTicks();
917 
918 				bool speech = Audio::get_ptr()->is_audio_enabled() &&
919 				              Audio::get_ptr()->is_speech_enabled();
920 				bool want_subs = !speech || Audio::get_ptr()->is_speech_with_subs();
921 
922 				auto AdvanceTextPointer = [&]() {
923 					txt_ptr = next_txt;
924 					txt_end = strchr(txt_ptr, '\r');
925 					*txt_end = '\0';
926 					next_txt = txt_end+2;
927 				};
928 				auto EraseAndDraw = [&](Image_buffer *backup, Shape_frame *fra, int shnum, int frnum, int dist) {
929 					win->put(backup, centerx - fra->get_xleft(),
930 									centery - dist - fra->get_yabove());
931 					sman->paint_shape(centerx, centery - dist,
932 									shapes.get_shape(shnum, frnum));
933 				};
934 				auto DrawSpeech = [&]() {
935 					// Erase text
936 					win->put(backup3.get(), win->get_start_x(), txt_ypos);
937 					// Erase and redraw eyes
938 					EraseAndDraw(backup2.get(), s2, guardian_eyes_shp, eye_frame, Eyes_Dist);
939 					// Erase and redraw mouth
940 					EraseAndDraw(backup.get(), s, guardian_mouth_shp, mouth_frame, 0);
941 					if (text_index > 0 && text_index < text_num_frames) {
942 						// Draw text
943 						font->center_text(win->get_ib8(), centerx, txt_ypos, txt_ptr);
944 					}
945 				};
946 
947 				// First event needs to be read here
948 				surfacing.get_event(next_time, next_code);
949 				DrawSpeech();
950 				// before speech
951 				while (time < 2000) {
952 					if (surfacing.have_events() && time >= next_time) {
953 						surfacing.translate_code(next_code, mouth_frame, eye_frame, last_eye_frame);
954 						surfacing.get_event(next_time, next_code);
955 						DrawSpeech();
956 					}
957 
958 					win->show();
959 					WAITDELAYCYCLE5(15);
960 					win->show();
961 					time = (SDL_GetTicks() - start);
962 				}
963 
964 
965 				SpeechManager mngr(INTROSND, PATCH_INTROSND, false);
966 				time = 0;
967 				start = SDL_GetTicks();
968 
969 				// First event needs to be read here
970 				lipsync.get_event(next_time, next_code);
971 				if (want_subs) {
972 					text_index = 0;
973 				} else {
974 					text_index = text_num_frames;	// Disable subtitles
975 				}
976 				// start speech
977 				while (time < 47537) {
978 					bool need_redraw = false;
979 					if (next_code && lipsync.have_events() && time >= next_time) {
980 						lipsync.translate_code(next_code, mouth_frame, eye_frame, last_eye_frame);
981 						lipsync.get_event(next_time, next_code);
982 						need_redraw = true;
983 					}
984 
985 					if (text_index < text_num_frames && time >= text_times[text_index]) {
986 						text_index++;
987 						AdvanceTextPointer();
988 						need_redraw = true;
989 					}
990 					if (need_redraw) {
991 						DrawSpeech();
992 					}
993 					win->show();
994 					WAITDELAYCYCLE6(15);
995 					win->show();
996 					time = (SDL_GetTicks() - start);
997 				}
998 
999 				win->show();
1000 				WAITDELAYCYCLE6(1000);
1001 				win->show();
1002 
1003 				win->put(backup3.get(), 0, txt_ypos);
1004 				win->put(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
1005 				win->put(cbackup2.get(), centerx - s2->get_xleft(),
1006 						centery - Eyes_Dist - s2->get_yabove());
1007 				win->put(cbackup3.get(), centerx - s3->get_xleft(),
1008 						centery - Forehead_Dist - s3->get_yabove());
1009 			}
1010 
1011 			// G. disappears again (sp. 0x23 again)
1012 			Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_GUARDIAN4_WAV);
1013 
1014 			{
1015 				Shape_frame *s = shapes.get_shape(0x23, 0);
1016 				unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width(), s->get_height()));
1017 				unique_ptr<Image_buffer> cbackup(win->create_buffer(s->get_width(), s->get_height()));
1018 				win->get(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
1019 				sman->paint_shape(centerx, centery, s); // frame 0 is background
1020 				win->get(backup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
1021 				for (int i = 15; i > 0; i--)
1022 					FLASH_SHAPE2(centerx, centery, 0x23, i, 70);
1023 				win->put(cbackup.get(), centerx - s->get_xleft(), centery - s->get_yabove());
1024 			}
1025 
1026 			win->show();
1027 		}
1028 		WAITDELAYCYCLE1(1200);
1029 
1030 		Audio::get_ptr()->stop_music();
1031 
1032 		//
1033 		// More static
1034 		//
1035 		Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_OUTSTATIC_WAV);
1036 
1037 		ticks = SDL_GetTicks();
1038 		while (true) {
1039 			win->get_ibuf()->fill_static(0, 7, 15);
1040 			win->show();
1041 			WAITDELAYCYCLE1(2);
1042 			if (SDL_GetTicks() > ticks + 400)
1043 				break;
1044 		}
1045 
1046 		gwin->clear_screen(true);
1047 
1048 		//
1049 		// White dot
1050 		//
1051 		Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_OUTNOISE_WAV);
1052 
1053 		Shape_frame *s = shapes.get_shape(0x14, 0);
1054 		unique_ptr<Image_buffer> backup(win->create_buffer(s->get_width() + 2, s->get_height() + 2));
1055 		win->get(backup.get(), centerx - 1, centery - 1);
1056 
1057 		ticks = SDL_GetTicks();
1058 		while (true) {
1059 			int x = centerx + rand() % 3 - 1;
1060 			int y = centery + rand() % 3 - 1;
1061 			FLASH_SHAPE1(x, y, 0x14, 0, 0);
1062 			WAITDELAYCYCLE1(2);
1063 			if (SDL_GetTicks() - ticks > 800)
1064 				break;
1065 		}
1066 	} catch (const UserSkipException &/*x*/) {
1067 	}
1068 }
1069 
1070 namespace {//anonymous
1071 class Hand_Handler {
1072 private:
1073 	enum HandlerScriptOps {
1074 		eNOP,                   // Does nothing except redraw static if needed
1075 		eHAND_HIT,              // Move hand to frame 3, and redraw static if needed
1076 		eHAND_RECOIL,           // Decrement hand frame, and redraw static if needed
1077 		eBLACK_SCREEN,          // Draw black screen
1078 		eSHOW_STATIC,           // Draw static
1079 		eFLASH_FAKE_TITLE,      // Draw fake title screen for 1 frame, then revert to black screen
1080 		eSHOW_FAKE_TITLE,       // Draw fake title screen permanently
1081 	};
1082 
1083 	static constexpr const HandlerScriptOps HandlerScript[] = {
1084 		eHAND_HIT        ,
1085 		eBLACK_SCREEN    ,
1086 		eSHOW_FAKE_TITLE ,
1087 		eSHOW_STATIC     , eHAND_RECOIL     ,
1088 		eNOP             , eHAND_RECOIL     ,
1089 		eNOP             , eHAND_RECOIL     ,
1090 		eBLACK_SCREEN    ,
1091 		eHAND_HIT        ,
1092 		eNOP             ,
1093 		eSHOW_FAKE_TITLE ,
1094 		eSHOW_STATIC     , eHAND_RECOIL     ,
1095 		eNOP             , eHAND_RECOIL     ,
1096 		eNOP             , eHAND_RECOIL     ,
1097 		eBLACK_SCREEN    ,
1098 		eHAND_HIT        ,
1099 		eNOP             ,
1100 		eFLASH_FAKE_TITLE, eHAND_RECOIL     ,
1101 		eFLASH_FAKE_TITLE, eHAND_RECOIL     ,
1102 		eFLASH_FAKE_TITLE, eHAND_RECOIL     ,
1103 		eSHOW_FAKE_TITLE
1104 	};
1105 
1106 	Image_window8 *win;
1107 	Shape_manager *sman;
1108 	std::unique_ptr<Shape_file> handshp;
1109 	std::unique_ptr<Image_buffer> handBackup;
1110 	std::unique_ptr<Image_buffer> staticScreen;
1111 	Shape_frame *screenShape;
1112 	Shape_frame *handFrame;
1113 	size_t scriptPosition;
1114 	int centerx, centery;
1115 	int handFrNum;
1116 	HandlerScriptOps currBackground;
1117 	bool playedStaticSFX;
1118 
1119 public:
Hand_Handler(BG_Game * game,Vga_file & shapes,Image_window8 * _win,Shape_manager * _sman,int _centerx,int _centery)1120 	Hand_Handler(BG_Game *game, Vga_file& shapes,
1121 	             Image_window8 *_win, Shape_manager *_sman,
1122 	             int _centerx, int _centery)
1123 		: win(_win), sman(_sman), staticScreen(win->create_buffer(160, 99)),
1124 		  screenShape(shapes.get_shape(0x1D, 0)), handFrame(nullptr),
1125 		  scriptPosition(0), centerx(_centerx), centery(_centery), handFrNum(-1),
1126 		  currBackground(eBLACK_SCREEN), playedStaticSFX(false) {
1127 		const str_int_pair &resource = game->get_resource("files/intro_hand");
1128 		U7object shpobj(resource.str, resource.num);
1129 		std::size_t len;
1130 		auto handBuffer = shpobj.retrieve(len);
1131 		IBufferDataSource ds(std::move(handBuffer), len);
1132 		handshp = std::make_unique<Shape_file>(&ds);
1133 	}
1134 	// Returns true to keep going.
1135 	bool draw_frame();
1136 
backup_shape_bkgnd(Shape_frame * fra,std::unique_ptr<Image_buffer> & backup)1137 	void backup_shape_bkgnd(Shape_frame *fra, std::unique_ptr<Image_buffer>& backup) {
1138 		backup = win->create_buffer(fra->get_width(), fra->get_height());
1139 
1140 		win->get(backup.get(), centerx - 156 - fra->get_xleft(),
1141 		         centery + 78 - fra->get_yabove());
1142 	}
1143 
restore_shape_bkgnd(Shape_frame * fra,std::unique_ptr<Image_buffer> & backup)1144 	void restore_shape_bkgnd(Shape_frame *fra, std::unique_ptr<Image_buffer>& backup) {
1145 		win->put(backup.get(), centerx - 156 - fra->get_xleft(),
1146 		         centery + 78 - fra->get_yabove());
1147 		backup.reset();
1148 	}
1149 };
1150 
1151 constexpr const Hand_Handler::HandlerScriptOps Hand_Handler::HandlerScript[];
1152 
draw_frame()1153 bool Hand_Handler::draw_frame() {
1154 	if (scriptPosition >= array_size(HandlerScript)) {
1155 		return false;
1156 	}
1157 	HandlerScriptOps currOp = HandlerScript[scriptPosition];
1158 	bool drawHand = false;
1159 	// Do hand first
1160 	switch (currOp) {
1161 		case eHAND_HIT:
1162 			// TODO: SFX for hand hitting monitor
1163 			drawHand = true;
1164 			handFrNum = 3;
1165 			break;
1166 		case eHAND_RECOIL:
1167 			if (handFrNum > 0) {
1168 				handFrNum--;
1169 				drawHand = true;
1170 			}
1171 			break;
1172 		default:
1173 			break;
1174 	};
1175 	if (drawHand) {
1176 		if (handFrame && handBackup) {
1177 			restore_shape_bkgnd(handFrame, handBackup);
1178 		}
1179 		handFrame = handshp->get_frame(handFrNum);
1180 		if (handFrame) {
1181 			backup_shape_bkgnd(handFrame, handBackup);
1182 			sman->paint_shape(centerx - 167, centery + 78, handFrame);
1183 		}
1184 	}
1185 	// Lets now handle backgrounds.
1186 	switch (currOp) {
1187 	case eHAND_HIT:
1188 	case eHAND_RECOIL:
1189 		// Special case for hand opcodes: redraw static
1190 		// if it is the current background.
1191 		if (currBackground != eSHOW_STATIC) {
1192 			break;
1193 		}
1194 		// FALL THROUGH
1195 	case eSHOW_STATIC:
1196 	case eNOP:
1197 		if (currOp == eSHOW_STATIC) {
1198 			currBackground = currOp;
1199 			if (!playedStaticSFX) {
1200 				playedStaticSFX = true;
1201 				//play static SFX
1202 				const File_spec sfxfile = BG_Game::get_sfx_subflex();
1203 				Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_MONITORSLAP_WAV);
1204 			}
1205 		}
1206 		staticScreen->fill_static(0, 7, 15);
1207 		win->put(staticScreen.get(),
1208 		         centerx + 12 - screenShape->get_width()/2,
1209 		         centery - 22 - screenShape->get_height()/2);
1210 		win->show();
1211 		drawHand = false;
1212 		break;
1213 	case eFLASH_FAKE_TITLE:
1214 	case eSHOW_FAKE_TITLE:
1215 		sman->paint_shape(centerx + 12, centery - 22, screenShape);
1216 		win->show();
1217 		drawHand = false;
1218 		if (currOp == eSHOW_FAKE_TITLE) {
1219 			currBackground = currOp;
1220 			break;
1221 		}
1222 		// FALL THROUGH
1223 	case eBLACK_SCREEN:
1224 		win->fill8(0, screenShape->get_width(), screenShape->get_height(),
1225 		           centerx + 12 - screenShape->get_width()/2,
1226 		           centery - 22 - screenShape->get_height()/2);
1227 		if (currOp == eBLACK_SCREEN) {
1228 			currBackground = currOp;
1229 			win->show();
1230 			drawHand = false;
1231 		}
1232 		break;
1233 	default:        // Just in case
1234 		return false;
1235 	};
1236 
1237 	if (drawHand) {
1238 		win->show();
1239 	}
1240 
1241 	scriptPosition++;
1242 	return scriptPosition < array_size(HandlerScript);
1243 }
1244 }// End of anonymous namespace
1245 
scene_desk()1246 void BG_Game::scene_desk() {
1247 	try {
1248 		Audio::get_ptr()->start_music(home_song_midi, false, INTROMUS);
1249 
1250 		gwin->clear_screen();
1251 		// Clip it to 320x200 region
1252 		WinClip clip(win, centerx - 160, centery - 100, 320, 200);
1253 
1254 		pal->load(INTROPAL_DAT, PATCH_INTROPAL, 1);
1255 		pal->apply();
1256 
1257 		// draw monitor (sh. 0x07, 0x08, 0x09, 0x0A: various parts of monitor)
1258 		sman->paint_shape(centerx, centery, shapes.get_shape(0x07, 0));
1259 		sman->paint_shape(centerx, centery, shapes.get_shape(0x09, 0));
1260 		sman->paint_shape(centerx, centery, shapes.get_shape(0x08, 0));
1261 		sman->paint_shape(centerx, centery, shapes.get_shape(0x0A, 0));
1262 
1263 		// Zoom out from zoomed in screen
1264 		{
1265 			unique_ptr<Image_buffer> unzoomed(win->create_buffer(320, 200));
1266 			win->get(unzoomed.get(), 0 + (win->get_game_width() - 320) / 2, 0 + (win->get_game_height() - 200) / 2);
1267 			unique_ptr<Image_buffer> zoomed(win->create_buffer(320, 200));
1268 
1269 			const Image_window::ScalerInfo &scaler = Image_window::Scalers[Image_window::point];
1270 
1271 			SDL_Surface *draw_surface = win->get_draw_surface();
1272 			SDL_SurfaceOwner unzoomed_surf(unzoomed.get(), draw_surface);
1273 			SDL_SurfaceOwner zoomed_surf(zoomed.get(), draw_surface);
1274 
1275 			Shape_frame *dotshp = shapes.get_shape(0x14, 0);
1276 			unique_ptr<Image_buffer> dot(win->create_buffer(dotshp->get_width(), dotshp->get_height()));
1277 			{
1278 				unique_ptr<Image_buffer> backup(win->create_buffer(dot->get_width(), dot->get_height()));
1279 				win->get(backup.get(), centerx + 12, centery - 22);
1280 				sman->paint_shape(centerx + 12, centery - 22, dotshp);
1281 				win->get(dot.get(), centerx + 12, centery - 22);
1282 				win->put(backup.get(), centerx + 12, centery - 22);
1283 			}
1284 			unique_ptr<Image_buffer> backup(win->create_buffer(dot->get_width() + 2, dot->get_height() + 2));
1285 			unzoomed->get(backup.get(), centerx + 12, centery - 22);
1286 
1287 			const int zx = 88;
1288 			const int zy = 22;
1289 			const int zw = 166;
1290 			const int zh = 112;
1291 
1292 			uint32 next_ticks = SDL_GetTicks() + 10;
1293 			for (int i = 0; i < 40; i++) {
1294 				int sw = zw + (320 - zw) * i / 40;
1295 				int sh = zh + (200 - zh) * i / 40;
1296 				int sx = zx + (0 - zx) * i / 40;
1297 				int sy = zy + (0 - zy) * i / 40;
1298 
1299 				// frame drop?
1300 				if (next_ticks > SDL_GetTicks()) {
1301 					unzoomed->put(backup.get(), centerx + 12, centery - 22);
1302 					unzoomed->put(dot.get(), centerx + rand() % 3 - 1 + 12, centery + rand() % 3 - 1 - 22);
1303 					scaler.arb->Scale(unzoomed_surf.get(), sx, sy, sw, sh, zoomed_surf.get(), 0, 0, 320, 200, true);
1304 					win->put(zoomed.get(), 0 + (win->get_game_width() - 320) / 2, 0 + (win->get_game_height() - 200) / 2);
1305 					win->show();
1306 					int delta = next_ticks - SDL_GetTicks();
1307 					if (delta < 0) delta = 0;
1308 					WAITDELAY(delta);
1309 				}
1310 				next_ticks += 10;
1311 			}
1312 
1313 			win->put(unzoomed.get(), 0 + (win->get_game_width() - 320) / 2, 0 + (win->get_game_height() - 200) / 2);
1314 			win->show();
1315 		}
1316 
1317 		{
1318 			// draw arm hitting pc
1319 			Hand_Handler hand(this, shapes, win, sman, centerx, centery);
1320 			while (hand.draw_frame()) {
1321 				WAITDELAY(60);
1322 			}
1323 
1324 			WAITDELAY(1300);
1325 		}
1326 
1327 		// "Something is obviously amiss"
1328 		sman->paint_shape(centerx, centery + 50, shapes.get_shape(0x15, 0));
1329 		win->show();
1330 		WAITDELAY(3000);
1331 
1332 		// TODO: misaligned?
1333 
1334 		// scroll right (sh. 0x06: map to the right of the monitor)
1335 		for (int i = 0; i <= 194; i += 2) { //was += 4
1336 			sman->paint_shape(centerx - i, centery, shapes.get_shape(0x07, 0));
1337 			sman->paint_shape(centerx - i, centery, shapes.get_shape(0x09, 0));
1338 			sman->paint_shape(centerx - i, centery, shapes.get_shape(0x08, 0));
1339 			sman->paint_shape(centerx - i, centery, shapes.get_shape(0x0A, 0));
1340 			sman->paint_shape(centerx - i + 12, centery - 22,
1341 			                  shapes.get_shape(0x1D, 0));
1342 			sman->paint_shape(topx + 320 - i, topy, shapes.get_shape(0x06, 0));
1343 
1344 			if (i > 75 && i < 194) {
1345 				// "It has been a long time..."
1346 				sman->paint_shape(centerx, centery + 50,
1347 				                  shapes.get_shape(0x16, 0));
1348 			}
1349 			win->show();
1350 			WAITDELAY(110); //was 30
1351 		}
1352 
1353 		WAITDELAY(1500);
1354 		// scroll down (sh. 0x0B: mouse + orb of moons, below map)
1355 		for (int i = 0; i <= 50; i += 2) {
1356 			sman->paint_shape(centerx - 194, centery - i,
1357 			                  shapes.get_shape(0x07, 0));
1358 			sman->paint_shape(centerx - 194, centery - i,
1359 			                  shapes.get_shape(0x09, 0));
1360 			sman->paint_shape(centerx - 194, centery - i,
1361 			                  shapes.get_shape(0x08, 0));
1362 			sman->paint_shape(centerx - 194, centery - i,
1363 			                  shapes.get_shape(0x0A, 0));
1364 			sman->paint_shape(centerx - 194 + 12, centery - 22 - i,
1365 			                  shapes.get_shape(0x1D, 0));
1366 			sman->paint_shape(topx + 319 - 194, topy - i,
1367 			                  shapes.get_shape(0x06, 0));
1368 			sman->paint_shape(topx + 319, topy + 199 - i,
1369 			                  shapes.get_shape(0x0B, 0));
1370 			// "The mystical Orb beckons you"
1371 			sman->paint_shape(centerx, topy, shapes.get_shape(0x17, 0));
1372 			win->show();
1373 			WAITDELAYCYCLE2(110);
1374 		}
1375 
1376 		sman->paint_shape(centerx - 194, centery - 50, shapes.get_shape(0x07, 0));
1377 		sman->paint_shape(centerx - 194, centery - 50, shapes.get_shape(0x09, 0));
1378 		sman->paint_shape(centerx - 194, centery - 50, shapes.get_shape(0x08, 0));
1379 		sman->paint_shape(centerx - 194, centery - 50, shapes.get_shape(0x0A, 0));
1380 		sman->paint_shape(centerx - 182, centery - 72, shapes.get_shape(0x1D, 0));
1381 		sman->paint_shape(topx + 319 - 194, topy - 50, shapes.get_shape(0x06, 0));
1382 		sman->paint_shape(topx + 319, topy + 149, shapes.get_shape(0x0B, 0));
1383 		// "It has opened gateways to Britannia in the past"
1384 		sman->paint_shape(centerx, topy, shapes.get_shape(0x18, 0));
1385 		win->show();
1386 
1387 		WAITDELAYCYCLE2(3200);
1388 		pal->fade_out(100);
1389 		gwin->clear_screen(true);
1390 	} catch (const UserSkipException &/*x*/) {
1391 	}
1392 }
1393 
scene_moongate()1394 void BG_Game::scene_moongate() {
1395 	// sh. 0x02, 0x03, 0x04, 0x05: various parts of moongate
1396 	// sh. 0x00, 0x01: parting trees before clearing
1397 	gwin->clear_screen(false);
1398 	pal->load(INTROPAL_DAT, PATCH_INTROPAL, 5);
1399 	pal->apply();
1400 
1401 	// "Behind your house is the circle of stones"
1402 	sman->paint_shape(centerx, centery + 50, shapes.get_shape(0x19, 0));
1403 	pal->fade_in(c_fade_in_time);
1404 
1405 	const File_spec sfxfile = get_sfx_subflex();
1406 	Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_MOONGATE_WAV);
1407 
1408 	// TODO: fade in screen while text is onscreen
1409 
1410 	WAITDELAY(4000);
1411 
1412 	// Bushes move out of the way
1413 	for (int i = 50; i >= -170; i -= 2) { // was for(i=120;i>=-170;i-=6)
1414 		sman->paint_shape(centerx + 1, centery + 1,
1415 		                  shapes.get_shape(0x02, 0));
1416 		sman->paint_shape(centerx + 1, centery + 1,
1417 		                  shapes.get_shape(0x03, 0));
1418 		sman->paint_shape(centerx + 1, centery + 1,
1419 		                  shapes.get_shape(0x04, 0));
1420 		sman->paint_shape(centerx + 1, centery + 1,
1421 		                  shapes.get_shape(0x05, 0));
1422 
1423 		// TODO: if you watch the original closely, the bushes are scaled up in size
1424 		// slightly as they move out.
1425 		sman->paint_shape(centerx + i, topy - ((i - 60) / 4), shapes.get_shape(0x00, 0));
1426 		sman->paint_shape(centerx - i, topy - ((i - 60) / 4), shapes.get_shape(0x01, 0));
1427 
1428 		if ((40 > i) && (i > -100)) {
1429 			// "Why is a moongate already there?"
1430 			sman->paint_shape(centerx, centery + 50, shapes.get_shape(0x1A, 0));
1431 		} else if (i <= -100) {
1432 			// "You have but one path to the answer"
1433 			sman->paint_shape(centerx, centery + 50, shapes.get_shape(0x1C, 0));
1434 		}
1435 
1436 		win->show();
1437 		WAITDELAYCYCLE3(80);
1438 	}
1439 
1440 	// Wait till the music finished playing
1441 	while (Audio::get_ptr()->is_track_playing(home_song_midi))
1442 		WAITDELAYCYCLE3(50);
1443 
1444 	// zoom (run) into moongate
1445 	sman->paint_shape(centerx + 1, centery + 1, shapes.get_shape(0x02, 0));
1446 	sman->paint_shape(centerx + 1, centery + 1, shapes.get_shape(0x03, 0));
1447 	sman->paint_shape(centerx + 1, centery + 1, shapes.get_shape(0x04, 0));
1448 	sman->paint_shape(centerx + 1, centery + 1, shapes.get_shape(0x05, 0));
1449 
1450 	unique_ptr<Image_buffer> unzoomed(win->create_buffer(320, 200));
1451 	win->get(unzoomed.get(), 0 + (win->get_game_width() - 320) / 2, 0 + (win->get_game_height() - 200) / 2);
1452 	unique_ptr<Image_buffer> zoomed(win->create_buffer(320, 200));
1453 
1454 	const Image_window::ScalerInfo &scaler = Image_window::Scalers[Image_window::point];
1455 
1456 	SDL_Surface *draw_surface = win->get_draw_surface();
1457 
1458 	SDL_SurfaceOwner unzoomed_surf(unzoomed.get(), draw_surface);
1459 	SDL_SurfaceOwner zoomed_surf(zoomed.get(), draw_surface);
1460 
1461 	const int zx = 151;
1462 	const int zy = 81;
1463 	const int zw = 5;
1464 	const int zh = 4;
1465 
1466 	Audio::get_ptr()->play_sound_effect(sfxfile, INTROSFX_MT32_FLX_INTRO_MT_SHOT_WAV);
1467 
1468 	uint32 next_ticks = SDL_GetTicks() + 10;
1469 	for (int i = 159; i >= 0; i--) {
1470 		int sw = zw + (320 - zw) * i / 160;
1471 		int sh = zh + (200 - zh) * i / 160;
1472 		int sx = zx + (0 - zx) * i / 160;
1473 		int sy = zy + (0 - zy) * i / 160;
1474 
1475 		// frame drop?
1476 		if (next_ticks > SDL_GetTicks()) {
1477 			scaler.arb->Scale(unzoomed_surf.get(), sx, sy, sw, sh, zoomed_surf.get(), 0, 0, 320, 200, true);
1478 			win->put(zoomed.get(), 0 + (win->get_game_width() - 320) / 2, 0 + (win->get_game_height() - 200) / 2);
1479 			win->show();
1480 			int delta = next_ticks - SDL_GetTicks();
1481 			if (delta < 0) delta = 0;
1482 			WAITDELAYCYCLE3(delta);
1483 		}
1484 		next_ticks += 5;
1485 	}
1486 }
1487 
get_menu_shape()1488 Shape_frame *BG_Game::get_menu_shape() {
1489 	return menushapes.get_shape(0x2, 0);
1490 }
1491 
1492 
top_menu()1493 void BG_Game::top_menu() {
1494 	Audio::get_ptr()->start_music(menu_midi, true, INTROMUS);
1495 	sman->paint_shape(topx, topy, get_menu_shape());
1496 	pal->load(INTROPAL_DAT, PATCH_INTROPAL, 6);
1497 	pal->fade_in(60);
1498 }
1499 
show_journey_failed()1500 void BG_Game::show_journey_failed() {
1501 	pal->fade_out(50);
1502 	gwin->clear_screen(true);
1503 	sman->paint_shape(topx, topy, get_menu_shape());
1504 	journey_failed_text();
1505 }
1506 
1507 class ExVoiceBuffer {
1508 private:
1509 	const char *file;
1510 	const char *patch;
1511 	int index;
1512 	bool played;
1513 public:
1514 	bool play_it();
1515 
ExVoiceBuffer(const char * f,const char * p,int i)1516 	ExVoiceBuffer(const char *f, const char *p, int i)
1517 		: file(f), patch(p), index(i), played(false)
1518 	{ }
can_play() const1519 	bool can_play() const {
1520 		return file || patch;
1521 	}
1522 };
1523 
play_it()1524 bool ExVoiceBuffer::play_it() {
1525 	size_t  size;
1526 	U7multiobject voc(file, patch, index);
1527 	auto buffer = voc.retrieve(size);
1528 	uint8 *buf = buffer.get();
1529 	if (!memcmp(buf, "voc", sizeof("voc") - 1)) {
1530 		// IFF junk.
1531 		buf += 8;
1532 		size -= 8;
1533 	}
1534 	Audio::get_ptr()->copy_and_play(buf, size, false);
1535 	played = true;
1536 
1537 	return false;
1538 }
1539 
end_game(bool success)1540 void BG_Game::end_game(bool success) {
1541 	Font *font = fontManager.get_font("MENU_FONT");
1542 
1543 	if (!success) {
1544 		TextScroller text(MAINSHP_FLX, 0x15, font, nullptr);
1545 		gwin->clear_screen();
1546 		pal->load(INTROPAL_DAT, PATCH_INTROPAL, 0);
1547 		for (sint32 i = 0; i < text.get_count(); i++) {
1548 			text.show_line(gwin, topx, topx + 320, topy + 20 + i * 12, i);
1549 		}
1550 
1551 		pal->fade_in(c_fade_in_time);
1552 		wait_delay(10000);
1553 		pal->fade_out(c_fade_out_time);
1554 
1555 		gwin->clear_screen();
1556 		font->center_text(ibuf, centerx, centery - 10, get_text_msg(end_of_ultima7));
1557 		pal->fade_in(c_fade_in_time);
1558 		wait_delay(4000);
1559 		pal->fade_out(c_fade_out_time);
1560 
1561 		gwin->clear_screen();
1562 		font->center_text(ibuf, centerx, centery - 10, get_text_msg(end_of_britannia));
1563 		pal->fade_in(c_fade_in_time);
1564 		wait_delay(4000);
1565 		pal->fade_out(c_fade_out_time);
1566 		gwin->clear_screen(true);
1567 		return;
1568 	}
1569 
1570 	Audio *audio = Audio::get_ptr();
1571 	audio->stop_music();
1572 	MyMidiPlayer *midi = audio->get_midi();
1573 	if (midi) midi->set_timbre_lib(MyMidiPlayer::TIMBRE_LIB_ENDGAME);
1574 
1575 	bool speech = Audio::get_ptr()->is_audio_enabled() &&
1576 	              Audio::get_ptr()->is_speech_enabled();
1577 	bool want_subs = !speech || Audio::get_ptr()->is_speech_with_subs();
1578 
1579 	// Clear screen
1580 	gwin->clear_screen(true);
1581 
1582 	ExVoiceBuffer speech1(ENDGAME, PATCH_ENDGAME, 7);
1583 	ExVoiceBuffer speech2(ENDGAME, PATCH_ENDGAME, 8);
1584 	ExVoiceBuffer speech3(ENDGAME, PATCH_ENDGAME, 9);
1585 
1586 	/* There seems to be something wrong with the shapes. Needs investigating
1587 	U7multiobject shapes(ENDGAME, PATCH_ENDGAME, 10);
1588 	shapes.retrieve("endgame.shp");
1589 	Shape_file sf("endgame.shp");
1590 	int x = get_width()/2-160;
1591 	int y = get_height()/2-100;
1592 	cout << "Shape in Endgame.dat has " << sf.get_num_frames() << endl;
1593 	*/
1594 
1595 	// Fli Buffers
1596 	playfli fli1(ENDGAME, PATCH_ENDGAME, 0);
1597 	playfli fli2(ENDGAME, PATCH_ENDGAME, 1);
1598 	playfli fli3(ENDGAME, PATCH_ENDGAME, 2);
1599 
1600 	fli1.play(win, 0, 0, 0);
1601 
1602 	// Start endgame music.
1603 	if (midi) midi->start_music(ENDSCORE_XMI, 1, false);
1604 
1605 	try {
1606 		unsigned int next = 0;
1607 		for (unsigned int i = 0; i < 240; i++) {
1608 			next = fli1.play(win, 0, 1, next);
1609 			if (wait_delay(0)) {
1610 				throw UserSkipException();
1611 			}
1612 		}
1613 
1614 		for (unsigned int i = 1; i < 150; i++) {
1615 			next = fli1.play(win, i, i + 1, next);
1616 			if (wait_delay(0)) {
1617 				throw UserSkipException();
1618 			}
1619 		}
1620 
1621 		if (speech) speech1.play_it();
1622 		Font *endfont2 = fontManager.get_font("END2_FONT");
1623 		Font *endfont3 = fontManager.get_font("END3_FONT");
1624 		Font *normal = fontManager.get_font("NORMAL_FONT");
1625 
1626 		const char *message = get_text_msg(you_cannot_do_that);
1627 		int height = topy + 200 - endfont2->get_text_height() * 2;
1628 		int width = (gwin->get_width() - endfont2->get_text_width(message)) / 2;
1629 
1630 		for (unsigned int i = 150; i < 204; i++) {
1631 			next = fli1.play(win, i, i, next);
1632 			if (want_subs)
1633 				endfont2->draw_text(ibuf, width, height, message);
1634 			win->show();
1635 			if (wait_delay(0, 0, 1)) {
1636 				throw UserSkipException();
1637 			}
1638 		}
1639 
1640 		// Set new music
1641 		if (midi) midi->start_music(ENDSCORE_XMI, 2, false);
1642 
1643 		// Set speech
1644 
1645 		if (speech) speech2.play_it();
1646 
1647 		message = get_text_msg(damn_avatar);
1648 		width = (gwin->get_width() - endfont2->get_text_width(message)) / 2;
1649 
1650 		for (unsigned int i = 0; i < 100; i++) {
1651 			next = fli2.play(win, i, i, next);
1652 			if (want_subs)
1653 				endfont2->draw_text(ibuf, width, height, message);
1654 			win->show();
1655 			if (wait_delay(0, 0, 1)) {
1656 				throw UserSkipException();
1657 			}
1658 		}
1659 
1660 		Palette *pal = fli2.get_palette();
1661 		next = SDL_GetTicks();
1662 		for (unsigned int i = 1000 + next; next < i; next += 10) {
1663 			// Speed related frame skipping detection
1664 			bool skip_frame = Game_window::get_instance()->get_frame_skipping() && SDL_GetTicks() >= next;
1665 			while (SDL_GetTicks() < next)
1666 				;
1667 			if (!skip_frame) {
1668 				pal->set_brightness((i - next) / 10);
1669 				pal->apply();
1670 			}
1671 			if (wait_delay(0, 0, 1)) {
1672 				throw UserSkipException();
1673 			}
1674 		}
1675 
1676 		pal->set_brightness(80);    // Set readable brightness
1677 		// Text message 1
1678 
1679 		// Paint backgound black
1680 		win->fill8(0);
1681 
1682 		// Paint text
1683 		message = get_text_msg(blackgate_destroyed);
1684 		width = (gwin->get_width() - normal->get_text_width(message)) / 2;
1685 		height = (gwin->get_height() - normal->get_text_height()) / 2;
1686 
1687 		normal->draw_text(ibuf, width, height, message);
1688 
1689 		// Fade in for 1 sec (50 cycles)
1690 		pal->fade(50, 1, 0);
1691 
1692 		// Display text for 3 seconds
1693 		for (unsigned int i = 0; i < 30; i++) {
1694 			if (wait_delay(100)) {
1695 				throw UserSkipException();
1696 			}
1697 		}
1698 
1699 		// Fade out for 1 sec (50 cycles)
1700 		pal->fade(50, 0, 0);
1701 
1702 		// Now the second text message
1703 
1704 		// Paint backgound black
1705 		win->fill8(0);
1706 
1707 		// Paint text
1708 		message = get_text_msg(guardian_has_stopped);
1709 		width = (gwin->get_width() - normal->get_text_width(message)) / 2;
1710 
1711 		normal->draw_text(ibuf, width, height, message);
1712 
1713 		// Fade in for 1 sec (50 cycles)
1714 		pal->fade(50, 1, 0);
1715 
1716 		// Display text for approx 3 seonds
1717 		for (unsigned int i = 0; i < 30; i++) {
1718 			if (wait_delay(100)) {
1719 				throw UserSkipException();
1720 			}
1721 		}
1722 
1723 		// Fade out for 1 sec (50 cycles)
1724 		pal->fade(50, 0, 0);
1725 
1726 		fli3.play(win, 0, 0, next);
1727 		pal = fli3.get_palette();
1728 		next = SDL_GetTicks();
1729 		for (unsigned int i = 1000 + next; next < i; next += 10) {
1730 			// Speed related frame skipping detection
1731 			bool skip_frame = Game_window::get_instance()->get_frame_skipping() && SDL_GetTicks() >= next;
1732 			while (SDL_GetTicks() < next)
1733 				;
1734 			if (!skip_frame) {
1735 				pal->set_brightness(100 - (i - next) / 10);
1736 				pal->apply();
1737 			}
1738 			if (wait_delay(0, 0, 1)) {
1739 				throw UserSkipException();
1740 			}
1741 		}
1742 
1743 		if (speech) speech3.play_it();
1744 
1745 		playfli::fliinfo finfo;
1746 		fli3.info(&finfo);
1747 
1748 		int m;
1749 		int starty = (gwin->get_height() - endfont3->get_text_height() * 8) / 2;
1750 
1751 		next = SDL_GetTicks();
1752 		for (unsigned int i = next + 28000; i > next;) {
1753 			for (unsigned int j = 0; j < static_cast<unsigned>(finfo.frames); j++) {
1754 				next = fli3.play(win, j, j, next);
1755 				if (want_subs) {
1756 					for (m = 0; m < 8; m++)
1757 						endfont3->center_text(ibuf, centerx, starty + endfont3->get_text_height()*m, get_text_msg(txt_screen0 + m));
1758 				}
1759 				win->show();
1760 				if (wait_delay(10, 0, 1)) {
1761 					throw UserSkipException();
1762 				}
1763 			}
1764 		}
1765 
1766 		next = SDL_GetTicks();
1767 		for (unsigned int i = 1000 + next; next < i; next += 10) {
1768 			// Speed related frame skipping detection
1769 			bool skip_frame = Game_window::get_instance()->get_frame_skipping() && SDL_GetTicks() >= next;
1770 			while (SDL_GetTicks() < next)
1771 				;
1772 			if (!skip_frame) {
1773 				pal->set_brightness((i - next) / 10);
1774 				pal->apply();
1775 			}
1776 			if (wait_delay(0, 0, 1)) {
1777 				throw UserSkipException();
1778 			}
1779 		}
1780 
1781 		// Text Screen 1
1782 		pal->set_brightness(80);    // Set readable brightness
1783 
1784 		// Paint backgound black
1785 		win->fill8(0);
1786 
1787 		//Because of the German version we have to fit 11 lines of height 20 into a screen of 200 pixels
1788 		//so starty has needs to be a tiny bit in the negative but not -10
1789 		starty = (gwin->get_height() - normal->get_text_height() * 11) / 2.5;
1790 
1791 		for (unsigned int i = 0; i < 11; i++) {
1792 			message = get_text_msg(txt_screen1 + i);
1793 			normal->draw_text(ibuf, centerx - normal->get_text_width(message) / 2, starty + normal->get_text_height()*i, message);
1794 		}
1795 
1796 		// Fade in for 1 sec (50 cycles)
1797 		pal->fade(50, 1, 0);
1798 
1799 		// Display text for 20 seconds (only 10 at the moment)
1800 		for (unsigned int i = 0; i < 100; i++) {
1801 			if (wait_delay(100)) {
1802 				throw UserSkipException();
1803 			}
1804 		}
1805 
1806 		// Fade out for 1 sec (50 cycles)
1807 		pal->fade(50, 0, 0);
1808 
1809 		if (wait_delay(10))
1810 			throw UserSkipException();
1811 
1812 		// Text Screen 2
1813 
1814 		// Paint backgound black
1815 		win->fill8(0);
1816 
1817 		starty = (gwin->get_height() - normal->get_text_height() * 9) / 2;
1818 
1819 		for (unsigned int i = 0; i < 9; i++) {
1820 			message = get_text_msg(txt_screen2 + i);
1821 			normal->draw_text(ibuf, centerx - normal->get_text_width(message) / 2, starty + normal->get_text_height()*i, message);
1822 		}
1823 
1824 		// Fade in for 1 sec (50 cycles)
1825 		pal->fade(50, 1, 0);
1826 
1827 		// Display text for 20 seonds (only 8 at the moment)
1828 		for (unsigned int i = 0; i < 80; i++) {
1829 			if (wait_delay(100)) {
1830 				throw UserSkipException();
1831 			}
1832 		}
1833 
1834 		// Fade out for 1 sec (50 cycles)
1835 		pal->fade(50, 0, 0);
1836 
1837 		if (wait_delay(10))
1838 			throw UserSkipException();
1839 
1840 		// Text Screen 3
1841 
1842 		// Paint backgound black
1843 		win->fill8(0);
1844 
1845 		starty = (gwin->get_height() - normal->get_text_height() * 8) / 2;
1846 
1847 		for (unsigned int i = 0; i < 8; i++) {
1848 			message = get_text_msg(txt_screen3 + i);
1849 			normal->draw_text(ibuf, centerx - normal->get_text_width(message) / 2, starty + normal->get_text_height()*i, message);
1850 		}
1851 
1852 		// Fade in for 1 sec (50 cycles)
1853 		pal->fade(50, 1, 0);
1854 
1855 		// Display text for 20 seonds (only 8 at the moment)
1856 		for (unsigned int i = 0; i < 80; i++) {
1857 			if (wait_delay(100)) {
1858 				throw UserSkipException();
1859 			}
1860 		}
1861 
1862 		// Fade out for 1 sec (50 cycles)
1863 		pal->fade(50, 0, 0);
1864 
1865 		if (wait_delay(10))
1866 			throw UserSkipException();
1867 
1868 		// Text Screen 4
1869 
1870 		// Paint backgound black
1871 		win->fill8(0);
1872 
1873 		starty = (gwin->get_height() - normal->get_text_height() * 5) / 2;
1874 
1875 		for (unsigned int i = 0; i < 5; i++) {
1876 			message = get_text_msg(txt_screen4 + i);
1877 			normal->draw_text(ibuf, centerx - normal->get_text_width(message) / 2, starty + normal->get_text_height()*i, message);
1878 		}
1879 
1880 		// Fade in for 1 sec (50 cycles)
1881 		pal->fade(50, 1, 0);
1882 
1883 		// Display text for 10 seonds (only 5 at the moment)
1884 		for (unsigned int i = 0; i < 50; i++) {
1885 			if (wait_delay(100)) {
1886 				throw UserSkipException();
1887 			}
1888 		}
1889 
1890 		// Fade out for 1 sec (50 cycles)
1891 		pal->fade(50, 0, 0);
1892 #if 0
1893 		//TODO: only when finishing a game and not when viewed from menu
1894 		if (when not in menu) {
1895 			if (wait_delay(10)) break;
1896 
1897 			// Congratulations
1898 
1899 			// Paint backgound black
1900 			win->fill8(0);
1901 
1902 			starty = (gwin->get_height() - normal->get_text_height() * 6) / 2;
1903 
1904 			//TODO: figure out the time it took to complete the game
1905 			// in exultmsg.txt it is "%d year s ,  %d month s , &  %d day s"
1906 			// only showing years or months if there were any
1907 			for (unsigned int i = 0; i < 9; i++) {
1908 				message = get_text_msg(congrats + i);
1909 				normal->draw_text(ibuf, centerx - normal->get_text_width(message) / 2, starty + normal->get_text_height()*i, message);
1910 			}
1911 
1912 			// Fade in for 1 sec (50 cycles)
1913 			pal->fade(50, 1, 0);
1914 
1915 			// Display text for 20 seonds (only 8 at the moment)
1916 			for (unsigned int i = 0; i < 80; i++) {
1917 				if (wait_delay(100)) {
1918 					throw UserSkipException();
1919 				}
1920 			}
1921 
1922 			// Fade out for 1 sec (50 cycles)
1923 			pal->fade(50, 0, 0);
1924 		}
1925 #endif
1926 
1927 	} catch (const UserSkipException &/*x*/) {
1928 	}
1929 
1930 	if (midi) {
1931 		midi->stop_music();
1932 		midi->set_timbre_lib(MyMidiPlayer::TIMBRE_LIB_GAME);
1933 	}
1934 
1935 	audio->stop_music();
1936 
1937 	gwin->clear_screen(true);
1938 }
1939 
show_quotes()1940 void BG_Game::show_quotes() {
1941 	Audio::get_ptr()->start_music(quotes_midi, false, INTROMUS);
1942 	TextScroller quotes(MAINSHP_FLX, 0x10,
1943 	                    fontManager.get_font("MENU_FONT"),
1944 	                    menushapes.extract_shape(0x14)
1945 	                   );
1946 	quotes.run(gwin);
1947 }
1948 
show_credits()1949 void BG_Game::show_credits() {
1950 	pal->load(INTROPAL_DAT, PATCH_INTROPAL, 6);
1951 	Audio::get_ptr()->start_music(credits_midi, false, INTROMUS);
1952 	TextScroller credits(MAINSHP_FLX, 0x0E,
1953 	                     fontManager.get_font("MENU_FONT"),
1954 	                     menushapes.extract_shape(0x14)
1955 	                    );
1956 	if (credits.run(gwin)) { // Watched through the entire sequence?
1957 		std::ofstream quotesflg;
1958 		U7open(quotesflg, "<SAVEGAME>/quotes.flg");
1959 		quotesflg.close();
1960 	}
1961 }
1962 
new_game(Vga_file & shapes)1963 bool BG_Game::new_game(Vga_file &shapes) {
1964 	int menuy = topy + 110;
1965 	Font *font = fontManager.get_font("MENU_FONT");
1966 
1967 	Vga_file faces_vga;
1968 	// Need to know if SI is installed
1969 	bool si_installed =
1970 	    (gamemanager->is_si_installed() || gamemanager->is_ss_installed())
1971 	    && U7exists("<SERPENT_STATIC>/shapes.vga");
1972 
1973 	// List of files to load.
1974 	std::vector<std::pair<std::string, int> > source;
1975 	source.emplace_back(FACES_VGA, -1);
1976 	// Multiracial faces.
1977 	const str_int_pair &resource = get_resource("files/mrfacesvga");
1978 	source.emplace_back(resource.str, resource.num);
1979 	source.emplace_back(PATCH_FACES, -1);
1980 	faces_vga.load(source);
1981 
1982 	const int max_name_len = 16;
1983 	char npc_name[max_name_len + 1];
1984 	char disp_name[max_name_len + 2];
1985 	npc_name[0] = 0;
1986 
1987 	int selected = 0;
1988 	int num_choices = 4;
1989 	SDL_Event event;
1990 	bool editing = true;
1991 	bool redraw = true;
1992 	bool ok = true;
1993 
1994 	// Skin info
1995 	Avatar_default_skin *defskin = Shapeinfo_lookup::GetDefaultAvSkin();
1996 	Skin_data *skindata =
1997 	    Shapeinfo_lookup::GetSkinInfoSafe(
1998 	        defskin->default_skin, defskin->default_female, si_installed);
1999 
2000 	Palette *pal = gwin->get_pal();
2001 	// This should work because the palette in exult_bg.flx is
2002 	// a single-file object.
2003 	pal->load(File_spec(INTROPAL_DAT, 6),
2004 	          File_spec(get_resource("files/gameflx").str, EXULT_BG_FLX_U7MENUPAL_PAL),
2005 	          File_spec(PATCH_INTROPAL, 6), 0);
2006 	auto *oldpal = new Palette();
2007 	oldpal->load(INTROPAL_DAT, PATCH_INTROPAL, 6);
2008 
2009 	// Create palette translation table. Maybe make them static?
2010 	auto *transto = new unsigned char[256];
2011 	oldpal->create_palette_map(pal, transto);
2012 	delete oldpal;
2013 	pal->apply(true);
2014 	do {
2015 		Delay();
2016 		if (redraw) {
2017 			gwin->clear_screen();
2018 			sman->paint_shape(topx, topy, shapes.get_shape(0x2, 0), false, transto);
2019 			sman->paint_shape(topx + 10, menuy + 10, shapes.get_shape(0xC, selected == 0), false, transto);
2020 			Shape_frame *sex_shape = shapes.get_shape(0xA, selected == 1);
2021 			sman->paint_shape(topx + 10, menuy + 25, sex_shape, false, transto);
2022 			int sex_width = sex_shape->get_width() + 10;
2023 			if (sex_width > 35) sex_width += 25;
2024 			else sex_width = 60;
2025 
2026 			sman->paint_shape(topx + sex_width, menuy + 25, shapes.get_shape(0xB, skindata->is_female), false, transto);
2027 
2028 			Shape_frame *portrait = faces_vga.get_shape(skindata->face_shape, skindata->face_frame);
2029 			sman->paint_shape(topx + 290, menuy + 61, portrait);
2030 
2031 			sman->paint_shape(topx + 10, topy + 180, shapes.get_shape(0x8, selected == 2), false, transto);
2032 			sman->paint_shape(centerx + 10, topy + 180, shapes.get_shape(0x7, selected == 3), false, transto);
2033 			if (selected == 0)
2034 				snprintf(disp_name, max_name_len + 2, "%s_", npc_name);
2035 			else
2036 				snprintf(disp_name, max_name_len + 2, "%s", npc_name);
2037 			font->draw_text(ibuf, topx + 60, menuy + 10, disp_name, transto);
2038 			gwin->get_win()->show();
2039 			redraw = false;
2040 		}
2041 
2042 		while (SDL_PollEvent(&event)) {
2043 			Uint16 keysym_unicode = 0;
2044 			bool isTextInput = false;
2045 			if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
2046 				SDL_Rect rectName   = { topx + 10,    menuy + 10, 130,  16 };
2047 				SDL_Rect rectSex    = { topx + 10,    menuy + 25, 130,  16 };
2048 				SDL_Rect rectOnward = { topx + 10,    topy + 180, 130,  16 };
2049 				SDL_Rect rectReturn = { centerx + 10, topy + 180, 130,  16 };
2050 				SDL_Point point;
2051 				gwin->get_win()->screen_to_game(event.button.x, event.button.y, gwin->get_fastmouse(), point.x, point.y);
2052 				if (SDL_EnclosePoints(&point, 1, &rectName, nullptr)) {
2053 					if (event.type == SDL_MOUSEBUTTONDOWN) {
2054 						selected = 0;
2055 					} else if (selected == 0 && touchui != nullptr) {
2056 						touchui->promptForName(npc_name);
2057 					}
2058 					redraw = true;
2059 				} else if (SDL_EnclosePoints(&point, 1, &rectSex, nullptr)) {
2060 					if (event.type == SDL_MOUSEBUTTONDOWN) {
2061 						selected = 1;
2062 					} else if (selected == 1) {
2063 						skindata = Shapeinfo_lookup::GetNextSelSkin(skindata, si_installed, true);
2064 					}
2065 					redraw = true;
2066 				} else if (SDL_EnclosePoints(&point, 1, &rectOnward, nullptr)) {
2067 					if (event.type == SDL_MOUSEBUTTONDOWN) {
2068 						selected = 2;
2069 					} else if (selected == 2) {
2070 						editing = false;
2071 						ok = true;
2072 					}
2073 					redraw = true;
2074 				} else if (SDL_EnclosePoints(&point, 1, &rectReturn, nullptr)) {
2075 					if (event.type == SDL_MOUSEBUTTONDOWN) {
2076 						selected = 3;
2077 					} else if (selected == 3) {
2078 						editing = false;
2079 						ok = false;
2080 					}
2081 					redraw = true;
2082 				}
2083 			} else if (event.type == TouchUI::eventType) {
2084 				if (event.user.code == TouchUI::EVENT_CODE_TEXT_INPUT) {
2085 					if (selected == 0 && event.user.data1 != nullptr) {
2086 						strncpy(npc_name, static_cast<char*>(event.user.data1), max_name_len);
2087 						npc_name[max_name_len] = '\0';
2088 						free(event.user.data1);
2089 						redraw = true;
2090 					}
2091 				}
2092 			} else if (event.type == SDL_TEXTINPUT) {
2093 				isTextInput = true;
2094 				event.type = SDL_KEYDOWN;
2095 				event.key.keysym.sym = SDLK_UNKNOWN;
2096 				keysym_unicode = event.text.text[0];
2097 			}
2098 			if (event.type == SDL_KEYDOWN) {
2099 				redraw = true;
2100 				switch (event.key.keysym.sym) {
2101 				case SDLK_SPACE:
2102 					if (selected == 0) {
2103 						int len = strlen(npc_name);
2104 						if (len < max_name_len) {
2105 							npc_name[len] = ' ';
2106 							npc_name[len + 1] = 0;
2107 						}
2108 					} else if (selected == 1)
2109 						skindata = Shapeinfo_lookup::GetNextSelSkin(skindata, si_installed, true);
2110 					else if (selected == 2) {
2111 						editing = false;
2112 						ok = true;
2113 					} else if (selected == 3)
2114 						editing = ok = false;
2115 					break;
2116 				case SDLK_LEFT:
2117 					if (selected == 1)
2118 						skindata = Shapeinfo_lookup::GetPrevSelSkin(skindata, si_installed, true);
2119 					break;
2120 				case SDLK_RIGHT:
2121 					if (selected == 1)
2122 						skindata = Shapeinfo_lookup::GetNextSelSkin(skindata, si_installed, true);
2123 					break;
2124 				case SDLK_ESCAPE:
2125 					editing = false;
2126 					ok = false;
2127 					break;
2128 				case SDLK_TAB:
2129 				case SDLK_DOWN:
2130 					++selected;
2131 					if (selected == num_choices)
2132 						selected = 0;
2133 					break;
2134 				case SDLK_UP:
2135 					--selected;
2136 					if (selected < 0)
2137 						selected = num_choices - 1;
2138 					break;
2139 				case SDLK_RETURN:
2140 				case SDLK_KP_ENTER:
2141 					if (selected < 2)
2142 						++selected;
2143 					else if (selected == 2) {
2144 						editing = false;
2145 						ok = true;
2146 					} else
2147 						editing = ok = false;
2148 					break;
2149 				case SDLK_BACKSPACE:
2150 					if (selected == 0 && strlen(npc_name) > 0)
2151 						npc_name[strlen(npc_name) - 1] = 0;
2152 					break;
2153 				default: {
2154 					if ((isTextInput && selected == 0) || (!isTextInput && keysym_unicode > +'~' && selected == 0))
2155 					{
2156 						int len = strlen(npc_name);
2157 						char chr = 0;
2158 						if ((keysym_unicode & 0xFF80) == 0)
2159 							chr = keysym_unicode & 0x7F;
2160 
2161 						if (chr >= ' ' && len < max_name_len) {
2162 							npc_name[len] = chr;
2163 							npc_name[len + 1] = 0;
2164 						}
2165 					} else
2166 						redraw = false;
2167 				}
2168 				break;
2169 				}
2170 			}
2171 		}
2172 	} while (editing);
2173 
2174 	delete [] transto;
2175 	gwin->clear_screen();
2176 
2177 	if (ok) {
2178 #ifdef DEBUG
2179 		std::cout << "Skin is: " << skindata->skin_id << " Sex is: " << skindata->is_female << std::endl;
2180 #endif
2181 		set_avskin(skindata->skin_id);
2182 		set_avname(npc_name);
2183 		set_avsex(skindata->is_female);
2184 		pal->fade_out(c_fade_out_time);
2185 		gwin->clear_screen(true);
2186 		ok = gwin->init_gamedat(true);
2187 	} else {
2188 		pal->load(INTROPAL_DAT, PATCH_INTROPAL, 6);
2189 		sman->paint_shape(topx, topy, shapes.get_shape(0x2, 0));
2190 		pal->apply();
2191 	}
2192 	return ok;
2193 }
2194