-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.js
1618 lines (1279 loc) · 83 KB
/
script.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// =======================[ Należy zmienić w zależoności od wydajności sprzętu ]======================= //
let ballSpeed = 4.5 // Prędkość piłki (domyślnie 4.5) - im większa wartość tym szybciej piłka porusza się po ekranie
// ==================================================================================================== //
// ========================================[ Ustawia pole gry ]======================================== //
const canvas = document.getElementById('canvas'); // Pole gry - obiekty dynamiczne
const staticCanvas = document.getElementById('staticCanvas'); // Pole gry - obiekty statyczne
const context = canvas.getContext('2d'); // Ustawia kontekst canvasa na 2d
const contextStatic = staticCanvas.getContext('2d'); // Ustawia kontekst canvasa statycznego na 2d
// ==================================================================================================== //
// ========================================= [ Ustawia guziki ] ======================================= //
const playButton = document.getElementById('playButton') // Przycisk startu gry
const introScreen = document.getElementById('introScreen') // Ekran startowy
// ==================================================================================================== //
// ===============================[ Ukrywa ekran startowy ]=========================================== //
playButton.addEventListener('click', () => { // Po kliknięciu w przycisk startu gry
playSound("clicked") // Odtwarza dźwięk kliknięcia
introScreen.style.display = "none"; // Ukrywa ekran startowy
gameStarted = true // Ustawia grę jako rozpoczętą
gameOvered = false // Ustawia grę jako nie zakończoną
gamePaused = false // Ustawia grę jako nie zatrzymaną
document.addEventListener("keydown", pauseTheGame) // Dodaje event listenera pozwalający na pauzy gry
})
// ==================================================================================================== //
// ================================[ Otwiera ekran pomocy ]============================================ //
const helpScreen = document.getElementById('helpScreen') // Ekran pomocy
helpScreen.addEventListener("click", function () { // Po kliknięciu w ekran pomocy
playSound("clicked") // Odtwarza dźwięk kliknięcia
helpScreen.style.display = "none" // Ukrywa ekran pomocy
})
function openHelp() { // Funkcja otwierająca ekran pomocy
helpScreen.style.display = "block"
}
document.querySelector("#helpButton").addEventListener("click", () => { // Po kliknięciu w przycisk pomoc
playSound("clicked") // Odtwarza dźwięk kliknięcia
openHelp() // Otwiera ekran pomocy
})
// ==================================================================================================== //
// ================================[ Otwiera ekran edytora ]============================================ //
const loadButton = document.getElementById('loadButton') // Przycisk do "WCZYTAJ" w menu głównym
loadButton.addEventListener("click", function () { // Po kliknięciu w przycisk "WCZYTAJ"
playSound("clicked") // Odtwarza dźwięk kliknięcia
setTimeout(() => {
window.open("generator/index.html", "_self") // Otwiera ekran edytora po 150ms - częściowo zapobiega błędom wczytywania
}, 150);
})
// ==================================================================================================== //
// ============================[ Ustawianie rozdzielczości okienka canvas ]============================ //
canvas.width = 1000 // Ustawia szerokość canvasa na 1000px
canvas.height = 1000 // Ustawia wysokość canvasa na 1000px
staticCanvas.width = canvas.width; // Ustawia szerokość canvasa statycznego na 1000px
staticCanvas.height = canvas.height; // Ustawia wysokość canvasa statycznego na 1000px
// ==================================================================================================== //
// ========================================[ Odtwarza dźwięk ]======================================== //
function playSound(sound, type = 0) {
switch (sound) {
case 'hitEdge': // Dźwięk uderzenia w krawędź
new Audio('audio/balHitEdge.ogg').play()
break;
case "brickHit": // Dźwięk uderzenia w cegłę
if (type != 8 && type != 9)
new Audio('audio/brickHit.ogg').play() // Domyślny dźwięk uderzenia w cegłę
else if (type == 9)
new Audio('audio/goldHitSound.ogg').play() // Dźwięk srebrnej cegły
else if (type == 8)
new Audio('audio/silverHitSound.ogg').play() // Dźwięk złotej cegły
break;
case "hitPlatform": // Dźwięk uderzenia w platformę
new Audio('audio/platformHit.ogg').play()
break;
case "hitSmallDOH": // Dźwięk uderzenia w małego DOH'a
new Audio('audio/smallDohHit.ogg').play()
break;
case "hitDOH": // Dźwięk uderzenia w DOH'a
new Audio('audio/dohHit.ogg').play()
break;
case "gameOver": // Dźwięk przegranej gry
new Audio('audio/gameOver.ogg').play()
break;
case "fallOfScreen": // Dźwięk spadnięcia piłki poza ekran
new Audio('audio/fallOfScreen.ogg').play()
break;
case "hitedByEnemy": // Dźwięk uderzenia pocisku przeciwnika w platformę
new Audio('audio/hitedByEnemy.ogg').play()
break;
case "clicked": // Dźwięk kliknięcia
new Audio('audio/click.ogg').play()
break;
case "victory": // Dźwięk wygranej gry
new Audio('audio/soundOfVictory.mp3').play()
}
}
// ==================================================================================================== //
// =======================================[ Dotyczy pauzy gry ]======================================== //
let gameStarted = false // Przechowuje stan czy gra jest uruchomiona
let gamePaused = false // Czy gra się zatrzymana
let gameOvered = false // Czy gra jest zakończona
function pauseTheGame(e) { // Funkcja pauzy gry
if (e.key == 'Escape' && gameStarted) { // Jeśli naciśnięto klawisz "Escape" i gra jest uruchomiona
gamePaused = !gamePaused // Zmienia stan pauzy gry na jej przeciwny
} // Jeśli gra jest zatrzymana, to ją wznawia, a jeśli jest wznowiona, to ją zatrzymuje
}
// ==================================================================================================== //
// =============================[ Uruchamiane po stracie wszystkich żyć ]============================== //
function gameOver() { // Funkcja kończąca grę
context.clearRect(0, 0, canvas.width, canvas.height) // Czyści canvas
document.querySelector("#staticCanvas").style.backgroundImage = "url(./img/background.jpg)" // Ustawia tło na domyślne
document.querySelector("#canvas").style.cursor = "none" // Ukrywa kursor
document.removeEventListener("click", gameOver) // Usuwa event listenera z documentu
restartTheGame() // Uruchamia funkcję restartującą grę
}
// ==================================================================================================== //
// ========================================[ Dotyczące gracza ]======================================== //
let playerLevel = 1 // Poziom gracza
let playerHealth = 3 // Życie gracza
let playerPoints = 0 // Punkty gracza
// ==================================================================================================== //
// =============[ Ustawia domyśle wartości pozycji i piłki po uruchominiu nowego poziomu ]============= //
function resetToDefault() {
// -------------------------- [ Usuwa upgrade'y ] -------------------------- //
prevUpgrade = curUpgrade; //Zapisuje obecny upgrade jako poprzedni
curUpgrade = null; //Usuwa obecny upgrade
removeAllUpgrades(); //Usuwa wszystkie upgrade'y
Upgrade.list = []; //Usuwa wszystkie upgrade'y z listy
// ----------------------------------------------------------------------- //
// ---------------------[ Usuwa lasery i fireballe ]--------------------- //
Projectile.list = []; //Usuwa wszystkie pociski z listy
DOH.list.forEach((el) => {
el.fireBalls = [null, null]; //Usuwa fireballe DOH'a
})
// --------------------------------------------------------------------- //
// ----------------------------[ Platforma ]----------------------------- //
platform.holdBall = originalBall; //Przywraca trzymanie piłki przez platformę
platform.pos = new Vector2D(canvas.width / 2 - platform.size.x / 2, canvas.height - platform.size.y * 2.5) //Przywraca pozycję platformy na środek ekranu
// --------------------------------------------------------------------- //
// ----------------------------[ Piłka ]-------------------------------- //
originalBall.pos.x = platform.pos.x + platform.size.x / 2 - originalBall.radius // Ustawia pozycję piłki na środku platformy
originalBall.pos.y = platform.pos.y - 4 - originalBall.radius * 2 // Ustawia pozycję piłki nad platformą
originalBall.speed = ballSpeed; // Ustawia prędkość piłki na domyślną - ze stałej
originalBall.dir.x = 0.25; // Ustawia kierunek X piłki na 0.25
originalBall.dir.y = -1; // Ustawia kierunek Y piłki na -1
originalBall.lastTouchedObj = null; // Ustawia ostatnio dotknięty obiekt na null
// --------------------------------------------------------------------- //
setTimeout(() => {
updateStaticCanvas(); //Aktualizuje canvas statyczny po 50ms
}, 50)
}
// ==================================================================================================== //
// ====================================[ Uruchami kolejny poziom ]===================================== //
function nextLevel() {
playerLevel++ // Zwiększa poziom gracza o 1
ballSpeed += 0.1 // Zwiększa prędkość piłki o 0.1
if (playerLevel == 33) // Jeżeli poziom gracza jest równy 33
summonDOH(); // Uruchamia poziom z DOH'em
else // Jeżeli nie
loadLevel(); // Wczytuje kolejny poziom
resetToDefault() // Ustawia domyśle wartości pozycji i piłki po uruchominiu nowego poziomu
}
// ==================================================================================================== //
// ====================================[ Generuje pozycje cegieł ]===================================== //
function generateBricks(startFrom = null) { // Funkcja generująca cegiełki
let json
if (startFrom != null) { // Jeżeli wybrano poziomu od którego zacząć
json = localStorage.getItem(startFrom) // Wczytuje do json poziom z localStorage
introScreen.style.display = "none"; // Ukrywa ekran startowy
if (startFrom.includes("Poziom")) // Jeżeli jest to zwykły poziom to wpisuje jego numer do playerLevel
playerLevel = parseInt(startFrom.replace("Poziom ", "")) // Wpisuje numer poziomu do playerLevel
} else // Jeżeli nie wybrano poziomu od którego zacząć
json = localStorage.getItem(`Poziom ${playerLevel}`) // Wczytuje do json I poziom z localStorage
let allBricks = JSON.parse(json) // Parsuje json z localStorage do tablicy cegieł
if (!allBricks) { // Zabezpieczenie przeciwko błędom wczytywania
localStorage.clear() // Czyści localstorage
alert("Błąd wczytywania. Spróbuj odświeżyć stronę.") // Wyświetla komunikat
location.reload() // Odświeża stronę
}
Brick.list = []; // Usuwa wszystkie cegiełki z listy
allBricks.forEach((el, i) => { // Tworzy nowe cegły korzystając z tablicy allBricks
new Brick(
new Vector2D(parseInt(el.x), parseInt(el.y)), // Pozycja cegły
new Vector2D(99.9, 49.9), // Rozmiar cegły
parseInt(el.type) // Typ cegły
)
})
}
// ==================================================================================================== //
// ======================================[ Wczytuje nowy poziom ]====================================== //
function loadLevel(startFrom = null) { // Funkcja wczytująca poziom
if (localStorage.length < 10) { // Jeżeli localstorage jest pusty lub zawiera mniej niż 10 poziomów
localStorage.clear(); // Czyści localstorage
fetch("generator/basicLevels.json") // Wczytuje plik json zawierający poziomy
.then(function (response) {
return response.json();
})
.then(function (jsonFile) {
jsonFile.forEach((el, i) => { // Dla każdego poziomu w pliku json
if (localStorage.getItem(el.id) == null) // Jeśli nie ma go w localStorage
localStorage.setItem(el.id, JSON.stringify(el.bricks)) // Dodaje go do localStorage
})
})
.then(() => {
generateBricks(startFrom) // Generuje cegły z parametru startFrom - domyślnie null
})
} else { // Jeżeli localstorage nie jest pusty
generateBricks(startFrom) // Generuje cegły z parametru startFrom - domyślnie null
}
}
// ==================================================================================================== //
// =========================================[ Restartuje gre ]========================================= //
function restartTheGame() {
// ------------------------------[ Flagi ]------------------------------- //
gameStarted = false // Zatrzymuje grę
gameOvered = false // Ustawia stan gry na "nie przegrana"
gamePaused = false // Ustawia stan gry na "nie wstrzymana"
// ---------------------------------------------------------------------- //
// -------------------------[ Listener canvas ]-------------------------- //
canvas.addEventListener("click", () => { // Uruchamia grę po kliknięciu w obszarze pola canvas
gameStarted = true // Uruchamia grę
gameOvered = false // Ustawia stan gry na "nie przegrana"
gamePaused = false // Ustawia stan gry na "nie wstrzymana"
document.addEventListener("keydown", pauseTheGame) // Dodaje event listener do pauzowania gry
})
// ---------------------------------------------------------------------- //
// -------------------------------[ DOH ]-------------------------------- //
DOH.list.forEach((el) => { // Dla każdego DOH'a
el.remove(); // Usuwa wszystkie jego pociski
})
MiniDOH.list = []; // Usuwa wszystkie MiniDOH'y
// ---------------------------------------------------------------------- //
// ------------------------------[ Gracz ]------------------------------- //
playerPoints = 0 // Ustawia punkty gracza na 0
playerLevel = 1 // Ustawia poziom gracza na 1
playerHealth = 3 // Ustawia życie gracza na 3
// ---------------------------------------------------------------------- //
// ---------------------[ Wczytuje pierwszy poziom ]--------------------- //
if (localStorage.getItem("_startFrom") != null) { // Wczytuje poziom wybrany przez gracza w edytorze
gameStarted = true // Uruchamia grę
gameOvered = false // Ustawia stan gry na "nie przegrana"
gamePaused = false // Ustawia stan gry na "nie wstrzymana"
if (localStorage.getItem("_startFrom") != "Poziom 1") // Jeśli wybrany poziom nie jest pierwszym
playerLevel = 0 // Ustawia poziom gracza na 0
loadLevel(localStorage.getItem("_startFrom")) // Wczytuje wybrany poziom
localStorage.removeItem("_startFrom") // Usuwa zmienną tymczasową z localStorage
} else
loadLevel() // Wczytuje pierwszy poziom jeśli nie ma wybranego przez gracza
// ---------------------------------------------------------------------- //
// -------------------------------[ Inne ]------------------------------- //
playerLevel = 1 // Ustawia poziom gracza na 1
introScreen.style.display = "grid"; // Pokazuje ekran startowy
resetToDefault() // Resetuje wszystkie ustawienia do wartości domyślnych
// ---------------------------------------------------------------------- //
}
// ==================================================================================================== //
// ========================================[ Funkcja victory ]========================================= //
function victory() { // Wyświetla ekran końcowy i restartuje grę po przejściu gry
playerHealth = 99 // Ustawia życie gracza na 99
gameStarted = false // Zatrzymuje grę
document.querySelector("#victory").style.display = "grid"; // Pokazuje ekran końcowy
playSound("victory") // Odtwarza dźwięk zwycięstwa
document.querySelector("#victory").addEventListener("click", () => { // Po kliknięciu w ekran końcowy
playSound("clicked") // Odtwarza dźwięk kliknięcia
document.querySelector("#victory").style.display = "none"; // Ukrywa ekran końcowy
restartTheGame() // Restartuje grę
})
}
// ================================[ Klasa wektorów ]========================= //
// Pomogą nam w lepszej organizacji dwunumerycznych list //
class Vector2D {
constructor(x, y) { // Konstruktor wektora
this.x = x; // Wartość x
this.y = y; // Wartość y
}
add(vec) { // Dodaje wektoru
this.x += vec.x; // Dodaje do siebie wartości x
this.y += vec.y; // Dodaje do siebie wartości y
}
mul(num) { // Mnoży wektory
this.x *= num; // Mnoży wartości x
this.y *= num; // Mnoży wartości y
}
length() { // Zwraca długość wektora
return Math.sqrt((this.x * this.x) + (this.y * this.y)) // Zwraca pierwiastek z sumy kwadratów wartości x i y
}
dist(vec) { // Zwraca dystans do podanego wektora
return new Vector2D(this.x - vec.x, this.y - vec.y); // Zwraca wektor z wartościami różnicy wartości x i y
}
normalized() { // Zwraca znormalizowany wektor (liczby w zakresie 0-1)
let len = this.length(); // Zapisuje długość wektora
return new Vector2D(this.x / len, this.y / len) // Zwraca wektor z wartościami podzielonymi przez długość wektora
}
normalize() { // Zwraca znormalizowany wektor (liczby w zakresie 0-1)
let norm = this.normalized(); // Zapisuje znormalizowany wektor do norm
this.x = norm.x; // Nadpisuje wartości x i y znormalizowanymi wersjami
this.y = norm.y; // Nadpisuje wartości x i y znormalizowanymi wersjami
}
}
// =========================================================================== //
// ================================[ Odpowiada za działanie platformy ]================================ //
// ------------------------------[ Obiekt ]------------------------------ //
let platform = {
size: new Vector2D(canvas.width / 4.54, canvas.height / 100 * 2.5), // Długość i szerokość platformy
pos: null, // Pozycja platformy
prevPos: null, //Poprzednia pozycja
move: canvas.width / 100 * 5, //Ilość dodawanej pozycji przy poruszaniu się strzałkami
holdBall: null, //Czy nasza platforma trzyma piłke
timesIncreased: 0, //Ile razy nasza platforma została powiększona upgradem
canCatchBall: false, //Czy może złapać piłke (upgrade łapania)
texture: new Image(), //Tekstura platformy
draw() { // Funkcja rysująca platformę
// Jeśli platforma może złapać piłkę to zmienia teksturę na sticky
if (this.canCatchBall) this.texture.src = "img/textures/platform_sticky.png"; else this.texture.src = "img/textures/platform.png";
context.drawImage(this.texture, this.pos.x, this.pos.y, this.size.x, this.size.y) // Rysuje platformę
}
}
// ---------------------------------------------------------------------- //
// -----------------------------[ Pozycje ]------------------------------ //
platform.pos = new Vector2D(Math.floor(canvas.width / 2 - platform.size.x / 2), Math.floor(canvas.height - platform.size.y * 2.5)); // Ustawia pozycję platformy na środku
platform.prevPos = new Vector2D(platform.pos.x, platform.pos.y); // Ustawia poprzednią pozycję platformy na tą samą jak aktualna
// ---------------------------------------------------------------------- //
// -------------------------------[ Klon ]------------------------------- //
let platformClone = { //Klon platformy używany do upgrade'u
...platform, //Klonuje platformę
}
platformClone.enabled = false; //Klon platformy jest zawsze wyłączony na starcie
platformClone.texture.src = "img/textures/platform.png"; //Tekstura klonu platformy
platformClone.draw = () => { //Funkcja rysująca klon platformy
context.globalAlpha = 0.5; //Klon platformy jest przeźroczysty
context.drawImage(platformClone.texture, platformClone.pos.x, platformClone.pos.y, platformClone.size.x, platformClone.size.y) // Rysuje klon platformy
context.globalAlpha = 1; //Przywraca normalną przezroczystość
}
// ---------------------------------------------------------------------- //
// ---------------------[ Sterowanie - klawiatura ]---------------------- //
document.addEventListener("keydown", e => { // Sterowanie platformą za pomocą klawiatury
if (e.key == "ArrowLeft") { // Ruch w lewo - strzałka w lewo
if (platform.pos.x > 0) { // Jeśli platforma nie jest w lewej krawędzi
platform.prevPos.x = platform.pos.x; //Stara pozycja platformy która będzie używana do obliczenia pozycji złapanej piłki
platform.pos.x -= platform.move; // Przesuwa platformę w lewo
platform.pos.x = Math.floor(platform.pos.x); // Zaokrągla pozycję platformy
if (platformClone.enabled) { //Jeśli klon platformy jest włączony
platformClone.prevPos.x = platformClone.pos.x; // Zapisuje poprzednią pozycję klonu platformy
platformClone.pos.x = Math.floor(canvas.width - platform.pos.x - platformClone.size.x); //Ustawiamy klona po przeciwnej stronie
}
if (platform.holdBall != null) { //Jeśli platforma trzyma piłkę
//Pozycja trzymanej piłki jest responsywna do nowej pozycji platformy
let ballDiff = platform.holdBall.pos.x - platform.prevPos.x; //Oblicza różnicę między pozycją piłki a platformy
platform.holdBall.prevPos.x = platform.holdBall.pos.x; //Zapisuje poprzednią pozycję piłki x
platform.holdBall.prevPos.y = platform.holdBall.pos.y; //Zapisuje poprzednią pozycję piłki y
platform.holdBall.pos.x = Math.floor(platform.pos.x + ballDiff); //Ustawia pozycję piłki na nową
}
}
} else if (e.key == "ArrowRight") { // Ruch w prawo - strzałka w prawo
if (platform.pos.x + platform.size.x < canvas.width) { // Jeśli platforma nie jest w prawej krawędzi
platform.prevPos.x = platform.pos.x; //Zapisuje poprzednią pozycję platformy
platform.pos.x += platform.move; // Przesuwa platformę w prawo
platform.pos.x = Math.floor(platform.pos.x) // Zaokrągla pozycję platformy
if (platformClone.enabled) { //Jeśli klon platformy jest włączony
platformClone.prevPos.x = platformClone.pos.x; //Zapisuje poprzednią pozycję klonu platformy
platformClone.pos.x = Math.floor(canvas.width - platform.pos.x - platformClone.size.x); //Ustawiamy klona po przeciwnej stronie
}
if (platform.holdBall != null) { //Jeśli platforma trzyma piłkę
let ballDiff = platform.holdBall.pos.x - platform.prevPos.x; //Oblicza różnicę między pozycją piłki a platformy
platform.holdBall.prevPos.x = platform.holdBall.pos.x; //Zapisuje poprzednią pozycję piłki x
platform.holdBall.prevPos.y = platform.holdBall.pos.y; //Zapisuje poprzednią pozycję piłki y
platform.holdBall.pos.x = Math.floor(platform.pos.x + ballDiff); //Ustawia pozycję piłki na nową
}
}
}
// Wyrzucenie piłki gdy ją trzymamy
if (platform.holdBall != null && e.key == " ") {
platform.holdBall = null;
}
})
// ---------------------------------------------------------------------- //
// ---------------------[ Sterowanie - myszka ]---------------------- //
// Odpowiada za ruch platformy za pomocą myszki
canvas.addEventListener("mousemove", e => {
// Odpowiada za niewychodzenie platformy poza planszę
let oldPlatformPosX = platform.pos.x;
if (
(e.clientX - canvas.getBoundingClientRect().left) * (canvas.width / canvas.getBoundingClientRect().width) - platform.size.x / 2 > 0 &&
(e.clientX - canvas.getBoundingClientRect().left) * (canvas.width / canvas.getBoundingClientRect().width) - platform.size.x / 2 + platform.size.x < canvas.width
) {
platform.prevPos.x = platform.pos.x;
platform.pos.x = Math.floor((e.clientX - canvas.getBoundingClientRect().left) * (canvas.width / canvas.getBoundingClientRect().width) - platform.size.x / 2);
}
if (platformClone.enabled) {
platformClone.prevPos.x = platformClone.pos.x;
platformClone.pos.x = Math.floor(canvas.width - platform.pos.x - platformClone.size.x);
}
// Odpowiada za przesuwanie piłki trzymanje przez platformę
if (platform.holdBall != null) {
let ballDiff = platform.holdBall.pos.x - oldPlatformPosX;
platform.holdBall.prevPos.x = platform.holdBall.pos.x;
platform.holdBall.prevPos.y = platform.holdBall.pos.y;
platform.holdBall.pos.x = Math.floor(platform.pos.x + ballDiff);
}
})
canvas.addEventListener("mousedown", e => {
// Wyrzucenie piłki gdy ją trzymamy LPM
if (platform.holdBall != null && e.button == 0) {
platform.holdBall = null;
}
})
// ---------------------------------------------------------------------- //
// ==================================================================================================== //
// ==========================================[ Klasa piłek ]=========================================== //
class Ball {
static list = []; // Lista wszystkich piłek
static ballPower = 0; // Moc wszystkich piłek związana z upgradem mocy. Liczba wskazuje na ilość razy w których piłka może swobodnie usunąć cegłe bez jej odbicia
static ballSpeedIncrease = (ballSpeed / 1000) * 3; //Wartość dodawana do predkości piłki przy kazdym uderzeniu
constructor(pos, dir, radius, enemyBall = false, enemyParent) {
this.pos = pos; // Przechowuje pozycje piłki
this.prevPos = new Vector2D(pos.x, pos.y); // Przechowuje poprzednią pozycje piłki
this.dir = dir; // Przechowuje kierunek piłki
this.radius = radius; // Przechowuje promień piłki
this.enemyBall = enemyBall; // Czy piłka jest fireballem?
if (enemyBall)
this.parent = enemyParent; // Rodzic fireballa
this.speed = ballSpeed; // Szybkość
if (enemyBall)
this.speed = 9; // Fireball jest szybszy
this.texture = new Image(); // Tekstura piłki
if (enemyBall)
this.texture.src = "img/FireBall.svg";
else
this.texture.src = "img/ball.png";
this.power = Ball.ballPower; // Siła piłki
if (!enemyBall) Ball.list.push(this); // Dodaje do listy wszystkich piłek, fireballe są dodawane do tablicy DOH'a
}
// Zmienia pozycje piłki
setPos(x, y) {
this.prevPos = this.pos;
this.pos.x = x;
this.pos.y = y;
}
// Zmienia kierunek piłki
setDir(x, y) {
this.dir.x = x;
this.dir.y = y;
}
invertDir() { this.dir.x *= -1; this.dir.y *= -1; } // Odwraca nam kierunek
invertDirX() { this.dir.x *= -1; } // Odwraca nasz kierunek tylko na osi X
invertDirY() { this.dir.y *= -1; } // Odwraca nasz kierunek tylko na osi Y
// Logika i działanie piłki
think() {
let hit = false; //Jeśli coś dotkneliśmy, nie sprawdzamy kolizji innych rzeczy
//Kolizja ze ścianami
if (this.pos.x <= 0) {
if (this.lastTouchedObj != 'leftwall') playSound("hitEdge");
this.lastTouchedObj = 'leftwall';
this.dir.x = Math.abs(this.dir.x);
hit = true;
} else if (this.pos.x + this.radius * 2 >= canvas.width) {
if (this.lastTouchedObj != 'rightwall') playSound("hitEdge");
this.lastTouchedObj = 'rightwall';
this.dir.x = -Math.abs(this.dir.x);
hit = true;
} else if (this.pos.y <= 0) {
if (this.lastTouchedObj != 'topwall') playSound("hitEdge");
this.lastTouchedObj = 'topwall';
this.dir.y = Math.abs(this.dir.y);
hit = true;
} else if (this.pos.y + this.radius * 2 >= canvas.height && this.enemyBall) {
if (this.lastTouchedObj != 'bottomwall') playSound("hitEdge");
this.lastTouchedObj = 'bottomwall';
this.dir.y = -Math.abs(this.dir.y);
hit = true;
}
//Kolizja z cegłami
if (!hit && !this.enemyBall) {
Brick.list.forEach((brick) => {
if (!hit && this.lastTouchedObj != brick) {
let col = circXrectCollision(this, brick)
if (col.hit) {
//Zabezpieczenie by piłka nie zbijała dwóch cegieł na raz
if ((col.side == 'left' && this.dir.x > 0) || (col.side == 'right' && this.dir.x < 0)) {
hit = true;
} else if ((col.side == 'top' && this.dir.y > 0) || (col.side == 'bottom' && this.dir.y < 0)) {
hit = true;
}
if (hit) {
//Jeśli piłka ma wystarczającą moc to nie odbija się
if (this.power > 0 && brick.type != 9 && brick.type != 8)
this.power--;
else if (this.power > 0 && brick.type == 8 && this.power >= brick.health)
this.power -= brick.health;
else //W przeciwnym wypadku piłka odbiję się normalnie
if (col.side == 'left' || col.side == 'right') this.invertDirX(); else this.invertDirY();
this.lastTouchedObj = brick;
brick.remove(); //Usuwa cegłe
}
}
}
})
}
//Kolizja z DOH'em
if (!hit && !this.enemyBall) {
DOH.list.forEach((doh) => {
let col = circXrectCollision(this, doh)
if (col.hit && this.lastTouchedObj != doh) {
if (col.side == 'left' || col.side == 'right')
this.invertDirX();
else
this.invertDirY();
if (doh.minionsNum <= 0) //Jeżeli nie ma mini doh'ów, możemy zranić samego doh'a
if (this.power > 0) doh.hp -= this.power; else doh.hp--;
this.lastTouchedObj = doh;
playSound('hitDOH'); // Odtwarzanie dźwięku uderzenia w DOHa
updateStaticCanvas(); //Odświeża statyczny canvas by odświeżyło pasek życia
}
})
}
//Kolizja z MiniDOH'em
if (!hit && !this.enemyBall) {
MiniDOH.list.forEach((doh) => {
let col = circXrectCollision(this, doh)
if (col.hit && this.lastTouchedObj != doh) {
if (col.side == 'left' || col.side == 'right')
this.invertDirX();
else
this.invertDirY();
if (this.power > 0) doh.hp -= this.power; else doh.hp--;
this.lastTouchedObj = doh;
if (doh.hp <= 0)
doh.remove();
playSound('hitSmallDOH'); // Odtwarzanie dźwięku uderzenia w małego DOHa
}
})
}
//Kolizja z platformą
if (!hit && platform.holdBall != this && this.lastTouchedObj != platform) {
let col = circXrectCollision(this, platform)
if (col.hit && this.lastTouchedObj != platform) {
if (!this.enemyBall) {
if (col.side == 'left' || col.side == 'right')
this.invertDirX();
else {
this.dir.x = col.hitFactor * 5; //Kierunek piłki jest zależny od pozycji w którą wleciała w paletke
this.invertDirY();
if (platform.canCatchBall && platform.holdBall == null && col.side == 'top')
platform.holdBall = this; //Jeśli możemy to przechwytujemy piłke
}
playSound('hitPlatform'); // Odtwarzanie dźwięku uderzenia w platformę
hit = true;
this.lastTouchedObj = platform;
this.power = Ball.ballPower; // Odświeżamy moc piłki
} else {
playSound("hitedByEnemy") // Odtwarzanie dźwięku uderzenia w platformę wrogiej piłki
playerHealth--;
resetToDefault();
}
}
}
//Kolizja z klonem platformy
if (!hit && platformClone.enabled && this.lastTouchedObj != platformClone && !this.enemyBall) {
let col = circXrectCollision(this, platformClone)
if (col.hit && this.lastTouchedObj != platformClone) {
if (col.side == 'left' || col.side == 'right')
this.invertDirX();
else {
this.dir.x = col.hitFactor * 5;
this.invertDirY();
}
playSound('hitPlatform'); // Odtwarzanie dźwięku uderzenia w platformę
hit = true;
this.lastTouchedObj = platformClone;
this.power = Ball.ballPower;
}
}
if (hit && platform.holdBall != this && !this.enemyBall) {
this.speed += Ball.ballSpeedIncrease; //Zwiększamy prędkość piłki po kolizji
}
//Ruch piłek
if (platform.holdBall != this) {
this.prevPos.x = this.pos.x;
this.prevPos.y = this.pos.y;
let len = this.dir.length()
this.pos.x += Math.floor((this.dir.x / len) * this.speed)
this.pos.y += Math.floor((this.dir.y / len) * this.speed)
}
// Sprawdza czy piłka wypadła
if (this.pos.y > canvas.height / 100 * 96.4 && !this.enemyBall) {
if (this == originalBall) {
playerHealth--;
playSound("fallOfScreen") // Odtwarzanie dźwięku upadku piłki poza ekran
resetToDefault();
} else { //Dodatkowe piłki są poprostu usuwane
this.remove();
}
}
}
// Rysuje naszą piłke
draw() {
//Efekt gdy piłka ma więcej mocy
if (this.power > 0 && !this.enemyBall) {
let len = this.dir.length();
for (let i = 0; i < this.power; i++) {
context.globalAlpha = 0.75 - (0.75 / (this.power + 1) * (i + 1));
context.drawImage(this.texture, Math.floor(this.pos.x - ((this.dir.x / len) * (this.radius * 2 / 4) * (i + 1))), Math.floor(this.pos.y - ((this.dir.y / len) * (this.radius * 2 / 4) * (i + 1))), this.radius * 2, this.radius * 2)
}
context.globalAlpha = 1;
}
//Dodatkowe piłki są przeźroczyste
if (this != originalBall && !this.enemyBall)
context.globalAlpha = 0.5;
context.drawImage(this.texture, this.pos.x, this.pos.y, this.radius * 2, this.radius * 2)
context.globalAlpha = 1;
}
remove() { // Usuwa piłkę
if (!this.enemyBall)
Ball.list.forEach((el, index) => {
if (el == this)
Ball.list.splice(index, 1);
})
else
this.enemyParent.fireBalls.forEach((el, index) => {
if (el == this)
this.enemyParent.fireBalls[index] = null;
})
}
static refreshBallPower() { // Odświeża moc piłki
Ball.list.forEach((el) => {
el.power = Ball.ballPower;
})
}
}
// ---------------------------[ Piłka główna ]--------------------------- //
let originalBall = new Ball( // Tworzy piłkę
new Vector2D( // Ustawia pozycje
null, // x
null // y
),
new Vector2D(0.25, -1), // Kierunek
canvas.width / 27.22 / 2 // Size
);
// --------------------------------------------------------------------- //
// -----------[ Na początku piłka pojawia się nad platformą ]------------ //
originalBall.pos.x = platform.pos.x + platform.size.x / 2 - originalBall.radius
originalBall.pos.y = platform.pos.y - 0 - originalBall.radius * 2
originalBall.prevPos.x = originalBall.pos.x;
originalBall.prevPos.y = originalBall.pos.y;
// --------------------------------------------------------------------- //
// ==================================================================================================== //
// =========================================[ Ulepszenia ]========================================= //
// ---------------------------[ Klasa lasera ]--------------------------- //
class Projectile {
static list = []; //Lista wszystkich
static playerLasers = 0; //Ilość doładowań lasera gracza
static nextPlayerFire = 0; //Czas następnego wystrzału lasera
static playerLaserSize = new Vector2D(canvas.width / 50, canvas.height / 20); //Wielkość lasera gracza
constructor(pos, dir, size, speed, player, texture = "img/laserProjectile.png") {
this.pos = pos; // Pozycja
this.prevPos = new Vector2D(pos.x, pos.y); // Poprzednia pozycja
this.dir = dir; // Kierunek
this.size = size; // Wielkość
this.speed = speed; // Szybkość
this.isPlayers = player // Czy laser należy do gracza
this.texture = new Image(); // Tekstura
this.texture.src = texture;
Projectile.list.push(this);
}
draw() { //Rysowanie
context.save()
//Obracamy teksture względem kierunku
context.translate(this.pos.x + this.size.x / 2, this.pos.y + this.size.y / 2)
context.rotate((90 * this.dir.x) * Math.PI / 180 * this.dir.y)
context.translate(-(this.pos.x - this.size.x / 2), -(this.pos.y - this.size.y / 2))
context.drawImage(this.texture, this.pos.x, this.pos.y, this.size.x, this.size.y)
context.restore()
}
remove() { //Usuwanie
Projectile.list.forEach((el, index) => {
if (el == this)
Projectile.list.splice(index, 1);
})
}
}
// --------------------------------------------------------------------- //
// --------------------------[ Klasa portali ]--------------------------- //
class Portal { //Portal
static list = []; //Lista wszystkich
static enabled = false; //Czy portal jest włączony
constructor(pos, size) { //Tworzenie
this.pos = pos;
this.size = size;
this.texture = new Image();
this.texture.src = "img/portal.png";
Portal.list.push(this);
}
draw() { //Rysowanie
contextStatic.drawImage(this.texture, this.pos.x, this.pos.y, this.size.x, this.size.y);
}
remove() { //Usuwanie
Portal.list.forEach((el, index) => {
if (el == this)
Portal.list.splice(index, 1); //Usuwa portal z listy
})
}
}
// --------------------------------------------------------------------- //
// ------------------------[ Ustawienia portalu ]------------------------ //
let portalW, portalH; //Wielkość portalu
portalW = canvas.width / 12.5; // Szerokość portalu
portalH = canvas.height / 10; // Wysokość portalu
new Portal(new Vector2D(canvas.width - canvas.width * 0.0005 - portalW + 25, canvas.height - canvas.height * 0.025 - portalH), new Vector2D(portalW, portalH)); // Portal będzie po prawej stronie
// --------------------------------------------------------------------- //
// ------------------------[ Ustawienia ulepszeń ]----------------------- //
let prevUpgrade;
let curUpgrade = null;
//Dla lepszej czytelności kodu
const UPGRADE_MOREHP = 0; // Więcej życia
const UPGRADE_BALLPOWER = 1; // Większa siła piłki
const UPGRADE_MOREBALLS = 2; // Więcej piłek
const UPGRADE_BALLCATCH = 3; // Tryb chwytania piłki
const UPGRADE_PLATFORMCLONE = 4; // Klon platformy
const UPGRADE_PLATFORMSIZE = 5; // Powiększenie platformy
const UPGRADE_LASER = 6; // Laser
const UPGRADE_SKIP = 7; // Przejście do następnego poziomu
// --------------------------------------------------------------------- //
// ----------------------[ Usuwa efekt ulepszenia ]---------------------- //
function removeUpgradeEffect(upgrade) {
switch (upgrade) { //Usuwa efekt ulepszenia
case UPGRADE_LASER: //Laser
Projectile.playerLasers = 0;
break;
case UPGRADE_SKIP: //Przejście do następnego poziomu
Portal.enabled = false;
updateStaticCanvas();
break;
case UPGRADE_BALLPOWER: //Większa siła piłki
Ball.ballPower = 0;
Ball.refreshBallPower();
break;
case UPGRADE_MOREBALLS: //Więcej piłek
Ball.list.splice(1);
break;
case UPGRADE_BALLCATCH: //Tryb chwytania piłki
platform.canCatchBall = false;
break;
case UPGRADE_PLATFORMSIZE: //Powiększenie platformy
platform.size.x -= Upgrade.platformSizeIncrease * platform.timesIncreased;
platform.pos.x += Upgrade.platformSizeIncrease * platform.timesIncreased / 2;
platform.timesIncreased = 0;
break;
case UPGRADE_PLATFORMCLONE: //Klon platformy
platformClone.enabled = false;
break;
default:
break;
}
}
// --------------------------------------------------------------------- //
// -----------------[ Usuwa wszystkie efekty ulepszeń ]------------------ //
function removeAllUpgrades() {
for (let i = 0; i <= 7; i++) {
removeUpgradeEffect(i);
}
}
// --------------------------------------------------------------------- //
// ----------------------[ Klasa ulepszeń - byty ]----------------------- //
class Upgrade {
static list = []; //Lista wszystkich
static typeToTexture = [ //Tekstury ulepszeń
"img/upgrades/upgrade_hp.svg",
"img/upgrades/upgrade_strength.svg",
"img/upgrades/upgrade_moreballs.svg",
"img/upgrades/upgrade_stick.svg",
"img/upgrades/upgrade_doppelganger.svg",
"img/upgrades/upgrade_size.svg",
"img/upgrades/upgrade_laser.svg",
"img/upgrades/upgrade_skip.svg"
]
static nextUpgradePoints = 1200; //Punkty do kolejnego upgrade'u
static platformSizeIncrease = canvas.width / 25; //Ilość wydłużenia platformy
constructor(pos, type) { //Tworzenie
this.pos = pos; //Pozycja
this.prevPos = new Vector2D(pos.x, pos.y); //Poprzednia pozycja
this.velY = 3.2; // Szybkość w płaszczyźnie Y, zaczynamy od 3.2 by dodać efekt "podskoczenia" przy pojawieniu się ulepszenia
this.type = type; //Typ
this.size = new Vector2D(canvas.width / 10 - 0.1, canvas.width / 10 - 0.1); //Wielkość
this.texture = new Image(); //Tekstura
this.texture.src = Upgrade.typeToTexture[this.type]; //Tekstura - źródło
Upgrade.list.push(this); //Dodaje do listy
}
draw() { //Rysowanie
context.drawImage(this.texture, this.pos.x, this.pos.y, this.size.x, this.size.y);
}
collect() { //Zbieranie
playSound("clicked") //Dźwięk
prevUpgrade = curUpgrade;
curUpgrade = this.type;
switch (curUpgrade) {
//Ogólne
case UPGRADE_MOREHP: //Więcej życia
playerHealth++;
updateStaticCanvas();
break;
case UPGRADE_LASER: //Laser
Projectile.playerLasers++;
break;
case UPGRADE_SKIP: //Przejście do następnego poziomu
Portal.enabled = true;