Spaces:
Running
Running
Update index.html
Browse files- index.html +1084 -19
index.html
CHANGED
|
@@ -1,19 +1,1084 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Deep Sea Breakout - Stable Version</title>
|
| 7 |
+
<!-- <script src="https://cdn.tailwindcss.com"></script> Removed for simplicity, add back if needed -->
|
| 8 |
+
<style>
|
| 9 |
+
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Orbitron:wght@400;700&display=swap');
|
| 10 |
+
|
| 11 |
+
:root {
|
| 12 |
+
--game-width: 800px;
|
| 13 |
+
--game-height: 600px;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
body {
|
| 17 |
+
margin: 0; padding: 0;
|
| 18 |
+
background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
|
| 19 |
+
display: flex; justify-content: center; align-items: center;
|
| 20 |
+
min-height: 100vh; font-family: 'Orbitron', sans-serif;
|
| 21 |
+
overflow: hidden; color: #e0f7ff;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
.stars-bg {
|
| 25 |
+
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 26 |
+
z-index: -1;
|
| 27 |
+
}
|
| 28 |
+
.stars-bg::before, .stars-bg::after {
|
| 29 |
+
content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
|
| 30 |
+
background-image:
|
| 31 |
+
radial-gradient(1px 1px at 20% 30%, white, rgba(255,255,255,0)),
|
| 32 |
+
radial-gradient(1px 1px at 50% 70%, white, rgba(255,255,255,0)),
|
| 33 |
+
radial-gradient(2px 2px at 80% 40%, white, rgba(255,255,255,0)),
|
| 34 |
+
radial-gradient(1px 1px at 10% 90%, white, rgba(255,255,255,0)),
|
| 35 |
+
radial-gradient(2px 2px at 90% 10%, white, rgba(255,255,255,0));
|
| 36 |
+
background-size: 200px 200px; animation: twinkle 15s linear infinite alternate; opacity: 0;
|
| 37 |
+
}
|
| 38 |
+
.stars-bg::after { background-size: 300px 300px; animation-delay: -7.5s; }
|
| 39 |
+
|
| 40 |
+
@keyframes twinkle {
|
| 41 |
+
0% { opacity: 0.1; transform: translateY(0px); }
|
| 42 |
+
50% { opacity: 0.7; transform: translateY(-5px); }
|
| 43 |
+
100% { opacity: 0.1; transform: translateY(0px); }
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.game-container {
|
| 47 |
+
position: relative;
|
| 48 |
+
width: var(--game-width);
|
| 49 |
+
height: var(--game-height);
|
| 50 |
+
border: 3px solid #0ff; border-radius: 12px;
|
| 51 |
+
box-shadow: 0 0 30px rgba(0, 255, 255, 0.5), 0 0 10px rgba(0,255,255,0.7) inset;
|
| 52 |
+
background: rgba(0, 10, 20, 0.85); overflow: hidden;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
canvas#gameCanvas {
|
| 56 |
+
display: block; width: 100%; height: 100%;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
.ui {
|
| 60 |
+
position: absolute; top: 10px; left: 10px; right: 10px;
|
| 61 |
+
display: flex; justify-content: space-between;
|
| 62 |
+
color: #0ff; font-size: calc(var(--game-height) * 0.03);
|
| 63 |
+
font-weight: bold; text-shadow: 0 0 10px rgba(0, 255, 255, 0.8);
|
| 64 |
+
pointer-events: none; font-family: 'Orbitron', sans-serif; z-index: 3;
|
| 65 |
+
}
|
| 66 |
+
.ui > div {
|
| 67 |
+
background: rgba(0, 40, 80, 0.6);
|
| 68 |
+
padding: calc(var(--game-height) * 0.008) calc(var(--game-height) * 0.025);
|
| 69 |
+
border-radius: 20px; border: 1px solid rgba(0, 255, 255, 0.3);
|
| 70 |
+
margin: 0 3px; white-space: nowrap;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.dialog-box, .menu, .settings-panel, .leaderboard, .shop, .daily-challenge,
|
| 74 |
+
.achievement-system, .tutorial-screen, .stats-screen, .credits, .customization,
|
| 75 |
+
.profile, .multiplayer-info, .matchmaking, .matchmaking-invite, .matchmaking-scoreboard,
|
| 76 |
+
#difficultySelectScreen /* Explicitly target this if it's a dialog */
|
| 77 |
+
{
|
| 78 |
+
position: absolute; top: 50%; left: 50%;
|
| 79 |
+
transform: translate(-50%, -50%);
|
| 80 |
+
background: rgba(0, 5, 15, 0.97); color: #0ff;
|
| 81 |
+
padding: clamp(20px, 4vh, 30px); border-radius: 15px;
|
| 82 |
+
text-align: center; border: 2px solid #0ff;
|
| 83 |
+
box-shadow: 0 0 30px rgba(0, 255, 255, 0.8), 0 0 20px #0af inset;
|
| 84 |
+
z-index: 20; width: 85%; max-width: 500px;
|
| 85 |
+
backdrop-filter: blur(4px); display: none;
|
| 86 |
+
max-height: 85vh; overflow-y: auto;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/* Titles for all dialogs */
|
| 90 |
+
.dialog-box h1, .dialog-box h2, .menu-title, .settings-header, .leaderboard-title,
|
| 91 |
+
.shop-title, .daily-challenge-title, .achievement-system-title, .tutorial-screen-title,
|
| 92 |
+
.stats-screen-title, .credits-title, .customization-title, .profile-title,
|
| 93 |
+
.multiplayer-title, .matchmaking-title, .matchmaking-scoreboard-title,
|
| 94 |
+
#difficultySelectScreen h2 { /* Target specific h2 for difficulty screen */
|
| 95 |
+
font-family: 'Press Start 2P', cursive;
|
| 96 |
+
font-size: clamp(20px, 3.5vw, 28px);
|
| 97 |
+
margin-bottom: 20px; text-shadow: 0 0 10px #0ff; color: #7ff;
|
| 98 |
+
}
|
| 99 |
+
.dialog-box p {
|
| 100 |
+
font-size: clamp(14px, 2.2vw, 16px); margin-bottom: 20px; line-height: 1.5;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
.start-screen { display: flex; flex-direction: column; align-items: center; }
|
| 104 |
+
|
| 105 |
+
.pause-message {
|
| 106 |
+
position: absolute; top: 50%; left: 50%;
|
| 107 |
+
transform: translate(-50%, -50%); color: #f0f;
|
| 108 |
+
font-size: clamp(40px, 8vw, 60px); font-weight: bold;
|
| 109 |
+
text-shadow: 0 0 15px #f0f, 0 0 30px #f0f; pointer-events: none;
|
| 110 |
+
display: none; font-family: 'Press Start 2P', cursive; z-index: 100;
|
| 111 |
+
animation: pulse 1.5s infinite alternate;
|
| 112 |
+
}
|
| 113 |
+
@keyframes pulse {
|
| 114 |
+
from { opacity: 0.6; transform: translate(-50%, -50%) scale(1); }
|
| 115 |
+
to { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
.powerup-indicator {
|
| 119 |
+
position: absolute; bottom: calc(var(--game-height) * 0.08);
|
| 120 |
+
right: 10px; display: flex; flex-direction: column; gap: 8px; z-index: 5;
|
| 121 |
+
}
|
| 122 |
+
.powerup-icon {
|
| 123 |
+
width: clamp(30px, 4vw, 40px); height: clamp(30px, 4vw, 40px);
|
| 124 |
+
background: rgba(0, 40, 80, 0.8); border-radius: 50%;
|
| 125 |
+
display: flex; align-items: center; justify-content: center;
|
| 126 |
+
font-weight: bold; font-size: clamp(12px, 2vw, 16px);
|
| 127 |
+
border: 1px solid rgba(0, 255, 255, 0.6); position: relative; color: #fff;
|
| 128 |
+
box-shadow: 0 0 8px rgba(0,255,255,0.4);
|
| 129 |
+
}
|
| 130 |
+
.powerup-icon::after {
|
| 131 |
+
content: ''; position: absolute; bottom: -4px; left: 50%; transform: translateX(-50%);
|
| 132 |
+
height: 5px; background: #0f0; border-radius: 2px;
|
| 133 |
+
transition: width 0.1s linear; width: var(--time-left, 100%);
|
| 134 |
+
}
|
| 135 |
+
.powerup-icon.rare::before {
|
| 136 |
+
content: ''; position: absolute; inset: -3px; border-radius: 50%;
|
| 137 |
+
border: 2px solid gold; animation: rareGlow 1s infinite alternate; opacity: 0.9;
|
| 138 |
+
}
|
| 139 |
+
@keyframes rareGlow {
|
| 140 |
+
from { box-shadow: 0 0 6px gold, 0 0 12px yellow; }
|
| 141 |
+
to { box-shadow: 0 0 12px gold, 0 0 18px yellow; }
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
.button, .menu-button, .sound-button, .save-button, .item-button, .challenge-button,
|
| 145 |
+
.profile-button, .matchmaking-button, .matchmaking-control-button, .matchmaking-invite-button,
|
| 146 |
+
.theme-button {
|
| 147 |
+
background: linear-gradient(45deg, #0077aa, #00aadd);
|
| 148 |
+
border: 1px solid #00ccff; color: white;
|
| 149 |
+
padding: clamp(10px, 1.8vh, 12px) clamp(18px, 2.5vw, 24px);
|
| 150 |
+
margin: 8px 5px; border-radius: 8px; cursor: pointer;
|
| 151 |
+
font-size: clamp(13px, 2.2vw, 16px);
|
| 152 |
+
transition: all 0.25s ease-out;
|
| 153 |
+
box-shadow: 0 4px 12px rgba(0, 180, 220, 0.3), 0 0 4px rgba(0,255,255,0.4) inset;
|
| 154 |
+
font-family: 'Orbitron', sans-serif; text-transform: uppercase;
|
| 155 |
+
letter-spacing: 0.5px; position: relative; overflow: hidden;
|
| 156 |
+
}
|
| 157 |
+
.button::before, .menu-button::before, .sound-button::before, .save-button::before,
|
| 158 |
+
.item-button::before, .challenge-button::before, .profile-button::before,
|
| 159 |
+
.matchmaking-button::before, .matchmaking-control-button::before, .matchmaking-invite-button::before,
|
| 160 |
+
.theme-button::before {
|
| 161 |
+
content: ''; position: absolute; top: -50%; left: -150%; width: 50%; height: 200%;
|
| 162 |
+
background: linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0) 100%);
|
| 163 |
+
transform: skewX(-25deg); transition: left 0.5s ease-in-out;
|
| 164 |
+
}
|
| 165 |
+
.button:hover, .menu-button:hover, .sound-button:hover, .save-button:hover,
|
| 166 |
+
.item-button:hover, .challenge-button:hover, .profile-button:hover,
|
| 167 |
+
.matchmaking-button:hover, .matchmaking-control-button:hover, .matchmaking-invite-button:hover,
|
| 168 |
+
.theme-button:hover {
|
| 169 |
+
transform: translateY(-2px) scale(1.02);
|
| 170 |
+
box-shadow: 0 6px 18px rgba(0, 255, 255, 0.6), 0 0 8px rgba(128,255,255,0.6) inset;
|
| 171 |
+
background: linear-gradient(45deg, #0088bb, #00bbff);
|
| 172 |
+
}
|
| 173 |
+
.button:hover::before, .menu-button:hover::before, .sound-button:hover::before,
|
| 174 |
+
.save-button:hover::before, .item-button:hover::before, .challenge-button:hover::before,
|
| 175 |
+
.profile-button:hover::before, .matchmaking-button:hover::before, .matchmaking-control-button:hover::before,
|
| 176 |
+
.matchmaking-invite-button:hover::before, .theme-button:hover::before { left: 150%; }
|
| 177 |
+
|
| 178 |
+
.button:active, .menu-button:active, .sound-button:active, .save-button:active,
|
| 179 |
+
.item-button:active, .challenge-button:active, .profile-button:active,
|
| 180 |
+
.matchmaking-button:active, .matchmaking-control-button:active, .matchmaking-invite-button:active,
|
| 181 |
+
.theme-button:active { transform: translateY(0px) scale(0.98); }
|
| 182 |
+
|
| 183 |
+
.instructions {
|
| 184 |
+
background: rgba(0, 20, 40, 0.7); padding: 15px; border-radius: 10px;
|
| 185 |
+
margin-top: 20px; border: 1px solid rgba(0, 255, 255, 0.3);
|
| 186 |
+
font-size: clamp(12px, 2vw, 14px);
|
| 187 |
+
}
|
| 188 |
+
.controls {
|
| 189 |
+
position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%);
|
| 190 |
+
color: #7ff; font-size: clamp(10px, 1.8vw, 12px); text-align: center;
|
| 191 |
+
width: 90%; max-width: 450px; background: rgba(0, 20, 40, 0.8);
|
| 192 |
+
padding: 6px; border-radius: 20px; border: 1px solid rgba(0, 255, 255, 0.4);
|
| 193 |
+
z-index: 5;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.combo-meter {
|
| 197 |
+
position: absolute; top: 45%; left: 50%;
|
| 198 |
+
transform: translate(-50%, -50%) scale(0.8);
|
| 199 |
+
font-size: clamp(30px, 7vw, 45px); font-weight: bold; color: #ff0;
|
| 200 |
+
text-shadow: 0 0 10px #ff0, 0 0 20px #f90, 3px 3px 0px #a50;
|
| 201 |
+
opacity: 0; pointer-events: none; z-index: 4;
|
| 202 |
+
font-family: 'Press Start 2P', cursive;
|
| 203 |
+
transition: opacity 0.3s ease-out, transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
.achievement-popup, .notification {
|
| 207 |
+
position: fixed; top: 20px; right: 20px;
|
| 208 |
+
background: rgba(10, 30, 50, 0.9); color: #fff;
|
| 209 |
+
padding: 15px 20px; border-radius: 10px; border: 2px solid #0ff;
|
| 210 |
+
box-shadow: 0 0 15px rgba(0, 255, 255, 0.5); font-family: 'Orbitron', sans-serif;
|
| 211 |
+
z-index: 100; opacity: 0; transform: translateX(100%);
|
| 212 |
+
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
| 213 |
+
width: auto; max-width: 300px;
|
| 214 |
+
}
|
| 215 |
+
.achievement-popup.show, .notification.show { opacity: 1; transform: translateX(0); }
|
| 216 |
+
.achievement-icon, .notification-icon {
|
| 217 |
+
width: 30px; height: 30px; background: #0ff; border-radius: 50%;
|
| 218 |
+
display: flex; align-items: center; justify-content: center;
|
| 219 |
+
float: left; margin-right: 10px; font-size: 16px; color: #036;
|
| 220 |
+
}
|
| 221 |
+
.achievement-title, .notification-title { font-weight: bold; font-size: 16px; margin-bottom: 5px; color: #0ff; }
|
| 222 |
+
.achievement-description, .notification-description { font-size: 12px; color: #ccc; }
|
| 223 |
+
|
| 224 |
+
.level-transition, .boss-intro, .boss-defeated, .matchmaking-result, .loading-screen {
|
| 225 |
+
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 226 |
+
background: rgba(0, 5, 10, 0.9); display: flex; flex-direction: column;
|
| 227 |
+
align-items: center; justify-content: center;
|
| 228 |
+
font-family: 'Press Start 2P', cursive; font-size: clamp(30px, 6vw, 48px);
|
| 229 |
+
color: #0ff; z-index: 1000; opacity: 0; visibility: hidden;
|
| 230 |
+
transition: opacity 0.4s ease, visibility 0.4s;
|
| 231 |
+
}
|
| 232 |
+
.level-transition.active, .boss-intro.active, .boss-defeated.active,
|
| 233 |
+
.matchmaking-result.active, .loading-screen:not(.hidden) { opacity: 1; visibility: visible; }
|
| 234 |
+
|
| 235 |
+
.level-number, .boss-name-large, .boss-defeated-text, .matchmaking-result-text { animation: popIn 0.8s ease-in-out; }
|
| 236 |
+
@keyframes popIn {
|
| 237 |
+
0% { transform: scale(0.3) rotate(-15deg); opacity: 0; }
|
| 238 |
+
60% { transform: scale(1.1) rotate(5deg); opacity: 1; }
|
| 239 |
+
100% { transform: scale(1) rotate(0deg); opacity: 1; }
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
.health-bar {
|
| 243 |
+
position: absolute; top: calc(var(--game-height) * 0.03); left: calc(var(--game-height) * 0.025);
|
| 244 |
+
width: clamp(100px, 20vw, 150px); height: calc(var(--game-height) * 0.02);
|
| 245 |
+
background: rgba(100, 0, 0, 0.5); border: 1px solid #f00;
|
| 246 |
+
border-radius: 5px; overflow: hidden; z-index: 3;
|
| 247 |
+
}
|
| 248 |
+
.health-fill {
|
| 249 |
+
height: 100%; width: 100%;
|
| 250 |
+
background: linear-gradient(to right, #ff3333, #ff9933);
|
| 251 |
+
border-radius: 4px 0 0 4px;
|
| 252 |
+
transition: width 0.3s ease-in-out;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
.glow-border::before {
|
| 256 |
+
content: ""; position: absolute; top: -3px; left: -3px; right: -3px; bottom: -3px;
|
| 257 |
+
border-radius: 14px;
|
| 258 |
+
background: repeating-linear-gradient( 45deg,
|
| 259 |
+
rgba(0,255,255,0) 0%, rgba(0,255,255,0) 5%,
|
| 260 |
+
rgba(0,255,255,0.2) 10%, rgba(0,255,255,0.2) 15%,
|
| 261 |
+
rgba(0,255,255,0) 20%, rgba(0,255,255,0) 100%
|
| 262 |
+
);
|
| 263 |
+
background-size: 200% 200%;
|
| 264 |
+
animation: glowBorderMove 4s linear infinite;
|
| 265 |
+
pointer-events: none; z-index: 0;
|
| 266 |
+
}
|
| 267 |
+
@keyframes glowBorderMove {
|
| 268 |
+
0% { background-position: 0% 0%; }
|
| 269 |
+
100% { background-position: -200% -200%; }
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
.vignette {
|
| 273 |
+
position: absolute; top:0; left:0; width:100%; height:100%;
|
| 274 |
+
pointer-events:none; box-shadow: inset 0 0 100px rgba(0,0,0,0.6);
|
| 275 |
+
z-index:1;
|
| 276 |
+
}
|
| 277 |
+
.radial-glow {
|
| 278 |
+
position: absolute; top:0; left:0; width:100%; height:100%;
|
| 279 |
+
background: radial-gradient(ellipse at center, rgba(0,128,255,0.08) 0%, rgba(0,0,0,0) 70%);
|
| 280 |
+
pointer-events:none; z-index:0;
|
| 281 |
+
}
|
| 282 |
+
.water-effect {
|
| 283 |
+
position: absolute; bottom:0; left:0; width:100%; height:15%;
|
| 284 |
+
background: linear-gradient(to top, rgba(0,100,150,0.15), transparent);
|
| 285 |
+
pointer-events:none; z-index:0; overflow: hidden;
|
| 286 |
+
}
|
| 287 |
+
.water-effect::after {
|
| 288 |
+
content:""; position: absolute; top: 0; left: -50%; width: 200%; height: 100%;
|
| 289 |
+
background: repeating-linear-gradient(0deg,
|
| 290 |
+
rgba(100,200,255,0.05) 0px, rgba(100,200,255,0.05) 1px,
|
| 291 |
+
transparent 1px, transparent 15px);
|
| 292 |
+
animation: waterRipple 8s linear infinite;
|
| 293 |
+
}
|
| 294 |
+
@keyframes waterRipple {
|
| 295 |
+
0% { transform: translateY(0%) translateX(0%); }
|
| 296 |
+
50% { transform: translateY(-10%) translateX(2%); }
|
| 297 |
+
100% { transform: translateY(0%) translateX(0%); }
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.depth-meter {
|
| 301 |
+
position: absolute; top: calc(var(--game-height) * 0.1); right: 15px;
|
| 302 |
+
width: calc(var(--game-height) * 0.03); height: calc(var(--game-height) * 0.3);
|
| 303 |
+
background: rgba(0,20,40,0.7); border: 2px solid #0ff; border-radius: 10px;
|
| 304 |
+
overflow: hidden; z-index:3;
|
| 305 |
+
}
|
| 306 |
+
.depth-indicator {
|
| 307 |
+
position:absolute; bottom:0; left:0; width:100%; height:0%;
|
| 308 |
+
background: linear-gradient(to top, #00BFFF, #00FFFF);
|
| 309 |
+
transition: height 0.5s ease-out;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
.level-progress {
|
| 313 |
+
position: absolute; bottom: calc(var(--game-height) * 0.08);
|
| 314 |
+
left: 10px; width: clamp(100px, 18vw, 150px); height: calc(var(--game-height) * 0.025);
|
| 315 |
+
background: rgba(0,20,40,0.7); border: 1px solid #0ff; border-radius: 10px;
|
| 316 |
+
overflow: hidden; z-index:3;
|
| 317 |
+
}
|
| 318 |
+
.level-progress-bar {
|
| 319 |
+
height:100%; width:0%;
|
| 320 |
+
background: linear-gradient(to right, #00FFFF, #00BFFF);
|
| 321 |
+
border-radius: 9px 0 0 9px;
|
| 322 |
+
transition: width 0.3s ease-out;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
.boss-health-container {
|
| 326 |
+
position: absolute; top: calc(var(--game-height) * 0.03); left: 50%;
|
| 327 |
+
transform: translateX(-50%); width: 60%; max-width: 400px;
|
| 328 |
+
height: calc(var(--game-height) * 0.025);
|
| 329 |
+
background: rgba(100,0,0,0.5); border: 2px solid #f00;
|
| 330 |
+
border-radius: 10px; z-index:3; display: none;
|
| 331 |
+
}
|
| 332 |
+
.boss-health-bar {
|
| 333 |
+
height:100%; width:100%;
|
| 334 |
+
background: linear-gradient(to right, #ff3333, #ff9933);
|
| 335 |
+
border-radius: 8px 0 0 8px;
|
| 336 |
+
transition: width 0.3s ease-out;
|
| 337 |
+
}
|
| 338 |
+
.boss-name {
|
| 339 |
+
position: absolute; top: calc(var(--game-height) * 0.03 + var(--game-height) * 0.025 + 5px);
|
| 340 |
+
left: 50%; transform: translateX(-50%);
|
| 341 |
+
color: #f33; font-size: clamp(12px, 2vw, 14px); font-weight: bold;
|
| 342 |
+
text-shadow: 0 0 5px #f00; z-index:3; display:none;
|
| 343 |
+
background-color: rgba(0,0,0,0.5); padding: 2px 8px; border-radius: 5px;
|
| 344 |
+
}
|
| 345 |
+
.boss-warning {
|
| 346 |
+
position: absolute; top: calc(var(--game-height) * 0.1); left: 50%;
|
| 347 |
+
transform: translateX(-50%); color: #f00; font-size: clamp(16px, 3vw, 20px);
|
| 348 |
+
font-weight: bold; text-shadow: 0 0 8px #f00; z-index:5; display:none;
|
| 349 |
+
animation: pulseWarning 1s infinite alternate;
|
| 350 |
+
}
|
| 351 |
+
@keyframes pulseWarning { from { opacity: 0.7; } to { opacity: 1; } }
|
| 352 |
+
|
| 353 |
+
.tutorial {
|
| 354 |
+
position: absolute; bottom: calc(var(--game-height) * 0.15); left: 50%;
|
| 355 |
+
transform: translateX(-50%); background: rgba(0,10,20,0.85);
|
| 356 |
+
padding: 10px 15px; border-radius: 8px; border: 1px solid #0ff;
|
| 357 |
+
font-size: clamp(12px, 2vw, 14px); color: #9ff; z-index:10;
|
| 358 |
+
opacity:0; transition: opacity 0.5s, transform 0.5s; pointer-events: none;
|
| 359 |
+
}
|
| 360 |
+
.tutorial.show { opacity:1; transform: translateX(-50%) translateY(-10px); }
|
| 361 |
+
|
| 362 |
+
.slider-container {
|
| 363 |
+
width: 60%; background: rgba(0,40,80,0.6); border-radius: 10px;
|
| 364 |
+
padding: 5px; border: 1px solid rgba(0,255,255,0.3);
|
| 365 |
+
}
|
| 366 |
+
input[type="range"].volume-slider {
|
| 367 |
+
width: 100%; height: 8px; background: rgba(0,255,255,0.2);
|
| 368 |
+
border-radius: 5px; outline: none; -webkit-appearance: none; appearance: none;
|
| 369 |
+
}
|
| 370 |
+
input[type="range"].volume-slider::-webkit-slider-thumb {
|
| 371 |
+
-webkit-appearance: none; appearance: none;
|
| 372 |
+
width: 18px; height: 18px; border-radius: 50%;
|
| 373 |
+
background: #0ff; cursor: pointer;
|
| 374 |
+
box-shadow: 0 0 5px #0ff;
|
| 375 |
+
}
|
| 376 |
+
input[type="range"].volume-slider::-moz-range-thumb {
|
| 377 |
+
width: 18px; height: 18px; border-radius: 50%;
|
| 378 |
+
background: #0ff; cursor: pointer; border: none;
|
| 379 |
+
box-shadow: 0 0 5px #0ff;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
.loading-bar {
|
| 383 |
+
width: 60%; max-width: 300px; height: 15px;
|
| 384 |
+
background: rgba(0,40,80,0.7); border-radius: 10px;
|
| 385 |
+
margin-top: 20px; overflow:hidden; border: 1px solid #08a;
|
| 386 |
+
}
|
| 387 |
+
.loading-progress {
|
| 388 |
+
height:100%; width:0%;
|
| 389 |
+
background: linear-gradient(to right, #0ff, #0aa);
|
| 390 |
+
transition: width 0.2s ease-out; border-radius: 9px 0 0 9px;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
.leaderboard-table { width: 100%; border-collapse: collapse; }
|
| 394 |
+
.leaderboard-table th, .leaderboard-table td {
|
| 395 |
+
padding: 8px; border-bottom: 1px solid rgba(0,255,255,0.2);
|
| 396 |
+
font-size: clamp(12px, 2vw, 14px);
|
| 397 |
+
}
|
| 398 |
+
.leaderboard-table th { background:rgba(0,40,80,0.6); text-transform: uppercase; }
|
| 399 |
+
.leaderboard-table tr:last-child td { border-bottom:none; }
|
| 400 |
+
.leaderboard-table tr:nth-child(even) { background-color: rgba(0,30,60,0.3); }
|
| 401 |
+
.leaderboard-table td:first-child { width: 10%; text-align: center; }
|
| 402 |
+
.leaderboard-table td:last-child { text-align: right; }
|
| 403 |
+
|
| 404 |
+
.skin-selector { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; justify-content: center;}
|
| 405 |
+
.skin {
|
| 406 |
+
width: 50px; height: 25px;
|
| 407 |
+
border-radius: 5px; display: flex; align-items: center; justify-content: center;
|
| 408 |
+
cursor: pointer; transition: all 0.2s; border: 2px solid transparent;
|
| 409 |
+
}
|
| 410 |
+
.skin.selected { transform: scale(1.1); border-color: #f0f; box-shadow: 0 0 10px #f0f; }
|
| 411 |
+
.skin span { font-size: 10px; color: white; text-shadow: 1px 1px 1px black;} /* For skin name preview if needed */
|
| 412 |
+
|
| 413 |
+
.dialog-box .button-group { display: flex; justify-content: center; gap: 10px; margin-top: 15px; flex-wrap: wrap;}
|
| 414 |
+
.placeholder-content p { margin-top: 15px; color: #789; font-style: italic; }
|
| 415 |
+
|
| 416 |
+
/* All other specific panel styles from user's HTML like .shop, .daily-challenge, etc. are fine */
|
| 417 |
+
/* The .matchmaking-game-score styles are extremely numerous and won't be used functionally */
|
| 418 |
+
|
| 419 |
+
</style>
|
| 420 |
+
</head>
|
| 421 |
+
<body>
|
| 422 |
+
<div class="stars-bg"></div>
|
| 423 |
+
<div class="game-container">
|
| 424 |
+
<div class="glow-border"></div>
|
| 425 |
+
<div class="radial-glow"></div>
|
| 426 |
+
<canvas id="gameCanvas"></canvas>
|
| 427 |
+
|
| 428 |
+
<div class="ui">
|
| 429 |
+
<div class="health-bar" id="healthBarContainer"><div class="health-fill" id="healthFill"></div></div>
|
| 430 |
+
<div>Score: <span id="score">0</span></div>
|
| 431 |
+
<div>Lvl: <span id="level">1</span>/<span id="maxLevels">10</span></div>
|
| 432 |
+
<div>Combo: <span id="combo">0x</span></div>
|
| 433 |
+
</div>
|
| 434 |
+
|
| 435 |
+
<div class="depth-meter"><div class="depth-indicator" id="depthIndicator"></div></div>
|
| 436 |
+
<div class="level-progress"><div class="level-progress-bar" id="levelProgressBar"></div></div>
|
| 437 |
+
|
| 438 |
+
<div class="boss-health-container" id="bossHealthContainer">
|
| 439 |
+
<div class="boss-health-bar" id="bossHealthBar"></div>
|
| 440 |
+
</div>
|
| 441 |
+
<div class="boss-name" id="bossName">BOSS NAME</div>
|
| 442 |
+
<div class="boss-warning" id="bossWarning">!!! BOSS APPROACHING !!!</div>
|
| 443 |
+
|
| 444 |
+
<div class="powerup-indicator" id="powerupIndicator"></div>
|
| 445 |
+
|
| 446 |
+
<div class="dialog-box start-screen" id="mainMenu">
|
| 447 |
+
<h1>DEEP SEA BREAKOUT</h1>
|
| 448 |
+
<p>Navigate treacherous depths, clear coral, and face menacing bosses. Collect power-ups to survive!</p>
|
| 449 |
+
<div class="button-group">
|
| 450 |
+
<button class="button" onclick="showDifficultySelect()">Start Game</button>
|
| 451 |
+
<button class="button" onclick="showScreen('settingsPanel')">Settings</button>
|
| 452 |
+
</div>
|
| 453 |
+
<div class="button-group">
|
| 454 |
+
<button class="button" onclick="showScreen('leaderboardScreen')">Leaderboard</button>
|
| 455 |
+
<button class="button" onclick="showScreen('tutorialScreenContent')">How to Play</button>
|
| 456 |
+
</div>
|
| 457 |
+
<div class="button-group">
|
| 458 |
+
<button class="button" onclick="showScreen('statsScreen')">Player Stats</button>
|
| 459 |
+
<button class="button" onclick="showScreen('customizationScreen')">Customize</button>
|
| 460 |
+
</div>
|
| 461 |
+
<div class="button-group">
|
| 462 |
+
<button class="button" onclick="showScreen('achievementsScreen')">Achievements</button>
|
| 463 |
+
<button class="button" onclick="showScreen('creditsScreen')">Credits</button>
|
| 464 |
+
</div>
|
| 465 |
+
<div class="button-group">
|
| 466 |
+
<button class="button" onclick="showScreenWithPlaceholder('shopScreen', 'Item Shop')">Shop</button>
|
| 467 |
+
<button class="button" onclick="showScreenWithPlaceholder('dailyChallengeScreen', 'Daily Challenges')">Challenges</button>
|
| 468 |
+
</div>
|
| 469 |
+
<div class="instructions" style="margin-top:15px; font-size: 12px;">
|
| 470 |
+
<p><strong>Quick Controls:</strong> Mouse: Move | Click: Launch/Laser | P: Pause | M: Mute</p>
|
| 471 |
+
</div>
|
| 472 |
+
</div>
|
| 473 |
+
|
| 474 |
+
<div class="dialog-box" id="difficultySelectScreen" style="display: none;">
|
| 475 |
+
<h2>Select Difficulty</h2>
|
| 476 |
+
<div class="button-group">
|
| 477 |
+
<button class="button" onclick="initGame('normal')">Normal</button>
|
| 478 |
+
<button class="button" onclick="initGame('hardcore')">Hardcore</button>
|
| 479 |
+
</div>
|
| 480 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 15px;">Back</button>
|
| 481 |
+
</div>
|
| 482 |
+
|
| 483 |
+
<div class="pause-message" id="pauseMessage">PAUSED</div>
|
| 484 |
+
<div class="combo-meter" id="comboMeter">0x COMBO!</div>
|
| 485 |
+
|
| 486 |
+
<div class="dialog-box" id="gameOver" style="display: none;">
|
| 487 |
+
<h2>MISSION FAILED</h2>
|
| 488 |
+
<p>Your score: <span id="finalScore">0</span></p>
|
| 489 |
+
<p id="highScoreText" style="color: #facc15;"></p>
|
| 490 |
+
<div class="button-group">
|
| 491 |
+
<button class="button" onclick="initGame(game.difficulty)">Try Again</button>
|
| 492 |
+
<button class="button" onclick="showScreen('mainMenu')">Main Menu</button>
|
| 493 |
+
</div>
|
| 494 |
+
</div>
|
| 495 |
+
|
| 496 |
+
<div class="dialog-box" id="gameWon" style="display: none;">
|
| 497 |
+
<h2>MISSION COMPLETE!</h2>
|
| 498 |
+
<p>You conquered all <span id="totalLevelsWon">10</span> ocean levels!</p>
|
| 499 |
+
<p>Final score: <span id="winScore">0</span></p>
|
| 500 |
+
<p id="winHighScoreText" style="color: #facc15;"></p>
|
| 501 |
+
<div class="button-group">
|
| 502 |
+
<button class="button" onclick="initGame(game.difficulty)">Play Again</button>
|
| 503 |
+
<button class="button" onclick="showScreen('mainMenu')">Main Menu</button>
|
| 504 |
+
</div>
|
| 505 |
+
</div>
|
| 506 |
+
|
| 507 |
+
<div class="controls" id="gameControls">
|
| 508 |
+
Mouse: Move • Click: Launch/Laser • P: Pause • M: Mute
|
| 509 |
+
</div>
|
| 510 |
+
|
| 511 |
+
<div class="achievement-popup" id="achievementPopup">
|
| 512 |
+
<div class="achievement-icon" id="achievementPopupIcon">🏆</div>
|
| 513 |
+
<div>
|
| 514 |
+
<div class="achievement-title" id="achievementPopupTitle">Achievement Unlocked!</div>
|
| 515 |
+
<div class="achievement-description" id="achievementPopupDesc">You did something awesome!</div>
|
| 516 |
+
</div>
|
| 517 |
+
</div>
|
| 518 |
+
<div class="notification" id="notificationPopup">
|
| 519 |
+
<div class="notification-icon" id="notificationPopupIcon">ℹ️</div>
|
| 520 |
+
<div>
|
| 521 |
+
<div class="notification-title" id="notificationPopupTitle">Notification</div>
|
| 522 |
+
<div class="notification-description" id="notificationPopupDesc">Something happened.</div>
|
| 523 |
+
</div>
|
| 524 |
+
</div>
|
| 525 |
+
|
| 526 |
+
<div class="level-transition" id="levelTransitionScreen"><div class="level-number" id="levelTransitionNumber">LEVEL 1</div></div>
|
| 527 |
+
<div class="boss-intro" id="bossIntroScreen"><div class="boss-name-large" id="bossIntroName">BOSS NAME</div></div>
|
| 528 |
+
<div class="boss-defeated" id="bossDefeatedScreen"><div class="boss-defeated-text" id="bossDefeatedName">BOSS DEFEATED!</div></div>
|
| 529 |
+
<div class="tutorial" id="tutorialPopup"><span id="tutorialText">Move mouse to control paddle!</span></div>
|
| 530 |
+
<div class="tooltip" id="tooltip" style="display:none;">Tooltip text</div>
|
| 531 |
+
|
| 532 |
+
<div class="settings-panel" id="settingsPanel">
|
| 533 |
+
<h2 class="settings-header">Settings</h2>
|
| 534 |
+
<div class="setting-row">
|
| 535 |
+
<span class="setting-label">Master Volume:</span>
|
| 536 |
+
<div class="slider-container">
|
| 537 |
+
<input type="range" min="0" max="1" step="0.01" value="0.4" class="volume-slider" id="masterVolumeSlider">
|
| 538 |
+
</div>
|
| 539 |
+
</div>
|
| 540 |
+
<div class="setting-row">
|
| 541 |
+
<span class="setting-label">SFX Volume:</span>
|
| 542 |
+
<div class="slider-container">
|
| 543 |
+
<input type="range" min="0" max="1" step="0.01" value="0.5" class="volume-slider" id="sfxVolumeSlider">
|
| 544 |
+
</div>
|
| 545 |
+
</div>
|
| 546 |
+
<div class="setting-row">
|
| 547 |
+
<span class="setting-label">Visual Theme:</span>
|
| 548 |
+
<select id="themeSelector" class="button" style="padding: 5px 10px; text-transform: none; background: #005577; border-color: #0088aa;">
|
| 549 |
+
<option value="deepSea">Deep Sea</option>
|
| 550 |
+
<option value="volcanic">Volcanic Depths</option>
|
| 551 |
+
<option value="crystalCaverns">Crystal Caverns</option>
|
| 552 |
+
</select>
|
| 553 |
+
</div>
|
| 554 |
+
<div class="sound-test" style="margin-top: 15px;">
|
| 555 |
+
<button class="sound-button" onclick="audioManager.playSound(audioManager.definitions.blockHit)">Test Hit</button>
|
| 556 |
+
<button class="sound-button" onclick="audioManager.playSound(audioManager.definitions.powerUpCollect)">Test PU</button>
|
| 557 |
+
</div>
|
| 558 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back to Menu</button>
|
| 559 |
+
</div>
|
| 560 |
+
|
| 561 |
+
<div class="leaderboard dialog-box" id="leaderboardScreen">
|
| 562 |
+
<h2 class="leaderboard-title">High Scores</h2>
|
| 563 |
+
<table class="leaderboard-table" id="leaderboardTable">
|
| 564 |
+
<thead><tr><th>Rank</th><th>Name</th><th>Score</th></tr></thead>
|
| 565 |
+
<tbody></tbody>
|
| 566 |
+
</table>
|
| 567 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back to Menu</button>
|
| 568 |
+
</div>
|
| 569 |
+
|
| 570 |
+
<div class="dialog-box" id="placeholderDialog" style="display: none;">
|
| 571 |
+
<h2 id="placeholderTitle">Feature Not Available</h2>
|
| 572 |
+
<p id="placeholderMessage">This feature is currently under construction or planned for a future update. Thanks for your interest!</p>
|
| 573 |
+
<button class="button" onclick="showScreen('mainMenu')">Back to Menu</button>
|
| 574 |
+
</div>
|
| 575 |
+
|
| 576 |
+
<div class="achievement-system dialog-box" id="achievementsScreen">
|
| 577 |
+
<h2 class="achievement-system-title">Achievements</h2>
|
| 578 |
+
<div id="achievementsListContainer" style="max-height: 300px; overflow-y: auto; text-align: left;"></div>
|
| 579 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back</button>
|
| 580 |
+
</div>
|
| 581 |
+
<div class="tutorial-screen dialog-box" id="tutorialScreenContent">
|
| 582 |
+
<h2 class="tutorial-screen-title">How To Play</h2>
|
| 583 |
+
<div style="text-align: left; font-size: clamp(12px, 2vw, 14px);">
|
| 584 |
+
<p><strong>Objective:</strong> Clear all blocks (coral creatures) on each level by hitting them with the pearl.</p>
|
| 585 |
+
<p><strong>Controls:</strong></p>
|
| 586 |
+
<ul>
|
| 587 |
+
<li>- Move your mouse to control the paddle.</li>
|
| 588 |
+
<li>- Click the left mouse button to launch the pearl or fire lasers (if active).</li>
|
| 589 |
+
<li>- Press 'P' to pause or resume the game.</li>
|
| 590 |
+
<li>- Press 'M' to mute or unmute game sounds.</li>
|
| 591 |
+
</ul>
|
| 592 |
+
<p><strong>Gameplay:</strong></p>
|
| 593 |
+
<ul>
|
| 594 |
+
<li>- Don't let the primary pearl fall below your paddle, or you'll lose a life!</li>
|
| 595 |
+
<li>- Some blocks require multiple hits. Some creatures are dangerous!</li>
|
| 596 |
+
<li>- Collect falling power-ups for special abilities. Rare power-ups are extra potent!</li>
|
| 597 |
+
<li>- Build combos by hitting blocks consecutively for bonus points.</li>
|
| 598 |
+
<li>- Defeat mighty bosses on special levels to progress further into the abyss.</li>
|
| 599 |
+
</ul>
|
| 600 |
+
<p>Good luck, brave diver!</p>
|
| 601 |
+
</div>
|
| 602 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back</button>
|
| 603 |
+
</div>
|
| 604 |
+
<div class="stats-screen dialog-box" id="statsScreen">
|
| 605 |
+
<h2 class="stats-screen-title">Player Statistics</h2>
|
| 606 |
+
<div id="statsContainer" style="text-align: left;"></div>
|
| 607 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back</button>
|
| 608 |
+
</div>
|
| 609 |
+
<div class="credits dialog-box" id="creditsScreen">
|
| 610 |
+
<h2 class="credits-title">Credits</h2>
|
| 611 |
+
<p>Game Design & Development: You & AI Collaborator</p>
|
| 612 |
+
<p>Inspired by: Classic Breakout / Arkanoid Games</p>
|
| 613 |
+
<p>Fonts: Orbitron, Press Start 2P (Google Fonts)</p>
|
| 614 |
+
<button class="button" onclick="showScreen('mainMenu')">Back</button>
|
| 615 |
+
</div>
|
| 616 |
+
<div class="customization dialog-box" id="customizationScreen">
|
| 617 |
+
<h2 class="customization-title">Customize Paddle</h2>
|
| 618 |
+
<p>Select your paddle's appearance:</p>
|
| 619 |
+
<div class="skin-selector" id="paddleSkinSelector"></div>
|
| 620 |
+
<button class="button" onclick="showScreen('mainMenu')" style="margin-top: 20px;">Back</button>
|
| 621 |
+
</div>
|
| 622 |
+
|
| 623 |
+
<div class="loading-screen hidden" id="loadingScreen"> <!-- Start hidden, show via JS -->
|
| 624 |
+
<div id="loadingText">LOADING...</div>
|
| 625 |
+
<div class="loading-bar"><div class="loading-progress" id="loadingProgress"></div></div>
|
| 626 |
+
</div>
|
| 627 |
+
<div class="vignette"></div>
|
| 628 |
+
<div class="water-effect"></div>
|
| 629 |
+
</div>
|
| 630 |
+
|
| 631 |
+
<script>
|
| 632 |
+
// --- Game Setup ---
|
| 633 |
+
const gameContainer = document.querySelector('.game-container');
|
| 634 |
+
const canvas = document.getElementById('gameCanvas');
|
| 635 |
+
|
| 636 |
+
const GAME_WIDTH = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--game-width')) || 800;
|
| 637 |
+
const GAME_HEIGHT = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--game-height')) || 600;
|
| 638 |
+
gameContainer.style.width = `${GAME_WIDTH}px`;
|
| 639 |
+
gameContainer.style.height = `${GAME_HEIGHT}px`;
|
| 640 |
+
canvas.width = GAME_WIDTH;
|
| 641 |
+
canvas.height = GAME_HEIGHT;
|
| 642 |
+
const ctx = canvas.getContext('2d');
|
| 643 |
+
|
| 644 |
+
// --- Chroma.js Basic Fallback ---
|
| 645 |
+
const chroma = window.chroma || {
|
| 646 |
+
contrast: (c1, c2) => {
|
| 647 |
+
const lum = (c) => 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
|
| 648 |
+
const c1Lum = typeof c1 === 'string' ? lum(chroma(c1)) : lum(c1);
|
| 649 |
+
const c2Lum = typeof c2 === 'string' ? lum(chroma(c2)) : lum(c2);
|
| 650 |
+
return Math.abs(c1Lum - c2Lum) > 100 ? 7 : 1.5; // Simplified: high if very different, low otherwise
|
| 651 |
+
},
|
| 652 |
+
(colorStr) => {
|
| 653 |
+
let r=0,g=0,b=0,a=1;
|
| 654 |
+
if (typeof colorStr !== 'string') colorStr = '#000'; // Safety for non-string
|
| 655 |
+
if (colorStr.startsWith('#')) {
|
| 656 |
+
const hex = colorStr.slice(1);
|
| 657 |
+
if (hex.length === 3) { r=parseInt(hex[0]+hex[0],16); g=parseInt(hex[1]+hex[1],16); b=parseInt(hex[2]+hex[2],16); }
|
| 658 |
+
else if (hex.length >= 6) { r=parseInt(hex.substring(0,2),16); g=parseInt(hex.substring(2,4),16); b=parseInt(hex.substring(4,6),16); if (hex.length === 8) a=parseInt(hex.substring(6,8),16)/255;}
|
| 659 |
+
} else if (colorStr.startsWith('rgb')) {
|
| 660 |
+
const parts = colorStr.match(/[\d.]+/g);
|
| 661 |
+
if (parts && parts.length >= 3) { r = +parts[0]; g = +parts[1]; b = +parts[2]; if (parts.length === 4) a = +parts[3]; }
|
| 662 |
+
}
|
| 663 |
+
return {
|
| 664 |
+
r,g,b,a,
|
| 665 |
+
darken: (amount = 1) => `rgb(${Math.max(0,r-30*amount)}, ${Math.max(0,g-30*amount)}, ${Math.max(0,b-30*amount)})`,
|
| 666 |
+
brighten: (amount = 1) => `rgb(${Math.min(255,r+30*amount)}, ${Math.min(255,g+30*amount)}, ${Math.min(255,b+30*amount)})`,
|
| 667 |
+
alpha: (newAlpha) => `rgba(${r},${g},${b},${newAlpha})`,
|
| 668 |
+
hex: () => `#${r.toString(16).padStart(2,'0')}${g.toString(16).padStart(2,'0')}${b.toString(16).padStart(2,'0')}`,
|
| 669 |
+
css: () => `rgba(${r},${g},${b},${a})`
|
| 670 |
+
}
|
| 671 |
+
}
|
| 672 |
+
};
|
| 673 |
+
|
| 674 |
+
// --- Audio Manager (from previous enhanced version, assumed stable) ---
|
| 675 |
+
class AudioManager {
|
| 676 |
+
constructor() {
|
| 677 |
+
this.audioContext = null; this.masterGain = null; this.sfxGain = null; this.isMuted = false;
|
| 678 |
+
this.definitions = { // Definitions moved here for clarity
|
| 679 |
+
blockHit: { freq1: 600, freq2: 300, duration: 0.05, volume: 0.15, type: 'triangle' },
|
| 680 |
+
paddleHit: { freq1: 440, freq2: 440, duration: 0.05, volume: 0.2, type: 'square' },
|
| 681 |
+
wallHit: { freq1: 200, freq2: 150, duration: 0.05, volume: 0.1, type: 'sawtooth' },
|
| 682 |
+
loseLife: { freq1: 300, freq2: 50, duration: 0.5, volume: 0.3, type: 'sawtooth' },
|
| 683 |
+
levelUp: [
|
| 684 |
+
{ freq1: 500, freq2: 1000, duration: 0.3, volume: 0.25, type: 'sine', attack: 0.05, decay: 0.3 },
|
| 685 |
+
{ freq1: 300, freq2: 600, duration: 0.3, volume: 0.15, type: 'square', attack: 0.05, decay: 0.3, delay: 0.05 }
|
| 686 |
+
],
|
| 687 |
+
powerUpSpawn: { freq1: 700, freq2: 900, duration: 0.1, volume: 0.2, type: 'sine' },
|
| 688 |
+
powerUpCollect: { freq1: 800, freq2: 1200, duration: 0.2, volume: 0.3, type: 'triangle' },
|
| 689 |
+
rarePowerUpCollect: [
|
| 690 |
+
{ freq1: 1000, freq2: 2000, duration: 0.4, volume: 0.4, type: 'sine', decay: 0.4 },
|
| 691 |
+
{ freq1: 800, freq2: 1500, duration: 0.3, volume: 0.3, type: 'square', decay: 0.3, delay: 0.1 }
|
| 692 |
+
],
|
| 693 |
+
combo: (comboCount) => ({
|
| 694 |
+
freq1: Math.min(1200, 300 + (comboCount * 25)), freq2: Math.min(1350, 450 + (comboCount * 25)),
|
| 695 |
+
duration: 0.15, volume: Math.min(0.4, 0.15 + (comboCount * 0.02)), type: 'sine'
|
| 696 |
+
}),
|
| 697 |
+
gameOver: [
|
| 698 |
+
{ freq1: 300, freq2: 100, duration: 1.5, volume: 0.4, type: 'sine' },
|
| 699 |
+
{ freq1: 200, freq2: 50, duration: 1.0, volume: 0.3, type: 'square', delay: 0.2 }
|
| 700 |
+
],
|
| 701 |
+
gameWon: [
|
| 702 |
+
{ freq1: 500, freq2: 1200, duration: 1.0, volume: 0.4, type: 'sine' },
|
| 703 |
+
{ freq1: 800, freq2: 1000, duration: 0.2, volume: 0.25, type: 'triangle', delay: 0.2 },
|
| 704 |
+
{ freq1: 900, freq2: 1100, duration: 0.2, volume: 0.25, type: 'triangle', delay: 0.4 },
|
| 705 |
+
{ freq1: 1000, freq2: 1200, duration: 0.2, volume: 0.25, type: 'triangle', delay: 0.6 }
|
| 706 |
+
],
|
| 707 |
+
laserFire: { freq1: 1800, freq2: 1000, duration: 0.08, volume: 0.08, type: 'sawtooth', attack:0.005, decay: 0.04 },
|
| 708 |
+
blackHoleOpen: { freq1: 100, freq2: 30, duration: 1.5, volume: 0.5, type: 'sawtooth', decay: 1.5 },
|
| 709 |
+
blackHoleAbsorb: { freq1: 200, freq2: 400, duration: 0.05, volume: 0.2, type: 'noise', decay: 0.1 },
|
| 710 |
+
bossHit: { freq1: 250, freq2: 150, duration: 0.2, volume: 0.35, type: 'square' },
|
| 711 |
+
bossDefeat: [
|
| 712 |
+
{ freq1: 100, freq2: 50, duration: 1.0, volume: 0.5, type: 'sawtooth' },
|
| 713 |
+
{ freq1: 1000, freq2: 400, duration: 0.8, volume: 0.4, type: 'sine', delay: 0.2 }
|
| 714 |
+
],
|
| 715 |
+
achievement: { freq1: 1200, freq2: 1800, duration: 0.3, volume: 0.3, type: 'triangle', attack: 0.02, decay: 0.2 }
|
| 716 |
+
};
|
| 717 |
+
}
|
| 718 |
+
async init() { /* ... same as before ... */
|
| 719 |
+
if(!this.audioContext){try{this.audioContext=new(window.AudioContext||window.webkitAudioContext)();this.masterGain=this.audioContext.createGain();this.sfxGain=this.audioContext.createGain();this.sfxGain.connect(this.masterGain);this.masterGain.connect(this.audioContext.destination);this.setVolume('master',parseFloat(localStorage.getItem('masterVolume_v2')||'0.4'));this.setVolume('sfx',parseFloat(localStorage.getItem('sfxVolume_v2')||'0.5'));}catch(e){console.error("Audio API init failed.",e);return;}}
|
| 720 |
+
if(this.audioContext.state==='suspended')await this.audioContext.resume();
|
| 721 |
+
}
|
| 722 |
+
setVolume(type, value) { /* ... same as before, ensure localStorage keys are unique ... */
|
| 723 |
+
const gainNode = type === 'master' ? this.masterGain : this.sfxGain;
|
| 724 |
+
if (gainNode && this.audioContext) { // Add audioContext check
|
| 725 |
+
gainNode.gain.setValueAtTime(this.isMuted && type === 'master' ? 0 : value, this.audioContext.currentTime);
|
| 726 |
+
localStorage.setItem(`${type}Volume_v2`, value); // Unique key
|
| 727 |
+
}
|
| 728 |
+
if (type === 'master' && document.getElementById('masterVolumeSlider')) document.getElementById('masterVolumeSlider').value = value;
|
| 729 |
+
if (type === 'sfx' && document.getElementById('sfxVolumeSlider')) document.getElementById('sfxVolumeSlider').value = value;
|
| 730 |
+
}
|
| 731 |
+
playSound(soundDef, dynamicParams = {}) { /* ... same as before ... */
|
| 732 |
+
if(this.isMuted||!this.audioContext||this.audioContext.state!=='running'||!soundDef)return;
|
| 733 |
+
const playSingle=(params)=>{const{freq1,freq2,duration,volume=0.3,type='sine',attack=0.01,decay=0.1,pan=0,delay=0}=params;const osc=this.audioContext.createOscillator();const gain=this.audioContext.createGain();const panner=this.audioContext.createStereoPanner();osc.connect(gain);gain.connect(panner);panner.connect(this.sfxGain);osc.type=type;panner.pan.setValueAtTime(pan,this.audioContext.currentTime+delay);osc.frequency.setValueAtTime(freq1,this.audioContext.currentTime+delay);if(freq2&&freq1!==freq2){osc.frequency.exponentialRampToValueAtTime(freq2,this.audioContext.currentTime+delay+duration);}gain.gain.setValueAtTime(0,this.audioContext.currentTime+delay);gain.gain.linearRampToValueAtTime(volume,this.audioContext.currentTime+delay+attack);gain.gain.exponentialRampToValueAtTime(0.001,this.audioContext.currentTime+delay+duration+decay);osc.start(this.audioContext.currentTime+delay);osc.stop(this.audioContext.currentTime+delay+duration+decay+0.05);};
|
| 734 |
+
let effDef=typeof soundDef==='function'?soundDef(dynamicParams):soundDef;if(Array.isArray(effDef)){effDef.forEach(p=>playSingle({...p,...dynamicParams}));}else{playSingle({...effDef,...dynamicParams});}
|
| 735 |
+
}
|
| 736 |
+
toggleMute() { /* ... same as before, ensure localStorage keys are unique ... */
|
| 737 |
+
if(!this.masterGain)return this.isMuted;this.isMuted=!this.isMuted;const currentMasterVolume=parseFloat(localStorage.getItem('masterVolume_v2')||'0.4');if(this.audioContext)this.masterGain.gain.setValueAtTime(this.isMuted?0:currentMasterVolume,this.audioContext.currentTime);return this.isMuted;
|
| 738 |
+
}
|
| 739 |
+
}
|
| 740 |
+
const audioManager = new AudioManager();
|
| 741 |
+
|
| 742 |
+
// --- Game Constants (using GAME_WIDTH, GAME_HEIGHT) ---
|
| 743 |
+
const PADDLE_DEFAULT_WIDTH = GAME_WIDTH * 0.13;
|
| 744 |
+
const PADDLE_HEIGHT = GAME_HEIGHT * 0.025;
|
| 745 |
+
const BALL_RADIUS = GAME_WIDTH * 0.01;
|
| 746 |
+
const BALL_INITIAL_SPEED = GAME_HEIGHT * 0.0085;
|
| 747 |
+
|
| 748 |
+
const POWERUP_DURATION = 9000;
|
| 749 |
+
const RARE_POWERUP_DURATION_MULTIPLIER = 1.4;
|
| 750 |
+
const POWERUP_DROP_CHANCE = 0.18; // Slightly increased
|
| 751 |
+
const RARE_POWERUP_CHANCE = 0.08; // Slightly increased
|
| 752 |
+
const COMBO_TIMEOUT = 2200;
|
| 753 |
+
const MAX_LEVELS = 10;
|
| 754 |
+
const BOSS_LEVELS = [5, MAX_LEVELS];
|
| 755 |
+
|
| 756 |
+
const POWERUP_TYPES = { WIDE_PADDLE: 'WIDE_PADDLE', STICKY_PADDLE: 'STICKY_PADDLE', PIERCING_BALL: 'PIERCING_BALL', LASER_PADDLE: 'LASER_PADDLE', MULTI_BALL: 'MULTI_BALL', SLOW_MO: 'SLOW_MO', SHIELD: 'SHIELD' };
|
| 757 |
+
const RARE_POWERUP_TYPES = { INVINCIBILITY: 'INVINCIBILITY', TIME_STOP: 'TIME_STOP', BLACK_HOLE: 'BLACK_HOLE', PENTA_BALL: 'PENTA_BALL', MEGA_LASER: 'MEGA_LASER', BLOCK_BOMB: 'BLOCK_BOMB' };
|
| 758 |
+
const ALL_POWERUP_TYPES = {...POWERUP_TYPES, ...RARE_POWERUP_TYPES};
|
| 759 |
+
|
| 760 |
+
const BLOCK_CREATURES = ['🐠', '🐟', '🐡', '🐢', '🦀', '🦞', '🦐', '🐚', '⭐', '🌊', '🫧'];
|
| 761 |
+
const DANGEROUS_CREATURES = ['🦈', '🐙', '🦑', '🐊']; // Pufferfish removed from dangerous, added to normal
|
| 762 |
+
const BOSS_CREATURES = {5: '🐙👑', 10: '🐲🌌'};
|
| 763 |
+
|
| 764 |
+
const PADDLE_SKINS = [
|
| 765 |
+
{ id: 'default', name: 'Classic Blue', color1: '#30A8F8', color2: '#0077cc', outline: '#60C8FF' },
|
| 766 |
+
{ id: 'ember', name: 'Ember Glow', color1: '#ff7e5f', color2: '#feb47b', outline: '#ffcf9f' },
|
| 767 |
+
{ id: 'forest', name: 'Forest Gem', color1: '#00b09b', color2: '#96c93d', outline: '#c0ff7d' },
|
| 768 |
+
{ id: 'void', name: 'Void Walker', color1: '#4A00E0', color2: '#8E2DE2', outline: '#C675FF' },
|
| 769 |
+
{ id: 'gold', name: 'Golden Pearl', color1: '#FFD700', color2: '#FFA500', outline: '#FFEEAA' },
|
| 770 |
+
];
|
| 771 |
+
|
| 772 |
+
let game = {
|
| 773 |
+
score: 0, highScore: 0, // Loaded in init
|
| 774 |
+
lives: 3, maxLives: 3, level: 1, running: false, paused: false, difficulty: 'normal',
|
| 775 |
+
initialBlockCount: 0, particles: [], backgroundFish: [], backgroundBubbles: [],
|
| 776 |
+
powerUpsOnScreen: [], activePowerUps: {},
|
| 777 |
+
combo: 0, lastHitTime: 0, balls: [], lasers: [], bubbleTexts: [], blackHole: null,
|
| 778 |
+
currentBackgroundHue: 180, currentTheme: 'deepSea', currentBoss: null,
|
| 779 |
+
achievements: {}, playerStats: {}, paddleSkin: PADDLE_SKINS[0],
|
| 780 |
+
levelTransitioning: false, gameTime: 0, currentScreen: 'loadingScreen',
|
| 781 |
+
gameSessionStartTime: 0, levelStartTime: 0,
|
| 782 |
+
isFlawlessLevel: true, // For achievement
|
| 783 |
+
};
|
| 784 |
+
|
| 785 |
+
let paddle = { x:0,y:0,width:0,height:0, targetX:0 }; // Initialized in initGame
|
| 786 |
+
let blocks = []; let mouseX = GAME_WIDTH / 2;
|
| 787 |
+
|
| 788 |
+
const blockColors = [ '#FF6347','#FFD700','#ADFF2F','#00CED1','#1E90FF','#9370DB','#FF69B4','#FFA07A','#20B2AA','#7FFF00','#BA55D3','#DA70D6','#6495ED','#FF4500','#32CD32','#8A2BE2' ];
|
| 789 |
+
|
| 790 |
+
// --- UI Management ---
|
| 791 |
+
function showScreen(screenId) {
|
| 792 |
+
document.querySelectorAll('.dialog-box, .menu, .settings-panel, .leaderboard, .shop, .daily-challenge, .achievement-system, .tutorial-screen, .stats-screen, .credits, .customization, .profile, .multiplayer-info, .matchmaking, #difficultySelectScreen, #placeholderDialog').forEach(el => el.style.display = 'none');
|
| 793 |
+
const screenElement = document.getElementById(screenId);
|
| 794 |
+
if (screenElement) {
|
| 795 |
+
screenElement.style.display = 'flex';
|
| 796 |
+
game.currentScreen = screenId;
|
| 797 |
+
// Pause game if showing a dialog and game was running
|
| 798 |
+
if (screenId !== 'game' && game.running && !game.paused) { // Check if game means the canvas play area
|
| 799 |
+
game.paused = true;
|
| 800 |
+
document.getElementById('pauseMessage').style.display = 'block'; // Show pause message if game was running
|
| 801 |
+
}
|
| 802 |
+
// Specific updates for certain screens
|
| 803 |
+
if (screenId === 'leaderboardScreen') updateLeaderboardDisplay();
|
| 804 |
+
else if (screenId === 'statsScreen') updateStatsDisplay();
|
| 805 |
+
else if (screenId === 'achievementsScreen') updateAchievementsDisplay();
|
| 806 |
+
else if (screenId === 'customizationScreen') populatePaddleSkins();
|
| 807 |
+
} else {
|
| 808 |
+
console.warn(`Screen with ID ${screenId} not found.`);
|
| 809 |
+
document.getElementById('mainMenu').style.display = 'flex';
|
| 810 |
+
game.currentScreen = 'mainMenu';
|
| 811 |
+
}
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
function showScreenWithPlaceholder(screenId, title) {
|
| 815 |
+
showScreen('placeholderDialog');
|
| 816 |
+
document.getElementById('placeholderTitle').textContent = title;
|
| 817 |
+
document.getElementById('placeholderMessage').textContent = `The ${title.toLowerCase()} feature is currently under construction or planned for a future update. Check back later!`;
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
+
function showNotification(title, description, icon = 'ℹ️', duration = 3000) { /* Same as before */
|
| 821 |
+
const p=document.getElementById('notificationPopup');document.getElementById('notificationPopupTitle').textContent=title;document.getElementById('notificationPopupDesc').textContent=description;document.getElementById('notificationPopupIcon').textContent=icon;p.classList.add('show');setTimeout(()=>p.classList.remove('show'),duration);
|
| 822 |
+
}
|
| 823 |
+
function showAchievementPopup(achievement) { /* Same as before */
|
| 824 |
+
const p=document.getElementById('achievementPopup');document.getElementById('achievementPopupTitle').textContent=achievement.name;document.getElementById('achievementPopupDesc').textContent=achievement.description;document.getElementById('achievementPopupIcon').textContent=achievement.icon;p.classList.add('show');audioManager.playSound(audioManager.definitions.achievement);setTimeout(()=>p.classList.remove('show'),4000);
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
// --- Initialization ---
|
| 828 |
+
function initPrimaryBall() { /* Same as before */
|
| 829 |
+
const sM=game.difficulty==='hardcore'?1.3:1;const bS=BALL_INITIAL_SPEED*sM;game.balls=[{x:paddle.x+paddle.width/2,y:paddle.y-BALL_RADIUS*1.5,dx:0,dy:0,radius:BALL_RADIUS,speed:bS+(game.level-1)*(GAME_HEIGHT*0.0004)*sM,attached:true,piercing:false,isPrimary:true,trail:[],color1:game.paddleSkin.color1,color2:game.paddleSkin.color2,color3:game.paddleSkin.outline}];
|
| 830 |
+
}
|
| 831 |
+
function createBackgroundElements() { /* Same as before */
|
| 832 |
+
if(game.backgroundFish.length===0){for(let i=0;i<15;i++)game.backgroundFish.push({x:Math.random()*GAME_WIDTH,y:Math.random()*GAME_HEIGHT,dx:(Math.random()-0.5)*(GAME_WIDTH*0.001),dy:(Math.random()-0.5)*(GAME_HEIGHT*0.0005),size:Math.random()*(GAME_WIDTH*0.02)+(GAME_WIDTH*0.015),creature:['🐠','🐟','🐡'][Math.floor(Math.random()*3)],alpha:Math.random()*0.2+0.05,flipX:Math.random()>0.5});}
|
| 833 |
+
if(game.backgroundBubbles.length===0){for(let i=0;i<40;i++){let b={r:Math.random()*(GAME_WIDTH*0.0035)+(GAME_WIDTH*0.001),dy:-(Math.random()*(GAME_HEIGHT*0.0008)+(GAME_HEIGHT*0.0002)),a:Math.random()*0.3+0.1,amp:Math.random()*(GAME_WIDTH*0.015)+(GAME_WIDTH*0.004),freq:Math.random()*0.05+0.01};b.x=Math.random()*GAME_WIDTH;b.y=GAME_HEIGHT+Math.random()*GAME_HEIGHT+b.r;b.ix=b.x;game.backgroundBubbles.push(b);}}
|
| 834 |
+
}
|
| 835 |
+
function createBlocks() { /* Same as before */
|
| 836 |
+
blocks=[];if(game.currentBoss)return;const bR=game.difficulty==='hardcore'?4:3;const rS=Math.min(10,bR+Math.floor(game.level*(game.difficulty==='hardcore'?0.9:0.7)));const cS=game.difficulty==='hardcore'?14:12;const tBAW=GAME_WIDTH*0.95;const sM=(GAME_WIDTH-tBAW)/2;const bW=(tBAW/cS)*0.92;const bG=(tBAW/cS)*0.08;const bH=GAME_HEIGHT*0.04;const tM=GAME_HEIGHT*0.12;const dBC=Math.min(0.25,game.level*0.025*(game.difficulty==='hardcore'?1.6:1.1));
|
| 837 |
+
for(let r=0;r<rS;r++){for(let c=0;c<cS;c++){if(Math.random()<0.08&&game.level>2)continue;const iD=Math.random()<dBC&&game.level>1;let mH=1;if(game.difficulty==='hardcore')mH=r<2?3:(r<5?2:1);else mH=r<1?2:1;if(iD)mH=Math.min(4,mH+1);mH=Math.min(5,mH+Math.floor(game.level/2.5));blocks.push({x:sM+c*(bW+bG),y:tM+r*(bH+bG*0.7),width:bW,height:bH,color:blockColors[(r*cS+c+game.level)%blockColors.length],hits:mH,maxHits:mH,creature:iD?DANGEROUS_CREATURES[Math.floor(Math.random()*DANGEROUS_CREATURES.length)]:BLOCK_CREATURES[Math.floor(Math.random()*BLOCK_CREATURES.length)],isDangerous:iD,animationOffset:Math.random()*Math.PI*2,id:`block-${r}-${c}`});}}
|
| 838 |
+
game.initialBlockCount=blocks.length;updateLevelProgressUI();game.isFlawlessLevel=true; // Reset for new level
|
| 839 |
+
}
|
| 840 |
+
function createBoss(level) { /* Same as before */
|
| 841 |
+
const bC=BOSS_CREATURES[level]||'😈';let bHP,bW,bH,bS,bAP;
|
| 842 |
+
if(level===BOSS_LEVELS[0]){bHP=50+20*(game.difficulty==='hardcore'?2:1);bW=GAME_WIDTH*0.3;bH=GAME_HEIGHT*0.15;bS=GAME_WIDTH*0.0005;bAP='tentacleSwipe';}
|
| 843 |
+
else if(level===BOSS_LEVELS[1]){bHP=80+30*(game.difficulty==='hardcore'?2:1);bW=GAME_WIDTH*0.4;bH=GAME_HEIGHT*0.2;bS=GAME_WIDTH*0.0003;bAP='energyBeam';}
|
| 844 |
+
else return null;
|
| 845 |
+
game.currentBoss={x:GAME_WIDTH/2-bW/2,y:GAME_HEIGHT*0.1,width:bW,height:bH,hp:bHP,maxHp:bHP,creature:bC,color:'#FF0055',dx:bS,vulnerableTime:0,lastAttackTime:0,attackPattern:bAP,animationOffset:Math.random()*Math.PI*2};blocks=[];document.getElementById('bossHealthContainer').style.display='block';document.getElementById('bossName').textContent=bC;document.getElementById('bossName').style.display='block';updateBossHealthUI();
|
| 846 |
+
}
|
| 847 |
+
|
| 848 |
+
function defineAchievements() { /* Same as before */
|
| 849 |
+
game.achievements={FIRST_BREAK:{name:"Icebreaker",description:"Destroy your first block.",unlocked:false,icon:'💥'},LEVEL_3:{name:"Deep Diver",description:"Reach Level 3.",unlocked:false,icon:'🌊'},LEVEL_5_BOSS:{name:"Kraken Guard Defeated",description:"Defeat the boss of Level 5.",unlocked:false,icon:'🐙'},GAME_WON:{name:"Abyssal Conqueror",description:`Clear all ${MAX_LEVELS} levels.`,unlocked:false,icon:'👑'},HIGH_SCORE_10K:{name:"Coral Collector",description:"Achieve a score of 10,000.",unlocked:false,icon:'💰'},POWERUP_MASTER:{name:"Power Overwhelming",description:"Collect 5 power-ups in a single game.",unlocked:false,icon:'⚡'},COMBO_15X:{name:"Combo King",description:"Achieve a 15x combo.",unlocked:false,icon:'✨'},NO_LIVES_LOST_LEVEL:{name:"Flawless Victory (Level)",description:"Clear a level without losing a life.",unlocked:false,icon:'🛡️'},};
|
| 850 |
+
const sA=JSON.parse(localStorage.getItem('deepSeaBreakoutAchievements_v2')||'{}');for(const k in game.achievements){if(sA[k])game.achievements[k].unlocked=true;}
|
| 851 |
+
}
|
| 852 |
+
function unlockAchievement(id) { /* Same as before */
|
| 853 |
+
if(game.achievements[id]&&!game.achievements[id].unlocked){game.achievements[id].unlocked=true;showAchievementPopup(game.achievements[id]);localStorage.setItem('deepSeaBreakoutAchievements_v2',JSON.stringify(game.achievements));game.score+=500;addBubbleText(`+500 (Achievement!)`,GAME_WIDTH/2,GAME_HEIGHT/2,'#FFD700',18);}
|
| 854 |
+
}
|
| 855 |
+
|
| 856 |
+
function initPlayerStats() { /* Same as before */
|
| 857 |
+
game.playerStats=JSON.parse(localStorage.getItem('deepSeaBreakoutStats_v2')||JSON.stringify({gamesPlayed:0,totalScore:0,totalBlocksBroken:0,totalPowerupsCollected:0,totalGameTime:0,highestLevelReached:0,bossesDefeated:0}));
|
| 858 |
+
}
|
| 859 |
+
function updatePlayerStat(stat, value) { /* Same as before */
|
| 860 |
+
if(game.playerStats.hasOwnProperty(stat)){game.playerStats[stat]=(game.playerStats[stat]||0)+value;}else{game.playerStats[stat]=value;}
|
| 861 |
+
}
|
| 862 |
+
function savePlayerStats() { /* Same as before */
|
| 863 |
+
localStorage.setItem('deepSeaBreakoutStats_v2',JSON.stringify(game.playerStats));
|
| 864 |
+
}
|
| 865 |
+
|
| 866 |
+
function addBubbleText(text, x, y, color, size, duration = 60) { /* Same as before */
|
| 867 |
+
game.bubbleTexts.push({text,x,y,color,size:size*(GAME_WIDTH/800),alpha:1,life:duration,initialY:y});
|
| 868 |
+
}
|
| 869 |
+
|
| 870 |
+
// --- Particle & Effect Drawers (simplified to save space, assume previous good implementations) ---
|
| 871 |
+
function drawParticles() { /* ... */ game.particles.forEach(p=>{let a=Math.pow(p.life/p.maxLife,1.5);ctx.save();ctx.globalAlpha=a;if(p.glow){let g=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,p.size*a);g.addColorStop(0,p.color);g.addColorStop(1,`${p.color}00`);ctx.fillStyle=g;}else{ctx.fillStyle=p.color;}ctx.beginPath();ctx.arc(p.x,p.y,p.size*a,0,Math.PI*2);ctx.fill();ctx.restore();});}
|
| 872 |
+
function drawBallTrails() { /* ... */ game.balls.forEach(b=>{if(b.trail.length>1){ctx.save();let p=game.activePowerUps[ALL_POWERUP_TYPES.PIERCING_BALL]||b.piercing;let c=p?[255,0,255]:(b.isPrimary?[parseInt(b.color2.substring(1,3),16),parseInt(b.color2.substring(3,5),16),parseInt(b.color2.substring(5,7),16)]:[200,200,200]);for(let i=0;i<b.trail.length;i++){let pt=b.trail[i],a=(i/b.trail.length)*0.4,r=pt.r*(i/b.trail.length);ctx.fillStyle=`rgba(${c[0]},${c[1]},${c[2]},${a})`;ctx.beginPath();ctx.arc(pt.x,pt.y,r,0,Math.PI*2);ctx.fill();}ctx.restore();}}); }
|
| 873 |
+
function drawBackgroundElements() { /* ... */
|
| 874 |
+
const targetHue=game.currentTheme==='volcanic'?20:(game.currentTheme==='crystalCaverns'?260:180+(game.level*4));game.currentBackgroundHue+=(targetHue-game.currentBackgroundHue)*0.01;
|
| 875 |
+
const grad=ctx.createLinearGradient(0,0,0,GAME_HEIGHT);
|
| 876 |
+
grad.addColorStop(0,`hsla(${game.currentBackgroundHue},70%,${game.currentTheme==='volcanic'?15:20}%,0.9)`);
|
| 877 |
+
grad.addColorStop(0.7,`hsla(${game.currentBackgroundHue+10},70%,${game.currentTheme==='volcanic'?8:10}%,1)`);
|
| 878 |
+
grad.addColorStop(1,`hsla(${game.currentBackgroundHue+20},70%,${game.currentTheme==='volcanic'?3:5}%,1)`);
|
| 879 |
+
ctx.fillStyle=grad;ctx.fillRect(0,0,GAME_WIDTH,GAME_HEIGHT);
|
| 880 |
+
game.backgroundFish.forEach(f=>{ctx.save();ctx.globalAlpha=f.alpha;ctx.font=`${f.size}px Arial`;ctx.textAlign='center';if(f.flipX){ctx.scale(-1,1);ctx.fillText(f.creature,-f.x,f.y);}else{ctx.fillText(f.creature,f.x,f.y);}ctx.restore();});
|
| 881 |
+
game.backgroundBubbles.forEach(b=>{ctx.save();ctx.globalAlpha=b.a;ctx.fillStyle='rgba(173,216,230,0.4)';ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,Math.PI*2);ctx.fill();ctx.fillStyle='rgba(255,255,255,0.6)';ctx.beginPath();ctx.arc(b.x-b.r*0.3,b.y-b.r*0.3,b.r*0.4,0,Math.PI*2);ctx.fill();ctx.restore();});
|
| 882 |
+
}
|
| 883 |
+
function drawBubbleTexts() { /* ... */ for(let i=game.bubbleTexts.length-1;i>=0;i--){let b=game.bubbleTexts[i];ctx.save();ctx.globalAlpha=b.alpha;ctx.font=`bold ${b.size}px 'Orbitron',sans-serif`;ctx.textAlign='center';ctx.fillStyle=b.color;ctx.shadowColor='rgba(0,0,0,0.7)';ctx.shadowBlur=3;ctx.shadowOffsetX=1;ctx.shadowOffsetY=1;ctx.fillText(b.text,b.x,b.y);ctx.restore();b.y-=1*(GAME_HEIGHT/600);b.alpha=Math.max(0,b.life/60);b.size*=0.995;b.life--;if(b.life<=0)game.bubbleTexts.splice(i,1);}}
|
| 884 |
+
function drawPaddle() { /* ... */ const{x,y,width,height}=paddle;const s=game.paddleSkin;const cR=height/2;ctx.save();ctx.shadowColor=s.outline;ctx.shadowBlur=15+Math.sin(Date.now()/200)*3;ctx.fillStyle=chroma(s.outline).alpha(0.15).css();ctx.beginPath();ctx.roundRect(x-5,y-5,width+10,height+10,cR+5);ctx.fill();ctx.restore();const grad=ctx.createLinearGradient(x,y,x,y+height);grad.addColorStop(0,s.color1);grad.addColorStop(1,s.color2);ctx.fillStyle=grad;ctx.beginPath();ctx.roundRect(x,y,width,height,cR);ctx.fill();ctx.strokeStyle=s.outline;ctx.lineWidth=2;ctx.beginPath();ctx.roundRect(x,y,width,height,cR);ctx.stroke();if(game.activePowerUps[POWERUP_TYPES.LASER_PADDLE]||game.activePowerUps[RARE_POWERUP_TYPES.MEGA_LASER]){const iM=game.activePowerUps[RARE_POWERUP_TYPES.MEGA_LASER];ctx.fillStyle=iM?'#FF8C00':'#ff3333';const cW=width*(iM?0.15:0.1);const cH=height*(iM?1.2:0.8);ctx.beginPath();ctx.roundRect(x+width*0.15-cW/2,y-cH,cW,cH,2);ctx.fill();ctx.beginPath();ctx.roundRect(x+width*0.85-cW/2,y-cH,cW,cH,2);ctx.fill();if(iM){ctx.beginPath();ctx.roundRect(x+width*0.5-cW*0.8,y-cH*1.2,cW*1.6,cH*1.2,3);ctx.fill();}}if(game.activePowerUps[POWERUP_TYPES.SHIELD]){ctx.save();ctx.strokeStyle='#00FFFF';ctx.lineWidth=3;ctx.globalAlpha=0.5+Math.sin(Date.now()/150)*0.2;ctx.beginPath();ctx.arc(x+width/2,y+height/2,width/1.8,0,Math.PI*2);ctx.stroke();ctx.restore();}}
|
| 885 |
+
function drawBalls() { /* ... */ game.balls.forEach(b=>{const{x,y,radius,attached,isPrimary}=b;const isP=b.piercing||game.activePowerUps[ALL_POWERUP_TYPES.PIERCING_BALL]||game.activePowerUps[RARE_POWERUP_TYPES.INVINCIBILITY];if(!attached){ctx.save();const gR=radius*(isP?2.2:1.8);const grad=ctx.createRadialGradient(x,y,0,x,y,gR);const rgb=isP?[255,50,255]:(isPrimary?[parseInt(b.color2.substring(1,3),16),parseInt(b.color2.substring(3,5),16),parseInt(b.color2.substring(5,7),16)]:[220,220,220]);grad.addColorStop(0,`rgba(${rgb[0]},${rgb[1]},${rgb[2]},0.4)`);grad.addColorStop(0.5,`rgba(${rgb[0]},${rgb[1]},${rgb[2]},0.15)`);grad.addColorStop(1,`rgba(${rgb[0]},${rgb[1]},${rgb[2]},0)`);ctx.fillStyle=grad;ctx.beginPath();ctx.arc(x,y,gR,0,Math.PI*2);ctx.fill();ctx.restore();}const bG=ctx.createRadialGradient(x-radius*0.3,y-radius*0.3,0,x,y,radius);const c1=isP?'#ffffff':b.color1;const c2=isP?'#ffccff':b.color2;const c3=isP?'#ff33ff':b.color3;bG.addColorStop(0,c1);bG.addColorStop(0.5,c2);bG.addColorStop(1,c3);ctx.fillStyle=bG;ctx.beginPath();ctx.arc(x,y,radius,0,Math.PI*2);ctx.fill();ctx.fillStyle='rgba(255,255,255,0.7)';ctx.beginPath();ctx.arc(x-radius*0.35,y-radius*0.35,radius*0.3,0,Math.PI*2);ctx.fill();if(game.activePowerUps[POWERUP_TYPES.STICKY_PADDLE]&&attached&&isPrimary){ctx.save();ctx.strokeStyle='rgba(0,255,255,0.4)';ctx.setLineDash([BALL_RADIUS*0.5,BALL_RADIUS*0.3]);ctx.lineWidth=1.5;ctx.beginPath();ctx.moveTo(x,y-radius);ctx.lineTo(x,0);ctx.stroke();ctx.restore();}}); }
|
| 886 |
+
function drawBlocks() { /* ... */ blocks.forEach(b=>{const{x,y,width,height,color,hits,maxHits,creature,isDangerous,animationOffset}=b;const hR=hits/maxHits;const bR=Math.min(width,height)*0.15;const ay=isDangerous?Math.sin(Date.now()/250+animationOffset)*(height*0.05):0;if(isDangerous){ctx.save();ctx.shadowColor=`rgba(255,${60+Math.sin(Date.now()/180+animationOffset)*40},0,0.7)`;ctx.shadowBlur=12+Math.sin(Date.now()/180+animationOffset)*4;ctx.fillStyle=`rgba(180,0,0,${0.08+Math.sin(Date.now()/180+animationOffset)*0.04})`;ctx.beginPath();ctx.roundRect(x-2,y-2+ay,width+4,height+4,bR+2);ctx.fill();ctx.restore();}const grad=ctx.createLinearGradient(x,y,x,y+height);if(hits>1){grad.addColorStop(0,color);grad.addColorStop(1,chroma(color).darken(1.8).hex());}else{grad.addColorStop(0,color);grad.addColorStop(1,chroma(color).darken(0.8).hex()+'E0');}ctx.fillStyle=grad;ctx.beginPath();ctx.roundRect(x,y+ay,width,height,bR);ctx.fill();ctx.strokeStyle='rgba(255,255,255,0.15)';ctx.lineWidth=1;ctx.beginPath();ctx.roundRect(x,y+ay,width,height,bR);ctx.stroke();ctx.font=`${Math.min(width,height)*0.55}px Arial`;ctx.textAlign='center';ctx.textBaseline='middle';const cY=y+height/2+ay;ctx.fillStyle='rgba(0,0,0,0.3)';ctx.fillText(creature,x+width/2+1,cY+1);ctx.fillStyle=hR===1?'#fff':`rgba(255,255,255,${0.4+hR*0.6})`;ctx.fillText(creature,x+width/2,cY);if(hits<maxHits&&hits>0){ctx.save();const bY=y-(height*0.18)+ay;const bH=height*0.08;ctx.fillStyle='rgba(0,0,0,0.3)';ctx.beginPath();ctx.roundRect(x,bY,width,bH,bH/2);ctx.fill();ctx.fillStyle=hR>0.5?'#5f5':(hR>0.2?'#ff5':'#f55');ctx.beginPath();ctx.roundRect(x,bY,width*hR,bH,bH/2);ctx.fill();ctx.restore();}}); }
|
| 887 |
+
function drawLasers() { /* ... */ game.lasers.forEach(l=>{ctx.save();let g=ctx.createLinearGradient(l.x,l.y,l.x,l.y-l.h);g.addColorStop(0,'rgba(255,0,0,0)');g.addColorStop(0.3,l.isMega?'rgba(255,100,0,1)':'rgba(255,50,50,1)');g.addColorStop(1,l.isMega?'rgba(255,255,0,1)':'rgba(255,200,0,1)');ctx.fillStyle=g;ctx.beginPath();ctx.moveTo(l.x-l.w/2,l.y);ctx.lineTo(l.x+l.w/2,l.y);ctx.lineTo(l.x,l.y-l.h);ctx.closePath();ctx.fill();ctx.fillStyle='rgba(255,255,255,0.8)';ctx.beginPath();ctx.moveTo(l.x-l.w/4,l.y);ctx.lineTo(l.x+l.w/4,l.y);ctx.lineTo(l.x,l.y-l.h);ctx.closePath();ctx.fill();ctx.restore();}); }
|
| 888 |
+
function drawBlackHole() { /* ... */ if(!game.blackHole)return;const{x,y,radius,timeLeft,maxTimeLeft}=game.blackHole;const effScale=Math.min(1,(maxTimeLeft-timeLeft)/(maxTimeLeft*0.2));const curRad=radius*effScale;ctx.save();const ogRad=curRad*(2.5+Math.sin(Date.now()/150)*0.3);let grad=ctx.createRadialGradient(x,y,curRad*0.8,x,y,ogRad);grad.addColorStop(0,'rgba(100,0,150,0)');grad.addColorStop(0.5,`rgba(100,0,150,${0.4*effScale})`);grad.addColorStop(1,'rgba(100,0,150,0)');ctx.fillStyle=grad;ctx.beginPath();ctx.arc(x,y,ogRad,0,Math.PI*2);ctx.fill();ctx.strokeStyle=`rgba(150,50,255,${0.6*effScale})`;ctx.lineWidth=2;const numT=5;for(let i=0;i<numT;i++){ctx.beginPath();const rot=Date.now()/500+(i*Math.PI*2/numT);for(let ang=0;ang<Math.PI*1.5;ang+=0.1){const r=curRad*0.3+ang*curRad*0.2+Math.sin(ang*5+Date.now()/200)*curRad*0.1;const sx=x+Math.cos(ang+rot)*r;const sy=y+Math.sin(ang+rot)*r;if(ang===0)ctx.moveTo(sx,sy);else ctx.lineTo(sx,sy);}ctx.stroke();}ctx.fillStyle=`rgba(0,0,0,${0.9*effScale})`;ctx.beginPath();ctx.arc(x,y,curRad,0,Math.PI*2);ctx.fill();ctx.restore();}
|
| 889 |
+
function drawPowerUpsOnScreen() { /* ... */ game.powerUpsOnScreen.forEach(pu=>{const{x,y,radius,color,text,isRare}=pu;const eR=radius*(1+Math.sin(Date.now()/180+x*0.1)*(isRare?0.15:0.08));if(isRare){ctx.save();const gG=ctx.createRadialGradient(x,y,0,x,y,eR*1.8);gG.addColorStop(0,chroma(color).alpha(0.5).css());gG.addColorStop(1,chroma(color).alpha(0).css());ctx.fillStyle=gG;ctx.beginPath();ctx.arc(x,y,eR*1.8,0,Math.PI*2);ctx.fill();ctx.restore();}ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,eR,0,Math.PI*2);ctx.fill();ctx.fillStyle=chroma.contrast(chroma(color),'black')>chroma.contrast(chroma(color),'white')?'black':'white';ctx.font=`bold ${eR*(text.length>1?0.75:0.95)}px 'Orbitron'`;ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(text,x,y+eR*0.05);});}
|
| 890 |
+
function drawBoss() { /* ... */ if(!game.currentBoss)return;const b=game.currentBoss;const{x,y,width,height,creature,color,animationOffset}=b;const aY=y+Math.sin(Date.now()/400+animationOffset)*(height*0.05);ctx.save();ctx.shadowColor=chroma(color).darken(1).hex();ctx.shadowBlur=20+Math.sin(Date.now()/200)*5;ctx.fillStyle=chroma(color).alpha(0.2).css();ctx.beginPath();ctx.ellipse(x+width/2,aY+height/2+10,width/1.8,height/3,0,0,Math.PI*2);ctx.fill();ctx.shadowColor='transparent';const grad=ctx.createRadialGradient(x+width/2,aY+height/2,0,x+width/2,aY+height/2,Math.max(width,height)/2);grad.addColorStop(0,chroma(color).brighten(1).hex());grad.addColorStop(0.7,color);grad.addColorStop(1,chroma(color).darken(1).hex());ctx.fillStyle=grad;ctx.fillRect(x,aY,width,height);ctx.font=`${Math.min(width,height)*0.6}px Arial`;ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle='#FFF';ctx.fillText(creature,x+width/2,aY+height/2);ctx.restore();}
|
| 891 |
+
function drawPowerUpIndicatorsUI() { /* Same as before */
|
| 892 |
+
const c=document.getElementById('powerupIndicator');c.innerHTML='';Object.entries(game.activePowerUps).forEach(([t,d])=>{const rT=Math.max(0,d.endTime-Date.now());const tD=d.isRare?POWERUP_DURATION*RARE_POWERUP_DURATION_MULTIPLIER:POWERUP_DURATION;const pc=(rT/tD)*100;const iD=document.createElement('div');iD.className='powerup-icon';if(d.isRare)iD.classList.add('rare');const pI=getPowerupInfo(t);iD.textContent=pI.icon;iD.style.background=pI.color;iD.style.setProperty('--time-left',`${pc}%`);c.appendChild(iD);});
|
| 893 |
+
}
|
| 894 |
+
|
| 895 |
+
// --- Update Logic (Slimmed for brevity - assume previous implementations for core logic) ---
|
| 896 |
+
function updatePaddle() { /* ... */ if(game.activePowerUps[RARE_POWERUP_TYPES.TIME_STOP]&&!game.paused)return;paddle.x+=(paddle.targetX-paddle.x)*0.28;paddle.x=Math.max(0,Math.min(paddle.x,GAME_WIDTH-paddle.width));game.balls.forEach(b=>{if(b.attached){b.x=paddle.x+paddle.width/2;b.y=paddle.y-b.radius-1;}}); }
|
| 897 |
+
function updateBalls() { /* ... from previous, ensuring stability */
|
| 898 |
+
for(let i=game.balls.length-1;i>=0;i--){
|
| 899 |
+
const ball=game.balls[i];if(ball.attached)continue;
|
| 900 |
+
let sF=1;if(game.activePowerUps[POWERUP_TYPES.SLOW_MO])sF=0.6;if(game.activePowerUps[RARE_POWERUP_TYPES.TIME_STOP]&&!ball.isPrimary&&!game.paused)sF=0.05;
|
| 901 |
+
ball.x+=ball.dx*sF;ball.y+=ball.dy*sF;
|
| 902 |
+
|
| 903 |
+
if(ball.x<=ball.radius||ball.x>=GAME_WIDTH-ball.radius){ball.dx*=-1;audioManager.playSound(audioManager.definitions.wallHit,{pan:(ball.x/GAME_WIDTH)*2-1});createEffectParticle(ball.x,ball.y,'#87ceeb',5,{sizeRange:[1,2.5]});ball.x=Math.max(ball.radius,Math.min(ball.x,GAME_WIDTH-ball.radius));}
|
| 904 |
+
if(ball.y<=ball.radius){ball.dy*=-1;audioManager.playSound(audioManager.definitions.wallHit,{pan:(ball.x/GAME_WIDTH)*2-1});createEffectParticle(ball.x,ball.y,'#87ceeb',5,{sizeRange:[1,2.5]});ball.y=ball.radius;}
|
| 905 |
+
|
| 906 |
+
if(ball.dy>0&&ball.y+ball.radius>=paddle.y&&ball.y-ball.radius<=paddle.y+paddle.height&&ball.x+ball.radius>=paddle.x&&ball.x-ball.radius<=paddle.x+paddle.width){
|
| 907 |
+
if(game.activePowerUps[POWERUP_TYPES.STICKY_PADDLE]&&ball.isPrimary){ball.attached=true;ball.dx=0;ball.dy=0;}
|
| 908 |
+
else{const hpN=(ball.x-(paddle.x+paddle.width/2))/(paddle.width/2);const bA=hpN*(Math.PI/2.7);const tS=ball.speed*(1+game.level*0.025);ball.dx=tS*Math.sin(bA);ball.dy=-tS*Math.cos(bA);ball.dx+=(Math.random()-0.5)*0.15*tS;}
|
| 909 |
+
audioManager.playSound(audioManager.definitions.paddleHit);createEffectParticle(ball.x,paddle.y,'#00ffff',8,{sizeRange:[1.5,3],upwardBias:-1});ball.y=paddle.y-ball.radius;
|
| 910 |
+
if(ball.isPrimary&&game.activePowerUps[POWERUP_TYPES.SHIELD]){delete game.activePowerUps[POWERUP_TYPES.SHIELD];addBubbleText("SHIELD BROKEN!",paddle.x+paddle.width/2,paddle.y-20,"#FF8C00",16);drawPowerUpIndicatorsUI();}
|
| 911 |
+
if(!ball.isPrimary&&game.combo>0&&!game.activePowerUps[RARE_POWERUP_TYPES.PENTA_BALL]){game.combo=Math.floor(game.combo/2);document.getElementById('combo').textContent=`${game.combo}x`;}
|
| 912 |
+
}
|
| 913 |
+
|
| 914 |
+
const isInv=game.activePowerUps[RARE_POWERUP_TYPES.INVINCIBILITY];
|
| 915 |
+
ball.piercing=game.activePowerUps[POWERUP_TYPES.PIERCING_BALL]||isInv;
|
| 916 |
+
|
| 917 |
+
if(game.currentBoss){const b=game.currentBoss;if(ball.x+ball.radius>b.x&&ball.x-ball.radius<b.x+b.width&&ball.y+ball.radius>b.y&&ball.y-ball.radius<b.y+b.height){if(b.vulnerableTime>0||isInv){b.hp--;game.score+=50*game.level;audioManager.playSound(audioManager.definitions.bossHit);createEffectParticle(ball.x,ball.y,b.color,15,{glow:true,sizeRange:[2,5]});updateBossHealthUI();if(b.hp<=0)bossDefeated();}else{audioManager.playSound(audioManager.definitions.wallHit,{pan:(ball.x/GAME_WIDTH)*2-1});createEffectParticle(ball.x,ball.y,'#AAAAFF',8,{sizeRange:[1,3]});}const dX=ball.x-(b.x+b.width/2),dY=ball.y-(b.y+b.height/2);if(Math.abs(dX)/(b.width/2)>Math.abs(dY)/(b.height/2))ball.dx*=-1;else ball.dy*=-1;}}
|
| 918 |
+
else{for(let j=blocks.length-1;j>=0;j--){const blk=blocks[j];if(ball.x+ball.radius>blk.x&&ball.x-ball.radius<blk.x+blk.width&&ball.y+ball.radius>blk.y&&ball.y-ball.radius<blk.y+blk.height){
|
| 919 |
+
if(blk.isDangerous&&ball.isPrimary&&!isInv&&!game.activePowerUps[POWERUP_TYPES.SHIELD]){game.lives--;game.isFlawlessLevel=false;audioManager.playSound(audioManager.definitions.loseLife);addBubbleText("-1 LIFE!",ball.x,ball.y,"#ff4444",20);createEffectParticle(ball.x,ball.y,"#ff0000",20,{glow:true});updateHealthBarUI();if(game.lives<=0){gameOver();return;}else{resetPrimaryBallAndPaddleState();continue;}}
|
| 920 |
+
else if(blk.isDangerous&&ball.isPrimary&&game.activePowerUps[POWERUP_TYPES.SHIELD]){delete game.activePowerUps[POWERUP_TYPES.SHIELD];addBubbleText("SHIELD PROTECTED!",paddle.x+paddle.width/2,paddle.y-20,"#00FFFF",16);drawPowerUpIndicatorsUI();}
|
| 921 |
+
audioManager.playSound(audioManager.definitions.blockHit);if(blocks.length===game.initialBlockCount)unlockAchievement('FIRST_BREAK');updatePlayerStat('totalBlocksBroken',1);createEffectParticle(blk.x+blk.width/2,blk.y+blk.height/2,blk.color,10,{sizeRange:[1,3]});
|
| 922 |
+
if(!ball.piercing){const bX=ball.x,bY=ball.y,blX=blk.x+blk.width/2,blY=blk.y+blk.height/2,dX=bX-blX,dY=bY-blY,wTH=(ball.radius+blk.width/2),hTH=(ball.radius+blk.height/2);if(Math.abs(dX)/wTH>Math.abs(dY)/hTH){ball.dx*=-1;ball.x+=ball.dx>0?0.1:-0.1;}else{ball.dy*=-1;ball.y+=ball.dy>0?0.1:-0.1;}}
|
| 923 |
+
blk.hits--;game.score+=5*game.level*(blk.isDangerous?1.5:1);
|
| 924 |
+
if(blk.hits<=0){game.score+=(10+(ball.piercing?5:0))*game.level*(blk.isDangerous?1.5:1);blocks.splice(j,1);updateLevelProgressUI();if(Math.random()<POWERUP_DROP_CHANCE)spawnPowerUpItem(blk.x+blk.width/2,blk.y+blk.height/2);}
|
| 925 |
+
updateCombo();if(!ball.piercing)break;}}}
|
| 926 |
+
|
| 927 |
+
if(ball.y>GAME_HEIGHT+ball.radius*3){if(ball.isPrimary){if(!isInv&&!game.activePowerUps[POWERUP_TYPES.SHIELD]){game.lives--;game.isFlawlessLevel=false;}else if(game.activePowerUps[POWERUP_TYPES.SHIELD]){delete game.activePowerUps[POWERUP_TYPES.SHIELD];addBubbleText("SHIELD SAVED BALL!",paddle.x+paddle.width/2,paddle.y-20,"#00FFFF",16);drawPowerUpIndicatorsUI();}audioManager.playSound(audioManager.definitions.loseLife);updateHealthBarUI();if(game.lives<=0&&!isInv){gameOver();return;}else{resetPrimaryBallAndPaddleState();}}else{game.balls.splice(i,1);}}
|
| 928 |
+
}
|
| 929 |
+
}
|
| 930 |
+
function updateCombo() { /* ... */ const now=Date.now();if(now-game.lastHitTime>COMBO_TIMEOUT)game.combo=0;game.lastHitTime=now;game.combo++;document.getElementById('combo').textContent=`${game.combo}x`;if(game.combo>=3){const pts=Math.min(25,game.combo)*game.level;game.score+=pts;addBubbleText(`+${pts} COMBO!`,GAME_WIDTH/2,GAME_HEIGHT*0.4,'#ffff00',16+Math.min(10,game.combo));}if(game.combo>=5&&game.combo%2===(game.combo<10?1:0)){audioManager.playSound(audioManager.definitions.combo(game.combo));if(game.combo>=5){const cm=document.getElementById('comboMeter');cm.textContent=`${game.combo}X COMBO!`;cm.style.opacity='1';cm.style.transform='translate(-50%,-50%)scale(1.1)';cm.style.color=game.combo>=15?'#ff69b4':(game.combo>=10?'#ffaa00':'#ffff00');setTimeout(()=>{cm.style.opacity='0';cm.style.transform='translate(-50%,-50%)scale(0.8)';},800);}}if(game.combo>=15)unlockAchievement('COMBO_15X');}
|
| 931 |
+
function updateLasers() { /* ... */ for(let i=game.lasers.length-1;i>=0;i--){let l=game.lasers[i];l.y-=l.speed;if(l.y+l.h<0){game.lasers.splice(i,1);continue;}for(let j=blocks.length-1;j>=0;j--){let b=blocks[j];if(l.x>=b.x&&l.x<=b.x+b.width&&l.y<=b.y+b.height&&l.y+l.h>=b.y){createEffectParticle(l.x,b.y+b.height/2,l.isMega?'#FFA500':'#ffff00',10,{glow:true});if(l.isMega)b.hits=0;else b.hits-=2;if(b.hits<=0){blocks.splice(j,1);updateLevelProgressUI();}game.score+=(l.isMega?25:15)*game.level;updateCombo();game.lasers.splice(i,1);break;}}if(game.currentBoss&&game.lasers[i]){const boss=game.currentBoss;const laser=game.lasers[i];if(laser.x>=boss.x&&laser.x<=boss.x+boss.width&&laser.y<=boss.y+boss.height&&laser.y+laser.height>=boss.y){if(boss.vulnerableTime>0){boss.hp-=(laser.isMega?3:1);game.score+=(laser.isMega?100:75)*game.level;audioManager.playSound(audioManager.definitions.bossHit);createEffectParticle(laser.x,laser.y,boss.color,10,{glow:true,sizeRange:[1.5,4]});updateBossHealthUI();if(boss.hp<=0)bossDefeated();}game.lasers.splice(i,1);}}}}
|
| 932 |
+
function updateBoss() { /* ... */ if(!game.currentBoss)return;const b=game.currentBoss;b.x+=b.dx;if(b.x<=0||b.x+b.width>=GAME_WIDTH){b.dx*=-1;b.vulnerableTime=120;}if(b.vulnerableTime>0)b.vulnerableTime--;if(Date.now()-b.lastAttackTime>3000&&b.vulnerableTime<=0){addBubbleText("BOSS ATTACK!",b.x+b.width/2,b.y+b.height,"#FF5555",18);b.lastAttackTime=Date.now();}}
|
| 933 |
+
function updateBlackHole() { /* ... */ if(!game.blackHole)return;const bh=game.blackHole;bh.timeLeft--;if(bh.timeLeft<=0){game.blackHole=null;return;}game.balls.forEach(b=>{if(b.attached)return;const dx=bh.x-b.x,dy=bh.y-b.y;const dSq=dx*dx+dy*dy;if(dSq<Math.pow(bh.radius*5,2)&&dSq>10){const d=Math.sqrt(dSq);const f=bh.strength/dSq;b.dx+=dx/d*f;b.dy+=dy/d*f;const s=Math.sqrt(b.dx*b.dx+b.dy*b.dy);if(s>b.speed*1.5){b.dx=(b.dx/s)*b.speed*1.5;b.dy=(b.dy/s)*b.speed*1.5;}}});for(let i=blocks.length-1;i>=0;i--){const bl=blocks[i];const bX=bl.x+bl.width/2,bY=bl.y+bl.height/2;const dx=bh.x-bX,dy=bh.y-bY;const dSq=dx*dx+dy*dy;if(dSq<Math.pow(bh.radius*3,2)){if(dSq<Math.pow(bh.radius*0.8,2)){audioManager.playSound(audioManager.definitions.blackHoleAbsorb);createEffectParticle(bX,bY,chroma(bl.color).brighten(1).hex(),15,{upwardBias:0,speedRange:[1,3]});blocks.splice(i,1);updateLevelProgressUI();game.score+=20*game.level;updateCombo();}}}}
|
| 934 |
+
function updatePowerUpsOnScreen() { /* ... */ for(let i=game.powerUpsOnScreen.length-1;i>=0;i--){let pu=game.powerUpsOnScreen[i];pu.y+=pu.dy;if(pu.y-pu.radius>GAME_HEIGHT){game.powerUpsOnScreen.splice(i,1);continue;}if(pu.y+pu.radius>=paddle.y&&pu.y-pu.radius<=paddle.y+paddle.height&&pu.x+pu.radius>=paddle.x&&pu.x-pu.radius<=paddle.x+paddle.width){activatePowerUp(pu.type,pu.isRare);updatePlayerStat('totalPowerupsCollected',1);if(game.playerStats.totalPowerupsCollected>=5)unlockAchievement('POWERUP_MASTER');addBubbleText((pu.isRare?"RARE: ":"")+pu.type.replace(/_/g,' ')+"!",pu.x,pu.y,pu.isRare?"gold":"#00dddd",16);game.powerUpsOnScreen.splice(i,1);}}}
|
| 935 |
+
function activatePowerUp(type, isRare) { /* ... */ isRare?audioManager.playSound(audioManager.definitions.rarePowerUpCollect):audioManager.playSound(audioManager.definitions.powerUpCollect);const dur=(isRare?POWERUP_DURATION*RARE_POWERUP_DURATION_MULTIPLIER:POWERUP_DURATION);if(game.activePowerUps[type])game.activePowerUps[type].endTime=Math.max(game.activePowerUps[type].endTime,Date.now()+dur);else game.activePowerUps[type]={endTime:Date.now()+dur,isRare,data:{}};switch(type){case POWERUP_TYPES.WIDE_PADDLE:paddle.width=PADDLE_DEFAULT_WIDTH*1.6;break;case POWERUP_TYPES.MULTI_BALL:addNewBall();break;case RARE_POWERUP_TYPES.PENTA_BALL:for(let k=0;k<4;k++)addNewBall();break;case RARE_POWERUP_TYPES.INVINCIBILITY:game.lives=Math.min(game.lives+1,game.maxLives);addBubbleText("+1 LIFE!",paddle.x+paddle.width/2,paddle.y,"#ffff00",20);updateHealthBarUI();break;case RARE_POWERUP_TYPES.BLACK_HOLE:audioManager.playSound(audioManager.definitions.blackHoleOpen);game.blackHole={x:GAME_WIDTH/2,y:GAME_HEIGHT/3,radius:GAME_WIDTH*0.06,strength:GAME_WIDTH*0.12,timeLeft:360,maxTimeLeft:360};break;case RARE_POWERUP_TYPES.BLOCK_BOMB:const bX=paddle.x+paddle.width/2;const bY=paddle.y-20;createEffectParticle(bX,bY,'#FF8C00',30,{sizeRange:[3,8],glow:true,speedRange:[5,10]});const bR=GAME_WIDTH*0.2;for(let i=blocks.length-1;i>=0;i--){const b=blocks[i];const dSq=Math.pow(b.x+b.width/2-bX,2)+Math.pow(b.y+b.height/2-bY,2);if(dSq<Math.pow(bR,2)){game.score+=10*game.level;blocks.splice(i,1);updateLevelProgressUI();createEffectParticle(b.x+b.width/2,b.y+b.height/2,b.color,5);}}updateCombo();break;}}
|
| 936 |
+
function updateActivePowerUps() { /* ... */ const now=Date.now();Object.entries(game.activePowerUps).forEach(([t,d])=>{if(now>d.endTime){deactivatePowerUp(t);delete game.activePowerUps[t];}});drawPowerUpIndicatorsUI();}
|
| 937 |
+
function deactivatePowerUp(type) { /* ... */ switch(type){case POWERUP_TYPES.WIDE_PADDLE:paddle.width=PADDLE_DEFAULT_WIDTH;break;case POWERUP_TYPES.PIERCING_BALL:game.balls.forEach(b=>b.piercing=false);break;}}
|
| 938 |
+
function fireLaser() { /* ... */ const iM=game.activePowerUps[RARE_POWERUP_TYPES.MEGA_LASER];if(game.lasers.length>=(iM?2:4)||!(game.activePowerUps[POWERUP_TYPES.LASER_PADDLE]||iM))return;audioManager.playSound(audioManager.definitions.laserFire);const lW=paddle.width*(iM?0.1:0.05);const lS=GAME_HEIGHT*(iM?0.025:0.02);const lH=PADDLE_HEIGHT*(iM?2.5:1.8);if(iM){game.lasers.push({x:paddle.x+paddle.width/2,y:paddle.y,width:lW*1.5,height:lH,speed:lS,isMega:true,h:lH});}else{game.lasers.push({x:paddle.x+paddle.width*0.2,y:paddle.y,width:lW,height:lH,speed:lS,isMega:false,h:lH});game.lasers.push({x:paddle.x+paddle.width*0.8,y:paddle.y,width:lW,height:lH,speed:lS,isMega:false,h:lH});}}
|
| 939 |
+
function addNewBall() { /* ... */ const pB=game.balls.find(b=>b.isPrimary);if(!pB||game.balls.length>=6)return;const nB={...pB};nB.isPrimary=false;nB.attached=false;nB.x=pB.x+(Math.random()-0.5)*pB.radius;nB.y=pB.y-pB.radius;nB.dx=(Math.random()-0.5)*pB.speed*0.6;nB.dy=-pB.speed*(0.7+Math.random()*0.3);nB.trail=[];game.balls.push(nB);}
|
| 940 |
+
|
| 941 |
+
// --- Game State Management ---
|
| 942 |
+
function resetPrimaryBallAndPaddleState() { /* ... */ const pB=game.balls.find(b=>b.isPrimary);if(pB){pB.x=paddle.x+paddle.width/2;pB.y=paddle.y-pB.radius*1.5;pB.dx=0;pB.dy=0;pB.attached=true;pB.piercing=false;pB.trail=[];}if(game.balls.some(b=>b.isPrimary&&b.attached))game.balls=game.balls.filter(b=>b.isPrimary);if(!game.activePowerUps[POWERUP_TYPES.WIDE_PADDLE])paddle.width=PADDLE_DEFAULT_WIDTH;game.combo=0;document.getElementById('combo').textContent='0x';}
|
| 943 |
+
async function showLevelTransitionScreen(levelNumber, text) { /* Combined transition screen */ const s=document.getElementById('levelTransitionScreen');document.getElementById('levelTransitionNumber').textContent=text||`LEVEL ${levelNumber}`;s.classList.add('active');await new Promise(r=>setTimeout(r,1500));s.classList.remove('active');await new Promise(r=>setTimeout(r,400));}
|
| 944 |
+
async function showBossScreen(type, name) { /* For intro/defeat */ const s=document.getElementById(type==='intro'?'bossIntroScreen':'bossDefeatedScreen');document.getElementById(type==='intro'?'bossIntroName':'bossDefeatedName').textContent=type==='intro'?name:`${name} DEFEATED!`;s.classList.add('active');if(type==='intro')document.getElementById('bossWarning').style.display='block';await new Promise(r=>setTimeout(r,2500));s.classList.remove('active');if(type==='intro')document.getElementById('bossWarning').style.display='none';await new Promise(r=>setTimeout(r,400));}
|
| 945 |
+
|
| 946 |
+
async function nextLevel() {
|
| 947 |
+
if(game.levelTransitioning) return; game.levelTransitioning = true;
|
| 948 |
+
if(game.isFlawlessLevel && game.level > 0) unlockAchievement('NO_LIVES_LOST_LEVEL');
|
| 949 |
+
|
| 950 |
+
await showLevelTransitionScreen(game.level + 1);
|
| 951 |
+
audioManager.playSound(audioManager.definitions.levelUp);
|
| 952 |
+
|
| 953 |
+
game.level++; game.levelStartTime = Date.now();
|
| 954 |
+
updatePlayerStat('highestLevelReached', Math.max(game.playerStats.highestLevelReached || 0, game.level));
|
| 955 |
+
|
| 956 |
+
if(game.level > MAX_LEVELS){gameWon();game.levelTransitioning=false;return;}
|
| 957 |
+
if(game.level===3)unlockAchievement('LEVEL_3');
|
| 958 |
+
|
| 959 |
+
game.balls.forEach(b=>b.speed+=(GAME_HEIGHT*0.0005)*(game.difficulty==='hardcore'?1.8:1.2));
|
| 960 |
+
Object.entries(game.activePowerUps).forEach(([t,d])=>{if(!d.isRare||t===RARE_POWERUP_TYPES.BLACK_HOLE){deactivatePowerUp(t);delete game.activePowerUps[t];}});
|
| 961 |
+
game.powerUpsOnScreen=[];game.lasers=[];if(game.blackHole)game.blackHole=null;
|
| 962 |
+
|
| 963 |
+
paddle.width=PADDLE_DEFAULT_WIDTH;initPrimaryBall();
|
| 964 |
+
|
| 965 |
+
if(BOSS_LEVELS.includes(game.level)){await showBossScreen('intro',BOSS_CREATURES[game.level]);createBoss(game.level);game.initialBlockCount=0;}
|
| 966 |
+
else{game.currentBoss=null;document.getElementById('bossHealthContainer').style.display='none';document.getElementById('bossName').style.display='none';createBlocks();}
|
| 967 |
+
|
| 968 |
+
updateDepthMeterUI();updateLevelProgressUI();
|
| 969 |
+
if(game.level===1&& (game.playerStats.gamesPlayed || 0) <=1)showTutorialMessage("Click to launch the Pearl!",5000);
|
| 970 |
+
game.levelTransitioning=false;
|
| 971 |
+
}
|
| 972 |
+
|
| 973 |
+
async function bossDefeated() {
|
| 974 |
+
const bossName = game.currentBoss.creature;
|
| 975 |
+
await showBossScreen('defeat', bossName);
|
| 976 |
+
audioManager.playSound(audioManager.definitions.bossDefeat);
|
| 977 |
+
addBubbleText(`${bossName} Vanquished!`, GAME_WIDTH/2, GAME_HEIGHT*0.4, '#FFD700', 30, 150);
|
| 978 |
+
game.score += 2000 * game.level; updatePlayerStat('bossesDefeated', 1);
|
| 979 |
+
if (game.level === BOSS_LEVELS[0]) unlockAchievement('LEVEL_5_BOSS');
|
| 980 |
+
game.currentBoss=null;document.getElementById('bossHealthContainer').style.display='none';document.getElementById('bossName').style.display='none';
|
| 981 |
+
for(let i=0;i<3;i++)spawnPowerUpItem(GAME_WIDTH/2+(i-1)*50,GAME_HEIGHT/3);
|
| 982 |
+
// Next level will be triggered by gameLoop detecting blocks.length === 0 (since boss is now null)
|
| 983 |
+
}
|
| 984 |
+
|
| 985 |
+
function updateHighScore() { if(game.score>game.highScore){game.highScore=game.score;localStorage.setItem('deepSeaBreakoutHighScore_v2',game.highScore);if(game.score>=10000)unlockAchievement('HIGH_SCORE_10K');return true;}return false;}
|
| 986 |
+
function gameOver() { game.running=false;updatePlayerStat('totalGameTime', (Date.now() - game.gameSessionStartTime)/1000);savePlayerStats();const nH=updateHighScore();document.getElementById('finalScore').textContent=game.score;document.getElementById('highScoreText').textContent=nH?`NEW HIGH SCORE: ${game.highScore}`:`High Score: ${game.highScore}`;showScreen('gameOver');audioManager.playSound(audioManager.definitions.gameOver);}
|
| 987 |
+
function gameWon() { game.running=false;updatePlayerStat('totalGameTime', (Date.now() - game.gameSessionStartTime)/1000);savePlayerStats();unlockAchievement('GAME_WON');const nH=updateHighScore();document.getElementById('winScore').textContent=game.score;document.getElementById('totalLevelsWon').textContent=MAX_LEVELS;document.getElementById('winHighScoreText').textContent=nH?`NEW HIGH SCORE: ${game.highScore}!`:`High Score: ${game.highScore}`;showScreen('gameWon');audioManager.playSound(audioManager.definitions.gameWon);}
|
| 988 |
+
|
| 989 |
+
async function initGame(difficulty = 'normal') {
|
| 990 |
+
const ldSc=document.getElementById('loadingScreen');const ldTxt=document.getElementById('loadingText');const ldPr=document.getElementById('loadingProgress');
|
| 991 |
+
ldSc.classList.remove('hidden');ldTxt.textContent="INITIALIZING AUDIO...";ldPr.style.width='10%';await audioManager.init();
|
| 992 |
+
ldTxt.textContent="PREPARING GAME STATE...";ldPr.style.width='30%';await new Promise(r=>setTimeout(r,100));
|
| 993 |
+
|
| 994 |
+
game.score=0;game.difficulty=difficulty;game.maxLives=game.difficulty==='hardcore'?2:3;game.lives=game.maxLives;game.level=1;game.paused=false;game.running=true;game.levelTransitioning=false;game.initialBlockCount=0;game.particles=[];game.powerUpsOnScreen=[];game.activePowerUps={};game.combo=0;game.balls=[];game.lasers=[];game.bubbleTexts=[];game.blackHole=null;game.currentBoss=null;game.currentBackgroundHue=180;game.gameTime=0;game.gameSessionStartTime=Date.now();game.levelStartTime=Date.now();game.isFlawlessLevel=true;
|
| 995 |
+
game.highScore = parseInt(localStorage.getItem('deepSeaBreakoutHighScore_v2') || '0');
|
| 996 |
+
|
| 997 |
+
if(!game.paddleSkin||PADDLE_SKINS.findIndex(s=>s.id===game.paddleSkin.id)===-1){game.paddleSkin=PADDLE_SKINS.find(s=>s.id===localStorage.getItem('deepSeaBreakoutPaddleSkin_v2'))||PADDLE_SKINS[0];}
|
| 998 |
+
updatePlayerStat('gamesPlayed',1);defineAchievements();initPlayerStats();
|
| 999 |
+
|
| 1000 |
+
paddle={x:GAME_WIDTH/2-PADDLE_DEFAULT_WIDTH/2,y:GAME_HEIGHT-PADDLE_HEIGHT*2.5,width:PADDLE_DEFAULT_WIDTH,height:PADDLE_HEIGHT,targetX:GAME_WIDTH/2-PADDLE_DEFAULT_WIDTH/2};
|
| 1001 |
+
initPrimaryBall();
|
| 1002 |
+
|
| 1003 |
+
ldTxt.textContent="BUILDING LEVEL...";ldPr.style.width='60%';await new Promise(r=>setTimeout(r,100));createBlocks();
|
| 1004 |
+
ldTxt.textContent="SUMMONING SEA LIFE...";ldPr.style.width='80%';await new Promise(r=>setTimeout(r,100));createBackgroundElements();
|
| 1005 |
+
|
| 1006 |
+
document.querySelectorAll('.dialog-box, .menu, .settings-panel, .leaderboard, .shop, .daily-challenge, .achievement-system, .tutorial-screen, .stats-screen, .credits, .customization, .profile, .multiplayer-info, .matchmaking, #difficultySelectScreen').forEach(el=>el.style.display='none');
|
| 1007 |
+
document.getElementById('bossHealthContainer').style.display='none';document.getElementById('bossName').style.display='none';document.getElementById('pauseMessage').style.display='none';
|
| 1008 |
+
|
| 1009 |
+
updateHealthBarUI();updateDepthMeterUI();updateLevelProgressUI();
|
| 1010 |
+
['score','level','combo'].forEach(id=>document.getElementById(id).textContent=game[id]||'0');
|
| 1011 |
+
document.getElementById('maxLevels').textContent=MAX_LEVELS;
|
| 1012 |
+
drawPowerUpIndicatorsUI();
|
| 1013 |
+
|
| 1014 |
+
ldTxt.textContent="GAME READY!";ldPr.style.width='100%';await new Promise(r=>setTimeout(r,200));ldSc.classList.add('hidden');game.currentScreen='game';
|
| 1015 |
+
|
| 1016 |
+
if((game.playerStats.gamesPlayed||0)<=1)showTutorialMessage("Move mouse to control paddle!",5000);
|
| 1017 |
+
|
| 1018 |
+
if(animationFrameId)cancelAnimationFrame(animationFrameId);
|
| 1019 |
+
lastTime=performance.now();
|
| 1020 |
+
gameLoop(lastTime);
|
| 1021 |
+
}
|
| 1022 |
+
|
| 1023 |
+
// --- Game Loop ---
|
| 1024 |
+
let lastTime = 0; let animationFrameId; let frameCount = 0;
|
| 1025 |
+
|
| 1026 |
+
function gameLoop(timestamp) {
|
| 1027 |
+
animationFrameId=requestAnimationFrame(gameLoop); // Loop continues even if paused, but returns early
|
| 1028 |
+
if(!game.running)return;
|
| 1029 |
+
|
| 1030 |
+
const deltaTime=(timestamp-lastTime)/1000||0;lastTime=timestamp;
|
| 1031 |
+
if (game.currentScreen === 'game' && !game.paused && !game.levelTransitioning) {
|
| 1032 |
+
game.gameTime += deltaTime;
|
| 1033 |
+
}
|
| 1034 |
+
|
| 1035 |
+
if(game.paused || game.levelTransitioning || game.currentScreen !== 'game') { // Only draw if paused but game is active screen, or if transitioning
|
| 1036 |
+
if (game.paused && game.currentScreen === 'game') {
|
| 1037 |
+
// Optionally draw a static "paused" frame of the game if desired
|
| 1038 |
+
// For now, only the HTML pause message is shown.
|
| 1039 |
+
}
|
| 1040 |
+
return; // Skip updates and full redraw if paused, transitioning, or not on game screen
|
| 1041 |
+
}
|
| 1042 |
+
|
| 1043 |
+
ctx.clearRect(0,0,GAME_WIDTH,GAME_HEIGHT);
|
| 1044 |
+
updateBackgroundElements();updatePaddle();updateBalls();updateBoss();updateLasers();updateParticles();createBallTrailParticles();updatePowerUpsOnScreen();updateActivePowerUps();updateBlackHole();
|
| 1045 |
+
drawBackgroundElements();drawBlocks();drawBoss();drawPowerUpsOnScreen();drawLasers();drawBlackHole();drawBallTrails();drawBalls();drawPaddle();drawParticles();drawBubbleTexts();
|
| 1046 |
+
|
| 1047 |
+
['score','level'].forEach(id=>document.getElementById(id).textContent=game[id]); // Combo is updated in its own func
|
| 1048 |
+
|
| 1049 |
+
if(!game.currentBoss&&blocks.length===0&&game.running&&!game.levelTransitioning){
|
| 1050 |
+
nextLevel(); // This is async and sets game.levelTransitioning
|
| 1051 |
+
}
|
| 1052 |
+
frameCount++;
|
| 1053 |
+
}
|
| 1054 |
+
|
| 1055 |
+
// --- Event Listeners & UI Helpers ---
|
| 1056 |
+
let eventController = new AbortController();
|
| 1057 |
+
function setupEventListeners() { /* ... same as before ... */
|
| 1058 |
+
eventController.abort();eventController=new AbortController();const{signal}=eventController;
|
| 1059 |
+
canvas.addEventListener('mousemove',e=>{if(!game.paused&&game.currentScreen==='game'){const r=canvas.getBoundingClientRect();paddle.targetX=e.clientX-r.left-paddle.width/2;mouseX=e.clientX-r.left;}},{signal});
|
| 1060 |
+
canvas.addEventListener('click',async()=>{if(!game.running||game.paused||game.currentScreen!=='game'||game.levelTransitioning)return;await audioManager.init();let bL=false;game.balls.forEach(b=>{if(b.attached){const aO=(Math.random()-0.5)*Math.PI*0.1;const bA=-Math.PI/2;b.dx=b.speed*Math.sin(bA+aO);b.dy=b.speed*Math.cos(bA+aO);b.attached=false;bL=true;}});if(!bL&&(game.activePowerUps[POWERUP_TYPES.LASER_PADDLE]||game.activePowerUps[RARE_POWERUP_TYPES.MEGA_LASER]))fireLaser();},{signal});
|
| 1061 |
+
window.addEventListener('keydown',e=>{const k=e.key.toLowerCase();
|
| 1062 |
+
if(k==='p'&&game.running&&game.currentScreen==='game'){game.paused=!game.paused;document.getElementById('pauseMessage').style.display=game.paused?'block':'none'; if(!game.paused && animationFrameId){ /* If unpausing ensure loop continues */ lastTime = performance.now(); /* Reset lastTime to avoid large deltaTime jump */ requestAnimationFrame(gameLoop);}}
|
| 1063 |
+
if(k==='m'){const iM=audioManager.toggleMute();showNotification(iM?"Audio Muted":"Audio Unmuted","",iM?"🔇":"🔊",1500);}
|
| 1064 |
+
if(k==='n'&&e.shiftKey&&game.running&&game.currentScreen==='game'){if(game.currentBoss)bossDefeated();else blocks=[];console.log("Debug: Level skip.");}
|
| 1065 |
+
},{signal});
|
| 1066 |
+
document.getElementById('masterVolumeSlider').addEventListener('input',(e)=>audioManager.setVolume('master',parseFloat(e.target.value)),{signal});
|
| 1067 |
+
document.getElementById('sfxVolumeSlider').addEventListener('input',(e)=>audioManager.setVolume('sfx',parseFloat(e.target.value)),{signal});
|
| 1068 |
+
document.getElementById('themeSelector').addEventListener('change',(e)=>{game.currentTheme=e.target.value;localStorage.setItem('deepSeaTheme_v2',game.currentTheme);},{signal});
|
| 1069 |
+
const comboUI=document.querySelector('.ui div:nth-child(4)');if(comboUI){comboUI.addEventListener('mouseenter',e=>{const tt=document.getElementById('tooltip');tt.textContent="Consecutive hits score bonus points!";tt.style.left=`${e.clientX+10}px`;tt.style.top=`${e.clientY-30}px`;tt.style.display='block';setTimeout(()=>tt.classList.add('show'),10);},{signal});comboUI.addEventListener('mouseleave',()=>{const tt=document.getElementById('tooltip');tt.classList.remove('show');setTimeout(()=>tt.style.display='none',200);},{signal});}
|
| 1070 |
+
}
|
| 1071 |
+
|
| 1072 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 1073 |
+
sessionStartTime=Date.now();
|
| 1074 |
+
game.highScore = parseInt(localStorage.getItem('deepSeaBreakoutHighScore_v2') || '0');
|
| 1075 |
+
populatePaddleSkins();
|
| 1076 |
+
game.currentTheme=localStorage.getItem('deepSeaTheme_v2')||'deepSea';document.getElementById('themeSelector').value=game.currentTheme;
|
| 1077 |
+
showScreen('mainMenu'); setupEventListeners();
|
| 1078 |
+
window.showDifficultySelect = () => showScreen('difficultySelectScreen');
|
| 1079 |
+
window.showScreenWithPlaceholder = showScreenWithPlaceholder; // Make it global for HTML onclick
|
| 1080 |
+
});
|
| 1081 |
+
|
| 1082 |
+
</script>
|
| 1083 |
+
</body>
|
| 1084 |
+
</html>
|