🎮ArcadeLab

Хз

by GoldenBear87
1323 lines19.7 KB
▶ Play
<script>

/* =========================================
   300 MONET — ЧАСТЬ 3
========================================= */


/* =========================================
   РАЗЛЁТ МОНЕТ
========================================= */

function coinBurst(sourceCoin){

  if(!running || ending)return;

  if(sourceCoin.dataset.busy==="1")return;

  sourceCoin.dataset.busy="1";
  sourceCoin.classList.add("taken");

  removeLockSignForCoin(sourceCoin);

  playSound(COIN_SOUND);

  const rect =
    sourceCoin.getBoundingClientRect();

  const count = 6;

  for(let i=0;i<count;i++){

    const burst =
      document.createElement("img");

    burst.className="burstCoin";
    burst.src=GOLD;

    burst.onerror=function(){
      fallbackCoin(burst);
    };

    burst.style.left =
      (rect.left + rect.width/2 - 12)+"px";

    burst.style.top =
      (rect.top + rect.height/2 - 12)+"px";

    document.body.appendChild(burst);

    const angle =
      (Math.PI*2/count)*i +
      (Math.random()-.5)*.35;

    const distance =
      45 + Math.random()*55;

    const targetX =
      rect.left +
      rect.width/2 +
      Math.cos(angle)*distance;

    const targetY =
      rect.top +
      rect.height/2 +
      Math.sin(angle)*distance;

    requestAnimationFrame(function(){

      burst.style.left =
        (targetX-12)+"px";

      burst.style.top =
        (targetY-12)+"px";

    });


    /* Каждая монета начинает полёт
       немного позже предыдущей */

    setTimeout(function(){

      if(!running || ending){

        burst.remove();
        return;

      }

      const counter =
        counterIcon.getBoundingClientRect();

      playSound(COIN_FLY_SOUND);

      burst.classList.add("flyToCounter");

      burst.style.left =
        (counter.left + counter.width/2 - 12)+"px";

      burst.style.top =
        (counter.top + counter.height/2 - 12)+"px";

    },180+i*80);


    /* Каждая монета отдельно
       добавляет +1 */

    setTimeout(function(){

      burst.remove();

      if(!running || ending)return;

      money++;

      /* ВАЖНО:
         текст НЕ увеличивается,
         просто меняется число */

      updateMoney(false);

    },730+i*80);

  }


  setTimeout(function(){

    sourceCoin.remove();

  },160);

}


/* =========================================
   УДАЛЕНИЕ ЗНАЧКА БЛОКИРОВКИ
========================================= */

function removeLockSignForCoin(coin){

  const id =
    coin.dataset.lock;

  if(!id)return;

  document
    .querySelectorAll(".lockSign")
    .forEach(function(sign){

      if(sign.dataset.coin===id){

        sign.remove();

      }

    });

}


/* =========================================
   A-90
========================================= */

function spawnVirus(){

  if(!running || ending)return;

  const virus =
    document.createElement("img");

  virus.className="virus";
  virus.src=VIRUS;

  virus.onerror=function(){

    fallbackVirus(virus);

  };

  const pos =
    randomPosition();

  virus.style.left =
    pos.x+"px";

  virus.style.top =
    pos.y+"px";


  virus.addEventListener(
    "click",
    function(e){

      e.preventDefault();
      e.stopPropagation();

      if(virus.dataset.hit==="1")return;

      virus.dataset.hit="1";

      playSound(VIRUS_SOUND);

      money =
        Math.max(0,money-5);

      updateMoney(false);

      showMinus(
        "-5",
        e.clientX,
        e.clientY
      );

      game.classList.remove(
        "screenGlitch"
      );

      void game.offsetWidth;

      game.classList.add(
        "screenGlitch"
      );

      glitchOverlay.style.display=
        "block";

      virus.classList.add(
        "glitch"
      );


      setTimeout(function(){

        glitchOverlay.style.display=
          "none";

        virus.src=VIRUS_AFTER;

        virus.onerror=function(){

          fallbackGlitch(virus);

        };

      },180);


      setTimeout(function(){

        virus.remove();

      },650);

    }
  );

  field.appendChild(virus);

}


