forked from rDrama/rDrama
1
0
Fork 0

use tabs, not spaces

master
Aevann1 2022-10-29 01:39:31 +02:00
parent 065a024639
commit 5719688178
25 changed files with 2124 additions and 2124 deletions

View File

@ -144,8 +144,8 @@ blockquote a {
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
h5.post-title a:visited { h5.post-title a:visited {

View File

@ -32,7 +32,7 @@
.ricardo img { .ricardo img {
max-height: 15rem !important; max-height: 15rem !important;
max-height: min(30vw,15rem) !important; max-height: min(30vw,15rem) !important;
} }
.ricardoleft { .ricardoleft {

View File

@ -1,70 +1,70 @@
#posts { #posts {
display: grid; display: grid;
grid-template: auto / auto auto auto; grid-template: auto / auto auto auto;
} }
@media (min-width: 1668px) { @media (min-width: 1668px) {
#frontpage .post-img { #frontpage .post-img {
height:105px; height:105px;
width:150px; width:150px;
} }
#frontpage .post-title { #frontpage .post-title {
font-size: 18px; font-size: 18px;
} }
#frontpage .post-meta, .post-actions button, .post-actions a { #frontpage .post-meta, .post-actions button, .post-actions a {
font-size:14px; font-size:14px;
} }
} }
@media (max-width: 960px) { @media (max-width: 960px) {
#posts { #posts {
display: grid; display: grid;
grid-template: auto / auto auto; grid-template: auto / auto auto;
} }
} }
@media (max-width: 768px) { @media (max-width: 768px) {
#posts { #posts {
display: grid; display: grid;
grid-template: auto / auto; grid-template: auto / auto;
} }
} }
#frontpage .sidebar { #frontpage .sidebar {
display:none !important; display:none !important;
} }
#frontpage .container { #frontpage .container {
max-width: 1750px; max-width: 1750px;
} }
#frontpage .voting.d-md-flex { #frontpage .voting.d-md-flex {
display: none !important; display: none !important;
} }
#frontpage .voting.d-md-none { #frontpage .voting.d-md-none {
display: block !important; display: block !important;
} }
#frontpage .card-footer { #frontpage .card-footer {
display: block !important; display: block !important;
} }
#frontpage .post-actions.d-md-block { #frontpage .post-actions.d-md-block {
display: none !important; display: none !important;
} }
#fronpage.card { #fronpage.card {
padding: 0.5rem !important; padding: 0.5rem !important;
} }
#frontpage .modal.d-md-none.show { #frontpage .modal.d-md-none.show {
display: inline-block !important; display: inline-block !important;
max-width: 30rem; max-width: 30rem;
} }
#frontpage .fa-expand-alt { #frontpage .fa-expand-alt {
display:none; display:none;
} }

View File

@ -1,7 +1,7 @@
@import 'classic.css?v=4031'; @import 'classic.css?v=4031';
:root { :root {
--muted: #999; --muted: #999;
--black: #999; --black: #999;
--bgc: #222; --bgc: #222;
} }

View File

