diff --git a/files/assets/css/casino.css b/files/assets/css/casino.css deleted file mode 100644 index 6c0603111..000000000 --- a/files/assets/css/casino.css +++ /dev/null @@ -1,138 +0,0 @@ -.casino-games { - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; -} - -.casino-game { - flex: 1; -} - -/* Slots */ -#slots-block { - max-width: 700px; -} - -.casino-slots-results { - display: flex; - align-items: center; - justify-content: center; -} - -.casino-slots-results .reel { - display: flex; - align-items: center; - justify-content: center; - width: 100px; - height: 100px; - border: 2px solid black; - background-color: var(--gray); - border: 1px solid var(--black); - border-radius: 8px; - font-size: 64px; -} - -.casino-slots-outcome { - visibility: hidden; - text-align: right; - margin: 1rem 0; -} - -/* Blackjack */ -#blackjack-block { - max-width: 800px; -} - -@media screen and (max-width: 600px) { - .blackjack-table .hand .playing-card { - width: 60px; - height: 90px; - } -} - -.casino-blackjack-outcome { - visibility: hidden; - text-align: right; - margin-bottom: 1rem; -} - -.blackjack-table { - max-width: 500px; -} - -.blackjack-table .hand { - display: flex; - align-items: center; - justify-content: space-evenly; -} - -.blackjack-table .hand .playing-card { - display: flex; - align-items: center; - justify-content: center; - width: 60px; - height: 90px; - border: 2px solid black; - background-color: var(--gray); - border: 1px solid var(--black); - border-radius: 8px; - font-size: 30px; -} - -.blackjack-table .hand .playing-card.dealt { - background-color: #ddd; -} - -.casino-blackjack-actions { - text-align: right; -} - -/* Lottery */ -.casino-lottery { - margin-top: 2rem; -} - -/* Blocks */ -.casino-block { - padding: 1rem; - border: 4px solid #361506; - border-radius: 8px; - background-color: #092711; - margin: 2rem 0; -} - -.casino-block-title { - display: flex; - align-items: center; - font-size: 32px; - text-transform: uppercase; - letter-spacing: 4px; - opacity: 0.5; -} - -.casino-block-inner { - display: flex; - align-items: center; - justify-content: space-between; -} - -.casino-block-left { - display: flex; - align-items: flex-end; - justify-content: center; - flex: 1; -} - -@media screen and (max-width: 600px) { - .casino-block-left { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } -} - -.casino-block-game { - margin-right: 2rem; -} diff --git a/files/assets/images/rDrama/brit.webp b/files/assets/images/rDrama/brit.webp new file mode 100644 index 000000000..cef4a1c75 Binary files /dev/null and b/files/assets/images/rDrama/brit.webp differ diff --git a/files/assets/js/casino.js b/files/assets/js/casino.js deleted file mode 100644 index 93aae1912..000000000 --- a/files/assets/js/casino.js +++ /dev/null @@ -1,350 +0,0 @@ -// Shared -function updatePlayerCoins(updated) { - if (updated.coins) { - document.getElementById("user-coins-amount").innerText = updated.coins; - } - - if (updated.procoins) { - document.getElementById("user-bux-amount").innerText = updated.procoins; - } -} - -// Slots -function pullSlots() { - const wager = document.getElementById("casinoSlotsBet").value; - const currency = document.querySelector( - 'input[name="casinoSlotsCurrency"]:checked' - ).value; - - document.getElementById("casinoSlotsBet").disabled = true; - document.getElementById("casinoSlotsPull").disabled = true; - - const xhr = new XMLHttpRequest(); - xhr.open("post", "/casino/slots"); - xhr.onload = handleSlotsResponse.bind(null, xhr); - - const form = new FormData(); - form.append("formkey", formkey()); - form.append("wager", wager); - form.append("currency", currency); - - xhr.send(form); -} - -function handleSlotsResponse(xhr) { - let response; - - try { - response = JSON.parse(xhr.response); - } catch (error) { - console.error(error); - } - - const succeeded = - xhr.status >= 200 && xhr.status < 300 && response && !response.error; - const slotsResult = document.getElementById("casinoSlotsResult"); - slotsResult.classList.remove("text-success", "text-danger"); - - if (succeeded) { - const { game_state, gambler } = response; - const state = JSON.parse(game_state); - const reels = Array.from(document.querySelectorAll(".reel")); - const symbols = state.symbols.split(","); - - for (let i = 0; i < 3; i++) { - reels[i].innerHTML = symbols[i]; - } - - slotsResult.style.visibility = "visible"; - slotsResult.innerText = state.text; - - if (state.text.includes("Won")) { - if (state.text.includes("Jackpot")) { - slotsResult.classList.add("text-warning"); - } else { - slotsResult.classList.add("text-success"); - } - } else if (state.text.includes("Lost")) { - slotsResult.classList.add("text-danger"); - } - - updatePlayerCoins(gambler); - } else { - slotsResult.style.visibility = "visible"; - slotsResult.innerText = response.error; - slotsResult.classList.add("text-danger"); - - console.error(response.error); - } - - document.getElementById("casinoSlotsBet").disabled = false; - document.getElementById("casinoSlotsPull").disabled = false; -} - -// Blackjack - -// Checking existing status onload. -// When the casino loads, look up the "blackjack status" of a player to either start a new game or continue an existing game. -if ( - document.readyState === "complete" || - (document.readyState !== "loading" && !document.documentElement.doScroll) -) { - checkBlackjackStatus(); -} else { - document.addEventListener("DOMContentLoaded", checkBlackjackStatus); -} - -function checkBlackjackStatus() { - const xhr = new XMLHttpRequest(); - xhr.open("get", "/casino/blackjack/status"); - xhr.onload = handleBlackjackStatusResponse.bind(null, xhr); - xhr.send(); -} - -function handleBlackjackStatusResponse(xhr) { - let response; - - try { - response = JSON.parse(xhr.response); - } catch (error) { - console.error(error); - } - - const succeeded = - xhr.status >= 200 && xhr.status < 300 && response && !response.error; - - if (succeeded) { - if (response.active) { - updateBlackjack(response.game_state); - } else { - updateBlackjackActions(null); - } - } else { - console.error("error"); - } -} - -// DOM Manipulation - -// When starting a new game or taking an action in an existing one, a new state will be returned, and the DOM must be updated. -function updateBlackjack(state) { - const { player, dealer, status } = state; - const lettersToSuits = { - S: "♠️", - H: "♥️", - C: "♣️", - D: "♦️", - "?": "?", - }; - const suitsToColors = { - "♠️": "black", - "♥️": "red", - "♣️": "black", - "♦️": "red", - "?": "black", - }; - - // Clear everything. - Array.from(document.querySelectorAll(".playing-card")).forEach((card) => { - card.innerText = ""; - card.style.color = "unset"; - card.classList.remove("dealt"); - }); - - // Show dealer cards. - const dealerSlots = Array.from( - document.querySelectorAll('.playing-card[data-who="dealer"]') - ); - for (let i = 0; i < dealer.length; i++) { - const slot = dealerSlots[i]; - - if (slot) { - // Technically, the dealer can use more than 5 cards, though it's rare. - // In that case, the result message is good enough. - // Thanks, Carp. 🐠 - slot.classList.add("dealt"); - - if (i > 0 && status === "active") { - break; - } - - const rank = dealer[i][0]; - const suit = lettersToSuits[dealer[i][1]]; - const card = rank + suit; - slot.innerText = card; - slot.style.color = suitsToColors[suit]; - } - } - - // Show player cards. - const playerSlots = Array.from( - document.querySelectorAll('.playing-card[data-who="player"]') - ); - for (let i = 0; i < player.length; i++) { - const slot = playerSlots[i]; - const rank = player[i][0]; - const suit = lettersToSuits[player[i][1]]; - const card = rank + suit; - slot.innerText = card; - slot.style.color = suitsToColors[suit]; - slot.classList.add("dealt"); - } - - updateBlackjackActions(state); - - if (status !== "active") { - revealBlackjackResult(state); - } -} - -function revealBlackjackResult(state) { - const blackjackResult = document.getElementById("casinoBlackjackResult"); - const lookup = { - bust: ["Bust. Didn't work out for you, did it?", "danger"], - push: ["Pushed. This whole hand never happened.", "secondary"], - insured_loss: ["Lost, but at least you had insurance.", "secondary"], - lost: ["Lost. That was pathetic.", "danger"], - won: ["Won. This time.", "success"], - blackjack: ["Blackjack! Must be your lucky day.", "warning"], - }; - const [resultText, resultClass] = lookup[state.status]; - - blackjackResult.style.visibility = "visible"; - blackjackResult.innerText = resultText; - blackjackResult.classList.add(`text-${resultClass}`); -} - -function buildBlackjackAction(id, method, title, fullWidth = false) { - return ` - - `; -} - -function clearBlackjackActions() { - const actionWrapper = document.getElementById("casinoBlackjackActions"); - actionWrapper.innerHTML = ""; -} - -function updateBlackjackActions(state) { - const actionWrapper = document.getElementById("casinoBlackjackActions"); - - clearBlackjackActions(); - - if (state && state.status === "active") { - document.getElementById("casinoBlackjackWager").style.display = "none"; - - const actionLookup = { - hit: buildBlackjackAction("casinoBlackjackHit", "hitBlackjack", "Hit"), - stay: buildBlackjackAction( - "casinoBlackjackStay", - "stayBlackjack", - "Stay" - ), - double_down: buildBlackjackAction( - "casinoBlackjackDouble", - "doubleBlackjack", - "Double Down" - ), - insure: buildBlackjackAction( - "casinoBlackjackInsure", - "insureBlackjack", - "Insure" - ), - }; - const actions = state.actions.map((action) => actionLookup[action]); - - actionWrapper.innerHTML = actions.join("\n"); - } else { - // Game is over, deal a new game. - showWagerWidget(); - - const deal = buildBlackjackAction( - "casinoBlackjackDeal", - "dealBlackjack", - "Deal", - true - ); - - actionWrapper.innerHTML = deal; - } -} - -function hideWagerWidget() { - document.getElementById("casinoBlackjackBet").disabled = true; - document.getElementById("casinoBlackjackDeal").disabled = true; - document.getElementById("casinoBlackjackWager").style.display = "none"; - document.getElementById("casinoBlackjackResult").style.visibility = "hidden"; -} - -function showWagerWidget() { - document.getElementById("casinoBlackjackWager").style.display = "flex"; - document.getElementById("casinoBlackjackBet").disabled = false; -} - -// Actions, Requests & Responses -function takeBlackjackAction(action, args = {}) { - const xhr = new XMLHttpRequest(); - xhr.open("post", "/casino/blackjack/action"); - xhr.onload = handleBlackjackResponse.bind(null, xhr); - - const form = new FormData(); - form.append("formkey", formkey()); - form.append("action", action); - - for (const [key, value] of Object.entries(args)) { - form.append(key, value); - } - - xhr.send(form); -} - -const dealBlackjack = () => { - const wager = document.getElementById("casinoBlackjackBet").value; - const currency = document.querySelector('input[name="casinoBlackjackCurrency"]:checked').value; - - hideWagerWidget(); - takeBlackjackAction("deal", { currency, wager }); -} -const hitBlackjack = takeBlackjackAction.bind(null, "hit"); -const stayBlackjack = takeBlackjackAction.bind(null, "stay"); -const doubleBlackjack = takeBlackjackAction.bind(null, "double_down"); -const insureBlackjack = takeBlackjackAction.bind(null, "insure"); - -function handleBlackjackResponse(xhr) { - let response; - - try { - response = JSON.parse(xhr.response); - } catch (error) { - console.error(error); - } - - const succeeded = - xhr.status >= 200 && xhr.status < 300 && response && !response.error; - const blackjackResult = document.getElementById("casinoBlackjackResult"); - blackjackResult.classList.remove("text-success", "text-danger", "text-warning"); - - if (succeeded) { - if (response.game_state) { - updateBlackjack(response.game_state); - } - - if (response.gambler) { - updatePlayerCoins(response.gambler); - } - } else { - blackjackResult.style.visibility = "visible"; - blackjackResult.innerText = response.error; - blackjackResult.classList.add("text-danger"); - - console.error(response.error); - } -} diff --git a/files/classes/casino_game.py b/files/classes/casino_game.py index bcc50a1b0..e8fcdd72b 100644 --- a/files/classes/casino_game.py +++ b/files/classes/casino_game.py @@ -1,7 +1,5 @@ from sqlalchemy import * from files.__main__ import Base -from files.helpers.lazy import lazy -from files.helpers.const import * import time diff --git a/files/classes/user.py b/files/classes/user.py index b295d179f..505233464 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -4,6 +4,7 @@ import pyotp from files.helpers.discord import remove_user from files.helpers.media import * from files.helpers.const import * +from files.helpers.twentyone import get_active_twentyone_game_state from files.helpers.sorting_and_time import * from .alts import Alt from .saves import * @@ -892,4 +893,9 @@ class User(Base): if self.truecoins >= 5000: return True if self.agendaposter: return True if self.patron: return True - return False \ No newline at end of file + return False + + @property + @lazy + def active_blackjack_game(self): + return json.dumps(get_active_twentyone_game_state(self)) diff --git a/files/helpers/blackjack.py b/files/helpers/blackjack.py deleted file mode 100644 index b259de101..000000000 --- a/files/helpers/blackjack.py +++ /dev/null @@ -1,327 +0,0 @@ -import json -from json.encoder import INFINITY -import random -from math import floor -from files.helpers.const import * -from files.classes.casino_game import Casino_Game -from files.classes.user import User -from flask import g - -deck_count = 4 -ranks = ("2", "3", "4", "5", "6", "7", "8", "9", "X", "J", "Q", "K", "A") -suits = ("S", "H", "C", "D") -minimum_bet = 5 -maximum_bet = INFINITY - -def build_game(gambler, currency_kind, wager): - casino_game = Casino_Game() - casino_game.user_id = gambler.id - casino_game.currency = currency_kind - casino_game.wager = wager - casino_game.winnings = 0 - casino_game.kind = 'blackjack' - casino_game.game_state = json.dumps(build_initial_state()) - g.db.add(casino_game) - g.db.flush() - - -def build_initial_state(): - player, dealer, deck = deal_initial_cards() - state = { - "player": player, - "dealer": dealer, - "deck": deck, - "actions": [], - "insurance": False, - "doubled_down": False, - "status": "active" - } - - state['actions'] = determine_actions(state) - - return state - - -def save_game_state(game, new_state): - game.game_state = json.dumps(new_state) - g.db.add(game) - - -def get_active_game(gambler): - game = g.db.query(Casino_Game) \ - .filter(Casino_Game.active == True, - Casino_Game.kind == 'blackjack', - Casino_Game.user_id == gambler.id).one_or_none() - - if game: - game_state = json.loads(game.game_state) - return game, game_state, get_safe_game_state(game_state) - else: - return None, None, None - - -def get_safe_game_state(game_state): - return { - "player": game_state['player'], - "dealer": [game_state['dealer'][0], "?"], - "actions": game_state['actions'], - "insurance": game_state['insurance'], - "doubled_down": game_state['doubled_down'], - "status": game_state['status'] - } - - -def apply_blackjack_result(gambler): - game, game_state, _ = get_active_game(gambler) - - if game: - result = game_state['status'] - - if result == 'push' or result == 'insured_loss': - reward = game.wager - elif result == 'won': - reward = game.wager * 2 - elif result == 'blackjack': - reward = floor(game.wager * 5/2) - else: - reward = 0 - - if reward: - currency_value = int(getattr(gambler, game.currency, 0)) - setattr(gambler, game.currency, currency_value + reward) - gambler.winnings += reward - game.winnings += reward - - game.active = False - g.db.add(game) - -# region Actions -def gambler_dealt(gambler, currency, wager): - existing_game, _, _ = get_active_game(gambler) - - if not existing_game: - over_min = wager >= minimum_bet - under_max = wager <= maximum_bet - using_dramacoin = currency == "dramacoin" - using_marseybux = not using_dramacoin - has_proper_funds = (using_dramacoin and gambler.coins >= wager) or ( - using_marseybux and gambler.procoins >= wager) - currency_prop = "coins" if using_dramacoin else "procoins" - currency_value = getattr(gambler, currency_prop, 0) - - if (over_min and under_max and has_proper_funds): - # Start the game. - build_game(gambler, currency_prop, wager) - game, game_state, safe_state = get_active_game(gambler) - player_value = get_hand_value(game_state['player']) - dealer_value = get_hand_value(game_state['dealer']) - - # Charge the gambler for the game, reduce their winnings. - setattr(gambler, currency_prop, currency_value - wager) - gambler.winnings -= wager - game.winnings -= wager - - # In two cases, the game is instantly over. - instantly_over = False - if player_value == 21 and dealer_value == 21: - instantly_over = True - game_state["status"] = 'push' - save_game_state(game, game_state) - apply_blackjack_result(gambler) - elif player_value == 21: - instantly_over = True - game_state["status"] = 'blackjack' - save_game_state(game, game_state) - apply_blackjack_result(gambler) - - g.db.flush() - - if instantly_over: - return True, game_state - else: - return True, safe_state - - -def gambler_hit(gambler): - game, game_state, safe_state = get_active_game(gambler) - - if game: - player = game_state['player'] - deck = game_state['deck'] - doubled_down = game_state['doubled_down'] - player.append(deck.pop(0)) - player_value = get_hand_value(player) - went_bust = player_value == -1 - five_card_charlied = len(player) >= 5 - - if went_bust: - game_state['status'] = 'bust' - save_game_state(game, game_state) - apply_blackjack_result(gambler) - elif five_card_charlied: - game_state['status'] = 'won' - save_game_state(game, game_state) - apply_blackjack_result(gambler) - else: - save_game_state(game, game_state) - - if doubled_down or player_value == 21: - forced_stay_success, forced_stay_state = gambler_stayed(gambler) - return forced_stay_success, forced_stay_state - else: - _, _, safe_state = get_active_game(gambler) - return True, safe_state - else: - return False, safe_state - - -def gambler_stayed(gambler): - game, game_state, safe_state = get_active_game(gambler) - - if game: - player = game_state['player'] - dealer = game_state['dealer'] - deck = game_state['deck'] - insured = game_state['insurance'] - - player_value = get_hand_value(player) - dealer_value = get_hand_value(dealer) - - if dealer_value == 21 and insured: - game_state["status"] = 'insured_loss' - save_game_state(game, game_state) - apply_blackjack_result(gambler) - else: - while dealer_value < 17 and dealer_value != -1: - next = deck.pop(0) - dealer.append(next) - dealer_value = get_hand_value(dealer) - - if player_value > dealer_value or dealer_value == -1: - game_state["status"] = 'won' - elif dealer_value > player_value: - game_state["status"] = 'lost' - else: - game_state["status"] = 'push' - - save_game_state(game, game_state) - apply_blackjack_result(gambler) - - return True, game_state - else: - return False, safe_state - - -def gambler_doubled_down(gambler): - game, game_state, safe_state = get_active_game(gambler) - - if game and not game_state['doubled_down']: - currency_value = getattr(gambler, game.currency, 0) - - if (currency_value < game.wager): - return False, game_state - - setattr(gambler, game.currency, currency_value - game.wager) - gambler.winnings -= game.wager - game.winnings -= game.wager - - game.wager *= 2 - game_state['doubled_down'] = True - save_game_state(game, game_state) - - g.db.flush() - - last_hit_success, last_hit_state = gambler_hit(gambler) - return last_hit_success, last_hit_state - else: - return False, safe_state - - -def gambler_purchased_insurance(gambler): - game, game_state, safe_state = get_active_game(gambler) - - if game and not game_state['insurance']: - insurance_cost = game.wager / 2 - currency_value = getattr(gambler, game.currency, 0) - - if (currency_value < insurance_cost): - return False, game_state - - setattr(gambler, game.currency, currency_value - insurance_cost) - gambler.winnings -= insurance_cost - game.winnings -= insurance_cost - - game_state['insurance'] = True - game_state['actions'] = determine_actions(game_state) - save_game_state(game, game_state) - - _, _, safe_state = get_active_game(gambler) - return True, safe_state - else: - return False, safe_state - -# endregion - -# region Utilities - - -def shuffle(x): - random.shuffle(x) - return x - - -def determine_actions(state): - actions = ['hit', 'stay', 'double_down'] - - if (state['dealer'][0][0] == "A" and not state['insurance']): - actions.append('insure') - - return actions - - -def deal_initial_cards(): - deck = shuffle( - [rank + suit for rank in ranks for suit in suits for _ in range(deck_count)]) - p1, d1, p2, d2, *rest_of_deck = deck - return [p1, p2], [d1, d2], rest_of_deck - - -def get_card_value(card): - rank = card[0] - return 0 if rank == "A" else min(ranks.index(rank) + 2, 10) - - -def get_hand_value(hand): - without_aces = sum(map(get_card_value, hand)) - ace_count = sum("A" in c for c in hand) - possibilities = [] - - for i in range(ace_count + 1): - value = without_aces + (ace_count - i) + i * 11 - possibilities.append(-1 if value > 21 else value) - - return max(possibilities) - -def purge_bad_games(): - # If for whatever reason a game is marked as active but has concluded, this will clear it up. - games = g.db.query(Casino_Game) \ - .filter(Casino_Game.active == True, - Casino_Game.kind == 'blackjack').all() - - for game in games: - game_state = json.loads(game.game_state) - - if (game_state['status'] != "active"): - game.active = False - g.db.add(game) - - # Victims of this status should have their currency refunded. - user = g.db.query(User).filter(User.id == game.user_id).one() - - user.winnings += game.wager - currencyBeforeFix = getattr(user, game.currency, 0) - setattr(user, game.currency, currencyBeforeFix + game.wager) - g.db.add(user) - - g.db.commit() - # endregion diff --git a/files/helpers/casino.py b/files/helpers/casino.py new file mode 100644 index 000000000..50a42a5bb --- /dev/null +++ b/files/helpers/casino.py @@ -0,0 +1,84 @@ +from files.__main__ import app, limiter, db_session +from files.helpers.wrappers import * +from files.helpers.alerts import * +from files.helpers.get import * +from files.helpers.const import * +from files.helpers.wrappers import * + + +def get_game_feed(game): + games = g.db.query(Casino_Game) \ + .filter(Casino_Game.active == False, Casino_Game.kind == game) \ + .order_by(Casino_Game.created_utc.desc()).limit(30).all() + + def format_game(game): + user = g.db.query(User).filter(User.id == game.user_id).one() + wonlost = 'lost' if game.winnings < 0 else 'won' + + return { + "user": user.username, + "won_or_lost": wonlost, + "amount": abs(game.winnings), + "currency": game.currency + } + + return list(map(format_game, games)) + + +def get_game_leaderboard(game): + timestamp_24h_ago = time.time() - 86400 + biggest_win_all_time = g.db.query(Casino_Game.user_id, User.username, Casino_Game.currency, Casino_Game.winnings).select_from( + Casino_Game).join(User).order_by(Casino_Game.winnings.desc()).filter(Casino_Game.kind == game).limit(1).one_or_none() + + biggest_win_last_24h = g.db.query(Casino_Game.user_id, User.username, Casino_Game.currency, Casino_Game.winnings).select_from( + Casino_Game).join(User).order_by(Casino_Game.winnings.desc()).filter(Casino_Game.kind == game, Casino_Game.created_utc > timestamp_24h_ago).limit(1).one_or_none() + + biggest_loss_all_time = g.db.query(Casino_Game.user_id, User.username, Casino_Game.currency, Casino_Game.winnings).select_from( + Casino_Game).join(User).order_by(Casino_Game.winnings.asc()).filter(Casino_Game.kind == game).limit(1).one_or_none() + + biggest_loss_last_24h = g.db.query(Casino_Game.user_id, User.username, Casino_Game.currency, Casino_Game.winnings).select_from( + Casino_Game).join(User).order_by(Casino_Game.winnings.asc()).filter(Casino_Game.kind == game, Casino_Game.created_utc > timestamp_24h_ago).limit(1).one_or_none() + + if not biggest_win_all_time: + biggest_win_all_time = [None, None, None, 0] + + if not biggest_win_last_24h: + biggest_win_last_24h = [None, None, None, 0] + + if not biggest_loss_all_time: + biggest_loss_all_time = [None, None, None, 0] + + if not biggest_loss_last_24h: + biggest_loss_last_24h = [None, None, None, 0] + + + return { + "all_time": { + "biggest_win": { + "user": biggest_win_all_time[1], + "currency": biggest_win_all_time[2], + "amount": biggest_win_all_time[3] + }, + "biggest_loss": { + "user": biggest_loss_all_time[1], + "currency": biggest_loss_all_time[2], + "amount": abs(biggest_loss_all_time[3]) + } + }, + "last_24h": { + "biggest_win": { + "user": biggest_win_last_24h[1], + "currency": biggest_win_last_24h[2], + "amount": biggest_win_last_24h[3] + }, + "biggest_loss": { + "user": biggest_loss_last_24h[1], + "currency": biggest_loss_last_24h[2], + "amount": abs(biggest_loss_last_24h[3]) + } + } + } + + +def get_game_stats_for_player(player): + pass diff --git a/files/helpers/slots.py b/files/helpers/slots.py index 229077477..fd25d86d4 100644 --- a/files/helpers/slots.py +++ b/files/helpers/slots.py @@ -18,24 +18,19 @@ payout_to_symbols = { def casino_slot_pull(gambler, wager_value, currency): over_min = wager_value >= minimum_bet under_max = wager_value <= maximum_bet - using_dramacoin = currency == "dramacoin" - using_marseybux = not using_dramacoin - has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or ( - using_marseybux and gambler.procoins >= wager_value) - currency_prop = "coins" if using_dramacoin else "procoins" - currency_value = getattr(gambler, currency_prop, 0) + currency_value = getattr(gambler, currency) + has_proper_funds = currency_value >= wager_value if (over_min and under_max and has_proper_funds): - setattr(gambler, currency_prop, currency_value - wager_value) + setattr(gambler, currency, currency_value - wager_value) gambler.winnings -= wager_value payout = determine_payout() reward = wager_value * payout - currency_value = getattr(gambler, currency_prop, 0) - setattr(gambler, currency_prop, currency_value + reward) + currency_value = getattr(gambler, currency, 0) + setattr(gambler, currency, currency_value + reward) gambler.winnings += reward - symbols = build_symbols(payout) text = build_text(wager_value, payout, currency) @@ -46,7 +41,7 @@ def casino_slot_pull(gambler, wager_value, currency): casino_game = Casino_Game() casino_game.active = False casino_game.user_id = gambler.id - casino_game.currency = currency_prop + casino_game.currency = currency casino_game.wager = wager_value casino_game.winnings = reward - wager_value casino_game.kind = 'slots' diff --git a/files/helpers/twentyone.py b/files/helpers/twentyone.py new file mode 100644 index 000000000..ca181506d --- /dev/null +++ b/files/helpers/twentyone.py @@ -0,0 +1,378 @@ +import json +from locale import currency +from math import floor +import random +from functools import reduce +from enum import Enum +from files.classes.casino_game import Casino_Game +from flask import g + + +class BlackjackStatus(str, Enum): + PLAYING = "PLAYING" + STAYED = "STAYED" + PUSHED = "PUSHED" + WON = "WON" + LOST = "LOST" + BLACKJACK = "BLACKJACK" + + +class BlackjackAction(str, Enum): + DEAL = "DEAL" + HIT = "HIT" + STAY = "STAY" + DOUBLE_DOWN = "DOUBLE_DOWN" + BUY_INSURANCE = "BUY_INSURANCE" + + +ranks = ("2", "3", "4", "5", "6", "7", "8", "9", "X", "J", "Q", "K", "A") +suits = ("S", "H", "C", "D") +deck = [rank + suit for rank in ranks for suit in suits] +deck_count = 4 +minimum_bet = 5 + + +def get_initial_state(): + return { + "player": [], + "player_value": 0, + "dealer": [], + "dealer_value": 0, + "player_bought_insurance": False, + "player_doubled_down": False, + "status": BlackjackStatus.PLAYING, + "actions": [BlackjackAction.DEAL], + "wager": { + "amount": 0, + "currency": "coins" + }, + "payout": 0 + } + + +def build_casino_game(gambler, wager, currency): + initial_state = get_initial_state() + initial_state['wager']['amount'] = wager + initial_state['wager']['currency'] = currency + + casino_game = Casino_Game() + casino_game.user_id = gambler.id + casino_game.currency = currency + casino_game.wager = wager + casino_game.winnings = 0 + casino_game.kind = 'blackjack' + casino_game.game_state = json.dumps(initial_state) + casino_game.active = True + g.db.add(casino_game) + + return casino_game + + +def get_active_twentyone_game(gambler): + return g.db.query(Casino_Game).filter( + Casino_Game.active == True, + Casino_Game.kind == 'blackjack', + Casino_Game.user_id == gambler.id).one_or_none() + + +def get_active_twentyone_game_state(gambler): + active_game = get_active_twentyone_game(gambler) + full_state = json.loads(active_game.game_state) + return remove_exploitable_information(full_state) + + +def charge_gambler(gambler, amount, currency): + currency_gambler_holds = getattr(gambler, currency) + can_afford = currency_gambler_holds >= amount + + if not can_afford: + raise Exception("Gambler cannot afford charge.") + + setattr(gambler, currency, currency_gambler_holds - amount) + g.db.add(gambler) + + +def create_new_game(gambler, wager, currency): + existing_game = get_active_twentyone_game(gambler) + over_minimum_bet = wager >= minimum_bet + + if existing_game: + raise Exception("Gambler already has a game in progress.") + + if not over_minimum_bet: + raise Exception(f"Gambler must bet over {minimum_bet} {currency}.") + + try: + charge_gambler(gambler, wager, currency) + new_game = build_casino_game(gambler, wager, currency) + g.db.add(new_game) + g.db.commit() + except: + raise Exception(f"Gambler cannot afford to bet {wager} {currency}.") + + +def handle_blackjack_deal(state): + deck = build_deck(state) + first = deck.pop() + second = deck.pop() + third = deck.pop() + fourth = deck.pop() + state['player'] = [first, third] + state['dealer'] = [second, fourth] + + return state + + +def handle_blackjack_hit(state): + deck = build_deck(state) + next_card = deck.pop() + state['player'].append(next_card) + + return state + + +def handle_blackjack_stay(state): + state['status'] = BlackjackStatus.STAYED + + return state + + +def handle_blackjack_double_down(state): + state['player_doubled_down'] = True + state = handle_blackjack_hit(state) + state = handle_blackjack_stay(state) + + return state + + +def handle_blackjack_buy_insurance(state): + state['player_bought_insurance'] = True + + return state + + +def check_for_completion(state): + after_initial_deal = len( + state['player']) == 2 and len(state['dealer']) == 2 + player_hand_value = get_value_of_hand(state['player']) + dealer_hand_value = get_value_of_hand(state['dealer']) + + # Both player and dealer were initially dealt 21: Push. + if after_initial_deal and player_hand_value == 21 and dealer_hand_value == 21: + state['status'] = BlackjackStatus.PUSHED + return True, state + + # Player was originally dealt 21, dealer was not: Blackjack. + if after_initial_deal and player_hand_value == 21: + state['status'] = BlackjackStatus.BLACKJACK + return True, state + + # Player went bust: Lost. + if player_hand_value == -1: + state['status'] = BlackjackStatus.LOST + return True, state + + # Player chose to stay: Deal rest for dealer then determine winner. + if state['status'] == BlackjackStatus.STAYED: + deck = build_deck(state) + + while dealer_hand_value < 17 and dealer_hand_value != -1: + next_card = deck.pop() + state['dealer'].append(next_card) + dealer_hand_value = get_value_of_hand(state['dealer']) + + if player_hand_value > dealer_hand_value or dealer_hand_value == -1: + state['status'] = BlackjackStatus.WON + elif dealer_hand_value > player_hand_value: + state['status'] = BlackjackStatus.LOST + else: + state['status'] = BlackjackStatus.PUSHED + + state['player_value'] = get_value_of_hand(state['player']) + state['dealer_value'] = get_value_of_hand(state['dealer']) + + return True, state + + return False, state + + +def does_insurance_apply(state): + dealer = state['dealer'] + dealer_hand_value = get_value_of_hand(dealer) + dealer_first_card_ace = dealer[0][0] == 'A' + dealer_never_hit = len(dealer) == 2 + return dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit + + +def can_purchase_insurance(state): + dealer = state['dealer'] + dealer_first_card_ace = dealer[0][0] == 'A' + dealer_never_hit = len(dealer) == 2 + return dealer_first_card_ace and dealer_never_hit and not state['player_bought_insurance'] + + +def can_double_down(state): + player = state['player'] + player_hand_value = get_value_of_hand(player) + player_never_hit = len(player) == 2 + return player_hand_value in (10, 11) and player_never_hit + + +def handle_payout(gambler, state, game): + status = state['status'] + payout = 0 + + if status == BlackjackStatus.BLACKJACK: + game.winnings = floor(game.wager * 3/2) + payout = game.wager + game.winnings + elif status == BlackjackStatus.WON: + game.winnings = game.wager + payout = game.wager * 2 + elif status == BlackjackStatus.LOST: + dealer = state['dealer'] + dealer_first_card_ace = dealer[0][0] == 'A' + dealer_never_hit = len(dealer) == 2 + dealer_hand_value = get_value_of_hand(dealer) == 21 + insurance_applies = dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit + + if insurance_applies and state['player_bought_insurance']: + game.winnings = 0 + payout = game.wager + else: + game.winnings = -game.wager + payout = 0 + elif status == BlackjackStatus.PUSHED: + game.winnings = 0 + payout = game.wager + else: + raise Exception("Attempted to payout a game that has not finished.") + + currency_gambler_holds = getattr(gambler, game.currency) + setattr(gambler, game.currency, currency_gambler_holds + payout) + + game.active = False + g.db.add(game) + g.db.add(gambler) + + return payout + + +def remove_exploitable_information(state): + safe_state = state + safe_state['dealer'][1] = '?' + safe_state['dealer_value'] = '?' + return safe_state + + +action_handlers = { + BlackjackAction.DEAL: handle_blackjack_deal, + BlackjackAction.HIT: handle_blackjack_hit, + BlackjackAction.STAY: handle_blackjack_stay, + BlackjackAction.DOUBLE_DOWN: handle_blackjack_double_down, + BlackjackAction.BUY_INSURANCE: handle_blackjack_buy_insurance, +} + + +def dispatch_action(gambler, action): + game = get_active_twentyone_game(gambler) + handler = action_handlers[action] + + if not game: + raise Exception( + 'Gambler has no active blackjack game.') + if not handler: + raise Exception( + f'Illegal action {action} passed to Blackjack#dispatch_action.') + + state = json.loads(game.game_state) + + if action == BlackjackAction.BUY_INSURANCE: + if not can_purchase_insurance(state): + raise Exception("Insurance cannot be purchased.") + + charge_gambler(gambler, floor(game.wager / 2), game.currency) + if action == BlackjackAction.DOUBLE_DOWN: + if not can_double_down(state): + raise Exception("Cannot double down.") + + charge_gambler(gambler, game.wager, game.currency) + game.wager *= 2 + + new_state = handler(state) + new_state['player_value'] = get_value_of_hand(new_state['player']) + new_state['dealer_value'] = get_value_of_hand(new_state['dealer']) + new_state['actions'] = get_available_actions(new_state) + + game.game_state = json.dumps(new_state) + g.db.add(game) + + game_over, final_state = check_for_completion(new_state) + + if game_over: + payout = handle_payout(gambler, final_state, game) + final_state['actions'] = [BlackjackAction.DEAL] + final_state['payout'] = payout + return final_state + else: + safe_state = remove_exploitable_information(new_state) + return safe_state + + +def shuffle(collection): + random.shuffle(collection) + return collection + + +def build_deck(state): + card_counts = {} + + for card in deck: + card_counts[card] = deck_count + + cards_already_dealt = state['player'].copy() + cards_already_dealt.extend(state['dealer'].copy()) + + for card in cards_already_dealt: + card_counts[card] = card_counts[card] - 1 + + deck_without_already_dealt_cards = [] + + for card in deck: + amount = card_counts[card] + + for _ in range(amount): + deck_without_already_dealt_cards.append(card) + + return shuffle(deck_without_already_dealt_cards) + + +def get_value_of_card(card): + rank = card[0] + return 0 if rank == "A" else min(ranks.index(rank) + 2, 10) + + +def get_value_of_hand(hand): + without_aces = sum(map(get_value_of_card, hand)) + ace_count = sum("A" in c for c in hand) + possibilities = [] + + for i in range(ace_count + 1): + value = without_aces + (ace_count - i) + i * 11 + possibilities.append(-1 if value > 21 else value) + + return max(possibilities) + +def get_available_actions(state): + actions = [] + + if state['status'] == BlackjackStatus.PLAYING: + actions.append(BlackjackAction.HIT) + actions.append(BlackjackAction.STAY) + + if can_double_down(state): + actions.append(BlackjackAction.DOUBLE_DOWN) + + if can_purchase_insurance(state): + actions.append(BlackjackAction.BUY_INSURANCE) + + return actions \ No newline at end of file diff --git a/files/routes/casino.py b/files/routes/casino.py index 9b6d4f769..c0d861d8f 100644 --- a/files/routes/casino.py +++ b/files/routes/casino.py @@ -1,140 +1,148 @@ -import json from files.__main__ import app from files.helpers.wrappers import * from files.helpers.alerts import * from files.helpers.get import * from files.helpers.const import * from files.helpers.wrappers import * -from files.helpers.blackjack import * from files.helpers.slots import * from files.helpers.lottery import * +from files.helpers.casino import * +from files.helpers.twentyone import * @app.get("/casino") @auth_required def casino(v): - if v.rehab: return {"error": "You are under Rehab award effect!"} + if v.rehab: return render_template("casino/rehab.html", v=v) + return render_template("casino.html", v=v) + + +@app.get("/lottershe") +@auth_required +def lottershe(v): + if v.rehab: return render_template("casino/rehab.html", v=v) + participants = get_users_participating_in_lottery() + return render_template("lottery.html", v=v, participants=participants) + - participants = get_users_participating_in_lottery() - return render_template("casino.html", v=v, participants=participants) @app.get("/casino/") @auth_required def casino_game_page(v, game): - if v.rehab: return {"error": "You are under Rehab award effect!"} + if v.rehab: return render_template("casino/rehab.html", v=v) + + feed = json.dumps(get_game_feed(game)) + leaderboard = json.dumps(get_game_leaderboard(game)) - return render_template( - f"casino/{game}_screen.html", - v=v, - game=game - ) + return render_template( + f"casino/{game}_screen.html", + v=v, + game=game, + feed=feed, + leaderboard=leaderboard + ) + + +@app.get("/casino//feed") +@limiter.limit("1/second;50/minute;600/hour;12000/day") +@auth_required +def casino_game_feed(v, game): + feed = get_game_feed(game) + return {"feed": feed} @app.post("/casino/slots") @limiter.limit("3/second;30/minute;600/hour;12000/day") @auth_required def pull_slots(v): - if v.rehab: return {"error": "You are under Rehab award effect!"} + if v.rehab: return {"error": "You are under Rehab award effect!"} - try: - wager = int(request.values.get("wager")) - except: - return {"error": "Invalid wager."} + try: + wager = int(request.values.get("wager")) + except: + return {"error": "Invalid wager."} - try: - currency = request.values.get("currency") - except: - return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."} + try: + currency = request.values.get("currency") + except: + return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."} - if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): - return {"error": f"Not enough {currency} to make that bet."} + if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): + return {"error": f"Not enough {currency} to make that bet."} - success, game_state = casino_slot_pull(v, wager, currency) + success, game_state = casino_slot_pull(v, wager, currency) - if success: - return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} - else: - return {"error": f"Wager must be more than 5 {currency}."} + if success: + return {"game_state": game_state, "gambler": {"coins": v.coins, "procoins": v.procoins}} + else: + return {"error": f"Wager must be more than 5 {currency}."} -@app.get("/casino/blackjack/status") -@limiter.limit("3/second;30/minute;600/hour;12000/day") +@app.post("/casino/twentyone/deal") @auth_required -def get_player_blackjack_status(v): - if v.rehab: return {"error": "You are under Rehab award effect!"} +def blackjack_deal_to_player(v): + if v.rehab: return {"error": "You are under Rehab award effect!"} - game, _, safe_state = get_active_game(v) + try: + wager = int(request.values.get("wager")) + currency = request.values.get("currency") + create_new_game(v, wager, currency) + state = dispatch_action(v, BlackjackAction.DEAL) + feed = get_game_feed('blackjack') - if game: - return { "active": True, "game_state": safe_state } - else: - return { "active": False } + return {"success": True, "state": state, "feed": feed} + except Exception as error: + print(error) + return {"error": "Unable to deal a new game."} -@app.post("/casino/blackjack/action") -@limiter.limit("3/second;30/minute;600/hour;12000/day") +@app.post("/casino/twentyone/hit") @auth_required -def player_took_blackjack_action(v): - if v.rehab: return {"error": "You are under Rehab award effect!"} +def blackjack_player_hit(v): + if v.rehab: return {"error": "You are under Rehab award effect!"} + + try: + state = dispatch_action(v, BlackjackAction.HIT) + feed = get_game_feed('blackjack') + return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} + except: + return {"error": "Unable to hit."} - try: - action = request.values.get("action") - except: - return { "error": "Invalid action." } - was_successful = False - state = None - - if action == 'deal': - try: - currency = request.values.get("currency") - wager = int(request.values.get("wager")) - except: - return { "error": "Missing either currency or wager values." } - - existing_game, _, _ = get_active_game(v) - - if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): - return {"error": f"Not enough {currency} to make that bet."} - elif existing_game: - return { "error": "Cannot start a new game while an existing game persists." } - else: - deal = gambler_dealt(v, currency, wager) - if not deal: return { "error": "Cannot start a new game while an existing game persists." } - success, game_state = deal - was_successful = success - state = game_state - elif action == 'hit': - success, game_state = gambler_hit(v) - was_successful = success - state = game_state - elif action == 'stay': - success, game_state = gambler_stayed(v) - was_successful = success - state = game_state - elif action == 'double_down': - success, game_state = gambler_doubled_down(v) - was_successful = success - state = game_state - elif action == 'insure': - success, game_state = gambler_purchased_insurance(v) - was_successful = success - state = game_state - - if was_successful: - return { - "active": True, - "game_state": state, - "gambler": { "coins": v.coins, "procoins": v.procoins } - } - else: - return { "active": False } - -@app.post("/casino/blackjack/purge") +@app.post("/casino/twentyone/stay") @auth_required -def fix_blackjack_games(v): - if v.admin_level < 3: - return { "success": False, "error": "Insufficient permissions." } - else: - purge_bad_games() - return { "success": True, "message": "Successfully purged bad blackjack games." } +def blackjack_player_stay(v): + if v.rehab: return {"error": "You are under Rehab award effect!"} + + try: + state = dispatch_action(v, BlackjackAction.STAY) + feed = get_game_feed('blackjack') + return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} + except: + return {"error": "Unable to stay."} + + +@app.post("/casino/twentyone/double-down") +@auth_required +def blackjack_player_doubled_down(v): + if v.rehab: return {"error": "You are under Rehab award effect!"} + + try: + state = dispatch_action(v, BlackjackAction.DOUBLE_DOWN) + feed = get_game_feed('blackjack') + return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} + except: + return {"error": "Unable to double down."} + + +@app.post("/casino/twentyone/buy-insurance") +@auth_required +def blackjack_player_bought_insurance(v): + if v.rehab: return {"error": "You are under Rehab award effect!"} + + try: + state = dispatch_action(v, BlackjackAction.BUY_INSURANCE) + feed = get_game_feed('blackjack') + return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} + except: + return {"error": "Unable to buy insurance."} diff --git a/files/routes/comments.py b/files/routes/comments.py index 70783360f..6c7b6d9dc 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -4,7 +4,6 @@ from files.helpers.media import * from files.helpers.const import * from files.helpers.regex import * from files.helpers.slots import * -from files.helpers.blackjack import * from files.helpers.treasure import * from files.helpers.actions import * from files.helpers.get import * @@ -329,11 +328,6 @@ def comment(v): g.db.add(c) g.db.flush() - if blackjack and any(i in c.body.lower() for i in blackjack.split()): - v.shadowbanned = 'AutoJanny' - notif = Notification(comment_id=c.id, user_id=CARP_ID) - g.db.add(notif) - if c.level == 1: c.top_comment_id = c.id else: c.top_comment_id = parent.top_comment_id @@ -693,14 +687,6 @@ def edit_comment(cid, v): c.body = body[:10000] c.body_html = body_html - if blackjack and any(i in c.body.lower() for i in blackjack.split()): - v.shadowbanned = 'AutoJanny' - g.db.add(v) - notif = g.db.query(Notification).filter_by(comment_id=c.id, user_id=CARP_ID).one_or_none() - if not notif: - notif = Notification(comment_id=c.id, user_id=CARP_ID) - g.db.add(notif) - if v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and c.post.sub != 'chudrama': return {"error": f'You have to include "{AGENDAPOSTER_PHRASE}" in your comment!'}, 403 diff --git a/files/templates/casino.html b/files/templates/casino.html index 7778e963c..d238dc7f7 100644 --- a/files/templates/casino.html +++ b/files/templates/casino.html @@ -1,244 +1,43 @@ -{% extends "default.html" %} {% block content %} - - +{% extends "default.html" %} - -
- -
-
- Slots -
-
-
-
- -
-
-
-
- coin -
-
- coin -
-
- coin -
-
-
-   -
-
-
- -
-
-
-
Enter Bet
-
- -
-
+{# Title (~25char max), Description (~80char max), +Icon (fa-foo-bar), Color (#ff0000), URL (/post/12345/) #} +{%- set GAME_INDEX = [] -%} -
-
- - -
-
- - -
-
-
+{%- if SITE_NAME == 'rDrama' -%} + {%- do GAME_INDEX.extend([ + ( + 'Slots', + 'Today\'s your lucky day', + 'fa-dollar-sign', '#666', + '/casino/slots', + ), + ( + 'Blackjack', + 'Twenty one ways to change your life', + 'fa-cards', '#333', + '/casino/blackjack', + ), + ( + 'Lottershe', + 'Can\'t win if you don\'t play.', + 'fa-ticket', '#888', + '/lottershe', + ) + ])-%} +{%- endif -%} - +{% block content %} +

rDrama.net Casino

+
+ {% for game in GAME_INDEX %} + + -
-
-
- - -
-
- Blackjack -
-
-
-
- -
-
-
-
-
- Dealer -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Player -
-
-
-
-
-
- -
-
-
-
Enter Bet
-
- -
-
- -
-
- - -
-
- - -
-
-
- -
-
-
-
-
-
-
{% include "lottery.html" %}
- {% endblock %} +
+ {% endfor %}
+{% endblock %} \ No newline at end of file diff --git a/files/templates/casino/blackjack_screen.html b/files/templates/casino/blackjack_screen.html new file mode 100644 index 000000000..38776c086 --- /dev/null +++ b/files/templates/casino/blackjack_screen.html @@ -0,0 +1,304 @@ +{% extends "casino/game_screen.html" %} + +{% block script %} + +{% endblock %} + +{% block screen %} +
+
+
+{% endblock %} + +{% block actions %} + + +
+ + + + + +
+{% endblock %} + +{% block leaders %} +Blackjack +{% endblock %} + +{% block feed %} +Blackjack +{% endblock %} \ No newline at end of file diff --git a/files/templates/casino/game_screen.html b/files/templates/casino/game_screen.html new file mode 100644 index 000000000..43931ce82 --- /dev/null +++ b/files/templates/casino/game_screen.html @@ -0,0 +1,468 @@ +{% extends "default.html" %} {% block title %} +{{game}} +{% endblock %} {% block content %} + + + + +{% block script %} {% endblock %} + + + +
+
+
+

{{game}}

+
+
+
{% block screen %} {% endblock %}
+
+ +
+
+
+
+
+
Wager
+
+
+ +
+
+
+
Currency
+
+
+
+ + + + +
+
+
+
+
+
+
Actions
+
+
+ {% block actions %} {% endblock %} +
+
+
+
Feed
+
+
+
    + +
    +
    +
    +
    Leaders
    +
    +
    +
    + +
    +
    + + +
    +
    + Biggest Winner (All Time) +

    -

    +
    +
    + +
    +
    + + +
    +
    + Biggest Winner (Last 24h) +

    -

    +
    +
    + +
    +
    + + +
    +
    + Biggest Loser (Last 24h) +

    -

    +
    +
    + +
    +
    + + +
    +
    + Biggest Loser (All Time) +

    -

    +
    +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/files/templates/casino/rehab.html b/files/templates/casino/rehab.html new file mode 100644 index 000000000..289b4aec0 --- /dev/null +++ b/files/templates/casino/rehab.html @@ -0,0 +1,11 @@ +{% extends "default.html" %} {% block content %} +
    +
    +

    No one was there for Britney…

    +
    +
    +

    …but we’re here for you. You’ve been checked into Rehab.

    +
    + +
    +{% endblock %} \ No newline at end of file diff --git a/files/templates/casino/slots_screen.html b/files/templates/casino/slots_screen.html new file mode 100644 index 000000000..0fc88c555 --- /dev/null +++ b/files/templates/casino/slots_screen.html @@ -0,0 +1,125 @@ +{% extends "casino/game_screen.html" %} {% block result %} N/A {% endblock %} + +{% block script %} + +{% endblock %} + +{% block screen %} + + +
    +
    + coin +
    +
    + coin +
    +
    + coin +
    +
    +{% endblock %} + +{% block actions %} +
    + +
    +{% endblock %} diff --git a/files/templates/lottery.html b/files/templates/lottery.html index fc6133e7c..62992bf81 100644 --- a/files/templates/lottery.html +++ b/files/templates/lottery.html @@ -1,4 +1,5 @@ -
    +{% extends "default.html" %} {% block content %} +
    @@ -187,3 +188,5 @@
    + +{% endblock %} \ No newline at end of file