/* =========================================
   ГЛЮЧНАЯ МОНЕТА
========================================= */

function spawnGlitchCoin(){

  if(!running || ending)return;

  const coin =
    document.createElement("img");

  coin.className="glitchCoin";
  coin.src=VIRUS_AFTER;

  coin.onerror=function(){

    fallbackGlitch(coin);

  };

  const pos =
    randomPosition();

  coin.style.left =
    pos.x+"px";

  coin.style.top =
    pos.y+"px";


  coin.addEventListener(
    "click",
    function(e){

      e.preventDefault();
      e.stopPropagation();

      if(coin.dataset.hit==="1")return;

      coin.dataset.hit="1";

      coin.classList.add("hit");

      playSound(COIN_SOUND);

      money+=3;

      updateMoney(false);

      showGlitchText(
        "+3",
        e.clientX,
        e.clientY
      );

      setTimeout(function(){

        coin.remove();

      },550);

    }
  );

  field.appendChild(coin);

}


/* =========================================
   ГОРШОК
========================================= */

function spawnMoneyPot(){

  if(!running || ending)return;

  const pot =
    document.createElement("img");

  pot.className="moneyPot";
  pot.src=MONEY_POT;

  pot.onerror=function(){

    fallbackPot(pot);

  };

  const pos =
    randomPosition();

  pot.style.left =
    pos.x+"px";

  pot.style.top =
    pos.y+"px";


  pot.addEventListener(
    "click",
    function(e){

      e.preventDefault();
      e.stopPropagation();

      if(pot.dataset.opened==="1")return;

      pot.dataset.opened="1";

      pot.classList.add("opened");

      const rect =
        pot.getBoundingClientRect();

      const counter =
        counterIcon.getBoundingClientRect();

      playSound(COIN_SOUND);

      showPotText(
        "+30",
        rect.left + rect.width/2,
        rect.top
      );


      for(let i=0;i<30;i++){

        const coin =
          document.createElement("img");

        coin.className="potCoin";
        coin.src=GOLD;

        coin.onerror=function(){

          fallbackCoin(coin);

        };

        coin.style.left =
          (rect.left + rect.width/2 - 14)+"px";

        coin.style.top =
          (rect.top + rect.height/2 - 14)+"px";

        document.body.appendChild(coin);


        setTimeout(function(){

          coin.style.left =
            (counter.left + counter.width/2 - 14)+"px";

          coin.style.top =
            (counter.top + counter.height/2 - 14)+"px";

          coin.classList.add("fly");

        },i*25);


        setTimeout(function(){

          coin.remove();

          if(running && !ending){

            money++;

            updateMoney(false);

          }

        },700+i*25);

      }


      setTimeout(function(){

        pot.remove();

      },450);

    }
  );

  field.appendChild(pot);

}


/* =========================================
   FALLBACK ГОРШКА
========================================= */

function fallbackPot(img){

  img.src =
    "data:image/svg+xml;charset=utf-8,"+
    encodeURIComponent(`
    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="0 0 100 100">

      <ellipse cx="50" cy="35"
        rx="31" ry="12"
        fill="#6b3518"
        stroke="#d8944b"
        stroke-width="5"/>

      <path d="M20 35 L27 80
        Q50 94 73 80 L80 35"
        fill="#8b4d20"
        stroke="#d8944b"
        stroke-width="5"/>

      <circle cx="38" cy="30"
        r="7"
        fill="#ffd34d"/>

      <circle cx="53" cy="27"
        r="7"
        fill="#ffd34d"/>

      <circle cx="66" cy="32"
        r="7"
        fill="#ffd34d"/>

    </svg>`);

}


/* =========================================
   ТЕКСТОВЫЕ ЭФФЕКТЫ
========================================= */

