Cosmic Luck Simulator
by PhantomOtter56798 lines12.8 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<title>Cosmic Luck Simulator</title>
<style>
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{
margin:0;
min-height:100%;
font-family:Arial,sans-serif;
color:white;
background:
radial-gradient(circle at 50% 15%,#302b70,#11142f 45%,#050612 100%);
}
body{overflow-x:hidden}
button{
font:inherit;
touch-action:manipulation;
}
header{
height:70px;
display:flex;
align-items:center;
justify-content:space-between;
padding:0 18px;
background:#08091dcc;
border-bottom:1px solid #ffffff18;
position:sticky;
top:0;
z-index:10;
backdrop-filter:blur(10px);
}
.logo{
font-size:clamp(19px,5vw,30px);
font-weight:900;
}
.logo span{color:#9c7cff}
.money{
background:#ffffff12;
border:1px solid #ffffff12;
padding:10px 15px;
border-radius:14px;
font-weight:bold;
font-size:18px;
}
main{
width:min(900px,94%);
margin:auto;
padding:30px 0 60px;
}
h1{
text-align:center;
font-size:clamp(35px,9vw,65px);
margin:10px 0;
background:linear-gradient(90deg,#fff,#9c8aff,#65eaff);
-webkit-background-clip:text;
color:transparent;
}
.subtitle{
text-align:center;
color:#a9acd0;
}
.spinnerWrap{
display:flex;
justify-content:center;
align-items:center;
position:relative;
margin:35px 0 25px;
}
.pointer{
position:absolute;
top:-20px;
z-index:5;
font-size:40px;
filter:drop-shadow(0 3px 5px #000);
}
.spinner{
width:min(72vw,350px);
height:min(72vw,350px);
border-radius:50%;
padding:11px;
background:conic-gradient(
#ff3838,
#ff9d38,
#fff038,
#48ff67,
#38ddff,
#6370ff,
#b33cff,
#ff3cae,
#ff3838
);
box-shadow:0 0 45px #7660ff66;
transition:transform 2.2s cubic-bezier(.12,.78,.15,1);
}
.spinnerInside{
width:100%;
height:100%;
border-radius:50%;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
background:radial-gradient(circle,#292953,#090a1c);
border:3px solid #ffffff18;
}
.dice{
font-size:65px;
}
.spinLabel{
font-size:27px;
font-weight:900;
}
.result{
text-align:center;
min-height:135px;
}
.chance{
font-size:clamp(45px,12vw,75px);
font-weight:1000;
}
.name{
font-size:clamp(20px,5vw,30px);
font-weight:900;
letter-spacing:1px;
}
.reward{
color:#6dff96;
font-weight:bold;
margin-top:8px;
}
#roll{
display:block;
width:min(500px,95%);
margin:10px auto 30px;
min-height:72px;
border:0;
border-radius:20px;
color:white;
background:linear-gradient(135deg,#6547ff,#a05cff);
font-size:30px;
font-weight:1000;
box-shadow:0 12px 35px #6345ff55;
transition:transform .1s,filter .1s;
}
#roll:active{
transform:scale(.95);
}
#roll:disabled{
opacity:.5;
}
.stats{
display:grid;
grid-template-columns:repeat(3,1fr);
gap:12px;
}
.stat{
background:#ffffff0d;
border:1px solid #ffffff10;
border-radius:17px;
padding:18px 10px;
text-align:center;
}
.statValue{
font-size:25px;
font-weight:900;
}
.statLabel{
color:#969abd;
font-size:12px;
margin-top:5px;
}
h2{
text-align:center;
margin-top:40px;
}
.shop{
display:grid;
grid-template-columns:repeat(3,1fr);
gap:13px;
}
.card{
background:#ffffff0d;
border:1px solid #ffffff10;
border-radius:18px;
padding:20px;
text-align:center;
}
.card h3{
margin:0 0 8px;
}
.card p{
color:#a5a8c6;
min-height:38px;
}
.buy{
width:100%;
padding:13px;
border:0;
border-radius:12px;
background:#29255b;
color:white;
font-weight:bold;
}
.buy:disabled{
opacity:.4;
}
.rarities{
margin-top:40px;
display:grid;
grid-template-columns:repeat(2,1fr);
gap:10px;
}
.rarity{
display:flex;
justify-content:space-between;
padding:14px;
border-radius:12px;
background:#ffffff0a;
}
.rarity strong{
font-size:14px;
}
.rarity span{
color:#aaaed0;
}
#popup{
position:fixed;
inset:0;
display:flex;
align-items:center;
justify-content:center;
pointer-events:none;
z-index:50;
}
#popupText{
font-size:clamp(35px,10vw,80px);
font-weight:1000;
text-align:center;
opacity:0;
transform:scale(.5);
}
.pop{
animation:pop .9s ease forwards;
}
@keyframes pop{
0%{opacity:0;transform:scale(.5)}
25%{opacity:1;transform:scale(1.15)}
70%{opacity:1;transform:scale(1)}
100%{opacity:0;transform:scale(1.35)}
}
.particle{
position:fixed;
width:8px;
height:8px;
border-radius:50%;
pointer-events:none;
z-index:100;
animation:fly 900ms ease-out forwards;
}
@keyframes fly{
from{
transform:translate(0,0);
opacity:1;
}
to{
transform:translate(var(--x),var(--y));
opacity:0;
}
}
@media(max-width:700px){
.stats{grid-template-columns:1fr}
.shop{grid-template-columns:1fr}
.rarities{grid-template-columns:1fr}
main{padding-top:20px}
}
</style>
</head>
<body>
<header>
<div class="logo">COSMIC<span>LUCK</span> 🎲</div>
<div class="money">💰 <span id="money">100</span></div>
</header>
<main>
<h1>LUCK SIMULATOR</h1>
<p class="subtitle">
Roll your luck and discover the rarest percentages!
</p>
<div class="spinnerWrap">
<div class="pointer">▼</div>
<div class="spinner" id="spinner">
<div class="spinnerInside">
<div class="dice">🎲</div>
<div class="spinLabel">ROLL</div>
</div>
</div>
</div>
<div class="result">
<div class="chance" id="chance">?</div>
<div class="name" id="name">READY?</div>
<div class="reward" id="reward">
Tap ROLL to begin!
</div>
</div>
<button id="roll">🎲 ROLL</button>
<div class="stats">
<div class="stat">
<div class="statValue" id="rolls">0</div>
<div class="statLabel">TOTAL ROLLS</div>
</div>
<div class="stat">
<div class="statValue" id="luck">1.00x</div>
<div class="statLabel">LUCK</div>
</div>
<div class="stat">
<div class="statValue" id="best">—</div>
<div class="statLabel">BEST</div>
</div>
</div>
<h2>🍀 LUCK SHOP</h2>
<div class="shop">
<div class="card">
<h3>🍀 Lucky Boost</h3>
<p>+0.25x Luck</p>
<button class="buy" data-cost="100" data-add="0.25">
💰 100
</button>
</div>
<div class="card">
<h3>✨ Super Luck</h3>
<p>+0.75x Luck</p>
<button class="buy" data-cost="500" data-add="0.75">
💰 500
</button>
</div>
<div class="card">
<h3>🌌 Cosmic Luck</h3>
<p>+2.00x Luck</p>
<button class="buy" data-cost="2500" data-add="2">
💰 2,500
</button>
</div>
</div>
<h2>📖 RARITY INDEX</h2>
<div class="rarities">
<div class="rarity"><strong>COMMON</strong><span>100%</span></div>
<div class="rarity"><strong>LUCKY</strong><span>90%</span></div>
<div class="rarity"><strong>UNUSUAL</strong><span>75%</span></div>
<div class="rarity"><strong>RARE</strong><span>60%</span></div>
<div class="rarity"><strong>FORTUNE</strong><span>50%</span></div>
<div class="rarity"><strong>EPIC</strong><span>40%</span></div>
<div class="rarity"><strong>LEGENDARY</strong><span>30%</span></div>
<div class="rarity"><strong>MYTHIC</strong><span>20%</span></div>
<div class="rarity"><strong>DIVINE</strong><span>10%</span></div>
<div class="rarity"><strong>CELESTIAL</strong><span>5%</span></div>
<div class="rarity"><strong>GALACTIC</strong><span>3%</span></div>
<div class="rarity"><strong>UNIVERSAL</strong><span>2%</span></div>
<div class="rarity" style="grid-column:1/-1">
<strong>🌌 COSMIC PERCENT</strong>
<span>1%</span>
</div>
</div>
</main>
<div id="popup">
<div id="popupText"></div>
</div>
<script>
"use strict";
/* =========================
GAME DATA
========================= */
const rarities = [
{chance:100,name:"COMMON",reward:5},
{chance:90,name:"LUCKY",reward:10},
{chance:75,name:"UNUSUAL",reward:20},
{chance:60,name:"RARE",reward:35},
{chance:50,name:"FORTUNE",reward:55},
{chance:40,name:"EPIC",reward:80},
{chance:30,name:"LEGENDARY",reward:130},
{chance:20,name:"MYTHIC",reward:220},
{chance:10,name:"DIVINE",reward:500},
{chance:5,name:"CELESTIAL",reward:1000},
{chance:3,name:"GALACTIC",reward:2500},
{chance:2,name:"UNIVERSAL",reward:6000},
{chance:1,name:"COSMIC PERCENT",reward:15000}
];
/* =========================
SAVE DATA
========================= */
let money = Number(localStorage.getItem("cosmic_money"));
if(!Number.isFinite(money)){
money = 100;
}
let rolls = Number(localStorage.getItem("cosmic_rolls"));
if(!Number.isFinite(rolls)){
rolls = 0;
}
let luck = Number(localStorage.getItem("cosmic_luck"));
if(!Number.isFinite(luck)){
luck = 1;
}
let best = Number(localStorage.getItem("cosmic_best"));
if(!Number.isFinite(best)){
best = 101;
}
let spinning = false;
let rotation = 0;
/* =========================
ELEMENTS
========================= */
const moneyEl = document.getElementById("money");
const rollsEl = document.getElementById("rolls");
const luckEl = document.getElementById("luck");
const bestEl = document.getElementById("best");
const spinner = document.getElementById("spinner");
const rollButton = document.getElementById("roll");
const chanceEl = document.getElementById("chance");
const nameEl = document.getElementById("name");
const rewardEl = document.getElementById("reward");
const popupText = document.getElementById("popupText");
/* =========================
SAVE
========================= */
function saveGame(){
localStorage.setItem("cosmic_money",money);
localStorage.setItem("cosmic_rolls",rolls);
localStorage.setItem("cosmic_luck",luck);
localStorage.setItem("cosmic_best",best);
}
/* =========================
UI
========================= */
function updateUI(){
moneyEl.textContent =
Math.floor(money).toLocaleString();
rollsEl.textContent =
rolls.toLocaleString();
luckEl.textContent =
luck.toFixed(2) + "x";
bestEl.textContent =
best === 101 ? "—" : best + "%";
document.querySelectorAll(".buy").forEach(button=>{
const cost = Number(button.dataset.cost);
button.disabled =
spinning || money < cost;
});
}
/* =========================
ROLL SELECTION
========================= */
function chooseRoll(){
/*
Higher luck increases the chance
of reaching the rarer entries.
*/
const weights = rarities.map(item=>{
const rarityPower =
(101 - item.chance) + 1;
return Math.pow(rarityPower,luck);
});
let total = 0;
for(const weight of weights){
total += weight;
}
let random = Math.random() * total;
for(let i=0;i<rarities.length;i++){
random -= weights[i];
if(random <= 0){
return rarities[i];
}
}
return rarities[0];
}
/* =========================
POPUP
========================= */
function popup(text){
popupText.textContent = text;
popupText.classList.remove("pop");
/*
Force the browser to notice the class
being removed before adding it again.
*/
void popupText.offsetWidth;
popupText.classList.add("pop");
}
/* =========================
PARTICLES
========================= */
function particles(){
for(let i=0;i<35;i++){
const p = document.createElement("div");
p.className = "particle";
p.style.left =
window.innerWidth / 2 + "px";
p.style.top =
window.innerHeight / 2 + "px";
p.style.setProperty(
"--x",
((Math.random()-.5)*500) + "px"
);
p.style.setProperty(
"--y",
((Math.random()-.5)*500) + "px"
);
document.body.appendChild(p);
setTimeout(()=>{
p.remove();
},900);
}
}
/* =========================
MAIN ROLL
========================= */
function doRoll(){
if(spinning){
return;
}
spinning = true;
rollButton.disabled = true;
chanceEl.textContent = "...";
nameEl.textContent = "SPINNING...";
rewardEl.textContent = "Good luck!";
const result = chooseRoll();
/*
Make the spinner rotate several full times.
*/
rotation +=
1800 +
Math.floor(Math.random()*900);
spinner.style.transform =
"rotate(" + rotation + "deg)";
/*
Wait for the visual spin to finish.
*/
setTimeout(()=>{
rolls++;
const earned =
Math.max(1,Math.floor(result.reward * luck));
money += earned;
if(result.chance < best){
best = result.chance;
}
chanceEl.textContent =
result.chance + "%";
nameEl.textContent =
result.name;
rewardEl.textContent =
"+💰 " + earned.toLocaleString();
popup(
result.chance +
"% " +
result.name
);
if(result.chance <= 10){
particles();
}
saveGame();
spinning = false;
updateUI();
},2300);
}
/* =========================
BUTTON EVENTS
========================= */
/*
Using addEventListener means the
button's click/tap handler is
attached directly after the page
has loaded.
*/
rollButton.addEventListener(
"click",
doRoll
);
/* =========================
SHOP
========================= */
document.querySelectorAll(".buy").forEach(button=>{
button.addEventListener("click",()=>{
if(spinning){
return;
}
const cost =
Number(button.dataset.cost);
const add =
Number(button.dataset.add);
if(money < cost){
return;
}
money -= cost;
luck += add;
saveGame();
updateUI();
popup(
"🍀 +" +
add.toFixed(2) +
"x LUCK!"
);
});
});
/* =========================
START
========================= */
updateUI();
</script>
</body>
</html>Game Source: Cosmic Luck Simulator
Creator: PhantomOtter56
Libraries: none
Complexity: complex (798 lines, 12.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: cosmic-luck-simulator-phantomotter56" to link back to the original. Then publish at arcadelab.ai/publish.