Mercurial > maze-src
comparison game.js @ 0:1eef88068f9f tip
initial commit of maze game source
| author | ferencd |
|---|---|
| date | Sun, 15 Sep 2019 11:46:47 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1eef88068f9f |
|---|---|
| 1 const pic_size = 64; // The size of 1 cell in the maze | |
| 2 | |
| 3 // initial position of the player is always: (0,0) ie. 12px 12px considering the wall size the the top and left | |
| 4 let x = 12; | |
| 5 let y = 12; | |
| 6 | |
| 7 // moving direction of the player | |
| 8 var x_delta = 0; | |
| 9 var y_delta = 0; | |
| 10 | |
| 11 // player location in grid | |
| 12 var col = 0; // The column in the maze where the player is | |
| 13 var row = 0; // The row in the maze where the player is | |
| 14 | |
| 15 // Prepare the canvas | |
| 16 var canvas = document.getElementById("canvas"); | |
| 17 var canvas_container = document.getElementById("canvasdiv"); | |
| 18 var context = canvas.getContext('2d'); | |
| 19 | |
| 20 // canvas size calculation | |
| 21 var canvas_calc_width = w * pic_size ; | |
| 22 var canvas_calc_height = h * pic_size; | |
| 23 | |
| 24 canvas.style.width = canvas_calc_width + "px"; | |
| 25 canvas.style.height = canvas_calc_height + "px"; | |
| 26 | |
| 27 canvas.width = canvas_calc_width; | |
| 28 canvas.height = canvas_calc_height; | |
| 29 | |
| 30 // Approximates the last row/col drawn for the canvas scrolling | |
| 31 var last_row_drawn = canvas_container.offsetHeight / pic_size; | |
| 32 var first_row_drawn = 0; | |
| 33 var last_col_drawn = canvas_container.offsetWidth / pic_size; | |
| 34 var first_col_drawn = 0; | |
| 35 | |
| 36 // fill the canvas with black | |
| 37 context.fillStyle = "black"; | |
| 38 context.fillRect(0, 0, canvas.width, canvas.height); | |
| 39 | |
| 40 // constants for direction checking | |
| 41 const N = 1; | |
| 42 const S = 2; | |
| 43 const E = 4; | |
| 44 const W = 8; | |
| 45 | |
| 46 // Corner constants, to know where to put a torch | |
| 47 const TOP_LEFT = 1; | |
| 48 const TOP_RIGHT = 2; | |
| 49 const BOTTOM_LEFT = 4; | |
| 50 const BOTTOM_RIGHT = 8; | |
| 51 | |
| 52 // How far must we be from the maze edge to trigger scrolling | |
| 53 const MAZE_EDGE_DISTANCE_SCROLL_TRIGGER = 4; | |
| 54 | |
| 55 // How much we can see from the maze | |
| 56 var LIGHT_RADIUS = 3; | |
| 57 | |
| 58 // how the maze looks | |
| 59 const FULL_FOG = 0; | |
| 60 const FULL_DRAWN = 1; | |
| 61 const FOG_DRAWN = 2; | |
| 62 | |
| 63 // indexes of the various directions the player can go | |
| 64 const MOVE_IDX_LEFT = 0; | |
| 65 const MOVE_IDX_UP = 1; | |
| 66 const MOVE_IDX_RIGHT = 2; | |
| 67 const MOVE_IDX_DOWN = 3; | |
| 68 | |
| 69 // Player state: Dead/Alive | |
| 70 const PLAYER_ALIVE = 1; | |
| 71 const PLAYER_DIED = 2; | |
| 72 var player_state = PLAYER_ALIVE; | |
| 73 | |
| 74 // Player weapon | |
| 75 const WEAPON_NONE = -1; | |
| 76 const WEAPON_SPEAR = 0; | |
| 77 | |
| 78 var player_weapon = WEAPON_NONE; | |
| 79 | |
| 80 // Player armor | |
| 81 const ARMOR_NONE = -1; | |
| 82 const ARMOR_CHAINMAIL = 1; | |
| 83 const SHOES = 2; | |
| 84 const RING = 3; | |
| 85 var player_armor = ARMOR_NONE; | |
| 86 | |
| 87 // whether we remove life from steps or not | |
| 88 const STEPS_COST = 0; // each step is 1 life | |
| 89 const STEPS_FREE = 1; // steps do not cost 1 life | |
| 90 const STEPS_SICK = 2; // player is sick, steps cost 2 life | |
| 91 var player_steps = STEPS_COST; | |
| 92 var step_count = 0; | |
| 93 | |
| 94 const TIME_HAS_EFFECT = 0; | |
| 95 const TIME_HAS_NO_EFFECT = 1; | |
| 96 var time_effect = TIME_HAS_EFFECT; | |
| 97 | |
| 98 const GAME_SUSPENDED = 1; | |
| 99 const GAME_RUNNING = 0; | |
| 100 var game_state = GAME_SUSPENDED; | |
| 101 | |
| 102 var time_passing_timeout = null; | |
| 103 | |
| 104 // These correspond to specific igw types: | |
| 105 // 0 - empty handed skeleton. Can be killed with SPEAR or anything above | |
| 106 // 1 - skeleton with spear. Can be killed with Spear but only if player has armor | |
| 107 // 2 - skeleton with bow. Does not move, just shoots. Can be killed with spear and armor | |
| 108 // 3 - skeletong with spear and armor. Cannot be killed | |
| 109 | |
| 110 function init_maze_draw_array() | |
| 111 { | |
| 112 var r = new Array(h); | |
| 113 for(var j=0; j<h; j++) | |
| 114 { | |
| 115 r[j] = new Array(w); | |
| 116 for(var i=0; i<w; i++) | |
| 117 { | |
| 118 r[j][i] = FULL_FOG; | |
| 119 } | |
| 120 } | |
| 121 return r; | |
| 122 } | |
| 123 | |
| 124 function update_step_visuals() | |
| 125 { | |
| 126 document.getElementById("steps_passed").innerHTML = " Steps: " + step_count.toString(); | |
| 127 } | |
| 128 | |
| 129 function update_life_visuals(current_life) | |
| 130 { | |
| 131 document.getElementById("energy").innerHTML = current_life.toString(); | |
| 132 document.getElementById("lifeprg").value = current_life; | |
| 133 } | |
| 134 | |
| 135 var dying_plyr_img_idx = 0; | |
| 136 var killer_igwidx = -1; | |
| 137 var dancing_kille_igw = 0; | |
| 138 var dancing_mod = 0; | |
| 139 var death_wall_drawn = false; | |
| 140 var skdc = 0; // skeleton dancer index, ie. the one rotating the skeleton | |
| 141 function player_dies() { | |
| 142 context.putImageData(imgDataPrev, x, y); // clear canvas | |
| 143 | |
| 144 if (dying_plyr_img_idx < 6) | |
| 145 { | |
| 146 | |
| 147 context.drawImage(dying_player[dying_plyr_img_idx], x, y); | |
| 148 dying_plyr_img_idx ++; | |
| 149 } | |
| 150 | |
| 151 if(killer_igwidx !== -1) { | |
| 152 if (!death_wall_drawn) { | |
| 153 draw_a_wall(row, col); | |
| 154 death_wall_drawn = true; | |
| 155 } | |
| 156 | |
| 157 dancing_mod ++; | |
| 158 context.drawImage(skel_dancer[skdc][dancing_kille_igw], x, y); | |
| 159 | |
| 160 if(dancing_mod % 3) | |
| 161 { | |
| 162 dancing_kille_igw++; | |
| 163 if(dancing_kille_igw === 6) | |
| 164 { | |
| 165 dancing_kille_igw = 0; | |
| 166 skdc ++; | |
| 167 if(skdc === 4) | |
| 168 { | |
| 169 skdc = 0; | |
| 170 } | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 requestAnimationFrame(player_dies); | |
| 176 } | |
| 177 | |
| 178 function kill_igw(i) | |
| 179 { | |
| 180 igw[i].state = DEAD; | |
| 181 var current_kills = parseInt(document.getElementById("skels_killed").innerHTML); | |
| 182 current_kills ++; | |
| 183 document.getElementById("skels_killed").innerHTML = current_kills.toString(); | |
| 184 } | |
| 185 | |
| 186 var go_texts=["Maybe you shouldn't have had eaten that mushroom.<p>Not all food is safe down here.", | |
| 187 "The <img src='/img/skel/wl/d_wl_1.png' title='... it is a spear ...' style='vertical-align:middle;'> is an easy prey when you have a <img src='/img/weapons/spear.png' title='... it is a spear ...' style='vertical-align:middle;'>.<p>Wearing <img src='/img/weapons/armor.png' title='... the chainmail ...' style='vertical-align:middle;'> also increases your chance of survival.<p>Otherwise ... you are easy prey to them.", | |
| 188 "You definitely should have eaten something on the way.<p>Finding the <img src='/img/weapons/shoe.png' title='Boots of Lightness' style='vertical-align:middle;'> and the <img src='/img/weapons/ring.png' title='Ring of Health' style='vertical-align:middle;'> also helps.", | |
| 189 leave_text | |
| 190 ]; | |
| 191 | |
| 192 | |
| 193 const GO_BAD_FOOD = 1; | |
| 194 const GO_SKELETON = 2; | |
| 195 const GO_FATIGUE = 3; | |
| 196 const GO_LEVEL_DONE = 4; | |
| 197 | |
| 198 // reason: 1 = player died since he ate something he wasn't supposed to | |
| 199 // 2 = player was killed by a skeleton | |
| 200 // 3 = fatigue | |
| 201 // 4 = player finished the current level | |
| 202 function game_over(reason, i) | |
| 203 { | |
| 204 if(reason === GO_SKELETON) | |
| 205 { | |
| 206 killer_igwidx = i; | |
| 207 context.putImageData(igw[killer_igwidx].savedImage, igw[killer_igwidx].x, igw[killer_igwidx].y); | |
| 208 } | |
| 209 document.getElementById("goimg").style.visibility = 'visible'; | |
| 210 | |
| 211 var final_message = go_texts[reason - 1]; | |
| 212 if(reason !== GO_LEVEL_DONE) | |
| 213 { | |
| 214 update_life_visuals(0); | |
| 215 | |
| 216 player_dies(); | |
| 217 draw_a_wall(row,col); | |
| 218 context.drawImage(dying_player[5], x, y); | |
| 219 | |
| 220 final_message += story_text; | |
| 221 document.getElementById("goimg").src ="/img/gameover.png"; | |
| 222 player_state = PLAYER_DIED; | |
| 223 } | |
| 224 else | |
| 225 { | |
| 226 document.getElementById('gonextlevel').style.visibility='visible'; | |
| 227 clearTimeout(time_passing_timeout); | |
| 228 } | |
| 229 | |
| 230 if(game_type === GAME_TYPE_TIMERUN) | |
| 231 { | |
| 232 final_message += "<p>" + document.getElementById("time_passed").innerHTML; | |
| 233 } | |
| 234 | |
| 235 document.getElementById("messages").style.visibility = 'visible'; | |
| 236 document.getElementById("messages").style.display = 'block'; | |
| 237 document.getElementById("gotext1").style.visibility = 'visible'; | |
| 238 document.getElementById("gotext1").innerHTML = final_message; | |
| 239 document.getElementById("gospan").style.visibility = 'hidden'; | |
| 240 | |
| 241 document.getElementById("sysmenu").style.visibility = 'hidden'; | |
| 242 | |
| 243 game_state = GAME_SUSPENDED; | |
| 244 } | |
| 245 | |
| 246 var maze_stats = init_maze_draw_array(); | |
| 247 | |
| 248 function point_in_circle(center_x, center_y, radius, x, y) | |
| 249 { | |
| 250 var D = Math.sqrt(Math.pow(center_x - x, 2) + Math.pow(center_y - y, 2)); | |
| 251 return D <= radius | |
| 252 } | |
| 253 | |
| 254 function draw_torches(next) | |
| 255 { | |
| 256 for(var i=0; i<torches.length; i++) { | |
| 257 if(next) { | |
| 258 torches[i].ctr ++; | |
| 259 } | |
| 260 if(torches[i].ctr % 5 === 0) | |
| 261 { | |
| 262 context.putImageData(torches[i].backgr, torches[i].x, torches[i].y); | |
| 263 | |
| 264 if(next) { | |
| 265 torches[i].anim_idx ++; | |
| 266 } | |
| 267 | |
| 268 if(torches[i].anim_idx === 3 && next) { | |
| 269 torches[i].anim_idx = 0; | |
| 270 } | |
| 271 context.drawImage(torch_images[torches[i].anim_idx], torches[i].x, torches[i].y); | |
| 272 } | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 function draw_a_wall(saver, savec) | |
| 277 { | |
| 278 if(maze_stats[saver][savec] === FULL_FOG) | |
| 279 { | |
| 280 return; | |
| 281 } | |
| 282 | |
| 283 if(maze_stats[saver][savec] === FULL_DRAWN) | |
| 284 { | |
| 285 var current_wall = wall_images[ maze[saver][savec] ]; | |
| 286 // Any door here? | |
| 287 let door_t = door_at(savec, saver); | |
| 288 if( door_t !== -1 ) | |
| 289 { | |
| 290 current_wall = get_doored_wall(maze[saver][savec], door_t); | |
| 291 } | |
| 292 context.drawImage(current_wall, savec * pic_size, saver * pic_size); | |
| 293 draw_extra_objects(saver, savec, savec * pic_size, saver * pic_size); | |
| 294 } | |
| 295 else | |
| 296 { | |
| 297 context.drawImage(fog_drawn_wall_images[ maze[saver][savec] ], | |
| 298 savec * pic_size, saver * pic_size); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 // The torch animations | |
| 303 var torches = []; | |
| 304 | |
| 305 var top_btm_chooser = []; | |
| 306 top_btm_chooser[0] = top_closed_doors; | |
| 307 top_btm_chooser[1] = bottom_closed_doors; | |
| 308 | |
| 309 var small_fellow = new Image(); | |
| 310 small_fellow.src = '/img/funnyfellow.png'; | |
| 311 | |
| 312 // index is the number of the cell we are looking for, type is whether the door is on | |
| 313 // the top part of the cell (0) or the bottom part (1) | |
| 314 function get_doored_wall(index, type) | |
| 315 { | |
| 316 if(type !== 5) | |
| 317 { | |
| 318 for(var i=0; i<top_btm_chooser[type].length; i++) | |
| 319 { | |
| 320 if(top_btm_chooser[type][i].idx === index) | |
| 321 { | |
| 322 return top_btm_chooser[type][i].img; | |
| 323 } | |
| 324 } | |
| 325 return wall_images[ index ]; | |
| 326 } | |
| 327 else | |
| 328 { | |
| 329 return stairs_down[index]; | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 // returns the torch type number if we have a torch at this location | |
| 334 function have_torch_here(ii, jj) | |
| 335 { | |
| 336 for(var i=0; i<torch_placements.length; i++) | |
| 337 { | |
| 338 if(torch_placements[i].i === ii && torch_placements[i].j === jj) | |
| 339 { | |
| 340 return torch_placements[i].p | |
| 341 } | |
| 342 } | |
| 343 return 0; | |
| 344 } | |
| 345 | |
| 346 // returns the food type number if we have a food item at this location | |
| 347 function have_food_here(r, c) | |
| 348 { | |
| 349 for(var i=0; i<food.length; i++) | |
| 350 { | |
| 351 if(food[i].r === r && food[i].c === c) | |
| 352 { | |
| 353 return food[i].t; | |
| 354 } | |
| 355 } | |
| 356 return -1; | |
| 357 } | |
| 358 | |
| 359 function have_loot_here(r, c) | |
| 360 { | |
| 361 for(var i=0; i<loot.length; i++) | |
| 362 { | |
| 363 if(loot[i].r === r && loot[i].c === c) | |
| 364 { | |
| 365 return loot[i].t; | |
| 366 } | |
| 367 } | |
| 368 return -1; | |
| 369 } | |
| 370 | |
| 371 function draw_food(x, y, t) | |
| 372 { | |
| 373 context.drawImage(food_image[t], x + (pic_size - 24) / 2 - food_image[t].clientWidth / 2, | |
| 374 y + (pic_size -24)/ 2 - food_image[t].clientHeight / 2); | |
| 375 } | |
| 376 | |
| 377 function draw_loot(x, y, t) | |
| 378 { | |
| 379 context.drawImage(item_image[t], x + (pic_size - 24) / 2 - item_image[t].clientWidth / 2, | |
| 380 y + (pic_size -24)/ 2 - item_image[t].clientHeight / 2); | |
| 381 } | |
| 382 | |
| 383 // returns the food type number if we have a food item at this location | |
| 384 function have_weapon_here(r, c) | |
| 385 { | |
| 386 for(var i=0; i<weapon_locations.length; i++) | |
| 387 { | |
| 388 if(weapon_locations[i].r === r && weapon_locations[i].c === c) | |
| 389 { | |
| 390 return weapon_locations[i].t; | |
| 391 } | |
| 392 } | |
| 393 return -1; | |
| 394 } | |
| 395 | |
| 396 function draw_weapon(x, y, t) | |
| 397 { | |
| 398 context.drawImage(weapon_images[t], x + (pic_size - 24) / 2 - weapon_images[t].clientWidth / 2, | |
| 399 y + (pic_size -24)/ 2 - weapon_images[t].clientHeight / 2); | |
| 400 } | |
| 401 | |
| 402 function draw_torch(torch_x, torch_y, i, j, cond) | |
| 403 { | |
| 404 if(cond) | |
| 405 { | |
| 406 var torch_backgr = context.getImageData(torch_x, torch_y, 6, 10); | |
| 407 context.drawImage(torch_images[0], torch_x, torch_y); | |
| 408 torches.push({x: torch_x, y: torch_y, anim_idx: 0, ctr: 0, i: i, j: j, backgr: torch_backgr}); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 function door_at(i, j) | |
| 413 { | |
| 414 for(var k=0; k<doors.length; k++) | |
| 415 if(doors[k].r === j && doors[k].c === i) | |
| 416 return doors[k].v; | |
| 417 return -1; | |
| 418 } | |
| 419 | |
| 420 // true if we are just drawing a maze | |
| 421 window.maze_drawing = false; | |
| 422 | |
| 423 // true if we are updating the walkers. Don't move the playuer meanwhile | |
| 424 window.walkers_updating = false; | |
| 425 | |
| 426 function draw_extra_objects(j, i, mx, my) | |
| 427 { | |
| 428 var food_type = have_food_here(j, i); | |
| 429 if (food_type !== -1) { | |
| 430 draw_food(mx, my, food_type); | |
| 431 } | |
| 432 | |
| 433 var loot_type = have_loot_here(j, i); | |
| 434 if (loot_type !== -1) { | |
| 435 draw_loot(mx, my, loot_type); | |
| 436 } | |
| 437 | |
| 438 var weapon_type = have_weapon_here(j, i); | |
| 439 if (weapon_type !== -1) { | |
| 440 draw_weapon(mx, my, weapon_type); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 var small_fellow_needs_to_be_drawn = true; | |
| 445 if(game_type !== GAME_TYPE_STORY) | |
| 446 { | |
| 447 small_fellow_needs_to_be_drawn = false; | |
| 448 } | |
| 449 var ffx = 0; // funny fellow x,y | |
| 450 var ffy = 0; | |
| 451 var ffdx = 0; // funny fellow deltax,deltay | |
| 452 var ffdy = 0; | |
| 453 var ffImgDataPrev = null; | |
| 454 | |
| 455 function draw_maze(mid_col, mid_row, force) | |
| 456 { | |
| 457 window.maze_drawing = true; | |
| 458 var mx = 0; | |
| 459 var my = 0; | |
| 460 | |
| 461 // var strow = Math.max(mid_row - LIGHT_RADIUS - 1, 0); | |
| 462 // var stcol = Math.max(mid_col - LIGHT_RADIUS - 1, 0); | |
| 463 for(var j=0; j<h; j++) // j - row | |
| 464 // for(var j=strow; j<mid_row + LIGHT_RADIUS + 1; j++) // j - row | |
| 465 { | |
| 466 // for (var i = stcol; i < mid_col + LIGHT_RADIUS + 1; i++) // i - col | |
| 467 for (var i = 0; i <w; i++) // i - col | |
| 468 { | |
| 469 mx = i * pic_size; | |
| 470 my = j * pic_size; | |
| 471 | |
| 472 var current_idx = maze[j][i]; | |
| 473 var current_wall = wall_images[ current_idx ]; | |
| 474 | |
| 475 if( point_in_circle(mid_col, mid_row, LIGHT_RADIUS, i, j) ) | |
| 476 { | |
| 477 if( maze_stats[j][i] !== FULL_DRAWN || force) | |
| 478 { | |
| 479 maze_stats[j][i] = FULL_DRAWN; | |
| 480 | |
| 481 var door_t = door_at(i, j); | |
| 482 | |
| 483 // Any door here? | |
| 484 if(door_t !== -1) | |
| 485 { | |
| 486 current_wall = get_doored_wall(current_idx, door_t); | |
| 487 } | |
| 488 context.drawImage(current_wall, mx, my); | |
| 489 | |
| 490 // let's see if this is the last cell, ie. stairs down | |
| 491 if(door_t === 5 && small_fellow_needs_to_be_drawn) | |
| 492 { | |
| 493 //1,8,9 | |
| 494 ffx = mx + 15; | |
| 495 ffy = my + 15; | |
| 496 if(current_idx === 1 || current_idx === 9) // small guy will stand above the stairs | |
| 497 { | |
| 498 ffy = ffy - pic_size; | |
| 499 ffdy = 1; | |
| 500 } | |
| 501 else // small guy will stand on the left of it | |
| 502 { | |
| 503 ffx = ffx - pic_size; | |
| 504 ffdx = 1; | |
| 505 } | |
| 506 ffImgDataPrev = context.getImageData(ffx,ffy, anim_size, anim_size); | |
| 507 context.drawImage(small_fellow, ffx, ffy); | |
| 508 } | |
| 509 | |
| 510 // Torches lying around the corner? | |
| 511 var torch_location_in_cell = have_torch_here(i,j); | |
| 512 if(torch_location_in_cell !== 0) | |
| 513 { | |
| 514 draw_torch(mx, my, i, j, torch_location_in_cell & TOP_LEFT); | |
| 515 draw_torch(mx, my + pic_size - 22, i, j, torch_location_in_cell & BOTTOM_LEFT); | |
| 516 draw_torch(mx+ pic_size - 10, my, i, j, torch_location_in_cell & TOP_RIGHT); | |
| 517 draw_torch(mx + pic_size - 10, my + pic_size - 22, i, j, torch_location_in_cell & BOTTOM_RIGHT); | |
| 518 } | |
| 519 draw_extra_objects(j, i, mx, my); | |
| 520 } | |
| 521 } | |
| 522 else | |
| 523 { | |
| 524 if(point_in_circle(mid_col, mid_row, LIGHT_RADIUS + 2, i, j) ) | |
| 525 { | |
| 526 if(maze_stats[j][i] === FULL_DRAWN || (maze_stats[j][i] === FOG_DRAWN && force) ) | |
| 527 { | |
| 528 context.drawImage(fog_drawn_wall_images[ current_idx ], mx, my); | |
| 529 maze_stats[j][i] = FOG_DRAWN; | |
| 530 // and remove the torch if found at i,j | |
| 531 for(var ti=0; ti < torches.length; ti++) | |
| 532 { | |
| 533 if(torches[ti].i === i && torches[ti].j === j) | |
| 534 { | |
| 535 torches.splice(ti, 1); | |
| 536 } | |
| 537 } | |
| 538 } | |
| 539 } | |
| 540 } | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 window.maze_drawing = false; | |
| 545 } | |
| 546 | |
| 547 // true if we are in between animation of the player | |
| 548 // Will be "window.animation_running" in scripts | |
| 549 var animation_running = false; | |
| 550 | |
| 551 // the current animation counter | |
| 552 var current_anim_counter = 0; | |
| 553 | |
| 554 // the direction in which the player is going, indexes into the array of images | |
| 555 var player_dir = MOVE_IDX_RIGHT; // 0 - Left, 1 - Up, 2 - Right, 3 - Down | |
| 556 | |
| 557 // The size of the animation frame, ie. what must be saved from the canvas | |
| 558 var anim_size = pic_size / 2; | |
| 559 | |
| 560 var player_imgset = [lr_player, lr_player_spear, lr_player_armor_spear, | |
| 561 lr_player_armor, lr_player_shoes, lr_player_shoes_spear, // +6 will give the thrust images | |
| 562 lr_player_thrust, lr_player_spear_thrust, lr_player_armor_spear_thrust, | |
| 563 lr_player_armor_thrust, lr_player_shoes_thrust, lr_player_shoes_spear_thrust | |
| 564 ]; | |
| 565 var player_imgset_index = 0; | |
| 566 | |
| 567 // returns the current player image according to | |
| 568 // direction and current animation counter | |
| 569 var anim_modder = step_anim_cnt; | |
| 570 function get_current_player_image(dir) | |
| 571 { | |
| 572 return player_imgset[player_imgset_index][dir][current_anim_counter % anim_modder]; | |
| 573 } | |
| 574 | |
| 575 function get_direction(c,fwd) { | |
| 576 if(fwd === 1) | |
| 577 { | |
| 578 if(c === 'N') return N; | |
| 579 if(c === 'S') return S; | |
| 580 if(c === 'E') return E; | |
| 581 if(c === 'W') return W; | |
| 582 } | |
| 583 else | |
| 584 { | |
| 585 if(c === 'N') return S; | |
| 586 if(c === 'S') return N; | |
| 587 if(c === 'E') return W; | |
| 588 if(c === 'W') return E; | |
| 589 } | |
| 590 return 0; | |
| 591 } | |
| 592 | |
| 593 function get_dir_idx(dir) | |
| 594 { | |
| 595 if(dir === W) return MOVE_IDX_LEFT; | |
| 596 if(dir === N) return MOVE_IDX_UP; | |
| 597 if(dir === E) return MOVE_IDX_RIGHT; | |
| 598 if(dir === S) return MOVE_IDX_DOWN; | |
| 599 return -1; | |
| 600 } | |
| 601 | |
| 602 const WALKING = 1; // IGW normally goes around | |
| 603 const DEAD = 5; // igw was killed | |
| 604 | |
| 605 // in game walkers | |
| 606 var igw = []; | |
| 607 | |
| 608 | |
| 609 function update_igw_location(i) { | |
| 610 switch(igw[i].dir) { | |
| 611 case N: | |
| 612 igw[i].r --; | |
| 613 break; | |
| 614 case E: | |
| 615 igw[i].c ++; | |
| 616 break; | |
| 617 case S: | |
| 618 igw[i].r ++; | |
| 619 break; | |
| 620 case W: | |
| 621 igw[i].c --; | |
| 622 break; | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 function init_walkers() { | |
| 627 for(var i=0; i<walkers_count; i++) { | |
| 628 var wx = walkers[i].c * pic_size + 12; | |
| 629 var wy = walkers[i].r * pic_size + 12; | |
| 630 | |
| 631 var cwalker = { | |
| 632 r:walkers[i].r, | |
| 633 c:walkers[i].c, | |
| 634 saver:-1, | |
| 635 savec:-1, | |
| 636 t:walkers[i].t, | |
| 637 p:walkers[i].p, | |
| 638 dir:get_direction(walkers[i].p[0], 1), | |
| 639 anim_ctr:0, | |
| 640 step_cnt:9, | |
| 641 x:wx, | |
| 642 y:wy, | |
| 643 savedImage:null, | |
| 644 path_ctr:0, | |
| 645 path_len:walkers[i].path_len, | |
| 646 move_dir:1, | |
| 647 state:WALKING, | |
| 648 just_updated: false, | |
| 649 anim_mod:true, | |
| 650 }; | |
| 651 | |
| 652 igw.push(cwalker); | |
| 653 } | |
| 654 | |
| 655 // Now patch the rows and cols for the walker to reflect the first step | |
| 656 for(i=0; i<walkers_count; i++) { | |
| 657 update_igw_location(i); | |
| 658 } | |
| 659 } | |
| 660 | |
| 661 function move_igw(i) { | |
| 662 switch(igw[i].dir) { | |
| 663 case N: | |
| 664 igw[i].y --; | |
| 665 return; | |
| 666 case E: | |
| 667 igw[i].x ++; | |
| 668 return; | |
| 669 case W: | |
| 670 igw[i].x --; | |
| 671 return; | |
| 672 case S: | |
| 673 igw[i].y ++; | |
| 674 return; | |
| 675 } | |
| 676 igw[i].just_updated = true; | |
| 677 } | |
| 678 | |
| 679 function can_go(fromrow, fromcol, pxd, pyd) | |
| 680 { | |
| 681 if(fromrow < 0) fromrow = 0; | |
| 682 if(fromcol < 0) fromcol = 0; | |
| 683 | |
| 684 var cell = maze[fromrow][fromcol]; | |
| 685 var cango = 0; | |
| 686 if(pyd === 1) cango = cell & S; | |
| 687 if(pyd === -1) cango = cell & N; | |
| 688 if(pxd === 1) cango = cell & E; | |
| 689 if(pxd === -1) cango = cell & W; | |
| 690 | |
| 691 return cango !== 0; | |
| 692 } | |
| 693 | |
| 694 function igw_touches_player(i, dist_comp) | |
| 695 { | |
| 696 var dc = (Math.abs(igw[i].x -x) < 5 && Math.abs(igw[i].y -y)<5 ); | |
| 697 if(dist_comp) | |
| 698 { | |
| 699 return dc; | |
| 700 } | |
| 701 return (( ( igw[i].r === row && igw[i].c === col && igw[i].anim_ctr >= pic_size / 2) | |
| 702 || (igw[i].saver === row && igw[i].savec === col) | |
| 703 )) | |
| 704 || dc; | |
| 705 } | |
| 706 | |
| 707 // Checks whether the player has a corresponding weapon which he can use to kill igw[i]. | |
| 708 // igw[i].t is the type of the igw and if the player weapon > igw[i].t then it can kill. | |
| 709 function player_has_corresponding_weapon(i) | |
| 710 { | |
| 711 // Fully equipped player will kill everything | |
| 712 if (player_armor === ARMOR_CHAINMAIL && player_weapon === WEAPON_SPEAR) | |
| 713 { | |
| 714 return true; | |
| 715 } | |
| 716 // player with spear will kill simple skeletons | |
| 717 if(player_weapon === WEAPON_SPEAR && igw[i].t === 0) | |
| 718 { | |
| 719 return true; | |
| 720 } | |
| 721 return (player_weapon >= igw[i].t); | |
| 722 } | |
| 723 | |
| 724 function player_has_armor() | |
| 725 { | |
| 726 return player_armor === ARMOR_CHAINMAIL; | |
| 727 } | |
| 728 | |
| 729 var anim_mod = 0; | |
| 730 | |
| 731 function resolve_weapon(weapon_here) { | |
| 732 if (weapon_here === RING) // ring, will stop the time | |
| 733 { | |
| 734 time_effect = TIME_HAS_NO_EFFECT; | |
| 735 document.getElementById("ring_of_health_div").style.visibility = 'visible'; | |
| 736 } | |
| 737 else if (weapon_here === SHOES) // shoes | |
| 738 { | |
| 739 if (player_armor === ARMOR_NONE) // player has no armor, images shoudl be lr_player_shoes | |
| 740 { | |
| 741 if(player_weapon === WEAPON_SPEAR) | |
| 742 { | |
| 743 player_imgset_index = 5; | |
| 744 } | |
| 745 else | |
| 746 { | |
| 747 player_imgset_index = 4; | |
| 748 } | |
| 749 } | |
| 750 player_steps = STEPS_FREE; | |
| 751 document.getElementById("boots_of_light_div").style.visibility = 'visible'; | |
| 752 LIGHT_RADIUS = 5; | |
| 753 } | |
| 754 else if (weapon_here === WEAPON_SPEAR) // spear | |
| 755 { | |
| 756 player_weapon = WEAPON_SPEAR; | |
| 757 if (player_armor === ARMOR_NONE) // player has no armor, images shoudl be lr_player_spear | |
| 758 { | |
| 759 if(player_steps === STEPS_FREE) // Are there shoes on the player? | |
| 760 { | |
| 761 player_imgset_index = 5; | |
| 762 } | |
| 763 else | |
| 764 { | |
| 765 player_imgset_index = 1; | |
| 766 } | |
| 767 } | |
| 768 else if (player_armor === ARMOR_CHAINMAIL) // player has armor, images should be lr_player_armor_spear | |
| 769 { | |
| 770 player_imgset_index = 2; | |
| 771 } | |
| 772 } | |
| 773 else if (weapon_here === ARMOR_CHAINMAIL) // found the armor | |
| 774 { | |
| 775 player_armor = ARMOR_CHAINMAIL; | |
| 776 if (player_weapon === WEAPON_SPEAR) { | |
| 777 player_imgset_index = 2; | |
| 778 } | |
| 779 else if (player_weapon === WEAPON_NONE) { | |
| 780 player_imgset_index = 3; | |
| 781 } | |
| 782 } | |
| 783 } | |
| 784 | |
| 785 function resolve_loot(t) | |
| 786 { | |
| 787 var loot_value = item_value[t]; | |
| 788 var current_gold = parseInt(document.getElementById("gold").innerHTML); | |
| 789 current_gold += loot_value; | |
| 790 document.getElementById("gold").innerHTML = current_gold.toString(); | |
| 791 } | |
| 792 | |
| 793 function igw_kills_player(i) | |
| 794 { | |
| 795 if (!player_has_corresponding_weapon(i)) | |
| 796 { | |
| 797 if(player_has_armor()) | |
| 798 { | |
| 799 let current_life = parseInt(document.getElementById("energy").innerHTML); | |
| 800 let temp_health = current_life - 1; | |
| 801 | |
| 802 if (temp_health <= 0) | |
| 803 { | |
| 804 game_over(GO_SKELETON, i); | |
| 805 return true; | |
| 806 } | |
| 807 update_life_visuals(temp_health); | |
| 808 return false; | |
| 809 } | |
| 810 else | |
| 811 { | |
| 812 game_over(GO_SKELETON, i); | |
| 813 return true; | |
| 814 } | |
| 815 } | |
| 816 else | |
| 817 { | |
| 818 if(igw_touches_player(i, true)) { | |
| 819 kill_igw(i); | |
| 820 } | |
| 821 return false; | |
| 822 } | |
| 823 | |
| 824 } | |
| 825 | |
| 826 var small_fellow_anim_idx = 0; | |
| 827 var small_fellow_anim_runs = false; | |
| 828 | |
| 829 function walkers_simulation() | |
| 830 { | |
| 831 if(player_state === PLAYER_DIED) | |
| 832 { | |
| 833 context.drawImage(dying_player[5], x, y); | |
| 834 return; | |
| 835 } | |
| 836 | |
| 837 if(game_state === GAME_SUSPENDED) | |
| 838 { | |
| 839 return; | |
| 840 } | |
| 841 | |
| 842 if(small_fellow_anim_runs && small_fellow_anim_idx < pic_size) | |
| 843 { | |
| 844 if(ffImgDataPrev) | |
| 845 { | |
| 846 context.putImageData(ffImgDataPrev, ffx, ffy); // clear canvas | |
| 847 } | |
| 848 | |
| 849 small_fellow_anim_idx ++; | |
| 850 ffx += ffdx; | |
| 851 ffy += ffdy; | |
| 852 | |
| 853 ffImgDataPrev = context.getImageData(ffx,ffy, anim_size, anim_size); | |
| 854 | |
| 855 context.drawImage(small_fellow, ffx, ffy); | |
| 856 | |
| 857 } | |
| 858 | |
| 859 // moving player drawing | |
| 860 if(animation_running) | |
| 861 { | |
| 862 if(!walkers_updating) | |
| 863 { | |
| 864 context.putImageData(imgDataPrev, x, y); // clear canvas | |
| 865 x += x_delta; | |
| 866 y += y_delta; | |
| 867 anim_mod = anim_mod + 1; | |
| 868 if (anim_mod % 4 === 0) current_anim_counter += 1; | |
| 869 if (current_anim_counter < pic_size / 4) { | |
| 870 imgDataPrev = context.getImageData(x, y, anim_size, anim_size); | |
| 871 } | |
| 872 else { | |
| 873 animation_running = false; | |
| 874 current_anim_counter = 0; | |
| 875 | |
| 876 // and here verify what is on the square we arrived to | |
| 877 | |
| 878 // 1. See did we find the exit? | |
| 879 if (col === exit_col && row === exit_row) { | |
| 880 game_over(GO_LEVEL_DONE, -1); | |
| 881 return; | |
| 882 } | |
| 883 | |
| 884 // 2. Any foodstuff or other here? | |
| 885 var food_type_here = have_food_here(row, col); | |
| 886 var maze_redraw_needed = false; | |
| 887 if (food_type_here !== -1) { | |
| 888 var current_life = parseInt(document.getElementById("energy").innerHTML); | |
| 889 var temp_health = current_life + food_effects[food_type_here]; | |
| 890 remove_food(row, col); | |
| 891 maze_redraw_needed = true; | |
| 892 if (temp_health > 100) { | |
| 893 temp_health = 100; | |
| 894 } | |
| 895 if (temp_health <= 0) { | |
| 896 game_over(GO_BAD_FOOD, -1); | |
| 897 return; | |
| 898 } | |
| 899 | |
| 900 update_life_visuals(temp_health); | |
| 901 } | |
| 902 | |
| 903 // Are we next to the small guy with a visible line? | |
| 904 if( row > h - 4 && col > w - 4 && game_type === GAME_TYPE_STORY) | |
| 905 { | |
| 906 small_fellow_needs_to_be_drawn = false; | |
| 907 maze_redraw_needed = true; | |
| 908 small_fellow_anim_runs = true; | |
| 909 } | |
| 910 | |
| 911 // see if there is a wepon there | |
| 912 var weapon_here = have_weapon_here(row, col); | |
| 913 if (weapon_here !== -1) | |
| 914 { | |
| 915 resolve_weapon(weapon_here); | |
| 916 remove_weapon(row, col); | |
| 917 maze_redraw_needed = true; | |
| 918 } | |
| 919 | |
| 920 // if this is an adventure game, see if there is any loot here | |
| 921 if(game_type === GAME_TYPE_ADVENTURE) | |
| 922 { | |
| 923 // see if there is a wepon there | |
| 924 var loot_here = have_loot_here(row, col); | |
| 925 if (loot_here !== -1) | |
| 926 { | |
| 927 resolve_loot(loot_here); | |
| 928 remove_loot(row, col); | |
| 929 maze_redraw_needed = true; | |
| 930 } | |
| 931 } | |
| 932 | |
| 933 if (maze_redraw_needed) | |
| 934 { | |
| 935 draw_maze(col, row, true); | |
| 936 imgDataPrev = context.getImageData(x, y, anim_size, anim_size); | |
| 937 } | |
| 938 | |
| 939 // see if this was a hit operation or not | |
| 940 if(anim_modder === thrust_anim_cnt) | |
| 941 { | |
| 942 // update the image set for the walking | |
| 943 anim_modder = step_anim_cnt; | |
| 944 player_imgset_index -= 6; | |
| 945 } | |
| 946 } | |
| 947 context.drawImage(get_current_player_image(player_dir), x, y); | |
| 948 } | |
| 949 } | |
| 950 | |
| 951 draw_torches(true); | |
| 952 | |
| 953 window.walkers_updating = true; | |
| 954 var drawn_walkers = []; | |
| 955 | |
| 956 for (var i = 0; i < igw.length; i++) { | |
| 957 if (igw[i].state === DEAD) { | |
| 958 context.drawImage(skel_dies[5], igw[i].x, igw[i].y); | |
| 959 continue; | |
| 960 } | |
| 961 | |
| 962 igw[i].anim_mod = !igw[i].anim_mod; | |
| 963 if (igw[i].anim_mod) igw[i].anim_ctr++; | |
| 964 igw[i].just_updated = false; | |
| 965 | |
| 966 igw[i].saver = igw[i].r; | |
| 967 igw[i].savec = igw[i].c; | |
| 968 | |
| 969 if (igw_touches_player(i,false)) | |
| 970 { | |
| 971 if(igw_kills_player(i)) | |
| 972 { | |
| 973 return; | |
| 974 } | |
| 975 } | |
| 976 | |
| 977 if (igw[i].anim_ctr > pic_size / 2 - 1) { | |
| 978 // means we have stepped over the current tile, time to advance to the next one | |
| 979 igw[i].path_ctr = igw[i].path_ctr + igw[i].move_dir; | |
| 980 // console.log("igw[",i,'].path_ctr=',igw[i].path_ctr,' path_len=',igw[i].path_len); | |
| 981 if (igw[i].path_ctr === igw[i].path_len || igw[i].path_ctr < 0) { | |
| 982 // just perform a routine check that the last step of the igw does not kill the player | |
| 983 var tempr = igw[i].r; | |
| 984 var tempc = igw[i].c; | |
| 985 update_igw_location(i); | |
| 986 if (igw_touches_player(i,false)) | |
| 987 { | |
| 988 if(igw_kills_player(i)) | |
| 989 { | |
| 990 return; | |
| 991 } | |
| 992 } | |
| 993 //draw_a_wall(igw[i].r, igw[i].c); | |
| 994 | |
| 995 igw[i].r = tempr; | |
| 996 igw[i].c = tempc; | |
| 997 | |
| 998 // this reverses the igw | |
| 999 igw[i].move_dir = -igw[i].move_dir; | |
| 1000 igw[i].path_ctr --; | |
| 1001 if(igw[i].path_ctr < 0) | |
| 1002 { | |
| 1003 igw[i].path_ctr = 0; | |
| 1004 } | |
| 1005 } | |
| 1006 | |
| 1007 igw[i].dir = get_direction(igw[i].p[igw[i].path_ctr], igw[i].move_dir); | |
| 1008 update_igw_location(i); | |
| 1009 igw[i].anim_ctr = 0; | |
| 1010 } | |
| 1011 | |
| 1012 if (maze_stats[igw[i].r][igw[i].c] === FULL_DRAWN && igw[i].savedImage === null) | |
| 1013 { | |
| 1014 igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); | |
| 1015 } | |
| 1016 else | |
| 1017 { | |
| 1018 if (maze_stats[igw[i].r][igw[i].c] === FOG_DRAWN) { | |
| 1019 context.drawImage(fog_drawn_wall_images[maze[igw[i].r][igw[i].c]], igw[i].c * pic_size, igw[i].r * pic_size); | |
| 1020 } | |
| 1021 | |
| 1022 // do we move away from a full drawn cell? | |
| 1023 if (maze_stats[igw[i].saver][igw[i].savec] === FULL_DRAWN) { | |
| 1024 draw_a_wall(igw[i].saver, igw[i].savec); | |
| 1025 } | |
| 1026 if (igw[i].savedImage === null) { | |
| 1027 igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); | |
| 1028 } | |
| 1029 | |
| 1030 } | |
| 1031 | |
| 1032 // restore the background | |
| 1033 if( maze_stats[igw[i].r][igw[i].c] === FULL_DRAWN && igw[i].savedImage !== null && maze_stats[igw[i].saver][igw[i].savec] === FULL_DRAWN ) | |
| 1034 { | |
| 1035 draw_a_wall(igw[i].r, igw[i].c); | |
| 1036 // draw_extra_objects(igw[i].r, igw[i].c, igw[i].r * pic_size, igw[i].c * pic_size) | |
| 1037 context.putImageData(igw[i].savedImage, igw[i].x, igw[i].y); | |
| 1038 | |
| 1039 // draw only torches which are being affected | |
| 1040 for (var k = 0; k < torches.length; k++) { | |
| 1041 if (torches[k].i === igw[i].c && torches[k].j === igw[i].r) { | |
| 1042 context.drawImage(torch_images[torches[k].anim_idx], torches[k].x, torches[k].y); | |
| 1043 } | |
| 1044 } | |
| 1045 } | |
| 1046 | |
| 1047 // move the walker on the screen | |
| 1048 move_igw(i); | |
| 1049 | |
| 1050 if (maze_stats[igw[i].r][igw[i].c] === FULL_DRAWN) { | |
| 1051 igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); | |
| 1052 let dir_idx = get_dir_idx(igw[i].dir); | |
| 1053 let ani_idx = igw[i].anim_ctr % igw[i].step_cnt; | |
| 1054 // console.log('igw[i].dir',igw[i].dir, 'dir_idx',dir_idx,'ani_idx',ani_idx); | |
| 1055 context.drawImage(walker_images[igw[i].t][dir_idx][ani_idx], igw[i].x, igw[i].y); | |
| 1056 drawn_walkers.push({ | |
| 1057 img: walker_images[igw[i].t][dir_idx][igw[i].anim_ctr % igw[i].step_cnt], | |
| 1058 x: igw[i].x, | |
| 1059 y: igw[i].y, | |
| 1060 r: igw[i].r, | |
| 1061 c: igw[i].c | |
| 1062 }); | |
| 1063 } | |
| 1064 } | |
| 1065 | |
| 1066 // and just draw again all the walkers that were drawn | |
| 1067 if (drawn_walkers.length > 0) { | |
| 1068 for (i = 0; i < drawn_walkers.length; i++) { | |
| 1069 if (maze_stats[drawn_walkers[i].r][drawn_walkers[i].c] === FULL_DRAWN && | |
| 1070 maze_stats[igw[i].saver][igw[i].savec] === FULL_DRAWN) { | |
| 1071 context.drawImage(drawn_walkers[i].img, drawn_walkers[i].x, drawn_walkers[i].y); | |
| 1072 } | |
| 1073 } | |
| 1074 // and the player | |
| 1075 context.drawImage(get_current_player_image(player_dir), x, y); | |
| 1076 } | |
| 1077 window.walkers_updating = false; | |
| 1078 | |
| 1079 requestAnimationFrame(walkers_simulation); | |
| 1080 } | |
| 1081 | |
| 1082 // previous image which was placed on the canvas | |
| 1083 var imgDataPrev = null; | |
| 1084 | |
| 1085 | |
| 1086 /* Mouse scroll handling */ | |
| 1087 // The canvas will scroll back to these coordinates when done with mouse move | |
| 1088 var marginLeftBackup = 0; | |
| 1089 var marginTopBackup = 0; | |
| 1090 var dragging = false; | |
| 1091 var lastX = 0; | |
| 1092 var lastY = 0; | |
| 1093 var marginLeft = 0; | |
| 1094 var marginTop = 0; | |
| 1095 // mouse press handler | |
| 1096 function mouse_press(e) | |
| 1097 { | |
| 1098 var evt = e || event; | |
| 1099 dragging = true; | |
| 1100 lastX = evt.clientX; | |
| 1101 lastY = evt.clientY; | |
| 1102 marginLeftBackup = canvas.style.marginLeft; | |
| 1103 marginTopBackup = canvas.style.marginTop; | |
| 1104 e.preventDefault(); | |
| 1105 } | |
| 1106 | |
| 1107 function mouse_move(e) | |
| 1108 { | |
| 1109 var evt = e || event; | |
| 1110 if (dragging) { | |
| 1111 var deltaX = evt.clientX - lastX; | |
| 1112 lastX = evt.clientX; | |
| 1113 | |
| 1114 if(marginLeft + deltaX <= 0 && canvas.width - Math.abs(marginLeft + deltaX) >= canvas_container.offsetWidth) // did we scroll out left/right | |
| 1115 { | |
| 1116 marginLeft += deltaX; | |
| 1117 canvas.style.marginLeft = marginLeft + "px"; | |
| 1118 } | |
| 1119 | |
| 1120 var deltaY = evt.clientY - lastY; | |
| 1121 lastY = evt.clientY; | |
| 1122 if(marginTop + deltaY <= 0 && canvas.height - Math.abs(marginTop + deltaY) >= canvas_container.offsetHeight) | |
| 1123 { | |
| 1124 marginTop += deltaY; | |
| 1125 canvas.style.marginTop = marginTop + "px"; | |
| 1126 } | |
| 1127 } | |
| 1128 e.preventDefault(); | |
| 1129 } | |
| 1130 | |
| 1131 function mouse_up() { | |
| 1132 dragging = false; | |
| 1133 canvas.style.marginLeft = marginLeftBackup; | |
| 1134 canvas.style.marginTop = marginTopBackup; | |
| 1135 } | |
| 1136 | |
| 1137 var canv_anim_counter = 0; | |
| 1138 var canv_moving_direction_ud = 0; | |
| 1139 function animate_canvas_movement_up_down() { | |
| 1140 canv_anim_counter += 1; | |
| 1141 marginTop += canv_moving_direction_ud; | |
| 1142 if (canv_anim_counter < pic_size) | |
| 1143 { | |
| 1144 requestAnimationFrame(animate_canvas_movement_up_down); | |
| 1145 } | |
| 1146 else | |
| 1147 { | |
| 1148 canv_anim_counter = 0; | |
| 1149 } | |
| 1150 if(canv_moving_direction_ud === -1) | |
| 1151 { | |
| 1152 if(canvas.height - Math.abs(marginTop) >= canvas_container.offsetHeight) | |
| 1153 { | |
| 1154 canvas.style.marginTop = marginTop + "px"; | |
| 1155 } | |
| 1156 } | |
| 1157 else | |
| 1158 { | |
| 1159 if(marginTop <= 0) | |
| 1160 { | |
| 1161 canvas.style.marginTop = marginTop + "px"; | |
| 1162 } | |
| 1163 } | |
| 1164 } | |
| 1165 | |
| 1166 var canv_moving_direction_lr = 0; | |
| 1167 function animate_canvas_movement_left_right() { | |
| 1168 canv_anim_counter += 1; | |
| 1169 marginLeft += canv_moving_direction_lr; | |
| 1170 if (canv_anim_counter < pic_size) | |
| 1171 { | |
| 1172 requestAnimationFrame(animate_canvas_movement_left_right); | |
| 1173 } | |
| 1174 else | |
| 1175 { | |
| 1176 canv_anim_counter = 0; | |
| 1177 } | |
| 1178 if(canv_moving_direction_lr === -1) | |
| 1179 { | |
| 1180 if(canvas.width - Math.abs(marginLeft) >= canvas_container.offsetWidth) | |
| 1181 { | |
| 1182 canvas.style.marginLeft = marginLeft + "px"; | |
| 1183 } | |
| 1184 } | |
| 1185 else | |
| 1186 { | |
| 1187 if(marginLeft <= 0) | |
| 1188 { | |
| 1189 canvas.style.marginLeft = marginLeft + "px"; | |
| 1190 } | |
| 1191 } | |
| 1192 } | |
| 1193 | |
| 1194 function remove_food(row, col) | |
| 1195 { | |
| 1196 for(var i=0; i<food.length; i++) | |
| 1197 { | |
| 1198 if(food[i].r === row && food[i].c === col) | |
| 1199 { | |
| 1200 food.splice(i, 1); | |
| 1201 return; | |
| 1202 } | |
| 1203 } | |
| 1204 } | |
| 1205 | |
| 1206 function remove_weapon(row, col) | |
| 1207 { | |
| 1208 for(var i=0; i<weapon_locations.length; i++) | |
| 1209 { | |
| 1210 if(weapon_locations[i].r === row && weapon_locations[i].c === col) | |
| 1211 { | |
| 1212 weapon_locations.splice(i, 1); | |
| 1213 return; | |
| 1214 } | |
| 1215 } | |
| 1216 } | |
| 1217 | |
| 1218 function remove_loot(row, col) | |
| 1219 { | |
| 1220 for(var i=0; i<loot.length; i++) | |
| 1221 { | |
| 1222 if(loot[i].r === row && loot[i].c === col) | |
| 1223 { | |
| 1224 loot.splice(i, 1); | |
| 1225 return; | |
| 1226 } | |
| 1227 } | |
| 1228 } | |
| 1229 var time_pass_timeout_set = false; | |
| 1230 | |
| 1231 function step(dir, px_delta, py_delta) | |
| 1232 { | |
| 1233 if(!time_pass_timeout_set) | |
| 1234 { | |
| 1235 time_passing_timeout = setTimeout(time_tick, 1000); | |
| 1236 time_pass_timeout_set = true; | |
| 1237 } | |
| 1238 | |
| 1239 if(! animation_running) // this tells us if we are in an animation | |
| 1240 { | |
| 1241 var current_life = parseInt(document.getElementById("energy").innerHTML); | |
| 1242 | |
| 1243 player_dir = dir; | |
| 1244 | |
| 1245 var t_row = row + py_delta; | |
| 1246 var t_col = col + px_delta; | |
| 1247 if(can_go(row, col, px_delta, py_delta)) | |
| 1248 { | |
| 1249 | |
| 1250 if(player_steps === STEPS_COST) { | |
| 1251 current_life --; | |
| 1252 } | |
| 1253 else | |
| 1254 if(player_steps === STEPS_SICK) { | |
| 1255 current_life -= 2; | |
| 1256 } | |
| 1257 if(current_life <= 0) | |
| 1258 { | |
| 1259 game_over(GO_FATIGUE, -1); | |
| 1260 return; | |
| 1261 } | |
| 1262 update_life_visuals(current_life); | |
| 1263 step_count ++; | |
| 1264 if(game_type === GAME_TYPE_TIMERUN) | |
| 1265 { | |
| 1266 update_step_visuals(); | |
| 1267 } | |
| 1268 | |
| 1269 var ud_scrolled = false; | |
| 1270 var lr_scrolled = false; | |
| 1271 | |
| 1272 if(px_delta === 1) // right | |
| 1273 { | |
| 1274 if(col >= last_col_drawn - MAZE_EDGE_DISTANCE_SCROLL_TRIGGER) | |
| 1275 { | |
| 1276 last_col_drawn ++ ; | |
| 1277 first_col_drawn ++; | |
| 1278 canv_moving_direction_lr = -1; | |
| 1279 canv_anim_counter = 0; | |
| 1280 animate_canvas_movement_left_right(); | |
| 1281 lr_scrolled = true; | |
| 1282 } | |
| 1283 } | |
| 1284 | |
| 1285 if(px_delta === -1) // left | |
| 1286 { | |
| 1287 if(col <= first_col_drawn + MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled && first_col_drawn > 0) | |
| 1288 { | |
| 1289 last_col_drawn -- ; | |
| 1290 first_col_drawn --; | |
| 1291 canv_moving_direction_lr = 1; | |
| 1292 canv_anim_counter = 0; | |
| 1293 animate_canvas_movement_left_right(); | |
| 1294 lr_scrolled = true; | |
| 1295 } | |
| 1296 } | |
| 1297 | |
| 1298 if(py_delta === 1) // down | |
| 1299 { | |
| 1300 if(row >= last_row_drawn - MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled) | |
| 1301 { | |
| 1302 last_row_drawn ++ ; | |
| 1303 first_row_drawn ++; | |
| 1304 canv_moving_direction_ud = -1; | |
| 1305 canv_anim_counter = 0; | |
| 1306 animate_canvas_movement_up_down(); | |
| 1307 ud_scrolled = true; | |
| 1308 } | |
| 1309 } | |
| 1310 | |
| 1311 if(py_delta === -1) //up | |
| 1312 { | |
| 1313 if(row <= first_row_drawn + MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled && !ud_scrolled && first_row_drawn > 0) | |
| 1314 { | |
| 1315 last_row_drawn -- ; | |
| 1316 first_row_drawn --; | |
| 1317 canv_moving_direction_ud = 1; | |
| 1318 canv_anim_counter = 0; | |
| 1319 animate_canvas_movement_up_down(); | |
| 1320 } | |
| 1321 } | |
| 1322 | |
| 1323 row = t_row; | |
| 1324 col = t_col; | |
| 1325 | |
| 1326 anim_modder = step_anim_cnt; | |
| 1327 window.animation_running = true; | |
| 1328 current_anim_counter = 0; | |
| 1329 x_delta = px_delta; | |
| 1330 y_delta = py_delta; | |
| 1331 } | |
| 1332 | |
| 1333 // and finally make sure the player is drawn back normally where it's supposed to be | |
| 1334 draw_maze(col, row, true); | |
| 1335 context.drawImage(get_current_player_image(player_dir), x, y); | |
| 1336 } | |
| 1337 } | |
| 1338 | |
| 1339 function sleep(ms) | |
| 1340 { | |
| 1341 return new Promise(resolve => setTimeout(resolve, ms)); | |
| 1342 } | |
| 1343 | |
| 1344 function hit_something() | |
| 1345 { | |
| 1346 if(! animation_running) // this tells us if we are in an animation | |
| 1347 { | |
| 1348 x_delta = 0; | |
| 1349 y_delta = 0; | |
| 1350 player_imgset_index += 6; | |
| 1351 anim_modder = thrust_anim_cnt; | |
| 1352 window.animation_running = true; | |
| 1353 current_anim_counter = 0; | |
| 1354 } | |
| 1355 } | |
| 1356 | |
| 1357 // the keyboard handler | |
| 1358 async function keypressed(e) | |
| 1359 { | |
| 1360 if(player_state === PLAYER_ALIVE) | |
| 1361 { | |
| 1362 let slept = 1; | |
| 1363 while(window.walkers_updating && slept < 60) | |
| 1364 { | |
| 1365 slept ++; | |
| 1366 await sleep(20); | |
| 1367 } | |
| 1368 | |
| 1369 var code = e.keyCode; | |
| 1370 switch (code) { | |
| 1371 case 37: step(MOVE_IDX_LEFT, -1, 0); break; //Left key | |
| 1372 case 38: step(MOVE_IDX_UP, 0, -1); break; //Up key | |
| 1373 case 39: step(MOVE_IDX_RIGHT, 1, 0); break; //Right key | |
| 1374 case 40: step(MOVE_IDX_DOWN, 0, 1); break; //Down key | |
| 1375 case 32: hit_something(); // Space | |
| 1376 | |
| 1377 default: console.log(code); //Everything else | |
| 1378 } | |
| 1379 } | |
| 1380 } | |
| 1381 | |
| 1382 Element.prototype.remove = function() { | |
| 1383 this.parentElement.removeChild(this); | |
| 1384 }; | |
| 1385 NodeList.prototype.remove = HTMLCollection.prototype.remove = function() { | |
| 1386 for(var i = this.length - 1; i >= 0; i--) { | |
| 1387 if(this[i] && this[i].parentElement) { | |
| 1388 this[i].parentElement.removeChild(this[i]); | |
| 1389 } | |
| 1390 } | |
| 1391 }; | |
| 1392 | |
| 1393 function toggle_gos(visible) | |
| 1394 { | |
| 1395 document.getElementById('gotext1').style.visibility=visible?'visible':'hidden'; | |
| 1396 document.getElementById('gotext2').style.visibility=visible?'visible':'hidden'; | |
| 1397 document.getElementById('gotext3').style.visibility=visible?'visible':'hidden'; | |
| 1398 document.getElementById('gotext4').style.visibility=visible?'visible':'hidden'; | |
| 1399 document.getElementById('gotext5').style.visibility=visible?'visible':'hidden'; | |
| 1400 | |
| 1401 } | |
| 1402 | |
| 1403 document.getElementById('badbrowser').remove(); | |
| 1404 document.getElementById('gobutton').style.visibility='visible'; | |
| 1405 toggle_gos(true); | |
| 1406 | |
| 1407 function time_passes_by() | |
| 1408 { | |
| 1409 if(player_state === PLAYER_DIED) | |
| 1410 { | |
| 1411 context.drawImage(dying_player[5], x, y); | |
| 1412 return; | |
| 1413 } | |
| 1414 if(time_effect === TIME_HAS_EFFECT) | |
| 1415 { | |
| 1416 var current_life = parseInt(document.getElementById("energy").innerHTML); | |
| 1417 current_life --; | |
| 1418 update_life_visuals(current_life); | |
| 1419 if(current_life <= 0) | |
| 1420 { | |
| 1421 game_over(GO_FATIGUE, -1); | |
| 1422 | |
| 1423 } | |
| 1424 else | |
| 1425 { | |
| 1426 setTimeout(time_passes_by, 3000); | |
| 1427 } | |
| 1428 } | |
| 1429 } | |
| 1430 | |
| 1431 var seconds_passed = 0; | |
| 1432 var minutes_passed = 0; | |
| 1433 var hours_passed = 0; | |
| 1434 var days_passed = 0; | |
| 1435 | |
| 1436 function time_tick() | |
| 1437 { | |
| 1438 seconds_passed ++; | |
| 1439 if(seconds_passed === 60) | |
| 1440 { | |
| 1441 minutes_passed ++; | |
| 1442 if(minutes_passed === 60) | |
| 1443 { | |
| 1444 hours_passed ++; | |
| 1445 if(hours_passed === 24) | |
| 1446 { | |
| 1447 days_passed ++; | |
| 1448 hours_passed = 0; | |
| 1449 } | |
| 1450 minutes_passed = 0; | |
| 1451 } | |
| 1452 seconds_passed = 0; | |
| 1453 } | |
| 1454 | |
| 1455 let new_time = hours_passed.toString() + "h : " + minutes_passed.toString() + "m : " + seconds_passed.toString() + "s " + " "; | |
| 1456 if(days_passed > 0) | |
| 1457 { | |
| 1458 new_time = days_passed.toString() + "days "; | |
| 1459 } | |
| 1460 document.getElementById('time_passed').innerHTML = new_time; | |
| 1461 time_passing_timeout = setTimeout(time_tick, 1000); | |
| 1462 | |
| 1463 } | |
| 1464 | |
| 1465 function get_current_level() { | |
| 1466 var lid = document.getElementById('lid').value; | |
| 1467 var level = parseInt(lid); | |
| 1468 return {level: level}; | |
| 1469 } | |
| 1470 | |
| 1471 function setup_labyrinth(evt) | |
| 1472 { | |
| 1473 if(game_type === GAME_TYPE_TIMERUN) | |
| 1474 { | |
| 1475 time_effect = TIME_HAS_NO_EFFECT; | |
| 1476 player_steps = STEPS_FREE; | |
| 1477 } | |
| 1478 | |
| 1479 var __ret = get_current_level(); | |
| 1480 var level = __ret.level; | |
| 1481 document.getElementById('levelctr').innerHTML = level.toString(); | |
| 1482 | |
| 1483 var gid = document.getElementById('gid').value; | |
| 1484 document.getElementById('gameid').innerHTML = "<a href=/" + gid + ">" + gid + " </a>"; | |
| 1485 | |
| 1486 document.getElementById('messages').style.visibility='hidden'; | |
| 1487 document.getElementById('gobutton').style.visibility='hidden'; | |
| 1488 | |
| 1489 toggle_gos(false); | |
| 1490 | |
| 1491 // draw the maze | |
| 1492 draw_maze(col, row, false); | |
| 1493 | |
| 1494 // set up the previous image data in order to save it for the next animation step | |
| 1495 imgDataPrev = context.getImageData(x,y, anim_size, anim_size); | |
| 1496 | |
| 1497 // draw the player | |
| 1498 context.drawImage(get_current_player_image(player_dir), x, y); | |
| 1499 | |
| 1500 // setting up the event listeners | |
| 1501 window.addEventListener('keydown', keypressed, false); | |
| 1502 canvas.addEventListener('mousedown', mouse_press, false); | |
| 1503 window.addEventListener('mousemove', mouse_move, false); | |
| 1504 window.addEventListener('mouseup', mouse_up, false); | |
| 1505 | |
| 1506 init_walkers(); | |
| 1507 | |
| 1508 setTimeout(time_passes_by, 3000); | |
| 1509 | |
| 1510 game_state = GAME_RUNNING; | |
| 1511 | |
| 1512 walkers_simulation(); | |
| 1513 } | |
| 1514 | |
| 1515 | |
| 1516 function post(path, params, method) { | |
| 1517 method = method || "post"; | |
| 1518 var form = document.createElement("form"); | |
| 1519 form.setAttribute("method", method); | |
| 1520 form.setAttribute("action", path); | |
| 1521 | |
| 1522 for(var key in params) { | |
| 1523 if(params.hasOwnProperty(key)) { | |
| 1524 console.log(key, params[key]); | |
| 1525 | |
| 1526 var hiddenField = document.createElement("input"); | |
| 1527 hiddenField.setAttribute("type", "hidden"); | |
| 1528 hiddenField.setAttribute("name", key); | |
| 1529 hiddenField.setAttribute("value", params[key]); | |
| 1530 | |
| 1531 form.appendChild(hiddenField); | |
| 1532 } | |
| 1533 } | |
| 1534 | |
| 1535 document.body.appendChild(form); | |
| 1536 form.submit(); | |
| 1537 } | |
| 1538 | |
| 1539 function go_next_level() | |
| 1540 { | |
| 1541 var gid = document.getElementById('gid').value; | |
| 1542 post(window.location.href, {gid: gid}); | |
| 1543 } | |
| 1544 | |
| 1545 function show_menu() | |
| 1546 { | |
| 1547 document.getElementById("messages").style.visibility = 'visible'; | |
| 1548 document.getElementById("messages").style.display = 'block'; | |
| 1549 document.getElementById("sysmenu").style.visibility = 'visible'; | |
| 1550 document.getElementById("sysmenu").style.display = 'block'; | |
| 1551 | |
| 1552 document.getElementById("goimg").src = "/img/dungeonslogo.png"; | |
| 1553 } | |
| 1554 | |
| 1555 document.addEventListener('touchstart', handleTouchStart, false); | |
| 1556 document.addEventListener('touchmove', handleTouchMove, false); | |
| 1557 | |
| 1558 var xDown = null; | |
| 1559 var yDown = null; | |
| 1560 | |
| 1561 function handleTouchStart(evt) { | |
| 1562 xDown = evt.touches[0].clientX; | |
| 1563 yDown = evt.touches[0].clientY; | |
| 1564 }; | |
| 1565 | |
| 1566 function handleTouchMove(evt) { | |
| 1567 if ( ! xDown || ! yDown ) { | |
| 1568 return; | |
| 1569 } | |
| 1570 | |
| 1571 var xUp = evt.touches[0].clientX; | |
| 1572 var yUp = evt.touches[0].clientY; | |
| 1573 | |
| 1574 var xDiff = xDown - xUp; | |
| 1575 var yDiff = yDown - yUp; | |
| 1576 | |
| 1577 if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/ | |
| 1578 if ( xDiff > 0 ) { | |
| 1579 step(MOVE_IDX_LEFT, -1, 0); | |
| 1580 } else { | |
| 1581 step(MOVE_IDX_RIGHT, 1, 0); | |
| 1582 } | |
| 1583 } else { | |
| 1584 if ( yDiff > 0 ) { | |
| 1585 step(MOVE_IDX_UP, 0, -1); | |
| 1586 } else { | |
| 1587 step(MOVE_IDX_DOWN, 0, 1); | |
| 1588 } | |
| 1589 } | |
| 1590 /* reset values */ | |
| 1591 xDown = null; | |
| 1592 yDown = null; | |
| 1593 }; |
