Advanced aquarium with fish schools, feeding, night mode, treasure chests, and interactive fish
// ==UserScript==
// @name Ultra Aquarium
// @namespace https://tampermonkey.net/
// @version 5.7
// @description Advanced aquarium with fish schools, feeding, night mode, treasure chests, and interactive fish
// @author Mustafa Hakan
// @license MIT
// @match http://*/*
// @match https://*/*
// @grant none
// @run-at document-end
// @noframes
// ==/UserScript==
(function() {
'use strict';
const SPECIES = [
{name:'Clownfish', emoji:'🐠', speed:1.2, size:35, color:'#ff6b35'},
{name:'Angelfish', emoji:'🐟', speed:0.8, size:40, color:'#ffd700'},
{name:'Pufferfish', emoji:'🐡', speed:0.5, size:45, color:'#00ff87'},
{name:'Goldfish', emoji:'🐠', speed:0.7, size:30, color:'#ff4444'},
{name:'Stingray', emoji:'🦈', speed:1.5, size:55, color:'#888888'},
{name:'Seahorse', emoji:'🦑', speed:0.3, size:25, color:'#ff69b4'},
{name:'Betta', emoji:'🐟', speed:0.6, size:32, color:'#7c3aed'},
{name:'Neon Tetra', emoji:'🐠', speed:1.4, size:22, color:'#00e5ff'},
{name:'Butterfly', emoji:'🐟', speed:0.9, size:38, color:'#ff8c00'},
{name:'Lanternfish',emoji:'🐡', speed:0.4, size:42, color:'#ffff00'},
{name:'Princess', emoji:'🐠', speed:1.1, size:28, color:'#f472b6'},
{name:'Dragonfish', emoji:'🦈', speed:1.8, size:60, color:'#dc2626'}
];
const AQUARIUM_HEIGHT = 180;
let fishes = [], plants = [], bubbles = [], foodParticles = [];
let canvas, ctx, aquariumWidth, aquariumHeight = AQUARIUM_HEIGHT;
let mouseX = -100, mouseY = -100;
let selectedFish = null, nightMode = false, feeding = false;
let animFrameId;
function rand(min, max) { return Math.random() * (max - min) + min; }
function randInt(min, max) { return Math.floor(rand(min, max)); }
class Fish {
constructor(species) {
Object.assign(this, species);
this.x = rand(50, aquariumWidth - 50);
this.y = rand(30, aquariumHeight - 60);
this.vx = rand(-1, 1) * species.speed;
this.vy = rand(-0.5, 0.5) * species.speed;
this.angle = 0;
this.hunger = randInt(0, 100);
this.happiness = randInt(50, 100);
this.bubbleTimer = randInt(0, 200);
this.born = Date.now();
this.direction = 1;
}
update() {
this.bubbleTimer++;
if (this.bubbleTimer > randInt(80, 200)) {
this.angle = rand(-0.5, 0.5);
this.bubbleTimer = 0;
}
if (this.vx !== 0) {
this.direction = this.vx > 0 ? 1 : -1;
}
if (feeding && foodParticles.length > 0) {
let nearest = null;
let minDist = Infinity;
for (const p of foodParticles) {
const d = Math.hypot(p.x - this.x, p.y - this.y);
if (d < minDist) { minDist = d; nearest = p; }
}
if (nearest && minDist < 150) {
this.vx += (nearest.x - this.x) * 0.02;
this.vy += (nearest.y - this.y) * 0.02;
if (minDist < 10) {
nearest.eaten = true;
this.hunger = Math.max(0, this.hunger - 10);
this.happiness = Math.min(100, this.happiness + 5);
}
}
}
if (Math.abs(mouseX - this.x) < 100 && Math.abs(mouseY - this.y) < 100 && mouseX > 0) {
this.vx += (this.x - mouseX) * 0.03;
this.vy += (this.y - mouseY) * 0.03;
}
this.vx += rand(-0.05, 0.05);
this.vy += rand(-0.05, 0.05);
this.vx *= 0.98;
this.vy *= 0.98;
const speed = Math.hypot(this.vx, this.vy);
const maxSpeed = this.speed * (this.hunger < 30 ? 1.5 : 1);
if (speed > maxSpeed) {
this.vx = (this.vx / speed) * maxSpeed;
this.vy = (this.vy / speed) * maxSpeed;
}
this.x += this.vx;
this.y += this.vy;
if (this.x < 10) { this.x = 10; this.vx *= -1; }
if (this.x > aquariumWidth - 10) { this.x = aquariumWidth - 10; this.vx *= -1; }
if (this.y < 10) { this.y = 10; this.vy *= -1; }
if (this.y > aquariumHeight - 60) { this.y = aquariumHeight - 60; this.vy *= -1; }
this.hunger = Math.min(100, this.hunger + 0.02);
if (this.hunger > 80) {
this.happiness = Math.max(0, this.happiness - 0.05);
}
if (this.bubbleTimer > randInt(80, 200)) {
bubbles.push({
x: this.x + rand(-5, 5),
y: this.y - this.size / 2,
radius: rand(2, 6),
speed: rand(0.3, 1.2),
wobble: rand(0, Math.PI * 2),
wobbleSpeed: rand(0.01, 0.03),
opacity: rand(0.3, 0.7)
});
this.bubbleTimer = 0;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
if (this.direction < 0) {
ctx.scale(-1, 1);
}
ctx.font = this.size + 'px serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.shadowColor = nightMode ? this.color : 'rgba(255,255,255,0.3)';
ctx.shadowBlur = nightMode ? 15 : 5;
ctx.fillText(this.emoji, 0, 0);
if (selectedFish === this) {
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(0, 0, this.size / 2 + 5, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = '#ffffff';
ctx.font = '10px sans-serif';
ctx.fillText(this.name, 0, -this.size / 2 - 15);
}
ctx.restore();
}
}
function getRandomSpecies() {
return SPECIES[Math.floor(Math.random() * SPECIES.length)];
}
function addFish() {
fishes.push(new Fish(getRandomSpecies()));
}
function addPlant() {
plants.push({
x: rand(30, aquariumWidth - 30),
height: rand(40, 120),
width: rand(4, 8),
segments: randInt(3, 7),
wave: rand(0, Math.PI * 2),
waveSpeed: rand(0.005, 0.02),
waveAmplitude: rand(5, 20),
color: 'hsl(' + randInt(120, 160) + ', ' + randInt(40, 70) + '%, ' + randInt(20, 40) + '%)'
});
}
function feedFish() {
if (feeding) return;
feeding = true;
let count = 0;
const interval = setInterval(function() {
if (count >= 30 || !feeding) {
clearInterval(interval);
return;
}
foodParticles.push({
x: rand(50, aquariumWidth - 50),
y: 0,
vy: rand(0.2, 0.8),
vx: rand(-0.3, 0.3),
size: rand(2, 4),
color: 'hsl(' + randInt(35, 50) + ', 100%, ' + randInt(40, 70) + '%)',
eaten: false
});
count++;
}, 50);
setTimeout(function() { feeding = false; }, 5000);
}
function toggleNight() {
nightMode = !nightMode;
const tank = document.getElementById('aq-tank');
if (tank) {
tank.style.background = nightMode ?
'linear-gradient(180deg, rgba(0,5,20,0.93), #000510)' :
'linear-gradient(180deg, rgba(10,61,98,0.87), #0a3d62)';
}
}
function showFishList() {
let list = 'AQUARIUM INHABITANTS:\n\n';
fishes.forEach(function(f, i) {
const ageMin = Math.floor((Date.now() - f.born) / 60000);
list += (i + 1) + '. ' + f.emoji + ' ' + f.name + ' | Hunger: ' + Math.floor(f.hunger) + '% | Happy: ' + Math.floor(f.happiness) + '% | Age: ' + ageMin + 'min\n';
});
list += '\nTotal: ' + fishes.length + ' fish | Plants: ' + plants.length;
alert(list);
}
function closeTank() {
cancelAnimationFrame(animFrameId);
const tank = document.getElementById('aq-tank');
if (tank) tank.remove();
}
function drawBackground() {
const gradient = ctx.createLinearGradient(0, 0, 0, aquariumHeight);
if (nightMode) {
gradient.addColorStop(0, 'rgba(0,10,30,0.4)');
gradient.addColorStop(1, 'rgba(0,5,20,0.6)');
} else {
gradient.addColorStop(0, 'rgba(10,61,98,0.1)');
gradient.addColorStop(1, 'rgba(10,61,98,0.3)');
}
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, aquariumWidth, aquariumHeight);
ctx.fillStyle = '#f6d365';
ctx.beginPath();
ctx.moveTo(0, aquariumHeight - 15);
for (let x = 0; x < aquariumWidth; x += 20) {
ctx.lineTo(x, aquariumHeight - 15 + Math.sin(x * 0.02) * 5);
}
ctx.lineTo(aquariumWidth, aquariumHeight);
ctx.lineTo(0, aquariumHeight);
ctx.fill();
if (!nightMode) {
for (let i = 0; i < 5; i++) {
ctx.fillStyle = 'rgba(255,255,200,0.04)';
ctx.beginPath();
const lx = aquariumWidth * (i / 5) + rand(-20, 20);
ctx.moveTo(lx, 0);
ctx.lineTo(lx + rand(-30, 30), aquariumHeight);
ctx.lineTo(lx + rand(20, 50), aquariumHeight);
ctx.lineTo(lx + rand(10, 30), 0);
ctx.fill();
}
}
if (nightMode) {
for (let j = 0; j < 20; j++) {
ctx.fillStyle = 'rgba(255,255,255,' + rand(0.1, 0.5) + ')';
ctx.beginPath();
ctx.arc(rand(0, aquariumWidth), rand(0, aquariumHeight), rand(0.5, 1.5), 0, Math.PI * 2);
ctx.fill();
}
}
}
function drawPlants() {
for (const p of plants) {
p.wave += p.waveSpeed;
ctx.save();
ctx.strokeStyle = p.color;
ctx.lineWidth = p.width;
ctx.lineCap = 'round';
ctx.globalAlpha = 0.8;
ctx.beginPath();
ctx.moveTo(p.x, aquariumHeight - 20);
for (let i = 1; i <= p.segments; i++) {
const t = i / p.segments;
ctx.lineTo(
p.x + Math.sin(p.wave + t * 3) * p.waveAmplitude * t,
aquariumHeight - 20 - p.height * t
);
}
ctx.stroke();
ctx.restore();
}
}
function drawBubbles() {
bubbles = bubbles.filter(function(b) { return b.y > -10 && b.radius > 0.5; });
for (const b of bubbles) {
b.y -= b.speed;
b.x += Math.sin(b.wobble) * 0.3;
b.wobble += b.wobbleSpeed;
b.radius *= 0.999;
ctx.save();
ctx.globalAlpha = b.opacity;
ctx.fillStyle = '#63b3ed';
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
function drawFood() {
foodParticles = foodParticles.filter(function(p) { return !p.eaten && p.y < aquariumHeight; });
for (const p of foodParticles) {
p.y += p.vy;
p.x += p.vx;
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
}
}
function animate() {
ctx.clearRect(0, 0, aquariumWidth, aquariumHeight);
drawBackground();
drawPlants();
drawBubbles();
drawFood();
for (const f of fishes) f.update();
fishes.sort(function(a, b) { return a.y - b.y; });
for (const f of fishes) f.draw(ctx);
animFrameId = requestAnimationFrame(animate);
}
function createToolbar(tank) {
const toolbar = document.createElement('div');
toolbar.style.cssText = 'position: absolute; top: 8px; right: 10px; display: flex; gap: 5px; z-index: 2147483641; user-select: none;';
const buttons = [
{ icon: '🍽️', title: 'Feed Fish', action: feedFish },
{ icon: '➕', title: 'Add Fish', action: addFish },
{ icon: '🌿', title: 'Add Plant', action: addPlant },
{ icon: '🌙', title: 'Toggle Night Mode', action: toggleNight },
{ icon: '📊', title: 'Fish List', action: showFishList },
{ icon: '❌', title: 'Close Aquarium', action: closeTank }
];
for (const btn of buttons) {
const button = document.createElement('button');
button.textContent = btn.icon;
button.title = btn.title;
button.style.cssText = 'width: 30px; height: 30px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.3); background: rgba(0,0,0,0.5); color: #fff; cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s;';
button.addEventListener('mouseenter', function() {
button.style.background = 'rgba(255,255,255,0.2)';
button.style.transform = 'scale(1.1)';
});
button.addEventListener('mouseleave', function() {
button.style.background = 'rgba(0,0,0,0.5)';
button.style.transform = 'scale(1)';
});
button.addEventListener('click', btn.action);
toolbar.appendChild(button);
}
return toolbar;
}
function handleResize() {
const tank = document.getElementById('aq-tank');
if (!tank || !canvas) return;
aquariumWidth = canvas.width = tank.offsetWidth;
}
function init() {
if (document.getElementById('aq-tank')) return;
const tank = document.createElement('div');
tank.id = 'aq-tank';
tank.style.cssText = 'position: fixed; left: 0; right: 0; bottom: 0; height: ' + AQUARIUM_HEIGHT + 'px; z-index: 2147483640; background: linear-gradient(180deg, rgba(10,61,98,0.87), #0a3d62); border-top: 3px solid rgba(255,255,255,0.2); box-shadow: 0 -10px 30px rgba(0,0,0,0.5); font-family: system-ui, sans-serif;';
canvas = document.createElement('canvas');
canvas.style.cssText = 'width: 100%; height: 100%; display: block;';
tank.appendChild(canvas);
const toolbar = createToolbar(tank);
tank.appendChild(toolbar);
document.body.appendChild(tank);
aquariumWidth = canvas.width = tank.offsetWidth;
aquariumHeight = canvas.height = AQUARIUM_HEIGHT;
ctx = canvas.getContext('2d');
window.addEventListener('resize', handleResize);
canvas.addEventListener('mousemove', function(e) {
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
});
canvas.addEventListener('mouseleave', function() {
mouseX = -100;
mouseY = -100;
});
canvas.addEventListener('click', function(e) {
const rect = canvas.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const clickY = e.clientY - rect.top;
selectedFish = null;
for (const f of fishes) {
if (Math.hypot(f.x - clickX, f.y - clickY) < f.size / 2 + 5) {
selectedFish = f;
break;
}
}
});
for (let i = 0; i < 8; i++) addFish();
for (let j = 0; j < 6; j++) addPlant();
animate();
}
setTimeout(init, 1000);
})();