function showMinus(text,x,y){

  const element =
    document.createElement("div");

  element.id="minus";
  element.textContent=text;

  element.style.left =
    (x-15)+"px";

  element.style.top =
    (y-15)+"px";

  document.body.appendChild(element);

  setTimeout(function(){

    element.remove();

  },850);

}


function showGlitchText(text,x,y){

  const element =
    document.createElement("div");

  element.id="glitchText";
  element.textContent=text;

  element.style.left =
    (x-15)+"px";

  element.style.top =
    (y-15)+"px";

  document.body.appendChild(element);

  setTimeout(function(){

    element.remove();

  },850);

}


function showPotText(text,x,y){

  const element =
    document.createElement("div");

  element.id="potText";
  element.textContent=text;

  element.style.left =
    (x-20)+"px";

  element.style.top =
    (y-10)+"px";

  document.body.appendChild(element);

  setTimeout(function(){

    element.remove();

  },850);

}


/* =========================================
   БЛОКИРОВКА МОНЕТ
========================================= */

function lockCoins(){

  field
    .querySelectorAll(".coin")
    .forEach(function(coin){

      if(coin.classList.contains("taken"))
        return;

      coin.classList.add("locked");

      const sign =
        document.createElement("img");

      sign.className="lockSign";
      sign.src=A90_SIGN;

      sign.onerror=function(){

        fallbackVirus(sign);

      };

      const rect =
        coin.getBoundingClientRect();

      sign.style.left =
        (rect.left + rect.width/2 - 19)+"px";

      sign.style.top =
        (rect.top + rect.height/2 - 19)+"px";

      const id =
        Math.random()
          .toString(36)
          .slice(2);

      sign.dataset.coin=id;
      coin.dataset.lock=id;

      document.body.appendChild(sign);

    });

}


/* =========================================
   ОКНО ТРАТЫ
========================================= */

function openSpend(){

  if(!running || ending)return;

  spendOverlay.style.display="flex";

  spendImage.src=A90;

  lockCoins();

}


function closeSpend(){

  spendOverlay.style.display="none";

  document
    .querySelectorAll(".lockSign")
    .forEach(function(sign){

      sign.remove();

    });

  field
    .querySelectorAll(".coin.locked")
    .forEach(function(coin){

      coin.classList.remove("locked");

    });

}


function buyCoins(){

  if(money<100)return;

  money-=100;
  money+=10;

  updateMoney(false);

  closeSpend();

}


/* =========================================
   СОЗДАНИЕ НАЧАЛЬНЫХ МОНЕТ
========================================= */

function spawnManyCoins(amount){

  for(let i=0;i<amount;i++){

    setTimeout(function(){

      if(running && !ending){

        spawnCoin();

      }

    },i*90);

  }

}


/* =========================================
   АВТОМАТИЧЕСКОЕ ПОЯВЛЕНИЕ МОНЕТ
========================================= */

function startCoinSpawning(){

  clearInterval(coinAutoTimer);

  coinAutoTimer =
    setInterval(function(){

      if(running && !ending){

        spawnCoin();

      }

    },coinEggMode ? 350 : 800);

}


/* =========================================
   МОНЕТНЫЙ ДОЖДЬ
========================================= */

function startCoinRain(){

  clearInterval(coinRainInterval);

  coinRainInterval =
    setInterval(function(){

      if(running && coinEggMode){

        for(let i=0;i<3;i++){

          spawnCoin();

        }

      }

    },700);

}


/* =========================================
   ТАЙМЕРЫ ОПАСНОСТЕЙ
========================================= */

function startTrapTimers(){

  clearInterval(virusTimer);
  clearInterval(glitchCoinTimer);
  clearInterval(potTimer);

  const difficulty =
    difficulties[selectedDifficulty];

  virusTimer =
    setInterval(function(){

      if(running && !ending){

        spawnVirus();

      }

    },difficulty.virusTime);


  glitchCoinTimer =
    setInterval(function(){

      if(running && !ending){

        spawnGlitchCoin();

      }

    },difficulty.glitchTime);


  potTimer =
    setInterval(function(){

      if(running && !ending){

        spawnMoneyPot();

      }

    },15000);

}


