#include <vectrex.h> static const char const *Version = "UNBLOCKME V0.2A 117 LEVELS"; #ifndef TRUE #define TRUE (0==0) #define FALSE (0!=0) #endif #define uint8_t unsigned int #define int8_t int #define int16_t long #define int32_t long long #define HORIZONTAL 0 #define VERTICAL 1 #define UP 0 #define LEFT 0 #define DOWN 1 #define RIGHT 1 #define SCALETEST 1 /* saves 5152 cycles */ // It's only the surrounding frame which needs vectors to be split in two, // so we could if we preferred have two scales, one for blocks and one // for the frame, so that the frame was drawn in single pieces. // But since we are already down to an average of 30,000 cycles, there's // no great advantage to be had from optimising more. // Although, that said, one of the corners I had to cut to get down to 30K cycles // was removing any permanent on-screen text. If we ever want that back, then // more scaling tweaks might be worth implementing. But I like the clean interface. #ifdef SCALETEST #define DRAWING_SCALE 0x60 #define SCALE 40 #define GAP 4 #define FUDGE 2 #define CURSOR_SIZE 6 #else #define DRAWING_SCALE 0xC0 #define SCALE 20 #define GAP 2 #define FUDGE 0 #define CURSOR_SIZE 3 #endif #define XOFF (3*SCALE) #define YOFF (3*SCALE) static inline void set_scale(unsigned int s) { VIA_t1_cnt_lo = s; } #ifdef DEBUG static char debug_buffer[64]; void debug_int32(int y, int x, char *var, int32_t i) { char *s = debug_buffer, *startpos, *endpos; (void)y; (void)x; while (*var != '\0') { char c; c = *var++; *s++ = c; } *s++ = ':'; *s++ = ' '; if (i < 0) *s++ = '-'; else i = -i; startpos = s; for (;;) { int32_t j = i / ((typeof(i))(10)); char digit = (char)(i - j*((typeof(i))(10))); *s++ = '0'-digit; i = j; if (i == 0) break; } endpos = s-1; //reverse(startpos, s); while (startpos < endpos) { char t = *startpos; *startpos = *endpos; *endpos = t; startpos += 1; endpos -= 1; } *s++ = 0x80; *s = '\0'; Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F(); Print_Str_d(y, x, debug_buffer); } #endif #define NUM_GAMES (42U+75U) const char const *puzzle[NUM_GAMES] __attribute__ ((section(".text"))) = { // new 75: "..JJFF" "AAG.ID" "XXG.ID" "B.HHHE" "B.KKKE" "CCC..E", "EEC..." "IHCAAA" "IHXXBK" "GG.FBK" "...FD." ".JJ.D.", "JBBKKA" "J.HHHA" "XXFE.A" "..FECC" ".GGGI." ".DDDI.", ".IDDD." ".I.AAE" "XXJBHE" "C.JBH." "CF.B.." "CFGGKK", "JJ..F." "IHHHFC" "I.EXXC" "BBE..C" ".ADD.." ".AKKGG", "G.EEKD" "G.I.KD" "..IXXJ" "BBBF.J" "..AFCC" "HHAF..", "..CCEE" "IILBJJ" "XXLB.." "KHHB.." "KF.DD." "KFAAGG", "J.IIC." "JHHHCG" "..EXXG" "BBEA.G" "...AFF" "DDDA..", "FDDD.J" "F.BEEJ" "XXBHIL" ".CCHIL" "...AKK" ".GGA..", "H....." "H.FBJ." "XXFBJ." ".A.BDD" "GA.IIC" "GAEE.C", "..AFFF" "..AHD." "XXEHDG" ".BEKKG" "JBII.G" "JLLCC.", "..DGGG" "CCD..H" "LFXXEH" "LFIIEH" "..ABJJ" "KKAB..", "H....." "H.FBJ." "XXFBJ." ".A.BDD" "GA.IIC" "GAEE.C", "..AFFF" "..AHD." "XXEHDG" ".BEKKG" "JBII.G" "JLLCC.", "..DGGG" "CCD..H" "LFXXEH" "LFIIEH" "..ABJJ" "KKAB..", "....II" "BBAAAK" "XXFGEK" ".CFGE." "JCF..." "JDDHHH", "..C.GG" "..C..E" "XXD..E" "..DIIF" "HHHB.F" "AA.B..", ".DJJKF" ".DCHKF" "XXCH.F" "..CEBB" "IGGE.." "I.AAA.", ".DGG.." ".DHAA." "XXHIBC" "EEEIBC" "K.FFF." "K..JJ.", "GIKKJF" "GI..JF" "G.AXXF" "DDAB.." "H.EB.." "H.EBCC", "..IIKB" "GCCCKB" "G.XXLF" "JJDALF" "..DAHH" "EEEA..", "..C.EE" "G.C.HH" "GXXIBJ" "FA.IBJ" "FA.ID." "F...D.", "....II" "..BAA." "XXBF.." "J.BFCC" "JEEFHD" "KKGGHD", ".FFFK." "JJ.EK." "XX.EBH" "ALL.BH" "A.DCGG" "IIDC..", "AII..." "AGGG.K" "XXC..K" "HFC.EE" "HFJ.DD" "H.JBB.", "JCCCFF" "J..GBB" "XXDG.E" "KLDAAE" "KLHHHE" "....II", "IFFKKE" "I.AAAE" "XXHD.E" "..HDBB" ".GGGC." "..JJC.", "H.IIB." "H.KKB." "GGXXDJ" "FC..DJ" "FCLEDJ" ".CLEAA", ".HKKBE" "GH.FBE" "GXXFBJ" "DLLF.J" "D.ACCC" "..A.II", "HJJDCC" "HGGD.." "XXBE.." "..BEAA" "..BKKF" ".II..F", "..BGG." "..BEE." "XXBA.." "FDDA.." "F..A.." "F..CC.", "..ACBB" "..ACK." "XXACKE" ".G...E" "JGDDFI" "J.HHFI", "...BKK" "..HBEE" "XXHI.F" "GG.I.F" "LDDDJC" "L.AAJC", "IJJF.." "IKKF.." "XXCF.." ".BCAA." "HBEGG." "HBEDD.", "FFB.J." "H.BCJ." "HXXC.." ".I.C.E" ".IDAAE" "..DGGE", "...FCC" "..BFK." "XXBHKG" "JJ.HEG" "IAAAED" "ILLL.D", "F.J..." "F.JIDH" "XXBIDH" ".CB.AA" "ECKK.." "ECGG..", "AIDDHJ" "AIBEHJ" "XXBE.." "..F..." "..FCGG" "..FC..", "I.KKDD" "I.AAAL" "XXEB.L" "..EBHH" ".GGGFC" "..JJFC", "L.GGGE" "L.BBBE" "XXFHAI" "JCFHAI" "JC.D.." ".C.DKK", "I.GGGC" "I....C" "XXEFLJ" "HHEFLJ" "D.EBBB" "DKKAAA", "DDDBJE" "LHHBJE" "L.XXJE" "CCK..." ".GKII." ".GAAFF", "FDDDKK" "F.AAJJ" "XXBGEI" "CCBGEI" ".H.LE." ".H.L..", "FKKIII" "F.JDEE" "XXJDBH" "..AABH" ".CCCBH" ".GG...", "JFFFBK" "J.E.BK" "XXE..." "..GADD" "IIGA.C" "HHHA.C", ".AA.D." "HG..D." "HGXXFI" "H..CFI" "BBBCF." "...CEE", "CCJJJH" "..KK.H" "XX..EL" "FAAAEL" "F.BIGG" "DDBI..", "..ADDD" "..ALLK" "XXAFMK" "HIIFMG" "HECCCG" ".EBBJJ", "..D.EE" "C.DHHH" "CXXJ.." "CB.J.A" "KB.FFA" "KBIIGG", "A..IKK" "AD.I.." "ADEXXJ" ".DE.CJ" ".FFFCG" "HHBBCG", ".LCCII" ".LEEEM" "XXFJ.M" "..FJDD" "G.KKHB" "GAAAHB", "BCJJJ." "BCIII." "XXFM.." "L.FMEE" "LGGDD." "KKAAHH", "IIHHGD" "EEE.GD" ".JKXXD" "FJKA.." "FBBA.." "F..ACC", "KK.CE." "BBBCEJ" "XX.CAJ" "H.DDA." "H.IGGL" "FFI..L", ".JJHHH" "EAAAGG" "EXX.FK" ".BB.FK" ".D.III" ".D.CCC", ".BFFAD" "GB.IAD" "GXXI.D" "J.CEEE" "J.C..." "..C.HH", "FFH.AA" "JJH.ID" "CXX.ID" "CBBBID" "C..LEE" "KK.LGG", "F.BBB." "F.A..." "XXA..." "..AE.." "DDDE.." "CCC...", "..HIII" "DDH..." "EXXB.." "E..BFF" "JJKKKC" "AAAGGC", ".IJJB." ".IEEB." "KXXFB." "K.HFCC" "L.HDDG" "L.AAAG", "F.IIKK" "F.JJGG" "AAAXXH" "..BBBH" ".....E" "CC.DDE", ".KJJEE" ".KDAAA" "XXD..G" "..DLBG" "FCCLB." "FIIHH.", "IIFB.." "..FB.." "AXXB.." "A.DDD." "ACCCEG" "JJHHEG", "..FBBB" "EEF.C." ".DXXC." ".DAAA." ".D...." "......", "..C.EE" "DDC.BK" "IXXABK" "ILLAHG" "IF.AHG" ".F.JJJ", "..FGGG" "DDFA.." "KXXAE." "KCCAE." "..BJJH" "IIB..H", "JIICCC" "JAAB.K" "XX.BHK" "EEEBHG" "...DDG" "..FF.G", "LG.DCC" "LG.D.B" "JG.XXB" "JKK.IE" "AAH.IE" "..HFF.", "GGG.CI" "KK..CI" "XX..EH" "JDFFEH" "JDBAA." "LLB...", "BEEK.." "B.FKHH" "XXFG.J" "CCCG.J" "DIA..." "DIA...", ".KJJFI" ".KB.FI" "XXB..I" "CCB..." "EGGH.." "EDDHAA", ".IICG." "FFFCGE" "XX.CHE" "B.JJH." "B.DAAL" "KKD..L", "IBBBHH" "I.GDDD" "XXGE.." "..GE.." ".AFFF." ".ACCJJ", "FFAB.." "C.AB.." "CXXB.." "CGIIHH" ".GEEJK" ".DD.JK", "EEEDA." "HHHDA." ".XXJA." "G..JCC" "G.BFFI" "..B..I", // original 42: "II.B.." "JHHB.E" "JAAXXE" "D.F..E" "D.F.CC" "D.FGGG", "EDDD.." "E.KICC" "XXKIAG" ".JFFAG" ".J.BB." ".J..HH", ".IIIGG" "..B..." "XXB..." "HEEJJK" "HFFADK" "CC.ADK", "..A.JJ" "DDABBI" "XXAFGI" ".CCFGE" ".HH.GE" ".....E", "HIIDDD" "H.FEJJ" "XXFEC." "....C." ".AAACG" "..BBBG", "...KBB" "I.AK.D" "I.AXXD" "GCCCHL" "GFEEHL" ".FJJH.", "..FAAA" "..FBEE" "XXFB.." "CDDD.H" "C....H" "CGG...", ".HGGAK" ".HF.AK" "XXFE.." "DDBEJJ" "C.BE.." "C.II..", "IIE..." "J.EFFL" "JXXAGL" "...AGD" "HHHABD" "CCKKB.", ".AADD." "EEEBF." "JXXBFI" "JHCKKI" "GHC..." "G.....", "...BH." "I..BH." "IXXBJC" "G.A.JC" "GEADDC" "GEFFF.", ".AABI." "HHHBI." "XX.BFD" "G.KKFD" "G.ECCL" "JJE..L", "EFFFJJ" "E.HC.D" "XXHC.D" ".GAA.." ".GKK.." "IIBBB.", ".JAAAE" ".JC..E" "XXC..." "H.CGGG" "HBBDFI" "KK.DFI", ".KJJAA" ".K.BD." "XX.BDF" "I.CCCF" "I.HGGF" "EEH...", ".G...." ".G..FF" "XXBIAE" "C.BIAE" "CDB..." "CDHH..", "AIJJJ." "AID..." "XXDBF." "GGDBFL" "E..KKL" "E.HHCC", "AAJ..C" "I.JBBC" "I.XXKC" "HH.FK." "DDDFE." "GGGFE.", ".DGG.." ".D.AFF" "XXHA.." "..HA.." "IEEECC" "I.BB..", "A.EHHH" "A.EI.." "AXXI.." ".JCCFD" ".JB.FD" ".JBGGD", "JGGEII" "J..E.F" "XXA..F" "..ACCF" "..AHDD" "BBBH..", ".JJ..." ".FF.BB" "XXA..G" "CDAIIG" "CDK.HH" "CDKEEE", "GGLL.B" "DDDEEB" "J.AXXB" "J.AHC." "IKAHC." "IK.HFF", "C.FFKK" "C.BEHH" "XXBE.." ".LBEAA" ".LJJ.G" ".IIDDG", "J..GAA" "JFFGIB" "XXDHIB" "KEDHCC" "KEDLL." "KEMM..", "DIIH.." "DB.HGG" "DBEXXJ" "AAE.LJ" "CCC.LK" "FFF..K", "..BIIC" "..BE.C" "XXBEFC" "HGGGF." "HDDA.." "JJ.A..", ".EJJ.." ".E.DBB" "XXKD.." "GGKD.." "HCCCAA" "HIIFF.", "FFEEH." "IBBBHC" "I.KXXC" "JJKD.C" "...DAA" "GGGD..", "A..FFF" "A.LLCC" "XXH..I" "JJHG.I" "BBBGKE" "DDD.KE", ".G.EED" ".GIIID" "HG.XXD" "HFFFJJ" ".AB.CC" ".AB.KK", ".FFJJJ" "BB.C.I" "KXXCGI" "KDAAGL" "HD...L" "HD.EEE", ".JHHLL" ".JCCCE" "XXBG.E" "..BGKK" "I.AAMD" "IFFFMD", "AAA..G" "CC...G" "IEEXXF" "I....F" "HJJBBB" "H...DD", "K.JJLL" "K.GGHH" "XXFE.." "DDFE.C" "AAAE.C" ".IIBB.", "EEEHB." "FKKHB." "FXXD.." "F.GDCC" "..GAAJ" "III..J", "..EE.." "..DFF." "XXD..." "..DAAA" "CCCB.." "GGGB..", "HH..IL" "CCC.IL" "XXA..K" "BFAGGK" "BF.EDD" "BJJE..", ".JJFB." "DDDFB." "XX.FGH" "E.KKGH" "E.CAAL" "IIC..L", "F.HH.." "FDDB.." "XXEB.G" "..EAAG" "..C..." "..C...", "EDD.G." "E.A.G." "XXAF.." "KK.FJJ" "CCCHHL" ".IIBBL", ".GCCFF" ".G.HD." "XX.HD." "IBBBDJ" "I.AKKJ" "I.AEEJ", }; static unsigned char patList[2]; static void drawline_patterned(int y, int x, unsigned char pat) { patList[0]=(unsigned char)y; patList[1]=(unsigned char)x; *(volatile unsigned char *)0xC829 = pat; *(volatile unsigned char *)0xC823 =0; Draw_Pat_VL(patList); } // a lot of global state in this program. int block_x, block_y; char block_tag; char dragged_block; int dragged_block_orientation; int can_move_forward, can_move_back; void draw_block(char *p, int ox, int oy, int horizontal, char block) { int h,w,blocksize; if (horizontal) { h = 1; w = 2; if ((ox < 4) && (p[oy*6+ox+2] == block)) w = 3; blocksize = w; } else { h = 2; w = 1; if ((oy < 4) && (p[(oy+2)*6+ox] == block)) h = 3; blocksize = h; } Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F(); Moveto_d((6-oy)*SCALE-YOFF-GAP, ox*SCALE-XOFF+GAP); // top-left corner of where block will be drawn if (block == block_tag) { if (horizontal) { dragged_block_orientation = HORIZONTAL; can_move_forward = (((ox + w) < 6) && (p[oy*6 + ox + w] == '.')); can_move_back = (ox > 0) && (p[oy*6 + ox - 1] == '.'); } else { dragged_block_orientation = VERTICAL; can_move_forward = (((oy + h) < 6) && (p[(oy+h)*6 + ox] == '.')); can_move_back = ((oy > 0) && (p[(oy-1)*6 + ox] == '.')); } // if there is room for this block to move, draw it dashed - otherwise draw it // solid but with Intensity_7F() ... make note of which direction movement is // possible. it may be both so need two flags: forward and back maps to right,down and up,left // depending on orientation Intensity_7F(); if (can_move_forward || can_move_back) { drawline_patterned(0, w*SCALE-GAP*2, 0xC3); drawline_patterned(-h*SCALE+GAP*2, 0, 0xC3); drawline_patterned(0,-w*SCALE+GAP*2, 0xC3); drawline_patterned(h*SCALE-GAP*2, 0, 0xC3); if (block == 'X') { // The key block drawline_patterned(-h*SCALE+GAP*2, w*SCALE-GAP*2, 0xC3); } return; } } Draw_Line_d(0, w*SCALE-GAP*2); Draw_Line_d(-h*SCALE+GAP*2, 0); Draw_Line_d(0,-w*SCALE+GAP*2); Draw_Line_d(h*SCALE-GAP*2, 0); if (block == 'X') { Draw_Line_d(-h*SCALE+GAP*2, w*SCALE-GAP*2); } } void Move(char *p, char c, int dir) { int i,j; for (j = 0; j < 6; j++) { // top to bottom for (i = 0; i < 6; i++) { // left to right if (p[j*6+i] == c) { // first one it hits, topmost or leftmost if ((i < 4) && (p[j*6+i+2] == c)) { // left of horizontal triple //fprintf(stderr, "[3] left/right\n"); if ((dir==LEFT) && (i > 0) && (p[j*6+i-1] == '.')) { // horizontal triple move left //fprintf(stderr, "[3] left\n"); p[j*6+i-1] = c; p[j*6+i+2] = '.'; } else if ((dir==RIGHT) && (i < 3) && (p[j*6+i+3] == '.')) { // horizontal triple move right //fprintf(stderr, "[3] right\n"); p[j*6+i+3] = c; p[j*6+i] = '.'; } } else if ((i < 5) && (p[j*6+i+1] == c)) { // left of horizontal pair //fprintf(stderr, "[2] left/right\n"); if ((dir==LEFT) && (i > 0) && (p[j*6+i-1] == '.')) { // horizontal pair move left //fprintf(stderr, "[2] left\n"); p[j*6+i-1] = c; p[j*6+i+1] = '.'; } else if ((dir==RIGHT) && (i < 4) && (p[j*6+i+2] == '.')) { // horizontal pair move right //fprintf(stderr, "[2] right\n"); p[j*6+i+2] = c; p[j*6+i] = '.'; } } if ((j < 4) && (p[(j+2)*6+i] == c)) { // top of vertical triple //fprintf(stderr, "[3] up/down\n"); if ((dir==UP) && (j > 0) && (p[(j-1)*6+i] == '.')) { // vertical triple move up p[(j-1)*6+i] = c; p[(j+2)*6+i] = '.'; //fprintf(stderr, "[3] up\n"); } else if ((dir==DOWN) && (j < 3) && (p[(j+3)*6+i] == '.')) { // vertical triple down p[(j+3)*6+i] = c; p[j*6+i] = '.'; //fprintf(stderr, "[3] down\n"); } } else if ((j < 5) && (p[(j+1)*6+i] == c)) { // top of vertical pair //fprintf(stderr, "[2] up/down\n"); if ((dir==UP) && (j > 0) && (p[(j-1)*6+i] == '.')) { // vertical pair move up p[(j-1)*6+i] = c; p[(j+1)*6+i] = '.'; //fprintf(stderr, "[2] up\n"); } else if ((dir==DOWN) && (j < 4) && (p[(j+2)*6+i] == '.')) { // vertical pair down p[(j+2)*6+i] = c; p[j*6+i] = '.'; //fprintf(stderr, "[2] down\n"); } } return; } } } } static unsigned int next_game = 0; void draw_screen(char *p, int show_screen_no) { int i,j; char block; char lev[5] = { ' ', ' ', ' ', 0x80, '\0' }; unsigned int tens, hundreds; Reset0Ref(); if (show_screen_no || (Vec_Btn_State&1)) { // 2413 extra cycles for this: :-( int first_digit = 2; lev[0] = lev[1] = lev[2] = ' '; hundreds = 0U; tens = next_game/10U; if (tens >= 10U) { tens -= 10U; hundreds = 1U; first_digit = 0;} if (tens && !hundreds) first_digit = 1; if (hundreds) lev[first_digit++] = '1'; if (tens || hundreds) lev[first_digit++] = (char)tens+'0'; lev[first_digit] = (char)(next_game - tens*10 - hundreds*100)+'0'; set_scale(DRAWING_SCALE); Intensity_7F(); if (hundreds) { first_digit = -21; } else if (tens && !hundreds) { first_digit = -26; } else { // single_digit first_digit = -30; } Print_Str_d(-120, first_digit, lev); } Intensity_3F(); set_scale(DRAWING_SCALE); Moveto_d(0*SCALE-YOFF-2-FUDGE, 0*SCALE-XOFF-2-FUDGE); // top-left corner of where block will be drawn // might be better to move to center of block at MOVESCALE before drawing at DRAWSCALE, // to allow for a small gap around the blocks... #ifdef SCALETEST // on vectrex it is drawing with a small gap in the center of the three // long perimeters. make sure there isn't a bug here in the handling of GAP... // (though most likely it is a vectrex issue. It looks OK in the emulator) Draw_Line_d(0, 3*SCALE+GAP); Draw_Line_d(0, 3*SCALE+GAP); #else Draw_Line_d(0, 6*SCALE+GAP*2); #endif Draw_Line_d(3*SCALE, 0); Moveto_d(SCALE+GAP*2, 0); Draw_Line_d(2*SCALE, 0); #ifdef SCALETEST Draw_Line_d(0, -3*SCALE-GAP); Draw_Line_d(0, -3*SCALE-GAP); #else Draw_Line_d(0, -6*SCALE-GAP*2); #endif #ifdef SCALETEST Draw_Line_d(-3*SCALE-GAP, 0); Draw_Line_d(-3*SCALE-GAP, 0); #else Draw_Line_d(-6*SCALE-GAP*2, 0); #endif for (j = 0; j < 6; j++) { // top to bottom for (i = 0; i < 6; i++) { // left to right block = p[j*6+i]; if (block == '.') continue; if ((i > 0) && (p[j*6+i-1] == block)) { // horizontal block handled already } else if ((i < 5) && (p[j*6+i+1] == block)) { // horizontal block draw_block(p, i,j, TRUE, block); } else if ((j > 0) && (p[(j-1)*6+i] == block)) { // vertical block handled already } else if ((j < 5) && (p[(j+1)*6+i] == block)) { // vertical block draw_block(p, i,j, FALSE, block); } else { // error } } } } void fetch_new_game(char *p) { int i,j; if (next_game == NUM_GAMES) { // Need some bells and whsitles to congratulate the player. for (;;) { // Should we add a loud musical fanfare? Wait_Recal(); Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F(); Print_Str_d(-120, -48, "CHAMPION!\x80"); draw_screen(p, FALSE); } } for (j = 0; j < 6; j++) { // top to bottom for (i = 0; i < 6; i++) { // left to right p[j*6+i] = puzzle[next_game][j*6+i]; // copy to writable string } } next_game += 1; } void Get_Cursor(char *p) { int mouse_x, mouse_y; // setting this to 0x7f or 0x80 freezes tracki //*(volatile int *)0xC81A = (int)0x80; // minimum analog resolution Joy_Analog(); // snap mouse to nearest block // avoid divides. mouse_x = (((Vec_Joy_1_X>>1)+1) >> 4) + 3; mouse_y = (((Vec_Joy_1_Y>>1)+1) >> 4) + 2; if (mouse_x < 0) mouse_x = 0; if (mouse_y < 0) mouse_y = 0; if (mouse_x > 5) mouse_x = 5; if (mouse_y > 5) mouse_y = 5; block_x = mouse_x; // grid coordinates within p[] block_y = 5-mouse_y; mouse_y = (mouse_y-3) * SCALE + SCALE/2; mouse_x = (mouse_x-3) * SCALE + SCALE/2; block_tag = p[block_y*6+block_x]; // used to highlight selected block // There is a problem displaying the cursor, where it gets distorted depending // on the y coordinate. Make sure that the cause is a vectrex problem and not // an actual bug, perhaps caused by wrong scaling of the positioning of the // beam before drawing the two bars of the X. (although it does look OK to me) Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F(); // Draw an X at the cursor Moveto_d(mouse_y-CURSOR_SIZE, mouse_x-CURSOR_SIZE); // lower left to top right Draw_Line_d(CURSOR_SIZE*2,CURSOR_SIZE*2); // / Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F(); Moveto_d(mouse_y+CURSOR_SIZE, mouse_x-CURSOR_SIZE); // upper left to lower right Draw_Line_d(-CURSOR_SIZE*2,CURSOR_SIZE*2); // \ ... } typedef enum {WAITING, MOVING, STOPPING, DONE} STATE; STATE state = WAITING; // set to DONE to skip intro animation #define set_scale(s) do { VIA_t1_cnt_lo = s; } while (0) static long reset_size; struct cartridge_t // from cartridge.c unfortunately { char copyright[11]; // copyright string, must start with "g GCE" and must end with "\x80" const void* music; // 16 bit memory adress of title music data signed int title_height; // signed 8 bit value, height of game title letters unsigned int title_width; // unsigned 8 bit value, width of game title letters int title_y; // signed 8 bit value, y coordinate of game title int title_x; // signed 8 bit value, x coordinate of game title char title[]; // game title string, must end with "\x80\x00" }; extern struct cartridge_t game_header __attribute__((section(".cartridge"))); // dropped const. void fake_title(int x, int y) { Reset0Ref(); set_scale(0x70); // default offset is -16 -72 Moveto_d(15,73); Moveto_d(game_header.title_y,game_header.title_x); Moveto_d(y,x); *(volatile long *)0xC82A = reset_size; Print_Str_d(69,-45, game_header.title); Reset0Ref(); set_scale(0x70); Moveto_d(y,x); set_scale(0x38); Moveto_d(0,1); *(volatile long *)0xC82A = (long)0xf848; // ht wd Print_Str_d(21,-38, game_header.copyright); } static int sin, cos; int main(void) { // int steps, direction; char block; int prev_x, prev_y; unsigned int Prev_Btn_State = 0; int mouse_down = 0, mouse_was_down = 0; char p[6*6]; int delay = 127; sin = -96; cos = 1; reset_size = *(volatile long *)0xC82A; for (;;) { switch (state) { case WAITING: Wait_Recal(); Intensity_7F(); fake_title(cos-31, sin); delay -= 1; if (delay == 0) { state = MOVING; delay = 2; // loops/2 } break; case MOVING: Wait_Recal(); Intensity_7F(); sin = sin - (cos >> 4); cos = cos + (sin >> 4); fake_title(cos-31, sin); if (sin == -96 && cos == 1) { delay -= 1; if (delay == 0) { state = STOPPING; delay = 0x7F; } } break; case STOPPING: Wait_Recal(); Intensity_a((unsigned int)delay); fake_title(cos-31, sin); // fade out delay -= 1; if (delay == 0) state = DONE; break; case DONE: goto PLAY; break; } } PLAY: dragged_block = 0, dragged_block_orientation = -1; can_move_forward = FALSE; can_move_back = FALSE; // short for 'dragged block can move forward' fetch_new_game(p); Vec_Joy_Mux_1_X = 1; // enable analog joystick mode Vec_Joy_Mux_1_Y = 3; Get_Cursor(p); // place result in block_x, block_y prev_x = block_x; prev_y = block_y; // initialise. for (;;) { Wait_Recal(); Reset0Ref(); set_scale(DRAWING_SCALE); block_tag = 0; Get_Cursor(p); // place result in block_x, block_y draw_screen(p, FALSE); // It is IMPORTANT that the screen is drawn before any // drag/drop actions are taken. If you don't understand why // then DO NOT move this line... if (block_tag) { // cursor is over a block, not an empty square Read_Btns(); // if we use buttons to skip forward and back through the levels, add that here... if ( ((Vec_Btn_State&3) == 3) && ((Prev_Btn_State&3) != 3) ) { if (next_game > 1) { next_game -= 2; fetch_new_game(p); } } else if ( ((Vec_Btn_State&5) == 5) && ((Prev_Btn_State&5) != 5) ) { if (next_game < NUM_GAMES) { fetch_new_game(p); } } Prev_Btn_State = Vec_Btn_State; mouse_down = ((Vec_Btn_State & 8) != 0); if (mouse_down && !mouse_was_down) { // this was a click dragged_block = block_tag; // all the cool stuff will happen on the next frame } else if (mouse_down) { // this is a continuing drag // constrain movement within allowed directions, move block to nearest square // which may be no movement at all block_tag = dragged_block;// force it to stay the same even of cursor wandering if (dragged_block_orientation == HORIZONTAL) { if ((block_x < prev_x) && can_move_back) { Move(p, block_tag, LEFT); } else if ((block_x > prev_x) && can_move_forward) { Move(p, block_tag, RIGHT); } } else if (dragged_block_orientation == VERTICAL) { if ((block_y < prev_y) && can_move_back) { Move(p, block_tag, UP); } else if ((block_y > prev_y) && can_move_forward) { Move(p, block_tag, DOWN); } } } else if (mouse_was_down) { // (but is no longer...) // end of drag - drop now. // actually if we were moving on the fly, nothing really needs to // be done except for unsetting a few variables dragged_block = 0; dragged_block_orientation = -1; can_move_forward = FALSE; can_move_back = FALSE; block_tag = 0; // probably should also do this. } else { // no change, just redraw } mouse_was_down = mouse_down; } if ((p[2*6+4] == 'X') && (p[2*6+5] == 'X')) { long t; for (t = 0L; t < 128L; t++) { // Freeze the display for a short time before starting next level! // should we add a small musical feedback? // Would also be nice to allow a pause here so that the player can // photograph the final position as proof of completion. // I considered freezing by default until a button was pressed to // move on, but rejected it as bad UI design. // But if we invent a pause button, need to show a "PAUSED" message // which then fades out to allow a clean photograph. And do we also // have to put up a message saying which button to use to unpause? // This is all getting rather messy... so in the end, doing nothing. Wait_Recal(); Reset0Ref(); set_scale(DRAWING_SCALE); Print_Str_d(-120, -76, "LEVEL SOLVED\x80"); draw_screen(p, TRUE); // force screen no to be drawn } fetch_new_game(p); } prev_x = block_x; prev_y = block_y; } (void)Version; return 0; }