From d170182292b0db9888f060549bac54b04f3c7486 Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:41:23 -0500 Subject: [PATCH] [MIRROR] Converts the slot machine over to TGUI (#1185) * Converts the slot machine over to TGUI (#81700) This PR is the long-awaited de-soulification of the slot machine, converting it over to TGUI. It also updates the jackpot, since it appears to have been quite broken. It will now give all of the available prize money plus 10000 credits. I'm fairly sure this is what was supposed to happen, but I honestly could not tell, so if this ends up being a balance change then so be it. Also adds a new funny kind of jackpot when you get 5 bombs in the middle row. Other than that, generally cleans up and updates some of the slot machine code. The styling of the slot machine ui is nothing fancy right now, and I'm open to suggestions about how the ui should look. * Converts the slot machine over to TGUI --------- Co-authored-by: Nick <42454181+Momo8289@users.noreply.github.com> --- code/game/machinery/slotmachine.dm | 227 ++++++++++-------- tgui/packages/tgui/interfaces/SlotMachine.tsx | 151 ++++++++++++ 2 files changed, 284 insertions(+), 94 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/SlotMachine.tsx diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index ddfe76f56af..b7eec3994b8 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -10,7 +10,7 @@ #define JACKPOT 10000 #define SPIN_TIME 65 //As always, deciseconds. #define REEL_DEACTIVATE_DELAY 7 -#define SEVEN "7" +#define JACKPOT_SEVENS FA_ICON_7 #define HOLOCHIP 1 #define COIN 2 @@ -24,7 +24,7 @@ density = TRUE circuit = /obj/item/circuitboard/computer/slot_machine light_color = LIGHT_COLOR_BROWN - interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE // don't need to be literate to play slots + interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON // don't need to be literate to play slots var/money = 3000 //How much money it has CONSUMED var/plays = 0 var/working = FALSE @@ -32,9 +32,19 @@ var/jackpots = 0 var/paymode = HOLOCHIP //toggles between HOLOCHIP/COIN, defined above var/cointype = /obj/item/coin/iron //default cointype + /// Icons that can be displayed by the slot machine. + var/static/list/icons = list( + FA_ICON_LEMON = list("value" = 2, "colour" = "yellow"), + FA_ICON_STAR = list("value" = 2, "colour" = "yellow"), + FA_ICON_BOMB = list("value" = 2, "colour" = "red"), + FA_ICON_BIOHAZARD = list("value" = 2, "colour" = "green"), + FA_ICON_APPLE_WHOLE = list("value" = 2, "colour" = "red"), + FA_ICON_7 = list("value" = 1, "colour" = "yellow"), + FA_ICON_DOLLAR_SIGN = list("value" = 2, "colour" = "green"), + ) + var/static/list/coinvalues var/list/reels = list(list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0, list("", "", "") = 0) - var/list/symbols = list(SEVEN = 1, "&" = 2, "@" = 2, "$" = 2, "?" = 2, "#" = 2, "!" = 2, "%" = 2) //if people are winning too much, multiply every number in this list by 2 and see if they are still winning too much. var/static/list/ray_filter = list(type = "rays", y = 16, size = 40, density = 4, color = COLOR_RED_LIGHT, factor = 15, flags = FILTER_OVERLAY) /obj/machinery/computer/slot_machine/Initialize(mapload) @@ -42,13 +52,13 @@ jackpots = rand(1, 4) //false hope plays = rand(75, 200) - INVOKE_ASYNC(src, PROC_REF(toggle_reel_spin), TRUE)//The reels won't spin unless we activate them + toggle_reel_spin_sync(1) //The reels won't spin unless we activate them var/list/reel = reels[1] for(var/i in 1 to reel.len) //Populate the reels. randomize_reels() - INVOKE_ASYNC(src, PROC_REF(toggle_reel_spin), FALSE) + toggle_reel_spin_sync(0) if (isnull(coinvalues)) coinvalues = list() @@ -84,46 +94,49 @@ icon_screen = "slots_screen" return ..() -/obj/machinery/computer/slot_machine/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/coin)) - var/obj/item/coin/C = I + +/obj/machinery/computer/slot_machine/item_interaction(mob/living/user, obj/item/inserted, list/modifiers, is_right_clicking) + if(istype(inserted, /obj/item/coin)) + var/obj/item/coin/inserted_coin = inserted if(paymode == COIN) if(prob(2)) - if(!user.transferItemToLoc(C, drop_location(), silent = FALSE)) + if(!user.transferItemToLoc(inserted_coin, drop_location(), silent = FALSE)) return - C.throw_at(user, 3, 10) + inserted_coin.throw_at(user, 3, 10) if(prob(10)) balance = max(balance - SPIN_PRICE, 0) to_chat(user, span_warning("[src] spits your coin back out!")) else - if(!user.temporarilyRemoveItemFromInventory(C)) + if(!user.temporarilyRemoveItemFromInventory(inserted_coin)) return - to_chat(user, span_notice("You insert [C] into [src]'s slot!")) - balance += C.value - qdel(C) + balloon_alert(user, "coin insterted") + balance += inserted_coin.value + qdel(inserted_coin) else - to_chat(user, span_warning("This machine is only accepting holochips!")) - else if(istype(I, /obj/item/holochip)) + balloon_alert(user, "holochips only!") + + else if(istype(inserted, /obj/item/holochip)) if(paymode == HOLOCHIP) - var/obj/item/holochip/H = I - if(!user.temporarilyRemoveItemFromInventory(H)) + var/obj/item/holochip/inserted_chip = inserted + if(!user.temporarilyRemoveItemFromInventory(inserted_chip)) return - to_chat(user, span_notice("You insert [H.credits] holocredits into [src]'s slot!")) - balance += H.credits - qdel(H) + balloon_alert(user, "[inserted_chip.credits] credit[inserted_chip.credits == 1 ? "" : "s"] inserted") + balance += inserted_chip.credits + qdel(inserted_chip) else - to_chat(user, span_warning("This machine is only accepting coins!")) - else if(I.tool_behaviour == TOOL_MULTITOOL) + balloon_alert(user, "coins only!") + + else if(inserted.tool_behaviour == TOOL_MULTITOOL) if(balance > 0) visible_message("[src] says, 'ERROR! Please empty the machine balance before altering paymode'") //Prevents converting coins into holocredits and vice versa else if(paymode == HOLOCHIP) paymode = COIN - visible_message("[src] says, 'This machine now works with COINS!'") + balloon_alert(user, "now using coins") else paymode = HOLOCHIP - visible_message("[src] says, 'This machine now works with HOLOCHIPS!'") + balloon_alert(user, "now using holochips") else return ..() @@ -138,49 +151,54 @@ balloon_alert(user, "machine rigged") return TRUE -/obj/machinery/computer/slot_machine/ui_interact(mob/living/user) +/obj/machinery/computer/slot_machine/ui_interact(mob/living/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SlotMachine", name) + ui.open() + +/obj/machinery/computer/slot_machine/ui_static_data(mob/user) + var/list/data = list() + data["icons"] = list() + for(var/icon_name in icons) + var/list/icon = icons[icon_name] + icon += list("icon" = icon_name) + data["icons"] += list(icon) + data["cost"] = SPIN_PRICE + data["jackpot"] = JACKPOT + + return data + +/obj/machinery/computer/slot_machine/ui_data(mob/user) + var/list/data = list() + var/list/reel_states = list() + for(var/reel_state in reels) + reel_states += list(reel_state) + data["state"] = reel_states + data["balance"] = balance + data["working"] = working + data["money"] = money + data["plays"] = plays + data["jackpots"] = jackpots + data["paymode"] = paymode + return data + + +/obj/machinery/computer/slot_machine/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() - var/reeltext = {"
- /*****^*****^*****^*****^*****\\
- | \[[reels[1][1]]\] | \[[reels[2][1]]\] | \[[reels[3][1]]\] | \[[reels[4][1]]\] | \[[reels[5][1]]\] |
- | \[[reels[1][2]]\] | \[[reels[2][2]]\] | \[[reels[3][2]]\] | \[[reels[4][2]]\] | \[[reels[5][2]]\] |
- | \[[reels[1][3]]\] | \[[reels[2][3]]\] | \[[reels[3][3]]\] | \[[reels[4][3]]\] | \[[reels[5][3]]\] |
- \\*****v*****v*****v*****v*****/
-
"} - - var/dat - if(working) - dat = reeltext - - else - dat = {"Five credits to play!
- Prize Money Available: [money] (jackpot payout is ALWAYS 100%!)
- Credit Remaining: [balance]
- [plays] players have tried their luck today, and [jackpots] have won a jackpot!
-

- Play!
-
- [reeltext] -
"} - if(balance > 0) - dat+="Refund balance
" - - var/datum/browser/popup = new(user, "slotmachine", "Slot Machine") - popup.set_content(dat) - popup.open() - -/obj/machinery/computer/slot_machine/Topic(href, href_list) - . = ..() //Sanity checks. if(.) - return . - - if(href_list["spin"]) - spin(usr) + return - else if(href_list["refund"]) - if(balance > 0) - give_payout(balance) - balance = 0 + switch(action) + if("spin") + spin(ui.user) + return TRUE + if("payout") + if(balance > 0) + give_payout(balance) + balance = 0 + return TRUE /obj/machinery/computer/slot_machine/emp_act(severity) . = ..() @@ -214,8 +232,6 @@ toggle_reel_spin(1) update_appearance() - updateDialog() - var/spin_loop = addtimer(CALLBACK(src, PROC_REF(do_spin)), 2, TIMER_LOOP|TIMER_STOPPABLE) addtimer(CALLBACK(src, PROC_REF(finish_spinning), spin_loop, user, the_name), SPIN_TIME - (REEL_DEACTIVATE_DELAY * reels.len)) @@ -223,7 +239,6 @@ /obj/machinery/computer/slot_machine/proc/do_spin() randomize_reels() - updateDialog() use_power(active_power_usage) /obj/machinery/computer/slot_machine/proc/finish_spinning(spin_loop, mob/user, the_name) @@ -232,50 +247,64 @@ deltimer(spin_loop) give_prizes(the_name, user) update_appearance() - updateDialog() +/// Check if the machine can be spun /obj/machinery/computer/slot_machine/proc/can_spin(mob/user) if(machine_stat & NOPOWER) - to_chat(user, span_warning("The slot machine has no power!")) + balloon_alert(user, "no power!") return FALSE if(machine_stat & BROKEN) - to_chat(user, span_warning("The slot machine is broken!")) + balloon_alert(user, "machine broken!") return FALSE if(working) - to_chat(user, span_warning("You need to wait until the machine stops spinning before you can play again!")) + balloon_alert(user, "already spinning!") return FALSE if(balance < SPIN_PRICE) - to_chat(user, span_warning("Insufficient money to play!")) + balloon_alert(user, "insufficient balance!") return FALSE return TRUE +/// Sets the spinning states of all reels to value, with a delay between them /obj/machinery/computer/slot_machine/proc/toggle_reel_spin(value, delay = 0) //value is 1 or 0 aka on or off for(var/list/reel in reels) if(!value) playsound(src, 'sound/machines/ding_short.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) reels[reel] = value - sleep(delay) + if(delay) + sleep(delay) +/// Same as toggle_reel_spin, but without the delay and runs synchronously +/obj/machinery/computer/slot_machine/proc/toggle_reel_spin_sync(value) + for(var/list/reel in reels) + reels[reel] = value + +/// Randomize the states of all reels /obj/machinery/computer/slot_machine/proc/randomize_reels() for(var/reel in reels) if(reels[reel]) reel[3] = reel[2] reel[2] = reel[1] - reel[1] = pick(symbols) + var/chosen = pick(icons) + reel[1] = icons[chosen] + list("icon_name" = chosen) +/// Checks if any prizes have been won, and pays them out /obj/machinery/computer/slot_machine/proc/give_prizes(usrname, mob/user) var/linelength = get_lines() var/did_player_win = TRUE - if(reels[1][2] + reels[2][2] + reels[3][2] + reels[4][2] + reels[5][2] == "[SEVEN][SEVEN][SEVEN][SEVEN][SEVEN]") - visible_message("[src] says, 'JACKPOT! You win [money] credits!'") + if(check_jackpot(FA_ICON_BOMB)) + var/obj/item/grenade/flashbang/bang = new(get_turf(src)) + bang.arm_grenade(null, 1 SECONDS) + + else if(check_jackpot(JACKPOT_SEVENS)) + var/prize = money + JACKPOT + visible_message("[src] says, 'JACKPOT! You win [prize] credits!'") priority_announce("Congratulations to [user ? user.real_name : usrname] for winning the jackpot at the slot machine in [get_area(src)]!") jackpots += 1 - balance += money - give_payout(JACKPOT) money = 0 if(paymode == HOLOCHIP) - new /obj/item/holochip(loc, JACKPOT) + new /obj/item/holochip(loc, prize) else for(var/i in 1 to 5) cointype = pick(subtypesof(/obj/item/coin)) @@ -298,7 +327,8 @@ money = max(money - SPIN_PRICE * 4, money) else - to_chat(user, span_warning("No luck!")) + balloon_alert(user, "no luck!") + playsound(src, 'sound/machines/buzz-sigh.ogg', 50) did_player_win = FALSE if(did_player_win) @@ -307,31 +337,38 @@ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "jackpot_rays"), 3 SECONDS) playsound(src, 'sound/machines/roulettejackpot.ogg', 50, TRUE) +/// Checks for a jackpot (5 matching icons in the middle row) with the given icon name +/obj/machinery/computer/slot_machine/proc/check_jackpot(name) + return reels[1][2]["icon_name"] + reels[2][2]["icon_name"] + reels[3][2]["icon_name"] + reels[4][2]["icon_name"] + reels[5][2]["icon_name"] == "[name][name][name][name][name]" + +/// Finds the largest number of consecutive matching icons in a row /obj/machinery/computer/slot_machine/proc/get_lines() var/amountthesame for(var/i in 1 to 3) - var/inputtext = reels[1][i] + reels[2][i] + reels[3][i] + reels[4][i] + reels[5][i] - for(var/symbol in symbols) + var/inputtext = reels[1][i]["icon_name"] + reels[2][i]["icon_name"] + reels[3][i]["icon_name"] + reels[4][i]["icon_name"] + reels[5][i]["icon_name"] + for(var/icon in icons) var/j = 3 //The lowest value we have to check for. - var/symboltext = symbol + symbol + symbol + var/symboltext = icon + icon + icon while(j <= 5) if(findtext(inputtext, symboltext)) amountthesame = max(j, amountthesame) j++ - symboltext += symbol + symboltext += icon if(amountthesame) break return amountthesame +/// Give the specified amount of money. If the amount is greater than the amount of prize money available, add the difference as balance /obj/machinery/computer/slot_machine/proc/give_money(amount) - var/amount_to_give = money >= amount ? amount : money - var/surplus = amount_to_give - give_payout(amount_to_give) - money = max(0, money - amount) + var/amount_to_give = min(amount, money) + var/surplus = amount - give_payout(amount_to_give) + money -= amount_to_give balance += surplus +/// Pay out the specified amount in either coins or holochips /obj/machinery/computer/slot_machine/proc/give_payout(amount) if(paymode == HOLOCHIP) cointype = /obj/item/holochip @@ -348,23 +385,25 @@ return amount -/obj/machinery/computer/slot_machine/proc/dispense(amount = 0, cointype = /obj/item/coin/silver, mob/living/target, throwit = 0) +/// Dispense the given amount. If machine is set to use coins, will use the specified coin type. +/// If throwit and target are set, will launch the payment at the target +/obj/machinery/computer/slot_machine/proc/dispense(amount = 0, cointype = /obj/item/coin/silver, throwit = FALSE, mob/living/target) if(paymode == HOLOCHIP) - var/obj/item/holochip/H = new /obj/item/holochip(loc,amount) + var/obj/item/holochip/chip = new /obj/item/holochip(loc,amount) if(throwit && target) - H.throw_at(target, 3, 10) + chip.throw_at(target, 3, 10) else var/value = coinvalues["[cointype]"] if(value <= 0) CRASH("Coin value of zero, refusing to payout in dispenser") while(amount >= value) - var/obj/item/coin/C = new cointype(loc) //DOUBLE THE PAIN + var/obj/item/coin/thrown_coin = new cointype(loc) //DOUBLE THE PAIN amount -= value if(throwit && target) - C.throw_at(target, 3, 10) + thrown_coin.throw_at(target, 3, 10) else - random_step(C, 2, 40) + random_step(thrown_coin, 2, 40) playsound(src, pick(list('sound/machines/coindrop.ogg', 'sound/machines/coindrop2.ogg')), 50, TRUE) return amount @@ -374,7 +413,7 @@ #undef HOLOCHIP #undef JACKPOT #undef REEL_DEACTIVATE_DELAY -#undef SEVEN +#undef JACKPOT_SEVENS #undef SMALL_PRIZE #undef SPIN_PRICE #undef SPIN_TIME diff --git a/tgui/packages/tgui/interfaces/SlotMachine.tsx b/tgui/packages/tgui/interfaces/SlotMachine.tsx new file mode 100644 index 00000000000..6d6d464f913 --- /dev/null +++ b/tgui/packages/tgui/interfaces/SlotMachine.tsx @@ -0,0 +1,151 @@ +import { useBackend } from '../backend'; +import { Button, Icon, Section } from '../components'; +import { Window } from '../layouts'; + +type IconInfo = { + value: number; + colour: string; + icon_name: string; +}; + +type BackendData = { + icons: IconInfo[]; + state: any[]; + balance: number; + working: boolean; + money: number; + cost: number; + plays: number; + jackpots: number; + jackpot: number; + paymode: number; +}; + +type SlotsTileProps = { + icon: string; + color?: string; + background?: string; +}; + +type SlotsReelProps = { + reel: IconInfo[]; +}; + +const pluralS = (amount: number) => { + return amount === 1 ? '' : 's'; +}; + +const SlotsReel = (props: SlotsReelProps) => { + const { reel } = props; + return ( +
+ {reel.map((slot, i) => ( + + ))} +
+ ); +}; + +const SlotsTile = (props: SlotsTileProps) => { + return ( +
+ +
+ ); +}; + +export const SlotMachine = (props) => { + const { act, data } = useBackend(); + // icons: The list of possible icons, including colour and name + // backendState: the current state of the slots according to the backend + const { + plays, + jackpots, + money, + cost, + state, + balance, + jackpot, + working: rolling, + paymode, + } = data; + + return ( + +
+
+

+ Only {cost} credit{pluralS(cost)} for a chance to win big! +

+

+ Available prize money:{' '} + + {money} credit{pluralS(money)} + {' '} +

+ {paymode === 1 && ( +

+ Current jackpot:{' '} + + {money + jackpot} credit{pluralS(money + jackpot)}! + +

+ )} +

+ So far people have spun{' '} + + {plays} time{pluralS(plays)}, + {' '} + and won{' '} + + {jackpots} jackpot{pluralS(jackpots)}! + +

+
+
+
+ {state.map((reel, i) => { + return ; + })} +
+
+ +
+ Balance: {balance} +
+ +
+
+
+ ); +};