/* =========================================
   ТАЙМЕР ИГРЫ
========================================= */

function startTimer(){

  const started =
    Date.now();

  clearInterval(timer);

  timer =
    setInterval(function(){

      if(!running)return;

      const elapsed =
        (Date.now()-started)/1000;

      time =
        Math.max(
          0,
          60-elapsed
        );

      timerText.textContent =
        time.toFixed(1);

      if(time<=0){

        clearInterval(timer);

        finishGame(false);

      }

    },100);

}


/* =========================================
   ОЧИСТКА ТАЙМЕРОВ
========================================= */

function clearGameTimers(){

  clearInterval(timer);
  clearInterval(virusTimer);
  clearInterval(glitchCoinTimer);
  clearInterval(coinRainInterval);
  clearInterval(spendTimer);
  clearInterval(coinAutoTimer);
  clearInterval(potTimer);

  timer=null;
  virusTimer=null;
  glitchCoinTimer=null;
  coinRainInterval=null;
  spendTimer=null;
  coinAutoTimer=null;
  potTimer=null;

}


/* =========================================
   ЗАПУСК ИГРЫ
========================================= */

function startGame(){

  if(running)return;

  running=true;
  ending=false;

  money=0;
  time=60;

  clearGameTimers();

  startScreen.style.display="none";
  difficultyMenu.style.display="none";
  mission.style.display="none";
  message.style.display="none";
  spendOverlay.style.display="none";
  glitchOverlay.style.display="none";

  field.style.display="block";
  hud.style.display="flex";

  field.innerHTML="";

  document
    .querySelectorAll(
      ".burstCoin,.potCoin,.lockSign,#minus,#glitchText,#potText"
    )
    .forEach(function(element){

      element.remove();

    });

  updateMoney(false);

  timerText.textContent="60.0";

  spawnManyCoins(
    coinEggMode ? 8 : 5
  );

  startTimer();
  startCoinSpawning();
  startTrapTimers();

  if(coinEggMode){

    startCoinRain();

  }

}


/* =========================================
   КОНЕЦ ИГРЫ
========================================= */

function finishGame(won){

  if(ending)return;

  ending=true;
  running=false;

  clearGameTimers();

  spendOverlay.style.display="none";
  glitchOverlay.style.display="none";

  document
    .querySelectorAll(".lockSign")
    .forEach(function(sign){

      sign.remove();

    });

  field
    .querySelectorAll(".coin.locked")
    .forEach(function(coin){

      coin.classList.remove("locked");

    });


  if(won){

    messageTitle.textContent =
      "ТЫ СОБРАЛ 300!";

    messageText.textContent =
      "Задание выполнено. Монет найдено: "+
      money+".";

  }else{

    messageTitle.textContent =
      "ВРЕМЯ ВЫШЛО";

    messageText.textContent =
      "Ты собрал "+
      money+
      " из 300 монет.";

  }

  message.style.display="flex";

}


/* =========================================
   ВОЗВРАТ В МЕНЮ
========================================= */

function returnToMenu(){

  clearGameTimers();

  running=false;
  ending=false;

  spendOverlay.style.display="none";
  message.style.display="none";
  difficultyMenu.style.display="none";
  mission.style.display="none";
  hud.style.display="none";
  field.style.display="none";
  glitchOverlay.style.display="none";

  field.innerHTML="";

  document
    .querySelectorAll(
      ".burstCoin,.potCoin,.lockSign,#minus,#glitchText,#potText"
    )
    .forEach(function(element){

      element.remove();

    });

  startScreen.style.display="flex";

}


/* =========================================
   МОБИЛЬНЫЕ КНОПКИ
========================================= */

