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