🎮ArcadeLab

Fruit Blast

by StormBear76
785 lines13.2 KB
▶ Play
<!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.