cutechicken commited on
Commit
1c303ed
·
verified ·
1 Parent(s): ca81386

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +538 -756
index.html CHANGED
@@ -33,9 +33,9 @@
33
  z-index: 1000;
34
  font-size: 18px;
35
  }
36
- .button {
37
  position: fixed;
38
- top: 80%; /* 70%에서 80%로 변경하여 버튼을 더 아래로 */
39
  left: 50%;
40
  transform: translate(-50%, -50%);
41
  padding: 20px 40px;
@@ -46,7 +46,7 @@
46
  border-radius: 5px;
47
  cursor: pointer;
48
  display: none;
49
- z-index: 999;
50
  }
51
  #countdown {
52
  position: fixed;
@@ -59,70 +59,22 @@
59
  z-index: 1000;
60
  display: none;
61
  }
62
- #titleScreen {
63
- position: fixed;
64
- top: 0;
65
- left: 0;
66
- width: 100%;
67
- height: 100%;
68
- background: url('city2.png') no-repeat center center;
69
- background-size: cover;
70
- z-index: 2000;
71
- display: flex;
72
- flex-direction: column;
73
- justify-content: center;
74
- align-items: center;
75
- }
76
- #titleScreen h1 {
77
- font-size: 72px;
78
- color: white;
79
- text-shadow: 2px 2px 5px black;
80
- margin-bottom: 50px;
81
- }
82
- #startButton {
83
- padding: 20px 50px;
84
- font-size: 32px;
85
- background: #4CAF50;
86
- color: white;
87
- border: none;
88
- border-radius: 5px;
89
- cursor: pointer;
90
- }
91
- #shop {
92
- position: fixed;
93
- top: 40%; /* 50%에서 40%로 변경하여 상점을 더 위로 */
94
- left: 50%;
95
- transform: translate(-50%, -50%);
96
- background: rgba(0,0,0,0.9);
97
- padding: 20px;
98
- border-radius: 10px;
99
- color: white;
100
- z-index: 1000;
101
- display: none;
102
- }
103
- #nextRound {
104
- position: fixed;
105
- top: 85%; /* 새로운 위치 지정 */
106
- left: 50%;
107
- transform: translate(-50%, -50%);
108
- z-index: 999;
109
- }
110
-
111
- #restart {
112
- position: fixed;
113
- top: 85%; /* 새로운 위치 지정 */
114
- left: 50%;
115
- transform: translate(-50%, -50%);
116
- z-index: 999;
117
- }
118
-
119
- #bossButton {
120
- position: fixed;
121
- top: 85%; /* 새로운 위치 지정 */
122
- left: 50%;
123
- transform: translate(-50%, -50%);
124
- z-index: 999;
125
- }
126
  </style>
127
  </head>
128
  <body>
@@ -139,176 +91,145 @@
139
  <button id="nextRound" class="button">Next Round</button>
140
  <button id="restart" class="button">Restart Game</button>
141
  <canvas id="gameCanvas"></canvas>
142
- <div id="titleScreen">
143
- <h1>TANK WAR</h1>
144
- <button id="startButton">START GAME</button>
145
- </div>
146
-
147
- <div id="shop" style="display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.9); padding:20px; border-radius:10px; color:white; z-index:1000;">
148
- <h2>Tank Shop</h2>
149
- <div style="display:flex; gap:20px;">
150
- <div id="tank1" style="text-align:center;">
151
- <h3>PZ.IV</h3>
152
- <img src="player2.png" width="90" height="50">
153
- <p>300 Gold</p>
154
- <p style="color: #4CAF50;">+50% HP</p>
155
- <button onclick="buyTank('player2.png', 300, 'tank1')">Buy</button>
156
- </div>
157
- <div id="tank2" style="text-align:center;">
158
- <h3>TIGER</h3>
159
- <img src="player3.png" width="110" height="55">
160
- <p>500 Gold</p>
161
- <p style="color: #4CAF50;">+100% HP</p>
162
- <p style="color: #ff6b6b;">-30% Speed</p>
163
- <button onclick="buyTank('player3.png', 500, 'tank2')">Buy</button>
164
- </div>
165
- <div id="bf109" style="text-align:center;">
166
- <h3>BF-109</h3>
167
- <img src="bf109.png" width="100" height="100">
168
- <p>1000 Gold</p>
169
- <p style="color: #4CAF50;">Air support from BF-109</p>
170
- <button onclick="buyBF109()">Buy</button>
171
- </div>
172
- <div id="ju87" style="text-align:center;">
173
- <h3>JU-87</h3>
174
- <img src="ju87.png" width="100" height="100">
175
- <p>1500 Gold</p>
176
- <p style="color: #4CAF50;">Get ju-87 air support</p>
177
- <button onclick="buyJU87()">Buy</button>
178
- </div>
179
- <div id="apcr" style="text-align:center;">
180
- <h3>APCR</h3>
181
- <img src="apcr.png" width="80" height="20">
182
- <p>1000 Gold</p>
183
- <p style="color: #4CAF50;">+100% Bullet Speed</p>
184
- <button onclick="buyAPCR()">Buy</button>
185
- </div>
186
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  </div>
 
188
  <button id="bossButton" class="button">Fight Boss!</button>
189
  <div id="winMessage" class="button" style="font-size: 72px; background: none;">You Win!</div>
190
- <script>
191
- const canvas = document.getElementById('gameCanvas');
192
- const ctx = canvas.getContext('2d');
193
- const nextRoundBtn = document.getElementById('nextRound');
194
- const restartBtn = document.getElementById('restart');
195
- const weaponInfo = document.getElementById('weaponInfo');
196
- const countdownEl = document.getElementById('countdown');
197
- const bossButton = document.getElementById('bossButton');
198
- canvas.width = window.innerWidth;
199
- canvas.height = window.innerHeight;
200
-
201
- // Game state
202
- let currentRound = 1;
203
- let gameOver = false;
204
- let currentWeapon = 'cannon';
205
- let enemies = [];
206
- let bullets = [];
207
- let items = [];
208
- let lastShot = 0;
209
- let isCountingDown = true;
210
- let countdownTime = 3;
211
- let autoFire = false;
212
- let gold = 0;
213
- let isBossStage = false;
214
- let effects = [];
215
- let hasAPCR = false;
216
- let hasBF109 = false;
217
- let hasJU87 = false;
218
- let lastJU87Spawn = 0;
219
- let supportUnits = [];
220
- let lastSupportSpawn = 0;
221
-
222
- // Load assets
223
- const backgroundImg = new Image();
224
- backgroundImg.src = 'city.png';
225
- const playerImg = new Image();
226
- playerImg.src = 'player.png';
227
- const enemyImg = new Image();
228
- enemyImg.src = 'enemy.png';
229
- const bulletImg = new Image();
230
- bulletImg.src = 'apcr2.png';
231
-
232
- // Audio setup
233
- const cannonSound = new Audio('firemn.ogg');
234
- const machinegunSound = new Audio('firemg.ogg');
235
- const enemyFireSound = new Audio('fireenemy.ogg');
236
- let bgm = new Audio('title.ogg');
237
- const countSound = new Audio('count.ogg');
238
- const deathSound = new Audio('death.ogg');
239
- bgm.loop = true;
240
- enemyFireSound.volume = 0.5;
241
-
242
- const weapons = {
243
- cannon: {
244
- fireRate: 1000,
245
- damage: 0.25,
246
- bulletSize: 5,
247
- sound: cannonSound
248
- },
249
- machinegun: {
250
- fireRate: 200,
251
- damage: 0.05,
252
- bulletSize: 2,
253
- sound: machinegunSound
254
- }
255
- };
256
-
257
- // Player setup
258
- const player = {
259
- x: canvas.width/2,
260
- y: canvas.height/2,
261
- speed: 5,
262
- angle: 0,
263
- width: 100,
264
- height: 45,
265
- health: 1000,
266
- maxHealth: 1000
267
- };
268
-
269
- // 다음 라운드 버튼 이벤트 리스너 추가
270
- nextRoundBtn.addEventListener('click', () => {
271
- currentRound++;
272
- document.getElementById('shop').style.display = 'none';
273
- nextRoundBtn.style.display = 'none';
274
- initRound();
275
- });
276
-
277
- // 재시작 버튼 이벤트 리스너 추가
278
- restartBtn.addEventListener('click', () => {
279
- currentRound = 1;
280
- gameOver = false;
281
- gold = 0;
282
- player.health = player.maxHealth;
283
- restartBtn.style.display = 'none';
284
- document.getElementById('winMessage').style.display = 'none';
285
- initRound();
286
- });
287
-
288
- // 보스 버튼 이벤트 리스너 추가
289
- bossButton.addEventListener('click', () => {
290
- startBossStage();
291
- });
292
-
293
- function startCountdown() {
294
- isCountingDown = true;
295
- countdownTime = 3;
296
- countdownEl.style.display = 'block';
297
- countdownEl.textContent = countdownTime;
298
- bgm.pause();
299
- countSound.play();
300
- const countInterval = setInterval(() => {
301
- countdownTime--;
302
- if(countdownTime <= 0) {
303
- clearInterval(countInterval);
304
- countdownEl.style.display = 'none';
305
- isCountingDown = false;
306
- bgm.play();
307
  }
308
- countdownEl.textContent = countdownTime > 0 ? countdownTime : 'GO!';
309
- }, 1000);
310
- }
311
- class Effect {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  constructor(x, y, duration, type, angle = 0, parent = null) {
313
  this.x = x;
314
  this.y = y;
@@ -316,8 +237,8 @@
316
  this.duration = duration;
317
  this.type = type;
318
  this.angle = angle;
319
- this.parent = parent;
320
- this.offset = { x: Math.cos(angle) * 30, y: Math.sin(angle) * 30 };
321
  this.img = new Image();
322
  this.img.src = type === 'death' ? 'bang.png' : 'fire2.png';
323
  }
@@ -334,8 +255,7 @@
334
  return Date.now() - this.startTime > this.duration;
335
  }
