1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * Handles the inventory and conversation windows.
22  *
23  * And the save/load game windows. Some of this will be platform
24  * specific - I'll try to separate this ASAP.
25  *
26  * And there's still a bit of tidying and commenting to do yet.
27  */
28 
29 #include "tinsel/actors.h"
30 #include "tinsel/anim.h"
31 #include "tinsel/background.h"
32 #include "tinsel/config.h"
33 #include "tinsel/cursor.h"
34 #include "tinsel/drives.h"
35 #include "tinsel/dw.h"
36 #include "tinsel/film.h"
37 #include "tinsel/font.h"
38 #include "tinsel/graphics.h"
39 #include "tinsel/handle.h"
40 #include "tinsel/heapmem.h"
41 #include "tinsel/dialogs.h"
42 #include "tinsel/multiobj.h"
43 #include "tinsel/music.h"
44 #include "tinsel/palette.h"
45 #include "tinsel/pcode.h"
46 #include "tinsel/pdisplay.h"
47 #include "tinsel/pid.h"
48 #include "tinsel/polygons.h"
49 #include "tinsel/savescn.h"
50 #include "tinsel/sched.h"
51 #include "tinsel/scn.h"
52 #include "common/serializer.h"
53 #include "tinsel/sound.h"
54 #include "tinsel/strres.h"
55 #include "tinsel/sysvar.h"
56 #include "tinsel/text.h"
57 #include "tinsel/timers.h"		// For ONE_SECOND constant
58 #include "tinsel/tinlib.h"
59 #include "tinsel/tinsel.h"		// For engine access
60 #include "tinsel/token.h"
61 
62 #include "common/textconsole.h"
63 
64 namespace Tinsel {
65 
66 //----------------- LOCAL DEFINES --------------------
67 
68 #define HOPPER_FILENAME		"hopper"
69 
70 #define INV_PICKUP	PLR_SLEFT		// Local names
71 #define INV_LOOK	PLR_SRIGHT		//	for button events
72 #define INV_ACTION	PLR_DLEFT		//
73 // For SlideSlider() and similar
74 enum SSFN {
75 	S_START, S_SLIDE, S_END, S_TIMEUP, S_TIMEDN
76 };
77 
78 /** attribute values - may become bit field if further attributes are added */
79 enum {
80 	IO_ONLYINV1	= 0x01,
81 	IO_ONLYINV2	= 0x02,
82 	IO_DROPCODE	= 0x04
83 };
84 
85 //-----------------------
86 // Moveable window translucent rectangle position limits
87 enum {
88 	MAXLEFT		= 315,		//
89 	MINRIGHT	= 3,		// These values keep 2 pixcells
90 	MINTOP		= -13,		// of header on the screen.
91 	MAXTOP		= 195		//
92 };
93 
94 //-----------------------
95 // Indices into hWinParts's reels
96 
97 enum PARTS_INDEX {
98 	IX_SLIDE = 0,		// Slider
99 	IX_V26 = 1,
100 	IX_V52 = 2,
101 	IX_V78 = 3,
102 	IX_V104 = 4,
103 	IX_V130 = 5,
104 	IX_H26 = 6,
105 	IX_H52 = 7,
106 	IX_H78 = 8,
107 	IX_H104 = 9,
108 	IX_H130 = 10,
109 	IX_H156 = 11,
110 	IX_H182 = 12,
111 	IX_H208 = 13,
112 	IX_H234 = 14,
113 	IX_TL = 15,		// Top left corner
114 	IX_TR = 16,		// Top right corner
115 	IX_BL = 17,		// Bottom left corner
116 	IX_BR = 18,		// Bottom right corner
117 
118 	IX1_H25 = 19,
119 	IX1_V11 = 20,
120 	IX1_RTL = 21,		// Re-sizing top left corner
121 	IX1_RTR = 22,		// Re-sizing top right corner
122 	IX1_RBR = 23,		// Re-sizing bottom right corner
123 	IX1_CURLR = 24,		// }
124 	IX1_CURUD = 25,		// }
125 	IX1_CURDU = 26,		// } Custom cursors
126 	IX1_CURDD = 27,		// }
127 	IX1_CURUP = 28,		// }
128 	IX1_CURDOWN = 29,	// }
129 	IX1_MDGROOVE = 30,	// 'Mixing desk' slider background
130 	IX1_MDSLIDER= 34,	// 'Mixing desk' slider
131 	IX1_BLANK1 = 35,		//
132 	IX1_BLANK2 = 36,		//
133 	IX1_BLANK3 = 37,		//
134 	IX1_CIRCLE1 = 38,	//
135 	IX1_CIRCLE2 = 39,	//
136 	IX1_CROSS1 = 40,		//
137 	IX1_CROSS2 = 41,		//
138 	IX1_CROSS3 = 42,		//
139 	IX1_QUIT1 = 43,	//
140 	IX1_QUIT2 = 44,	//
141 	IX1_QUIT3 = 45,	//
142 	IX1_TICK1 = 46,		//
143 	IX1_TICK2 = 47,		//
144 	IX1_TICK3 = 48,		//
145 	IX1_NTR = 49,		// New top right corner
146 
147 	IX2_RTL = 19,			// Re-sizing top left corner
148 	IX2_RTR = 20,			// Re-sizing top right corner
149 	IX2_RBR = 21,			// Re-sizing bottom right corner
150 	IX2_CURLR = 22,		// }
151 	IX2_CURUD = 23,		// }
152 	IX2_CURDU = 24,		// } Custom cursors
153 	IX2_CURDD = 25,		// }
154 	IX2_MDGROOVE = 26,	// 'Mixing desk' slider background
155 	IX2_MDSLIDER = 27,	// 'Mixing desk' slider
156 	IX2_CIRCLE1 = 28,	//
157 	IX2_CIRCLE2 = 29,	//
158 	IX2_CROSS1 = 30,	//
159 	IX2_CROSS2 = 31,	//
160 	IX2_CROSS3 = 32,	//
161 	IX2_TICK1 = 33,		//
162 	IX2_TICK2 = 34,		//
163 	IX2_TICK3 = 35,		//
164 	IX2_NTR = 36,		// New top right corner
165 	IX2_TR4 = 37,
166 	IX2_LEFT1 = 38,
167 	IX2_LEFT2 = 39,
168 	IX2_RIGHT1 = 40,
169 	IX2_RIGHT2 = 41,
170 
171 	T1_HOPEDFORREELS = 50,
172 	T2_HOPEDFORREELS = 42
173 };
174 
175 // The following defines select the correct constant depending on Tinsel version
176 #define IX_CROSS1	(TinselV2 ? IX2_CROSS1 :	IX1_CROSS1)
177 #define IX_CURDD	(TinselV2 ? IX2_CURDD :		IX1_CURDD)
178 #define IX_CURDU	(TinselV2 ? IX2_CURDU :		IX1_CURDU)
179 #define IX_CURLR	(TinselV2 ? IX2_CURLR :		IX1_CURLR)
180 #define IX_CURUD	(TinselV2 ? IX2_CURUD :		IX1_CURUD)
181 #define IX_CURUL	(TinselV2 ? IX2_CURUL :		IX1_CURUL)
182 #define IX_MDGROOVE	(TinselV2 ? IX2_MDGROOVE :	IX1_MDGROOVE)
183 #define IX_MDSLIDER	(TinselV2 ? IX2_MDSLIDER :	IX1_MDSLIDER)
184 #define IX_NTR		(TinselV2 ? IX2_NTR :		IX1_NTR)
185 #define IX_RBR		(TinselV2 ? IX2_RBR :		IX1_RBR)
186 #define IX_RTL		(TinselV2 ? IX2_RTL :		IX1_RTL)
187 #define IX_RTR		(TinselV2 ? IX2_RTR :		IX1_RTR)
188 #define IX_TICK1	(TinselV2 ? IX2_TICK1 :		IX1_TICK1)
189 
190 
191 
192 #define NORMGRAPH	0
193 #define DOWNGRAPH	1
194 #define HIGRAPH		2
195 //-----------------------
196 #define FIX_UK		0
197 #define FIX_FR		1
198 #define FIX_GR		2
199 #define FIX_IT		3
200 #define FIX_SP		4
201 #define FIX_USA		5
202 #define HOPEDFORFREELS	6	// Expected flag reels
203 //-----------------------
204 
205 #define MAX_ININV	(TinselV2 ? 160 : 150)		// Max in an inventory
206 #define MAX_ININV_TOT	160
207 #define MAX_PERMICONS	10	// Max permanent conversation icons
208 
209 #define MAXHICONS	10	// Max dimensions of
210 #define MAXVICONS	6	// an inventory window
211 
212 #define ITEM_WIDTH	(TinselV2 ? 50 : 25)	// Dimensions of an icon
213 #define ITEM_HEIGHT	(TinselV2 ? 50 : 25)	//
214 #define I_SEPARATION	(TinselV2 ? 2 : 1)	// Item separation
215 
216 #define NM_TOFF		11	// Title text Y offset from top
217 #define NM_TBT		(TinselV2 ? 4 : 0)		// Y, title box top
218 #define NM_TBB		33
219 #define NM_LSX		(TinselV2 ? 4 : 0)		// X, left side
220 #define NM_BSY		(TinselV2 ? -9 : - M_TH + 1)
221 #define NM_RSX		(TinselV2 ? -9 : - M_SW + 1)
222 #define NM_SBL		(-27)
223 #define NM_SLH		(TinselV2 ? 11 : 5)	// Slider height
224 #define NM_SLX			(-11)	// Slider X offset (from right)
225 
226 #define NM_BG_POS_X (TinselV2 ? 9 : 1)		// }
227 #define NM_BG_POS_Y (TinselV2 ? 9 : 1)		// } Offset of translucent rectangle
228 #define NM_BG_SIZ_X (TinselV2 ? -18 : -3)	// }
229 #define NM_BG_SIZ_Y (TinselV2 ? -18 : -3)	// } How much larger it is than edges
230 
231 #define NM_RS_T_INSET		3
232 #define NM_RS_B_INSET		4
233 #define NM_RS_L_INSET		3
234 #define NM_RS_R_INSET		4
235 #define NM_RS_THICKNESS		5
236 #define NM_MOVE_AREA_B_Y	30
237 #define NM_SLIDE_INSET		(TinselV2 ? 18 : 9)	// X offset (from right) of left of scroll region
238 #define NM_SLIDE_THICKNESS	(TinselV2 ? 13 : 7)		// thickness of scroll region
239 #define NM_UP_ARROW_TOP		34	// Y offset of top of up arrow
240 #define NM_UP_ARROW_BOTTOM	49	// Y offset of bottom of up arrow
241 #define NM_DN_ARROW_TOP		22	// Y offset (from bottom) of top of down arrow
242 #define NM_DN_ARROW_BOTTOM	5	// Y offset (from bottom) of bottom of down arrow
243 
244 #define MD_YBUTTOP	(TinselV2 ? 2 : 9)
245 #define MD_YBUTBOT	(TinselV2 ? 16 : 0)
246 #define MD_XLBUTL	(TinselV2 ? 4 : 1)
247 #define MD_XLBUTR	(TinselV2 ? 26 : 10)
248 #define MD_XRBUTL	(TinselV2 ? 173 : 105)
249 #define MD_XRBUTR	(TinselV2 ? 195 : 114)
250 #define ROTX1 60	// Rotate button's offsets from the center
251 
252 // Number of objects that makes up an empty window
253 #define MAX_WCOMP	21		// 4 corners + (3+3) sides + (2+2) extra sides
254 					// + Bground + title + slider
255 					// + more Needed for save game window
256 
257 #define MAX_ICONS	MAXHICONS*MAXVICONS
258 
259 #define MAX_NAME_RIGHT (TinselV2 ? 417 : 213)
260 
261 #define SLIDE_RANGE	(TinselV2 ? 120 : 81)
262 #define SLIDE_MINX	(TinselV2 ? 25 : 8)
263 #define SLIDE_MAXX	(TinselV2 ? 25 + 120 : 8 + 81)
264 
265 #define MDTEXT_YOFF	(TinselV2 ? -1 : 6)
266 #define MDTEXT_XOFF	-4
267 #define TOG2_YOFF	-22
268 #define ROT_YOFF	48
269 #define TYOFF (TinselV2 ? 4 : 0)
270 #define FLAGX (-5)
271 #define FLAGY 4
272 
273 
274 //----------------- LOCAL GLOBAL DATA --------------------
275 
276 //----- Permanent data (compiled in) -----
277 
278 // Save game name editing cursor
279 
280 #define CURSOR_CHAR	'_'
281 char sCursor[2]	= { CURSOR_CHAR, 0 };
282 static const int hFillers[MAXHICONS] = {
283 	IX_H26,			// 2 icons wide
284 	IX_H52,			// 3
285 	IX_H78,			// 4
286 	IX_H104,		// 5
287 	IX_H130,		// 6
288 	IX_H156,		// 7
289 	IX_H182,		// 8
290 	IX_H208,		// 9
291 	IX_H234			// 10 icons wide
292 };
293 static const int vFillers[MAXVICONS] = {
294 	IX_V26,			// 2 icons high
295 	IX_V52,			// 3
296 	IX_V78,			// 4
297 	IX_V104,		// 5
298 	IX_V130			// 6 icons high
299 };
300 
301 
302 //----- Permanent data (set once) -----
303 
304 static SCNHANDLE g_hWinParts = 0;	// Window members and cursors' graphic data
305 static SCNHANDLE g_flagFilm = 0;	// Window members and cursors' graphic data
306 static SCNHANDLE g_configStrings[20];
307 
308 static INV_OBJECT *g_invObjects = NULL;	// Inventory objects' data
309 static int g_numObjects = 0;				// Number of inventory objects
310 static SCNHANDLE *g_invFilms = NULL;
311 static bool g_bNoLanguage = false;
312 static DIRECTION g_initialDirection;
313 
314 //----- Permanent data (updated, valid while inventory closed) -----
315 
316 static enum {NO_INV, IDLE_INV, ACTIVE_INV, BOGUS_INV} g_InventoryState;
317 
318 static int g_heldItem = INV_NOICON;	// Current held item
319 
320 static SCNHANDLE g_heldFilm;
321 
322 struct INV_DEF {
323 
324 	int MinHicons;		// }
325 	int MinVicons;		// } Dimension limits
326 	int MaxHicons;		// }
327 	int MaxVicons;		// }
328 
329 	int NoofHicons;		// }
330 	int NoofVicons;		// } Current dimentsions
331 
332 	int contents[160];	// Contained items
333 	int NoofItems;			// Current number of held items
334 
335 	int FirstDisp;		// Index to first item currently displayed
336 
337 	int inventoryX;		// } Display position
338 	int inventoryY;		// }
339 	int otherX;		// } Display position
340 	int otherY;		// }
341 
342 	int MaxInvObj;		// Max. allowed contents
343 
344 	SCNHANDLE hInvTitle;	// Window heading
345 
346 	bool resizable;		// Re-sizable window?
347 	bool bMoveable;		// Moveable window?
348 
349 	int sNoofHicons;	// }
350 	int sNoofVicons;	// } Current dimensions
351 
352 	bool bMax;		// Maximised last time open?
353 
354 };
355 
356 static INV_DEF g_InvD[NUM_INV];		// Conversation + 2 inventories + ...
357 
358 
359 // Permanent contents of conversation inventory
360 static int g_permIcons[MAX_PERMICONS];	// Basic items i.e. permanent contents
361 static int g_numPermIcons = 0;			// - copy to conv. inventory at pop-up time
362 static int g_numEndIcons = 0;
363 
364 //----- Data pertinant to current active inventory -----
365 
366 static int g_ino = 0;		// Which inventory is currently active
367 
368 static bool g_InventoryHidden = false;
369 static bool g_InventoryMaximised = false;
370 
371 static enum {	ID_NONE, ID_MOVE, ID_SLIDE,
372 		ID_BOTTOM, ID_TOP, ID_LEFT, ID_RIGHT,
373 		ID_TLEFT, ID_TRIGHT, ID_BLEFT, ID_BRIGHT,
374 		ID_CSLIDE, ID_MDCONT } g_InvDragging;
375 
376 static int g_SuppH = 0;		// 'Linear' element of
377 static int g_SuppV = 0;		// dimensions during re-sizing
378 
379 static int g_Ychange = 0;		//
380 static int g_Ycompensate = 0;		// All to do with re-sizing.
381 static int g_Xchange = 0;		//
382 static int g_Xcompensate = 0;		//
383 
384 static bool g_ItemsChanged = 0;	// When set, causes items to be re-drawn
385 
386 static bool g_bReOpenMenu = 0;
387 
388 static int g_TL = 0, g_TR = 0, g_BL = 0, g_BR = 0;	// Used during window construction
389 static int g_TLwidth = 0, g_TLheight = 0;	//
390 static int g_TRwidth = 0;		//
391 static int g_BLheight = 0;		//
392 
393 static LANGUAGE g_displayedLanguage;
394 
395 static OBJECT	*g_objArray[MAX_WCOMP];	// Current display objects (window)
396 static OBJECT	*g_iconArray[MAX_ICONS];	// Current display objects (icons)
397 static ANIM		g_iconAnims[MAX_ICONS];
398 static OBJECT	*g_DobjArray[MAX_WCOMP];	// Current display objects (re-sizing window)
399 
400 static OBJECT *g_RectObject = 0, *g_SlideObject = 0;	// Current display objects, for reference
401 					// objects are in objArray.
402 
403 static int g_sliderYpos = 0;			// For positioning the slider
404 static int g_sliderYmax = 0, g_sliderYmin = 0;	//
405 
406 #define sliderRange	(g_sliderYmax - g_sliderYmin)
407 
408 // Also to do with the slider
409 static struct { int n; int y; } g_slideStuff[MAX_ININV_TOT+1];
410 
411 #define MAXSLIDES 4
412 struct MDSLIDES {
413 	int	num;
414 	OBJECT	*obj;
415 	int	min, max;
416 };
417 static MDSLIDES g_mdSlides[MAXSLIDES];
418 static int g_numMdSlides = 0;
419 
420 static int g_GlitterIndex = 0;
421 
422 // Icon clicked on to cause an event
423 // - Passed to conversation polygon or actor code via Topic()
424 // - (sometimes) Passed to inventory icon code via OtherObject()
425 static int g_thisIcon = 0;
426 
427 static CONV_PARAM g_thisConvFn;				// Top, 'Middle' or Bottom
428 static HPOLYGON g_thisConvPoly = 0;			// Conversation code is in a polygon code block
429 static int g_thisConvActor;					// ...or an actor's code block.
430 static int g_pointedIcon = INV_NOICON;		// used by InvLabels - icon pointed to on last call
431 static volatile int g_PointedWaitCount = 0;	// used by ObjectProcess - fix the 'repeated pressing bug'
432 static int g_sX = 0;							// used by SlideMSlider() - current x-coordinate
433 static int g_lX = 0;							// used by SlideMSlider() - last x-coordinate
434 
435 static bool g_bMoveOnUnHide;	// Set before start of conversation
436 				// - causes conversation to be started in a sensible place
437 
438 //----- Data pertinant to configure (incl. load/save game) -----
439 
440 #define COL_MAINBOX	TBLUE1		// Base blue color
441 #define COL_BOX		TBLUE1
442 #define COL_HILIGHT	TBLUE4
443 
444 #ifdef JAPAN
445 #define BOX_HEIGHT	17
446 #define EDIT_BOX1_WIDTH	149
447 #else
448 #define BOX_HEIGHT	13
449 #define EDIT_BOX1_WIDTH	145
450 #endif
451 #define EDIT_BOX2_WIDTH	166
452 
453 #define T2_EDIT_BOX1_WIDTH 290
454 #define T2_EDIT_BOX2_WIDTH 322
455 #define T2_BOX_HEIGHT 26
456 
457 //----- Data pertinant to scene hoppers ------------------------
458 
459 #include "common/pack-start.h"	// START STRUCT PACKING
460 
461 struct HOPPER {
462 	uint32		hScene;
463 	SCNHANDLE	hSceneDesc;
464 	uint32		numEntries;
465 	uint32		entryIndex;
466 } PACKED_STRUCT;
467 typedef HOPPER *PHOPPER;
468 
469 struct HOPENTRY {
470 	uint32	eNumber;	// entrance number
471 	SCNHANDLE hDesc;	// handle to entrance description
472 	uint32	flags;
473 } PACKED_STRUCT;
474 typedef HOPENTRY *PHOPENTRY;
475 
476 #include "common/pack-end.h"	// END STRUCT PACKING
477 
478 static PHOPPER		g_pHopper;
479 static PHOPENTRY	g_pEntries;
480 static int g_numScenes;
481 
482 static int g_numEntries;
483 
484 static PHOPPER g_pChosenScene = NULL;
485 
486 static int g_lastChosenScene;
487 static bool g_bRemember;
488 
489 //--------------------------------------------------------------
490 
491 
492 
493 enum BTYPE {
494 	RGROUP,		///< Radio button group - 1 is selectable at a time. Action on double click
495 	ARSBUT,		///< Action if a radio button is selected
496 	AABUT,		///< Action always
497 	AATBUT,		///< Action always, text box
498 	ARSGBUT,
499 	AAGBUT,		///< Action always, graphic button
500 	SLIDER,		///< Not a button at all
501 	TOGGLE,		///< Discworld 1 toggle
502 	TOGGLE1,	///< Discworld 2 toggle type 1
503 	TOGGLE2,	///< Discworld 2 toggle type 2
504 	DCTEST,
505 	FLIP,
506 	FRGROUP,
507 	ROTATE,
508 	NOTHING
509 };
510 
511 enum BFUNC {
512 	NOFUNC,
513 	SAVEGAME,
514 	LOADGAME,
515 	IQUITGAME,
516 	CLOSEWIN,
517 	OPENLOAD,
518 	OPENSAVE,
519 	OPENREST,
520 	OPENSOUND,
521 	OPENCONT,
522 #ifndef JAPAN
523 	OPENSUBT,
524 #endif
525 	OPENQUIT,
526 	INITGAME,
527 	MUSICVOL,
528 
529 	HOPPER2,		// Call up Scene Hopper 2
530 	BF_CHANGESCENE,
531 
532 	CLANG,
533 	RLANG
534 #ifdef MAC_OPTIONS
535 	, MASTERVOL, SAMPVOL
536 #endif
537 };
538 
539 #define NO_HEADING		((SCNHANDLE)-1)
540 #define USE_POINTER		(-1)
541 #define SIX_LOAD_OPTION		0
542 #define SIX_SAVE_OPTION		1
543 #define SIX_RESTART_OPTION	2
544 #define SIX_SOUND_OPTION	3
545 #define SIX_CONTROL_OPTION	4
546 #ifndef JAPAN
547 #define SIX_SUBTITLES_OPTION	5
548 #endif
549 #define SIX_QUIT_OPTION		6
550 #define SIX_RESUME_OPTION	7
551 #define SIX_LOAD_HEADING	8
552 #define SIX_SAVE_HEADING	9
553 #define SIX_RESTART_HEADING	10
554 #define SIX_MVOL_SLIDER		11
555 #define SIX_SVOL_SLIDER		12
556 #define SIX_VVOL_SLIDER		13
557 #define SIX_DCLICK_SLIDER	14
558 #define SIX_DCLICK_TEST		15
559 #define SIX_SWAP_TOGGLE		16
560 #define SIX_TSPEED_SLIDER	17
561 #define SIX_STITLE_TOGGLE	18
562 #define SIX_QUIT_HEADING	19
563 
564 enum TM {TM_POINTER, TM_INDEX, TM_STRINGNUM, TM_NONE};
565 
566 struct CONFBOX {
567 	BTYPE	boxType;
568 	BFUNC	boxFunc;
569 	TM		textMethod;
570 
571 	char	*boxText;
572 	int	ixText;
573 	int	xpos;
574 	int	ypos;
575 	int	w;		// Doubles as max value for SLIDERs
576 	int	h;		// Doubles as iteration size for SLIDERs
577 	int	*ival;
578 	int	bi;		// Base index for AAGBUTs
579 };
580 
581 struct CONFINIT {
582 	int	h;
583 	int	v;
584 	int	x;
585 	int	y;
586 	bool bExtraWin;
587 	CONFBOX *Box;
588 	int	NumBoxes;
589 	uint32	ixHeading;
590 };
591 
592 #define BW	44	// Width of crosses and ticks etc. buttons
593 #define BH	41	// Height of crosses and ticks etc. buttons
594 
595 /*-------------------------------------------------------------*\
596 | This is the main menu (that comes up when you hit F1 on a PC)	|
597 \*-------------------------------------------------------------*/
598 
599 #ifdef JAPAN
600 #define FBY	11	// y-offset of first button
601 #define FBX	13	// x-offset of first button
602 #else
603 #define FBY	20	// y-offset of first button
604 #define FBX	15	// x-offset of first button
605 #endif
606 
607 #define OPTX	33
608 #define OPTY	30
609 #define BOX_V_SEP	7
610 
611 #define BOXX	56	// X-position of text boxes
612 #define BOXY	50	// Y-position of text boxes
613 #define T2_OPTX	33
614 #define T2_OPTY	36
615 #define T2_BOX_V_SEP	12
616 #define T2_BOX_V2_SEP	6
617 
618 static CONFBOX t1OptionBox[] = {
619 
620  { AATBUT, OPENLOAD, TM_NONE, NULL, SIX_LOAD_OPTION,	FBX, FBY,			EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
621  { AATBUT, OPENSAVE, TM_NONE, NULL, SIX_SAVE_OPTION,	FBX, FBY + (BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
622  { AATBUT, OPENREST, TM_NONE, NULL, SIX_RESTART_OPTION,	FBX, FBY + 2*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
623  { AATBUT, OPENSOUND, TM_NONE, NULL, SIX_SOUND_OPTION,	FBX, FBY + 3*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
624  { AATBUT, OPENCONT, TM_NONE, NULL, SIX_CONTROL_OPTION,	FBX, FBY + 4*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
625 #ifdef JAPAN
626 // TODO: If in JAPAN mode, simply disable the subtitles button?
627  { AATBUT, OPENQUIT, NULL, SIX_QUIT_OPTION,	FBX, FBY + 5*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
628  { AATBUT, CLOSEWIN, NULL, SIX_RESUME_OPTION,	FBX, FBY + 6*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 }
629 #else
630  { AATBUT, OPENSUBT, TM_NONE, NULL, SIX_SUBTITLES_OPTION,FBX, FBY + 5*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
631  { AATBUT, OPENQUIT, TM_NONE, NULL, SIX_QUIT_OPTION,	FBX, FBY + 6*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 },
632  { AATBUT, CLOSEWIN, TM_NONE, NULL, SIX_RESUME_OPTION,	FBX, FBY + 7*(BOX_HEIGHT + 2),	EDIT_BOX1_WIDTH, BOX_HEIGHT, NULL, 0 }
633 #endif
634 
635 };
636 
637 static CONFINIT t1ciOption	= { 6, 5, 72, 23, false, t1OptionBox,	ARRAYSIZE(t1OptionBox),	NO_HEADING };
638 
639 static CONFBOX t2OptionBox[] = {
640 
641  { AATBUT, OPENLOAD, TM_INDEX, NULL, SS_LOAD_OPTION,	T2_OPTX, T2_OPTY,									T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
642  { AATBUT, OPENSAVE, TM_INDEX, NULL, SS_SAVE_OPTION,	T2_OPTX, T2_OPTY + (T2_BOX_HEIGHT + T2_BOX_V_SEP),	T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
643  { AATBUT, OPENREST, TM_INDEX, NULL, SS_RESTART_OPTION,	T2_OPTX, T2_OPTY + 2*(T2_BOX_HEIGHT + T2_BOX_V_SEP),	T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
644  { AATBUT, OPENSOUND, TM_INDEX, NULL, SS_SOUND_OPTION,	T2_OPTX, T2_OPTY + 3*(T2_BOX_HEIGHT + T2_BOX_V_SEP),	T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
645  { AATBUT, OPENQUIT, TM_INDEX, NULL, SS_QUIT_OPTION,	T2_OPTX, T2_OPTY + 4*(T2_BOX_HEIGHT + T2_BOX_V_SEP),	T2_EDIT_BOX1_WIDTH, T2_BOX_HEIGHT, NULL, 0 }
646 
647 };
648 
649 static CONFINIT t2ciOption = { 6, 4, 144, 60, false, t2OptionBox, sizeof(t2OptionBox)/sizeof(CONFBOX), NO_HEADING };
650 
651 #define ciOption (TinselV2 ? t2ciOption : t1ciOption)
652 #define optionBox (TinselV2 ? t2OptionBox : t1OptionBox)
653 
654 /*-------------------------------------------------------------*\
655 | These are the load and save game menus.			|
656 \*-------------------------------------------------------------*/
657 
658 #define NUM_RGROUP_BOXES	9
659 
660 #ifdef JAPAN
661 #define NUM_RGROUP_BOXES	7	// number of visible slots
662 #define SY		32	// y-position of first slot
663 #else
664 #define NUM_RGROUP_BOXES	9	// number of visible slots
665 #define SY		31	// y-position of first slot
666 #endif
667 
668 static CONFBOX t1LoadBox[NUM_RGROUP_BOXES+2] = {
669 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY,				EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
670 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + (BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
671 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 2*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
672 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 3*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
673 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 4*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
674 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 5*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
675 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 6*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
676 #ifndef JAPAN
677 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 7*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
678 	{ RGROUP, LOADGAME, TM_NONE, NULL, USE_POINTER, 28, SY + 8*(BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
679 #endif
680 	{ ARSGBUT, LOADGAME, TM_NONE, NULL, USE_POINTER, 230, 44,	23, 19, NULL, IX1_TICK1 },
681 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 230, 44+47,	23, 19, NULL, IX1_CROSS1 }
682 };
683 
684 static CONFBOX t2LoadBox[] = {
685 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY,				T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
686 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
687 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
688 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
689 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
690 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
691 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
692 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
693 	{ RGROUP, LOADGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
694 
695 	{ ARSGBUT, LOADGAME, TM_NONE, NULL, 0, 460, 100,	BW, BH, NULL, IX2_TICK1 },
696 	{ AAGBUT, CLOSEWIN,  TM_NONE, NULL, 0, 460, 100+100,	BW, BH, NULL, IX2_CROSS1 }
697 };
698 
699 static CONFINIT t1ciLoad	= { 10, 6, 20, 16, true, t1LoadBox,	ARRAYSIZE(t1LoadBox), SIX_LOAD_HEADING };
700 static CONFINIT t2ciLoad	= { 10, 6, 40, 16, true, t2LoadBox, sizeof(t2LoadBox)/sizeof(CONFBOX), SS_LOAD_HEADING };
701 
702 
703 static CONFBOX t1SaveBox[NUM_RGROUP_BOXES+2] = {
704 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY,			EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
705 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + (BOX_HEIGHT + 2),	EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
706 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 2*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
707 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 3*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
708 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 4*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
709 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 5*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
710 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 6*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
711 #ifndef JAPAN
712 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 7*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
713 	{ RGROUP, SAVEGAME, TM_NONE, NULL, USE_POINTER, 28,	SY + 8*(BOX_HEIGHT + 2),EDIT_BOX2_WIDTH, BOX_HEIGHT, NULL, 0 },
714 #endif
715 	{ ARSGBUT, SAVEGAME, TM_NONE, NULL,USE_POINTER, 230, 44,	23, 19, NULL, IX1_TICK1 },
716 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 230, 44+47,	23, 19, NULL, IX1_CROSS1 }
717 };
718 
719 static CONFBOX t2SaveBox[] = {
720  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY,				T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
721  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
722  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
723  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
724  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
725  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
726  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
727  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
728  { RGROUP, SAVEGAME, TM_POINTER, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP),	T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
729 
730  { ARSGBUT, SAVEGAME, TM_NONE, NULL, 0, 460, 100,	BW, BH, NULL, IX2_TICK1 },
731  { AAGBUT, CLOSEWIN,  TM_NONE, NULL, 0, 460, 100+100,	BW, BH, NULL, IX2_CROSS1 }
732 };
733 
734 static CONFINIT t1ciSave	= { 10, 6, 20, 16, true, t1SaveBox,	ARRAYSIZE(t1SaveBox),	SIX_SAVE_HEADING };
735 static CONFINIT t2ciSave	= { 10, 6, 40, 16, true, t2SaveBox, sizeof(t2SaveBox)/sizeof(CONFBOX), SS_SAVE_HEADING };
736 
737 #define ciLoad (TinselV2 ? t2ciLoad : t1ciLoad)
738 #define loadBox (TinselV2 ? t2LoadBox : t1LoadBox)
739 #define ciSave (TinselV2 ? t2ciSave : t1ciSave)
740 #define saveBox (TinselV2 ? t2SaveBox : t1SaveBox)
741 
742 /*-------------------------------------------------------------*\
743 | This is the restart confirmation 'menu'.			|
744 \*-------------------------------------------------------------*/
745 
746 static CONFBOX t1RestartBox[] = {
747 #ifdef JAPAN
748 	{ AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 96, 44,	23, 19, NULL, IX_TICK1 },
749 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 56, 44,	23, 19, NULL, IX_CROSS1 }
750 #else
751 	{ AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 70, 28,	23, 19, NULL, IX1_TICK1 },
752 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 30, 28,	23, 19, NULL, IX1_CROSS1 }
753 #endif
754 };
755 
756 static CONFBOX t1RestartBoxPSX[] = {
757 	{ AAGBUT, INITGAME, TM_NONE, NULL, USE_POINTER, 122, 48,	23, 19, NULL, IX1_TICK1 },
758 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER, 82, 48,	23, 19, NULL, IX1_CROSS1 }
759 };
760 
761 static CONFBOX t2RestartBox[] = {
762 	{ AAGBUT, INITGAME, TM_NONE, NULL, 0, 140, 78, BW, BH, NULL, IX2_TICK1 },
763 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78,  BW, BH, NULL, IX2_CROSS1 }
764 };
765 
766 #ifdef JAPAN
767 static CONFINIT t1ciRestart	= { 6, 2, 72, 53, false, t1RestartBox,	ARRAYSIZE(t1RestartBox),	SIX_RESTART_HEADING };
768 #else
769 static CONFINIT t1ciRestart	= { 4, 2, 98, 53, false, t1RestartBox,	ARRAYSIZE(t1RestartBox),	SIX_RESTART_HEADING };
770 #endif
771 static CONFINIT t1ciRestartPSX	= { 8, 2, 46, 53, false, t1RestartBoxPSX,	ARRAYSIZE(t1RestartBoxPSX),	SIX_RESTART_HEADING };
772 static CONFINIT t2ciRestart	= { 4, 2, 196, 53, false, t2RestartBox, sizeof(t2RestartBox)/sizeof(CONFBOX), SS_RESTART_HEADING };
773 
774 #define ciRestart (TinselV2 ? t2ciRestart : (TinselV1PSX ? t1ciRestartPSX : t1ciRestart))
775 
776 /*-------------------------------------------------------------*\
777 | This is the sound control 'menu'. In Discworld 2, it also		|
778 | contains the subtitles and language selection.				|
779 \*-------------------------------------------------------------*/
780 
781 static CONFBOX t1SoundBox[] = {
782 	{ SLIDER, MUSICVOL, TM_NONE, NULL, SIX_MVOL_SLIDER,	142, 25,	Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_musicVolume*/, 0 },
783 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_SVOL_SLIDER,	142, 25+40,	Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_soundVolume*/, 0 },
784 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_VVOL_SLIDER,	142, 25+2*40,	Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_voiceVolume*/, 0 }
785 };
786 
787 static CONFBOX t2SoundBox[] = {
788 	{ SLIDER, MUSICVOL, TM_INDEX, NULL, SS_MVOL_SLIDER, 280, 50,      Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_musicVolume*/, 0 },
789 	{ SLIDER, NOFUNC, TM_INDEX, NULL, SS_SVOL_SLIDER,   280, 50+30,   Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_soundVolume*/, 0 },
790 	{ SLIDER, NOFUNC, TM_INDEX, NULL, SS_VVOL_SLIDER,   280, 50+2*30, Audio::Mixer::kMaxChannelVolume, 2, 0 /*&_vm->_config->_voiceVolume*/, 0 },
791 
792 	{ SLIDER, NOFUNC, TM_INDEX, NULL, SS_TSPEED_SLIDER, 280, 160, 100, 2, 0 /*&_vm->_config->_textSpeed*/, 0 },
793 	{ TOGGLE2, NOFUNC, TM_INDEX, NULL, SS_STITLE_TOGGLE, 100, 220, BW, BH, 0 /*&_vm->_config->_useSubtitles*/, 0 },
794 	{ ROTATE, NOFUNC, TM_INDEX, NULL, SS_LANGUAGE_SELECT, 320,220, BW, BH, NULL, 0 }
795 };
796 
797 static CONFINIT t1ciSound	= { 10, 5, 20, 16, false, t1SoundBox, ARRAYSIZE(t1SoundBox), NO_HEADING };
798 static CONFINIT t2ciSound = { 10, 5, 40, 16, false, t2SoundBox, sizeof(t2SoundBox)/sizeof(CONFBOX), SS_SOUND_HEADING };
799 
800 #define ciSound (TinselV2 ? t2ciSound : t1ciSound)
801 
802 /*-------------------------------------------------------------*\
803 | This is the (mouse) control 'menu'.				|
804 \*-------------------------------------------------------------*/
805 
806 static int bFlipped;	// looks like this is just so the code has something to alter!
807 
808 static CONFBOX controlBox[] = {
809 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_DCLICK_SLIDER,	142, 25,	3*DOUBLE_CLICK_TIME, 1, 0 /*&_vm->_config->_dclickSpeed*/, 0 },
810 	{ FLIP, NOFUNC, TM_NONE, NULL, SIX_DCLICK_TEST,		142, 25+30,	23, 19, &bFlipped, IX1_CIRCLE1 },
811 #ifdef JAPAN
812 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_SWAP_TOGGLE,	205, 25+70,	23, 19, 0 /*&_vm->_config->_swapButtons*/, 0 }
813 #else
814 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_SWAP_TOGGLE,	155, 25+70,	23, 19, 0 /*&_vm->_config->_swapButtons*/, 0 }
815 #endif
816 };
817 
818 static CONFINIT ciControl	= { 10, 5, 20, 16, false, controlBox,	ARRAYSIZE(controlBox),	NO_HEADING };
819 
820 /*-------------------------------------------------------------*\
821 | This is the subtitles 'menu'.					|
822 \*-------------------------------------------------------------*/
823 
824 static CONFBOX subtitlesBox[] = {
825 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER,	142, 20,	100, 2, 0 /*&_vm->_config->_textSpeed*/, 0 },
826 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE,	142, 20+40,	23, 19, 0 /*&_vm->_config->_useSubtitles*/, 0 },
827 };
828 
829 static CONFBOX subtitlesBox3Flags[] = {
830 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	15, 118,	56, 32, NULL, FIX_FR },
831 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	85, 118,	56, 32, NULL, FIX_GR },
832 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	155, 118,	56, 32, NULL, FIX_SP },
833 
834 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER,	142, 20,	100, 2, 0 /*&_vm->_config->_textSpeed*/, 0 },
835 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE,	142, 20+40,	23, 19, 0 /*&_vm->_config->_useSubtitles*/, 0 },
836 
837 	{ ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER,	230, 110,	23, 19, NULL, IX1_TICK1 },
838 	{ AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER,	230, 140,	23, 19, NULL, IX1_CROSS1 }
839 };
840 
841 static CONFBOX subtitlesBox4Flags[] = {
842 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	20, 100,	56, 32, NULL, FIX_FR },
843 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	108, 100,	56, 32, NULL, FIX_GR },
844 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	64, 137,	56, 32, NULL, FIX_IT },
845 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	152, 137,	56, 32, NULL, FIX_SP },
846 
847 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER,	142, 20,	100, 2, 0 /*&_vm->_config->_textSpeed*/, 0 },
848 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE,	142, 20+40,	23, 19, 0 /*&_vm->_config->_useSubtitles*/, 0 },
849 
850 	{ ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER,	230, 110,	23, 19, NULL, IX1_TICK1 },
851 	{ AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER,	230, 140,	23, 19, NULL, IX1_CROSS1 }
852 };
853 
854 
855 static CONFBOX subtitlesBox5Flags[] =	{
856 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	15, 100,	56, 32, NULL, FIX_UK },
857 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	85, 100,	56, 32, NULL, FIX_FR },
858 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	155, 100,	56, 32, NULL, FIX_GR },
859 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	50, 137,	56, 32, NULL, FIX_IT },
860 	{ FRGROUP, NOFUNC, TM_NONE, NULL, USE_POINTER,	120, 137,	56, 32, NULL, FIX_SP },
861 
862 	{ SLIDER, NOFUNC, TM_NONE, NULL, SIX_TSPEED_SLIDER,	142, 20,	100, 2, 0 /*&_vm->_config->_textSpeed*/, 0 },
863 	{ TOGGLE, NOFUNC, TM_NONE, NULL, SIX_STITLE_TOGGLE,	142, 20+40,	23, 19, 0 /*&_vm->_config->_useSubtitles*/, 0 },
864 
865 	{ ARSGBUT, CLANG, TM_NONE, NULL, USE_POINTER,	230, 110,	23, 19, NULL, IX1_TICK1 },
866 	{ AAGBUT, RLANG, TM_NONE, NULL, USE_POINTER,	230, 140,	23, 19, NULL, IX1_CROSS1 }
867 };
868 
869 
870 /*-------------------------------------------------------------*\
871 | This is the quit confirmation 'menu'.				|
872 \*-------------------------------------------------------------*/
873 
874 static CONFBOX t1QuitBox[] = {
875 #ifdef JAPAN
876  { AAGBUT, IQUITGAME, TM_NONE, NULL, USE_POINTER,70, 44,	23, 19, NULL, IX_TICK1 },
877  { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER,	30, 44,	23, 19, NULL, IX_CROSS1 }
878 #else
879  { AAGBUT, IQUITGAME, TM_NONE, NULL, USE_POINTER,70, 28,	23, 19, NULL, IX1_TICK1 },
880  { AAGBUT, CLOSEWIN, TM_NONE, NULL, USE_POINTER,	30, 28,	23, 19, NULL, IX1_CROSS1 }
881 #endif
882 };
883 
884 static CONFBOX t2QuitBox[] = {
885 	{ AAGBUT, IQUITGAME, TM_NONE, NULL, 0,140, 78, BW, BH, NULL, IX2_TICK1 },
886 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 60, 78,  BW, BH, NULL, IX2_CROSS1 }
887 };
888 
889 static CONFINIT t1ciQuit	= { 4, 2, 98, 53, false, t1QuitBox,	ARRAYSIZE(t1QuitBox),	SIX_QUIT_HEADING };
890 static CONFINIT t2ciQuit	= { 4, 2, 196, 53, false, t2QuitBox, sizeof(t2QuitBox)/sizeof(CONFBOX), SS_QUIT_HEADING };
891 
892 #define quitBox (TinselV2 ? t2QuitBox : t1QuitBox)
893 #define ciQuit (TinselV2 ? t2ciQuit : t1ciQuit)
894 
895 /***************************************************************************\
896 |************************    Startup and shutdown    ***********************|
897 \***************************************************************************/
898 
899 static CONFBOX hopperBox1[] = {
900 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY,									 T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
901 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP),	 T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
902 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
903 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
904 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
905 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
906 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
907 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
908 	{ RGROUP, HOPPER2, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
909 
910 	{ ARSGBUT, HOPPER2, TM_NONE, NULL, 0, 460, 100, BW, BH, NULL, IX2_TICK1 },
911 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 100 + 100, BW, BH, NULL, IX2_CROSS1 }
912 };
913 
914 static CONFINIT ciHopper1 = { 10, 6, 40, 16, true, hopperBox1, sizeof(hopperBox1) / sizeof(CONFBOX), SS_HOPPER1 };
915 
916 static CONFBOX hopperBox2[] = {
917 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY,				T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
918 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + (T2_BOX_HEIGHT + T2_BOX_V2_SEP),	 T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
919 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 2*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
920 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 3*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
921 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 4*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
922 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 5*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
923 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 6*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
924 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 7*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
925 	{ RGROUP, BF_CHANGESCENE, TM_STRINGNUM, NULL, 0, BOXX, BOXY + 8*(T2_BOX_HEIGHT + T2_BOX_V2_SEP), T2_EDIT_BOX2_WIDTH, T2_BOX_HEIGHT, NULL, 0 },
926 
927 	{ ARSGBUT, BF_CHANGESCENE, TM_NONE, NULL, 0, 460, 50,  BW, BH, NULL, IX2_TICK1 },
928 	{ AAGBUT, CLOSEWIN, TM_NONE, NULL, 0, 460, 200, BW, BH, NULL, IX2_CROSS1 }
929 };
930 
931 static CONFINIT ciHopper2 = { 10, 6, 40, 16, true, hopperBox2, sizeof(hopperBox2)/sizeof(CONFBOX), NO_HEADING };
932 
933 
934 /***************************************************************************\
935 |****************************    Top Window    *****************************|
936 \***************************************************************************/
937 static CONFBOX topwinBox[] = {
938  { NOTHING, NOFUNC, TM_NONE, NULL, USE_POINTER, 0, 0, 0, 0, NULL, 0 }
939 };
940 
941 
942 static CONFINIT ciSubtitles	= { 10, 3, 20, 16, false, subtitlesBox,	ARRAYSIZE(subtitlesBox),	NO_HEADING };
943 
944 static CONFINIT ciTopWin	= { 6, 5, 72, 23, false, topwinBox,	0,					NO_HEADING };
945 
946 #define NOBOX (-1)
947 
948 // Conf window globals
949 static struct {
950 	CONFBOX *box;
951 	int	NumBoxes;
952 	bool bExtraWin;
953 	uint32 ixHeading;
954 	bool editableRgroup;
955 
956 	int	selBox;
957 	int	pointBox;	// Box pointed to on last call
958 	int	modifier;
959 	int	extraBase;
960 	int	numSaved;
961 } cd = {
962 	NULL, 0, false, 0, false,
963 	NOBOX, NOBOX, 0, 0, 0
964 };
965 
966 // For editing save game names
967 static char g_sedit[SG_DESC_LEN+2];
968 
969 #define HL1	0	// Hilight that moves with the cursor
970 #define HL2	1	// Hilight on selected RGROUP box
971 #define HL3	2	// Text on selected RGROUP box
972 #define NUMHL	3
973 
974 
975 // Data for button press/toggle effects
976 static struct {
977 	bool bButAnim;
978 	CONFBOX *box;
979 	bool press;		// true = button press; false = button toggle
980 } g_buttonEffect = { false, 0, false };
981 
982 
983 //----- LOCAL FORWARD REFERENCES -----
984 
985 enum {
986 	IB_NONE			= -1,	//
987 	IB_UP			= -2,	// negative numbers returned
988 	IB_DOWN			= -3,	// by WhichMenuBox()
989 	IB_SLIDE		= -4,	//
990 	IB_SLIDE_UP		= -5,	//
991 	IB_SLIDE_DOWN	= -6	//
992 };
993 
994 enum {
995 	HI_BIT		= ((uint)MIN_INT >> 1),	// The next to top bit
996 	IS_LEFT		= HI_BIT,
997 	IS_SLIDER	= (IS_LEFT >> 1),
998 	IS_RIGHT	= (IS_SLIDER >> 1),
999 	IS_MASK		= (IS_LEFT | IS_SLIDER | IS_RIGHT)
1000 };
1001 
1002 static int WhichMenuBox(int curX, int curY, bool bSlides);
1003 static void SlideMSlider(int x, SSFN fn);
1004 static OBJECT *AddObject(const FREEL *pfreel, int num);
1005 static void AddBoxes(bool posnSlide);
1006 
1007 static void ConfActionSpecial(int i);
1008 
1009 static bool RePosition();
1010 
1011 /*-------------------------------------------------------------------------*/
1012 /***	Magic numbers	***/
1013 
1014 #define M_SW	5	// Side width
1015 #define M_TH	5	// Top height
1016 #ifdef JAPAN
1017 #define M_TOFF	6	// Title text Y offset from top
1018 #define M_TBB	20	// Title box bottom Y offset
1019 #else
1020 #define M_TOFF	4	// Title text Y offset from top
1021 #define M_TBB	14	// Title box bottom Y offset
1022 #endif
1023 #define M_SBL	26	// Scroll bar left X offset
1024 #define M_SH	5	// Slider height (*)
1025 #define M_SW	5	// Slider width (*)
1026 #define M_SXOFF	9	// Slider X offset from right-hand side
1027 #ifdef JAPAN
1028 #define M_IUT	22	// Y offset of top of up arrow
1029 #define M_IUB	30	// Y offset of bottom of up arrow
1030 #else
1031 #define M_IUT	16	// Y offset of top of up arrow
1032 #define M_IUB	24	// Y offset of bottom of up arrow
1033 #endif
1034 #define M_IDT	10	// Y offset (from bottom) of top of down arrow
1035 #define M_IDB	3	// Y offset (from bottom) of bottom of down arrow
1036 
1037 #define START_ICONX	(TinselV2 ? 12 : (M_SW+1))			// } Relative offset of first icon
1038 #define START_ICONY	(TinselV2 ? 40 : (M_TBB+M_TH+1))	// } within the inventory window
1039 
1040 /*-------------------------------------------------------------------------*/
1041 
1042 
LanguageChange()1043 static bool LanguageChange() {
1044 	LANGUAGE nLang = _vm->_config->_language;
1045 
1046 	if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
1047 		// Languages: TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH
1048 		// 5 flag versions include English
1049 		int selected = (_vm->getFeatures() & GF_USE_5FLAGS) ? cd.selBox : cd.selBox + 1;
1050 		// Make sure that a language flag has been selected. If the user has
1051 		// changed the language speed slider and hasn't clicked on a flag, it
1052 		// won't be selected.
1053 		if (selected >= 0 && selected <= 4) {
1054 			nLang = (LANGUAGE)selected;
1055 
1056 			// 3 flag versions don't include Italian
1057 			if (selected >= 3 && (_vm->getFeatures() & GF_USE_3FLAGS))
1058 				nLang = TXT_SPANISH;
1059 		}
1060 	}
1061 
1062 	if (nLang != _vm->_config->_language) {
1063 		KillInventory();
1064 		ChangeLanguage(nLang);
1065 		_vm->_config->_language = nLang;
1066 		return true;
1067 	} else
1068 		return false;
1069 }
1070 
1071 /**************************************************************************/
1072 /*****************************  Scene Hopper ******************************/
1073 /**************************************************************************/
1074 
1075 /**
1076  * Read in the scene hopper data file and set the
1077  *  pointers to the data and scene count.
1078  */
PrimeSceneHopper()1079 static void PrimeSceneHopper() {
1080 	Common::File f;
1081 	char *pBuffer;
1082 	uint32 vSize;
1083 
1084 	// Open the file (it's on the CD)
1085 	CdCD(Common::nullContext);
1086 	if (!f.open(HOPPER_FILENAME))
1087 		error(CANNOT_FIND_FILE, HOPPER_FILENAME);
1088 
1089 	// Read in header
1090 	if (f.readUint32LE() != CHUNK_SCENE_HOPPER)
1091 		error(FILE_IS_CORRUPT, HOPPER_FILENAME);
1092 	vSize = f.readUint32LE();
1093 
1094 	// allocate a buffer for it all
1095 	assert(g_pHopper == NULL);
1096 	uint32 size = f.size() - 8;
1097 
1098 	// make sure memory allocated
1099 	pBuffer = (char *)malloc(size);
1100 	if (pBuffer == NULL)
1101 		// cannot alloc buffer for index
1102 		error(NO_MEM, "Scene hopper data");
1103 
1104 	// load data
1105 	if (f.read(pBuffer, size) != size)
1106 		// file must be corrupt if we get to here
1107 		error(FILE_IS_CORRUPT, HOPPER_FILENAME);
1108 
1109 	// Set data pointers
1110 	g_pHopper = (PHOPPER)pBuffer;
1111 	g_pEntries = (PHOPENTRY)(pBuffer + vSize);
1112 	g_numScenes = vSize / sizeof(HOPPER);
1113 
1114 	// close the file
1115 	f.close();
1116 }
1117 
1118 /**
1119  * Free the scene hopper data file
1120  */
FreeSceneHopper()1121 static void FreeSceneHopper() {
1122 	free(g_pHopper);
1123 	g_pHopper = NULL;
1124 }
1125 
FirstScene(int first)1126 static void FirstScene(int first) {
1127 	int	i;
1128 
1129 	assert(g_numScenes && g_pHopper);
1130 
1131 	if (g_bRemember) {
1132 		assert(first == 0);
1133 		first = g_lastChosenScene;
1134 		g_bRemember = false;
1135 	}
1136 
1137 	// Force it to a sensible value
1138 	if (first > g_numScenes - NUM_RGROUP_BOXES)
1139 		first = g_numScenes - NUM_RGROUP_BOXES;
1140 	if (first < 0)
1141 		first = 0;
1142 
1143 	// Fill in the rest
1144 	for (i = 0; i < NUM_RGROUP_BOXES && i + first < g_numScenes; i++) {
1145 		cd.box[i].textMethod = TM_STRINGNUM;
1146 		cd.box[i].ixText = FROM_32(g_pHopper[i + first].hSceneDesc);
1147 	}
1148 	// Blank out the spare ones (if any)
1149 	while (i < NUM_RGROUP_BOXES) {
1150 		cd.box[i].textMethod = TM_NONE;
1151 		cd.box[i++].ixText = 0;
1152 	}
1153 
1154 	cd.extraBase = first;
1155 }
1156 
RememberChosenScene()1157 static void RememberChosenScene() {
1158 	g_bRemember = true;
1159 }
1160 
SetChosenScene()1161 static void SetChosenScene() {
1162 	g_lastChosenScene = cd.selBox + cd.extraBase;
1163 	g_pChosenScene = &g_pHopper[cd.selBox + cd.extraBase];
1164 }
1165 
FirstEntry(int first)1166 static void FirstEntry(int first) {
1167 	int	i;
1168 
1169 	g_InvD[INV_MENU].hInvTitle = FROM_32(g_pChosenScene->hSceneDesc);
1170 
1171 	// get number of entrances
1172 	g_numEntries = FROM_32(g_pChosenScene->numEntries);
1173 
1174 	// Force first to a sensible value
1175 	if (first > g_numEntries-NUM_RGROUP_BOXES)
1176 		first = g_numEntries-NUM_RGROUP_BOXES;
1177 	if (first < 0)
1178 		first = 0;
1179 
1180 	for (i = 0; i < NUM_RGROUP_BOXES && i < g_numEntries; i++) {
1181 		cd.box[i].textMethod = TM_STRINGNUM;
1182 		cd.box[i].ixText = FROM_32(g_pEntries[FROM_32(g_pChosenScene->entryIndex) + i + first].hDesc);
1183 	}
1184 	// Blank out the spare ones (if any)
1185 	while (i < NUM_RGROUP_BOXES) {
1186 		cd.box[i].textMethod = TM_NONE;
1187 		cd.box[i++].ixText = 0;
1188 	}
1189 
1190 	cd.extraBase = first;
1191 }
1192 
HopAction()1193 static void HopAction() {
1194 	PHOPENTRY pEntry = g_pEntries + FROM_32(g_pChosenScene->entryIndex) + cd.selBox + cd.extraBase;
1195 
1196 	uint32 hScene = FROM_32(g_pChosenScene->hScene);
1197 	uint32 eNumber = FROM_32(pEntry->eNumber);
1198 	debugC(DEBUG_BASIC, kTinselDebugAnimations, "Scene hopper chose scene %xh,%d\n", hScene, eNumber);
1199 
1200 	if (FROM_32(pEntry->flags) & fCall) {
1201 		SaveScene(Common::nullContext);
1202 		NewScene(Common::nullContext, g_pChosenScene->hScene, pEntry->eNumber, TRANS_FADE);
1203 	}
1204 	else if (FROM_32(pEntry->flags) & fHook)
1205 		HookScene(hScene, eNumber, TRANS_FADE);
1206 	else
1207 		NewScene(Common::nullContext, hScene, eNumber, TRANS_CUT);
1208 }
1209 
1210 /**************************************************************************/
1211 /******************** Some miscellaneous functions ************************/
1212 /**************************************************************************/
1213 
1214 /**
1215  * Delete all the objects in iconArray[]
1216  */
DumpIconArray()1217 static void DumpIconArray() {
1218 	for (int i = 0; i < MAX_ICONS; i++) {
1219 		if (g_iconArray[i] != NULL) {
1220 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[i]);
1221 			g_iconArray[i] = NULL;
1222 		}
1223 	}
1224 }
1225 
1226 /**
1227  * Delete all the objects in DobjArray[]
1228  */
DumpDobjArray()1229 static void DumpDobjArray() {
1230 	for (int i = 0; i < MAX_WCOMP; i++) {
1231 		if (g_DobjArray[i] != NULL) {
1232 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_DobjArray[i]);
1233 			g_DobjArray[i] = NULL;
1234 		}
1235 	}
1236 }
1237 
1238 /**
1239  * Delete all the objects in objArray[]
1240  */
DumpObjArray()1241 static void DumpObjArray() {
1242 	for (int i = 0; i < MAX_WCOMP; i++) {
1243 		if (g_objArray[i] != NULL) {
1244 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_objArray[i]);
1245 			g_objArray[i] = NULL;
1246 		}
1247 	}
1248 }
1249 
1250 /**
1251  * Convert item ID number to pointer to item's compiled data
1252  * i.e. Image data and Glitter code.
1253  */
GetInvObject(int id)1254 static INV_OBJECT *GetInvObject(int id) {
1255 	INV_OBJECT *pObject = g_invObjects;
1256 
1257 	for (int i = 0; i < g_numObjects; i++, pObject++) {
1258 		if (pObject->id == id)
1259 			return pObject;
1260 	}
1261 
1262 	error("GetInvObject(%d): Trying to manipulate undefined inventory icon", id);
1263 }
1264 
1265 /**
1266  * Returns true if the given id represents a valid inventory object
1267  */
GetIsInvObject(int id)1268 bool GetIsInvObject(int id) {
1269 	INV_OBJECT *pObject = g_invObjects;
1270 
1271 	for (int i = 0; i < g_numObjects; i++, pObject++) {
1272 		if (pObject->id == id)
1273 			return true;
1274 	}
1275 
1276 	return false;
1277 }
1278 
1279 /**
1280  * Convert item ID number to index.
1281  */
GetObjectIndex(int id)1282 static int GetObjectIndex(int id) {
1283 	INV_OBJECT *pObject = g_invObjects;
1284 
1285 	for (int i = 0; i < g_numObjects; i++, pObject++) {
1286 		if (pObject->id == id)
1287 			return i;
1288 	}
1289 
1290 	error("GetObjectIndex(%d): Trying to manipulate undefined inventory icon", id);
1291 }
1292 
1293 /**
1294  * Returns position of an item in one of the inventories.
1295  * The actual position is not important for the uses that this is put to.
1296  */
InventoryPos(int num)1297 extern int InventoryPos(int num) {
1298 	int	i;
1299 
1300 	for (i = 0; i < g_InvD[INV_1].NoofItems; i++)	// First inventory
1301 		if (g_InvD[INV_1].contents[i] == num)
1302 			return i;
1303 
1304 	for (i = 0; i < g_InvD[INV_2].NoofItems; i++)	// Second inventory
1305 		if (g_InvD[INV_2].contents[i] == num)
1306 			return i;
1307 
1308 	if (g_heldItem == num)
1309 		return INV_HELDNOTIN;	// Held, but not in either inventory
1310 
1311 	return INV_NOICON;		// Not held, not in either inventory
1312 }
1313 
IsInInventory(int object,int invnum)1314 extern bool IsInInventory(int object, int invnum) {
1315 	assert(invnum == INV_1 || invnum == INV_2);
1316 
1317 	for (int i = 0; i < g_InvD[invnum].NoofItems; i++)	// First inventory
1318 		if (g_InvD[invnum].contents[i] == object)
1319 			return true;
1320 
1321 	return false;
1322 }
1323 
1324 /**
1325  * Returns which item is held (INV_NOICON (-1) if none)
1326  */
WhichItemHeld()1327 extern int WhichItemHeld() {
1328 	return g_heldItem;
1329 }
1330 
1331 /**
1332  * Called from the cursor module when it re-initializes (at the start of
1333  * a new scene). For if we are holding something at scene-change time.
1334  */
InventoryIconCursor(bool bNewItem)1335 extern void InventoryIconCursor(bool bNewItem) {
1336 
1337 	if (g_heldItem != INV_NOICON) {
1338 		if (TinselV2) {
1339 			if (bNewItem) {
1340 				int	objIndex = GetObjectIndex(g_heldItem);
1341 				g_heldFilm = g_invFilms[objIndex];
1342 			}
1343 			SetAuxCursor(g_heldFilm);
1344 		} else {
1345 			INV_OBJECT *invObj = GetInvObject(g_heldItem);
1346 			SetAuxCursor(invObj->hIconFilm);
1347 		}
1348 	}
1349 }
1350 
1351 /**
1352  * Returns true if the inventory is active.
1353  */
InventoryActive()1354 extern bool InventoryActive() {
1355 	return (g_InventoryState == ACTIVE_INV);
1356 }
1357 
WhichInventoryOpen()1358 extern int WhichInventoryOpen() {
1359 	if (g_InventoryState != ACTIVE_INV)
1360 		return 0;
1361 	else
1362 		return g_ino;
1363 }
1364 
1365 
1366 /**************************************************************************/
1367 /************** Running inventory item's Glitter code *********************/
1368 /**************************************************************************/
1369 
1370 struct OP_INIT {
1371 	INV_OBJECT *pinvo;
1372 	TINSEL_EVENT	event;
1373 	PLR_EVENT	bev;
1374 	int	myEscape;
1375 };
1376 
1377 /**
1378  * Run inventory item's Glitter code
1379  */
ObjectProcess(CORO_PARAM,const void * param)1380 static void ObjectProcess(CORO_PARAM, const void *param) {
1381 	// COROUTINE
1382 	CORO_BEGIN_CONTEXT;
1383 		INT_CONTEXT *pic;
1384 		int	ThisPointedWait;			//	Fix the 'repeated pressing bug'
1385 	CORO_END_CONTEXT(_ctx);
1386 
1387 	// get the stuff copied to process when it was created
1388 	const OP_INIT *to = (const OP_INIT *)param;
1389 
1390 	CORO_BEGIN_CODE(_ctx);
1391 
1392 	if (!TinselV2)
1393 		CORO_INVOKE_1(AllowDclick, to->bev);
1394 
1395 	_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, to->event, NOPOLY, 0, to->pinvo,
1396 		to->myEscape);
1397 	CORO_INVOKE_1(Interpret, _ctx->pic);
1398 
1399 	if (to->event == POINTED) {
1400 		_ctx->ThisPointedWait = ++g_PointedWaitCount;
1401 		while (1) {
1402 			CORO_SLEEP(1);
1403 			int	x, y;
1404 			GetCursorXY(&x, &y, false);
1405 			if (InvItemId(x, y) != to->pinvo->id)
1406 				break;
1407 
1408 			// Fix the 'repeated pressing bug'
1409 			if (_ctx->ThisPointedWait != g_PointedWaitCount)
1410 				CORO_KILL_SELF();
1411 		}
1412 
1413 		_ctx->pic = InitInterpretContext(GS_INVENTORY, to->pinvo->hScript, UNPOINT, NOPOLY, 0, to->pinvo);
1414 		CORO_INVOKE_1(Interpret, _ctx->pic);
1415 	}
1416 
1417 	CORO_END_CODE;
1418 }
1419 
1420 /**
1421  * Run inventory item's Glitter code
1422  */
InvTinselEvent(INV_OBJECT * pinvo,TINSEL_EVENT event,PLR_EVENT be,int index)1423 static void InvTinselEvent(INV_OBJECT *pinvo, TINSEL_EVENT event, PLR_EVENT be, int index) {
1424 	OP_INIT to = { pinvo, event, be, 0 };
1425 
1426 	if (g_InventoryHidden || (TinselV2 && !pinvo->hScript))
1427 		return;
1428 
1429 	g_GlitterIndex = index;
1430 	CoroScheduler.createProcess(PID_TCODE, ObjectProcess, &to, sizeof(to));
1431 }
1432 
ObjectEvent(CORO_PARAM,int objId,TINSEL_EVENT event,bool bWait,int myEscape,bool * result)1433 extern void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result) {
1434 	// COROUTINE
1435 	CORO_BEGIN_CONTEXT;
1436 		Common::PROCESS		*pProc;
1437 		INV_OBJECT	*pInvo;
1438 		OP_INIT		op;
1439 	CORO_END_CONTEXT(_ctx);
1440 
1441 	CORO_BEGIN_CODE(_ctx);
1442 
1443 	if (result) *result = false;
1444 	_ctx->pInvo = GetInvObject(objId);
1445 	if (!_ctx->pInvo->hScript)
1446 		return;
1447 
1448 	_ctx->op.pinvo = _ctx->pInvo;
1449 	_ctx->op.event = event;
1450 	_ctx->op.myEscape = myEscape;
1451 
1452 	CoroScheduler.createProcess(PID_TCODE, ObjectProcess, &_ctx->op, sizeof(_ctx->op));
1453 
1454 	if (bWait)
1455 		CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
1456 	else if (result)
1457 		*result = false;
1458 
1459 	CORO_END_CODE;
1460 }
1461 
1462 /**************************************************************************/
1463 /****************** Load/Save game specific functions *********************/
1464 /**************************************************************************/
1465 
1466 /**
1467  * Set first load/save file entry displayed.
1468  * Point Box[] text pointers to appropriate file descriptions.
1469  */
FirstFile(int first)1470 static void FirstFile(int first) {
1471 	int	i, j;
1472 
1473 	i = getList();
1474 
1475 	cd.numSaved = i;
1476 
1477 	if (first < 0)
1478 		first = 0;
1479 	else if (first > MAX_SAVED_FILES - NUM_RGROUP_BOXES)
1480 		first = MAX_SAVED_FILES - NUM_RGROUP_BOXES;
1481 
1482 	if (first == 0 && i < MAX_SAVED_FILES && cd.box == saveBox) {
1483 		// Blank first entry for new save
1484 		cd.box[0].boxText = NULL;
1485 		cd.modifier = j = 1;
1486 	} else {
1487 		cd.modifier = j = 0;
1488 	}
1489 
1490 	for (i = first; j < NUM_RGROUP_BOXES; j++, i++) {
1491 		cd.box[j].boxText = ListEntry(i, LE_DESC);
1492 	}
1493 
1494 	cd.extraBase = first;
1495 }
1496 
1497 /**
1498  * Save the game using filename from selected slot & current description.
1499  */
1500 
InvSaveGame()1501 static void InvSaveGame() {
1502 	if (cd.selBox != NOBOX) {
1503 #ifndef JAPAN
1504 		g_sedit[strlen(g_sedit)-1] = 0;	// Don't include the cursor!
1505 #endif
1506 		SaveGame(ListEntry(cd.selBox-cd.modifier+cd.extraBase, LE_NAME), g_sedit);
1507 	}
1508 }
1509 
1510 /**
1511  * Load the selected saved game.
1512  */
InvLoadGame()1513 static void InvLoadGame() {
1514 	int	rGame;
1515 
1516 	if (cd.selBox != NOBOX && (cd.selBox+cd.extraBase < cd.numSaved)) {
1517 		rGame = cd.selBox;
1518 		cd.selBox = NOBOX;
1519 		if (g_iconArray[HL3] != NULL) {
1520 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL3]);
1521 			g_iconArray[HL3] = NULL;
1522 		}
1523 		if (g_iconArray[HL2] != NULL) {
1524 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL2]);
1525 			g_iconArray[HL2] = NULL;
1526 		}
1527 		if (g_iconArray[HL1] != NULL) {
1528 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
1529 			g_iconArray[HL1] = NULL;
1530 		}
1531 		RestoreGame(rGame+cd.extraBase);
1532 	}
1533 }
1534 
1535 /**
1536  * Edit the string in sedit[]
1537  * Returns true if the string was altered.
1538  */
1539 #ifndef JAPAN
UpdateString(const Common::KeyState & kbd)1540 static bool UpdateString(const Common::KeyState &kbd) {
1541 	int	cpos;
1542 
1543 	if (!cd.editableRgroup)
1544 		return false;
1545 
1546 	cpos = strlen(g_sedit)-1;
1547 
1548 	if (kbd.ascii == 0)
1549 		return false;
1550 
1551 	if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
1552 		if (!cpos)
1553 			return false;
1554 		g_sedit[cpos] = 0;
1555 		cpos--;
1556 		g_sedit[cpos] = CURSOR_CHAR;
1557 		return true;
1558 //	} else if (isalnum(c) || c == ',' || c == '.' || c == '\'' || (c == ' ' && cpos != 0)) {
1559 	} else if (IsCharImage(GetTagFontHandle(), kbd.ascii) || (kbd.ascii == ' ' && cpos != 0)) {
1560 		if (cpos == SG_DESC_LEN)
1561 			return false;
1562 		g_sedit[cpos] = kbd.ascii;
1563 		cpos++;
1564 		g_sedit[cpos] = CURSOR_CHAR;
1565 		g_sedit[cpos+1] = 0;
1566 		return true;
1567 	}
1568 	return false;
1569 }
1570 #endif
1571 
1572 /**
1573  * Keystrokes get sent here when load/save screen is up.
1574  */
InvKeyIn(const Common::KeyState & kbd)1575 static bool InvKeyIn(const Common::KeyState &kbd) {
1576 	if (kbd.keycode == Common::KEYCODE_PAGEUP ||
1577 	    kbd.keycode == Common::KEYCODE_PAGEDOWN ||
1578 	    kbd.keycode == Common::KEYCODE_HOME ||
1579 	    kbd.keycode == Common::KEYCODE_END)
1580 		return true;	// Key needs processing
1581 
1582 	if (kbd.keycode == 0 && kbd.ascii == 0) {
1583 		;
1584 	} else if (kbd.keycode == Common::KEYCODE_RETURN) {
1585 		return true;	// Key needs processing
1586 	} else if (kbd.keycode == Common::KEYCODE_ESCAPE) {
1587 		return true;	// Key needs processing
1588 	} else {
1589 #ifndef JAPAN
1590 		if (UpdateString(kbd)) {
1591 			/*
1592 			* Delete display of text currently being edited,
1593 			* and replace it with freshly edited text.
1594 			*/
1595 			if (g_iconArray[HL3] != NULL) {
1596 				MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL3]);
1597 				g_iconArray[HL3] = NULL;
1598 			}
1599 			g_iconArray[HL3] = ObjectTextOut(
1600 				GetPlayfieldList(FIELD_STATUS), g_sedit, 0,
1601 				g_InvD[g_ino].inventoryX + cd.box[cd.selBox].xpos + 2,
1602 				g_InvD[g_ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
1603 				GetTagFontHandle(), 0);
1604 			if (MultiRightmost(g_iconArray[HL3]) > MAX_NAME_RIGHT) {
1605 				MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL3]);
1606 				UpdateString(Common::KeyState(Common::KEYCODE_BACKSPACE));
1607 				g_iconArray[HL3] = ObjectTextOut(
1608 					GetPlayfieldList(FIELD_STATUS), g_sedit, 0,
1609 					g_InvD[g_ino].inventoryX + cd.box[cd.selBox].xpos + 2,
1610 					g_InvD[g_ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
1611 					GetTagFontHandle(), 0);
1612 			}
1613 			MultiSetZPosition(g_iconArray[HL3], Z_INV_ITEXT + 2);
1614 		}
1615 #endif
1616 	}
1617 	return false;
1618 }
1619 
1620 /**
1621  * Highlights selected box.
1622  * If it's editable (save game), copy existing description and add a cursor.
1623  */
Select(int i,bool force)1624 static void Select(int i, bool force) {
1625 #ifdef JAPAN
1626 	time_t		secs_now;
1627 	struct tm	*time_now;
1628 #endif
1629 
1630 	i &= ~IS_MASK;
1631 
1632 	if (cd.selBox == i && !force)
1633 		return;
1634 
1635 	cd.selBox = i;
1636 
1637 	// Clear previous selected highlight and text
1638 	if (g_iconArray[HL2] != NULL) {
1639 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL2]);
1640 		g_iconArray[HL2] = NULL;
1641 	}
1642 	if (g_iconArray[HL3] != NULL) {
1643 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL3]);
1644 		g_iconArray[HL3] = NULL;
1645 	}
1646 
1647 	// New highlight box
1648 	switch (cd.box[i].boxType) {
1649 	case RGROUP:
1650 		g_iconArray[HL2] = RectangleObject(BgPal(),
1651 			(TinselV2 ? HighlightColor() : COL_HILIGHT), cd.box[i].w, cd.box[i].h);
1652 		MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL2]);
1653 		MultiSetAniXY(g_iconArray[HL2],
1654 		g_InvD[g_ino].inventoryX + cd.box[i].xpos,
1655 		g_InvD[g_ino].inventoryY + cd.box[i].ypos);
1656 
1657 		// Z-position of box, and add edit text if appropriate
1658 		if (cd.editableRgroup) {
1659 			MultiSetZPosition(g_iconArray[HL2], Z_INV_ITEXT+1);
1660 
1661 			if (TinselV2) {
1662 				assert(cd.box[i].textMethod == TM_POINTER);
1663 			} else {
1664 				assert(cd.box[i].ixText == USE_POINTER);
1665 			}
1666 #ifdef JAPAN
1667 			// Current date and time
1668 			time(&secs_now);
1669 			time_now = localtime(&secs_now);
1670 			strftime(g_sedit, SG_DESC_LEN, "%D %H:%M", time_now);
1671 #else
1672 			// Current description with cursor appended
1673 			if (cd.box[i].boxText != NULL) {
1674 				Common::strlcpy(g_sedit, cd.box[i].boxText, SG_DESC_LEN+2);
1675 				Common::strlcat(g_sedit, sCursor, SG_DESC_LEN+2);
1676 			} else {
1677 				Common::strlcpy(g_sedit, sCursor, SG_DESC_LEN+2);
1678 			}
1679 #endif
1680 
1681 			g_iconArray[HL3] = ObjectTextOut(
1682 				GetPlayfieldList(FIELD_STATUS), g_sedit, 0,
1683 				g_InvD[g_ino].inventoryX + cd.box[i].xpos + 2,
1684 #ifdef JAPAN
1685 				g_InvD[g_ino].inventoryY + cd.box[i].ypos + 2,
1686 #else
1687 				g_InvD[g_ino].inventoryY + cd.box[i].ypos + TYOFF,
1688 #endif
1689 				GetTagFontHandle(), 0);
1690 			MultiSetZPosition(g_iconArray[HL3], Z_INV_ITEXT + 2);
1691 		} else {
1692 			MultiSetZPosition(g_iconArray[HL2], Z_INV_ICONS + 1);
1693 		}
1694 
1695 		_vm->divertKeyInput(InvKeyIn);
1696 
1697 		break;
1698 
1699 	case FRGROUP:
1700 		g_iconArray[HL2] = RectangleObject(BgPal(), COL_HILIGHT, cd.box[i].w+6, cd.box[i].h+6);
1701 		MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL2]);
1702 		MultiSetAniXY(g_iconArray[HL2],
1703 		g_InvD[g_ino].inventoryX + cd.box[i].xpos - 2,
1704 		g_InvD[g_ino].inventoryY + cd.box[i].ypos - 2);
1705 		MultiSetZPosition(g_iconArray[HL2], Z_INV_BRECT+1);
1706 
1707 		break;
1708 
1709 	default:
1710 		break;
1711 	}
1712 }
1713 
1714 
1715 /**************************************************************************/
1716 /***/
1717 /**************************************************************************/
1718 
1719 /**
1720  * Stop holding an item.
1721  */
DropItem(int item)1722 extern void DropItem(int item) {
1723 	if (g_heldItem == item) {
1724 		g_heldItem = INV_NOICON;		// Item not held
1725 		DelAuxCursor();			// no longer aux cursor
1726 	}
1727 
1728 	// Redraw contents - held item was not displayed as a content.
1729 	g_ItemsChanged = true;
1730 }
1731 
1732 /**
1733  * Clears the specified inventory
1734  */
ClearInventory(int invno)1735 extern void ClearInventory(int invno) {
1736 	assert(invno == INV_1 || invno == INV_2);
1737 
1738 	g_InvD[invno].NoofItems = 0;
1739 	memset(g_InvD[invno].contents, 0, sizeof(g_InvD[invno].contents));
1740 }
1741 
1742 /**
1743  * Stick the item into an inventory list (contents[]), and hold the
1744  * item if requested.
1745  */
AddToInventory(int invno,int icon,bool hold)1746 extern void AddToInventory(int invno, int icon, bool hold) {
1747 	int i;
1748 	bool bOpen;
1749 	INV_OBJECT *invObj;
1750 
1751 	// Validate trying to add to a legal inventory
1752 	assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV
1753 		|| invno == INV_OPEN || (invno == INV_DEFAULT && TinselV2));
1754 
1755 	if (invno == INV_OPEN) {
1756 		assert(g_InventoryState == ACTIVE_INV && (g_ino == INV_1 || g_ino == INV_2)); // addopeninv() with inventry not open
1757 		invno = g_ino;
1758 		bOpen = true;
1759 
1760 		// Make sure it doesn't get in both!
1761 		RemFromInventory(g_ino == INV_1 ? INV_2 : INV_1, icon);
1762 	} else {
1763 		bOpen = false;
1764 
1765 		if (TinselV2 && invno == INV_DEFAULT) {
1766 			invObj = GetInvObject(icon);
1767 			if (invObj->attribute & DEFINV2)
1768 				invno = INV_2;
1769 			else if (invObj->attribute & DEFINV1)
1770 				invno = INV_1;
1771 			else
1772 				invno = SysVar(SV_DEFAULT_INV);
1773 		}
1774 	}
1775 
1776 	if (invno == INV_1)
1777 		RemFromInventory(INV_2, icon);
1778 	else if (invno == INV_2)
1779 		RemFromInventory(INV_1, icon);
1780 
1781 	// See if it's already there
1782 	for (i = 0; i < g_InvD[invno].NoofItems; i++) {
1783 		if (g_InvD[invno].contents[i] == icon)
1784 			break;
1785 	}
1786 
1787 	// Add it if it isn't already there
1788 	if (i == g_InvD[invno].NoofItems) {
1789 		if (!bOpen) {
1790 			if (invno == INV_CONV) {
1791 				if (TinselV2) {
1792 					int nei;
1793 
1794 					// Count how many current contents have end attribute
1795 					for (i = 0, nei = 0; i < g_InvD[INV_CONV].NoofItems; i++) {
1796 						invObj = GetInvObject(g_InvD[INV_CONV].contents[i]);
1797 						if (invObj->attribute & CONVENDITEM)
1798 							nei++;
1799 					}
1800 
1801 					// For conversation, insert before end icons
1802 					memmove(&g_InvD[INV_CONV].contents[i-nei+1],
1803 						&g_InvD[INV_CONV].contents[i-nei], nei * sizeof(int));
1804 					g_InvD[INV_CONV].contents[i - nei] = icon;
1805 					g_InvD[INV_CONV].NoofItems++;
1806 					g_InvD[INV_CONV].NoofHicons = g_InvD[INV_CONV].NoofItems;
1807 
1808 					// Get the window to re-position
1809 					g_bMoveOnUnHide = true;
1810 				} else {
1811 					// For conversation, insert before last icon
1812 					// which will always be the goodbye icon
1813 					g_InvD[invno].contents[g_InvD[invno].NoofItems] = g_InvD[invno].contents[g_InvD[invno].NoofItems-1];
1814 					g_InvD[invno].contents[g_InvD[invno].NoofItems-1] = icon;
1815 					g_InvD[invno].NoofItems++;
1816 				}
1817 			} else {
1818 				g_InvD[invno].contents[g_InvD[invno].NoofItems++] = icon;
1819 			}
1820 			g_ItemsChanged = true;
1821 		} else {
1822 			// It could be that the index is beyond what you'd expect
1823 			// as delinv may well have been called
1824 			if (g_GlitterIndex < g_InvD[invno].NoofItems) {
1825 				memmove(&g_InvD[invno].contents[g_GlitterIndex + 1],
1826 					&g_InvD[invno].contents[g_GlitterIndex],
1827 					(g_InvD[invno].NoofItems - g_GlitterIndex) * sizeof(int));
1828 				g_InvD[invno].contents[g_GlitterIndex] = icon;
1829 			} else {
1830 				g_InvD[invno].contents[g_InvD[invno].NoofItems] = icon;
1831 			}
1832 			g_InvD[invno].NoofItems++;
1833 		}
1834 
1835 		// Move here after bug on Japenese DW1
1836 		g_ItemsChanged = true;
1837 	}
1838 
1839 	// Hold it if requested
1840 	if (hold)
1841 		HoldItem(icon);
1842 }
1843 
1844 /**
1845  * Take the item from the inventory list (contents[]).
1846  * Return FALSE if item wasn't present, true if it was.
1847  */
RemFromInventory(int invno,int icon)1848 extern bool RemFromInventory(int invno, int icon) {
1849 	int i;
1850 
1851 	assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV); // Trying to delete from illegal inventory
1852 
1853 	// See if it's there
1854 	for (i = 0; i < g_InvD[invno].NoofItems; i++) {
1855 		if (g_InvD[invno].contents[i] == icon)
1856 			break;
1857 	}
1858 
1859 	if (i == g_InvD[invno].NoofItems)
1860 		return false;			// Item wasn't there
1861 	else {
1862 		memmove(&g_InvD[invno].contents[i], &g_InvD[invno].contents[i+1], (g_InvD[invno].NoofItems-i)*sizeof(int));
1863 		g_InvD[invno].NoofItems--;
1864 
1865 		if (TinselV2 && invno == INV_CONV) {
1866 			g_InvD[INV_CONV].NoofHicons = g_InvD[invno].NoofItems;
1867 
1868 			// Get the window to re-position
1869 			g_bMoveOnUnHide = true;
1870 		}
1871 
1872 		g_ItemsChanged = true;
1873 		return true;			// Item removed
1874 	}
1875 }
1876 
1877 /**
1878  * If the item is not already held, hold it.
1879  */
HoldItem(int item,bool bKeepFilm)1880 extern void HoldItem(int item, bool bKeepFilm) {
1881 	INV_OBJECT *invObj;
1882 
1883 	if (g_heldItem != item) {
1884 		if (TinselV2 && (g_heldItem != NOOBJECT)) {
1885 			// No longer holding previous item
1886 			DelAuxCursor();	 // no longer aux cursor
1887 
1888 			// If old held object is not in an inventory, and
1889 			// has a default, stick it in its default inventory.
1890 			if (!IsInInventory(g_heldItem, INV_1) && !IsInInventory(g_heldItem, INV_2)) {
1891 				invObj = GetInvObject(g_heldItem);
1892 
1893 				if (invObj->attribute & DEFINV1)
1894 					AddToInventory(INV_1, g_heldItem);
1895 				else if (invObj->attribute & DEFINV2)
1896 					AddToInventory(INV_2, g_heldItem);
1897 				else
1898 					// Hook for definable default inventory
1899 					AddToInventory(INV_1, g_heldItem);
1900 			}
1901 
1902 		} else if (!TinselV2) {
1903 			if (item == INV_NOICON && g_heldItem != INV_NOICON)
1904 				DelAuxCursor();			// no longer aux cursor
1905 
1906 			if (item != INV_NOICON) {
1907 				invObj = GetInvObject(item);
1908 				SetAuxCursor(invObj->hIconFilm);	// and is aux. cursor
1909 			}
1910 
1911 			// WORKAROUND: If a held item is being removed that's not in either inventory (i.e. it was picked up
1912 			// but never put in them), then when removing it from being held, drop it in the luggage
1913 			if (g_heldItem != INV_NOICON && InventoryPos(g_heldItem) == INV_HELDNOTIN)
1914 				AddToInventory(INV_1, g_heldItem);
1915 		}
1916 
1917 		g_heldItem = item;			// Item held
1918 
1919 		if (TinselV2) {
1920 			InventoryIconCursor(!bKeepFilm);
1921 
1922 			// Redraw contents - held item not displayed as a content.
1923 			g_ItemsChanged = true;
1924 		}
1925 	}
1926 
1927 	if (!TinselV2)
1928 		// Redraw contents - held item not displayed as a content.
1929 		g_ItemsChanged = true;
1930 }
1931 
1932 /**************************************************************************/
1933 /***/
1934 /**************************************************************************/
1935 
1936 enum {	I_NOTIN, I_HEADER, I_BODY,
1937 	I_TLEFT, I_TRIGHT, I_BLEFT, I_BRIGHT,
1938 	I_TOP, I_BOTTOM, I_LEFT, I_RIGHT,
1939 	I_UP, I_SLIDE_UP, I_SLIDE, I_SLIDE_DOWN, I_DOWN,
1940 	I_ENDCHANGE
1941 };
1942 
1943 #define EXTRA	1	// This was introduced when we decided to increase
1944 			// the active area of the borders for re-sizing.
1945 
1946 /*---------------------------------*/
1947 #define LeftX	g_InvD[g_ino].inventoryX
1948 #define TopY	g_InvD[g_ino].inventoryY
1949 /*---------------------------------*/
1950 
1951 /**
1952  * Work out which area of the inventory window the cursor is in.
1953  *
1954  * This used to be worked out with appropriately defined magic numbers.
1955  * Then the graphic changed and I got it right again. Then the graphic
1956  * changed and I got fed up of faffing about. It's probably easier just
1957  * to rework all this.
1958  */
InvArea(int x,int y)1959 static int InvArea(int x, int y) {
1960 	if (TinselV2) {
1961 		int RightX = MultiRightmost(g_RectObject) - NM_BG_SIZ_X - NM_BG_POS_X - NM_RS_R_INSET;
1962 		int BottomY = MultiLowest(g_RectObject) - NM_BG_SIZ_Y - NM_BG_POS_Y - NM_RS_B_INSET;
1963 
1964 		// Outside the whole rectangle?
1965 		if (x <= LeftX || x > RightX || y <= TopY || y > BottomY)
1966 			return I_NOTIN;
1967 
1968 		// The bottom line
1969 		if (y > BottomY - NM_RS_THICKNESS) {
1970 			// Below top of bottom line?
1971 			if (x <= LeftX + NM_RS_THICKNESS)
1972 				return I_BLEFT;		// Bottom left corner
1973 			else if (x > RightX - NM_RS_THICKNESS)
1974 				return I_BRIGHT;	// Bottom right corner
1975 			else
1976 				return I_BOTTOM;	// Just plain bottom
1977 		}
1978 
1979 		// The top line
1980 		if (y <= TopY + NM_RS_THICKNESS) {
1981 			// Above bottom of top line?
1982 			if (x <= LeftX + NM_RS_THICKNESS)
1983 				return I_TLEFT;		// Top left corner
1984 			else if (x > RightX - NM_RS_THICKNESS)
1985 				return I_TRIGHT;	// Top right corner
1986 			else
1987 				return I_TOP;		// Just plain top
1988 		}
1989 
1990 		// Sides
1991 		if (x <= LeftX + NM_RS_THICKNESS)	// Left of right of left side?
1992 			return I_LEFT;
1993 		else if (x > RightX - NM_RS_THICKNESS)	// Right of left of right side?
1994 			return I_RIGHT;
1995 
1996 		// In the move area?
1997 		if (y < TopY + NM_MOVE_AREA_B_Y)
1998 			return I_HEADER;
1999 
2000 		// Scroll bits
2001 		if (!(g_ino == INV_MENU && cd.bExtraWin)) {
2002 			if (x > RightX - NM_SLIDE_INSET && x <= RightX - NM_SLIDE_INSET + NM_SLIDE_THICKNESS) {
2003 				if (y > TopY + NM_UP_ARROW_TOP && y < TopY + NM_UP_ARROW_BOTTOM)
2004 					return I_UP;
2005 				if (y > BottomY - NM_DN_ARROW_TOP && y <= BottomY - NM_DN_ARROW_BOTTOM)
2006 					return I_DOWN;
2007 
2008 				/* '3' is a magic adjustment with no apparent sense */
2009 
2010 				if (y >= TopY + g_sliderYmin - 3 && y < TopY + g_sliderYmax + NM_SLH) {
2011 					if (y < TopY + g_sliderYpos - 3)
2012 						return I_SLIDE_UP;
2013 					if (y < TopY + g_sliderYpos + NM_SLH - 3)
2014 						return I_SLIDE;
2015 					else
2016 						return I_SLIDE_DOWN;
2017 				}
2018 			}
2019 		}
2020 	} else {
2021 		int RightX = MultiRightmost(g_RectObject) + 1;
2022 		int BottomY = MultiLowest(g_RectObject) + 1;
2023 
2024 		// Outside the whole rectangle?
2025 		if (x <= LeftX - EXTRA || x > RightX + EXTRA
2026 		|| y <= TopY - EXTRA || y > BottomY + EXTRA)
2027 			return I_NOTIN;
2028 
2029 		// The bottom line
2030 		if (y > BottomY - 2 - EXTRA) {		// Below top of bottom line?
2031 			if (x <= LeftX + 2 + EXTRA)
2032 				return I_BLEFT;		// Bottom left corner
2033 			else if (x > RightX - 2 - EXTRA)
2034 				return I_BRIGHT;	// Bottom right corner
2035 			else
2036 				return I_BOTTOM;	// Just plain bottom
2037 		}
2038 
2039 		// The top line
2040 		if (y <= TopY + 2 + EXTRA) {		// Above bottom of top line?
2041 			if (x <= LeftX + 2 + EXTRA)
2042 				return I_TLEFT;		// Top left corner
2043 			else if (x > RightX - 2 - EXTRA)
2044 				return I_TRIGHT;	// Top right corner
2045 			else
2046 				return I_TOP;		// Just plain top
2047 		}
2048 
2049 		// Sides
2050 		if (x <= LeftX + 2 + EXTRA)		// Left of right of left side?
2051 			return I_LEFT;
2052 		else if (x > RightX - 2 - EXTRA)		// Right of left of right side?
2053 			return I_RIGHT;
2054 
2055 		// From here down still needs fixing up properly
2056 		/*
2057 		 * In the move area?
2058 		 */
2059 		if (g_ino != INV_CONF
2060 		&& x >= LeftX + M_SW - 2 && x <= RightX - M_SW + 3 &&
2061 		   y >= TopY + M_TH - 2  && y < TopY + M_TBB + 2)
2062 			return I_HEADER;
2063 
2064 		/*
2065 		 * Scroll bits
2066 		 */
2067 		if (!(g_ino == INV_CONF && cd.bExtraWin)) {
2068 			if (x > RightX - NM_SLIDE_INSET && x <= RightX - NM_SLIDE_INSET + NM_SLIDE_THICKNESS) {
2069 				if (y > TopY + M_IUT + 1 && y < TopY + M_IUB - 1)
2070 					return I_UP;
2071 				if (y > BottomY - M_IDT + 4 && y <= BottomY - M_IDB + 1)
2072 					return I_DOWN;
2073 
2074 				if (y >= TopY + g_sliderYmin && y < TopY + g_sliderYmax + M_SH) {
2075 					if (y < TopY + g_sliderYpos)
2076 						return I_SLIDE_UP;
2077 					if (y < TopY + g_sliderYpos + M_SH)
2078 						return I_SLIDE;
2079 					else
2080 						return I_SLIDE_DOWN;
2081 				}
2082 			}
2083 		}
2084 	}
2085 
2086 	return I_BODY;
2087 }
2088 
2089 /**
2090  * Returns the id of the icon displayed under the given position.
2091  * Also return co-ordinates of items tag display position, if requested.
2092  */
InvItem(int * x,int * y,bool update)2093 extern int InvItem(int *x, int *y, bool update) {
2094 	int itop, ileft;
2095 	int row, col;
2096 	int item;
2097 	int IconsX;
2098 
2099 	itop = g_InvD[g_ino].inventoryY + START_ICONY;
2100 
2101 	IconsX = g_InvD[g_ino].inventoryX + START_ICONX;
2102 
2103 	for (item = g_InvD[g_ino].FirstDisp, row = 0; row < g_InvD[g_ino].NoofVicons; row++) {
2104 		ileft = IconsX;
2105 
2106 		for (col = 0; col < g_InvD[g_ino].NoofHicons; col++, item++) {
2107 			if (*x >= ileft && *x < ileft + ITEM_WIDTH &&
2108 			   *y >= itop  && *y < itop + ITEM_HEIGHT) {
2109 				if (update) {
2110 					*x = ileft + ITEM_WIDTH/2;
2111 					*y = itop /*+ ITEM_HEIGHT/4*/;
2112 				}
2113 				return item;
2114 			}
2115 
2116 			ileft += ITEM_WIDTH + 1;
2117 		}
2118 		itop += ITEM_HEIGHT + 1;
2119 	}
2120 	return INV_NOICON;
2121 }
2122 
InvItem(Common::Point & coOrds,bool update)2123 static int InvItem(Common::Point &coOrds, bool update) {
2124 	int x = coOrds.x;
2125 	int y = coOrds.y;
2126 	return InvItem(&x, &y, update);
2127 	//coOrds.x = x;
2128 	//coOrds.y = y;
2129 }
2130 
2131 /**
2132  * Returns the id of the icon displayed under the given position.
2133  */
InvItemId(int x,int y)2134 int InvItemId(int x, int y) {
2135 	int itop, ileft;
2136 	int row, col;
2137 	int item;
2138 
2139 	if (g_InventoryHidden || g_InventoryState == IDLE_INV)
2140 		return INV_NOICON;
2141 
2142 	itop = g_InvD[g_ino].inventoryY + START_ICONY;
2143 
2144 	int IconsX = g_InvD[g_ino].inventoryX + START_ICONX;
2145 
2146 	for (item = g_InvD[g_ino].FirstDisp, row = 0; row < g_InvD[g_ino].NoofVicons; row++) {
2147 		ileft = IconsX;
2148 
2149 		for (col = 0; col < g_InvD[g_ino].NoofHicons; col++, item++) {
2150 			if (x >= ileft && x < ileft + ITEM_WIDTH &&
2151 			   y >= itop  && y < itop + ITEM_HEIGHT) {
2152 				return g_InvD[g_ino].contents[item];
2153 			}
2154 
2155 			ileft += ITEM_WIDTH + 1;
2156 		}
2157 		itop += ITEM_HEIGHT + 1;
2158 	}
2159 	return INV_NOICON;
2160 }
2161 
2162 /**
2163  * Finds which box the cursor is in.
2164  */
WhichMenuBox(int curX,int curY,bool bSlides)2165 static int WhichMenuBox(int curX, int curY, bool bSlides) {
2166 	if (bSlides) {
2167 		for (int i = 0; i < g_numMdSlides; i++) {
2168 			if (curY > MultiHighest(g_mdSlides[i].obj) && curY < MultiLowest(g_mdSlides[i].obj)
2169 			&& curX > MultiLeftmost(g_mdSlides[i].obj) && curX < MultiRightmost(g_mdSlides[i].obj))
2170 				return g_mdSlides[i].num | IS_SLIDER;
2171 		}
2172 	}
2173 
2174 	curX -= g_InvD[g_ino].inventoryX;
2175 	curY -= g_InvD[g_ino].inventoryY;
2176 
2177 	for (int i = 0; i < cd.NumBoxes; i++) {
2178 		switch (cd.box[i].boxType) {
2179 		case SLIDER:
2180 			if (bSlides) {
2181 				if (curY >= cd.box[i].ypos+MD_YBUTTOP && curY < cd.box[i].ypos+MD_YBUTBOT) {
2182 					if (curX >= cd.box[i].xpos+MD_XLBUTL && curX < cd.box[i].xpos+MD_XLBUTR)
2183 						return i | IS_LEFT;
2184 					if (curX >= cd.box[i].xpos+MD_XRBUTL && curX < cd.box[i].xpos+MD_XRBUTR)
2185 						return i | IS_RIGHT;
2186 				}
2187 			}
2188 			break;
2189 
2190 		case AAGBUT:
2191 		case ARSGBUT:
2192 		case TOGGLE:
2193 		case TOGGLE1:
2194 		case TOGGLE2:
2195 		case FLIP:
2196 			if (curY > cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h
2197 			&& curX > cd.box[i].xpos && curX < cd.box[i].xpos + cd.box[i].w)
2198 				return i;
2199 			break;
2200 
2201 		case ROTATE:
2202 			if (g_bNoLanguage)
2203 				break;
2204 
2205 			if (curY > cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h) {
2206 				// Left one?
2207 				if (curX > cd.box[i].xpos-ROTX1 && curX < cd.box[i].xpos-ROTX1 + cd.box[i].w) {
2208 					cd.box[i].bi = IX2_LEFT1;
2209 					return i;
2210 				}
2211 				// Right one?
2212 				if (curX > cd.box[i].xpos+ROTX1 && curX < cd.box[i].xpos+ROTX1 + cd.box[i].w) {
2213 					cd.box[i].bi = IX2_RIGHT1;
2214 					return i;
2215 				}
2216 			}
2217 			break;
2218 
2219 		default:
2220 			// 'Normal' box
2221 			if (curY >= cd.box[i].ypos && curY < cd.box[i].ypos + cd.box[i].h
2222 			&& curX >= cd.box[i].xpos && curX < cd.box[i].xpos + cd.box[i].w)
2223 				return i;
2224 			break;
2225 		}
2226 	}
2227 
2228 	// Slider on extra window
2229 	if (cd.bExtraWin) {
2230 		const Common::Rect r = TinselV2 ?
2231 			Common::Rect(411, 46, 425, 339) :
2232 			Common::Rect(20 + 181, 24 + 2, 20 + 181 + 8, 24 + 139 + 5);
2233 
2234 		if (r.contains(curX, curY)) {
2235 
2236 			if (curY < (r.top + (TinselV2 ? 18 : 5)))
2237 				return IB_UP;
2238 			else if (curY > (r.bottom - (TinselV2 ? 18 : 5)))
2239 				return IB_DOWN;
2240 			else if (curY + g_InvD[g_ino].inventoryY < g_sliderYpos)
2241 				return IB_SLIDE_UP;
2242 			else if (curY + g_InvD[g_ino].inventoryY >= g_sliderYpos + NM_SLH)
2243 				return IB_SLIDE_DOWN;
2244 			else
2245 				return IB_SLIDE;
2246 		}
2247 	}
2248 
2249 	return IB_NONE;
2250 }
2251 
2252 /**************************************************************************/
2253 /***/
2254 /**************************************************************************/
2255 
2256 #define ROTX1 60	// Rotate button's offsets from the center
2257 
2258 /**
2259  * InvBoxes
2260  */
InvBoxes(bool InBody,int curX,int curY)2261 static void InvBoxes(bool InBody, int curX, int curY) {
2262 	static int rotateIndex = -1;	// FIXME: Avoid non-const global vars
2263 	int	index;			// Box pointed to on this call
2264 	const FILM *pfilm;
2265 
2266 	// Find out which icon is currently pointed to
2267 	if (!InBody)
2268 		index = -1;
2269 	else {
2270 		index = WhichMenuBox(curX, curY, false);
2271 	}
2272 
2273 	// If no icon pointed to, or points to (logical position of)
2274 	// currently held icon, then no icon is pointed to!
2275 	if (index < 0) {
2276 		// unhigh-light box (if one was)
2277 		cd.pointBox = NOBOX;
2278 		if (g_iconArray[HL1] != NULL) {
2279 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2280 			g_iconArray[HL1] = NULL;
2281 		}
2282 	} else if (index != cd.pointBox) {
2283 		cd.pointBox = index;
2284 		// A new box is pointed to - high-light it
2285 		if (g_iconArray[HL1] != NULL) {
2286 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2287 			g_iconArray[HL1] = NULL;
2288 		}
2289 		if ((cd.box[cd.pointBox].boxType == ARSBUT && cd.selBox != NOBOX) ||
2290 ///* I don't agree */ cd.box[cd.pointBox].boxType == RGROUP ||
2291 		    cd.box[cd.pointBox].boxType == AATBUT ||
2292 		    cd.box[cd.pointBox].boxType == AABUT) {
2293 			g_iconArray[HL1] = RectangleObject(BgPal(),
2294 				(TinselV2 ? HighlightColor() : COL_HILIGHT),
2295 				cd.box[cd.pointBox].w, cd.box[cd.pointBox].h);
2296 			MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2297 			MultiSetAniXY(g_iconArray[HL1],
2298 				g_InvD[g_ino].inventoryX + cd.box[cd.pointBox].xpos,
2299 				g_InvD[g_ino].inventoryY + cd.box[cd.pointBox].ypos);
2300 			MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2301 		} else if (cd.box[cd.pointBox].boxType == AAGBUT ||
2302 				cd.box[cd.pointBox].boxType == ARSGBUT ||
2303 				cd.box[cd.pointBox].boxType == TOGGLE ||
2304 				cd.box[cd.pointBox].boxType == TOGGLE1 ||
2305 				cd.box[cd.pointBox].boxType == TOGGLE2) {
2306 			pfilm = (const FILM *)LockMem(g_hWinParts);
2307 
2308 			g_iconArray[HL1] = AddObject(&pfilm->reels[cd.box[cd.pointBox].bi+HIGRAPH], -1);
2309 			MultiSetAniXY(g_iconArray[HL1],
2310 				g_InvD[g_ino].inventoryX + cd.box[cd.pointBox].xpos,
2311 				g_InvD[g_ino].inventoryY + cd.box[cd.pointBox].ypos);
2312 			MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2313 		} else if (cd.box[cd.pointBox].boxType == ROTATE) {
2314 			if (g_bNoLanguage)
2315 				return;
2316 
2317 			pfilm = (const FILM *)LockMem(g_hWinParts);
2318 
2319 			rotateIndex = cd.box[cd.pointBox].bi;
2320 			if (rotateIndex == IX2_LEFT1) {
2321 				g_iconArray[HL1] = AddObject(&pfilm->reels[IX2_LEFT2], -1 );
2322 				MultiSetAniXY(g_iconArray[HL1],
2323 					g_InvD[g_ino].inventoryX + cd.box[cd.pointBox].xpos - ROTX1,
2324 					g_InvD[g_ino].inventoryY + cd.box[cd.pointBox].ypos);
2325 				MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2326 			} else if (rotateIndex == IX2_RIGHT1) {
2327 				g_iconArray[HL1] = AddObject(&pfilm->reels[IX2_RIGHT2], -1);
2328 				MultiSetAniXY(g_iconArray[HL1],
2329 					g_InvD[g_ino].inventoryX + cd.box[cd.pointBox].xpos + ROTX1,
2330 					g_InvD[g_ino].inventoryY + cd.box[cd.pointBox].ypos);
2331 				MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS + 1);
2332 			}
2333 		}
2334 	}
2335 }
2336 
ButtonPress(CORO_PARAM,CONFBOX * box)2337 static void ButtonPress(CORO_PARAM, CONFBOX *box) {
2338 	CORO_BEGIN_CONTEXT;
2339 	CORO_END_CONTEXT(_ctx);
2340 
2341 	CORO_BEGIN_CODE(_ctx);
2342 
2343 	const FILM *pfilm;
2344 
2345 	assert(box->boxType == AAGBUT || box->boxType == ARSGBUT);
2346 
2347 	// Replace highlight image with normal image
2348 	pfilm = (const FILM *)LockMem(g_hWinParts);
2349 	if (g_iconArray[HL1] != NULL)
2350 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2351 	pfilm = (const FILM *)LockMem(g_hWinParts);
2352 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+NORMGRAPH], -1);
2353 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2354 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2355 
2356 	// Hold normal image for 1 frame
2357 	CORO_SLEEP(1);
2358 	if (g_iconArray[HL1] == NULL)
2359 		return;
2360 
2361 	// Replace normal image with depresses image
2362 	pfilm = (const FILM *)LockMem(g_hWinParts);
2363 	MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2364 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
2365 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2366 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2367 
2368 	// Hold depressed image for 2 frames
2369 	CORO_SLEEP(2);
2370 	if (g_iconArray[HL1] == NULL)
2371 		return;
2372 
2373 	// Replace depressed image with normal image
2374 	pfilm = (const FILM *)LockMem(g_hWinParts);
2375 	MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2376 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+NORMGRAPH], -1);
2377 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2378 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2379 
2380 	CORO_SLEEP(1);
2381 
2382 	CORO_END_CODE;
2383 }
2384 
ButtonToggle(CORO_PARAM,CONFBOX * box)2385 static void ButtonToggle(CORO_PARAM, CONFBOX *box) {
2386 	CORO_BEGIN_CONTEXT;
2387 	CORO_END_CONTEXT(_ctx);
2388 
2389 	CORO_BEGIN_CODE(_ctx);
2390 
2391 	const FILM *pfilm;
2392 
2393 	assert((box->boxType == TOGGLE) || (box->boxType == TOGGLE1)
2394 		|| (box->boxType == TOGGLE2));
2395 
2396 	// Remove hilight image
2397 	if (g_iconArray[HL1] != NULL) {
2398 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2399 		g_iconArray[HL1] = NULL;
2400 	}
2401 
2402 	// Hold normal image for 1 frame
2403 	CORO_SLEEP(1);
2404 	if (g_InventoryState != ACTIVE_INV)
2405 		return;
2406 
2407 	// Add depressed image
2408 	pfilm = (const FILM *)LockMem(g_hWinParts);
2409 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
2410 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2411 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2412 
2413 	// Hold depressed image for 1 frame
2414 	CORO_SLEEP(1);
2415 	if (g_iconArray[HL1] == NULL)
2416 		return;
2417 
2418 	// Toggle state
2419 	(*box->ival) = *(box->ival) ^ 1;	// XOR with true
2420 	box->bi = *(box->ival) ? IX_TICK1 : IX_CROSS1;
2421 	AddBoxes(false);
2422 	// Keep highlight (e.g. flag)
2423 	if (cd.selBox != NOBOX)
2424 		Select(cd.selBox, true);
2425 
2426 	// New state, depressed image
2427 	pfilm = (const FILM *)LockMem(g_hWinParts);
2428 	if (g_iconArray[HL1] != NULL)
2429 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2430 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+DOWNGRAPH], -1);
2431 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2432 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2433 
2434 	// Hold new depressed image for 1 frame
2435 	CORO_SLEEP(1);
2436 	if (g_iconArray[HL1] == NULL)
2437 		return;
2438 
2439 	// New state, normal
2440 	MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2441 	g_iconArray[HL1] = NULL;
2442 
2443 	// Hold normal image for 1 frame
2444 	CORO_SLEEP(1);
2445 	if (g_InventoryState != ACTIVE_INV)
2446 		return;
2447 
2448 	// New state, highlighted
2449 	pfilm = (const FILM *)LockMem(g_hWinParts);
2450 	if (g_iconArray[HL1] != NULL)
2451 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[HL1]);
2452 	g_iconArray[HL1] = AddObject(&pfilm->reels[box->bi+HIGRAPH], -1);
2453 	MultiSetAniXY(g_iconArray[HL1], g_InvD[g_ino].inventoryX + box->xpos, g_InvD[g_ino].inventoryY + box->ypos);
2454 	MultiSetZPosition(g_iconArray[HL1], Z_INV_ICONS+1);
2455 
2456 	CORO_END_CODE;
2457 }
2458 
2459 /**
2460  * Monitors for POINTED event for inventory icons.
2461  */
InvLabels(bool InBody,int aniX,int aniY)2462 static void InvLabels(bool InBody, int aniX, int aniY) {
2463 	int	index;				// Icon pointed to on this call
2464 	INV_OBJECT *invObj;
2465 
2466 	// Find out which icon is currently pointed to
2467 	if (!InBody)
2468 		index = INV_NOICON;
2469 	else {
2470 		index = InvItem(&aniX, &aniY, false);
2471 		if (index != INV_NOICON) {
2472 			if (index >= g_InvD[g_ino].NoofItems)
2473 				index = INV_NOICON;
2474 			else
2475 				index = g_InvD[g_ino].contents[index];
2476 		}
2477 	}
2478 
2479 	// If no icon pointed to, or points to (logical position of)
2480 	// currently held icon, then no icon is pointed to!
2481 	if (index == INV_NOICON || index == g_heldItem) {
2482 		g_pointedIcon = INV_NOICON;
2483 	} else if (index != g_pointedIcon) {
2484 		// A new icon is pointed to - run its script with POINTED event
2485 		invObj = GetInvObject(index);
2486 		if (invObj->hScript)
2487 			InvTinselEvent(invObj, POINTED, PLR_NOEVENT, index);
2488 		g_pointedIcon = index;
2489 	}
2490 }
2491 
2492 /**************************************************************************/
2493 /***/
2494 /**************************************************************************/
2495 
2496 /**
2497  * All to do with the slider.
2498  * I can't remember how it works - or, indeed, what it does.
2499  * It seems to set up slideStuff[], an array of possible first-displayed
2500  * icons set against the matching y-positions of the slider.
2501  */
AdjustTop()2502 static void AdjustTop() {
2503 	int tMissing, bMissing, nMissing;
2504 	int nsliderYpos;
2505 	int rowsWanted;
2506 	int slideRange;
2507 	int n, i;
2508 
2509 	// Only do this if there's a slider
2510 	if (!g_SlideObject)
2511 		return;
2512 
2513 	rowsWanted = (g_InvD[g_ino].NoofItems - g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons-1) / g_InvD[g_ino].NoofHicons;
2514 
2515 	while (rowsWanted < g_InvD[g_ino].NoofVicons) {
2516 		if (g_InvD[g_ino].FirstDisp) {
2517 			g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
2518 			if (g_InvD[g_ino].FirstDisp < 0)
2519 				g_InvD[g_ino].FirstDisp = 0;
2520 			rowsWanted++;
2521 		} else
2522 			break;
2523 	}
2524 	tMissing = g_InvD[g_ino].FirstDisp ? (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons-1)/g_InvD[g_ino].NoofHicons : 0;
2525 	bMissing = (rowsWanted > g_InvD[g_ino].NoofVicons) ? rowsWanted - g_InvD[g_ino].NoofVicons : 0;
2526 
2527 	nMissing = tMissing + bMissing;
2528 	slideRange = g_sliderYmax - g_sliderYmin;
2529 
2530 	if (!tMissing)
2531 		nsliderYpos = g_sliderYmin;
2532 	else if (!bMissing)
2533 		nsliderYpos = g_sliderYmax;
2534 	else {
2535 		nsliderYpos = tMissing*slideRange/nMissing;
2536 		nsliderYpos += g_sliderYmin;
2537 	}
2538 
2539 	if (nMissing) {
2540 		n = g_InvD[g_ino].FirstDisp - tMissing*g_InvD[g_ino].NoofHicons;
2541 		for (i = 0; i <= nMissing; i++, n += g_InvD[g_ino].NoofHicons) {
2542 			g_slideStuff[i].n = n;
2543 			g_slideStuff[i].y = (i*slideRange/nMissing) + g_sliderYmin;
2544 		}
2545 		if (g_slideStuff[0].n < 0)
2546 			g_slideStuff[0].n = 0;
2547 		assert(i < MAX_ININV + 1);
2548 		g_slideStuff[i].n = -1;
2549 	} else {
2550 		g_slideStuff[0].n = 0;
2551 		g_slideStuff[0].y = g_sliderYmin;
2552 		g_slideStuff[1].n = -1;
2553 	}
2554 
2555 	if (nsliderYpos != g_sliderYpos) {
2556 		MultiMoveRelXY(g_SlideObject, 0, nsliderYpos - g_sliderYpos);
2557 		g_sliderYpos = nsliderYpos;
2558 	}
2559 }
2560 
2561 /**
2562  * Insert an inventory icon object onto the display list.
2563  */
AddInvObject(int num,const FREEL ** pfreel,const FILM ** pfilm)2564 static OBJECT *AddInvObject(int num, const FREEL **pfreel, const FILM **pfilm) {
2565 	INV_OBJECT *invObj;		// Icon data
2566 	const MULTI_INIT *pmi;		// Its INIT structure - from the reel
2567 	IMAGE *pim;		// ... you get the picture
2568 	OBJECT *pPlayObj;	// The object we insert
2569 
2570 	invObj = GetInvObject(num);
2571 
2572 	// Get pointer to image
2573 	pim = GetImageFromFilm(invObj->hIconFilm, 0, pfreel, &pmi, pfilm);
2574 
2575 	// Poke in the background palette
2576 	pim->hImgPal = TO_32(BgPal());
2577 
2578 	// Set up the multi-object
2579 	pPlayObj = MultiInitObject(pmi);
2580 	MultiInsertObject(GetPlayfieldList(FIELD_STATUS), pPlayObj);
2581 
2582 	return pPlayObj;
2583 }
2584 
2585 /**
2586  * Create display objects for the displayed icons in an inventory window.
2587  */
FillInInventory()2588 static void FillInInventory() {
2589 	int	Index;		// Index into contents[]
2590 	int	n = 0;		// index into iconArray[]
2591 	int	xpos, ypos;
2592 	int	row, col;
2593 	const FREEL *pfr;
2594 	const FILM *pfilm;
2595 
2596 	DumpIconArray();
2597 
2598 	if (g_InvDragging != ID_SLIDE)
2599 		AdjustTop();		// Set up slideStuff[]
2600 
2601 	Index = g_InvD[g_ino].FirstDisp;	// Start from first displayed object
2602 	n = 0;
2603 	ypos = START_ICONY;		// Y-offset of first display row
2604 
2605 	for (row = 0; row < g_InvD[g_ino].NoofVicons; row++,	ypos += ITEM_HEIGHT + 1) {
2606 		xpos = START_ICONX;		// X-offset of first display column
2607 
2608 		for (col = 0; col < g_InvD[g_ino].NoofHicons; col++) {
2609 			if (Index >= g_InvD[g_ino].NoofItems)
2610 				break;
2611 			else if (g_InvD[g_ino].contents[Index] != g_heldItem) {
2612 				// Create a display object and position it
2613 				g_iconArray[n] = AddInvObject(g_InvD[g_ino].contents[Index], &pfr, &pfilm);
2614 				MultiSetAniXY(g_iconArray[n], g_InvD[g_ino].inventoryX + xpos , g_InvD[g_ino].inventoryY + ypos);
2615 				MultiSetZPosition(g_iconArray[n], Z_INV_ICONS);
2616 
2617 				InitStepAnimScript(&g_iconAnims[n], g_iconArray[n], FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
2618 
2619 				n++;
2620 			}
2621 			Index++;
2622 			xpos += ITEM_WIDTH + 1;	// X-offset of next display column
2623 		}
2624 	}
2625 }
2626 
2627 enum {FROM_HANDLE, FROM_STRING};
2628 
2629 /**
2630  * Set up a rectangle as the background to the inventory window.
2631  *  Additionally, sticks the window title up.
2632  */
AddBackground(OBJECT ** rect,OBJECT ** title,int extraH,int extraV,int textFrom)2633 static void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV, int textFrom) {
2634 	// Why not 2 ????
2635 	int width = g_TLwidth + extraH + g_TRwidth + NM_BG_SIZ_X;
2636 	int height = g_TLheight + extraV + g_BLheight + NM_BG_SIZ_Y;
2637 
2638 	// Create a rectangle object
2639 	g_RectObject = *rect = TranslucentObject(width, height);
2640 
2641 	// add it to display list and position it
2642 	MultiInsertObject(GetPlayfieldList(FIELD_STATUS), *rect);
2643 	MultiSetAniXY(*rect, g_InvD[g_ino].inventoryX + NM_BG_POS_X,
2644 		g_InvD[g_ino].inventoryY + NM_BG_POS_Y);
2645 	MultiSetZPosition(*rect, Z_INV_BRECT);
2646 
2647 	if (title == NULL)
2648 		return;
2649 
2650 	// Create text object using title string
2651 	if (textFrom == FROM_HANDLE) {
2652 		LoadStringRes(g_InvD[g_ino].hInvTitle, TextBufferAddr(), TBUFSZ);
2653 		*title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
2654 					g_InvD[g_ino].inventoryX + width/2, g_InvD[g_ino].inventoryY + M_TOFF,
2655 					GetTagFontHandle(), TXT_CENTER);
2656 		assert(*title); // Inventory title string produced NULL text
2657 		MultiSetZPosition(*title, Z_INV_HTEXT);
2658 	} else if (textFrom == FROM_STRING && cd.ixHeading != NO_HEADING) {
2659 		LoadStringRes(g_configStrings[cd.ixHeading], TextBufferAddr(), TBUFSZ);
2660 		*title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
2661 					g_InvD[g_ino].inventoryX + width/2, g_InvD[g_ino].inventoryY + M_TOFF,
2662 					GetTagFontHandle(), TXT_CENTER);
2663 		assert(*title); // Inventory title string produced NULL text
2664 		MultiSetZPosition(*title, Z_INV_HTEXT);
2665 	}
2666 }
2667 
2668 /**
2669  * Set up a rectangle as the background to the inventory window.
2670  */
AddBackground(OBJECT ** rect,int extraH,int extraV)2671 static void AddBackground(OBJECT **rect, int extraH, int extraV) {
2672 	AddBackground(rect, NULL, extraH, extraV, 0);
2673 }
2674 
2675 /**
2676  * Adds a title for a dialog
2677  */
AddTitle(POBJECT * title,int extraH)2678 static void AddTitle(POBJECT *title, int extraH) {
2679 	int width = g_TLwidth + extraH + g_TRwidth + NM_BG_SIZ_X;
2680 
2681 	// Create text object using title string
2682 	if (g_InvD[g_ino].hInvTitle != (SCNHANDLE)NO_HEADING) {
2683 		LoadStringRes(g_InvD[g_ino].hInvTitle, TextBufferAddr(), TBUFSZ);
2684 		*title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
2685 					g_InvD[g_ino].inventoryX + (width/2)+NM_BG_POS_X, g_InvD[g_ino].inventoryY + NM_TOFF,
2686 					GetTagFontHandle(), TXT_CENTER, 0);
2687 		assert(*title);
2688 		MultiSetZPosition(*title, Z_INV_HTEXT);
2689 	}
2690 }
2691 
2692 
2693 /**
2694  * Insert a part of the inventory window frame onto the display list.
2695  */
AddObject(const FREEL * pfreel,int num)2696 static OBJECT *AddObject(const FREEL *pfreel, int num) {
2697 	const MULTI_INIT *pmi;	// Get the MULTI_INIT structure
2698 	IMAGE *pim;
2699 	OBJECT *pPlayObj;
2700 
2701 	// Get pointer to image
2702 	pim = GetImageFromReel(pfreel, &pmi);
2703 
2704 	// Poke in the background palette
2705 	pim->hImgPal = TO_32(BgPal());
2706 
2707 	// Horrible bodge involving global variables to save
2708 	// width and/or height of some window frame components
2709 	if (num == g_TL) {
2710 		g_TLwidth = FROM_16(pim->imgWidth);
2711 		g_TLheight = FROM_16(pim->imgHeight) & ~C16_FLAG_MASK;
2712 	} else if (num == g_TR) {
2713 		g_TRwidth = FROM_16(pim->imgWidth);
2714 	} else if (num == g_BL) {
2715 		g_BLheight = FROM_16(pim->imgHeight) & ~C16_FLAG_MASK;
2716 	}
2717 
2718 	// Set up and insert the multi-object
2719 	pPlayObj = MultiInitObject(pmi);
2720 	MultiInsertObject(GetPlayfieldList(FIELD_STATUS), pPlayObj);
2721 
2722 	return pPlayObj;
2723 }
2724 
2725 /**
2726  * Display the scroll bar slider.
2727  */
2728 
AddSlider(OBJECT ** slide,const FILM * pfilm)2729 static void AddSlider(OBJECT **slide, const FILM *pfilm) {
2730 	g_SlideObject = *slide = AddObject(&pfilm->reels[IX_SLIDE], -1);
2731 	MultiSetAniXY(*slide, MultiRightmost(g_RectObject) + (TinselV2 ? NM_SLX : -M_SXOFF + 2),
2732 		g_InvD[g_ino].inventoryY + g_sliderYpos);
2733 	MultiSetZPosition(*slide, Z_INV_MFRAME);
2734 }
2735 
2736 /**
2737  * Display a box with some text in it.
2738  */
AddBox(int * pi,const int i)2739 static void AddBox(int *pi, const int i) {
2740 	int x	= g_InvD[g_ino].inventoryX + cd.box[i].xpos;
2741 	int y	= g_InvD[g_ino].inventoryY + cd.box[i].ypos;
2742 	int *pival = cd.box[i].ival;
2743 	int	xdisp;
2744 	const FILM *pFilm;
2745 
2746 	switch (cd.box[i].boxType) {
2747 	default:
2748 		// Ignore if it's a blank scene hopper box
2749 		if (TinselV2 && (cd.box[i].textMethod == TM_NONE))
2750 			break;
2751 
2752 		// Give us a box
2753 		g_iconArray[*pi] = RectangleObject(BgPal(), TinselV2 ? BoxColor() : COL_BOX,
2754 			cd.box[i].w, cd.box[i].h);
2755 		MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_iconArray[*pi]);
2756 		MultiSetAniXY(g_iconArray[*pi], x, y);
2757 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT + 1);
2758 		*pi += 1;
2759 
2760 		// Stick in the text
2761 		if ((cd.box[i].textMethod == TM_POINTER) ||
2762 				(!TinselV2 && (cd.box[i].ixText == USE_POINTER))) {
2763 			if (cd.box[i].boxText != NULL) {
2764 				if (cd.box[i].boxType == RGROUP) {
2765 					g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
2766 #ifdef JAPAN
2767 							x + 2, y+2, GetTagFontHandle(), 0);
2768 #else
2769 							x + 2, y + TYOFF, GetTagFontHandle(), 0);
2770 #endif
2771 				} else {
2772 					g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
2773 #ifdef JAPAN
2774 // Note: it never seems to go here!
2775 							x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTER);
2776 #else
2777 							x + cd.box[i].w / 2, y + TYOFF, GetTagFontHandle(), TXT_CENTER);
2778 #endif
2779 				}
2780 
2781 				MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2782 				*pi += 1;
2783 			}
2784 		} else {
2785 			if (TinselV2) {
2786 				if (cd.box[i].textMethod == TM_INDEX)
2787 					LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
2788 				else {
2789 					assert(cd.box[i].textMethod == TM_STRINGNUM);
2790 					LoadStringRes(cd.box[i].ixText, TextBufferAddr(), TBUFSZ);
2791 				}
2792 			} else {
2793 				LoadStringRes(g_configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
2794 				assert(cd.box[i].boxType != RGROUP); // You'll need to add some code!
2795 			}
2796 
2797 			if (TinselV2 && (cd.box[i].boxType == RGROUP))
2798 				g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
2799 						0, x + 2, y + TYOFF, GetTagFontHandle(), 0, 0);
2800 			else
2801 				g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2802 					TextBufferAddr(), 0,
2803 #ifdef JAPAN
2804 					x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTER);
2805 #else
2806 					x + cd.box[i].w / 2, y + TYOFF, GetTagFontHandle(), TXT_CENTER);
2807 #endif
2808 			MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2809 			*pi += 1;
2810 		}
2811 		break;
2812 
2813 	case AAGBUT:
2814 	case ARSGBUT:
2815 		pFilm = (const FILM *)LockMem(g_hWinParts);
2816 
2817 		g_iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi + NORMGRAPH], -1);
2818 		MultiSetAniXY(g_iconArray[*pi], x, y);
2819 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT + 1);
2820 		*pi += 1;
2821 
2822 		break;
2823 
2824 	case FRGROUP:
2825 		assert(g_flagFilm != 0); // Language flags not declared!
2826 
2827 		pFilm = (const FILM *)LockMem(g_flagFilm);
2828 
2829 		if (_vm->_config->_isAmericanEnglishVersion && cd.box[i].bi == FIX_UK)
2830 			cd.box[i].bi = FIX_USA;
2831 
2832 		g_iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi], -1);
2833 		MultiSetAniXY(g_iconArray[*pi], x, y);
2834 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT+2);
2835 		*pi += 1;
2836 
2837 		break;
2838 
2839 	case FLIP:
2840 		pFilm = (const FILM *)LockMem(g_hWinParts);
2841 
2842 		if (*pival)
2843 			g_iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi], -1);
2844 		else
2845 			g_iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi+1], -1);
2846 		MultiSetAniXY(g_iconArray[*pi], x, y);
2847 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT+1);
2848 		*pi += 1;
2849 
2850 		// Stick in the text
2851 		if (TinselV2) {
2852 			assert(cd.box[i].textMethod == TM_INDEX);
2853 			LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
2854 		} else {
2855 			assert(cd.box[i].ixText != USE_POINTER);
2856 			LoadStringRes(g_configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
2857 		}
2858 		g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2859 			TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
2860 		MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2861 		*pi += 1;
2862 		break;
2863 
2864 	case TOGGLE:
2865 	case TOGGLE1:
2866 	case TOGGLE2:
2867 		pFilm = (const FILM *)LockMem(g_hWinParts);
2868 
2869 		cd.box[i].bi = *pival ? IX_TICK1 : IX_CROSS1;
2870 		g_iconArray[*pi] = AddObject(&pFilm->reels[cd.box[i].bi + NORMGRAPH], -1);
2871 		MultiSetAniXY(g_iconArray[*pi], x, y);
2872 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT+1);
2873 		*pi += 1;
2874 
2875 		// Stick in the text
2876 		if (TinselV2) {
2877 			assert(cd.box[i].textMethod == TM_INDEX);
2878 			LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
2879 		} else {
2880 			assert(cd.box[i].ixText != USE_POINTER);
2881 			LoadStringRes(g_configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
2882 		}
2883 
2884 		if (cd.box[i].boxType == TOGGLE2) {
2885 			g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2886 				TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
2887 				GetTagFontHandle(), TXT_CENTER, 0);
2888 		} else {
2889 			g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2890 				TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF,
2891 				GetTagFontHandle(), TXT_RIGHT, 0);
2892 		}
2893 
2894 		MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2895 		*pi += 1;
2896 		break;
2897 
2898 	case SLIDER:
2899 		pFilm = (const FILM *)LockMem(g_hWinParts);
2900 		xdisp = SLIDE_RANGE*(*pival)/cd.box[i].w;
2901 
2902 		g_iconArray[*pi] = AddObject(&pFilm->reels[IX_MDGROOVE], -1);
2903 		MultiSetAniXY(g_iconArray[*pi], x, y);
2904 		MultiSetZPosition(g_iconArray[*pi], Z_MDGROOVE);
2905 		*pi += 1;
2906 		g_iconArray[*pi] = AddObject(&pFilm->reels[IX_MDSLIDER], -1);
2907 		MultiSetAniXY(g_iconArray[*pi], x+SLIDE_MINX+xdisp, y);
2908 		MultiSetZPosition(g_iconArray[*pi], Z_MDSLIDER);
2909 		assert(g_numMdSlides < MAXSLIDES);
2910 		g_mdSlides[g_numMdSlides].num = i;
2911 		g_mdSlides[g_numMdSlides].min = x + SLIDE_MINX;
2912 		g_mdSlides[g_numMdSlides].max = x + SLIDE_MAXX;
2913 		g_mdSlides[g_numMdSlides++].obj = g_iconArray[*pi];
2914 		*pi += 1;
2915 
2916 		// Stick in the text
2917 		if (TinselV2) {
2918 			assert(cd.box[i].textMethod == TM_INDEX);
2919 			LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
2920 		} else {
2921 			assert(cd.box[i].ixText != USE_POINTER);
2922 			LoadStringRes(g_configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
2923 		}
2924 		g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2925 			TextBufferAddr(), 0, x+MDTEXT_XOFF, y+MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
2926 		MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2927 		*pi += 1;
2928 		break;
2929 
2930 	case ROTATE:
2931 		pFilm = (const FILM *)LockMem(g_hWinParts);
2932 
2933 		// Left one
2934 		if (!g_bNoLanguage) {
2935 			g_iconArray[*pi] = AddObject(&pFilm->reels[IX2_LEFT1], -1);
2936 			MultiSetAniXY(g_iconArray[*pi], x-ROTX1, y);
2937 			MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT + 1);
2938 			*pi += 1;
2939 
2940 			// Right one
2941 			g_iconArray[*pi] = AddObject( &pFilm->reels[IX2_RIGHT1], -1);
2942 			MultiSetAniXY(g_iconArray[*pi], x + ROTX1, y);
2943 			MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT + 1);
2944 			*pi += 1;
2945 
2946 			// Stick in the text
2947 			assert(cd.box[i].textMethod == TM_INDEX);
2948 			LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
2949 			g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2950 				TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
2951 				GetTagFontHandle(), TXT_CENTER, 0);
2952 			MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2953 			*pi += 1;
2954 		}
2955 
2956 		// Current language's text
2957 		if (LanguageDesc(g_displayedLanguage) == 0)
2958 			break;
2959 
2960 		LoadStringRes(LanguageDesc(g_displayedLanguage), TextBufferAddr(), TBUFSZ);
2961 		g_iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
2962 				x + cd.box[i].w / 2, y + ROT_YOFF, GetTagFontHandle(), TXT_CENTER, 0);
2963 		MultiSetZPosition(g_iconArray[*pi], Z_INV_ITEXT);
2964 		*pi += 1;
2965 
2966 		// Current language's flag
2967 		pFilm = (const FILM *)LockMem(LanguageFlag(g_displayedLanguage));
2968 		g_iconArray[*pi] = AddObject(&pFilm->reels[0], -1);
2969 		MultiSetAniXY(g_iconArray[*pi], x + FLAGX, y + FLAGY);
2970 		MultiSetZPosition(g_iconArray[*pi], Z_INV_BRECT + 1);
2971 		*pi += 1;
2972 		break;
2973 	}
2974 }
2975 
2976 /**
2977  * Display some boxes.
2978  */
AddBoxes(bool bPosnSlide)2979 static void AddBoxes(bool bPosnSlide) {
2980 	int	objCount = NUMHL;	// Object count - allow for HL1, HL2 etc.
2981 
2982 	DumpIconArray();
2983 	g_numMdSlides = 0;
2984 
2985 	for (int i = 0; i < cd.NumBoxes; i++) {
2986 		AddBox(&objCount, i);
2987 	}
2988 
2989 	if (cd.bExtraWin) {
2990 		if (bPosnSlide && !TinselV2)
2991 			g_sliderYpos = g_sliderYmin + (cd.extraBase*(g_sliderYmax-g_sliderYmin))/(MAX_SAVED_FILES-NUM_RGROUP_BOXES);
2992 		else if (bPosnSlide) {
2993 			// Tinsel 2 bPosnSlide code
2994 			int lastY = g_sliderYpos;
2995 
2996 			if (cd.box == loadBox || cd.box == saveBox)
2997 				g_sliderYpos = g_sliderYmin + (cd.extraBase * (sliderRange)) /
2998 				(MAX_SAVED_FILES - NUM_RGROUP_BOXES);
2999 			else if (cd.box == hopperBox1) {
3000 				if (g_numScenes <= NUM_RGROUP_BOXES)
3001 					g_sliderYpos = g_sliderYmin;
3002 				else
3003 					g_sliderYpos = g_sliderYmin + (cd.extraBase*(sliderRange))/(g_numScenes-NUM_RGROUP_BOXES);
3004 			} else if (cd.box == hopperBox2) {
3005 				if (g_numEntries <= NUM_RGROUP_BOXES)
3006 					g_sliderYpos = g_sliderYmin;
3007 				else
3008 					g_sliderYpos = g_sliderYmin + (cd.extraBase * (sliderRange)) /
3009 					(g_numEntries-NUM_RGROUP_BOXES);
3010 			}
3011 
3012 			MultiMoveRelXY(g_SlideObject, 0, g_sliderYpos - lastY);
3013 		}
3014 
3015 		if (!TinselV2)
3016 			MultiSetAniXY(g_SlideObject, g_InvD[g_ino].inventoryX + 24 + 179, g_sliderYpos);
3017 	}
3018 
3019 	assert(objCount < MAX_ICONS); // added too many icons
3020 }
3021 
3022 /**
3023  * Display the scroll bar slider.
3024  */
AddEWSlider(OBJECT ** slide,const FILM * pfilm)3025 static void AddEWSlider(OBJECT **slide, const FILM *pfilm) {
3026 	g_SlideObject = *slide = AddObject(&pfilm->reels[IX_SLIDE], -1);
3027 	MultiSetAniXY(*slide, g_InvD[g_ino].inventoryX + 24 + 127, g_sliderYpos);
3028 	MultiSetZPosition(*slide, Z_INV_MFRAME);
3029 }
3030 
3031 /**
3032  * AddExtraWindow
3033  */
AddExtraWindow(int x,int y,OBJECT ** retObj)3034 static int AddExtraWindow(int x, int y, OBJECT **retObj) {
3035 	int	n = 0;
3036 	const FILM *pfilm;
3037 
3038 	// Get the frame's data
3039 	pfilm = (const FILM *)LockMem(g_hWinParts);
3040 
3041 	x += TinselV2 ? 30 : 20;
3042 	y += TinselV2 ? 38 : 24;
3043 
3044 	// Draw the four corners
3045 	retObj[n] = AddObject(&pfilm->reels[IX_RTL], -1);	// Top left
3046 	MultiSetAniXY(retObj[n], x, y);
3047 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3048 	n++;
3049 	retObj[n] = AddObject(&pfilm->reels[IX_NTR], -1);	// Top right
3050 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth + 312 : 152), y);
3051 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3052 	n++;
3053 	retObj[n] = AddObject(&pfilm->reels[IX_BL], -1);	// Bottom left
3054 	MultiSetAniXY(retObj[n], x, y + (TinselV2 ? g_TLheight + 208 : 124));
3055 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3056 	n++;
3057 	retObj[n] = AddObject(&pfilm->reels[IX_BR], -1);	// Bottom right
3058 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth + 312 : 152),
3059 		y + (TinselV2 ? g_TLheight + 208 : 124));
3060 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3061 	n++;
3062 
3063 	// Draw the edges
3064 	retObj[n] = AddObject(&pfilm->reels[IX_H156], -1);	// Top
3065 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth : 6), y + NM_TBT);
3066 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3067 	n++;
3068 	retObj[n] = AddObject(&pfilm->reels[IX_H156], -1);	// Bottom
3069 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth : 6), y +
3070 		(TinselV2 ? g_TLheight + 208 + g_BLheight + NM_BSY : 143));
3071 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3072 	n++;
3073 	retObj[n] = AddObject(&pfilm->reels[IX_V104], -1);	// Left
3074 	MultiSetAniXY(retObj[n], x + NM_LSX, y + (TinselV2 ? g_TLheight : 20));
3075 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3076 	n++;
3077 	retObj[n] = AddObject(&pfilm->reels[IX_V104], -1);	// Right 1
3078 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth + 312 + g_TRwidth + NM_RSX : 179),
3079 		y + (TinselV2 ? g_TLheight : 20));
3080 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3081 	n++;
3082 	retObj[n] = AddObject(&pfilm->reels[IX_V104], -1);	// Right 2
3083 	MultiSetAniXY(retObj[n], x + (TinselV2 ? g_TLwidth + 312 + g_TRwidth + NM_SBL : 188),
3084 		y + (TinselV2 ? g_TLheight : 20));
3085 	MultiSetZPosition(retObj[n], Z_INV_MFRAME);
3086 	n++;
3087 
3088 	if (TinselV2) {
3089 		g_sliderYpos = g_sliderYmin = y + 27;
3090 		g_sliderYmax = y + 273;
3091 
3092 		retObj[n++] = g_SlideObject = AddObject( &pfilm->reels[IX_SLIDE], -1);
3093 		MultiSetAniXY(g_SlideObject,
3094 			x + g_TLwidth + 320 + g_TRwidth - NM_BG_POS_X + NM_BG_SIZ_X - 2,
3095 			g_sliderYpos);
3096 		MultiSetZPosition(g_SlideObject, Z_INV_MFRAME);
3097 	} else {
3098 		g_sliderYpos = g_sliderYmin = y + 9;
3099 		g_sliderYmax = y + 134;
3100 		AddEWSlider(&retObj[n++], pfilm);
3101 	}
3102 
3103 	return n;
3104 }
3105 
3106 
3107 enum InventoryType { EMPTY, FULL, CONF };
3108 
3109 /**
3110  * Construct an inventory window - either a standard one, with
3111  * background, slider and icons, or a re-sizing window.
3112  */
ConstructInventory(InventoryType filling)3113 static void ConstructInventory(InventoryType filling) {
3114 	int	eH, eV;		// Extra width and height
3115 	int	n = 0;		// Index into object array
3116 	int	zpos;		// Z-position of frame
3117 	int	invX = g_InvD[g_ino].inventoryX;
3118 	int	invY = g_InvD[g_ino].inventoryY;
3119 	OBJECT **retObj;
3120 	const FILM *pfilm;
3121 
3122 	// Select the object array to use
3123 	if (filling == FULL || filling == CONF) {
3124 		retObj = g_objArray;		// Standard window
3125 		zpos = Z_INV_MFRAME;
3126 	} else {
3127 		retObj = g_DobjArray;		// Re-sizing window
3128 		zpos = Z_INV_RFRAME;
3129 	}
3130 
3131 	// Dispose of anything it may be replacing
3132 	for (int i = 0; i < MAX_WCOMP; i++) {
3133 		if (retObj[i] != NULL) {
3134 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), retObj[i]);
3135 			retObj[i] = NULL;
3136 		}
3137 	}
3138 
3139 	// Get the frame's data
3140 	pfilm = (const FILM *)LockMem(g_hWinParts);
3141 
3142 	// Standard window is of granular dimensions
3143 	if (filling == FULL) {
3144 		// Round-up/down to nearest number of icons
3145 		if (g_SuppH > ITEM_WIDTH / 2)
3146 			g_InvD[g_ino].NoofHicons++;
3147 		if (g_SuppV > ITEM_HEIGHT / 2)
3148 			g_InvD[g_ino].NoofVicons++;
3149 		g_SuppH = g_SuppV = 0;
3150 	}
3151 
3152 	// Extra width and height
3153 	eH = (g_InvD[g_ino].NoofHicons - 1) * (ITEM_WIDTH+I_SEPARATION) + g_SuppH;
3154 	eV = (g_InvD[g_ino].NoofVicons - 1) * (ITEM_HEIGHT+I_SEPARATION) + g_SuppV;
3155 
3156 	// Which window frame corners to use
3157 	if (TinselV2 && (g_ino == INV_CONV)) {
3158 		g_TL = IX_TL;
3159 		g_TR = IX2_TR4;
3160 		g_BL = IX_BL;
3161 		g_BR = IX_RBR;
3162 	} else if ((filling == FULL) && (g_ino != INV_CONV)) {
3163 		g_TL = IX_TL;
3164 		g_TR = IX_TR;
3165 		g_BL = IX_BL;
3166 		g_BR = IX_BR;
3167 	} else {
3168 		g_TL = IX_RTL;
3169 		g_TR = IX_RTR;
3170 		g_BL = IX_BL;
3171 		g_BR = IX_RBR;
3172 	}
3173 
3174 	// Draw the four corners
3175 	retObj[n] = AddObject(&pfilm->reels[g_TL], g_TL);
3176 	MultiSetAniXY(retObj[n], invX, invY);
3177 	MultiSetZPosition(retObj[n], zpos);
3178 	n++;
3179 	retObj[n] = AddObject(&pfilm->reels[g_TR], g_TR);
3180 	MultiSetAniXY(retObj[n], invX + g_TLwidth + eH, invY);
3181 	MultiSetZPosition(retObj[n], zpos);
3182 	n++;
3183 	retObj[n] = AddObject(&pfilm->reels[g_BL], g_BL);
3184 	MultiSetAniXY(retObj[n], invX, invY + g_TLheight + eV);
3185 	MultiSetZPosition(retObj[n], zpos);
3186 	n++;
3187 	retObj[n] = AddObject(&pfilm->reels[g_BR], g_BR);
3188 	MultiSetAniXY(retObj[n], invX + g_TLwidth + eH, invY + g_TLheight + eV);
3189 	MultiSetZPosition(retObj[n], zpos);
3190 	n++;
3191 
3192 	// Draw extra Top and bottom parts
3193 	if (g_InvD[g_ino].NoofHicons > 1) {
3194 		// Top side
3195 		retObj[n] = AddObject(&pfilm->reels[hFillers[g_InvD[g_ino].NoofHicons-2]], -1);
3196 		MultiSetAniXY(retObj[n], invX + g_TLwidth, invY + NM_TBT);
3197 		MultiSetZPosition(retObj[n], zpos);
3198 		n++;
3199 
3200 		// Bottom of header box
3201 		if (filling == FULL) {
3202 			if (TinselV2) {
3203 				retObj[n] = AddObject(&pfilm->reels[hFillers[g_InvD[g_ino].NoofHicons-2]], -1);
3204 				MultiSetAniXY(retObj[n], invX + g_TLwidth, invY + NM_TBB);
3205 				MultiSetZPosition(retObj[n], zpos);
3206 				n++;
3207 			} else {
3208 				retObj[n] = AddObject(&pfilm->reels[hFillers[g_InvD[g_ino].NoofHicons-2]], -1);
3209 				MultiSetAniXY(retObj[n], invX + g_TLwidth, invY + M_TBB + 1);
3210 				MultiSetZPosition(retObj[n], zpos);
3211 				n++;
3212 
3213 				// Extra bits for conversation - hopefully temporary
3214 				if (g_ino == INV_CONV) {
3215 					retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
3216 					MultiSetAniXY(retObj[n], invX + g_TLwidth - 2, invY + M_TBB + 1);
3217 					MultiSetZPosition(retObj[n], zpos);
3218 					n++;
3219 
3220 					retObj[n] = AddObject(&pfilm->reels[IX_H52], -1);
3221 					MultiSetAniXY(retObj[n], invX + eH - 10, invY + M_TBB + 1);
3222 					MultiSetZPosition(retObj[n], zpos);
3223 					n++;
3224 				}
3225 			}
3226 		}
3227 
3228 		// Bottom side
3229 		retObj[n] = AddObject(&pfilm->reels[hFillers[g_InvD[g_ino].NoofHicons-2]], -1);
3230 		MultiSetAniXY(retObj[n], invX + g_TLwidth, invY + g_TLheight + eV + g_BLheight + NM_BSY);
3231 
3232 		MultiSetZPosition(retObj[n], zpos);
3233 		n++;
3234 	}
3235 	if (g_SuppH) {
3236 		int offx = g_TLwidth + eH - (TinselV2 ? ITEM_WIDTH + I_SEPARATION : 26);
3237 		if (offx < g_TLwidth)	// Not too far!
3238 			offx = g_TLwidth;
3239 
3240 		// Top side extra
3241 		retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
3242 		MultiSetAniXY(retObj[n], invX + offx, invY + NM_TBT);
3243 		MultiSetZPosition(retObj[n], zpos);
3244 		n++;
3245 
3246 		// Bottom side extra
3247 		retObj[n] = AddObject(&pfilm->reels[IX_H26], -1);
3248 		MultiSetAniXY(retObj[n], invX + offx, invY + g_TLheight + eV + g_BLheight + NM_BSY);
3249 
3250 		MultiSetZPosition(retObj[n], zpos);
3251 		n++;
3252 	}
3253 
3254 	// Draw extra side parts
3255 	if (g_InvD[g_ino].NoofVicons > 1) {
3256 		// Left side
3257 		retObj[n] = AddObject(&pfilm->reels[vFillers[g_InvD[g_ino].NoofVicons-2]], -1);
3258 		MultiSetAniXY(retObj[n], invX + NM_LSX, invY + g_TLheight);
3259 		MultiSetZPosition(retObj[n], zpos);
3260 		n++;
3261 
3262 		// Left side of scroll bar
3263 		if (filling == FULL && g_ino != INV_CONV) {
3264 			retObj[n] = AddObject(&pfilm->reels[vFillers[g_InvD[g_ino].NoofVicons-2]], -1);
3265 			if (TinselV2)
3266 				MultiSetAniXY(retObj[n], invX + g_TLwidth + eH + g_TRwidth + NM_SBL, invY + g_TLheight);
3267 			else
3268 				MultiSetAniXY(retObj[n], invX + g_TLwidth + eH + M_SBL + 1, invY + g_TLheight);
3269 			MultiSetZPosition(retObj[n], zpos);
3270 			n++;
3271 		}
3272 
3273 		// Right side
3274 		retObj[n] = AddObject(&pfilm->reels[vFillers[g_InvD[g_ino].NoofVicons-2]], -1);
3275 		MultiSetAniXY(retObj[n], invX + g_TLwidth + eH + g_TRwidth + NM_RSX, invY + g_TLheight);
3276 		MultiSetZPosition(retObj[n], zpos);
3277 		n++;
3278 	}
3279 	if (g_SuppV) {
3280 		int offy = g_TLheight + eV - (TinselV2 ? ITEM_HEIGHT + I_SEPARATION : 26);
3281 		int minAmount = TinselV2 ? 20 : 5;
3282 		if (offy < minAmount)
3283 			offy = minAmount;
3284 
3285 		// Left side extra
3286 		retObj[n] = AddObject(&pfilm->reels[IX_V26], -1);
3287 		MultiSetAniXY(retObj[n], invX + NM_LSX, invY + offy);
3288 		MultiSetZPosition(retObj[n], zpos);
3289 		n++;
3290 
3291 		// Right side extra
3292 		retObj[n] = AddObject(&pfilm->reels[IX_V26], -1);
3293 		MultiSetAniXY(retObj[n], invX + g_TLwidth + eH + g_TRwidth + NM_RSX, invY + offy);
3294 		MultiSetZPosition(retObj[n], zpos);
3295 		n++;
3296 	}
3297 
3298 	OBJECT **rect, **title;
3299 
3300 	// Draw background, slider and icons
3301 	if (TinselV2 && (filling != EMPTY)) {
3302 		AddBackground(&retObj[n++], eH, eV);
3303 		AddTitle(&retObj[n++], eH);
3304 	}
3305 
3306 	if (filling == FULL) {
3307 		if (!TinselV2) {
3308 			rect = &retObj[n++];
3309 			title = &retObj[n++];
3310 
3311 			AddBackground(rect, title, eH, eV, FROM_HANDLE);
3312 		}
3313 
3314 		if (g_ino == INV_CONV) {
3315 			g_SlideObject = NULL;
3316 
3317 			if (TinselV2) {
3318 				// !!!!! MAGIC NUMBER ALERT !!!!!
3319 				// Make sure it's big enough for the heading
3320 				if (MultiLeftmost(retObj[n-1]) < g_InvD[INV_CONV].inventoryX + 10) {
3321 					g_InvD[INV_CONV].NoofHicons++;
3322 					ConstructInventory(FULL);
3323 				}
3324 			}
3325 		} else if (g_InvD[g_ino].NoofItems > g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons) {
3326 			g_sliderYmin = g_TLheight - (TinselV2 ? 1 : 2);
3327 			g_sliderYmax = g_TLheight + eV + (TinselV2 ? 12 : 10);
3328 			AddSlider(&retObj[n++], pfilm);
3329 		}
3330 
3331 		FillInInventory();
3332 	} else if (filling == CONF) {
3333 		if (!TinselV2) {
3334 			rect = &retObj[n++];
3335 			title = &retObj[n++];
3336 
3337 			AddBackground(rect, title, eH, eV, FROM_STRING);
3338 			if (cd.bExtraWin)
3339 				n += AddExtraWindow(invX, invY, &retObj[n]);
3340 		} else {
3341 			if (cd.bExtraWin)
3342 				AddExtraWindow(invX, invY, &retObj[n]);
3343 		}
3344 
3345 		AddBoxes(true);
3346 	}
3347 
3348 	assert(n < MAX_WCOMP); // added more parts than we can handle!
3349 
3350 	// Reposition returns true if needs to move
3351 	if (g_InvD[g_ino].bMoveable && filling == FULL && RePosition()) {
3352 		ConstructInventory(FULL);
3353 	}
3354 }
3355 
3356 
3357 /**
3358  * Call this when drawing a 'FULL', movable inventory. Checks that the
3359  * position of the Translucent object is within limits. If it isn't,
3360  * adjusts the x/y position of the current inventory and returns true.
3361  */
RePosition()3362 static bool RePosition() {
3363 	int	p;
3364 	bool	bMoveitMoveit = false;
3365 
3366 	assert(g_RectObject); // no recangle object!
3367 
3368 	// Test for off-screen horizontally
3369 	p = MultiLeftmost(g_RectObject);
3370 	if (p > MAXLEFT) {
3371 		// Too far to the right
3372 		g_InvD[g_ino].inventoryX += MAXLEFT - p;
3373 		bMoveitMoveit = true;			// I like to....
3374 	} else {
3375 		// Too far to the left?
3376 		p = MultiRightmost(g_RectObject);
3377 		if (p < MINRIGHT) {
3378 			g_InvD[g_ino].inventoryX += MINRIGHT - p;
3379 			bMoveitMoveit = true;		// I like to....
3380 		}
3381 	}
3382 
3383 	// Test for off-screen vertically
3384 	p = MultiHighest(g_RectObject);
3385 	if (p < MINTOP) {
3386 		// Too high
3387 		g_InvD[g_ino].inventoryY += MINTOP - p;
3388 		bMoveitMoveit = true;			// I like to....
3389 	} else if (p > MAXTOP) {
3390 		// Too low
3391 		g_InvD[g_ino].inventoryY += MAXTOP - p;
3392 		bMoveitMoveit = true;			// I like to....
3393 	}
3394 
3395 	return bMoveitMoveit;
3396 }
3397 
3398 /**************************************************************************/
3399 /***/
3400 /**************************************************************************/
3401 
3402 /**
3403  * Get the cursor's reel, poke in the background palette,
3404  * and customise the cursor.
3405  */
AlterCursor(int num)3406 static void AlterCursor(int num) {
3407 	const FREEL *pfreel;
3408 	IMAGE *pim;
3409 
3410 	// Get pointer to image
3411 	pim = GetImageFromFilm(g_hWinParts, num, &pfreel);
3412 
3413 	// Poke in the background palette
3414 	pim->hImgPal = TO_32(BgPal());
3415 
3416 	SetTempCursor(FROM_32(pfreel->script));
3417 }
3418 
3419 enum InvCursorFN {IC_AREA, IC_DROP};
3420 
3421 /**
3422  * InvCursor
3423  */
InvCursor(InvCursorFN fn,int CurX,int CurY)3424 static void InvCursor(InvCursorFN fn, int CurX, int CurY) {
3425 	static enum { IC_NORMAL, IC_DR, IC_UR, IC_TB, IC_LR,
3426 		IC_INV, IC_UP, IC_DN } ICursor = IC_NORMAL;	// FIXME: Avoid non-const global vars
3427 
3428 	int	area;		// The part of the window the cursor is over
3429 	bool	restoreMain = false;
3430 
3431 	// If currently dragging, don't be messing about with the cursor shape
3432 	if (g_InvDragging != ID_NONE)
3433 		return;
3434 
3435 	switch (fn) {
3436 	case IC_DROP:
3437 		ICursor = IC_NORMAL;
3438 		InvCursor(IC_AREA, CurX, CurY);
3439 		break;
3440 
3441 	case IC_AREA:
3442 		area = InvArea(CurX, CurY);
3443 
3444 		// Check for POINTED events
3445 		if (g_ino == INV_CONF)
3446 			InvBoxes(area == I_BODY, CurX, CurY);
3447 		else
3448 			InvLabels(area == I_BODY, CurX, CurY);
3449 
3450 		// No cursor trails while within inventory window
3451 		if (area == I_NOTIN)
3452 			UnHideCursorTrails();
3453 		else
3454 			HideCursorTrails();
3455 
3456 		switch (area) {
3457 		case I_NOTIN:
3458 			restoreMain = true;
3459 			break;
3460 
3461 		case I_TLEFT:
3462 		case I_BRIGHT:
3463 			if (!g_InvD[g_ino].resizable)
3464 				restoreMain = true;
3465 			else if (ICursor != IC_DR) {
3466 				AlterCursor(IX_CURDD);
3467 				ICursor = IC_DR;
3468 			}
3469 			break;
3470 
3471 		case I_TRIGHT:
3472 		case I_BLEFT:
3473 			if (!g_InvD[g_ino].resizable)
3474 				restoreMain = true;
3475 			else if (ICursor != IC_UR) {
3476 				AlterCursor(IX_CURDU);
3477 				ICursor = IC_UR;
3478 			}
3479 			break;
3480 
3481 		case I_TOP:
3482 		case I_BOTTOM:
3483 			if (!g_InvD[g_ino].resizable) {
3484 				restoreMain = true;
3485 				break;
3486 			}
3487 			if (ICursor != IC_TB) {
3488 				AlterCursor(IX_CURUD);
3489 				ICursor = IC_TB;
3490 			}
3491 			break;
3492 
3493 		case I_LEFT:
3494 		case I_RIGHT:
3495 			if (!g_InvD[g_ino].resizable)
3496 				restoreMain = true;
3497 			else if (ICursor != IC_LR) {
3498 				AlterCursor(IX_CURLR);
3499 				ICursor = IC_LR;
3500 			}
3501 			break;
3502 
3503 		case I_UP:
3504 		case I_SLIDE_UP:
3505 		case I_DOWN:
3506 		case I_SLIDE_DOWN:
3507 		case I_SLIDE:
3508 		case I_HEADER:
3509 		case I_BODY:
3510 			restoreMain = true;
3511 			break;
3512 		}
3513 		break;
3514 	}
3515 
3516 	if (restoreMain && ICursor != IC_NORMAL) {
3517 		RestoreMainCursor();
3518 		ICursor = IC_NORMAL;
3519 	}
3520 }
3521 
3522 
3523 
3524 
3525 /*-------------------------------------------------------------------------*/
3526 
3527 
3528 /**************************************************************************/
3529 /******************** Conversation specific functions *********************/
3530 /**************************************************************************/
3531 
3532 
ConvAction(int index)3533 extern void ConvAction(int index) {
3534 	assert(g_ino == INV_CONV); // not conv. window!
3535 	PMOVER pMover = TinselV2 ? GetMover(GetLeadId()) : NULL;
3536 
3537 	switch (index) {
3538 	case INV_NOICON:
3539 		return;
3540 
3541 	case INV_CLOSEICON:
3542 		g_thisIcon = -1;	// Postamble
3543 		break;
3544 
3545 	case INV_OPENICON:
3546 		// Store the direction the lead character is facing in when the conversation starts
3547 		if (TinselV2)
3548 			g_initialDirection = GetMoverDirection(pMover);
3549 		g_thisIcon = -2;	// Preamble
3550 		break;
3551 
3552 	default:
3553 		g_thisIcon = g_InvD[g_ino].contents[index];
3554 		break;
3555 	}
3556 
3557 	if (!TinselV2)
3558 		RunPolyTinselCode(g_thisConvPoly, CONVERSE, PLR_NOEVENT, true);
3559 	else {
3560 		// If the lead's direction has changed for any reason (such as having broken the
3561 		// fourth wall and talked to the screen), reset back to the original direction
3562 		DIRECTION currDirection = GetMoverDirection(pMover);
3563 		if (currDirection != g_initialDirection) {
3564 			SetMoverDirection(pMover, g_initialDirection);
3565 			SetMoverStanding(pMover);
3566 		}
3567 
3568 		if (g_thisConvPoly != NOPOLY)
3569 			PolygonEvent(Common::nullContext, g_thisConvPoly, CONVERSE, 0, false, 0);
3570 		else
3571 			ActorEvent(Common::nullContext, g_thisConvActor, CONVERSE, false, 0);
3572 	}
3573 
3574 }
3575 
3576 /**
3577  * Called to specify whether conversation window is going to
3578  * appear at the top or bottom of the screen.
3579  * Also to specify which polygon or actor is opening the conversation.
3580  *
3581  * Note: ano may (will probably) be set when it's a polygon.
3582  */
SetConvDetails(CONV_PARAM fn,HPOLYGON hPoly,int ano)3583 extern void SetConvDetails(CONV_PARAM fn, HPOLYGON hPoly, int ano) {
3584 	g_thisConvFn = fn;
3585 	g_thisConvPoly = hPoly;
3586 	g_thisConvActor = ano;
3587 
3588 	g_bMoveOnUnHide = true;
3589 
3590 	// Get the Actor Tag's or Tagged Actor's label for the conversation window title
3591 	if (hPoly != NOPOLY)	{
3592 		int x, y;
3593 		GetTagTag(hPoly, &g_InvD[INV_CONV].hInvTitle, &x, &y);
3594 	} else {
3595 		g_InvD[INV_CONV].hInvTitle = GetActorTagHandle(ano);
3596 	}
3597 }
3598 
3599 /*-------------------------------------------------------------------------*/
3600 
3601 /**
3602  * Add an icon to the permanent conversation list.
3603  */
PermaConvIcon(int icon,bool bEnd)3604 extern void PermaConvIcon(int icon, bool bEnd) {
3605 	int i;
3606 
3607 	// See if it's already there
3608 	for (i = 0; i < g_numPermIcons; i++) {
3609 		if (g_permIcons[i] == icon)
3610 			break;
3611 	}
3612 
3613 	// Add it if it isn't already there
3614 	if (i == g_numPermIcons) {
3615 		assert(g_numPermIcons < MAX_PERMICONS);
3616 
3617 		if (bEnd || !g_numEndIcons) {
3618 			// Add it at the end
3619 			g_permIcons[g_numPermIcons++] = icon;
3620 			if (bEnd)
3621 				g_numEndIcons++;
3622 		} else {
3623 			// Insert before end icons
3624 			memmove(&g_permIcons[g_numPermIcons-g_numEndIcons+1],
3625 				&g_permIcons[g_numPermIcons-g_numEndIcons],
3626 				g_numEndIcons * sizeof(int));
3627 			g_permIcons[g_numPermIcons-g_numEndIcons] = icon;
3628 			g_numPermIcons++;
3629 		}
3630 	}
3631 }
3632 
3633 /*-------------------------------------------------------------------------*/
3634 
convPos(int fn)3635 extern void convPos(int fn) {
3636 	if (fn == CONV_DEF)
3637 		g_InvD[INV_CONV].inventoryY = 8;
3638 	else if (fn == CONV_BOTTOM)
3639 		g_InvD[INV_CONV].inventoryY = 150;
3640 }
3641 
ConvPoly(HPOLYGON hPoly)3642 extern void ConvPoly(HPOLYGON hPoly) {
3643 	g_thisConvPoly = hPoly;
3644 }
3645 
GetIcon()3646 extern int GetIcon() {
3647 	return g_thisIcon;
3648 }
3649 
CloseDownConv()3650 extern void CloseDownConv() {
3651 	if (g_InventoryState == ACTIVE_INV && g_ino == INV_CONV) {
3652 		KillInventory();
3653 	}
3654 }
3655 
HideConversation(bool bHide)3656 extern void HideConversation(bool bHide) {
3657 	int aniX, aniY;
3658 	int i;
3659 
3660 	if (g_InventoryState == ACTIVE_INV && g_ino == INV_CONV) {
3661 		if (bHide) {
3662 			// Move all the window and icons off-screen
3663 			for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) {
3664 				MultiAdjustXY(g_objArray[i], 2 * SCREEN_WIDTH, 0);
3665 			}
3666 			for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) {
3667 				MultiAdjustXY(g_iconArray[i], 2 * SCREEN_WIDTH, 0);
3668 			}
3669 
3670 			// Window is hidden
3671 			g_InventoryHidden = true;
3672 
3673 			// Remove any labels
3674 			InvLabels(false, 0, 0);
3675 		} else {
3676 			// Window is not hidden
3677 			g_InventoryHidden = false;
3678 
3679 			if (TinselV2 && g_ItemsChanged)
3680 				// Just rebuild the whole thing
3681 				ConstructInventory(FULL);
3682 			else {
3683 				// Move it all back on-screen
3684 				for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) {
3685 					MultiAdjustXY(g_objArray[i], -2 * SCREEN_WIDTH, 0);
3686 				}
3687 
3688 				// Don't flash if items changed. If they have, will be redrawn anyway.
3689 				if (TinselV2 || !g_ItemsChanged) {
3690 					for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) {
3691 						MultiAdjustXY(g_iconArray[i], -2*SCREEN_WIDTH, 0);
3692 					}
3693 				}
3694 			}
3695 
3696 			if (TinselV2 && g_bMoveOnUnHide) {
3697 				/*
3698 				 * First time, position it appropriately
3699 				 */
3700 				int left, center;
3701 				int x, y, deltay;
3702 
3703 				// Only do it once per conversation
3704 				g_bMoveOnUnHide = false;
3705 
3706 				// Current center of the window
3707 				left = MultiLeftmost(g_RectObject);
3708 				center = (MultiRightmost(g_RectObject) + left) / 2;
3709 
3710 				// Get the x-offset for the conversation window
3711 				if (g_thisConvActor) {
3712 					int Loffset, Toffset;
3713 
3714 					GetActorMidTop(g_thisConvActor, &x, &y);
3715 					PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
3716 					x -= Loffset;
3717 					y -= Toffset;
3718 				} else {
3719 					x = SCREEN_WIDTH / 2;
3720 					y = SCREEN_BOX_HEIGHT2 / 2;
3721 				}
3722 
3723 				// Save old y-position
3724 				deltay = g_InvD[INV_CONV].inventoryY;
3725 
3726 				switch (g_thisConvFn) {
3727 				case CONV_TOP:
3728 					g_InvD[INV_CONV].inventoryY = SysVar(SV_CONV_TOPY);
3729 					break;
3730 
3731 				case CONV_BOTTOM:
3732 					g_InvD[INV_CONV].inventoryY = SysVar(SV_CONV_BOTY);
3733 					break;
3734 
3735 				case CONV_DEF:
3736 					g_InvD[INV_CONV].inventoryY = y - SysVar(SV_CONV_ABOVE_Y);
3737 					break;
3738 
3739 				default:
3740 					break;
3741 				}
3742 
3743 				// Calculate y change
3744 				deltay = g_InvD[INV_CONV].inventoryY - deltay;
3745 
3746 				// Move it all
3747 				for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) {
3748 					MultiMoveRelXY(g_objArray[i], x - center, deltay);
3749 				}
3750 				for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) {
3751 					MultiMoveRelXY(g_iconArray[i], x - center, deltay);
3752 				}
3753 				g_InvD[INV_CONV].inventoryX += x - center;
3754 
3755 				/*
3756 				 * Now positioned as worked out
3757 				 * - but it must be in a sensible place
3758 				*/
3759 				if (MultiLeftmost(g_RectObject) < SysVar(SV_CONV_MINX))
3760 					x = SysVar(SV_CONV_MINX) - MultiLeftmost(g_RectObject);
3761 				else if (MultiRightmost(g_RectObject) > SCREEN_WIDTH - SysVar(SV_CONV_MINX))
3762 					x = SCREEN_WIDTH - SysVar(SV_CONV_MINX) - MultiRightmost(g_RectObject);
3763 				else
3764 					x = 0;
3765 
3766 				if (g_thisConvFn == CONV_DEF && MultiHighest(g_RectObject) < SysVar(SV_CONV_MINY)
3767 						&& g_thisConvActor) {
3768 					int Loffset, Toffset;
3769 
3770 					PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
3771 					y = GetActorBottom(g_thisConvActor) - MultiHighest(g_RectObject) +
3772 						SysVar(SV_CONV_BELOW_Y);
3773 					y -= Toffset;
3774 				}
3775 				else
3776 					y = 0;
3777 
3778 				if (x || y) {
3779 					for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) {
3780 						MultiMoveRelXY(g_objArray[i], x, y);
3781 					}
3782 					for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) {
3783 						MultiMoveRelXY(g_iconArray[i], x, y);
3784 					}
3785 					g_InvD[INV_CONV].inventoryX += x;
3786 					g_InvD[INV_CONV].inventoryY += y;
3787 				}
3788 
3789 				/*
3790 				 * Oh shit! We might have gone off the bottom
3791 				 */
3792 				if (MultiLowest(g_RectObject) > SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) {
3793 					y = (SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) - MultiLowest(g_RectObject);
3794 					for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) {
3795 						MultiMoveRelXY(g_objArray[i], 0, y);
3796 					}
3797 					for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) {
3798 						MultiMoveRelXY(g_iconArray[i], 0, y);
3799 					}
3800 					g_InvD[INV_CONV].inventoryY += y;
3801 				}
3802 			}
3803 
3804 			GetCursorXY(&aniX, &aniY, false);
3805 			InvLabels(true, aniX, aniY);
3806 		}
3807 	}
3808 }
3809 
ConvIsHidden()3810 extern bool ConvIsHidden() {
3811 	return g_InventoryHidden;
3812 }
3813 
3814 
3815 /**************************************************************************/
3816 /******************* Open and closing functions ***************************/
3817 /**************************************************************************/
3818 
3819 /**
3820  * Start up an inventory window.
3821  */
PopUpInventory(int invno)3822 extern void PopUpInventory(int invno) {
3823 	assert(invno == INV_1 || invno == INV_2 || invno == INV_CONV
3824 		|| invno == INV_CONF || invno == INV_MENU); // Trying to open illegal inventory
3825 
3826 	if (g_InventoryState == IDLE_INV) {
3827 		g_bReOpenMenu = false;	// Better safe than sorry...
3828 
3829 		DisableTags();		// Tags disabled during inventory
3830 		if (TinselV2)
3831 			DisablePointing();	// Pointing disabled during inventory
3832 
3833 		if (invno == INV_CONV) {	// Conversation window?
3834 			if (TinselV2)
3835 				// Quiet please..
3836 				_vm->_pcmMusic->dim(false);
3837 
3838 			// Start conversation with permanent contents
3839 			memset(g_InvD[INV_CONV].contents, 0, MAX_ININV*sizeof(int));
3840 			memcpy(g_InvD[INV_CONV].contents, g_permIcons, g_numPermIcons*sizeof(int));
3841 			g_InvD[INV_CONV].NoofItems = g_numPermIcons;
3842 			if (TinselV2)
3843 				g_InvD[INV_CONV].NoofHicons = g_numPermIcons;
3844 			else
3845 				g_thisIcon = 0;
3846 		} else if (invno == INV_CONF) {	// Configuration window?
3847 			cd.selBox = NOBOX;
3848 			cd.pointBox = NOBOX;
3849 		}
3850 
3851 		g_ino = invno;			// The open inventory
3852 
3853 		g_ItemsChanged = false;		// Nothing changed
3854 		g_InvDragging = ID_NONE;		// Not dragging
3855 		g_InventoryState = ACTIVE_INV;	// Inventory actiive
3856 		g_InventoryHidden = false;	// Not hidden
3857 		g_InventoryMaximised = g_InvD[g_ino].bMax;
3858 		if (invno != INV_CONF)	// Configuration window?
3859 			ConstructInventory(FULL);	// Draw it up
3860 		else {
3861 			ConstructInventory(CONF);	// Draw it up
3862 		}
3863 	}
3864 }
3865 
SetMenuGlobals(CONFINIT * ci)3866 static void SetMenuGlobals(CONFINIT *ci) {
3867 	g_InvD[INV_CONF].MinHicons = g_InvD[INV_CONF].MaxHicons = g_InvD[INV_CONF].NoofHicons = ci->h;
3868 	g_InvD[INV_CONF].MaxVicons = g_InvD[INV_CONF].MinVicons = g_InvD[INV_CONF].NoofVicons = ci->v;
3869 	g_InvD[INV_CONF].inventoryX = ci->x;
3870 	g_InvD[INV_CONF].inventoryY = ci->y;
3871 	cd.bExtraWin = ci->bExtraWin;
3872 	cd.box = ci->Box;
3873 	cd.NumBoxes = ci->NumBoxes;
3874 	cd.ixHeading = ci->ixHeading;
3875 
3876 	if (TinselV2) {
3877 		if ((ci->ixHeading != NO_HEADING) && SysString(ci->ixHeading))
3878 			g_InvD[INV_MENU].hInvTitle = SysString(ci->ixHeading);
3879 		else
3880 			g_InvD[INV_MENU].hInvTitle = NO_HEADING;
3881 	}
3882 }
3883 
3884 /**
3885  * PopupConf
3886  */
OpenMenu(CONFTYPE menuType)3887 extern void OpenMenu(CONFTYPE menuType) {
3888 	int curX, curY;
3889 
3890 	// In the DW 1 demo, don't allow any menu to be opened
3891 	if (TinselV0)
3892 		return;
3893 
3894 	if (g_InventoryState != IDLE_INV)
3895 		return;
3896 
3897 	g_InvD[INV_CONF].resizable = false;
3898 	g_InvD[INV_CONF].bMoveable = false;
3899 
3900 	switch (menuType) {
3901 	case MAIN_MENU:
3902 		SetMenuGlobals(&ciOption);
3903 		break;
3904 
3905 	case SAVE_MENU:
3906 		g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);	// Show VK when saving a game
3907 		if (!TinselV2)
3908 			SetCursorScreenXY(262, 91);
3909 		SetMenuGlobals(&ciSave);
3910 		cd.editableRgroup = true;
3911 		FirstFile(0);
3912 		break;
3913 
3914 	case LOAD_MENU:
3915 		SetMenuGlobals(&ciLoad);
3916 		cd.editableRgroup = false;
3917 		FirstFile(0);
3918 		break;
3919 
3920 	case RESTART_MENU:
3921 		if (TinselV2)
3922 			SetCursorScreenXY(360, 153);
3923 		else if (_vm->getLanguage() == Common::JA_JPN)
3924 			SetCursorScreenXY(180, 106);
3925 		else
3926 			SetCursorScreenXY(180, 90);
3927 
3928 		SetMenuGlobals(&ciRestart);
3929 		break;
3930 
3931 	case SOUND_MENU:
3932 		if (TinselV2)
3933 			g_displayedLanguage = TextLanguage();
3934 #if 1
3935 		// FIXME: Hack to setup CONFBOX pointer to data in the global Config object
3936 		if (TinselV2) {
3937 			t2SoundBox[0].ival = &_vm->_config->_musicVolume;
3938 			t2SoundBox[1].ival = &_vm->_config->_soundVolume;
3939 			t2SoundBox[2].ival = &_vm->_config->_voiceVolume;
3940 			t2SoundBox[3].ival = &_vm->_config->_textSpeed;
3941 			t2SoundBox[4].ival = &_vm->_config->_useSubtitles;
3942 		} else {
3943 			t1SoundBox[0].ival = &_vm->_config->_musicVolume;
3944 			t1SoundBox[1].ival = &_vm->_config->_soundVolume;
3945 			t1SoundBox[2].ival = &_vm->_config->_voiceVolume;
3946 		}
3947 #endif
3948 		SetMenuGlobals(&ciSound);
3949 		break;
3950 
3951 	case CONTROLS_MENU:
3952 #if 1
3953 		// FIXME: Hack to setup CONFBOX pointer to data in the global Config object
3954 		controlBox[0].ival = &_vm->_config->_dclickSpeed;
3955 		controlBox[2].ival = &_vm->_config->_swapButtons;
3956 #endif
3957 		SetMenuGlobals(&ciControl);
3958 		break;
3959 
3960 	case QUIT_MENU:
3961 		if (TinselV2)
3962 			SetCursorScreenXY(360, 153);
3963 		else if (_vm->getLanguage() == Common::JA_JPN)
3964 			SetCursorScreenXY(180, 106);
3965 		else
3966 			SetCursorScreenXY(180, 90);
3967 
3968 		SetMenuGlobals(&ciQuit);
3969 		break;
3970 
3971 	case HOPPER_MENU1:
3972 		PrimeSceneHopper();
3973 		SetMenuGlobals(&ciHopper1);
3974 		cd.editableRgroup = false;
3975 		RememberChosenScene();
3976 		FirstScene(0);
3977 		break;
3978 
3979 	case HOPPER_MENU2:
3980 		SetMenuGlobals(&ciHopper2);
3981 		cd.editableRgroup = false;
3982 		SetChosenScene();
3983 		FirstEntry(0);
3984 		break;
3985 
3986 	case SUBTITLES_MENU: {
3987 		int hackOffset = 0;
3988 		if (_vm->getFeatures() & GF_USE_3FLAGS) {
3989 			hackOffset = 3;
3990 			ciSubtitles.v = 6;
3991 			ciSubtitles.Box = subtitlesBox3Flags;
3992 			ciSubtitles.NumBoxes = ARRAYSIZE(subtitlesBox3Flags);
3993 		} else if (_vm->getFeatures() & GF_USE_4FLAGS) {
3994 			hackOffset = 4;
3995 			ciSubtitles.v = 6;
3996 			ciSubtitles.Box = subtitlesBox4Flags;
3997 			ciSubtitles.NumBoxes = ARRAYSIZE(subtitlesBox4Flags);
3998 		} else if (_vm->getFeatures() & GF_USE_5FLAGS) {
3999 			hackOffset = 5;
4000 			ciSubtitles.v = 6;
4001 			ciSubtitles.Box = subtitlesBox5Flags;
4002 			ciSubtitles.NumBoxes = ARRAYSIZE(subtitlesBox5Flags);
4003 		} else {
4004 			hackOffset = 0;
4005 			ciSubtitles.v = 3;
4006 			ciSubtitles.Box = subtitlesBox;
4007 			ciSubtitles.NumBoxes = ARRAYSIZE(subtitlesBox);
4008 		}
4009 #if 1
4010 		// FIXME: Hack to setup CONFBOX pointer to data in the global Config object
4011 		ciSubtitles.Box[hackOffset].ival = &_vm->_config->_textSpeed;
4012 		ciSubtitles.Box[hackOffset+1].ival = &_vm->_config->_useSubtitles;
4013 #endif
4014 
4015 		SetMenuGlobals(&ciSubtitles);
4016 		}
4017 		break;
4018 
4019 	case TOP_WINDOW:
4020 		SetMenuGlobals(&ciTopWin);
4021 		g_ino = INV_CONF;
4022 		ConstructInventory(CONF);	// Draw it up
4023 		g_InventoryState = BOGUS_INV;
4024 		return;
4025 
4026 	default:
4027 		return;
4028 	}
4029 
4030 	if (g_heldItem != INV_NOICON)
4031 		DelAuxCursor();			// no longer aux cursor
4032 
4033 	PopUpInventory(INV_CONF);
4034 
4035 	// Make initial box selections if appropriate
4036 	if (menuType == SAVE_MENU || menuType == LOAD_MENU
4037 			|| menuType == HOPPER_MENU1 || menuType == HOPPER_MENU2)
4038 		Select(0, false);
4039 	else if (menuType == SUBTITLES_MENU) {
4040 		if (_vm->getFeatures() & GF_USE_3FLAGS) {
4041 			// VERY quick dirty bodges
4042 			if (_vm->_config->_language == TXT_FRENCH)
4043 				Select(0, false);
4044 			else if (_vm->_config->_language == TXT_GERMAN)
4045 				Select(1, false);
4046 			else
4047 				Select(2, false);
4048 		} else if (_vm->getFeatures() & GF_USE_4FLAGS) {
4049 			Select(_vm->_config->_language-1, false);
4050 		} else if (_vm->getFeatures() & GF_USE_5FLAGS) {
4051 			Select(_vm->_config->_language, false);
4052 		}
4053 	}
4054 
4055 	GetCursorXY(&curX, &curY, false);
4056 	InvCursor(IC_AREA, curX, curY);
4057 }
4058 
4059 /**
4060  * Close down an inventory window.
4061  */
KillInventory()4062 extern void KillInventory() {
4063 	if (g_objArray[0] != NULL) {
4064 		DumpObjArray();
4065 		DumpDobjArray();
4066 		DumpIconArray();
4067 	}
4068 
4069 	if (g_InventoryState == ACTIVE_INV) {
4070 		EnableTags();
4071 		if (TinselV2)
4072 			EnablePointing();
4073 
4074 		g_InvD[g_ino].bMax = g_InventoryMaximised;
4075 
4076 		UnHideCursorTrails();
4077 		_vm->divertKeyInput(NULL);
4078 	}
4079 
4080 	g_InventoryState = IDLE_INV;
4081 
4082 	if (g_bReOpenMenu) {
4083 		g_bReOpenMenu = false;
4084 		OpenMenu(MAIN_MENU);
4085 
4086 		// Write config changes
4087 		_vm->_config->writeToDisk();
4088 
4089 	} else if (g_ino == INV_CONF)
4090 		InventoryIconCursor(false);
4091 
4092 	if (TinselV2)
4093 		// Pump up the volume
4094 		if (g_ino == INV_CONV)
4095 			_vm->_pcmMusic->unDim(false);
4096 
4097 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);	// Hide VK after save dialog closes
4098 }
4099 
CloseInventory()4100 extern void CloseInventory() {
4101 	// If not active, ignore this
4102 	if (g_InventoryState != ACTIVE_INV)
4103 		return;
4104 
4105 	// If hidden, a conversation action is still underway - ignore this
4106 	if (g_InventoryHidden)
4107 		return;
4108 
4109 	// If conversation, this is a closeing event
4110 	if (g_ino == INV_CONV)
4111 		ConvAction(INV_CLOSEICON);
4112 
4113 	KillInventory();
4114 
4115 	RestoreMainCursor();
4116 }
4117 
4118 
4119 
4120 /**************************************************************************/
4121 /************************ The inventory process ***************************/
4122 /**************************************************************************/
4123 
4124 /**
4125  * Redraws the icons if appropriate. Also handle button press/toggle effects
4126  */
InventoryProcess(CORO_PARAM,const void *)4127 extern void InventoryProcess(CORO_PARAM, const void *) {
4128 	// COROUTINE
4129 	CORO_BEGIN_CONTEXT;
4130 	CORO_END_CONTEXT(_ctx);
4131 
4132 	CORO_BEGIN_CODE(_ctx);
4133 
4134 	if (NumberOfLanguages() <= 1)
4135 		g_bNoLanguage = true;
4136 
4137 	while (1) {
4138 		CORO_SLEEP(1);		// allow scheduling
4139 
4140 		if (g_objArray[0] != NULL) {
4141 			if (g_ItemsChanged && g_ino != INV_CONF && !g_InventoryHidden) {
4142 				FillInInventory();
4143 
4144 				// Needed when clicking on scroll bar.
4145 				int	curX, curY;
4146 				GetCursorXY(&curX, &curY, false);
4147 				InvCursor(IC_AREA, curX, curY);
4148 
4149 				g_ItemsChanged = false;
4150 			}
4151 			if (g_ino != INV_CONF) {
4152 				for (int i = 0; i < MAX_ICONS; i++) {
4153 					if (g_iconArray[i] != NULL)
4154 						StepAnimScript(&g_iconAnims[i]);
4155 				}
4156 			}
4157 			if (g_InvDragging == ID_MDCONT) {
4158 				// Mixing desk control
4159 				int sval, index, *pival;
4160 
4161 				index = cd.selBox & ~IS_MASK;
4162 				pival = cd.box[index].ival;
4163 				sval = *pival;
4164 
4165 				if (cd.selBox & IS_LEFT) {
4166 					*pival -= cd.box[index].h;
4167 					if (*pival < 0)
4168 						*pival = 0;
4169 				} else if (cd.selBox & IS_RIGHT) {
4170 					*pival += cd.box[index].h;
4171 					if (*pival > cd.box[index].w)
4172 						*pival = cd.box[index].w;
4173 				}
4174 
4175 				if (sval != *pival) {
4176 					SlideMSlider(0, (cd.selBox & IS_RIGHT) ? S_TIMEUP : S_TIMEDN);
4177 				}
4178 			}
4179 		}
4180 
4181 		if (g_buttonEffect.bButAnim) {
4182 			assert(g_buttonEffect.box);
4183 			if (g_buttonEffect.press) {
4184 				if (g_buttonEffect.box->boxType == AAGBUT || g_buttonEffect.box->boxType == ARSGBUT)
4185 					CORO_INVOKE_1(ButtonPress, g_buttonEffect.box);
4186 				switch (g_buttonEffect.box->boxFunc) {
4187 				case SAVEGAME:
4188 					KillInventory();
4189 					InvSaveGame();
4190 					break;
4191 				case LOADGAME:
4192 					KillInventory();
4193 					InvLoadGame();
4194 					break;
4195 				case IQUITGAME:
4196 					_vm->quitGame();
4197 					break;
4198 				case CLOSEWIN:
4199 					KillInventory();
4200 					if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
4201 						FreeSceneHopper();
4202 					break;
4203 				case OPENLOAD:
4204 					KillInventory();
4205 					OpenMenu(LOAD_MENU);
4206 					break;
4207 				case OPENSAVE:
4208 					KillInventory();
4209 					OpenMenu(SAVE_MENU);
4210 					break;
4211 				case OPENREST:
4212 					KillInventory();
4213 					OpenMenu(RESTART_MENU);
4214 					break;
4215 				case OPENSOUND:
4216 					KillInventory();
4217 					OpenMenu(SOUND_MENU);
4218 					break;
4219 				case OPENCONT:
4220 					KillInventory();
4221 					OpenMenu(CONTROLS_MENU);
4222 					break;
4223 	#ifndef JAPAN
4224 				case OPENSUBT:
4225 					KillInventory();
4226 					OpenMenu(SUBTITLES_MENU);
4227 					break;
4228 	#endif
4229 				case OPENQUIT:
4230 					KillInventory();
4231 					OpenMenu(QUIT_MENU);
4232 					break;
4233 				case INITGAME:
4234 					KillInventory();
4235 					FnRestartGame();
4236 					break;
4237 				case CLANG:
4238 					if (!LanguageChange())
4239 						KillInventory();
4240 					break;
4241 				case RLANG:
4242 					KillInventory();
4243 					break;
4244 				case HOPPER2:
4245 					KillInventory();
4246 					OpenMenu(HOPPER_MENU2);
4247 					break;
4248 				case BF_CHANGESCENE:
4249 					KillInventory();
4250 					HopAction();
4251 					FreeSceneHopper();
4252 					break;
4253 				default:
4254 					break;
4255 				}
4256 			} else
4257 				CORO_INVOKE_1(ButtonToggle, g_buttonEffect.box);
4258 
4259 			g_buttonEffect.bButAnim = false;
4260 		}
4261 
4262 	}
4263 	CORO_END_CODE;
4264 }
4265 
4266 /**************************************************************************/
4267 /*************** Drag stuff - Resizing and moving window ******************/
4268 /**************************************************************************/
4269 
4270 /**
4271  * Appears to find the nearest entry in slideStuff[] to the supplied
4272  * y-coordinate.
4273  */
NearestSlideY(int fity)4274 static int NearestSlideY(int fity) {
4275 	int nearDist = 1000;
4276 	int thisDist;
4277 	int nearI = 0;	// Index of nearest fit
4278 	int i = 0;
4279 
4280 	do {
4281 		thisDist = ABS(g_slideStuff[i].y - fity);
4282 		if (thisDist < nearDist) {
4283 			nearDist = thisDist;
4284 			nearI = i;
4285 		}
4286 	} while (g_slideStuff[++i].n != -1);
4287 	return nearI;
4288 }
4289 
4290 /**
4291  * Gets called at the start and end of a drag on the slider, and upon
4292  * y-movement during such a drag.
4293  */
SlideSlider(int y,SSFN fn)4294 static void SlideSlider(int y, SSFN fn) {
4295 	static int newY = 0, lasti = 0;	// FIXME: Avoid non-const global vars
4296 	int gotoY, ati;
4297 
4298 	// Only do this if there's a slider
4299 	if (!g_SlideObject)
4300 		return;
4301 
4302 	switch (fn) {
4303 	case S_START:			// Start of a drag on the slider
4304 		newY = g_sliderYpos;
4305 		lasti = NearestSlideY(g_sliderYpos);
4306 		break;
4307 
4308 	case S_SLIDE:			// Y-movement during drag
4309 		newY = newY + y;		// New y-position
4310 
4311 		if (newY < g_sliderYmin)
4312 			gotoY = g_sliderYmin;	// Above top limit
4313 		else if (newY > g_sliderYmax)
4314 			gotoY = g_sliderYmax;	// Below bottom limit
4315 		else
4316 			gotoY = newY;		// Hunky-Dory
4317 
4318 		// Move slider to new position
4319 		MultiMoveRelXY(g_SlideObject, 0, gotoY - g_sliderYpos);
4320 		g_sliderYpos = gotoY;
4321 
4322 		// Re-draw icons if necessary
4323 		ati = NearestSlideY(g_sliderYpos);
4324 		if (ati != lasti) {
4325 			g_InvD[g_ino].FirstDisp = g_slideStuff[ati].n;
4326 			assert(g_InvD[g_ino].FirstDisp >= 0); // negative first displayed
4327 			g_ItemsChanged = true;
4328 			lasti = ati;
4329 		}
4330 		break;
4331 
4332 	case S_END:			// End of a drag on the slider
4333 		// Draw icons from new start icon
4334 		ati = NearestSlideY(g_sliderYpos);
4335 		g_InvD[g_ino].FirstDisp = g_slideStuff[ati].n;
4336 		g_ItemsChanged = true;
4337 		break;
4338 
4339 	default:
4340 		break;
4341 	}
4342 }
4343 
4344 /**
4345  * Gets called at the start and end of a drag on the slider, and upon
4346  * y-movement during such a drag.
4347  */
SlideCSlider(int y,SSFN fn)4348 static void SlideCSlider(int y, SSFN fn) {
4349 	static int newY = 0;	// FIXME: Avoid non-const global vars
4350 	int	gotoY;
4351 	int	fc;
4352 
4353 	// Only do this if there's a slider
4354 	if (!g_SlideObject)
4355 		return;
4356 
4357 	switch (fn) {
4358 	case S_START:			// Start of a drag on the slider
4359 		newY = g_sliderYpos;
4360 		break;
4361 
4362 	case S_SLIDE:			// Y-movement during drag
4363 		newY = newY + y;		// New y-position
4364 
4365 		if (newY < g_sliderYmin)
4366 			gotoY = g_sliderYmin;	// Above top limit
4367 		else if (newY > g_sliderYmax)
4368 			gotoY = g_sliderYmax;	// Below bottom limit
4369 		else
4370 			gotoY = newY;		// Hunky-Dory
4371 
4372 		// Move slider to new position
4373 		if (TinselV2)
4374 			MultiMoveRelXY(g_SlideObject, 0, gotoY - g_sliderYpos);
4375 		g_sliderYpos = gotoY;
4376 
4377 		fc = cd.extraBase;
4378 
4379 		if ((cd.box == saveBox || cd.box == loadBox))
4380 			FirstFile((g_sliderYpos - g_sliderYmin) * (MAX_SAVED_FILES - NUM_RGROUP_BOXES) /
4381 				(g_sliderYmax - g_sliderYmin));
4382 		else if (cd.box == hopperBox1)
4383 			FirstScene((g_sliderYpos - g_sliderYmin) * (g_numScenes - NUM_RGROUP_BOXES) / sliderRange);
4384 		else if (cd.box == hopperBox2)
4385 			FirstEntry((g_sliderYpos - g_sliderYmin) * (g_numEntries - NUM_RGROUP_BOXES) / sliderRange);
4386 
4387 		// If extraBase has changed...
4388 		if (fc != cd.extraBase) {
4389 			AddBoxes(false);
4390 			fc -= cd.extraBase;
4391 			cd.selBox += fc;
4392 
4393 			// Ensure within legal limits
4394 			if (cd.selBox < 0)
4395 				cd.selBox = 0;
4396 			else if (cd.selBox >= NUM_RGROUP_BOXES)
4397 				cd.selBox = NUM_RGROUP_BOXES-1;
4398 
4399 			Select(cd.selBox, true);
4400 		}
4401 		break;
4402 
4403 	case S_END:			// End of a drag on the slider
4404 		break;
4405 
4406 	default:
4407 		break;
4408 	}
4409 }
4410 
4411 /**
4412  * Gets called at the start and end of a drag on a mixing desk slider,
4413  * and upon x-movement during such a drag.
4414  */
SlideMSlider(int x,SSFN fn)4415 static void SlideMSlider(int x, SSFN fn) {
4416 	static int newX = 0;	// FIXME: Avoid non-const global vars
4417 	int gotoX;
4418 	int index, i;
4419 
4420 	if (fn == S_END || fn == S_TIMEUP || fn == S_TIMEDN)
4421 		;
4422 	else if (!(cd.selBox & IS_SLIDER))
4423 		return;
4424 
4425 	// Work out the indices
4426 	index = cd.selBox & ~IS_MASK;
4427 	for (i = 0; i < g_numMdSlides; i++)
4428 		if (g_mdSlides[i].num == index)
4429 			break;
4430 	assert(i < g_numMdSlides);
4431 
4432 	switch (fn) {
4433 	case S_START:			// Start of a drag on the slider
4434 		// can use index as a throw-away value
4435 		GetAniPosition(g_mdSlides[i].obj, &newX, &index);
4436 		g_lX = g_sX = newX;
4437 		break;
4438 
4439 	case S_SLIDE:			// X-movement during drag
4440 		if (x == 0)
4441 			return;
4442 
4443 		newX = newX + x;	// New x-position
4444 
4445 		if (newX < g_mdSlides[i].min)
4446 			gotoX = g_mdSlides[i].min;	// Below bottom limit
4447 		else if (newX > g_mdSlides[i].max)
4448 			gotoX = g_mdSlides[i].max;	// Above top limit
4449 		else
4450 			gotoX = newX;		// Hunky-Dory
4451 
4452 		// Move slider to new position
4453 		MultiMoveRelXY(g_mdSlides[i].obj, gotoX - g_sX, 0);
4454 		g_sX = gotoX;
4455 
4456 		if (g_lX != g_sX) {
4457 			*cd.box[index].ival = (g_sX - g_mdSlides[i].min)*cd.box[index].w/SLIDE_RANGE;
4458 			if (cd.box[index].boxFunc == MUSICVOL)
4459 				SetMidiVolume(*cd.box[index].ival);
4460 #ifdef MAC_OPTIONS
4461 			if (cd.box[index].boxFunc == MASTERVOL)
4462 				SetSystemVolume(*cd.box[index].ival);
4463 
4464 			if (cd.box[index].boxFunc == SAMPVOL)
4465 				SetSampleVolume(*cd.box[index].ival);
4466 #endif
4467 			g_lX = g_sX;
4468 		}
4469 		break;
4470 
4471 	case S_TIMEUP:
4472 	case S_TIMEDN:
4473 		gotoX = SLIDE_RANGE*(*cd.box[index].ival)/cd.box[index].w;
4474 		MultiSetAniX(g_mdSlides[i].obj, g_mdSlides[i].min+gotoX);
4475 
4476 		if (cd.box[index].boxFunc == MUSICVOL)
4477 			SetMidiVolume(*cd.box[index].ival);
4478 #ifdef MAC_OPTIONS
4479 		if (cd.box[index].boxFunc == MASTERVOL)
4480 			SetSystemVolume(*cd.box[index].ival);
4481 
4482 		if (cd.box[index].boxFunc == SAMPVOL)
4483 			SetSampleVolume(*cd.box[index].ival);
4484 #endif
4485 		break;
4486 
4487 	case S_END:			// End of a drag on the slider
4488 		AddBoxes(false);	// Might change position slightly
4489 		if (g_ino == INV_CONF && cd.box == subtitlesBox)
4490 			Select(_vm->_config->_language, false);
4491 		break;
4492 	}
4493 }
4494 
4495 /**
4496  * Called from ChangeingSize() during re-sizing.
4497  */
GettingTaller()4498 static void GettingTaller() {
4499 	if (g_SuppV) {
4500 		g_Ychange += g_SuppV;
4501 		if (g_Ycompensate == 'T')
4502 			g_InvD[g_ino].inventoryY += g_SuppV;
4503 		g_SuppV = 0;
4504 	}
4505 	while (g_Ychange > (ITEM_HEIGHT+1) && g_InvD[g_ino].NoofVicons < g_InvD[g_ino].MaxVicons) {
4506 		g_Ychange -= (ITEM_HEIGHT+1);
4507 		g_InvD[g_ino].NoofVicons++;
4508 		if (g_Ycompensate == 'T')
4509 			g_InvD[g_ino].inventoryY -= (ITEM_HEIGHT+1);
4510 	}
4511 	if (g_InvD[g_ino].NoofVicons < g_InvD[g_ino].MaxVicons) {
4512 		g_SuppV = g_Ychange;
4513 		g_Ychange = 0;
4514 		if (g_Ycompensate == 'T')
4515 			g_InvD[g_ino].inventoryY -= g_SuppV;
4516 	}
4517 }
4518 
4519 /**
4520  * Called from ChangeingSize() during re-sizing.
4521  */
GettingShorter()4522 static void GettingShorter() {
4523 	int StartNvi = g_InvD[g_ino].NoofVicons;
4524 	int StartUv = g_SuppV;
4525 
4526 	if (g_SuppV) {
4527 		g_Ychange += (g_SuppV - (ITEM_HEIGHT+1));
4528 		g_InvD[g_ino].NoofVicons++;
4529 		g_SuppV = 0;
4530 	}
4531 	while (g_Ychange < -(ITEM_HEIGHT+1) && g_InvD[g_ino].NoofVicons > g_InvD[g_ino].MinVicons) {
4532 		g_Ychange += (ITEM_HEIGHT+1);
4533 		g_InvD[g_ino].NoofVicons--;
4534 	}
4535 	if (g_InvD[g_ino].NoofVicons > g_InvD[g_ino].MinVicons && g_Ychange) {
4536 		g_SuppV = (ITEM_HEIGHT+1) + g_Ychange;
4537 		g_InvD[g_ino].NoofVicons--;
4538 		g_Ychange = 0;
4539 	}
4540 	if (g_Ycompensate == 'T')
4541 		g_InvD[g_ino].inventoryY += (ITEM_HEIGHT+1)*(StartNvi - g_InvD[g_ino].NoofVicons) - (g_SuppV - StartUv);
4542 }
4543 
4544 /**
4545  * Called from ChangeingSize() during re-sizing.
4546  */
GettingWider()4547 static void GettingWider() {
4548 	int StartNhi = g_InvD[g_ino].NoofHicons;
4549 	int StartUh = g_SuppH;
4550 
4551 	if (g_SuppH) {
4552 		g_Xchange += g_SuppH;
4553 		g_SuppH = 0;
4554 	}
4555 	while (g_Xchange > (ITEM_WIDTH+1) && g_InvD[g_ino].NoofHicons < g_InvD[g_ino].MaxHicons) {
4556 		g_Xchange -= (ITEM_WIDTH+1);
4557 		g_InvD[g_ino].NoofHicons++;
4558 	}
4559 	if (g_InvD[g_ino].NoofHicons < g_InvD[g_ino].MaxHicons) {
4560 		g_SuppH = g_Xchange;
4561 		g_Xchange = 0;
4562 	}
4563 	if (g_Xcompensate == 'L')
4564 		g_InvD[g_ino].inventoryX += (ITEM_WIDTH+1)*(StartNhi - g_InvD[g_ino].NoofHicons) - (g_SuppH - StartUh);
4565 }
4566 
4567 /**
4568  * Called from ChangeingSize() during re-sizing.
4569  */
GettingNarrower()4570 static void GettingNarrower() {
4571 	int StartNhi = g_InvD[g_ino].NoofHicons;
4572 	int StartUh = g_SuppH;
4573 
4574 	if (g_SuppH) {
4575 		g_Xchange += (g_SuppH - (ITEM_WIDTH+1));
4576 		g_InvD[g_ino].NoofHicons++;
4577 		g_SuppH = 0;
4578 	}
4579 	while (g_Xchange < -(ITEM_WIDTH+1) && g_InvD[g_ino].NoofHicons > g_InvD[g_ino].MinHicons) {
4580 		g_Xchange += (ITEM_WIDTH+1);
4581 		g_InvD[g_ino].NoofHicons--;
4582 	}
4583 	if (g_InvD[g_ino].NoofHicons > g_InvD[g_ino].MinHicons && g_Xchange) {
4584 		g_SuppH = (ITEM_WIDTH+1) + g_Xchange;
4585 		g_InvD[g_ino].NoofHicons--;
4586 		g_Xchange = 0;
4587 	}
4588 	if (g_Xcompensate == 'L')
4589 		g_InvD[g_ino].inventoryX += (ITEM_WIDTH+1)*(StartNhi - g_InvD[g_ino].NoofHicons) - (g_SuppH - StartUh);
4590 }
4591 
4592 
4593 /**
4594  * Called from Xmovement()/Ymovement() during re-sizing.
4595  */
ChangeingSize()4596 static void ChangeingSize() {
4597 	/* Make it taller or shorter if necessary. */
4598 	if (g_Ychange > 0)
4599 		GettingTaller();
4600 	else if (g_Ychange < 0)
4601 		GettingShorter();
4602 
4603 	/* Make it wider or narrower if necessary. */
4604 	if (g_Xchange > 0)
4605 		GettingWider();
4606 	else if (g_Xchange < 0)
4607 		GettingNarrower();
4608 
4609 	ConstructInventory(EMPTY);
4610 }
4611 
4612 /**
4613  * Called from cursor module when cursor moves while inventory is up.
4614  */
Xmovement(int x)4615 extern void Xmovement(int x) {
4616 	int aniX, aniY;
4617 	int i;
4618 
4619 	if (x && g_objArray[0] != NULL) {
4620 		switch (g_InvDragging) {
4621 		case ID_MOVE:
4622 			GetAniPosition(g_objArray[0], &g_InvD[g_ino].inventoryX, &aniY);
4623 			g_InvD[g_ino].inventoryX +=x;
4624 			MultiSetAniX(g_objArray[0], g_InvD[g_ino].inventoryX);
4625 			for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++)
4626 				MultiMoveRelXY(g_objArray[i], x, 0);
4627 			for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++)
4628 				MultiMoveRelXY(g_iconArray[i], x, 0);
4629 			break;
4630 
4631 		case ID_LEFT:
4632 		case ID_TLEFT:
4633 		case ID_BLEFT:
4634 			g_Xchange -= x;
4635 			ChangeingSize();
4636 			break;
4637 
4638 		case ID_RIGHT:
4639 		case ID_TRIGHT:
4640 		case ID_BRIGHT:
4641 			g_Xchange += x;
4642 			ChangeingSize();
4643 			break;
4644 
4645 		case ID_NONE:
4646 			GetCursorXY(&aniX, &aniY, false);
4647 			InvCursor(IC_AREA, aniX, aniY);
4648 			break;
4649 
4650 		case ID_MDCONT:
4651 			SlideMSlider(x, S_SLIDE);
4652 			break;
4653 
4654 		default:
4655 			break;
4656 		}
4657 	}
4658 }
4659 
4660 /**
4661  * Called from cursor module when cursor moves while inventory is up.
4662  */
Ymovement(int y)4663 extern void Ymovement(int y) {
4664 	int aniX, aniY;
4665 	int i;
4666 
4667 	if (y && g_objArray[0] != NULL) {
4668 		switch (g_InvDragging) {
4669 		case ID_MOVE:
4670 			GetAniPosition(g_objArray[0], &aniX, &g_InvD[g_ino].inventoryY);
4671 			g_InvD[g_ino].inventoryY +=y;
4672 			MultiSetAniY(g_objArray[0], g_InvD[g_ino].inventoryY);
4673 			for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++)
4674 				MultiMoveRelXY(g_objArray[i], 0, y);
4675 			for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++)
4676 				MultiMoveRelXY(g_iconArray[i], 0, y);
4677 			break;
4678 
4679 		case ID_SLIDE:
4680 			SlideSlider(y, S_SLIDE);
4681 			break;
4682 
4683 		case ID_CSLIDE:
4684 			SlideCSlider(y, S_SLIDE);
4685 			break;
4686 
4687 		case ID_BOTTOM:
4688 		case ID_BLEFT:
4689 		case ID_BRIGHT:
4690 			g_Ychange += y;
4691 			ChangeingSize();
4692 			break;
4693 
4694 		case ID_TOP:
4695 		case ID_TLEFT:
4696 		case ID_TRIGHT:
4697 			g_Ychange -= y;
4698 			ChangeingSize();
4699 			break;
4700 
4701 		case ID_NONE:
4702 			GetCursorXY(&aniX, &aniY, false);
4703 			InvCursor(IC_AREA, aniX, aniY);
4704 			break;
4705 
4706 		default:
4707 			break;
4708 		}
4709 	}
4710 }
4711 
4712 /**
4713  * Called when a drag is commencing.
4714  */
InvDragStart()4715 static void InvDragStart() {
4716 	int curX, curY;		// cursor's animation position
4717 
4718 	GetCursorXY(&curX, &curY, false);
4719 
4720 	/*
4721 	 * Do something different for Save/Restore screens
4722 	 */
4723 	if (g_ino == INV_CONF) {
4724 		int	whichbox;
4725 
4726 		whichbox = WhichMenuBox(curX, curY, true);
4727 
4728 		if (whichbox == IB_SLIDE) {
4729 			g_InvDragging = ID_CSLIDE;
4730 			SlideCSlider(0, S_START);
4731 		} else if (whichbox > 0 && (whichbox & IS_MASK)) {
4732 			g_InvDragging = ID_MDCONT;	// Mixing desk control
4733 			cd.selBox = whichbox;
4734 			SlideMSlider(0, S_START);
4735 		}
4736 		return;
4737 	}
4738 
4739 	/*
4740 	 * Normal operation
4741 	 */
4742 	switch (InvArea(curX, curY)) {
4743 	case I_HEADER:
4744 		if (g_InvD[g_ino].bMoveable) {
4745 			g_InvDragging = ID_MOVE;
4746 		}
4747 		break;
4748 
4749 	case I_SLIDE:
4750 		g_InvDragging = ID_SLIDE;
4751 		SlideSlider(0, S_START);
4752 		break;
4753 
4754 	case I_BOTTOM:
4755 		if (g_InvD[g_ino].resizable) {
4756 			g_Ychange = 0;
4757 			g_InvDragging = ID_BOTTOM;
4758 			g_Ycompensate = 'B';
4759 		}
4760 		break;
4761 
4762 	case I_TOP:
4763 		if (g_InvD[g_ino].resizable) {
4764 			g_Ychange = 0;
4765 			g_InvDragging = ID_TOP;
4766 			g_Ycompensate = 'T';
4767 		}
4768 		break;
4769 
4770 	case I_LEFT:
4771 		if (g_InvD[g_ino].resizable) {
4772 			g_Xchange = 0;
4773 			g_InvDragging = ID_LEFT;
4774 			g_Xcompensate = 'L';
4775 		}
4776 		break;
4777 
4778 	case I_RIGHT:
4779 		if (g_InvD[g_ino].resizable) {
4780 			g_Xchange = 0;
4781 			g_InvDragging = ID_RIGHT;
4782 			g_Xcompensate = 'R';
4783 		}
4784 		break;
4785 
4786 	case I_TLEFT:
4787 		if (g_InvD[g_ino].resizable) {
4788 			g_Ychange = 0;
4789 			g_Ycompensate = 'T';
4790 			g_Xchange = 0;
4791 			g_Xcompensate = 'L';
4792 			g_InvDragging = ID_TLEFT;
4793 		}
4794 		break;
4795 
4796 	case I_TRIGHT:
4797 		if (g_InvD[g_ino].resizable) {
4798 			g_Ychange = 0;
4799 			g_Ycompensate = 'T';
4800 			g_Xchange = 0;
4801 			g_Xcompensate = 'R';
4802 			g_InvDragging = ID_TRIGHT;
4803 		}
4804 		break;
4805 
4806 	case I_BLEFT:
4807 		if (g_InvD[g_ino].resizable) {
4808 			g_Ychange = 0;
4809 			g_Ycompensate = 'B';
4810 			g_Xchange = 0;
4811 			g_Xcompensate = 'L';
4812 			g_InvDragging = ID_BLEFT;
4813 		}
4814 		break;
4815 
4816 	case I_BRIGHT:
4817 		if (g_InvD[g_ino].resizable) {
4818 			g_Ychange = 0;
4819 			g_Ycompensate = 'B';
4820 			g_Xchange = 0;
4821 			g_Xcompensate = 'R';
4822 			g_InvDragging = ID_BRIGHT;
4823 		}
4824 		break;
4825 	}
4826 }
4827 
4828 /**
4829  * Called when a drag is over.
4830  */
InvDragEnd()4831 static void InvDragEnd() {
4832 	int curX, curY;		// cursor's animation position
4833 
4834 	GetCursorXY(&curX, &curY, false);
4835 
4836 	if (g_InvDragging != ID_NONE) {
4837 		if (g_InvDragging == ID_SLIDE) {
4838 			SlideSlider(0, S_END);
4839 		} else if (g_InvDragging == ID_CSLIDE) {
4840 			;	// No action
4841 		} else if (g_InvDragging == ID_MDCONT) {
4842 			SlideMSlider(0, S_END);
4843 		} else if (g_InvDragging == ID_MOVE) {
4844 			;	// No action
4845 		} else {
4846 			// Were re-sizing. Redraw the whole thing.
4847 			DumpDobjArray();
4848 			DumpObjArray();
4849 			ConstructInventory(FULL);
4850 
4851 			// If this was the maximised, it no longer is!
4852 			if (g_InventoryMaximised) {
4853 				g_InventoryMaximised = false;
4854 				g_InvD[g_ino].otherX = g_InvD[g_ino].inventoryX;
4855 				g_InvD[g_ino].otherY = g_InvD[g_ino].inventoryY;
4856 			}
4857 		}
4858 
4859 		g_InvDragging = ID_NONE;
4860 		ProcessedProvisional();
4861 	}
4862 
4863 	// Cursor could well now be inappropriate
4864 	InvCursor(IC_AREA, curX, curY);
4865 
4866 	g_Xchange = g_Ychange = 0;		// Probably no need, but does no harm!
4867 }
4868 
MenuDown(int lines)4869 static bool MenuDown(int lines) {
4870 	if (cd.box == loadBox || cd.box == saveBox) {
4871 		if (cd.extraBase < MAX_SAVED_FILES - NUM_RGROUP_BOXES) {
4872 			FirstFile(cd.extraBase + lines);
4873 			AddBoxes(true);
4874 			return true;
4875 		}
4876 	} else if (cd.box == hopperBox1) {
4877 		if (cd.extraBase < g_numScenes - NUM_RGROUP_BOXES) {
4878 			FirstScene(cd.extraBase + lines);
4879 			AddBoxes(true);
4880 			return true;
4881 		}
4882 	} else if (cd.box == hopperBox2) {
4883 		if (cd.extraBase < g_numEntries - NUM_RGROUP_BOXES) {
4884 			FirstEntry(cd.extraBase + lines);
4885 			AddBoxes(true);
4886 			return true;
4887 		}
4888 	}
4889 	return false;
4890 }
4891 
MenuUp(int lines)4892 static bool MenuUp(int lines) {
4893 	if (cd.extraBase > 0) {
4894 		if (cd.box == loadBox || cd.box == saveBox)
4895 			FirstFile(cd.extraBase - lines);
4896 		else if (cd.box == hopperBox1)
4897 			FirstScene(cd.extraBase - lines);
4898 		else if (cd.box == hopperBox2)
4899 			FirstEntry(cd.extraBase - lines);
4900 		else
4901 			return false;
4902 
4903 		AddBoxes(true);
4904 		return true;
4905 	}
4906 	return false;
4907 }
4908 
MenuRollDown()4909 static void MenuRollDown() {
4910 	if (MenuDown(1)) {
4911 		if (cd.selBox > 0)
4912 			cd.selBox--;
4913 		Select(cd.selBox, true);
4914 	}
4915 }
4916 
MenuRollUp()4917 static void MenuRollUp() {
4918 	if (MenuUp(1)) {
4919 		if (cd.selBox < NUM_RGROUP_BOXES - 1)
4920 			cd.selBox++;
4921 		Select(cd.selBox, true);
4922 	}
4923 }
4924 
MenuPageDown()4925 static void MenuPageDown() {
4926 	if (MenuDown(NUM_RGROUP_BOXES - 1)) {
4927 		cd.selBox = NUM_RGROUP_BOXES - 1;
4928 		Select(cd.selBox, true);
4929 	}
4930 }
4931 
MenuPageUp()4932 static void MenuPageUp() {
4933 	if (MenuUp(NUM_RGROUP_BOXES - 1)) {
4934 		cd.selBox = 0;
4935 		Select(cd.selBox, true);
4936 	}
4937 }
4938 
InventoryDown()4939 static void InventoryDown() {
4940 	// This code is a copy of the IB_SLIDE_DOWN case in InvWalkTo
4941 	// TODO: So share this duplicate code
4942 	if (g_InvD[g_ino].NoofVicons == 1)
4943 		if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems)
4944 			g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
4945 	for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++) {
4946 		if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems)
4947 			g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
4948 	}
4949 	g_ItemsChanged = true;
4950 }
4951 
InventoryUp()4952 static void InventoryUp() {
4953 	// This code is a copy of the I_SLIDE_UP case in InvWalkTo
4954 	// TODO: So share this duplicate code
4955 	if (g_InvD[g_ino].NoofVicons == 1)
4956 		g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
4957 	for (int i = 1; i < g_InvD[g_ino].NoofVicons; i++)
4958 		g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
4959 	if (g_InvD[g_ino].FirstDisp < 0)
4960 		g_InvD[g_ino].FirstDisp = 0;
4961 	g_ItemsChanged = true;
4962 }
4963 
4964 /**************************************************************************/
4965 /************** Incoming events - further processing **********************/
4966 /**************************************************************************/
4967 
4968 /**
4969  * MenuAction
4970  */
MenuAction(int i,bool dbl)4971 static void MenuAction(int i, bool dbl) {
4972 
4973 	if (i >= 0) {
4974 		switch (cd.box[i].boxType) {
4975 		case FLIP:
4976 			if (dbl) {
4977 				*(cd.box[i].ival) ^= 1;	// XOR with true
4978 				AddBoxes(false);
4979 			}
4980 			break;
4981 
4982 		case TOGGLE:
4983 		case TOGGLE1:
4984 		case TOGGLE2:
4985 			if (!g_buttonEffect.bButAnim) {
4986 				g_buttonEffect.bButAnim = true;
4987 				g_buttonEffect.box = &cd.box[i];
4988 				g_buttonEffect.press = false;
4989 			}
4990 			break;
4991 
4992 		case RGROUP:
4993 			if (dbl) {
4994 				// Already highlighted
4995 				switch (cd.box[i].boxFunc) {
4996 				case SAVEGAME:
4997 					KillInventory();
4998 					InvSaveGame();
4999 					break;
5000 				case LOADGAME:
5001 					KillInventory();
5002 					InvLoadGame();
5003 					break;
5004 				case HOPPER2:
5005 					KillInventory();
5006 					OpenMenu(HOPPER_MENU2);
5007 					break;
5008 				case BF_CHANGESCENE:
5009 					KillInventory();
5010 					HopAction();
5011 					FreeSceneHopper();
5012 					break;
5013 				default:
5014 					break;
5015 				}
5016 			} else {
5017 				Select(i, false);
5018 			}
5019 			break;
5020 
5021 		case FRGROUP:
5022 			if (dbl) {
5023 				Select(i, false);
5024 				LanguageChange();
5025 			} else {
5026 				Select(i, false);
5027 			}
5028 			break;
5029 
5030 		case AAGBUT:
5031 		case ARSGBUT:
5032 		case ARSBUT:
5033 		case AABUT:
5034 		case AATBUT:
5035 			if (g_buttonEffect.bButAnim)
5036 				break;
5037 
5038 			g_buttonEffect.bButAnim = true;
5039 			g_buttonEffect.box = &cd.box[i];
5040 			g_buttonEffect.press = true;
5041 			break;
5042 		default:
5043 			break;
5044 		}
5045 	} else {
5046 		ConfActionSpecial(i);
5047 	}
5048 }
5049 
ConfActionSpecial(int i)5050 static void ConfActionSpecial(int i) {
5051 	switch (i) {
5052 	case IB_NONE:
5053 		break;
5054 	case IB_UP:	// Scroll up
5055 		if (cd.extraBase > 0) {
5056 			if ((cd.box == loadBox) || (cd.box == saveBox))
5057 				FirstFile(cd.extraBase - 1);
5058 			else if (cd.box == hopperBox1)
5059 				FirstScene(cd.extraBase - 1);
5060 			else if (cd.box == hopperBox2)
5061 				FirstEntry(cd.extraBase - 1);
5062 
5063 			AddBoxes(true);
5064 			if (cd.selBox < NUM_RGROUP_BOXES - 1)
5065 				cd.selBox += 1;
5066 			Select(cd.selBox, true);
5067 		}
5068 		break;
5069 	case IB_DOWN:	// Scroll down
5070 		if ((cd.box == loadBox) || (cd.box == saveBox)) {
5071 			if (cd.extraBase < MAX_SAVED_FILES - NUM_RGROUP_BOXES) {
5072 				FirstFile(cd.extraBase + 1);
5073 				AddBoxes(true);
5074 				if (cd.selBox)
5075 					cd.selBox -= 1;
5076 				Select(cd.selBox, true);
5077 			}
5078 		} else if (cd.box == hopperBox1) {
5079 			if (cd.extraBase < g_numScenes - NUM_RGROUP_BOXES) {
5080 				FirstScene(cd.extraBase + 1);
5081 				AddBoxes(true);
5082 				if (cd.selBox)
5083 					cd.selBox -= 1;
5084 				Select(cd.selBox, true);
5085 			}
5086 		} else if (cd.box == hopperBox2) {
5087 			if (cd.extraBase < g_numEntries - NUM_RGROUP_BOXES) {
5088 				FirstEntry(cd.extraBase + 1);
5089 				AddBoxes(true);
5090 				if (cd.selBox)
5091 					cd.selBox -= 1;
5092 				Select(cd.selBox, true);
5093 			}
5094 		}
5095 		break;
5096 
5097 	case IB_SLIDE_UP:
5098 		MenuPageUp();
5099 		break;
5100 
5101 	case IB_SLIDE_DOWN:
5102 		MenuPageDown();
5103 		break;
5104 	}
5105 }
5106 // SLIDE_UP and SLIDE_DOWN on d click??????
5107 
InvPutDown(int index)5108 static void InvPutDown(int index) {
5109 	int aniX, aniY;
5110 			// index is the drop position
5111 	int hiIndex;	// Current position of held item (if in)
5112 
5113 	// Find where the held item is positioned in this inventory (if it is)
5114 	for (hiIndex = 0; hiIndex < g_InvD[g_ino].NoofItems; hiIndex++)
5115 		if (g_InvD[g_ino].contents[hiIndex] == g_heldItem)
5116 			break;
5117 
5118 	// If drop position would leave a gap, move it up
5119 	if (index >= g_InvD[g_ino].NoofItems) {
5120 		if (hiIndex == g_InvD[g_ino].NoofItems)	// Not in, add it
5121 			index = g_InvD[g_ino].NoofItems;
5122 		else
5123 			index = g_InvD[g_ino].NoofItems - 1;
5124 	}
5125 
5126 	if (hiIndex == g_InvD[g_ino].NoofItems) {	// Not in, add it
5127 		if (g_InvD[g_ino].NoofItems < g_InvD[g_ino].MaxInvObj) {
5128 			g_InvD[g_ino].NoofItems++;
5129 
5130 			// Don't leave it in the other inventory!
5131 			if (InventoryPos(g_heldItem) != INV_HELDNOTIN)
5132 				RemFromInventory(g_ino == INV_1 ? INV_2 : INV_1, g_heldItem);
5133 		} else {
5134 			// No room at the inn!
5135 			return;
5136 		}
5137 	}
5138 
5139 	// Position it in the inventory
5140 	if (index < hiIndex) {
5141 		memmove(&g_InvD[g_ino].contents[index + 1], &g_InvD[g_ino].contents[index], (hiIndex-index)*sizeof(int));
5142 		g_InvD[g_ino].contents[index] = g_heldItem;
5143 	} else if (index > hiIndex) {
5144 		memmove(&g_InvD[g_ino].contents[hiIndex], &g_InvD[g_ino].contents[hiIndex+1], (index-hiIndex)*sizeof(int));
5145 		g_InvD[g_ino].contents[index] = g_heldItem;
5146 	} else {
5147 		g_InvD[g_ino].contents[index] = g_heldItem;
5148 	}
5149 
5150 	g_heldItem = INV_NOICON;
5151 	g_ItemsChanged = true;
5152 	DelAuxCursor();
5153 	RestoreMainCursor();
5154 	GetCursorXY(&aniX, &aniY, false);
5155 	InvCursor(IC_DROP, aniX, aniY);
5156 }
5157 
InvPdProcess(CORO_PARAM,const void * param)5158 static void InvPdProcess(CORO_PARAM, const void *param) {
5159 	// COROUTINE
5160 	CORO_BEGIN_CONTEXT;
5161 	CORO_END_CONTEXT(_ctx);
5162 
5163 	CORO_BEGIN_CODE(_ctx);
5164 
5165 	GetToken(TOKEN_LEFT_BUT);
5166 	CORO_SLEEP(_vm->_config->_dclickSpeed+1);
5167 	FreeToken(TOKEN_LEFT_BUT);
5168 
5169 	// get the stuff copied to process when it was created
5170 	const int *pindex = (const int *)param;
5171 
5172 	InvPutDown(*pindex);
5173 
5174 	CORO_END_CODE;
5175 }
5176 
InvPickup(int index)5177 static void InvPickup(int index) {
5178 	INV_OBJECT *invObj;
5179 
5180 	// Do nothing if not clicked on anything
5181 	if (index == NOOBJECT)
5182 		return;
5183 
5184 	// If not holding anything
5185 	if (g_heldItem == INV_NOICON && g_InvD[g_ino].contents[index] &&
5186 			(!TinselV2 || g_InvD[g_ino].contents[index] != g_heldItem)) {
5187 		// Pick-up
5188 		invObj = GetInvObject(g_InvD[g_ino].contents[index]);
5189 		g_thisIcon = g_InvD[g_ino].contents[index];
5190 		if (TinselV2)
5191 			InvTinselEvent(invObj, PICKUP, INV_PICKUP, index);
5192 		else if (invObj->hScript)
5193 			InvTinselEvent(invObj, WALKTO, INV_PICKUP, index);
5194 
5195 	} else if (g_heldItem != INV_NOICON) {
5196 		// Put-down
5197 		invObj = GetInvObject(g_heldItem);
5198 
5199 		// If DROPCODE set, send event, otherwise it's a putdown
5200 		if (invObj->attribute & IO_DROPCODE && invObj->hScript)
5201 			InvTinselEvent(invObj, PUTDOWN, INV_PICKUP, index);
5202 
5203 		else if (!(invObj->attribute & IO_ONLYINV1 && g_ino != INV_1)
5204 				&& !(invObj->attribute & IO_ONLYINV2 && g_ino != INV_2)) {
5205 			if (TinselV2)
5206 				InvPutDown(index);
5207 			else
5208 				CoroScheduler.createProcess(PID_TCODE, InvPdProcess, &index, sizeof(index));
5209 		}
5210 	}
5211 }
5212 
5213 /**
5214  * Handle WALKTO event (Pick up/put down event)
5215  */
InvWalkTo(const Common::Point & coOrds)5216 static void InvWalkTo(const Common::Point &coOrds) {
5217 	int i;
5218 
5219 	switch (InvArea(coOrds.x, coOrds.y)) {
5220 	case I_NOTIN:
5221 		if (g_ino == INV_CONV)
5222 			ConvAction(INV_CLOSEICON);
5223 		if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
5224 			FreeSceneHopper();
5225 		KillInventory();
5226 		break;
5227 
5228 	case I_SLIDE_UP:
5229 		if (g_InvD[g_ino].NoofVicons == 1)
5230 			g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
5231 		for (i = 1; i < g_InvD[g_ino].NoofVicons; i++)
5232 			g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
5233 		if (g_InvD[g_ino].FirstDisp < 0)
5234 			g_InvD[g_ino].FirstDisp = 0;
5235 		g_ItemsChanged = true;
5236 		break;
5237 
5238 	case I_UP:
5239 		g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
5240 		if (g_InvD[g_ino].FirstDisp < 0)
5241 			g_InvD[g_ino].FirstDisp = 0;
5242 		g_ItemsChanged = true;
5243 		break;
5244 
5245 	case I_SLIDE_DOWN:
5246 		if (g_InvD[g_ino].NoofVicons == 1)
5247 			if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems)
5248 				g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
5249 		for (i = 1; i < g_InvD[g_ino].NoofVicons; i++) {
5250 			if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems)
5251 				g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
5252 		}
5253 		g_ItemsChanged = true;
5254 		break;
5255 
5256 	case I_DOWN:
5257 		if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) {
5258 			g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
5259 			g_ItemsChanged = true;
5260 		}
5261 		break;
5262 
5263 	case I_BODY:
5264 		if (g_ino == INV_CONF) {
5265 			if (!g_InventoryHidden)
5266 				MenuAction(WhichMenuBox(coOrds.x, coOrds.y, false), false);
5267 		} else {
5268 			Common::Point pt = coOrds;
5269 			i = InvItem(pt, false);
5270 
5271 			// To cater for drop in dead space between icons,
5272 			// look 1 pixel right, then 1 down, then 1 right and down.
5273 			if (i == INV_NOICON && g_heldItem != INV_NOICON &&
5274 					(g_ino == INV_1 || g_ino == INV_2)) {
5275 				pt.x += 1;				// 1 to the right
5276 				i = InvItem(pt, false);
5277 				if (i == INV_NOICON) {
5278 					pt.x -= 1;			// 1 down
5279 					pt.y += 1;
5280 					i = InvItem(pt, false);
5281 					if (i == INV_NOICON) {
5282 						pt.x += 1;		// 1 down-right
5283 						i = InvItem(pt, false);
5284 					}
5285 				}
5286 			}
5287 
5288 			if (g_ino == INV_CONV) {
5289 				ConvAction(i);
5290 			} else
5291 				InvPickup(i);
5292 		}
5293 		break;
5294 	}
5295 }
5296 
InvAction()5297 static void InvAction() {
5298 	int index;
5299 	INV_OBJECT *invObj;
5300 	int aniX, aniY;
5301 	int i;
5302 
5303 	GetCursorXY(&aniX, &aniY, false);
5304 
5305 	switch (InvArea(aniX, aniY)) {
5306 	case I_BODY:
5307 		if (g_ino == INV_CONF) {
5308 			if (!g_InventoryHidden)
5309 				MenuAction(WhichMenuBox(aniX, aniY, false), true);
5310 		} else if (g_ino == INV_CONV) {
5311 			index = InvItem(&aniX, &aniY, false);
5312 			ConvAction(index);
5313 		} else {
5314 			index = InvItem(&aniX, &aniY, false);
5315 			if (index != INV_NOICON) {
5316 				if (g_InvD[g_ino].contents[index] && g_InvD[g_ino].contents[index] != g_heldItem) {
5317 					invObj = GetInvObject(g_InvD[g_ino].contents[index]);
5318 					if (TinselV2)
5319 						g_thisIcon = g_InvD[g_ino].contents[index];
5320 					if (TinselV2 || (invObj->hScript))
5321 						InvTinselEvent(invObj, ACTION, INV_ACTION, index);
5322 				}
5323 			}
5324 		}
5325 		break;
5326 
5327 	case I_HEADER:	// Maximise/unmaximise inventory
5328 		if (!g_InvD[g_ino].resizable)
5329 			break;
5330 
5331 		if (!g_InventoryMaximised) {
5332 			g_InvD[g_ino].sNoofHicons = g_InvD[g_ino].NoofHicons;
5333 			g_InvD[g_ino].sNoofVicons = g_InvD[g_ino].NoofVicons;
5334 			g_InvD[g_ino].NoofHicons = g_InvD[g_ino].MaxHicons;
5335 			g_InvD[g_ino].NoofVicons = g_InvD[g_ino].MaxVicons;
5336 			g_InventoryMaximised = true;
5337 
5338 			i = g_InvD[g_ino].inventoryX;
5339 			g_InvD[g_ino].inventoryX = g_InvD[g_ino].otherX;
5340 			g_InvD[g_ino].otherX = i;
5341 			i = g_InvD[g_ino].inventoryY;
5342 			g_InvD[g_ino].inventoryY = g_InvD[g_ino].otherY;
5343 			g_InvD[g_ino].otherY = i;
5344 		} else {
5345 			g_InvD[g_ino].NoofHicons = g_InvD[g_ino].sNoofHicons;
5346 			g_InvD[g_ino].NoofVicons = g_InvD[g_ino].sNoofVicons;
5347 			g_InventoryMaximised = false;
5348 
5349 			i = g_InvD[g_ino].inventoryX;
5350 			g_InvD[g_ino].inventoryX = g_InvD[g_ino].otherX;
5351 			g_InvD[g_ino].otherX = i;
5352 			i = g_InvD[g_ino].inventoryY;
5353 			g_InvD[g_ino].inventoryY = g_InvD[g_ino].otherY;
5354 			g_InvD[g_ino].otherY = i;
5355 		}
5356 
5357 		// Delete current, and re-draw
5358 		DumpDobjArray();
5359 		DumpObjArray();
5360 		ConstructInventory(FULL);
5361 		break;
5362 
5363 	case I_UP:
5364 		g_InvD[g_ino].FirstDisp -= g_InvD[g_ino].NoofHicons;
5365 		if (g_InvD[g_ino].FirstDisp < 0)
5366 			g_InvD[g_ino].FirstDisp = 0;
5367 		g_ItemsChanged = true;
5368 		break;
5369 	case I_DOWN:
5370 		if (g_InvD[g_ino].FirstDisp + g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons < g_InvD[g_ino].NoofItems) {
5371 			g_InvD[g_ino].FirstDisp += g_InvD[g_ino].NoofHicons;
5372 			g_ItemsChanged = true;
5373 		}
5374 		break;
5375 	}
5376 
5377 }
5378 
InvLook(const Common::Point & coOrds)5379 static void InvLook(const Common::Point &coOrds) {
5380 	int index;
5381 	INV_OBJECT *invObj;
5382 	Common::Point pt = coOrds;
5383 
5384 	switch (InvArea(pt.x, pt.y)) {
5385 	case I_BODY:
5386 		index = InvItem(pt, false);
5387 		if (index != INV_NOICON) {
5388 			if (g_InvD[g_ino].contents[index] && g_InvD[g_ino].contents[index] != g_heldItem) {
5389 				invObj = GetInvObject(g_InvD[g_ino].contents[index]);
5390 				if (invObj->hScript)
5391 					InvTinselEvent(invObj, LOOK, INV_LOOK, index);
5392 			}
5393 		}
5394 		break;
5395 
5396 	case I_NOTIN:
5397 		if (g_ino == INV_CONV)
5398 			ConvAction(INV_CLOSEICON);
5399 		KillInventory();
5400 		break;
5401 	}
5402 }
5403 
5404 
5405 /**************************************************************************/
5406 /********************* Incoming events ************************************/
5407 /**************************************************************************/
5408 
EventToInventory(PLR_EVENT pEvent,const Common::Point & coOrds)5409 extern void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds) {
5410 	if (g_InventoryHidden)
5411 		return;
5412 
5413 	switch (pEvent) {
5414 	case PLR_PROV_WALKTO:
5415 		if (MenuActive()) {
5416 			ProcessedProvisional();
5417 			InvWalkTo(coOrds);
5418 		}
5419 		break;
5420 
5421 	case PLR_WALKTO:		// PLR_SLEFT
5422 		InvWalkTo(coOrds);
5423 		break;
5424 
5425 	case INV_LOOK:			// PLR_SRIGHT
5426 		if (MenuActive())
5427 			InvWalkTo(coOrds);
5428 		else
5429 			InvLook(coOrds);
5430 		break;
5431 
5432 	case PLR_ACTION:		// PLR_DLEFT
5433 		if (g_InvDragging != ID_MDCONT)
5434 			InvDragEnd();
5435 		InvAction();
5436 		break;
5437 
5438 	case PLR_DRAG1_START:		// Left drag start
5439 		InvDragStart();
5440 		break;
5441 
5442 	case PLR_DRAG1_END:		// Left drag end
5443 		InvDragEnd();
5444 		break;
5445 
5446 	case PLR_ESCAPE:
5447 		if (MenuActive()) {
5448 			if (cd.box != optionBox && cd.box != hopperBox1 && cd.box != hopperBox2)
5449 				g_bReOpenMenu = true;
5450 			if ((cd.box == hopperBox1) || (cd.box == hopperBox2))
5451 				FreeSceneHopper();
5452 		}
5453 		CloseInventory();
5454 		break;
5455 
5456 	case PLR_PGDN:
5457 		if (g_ino == INV_MENU) {
5458 			// Load or Save screen
5459 			MenuPageDown();
5460 		} else {
5461 			// Inventory window
5462 			InventoryDown();
5463 		}
5464 		break;
5465 
5466 	case PLR_PGUP:
5467 		if (g_ino == INV_MENU) {
5468 			// Load or Save screen
5469 			MenuPageUp();
5470 		} else {
5471 			// Inventory window
5472 			InventoryUp();
5473 		}
5474 		break;
5475 
5476 	case PLR_WHEEL_DOWN:
5477 		if (g_ino == INV_MENU) {
5478 			// Load or Save screen
5479 			MenuRollDown();
5480 		} else {
5481 			// Inventory window
5482 			InventoryDown();
5483 		}
5484 		break;
5485 
5486 	case PLR_WHEEL_UP:
5487 		if (g_ino == INV_MENU) {
5488 			// Load or Save screen
5489 			MenuRollUp();
5490 		} else {
5491 			// Inventory window
5492 			InventoryUp();
5493 		}
5494 		break;
5495 
5496 	case PLR_HOME:
5497 		if (g_ino == INV_MENU) {
5498 			// Load or Save screen
5499 			if (cd.box == loadBox || cd.box == saveBox)
5500 				FirstFile(0);
5501 			else if (cd.box == hopperBox1)
5502 				FirstScene(0);
5503 			else if (cd.box == hopperBox2)
5504 				FirstEntry(0);
5505 			else
5506 				break;
5507 
5508 			AddBoxes(true);
5509 			cd.selBox = 0;
5510 			Select(cd.selBox, true);
5511 		} else {
5512 			// Inventory window
5513 			g_InvD[g_ino].FirstDisp = 0;
5514 			g_ItemsChanged = true;
5515 		}
5516 		break;
5517 
5518 	case PLR_END:
5519 		if (g_ino == INV_MENU) {
5520 			// Load or Save screen
5521 			if (cd.box == loadBox || cd.box == saveBox)
5522 				FirstFile(MAX_SAVED_FILES);	// Will get reduced to appropriate value
5523 			else if (cd.box == hopperBox1)
5524 				FirstScene(g_numScenes);		// Will get reduced to appropriate value
5525 			else if (cd.box == hopperBox2)
5526 				FirstEntry(g_numEntries);		// Will get reduced to appropriate value
5527 			else
5528 				break;
5529 
5530 			AddBoxes(true);
5531 			cd.selBox = 0;
5532 			Select(cd.selBox, true);
5533 		} else {
5534 			// Inventory window
5535 			g_InvD[g_ino].FirstDisp = g_InvD[g_ino].NoofItems - g_InvD[g_ino].NoofHicons*g_InvD[g_ino].NoofVicons;
5536 			if (g_InvD[g_ino].FirstDisp < 0)
5537 				g_InvD[g_ino].FirstDisp = 0;
5538 			g_ItemsChanged = true;
5539 		}
5540 		break;
5541 	default:
5542 		break;
5543 	}
5544 }
5545 
5546 /**************************************************************************/
5547 /************************* Odds and Ends **********************************/
5548 /**************************************************************************/
5549 
5550 /**
5551  * Called from Glitter function invdepict()
5552  * Changes (permanently) the animation film for that object.
5553  */
SetObjectFilm(int object,SCNHANDLE hFilm)5554 extern void SetObjectFilm(int object, SCNHANDLE hFilm) {
5555 	INV_OBJECT *invObj;
5556 
5557 	invObj = GetInvObject(object);
5558 	invObj->hIconFilm = hFilm;
5559 
5560 	if (g_heldItem != object)
5561 		g_ItemsChanged = true;
5562 }
5563 
5564 /**
5565  * (Un)serialize the inventory data for save/restore game.
5566  */
syncInvInfo(Common::Serializer & s)5567 extern void syncInvInfo(Common::Serializer &s) {
5568 	for (int i = 0; i < NUM_INV; i++) {
5569 		s.syncAsSint32LE(g_InvD[i].MinHicons);
5570 		s.syncAsSint32LE(g_InvD[i].MinVicons);
5571 		s.syncAsSint32LE(g_InvD[i].MaxHicons);
5572 		s.syncAsSint32LE(g_InvD[i].MaxVicons);
5573 		s.syncAsSint32LE(g_InvD[i].NoofHicons);
5574 		s.syncAsSint32LE(g_InvD[i].NoofVicons);
5575 		for (int j = 0; j < MAX_ININV; j++) {
5576 			s.syncAsSint32LE(g_InvD[i].contents[j]);
5577 		}
5578 		s.syncAsSint32LE(g_InvD[i].NoofItems);
5579 		s.syncAsSint32LE(g_InvD[i].FirstDisp);
5580 		s.syncAsSint32LE(g_InvD[i].inventoryX);
5581 		s.syncAsSint32LE(g_InvD[i].inventoryY);
5582 		s.syncAsSint32LE(g_InvD[i].otherX);
5583 		s.syncAsSint32LE(g_InvD[i].otherY);
5584 		s.syncAsSint32LE(g_InvD[i].MaxInvObj);
5585 		s.syncAsSint32LE(g_InvD[i].hInvTitle);
5586 		s.syncAsSint32LE(g_InvD[i].resizable);
5587 		s.syncAsSint32LE(g_InvD[i].bMoveable);
5588 		s.syncAsSint32LE(g_InvD[i].sNoofHicons);
5589 		s.syncAsSint32LE(g_InvD[i].sNoofVicons);
5590 		s.syncAsSint32LE(g_InvD[i].bMax);
5591 	}
5592 
5593 	if (TinselV2) {
5594 		for (int i = 0; i < g_numObjects; ++i)
5595 			s.syncAsUint32LE(g_invFilms[i]);
5596 		s.syncAsUint32LE(g_heldFilm);
5597 	}
5598 }
5599 
5600 /**************************************************************************/
5601 /************************ Initialisation stuff ****************************/
5602 /**************************************************************************/
5603 
5604 /**
5605  * Called from PlayGame(), stores handle to inventory objects' data -
5606  * its id, animation film and Glitter script.
5607  */
5608 // Note: the SCHANDLE type here has been changed to a void*
RegisterIcons(void * cptr,int num)5609 extern void RegisterIcons(void *cptr, int num) {
5610 	g_numObjects = num;
5611 	g_invObjects = (INV_OBJECT *) cptr;
5612 
5613 	if (TinselV0) {
5614 		// In Tinsel 0, the INV_OBJECT structure doesn't have an attributes field, so we
5615 		// need to 'unpack' the source structures into the standard Tinsel v1/v2 format
5616 		MEM_NODE *node = MemoryAllocFixed(g_numObjects * sizeof(INV_OBJECT));
5617 		assert(node);
5618 		g_invObjects = (INV_OBJECT *)MemoryDeref(node);
5619 		assert(g_invObjects);
5620 		byte *srcP = (byte *)cptr;
5621 		INV_OBJECT *destP = (INV_OBJECT *)g_invObjects;
5622 
5623 		for (int i = 0; i < num; ++i, ++destP, srcP += 12) {
5624 			memmove(destP, srcP, 12);
5625 			destP->attribute = 0;
5626 		}
5627 	} else if (TinselV2) {
5628 		if (g_invFilms == NULL) {
5629 			// First time - allocate memory
5630 			MEM_NODE *node = MemoryAllocFixed(g_numObjects * sizeof(SCNHANDLE));
5631 			assert(node);
5632 			g_invFilms = (SCNHANDLE *)MemoryDeref(node);
5633 			if (g_invFilms == NULL)
5634 				error(NO_MEM, "inventory scripts");
5635 			memset(g_invFilms, 0, g_numObjects * sizeof(SCNHANDLE));
5636 		}
5637 
5638 
5639 		// Add defined permanent conversation icons
5640 		// and store all the films separately
5641 		int i;
5642 		INV_OBJECT *pio;
5643 		for (i = 0, pio = g_invObjects; i < g_numObjects; i++, pio++) {
5644 			if (pio->attribute & PERMACONV)
5645 				PermaConvIcon(pio->id, pio->attribute & CONVENDITEM);
5646 
5647 			g_invFilms[i] = pio->hIconFilm;
5648 		}
5649 	}
5650 }
5651 
5652 /**
5653  * Called from Glitter function 'dec_invw()' - Declare the bits that the
5654  * inventory windows are constructed from, and special cursors.
5655  */
setInvWinParts(SCNHANDLE hf)5656 extern void setInvWinParts(SCNHANDLE hf) {
5657 #ifdef DEBUG
5658 	const FILM *pfilm;
5659 #endif
5660 
5661 	g_hWinParts = hf;
5662 
5663 #ifdef DEBUG
5664 	pfilm = (const FILM *)LockMem(hf);
5665 	assert(FROM_32(pfilm->numreels) >= (uint32)(TinselV2 ? T2_HOPEDFORREELS : T1_HOPEDFORREELS)); // not as many reels as expected
5666 #endif
5667 }
5668 
5669 /**
5670  * Called from Glitter function 'dec_flags()' - Declare the language
5671  * flag films
5672  */
setFlagFilms(SCNHANDLE hf)5673 extern void setFlagFilms(SCNHANDLE hf) {
5674 #ifdef DEBUG
5675 	const FILM *pfilm;
5676 #endif
5677 
5678 	g_flagFilm = hf;
5679 
5680 #ifdef DEBUG
5681 	pfilm = (const FILM *)LockMem(hf);
5682 	assert(FROM_32(pfilm->numreels) >= HOPEDFORFREELS); // not as many reels as expected
5683 #endif
5684 }
5685 
5686 /**
5687  * Called from Glitter function 'DecCStrings()'
5688  */
setConfigStrings(SCNHANDLE * tp)5689 extern void setConfigStrings(SCNHANDLE *tp) {
5690 	memcpy(g_configStrings, tp, sizeof(g_configStrings));
5691 }
5692 
5693 /**
5694  * Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
5695  * - Declare the heading text and dimensions etc.
5696  */
idec_inv(int num,SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight,int startx,int starty,bool moveable)5697 extern void idec_inv(int num, SCNHANDLE text, int MaxContents,
5698 		int MinWidth, int MinHeight,
5699 		int StartWidth, int StartHeight,
5700 		int MaxWidth, int MaxHeight,
5701 		int startx, int starty, bool moveable) {
5702 	if (MaxWidth > MAXHICONS)
5703 		MaxWidth = MAXHICONS;		// Max window width
5704 	if (MaxHeight > MAXVICONS)
5705 		MaxHeight = MAXVICONS;		// Max window height
5706 	if (MaxContents > MAX_ININV)
5707 		MaxContents = MAX_ININV;	// Max contents
5708 
5709 	if (StartWidth > MaxWidth)
5710 		StartWidth = MaxWidth;
5711 	if (StartHeight > MaxHeight)
5712 		StartHeight = MaxHeight;
5713 
5714 	g_InventoryState = IDLE_INV;
5715 
5716 	g_InvD[num].MaxHicons = MaxWidth;
5717 	g_InvD[num].MinHicons = MinWidth;
5718 	g_InvD[num].MaxVicons = MaxHeight;
5719 	g_InvD[num].MinVicons = MinHeight;
5720 
5721 	g_InvD[num].NoofHicons = StartWidth;
5722 	g_InvD[num].NoofVicons = StartHeight;
5723 
5724 	memset(g_InvD[num].contents, 0, sizeof(g_InvD[num].contents));
5725 	g_InvD[num].NoofItems = 0;
5726 
5727 	g_InvD[num].FirstDisp = 0;
5728 
5729 	g_InvD[num].inventoryX = startx;
5730 	g_InvD[num].inventoryY = starty;
5731 	g_InvD[num].otherX = 21;
5732 	g_InvD[num].otherY = 15;
5733 
5734 	g_InvD[num].MaxInvObj = MaxContents;
5735 
5736 	g_InvD[num].hInvTitle = text;
5737 
5738 	if (MaxWidth != MinWidth && MaxHeight != MinHeight)
5739 		g_InvD[num].resizable = true;
5740 
5741 	g_InvD[num].bMoveable = moveable;
5742 
5743 	g_InvD[num].bMax = false;
5744 }
5745 
5746 /**
5747  * Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
5748  * - Declare the heading text and dimensions etc.
5749  */
idec_convw(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)5750 extern void idec_convw(SCNHANDLE text, int MaxContents,
5751 		int MinWidth, int MinHeight,
5752 		int StartWidth, int StartHeight,
5753 		int MaxWidth, int MaxHeight) {
5754 	idec_inv(INV_CONV, text, MaxContents, MinWidth, MinHeight,
5755 			StartWidth, StartHeight, MaxWidth, MaxHeight,
5756 			20, 8, true);
5757 }
5758 
5759 /**
5760  * Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
5761  * - Declare the heading text and dimensions etc.
5762  */
idec_inv1(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)5763 extern void idec_inv1(SCNHANDLE text, int MaxContents,
5764 		int MinWidth, int MinHeight,
5765 		int StartWidth, int StartHeight,
5766 		int MaxWidth, int MaxHeight) {
5767 	idec_inv(INV_1, text, MaxContents, MinWidth, MinHeight,
5768 			StartWidth, StartHeight, MaxWidth, MaxHeight,
5769 			100, 100, true);
5770 }
5771 
5772 /**
5773  * Called from Glitter functions: dec_convw()/dec_inv1()/dec_inv2()
5774  * - Declare the heading text and dimensions etc.
5775  */
idec_inv2(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)5776 extern void idec_inv2(SCNHANDLE text, int MaxContents,
5777 		int MinWidth, int MinHeight,
5778 		int StartWidth, int StartHeight,
5779 		int MaxWidth, int MaxHeight) {
5780 	idec_inv(INV_2, text, MaxContents, MinWidth, MinHeight,
5781 			StartWidth, StartHeight, MaxWidth, MaxHeight,
5782 			100, 100, true);
5783 }
5784 
5785 /**
5786  * Called from Glitter function 'GetInvLimit()'
5787  */
InvGetLimit(int invno)5788 extern int InvGetLimit(int invno) {
5789 	assert(invno == INV_1 || invno == INV_2); // only INV_1 and INV_2 supported
5790 
5791 	return g_InvD[invno].MaxInvObj;
5792 }
5793 
5794 /**
5795  * Called from Glitter function 'SetInvLimit()'
5796  */
InvSetLimit(int invno,int MaxContents)5797 extern void InvSetLimit(int invno, int MaxContents) {
5798 	assert(invno == INV_1 || invno == INV_2); // only INV_1 and INV_2 supported
5799 	assert(MaxContents >= g_InvD[invno].NoofItems); // can't reduce maximum contents below current contents
5800 
5801 	if (MaxContents > MAX_ININV)
5802 		MaxContents = MAX_ININV;	// Max contents
5803 
5804 	g_InvD[invno].MaxInvObj = MaxContents;
5805 }
5806 
5807 /**
5808  * Called from Glitter function 'SetInvSize()'
5809  */
InvSetSize(int invno,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)5810 extern void InvSetSize(int invno, int MinWidth, int MinHeight,
5811 		int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
5812 	assert(invno == INV_1 || invno == INV_2); // only INV_1 and INV_2 supported
5813 
5814 	if (StartWidth > MaxWidth)
5815 		StartWidth = MaxWidth;
5816 	if (StartHeight > MaxHeight)
5817 		StartHeight = MaxHeight;
5818 
5819 	g_InvD[invno].MaxHicons = MaxWidth;
5820 	g_InvD[invno].MinHicons = MinWidth;
5821 	g_InvD[invno].MaxVicons = MaxHeight;
5822 	g_InvD[invno].MinVicons = MinHeight;
5823 
5824 	g_InvD[invno].NoofHicons = StartWidth;
5825 	g_InvD[invno].NoofVicons = StartHeight;
5826 
5827 	if (MaxWidth != MinWidth && MaxHeight != MinHeight)
5828 		g_InvD[invno].resizable = true;
5829 	else
5830 		g_InvD[invno].resizable = false;
5831 
5832 	g_InvD[invno].bMax = false;
5833 }
5834 
5835 /**************************************************************************/
5836 
IsTopWindow()5837 extern bool IsTopWindow() {
5838 	return (g_InventoryState == BOGUS_INV);
5839 }
5840 
MenuActive()5841 extern bool MenuActive() {
5842 	return (g_InventoryState == ACTIVE_INV && g_ino == INV_CONF);
5843 }
5844 
IsConvWindow()5845 extern bool IsConvWindow() {
5846 	return (g_InventoryState == ACTIVE_INV && g_ino == INV_CONV);
5847 }
5848 
5849 } // End of namespace Tinsel
5850