mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-12 23:54:24 +01:00
game collection (#19179)
* game collection * no phased usage * . * spellcheck * chesskers * fixes runtime during del --------- Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
/obj/structure/casino_table/board_game
|
||||
name = "board game table"
|
||||
desc = "A collection of various board games."
|
||||
icon_state = "gamble_preview"
|
||||
var/datum/board_game/game_ui
|
||||
var/static/list/possible_games = list(
|
||||
GAME_SWEEPER = /datum/board_game/four_row,
|
||||
GAME_FOUR_ROW = /datum/board_game/vore_sweeper,
|
||||
GAME_SPACE_BATTLE = /datum/board_game/space_battle,
|
||||
GAME_RGP_DICE = /datum/board_game/rpg_dice,
|
||||
GAME_CHESS = /datum/board_game/chess,
|
||||
GAME_CHECKERS = /datum/board_game/checkers,
|
||||
GAME_NINE_MENS_MORRIS = /datum/board_game/nine_mens,
|
||||
GAME_TIC_TAC_TOE = /datum/board_game/four_row/tic_tac_toe
|
||||
)
|
||||
|
||||
/obj/structure/casino_table/board_game/Initialize(mapload)
|
||||
. = ..()
|
||||
if(ispath(game_ui))
|
||||
game_ui = new game_ui(src)
|
||||
|
||||
/obj/structure/casino_table/board_game/Destroy()
|
||||
if(game_ui)
|
||||
game_ui.parent = null
|
||||
QDEL_NULL(game_ui)
|
||||
. = ..()
|
||||
|
||||
/obj/structure/casino_table/board_game/attack_hand(mob/user)
|
||||
. = ..()
|
||||
if(isliving(user))
|
||||
if(!game_ui)
|
||||
pick_game(user)
|
||||
game_ui.tgui_interact(user)
|
||||
|
||||
/obj/structure/casino_table/board_game/click_alt(mob/user)
|
||||
pick_game(user)
|
||||
|
||||
/obj/structure/casino_table/board_game/proc/pick_game(mob/user)
|
||||
if(game_ui?.game_state != GAME_SETUP)
|
||||
return
|
||||
var/datum/board_game/new_game = tgui_input_list(user, "Pick the game to play", "Choose Game", possible_games)
|
||||
if(!new_game)
|
||||
return
|
||||
new_game = possible_games[new_game]
|
||||
if(game_ui)
|
||||
QDEL_NULL(game_ui)
|
||||
game_ui = new new_game(src)
|
||||
icon_state = game_ui.table_icon
|
||||
|
||||
/datum/board_game
|
||||
var/name
|
||||
var/atom/parent
|
||||
var/game_state = GAME_SETUP
|
||||
var/table_icon = "gamble_preview"
|
||||
|
||||
/datum/board_game/tgui_state(mob/user)
|
||||
return GLOB.tgui_board_game_state
|
||||
|
||||
/datum/board_game/New(atom/holder)
|
||||
. = ..()
|
||||
parent = holder
|
||||
|
||||
/datum/board_game/Destroy(force)
|
||||
parent = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/tgui_host(mob/user)
|
||||
return parent
|
||||
|
||||
/datum/board_game/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("invite_player")
|
||||
var/list/possible_mobs = ui.user.living_mobs_in_view(1, TRUE, TRUE)
|
||||
for(var/obj/belly/our_belly in ui.user.vore_organs)
|
||||
for(var/mob/living/prey in our_belly.contents)
|
||||
if(prey.client)
|
||||
possible_mobs += prey
|
||||
var/mob/living/new_player = tgui_input_list(ui.user, "Invite a nearby player to the game.", "Invite Player", possible_mobs)
|
||||
if(!new_player)
|
||||
return FALSE
|
||||
tgui_interact(new_player)
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -0,0 +1,523 @@
|
||||
#define GAME_PLAYER_ONE 1
|
||||
#define GAME_PLAYER_TWO 2
|
||||
#define GAME_OVER 3
|
||||
#define GAME_OVER_DRAW 4
|
||||
#define GRID_SIZE 8
|
||||
|
||||
#define GAME_ACTION_NONE 0
|
||||
#define GAME_ACTION_SELECT 1
|
||||
#define GAME_ACTION_END_TURN 2
|
||||
|
||||
/obj/structure/casino_table/board_game/checkers
|
||||
name = GAME_CHECKERS
|
||||
desc = "A classic checkers game."
|
||||
icon_state = "gamble_chess"
|
||||
game_ui = /datum/board_game/checkers
|
||||
|
||||
/datum/board_game/checkers
|
||||
name = GAME_CHECKERS
|
||||
table_icon = "gamble_chess"
|
||||
var/datum/weakref/player_one
|
||||
var/datum/weakref/player_two
|
||||
var/player_one_time = 0
|
||||
var/player_two_time = 0
|
||||
var/static/list/default_board = list(
|
||||
list(null, "bM", null, "bM", null, "bM", null, "bM"),
|
||||
list("bM", null, "bM", null, "bM", null, "bM", null),
|
||||
list(null, "bM", null, "bM", null, "bM", null, "bM"),
|
||||
list(null, null, null, null, null, null, null, null),
|
||||
list(null, null, null, null, null, null, null, null),
|
||||
list("wM", null, "wM", null, "wM", null, "wM", null),
|
||||
list(null, "wM", null, "wM", null, "wM", null, "wM"),
|
||||
list("wM", null, "wM", null, "wM", null, "wM", null)
|
||||
)
|
||||
var/list/current_board = list()
|
||||
var/list/valid_moves = list()
|
||||
var/list/selected_figure = list()
|
||||
var/list/possible_jumps = list()
|
||||
var/turn_start_time = 0
|
||||
var/winner
|
||||
|
||||
/datum/board_game/checkers/Destroy(force)
|
||||
player_one = null
|
||||
player_two = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/checkers/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ChessCheckers", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/checkers/tgui_static_data(mob/user)
|
||||
return list("game_type" = "checkers")
|
||||
|
||||
/datum/board_game/checkers/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
|
||||
return list(
|
||||
"player_one" = player_one_mob,
|
||||
"player_two" = player_two_mob,
|
||||
"player_one_time" = player_one_time + (game_state == GAME_PLAYER_ONE ? world.time - turn_start_time : 0),
|
||||
"player_two_time" = player_two_time + (game_state == GAME_PLAYER_TWO ? world.time - turn_start_time : 0),
|
||||
"current_board" = current_board,
|
||||
"selected_figure" = selected_figure,
|
||||
"valid_moves" = valid_moves,
|
||||
"game_state" = game_state,
|
||||
"winner" = winner,
|
||||
"has_won" = winner == ui.user.name,
|
||||
"possible_jumps" = possible_jumps
|
||||
)
|
||||
|
||||
/datum/board_game/checkers/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_player_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() == ui.user)
|
||||
player_one = null
|
||||
return TRUE
|
||||
player_one = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("be_player_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() == ui.user)
|
||||
player_two = null
|
||||
return TRUE
|
||||
player_two = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("swap_players")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
if("clear_game")
|
||||
if(game_state == GAME_SETUP)
|
||||
return FALSE
|
||||
reset(TRUE)
|
||||
return TRUE
|
||||
if("start_game")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
current_board = get_defaultboard()
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again_swapped")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("game_action")
|
||||
if(ui.user == player_one?.resolve() && game_state == GAME_PLAYER_ONE)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "w")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_TWO
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if(ui.user == player_two?.resolve() && game_state == GAME_PLAYER_TWO)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "b")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/checkers/proc/reset(full)
|
||||
winner = null
|
||||
player_one_time = 0
|
||||
player_two_time = 0
|
||||
selected_figure.Cut()
|
||||
valid_moves.Cut()
|
||||
if(full)
|
||||
current_board.Cut()
|
||||
game_state = GAME_SETUP
|
||||
player_one = null
|
||||
player_two = null
|
||||
else
|
||||
current_board = get_defaultboard()
|
||||
game_state = GAME_PLAYER_ONE
|
||||
|
||||
/datum/board_game/checkers/proc/player_actions(action, list/params, mob/user, active_color)
|
||||
switch(action)
|
||||
if("select_figure")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if (!validated_data)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/piece = current_board[validated_data[2]][validated_data[1]]
|
||||
if(!piece || piece[1] != active_color)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
if(has_available_jumps(active_color))
|
||||
var/list/jumps = generate_valid_moves(validated_data[1], validated_data[2], TRUE)
|
||||
if (length(jumps) == 0)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
selected_figure = validated_data
|
||||
update_valid_moves()
|
||||
return GAME_ACTION_SELECT
|
||||
|
||||
if("move_figure")
|
||||
var/list/coords = validate_coords(params)
|
||||
if(!coords || !selected_figure)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/to_x = coords[1]
|
||||
var/to_y = coords[2]
|
||||
|
||||
if(!valid_move(to_x, to_y))
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/from_x = selected_figure[1]
|
||||
var/from_y = selected_figure[2]
|
||||
var/moving_piece = current_board[from_y][from_x]
|
||||
|
||||
if(moving_piece[2] == "M" && ((moving_piece[1] == "w" && to_y == 1) || (moving_piece[1] == "b" && to_y == GRID_SIZE)))
|
||||
moving_piece = moving_piece[1] + "K"
|
||||
|
||||
current_board[to_y][to_x] = moving_piece
|
||||
current_board[from_y][from_x] = null
|
||||
|
||||
var/did_jump = FALSE
|
||||
if(abs(to_x - from_x) >= 2 && abs(to_y - from_y) >= 2)
|
||||
var/step_x = (to_x - from_x) / abs(to_x - from_x)
|
||||
var/step_y = (to_y - from_y) / abs(to_y - from_y)
|
||||
var/x = from_x + step_x
|
||||
var/y = from_y + step_y
|
||||
|
||||
while(x != to_x && y != to_y)
|
||||
var/p = current_board[y][x]
|
||||
if(p && p[1] != moving_piece[1])
|
||||
current_board[y][x] = null
|
||||
did_jump = TRUE
|
||||
break
|
||||
x += step_x
|
||||
y += step_y
|
||||
|
||||
if(did_jump && piece_can_jump_again(to_x, to_y))
|
||||
selected_figure = list(to_x, to_y)
|
||||
update_valid_moves()
|
||||
validate_victory(active_color)
|
||||
possible_jumps = get_mandatory_jumps(active_color)
|
||||
return GAME_ACTION_SELECT
|
||||
|
||||
var/turn_duration = world.time - turn_start_time
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
player_one_time += turn_duration
|
||||
else if(game_state == GAME_PLAYER_TWO)
|
||||
player_two_time += turn_duration
|
||||
|
||||
selected_figure.Cut()
|
||||
update_valid_moves()
|
||||
validate_victory(active_color)
|
||||
if(game_state != GAME_OVER)
|
||||
possible_jumps = get_mandatory_jumps(active_color == "w" ? "b" : "w")
|
||||
else
|
||||
possible_jumps.Cut()
|
||||
return GAME_ACTION_END_TURN
|
||||
|
||||
/datum/board_game/checkers/proc/get_mandatory_jumps(active_color)
|
||||
var/list/jumping_pieces = list()
|
||||
if(!has_available_jumps(active_color))
|
||||
return jumping_pieces
|
||||
|
||||
for(var/row = 1 to GRID_SIZE)
|
||||
for(var/col = 1 to GRID_SIZE)
|
||||
var/piece = current_board[row][col]
|
||||
if(piece && piece[1] == active_color)
|
||||
var/list/jumps = generate_valid_moves(col, row, TRUE)
|
||||
if(length(jumps) > 0)
|
||||
UNTYPED_LIST_ADD(jumping_pieces, list(col, row))
|
||||
return jumping_pieces
|
||||
|
||||
/datum/board_game/checkers/proc/update_valid_moves()
|
||||
valid_moves.Cut()
|
||||
if(length(selected_figure))
|
||||
var/x = selected_figure[1]
|
||||
var/y = selected_figure[2]
|
||||
valid_moves = generate_valid_moves(x, y)
|
||||
|
||||
/datum/board_game/checkers/proc/piece_can_jump_again(x, y)
|
||||
var/piece = current_board[y][x]
|
||||
if(!piece)
|
||||
return FALSE
|
||||
|
||||
for(var/list/move in generate_valid_moves(x, y, TRUE))
|
||||
if(abs(move[1] - x) >= 2 && abs(move[2] - y) >= 2)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/checkers/proc/valid_move(to_x, to_y)
|
||||
for(var/list/move in valid_moves)
|
||||
if(move[1] == to_x && move[2] == to_y)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/checkers/proc/validate_victory(active_color)
|
||||
var/opponent_color = active_color == "w" ? "b" : "w"
|
||||
var/any_opponent_pieces = FALSE
|
||||
var/opponent_has_moves = FALSE
|
||||
|
||||
var/opponent_has_jumps = has_available_jumps(opponent_color)
|
||||
|
||||
for(var/row = 1 to GRID_SIZE)
|
||||
for(var/col = 1 to GRID_SIZE)
|
||||
var/piece = current_board[row][col]
|
||||
if(piece && piece[1] == opponent_color)
|
||||
any_opponent_pieces = TRUE
|
||||
|
||||
var/list/moves
|
||||
if(opponent_has_jumps)
|
||||
moves = generate_valid_moves(col, row, TRUE)
|
||||
else
|
||||
moves = generate_valid_moves(col, row)
|
||||
|
||||
if(length(moves) > 0)
|
||||
opponent_has_moves = TRUE
|
||||
break
|
||||
if(opponent_has_moves)
|
||||
break
|
||||
|
||||
if(!any_opponent_pieces)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
winner = active_color == "w" ? player_one_mob?.name : player_two_mob?.name
|
||||
game_state = GAME_OVER
|
||||
return
|
||||
|
||||
if(!opponent_has_moves)
|
||||
if(any_opponent_pieces)
|
||||
winner = null
|
||||
game_state = GAME_OVER_DRAW
|
||||
return
|
||||
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
winner = active_color == "w" ? player_one_mob?.name : player_two_mob?.name
|
||||
game_state = GAME_OVER
|
||||
|
||||
/datum/board_game/checkers/proc/generate_valid_moves(x, y, jump_only)
|
||||
var/piece = current_board[y][x]
|
||||
var/list/moves = list()
|
||||
|
||||
if (!piece)
|
||||
return moves
|
||||
|
||||
var/directions = list()
|
||||
|
||||
switch(piece[2])
|
||||
if("M")
|
||||
if(piece[1] == "w")
|
||||
directions = list(list(1, -1), list(-1, -1))
|
||||
else
|
||||
directions = list(list(1, 1), list(-1, 1))
|
||||
if("K")
|
||||
directions = list(list(1, 1), list(-1, 1), list(1, -1), list(-1, -1))
|
||||
|
||||
var/list/jump_moves = list()
|
||||
add_jumps(directions, x, y, jump_moves)
|
||||
|
||||
if(length(jump_moves) || jump_only)
|
||||
return jump_moves
|
||||
|
||||
switch(piece[2])
|
||||
if("M")
|
||||
for(var/list/direction in directions)
|
||||
var/new_x = x + direction[1]
|
||||
var/new_y = y + direction[2]
|
||||
if(field_check(new_x, new_y) && current_board[new_y][new_x] == null)
|
||||
UNTYPED_LIST_ADD(moves, list(new_x, new_y))
|
||||
if("K")
|
||||
for(var/list/direction in directions)
|
||||
var/step = 1
|
||||
while(TRUE)
|
||||
var/new_x = x + direction[1] * step
|
||||
var/new_y = y + direction[2] * step
|
||||
if(!field_check(new_x, new_y))
|
||||
break
|
||||
if(current_board[new_y][new_x])
|
||||
break
|
||||
UNTYPED_LIST_ADD(moves, list(new_x, new_y))
|
||||
step += 1
|
||||
return moves
|
||||
|
||||
/datum/board_game/checkers/proc/has_available_jumps(active_color)
|
||||
for (var/row = 1 to GRID_SIZE)
|
||||
for (var/col = 1 to GRID_SIZE)
|
||||
var/piece = current_board[row][col]
|
||||
if (piece && piece[1] == active_color)
|
||||
var/list/jumps = generate_valid_moves(col, row, TRUE)
|
||||
if (length(jumps) > 0)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/checkers/proc/already_visited(x, y, list/visited_list)
|
||||
for(var/list/coord in visited_list)
|
||||
if(coord[1] == x && coord[2] == y)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/checkers/proc/add_jumps(var/list/directions, start_x, start_y, list/jump_moves, list/visited = list())
|
||||
var/piece = current_board[start_y][start_x]
|
||||
if(!piece)
|
||||
return
|
||||
|
||||
for(var/list/direction in directions)
|
||||
switch(piece[2])
|
||||
if("M")
|
||||
var/tx = start_x + direction[1] * 2
|
||||
var/ty = start_y + direction[2] * 2
|
||||
|
||||
if(!field_check(tx, ty) || already_visited(tx, ty, visited))
|
||||
continue
|
||||
|
||||
if(can_jump_piece(start_x, start_y, tx, ty))
|
||||
var/list/new_visited = visited.Copy()
|
||||
UNTYPED_LIST_ADD(new_visited, list(tx, ty))
|
||||
UNTYPED_LIST_ADD(jump_moves, list(tx, ty))
|
||||
add_jumps(directions, tx, ty, jump_moves, new_visited)
|
||||
if("K")
|
||||
var/step = 1
|
||||
while(TRUE)
|
||||
var/tx = start_x + direction[1] * step
|
||||
var/ty = start_y + direction[2] * step
|
||||
|
||||
if(!field_check(tx, ty))
|
||||
break
|
||||
|
||||
if(already_visited(tx, ty, visited))
|
||||
step++
|
||||
continue
|
||||
|
||||
if(can_jump_piece(start_x, start_y, tx, ty))
|
||||
var/list/new_visited = visited.Copy()
|
||||
UNTYPED_LIST_ADD(new_visited, list(tx, ty))
|
||||
UNTYPED_LIST_ADD(jump_moves, list(tx, ty))
|
||||
add_jumps(directions, tx, ty, jump_moves, new_visited)
|
||||
|
||||
step++
|
||||
|
||||
/datum/board_game/checkers/proc/can_jump_piece(from_x, from_y, to_x, to_y)
|
||||
var/piece = current_board[from_y][from_x]
|
||||
if(!piece)
|
||||
return FALSE
|
||||
|
||||
var/dx = to_x - from_x
|
||||
var/dy = to_y - from_y
|
||||
var/color = piece[1]
|
||||
|
||||
switch(piece[2])
|
||||
if("M")
|
||||
if(abs(dx) != 2 || abs(dy) != 2)
|
||||
return FALSE
|
||||
var/mid_x = from_x + dx / 2
|
||||
var/mid_y = from_y + dy / 2
|
||||
var/mid_piece = current_board[mid_y][mid_x]
|
||||
if(mid_piece && mid_piece[1] != color && current_board[to_y][to_x] == null)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if("K")
|
||||
if(abs(dx) != abs(dy))
|
||||
return FALSE
|
||||
|
||||
var/step_x = dx / abs(dx)
|
||||
var/step_y = dy / abs(dy)
|
||||
var/x = from_x + step_x
|
||||
var/y = from_y + step_y
|
||||
|
||||
var/jumped_piece_found = FALSE
|
||||
var/jumped_x = 0
|
||||
var/jumped_y = 0
|
||||
|
||||
while(x != to_x && y != to_y)
|
||||
var/piece_at_square = current_board[y][x]
|
||||
if(piece_at_square)
|
||||
if(piece_at_square[1] == color)
|
||||
return FALSE
|
||||
if(jumped_piece_found)
|
||||
return FALSE
|
||||
jumped_piece_found = TRUE
|
||||
jumped_x = x
|
||||
jumped_y = y
|
||||
x += step_x
|
||||
y += step_y
|
||||
|
||||
if(!jumped_piece_found)
|
||||
return FALSE
|
||||
|
||||
if(current_board[to_y][to_x] != null)
|
||||
return FALSE
|
||||
|
||||
x = jumped_x + step_x
|
||||
y = jumped_y + step_y
|
||||
|
||||
while(x != to_x || y != to_y)
|
||||
if(current_board[y][x] != null)
|
||||
return FALSE
|
||||
x += step_x
|
||||
y += step_y
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/checkers/proc/field_check(new_x_location, new_y_location)
|
||||
if(new_x_location < 1 || new_x_location > GRID_SIZE)
|
||||
return FALSE
|
||||
if(new_y_location < 1 || new_y_location > GRID_SIZE)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/checkers/proc/validate_coords(list/params)
|
||||
var/x_loc = text2num(params["loc_x"])
|
||||
var/y_loc = text2num(params["loc_y"])
|
||||
|
||||
if(!isnum(x_loc) || !isnum(y_loc))
|
||||
return null
|
||||
|
||||
if(!field_check(x_loc, y_loc))
|
||||
return null
|
||||
|
||||
return list(x_loc, y_loc)
|
||||
|
||||
/datum/board_game/checkers/proc/get_defaultboard()
|
||||
var/list/deep_copy = list()
|
||||
for(var/list/row in default_board)
|
||||
UNTYPED_LIST_ADD(deep_copy, row.Copy())
|
||||
return deep_copy
|
||||
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GAME_PLAYER_TWO
|
||||
#undef GAME_OVER
|
||||
#undef GAME_OVER_DRAW
|
||||
#undef GRID_SIZE
|
||||
|
||||
#undef GAME_ACTION_NONE
|
||||
#undef GAME_ACTION_SELECT
|
||||
#undef GAME_ACTION_END_TURN
|
||||
@@ -0,0 +1,566 @@
|
||||
#define GAME_PLAYER_ONE 1
|
||||
#define GAME_PLAYER_TWO 2
|
||||
#define GAME_OVER 3
|
||||
#define GAME_OVER_DRAW 4
|
||||
#define GRID_SIZE 8
|
||||
#define GAME_FLAG_CHECK_PONE 0x1
|
||||
#define GAME_FLAG_CHECK_PTWO 0x2
|
||||
#define GAME_FLAG_CASTLING_USED_PONE 0x4
|
||||
#define GAME_FLAG_CASTLING_USED_PTWO 0x8
|
||||
|
||||
#define GAME_ACTION_NONE 0
|
||||
#define GAME_ACTION_SELECT 1
|
||||
#define GAME_ACTION_END_TURN 2
|
||||
|
||||
/obj/structure/casino_table/board_game/chess
|
||||
name = GAME_CHESS
|
||||
desc = "A classic chess game."
|
||||
icon_state = "gamble_chess"
|
||||
game_ui = /datum/board_game/chess
|
||||
|
||||
/datum/board_game/chess
|
||||
name = GAME_CHESS
|
||||
table_icon = "gamble_chess"
|
||||
var/datum/weakref/player_one
|
||||
var/datum/weakref/player_two
|
||||
var/player_one_time = 0
|
||||
var/player_two_time = 0
|
||||
var/static/list/default_board = list(
|
||||
list("bR","bN","bB","bQ","bK","bB","bN","bR"),
|
||||
list("bP","bP","bP","bP","bP","bP","bP","bP"),
|
||||
list(null,null,null,null,null,null,null,null),
|
||||
list(null,null,null,null,null,null,null,null),
|
||||
list(null,null,null,null,null,null,null,null),
|
||||
list(null,null,null,null,null,null,null,null),
|
||||
list("wP","wP","wP","wP","wP","wP","wP","wP"),
|
||||
list("wR","wN","wB","wQ","wK","wB","wN","wR")
|
||||
)
|
||||
var/list/current_board = list()
|
||||
var/list/valid_moves = list()
|
||||
var/list/selected_figure = list()
|
||||
var/list/last_double_pawn_move = list()
|
||||
var/game_flags = NONE
|
||||
var/turn_start_time = 0
|
||||
var/winner
|
||||
|
||||
/datum/board_game/chess/Destroy(force)
|
||||
player_one = null
|
||||
player_two = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/chess/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ChessCheckers", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/chess/tgui_static_data(mob/user)
|
||||
return list("game_type" = "chess")
|
||||
|
||||
/datum/board_game/chess/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
|
||||
return list(
|
||||
"player_one" = player_one_mob,
|
||||
"player_two" = player_two_mob,
|
||||
"player_one_time" = player_one_time + (game_state == GAME_PLAYER_ONE ? world.time - turn_start_time : 0),
|
||||
"player_two_time" = player_two_time + (game_state == GAME_PLAYER_TWO ? world.time - turn_start_time : 0),
|
||||
"current_board" = current_board,
|
||||
"selected_figure" = selected_figure,
|
||||
"valid_moves" = valid_moves,
|
||||
"game_state" = game_state,
|
||||
"winner" = winner,
|
||||
"has_won" = winner == ui.user.name,
|
||||
"game_flags" = game_flags
|
||||
)
|
||||
|
||||
/datum/board_game/chess/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_player_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() == ui.user)
|
||||
player_one = null
|
||||
return TRUE
|
||||
player_one = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("be_player_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() == ui.user)
|
||||
player_two = null
|
||||
return TRUE
|
||||
player_two = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("swap_players")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
if("clear_game")
|
||||
if(game_state == GAME_SETUP)
|
||||
return FALSE
|
||||
reset(TRUE)
|
||||
return TRUE
|
||||
if("start_game")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
current_board = get_defaultboard()
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again_swapped")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("game_action")
|
||||
if(ui.user == player_one?.resolve() && game_state == GAME_PLAYER_ONE)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "w")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_TWO
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if(ui.user == player_two?.resolve() && game_state == GAME_PLAYER_TWO)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "b")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/chess/proc/reset(full)
|
||||
winner = null
|
||||
player_one_time = 0
|
||||
player_two_time = 0
|
||||
selected_figure.Cut()
|
||||
valid_moves.Cut()
|
||||
last_double_pawn_move.Cut()
|
||||
game_flags = NONE
|
||||
if(full)
|
||||
current_board.Cut()
|
||||
game_state = GAME_SETUP
|
||||
player_one = null
|
||||
player_two = null
|
||||
else
|
||||
current_board = get_defaultboard()
|
||||
game_state = GAME_PLAYER_ONE
|
||||
|
||||
/datum/board_game/chess/proc/player_actions(action, list/params, mob/user, active_color)
|
||||
switch(action)
|
||||
if("select_figure")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/piece = current_board[validated_data[2]][validated_data[1]]
|
||||
if(!piece)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
if(piece[1] != active_color)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
selected_figure = validated_data
|
||||
update_valid_moves()
|
||||
return GAME_ACTION_SELECT
|
||||
if("move_figure")
|
||||
var/list/coords = validate_coords(params)
|
||||
if(!coords || !selected_figure)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/from_x = selected_figure[1]
|
||||
var/from_y = selected_figure[2]
|
||||
var/to_x = coords[1]
|
||||
var/to_y = coords[2]
|
||||
|
||||
if(!valid_move(to_x, to_y))
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/moving_piece = current_board[from_y][from_x]
|
||||
var/target_piece = current_board[to_y][to_x]
|
||||
|
||||
var/turn_duration = world.time - turn_start_time
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
player_one_time += turn_duration
|
||||
else if(game_state == GAME_PLAYER_TWO)
|
||||
player_two_time += turn_duration
|
||||
|
||||
if(moving_piece[2] == "K" && abs(to_x - from_x) == 2)
|
||||
if(!handle_castling(active_color, from_x, from_y, to_x, to_y))
|
||||
return GAME_ACTION_NONE
|
||||
do_castling(active_color, from_x, from_y, to_x, to_y)
|
||||
|
||||
if(moving_piece[2] == "P" && abs(to_x - from_x) == 1 && !target_piece)
|
||||
if(can_en_passant(from_x, from_y, to_x, to_y))
|
||||
current_board[from_y][to_x] = null
|
||||
|
||||
if(!(moving_piece[2] == "K" && abs(to_x - from_x) == 2))
|
||||
current_board[to_y][to_x] = moving_piece
|
||||
current_board[from_y][from_x] = null
|
||||
|
||||
if(moving_piece[2] == "P" && abs(to_y - from_y) == 2)
|
||||
last_double_pawn_move = list(to_x, to_y, moving_piece[1])
|
||||
else
|
||||
last_double_pawn_move.Cut()
|
||||
|
||||
if(moving_piece[2] == "P")
|
||||
if((moving_piece[1] == "w" && to_y == 1) || (moving_piece[1] == "b" && to_y == GRID_SIZE))
|
||||
var/promotion = params["promotion_choice"]
|
||||
if(!(promotion in list("Q", "R", "B", "N")))
|
||||
promotion = "Q"
|
||||
current_board[to_y][to_x] = moving_piece[1] + promotion
|
||||
|
||||
selected_figure.Cut()
|
||||
update_valid_moves()
|
||||
validate_victory(active_color)
|
||||
|
||||
return GAME_ACTION_END_TURN
|
||||
|
||||
/datum/board_game/chess/proc/valid_move(to_x, to_y)
|
||||
for(var/list/move in valid_moves)
|
||||
if(move[1] == to_x && move[2] == to_y)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/chess/proc/handle_castling(active_color, from_x, from_y, to_x, to_y)
|
||||
if(active_color == "w" && (game_flags & (GAME_FLAG_CASTLING_USED_PONE|GAME_FLAG_CHECK_PONE)))
|
||||
return FALSE
|
||||
if(active_color == "b" && (game_flags & (GAME_FLAG_CASTLING_USED_PTWO|GAME_FLAG_CHECK_PTWO)))
|
||||
return FALSE
|
||||
|
||||
var/king = current_board[from_y][from_x]
|
||||
|
||||
if(king != "wK" && king != "bK")
|
||||
return FALSE
|
||||
|
||||
if(abs(to_x - from_x) != 2)
|
||||
return FALSE
|
||||
|
||||
var/row = from_y
|
||||
var/rook_x
|
||||
var/rook
|
||||
var/col_start
|
||||
var/col_end
|
||||
|
||||
if(to_x > from_x)
|
||||
rook_x = GRID_SIZE
|
||||
rook = current_board[row][rook_x]
|
||||
if(rook != "wR" && rook != "bR")
|
||||
return FALSE
|
||||
col_start = from_x + 1
|
||||
col_end = to_x
|
||||
else
|
||||
rook_x = 1
|
||||
rook = current_board[row][rook_x]
|
||||
if(rook != "wR" && rook != "bR")
|
||||
return FALSE
|
||||
col_start = to_x
|
||||
col_end = from_x - 1
|
||||
|
||||
for(var/c = col_start to col_end)
|
||||
if(current_board[row][c] != null && c != from_x)
|
||||
return FALSE
|
||||
|
||||
var/opponent_color = active_color == "w" ? "b" : "w"
|
||||
if(!opponent_color)
|
||||
return FALSE
|
||||
|
||||
if(square_under_attack(opponent_color, from_x, from_y))
|
||||
return FALSE
|
||||
|
||||
var/step = (to_x > from_x ? 1 : -1)
|
||||
var/x = from_x
|
||||
while(x != to_x + step)
|
||||
if(x != from_x && x != to_x && square_under_attack(opponent_color, x, row))
|
||||
return FALSE
|
||||
x += step
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/chess/proc/do_castling(active_color, from_x, from_y, to_x, to_y)
|
||||
var/row = from_y
|
||||
var/king = current_board[from_y][from_x]
|
||||
var/rook_x
|
||||
var/rook
|
||||
var/rook_target_x
|
||||
|
||||
if(to_x > from_x)
|
||||
rook_x = GRID_SIZE
|
||||
rook = current_board[row][rook_x]
|
||||
rook_target_x = to_x - 1
|
||||
else
|
||||
rook_x = 1
|
||||
rook = current_board[row][rook_x]
|
||||
rook_target_x = to_x + 1
|
||||
|
||||
current_board[row][to_x] = king
|
||||
current_board[row][from_x] = null
|
||||
|
||||
current_board[row][rook_target_x] = rook
|
||||
current_board[row][rook_x] = null
|
||||
|
||||
if(active_color == "w")
|
||||
game_flags |= GAME_FLAG_CASTLING_USED_PONE
|
||||
else
|
||||
game_flags |= GAME_FLAG_CASTLING_USED_PTWO
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/chess/proc/update_valid_moves()
|
||||
valid_moves.Cut()
|
||||
if(length(selected_figure))
|
||||
var/x = selected_figure[1]
|
||||
var/y = selected_figure[2]
|
||||
valid_moves = generate_valid_moves(x, y)
|
||||
|
||||
/datum/board_game/chess/proc/square_under_attack(opponent_color, x, y)
|
||||
for(var/row = 1; row <= GRID_SIZE; row++)
|
||||
for(var/col = 1; col <= GRID_SIZE; col++)
|
||||
var/piece = current_board[row][col]
|
||||
if(!piece)
|
||||
continue
|
||||
|
||||
if(piece[1] != opponent_color)
|
||||
continue
|
||||
|
||||
var/dx = x - col
|
||||
var/dy = y - row
|
||||
|
||||
switch(piece[2])
|
||||
if("P")
|
||||
var/dir = (piece[1] == "w" ? -1 : 1)
|
||||
if(dy == dir && abs(dx) == 1)
|
||||
return TRUE
|
||||
if("N")
|
||||
if(abs(dx) == 2 && abs(dy) == 1 || abs(dx) == 1 && abs(dy) == 2)
|
||||
return TRUE
|
||||
if("B")
|
||||
if(abs(dx) == abs(dy) && path_clear(col, row, x, y))
|
||||
return TRUE
|
||||
if("R")
|
||||
if((dx == 0 || dy == 0) && path_clear(col, row, x, y))
|
||||
return TRUE
|
||||
if("Q")
|
||||
if((dx == 0 || dy == 0 || abs(dx) == abs(dy)) && path_clear(col, row, x, y))
|
||||
return TRUE
|
||||
if("K")
|
||||
if(abs(dx) <= 1 && abs(dy) <= 1)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/chess/proc/path_clear(from_x, from_y, to_x, to_y)
|
||||
var/dx = to_x - from_x
|
||||
var/dy = to_y - from_y
|
||||
var/step_x = dx == 0 ? 0 : (dx / abs(dx))
|
||||
var/step_y = dy == 0 ? 0 : (dy / abs(dy))
|
||||
var/x = from_x + step_x
|
||||
var/y = from_y + step_y
|
||||
|
||||
while(x != to_x || y != to_y)
|
||||
if(!field_check(x, y))
|
||||
return FALSE
|
||||
if(current_board[y][x] != null)
|
||||
return FALSE
|
||||
x += step_x
|
||||
y += step_y
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/chess/proc/validate_victory(active_color)
|
||||
var/opponent_color = active_color == "w" ? "b" : "w"
|
||||
|
||||
var/king_pos = locate_king(opponent_color)
|
||||
if(!king_pos)
|
||||
var/mob/winning_player = (active_color == "w" ? player_two?.resolve() : player_one?.resolve())
|
||||
winner = winning_player?.name
|
||||
game_state = GAME_OVER
|
||||
return
|
||||
|
||||
var/in_check = square_under_attack(active_color, king_pos[1], king_pos[2])
|
||||
|
||||
if(in_check)
|
||||
game_flags |= (opponent_color == "w" ? GAME_FLAG_CHECK_PONE : GAME_FLAG_CHECK_PTWO)
|
||||
else
|
||||
game_flags &= ~(opponent_color == "w" ? GAME_FLAG_CHECK_PONE : GAME_FLAG_CHECK_PTWO)
|
||||
|
||||
var/no_moves = TRUE
|
||||
for(var/row = 1 to GRID_SIZE)
|
||||
for(var/col = 1 to GRID_SIZE)
|
||||
var/piece = current_board[row][col]
|
||||
if(!piece)
|
||||
continue
|
||||
if(piece[1] != opponent_color)
|
||||
continue
|
||||
var/list/moves = generate_valid_moves(col, row)
|
||||
if(length(moves) > 0)
|
||||
no_moves = FALSE
|
||||
|
||||
if(in_check && no_moves)
|
||||
var/mob/winning_player = active_color == "w" ? player_one?.resolve() : player_two?.resolve()
|
||||
winner = winning_player?.name
|
||||
game_state = GAME_OVER
|
||||
return
|
||||
if(!in_check && no_moves)
|
||||
winner = null
|
||||
game_state = GAME_OVER_DRAW
|
||||
|
||||
/datum/board_game/chess/proc/locate_king(color)
|
||||
var/king_symbol = color + "K"
|
||||
for(var/row = 1 to GRID_SIZE)
|
||||
for(var/col = 1 to GRID_SIZE)
|
||||
if(current_board[row][col] == king_symbol)
|
||||
return list(col, row)
|
||||
return null
|
||||
|
||||
/datum/board_game/chess/proc/generate_valid_moves(x, y)
|
||||
var/piece = current_board[y][x]
|
||||
var/moves = list()
|
||||
if(!piece)
|
||||
return moves
|
||||
|
||||
for(var/row = 1 to GRID_SIZE)
|
||||
for(var/col = 1 to GRID_SIZE)
|
||||
if(row == y && col == x)
|
||||
continue
|
||||
if(can_move_piece(x, y, col, row))
|
||||
UNTYPED_LIST_ADD(moves, list(col, row))
|
||||
return moves
|
||||
|
||||
/datum/board_game/chess/proc/can_move_piece(from_x, from_y, to_x, to_y)
|
||||
var/piece = current_board[from_y][from_x]
|
||||
if(!piece)
|
||||
return FALSE
|
||||
|
||||
var/dx = to_x - from_x
|
||||
var/dy = to_y - from_y
|
||||
var/abs_dx = abs(dx)
|
||||
var/abs_dy = abs(dy)
|
||||
|
||||
if(dx == 0 && dy == 0)
|
||||
return FALSE
|
||||
|
||||
var/color = piece[1]
|
||||
var/target = current_board[to_y][to_x]
|
||||
if(target && target[1] == color)
|
||||
return FALSE
|
||||
|
||||
var/can_move = FALSE
|
||||
|
||||
switch(piece[2])
|
||||
if("P")
|
||||
var/dir = (color == "w" ? -1 : 1)
|
||||
if(dx == 0 && dy == dir && !target)
|
||||
can_move = TRUE
|
||||
else if(dx == 0 && dy == 2*dir && !target && current_board[from_y + dir][from_x] == null && ((color == "w" && from_y == 7) || (color == "b" && from_y == 2)))
|
||||
can_move = TRUE
|
||||
else if(abs_dx == 1 && dy == dir && target)
|
||||
can_move = TRUE
|
||||
else if(abs_dx == 1 && dy == dir && !target && length(last_double_pawn_move))
|
||||
if(last_double_pawn_move[1] == to_x && last_double_pawn_move[2] == from_y && last_double_pawn_move[3] == (color == "w" ? "b" : "w"))
|
||||
can_move = TRUE
|
||||
if("N")
|
||||
if((abs_dx == 2 && abs_dy == 1) || (abs_dx == 1 && abs_dy == 2))
|
||||
can_move = TRUE
|
||||
if("B")
|
||||
if(abs_dx == abs_dy && path_clear(from_x, from_y, to_x, to_y))
|
||||
can_move = TRUE
|
||||
if("R")
|
||||
if((dx == 0 || dy == 0) && path_clear(from_x, from_y, to_x, to_y))
|
||||
can_move = TRUE
|
||||
if("Q")
|
||||
if((dx == 0 || dy == 0 || abs_dx == abs_dy) && path_clear(from_x, from_y, to_x, to_y))
|
||||
can_move = TRUE
|
||||
if("K")
|
||||
if(abs_dx <= 1 && abs_dy <= 1)
|
||||
can_move = TRUE
|
||||
else if(abs_dx == 2 && dy == 0)
|
||||
can_move = handle_castling(color, from_x, from_y, to_x, to_y)
|
||||
|
||||
if(!can_move)
|
||||
return FALSE
|
||||
|
||||
var/orig_piece = current_board[to_y][to_x]
|
||||
current_board[to_y][to_x] = piece
|
||||
current_board[from_y][from_x] = null
|
||||
|
||||
var/king_pos = locate_king(color)
|
||||
if(!king_pos)
|
||||
current_board[from_y][from_x] = piece
|
||||
current_board[to_y][to_x] = orig_piece
|
||||
return FALSE
|
||||
|
||||
var/in_check = square_under_attack(color == "w" ? "b" : "w", king_pos[1], king_pos[2])
|
||||
|
||||
current_board[from_y][from_x] = piece
|
||||
current_board[to_y][to_x] = orig_piece
|
||||
|
||||
return !in_check
|
||||
|
||||
/datum/board_game/chess/proc/can_en_passant(from_x, from_y, to_x, to_y)
|
||||
if(!last_double_pawn_move)
|
||||
return FALSE
|
||||
return last_double_pawn_move[1] == to_x && last_double_pawn_move[2] == from_y && last_double_pawn_move[3] == (current_board[from_y][from_x][1] == "w" ? "b" : "w")
|
||||
|
||||
/datum/board_game/chess/proc/field_check(new_x_location, new_y_location)
|
||||
if(new_x_location < 1 || new_x_location > GRID_SIZE)
|
||||
return FALSE
|
||||
if(new_y_location < 1 || new_y_location > GRID_SIZE)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/chess/proc/validate_coords(list/params)
|
||||
var/x_loc = text2num(params["loc_x"])
|
||||
var/y_loc = text2num(params["loc_y"])
|
||||
|
||||
if(!isnum(x_loc) || !isnum(y_loc))
|
||||
return null
|
||||
|
||||
if(!field_check(x_loc, y_loc))
|
||||
return null
|
||||
|
||||
return list(x_loc, y_loc)
|
||||
|
||||
/datum/board_game/chess/proc/get_defaultboard()
|
||||
var/list/deep_copy = list()
|
||||
for(var/list/row in default_board)
|
||||
UNTYPED_LIST_ADD(deep_copy, row.Copy())
|
||||
return deep_copy
|
||||
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GAME_PLAYER_TWO
|
||||
#undef GAME_OVER
|
||||
#undef GAME_OVER_DRAW
|
||||
#undef GRID_SIZE
|
||||
#undef GAME_FLAG_CHECK_PONE
|
||||
#undef GAME_FLAG_CHECK_PTWO
|
||||
#undef GAME_FLAG_CASTLING_USED_PONE
|
||||
#undef GAME_FLAG_CASTLING_USED_PTWO
|
||||
|
||||
#undef GAME_ACTION_NONE
|
||||
#undef GAME_ACTION_SELECT
|
||||
#undef GAME_ACTION_END_TURN
|
||||
@@ -0,0 +1,330 @@
|
||||
#define GAME_PLAYER_ONE 1
|
||||
#define GAME_PLAYER_TWO 2
|
||||
#define GAME_OVER 3
|
||||
#define GAME_OVER_DRAW 4
|
||||
|
||||
/obj/structure/casino_table/board_game/four_row
|
||||
name = GAME_FOUR_ROW
|
||||
desc = "A game where two players need to connect their chips in a row."
|
||||
icon_state = "gamble_four"
|
||||
game_ui = /datum/board_game/four_row
|
||||
|
||||
|
||||
/datum/board_game/four_row
|
||||
name = GAME_FOUR_ROW
|
||||
table_icon = "gamble_four"
|
||||
var/datum/weakref/player_one
|
||||
var/datum/weakref/player_two
|
||||
var/list/placed_chips_pone = list()
|
||||
var/list/placed_chips_ptwo = list()
|
||||
var/list/winning_tiles = list()
|
||||
var/grid_x_size = 7
|
||||
var/grid_y_size = 6
|
||||
var/win_count = 4
|
||||
var/player_one_color = "yellow"
|
||||
var/player_two_color = "red"
|
||||
var/winner
|
||||
var/static/list/possible_colors = list("red", "yellow", "green", "orange", "blue", "cyan")
|
||||
|
||||
/datum/board_game/four_row/Destroy(force)
|
||||
player_one = null
|
||||
player_two = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/four_row/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "FourInARow", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/four_row/tgui_static_data(mob/user)
|
||||
return list(
|
||||
"colors" = possible_colors
|
||||
)
|
||||
|
||||
/datum/board_game/four_row/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
|
||||
return list(
|
||||
"player_one" = player_one_mob,
|
||||
"player_two" = player_two_mob,
|
||||
"placed_chips_pone" = placed_chips_pone,
|
||||
"placed_chips_ptwo" = placed_chips_ptwo,
|
||||
"game_state" = game_state,
|
||||
"grid_x_size" = grid_x_size,
|
||||
"grid_y_size" = grid_y_size,
|
||||
"player_one_color" = player_one_color,
|
||||
"player_two_color" = player_two_color,
|
||||
"win_count" = win_count,
|
||||
"winner" = winner,
|
||||
"has_won" = winner == ui.user.name,
|
||||
"winning_tiles" = winning_tiles,
|
||||
)
|
||||
|
||||
/datum/board_game/four_row/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_player_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() == ui.user)
|
||||
player_one = null
|
||||
return TRUE
|
||||
player_one = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("be_player_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() == ui.user)
|
||||
player_two = null
|
||||
return TRUE
|
||||
player_two = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("swap_players")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
if("set_color_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() != ui.user)
|
||||
return FALSE
|
||||
var/new_color = params["color"]
|
||||
if(new_color == player_two_color)
|
||||
return FALSE
|
||||
if(!(new_color in possible_colors))
|
||||
return FALSE
|
||||
player_one_color = new_color
|
||||
return TRUE
|
||||
if("set_color_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() != ui.user)
|
||||
return FALSE
|
||||
var/new_color = params["color"]
|
||||
if(new_color == player_one_color)
|
||||
return FALSE
|
||||
if(!(new_color in possible_colors))
|
||||
return FALSE
|
||||
player_two_color = new_color
|
||||
return TRUE
|
||||
if("change_size")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
var/new_size = text2num(params["size"])
|
||||
return set_new_size(new_size)
|
||||
if("change_win")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
return change_win_count(text2num(params["count"]))
|
||||
if("clear_game")
|
||||
if(game_state == GAME_SETUP)
|
||||
return FALSE
|
||||
reset(TRUE)
|
||||
return TRUE
|
||||
if("start_game")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
game_state = GAME_PLAYER_ONE
|
||||
return TRUE
|
||||
if("play_again")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
return TRUE
|
||||
if("play_again_swapped")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
return TRUE
|
||||
if("game_action")
|
||||
if(ui.user == player_one?.resolve() && game_state == GAME_PLAYER_ONE)
|
||||
if(player_actions(params["action"], params["data"], ui.user))
|
||||
if(game_state < GAME_OVER)
|
||||
game_state = GAME_PLAYER_TWO
|
||||
return TRUE
|
||||
if(ui.user == player_two?.resolve() && game_state == GAME_PLAYER_TWO)
|
||||
if(player_actions(params["action"], params["data"], ui.user))
|
||||
if(game_state < GAME_OVER)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/four_row/proc/change_win_count(new_count)
|
||||
if(!isnum(new_count))
|
||||
return FALSE
|
||||
if(new_count < 4 || new_count > 5)
|
||||
return FALSE
|
||||
win_count = new_count
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/four_row/proc/reset(full)
|
||||
placed_chips_pone.Cut()
|
||||
placed_chips_ptwo.Cut()
|
||||
winning_tiles.Cut()
|
||||
winner = null
|
||||
if(full)
|
||||
game_state = GAME_SETUP
|
||||
player_one = null
|
||||
player_two = null
|
||||
else
|
||||
game_state = GAME_PLAYER_ONE
|
||||
|
||||
/datum/board_game/four_row/proc/player_actions(action, list/params, mob/user)
|
||||
switch(action)
|
||||
if("place_chip")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
var/x_loc = validated_data[2]
|
||||
|
||||
var/target_y = 0
|
||||
for(var/y = grid_y_size; y >= 1; y--)
|
||||
var/key = "[x_loc],[y]"
|
||||
if(!placed_chips_pone[key] && !placed_chips_ptwo[key])
|
||||
target_y = y
|
||||
break
|
||||
|
||||
if(target_y == 0)
|
||||
return FALSE
|
||||
|
||||
var/key = "[x_loc],[target_y]"
|
||||
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
placed_chips_pone[key] = TRUE
|
||||
else
|
||||
placed_chips_ptwo[key] = TRUE
|
||||
|
||||
validate_victory(x_loc, target_y, user.name)
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/board_game/four_row/proc/has_chip(list/player_list, x, y)
|
||||
return player_list["[x],[y]"]
|
||||
|
||||
/datum/board_game/four_row/proc/collect_direction(list/player_list, start_x, start_y, dx, dy)
|
||||
var/list/tiles = list()
|
||||
var/x = start_x
|
||||
var/y = start_y
|
||||
|
||||
while(field_check(x, y) && has_chip(player_list, x, y))
|
||||
tiles += "[x],[y]"
|
||||
x += dx
|
||||
y += dy
|
||||
|
||||
return tiles
|
||||
|
||||
/datum/board_game/four_row/proc/validate_victory(x, y, user_name)
|
||||
var/list/current_list
|
||||
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
current_list = placed_chips_pone
|
||||
else
|
||||
current_list = placed_chips_ptwo
|
||||
|
||||
var/list/h1 = collect_direction(current_list, x, y, 1, 0)
|
||||
var/list/h2 = collect_direction(current_list, x, y, -1, 0)
|
||||
var/list/horizontal = h1 + h2
|
||||
horizontal -= "[x],[y]"
|
||||
|
||||
if(length(horizontal) >= win_count)
|
||||
winning_tiles = horizontal
|
||||
game_state = GAME_OVER
|
||||
winner = user_name
|
||||
return
|
||||
|
||||
var/list/v1 = collect_direction(current_list, x, y, 0, 1)
|
||||
var/list/v2 = collect_direction(current_list, x, y, 0, -1)
|
||||
var/list/vertical = v1 + v2
|
||||
vertical -= "[x],[y]"
|
||||
|
||||
if(length(vertical) >= win_count)
|
||||
winning_tiles = vertical
|
||||
game_state = GAME_OVER
|
||||
winner = user_name
|
||||
return
|
||||
|
||||
var/list/d1 = collect_direction(current_list, x, y, 1, 1)
|
||||
var/list/d2 = collect_direction(current_list, x, y, -1, -1)
|
||||
var/list/diag1 = d1 + d2
|
||||
diag1 -= "[x],[y]"
|
||||
|
||||
if(length(diag1) >= win_count)
|
||||
winning_tiles = diag1
|
||||
game_state = GAME_OVER
|
||||
winner = user_name
|
||||
return
|
||||
|
||||
var/list/d3 = collect_direction(current_list, x, y, 1, -1)
|
||||
var/list/d4 = collect_direction(current_list, x, y, -1, 1)
|
||||
var/list/diag2 = d3 + d4
|
||||
diag2 -= "[x],[y]"
|
||||
|
||||
if(length(diag2) >= win_count)
|
||||
winning_tiles = diag2
|
||||
game_state = GAME_OVER
|
||||
winner = user_name
|
||||
return
|
||||
check_draw()
|
||||
|
||||
/datum/board_game/four_row/proc/check_draw()
|
||||
for(var/x = 1 to grid_x_size)
|
||||
for(var/y = 1 to grid_y_size)
|
||||
var/key = "[x],[y]"
|
||||
if(!placed_chips_pone[key] && !placed_chips_ptwo[key])
|
||||
return FALSE
|
||||
|
||||
winner = null
|
||||
game_state = GAME_OVER_DRAW
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/four_row/proc/set_new_size(new_size)
|
||||
if(!isnum(new_size))
|
||||
return FALSE
|
||||
if(new_size < 5 || new_size > 10)
|
||||
return FALSE
|
||||
grid_x_size = new_size
|
||||
grid_y_size = new_size - 1
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/four_row/proc/field_check(new_x_location, new_y_location)
|
||||
if(new_x_location < 1 || new_x_location > grid_x_size)
|
||||
return FALSE
|
||||
if(new_y_location < 1 || new_y_location > grid_y_size)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/four_row/proc/validate_coords(list/params)
|
||||
var/x_loc = text2num(params["loc_x"])
|
||||
var/y_loc = text2num(params["loc_y"])
|
||||
|
||||
if(!isnum(x_loc) || !isnum(y_loc))
|
||||
return null
|
||||
|
||||
if(!field_check(x_loc, y_loc))
|
||||
return null
|
||||
|
||||
var/key = "[x_loc],[y_loc]"
|
||||
return list(key, x_loc, y_loc)
|
||||
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GAME_PLAYER_TWO
|
||||
#undef GAME_OVER
|
||||
#undef GAME_OVER_DRAW
|
||||
@@ -0,0 +1,421 @@
|
||||
#define GAME_PLAYER_ONE 1
|
||||
#define GAME_PLAYER_TWO 2
|
||||
#define GAME_OVER 3
|
||||
#define GAME_OVER_DRAW 4
|
||||
#define NODE_COUNT 24
|
||||
|
||||
#define GAME_ACTION_NONE 0
|
||||
#define GAME_ACTION_SELECT 1
|
||||
#define GAME_ACTION_END_TURN 2
|
||||
|
||||
#define GAME_PHASE_PLACING 0
|
||||
#define GAME_PHASE_MOVING 1
|
||||
#define GAME_PHASE_REMOVING 2
|
||||
#define GAME_PHASE_NONE 3
|
||||
|
||||
/obj/structure/casino_table/board_game/nine_mens
|
||||
name = GAME_NINE_MENS_MORRIS
|
||||
desc = "A classic nine_mens game."
|
||||
icon_state = "gamble_ninemen"
|
||||
game_ui = /datum/board_game/nine_mens
|
||||
|
||||
/datum/board_game/nine_mens
|
||||
name = GAME_NINE_MENS_MORRIS
|
||||
table_icon = "gamble_ninemen"
|
||||
var/datum/weakref/player_one
|
||||
var/datum/weakref/player_two
|
||||
var/player_one_time = 0
|
||||
var/player_two_time = 0
|
||||
var/list/current_board = list(
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
null, null, null
|
||||
)
|
||||
var/static/list/adjacent = list(
|
||||
list(2, 10),
|
||||
list(1, 3, 5),
|
||||
list(2, 15),
|
||||
list(5, 11),
|
||||
list(2, 4, 6, 8),
|
||||
list(5, 14),
|
||||
list(8, 12),
|
||||
list(5, 7, 9),
|
||||
list(8, 13),
|
||||
list(1, 11, 22),
|
||||
list(4, 10, 12, 19),
|
||||
list(7, 11, 16),
|
||||
list(9, 14, 18),
|
||||
list(6, 13, 15, 21),
|
||||
list(3, 14, 24),
|
||||
list(12, 17),
|
||||
list(16, 18, 20),
|
||||
list(13, 17),
|
||||
list(11, 20),
|
||||
list(17, 19, 21, 23),
|
||||
list(14, 20),
|
||||
list(10, 23),
|
||||
list(20, 22, 24),
|
||||
list(15, 23)
|
||||
)
|
||||
var/static/list/mills = list(
|
||||
list(1,2,3),
|
||||
list(4,5,6),
|
||||
list(7,8,9),
|
||||
list(10,11,12),
|
||||
list(13,14,15),
|
||||
list(16,17,18),
|
||||
list(19,20,21),
|
||||
list(22,23,24),
|
||||
|
||||
list(1,10,22),
|
||||
list(4,11,19),
|
||||
list(7,12,16),
|
||||
list(2,5,8),
|
||||
list(17,20,23),
|
||||
list(9,13,18),
|
||||
list(6,14,21),
|
||||
list(3,15,24)
|
||||
)
|
||||
var/list/valid_moves = list()
|
||||
var/list/valid_removes = list()
|
||||
var/selected_node
|
||||
var/turn_start_time = 0
|
||||
var/pone_pieces = 9
|
||||
var/ptwo_pieces = 9
|
||||
var/winner
|
||||
var/phase = GAME_PHASE_PLACING
|
||||
|
||||
/datum/board_game/nine_mens/Destroy(force)
|
||||
player_one = null
|
||||
player_two = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/nine_mens/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "NineMen", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/nine_mens/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
|
||||
return list(
|
||||
"player_one" = player_one_mob,
|
||||
"player_two" = player_two_mob,
|
||||
"player_one_time" = player_one_time + (game_state == GAME_PLAYER_ONE ? world.time - turn_start_time : 0),
|
||||
"player_two_time" = player_two_time + (game_state == GAME_PLAYER_TWO ? world.time - turn_start_time : 0),
|
||||
"current_board" = current_board,
|
||||
"selected_node" = selected_node,
|
||||
"valid_moves" = valid_moves,
|
||||
"valid_removes" = valid_removes,
|
||||
"game_state" = game_state,
|
||||
"winner" = winner,
|
||||
"has_won" = winner == ui.user.name,
|
||||
"phase" = phase,
|
||||
"pone_pieces" = pone_pieces,
|
||||
"ptwo_pieces" = ptwo_pieces
|
||||
)
|
||||
|
||||
/datum/board_game/nine_mens/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_player_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() == ui.user)
|
||||
player_one = null
|
||||
return TRUE
|
||||
player_one = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("be_player_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() == ui.user)
|
||||
player_two = null
|
||||
return TRUE
|
||||
player_two = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("swap_players")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
if("clear_game")
|
||||
if(game_state == GAME_SETUP)
|
||||
return FALSE
|
||||
reset(TRUE)
|
||||
return TRUE
|
||||
if("start_game")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("play_again_swapped")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if("game_action")
|
||||
if(ui.user == player_one?.resolve() && game_state == GAME_PLAYER_ONE)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "w")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_TWO
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
if(ui.user == player_two?.resolve() && game_state == GAME_PLAYER_TWO)
|
||||
var/game_action = player_actions(params["action"], params["data"], ui.user, "b")
|
||||
if(game_action)
|
||||
if(game_state < GAME_OVER && game_action == GAME_ACTION_END_TURN)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
turn_start_time = world.time
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/nine_mens/proc/reset(full)
|
||||
winner = null
|
||||
player_one_time = 0
|
||||
player_two_time = 0
|
||||
selected_node = null
|
||||
valid_moves.Cut()
|
||||
valid_removes.Cut()
|
||||
pone_pieces = 9
|
||||
ptwo_pieces = 9
|
||||
phase = GAME_PHASE_PLACING
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
current_board[i] = null
|
||||
if(full)
|
||||
game_state = GAME_SETUP
|
||||
player_one = null
|
||||
player_two = null
|
||||
else
|
||||
game_state = GAME_PLAYER_ONE
|
||||
|
||||
/datum/board_game/nine_mens/proc/player_actions(action, list/params, mob/user, active_color)
|
||||
switch(action)
|
||||
if("select_figure")
|
||||
var/node = validate_node(params)
|
||||
if(!node)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
var/piece = current_board[node]
|
||||
if(!piece || piece[1] != active_color)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
selected_node = node
|
||||
update_valid_moves()
|
||||
return GAME_ACTION_SELECT
|
||||
|
||||
if("move_figure")
|
||||
var/target_node = validate_node(params)
|
||||
if(!target_node)
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
switch(phase)
|
||||
if(GAME_PHASE_PLACING)
|
||||
if(current_board[target_node])
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
current_board[target_node] = active_color
|
||||
|
||||
if(active_color == "w")
|
||||
if(pone_pieces > 0)
|
||||
pone_pieces--
|
||||
else
|
||||
if(ptwo_pieces > 0)
|
||||
ptwo_pieces--
|
||||
|
||||
if(check_for_mill(target_node, active_color))
|
||||
phase = GAME_PHASE_REMOVING
|
||||
update_valid_removals(active_color == "w" ? "b" : "w")
|
||||
else
|
||||
if((ptwo_pieces && active_color == "w") || (pone_pieces && active_color == "b"))
|
||||
phase = GAME_PHASE_PLACING
|
||||
else
|
||||
phase = GAME_PHASE_MOVING
|
||||
return phase == GAME_PHASE_REMOVING ? GAME_ACTION_SELECT : GAME_ACTION_END_TURN
|
||||
|
||||
if(GAME_PHASE_MOVING)
|
||||
if(!selected_node)
|
||||
return GAME_ACTION_NONE
|
||||
if(!valid_move(active_color, selected_node, target_node))
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
current_board[target_node] = current_board[selected_node]
|
||||
current_board[selected_node] = null
|
||||
selected_node = null
|
||||
valid_moves.Cut()
|
||||
validate_victory(active_color)
|
||||
if(game_state < GAME_OVER)
|
||||
if(check_for_mill(target_node, active_color))
|
||||
phase = GAME_PHASE_REMOVING
|
||||
update_valid_removals(active_color == "w" ? "b" : "w")
|
||||
else
|
||||
phase = GAME_PHASE_MOVING
|
||||
else
|
||||
phase = GAME_PHASE_NONE
|
||||
|
||||
return phase == GAME_PHASE_REMOVING ? GAME_ACTION_SELECT : GAME_ACTION_END_TURN
|
||||
|
||||
if(GAME_PHASE_REMOVING)
|
||||
if(!valid_remove(target_node))
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
current_board[target_node] = null
|
||||
|
||||
valid_removes.Cut()
|
||||
validate_victory(active_color)
|
||||
if(game_state < GAME_OVER)
|
||||
if((ptwo_pieces && active_color == "w") || (pone_pieces && active_color == "b"))
|
||||
phase = GAME_PHASE_PLACING
|
||||
else
|
||||
phase = GAME_PHASE_MOVING
|
||||
else
|
||||
phase = GAME_PHASE_NONE
|
||||
|
||||
return GAME_ACTION_END_TURN
|
||||
|
||||
return GAME_ACTION_NONE
|
||||
|
||||
/datum/board_game/nine_mens/proc/update_valid_moves()
|
||||
valid_moves.Cut()
|
||||
if(selected_node)
|
||||
valid_moves = generate_valid_moves(selected_node)
|
||||
|
||||
/datum/board_game/nine_mens/proc/update_valid_removals(opponent_color)
|
||||
valid_removes.Cut()
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
if(current_board[i] && current_board[i][1] == opponent_color)
|
||||
if(!check_for_mill(i, opponent_color))
|
||||
valid_removes += i
|
||||
if(!length(valid_removes))
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
if(current_board[i] && current_board[i][1] == opponent_color)
|
||||
valid_removes += i
|
||||
|
||||
/datum/board_game/nine_mens/proc/valid_remove(picked_node)
|
||||
if(!picked_node || !current_board[picked_node] || !(picked_node in valid_removes))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/nine_mens/proc/check_for_mill(node, color)
|
||||
for(var/mill in mills)
|
||||
if(node in mill)
|
||||
var/mill_complete = TRUE
|
||||
for(var/mill_node in mill)
|
||||
if(!current_board[mill_node] || current_board[mill_node][1] != color)
|
||||
mill_complete = FALSE
|
||||
break
|
||||
if(mill_complete)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/nine_mens/proc/valid_move(active_color, from_node, to_node)
|
||||
if(current_board[to_node])
|
||||
return FALSE
|
||||
|
||||
if(count_pieces(active_color) == 3)
|
||||
return TRUE
|
||||
|
||||
for(var/adj in adjacent[from_node])
|
||||
if(adj == to_node)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/nine_mens/proc/validate_victory(active_color)
|
||||
var/opponent = (active_color == "w") ? "b" : "w"
|
||||
|
||||
if(count_pieces(opponent) < 3 || !has_moves(opponent))
|
||||
var/mob/winning_player = (active_color == "w" ? player_one?.resolve() : player_two?.resolve())
|
||||
winner = winning_player?.name
|
||||
game_state = GAME_OVER
|
||||
|
||||
/datum/board_game/nine_mens/proc/generate_valid_moves(node)
|
||||
var/list/moves = list()
|
||||
|
||||
if(!current_board[node])
|
||||
return moves
|
||||
|
||||
var/color = current_board[node][1]
|
||||
|
||||
if(count_pieces(color) == 3)
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
if(!current_board[i])
|
||||
moves += i
|
||||
return moves
|
||||
|
||||
for(var/adj in adjacent[node])
|
||||
if(!current_board[adj])
|
||||
moves += adj
|
||||
|
||||
return moves
|
||||
|
||||
/datum/board_game/nine_mens/proc/count_pieces(color)
|
||||
var/count = color == "w" ? pone_pieces : ptwo_pieces
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
if(current_board[i] && current_board[i][1] == color)
|
||||
count++
|
||||
return count
|
||||
|
||||
/datum/board_game/nine_mens/proc/has_moves(color)
|
||||
for(var/i = 1 to NODE_COUNT)
|
||||
if(current_board[i] && current_board[i][1] == color)
|
||||
if(length(generate_valid_moves(i)))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/nine_mens/proc/validate_node(list/params)
|
||||
var/node = text2num(params["node_number"])
|
||||
|
||||
if(!isnum(node))
|
||||
return null
|
||||
|
||||
if(node < 1 || node > 24)
|
||||
return null
|
||||
|
||||
return node
|
||||
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GAME_PLAYER_TWO
|
||||
#undef GAME_OVER
|
||||
#undef GAME_OVER_DRAW
|
||||
#undef NODE_COUNT
|
||||
|
||||
#undef GAME_ACTION_NONE
|
||||
#undef GAME_ACTION_SELECT
|
||||
#undef GAME_ACTION_END_TURN
|
||||
|
||||
#undef GAME_PHASE_PLACING
|
||||
#undef GAME_PHASE_MOVING
|
||||
#undef GAME_PHASE_REMOVING
|
||||
#undef GAME_PHASE_NONE
|
||||
@@ -0,0 +1,72 @@
|
||||
/obj/structure/casino_table/board_game/rpg_dice
|
||||
name = GAME_RGP_DICE
|
||||
desc = "A set of dice to roll with your friends."
|
||||
icon_state = "gamble_dice"
|
||||
game_ui = /datum/board_game/rpg_dice
|
||||
|
||||
/datum/board_game/rpg_dice
|
||||
name = GAME_RGP_DICE
|
||||
table_icon = "gamble_dice"
|
||||
var/list/last_rolls = list()
|
||||
|
||||
/datum/board_game/rpg_dice/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "RpgDice", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/rpg_dice/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
return list(
|
||||
"last_rolls" = last_rolls,
|
||||
)
|
||||
|
||||
/datum/board_game/rpg_dice/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("roll_dice")
|
||||
var/dice_size = params["dice_size"]
|
||||
if(!isnum(dice_size) || dice_size > 10000)
|
||||
return FALSE
|
||||
var/dice_count = params["dice_count"]
|
||||
if(!isnum(dice_count) || dice_count < 1 || dice_count > 10)
|
||||
dice_count = 1
|
||||
var/modifier = params["dice_mod"]
|
||||
var/list/results = list()
|
||||
var/sum = 0
|
||||
if(!isnum(modifier))
|
||||
modifier = 0
|
||||
var/apply_to_all = params["mod_all"]
|
||||
for(var/dice in 1 to dice_count)
|
||||
var/result = rand(1, dice_size)
|
||||
UNTYPED_LIST_ADD(results, list("result" = result, "state" = check_crit(1, dice_size, result)))
|
||||
sum += result
|
||||
sum += apply_to_all ? modifier * dice_count : modifier
|
||||
UNTYPED_LIST_ADD(last_rolls, list("player" = ui.user, "count" = dice_count, "size" = dice_size, "results" = results, "mod" = modifier, "apply_to_all" = apply_to_all, "sum" = sum))
|
||||
return TRUE
|
||||
/* Requires rust g dice
|
||||
if("custom_roll")
|
||||
var/input = params["custom"]
|
||||
if(length(input) > 200)
|
||||
to_chat(ui.user, span_warning("Your input was too long."))
|
||||
return FALSE
|
||||
var/static/regex/valid_dice_roll = regex(@"^(\d+d\d+(\s*[\+\-\*\/]\s*\d+)?(\s*[\+\-\*\/]\s*\d+d\d+(\s*[\+\-\*\/]\s*\d+)?)*)$")
|
||||
if(!findtext(input, valid_dice_roll))
|
||||
to_chat(ui.user, span_warning("Invalid input, please follow the common pattern e.g.: 2d6 + 3d10 + 5"))
|
||||
return FALSE
|
||||
var/result = rustg_roll_dice("[input]")
|
||||
UNTYPED_LIST_ADD(last_rolls, list("player" = ui.user, "count" = input, "sum" = result))
|
||||
return TRUE
|
||||
*/
|
||||
if("clear_history")
|
||||
last_rolls.Cut()
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/rpg_dice/proc/check_crit(low, max, result)
|
||||
if(result == low)
|
||||
return 1
|
||||
if(result == max)
|
||||
return 2
|
||||
return 0
|
||||
@@ -0,0 +1,436 @@
|
||||
#define GAME_PLACE_SHIPS 1
|
||||
#define GAME_PLAYER_ONE 2
|
||||
#define GAME_PLAYER_TWO 3
|
||||
#define GAME_OVER 4
|
||||
#define GRID_SIZE 10
|
||||
#define PLAYER_ONE_PLACED_SHIPS 0x1
|
||||
#define PLAYER_TWO_PLACED_SHIPS 0x2
|
||||
|
||||
/obj/structure/casino_table/board_game/space_battle
|
||||
name = GAME_SPACE_BATTLE
|
||||
desc = "A game with the goal to destroy the opponent's ships."
|
||||
icon_state = "gamble_space"
|
||||
game_ui = /datum/board_game/space_battle
|
||||
|
||||
/datum/board_game/space_battle
|
||||
name = GAME_SPACE_BATTLE
|
||||
table_icon = "gamble_space"
|
||||
var/datum/weakref/player_one
|
||||
var/datum/weakref/player_two
|
||||
var/list/ship_count_pone = list()
|
||||
var/list/ship_count_ptwo = list()
|
||||
var/list/shots_fired_pone = list()
|
||||
var/list/shots_fired_ptwo = list()
|
||||
var/list/ships_placed_pone = list()
|
||||
var/list/ships_placed_ptwo = list()
|
||||
var/list/destroyed_ships_pone = list()
|
||||
var/list/destroyed_ships_ptwo = list()
|
||||
var/static/list/total_ships = list(
|
||||
"Carrier" = 1,
|
||||
"Cruiser" = 2,
|
||||
"Corvette" = 3,
|
||||
"Figher" = 4,
|
||||
)
|
||||
var/static/list/ship_sizes = list(
|
||||
"Carrier" = 6,
|
||||
"Cruiser" = 4,
|
||||
"Corvette" = 3,
|
||||
"Figher" = 2,
|
||||
)
|
||||
var/winner
|
||||
var/ships_have_been_placed = NONE
|
||||
|
||||
/datum/board_game/space_battle/Destroy(force)
|
||||
player_one = null
|
||||
player_two = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/space_battle/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "SpaceBattle", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/space_battle/tgui_static_data(mob/user)
|
||||
return list(
|
||||
"ship_sizes" = ship_sizes,
|
||||
"total_ships" = total_ships
|
||||
)
|
||||
|
||||
/datum/board_game/space_battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
|
||||
var/list/visible_ships = list()
|
||||
if(ui.user == player_one_mob || game_state == GAME_OVER)
|
||||
visible_ships += ships_placed_pone
|
||||
if(ui.user == player_two_mob || game_state == GAME_OVER)
|
||||
visible_ships += ships_placed_ptwo
|
||||
|
||||
return list(
|
||||
"current_player" = ui.user,
|
||||
"player_one" = player_one_mob,
|
||||
"player_two" = player_two_mob,
|
||||
"all_placed" = ships_have_been_placed == (PLAYER_ONE_PLACED_SHIPS | PLAYER_TWO_PLACED_SHIPS),
|
||||
"shots_fired_pone" = shots_fired_pone,
|
||||
"shots_fired_ptwo" = shots_fired_ptwo,
|
||||
"destroyed_ships_pone" = destroyed_ships_pone,
|
||||
"destroyed_ships_ptwo" = destroyed_ships_ptwo,
|
||||
"visible_ships" = visible_ships,
|
||||
"ship_count_pone" = ship_count_pone,
|
||||
"ship_count_ptwo" = ship_count_ptwo,
|
||||
"game_state" = game_state,
|
||||
"winner" = winner,
|
||||
"has_won" = winner == ui.user.name
|
||||
)
|
||||
|
||||
/datum/board_game/space_battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_player_one")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_one?.resolve() == ui.user)
|
||||
player_one = null
|
||||
return TRUE
|
||||
player_one = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("be_player_two")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(player_two?.resolve() == ui.user)
|
||||
player_two = null
|
||||
return TRUE
|
||||
player_two = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("swap_players")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
if("clear_game")
|
||||
if(game_state == GAME_SETUP)
|
||||
return FALSE
|
||||
reset(TRUE)
|
||||
return TRUE
|
||||
if("prepare_game")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
if(!player_one_mob || !player_two_mob)
|
||||
return FALSE
|
||||
game_state = GAME_PLACE_SHIPS
|
||||
ship_count_pone = get_remaining_ships(1)
|
||||
ship_count_ptwo = get_remaining_ships(2)
|
||||
return TRUE
|
||||
if("start_game")
|
||||
if(game_state != GAME_PLACE_SHIPS)
|
||||
return FALSE
|
||||
if(!(ships_have_been_placed == (PLAYER_ONE_PLACED_SHIPS | PLAYER_TWO_PLACED_SHIPS)))
|
||||
return FALSE
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
if(!player_one_mob || !player_two_mob)
|
||||
return FALSE
|
||||
ship_count_pone = get_alive_ships(1)
|
||||
ship_count_ptwo = get_alive_ships(2)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
return TRUE
|
||||
if("play_again")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
return TRUE
|
||||
if("play_again_swapped")
|
||||
if(game_state < GAME_OVER)
|
||||
return FALSE
|
||||
if(!player_one?.resolve() || !player_two?.resolve())
|
||||
return FALSE
|
||||
reset()
|
||||
var/datum/weakref/temp_player = player_one
|
||||
player_one = player_two
|
||||
player_two = temp_player
|
||||
return TRUE
|
||||
if("place_ship")
|
||||
if(game_state != GAME_PLACE_SHIPS)
|
||||
return FALSE
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
if(!player_one_mob || !player_two_mob)
|
||||
return FALSE
|
||||
|
||||
var/list/ship_data = params["ship"]
|
||||
if(!ship_data["name"] || !ship_data["coords"])
|
||||
return FALSE
|
||||
|
||||
var/player = ship_data["player"]
|
||||
if(!player)
|
||||
return FALSE
|
||||
|
||||
if(player == 1 && ui.user != player_one_mob)
|
||||
return FALSE
|
||||
if(player == 2 && ui.user != player_two_mob)
|
||||
return FALSE
|
||||
|
||||
var/allowed = total_ships[ship_data["name"]]
|
||||
if(!allowed)
|
||||
return FALSE
|
||||
|
||||
var/list/current_ships
|
||||
if(player == 1)
|
||||
current_ships = ships_placed_pone
|
||||
else
|
||||
current_ships = ships_placed_ptwo
|
||||
|
||||
var/count = 0
|
||||
for(var/list/existing in current_ships)
|
||||
if(existing["name"] == ship_data["name"])
|
||||
count++
|
||||
|
||||
if(count >= allowed)
|
||||
return FALSE
|
||||
|
||||
var/list/new_coords = ship_data["coords"]
|
||||
for(var/list/new_coord in new_coords)
|
||||
if(!isnum(new_coord[1]) || !isnum(new_coord[2]))
|
||||
return FALSE
|
||||
if(!field_check(new_coord[1], new_coord[2]))
|
||||
return FALSE
|
||||
|
||||
for(var/list/existing_ship in current_ships)
|
||||
for(var/list/coord in existing_ship["coords"])
|
||||
for(var/list/new_coord in new_coords)
|
||||
if(coord[1] == new_coord[1] && coord[2] == new_coord[2])
|
||||
return FALSE
|
||||
|
||||
UNTYPED_LIST_ADD(current_ships, ship_data)
|
||||
if(player == 1)
|
||||
ship_count_pone = get_remaining_ships(1)
|
||||
else
|
||||
ship_count_ptwo = get_remaining_ships(2)
|
||||
|
||||
return TRUE
|
||||
if("remove_ship")
|
||||
if(game_state != GAME_PLACE_SHIPS)
|
||||
return FALSE
|
||||
var/mob/player_one_mob = player_one?.resolve()
|
||||
var/mob/player_two_mob = player_two?.resolve()
|
||||
if(!player_one_mob || !player_two_mob)
|
||||
return FALSE
|
||||
|
||||
var/player = params["player"]
|
||||
if(player == 1 && ui.user != player_one_mob)
|
||||
return FALSE
|
||||
if(player == 2 && ui.user != player_two_mob)
|
||||
return FALSE
|
||||
|
||||
var/list/ships
|
||||
if(player == 1)
|
||||
ships = ships_placed_pone
|
||||
else
|
||||
ships = ships_placed_ptwo
|
||||
|
||||
var/loc_x = params["loc_x"]
|
||||
var/loc_y = params["loc_y"]
|
||||
for(var/i in length(ships) to 1 step -1)
|
||||
var/list/ship = ships[i]
|
||||
for(var/list/coord in ship["coords"])
|
||||
if(coord[1] == loc_x && coord[2] == loc_y)
|
||||
ships.Cut(i, i + 1)
|
||||
if(player == 1)
|
||||
ship_count_pone = get_remaining_ships(1)
|
||||
else
|
||||
ship_count_ptwo = get_remaining_ships(2)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if("game_action")
|
||||
if(ui.user == player_one?.resolve() && game_state == GAME_PLAYER_ONE)
|
||||
if(params["data"]["player"] == 1)
|
||||
return FALSE
|
||||
if(player_actions(params["action"], params["data"], ui.user))
|
||||
if(game_state < GAME_OVER)
|
||||
game_state = GAME_PLAYER_TWO
|
||||
return TRUE
|
||||
if(ui.user == player_two?.resolve() && game_state == GAME_PLAYER_TWO)
|
||||
if(params["data"]["player"] == 2)
|
||||
return FALSE
|
||||
if(player_actions(params["action"], params["data"], ui.user))
|
||||
if(game_state < GAME_OVER)
|
||||
game_state = GAME_PLAYER_ONE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/space_battle/proc/reset(full)
|
||||
ship_count_pone.Cut()
|
||||
ship_count_ptwo.Cut()
|
||||
shots_fired_pone.Cut()
|
||||
shots_fired_ptwo.Cut()
|
||||
ships_placed_pone.Cut()
|
||||
ships_placed_ptwo.Cut()
|
||||
destroyed_ships_pone.Cut()
|
||||
destroyed_ships_ptwo.Cut()
|
||||
winner = null
|
||||
ships_have_been_placed = NONE
|
||||
if(full)
|
||||
game_state = GAME_SETUP
|
||||
player_one = null
|
||||
player_two = null
|
||||
else
|
||||
game_state = GAME_PLACE_SHIPS
|
||||
|
||||
/datum/board_game/space_battle/proc/player_actions(action, list/params, mob/user)
|
||||
switch(action)
|
||||
if("fire_shot")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
|
||||
var/key = validated_data[1]
|
||||
|
||||
var/list/opponent_ships
|
||||
var/list/current_shots
|
||||
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
current_shots = shots_fired_pone
|
||||
opponent_ships = ships_placed_ptwo
|
||||
else
|
||||
current_shots = shots_fired_ptwo
|
||||
opponent_ships = ships_placed_pone
|
||||
|
||||
if(!isnull(current_shots[key]))
|
||||
return FALSE
|
||||
|
||||
var/hit = 0
|
||||
for(var/list/ship in opponent_ships)
|
||||
for(var/coord in ship["coords"])
|
||||
if(coord[1] == validated_data[2] && coord[2] == validated_data[3])
|
||||
hit = 1
|
||||
break
|
||||
if(hit)
|
||||
break
|
||||
|
||||
current_shots[key] = hit
|
||||
|
||||
if(hit)
|
||||
validate_victory(opponent_ships, current_shots, user)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/space_battle/proc/validate_victory(list/opponent_ships, list/current_shots, mob/user)
|
||||
for(var/list/ship in opponent_ships)
|
||||
var/ship_destroyed = TRUE
|
||||
for(var/coord in ship["coords"])
|
||||
var/key = "[coord[1]],[coord[2]]"
|
||||
if(!current_shots[key])
|
||||
ship_destroyed = FALSE
|
||||
break
|
||||
|
||||
if(ship_destroyed)
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
if(!(destroyed_ships_pone.Find(ship)))
|
||||
UNTYPED_LIST_ADD(destroyed_ships_pone, ship)
|
||||
ship_count_pone = get_alive_ships(1)
|
||||
else
|
||||
if(!(destroyed_ships_ptwo.Find(ship)))
|
||||
UNTYPED_LIST_ADD(destroyed_ships_ptwo, ship)
|
||||
ship_count_ptwo = get_alive_ships(2)
|
||||
|
||||
for(var/list/ship in opponent_ships)
|
||||
var/key
|
||||
for(var/coord in ship["coords"])
|
||||
key = "[coord[1]],[coord[2]]"
|
||||
if(!current_shots[key])
|
||||
return
|
||||
|
||||
winner = user.name
|
||||
game_state = GAME_OVER
|
||||
|
||||
/datum/board_game/space_battle/proc/field_check(new_x_location, new_y_location)
|
||||
if(new_x_location < 1 || new_x_location > GRID_SIZE)
|
||||
return FALSE
|
||||
if(new_y_location < 1 || new_y_location > GRID_SIZE)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/space_battle/proc/validate_coords(list/params)
|
||||
var/x_loc = text2num(params["loc_x"])
|
||||
var/y_loc = text2num(params["loc_y"])
|
||||
|
||||
if(!isnum(x_loc) || !isnum(y_loc))
|
||||
return null
|
||||
|
||||
if(!field_check(x_loc, y_loc))
|
||||
return null
|
||||
|
||||
var/key = "[x_loc],[y_loc]"
|
||||
return list(key, x_loc, y_loc)
|
||||
|
||||
/datum/board_game/space_battle/proc/get_remaining_ships(player)
|
||||
var/list/remaining_ships = list()
|
||||
var/list/placed_ships
|
||||
var/ship_placed_flag
|
||||
|
||||
if(player == 1)
|
||||
placed_ships = ships_placed_pone
|
||||
ship_placed_flag = PLAYER_ONE_PLACED_SHIPS
|
||||
else if(player == 2)
|
||||
placed_ships = ships_placed_ptwo
|
||||
ship_placed_flag = PLAYER_TWO_PLACED_SHIPS
|
||||
else
|
||||
return remaining_ships
|
||||
|
||||
for(var/ship_name in ship_sizes)
|
||||
var/allowed_count = total_ships[ship_name]
|
||||
var/placed_count = 0
|
||||
|
||||
for(var/list/placed_ship in placed_ships)
|
||||
if(placed_ship["name"] == ship_name)
|
||||
placed_count++
|
||||
|
||||
var/remaining_count = allowed_count - placed_count
|
||||
if(remaining_count > 0)
|
||||
remaining_ships[ship_name] = remaining_count
|
||||
|
||||
if(length(remaining_ships))
|
||||
ships_have_been_placed &= ~ship_placed_flag
|
||||
else
|
||||
ships_have_been_placed |= ship_placed_flag
|
||||
|
||||
return remaining_ships
|
||||
|
||||
/datum/board_game/space_battle/proc/get_alive_ships(player)
|
||||
var/list/alive_ships = list()
|
||||
var/list/ships
|
||||
|
||||
if(player == 1)
|
||||
ships = ships_placed_pone
|
||||
else if(player == 2)
|
||||
ships = ships_placed_ptwo
|
||||
else
|
||||
return alive_ships
|
||||
|
||||
for(var/list/ship in ships)
|
||||
if(game_state == GAME_PLAYER_ONE && !(destroyed_ships_pone.Find(ship)))
|
||||
alive_ships[ship["name"]] = length(alive_ships) ? alive_ships[ship["name"]] + 1 : 1
|
||||
|
||||
if(game_state == GAME_PLAYER_TWO && !(destroyed_ships_ptwo.Find(ship)))
|
||||
alive_ships[ship["name"]] = length(alive_ships) ? alive_ships[ship["name"]] + 1 : 1
|
||||
|
||||
return alive_ships
|
||||
|
||||
#undef GAME_PLACE_SHIPS
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GAME_PLAYER_TWO
|
||||
#undef GAME_OVER
|
||||
#undef GRID_SIZE
|
||||
#undef PLAYER_ONE_PLACED_SHIPS
|
||||
#undef PLAYER_TWO_PLACED_SHIPS
|
||||
@@ -0,0 +1,51 @@
|
||||
#define GAME_PLAYER_ONE 1
|
||||
#define GRID_SIZE 3
|
||||
|
||||
/obj/structure/casino_table/board_game/tic_tac_toe
|
||||
name = GAME_TIC_TAC_TOE
|
||||
desc = "A small tic-tac-toe board."
|
||||
icon_state = "gamble_toe"
|
||||
game_ui = /datum/board_game/four_row/tic_tac_toe
|
||||
|
||||
/datum/board_game/four_row/tic_tac_toe
|
||||
name = GAME_TIC_TAC_TOE
|
||||
table_icon = "gamble_toe"
|
||||
grid_x_size = GRID_SIZE
|
||||
grid_y_size = GRID_SIZE
|
||||
win_count = GRID_SIZE
|
||||
|
||||
/datum/board_game/four_row/tic_tac_toe/tgui_static_data(mob/user)
|
||||
return list(
|
||||
"colors" = possible_colors - "blue"
|
||||
)
|
||||
|
||||
/datum/board_game/four_row/tic_tac_toe/player_actions(action, list/params, mob/user)
|
||||
switch(action)
|
||||
if("place_chip")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
|
||||
var/key = validated_data[1]
|
||||
if(placed_chips_pone[key] || placed_chips_ptwo[key])
|
||||
return FALSE
|
||||
|
||||
var/x_loc = validated_data[2]
|
||||
var/y_loc = validated_data[3]
|
||||
|
||||
if(game_state == GAME_PLAYER_ONE)
|
||||
placed_chips_pone[key] = TRUE
|
||||
else
|
||||
placed_chips_ptwo[key] = TRUE
|
||||
|
||||
validate_victory(x_loc, y_loc, user.name)
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/four_row/tic_tac_toe/set_new_size(new_size)
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/four_row/change_win_count(new_count)
|
||||
return FALSE
|
||||
|
||||
#undef GAME_PLAYER_ONE
|
||||
#undef GRID_SIZE
|
||||
@@ -0,0 +1,321 @@
|
||||
#define GAME_PLAYING 1
|
||||
#define GAME_LOST 2
|
||||
#define GAME_WON 3
|
||||
#define MAX_MINE_RATE 0.8
|
||||
|
||||
/obj/structure/casino_table/board_game/vore_sweeper
|
||||
name = GAME_SWEEPER
|
||||
desc = "A game about avoiding the mines"
|
||||
icon_state = "gamble_sweeper"
|
||||
game_ui = /datum/board_game/vore_sweeper
|
||||
|
||||
/datum/board_game/vore_sweeper
|
||||
name = GAME_SWEEPER
|
||||
table_icon = "gamble_sweeper"
|
||||
var/grid_size = 8
|
||||
var/mine_count = 10
|
||||
var/datum/weakref/dealer
|
||||
var/list/placed_mines = list()
|
||||
var/list/revealed_fields = list()
|
||||
var/list/placed_flags = list()
|
||||
|
||||
/datum/board_game/vore_sweeper/New(atom/holder)
|
||||
. = ..()
|
||||
parent = holder
|
||||
|
||||
/datum/board_game/vore_sweeper/Destroy(force)
|
||||
dealer = null
|
||||
parent = null
|
||||
. = ..()
|
||||
|
||||
/datum/board_game/vore_sweeper/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "VoreSweeper", name)
|
||||
ui.open()
|
||||
|
||||
/datum/board_game/vore_sweeper/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/mob/dealer_mob = dealer?.resolve()
|
||||
|
||||
var/placed_mine_data = game_state > GAME_PLAYING || (ui.user == dealer_mob) ? placed_mines : null
|
||||
var/total_tiles = grid_size * grid_size
|
||||
return list(
|
||||
"grid_size" = grid_size,
|
||||
"mine_count" = mine_count,
|
||||
"max_mines" = round(total_tiles * MAX_MINE_RATE),
|
||||
"dealer" = dealer_mob,
|
||||
"placed_mines" = placed_mine_data,
|
||||
"revealed_fields" = revealed_fields,
|
||||
"placed_flags" = placed_flags,
|
||||
"game_state" = game_state,
|
||||
"is_dealer" = dealer_mob == ui.user
|
||||
)
|
||||
|
||||
/datum/board_game/vore_sweeper/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("be_dealer")
|
||||
if(game_state == GAME_PLAYING)
|
||||
return FALSE
|
||||
dealer = WEAKREF(ui.user)
|
||||
return TRUE
|
||||
if("clear_dealer")
|
||||
var/mob/dealer_mob = dealer?.resolve()
|
||||
if(!dealer_mob)
|
||||
return FALSE
|
||||
if(dealer_mob == ui.user)
|
||||
parent.atom_say("[ui.user] stopped dealing.")
|
||||
dealer = null
|
||||
return TRUE
|
||||
if(get_dist(ui.user, dealer_mob) > 3)
|
||||
parent.atom_say("Dealer has been cleared by [ui.user].")
|
||||
dealer = null
|
||||
return TRUE
|
||||
return FALSE
|
||||
if("restart_game")
|
||||
var/mob/dealer_mob = dealer?.resolve()
|
||||
if(game_state < GAME_PLAYING)
|
||||
return FALSE
|
||||
placed_mines.Cut()
|
||||
revealed_fields.Cut()
|
||||
placed_flags.Cut()
|
||||
if(!dealer_mob && game_state > GAME_PLAYING)
|
||||
auto_place_mines(ui.user, TRUE)
|
||||
return TRUE
|
||||
game_state = GAME_SETUP
|
||||
return TRUE
|
||||
if("game_action")
|
||||
if(player_actions(params["action"], params["data"], ui.user))
|
||||
return TRUE
|
||||
return FALSE
|
||||
if("setup_action")
|
||||
if(dealer_actions(params["action"], params["data"], ui.user))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/player_actions(action, list/params, mob/user)
|
||||
if(user == dealer?.resolve())
|
||||
return FALSE
|
||||
if(game_state != GAME_PLAYING)
|
||||
return FALSE
|
||||
switch(action)
|
||||
if("open_field")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
var/key = validated_data[1]
|
||||
if(placed_flags[key])
|
||||
return FALSE
|
||||
if(revealed_fields[key])
|
||||
return FALSE
|
||||
if(placed_mines[key])
|
||||
game_state = GAME_LOST
|
||||
revealed_fields[key] = "M"
|
||||
return TRUE
|
||||
var/mine_count = count_surrounding_mines(validated_data[2], validated_data[3])
|
||||
revealed_fields[key] = mine_count
|
||||
if(!mine_count)
|
||||
reveal_empty_area(validated_data[2], validated_data[3])
|
||||
validate_victory()
|
||||
return TRUE
|
||||
if("toggle_flag")
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
var/key = validated_data[1]
|
||||
if(revealed_fields[key])
|
||||
return FALSE
|
||||
if(placed_flags[key])
|
||||
placed_flags -= key
|
||||
return TRUE
|
||||
placed_flags[key] = TRUE
|
||||
validate_flag_victory()
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/validate_victory()
|
||||
var/total_tiles = grid_size * grid_size
|
||||
var/safe_tiles = total_tiles - length(placed_mines)
|
||||
if(length(revealed_fields) >= safe_tiles)
|
||||
game_state = GAME_WON
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/validate_flag_victory()
|
||||
var/all_flagged = TRUE
|
||||
|
||||
for(var/mine_key in placed_mines)
|
||||
if(placed_mines[mine_key] && !placed_flags[mine_key])
|
||||
all_flagged = FALSE
|
||||
break
|
||||
|
||||
for(var/flag_key in placed_flags)
|
||||
if(!placed_mines[flag_key])
|
||||
all_flagged = FALSE
|
||||
break
|
||||
|
||||
if(!all_flagged)
|
||||
return
|
||||
|
||||
for(var/x = 1 to grid_size)
|
||||
for(var/y = 1 to grid_size)
|
||||
var/key = "[x],[y]"
|
||||
|
||||
if(placed_mines[key])
|
||||
continue
|
||||
|
||||
revealed_fields[key] = count_surrounding_mines(x, y)
|
||||
|
||||
game_state = GAME_WON
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/dealer_actions(action, list/params, mob/user)
|
||||
if(user != dealer?.resolve())
|
||||
return FALSE
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
switch(action)
|
||||
if("change_grid_size")
|
||||
var/new_grid_size = text2num(params["new_grid"])
|
||||
if(!new_grid_size)
|
||||
return FALSE
|
||||
if(new_grid_size < 4 || new_grid_size > 16)
|
||||
return FALSE
|
||||
validate_mine_count(mine_count, new_grid_size)
|
||||
grid_size = new_grid_size
|
||||
return TRUE
|
||||
if("change_mine_count")
|
||||
var/new_mine_count = text2num(params["new_mines"])
|
||||
if(!new_mine_count)
|
||||
return FALSE
|
||||
validate_mine_count(new_mine_count, grid_size)
|
||||
return TRUE
|
||||
if("place_mine")
|
||||
if(length(placed_mines) >= mine_count)
|
||||
return FALSE
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
var/key = validated_data[1]
|
||||
if(placed_mines[key])
|
||||
return FALSE
|
||||
placed_mines[key] = TRUE
|
||||
return TRUE
|
||||
if("remove_mine")
|
||||
if(game_state != GAME_SETUP)
|
||||
return FALSE
|
||||
if(length(placed_mines) <= 0)
|
||||
return FALSE
|
||||
var/list/validated_data = validate_coords(params)
|
||||
if(!validated_data)
|
||||
return FALSE
|
||||
var/key = validated_data[1]
|
||||
placed_mines -= key
|
||||
return TRUE
|
||||
if("auto_place_mines")
|
||||
return auto_place_mines(user)
|
||||
if("auto_place_mines_self")
|
||||
return auto_place_mines(user, TRUE)
|
||||
if("clear_all_mines")
|
||||
placed_mines.Cut()
|
||||
return TRUE
|
||||
if("start_game")
|
||||
game_state = GAME_PLAYING
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/reveal_empty_area(x, y)
|
||||
var/list/to_check = list(list(x, y))
|
||||
var/list/checked = list()
|
||||
var/i = 1
|
||||
|
||||
while(i <= length(to_check))
|
||||
var/current = to_check[i]
|
||||
i += 1
|
||||
var/cx = current[1]
|
||||
var/cy = current[2]
|
||||
var/key = "[cx],[cy]"
|
||||
|
||||
if(checked[key])
|
||||
continue
|
||||
checked[key] = TRUE
|
||||
|
||||
if(revealed_fields[key] || placed_flags[key])
|
||||
continue
|
||||
|
||||
var/adjacent_mines = count_surrounding_mines(cx, cy)
|
||||
revealed_fields[key] = adjacent_mines
|
||||
|
||||
if(adjacent_mines == 0)
|
||||
for(var/dx = -1 to 1)
|
||||
for(var/dy = -1 to 1)
|
||||
if(dx == 0 && dy == 0)
|
||||
continue
|
||||
var/nx = cx + dx
|
||||
var/ny = cy + dy
|
||||
if(field_check(nx, ny))
|
||||
to_check += list(list(nx, ny))
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/auto_place_mines(mob/user, play)
|
||||
var/placed = length(placed_mines)
|
||||
if(placed && play)
|
||||
to_chat(user, span_warning("You can't do this while there are mines placed."))
|
||||
while(placed < mine_count)
|
||||
var/x = rand(1, grid_size)
|
||||
var/y = rand(1, grid_size)
|
||||
var/key = "[x],[y]"
|
||||
if(!placed_mines[key])
|
||||
placed_mines[key] = TRUE
|
||||
placed++
|
||||
if(play)
|
||||
dealer = null
|
||||
game_state = GAME_PLAYING
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/field_check(new_x_location, new_y_location)
|
||||
if(new_x_location < 1 || new_x_location > grid_size)
|
||||
return FALSE
|
||||
if(new_y_location < 1 || new_y_location > grid_size)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/count_surrounding_mines(x, y)
|
||||
var/count = 0
|
||||
|
||||
for(var/dx = -1 to 1)
|
||||
for(var/dy = -1 to 1)
|
||||
if(dx == 0 && dy == 0)
|
||||
continue
|
||||
|
||||
var/check_x = x + dx
|
||||
var/check_y = y + dy
|
||||
|
||||
if(placed_mines["[check_x],[check_y]"])
|
||||
count++
|
||||
|
||||
return count
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/validate_mine_count(mines, size)
|
||||
var/total_tiles = size * size
|
||||
var/max_mines = round(total_tiles * MAX_MINE_RATE)
|
||||
if(mines <= max_mines)
|
||||
mine_count = mines
|
||||
return
|
||||
parent.atom_say("The grid with [total_tiles] tiles only supports a maximum of [max_mines] mines.")
|
||||
mine_count = max_mines
|
||||
|
||||
/datum/board_game/vore_sweeper/proc/validate_coords(list/params)
|
||||
var/x_loc = text2num(params["loc_x"])
|
||||
var/y_loc = text2num(params["loc_y"])
|
||||
|
||||
if(!isnum(x_loc) || !isnum(y_loc))
|
||||
return null
|
||||
|
||||
if(!field_check(x_loc, y_loc))
|
||||
return null
|
||||
|
||||
var/key = "[x_loc],[y_loc]"
|
||||
return list(key, x_loc, y_loc)
|
||||
|
||||
#undef GAME_PLAYING
|
||||
#undef GAME_LOST
|
||||
#undef GAME_WON
|
||||
#undef MAX_MINE_RATE
|
||||
@@ -767,19 +767,26 @@ var/global/image/backplane
|
||||
return TRUE
|
||||
|
||||
|
||||
/atom/proc/living_mobs_in_view(var/range = world.view, var/count_held = FALSE)
|
||||
/atom/proc/living_mobs_in_view(var/range = world.view, var/count_held = FALSE, var/needs_client = FALSE)
|
||||
var/list/viewers = oviewers(src, range)
|
||||
if(count_held)
|
||||
viewers = viewers(src,range)
|
||||
var/list/living = list()
|
||||
for(var/mob/living/L in viewers)
|
||||
if(L.is_incorporeal())
|
||||
for(var/mob/living/living_in_view in viewers)
|
||||
if(living_in_view.is_incorporeal())
|
||||
continue
|
||||
living += L
|
||||
if(count_held)
|
||||
for(var/obj/item/holder/H in L.contents)
|
||||
if(istype(H.held_mob, /mob/living))
|
||||
living += H.held_mob
|
||||
if(needs_client && !living_in_view.client)
|
||||
continue
|
||||
living += living_in_view
|
||||
if(!count_held)
|
||||
continue
|
||||
for(var/obj/item/holder/mob_holder in living_in_view.contents)
|
||||
if(!isliving(mob_holder.held_mob))
|
||||
continue
|
||||
var/mob/living/held_living = mob_holder.held_mob
|
||||
if(needs_client && !held_living.client)
|
||||
continue
|
||||
living += held_living
|
||||
return living
|
||||
|
||||
/proc/censor_swears(t)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
pda.current_app = src
|
||||
return 1
|
||||
|
||||
/datum/data/pda/app/proc/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/proc/update_ui(mob/user, list/data)
|
||||
|
||||
|
||||
// Utilities just have a button on the home screen, but custom code when clicked
|
||||
@@ -104,6 +104,6 @@
|
||||
pda.update_shortcuts()
|
||||
return 1
|
||||
|
||||
/datum/data/pda/utility/scanmode/proc/scan_mob(mob/living/C as mob, mob/living/user as mob)
|
||||
/datum/data/pda/utility/scanmode/proc/scan_mob(mob/living/C, mob/living/user)
|
||||
|
||||
/datum/data/pda/utility/scanmode/proc/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob)
|
||||
/datum/data/pda/utility/scanmode/proc/scan_atom(atom/A, mob/user)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
var/message1 // used for status_displays
|
||||
var/message2
|
||||
|
||||
/datum/data/pda/app/status_display/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/status_display/update_ui(mob/user, list/data)
|
||||
data["records"] = list(
|
||||
"message1" = message1 ? message1 : "(none)",
|
||||
"message2" = message2 ? message2 : "(none)")
|
||||
@@ -63,7 +63,7 @@
|
||||
template = "pda_signaller"
|
||||
category = "Utilities"
|
||||
|
||||
/datum/data/pda/app/signaller/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/signaller/update_ui(mob/user, list/data)
|
||||
if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/signal))
|
||||
var/obj/item/radio/integrated/signal/R = pda.cartridge.radio
|
||||
data["frequency"] = R.frequency
|
||||
@@ -112,7 +112,7 @@
|
||||
QDEL_NULL(power_monitor)
|
||||
return ..()
|
||||
|
||||
/datum/data/pda/app/power/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/power/update_ui(mob/user, list/data)
|
||||
data.Add(power_monitor.tgui_data(user))
|
||||
|
||||
/datum/data/pda/app/power/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
@@ -128,7 +128,7 @@
|
||||
/datum/data/pda/app/crew_records
|
||||
var/datum/data/record/general_records = null
|
||||
|
||||
/datum/data/pda/app/crew_records/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/crew_records/update_ui(mob/user, list/data)
|
||||
var/list/records[0]
|
||||
|
||||
if(general_records && (general_records in GLOB.data_core.general))
|
||||
@@ -169,7 +169,7 @@
|
||||
|
||||
var/datum/data/record/medical_records = null
|
||||
|
||||
/datum/data/pda/app/crew_records/medical/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/crew_records/medical/update_ui(mob/user, list/data)
|
||||
var/list/records = ..()
|
||||
if(!records)
|
||||
return
|
||||
@@ -194,7 +194,7 @@
|
||||
|
||||
var/datum/data/record/security_records = null
|
||||
|
||||
/datum/data/pda/app/crew_records/security/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/crew_records/security/update_ui(mob/user, list/data)
|
||||
var/list/records = ..()
|
||||
if(!records)
|
||||
return
|
||||
@@ -217,7 +217,7 @@
|
||||
template = "pda_supply"
|
||||
category = "Quartermaster"
|
||||
|
||||
/datum/data/pda/app/supply/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/supply/update_ui(mob/user, list/data)
|
||||
var/supplyData[0]
|
||||
var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle
|
||||
if (shuttle)
|
||||
@@ -254,7 +254,7 @@
|
||||
template = "pda_janitor"
|
||||
category = "Utilities"
|
||||
|
||||
/datum/data/pda/app/janitor/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/janitor/update_ui(mob/user, list/data)
|
||||
var/JaniData[0]
|
||||
var/turf/cl = get_turf(pda)
|
||||
|
||||
|
||||
@@ -32,15 +32,15 @@
|
||||
hold.max_storage_space = slots * 2
|
||||
hold.max_w_class = ITEMSIZE_SMALL
|
||||
|
||||
/obj/item/cartridge/storage/attack_hand(mob/user as mob)
|
||||
/obj/item/cartridge/storage/attack_hand(mob/user)
|
||||
if (hold.handle_attack_hand(user)) //otherwise interact as a regular storage item
|
||||
..(user)
|
||||
|
||||
/obj/item/cartridge/storage/attackby(obj/item/W as obj, mob/user as mob)
|
||||
/obj/item/cartridge/storage/attackby(obj/item/W, mob/user)
|
||||
..()
|
||||
return hold.attackby(W, user)
|
||||
|
||||
/obj/item/cartridge/storage/MouseDrop(obj/over_object as obj)
|
||||
/obj/item/cartridge/storage/MouseDrop(obj/over_object)
|
||||
if (hold.handle_mousedrop(usr, over_object))
|
||||
..(over_object)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
template = "pda_main_menu"
|
||||
hidden = 1
|
||||
|
||||
/datum/data/pda/app/main_menu/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/main_menu/update_ui(mob/user, list/data)
|
||||
title = pda.name
|
||||
|
||||
data["app"]["is_home"] = 1
|
||||
@@ -63,7 +63,7 @@
|
||||
note = "Thank you for choosing the [pda.model_name]!"
|
||||
notetitle = "Congratulations!"
|
||||
|
||||
/datum/data/pda/app/notekeeper/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/notekeeper/update_ui(mob/user, list/data)
|
||||
data["note"] = note // current pda notes
|
||||
data["notename"] = "Note [GLOB.alphabet_upper[currentnote]] : [notetitle]"
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
icon = "user"
|
||||
template = "pda_manifest"
|
||||
|
||||
/datum/data/pda/app/manifest/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/manifest/update_ui(mob/user, list/data)
|
||||
if(GLOB.data_core)
|
||||
GLOB.data_core.get_manifest_list()
|
||||
data["manifest"] = GLOB.PDA_Manifest
|
||||
@@ -221,7 +221,7 @@
|
||||
template = "pda_atmos_scan"
|
||||
category = "Utilities"
|
||||
|
||||
/datum/data/pda/app/atmos_scanner/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/atmos_scanner/update_ui(mob/user, list/data)
|
||||
data["aircontents"] = get_gas_mixture_default_scan_data(get_turf(user))
|
||||
|
||||
/datum/data/pda/app/news
|
||||
@@ -231,7 +231,7 @@
|
||||
|
||||
var/newsfeed_channel
|
||||
|
||||
/datum/data/pda/app/news/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/news/update_ui(mob/user, list/data)
|
||||
data["feeds"] = compile_news()
|
||||
data["latest_news"] = get_recent_news()
|
||||
if(newsfeed_channel)
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/datum/data/pda/app/game_launcher
|
||||
name = "Game Launcher"
|
||||
icon = "dice"
|
||||
notify_icon = "dice-d20"
|
||||
title = "Game Launcher V1.0"
|
||||
template = "pda_game_launcher"
|
||||
|
||||
var/datum/board_game/vore_sweeper/voresweeper
|
||||
var/datum/board_game/four_row/fourrow
|
||||
var/datum/board_game/space_battle/spacebattle
|
||||
var/datum/board_game/rpg_dice/rpgdice
|
||||
var/datum/board_game/chess/chess
|
||||
var/datum/board_game/checkers/checkers
|
||||
var/datum/board_game/nine_mens/ninemens
|
||||
var/datum/board_game/four_row/tic_tac_toe/tictactoe
|
||||
|
||||
/datum/data/pda/app/game_launcher/update_ui(mob/user, list/data)
|
||||
data["available_games"] = list(GAME_SWEEPER = voresweeper, GAME_FOUR_ROW = fourrow, GAME_SPACE_BATTLE = spacebattle, GAME_RGP_DICE = rpgdice, GAME_CHESS = chess, GAME_CHECKERS = checkers, GAME_NINE_MENS_MORRIS = ninemens, GAME_TIC_TAC_TOE = tictactoe)
|
||||
|
||||
/datum/data/pda/app/game_launcher/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if(GAME_SWEEPER)
|
||||
if(params["close"])
|
||||
if(!voresweeper)
|
||||
return FALSE
|
||||
QDEL_NULL(voresweeper)
|
||||
return TRUE
|
||||
if(!voresweeper)
|
||||
voresweeper = new(pda)
|
||||
voresweeper.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_FOUR_ROW)
|
||||
if(params["close"])
|
||||
if(!fourrow)
|
||||
return FALSE
|
||||
QDEL_NULL(fourrow)
|
||||
return TRUE
|
||||
if(!fourrow)
|
||||
fourrow = new(pda)
|
||||
fourrow.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_SPACE_BATTLE)
|
||||
if(params["close"])
|
||||
if(!spacebattle)
|
||||
return FALSE
|
||||
QDEL_NULL(spacebattle)
|
||||
return TRUE
|
||||
if(!spacebattle)
|
||||
spacebattle = new(pda)
|
||||
spacebattle.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_RGP_DICE)
|
||||
if(params["close"])
|
||||
if(!rpgdice)
|
||||
return FALSE
|
||||
QDEL_NULL(rpgdice)
|
||||
return TRUE
|
||||
if(!rpgdice)
|
||||
rpgdice = new(pda)
|
||||
rpgdice.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_CHESS)
|
||||
if(params["close"])
|
||||
if(!chess)
|
||||
return FALSE
|
||||
QDEL_NULL(chess)
|
||||
return TRUE
|
||||
if(!chess)
|
||||
chess = new(pda)
|
||||
chess.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_CHECKERS)
|
||||
if(params["close"])
|
||||
if(!checkers)
|
||||
return FALSE
|
||||
QDEL_NULL(checkers)
|
||||
return TRUE
|
||||
if(!checkers)
|
||||
checkers = new(pda)
|
||||
checkers.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_NINE_MENS_MORRIS)
|
||||
if(params["close"])
|
||||
if(!ninemens)
|
||||
return FALSE
|
||||
QDEL_NULL(ninemens)
|
||||
return TRUE
|
||||
if(!ninemens)
|
||||
ninemens = new(pda)
|
||||
ninemens.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
if(GAME_TIC_TAC_TOE)
|
||||
if(params["close"])
|
||||
if(!tictactoe)
|
||||
return FALSE
|
||||
QDEL_NULL(tictactoe)
|
||||
return TRUE
|
||||
if(!tictactoe)
|
||||
tictactoe = new(pda)
|
||||
tictactoe.tgui_interact(ui.user)
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/data/pda/app/game_launcher/Destroy()
|
||||
if(voresweeper)
|
||||
QDEL_NULL(voresweeper)
|
||||
if(fourrow)
|
||||
QDEL_NULL(fourrow)
|
||||
if(spacebattle)
|
||||
QDEL_NULL(spacebattle)
|
||||
if(rpgdice)
|
||||
QDEL_NULL(rpgdice)
|
||||
if(chess)
|
||||
QDEL_NULL(chess)
|
||||
if(checkers)
|
||||
QDEL_NULL(checkers)
|
||||
if(checkers)
|
||||
QDEL_NULL(ninemens)
|
||||
if(tictactoe)
|
||||
QDEL_NULL(tictactoe)
|
||||
. = ..()
|
||||
@@ -18,7 +18,7 @@
|
||||
. = ..()
|
||||
unnotify()
|
||||
|
||||
/datum/data/pda/app/messenger/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/messenger/update_ui(mob/user, list/data)
|
||||
data["silent"] = notify_silent // does the pda make noise when it receives a message?
|
||||
data["toff"] = toff // is the messenger function turned off?
|
||||
data["active_conversation"] = active_conversation // Which conversation are we following right now?
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/datum/data/pda/messenger_plugin
|
||||
var/datum/data/pda/app/messenger/messenger
|
||||
|
||||
/datum/data/pda/messenger_plugin/proc/user_act(mob/user as mob, obj/item/pda/P)
|
||||
/datum/data/pda/messenger_plugin/proc/user_act(mob/user, obj/item/pda/P)
|
||||
|
||||
|
||||
/datum/data/pda/messenger_plugin/virus
|
||||
name = "*Send Virus*"
|
||||
|
||||
/datum/data/pda/messenger_plugin/virus/user_act(mob/user as mob, obj/item/pda/P)
|
||||
/datum/data/pda/messenger_plugin/virus/user_act(mob/user, obj/item/pda/P)
|
||||
var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger)
|
||||
|
||||
if(M && !M.toff && pda.cartridge.charges > 0)
|
||||
@@ -19,7 +19,7 @@
|
||||
/datum/data/pda/messenger_plugin/virus/clown
|
||||
icon = "star"
|
||||
|
||||
/datum/data/pda/messenger_plugin/virus/clown/user_act(mob/user as mob, obj/item/pda/P)
|
||||
/datum/data/pda/messenger_plugin/virus/clown/user_act(mob/user, obj/item/pda/P)
|
||||
. = ..(user, P)
|
||||
if(.)
|
||||
user.show_message(span_notice("Virus sent!"), 1)
|
||||
@@ -30,7 +30,7 @@
|
||||
/datum/data/pda/messenger_plugin/virus/mime
|
||||
icon = "arrow-circle-down"
|
||||
|
||||
/datum/data/pda/messenger_plugin/virus/mime/user_act(mob/user as mob, obj/item/pda/P)
|
||||
/datum/data/pda/messenger_plugin/virus/mime/user_act(mob/user, obj/item/pda/P)
|
||||
. = ..(user, P)
|
||||
if(.)
|
||||
user.show_message(span_notice("Virus sent!"), 1)
|
||||
@@ -44,7 +44,7 @@
|
||||
name = "*Detonate*"
|
||||
icon = "exclamation-circle"
|
||||
|
||||
/datum/data/pda/messenger_plugin/virus/detonate/user_act(mob/user as mob, obj/item/pda/P)
|
||||
/datum/data/pda/messenger_plugin/virus/detonate/user_act(mob/user, obj/item/pda/P)
|
||||
. = ..(user, P)
|
||||
if(.)
|
||||
var/difficulty = 0
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
LAZYADD(serialized_guesses, list(out)) // Wrap it in a list so it stays a list
|
||||
|
||||
/datum/data/pda/app/nerdle/update_ui(mob/user as mob, list/data)
|
||||
/datum/data/pda/app/nerdle/update_ui(mob/user, list/data)
|
||||
data["guesses"] = serialized_guesses
|
||||
data["guesses_raw"] = guesses
|
||||
data["max"] = max_guesses
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
new/datum/data/pda/app/manifest,
|
||||
new/datum/data/pda/app/atmos_scanner,
|
||||
new/datum/data/pda/app/nerdle,
|
||||
new/datum/data/pda/app/game_launcher,
|
||||
new/datum/data/pda/utility/scanmode/notes,
|
||||
new/datum/data/pda/utility/flashlight)
|
||||
var/list/shortcut_cache = list()
|
||||
@@ -177,7 +178,7 @@
|
||||
/obj/item/pda/GetID()
|
||||
return id
|
||||
|
||||
/obj/item/pda/MouseDrop(obj/over_object as obj, src_location, over_location)
|
||||
/obj/item/pda/MouseDrop(obj/over_object, src_location, over_location)
|
||||
var/mob/M = usr
|
||||
if((!istype(over_object, /atom/movable/screen)) && can_use(usr))
|
||||
return attack_self(M)
|
||||
@@ -389,7 +390,7 @@
|
||||
start_program(find_program(/datum/data/pda/app/main_menu))
|
||||
|
||||
|
||||
/obj/item/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use.
|
||||
/obj/item/pda/proc/id_check(mob/user, choice)//To check for IDs; 1 for in-pda use, 2 for out of pda use.
|
||||
if(choice == 1)
|
||||
if (id)
|
||||
remove_id()
|
||||
@@ -411,7 +412,7 @@
|
||||
return 0
|
||||
|
||||
// access to status display signals
|
||||
/obj/item/pda/attackby(obj/item/C as obj, mob/user)
|
||||
/obj/item/pda/attackby(obj/item/C, mob/user)
|
||||
..()
|
||||
if(istype(C, /obj/item/cartridge) && !cartridge)
|
||||
cartridge = C
|
||||
@@ -456,11 +457,11 @@
|
||||
add_overlay("pda-pen")
|
||||
return
|
||||
|
||||
/obj/item/pda/attack(mob/living/C as mob, mob/living/user as mob)
|
||||
/obj/item/pda/attack(mob/living/C, mob/living/user)
|
||||
if (istype(C, /mob/living/carbon) && scanmode)
|
||||
scanmode.scan_mob(C, user)
|
||||
|
||||
/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity)
|
||||
/obj/item/pda/afterattack(atom/A, mob/user, proximity)
|
||||
if(proximity && scanmode)
|
||||
scanmode.scan_atom(A, user)
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
name = "Civilian Services Department (Relay)"
|
||||
cartridges_to_send_to = GLOB.civilian_cartridges
|
||||
|
||||
/obj/item/pda/clown/Crossed(atom/movable/AM as mob|obj) //Clown PDA is slippery.
|
||||
/obj/item/pda/clown/Crossed(atom/movable/AM) //Clown PDA is slippery.
|
||||
if(AM.is_incorporeal())
|
||||
return
|
||||
if (isliving(AM))
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
base_name = "Med Scanner"
|
||||
icon = "heart-o"
|
||||
|
||||
/datum/data/pda/utility/scanmode/medical/scan_mob(mob/living/C as mob, mob/living/user as mob)
|
||||
/datum/data/pda/utility/scanmode/medical/scan_mob(mob/living/C, mob/living/user)
|
||||
C.visible_message(span_warning("[user] has analyzed [C]'s vitals!"))
|
||||
user.show_message(span_notice("Analyzing Results for [C]:"))
|
||||
if(C.status_flags & FAKEDEATH)
|
||||
@@ -79,7 +79,7 @@
|
||||
base_name = "DNA Scanner"
|
||||
icon = "link"
|
||||
|
||||
/datum/data/pda/utility/scanmode/dna/scan_mob(mob/living/C as mob, mob/living/user as mob)
|
||||
/datum/data/pda/utility/scanmode/dna/scan_mob(mob/living/C, mob/living/user)
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(!istype(H.dna, /datum/dna))
|
||||
@@ -88,7 +88,7 @@
|
||||
to_chat(user, span_notice("[H]'s Fingerprints: [md5(H.dna.uni_identity)]"))
|
||||
scan_blood(C, user)
|
||||
|
||||
/datum/data/pda/utility/scanmode/dna/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob)
|
||||
/datum/data/pda/utility/scanmode/dna/scan_atom(atom/A, mob/user)
|
||||
scan_blood(A, user)
|
||||
|
||||
/datum/data/pda/utility/scanmode/dna/proc/scan_blood(atom/A, mob/user)
|
||||
@@ -105,7 +105,7 @@
|
||||
base_name = "Halogen Counter"
|
||||
icon = "exclamation-circle"
|
||||
|
||||
/datum/data/pda/utility/scanmode/halogen/scan_mob(mob/living/C as mob, mob/living/user as mob)
|
||||
/datum/data/pda/utility/scanmode/halogen/scan_mob(mob/living/C, mob/living/user)
|
||||
C.visible_message(span_warning("[user] has analyzed [C]'s radiation levels!"))
|
||||
|
||||
user.show_message(span_notice("Analyzing Results for [C]:"))
|
||||
@@ -118,7 +118,7 @@
|
||||
base_name = "Reagent Scanner"
|
||||
icon = "flask"
|
||||
|
||||
/datum/data/pda/utility/scanmode/reagent/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob)
|
||||
/datum/data/pda/utility/scanmode/reagent/scan_atom(atom/A, mob/user)
|
||||
if(!isnull(A.reagents))
|
||||
if(A.reagents.reagent_list.len > 0)
|
||||
var/reagents_length = A.reagents.reagent_list.len
|
||||
@@ -134,7 +134,7 @@
|
||||
base_name = "Gas Scanner"
|
||||
icon = "tachometer-alt"
|
||||
|
||||
/datum/data/pda/utility/scanmode/gas/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob)
|
||||
/datum/data/pda/utility/scanmode/gas/scan_atom(atom/A, mob/user)
|
||||
pda.analyze_gases(A, user)
|
||||
|
||||
/datum/data/pda/utility/scanmode/notes
|
||||
@@ -146,7 +146,7 @@
|
||||
. = ..()
|
||||
notes = pda.find_program(/datum/data/pda/app/notekeeper)
|
||||
|
||||
/datum/data/pda/utility/scanmode/notes/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob)
|
||||
/datum/data/pda/utility/scanmode/notes/scan_atom(atom/A, mob/user)
|
||||
if(notes && istype(A, /obj/item/paper))
|
||||
var/obj/item/paper/P = A
|
||||
var/list/brlist = list("p", "/p", "br", "hr", "h1", "h2", "h3", "h4", "/h1", "/h2", "/h3", "/h4")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* tgui state: board_game_state
|
||||
*
|
||||
* Checks that the default living handling or if the user or the object are inside a belly
|
||||
**/
|
||||
|
||||
GLOBAL_DATUM_INIT(tgui_board_game_state, /datum/tgui_state/board_game_state, new)
|
||||
|
||||
/datum/tgui_state/board_game_state/can_use_topic(atom/src_object, mob/user)
|
||||
if(!isliving(user))
|
||||
return STATUS_UPDATE
|
||||
var/mob/living/living_user = user
|
||||
if(isbelly(living_user.loc) || isbelly(src_object.loc))
|
||||
return living_user.board_game_can_use_tgui_topic(src_object)
|
||||
return living_user.default_can_use_tgui_topic(src_object)
|
||||
|
||||
/mob/proc/board_game_can_use_tgui_topic(atom/src_object)
|
||||
return STATUS_CLOSE
|
||||
|
||||
/mob/living/board_game_can_use_tgui_topic(atom/src_object)
|
||||
if(is_incorporeal())
|
||||
return STATUS_CLOSE
|
||||
var/dist = get_dist(get_turf(src_object), get_turf(src))
|
||||
if(dist <= 1)
|
||||
return STATUS_INTERACTIVE
|
||||
return STATUS_CLOSE
|
||||
Reference in New Issue
Block a user