Fruit Blast
by StormBear76785 lines13.2 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Fruit Blast</title>
<style>
*{box-sizing:border-box}
body{
margin:0;background:linear-gradient(135deg,#ff4081,#5420c9);
color:#fff;font-family:Arial;text-align:center;
overflow:hidden
}
button{
border:0;border-radius:16px;padding:13px 20px;
margin:5px;font-size:16px;font-weight:bold;
background:#fff;color:#6420a8;box-shadow:0 4px 10px #0005
}
.screen{
min-height:100vh;display:flex;align-items:center;
justify-content:center;padding:12px
}
.box{
width:94%;max-width:420px;padding:22px 10px;
background:#ffffff22;border-radius:25px
}
#game{display:none;padding:8px}
#board{
width:94vw;max-width:430px;aspect-ratio:1;
margin:8px auto;display:grid;
grid-template-columns:repeat(8,1fr);gap:4px;
padding:5px;background:#ffffff25;border-radius:18px
}
.cell{
display:flex;align-items:center;justify-content:center;
background:#ffffff25;border-radius:9px;
font-size:clamp(21px,7vw,38px);
transition:.15s
}
.selected{
transform:scale(.78);
box-shadow:0 0 0 4px white,0 0 15px white
}
.blast{animation:blast .35s forwards}
@keyframes blast{
0%{transform:scale(1);opacity:1}
50%{transform:scale(1.5);opacity:.8}
100%{transform:scale(0);opacity:0}
}
.info{
width:94vw;max-width:430px;margin:auto;
display:flex;gap:4px
}
.info div{
flex:1;background:#0005;border-radius:10px;
padding:7px 2px;font-size:12px
}
#levels{
display:grid;grid-template-columns:repeat(5,1fr);
gap:6px;max-height:55vh;overflow:auto
}
.lvl{width:auto;margin:0;padding:10px 3px}
.unlocked{background:linear-gradient(135deg,#ffd000,#ff4b88);color:white}
.overlay{
position:fixed;inset:0;display:none;
align-items:center;justify-content:center;
background:#000b;z-index:20
}
.popup{
width:90%;max-width:390px;padding:25px 10px;
background:linear-gradient(135deg,#ff4198,#5923c8);
border-radius:28px
}
#intro{z-index:50;background:linear-gradient(135deg,#ff4198,#5923c8)}
.introFruit{
position:absolute;font-size:65px;
animation:fly 1s forwards
}
.a{left:-100px;top:25%}
.b{right:-100px;top:35%;animation-delay:.15s}
.c{left:30%;top:-100px;animation-delay:.3s}
.d{right:25%;bottom:-100px;animation-delay:.45s}
@keyframes fly{
to{transform:translate(0,0);opacity:1}
}
#mango{
font-size:100px;opacity:0;
animation:mangoIn .8s 1.1s forwards
}
@keyframes mangoIn{
to{opacity:1;transform:scale(1.1)}
}
.mangoBoom{animation:mangoBoom .5s forwards!important}
@keyframes mangoBoom{
50%{transform:scale(1.8)}
100%{transform:scale(4);opacity:0}
}
.hand{font-size:60px;animation:hand 1s infinite}
@keyframes hand{50%{transform:translateY(10px)}}
.pop{animation:pop .4s}
@keyframes pop{
50%{transform:scale(1.15)}
}
</style>
</head>
<body>
<!-- MENU -->
<div id="menu" class="screen">
<div class="box">
<h1>🍎🍊 FRUIT BLAST 🍇🍓</h1>
<p>🍬 Match 3 • Blast • Win!</p>
<button onclick="play()">▶ PLAY</button>
<button onclick="tutorial()">🎓 TUTORIAL</button>
<button onclick="levelMenu()">🗺 LEVEL SELECT</button>
<button onclick="musicToggle()">
🎵 MUSIC: <span id="mt">ON</span>
</button>
<button onclick="soundToggle()">
🔊 SOUND: <span id="st">ON</span>
</button>
<p>🔓 <span id="unlock"></span></p>
</div>
</div>
<!-- LEVEL MENU -->
<div id="levelMenu" class="screen" style="display:none">
<div class="box">
<h1>🗺 LEVEL SELECT</h1>
<div id="levels"></div>
<button onclick="menu()">🏠 MENU</button>
</div>
</div>
<!-- GAME -->
<div id="game">
<h2>🍬 FRUIT BLAST</h2>
<div class="info">
<div>🏆 LEVEL<br><b id="lv">1</b></div>
<div>🎯 MATCH<br><b id="mc">0/10</b></div>
<div>❤️ MOVES<br><b id="mv">30</b></div>
<div>⭐ SCORE<br><b id="sc">0</b></div>
</div>
<div id="board"></div>
<button onclick="restart()">🔄 RESTART</button>
<button onclick="levelMenu()">🗺 LEVELS</button>
<button onclick="menu()">🏠 MENU</button>
</div>
<!-- INTRO -->
<div id="intro" class="overlay">
<div class="introFruit a">🍎</div>
<div class="introFruit b">🍓</div>
<div class="introFruit c">🍇</div>
<div class="introFruit d">🍊</div>
<div id="mango">🥭</div>
<h2 style="position:absolute;bottom:12%">
GET READY! 🍬
</h2>
</div>
<!-- TUTORIAL -->
<div id="tutorialBox" class="overlay">
<div class="popup">
<div class="hand">☝️</div>
<h1>🎓 TUTORIAL</h1>
<p id="tText">3 same fruits ko match karo!</p>
<button onclick="nextTut()">NEXT ➜</button>
<button onclick="skipTut()">SKIP</button>
</div>
</div>
<!-- WIN -->
<div id="win" class="overlay">
<div class="popup pop">
<div style="font-size:65px">🎉</div>
<h1 id="winTitle">LEVEL COMPLETE!</h1>
<p id="winText"></p>
<button id="nextBtn" onclick="nextLevel()">➡ NEXT LEVEL</button>
<button onclick="restart()">🔄 RESTART</button>
<button onclick="menu()">🏠 MENU</button>
</div>
</div>
<!-- GAME OVER -->
<div id="lose" class="overlay">
<div class="popup pop">
<div style="font-size:65px">😢</div>
<h1>GAME OVER</h1>
<p id="loseText"></p>
<button onclick="restart()">🔄 RESTART</button>
<button onclick="menu()">🏠 MENU</button>
</div>
</div>
<script>
var N=8;
var fruits=["🍎","🍋","🍇","🍊","🍓","🍬"];
var board=[];
var selected=null;
var level=1;
var matches=0;
var moves=30;
var target=10;
var score=0;
var locked=false;
var unlocked=Number(localStorage.getItem("FB_UNLOCK")||1);
var music=true;
var sound=true;
var audio=null;
var musicTimer=null;
/* AUDIO */
function audioStart(){
if(!audio){
var AC=window.AudioContext||window.webkitAudioContext;
if(AC) audio=new AC();
}
if(audio&&audio.state=="suspended")audio.resume();
}
function beep(f,d){
if(!sound)return;
audioStart();
if(!audio)return;
var o=audio.createOscillator();
var g=audio.createGain();
o.frequency.value=f;
g.gain.value=.035;
o.connect(g);
g.connect(audio.destination);
o.start();
o.stop(audio.currentTime+d);
}
function startMusic(){
if(!music||musicTimer)return;
var notes=[523,659,784,659,587,698,880,698];
var i=0;
musicTimer=setInterval(function(){
if(music)beep(notes[i++%notes.length],.06);
},500);
}
function musicToggle(){
music=!music;
document.getElementById("mt").innerText=music?"ON":"OFF";
if(music){
audioStart();
startMusic();
}else{
clearInterval(musicTimer);
musicTimer=null;
}
}
function soundToggle(){
sound=!sound;
document.getElementById("st").innerText=sound?"ON":"OFF";
}
/* SCREENS */
function hide(){
document.getElementById("menu").style.display="none";
document.getElementById("levelMenu").style.display="none";
document.getElementById("game").style.display="none";
document.getElementById("intro").style.display="none";
document.getElementById("tutorialBox").style.display="none";
document.getElementById("win").style.display="none";
document.getElementById("lose").style.display="none";
}
function menu(){
hide();
document.getElementById("menu").style.display="flex";
updateUnlock();
}
function play(){
audioStart();
startMusic();
hide();
document.getElementById("game").style.display="block";
intro();
}
function intro(){
var x=document.getElementById("intro");
var m=document.getElementById("mango");
m.className="";
x.style.display="flex";
setTimeout(function(){
m.className="mangoBoom";
beep(180,.1);
beep(350,.1);
beep(600,.15);
},2200);
setTimeout(function(){
x.style.display="none";
start(1);
},2800);
}
/* TUTORIAL */
var tutStep=0;
function tutorial(){
audioStart();
startMusic();
hide();
tutStep=0;
document.getElementById("tutorialBox").style.display="flex";
}
function nextTut(){
tutStep++;
var t=document.getElementById("tText");
if(tutStep==1){
t.innerHTML="☝️ Ek fruit tap karo, phir paas wala fruit tap karo.";
}
else if(tutStep==2){
t.innerHTML="🍎🍎🍎 Teen same fruits ko line mein match karo.";
}
else if(tutStep==3){
t.innerHTML="💥 Match hone par fruits blast honge aur 1 match count hoga.";
}
else if(tutStep==4){
t.innerHTML="🎯 Level 1 mein 10 matches aur 30 moves hain.";
}
else{
skipTut();
}
}
function skipTut(){
document.getElementById("tutorialBox").style.display="none";
start(1);
}
/* LEVEL MENU */
function levelMenu(){
hide();
var box=document.getElementById("levels");
box.innerHTML="";
for(var i=1;i<=21;i++){
var b=document.createElement("button");
b.className="lvl";
if(i<=unlocked){
b.className+=" unlocked";
b.innerHTML="⭐<br>"+i;
b.onclick=(function(x){
return function(){
start(x);
};
})(i);
}else{
b.innerHTML="🔒<br>"+i;
}
box.appendChild(b);
}
document.getElementById("levelMenu").style.display="flex";
updateUnlock();
}
function updateUnlock(){
document.getElementById("unlock").innerText=
unlocked+"/21 levels unlocked";
}
/* START LEVEL */
function start(l){
hide();
level=l;
matches=0;
score=0;
selected=null;
locked=false;
if(level==1){
moves=30;
target=10;
}else{
moves=20;
target=20;
}
document.getElementById("game").style.display="block";
makeBoard();
update();
}
/* RESTART */
function restart(){
document.getElementById("win").style.display="none";
document.getElementById("lose").style.display="none";
start(level);
}
/* BOARD */
function makeBoard(){
board=[];
for(var r=0;r<N;r++){
board[r]=[];
for(var c=0;c<N;c++){
var x;
do{
x=Math.floor(Math.random()*fruits.length);
}while(
c>=2&&
board[r][c-1]==x&&
board[r][c-2]==x
||
r>=2&&
board[r-1][c]==x&&
board[r-2][c]==x
);
board[r][c]=x;
}
}
draw();
}
/* DRAW */
function draw(){
var b=document.getElementById("board");
b.innerHTML="";
for(var r=0;r<N;r++){
for(var c=0;c<N;c++){
var d=document.createElement("div");
d.className="cell";
if(selected&&selected.r==r&&selected.c==c)
d.className+=" selected";
d.innerHTML=fruits[board[r][c]];
d.onclick=(function(rr,cc){
return function(){
tap(rr,cc);
};
})(r,c);
b.appendChild(d);
}
}
}
/* TAP */
function tap(r,c){
if(locked)return;
if(!selected){
selected={r:r,c:c};
draw();
beep(650,.05);
return;
}
if(
Math.abs(selected.r-r)+
Math.abs(selected.c-c)!=1
){
selected={r:r,c:c};
draw();
return;
}
var a={r:selected.r,c:selected.c};
var b={r:r,c:c};
selected=null;
swap(a,b);
var m=find();
if(!m.length){
swap(a,b);
draw();
beep(160,.1);
return;
}
moves--;
locked=true;
draw();
setTimeout(remove,220);
}
/* SWAP */
function swap(a,b){
var x=board[a.r][a.c];
board[a.r][a.c]=board[b.r][b.c];
board[b.r][b.c]=x;
}
/* FIND MATCHES */
function find(){
var list=[];
function add(r,c){
for(var i=0;i<list.length;i++)
if(list[i].r==r&&list[i].c==c)return;
list.push({r:r,c:c});
}
for(var r=0;r<N;r++){
for(var c=0;c<N-2;c++){
var x=board[r][c];
if(
board[r][c+1]==x&&
board[r][c+2]==x
){
for(var k=c;k<N&&board[r][k]==x;k++)
add(r,k);
}
}
}
for(var c=0;c<N;c++){
for(var r=0;r<N-2;r++){
var x=board[r][c];
if(
board[r+1][c]==x&&
board[r+2][c]==x
){
for(var k=r;k<N&&board[k][c]==x;k++)
add(k,c);
}
}
}
return list;
}
/* REMOVE + ANIMATION */
function remove(){
var list=find();
if(!list.length){
locked=false;
draw();
check();
return;
}
matches++;
score+=list.length*50;
beep(700,.1);
var cells=document.querySelectorAll(".cell");
for(var i=0;i<list.length;i++){
var index=list[i].r*N+list[i].c;
if(cells[index])
cells[index].classList.add("blast");
board[list[i].r][list[i].c]=null;
}
update();
setTimeout(drop,350);
}
/* DROP */
function drop(){
for(var c=0;c<N;c++){
var w=N-1;
for(var r=N-1;r>=0;r--){
if(board[r][c]!=null){
board[w][c]=board[r][c];
if(w!=r)board[r][c]=null;
w--;
}
}
for(var r=w;r>=0;r--)
board[r][c]=Math.floor(Math.random()*fruits.length);
}
draw();
update();
setTimeout(remove,180);
}
/* CHECK */
function check(){
update();
if(matches>=target){
win();
return;
}
if(moves<=0){
lose();
}
}
/* WIN */
function win(){
if(level==unlocked&&unlocked<21){
unlocked++;
localStorage.setItem("FB_UNLOCK",unlocked);
}
beep(523,.1);
setTimeout(function(){beep(659,.1)},100);
setTimeout(function(){beep(784,.1)},200);
setTimeout(function(){beep(1047,.15)},300);
document.getElementById("game").style.display="none";
document.getElementById("win").style.display="flex";
document.getElementById("winTitle").innerText=
level==21?
"🏆 ALL LEVELS COMPLETE!":
"🎉 LEVEL "+level+" COMPLETE!";
document.getElementById("winText").innerHTML=
"🎯 Matches: "+matches+"/"+target+
"<br>❤️ Moves left: "+moves+
"<br>⭐ Score: "+score;
document.getElementById("nextBtn").style.display=
level<21?"block":"none";
}
/* NEXT */
function nextLevel(){
if(level<21){
start(level+1);
}else{
menu();
}
}
/* LOSE */
function lose(){
document.getElementById("game").style.display="none";
document.getElementById("lose").style.display="flex";
document.getElementById("loseText").innerHTML=
"🎯 Matches: "+matches+"/"+target+
"<br>❤️ Moves: 0";
}
/* UPDATE */
function update(){
document.getElementById("lv").innerText=level;
document.getElementById("mc").innerText=
matches+"/"+target;
document.getElementById("mv").innerText=moves;
document.getElementById("sc").innerText=score;
}
/* START */
updateUnlock();
startMusic();
</script>
</body>
</html>Game Source: Fruit Blast
Creator: StormBear76
Libraries: none
Complexity: complex (785 lines, 13.2 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: fruit-blast-stormbear76" to link back to the original. Then publish at arcadelab.ai/publish.