Fisch 2D
by MegaFlare512676 lines32.7 KB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Fisch 2D</title>
<style>
*{
box-sizing:border-box;
margin:0;
padding:0;
user-select:none;
-webkit-tap-highlight-color:transparent;
}
html,body{
width:100%;
height:100%;
overflow:hidden;
background:#031b2d;
font-family:Arial,sans-serif;
touch-action:none;
color:white;
}
canvas{
position:fixed;
inset:0;
width:100%;
height:100%;
}
#ui{
position:fixed;
inset:0;
pointer-events:none;
}
.top{
display:flex;
align-items:center;
gap:6px;
padding:8px;
}
.box{
background:#03182ddd;
border:1px solid #35dfff;
border-radius:13px;
padding:7px 9px;
color:white;
font-weight:bold;
font-size:13px;
white-space:nowrap;
box-shadow:0 0 15px #00cfff33;
}
.spacer{
flex:1;
}
button{
pointer-events:auto;
border:0;
border-radius:14px;
padding:10px 12px;
font-size:14px;
font-weight:bold;
background:#09baff;
color:#002033;
box-shadow:0 4px #05678c;
}
button:active{
transform:translateY(4px);
box-shadow:0 1px #05678c;
}
button:disabled{
opacity:.45;
}
#hint{
position:fixed;
top:58px;
left:50%;
transform:translateX(-50%);
background:#03182deb;
border:1px solid #39ddff;
border-radius:20px;
padding:8px 14px;
white-space:nowrap;
font-weight:bold;
font-size:13px;
}
#message{
position:fixed;
top:100px;
left:50%;
transform:translateX(-50%);
background:#061a2df5;
border:1px solid white;
border-radius:20px;
padding:9px 15px;
opacity:0;
transition:.2s;
white-space:nowrap;
font-weight:bold;
z-index:50;
}
#effects{
position:fixed;
top:140px;
left:50%;
transform:translateX(-50%);
display:flex;
gap:6px;
}
.effect{
display:none;
background:#041a2cf5;
border:1px solid #20cfff;
border-radius:14px;
padding:6px 9px;
font-size:12px;
font-weight:bold;
}
#buttons{
position:fixed;
bottom:15px;
left:0;
right:0;
display:flex;
justify-content:center;
}
#start{
font-size:20px;
padding:14px 30px;
}
#menu{
display:none;
position:fixed;
z-index:40;
left:50%;
top:50%;
transform:translate(-50%,-50%);
width:min(94vw,520px);
max-height:86vh;
overflow:auto;
background:#041a2cf9;
border:2px solid #20cfff;
border-radius:21px;
padding:16px;
box-shadow:0 0 40px #00bfff55;
pointer-events:auto;
}
#menu h2{
text-align:center;
margin-bottom:12px;
}
.tabs{
display:flex;
gap:6px;
margin-bottom:10px;
}
.tabs button{
flex:1;
}
.item{
background:#082c47;
border:1px solid #176786;
border-radius:14px;
padding:12px;
margin:8px 0;
text-align:center;
}
.fishIcon{
font-size:36px;
}
.fishName{
font-size:19px;
font-weight:bold;
margin:4px 0;
}
.small{
font-size:13px;
line-height:1.65;
}
.shopItem{
border-color:#29d8ff;
}
.buy{
width:100%;
margin-top:9px;
}
#close{
width:100%;
margin-top:8px;
background:#e76068;
color:white;
}
input{
width:100%;
margin-top:9px;
padding:11px;
border-radius:11px;
border:1px solid #25d8ff;
background:#021522;
color:white;
text-align:center;
font-size:18px;
outline:none;
}
.tokenButton{
width:100%;
margin-top:8px;
}
.cooldown{
color:#ffcf45;
font-weight:bold;
}
.stage{
display:inline-block;
padding:5px 10px;
border-radius:12px;
background:#062238;
border:1px solid #21cfff;
margin-top:6px;
}
@media(max-width:600px){
.box{
font-size:10px;
padding:6px 7px;
}
button{
font-size:12px;
padding:9px 9px;
}
#hint{
font-size:11px;
}
}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="ui">
<div class="top">
<div class="box">
⭐ Nível <span id="level">1</span>
</div>
<div class="box">
XP <span id="xp">0</span>/<span id="need">100</span>
</div>
<div class="box">
💰 R$ <span id="money">0</span>
</div>
<div class="spacer"></div>
<button id="inventory">
🎒
</button>
<button id="shop">
🏪
</button>
<button id="mutations">
🧬
</button>
</div>
<div id="hint">
🎯 TOQUE NO PEIXE!
</div>
<div id="message"></div>
<div id="effects">
<div class="effect" id="kingEffect">
👑 KING: <span></span>
</div>
<div class="effect" id="lingsEffect">
🔥 LINGS: <span></span>
</div>
<div class="effect" id="token1Effect">
🎟️ ×5: <span></span>
</div>
<div class="effect" id="token2Effect">
💀 ×10: <span></span>
</div>
</div>
<div id="buttons">
<button id="start">
▶️ COMEÇAR
</button>
</div>
</div>
<div id="menu">
<h2 id="menuTitle">
🎒 INVENTÁRIO
</h2>
<div class="tabs">
<button id="inventoryTab">
🎒 INVENTÁRIO
</button>
<button id="shopTab">
🏪 LOJA
</button>
<button id="mutationTab">
🧬 MUTAÇÕES
</button>
</div>
<div id="menuContent"></div>
<button id="close">
FECHAR
</button>
</div>
<script>
// ======================================================
// CANVAS
// ======================================================
const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");
let W=innerWidth;
let H=innerHeight;
function resize(){
W=innerWidth;
H=innerHeight;
const d=devicePixelRatio||1;
canvas.width=W*d;
canvas.height=H*d;
canvas.style.width=W+"px";
canvas.style.height=H+"px";
ctx.setTransform(d,0,0,d,0,0);
}
addEventListener("resize",resize);
resize();
// ======================================================
// SAVE
// ======================================================
let save;
try{
save=JSON.parse(
localStorage.getItem("Fisch2D_Save")
);
}catch(e){
save=null;
}
if(!save){
save={
level:1,
xp:0,
money:0,
xpMoneyProgress:0,
total:0,
caught:[]
};
}
if(typeof save.level!=="number")
save.level=1;
if(typeof save.xp!=="number")
save.xp=0;
if(typeof save.money!=="number")
save.money=0;
if(typeof save.xpMoneyProgress!=="number")
save.xpMoneyProgress=0;
if(typeof save.total!=="number")
save.total=0;
if(!Array.isArray(save.caught))
save.caught=[];
function saveGame(){
localStorage.setItem(
"Fisch2D_Save",
JSON.stringify(save)
);
}
// ======================================================
// PEIXES
// ======================================================
const fishTypes=[
{
name:"Clow",
emoji:"🐟",
xp:30,
chance:90,
value:30,
speed:6
},
{
name:"Winder",
emoji:"🐠",
xp:50,
chance:70,
value:50,
speed:7
},
{
name:"Xisper",
emoji:"🐡",
xp:70,
chance:55,
value:70,
speed:8
},
{
name:"Fhsg",
emoji:"🐟",
xp:90,
chance:35,
value:90,
speed:9
},
{
name:"Klingh",
emoji:"🐠",
xp:120,
chance:20,
value:120,
speed:10
},
{
name:"Ukitronn",
emoji:"🐡",
xp:200,
chance:1,
value:200,
speed:11
},
{
name:"Ukinato",
emoji:"🐟",
xp:300,
chance:0.002,
value:300,
speed:12
},
{
name:"Unki",
emoji:"🐠",
xp:500,
chance:0.0001,
value:500,
speed:13
},
{
name:"Rei",
emoji:"👑🐟",
xp:1000,
chance:0.0000001,
value:1000,
speed:14
}
];
// ======================================================
// NÍVEIS
// ======================================================
function xpNeeded(){
const l=save.level;
if(l>=10000)
return 3000;
if(l>=5000)
return 2000;
if(l>=2001)
return 1200;
if(l>=1001)
return 1000;
if(l>=701)
return 900;
if(l>=501)
return 830;
if(l>=201)
return 700;
if(l>=51)
return 450;
if(l>=11)
return 300;
return 100;
}
// ======================================================
// ESTÁGIO
// ======================================================
function getStage(){
if(save.level>=10000)
return "👑 FULL KETER — ESTÁGIO 2";
if(save.level>=5000)
return "🟣 KETER 3";
return "🌊 KETER";
}
// ======================================================
// XP / DINHEIRO
// ======================================================
function addXP(amount){
save.xp+=amount;
// A cada 25 XP = R$1
save.xpMoneyProgress+=amount;
const moneyEarned=
Math.floor(
save.xpMoneyProgress/25
);
if(moneyEarned>0){
save.money+=moneyEarned;
save.xpMoneyProgress%=
25;
}
let levels=0;
while(
save.xp>=xpNeeded()
){
save.xp-=xpNeeded();
save.level++;
levels++;
}
if(levels>0){
message(
"⬆️ NÍVEL "+save.level+
" — "+getStage()
);
}
updateUI();
saveGame();
}
// ======================================================
// UI
// ======================================================
function updateUI(){
document.getElementById("level")
.textContent=
save.level.toLocaleString("pt-BR");
document.getElementById("xp")
.textContent=
Math.floor(save.xp).toLocaleString("pt-BR");
document.getElementById("need")
.textContent=
xpNeeded().toLocaleString("pt-BR");
document.getElementById("money")
.textContent=
save.money.toLocaleString("pt-BR");
}
// ======================================================
// MENSAGEM
// ======================================================
function message(text){
const el=
document.getElementById("message");
el.textContent=text;
el.style.opacity=1;
clearTimeout(window.messageTimer);
window.messageTimer=
setTimeout(()=>{
el.style.opacity=0;
},1800);
}
// ======================================================
// EFEITOS
// ======================================================
let kingUntil=0;
let lingsUntil=0;
let token1Until=0;
let token2Until=0;
let token1Cooldown=0;
let token2Cooldown=0;
function active(end){
return Date.now()<end;
}
function secondsLeft(end){
return Math.max(
0,
Math.ceil(
(end-Date.now())/1000
)
);
}
function updateEffects(){
const effects=[
[
"kingEffect",
kingUntil
],
[
"lingsEffect",
lingsUntil
],
[
"token1Effect",
token1Until
],
[
"token2Effect",
token2Until
]
];
effects.forEach(([id,end])=>{
const el=
document.getElementById(id);
if(active(end)){
el.style.display="block";
el.querySelector("span")
.textContent=
secondsLeft(end)+"s";
}else{
el.style.display="none";
}
});
}
setInterval(
updateEffects,
200
);
// ======================================================
// ESCOLHER PEIXE
// ======================================================
function chooseFish(){
let weights=
fishTypes.map(f=>{
let w=f.chance;
// LINGS:
// peixes abaixo de 20 passam a ter peso 20
if(
active(lingsUntil) &&
w<20
){
w=20;
}
return w;
});
// TOKEN 1:
// durante 7 segundos,
// os peixes aparecem 5x mais
// TOKEN 2:
// durante 3 segundos,
// os peixes aparecem 10x mais
let total=
weights.reduce(
(a,b)=>a+b,
0
);
let r=
Math.random()*total;
for(
let i=0;
i<fishTypes.length;
i++
){
r-=weights[i];
if(r<=0)
return fishTypes[i];
}
return fishTypes[0];
}
// ======================================================
// TAMANHO
// ======================================================
function getFishSize(){
let r=Math.random()*100;
// KING
// 99% grande ou maior
if(active(kingUntil)){
if(r<99){
let x=Math.random()*100;
if(x<90){
return{
multiplier:2,
text:"2× GRANDE",
rarity:"GRANDE"
};
}
if(x<99){
return{
multiplier:3,
text:"3× GIGANTE",
rarity:"GIGANTE"
};
}
return{
multiplier:4,
text:"4× MEGA",
rarity:"MEGA"
};
}
}
// NORMAL
if(r<0.001){
return{
multiplier:4,
text:"4× MEGA",
rarity:"MEGA"
};
}
if(r<3.001){
return{
multiplier:3,
text:"3× GIGANTE",
rarity:"GIGANTE"
};
}
if(r<33.001){
return{
multiplier:2,
text:"2× GRANDE",
rarity:"GRANDE"
};
}
return{
multiplier:1,
text:"NORMAL",
rarity:"NORMAL"
};
}
// ======================================================
// GAME
// ======================================================
let gameRunning=false;
let activeFish=[];
let bubbles=[];
let particles=[];
let spawnTimer=0;
let waveTime=0;
// ======================================================
// BOLHAS
// ======================================================
for(let i=0;i<65;i++){
bubbles.push({
x:Math.random()*W,
y:H*.38+
Math.random()*H*.58,
r:1+
Math.random()*4,
speed:.2+
Math.random()*.8
});
}
// ======================================================
// SPAWN
// ======================================================
function spawnFish(){
if(!gameRunning)
return;
const type=
chooseFish();
const size=
getFishSize();
const fromLeft=
Math.random()<.5;
activeFish.push({
type:type,
x:
fromLeft
? -100
: W+100,
y:
H*.49+
Math.random()*H*.36,
direction:
fromLeft
? 1
: -1,
speed:
type.speed+
Math.random()*2.5,
baseSize:30,
multiplier:
size.multiplier,
sizeText:
size.text,
sizeRarity:
size.rarity,
phase:
Math.random()*10
});
}
// ======================================================
// CAPTURAR
// ======================================================
function catchFish(f){
const index=
activeFish.indexOf(f);
if(index===-1)
return;
activeFish.splice(
index,
1
);
save.caught.push({
name:f.type.name,
multiplier:f.multiplier,
xp:f.type.xp,
time:Date.now()
});
save.total++;
// XP
addXP(f.type.xp);
createParticles(
f.x,
f.y
);
let extra="";
if(f.multiplier>1){
extra=
" 🔥 "+f.sizeText;
}
message(
f.type.emoji+
" "+f.type.name+
" +"+
f.type.xp+
" XP"+
extra
);
saveGame();
updateUI();
}
// ======================================================
// TOQUE
// ======================================================
function touchGame(e){
if(!gameRunning)
return;
const rect=
canvas.getBoundingClientRect();
const x=
(e.clientX-rect.left)*
W/rect.width;
const y=
(e.clientY-rect.top)*
H/rect.height;
for(
let i=activeFish.length-1;
i>=0;
i--
){
const f=
activeFish[i];
const size=
f.baseSize*
f.multiplier;
const dx=
x-f.x;
const dy=
y-f.y;
const distance=
Math.sqrt(
dx*dx+
dy*dy
);
if(
distance<
size+28
){
catchFish(f);
return;
}
}
}
canvas.addEventListener(
"pointerdown",
touchGame
);
// ======================================================
// PARTÍCULAS
// ======================================================
function createParticles(x,y){
for(let i=0;i<18;i++){
particles.push({
x:x,
y:y,
vx:
(Math.random()-.5)*5,
vy:
(Math.random()-.5)*5,
life:1
});
}
}
// ======================================================
// START
// ======================================================
document.getElementById("start").onclick=()=>{
if(gameRunning)
return;
gameRunning=true;
document.getElementById("start")
.textContent=
"🎣 PESCANDO...";
message(
"🌊 OS PEIXES ESTÃO PASSANDO!"
);
for(let i=0;i<8;i++){
setTimeout(
spawnFish,
i*200
);
}
};
// ======================================================
// MENU
// ======================================================
const menu=
document.getElementById("menu");
const menuTitle=
document.getElementById("menuTitle");
const menuContent=
document.getElementById("menuContent");
// ======================================================
// INVENTÁRIO
// ======================================================
function openInventory(){
menu.style.display="block";
menuTitle.textContent=
"🎒 INVENTÁRIO";
const counts={};
fishTypes.forEach(f=>{
counts[f.name]={
normal:0,
big:0,
giant:0,
mega:0,
total:0
};
});
save.caught.forEach(f=>{
const name=
typeof f==="string"
? f
: f.name;
const multiplier=
typeof f==="string"
? 1
: Number(f.multiplier)||1;
if(!counts[name])
return;
counts[name].total++;
if(multiplier===1)
counts[name].normal++;
else if(multiplier===2)
counts[name].big++;
else if(multiplier===3)
counts[name].giant++;
else if(multiplier===4)
counts[name].mega++;
});
let html=`
<div class="item">
<div style="font-size:21px">
🐟 PEIXES CAPTURADOS
</div>
<br>
<b style="font-size:25px">
${save.total.toLocaleString("pt-BR")}
</b>
<br><br>
💰 R$ ${save.money.toLocaleString("pt-BR")}
<br>
<span class="stage">
${getStage()}
</span>
</div>
<div class="item">
⭐ NÍVEL
<b>${save.level.toLocaleString("pt-BR")}</b>
<br>
XP:
<b>${save.xp.toLocaleString("pt-BR")}</b>
/
${xpNeeded().toLocaleString("pt-BR")}
</div>
`;
fishTypes.forEach(f=>{
const c=
counts[f.name];
html+=`
<div class="item">
<div class="fishIcon">
${f.emoji}
</div>
<div class="fishName">
${f.name}
</div>
<div class="small">
⭐ ${f.xp} XP
<br>
🎯 Chance:
${formatChance(f.chance)}
<br><br>
📦 Total:
<b>${c.total}</b>
<br>
Normal:
${c.normal}
<br>
🔵 2× Grande:
${c.big}
<br>
🟣 3× Gigante:
${c.giant}
<br>
🟡 4× Mega:
${c.mega}
</div>
</div>
`;
});
menuContent.innerHTML=
html;
}
function formatChance(n){
if(n>=1)
return n+"%";
return n.toLocaleString(
"pt-BR",
{
maximumFractionDigits:10
}
)+"%";
}
// ======================================================
// LOJA
// ======================================================
function openShop(){
menu.style.display="block";
menuTitle.textContent=
"🏪 LOJA";
renderShop();
}
function renderShop(){
menuContent.innerHTML=`
<div class="item">
<div style="font-size:21px">
💰 SEU DINHEIRO
</div>
<br>
<b style="font-size:26px">
R$ ${save.money.toLocaleString("pt-BR")}
</b>
<br><br>
<div class="small">
💡 A cada <b>25 XP</b> ganhos:
<b>+R$1</b>
</div>
</div>
<div class="item shopItem">
<div style="font-size:42px">
👑
</div>
<div class="fishName">
KING
</div>
<div class="small">
Aumenta a chance de peixes
grandes ou maiores para
<b>99%</b>.
<br><br>
⏱️ Duração:
<b>50 segundos</b>
<br>
💰 Preço:
<b>R$30</b>
</div>
<button
class="buy"
id="buyKing"
${save.money<30?"disabled":""}
>
COMPRAR 👑 — R$30
</button>
</div>
<div class="item shopItem">
<div style="font-size:42px">
🔥
</div>
<div class="fishName">
LINGS
</div>
<div class="small">
Todos os peixes com chance
abaixo de 20% passam a ter
peso de <b>20%</b>.
<br><br>
⏱️ Duração:
<b>10 segundos</b>
<br>
💰 Preço:
<b>R$25</b>
</div>
<button
class="buy"
id="buyLings"
${save.money<25?"disabled":""}
>
COMPRAR 🔥 — R$25
</button>
</div>
<div class="item">
<b>📋 TABELA DE PEIXES</b>
<br><br>
${fishTypes.map(f=>`
<div style="
display:flex;
justify-content:space-between;
padding:7px;
border-bottom:1px solid #176786;
">
<span>
${f.emoji} ${f.name}
</span>
<span>
${f.xp} XP
</span>
</div>
`).join("")}
</div>
`;
document.getElementById(
"buyKing"
).onclick=
buyKing;
document.getElementById(
"buyLings"
).onclick=
buyLings;
}
function buyKing(){
if(save.money<30){
message(
"❌ Você precisa de R$30!"
);
return;
}
save.money-=30;
kingUntil=
Math.max(
Date.now(),
kingUntil
)+
50000;
saveGame();
updateUI();
updateEffects();
renderShop();
message(
"👑 KING ativado por 50 segundos!"
);
}
function buyLings(){
if(save.money<25){
message(
"❌ Você precisa de R$25!"
);
return;
}
save.money-=25;
lingsUntil=
Math.max(
Date.now(),
lingsUntil
)+
10000;
saveGame();
updateUI();
updateEffects();
renderShop();
message(
"🔥 LINGS ativado por 10 segundos!"
);
}
// ======================================================
// MUTAÇÕES
// ======================================================
function openMutations(){
menu.style.display="block";
menuTitle.textContent=
"🧬 MUTAÇÕES";
renderMutations();
}
function renderMutations(){
const now=Date.now();
const token1Ready=
now>=token1Cooldown;
const token2Ready=
now>=token2Cooldown;
menuContent.innerHTML=`
<div class="item">
<div style="font-size:25px">
🧬 MUTAÇÕES
</div>
<br>
<div class="small">
Digite o código correto
para ativar a mutação.
<br><br>
⚠️ Cada código possui
seu próprio tempo de espera.
</div>
</div>
<!-- TOKEN 1 -->
<div class="item shopItem">
<div style="font-size:38px">
🎟️
</div>
<div class="fishName">
TOKENS 1
</div>
<div class="small">
Código necessário.
<br><br>
🎁 <b>GRÁTIS</b>
<br>
⏱️ <b>7 segundos</b>
<br>
🐟 Peixes:
<b>×5</b>
<br>
⭐ XP:
<b>1.000</b>
<br><br>
${
token1Ready
? "🟢 PRONTO"
: "🔒 Aguarde "+
Math.ceil(
(token1Cooldown-now)/1000
)+
" segundos"
}
</div>
<input
id="tokenInput1"
inputmode="numeric"
maxlength="10"
placeholder="Código do Token 1"
>
<button
class="tokenButton"
id="tokenButton1"
${token1Ready?"":"disabled"}
>
ATIVAR TOKEN 1
</button>
</div>
<!-- TOKEN 2 -->
<div class="item shopItem">
<div style="font-size:38px">
💀
</div>
<div class="fishName">
TOKENS 2
</div>
<div class="small">
Código necessário.
<br><br>
🎁 <b>GRÁTIS</b>
<br>
⏱️ <b>3 segundos</b>
<br>
🐟 Peixes:
<b>×10</b>
<br>
⭐ XP:
<b>XP normal dos peixes</b>
<br><br>
${
token2Ready
? "🟢 PRONTO"
: "🔒 Aguarde "+
Math.ceil(
(token2Cooldown-now)/1000
)+
" segundos"
}
</div>
<input
id="tokenInput2"
inputmode="numeric"
maxlength="10"
placeholder="Código do Token 2"
>
<button
class="tokenButton"
id="tokenButton2"
${token2Ready?"":"disabled"}
>
ATIVAR TOKEN 2
</button>
</div>
<div class="item">
<b>📜 EFEITOS ATUAIS</b>
<br><br>
👑 King:
${
active(kingUntil)
? secondsLeft(kingUntil)+"s"
: "inativo"
}
<br>
🔥 Lings:
${
active(lingsUntil)
? secondsLeft(lingsUntil)+"s"
: "inativo"
}
<br>
🎟️ Token 1:
${
active(token1Until)
? secondsLeft(token1Until)+"s"
: "inativo"
}
<br>
💀 Token 2:
${
active(token2Until)
? secondsLeft(token2Until)+"s"
: "inativo"
}
</div>
`;
document.getElementById(
"tokenButton1"
).onclick=
useToken1;
document.getElementById(
"tokenButton2"
).onclick=
useToken2;
}
function useToken1(){
const input=
document.getElementById(
"tokenInput1"
);
if(Date.now()<token1Cooldown){
message(
"🔒 Token 1 ainda está bloqueado!"
);
return;
}
if(input.value!=="9373"){
message(
"❌ Código do Token 1 incorreto!"
);
return;
}
/*
Token 1:
7 segundos
peixes ×5
1000 XP
*/
token1Until=
Date.now()+7000;
token1Cooldown=
Date.now()+180000;
addXP(1000);
message(
"🎟️ TOKEN 1 ATIVADO! ×5 por 7s!"
);
saveGame();
renderMutations();
}
function useToken2(){
const input=
document.getElementById(
"tokenInput2"
);
if(Date.now()<token2Cooldown){
message(
"🔒 Token 2 ainda está bloqueado!"
);
return;
}
if(input.value!=="93783"){
message(
"❌ Código do Token 2 incorreto!"
);
return;
}
/*
Token 2:
3 segundos
peixes ×10
XP normal
*/
token2Until=
Date.now()+3000;
token2Cooldown=
Date.now()+300000;
message(
"💀 TOKEN 2 ATIVADO! ×10 por 3s!"
);
saveGame();
renderMutations();
}
// ======================================================
// BOTÕES DO MENU
// ======================================================
document.getElementById(
"inventory"
).onclick=
openInventory;
document.getElementById(
"inventoryTab"
).onclick=
openInventory;
document.getElementById(
"shop"
).onclick=
openShop;
document.getElementById(
"shopTab"
).onclick=
openShop;
document.getElementById(
"mutations"
).onclick=
openMutations;
document.getElementById(
"mutationTab"
).onclick=
openMutations;
document.getElementById(
"close"
).onclick=()=>{
menu.style.display="none";
};
// ======================================================
// LOOP DO TOKEN 1 / TOKEN 2
// ======================================================
let specialSpawnTimer=0;
function getSpawnInterval(){
if(active(token2Until))
return 2;
if(active(token1Until))
return 5;
return 24;
}
function getSpawnAmount(){
if(active(token2Until))
return 10;
if(active(token1Until))
return 5;
return 1;
}
// ======================================================
// DESENHO
// ======================================================
function draw(){
waveTime+=.02;
ctx.clearRect(
0,
0,
W,
H
);
// ==================================================
// CÉU
// ==================================================
const sky=
ctx.createLinearGradient(
0,
0,
0,
H*.4
);
sky.addColorStop(
0,
"#62d9ff"
);
sky.addColorStop(
1,
"#1688b8"
);
ctx.fillStyle=sky;
ctx.fillRect(
0,
0,
W,
H*.4
);
// ==================================================
// SOL
// ==================================================
ctx.fillStyle="#ffe58a";
ctx.beginPath();
ctx.arc(
W*.82,
H*.14,
34,
0,
Math.PI*2
);
ctx.fill();
// ==================================================
// MAR
// ==================================================
const sea=
ctx.createLinearGradient(
0,
H*.35,
0,
H
);
sea.addColorStop(
0,
"#08a8d7"
);
sea.addColorStop(
.45,
"#0575ae"
);
sea.addColorStop(
1,
"#022f55"
);
ctx.fillStyle=sea;
ctx.fillRect(
0,
H*.35,
W,
H*.65
);
// ==================================================
// ONDAS
// ==================================================
ctx.strokeStyle="#a0f4ff88";
ctx.lineWidth=2;
for(
let y=H*.36;
y<H*.43;
y+=12
){
ctx.beginPath();
for(
let x=0;
x<=W;
x+=18
){
const yy=
y+
Math.sin(
x*.025+
waveTime*2+
y
)*3;
if(x===0)
ctx.moveTo(x,yy);
else
ctx.lineTo(x,yy);
}
ctx.stroke();
}
// ==================================================
// ILHA
// ==================================================
ctx.fillStyle="#d5a45a";
ctx.beginPath();
ctx.ellipse(
W*.13,
H*.39,
100,
24,
0,
0,
Math.PI*2
);
ctx.fill();
ctx.fillStyle="#29964e";
for(let i=0;i<5;i++){
ctx.beginPath();
ctx.arc(
W*.06+i*25,
H*.35,
22,
0,
Math.PI*2
);
ctx.fill();
}
// ==================================================
// BOLHAS
// ==================================================
bubbles.forEach(b=>{
b.y-=b.speed;
if(b.y<H*.37){
b.y=
H*.95+
Math.random()*50;
}
ctx.strokeStyle="#b9f6ff88";
ctx.lineWidth=1;
ctx.beginPath();
ctx.arc(
b.x,
b.y,
b.r,
0,
Math.PI*2
);
ctx.stroke();
});
// ==================================================
// PEIXES
// ==================================================
activeFish.forEach(f=>{
f.x+=
f.speed*
f.direction;
f.y+=
Math.sin(
waveTime*3+
f.phase
)*.35;
const size=
f.baseSize*
f.multiplier;
ctx.save();
ctx.translate(
f.x,
f.y
);
if(f.direction<0){
ctx.scale(
-1,
1
);
}
// BRILHO
if(f.multiplier===4){
ctx.shadowBlur=28;
ctx.shadowColor="#ffd83d";
}
else if(f.multiplier===3){
ctx.shadowBlur=20;
ctx.shadowColor="#bd6cff";
}
else if(f.multiplier===2){
ctx.shadowBlur=14;
ctx.shadowColor="#39ddff";
}
ctx.font=
size+
"px Arial";
ctx.textAlign="center";
ctx.textBaseline="middle";
ctx.fillText(
f.type.emoji,
0,
0
);
ctx.shadowBlur=0;
// NOME
ctx.font=
Math.max(
10,
size*.25
)+
"px Arial";
ctx.fillStyle="#ffffff";
ctx.fillText(
f.type.name,
0,
size*.62
);
// TAMANHO
if(f.multiplier>1){
ctx.font=
Math.max(
10,
size*.27
)+
"px Arial";
ctx.fillStyle=
f.multiplier===4
? "#ffd83d"
: f.multiplier===3
? "#d59aff"
: "#7deaff";
ctx.fillText(
f.sizeText,
0,
-size*.78
);
}
ctx.restore();
});
// ==================================================
// REMOVER PEIXES
// ==================================================
activeFish=
activeFish.filter(f=>
f.x>-170 &&
f.x<W+170
);
// ==================================================
// SPAWN
// ==================================================
if(gameRunning){
spawnTimer++;
const interval=
getSpawnInterval();
if(spawnTimer>=interval){
spawnTimer=0;
const amount=
getSpawnAmount();
for(
let i=0;
i<amount;
i++
){
spawnFish();
}
}
}
// ==================================================
// PARTÍCULAS
// ==================================================
particles=
particles.filter(p=>{
p.x+=p.vx;
p.y+=p.vy;
p.vy+=.08;
p.life-=.035;
ctx.globalAlpha=
Math.max(
0,
p.life
);
ctx.fillStyle="#fff";
ctx.beginPath();
ctx.arc(
p.x,
p.y,
4,
0,
Math.PI*2
);
ctx.fill();
ctx.globalAlpha=1;
return p.life>0;
});
requestAnimationFrame(draw);
}
draw();
updateUI();
updateEffects();
</script>
</body>
</html>Game Source: Fisch 2D
Creator: MegaFlare51
Libraries: none
Complexity: complex (2676 lines, 32.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: fisch-2d-megaflare51" to link back to the original. Then publish at arcadelab.ai/publish.