Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: coin flip minigame #436

Merged
merged 17 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion assets/css/components.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import "components/avatar.css";
@import "components/field.css";
@import "components/dropdown.css";
@import "components/dropdown.css";
@import "components/coinflip.css";
105 changes: 105 additions & 0 deletions assets/css/components/coinflip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.coin {
position: relative;
margin: 0 auto;
width: 80px;
height: 80px;
perspective: 1000px; /* Add perspective to the parent element */
}

.coin div {
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: inset 0 0 45px rgba(255, 255, 255, 0.3), 0 12px 20px -10px rgba(0, 0, 0, 0.4);
background-size: contain;
position: absolute;
backface-visibility: hidden; /* Ensure backface is hidden */
}

.side-a {
background-image: url('/images/coin-void.svg');
z-index: 100;
}

.side-b {
background-image: url('/images/coin-dollar-sign.svg');
transform: rotateY(180deg); /* Rotate the back side */
}

.side-b-not-rotated {
background-image: url('/images/coin-dollar-sign.svg');
}

.coin {
transform-style: preserve-3d; /* Ensure 3D transformation */
transition: transform 1s ease-in;
}

.coin.heads {
animation: flipHeads 3s ease-out forwards;
}

.coin.tails {
animation: flipTails 3s ease-out forwards;
}

@keyframes flipHeads {
0% {
transform: rotateY(0);
scale: 1;
}
50% {
transform: rotateY(900deg); /* 2.5 full rotations */
scale: 1.2;
}
100% {
transform: rotateY(1800deg); /* 5 full rotations */
scale: 1;
}
}

@keyframes flipTails {
0% {
transform: rotateY(0);
scale: 1;
}
50% {
transform: rotateY(900deg); /* 2.5 full rotations */
scale: 1.2;
}
100% {
transform: rotateY(1980deg); /* 5.5 full rotations */
scale: 1;
}
}

@keyframes countdownAnimation {
0% {
transform: scale(1);
opacity: 1;
}
90% {
transform: scale(1.125);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}

.countdown-animation {
animation: countdownAnimation infinite 1s ease-in-out;
}

.gray-overlay::after {
content: '';
position: absolute;
@apply rounded-md;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3); /* Gray color with 50% opacity */
z-index: 1;
}
3 changes: 2 additions & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import live_select from "live_select"
import { QrScanner, Wheel, Confetti, Countdown, Sorting } from "./hooks";
import { QrScanner, Wheel, Confetti, Countdown, Sorting, CoinFlip } from "./hooks";

let Hooks = {
QrScanner: QrScanner,
Wheel: Wheel,
Confetti: Confetti,
Countdown: Countdown,
Sorting: Sorting,
CoinFlip: CoinFlip,
...live_select
};

Expand Down
112 changes: 112 additions & 0 deletions assets/js/hooks/coinflip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

export const CoinFlip = {
mounted() {
const roomId = this.el.dataset.roomId;
const streamId = this.el.dataset.streamId;
const result = this.el.dataset.result;
const finished = this.el.dataset.finished;
const player1Id = this.el.dataset.player1Id;
const player2Id = this.el.dataset.player2Id;
const fee = this.el.dataset.fee;


document.getElementById(`${streamId}-coin`).addEventListener('animationend', async (event) => {
const player2Id = this.el.dataset.player2Id;
const result = this.el.dataset.result;
console.log('animation end');
nunom27 marked this conversation as resolved.
Show resolved Hide resolved
await delay(500);
this.updateBetDisplay(streamId, player1Id, player2Id, fee, result);
await delay(1000);
this.pushEvent('animation-done', { room_id: roomId });
console.log('animation done');
console.log(event.animationName)
nunom27 marked this conversation as resolved.
Show resolved Hide resolved
});

this.initializeGame(player1Id, player2Id, streamId, fee, result, finished);
},
updated() {
const roomId = this.el.dataset.roomId;
const streamId = this.el.dataset.streamId;
const result = this.el.dataset.result;
const finished = this.el.dataset.finished;
const player1Id = this.el.dataset.player1Id;
const player2Id = this.el.dataset.player2Id;
const fee = this.el.dataset.fee;

this.initializeGame(player1Id, player2Id, streamId, fee, result, finished);
},

initializeGame(player1Id, player2Id, streamId, fee, result, finished) {
const coin = document.getElementById(`${streamId}-coin`);
const counter = document.getElementById(`${streamId}-countdown`);
const vsText = document.getElementById(`${streamId}-vs-text`);
coin.style.display = 'none';
counter.style.display = 'none';

const startCountdown = async () => {
vsText.style.display = 'none';
counter.style.display = 'flex';
for (let i = 3; i > 0; i--) {
counter.textContent = i.toString();
counter.classList.add('countdown-animation');
await delay(1000);
counter.classList.remove('countdown-animation');
};

counter.style.display = 'none'
coin.style.display = 'block';

if (result === 'heads') {
coin.classList.add('heads');
} else {
coin.classList.add('tails');
}

}

if (finished === 'true') {
vsText.style.display = 'none';
counter.style.display = 'none';
coin.style.display = 'block';
if (result === 'heads') {
coin.children[1].hidden = true;
} else {
coin.children[0].hidden = true;
coin.children[1].style.transform = 'rotateY(0deg)';
}
this.updateBetDisplay(streamId, player1Id, player2Id, fee, result);
return;
}

if (finished === 'false' && (result === 'heads' || result === 'tails')) {
startCountdown();

}
},
updateBetDisplay(streamId, player1Id, player2Id, fee, result) {
const player1Card = document.getElementById(`${streamId}-${player1Id}-card`);
const player1Bet = document.getElementById(`${streamId}-${player1Id}-bet`);
const player2Card = document.getElementById(`${streamId}-${player2Id}-card`);
const player2Bet = document.getElementById(`${streamId}-${player2Id}-bet`);

if (!player1Bet || !player2Bet) {
console.error('Bet elements not found');
return;
}

if (result === 'heads') {
player2Card.classList.add('gray-overlay');
player1Bet.textContent = Math.round(player1Bet.dataset.bet * 2 * (1 - fee));
// player1Bet.classList.add('text-green-500');
nunom27 marked this conversation as resolved.
Show resolved Hide resolved
player2Bet.textContent = `-${player2Bet.dataset.bet}`;
player2Bet.classList.add('text-red-500');
} else {
player1Card.classList.add('gray-overlay');
player1Bet.textContent = `-${player1Bet.dataset.bet}`;
player1Bet.classList.add('text-red-500');
player2Bet.textContent = Math.round(player2Bet.dataset.bet * 2 * (1 - fee));
// player2Bet.classList.add('text-green-500');
nunom27 marked this conversation as resolved.
Show resolved Hide resolved
}
}
};
1 change: 1 addition & 0 deletions assets/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { Wheel } from "./wheel.js";
export { Confetti } from "./confetti.js";
export { Sorting } from "./sorting.js";
export { Countdown } from "./countdown.js";
export { CoinFlip } from "./coinflip.js";
3 changes: 2 additions & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ config :safira, SafiraWeb.Endpoint,
patterns: [
~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/safira_web/(controllers|live|components)/.*(ex|heex)$"
~r"lib/safira_web/(controllers|live|components)/.*(ex|heex)$",
~r"lib/safira_web/.*(ex|heex)$"
]
]

Expand Down
Loading
Loading