4319 lines
161 KiB
HTML
4319 lines
161 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta
|
||
name="viewport"
|
||
content="width=device-width, initial-scale=1, viewport-fit=cover"
|
||
/>
|
||
<title data-i18n="diceMatch.pageTitle">Dice Match</title>
|
||
<link rel="stylesheet" href="../../common/theme.css" />
|
||
<link rel="stylesheet" href="../common/game-match.css" />
|
||
<link rel="stylesheet" href="../common/game-loading.css" />
|
||
<style>
|
||
:root {
|
||
color-scheme: dark;
|
||
--dice-frame-width: 375px;
|
||
--dice-frame-height: 812px;
|
||
--dice-scale: 1;
|
||
--dice-playfield-offset: 24px;
|
||
--dice-topbar-offset: -24px;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html,
|
||
body {
|
||
width: 100%;
|
||
min-width: 100%;
|
||
height: 100%;
|
||
min-height: 100%;
|
||
margin: 0;
|
||
overflow: hidden;
|
||
background: #030623;
|
||
font-family:
|
||
'Source Han Sans SC', 'Helvetica Neue', Arial, sans-serif;
|
||
-webkit-font-smoothing: antialiased;
|
||
text-rendering: geometricprecision;
|
||
}
|
||
|
||
body {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: center;
|
||
}
|
||
|
||
button {
|
||
padding: 0;
|
||
border: 0;
|
||
color: inherit;
|
||
font: inherit;
|
||
background: transparent;
|
||
cursor: pointer;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
.dice-stage {
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: 768px;
|
||
height: calc(var(--dice-frame-height) * var(--dice-scale));
|
||
margin: 0 auto;
|
||
overflow: hidden;
|
||
background: #030623;
|
||
}
|
||
|
||
.dice-frame {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: var(--dice-frame-width);
|
||
height: var(--dice-frame-height);
|
||
overflow: hidden;
|
||
background: #030623;
|
||
transform: scale(var(--dice-scale));
|
||
transform-origin: top left;
|
||
}
|
||
|
||
.dice-frame.is-matching {
|
||
animation: screen-impact-shake 520ms ease-out 420ms both;
|
||
}
|
||
|
||
.dice-frame.is-self-win-compare {
|
||
animation: you-win-screen-smash 780ms
|
||
cubic-bezier(0.14, 0.84, 0.18, 1) 430ms both;
|
||
}
|
||
|
||
.game-content {
|
||
position: absolute;
|
||
inset: 0;
|
||
transform: translateY(
|
||
calc(-44px + var(--dice-playfield-offset))
|
||
);
|
||
}
|
||
|
||
.layer {
|
||
position: absolute;
|
||
}
|
||
|
||
.vs-badge {
|
||
transform-origin: 50% 50%;
|
||
will-change: transform, filter;
|
||
}
|
||
|
||
.vs-badge::after {
|
||
position: absolute;
|
||
inset: 14px 10px;
|
||
content: '';
|
||
border: 2px solid rgba(255, 213, 87, 0.68);
|
||
border-radius: 50%;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: scale(0.62);
|
||
filter: drop-shadow(0 0 10px rgba(255, 96, 243, 0.68));
|
||
}
|
||
|
||
.dice-frame.is-matching .vs-badge {
|
||
animation: vs-drop-impact 900ms
|
||
cubic-bezier(0.13, 0.92, 0.18, 1) both;
|
||
}
|
||
|
||
.dice-frame.is-matching .vs-badge::after {
|
||
animation: vs-impact-ring 580ms ease-out 430ms both;
|
||
}
|
||
|
||
.layer-img {
|
||
position: absolute;
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
max-width: none;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
}
|
||
|
||
.cover {
|
||
object-fit: cover;
|
||
}
|
||
|
||
.fill {
|
||
object-fit: fill;
|
||
}
|
||
|
||
.status-time {
|
||
position: absolute;
|
||
top: 13px;
|
||
left: 33.87px;
|
||
width: 54px;
|
||
height: 15px;
|
||
color: #ffffff;
|
||
font-family: Helvetica, Arial, sans-serif;
|
||
font-size: 15px;
|
||
line-height: 15px;
|
||
text-align: center;
|
||
}
|
||
|
||
.top-bar {
|
||
position: absolute;
|
||
top: 60px;
|
||
left: 16px;
|
||
z-index: 10;
|
||
width: 343px;
|
||
height: 36px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
transform: translateY(var(--dice-topbar-offset));
|
||
}
|
||
|
||
.top-back {
|
||
position: relative;
|
||
width: 46px;
|
||
height: 36px;
|
||
flex: 0 0 46px;
|
||
transform: translateY(-17px);
|
||
}
|
||
|
||
.top-game-icon {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 36px;
|
||
height: 36px;
|
||
overflow: hidden;
|
||
border-radius: 6px;
|
||
transform: translate(-50%, -50%);
|
||
}
|
||
|
||
.top-coin {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
width: max-content;
|
||
min-width: 102px;
|
||
max-width: 176px;
|
||
height: 32px;
|
||
flex: 0 1 auto;
|
||
gap: 4px;
|
||
overflow: visible;
|
||
padding: 0 7px;
|
||
/* border: 1px solid rgba(108, 113, 194, 0.96);
|
||
border-radius: 4px; */
|
||
/* background: linear-gradient(
|
||
180deg,
|
||
rgba(15, 18, 58, 0.92) 0%,
|
||
rgba(4, 6, 31, 0.86) 100%
|
||
);
|
||
box-shadow:
|
||
inset 0 0 0 1px rgba(255, 255, 255, 0.05),
|
||
0 0 11px rgba(79, 93, 202, 0.22); */
|
||
color: #ffe38e;
|
||
font-family: inherit;
|
||
font-size: 18px;
|
||
font-weight: 900;
|
||
line-height: 20px;
|
||
margin: 0;
|
||
text-align: center;
|
||
text-shadow:
|
||
0 1px 1px rgba(65, 23, 0, 0.86),
|
||
0 0 6px rgba(255, 204, 74, 0.34);
|
||
white-space: nowrap;
|
||
appearance: none;
|
||
-webkit-appearance: none;
|
||
border-image: none;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.top-coin span {
|
||
display: block;
|
||
min-width: max-content;
|
||
max-width: none;
|
||
flex: 0 0 auto;
|
||
overflow: visible;
|
||
line-height: 20px;
|
||
}
|
||
|
||
.top-coin.is-compact span {
|
||
max-width: none;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.top-coin img {
|
||
display: block;
|
||
width: 23px;
|
||
height: 23px;
|
||
flex: 0 0 23px;
|
||
filter: drop-shadow(0 0 5px rgba(255, 197, 49, 0.45));
|
||
}
|
||
|
||
.top-coin-plus {
|
||
color: #ffd15a;
|
||
font-size: 21px;
|
||
font-weight: 900;
|
||
/* line-height: 8px; */
|
||
text-shadow:
|
||
0 1px 1px rgba(65, 23, 0, 0.86),
|
||
0 0 8px rgba(255, 178, 47, 0.74);
|
||
align-items: center;
|
||
/* font-size: 0; */
|
||
display: inline-block;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.top-coin.is-crediting {
|
||
animation: top-coin-credit-pulse 720ms ease-out both;
|
||
}
|
||
|
||
.coin-credit-layer {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 60;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.coin-credit-flyer {
|
||
position: absolute;
|
||
left: var(--coin-credit-x);
|
||
top: var(--coin-credit-y);
|
||
width: 24px;
|
||
height: 24px;
|
||
opacity: 0;
|
||
filter: drop-shadow(0 0 6px rgba(255, 203, 67, 0.86));
|
||
transform: translate3d(0, 0, 0) scale(0.78);
|
||
animation: coin-credit-fly 920ms
|
||
cubic-bezier(0.2, 0.82, 0.16, 1) forwards;
|
||
animation-delay: var(--coin-credit-delay, 0ms);
|
||
will-change: transform, opacity;
|
||
}
|
||
|
||
.language-switcher {
|
||
position: absolute;
|
||
top: 41px;
|
||
right: 0;
|
||
z-index: 20;
|
||
width: 102px;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.language-button {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
height: 22px;
|
||
border: 1px solid rgba(108, 113, 194, 0.68);
|
||
border-radius: 4px;
|
||
background: rgba(7, 10, 40, 0.72);
|
||
color: #ffe38e;
|
||
font-size: 12px;
|
||
font-weight: 900;
|
||
line-height: 14px;
|
||
letter-spacing: 0;
|
||
text-shadow: 0 0 5px rgba(255, 204, 74, 0.42);
|
||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||
}
|
||
|
||
.language-button::after {
|
||
margin-left: 4px;
|
||
color: #ffd15a;
|
||
content: 'v';
|
||
font-size: 10px;
|
||
line-height: 10px;
|
||
}
|
||
|
||
.language-menu {
|
||
position: absolute;
|
||
top: 26px;
|
||
right: 0;
|
||
width: 102px;
|
||
padding: 4px;
|
||
border: 1px solid rgba(108, 113, 194, 0.78);
|
||
border-radius: 6px;
|
||
background: rgba(5, 8, 35, 0.94);
|
||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.42);
|
||
}
|
||
|
||
.language-menu button {
|
||
display: block;
|
||
width: 100%;
|
||
min-height: 24px;
|
||
border-radius: 4px;
|
||
color: rgba(255, 227, 142, 0.78);
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
line-height: 14px;
|
||
text-align: center;
|
||
}
|
||
|
||
.language-menu button.is-active {
|
||
background: rgba(255, 209, 90, 0.16);
|
||
color: #ffd15a;
|
||
}
|
||
|
||
html[dir='rtl'] .language-button::after {
|
||
margin-right: 4px;
|
||
margin-left: 0;
|
||
}
|
||
|
||
.lobby-avatar-glow {
|
||
top: 163px;
|
||
left: 151px;
|
||
width: 73px;
|
||
height: 73px;
|
||
}
|
||
|
||
.lobby-player-card {
|
||
top: 146px;
|
||
left: 131px;
|
||
width: 113px;
|
||
height: 158px;
|
||
}
|
||
|
||
.dice-avatar,
|
||
.dice-avatar-frame {
|
||
position: absolute;
|
||
display: block;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
}
|
||
|
||
.dice-avatar {
|
||
z-index: 4;
|
||
width: 71px;
|
||
height: 71px;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
clip-path: circle(50% at 50% 50%);
|
||
background: transparent;
|
||
}
|
||
|
||
.dice-avatar-frame {
|
||
z-index: 5;
|
||
width: 84px;
|
||
height: 84px;
|
||
object-fit: contain;
|
||
opacity: 0;
|
||
}
|
||
|
||
.dice-avatar-frame.has-frame {
|
||
opacity: 1;
|
||
}
|
||
|
||
.avatar-hole-mask {
|
||
position: absolute;
|
||
z-index: 3;
|
||
width: 74px;
|
||
height: 74px;
|
||
border-radius: 50%;
|
||
background: radial-gradient(
|
||
circle at 50% 44%,
|
||
rgba(27, 19, 36, 0.98) 0%,
|
||
rgba(13, 10, 22, 0.98) 68%,
|
||
rgba(6, 5, 15, 0.98) 100%
|
||
);
|
||
box-shadow:
|
||
inset 0 0 16px rgba(0, 0, 0, 0.72),
|
||
0 0 10px rgba(255, 174, 58, 0.12);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.avatar-hole-mask-lobby {
|
||
top: 163px;
|
||
left: 150px;
|
||
}
|
||
|
||
.avatar-hole-mask-self {
|
||
top: 163px;
|
||
left: 40px;
|
||
}
|
||
|
||
.dice-avatar.is-empty,
|
||
.dice-avatar-frame.is-empty {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
}
|
||
|
||
.dice-avatar-lobby {
|
||
top: 163px;
|
||
left: 152px;
|
||
}
|
||
|
||
.dice-avatar-frame-lobby {
|
||
top: 158px;
|
||
left: 145px;
|
||
}
|
||
|
||
.dice-avatar-self {
|
||
top: 168px;
|
||
left: 45px;
|
||
}
|
||
|
||
.dice-avatar-frame-self {
|
||
top: 158px;
|
||
left: 35px;
|
||
}
|
||
|
||
.dice-avatar-opponent {
|
||
top: 168px;
|
||
left: 266px;
|
||
}
|
||
|
||
.dice-avatar-frame-opponent {
|
||
top: 158px;
|
||
left: 256px;
|
||
}
|
||
|
||
.lobby-player-name {
|
||
position: absolute;
|
||
top: 247px;
|
||
left: 147px;
|
||
width: 81px;
|
||
height: 32px;
|
||
overflow: hidden;
|
||
color: #ffffff;
|
||
font-size: 14px;
|
||
line-height: 16px;
|
||
text-align: center;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.player-name {
|
||
position: absolute;
|
||
top: 247px;
|
||
left: 37px;
|
||
width: 81px;
|
||
height: 32px;
|
||
overflow: hidden;
|
||
color: #ffffff;
|
||
font-size: 14px;
|
||
line-height: 16px;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.opponent-mark {
|
||
position: absolute;
|
||
top: 247px;
|
||
left: 258px;
|
||
width: 81px;
|
||
height: 16px;
|
||
color: #ffffff;
|
||
font-size: 14px;
|
||
line-height: 16px;
|
||
text-align: center;
|
||
}
|
||
|
||
.opponent-name {
|
||
position: absolute;
|
||
top: 247px;
|
||
left: 258px;
|
||
z-index: 6;
|
||
width: 81px;
|
||
height: 32px;
|
||
overflow: hidden;
|
||
color: #ffffff;
|
||
font-size: 14px;
|
||
line-height: 16px;
|
||
text-align: center;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.matching-label {
|
||
position: absolute;
|
||
top: 270px;
|
||
left: 282px;
|
||
width: 54px;
|
||
height: 16px;
|
||
overflow: hidden;
|
||
color: #b9defb;
|
||
font-size: 10px;
|
||
line-height: 16px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.winner-label {
|
||
position: absolute;
|
||
top: 608px;
|
||
left: 123px;
|
||
width: 130px;
|
||
height: 14px;
|
||
color: transparent;
|
||
font-size: 14px;
|
||
line-height: 14px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
background-image: url('assets/figma/winner-text-fill.png');
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: cover;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
}
|
||
|
||
.stake {
|
||
position: absolute;
|
||
top: 634px;
|
||
width: 77px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
overflow: hidden;
|
||
padding-left: 8px;
|
||
color: #ffe293;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
line-height: 16px;
|
||
text-shadow: 0 0 1px #d5942b;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.stake:active {
|
||
filter: brightness(1.15);
|
||
}
|
||
|
||
.stake[data-selected='true'] {
|
||
filter: drop-shadow(0 0 5px rgba(255, 197, 47, 0.4));
|
||
}
|
||
|
||
.stake-bg {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 77px;
|
||
height: 28px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.stake[data-selected='true'] .stake-bg {
|
||
display: none;
|
||
}
|
||
|
||
.stake-selected-slice {
|
||
position: absolute;
|
||
top: 0;
|
||
z-index: 0;
|
||
display: none;
|
||
width: 45px;
|
||
height: 28px;
|
||
pointer-events: none;
|
||
background-image: url('assets/figma/stake-fill.png');
|
||
background-repeat: no-repeat;
|
||
background-size: 105px 28px;
|
||
}
|
||
|
||
.stake[data-selected='true'] .stake-selected-slice {
|
||
display: block;
|
||
}
|
||
|
||
.stake-selected-left {
|
||
left: 0;
|
||
background-position: left top;
|
||
}
|
||
|
||
.stake-selected-right {
|
||
right: 0;
|
||
background-position: right top;
|
||
}
|
||
|
||
.stake-coin {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 14px;
|
||
height: 14px;
|
||
flex: 0 0 14px;
|
||
}
|
||
|
||
.stake-value {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: 12px;
|
||
line-height: 12px;
|
||
}
|
||
|
||
.match-button {
|
||
position: absolute;
|
||
top: 690px;
|
||
left: 74px;
|
||
width: 227px;
|
||
height: 50px;
|
||
}
|
||
|
||
.match-button img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.match-button span {
|
||
position: absolute;
|
||
top: 13px;
|
||
left: 0;
|
||
width: 227px;
|
||
color: #3e1f04;
|
||
font-size: 24px;
|
||
font-weight: 900;
|
||
line-height: 24px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.match-button:active {
|
||
transform: translateY(1px);
|
||
filter: brightness(0.96);
|
||
}
|
||
|
||
.victory-button {
|
||
position: absolute;
|
||
}
|
||
|
||
.victory-button img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.victory-button:active {
|
||
filter: brightness(1.12);
|
||
transform: translateY(1px);
|
||
}
|
||
|
||
.fee-note {
|
||
position: absolute;
|
||
top: 752px;
|
||
left: 62px;
|
||
width: 252px;
|
||
height: 12px;
|
||
overflow: hidden;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
font-size: 12px;
|
||
line-height: 12px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.waiting-only {
|
||
opacity: 1;
|
||
transition:
|
||
opacity 260ms ease,
|
||
visibility 0s linear 0s;
|
||
will-change: opacity;
|
||
}
|
||
|
||
.dice-frame.is-matching .waiting-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 260ms ease 560ms,
|
||
visibility 0s linear 830ms;
|
||
}
|
||
|
||
.dice-frame.is-searching .waiting-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 240ms ease,
|
||
visibility 0s linear 260ms;
|
||
}
|
||
|
||
.lobby-only {
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 0s,
|
||
transform 820ms ease-in-out;
|
||
will-change: opacity, transform;
|
||
}
|
||
|
||
.dice-frame.is-searching .lobby-only,
|
||
.dice-frame.is-matching .lobby-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 0s,
|
||
transform 820ms ease-in-out;
|
||
}
|
||
|
||
.dice-frame.is-searching .lobby-avatar-glow,
|
||
.dice-frame.is-matching .lobby-avatar-glow,
|
||
.dice-frame.is-searching .lobby-player-card,
|
||
.dice-frame.is-matching .lobby-player-card,
|
||
.dice-frame.is-searching .avatar-hole-mask-lobby,
|
||
.dice-frame.is-matching .avatar-hole-mask-lobby,
|
||
.dice-frame.is-searching .dice-avatar-lobby,
|
||
.dice-frame.is-matching .dice-avatar-lobby,
|
||
.dice-frame.is-searching .dice-avatar-frame-lobby,
|
||
.dice-frame.is-matching .dice-avatar-frame-lobby,
|
||
.dice-frame.is-searching .lobby-player-name,
|
||
.dice-frame.is-matching .lobby-player-name {
|
||
transform: translateX(-110px);
|
||
}
|
||
|
||
.matching-card-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 180ms;
|
||
}
|
||
|
||
.dice-frame.is-searching .matching-card-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
transition:
|
||
opacity 220ms ease 760ms,
|
||
visibility 0s linear 760ms;
|
||
}
|
||
|
||
.search-indicator img {
|
||
transform-origin: 50% 50%;
|
||
}
|
||
|
||
.search-indicator img {
|
||
--search-orbit-radius: 5px;
|
||
}
|
||
|
||
.dice-frame.is-searching .search-indicator img {
|
||
animation: search-orbit 1100ms linear infinite;
|
||
}
|
||
|
||
.searching-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 180ms;
|
||
}
|
||
|
||
.dice-frame.is-searching .searching-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
transition:
|
||
opacity 220ms ease 820ms,
|
||
visibility 0s linear 820ms;
|
||
}
|
||
|
||
.center-search {
|
||
position: absolute;
|
||
top: 157px;
|
||
left: 122px;
|
||
z-index: 8;
|
||
width: 132px;
|
||
height: 132px;
|
||
filter: drop-shadow(0 0 12px rgba(77, 224, 255, 0.72));
|
||
}
|
||
|
||
.center-search-bg,
|
||
.center-search-icon {
|
||
position: absolute;
|
||
display: block;
|
||
pointer-events: none;
|
||
user-select: none;
|
||
}
|
||
|
||
.center-search-bg {
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
transform-origin: 50% 50%;
|
||
will-change: transform;
|
||
}
|
||
|
||
.dice-frame.is-searching .center-search-bg {
|
||
animation: matching-center-spin 3000ms linear infinite;
|
||
}
|
||
|
||
.center-search-icon {
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 44px;
|
||
height: 44px;
|
||
filter: drop-shadow(0 0 8px rgba(126, 220, 255, 0.88));
|
||
transform: translate(-50%, -50%);
|
||
}
|
||
|
||
.matching-action {
|
||
position: absolute;
|
||
top: 676px;
|
||
left: 74px;
|
||
width: 264px;
|
||
height: 24px;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
transition:
|
||
opacity 260ms ease 180ms,
|
||
visibility 0s linear 440ms;
|
||
will-change: opacity;
|
||
}
|
||
|
||
.dice-frame.is-searching .matching-action {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
transition:
|
||
opacity 260ms ease 860ms,
|
||
visibility 0s linear 860ms;
|
||
}
|
||
|
||
.matching-action img {
|
||
position: absolute;
|
||
top: 7px;
|
||
width: 36px;
|
||
height: 7px;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 180ms ease 260ms,
|
||
transform 260ms ease 240ms;
|
||
}
|
||
|
||
.matching-action-left {
|
||
left: 0;
|
||
transform: translateX(18px);
|
||
}
|
||
|
||
.matching-action-right {
|
||
left: 228px;
|
||
transform: translateX(-18px);
|
||
}
|
||
|
||
.dice-frame.is-searching .matching-action img {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
transition:
|
||
opacity 180ms ease 940ms,
|
||
transform 260ms ease 920ms;
|
||
}
|
||
|
||
.matching-action-text {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 44px;
|
||
width: 140px;
|
||
opacity: 0;
|
||
color: transparent;
|
||
font-size: 24px;
|
||
font-weight: 900;
|
||
line-height: 24px;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
background: linear-gradient(180deg, #fef8ca 27.76%, #ffb36c);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
text-shadow: 0 0 3px #bc0003;
|
||
transition: opacity 220ms ease 260ms;
|
||
}
|
||
|
||
.dice-frame.is-searching .matching-action-text {
|
||
opacity: 1;
|
||
transition: opacity 220ms ease 920ms;
|
||
}
|
||
|
||
.dice-phase-text {
|
||
position: absolute;
|
||
top: 318px;
|
||
left: 52px;
|
||
z-index: 12;
|
||
width: 271px;
|
||
min-height: 42px;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
color: #fff1a8;
|
||
font-size: 36px;
|
||
font-weight: 900;
|
||
line-height: 42px;
|
||
text-align: center;
|
||
text-shadow:
|
||
0 2px 0 rgba(96, 16, 0, 0.78),
|
||
0 0 14px rgba(255, 88, 0, 0.82);
|
||
transition: opacity 180ms ease;
|
||
}
|
||
|
||
.dice-frame.is-searching .dice-phase-text,
|
||
.dice-frame.is-matching .dice-phase-text {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
|
||
.dice-frame.is-countdown .dice-phase-text {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
}
|
||
|
||
.countdown-layer {
|
||
position: absolute;
|
||
top: 268px;
|
||
left: 0;
|
||
z-index: 18;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 375px;
|
||
height: 158px;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 120ms ease;
|
||
}
|
||
|
||
.dice-frame.is-countdown .countdown-layer {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
|
||
.you-win-smash-layer,
|
||
.you-win-impact {
|
||
position: absolute;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.you-win-smash-layer {
|
||
inset: 0;
|
||
z-index: 23;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.you-win-smash-layer::before {
|
||
position: absolute;
|
||
inset: 0;
|
||
content: '';
|
||
background:
|
||
radial-gradient(
|
||
circle at 50% 39%,
|
||
rgba(255, 248, 179, 0.72) 0%,
|
||
rgba(255, 126, 20, 0.3) 18%,
|
||
rgba(255, 73, 0, 0.08) 35%,
|
||
rgba(255, 73, 0, 0) 58%
|
||
),
|
||
rgba(255, 255, 255, 0.2);
|
||
opacity: 0;
|
||
}
|
||
|
||
.you-win-smash-floor {
|
||
position: absolute;
|
||
top: 362px;
|
||
left: 50%;
|
||
width: 312px;
|
||
height: 92px;
|
||
border-radius: 50%;
|
||
background: radial-gradient(
|
||
ellipse at center,
|
||
rgba(255, 215, 75, 0.68) 0%,
|
||
rgba(255, 103, 15, 0.34) 30%,
|
||
rgba(120, 16, 0, 0.28) 54%,
|
||
rgba(0, 0, 0, 0) 76%
|
||
);
|
||
opacity: 0;
|
||
filter: blur(1px) drop-shadow(0 0 18px rgba(255, 118, 22, 0.76));
|
||
transform: translateX(-50%) scale(0.38, 0.2);
|
||
transform-origin: 50% 50%;
|
||
}
|
||
|
||
.dice-frame.is-self-win-compare .you-win-smash-layer {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
|
||
.dice-frame.is-self-win-compare .you-win-smash-layer::before {
|
||
animation: you-win-screen-flash 760ms ease-out 430ms both;
|
||
}
|
||
|
||
.dice-frame.is-self-win-compare .you-win-smash-floor {
|
||
animation: you-win-floor-impact 900ms ease-out 430ms both;
|
||
}
|
||
|
||
.you-win-impact {
|
||
top: 300px;
|
||
left: 0;
|
||
z-index: 25;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 375px;
|
||
height: 92px;
|
||
color: transparent;
|
||
font-size: 56px;
|
||
font-weight: 1000;
|
||
line-height: 62px;
|
||
text-align: center;
|
||
background: linear-gradient(
|
||
180deg,
|
||
#ffffff 0%,
|
||
#fff5b6 19%,
|
||
#ffcf44 48%,
|
||
#ff7a00 74%,
|
||
#9b2d00 100%
|
||
);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-stroke: 2px #7d2700;
|
||
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
|
||
drop-shadow(0 0 22px rgba(255, 205, 46, 0.9))
|
||
drop-shadow(0 0 36px rgba(255, 77, 0, 0.78));
|
||
transform-origin: 50% 78%;
|
||
will-change: opacity, transform, filter;
|
||
}
|
||
|
||
.dice-frame.is-self-win-compare .you-win-impact {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
animation: you-win-drop-impact 980ms
|
||
cubic-bezier(0.13, 0.92, 0.18, 1) both;
|
||
}
|
||
|
||
.countdown-number {
|
||
min-width: 132px;
|
||
color: transparent;
|
||
font-size: 132px;
|
||
font-weight: 1000;
|
||
line-height: 132px;
|
||
text-align: center;
|
||
background: linear-gradient(
|
||
180deg,
|
||
#ffffff 0%,
|
||
#fff5b6 17%,
|
||
#ffcf44 43%,
|
||
#ff7a00 71%,
|
||
#9b2d00 100%
|
||
);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-stroke: 2px #7d2700;
|
||
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
|
||
drop-shadow(0 0 18px rgba(255, 205, 46, 0.92))
|
||
drop-shadow(0 0 30px rgba(255, 77, 0, 0.72));
|
||
opacity: 0;
|
||
transform: scale(0.72);
|
||
}
|
||
|
||
.countdown-number.is-ticking {
|
||
animation: countdown-number-pop 940ms ease-out both;
|
||
}
|
||
|
||
.dice-score {
|
||
position: absolute;
|
||
top: 379px;
|
||
z-index: 12;
|
||
width: 132px;
|
||
height: 132px;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
pointer-events: none;
|
||
transition: opacity 180ms ease;
|
||
}
|
||
|
||
.dice-score-self {
|
||
left: 8px;
|
||
}
|
||
|
||
.dice-score-opponent {
|
||
left: 230px;
|
||
}
|
||
|
||
.dice-frame.has-scores .dice-score {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
|
||
.dice-score img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
user-select: none;
|
||
}
|
||
|
||
.dice-roll-effect {
|
||
position: absolute;
|
||
z-index: 11;
|
||
width: 132px;
|
||
height: 132px;
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 120ms ease;
|
||
}
|
||
|
||
.dice-roll-effect canvas,
|
||
.dice-roll-effect video {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.dice-roll-effect-self {
|
||
left: 8px;
|
||
top: 379px;
|
||
}
|
||
|
||
.dice-roll-effect-opponent {
|
||
left: 230px;
|
||
top: 379px;
|
||
}
|
||
|
||
.dice-roll-effect.is-playing,
|
||
.dice-roll-effect.is-fallback {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
}
|
||
|
||
.dice-roll-effect.is-fallback::before {
|
||
position: absolute;
|
||
inset: 28px;
|
||
content: '';
|
||
border-radius: 50%;
|
||
background: radial-gradient(
|
||
circle at 45% 38%,
|
||
rgba(255, 255, 255, 0.95) 0%,
|
||
rgba(255, 230, 124, 0.72) 32%,
|
||
rgba(255, 108, 26, 0.38) 58%,
|
||
rgba(255, 108, 26, 0) 78%
|
||
);
|
||
filter: blur(1px) drop-shadow(0 0 14px rgba(255, 196, 69, 0.82));
|
||
animation: dice-roll-fallback 520ms linear infinite;
|
||
}
|
||
|
||
.battle-only {
|
||
opacity: 1;
|
||
transition:
|
||
opacity 240ms ease,
|
||
visibility 0s linear 0s;
|
||
}
|
||
|
||
.match-battle-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 180ms;
|
||
}
|
||
|
||
.dice-frame.is-matching .match-battle-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition:
|
||
opacity 220ms ease,
|
||
visibility 0s linear 0s;
|
||
}
|
||
|
||
.player-copy-only {
|
||
display: none;
|
||
}
|
||
|
||
.opponent-battle-only,
|
||
.versus-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 180ms ease,
|
||
visibility 0s linear 180ms;
|
||
}
|
||
|
||
.dice-frame.is-searching .opponent-battle-only,
|
||
.dice-frame.is-matching .opponent-battle-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition:
|
||
opacity 220ms ease 760ms,
|
||
visibility 0s linear 760ms;
|
||
}
|
||
|
||
.opponent-name {
|
||
display: none;
|
||
}
|
||
|
||
.dice-frame.is-matching .opponent-name {
|
||
display: block;
|
||
}
|
||
|
||
.dice-frame.has-opponent .opponent-default-avatar {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.dice-frame.is-matching .versus-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
transition:
|
||
opacity 80ms ease,
|
||
visibility 0s linear 0s;
|
||
}
|
||
|
||
.dice-frame .dice-avatar.is-empty,
|
||
.dice-frame .dice-avatar-frame.is-empty,
|
||
.dice-frame.is-searching .dice-avatar.is-empty,
|
||
.dice-frame.is-searching .dice-avatar-frame.is-empty,
|
||
.dice-frame.is-matching .dice-avatar.is-empty,
|
||
.dice-frame.is-matching .dice-avatar-frame.is-empty {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.dice-frame.is-victory .battle-only,
|
||
.dice-frame.is-victory .waiting-only,
|
||
.dice-frame.is-victory .matching-card-only,
|
||
.dice-frame.is-victory .matching-action,
|
||
.dice-frame.is-victory .searching-only,
|
||
.dice-frame.is-victory .opponent-battle-only,
|
||
.dice-frame.is-victory .dice-roll-effect,
|
||
.dice-frame.is-victory .versus-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition:
|
||
opacity 240ms ease,
|
||
visibility 0s linear 260ms;
|
||
}
|
||
|
||
.victory-only {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
transform: translateY(18px) scale(0.98);
|
||
transition:
|
||
opacity 320ms ease 120ms,
|
||
transform 420ms cubic-bezier(0.16, 1, 0.3, 1) 120ms,
|
||
visibility 0s linear 440ms;
|
||
will-change: opacity, transform;
|
||
}
|
||
|
||
.dice-frame.is-victory .victory-only {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
transition:
|
||
opacity 320ms ease 120ms,
|
||
transform 420ms cubic-bezier(0.16, 1, 0.3, 1) 120ms,
|
||
visibility 0s linear 120ms;
|
||
}
|
||
|
||
.winner-avatar-mask,
|
||
.winner-avatar {
|
||
position: absolute;
|
||
top: 268px;
|
||
left: 134px;
|
||
width: 108px;
|
||
height: 108px;
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.winner-avatar-mask {
|
||
z-index: 3;
|
||
background: radial-gradient(
|
||
circle at 50% 44%,
|
||
rgba(27, 19, 36, 0.98) 0%,
|
||
rgba(13, 10, 22, 0.98) 68%,
|
||
rgba(6, 5, 15, 0.98) 100%
|
||
);
|
||
box-shadow: inset 0 0 24px rgba(0, 0, 0, 0.72);
|
||
}
|
||
|
||
.winner-avatar {
|
||
z-index: 4;
|
||
object-fit: cover;
|
||
user-select: none;
|
||
}
|
||
|
||
.winner-avatar.is-empty {
|
||
opacity: 0;
|
||
}
|
||
|
||
.victory-name {
|
||
position: absolute;
|
||
top: 392px;
|
||
left: 121px;
|
||
width: 133px;
|
||
height: 52px;
|
||
overflow: hidden;
|
||
color: #ffffff;
|
||
font-size: 24px;
|
||
line-height: 26px;
|
||
text-align: center;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.victory-panel-label,
|
||
.victory-panel-amount,
|
||
.victory-panel-currency {
|
||
position: absolute;
|
||
left: 201px;
|
||
width: 65px;
|
||
color: #f7cf58;
|
||
font-weight: 700;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.victory-panel-label {
|
||
top: 540px;
|
||
height: 20px;
|
||
font-size: 18px;
|
||
line-height: 20px;
|
||
}
|
||
|
||
.victory-panel-amount {
|
||
top: 566px;
|
||
height: 24px;
|
||
font-size: 24px;
|
||
line-height: 24px;
|
||
}
|
||
|
||
.victory-panel-currency {
|
||
top: 597px;
|
||
height: 20px;
|
||
font-size: 18px;
|
||
line-height: 20px;
|
||
text-transform: uppercase;
|
||
background: linear-gradient(180deg, #f9d76f 0%, #f1bb3c 95%);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.waiting-only,
|
||
.lobby-only,
|
||
.searching-only,
|
||
.matching-action,
|
||
.matching-action img,
|
||
.matching-action-text,
|
||
.battle-only,
|
||
.match-battle-only,
|
||
.opponent-battle-only,
|
||
.dice-roll-effect,
|
||
.coin-credit-flyer,
|
||
.versus-only,
|
||
.victory-only {
|
||
transition: none;
|
||
}
|
||
|
||
.dice-frame.is-matching .vs-badge,
|
||
.dice-frame.is-matching .vs-badge::after,
|
||
.dice-frame.is-self-win-compare,
|
||
.dice-frame.is-self-win-compare .you-win-smash-layer::before,
|
||
.dice-frame.is-self-win-compare .you-win-smash-floor,
|
||
.dice-frame.is-self-win-compare .you-win-impact,
|
||
.dice-frame.is-matching,
|
||
.dice-frame.is-searching .center-search-bg,
|
||
.dice-frame.is-searching .search-indicator img,
|
||
.dice-roll-effect.is-fallback::before,
|
||
.coin-credit-flyer,
|
||
.top-coin.is-crediting {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@keyframes screen-impact-shake {
|
||
0%,
|
||
100% {
|
||
transform: translate3d(0, 0, 0) scale(var(--dice-scale));
|
||
}
|
||
12% {
|
||
transform: translate3d(0, 9px, 0) scale(var(--dice-scale));
|
||
}
|
||
24% {
|
||
transform: translate3d(-7px, -5px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
36% {
|
||
transform: translate3d(6px, 4px, 0) scale(var(--dice-scale));
|
||
}
|
||
50% {
|
||
transform: translate3d(-4px, 2px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
66% {
|
||
transform: translate3d(3px, -2px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
82% {
|
||
transform: translate3d(-1px, 1px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
}
|
||
|
||
@keyframes vs-drop-impact {
|
||
0% {
|
||
opacity: 0;
|
||
filter: brightness(1.9) drop-shadow(0 0 34px #ff79ff);
|
||
transform: translateY(-640px) scale(4.8) rotate(-12deg);
|
||
}
|
||
34% {
|
||
opacity: 1;
|
||
filter: brightness(1.75) drop-shadow(0 0 32px #4cc9ff);
|
||
transform: translateY(-120px) scale(2.35) rotate(5deg);
|
||
}
|
||
58% {
|
||
opacity: 1;
|
||
filter: brightness(1.62) drop-shadow(0 0 28px #ffd557);
|
||
transform: translateY(22px) scale(0.78) rotate(2deg);
|
||
}
|
||
72% {
|
||
filter: brightness(1.48) drop-shadow(0 0 24px #ff79ff);
|
||
transform: translateY(-12px) scale(1.18) rotate(-1deg);
|
||
}
|
||
86% {
|
||
filter: brightness(1.18) drop-shadow(0 0 14px #4cc9ff);
|
||
transform: translateY(4px) scale(0.96) rotate(0.5deg);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
filter: brightness(1);
|
||
transform: translateY(0) scale(1) rotate(0);
|
||
}
|
||
}
|
||
|
||
@keyframes vs-impact-ring {
|
||
0% {
|
||
opacity: 0.95;
|
||
transform: scale(0.36);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(1.92);
|
||
}
|
||
}
|
||
|
||
@keyframes you-win-drop-impact {
|
||
0% {
|
||
opacity: 0;
|
||
filter: brightness(1.9) drop-shadow(0 0 36px #ff79ff);
|
||
transform: translateY(-650px) scale(3.9) rotate(-7deg);
|
||
}
|
||
34% {
|
||
opacity: 1;
|
||
filter: brightness(1.75) drop-shadow(0 0 32px #4cc9ff);
|
||
transform: translateY(-122px) scale(2.05) rotate(4deg);
|
||
}
|
||
58% {
|
||
opacity: 1;
|
||
filter: brightness(1.55) drop-shadow(0 0 30px #ffd557);
|
||
transform: translateY(28px) scale(0.78) rotate(1deg);
|
||
}
|
||
72% {
|
||
filter: brightness(1.42) drop-shadow(0 0 24px #ff79ff);
|
||
transform: translateY(-16px) scale(1.16) rotate(-0.8deg);
|
||
}
|
||
86% {
|
||
filter: brightness(1.18) drop-shadow(0 0 16px #4cc9ff);
|
||
transform: translateY(4px) scale(0.96) rotate(0.3deg);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
|
||
drop-shadow(0 0 22px rgba(255, 205, 46, 0.9))
|
||
drop-shadow(0 0 36px rgba(255, 77, 0, 0.78));
|
||
transform: translateY(0) scale(1) rotate(0);
|
||
}
|
||
}
|
||
|
||
@keyframes you-win-screen-smash {
|
||
0%,
|
||
100% {
|
||
transform: translate3d(0, 0, 0) scale(var(--dice-scale));
|
||
filter: none;
|
||
}
|
||
12% {
|
||
transform: translate3d(0, 14px, 0) scale(var(--dice-scale));
|
||
filter: brightness(1.22) contrast(1.08);
|
||
}
|
||
24% {
|
||
transform: translate3d(-12px, -8px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
36% {
|
||
transform: translate3d(10px, 7px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
50% {
|
||
transform: translate3d(-7px, 4px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
66% {
|
||
transform: translate3d(5px, -3px, 0)
|
||
scale(var(--dice-scale));
|
||
filter: brightness(1.08) contrast(1.03);
|
||
}
|
||
82% {
|
||
transform: translate3d(-2px, 1px, 0)
|
||
scale(var(--dice-scale));
|
||
}
|
||
}
|
||
|
||
@keyframes you-win-screen-flash {
|
||
0% {
|
||
opacity: 0;
|
||
}
|
||
8% {
|
||
opacity: 1;
|
||
}
|
||
22% {
|
||
opacity: 0.46;
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes you-win-floor-impact {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateX(-50%) scale(0.38, 0.2);
|
||
}
|
||
16% {
|
||
opacity: 1;
|
||
transform: translateX(-50%) scale(1.05, 0.52);
|
||
}
|
||
58% {
|
||
opacity: 0.62;
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateX(-50%) scale(1.32, 0.72);
|
||
}
|
||
}
|
||
|
||
@keyframes search-orbit {
|
||
0% {
|
||
transform: rotate(0deg)
|
||
translateX(var(--search-orbit-radius, 12px))
|
||
rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg)
|
||
translateX(var(--search-orbit-radius, 12px))
|
||
rotate(-360deg);
|
||
}
|
||
}
|
||
|
||
@keyframes matching-center-spin {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
@keyframes dice-roll-fallback {
|
||
0% {
|
||
transform: scale(0.88) rotate(0deg);
|
||
opacity: 0.78;
|
||
}
|
||
50% {
|
||
transform: scale(1.08) rotate(180deg);
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
transform: scale(0.88) rotate(360deg);
|
||
opacity: 0.78;
|
||
}
|
||
}
|
||
|
||
@keyframes coin-credit-fly {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translate3d(0, 0, 0) scale(0.78) rotate(0deg);
|
||
}
|
||
12% {
|
||
opacity: 1;
|
||
}
|
||
58% {
|
||
opacity: 1;
|
||
transform: translate3d(
|
||
calc(var(--coin-credit-dx) * 0.72),
|
||
calc(var(--coin-credit-dy) * 0.58 - 34px),
|
||
0
|
||
)
|
||
scale(1.05) rotate(220deg);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate3d(
|
||
var(--coin-credit-dx),
|
||
var(--coin-credit-dy),
|
||
0
|
||
)
|
||
scale(0.36) rotate(520deg);
|
||
}
|
||
}
|
||
|
||
@keyframes top-coin-credit-pulse {
|
||
0% {
|
||
transform: scale(1);
|
||
filter: drop-shadow(0 0 0 rgba(255, 211, 79, 0));
|
||
}
|
||
42% {
|
||
transform: scale(1.12);
|
||
filter: drop-shadow(0 0 13px rgba(255, 211, 79, 0.86));
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
filter: drop-shadow(0 0 0 rgba(255, 211, 79, 0));
|
||
}
|
||
}
|
||
|
||
@keyframes countdown-number-pop {
|
||
0% {
|
||
opacity: 0;
|
||
transform: scale(0.32);
|
||
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
|
||
drop-shadow(0 0 8px rgba(255, 205, 46, 0.55));
|
||
}
|
||
|
||
24% {
|
||
opacity: 1;
|
||
transform: scale(1.18);
|
||
filter: drop-shadow(0 5px 0 rgba(94, 17, 0, 0.9))
|
||
drop-shadow(0 0 24px rgba(255, 229, 86, 1))
|
||
drop-shadow(0 0 42px rgba(255, 77, 0, 0.86));
|
||
}
|
||
|
||
70% {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(0.86);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div
|
||
class="dice-stage"
|
||
role="application"
|
||
aria-label="Dice match lobby"
|
||
data-i18n-aria="diceMatch.pageLabel"
|
||
>
|
||
<main
|
||
class="dice-frame is-preloading"
|
||
data-node-id="466:61"
|
||
data-name="dice"
|
||
>
|
||
<div
|
||
class="loading-screen"
|
||
data-role="loading-screen"
|
||
aria-label="Loading"
|
||
role="status"
|
||
>
|
||
<img
|
||
class="loading-bg"
|
||
src="assets/figma/loading-bg.jpg"
|
||
alt=""
|
||
/>
|
||
<div
|
||
class="loading-progress"
|
||
data-role="loading-progress"
|
||
role="progressbar"
|
||
aria-valuemin="0"
|
||
aria-valuemax="100"
|
||
aria-valuenow="0"
|
||
>
|
||
<span
|
||
class="loading-progress-bar"
|
||
data-role="loading-progress-bar"
|
||
></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="layer"
|
||
style="left: -47px; top: 0; width: 470px; height: 834px"
|
||
data-node-id="466:64"
|
||
data-name="arena background"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/bg-arena.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div class="game-content">
|
||
<div class="top-bar" data-name="top bar">
|
||
<button
|
||
class="top-back"
|
||
type="button"
|
||
aria-label="Back"
|
||
data-node-id="466:68"
|
||
data-action="back"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/back-button.png"
|
||
alt=""
|
||
/>
|
||
</button>
|
||
|
||
<div
|
||
class="top-game-icon"
|
||
data-node-id="466:87"
|
||
data-name="game icon"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/game-icon.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
class="top-coin"
|
||
type="button"
|
||
data-name="coin balance"
|
||
data-action="recharge"
|
||
aria-label="Recharge"
|
||
>
|
||
<img src="assets/figma/coin.png" alt="" />
|
||
<span>8,000</span>
|
||
<strong class="top-coin-plus" aria-hidden="true"
|
||
>+</strong
|
||
>
|
||
</button>
|
||
|
||
<div class="language-switcher">
|
||
<button
|
||
class="language-button"
|
||
type="button"
|
||
data-language-toggle
|
||
data-i18n-aria="diceMatch.changeLanguage"
|
||
aria-label="Change language"
|
||
aria-expanded="false"
|
||
>
|
||
<span data-current-lang>EN</span>
|
||
</button>
|
||
<div
|
||
class="language-menu"
|
||
data-language-menu
|
||
hidden
|
||
>
|
||
<button type="button" data-lang-option="en">
|
||
EN
|
||
</button>
|
||
<button type="button" data-lang-option="ar">
|
||
AR
|
||
</button>
|
||
<button type="button" data-lang-option="es">
|
||
ES
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="layer waiting-only lobby-only lobby-avatar-glow"
|
||
data-name="lobby avatar glow"
|
||
>
|
||
<img
|
||
class="layer-img fill"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer waiting-only lobby-only lobby-player-card"
|
||
data-name="lobby player card"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/player-card.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="avatar-hole-mask avatar-hole-mask-lobby waiting-only lobby-only"
|
||
data-name="lobby avatar empty mask"
|
||
></div>
|
||
<img
|
||
class="dice-avatar dice-avatar-lobby waiting-only lobby-only is-empty"
|
||
data-role="self-avatar"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<img
|
||
class="dice-avatar-frame dice-avatar-frame-lobby waiting-only lobby-only is-empty"
|
||
data-role="self-avatar-frame"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<div
|
||
class="lobby-player-name waiting-only lobby-only"
|
||
data-name="lobby player name"
|
||
>
|
||
<div>NameName</div>
|
||
<div>Name...</div>
|
||
</div>
|
||
|
||
<div
|
||
class="layer battle-only match-battle-only player-copy-only"
|
||
style="
|
||
left: 41px;
|
||
top: 163px;
|
||
width: 73px;
|
||
height: 73px;
|
||
"
|
||
data-node-id="466:89"
|
||
>
|
||
<img
|
||
class="layer-img fill"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer battle-only match-battle-only player-copy-only"
|
||
style="
|
||
left: 21px;
|
||
top: 146px;
|
||
width: 113px;
|
||
height: 158px;
|
||
"
|
||
data-node-id="466:90"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/player-card.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="avatar-hole-mask avatar-hole-mask-self battle-only match-battle-only"
|
||
data-name="self avatar empty mask"
|
||
></div>
|
||
<img
|
||
class="dice-avatar dice-avatar-self battle-only match-battle-only is-empty"
|
||
data-role="self-battle-avatar"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<img
|
||
class="dice-avatar-frame dice-avatar-frame-self battle-only match-battle-only is-empty"
|
||
data-role="self-battle-avatar-frame"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<div
|
||
class="player-name battle-only match-battle-only player-copy-only"
|
||
data-node-id="466:91"
|
||
>
|
||
<div>NameName</div>
|
||
<div>Name...</div>
|
||
</div>
|
||
|
||
<div
|
||
class="layer vs-badge battle-only versus-only"
|
||
style="
|
||
left: 120px;
|
||
top: 159px;
|
||
width: 133px;
|
||
height: 128px;
|
||
"
|
||
data-node-id="466:122"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/vs.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
class="center-search searching-only"
|
||
aria-hidden="true"
|
||
>
|
||
<img
|
||
class="center-search-bg"
|
||
src="assets/figma/matching-center-bg.svg"
|
||
alt=""
|
||
/>
|
||
<img
|
||
class="center-search-icon"
|
||
src="assets/figma/search-icon.svg"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: -51px;
|
||
top: 311px;
|
||
width: 258px;
|
||
height: 258px;
|
||
"
|
||
data-node-id="466:124"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/red-ring.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 32px;
|
||
top: 400px;
|
||
width: 84px;
|
||
height: 89px;
|
||
"
|
||
data-node-id="466:125"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/red-dice.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: -44px;
|
||
top: 288px;
|
||
width: 244px;
|
||
height: 236px;
|
||
"
|
||
data-node-id="466:126"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/red-flare.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 170px;
|
||
top: 311px;
|
||
width: 258px;
|
||
height: 258px;
|
||
"
|
||
data-node-id="466:128"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/blue-ring.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 254px;
|
||
top: 400px;
|
||
width: 83px;
|
||
height: 87px;
|
||
"
|
||
data-node-id="466:129"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/blue-dice.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 177px;
|
||
top: 288px;
|
||
width: 244px;
|
||
height: 236px;
|
||
"
|
||
data-node-id="466:130"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/blue-flare.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
class="dice-roll-effect dice-roll-effect-self"
|
||
data-role="self-roll-effect"
|
||
aria-hidden="true"
|
||
></div>
|
||
<div
|
||
class="dice-roll-effect dice-roll-effect-opponent"
|
||
data-role="opponent-roll-effect"
|
||
aria-hidden="true"
|
||
></div>
|
||
|
||
<div
|
||
class="layer battle-only opponent-battle-only"
|
||
style="
|
||
left: 262px;
|
||
top: 163px;
|
||
width: 73px;
|
||
height: 73px;
|
||
"
|
||
data-node-id="466:132"
|
||
>
|
||
<img
|
||
class="layer-img fill"
|
||
src="assets/figma/opponent-avatar-glow.svg"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer battle-only opponent-battle-only opponent-default-avatar"
|
||
style="
|
||
left: 262px;
|
||
top: 163px;
|
||
width: 73px;
|
||
height: 73px;
|
||
"
|
||
data-node-id="466:133"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/opponent-question.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="layer battle-only opponent-battle-only"
|
||
style="
|
||
left: 242px;
|
||
top: 146px;
|
||
width: 113px;
|
||
height: 158px;
|
||
"
|
||
data-node-id="466:134"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/opponent-card.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<img
|
||
class="dice-avatar dice-avatar-opponent battle-only opponent-battle-only is-empty"
|
||
data-role="opponent-avatar"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<img
|
||
class="dice-avatar-frame dice-avatar-frame-opponent battle-only opponent-battle-only is-empty"
|
||
data-role="opponent-avatar-frame"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<div
|
||
class="opponent-name battle-only opponent-battle-only"
|
||
data-role="opponent-name"
|
||
>
|
||
<div>?</div>
|
||
<div></div>
|
||
</div>
|
||
<div
|
||
class="opponent-mark matching-card-only"
|
||
data-node-id="466:135"
|
||
data-i18n="diceMatch.unknownOpponent"
|
||
>
|
||
?
|
||
</div>
|
||
<div
|
||
class="layer matching-card-only search-indicator"
|
||
style="
|
||
left: 262px;
|
||
top: 269px;
|
||
width: 18px;
|
||
height: 18px;
|
||
"
|
||
data-node-id="466:137"
|
||
>
|
||
<img
|
||
class="layer-img fill"
|
||
src="assets/figma/search-icon.svg"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="matching-label matching-card-only"
|
||
data-node-id="466:136"
|
||
data-i18n="diceMatch.matching"
|
||
>
|
||
Matching...
|
||
</div>
|
||
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 13px;
|
||
top: 596px;
|
||
width: 350px;
|
||
height: 78px;
|
||
"
|
||
data-node-id="466:94"
|
||
>
|
||
<img
|
||
class="layer-img fill"
|
||
src="assets/figma/prize-panel.svg"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="winner-label waiting-only"
|
||
data-node-id="466:95"
|
||
data-i18n="diceMatch.winnerReceives"
|
||
>
|
||
The winner receives
|
||
</div>
|
||
<button
|
||
class="stake waiting-only"
|
||
type="button"
|
||
style="left: 21px"
|
||
data-node-id="466:96"
|
||
data-action="stake"
|
||
data-stake="8000"
|
||
aria-label="8,000"
|
||
aria-pressed="false"
|
||
>
|
||
<img
|
||
class="stake-bg"
|
||
src="assets/figma/stake-outline.svg"
|
||
alt=""
|
||
/>
|
||
<span
|
||
class="stake-selected-slice stake-selected-left"
|
||
></span>
|
||
<span
|
||
class="stake-selected-slice stake-selected-right"
|
||
></span>
|
||
<img
|
||
class="stake-coin"
|
||
src="assets/figma/coin.png"
|
||
alt=""
|
||
/>
|
||
<span class="stake-value">8,000</span>
|
||
</button>
|
||
<button
|
||
class="stake waiting-only"
|
||
type="button"
|
||
style="left: 107px"
|
||
data-node-id="466:99"
|
||
data-action="stake"
|
||
data-stake="8000"
|
||
data-selected="true"
|
||
aria-label="8,000"
|
||
aria-pressed="true"
|
||
>
|
||
<img
|
||
class="stake-bg"
|
||
src="assets/figma/stake-fill.png"
|
||
alt=""
|
||
/>
|
||
<span
|
||
class="stake-selected-slice stake-selected-left"
|
||
></span>
|
||
<span
|
||
class="stake-selected-slice stake-selected-right"
|
||
></span>
|
||
<img
|
||
class="stake-coin"
|
||
src="assets/figma/coin.png"
|
||
alt=""
|
||
/>
|
||
<span class="stake-value">8,000</span>
|
||
</button>
|
||
<button
|
||
class="stake waiting-only"
|
||
type="button"
|
||
style="left: 192px"
|
||
data-node-id="466:97"
|
||
data-action="stake"
|
||
data-stake="8000"
|
||
aria-label="8,000"
|
||
aria-pressed="false"
|
||
>
|
||
<img
|
||
class="stake-bg"
|
||
src="assets/figma/stake-outline.svg"
|
||
alt=""
|
||
/>
|
||
<span
|
||
class="stake-selected-slice stake-selected-left"
|
||
></span>
|
||
<span
|
||
class="stake-selected-slice stake-selected-right"
|
||
></span>
|
||
<img
|
||
class="stake-coin"
|
||
src="assets/figma/coin.png"
|
||
alt=""
|
||
/>
|
||
<span class="stake-value">8,000</span>
|
||
</button>
|
||
<button
|
||
class="stake waiting-only"
|
||
type="button"
|
||
style="left: 278px"
|
||
data-node-id="466:98"
|
||
data-action="stake"
|
||
data-stake="8000"
|
||
aria-label="8,000"
|
||
aria-pressed="false"
|
||
>
|
||
<img
|
||
class="stake-bg"
|
||
src="assets/figma/stake-outline.svg"
|
||
alt=""
|
||
/>
|
||
<span
|
||
class="stake-selected-slice stake-selected-left"
|
||
></span>
|
||
<span
|
||
class="stake-selected-slice stake-selected-right"
|
||
></span>
|
||
<img
|
||
class="stake-coin"
|
||
src="assets/figma/coin.png"
|
||
alt=""
|
||
/>
|
||
<span class="stake-value">8,000</span>
|
||
</button>
|
||
<div
|
||
class="layer waiting-only"
|
||
style="
|
||
left: 335px;
|
||
top: 602px;
|
||
width: 20px;
|
||
height: 20px;
|
||
"
|
||
data-node-id="466:118"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/badge-help.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<button
|
||
class="match-button waiting-only"
|
||
type="button"
|
||
data-node-id="466:119"
|
||
data-action="match"
|
||
aria-label="Match"
|
||
data-i18n-aria="diceMatch.match"
|
||
>
|
||
<img src="assets/figma/match-button.png" alt="" />
|
||
<span data-node-id="466:121" data-i18n="diceMatch.match"
|
||
>Match</span
|
||
>
|
||
</button>
|
||
|
||
<div
|
||
class="fee-note waiting-only"
|
||
data-node-id="466:92"
|
||
data-i18n="diceMatch.feeNote"
|
||
>
|
||
The winning player pays a 5% transaction fee
|
||
</div>
|
||
|
||
<div
|
||
class="matching-action"
|
||
data-node-id="466:255"
|
||
aria-live="polite"
|
||
>
|
||
<img
|
||
class="matching-action-left"
|
||
src="assets/figma/matching-left.svg"
|
||
alt=""
|
||
/>
|
||
<span
|
||
class="matching-action-text"
|
||
data-node-id="466:262"
|
||
data-i18n="diceMatch.matching"
|
||
>Matching...</span
|
||
>
|
||
<img
|
||
class="matching-action-right"
|
||
src="assets/figma/matching-right.svg"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="dice-phase-text"
|
||
data-role="phase-text"
|
||
aria-live="polite"
|
||
></div>
|
||
<div
|
||
class="you-win-smash-layer"
|
||
data-role="you-win-smash"
|
||
aria-hidden="true"
|
||
>
|
||
<span class="you-win-smash-floor"></span>
|
||
</div>
|
||
<div
|
||
class="you-win-impact"
|
||
data-role="you-win-impact"
|
||
aria-hidden="true"
|
||
>
|
||
YOU WIN
|
||
</div>
|
||
<div
|
||
class="countdown-layer"
|
||
data-role="countdown-layer"
|
||
aria-live="polite"
|
||
aria-hidden="true"
|
||
>
|
||
<div
|
||
class="countdown-number"
|
||
data-role="countdown-number"
|
||
></div>
|
||
</div>
|
||
<div
|
||
class="dice-score dice-score-self"
|
||
data-role="self-score"
|
||
></div>
|
||
<div
|
||
class="dice-score dice-score-opponent"
|
||
data-role="opponent-score"
|
||
></div>
|
||
|
||
<div
|
||
class="layer victory-only"
|
||
style="
|
||
left: 29px;
|
||
top: 90px;
|
||
width: 316px;
|
||
height: 124px;
|
||
"
|
||
data-node-id="466:297"
|
||
data-name="victory light"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/win-victory-light.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="winner-avatar-mask victory-only"
|
||
data-name="winner avatar empty mask"
|
||
></div>
|
||
<img
|
||
class="winner-avatar victory-only is-empty"
|
||
data-role="winner-avatar"
|
||
src="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E"
|
||
alt=""
|
||
/>
|
||
<div
|
||
class="layer victory-only"
|
||
style="
|
||
left: 104px;
|
||
top: 243px;
|
||
width: 167px;
|
||
height: 233px;
|
||
"
|
||
data-node-id="466:293"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
data-role="winner-card"
|
||
src="assets/figma/win-player-card.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="victory-name victory-only"
|
||
data-node-id="466:294"
|
||
>
|
||
<div>NameName</div>
|
||
<div>Name...</div>
|
||
</div>
|
||
<div
|
||
class="layer victory-only"
|
||
style="
|
||
left: 38px;
|
||
top: 528px;
|
||
width: 300px;
|
||
height: 103px;
|
||
"
|
||
data-node-id="466:299"
|
||
>
|
||
<img
|
||
class="layer-img cover"
|
||
src="assets/figma/win-reward-panel.png"
|
||
alt=""
|
||
/>
|
||
</div>
|
||
<div
|
||
class="victory-panel-label victory-only"
|
||
data-node-id="466:301"
|
||
data-i18n="diceMatch.win"
|
||
>
|
||
Win
|
||
</div>
|
||
<div
|
||
class="victory-panel-amount victory-only"
|
||
data-node-id="466:300"
|
||
>
|
||
1,900
|
||
</div>
|
||
<div
|
||
class="victory-panel-currency victory-only"
|
||
data-node-id="466:302"
|
||
data-i18n="diceMatch.coins"
|
||
>
|
||
Coins
|
||
</div>
|
||
<button
|
||
class="victory-button victory-only"
|
||
type="button"
|
||
style="
|
||
left: 12px;
|
||
top: 692px;
|
||
width: 168px;
|
||
height: 38px;
|
||
"
|
||
data-node-id="466:295"
|
||
data-action="rematch"
|
||
aria-label="Re-match"
|
||
>
|
||
<img src="assets/figma/win-rematch-button.png" alt="" />
|
||
</button>
|
||
<button
|
||
class="victory-button victory-only"
|
||
type="button"
|
||
style="
|
||
left: 193px;
|
||
top: 692px;
|
||
width: 170px;
|
||
height: 38px;
|
||
"
|
||
data-node-id="466:296"
|
||
data-action="play-again"
|
||
aria-label="Play again"
|
||
>
|
||
<img
|
||
src="assets/figma/win-play-again-button.png"
|
||
alt=""
|
||
/>
|
||
</button>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script src="../../common/api.js"></script>
|
||
<script src="../../common/params.js"></script>
|
||
<script src="../../common/jsbridge.js"></script>
|
||
<script>
|
||
window.HyAppI18nSupported = ['en', 'ar', 'es'];
|
||
</script>
|
||
<script src="../../common/i18n.js?v=dice-lang-20260611"></script>
|
||
<script src="../../common/effect-player.js?v=dice-preload-20260611"></script>
|
||
<script src="../../common/game-preloader.js?v=game-preloader-20260611"></script>
|
||
<script>
|
||
(function () {
|
||
var DESIGN_WIDTH = 375;
|
||
var DESIGN_HEIGHT = 812;
|
||
var MAX_STAGE_WIDTH = 768;
|
||
var SETTLEMENT_AUTO_CLOSE_MS = 30000;
|
||
var DICE_ROLL_EFFECT_URL = 'assets/figma/dice-roll.svga';
|
||
var DICE_PRELOAD_ASSET_URLS = [
|
||
'assets/figma/loading-bg.jpg',
|
||
'assets/figma/amount-fill.png',
|
||
'assets/figma/back-button.png',
|
||
'assets/figma/badge-help.png',
|
||
'assets/figma/bg-arena.png',
|
||
'assets/figma/blue-dice.png',
|
||
'assets/figma/blue-flare.png',
|
||
'assets/figma/blue-ring.png',
|
||
'assets/figma/coin.png',
|
||
'assets/figma/dice-blue-1.png',
|
||
'assets/figma/dice-blue-2.png',
|
||
'assets/figma/dice-blue-3.png',
|
||
'assets/figma/dice-blue-4.png',
|
||
'assets/figma/dice-blue-5.png',
|
||
'assets/figma/dice-blue-6.png',
|
||
'assets/figma/dice-red-1.png',
|
||
'assets/figma/dice-red-2.png',
|
||
'assets/figma/dice-red-3.png',
|
||
'assets/figma/dice-red-4.png',
|
||
'assets/figma/dice-red-5.png',
|
||
'assets/figma/dice-red-6.png',
|
||
DICE_ROLL_EFFECT_URL,
|
||
'assets/figma/game-icon.png',
|
||
'assets/figma/home-indicator.svg',
|
||
'assets/figma/mask-bg.svg',
|
||
'assets/figma/match-button.png',
|
||
'assets/figma/matching-center-bg.svg',
|
||
'assets/figma/matching-center.svg',
|
||
'assets/figma/matching-left.svg',
|
||
'assets/figma/matching-right.svg',
|
||
'assets/figma/opponent-avatar-glow.svg',
|
||
'assets/figma/opponent-card.png',
|
||
'assets/figma/opponent-question.png',
|
||
'assets/figma/player-avatar-glow.png',
|
||
'assets/figma/player-card.png',
|
||
'assets/figma/prize-panel.svg',
|
||
'assets/figma/red-dice.png',
|
||
'assets/figma/red-flare.png',
|
||
'assets/figma/red-ring.png',
|
||
'assets/figma/search-icon.svg',
|
||
'assets/figma/stake-fill.png',
|
||
'assets/figma/stake-mask-left.svg',
|
||
'assets/figma/stake-mask-right.svg',
|
||
'assets/figma/stake-outline.svg',
|
||
'assets/figma/status-battery.svg',
|
||
'assets/figma/status-cellular.svg',
|
||
'assets/figma/status-frame.svg',
|
||
'assets/figma/status-wifi.svg',
|
||
'assets/figma/vs.png',
|
||
'assets/figma/win-avatar-glow.png',
|
||
'assets/figma/win-play-again-button.png',
|
||
'assets/figma/win-player-card.png',
|
||
'assets/figma/win-rematch-button.png',
|
||
'assets/figma/win-reward-panel.png',
|
||
'assets/figma/win-victory-light.png',
|
||
'assets/figma/winner-text-fill.png',
|
||
];
|
||
var root = document.documentElement;
|
||
var transparentPixel =
|
||
'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E';
|
||
var frame = document.querySelector('.dice-frame');
|
||
var timers = [];
|
||
var pollTimer = 0;
|
||
var roundSequence = 0;
|
||
var pageStartedAt = performance.now();
|
||
var coinCreditFrame = 0;
|
||
var coinCreditLayer = null;
|
||
var rollEffectPlayers = {
|
||
self: null,
|
||
opponent: null,
|
||
};
|
||
var state = {
|
||
preloading: true,
|
||
profile: {},
|
||
appearance: {},
|
||
balanceCoin: 0,
|
||
config: null,
|
||
selectedStake: 0,
|
||
match: null,
|
||
busy: false,
|
||
roundStarted: false,
|
||
serverTimeMS: 0,
|
||
};
|
||
|
||
function trackSelfGameEvent(name, extra) {
|
||
var api =
|
||
window.HyAppAPI &&
|
||
window.HyAppAPI.game &&
|
||
window.HyAppAPI.game.reportEvent;
|
||
if (typeof api !== 'function') return;
|
||
var payload = Object.assign(
|
||
{
|
||
event_name: name,
|
||
game_id: 'dice',
|
||
page: 'game/touzi',
|
||
h5_version: 'dice-h5-20260611',
|
||
entry_source: currentRoomID() ? 'room' : 'direct',
|
||
success: true,
|
||
},
|
||
extra || {}
|
||
);
|
||
api(payload).catch(function () {});
|
||
}
|
||
|
||
function updateScale() {
|
||
// 游戏区域宽度始终吃满可用宽度,但最多放大到 768px;缩放只跟最终宽度走,避免大屏时被高度压窄后露出右侧空条。
|
||
var viewportWidth = Math.min(
|
||
window.innerWidth,
|
||
MAX_STAGE_WIDTH
|
||
);
|
||
var scale = viewportWidth / DESIGN_WIDTH;
|
||
root.style.setProperty(
|
||
'--dice-scale',
|
||
String(Math.max(scale, 0.1))
|
||
);
|
||
}
|
||
|
||
function postBridge(action, payload) {
|
||
if (window.HyAppBridge && window.HyAppBridge.post) {
|
||
window.HyAppBridge.post(action, payload || {});
|
||
}
|
||
}
|
||
|
||
function openRecharge() {
|
||
var payload = { source: 'dice', game_code: 'touzi' };
|
||
if (
|
||
window.HyAppBridge &&
|
||
typeof window.HyAppBridge.recharge === 'function'
|
||
) {
|
||
window.HyAppBridge.recharge(payload);
|
||
return;
|
||
}
|
||
postBridge('gameRecharge', payload);
|
||
postBridge('recharge', payload);
|
||
}
|
||
|
||
function isInsufficientBalanceError(error) {
|
||
var code = String(
|
||
(error && error.code) || ''
|
||
).toUpperCase();
|
||
var message = String(
|
||
(error && error.message) || ''
|
||
).toLowerCase();
|
||
return (
|
||
code === 'INSUFFICIENT_BALANCE' ||
|
||
message === 'insufficient balance'
|
||
);
|
||
}
|
||
|
||
function handleInsufficientBalance() {
|
||
// 余额不足需要马上让用户补币:先给固定英文提示,再拉起 Flutter 支付页桥接。
|
||
showMessage('insufficient balance');
|
||
openRecharge();
|
||
}
|
||
|
||
function setLoadingProgress(progress) {
|
||
window.HyAppGamePreloader.setProgress({
|
||
root: root,
|
||
progress: progress,
|
||
});
|
||
}
|
||
|
||
function completeLoadingScreen() {
|
||
state.preloading = false;
|
||
setMatchButtonBusy(false, matchText());
|
||
window.HyAppGamePreloader.complete({
|
||
root: root,
|
||
frame: frame,
|
||
loadingScreen: role('loading-screen'),
|
||
progressNode: role('loading-progress'),
|
||
});
|
||
}
|
||
|
||
function collectDicePreloadURLs() {
|
||
return window.HyAppGamePreloader.collectResourceURLs(
|
||
DICE_PRELOAD_ASSET_URLS
|
||
);
|
||
}
|
||
|
||
function preloadResourceOnce(url) {
|
||
return window.HyAppGamePreloader.preloadResource(url, {
|
||
svga: {
|
||
svgaScript: '../../common/vendor/svga.min.js',
|
||
waitForWindowLoad: false,
|
||
},
|
||
});
|
||
}
|
||
|
||
function preloadLobbyDataImages(lobbyData) {
|
||
var urls = [];
|
||
var seen = {};
|
||
var profile = (lobbyData && lobbyData.profile) || {};
|
||
var appearance = (lobbyData && lobbyData.appearance) || {};
|
||
window.HyAppGamePreloader.addURL(
|
||
urls,
|
||
seen,
|
||
avatarURL(profile)
|
||
);
|
||
window.HyAppGamePreloader.addURL(
|
||
urls,
|
||
seen,
|
||
frameURL(appearance.avatar_frame || {})
|
||
);
|
||
if (!urls.length) return Promise.resolve();
|
||
return Promise.all(
|
||
urls.map(function (url) {
|
||
return window.HyAppGamePreloader.preloadImage(url);
|
||
})
|
||
);
|
||
}
|
||
|
||
function runPreloadTasks(tasks) {
|
||
return window.HyAppGamePreloader.runTasks(
|
||
tasks,
|
||
setLoadingProgress
|
||
);
|
||
}
|
||
|
||
function each(nodes, iteratee) {
|
||
Array.prototype.forEach.call(nodes, iteratee);
|
||
}
|
||
|
||
function schedule(callback, ms) {
|
||
var timer = window.setTimeout(function () {
|
||
timers = timers.filter(function (item) {
|
||
return item !== timer;
|
||
});
|
||
callback();
|
||
}, ms);
|
||
timers.push(timer);
|
||
return timer;
|
||
}
|
||
|
||
function delay(ms) {
|
||
return new Promise(function (resolve) {
|
||
schedule(resolve, ms);
|
||
});
|
||
}
|
||
|
||
function clearPollTimer() {
|
||
if (!pollTimer) return;
|
||
window.clearTimeout(pollTimer);
|
||
pollTimer = 0;
|
||
}
|
||
|
||
function clearMatchTimers() {
|
||
clearPollTimer();
|
||
timers.forEach(function (timer) {
|
||
window.clearTimeout(timer);
|
||
});
|
||
timers = [];
|
||
}
|
||
|
||
function api() {
|
||
return window.HyAppAPI || {};
|
||
}
|
||
|
||
function diceAPI() {
|
||
return api().game && api().game.dice;
|
||
}
|
||
|
||
function role(name) {
|
||
return document.querySelector('[data-role="' + name + '"]');
|
||
}
|
||
|
||
function rollEffectNode(side) {
|
||
return role(
|
||
side === 'opponent'
|
||
? 'opponent-roll-effect'
|
||
: 'self-roll-effect'
|
||
);
|
||
}
|
||
|
||
function createRollEffectPlayer(side) {
|
||
var node = rollEffectNode(side);
|
||
if (!node || !window.HyAppEffectPlayer) return null;
|
||
if (!rollEffectPlayers[side]) {
|
||
rollEffectPlayers[side] =
|
||
window.HyAppEffectPlayer.create(node, {
|
||
activeClass: 'is-playing',
|
||
fit: 'contain',
|
||
loop: true,
|
||
svgaScript: '../../common/vendor/svga.min.js',
|
||
waitForWindowLoad: false,
|
||
});
|
||
}
|
||
return rollEffectPlayers[side];
|
||
}
|
||
|
||
function playSingleRollEffect(side) {
|
||
var node = rollEffectNode(side);
|
||
var player = createRollEffectPlayer(side);
|
||
if (!node) return Promise.resolve();
|
||
node.classList.remove('is-fallback');
|
||
if (!player) {
|
||
node.classList.add('is-fallback');
|
||
return Promise.resolve();
|
||
}
|
||
player.stop();
|
||
return player
|
||
.play(DICE_ROLL_EFFECT_URL, {
|
||
type: 'svga',
|
||
waitForWindowLoad: false,
|
||
})
|
||
.catch(function () {
|
||
node.innerHTML = '';
|
||
node.classList.remove('is-playing');
|
||
node.classList.add('is-fallback');
|
||
});
|
||
}
|
||
|
||
function playRollEffects() {
|
||
return Promise.all([
|
||
playSingleRollEffect('self'),
|
||
playSingleRollEffect('opponent'),
|
||
]);
|
||
}
|
||
|
||
function stopSingleRollEffect(side) {
|
||
var node = rollEffectNode(side);
|
||
var player = rollEffectPlayers[side];
|
||
if (player) player.stop();
|
||
if (node) {
|
||
node.innerHTML = '';
|
||
node.classList.remove('is-playing', 'is-fallback');
|
||
}
|
||
}
|
||
|
||
function stopRollEffects() {
|
||
stopSingleRollEffect('self');
|
||
stopSingleRollEffect('opponent');
|
||
}
|
||
|
||
function setText(node, value) {
|
||
if (node) node.textContent = value == null ? '' : value;
|
||
}
|
||
|
||
function i18nText(key, fallback) {
|
||
return window.HyAppI18n && window.HyAppI18n.t
|
||
? window.HyAppI18n.t(key, fallback)
|
||
: fallback;
|
||
}
|
||
|
||
function i18nFormat(key, fallback, values) {
|
||
var text = i18nText(key, fallback);
|
||
return text.replace(
|
||
/\{([a-zA-Z0-9_]+)\}/g,
|
||
function (match, name) {
|
||
return Object.prototype.hasOwnProperty.call(
|
||
values || {},
|
||
name
|
||
)
|
||
? values[name]
|
||
: match;
|
||
}
|
||
);
|
||
}
|
||
|
||
function matchText() {
|
||
return i18nText('diceMatch.match', 'Match');
|
||
}
|
||
|
||
function matchingText() {
|
||
return i18nText('diceMatch.matching', 'Matching...');
|
||
}
|
||
|
||
function setPhaseText(value) {
|
||
setText(role('phase-text'), value || '');
|
||
}
|
||
|
||
function setCountdownNumber(value) {
|
||
var node = role('countdown-number');
|
||
var layer = role('countdown-layer');
|
||
var next = value == null ? '' : String(value);
|
||
if (layer) {
|
||
layer.setAttribute(
|
||
'aria-hidden',
|
||
next ? 'false' : 'true'
|
||
);
|
||
}
|
||
if (!node) return;
|
||
if (
|
||
(node.getAttribute('data-countdown-value') || '') ===
|
||
next
|
||
) {
|
||
return;
|
||
}
|
||
if (next) {
|
||
node.setAttribute('data-countdown-value', next);
|
||
} else {
|
||
node.removeAttribute('data-countdown-value');
|
||
}
|
||
node.textContent = next;
|
||
node.classList.remove('is-ticking');
|
||
if (!next) return;
|
||
// 重新触发每一跳的弹出动画,保证 3、2、1 都是独立的一次视觉反馈。
|
||
void node.offsetWidth;
|
||
node.classList.add('is-ticking');
|
||
}
|
||
|
||
function setActionText(value) {
|
||
setText(
|
||
document.querySelector('.matching-action-text'),
|
||
value || matchingText()
|
||
);
|
||
}
|
||
|
||
function showMessage(message) {
|
||
var text =
|
||
message ||
|
||
i18nText('diceMatch.requestFailed', 'Request failed');
|
||
if (window.HyAppToast && window.HyAppToast.show) {
|
||
window.HyAppToast.show(text);
|
||
return;
|
||
}
|
||
setPhaseText(text);
|
||
}
|
||
|
||
function formatCoin(value) {
|
||
var number = Number(value || 0);
|
||
if (!Number.isFinite(number)) number = 0;
|
||
return Math.trunc(number).toLocaleString('en-US');
|
||
}
|
||
|
||
function formatTopCoin(value) {
|
||
var number = Number(value || 0);
|
||
if (!Number.isFinite(number)) number = 0;
|
||
number = Math.max(0, Math.trunc(number));
|
||
if (number < 1000000) return formatCoin(number);
|
||
return Math.floor(number / 1000) + 'K';
|
||
}
|
||
|
||
function setCoin(value) {
|
||
var number = Number(value || 0);
|
||
if (!Number.isFinite(number)) number = 0;
|
||
number = Math.max(0, Math.trunc(number));
|
||
state.balanceCoin = number;
|
||
var fullLabel = formatCoin(number);
|
||
var topCoin = document.querySelector('.top-coin');
|
||
if (topCoin) {
|
||
topCoin.classList.toggle(
|
||
'is-compact',
|
||
number >= 1000000
|
||
);
|
||
topCoin.setAttribute('title', fullLabel);
|
||
topCoin.setAttribute(
|
||
'aria-label',
|
||
'Recharge ' + fullLabel
|
||
);
|
||
}
|
||
setText(
|
||
document.querySelector('.top-coin span'),
|
||
formatTopCoin(number)
|
||
);
|
||
}
|
||
|
||
function cancelCoinCreditAnimation() {
|
||
if (coinCreditFrame) {
|
||
window.cancelAnimationFrame(coinCreditFrame);
|
||
coinCreditFrame = 0;
|
||
}
|
||
if (coinCreditLayer && coinCreditLayer.parentNode) {
|
||
coinCreditLayer.parentNode.removeChild(coinCreditLayer);
|
||
}
|
||
coinCreditLayer = null;
|
||
var topCoin = document.querySelector('.top-coin');
|
||
if (topCoin) topCoin.classList.remove('is-crediting');
|
||
}
|
||
|
||
function framePointFromRect(rect, anchorX, anchorY) {
|
||
var frameRect = frame.getBoundingClientRect();
|
||
var scale = frameRect.width / DESIGN_WIDTH || 1;
|
||
return {
|
||
x:
|
||
(rect.left +
|
||
rect.width * anchorX -
|
||
frameRect.left) /
|
||
scale,
|
||
y:
|
||
(rect.top + rect.height * anchorY - frameRect.top) /
|
||
scale,
|
||
};
|
||
}
|
||
|
||
function createCoinCreditLayer() {
|
||
cancelCoinCreditAnimation();
|
||
coinCreditLayer = document.createElement('div');
|
||
coinCreditLayer.className = 'coin-credit-layer';
|
||
frame.appendChild(coinCreditLayer);
|
||
return coinCreditLayer;
|
||
}
|
||
|
||
function burstCreditCoins(start, end) {
|
||
var layer = createCoinCreditLayer();
|
||
var offsets = [
|
||
[-8, 6],
|
||
[12, -4],
|
||
[3, 16],
|
||
[20, 10],
|
||
[-16, -3],
|
||
[8, -17],
|
||
[-2, -12],
|
||
[17, 22],
|
||
];
|
||
offsets.forEach(function (offset, index) {
|
||
var coin = document.createElement('img');
|
||
coin.className = 'coin-credit-flyer';
|
||
coin.src = 'assets/figma/coin.png';
|
||
coin.alt = '';
|
||
coin.draggable = false;
|
||
coin.setAttribute('aria-hidden', 'true');
|
||
coin.style.setProperty(
|
||
'--coin-credit-x',
|
||
start.x + offset[0] + 'px'
|
||
);
|
||
coin.style.setProperty(
|
||
'--coin-credit-y',
|
||
start.y + offset[1] + 'px'
|
||
);
|
||
coin.style.setProperty(
|
||
'--coin-credit-dx',
|
||
end.x - start.x - offset[0] + 'px'
|
||
);
|
||
coin.style.setProperty(
|
||
'--coin-credit-dy',
|
||
end.y - start.y - offset[1] + 'px'
|
||
);
|
||
coin.style.setProperty(
|
||
'--coin-credit-delay',
|
||
index * 64 + 'ms'
|
||
);
|
||
layer.appendChild(coin);
|
||
});
|
||
schedule(function () {
|
||
if (coinCreditLayer === layer) {
|
||
layer.remove();
|
||
coinCreditLayer = null;
|
||
}
|
||
}, 1500);
|
||
}
|
||
|
||
function animateCoinNumber(startValue, targetValue) {
|
||
var start = Math.max(
|
||
0,
|
||
Math.trunc(Number(startValue) || 0)
|
||
);
|
||
var target = Math.max(
|
||
0,
|
||
Math.trunc(Number(targetValue) || 0)
|
||
);
|
||
var startedAt = performance.now();
|
||
var duration = 1180;
|
||
function tick(now) {
|
||
var progress = Math.min(
|
||
Math.max((now - startedAt) / duration, 0),
|
||
1
|
||
);
|
||
var eased = 1 - Math.pow(1 - progress, 3);
|
||
setCoin(start + (target - start) * eased);
|
||
if (progress < 1) {
|
||
coinCreditFrame =
|
||
window.requestAnimationFrame(tick);
|
||
return;
|
||
}
|
||
coinCreditFrame = 0;
|
||
setCoin(target);
|
||
}
|
||
if (coinCreditFrame) {
|
||
window.cancelAnimationFrame(coinCreditFrame);
|
||
}
|
||
coinCreditFrame = window.requestAnimationFrame(tick);
|
||
}
|
||
|
||
function playVictoryCoinCredit(startValue, targetValue) {
|
||
var topCoin = document.querySelector('.top-coin');
|
||
var topCoinIcon = topCoin && topCoin.querySelector('img');
|
||
if (!topCoinIcon || targetValue <= startValue) {
|
||
setCoin(targetValue);
|
||
return false;
|
||
}
|
||
var start = { x: 95, y: 584 };
|
||
var end = framePointFromRect(
|
||
topCoinIcon.getBoundingClientRect(),
|
||
0.5,
|
||
0.5
|
||
);
|
||
burstCreditCoins(start, end);
|
||
animateCoinNumber(startValue, targetValue);
|
||
topCoin.classList.remove('is-crediting');
|
||
void topCoin.offsetWidth;
|
||
topCoin.classList.add('is-crediting');
|
||
schedule(function () {
|
||
topCoin.classList.remove('is-crediting');
|
||
}, 780);
|
||
return true;
|
||
}
|
||
|
||
function displayName(user) {
|
||
user = user || {};
|
||
return (
|
||
user.nickname ||
|
||
user.username ||
|
||
user.name ||
|
||
user.display_user_id ||
|
||
user.user_id ||
|
||
'NameName'
|
||
);
|
||
}
|
||
|
||
function displayID(user) {
|
||
user = user || {};
|
||
return user.display_user_id || user.user_id || '';
|
||
}
|
||
|
||
function setNameGroup(selector, user) {
|
||
var nodes = document.querySelectorAll(selector + ' div');
|
||
var name = displayName(user);
|
||
var sub = displayID(user);
|
||
if (nodes[0]) nodes[0].textContent = name;
|
||
if (nodes[1]) nodes[1].textContent = sub || name;
|
||
}
|
||
|
||
function avatarURL(user) {
|
||
user = user || {};
|
||
var url =
|
||
user.avatar || user.avatar_url || user.avatarUrl || '';
|
||
url = String(url || '').trim();
|
||
if (!url) return '';
|
||
var normalized = url.toLowerCase();
|
||
if (
|
||
normalized.indexOf('/avatar/default') !== -1 ||
|
||
normalized.indexOf('avatar/default.') !== -1 ||
|
||
normalized.indexOf('/default-avatar') !== -1 ||
|
||
normalized.indexOf('default_avatar') !== -1 ||
|
||
normalized.indexOf('cdn.example.com/avatar/default') !==
|
||
-1
|
||
) {
|
||
return '';
|
||
}
|
||
return url;
|
||
}
|
||
|
||
function frameURL(frameData) {
|
||
frameData = frameData || {};
|
||
return (
|
||
frameData.asset_url ||
|
||
frameData.preview_url ||
|
||
frameData.animation_url ||
|
||
frameData.url ||
|
||
''
|
||
);
|
||
}
|
||
|
||
function setImage(node, url, fallbackURL) {
|
||
if (!node) return;
|
||
var nextURL = url || fallbackURL || '';
|
||
if (nextURL) {
|
||
node.onerror = function () {
|
||
node.onerror = null;
|
||
node.src = transparentPixel;
|
||
node.classList.add('is-empty');
|
||
};
|
||
node.src = nextURL;
|
||
node.classList.remove('is-empty');
|
||
} else {
|
||
node.onerror = null;
|
||
node.src = transparentPixel;
|
||
node.classList.add('is-empty');
|
||
}
|
||
}
|
||
|
||
function setFrame(node, frameData) {
|
||
var url = frameURL(frameData);
|
||
setImage(node, url, '');
|
||
if (url) {
|
||
node.classList.add('has-frame');
|
||
node.classList.remove('is-empty');
|
||
} else {
|
||
node.classList.remove('has-frame');
|
||
node.classList.add('is-empty');
|
||
}
|
||
}
|
||
|
||
function renderSelfProfile() {
|
||
var profile = state.profile || {};
|
||
var avatar = avatarURL(profile);
|
||
var frameData = state.appearance.avatar_frame || {};
|
||
setNameGroup('.lobby-player-name', profile);
|
||
setNameGroup('.player-name', profile);
|
||
setImage(role('self-avatar'), avatar, '');
|
||
setImage(role('self-battle-avatar'), avatar, '');
|
||
setFrame(role('self-avatar-frame'), frameData);
|
||
setFrame(role('self-battle-avatar-frame'), frameData);
|
||
}
|
||
|
||
function balanceFromResponse(data) {
|
||
var balances = (data && data.balances) || [];
|
||
for (var i = 0; i < balances.length; i += 1) {
|
||
if (balances[i].asset_type === 'COIN') {
|
||
return balances[i].available_amount || 0;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
function enabledStakeOptions(config) {
|
||
var options = (config && config.stake_options) || [];
|
||
return options
|
||
.filter(function (item) {
|
||
return (
|
||
item &&
|
||
item.enabled !== false &&
|
||
item.stake_coin > 0
|
||
);
|
||
})
|
||
.sort(function (left, right) {
|
||
return (
|
||
Number(left.sort_order || 0) -
|
||
Number(right.sort_order || 0) ||
|
||
Number(left.stake_coin || 0) -
|
||
Number(right.stake_coin || 0)
|
||
);
|
||
});
|
||
}
|
||
|
||
function selectStake(button) {
|
||
if (!button) return;
|
||
each(document.querySelectorAll('.stake'), function (stake) {
|
||
var selected = stake === button;
|
||
var img = stake.querySelector('img');
|
||
if (selected) {
|
||
stake.setAttribute('data-selected', 'true');
|
||
state.selectedStake = Number(
|
||
stake.getAttribute('data-stake') || 0
|
||
);
|
||
} else {
|
||
stake.removeAttribute('data-selected');
|
||
}
|
||
stake.setAttribute(
|
||
'aria-pressed',
|
||
selected ? 'true' : 'false'
|
||
);
|
||
if (img) {
|
||
img.src = selected
|
||
? 'assets/figma/stake-fill.png'
|
||
: 'assets/figma/stake-outline.svg';
|
||
}
|
||
});
|
||
}
|
||
|
||
function renderStakeOptions(config) {
|
||
var options = enabledStakeOptions(config);
|
||
if (!options.length) {
|
||
selectStake(
|
||
document.querySelector(
|
||
'.stake[data-selected="true"]'
|
||
) || document.querySelector('.stake')
|
||
);
|
||
return;
|
||
}
|
||
var selected = state.selectedStake;
|
||
var fallbackSelected = options[0] && options[0].stake_coin;
|
||
var selectedButton = null;
|
||
each(
|
||
document.querySelectorAll('.stake'),
|
||
function (button, index) {
|
||
var option = options[index];
|
||
if (!option) {
|
||
button.style.display = 'none';
|
||
return;
|
||
}
|
||
button.style.display = '';
|
||
button.setAttribute(
|
||
'data-stake',
|
||
String(option.stake_coin)
|
||
);
|
||
button.setAttribute(
|
||
'aria-label',
|
||
formatCoin(option.stake_coin)
|
||
);
|
||
setText(
|
||
button.querySelector('.stake-value'),
|
||
formatCoin(option.stake_coin)
|
||
);
|
||
if (option.stake_coin === selected) {
|
||
selectedButton = button;
|
||
}
|
||
if (
|
||
!selectedButton &&
|
||
option.stake_coin === fallbackSelected
|
||
) {
|
||
selectedButton = button;
|
||
}
|
||
}
|
||
);
|
||
selectStake(
|
||
selectedButton || document.querySelector('.stake')
|
||
);
|
||
if (config) {
|
||
var totalFee =
|
||
(Number(config.fee_bps || 0) +
|
||
Number(config.pool_bps || 0)) /
|
||
100;
|
||
setText(
|
||
document.querySelector('.fee-note'),
|
||
i18nFormat(
|
||
'diceMatch.configFeeNote',
|
||
'Fee {fee}%',
|
||
{
|
||
fee: totalFee,
|
||
}
|
||
)
|
||
);
|
||
}
|
||
}
|
||
|
||
function resetFrame() {
|
||
stopRollEffects();
|
||
cancelCoinCreditAnimation();
|
||
frame.classList.remove(
|
||
'is-searching',
|
||
'is-matching',
|
||
'is-countdown',
|
||
'is-victory',
|
||
'is-self-win-compare',
|
||
'has-scores'
|
||
);
|
||
setCountdownNumber('');
|
||
}
|
||
|
||
function setMatchButtonBusy(busy, label) {
|
||
var button = document.querySelector('.match-button');
|
||
if (!button) return;
|
||
button.disabled = !!busy;
|
||
setText(button.querySelector('span'), label || matchText());
|
||
}
|
||
|
||
function currentRoomID() {
|
||
var params =
|
||
window.HyAppParams &&
|
||
window.HyAppParams.current &&
|
||
window.HyAppParams.current.raw;
|
||
if (params && params.get) {
|
||
return (
|
||
params.get('room_id') || params.get('roomId') || ''
|
||
);
|
||
}
|
||
return (
|
||
new URLSearchParams(window.location.search).get(
|
||
'room_id'
|
||
) || ''
|
||
);
|
||
}
|
||
|
||
function selfUserID() {
|
||
return String(
|
||
(state.profile && state.profile.user_id) || ''
|
||
);
|
||
}
|
||
|
||
function participantUser(participant) {
|
||
participant = participant || {};
|
||
var user = Object.assign(
|
||
{
|
||
user_id: participant.user_id || '',
|
||
display_user_id: participant.display_user_id || '',
|
||
nickname:
|
||
participant.nickname ||
|
||
participant.username ||
|
||
'',
|
||
username: participant.username || '',
|
||
avatar:
|
||
participant.avatar ||
|
||
participant.avatar_url ||
|
||
participant.avatarUrl ||
|
||
'',
|
||
},
|
||
participant.user || {}
|
||
);
|
||
if (!user.user_id) user.user_id = participant.user_id || '';
|
||
if (!user.display_user_id) {
|
||
user.display_user_id =
|
||
participant.display_user_id || '';
|
||
}
|
||
if (!user.nickname && participant.nickname) {
|
||
user.nickname = participant.nickname;
|
||
}
|
||
if (!user.username && participant.username) {
|
||
user.username = participant.username;
|
||
}
|
||
if (!user.avatar) {
|
||
user.avatar =
|
||
participant.avatar ||
|
||
participant.avatar_url ||
|
||
participant.avatarUrl ||
|
||
'';
|
||
}
|
||
return user;
|
||
}
|
||
|
||
function findSelfParticipant(match) {
|
||
var participants = (match && match.participants) || [];
|
||
var id = selfUserID();
|
||
for (var i = 0; i < participants.length; i += 1) {
|
||
if (String(participants[i].user_id || '') === id) {
|
||
return participants[i];
|
||
}
|
||
}
|
||
for (var j = 0; j < participants.length; j += 1) {
|
||
if (!participants[j].is_robot) return participants[j];
|
||
}
|
||
return participants[0] || null;
|
||
}
|
||
|
||
function findOpponentParticipant(match, selfParticipant) {
|
||
var participants = (match && match.participants) || [];
|
||
for (var i = 0; i < participants.length; i += 1) {
|
||
if (participants[i] !== selfParticipant) {
|
||
return participants[i];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function sameParticipant(left, right) {
|
||
if (!left || !right) return false;
|
||
var leftID = String(left.user_id || '');
|
||
var rightID = String(right.user_id || '');
|
||
return leftID !== '' && leftID === rightID;
|
||
}
|
||
|
||
function findWinnerParticipant(match) {
|
||
var participants = (match && match.participants) || [];
|
||
for (var i = 0; i < participants.length; i += 1) {
|
||
if (
|
||
participants[i] &&
|
||
participants[i].result === 'win'
|
||
) {
|
||
return participants[i];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function dicePointValue(participant) {
|
||
var points = (participant && participant.dice_points) || [];
|
||
if (!points.length) return '';
|
||
var point = Number(points[0] || 0);
|
||
if (!isFinite(point) || point < 1 || point > 6) return '';
|
||
return String(Math.floor(point));
|
||
}
|
||
|
||
function diceScoreImage(point, side) {
|
||
if (!point) return '';
|
||
return (
|
||
'assets/figma/dice-' +
|
||
(side === 'self' ? 'red' : 'blue') +
|
||
'-' +
|
||
point +
|
||
'.png'
|
||
);
|
||
}
|
||
|
||
function setDiceScore(node, point, side) {
|
||
if (!node) return;
|
||
node.textContent = '';
|
||
if (!point) {
|
||
node.removeAttribute('data-score-value');
|
||
return;
|
||
}
|
||
node.setAttribute('data-score-value', point);
|
||
var img = document.createElement('img');
|
||
img.src = diceScoreImage(point, side);
|
||
img.alt = '';
|
||
img.draggable = false;
|
||
img.setAttribute('aria-hidden', 'true');
|
||
node.appendChild(img);
|
||
}
|
||
|
||
function isDrawReroll(match) {
|
||
return (
|
||
match &&
|
||
match.result === 'draw' &&
|
||
match.status !== 'settled'
|
||
);
|
||
}
|
||
|
||
function isSelfWinner(match) {
|
||
var self = findSelfParticipant(match);
|
||
var winner = findWinnerParticipant(match);
|
||
if (!self) return false;
|
||
if (winner && sameParticipant(winner, self)) return true;
|
||
return self.result === 'win';
|
||
}
|
||
|
||
function setSelfWinCompare(visible) {
|
||
frame.classList.remove('is-self-win-compare');
|
||
if (!visible) return;
|
||
// 每一局 Compare 都要重新播放“砸下”效果,因此先读一次布局强制浏览器提交移除状态。
|
||
void frame.offsetWidth;
|
||
frame.classList.add('is-self-win-compare');
|
||
}
|
||
|
||
function renderMatchParticipants(match) {
|
||
var self = findSelfParticipant(match);
|
||
var opponent = findOpponentParticipant(match, self);
|
||
if (self && self.user) {
|
||
setNameGroup('.player-name', participantUser(self));
|
||
}
|
||
if (opponent) {
|
||
var opponentUser = participantUser(opponent);
|
||
var opponentAvatar = avatarURL(opponentUser);
|
||
var hasOpponentProfile =
|
||
displayName(opponentUser) !== '?' ||
|
||
!!opponentAvatar ||
|
||
!!displayID(opponentUser);
|
||
frame.classList.toggle(
|
||
'has-opponent',
|
||
hasOpponentProfile
|
||
);
|
||
setText(
|
||
document.querySelector('.opponent-mark'),
|
||
displayName(opponentUser)
|
||
);
|
||
setNameGroup(
|
||
'[data-role="opponent-name"]',
|
||
opponentUser
|
||
);
|
||
setText(
|
||
document.querySelector('.matching-label'),
|
||
i18nText('diceMatch.ready', 'Ready')
|
||
);
|
||
setImage(role('opponent-avatar'), opponentAvatar, '');
|
||
setFrame(
|
||
role('opponent-avatar-frame'),
|
||
opponent.avatar_frame || {}
|
||
);
|
||
} else {
|
||
setText(document.querySelector('.opponent-mark'), '?');
|
||
setText(
|
||
document.querySelector('.matching-label'),
|
||
matchingText()
|
||
);
|
||
setNameGroup('[data-role="opponent-name"]', {
|
||
nickname: '?',
|
||
});
|
||
frame.classList.remove('has-opponent');
|
||
setImage(role('opponent-avatar'), '', '');
|
||
setFrame(role('opponent-avatar-frame'), {});
|
||
}
|
||
var selfScore = dicePointValue(self);
|
||
var opponentScore = dicePointValue(opponent);
|
||
setDiceScore(role('self-score'), selfScore, 'self');
|
||
setDiceScore(
|
||
role('opponent-score'),
|
||
opponentScore,
|
||
'opponent'
|
||
);
|
||
if (selfScore && opponentScore) {
|
||
frame.classList.add('has-scores');
|
||
} else {
|
||
frame.classList.remove('has-scores');
|
||
}
|
||
}
|
||
|
||
function matchFromData(data) {
|
||
state.serverTimeMS = Number(
|
||
(data && data.server_time_ms) || 0
|
||
);
|
||
return (data && data.match) || data || {};
|
||
}
|
||
|
||
function isWaitingMatch(match) {
|
||
if (!match) return false;
|
||
return (
|
||
match.phase === 'waiting' ||
|
||
(match.status === 'waiting' &&
|
||
Number(match.current_players || 0) <
|
||
Number(match.min_players || 2))
|
||
);
|
||
}
|
||
|
||
function phaseDeadlineLeft(match) {
|
||
var deadline = Number(
|
||
(match && match.phase_deadline_ms) || 0
|
||
);
|
||
var serverTime = state.serverTimeMS || Date.now();
|
||
if (!deadline) return 3000;
|
||
return Math.max(deadline - serverTime, 0);
|
||
}
|
||
|
||
function pollMatch(matchID) {
|
||
clearPollTimer();
|
||
pollTimer = window.setTimeout(function () {
|
||
pollTimer = 0;
|
||
var game = diceAPI();
|
||
if (!game || !matchID || !state.busy) return;
|
||
game.getMatch(matchID)
|
||
.then(handleMatchData)
|
||
.catch(function (error) {
|
||
showMessage(error.message);
|
||
schedule(function () {
|
||
pollMatch(matchID);
|
||
}, 1000);
|
||
});
|
||
}, 1000);
|
||
}
|
||
|
||
function enterWaiting(match) {
|
||
state.match = match;
|
||
state.roundStarted = false;
|
||
resetFrame();
|
||
frame.classList.add('is-searching');
|
||
setActionText(matchingText());
|
||
setPhaseText(matchingText());
|
||
setMatchButtonBusy(true, matchingText());
|
||
renderMatchParticipants(match);
|
||
pollMatch(match.match_id);
|
||
}
|
||
|
||
function runCountdown(match, sequence) {
|
||
var duration = Math.min(phaseDeadlineLeft(match), 3000);
|
||
var endAt = Date.now() + Math.max(duration, 0);
|
||
function tick() {
|
||
if (sequence !== roundSequence) return;
|
||
var left = Math.max(endAt - Date.now(), 0);
|
||
if (left <= 0) {
|
||
frame.classList.remove('is-countdown');
|
||
setCountdownNumber('');
|
||
setPhaseText(i18nText('diceMatch.roll', 'Roll'));
|
||
rollAndAnimate(sequence);
|
||
return;
|
||
}
|
||
setPhaseText('');
|
||
setCountdownNumber(Math.ceil(left / 1000));
|
||
schedule(tick, 160);
|
||
}
|
||
tick();
|
||
}
|
||
|
||
function beginRound(match) {
|
||
if (state.roundStarted) {
|
||
renderMatchParticipants(match);
|
||
return;
|
||
}
|
||
clearPollTimer();
|
||
state.roundStarted = true;
|
||
state.busy = true;
|
||
state.match = match;
|
||
roundSequence += 1;
|
||
resetFrame();
|
||
frame.classList.add('is-matching', 'is-countdown');
|
||
setMatchButtonBusy(true, matchText());
|
||
renderMatchParticipants(match);
|
||
postBridge('gameMatchReady', {
|
||
game_code: 'touzi',
|
||
match_id: match.match_id,
|
||
});
|
||
runCountdown(match, roundSequence);
|
||
}
|
||
|
||
function rollAndAnimate(sequence) {
|
||
var game = diceAPI();
|
||
var matchID = state.match && state.match.match_id;
|
||
if (!game || !matchID) {
|
||
showMessage(
|
||
i18nText(
|
||
'diceMatch.apiUnavailable',
|
||
'Dice API unavailable'
|
||
)
|
||
);
|
||
resetToLobby(false);
|
||
return;
|
||
}
|
||
frame.classList.remove('is-countdown');
|
||
setCountdownNumber('');
|
||
setPhaseText(i18nText('diceMatch.rolling', 'Rolling...'));
|
||
frame.classList.remove('has-scores');
|
||
playRollEffects();
|
||
trackSelfGameEvent('reveal_enter', {
|
||
match_id: matchID,
|
||
});
|
||
postBridge('gameDiceRolling', {
|
||
game_code: 'touzi',
|
||
match_id: matchID,
|
||
});
|
||
var rollPromise = game
|
||
.roll(matchID)
|
||
.then(function (data) {
|
||
trackSelfGameEvent('roll_api', {
|
||
match_id: matchID,
|
||
success: true,
|
||
});
|
||
return matchFromData(data);
|
||
})
|
||
.catch(function () {
|
||
trackSelfGameEvent('roll_api', {
|
||
match_id: matchID,
|
||
success: false,
|
||
error_code: 'roll_failed',
|
||
});
|
||
return game.getMatch(matchID).then(function (data) {
|
||
return matchFromData(data);
|
||
});
|
||
});
|
||
Promise.all([rollPromise, delay(3000)])
|
||
.then(function (results) {
|
||
if (sequence !== roundSequence) {
|
||
stopRollEffects();
|
||
return;
|
||
}
|
||
var match = results[0];
|
||
state.match = match;
|
||
stopRollEffects();
|
||
renderMatchParticipants(match);
|
||
frame.classList.add('has-scores');
|
||
var drawReroll = isDrawReroll(match);
|
||
var selfWinCompare =
|
||
!drawReroll && isSelfWinner(match);
|
||
setPhaseText(
|
||
selfWinCompare
|
||
? ''
|
||
: drawReroll
|
||
? i18nText('diceMatch.draw', 'Draw')
|
||
: i18nText('diceMatch.compare', 'Compare')
|
||
);
|
||
setSelfWinCompare(selfWinCompare);
|
||
if (drawReroll) {
|
||
return delay(2000).then(function () {
|
||
if (sequence !== roundSequence) return;
|
||
rollAndAnimate(sequence);
|
||
});
|
||
}
|
||
return delay(3000).then(function () {
|
||
showSettlement(match, sequence);
|
||
});
|
||
})
|
||
.catch(function (error) {
|
||
stopRollEffects();
|
||
if (isInsufficientBalanceError(error)) {
|
||
resetToLobby(false);
|
||
handleInsufficientBalance(error);
|
||
return;
|
||
}
|
||
showMessage(error.message);
|
||
resetToLobby(false);
|
||
});
|
||
}
|
||
|
||
function renderVictory(match) {
|
||
var self = findSelfParticipant(match);
|
||
var winner = findWinnerParticipant(match) || self;
|
||
var winnerIsSelf = sameParticipant(winner, self);
|
||
var winnerUser = winnerIsSelf
|
||
? Object.assign(
|
||
{},
|
||
participantUser(winner),
|
||
state.profile || {}
|
||
)
|
||
: participantUser(winner);
|
||
var winnerCard = role('winner-card');
|
||
if (winnerCard) {
|
||
winnerCard.src = winnerIsSelf
|
||
? 'assets/figma/win-player-card.png'
|
||
: 'assets/figma/opponent-card.png';
|
||
}
|
||
setImage(role('winner-avatar'), avatarURL(winnerUser), '');
|
||
setNameGroup('.victory-name', winnerUser);
|
||
|
||
setText(
|
||
document.querySelector('.victory-panel-label'),
|
||
i18nText('diceMatch.win', 'Win')
|
||
);
|
||
setText(
|
||
document.querySelector('.victory-panel-amount'),
|
||
formatCoin((winner && winner.payout_coin) || 0)
|
||
);
|
||
if (self && self.balance_after > 0) {
|
||
var balanceAfter = Math.trunc(
|
||
Number(self.balance_after) || 0
|
||
);
|
||
if (winnerIsSelf && balanceAfter > state.balanceCoin) {
|
||
return playVictoryCoinCredit(
|
||
state.balanceCoin,
|
||
balanceAfter
|
||
);
|
||
}
|
||
setCoin(balanceAfter);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function refreshBalance() {
|
||
if (!api().wallet || !api().wallet.balances) return;
|
||
api()
|
||
.wallet.balances('COIN')
|
||
.then(function (data) {
|
||
setCoin(balanceFromResponse(data));
|
||
})
|
||
.catch(function () {});
|
||
}
|
||
|
||
function showSettlement(match, sequence) {
|
||
if (sequence !== roundSequence) return;
|
||
state.match = match;
|
||
renderMatchParticipants(match);
|
||
resetFrame();
|
||
frame.classList.add('is-victory');
|
||
if (!renderVictory(match)) refreshBalance();
|
||
trackSelfGameEvent('settlement_show', {
|
||
match_id: match.match_id,
|
||
result: match.result,
|
||
});
|
||
postBridge('gameDiceSettled', {
|
||
game_code: 'touzi',
|
||
match_id: match.match_id,
|
||
result: match.result,
|
||
});
|
||
delay(SETTLEMENT_AUTO_CLOSE_MS).then(function () {
|
||
if (sequence === roundSequence) resetToLobby(true);
|
||
});
|
||
}
|
||
|
||
function handleMatchData(data) {
|
||
var match = matchFromData(data);
|
||
if (!match || !match.match_id) return;
|
||
state.match = match;
|
||
renderMatchParticipants(match);
|
||
if (
|
||
match.status === 'settled' ||
|
||
match.phase === 'settled'
|
||
) {
|
||
roundSequence += 1;
|
||
showSettlement(match, roundSequence);
|
||
return;
|
||
}
|
||
if (
|
||
match.status === 'canceled' ||
|
||
match.phase === 'canceled'
|
||
) {
|
||
resetToLobby(true);
|
||
return;
|
||
}
|
||
if (isWaitingMatch(match)) {
|
||
enterWaiting(match);
|
||
return;
|
||
}
|
||
beginRound(match);
|
||
}
|
||
|
||
function selectedStakeCoin() {
|
||
var selected = document.querySelector(
|
||
'.stake[data-selected="true"]'
|
||
);
|
||
return Number(
|
||
(selected && selected.getAttribute('data-stake')) ||
|
||
state.selectedStake ||
|
||
0
|
||
);
|
||
}
|
||
|
||
function startMatch() {
|
||
var game = diceAPI();
|
||
var stake = selectedStakeCoin();
|
||
if (!game) {
|
||
showMessage(
|
||
i18nText(
|
||
'diceMatch.apiUnavailable',
|
||
'Dice API unavailable'
|
||
)
|
||
);
|
||
return;
|
||
}
|
||
if (!stake) {
|
||
showMessage(
|
||
i18nText('diceMatch.selectStake', 'Select stake')
|
||
);
|
||
return;
|
||
}
|
||
clearMatchTimers();
|
||
state.busy = true;
|
||
state.roundStarted = false;
|
||
state.match = null;
|
||
roundSequence += 1;
|
||
resetFrame();
|
||
frame.classList.add('is-searching');
|
||
setActionText(matchingText());
|
||
setPhaseText(matchingText());
|
||
setMatchButtonBusy(true, matchingText());
|
||
postBridge('gameMatchRequested', {
|
||
game_code: 'touzi',
|
||
stake: stake,
|
||
});
|
||
trackSelfGameEvent('match_click', {
|
||
stake_coin: stake,
|
||
});
|
||
var matchStartedAt = performance.now();
|
||
game.match({
|
||
game_id: 'dice',
|
||
room_id: currentRoomID(),
|
||
stake_coin: stake,
|
||
})
|
||
.then(function (data) {
|
||
var match = matchFromData(data);
|
||
trackSelfGameEvent('match_success', {
|
||
match_id: match && match.match_id,
|
||
stake_coin: stake,
|
||
duration_ms: Math.round(
|
||
performance.now() - matchStartedAt
|
||
),
|
||
});
|
||
handleMatchData(data);
|
||
})
|
||
.catch(function (error) {
|
||
trackSelfGameEvent('match_api', {
|
||
stake_coin: stake,
|
||
duration_ms: Math.round(
|
||
performance.now() - matchStartedAt
|
||
),
|
||
success: false,
|
||
error_code: error && error.message,
|
||
});
|
||
if (isInsufficientBalanceError(error)) {
|
||
resetToLobby(true);
|
||
handleInsufficientBalance(error);
|
||
return;
|
||
}
|
||
showMessage(error.message);
|
||
resetToLobby(true);
|
||
});
|
||
}
|
||
|
||
function cancelWaitingMatch() {
|
||
var game = diceAPI();
|
||
var match = state.match;
|
||
if (
|
||
!game ||
|
||
!match ||
|
||
!match.match_id ||
|
||
!isWaitingMatch(match)
|
||
) {
|
||
return Promise.resolve();
|
||
}
|
||
return game
|
||
.cancel(match.match_id)
|
||
.then(function () {
|
||
trackSelfGameEvent('match_cancel', {
|
||
match_id: match.match_id,
|
||
});
|
||
})
|
||
.catch(function () {
|
||
trackSelfGameEvent('cancel_api', {
|
||
match_id: match.match_id,
|
||
success: false,
|
||
error_code: 'cancel_failed',
|
||
});
|
||
});
|
||
}
|
||
|
||
function resetToLobby(keepStake) {
|
||
clearMatchTimers();
|
||
state.busy = false;
|
||
state.roundStarted = false;
|
||
state.match = null;
|
||
roundSequence += 1;
|
||
resetFrame();
|
||
setPhaseText('');
|
||
setActionText(matchingText());
|
||
setDiceScore(role('self-score'), '', 'self');
|
||
setDiceScore(role('opponent-score'), '', 'opponent');
|
||
setText(document.querySelector('.opponent-mark'), '?');
|
||
setText(
|
||
document.querySelector('.matching-label'),
|
||
matchingText()
|
||
);
|
||
setNameGroup('[data-role="opponent-name"]', {
|
||
nickname: '?',
|
||
});
|
||
frame.classList.remove('has-opponent');
|
||
setImage(role('opponent-avatar'), '', '');
|
||
setFrame(role('opponent-avatar-frame'), {});
|
||
setMatchButtonBusy(false, matchText());
|
||
if (!keepStake) state.selectedStake = 0;
|
||
renderStakeOptions(state.config);
|
||
}
|
||
|
||
function refreshLocalizedRuntimeText() {
|
||
renderStakeOptions(state.config);
|
||
if (state.match) {
|
||
renderMatchParticipants(state.match);
|
||
}
|
||
if (frame.classList.contains('is-victory')) {
|
||
renderVictory(state.match || {});
|
||
}
|
||
if (frame.classList.contains('is-searching')) {
|
||
setActionText(matchingText());
|
||
setPhaseText(matchingText());
|
||
setMatchButtonBusy(true, matchingText());
|
||
return;
|
||
}
|
||
if (state.preloading) {
|
||
setMatchButtonBusy(
|
||
true,
|
||
i18nText('diceMatch.loading', 'Loading')
|
||
);
|
||
return;
|
||
}
|
||
if (!state.busy) {
|
||
setActionText(matchingText());
|
||
setMatchButtonBusy(false, matchText());
|
||
}
|
||
}
|
||
|
||
function requestLobbyData() {
|
||
var lobbyData = {
|
||
profile: {},
|
||
appearance: {},
|
||
balance: null,
|
||
configResponse: null,
|
||
};
|
||
function onceTask(task) {
|
||
var promise = null;
|
||
return function () {
|
||
if (!promise) promise = task();
|
||
return promise;
|
||
};
|
||
}
|
||
if (!window.HyAppAPI || !diceAPI()) {
|
||
showMessage(
|
||
i18nText(
|
||
'diceMatch.apiUnavailable',
|
||
'Dice API unavailable'
|
||
)
|
||
);
|
||
return {
|
||
lobbyData: lobbyData,
|
||
tasks: [
|
||
function () {
|
||
return Promise.resolve();
|
||
},
|
||
],
|
||
};
|
||
}
|
||
setMatchButtonBusy(
|
||
true,
|
||
i18nText('diceMatch.loading', 'Loading')
|
||
);
|
||
var configStartedAt = performance.now();
|
||
var profileTask = function () {
|
||
return api()
|
||
.user.me()
|
||
.then(function (data) {
|
||
lobbyData.profile = data || {};
|
||
return data;
|
||
})
|
||
.catch(function () {
|
||
lobbyData.profile = {};
|
||
return null;
|
||
});
|
||
};
|
||
var appearanceTask = function () {
|
||
return api()
|
||
.user.appearance()
|
||
.then(function (data) {
|
||
lobbyData.appearance = data || {};
|
||
return data;
|
||
})
|
||
.catch(function () {
|
||
lobbyData.appearance = {};
|
||
return null;
|
||
});
|
||
};
|
||
var balanceTask = function () {
|
||
return api()
|
||
.wallet.balances('COIN')
|
||
.then(function (data) {
|
||
lobbyData.balance = data || null;
|
||
return data;
|
||
})
|
||
.catch(function () {
|
||
lobbyData.balance = null;
|
||
return null;
|
||
});
|
||
};
|
||
var configTask = function () {
|
||
return diceAPI()
|
||
.config('dice')
|
||
.then(function (data) {
|
||
lobbyData.configResponse = data || null;
|
||
trackSelfGameEvent('config_load_success', {
|
||
duration_ms: Math.round(
|
||
performance.now() - configStartedAt
|
||
),
|
||
});
|
||
return data;
|
||
})
|
||
.catch(function () {
|
||
lobbyData.configResponse = null;
|
||
trackSelfGameEvent('config_load_fail', {
|
||
duration_ms: Math.round(
|
||
performance.now() - configStartedAt
|
||
),
|
||
success: false,
|
||
error_code: 'config_failed',
|
||
});
|
||
return null;
|
||
});
|
||
};
|
||
return {
|
||
lobbyData: lobbyData,
|
||
tasks: [
|
||
onceTask(profileTask),
|
||
onceTask(appearanceTask),
|
||
onceTask(balanceTask),
|
||
onceTask(configTask),
|
||
],
|
||
};
|
||
}
|
||
|
||
function applyLobbyData(lobbyData) {
|
||
lobbyData = lobbyData || {};
|
||
state.profile = lobbyData.profile || {};
|
||
state.appearance = lobbyData.appearance || {};
|
||
state.config =
|
||
(lobbyData.configResponse &&
|
||
lobbyData.configResponse.config) ||
|
||
null;
|
||
if (lobbyData.balance) {
|
||
setCoin(balanceFromResponse(lobbyData.balance));
|
||
}
|
||
renderSelfProfile();
|
||
renderStakeOptions(state.config);
|
||
setMatchButtonBusy(
|
||
state.preloading,
|
||
state.preloading
|
||
? i18nText('diceMatch.loading', 'Loading')
|
||
: matchText()
|
||
);
|
||
}
|
||
|
||
function loadLobby() {
|
||
var lobby = requestLobbyData();
|
||
return runPreloadTasks(lobby.tasks).then(function () {
|
||
return preloadLobbyDataImages(lobby.lobbyData).then(
|
||
function () {
|
||
applyLobbyData(lobby.lobbyData);
|
||
return lobby.lobbyData;
|
||
}
|
||
);
|
||
});
|
||
}
|
||
|
||
function loadStartup() {
|
||
var lobby = requestLobbyData();
|
||
var tasks = lobby.tasks.slice();
|
||
collectDicePreloadURLs().forEach(function (url) {
|
||
tasks.push(function () {
|
||
return preloadResourceOnce(url);
|
||
});
|
||
});
|
||
tasks.push(function () {
|
||
return Promise.all([
|
||
lobby.tasks[0](),
|
||
lobby.tasks[1](),
|
||
]).then(function () {
|
||
return preloadLobbyDataImages(lobby.lobbyData);
|
||
});
|
||
});
|
||
// 启动期必须等用户、余额、配置、骰子 SVGA、六面骰子和页面图片都进入缓存后,才允许用户进入大厅操作。
|
||
return runPreloadTasks(tasks)
|
||
.then(function () {
|
||
applyLobbyData(lobby.lobbyData);
|
||
return new Promise(function (resolve) {
|
||
window.setTimeout(resolve, 180);
|
||
});
|
||
})
|
||
.then(function () {
|
||
completeLoadingScreen();
|
||
trackSelfGameEvent('preload_complete', {
|
||
duration_ms: Math.round(
|
||
performance.now() - pageStartedAt
|
||
),
|
||
asset_count: tasks.length,
|
||
});
|
||
});
|
||
}
|
||
|
||
function initActions() {
|
||
document.addEventListener('click', function (event) {
|
||
var target = event.target.closest('[data-action]');
|
||
if (!target) return;
|
||
var action = target.getAttribute('data-action');
|
||
if (action === 'back') {
|
||
cancelWaitingMatch().then(function () {
|
||
postBridge('back');
|
||
});
|
||
return;
|
||
}
|
||
if (action === 'recharge') {
|
||
openRecharge();
|
||
return;
|
||
}
|
||
if (state.preloading) return;
|
||
if (action === 'stake') {
|
||
if (state.busy) return;
|
||
selectStake(target);
|
||
trackSelfGameEvent('stake_select', {
|
||
stake_coin: state.selectedStake,
|
||
});
|
||
return;
|
||
}
|
||
if (action === 'match') {
|
||
if (!state.busy) startMatch();
|
||
return;
|
||
}
|
||
if (action === 'rematch') {
|
||
cancelWaitingMatch().then(function () {
|
||
resetToLobby(true);
|
||
postBridge('gameRematch', {
|
||
game_code: 'touzi',
|
||
});
|
||
trackSelfGameEvent('play_again', {
|
||
source: 'rematch',
|
||
stake_coin: state.selectedStake,
|
||
});
|
||
startMatch();
|
||
});
|
||
return;
|
||
}
|
||
if (action === 'play-again') {
|
||
cancelWaitingMatch().then(function () {
|
||
resetToLobby(true);
|
||
postBridge('gamePlayAgain', {
|
||
game_code: 'touzi',
|
||
});
|
||
trackSelfGameEvent('play_again', {
|
||
source: 'settlement',
|
||
stake_coin: state.selectedStake,
|
||
});
|
||
});
|
||
}
|
||
});
|
||
window.addEventListener('beforeunload', function () {
|
||
cancelWaitingMatch();
|
||
});
|
||
}
|
||
|
||
updateScale();
|
||
initActions();
|
||
trackSelfGameEvent('page_open', {
|
||
duration_ms: Math.round(performance.now() - pageStartedAt),
|
||
});
|
||
window.addEventListener(
|
||
'hyapp:i18n-ready',
|
||
refreshLocalizedRuntimeText
|
||
);
|
||
loadStartup().catch(function (error) {
|
||
showMessage(error && error.message);
|
||
completeLoadingScreen();
|
||
setMatchButtonBusy(false, matchText());
|
||
});
|
||
window.addEventListener('resize', updateScale);
|
||
window.addEventListener('orientationchange', updateScale);
|
||
if (window.HyAppBridge && window.HyAppBridge.ready) {
|
||
window.HyAppBridge.ready({
|
||
page: 'game/touzi',
|
||
figma_node_id: '466:61',
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|