Spaces:
Running
Running
Update index.html
Browse files- index.html +2472 -18
index.html
CHANGED
|
@@ -1,19 +1,2473 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 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, maximum-scale=1.0, user-scalable=no">
|
| 6 |
+
<title>Doge Whale Game</title>
|
| 7 |
+
<style>
|
| 8 |
+
body {
|
| 9 |
+
margin: 0;
|
| 10 |
+
padding: 0;
|
| 11 |
+
font-family: Arial, sans-serif;
|
| 12 |
+
background: linear-gradient(to bottom, #4169E1, #000080);
|
| 13 |
+
color: white;
|
| 14 |
+
height: 100vh;
|
| 15 |
+
overflow: hidden;
|
| 16 |
+
display: flex;
|
| 17 |
+
flex-direction: column;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
header {
|
| 21 |
+
background-color: rgba(0, 0, 128, 0.7);
|
| 22 |
+
padding: 10px 20px;
|
| 23 |
+
display: flex;
|
| 24 |
+
justify-content: space-between;
|
| 25 |
+
align-items: center;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
h1 {
|
| 29 |
+
margin: 0;
|
| 30 |
+
font-size: 24px;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.user-info {
|
| 34 |
+
display: flex;
|
| 35 |
+
align-items: center;
|
| 36 |
+
gap: 10px;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.coin-display {
|
| 40 |
+
display: flex;
|
| 41 |
+
align-items: center;
|
| 42 |
+
gap: 5px;
|
| 43 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 44 |
+
padding: 5px 10px;
|
| 45 |
+
border-radius: 20px;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.coin-icon {
|
| 49 |
+
width: 20px;
|
| 50 |
+
height: 20px;
|
| 51 |
+
background-color: gold;
|
| 52 |
+
border-radius: 50%;
|
| 53 |
+
display: flex;
|
| 54 |
+
justify-content: center;
|
| 55 |
+
align-items: center;
|
| 56 |
+
font-weight: bold;
|
| 57 |
+
color: #333;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.nav-buttons {
|
| 61 |
+
display: flex;
|
| 62 |
+
gap: 10px;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.nav-button {
|
| 66 |
+
background-color: rgba(65, 105, 225, 0.7);
|
| 67 |
+
border: 2px solid #87CEEB;
|
| 68 |
+
color: white;
|
| 69 |
+
padding: 5px 15px;
|
| 70 |
+
border-radius: 20px;
|
| 71 |
+
cursor: pointer;
|
| 72 |
+
transition: all 0.3s;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.nav-button:hover {
|
| 76 |
+
background-color: rgba(135, 206, 235, 0.7);
|
| 77 |
+
transform: scale(1.05);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
main {
|
| 81 |
+
flex: 1;
|
| 82 |
+
display: flex;
|
| 83 |
+
justify-content: center;
|
| 84 |
+
align-items: center;
|
| 85 |
+
position: relative;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
#game-container {
|
| 89 |
+
position: relative;
|
| 90 |
+
width: 800px;
|
| 91 |
+
height: 500px;
|
| 92 |
+
background-color: rgba(30, 144, 255, 0.3);
|
| 93 |
+
border-radius: 10px;
|
| 94 |
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
| 95 |
+
overflow: hidden;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
#game-canvas {
|
| 99 |
+
width: 100%;
|
| 100 |
+
height: 100%;
|
| 101 |
+
display: block;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
.overlay {
|
| 105 |
+
position: absolute;
|
| 106 |
+
top: 0;
|
| 107 |
+
left: 0;
|
| 108 |
+
width: 100%;
|
| 109 |
+
height: 100%;
|
| 110 |
+
background-color: rgba(0, 0, 128, 0.7);
|
| 111 |
+
display: flex;
|
| 112 |
+
flex-direction: column;
|
| 113 |
+
justify-content: center;
|
| 114 |
+
align-items: center;
|
| 115 |
+
z-index: 10;
|
| 116 |
+
opacity: 0;
|
| 117 |
+
pointer-events: none;
|
| 118 |
+
transition: opacity 0.5s;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.overlay.active {
|
| 122 |
+
opacity: 1;
|
| 123 |
+
pointer-events: all;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.overlay h2 {
|
| 127 |
+
font-size: 36px;
|
| 128 |
+
margin-bottom: 20px;
|
| 129 |
+
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.overlay p {
|
| 133 |
+
font-size: 18px;
|
| 134 |
+
margin-bottom: 30px;
|
| 135 |
+
max-width: 80%;
|
| 136 |
+
text-align: center;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
.button {
|
| 140 |
+
background-color: #4169E1;
|
| 141 |
+
color: white;
|
| 142 |
+
border: none;
|
| 143 |
+
padding: 10px 30px;
|
| 144 |
+
font-size: 18px;
|
| 145 |
+
border-radius: 30px;
|
| 146 |
+
cursor: pointer;
|
| 147 |
+
transition: all 0.3s;
|
| 148 |
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.button:hover {
|
| 152 |
+
background-color: #87CEEB;
|
| 153 |
+
transform: scale(1.05);
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
#hud {
|
| 157 |
+
position: absolute;
|
| 158 |
+
top: 10px;
|
| 159 |
+
left: 10px;
|
| 160 |
+
display: flex;
|
| 161 |
+
flex-direction: column;
|
| 162 |
+
gap: 10px;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
.bar-container {
|
| 166 |
+
width: 200px;
|
| 167 |
+
height: 20px;
|
| 168 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 169 |
+
border-radius: 10px;
|
| 170 |
+
overflow: hidden;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
#health-bar {
|
| 174 |
+
height: 100%;
|
| 175 |
+
width: 100%;
|
| 176 |
+
background: linear-gradient(to right, #FF4500, #FF8C00);
|
| 177 |
+
transition: width 0.3s;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
#power-bar {
|
| 181 |
+
height: 100%;
|
| 182 |
+
width: 0%;
|
| 183 |
+
background: linear-gradient(to right, #4169E1, #00BFFF);
|
| 184 |
+
transition: width 0.3s;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
#score-display {
|
| 188 |
+
position: absolute;
|
| 189 |
+
top: 10px;
|
| 190 |
+
right: 10px;
|
| 191 |
+
font-size: 36px;
|
| 192 |
+
font-weight: bold;
|
| 193 |
+
color: white;
|
| 194 |
+
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
#combo-display {
|
| 198 |
+
position: absolute;
|
| 199 |
+
top: 50px;
|
| 200 |
+
right: 10px;
|
| 201 |
+
font-size: 24px;
|
| 202 |
+
color: gold;
|
| 203 |
+
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
|
| 204 |
+
opacity: 0;
|
| 205 |
+
transition: opacity 0.3s;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
#power-up-notification {
|
| 209 |
+
position: absolute;
|
| 210 |
+
top: 100px;
|
| 211 |
+
left: 50%;
|
| 212 |
+
transform: translateX(-50%);
|
| 213 |
+
background-color: rgba(0, 0, 0, 0.7);
|
| 214 |
+
color: white;
|
| 215 |
+
padding: 10px 20px;
|
| 216 |
+
border-radius: 20px;
|
| 217 |
+
font-size: 18px;
|
| 218 |
+
opacity: 0;
|
| 219 |
+
transition: opacity 0.3s, transform 0.3s;
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
#mobile-controls {
|
| 223 |
+
position: absolute;
|
| 224 |
+
bottom: 20px;
|
| 225 |
+
left: 20px;
|
| 226 |
+
display: flex;
|
| 227 |
+
gap: 20px;
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
.mobile-button {
|
| 231 |
+
width: 50px;
|
| 232 |
+
height: 50px;
|
| 233 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 234 |
+
border-radius: 50%;
|
| 235 |
+
display: flex;
|
| 236 |
+
justify-content: center;
|
| 237 |
+
align-items: center;
|
| 238 |
+
color: white;
|
| 239 |
+
font-size: 24px;
|
| 240 |
+
cursor: pointer;
|
| 241 |
+
user-select: none;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
#combat-controls {
|
| 245 |
+
position: absolute;
|
| 246 |
+
bottom: 20px;
|
| 247 |
+
right: 20px;
|
| 248 |
+
display: flex;
|
| 249 |
+
gap: 20px;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
.combat-button {
|
| 253 |
+
width: 60px;
|
| 254 |
+
height: 60px;
|
| 255 |
+
border-radius: 50%;
|
| 256 |
+
display: flex;
|
| 257 |
+
justify-content: center;
|
| 258 |
+
align-items: center;
|
| 259 |
+
color: white;
|
| 260 |
+
font-size: 24px;
|
| 261 |
+
cursor: pointer;
|
| 262 |
+
user-select: none;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
#combat-fire {
|
| 266 |
+
background-color: rgba(255, 69, 0, 0.7);
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
#combat-special {
|
| 270 |
+
background-color: rgba(65, 105, 225, 0.7);
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
#auto-fire-toggle {
|
| 274 |
+
background-color: rgba(0, 255, 0, 0.7);
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
#auto-target-toggle {
|
| 278 |
+
background-color: rgba(0, 255, 0, 0.7);
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
#orientation-toggle {
|
| 282 |
+
position: absolute;
|
| 283 |
+
bottom: 20px;
|
| 284 |
+
left: 20px;
|
| 285 |
+
width: 40px;
|
| 286 |
+
height: 40px;
|
| 287 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 288 |
+
border-radius: 5px;
|
| 289 |
+
display: flex;
|
| 290 |
+
justify-content: center;
|
| 291 |
+
align-items: center;
|
| 292 |
+
cursor: pointer;
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
.vertical #game-container {
|
| 296 |
+
width: 400px;
|
| 297 |
+
height: 600px;
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.background-particle {
|
| 301 |
+
position: absolute;
|
| 302 |
+
background-color: rgba(255, 255, 255, 0.3);
|
| 303 |
+
border-radius: 50%;
|
| 304 |
+
pointer-events: none;
|
| 305 |
+
animation: float var(--duration) linear infinite;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
@keyframes float {
|
| 309 |
+
0% {
|
| 310 |
+
transform: translateY(100vh);
|
| 311 |
+
}
|
| 312 |
+
100% {
|
| 313 |
+
transform: translateY(-100px);
|
| 314 |
+
}
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
.cloud {
|
| 318 |
+
position: absolute;
|
| 319 |
+
background-color: rgba(255, 255, 255, 0.7);
|
| 320 |
+
border-radius: 50%;
|
| 321 |
+
pointer-events: none;
|
| 322 |
+
animation: drift 30s linear infinite;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
@keyframes drift {
|
| 326 |
+
0% {
|
| 327 |
+
transform: translateX(-100px);
|
| 328 |
+
}
|
| 329 |
+
100% {
|
| 330 |
+
transform: translateX(100vw);
|
| 331 |
+
}
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
/* Multi-layered water animation */
|
| 335 |
+
.water-container {
|
| 336 |
+
position: absolute;
|
| 337 |
+
bottom: 0;
|
| 338 |
+
left: 0;
|
| 339 |
+
width: 100%;
|
| 340 |
+
height: 80px;
|
| 341 |
+
overflow: hidden;
|
| 342 |
+
z-index: 5;
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
.water-wave-1, .water-wave-2, .water-wave-3 {
|
| 346 |
+
position: absolute;
|
| 347 |
+
bottom: 0;
|
| 348 |
+
left: 0;
|
| 349 |
+
width: 200%;
|
| 350 |
+
height: 100%;
|
| 351 |
+
background-repeat: repeat-x;
|
| 352 |
+
background-position: 0 bottom;
|
| 353 |
+
transform-origin: center bottom;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
.water-wave-1 {
|
| 357 |
+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23FF0000" fill-opacity="0.6" d="M0,192L48,197.3C96,203,192,213,288,229.3C384,245,480,267,576,250.7C672,235,768,181,864,181.3C960,181,1056,235,1152,234.7C1248,235,1344,181,1392,154.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>');
|
| 358 |
+
animation: wave1 13s linear infinite;
|
| 359 |
+
opacity: 0.7;
|
| 360 |
+
z-index: 3;
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
.water-wave-2 {
|
| 364 |
+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23808080" fill-opacity="0.5" d="M0,160L48,181.3C96,203,192,245,288,240C384,235,480,181,576,186.7C672,192,768,256,864,261.3C960,267,1056,213,1152,192C1248,171,1344,181,1392,186.7L1440,192L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>');
|
| 365 |
+
animation: wave2 7s linear infinite;
|
| 366 |
+
opacity: 0.5;
|
| 367 |
+
z-index: 2;
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
.water-wave-3 {
|
| 371 |
+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%23A00000" fill-opacity="0.4" d="M0,224L48,213.3C96,203,192,181,288,154.7C384,128,480,96,576,106.7C672,117,768,171,864,197.3C960,224,1056,224,1152,208C1248,192,1344,160,1392,144L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>');
|
| 372 |
+
animation: wave3 10s linear infinite;
|
| 373 |
+
opacity: 0.3;
|
| 374 |
+
z-index: 1;
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
@keyframes wave1 {
|
| 378 |
+
0% { transform: translateX(0) translateZ(0) scaleY(1); }
|
| 379 |
+
50% { transform: translateX(-25%) translateZ(0) scaleY(1.1); }
|
| 380 |
+
100% { transform: translateX(-50%) translateZ(0) scaleY(1); }
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
@keyframes wave2 {
|
| 384 |
+
0% { transform: translateX(0) translateZ(0) scaleY(1); }
|
| 385 |
+
50% { transform: translateX(-30%) translateZ(0) scaleY(0.9); }
|
| 386 |
+
100% { transform: translateX(-50%) translateZ(0) scaleY(1); }
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
@keyframes wave3 {
|
| 390 |
+
0% { transform: translateX(-50%) translateZ(0) scaleY(1); }
|
| 391 |
+
50% { transform: translateX(-25%) translateZ(0) scaleY(1.05); }
|
| 392 |
+
100% { transform: translateX(0) translateZ(0) scaleY(1); }
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
/* Add water particles for extra effect */
|
| 396 |
+
.water-particle {
|
| 397 |
+
position: absolute;
|
| 398 |
+
background-color: rgba(255, 255, 255, 0.6);
|
| 399 |
+
border-radius: 50%;
|
| 400 |
+
pointer-events: none;
|
| 401 |
+
animation: float-up var(--duration) ease-out forwards;
|
| 402 |
+
z-index: 4;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
@keyframes float-up {
|
| 406 |
+
0% {
|
| 407 |
+
transform: translateY(0) scale(1);
|
| 408 |
+
opacity: 0.7;
|
| 409 |
+
}
|
| 410 |
+
100% {
|
| 411 |
+
transform: translateY(-100px) scale(0);
|
| 412 |
+
opacity: 0;
|
| 413 |
+
}
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
#cloud-container {
|
| 417 |
+
position: absolute;
|
| 418 |
+
top: 0;
|
| 419 |
+
left: 0;
|
| 420 |
+
width: 100%;
|
| 421 |
+
height: 100px;
|
| 422 |
+
overflow: hidden;
|
| 423 |
+
pointer-events: none;
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
/* Spinner Wheel Styles */
|
| 427 |
+
#spinner-overlay {
|
| 428 |
+
background-color: rgba(0, 0, 128, 0.9);
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
#spinner-container {
|
| 432 |
+
position: relative;
|
| 433 |
+
width: 300px;
|
| 434 |
+
height: 300px;
|
| 435 |
+
margin: 20px auto;
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
#spinner-wheel {
|
| 439 |
+
width: 100%;
|
| 440 |
+
height: 100%;
|
| 441 |
+
border-radius: 50%;
|
| 442 |
+
background-color: #4169E1;
|
| 443 |
+
position: relative;
|
| 444 |
+
overflow: hidden;
|
| 445 |
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
| 446 |
+
transition: transform 3s cubic-bezier(0.17, 0.67, 0.83, 0.67);
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
#spinner-arrow {
|
| 450 |
+
position: absolute;
|
| 451 |
+
top: -10px;
|
| 452 |
+
left: 50%;
|
| 453 |
+
transform: translateX(-50%);
|
| 454 |
+
width: 0;
|
| 455 |
+
height: 0;
|
| 456 |
+
border-left: 15px solid transparent;
|
| 457 |
+
border-right: 15px solid transparent;
|
| 458 |
+
border-top: 30px solid #FF4500;
|
| 459 |
+
z-index: 1;
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
.wheel-section {
|
| 463 |
+
position: absolute;
|
| 464 |
+
width: 50%;
|
| 465 |
+
height: 50%;
|
| 466 |
+
transform-origin: bottom right;
|
| 467 |
+
display: flex;
|
| 468 |
+
justify-content: center;
|
| 469 |
+
align-items: center;
|
| 470 |
+
color: white;
|
| 471 |
+
font-weight: bold;
|
| 472 |
+
font-size: 18px;
|
| 473 |
+
text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
#spin-result {
|
| 477 |
+
margin-top: 20px;
|
| 478 |
+
font-size: 24px;
|
| 479 |
+
font-weight: bold;
|
| 480 |
+
color: gold;
|
| 481 |
+
text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
| 482 |
+
height: 30px;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
#spin-timer {
|
| 486 |
+
margin-top: 10px;
|
| 487 |
+
font-size: 16px;
|
| 488 |
+
color: white;
|
| 489 |
+
height: 20px;
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
/* Lottery Styles */
|
| 493 |
+
#lottery-overlay {
|
| 494 |
+
background-color: rgba(0, 0, 128, 0.9);
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
#lottery-container {
|
| 498 |
+
width: 80%;
|
| 499 |
+
max-width: 600px;
|
| 500 |
+
display: flex;
|
| 501 |
+
flex-direction: column;
|
| 502 |
+
align-items: center;
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
#lottery-info {
|
| 506 |
+
width: 100%;
|
| 507 |
+
text-align: center;
|
| 508 |
+
margin-bottom: 20px;
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
#lottery-pool {
|
| 512 |
+
font-size: 24px;
|
| 513 |
+
font-weight: bold;
|
| 514 |
+
color: gold;
|
| 515 |
+
margin-bottom: 10px;
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
#lottery-status {
|
| 519 |
+
font-size: 18px;
|
| 520 |
+
margin-bottom: 10px;
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
#lottery-timer {
|
| 524 |
+
font-size: 16px;
|
| 525 |
+
color: #87CEEB;
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
#lottery-cards {
|
| 529 |
+
display: flex;
|
| 530 |
+
flex-wrap: wrap;
|
| 531 |
+
justify-content: center;
|
| 532 |
+
gap: 20px;
|
| 533 |
+
margin: 20px 0;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
.lottery-card {
|
| 537 |
+
width: 100px;
|
| 538 |
+
height: 150px;
|
| 539 |
+
background-color: #4169E1;
|
| 540 |
+
border-radius: 10px;
|
| 541 |
+
position: relative;
|
| 542 |
+
cursor: pointer;
|
| 543 |
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
| 544 |
+
transition: transform 0.3s;
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
.lottery-card:hover {
|
| 548 |
+
transform: scale(1.05);
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
.card-cover {
|
| 552 |
+
position: absolute;
|
| 553 |
+
top: 0;
|
| 554 |
+
left: 0;
|
| 555 |
+
width: 100%;
|
| 556 |
+
height: 100%;
|
| 557 |
+
background-color: #87CEEB;
|
| 558 |
+
border-radius: 10px;
|
| 559 |
+
display: flex;
|
| 560 |
+
justify-content: center;
|
| 561 |
+
align-items: center;
|
| 562 |
+
font-size: 36px;
|
| 563 |
+
color: white;
|
| 564 |
+
transition: opacity 0.5s;
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
.card-content {
|
| 568 |
+
width: 100%;
|
| 569 |
+
height: 100%;
|
| 570 |
+
display: flex;
|
| 571 |
+
justify-content: center;
|
| 572 |
+
align-items: center;
|
| 573 |
+
font-size: 24px;
|
| 574 |
+
font-weight: bold;
|
| 575 |
+
color: gold;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
.lottery-card.scratched .card-cover {
|
| 579 |
+
opacity: 0;
|
| 580 |
+
pointer-events: none;
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
/* Admin Panel Styles */
|
| 584 |
+
#admin-overlay {
|
| 585 |
+
background-color: rgba(0, 0, 128, 0.9);
|
| 586 |
+
overflow-y: auto;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
#admin-container {
|
| 590 |
+
width: 80%;
|
| 591 |
+
max-width: 800px;
|
| 592 |
+
padding: 20px;
|
| 593 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 594 |
+
border-radius: 10px;
|
| 595 |
+
margin: 20px 0;
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
.admin-section {
|
| 599 |
+
margin-bottom: 30px;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
.admin-section h3 {
|
| 603 |
+
font-size: 24px;
|
| 604 |
+
margin-bottom: 15px;
|
| 605 |
+
color: #87CEEB;
|
| 606 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
| 607 |
+
padding-bottom: 5px;
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
+
.admin-controls {
|
| 611 |
+
display: grid;
|
| 612 |
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
| 613 |
+
gap: 15px;
|
| 614 |
+
}
|
| 615 |
+
|
| 616 |
+
.admin-control {
|
| 617 |
+
display: flex;
|
| 618 |
+
flex-direction: column;
|
| 619 |
+
gap: 5px;
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
.admin-control label {
|
| 623 |
+
font-size: 16px;
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
.admin-control input[type="number"] {
|
| 627 |
+
padding: 8px;
|
| 628 |
+
border-radius: 5px;
|
| 629 |
+
border: 1px solid #4169E1;
|
| 630 |
+
background-color: rgba(0, 0, 0, 0.2);
|
| 631 |
+
color: white;
|
| 632 |
+
}
|
| 633 |
+
|
| 634 |
+
.admin-control input[type="checkbox"] {
|
| 635 |
+
width: 20px;
|
| 636 |
+
height: 20px;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
.admin-buttons {
|
| 640 |
+
display: flex;
|
| 641 |
+
justify-content: center;
|
| 642 |
+
gap: 20px;
|
| 643 |
+
margin-top: 20px;
|
| 644 |
+
}
|
| 645 |
+
|
| 646 |
+
/* Store Styles */
|
| 647 |
+
#store-overlay {
|
| 648 |
+
background-color: rgba(0, 0, 128, 0.9);
|
| 649 |
+
overflow-y: auto;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
#store-container {
|
| 653 |
+
width: 80%;
|
| 654 |
+
max-width: 800px;
|
| 655 |
+
padding: 20px;
|
| 656 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 657 |
+
border-radius: 10px;
|
| 658 |
+
margin: 20px 0;
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
.store-items {
|
| 662 |
+
display: grid;
|
| 663 |
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
| 664 |
+
gap: 20px;
|
| 665 |
+
margin-bottom: 30px;
|
| 666 |
+
}
|
| 667 |
+
|
| 668 |
+
.store-item {
|
| 669 |
+
background-color: rgba(65, 105, 225, 0.3);
|
| 670 |
+
border-radius: 10px;
|
| 671 |
+
padding: 15px;
|
| 672 |
+
display: flex;
|
| 673 |
+
flex-direction: column;
|
| 674 |
+
align-items: center;
|
| 675 |
+
gap: 10px;
|
| 676 |
+
transition: transform 0.3s;
|
| 677 |
+
}
|
| 678 |
+
|
| 679 |
+
.store-item:hover {
|
| 680 |
+
transform: scale(1.03);
|
| 681 |
+
background-color: rgba(65, 105, 225, 0.5);
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
.store-item-image {
|
| 685 |
+
width: 60px;
|
| 686 |
+
height: 60px;
|
| 687 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 688 |
+
border-radius: 50%;
|
| 689 |
+
display: flex;
|
| 690 |
+
justify-content: center;
|
| 691 |
+
align-items: center;
|
| 692 |
+
font-size: 30px;
|
| 693 |
+
color: #87CEEB;
|
| 694 |
+
}
|
| 695 |
+
|
| 696 |
+
.store-item-name {
|
| 697 |
+
font-size: 18px;
|
| 698 |
+
font-weight: bold;
|
| 699 |
+
color: white;
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
.store-item-description {
|
| 703 |
+
font-size: 14px;
|
| 704 |
+
text-align: center;
|
| 705 |
+
color: #CCC;
|
| 706 |
+
}
|
| 707 |
+
|
| 708 |
+
.store-item-price {
|
| 709 |
+
font-size: 16px;
|
| 710 |
+
font-weight: bold;
|
| 711 |
+
color: gold;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
.store-item-button {
|
| 715 |
+
background-color: #4169E1;
|
| 716 |
+
color: white;
|
| 717 |
+
border: none;
|
| 718 |
+
padding: 8px 15px;
|
| 719 |
+
border-radius: 20px;
|
| 720 |
+
cursor: pointer;
|
| 721 |
+
transition: all 0.3s;
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
.store-item-button:hover:not(:disabled) {
|
| 725 |
+
background-color: #87CEEB;
|
| 726 |
+
transform: scale(1.05);
|
| 727 |
+
}
|
| 728 |
+
|
| 729 |
+
.store-item-button:disabled {
|
| 730 |
+
background-color: #666;
|
| 731 |
+
cursor: not-allowed;
|
| 732 |
+
}
|
| 733 |
+
|
| 734 |
+
#withdrawal-section {
|
| 735 |
+
text-align: center;
|
| 736 |
+
padding: 20px;
|
| 737 |
+
background-color: rgba(0, 0, 0, 0.2);
|
| 738 |
+
border-radius: 10px;
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
/* Leaderboard Styles */
|
| 742 |
+
#leaderboard-overlay {
|
| 743 |
+
background-color: rgba(0, 0, 128, 0.9);
|
| 744 |
+
}
|
| 745 |
+
|
| 746 |
+
#leaderboard-container {
|
| 747 |
+
width: 80%;
|
| 748 |
+
max-width: 600px;
|
| 749 |
+
padding: 20px;
|
| 750 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 751 |
+
border-radius: 10px;
|
| 752 |
+
margin: 20px 0;
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
.leaderboard-tabs {
|
| 756 |
+
display: flex;
|
| 757 |
+
justify-content: center;
|
| 758 |
+
gap: 10px;
|
| 759 |
+
margin-bottom: 20px;
|
| 760 |
+
}
|
| 761 |
+
|
| 762 |
+
.leaderboard-tab {
|
| 763 |
+
padding: 8px 15px;
|
| 764 |
+
background-color: rgba(65, 105, 225, 0.3);
|
| 765 |
+
border-radius: 20px;
|
| 766 |
+
cursor: pointer;
|
| 767 |
+
transition: all 0.3s;
|
| 768 |
+
}
|
| 769 |
+
|
| 770 |
+
.leaderboard-tab:hover {
|
| 771 |
+
background-color: rgba(65, 105, 225, 0.5);
|
| 772 |
+
}
|
| 773 |
+
|
| 774 |
+
.leaderboard-tab.active {
|
| 775 |
+
background-color: #4169E1;
|
| 776 |
+
font-weight: bold;
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
.leaderboard-table {
|
| 780 |
+
width: 100%;
|
| 781 |
+
border-collapse: collapse;
|
| 782 |
+
margin-bottom: 20px;
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
.leaderboard-table th, .leaderboard-table td {
|
| 786 |
+
padding: 10px;
|
| 787 |
+
text-align: left;
|
| 788 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
.leaderboard-table th {
|
| 792 |
+
background-color: rgba(0, 0, 0, 0.3);
|
| 793 |
+
color: #87CEEB;
|
| 794 |
+
}
|
| 795 |
+
|
| 796 |
+
.leaderboard-table .rank, .leaderboard-table .score {
|
| 797 |
+
text-align: center;
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
/* Close and Back Buttons */
|
| 801 |
+
.close-button {
|
| 802 |
+
position: absolute;
|
| 803 |
+
top: 20px;
|
| 804 |
+
right: 20px;
|
| 805 |
+
width: 40px;
|
| 806 |
+
height: 40px;
|
| 807 |
+
background-color: rgba(255, 0, 0, 0.7);
|
| 808 |
+
border: none;
|
| 809 |
+
border-radius: 50%;
|
| 810 |
+
color: white;
|
| 811 |
+
font-size: 20px;
|
| 812 |
+
display: flex;
|
| 813 |
+
justify-content: center;
|
| 814 |
+
align-items: center;
|
| 815 |
+
cursor: pointer;
|
| 816 |
+
transition: all 0.3s;
|
| 817 |
+
z-index: 20;
|
| 818 |
+
}
|
| 819 |
+
|
| 820 |
+
.close-button:hover {
|
| 821 |
+
background-color: rgba(255, 0, 0, 0.9);
|
| 822 |
+
transform: scale(1.1);
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
.back-button {
|
| 826 |
+
position: absolute;
|
| 827 |
+
top: 20px;
|
| 828 |
+
left: 20px;
|
| 829 |
+
width: 40px;
|
| 830 |
+
height: 40px;
|
| 831 |
+
background-color: rgba(65, 105, 225, 0.7);
|
| 832 |
+
border: none;
|
| 833 |
+
border-radius: 50%;
|
| 834 |
+
color: white;
|
| 835 |
+
font-size: 20px;
|
| 836 |
+
display: flex;
|
| 837 |
+
justify-content: center;
|
| 838 |
+
align-items: center;
|
| 839 |
+
cursor: pointer;
|
| 840 |
+
transition: all 0.3s;
|
| 841 |
+
z-index: 20;
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
.back-button:hover {
|
| 845 |
+
background-color: rgba(65, 105, 225, 0.9);
|
| 846 |
+
transform: scale(1.1);
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
@media (max-width: 768px) {
|
| 850 |
+
#game-container {
|
| 851 |
+
width: 100%;
|
| 852 |
+
height: 100%;
|
| 853 |
+
border-radius: 0;
|
| 854 |
+
}
|
| 855 |
+
|
| 856 |
+
header {
|
| 857 |
+
padding: 5px 10px;
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
h1 {
|
| 861 |
+
font-size: 18px;
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
.nav-button {
|
| 865 |
+
padding: 3px 10px;
|
| 866 |
+
font-size: 14px;
|
| 867 |
+
}
|
| 868 |
+
|
| 869 |
+
#hud {
|
| 870 |
+
top: 5px;
|
| 871 |
+
left: 5px;
|
| 872 |
+
}
|
| 873 |
+
|
| 874 |
+
.bar-container {
|
| 875 |
+
width: 150px;
|
| 876 |
+
height: 15px;
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
#score-display {
|
| 880 |
+
font-size: 24px;
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
#combo-display {
|
| 884 |
+
font-size: 18px;
|
| 885 |
+
}
|
| 886 |
+
|
| 887 |
+
.mobile-button, .combat-button {
|
| 888 |
+
width: 40px;
|
| 889 |
+
height: 40px;
|
| 890 |
+
font-size: 18px;
|
| 891 |
+
}
|
| 892 |
+
}
|
| 893 |
+
</style>
|
| 894 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
| 895 |
+
</head>
|
| 896 |
+
<body>
|
| 897 |
+
<header>
|
| 898 |
+
<h1>Doge Whale Game</h1>
|
| 899 |
+
<div class="nav-buttons">
|
| 900 |
+
<button class="nav-button" id="play-button">Play</button>
|
| 901 |
+
<button class="nav-button" id="spinner-button">Spinner</button>
|
| 902 |
+
<button class="nav-button" id="lottery-button">Lottery</button>
|
| 903 |
+
<button class="nav-button" id="store-button">Store</button>
|
| 904 |
+
<button class="nav-button" id="leaderboard-button">Leaderboard</button>
|
| 905 |
+
<button class="nav-button" id="admin-button">Admin</button>
|
| 906 |
+
</div>
|
| 907 |
+
<div class="user-info">
|
| 908 |
+
<span>Guest</span>
|
| 909 |
+
<div class="coin-display">
|
| 910 |
+
<div class="coin-icon">$</div>
|
| 911 |
+
<span id="coin-count">0</span>
|
| 912 |
+
</div>
|
| 913 |
+
</div>
|
| 914 |
+
</header>
|
| 915 |
+
|
| 916 |
+
<main>
|
| 917 |
+
<div id="game-container">
|
| 918 |
+
<canvas id="game-canvas"></canvas>
|
| 919 |
+
|
| 920 |
+
<div id="hud">
|
| 921 |
+
<div class="bar-container">
|
| 922 |
+
<div id="health-bar"></div>
|
| 923 |
+
</div>
|
| 924 |
+
<div class="bar-container">
|
| 925 |
+
<div id="power-bar"></div>
|
| 926 |
+
</div>
|
| 927 |
+
</div>
|
| 928 |
+
|
| 929 |
+
<div id="score-display">0</div>
|
| 930 |
+
<div id="combo-display">Combo x1</div>
|
| 931 |
+
<div id="power-up-notification"></div>
|
| 932 |
+
|
| 933 |
+
<div id="mobile-controls">
|
| 934 |
+
<div class="mobile-button" id="mobile-up"><i class="fas fa-arrow-up"></i></div>
|
| 935 |
+
<div class="mobile-button" id="mobile-down"><i class="fas fa-arrow-down"></i></div>
|
| 936 |
+
</div>
|
| 937 |
+
|
| 938 |
+
<div id="combat-controls">
|
| 939 |
+
<div class="combat-button" id="combat-fire"><i class="fas fa-bolt"></i></div>
|
| 940 |
+
<div class="combat-button" id="combat-special"><i class="fas fa-star"></i></div>
|
| 941 |
+
<div class="combat-button" id="auto-fire-toggle"><i class="fas fa-robot"></i></div>
|
| 942 |
+
<div class="combat-button" id="auto-target-toggle"><i class="fas fa-crosshairs"></i></div>
|
| 943 |
+
</div>
|
| 944 |
+
|
| 945 |
+
<div class="water-container">
|
| 946 |
+
<div class="water-wave-1"></div>
|
| 947 |
+
<div class="water-wave-2"></div>
|
| 948 |
+
<div class="water-wave-3"></div>
|
| 949 |
+
</div>
|
| 950 |
+
|
| 951 |
+
<div class="overlay active" id="start-overlay">
|
| 952 |
+
<h2>Doge Whale Game</h2>
|
| 953 |
+
<p>Navigate through pipes and defeat enemy whales!</p>
|
| 954 |
+
<button class="button" id="start-button">Start Game</button>
|
| 955 |
+
</div>
|
| 956 |
+
|
| 957 |
+
<div class="overlay" id="game-over-overlay">
|
| 958 |
+
<h2>Game Over</h2>
|
| 959 |
+
<p>Your score: <span id="final-score">0</span></p>
|
| 960 |
+
<button class="button" id="restart-button">Play Again</button>
|
| 961 |
+
<button class="close-button" id="game-over-close-button"><i class="fas fa-times"></i></button>
|
| 962 |
+
</div>
|
| 963 |
+
|
| 964 |
+
<!-- Spinner Wheel Overlay -->
|
| 965 |
+
<div class="overlay" id="spinner-overlay">
|
| 966 |
+
<button class="close-button" id="spinner-x-button"><i class="fas fa-times"></i></button>
|
| 967 |
+
<button class="back-button" id="spinner-back-button"><i class="fas fa-arrow-left"></i></button>
|
| 968 |
+
<h2>Spin & Win</h2>
|
| 969 |
+
<p>Spin the wheel to win DWHL coins!</p>
|
| 970 |
+
|
| 971 |
+
<div id="spinner-container">
|
| 972 |
+
<div id="spinner-arrow"></div>
|
| 973 |
+
<div id="spinner-wheel">
|
| 974 |
+
<!-- Wheel sections will be generated by JavaScript -->
|
| 975 |
+
</div>
|
| 976 |
+
</div>
|
| 977 |
+
|
| 978 |
+
<div id="spin-result"></div>
|
| 979 |
+
<div id="spin-timer"></div>
|
| 980 |
+
<button class="button" id="spin-button">SPIN</button>
|
| 981 |
+
<button class="button" id="spinner-close-button">Close</button>
|
| 982 |
+
</div>
|
| 983 |
+
|
| 984 |
+
<!-- Lottery Overlay -->
|
| 985 |
+
<div class="overlay" id="lottery-overlay">
|
| 986 |
+
<button class="close-button" id="lottery-x-button"><i class="fas fa-times"></i></button>
|
| 987 |
+
<button class="back-button" id="lottery-back-button"><i class="fas fa-arrow-left"></i></button>
|
| 988 |
+
<h2>DWHL Lottery</h2>
|
| 989 |
+
<p>Pay 5 DWHL to enter the lottery pool and scratch cards to win!</p>
|
| 990 |
+
|
| 991 |
+
<div id="lottery-container">
|
| 992 |
+
<div id="lottery-info">
|
| 993 |
+
<div id="lottery-pool">Current Pool: 0 DWHL</div>
|
| 994 |
+
<div id="lottery-status">Enter the lottery for a chance to win!</div>
|
| 995 |
+
<div id="lottery-timer"></div>
|
| 996 |
+
</div>
|
| 997 |
+
|
| 998 |
+
<div id="lottery-cards">
|
| 999 |
+
<!-- Lottery cards will be generated by JavaScript -->
|
| 1000 |
+
</div>
|
| 1001 |
+
|
| 1002 |
+
<button class="button" id="lottery-entry-button">Enter Lottery (5 DWHL)</button>
|
| 1003 |
+
<button class="button" id="lottery-close-button">Close</button>
|
| 1004 |
+
</div>
|
| 1005 |
+
</div>
|
| 1006 |
+
|
| 1007 |
+
<!-- Admin Panel Overlay -->
|
| 1008 |
+
<div class="overlay" id="admin-overlay">
|
| 1009 |
+
<button class="close-button" id="admin-x-button"><i class="fas fa-times"></i></button>
|
| 1010 |
+
<button class="back-button" id="admin-back-button"><i class="fas fa-arrow-left"></i></button>
|
| 1011 |
+
<h2>Admin Panel</h2>
|
| 1012 |
+
<p>Configure game settings and features</p>
|
| 1013 |
+
|
| 1014 |
+
<div id="admin-container">
|
| 1015 |
+
<div class="admin-section">
|
| 1016 |
+
<h3>Spinner Settings</h3>
|
| 1017 |
+
<div class="admin-controls">
|
| 1018 |
+
<div class="admin-control">
|
| 1019 |
+
<label for="spinner-min-prize">Minimum Prize</label>
|
| 1020 |
+
<input type="number" id="spinner-min-prize" min="1" max="100" value="1">
|
| 1021 |
+
</div>
|
| 1022 |
+
<div class="admin-control">
|
| 1023 |
+
<label for="spinner-max-prize">Maximum Prize</label>
|
| 1024 |
+
<input type="number" id="spinner-max-prize" min="1" max="1000" value="100">
|
| 1025 |
+
</div>
|
| 1026 |
+
<div class="admin-control">
|
| 1027 |
+
<label for="spinner-cooldown">Cooldown (hours)</label>
|
| 1028 |
+
<input type="number" id="spinner-cooldown" min="0" max="72" value="24">
|
| 1029 |
+
</div>
|
| 1030 |
+
<div class="admin-control">
|
| 1031 |
+
<label for="spinner-enabled">Enable Spinner</label>
|
| 1032 |
+
<input type="checkbox" id="spinner-enabled" checked>
|
| 1033 |
+
</div>
|
| 1034 |
+
</div>
|
| 1035 |
+
</div>
|
| 1036 |
+
|
| 1037 |
+
<div class="admin-section">
|
| 1038 |
+
<h3>Lottery Settings</h3>
|
| 1039 |
+
<div class="admin-controls">
|
| 1040 |
+
<div class="admin-control">
|
| 1041 |
+
<label for="lottery-entry-fee">Entry Fee</label>
|
| 1042 |
+
<input type="number" id="lottery-entry-fee" min="1" max="100" value="5">
|
| 1043 |
+
</div>
|
| 1044 |
+
<div class="admin-control">
|
| 1045 |
+
<label for="lottery-max-prize">Maximum Prize</label>
|
| 1046 |
+
<input type="number" id="lottery-max-prize" min="10" max="1000" value="200">
|
| 1047 |
+
</div>
|
| 1048 |
+
<div class="admin-control">
|
| 1049 |
+
<label for="lottery-card-count">Card Count</label>
|
| 1050 |
+
<input type="number" id="lottery-card-count" min="3" max="12" value="6">
|
| 1051 |
+
</div>
|
| 1052 |
+
<div class="admin-control">
|
| 1053 |
+
<label for="lottery-win-chance">Win Chance (%)</label>
|
| 1054 |
+
<input type="number" id="lottery-win-chance" min="1" max="100" value="30">
|
| 1055 |
+
</div>
|
| 1056 |
+
<div class="admin-control">
|
| 1057 |
+
<label for="lottery-enabled">Enable Lottery</label>
|
| 1058 |
+
<input type="checkbox" id="lottery-enabled" checked>
|
| 1059 |
+
</div>
|
| 1060 |
+
</div>
|
| 1061 |
+
</div>
|
| 1062 |
+
|
| 1063 |
+
<div class="admin-section">
|
| 1064 |
+
<h3>Game Settings</h3>
|
| 1065 |
+
<div class="admin-controls">
|
| 1066 |
+
<div class="admin-control">
|
| 1067 |
+
<label for="game-difficulty">Difficulty</label>
|
| 1068 |
+
<input type="number" id="game-difficulty" min="1" max="10" value="5">
|
| 1069 |
+
</div>
|
| 1070 |
+
<div class="admin-control">
|
| 1071 |
+
<label for="game-coin-multiplier">Coin Multiplier</label>
|
| 1072 |
+
<input type="number" id="game-coin-multiplier" min="0.1" max="10" step="0.1" value="1">
|
| 1073 |
+
</div>
|
| 1074 |
+
<div class="admin-control">
|
| 1075 |
+
<label for="game-score-multiplier">Score Multiplier</label>
|
| 1076 |
+
<input type="number" id="game-score-multiplier" min="0.1" max="10" step="0.1" value="1">
|
| 1077 |
+
</div>
|
| 1078 |
+
</div>
|
| 1079 |
+
</div>
|
| 1080 |
+
|
| 1081 |
+
<div class="admin-buttons">
|
| 1082 |
+
<button class="button" id="admin-save-button">Save Settings</button>
|
| 1083 |
+
<button class="button" id="admin-reset-button">Reset to Default</button>
|
| 1084 |
+
<button class="button" id="admin-close-button">Close</button>
|
| 1085 |
+
</div>
|
| 1086 |
+
</div>
|
| 1087 |
+
</div>
|
| 1088 |
+
|
| 1089 |
+
<!-- Store Overlay -->
|
| 1090 |
+
<div class="overlay" id="store-overlay">
|
| 1091 |
+
<button class="close-button" id="store-x-button"><i class="fas fa-times"></i></button>
|
| 1092 |
+
<button class="back-button" id="store-back-button"><i class="fas fa-arrow-left"></i></button>
|
| 1093 |
+
<h2>DWHL Store</h2>
|
| 1094 |
+
<p>Spend your DWHL coins on upgrades and items!</p>
|
| 1095 |
+
|
| 1096 |
+
<div id="store-container">
|
| 1097 |
+
<div class="store-items">
|
| 1098 |
+
<!-- Store items will be generated by JavaScript -->
|
| 1099 |
+
</div>
|
| 1100 |
+
|
| 1101 |
+
<div id="withdrawal-section">
|
| 1102 |
+
<h3>Withdraw DWHL</h3>
|
| 1103 |
+
<p>Convert your DWHL coins to real value!</p>
|
| 1104 |
+
<p>Current Balance: <span id="withdrawal-balance">0</span> DWHL</p>
|
| 1105 |
+
<button class="button" id="withdrawal-button">Withdraw</button>
|
| 1106 |
+
</div>
|
| 1107 |
+
|
| 1108 |
+
<button class="button" id="store-close-button">Close</button>
|
| 1109 |
+
</div>
|
| 1110 |
+
</div>
|
| 1111 |
+
|
| 1112 |
+
<!-- Leaderboard Overlay -->
|
| 1113 |
+
<div class="overlay" id="leaderboard-overlay">
|
| 1114 |
+
<button class="close-button" id="leaderboard-x-button"><i class="fas fa-times"></i></button>
|
| 1115 |
+
<button class="back-button" id="leaderboard-back-button"><i class="fas fa-arrow-left"></i></button>
|
| 1116 |
+
<h2>Leaderboard</h2>
|
| 1117 |
+
<p>See the top players and their scores!</p>
|
| 1118 |
+
|
| 1119 |
+
<div id="leaderboard-container">
|
| 1120 |
+
<div class="leaderboard-tabs">
|
| 1121 |
+
<div class="leaderboard-tab active" data-tab="daily">Daily</div>
|
| 1122 |
+
<div class="leaderboard-tab" data-tab="weekly">Weekly</div>
|
| 1123 |
+
<div class="leaderboard-tab" data-tab="all-time">All Time</div>
|
| 1124 |
+
</div>
|
| 1125 |
+
|
| 1126 |
+
<table class="leaderboard-table">
|
| 1127 |
+
<thead>
|
| 1128 |
+
<tr>
|
| 1129 |
+
<th class="rank">Rank</th>
|
| 1130 |
+
<th>Player</th>
|
| 1131 |
+
<th class="score">Score</th>
|
| 1132 |
+
</tr>
|
| 1133 |
+
</thead>
|
| 1134 |
+
<tbody id="leaderboard-body">
|
| 1135 |
+
<!-- Leaderboard entries will be generated by JavaScript -->
|
| 1136 |
+
</tbody>
|
| 1137 |
+
</table>
|
| 1138 |
+
|
| 1139 |
+
<button class="button" id="leaderboard-close-button">Close</button>
|
| 1140 |
+
</div>
|
| 1141 |
+
</div>
|
| 1142 |
+
</div>
|
| 1143 |
+
</main>
|
| 1144 |
+
|
| 1145 |
+
<!-- Audio elements for sound effects -->
|
| 1146 |
+
<audio id="water-ambient" loop preload="auto">
|
| 1147 |
+
<source src="https://assets.mixkit.co/sfx/preview/mixkit-water-flowing-loop-1189.mp3" type="audio/mpeg">
|
| 1148 |
+
</audio>
|
| 1149 |
+
<audio id="water-splash" preload="auto">
|
| 1150 |
+
<source src="https://assets.mixkit.co/sfx/preview/mixkit-water-splash-1295.mp3" type="audio/mpeg">
|
| 1151 |
+
</audio>
|
| 1152 |
+
<audio id="weapon-sound" preload="auto">
|
| 1153 |
+
<source src="https://assets.mixkit.co/sfx/preview/mixkit-laser-weapon-shot-1681.mp3" type="audio/mpeg">
|
| 1154 |
+
</audio>
|
| 1155 |
+
|
| 1156 |
+
<script>
|
| 1157 |
+
// Game initialization
|
| 1158 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 1159 |
+
// Game variables
|
| 1160 |
+
const canvas = document.getElementById('game-canvas');
|
| 1161 |
+
const ctx = canvas.getContext('2d');
|
| 1162 |
+
const healthBar = document.getElementById('health-bar');
|
| 1163 |
+
const powerBar = document.getElementById('power-bar');
|
| 1164 |
+
const scoreDisplay = document.getElementById('score-display');
|
| 1165 |
+
const comboDisplay = document.getElementById('combo-display');
|
| 1166 |
+
const powerUpNotification = document.getElementById('power-up-notification');
|
| 1167 |
+
const startOverlay = document.getElementById('start-overlay');
|
| 1168 |
+
const gameOverOverlay = document.getElementById('game-over-overlay');
|
| 1169 |
+
const finalScoreDisplay = document.getElementById('final-score');
|
| 1170 |
+
const startButton = document.getElementById('start-button');
|
| 1171 |
+
const restartButton = document.getElementById('restart-button');
|
| 1172 |
+
const gameOverCloseButton = document.getElementById('game-over-close-button');
|
| 1173 |
+
const mobileUpButton = document.getElementById('mobile-up');
|
| 1174 |
+
const mobileDownButton = document.getElementById('mobile-down');
|
| 1175 |
+
const combatFireButton = document.getElementById('combat-fire');
|
| 1176 |
+
const combatSpecialButton = document.getElementById('combat-special');
|
| 1177 |
+
const autoFireToggle = document.getElementById('auto-fire-toggle');
|
| 1178 |
+
const autoTargetToggle = document.getElementById('auto-target-toggle');
|
| 1179 |
+
|
| 1180 |
+
// Audio elements
|
| 1181 |
+
const waterAmbientSound = document.getElementById('water-ambient');
|
| 1182 |
+
const waterSplashSound = document.getElementById('water-splash');
|
| 1183 |
+
const weaponSound = document.getElementById('weapon-sound');
|
| 1184 |
+
|
| 1185 |
+
// Set volume levels
|
| 1186 |
+
waterAmbientSound.volume = 0.3;
|
| 1187 |
+
waterSplashSound.volume = 0.4;
|
| 1188 |
+
weaponSound.volume = 0.2;
|
| 1189 |
+
|
| 1190 |
+
// Game state
|
| 1191 |
+
let gameActive = false;
|
| 1192 |
+
let health = 100;
|
| 1193 |
+
let power = 0;
|
| 1194 |
+
let score = 0;
|
| 1195 |
+
let combo = 1;
|
| 1196 |
+
let coins = 0;
|
| 1197 |
+
let lastFrameTime = 0;
|
| 1198 |
+
let keys = {};
|
| 1199 |
+
|
| 1200 |
+
// Game objects
|
| 1201 |
+
let player = {
|
| 1202 |
+
x: 50,
|
| 1203 |
+
y: 200,
|
| 1204 |
+
width: 60,
|
| 1205 |
+
height: 40,
|
| 1206 |
+
velocity: 0,
|
| 1207 |
+
color: '#FF4500'
|
| 1208 |
+
};
|
| 1209 |
+
|
| 1210 |
+
let pipes = [];
|
| 1211 |
+
let enemies = [];
|
| 1212 |
+
let projectiles = [];
|
| 1213 |
+
let enemyProjectiles = [];
|
| 1214 |
+
let powerUps = [];
|
| 1215 |
+
let particles = [];
|
| 1216 |
+
let clouds = [];
|
| 1217 |
+
|
| 1218 |
+
// Game settings
|
| 1219 |
+
const gameSettings = {
|
| 1220 |
+
combat: {
|
| 1221 |
+
autoFireEnabled: true,
|
| 1222 |
+
autoFireRate: 500, // milliseconds
|
| 1223 |
+
autoTargetingEnabled: true,
|
| 1224 |
+
targetingRange: 300,
|
| 1225 |
+
projectileSpeed: 10,
|
| 1226 |
+
projectileSize: 5,
|
| 1227 |
+
projectileDamage: 1
|
| 1228 |
+
}
|
| 1229 |
+
};
|
| 1230 |
+
|
| 1231 |
+
let lastAutoFireTime = 0;
|
| 1232 |
+
|
| 1233 |
+
// Whale animator
|
| 1234 |
+
const whaleAnimator = new WhaleAnimator(ctx);
|
| 1235 |
+
let whaleImagesLoaded = false;
|
| 1236 |
+
|
| 1237 |
+
// Initialize the game
|
| 1238 |
+
function initGame() {
|
| 1239 |
+
// Set canvas size
|
| 1240 |
+
canvas.width = canvas.clientWidth;
|
| 1241 |
+
canvas.height = canvas.clientHeight;
|
| 1242 |
+
|
| 1243 |
+
// Reset game state
|
| 1244 |
+
health = 100;
|
| 1245 |
+
power = 0;
|
| 1246 |
+
score = 0;
|
| 1247 |
+
combo = 1;
|
| 1248 |
+
|
| 1249 |
+
// Update UI
|
| 1250 |
+
healthBar.style.width = `${health}%`;
|
| 1251 |
+
powerBar.style.width = `${power}%`;
|
| 1252 |
+
scoreDisplay.textContent = score;
|
| 1253 |
+
comboDisplay.textContent = `Combo x${combo}`;
|
| 1254 |
+
comboDisplay.style.opacity = '0';
|
| 1255 |
+
|
| 1256 |
+
// Reset game objects
|
| 1257 |
+
player = {
|
| 1258 |
+
x: 50,
|
| 1259 |
+
y: canvas.height / 2 - 20,
|
| 1260 |
+
width: 60,
|
| 1261 |
+
height: 40,
|
| 1262 |
+
velocity: 0,
|
| 1263 |
+
color: '#FF4500'
|
| 1264 |
+
};
|
| 1265 |
+
|
| 1266 |
+
pipes = [];
|
| 1267 |
+
enemies = [];
|
| 1268 |
+
projectiles = [];
|
| 1269 |
+
enemyProjectiles = [];
|
| 1270 |
+
powerUps = [];
|
| 1271 |
+
particles = [];
|
| 1272 |
+
|
| 1273 |
+
// Generate initial clouds
|
| 1274 |
+
clouds = [];
|
| 1275 |
+
generateClouds();
|
| 1276 |
+
|
| 1277 |
+
// Create water particles
|
| 1278 |
+
createWaterParticles();
|
| 1279 |
+
|
| 1280 |
+
// Start ambient water sound
|
| 1281 |
+
waterAmbientSound.play();
|
| 1282 |
+
|
| 1283 |
+
// Load whale images if not already loaded
|
| 1284 |
+
if (!whaleImagesLoaded) {
|
| 1285 |
+
whaleAnimator.loadImages().then(success => {
|
| 1286 |
+
whaleImagesLoaded = success;
|
| 1287 |
+
console.log("Whale images loaded:", success);
|
| 1288 |
+
});
|
| 1289 |
+
}
|
| 1290 |
+
|
| 1291 |
+
// Start game loop
|
| 1292 |
+
gameActive = true;
|
| 1293 |
+
requestAnimationFrame(gameLoop);
|
| 1294 |
+
}
|
| 1295 |
+
|
| 1296 |
+
// Game loop
|
| 1297 |
+
function gameLoop(timestamp) {
|
| 1298 |
+
if (!gameActive) return;
|
| 1299 |
+
|
| 1300 |
+
// Calculate delta time
|
| 1301 |
+
const deltaTime = (timestamp - lastFrameTime) / 1000;
|
| 1302 |
+
lastFrameTime = timestamp;
|
| 1303 |
+
|
| 1304 |
+
// Clear canvas
|
| 1305 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 1306 |
+
|
| 1307 |
+
// Update game state
|
| 1308 |
+
updateGameState(deltaTime);
|
| 1309 |
+
|
| 1310 |
+
// Check collisions
|
| 1311 |
+
checkCollisions();
|
| 1312 |
+
|
| 1313 |
+
// Draw game objects
|
| 1314 |
+
drawGameObjects();
|
| 1315 |
+
|
| 1316 |
+
// Request next frame
|
| 1317 |
+
requestAnimationFrame(gameLoop);
|
| 1318 |
+
}
|
| 1319 |
+
|
| 1320 |
+
// Update game state
|
| 1321 |
+
function updateGameState(deltaTime) {
|
| 1322 |
+
// Update player position
|
| 1323 |
+
if (keys['ArrowUp'] || keys['w']) {
|
| 1324 |
+
player.velocity -= 0.5;
|
| 1325 |
+
}
|
| 1326 |
+
|
| 1327 |
+
if (keys['ArrowDown'] || keys['s']) {
|
| 1328 |
+
player.velocity += 0.5;
|
| 1329 |
+
}
|
| 1330 |
+
|
| 1331 |
+
// Apply gravity
|
| 1332 |
+
player.velocity += 0.2;
|
| 1333 |
+
|
| 1334 |
+
// Limit velocity
|
| 1335 |
+
player.velocity = Math.max(-10, Math.min(10, player.velocity));
|
| 1336 |
+
|
| 1337 |
+
// Update player position
|
| 1338 |
+
player.y += player.velocity;
|
| 1339 |
+
|
| 1340 |
+
// Keep player within bounds
|
| 1341 |
+
if (player.y < 0) {
|
| 1342 |
+
player.y = 0;
|
| 1343 |
+
player.velocity = 0;
|
| 1344 |
+
}
|
| 1345 |
+
|
| 1346 |
+
if (player.y + player.height > canvas.height) {
|
| 1347 |
+
player.y = canvas.height - player.height;
|
| 1348 |
+
player.velocity = 0;
|
| 1349 |
+
}
|
| 1350 |
+
|
| 1351 |
+
// Generate pipes
|
| 1352 |
+
if (Math.random() < 0.02) {
|
| 1353 |
+
const gapHeight = 150;
|
| 1354 |
+
const gapPosition = Math.random() * (canvas.height - gapHeight);
|
| 1355 |
+
|
| 1356 |
+
pipes.push({
|
| 1357 |
+
x: canvas.width,
|
| 1358 |
+
y: 0,
|
| 1359 |
+
width: 50,
|
| 1360 |
+
height: gapPosition,
|
| 1361 |
+
passed: false
|
| 1362 |
+
});
|
| 1363 |
+
|
| 1364 |
+
pipes.push({
|
| 1365 |
+
x: canvas.width,
|
| 1366 |
+
y: gapPosition + gapHeight,
|
| 1367 |
+
width: 50,
|
| 1368 |
+
height: canvas.height - gapPosition - gapHeight,
|
| 1369 |
+
passed: false
|
| 1370 |
+
});
|
| 1371 |
+
}
|
| 1372 |
+
|
| 1373 |
+
// Update pipes
|
| 1374 |
+
for (let i = 0; i < pipes.length; i++) {
|
| 1375 |
+
pipes[i].x -= 2;
|
| 1376 |
+
|
| 1377 |
+
// Check if pipe is passed
|
| 1378 |
+
if (!pipes[i].passed && pipes[i].x + pipes[i].width < player.x) {
|
| 1379 |
+
pipes[i].passed = true;
|
| 1380 |
+
score += 10;
|
| 1381 |
+
scoreDisplay.textContent = score;
|
| 1382 |
+
}
|
| 1383 |
+
|
| 1384 |
+
// Remove pipes that are off screen
|
| 1385 |
+
if (pipes[i].x + pipes[i].width < 0) {
|
| 1386 |
+
pipes.splice(i, 1);
|
| 1387 |
+
i--;
|
| 1388 |
+
}
|
| 1389 |
+
}
|
| 1390 |
+
|
| 1391 |
+
// Generate enemies
|
| 1392 |
+
if (Math.random() < 0.01) {
|
| 1393 |
+
const enemy = {
|
| 1394 |
+
x: canvas.width,
|
| 1395 |
+
y: Math.random() * (canvas.height - 40),
|
| 1396 |
+
width: 50,
|
| 1397 |
+
height: 40,
|
| 1398 |
+
velocity: -2 - Math.random() * 2,
|
| 1399 |
+
color: '#4169E1',
|
| 1400 |
+
health: 3,
|
| 1401 |
+
lastShot: 0
|
| 1402 |
+
};
|
| 1403 |
+
|
| 1404 |
+
enemies.push(enemy);
|
| 1405 |
+
}
|
| 1406 |
+
|
| 1407 |
+
// Update enemies
|
| 1408 |
+
for (let i = 0; i < enemies.length; i++) {
|
| 1409 |
+
enemies[i].x += enemies[i].velocity;
|
| 1410 |
+
|
| 1411 |
+
// Enemy shooting
|
| 1412 |
+
if (Date.now() - enemies[i].lastShot > 2000) {
|
| 1413 |
+
enemies[i].lastShot = Date.now();
|
| 1414 |
+
|
| 1415 |
+
// Calculate angle to player
|
| 1416 |
+
const dx = player.x + player.width/2 - (enemies[i].x + enemies[i].width/2);
|
| 1417 |
+
const dy = player.y + player.height/2 - (enemies[i].y + enemies[i].height/2);
|
| 1418 |
+
const angle = Math.atan2(dy, dx);
|
| 1419 |
+
|
| 1420 |
+
// Create enemy projectile
|
| 1421 |
+
enemyProjectiles.push({
|
| 1422 |
+
x: enemies[i].x,
|
| 1423 |
+
y: enemies[i].y + enemies[i].height / 2,
|
| 1424 |
+
size: 5,
|
| 1425 |
+
color: '#FF0000',
|
| 1426 |
+
vx: Math.cos(angle) * 5,
|
| 1427 |
+
vy: Math.sin(angle) * 5
|
| 1428 |
+
});
|
| 1429 |
+
}
|
| 1430 |
+
|
| 1431 |
+
// Remove enemies that are off screen
|
| 1432 |
+
if (enemies[i].x + enemies[i].width < 0) {
|
| 1433 |
+
enemies.splice(i, 1);
|
| 1434 |
+
i--;
|
| 1435 |
+
}
|
| 1436 |
+
}
|
| 1437 |
+
|
| 1438 |
+
// Auto-fire system
|
| 1439 |
+
if (gameSettings.combat.autoFireEnabled && gameActive) {
|
| 1440 |
+
const currentTime = Date.now();
|
| 1441 |
+
if (currentTime - lastAutoFireTime >= gameSettings.combat.autoFireRate) {
|
| 1442 |
+
lastAutoFireTime = currentTime;
|
| 1443 |
+
autoFireProjectile();
|
| 1444 |
+
}
|
| 1445 |
+
}
|
| 1446 |
+
|
| 1447 |
+
// Update projectiles
|
| 1448 |
+
updateProjectiles();
|
| 1449 |
+
|
| 1450 |
+
// Update enemy projectiles
|
| 1451 |
+
for (let i = 0; i < enemyProjectiles.length; i++) {
|
| 1452 |
+
enemyProjectiles[i].x += enemyProjectiles[i].vx;
|
| 1453 |
+
enemyProjectiles[i].y += enemyProjectiles[i].vy;
|
| 1454 |
+
|
| 1455 |
+
// Remove projectiles that are off screen
|
| 1456 |
+
if (
|
| 1457 |
+
enemyProjectiles[i].x < 0 ||
|
| 1458 |
+
enemyProjectiles[i].x > canvas.width ||
|
| 1459 |
+
enemyProjectiles[i].y < 0 ||
|
| 1460 |
+
enemyProjectiles[i].y > canvas.height
|
| 1461 |
+
) {
|
| 1462 |
+
enemyProjectiles.splice(i, 1);
|
| 1463 |
+
i--;
|
| 1464 |
+
}
|
| 1465 |
+
}
|
| 1466 |
+
|
| 1467 |
+
// Update power-ups
|
| 1468 |
+
for (let i = 0; i < powerUps.length; i++) {
|
| 1469 |
+
powerUps[i].x -= 2;
|
| 1470 |
+
|
| 1471 |
+
// Remove power-ups that are off screen
|
| 1472 |
+
if (powerUps[i].x + powerUps[i].size < 0) {
|
| 1473 |
+
powerUps.splice(i, 1);
|
| 1474 |
+
i--;
|
| 1475 |
+
}
|
| 1476 |
+
}
|
| 1477 |
+
|
| 1478 |
+
// Update particles
|
| 1479 |
+
for (let i = 0; i < particles.length; i++) {
|
| 1480 |
+
particles[i].x += particles[i].vx;
|
| 1481 |
+
particles[i].y += particles[i].vy;
|
| 1482 |
+
particles[i].alpha -= 0.01;
|
| 1483 |
+
|
| 1484 |
+
// Remove particles that are faded out
|
| 1485 |
+
if (particles[i].alpha <= 0) {
|
| 1486 |
+
particles.splice(i, 1);
|
| 1487 |
+
i--;
|
| 1488 |
+
}
|
| 1489 |
+
}
|
| 1490 |
+
|
| 1491 |
+
// Update clouds
|
| 1492 |
+
for (let i = 0; i < clouds.length; i++) {
|
| 1493 |
+
clouds[i].x += clouds[i].speed;
|
| 1494 |
+
|
| 1495 |
+
// Remove clouds that are off screen
|
| 1496 |
+
if (clouds[i].x - clouds[i].size > canvas.width) {
|
| 1497 |
+
clouds.splice(i, 1);
|
| 1498 |
+
i--;
|
| 1499 |
+
}
|
| 1500 |
+
}
|
| 1501 |
+
|
| 1502 |
+
// Generate new clouds
|
| 1503 |
+
if (clouds.length < 5 && Math.random() < 0.01) {
|
| 1504 |
+
generateCloud();
|
| 1505 |
+
}
|
| 1506 |
+
|
| 1507 |
+
// Update whale animations
|
| 1508 |
+
if (whaleImagesLoaded) {
|
| 1509 |
+
whaleAnimator.update(deltaTime);
|
| 1510 |
+
}
|
| 1511 |
+
}
|
| 1512 |
+
|
| 1513 |
+
// Update projectiles with homing behavior
|
| 1514 |
+
function updateProjectiles() {
|
| 1515 |
+
for (let i = 0; i < projectiles.length; i++) {
|
| 1516 |
+
const projectile = projectiles[i];
|
| 1517 |
+
|
| 1518 |
+
// Handle homing projectiles
|
| 1519 |
+
if (projectile.homing && projectile.targetId >= 0 && projectile.targetId < enemies.length) {
|
| 1520 |
+
const target = enemies[projectile.targetId];
|
| 1521 |
+
|
| 1522 |
+
// Calculate new angle to target
|
| 1523 |
+
const dx = target.x + target.width/2 - projectile.x;
|
| 1524 |
+
const dy = target.y + target.height/2 - projectile.y;
|
| 1525 |
+
const targetAngle = Math.atan2(dy, dx);
|
| 1526 |
+
|
| 1527 |
+
// Gradually adjust angle towards target (homing effect)
|
| 1528 |
+
const angleDiff = targetAngle - projectile.angle;
|
| 1529 |
+
|
| 1530 |
+
// Normalize angle difference to -PI to PI range
|
| 1531 |
+
let normalizedAngleDiff = angleDiff;
|
| 1532 |
+
while (normalizedAngleDiff > Math.PI) normalizedAngleDiff -= 2 * Math.PI;
|
| 1533 |
+
while (normalizedAngleDiff < -Math.PI) normalizedAngleDiff += 2 * Math.PI;
|
| 1534 |
+
|
| 1535 |
+
// Adjust angle with a turning rate factor (0.1 for gentle homing)
|
| 1536 |
+
projectile.angle += normalizedAngleDiff * 0.1;
|
| 1537 |
+
|
| 1538 |
+
// Update velocity based on new angle
|
| 1539 |
+
projectile.vx = Math.cos(projectile.angle) * projectile.speed;
|
| 1540 |
+
projectile.vy = Math.sin(projectile.angle) * projectile.speed;
|
| 1541 |
+
} else if (!projectile.homing) {
|
| 1542 |
+
// Non-homing projectiles move straight
|
| 1543 |
+
projectile.vx = projectile.speed;
|
| 1544 |
+
projectile.vy = 0;
|
| 1545 |
+
}
|
| 1546 |
+
|
| 1547 |
+
// Move projectile
|
| 1548 |
+
projectile.x += projectile.vx;
|
| 1549 |
+
projectile.y += projectile.vy;
|
| 1550 |
+
|
| 1551 |
+
// Create trail effect for homing projectiles
|
| 1552 |
+
if (projectile.homing) {
|
| 1553 |
+
createParticles(projectile.x, projectile.y, 1, projectile.color);
|
| 1554 |
+
}
|
| 1555 |
+
|
| 1556 |
+
// Remove projectiles that are off screen
|
| 1557 |
+
if (
|
| 1558 |
+
projectile.x < 0 ||
|
| 1559 |
+
projectile.x > canvas.width ||
|
| 1560 |
+
projectile.y < 0 ||
|
| 1561 |
+
projectile.y > canvas.height
|
| 1562 |
+
) {
|
| 1563 |
+
projectiles.splice(i, 1);
|
| 1564 |
+
i--;
|
| 1565 |
+
}
|
| 1566 |
+
}
|
| 1567 |
+
}
|
| 1568 |
+
|
| 1569 |
+
// Draw game objects
|
| 1570 |
+
function drawGameObjects() {
|
| 1571 |
+
// Draw background
|
| 1572 |
+
ctx.fillStyle = 'rgba(135, 206, 235, 0.3)'; // Light blue background
|
| 1573 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 1574 |
+
|
| 1575 |
+
// Draw clouds
|
| 1576 |
+
for (let i = 0; i < clouds.length; i++) {
|
| 1577 |
+
const cloud = clouds[i];
|
| 1578 |
+
|
| 1579 |
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
|
| 1580 |
+
ctx.beginPath();
|
| 1581 |
+
ctx.arc(cloud.x, cloud.y, cloud.size, 0, Math.PI * 2);
|
| 1582 |
+
ctx.fill();
|
| 1583 |
+
|
| 1584 |
+
// Draw additional cloud parts
|
| 1585 |
+
ctx.beginPath();
|
| 1586 |
+
ctx.arc(cloud.x + cloud.size * 0.5, cloud.y - cloud.size * 0.2, cloud.size * 0.7, 0, Math.PI * 2);
|
| 1587 |
+
ctx.fill();
|
| 1588 |
+
|
| 1589 |
+
ctx.beginPath();
|
| 1590 |
+
ctx.arc(cloud.x - cloud.size * 0.5, cloud.y - cloud.size * 0.1, cloud.size * 0.6, 0, Math.PI * 2);
|
| 1591 |
+
ctx.fill();
|
| 1592 |
+
}
|
| 1593 |
+
|
| 1594 |
+
// Draw pipes with gradient
|
| 1595 |
+
for (let i = 0; i < pipes.length; i++) {
|
| 1596 |
+
const gradient = ctx.createLinearGradient(
|
| 1597 |
+
pipes[i].x,
|
| 1598 |
+
pipes[i].y,
|
| 1599 |
+
pipes[i].x,
|
| 1600 |
+
pipes[i].y + pipes[i].height
|
| 1601 |
+
);
|
| 1602 |
+
gradient.addColorStop(0, '#FF0000'); // Red at top
|
| 1603 |
+
gradient.addColorStop(1, '#808080'); // Gray at bottom
|
| 1604 |
+
|
| 1605 |
+
ctx.fillStyle = gradient;
|
| 1606 |
+
ctx.fillRect(pipes[i].x, pipes[i].y, pipes[i].width, pipes[i].height);
|
| 1607 |
+
|
| 1608 |
+
// Add highlight on the left edge for depth
|
| 1609 |
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
|
| 1610 |
+
ctx.fillRect(pipes[i].x, pipes[i].y, 3, pipes[i].height);
|
| 1611 |
+
|
| 1612 |
+
// Add shadow on the right edge for depth
|
| 1613 |
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
|
| 1614 |
+
ctx.fillRect(pipes[i].x + pipes[i].width - 3, pipes[i].y, 3, pipes[i].height);
|
| 1615 |
+
|
| 1616 |
+
// Add a border for better definition
|
| 1617 |
+
ctx.strokeStyle = '#606060';
|
| 1618 |
+
ctx.lineWidth = 2;
|
| 1619 |
+
ctx.strokeRect(pipes[i].x, pipes[i].y, pipes[i].width, pipes[i].height);
|
| 1620 |
+
}
|
| 1621 |
+
|
| 1622 |
+
// Draw power-ups
|
| 1623 |
+
for (let i = 0; i < powerUps.length; i++) {
|
| 1624 |
+
ctx.fillStyle = powerUps[i].color;
|
| 1625 |
+
ctx.beginPath();
|
| 1626 |
+
ctx.arc(powerUps[i].x, powerUps[i].y, powerUps[i].size, 0, Math.PI * 2);
|
| 1627 |
+
ctx.fill();
|
| 1628 |
+
|
| 1629 |
+
// Draw icon
|
| 1630 |
+
ctx.fillStyle = 'white';
|
| 1631 |
+
ctx.font = `${powerUps[i].size}px Arial`;
|
| 1632 |
+
ctx.textAlign = 'center';
|
| 1633 |
+
ctx.textBaseline = 'middle';
|
| 1634 |
+
ctx.fillText(powerUps[i].icon, powerUps[i].x, powerUps[i].y);
|
| 1635 |
+
}
|
| 1636 |
+
|
| 1637 |
+
// Draw player whale
|
| 1638 |
+
if (whaleImagesLoaded) {
|
| 1639 |
+
whaleAnimator.animateSwimming(player.x, player.y, player.width, player.height, 2);
|
| 1640 |
+
} else {
|
| 1641 |
+
// Fallback to original player drawing
|
| 1642 |
+
ctx.fillStyle = player.color;
|
| 1643 |
+
ctx.beginPath();
|
| 1644 |
+
ctx.ellipse(
|
| 1645 |
+
player.x + player.width / 2,
|
| 1646 |
+
player.y + player.height / 2,
|
| 1647 |
+
player.width / 2,
|
| 1648 |
+
player.height / 2,
|
| 1649 |
+
0,
|
| 1650 |
+
0,
|
| 1651 |
+
Math.PI * 2
|
| 1652 |
+
);
|
| 1653 |
+
ctx.fill();
|
| 1654 |
+
|
| 1655 |
+
// Draw player eye
|
| 1656 |
+
ctx.fillStyle = 'black';
|
| 1657 |
+
ctx.beginPath();
|
| 1658 |
+
ctx.arc(
|
| 1659 |
+
player.x + player.width * 0.7,
|
| 1660 |
+
player.y + player.height * 0.4,
|
| 1661 |
+
player.width * 0.1,
|
| 1662 |
+
0,
|
| 1663 |
+
Math.PI * 2
|
| 1664 |
+
);
|
| 1665 |
+
ctx.fill();
|
| 1666 |
+
}
|
| 1667 |
+
|
| 1668 |
+
// Draw enemies with targeting indicators
|
| 1669 |
+
for (let i = 0; i < enemies.length; i++) {
|
| 1670 |
+
if (whaleImagesLoaded) {
|
| 1671 |
+
// Use whale animation for enemies
|
| 1672 |
+
whaleAnimator.animateCombat(enemies[i].x, enemies[i].y, enemies[i].width, enemies[i].height);
|
| 1673 |
+
} else {
|
| 1674 |
+
// Fallback to original enemy drawing
|
| 1675 |
+
ctx.fillStyle = enemies[i].color;
|
| 1676 |
+
ctx.beginPath();
|
| 1677 |
+
ctx.ellipse(
|
| 1678 |
+
enemies[i].x + enemies[i].width / 2,
|
| 1679 |
+
enemies[i].y + enemies[i].height / 2,
|
| 1680 |
+
enemies[i].width / 2,
|
| 1681 |
+
enemies[i].height / 2,
|
| 1682 |
+
0,
|
| 1683 |
+
0,
|
| 1684 |
+
Math.PI * 2
|
| 1685 |
+
);
|
| 1686 |
+
ctx.fill();
|
| 1687 |
+
|
| 1688 |
+
// Draw enemy eye
|
| 1689 |
+
ctx.fillStyle = 'black';
|
| 1690 |
+
ctx.beginPath();
|
| 1691 |
+
ctx.arc(
|
| 1692 |
+
enemies[i].x + enemies[i].width * 0.3,
|
| 1693 |
+
enemies[i].y + enemies[i].height * 0.4,
|
| 1694 |
+
enemies[i].width * 0.1,
|
| 1695 |
+
0,
|
| 1696 |
+
Math.PI * 2
|
| 1697 |
+
);
|
| 1698 |
+
ctx.fill();
|
| 1699 |
+
}
|
| 1700 |
+
|
| 1701 |
+
// Draw targeting indicator for nearest enemy
|
| 1702 |
+
if (gameSettings.combat.autoTargetingEnabled) {
|
| 1703 |
+
const nearestEnemy = findNearestEnemy();
|
| 1704 |
+
if (nearestEnemy === enemies[i]) {
|
| 1705 |
+
drawTargetingIndicator(enemies[i]);
|
| 1706 |
+
}
|
| 1707 |
+
}
|
| 1708 |
+
}
|
| 1709 |
+
|
| 1710 |
+
// Draw projectiles
|
| 1711 |
+
for (let i = 0; i < projectiles.length; i++) {
|
| 1712 |
+
ctx.fillStyle = projectiles[i].color;
|
| 1713 |
+
ctx.beginPath();
|
| 1714 |
+
ctx.arc(projectiles[i].x, projectiles[i].y, projectiles[i].size, 0, Math.PI * 2);
|
| 1715 |
+
ctx.fill();
|
| 1716 |
+
|
| 1717 |
+
// Draw trail for homing projectiles
|
| 1718 |
+
if (projectiles[i].homing) {
|
| 1719 |
+
ctx.strokeStyle = projectiles[i].color;
|
| 1720 |
+
ctx.lineWidth = 2;
|
| 1721 |
+
ctx.beginPath();
|
| 1722 |
+
ctx.moveTo(projectiles[i].x, projectiles[i].y);
|
| 1723 |
+
ctx.lineTo(projectiles[i].x - 20, projectiles[i].y);
|
| 1724 |
+
ctx.stroke();
|
| 1725 |
+
}
|
| 1726 |
+
}
|
| 1727 |
+
|
| 1728 |
+
// Draw enemy projectiles
|
| 1729 |
+
for (let i = 0; i < enemyProjectiles.length; i++) {
|
| 1730 |
+
ctx.fillStyle = enemyProjectiles[i].color;
|
| 1731 |
+
ctx.beginPath();
|
| 1732 |
+
ctx.arc(enemyProjectiles[i].x, enemyProjectiles[i].y, enemyProjectiles[i].size, 0, Math.PI * 2);
|
| 1733 |
+
ctx.fill();
|
| 1734 |
+
}
|
| 1735 |
+
|
| 1736 |
+
// Draw particles
|
| 1737 |
+
for (let i = 0; i < particles.length; i++) {
|
| 1738 |
+
ctx.fillStyle = `rgba(${particles[i].r}, ${particles[i].g}, ${particles[i].b}, ${particles[i].alpha})`;
|
| 1739 |
+
ctx.beginPath();
|
| 1740 |
+
ctx.arc(particles[i].x, particles[i].y, particles[i].size, 0, Math.PI * 2);
|
| 1741 |
+
ctx.fill();
|
| 1742 |
+
}
|
| 1743 |
+
}
|
| 1744 |
+
|
| 1745 |
+
// Find the nearest enemy within range
|
| 1746 |
+
function findNearestEnemy() {
|
| 1747 |
+
if (enemies.length === 0) return null;
|
| 1748 |
+
|
| 1749 |
+
let nearestEnemy = null;
|
| 1750 |
+
let shortestDistance = Infinity;
|
| 1751 |
+
|
| 1752 |
+
for (let i = 0; i < enemies.length; i++) {
|
| 1753 |
+
const enemy = enemies[i];
|
| 1754 |
+
const dx = enemy.x - player.x;
|
| 1755 |
+
const dy = enemy.y - player.y;
|
| 1756 |
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 1757 |
+
|
| 1758 |
+
// Check if enemy is within targeting range
|
| 1759 |
+
if (distance < gameSettings.combat.targetingRange && distance < shortestDistance) {
|
| 1760 |
+
shortestDistance = distance;
|
| 1761 |
+
nearestEnemy = enemy;
|
| 1762 |
+
}
|
| 1763 |
+
}
|
| 1764 |
+
|
| 1765 |
+
return nearestEnemy;
|
| 1766 |
+
}
|
| 1767 |
+
|
| 1768 |
+
// Draw targeting indicator for enemy
|
| 1769 |
+
function drawTargetingIndicator(enemy) {
|
| 1770 |
+
ctx.strokeStyle = 'rgba(255, 0, 0, 0.7)';
|
| 1771 |
+
ctx.lineWidth = 2;
|
| 1772 |
+
|
| 1773 |
+
// Draw targeting circle
|
| 1774 |
+
ctx.beginPath();
|
| 1775 |
+
ctx.arc(
|
| 1776 |
+
enemy.x + enemy.width/2,
|
| 1777 |
+
enemy.y + enemy.height/2,
|
| 1778 |
+
enemy.width/2 + 5,
|
| 1779 |
+
0,
|
| 1780 |
+
Math.PI * 2
|
| 1781 |
+
);
|
| 1782 |
+
ctx.stroke();
|
| 1783 |
+
|
| 1784 |
+
// Draw crosshair lines
|
| 1785 |
+
const centerX = enemy.x + enemy.width/2;
|
| 1786 |
+
const centerY = enemy.y + enemy.height/2;
|
| 1787 |
+
const size = enemy.width/2 + 10;
|
| 1788 |
+
|
| 1789 |
+
ctx.beginPath();
|
| 1790 |
+
// Horizontal line
|
| 1791 |
+
ctx.moveTo(centerX - size, centerY);
|
| 1792 |
+
ctx.lineTo(centerX + size, centerY);
|
| 1793 |
+
// Vertical line
|
| 1794 |
+
ctx.moveTo(centerX, centerY - size);
|
| 1795 |
+
ctx.lineTo(centerX, centerY + size);
|
| 1796 |
+
ctx.stroke();
|
| 1797 |
+
}
|
| 1798 |
+
|
| 1799 |
+
// Fire projectile
|
| 1800 |
+
function fireProjectile() {
|
| 1801 |
+
let projectileType = 'normal';
|
| 1802 |
+
let projectileColor = '#4169E1'; // Royal blue
|
| 1803 |
+
let projectileSize = 5;
|
| 1804 |
+
let projectileSpeed = 10;
|
| 1805 |
+
|
| 1806 |
+
// Create projectile
|
| 1807 |
+
const projectile = {
|
| 1808 |
+
x: player.x + player.width,
|
| 1809 |
+
y: player.y + player.height / 2,
|
| 1810 |
+
size: projectileSize,
|
| 1811 |
+
speed: projectileSpeed,
|
| 1812 |
+
color: projectileColor,
|
| 1813 |
+
type: projectileType,
|
| 1814 |
+
vx: projectileSpeed,
|
| 1815 |
+
vy: 0
|
| 1816 |
+
};
|
| 1817 |
+
|
| 1818 |
+
projectiles.push(projectile);
|
| 1819 |
+
|
| 1820 |
+
// Play weapon sound
|
| 1821 |
+
weaponSound.currentTime = 0;
|
| 1822 |
+
weaponSound.play();
|
| 1823 |
+
}
|
| 1824 |
+
|
| 1825 |
+
// Auto-fire projectile with targeting
|
| 1826 |
+
function autoFireProjectile() {
|
| 1827 |
+
// Find nearest enemy
|
| 1828 |
+
const targetEnemy = findNearestEnemy();
|
| 1829 |
+
|
| 1830 |
+
// If no enemy in range, don't fire
|
| 1831 |
+
if (!targetEnemy) return;
|
| 1832 |
+
|
| 1833 |
+
let projectileType = 'homing';
|
| 1834 |
+
let projectileColor = '#4169E1'; // Royal blue
|
| 1835 |
+
let projectileSize = gameSettings.combat.projectileSize;
|
| 1836 |
+
let projectileSpeed = gameSettings.combat.projectileSpeed;
|
| 1837 |
+
|
| 1838 |
+
// Calculate angle to target
|
| 1839 |
+
const dx = targetEnemy.x + targetEnemy.width/2 - (player.x + player.width);
|
| 1840 |
+
const dy = targetEnemy.y + targetEnemy.height/2 - (player.y + player.height/2);
|
| 1841 |
+
const angle = Math.atan2(dy, dx);
|
| 1842 |
+
|
| 1843 |
+
// Create projectile with targeting information
|
| 1844 |
+
const projectile = {
|
| 1845 |
+
x: player.x + player.width,
|
| 1846 |
+
y: player.y + player.height / 2,
|
| 1847 |
+
size: projectileSize,
|
| 1848 |
+
speed: projectileSpeed,
|
| 1849 |
+
color: projectileColor,
|
| 1850 |
+
type: projectileType,
|
| 1851 |
+
damage: gameSettings.combat.projectileDamage,
|
| 1852 |
+
homing: true,
|
| 1853 |
+
targetId: enemies.indexOf(targetEnemy),
|
| 1854 |
+
vx: Math.cos(angle) * projectileSpeed,
|
| 1855 |
+
vy: Math.sin(angle) * projectileSpeed,
|
| 1856 |
+
angle: angle
|
| 1857 |
+
};
|
| 1858 |
+
|
| 1859 |
+
// Add to projectiles array
|
| 1860 |
+
projectiles.push(projectile);
|
| 1861 |
+
|
| 1862 |
+
// Create muzzle flash effect
|
| 1863 |
+
createParticles(player.x + player.width, player.y + player.height / 2, 5, projectileColor);
|
| 1864 |
+
|
| 1865 |
+
// Play weapon sound
|
| 1866 |
+
weaponSound.currentTime = 0;
|
| 1867 |
+
weaponSound.play();
|
| 1868 |
+
}
|
| 1869 |
+
|
| 1870 |
+
// Create particles
|
| 1871 |
+
function createParticles(x, y, count, color) {
|
| 1872 |
+
// Parse color to RGB
|
| 1873 |
+
let r, g, b;
|
| 1874 |
+
|
| 1875 |
+
if (color.startsWith('#')) {
|
| 1876 |
+
// Hex color
|
| 1877 |
+
const hex = color.substring(1);
|
| 1878 |
+
r = parseInt(hex.substring(0, 2), 16);
|
| 1879 |
+
g = parseInt(hex.substring(2, 4), 16);
|
| 1880 |
+
b = parseInt(hex.substring(4, 6), 16);
|
| 1881 |
+
} else if (color.startsWith('rgb')) {
|
| 1882 |
+
// RGB color
|
| 1883 |
+
const rgb = color.match(/\d+/g);
|
| 1884 |
+
r = parseInt(rgb[0]);
|
| 1885 |
+
g = parseInt(rgb[1]);
|
| 1886 |
+
b = parseInt(rgb[2]);
|
| 1887 |
+
} else {
|
| 1888 |
+
// Default color (white)
|
| 1889 |
+
r = g = b = 255;
|
| 1890 |
+
}
|
| 1891 |
+
|
| 1892 |
+
// Create particles
|
| 1893 |
+
for (let i = 0; i < count; i++) {
|
| 1894 |
+
const angle = Math.random() * Math.PI * 2;
|
| 1895 |
+
const speed = 1 + Math.random() * 3;
|
| 1896 |
+
|
| 1897 |
+
particles.push({
|
| 1898 |
+
x: x,
|
| 1899 |
+
y: y,
|
| 1900 |
+
size: 1 + Math.random() * 3,
|
| 1901 |
+
vx: Math.cos(angle) * speed,
|
| 1902 |
+
vy: Math.sin(angle) * speed,
|
| 1903 |
+
alpha: 1,
|
| 1904 |
+
r: r,
|
| 1905 |
+
g: g,
|
| 1906 |
+
b: b
|
| 1907 |
+
});
|
| 1908 |
+
}
|
| 1909 |
+
}
|
| 1910 |
+
|
| 1911 |
+
// Generate clouds
|
| 1912 |
+
function generateClouds() {
|
| 1913 |
+
for (let i = 0; i < 5; i++) {
|
| 1914 |
+
generateCloud(true);
|
| 1915 |
+
}
|
| 1916 |
+
}
|
| 1917 |
+
|
| 1918 |
+
// Generate a single cloud
|
| 1919 |
+
function generateCloud(initial = false) {
|
| 1920 |
+
const size = 20 + Math.random() * 30;
|
| 1921 |
+
const x = initial ? Math.random() * canvas.width : -size * 2;
|
| 1922 |
+
const y = 20 + Math.random() * 100;
|
| 1923 |
+
const speed = 0.2 + Math.random() * 0.3;
|
| 1924 |
+
|
| 1925 |
+
clouds.push({
|
| 1926 |
+
x: x,
|
| 1927 |
+
y: y,
|
| 1928 |
+
size: size,
|
| 1929 |
+
speed: speed
|
| 1930 |
+
});
|
| 1931 |
+
}
|
| 1932 |
+
|
| 1933 |
+
// Create water particles for animation
|
| 1934 |
+
function createWaterParticles() {
|
| 1935 |
+
const waterContainer = document.querySelector('.water-container');
|
| 1936 |
+
|
| 1937 |
+
// Create a new particle
|
| 1938 |
+
function createParticle() {
|
| 1939 |
+
const particle = document.createElement('div');
|
| 1940 |
+
particle.classList.add('water-particle');
|
| 1941 |
+
|
| 1942 |
+
// Random size between 3px and 8px
|
| 1943 |
+
const size = 3 + Math.random() * 5;
|
| 1944 |
+
particle.style.width = `${size}px`;
|
| 1945 |
+
particle.style.height = `${size}px`;
|
| 1946 |
+
|
| 1947 |
+
// Random position along the water
|
| 1948 |
+
const posX = Math.random() * waterContainer.offsetWidth;
|
| 1949 |
+
particle.style.left = `${posX}px`;
|
| 1950 |
+
particle.style.bottom = `${Math.random() * 20}px`;
|
| 1951 |
+
|
| 1952 |
+
// Random duration between 1s and 3s
|
| 1953 |
+
const duration = 1 + Math.random() * 2;
|
| 1954 |
+
particle.style.setProperty('--duration', `${duration}s`);
|
| 1955 |
+
|
| 1956 |
+
// Add to container
|
| 1957 |
+
waterContainer.appendChild(particle);
|
| 1958 |
+
|
| 1959 |
+
// Remove after animation completes
|
| 1960 |
+
setTimeout(() => {
|
| 1961 |
+
particle.remove();
|
| 1962 |
+
}, duration * 1000);
|
| 1963 |
+
}
|
| 1964 |
+
|
| 1965 |
+
// Create particles at random intervals
|
| 1966 |
+
setInterval(createParticle, 300);
|
| 1967 |
+
}
|
| 1968 |
+
|
| 1969 |
+
// Check water collision for sound effects
|
| 1970 |
+
function checkWaterCollision() {
|
| 1971 |
+
const waterLevel = canvas.height - 50; // Adjust based on water height
|
| 1972 |
+
|
| 1973 |
+
// If player is near water level and moving
|
| 1974 |
+
if (player.y + player.height > waterLevel && player.velocity > 1) {
|
| 1975 |
+
waterSplashSound.currentTime = 0;
|
| 1976 |
+
waterSplashSound.play();
|
| 1977 |
+
}
|
| 1978 |
+
}
|
| 1979 |
+
|
| 1980 |
+
// Check collisions
|
| 1981 |
+
function checkCollisions() {
|
| 1982 |
+
// Check player-pipe collisions
|
| 1983 |
+
for (let i = 0; i < pipes.length; i++) {
|
| 1984 |
+
if (
|
| 1985 |
+
player.x < pipes[i].x + pipes[i].width &&
|
| 1986 |
+
player.x + player.width > pipes[i].x &&
|
| 1987 |
+
player.y < pipes[i].y + pipes[i].height &&
|
| 1988 |
+
player.y + player.height > pipes[i].y
|
| 1989 |
+
) {
|
| 1990 |
+
// Player hit pipe
|
| 1991 |
+
takeDamage(10);
|
| 1992 |
+
createParticles(player.x, player.y, 20, player.color);
|
| 1993 |
+
}
|
| 1994 |
+
}
|
| 1995 |
+
|
| 1996 |
+
// Check player-enemy collisions
|
| 1997 |
+
for (let i = 0; i < enemies.length; i++) {
|
| 1998 |
+
if (
|
| 1999 |
+
player.x < enemies[i].x + enemies[i].width &&
|
| 2000 |
+
player.x + player.width > enemies[i].x &&
|
| 2001 |
+
player.y < enemies[i].y + enemies[i].height &&
|
| 2002 |
+
player.y + player.height > enemies[i].y
|
| 2003 |
+
) {
|
| 2004 |
+
// Player hit enemy
|
| 2005 |
+
takeDamage(20);
|
| 2006 |
+
createParticles(enemies[i].x, enemies[i].y, 30, enemies[i].color);
|
| 2007 |
+
|
| 2008 |
+
// Remove enemy
|
| 2009 |
+
enemies.splice(i, 1);
|
| 2010 |
+
i--;
|
| 2011 |
+
}
|
| 2012 |
+
}
|
| 2013 |
+
|
| 2014 |
+
// Check projectile-enemy collisions
|
| 2015 |
+
for (let i = 0; i < projectiles.length; i++) {
|
| 2016 |
+
for (let j = 0; j < enemies.length; j++) {
|
| 2017 |
+
if (
|
| 2018 |
+
projectiles[i].x > enemies[j].x &&
|
| 2019 |
+
projectiles[i].x < enemies[j].x + enemies[j].width &&
|
| 2020 |
+
projectiles[i].y > enemies[j].y &&
|
| 2021 |
+
projectiles[i].y < enemies[j].y + enemies[j].height
|
| 2022 |
+
) {
|
| 2023 |
+
// Projectile hit enemy
|
| 2024 |
+
createParticles(projectiles[i].x, projectiles[i].y, 10, projectiles[i].color);
|
| 2025 |
+
|
| 2026 |
+
// Damage enemy
|
| 2027 |
+
enemies[j].health--;
|
| 2028 |
+
|
| 2029 |
+
// Check if enemy is defeated
|
| 2030 |
+
if (enemies[j].health <= 0) {
|
| 2031 |
+
// Create explosion
|
| 2032 |
+
createParticles(
|
| 2033 |
+
enemies[j].x + enemies[j].width / 2,
|
| 2034 |
+
enemies[j].y + enemies[j].height / 2,
|
| 2035 |
+
30,
|
| 2036 |
+
enemies[j].color
|
| 2037 |
+
);
|
| 2038 |
+
|
| 2039 |
+
// Increase score
|
| 2040 |
+
score += 50 * combo;
|
| 2041 |
+
scoreDisplay.textContent = score;
|
| 2042 |
+
|
| 2043 |
+
// Increase combo
|
| 2044 |
+
combo++;
|
| 2045 |
+
comboDisplay.textContent = `Combo x${combo}`;
|
| 2046 |
+
comboDisplay.style.opacity = '1';
|
| 2047 |
+
|
| 2048 |
+
// Increase power
|
| 2049 |
+
power += 10 * combo;
|
| 2050 |
+
if (power > 100) power = 100;
|
| 2051 |
+
powerBar.style.width = `${power}%`;
|
| 2052 |
+
|
| 2053 |
+
// Chance to spawn power-up
|
| 2054 |
+
if (Math.random() < 0.3) {
|
| 2055 |
+
const powerUpType = Math.floor(Math.random() * 3);
|
| 2056 |
+
let powerUpColor, powerUpIcon, powerUpEffect;
|
| 2057 |
+
|
| 2058 |
+
switch (powerUpType) {
|
| 2059 |
+
case 0: // Health
|
| 2060 |
+
powerUpColor = '#FF4500';
|
| 2061 |
+
powerUpIcon = '+';
|
| 2062 |
+
powerUpEffect = () => {
|
| 2063 |
+
health += 20;
|
| 2064 |
+
if (health > 100) health = 100;
|
| 2065 |
+
healthBar.style.width = `${health}%`;
|
| 2066 |
+
showNotification('Health +20');
|
| 2067 |
+
};
|
| 2068 |
+
break;
|
| 2069 |
+
case 1: // Power
|
| 2070 |
+
powerUpColor = '#4169E1';
|
| 2071 |
+
powerUpIcon = '*';
|
| 2072 |
+
powerUpEffect = () => {
|
| 2073 |
+
power += 30;
|
| 2074 |
+
if (power > 100) power = 100;
|
| 2075 |
+
powerBar.style.width = `${power}%`;
|
| 2076 |
+
showNotification('Power +30');
|
| 2077 |
+
};
|
| 2078 |
+
break;
|
| 2079 |
+
case 2: // Coins
|
| 2080 |
+
powerUpColor = '#FFD700';
|
| 2081 |
+
powerUpIcon = '$';
|
| 2082 |
+
powerUpEffect = () => {
|
| 2083 |
+
updateCoins(10);
|
| 2084 |
+
showNotification('Coins +10');
|
| 2085 |
+
};
|
| 2086 |
+
break;
|
| 2087 |
+
}
|
| 2088 |
+
|
| 2089 |
+
powerUps.push({
|
| 2090 |
+
x: enemies[j].x + enemies[j].width / 2,
|
| 2091 |
+
y: enemies[j].y + enemies[j].height / 2,
|
| 2092 |
+
size: 15,
|
| 2093 |
+
color: powerUpColor,
|
| 2094 |
+
icon: powerUpIcon,
|
| 2095 |
+
effect: powerUpEffect
|
| 2096 |
+
});
|
| 2097 |
+
}
|
| 2098 |
+
|
| 2099 |
+
// Remove enemy
|
| 2100 |
+
enemies.splice(j, 1);
|
| 2101 |
+
j--;
|
| 2102 |
+
}
|
| 2103 |
+
|
| 2104 |
+
// Remove projectile
|
| 2105 |
+
projectiles.splice(i, 1);
|
| 2106 |
+
i--;
|
| 2107 |
+
break;
|
| 2108 |
+
}
|
| 2109 |
+
}
|
| 2110 |
+
}
|
| 2111 |
+
|
| 2112 |
+
// Check player-enemy projectile collisions
|
| 2113 |
+
for (let i = 0; i < enemyProjectiles.length; i++) {
|
| 2114 |
+
if (
|
| 2115 |
+
enemyProjectiles[i].x > player.x &&
|
| 2116 |
+
enemyProjectiles[i].x < player.x + player.width &&
|
| 2117 |
+
enemyProjectiles[i].y > player.y &&
|
| 2118 |
+
enemyProjectiles[i].y < player.y + player.height
|
| 2119 |
+
) {
|
| 2120 |
+
// Player hit by enemy projectile
|
| 2121 |
+
takeDamage(5);
|
| 2122 |
+
createParticles(enemyProjectiles[i].x, enemyProjectiles[i].y, 10, enemyProjectiles[i].color);
|
| 2123 |
+
|
| 2124 |
+
// Remove projectile
|
| 2125 |
+
enemyProjectiles.splice(i, 1);
|
| 2126 |
+
i--;
|
| 2127 |
+
}
|
| 2128 |
+
}
|
| 2129 |
+
|
| 2130 |
+
// Check player-power up collisions
|
| 2131 |
+
for (let i = 0; i < powerUps.length; i++) {
|
| 2132 |
+
const dx = powerUps[i].x - (player.x + player.width / 2);
|
| 2133 |
+
const dy = powerUps[i].y - (player.y + player.height / 2);
|
| 2134 |
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 2135 |
+
|
| 2136 |
+
if (distance < powerUps[i].size + player.width / 2) {
|
| 2137 |
+
// Player collected power-up
|
| 2138 |
+
powerUps[i].effect();
|
| 2139 |
+
|
| 2140 |
+
// Remove power-up
|
| 2141 |
+
powerUps.splice(i, 1);
|
| 2142 |
+
i--;
|
| 2143 |
+
}
|
| 2144 |
+
}
|
| 2145 |
+
|
| 2146 |
+
// Check water collision for sound effects
|
| 2147 |
+
checkWaterCollision();
|
| 2148 |
+
}
|
| 2149 |
+
|
| 2150 |
+
// Take damage
|
| 2151 |
+
function takeDamage(amount) {
|
| 2152 |
+
health -= amount;
|
| 2153 |
+
healthBar.style.width = `${health}%`;
|
| 2154 |
+
|
| 2155 |
+
// Reset combo
|
| 2156 |
+
combo = 1;
|
| 2157 |
+
comboDisplay.style.opacity = '0';
|
| 2158 |
+
|
| 2159 |
+
// Check if player is defeated
|
| 2160 |
+
if (health <= 0) {
|
| 2161 |
+
gameOver();
|
| 2162 |
+
}
|
| 2163 |
+
}
|
| 2164 |
+
|
| 2165 |
+
// Game over
|
| 2166 |
+
function gameOver() {
|
| 2167 |
+
gameActive = false;
|
| 2168 |
+
finalScoreDisplay.textContent = score;
|
| 2169 |
+
gameOverOverlay.classList.add('active');
|
| 2170 |
+
|
| 2171 |
+
// Stop ambient water sound
|
| 2172 |
+
waterAmbientSound.pause();
|
| 2173 |
+
waterAmbientSound.currentTime = 0;
|
| 2174 |
+
}
|
| 2175 |
+
|
| 2176 |
+
// Show notification
|
| 2177 |
+
function showNotification(text) {
|
| 2178 |
+
powerUpNotification.textContent = text;
|
| 2179 |
+
powerUpNotification.style.opacity = '1';
|
| 2180 |
+
powerUpNotification.style.transform = 'translateX(-50%) translateY(-20px)';
|
| 2181 |
+
|
| 2182 |
+
setTimeout(() => {
|
| 2183 |
+
powerUpNotification.style.opacity = '0';
|
| 2184 |
+
powerUpNotification.style.transform = 'translateX(-50%) translateY(0)';
|
| 2185 |
+
}, 2000);
|
| 2186 |
+
}
|
| 2187 |
+
|
| 2188 |
+
// Update coins
|
| 2189 |
+
function updateCoins(amount) {
|
| 2190 |
+
coins += amount;
|
| 2191 |
+
document.getElementById('coin-count').textContent = coins;
|
| 2192 |
+
}
|
| 2193 |
+
|
| 2194 |
+
// Event listeners
|
| 2195 |
+
startButton.addEventListener('click', () => {
|
| 2196 |
+
startOverlay.classList.remove('active');
|
| 2197 |
+
initGame();
|
| 2198 |
+
});
|
| 2199 |
+
|
| 2200 |
+
restartButton.addEventListener('click', () => {
|
| 2201 |
+
gameOverOverlay.classList.remove('active');
|
| 2202 |
+
initGame();
|
| 2203 |
+
});
|
| 2204 |
+
|
| 2205 |
+
gameOverCloseButton.addEventListener('click', () => {
|
| 2206 |
+
gameOverOverlay.classList.remove('active');
|
| 2207 |
+
});
|
| 2208 |
+
|
| 2209 |
+
// Keyboard controls
|
| 2210 |
+
document.addEventListener('keydown', (e) => {
|
| 2211 |
+
keys[e.key] = true;
|
| 2212 |
+
|
| 2213 |
+
// Fire on space
|
| 2214 |
+
if (e.key === ' ' && gameActive) {
|
| 2215 |
+
fireProjectile();
|
| 2216 |
+
}
|
| 2217 |
+
});
|
| 2218 |
+
|
| 2219 |
+
document.addEventListener('keyup', (e) => {
|
| 2220 |
+
keys[e.key] = false;
|
| 2221 |
+
});
|
| 2222 |
+
|
| 2223 |
+
// Mobile controls
|
| 2224 |
+
mobileUpButton.addEventListener('touchstart', () => {
|
| 2225 |
+
keys['ArrowUp'] = true;
|
| 2226 |
+
});
|
| 2227 |
+
|
| 2228 |
+
mobileUpButton.addEventListener('touchend', () => {
|
| 2229 |
+
keys['ArrowUp'] = false;
|
| 2230 |
+
});
|
| 2231 |
+
|
| 2232 |
+
mobileDownButton.addEventListener('touchstart', () => {
|
| 2233 |
+
keys['ArrowDown'] = true;
|
| 2234 |
+
});
|
| 2235 |
+
|
| 2236 |
+
mobileDownButton.addEventListener('touchend', () => {
|
| 2237 |
+
keys['ArrowDown'] = false;
|
| 2238 |
+
});
|
| 2239 |
+
|
| 2240 |
+
// Combat controls
|
| 2241 |
+
combatFireButton.addEventListener('click', () => {
|
| 2242 |
+
if (gameActive) {
|
| 2243 |
+
fireProjectile();
|
| 2244 |
+
}
|
| 2245 |
+
});
|
| 2246 |
+
|
| 2247 |
+
combatSpecialButton.addEventListener('click', () => {
|
| 2248 |
+
if (gameActive && power >= 50) {
|
| 2249 |
+
// Special attack: multiple projectiles
|
| 2250 |
+
for (let i = -2; i <= 2; i++) {
|
| 2251 |
+
const angle = i * Math.PI / 10;
|
| 2252 |
+
const projectile = {
|
| 2253 |
+
x: player.x + player.width,
|
| 2254 |
+
y: player.y + player.height / 2,
|
| 2255 |
+
size: 5,
|
| 2256 |
+
speed: 10,
|
| 2257 |
+
color: '#FF8C00',
|
| 2258 |
+
type: 'special',
|
| 2259 |
+
vx: Math.cos(angle) * 10,
|
| 2260 |
+
vy: Math.sin(angle) * 10
|
| 2261 |
+
};
|
| 2262 |
+
|
| 2263 |
+
projectiles.push(projectile);
|
| 2264 |
+
}
|
| 2265 |
+
|
| 2266 |
+
// Use power
|
| 2267 |
+
power -= 50;
|
| 2268 |
+
powerBar.style.width = `${power}%`;
|
| 2269 |
+
|
| 2270 |
+
// Play sound
|
| 2271 |
+
weaponSound.currentTime = 0;
|
| 2272 |
+
weaponSound.play();
|
| 2273 |
+
}
|
| 2274 |
+
});
|
| 2275 |
+
|
| 2276 |
+
// Auto-fire toggle
|
| 2277 |
+
autoFireToggle.addEventListener('click', () => {
|
| 2278 |
+
gameSettings.combat.autoFireEnabled = !gameSettings.combat.autoFireEnabled;
|
| 2279 |
+
autoFireToggle.style.backgroundColor = gameSettings.combat.autoFireEnabled ?
|
| 2280 |
+
'rgba(0, 255, 0, 0.7)' : 'rgba(255, 0, 0, 0.7)';
|
| 2281 |
+
});
|
| 2282 |
+
|
| 2283 |
+
// Auto-targeting toggle
|
| 2284 |
+
autoTargetToggle.addEventListener('click', () => {
|
| 2285 |
+
gameSettings.combat.autoTargetingEnabled = !gameSettings.combat.autoTargetingEnabled;
|
| 2286 |
+
autoTargetToggle.style.backgroundColor = gameSettings.combat.autoTargetingEnabled ?
|
| 2287 |
+
'rgba(0, 255, 0, 0.7)' : 'rgba(255, 0, 0, 0.7)';
|
| 2288 |
+
});
|
| 2289 |
+
|
| 2290 |
+
// Resize canvas when window is resized
|
| 2291 |
+
window.addEventListener('resize', () => {
|
| 2292 |
+
canvas.width = canvas.clientWidth;
|
| 2293 |
+
canvas.height = canvas.clientHeight;
|
| 2294 |
+
});
|
| 2295 |
+
});
|
| 2296 |
+
|
| 2297 |
+
// Whale Animator Class
|
| 2298 |
+
class WhaleAnimator {
|
| 2299 |
+
constructor(gameContext) {
|
| 2300 |
+
this.ctx = gameContext;
|
| 2301 |
+
this.whaleImages = [];
|
| 2302 |
+
this.animationSequences = {
|
| 2303 |
+
swim: {
|
| 2304 |
+
frames: [1, 2, 3, 4, 5, 6, 7, 8],
|
| 2305 |
+
frameRate: 8, // frames per second
|
| 2306 |
+
loop: true
|
| 2307 |
+
},
|
| 2308 |
+
attack: {
|
| 2309 |
+
frames: [10, 11, 12, 13, 14, 15],
|
| 2310 |
+
frameRate: 10,
|
| 2311 |
+
loop: false
|
| 2312 |
+
},
|
| 2313 |
+
combat: {
|
| 2314 |
+
frames: [20, 21, 22, 23, 24, 25, 26, 27],
|
| 2315 |
+
frameRate: 12,
|
| 2316 |
+
loop: true
|
| 2317 |
+
},
|
| 2318 |
+
idle: {
|
| 2319 |
+
frames: [30, 31, 32, 33],
|
| 2320 |
+
frameRate: 4,
|
| 2321 |
+
loop: true
|
| 2322 |
+
},
|
| 2323 |
+
damaged: {
|
| 2324 |
+
frames: [40, 41, 42, 43],
|
| 2325 |
+
frameRate: 8,
|
| 2326 |
+
loop: false
|
| 2327 |
+
}
|
| 2328 |
+
};
|
| 2329 |
+
|
| 2330 |
+
this.currentAnimation = 'swim';
|
| 2331 |
+
this.currentFrame = 0;
|
| 2332 |
+
this.frameCounter = 0;
|
| 2333 |
+
this.lastFrameTime = 0;
|
| 2334 |
+
}
|
| 2335 |
+
|
| 2336 |
+
// Load all whale images
|
| 2337 |
+
async loadImages() {
|
| 2338 |
+
const imagePaths = [];
|
| 2339 |
+
for (let i = 1; i <= 200; i++) {
|
| 2340 |
+
imagePaths.push(`/home/ubuntu/processed_images_hd/1 (${i}).png`);
|
| 2341 |
+
}
|
| 2342 |
+
|
| 2343 |
+
// Load all images
|
| 2344 |
+
for (let path of imagePaths) {
|
| 2345 |
+
const img = new Image();
|
| 2346 |
+
img.src = path;
|
| 2347 |
+
await new Promise(resolve => {
|
| 2348 |
+
img.onload = resolve;
|
| 2349 |
+
img.onerror = resolve; // Continue even if some images fail to load
|
| 2350 |
+
});
|
| 2351 |
+
this.whaleImages.push(img);
|
| 2352 |
+
}
|
| 2353 |
+
|
| 2354 |
+
console.log(`Loaded ${this.whaleImages.length} whale images`);
|
| 2355 |
+
return this.whaleImages.length > 0;
|
| 2356 |
+
}
|
| 2357 |
+
|
| 2358 |
+
// Set current animation
|
| 2359 |
+
setAnimation(animationName) {
|
| 2360 |
+
if (this.animationSequences[animationName] && this.currentAnimation !== animationName) {
|
| 2361 |
+
this.currentAnimation = animationName;
|
| 2362 |
+
this.currentFrame = 0;
|
| 2363 |
+
this.frameCounter = 0;
|
| 2364 |
+
}
|
| 2365 |
+
}
|
| 2366 |
+
|
| 2367 |
+
// Update animation frame
|
| 2368 |
+
update(deltaTime) {
|
| 2369 |
+
const sequence = this.animationSequences[this.currentAnimation];
|
| 2370 |
+
if (!sequence) return;
|
| 2371 |
+
|
| 2372 |
+
this.frameCounter += deltaTime * sequence.frameRate;
|
| 2373 |
+
|
| 2374 |
+
if (this.frameCounter >= 1) {
|
| 2375 |
+
this.currentFrame = (this.currentFrame + Math.floor(this.frameCounter)) % sequence.frames.length;
|
| 2376 |
+
this.frameCounter = this.frameCounter % 1;
|
| 2377 |
+
|
| 2378 |
+
// If animation is not looping and we reached the end
|
| 2379 |
+
if (!sequence.loop && this.currentFrame === sequence.frames.length - 1) {
|
| 2380 |
+
// Switch back to swimming animation
|
| 2381 |
+
this.setAnimation('swim');
|
| 2382 |
+
}
|
| 2383 |
+
}
|
| 2384 |
+
}
|
| 2385 |
+
|
| 2386 |
+
// Draw the current animation frame
|
| 2387 |
+
draw(x, y, width, height, flipX = false) {
|
| 2388 |
+
const sequence = this.animationSequences[this.currentAnimation];
|
| 2389 |
+
if (!sequence || this.whaleImages.length === 0) return;
|
| 2390 |
+
|
| 2391 |
+
const frameIndex = sequence.frames[this.currentFrame];
|
| 2392 |
+
if (frameIndex >= this.whaleImages.length) return;
|
| 2393 |
+
|
| 2394 |
+
const image = this.whaleImages[frameIndex];
|
| 2395 |
+
|
| 2396 |
+
this.ctx.save();
|
| 2397 |
+
|
| 2398 |
+
if (flipX) {
|
| 2399 |
+
this.ctx.translate(x + width, y);
|
| 2400 |
+
this.ctx.scale(-1, 1);
|
| 2401 |
+
this.ctx.drawImage(image, 0, 0, width, height);
|
| 2402 |
+
} else {
|
| 2403 |
+
this.ctx.drawImage(image, x, y, width, height);
|
| 2404 |
+
}
|
| 2405 |
+
|
| 2406 |
+
this.ctx.restore();
|
| 2407 |
+
}
|
| 2408 |
+
|
| 2409 |
+
// Create a swimming animation
|
| 2410 |
+
animateSwimming(x, y, width, height, speed) {
|
| 2411 |
+
this.setAnimation('swim');
|
| 2412 |
+
|
| 2413 |
+
// Add wave-like motion
|
| 2414 |
+
const waveAmplitude = 5;
|
| 2415 |
+
const waveFrequency = 0.1;
|
| 2416 |
+
const offsetY = Math.sin(Date.now() * waveFrequency) * waveAmplitude;
|
| 2417 |
+
|
| 2418 |
+
this.draw(x, y + offsetY, width, height);
|
| 2419 |
+
}
|
| 2420 |
+
|
| 2421 |
+
// Create an attack animation
|
| 2422 |
+
animateAttack(x, y, width, height) {
|
| 2423 |
+
this.setAnimation('attack');
|
| 2424 |
+
|
| 2425 |
+
// Add forward thrust motion during attack
|
| 2426 |
+
const thrustDistance = 10;
|
| 2427 |
+
const attackProgress = this.currentFrame / this.animationSequences.attack.frames.length;
|
| 2428 |
+
const thrustOffset = Math.sin(attackProgress * Math.PI) * thrustDistance;
|
| 2429 |
+
|
| 2430 |
+
this.draw(x + thrustOffset, y, width, height);
|
| 2431 |
+
}
|
| 2432 |
+
|
| 2433 |
+
// Create a combat animation
|
| 2434 |
+
animateCombat(x, y, width, height) {
|
| 2435 |
+
this.setAnimation('combat');
|
| 2436 |
+
|
| 2437 |
+
// Add combat effects (rotation, scaling)
|
| 2438 |
+
const rotationAmount = 0.05;
|
| 2439 |
+
const scaleAmount = 0.1;
|
| 2440 |
+
const rotationOffset = Math.sin(Date.now() * 0.01) * rotationAmount;
|
| 2441 |
+
const scaleOffset = 1 + Math.abs(Math.sin(Date.now() * 0.02)) * scaleAmount;
|
| 2442 |
+
|
| 2443 |
+
this.ctx.save();
|
| 2444 |
+
this.ctx.translate(x + width/2, y + height/2);
|
| 2445 |
+
this.ctx.rotate(rotationOffset);
|
| 2446 |
+
this.ctx.scale(scaleOffset, scaleOffset);
|
| 2447 |
+
this.ctx.translate(-(x + width/2), -(y + height/2));
|
| 2448 |
+
|
| 2449 |
+
this.draw(x, y, width, height);
|
| 2450 |
+
|
| 2451 |
+
this.ctx.restore();
|
| 2452 |
+
}
|
| 2453 |
+
|
| 2454 |
+
// Create a damaged animation
|
| 2455 |
+
animateDamaged(x, y, width, height) {
|
| 2456 |
+
this.setAnimation('damaged');
|
| 2457 |
+
|
| 2458 |
+
// Add flash effect
|
| 2459 |
+
const flashRate = 100; // ms
|
| 2460 |
+
const shouldFlash = Math.floor(Date.now() / flashRate) % 2 === 0;
|
| 2461 |
+
|
| 2462 |
+
if (shouldFlash) {
|
| 2463 |
+
this.ctx.globalAlpha = 0.7;
|
| 2464 |
+
}
|
| 2465 |
+
|
| 2466 |
+
this.draw(x, y, width, height);
|
| 2467 |
+
|
| 2468 |
+
this.ctx.globalAlpha = 1.0;
|
| 2469 |
+
}
|
| 2470 |
+
}
|
| 2471 |
+
</script>
|
| 2472 |
+
</body>
|
| 2473 |
</html>
|