336
  }
337
-
338
- class SupportUnit {
339
  constructor(yPosition) {
340
  this.x = 0;
341
  this.y = yPosition;
@@ -346,13 +266,15 @@ class SupportUnit {
346
  this.angle = 0;
347
  this.img = new Image();
348
  this.img.src = 'bf109.png';
349
- this.hasPlayedSound = false;
350
- this.mgSound = null;
351
  }
352
 
353
  update() {
 
354
  this.x += this.speed;
355
 
 
356
  if (isCountingDown) {
357
  if (this.mgSound) {
358
  this.mgSound.pause();
@@ -361,6 +283,7 @@ class SupportUnit {
361
  this.hasPlayedSound = false;
362
  }
363
 
 
364
  const now = Date.now();
365
  if (now - this.lastShot > 200 && !isCountingDown) {
366
  this.shoot();
@@ -370,6 +293,7 @@ class SupportUnit {
370
  }
371
 
372
  shoot() {
 
373
  if (!this.hasPlayedSound) {
374
  const firstSound = new Audio('bf109mg.ogg');
375
  firstSound.volume = 1.0;
@@ -377,9 +301,10 @@ class SupportUnit {
377
  this.hasPlayedSound = true;
378
  }
379
 
 
380
  if (!isCountingDown) {
381
  const shootSound = new Audio('bf109mgse.ogg');
382
- shootSound.volume = 0.5;
383
  shootSound.play();
384
  }
385
 
@@ -394,8 +319,7 @@ class SupportUnit {
394
  });
395
  }
396
  }
397
-
398
- class JU87 {
399
  constructor() {
400
  this.x = canvas.width;
401
  this.y = 50;
@@ -412,7 +336,7 @@ class JU87 {
412
  this.hasPlayedMGSound = false;
413
  this.isReturning = false;
414
  this.circleAngle = 0;
415
- this.returningToCenter = false;
416
  }
417
 
418
  selectTarget() {
@@ -420,34 +344,35 @@ class JU87 {
420
  enemies[Math.floor(Math.random() * enemies.length)] : null;
421
  }
422
 
423
- shoot() {
424
- if (!this.hasPlayedMGSound && !isCountingDown) {
425
- const mgSound = new Audio('ju87mg.ogg');
426
- mgSound.volume = 1.0;
427
- mgSound.currentTime = 0;
428
- mgSound.play().catch(error => console.error('Audio play failed:', error));
429
- this.hasPlayedMGSound = true;
430
- }
 
431
 
432
- [[20, 50], [80, 50]].forEach(([x, y]) => {
433
- const offsetX = x - 50;
434
- const offsetY = y - 50;
435
-
436
- const rotatedX = this.x + (Math.cos(this.angle) * offsetX - Math.sin(this.angle) * offsetY);
437
- const rotatedY = this.y + (Math.sin(this.angle) * offsetX + Math.cos(this.angle) * offsetY);
 
438
 
439
- bullets.push({
440
- x: rotatedX,
441
- y: rotatedY,
442
- angle: this.angle,
443
- speed: 10,
444
- isEnemy: false,
445
- damage: weapons.machinegun.damage * 2,
446
- size: weapons.machinegun.bulletSize
447
- });
448
  });
449
- }
450
-
451
  update() {
452
  if (!this.hasPlayedSound) {
453
  const sirenSound = new Audio('ju87siren.ogg');
@@ -511,147 +436,89 @@ class JU87 {
511
  return true;
512
  }
513
  }
514
- class Enemy {
515
- constructor(isBoss = false) {
516
- this.x = Math.random() * canvas.width;
517
- this.y = Math.random() * canvas.height;
518
- this.health = isBoss ? 15000 : 1000;
519
- this.maxHealth = this.health;
520
- this.speed = isBoss ? 1 : 2;
521
- this.lastShot = 0;
522
- this.shootInterval = isBoss ? 1000 : 1000;
523
- this.angle = 0;
524
- this.width = 100;
525
- this.height = 45;
526
- this.moveTimer = 0;
527
- this.moveInterval = Math.random() * 2000 + 1000;
528
- this.moveAngle = Math.random() * Math.PI * 2;
529
- this.isBoss = isBoss;
530
-
531
- if (isBoss) {
532
- this.enemyImg = new Image();
533
- this.enemyImg.src = 'boss.png';
534
- } else if (currentRound >= 7) {
535
- this.enemyImg = new Image();
536
- this.enemyImg.src = 'enemy3.png';
537
- } else if (currentRound >= 4) {
538
- this.enemyImg = new Image();
539
- this.enemyImg.src = 'enemy2.png';
540
- }
541
- }
542
-
543
- update() {
544
- if(isCountingDown) return;
545
- const now = Date.now();
546
-
547
- if (now - this.moveTimer > this.moveInterval) {
548
- this.moveAngle = Math.random() * Math.PI * 2;
549
- this.moveTimer = now;
550
- }
551
- this.x += Math.cos(this.moveAngle) * this.speed;
552
- this.y += Math.sin(this.moveAngle) * this.speed;
553
- this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
554
- this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
555
- this.angle = Math.atan2(player.y - this.y, player.x - this.x);
556
-
557
- if (now - this.lastShot > this.shootInterval && !isCountingDown) {
558
- this.shoot();
559
- this.lastShot = now;
560
- }
561
- }
562
-
563
- shoot() {
564
- const sound = this.isBoss ? new Audio('firemn.ogg') : enemyFireSound.cloneNode();
565
- sound.play();
566
-
567
- effects.push(new Effect(
568
- this.x + Math.cos(this.angle) * 30,
569
- this.y + Math.sin(this.angle) * 30,
570
- 500,
571
- 'fire',
572
- this.angle,
573
- this
574
- ));
575
-
576
- bullets.push({
577
- x: this.x + Math.cos(this.angle) * 30,
578
- y: this.y + Math.sin(this.angle) * 30,
579
- angle: this.angle,
580
- speed: this.isBoss ? 10 : 5,
581
- isEnemy: true,
582
- size: this.isBoss ? 5 : 3,
583
- damage: this.isBoss ? 300 : 150
584
- });
585
- }
586
- }
587
-
588
- nextRoundBtn.addEventListener('click', () => {
589
- currentRound++;
590
- nextRoundBtn.style.display = 'none';
591
- document.getElementById('shop').style.display = 'none';
592
- initRound();
593
- });
594
-
595
- r// restartBtn 이벤트 리스너 수정
596
- restartBtn.addEventListener('click', () => {
597
- // 게임 상태 완전 초기화
598
- currentRound = 1;
599
- gameOver = false;
600
- isBossStage = false;
601
- player.health = player.maxHealth;
602
- gold = 0;
603
- hasAPCR = false;
604
- hasBF109 = false;
605
- hasJU87 = false;
606
- enemies = [];
607
- bullets = [];
608
- items = [];
609
- supportUnits = [];
610
- effects = [];
611
-
612
- // UI 요소 초기화
613
- document.getElementById('apcr').style.display = 'block';
614
- document.getElementById('bf109').style.display = 'block';
615
- document.getElementById('ju87').style.display = 'block';
616
- document.getElementById('tank1').style.display = 'block';
617
- document.getElementById('tank2').style.display = 'block';
618
- document.getElementById('winMessage').style.display = 'none';
619
- document.getElementById('bossButton').style.display = 'none';
620
- restartBtn.style.display = 'none';
621
-
622
- // 플레이어 초기화
623
- playerImg.src = 'player.png';
624
- player.maxHealth = defaultPlayerStats.maxHealth;
625
- player.speed = defaultPlayerStats.speed;
626
- player.width = defaultPlayerStats.width;
627
- player.height = defaultPlayerStats.height;
628
- player.health = player.maxHealth;
629
-
630
- // BGM 초기화
631
- bgm.pause();
632
- bgm.src = 'BGM2.ogg';
633
- bgm.currentTime = 0;
634
- bgm.play().catch(err => console.error("Error playing game music:", err));
635
 
636
- // 게임 재시작
637
- initRound();
638
- });
639
-
640
- bossButton.addEventListener('click', () => {
641
- startBossStage();
642
- });
643
-
644
- function showShop() {
645
- document.getElementById('shop').style.display = 'block';
 
 
 
 
 
 
 
 
 
646
  }
647
-
 
 
 
 
648
  const defaultPlayerStats = {
649
  maxHealth: 1000,
650
  speed: 5,
651
  width: 100,
652
  height: 45
653
  };
654
-
655
  function buyTank(tankImg, cost, tankId) {
656
  if (gold >= cost) {
657
  gold -= cost;
@@ -659,24 +526,23 @@ function buyTank(tankImg, cost, tankId) {
659
  document.getElementById(tankId).style.display = 'none';
660
  document.getElementById('shop').style.display = 'none';
661
 
662
- if (tankId === 'tank1') {
663
  player.maxHealth = 1500;
664
- player.speed = defaultPlayerStats.speed;
665
- player.width = 90;
666
- player.height = 50;
667
  }
668
- else if (tankId === 'tank2') {
669
  player.maxHealth = 2000;
670
  player.speed = defaultPlayerStats.speed * 0.7;
671
- player.width = 100;
672
- player.height = 45;
673
  }
674
 
675
  player.health = player.maxHealth;
676
  }
677
  }
678
-
679
- function buyAPCR() {
680
  if (gold >= 1000 && !hasAPCR) {
681
  gold -= 1000;
682
  hasAPCR = true;
@@ -684,8 +550,7 @@ function buyAPCR() {
684
  document.getElementById('shop').style.display = 'none';
685
  }
686
  }
687
-
688
- function buyBF109() {
689
  if (gold >= 1000 && !hasBF109) {
690
  gold -= 1000;
691
  hasBF109 = true;
@@ -693,18 +558,16 @@ function buyBF109() {
693
  document.getElementById('shop').style.display = 'none';
694
  }
695
  }
696
-
697
- function buyJU87() {
698
  if (gold >= 1500 && !hasJU87) {
699
  gold -= 1500;
700
  hasJU87 = true;
701
  document.getElementById('ju87').style.display = 'none';
702
  document.getElementById('shop').style.display = 'none';
703
- lastJU87Spawn = Date.now();
704
  }
705
  }
706
- // initRound 함수 수정
707
- function initRound() {
708
  enemies = [];
709
  for(let i = 0; i < 1 * currentRound; i++) {
710
  enemies.push(new Enemy());
@@ -714,45 +577,89 @@ function initRound() {
714
  items = [];
715
  supportUnits = [];
716
  lastSupportSpawn = 0;
717
-
718
- // 모든 UI 요소 숨기기
719
- nextRoundBtn.style.display = 'none';
720
- document.getElementById('shop').style.display = 'none';
721
-
722
  startCountdown();
723
 
 
724
  setTimeout(() => {
725
  if (hasJU87) {
726
  supportUnits.push(new JU87());
727
  lastJU87Spawn = Date.now();
728
  }
729
- }, 3000);
730
  }
731
- function startBossStage() {
732
- isBossStage = true;
733
- enemies = [];
734
- enemies.push(new Enemy(true));
735
- player.health = player.maxHealth;
736
- bullets = [];
737
- items = [];
738
- document.getElementById('bossButton').style.display = 'none';
739
- bgm.src = 'BGM.ogg';
740
- startCountdown();
741
- }
742
- function updateGame() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
743
  if(gameOver) return;
744
  if(!isCountingDown) {
745
- if(keys['w']) player.y -= player.speed;
746
- if(keys['s']) player.y += player.speed;
747
- if(keys['a']) player.x -= player.speed;
748
- if(keys['d']) player.x += player.speed;
749
- player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
750
- player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
751
- fireBullet();
 
752
  }
 
 
753
  if (hasBF109 && !isCountingDown) {
754
  const now = Date.now();
755
- if (now - lastSupportSpawn > 10000) {
756
  supportUnits.push(
757
  new SupportUnit(canvas.height * 0.2),
758
  new SupportUnit(canvas.height * 0.5),
@@ -761,136 +668,220 @@ startCountdown();
761
  lastSupportSpawn = now;
762
  }
763
  }
764
-
 
765
  if (hasJU87 && !isCountingDown) {
766
  const now = Date.now();
767
- if (now - lastJU87Spawn > 15000) {
768
  supportUnits.push(new JU87());
769
  lastJU87Spawn = now;
770
  }
771
  }
772
-
 
773
  supportUnits = supportUnits.filter(unit => unit.update());
774
-
775
  enemies.forEach(enemy => enemy.update());
776
-
777
- if(!isCountingDown) {
778
- bullets = bullets.filter(bullet => {
779
- bullet.x += Math.cos(bullet.angle) * bullet.speed;
780
- bullet.y += Math.sin(bullet.angle) * bullet.speed;
781
- if(!bullet.isEnemy) {
782
- enemies = enemies.filter(enemy => {
783
- const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
784
- if(dist < 30) {
785
- let damage = currentWeapon === 'cannon' ? 250 : 50;
786
- enemy.health -= damage;
787
- if(enemy.health <= 0) {
788
- spawnHealthItem(enemy.x, enemy.y);
789
- gold += 100;
790
- effects.push(new Effect(enemy.x, enemy.y, 1000, 'death'));
791
- deathSound.cloneNode().play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
  return false;
793
  }
794
- return true;
795
  }
796
- return true;
 
797
  });
798
- } else {
799
- const dist = Math.hypot(bullet.x - player.x, bullet.y - player.y);
800
- if(dist < 30) {
801
- player.health -= bullet.damage || 100;
802
- if(player.health <= 0) {
803
- gameOver = true;
804
- restartBtn.style.display = 'block';
805
- effects.push(new Effect(player.x, player.y, 1000, 'death'));
806
- deathSound.cloneNode().play();
807
  }
808
- return false;
 
 
 
 
 
 
 
 
809
  }
 
 
 
 
 
 
 
810
  }
811
- return bullet.x >= 0 && bullet.x <= canvas.width &&
812
- bullet.y >= 0 && bullet.y <= canvas.height;
813
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
 
815
- items = items.filter(item => {
816
- const dist = Math.hypot(item.x - player.x, item.y - player.y);
817
- if(dist < 30) {
818
- player.health = Math.min(player.health + 200, player.maxHealth);
819
- return false;
820
- }
821
- return true;
822
- });
 
823
 
824
- // updateGame 함수 내의 enemies.length === 0 처리 부분 수정
825
- if(enemies.length === 0) {
826
- if (!isBossStage) {
827
- if(currentRound < 10) {
828
- nextRoundBtn.style.display = 'block';
829
- showShop();
830
- // 여러 실행되는 것을 방지하기 위해 빈 배열 추가
831
- enemies.push({health: -1}); // 임시 enemy 추가로 중복 실행 방지
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
832
  } else {
833
- bossButton.style.display = 'block';
 
 
 
 
 
 
 
834
  }
835
- } else {
836
- gameOver = true;
837
- document.getElementById('winMessage').style.display = 'block';
838
- restartBtn.style.display = 'block';
839
- bgm.pause();
840
- const victorySound = new Audio('victory.ogg');
841
- victorySound.play();
842
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
  }
844
- }
845
- }
846
- function spawnHealthItem(x, y) {
847
- items.push({x, y});
848
- }
849
- function drawHealthBar(x, y, health, maxHealth, width, height, color) {
850
- ctx.fillStyle = '#333';
851
- ctx.fillRect(x - width/2, y - height/2, width, height);
852
- ctx.fillStyle = color;
853
- ctx.fillRect(x - width/2, y - height/2, width * (health/maxHealth), height);
854
  }
 
 
855
  function gameLoop() {
856
- if (!gameOver) {
857
- updateGame();
858
- drawGame();
859
- requestAnimationFrame(gameLoop);
860
  }
861
- }
862
- document.addEventListener('DOMContentLoaded', () => {
863
- const titleScreen = document.getElementById('titleScreen');
864
- const startButton = document.getElementById('startButton');
865
- const instructions = document.getElementById('instructions');
866
- const weaponInfo = document.getElementById('weaponInfo');
867
- const gameCanvas = document.getElementById('gameCanvas');
868
- instructions.style.display = 'none';
869
- weaponInfo.style.display = 'none';
870
- gameCanvas.style.display = 'none';
871
-
872
- console.log("DOM Loaded");
873
 
874
- bgm.play().catch(err => console.error("Error playing title music:", err));
875
-
876
- startButton.addEventListener('click', () => {
877
- console.log("Start Button Clicked");
878
- if (!titleScreen || !instructions || !weaponInfo || !gameCanvas) {
879
- console.error("DOM elements not found");
880
- return;
881
- }
882
 
883
- titleScreen.style.display = 'none';
884
- instructions.style.display = 'block';
885
- weaponInfo.style.display = 'block';
886
- gameCanvas.style.display = 'block';
887
 
888
- bgm.pause();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  bgm.src = 'BGM2.ogg';
890
- bgm.play().catch(err => console.error("Error playing game music:", err));
891
-
892
  initRound();
893
- gameLoop();
894
  });
895
 
896
  Promise.all([
@@ -898,224 +889,15 @@ Promise.all([
898
  new Promise(resolve => playerImg.onload = resolve),
899
  new Promise(resolve => enemyImg.onload = resolve)
900
  ]).then(() => {
901
- console.log("Assets loaded successfully");
902
- }).catch(err => console.error("Error loading assets:", err));
903
-
904
- window.addEventListener('resize', () => {
905
- canvas.width = window.innerWidth;
906
- canvas.height = window.innerHeight;
907
- });
908
- });
909
- // 키보드 이벤트 리스너
910
- const keys = {};
911
- document.addEventListener('keydown', e => {
912
- keys[e.key] = true;
913
- if(e.key.toLowerCase() === 'c') {
914
- currentWeapon = currentWeapon === 'cannon' ? 'machinegun' : 'cannon';
915
- weaponInfo.textContent = `Current Weapon: ${currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1)}`;
916
- } else if(e.key.toLowerCase() === 'r') {
917
- autoFire = !autoFire;
918
- }
919
- });
920
- nextRoundBtn.addEventListener('click', () => {
921
- currentRound++;
922
- nextRoundBtn.style.display = 'none';
923
- document.getElementById('shop').style.display = 'none';
924
- initRound();
925
- });
926
- bossButton.addEventListener('click', () => {
927
- startBossStage();
928
- });
929
- restartBtn.addEventListener('click', () => {
930
- location.reload();
931
- });
932
- function initRound() {
933
- enemies = [];
934
- for(let i = 0; i < 1 * currentRound; i++) {
935
- enemies.push(new Enemy());
936
- }
937
- player.health = player.maxHealth;
938
- bullets = [];
939
- items = [];
940
- supportUnits = [];
941
- lastSupportSpawn = 0;
942
- startCountdown();
943
-
944
- setTimeout(() => {
945
- if (hasJU87) {
946
- supportUnits.push(new JU87());
947
- lastJU87Spawn = Date.now();
948
- }
949
- }, 3000);
950
- }
951
- function startBossStage() {
952
- isBossStage = true;
953
- enemies = [];
954
- enemies.push(new Enemy(true));
955
- player.health = player.maxHealth;
956
- bullets = [];
957
- items = [];
958
- document.getElementById('bossButton').style.display = 'none';
959
- bgm.src = 'BGM.ogg';
960
- bgm.play().catch(err => console.error("Error playing boss music:", err));
961
- startCountdown();
962
- }
963
- function gameLoop() {
964
- if (!gameOver) {
965
- updateGame();
966
- drawGame();
967
- requestAnimationFrame(gameLoop);
968
- }
969
- }
970
- document.addEventListener('DOMContentLoaded', () => {
971
- const titleScreen = document.getElementById('titleScreen');
972
- const startButton = document.getElementById('startButton');
973
- const instructions = document.getElementById('instructions');
974
- const weaponInfo = document.getElementById('weaponInfo');
975
- const gameCanvas = document.getElementById('gameCanvas');
976
- instructions.style.display = 'none';
977
- weaponInfo.style.display = 'none';
978
- gameCanvas.style.display = 'none';
979
-
980
- console.log("DOM Loaded");
981
-
982
- bgm.play().catch(err => console.error("Error playing title music:", err));
983
-
984
- startButton.addEventListener('click', () => {
985
- console.log("Start Button Clicked");
986
- titleScreen.style.display = 'none';
987
- instructions.style.display = 'block';
988
- weaponInfo.style.display = 'block';
989
- gameCanvas.style.display = 'block';
990
-
991
- bgm.pause();
992
- bgm = new Audio('BGM2.ogg');
993
- bgm.loop = true;
994
- bgm.play().catch(err => console.error("Error playing game music:", err));
995
-
996
- currentRound = 1; // 라운드 초기화 추가
997
  initRound();
998
  gameLoop();
 
999
  });
1000
 
1001
- Promise.all([
1002
- new Promise(resolve => backgroundImg.onload = resolve),
1003
- new Promise(resolve => playerImg.onload = resolve),
1004
- new Promise(resolve => enemyImg.onload = resolve)
1005
- ]).then(() => {
1006
- console.log("Assets loaded successfully");
1007
- }).catch(err => console.error("Error loading assets:", err));
1008
-
1009
  window.addEventListener('resize', () => {
1010
  canvas.width = window.innerWidth;
1011
  canvas.height = window.innerHeight;
1012
- });
1013
  });
1014
- function updateGame() {
1015
- if(gameOver) return;
1016
- if(!isCountingDown) {
1017
- if(keys['w']) player.y -= player.speed;
1018
- if(keys['s']) player.y += player.speed;
1019
- if(keys['a']) player.x -= player.speed;
1020
- if(keys['d']) player.x += player.speed;
1021
-
1022
- player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
1023
- player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
1024
-
1025
- fireBullet();
1026
- }
1027
-
1028
- if (hasBF109 && !isCountingDown) {
1029
- const now = Date.now();
1030
- if (now - lastSupportSpawn > 10000) {
1031
- supportUnits.push(
1032
- new SupportUnit(canvas.height * 0.2),
1033
- new SupportUnit(canvas.height * 0.5),
1034
- new SupportUnit(canvas.height * 0.8)
1035
- );
1036
- lastSupportSpawn = now;
1037
- }
1038
- }
1039
-
1040
- if (hasJU87 && !isCountingDown) {
1041
- const now = Date.now();
1042
- if (now - lastJU87Spawn > 15000) {
1043
- supportUnits.push(new JU87());
1044
- lastJU87Spawn = now;
1045
- }
1046
- }
1047
-
1048
- supportUnits = supportUnits.filter(unit => unit.update());
1049
- enemies.forEach(enemy => enemy.update());
1050
-
1051
- if(!isCountingDown) {
1052
- bullets = bullets.filter(bullet => {
1053
- bullet.x += Math.cos(bullet.angle) * bullet.speed;
1054
- bullet.y += Math.sin(bullet.angle) * bullet.speed;
1055
-
1056
- if(!bullet.isEnemy) {
1057
- enemies = enemies.filter(enemy => {
1058
- const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
1059
- if(dist < 30) {
1060
- enemy.health -= bullet.damage * (hasAPCR ? 2 : 1);
1061
- if(enemy.health <= 0) {
1062
- spawnHealthItem(enemy.x, enemy.y);
1063
- gold += 100;
1064
- effects.push(new Effect(enemy.x, enemy.y, 1000, 'death'));
1065
- deathSound.cloneNode().play();
1066
- return false;
1067
- }
1068
- return true;
1069
- }
1070
- return true;
1071
- });
1072
- } else {
1073
- const dist = Math.hypot(bullet.x - player.x, bullet.y - player.y);
1074
- if(dist < 30) {
1075
- player.health -= bullet.damage;
1076
- if(player.health <= 0) {
1077
- gameOver = true;
1078
- restartBtn.style.display = 'block';
1079
- effects.push(new Effect(player.x, player.y, 1000, 'death'));
1080
- deathSound.cloneNode().play();
1081
- }
1082
- return false;
1083
- }
1084
- }
1085
-
1086
- return bullet.x >= 0 && bullet.x <= canvas.width &&
1087
- bullet.y >= 0 && bullet.y <= canvas.height;
1088
- });
1089
-
1090
- items = items.filter(item => {
1091
- const dist = Math.hypot(item.x - player.x, item.y - player.y);
1092
- if(dist < 30) {
1093
- player.health = Math.min(player.health + 200, player.maxHealth);
1094
- return false;
1095
- }
1096
- return true;
1097
- });
1098
-
1099
- if(enemies.length === 0) {
1100
- if (!isBossStage) {
1101
- if(currentRound < 10) {
1102
- nextRoundBtn.style.display = 'block';
1103
- showShop();
1104
- } else {
1105
- bossButton.style.display = 'block';
1106
- }
1107
- } else {
1108
- gameOver = true;
1109
- document.getElementById('winMessage').style.display = 'block';
1110
- restartBtn.style.display = 'block';
1111
- bgm.pause();
1112
- const victorySound = new Audio('victory.ogg');
1113
- victorySound.play();
1114
- }
1115
- }
1116
- }
1117
- }
1118
-
1119
- </script>
1120
  </body>
1121
- </html>
 
33
  z-index: 1000;
34
  font-size: 18px;
35
  }
36
+ .button {
37
  position: fixed;
38
+ top: 50%;
39
  left: 50%;
40
  transform: translate(-50%, -50%);
41
  padding: 20px 40px;
 
46
  border-radius: 5px;
47
  cursor: pointer;
48
  display: none;
49
+ z-index: 1000;
50
  }
51
  #countdown {
52
  position: fixed;
 
59
  z-index: 1000;
60
  display: none;
61
  }
62
+ #winMessage {
63
+ font-size: 72px;
64
+ background: none;
65
+ top: 30%; /* 화면의 위쪽으로 이동 */
66
+ left: 50%;
67
+ transform: translate(-50%, -50%);
68
+ z-index: 1001; /* 다음 라운드 버튼보다 위로 설정 */
69
+ }
70
+
71
+ #nextRound {
72
+ top: 80%; /* 화면의 아래쪽으로 이동 */
73
+ z-index: 1000; /* "You Win"보다 아래로 설정 */
74
+ }
75
+ #nextRound {
76
+ top: 80%; /* 아래쪽으로 이동 */
77
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  </style>
79
  </head>
80
  <body>
 
91
  <button id="nextRound" class="button">Next Round</button>
92
  <button id="restart" class="button">Restart Game</button>
93
  <canvas id="gameCanvas"></canvas>
94
+
95
+ <div id="shop" style="display:none; position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.9); padding:20px; border-radius:10px; color:white; z-index:1000;">
96
+ <h2>Tank Shop</h2>
97
+ <div style="display:flex; gap:20px;">
98
+ <div id="tank1" style="text-align:center;">
99
+ <h3>PZ.IV</h3>
100
+ <img src="player2.png" width="90" height="50">
101
+ <p>300 Gold</p>
102
+ <p style="color: #4CAF50;">+50% HP</p>
103
+ <button onclick="buyTank('player2.png', 300, 'tank1')">Buy</button>
104
+ </div>
105
+ <div id="tank2" style="text-align:center;">
106
+ <h3>TIGER</h3>
107
+ <img src="player3.png" width="110" height="55">
108
+ <p>500 Gold</p>
109
+ <p style="color: #4CAF50;">+100% HP</p>
110
+ <p style="color: #ff6b6b;">-30% Speed</p>
111
+ <button onclick="buyTank('player3.png', 500, 'tank2')">Buy</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </div>
113
+ <div id="bf109" style="text-align:center;">
114
+ <h3>BF-109</h3>
115
+ <img src="bf109.png" width="100" height="100">
116
+ <p>1000 Gold</p>
117
+ <p style="color: #4CAF50;">Air support from BF-109</p>
118
+ <button onclick="buyBF109()">Buy</button>
119
+ </div>
120
+ <div id="ju87" style="text-align:center;">
121
+ <h3>JU-87</h3>
122
+ <img src="ju87.png" width="100" height="100">
123
+ <p>1500 Gold</p>
124
+ <p style="color: #4CAF50;">Get ju-87 air support</p>
125
+ <button onclick="buyJU87()">Buy</button>
126
+ </div>
127
+ <div id="apcr" style="text-align:center;">
128
+ <h3>APCR</h3>
129
+ <img src="apcr.png" width="80" height="20"> <!-- 여기를 80x20으로 수정 -->
130
+ <p>1000 Gold</p>
131
+ <p style="color: #4CAF50;">+100% Bullet Speed</p>
132
+ <button onclick="buyAPCR()">Buy</button>
133
+ </div>
134
  </div>
135
+ </div>
136
  <button id="bossButton" class="button">Fight Boss!</button>
137
  <div id="winMessage" class="button" style="font-size: 72px; background: none;">You Win!</div>
138
+
139
+
140
+ <script>
141
+ const canvas = document.getElementById('gameCanvas');
142
+ const ctx = canvas.getContext('2d');
143
+ const nextRoundBtn = document.getElementById('nextRound');
144
+ const restartBtn = document.getElementById('restart');
145
+ const weaponInfo = document.getElementById('weaponInfo');
146
+ const countdownEl = document.getElementById('countdown');
147
+ const bossButton = document.getElementById('bossButton');
148
+ canvas.width = window.innerWidth;
149
+ canvas.height = window.innerHeight;
150
+ // Game state
151
+ let currentRound = 1;
152
+ let gameOver = false;
153
+ let currentWeapon = 'cannon';
154
+ let enemies = [];
155
+ let bullets = [];
156
+ let items = [];
157
+ let lastShot = 0;
158
+ let isCountingDown = true;
159
+ let countdownTime = 3;
160
+ let autoFire = false;
161
+ let gold = 0;
162
+ let isBossStage = false;
163
+ let effects = [];
164
+ let hasAPCR = false; // APCR 구매 여부
165
+ let hasBF109 = false; // BF-109 구매 여부
166
+ let hasJU87 = false; // JU-87 구매 여부
167
+ let lastJU87Spawn = 0; // 마지막 JU-87 생성 시간
168
+ let supportUnits = []; // 지원 유닛 배열
169
+ let lastSupportSpawn = 0; // 마지막 지원 유닛 생성 시간
170
+ // Load assets
171
+ const backgroundImg = new Image();
172
+ backgroundImg.src = 'city.png';
173
+ const playerImg = new Image();
174
+ playerImg.src = 'player.png';
175
+ const enemyImg = new Image();
176
+ enemyImg.src = 'enemy.png';
177
+ const bulletImg = new Image(); // APCR 총알 이미지
178
+ bulletImg.src = 'apcr2.png';
179
+ // Audio setup
180
+ const cannonSound = new Audio('firemn.ogg');
181
+ const machinegunSound = new Audio('firemg.ogg');
182
+ const enemyFireSound = new Audio('fireenemy.ogg');
183
+ const bgm = new Audio('BGM2.ogg');
184
+ const countSound = new Audio('count.ogg');
185
+ const deathSound = new Audio('death.ogg');
186
+ bgm.loop = true;
187
+ enemyFireSound.volume = 0.5;
188
+ const weapons = {
189
+ cannon: {
190
+ fireRate: 1000,
191
+ damage: 0.25,
192
+ bulletSize: 5,
193
+ sound: cannonSound
194
+ },
195
+ machinegun: {
196
+ fireRate: 200,
197
+ damage: 0.05,
198
+ bulletSize: 2,
199
+ sound: machinegunSound
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
201
+ };
202
+ // Player setup
203
+ const player = {
204
+ x: canvas.width/2,
205
+ y: canvas.height/2,
206
+ speed: 5,
207
+ angle: 0,
208
+ width: 100,
209
+ height: 45,
210
+ health: 1000,
211
+ maxHealth: 1000
212
+ };
213
+ function startCountdown() {
214
+ isCountingDown = true;
215
+ countdownTime = 3;
216
+ countdownEl.style.display = 'block';
217
+ countdownEl.textContent = countdownTime;
218
+ bgm.pause();
219
+ countSound.play();
220
+ const countInterval = setInterval(() => {
221
+ countdownTime--;
222
+ if(countdownTime <= 0) {
223
+ clearInterval(countInterval);
224
+ countdownEl.style.display = 'none';
225
+ isCountingDown = false;
226
+ bgm.play();
227
+ }
228
+ countdownEl.textContent = countdownTime > 0 ? countdownTime : 'GO!';
229
+ }, 1000);
230
+ }
231
+
232
+ class Effect {
233
  constructor(x, y, duration, type, angle = 0, parent = null) {
234
  this.x = x;
235
  this.y = y;
 
237
  this.duration = duration;
238
  this.type = type;
239
  this.angle = angle;
240
+ this.parent = parent; // 부모 유닛 (발사한 유닛)
241
+ this.offset = { x: Math.cos(angle) * 30, y: Math.sin(angle) * 30 }; // 부모로부터의 오프셋
242
  this.img = new Image();
243
  this.img.src = type === 'death' ? 'bang.png' : 'fire2.png';
244
  }
 
255
  return Date.now() - this.startTime > this.duration;
256
  }
257
  }
258
+ class SupportUnit {
 
259
  constructor(yPosition) {
260
  this.x = 0;
261
  this.y = yPosition;
 
266
  this.angle = 0;
267
  this.img = new Image();
268
  this.img.src = 'bf109.png';
269
+ this.hasPlayedSound = false; // 첫 등장 소리용
270
+ this.mgSound = null; // 기관총 소리 객체
271
  }
272
 
273
  update() {
274
+ // 이동
275
  this.x += this.speed;
276
 
277
+ // 카운트다운 중이면 소리 정지 및 초기화
278
  if (isCountingDown) {
279
  if (this.mgSound) {
280
  this.mgSound.pause();
 
283
  this.hasPlayedSound = false;
284
  }
285
 
286
+ // 발사 (1초에 5발)
287
  const now = Date.now();
288
  if (now - this.lastShot > 200 && !isCountingDown) {
289
  this.shoot();
 
293
  }
294
 
295
  shoot() {
296
+ // 최초 등장시에만 bf109mg.ogg 재생
297
  if (!this.hasPlayedSound) {
298
  const firstSound = new Audio('bf109mg.ogg');
299
  firstSound.volume = 1.0;
 
301
  this.hasPlayedSound = true;
302
  }
303
 
304
+ // 발사할 때마다 새로운 bf109mgse.ogg 재생
305
  if (!isCountingDown) {
306
  const shootSound = new Audio('bf109mgse.ogg');
307
+ shootSound.volume = 0.5; // 볼륨 낮춤
308
  shootSound.play();
309
  }
310
 
 
319
  });
320
  }
321
  }
322
+ class JU87 {
 
323
  constructor() {
324
  this.x = canvas.width;
325
  this.y = 50;
 
336
  this.hasPlayedMGSound = false;
337
  this.isReturning = false;
338
  this.circleAngle = 0;
339
+ this.returningToCenter = false; // 중앙으로 돌아가는 상태 추가
340
  }
341
 
342
  selectTarget() {
 
344
  enemies[Math.floor(Math.random() * enemies.length)] : null;
345
  }
346
 
347
+ shoot() {
348
+ // 카운트다운 중이 아닐 때만 소리 재생
349
+ if (!this.hasPlayedMGSound && !isCountingDown) {
350
+ const mgSound = new Audio('ju87mg.ogg');
351
+ mgSound.volume = 1.0;
352
+ mgSound.currentTime = 0;
353
+ mgSound.play().catch(error => console.error('Audio play failed:', error));
354
+ this.hasPlayedMGSound = true;
355
+ }
356
 
357
+ // 100x100 픽셀 기준으로 날개 위치 좌표 설정
358
+ [[20, 50], [80, 50]].forEach(([x, y]) => {
359
+ const offsetX = x - 50;
360
+ const offsetY = y - 50;
361
+
362
+ const rotatedX = this.x + (Math.cos(this.angle) * offsetX - Math.sin(this.angle) * offsetY);
363
+ const rotatedY = this.y + (Math.sin(this.angle) * offsetX + Math.cos(this.angle) * offsetY);
364
 
365
+ bullets.push({
366
+ x: rotatedX,
367
+ y: rotatedY,
368
+ angle: this.angle,
369
+ speed: 10,
370
+ isEnemy: false,
371
+ damage: weapons.machinegun.damage * 2,
372
+ size: weapons.machinegun.bulletSize
 
373
  });
374
+ });
375
+ }
376
  update() {
377
  if (!this.hasPlayedSound) {
378
  const sirenSound = new Audio('ju87siren.ogg');
 
436
  return true;
437
  }
438
  }
439
+
440
+ class Enemy {
441
+ constructor(isBoss = false) {
442
+ this.x = Math.random() * canvas.width;
443
+ this.y = Math.random() * canvas.height;
444
+ this.health = isBoss ? 15000 : 1000;
445
+ this.maxHealth = this.health;
446
+ this.speed = isBoss ? 1 : 2;
447
+ this.lastShot = 0;
448
+ this.shootInterval = isBoss ? 1000 : 1000;
449
+ this.angle = 0;
450
+ this.width = 100;
451
+ this.height = 45;
452
+ this.moveTimer = 0;
453
+ this.moveInterval = Math.random() * 2000 + 1000;
454
+ this.moveAngle = Math.random() * Math.PI * 2;
455
+ this.isBoss = isBoss;
456
+
457
+ if (isBoss) {
458
+ this.enemyImg = new Image();
459
+ this.enemyImg.src = 'boss.png';
460
+ } else if (currentRound >= 7) {
461
+ this.enemyImg = new Image();
462
+ this.enemyImg.src = 'enemy3.png';
463
+ } else if (currentRound >= 4) {
464
+ this.enemyImg = new Image();
465
+ this.enemyImg.src = 'enemy2.png';
466
+ }
467
+ }
468
+ update() {
469
+ if(isCountingDown) return;
470
+ const now = Date.now();
471
+
472
+ if (now - this.moveTimer > this.moveInterval) {
473
+ this.moveAngle = Math.random() * Math.PI * 2;
474
+ this.moveTimer = now;
475
+ }
476
+ this.x += Math.cos(this.moveAngle) * this.speed;
477
+ this.y += Math.sin(this.moveAngle) * this.speed;
478
+ this.x = Math.max(this.width/2, Math.min(canvas.width - this.width/2, this.x));
479
+ this.y = Math.max(this.height/2, Math.min(canvas.height - this.height/2, this.y));
480
+ this.angle = Math.atan2(player.y - this.y, player.x - this.x);
481
+
482
+ if (now - this.lastShot > this.shootInterval && !isCountingDown) {
483
+ this.shoot();
484
+ this.lastShot = now;
485
+ }
486
+ }
487
+ shoot() {
488
+ const sound = this.isBoss ? new Audio('firemn.ogg') : enemyFireSound.cloneNode();
489
+ sound.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
 
491
+ // 발사 이펙트 추가
492
+ effects.push(new Effect(
493
+ this.x + Math.cos(this.angle) * 30,
494
+ this.y + Math.sin(this.angle) * 30,
495
+ 500,
496
+ 'fire',
497
+ this.angle,
498
+ this // 자신을 부모로 전달
499
+ ));
500
+
501
+ bullets.push({
502
+ x: this.x + Math.cos(this.angle) * 30,
503
+ y: this.y + Math.sin(this.angle) * 30,
504
+ angle: this.angle,
505
+ speed: this.isBoss ? 10 : 5,
506
+ isEnemy: true,
507
+ size: this.isBoss ? 5 : 3,
508
+ damage: this.isBoss ? 300 : 150
509
+ });
510
  }
511
+ }
512
+ function showShop() {
513
+ document.getElementById('shop').style.display = 'block';
514
+ }
515
+ // 플레이어의 기본 상태를 저장
516
  const defaultPlayerStats = {
517
  maxHealth: 1000,
518
  speed: 5,
519
  width: 100,
520
  height: 45
521
  };
 
522
  function buyTank(tankImg, cost, tankId) {
523
  if (gold >= cost) {
524
  gold -= cost;
 
526
  document.getElementById(tankId).style.display = 'none';
527
  document.getElementById('shop').style.display = 'none';
528
 
529
+ if (tankId === 'tank1') { // PZ.IV
530
  player.maxHealth = 1500;
531
+ player.speed = defaultPlayerStats.speed; // 기본 이동속도로 복구
532
+ player.width = 90; // PZ.IV의 크기 설정
533
+ player.height = 50; // PZ.IV의 크기 설정
534
  }
535
+ else if (tankId === 'tank2') { // TIGER
536
  player.maxHealth = 2000;
537
  player.speed = defaultPlayerStats.speed * 0.7;
538
+ player.width = 100; // TIGER는 기본 크기 유지
539
+ player.height = 45; // TIGER는 기본 크기 유지
540
  }
541
 
542
  player.health = player.maxHealth;
543
  }
544
  }
545
+ function buyAPCR() {
 
546
  if (gold >= 1000 && !hasAPCR) {
547
  gold -= 1000;
548
  hasAPCR = true;
 
550
  document.getElementById('shop').style.display = 'none';
551
  }
552
  }
553
+ function buyBF109() {
 
554
  if (gold >= 1000 && !hasBF109) {
555
  gold -= 1000;
556
  hasBF109 = true;
 
558
  document.getElementById('shop').style.display = 'none';
559
  }
560
  }
561
+ function buyJU87() {
 
562
  if (gold >= 1500 && !hasJU87) {
563
  gold -= 1500;
564
  hasJU87 = true;
565
  document.getElementById('ju87').style.display = 'none';
566
  document.getElementById('shop').style.display = 'none';
567
+ lastJU87Spawn = Date.now(); // 구매 즉시 스폰 타이머 초기화
568
  }
569
  }
570
+ function initRound() {
 
571
  enemies = [];
572
  for(let i = 0; i < 1 * currentRound; i++) {
573
  enemies.push(new Enemy());
 
577
  items = [];
578
  supportUnits = [];
579
  lastSupportSpawn = 0;
580
+
581
+ // 카운트다운 시작
 
 
 
582
  startCountdown();
583
 
584
+ // 카운트다운이 끝나면 JU87 스폰
585
  setTimeout(() => {
586
  if (hasJU87) {
587
  supportUnits.push(new JU87());
588
  lastJU87Spawn = Date.now();
589
  }
590
+ }, 3000); // 3초 후에 스폰
591
  }
592
+ function startBossStage() {
593
+ isBossStage = true;
594
+ enemies = [];
595
+ enemies.push(new Enemy(true));
596
+ player.health = player.maxHealth;
597
+ bullets = [];
598
+ items = [];
599
+ document.getElementById('bossButton').style.display = 'none';
600
+ bgm.src = 'BGM.ogg'; // 보스전 BGM으로 변경
601
+ startCountdown();
602
+ }
603
+ canvas.addEventListener('mousemove', (e) => {
604
+ player.angle = Math.atan2(e.clientY - player.y, e.clientX - player.x);
605
+ });
606
+ const keys = {};
607
+ document.addEventListener('keydown', e => {
608
+ keys[e.key] = true;
609
+ if(e.key.toLowerCase() === 'c') {
610
+ currentWeapon = currentWeapon === 'cannon' ? 'machinegun' : 'cannon';
611
+ weaponInfo.textContent = `Current Weapon: ${currentWeapon.charAt(0).toUpperCase() + currentWeapon.slice(1)}`;
612
+ } else if(e.key.toLowerCase() === 'r') {
613
+ autoFire = !autoFire;
614
+ }
615
+ });
616
+
617
+ document.addEventListener('keyup', e => keys[e.key] = false);
618
+ function fireBullet() {
619
+ if(isCountingDown) return;
620
+ const weapon = weapons[currentWeapon];
621
+ const now = Date.now();
622
+ if ((keys[' '] || autoFire) && now - lastShot > weapon.fireRate) {
623
+ weapon.sound.cloneNode().play();
624
+ effects.push(new Effect(
625
+ player.x + Math.cos(player.angle) * 30,
626
+ player.y + Math.sin(player.angle) * 30,
627
+ 500,
628
+ 'fire',
629
+ player.angle,
630
+ player
631
+ ));
632
+
633
+ bullets.push({
634
+ x: player.x + Math.cos(player.angle) * 30,
635
+ y: player.y + Math.sin(player.angle) * 30,
636
+ angle: player.angle,
637
+ speed: hasAPCR ? 20 : 10, // APCR 적용 시 100% 증가
638
+ isEnemy: false,
639
+ damage: weapon.damage,
640
+ size: weapon.bulletSize,
641
+ isAPCR: hasAPCR
642
+ });
643
+ lastShot = now;
644
+ }
645
+ }
646
+ function updateGame() {
647
  if(gameOver) return;
648
  if(!isCountingDown) {
649
+ // 플레이어 움직임
650
+ if(keys['w']) player.y -= player.speed;
651
+ if(keys['s']) player.y += player.speed;
652
+ if(keys['a']) player.x -= player.speed;
653
+ if(keys['d']) player.x += player.speed;
654
+ player.x = Math.max(player.width/2, Math.min(canvas.width - player.width/2, player.x));
655
+ player.y = Math.max(player.height/2, Math.min(canvas.height - player.height/2, player.y));
656
+ fireBullet();
657
  }
658
+
659
+ // BF109 관련 코드
660
  if (hasBF109 && !isCountingDown) {
661
  const now = Date.now();
662
+ if (now - lastSupportSpawn > 10000) { // 10초마다
663
  supportUnits.push(
664
  new SupportUnit(canvas.height * 0.2),
665
  new SupportUnit(canvas.height * 0.5),
 
668
  lastSupportSpawn = now;
669
  }
670
  }
671
+
672
+ // JU87 관련 코드 추가
673
  if (hasJU87 && !isCountingDown) {
674
  const now = Date.now();
675
+ if (now - lastJU87Spawn > 15000) { // 15초마다
676
  supportUnits.push(new JU87());
677
  lastJU87Spawn = now;
678
  }
679
  }
680
+
681
+ // 모든 지원 유닛 업데이트 (BF109와 JU87 모두 처리)
682
  supportUnits = supportUnits.filter(unit => unit.update());
683
+
684
  enemies.forEach(enemy => enemy.update());
685
+ if(!isCountingDown) {
686
+ bullets = bullets.filter(bullet => {
687
+ bullet.x += Math.cos(bullet.angle) * bullet.speed;
688
+ bullet.y += Math.sin(bullet.angle) * bullet.speed;
689
+ if(!bullet.isEnemy) {
690
+ enemies = enemies.filter(enemy => {
691
+ const dist = Math.hypot(bullet.x - enemy.x, bullet.y - enemy.y);
692
+ if(dist < 30) {
693
+ let damage = currentWeapon === 'cannon' ? 250 : 50; // 고정 데미지로 변경
694
+ enemy.health -= damage;
695
+ if(enemy.health <= 0) {
696
+ spawnHealthItem(enemy.x, enemy.y);
697
+ gold += 100;
698
+ // 죽음 이펙트와 사운드 추가
699
+ effects.push(new Effect(enemy.x, enemy.y, 1000, 'death'));
700
+ deathSound.cloneNode().play();
701
+ return false;
702
+ }
703
+ if(player.health <= 0) {
704
+ gameOver = true;
705
+ restartBtn.style.display = 'block';
706
+ effects.push(new Effect(player.x, player.y, 1000, 'death'));
707
+ deathSound.cloneNode().play();
708
+ }
709
+ return true;
710
+ }
711
+ return true;
712
+ });
713
+ } else {
714
+ const dist = Math.hypot(bullet.x - player.x, bullet.y - player.y);
715
+ if(dist < 30) {
716
+ player.health -= bullet.damage || 100;
717
+ if(player.health <= 0) {
718
+ gameOver = true;
719
+ restartBtn.style.display = 'block';
720
+ }
721
  return false;
722
  }
 
723
  }
724
+ return bullet.x >= 0 && bullet.x <= canvas.width &&
725
+ bullet.y >= 0 && bullet.y <= canvas.height;
726
  });
727
+ items = items.filter(item => {
728
+ const dist = Math.hypot(item.x - player.x, item.y - player.y);
729
+ if(dist < 30) {
730
+ player.health = Math.min(player.health + 200, player.maxHealth);
731
+ return false;
 
 
 
 
732
  }
733
+ return true;
734
+ });
735
+ if(enemies.length === 0) {
736
+ if (!isBossStage) {
737
+ if(currentRound < 10) {
738
+ nextRoundBtn.style.display = 'block';
739
+ showShop();
740
+ } else {
741
+ document.getElementById('bossButton').style.display = 'block';
742
  }
743
+ } else {
744
+ gameOver = true;
745
+ document.getElementById('winMessage').style.display = 'block';
746
+ restartBtn.style.display = 'block';
747
+ bgm.pause(); // 현재 BGM 정지
748
+ const victorySound = new Audio('victory.ogg'); // 승리 사운드 생성
749
+ victorySound.play(); // 승리 사운드 재생
750
  }
751
+ }
752
+ }
753
+ enemies.forEach(enemy => enemy.update());
754
+ }
755
+ function spawnHealthItem(x, y) {
756
+ items.push({x, y});
757
+ }
758
+ function drawHealthBar(x, y, health, maxHealth, width, height, color) {
759
+ ctx.fillStyle = '#333';
760
+ ctx.fillRect(x - width/2, y - height/2, width, height);
761
+ ctx.fillStyle = color;
762
+ ctx.fillRect(x - width/2, y - height/2, width * (health/maxHealth), height);
763
+ }
764
+ function drawGame() {
765
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
766
+ const pattern = ctx.createPattern(backgroundImg, 'repeat');
767
+ ctx.fillStyle = pattern;
768
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
769
 
770
+ // 플레이어 그리기
771
+ ctx.save();
772
+ ctx.translate(player.x, player.y);
773
+ ctx.rotate(player.angle);
774
+ ctx.drawImage(playerImg, -player.width/2, -player.height/2, player.width, player.height);
775
+ ctx.restore();
776
+
777
+ // 체력바
778
+ drawHealthBar(canvas.width/2, 30, player.health, player.maxHealth, 200, 20, 'green');
779
 
780
+ // 그리기
781
+ enemies.forEach(enemy => {
782
+ ctx.save();
783
+ ctx.translate(enemy.x, enemy.y);
784
+ ctx.rotate(enemy.angle);
785
+ const img = enemy.isBoss ? enemy.enemyImg : (enemy.enemyImg || enemyImg);
786
+ ctx.drawImage(img, -enemy.width/2, -enemy.height/2, enemy.width, enemy.height);
787
+ ctx.restore();
788
+ drawHealthBar(enemy.x, enemy.y - 40, enemy.health, enemy.maxHealth, 60, 5, 'red');
789
+ });
790
+ supportUnits.forEach(unit => {
791
+ ctx.save();
792
+ ctx.translate(unit.x, unit.y);
793
+ ctx.rotate(unit.angle);
794
+ ctx.drawImage(unit.img, -unit.width/2, -unit.height/2, unit.width, unit.height);
795
+ ctx.restore();
796
+ });
797
+ // 총알 그리기
798
+ bullets.forEach(bullet => {
799
+ if (bullet.isEnemy || !bullet.isAPCR) {
800
+ ctx.beginPath();
801
+ ctx.fillStyle = bullet.isEnemy ? 'red' : 'blue';
802
+ ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
803
+ ctx.fill();
804
  } else {
805
+ ctx.save();
806
+ ctx.translate(bullet.x, bullet.y);
807
+ ctx.rotate(bullet.angle);
808
+ // 기관총일 때 크기 50% 감소
809
+ const width = currentWeapon === 'machinegun' ? 10 : 20; // 기관총일 때 10, 캐논일 때 20
810
+ const height = currentWeapon === 'machinegun' ? 5 : 10; // 기관총일 때 5, 캐논일 때 10
811
+ ctx.drawImage(bulletImg, -width/2, -height/2, width, height);
812
+ ctx.restore();
813
  }
814
+ });
815
+ // 아이템 그리기
816
+ items.forEach(item => {
817
+ ctx.beginPath();
818
+ ctx.fillStyle = 'green';
819
+ ctx.arc(item.x, item.y, 10, 0, Math.PI * 2);
820
+ ctx.fill();
821
+ });
822
+
823
+ // UI 그리기
824
+ ctx.fillStyle = 'white';
825
+ ctx.font = '24px Arial';
826
+ ctx.fillText(`Round ${currentRound}/10`, 10, 30);
827
+ ctx.fillText(`Gold: ${gold}`, 10, 60);
828
+
829
+ // 이펙트 그리기
830
+ effects = effects.filter(effect => !effect.isExpired());
831
+ effects.forEach(effect => {
832
+ effect.update(); // 이펙트 위치 업데이트
833
+ ctx.save();
834
+ ctx.translate(effect.x, effect.y);
835
+ if(effect.type === 'fire') ctx.rotate(effect.angle);
836
+ // bang.png는 1.5배 크게
837
+ const size = effect.type === 'death' ? 75 : 42; // death는 75px (1.5배), fire는 42px
838
+ ctx.drawImage(effect.img, -size/2, -size/2, size, size);
839
+ ctx.restore();
840
+ });
841
+
842
+ if(isCountingDown) {
843
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
844
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
845
  }
 
 
 
 
 
 
 
 
 
 
846
  }
847
+
848
+ // drawGame 함수 밖으로 이동
849
  function gameLoop() {
850
+ updateGame();
851
+ drawGame();
852
+ requestAnimationFrame(gameLoop);
 
853
  }
 
 
 
 
 
 
 
 
 
 
 
 
854
 
855
+ nextRoundBtn.addEventListener('click', () => {
856
+ currentRound++;
857
+ nextRoundBtn.style.display = 'none';
858
+ document.getElementById('shop').style.display = 'none';
859
+ initRound();
860
+ });
 
 
861
 
862
+ bossButton.addEventListener('click', startBossStage);
 
 
 
863
 
864
+ restartBtn.addEventListener('click', () => {
865
+ currentRound = 1;
866
+ gameOver = false;
867
+ isBossStage = false;
868
+ gold = 0;
869
+ hasAPCR = false; // APCR 초기화
870
+ hasBF109 = false; // BF109 초기화
871
+ hasJU87 = false;
872
+ supportUnits = []; // 지원 유닛 배열 초기화
873
+
874
+ restartBtn.style.display = 'none';
875
+ document.getElementById('winMessage').style.display = 'none';
876
+ document.getElementById('tank1').style.display = 'block';
877
+ document.getElementById('tank2').style.display = 'block';
878
+ document.getElementById('apcr').style.display = 'block';
879
+ document.getElementById('bf109').style.display = 'block'; // BF109 상점 아이템 다시 표시
880
+
881
+ playerImg.src = 'player.png';
882
  bgm.src = 'BGM2.ogg';
883
+ bgm.play();
 
884
  initRound();
 
885
  });
886
 
887
  Promise.all([
 
889
  new Promise(resolve => playerImg.onload = resolve),
890
  new Promise(resolve => enemyImg.onload = resolve)
891
  ]).then(() => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
892
  initRound();
893
  gameLoop();
894
+ bgm.play();
895
  });
896
 
 
 
 
 
 
 
 
 
897
  window.addEventListener('resize', () => {
898
  canvas.width = window.innerWidth;
899
  canvas.height = window.innerHeight;
 
900
  });
901
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
902
  </body>
903
+ </html>