1 /*
2 * PROPRIETARY INFORMATION. This software is proprietary to POWDER
3 * Development, and is not to be reproduced, transmitted, or disclosed
4 * in any way without written permission.
5 *
6 * Produced by: Jeff Lait
7 *
8 * POWDER Development
9 *
10 * NAME: gfxengine.cpp ( POWDER Library, C++ )
11 *
12 * COMMENTS:
13 * This simple set of routines allows a somewhat platform independent
14 * way of drawing to the GBA.
15 */
16
17 #include "gfxengine.h"
18 #include "gfxengine.h"
19
20 #include "mygba.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25
26 #include "gfx/all_bitmaps.h"
27
28 #include "map.h"
29 #include "creature.h"
30 #include "glbdef.h"
31 #include "assert.h"
32 #include "item.h"
33 #include "control.h"
34 #include "msg.h"
35 #include "stylus.h"
36 #include "victory.h"
37
38 // The number of 8x8 tiles to have. The maximum for 256 colours
39 // on the GBA is 512. SDL is theoritically infinite, except the
40 // 1024 and 2048 bits are used for flip flags.
41 #if defined(USING_SDL)
42 #define TILEARRAY 1023
43 #else
44 #define TILEARRAY 512
45 #endif
46
47 // This is used for the maximum number the bitmap routines can grab.
48 // We can safely bump this up in the SDL case.
49 #if defined(USING_SDL)
50 // Since it is a 16x16 map, this is sufficient.
51 #define TILESTASH 256
52 #else
53 #define TILESTASH 50
54 #endif
55
56 // Gets the number of bytes of a tile in our current tileset
57 #ifdef USING_SDL
58 #define BYTEPERTILE (glb_tilesets[glb_tileset].tilewidth*glb_tilesets[glb_tileset].tilewidth)
59 #define WORDPERTILE (glb_tilesets[glb_tileset].tilewidth*glb_tilesets[glb_tileset].tilewidth / 2)
60 #define TILEWIDTH (glb_tilesets[glb_tileset].tilewidth)
61 #define TILEHEIGHT (glb_tilesets[glb_tileset].tilewidth)
62 #else
63 #define BYTEPERTILE 8*8
64 #define WORDPERTILE 4*8
65 #define TILEWIDTH 8
66 #define TILEHEIGHT 8
67 #endif
68
69 #ifdef USING_DS
70 #define RAW_SCREEN_WIDTH 256
71 #define RAW_SCREEN_HEIGHT 192
72 #define PLATE_WIDTH 256
73 #define PLATE_HEIGHT 192
74 #include <fat.h>
75 #else
76 #ifdef USING_SDL
77 #define RAW_SCREEN_WIDTH (TILEWIDTH * 32)
78 #define RAW_SCREEN_HEIGHT (TILEWIDTH * 24)
79 #define PLATE_WIDTH 256
80 #define PLATE_HEIGHT 192
81 #else
82 #define RAW_SCREEN_WIDTH 240
83 #define RAW_SCREEN_HEIGHT 160
84 #define PLATE_WIDTH 240
85 #define PLATE_HEIGHT 160
86 #endif
87 #endif
88
89 // Global variables
90 int glb_newframe = 0;
91 volatile int glb_framecount = 0;
92
93 int glb_gfxmode = -1;
94
95 // Tileset defaults to Akoi Meexx.
96 #ifndef USE_VIRTUAL_SCREEN
97 int glb_tileset = 4;
98 #else
99 int glb_tileset = 2;
100 int glb_modetilesets[2] = { 2, 2 };
101 int glb_tilesetmode = 0;
102 #endif
103 // Fonts default to shadowed.
104 int glb_currentfont = 2;
105
106 // This is our current tile reference array.
107 // glb_tileidx[TILE_NAME] gives the gba tile index.
108 // This has already been multiplied by 4!
109 u16 glb_tileidx[NUM_TILES];
110
111 // This is the gba tile that ties to the given character.
112 u16 glb_charidx[256];
113
114 #define MAX_COLOUR_CHAR 10
115 u8 *glb_colourchardata[MAX_COLOUR_CHAR];
116
117 // glb_idxcount[gbaidx] gives the number of times that tile
118 // is currently used.
119 u16 glb_idxcount[TILEARRAY];
120
121 // This is a map of our current 64x64 tile layout.
122 u16 *glb_tilemap, *glb_shadowmap, *glb_mobmap;
123 u16 *glb_abstilemap, *glb_absmobmap, *glb_absshadowmap;
124
125 // This is 32x24 and matches the currently displayed characters.
126 u8 *glb_charmap;
127
128 // Used in our compositing engine for building the paper doll
129 u8 *glb_comptiledata = 0;
130 int glb_lastdesttile;
131
132 // Used in our greyscale sprite engine, must be global so we
133 // can resize this
134 u8 *glb_greyscaletiledata = 0;
135
136 // Our one lone sprite.
137 u16 glb_sprite = SPRITE_CURSOR;
138
139 // This is the current scroll offset of where our tile layout matches
140 // with the tilemap.
141 int glb_scrollx, glb_scrolly;
142 // This is the tile offset of the center tile. We use this for inventory.
143 int glb_offx, glb_offy;
144 // This is the pixel level scroll to nudge all the scrolling planes.
145 s8 glb_nudgex = 0, glb_nudgey = 0;
146
147 // Current inventory position.. Defaults to where the first added
148 // item will go.
149 int glb_invx = 1, glb_invy = 0;
150 MOB *glb_invmob = 0;
151
152 // Are we in sprite or dungeon mode?
153 bool glb_usesprites = true;
154
155 // bg2 tiles. This maps to the screen with 240/8 = 30 to 160/8 = 20
156 // ratio. Thus, there are are 600 tiles.
157 tile_info_ptr glb_bg2tiles;
158 tile_info_ptr glb_bg1tiles;
159 // This has the same format as tiles! It is XxY for 8x8 blocks,
160 // of which there are 30x20.
161 char *glb_bg2tiledata;
162
163 // This the palette entry for each of the standard colours.
164 char glb_stdcolor[NUM_COLOURS];
165
166 // This is the mapping for each colour in the sprite palette into
167 // the greyscale equivalent.
168 u8 *glb_spritegreyscale = 0;
169
170 // Text look up tables...
171 const char *glb_texttable[] = {
172 "abcdefghij",
173 "klmnopqrst",
174 "uvwxyz" SYMBOLSTRING_HEART SYMBOLSTRING_MAGIC SYMBOLSTRING_NEXT SYMBOLSTRING_DLEVEL,
175 "ABCDEFGHIJ",
176 "KLMNOPQRST",
177 "UVWXYZ" SYMBOLSTRING_LEFT SYMBOLSTRING_RIGHT SYMBOLSTRING_UP SYMBOLSTRING_DOWN,
178 "0123456789",
179 "!@#$%^&*()",
180 "=-\\',./+_|",
181 "\"<>?[]{}`~",
182 ",.<>;:" SYMBOLSTRING_AC SYMBOLSTRING_TRIDUDE SYMBOLSTRING_CURSOR " ",
183 SYMBOLSTRING_UNIQUE " ",
184 0
185 };
186
187 u8
rgbtogrey(u8 cr,u8 cg,u8 cb)188 rgbtogrey(u8 cr, u8 cg, u8 cb)
189 {
190 // Roughly a 5:6:1 weighting, but scaled up so we
191 // get to divide by 16 for some meaningless efficiency.
192 int grey = cg * 8 + cr * 6 + cb * 2;
193 grey += 7;
194 grey /= 16;
195 if (grey > 255)
196 grey = 255;
197
198 return grey;
199 }
200
201 #ifdef USING_SDL
202 static int
rawtoplate_x(int x)203 rawtoplate_x(int x)
204 {
205 int sx = (int) ((x * PLATE_WIDTH) / ((float)(RAW_SCREEN_WIDTH)) + 0.5);
206 if (sx >= PLATE_WIDTH)
207 sx = PLATE_WIDTH-1;
208 return sx;
209 }
210
211 static int
rawtoplate_y(int y)212 rawtoplate_y(int y)
213 {
214 int sy = (int) ((y * PLATE_HEIGHT) / ((float)(RAW_SCREEN_HEIGHT)) + 0.5);
215 if (sy >= PLATE_HEIGHT)
216 sy = PLATE_HEIGHT-1;
217 return sy;
218 }
219 #else
220 static int
rawtoplate_x(int x)221 rawtoplate_x(int x)
222 {
223 return x;
224 }
225
226 static int
rawtoplate_y(int y)227 rawtoplate_y(int y)
228 {
229 return y;
230 }
231 #endif
232
233 int
gfx_getchartile(char c)234 gfx_getchartile(char c)
235 {
236 int textline;
237 const char *text;
238 int pos;
239
240 // Special cases...
241 switch (c)
242 {
243 case '\b':
244 c = SYMBOL_LEFT;
245 break;
246 case '\n':
247 case '\r':
248 c = SYMBOL_NEXT;
249 break;
250 case '\t':
251 c = SYMBOL_UP;
252 break;
253 }
254
255 pos = 0;
256 textline = 0;
257 while (glb_texttable[textline])
258 {
259 text = glb_texttable[textline];
260 while (*text)
261 {
262 if (*text == c)
263 return pos;
264 pos++;
265 text++;
266 }
267 textline++;
268 }
269 // Failure, lower case a.
270 return 1;
271 }
272
273 u16
gfx_lockColourCharTile(u8 c,COLOUR_NAMES colour,u8 * result)274 gfx_lockColourCharTile(u8 c, COLOUR_NAMES colour, u8 *result)
275 {
276 u8 virtualtile, charnum;
277 int idx, sourceidx;
278
279 // Find a free colour tile in our desired range.
280 for (virtualtile = COLOURED_SYMBOLS; virtualtile < COLOURED_SYMBOLS + MAX_COLOUR_CHAR; virtualtile++)
281 {
282 if (glb_charidx[virtualtile] == INVALID_TILEIDX)
283 {
284 // This is a free index!
285 break;
286 }
287 }
288 // Check to see if we failed, if so, return the no tile.
289 // Flag that we shouldn't unlock this tile.
290 if (virtualtile == COLOURED_SYMBOLS + MAX_COLOUR_CHAR)
291 {
292 *result = 0xff;
293 return gfx_lookupTile(TILE_NOTILE);
294 }
295
296 // Now try and allocate a tile.
297 idx = gfx_findFreeTile(1);
298 if (idx == INVALID_TILEIDX)
299 {
300 // Bah, ran out of tiles!
301 *result = 0xff;
302 return gfx_lookupTile(TILE_NOTILE);
303 }
304
305 // Now find what origin tile we want to map from.
306 sourceidx = gfx_getchartile(c);
307
308 charnum = virtualtile - COLOURED_SYMBOLS;
309 // Check to see if our tiledata is available.
310 if (!glb_colourchardata[charnum])
311 glb_colourchardata[charnum] = (u8 *) new u16[WORDPERTILE];
312
313 u8 *dst;
314 const u8 *src;
315 u16 *palette = (u16 *) glb_tilesets[glb_tileset].palette;
316 int i;
317
318 dst = glb_colourchardata[charnum];
319 src = (const u8*)&glb_tilesets[glb_tileset].alphabet[glb_currentfont][sourceidx*BYTEPERTILE];
320
321 for (i = 0; i < BYTEPERTILE; i++)
322 {
323 // Anything not black or transparent is turned into
324 // our desired colour.
325 if (*src == 0 || *src == glb_stdcolor[COLOUR_BLACK] || !palette[*src] || palette[*src] == 0x842)
326 *dst = *src;
327 else
328 *dst = glb_stdcolor[colour];
329 dst++;
330 src++;
331 }
332
333 // And send the resulting tile to VRAM.
334 ham_ReloadTileGfx(glb_bg1tiles,
335 (u16*) glb_colourchardata[charnum],
336 idx,
337 1);
338 glb_idxcount[idx]++;
339 glb_charidx[virtualtile] = idx;
340
341 // When we unlock, this is the tile we'll actually unlock!
342 *result = virtualtile;
343
344 return idx;
345 }
346
347 u16
gfx_lockCharTile(u8 c,u8 * result)348 gfx_lockCharTile(u8 c, u8 *result)
349 {
350 int idx;
351 int sourceidx;
352
353 // Check our character tile array to see if it is already done.
354 *result = c;
355
356 idx = glb_charidx[c];
357 if (idx != INVALID_TILEIDX)
358 {
359 // Great, already allocated, inc and return.
360 glb_idxcount[idx]++;
361 return idx;
362 }
363
364 // Must allocate it...
365 idx = gfx_findFreeTile(1);
366
367 if (idx != INVALID_TILEIDX)
368 {
369 sourceidx = gfx_getchartile(c);
370 ham_ReloadTileGfx(glb_bg1tiles,
371 (u16*)(&glb_tilesets[glb_tileset].alphabet[glb_currentfont][sourceidx * BYTEPERTILE]),
372 idx,
373 1);
374 glb_idxcount[idx]++;
375 glb_charidx[c] = idx;
376 return idx;
377 }
378
379 // Damn, failure to allocate!
380 UT_ASSERT(!"Failure to allocate char tile");
381 // Flag that we shouldn't unlock this tile.
382 *result = 0xff;
383 return gfx_lookupTile(TILE_NOTILE);
384 }
385
386 void
gfx_unlockCharTile(u8 c)387 gfx_unlockCharTile(u8 c)
388 {
389 u16 idx;
390
391 if (c == 0xff)
392 return; // Blank dude.
393
394 idx = glb_charidx[c];
395
396 UT_ASSERT(idx != INVALID_TILEIDX);
397 if (idx == INVALID_TILEIDX)
398 return;
399
400 UT_ASSERT(glb_idxcount[idx]);
401 if (glb_idxcount[idx])
402 glb_idxcount[idx]--;
403 if (!glb_idxcount[idx])
404 {
405 // TIle is now free.
406 glb_charidx[c] = INVALID_TILEIDX;
407 }
408 }
409
410 u16
gfx_findFreeTile(int size)411 gfx_findFreeTile(int size)
412 {
413 int tile, j;
414
415 for (tile = 0; tile <= TILEARRAY - size; tile++)
416 {
417 if (glb_idxcount[tile])
418 continue;
419
420 // We have a zero, check if next size are zero..
421 for (j = 0; j < size; j++)
422 {
423 if (glb_idxcount[tile+j])
424 {
425 tile = tile+j;
426 break;
427 }
428 }
429
430 // Making it here implies that we found a free tile.
431 if (j == size)
432 return tile;
433 }
434
435 // Failure to find a free one.
436 return INVALID_TILEIDX;
437 }
438
439 u16
gfx_lookupTile(TILE_NAMES tile)440 gfx_lookupTile(TILE_NAMES tile)
441 {
442 if (tile == INVALID_TILEIDX)
443 return INVALID_TILEIDX;
444
445 return glb_tileidx[tile];
446 }
447
448 u16
gfx_lockTile(TILE_NAMES tile,TILE_NAMES * result)449 gfx_lockTile(TILE_NAMES tile, TILE_NAMES *result)
450 {
451 u16 idx;
452
453 if (result)
454 *result = tile;
455
456 if (tile == INVALID_TILEIDX)
457 return INVALID_TILEIDX;
458 idx = glb_tileidx[tile];
459 if (idx != INVALID_TILEIDX)
460 {
461 if (tile == TILE_VOID || tile == TILE_NOTILE)
462 {
463 // We only lock these tiles once ever.
464 return idx;
465 }
466 glb_idxcount[idx]++;
467 glb_idxcount[idx+1]++;
468 glb_idxcount[idx+2]++;
469 glb_idxcount[idx+3]++;
470 return idx;
471 }
472 idx = gfx_findFreeTile(4);
473 if (idx != INVALID_TILEIDX)
474 {
475 // A free index! Return it.
476 ham_ReloadTileGfx(glb_bg1tiles,
477 (u16*)(&glb_tilesets[glb_tileset].dungeon[tile * 4 * BYTEPERTILE]),
478 idx,
479 4);
480 glb_idxcount[idx]++;
481 glb_idxcount[idx+1]++;
482 glb_idxcount[idx+2]++;
483 glb_idxcount[idx+3]++;
484 glb_tileidx[tile] = idx;
485 return idx;
486 }
487 // Failed to find free...
488 UT_ASSERT(!"FAILED TO ALLOC TILE!");
489 if (result)
490 *result = (TILE_NAMES) INVALID_TILEIDX;
491 return gfx_lookupTile(TILE_NOTILE);
492 }
493
494 void
gfx_forceLockTile(TILE_NAMES tile,int mincount)495 gfx_forceLockTile(TILE_NAMES tile, int mincount)
496 {
497 int idx, j;
498
499 // See if our tile is already locked.
500 // Problem: If we have a
501 idx = gfx_lookupTile(tile);
502 if (idx == INVALID_TILEIDX)
503 {
504 // Tile not yet locked, lock it!
505 idx = gfx_lockTile(tile, 0);
506 }
507
508 // Verify we have the minimum count.
509 for (j = 0; j < 4; j++)
510 {
511 if (glb_idxcount[idx+j] < mincount)
512 glb_idxcount[idx+j] = mincount;
513 }
514 }
515
516 void
gfx_unlockTile(TILE_NAMES tile)517 gfx_unlockTile(TILE_NAMES tile)
518 {
519 u16 idx;
520
521 if (tile == INVALID_TILEIDX)
522 return;
523
524 if (tile == TILE_NOTILE || tile == TILE_VOID)
525 {
526 // Never unlock these special tiles.
527 // We need to be able to always procur them
528 return;
529 }
530
531 idx = glb_tileidx[tile];
532
533 UT_ASSERT(idx != INVALID_TILEIDX);
534 if (idx == INVALID_TILEIDX)
535 return;
536
537 // Must have already locked it...
538 UT_ASSERT(glb_idxcount[idx]);
539 UT_ASSERT(glb_idxcount[idx+1]);
540 UT_ASSERT(glb_idxcount[idx+2]);
541 UT_ASSERT(glb_idxcount[idx+3]);
542 glb_idxcount[idx]--;
543 glb_idxcount[idx+1]--;
544 glb_idxcount[idx+2]--;
545 glb_idxcount[idx+3]--;
546 if (!glb_idxcount[idx])
547 {
548 // Tile is now free...
549 glb_tileidx[tile] = INVALID_TILEIDX;
550 }
551 }
552
553 int
gfx_getNumFreeTiles()554 gfx_getNumFreeTiles()
555 {
556 int idx, num;
557
558 num = 0;
559 for (idx = 0; idx < TILEARRAY; idx++)
560 {
561 if (glb_idxcount[idx] == 0)
562 num++;
563 }
564 return num;
565 }
566
567 //
568 // VBL func
569 //
vblFunc()570 void vblFunc()
571 {
572 glb_newframe = 1;
573 glb_framecount++;
574 }
575
576 void
gfx_buildstdcolors()577 gfx_buildstdcolors()
578 {
579 COLOUR_NAMES colour;
580
581 FOREACH_COLOUR(colour)
582 {
583 glb_stdcolor[colour] = gfx_lookupcolor(glb_colourdefs[colour].red,
584 glb_colourdefs[colour].green,
585 glb_colourdefs[colour].blue);
586 }
587 // Build the standard colours...
588 glb_stdcolor[COLOUR_INVISIBLE] = 0; // Always true.
589
590 // This is also a good time to update our grey table if we have it.
591 gfx_updatespritegreytable();
592 }
593
594 void
gfx_init()595 gfx_init()
596 {
597 // Initialize everything...
598 ham_Init();
599
600 // Install our interrupt handler
601 #if defined(USING_SDL) || defined(USING_DS) || defined(NO_HAM)
602 ham_StartIntHandler(INT_TYPE_VBL, &vblFunc);
603 #else
604 ham_StartIntHandler(INT_TYPE_VBL, (void *)&vblFunc);
605 #endif
606
607 // These maps store the TILE_NAMES currently at each map position.
608 // The absolute version do the same for absolute overlays.
609 // This allows us to avoid double writes, and load tiles into
610 // VRAM as necessary.
611 // 0xff is used as INVALID_TILEIDX is 65535.
612
613 glb_tilemap = new u16[32*32];
614 glb_shadowmap = new u16[32*32];
615 glb_mobmap = new u16[32*32];
616 glb_abstilemap = new u16[17*12];
617 glb_absmobmap = new u16[17*12];
618 glb_absshadowmap = new u16[17*12];
619 glb_charmap = new u8[32*24];
620
621 memset(glb_tilemap, 0xff, 32*32 * sizeof(u16));
622 memset(glb_shadowmap, 0xff, 32*32 * sizeof(u16));
623 memset(glb_mobmap, 0xff, 32*32 * sizeof(u16));
624 memset(glb_abstilemap, 0xff, 17*12*sizeof(u16));
625 memset(glb_absmobmap, 0xff, 17*12*sizeof(u16));
626 memset(glb_absshadowmap, 0xff, 17*12*sizeof(u16));
627 memset(glb_charmap, 0xff, 32*24);
628 }
629
630 void
gfx_reblitslugandblood()631 gfx_reblitslugandblood()
632 {
633 u16 *screen;
634 int x, y;
635
636 screen = hamfake_lockScreen();
637 #if defined(USING_DS) || defined(USING_SDL)
638 // Scroll back the screen since it was in GBA coords.
639 screen -= TILEWIDTH + 2*TILEHEIGHT * RAW_SCREEN_WIDTH;
640
641 if (RAW_SCREEN_WIDTH != PLATE_WIDTH ||
642 RAW_SCREEN_HEIGHT != PLATE_HEIGHT)
643 {
644 for (y = 0; y < RAW_SCREEN_HEIGHT; y++)
645 {
646 int sy = rawtoplate_y(y);
647 for (x = 0; x < RAW_SCREEN_WIDTH; x++)
648 {
649 int sx = rawtoplate_x(x);
650
651 screen[y*RAW_SCREEN_WIDTH+x] = bmp_slug_and_blood[sy*PLATE_WIDTH+sx];
652 }
653 }
654 }
655 else
656 {
657 for (y = 0; y < PLATE_HEIGHT; y++)
658 {
659 for (x = 0; x < PLATE_WIDTH; x++)
660 {
661 screen[y*RAW_SCREEN_WIDTH+x] = bmp_slug_and_blood[y*PLATE_WIDTH+x];
662 }
663 }
664 }
665 #else
666 for (y = 0; y < PLATE_WIDTH*PLATE_HEIGHT; y++)
667 {
668 screen[y] = bmp_slug_and_blood[y];
669 }
670 #endif
671 hamfake_unlockScreen(screen);
672 }
673
674 void
gfx_setmode(int mode)675 gfx_setmode(int mode)
676 {
677 int x, y;
678 map_info_ptr bg2mapset;
679
680 // A no-op.
681 if (mode == glb_gfxmode)
682 return;
683
684 glb_gfxmode = mode;
685
686 if (mode == 3)
687 {
688 // Intro screen...
689 ham_SetBgMode(3);
690
691 gfx_reblitslugandblood();
692
693 // Ensure we have current sprite date.
694 hamfake_LoadSpritePal((void *)glb_tilesets[glb_tileset].spritepalette, 512);
695 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].sprite[glb_sprite*4*BYTEPERTILE]),
696 0, 4);
697
698 // NOTE: This actually copies incorrect data! Likely due to it
699 // using 32bit copies or ignoring waitstates.
700 // memcpy( (unsigned short *) 0x06000000,
701 // bmp_slug_and_blood,
702 // 38400*2 );
703 return;
704 }
705
706 // Must be tiled mode.
707 UT_ASSERT(mode == 0);
708
709 // This is all four bgs, no zooming.
710 ham_SetBgMode(0);
711
712 // Wipe any charmap left over from raw mode.
713 memset(glb_charmap, 0xff, 32*24);
714
715 ham_LoadBGPal((void *)glb_tilesets[glb_tileset].palette, 512);
716 hamfake_LoadSpritePal((void *)glb_tilesets[glb_tileset].spritepalette, 512);
717
718 // So, what are our layers?
719
720 // 0: 32x32: Text and minimap.
721 // 1: 64x64: Terrain tiles
722 // 2: 64x64: Monters/Item tiles
723 // 3: 64x64: Shadow tiles
724
725 #if 1
726 // We want a 32x32 as we only will use one screen worth.
727 ham_bg[0].mi = ham_InitMapEmptySet(0, 0);
728
729 // We want a 64x64 screen.
730 ham_bg[1].mi = ham_InitMapEmptySet(3, 0);
731
732 // 64x64 map
733 bg2mapset = ham_InitMapEmptySet(3, 0);
734
735 // We want a 64x64 screen.
736 ham_bg[3].mi = ham_InitMapEmptySet(3, 0);
737 #else
738 // We want a 32x32 as we only will use one screen worth.
739 ham_bg[0].mi = ham_InitMapEmptySet(0, 0);
740
741 // We want a 64x64 screen.
742 ham_bg[1].mi = ham_bg[0].mi;
743
744 // 32 x 32 map.
745 bg2mapset = ham_bg[0].mi;
746
747 // We want a 64x64 screen.
748 ham_bg[3].mi = ham_bg[0].mi;
749 #endif
750
751 ham_bg[1].ti = ham_InitTileEmptySet(TILEARRAY, 1, 1);
752
753 // Init the dungeon tile set...
754 // ** EXCESSIVE PROFANITY DETECTED **
755 // ** DELETING OFFENDING LINES **
756 // ** RESUMING COMMENT **
757 // to respond to so FOAD, HTH, HAND.
758
759 // Thus, we now create a single tile set of TILEARRAY size.
760 // NOTE: We now share this with the bitmap cache, so that
761 // code currently assumes it is contiguous and uses 8x8 rather
762 // than 16x16, so for now we'll keep them the same.
763 //ham_bg[1].ti = ham_InitTileSet((void *)&DUNGEONTILES,
764 // (NUM_TILES-3) * 4 * WORDPERTILE, 1, 1);
765 glb_bg1tiles = ham_bg[1].ti;
766
767 ham_bg[0].ti = ham_bg[1].ti;
768
769 // Initialize our arrays to be all empty...
770 memset(glb_tileidx, 0xff, sizeof(u16) * NUM_TILES);
771 memset(glb_charidx, 0xff, sizeof(u16) * 256);
772 memset(glb_idxcount, 0, sizeof(u16) * TILEARRAY);
773
774 // Create the level 2 bg, where we do all our crazy pixel level
775 // stuff.
776 // You would think this size would have to be 600. That crashes
777 // due to out of vram, yet I seem to be able to write to it just
778 // fine? WTF?
779 // Calculations show that we can't fit, even in an ideal case,
780 // this much data in the 64k available in the tile/map data.
781 // One option is to use the object data set (which is 32k), except
782 // the actual screen required is 37.5k. THus, we instead create
783 // a tilestash which we can read from & deal with that way.
784 // glb_bg2tiles = ham_InitTileEmptySet(TILESTASH, 1, 0);
785 glb_bg2tiles = glb_bg1tiles;
786
787 // Highest priority as it will sit on top.
788 ham_InitBg(0, 1, 0, 0);
789
790 ham_InitBg(1, 1, 3, 0);
791
792 // Create our level 3 background, where we have our lighting overlay
793 // and put blood sprites, etc.
794 ham_bg[3].ti = ham_bg[1].ti;
795 // Above the lower map, but below the text.
796 ham_InitBg(3, 1, 1, 0);
797
798 memset(glb_tilemap, 0xff, 32*32 * sizeof(u16));
799 memset(glb_shadowmap, 0xff, 32*32 * sizeof(u16));
800 memset(glb_mobmap, 0xff, 32*32 * sizeof(u16));
801 memset(glb_abstilemap, 0xff, 17*12*sizeof(u16));
802 memset(glb_absmobmap, 0xff, 17*12*sizeof(u16));
803 memset(glb_absshadowmap, 0xff, 17*12*sizeof(u16));
804 memset(glb_charmap, 0xff, 32*24);
805
806 ham_bg[2].ti = glb_bg2tiles;
807 ham_bg[2].mi = bg2mapset;
808
809 // We allocate a half sized block as u16 and cast back to char
810 // to ensure we are aligned to u16.
811 glb_bg2tiledata = (char *) new u16[WORDPERTILE*TILESTASH];
812 memset(glb_bg2tiledata, 0, BYTEPERTILE*TILESTASH);
813
814 // No rot. Above the ground but below the shadow.
815 ham_InitBg(2, 1, 2, 0);
816
817 gfx_buildstdcolors();
818
819 // We always want the no tile locked so we can show it when
820 // things go bad...
821 // We want to lock VOID first as it will be tile 0, and thus
822 // our default screen will be sweet, sweet, black.
823 // Actaully, there is apparently no guarantee that the initial
824 // tile values are 0. Thus, we must manually set all of our
825 // tile planes to void here.
826 int voidtileidx;
827
828 // We know these locks must always succeed so we don't care to stash
829 // the result. In any case, we have engineered it that they are
830 // never freed.
831 voidtileidx = gfx_lockTile(TILE_VOID, 0);
832 gfx_lockTile(TILE_NOTILE, 0);
833
834 for (y = 0; y < 64; y++)
835 {
836 for (x = 0; x < 64; x++)
837 {
838 if (x < 32 && y < 32)
839 {
840 ham_SetMapTile(0, x, y, voidtileidx);
841 }
842 ham_SetMapTile(1, x, y, voidtileidx);
843 ham_SetMapTile(2, x, y, voidtileidx);
844 ham_SetMapTile(3, x, y, voidtileidx);
845 }
846 }
847
848 // Load the sprite.
849 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].sprite[glb_sprite*4*BYTEPERTILE]),
850 0, 4);
851 }
852
853
854 //
855 // This forces a rebuilding of all the tiles from our cache to the
856 // ham space.
857 //
858 void
gfx_refreshtiles()859 gfx_refreshtiles()
860 {
861 int x, y, tile, tileidx;
862
863 // Ignore this if we are not in a graphics mode.
864 if (glb_gfxmode)
865 return;
866
867 // A refresh will also clear anything in the abs maps, so we
868 // first unlock those tiles.
869 for (y = 0; y < 12; y++)
870 {
871 for (x = 0; x < 17; x++)
872 {
873 tile = glb_abstilemap[y * 17 + x];
874 if (tile != INVALID_TILEIDX)
875 {
876 gfx_unlockTile((TILE_NAMES)tile);
877 glb_abstilemap[y * 17 + x] = INVALID_TILEIDX;
878 }
879 tile = glb_absshadowmap[y * 17 + x];
880 if (tile != INVALID_TILEIDX)
881 {
882 gfx_unlockTile((TILE_NAMES)tile);
883 glb_absshadowmap[y * 17 + x] = INVALID_TILEIDX;
884 }
885 tile = glb_absmobmap[y * 17 + x];
886 if (tile != INVALID_TILEIDX)
887 {
888 gfx_unlockTile((TILE_NAMES)tile);
889 glb_absmobmap[y * 17 + x] = INVALID_TILEIDX;
890 }
891 }
892 }
893
894 // TODO: Insert map might prove a tad bit more efficient, though
895 // would need a full size scratch space.
896 for (y = 0; y < 32; y++)
897 {
898 for (x = 0; x < 32; x++)
899 {
900 tile = glb_tilemap[(y + glb_scrolly) * 32 + x + glb_scrollx];
901 tileidx = gfx_lockTile((TILE_NAMES)tile, (TILE_NAMES *) &tile);
902 ham_SetMapTile(1, x*2, y*2, tileidx);
903 ham_SetMapTile(1, x*2+1, y*2, tileidx+1);
904 ham_SetMapTile(1, x*2, y*2+1, tileidx+2);
905 ham_SetMapTile(1, x*2+1, y*2+1, tileidx+3);
906
907 // The above lock should be a double count, so we unlock
908 // afterwards. Any write to glb_tilemap must have done
909 // a lock!
910 gfx_unlockTile((TILE_NAMES)tile);
911 }
912 }
913
914 // Update overlay buffer...
915 for (y = 0; y < 32; y++)
916 {
917 for (x = 0; x < 32; x++)
918 {
919 tile = glb_shadowmap[(y + glb_scrolly) * 32 + x + glb_scrollx];
920 tileidx = gfx_lockTile((TILE_NAMES)tile, (TILE_NAMES *) &tile);
921 ham_SetMapTile(3, x*2, y*2, tileidx);
922 ham_SetMapTile(3, x*2+1, y*2, tileidx+1);
923 ham_SetMapTile(3, x*2, y*2+1, tileidx+2);
924 ham_SetMapTile(3, x*2+1, y*2+1, tileidx+3);
925 // See comment above why this is needed.
926 gfx_unlockTile((TILE_NAMES)tile);
927 }
928 }
929 // Update mob buffer...
930 for (y = 0; y < 32; y++)
931 {
932 for (x = 0; x < 32; x++)
933 {
934 tile = glb_mobmap[(y + glb_scrolly) * 32 + x + glb_scrollx];
935 tileidx = gfx_lockTile((TILE_NAMES)tile, (TILE_NAMES *) &tile);
936 ham_SetMapTile(2, x*2, y*2, tileidx);
937 ham_SetMapTile(2, x*2+1, y*2, tileidx+1);
938 ham_SetMapTile(2, x*2, y*2+1, tileidx+2);
939 ham_SetMapTile(2, x*2+1, y*2+1, tileidx+3);
940 // See comment above why this is needed.
941 gfx_unlockTile((TILE_NAMES)tile);
942 }
943 }
944 #if 0
945 BUF buf;
946 buf.sprintf("%d, %d, %d, %d...%d, %d, %d, %d...",
947 (int)ham_bg[0].ti->first_block,
948 (int)ham_bg[1].ti->first_block,
949 (int)ham_bg[2].ti->first_block,
950 (int)ham_bg[3].ti->first_block,
951 (int)ham_bg[0].mi->first_block,
952 (int)ham_bg[1].mi->first_block,
953 (int)ham_bg[2].mi->first_block,
954 (int)ham_bg[3].mi->first_block);
955 msg_report(buf);
956 #endif
957 }
958
959 void
gfx_settile(int tx,int ty,int tileno)960 gfx_settile(int tx, int ty, int tileno)
961 {
962 int hamx, hamy, tileidx;
963
964 hamx = tx - glb_scrollx;
965 hamy = ty - glb_scrolly;
966 // Only send onto ham if on screen and is a real change.
967 if (glb_tilemap[ty * 32 + tx] != tileno)
968 // && hamx < 32 && hamy < 32)
969 {
970 gfx_unlockTile((TILE_NAMES)glb_tilemap[ty * 32 + tx]);
971 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *) &tileno);
972 ham_SetMapTile(1, tx*2, ty*2, tileidx);
973 ham_SetMapTile(1, tx*2+1, ty*2, tileidx+1);
974 ham_SetMapTile(1, tx*2, ty*2+1, tileidx+2);
975 ham_SetMapTile(1, tx*2+1, ty*2+1, tileidx+3);
976
977 // UPdate our local map.
978 glb_tilemap[ty * 32 + tx] = tileno;
979 }
980 }
981
982 void
gfx_setabstile(int tx,int ty,int tileno)983 gfx_setabstile(int tx, int ty, int tileno)
984 {
985 // Handle ability to write to -1 column
986 tx++;
987
988 UT_ASSERT(tx >= 0);
989 UT_ASSERT(tx < 17);
990 UT_ASSERT(ty >= 0);
991 UT_ASSERT(ty < 12);
992
993 int tx1, ty1, tileidx, abstile;
994
995 // Update the abs map...
996 abstile = glb_abstilemap[ty * 17 + tx];
997 if (abstile == tileno)
998 return;
999
1000 gfx_unlockTile((TILE_NAMES)abstile);
1001 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *)&tileno);
1002 glb_abstilemap[ty * 17 + tx] = tileno;
1003
1004 // Extra -1 is for our offset.
1005 tx += glb_offx - 7 - 1;
1006 ty += glb_offy - 5;
1007
1008 tx *= 2;
1009 ty *= 2;
1010 ty += 1; // Correct for the off by one nature of glb_offx
1011
1012 tx1 = tx + 1;
1013 ty1 = ty + 1;
1014 tx &= 63;
1015 ty &= 63;
1016 tx1 &= 63;
1017 ty1 &= 63;
1018
1019 ham_SetMapTile(1, tx, ty, tileidx);
1020 ham_SetMapTile(1, tx1, ty, tileidx+1);
1021 ham_SetMapTile(1, tx, ty1, tileidx+2);
1022 ham_SetMapTile(1, tx1, ty1, tileidx+3);
1023 }
1024
1025 void
gfx_setoverlay(int tx,int ty,int tileno)1026 gfx_setoverlay(int tx, int ty, int tileno)
1027 {
1028 int hamx, hamy, tileidx;
1029
1030 hamx = tx - glb_scrollx;
1031 hamy = ty - glb_scrolly;
1032 // Only send onto ham if on screen and is a real change.
1033 if (glb_shadowmap[ty * 32 + tx] != tileno)
1034 // && hamx < 32 && hamy < 32)
1035 {
1036 gfx_unlockTile((TILE_NAMES)glb_shadowmap[ty * 32 + tx]);
1037 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *)&tileno);
1038
1039 ham_SetMapTile(3, tx*2, ty*2, tileidx);
1040 ham_SetMapTile(3, tx*2+1, ty*2, tileidx+1);
1041 ham_SetMapTile(3, tx*2, ty*2+1, tileidx+2);
1042 ham_SetMapTile(3, tx*2+1, ty*2+1, tileidx+3);
1043
1044 // UPdate our local map.
1045 glb_shadowmap[ty * 32 + tx] = tileno;
1046 }
1047 }
1048
1049 int
gfx_getoverlay(int tx,int ty)1050 gfx_getoverlay(int tx, int ty)
1051 {
1052 return glb_shadowmap[ty * 32 + tx];
1053 }
1054
1055 void
gfx_setabsoverlay(int tx,int ty,int tileno)1056 gfx_setabsoverlay(int tx, int ty, int tileno)
1057 {
1058 // Correct for ability to write to -1 column
1059 tx++;
1060
1061 UT_ASSERT(tx >= 0);
1062 UT_ASSERT(tx < 17);
1063 UT_ASSERT(ty >= 0);
1064 UT_ASSERT(ty < 12);
1065
1066 // Ignore this if we are not in a valid mode.
1067 if (glb_gfxmode)
1068 return;
1069
1070 int tx1, ty1, tileidx, abstile;
1071
1072 // Update the abs map...
1073 abstile = glb_absshadowmap[ty * 17 + tx];
1074 if (abstile == tileno)
1075 return;
1076
1077 gfx_unlockTile((TILE_NAMES)abstile);
1078 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *)&tileno);
1079 glb_absshadowmap[ty * 17 + tx] = tileno;
1080
1081 tx += glb_offx - 7 - 1; // Extra -1 is for writing to -1 column
1082 ty += glb_offy - 5;
1083
1084 tx *= 2;
1085 ty *= 2;
1086 ty += 1; // Correct for the off by one nature of glb_offx
1087
1088 tx1 = tx + 1;
1089 ty1 = ty + 1;
1090 tx &= 63;
1091 ty &= 63;
1092 tx1 &= 63;
1093 ty1 &= 63;
1094
1095 ham_SetMapTile(3, tx, ty, tileidx);
1096 ham_SetMapTile(3, tx1, ty, tileidx+1);
1097 ham_SetMapTile(3, tx, ty1, tileidx+2);
1098 ham_SetMapTile(3, tx1, ty1, tileidx+3);
1099 }
1100
1101 void
gfx_setmoblayer(int tx,int ty,int tileno)1102 gfx_setmoblayer(int tx, int ty, int tileno)
1103 {
1104 int hamx, hamy, tileidx;
1105
1106 hamx = tx - glb_scrollx;
1107 hamy = ty - glb_scrolly;
1108 // Only send onto ham if on screen and is a real change.
1109 if (glb_mobmap[ty * 32 + tx] != tileno)
1110 // && hamx < 32 && hamy < 32)
1111 {
1112 gfx_unlockTile((TILE_NAMES)glb_mobmap[ty * 32 + tx]);
1113 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *)&tileno);
1114
1115 ham_SetMapTile(2, tx*2, ty*2, tileidx);
1116 ham_SetMapTile(2, tx*2+1, ty*2, tileidx+1);
1117 ham_SetMapTile(2, tx*2, ty*2+1, tileidx+2);
1118 ham_SetMapTile(2, tx*2+1, ty*2+1, tileidx+3);
1119
1120 // UPdate our local map.
1121 glb_mobmap[ty * 32 + tx] = tileno;
1122 }
1123 }
1124
1125 void
gfx_setabsmob(int tx,int ty,int tileno)1126 gfx_setabsmob(int tx, int ty, int tileno)
1127 {
1128 // Handle ability to write to -1 column
1129 tx++;
1130
1131 UT_ASSERT(tx >= 0);
1132 UT_ASSERT(tx < 17);
1133 UT_ASSERT(ty >= 0);
1134 UT_ASSERT(ty < 12);
1135
1136 int tx1, ty1, tileidx, abstile;
1137
1138 // Update the abs map...
1139 abstile = glb_absmobmap[ty * 17 + tx];
1140 if (abstile == tileno)
1141 return;
1142
1143 gfx_unlockTile((TILE_NAMES)abstile);
1144 tileidx = gfx_lockTile((TILE_NAMES)tileno, (TILE_NAMES *)&tileno);
1145 glb_absmobmap[ty * 17 + tx] = tileno;
1146
1147 // Extra -1 is for our offset.
1148 tx += glb_offx - 7 - 1;
1149 ty += glb_offy - 5;
1150
1151 tx *= 2;
1152 ty *= 2;
1153 ty += 1; // Correct for the off by one nature of glb_offx
1154
1155 tx1 = tx + 1;
1156 ty1 = ty + 1;
1157 tx &= 63;
1158 ty &= 63;
1159 tx1 &= 63;
1160 ty1 &= 63;
1161
1162 ham_SetMapTile(2, tx, ty, tileidx);
1163 ham_SetMapTile(2, tx1, ty, tileidx+1);
1164 ham_SetMapTile(2, tx, ty1, tileidx+2);
1165 ham_SetMapTile(2, tx1, ty1, tileidx+3);
1166 }
1167
1168 int
gfx_getmoblayer(int tx,int ty)1169 gfx_getmoblayer(int tx, int ty)
1170 {
1171 return glb_mobmap[ty * 32 + tx];
1172 }
1173
1174 void
gfx_printtext(int x,int y,const char * text)1175 gfx_printtext(int x, int y, const char *text)
1176 {
1177 // We silently clip at 30.
1178 while (x < 30 && *text)
1179 {
1180 gfx_printchar(x++, y, *text++);
1181 }
1182 }
1183
1184 void
gfx_printcharraw(int x,int y,char c)1185 gfx_printcharraw(int x, int y, char c)
1186 {
1187 int idx, offx, offy;
1188 u16 *screen = hamfake_lockScreen();
1189 u8 *chardata;
1190 u16 *palette;
1191 u16 *backplate;
1192
1193 idx = gfx_getchartile(c);
1194
1195 glb_charmap[(y/TILEHEIGHT)*32 + (x/TILEWIDTH)] = c;
1196
1197 chardata = (u8 *) (&glb_tilesets[glb_tileset].alphabet[glb_currentfont][idx * BYTEPERTILE]);
1198 palette = (u16 *) glb_tilesets[glb_tileset].palette;
1199 backplate = (u16 *) bmp_slug_and_blood;
1200
1201 // Find offset for x & y.
1202 // We have to update the backplate location as we were given
1203 // GBA coords and backplate is in DS coords.
1204 #if defined(USING_DS) || defined(USING_SDL)
1205 // This is not tile specific as we don't scale our plate (yet)
1206 backplate += 8 + 16 * PLATE_WIDTH;
1207 #endif
1208
1209 int backx = x;
1210 int backoff = 0;
1211
1212 x += y * RAW_SCREEN_WIDTH;
1213
1214 screen += x;
1215
1216 for (offy = 0; offy < TILEHEIGHT; offy++)
1217 {
1218 backoff = rawtoplate_y(y+offy) * PLATE_WIDTH;
1219
1220 for (offx = 0; offx < TILEWIDTH; offx++)
1221 {
1222 if (*chardata)
1223 {
1224 *screen = palette[*chardata];
1225 }
1226 else // Transparent.
1227 *screen = backplate[backoff + rawtoplate_x(backx+offx)];
1228
1229 screen++;
1230 chardata++;
1231 }
1232 screen += RAW_SCREEN_WIDTH - TILEWIDTH;
1233 }
1234
1235 hamfake_unlockScreen(screen);
1236 }
1237
1238 void
gfx_printcolourcharraw(int x,int y,char c,COLOUR_NAMES colour)1239 gfx_printcolourcharraw(int x, int y, char c, COLOUR_NAMES colour)
1240 {
1241 int idx, offx, offy;
1242 u16 *screen = hamfake_lockScreen();
1243 u8 *chardata;
1244 u16 *palette;
1245 u16 *backplate;
1246 u16 charcolour, desiredcolour;
1247
1248 desiredcolour = (glb_colourdefs[colour].blue >> 3) << 10;
1249 desiredcolour |= (glb_colourdefs[colour].green >> 3) << 5;
1250 desiredcolour |= (glb_colourdefs[colour].red >> 3);
1251
1252 idx = gfx_getchartile(c);
1253 glb_charmap[(y/TILEHEIGHT)*32 + (x/TILEWIDTH)] = c;
1254
1255 chardata = (u8 *) (&glb_tilesets[glb_tileset].alphabet[glb_currentfont][idx * BYTEPERTILE]);
1256 palette = (u16 *) glb_tilesets[glb_tileset].palette;
1257 backplate = (u16 *) bmp_slug_and_blood;
1258
1259 // Find offset for x & y.
1260 // We have to update the backplate location as we were given
1261 // GBA coords and backplate is in DS coords.
1262 #if defined(USING_DS) || defined(USING_SDL)
1263 backplate += 8 + 16 * PLATE_WIDTH;
1264 #endif
1265
1266 int backx = x;
1267 int backoff = 0;
1268
1269 x += y * RAW_SCREEN_WIDTH;
1270
1271 screen += x;
1272
1273 for (offy = 0; offy < TILEHEIGHT; offy++)
1274 {
1275 backoff = rawtoplate_y(y+offy) * PLATE_WIDTH;
1276
1277 for (offx = 0; offx < TILEWIDTH; offx++)
1278 {
1279 if (*chardata)
1280 {
1281 // If the palette is black, we pass it through.
1282 // Otherwise we use our desired colour.
1283 charcolour = palette[*chardata];
1284 if (!charcolour || charcolour == 0x842)
1285 *screen = charcolour;
1286 else
1287 *screen = desiredcolour;
1288 }
1289 else
1290 *screen = backplate[backoff + rawtoplate_x(backx+offx)];
1291
1292 screen++;
1293 chardata++;
1294 }
1295 screen += RAW_SCREEN_WIDTH - TILEWIDTH;
1296 }
1297
1298 hamfake_unlockScreen(screen);
1299 }
1300
1301 void
gfx_printchar(int x,int y,char c)1302 gfx_printchar(int x, int y, char c)
1303 {
1304 int idx;
1305
1306 if (glb_gfxmode == 3)
1307 {
1308 gfx_printcharraw(x * TILEWIDTH, y * TILEHEIGHT, c);
1309 return;
1310 }
1311
1312 // If there is no change, don't do anything...
1313 if (glb_charmap[y * 32 + x] == c)
1314 return;
1315
1316 // Locking error:
1317 // 1) Attempt lock on 'a'. Fail and get notile
1318 // 2) Update charmap with 'a'
1319 // 3) Attempt lock on 'a', succeed.
1320 // 4) Unlock this charmap and free the tile despite extra count.
1321 // Thus, we request the actual number to record in our charmap
1322 // that is set to INVALID if lock fails.
1323 // Unlock the old tile...
1324 gfx_unlockCharTile(glb_charmap[y*32 + x]);
1325
1326 idx = gfx_lockCharTile(c, (u8 *) &c);
1327 glb_charmap[y*32 + x] = c;
1328
1329 ham_SetMapTile(0, x, y, idx);
1330 }
1331
1332 void
gfx_printcolourchar(int x,int y,char c,COLOUR_NAMES colour)1333 gfx_printcolourchar(int x, int y, char c, COLOUR_NAMES colour)
1334 {
1335 int idx;
1336
1337 if (glb_gfxmode == 3)
1338 {
1339 gfx_printcolourcharraw(x * TILEWIDTH, y * TILEHEIGHT, c, colour);
1340 return;
1341 }
1342
1343 // There is no such thing as no change in the coloured char world.
1344
1345 // Locking error:
1346 // 1) Attempt lock on 'a'. Fail and get notile
1347 // 2) Update charmap with 'a'
1348 // 3) Attempt lock on 'a', succeed.
1349 // 4) Unlock this charmap and free the tile despite extra count.
1350 // Thus, we request the actual number to record in our charmap
1351 // that is set to INVALID if lock fails.
1352 // Unlock the old tile...
1353 gfx_unlockCharTile(glb_charmap[y*32 + x]);
1354
1355 idx = gfx_lockColourCharTile(c, colour, (u8 *) &c);
1356 glb_charmap[y*32 + x] = c;
1357
1358 ham_SetMapTile(0, x, y, idx);
1359 }
1360
1361 void
gfx_cleartextline(int y,int startx)1362 gfx_cleartextline(int y, int startx)
1363 {
1364 int x;
1365
1366 for (x = startx; x < 30; x++)
1367 gfx_printchar(x, y, ' ');
1368 }
1369
1370 void
gfx_nudgecenter(int px,int py)1371 gfx_nudgecenter(int px, int py)
1372 {
1373 int tx, ty;
1374
1375 glb_nudgex = px;
1376 glb_nudgey = py;
1377
1378 gfx_getscrollcenter(tx, ty);
1379 gfx_scrollcenter(tx, ty);
1380 }
1381
1382 void
gfx_getscrollcenter(int & tx,int & ty)1383 gfx_getscrollcenter(int &tx, int &ty)
1384 {
1385 tx = glb_offx;
1386 ty = glb_offy;
1387 }
1388
1389
gfx_scrollcenter(int tx,int ty)1390 void gfx_scrollcenter(int tx, int ty)
1391 {
1392 // int dorebuild = 0;
1393
1394 // Clamp to the size of the map...
1395
1396 #if 0
1397 if (tx - 8 < 0)
1398 tx = 8;
1399 if (tx + 8 >= MAP_WIDTH)
1400 tx = MAP_WIDTH - 8;
1401 if (ty - 5 < 0)
1402 ty = 5;
1403 if (ty + 5 >= MAP_HEIGHT)
1404 ty = MAP_HEIGHT - 5;
1405 #endif
1406
1407 // We want the specified tx/ty to become the center of our tile map.
1408 // The problem is if it doesn't fit on our current screen, we must
1409 // scroll & rebuild.
1410 // Our screen width is 15 tiles and height 10 tiles.
1411
1412 // This code allows us to have a real screen larger than 32x32
1413 // by scrolling the area. Sadly, my scroll code sucks and costs
1414 // way too much to do, so results in horrible flickering etc,
1415 // thus I decided to go back to 32x32.
1416 #if 0
1417 if (tx + 8 - glb_scrollx > 16)
1418 {
1419 glb_scrollx += 16;
1420 dorebuild = 1;
1421 }
1422 if (tx - 8 < glb_scrollx)
1423 {
1424 glb_scrollx -= 16;
1425 dorebuild = 1;
1426 }
1427 if (ty + 5 - glb_scrolly > 16)
1428 {
1429 glb_scrolly += 16;
1430 dorebuild = 1;
1431 }
1432 if (ty - 5 < glb_scrolly)
1433 {
1434 glb_scrolly -= 16;
1435 dorebuild = 1;
1436 }
1437 if (dorebuild)
1438 {
1439 gfx_refreshtiles();
1440 }
1441 #endif
1442 // If we are running on the DS we need slightly different magic constants.
1443 // Specifically, we have 256 rather than 240 wide, so need an extra
1444 // 8 offset horizontally. Likewise, we have 192 rather than 160 vertically
1445 // so need an extra 16 vertically. Ideally we will eventually use this
1446 // extra space.
1447 #ifdef USE_EXTENDED_HEIGHT
1448 M_BG1SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1449 M_BG1SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 3*TILEHEIGHT + glb_nudgey);
1450 M_BG2SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1451 M_BG2SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 3*TILEHEIGHT + glb_nudgey);
1452 M_BG3SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1453 M_BG3SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 3*TILEHEIGHT + glb_nudgey);
1454 // We also need to set the text port
1455 M_BG0SCRLX_SET(-TILEWIDTH);
1456 M_BG0SCRLY_SET(-TILEHEIGHT*3);
1457 #elif defined(USING_DS) || defined(USING_SDL)
1458 M_BG1SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1459 M_BG1SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 2*TILEHEIGHT + glb_nudgey);
1460 M_BG2SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1461 M_BG2SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 2*TILEHEIGHT + glb_nudgey);
1462 M_BG3SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH - TILEWIDTH + glb_nudgex);
1463 M_BG3SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT - 2*TILEHEIGHT + glb_nudgey);
1464 // We also need to set the text port
1465 M_BG0SCRLX_SET(-TILEWIDTH);
1466 M_BG0SCRLY_SET(-TILEHEIGHT*2);
1467 #else
1468 M_BG1SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH + glb_nudgex);
1469 M_BG1SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT + glb_nudgey);
1470 M_BG2SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH + glb_nudgex);
1471 M_BG2SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT + glb_nudgey);
1472 M_BG3SCRLX_SET((tx-glb_scrollx)*TILEWIDTH*2 - 14*TILEWIDTH + glb_nudgex);
1473 M_BG3SCRLY_SET((ty-glb_scrolly)*TILEHEIGHT*2 - 9*TILEHEIGHT + glb_nudgey);
1474 // No need to adjust the textport in raw GBA mode.
1475 #endif
1476
1477 glb_offx = tx;
1478 glb_offy = ty;
1479 }
1480
1481 int
gfx_isnewframe()1482 gfx_isnewframe()
1483 {
1484 int result;
1485
1486 hamfake_rebuildScreen();
1487 hamfake_awaitEvent();
1488 // The event could be a new frame...
1489 result = glb_newframe;
1490 glb_newframe = 0;
1491 return result;
1492 }
1493
1494 int
gfx_getframecount()1495 gfx_getframecount()
1496 {
1497 return glb_framecount;
1498 }
1499
1500
1501 u16 glb_tilestashtiles[TILESTASH];
1502 char glb_tilestashdata[TILESTASH][4];
1503 int glb_tilestashsize = 0;
1504
1505 void
gfx_updatebitmap()1506 gfx_updatebitmap()
1507 {
1508 int i;
1509
1510 // Write out bg2tiledata to the bg2tiles.
1511 //ham_ReloadTileGfx(glb_bg2tiles, (u16 *)glb_bg2tiledata, 0, 600);
1512 //ham_ReloadTileGfx(glb_bg2tiles, (u16 *)glb_bg2tiledata, 0, glb_tilestashsize);
1513
1514 for (i = 0; i < glb_tilestashsize; i++)
1515 {
1516 ham_ReloadTileGfx(glb_bg1tiles,
1517 &((u16 *) glb_bg2tiledata)[WORDPERTILE * i],
1518 glb_tilestashtiles[i], 1);
1519 }
1520 }
1521
1522 void
gfx_putpixel(char c,int x,int y)1523 gfx_putpixel(char c, int x, int y)
1524 {
1525 int tx, ty;
1526
1527 UT_ASSERT(x >= 0 && y >= 0 && x < 240 && y < 160);
1528
1529 tx = x >> 3;
1530 ty = y >> 3;
1531 x = x & 7;
1532 y = y & 7;
1533
1534 glb_bg2tiledata[tx * BYTEPERTILE + ty * BYTEPERTILE * 30 + y * TILEWIDTH + x] = c;
1535 }
1536
1537 void
gfx_drawverline(char c,int x,int ly,int hy)1538 gfx_drawverline(char c, int x, int ly, int hy)
1539 {
1540 while (ly <= hy)
1541 gfx_putpixel(c, x, ly++);
1542 }
1543
1544 void
gfx_drawhorline(char c,int lx,int y,int hx)1545 gfx_drawhorline(char c, int lx, int y, int hx)
1546 {
1547 while (lx <= hx)
1548 gfx_putpixel(c, lx++, y);
1549 }
1550
1551 void
gfx_drawbox(char c,int lx,int ly,int hx,int hy,int solid)1552 gfx_drawbox(char c, int lx, int ly, int hx, int hy, int solid)
1553 {
1554 if (solid)
1555 {
1556 while (ly <= hy)
1557 gfx_drawhorline(c, lx, ly++, hx);
1558 }
1559 else
1560 {
1561 gfx_drawhorline(c, lx, ly, hx);
1562 gfx_drawhorline(c, lx, hy, hx);
1563 gfx_drawverline(c, lx, ly+1, hy-1);
1564 gfx_drawverline(c, hx, ly+1, hy-1);
1565 }
1566 }
1567
1568 // Given a SQUARE number give us the minimap colour.
1569 int
gfx_getcolfromtile(int tile,int mapped)1570 gfx_getcolfromtile(int tile, int mapped)
1571 {
1572 COLOUR_NAMES c;
1573
1574 if (mapped)
1575 {
1576 c = (COLOUR_NAMES) glb_squaredefs[tile].minicolour;
1577 }
1578 else
1579 {
1580 c = COLOUR_LIGHTBLACK;
1581 }
1582 return gfx_lookupcolor(c);
1583 }
1584
1585 static void
gfx_resettilestash()1586 gfx_resettilestash()
1587 {
1588 int i;
1589
1590 for (i = 0; i < glb_tilestashsize; i++)
1591 {
1592 // Mark it as freed...
1593 glb_idxcount[glb_tilestashtiles[i]] = 0;
1594 }
1595 glb_tilestashsize = 0;
1596 }
1597
1598 #ifdef USING_SDL
1599 void
gfx_fillminimapblock(char * dst,char c[4])1600 gfx_fillminimapblock(char *dst, char c[4])
1601 {
1602 int sy, sx;
1603 for (sy = 0; sy < TILEHEIGHT/2; sy++)
1604 {
1605 for (sx = 0; sx < TILEWIDTH/2; sx++)
1606 *dst++ = c[0];
1607 for (; sx < TILEWIDTH; sx++)
1608 *dst++ = c[1];
1609 }
1610
1611 for (; sy < TILEHEIGHT; sy++)
1612 {
1613 for (sx = 0; sx < TILEWIDTH/2; sx++)
1614 *dst++ = c[2];
1615 for (; sx < TILEWIDTH; sx++)
1616 *dst++ = c[3];
1617 }
1618 }
1619 #else
1620 void
gfx_fillminimapblock(char * dst,char c[4])1621 gfx_fillminimapblock(char *dst, char c[4])
1622 {
1623 int sy;
1624 for (sy = 0; sy < 4; sy++)
1625 {
1626 *dst++ = c[0];
1627 *dst++ = c[0];
1628 *dst++ = c[0];
1629 *dst++ = c[0];
1630 *dst++ = c[1];
1631 *dst++ = c[1];
1632 *dst++ = c[1];
1633 *dst++ = c[1];
1634 }
1635
1636 for (sy = 0; sy < 4; sy++)
1637 {
1638 *dst++ = c[2];
1639 *dst++ = c[2];
1640 *dst++ = c[2];
1641 *dst++ = c[2];
1642 *dst++ = c[3];
1643 *dst++ = c[3];
1644 *dst++ = c[3];
1645 *dst++ = c[3];
1646 }
1647 }
1648 #endif
1649
1650 int
gfx_findminimaptilenumber(char c[4])1651 gfx_findminimaptilenumber(char c[4])
1652 {
1653 int i;
1654 u16 tile;
1655
1656 // Search the tile stash to see if we are already in it...
1657 for (i = 0; i < glb_tilestashsize; i++)
1658 {
1659 tile = glb_tilestashtiles[i];
1660
1661 // We can flip horizontally, vertically, or both.
1662 if (c[0] == glb_tilestashdata[i][0])
1663 {
1664 // No flip.
1665 if (c[1] == glb_tilestashdata[i][1] &&
1666 c[2] == glb_tilestashdata[i][2] &&
1667 c[3] == glb_tilestashdata[i][3])
1668 {
1669 return tile;
1670 }
1671 }
1672 if (c[0] == glb_tilestashdata[i][1])
1673 {
1674 // Flip horizontally
1675 if (c[1] == glb_tilestashdata[i][0] &&
1676 c[2] == glb_tilestashdata[i][3] &&
1677 c[3] == glb_tilestashdata[i][2])
1678 {
1679 return tile | 1024;
1680 }
1681 }
1682 if (c[0] == glb_tilestashdata[i][2])
1683 {
1684 // Flip vertically
1685 if (c[1] == glb_tilestashdata[i][3] &&
1686 c[2] == glb_tilestashdata[i][0] &&
1687 c[3] == glb_tilestashdata[i][1])
1688 {
1689 return tile | 2048;
1690 }
1691 }
1692 if (c[0] == glb_tilestashdata[i][3])
1693 {
1694 // Flip both.
1695 if (c[1] == glb_tilestashdata[i][2] &&
1696 c[2] == glb_tilestashdata[i][1] &&
1697 c[3] == glb_tilestashdata[i][0])
1698 {
1699 return tile | 1024 | 2048;
1700 }
1701 }
1702 }
1703
1704 // We didn't find the tile.
1705 tile = gfx_findFreeTile(1);
1706
1707 if (tile == INVALID_TILEIDX)
1708 return gfx_lookupTile(TILE_NOTILE);
1709 // Ignore for now as we know it occrus on Quizar's map.
1710 // Seems rather mean to keep this limit since we know we have
1711 // a hard limit with gfx_findFreeTile(1).
1712 // Why not allow stash to be bigger and just make sure
1713 // we free the tile's afterwards?
1714 // UT_ASSERT(i < TILESTASH);
1715 if (i >= TILESTASH)
1716 {
1717 return gfx_lookupTile(TILE_NOTILE);
1718 }
1719
1720 i = glb_tilestashsize++;
1721
1722 glb_tilestashtiles[i] = tile;
1723
1724 glb_idxcount[tile]++;
1725
1726 // Build the tile.
1727 memcpy(glb_tilestashdata[i], c, 4);
1728
1729 gfx_fillminimapblock(&glb_bg2tiledata[i * BYTEPERTILE], c);
1730
1731 return tile;
1732 }
1733
1734 // Fill in an 8x8 block given the colour array. Colours are
1735 // (0,0), (1,0), (0,1), (1,1).
1736 void
gfx_drawminimapblock(int bx,int by,char c[4])1737 gfx_drawminimapblock(int bx, int by, char c[4])
1738 {
1739 char *dst;
1740
1741 dst = &glb_bg2tiledata[by * BYTEPERTILE * 30 + bx * BYTEPERTILE];
1742 gfx_fillminimapblock(dst, c);
1743 }
1744
1745
1746 void
gfx_displayminimap(MAP * level)1747 gfx_displayminimap(MAP *level)
1748 {
1749 gfx_resettilestash();
1750
1751 // gfx_drawbox(glb_stdcolor[COLOUR_RED], 51, 11, 189, 149, 0);
1752
1753 char c[4];
1754 int ax, ay; // AVatar position.
1755
1756 if (MOB::getAvatar())
1757 {
1758 ax = MOB::getAvatar()->getX();
1759 ay = MOB::getAvatar()->getY();
1760 }
1761 else
1762 {
1763 ax = -1;
1764 ay = -1;
1765 }
1766
1767 // The map is 32x32.
1768 // With 4 pixel size elements, we have a 128x128 map.
1769 // We draw it from 56 to 184 and 16 to 144
1770 // We have a 10 pixel border on all sides.
1771 int x, y, bx, by, tile;
1772
1773 // For efficiency, we write to one tile at a time. This greatly
1774 // improves the efficiency...
1775 y = 0;
1776 for (by = 0; by < 16; by++)
1777 {
1778 x = 0;
1779 for (bx = 0; bx < 16; bx++)
1780 {
1781 c[0] = gfx_getcolfromtile(level->getTile(x, y),
1782 level->getFlag(x, y, SQUAREFLAG_MAPPED));
1783 c[1] = gfx_getcolfromtile(level->getTile(x+1, y),
1784 level->getFlag(x+1, y, SQUAREFLAG_MAPPED));
1785 c[2] = gfx_getcolfromtile(level->getTile(x, y+1),
1786 level->getFlag(x, y+1, SQUAREFLAG_MAPPED));
1787 c[3] = gfx_getcolfromtile(level->getTile(x+1, y+1),
1788 level->getFlag(x+1, y+1, SQUAREFLAG_MAPPED));
1789
1790 if ((ax >> 1) == bx && (ay >> 1) == by)
1791 {
1792 c[(ax & 1) + ((ay &1) << 1)] = glb_stdcolor[COLOUR_WHITE];
1793 }
1794
1795 tile = gfx_findminimaptilenumber(c);
1796 ham_SetMapTile(0, bx+7, by+2, tile);
1797 // gfx_drawminimapblock(bx + 7, by + 2, c);
1798
1799 x += 2;
1800 }
1801 y += 2;
1802 }
1803
1804 #if 0
1805 for (item = level->getItemHead(); item; item = item->getNext())
1806 {
1807 gfx_drawbox(glb_stdcolor[COLOUR_GREEN],
1808 56 + item->getX() * 4 + 1, 16 + item->getY() * 4 + 1,
1809 56 + item->getX() * 4 + 2, 16 + item->getY() * 4 + 2,
1810 1);
1811 }
1812
1813 for (mob = level->getMobHead(); mob; mob = mob->getNext())
1814 {
1815 // Turn this #if off in order to get clarivoyance all the
1816 // time.
1817 #if 1
1818 if (level->hasLOS(MOB::getAvatar()->getX(), MOB::getAvatar()->getY(),
1819 mob->getX(), mob->getY()))
1820 #endif
1821 {
1822 gfx_drawbox(glb_stdcolor[COLOUR_WHITE],
1823 56 + mob->getX() * 4 + 1, 16 + mob->getY() * 4 + 1,
1824 56 + mob->getX() * 4 + 2, 16 + mob->getY() * 4 + 2,
1825 1);
1826 }
1827 }
1828 #endif
1829
1830 gfx_updatebitmap();
1831
1832 #if 0
1833 {
1834 BUF buf;
1835 buf.sprintf("%d tiles used", glb_tilestashsize);
1836 msg_report(buf);
1837 }
1838 #endif
1839 }
1840
1841 void
gfx_hideminimap()1842 gfx_hideminimap()
1843 {
1844 // Free up tiles.
1845 gfx_resettilestash();
1846
1847 // Erase the tile list.
1848 int tile, bx, by;
1849
1850 tile = gfx_lookupTile(TILE_VOID);
1851
1852 for (by = 0; by < 16; by++)
1853 {
1854 // We corrupted the text line so just write spaces over it.
1855 gfx_cleartextline(by + 2);
1856
1857 // The text clear may be a no-op if the tile was already a space
1858 // we thus explicitly set the tile to void
1859 for (bx = 0; bx < 16; bx++)
1860 ham_SetMapTile(0, bx+7, by+2, tile);
1861 }
1862 }
1863
1864 int
gfx_lookupcolor(int r,int g,int b)1865 gfx_lookupcolor(int r, int g, int b)
1866 {
1867 return gfx_lookupcolor(r, g, b, glb_tilesets[glb_tileset].palette);
1868 }
1869
1870 int
gfx_lookupcolor(int r,int g,int b,const u16 * palette)1871 gfx_lookupcolor(int r, int g, int b, const u16 *palette)
1872 {
1873 int i;
1874 int minerror, closematch, cr, cg, cb;
1875
1876 // Larger than any possible match.
1877 minerror = 255*255*3 + 1;
1878 closematch = 0;
1879 // We skip colour 0 as that is invisible.
1880 for (i = 1; i < (int)512 / 2; i++)
1881 {
1882 // Unpack the given colour from 5:5:5
1883 cb = (palette[i] >> 10) << 3;
1884 cg = ((palette[i] >> 5) & 31) << 3;
1885 cr = (palette[i] & 31) << 3;
1886
1887 // Find the difference squared.
1888 cr -= r;
1889 cg -= g;
1890 cb -= b;
1891 cr *= cr;
1892 cg *= cg;
1893 cb *= cb;
1894 cr += cg;
1895 cr += cb;
1896 // cr is now the squared difference. If this is a new low, keep.
1897 if (cr < minerror)
1898 {
1899 minerror = cr;
1900 closematch = i;
1901 }
1902 }
1903 UT_ASSERT(closematch);
1904
1905 // Result is closematch.
1906 return closematch;
1907 }
1908
1909 int
gfx_lookupcolor(COLOUR_NAMES color)1910 gfx_lookupcolor(COLOUR_NAMES color)
1911 {
1912 UT_ASSERT(color < NUM_COLOURS);
1913 return glb_stdcolor[color];
1914 }
1915
1916
1917 void
gfx_sleep(int vcycles)1918 gfx_sleep(int vcycles)
1919 {
1920 if (glbStressTest)
1921 return;
1922
1923 int start = glb_framecount;
1924
1925 while (glb_framecount < start + vcycles)
1926 {
1927 hamfake_rebuildScreen();
1928 hamfake_awaitEvent();
1929 }
1930 }
1931
1932 void
gfx_updateinventoryline(MOB * mob)1933 gfx_updateinventoryline(MOB *mob)
1934 {
1935 ITEM *item;
1936
1937 gfx_cleartextline(18);
1938 if (!mob)
1939 {
1940 gfx_printtext(0, 18, "No item slot");
1941 return;
1942 }
1943 item = mob->getItem(glb_invx, glb_invy);
1944 if (item)
1945 gfx_printtext(0, 18,
1946 gram_capitalize(item->getName(false, false, true)));
1947 else if (glb_invx == 0)
1948 {
1949 ITEMSLOT_NAMES slot = (ITEMSLOT_NAMES) glb_invy;
1950 if (mob->hasSlot(slot))
1951 gfx_printtext(0, 18,
1952 gram_capitalize(mob->getSlotName(slot)));
1953 else
1954 gfx_printtext(0, 18, "No item slot");
1955 }
1956 else
1957 gfx_printtext(0, 18, "Empty slot");
1958 }
1959
1960 void
gfx_showinventory(MOB * mob)1961 gfx_showinventory(MOB *mob)
1962 {
1963 int tx, ty, ix, iy;
1964 ITEM *item;
1965 BUF buf;
1966
1967 glb_invmob = mob;
1968
1969 // Clear out all the lines...
1970 for (ty = 2; ty < 19; ty++)
1971 gfx_cleartextline(ty);
1972
1973 // Write out the descriptions...
1974 for (iy = 0; iy < NUM_ITEMSLOTS; iy++)
1975 {
1976 gfx_printtext(0, iy * 2 + 2, glb_itemslotdefs[iy].desc1);
1977 gfx_printtext(0, iy * 2 + 3, glb_itemslotdefs[iy].desc2);
1978 }
1979
1980 // Width 12, height 8.
1981 // Our complete size 15 x 10
1982 for (iy = 0; iy < MOBINV_HEIGHT; iy++)
1983 {
1984 ty = iy + 1;
1985 for (ix = 0; ix < MOBINV_WIDTH; ix++)
1986 {
1987 tx = ix + 2 + (ix!=0);
1988
1989 // Draw the inventory item with pass thru and clear out
1990 // the overlay..
1991 item = mob->getItem(ix, iy);
1992 if (!item)
1993 gfx_setabstile(tx, ty, TILE_NORMALSLOT);
1994 else if (item->isFavorite())
1995 {
1996 gfx_setabstile(tx, ty, TILE_FAVORITESLOT);
1997 }
1998 else
1999 {
2000 if (item->isKnownCursed())
2001 {
2002 if (item->isBlessed())
2003 gfx_setabstile(tx, ty, TILE_HOLYSLOT);
2004 else if (item->isCursed())
2005 gfx_setabstile(tx, ty, TILE_CURSEDSLOT);
2006 else
2007 gfx_setabstile(tx, ty, TILE_NORMALSLOT);
2008 }
2009 else
2010 {
2011 if (item->isKnownNotCursed())
2012 gfx_setabstile(tx, ty, TILE_NORMALSLOT);
2013 else
2014 gfx_setabstile(tx, ty, TILE_EMPTYSLOT);
2015 }
2016 }
2017 if (!ix)
2018 {
2019 if (!mob->hasSlot((ITEMSLOT_NAMES) iy))
2020 {
2021 gfx_setabsmob(tx, ty, TILE_INVALIDSLOT);
2022 }
2023 else
2024 gfx_setabsmob(tx, ty,
2025 (item ? item->getTile() : TILE_VOID));
2026 }
2027 else
2028 gfx_setabsmob(tx, ty,
2029 (item ? item->getTile() : TILE_VOID));
2030 if (ix == glb_invx && iy == glb_invy)
2031 gfx_setabsoverlay(tx, ty, TILE_CURSOR);
2032 else
2033 gfx_setabsoverlay(tx, ty, TILE_VOID);
2034
2035 // If the item is quivered, add the q flag.
2036 if (item && item->isQuivered())
2037 {
2038 gfx_printtext(tx * 2 + 1, ty * 2, "q");
2039 }
2040 // Flag artifacts.
2041 if (item && item->isArtifact())
2042 {
2043 gfx_printtext(tx * 2, ty * 2, SYMBOLSTRING_UNIQUE);
2044 }
2045 // If the stack count is non-unit, we add the number.
2046 if (item && item->getStackCount() != 1)
2047 {
2048 buf.sprintf("%2d", item->getStackCount());
2049
2050 gfx_printtext(tx * 2, ty * 2 + 1, buf);
2051 }
2052 }
2053 }
2054
2055 // Update the current inventory item...
2056 gfx_updateinventoryline(mob);
2057 }
2058
2059 void
gfx_hideinventory()2060 gfx_hideinventory()
2061 {
2062 int ty;
2063
2064 // Clear out all the lines...
2065 for (ty = 2; ty < 19; ty++)
2066 gfx_cleartextline(ty);
2067
2068 gfx_refreshtiles();
2069 glb_invmob = 0;
2070 }
2071
2072 void
gfx_getinvcursor(int & ix,int & iy)2073 gfx_getinvcursor(int &ix, int &iy)
2074 {
2075 ix = glb_invx;
2076 iy = glb_invy;
2077 }
2078
2079 void
gfx_setinvcursor(int ix,int iy,bool onlyslots)2080 gfx_setinvcursor(int ix, int iy, bool onlyslots)
2081 {
2082 int tx, ty;
2083 ITEM *item;
2084
2085 // Erase from old pos...
2086 tx = glb_invx + 2 + (glb_invx != 0);
2087 ty = glb_invy + 1;
2088
2089 // but only if inventory currently up
2090 if (glb_invmob)
2091 gfx_setabsoverlay(tx, ty, TILE_VOID);
2092
2093 // Scroll the cursor as you hit the top of the inventory
2094 // If we are on the equipment column, we always stay on the
2095 // equipment column.
2096 // If we are in the inventory area, we loop only in there.
2097 // Horizontal movement never adjusts y value.
2098 if (iy < 0)
2099 {
2100 if (ix)
2101 {
2102 ix--;
2103 if (!ix)
2104 ix = MOBINV_WIDTH-1;
2105 }
2106 iy = MOBINV_HEIGHT-1;
2107 }
2108 else if (iy >= MOBINV_HEIGHT)
2109 {
2110 if (ix)
2111 {
2112 ix++;
2113 if (ix == MOBINV_WIDTH)
2114 ix = 1;
2115 }
2116 iy = 0;
2117 }
2118 else if (ix < 0)
2119 {
2120 ix = MOBINV_WIDTH-1;
2121 }
2122 else if (ix >= MOBINV_WIDTH)
2123 {
2124 ix = 0;
2125 }
2126
2127 // Ensure we never leave the itemslot list...
2128 if (onlyslots)
2129 ix = 0;
2130
2131 // ix/iy might still be out of range, so it makes sense
2132 // to reclamp here.
2133
2134 // Update new position...
2135 glb_invx = ix % MOBINV_WIDTH;
2136 if (glb_invx < 0) glb_invx += MOBINV_WIDTH;
2137 glb_invy = iy % MOBINV_HEIGHT;
2138 if (glb_invy < 0) glb_invy += MOBINV_HEIGHT;
2139 tx = glb_invx + 2 + (glb_invx != 0);
2140 ty = glb_invy + 1;
2141
2142 // Rest of this is only needed if inventory is displayed.
2143 if (!glb_invmob)
2144 return;
2145
2146 gfx_setabsoverlay(tx, ty, TILE_CURSOR);
2147
2148 // Update the current inventory item...
2149 gfx_updateinventoryline(glb_invmob);
2150 }
2151
2152 void
gfx_displaylist(int x,int y,int ylen,const char ** list,int select,int & startoflist,int & endoflist)2153 gfx_displaylist(int x, int y, int ylen, const char **list, int select, int &startoflist, int &endoflist)
2154 {
2155 int i, j, n, displaylen;
2156 int scroll;
2157 const char *entry = "You should stop reading the binary!";
2158
2159 // Calculate list size.
2160 for (n = 0; list[n]; n++);
2161
2162 // Determine the scroll offset required for select to be centered.
2163 displaylen = ylen-y;
2164 if (n < displaylen)
2165 scroll = 0; // No scrolling necessary.
2166 else
2167 {
2168 scroll = select - displaylen/2 + 1;
2169 // Do not scroll down past end.
2170 if (scroll + displaylen > n)
2171 {
2172 scroll = n - displaylen+1;
2173 }
2174 // Do not scroll up past start.
2175 // Note that a scroll of 1 merely hides the first entry, so
2176 // isn't at all useful.
2177 if (scroll <= 1)
2178 scroll = 0;
2179 }
2180
2181 // First, chew up the scrolled entries.
2182 for (i = 0; i < scroll; i++)
2183 {
2184 entry = list[i];
2185 if (!entry) break;
2186 }
2187
2188 startoflist = scroll;
2189
2190 // Now, display & clear the rest of the text.
2191 j = y;
2192 if (scroll)
2193 {
2194 // Print the scroll up message
2195 gfx_cleartextline(j, x-1);
2196 gfx_printtext(x, j, SYMBOLSTRING_UP SYMBOLSTRING_UP SYMBOLSTRING_UP);
2197
2198 j++;
2199 }
2200
2201 for (; j < ylen; j++)
2202 {
2203 gfx_cleartextline(j, x-1);
2204 if (entry)
2205 {
2206 entry = list[i];
2207 if (entry)
2208 {
2209 // Last line may be scroll down. This is true so long as
2210 // there is extra entries.
2211 if (list[i+1] && j == (ylen-1))
2212 {
2213 gfx_printtext(x, j,
2214 SYMBOLSTRING_DOWN SYMBOLSTRING_DOWN SYMBOLSTRING_DOWN);
2215 }
2216 else
2217 {
2218 gfx_printtext(x, j, entry);
2219 endoflist = i;
2220 }
2221
2222 // Highlight current selection.
2223 if (i == select)
2224 {
2225 gfx_printtext(x-1, j, SYMBOLSTRING_RIGHT);
2226 gfx_printtext(x+strlen(entry), j, SYMBOLSTRING_LEFT);
2227 }
2228 }
2229 i++;
2230 }
2231 }
2232 }
2233
2234 // Evil!
2235 void writeGlobalActionBar(bool useemptyslot);
2236
2237 // Returns selected menu entry...
2238 // x & y is where top left of menu will be. Default is menu[0].
2239 // -1 means the selection was cancelled.
2240 int
gfx_selectmenu(int x,int y,const char ** menu,int & aorb,int def,bool anykey,bool disablepaging,const u8 * menuactions,u8 * actionstrip)2241 gfx_selectmenu(int x, int y, const char **menu, int &aorb, int def,
2242 bool anykey, bool disablepaging,
2243 const u8 *menuactions,
2244 u8 *actionstrip)
2245 {
2246 int i, num, select, dx, dy;
2247 const char *entry;
2248
2249 aorb = -1;
2250
2251 num = 0;
2252 for (i = 0; (entry = menu[i]); i++)
2253 {
2254 num++;
2255 }
2256
2257 // If there are no items to pick, cancel immediately.
2258 if (!num)
2259 return -1;
2260
2261 // Now, we can treat menu as a 0..num-1 array.
2262 select = def;
2263 if (select >= num)
2264 select = 0; // Illegal default given.
2265 if (select < 0)
2266 select = 0; // Less than zero is also illegal.
2267
2268 STYLUSLOCK styluslock((!menuactions) ? REGION_MENU
2269 : (REGION_MENU | REGION_BOTTOMBUTTON | REGION_SIDEBUTTON));
2270 int startoflist = 0, endoflist = 0;
2271
2272 // If we are in drag mode, we need to free up the borders so they
2273 // can be selected.
2274 if (menuactions)
2275 styluslock.setRange(x, 0, 29, 20);
2276 else
2277 styluslock.setRange(x, -1, -1, -1);
2278
2279 // Display the menu.
2280 gfx_displaylist(x, y, 18, menu, select, startoflist, endoflist);
2281
2282 // Wait for all buttons to go up to start...
2283 // We only check for buttons that we care about. We don't want to
2284 // abort key repeat on arrow keys in case someone cancels a popup
2285 // window.
2286 #ifndef HAS_KEYBOARD
2287 while ( (ctrl_rawpressed(BUTTON_A) || ctrl_rawpressed(BUTTON_B))
2288 && !hamfake_forceQuit())
2289 {
2290 if (!gfx_isnewframe())
2291 continue;
2292 }
2293 #endif
2294 // Wipe out all of our hit states.
2295 hamfake_clearKeyboardBuffer();
2296 if (menuactions) writeGlobalActionBar(true);
2297
2298 while (1)
2299 {
2300 if (!gfx_isnewframe())
2301 continue;
2302
2303 if (hamfake_forceQuit())
2304 return -1;
2305
2306 #ifdef HAS_KEYBOARD
2307 int key;
2308
2309 key = hamfake_getKeyPress(false);
2310
2311 dx = 0;
2312 dy = 0;
2313 switch (key)
2314 {
2315 case GFX_KEYF1:
2316 case GFX_KEYF2:
2317 case GFX_KEYF3:
2318 case GFX_KEYF4:
2319 case GFX_KEYF5:
2320 case GFX_KEYF6:
2321 case GFX_KEYF7:
2322 case GFX_KEYF8:
2323 case GFX_KEYF9:
2324 case GFX_KEYF10:
2325 case GFX_KEYF11:
2326 case GFX_KEYF12:
2327 case GFX_KEYF13:
2328 case GFX_KEYF14:
2329 case GFX_KEYF15:
2330 if (menuactions)
2331 {
2332 // Bind the first 15 menu actions to our key
2333 int index = key - GFX_KEYF1;
2334
2335 actionstrip[index] = menuactions[select];
2336
2337 // Rebuild the list.
2338 writeGlobalActionBar(true);
2339 }
2340 break;
2341 case 'k':
2342 case GFX_KEYUP:
2343 case '8':
2344 dy = -1;
2345 break;
2346 case 'j':
2347 case GFX_KEYDOWN:
2348 case '2':
2349 dy = 1;
2350 break;
2351 case GFX_KEYPGUP:
2352 dy = MAX(-10, -select);
2353 break;
2354 case GFX_KEYPGDOWN:
2355 dy = MIN(10, num - select - 1);
2356 break;
2357 case 'l':
2358 case 'h':
2359 case GFX_KEYLEFT:
2360 case GFX_KEYRIGHT:
2361 case '4':
2362 case '6':
2363 dy = 0;
2364 break;
2365
2366 // Press a.
2367 case ' ':
2368 case '\n':
2369 case '\r':
2370 case '5':
2371 aorb = 0;
2372 if (anykey)
2373 aorb = BUTTON_A;
2374 if (menuactions) writeGlobalActionBar(false);
2375 return select;
2376
2377 // Press b.
2378 case '0':
2379 case '\x1b':
2380 aorb = 1;
2381 if (anykey)
2382 aorb = BUTTON_B;
2383 if (menuactions) writeGlobalActionBar(false);
2384 return select;
2385
2386 // Request for more information, a select key!
2387 case 'i':
2388 case '?':
2389 if (anykey)
2390 {
2391 aorb = BUTTON_SELECT;
2392 if (menuactions) writeGlobalActionBar(false);
2393 return select;
2394 }
2395
2396 default:
2397 dx = 0;
2398 dy = 0;
2399 break;
2400 }
2401 #else
2402 dx = dy = 0;
2403
2404 if (ctrl_hit(BUTTON_START))
2405 {
2406 // Cancel
2407 if (menuactions) writeGlobalActionBar(false);
2408 return -1;
2409 }
2410
2411 if (ctrl_hit(BUTTON_A))
2412 {
2413 // Accept....
2414 aorb = 0;
2415 if (anykey)
2416 aorb = BUTTON_A;
2417 if (menuactions) writeGlobalActionBar(false);
2418 return select;
2419 }
2420 if (ctrl_hit(BUTTON_B))
2421 {
2422 aorb = 1;
2423 if (anykey)
2424 aorb = BUTTON_B;
2425 if (menuactions) writeGlobalActionBar(false);
2426 return select;
2427 }
2428 if (anykey)
2429 {
2430 if (ctrl_hit(BUTTON_SELECT))
2431 {
2432 aorb = BUTTON_SELECT;
2433 if (menuactions) writeGlobalActionBar(false);
2434 return select;
2435 }
2436 if (ctrl_hit(BUTTON_X))
2437 {
2438 aorb = BUTTON_X;
2439 if (menuactions) writeGlobalActionBar(false);
2440 return select;
2441 }
2442 if (ctrl_hit(BUTTON_Y))
2443 {
2444 aorb = BUTTON_Y;
2445 if (menuactions) writeGlobalActionBar(false);
2446 return select;
2447 }
2448 }
2449 if (anykey && disablepaging)
2450 {
2451 if (ctrl_hit(BUTTON_L))
2452 {
2453 aorb = BUTTON_L;
2454 if (menuactions) writeGlobalActionBar(false);
2455 return select;
2456 }
2457 if (ctrl_hit(BUTTON_R))
2458 {
2459 aorb = BUTTON_R;
2460 if (menuactions) writeGlobalActionBar(false);
2461 return select;
2462 }
2463 }
2464 else
2465 {
2466 // We clamp page up/down to the limits
2467 if (ctrl_hit(BUTTON_L))
2468 dy = MAX(-10, -select);
2469 if (ctrl_hit(BUTTON_R))
2470 dy = MIN(10, num - select - 1);
2471 }
2472
2473
2474 // Change our position if dy not zero.
2475 // Only test arrow keys if r or l not pressed
2476 if (!dx && !dy)
2477 ctrl_getdir(dx, dy);
2478 #endif
2479
2480 // Process any drag requests.
2481 if (menuactions)
2482 {
2483 if (styluslock.performDrags(y*TILEHEIGHT, startoflist, endoflist, menuactions, actionstrip))
2484 {
2485 // Update...
2486 if (menuactions) writeGlobalActionBar(true);
2487 }
2488 }
2489
2490 int styluschoice;
2491 bool inbounds = false;
2492 if (styluslock.selectmenu(styluschoice, x*TILEWIDTH, y*TILEHEIGHT, inbounds))
2493 {
2494 if (!inbounds)
2495 {
2496 // User clicked outside of the menu, consider
2497 // a cancel.
2498 aorb = 1;
2499 if (anykey)
2500 aorb = BUTTON_B;
2501 if (menuactions) writeGlobalActionBar(false);
2502 return select;
2503 }
2504 else
2505 {
2506 // A real selection if this was valid.
2507 // Check for the case of up arrows.
2508 if (startoflist)
2509 styluschoice--;
2510 if (styluschoice < 0)
2511 {
2512 // Scroll up.
2513 dy = MAX(-10, -select);
2514 }
2515 else if (startoflist + styluschoice > endoflist)
2516 {
2517 // Scroll down.
2518 dy = MIN(10, num - select - 1);
2519 }
2520 else
2521 {
2522 aorb = 0;
2523 if (anykey)
2524 aorb = BUTTON_A;
2525 if (menuactions) writeGlobalActionBar(false);
2526 return startoflist + styluschoice;
2527 }
2528 }
2529 }
2530
2531 if (!dx && !dy)
2532 {
2533 // Nothing to do.
2534 continue;
2535 }
2536 else if (dx && !dy)
2537 {
2538 // Fast paging with left/right for cases where L/R
2539 // are already overload.
2540 if (ctrl_hit(BUTTON_L))
2541 dy = MAX(-10, -select);
2542 if (ctrl_hit(BUTTON_R))
2543 dy = MIN(10, num - select - 1);
2544 }
2545
2546 if (dy)
2547 {
2548 // Adjust selection...
2549 select += dy;
2550
2551 // Note: On the libdns system this triggers a compiler bug
2552 // of some sort with -O2. Basically, if dy < 0 we take the
2553 // following branch as if select == dy.
2554 if (select < 0)
2555 select = num-1;
2556 if (select >= num) // Not else so zero length lists yield 0
2557 select = 0;
2558
2559 gfx_displaylist(x, y, 18, menu, select, startoflist, endoflist);
2560 }
2561 }
2562 if (menuactions) writeGlobalActionBar(false);
2563 return -1;
2564 }
2565
2566 bool
gfx_yesnomenu(const char * prompt,bool defchoice)2567 gfx_yesnomenu(const char *prompt, bool defchoice)
2568 {
2569 int aorb;
2570 int choice, width;
2571 // Prompt...
2572 const char *menu[4] =
2573 {
2574 "Yes",
2575 "No",
2576 0
2577 };
2578
2579 width = strlen(prompt);
2580
2581 // Build an overlay for our question...
2582 {
2583 // Erase all tiles.
2584 int sx, sy;
2585 TILE_NAMES tile;
2586 for (sy = 2; sy < 6; sy++)
2587 {
2588 if (sy == 2)
2589 tile = TILE_SCROLL_TOP;
2590 else if (sy == 5)
2591 tile = TILE_SCROLL_BOTTOM;
2592 else
2593 tile = TILE_SCROLL_BACK;
2594 for (sx = 2; sx < 2 + (width+1)/2; sx++)
2595 {
2596 // We don't want to write past the end
2597 // even for a large prompt.
2598 if (sx >= 16)
2599 break;
2600 gfx_setabsoverlay(sx, sy, tile);
2601 }
2602 }
2603 }
2604
2605 // Write out the query.
2606 // This really should be made a horizontal
2607 // yes/no box!
2608 gfx_printtext(4, 6, prompt);
2609
2610 choice = gfx_selectmenu(8, 8, menu, aorb, !defchoice);
2611 if (aorb)
2612 choice = -1;
2613 if (choice >= 1)
2614 choice = -1;
2615
2616 // Clear
2617 {
2618 int y;
2619 for (y = 3; y < 19; y++)
2620 gfx_cleartextline(y);
2621 }
2622 // This is because of our overlay options...
2623 gfx_refreshtiles();
2624
2625 if (choice == 0)
2626 return true;
2627 return false;
2628 }
2629
2630 void
gfx_displayinfoblock(const char * text,bool prevline)2631 gfx_displayinfoblock(const char *text, bool prevline)
2632 {
2633 int x, y;
2634
2635 gfx_cleartextline(3);
2636 gfx_cleartextline(18);
2637 if (prevline)
2638 {
2639 gfx_printchar(0, 3, SYMBOL_UP);
2640 gfx_printchar(14, 3, SYMBOL_UP);
2641 gfx_printchar(29, 3, SYMBOL_UP);
2642 }
2643
2644 for (y = 4; y < 18; y++)
2645 {
2646 for (x = 0; x < 30; x++)
2647 {
2648 if (!*text)
2649 {
2650 gfx_printchar(x, y, ' ');
2651 }
2652 else
2653 {
2654 gfx_printchar(x, y, *text);
2655 text++;
2656 }
2657 }
2658 }
2659
2660 // If there is more text (which there should be if we get here)
2661 // prompt the fact.
2662 if (*text)
2663 {
2664 gfx_printchar(0, 18, SYMBOL_DOWN);
2665 gfx_printchar(14, 18, SYMBOL_DOWN);
2666 gfx_printchar(29, 18, SYMBOL_DOWN);
2667 }
2668 }
2669
2670 void
gfx_displayinfotext(const char * text)2671 gfx_displayinfotext(const char *text)
2672 {
2673 const char *curtext;
2674 int dx, dy, y;
2675 STYLUSLOCK styluslock(REGION_DISPLAYTEXT);
2676
2677 gfx_displayinfoblock(text, false);
2678
2679 curtext = text;
2680 while (1)
2681 {
2682 if (!gfx_isnewframe())
2683 continue;
2684
2685 if (hamfake_forceQuit())
2686 return;
2687
2688 #ifdef HAS_KEYBOARD
2689 int key;
2690 bool stop = false;
2691
2692 key = hamfake_getKeyPress(false);
2693
2694 dy = 0;
2695 switch (key)
2696 {
2697 case 'k':
2698 case GFX_KEYUP:
2699 case '8':
2700 dy = -1;
2701 break;
2702 case 'j':
2703 case GFX_KEYDOWN:
2704 case '2':
2705 dy = 1;
2706 break;
2707 case GFX_KEYPGUP:
2708 dy = -10;
2709 break;
2710 case GFX_KEYPGDOWN:
2711 dy = 10;
2712 break;
2713 case 'l':
2714 case 'h':
2715 case '4':
2716 case '6':
2717 case GFX_KEYLEFT:
2718 case GFX_KEYRIGHT:
2719 dy = 0;
2720 break;
2721 case ' ':
2722 case '5':
2723 case '0':
2724 case '\r':
2725 case '\x1b':
2726 stop = true;
2727 break;
2728 default:
2729 dy = 0;
2730 break;
2731 }
2732
2733 if (stop)
2734 break;
2735 #else
2736 if (ctrl_hit(BUTTON_A))
2737 break;
2738 if (ctrl_hit(BUTTON_B))
2739 break;
2740
2741 if (ctrl_hit(BUTTON_R))
2742 {
2743 dy = 10;
2744 }
2745 else if (ctrl_hit(BUTTON_L))
2746 {
2747 dy = -10;
2748 }
2749 else
2750 {
2751 ctrl_getdir(dx, dy);
2752 }
2753 hamfake_clearKeyboardBuffer();
2754 #endif
2755
2756 if (styluslock.getdisplaytext(dy))
2757 {
2758 dy = dy;
2759 if (!dy)
2760 break;
2761 }
2762
2763 if (!dy)
2764 continue;
2765
2766 while (dy)
2767 {
2768 if (dy < 0)
2769 {
2770 dy++;
2771 // User wants to scroll up.
2772 if (curtext != text)
2773 {
2774 curtext -= 30;
2775 }
2776 else
2777 dy = 0;
2778 }
2779 if (dy > 0)
2780 {
2781 dy--;
2782 // User wants scroll down.
2783 if (strlen(curtext) >= 30 * (18 - 4))
2784 {
2785 curtext += 30;
2786 }
2787 else
2788 dy = 0;
2789 }
2790 }
2791 gfx_displayinfoblock(curtext, curtext != text);
2792 }
2793
2794 for (y = 3; y < 19; y++)
2795 gfx_cleartextline(y);
2796 }
2797
2798 char **glb_pager = 0;
2799 int glb_pager_curline = -1, glb_pager_size = 0;
2800 int glb_pager_width = 30;
2801
2802 void
gfx_pager_setwidth(int width)2803 gfx_pager_setwidth(int width)
2804 {
2805 glb_pager_width = width;
2806 }
2807
2808 void
gfx_pager_addtext(BUF buf)2809 gfx_pager_addtext(BUF buf)
2810 {
2811 gfx_pager_addtext(buf.buffer());
2812 }
2813
2814 void
gfx_pager_addtext(const char * text)2815 gfx_pager_addtext(const char *text)
2816 {
2817 // Make sure we have a line...
2818 if (!glb_pager)
2819 {
2820 glb_pager_curline = -1;
2821 glb_pager_size = 0;
2822 gfx_pager_newline();
2823 }
2824
2825 const char *src;
2826 char *dst, *origdst;
2827 int dstlen, dstmaxlen;
2828
2829 // Point to the next write location...
2830 src = text;
2831 origdst = dst = glb_pager[glb_pager_curline];
2832 dstmaxlen = glb_pager_width;
2833 dstlen = strlen(dst);
2834 dst += dstlen;
2835
2836 while (*src && dstlen < dstmaxlen)
2837 {
2838 *dst++ = *src++;
2839 dstlen++;
2840 }
2841
2842 // If we ran out of source material, done.
2843 if (!*src)
2844 {
2845 *dst++ = '\0';
2846 return;
2847 }
2848
2849 // We have to wrap :< If src is a space, wrapping is trivial.
2850 // Otherwise, we backup src until it is a space...
2851 while (!isspace(*src) && src > text)
2852 {
2853 src--;
2854 dst--;
2855 dstlen--;
2856 }
2857
2858 // Now, move forward until we ate all the spaces (this seems odd
2859 // except if *src was a space going into the last loop)
2860 while (*src && isspace(*src))
2861 {
2862 src++;
2863 }
2864
2865 // It could be trailing spaces sent us over the bounds. We will
2866 // thus eat these and ignore them.
2867 if (!*src)
2868 {
2869 *dst++ = '\0';
2870 return;
2871 }
2872
2873 // Avoid throwing this on the stack as we recurse and GBA sucks.
2874 char *prefix = new char[30+1];
2875 prefix[0] = '\0';
2876
2877 // Special case: If src == str now, yet our dstlen == 0, we have a
2878 // single word over SCREEN_WIDTH. We resolve this by dumping
2879 // dstmaxlen - 1 chars, a hyphen, and then continuing as if we
2880 // found a space.
2881 if (src == text && !dstlen)
2882 {
2883 while (*src && dstlen < dstmaxlen-1)
2884 {
2885 *dst++ = *src++;
2886 dstlen++;
2887 }
2888 // Append a hyphen...
2889 *dst++ = '-';
2890 }
2891 // Special case: If src == str, dstlen != 0, we are tacking onto
2892 // the end of an existing word. If that word ends with a space,
2893 // great, we wrap as expected. However, we often build up
2894 // a string with a series of writes, such as "foo" followed by ", bar".
2895 // We want to wrap the last word on dst. If dst doesn't have a sub
2896 // word, obviously we avoid this.
2897 // Of course, this isn't the full story. It could be that the
2898 // previous line got to exactly 30 columns. Any spaces would be
2899 // silently eaten so it would look like there are no spaces.
2900 // To solve this, we look for special non-breaking characters that
2901 // we feel should be enjoined. Specifically, a punctuation on the
2902 // LHS means we should break, and a punctuation on the RHS means
2903 // we should not.
2904 else if (src == text)
2905 {
2906 char rhs, lhs;
2907 rhs = src[0];
2908 lhs = origdst[dstlen-1];
2909 // Check if LHS is breaking, in which case we always break
2910 // so ignore this code path.
2911 if (!isspace(lhs) && lhs != '.' && lhs != ',' && lhs != '!' && lhs != '?')
2912 {
2913 // Find the start of the word in dst...
2914 int wordstart = dstlen - 1;
2915 while (!isspace(origdst[wordstart]) && wordstart > 0)
2916 {
2917 wordstart--;
2918 }
2919
2920 // If wordstart is now zero, our previuos entry was the
2921 // full length. We might hyphenate here, but I think it
2922 // is simpler just to let the break lie where we broke
2923 // it before.
2924 if (wordstart)
2925 {
2926 // Since wordstart points to a space, we can safely
2927 // mark wordstart as null & strcpy out wordstart+1
2928 strcpy(prefix, &origdst[wordstart+1]);
2929 origdst[wordstart] = '\0';
2930 }
2931 }
2932 }
2933
2934 // Wrap to the next line. Null terminate dst and build
2935 // a new line.
2936 *dst++ = '\0';
2937 gfx_pager_newline();
2938
2939 // And, because we are too lazy to make this a loop, recurse.
2940 gfx_pager_addtext(prefix);
2941 delete [] prefix;
2942 gfx_pager_addtext(src);
2943 }
2944
2945 void
gfx_pager_addsingleline(const char * text)2946 gfx_pager_addsingleline(const char *text)
2947 {
2948 // Make sure we have a line...
2949 if (!glb_pager)
2950 {
2951 glb_pager_curline = -1;
2952 glb_pager_size = 0;
2953 gfx_pager_newline();
2954 }
2955
2956 const char *src;
2957 char *dst;
2958 int dstlen, dstmaxlen;
2959
2960 // Point to the next write location...
2961 src = text;
2962 dst = glb_pager[glb_pager_curline];
2963 dstmaxlen = glb_pager_width;
2964 dstlen = strlen(dst);
2965
2966 if (dstlen > 0)
2967 gfx_pager_newline();
2968
2969 dst = glb_pager[glb_pager_curline];
2970 dstmaxlen = glb_pager_width;
2971 dstlen = strlen(dst);
2972
2973 dst += dstlen;
2974
2975 while (*src && dstlen < dstmaxlen)
2976 {
2977 *dst++ = *src++;
2978 dstlen++;
2979 }
2980
2981 // Even if we didn't run out of source, we truncate and return.
2982 *dst++ = '\0';
2983
2984 // Ensure next line starts fresh.
2985 gfx_pager_newline();
2986 }
2987
2988 void
gfx_pager_newline()2989 gfx_pager_newline()
2990 {
2991 // Move to the next line.
2992 glb_pager_curline++;
2993
2994 // Check for over flow.
2995 if (glb_pager_curline >= glb_pager_size)
2996 {
2997 // Reallocate.
2998 char **newpager;
2999
3000 newpager = new char *[glb_pager_size + 32];
3001
3002 if (glb_pager)
3003 memcpy(newpager, glb_pager, sizeof(char *) * glb_pager_size);
3004
3005 glb_pager_size += 32;
3006 delete [] glb_pager;
3007 glb_pager = newpager;
3008 }
3009
3010 // And ensure the new buffer is allocated.
3011 glb_pager[glb_pager_curline] = new char[glb_pager_width+2];
3012 glb_pager[glb_pager_curline][0] = '\0';
3013 }
3014
3015 void
gfx_pager_separator()3016 gfx_pager_separator()
3017 {
3018 // This is so long so 80 column print outs get a full separator.
3019 gfx_pager_addsingleline("-------------------------------------------------------------------------------");
3020 }
3021
3022 void
gfx_pager_display_block(int y)3023 gfx_pager_display_block(int y)
3024 {
3025 int sx, sy;
3026 const char *text;
3027
3028 gfx_cleartextline(3);
3029 gfx_cleartextline(18);
3030 if (y)
3031 {
3032 gfx_printchar(0, 3, SYMBOL_UP);
3033 gfx_printchar(14, 3, SYMBOL_UP);
3034 gfx_printchar(29, 3, SYMBOL_UP);
3035 }
3036
3037 for (sy = 4; sy < 18; sy++, y++)
3038 {
3039 if (y > glb_pager_curline)
3040 gfx_cleartextline(sy);
3041 else
3042 {
3043 text = glb_pager[y];
3044 for (sx = 0; sx < 30; sx++)
3045 {
3046
3047 if (!*text)
3048 {
3049 gfx_printchar(sx, sy, ' ');
3050 }
3051 else
3052 {
3053 gfx_printchar(sx, sy, *text);
3054 text++;
3055 }
3056 }
3057 }
3058 }
3059
3060 // If we still have rows left, display down arrows.
3061 if (y < glb_pager_curline)
3062 {
3063 gfx_printchar(0, 18, SYMBOL_DOWN);
3064 gfx_printchar(14, 18, SYMBOL_DOWN);
3065 gfx_printchar(29, 18, SYMBOL_DOWN);
3066 }
3067 }
3068
3069 void
gfx_pager_savetofile(const char * name)3070 gfx_pager_savetofile(const char *name)
3071 {
3072 #ifdef HAS_DISKIO
3073 if (!hamfake_fatvalid())
3074 {
3075 gfx_pager_reset();
3076 return;
3077 }
3078
3079 #ifdef iPOWDER
3080 if (hamfake_cansendemail())
3081 {
3082 for (int y = 3; y < 19; y++)
3083 gfx_cleartextline(y);
3084 if (gfx_yesnomenu("Email Game Summary?", false))
3085 {
3086 // Send an email.
3087 BUF body;
3088 for (int i = 0; i <= glb_pager_curline; i++)
3089 {
3090 body.strcat(glb_pager[i]);
3091 body.strcat("\n\r");
3092 }
3093
3094 hamfake_sendemail("", name, body.buffer());
3095 }
3096 }
3097 gfx_pager_reset();
3098 return;
3099 #else
3100
3101 FILE *fp;
3102
3103 fp = hamfake_fopen(name, "wt");
3104 if (!fp)
3105 {
3106 printf("OPEN %s FAILED!\n", name);
3107 return;
3108 }
3109
3110 int i;
3111 for (i = 0; i <= glb_pager_curline; i++)
3112 {
3113 #if defined(LINUX) || defined(WIN32)
3114 fprintf(fp, "%s\n", glb_pager[i]);
3115 #else
3116 // For embedded systems always use line feeds so
3117 // the windows users can parse it easier.
3118 // Linux users are more used to crappy line feeds.
3119 fprintf(fp, "%s\r\n", glb_pager[i]);
3120 #endif
3121 }
3122
3123 fclose(fp);
3124 gfx_pager_reset();
3125 #endif
3126 #endif
3127 }
3128
3129 void hideSideActionBar();
3130
3131 void
gfx_pager_display(int y)3132 gfx_pager_display(int y)
3133 {
3134 if (!glb_pager) return;
3135
3136 int dx, dy;
3137 int tile;
3138
3139 // Make y the center.
3140 y -= (18 - 4) / 2;
3141
3142 if (y >= glb_pager_curline - (18 - 4))
3143 y = glb_pager_curline - (18 - 4);
3144
3145 // Hide the side buttons.
3146 hideSideActionBar();
3147
3148 // Clip the requested y position.
3149 if (y < 0)
3150 y = 0;
3151
3152 // Erase all tiles.
3153 int sx, sy;
3154 for (sy = 1; sy < 10; sy++)
3155 {
3156 if (sy == 1)
3157 tile = TILE_SCROLL_TOP;
3158 else if (sy == 9)
3159 tile = TILE_SCROLL_BOTTOM;
3160 else
3161 tile = TILE_SCROLL_BACK;
3162 for (sx = -1; sx < 16; sx++)
3163 {
3164 gfx_setabsoverlay(sx, sy, tile);
3165 }
3166 }
3167
3168 gfx_pager_display_block(y);
3169
3170 #ifndef HAS_KEYBOARD
3171 // Wait for no button to be hit.
3172 while (ctrl_anyrawpressed() && !hamfake_forceQuit())
3173 {
3174 hamfake_awaitEvent();
3175 }
3176 #endif
3177
3178 // Clear the repeat queue of SDL
3179 hamfake_clearKeyboardBuffer();
3180
3181 STYLUSLOCK styluslock(REGION_DISPLAYTEXT);
3182 while (1)
3183 {
3184 if (!gfx_isnewframe())
3185 continue;
3186
3187 if (hamfake_forceQuit())
3188 return;
3189
3190 // Quit immediately in stress test
3191 if (glbStressTest)
3192 break;
3193
3194 #ifdef HAS_KEYBOARD
3195 int key;
3196 bool stop = false;
3197
3198 key = hamfake_getKeyPress(false);
3199
3200 dy = 0;
3201 switch (key)
3202 {
3203 case 'k':
3204 case GFX_KEYUP:
3205 case '8':
3206 dy = -1;
3207 break;
3208 case 'j':
3209 case GFX_KEYDOWN:
3210 case '2':
3211 dy = 1;
3212 break;
3213 case GFX_KEYPGUP:
3214 dy = -10;
3215 break;
3216 case GFX_KEYPGDOWN:
3217 dy = 10;
3218 break;
3219 case 'l':
3220 case 'h':
3221 case GFX_KEYLEFT:
3222 case GFX_KEYRIGHT:
3223 case '4':
3224 case '6':
3225 dy = 0;
3226 break;
3227 case ' ':
3228 case '\n':
3229 case '\r':
3230 case '5':
3231 case '0':
3232 case '\x1b':
3233 stop = true;
3234 break;
3235 default:
3236 dy = 0;
3237 break;
3238 }
3239
3240 if (stop)
3241 break;
3242 #else
3243 if (ctrl_hit(BUTTON_A))
3244 break;
3245 if (ctrl_hit(BUTTON_B))
3246 break;
3247
3248 if (ctrl_hit(BUTTON_R))
3249 {
3250 dy = 10;
3251 }
3252 else if (ctrl_hit(BUTTON_L))
3253 {
3254 dy = -10;
3255 }
3256 else
3257 {
3258 ctrl_getdir(dx, dy);
3259 }
3260 hamfake_clearKeyboardBuffer();
3261 #endif
3262 if (styluslock.getdisplaytext(dy))
3263 {
3264 dy = dy;
3265 if (!dy)
3266 break;
3267 }
3268
3269 if (dy < 0)
3270 {
3271 // User wants to scroll up.
3272 if (y)
3273 {
3274 y += dy;
3275 if (y < 0)
3276 y = 0;
3277 gfx_pager_display_block(y);
3278 }
3279 }
3280 if (dy > 0)
3281 {
3282 if (y < glb_pager_curline - (18 - 4))
3283 {
3284 y += dy;
3285 if (y > glb_pager_curline - (18 - 4))
3286 {
3287 y = glb_pager_curline - (18 - 4);
3288 }
3289 gfx_pager_display_block(y);
3290 }
3291 }
3292 }
3293
3294 for (y = 3; y < 19; y++)
3295 gfx_cleartextline(y);
3296
3297 gfx_pager_reset();
3298
3299 // Because we did absolute sets, this will restore.
3300 gfx_refreshtiles();
3301
3302 // Clear the repeat queue of SDL
3303 hamfake_clearKeyboardBuffer();
3304 }
3305
3306 void
gfx_pager_reset()3307 gfx_pager_reset()
3308 {
3309 int y;
3310
3311 // Clear out our pager.
3312 for (y = 0; y <= glb_pager_curline; y++)
3313 {
3314 delete [] glb_pager[y];
3315 }
3316 delete [] glb_pager;
3317 glb_pager = 0;
3318 glb_pager_size = 0;
3319 glb_pager_curline = -1;
3320 }
3321
3322 bool
gfx_selecttile(int & tx,int & ty,bool quickselect,int * quickold,int * aorb,bool * stylus)3323 gfx_selecttile(int &tx, int &ty, bool quickselect, int *quickold, int *aorb,
3324 bool *stylus)
3325 {
3326 int dx, dy;
3327 int oldoverlay;
3328 bool cancel;
3329 STYLUSLOCK styluslock(REGION_MAPTILES);
3330
3331 if (stylus)
3332 *stylus = false;
3333
3334 // If we are quickselecting, our old overlay may be the cursor
3335 // itself - we require the caller to maintain its value.
3336 oldoverlay = gfx_getoverlay(tx, ty);
3337 if (quickselect)
3338 oldoverlay = quickold ? *quickold : TILE_VOID;
3339 if (aorb)
3340 *aorb = 0;
3341 gfx_setoverlay(tx, ty, TILE_CURSOR);
3342 gfx_scrollcenter(tx, ty);
3343 while (1)
3344 {
3345 if (!gfx_isnewframe())
3346 continue;
3347
3348 if (hamfake_forceQuit())
3349 return false;
3350
3351 if (ctrl_hit(BUTTON_A))
3352 {
3353 // Accept....
3354 gfx_scrollcenter(MOB::getAvatar()->getX(), MOB::getAvatar()->getY());
3355 gfx_setoverlay(tx, ty, oldoverlay);
3356 if (aorb)
3357 *aorb = 0;
3358 hamfake_clearKeyboardBuffer();
3359 return true;
3360 }
3361 if (ctrl_hit(BUTTON_B))
3362 {
3363 // Cancel
3364 gfx_scrollcenter(MOB::getAvatar()->getX(), MOB::getAvatar()->getY());
3365 gfx_setoverlay(tx, ty, oldoverlay);
3366 if (aorb)
3367 *aorb = 1;
3368 hamfake_clearKeyboardBuffer();
3369 if (quickselect)
3370 return true;
3371 else
3372 return false;
3373 }
3374
3375 if (styluslock.getmaptile(dx, dy, cancel))
3376 {
3377 // User pressed on a tile successfully.
3378 gfx_scrollcenter(MOB::getAvatar()->getX(), MOB::getAvatar()->getY());
3379 gfx_setoverlay(tx, ty, oldoverlay);
3380
3381 // Set the new location
3382 tx = dx;
3383 ty = dy;
3384
3385 // Determine the overlay value of the new tile that we
3386 // are moving the curosr to.
3387 oldoverlay = gfx_getoverlay(tx, ty);
3388 if (quickselect && quickold)
3389 *quickold = oldoverlay;
3390
3391 if (aorb)
3392 *aorb = cancel;
3393 if (stylus)
3394 *stylus = true;
3395 hamfake_clearKeyboardBuffer();
3396 return !cancel;
3397 }
3398
3399 ctrl_getdir(dx, dy);
3400 if (!dx && !dy)
3401 {
3402 // Consume any key in case an invalid key was hit
3403 hamfake_getKeyPress(false);
3404 continue;
3405 }
3406
3407 // Erase old...
3408 gfx_setoverlay(tx, ty, oldoverlay);
3409 tx += dx;
3410 ty += dy;
3411 tx &= MAP_WIDTH - 1;
3412 ty &= MAP_HEIGHT - 1;
3413 oldoverlay = gfx_getoverlay(tx, ty);
3414 if (quickselect && quickold)
3415 *quickold = oldoverlay;
3416 gfx_setoverlay(tx, ty, TILE_CURSOR);
3417 gfx_scrollcenter(tx, ty);
3418
3419 if (quickselect)
3420 return false;
3421 }
3422 }
3423
3424 void writeYesNoBar();
3425
3426 bool
gfx_selectinventory(int & selectx,int & selecty)3427 gfx_selectinventory(int &selectx, int &selecty)
3428 {
3429 bool madechoice = false;
3430 int button;
3431 int dx, dy;
3432 bool apress, bpress;
3433
3434 STYLUSLOCK styluslock(REGION_BOTTOMBUTTON);
3435
3436 writeYesNoBar();
3437
3438 gfx_setinvcursor(selectx, selecty, false);
3439 while (1)
3440 {
3441 if (!gfx_isnewframe())
3442 continue;
3443
3444 if (hamfake_forceQuit())
3445 return false;
3446
3447 ctrl_getdir(dx, dy);
3448 apress = ctrl_hit(BUTTON_A);
3449 bpress = ctrl_hit(BUTTON_B);
3450
3451 if (styluslock.getbottombutton(button))
3452 {
3453 // Either a cancel or accept.
3454 if (button == 18)
3455 apress = true;
3456 if (button == 26)
3457 bpress = true;
3458 }
3459
3460 if (bpress)
3461 {
3462 // Cancel
3463 madechoice = false;
3464 break;
3465 }
3466 if (apress)
3467 {
3468 madechoice = true;
3469 break;
3470 }
3471
3472 if (stylus_queryinventoryitem(selectx, selecty))
3473 {
3474 gfx_setinvcursor(selectx, selecty, false);
3475 continue;
3476 }
3477
3478 // Consume invalid keys.
3479 hamfake_getKeyPress(false);
3480
3481 // We refetch this as this will deal
3482 // silently with wrap.
3483 gfx_getinvcursor(selectx, selecty);
3484 selectx += dx;
3485 selecty += dy;
3486 gfx_setinvcursor(selectx, selecty, false);
3487 }
3488
3489 return madechoice;
3490 }
3491
3492 bool
gfx_selectdirection(int otx,int oty,int & rdx,int & rdy,int & rdz)3493 gfx_selectdirection(int otx, int oty, int &rdx, int &rdy, int &rdz)
3494 {
3495 int dx, dy, ndx, ndy, tx, ty;
3496 int oldoverlay;
3497 bool chose = false;
3498 STYLUSLOCK styluslock(REGION_TENDIR);
3499
3500 hamfake_flushdir();
3501 hamfake_buttonreq(5, 1);
3502
3503 rdx = rdy = rdz = 0;
3504 tx = otx;
3505 ty = oty;
3506 oldoverlay = gfx_getoverlay(tx, ty);
3507 gfx_setoverlay(tx, ty, TILE_CURSOR);
3508 dx = dy = 0;
3509 while (1)
3510 {
3511 if (!gfx_isnewframe())
3512 continue;
3513
3514 if (hamfake_forceQuit())
3515 return false;
3516
3517 if (ctrl_hit(BUTTON_A))
3518 {
3519 // Accept....
3520 gfx_setoverlay(tx, ty, oldoverlay);
3521 chose = true;
3522 break;
3523 }
3524 if (ctrl_hit(BUTTON_B))
3525 {
3526 // Cancel
3527 gfx_setoverlay(tx, ty, oldoverlay);
3528 chose = false;
3529 break;
3530 }
3531
3532 ctrl_getdir(ndx, ndy);
3533
3534 if (hamfake_externaldir(dx, dy))
3535 {
3536 gfx_setoverlay(tx, ty, oldoverlay);
3537 chose = true;
3538 break;
3539 }
3540
3541 if (styluslock.gettendir(rdx, rdy, rdz, chose))
3542 {
3543 // Invert sense of choosing.
3544 gfx_setoverlay(tx, ty, oldoverlay);
3545 chose = !chose;
3546 hamfake_buttonreq(5, 0);
3547 return chose;
3548 }
3549
3550 if (!ndx && !ndy)
3551 {
3552 // Consume any key in case an invalid key was hit
3553 hamfake_getKeyPress(false);
3554 continue;
3555 }
3556
3557 // Erase old...
3558 gfx_setoverlay(tx, ty, oldoverlay);
3559
3560 dx += ndx;
3561 dy += ndy;
3562
3563 // Clamp to our funny shape...
3564 if (dy >= 2)
3565 {
3566 dy = 2;
3567 dx = 0;
3568 }
3569 if (dy <= -2)
3570 {
3571 dy = -2;
3572 dx = 0;
3573 }
3574 if (dx < -1)
3575 dx = -1;
3576 if (dx > 1)
3577 dx = 1;
3578
3579 tx = otx + dx;
3580 ty = oty + dy;
3581 tx &= MAP_WIDTH - 1;
3582 ty &= MAP_HEIGHT - 1;
3583
3584 oldoverlay = gfx_getoverlay(tx, ty);
3585 gfx_setoverlay(tx, ty, TILE_CURSOR);
3586 }
3587
3588 // Parse out our dx,dy into rdx, rdy, rdz.
3589
3590 if (dy == 2)
3591 rdz = -1;
3592 else if (dy == -2)
3593 rdz = 1;
3594 else
3595 {
3596 rdx = dx;
3597 rdy = dy;
3598 }
3599
3600 hamfake_buttonreq(5, 0);
3601 return chose;
3602 }
3603
3604 void
gfx_copytiledata(TILE_NAMES desttile,MINI_NAMES minitile,bool ismale)3605 gfx_copytiledata(TILE_NAMES desttile, MINI_NAMES minitile, bool ismale)
3606 {
3607 const u8 *tiledata;
3608 int i;
3609 u16 tile;
3610
3611 tile = gfx_lookupTile(desttile);
3612
3613 if (tile == INVALID_TILEIDX)
3614 return;
3615
3616 if (ismale)
3617 tiledata = &glb_tilesets[glb_tileset].mini[minitile * BYTEPERTILE*4];
3618 else
3619 tiledata = &glb_tilesets[glb_tileset].minif[minitile * BYTEPERTILE*4];
3620
3621 if (!glb_comptiledata)
3622 {
3623 // Allocate a u16 to guarantee alignment.
3624 u16 *origdata = new u16 [WORDPERTILE*4];
3625
3626 glb_comptiledata = (u8 *) origdata;
3627 }
3628 for (i = 0; i < BYTEPERTILE*4; i++)
3629 glb_comptiledata[i] = tiledata[i];
3630
3631 // HMm... I think ReloadTIleGfx needs aligned data, which is not
3632 // guaranteed with a static array. No idea how the minitiles
3633 // work around this...
3634 glb_lastdesttile = tile;
3635 ham_ReloadTileGfx(glb_bg1tiles, (u16 *) glb_comptiledata, glb_lastdesttile, 4);
3636 }
3637
3638 u8 *
gfx_extractsprite(int tileset,SPRITE_NAMES tile)3639 gfx_extractsprite(int tileset, SPRITE_NAMES tile)
3640 {
3641 int w, h, x, y, srcidx;
3642 u8 *data, *dataout;
3643 u16 c;
3644
3645 w = glb_tilesets[tileset].tilewidth;
3646 h = glb_tilesets[tileset].tilewidth;
3647
3648 data = (u8 *) malloc(4 * w * h * 3);
3649 dataout = data;
3650
3651 for (y = 0; y < h * 2; y++)
3652 {
3653 for (x = 0; x < w * 2; x++)
3654 {
3655 srcidx = 0;
3656 srcidx += y * w;
3657 srcidx += x;
3658 if (x >= w)
3659 srcidx += w * h - w;
3660 if (y >= h)
3661 srcidx += w * h * 2 - w * h;
3662 c = glb_tilesets[tileset].spritepalette[glb_tilesets[tileset].sprite[tile * w * h * 4 + srcidx]];
3663
3664 int cb, cg, cr;
3665
3666 cb = (c >> 10) << 3;
3667 cg = ((c >> 5) & 31) << 3;
3668 cr = (c & 31) << 3;
3669 *dataout++ = cr;
3670 *dataout++ = cg;
3671 *dataout++ = cb;
3672 }
3673 }
3674
3675 return data;
3676 }
3677
3678 u8 *
gfx_extractlargesprite(SPRITE_NAMES tile,int grey,SPRITE_NAMES overlay)3679 gfx_extractlargesprite(SPRITE_NAMES tile, int grey, SPRITE_NAMES overlay)
3680 {
3681 #ifdef USE_VIRTUAL_SCREEN
3682 int w, h, x, y, srcidx;
3683 u8 *data, *dataout;
3684 u16 c;
3685
3686 w = 48;
3687 h = 48;
3688
3689 grey = 256 - grey;
3690 int cutoff = (grey * h + 128) / 256;
3691
3692 data = (u8 *) malloc(w * h * 4);
3693 dataout = data;
3694
3695 int row, col;
3696
3697 if (tile != SPRITE_INVALID)
3698 {
3699 row = tile / 24;
3700 col = tile - row*24;
3701
3702 for (y = 0; y < h; y++)
3703 {
3704 for (x = 0; x < w; x++)
3705 {
3706 srcidx = col * w + row * w * h * 24;
3707 srcidx += y * w * 24;
3708 srcidx += x;
3709
3710 c = bmp_sprite16_3x[srcidx];
3711
3712 int cb, cg, cr;
3713
3714 cb = (c >> 10) << 3;
3715 cg = ((c >> 5) & 31) << 3;
3716 cr = (c & 31) << 3;
3717
3718 if (y < cutoff)
3719 {
3720 cr = rgbtogrey(cr, cg, cb);
3721 cg = cb = cr;
3722 }
3723
3724 *dataout++ = cr;
3725 *dataout++ = cg;
3726 *dataout++ = cb;
3727 if (c)
3728 *dataout++ = 255;
3729 else
3730 *dataout++ = 0;
3731 }
3732 }
3733 }
3734 else
3735 {
3736 memset(data, 0, w * h * 4);
3737 }
3738
3739 if (overlay != SPRITE_INVALID)
3740 {
3741 row = overlay / 24;
3742 col = overlay - row*24;
3743
3744 dataout = data;
3745
3746 for (y = 0; y < h; y++)
3747 {
3748 for (x = 0; x < w; x++)
3749 {
3750 srcidx = col * w + row * w * h * 24;
3751 srcidx += y * w * 24;
3752 srcidx += x;
3753
3754 c = bmp_sprite16_3x[srcidx];
3755 if (!c)
3756 {
3757 dataout += 4;
3758 }
3759 else
3760 {
3761 int cb, cg, cr;
3762
3763 cb = (c >> 10) << 3;
3764 cg = ((c >> 5) & 31) << 3;
3765 cr = (c & 31) << 3;
3766
3767 *dataout++ = cr;
3768 *dataout++ = cg;
3769 *dataout++ = cb;
3770 *dataout++ = 255;
3771 }
3772 }
3773 }
3774 }
3775
3776 return data;
3777 #else
3778 return 0;
3779 #endif
3780 }
3781
3782 void
gfx_compositetile(ITEMSLOT_NAMES dstslot,MINI_NAMES minitile,bool ismale)3783 gfx_compositetile(ITEMSLOT_NAMES dstslot, MINI_NAMES minitile, bool ismale)
3784 {
3785 const u8 *minidata;
3786 int x, y, sx, sy;
3787 int dstidx, srcidx;
3788
3789 // The use of the suffix "f" to denote the female tileset is
3790 // derogratory: it implies all humans are male until they are explicitly
3791 // labeled FEmale. To atone for this, the parameter to determine gender
3792 // has the opposite sense: ismale implies it is the men that must be
3793 // explicitly specified.
3794 if (ismale)
3795 minidata = &glb_tilesets[glb_tileset].mini[minitile * 4 * BYTEPERTILE];
3796 else
3797 minidata = &glb_tilesets[glb_tileset].minif[minitile * 4 * BYTEPERTILE];
3798
3799 // Determine our offset.
3800 int offx, offy;
3801 bool flipx, flipy;
3802 offx = glb_itemslotdefs[glb_minidefs[minitile].slot].posx;
3803 offx -= glb_itemslotdefs[dstslot].posx;
3804 offy = glb_itemslotdefs[glb_minidefs[minitile].slot].posy;
3805 offy -= glb_itemslotdefs[dstslot].posy;
3806
3807 // Convert our offset into our new tilespace...
3808 if (TILEWIDTH != 8)
3809 {
3810 offx = (int) ((offx / 8.0) * TILEWIDTH + 0.5);
3811 }
3812 if (TILEHEIGHT != 8)
3813 {
3814 offy = (int) ((offy / 8.0) * TILEHEIGHT + 0.5);
3815 }
3816
3817 flipx = glb_itemslotdefs[glb_minidefs[minitile].slot].flipx ^
3818 glb_itemslotdefs[dstslot].flipx;
3819 flipy = glb_itemslotdefs[glb_minidefs[minitile].slot].flipy ^
3820 glb_itemslotdefs[dstslot].flipy;
3821
3822 for (y = 0; y < TILEHEIGHT*2; y++)
3823 {
3824 for (x = 0; x < TILEWIDTH*2; x++)
3825 {
3826 if (flipx)
3827 sx = TILEWIDTH*2-1 - x;
3828 else
3829 sx = x;
3830 if (flipy)
3831 sy = TILEHEIGHT*2-1 - y;
3832 else
3833 sy = y;
3834 sx += offx;
3835 sy += offy;
3836 if (sx < 0 || sx >= TILEWIDTH*2)
3837 continue;
3838 if (sy < 0 || sy >= TILEHEIGHT*2)
3839 continue;
3840
3841 // Get position inside sub tile.
3842 dstidx = 0;
3843 dstidx += y * TILEWIDTH;
3844 dstidx += x;
3845 // Find actual sub tile.
3846 if (x >= TILEWIDTH)
3847 dstidx += BYTEPERTILE - TILEWIDTH;
3848 if (y >= TILEHEIGHT)
3849 dstidx += BYTEPERTILE*2 - BYTEPERTILE;
3850
3851 srcidx = 0;
3852 srcidx += sy * TILEWIDTH;
3853 srcidx += sx;
3854 if (sx >= TILEWIDTH)
3855 srcidx += BYTEPERTILE - TILEWIDTH;
3856 if (sy >= TILEHEIGHT)
3857 srcidx += BYTEPERTILE*2 - BYTEPERTILE;
3858
3859 // Actual comp:
3860 if (minidata[srcidx])
3861 glb_comptiledata[dstidx] = minidata[srcidx];
3862 }
3863 }
3864
3865 ham_ReloadTileGfx(glb_bg1tiles, (u16 *) glb_comptiledata,
3866 glb_lastdesttile, 4);
3867 }
3868
3869 int
gfx_getfont()3870 gfx_getfont()
3871 {
3872 return glb_currentfont;
3873 }
3874
3875 void
gfx_switchfonts(int newfont)3876 gfx_switchfonts(int newfont)
3877 {
3878 if (newfont == glb_currentfont)
3879 return;
3880
3881 glb_currentfont = newfont;
3882
3883 // Do nothing in mode 3 or -1.
3884 if (glb_gfxmode)
3885 return;
3886
3887 // Reload all locked font tiles..
3888 int c;
3889 int sourceidx;
3890 int idx;
3891
3892 // Active alphabet tiles:
3893 for (c = 0; c < 255; c++)
3894 {
3895 idx = glb_charidx[c];
3896 if (idx != INVALID_TILEIDX)
3897 {
3898 sourceidx = gfx_getchartile(c);
3899
3900 ham_ReloadTileGfx(glb_bg1tiles,
3901 (u16*)(&glb_tilesets[glb_tileset].alphabet[glb_currentfont][sourceidx * BYTEPERTILE]),
3902 idx,
3903 1);
3904 }
3905 }
3906 }
3907
3908 void
gfx_switchtilesets(int newtileset)3909 gfx_switchtilesets(int newtileset)
3910 {
3911 int i;
3912
3913 if (newtileset == glb_tileset)
3914 return;
3915
3916 glb_tileset = newtileset;
3917 #ifdef USE_VIRTUAL_SCREEN
3918 glb_modetilesets[glb_tilesetmode] = newtileset;
3919 #endif
3920
3921 // TODO: Guard if sizes match,
3922 hamfake_setTileSize(glb_tilesets[glb_tileset].tilewidth,
3923 glb_tilesets[glb_tileset].tilewidth);
3924
3925 // Rebuild our arrays:
3926 delete [] glb_bg2tiledata;
3927 glb_bg2tiledata = (char *) new u16[WORDPERTILE*TILESTASH];
3928 memset(glb_bg2tiledata, 0, BYTEPERTILE*TILESTASH);
3929
3930 delete [] glb_comptiledata;
3931 glb_comptiledata = (u8 *) (new u16[WORDPERTILE*4]);
3932
3933 for (i = 0; i < MAX_COLOUR_CHAR; i++)
3934 {
3935 if (glb_colourchardata[i])
3936 {
3937 delete [] glb_colourchardata[i];
3938 glb_colourchardata[i] = (u8 *) new u16[WORDPERTILE];
3939 }
3940 }
3941
3942 delete [] glb_greyscaletiledata;
3943 glb_greyscaletiledata = (u8 *) (new u16[WORDPERTILE*4]);
3944
3945 // If we are in mode 3, reblit our background!
3946 if (glb_gfxmode == 3)
3947 {
3948 gfx_reblitslugandblood();
3949 for (int y = 0; y < 20; y++)
3950 {
3951 for (int x = 0; x < 32; x++)
3952 {
3953 if (glb_charmap[y*32+x] != 0xff)
3954 gfx_printcharraw(x*TILEWIDTH, y*TILEHEIGHT, glb_charmap[y*32+x]);
3955 }
3956 }
3957 }
3958
3959 // Do nothing in mode 3 or -1.
3960 if (glb_gfxmode)
3961 return;
3962
3963 // Swap palette:
3964 ham_LoadBGPal((void *)glb_tilesets[glb_tileset].palette, 512);
3965 hamfake_LoadSpritePal((void *)glb_tilesets[glb_tileset].spritepalette, 512);
3966
3967 // Rebuild our standard colours
3968 gfx_buildstdcolors();
3969
3970 // Reload the sprite.
3971 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].dungeon[glb_sprite*4*BYTEPERTILE]),
3972 0, 4);
3973
3974 // Reload all locked tiles..
3975 int c;
3976 int sourceidx;
3977 int idx;
3978
3979 // Active alphabet tiles:
3980 for (c = 0; c < 255; c++)
3981 {
3982 idx = glb_charidx[c];
3983 if (idx != INVALID_TILEIDX)
3984 {
3985 sourceidx = gfx_getchartile(c);
3986
3987 ham_ReloadTileGfx(glb_bg1tiles,
3988 (u16*)(&glb_tilesets[glb_tileset].alphabet[glb_currentfont][sourceidx * BYTEPERTILE]),
3989 idx,
3990 1);
3991 }
3992 }
3993
3994 // Active dungeon tiles:
3995 int tile;
3996
3997 for (tile = 0; tile < NUM_TILES; tile++)
3998 {
3999 idx = glb_tileidx[tile];
4000
4001 if (idx == INVALID_TILEIDX)
4002 continue;
4003
4004 ham_ReloadTileGfx(glb_bg1tiles,
4005 (u16*)(&glb_tilesets[glb_tileset].dungeon[tile * 4 * BYTEPERTILE]),
4006 idx,
4007 4);
4008 }
4009
4010 // Rebuild custom dude.
4011 if (MOB::getAvatar())
4012 MOB::getAvatar()->rebuildAppearance();
4013
4014 // Reset our center as we may have switched to a different sized
4015 // tile and the lower layer stores the offset in pixels
4016 int tx, ty;
4017 gfx_getscrollcenter(tx, ty);
4018 gfx_scrollcenter(tx, ty);
4019 }
4020
4021 int
gfx_gettileset()4022 gfx_gettileset()
4023 {
4024 return glb_tileset;
4025 }
4026
4027 int
gfx_gettilesetmode(int mode)4028 gfx_gettilesetmode(int mode)
4029 {
4030 #ifdef USE_VIRTUAL_SCREEN
4031 return glb_modetilesets[mode];
4032 #else
4033 return glb_tileset;
4034 #endif
4035 }
4036
4037 void
gfx_tilesetmode(int mode)4038 gfx_tilesetmode(int mode)
4039 {
4040 #ifdef USE_VIRTUAL_SCREEN
4041 if (mode == glb_tilesetmode)
4042 return;
4043
4044 glb_tilesetmode = mode;
4045 gfx_switchtilesets(glb_modetilesets[glb_tilesetmode]);
4046 #endif
4047 }
4048
4049 void
gfx_settilesetmode(int mode,int tileset)4050 gfx_settilesetmode(int mode, int tileset)
4051 {
4052 #ifdef USE_VIRTUAL_SCREEN
4053 glb_modetilesets[mode] = tileset;
4054 if (mode == glb_tilesetmode)
4055 gfx_switchtilesets(tileset);
4056 #else
4057 if (!mode)
4058 gfx_switchtilesets(tileset);
4059 #endif
4060 }
4061
4062 int
gfx_gettilewidth()4063 gfx_gettilewidth()
4064 {
4065 return TILEWIDTH;
4066 }
4067
4068 int
gfx_gettileheight()4069 gfx_gettileheight()
4070 {
4071 return TILEHEIGHT;
4072 }
4073
4074 void
gfx_drawcursor(int x,int y)4075 gfx_drawcursor(int x, int y)
4076 {
4077 hamfake_movesprite(0, x-TILEWIDTH, y-TILEHEIGHT);
4078 hamfake_enablesprite(0, true);
4079 }
4080
4081 void
gfx_removecursor()4082 gfx_removecursor()
4083 {
4084 hamfake_enablesprite(0, false);
4085 }
4086
4087 void
gfx_cursortile(SPRITE_NAMES tile)4088 gfx_cursortile(SPRITE_NAMES tile)
4089 {
4090 UT_ASSERT(glb_usesprites);
4091 glb_sprite = (u16) tile;
4092 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].sprite[glb_sprite*4*BYTEPERTILE]),
4093 0, 4);
4094 }
4095
4096 void
gfx_spritetile(int spriteno,SPRITE_NAMES tile)4097 gfx_spritetile(int spriteno, SPRITE_NAMES tile)
4098 {
4099 UT_ASSERT(glb_usesprites);
4100
4101 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].sprite[tile*4*BYTEPERTILE]),
4102 spriteno*4, 4);
4103 }
4104
4105
4106 u8
gfx_togreyscale(u8 src,const u16 * palette)4107 gfx_togreyscale(u8 src, const u16 *palette)
4108 {
4109 int cb, cg, cr;
4110
4111 cb = (palette[src] >> 10) << 3;
4112 cg = ((palette[src] >> 5) & 31) << 3;
4113 cr = (palette[src] & 31) << 3;
4114
4115 int grey = rgbtogrey(cr, cg, cb);
4116 return gfx_lookupcolor(grey, grey, grey, palette);
4117 }
4118
4119 void
gfx_updateGreyTables(u8 * greytable,const u16 * palette)4120 gfx_updateGreyTables(u8 *greytable, const u16 *palette)
4121 {
4122 int i;
4123
4124 for (i = 0; i < 256; i++)
4125 {
4126 greytable[i] = gfx_togreyscale(i, palette);
4127 }
4128 }
4129
4130 void
gfx_updatespritegreytable()4131 gfx_updatespritegreytable()
4132 {
4133 if (!glb_spritegreyscale)
4134 return;
4135
4136 gfx_updateGreyTables(glb_spritegreyscale,
4137 glb_tilesets[glb_tileset].spritepalette);
4138 }
4139
4140 const u8 *
gfx_getspritegreytable()4141 gfx_getspritegreytable()
4142 {
4143 if (!glb_spritegreyscale)
4144 {
4145 glb_spritegreyscale = new u8[256];
4146 gfx_updatespritegreytable();
4147 }
4148
4149 return glb_spritegreyscale;
4150 }
4151
4152
4153
4154 void
gfx_spritetile_grey(int spriteno,SPRITE_NAMES tile,int grey,SPRITE_NAMES overlay)4155 gfx_spritetile_grey(int spriteno, SPRITE_NAMES tile, int grey, SPRITE_NAMES overlay)
4156 {
4157 const u8 *greytable;
4158
4159 if (!glb_greyscaletiledata)
4160 {
4161 u16 *origdata = new u16 [WORDPERTILE*4];
4162 glb_greyscaletiledata = (u8 *) origdata;
4163 }
4164
4165 greytable = gfx_getspritegreytable();
4166
4167 u8 *src;
4168 grey = 256 - grey;
4169 int cutoff = (grey * TILEHEIGHT * 2 + 128) / 256;
4170 int i;
4171 int x, y, t, yc;
4172
4173 src = (u8 *) (&glb_tilesets[glb_tileset].sprite[tile*4*BYTEPERTILE]);
4174
4175 i = 0;
4176 for (t = 0; t < 4; t++)
4177 {
4178 if (t < 2)
4179 y = 0;
4180 else
4181 y = TILEHEIGHT;
4182
4183 for (yc = 0; yc < TILEHEIGHT; yc++)
4184 {
4185 if (y + yc >= cutoff)
4186 {
4187 for (x = 0; x < TILEWIDTH; x++)
4188 {
4189 glb_greyscaletiledata[i] = src[i];
4190 i++;
4191 }
4192 }
4193 else
4194 {
4195 for (x = 0; x < TILEWIDTH; x++)
4196 {
4197 glb_greyscaletiledata[i] = greytable[src[i]];
4198 i++;
4199 }
4200 }
4201 }
4202 }
4203
4204 if (overlay != SPRITE_INVALID)
4205 {
4206 src = (u8 *) (&glb_tilesets[glb_tileset].sprite[overlay*4*BYTEPERTILE]);
4207 for (i = 0; i < BYTEPERTILE * 4; i++)
4208 {
4209 if (src[i])
4210 glb_greyscaletiledata[i] = src[i];
4211 }
4212 }
4213
4214
4215 hamfake_ReloadSpriteGfx((u16*)glb_greyscaletiledata,
4216 spriteno*4, 4);
4217 }
4218
4219 void
gfx_spritefromdungeon(int spriteno,TILE_NAMES tile)4220 gfx_spritefromdungeon(int spriteno, TILE_NAMES tile)
4221 {
4222 UT_ASSERT(!glb_usesprites);
4223 hamfake_ReloadSpriteGfx((u16*)(&glb_tilesets[glb_tileset].dungeon[tile*4*BYTEPERTILE]),
4224 spriteno*4, 4);
4225 }
4226
4227 void
gfx_spritemode(bool usesprites)4228 gfx_spritemode(bool usesprites)
4229 {
4230 int i;
4231 // Disable all sprites
4232 for (i = 0; i < 128; i++)
4233 hamfake_enablesprite(i, false);
4234
4235 // Swap palette
4236 if (usesprites)
4237 hamfake_LoadSpritePal((void *)glb_tilesets[glb_tileset].spritepalette, 512);
4238 else
4239 hamfake_LoadSpritePal((void *)glb_tilesets[glb_tileset].palette, 512);
4240
4241 glb_usesprites = usesprites;
4242 }
4243
4244 //
4245 // Fake Ham Functions
4246 //
4247 #if !defined(USING_SDL) && !defined(USING_DS)
4248
4249 #if !defined(NO_HAM)
4250 void
hamfake_rebuildScreen()4251 hamfake_rebuildScreen()
4252 {
4253 }
4254
4255 void
hamfake_awaitEvent()4256 hamfake_awaitEvent()
4257 {
4258 }
4259
4260 u16 *
hamfake_lockScreen()4261 hamfake_lockScreen()
4262 {
4263 return ((u16 *) 0x06000000);
4264 }
4265
4266 void
hamfake_unlockScreen(u16 *)4267 hamfake_unlockScreen(u16 *)
4268 {
4269 }
4270
4271 int
hamfake_peekKeyPress()4272 hamfake_peekKeyPress()
4273 {
4274 return 0;
4275 }
4276
4277 int
hamfake_getKeyPress(bool)4278 hamfake_getKeyPress(bool)
4279 {
4280 return 0;
4281 }
4282
4283 void
hamfake_clearKeyboardBuffer()4284 hamfake_clearKeyboardBuffer()
4285 {
4286 }
4287
4288 void
hamfake_insertKeyPress(u8 key)4289 hamfake_insertKeyPress(u8 key)
4290 {
4291 }
4292
4293 void
hamfake_softReset()4294 hamfake_softReset()
4295 {
4296 return;
4297 }
4298
4299 #endif
4300
4301 int
hamfake_getKeyModifiers()4302 hamfake_getKeyModifiers()
4303 {
4304 return 0;
4305 }
4306
4307 void
hamfake_setFullScreen(bool fullscreen)4308 hamfake_setFullScreen(bool fullscreen)
4309 {
4310 }
4311
4312 bool
hamfake_isFullScreen()4313 hamfake_isFullScreen()
4314 {
4315 return true; // Always full :>
4316 }
4317
4318 bool
hamfake_extratileset()4319 hamfake_extratileset()
4320 {
4321 return false; // No fat system yet!
4322 }
4323
4324 void
hamfake_getstyluspos(int & x,int & y)4325 hamfake_getstyluspos(int &x, int &y)
4326 {
4327 x = y = 0;
4328 }
4329
4330 bool
hamfake_getstylusstate()4331 hamfake_getstylusstate()
4332 {
4333 return false;
4334 }
4335
4336 void
hamfake_movesprite(int spriteno,int x,int y)4337 hamfake_movesprite(int spriteno, int x, int y)
4338 {
4339 }
4340
4341 void
hamfake_enablesprite(int spriteno,bool enabled)4342 hamfake_enablesprite(int spriteno, bool enabled)
4343 {
4344 }
4345
4346 void
hamfake_ReloadSpriteGfx(const u16 * data,int tileno,int numtile)4347 hamfake_ReloadSpriteGfx(const u16 *data, int tileno, int numtile)
4348 {
4349 }
4350
4351 void
hamfake_LoadSpritePal(void * data,int bytes)4352 hamfake_LoadSpritePal(void *data, int bytes)
4353 {
4354 }
4355
4356 void
hamfake_setTileSize(int width,int height)4357 hamfake_setTileSize(int width, int height)
4358 {
4359 }
4360
4361 #endif
4362
4363 #if !defined(USE_VIRTUAL_SCREEN)
4364
hamfake_forceQuit()4365 bool hamfake_forceQuit() { return false; }
hamfake_awaitShutdown()4366 void hamfake_awaitShutdown() {}
hamfake_postShutdown()4367 void hamfake_postShutdown() {}
4368
hamfake_setinventorymode(bool invmode)4369 void hamfake_setinventorymode(bool invmode) {}
hamfake_enableexternalactions(bool enable)4370 void hamfake_enableexternalactions(bool enable) {}
hamfake_isunlocked()4371 bool hamfake_isunlocked() { return true; }
hamfake_externalaction(int & iact,int & ispell)4372 void hamfake_externalaction(int &iact, int &ispell)
4373 { iact = ACTION_NONE; ispell = SPELL_NONE; }
4374
hamfake_flushdir()4375 void hamfake_flushdir() {}
hamfake_externaldir(int & dx,int & dy)4376 bool hamfake_externaldir(int &dx, int &dy) { return false; }
4377
hamfake_buttonreq(int mode,int type)4378 void hamfake_buttonreq(int mode, int type) { }
4379 #endif
4380
4381