function bindTap(element,handler){

  let lastTouch=0;

  element.addEventListener(
    "touchend",
    function(event){

      event.preventDefault();
      event.stopPropagation();

      lastTouch=Date.now();

      handler(event);

    },
    {passive:false}
  );


  element.addEventListener(
    "click",
    function(event){

      if(Date.now()-lastTouch<700)
        return;

      event.preventDefault();
      event.stopPropagation();

      handler(event);

    }
  );

}


/* =========================================
   ПРИВЯЗКА КНОПОК
========================================= */

function bindAllButtons(){

  bindTap(
    difficultyBtn,
    function(){

      if(!running){

        difficultyMenu.style.display=
          "flex";

      }

    }
  );


  bindTap(
    closeDifficulty,
    function(){

      difficultyMenu.style.display=
        "none";

    }
  );


  document
    .querySelectorAll(".difficultyOption")
    .forEach(function(button){

      bindTap(
        button,
        function(){

          selectedDifficulty =
            button.dataset.difficulty;

          document
            .querySelectorAll(
              ".difficultyOption"
            )
            .forEach(function(item){

              item.classList.remove(
                "selected"
              );

            });

          button.classList.add(
            "selected"
          );

          difficultyName.textContent =
            difficulties[
              selectedDifficulty
            ].name;

          difficultyMenu.style.display=
            "none";

        }
      );

    });


  bindTap(
    startBtn,
    function(){

      if(running)return;

      coinEggMode=false;

      missionText.innerHTML =
        "Найди мне <b>300 монет</b>.";

      missionTime.innerHTML =
        "У тебя есть <b>60 секунд</b>.";

      difficultyInfo.textContent =
        difficulties[
          selectedDifficulty
        ].info;

      mission.style.display="flex";

    }
  );


  bindTap(
    coinEggBtn,
    function(){

      if(running)return;

      coinEggMode=true;

      missionText.innerHTML =
        "Насыпь мне <b>300 монет</b>.";

      missionTime.innerHTML =
        "У тебя есть <b>60 секунд</b>.";

      difficultyInfo.textContent =
        "ПАСХАЛЬНЫЙ РЕЖИМ: МОНЕТНЫЙ ДОЖДЬ.";

      mission.style.display="flex";

    }
  );


  bindTap(
    searchBtn,
    function(){

      startGame();

    }
  );


  bindTap(
    buyCoinsBtn,
    function(){

      buyCoins();

    }
  );


  bindTap(
    closeSpendBtn,
    function(){

      closeSpend();

    }
  );


  bindTap(
    againBtn,
    function(){

      message.style.display="none";

      startGame();

    }
  );


  bindTap(
    menuBtn,
    function(){

      returnToMenu();

    }
  );

}


/* =========================================
   ЗАПУСК ОБРАБОТЧИКОВ
========================================= */

let buttonsBound=false;

function safeBindAllButtons(){

  if(buttonsBound)return;

  buttonsBound=true;

  bindAllButtons();

}


/* =========================================
   DOM READY
========================================= */

document.addEventListener(
  "DOMContentLoaded",
  function(){

    safeBindAllButtons();

    counterIcon.src=GOLD;
    spendImage.src=A90;

  }
);


/* =========================================
   ЕСЛИ DOM УЖЕ ГОТОВ
========================================= */

if(document.readyState!=="loading"){

  safeBindAllButtons();

}


/* =========================================
   ЗАЩИТА ОТ КОНТЕКСТНОГО МЕНЮ
========================================= */

document.addEventListener(
  "contextmenu",
  function(event){

    event.preventDefault();

  }
);


/* =========================================
   ЗАЩИТА ОТ ВЫДЕЛЕНИЯ
========================================= */

document.addEventListener(
  "selectstart",
  function(event){

    event.preventDefault();

  }
);


/* =========================================
   КОНЕЦ ИГРЫ
========================================= */

</script>

</body>
</html>

Game Source: Хз

Creator: GoldenBear87

Libraries: none

Complexity: complex (1323 lines, 19.7 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: game-goldenbear87-mug5sf9b" to link back to the original. Then publish at arcadelab.ai/publish.