2 // Thunder Graphic User Interface
5 // (c) 2004, 2014 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 07/23/2009 Added changelog ;-)
17 #include <fstream> // Needed for tracelog
20 #include "resource.h" // Thunder graphics & sounds
26 extern SDL_Surface * screen;
28 extern uint8_t my_scr[0x14000]; // Screen buffer...
29 extern uint8_t gram1[]; // Game RAM (do here??)
30 extern uint8_t hScrollOffset; // Horizontal scroll offset
31 extern uint8_t vScrollOffset; // Vertical scroll offset
32 extern uint32_t voffsets[8];
33 extern fstream tr; // Tracelog
37 uint16_t text_life; // How long text is visible
38 bool show_text; // Whether or not to show text
39 uint16_t show_which_msg; // Which message to show
40 bool show_gui; // Whether or not to show GUI
41 uint16_t selection; // Which GUI item currently selected
42 uint16_t gui_debounce; // GUI key debounce value
43 uint16_t num_coins; // Number of coins dropped
44 uint16_t blink = 0; // Used to blink player 1 & 2 start buttons
45 uint16_t flash = 23; // Used to flash GUI lights
46 uint16_t iline = 0; // Used to roll line
47 uint16_t dcurcol = 179; // dipswitch cursor color
48 int dcurdir = 1; // Initially going up...
49 bool blink_on = false;
50 bool game_refresh; // Refresh rate user set
51 bool do_decrement; // Flag to handle decrement...
52 bool user_selected_something; // Flag for well, you know...
53 uint16_t dswitch; // Which dipswitch is selected...
58 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
59 0,0,1,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,
60 0,1,0,0,0,0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,
61 0,1,1,1,0,0,1,0,0,1,0,1,1,1,1,0,0,0,1,0,0,
62 0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,
63 0,0,1,1,0,0,0,1,1,0,0,1,0,0,1,0,1,1,1,1,0,
64 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
67 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
68 0,1,1,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,
69 0,0,0,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,
70 0,0,1,1,0,0,1,0,0,1,0,1,1,1,1,0,0,0,1,0,0,
71 0,0,0,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,
72 0,1,1,1,0,0,0,1,1,0,0,1,0,0,1,0,1,1,1,1,0,
73 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
76 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
77 0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
78 0,1,0,0,0,0,1,1,1,0,0,0,1,1,0,0,1,1,1,0,0,
79 0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,1,0,0,1,0,
80 0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,1,0,
81 0,1,1,1,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,0,
82 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0
84 uint8_t boptions[] = { // 35x9
85 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
86 0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
87 0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
88 0,1,0,0,1,0,1,1,1,0,0,1,1,1,1,0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,0,1,1,0,0,
89 0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,0,0,
90 0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,
91 0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,
92 0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,1,1,1,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,
93 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
248 user_selected_something = false;
253 // Handle key debounce
255 void HandleGUIDebounce(void)
258 gui_debounce--; // Debounce GUI keys...
260 do_decrement = !do_decrement; // Called at 60Hz, so skip decrementing blink
265 blink--; // Handle blinking stuff (Should prb go in DrawGUI)
267 flash -= 2; // Handle flashing stuff (Should prb go in DrawGUI)
275 iline = 0; // 30 pixels high, going past by 1
289 // Set the refresh rate (30/60 Hz)
291 void SetRefreshRate(bool refresh)
293 game_refresh = refresh;
298 // Whether or not GUI is showing
309 void ActivateGUI(void)
318 void DeactivateGUI(void)
325 // Draw the small icons...
327 void DrawSmallIcons(uint16_t icon_not_to_draw)
331 uint8_t * sIcons[12] = { inoguis, icoinus, ipl1sts, ipl2sts, ii30hzs,
332 ii60hzs, idipsws, ichecks, ikeycns, isnapss, iresets, ibyebys };
333 uint8_t xlens[12] = { 31, 18, 32, 35, 37, 37, 29, 23, 28, 32, 19, 19 };
334 uint8_t ylens[12] = { 31, 18, 19, 19, 21, 21, 23, 20, 16, 17, 20, 23 };
335 uint8_t xpos[11] = { 33, 48, 63, 78, 104, 0, 184, 210, 225, 240, 255 };
336 uint8_t iconidx[11] = { 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 6 };
339 iconidx[9] = 5; // 60 Hz...
341 // Draw small icons 1 to 5 on left, then 11 to 7 on right.
343 for(int i=0; i<5; i++)
345 uint16_t idx = i + icon_not_to_draw; // Get correct start pos.
350 iconmem = sIcons[iconidx[idx]];
351 xl = xlens[iconidx[idx]];
352 yl = ylens[iconidx[idx]];
354 uint32_t scadr = hScrollOffset + voffsets[vScrollOffset];
355 scadr += 320 * ((224 - yl) / 2); // Center vertically
356 scadr += xpos[i] - (xl / 2); // Center around horiz. pos.
359 for(int yy=0; yy<yl; yy++)
361 for(int xx=0; xx<xl; xx++)
363 uint8_t b = iconmem[bmpptr++];
366 my_scr[scadr + xx + yy * 320] = b;
371 for(int i=10; i>5; i--)
373 uint16_t idx = i + icon_not_to_draw; // Get correct start pos.
378 iconmem = sIcons[iconidx[idx]];
379 xl = xlens[iconidx[idx]];
380 yl = ylens[iconidx[idx]];
382 uint32_t scadr = hScrollOffset + voffsets[vScrollOffset];
383 scadr += 320 * ((224 - yl) / 2); // Center vertically
384 scadr += xpos[i] - (xl / 2); // Center around horiz. pos.
387 for(int yy=0; yy<yl; yy++)
389 for(int xx=0; xx<xl; xx++)
391 uint8_t b = iconmem[bmpptr++];
394 my_scr[scadr + xx + yy * 320] = b;
402 // Draw the large (selected) icon
404 void DrawLargeIcon(uint16_t icon)
408 uint8_t * lIcons[11] = { inoguib, icoinub, ipl1stb, ipl2stb, ii30hzb,
409 ii60hzb, idipswb, ikeycnb, isnapsb, iresetb, ibyebyb };
410 uint8_t xlens[11] = { 44, 45, 50, 52, 59, 59, 42, 45, 48, 58, 42 };
411 uint8_t ylens[11] = { 44, 40, 33, 29, 52, 52, 34, 45, 37, 40, 50 };
413 uint8_t gsubs1[24] = { 21, 21, 20, 19, 168, 168, 31, 155, 68, 68, 67, 66,
414 36, 36, 35, 34, 188, 188, 183, 181, 81, 81, 85, 80 },
415 gsubs2[24] = { 20, 20, 19, 19, 31, 31, 155, 155, 67, 67, 66, 66,
416 35, 35, 34, 34, 183, 183, 181, 181, 85, 85, 80, 80 },
417 gsubs3[24] = { 35, 34, 188, 188, 183, 181, 81, 81, 85, 80, 21, 21,
418 20, 19, 168, 168, 31, 155, 68, 68, 67, 66, 36, 36 },
419 gsubs4[24] = { 34, 34, 183, 183, 181, 181, 85, 85, 80, 80, 20, 20,
420 19, 19, 31, 31, 155, 155, 67, 67, 66, 66, 35, 35 },
421 gsubs5[24] = { 20, 20, 183, 183, 31, 31, 85, 85, 67, 67, 20, 20,
422 35, 35, 31, 31, 183, 183, 67, 67, 85, 85, 35, 35 };
424 iconmem = lIcons[icon];
435 if ((icon == REFRESH) && game_refresh)
442 if (icon == DIPSWITCH)
449 uint32_t scadr = hScrollOffset + voffsets[vScrollOffset];
450 scadr += 320 * ((224 - yl) / 2); // Center vertically
451 scadr += (288 - xl) / 2; // Center horizontally
454 for(int yy=0; yy<yl; yy++)
456 for(int xx=0; xx<xl; xx++)
458 uint8_t b = iconmem[bmpptr++];
462 if ((icon == PL1START) && (b == 235) && (num_coins) && !blink_on)
463 b = 125; // Light ON color
464 /*noguib: 44x44 [Green (hi/lo): 36/235 Orange: 168/31 Neutral:12]
465 Rainbow (ROYGBP, hi/med/lo): 21,20,19; 168, 31,155; 68,67,66;
466 36,35,34; 188,183,181; 81,85,80 */
469 uint8_t fln = (23 - flash) + 1; // Want to go forward (maybe fix it?)
472 case 36: { b = gsubs1[fln]; break; }
473 case 235: { b = gsubs2[fln]; break; }
474 case 168: { b = gsubs3[fln]; break; }
475 case 31: { b = gsubs4[fln]; break; }
476 case 12: { b = gsubs5[fln]; break; }
480 if ((icon == REFRESH) && (iline == yy) && (b == 50))
483 my_scr[scadr+xx+yy*320] = b;
488 if (!blink) // Should go here???
490 blink_on = !blink_on; // Switch blink state
497 //if (flash == 1) flash = 23; // Reset flash value
502 // Draw the dipswitch portion of the GUI
504 void DrawDipswitch(void)
506 uint8_t dseloff[16] = { 0, 1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 11, 12 };
507 uint8_t * dtxt[13] = { idstext1, idstext2, idstext3, idstext4, idstext5,
508 idstext2, idstext6, idstext7, idstext8, idstext9, idstext10, idstext11,
511 uint8_t dtx[13] = { 48, 80, 96, 82, 60, 80, 76, 57, 33, 50, 62, 65, 63 },
512 dty[13] = { 7, 9, 7, 9, 7, 9, 7, 7, 7, 9, 7, 9, 7 };
513 uint32_t dtxtoff[13] = { 4*320+24, 14*320-78, 25*320+24, 32*320-80,
514 39*320+24, 49*320-78, 4*320+24, 11*320-55, 18*320+24, 25*320-48,
515 32*320+24, 42*320-63, 53*320+24 };
516 uint32_t scadr, bmpptr;
519 uint32_t dbase = hScrollOffset + voffsets[vScrollOffset];
520 dbase += (288 - 26) / 2; // Center horizontally
521 dbase += 320 * ((224 - ((65 * 2) + 8)) / 2); // Center vertically
523 scadr = dbase; // Reset screen address
526 for(int yy=0; yy<65; yy++)
528 for(int xx=0; xx<26; xx++)
530 uint8_t b = idswitch[bmpptr++];
533 my_scr[scadr + xx + yy * 320] = b;
537 scadr = dbase + (320*73); // Reset screen address
540 for(int yy=0; yy<65; yy++)
542 for(int xx=0; xx<26; xx++)
544 uint8_t b = idswitch[bmpptr++];
547 my_scr[scadr + xx + yy * 320] = b;
551 for(int i=0; i<16; i++)
553 scadr = dbase + (5 * 320 + 5) + i * 7 * 320;
556 scadr += 17*320; // Adjust for DSW #2
560 if (gram1[0x423D + (i << 1)])
561 scadr += 12; // Adjust position if ON
563 for(int yy=0; yy<5; yy++)
565 for(int xx=0; xx<5; xx++)
567 my_scr[scadr++] = idsbutton[bmpptr++];
570 scadr += 315; // Adjust position...
574 uint8_t dselected_text = dseloff[dswitch];
576 for(int i=0; i<13; i++)
578 if (dselected_text != i)
580 scadr = dbase + dtxtoff[i];
587 for(int yy=0; yy<dty[i]; yy++)
589 for(int xx=0; xx<dtx[i]; xx++)
591 uint8_t b = dtxt[i][bmpptr++];
599 scadr += (320 - dtx[i]); // Adjust position...
604 scadr = dbase + dtxtoff[dselected_text];
606 if (dselected_text>5)
611 for(int yy=0; yy<dty[dselected_text]; yy++)
613 for(int xx=0; xx<dtx[dselected_text]; xx++)
615 uint8_t b = dtxt[dselected_text][bmpptr++];
623 scadr += (320 - dtx[dselected_text]); // Adjust position...
626 if (dswitch != 16) // Draw cursor
628 scadr = dbase + (4 * 320 + 4) + dswitch * 7 * 320;
631 scadr += 17 * 320; // Adjust for DSW #2
633 for(int xx=0; xx<19; xx++)
634 my_scr[scadr++] = dcurcol;
638 for(int xx=0; xx<5; xx++)
640 my_scr[scadr] = dcurcol;
642 my_scr[scadr] = dcurcol;
646 for(int xx=0; xx<19; xx++)
647 my_scr[scadr++] = dcurcol;
653 // The actual GUI display routine
657 if (!user_selected_something) // i.e. we're not inside a selection...
659 DrawSmallIcons(selection); // 'selection' is icon *not* to draw
660 DrawLargeIcon(selection);
664 if (selection == DIPSWITCH)
671 // User pressed left arrow handler
673 void SelectLeft(void)
679 if (!user_selected_something)
688 if (gram1[0x423D + (dswitch << 1)]) // It's switchable...
689 {} //SpawnSound(USERSOUND, SCLICK);
691 {} //SpawnSound(USERSOUND, SUNGH);
693 gram1[0x423D + (dswitch << 1)] = 0; // & turn it off
700 // User pressed right arrow handler
702 void SelectRight(void)
708 if (!user_selected_something)
713 selection = 10; // Unsigned compare
717 if (!gram1[0x423D + (dswitch << 1)]) // It's switchable...
718 {} //SpawnSound(USERSOUND, SCLICK);
720 {} //SpawnSound(USERSOUND, SUNGH);
722 gram1[0x423D+(dswitch<<1)] = 1; // & turn it on
729 // User pressed Up arrow handler
737 if (user_selected_something)
739 if (selection == DIPSWITCH)
744 dswitch = 16; // Wrap non-int
745 //snd_num = dswitch; SpawnMsg(MSHOWNUMS); // Temp...
753 // User pressed down arrow handler
755 void SelectDown(void)
761 if (user_selected_something)
763 if (selection == DIPSWITCH)
769 //snd_num = dswitch; SpawnMsg(MSHOWNUMS); // Temp...
777 // User selected something! Handle it!
779 uint8_t UserSelectedSomething(void)
781 //extern uint8_t * gram1;
787 if (!user_selected_something) // Inside a selection? no...
790 if (selection == NOGUI) // Turn off GUI
795 if (selection == COINUP) // Coin up machine
797 gram1[0x41A5]++; // Add one coin... (prob. need sep. counter)
799 gram1[0x4189] = num_coins / 10; // Should be in THUNDER.CPP?
800 gram1[0x418A] = num_coins - (gram1[0x4189] * 10);
803 if (selection == PL1START) // 1 Player start
808 show_gui = false; // Shut off GUI only if coined up
811 gram1[0x418C] = 1; // Strobe start location
814 if (selection == PL2START) // 2 Player start
823 if (selection == REFRESH) // Toggle refresh rate
825 //SpawnSound(USERSOUND, SCLICK);
828 if (selection == DIPSWITCH) // Edit game settings
830 //SpawnSound(USERSOUND, SBLAH);
831 user_selected_something = true;
832 dswitch = 0; // Set at first dipswitch
835 if (selection == OPTIONS) // Edit emulator settings
839 if (selection == KEYCONFIG) // Edit game keys
843 if (selection == SNAPSHOT) // Snapshot
845 SpawnSound(USERSOUND, SCAMERA);
849 if (selection == RESET) // Reset machine
851 //SpawnSound(USERSOUND, SRESET);
854 if (selection == EXIT)
856 SpawnSound(USERSOUND, SCYA);
861 else // Selected something inside selection...
863 if (selection == DIPSWITCH)
865 if (dswitch == 16) // Selected 'back to GUI'
867 //SpawnSound(USERSOUND, SBLAH2);
868 user_selected_something = false;
872 //SpawnSound(USERSOUND, SCLICK);
873 gram1[0x423D + (dswitch << 1)] = !gram1[0x423D + (dswitch << 1)];
877 return 0xFF; // Nothing for main to do...
881 return 0xFF; // Wasn't debounced, so return invalid
886 // Show byte passed to it
888 void ShowNumbers(int number)
890 uint8_t * bnarray[16] = { bn0, bn1, bn2, bn3, bn4, bn5, bn6, bn7, bn8, bn9,
891 bnA, bnB, bnC, bnD, bnE, bnF };
892 // uint32_t scadr = hScrollOffset + voffsets[vScrollOffset] + 642 + 2560;
893 uint32_t scadr = ((2 * 288) + 2) + (8 * 288);
896 uint8_t first_dig = number >> 4, second_dig = number & 0x0F;
898 for(int y=0; y<7; y++)
900 for(int x=0; x<6; x++)
902 if (bnarray[first_dig][bmpptr++] == 1)
903 // my_scr[scadr + x + y * 320] = 7;
904 my_scr[scadr + x + y * 288] = 7;
906 // my_scr[scadr + x + y * 320] = 0;
907 my_scr[scadr + x + y * 288] = 0;
914 for(int y=0; y<7; y++)
916 for(int x=0; x<6; x++)
918 if (bnarray[second_dig][bmpptr++] == 1)
919 // my_scr[scadr + x + y * 320] = 7;
920 my_scr[scadr + x + y * 288] = 7;
922 // my_scr[scadr + x + y * 320] = 0;
923 my_scr[scadr + x + y * 288] = 0;
932 void SpawnMsg(uint8_t msg)
934 text_life = 60; // 1 second...
935 show_text = true; // Show the damn thing...
936 show_which_msg = msg; // And tell it which message to show...
945 // Kill text if it's time
952 // Your life force is running out...
955 // Draw the message here...
956 // uint32_t scadr = hScrollOffset + voffsets[vScrollOffset] + 642;
957 uint32_t scadr = (288 * 2) + 2;
960 for(int y=0; y<7; y++)
962 for(int x=0; x<21; x++)
964 if (show_which_msg == M60FPS)
966 if (bmp1[bmpptr++] == 1)
967 // my_scr[scadr + x + y * 320] = 7;
968 my_scr[scadr + x + y * 288] = 7;
970 // my_scr[scadr + x + y * 320] = 0;
971 my_scr[scadr + x + y * 288] = 0;
973 else if (show_which_msg == M30FPS)
975 if (bmp2[bmpptr++] == 1)
976 // my_scr[scadr + x + y * 320] = 7;
977 my_scr[scadr + x + y * 288] = 7;
979 // my_scr[scadr + x + y * 320] = 0;
980 my_scr[scadr + x + y * 288] = 0;
982 else if (show_which_msg == MSNAPSHOT)
984 if (bmp3[bmpptr++] == 1)
985 // my_scr[scadr + x + y * 320] = 7;
986 my_scr[scadr + x + y * 288] = 7;
988 // my_scr[scadr + x + y * 320] = 0;
989 my_scr[scadr + x + y * 288] = 0;
994 if (show_which_msg == MSHOWNUMS)
995 ShowNumbers(snd_num);