From 51108b220a2f232cd7649d58d7a1abf95012a9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net> Date: Tue, 1 Oct 2024 03:09:18 -0400 Subject: [PATCH] Initial support for SyncTERM PPM/PBM graphics. Hidden behind the graphics option for now since there's still issues... the board is offset in the window, the level 1 board is framed wrong (since the graphics are always 2 cols), and it doesn't check the graphical resolution, so if you're in a weird mode, it can break. Also, the graphics are uninspired at best... just hacked them up in Gimp. --- xtrn/minesweeper/graphics.ppm | Bin 0 -> 12348 bytes xtrn/minesweeper/minesweeper.js | 222 +++++++++++++++++++++++++++++++- xtrn/minesweeper/selmask.pbm | 4 + 3 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 xtrn/minesweeper/graphics.ppm create mode 100644 xtrn/minesweeper/selmask.pbm diff --git a/xtrn/minesweeper/graphics.ppm b/xtrn/minesweeper/graphics.ppm new file mode 100644 index 0000000000000000000000000000000000000000..a723d98ffb831ed78d38b8d6eb9cc49a28d8d874 GIT binary patch literal 12348 zcmWGA<5E^|E=o--Nlj5ms#I|I^bJrbOD!tS%+FIW(la#BGd5EQ@bgtD$SF<N&CKI6 zGBr~$G~+TdHRWO$1*0J_vO{1eDS&~2ffVICN7dsGt`QhM|49LZDtxT1tx0mvsCxW~ zpVaW#xs$Z=gjDnCqn?!H{~wax{xdKTgD7!-IU!&T1`zk`+_{sG@^Y|pM1C1nj}`)G z;e$V?z-0$*_(Resnjaxth!otvkn|6c!b3uoLHr9bfgp*T{_&U$k%GGi61EUQI0?yc zaBg`y&h(6yGvQjWnvdxP{Ob8wS|O;syd1*dV`+tG!D@azD+4CNqrSYnoWa)@89@}| z@-Kq`A0~om!Rj8kIS^TEYio#NxHu$!AyGgK30Ds{2f|=rV1O{-B*aEs>LC#USB&W% zxKv0812Te(qnQse@IQt#;No!gkc1642NwgP4B~#cA#er*B!Qxm)Jp$wQ<2>UcRwVW zAW~?FAHs!5VO0-zKQaT4dgL^Z%*GX8n2Eoh6`C-xx(7Ms!`XP;gPHgl1o-f%hggb6 zVpR_bRe~fWJ;3W<NV13bUqRU!mI5J`qNy)0FUJgdtnPsbBBy*f8zO~iKHOx80%W_8 z;|r3o;c9R(Aj%-_hnoyZ_Hh4#nn&=&%D_OC^p9lTe`L2|`WF=1V3$FZK@va1e|+f; zq7p)awSqK3xLD0cPV>lY<nV-~8Hge9^bg@8lUU71OY;QPBP)RC1&AP4^C5zeVjr1} z>3)bW2$GQW08js*D1fI1FazQkO!Fb)@GyrkAc7DQ;yg6-k<&afo1pn<i62=#Bw<4= z#X~}rLEHmzErbO75<x%&AtdGLA35<eFc41v$nFOvWw1LSf>^^7Iat7IL7MQWho^aD z1|IeB^bco16eEW}B#lAD;prdFfQaK#50^zt{J7LZ0s)PL7y=Q5#4p52#E@|H5JMp8 z;QxO}K?vayD1YF=j@DK|a}UIPWRjTlj~Tyk6X51U5;j~M7XzXU;$OI%AwER&FI<-L z^bc_iJQqN?5EAZwNHjqN;Up*>gWUla!KEIo3a%W)fM~&`9y#Sh*!b1MQ$C!5M?E~* zBQx-*he&}+U06o|E`rPb5OFw(OFbl136hZX04e_=i2*B#D?h?*hr|M$OHe&p;>WKZ zVmPk+1xeV*R%2yDltKIpF%YXyA%YME1C{<ki5TnxWRE~%9O8aRd?Cvs7Zeb2g6biO zAAkQBVibfVDgA?zG^`^G(T>MGn1w&C^Z+r0cI_WX?4ywc-49U!={rE!xZDqs#Z3Ho z)PoZOJVih&BV_fEgbgtn4+&8QNk4Ei(R>Qw!es{}{lmSA<`+oRL)<@{(m&S3j~u^{ zG8_^bkU*ea`UhoONFsod5PNaC2QCZIh|GmJ6;FI2E5>C$JSpNS-yjJa%`{9dL>a_A zaAQ&0d0;OiOHrQw;VmCz2E=Vx!v~)Dkr{Z@BfA#P#-ko01tEzk|KVjmG6SL=yZd2f zK9TJoT!|l7dVpApMq+gjB-aomA?X2Lo<PcQa6$kP$f+7)DVlnS0+3P|hByY7`ysMK zmj7rGik1c7?tvt1h{<?Jh%$)#;U?31{0BMN!`To=!rc#vBZwe!vWK%FQdrf)JpyMy z6ypznh#F$je|dR1a>|Dfe&R77Ips6>`a<l&rJmIE4>t!A8jy+rVg*D562FijC5D8n zhZq7UAsGSAh1-j!9+v7+XoxN}_do<8Br)k9GeRMH;o@-fAqg8Ii-&|LgSa1V2%G`Q z2yiZhL3#O)$31ZOL!t?fN{G~`dPq3nBGJMJ5~>7ANP2*m|F{gtmHug`9+CpFnh%kJ z`xlb1v8qHCgeZgf7h(cIl9KcfaUMYu;+~y5cM?<yQ820=5)QaXc=!-Z`49zg^|%Z~ z6CZ@?AqksE65@V>DIcN$IsK!B11>Jad|a}a;-l&@!vtA`yzqgfZDL6B%tv-1el{J< MCo<(jl+oHf0BEpo4FCWD literal 0 HcmV?d00001 diff --git a/xtrn/minesweeper/minesweeper.js b/xtrn/minesweeper/minesweeper.js index a3dd700f79..2d07bbbae7 100644 --- a/xtrn/minesweeper/minesweeper.js +++ b/xtrn/minesweeper/minesweeper.js @@ -6,7 +6,7 @@ const title = "Synchronet Minesweeper"; const ini_section = "minesweeper"; -const REVISION = "$Revision: 2.15 $".split(' ')[1]; +const REVISION = "$Revision: 2.16 $".split(' ')[1]; const author = "Digital Man"; const header_height = 4; const winners_list = js.exec_dir + "winners.jsonl"; @@ -33,6 +33,26 @@ const winner_subject = "Winner"; const highscores_subject = "High Scores"; const tear_line = "\r\n--- " + js.exec_file + " " + REVISION + "\r\n"; const selectors = ["()", "[]", "<>", "{}", "--", " "]; +const gfile = "graphics.ppm"; +const mfile = "selmask.pbm"; +const image = { + 'selected': 208 +}; +image[attr_count + '1'] = 0; +image[attr_count + '2'] = 16; +image[attr_count + '3'] = 32; +image[attr_count + '4'] = 48; +image[attr_count + '5'] = 64; +image[attr_count + '6'] = 80; +image[attr_count + '7'] = 96; +image[attr_count + '8'] = 112; +image[char_empty] = 128; +image[char_covered] = 240; +image[char_mine] = 144; +image[char_flag] = 160; +image[char_unsure] = 176; +image[char_badflag] = 192; +image[char_detonated_mine] = 224; require("sbbsdefs.js", "K_NONE"); require("mouse_getkey.js", "mouse_getkey"); @@ -87,6 +107,7 @@ var win_rank = false; var view_details = false; var cell_width; // either 3 or 2 var best = null; +var graph = false; log(LOG_DEBUG, title + " options: " + JSON.stringify(options)); @@ -544,7 +565,18 @@ function draw_cell(x, y) left = "\x01n\x01h" + selectors[selector%selectors.length][0]; right = "\x01n\x01h" + selectors[selector%selectors.length][1]; } - console.print(left + val + right); + if (graph) { + const margin = Math.floor((console.screen_columns - (game.width * cell_width)) / 2); + var xpos = (x * cell_width + margin) * 8; + var ypos = (header_height + y + top) * 16; + console.write('\x1b_SyncTERM:P;Paste;SX='+image[val]+';SY=0;SW=16;SH=16;DX='+xpos+';DY='+ypos+';B=0\x1b\\'); + if (selected.x == x && selected.y == y) { + console.write('\x1b_SyncTERM:P;Paste;SX='+image['selected']+';SY=0;SW=16;SH=16;DX='+xpos+';DY='+ypos+';MBUF;B=0\x1b\\'); + } + } + else { + console.print(left + val + right); + } } // Return total number of surrounding flags @@ -711,7 +743,9 @@ function draw_board(full) draw_border(); for(var x = 0; x < game.width; x++) { if(full || board[y][x].changed !== false) { - if(console.term_supports(USER_ANSI)) + if (graph) { + } + else if(console.term_supports(USER_ANSI)) console.gotoxy((x * cell_width) + margin + 1, header_height + y + top + 1); else { console.creturn(); @@ -743,7 +777,9 @@ function draw_board(full) console.attributes = LIGHTGRAY; } if(redraw_selection) { // We need to draw/redraw the selected cell last in this case - if(console.term_supports(USER_ANSI)) + if (graph) { + } + else if(console.term_supports(USER_ANSI)) console.gotoxy(margin + (selected.x * cell_width) + 1, header_height + selected.y + top + 1); else { console.up(height - (selected.y + 1)); @@ -885,7 +921,7 @@ function init_game(difficulty) game.width = game.height; game.height = Math.min(game.height, console.screen_rows - header_height); game.width += game.width - game.height; - if(game.width > 10 && (game.width * 3) + 2 > (console.screen_columns - 20)) + if(graph || (game.width > 10 && (game.width * 3) + 2 > (console.screen_columns - 20))) cell_width = 2; else cell_width = 3; @@ -946,8 +982,181 @@ function screen_to_board(mouse) return true; } +function read_apc() +{ + var ret = ''; + var ch; + var state = 0; + + for(;;) { + ch = console.getbyte(1000); + if (ch === null) + return undefined; + switch(state) { + case 0: + if (ch == 0x1b) { + state++; + break; + } + break; + case 1: + if (ch == 95) { + state++; + break; + } + state = 0; + break; + case 2: + if (ch == 0x1b) { + state++; + break; + } + ret += ascii(ch); + break; + case 3: + if (ch == 92) { + return ret; + } + return undefined; + } + } + return undefined; +} + + +function pixel_capability() +{ + var ret = false; + var ch; + var state = 0; + var optval = 0; + + for(;;) { + ch = console.getbyte(); + switch(state) { + case 0: + if (ch == 0x1b) { // ESC + state++; + break; + } + break; + case 1: + if (ch == 91) { // [ + state++; + break; + } + state = 0; + break; + case 2: + if (ch == 60) { // < + state++; + break; + } + state = 0; + break; + case 3: + if (ch == 48) { // 0 + state++; + break; + } + state = 0; + break; + case 4: + if (ch == 59) { // ; + state++; + break; + } + state = 0; + break; + case 5: + if (ch >= ascii('0') && ch <= ascii('9')) { + optval = optval * 10 + (ch - ascii('0')); + break; + } + else if(ch == 59) { + if (optval === 3) + ret = true; + optval = 0; + break; + } + else if (ch === 99) { // c + if (optval === 3) + ret = true; + return ret; + } + state = 0; + break; + } + } + return ret; +} + +function detect_graphics() +{ + var tmpckpt; + var f; + var md5; + var lst; + var m; + var b; + + // Detect PPM graphics and load the cache + graph = false; + tmpckpt = console.ctrlkey_passthru; + if (console.cterm_version >= 1316) { + console.ctrlkey_passthru = '+['; + console.write('\x1b[<c'); + graph = pixel_capability(); + } + + if (graph) { + // Load up cache... + f = new File(js.exec_dir+'/'+gfile); + if (f.open('rb')) { + md5 = f.md5_hex; + console.write('\x1b_SyncTERM:C;L;minesweeper/'+gfile+'\x1b\\'); + lst = read_apc(); + m = lst.match(/\ngraphics.ppm\t([0-9a-f]+)\n/); + if (m == null || m[1] !== md5) { + // Store in cache... + console.write('\x1b_SyncTERM:C;S;minesweeper/'+gfile+';'); + f.base64 = true; + console.write(f.read()); + console.write('\x1b\\'); + } + f.close(); + console.write('\x1b_SyncTERM:C;LoadPPM;B=0;minesweeper/'+gfile+'\x1b\\'); + f = new File(js.exec_dir+'/'+mfile); + if (f.open('rb')) { + md5 = f.md5_hex; + console.write('\x1b_SyncTERM:C;L;minesweeper/'+mfile+'\x1b\\'); + lst = read_apc(); + m = lst.match(/\nselmask.pbm\t([0-9a-f]+)\n/); + if (m == null || m[1] !== md5) { + // Store in cache... + console.write('\x1b_SyncTERM:C;S;minesweeper/'+mfile+';'); + f.base64 = true; + console.write(f.read()); + console.write('\x1b\\'); + } + f.close(); + console.write('\x1b_SyncTERM:C;LoadPBM;minesweeper/'+mfile+'\x1b\\'); + } + else { + graph = false; + } + } + else { + graph = false; + } + } + console.ctrlkey_passthru = tmpckpt; +} + function play() { + if (graph) + detect_graphics(); console.clear(); var start = Date.now(); show_image(welcome_image, /* fx: */false, /* delay: */0); @@ -1253,6 +1462,9 @@ try { js.on_exit("console.attributes = LIGHTGRAY"); } + if(argv.indexOf("graphics") >= 0) { + graph = true; + } if(argv.indexOf("winners") >= 0) { if(!isNaN(numval) && numval > 0) options.winners = numval; diff --git a/xtrn/minesweeper/selmask.pbm b/xtrn/minesweeper/selmask.pbm new file mode 100644 index 0000000000..9e9cc16d5b --- /dev/null +++ b/xtrn/minesweeper/selmask.pbm @@ -0,0 +1,4 @@ +P4 +# Created by GIMP version 2.10.36 PNM plug-in +16 16 +�������������������� \ No newline at end of file -- GitLab