offerpk3 commited on
Commit
a0d3437
·
verified ·
1 Parent(s): 1592055

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +2472 -18
index.html CHANGED
@@ -1,19 +1,2473 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>