From 7d0e13250798ad771978d8ec9c9baf264df3f409 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:06:30 +0300 Subject: [PATCH 01/27] minesweeper --- .../minesweeper/_minesweeper.dm | 4 + .../minesweeper/_minesweeper.dme | 3 + .../minesweeper/code/minesweeper.dm | 299 ++++++++++++++++++ modular_bandastation/modular_bandastation.dme | 1 + .../interfaces/NtosMinesweeperPanel220.tsx | 251 +++++++++++++++ .../tgui/styles/interfaces/Minesweeper.scss | 40 +++ tgui/packages/tgui/styles/main.scss | 1 + 7 files changed, 599 insertions(+) create mode 100644 modular_bandastation/minesweeper/_minesweeper.dm create mode 100644 modular_bandastation/minesweeper/_minesweeper.dme create mode 100644 modular_bandastation/minesweeper/code/minesweeper.dm create mode 100644 tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx create mode 100644 tgui/packages/tgui/styles/interfaces/Minesweeper.scss diff --git a/modular_bandastation/minesweeper/_minesweeper.dm b/modular_bandastation/minesweeper/_minesweeper.dm new file mode 100644 index 0000000000000..124e899d6c812 --- /dev/null +++ b/modular_bandastation/minesweeper/_minesweeper.dm @@ -0,0 +1,4 @@ +/datum/modpack/example + name = "Minesweeper" + desc = "Добавляет сапер" + author = "ROdenFL" diff --git a/modular_bandastation/minesweeper/_minesweeper.dme b/modular_bandastation/minesweeper/_minesweeper.dme new file mode 100644 index 0000000000000..b2319e9db92e7 --- /dev/null +++ b/modular_bandastation/minesweeper/_minesweeper.dme @@ -0,0 +1,3 @@ +#include "_minesweeper.dm" + +#include "code/minesweeper.dm" diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm new file mode 100644 index 0000000000000..e795318736675 --- /dev/null +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -0,0 +1,299 @@ +#define MINESWEEPER_ROWS 16 +#define MINESWEEPER_COLUMNS 16 +#define MINESWEEPER_BOMBS 40 + +/datum/computer_file/program/minesweeper + filename = "minesweeper" + filedesc = "Minesweeper" + // program_open_overlay = "minesweeper" + extended_desc = "A program with the Minesweeper game! Don`t forget to share your results in online leaderbaord." + downloader_category = PROGRAM_CATEGORY_GAMES + size = 6 + tgui_id = "NtosMinesweeperPanel" + program_icon = "bomb" + + /// Thing, to make first touch safety + var/first_touch = TRUE + // Win condition things + var/setted_flags = 0 + var/flagged_bombs = 0 + var/opened_cells = 0 + /// Decision to make interface untouchable in the momemnt of regenerating + var/ignore_touches = FALSE + /// Here we have all the minesweeper info + var/list/minesweeper_matrix = list() + // generations vars + var/generation_rows = MINESWEEPER_ROWS + var/generation_columns = MINESWEEPER_COLUMNS + var/generation_bombs = MINESWEEPER_BOMBS + ///Current 3BV(special system of score for minesweeper) + var/current_3BV = 0 + /// The moment then game was started for point count + var/start_time = 0 + + /// The leaderboard list + var/static/list/leaderboard = list() + +/datum/computer_file/program/minesweeper/ui_interact(mob/user, datum/tgui/ui) + if(!LAZYLEN(minesweeper_matrix)) + make_empty_matrix() + +/datum/computer_file/program/minesweeper/ui_data(mob/user) + var/list/data = list() + data["matrix"] = minesweeper_matrix + data["flags"] = setted_flags + data["bombs"] = generation_bombs + data["leaderboard"] = leaderboard + data["first_touch"] = first_touch + data["field_params"] = list("width" = generation_columns, "height" = generation_rows, "bombs" = generation_bombs) + return data + +/datum/computer_file/program/minesweeper/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + + if(.) + return + + if(ignore_touches) + return + + switch(action) + if("Square") + var/x = text2num(params["X"]) + 1 + var/y = text2num(params["Y"]) + 1 + switch(params["mode"]) + if("bomb") + if(first_touch) + generate_matrix(x, y) + open_cell(x, y) + + if(minesweeper_matrix[x][y]["bomb"]) + on_loose(ui.user) + return TRUE + + if("flag") + if(first_touch || minesweeper_matrix[x][y]["open"]) + return + + if(minesweeper_matrix[x][y]["flag"]) + minesweeper_matrix[x][y]["flag"] = FALSE + setted_flags -= 1 + if(minesweeper_matrix[x][y]["bomb"]) + flagged_bombs -= 1 + else + minesweeper_matrix[x][y]["flag"] = TRUE + setted_flags += 1 + if(minesweeper_matrix[x][y]["bomb"]) + flagged_bombs += 1 + + check_win(ui.user) + if("ChangeSize") + if(!first_touch) + return + var/ans = tgui_alert(ui.user, "You want to change field parametrs?", "Minesweeper Settings", list("Yes", "No")) + if(ans == "No") + return + var/width = tgui_input_number(ui.user, "Set new width", "Minesweeper Settings", generation_columns, 25, 9) + var/height = tgui_input_number(ui.user, "Set new height", "Minesweeper Settings", generation_rows, 25, 9) + var/bombs = tgui_input_number(ui.user, "Set new bombs quantity", "Minesweeper Settings", generation_bombs, 100, 10) + if(computer.loc != ui.user) + return + if(bombs > (width*height/5)) + tgui_alert(ui.user, "Too many bombs for this size!", "Minesweeper Settings", list("Ok")) + return + generation_rows = height + generation_columns = width + generation_bombs = bombs + make_empty_matrix() + + + return TRUE + +/datum/computer_file/program/minesweeper/proc/check_win(mob/user) + if(flagged_bombs == generation_bombs && \ + setted_flags == generation_bombs && \ + opened_cells == (generation_rows * generation_columns - generation_bombs)) + on_win(user) + +/datum/computer_file/program/minesweeper/proc/on_win(mob/user) + ignore_touches = TRUE + playsound(get_turf(computer), 'sound/machines/ping.ogg', 20, TRUE) + addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 5 SECONDS) + add_into_leaders(user, world.time - start_time) + +/datum/computer_file/program/minesweeper/proc/add_into_leaders(mob/user, game_time) + var/nickname = tgui_input_text(user, "You finished the game in [game_time / 10] seconds.\n Write a nickname to save your result on the leaderboard.\n", "Minesweeper", "", 10) + if(!nickname) + return + + leaderboard += list(list("name" = nickname, "time" = "[game_time/10]", "points" = "[current_3BV]", "pointsPerSec" = "[round(current_3BV/(game_time/10), 0.1)]", "fieldParams" = "[generation_columns]X[generation_rows]([generation_bombs])")) + +/datum/computer_file/program/minesweeper/proc/on_loose(mob/user) + ignore_touches = TRUE + playsound(get_turf(computer), 'sound/effects/explosion/explosion1.ogg', 50, TRUE) + addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 3 SECONDS) + +/datum/computer_file/program/minesweeper/proc/make_empty_matrix(pay = TRUE) + minesweeper_matrix = list() + for(var/i in 1 to generation_rows) + var/list/new_row = list() + for(var/j in 1 to generation_columns) + new_row += list(list("open" = FALSE, "bomb" = FALSE, "flag" = FALSE, "around" = 0, "marked" = FALSE)) + minesweeper_matrix += list(new_row) + first_touch = TRUE + ignore_touches = FALSE + SStgui.update_uis(computer) + +/datum/computer_file/program/minesweeper/proc/generate_matrix(x, y) + flagged_bombs = 0 + setted_flags = 0 + opened_cells = 0 + var/list/possible_list = list() + var/this_x = x + var/this_y = y + var/count = 0 + + for(var/i in 1 to generation_rows) + for(var/j in 1 to generation_columns) + if((i in list(this_x - 1, this_x, this_x + 1)) && (j in list(this_y - 1, this_y, this_y + 1))) + continue + possible_list["[count]"] = list(i, j) + count++ + + for(var/bomb in 1 to generation_bombs) + var/cell = pick(possible_list) + var/coordinates = possible_list[cell] + possible_list -= cell + var/new_x = coordinates[1] + var/new_y = coordinates[2] + minesweeper_matrix[new_x][new_y]["bomb"] = TRUE + + if(new_x != 1) + minesweeper_matrix[new_x-1][new_y]["around"] += 1 + + if(new_y != 1) + minesweeper_matrix[new_x][new_y-1]["around"] += 1 + + if(new_x != 1 && new_y != 1) + minesweeper_matrix[new_x-1][new_y-1]["around"] += 1 + + if(new_x != generation_rows) + minesweeper_matrix[new_x+1][new_y]["around"] += 1 + + if(new_y != generation_columns) + minesweeper_matrix[new_x][new_y+1]["around"] += 1 + + if(new_x != generation_rows && new_y != generation_columns) + minesweeper_matrix[new_x+1][new_y+1]["around"] += 1 + + if(new_x != 1 && new_y != generation_columns) + minesweeper_matrix[new_x-1][new_y+1]["around"] += 1 + + if(new_x != generation_rows && new_y != 1) + minesweeper_matrix[new_x+1][new_y-1]["around"] += 1 + + first_touch = FALSE + count_3BV() + start_time = world.time + +/datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) + . = list() + if(!minesweeper_matrix[x][y]["open"]) + minesweeper_matrix[x][y]["open"] = TRUE + opened_cells += 1 + + if(minesweeper_matrix[x][y]["flag"]) + minesweeper_matrix[x][y]["flag"] = FALSE + setted_flags -= 1 + if(minesweeper_matrix[x][y]["bomb"]) + flagged_bombs -= 1 + + if(minesweeper_matrix[x][y]["around"] == 0) + if(start_cycle) + update_zeros(x, y) + else + . = list(list(x, y)) + +/datum/computer_file/program/minesweeper/proc/update_zeros(x, y) + var/list/list_for_update = list(list(x, y)) + for(var/list/coordinates in list_for_update) + var/this_x = coordinates[1] + var/this_y = coordinates[2] + var/new_x + var/new_y + + if(this_x != 1) + new_x = this_x-1 + list_for_update += open_cell(new_x, this_y) + + if(this_y != 1) + new_y = this_y-1 + list_for_update += open_cell(this_x, new_y) + + if(this_x != generation_rows) + new_x = this_x+1 + list_for_update += open_cell(new_x, this_y) + + if(this_y != generation_columns) + new_y = this_y+1 + list_for_update += open_cell(this_x, new_y) + + if(this_x != 1 && this_y != 1) + new_x = this_x-1 + new_y = this_y-1 + if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) + list_for_update += open_cell(new_x, new_y) + + if(this_x != generation_rows && this_y != generation_columns) + new_x = this_x+1 + new_y = this_y+1 + if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) + list_for_update += open_cell(new_x, new_y) + + if(this_x != 1 && this_y != generation_columns) + new_x = this_x-1 + new_y = this_y+1 + if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) + list_for_update += open_cell(new_x, new_y) + + if(this_x != generation_rows && this_y != 1) + new_x = this_x+1 + new_y = this_y-1 + if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) + list_for_update += open_cell(new_x, new_y) + +/datum/computer_file/program/minesweeper/proc/count_3BV() + current_3BV = 0 + for(var/i in 1 to generation_rows) + for(var/j in 1 to generation_columns) + if(minesweeper_matrix[i][j]["marked"]) + continue + minesweeper_matrix[i][j]["marked"] = TRUE + if(minesweeper_matrix[i][j]["bomb"]) + continue + if(minesweeper_matrix[i][j]["around"]) + current_3BV++ + continue + else + current_3BV++ + count_zeros(i, j) + continue + +/datum/computer_file/program/minesweeper/proc/count_zeros(start_x, start_y) + var/check_list = list(list(start_x, start_y)) + for(var/coordinates in check_list) + var/x = coordinates[1] + var/y = coordinates[2] + minesweeper_matrix[x][y]["marked"] = TRUE + for(var/i in list(x - 1, x, x + 1)) + for(var/j in list(y - 1, y, y + 1)) + if((i== 0) || (j == 0) || (i > generation_rows) || (j > generation_columns)) + continue + if(minesweeper_matrix[i][j]["marked"]) + continue + if(!minesweeper_matrix[i][j]["around"]) + check_list += list(list(i, j)) + +#undef MINESWEEPER_ROWS +#undef MINESWEEPER_COLUMNS +#undef MINESWEEPER_BOMBS diff --git a/modular_bandastation/modular_bandastation.dme b/modular_bandastation/modular_bandastation.dme index 410f0d8f64290..6bb7575ab2983 100644 --- a/modular_bandastation/modular_bandastation.dme +++ b/modular_bandastation/modular_bandastation.dme @@ -37,6 +37,7 @@ #include "mapping/_mapping.dme" #include "medical/_medical.dme" #include "mobs/_mobs.dme" +#include "minesweeper/_minesweeper.dme" #include "nanomap/_nanomap.dme" #include "objects/_objects.dme" #include "orderables/_orderables.dme" diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx new file mode 100644 index 0000000000000..43b9b754b869c --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx @@ -0,0 +1,251 @@ +import { Box, Button, Icon, Stack, Table } from 'tgui-core/components'; + +import { useBackend, useSharedState } from '../backend'; +import { NtosWindow } from '../layouts'; + +interface Bomb { + open: number; + bomb: number; + flag: number; + around: number; +} + +type Matrix = Bomb[][]; + +type PlayerResult = { + name: string; + time: string; + points: string; + pointsPerSec: string; + fieldParams: string; +}; + +type Leaderboard = PlayerResult[]; + +type FieldParams = { + width: number; + height: number; + bombs: number; +}; + +type MinesweeperData = { + matrix: Matrix; + flags: number; + bombs: number; + leaderboard: Leaderboard; + first_touch: boolean; + field_params: FieldParams; +}; + +export const NtosMinesweeperPanel = (props) => { + const { act, data } = useBackend(); + const { field_params } = data; + + return ( + + + + + + ); +}; + +export const MinesweeperWindow = (props) => { + const { act, data } = useBackend(); + + const [currentWindow, setWindow] = useSharedState('window', 'Game'); + + const AltWindow = { + Game: 'Leaderboard', + Leaderboard: 'Game', + }; + + return ( + + + {currentWindow === 'Game' ? ( + + ) : ( + + )} + + + + + + ); +}; + +export const MineSweeperGame = (props) => { + const { act, data } = useBackend(); + const { matrix, flags, bombs, first_touch } = data; + + const NumColor = { + 1: 'blue', + 2: 'green', + 3: 'red', + 4: 'darkblue', + 5: 'brown', + 6: 'lightblue', + 7: 'black', + 8: 'white', + }; + + const handleClick = (row, cell, mode) => { + act('Square', { + X: row, + Y: cell, + mode: mode, + }); + }; + + return ( + + + {matrix.map((row, i) => ( + + {matrix[i].map((cell, j) => ( + + ))} + + ))} + + + + + : {bombs} + + + + : {flags} + + + + + + + + ); +}; + +export const MineSweeperLeaderboard = (props) => { + const { act, data } = useBackend(); + const { leaderboard } = data; + const [sortId, _setSortId] = useSharedState('sortId', 'time'); + const [sortOrder, _setSortOrder] = useSharedState('sortOrder', false); + + return ( + + + Nick + Time + 3BV + 3BV/s + Params + + {leaderboard && + leaderboard + .sort((a, b) => { + const i = sortOrder ? 1 : -1; + return a[sortId].localeCompare(b[sortId]) * i; + }) + .map((player, i) => ( + + {player.name} + {player.time} + {player.points} + {player.pointsPerSec} + {player.fieldParams} + + ))} +
+ ); +}; + +const SortButton = (properties) => { + const [sortId, setSortId] = useSharedState('sortId', 'time'); + const [sortOrder, setSortOrder] = useSharedState('sortOrder', false); + const { id, children } = properties; + return ( + + + + ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss new file mode 100644 index 0000000000000..038380faa44a4 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss @@ -0,0 +1,40 @@ +@use '../base.scss'; +@use '../functions.scss' as *; + +.Minesweeper__closed { + vertical-align: middle; + background-color: lighten(base.$color-bg, 20%); + border: base.em(2px) outset lighten(base.$color-bg, 80%); +} + +.Minesweeper__open { + vertical-align: middle; + text-align: center; + font-size: medium; + background-color: lighten(base.$color-bg, 50%) !important; +} + +.Minesweeper__list { + tr > td { + text-align: center; + } + + tr:not(:first-child) { + height: 2em; + line-height: 1.75em; + transition: background-color 50ms; + cursor: pointer; + + &:hover, + &:focus { + background-color: rgba(base.$color-bg, 1); + } + } +} + +.Minesweeper__infobox { + max-height: 8em; + border: base.em(3px) outset lighten(base.$color-bg, 25%); + user-select: none; + -ms-user-select: none; // Remove with Byond 516 release +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 352d9c1a051fd..267312da6e8e7 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -38,6 +38,7 @@ @include meta.load-css('./interfaces/ListInput.scss'); @include meta.load-css('./interfaces/LootPanel.scss'); @include meta.load-css('./interfaces/Mecha.scss'); +@include meta.load-css('./interfaces/Minesweeper.scss'); @include meta.load-css('./interfaces/NtosMessenger.scss'); @include meta.load-css('./interfaces/NtosNotepad.scss'); @include meta.load-css('./interfaces/NuclearBomb.scss'); From 625ca273d3c31684eb3ad9f588f638dd7ccc6406 Mon Sep 17 00:00:00 2001 From: Aylong Date: Sun, 26 Jan 2025 17:15:39 +0200 Subject: [PATCH 02/27] Better then nothing --- ...rPanel220.tsx => NtosMinesweeperPanel.tsx} | 22 ++++++++++--------- .../tgui/styles/interfaces/Minesweeper.scss | 16 +++++++++----- 2 files changed, 23 insertions(+), 15 deletions(-) rename tgui/packages/tgui/interfaces/{NtosMinesweeperPanel220.tsx => NtosMinesweeperPanel.tsx} (92%) diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx similarity index 92% rename from tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx rename to tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx index 43b9b754b869c..81ce8d2817cd5 100644 --- a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel220.tsx +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx @@ -75,7 +75,6 @@ export const MinesweeperWindow = (props) => { + + ); }; From eb3a2322db25304ac3c6b2e75b0d52665b093c0a Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:26:12 +0300 Subject: [PATCH 04/27] better 516 --- tgui/packages/tgui/styles/interfaces/Minesweeper.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss index aaae2a757de06..30c8157b7b109 100644 --- a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss +++ b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss @@ -13,6 +13,7 @@ } .Minesweeper__open { + filter: brightness(80%); pointer-events: none; vertical-align: middle; text-align: center; From 916c8c24323084704d3dc3d8e52dcd8f9d1cae92 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:39:05 +0300 Subject: [PATCH 05/27] oi --- SQL/bandastation/database_changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQL/bandastation/database_changelog.md b/SQL/bandastation/database_changelog.md index a8624201331a9..27136a89e96d6 100644 --- a/SQL/bandastation/database_changelog.md +++ b/SQL/bandastation/database_changelog.md @@ -15,7 +15,7 @@ INSERT INTO `SS13_schema_revision_220` (`major`, `minor`) VALUES (1, 3); ----------------------------------------------------- Version 1.3, 29 January 2024, by ROdenFL -Created the table: budget +Created the table: minesweeper ```sql CREATE TABLE `minesweeper` ( From 7e8fb17f0e3ac6108fc6269f7cbb7b06b635d039 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:42:10 +0300 Subject: [PATCH 06/27] some tabs --- SQL/bandastation/bandastation_update.sql | 10 +++++----- SQL/bandastation/database_changelog.md | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/SQL/bandastation/bandastation_update.sql b/SQL/bandastation/bandastation_update.sql index 350ef552bffdf..5d6aaa388020d 100644 --- a/SQL/bandastation/bandastation_update.sql +++ b/SQL/bandastation/bandastation_update.sql @@ -61,15 +61,15 @@ CREATE TABLE `budget` ( -- DROP TABLE IF EXISTS `minesweeper`; CREATE TABLE `minesweeper` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `date` DATETIME NOT NULL DEFAULT current_timestamp(), - `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `id` INT(11) NOT NULL AUTO_INCREMENT, + `date` DATETIME NOT NULL DEFAULT current_timestamp(), + `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', - `time` INT(10) UNSIGNED NOT NULL, + `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, `pointspersec` FLOAT(10) UNSIGNED NOT NULL, `width` TINYINT(3) UNSIGNED NOT NULL, `height` TINYINT(3) UNSIGNED NOT NULL, `bombs` TINYINT(3) UNSIGNED NOT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`) ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; diff --git a/SQL/bandastation/database_changelog.md b/SQL/bandastation/database_changelog.md index 27136a89e96d6..c9a99823022f0 100644 --- a/SQL/bandastation/database_changelog.md +++ b/SQL/bandastation/database_changelog.md @@ -19,11 +19,11 @@ Created the table: minesweeper ```sql CREATE TABLE `minesweeper` ( - `id` INT(11) NOT NULL AUTO_INCREMENT, - `date` DATETIME NOT NULL DEFAULT current_timestamp(), - `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `id` INT(11) NOT NULL AUTO_INCREMENT, + `date` DATETIME NOT NULL DEFAULT current_timestamp(), + `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', - `time` INT(10) UNSIGNED NOT NULL, + `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, `pointspersec` FLOAT(10) UNSIGNED NOT NULL, `width` TINYINT(3) UNSIGNED NOT NULL, From 7d4e404b016854154411c6231218b232746026d6 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:53:06 +0300 Subject: [PATCH 07/27] not null ckey --- SQL/bandastation/bandastation_update.sql | 2 +- SQL/bandastation/database_changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SQL/bandastation/bandastation_update.sql b/SQL/bandastation/bandastation_update.sql index 5d6aaa388020d..cfcae0e500595 100644 --- a/SQL/bandastation/bandastation_update.sql +++ b/SQL/bandastation/bandastation_update.sql @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `minesweeper`; CREATE TABLE `minesweeper` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `date` DATETIME NOT NULL DEFAULT current_timestamp(), - `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `ckey` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, diff --git a/SQL/bandastation/database_changelog.md b/SQL/bandastation/database_changelog.md index c9a99823022f0..f4a611dd77e13 100644 --- a/SQL/bandastation/database_changelog.md +++ b/SQL/bandastation/database_changelog.md @@ -21,7 +21,7 @@ Created the table: minesweeper CREATE TABLE `minesweeper` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `date` DATETIME NOT NULL DEFAULT current_timestamp(), - `ckey` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `ckey` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, From 6fa738ddd261fae0270ed7ea1725a827bd3bb8ff Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:54:27 +0300 Subject: [PATCH 08/27] not null nickname --- SQL/bandastation/bandastation_update.sql | 2 +- SQL/bandastation/database_changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SQL/bandastation/bandastation_update.sql b/SQL/bandastation/bandastation_update.sql index cfcae0e500595..ddf081e46eaab 100644 --- a/SQL/bandastation/bandastation_update.sql +++ b/SQL/bandastation/bandastation_update.sql @@ -64,7 +64,7 @@ CREATE TABLE `minesweeper` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `date` DATETIME NOT NULL DEFAULT current_timestamp(), `ckey` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', - `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `nickname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, `pointspersec` FLOAT(10) UNSIGNED NOT NULL, diff --git a/SQL/bandastation/database_changelog.md b/SQL/bandastation/database_changelog.md index f4a611dd77e13..23998430596d9 100644 --- a/SQL/bandastation/database_changelog.md +++ b/SQL/bandastation/database_changelog.md @@ -22,7 +22,7 @@ CREATE TABLE `minesweeper` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `date` DATETIME NOT NULL DEFAULT current_timestamp(), `ckey` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', - `nickname` VARCHAR(32) NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `nickname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, `pointspersec` FLOAT(10) UNSIGNED NOT NULL, From 5cf5a8988e47f70fa9c2ccd800920e5827547f7e Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Wed, 29 Jan 2025 22:05:08 +0300 Subject: [PATCH 09/27] points_per_sec --- SQL/bandastation/bandastation_update.sql | 2 +- SQL/bandastation/database_changelog.md | 4 ++-- modular_bandastation/minesweeper/code/minesweeper.dm | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SQL/bandastation/bandastation_update.sql b/SQL/bandastation/bandastation_update.sql index ddf081e46eaab..763864fa6d3db 100644 --- a/SQL/bandastation/bandastation_update.sql +++ b/SQL/bandastation/bandastation_update.sql @@ -67,7 +67,7 @@ CREATE TABLE `minesweeper` ( `nickname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, - `pointspersec` FLOAT(10) UNSIGNED NOT NULL, + `points_per_sec` FLOAT(10) UNSIGNED NOT NULL, `width` TINYINT(3) UNSIGNED NOT NULL, `height` TINYINT(3) UNSIGNED NOT NULL, `bombs` TINYINT(3) UNSIGNED NOT NULL, diff --git a/SQL/bandastation/database_changelog.md b/SQL/bandastation/database_changelog.md index 23998430596d9..43b92d51a4c1e 100644 --- a/SQL/bandastation/database_changelog.md +++ b/SQL/bandastation/database_changelog.md @@ -25,11 +25,11 @@ CREATE TABLE `minesweeper` ( `nickname` VARCHAR(32) NOT NULL COLLATE 'utf8mb4_unicode_ci', `time` INT(10) UNSIGNED NOT NULL, `points` INT(10) UNSIGNED NOT NULL, - `pointspersec` FLOAT(10) UNSIGNED NOT NULL, + `points_per_sec` FLOAT(10) UNSIGNED NOT NULL, `width` TINYINT(3) UNSIGNED NOT NULL, `height` TINYINT(3) UNSIGNED NOT NULL, `bombs` TINYINT(3) UNSIGNED NOT NULL, - PRIMARY KEY (`id`) USING BTREE + PRIMARY KEY (`id`) ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; ``` ----------------------------------------------------- diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 43f6828960471..6ea2800757276 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -51,7 +51,7 @@ init_leaderboard() /datum/computer_file/program/minesweeper/proc/init_leaderboard() - var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, pointspersec, time, width, height, bombs FROM [format_table_name("minesweeper")]") + var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, points_per_sec, time, width, height, bombs FROM [format_table_name("minesweeper")]") if(!minesweeper_query.Execute()) return else @@ -64,8 +64,8 @@ /datum/computer_file/program/minesweeper/proc/add_result_to_db(list/new_result, ckey, width, height, bombs) if(SSdbcore.Connect()) var/datum/db_query/query_minesweeper = SSdbcore.NewQuery( - "INSERT INTO [format_table_name("minesweeper")] (ckey, time, points, pointspersec, nickname, width, height, bombs) VALUES (:ckey, :time, :points, :pointspersec, :nickname, :width, :height, :bombs)", - list("ckey" = ckey, "time" = new_result["time"], "points" = new_result["points"], "pointspersec" = new_result["pointsPerSec"], + "INSERT INTO [format_table_name("minesweeper")] (ckey, time, points, points_per_sec, nickname, width, height, bombs) VALUES (:ckey, :time, :points, :points_per_sec, :nickname, :width, :height, :bombs)", + list("ckey" = ckey, "time" = new_result["time"], "points" = new_result["points"], "points_per_sec" = new_result["pointsPerSec"], "nickname" = new_result["name"], "width" = width, "height" = height, "bombs" = bombs) ) query_minesweeper.Execute() From cbee10bb7e37705d40f47e1ddb380ab3027cd1af Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:31:59 +0300 Subject: [PATCH 10/27] comments --- .../minesweeper/code/minesweeper.dm | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 6ea2800757276..66c614758a1f0 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -50,6 +50,7 @@ leaderboard_init = TRUE init_leaderboard() +///Get stored in database player results and fill glob_leaderboard with them /datum/computer_file/program/minesweeper/proc/init_leaderboard() var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, points_per_sec, time, width, height, bombs FROM [format_table_name("minesweeper")]") if(!minesweeper_query.Execute()) @@ -61,6 +62,7 @@ "fieldParams" = "[minesweeper_query.item[5]]X[minesweeper_query.item[6]]([minesweeper_query.item[7]])")) qdel(minesweeper_query) +///Insert new player result into database /datum/computer_file/program/minesweeper/proc/add_result_to_db(list/new_result, ckey, width, height, bombs) if(SSdbcore.Connect()) var/datum/db_query/query_minesweeper = SSdbcore.NewQuery( @@ -92,10 +94,12 @@ if(.) return + // After loose/win cooldown if(ignore_touches) return switch(action) + // Click on game field if("Square") var/x = text2num(params["X"]) + 1 var/y = text2num(params["Y"]) + 1 @@ -125,6 +129,7 @@ flagged_bombs += 1 check_win(ui.user) + // Change field params if("ChangeSize") if(!first_touch) return @@ -159,6 +164,7 @@ addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 5 SECONDS) add_into_leaders(user, world.time - start_time) +/// Add player result to local, global leaderboards and DB /datum/computer_file/program/minesweeper/proc/add_into_leaders(mob/user, game_time) var/nickname = tgui_input_text(user, "You finished the game in [game_time / 10] seconds.\n Write a nickname to save your result on the leaderboard.\n", "Minesweeper", "", 10) if(!nickname) @@ -175,10 +181,10 @@ ignore_touches = TRUE playsound(get_turf(computer), 'sound/effects/explosion/explosion1.ogg', 50, TRUE) if(computer.obj_flags & EMAGGED) - //Small bomb core stats copy explosion(computer, range_heavy, range_medium, range_light, range_flame) addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 3 SECONDS) +// Return the minesweeper matrix to initial state /datum/computer_file/program/minesweeper/proc/make_empty_matrix(pay = TRUE) minesweeper_matrix = list() for(var/i in 1 to generation_rows) @@ -190,6 +196,7 @@ ignore_touches = FALSE SStgui.update_uis(computer) +// Fill matrix with bombs, ignores 3x3 square around first touch place /datum/computer_file/program/minesweeper/proc/generate_matrix(x, y) flagged_bombs = 0 setted_flags = 0 @@ -242,6 +249,7 @@ count_3BV() start_time = world.time +// Makes cell open, and make show it contains /datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) . = list() if(!minesweeper_matrix[x][y]["open"]) @@ -260,6 +268,7 @@ else . = list(list(x, y)) +// Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) var/list/list_for_update = list(list(x, y)) for(var/list/coordinates in list_for_update) @@ -308,6 +317,7 @@ if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) list_for_update += open_cell(new_x, new_y) +// Count value of field for scoring /datum/computer_file/program/minesweeper/proc/count_3BV() current_3BV = 0 for(var/i in 1 to generation_rows) @@ -325,6 +335,7 @@ count_zeros(i, j) continue +// part of proc/count_3BV, used to ignore adjacent "zeroes" /datum/computer_file/program/minesweeper/proc/count_zeros(start_x, start_y) var/check_list = list(list(start_x, start_y)) for(var/coordinates in check_list) From b0a32871394e732736334346f8cdf3aaff95d244 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:51:58 +0300 Subject: [PATCH 11/27] review and qdel memes --- .../minesweeper/code/minesweeper.dm | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 66c614758a1f0..1b2e3e4b5d0fe 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -32,6 +32,8 @@ var/start_time = 0 /// The current round leaderboard list var/static/list/leaderboard = list() + /// The global leaderboard list + var/static/list/glob_leaderboard = null //Emagged bomb stats var/range_heavy = -1 @@ -39,27 +41,28 @@ var/range_light = 3 var/range_flame = 2 - /// Had the program got db results - var/static/leaderboard_init = FALSE - /// The global leaderboard list - var/static/list/glob_leaderboard = list() - /datum/computer_file/program/minesweeper/New() ..() - if(!leaderboard_init) - leaderboard_init = TRUE + if(!glob_leaderboard) init_leaderboard() ///Get stored in database player results and fill glob_leaderboard with them /datum/computer_file/program/minesweeper/proc/init_leaderboard() + glob_leaderboard = list() var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, points_per_sec, time, width, height, bombs FROM [format_table_name("minesweeper")]") if(!minesweeper_query.Execute()) + qdel(minesweeper_query) return - else - while(minesweeper_query.NextRow()) - glob_leaderboard += list(list("name" = minesweeper_query.item[1], "time" = "[minesweeper_query.item[4]]", "points" = "[minesweeper_query.item[2]]", - "pointsPerSec" = "[minesweeper_query.item[3]]", - "fieldParams" = "[minesweeper_query.item[5]]X[minesweeper_query.item[6]]([minesweeper_query.item[7]])")) + while(minesweeper_query.NextRow()) + glob_leaderboard += list( + list( + "name" = minesweeper_query.item[1], + "time" = "[minesweeper_query.item[4]]", + "points" = "[minesweeper_query.item[2]]", + "pointsPerSec" = "[minesweeper_query.item[3]]", + "fieldParams" = "[minesweeper_query.item[5]]X[minesweeper_query.item[6]]([minesweeper_query.item[7]])" + ) + ) qdel(minesweeper_query) ///Insert new player result into database @@ -67,8 +70,16 @@ if(SSdbcore.Connect()) var/datum/db_query/query_minesweeper = SSdbcore.NewQuery( "INSERT INTO [format_table_name("minesweeper")] (ckey, time, points, points_per_sec, nickname, width, height, bombs) VALUES (:ckey, :time, :points, :points_per_sec, :nickname, :width, :height, :bombs)", - list("ckey" = ckey, "time" = new_result["time"], "points" = new_result["points"], "points_per_sec" = new_result["pointsPerSec"], - "nickname" = new_result["name"], "width" = width, "height" = height, "bombs" = bombs) + list( + "ckey" = ckey, + "time" = new_result["time"], + "points" = new_result["points"], + "points_per_sec" = new_result["pointsPerSec"], + "nickname" = new_result["name"], + "width" = width, + "height" = height, + "bombs" = bombs + ) ) query_minesweeper.Execute() qdel(query_minesweeper) @@ -360,5 +371,5 @@ /obj/item/modular_computer/pda/emag_act(mob/user, obj/item/card/emag/emag_card, forced) . = ..() if(.) - store_file(SSmodular_computers.find_ntnet_file_by_name("minesweeper")) + store_file(new /datum/computer_file/program/minesweeper) From f1406ecbfd465b6d8de569d2abf7f6f38897d310 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:52:54 +0300 Subject: [PATCH 12/27] forgot --- modular_bandastation/minesweeper/code/minesweeper.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 1b2e3e4b5d0fe..9111aed1ad366 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -181,8 +181,13 @@ if(!nickname) return - var/result_to_add = list("name" = nickname, "time" = "[game_time/10]", "points" = "[current_3BV]", - "pointsPerSec" = "[round(current_3BV/(game_time/10), 0.1)]", "fieldParams" = "[generation_columns]X[generation_rows]([generation_bombs])") + var/result_to_add = list( + "name" = nickname, + "time" = "[game_time/10]", + "points" = "[current_3BV]", + "pointsPerSec" = "[round(current_3BV/(game_time/10), 0.1)]", + "fieldParams" = "[generation_columns]X[generation_rows]([generation_bombs])" + ) leaderboard += list(result_to_add) glob_leaderboard += list(result_to_add) From ea8b0360df571c2b71a5099f9a5f0214f23bdeeb Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:55:19 +0300 Subject: [PATCH 13/27] =?UTF-8?q?=D0=BD=D0=B0=D0=B2=D1=81=D1=8F=D0=BA?= =?UTF-8?q?=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modular_bandastation/minesweeper/code/minesweeper.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 9111aed1ad366..689ddf0e8073f 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -43,7 +43,7 @@ /datum/computer_file/program/minesweeper/New() ..() - if(!glob_leaderboard) + if(isnull(glob_leaderboard)) init_leaderboard() ///Get stored in database player results and fill glob_leaderboard with them From 1e861b448e4ae8ca9152f20f59e65907f82935cd Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Thu, 30 Jan 2025 22:30:51 +0300 Subject: [PATCH 14/27] async mems --- modular_bandastation/minesweeper/code/minesweeper.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 689ddf0e8073f..d2e2285f1939d 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -376,5 +376,7 @@ /obj/item/modular_computer/pda/emag_act(mob/user, obj/item/card/emag/emag_card, forced) . = ..() if(.) - store_file(new /datum/computer_file/program/minesweeper) + INVOKE_ASYNC(src, PROC_REF(add_minesweeper)) +/obj/item/modular_computer/pda/proc/add_minesweeper() + store_file(new /datum/computer_file/program/minesweeper) From 0404a62fe6901fd603d3386e136b662579ad60c1 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:50:05 +0300 Subject: [PATCH 15/27] ie style memes --- .../tgui/interfaces/NtosMinesweeperPanel.tsx | 92 ++++++++++--------- .../tgui/styles/interfaces/Minesweeper.scss | 15 ++- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx index 8da8a92a09001..73abc288acb12 100644 --- a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx @@ -1,3 +1,4 @@ +import { Fragment } from 'react'; import { Box, Button, Icon, Section, Stack, Table } from 'tgui-core/components'; import { useBackend, useSharedState } from '../backend'; @@ -117,47 +118,56 @@ export const MineSweeperGame = (props) => { {matrix.map((row, i) => ( {matrix[i].map((cell, j) => ( - + + {!!matrix[i][j].open && ( + + ))} ))} diff --git a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss index 30c8157b7b109..f2e583f6d6a21 100644 --- a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss +++ b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss @@ -12,8 +12,14 @@ } } +.Minesweeper__CellContainer { + position: relative; + display: inline-block; +} + .Minesweeper__open { - filter: brightness(80%); + position: relative; + z-index: 0; pointer-events: none; vertical-align: middle; text-align: center; @@ -21,6 +27,13 @@ outline: none !important; } +.Minesweeper__ButtonOverlay { + position: absolute !important; + background-color: rgba(0, 0, 0, 0.2) !important; + z-index: 1 !important; + pointer-events: none !important; +} + .Minesweeper__list { tr > td { text-align: center; From ca94d7bde0f4c6e6364fa7a372d1e2d21b2e77da Mon Sep 17 00:00:00 2001 From: Aylong Date: Sat, 1 Feb 2025 20:23:46 +0200 Subject: [PATCH 16/27] Revert "ie style memes" This reverts commit 0404a62fe6901fd603d3386e136b662579ad60c1. --- .../tgui/interfaces/NtosMinesweeperPanel.tsx | 92 +++++++++---------- .../tgui/styles/interfaces/Minesweeper.scss | 15 +-- 2 files changed, 42 insertions(+), 65 deletions(-) diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx index 73abc288acb12..8da8a92a09001 100644 --- a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx @@ -1,4 +1,3 @@ -import { Fragment } from 'react'; import { Box, Button, Icon, Section, Stack, Table } from 'tgui-core/components'; import { useBackend, useSharedState } from '../backend'; @@ -118,56 +117,47 @@ export const MineSweeperGame = (props) => { {matrix.map((row, i) => ( {matrix[i].map((cell, j) => ( - - {!!matrix[i][j].open && ( - - + ))} ))} diff --git a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss index f2e583f6d6a21..30c8157b7b109 100644 --- a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss +++ b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss @@ -12,14 +12,8 @@ } } -.Minesweeper__CellContainer { - position: relative; - display: inline-block; -} - .Minesweeper__open { - position: relative; - z-index: 0; + filter: brightness(80%); pointer-events: none; vertical-align: middle; text-align: center; @@ -27,13 +21,6 @@ outline: none !important; } -.Minesweeper__ButtonOverlay { - position: absolute !important; - background-color: rgba(0, 0, 0, 0.2) !important; - z-index: 1 !important; - pointer-events: none !important; -} - .Minesweeper__list { tr > td { text-align: center; From f76cc1be9de86f0b0d3c847a0af63f3ad8f604fe Mon Sep 17 00:00:00 2001 From: Aylong Date: Sat, 1 Feb 2025 20:36:49 +0200 Subject: [PATCH 17/27] Remove costulyaka --- .../tgui/styles/interfaces/Minesweeper.scss | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss index 30c8157b7b109..dd56886cdfac3 100644 --- a/tgui/packages/tgui/styles/interfaces/Minesweeper.scss +++ b/tgui/packages/tgui/styles/interfaces/Minesweeper.scss @@ -13,12 +13,23 @@ } .Minesweeper__open { - filter: brightness(80%); + position: relative; pointer-events: none; vertical-align: middle; text-align: center; font-size: medium; outline: none !important; + + &:before { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: hsla(0, 0%, 0%, 0.2); + z-index: 1; + } } .Minesweeper__list { From 250b2dec485c6e90a2d52004e9f7f790e4e33e4b Mon Sep 17 00:00:00 2001 From: Aylong Date: Sat, 1 Feb 2025 20:46:58 +0200 Subject: [PATCH 18/27] Fix infobox --- tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx index 8da8a92a09001..c28ab7141ded3 100644 --- a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx @@ -164,7 +164,7 @@ export const MineSweeperGame = (props) => { - + : {bombs} From cd0535f5009557de094536a544a6c2546d63da7f Mon Sep 17 00:00:00 2001 From: ROdenFL Date: Sun, 2 Feb 2025 14:23:47 +0300 Subject: [PATCH 19/27] Update modular_bandastation/minesweeper/code/minesweeper.dm Co-authored-by: Gaxeer <44334376+Gaxeer@users.noreply.github.com> --- .../minesweeper/code/minesweeper.dm | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index d2e2285f1939d..97adf60fe2d17 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -267,22 +267,27 @@ // Makes cell open, and make show it contains /datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) - . = list() - if(!minesweeper_matrix[x][y]["open"]) - minesweeper_matrix[x][y]["open"] = TRUE - opened_cells += 1 - - if(minesweeper_matrix[x][y]["flag"]) - minesweeper_matrix[x][y]["flag"] = FALSE - setted_flags -= 1 - if(minesweeper_matrix[x][y]["bomb"]) - flagged_bombs -= 1 - - if(minesweeper_matrix[x][y]["around"] == 0) - if(start_cycle) - update_zeros(x, y) - else - . = list(list(x, y)) + var/list/minesweeper_cell = minesweeper_matrix[x][y] + if(minesweeper_cell["open"]) + return list() + + minesweeper_cell["open"] = TRUE + opened_cells += 1 + + if(minesweeper_cell["flag"]) + minesweeper_cell["flag"] = FALSE + setted_flags -= 1 + if(minesweeper_cell["bomb"]) + flagged_bombs -= 1 + + if(minesweeper_cell["around"] != 0) + return list() + + if(start_cycle) + update_zeros(x, y) + return list() + + return list(list(x, y)) // Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) From a57bb16348bb203ad739ae82ccfe569817803a71 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:24:37 +0300 Subject: [PATCH 20/27] work in progress --- .../minesweeper/code/minesweeper.dm | 123 ++++++++---------- 1 file changed, 56 insertions(+), 67 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index d2e2285f1939d..257ad02d3fa57 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -6,7 +6,9 @@ filename = "minesweeper" filedesc = "Minesweeper" // program_open_overlay = "minesweeper" - extended_desc = "A program with the Minesweeper game! Don`t forget to share your results in online leaderbaord." + extended_desc = "Погрузись в удивительный мир 'Сапера',\ +где каждое неверное нажатие может привести к взрыву!\ +Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." downloader_category = PROGRAM_CATEGORY_GAMES size = 6 tgui_id = "NtosMinesweeperPanel" @@ -144,16 +146,16 @@ if("ChangeSize") if(!first_touch) return - var/ans = tgui_alert(ui.user, "You want to change field parametrs?", "Minesweeper Settings", list("Yes", "No")) - if(ans == "No") + var/ans = tgui_alert(ui.user, "Вы хотите изменить параметры поля?", "Настройки Сапёра", list("Да", "Нет")) + if(ans != "Да") return - var/width = tgui_input_number(ui.user, "Set new width", "Minesweeper Settings", generation_columns, 25, 9) - var/height = tgui_input_number(ui.user, "Set new height", "Minesweeper Settings", generation_rows, 25, 9) - var/bombs = tgui_input_number(ui.user, "Set new bombs quantity", "Minesweeper Settings", generation_bombs, 100, 10) + var/width = tgui_input_number(ui.user, "Выставите ширину", "Настройки Сапёра", generation_columns, 25, 9) + var/height = tgui_input_number(ui.user, "Выставите длину", "Настройки Сапёра", generation_rows, 25, 9) + var/bombs = tgui_input_number(ui.user, "Выставите кол-во бомб", "Настройки Сапёра", generation_bombs, 100, 10) if(computer.loc != ui.user) return if(bombs > (width*height/5)) - tgui_alert(ui.user, "Too many bombs for this size!", "Minesweeper Settings", list("Ok")) + tgui_alert(ui.user, "Слишком много бомб для данного размера поля!", "Настройки Сапёра", list("Ок")) return generation_rows = height generation_columns = width @@ -201,7 +203,7 @@ addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 3 SECONDS) // Return the minesweeper matrix to initial state -/datum/computer_file/program/minesweeper/proc/make_empty_matrix(pay = TRUE) +/datum/computer_file/program/minesweeper/proc/make_empty_matrix() minesweeper_matrix = list() for(var/i in 1 to generation_rows) var/list/new_row = list() @@ -265,73 +267,60 @@ count_3BV() start_time = world.time -// Makes cell open, and make show it contains +// Makes cell open, and show it contains /datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) . = list() - if(!minesweeper_matrix[x][y]["open"]) - minesweeper_matrix[x][y]["open"] = TRUE - opened_cells += 1 - - if(minesweeper_matrix[x][y]["flag"]) - minesweeper_matrix[x][y]["flag"] = FALSE - setted_flags -= 1 - if(minesweeper_matrix[x][y]["bomb"]) - flagged_bombs -= 1 - - if(minesweeper_matrix[x][y]["around"] == 0) - if(start_cycle) - update_zeros(x, y) - else - . = list(list(x, y)) + if(minesweeper_matrix[x][y]["open"]) + return + + minesweeper_matrix[x][y]["open"] = TRUE + opened_cells += 1 + + if(minesweeper_matrix[x][y]["flag"]) + minesweeper_matrix[x][y]["flag"] = FALSE + setted_flags -= 1 + if(minesweeper_matrix[x][y]["bomb"]) + flagged_bombs -= 1 + + if(minesweeper_matrix[x][y]["around"] != 0) + return + + if(start_cycle) + update_zeros(x, y) + else + . += list(list(x, y)) // Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) + var/list/directions = list( + list(-1, 0), // Left + list( 1, 0), // Right + list( 0, -1), // Up + list( 0, 1), // Down + list(-1, -1), // Top-Left + list( 1, 1), // Bottom-Right + list(-1, 1), // Top-Right + list( 1, -1) // Bottom-Left + ) + var/list/list_for_update = list(list(x, y)) - for(var/list/coordinates in list_for_update) + + for (var/list/coordinates in list_for_update) var/this_x = coordinates[1] var/this_y = coordinates[2] - var/new_x - var/new_y - - if(this_x != 1) - new_x = this_x-1 - list_for_update += open_cell(new_x, this_y) - - if(this_y != 1) - new_y = this_y-1 - list_for_update += open_cell(this_x, new_y) - - if(this_x != generation_rows) - new_x = this_x+1 - list_for_update += open_cell(new_x, this_y) - - if(this_y != generation_columns) - new_y = this_y+1 - list_for_update += open_cell(this_x, new_y) - - if(this_x != 1 && this_y != 1) - new_x = this_x-1 - new_y = this_y-1 - if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) - list_for_update += open_cell(new_x, new_y) - - if(this_x != generation_rows && this_y != generation_columns) - new_x = this_x+1 - new_y = this_y+1 - if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) - list_for_update += open_cell(new_x, new_y) - - if(this_x != 1 && this_y != generation_columns) - new_x = this_x-1 - new_y = this_y+1 - if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) - list_for_update += open_cell(new_x, new_y) - - if(this_x != generation_rows && this_y != 1) - new_x = this_x+1 - new_y = this_y-1 - if(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"]) - list_for_update += open_cell(new_x, new_y) + + for (var/list/direction in directions) + var/new_x = this_x + direction[1] + var/new_y = this_y + direction[2] + + // Boundary checks + if (new_x >= 1 && new_x <= generation_rows && new_y >= 1 && new_y <= generation_columns) + // Diagonal check requires both adjacent cells to be open + if (abs(direction[1]) == 1 && abs(direction[2]) == 1) + if (!(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"])) + continue + + list_for_update += open_cell(new_x, new_y, FALSE) // Count value of field for scoring /datum/computer_file/program/minesweeper/proc/count_3BV() From cd735de280a73feaa8ad3c1ffd9a7653ccc01203 Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:28:31 +0300 Subject: [PATCH 21/27] review --- .../minesweeper/code/minesweeper.dm | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 257ad02d3fa57..892f751dfcfb6 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -269,11 +269,11 @@ // Makes cell open, and show it contains /datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) - . = list() - if(minesweeper_matrix[x][y]["open"]) - return + var/list/minesweeper_cell = minesweeper_matrix[x][y] + if(minesweeper_cell["open"]) + return list() - minesweeper_matrix[x][y]["open"] = TRUE + minesweeper_cell["open"] = TRUE opened_cells += 1 if(minesweeper_matrix[x][y]["flag"]) @@ -282,13 +282,14 @@ if(minesweeper_matrix[x][y]["bomb"]) flagged_bombs -= 1 - if(minesweeper_matrix[x][y]["around"] != 0) - return + if(minesweeper_cell["around"] != 0) + return list() if(start_cycle) update_zeros(x, y) - else - . += list(list(x, y)) + return list() + + return list(list(x, y)) // Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) From 1bf7037e82510bf1075555accf14d617b0d204ad Mon Sep 17 00:00:00 2001 From: ROdenFL <144662735+ROdenFL@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:51:55 +0300 Subject: [PATCH 22/27] update_zeros update --- modular_bandastation/minesweeper/code/minesweeper.dm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 99999dce659ba..f4ad8bf13f435 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -8,7 +8,7 @@ // program_open_overlay = "minesweeper" extended_desc = "Погрузись в удивительный мир 'Сапера',\ где каждое неверное нажатие может привести к взрыву!\ -Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." + Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." downloader_category = PROGRAM_CATEGORY_GAMES size = 6 tgui_id = "NtosMinesweeperPanel" @@ -305,8 +305,10 @@ ) var/list/list_for_update = list(list(x, y)) + var/index = 1 - for (var/list/coordinates in list_for_update) + while (index < list_for_update.len) + var/list/coordinates = list_for_update[index] var/this_x = coordinates[1] var/this_y = coordinates[2] @@ -322,6 +324,7 @@ continue list_for_update += open_cell(new_x, new_y, FALSE) + index++ // Count value of field for scoring /datum/computer_file/program/minesweeper/proc/count_3BV() From 3e12362a518c97ca196bba2e1f6ddd85c716d770 Mon Sep 17 00:00:00 2001 From: ROdenFL Date: Mon, 3 Feb 2025 10:55:41 +0300 Subject: [PATCH 23/27] Update modular_bandastation/minesweeper/code/minesweeper.dm Co-authored-by: Gaxeer <44334376+Gaxeer@users.noreply.github.com> --- .../minesweeper/code/minesweeper.dm | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index f4ad8bf13f435..6694c4d1330a4 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -267,12 +267,21 @@ count_3BV() start_time = world.time -// Makes cell open, and show it contains -/datum/computer_file/program/minesweeper/proc/open_cell(x, y, start_cycle = TRUE) +/** + * Makes cell by passed coordinates open. + * Increases `opened_cells` if cell was successfully opened and removes flag from it. + * + * @param {number] x - The row of the cell + * @param {number] y - The column of the cell + * + * @return coordinates of opened cell as `list(x, y)` if cell was successfully opened + * and no bombs are around, `null` otherwise +/* +/datum/computer_file/program/minesweeper/proc/open_cell(x, y) var/list/minesweeper_cell = minesweeper_matrix[x][y] if(minesweeper_cell["open"]) - return list() - + return null + minesweeper_cell["open"] = TRUE opened_cells += 1 @@ -281,15 +290,11 @@ setted_flags -= 1 if(minesweeper_cell["bomb"]) flagged_bombs -= 1 - + if(minesweeper_cell["around"] != 0) - return list() - - if(start_cycle) - update_zeros(x, y) - return list() + return null - return list(list(x, y)) + return list(x, y) // Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) From b1cd0641a065060a69e2266d40241b4cb93857d4 Mon Sep 17 00:00:00 2001 From: ROdenFL Date: Mon, 3 Feb 2025 10:56:28 +0300 Subject: [PATCH 24/27] Update modular_bandastation/minesweeper/code/minesweeper.dm Co-authored-by: Gaxeer <44334376+Gaxeer@users.noreply.github.com> --- modular_bandastation/minesweeper/code/minesweeper.dm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 6694c4d1330a4..17abe30a61ec8 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -298,7 +298,11 @@ // Open all "zeroes" around the click place /datum/computer_file/program/minesweeper/proc/update_zeros(x, y) - var/list/directions = list( + // First we open cell by passed coordinates and stop if it has bombs around or was already opened + if(!open_cell(x, y)) + return + + var/static/list/directions = list( list(-1, 0), // Left list( 1, 0), // Right list( 0, -1), // Up @@ -328,7 +332,9 @@ if (!(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"])) continue - list_for_update += open_cell(new_x, new_y, FALSE) + var/list/opened_cell_coordinates = open_cell(new_x, new_y) + if(opened_cell_coordinates) + list_for_update += opened_cell_coordinates index++ // Count value of field for scoring From cc50fba4e24414d6ff3790620e5bb7a66c32ea26 Mon Sep 17 00:00:00 2001 From: Gaxeer <44334376+Gaxeer@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:35:07 +0200 Subject: [PATCH 25/27] Update modular_bandastation/minesweeper/code/minesweeper.dm --- modular_bandastation/minesweeper/code/minesweeper.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 17abe30a61ec8..3681a96defb8d 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -276,7 +276,7 @@ * * @return coordinates of opened cell as `list(x, y)` if cell was successfully opened * and no bombs are around, `null` otherwise -/* + */ /datum/computer_file/program/minesweeper/proc/open_cell(x, y) var/list/minesweeper_cell = minesweeper_matrix[x][y] if(minesweeper_cell["open"]) From b6f4be62fb49525f5d06d2b4ca91971464247edd Mon Sep 17 00:00:00 2001 From: gaxeer Date: Mon, 3 Feb 2025 19:31:21 +0200 Subject: [PATCH 26/27] replace magic strings and numbers with macros, general code cleanup --- .../minesweeper/code/minesweeper.dm | 620 +++++++++++------- .../tgui/interfaces/NtosMinesweeperPanel.tsx | 18 +- 2 files changed, 380 insertions(+), 258 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 3681a96defb8d..7b554dd0ce36d 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -1,14 +1,27 @@ -#define MINESWEEPER_ROWS 16 -#define MINESWEEPER_COLUMNS 16 -#define MINESWEEPER_BOMBS 40 +#define DEFAULT_FIELD_HEIGHT 16 +#define DEFAULT_FIELD_WIDTH 16 +#define DEFAULT_BOMBS_AMOUNT 40 + +#define MIN_FIELD_SIDE_SIZE 9 +#define MAX_FIELD_SIDE_SIZE 25 +#define MIN_BOMBS_AMOUNT 10 +#define MAX_BOMBS_AMOUNT 100 +#define FIELD_AREA_TO_BOMBS_MIN_RATIO 5 + +#define CELL_PARAM_OPEN "open" +#define CELL_PARAM_BOMB "bomb" +#define CELL_PARAM_FLAG "flag" +#define CELL_PARAM_AROUND "around" +#define CELL_PARAM_MARKED "marked" +#define CELL_PARAM_FINAL "final" /datum/computer_file/program/minesweeper filename = "minesweeper" filedesc = "Minesweeper" // program_open_overlay = "minesweeper" - extended_desc = "Погрузись в удивительный мир 'Сапера',\ -где каждое неверное нажатие может привести к взрыву!\ - Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." + extended_desc = "Погрузись в удивительный мир 'Сапера', \ + где каждое неверное нажатие может привести к взрыву! \ + Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." downloader_category = PROGRAM_CATEGORY_GAMES size = 6 tgui_id = "NtosMinesweeperPanel" @@ -16,89 +29,73 @@ /// Thing, to make first touch safety var/first_touch = TRUE - // Win condition things - var/setted_flags = 0 + + /// Amount of set flags. Used to check win condition + var/set_flags = 0 + /// Amount of flagged bombs. Used to check win condition var/flagged_bombs = 0 + /// Amount of opened cells. Used to check win condition var/opened_cells = 0 + /// Decision to make interface untouchable in the momemnt of regenerating var/ignore_touches = FALSE - /// Here we have all the minesweeper info - var/list/minesweeper_matrix = list() - // generations vars - var/generation_rows = MINESWEEPER_ROWS - var/generation_columns = MINESWEEPER_COLUMNS - var/generation_bombs = MINESWEEPER_BOMBS - ///Current 3BV(special system of score for minesweeper) + /// Current field amount of rows + var/field_height = DEFAULT_FIELD_HEIGHT + /// Current field amount of columns + var/field_width = DEFAULT_FIELD_WIDTH + /// Current field amount of bombs + var/field_bombs_amount = DEFAULT_BOMBS_AMOUNT + /// Current field 3BV (special system of score for minesweeper). Used to calculate 3BV/s (user efficiency) var/current_3BV = 0 - /// The moment then game was started for point count + /// The world.time the game was started. Used to calculate 3BV/s (user efficiency) var/start_time = 0 - /// The current round leaderboard list - var/static/list/leaderboard = list() + + /// Emagged bomb stats + var/loose_explosion_range_heavy = -1 + var/loose_explosion_range_medium = 1 + var/loose_explosion_range_light = 3 + var/loose_explosion_range_flame = 2 + + /// Cells with bombs. Used in `reveal_all_bombs` + var/list/bomb_cells = list() + + /// Here we have all the minesweeper info + var/list/minesweeper_matrix = list() + /// The global leaderboard list var/static/list/glob_leaderboard = null - - //Emagged bomb stats - var/range_heavy = -1 - var/range_medium = 1 - var/range_light = 3 - var/range_flame = 2 + /// The current round leaderboard list + var/static/list/leaderboard = list() + /// Directions for adjacent cells + var/static/list/directions = list( + list(-1, 0), // Left + list( 1, 0), // Right + list( 0, -1), // Up + list( 0, 1), // Down + list(-1, -1), // Top-Left + list( 1, 1), // Bottom-Right + list(-1, 1), // Top-Right + list( 1, -1) // Bottom-Left + ) /datum/computer_file/program/minesweeper/New() ..() if(isnull(glob_leaderboard)) init_leaderboard() -///Get stored in database player results and fill glob_leaderboard with them -/datum/computer_file/program/minesweeper/proc/init_leaderboard() - glob_leaderboard = list() - var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, points_per_sec, time, width, height, bombs FROM [format_table_name("minesweeper")]") - if(!minesweeper_query.Execute()) - qdel(minesweeper_query) - return - while(minesweeper_query.NextRow()) - glob_leaderboard += list( - list( - "name" = minesweeper_query.item[1], - "time" = "[minesweeper_query.item[4]]", - "points" = "[minesweeper_query.item[2]]", - "pointsPerSec" = "[minesweeper_query.item[3]]", - "fieldParams" = "[minesweeper_query.item[5]]X[minesweeper_query.item[6]]([minesweeper_query.item[7]])" - ) - ) - qdel(minesweeper_query) - -///Insert new player result into database -/datum/computer_file/program/minesweeper/proc/add_result_to_db(list/new_result, ckey, width, height, bombs) - if(SSdbcore.Connect()) - var/datum/db_query/query_minesweeper = SSdbcore.NewQuery( - "INSERT INTO [format_table_name("minesweeper")] (ckey, time, points, points_per_sec, nickname, width, height, bombs) VALUES (:ckey, :time, :points, :points_per_sec, :nickname, :width, :height, :bombs)", - list( - "ckey" = ckey, - "time" = new_result["time"], - "points" = new_result["points"], - "points_per_sec" = new_result["pointsPerSec"], - "nickname" = new_result["name"], - "width" = width, - "height" = height, - "bombs" = bombs - ) - ) - query_minesweeper.Execute() - qdel(query_minesweeper) - /datum/computer_file/program/minesweeper/ui_interact(mob/user, datum/tgui/ui) - if(!LAZYLEN(minesweeper_matrix)) + if(!length(minesweeper_matrix)) make_empty_matrix() /datum/computer_file/program/minesweeper/ui_data(mob/user) var/list/data = list() data["matrix"] = minesweeper_matrix - data["flags"] = setted_flags - data["bombs"] = generation_bombs + data["flags"] = set_flags + data["bombs"] = field_bombs_amount data["leaderboard"] = leaderboard data["glob_leaderboard"] = glob_leaderboard data["first_touch"] = first_touch - data["field_params"] = list("width" = generation_columns, "height" = generation_rows, "bombs" = generation_bombs) + data["field_params"] = list("width" = field_width, "height" = field_height, "bombs" = field_bombs_amount) return data /datum/computer_file/program/minesweeper/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) @@ -114,64 +111,107 @@ switch(action) // Click on game field if("Square") - var/x = text2num(params["X"]) + 1 - var/y = text2num(params["Y"]) + 1 - switch(params["mode"]) - if("bomb") - if(first_touch) - generate_matrix(x, y) - open_cell(x, y) - - if(minesweeper_matrix[x][y]["bomb"]) - on_loose(ui.user) - return TRUE - - if("flag") - if(first_touch || minesweeper_matrix[x][y]["open"]) - return - - if(minesweeper_matrix[x][y]["flag"]) - minesweeper_matrix[x][y]["flag"] = FALSE - setted_flags -= 1 - if(minesweeper_matrix[x][y]["bomb"]) - flagged_bombs -= 1 - else - minesweeper_matrix[x][y]["flag"] = TRUE - setted_flags += 1 - if(minesweeper_matrix[x][y]["bomb"]) - flagged_bombs += 1 - - check_win(ui.user) + return handle_square_click(text2num(params["X"]) + 1, text2num(params["Y"]) + 1, params["mode"], ui.user) + // Change field params if("ChangeSize") - if(!first_touch) - return - var/ans = tgui_alert(ui.user, "Вы хотите изменить параметры поля?", "Настройки Сапёра", list("Да", "Нет")) - if(ans != "Да") - return - var/width = tgui_input_number(ui.user, "Выставите ширину", "Настройки Сапёра", generation_columns, 25, 9) - var/height = tgui_input_number(ui.user, "Выставите длину", "Настройки Сапёра", generation_rows, 25, 9) - var/bombs = tgui_input_number(ui.user, "Выставите кол-во бомб", "Настройки Сапёра", generation_bombs, 100, 10) - if(computer.loc != ui.user) - return - if(bombs > (width*height/5)) - tgui_alert(ui.user, "Слишком много бомб для данного размера поля!", "Настройки Сапёра", list("Ок")) - return - generation_rows = height - generation_columns = width - generation_bombs = bombs - make_empty_matrix() + return change_field_params(ui.user) +/** + * Handles a click on a square on the field. + * + * @param {number} x - Row of the square + * @param {number} y - Column of the square + * @param {string} mode - The mode of the click. Can be "bomb" or "flag" + * @param {mob} user - Mob that made the click + * + * @return {boolean} - TRUE if the click was handled successfully, FALSE otherwise + */ +/datum/computer_file/program/minesweeper/proc/handle_square_click(x, y, mode, mob/user) + PRIVATE_PROC(TRUE) + + if(x < 1) + stack_trace("invalid x '[x]' passed.") + return FALSE + + if(y < 1) + stack_trace("invalid y '[y]' passed.") + return FALSE + + switch(mode) + if("bomb") + if(first_touch) + generate_field(x, y) + + if(minesweeper_matrix[x][y][CELL_PARAM_BOMB]) + on_loose(x, y) + return TRUE + + update_zeros(x, y) + + if("flag") + var/list/minesweeper_cell = minesweeper_matrix[x][y] + if(first_touch || minesweeper_cell[CELL_PARAM_OPEN]) + return FALSE + + if(minesweeper_cell[CELL_PARAM_FLAG]) + minesweeper_cell[CELL_PARAM_FLAG] = FALSE + set_flags -= 1 + if(minesweeper_cell[CELL_PARAM_BOMB]) + flagged_bombs -= 1 + else + minesweeper_cell[CELL_PARAM_FLAG] = TRUE + set_flags += 1 + if(minesweeper_cell[CELL_PARAM_BOMB]) + flagged_bombs += 1 + else + stack_trace("Invalid mode '[mode]' passed.") + return FALSE + + check_win(user) + return TRUE + + +/// Requests user the new field params and updates the field +/datum/computer_file/program/minesweeper/proc/change_field_params(mob/user) + PRIVATE_PROC(TRUE) + + if(computer.loc != user) + return FALSE + + if(!first_touch) + return FALSE + + var/ans = tgui_alert(user, "Вы хотите изменить параметры поля?", "Настройки Сапёра", list("Да", "Нет")) + if(ans != "Да") + return FALSE + + var/width = tgui_input_number(user, "Выставите ширину", "Настройки Сапёра", field_width, MAX_FIELD_SIDE_SIZE, MIN_FIELD_SIDE_SIZE) + var/height = tgui_input_number(user, "Выставите длину", "Настройки Сапёра", field_height, MAX_FIELD_SIDE_SIZE, MIN_FIELD_SIDE_SIZE) + + var/max_bombs_amount = clamp(floor(width * height / FIELD_AREA_TO_BOMBS_MIN_RATIO), MIN_BOMBS_AMOUNT, MAX_BOMBS_AMOUNT) + var/bombs = tgui_input_number(user, "Выставите кол-во бомб", "Настройки Сапёра", min(field_bombs_amount, max_bombs_amount), max_bombs_amount, MIN_BOMBS_AMOUNT) + field_height = height + field_width = width + field_bombs_amount = bombs + make_empty_matrix() return TRUE /datum/computer_file/program/minesweeper/proc/check_win(mob/user) - if(flagged_bombs == generation_bombs && \ - setted_flags == generation_bombs && \ - opened_cells == (generation_rows * generation_columns - generation_bombs)) + PRIVATE_PROC(TRUE) + + if( + flagged_bombs == field_bombs_amount && \ + set_flags == field_bombs_amount && \ + opened_cells == (field_height * field_width - field_bombs_amount) + ) + on_win(user) /datum/computer_file/program/minesweeper/proc/on_win(mob/user) + PRIVATE_PROC(TRUE) + ignore_touches = TRUE playsound(get_turf(computer), 'sound/machines/ping.ogg', 20, TRUE) addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 5 SECONDS) @@ -179,94 +219,160 @@ /// Add player result to local, global leaderboards and DB /datum/computer_file/program/minesweeper/proc/add_into_leaders(mob/user, game_time) - var/nickname = tgui_input_text(user, "You finished the game in [game_time / 10] seconds.\n Write a nickname to save your result on the leaderboard.\n", "Minesweeper", "", 10) + PRIVATE_PROC(TRUE) + + var/game_time_in_seconds = game_time / 1 SECONDS + var/nickname = tgui_input_text(user, "You finished the game in [game_time_in_seconds] seconds.\n Write a nickname to save your result on the leaderboard.\n", "Minesweeper", "", 10) if(!nickname) return var/result_to_add = list( "name" = nickname, - "time" = "[game_time/10]", + "time" = "[game_time_in_seconds]", "points" = "[current_3BV]", - "pointsPerSec" = "[round(current_3BV/(game_time/10), 0.1)]", - "fieldParams" = "[generation_columns]X[generation_rows]([generation_bombs])" + "pointsPerSec" = "[round(current_3BV / (game_time_in_seconds), 0.1)]", + "fieldParams" = "[field_width]X[field_height]([field_bombs_amount])" ) leaderboard += list(result_to_add) glob_leaderboard += list(result_to_add) - add_result_to_db(result_to_add, user.ckey, generation_columns, generation_rows, generation_bombs) + add_result_to_db(result_to_add, user.ckey, field_width, field_height, field_bombs_amount) + +///Insert new player result into database +/datum/computer_file/program/minesweeper/proc/add_result_to_db(list/new_result, ckey, width, height, bombs) + PRIVATE_PROC(TRUE) + + if(SSdbcore.Connect()) + var/datum/db_query/query_minesweeper = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("minesweeper")] (ckey, time, points, points_per_sec, nickname, width, height, bombs) VALUES (:ckey, :time, :points, :points_per_sec, :nickname, :width, :height, :bombs)", + list( + "ckey" = ckey, + "time" = new_result["time"], + "points" = new_result["points"], + "points_per_sec" = new_result["pointsPerSec"], + "nickname" = new_result["name"], + "width" = width, + "height" = height, + "bombs" = bombs + ) + ) + query_minesweeper.Execute() + qdel(query_minesweeper) + +/// Called when player lost the game +/datum/computer_file/program/minesweeper/proc/on_loose(final_bomb_x, final_bomb_y) + PRIVATE_PROC(TRUE) -/datum/computer_file/program/minesweeper/proc/on_loose(mob/user) ignore_touches = TRUE + minesweeper_matrix[final_bomb_x][final_bomb_y][CELL_PARAM_FINAL] = TRUE + reveal_all_bombs() playsound(get_turf(computer), 'sound/effects/explosion/explosion1.ogg', 50, TRUE) if(computer.obj_flags & EMAGGED) - explosion(computer, range_heavy, range_medium, range_light, range_flame) - addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 3 SECONDS) + explosion( + computer, + loose_explosion_range_heavy, + loose_explosion_range_medium, + loose_explosion_range_light, + loose_explosion_range_flame + ) -// Return the minesweeper matrix to initial state + if(!QDELETED(src)) + addtimer(CALLBACK(src, PROC_REF(make_empty_matrix)), 3 SECONDS) + +/// Makes all cells with bombs open. Used in `on_loose` proc +/datum/computer_file/program/minesweeper/proc/reveal_all_bombs() + PRIVATE_PROC(TRUE) + + for(var/list/bomb_cell in bomb_cells) + bomb_cell[CELL_PARAM_OPEN] = TRUE + +/// Return the minesweeper matrix to initial state /datum/computer_file/program/minesweeper/proc/make_empty_matrix() + PRIVATE_PROC(TRUE) + minesweeper_matrix = list() - for(var/i in 1 to generation_rows) + for(var/row_number in 1 to field_height) var/list/new_row = list() - for(var/j in 1 to generation_columns) - new_row += list(list("open" = FALSE, "bomb" = FALSE, "flag" = FALSE, "around" = 0, "marked" = FALSE)) - minesweeper_matrix += list(new_row) + for(var/column_number in 1 to field_width) + var/list/cell = list( + CELL_PARAM_OPEN = FALSE, + CELL_PARAM_BOMB = FALSE, + CELL_PARAM_FLAG = FALSE, + CELL_PARAM_AROUND = 0, + CELL_PARAM_MARKED = FALSE + ) + + UNTYPED_LIST_ADD(new_row, cell) + + UNTYPED_LIST_ADD(minesweeper_matrix, new_row) + first_touch = TRUE ignore_touches = FALSE SStgui.update_uis(computer) -// Fill matrix with bombs, ignores 3x3 square around first touch place -/datum/computer_file/program/minesweeper/proc/generate_matrix(x, y) +/// Fill matrix with bombs, ignores 3x3 square around first touch place +/datum/computer_file/program/minesweeper/proc/generate_field(start_x, start_y) + PRIVATE_PROC(TRUE) + flagged_bombs = 0 - setted_flags = 0 + set_flags = 0 opened_cells = 0 - var/list/possible_list = list() - var/this_x = x - var/this_y = y - var/count = 0 - - for(var/i in 1 to generation_rows) - for(var/j in 1 to generation_columns) - if((i in list(this_x - 1, this_x, this_x + 1)) && (j in list(this_y - 1, this_y, this_y + 1))) + bomb_cells.Cut() + + var/list/possible_bomb_cells = list() + var/list/adjacent_cells_x = list(start_x - 1, start_x, start_x + 1) + var/list/adjacent_cells_y = list(start_y - 1, start_y, start_y + 1) + for(var/possible_bomb_cell_x in 1 to field_height) + for(var/possible_bomb_cell_y in 1 to field_width) + if((possible_bomb_cell_x in adjacent_cells_x) && (possible_bomb_cell_y in adjacent_cells_y)) continue - possible_list["[count]"] = list(i, j) - count++ - - for(var/bomb in 1 to generation_bombs) - var/cell = pick(possible_list) - var/coordinates = possible_list[cell] - possible_list -= cell - var/new_x = coordinates[1] - var/new_y = coordinates[2] - minesweeper_matrix[new_x][new_y]["bomb"] = TRUE - - if(new_x != 1) - minesweeper_matrix[new_x-1][new_y]["around"] += 1 - if(new_y != 1) - minesweeper_matrix[new_x][new_y-1]["around"] += 1 + UNTYPED_LIST_ADD(possible_bomb_cells, list(possible_bomb_cell_x, possible_bomb_cell_y)) - if(new_x != 1 && new_y != 1) - minesweeper_matrix[new_x-1][new_y-1]["around"] += 1 + for(var/bomb in 1 to field_bombs_amount) + var/list/cell_coordinates = pick_n_take(possible_bomb_cells) + var/cell_x = cell_coordinates[1] + var/cell_y = cell_coordinates[2] - if(new_x != generation_rows) - minesweeper_matrix[new_x+1][new_y]["around"] += 1 + var/list/cell = minesweeper_matrix[cell_x][cell_y] + cell[CELL_PARAM_BOMB] = TRUE + UNTYPED_LIST_ADD(bomb_cells, cell) - if(new_y != generation_columns) - minesweeper_matrix[new_x][new_y+1]["around"] += 1 - - if(new_x != generation_rows && new_y != generation_columns) - minesweeper_matrix[new_x+1][new_y+1]["around"] += 1 - - if(new_x != 1 && new_y != generation_columns) - minesweeper_matrix[new_x-1][new_y+1]["around"] += 1 + for(var/list/direction in directions) + var/adjacent_cell_x = cell_x + direction[1] + var/adjacent_cell_y = cell_y + direction[2] + if(!is_cell_in_bounds(adjacent_cell_x, adjacent_cell_y)) + continue - if(new_x != generation_rows && new_y != 1) - minesweeper_matrix[new_x+1][new_y-1]["around"] += 1 + minesweeper_matrix[adjacent_cell_x][adjacent_cell_y][CELL_PARAM_AROUND] += 1 first_touch = FALSE count_3BV() start_time = world.time +/// Open all "zeroes" around the click place +/datum/computer_file/program/minesweeper/proc/update_zeros(x, y) + PRIVATE_PROC(TRUE) + + var/list/list_for_update = list(list(x, y)) + var/list/visited = list() + while(length(list_for_update)) + var/list/coordinates = pop(list_for_update) + var/this_cell_x = coordinates[1] + var/this_cell_y = coordinates[2] + + if(!open_cell(this_cell_x, this_cell_y)) + continue + + for(var/list/direction in directions) + var/new_x = this_cell_x + direction[1] + var/new_y = this_cell_y + direction[2] + if(!is_cell_in_bounds(new_x, new_y) || visited["[new_x][new_y]"]) + continue + + visited["[new_x][new_y]"] = TRUE + UNTYPED_LIST_ADD(list_for_update, list(new_x, new_y)) + /** * Makes cell by passed coordinates open. * Increases `opened_cells` if cell was successfully opened and removes flag from it. @@ -274,106 +380,116 @@ * @param {number] x - The row of the cell * @param {number] y - The column of the cell * - * @return coordinates of opened cell as `list(x, y)` if cell was successfully opened - * and no bombs are around, `null` otherwise - */ + * @return {boolean} - TRUE if cell should cause recursive opening, FALSE otherwise + */ /datum/computer_file/program/minesweeper/proc/open_cell(x, y) + PRIVATE_PROC(TRUE) + var/list/minesweeper_cell = minesweeper_matrix[x][y] - if(minesweeper_cell["open"]) - return null - - minesweeper_cell["open"] = TRUE + if(minesweeper_cell[CELL_PARAM_OPEN]) + return FALSE + + minesweeper_cell[CELL_PARAM_OPEN] = TRUE opened_cells += 1 - if(minesweeper_cell["flag"]) - minesweeper_cell["flag"] = FALSE - setted_flags -= 1 - if(minesweeper_cell["bomb"]) + if(minesweeper_cell[CELL_PARAM_FLAG]) + minesweeper_cell[CELL_PARAM_FLAG] = FALSE + set_flags -= 1 + if(minesweeper_cell[CELL_PARAM_BOMB]) flagged_bombs -= 1 - - if(minesweeper_cell["around"] != 0) - return null - return list(x, y) + if(minesweeper_cell[CELL_PARAM_AROUND] > 0) + return FALSE -// Open all "zeroes" around the click place -/datum/computer_file/program/minesweeper/proc/update_zeros(x, y) - // First we open cell by passed coordinates and stop if it has bombs around or was already opened - if(!open_cell(x, y)) - return - - var/static/list/directions = list( - list(-1, 0), // Left - list( 1, 0), // Right - list( 0, -1), // Up - list( 0, 1), // Down - list(-1, -1), // Top-Left - list( 1, 1), // Bottom-Right - list(-1, 1), // Top-Right - list( 1, -1) // Bottom-Left - ) + return TRUE - var/list/list_for_update = list(list(x, y)) - var/index = 1 - - while (index < list_for_update.len) - var/list/coordinates = list_for_update[index] - var/this_x = coordinates[1] - var/this_y = coordinates[2] - - for (var/list/direction in directions) - var/new_x = this_x + direction[1] - var/new_y = this_y + direction[2] - - // Boundary checks - if (new_x >= 1 && new_x <= generation_rows && new_y >= 1 && new_y <= generation_columns) - // Diagonal check requires both adjacent cells to be open - if (abs(direction[1]) == 1 && abs(direction[2]) == 1) - if (!(minesweeper_matrix[new_x][this_y]["open"] && minesweeper_matrix[this_x][new_y]["open"])) - continue - - var/list/opened_cell_coordinates = open_cell(new_x, new_y) - if(opened_cell_coordinates) - list_for_update += opened_cell_coordinates - index++ - -// Count value of field for scoring +/// Count value of field for scoring /datum/computer_file/program/minesweeper/proc/count_3BV() + PRIVATE_PROC(TRUE) + current_3BV = 0 - for(var/i in 1 to generation_rows) - for(var/j in 1 to generation_columns) - if(minesweeper_matrix[i][j]["marked"]) + for(var/x in 1 to field_height) + for(var/y in 1 to field_width) + var/list/minesweeper_cell = minesweeper_matrix[x][y] + if(minesweeper_cell[CELL_PARAM_MARKED]) continue - minesweeper_matrix[i][j]["marked"] = TRUE - if(minesweeper_matrix[i][j]["bomb"]) + + minesweeper_cell[CELL_PARAM_MARKED] = TRUE + if(minesweeper_cell[CELL_PARAM_BOMB]) continue - if(minesweeper_matrix[i][j]["around"]) - current_3BV++ + + current_3BV++ + if(minesweeper_cell[CELL_PARAM_AROUND]) continue - else - current_3BV++ - count_zeros(i, j) + + mark_adjacent_zeros(x, y) + +/// part of proc/count_3BV, used to ignore adjacent "zeroes" +/datum/computer_file/program/minesweeper/proc/mark_adjacent_zeros(start_x, start_y) + PRIVATE_PROC(TRUE) + + var/list/check_list = list(list(start_x, start_y)) + while(length(check_list)) + var/list/coordinates = pop(check_list) + var/this_cell_x = coordinates[1] + var/this_cell_y = coordinates[2] + minesweeper_matrix[this_cell_x][this_cell_y][CELL_PARAM_MARKED] = TRUE + for(var/list/direction as anything in directions) + var/adjacent_cell_x = this_cell_x + direction[1] + var/adjacent_cell_y = this_cell_y + direction[2] + if(!is_cell_in_bounds(adjacent_cell_x, adjacent_cell_y)) + continue + + var/list/adjacent_cell = minesweeper_matrix[adjacent_cell_x][adjacent_cell_y] + if(adjacent_cell[CELL_PARAM_MARKED]) continue -// part of proc/count_3BV, used to ignore adjacent "zeroes" -/datum/computer_file/program/minesweeper/proc/count_zeros(start_x, start_y) - var/check_list = list(list(start_x, start_y)) - for(var/coordinates in check_list) - var/x = coordinates[1] - var/y = coordinates[2] - minesweeper_matrix[x][y]["marked"] = TRUE - for(var/i in list(x - 1, x, x + 1)) - for(var/j in list(y - 1, y, y + 1)) - if((i== 0) || (j == 0) || (i > generation_rows) || (j > generation_columns)) - continue - if(minesweeper_matrix[i][j]["marked"]) - continue - if(!minesweeper_matrix[i][j]["around"]) - check_list += list(list(i, j)) - -#undef MINESWEEPER_ROWS -#undef MINESWEEPER_COLUMNS -#undef MINESWEEPER_BOMBS + if(adjacent_cell[CELL_PARAM_AROUND]) + continue + + UNTYPED_LIST_ADD(check_list, list(adjacent_cell_x, adjacent_cell_y)) + +/// Checks if cell by passed coordinates is in field bounds +/datum/computer_file/program/minesweeper/proc/is_cell_in_bounds(cell_x, cell_y) + PRIVATE_PROC(TRUE) + + return cell_x >= 1 && cell_x <= field_height && cell_y >= 1 && cell_y <= field_width + +///Get stored in database player results and fill glob_leaderboard with them +/datum/computer_file/program/minesweeper/proc/init_leaderboard() + PRIVATE_PROC(TRUE) + + glob_leaderboard = list() + var/datum/db_query/minesweeper_query = SSdbcore.NewQuery("SELECT nickname, points, points_per_sec, time, width, height, bombs FROM [format_table_name("minesweeper")]") + if(!minesweeper_query.Execute()) + qdel(minesweeper_query) + return + + while(minesweeper_query.NextRow()) + glob_leaderboard += list( + list( + "name" = minesweeper_query.item[1], + "time" = "[minesweeper_query.item[4]]", + "points" = "[minesweeper_query.item[2]]", + "pointsPerSec" = "[minesweeper_query.item[3]]", + "fieldParams" = "[minesweeper_query.item[5]]X[minesweeper_query.item[6]]([minesweeper_query.item[7]])" + ) + ) + qdel(minesweeper_query) + +#undef DEFAULT_FIELD_HEIGHT +#undef DEFAULT_FIELD_WIDTH +#undef DEFAULT_BOMBS_AMOUNT +#undef MIN_FIELD_SIDE_SIZE +#undef MAX_FIELD_SIDE_SIZE +#undef MIN_BOMBS_AMOUNT +#undef MAX_BOMBS_AMOUNT +#undef FIELD_AREA_TO_BOMBS_MIN_RATIO +#undef CELL_PARAM_OPEN +#undef CELL_PARAM_BOMB +#undef CELL_PARAM_FLAG +#undef CELL_PARAM_AROUND +#undef CELL_PARAM_MARKED /* MINESWEEPER-PDA EMAG ACT */ diff --git a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx index c28ab7141ded3..a6743148540a4 100644 --- a/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx +++ b/tgui/packages/tgui/interfaces/NtosMinesweeperPanel.tsx @@ -1,13 +1,15 @@ import { Box, Button, Icon, Section, Stack, Table } from 'tgui-core/components'; +import { BooleanLike } from 'tgui-core/react'; import { useBackend, useSharedState } from '../backend'; import { NtosWindow } from '../layouts'; interface Bomb { - open: number; - bomb: number; - flag: number; + open: BooleanLike; + bomb: BooleanLike; + flag: BooleanLike; around: number; + final: BooleanLike; } type Matrix = Bomb[][]; @@ -34,7 +36,7 @@ type MinesweeperData = { bombs: number; leaderboard: Leaderboard; glob_leaderboard: Leaderboard; - first_touch: boolean; + first_touch: BooleanLike; field_params: FieldParams; }; @@ -131,7 +133,9 @@ export const MineSweeperGame = (props) => { icon={ matrix[i][j].open ? matrix[i][j].bomb - ? 'bomb' + ? matrix[i][j].final + ? 'burst' + : 'bomb' : '' : matrix[i][j].flag ? 'flag' @@ -140,7 +144,9 @@ export const MineSweeperGame = (props) => { textColor={ matrix[i][j].open ? matrix[i][j].bomb - ? 'black' + ? matrix[i][j].final + ? 'red' + : 'black' : NumColor[matrix[i][j].around] : matrix[i][j].flag ? 'red' From 8fd1393bcc1922f91dc5e905e565dea8f3350bc5 Mon Sep 17 00:00:00 2001 From: gaxeer Date: Tue, 4 Feb 2025 13:29:16 +0200 Subject: [PATCH 27/27] fix game time convertion to seconds, add some validations --- .../minesweeper/code/minesweeper.dm | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/modular_bandastation/minesweeper/code/minesweeper.dm b/modular_bandastation/minesweeper/code/minesweeper.dm index 7b554dd0ce36d..1704d3622b97a 100644 --- a/modular_bandastation/minesweeper/code/minesweeper.dm +++ b/modular_bandastation/minesweeper/code/minesweeper.dm @@ -2,6 +2,7 @@ #define DEFAULT_FIELD_WIDTH 16 #define DEFAULT_BOMBS_AMOUNT 40 +#define STARTING_AREA_SIZE 9 #define MIN_FIELD_SIDE_SIZE 9 #define MAX_FIELD_SIDE_SIZE 25 #define MIN_BOMBS_AMOUNT 10 @@ -17,9 +18,9 @@ /datum/computer_file/program/minesweeper filename = "minesweeper" - filedesc = "Minesweeper" + filedesc = "Сапёр" // program_open_overlay = "minesweeper" - extended_desc = "Погрузись в удивительный мир 'Сапера', \ + extended_desc = "Погрузись в удивительный мир 'Сапёра', \ где каждое неверное нажатие может привести к взрыву! \ Сразись с друзьями и стань мастером разминирования в этой захватывающей игре!." downloader_category = PROGRAM_CATEGORY_GAMES @@ -189,8 +190,21 @@ var/width = tgui_input_number(user, "Выставите ширину", "Настройки Сапёра", field_width, MAX_FIELD_SIDE_SIZE, MIN_FIELD_SIDE_SIZE) var/height = tgui_input_number(user, "Выставите длину", "Настройки Сапёра", field_height, MAX_FIELD_SIDE_SIZE, MIN_FIELD_SIDE_SIZE) - var/max_bombs_amount = clamp(floor(width * height / FIELD_AREA_TO_BOMBS_MIN_RATIO), MIN_BOMBS_AMOUNT, MAX_BOMBS_AMOUNT) - var/bombs = tgui_input_number(user, "Выставите кол-во бомб", "Настройки Сапёра", min(field_bombs_amount, max_bombs_amount), max_bombs_amount, MIN_BOMBS_AMOUNT) + var/field_area = width * height + if(field_area - STARTING_AREA_SIZE < MIN_BOMBS_AMOUNT) + stack_trace("Field area is too small [field_area].") + to_chat(user, span_warning("Поле слишком маленькое!")) + return FALSE + + var/max_bombs_amount = clamp(floor(field_area / FIELD_AREA_TO_BOMBS_MIN_RATIO), MIN_BOMBS_AMOUNT, MAX_BOMBS_AMOUNT) + var/bombs = tgui_input_number( + user, + "Выставите кол-во бомб", + "Настройки Сапёра", + min(field_bombs_amount, max_bombs_amount), + max_bombs_amount, + MIN_BOMBS_AMOUNT + ) field_height = height field_width = width @@ -221,8 +235,14 @@ /datum/computer_file/program/minesweeper/proc/add_into_leaders(mob/user, game_time) PRIVATE_PROC(TRUE) - var/game_time_in_seconds = game_time / 1 SECONDS - var/nickname = tgui_input_text(user, "You finished the game in [game_time_in_seconds] seconds.\n Write a nickname to save your result on the leaderboard.\n", "Minesweeper", "", 10) + var/game_time_in_seconds = game_time / (1 SECONDS) + var/nickname = tgui_input_text( + user, + "Вы сравелись за [game_time_in_seconds] секунд!\n Напишите ваш никнейм чтобы сохранить результат в рейтинговой таблице.\n", + "Сапёр", + "", + 10 + ) if(!nickname) return @@ -330,6 +350,9 @@ UNTYPED_LIST_ADD(possible_bomb_cells, list(possible_bomb_cell_x, possible_bomb_cell_y)) for(var/bomb in 1 to field_bombs_amount) + if(!length(possible_bomb_cells)) + break + var/list/cell_coordinates = pick_n_take(possible_bomb_cells) var/cell_x = cell_coordinates[1] var/cell_y = cell_coordinates[2]