1+ // game.js z aktywacją gracza ruchem i wstrzymaniem wrogów przed aktywacją
2+
13const canvas = document . getElementById ( "gameCanvas" ) ;
24const ctx = canvas . getContext ( "2d" ) ;
3-
45canvas . width = 800 ;
56canvas . height = 600 ;
67
78const playerColors = [ "white" , "lime" , "cyan" , "orange" , "violet" ] ;
8- const enemyTypes = [ "vi" , "vim" , "neovim" ] ;
9+ const enemySprites = {
10+ vi : new Image ( ) ,
11+ vim : new Image ( ) ,
12+ neovim : new Image ( )
13+ } ;
14+ enemySprites . vi . src = "/sprites/vi.png" ;
15+ enemySprites . vim . src = "/sprites/vim.png" ;
16+ enemySprites . neovim . src = "/sprites/neovim.png" ;
17+
918let enemyBullets = [ ] ;
1019let gameOver = false ;
1120let level = 1 ;
21+ let fireInterval = 1500 ;
22+ let fireTimer ;
23+
24+ function waitForSprites ( callback ) {
25+ const allLoaded = Object . values ( enemySprites ) . every ( img => img . complete && img . naturalHeight !== 0 ) ;
26+ if ( allLoaded ) callback ( ) ;
27+ else setTimeout ( ( ) => waitForSprites ( callback ) , 100 ) ;
28+ }
1229
1330class Player {
1431 constructor ( x , color ) {
@@ -25,13 +42,17 @@ class Player {
2542 this . score = 0 ;
2643 this . alive = true ;
2744 this . respawnTimer = 0 ;
28- this . respawnCountdown = null ;
2945 }
3046
3147 draw ( ) {
3248 if ( this . active && this . alive ) {
49+ ctx . beginPath ( ) ;
50+ ctx . moveTo ( this . x , this . y ) ;
51+ ctx . lineTo ( this . x - 15 , this . y + 30 ) ;
52+ ctx . lineTo ( this . x + 15 , this . y + 30 ) ;
53+ ctx . closePath ( ) ;
3354 ctx . fillStyle = this . color ;
34- ctx . fillRect ( this . x - this . width / 2 , this . y , this . width , this . height ) ;
55+ ctx . fill ( ) ;
3556 } else if ( ! this . alive && this . lives > 0 ) {
3657 const secondsLeft = Math . ceil ( ( 5000 - ( Date . now ( ) - this . respawnTimer ) ) / 1000 ) ;
3758 ctx . fillStyle = "white" ;
@@ -45,6 +66,8 @@ class Player {
4566 if ( now - this . lastShot > 1000 && this . active && this . alive ) {
4667 this . bullets . push ( { x : this . x , y : this . y } ) ;
4768 this . lastShot = now ;
69+ } else if ( gameOver ) {
70+ newGame ( ) ;
4871 }
4972 }
5073
@@ -81,22 +104,22 @@ class Enemy {
81104 }
82105
83106 draw ( ) {
84- ctx . fillStyle = "red" ;
85- ctx . font = "16px monospace" ;
86- ctx . fillText ( this . type , this . x , this . y ) ;
107+ const sprite = enemySprites [ this . type ] ;
108+ if ( sprite . complete && sprite . naturalHeight !== 0 ) {
109+ ctx . drawImage ( sprite , this . x , this . y , 30 , 30 ) ;
110+ } else {
111+ ctx . fillStyle = "red" ;
112+ ctx . fillRect ( this . x , this . y , 30 , 30 ) ;
113+ }
87114 }
88115
89116 update ( ) {
117+ if ( ! player1 . active && ! player2 . active ) return ;
90118 this . x += this . dx ;
91119 if ( this . x < 0 || this . x > canvas . width - 30 ) {
92120 this . dx *= - 1 ;
93121 this . y += 10 ;
94122 }
95-
96- if ( this . y > canvas . height - 100 && ! gameOver ) {
97- gameOver = true ;
98- setTimeout ( ( ) => alert ( "Game Over – wróg dotarł do bazy!" ) , 10 ) ;
99- }
100123 }
101124}
102125
@@ -110,64 +133,32 @@ function newGame() {
110133 level = 1 ;
111134 gameOver = false ;
112135 createEnemies ( ) ;
136+ updateFireRate ( ) ;
113137}
114138
115- function randomColor ( ) {
116- return playerColors [ Math . floor ( Math . random ( ) * playerColors . length ) ] ;
139+ function updateFireRate ( ) {
140+ clearInterval ( fireTimer ) ;
141+ fireInterval = Math . max ( 200 , 1500 - level * 100 ) ;
142+ fireTimer = setInterval ( ( ) => {
143+ if ( player1 . active || player2 . active ) enemyFire ( ) ;
144+ } , fireInterval ) ;
117145}
118146
119147function createEnemies ( ) {
120148 enemies = [ ] ;
149+ const types = [ "vi" , "vim" , "neovim" ] ;
121150 const rows = 2 + level ;
122151 const cols = 4 + level ;
123152 for ( let row = 0 ; row < rows ; row ++ ) {
124153 for ( let col = 0 ; col < cols ; col ++ ) {
125- enemies . push ( new Enemy ( 60 + col * 60 , 50 + row * 40 , enemyTypes [ Math . floor ( Math . random ( ) * 3 ) ] ) ) ;
154+ const type = types [ Math . floor ( Math . random ( ) * types . length ) ] ;
155+ enemies . push ( new Enemy ( 60 + col * 60 , 50 + row * 40 , type ) ) ;
126156 }
127157 }
128158}
129159
130- function checkCollisions ( player ) {
131- player . bullets . forEach ( ( b , i ) => {
132- enemies . forEach ( ( e , j ) => {
133- if ( b . x > e . x - 10 && b . x < e . x + 30 && b . y < e . y + 16 && b . y > e . y ) {
134- enemies . splice ( j , 1 ) ;
135- player . bullets . splice ( i , 1 ) ;
136- player . score += 1 ;
137- }
138- } ) ;
139-
140- enemyBullets . forEach ( ( eb , k ) => {
141- if ( b . x > eb . x - 4 && b . x < eb . x + 4 && b . y < eb . y + 10 && b . y > eb . y ) {
142- enemyBullets . splice ( k , 1 ) ;
143- player . bullets . splice ( i , 1 ) ;
144- }
145- } ) ;
146- } ) ;
147- }
148-
149- function checkPlayerHit ( player ) {
150- if ( ! player . alive ) return ;
151- enemyBullets . forEach ( ( b , i ) => {
152- if (
153- b . x > player . x - player . width / 2 &&
154- b . x < player . x + player . width / 2 &&
155- b . y > player . y &&
156- b . y < player . y + player . height
157- ) {
158- player . takeHit ( ) ;
159- enemyBullets . splice ( i , 1 ) ;
160- }
161- } ) ;
162- }
163-
164- function updateEnemyBullets ( ) {
165- enemyBullets = enemyBullets . filter ( b => b . y < canvas . height ) ;
166- enemyBullets . forEach ( b => {
167- b . y += 4 ;
168- ctx . fillStyle = "yellow" ;
169- ctx . fillRect ( b . x - 2 , b . y , 4 , 10 ) ;
170- } ) ;
160+ function randomColor ( ) {
161+ return playerColors [ Math . floor ( Math . random ( ) * playerColors . length ) ] ;
171162}
172163
173164function enemyFire ( ) {
@@ -186,7 +177,6 @@ function drawHUD() {
186177
187178function draw ( ) {
188179 ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
189-
190180 if ( gameOver ) {
191181 ctx . fillStyle = "red" ;
192182 ctx . font = "30px sans-serif" ;
@@ -217,94 +207,94 @@ function draw() {
217207 updateEnemyBullets ( ) ;
218208 drawHUD ( ) ;
219209
220- // Level progression
221210 if ( enemies . length === 0 && ! gameOver ) {
222211 level ++ ;
223212 createEnemies ( ) ;
213+ updateFireRate ( ) ;
214+ }
215+
216+ if ( ! player1 . alive && player1 . lives === 0 && ! player2 . alive && player2 . lives === 0 ) {
217+ gameOver = true ;
224218 }
225219
226220 requestAnimationFrame ( draw ) ;
227221}
228222
229- setInterval ( enemyFire , 1500 ) ;
230- newGame ( ) ;
231- draw ( ) ;
223+ function updateEnemyBullets ( ) {
224+ enemyBullets = enemyBullets . filter ( b => b . y < canvas . height ) ;
225+ enemyBullets . forEach ( b => {
226+ b . y += 4 ;
227+ ctx . fillStyle = "yellow" ;
228+ ctx . fillRect ( b . x - 2 , b . y , 4 , 10 ) ;
229+ } ) ;
230+ }
232231
233- // Input: keyboard
234- document . addEventListener ( "keydown" , ( e ) => {
235- if ( e . key === "a" ) {
236- player1 . x -= player1 . speed ;
237- player1 . active = true ;
238- }
239- if ( e . key === "d" ) {
240- player1 . x += player1 . speed ;
241- player1 . active = true ;
242- }
243- if ( e . key === " " ) {
244- player1 . shoot ( ) ;
245- player1 . active = true ;
246- }
232+ function checkCollisions ( player ) {
233+ player . bullets . forEach ( ( b , i ) => {
234+ enemies . forEach ( ( e , j ) => {
235+ if ( b . x > e . x - 10 && b . x < e . x + 30 && b . y < e . y + 16 && b . y > e . y ) {
236+ enemies . splice ( j , 1 ) ;
237+ player . bullets . splice ( i , 1 ) ;
238+ player . score += 1 ;
239+ }
240+ } ) ;
241+ enemyBullets . forEach ( ( eb , k ) => {
242+ if ( b . x > eb . x - 4 && b . x < eb . x + 4 && b . y < eb . y + 10 && b . y > eb . y ) {
243+ enemyBullets . splice ( k , 1 ) ;
244+ player . bullets . splice ( i , 1 ) ;
245+ }
246+ } ) ;
247+ } ) ;
248+ }
247249
248- if ( e . key === "ArrowLeft" ) {
249- player2 . x -= player2 . speed ;
250- player2 . active = true ;
251- }
252- if ( e . key === "ArrowRight" ) {
253- player2 . x += player2 . speed ;
254- player2 . active = true ;
255- }
256- if ( e . key === "ArrowUp" ) {
257- player2 . shoot ( ) ;
258- player2 . active = true ;
259- }
250+ function checkPlayerHit ( player ) {
251+ if ( ! player . alive ) return ;
252+ enemyBullets . forEach ( ( b , i ) => {
253+ if (
254+ b . x > player . x - player . width / 2 &&
255+ b . x < player . x + player . width / 2 &&
256+ b . y > player . y &&
257+ b . y < player . y + player . height
258+ ) {
259+ player . takeHit ( ) ;
260+ enemyBullets . splice ( i , 1 ) ;
261+ }
262+ } ) ;
263+ }
260264
261- if ( e . key === "r" ) {
262- newGame ( ) ;
263- }
265+ window . addEventListener ( "keydown" , ( e ) => {
266+ if ( e . key === "a" ) { player1 . x -= player1 . speed ; player1 . active = true ; }
267+ if ( e . key === "d" ) { player1 . x += player1 . speed ; player1 . active = true ; }
268+ if ( e . key === " " ) { player1 . shoot ( ) ; player1 . active = true ; }
269+ if ( e . key === "ArrowLeft" ) { player2 . x -= player2 . speed ; player2 . active = true ; }
270+ if ( e . key === "ArrowRight" ) { player2 . x += player2 . speed ; player2 . active = true ; }
271+ if ( e . key === "ArrowUp" ) { player2 . shoot ( ) ; player2 . active = true ; }
272+ if ( e . key === "r" ) newGame ( ) ;
264273} ) ;
265274
266- // Input: touch
267275canvas . addEventListener ( "touchstart" , ( e ) => {
268276 e . preventDefault ( ) ;
269277 const x = e . touches [ 0 ] . clientX ;
270278 const relX = x - canvas . getBoundingClientRect ( ) . left ;
271-
272279 player1 . active = true ;
273- if ( relX < canvas . width / 3 ) {
274- player1 . x -= player1 . speed * 1.5 ;
275- } else if ( relX > 2 * canvas . width / 3 ) {
276- player1 . x += player1 . speed * 1.5 ;
277- } else {
278- player1 . shoot ( ) ;
279- }
280+ if ( relX < canvas . width / 3 ) player1 . x -= player1 . speed ;
281+ else if ( relX > 2 * canvas . width / 3 ) player1 . x += player1 . speed ;
282+ else player1 . shoot ( ) ;
280283} , { passive : false } ) ;
281284
282- // Input: joystick (WebSocket)
283285const ws = new WebSocket ( "ws://" + location . hostname + "/ws" ) ;
284286ws . onmessage = ( event ) => {
285287 const data = JSON . parse ( event . data ) ;
288+ if ( data . j1x < 1500 ) { player1 . x -= player1 . speed ; player1 . active = true ; }
289+ else if ( data . j1x > 3500 ) { player1 . x += player1 . speed ; player1 . active = true ; }
290+ if ( data . j1f ) { player1 . shoot ( ) ; player1 . active = true ; }
286291
287- if ( data . j1x < 1500 ) {
288- player1 . x -= player1 . speed ;
289- player1 . active = true ;
290- } else if ( data . j1x > 3500 ) {
291- player1 . x += player1 . speed ;
292- player1 . active = true ;
293- }
294- if ( data . j1f ) {
295- player1 . shoot ( ) ;
296- player1 . active = true ;
297- }
298-
299- if ( data . j2x < 1500 ) {
300- player2 . x -= player2 . speed ;
301- player2 . active = true ;
302- } else if ( data . j2x > 3500 ) {
303- player2 . x += player2 . speed ;
304- player2 . active = true ;
305- }
306- if ( data . j2f ) {
307- player2 . shoot ( ) ;
308- player2 . active = true ;
309- }
292+ if ( data . j2x < 1500 ) { player2 . x -= player2 . speed ; player2 . active = true ; }
293+ else if ( data . j2x > 3500 ) { player2 . x += player2 . speed ; player2 . active = true ; }
294+ if ( data . j2f ) { player2 . shoot ( ) ; player2 . active = true ; }
310295} ;
296+
297+ waitForSprites ( ( ) => {
298+ newGame ( ) ;
299+ draw ( ) ;
300+ } ) ;
0 commit comments