From 4325c905d5a3e8ba635bca761d491bbb8ec3ad93 Mon Sep 17 00:00:00 2001 From: techbot Date: Mon, 10 Aug 2026 13:17:20 +0200 Subject: [PATCH 1/4] fix passing wrong value to knobs --- tgui/packages/tgui/interfaces/Minesweeper.jsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tgui/packages/tgui/interfaces/Minesweeper.jsx b/tgui/packages/tgui/interfaces/Minesweeper.jsx index bc909609fe0241..54309f09eaa10a 100644 --- a/tgui/packages/tgui/interfaces/Minesweeper.jsx +++ b/tgui/packages/tgui/interfaces/Minesweeper.jsx @@ -160,11 +160,11 @@ export const MinesweeperContent = (props, context) => { inline animated unit=" Width" - minValue="5" - maxValue="30" - stepPixelSize="10" + minValue={5} + maxValue={30} + stepPixelSize={10} value={custom_width} - onDrag={(e, value) => + onChange={(e, value) => act('PRG_width', { width: value, }) @@ -175,11 +175,11 @@ export const MinesweeperContent = (props, context) => { animated color="blue" unit=" Height" - minValue="5" - maxValue="17" - stepPixelSize="20" + minValue={5} + maxValue={17} + stepPixelSize={20} value={custom_height} - onDrag={(e, value) => + onChange={(e, value) => act('PRG_height', { height: value, }) @@ -190,11 +190,11 @@ export const MinesweeperContent = (props, context) => { animated color="bad" unit=" Mines" - minValue="5" - maxValue="250" - stepPixelSize="2" + minValue={5} + maxValue={250} + stepPixelSize={2} value={custom_mines} - onDrag={(e, value) => + onChange={(e, value) => act('PRG_mines', { mines: value, }) From c16b9f08e1a50a8d5a3d5e6cebc1f025924a88e4 Mon Sep 17 00:00:00 2001 From: techbot Date: Mon, 10 Aug 2026 13:23:32 +0200 Subject: [PATCH 2/4] bigger fields!!!! --- .../arcades/code/minesweeper/minesweeper.dm | 59 ++++++++++++++----- tgui/packages/tgui/interfaces/Minesweeper.jsx | 8 +-- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm index af36a95a9918a7..b1bd5486e48d13 100644 --- a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm +++ b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm @@ -67,7 +67,7 @@ /obj/machinery/computer/arcade/minesweeper/ui_data(mob/user) var/list/data = ..() - data["board_data"] = board.board_data + data["board_data"] = board.get_display_board() data["game_status"] = board.game_status data["difficulty"] = board.diff_text(board.difficulty) data["current_difficulty"] = board.current_difficulty @@ -122,8 +122,10 @@ if(!cin) return cin = text2num(cin) - if(cin < 5 || cin > 17) - cin = clamp(cin, 5, 17) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + if(cin < 5 || cin > 100) + cin = clamp(cin, 5, 100) + // META EDIT - CHANGE - END board.custom_height = cin board.custom_mines = min(board.custom_mines, FLOOR(board.custom_width*board.custom_height/2,1)) board.difficulty = MINESWEEPER_CUSTOM @@ -134,8 +136,10 @@ if(!cin) return cin = text2num(cin) - if(cin < 5 || cin > 30) - cin = clamp(cin, 5, 30) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + if(cin < 5 || cin > 100) + cin = clamp(cin, 5, 100) + // META EDIT - CHANGE - END board.custom_width = cin board.custom_mines = min(board.custom_mines, FLOOR(board.custom_width*board.custom_height/2,1)) board.difficulty = MINESWEEPER_CUSTOM @@ -146,8 +150,11 @@ if(!cin) return cin = text2num(cin) - if(cin < 5 || cin > FLOOR(board.custom_width*board.custom_height/2,1)) - cin = clamp(cin, 5, FLOOR(board.custom_width*board.custom_height/2,1)) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + var/max_mines = min(2000, FLOOR(board.custom_width*board.custom_height/2,1)) + if(cin < 5 || cin > max_mines) + cin = clamp(cin, 5, max_mines) + // META EDIT - CHANGE - END board.custom_mines = cin board.difficulty = MINESWEEPER_CUSTOM return TRUE @@ -216,7 +223,7 @@ /datum/computer_file/program/minesweeper/ui_data(mob/user) var/list/data = list() - data["board_data"] = board.board_data + data["board_data"] = board.get_display_board() data["game_status"] = board.game_status data["difficulty"] = board.diff_text(board.difficulty) data["current_difficulty"] = board.current_difficulty @@ -277,8 +284,10 @@ if(!cin) return cin = text2num(cin) - if(cin < 5 || cin > 30) - cin = clamp(cin, 5, 30) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + if(cin < 5 || cin > 100) + cin = clamp(cin, 5, 100) + // META EDIT - CHANGE - END board.custom_width = cin board.custom_mines = min(board.custom_mines, FLOOR(board.custom_width*board.custom_height/2,1)) board.difficulty = MINESWEEPER_CUSTOM @@ -289,8 +298,11 @@ if(!cin) return cin = text2num(cin) - if(cin < 5 || cin > FLOOR(board.custom_width*board.custom_height/2,1)) - cin = clamp(cin, 5, FLOOR(board.custom_width*board.custom_height/2,1)) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + var/max_mines = min(2000, FLOOR(board.custom_width*board.custom_height/2,1)) + if(cin < 5 || cin > max_mines) + cin = clamp(cin, 5, max_mines) + // META EDIT - CHANGE - END board.custom_mines = cin board.difficulty = MINESWEEPER_CUSTOM return TRUE @@ -319,7 +331,9 @@ var/game_status = MINESWEEPER_IDLE - var/board_data[31][18] + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + var/board_data[101][101] + // META EDIT - CHANGE - END var/mine_spots = list() var/height = 10 var/width = 10 @@ -343,8 +357,10 @@ /datum/minesweeper/proc/set_custom_height(cin) cin = text2num(cin) - if(cin < 5 || cin > 17) - cin = clamp(cin, 5, 17) + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + if(cin < 5 || cin > 100) + cin = clamp(cin, 5, 100) + // META EDIT - CHANGE - END custom_height = cin custom_mines = min(custom_mines, FLOOR(custom_width*custom_height/2,1)) difficulty = MINESWEEPER_CUSTOM @@ -480,7 +496,9 @@ return TRUE /datum/minesweeper/proc/generate_new_board(diff) - board_data = new /list(31,18) // Fresh board + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + board_data = new /list(101,101) // Fresh board + // META EDIT - CHANGE - END mine_spots = list() switch(diff) @@ -566,6 +584,15 @@ /datum/minesweeper/proc/diff_text(diff) return list("Beginner", "Intermediate", "Expert", "Custom")[diff] +// META EDIT - ADDITION - START - MINESWEEPER_BIGGER_BOARDS +/// Sends only the active width x height slice of board_data, since the array itself is allocated at the max custom size. +/datum/minesweeper/proc/get_display_board() + var/list/trimmed = new /list(width) + for(var/x in 1 to width) + trimmed[x] = board_data[x].Copy(1, height + 1) + return trimmed +// META EDIT - ADDITION - END + /datum/minesweeper/proc/find_in_mines(list/coord) var/order = 0 for(var/list/L in mine_spots) diff --git a/tgui/packages/tgui/interfaces/Minesweeper.jsx b/tgui/packages/tgui/interfaces/Minesweeper.jsx index 54309f09eaa10a..ac50c95f2eb090 100644 --- a/tgui/packages/tgui/interfaces/Minesweeper.jsx +++ b/tgui/packages/tgui/interfaces/Minesweeper.jsx @@ -161,7 +161,7 @@ export const MinesweeperContent = (props, context) => { animated unit=" Width" minValue={5} - maxValue={30} + maxValue={100} stepPixelSize={10} value={custom_width} onChange={(e, value) => @@ -176,8 +176,8 @@ export const MinesweeperContent = (props, context) => { color="blue" unit=" Height" minValue={5} - maxValue={17} - stepPixelSize={20} + maxValue={100} + stepPixelSize={10} value={custom_height} onChange={(e, value) => act('PRG_height', { @@ -191,7 +191,7 @@ export const MinesweeperContent = (props, context) => { color="bad" unit=" Mines" minValue={5} - maxValue={250} + maxValue={2000} stepPixelSize={2} value={custom_mines} onChange={(e, value) => From 840e55de86f74b284cddb6d0682642c3feba2144 Mon Sep 17 00:00:00 2001 From: techbot Date: Mon, 10 Aug 2026 13:33:30 +0200 Subject: [PATCH 3/4] forgot type --- .../code/modules/arcades/code/minesweeper/minesweeper.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm index b1bd5486e48d13..2723c5e85ffb69 100644 --- a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm +++ b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm @@ -332,7 +332,7 @@ var/game_status = MINESWEEPER_IDLE // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS - var/board_data[101][101] + var/list/board_data[101][101] // META EDIT - CHANGE - END var/mine_spots = list() var/height = 10 From 812f988b72cdca130b9d0f6ef7a87b84c268edb6 Mon Sep 17 00:00:00 2001 From: techbot Date: Mon, 10 Aug 2026 15:08:46 +0200 Subject: [PATCH 4/4] fix the fill if the board is maxed and changed the knobs to numberimput --- .../arcades/code/minesweeper/minesweeper.dm | 59 +-- tgui/packages/tgui/interfaces/Minesweeper.jsx | 369 ++++++++++-------- 2 files changed, 245 insertions(+), 183 deletions(-) diff --git a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm index 2723c5e85ffb69..f4d21262cb2dda 100644 --- a/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm +++ b/modular_zubbers/code/modules/arcades/code/minesweeper/minesweeper.dm @@ -551,33 +551,48 @@ play_snd('modular_zubbers/sound/arcade/minesweeper_explosion3.ogg') return MINESWEEPER_DEAD - tiles_left-- - - var/mine_count = 0 - for(var/scanx=-1, scanx<2, scanx++) // -1, 0, 1 - for(var/scany=-1, scany<2, scany++) - if(scanx+x < 1 || scany+y < 1) - continue - if(scanx == 0 && scany == 0) // We know we aren't a mine - continue - if(board_data[scanx+x][scany+y] != "minesweeper_hidden.png" && board_data[scanx+x][scany+y] != "minesweeper_flag.png") - continue - if(find_in_mines(list(scanx+x-1,scany+y-1))) - mine_count++ - - if(mine_count == FALSE) // There are no mines around me! Select every square adjacent! - board_data[x][y] = "minesweeper_empty.png" + // META EDIT - CHANGE - START - MINESWEEPER_BIGGER_BOARDS + // Flood fill is iterative (not recursive proc calls) since the connected empty + // region on a big sparse board can span thousands of tiles, and recursion that + // deep hits DM's call stack limit partway through, leaving the reveal incomplete. + var/list/queue = list(list(x,y)) + var/qhead = 1 + while(qhead <= length(queue)) + var/list/coord = queue[qhead] + qhead++ + var/cx = coord[1] + var/cy = coord[2] + if(board_data[cx][cy] != "minesweeper_hidden.png" && board_data[cx][cy] != "minesweeper_flag.png") + continue // Already processed via another neighbor. + + tiles_left-- + + var/mine_count = 0 for(var/scanx=-1, scanx<2, scanx++) // -1, 0, 1 for(var/scany=-1, scany<2, scany++) - if(scanx+x < 1 || scany+y < 1) + if(scanx+cx < 1 || scany+cy < 1) continue - if(scanx == 0 && scany == 0) + if(scanx == 0 && scany == 0) // We know we aren't a mine continue - if(board_data[scanx+x][scany+y] != "minesweeper_hidden.png") + if(board_data[scanx+cx][scany+cy] != "minesweeper_hidden.png" && board_data[scanx+cx][scany+cy] != "minesweeper_flag.png") continue - select_square(scanx+x,scany+y) - else - board_data[x][y] = "minesweeper_[mine_count].png" + if(find_in_mines(list(scanx+cx-1,scany+cy-1))) + mine_count++ + + if(mine_count == FALSE) // There are no mines around me! Select every square adjacent! + board_data[cx][cy] = "minesweeper_empty.png" + for(var/scanx=-1, scanx<2, scanx++) // -1, 0, 1 + for(var/scany=-1, scany<2, scany++) + if(scanx+cx < 1 || scany+cy < 1) + continue + if(scanx == 0 && scany == 0) + continue + if(board_data[scanx+cx][scany+cy] != "minesweeper_hidden.png") + continue + queue += list(list(scanx+cx, scany+cy)) + else + board_data[cx][cy] = "minesweeper_[mine_count].png" + // META EDIT - CHANGE - END return tiles_left <= mines ? MINESWEEPER_VICTORY : MINESWEEPER_CONTINUE diff --git a/tgui/packages/tgui/interfaces/Minesweeper.jsx b/tgui/packages/tgui/interfaces/Minesweeper.jsx index ac50c95f2eb090..871a5b9b30d080 100644 --- a/tgui/packages/tgui/interfaces/Minesweeper.jsx +++ b/tgui/packages/tgui/interfaces/Minesweeper.jsx @@ -1,12 +1,44 @@ -import { Box, Button, Knob, Section } from 'tgui-core/components'; +import { Box, Button, NumberInput, Section, Stack } from 'tgui-core/components'; +import { clamp } from 'tgui-core/math'; import { resolveAsset } from '../assets'; import { useBackend } from '../backend'; import { Window } from '../layouts'; +const MAX_WIDTH = 700; +const MAX_HEIGHT = 600; +const MIN_WIDTH = 420; +const MIN_HEIGHT = 320; +// Native sprite size of each board tile. +const TILE_PX = 16; +const WINDOW_CHROME_H = 44; +const WINDOW_CHROME_W = 24; +const HEADER_HEIGHT = 80; +const FOOTER_HEIGHT = 64; +const KNOB_ROW_HEIGHT = 40; +const SECTION_GAP = 12; +const BOARD_PADDING = 16; + export const Minesweeper = (props, context) => { + const { data } = useBackend(context); + const { board_data = [], difficulty } = data; + const cols = board_data.length || 10; + const rows = board_data[0]?.length || 10; + const knobRow = difficulty === 'Custom' ? KNOB_ROW_HEIGHT : 0; + const width = clamp(cols * TILE_PX + WINDOW_CHROME_W, MIN_WIDTH, MAX_WIDTH); + const height = clamp( + WINDOW_CHROME_H + + HEADER_HEIGHT + + SECTION_GAP + + BOARD_PADDING + + rows * TILE_PX + + FOOTER_HEIGHT + + knobRow, + MIN_HEIGHT, + MAX_HEIGHT, + ); return ( - + @@ -32,178 +64,193 @@ export const MinesweeperContent = (props, context) => { time_string, } = data; return ( -
- DIFFICULTY: - {current_difficulty} -
- - {emagged ? 'Explode in the game, explode in real life!' : 'Tickets: '} - - {emagged ? '' : tickets} - {emagged ? '' : ' Mines left: '} - {emagged ? '' : current_mines - flags} - {emagged ? '' : ' Time: '} - {emagged ? '' : time_string} -
-
- - {game_status !== 3 ? ( - board_data.map((xdata, xind) => ( - - {xdata.map((imagec, yind) => ( - - {imagec ? ( - - act('PRG_do_tile', { - x: xind + 1, - y: yind + 1, - flag: false, - }) - } - onContextMenu={(eve) => { - eve.preventDefault(); - act('PRG_do_tile', { - x: xind + 1, - y: yind + 1, - flag: true, - }); - }} - > - + + +
+ DIFFICULTY: + {current_difficulty} +
+ + {emagged + ? 'Explode in the game, explode in real life!' + : 'Tickets: '} + + {emagged ? '' : tickets} + {emagged ? '' : ' Mines left: '} + {emagged ? '' : current_mines - flags} + {emagged ? '' : ' Time: '} + {emagged ? '' : time_string} +
+
+ +
+ {game_status !== 3 ? ( + + {board_data.map((xdata, xind) => ( + + {xdata.map((imagec, yind) => ( + + {imagec ? ( + + act('PRG_do_tile', { + x: xind + 1, + y: yind + 1, + flag: false, + }) + } + onContextMenu={(eve) => { + eve.preventDefault(); + act('PRG_do_tile', { + x: xind + 1, + y: yind + 1, + flag: true, + }); + }} + > + + + ) : ( + '' + )} - ) : ( - '' - )} + ))} ))} - )) - ) : ( - -
-
-
-
-
-
-
-
-
-
-
-
-
- )} - -
-
+
+ +
+
+ {difficulty === 'Custom' ? ( + + + act('PRG_width', { + width: value, + }) + } + /> + + act('PRG_height', { + height: value, + }) + } + /> + + act('PRG_mines', { + mines: value, + }) + } + /> + + ) : ( + '' + )} +
+ + ); };