diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index cfe65a9ebba6..56461984dd89 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -34,4 +34,7 @@ else temp.filename = filename temp.filetype = filetype - return temp \ No newline at end of file + return temp + +/datum/computer_file/proc/try_insert(obj/item/inserted_item, mob/living/user = null) + return FALSE diff --git a/code/modules/modular_computers/file_system/programs/blackjack.dm b/code/modules/modular_computers/file_system/programs/blackjack.dm new file mode 100644 index 000000000000..647fcc10a3ed --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/blackjack.dm @@ -0,0 +1,276 @@ +#define BLACKJACK_CONTINUE 0 +#define BLACKJACK_LOSS 1 +#define BLACKJACK_WIN 2 +#define BLACKJACK_TIE 3 +#define BLACKJACK_IDLE 4 +#define BLACKJACK_DEALER_TURN 5 + +#define JACK_CARD "J" +#define QUEEN_CARD "Q" +#define KING_CARD "K" +#define ACE_CARD "A" + +/datum/computer_file/program/blackjack + filename = "blackjack" + filedesc = "Nanotrasen Micro Arcade: Blackjack" + program_icon_state = "arcade" + extended_desc = "Nanotrasen does not permit anyone under the age of 21 to partake in gambling. Requires an NTNet connection." + requires_ntnet = TRUE + network_destination = "arcade network" + size = 4 + tgui_id = "NtosBlackjack" + program_icon = "gamepad" + + var/game_state = BLACKJACK_IDLE + /// Card format is value and suit i.e. "2H" = 2 of Hearts, "KC" = King of Clubs + var/list/active_player_cards = list() + var/list/active_dealer_cards = list() + var/list/current_deck = list() + var/credits_stored = 0 + var/active_wager = 0 + var/set_wager = 5 + + var/game_end_reason = "" + + var/game_id = 0 + + var/datum/bank_account/temp_account // Call refresh_id_account() or get_credit_amount() before using this + +/datum/computer_file/program/blackjack/proc/refresh_id_account() + temp_account = null + if(holder && istype(holder.holder, /obj/item/modular_computer)) + var/obj/item/modular_computer/computer = holder.holder + var/obj/item/card/id/stored_card = computer.GetID() + if(istype(stored_card) && istype(stored_card.registered_account)) + temp_account = stored_card.registered_account + return stored_card.registered_account + +/datum/computer_file/program/blackjack/proc/get_credit_amount() + refresh_id_account() + return temp_account?.account_balance || credits_stored + +/datum/computer_file/program/blackjack/proc/adjust_credits(amt) + if(refresh_id_account()) + temp_account.adjust_money(amt) + else + credits_stored += amt + +/datum/computer_file/program/blackjack/proc/new_game() + if(set_wager <= 0 || get_credit_amount() < set_wager) + return + + game_id++ + + adjust_credits(-set_wager) + active_wager = set_wager + + game_state = BLACKJACK_CONTINUE + game_end_reason = "" + active_player_cards = list() + active_dealer_cards = list() + current_deck = list( + "2S","3S","4S","5S","6S","7S","8S","9S","10S","JS","QS","KS","AS", + "2H","3H","4H","5H","6H","7H","8H","9H","10H","JH","QH","KH","AH", + "2C","3C","4C","5C","6C","7C","8C","9C","10C","JC","QC","KC","AC", + "2D","3D","4D","5D","6D","7D","8D","9D","10D","JD","QD","KD","AD" + ) + shuffle_inplace(current_deck) + + // smooth + play_snd('sound/machines/switch3.ogg') + active_player_cards += pop(current_deck) + active_dealer_cards += pop(current_deck) + active_player_cards += pop(current_deck) + +/datum/computer_file/program/blackjack/proc/calculate_hand_worth(list/hand) + var/worth = 0 + var/aces = 0 + var/list/hand_copy = LAZYCOPY(hand) + for(var/card in hand_copy) + card = copytext(card, 1, -1) // Remove the suit, we do not care about it + if(text2num(card)) + worth += text2num(card) + else + switch(card) + if(JACK_CARD, QUEEN_CARD, KING_CARD) // "Picture" cards are worth 10 + worth += 10 + if(ACE_CARD) // Aces are calculated last + aces += 1 + + for(var/i in 1 to aces) // Ace is worth either 1 or 11, whichever is more beneficial + if(worth < 12 - aces) // This algorithm will never surpass 21 unless you have too many aces (somehow) + worth += 11 + else + worth += 1 + + return worth + +/datum/computer_file/program/blackjack/ui_data(mob/user) + var/list/data = get_header_data() + + data["game_state"] = game_state + data["active_dealer_cards"] = active_dealer_cards + data["active_player_cards"] = active_player_cards + data["credits_stored"] = get_credit_amount() + data["local_credits"] = credits_stored + data["has_id"] = !!temp_account + data["active_wager"] = active_wager + data["set_wager"] = set_wager + data["game_end_reason"] = game_end_reason + + return data + +/datum/computer_file/program/blackjack/try_insert(obj/item/inserted_item, mob/living/user) + if(!holder || !istype(holder?.holder, /obj/item/modular_computer)) + return FALSE + + if(!inserted_item.get_item_credit_value()) + return FALSE + + credits_stored += inserted_item.get_item_credit_value() + QDEL_NULL(inserted_item) + play_snd('sound/machines/ping.ogg') + SStgui.try_update_ui(user, src) + return TRUE + +/datum/computer_file/program/blackjack/proc/play_snd(sound_path) + if(holder && istype(holder.holder, /obj/item/modular_computer)) + var/obj/item/modular_computer/computer = holder.holder + computer.play_computer_sound(sound_path, 40, 1) + +/datum/computer_file/program/blackjack/ui_act(action, list/params, datum/tgui/ui) + . = ..() + if(..()) + return . + + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + if(user.age < 21) // We do not condone underage gambling + play_snd('sound/machines/buzz-sigh.ogg') + return FALSE + + switch(action) + if("PRG_new_game") + new_game() + return TRUE + + if("PRG_eject_credits") + if(credits_stored < 1) + return FALSE + var/obj/item/holochip/holochip = new (usr.drop_location(), credits_stored) + usr.put_in_hands(holochip) + to_chat(usr, span_notice("You withdraw [credits_stored] credits into a holochip.")) + credits_stored = 0 + play_snd('sound/machines/click.ogg') + return TRUE + + if("PRG_set_wager") + if(!isnum(text2num(params["wager"]))) + return FALSE + var/new_wager = text2num(params["wager"]) + if(round(new_wager) < 1) + return FALSE + set_wager = round(new_wager) + play_snd('sound/machines/click.ogg') + return TRUE + + if("PRG_hit") + if(game_state != BLACKJACK_CONTINUE) + return FALSE + play_snd('sound/machines/switch3.ogg') + active_player_cards += pop(current_deck) + check_player_hand(usr) + return TRUE + + if("PRG_stand") + if(game_state != BLACKJACK_CONTINUE) + return FALSE + game_state = BLACKJACK_DEALER_TURN + addtimer(CALLBACK(src, PROC_REF(dealer_turn), game_id, usr), 1 SECONDS) + play_snd('sound/machines/click.ogg') + return TRUE + + if("PRG_double_down") + if(game_state != BLACKJACK_CONTINUE) + return FALSE + if(get_credit_amount() < active_wager) + return + adjust_credits(-active_wager) + active_wager *= 2 + play_snd('sound/machines/switch3.ogg') + active_player_cards += pop(current_deck) + if(!check_player_hand(usr)) + game_state = BLACKJACK_DEALER_TURN + addtimer(CALLBACK(src, PROC_REF(dealer_turn), game_id, usr), 1 SECONDS) + return TRUE + +/datum/computer_file/program/blackjack/proc/check_player_hand(mob/user) + var/worth = calculate_hand_worth(active_player_cards) + if(worth == 21) + game_state = BLACKJACK_DEALER_TURN + addtimer(CALLBACK(src, PROC_REF(dealer_turn), game_id, user), 1 SECONDS) + return TRUE + + if(worth > 21) + game_state = BLACKJACK_LOSS + game_end_reason = "You busted!" + active_wager = 0 + play_snd('sound/machines/buzz-two.ogg') + return TRUE + + return FALSE + +/datum/computer_file/program/blackjack/proc/dealer_turn(g_id, mob/user) + if(game_state != BLACKJACK_DEALER_TURN || game_id != g_id) + return + + play_snd('sound/machines/switch1.ogg') + active_dealer_cards += pop(current_deck) + SStgui.try_update_ui(user, src) + + var/dealer_worth = calculate_hand_worth(active_dealer_cards) + if(dealer_worth < 17) // Dealer draws until it has 17 or higher + addtimer(CALLBACK(src, PROC_REF(dealer_turn), game_id, user), 1 SECONDS) + return + + var/player_worth = calculate_hand_worth(active_player_cards) + // check_player_hand() already checked for player bust + if(dealer_worth == player_worth) + game_state = BLACKJACK_TIE + adjust_credits(active_wager) + active_wager = 0 + SStgui.try_update_ui(user, src) + play_snd('sound/machines/button1.ogg') + return + + if(dealer_worth > 21 || player_worth > dealer_worth) + if(dealer_worth > 21) + game_end_reason = "Dealer busted!" + else + game_end_reason = "You won!" + game_state = BLACKJACK_WIN + adjust_credits(active_wager * 2) + active_wager = 0 + SStgui.try_update_ui(user, src) + play_snd('sound/machines/chime.ogg') + return + + if(player_worth < dealer_worth) // this is redundant, but it helps understand what is happening + game_state = BLACKJACK_LOSS // | || || |_ + game_end_reason = "You lost!" + active_wager = 0 + SStgui.try_update_ui(user, src) + play_snd('sound/machines/buzz-two.ogg') + return + +#undef BLACKJACK_CONTINUE +#undef BLACKJACK_LOSS +#undef BLACKJACK_WIN +#undef BLACKJACK_TIE +#undef BLACKJACK_IDLE +#undef BLACKJACK_DEALER_TURN + +#undef JACK_CARD +#undef QUEEN_CARD +#undef KING_CARD +#undef ACE_CARD diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 848af8ce5200..f1cb63c5d3dd 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -51,7 +51,7 @@ if(istype(F, /datum/computer_file/program)) var/datum/computer_file/program/P = F P.computer = holder - + stored_files.Add(F) recalculate_size() return F @@ -121,6 +121,16 @@ return F return null +/obj/item/computer_hardware/hard_drive/try_insert(obj/item/I, mob/living/user) + if(!holder) + return FALSE + + for(var/datum/computer_file/file in stored_files) + if(file.try_insert(I, user)) + return TRUE + + return FALSE + /obj/item/computer_hardware/hard_drive/Destroy() for(var/F in stored_files) qdel(F) diff --git a/tgui/packages/tgui/interfaces/NtosBlackjack.tsx b/tgui/packages/tgui/interfaces/NtosBlackjack.tsx new file mode 100644 index 000000000000..47e91f0beb3b --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosBlackjack.tsx @@ -0,0 +1,211 @@ +import { useBackend } from '../backend'; +import { Box, Button, Icon, Section, Stack } from '../components'; +import { NtosWindow } from '../layouts'; + +enum GameState { + Continue = 0, + Loss = 1, + Win = 2, + Tie = 3, + Idle = 4, + DealerTurn = 5, +} + +const GameStateFlavor = { + 0:"Good luck!", + 1:"Better luck next time!", + 2:"Congratulations!", + 3:"Tied!", + 4:"Gamble responsibly!", + 5:"Dealer is drawing...", +}; + +const ColorSuit = { + "S":"black", + "H":"red", + "C":"black", + "D":"red", +}; + +const IconSuit = { + "S":"spa", + "H":"heart", + "C":"cubes", + "D":"diamond", +}; + +type Data = { + game_state: GameState; + active_dealer_cards: Array; + active_player_cards: Array; + credits_stored: number; + local_credits: number; + has_id: boolean; + active_wager: number; + set_wager: number; + game_end_reason: string; +}; + +export const NtosBlackjack = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + +
+
+
+
+ act('PRG_new_game')} + textAlign='center' + width={15} + content={"New Game"} /> +
+
+
+ act('PRG_eject_credits')} + textAlign='center' + width={15} + content={"Eject Credits"} /> +
+ act('PRG_set_wager', { + wager: value, + })} + textAlign='center' + width={15} + content={"Set Wager"} /> +
+
+
+ act('PRG_hit')} + textAlign='center' + width={15} + content={"Hit"} /> +
+ act('PRG_stand')} + textAlign='center' + width={15} + content={"Stand"} /> +
+ act('PRG_double_down')} + textAlign='center' + width={15} + content={"Double Down"} /> +
+
+
+ + Credits {data.has_id ? " in account" : ""} +
+ {data.credits_stored} +
+
+ Active Bet +
+ {data.active_wager} +
+
+
+
+ {data.game_end_reason} +
+ {GameStateFlavor[data.game_state]} +
+
+
+ + + +
+ + Dealer's hand + +
+ + {data.active_dealer_cards.map((card_string, index) => + ( + + + + {card_string.slice(0, -1)} + + + + + + + + {card_string.slice(0, -1)} + + + + ) + )} + +
+
+ +
+ + Your hand + +
+ + {data.active_player_cards.map((card_string, index) => + ( + + + + {card_string.slice(0, -1)} + + + + + + + + {card_string.slice(0, -1)} + + + + ) + )} + +
+
+
+
+
+
+
+ ); +}; diff --git a/yogstation.dme b/yogstation.dme index 46d187435185..a30db4b67916 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -3022,6 +3022,7 @@ #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\program_events.dm" #include "code\modules\modular_computers\file_system\programs\arcade.dm" +#include "code\modules\modular_computers\file_system\programs\blackjack.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\minesweeper.dm"