@ -96,8 +96,8 @@ blockquote {
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
h5.post-title a:visited { h5.post-title a:visited {

View File

@ -71,12 +71,12 @@ pre {
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
.modal-content { .modal-content {
background-color: var(--gray-900); background-color: var(--gray-900);
} }
.modal .comment-actions .list-group-item { .modal .comment-actions .list-group-item {
@ -97,5 +97,5 @@ h5.post-title a:visited {
} }
.empty-state-img { .empty-state-img {
filter: brightness(99); filter: brightness(99);
} }

View File

@ -135,7 +135,7 @@ color: var(--gray-700);
.post-title .post-title
{ {
color: var(--gray-300) !important; color: var(--gray-300) !important;
} }
.tooltip-inner { .tooltip-inner {

View File

@ -1,12 +1,12 @@
@charset "UTF-8"; @charset "UTF-8";
button { button {
background: none; background: none;
border: none; border: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
color: var(--primary); color: var(--primary);
text-decoration: none; text-decoration: none;
background-color: transparent; background-color: transparent;
} }
*, *::before, *::after { *, *::before, *::after {
box-sizing: border-box; box-sizing: border-box;
@ -334,7 +334,7 @@ pre code {
.shadow { .shadow {
/* box-shadow: 0 0.1px 3px rgba(190, 113, 113, 0.05), 0 0 0 0.1px rgba(0, 0, 0, 0.05); */ /* box-shadow: 0 0.1px 3px rgba(190, 113, 113, 0.05), 0 0 0 0.1px rgba(0, 0, 0, 0.05); */
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.4); box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.4);
} }
.shadow-none { .shadow-none {
box-shadow: none !important; box-shadow: none !important;
@ -796,14 +796,14 @@ input[type=submit].btn-follow, input[type=reset].btn-follow, input[type=button].
} }
@keyframes expand @keyframes expand
{ {
0% { opacity: 0.0; transform: scaleY(0.7); } 0% { opacity: 0.0; transform: scaleY(0.7); }
100% { opacity: 1.0; transform: scaleY(1.0); } 100% { opacity: 1.0; transform: scaleY(1.0); }
} }
.dropdown-menu-right.show .dropdown-menu-right.show
{ {
transform-origin: top; transform-origin: top;
animation: expand .20s 1; animation: expand .20s 1;
} }
@ -825,12 +825,12 @@ input[type=submit].btn-follow, input[type=reset].btn-follow, input[type=button].
background-clip: padding-box; background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.4); border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 0.35rem; border-radius: 0.35rem;
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.3); box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.3);
} }
.dropdown-menu-right { .dropdown-menu-right {
right: 0; right: 0;
left: auto; left: auto;
left: -2px !important; left: -2px !important;
} }
@media (min-width: 992px) { @media (min-width: 992px) {
.dropdown-menu-lg-left { .dropdown-menu-lg-left {
@ -1123,11 +1123,11 @@ nav
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
transition: border-bottom .2s; transition: border-bottom .2s;
} }
.navbar-active .navbar-active
{ {
border-bottom: 1px solid rgba(18, 18, 18, .4); border-bottom: 1px solid rgba(18, 18, 18, .4);
} }
.navbar .container, .navbar .container-fluid { .navbar .container, .navbar .container-fluid {
display: flex; display: flex;
@ -3071,7 +3071,7 @@ label.terms {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border-radius: 0.35rem; border-radius: 0.35rem;
color: var(--black); color: var(--black);
transition: background .1s, transform .2s; transition: background .1s, transform .2s;
} }
.dropdown-item:hover, .dropdown-item:focus, .dropdown-item.active { .dropdown-item:hover, .dropdown-item:focus, .dropdown-item.active {
color: var(--black); color: var(--black);
@ -3079,7 +3079,7 @@ label.terms {
background-color: var(--gray-300); background-color: var(--gray-300);
} }
.dropdown-item:active { .dropdown-item:active {
transform: scale(0.95); transform: scale(0.95);
} }
@ -3134,7 +3134,7 @@ small, .small {
.active-anim.arrow-up::before .active-anim.arrow-up::before
{ {
color: var(--primary); color: var(--primary);
} }
@ -3153,78 +3153,78 @@ small, .small {
} }
.arrow-up, .arrow-down .arrow-up, .arrow-down
{ {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.arrow-up.active-anim .arrow-up.active-anim
{ {
animation: bounce-top .7s 1 0s; animation: bounce-top .7s 1 0s;
-webkit-animation: bounce-top .7s 1 0s; -webkit-animation: bounce-top .7s 1 0s;
} }
.arrow-down.active-anim .arrow-down.active-anim
{ {
animation: bounce-bot .7s 1 0s; animation: bounce-bot .7s 1 0s;
-webkit-animation: bounce-bot .7s 1 0s; -webkit-animation: bounce-bot .7s 1 0s;
} }
@keyframes bounce-top { @keyframes bounce-top {
20% { 20% {
transform: translateY(-9px); transform: translateY(-9px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
52% { 52% {
transform: translateY(-6px); transform: translateY(-6px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
69% { 69% {
transform: translateY(-3px); transform: translateY(-3px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
35%, 35%,
63%, 63%,
79%, 79%,
100% { 100% {
transform: translateY(0px); transform: translateY(0px);
animation-timing-function: ease-out; animation-timing-function: ease-out;
} }
} }
@keyframes bounce-bot { @keyframes bounce-bot {
30% { 30% {
transform: translateY(6px); transform: translateY(6px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
52% { 52% {
transform: translateY(3px); transform: translateY(3px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
69% { 69% {
transform: translateY(2px); transform: translateY(2px);
animation-timing-function: ease-in; animation-timing-function: ease-in;
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: translateY(0px); transform: translateY(0px);
animation-timing-function: ease-out; animation-timing-function: ease-out;
} }
} }
.comment-write.collapsed .comment-write.collapsed
{ {
animation: expand-reply .3s 1; animation: expand-reply .3s 1;
} }
@keyframes expand-reply @keyframes expand-reply
{ {
0% { opacity: .6; padding-top: 0px; height: 0px; overflow: hidden; } 0% { opacity: .6; padding-top: 0px; height: 0px; overflow: hidden; }
100% { opacity: 1;padding-top: 0px; height: 182px; overflow: hidden; } 100% { opacity: 1;padding-top: 0px; height: 182px; overflow: hidden; }
} }
.active.arrow-down::before { .active.arrow-down::before {
@ -3261,7 +3261,7 @@ small, .small {
} }
.score-up-anim .score-up-anim
{ {
color: var(--primary); color: var(--primary);
} }
.score-down { .score-down {
color: #38B2AC !important; color: #38B2AC !important;
@ -3272,13 +3272,13 @@ small, .small {
.voting .arrow-up, .voting .arrow-up,
.voting .arrow-down .voting .arrow-down
{ {
display: block; display: block;
} }
.catalog .voting .arrow-up, .catalog .voting .arrow-up,
.catalog .voting .arrow-down .catalog .voting .arrow-down
{ {
display: inline-block; display: inline-block;
} }
@ -4207,9 +4207,9 @@ pre .com, code .com {
-ms-transition: all 0.15s ease; -ms-transition: all 0.15s ease;
transition: all 0.15s ease; transition: all 0.15s ease;
width: 25vw; width: 25vw;
height: 35vh; height: 35vh;
-o-object-fit: contain; -o-object-fit: contain;
object-fit: contain; object-fit: contain;
} }
@media (max-width: 767.98px) { @media (max-width: 767.98px) {
.gif-categories img { .gif-categories img {
@ -4244,7 +4244,7 @@ pre .com, code .com {
} }
.modal-backdrop.show .modal-backdrop.show
{ {
background-color: rgba(33, 38, 45, .8); background-color: rgba(33, 38, 45, .8);
} }
@media (max-width: 767.98px) { @media (max-width: 767.98px) {
@ -4376,13 +4376,13 @@ pre .com, code .com {
font-size: 1.5rem; font-size: 1.5rem;
color: var(--gray-200); color: var(--gray-200);
opacity: 1; opacity: 1;
} }
#voting .arrow-up, #voting .arrow-up,
.voting .arrow-up, .voting .arrow-up,
.voting .arrow-down .voting .arrow-down
{ {
display: inline-block; display: inline-block;
} }
.active.arrow-up::before, .active.arrow-up:hover::before { .active.arrow-up::before, .active.arrow-up:hover::before {
color: var(--primary); color: var(--primary);
@ -4819,30 +4819,30 @@ input[type=radio] ~ .custom-control-label::before {
} }
span[data-bs-toggle], .pat-preview { span[data-bs-toggle], .pat-preview {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
img[src="/i/hand.webp"] { img[src="/i/hand.webp"] {
position: absolute; position: absolute;
width: 90%; width: 90%;
height: 90%; height: 90%;
margin-top: -10%; margin-top: -10%;
z-index: 1; z-index: 1;
} }
img[src="/i/hand.webp"]+img { img[src="/i/hand.webp"]+img {
animation: pat-pfp-anim 0.3s infinite; animation: pat-pfp-anim 0.3s infinite;
transform-origin: bottom center; transform-origin: bottom center;
margin-top: 10%; margin-top: 10%;
text-align: center; text-align: center;
object-fit: contain; object-fit: contain;
} }
img[src="/i/hand.webp"]+img[src^="/pp/"], img[src="/i/hand.webp"]+img[src$="/pic"] { img[src="/i/hand.webp"]+img[src^="/pp/"], img[src="/i/hand.webp"]+img[src$="/pic"] {
border-radius: 50%; border-radius: 50%;
} }
@keyframes pat-pfp-anim { @keyframes pat-pfp-anim {
0% { transform: scale(1, 0.8) } 0% { transform: scale(1, 0.8) }
50% { transform: scale(0.8, 1) } 50% { transform: scale(0.8, 1) }
100% { transform: scale(1, 0.8) } 100% { transform: scale(1, 0.8) }
} }
/* Fix for <ol> being populated with <li><p></p></li> in many contexts. */ /* Fix for <ol> being populated with <li><p></p></li> in many contexts. */
@ -5211,8 +5211,8 @@ li > .sidebar {
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
@ -5305,20 +5305,20 @@ th, td {
} }
.glow .post-title, .glow a, .glow .post-meta *, .glow .user-info *, .glow .comment-text, .glow .comment-actions *, .glow { .glow .post-title, .glow a, .glow .post-meta *, .glow .user-info *, .glow .comment-text, .glow .comment-actions *, .glow {
color:lightgreen !important; color:lightgreen !important;
text-shadow:1px 1px 1px darkgreen, 0 0 5px green; text-shadow:1px 1px 1px darkgreen, 0 0 5px green;
} }
.glow .score-up, .glow .active.arrow-up::before, .glow .arrow-up::after, .glow .arrow-up:hover::before { .glow .score-up, .glow .active.arrow-up::before, .glow .arrow-up::after, .glow .arrow-up:hover::before {
color: lime !important; color: lime !important;
} }
.glow .score-down, .glow .active.arrow-down::before, .glow .arrow-down::after, .glow .arrow-down:hover::before { .glow .score-down, .glow .active.arrow-down::before, .glow .arrow-down::after, .glow .arrow-down:hover::before {
color: lime !important; color: lime !important;
} }
.glow .arrow-up::before, .glow .arrow-down::before, .glow .score { .glow .arrow-up::before, .glow .arrow-down::before, .glow .score {
color: lightgreen; color: lightgreen;
} }
.glow .post-body a, .glow .comment-text a { .glow .post-body a, .glow .comment-text a {
@ -5468,14 +5468,14 @@ audio, video {
.lottery-page--wrapper { .lottery-page--wrapper {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
padding: 2rem; padding: 2rem;
} }
.lottery-page--wrapper > div .lottery-page--wrapper > div
{ {
width: 300px; width: 300px;
height: 300px; height: 300px;
} }
.lottery-page--action { .lottery-page--action {
margin-bottom: 1rem; margin-bottom: 1rem;
@ -5647,14 +5647,14 @@ g {
} }
.ext-link { .ext-link {
position:absolute; position:absolute;
bottom: 0; bottom: 0;
right: 0; right: 0;
font-size:14px; font-size:14px;
color:white; color:white;
background-color:var(--primary); background-color:var(--primary);
padding:3px; padding:3px;
border-radius:.35rem; border-radius:.35rem;
} }
/* ------- Font Awesome ------- */ /* ------- Font Awesome ------- */
@ -6042,8 +6042,8 @@ g {
.post-preview { .post-preview {
padding: 11px 14px 0 14px !important; padding: 11px 14px 0 14px !important;
margin-bottom: 0.5rem !important; margin-bottom: 0.5rem !important;
margin-top: 0.5rem !important; margin-top: 0.5rem !important;
} }
@ -6054,7 +6054,7 @@ g {
} }
.showmore { .showmore {
width: 99%; width: 99%;
padding: 5px; padding: 5px;
margin: 5px 1px; margin: 5px 1px;
border-radius: 5px; border-radius: 5px;
@ -6063,7 +6063,7 @@ g {
background: -webkit-linear-gradient(left, red, orange, yellow, green, blue, indigo, violet ); background: -webkit-linear-gradient(left, red, orange, yellow, green, blue, indigo, violet );
text-shadow:-1px -1px 0 black,1px -1px 0 black,-1px 1px 0 black,1px 1px 0 black; text-shadow:-1px -1px 0 black,1px -1px 0 black,-1px 1px 0 black,1px 1px 0 black;
font-weight: 600; font-weight: 600;
border: 2px solid var(--primary); border: 2px solid var(--primary);
font-size: 20px; font-size: 20px;
} }
@ -6089,66 +6089,66 @@ g {
.ghostdiv .ghostdiv
{ {
display: block; display: block;
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;
/* Attempt to copy the textarea/input padding */ /* Attempt to copy the textarea/input padding */
padding: 15px; padding: 15px;
} }
#speed-carot-modal #speed-carot-modal
{ {
background-color: var(--gray-700); background-color: var(--gray-700);
max-height: 500px; max-height: 500px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
border-radius: 4px; border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
z-index:999; z-index:999;
} }
#speed-carot-modal .speed-modal-option #speed-carot-modal .speed-modal-option
{ {
border-bottom: 1px solid #606060; border-bottom: 1px solid #606060;
padding: 4px; padding: 4px;
cursor: pointer; cursor: pointer;
} }
#speed-carot-modal .speed-modal-option:hover, #speed-carot-modal .speed-modal-option:hover,
#speed-carot-modal .speed-modal-option:focus, #speed-carot-modal .speed-modal-option:focus,
#speed-carot-modal .speed-modal-option.selected #speed-carot-modal .speed-modal-option.selected
{ {
background-color: rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.2);
} }
#speed-carot-modal .speed-modal-image #speed-carot-modal .speed-modal-image
{ {
object-fit: contain; object-fit: contain;
width: 30px; width: 30px;
height: 30px; height: 30px;
} }
#speed-carot-modal .speed-modal-option span #speed-carot-modal .speed-modal-option span
{ {
overflow: hidden; overflow: hidden;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
} }
.rainbow-text:not(a) { .rainbow-text:not(a) {
background-image: repeating-linear-gradient(135deg, violet, rgb(178, 94, 238), lightblue, green, yellow, orange, #ff7f7f 50%) !important; background-image: repeating-linear-gradient(135deg, violet, rgb(178, 94, 238), lightblue, green, yellow, orange, #ff7f7f 50%) !important;
color: transparent !important; color: transparent !important;
background-clip: text !important; background-clip: text !important;
-webkit-background-clip: text !important; -webkit-background-clip: text !important;
font-weight: 700 !important; font-weight: 700 !important;
} }
.rainbow-text:not(a) > p { .rainbow-text:not(a) > p {
color: transparent !important; color: transparent !important;
} }
.btn-rainbow { .btn-rainbow {
@ -6219,7 +6219,7 @@ blockquote + blockquote, div > blockquote:first-child, blockquote:last-child {
} }
div.markdown { div.markdown {
height: auto; height: auto;
white-space: pre-wrap; white-space: pre-wrap;
} }
@media (max-width: 768px) or (min-width: 992px) { @media (max-width: 768px) or (min-width: 992px) {
#sidebar-btn { #sidebar-btn {
@ -6236,11 +6236,11 @@ div.markdown {
} }
.directory--link:hover * { .directory--link:hover * {
color: var(--primary) !important; color: var(--primary) !important;
} }
::-webkit-input-placeholder { ::-webkit-input-placeholder {
opacity: 0.7 !important; opacity: 0.7 !important;
} }
.text-brown { .text-brown {

View File

@ -44,8 +44,8 @@ body, .navbar-light, .navbar-dark, .card, .modal-content, .comment-write textare
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
.table th, .table td { .table th, .table td {

View File

@ -1,7 +1,7 @@
.mod:before { .mod:before {
content: '((('; content: '(((';
} }
.mod:after { .mod:after {
content: ')))'; content: ')))';
} }

View File

@ -221,8 +221,8 @@
.sidebar .sidebar
{ {
border-radius: 6px; border-radius: 6px;
margin-top: 6px; margin-top: 6px;
} }
.tooltip-inner { .tooltip-inner {

View File

@ -158,8 +158,8 @@ blockquote {
.sidebar .sidebar
{ {
border-radius: 0px; border-radius: 0px;
margin-top: 6px; margin-top: 6px;
} }
h5.post-title a:visited { h5.post-title a:visited {

View File

@ -20,7 +20,7 @@ class Leaderboard:
value_func = None value_func = None
def __init__(self, header_name:str, table_header_name:str, html_id:str, table_column_name:str, def __init__(self, header_name:str, table_header_name:str, html_id:str, table_column_name:str,
user_relative_url:Optional[str], query_function:Callable[..., Tuple[Any, Any, Any]], user_relative_url:Optional[str], query_function:Callable[..., Tuple[Any, Any, Any]],
criteria, v:User, value_func:Optional[Callable[[User], Union[int, Column]]], db:scoped_session, users, limit=LEADERBOARD_LIMIT): criteria, v:User, value_func:Optional[Callable[[User], Union[int, Column]]], db:scoped_session, users, limit=LEADERBOARD_LIMIT):
self.header_name = header_name self.header_name = header_name
self.table_header_name = table_header_name self.table_header_name = table_header_name

View File

@ -9,99 +9,99 @@ from files.helpers.wrappers import *
def get_game_feed(game): def get_game_feed(game):
games = g.db.query(Casino_Game) \ games = g.db.query(Casino_Game) \
.filter(Casino_Game.active == False, Casino_Game.kind == game) \ .filter(Casino_Game.active == False, Casino_Game.kind == game) \
.order_by(Casino_Game.created_utc.desc()).limit(30).all() .order_by(Casino_Game.created_utc.desc()).limit(30).all()
def format_game(game): def format_game(game):
user = g.db.query(User).filter(User.id == game.user_id).one() user = g.db.query(User).filter(User.id == game.user_id).one()
wonlost = 'lost' if game.winnings < 0 else 'won' wonlost = 'lost' if game.winnings < 0 else 'won'
relevant_currency = "coin" if game.currency == "coins" else "marseybux" relevant_currency = "coin" if game.currency == "coins" else "marseybux"
return { return {
"user": user.username, "user": user.username,
"won_or_lost": wonlost, "won_or_lost": wonlost,
"amount": abs(game.winnings), "amount": abs(game.winnings),
"currency": relevant_currency "currency": relevant_currency
} }
return list(map(format_game, games)) return list(map(format_game, games))
def get_game_leaderboard(game): def get_game_leaderboard(game):
timestamp_24h_ago = time.time() - 86400 timestamp_24h_ago = time.time() - 86400
timestamp_all_time = 1662825600 # "All Time" starts on release day timestamp_all_time = 1662825600 # "All Time" starts on release day
biggest_win_all_time = g.db.query(Casino_Game.user_id, User.username, Casino_Game.currency, Casino_Game.winnings).select_from( 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, Casino_Game.created_utc > timestamp_all_time).limit(1).one_or_none() Casino_Game).join(User).order_by(Casino_Game.winnings.desc()).filter(Casino_Game.kind == game, Casino_Game.created_utc > timestamp_all_time).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( 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() 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( 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, Casino_Game.created_utc > timestamp_all_time).limit(1).one_or_none() Casino_Game).join(User).order_by(Casino_Game.winnings.asc()).filter(Casino_Game.kind == game, Casino_Game.created_utc > timestamp_all_time).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( 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() 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: if not biggest_win_all_time:
biggest_win_all_time = [None, None, None, 0] biggest_win_all_time = [None, None, None, 0]
if not biggest_win_last_24h: if not biggest_win_last_24h:
biggest_win_last_24h = [None, None, None, 0] biggest_win_last_24h = [None, None, None, 0]
if not biggest_loss_all_time: if not biggest_loss_all_time:
biggest_loss_all_time = [None, None, None, 0] biggest_loss_all_time = [None, None, None, 0]
if not biggest_loss_last_24h: if not biggest_loss_last_24h:
biggest_loss_last_24h = [None, None, None, 0] biggest_loss_last_24h = [None, None, None, 0]
return { return {
"all_time": { "all_time": {
"biggest_win": { "biggest_win": {
"user": biggest_win_all_time[1], "user": biggest_win_all_time[1],
"currency": biggest_win_all_time[2], "currency": biggest_win_all_time[2],
"amount": biggest_win_all_time[3] "amount": biggest_win_all_time[3]
}, },
"biggest_loss": { "biggest_loss": {
"user": biggest_loss_all_time[1], "user": biggest_loss_all_time[1],
"currency": biggest_loss_all_time[2], "currency": biggest_loss_all_time[2],
"amount": abs(biggest_loss_all_time[3]) "amount": abs(biggest_loss_all_time[3])
} }
}, },
"last_24h": { "last_24h": {
"biggest_win": { "biggest_win": {
"user": biggest_win_last_24h[1], "user": biggest_win_last_24h[1],
"currency": biggest_win_last_24h[2], "currency": biggest_win_last_24h[2],
"amount": biggest_win_last_24h[3] "amount": biggest_win_last_24h[3]
}, },
"biggest_loss": { "biggest_loss": {
"user": biggest_loss_last_24h[1], "user": biggest_loss_last_24h[1],
"currency": biggest_loss_last_24h[2], "currency": biggest_loss_last_24h[2],
"amount": abs(biggest_loss_last_24h[3]) "amount": abs(biggest_loss_last_24h[3])
} }
} }
} }
def distribute_wager_badges(user, wager, won): def distribute_wager_badges(user, wager, won):
badges_earned = [] badges_earned = []
if won: if won:
if wager >= 1000: if wager >= 1000:
badges_earned.append(160) badges_earned.append(160)
if wager >= 10000: if wager >= 10000:
badges_earned.append(161) badges_earned.append(161)
if wager >= 100000: if wager >= 100000:
badges_earned.append(162) badges_earned.append(162)
else: else:
if wager >= 1000: if wager >= 1000:
badges_earned.append(157) badges_earned.append(157)
if wager >= 10000: if wager >= 10000:
badges_earned.append(158) badges_earned.append(158)
if wager >= 100000: if wager >= 100000:
badges_earned.append(159) badges_earned.append(159)
for badge in badges_earned: for badge in badges_earned:
badge_grant(user, badge) badge_grant(user, badge)

View File

@ -8,35 +8,35 @@ CLOUDFLARE_REQUEST_TIMEOUT_SECS = 5
DEFAULT_CLOUDFLARE_ZONE = 'blahblahblah' DEFAULT_CLOUDFLARE_ZONE = 'blahblahblah'
def _request_from_cloudflare(url:str, method:str, post_data_str) -> bool: def _request_from_cloudflare(url:str, method:str, post_data_str) -> bool:
if CF_ZONE == DEFAULT_CLOUDFLARE_ZONE: return False if CF_ZONE == DEFAULT_CLOUDFLARE_ZONE: return False
try: try:
res = str(requests.request(method, f"{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/{url}", headers=CF_HEADERS, data=post_data_str, timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS)) res = str(requests.request(method, f"{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/{url}", headers=CF_HEADERS, data=post_data_str, timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS))
except: except:
return False return False
return res == "<Response [200]>" return res == "<Response [200]>"
def get_security_level() -> Optional[str]: def get_security_level() -> Optional[str]:
res = None res = None
try: try:
res = requests.get(f'{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS).json()['result']['value'] res = requests.get(f'{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS).json()['result']['value']
except: except:
pass pass
return res return res
def set_security_level(under_attack="high") -> bool: def set_security_level(under_attack="high") -> bool:
return _request_from_cloudflare("settings/security_level", "PATCH", f'{{"value":"{under_attack}"}}') return _request_from_cloudflare("settings/security_level", "PATCH", f'{{"value":"{under_attack}"}}')
def purge_entire_cache() -> bool: def purge_entire_cache() -> bool:
return _request_from_cloudflare("purge_cache", "POST", '{"purge_everything":true}') return _request_from_cloudflare("purge_cache", "POST", '{"purge_everything":true}')
def purge_files_in_cache(files:Union[List[str],str]) -> bool: def purge_files_in_cache(files:Union[List[str],str]) -> bool:
if CF_ZONE == DEFAULT_CLOUDFLARE_ZONE: return False if CF_ZONE == DEFAULT_CLOUDFLARE_ZONE: return False
if isinstance(files, str): if isinstance(files, str):
files = [files] files = [files]
post_data = {"files": files} post_data = {"files": files}
res = None res = None
try: try:
res = requests.post(f'{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS, data=json.dumps(post_data), timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS) res = requests.post(f'{CLOUDFLARE_API_URL}/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS, data=json.dumps(post_data), timeout=CLOUDFLARE_REQUEST_TIMEOUT_SECS)
except: except:
return False return False
return res == "<Response [200]>" return res == "<Response [200]>"

View File

@ -866,13 +866,13 @@ AWARDS = {
"price": 1500 "price": 1500
}, },
"spider": { "spider": {
"kind": "spider", "kind": "spider",
"title": "Spider!", "title": "Spider!",
"description": f"Summons a spider to terrorize the recipient for 24 hours.", "description": f"Summons a spider to terrorize the recipient for 24 hours.",
"icon": "fas fa-spider", "icon": "fas fa-spider",
"color": "text-brown", "color": "text-brown",
"price": 2000 "price": 2000
}, },
"agendaposter": { "agendaposter": {
"kind": "agendaposter", "kind": "agendaposter",
"title": "Chud", "title": "Chud",

View File

@ -199,58 +199,58 @@ def get_comment(i:Union[str, int], v:Optional[User]=None, graceful=False) -> Opt
return add_vote_and_block_props(comment, v, CommentVote) return add_vote_and_block_props(comment, v, CommentVote)
def add_block_props(target:Union[Submission, Comment, User], v:Optional[User]): def add_block_props(target:Union[Submission, Comment, User], v:Optional[User]):
if not v: return target if not v: return target
id = None id = None
if any(isinstance(target, cls) for cls in [Submission, Comment]): if any(isinstance(target, cls) for cls in [Submission, Comment]):
id = target.author_id id = target.author_id
elif isinstance(target, User): elif isinstance(target, User):
id = target.id id = target.id
else: else:
raise TypeError("add_block_props only supports non-None submissions, comments, and users") raise TypeError("add_block_props only supports non-None submissions, comments, and users")
if hasattr(target, 'is_blocking') and hasattr(target, 'is_blocked'): if hasattr(target, 'is_blocking') and hasattr(target, 'is_blocked'):
return target return target
if v.id == id or id == AUTOJANNY_ID: # users can't block or be blocked by themselves or AutoJanny if v.id == id or id == AUTOJANNY_ID: # users can't block or be blocked by themselves or AutoJanny
target.is_blocking = False target.is_blocking = False
target.is_blocked = False target.is_blocked = False
return target return target
block = g.db.query(UserBlock).filter( block = g.db.query(UserBlock).filter(
or_( or_(
and_( and_(
UserBlock.user_id == v.id, UserBlock.user_id == v.id,
UserBlock.target_id == id UserBlock.target_id == id
), ),
and_( and_(
UserBlock.user_id == id, UserBlock.user_id == id,
UserBlock.target_id == v.id UserBlock.target_id == v.id
) )
) )
).first() ).first()
target.is_blocking = block and block.user_id == v.id target.is_blocking = block and block.user_id == v.id
target.is_blocked = block and block.target_id == v.id target.is_blocked = block and block.target_id == v.id
return target return target
def add_vote_props(target:Union[Submission, Comment], v:Optional[User], vote_cls): def add_vote_props(target:Union[Submission, Comment], v:Optional[User], vote_cls):
if hasattr(target, 'voted'): return target if hasattr(target, 'voted'): return target
vt = g.db.query(vote_cls.vote_type).filter_by(user_id=v.id) vt = g.db.query(vote_cls.vote_type).filter_by(user_id=v.id)
if vote_cls == Vote: if vote_cls == Vote:
vt = vt.filter_by(submission_id=target.id) vt = vt.filter_by(submission_id=target.id)
elif vote_cls == CommentVote: elif vote_cls == CommentVote:
vt = vt.filter_by(comment_id=target.id) vt = vt.filter_by(comment_id=target.id)
else: else:
vt = None vt = None
if vt: vt = vt.one_or_none() if vt: vt = vt.one_or_none()
target.voted = vt.vote_type if vt else 0 target.voted = vt.vote_type if vt else 0
return target return target
def add_vote_and_block_props(target:Union[Submission, Comment], v:Optional[User], vote_cls): def add_vote_and_block_props(target:Union[Submission, Comment], v:Optional[User], vote_cls):
if not v: return target if not v: return target
target = add_block_props(target, v) target = add_block_props(target, v)
return add_vote_props(target, v, vote_cls) return add_vote_props(target, v, vote_cls)
def get_comments(cids:Iterable[int], v:Optional[User]=None) -> List[Comment]: def get_comments(cids:Iterable[int], v:Optional[User]=None) -> List[Comment]:
if not cids: return [] if not cids: return []

View File

@ -8,290 +8,290 @@ from flask import g
class RouletteAction(str, Enum): class RouletteAction(str, Enum):
STRAIGHT_UP_BET = "STRAIGHT_UP_BET" STRAIGHT_UP_BET = "STRAIGHT_UP_BET"
LINE_BET = "LINE_BET" LINE_BET = "LINE_BET"
COLUMN_BET = "COLUMN_BET" COLUMN_BET = "COLUMN_BET"
DOZEN_BET = "DOZEN_BET" DOZEN_BET = "DOZEN_BET"
EVEN_ODD_BET = "EVEN_ODD_BET" EVEN_ODD_BET = "EVEN_ODD_BET"
RED_BLACK_BET = "RED_BLACK_BET" RED_BLACK_BET = "RED_BLACK_BET"
HIGH_LOW_BET = "HIGH_LOW_BET" HIGH_LOW_BET = "HIGH_LOW_BET"
class RouletteEvenOdd(str, Enum): class RouletteEvenOdd(str, Enum):
EVEN = "EVEN" EVEN = "EVEN"
ODD = "ODD" ODD = "ODD"
class RouletteRedBlack(str, Enum): class RouletteRedBlack(str, Enum):
RED = "RED" RED = "RED"
BLACK = "BLACK" BLACK = "BLACK"
class RouletteHighLow(str, Enum): class RouletteHighLow(str, Enum):
HIGH = "HIGH" HIGH = "HIGH"
LOW = "LOW" LOW = "LOW"
REDS = (1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36) REDS = (1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36)
BLACKS = (2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35) BLACKS = (2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35)
LINES = { LINES = {
1: (1, 2, 3, 4, 5, 6), 1: (1, 2, 3, 4, 5, 6),
2: (7, 8, 9, 10, 11, 12), 2: (7, 8, 9, 10, 11, 12),
3: (13, 14, 15, 16, 17, 18), 3: (13, 14, 15, 16, 17, 18),
4: (19, 20, 21, 22, 23, 24), 4: (19, 20, 21, 22, 23, 24),
5: (25, 26, 27, 28, 29, 30), 5: (25, 26, 27, 28, 29, 30),
6: (31, 32, 33, 34, 35, 36) 6: (31, 32, 33, 34, 35, 36)
} }
COLUMNS = { COLUMNS = {
1: (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34), 1: (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34),
2: (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35), 2: (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35),
3: (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36) 3: (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36)
} }
DOZENS = { DOZENS = {
1: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 1: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
2: (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24), 2: (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24),
3: (25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36) 3: (25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36)
} }
PAYOUT_MULITPLIERS = { PAYOUT_MULITPLIERS = {
RouletteAction.STRAIGHT_UP_BET: 35, RouletteAction.STRAIGHT_UP_BET: 35,
RouletteAction.LINE_BET: 5, RouletteAction.LINE_BET: 5,
RouletteAction.COLUMN_BET: 2, RouletteAction.COLUMN_BET: 2,
RouletteAction.DOZEN_BET: 2, RouletteAction.DOZEN_BET: 2,
RouletteAction.EVEN_ODD_BET: 1, RouletteAction.EVEN_ODD_BET: 1,
RouletteAction.RED_BLACK_BET: 1, RouletteAction.RED_BLACK_BET: 1,
RouletteAction.HIGH_LOW_BET: 1, RouletteAction.HIGH_LOW_BET: 1,
} }
def get_active_roulette_games(): def get_active_roulette_games():
return g.db.query(Casino_Game).filter( return g.db.query(Casino_Game).filter(
Casino_Game.active == True, Casino_Game.active == True,
Casino_Game.kind == 'roulette' Casino_Game.kind == 'roulette'
).all() ).all()
def charge_gambler(gambler, amount, currency): def charge_gambler(gambler, amount, currency):
charged = gambler.charge_account(currency, amount) charged = gambler.charge_account(currency, amount)
if not charged: if not charged:
raise Exception("Gambler cannot afford charge.") raise Exception("Gambler cannot afford charge.")
def gambler_placed_roulette_bet(gambler, bet, which, amount, currency): def gambler_placed_roulette_bet(gambler, bet, which, amount, currency):
if not bet in ( if not bet in (
RouletteAction.STRAIGHT_UP_BET, RouletteAction.STRAIGHT_UP_BET,
RouletteAction.LINE_BET, RouletteAction.LINE_BET,
RouletteAction.COLUMN_BET, RouletteAction.COLUMN_BET,
RouletteAction.DOZEN_BET, RouletteAction.DOZEN_BET,
RouletteAction.EVEN_ODD_BET, RouletteAction.EVEN_ODD_BET,
RouletteAction.RED_BLACK_BET, RouletteAction.RED_BLACK_BET,
RouletteAction.HIGH_LOW_BET RouletteAction.HIGH_LOW_BET
): ):
raise Exception( raise Exception(
f'Illegal bet {bet} passed to Roulette#gambler_placed_roulette_bet') f'Illegal bet {bet} passed to Roulette#gambler_placed_roulette_bet')
active_games = get_active_roulette_games() active_games = get_active_roulette_games()
if len(active_games) == 0: if len(active_games) == 0:
parent_id = int(time.time()) parent_id = int(time.time())
else: else:
parent_id = json.loads(active_games[0].game_state)['parent_id'] parent_id = json.loads(active_games[0].game_state)['parent_id']
charge_gambler(gambler, amount, currency) charge_gambler(gambler, amount, currency)
game = Casino_Game() game = Casino_Game()
game.user_id = gambler.id game.user_id = gambler.id
game.currency = currency game.currency = currency
game.wager = amount game.wager = amount
game.winnings = 0 game.winnings = 0
game.kind = 'roulette' game.kind = 'roulette'
game.game_state = json.dumps( game.game_state = json.dumps(
{"parent_id": parent_id, "bet": bet, "which": which}) {"parent_id": parent_id, "bet": bet, "which": which})
game.active = True game.active = True
g.db.add(game) g.db.add(game)
g.db.commit() g.db.commit()
def get_roulette_bets_and_betters(): def get_roulette_bets_and_betters():
participants = [] participants = []
bets = { bets = {
RouletteAction.STRAIGHT_UP_BET: [], RouletteAction.STRAIGHT_UP_BET: [],
RouletteAction.LINE_BET: [], RouletteAction.LINE_BET: [],
RouletteAction.COLUMN_BET: [], RouletteAction.COLUMN_BET: [],
RouletteAction.DOZEN_BET: [], RouletteAction.DOZEN_BET: [],
RouletteAction.EVEN_ODD_BET: [], RouletteAction.EVEN_ODD_BET: [],
RouletteAction.RED_BLACK_BET: [], RouletteAction.RED_BLACK_BET: [],
RouletteAction.HIGH_LOW_BET: [], RouletteAction.HIGH_LOW_BET: [],
} }
active_games = get_active_roulette_games() active_games = get_active_roulette_games()
for game in active_games: for game in active_games:
if not game.user_id in participants: if not game.user_id in participants:
participants.append(game.user_id) participants.append(game.user_id)
user = get_account(game.user_id) user = get_account(game.user_id)
game_state = json.loads(game.game_state) game_state = json.loads(game.game_state)
bet = game_state['bet'] bet = game_state['bet']
bets[bet].append({ bets[bet].append({
'game_id': game.id, 'game_id': game.id,
'gambler': game.user_id, 'gambler': game.user_id,
'gambler_username': user.username, 'gambler_username': user.username,
'gambler_profile_url': user.profile_url, 'gambler_profile_url': user.profile_url,
'bet': bet, 'bet': bet,
'which': game_state['which'], 'which': game_state['which'],
'wager': { 'wager': {
'amount': game.wager, 'amount': game.wager,
'currency': game.currency 'currency': game.currency
} }
}) })
return participants, bets, active_games return participants, bets, active_games
def spin_roulette_wheel(): def spin_roulette_wheel():
participants, bets, active_games = get_roulette_bets_and_betters() participants, bets, active_games = get_roulette_bets_and_betters()
if len(participants) > 0: if len(participants) > 0:
number = randint(0, 37) # 37 is 00 number = randint(0, 37) # 37 is 00
if number > 0 and number < 37: # 0 and 00 do not pay anything if number > 0 and number < 37: # 0 and 00 do not pay anything
winners, payouts, rewards_by_game_id = determine_roulette_winners(number, bets) winners, payouts, rewards_by_game_id = determine_roulette_winners(number, bets)
else: else:
winners = [] winners = []
payouts = {} payouts = {}
rewards_by_game_id = {} rewards_by_game_id = {}
# Pay out to the winners and send a notification. # Pay out to the winners and send a notification.
for user_id in winners: for user_id in winners:
gambler = get_account(user_id) gambler = get_account(user_id)
gambler_payout = payouts[user_id] gambler_payout = payouts[user_id]
coin_winnings = gambler_payout['coins'] coin_winnings = gambler_payout['coins']
procoin_winnings = gambler_payout['procoins'] procoin_winnings = gambler_payout['procoins']
gambler.pay_account('coins', coin_winnings) gambler.pay_account('coins', coin_winnings)
gambler.pay_account('procoins', procoin_winnings) gambler.pay_account('procoins', procoin_winnings)
# Notify the winners. # Notify the winners.
notification_text = f"Winning number: {number}\nCongratulations! One or more of your roulette bets paid off!\n" notification_text = f"Winning number: {number}\nCongratulations! One or more of your roulette bets paid off!\n"
if coin_winnings > 0: if coin_winnings > 0:
notification_text = notification_text + \ notification_text = notification_text + \
f"* You received {coin_winnings} coins.\n" f"* You received {coin_winnings} coins.\n"
if procoin_winnings > 0: if procoin_winnings > 0:
notification_text = notification_text + \ notification_text = notification_text + \
f"* You received {procoin_winnings} marseybux.\n" f"* You received {procoin_winnings} marseybux.\n"
send_repeatable_notification(user_id, notification_text) send_repeatable_notification(user_id, notification_text)
# Give condolences. # Give condolences.
for participant in participants: for participant in participants:
if not participant in winners: if not participant in winners:
send_repeatable_notification( send_repeatable_notification(
participant, f"Winning number: {number}\nSorry, none of your recent roulette bets paid off.") participant, f"Winning number: {number}\nSorry, none of your recent roulette bets paid off.")
g.db.flush() g.db.flush()
# Adjust game winnings. # Adjust game winnings.
for game in active_games: for game in active_games:
if rewards_by_game_id.get(game.id): if rewards_by_game_id.get(game.id):
game.winnings = rewards_by_game_id[game.id] game.winnings = rewards_by_game_id[game.id]
else: else:
game.winnings = -game.wager game.winnings = -game.wager
game.active = False game.active = False
g.db.add(game) g.db.add(game)
# Commit early when dirty because of long-running tasks after roulette # Commit early when dirty because of long-running tasks after roulette
g.db.commit() g.db.commit()
def determine_roulette_winners(number, bets): def determine_roulette_winners(number, bets):
winners = [] winners = []
payouts = {} payouts = {}
rewards_by_game_id = {} rewards_by_game_id = {}
def add_to_winnings(bet): def add_to_winnings(bet):
game_id = int(bet['game_id']) game_id = int(bet['game_id'])
gambler_id = bet['gambler'] gambler_id = bet['gambler']
wager_amount = bet['wager']['amount'] wager_amount = bet['wager']['amount']
bet_kind = bet['bet'] bet_kind = bet['bet']
reward = wager_amount * PAYOUT_MULITPLIERS[bet_kind] reward = wager_amount * PAYOUT_MULITPLIERS[bet_kind]
payout = wager_amount + reward payout = wager_amount + reward
currency = bet['wager']['currency'] currency = bet['wager']['currency']
if not gambler_id in winners: if not gambler_id in winners:
winners.append(gambler_id) winners.append(gambler_id)
if not payouts.get(gambler_id): if not payouts.get(gambler_id):
payouts[gambler_id] = { payouts[gambler_id] = {
'coins': 0, 'coins': 0,
'procoins': 0 'procoins': 0
} }
if not rewards_by_game_id.get(game_id): if not rewards_by_game_id.get(game_id):
rewards_by_game_id[game_id] = reward rewards_by_game_id[game_id] = reward
payouts[gambler_id][currency] += payout payouts[gambler_id][currency] += payout
# Straight-Up Bet # Straight-Up Bet
for bet in bets[RouletteAction.STRAIGHT_UP_BET]: for bet in bets[RouletteAction.STRAIGHT_UP_BET]:
if int(bet['which']) == number: if int(bet['which']) == number:
add_to_winnings(bet) add_to_winnings(bet)
# Line Bet # Line Bet
line = -1 line = -1
for i in range(1, 7): for i in range(1, 7):
if number in LINES[i]: if number in LINES[i]:
line = i line = i
for bet in bets[RouletteAction.LINE_BET]: for bet in bets[RouletteAction.LINE_BET]:
if int(bet['which']) == line: if int(bet['which']) == line:
add_to_winnings(bet) add_to_winnings(bet)
# Column Bet # Column Bet
column = -1 column = -1
for i in range(1, 4): for i in range(1, 4):
if number in COLUMNS[i]: if number in COLUMNS[i]:
column = i column = i
for bet in bets[RouletteAction.COLUMN_BET]: for bet in bets[RouletteAction.COLUMN_BET]:
if int(bet['which']) == column: if int(bet['which']) == column:
add_to_winnings(bet) add_to_winnings(bet)
# Dozen Bet # Dozen Bet
dozen = -1 dozen = -1
for i in range(1, 4): for i in range(1, 4):
if number in DOZENS[i]: if number in DOZENS[i]:
dozen = i dozen = i
for bet in bets[RouletteAction.DOZEN_BET]: for bet in bets[RouletteAction.DOZEN_BET]:
if int(bet['which']) == dozen: if int(bet['which']) == dozen:
add_to_winnings(bet) add_to_winnings(bet)
# Even/Odd Bet # Even/Odd Bet
even_odd = RouletteEvenOdd.EVEN if number % 2 == 0 else RouletteEvenOdd.ODD even_odd = RouletteEvenOdd.EVEN if number % 2 == 0 else RouletteEvenOdd.ODD
for bet in bets[RouletteAction.EVEN_ODD_BET]: for bet in bets[RouletteAction.EVEN_ODD_BET]:
if bet['which'] == even_odd: if bet['which'] == even_odd:
add_to_winnings(bet) add_to_winnings(bet)
# Red/Black Bet # Red/Black Bet
red_black = RouletteRedBlack.RED if number in REDS else RouletteRedBlack.BLACK red_black = RouletteRedBlack.RED if number in REDS else RouletteRedBlack.BLACK
for bet in bets[RouletteAction.RED_BLACK_BET]: for bet in bets[RouletteAction.RED_BLACK_BET]:
if bet['which'] == red_black: if bet['which'] == red_black:
add_to_winnings(bet) add_to_winnings(bet)
# High/Low Bet # High/Low Bet
high_low = RouletteHighLow.HIGH if number > 18 else RouletteHighLow.LOW high_low = RouletteHighLow.HIGH if number > 18 else RouletteHighLow.LOW
for bet in bets[RouletteAction.HIGH_LOW_BET]: for bet in bets[RouletteAction.HIGH_LOW_BET]:
if bet['which'] == high_low: if bet['which'] == high_low:
add_to_winnings(bet) add_to_winnings(bet)
return winners, payouts, rewards_by_game_id return winners, payouts, rewards_by_game_id
def get_roulette_bets(): def get_roulette_bets():
return get_roulette_bets_and_betters()[1] return get_roulette_bets_and_betters()[1]

View File

@ -8,20 +8,20 @@ from flask import g
class BlackjackStatus(str, Enum): class BlackjackStatus(str, Enum):
PLAYING = "PLAYING" PLAYING = "PLAYING"
STAYED = "STAYED" STAYED = "STAYED"
PUSHED = "PUSHED" PUSHED = "PUSHED"
WON = "WON" WON = "WON"
LOST = "LOST" LOST = "LOST"
BLACKJACK = "BLACKJACK" BLACKJACK = "BLACKJACK"
class BlackjackAction(str, Enum): class BlackjackAction(str, Enum):
DEAL = "DEAL" DEAL = "DEAL"
HIT = "HIT" HIT = "HIT"
STAY = "STAY" STAY = "STAY"
DOUBLE_DOWN = "DOUBLE_DOWN" DOUBLE_DOWN = "DOUBLE_DOWN"
BUY_INSURANCE = "BUY_INSURANCE" BUY_INSURANCE = "BUY_INSURANCE"
ranks = ("2", "3", "4", "5", "6", "7", "8", "9", "X", "J", "Q", "K", "A") ranks = ("2", "3", "4", "5", "6", "7", "8", "9", "X", "J", "Q", "K", "A")
@ -32,350 +32,350 @@ minimum_bet = 5
def get_initial_state(): def get_initial_state():
return { return {
"player": [], "player": [],
"player_value": 0, "player_value": 0,
"dealer": [], "dealer": [],
"dealer_value": 0, "dealer_value": 0,
"player_bought_insurance": False, "player_bought_insurance": False,
"player_doubled_down": False, "player_doubled_down": False,
"status": BlackjackStatus.PLAYING, "status": BlackjackStatus.PLAYING,
"actions": [BlackjackAction.DEAL], "actions": [BlackjackAction.DEAL],
"wager": { "wager": {
"amount": 0, "amount": 0,
"currency": "coins" "currency": "coins"
}, },
"payout": 0 "payout": 0
} }
def build_casino_game(gambler, wager, currency): def build_casino_game(gambler, wager, currency):
initial_state = get_initial_state() initial_state = get_initial_state()
initial_state['wager']['amount'] = wager initial_state['wager']['amount'] = wager
initial_state['wager']['currency'] = currency initial_state['wager']['currency'] = currency
casino_game = Casino_Game() casino_game = Casino_Game()
casino_game.user_id = gambler.id casino_game.user_id = gambler.id
casino_game.currency = currency casino_game.currency = currency
casino_game.wager = wager casino_game.wager = wager
casino_game.winnings = 0 casino_game.winnings = 0
casino_game.kind = 'blackjack' casino_game.kind = 'blackjack'
casino_game.game_state = json.dumps(initial_state) casino_game.game_state = json.dumps(initial_state)
casino_game.active = True casino_game.active = True
g.db.add(casino_game) g.db.add(casino_game)
return casino_game return casino_game
def get_active_twentyone_game(gambler): def get_active_twentyone_game(gambler):
return g.db.query(Casino_Game).filter( return g.db.query(Casino_Game).filter(
Casino_Game.active == True, Casino_Game.active == True,
Casino_Game.kind == 'blackjack', Casino_Game.kind == 'blackjack',
Casino_Game.user_id == gambler.id).first() Casino_Game.user_id == gambler.id).first()
def get_active_twentyone_game_state(gambler): def get_active_twentyone_game_state(gambler):
active_game = get_active_twentyone_game(gambler) active_game = get_active_twentyone_game(gambler)
full_state = json.loads(active_game.game_state) full_state = json.loads(active_game.game_state)
return remove_exploitable_information(full_state) return remove_exploitable_information(full_state)
def charge_gambler(gambler, amount, currency): def charge_gambler(gambler, amount, currency):
charged = gambler.charge_account(currency, amount) charged = gambler.charge_account(currency, amount)
if not charged: if not charged:
raise Exception("Gambler cannot afford charge.") raise Exception("Gambler cannot afford charge.")
def create_new_game(gambler, wager, currency): def create_new_game(gambler, wager, currency):
existing_game = get_active_twentyone_game(gambler) existing_game = get_active_twentyone_game(gambler)
over_minimum_bet = wager >= minimum_bet over_minimum_bet = wager >= minimum_bet
if existing_game: if existing_game:
raise Exception("Gambler already has a game in progress.") raise Exception("Gambler already has a game in progress.")
if not over_minimum_bet: if not over_minimum_bet:
raise Exception(f"Gambler must bet over {minimum_bet} {currency}.") raise Exception(f"Gambler must bet over {minimum_bet} {currency}.")
try: try:
charge_gambler(gambler, wager, currency) charge_gambler(gambler, wager, currency)
new_game = build_casino_game(gambler, wager, currency) new_game = build_casino_game(gambler, wager, currency)
g.db.add(new_game) g.db.add(new_game)
g.db.commit() g.db.commit()
except: except:
raise Exception(f"Gambler cannot afford to bet {wager} {currency}.") raise Exception(f"Gambler cannot afford to bet {wager} {currency}.")
def handle_blackjack_deal(state): def handle_blackjack_deal(state):
deck = build_deck(state) deck = build_deck(state)
first = deck.pop() first = deck.pop()
second = deck.pop() second = deck.pop()
third = deck.pop() third = deck.pop()
fourth = deck.pop() fourth = deck.pop()
state['player'] = [first, third] state['player'] = [first, third]
state['dealer'] = [second, fourth] state['dealer'] = [second, fourth]
return state return state
def handle_blackjack_hit(state): def handle_blackjack_hit(state):
deck = build_deck(state) deck = build_deck(state)
next_card = deck.pop() next_card = deck.pop()
state['player'].append(next_card) state['player'].append(next_card)
return state return state
def handle_blackjack_stay(state): def handle_blackjack_stay(state):
state['status'] = BlackjackStatus.STAYED state['status'] = BlackjackStatus.STAYED
return state return state
def handle_blackjack_double_down(state): def handle_blackjack_double_down(state):
state['player_doubled_down'] = True state['player_doubled_down'] = True
state = handle_blackjack_hit(state) state = handle_blackjack_hit(state)
state = handle_blackjack_stay(state) state = handle_blackjack_stay(state)
return state return state
def handle_blackjack_buy_insurance(state): def handle_blackjack_buy_insurance(state):
state['player_bought_insurance'] = True state['player_bought_insurance'] = True
return state return state
def check_for_completion(state): def check_for_completion(state):
after_initial_deal = len( after_initial_deal = len(
state['player']) == 2 and len(state['dealer']) == 2 state['player']) == 2 and len(state['dealer']) == 2
player_hand_value = get_value_of_hand(state['player']) player_hand_value = get_value_of_hand(state['player'])
dealer_hand_value = get_value_of_hand(state['dealer']) dealer_hand_value = get_value_of_hand(state['dealer'])
# Both player and dealer were initially dealt 21: Push. # Both player and dealer were initially dealt 21: Push.
if after_initial_deal and player_hand_value == 21 and dealer_hand_value == 21: if after_initial_deal and player_hand_value == 21 and dealer_hand_value == 21:
state['status'] = BlackjackStatus.PUSHED state['status'] = BlackjackStatus.PUSHED
return True, state return True, state
# Player was originally dealt 21, dealer was not: Blackjack. # Player was originally dealt 21, dealer was not: Blackjack.
if after_initial_deal and player_hand_value == 21: if after_initial_deal and player_hand_value == 21:
state['status'] = BlackjackStatus.BLACKJACK state['status'] = BlackjackStatus.BLACKJACK
return True, state return True, state
# Player went bust: Lost. # Player went bust: Lost.
if player_hand_value == -1: if player_hand_value == -1:
state['status'] = BlackjackStatus.LOST state['status'] = BlackjackStatus.LOST
return True, state return True, state
# Player chose to stay: Deal rest for dealer then determine winner. # Player chose to stay: Deal rest for dealer then determine winner.
if state['status'] == BlackjackStatus.STAYED: if state['status'] == BlackjackStatus.STAYED:
deck = build_deck(state) deck = build_deck(state)
while dealer_hand_value < 17 and dealer_hand_value != -1: while dealer_hand_value < 17 and dealer_hand_value != -1:
next_card = deck.pop() next_card = deck.pop()
state['dealer'].append(next_card) state['dealer'].append(next_card)
dealer_hand_value = get_value_of_hand(state['dealer']) dealer_hand_value = get_value_of_hand(state['dealer'])
if player_hand_value > dealer_hand_value or dealer_hand_value == -1: if player_hand_value > dealer_hand_value or dealer_hand_value == -1:
state['status'] = BlackjackStatus.WON state['status'] = BlackjackStatus.WON
elif dealer_hand_value > player_hand_value: elif dealer_hand_value > player_hand_value:
state['status'] = BlackjackStatus.LOST state['status'] = BlackjackStatus.LOST
else: else:
state['status'] = BlackjackStatus.PUSHED state['status'] = BlackjackStatus.PUSHED
state['player_value'] = get_value_of_hand(state['player']) state['player_value'] = get_value_of_hand(state['player'])
state['dealer_value'] = get_value_of_hand(state['dealer']) state['dealer_value'] = get_value_of_hand(state['dealer'])
return True, state return True, state
return False, state return False, state
def does_insurance_apply(state): def does_insurance_apply(state):
dealer = state['dealer'] dealer = state['dealer']
dealer_hand_value = get_value_of_hand(dealer) dealer_hand_value = get_value_of_hand(dealer)
dealer_first_card_ace = dealer[0][0] == 'A' dealer_first_card_ace = dealer[0][0] == 'A'
dealer_never_hit = len(dealer) == 2 dealer_never_hit = len(dealer) == 2
return dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit return dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit
def can_purchase_insurance(state): def can_purchase_insurance(state):
dealer = state['dealer'] dealer = state['dealer']
dealer_first_card_ace = dealer[0][0] == 'A' dealer_first_card_ace = dealer[0][0] == 'A'
dealer_never_hit = len(dealer) == 2 dealer_never_hit = len(dealer) == 2
return dealer_first_card_ace and dealer_never_hit and not state['player_bought_insurance'] return dealer_first_card_ace and dealer_never_hit and not state['player_bought_insurance']
def can_double_down(state): def can_double_down(state):
player = state['player'] player = state['player']
player_hand_value = get_value_of_hand(player) player_hand_value = get_value_of_hand(player)
player_never_hit = len(player) == 2 player_never_hit = len(player) == 2
return player_hand_value in (10, 11) and player_never_hit return player_hand_value in (10, 11) and player_never_hit
def handle_payout(gambler, state, game): def handle_payout(gambler, state, game):
status = state['status'] status = state['status']
payout = 0 payout = 0
if status == BlackjackStatus.BLACKJACK: if status == BlackjackStatus.BLACKJACK:
game.winnings = floor(game.wager * 3/2) game.winnings = floor(game.wager * 3/2)
payout = game.wager + game.winnings payout = game.wager + game.winnings
elif status == BlackjackStatus.WON: elif status == BlackjackStatus.WON:
game.winnings = game.wager game.winnings = game.wager
payout = game.wager * 2 payout = game.wager * 2
elif status == BlackjackStatus.LOST: elif status == BlackjackStatus.LOST:
dealer = state['dealer'] dealer = state['dealer']
dealer_first_card_ace = dealer[0][0] == 'A' dealer_first_card_ace = dealer[0][0] == 'A'
dealer_never_hit = len(dealer) == 2 dealer_never_hit = len(dealer) == 2
dealer_hand_value = get_value_of_hand(dealer) == 21 dealer_hand_value = get_value_of_hand(dealer) == 21
insurance_applies = dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit insurance_applies = dealer_hand_value == 21 and dealer_first_card_ace and dealer_never_hit
if insurance_applies and state['player_bought_insurance']: if insurance_applies and state['player_bought_insurance']:
game.winnings = 0 game.winnings = 0
payout = game.wager payout = game.wager
else: else:
game.winnings = -game.wager game.winnings = -game.wager
payout = 0 payout = 0
elif status == BlackjackStatus.PUSHED: elif status == BlackjackStatus.PUSHED:
game.winnings = 0 game.winnings = 0
payout = game.wager payout = game.wager
else: else:
raise Exception("Attempted to payout a game that has not finished.") raise Exception("Attempted to payout a game that has not finished.")
gambler.pay_account(game.currency, payout) gambler.pay_account(game.currency, payout)
if game.currency == 'coins': if game.currency == 'coins':
if status in (BlackjackStatus.BLACKJACK, BlackjackStatus.WON): if status in (BlackjackStatus.BLACKJACK, BlackjackStatus.WON):
distribute_wager_badges(gambler, game.wager, won=True) distribute_wager_badges(gambler, game.wager, won=True)
elif status == BlackjackStatus.LOST: elif status == BlackjackStatus.LOST:
distribute_wager_badges(gambler, game.wager, won=False) distribute_wager_badges(gambler, game.wager, won=False)
game.active = False game.active = False
g.db.add(game) g.db.add(game)
return payout return payout
def remove_exploitable_information(state): def remove_exploitable_information(state):
safe_state = state safe_state = state
if len(safe_state['dealer']) >= 2: if len(safe_state['dealer']) >= 2:
safe_state['dealer'][1] = '?' safe_state['dealer'][1] = '?'
safe_state['dealer_value'] = '?' safe_state['dealer_value'] = '?'
return safe_state return safe_state
action_handlers = { action_handlers = {
BlackjackAction.DEAL: handle_blackjack_deal, BlackjackAction.DEAL: handle_blackjack_deal,
BlackjackAction.HIT: handle_blackjack_hit, BlackjackAction.HIT: handle_blackjack_hit,
BlackjackAction.STAY: handle_blackjack_stay, BlackjackAction.STAY: handle_blackjack_stay,
BlackjackAction.DOUBLE_DOWN: handle_blackjack_double_down, BlackjackAction.DOUBLE_DOWN: handle_blackjack_double_down,
BlackjackAction.BUY_INSURANCE: handle_blackjack_buy_insurance, BlackjackAction.BUY_INSURANCE: handle_blackjack_buy_insurance,
} }
def dispatch_action(gambler, action): def dispatch_action(gambler, action):
game = get_active_twentyone_game(gambler) game = get_active_twentyone_game(gambler)
handler = action_handlers[action] handler = action_handlers[action]
if not game: if not game:
raise Exception( raise Exception(
'Gambler has no active blackjack game.') 'Gambler has no active blackjack game.')
if not handler: if not handler:
raise Exception( raise Exception(
f'Illegal action {action} passed to Blackjack#dispatch_action.') f'Illegal action {action} passed to Blackjack#dispatch_action.')
state = json.loads(game.game_state) state = json.loads(game.game_state)
if action == BlackjackAction.BUY_INSURANCE: if action == BlackjackAction.BUY_INSURANCE:
if not can_purchase_insurance(state): if not can_purchase_insurance(state):
raise Exception("Insurance cannot be purchased.") raise Exception("Insurance cannot be purchased.")
charge_gambler(gambler, floor(game.wager / 2), game.currency) charge_gambler(gambler, floor(game.wager / 2), game.currency)
if action == BlackjackAction.DOUBLE_DOWN: if action == BlackjackAction.DOUBLE_DOWN:
if not can_double_down(state): if not can_double_down(state):
raise Exception("Cannot double down.") raise Exception("Cannot double down.")
charge_gambler(gambler, game.wager, game.currency) charge_gambler(gambler, game.wager, game.currency)
game.wager *= 2 game.wager *= 2
new_state = handler(state) new_state = handler(state)
new_state['player_value'] = get_value_of_hand(new_state['player']) new_state['player_value'] = get_value_of_hand(new_state['player'])
new_state['dealer_value'] = get_value_of_hand(new_state['dealer']) new_state['dealer_value'] = get_value_of_hand(new_state['dealer'])
new_state['actions'] = get_available_actions(new_state) new_state['actions'] = get_available_actions(new_state)
game.game_state = json.dumps(new_state) game.game_state = json.dumps(new_state)
g.db.add(game) g.db.add(game)
game_over, final_state = check_for_completion(new_state) game_over, final_state = check_for_completion(new_state)
if game_over: if game_over:
payout = handle_payout(gambler, final_state, game) payout = handle_payout(gambler, final_state, game)
final_state['actions'] = [BlackjackAction.DEAL] final_state['actions'] = [BlackjackAction.DEAL]
final_state['payout'] = payout final_state['payout'] = payout
return final_state return final_state
else: else:
safe_state = remove_exploitable_information(new_state) safe_state = remove_exploitable_information(new_state)
return safe_state return safe_state
def shuffle(collection): def shuffle(collection):
random.shuffle(collection) random.shuffle(collection)
return collection return collection
def build_deck(state): def build_deck(state):
card_counts = {} card_counts = {}
for card in deck: for card in deck:
card_counts[card] = deck_count card_counts[card] = deck_count
cards_already_dealt = state['player'].copy() cards_already_dealt = state['player'].copy()
cards_already_dealt.extend(state['dealer'].copy()) cards_already_dealt.extend(state['dealer'].copy())
for card in cards_already_dealt: for card in cards_already_dealt:
card_counts[card] = card_counts[card] - 1 card_counts[card] = card_counts[card] - 1
deck_without_already_dealt_cards = [] deck_without_already_dealt_cards = []
for card in deck: for card in deck:
amount = card_counts[card] amount = card_counts[card]
for _ in range(amount): for _ in range(amount):
deck_without_already_dealt_cards.append(card) deck_without_already_dealt_cards.append(card)
return shuffle(deck_without_already_dealt_cards) return shuffle(deck_without_already_dealt_cards)
def get_value_of_card(card): def get_value_of_card(card):
rank = card[0] rank = card[0]
return 0 if rank == "A" else min(ranks.index(rank) + 2, 10) return 0 if rank == "A" else min(ranks.index(rank) + 2, 10)
def get_value_of_hand(hand): def get_value_of_hand(hand):
without_aces = sum(map(get_value_of_card, hand)) without_aces = sum(map(get_value_of_card, hand))
ace_count = sum("A" in c for c in hand) ace_count = sum("A" in c for c in hand)
possibilities = [] possibilities = []
for i in range(ace_count + 1): for i in range(ace_count + 1):
value = without_aces + (ace_count - i) + i * 11 value = without_aces + (ace_count - i) + i * 11
possibilities.append(-1 if value > 21 else value) possibilities.append(-1 if value > 21 else value)
return max(possibilities) return max(possibilities)
def get_available_actions(state): def get_available_actions(state):
actions = [] actions = []
if state['status'] == BlackjackStatus.PLAYING: if state['status'] == BlackjackStatus.PLAYING:
actions.append(BlackjackAction.HIT) actions.append(BlackjackAction.HIT)
actions.append(BlackjackAction.STAY) actions.append(BlackjackAction.STAY)
if can_double_down(state): if can_double_down(state):
actions.append(BlackjackAction.DOUBLE_DOWN) actions.append(BlackjackAction.DOUBLE_DOWN)
if can_purchase_insurance(state): if can_purchase_insurance(state):
actions.append(BlackjackAction.BUY_INSURANCE) actions.append(BlackjackAction.BUY_INSURANCE)
return actions return actions

View File

@ -16,10 +16,10 @@ from files.helpers.lottery import *
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def casino(v): def casino(v):
if v.rehab: if v.rehab:
return render_template("casino/rehab.html", v=v) return render_template("casino/rehab.html", v=v)
return render_template("casino.html", v=v) return render_template("casino.html", v=v)
@app.get("/casino/<game>") @app.get("/casino/<game>")
@ -27,27 +27,27 @@ def casino(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def casino_game_page(v, game): def casino_game_page(v, game):
if v.rehab: if v.rehab:
return render_template("casino/rehab.html", v=v) return render_template("casino/rehab.html", v=v)
elif game not in CASINO_GAME_KINDS: elif game not in CASINO_GAME_KINDS:
abort(404) abort(404)
feed = json.dumps(get_game_feed(game)) feed = json.dumps(get_game_feed(game))
leaderboard = json.dumps(get_game_leaderboard(game)) leaderboard = json.dumps(get_game_leaderboard(game))
game_state = '' game_state = ''
if game == 'blackjack': if game == 'blackjack':
if get_active_twentyone_game(v): if get_active_twentyone_game(v):
game_state = json.dumps(get_active_twentyone_game_state(v)) game_state = json.dumps(get_active_twentyone_game_state(v))
return render_template( return render_template(
f"casino/{game}_screen.html", f"casino/{game}_screen.html",
v=v, v=v,
game=game, game=game,
feed=feed, feed=feed,
leaderboard=leaderboard, leaderboard=leaderboard,
game_state=game_state game_state=game_state
) )
@app.get("/casino/<game>/feed") @app.get("/casino/<game>/feed")
@ -55,13 +55,13 @@ def casino_game_page(v, game):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def casino_game_feed(v, game): def casino_game_feed(v, game):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
elif game not in CASINO_GAME_KINDS: elif game not in CASINO_GAME_KINDS:
abort(404) abort(404)
feed = get_game_feed(game) feed = get_game_feed(game)
return {"feed": feed} return {"feed": feed}
# Lottershe # Lottershe
@ -70,11 +70,11 @@ def casino_game_feed(v, game):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def lottershe(v): def lottershe(v):
if v.rehab: if v.rehab:
return render_template("casino/rehab.html", v=v) return render_template("casino/rehab.html", v=v)
participants = get_users_participating_in_lottery() participants = get_users_participating_in_lottery()
return render_template("lottery.html", v=v, participants=participants) return render_template("lottery.html", v=v, participants=participants)
# Slots # Slots
@app.post("/casino/slots") @app.post("/casino/slots")
@ -82,28 +82,28 @@ def lottershe(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def pull_slots(v): def pull_slots(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
wager = int(request.values.get("wager")) wager = int(request.values.get("wager"))
except: except:
abort(400, "Invalid wager.") abort(400, "Invalid wager.")
try: try:
currency = request.values.get("currency") currency = request.values.get("currency")
except: except:
abort(400, "Invalid currency (expected 'coin' or 'marseybux').") abort(400, "Invalid currency (expected 'coin' or 'marseybux').")
if (currency == "coin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): if (currency == "coin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins):
abort(400, f"Not enough {currency} to make that bet") abort(400, 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: if success:
return {"game_state": game_state, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"game_state": game_state, "gambler": {"coins": v.coins, "procoins": v.procoins}}
else: else:
abort(400, f"Wager must be more than 5 {currency}") abort(400, f"Wager must be more than 5 {currency}")
# 21 # 21
@ -112,19 +112,19 @@ def pull_slots(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def blackjack_deal_to_player(v): def blackjack_deal_to_player(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
wager = int(request.values.get("wager")) wager = int(request.values.get("wager"))
currency = request.values.get("currency") currency = request.values.get("currency")
create_new_game(v, wager, currency) create_new_game(v, wager, currency)
state = dispatch_action(v, BlackjackAction.DEAL) state = dispatch_action(v, BlackjackAction.DEAL)
feed = get_game_feed('blackjack') feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except Exception as e: except Exception as e:
abort(400, str(e)) abort(400, str(e))
@app.post("/casino/twentyone/hit") @app.post("/casino/twentyone/hit")
@ -132,15 +132,15 @@ def blackjack_deal_to_player(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def blackjack_player_hit(v): def blackjack_player_hit(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
state = dispatch_action(v, BlackjackAction.HIT) state = dispatch_action(v, BlackjackAction.HIT)
feed = get_game_feed('blackjack') feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except: except:
abort(400, "Unable to hit.") abort(400, "Unable to hit.")
@app.post("/casino/twentyone/stay") @app.post("/casino/twentyone/stay")
@ -148,15 +148,15 @@ def blackjack_player_hit(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def blackjack_player_stay(v): def blackjack_player_stay(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
state = dispatch_action(v, BlackjackAction.STAY) state = dispatch_action(v, BlackjackAction.STAY)
feed = get_game_feed('blackjack') feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except: except:
abort(400, "Unable to stay.") abort(400, "Unable to stay.")
@app.post("/casino/twentyone/double-down") @app.post("/casino/twentyone/double-down")
@ -164,15 +164,15 @@ def blackjack_player_stay(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def blackjack_player_doubled_down(v): def blackjack_player_doubled_down(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
state = dispatch_action(v, BlackjackAction.DOUBLE_DOWN) state = dispatch_action(v, BlackjackAction.DOUBLE_DOWN)
feed = get_game_feed('blackjack') feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except: except:
abort(400, "Unable to double down.") abort(400, "Unable to double down.")
@app.post("/casino/twentyone/buy-insurance") @app.post("/casino/twentyone/buy-insurance")
@ -180,15 +180,15 @@ def blackjack_player_doubled_down(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def blackjack_player_bought_insurance(v): def blackjack_player_bought_insurance(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
state = dispatch_action(v, BlackjackAction.BUY_INSURANCE) state = dispatch_action(v, BlackjackAction.BUY_INSURANCE)
feed = get_game_feed('blackjack') feed = get_game_feed('blackjack')
return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except: except:
abort(403, "Unable to buy insurance.") abort(403, "Unable to buy insurance.")
# Roulette # Roulette
@app.get("/casino/roulette/bets") @app.get("/casino/roulette/bets")
@ -196,12 +196,12 @@ def blackjack_player_bought_insurance(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def roulette_get_bets(v): def roulette_get_bets(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
bets = get_roulette_bets() bets = get_roulette_bets()
return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}}
@app.post("/casino/roulette/place-bet") @app.post("/casino/roulette/place-bet")
@ -209,22 +209,22 @@ def roulette_get_bets(v):
@auth_required @auth_required
@feature_required('GAMBLING') @feature_required('GAMBLING')
def roulette_player_placed_bet(v): def roulette_player_placed_bet(v):
if v.rehab: if v.rehab:
abort(403, "You are under Rehab award effect!") abort(403, "You are under Rehab award effect!")
try: try:
bet = request.values.get("bet") bet = request.values.get("bet")
which = request.values.get("which") which = request.values.get("which")
amount = int(request.values.get("wager")) amount = int(request.values.get("wager"))
currency = request.values.get("currency") currency = request.values.get("currency")
if amount < 5: if amount < 5:
abort(400, f"Minimum bet is 5 {currency}.") abort(400, f"Minimum bet is 5 {currency}.")
gambler_placed_roulette_bet(v, bet, which, amount, currency) gambler_placed_roulette_bet(v, bet, which, amount, currency)
bets = get_roulette_bets() bets = get_roulette_bets()
return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}} return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}}
except: except:
abort(400, "Unable to place a bet.") abort(400, "Unable to place a bet.")

View File

@ -2,218 +2,218 @@
{% block script %} {% block script %}
<script> <script>
function makeBlackjackRequest(action) { function makeBlackjackRequest(action) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", `/casino/twentyone/${action}`); xhr.open("post", `/casino/twentyone/${action}`);
xhr.onload = handleBlackjackResponse.bind(null, xhr); xhr.onload = handleBlackjackResponse.bind(null, xhr);
xhr.blackjackAction = action; xhr.blackjackAction = action;
return xhr; return xhr;
} }
function handleBlackjackResponse(xhr) { function handleBlackjackResponse(xhr) {
try { try {
const response = JSON.parse(xhr.response); const response = JSON.parse(xhr.response);
const succeeded = xhr.status >= 200 && const succeeded = xhr.status >= 200 &&
xhr.status < 300 && xhr.status < 300 &&
response && response &&
!response.error; !response.error;
clearResult(); clearResult();
if (succeeded) { if (succeeded) {
updateBlackjackTable(response.state); updateBlackjackTable(response.state);
updateFeed(response.feed); updateFeed(response.feed);
updatePlayerCurrencies(response.gambler); updatePlayerCurrencies(response.gambler);
} else { } else {
console.error("Error: ", response.error); console.error("Error: ", response.error);
throw new Error("Error") throw new Error("Error")
} }
} catch (error) { } catch (error) {
const results = { const results = {
deal: "Unable to deal a new hand. Is one in progress?", deal: "Unable to deal a new hand. Is one in progress?",
hit: "Unable to hit.", hit: "Unable to hit.",
stay: "Unable to stay.", stay: "Unable to stay.",
"double-down": "Unable to double down.", "double-down": "Unable to double down.",
"buy-insurance": "Unable to buy insurance." "buy-insurance": "Unable to buy insurance."
}; };
updateResult(results[xhr.blackjackAction], "danger"); updateResult(results[xhr.blackjackAction], "danger");
} }
} }
function updateBlackjackActions(state) { function updateBlackjackActions(state) {
const actions = Array.from(document.querySelectorAll('.twentyone-btn')); const actions = Array.from(document.querySelectorAll('.twentyone-btn'));
// Hide all actions. // Hide all actions.
actions.forEach(action => action.style.display = 'none'); actions.forEach(action => action.style.display = 'none');
if (state) { if (state) {
// Show the correct ones. // Show the correct ones.
state.actions.forEach(action => document.getElementById(`twentyone-${action}`).style.display = 'inline-block'); state.actions.forEach(action => document.getElementById(`twentyone-${action}`).style.display = 'inline-block');
} else { } else {
const dealButton = document.getElementById(`twentyone-DEAL`); const dealButton = document.getElementById(`twentyone-DEAL`);
setTimeout(() => { setTimeout(() => {
const dealButton = document.getElementById(`twentyone-DEAL`); const dealButton = document.getElementById(`twentyone-DEAL`);
}) })
if (dealButton) { if (dealButton) {
dealButton.style.display = 'inline-block' dealButton.style.display = 'inline-block'
} }
} }
} }
function updateBlackjackTable(state) { function updateBlackjackTable(state) {
const table = document.getElementById('blackjack-table'); const table = document.getElementById('blackjack-table');
const charactersToRanks = { const charactersToRanks = {
X: "10" X: "10"
}; };
const charactersToSuits = { const charactersToSuits = {
S: "♠️", S: "♠️",
H: "♥️", H: "♥️",
C: "♣️", C: "♣️",
D: "♦️", D: "♦️",
}; };
const makeCardset = (from, who, value) => ` const makeCardset = (from, who, value) => `
<div class="blackjack-cardset"> <div class="blackjack-cardset">
<div class="blackjack-cardset-value"> <div class="blackjack-cardset-value">
${value === -1 ? `${who} went bust` : `${who} has ${value}`} ${value === -1 ? `${who} went bust` : `${who} has ${value}`}
</div> </div>
${from ${from
.filter(card => card !== "?") .filter(card => card !== "?")
.map(([rankCharacter, suitCharacter]) => { .map(([rankCharacter, suitCharacter]) => {
const rank = charactersToRanks[rankCharacter] || rankCharacter; const rank = charactersToRanks[rankCharacter] || rankCharacter;
const suit = charactersToSuits[suitCharacter] || suitCharacter; const suit = charactersToSuits[suitCharacter] || suitCharacter;
return buildPlayingCard(rank, suit); return buildPlayingCard(rank, suit);
}) })
.join('')} .join('')}
</div> </div>
`; `;
const dealerCards = makeCardset(state.dealer, 'Dealer', state.dealer_value); const dealerCards = makeCardset(state.dealer, 'Dealer', state.dealer_value);
const playerCards = makeCardset(state.player, 'Player', state.player_value); const playerCards = makeCardset(state.player, 'Player', state.player_value);
updateBlackjackActions(state); updateBlackjackActions(state);
table.innerHTML = ` table.innerHTML = `
<div style="position: relative;"> <div style="position: relative;">
${dealerCards} ${dealerCards}
</div> </div>
${playerCards} ${playerCards}
`; `;
const currency = state.wager.currency === 'coins' ? 'coins' : 'marseybux'; const currency = state.wager.currency === 'coins' ? 'coins' : 'marseybux';
switch (state.status) { switch (state.status) {
case 'BLACKJACK': case 'BLACKJACK':
updateResult(`Blackjack: Received ${state.payout} ${currency}`, "warning"); updateResult(`Blackjack: Received ${state.payout} ${currency}`, "warning");
break; break;
case 'WON': case 'WON':
updateResult(`Won: Received ${state.payout} ${currency}`, "success"); updateResult(`Won: Received ${state.payout} ${currency}`, "success");
break; break;
case 'PUSHED': case 'PUSHED':
updateResult(`Pushed: Received ${state.wager.amount} ${currency}`, "success"); updateResult(`Pushed: Received ${state.wager.amount} ${currency}`, "success");
break; break;
case 'LOST': case 'LOST':
updateResult(`Lost ${state.wager.amount} ${currency}`, "danger"); updateResult(`Lost ${state.wager.amount} ${currency}`, "danger");
break; break;
default: default:
break; break;
} }
updateCardsetBackgrounds(state); updateCardsetBackgrounds(state);
if (state.status === 'PLAYING') { if (state.status === 'PLAYING') {
updateResult(`${state.wager.amount} ${currency} are at stake`, "success"); updateResult(`${state.wager.amount} ${currency} are at stake`, "success");
} else { } else {
enableWager(); enableWager();
} }
} }
function updateCardsetBackgrounds(state) { function updateCardsetBackgrounds(state) {
const cardsets = Array.from(document.querySelectorAll('.blackjack-cardset')); const cardsets = Array.from(document.querySelectorAll('.blackjack-cardset'));
for (const cardset of cardsets) { for (const cardset of cardsets) {
['PLAYING', 'LOST', 'PUSHED', 'WON', 'BLACKJACK'].forEach(status => cardset.classList.remove(`blackjack-cardset__${status}`)); ['PLAYING', 'LOST', 'PUSHED', 'WON', 'BLACKJACK'].forEach(status => cardset.classList.remove(`blackjack-cardset__${status}`));
cardset.classList.add(`blackjack-cardset__${state.status}`) cardset.classList.add(`blackjack-cardset__${state.status}`)
} }
} }
function deal() { function deal() {
const request = makeBlackjackRequest('deal'); const request = makeBlackjackRequest('deal');
const { amount, currency } = getWager(); const { amount, currency } = getWager();
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("wager", amount); form.append("wager", amount);
form.append("currency", currency); form.append("currency", currency);
request.send(form); request.send(form);
clearResult(); clearResult();
disableWager(); disableWager();
drawFromDeck(); drawFromDeck();
} }
function hit() { function hit() {
const request = makeBlackjackRequest('hit'); const request = makeBlackjackRequest('hit');
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
request.send(form); request.send(form);
drawFromDeck(); drawFromDeck();
} }
function stay() { function stay() {
const request = makeBlackjackRequest('stay'); const request = makeBlackjackRequest('stay');
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
request.send(form); request.send(form);
} }
function doubleDown() { function doubleDown() {
const request = makeBlackjackRequest('double-down'); const request = makeBlackjackRequest('double-down');
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
request.send(form); request.send(form);
drawFromDeck(); drawFromDeck();
} }
function buyInsurance() { function buyInsurance() {
const request = makeBlackjackRequest('buy-insurance'); const request = makeBlackjackRequest('buy-insurance');
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
request.send(form); request.send(form);
} }
function buildBlackjackDeck() { function buildBlackjackDeck() {
document.getElementById('blackjack-table-deck').innerHTML = ` document.getElementById('blackjack-table-deck').innerHTML = `
<div style="position: absolute; top: 150px; left: -100px;"> <div style="position: absolute; top: 150px; left: -100px;">
${buildPlayingCardDeck()} ${buildPlayingCardDeck()}
</div> </div>
`; `;
} }
function initializeBlackjack() { function initializeBlackjack() {
buildBlackjackDeck(); buildBlackjackDeck();
try { try {
const passed = document.getElementById('blackjack-table').dataset.state; const passed = document.getElementById('blackjack-table').dataset.state;
const state = JSON.parse(passed); const state = JSON.parse(passed);
updateBlackjackTable(state); updateBlackjackTable(state);
} catch (error) { } catch (error) {
updateBlackjackActions(); updateBlackjackActions();
} }
} }
if ( if (
document.readyState === "complete" || document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll) (document.readyState !== "loading" && !document.documentElement.doScroll)
) { ) {
initializeBlackjack(); initializeBlackjack();
} else { } else {
document.addEventListener("DOMContentLoaded", initializeBlackjack); document.addEventListener("DOMContentLoaded", initializeBlackjack);
} }
</script> </script>
{% endblock %} {% endblock %}
@ -225,74 +225,74 @@
{% block actions %} {% block actions %}
<style> <style>
.blackjack-cardset { .blackjack-cardset {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 1rem; margin-bottom: 1rem;
max-width: 470px; max-width: 470px;
overflow: auto; overflow: auto;
box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset; box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset;
-webkit-box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset; -webkit-box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset;
-moz-box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset; -moz-box-shadow: 1px 1px 5px 1px rgba(60, 60, 60, 0.81) inset;
} }
.blackjack-cardset__PLAYING { .blackjack-cardset__PLAYING {
background-image: radial-gradient(circle farthest-corner at 10% 20%, rgba(14, 174, 87, 1) 0%, rgba(12, 116, 117, 1) 90%); background-image: radial-gradient(circle farthest-corner at 10% 20%, rgba(14, 174, 87, 1) 0%, rgba(12, 116, 117, 1) 90%);
} }
.blackjack-cardset__LOST { .blackjack-cardset__LOST {
background-image: linear-gradient(109.6deg, rgba(14, 11, 56, 1) 11.2%, rgba(239, 37, 37, 1) 91.1%); background-image: linear-gradient(109.6deg, rgba(14, 11, 56, 1) 11.2%, rgba(239, 37, 37, 1) 91.1%);
} }
.blackjack-cardset__PUSHED { .blackjack-cardset__PUSHED {
background-image: linear-gradient(110.3deg, rgba(73, 93, 109, 1) 4.3%, rgba(49, 55, 82, 1) 96.7%); background-image: linear-gradient(110.3deg, rgba(73, 93, 109, 1) 4.3%, rgba(49, 55, 82, 1) 96.7%);
} }
.blackjack-cardset__WON { .blackjack-cardset__WON {
background-image: radial-gradient( circle farthest-corner at -0.6% 44.4%, rgba(142,252,152,1) 0%, rgba(107,214,250,1) 90% ); background-image: radial-gradient( circle farthest-corner at -0.6% 44.4%, rgba(142,252,152,1) 0%, rgba(107,214,250,1) 90% );
} }
.blackjack-cardset__BLACKJACK { .blackjack-cardset__BLACKJACK {
background-image: linear-gradient(64.3deg, rgba(254, 122, 152, 0.81) 17.7%, rgba(255, 206, 134, 1) 64.7%, rgba(172, 253, 163, 0.64) 112.1%); background-image: linear-gradient(64.3deg, rgba(254, 122, 152, 0.81) 17.7%, rgba(255, 206, 134, 1) 64.7%, rgba(172, 253, 163, 0.64) 112.1%);
} }
.blackjack-cardset .playing-card { .blackjack-cardset .playing-card {
margin-right: -3rem; margin-right: -3rem;
min-width: 100px; min-width: 100px;
} }
.blackjack-cardset-value { .blackjack-cardset-value {
z-index: 3; z-index: 3;
top: 0; top: 0;
right: 0; right: 0;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 2px; letter-spacing: 2px;
text-align: right; text-align: right;
position: absolute; position: absolute;
background-color: rgba(70, 70, 70, 0.6); background-color: rgba(70, 70, 70, 0.6);
padding: 0.5rem; padding: 0.5rem;
color: #DDD; color: #DDD;
} }
.twentyone-btn { .twentyone-btn {
margin-right: 1rem; margin-right: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 2px; letter-spacing: 2px;
} }
</style> </style>
<div role="group" class="btn-group"> <div role="group" class="btn-group">
<button type="button" id="twentyone-DEAL" class="btn btn-primary twentyone-btn" onclick="deal()">Deal</button> <button type="button" id="twentyone-DEAL" class="btn btn-primary twentyone-btn" onclick="deal()">Deal</button>
<button type="button" id="twentyone-HIT" class="btn btn-primary twentyone-btn" onclick="hit()" style="display: none;">Hit</button> <button type="button" id="twentyone-HIT" class="btn btn-primary twentyone-btn" onclick="hit()" style="display: none;">Hit</button>
<button type="button" id="twentyone-STAY" class="btn btn-primary twentyone-btn" onclick="stay()" <button type="button" id="twentyone-STAY" class="btn btn-primary twentyone-btn" onclick="stay()"
style="display: none;">Stay</button> style="display: none;">Stay</button>
<button type="button" id="twentyone-DOUBLE_DOWN" class="btn btn-primary twentyone-btn" onclick="doubleDown()" <button type="button" id="twentyone-DOUBLE_DOWN" class="btn btn-primary twentyone-btn" onclick="doubleDown()"
style="display: none;">Double Down</button> style="display: none;">Double Down</button>
<button type="button" id="twentyone-BUY_INSURANCE" class="btn btn-primary twentyone-btn" onclick="buyInsurance()" <button type="button" id="twentyone-BUY_INSURANCE" class="btn btn-primary twentyone-btn" onclick="buyInsurance()"
style="display: none;">Buy style="display: none;">Buy
Insurance</button> Insurance</button>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -2,468 +2,468 @@
<title>{{game.capitalize()}}</title> <title>{{game.capitalize()}}</title>
{% endblock %} {% block content %} {% endblock %} {% block content %}
<style> <style>
.game_screen-title { .game_screen-title {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-transform: uppercase; text-transform: uppercase;
opacity: 0.6; opacity: 0.6;
} }
.game_screen-title hr { .game_screen-title hr {
flex: 1; flex: 1;
margin-left: 0.5rem; margin-left: 0.5rem;
} }
#casinoGameResult { #casinoGameResult {
visibility: hidden; visibility: hidden;
margin-top: 1rem; margin-top: 1rem;
} }
#casinoGameFeedList { #casinoGameFeedList {
max-height: 110px; max-height: 110px;
overflow: auto; overflow: auto;
list-style-type: none; list-style-type: none;
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
/** /**
* This script block contains generic helper function usable across casino games: * This script block contains generic helper function usable across casino games:
* - Wagers * - Wagers
* - Feed * - Feed
* - Leaderboard * - Leaderboard
*/ */
if ( if (
document.readyState === "complete" || document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll) (document.readyState !== "loading" && !document.documentElement.doScroll)
) { ) {
initializeGame(); initializeGame();
} else { } else {
document.addEventListener("DOMContentLoaded", initializeGame); document.addEventListener("DOMContentLoaded", initializeGame);
} }
function initializeGame() { function initializeGame() {
updateFeed(); updateFeed();
updateLeaderboard(); updateLeaderboard();
} }
function updatePlayerCurrencies(updated) { function updatePlayerCurrencies(updated) {
if (updated.coins) { if (updated.coins) {
document.getElementById("user-coins-amount").innerText = updated.coins; document.getElementById("user-coins-amount").innerText = updated.coins;
} }
if (updated.procoins) { if (updated.procoins) {
document.getElementById("user-bux-amount").innerText = updated.procoins; document.getElementById("user-bux-amount").innerText = updated.procoins;
} }
} }
function getWager() { function getWager() {
const amount = document.getElementById("wagerAmount").value; const amount = document.getElementById("wagerAmount").value;
const currency = document.querySelector( const currency = document.querySelector(
'input[name="wagerCurrency"]:checked' 'input[name="wagerCurrency"]:checked'
).value; ).value;
const genericCurrency = currency == 'marseybux' ? 'procoins' : 'coins'; const genericCurrency = currency == 'marseybux' ? 'procoins' : 'coins';
return { amount, currency: genericCurrency, localCurrency: currency }; return { amount, currency: genericCurrency, localCurrency: currency };
} }
function disableWager() { function disableWager() {
document.getElementById("wagerAmount").disabled = true; document.getElementById("wagerAmount").disabled = true;
document.getElementById("wagerCoins").disabled = true; document.getElementById("wagerCoins").disabled = true;
document.getElementById("wagerProcoins").disabled = true; document.getElementById("wagerProcoins").disabled = true;
} }
function enableWager() { function enableWager() {
document.getElementById("wagerAmount").disabled = false; document.getElementById("wagerAmount").disabled = false;
document.getElementById("wagerCoins").disabled = false; document.getElementById("wagerCoins").disabled = false;
document.getElementById("wagerProcoins").disabled = false; document.getElementById("wagerProcoins").disabled = false;
} }
function updateResult(text, className) { function updateResult(text, className) {
clearResult(); clearResult();
const result = document.getElementById("casinoGameResult"); const result = document.getElementById("casinoGameResult");
result.style.visibility = "visible"; result.style.visibility = "visible";
result.innerText = text; result.innerText = text;
result.classList.add(`alert-${className}`); result.classList.add(`alert-${className}`);
} }
function clearResult() { function clearResult() {
const result = document.getElementById("casinoGameResult"); const result = document.getElementById("casinoGameResult");
result.style.visibility = "hidden"; result.style.visibility = "hidden";
result.innerText = "N/A"; result.innerText = "N/A";
result.classList.remove("alert-success", "alert-danger", "alert-warning"); result.classList.remove("alert-success", "alert-danger", "alert-warning");
} }
function updateFeed(newFeed) { function updateFeed(newFeed) {
let feed; let feed;
if (newFeed) { if (newFeed) {
feed = newFeed; feed = newFeed;
} else { } else {
const gameFeed = document.getElementById("casinoGameFeed"); const gameFeed = document.getElementById("casinoGameFeed");
feed = gameFeed.dataset.feed; feed = gameFeed.dataset.feed;
feed = JSON.parse(feed); feed = JSON.parse(feed);
gameFeed.dataset.feed = ""; gameFeed.dataset.feed = "";
} }
const feedHtml = feed const feedHtml = feed
.map( .map(
(entry) => (entry) =>
` `
<li <li
style="display: flex; align-items: center; justify-content: space-between;" style="display: flex; align-items: center; justify-content: space-between;"
class="${entry.won_or_lost === "won" ? "text-success" : "text-danger"}"> class="${entry.won_or_lost === "won" ? "text-success" : "text-danger"}">
<div> <div>
<a href="/@${entry.user}">@${entry.user}</a> ${entry.won_or_lost} ${entry.amount <a href="/@${entry.user}">@${entry.user}</a> ${entry.won_or_lost} ${entry.amount
} ${entry.currency} } ${entry.currency}
</div> </div>
</li> </li>
` `
) )
.join(""); .join("");
document.getElementById("casinoGameFeedList").innerHTML = feedHtml; document.getElementById("casinoGameFeedList").innerHTML = feedHtml;
} }
function reloadFeed() { function reloadFeed() {
const game = document.getElementById('casino-game-wrapper').dataset.game; const game = document.getElementById('casino-game-wrapper').dataset.game;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("get", `/casino/${game}/feed`); xhr.open("get", `/casino/${game}/feed`);
xhr.onload = handleFeedResponse.bind(null, xhr); xhr.onload = handleFeedResponse.bind(null, xhr);
xhr.send(); xhr.send();
} }
function handleFeedResponse(xhr) { function handleFeedResponse(xhr) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && !response.error; xhr.status >= 200 && xhr.status < 300 && response && !response.error;
if (succeeded) { if (succeeded) {
document.getElementById("casinoGameFeed").dataset.feed = JSON.stringify(response.feed); document.getElementById("casinoGameFeed").dataset.feed = JSON.stringify(response.feed);
updateFeed(); updateFeed();
} else { } else {
console.error("error"); console.error("error");
} }
} }
function updateLeaderboard() { function updateLeaderboard() {
const leaderboardContainer = document.getElementById("gameLeaderboard"); const leaderboardContainer = document.getElementById("gameLeaderboard");
const leaderboardData = JSON.parse(leaderboardContainer.dataset.leaderboard); const leaderboardData = JSON.parse(leaderboardContainer.dataset.leaderboard);
const [biggestWinnerAllTime, biggestWinner24h, biggestLoser24h, biggestLoserAllTime] = [ const [biggestWinnerAllTime, biggestWinner24h, biggestLoser24h, biggestLoserAllTime] = [
'biggestWinnerAllTime', 'biggestWinner24h', 'biggestLoser24h', 'biggestLoserAllTime' 'biggestWinnerAllTime', 'biggestWinner24h', 'biggestLoser24h', 'biggestLoserAllTime'
].map(id => document.getElementById(id)); ].map(id => document.getElementById(id));
const formatLocalCurrencyName = currency => ({ coins: 'coins', procoins: 'marseybux' })[currency]; const formatLocalCurrencyName = currency => ({ coins: 'coins', procoins: 'marseybux' })[currency];
biggestWinnerAllTime.innerHTML = ` biggestWinnerAllTime.innerHTML = `
<a href="/@${leaderboardData.all_time.biggest_win.user}">${leaderboardData.all_time.biggest_win.user}</a> <br><small>${leaderboardData.all_time.biggest_win.amount} ${formatLocalCurrencyName(leaderboardData.all_time.biggest_win.currency)}</small> <a href="/@${leaderboardData.all_time.biggest_win.user}">${leaderboardData.all_time.biggest_win.user}</a> <br><small>${leaderboardData.all_time.biggest_win.amount} ${formatLocalCurrencyName(leaderboardData.all_time.biggest_win.currency)}</small>
`; `;
biggestWinner24h.innerHTML = ` biggestWinner24h.innerHTML = `
<a href="/@${leaderboardData.last_24h.biggest_win.user}">${leaderboardData.last_24h.biggest_win.user}</a> <br> <small>${leaderboardData.last_24h.biggest_win.amount} ${formatLocalCurrencyName(leaderboardData.last_24h.biggest_win.currency)}</small> <a href="/@${leaderboardData.last_24h.biggest_win.user}">${leaderboardData.last_24h.biggest_win.user}</a> <br> <small>${leaderboardData.last_24h.biggest_win.amount} ${formatLocalCurrencyName(leaderboardData.last_24h.biggest_win.currency)}</small>
`; `;
biggestLoser24h.innerHTML = ` biggestLoser24h.innerHTML = `
<a href="/@${leaderboardData.last_24h.biggest_loss.user}">${leaderboardData.last_24h.biggest_loss.user}</a> <br> <small>${leaderboardData.last_24h.biggest_loss.amount} ${formatLocalCurrencyName(leaderboardData.last_24h.biggest_loss.currency)}</small> <a href="/@${leaderboardData.last_24h.biggest_loss.user}">${leaderboardData.last_24h.biggest_loss.user}</a> <br> <small>${leaderboardData.last_24h.biggest_loss.amount} ${formatLocalCurrencyName(leaderboardData.last_24h.biggest_loss.currency)}</small>
`; `;
biggestLoserAllTime.innerHTML = ` biggestLoserAllTime.innerHTML = `
<a href="/@${leaderboardData.all_time.biggest_loss.user}">${leaderboardData.all_time.biggest_loss.user}</a> <br> <small>${leaderboardData.all_time.biggest_loss.amount} ${formatLocalCurrencyName(leaderboardData.all_time.biggest_loss.currency)}</small> <a href="/@${leaderboardData.all_time.biggest_loss.user}">${leaderboardData.all_time.biggest_loss.user}</a> <br> <small>${leaderboardData.all_time.biggest_loss.amount} ${formatLocalCurrencyName(leaderboardData.all_time.biggest_loss.currency)}</small>
`; `;
} }
function getRandomInt(min, max) { function getRandomInt(min, max) {
min = Math.ceil(min); min = Math.ceil(min);
max = Math.floor(max); max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
} }
function getRandomCardAngle() { function getRandomCardAngle() {
const skew = 10 const skew = 10
return getRandomInt(-skew, skew); return getRandomInt(-skew, skew);
} }
function buildPlayingCard(rank, suit) { function buildPlayingCard(rank, suit) {
return ` return `
<div <div
style="transform: scale(0.7) rotateZ(${getRandomCardAngle()}deg)" style="transform: scale(0.7) rotateZ(${getRandomCardAngle()}deg)"
class="playing-card playing-card_${["♥️", "♦️"].includes(suit) ? 'red' : 'black'}"> class="playing-card playing-card_${["♥️", "♦️"].includes(suit) ? 'red' : 'black'}">
<div class="playing-card_small playing-card_topright">${rank}${suit}</div> <div class="playing-card_small playing-card_topright">${rank}${suit}</div>
<div class="playing-card_large">${rank}${suit}</div> <div class="playing-card_large">${rank}${suit}</div>
<div class="playing-card_small playing-card_bottomleft">${rank}${suit}</div> <div class="playing-card_small playing-card_bottomleft">${rank}${suit}</div>
</div> </div>
`; `;
} }
function buildPlayingCardDeck(size = 14) { function buildPlayingCardDeck(size = 14) {
const cards = Array.from({ length: size }, (_, index) => ` const cards = Array.from({ length: size }, (_, index) => `
<div <div
style="bottom: ${index}px; left: ${-index}px" style="bottom: ${index}px; left: ${-index}px"
class="flipped-playing-card"></div> class="flipped-playing-card"></div>
`).join('\n'); `).join('\n');
return ` return `
<div id="playingCardDeck" class="playing-card-deck"> <div id="playingCardDeck" class="playing-card-deck">
${cards} ${cards}
</div> </div>
`; `;
} }
function drawFromDeck() { function drawFromDeck() {
try { try {
const [topCard] = Array.from(document.querySelectorAll("#playingCardDeck > *")).reverse(); const [topCard] = Array.from(document.querySelectorAll("#playingCardDeck > *")).reverse();
topCard.classList.add('drawing-a-card'); topCard.classList.add('drawing-a-card');
setTimeout(() => { setTimeout(() => {
topCard.classList.remove('drawing-a-card'); topCard.classList.remove('drawing-a-card');
}, 600); }, 600);
} catch { } } catch { }
} }
</script> </script>
{% block script %} {% endblock %} {% block script %} {% endblock %}
<style> <style>
@keyframes drawing { @keyframes drawing {
from { from {
left: 0; left: 0;
opacity: 1; opacity: 1;
} }
to { to {
left: 100px; left: 100px;
opacity: 0; opacity: 0;
} }
} }
.drawing-a-card { .drawing-a-card {
animation: drawing 1s ease-in-out; animation: drawing 1s ease-in-out;
} }
.playing-card-deck { .playing-card-deck {
position: relative; position: relative;
z-index: 3; z-index: 3;
box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
-webkit-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); -webkit-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
-moz-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); -moz-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
} }
.flipped-playing-card { .flipped-playing-card {
position: absolute; position: absolute;
width: 100px; width: 100px;
height: 150px; height: 150px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #21262C; border: 1px solid #21262C;
background-color: #FF66AC; background-color: #FF66AC;
transform: scale(0.7); transform: scale(0.7);
} }
.playing-card { .playing-card {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100px; width: 100px;
height: 150px; height: 150px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #21262C; border: 1px solid #21262C;
background-color: #FFF; background-color: #FFF;
box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
-webkit-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); -webkit-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
-moz-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56); -moz-box-shadow: -5px 5px 5px 0px rgba(60, 60, 60, 0.56);
} }
.playing-card_red { .playing-card_red {
color: #ff0000; color: #ff0000;
} }
.playing-card_black { .playing-card_black {
color: #333; color: #333;
} }
.playing-card_small { .playing-card_small {
font-size: 18px; font-size: 18px;
position: absolute; position: absolute;
} }
.playing-card_large { .playing-card_large {
font-size: 48px; font-size: 48px;
text-align: center; text-align: center;
} }
.playing-card_topright { .playing-card_topright {
top: 6px; top: 6px;
right: 6px; right: 6px;
} }
.playing-card_bottomleft { .playing-card_bottomleft {
bottom: 6px; bottom: 6px;
left: 6px; left: 6px;
transform: scaleX(-1) scaleY(-1); transform: scaleX(-1) scaleY(-1);
} }
#casinoGameResult { #casinoGameResult {
text-transform: uppercase; text-transform: uppercase;
text-align: center; text-align: center;
letter-spacing: 3px; letter-spacing: 3px;
font-weight: 700; font-weight: 700;
} }
.casino-game-leaderboard { .casino-game-leaderboard {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.casino-game-leaderboard-info { .casino-game-leaderboard-info {
text-align: right; text-align: right;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 2px; letter-spacing: 2px;
} }
.leaderboard-marsey-trophy { .leaderboard-marsey-trophy {
position: relative; position: relative;
width: 100px; width: 100px;
height: 100px; height: 100px;
} }
.leaderboard-marsey-trophy__marsey { .leaderboard-marsey-trophy__marsey {
position: relative; position: relative;
z-index: 1; z-index: 1;
height: 100px; height: 100px;
} }
.leaderboard-marsey-trophy__trophy { .leaderboard-marsey-trophy__trophy {
position: absolute; position: absolute;
right: 0; right: 0;
bottom: 0; bottom: 0;
z-index: 2; z-index: 2;
font-size: 48px; font-size: 48px;
} }
</style> </style>
<div id="casino-game-wrapper" data-game="{{game}}" class="container-fluid" style="max-width: 500px"> <div id="casino-game-wrapper" data-game="{{game}}" class="container-fluid" style="max-width: 500px">
<div class="row row-cols-1"> <div class="row row-cols-1">
<div class="col game_screen-title"> <div class="col game_screen-title">
<h3>{{game}}</h3> <h3>{{game}}</h3>
<hr> <hr>
</div> </div>
<div class="col">{% block screen %} {% endblock %}</div> <div class="col">{% block screen %} {% endblock %}</div>
<div class="col"> <div class="col">
<div id="casinoGameResult" class="alert" role="alert"> <div id="casinoGameResult" class="alert" role="alert">
{% block result %} {% endblock %} {% block result %} {% endblock %}
</div> </div>
</div> </div>
<div class="col"> <div class="col">
<div class="row row-cols-2"> <div class="row row-cols-2">
<div class="col"> <div class="col">
<div class="game_screen-title"> <div class="game_screen-title">
<h5>Wager</h5> <h5>Wager</h5>
<hr> <hr>
</div> </div>
<input id="wagerAmount" type="number" min="5" step="1" value="5" class="form-control"> <input id="wagerAmount" type="number" min="5" step="1" value="5" class="form-control">
</div> </div>
<div class="col"> <div class="col">
<div class="game_screen-title"> <div class="game_screen-title">
<h5>Currency</h5> <h5>Currency</h5>
<hr> <hr>
</div> </div>
<div class="btn-group" role="group" aria-label="Select a currency."> <div class="btn-group" role="group" aria-label="Select a currency.">
<input type="radio" class="btn-check" name="wagerCurrency" autocomplete="off" id="wagerCoins" <input type="radio" class="btn-check" name="wagerCurrency" autocomplete="off" id="wagerCoins"
value="coin" checked> value="coin" checked>
<label for="wagerCoins" class="btn btn-primary"> <label for="wagerCoins" class="btn btn-primary">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" width="32" data-bs-toggle="tooltip" <img src="/i/rDrama/coins.webp?v=3009" alt="coin" width="32" data-bs-toggle="tooltip"
data-bs-placement="bottom" title="Coin" aria-label="Coin"> data-bs-placement="bottom" title="Coin" aria-label="Coin">
</label> </label>
<input type="radio" class="btn-check ml-2" name="wagerCurrency" autocomplete="off" id="wagerProcoins" <input type="radio" class="btn-check ml-2" name="wagerCurrency" autocomplete="off" id="wagerProcoins"
value="marseybux"> value="marseybux">
<label for="wagerProcoins" class="btn btn-primary"> <label for="wagerProcoins" class="btn btn-primary">
<img src="/i/marseybux.webp?v=2000" alt="marseybux" width="32" data-bs-toggle="tooltip" <img src="/i/marseybux.webp?v=2000" alt="marseybux" width="32" data-bs-toggle="tooltip"
data-bs-placement="bottom" title="Marseybux" aria-label="Marseybux"> data-bs-placement="bottom" title="Marseybux" aria-label="Marseybux">
</label> </label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col"> <div class="col">
<div class="game_screen-title"> <div class="game_screen-title">
<h5>{% block actiontext %}Actions{% endblock %}</h5> <h5>{% block actiontext %}Actions{% endblock %}</h5>
<hr> <hr>
</div> </div>
{% block actions %} {% endblock %} {% block actions %} {% endblock %}
</div> </div>
<div id="casinoGameFeed" data-feed="{{feed}}" class="col"> <div id="casinoGameFeed" data-feed="{{feed}}" class="col">
<div class="game_screen-title"> <div class="game_screen-title">
<h5>Feed</h5> <h5>Feed</h5>
<hr> <hr>
</div> </div>
<ul id="casinoGameFeedList"></ul> <ul id="casinoGameFeedList"></ul>
<button type="button" class="btn btn-secondary" style="width: 100%" onclick="reloadFeed()"> <button type="button" class="btn btn-secondary" style="width: 100%" onclick="reloadFeed()">
Reload Feed Reload Feed
</button> </button>
</div> </div>
<div class="col"> <div class="col">
<div class="game_screen-title"> <div class="game_screen-title">
<h5>Leaders</h5> <h5>Leaders</h5>
<hr> <hr>
</div> </div>
<div id="gameLeaderboard" data-leaderboard="{{leaderboard}}"> <div id="gameLeaderboard" data-leaderboard="{{leaderboard}}">
<!-- Biggest Winner All Time --> <!-- Biggest Winner All Time -->
<div class="casino-game-leaderboard"> <div class="casino-game-leaderboard">
<div class="leaderboard-marsey-trophy"> <div class="leaderboard-marsey-trophy">
<img class="leaderboard-marsey-trophy__marsey" src="/e/marseyhappytears.webp"> <img class="leaderboard-marsey-trophy__marsey" src="/e/marseyhappytears.webp">
<i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: gold;"></i> <i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: gold;"></i>
</div> </div>
<div class="casino-game-leaderboard-info"> <div class="casino-game-leaderboard-info">
<small>Biggest Winner (All Time)</small> <small>Biggest Winner (All Time)</small>
<h3 id="biggestWinnerAllTime">-</h3> <h3 id="biggestWinnerAllTime">-</h3>
</div> </div>
</div> </div>
<!-- Biggest Winner 24h --> <!-- Biggest Winner 24h -->
<div class="casino-game-leaderboard"> <div class="casino-game-leaderboard">
<div class="leaderboard-marsey-trophy"> <div class="leaderboard-marsey-trophy">
<img class="leaderboard-marsey-trophy__marsey" src="/e/marseyexcited.webp"> <img class="leaderboard-marsey-trophy__marsey" src="/e/marseyexcited.webp">
<i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: gold;"></i> <i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: gold;"></i>
</div> </div>
<div class="casino-game-leaderboard-info"> <div class="casino-game-leaderboard-info">
<small>Biggest Winner (Last 24h)</small> <small>Biggest Winner (Last 24h)</small>
<h3 id="biggestWinner24h">-</h3> <h3 id="biggestWinner24h">-</h3>
</div> </div>
</div> </div>
<!-- Biggest Loser 24h --> <!-- Biggest Loser 24h -->
<div class="casino-game-leaderboard"> <div class="casino-game-leaderboard">
<div class="leaderboard-marsey-trophy"> <div class="leaderboard-marsey-trophy">
<img class="leaderboard-marsey-trophy__marsey" src="/e/marseycry.webp"> <img class="leaderboard-marsey-trophy__marsey" src="/e/marseycry.webp">
<i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: darkred;"></i> <i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: darkred;"></i>
</div> </div>
<div class="casino-game-leaderboard-info"> <div class="casino-game-leaderboard-info">
<small>Biggest Loser (Last 24h)</small> <small>Biggest Loser (Last 24h)</small>
<h3 id="biggestLoser24h">-</h3> <h3 id="biggestLoser24h">-</h3>
</div> </div>
</div> </div>
<!-- Biggest Loser All Time --> <!-- Biggest Loser All Time -->
<div class="casino-game-leaderboard"> <div class="casino-game-leaderboard">
<div class="leaderboard-marsey-trophy"> <div class="leaderboard-marsey-trophy">
<img class="leaderboard-marsey-trophy__marsey" src="/e/marseyrain.webp"> <img class="leaderboard-marsey-trophy__marsey" src="/e/marseyrain.webp">
<i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: darkred;"></i> <i class="fas fa-trophy leaderboard-marsey-trophy__trophy" style="color: darkred;"></i>
</div> </div>
<div class="casino-game-leaderboard-info"> <div class="casino-game-leaderboard-info">
<small>Biggest Loser (All Time)</small> <small>Biggest Loser (All Time)</small>
<h3 id="biggestLoserAllTime">-</h3> <h3 id="biggestLoserAllTime">-</h3>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,11 +1,11 @@
{% extends "default.html" %} {% block content %} {% extends "default.html" %} {% block content %}
<div style="text-transform: uppercase; letter-spacing: 2px; display: flex; flex-direction: column; align-items: center;"> <div style="text-transform: uppercase; letter-spacing: 2px; display: flex; flex-direction: column; align-items: center;">
<div style="margin-top: 3rem;"> <div style="margin-top: 3rem;">
<h2>No one was there for Britney…</h2> <h2>No one was there for Britney…</h2>
</div> </div>
<div style="text-align: right; margin-bottom: 3rem;"> <div style="text-align: right; margin-bottom: 3rem;">
<h2>…but were here for you. Youve been checked into Rehab.</h2> <h2>…but were here for you. Youve been checked into Rehab.</h2>
</div> </div>
<img src="/i/rDrama/brit.webp" style="text-align: center"> <img src="/i/rDrama/brit.webp" style="text-align: center">
</div> </div>
{% endblock %} {% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -2,124 +2,124 @@
{% block script %} {% block script %}
<script type="text/javascript"> <script type="text/javascript">
function pullSlots() { function pullSlots() {
const { amount, currency } = getWager(); const { amount, currency } = getWager();
console.log({amount, currency}) console.log({amount, currency})
disableWager(); disableWager();
clearResult(); clearResult();
document.getElementById("casinoSlotsPull").disabled = true; document.getElementById("casinoSlotsPull").disabled = true;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", "/casino/slots"); xhr.open("post", "/casino/slots");
xhr.onload = handleSlotsResponse.bind(null, xhr); xhr.onload = handleSlotsResponse.bind(null, xhr);
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("wager", amount); form.append("wager", amount);
form.append("currency", currency); form.append("currency", currency);
xhr.send(form); xhr.send(form);
} }
function handleSlotsResponse(xhr) { function handleSlotsResponse(xhr) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && !response.error; xhr.status >= 200 && xhr.status < 300 && response && !response.error;
if (succeeded) { if (succeeded) {
const { game_state, gambler } = response; const { game_state, gambler } = response;
const state = JSON.parse(game_state); const state = JSON.parse(game_state);
const reels = Array.from(document.querySelectorAll(".slots_reel")); const reels = Array.from(document.querySelectorAll(".slots_reel"));
const symbols = state.symbols.split(","); const symbols = state.symbols.split(",");
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
reels[i].innerHTML = symbols[i]; reels[i].innerHTML = symbols[i];
} }
let className; let className;
if (state.text.includes("Jackpot")) { if (state.text.includes("Jackpot")) {
className = "warning"; className = "warning";
} else if (state.text.includes("Won")) { } else if (state.text.includes("Won")) {
className = "success"; className = "success";
} else if (state.text.includes("Lost")) { } else if (state.text.includes("Lost")) {
className = "danger"; className = "danger";
} else { } else {
className = "success"; className = "success";
} }
updateResult(state.text, className); updateResult(state.text, className);
updatePlayerCurrencies(gambler); updatePlayerCurrencies(gambler);
reloadFeed() reloadFeed()
} else { } else {
updateResult(response.error, "danger"); updateResult(response.error, "danger");
console.error(response.error); console.error(response.error);
} }
enableWager(); enableWager();
document.getElementById("casinoSlotsPull").disabled = false; document.getElementById("casinoSlotsPull").disabled = false;
} }
</script> </script>
{% endblock %} {% endblock %}
{% block screen %} {% block screen %}
<style> <style>
.slots_reels { .slots_reels {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.slots_reel { .slots_reel {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100px; width: 100px;
height: 100px; height: 100px;
border: 2px solid black; border: 2px solid black;
background-color: var(--gray); background-color: var(--gray);
border: 1px solid var(--black); border: 1px solid var(--black);
border-radius: 8px; border-radius: 8px;
font-size: 64px; font-size: 64px;
} }
.slots_reel:nth-child(2) { .slots_reel:nth-child(2) {
margin: 0 1rem; margin: 0 1rem;
} }
</style> </style>
<div class="slots_reels"> <div class="slots_reels">
<div class="slots_reel"> <div class="slots_reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin"> <img src="/i/rDrama/coins.webp?v=3009" alt="coin">
</div> </div>
<div class="slots_reel"> <div class="slots_reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin"> <img src="/i/rDrama/coins.webp?v=3009" alt="coin">
</div> </div>
<div class="slots_reel"> <div class="slots_reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin"> <img src="/i/rDrama/coins.webp?v=3009" alt="coin">
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block actions %} {% block actions %}
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<button <button
type="button" type="button"
id="casinoSlotsPull" id="casinoSlotsPull"
class="btn btn-primary" class="btn btn-primary"
style="width: 100%" style="width: 100%"
onclick="pullSlots()" onclick="pullSlots()"
> >
Pull Pull
</button> </button>
</div> </div>
{% endblock %} {% endblock %}