Mercurial > maze-src
diff game.js @ 0:1eef88068f9f tip
initial commit of maze game source
| author | ferencd |
|---|---|
| date | Sun, 15 Sep 2019 11:46:47 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/game.js Sun Sep 15 11:46:47 2019 +0200 @@ -0,0 +1,1593 @@ +const pic_size = 64; // The size of 1 cell in the maze + +// initial position of the player is always: (0,0) ie. 12px 12px considering the wall size the the top and left +let x = 12; +let y = 12; + +// moving direction of the player +var x_delta = 0; +var y_delta = 0; + +// player location in grid +var col = 0; // The column in the maze where the player is +var row = 0; // The row in the maze where the player is + +// Prepare the canvas +var canvas = document.getElementById("canvas"); +var canvas_container = document.getElementById("canvasdiv"); +var context = canvas.getContext('2d'); + +// canvas size calculation +var canvas_calc_width = w * pic_size ; +var canvas_calc_height = h * pic_size; + +canvas.style.width = canvas_calc_width + "px"; +canvas.style.height = canvas_calc_height + "px"; + +canvas.width = canvas_calc_width; +canvas.height = canvas_calc_height; + +// Approximates the last row/col drawn for the canvas scrolling +var last_row_drawn = canvas_container.offsetHeight / pic_size; +var first_row_drawn = 0; +var last_col_drawn = canvas_container.offsetWidth / pic_size; +var first_col_drawn = 0; + +// fill the canvas with black +context.fillStyle = "black"; +context.fillRect(0, 0, canvas.width, canvas.height); + +// constants for direction checking +const N = 1; +const S = 2; +const E = 4; +const W = 8; + +// Corner constants, to know where to put a torch +const TOP_LEFT = 1; +const TOP_RIGHT = 2; +const BOTTOM_LEFT = 4; +const BOTTOM_RIGHT = 8; + +// How far must we be from the maze edge to trigger scrolling +const MAZE_EDGE_DISTANCE_SCROLL_TRIGGER = 4; + +// How much we can see from the maze +var LIGHT_RADIUS = 3; + +// how the maze looks +const FULL_FOG = 0; +const FULL_DRAWN = 1; +const FOG_DRAWN = 2; + +// indexes of the various directions the player can go +const MOVE_IDX_LEFT = 0; +const MOVE_IDX_UP = 1; +const MOVE_IDX_RIGHT = 2; +const MOVE_IDX_DOWN = 3; + +// Player state: Dead/Alive +const PLAYER_ALIVE = 1; +const PLAYER_DIED = 2; +var player_state = PLAYER_ALIVE; + +// Player weapon +const WEAPON_NONE = -1; +const WEAPON_SPEAR = 0; + +var player_weapon = WEAPON_NONE; + +// Player armor +const ARMOR_NONE = -1; +const ARMOR_CHAINMAIL = 1; +const SHOES = 2; +const RING = 3; +var player_armor = ARMOR_NONE; + +// whether we remove life from steps or not +const STEPS_COST = 0; // each step is 1 life +const STEPS_FREE = 1; // steps do not cost 1 life +const STEPS_SICK = 2; // player is sick, steps cost 2 life +var player_steps = STEPS_COST; +var step_count = 0; + +const TIME_HAS_EFFECT = 0; +const TIME_HAS_NO_EFFECT = 1; +var time_effect = TIME_HAS_EFFECT; + +const GAME_SUSPENDED = 1; +const GAME_RUNNING = 0; +var game_state = GAME_SUSPENDED; + +var time_passing_timeout = null; + +// These correspond to specific igw types: +// 0 - empty handed skeleton. Can be killed with SPEAR or anything above +// 1 - skeleton with spear. Can be killed with Spear but only if player has armor +// 2 - skeleton with bow. Does not move, just shoots. Can be killed with spear and armor +// 3 - skeletong with spear and armor. Cannot be killed + +function init_maze_draw_array() +{ + var r = new Array(h); + for(var j=0; j<h; j++) + { + r[j] = new Array(w); + for(var i=0; i<w; i++) + { + r[j][i] = FULL_FOG; + } + } + return r; +} + +function update_step_visuals() +{ + document.getElementById("steps_passed").innerHTML = " Steps: " + step_count.toString(); +} + +function update_life_visuals(current_life) +{ + document.getElementById("energy").innerHTML = current_life.toString(); + document.getElementById("lifeprg").value = current_life; +} + +var dying_plyr_img_idx = 0; +var killer_igwidx = -1; +var dancing_kille_igw = 0; +var dancing_mod = 0; +var death_wall_drawn = false; +var skdc = 0; // skeleton dancer index, ie. the one rotating the skeleton +function player_dies() { + context.putImageData(imgDataPrev, x, y); // clear canvas + + if (dying_plyr_img_idx < 6) + { + + context.drawImage(dying_player[dying_plyr_img_idx], x, y); + dying_plyr_img_idx ++; + } + + if(killer_igwidx !== -1) { + if (!death_wall_drawn) { + draw_a_wall(row, col); + death_wall_drawn = true; + } + + dancing_mod ++; + context.drawImage(skel_dancer[skdc][dancing_kille_igw], x, y); + + if(dancing_mod % 3) + { + dancing_kille_igw++; + if(dancing_kille_igw === 6) + { + dancing_kille_igw = 0; + skdc ++; + if(skdc === 4) + { + skdc = 0; + } + } + } + } + + requestAnimationFrame(player_dies); +} + +function kill_igw(i) +{ + igw[i].state = DEAD; + var current_kills = parseInt(document.getElementById("skels_killed").innerHTML); + current_kills ++; + document.getElementById("skels_killed").innerHTML = current_kills.toString(); +} + +var go_texts=["Maybe you shouldn't have had eaten that mushroom.<p>Not all food is safe down here.", + "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.", + "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.", + leave_text +]; + + +const GO_BAD_FOOD = 1; +const GO_SKELETON = 2; +const GO_FATIGUE = 3; +const GO_LEVEL_DONE = 4; + +// reason: 1 = player died since he ate something he wasn't supposed to +// 2 = player was killed by a skeleton +// 3 = fatigue +// 4 = player finished the current level +function game_over(reason, i) +{ + if(reason === GO_SKELETON) + { + killer_igwidx = i; + context.putImageData(igw[killer_igwidx].savedImage, igw[killer_igwidx].x, igw[killer_igwidx].y); + } + document.getElementById("goimg").style.visibility = 'visible'; + + var final_message = go_texts[reason - 1]; + if(reason !== GO_LEVEL_DONE) + { + update_life_visuals(0); + + player_dies(); + draw_a_wall(row,col); + context.drawImage(dying_player[5], x, y); + + final_message += story_text; + document.getElementById("goimg").src ="/img/gameover.png"; + player_state = PLAYER_DIED; + } + else + { + document.getElementById('gonextlevel').style.visibility='visible'; + clearTimeout(time_passing_timeout); + } + + if(game_type === GAME_TYPE_TIMERUN) + { + final_message += "<p>" + document.getElementById("time_passed").innerHTML; + } + + document.getElementById("messages").style.visibility = 'visible'; + document.getElementById("messages").style.display = 'block'; + document.getElementById("gotext1").style.visibility = 'visible'; + document.getElementById("gotext1").innerHTML = final_message; + document.getElementById("gospan").style.visibility = 'hidden'; + + document.getElementById("sysmenu").style.visibility = 'hidden'; + + game_state = GAME_SUSPENDED; +} + +var maze_stats = init_maze_draw_array(); + +function point_in_circle(center_x, center_y, radius, x, y) +{ + var D = Math.sqrt(Math.pow(center_x - x, 2) + Math.pow(center_y - y, 2)); + return D <= radius +} + +function draw_torches(next) +{ + for(var i=0; i<torches.length; i++) { + if(next) { + torches[i].ctr ++; + } + if(torches[i].ctr % 5 === 0) + { + context.putImageData(torches[i].backgr, torches[i].x, torches[i].y); + + if(next) { + torches[i].anim_idx ++; + } + + if(torches[i].anim_idx === 3 && next) { + torches[i].anim_idx = 0; + } + context.drawImage(torch_images[torches[i].anim_idx], torches[i].x, torches[i].y); + } + } +} + +function draw_a_wall(saver, savec) +{ + if(maze_stats[saver][savec] === FULL_FOG) + { + return; + } + + if(maze_stats[saver][savec] === FULL_DRAWN) + { + var current_wall = wall_images[ maze[saver][savec] ]; + // Any door here? + let door_t = door_at(savec, saver); + if( door_t !== -1 ) + { + current_wall = get_doored_wall(maze[saver][savec], door_t); + } + context.drawImage(current_wall, savec * pic_size, saver * pic_size); + draw_extra_objects(saver, savec, savec * pic_size, saver * pic_size); + } + else + { + context.drawImage(fog_drawn_wall_images[ maze[saver][savec] ], + savec * pic_size, saver * pic_size); + } +} + +// The torch animations +var torches = []; + +var top_btm_chooser = []; +top_btm_chooser[0] = top_closed_doors; +top_btm_chooser[1] = bottom_closed_doors; + +var small_fellow = new Image(); +small_fellow.src = '/img/funnyfellow.png'; + +// index is the number of the cell we are looking for, type is whether the door is on +// the top part of the cell (0) or the bottom part (1) +function get_doored_wall(index, type) +{ + if(type !== 5) + { + for(var i=0; i<top_btm_chooser[type].length; i++) + { + if(top_btm_chooser[type][i].idx === index) + { + return top_btm_chooser[type][i].img; + } + } + return wall_images[ index ]; + } + else + { + return stairs_down[index]; + } +} + +// returns the torch type number if we have a torch at this location +function have_torch_here(ii, jj) +{ + for(var i=0; i<torch_placements.length; i++) + { + if(torch_placements[i].i === ii && torch_placements[i].j === jj) + { + return torch_placements[i].p + } + } + return 0; +} + +// returns the food type number if we have a food item at this location +function have_food_here(r, c) +{ + for(var i=0; i<food.length; i++) + { + if(food[i].r === r && food[i].c === c) + { + return food[i].t; + } + } + return -1; +} + +function have_loot_here(r, c) +{ + for(var i=0; i<loot.length; i++) + { + if(loot[i].r === r && loot[i].c === c) + { + return loot[i].t; + } + } + return -1; +} + +function draw_food(x, y, t) +{ + context.drawImage(food_image[t], x + (pic_size - 24) / 2 - food_image[t].clientWidth / 2, + y + (pic_size -24)/ 2 - food_image[t].clientHeight / 2); +} + +function draw_loot(x, y, t) +{ + context.drawImage(item_image[t], x + (pic_size - 24) / 2 - item_image[t].clientWidth / 2, + y + (pic_size -24)/ 2 - item_image[t].clientHeight / 2); +} + +// returns the food type number if we have a food item at this location +function have_weapon_here(r, c) +{ + for(var i=0; i<weapon_locations.length; i++) + { + if(weapon_locations[i].r === r && weapon_locations[i].c === c) + { + return weapon_locations[i].t; + } + } + return -1; +} + +function draw_weapon(x, y, t) +{ + context.drawImage(weapon_images[t], x + (pic_size - 24) / 2 - weapon_images[t].clientWidth / 2, + y + (pic_size -24)/ 2 - weapon_images[t].clientHeight / 2); +} + +function draw_torch(torch_x, torch_y, i, j, cond) +{ + if(cond) + { + var torch_backgr = context.getImageData(torch_x, torch_y, 6, 10); + context.drawImage(torch_images[0], torch_x, torch_y); + torches.push({x: torch_x, y: torch_y, anim_idx: 0, ctr: 0, i: i, j: j, backgr: torch_backgr}); + } +} + +function door_at(i, j) +{ + for(var k=0; k<doors.length; k++) + if(doors[k].r === j && doors[k].c === i) + return doors[k].v; + return -1; +} + +// true if we are just drawing a maze +window.maze_drawing = false; + +// true if we are updating the walkers. Don't move the playuer meanwhile +window.walkers_updating = false; + +function draw_extra_objects(j, i, mx, my) +{ + var food_type = have_food_here(j, i); + if (food_type !== -1) { + draw_food(mx, my, food_type); + } + + var loot_type = have_loot_here(j, i); + if (loot_type !== -1) { + draw_loot(mx, my, loot_type); + } + + var weapon_type = have_weapon_here(j, i); + if (weapon_type !== -1) { + draw_weapon(mx, my, weapon_type); + } +} + +var small_fellow_needs_to_be_drawn = true; +if(game_type !== GAME_TYPE_STORY) +{ + small_fellow_needs_to_be_drawn = false; +} +var ffx = 0; // funny fellow x,y +var ffy = 0; +var ffdx = 0; // funny fellow deltax,deltay +var ffdy = 0; +var ffImgDataPrev = null; + +function draw_maze(mid_col, mid_row, force) +{ + window.maze_drawing = true; + var mx = 0; + var my = 0; + +// var strow = Math.max(mid_row - LIGHT_RADIUS - 1, 0); +// var stcol = Math.max(mid_col - LIGHT_RADIUS - 1, 0); + for(var j=0; j<h; j++) // j - row +// for(var j=strow; j<mid_row + LIGHT_RADIUS + 1; j++) // j - row + { +// for (var i = stcol; i < mid_col + LIGHT_RADIUS + 1; i++) // i - col + for (var i = 0; i <w; i++) // i - col + { + mx = i * pic_size; + my = j * pic_size; + + var current_idx = maze[j][i]; + var current_wall = wall_images[ current_idx ]; + + if( point_in_circle(mid_col, mid_row, LIGHT_RADIUS, i, j) ) + { + if( maze_stats[j][i] !== FULL_DRAWN || force) + { + maze_stats[j][i] = FULL_DRAWN; + + var door_t = door_at(i, j); + + // Any door here? + if(door_t !== -1) + { + current_wall = get_doored_wall(current_idx, door_t); + } + context.drawImage(current_wall, mx, my); + + // let's see if this is the last cell, ie. stairs down + if(door_t === 5 && small_fellow_needs_to_be_drawn) + { + //1,8,9 + ffx = mx + 15; + ffy = my + 15; + if(current_idx === 1 || current_idx === 9) // small guy will stand above the stairs + { + ffy = ffy - pic_size; + ffdy = 1; + } + else // small guy will stand on the left of it + { + ffx = ffx - pic_size; + ffdx = 1; + } + ffImgDataPrev = context.getImageData(ffx,ffy, anim_size, anim_size); + context.drawImage(small_fellow, ffx, ffy); + } + + // Torches lying around the corner? + var torch_location_in_cell = have_torch_here(i,j); + if(torch_location_in_cell !== 0) + { + draw_torch(mx, my, i, j, torch_location_in_cell & TOP_LEFT); + draw_torch(mx, my + pic_size - 22, i, j, torch_location_in_cell & BOTTOM_LEFT); + draw_torch(mx+ pic_size - 10, my, i, j, torch_location_in_cell & TOP_RIGHT); + draw_torch(mx + pic_size - 10, my + pic_size - 22, i, j, torch_location_in_cell & BOTTOM_RIGHT); + } + draw_extra_objects(j, i, mx, my); + } + } + else + { + if(point_in_circle(mid_col, mid_row, LIGHT_RADIUS + 2, i, j) ) + { + if(maze_stats[j][i] === FULL_DRAWN || (maze_stats[j][i] === FOG_DRAWN && force) ) + { + context.drawImage(fog_drawn_wall_images[ current_idx ], mx, my); + maze_stats[j][i] = FOG_DRAWN; + // and remove the torch if found at i,j + for(var ti=0; ti < torches.length; ti++) + { + if(torches[ti].i === i && torches[ti].j === j) + { + torches.splice(ti, 1); + } + } + } + } + } + } + } + + window.maze_drawing = false; +} + +// true if we are in between animation of the player +// Will be "window.animation_running" in scripts +var animation_running = false; + +// the current animation counter +var current_anim_counter = 0; + +// the direction in which the player is going, indexes into the array of images +var player_dir = MOVE_IDX_RIGHT; // 0 - Left, 1 - Up, 2 - Right, 3 - Down + +// The size of the animation frame, ie. what must be saved from the canvas +var anim_size = pic_size / 2; + +var player_imgset = [lr_player, lr_player_spear, lr_player_armor_spear, + lr_player_armor, lr_player_shoes, lr_player_shoes_spear, // +6 will give the thrust images + lr_player_thrust, lr_player_spear_thrust, lr_player_armor_spear_thrust, + lr_player_armor_thrust, lr_player_shoes_thrust, lr_player_shoes_spear_thrust +]; +var player_imgset_index = 0; + +// returns the current player image according to +// direction and current animation counter +var anim_modder = step_anim_cnt; +function get_current_player_image(dir) +{ + return player_imgset[player_imgset_index][dir][current_anim_counter % anim_modder]; +} + +function get_direction(c,fwd) { + if(fwd === 1) + { + if(c === 'N') return N; + if(c === 'S') return S; + if(c === 'E') return E; + if(c === 'W') return W; + } + else + { + if(c === 'N') return S; + if(c === 'S') return N; + if(c === 'E') return W; + if(c === 'W') return E; + } + return 0; +} + +function get_dir_idx(dir) +{ + if(dir === W) return MOVE_IDX_LEFT; + if(dir === N) return MOVE_IDX_UP; + if(dir === E) return MOVE_IDX_RIGHT; + if(dir === S) return MOVE_IDX_DOWN; + return -1; +} + +const WALKING = 1; // IGW normally goes around +const DEAD = 5; // igw was killed + +// in game walkers +var igw = []; + + +function update_igw_location(i) { + switch(igw[i].dir) { + case N: + igw[i].r --; + break; + case E: + igw[i].c ++; + break; + case S: + igw[i].r ++; + break; + case W: + igw[i].c --; + break; + } +} + +function init_walkers() { + for(var i=0; i<walkers_count; i++) { + var wx = walkers[i].c * pic_size + 12; + var wy = walkers[i].r * pic_size + 12; + + var cwalker = { + r:walkers[i].r, + c:walkers[i].c, + saver:-1, + savec:-1, + t:walkers[i].t, + p:walkers[i].p, + dir:get_direction(walkers[i].p[0], 1), + anim_ctr:0, + step_cnt:9, + x:wx, + y:wy, + savedImage:null, + path_ctr:0, + path_len:walkers[i].path_len, + move_dir:1, + state:WALKING, + just_updated: false, + anim_mod:true, + }; + + igw.push(cwalker); + } + + // Now patch the rows and cols for the walker to reflect the first step + for(i=0; i<walkers_count; i++) { + update_igw_location(i); + } +} + +function move_igw(i) { + switch(igw[i].dir) { + case N: + igw[i].y --; + return; + case E: + igw[i].x ++; + return; + case W: + igw[i].x --; + return; + case S: + igw[i].y ++; + return; + } + igw[i].just_updated = true; +} + +function can_go(fromrow, fromcol, pxd, pyd) +{ + if(fromrow < 0) fromrow = 0; + if(fromcol < 0) fromcol = 0; + + var cell = maze[fromrow][fromcol]; + var cango = 0; + if(pyd === 1) cango = cell & S; + if(pyd === -1) cango = cell & N; + if(pxd === 1) cango = cell & E; + if(pxd === -1) cango = cell & W; + + return cango !== 0; +} + +function igw_touches_player(i, dist_comp) +{ + var dc = (Math.abs(igw[i].x -x) < 5 && Math.abs(igw[i].y -y)<5 ); + if(dist_comp) + { + return dc; + } + return (( ( igw[i].r === row && igw[i].c === col && igw[i].anim_ctr >= pic_size / 2) + || (igw[i].saver === row && igw[i].savec === col) + )) + || dc; +} + +// Checks whether the player has a corresponding weapon which he can use to kill igw[i]. +// igw[i].t is the type of the igw and if the player weapon > igw[i].t then it can kill. +function player_has_corresponding_weapon(i) +{ + // Fully equipped player will kill everything + if (player_armor === ARMOR_CHAINMAIL && player_weapon === WEAPON_SPEAR) + { + return true; + } + // player with spear will kill simple skeletons + if(player_weapon === WEAPON_SPEAR && igw[i].t === 0) + { + return true; + } + return (player_weapon >= igw[i].t); +} + +function player_has_armor() +{ + return player_armor === ARMOR_CHAINMAIL; +} + +var anim_mod = 0; + +function resolve_weapon(weapon_here) { + if (weapon_here === RING) // ring, will stop the time + { + time_effect = TIME_HAS_NO_EFFECT; + document.getElementById("ring_of_health_div").style.visibility = 'visible'; + } + else if (weapon_here === SHOES) // shoes + { + if (player_armor === ARMOR_NONE) // player has no armor, images shoudl be lr_player_shoes + { + if(player_weapon === WEAPON_SPEAR) + { + player_imgset_index = 5; + } + else + { + player_imgset_index = 4; + } + } + player_steps = STEPS_FREE; + document.getElementById("boots_of_light_div").style.visibility = 'visible'; + LIGHT_RADIUS = 5; + } + else if (weapon_here === WEAPON_SPEAR) // spear + { + player_weapon = WEAPON_SPEAR; + if (player_armor === ARMOR_NONE) // player has no armor, images shoudl be lr_player_spear + { + if(player_steps === STEPS_FREE) // Are there shoes on the player? + { + player_imgset_index = 5; + } + else + { + player_imgset_index = 1; + } + } + else if (player_armor === ARMOR_CHAINMAIL) // player has armor, images should be lr_player_armor_spear + { + player_imgset_index = 2; + } + } + else if (weapon_here === ARMOR_CHAINMAIL) // found the armor + { + player_armor = ARMOR_CHAINMAIL; + if (player_weapon === WEAPON_SPEAR) { + player_imgset_index = 2; + } + else if (player_weapon === WEAPON_NONE) { + player_imgset_index = 3; + } + } +} + +function resolve_loot(t) +{ + var loot_value = item_value[t]; + var current_gold = parseInt(document.getElementById("gold").innerHTML); + current_gold += loot_value; + document.getElementById("gold").innerHTML = current_gold.toString(); +} + +function igw_kills_player(i) +{ + if (!player_has_corresponding_weapon(i)) + { + if(player_has_armor()) + { + let current_life = parseInt(document.getElementById("energy").innerHTML); + let temp_health = current_life - 1; + + if (temp_health <= 0) + { + game_over(GO_SKELETON, i); + return true; + } + update_life_visuals(temp_health); + return false; + } + else + { + game_over(GO_SKELETON, i); + return true; + } + } + else + { + if(igw_touches_player(i, true)) { + kill_igw(i); + } + return false; + } + +} + +var small_fellow_anim_idx = 0; +var small_fellow_anim_runs = false; + +function walkers_simulation() +{ + if(player_state === PLAYER_DIED) + { + context.drawImage(dying_player[5], x, y); + return; + } + + if(game_state === GAME_SUSPENDED) + { + return; + } + + if(small_fellow_anim_runs && small_fellow_anim_idx < pic_size) + { + if(ffImgDataPrev) + { + context.putImageData(ffImgDataPrev, ffx, ffy); // clear canvas + } + + small_fellow_anim_idx ++; + ffx += ffdx; + ffy += ffdy; + + ffImgDataPrev = context.getImageData(ffx,ffy, anim_size, anim_size); + + context.drawImage(small_fellow, ffx, ffy); + + } + + // moving player drawing + if(animation_running) + { + if(!walkers_updating) + { + context.putImageData(imgDataPrev, x, y); // clear canvas + x += x_delta; + y += y_delta; + anim_mod = anim_mod + 1; + if (anim_mod % 4 === 0) current_anim_counter += 1; + if (current_anim_counter < pic_size / 4) { + imgDataPrev = context.getImageData(x, y, anim_size, anim_size); + } + else { + animation_running = false; + current_anim_counter = 0; + + // and here verify what is on the square we arrived to + + // 1. See did we find the exit? + if (col === exit_col && row === exit_row) { + game_over(GO_LEVEL_DONE, -1); + return; + } + + // 2. Any foodstuff or other here? + var food_type_here = have_food_here(row, col); + var maze_redraw_needed = false; + if (food_type_here !== -1) { + var current_life = parseInt(document.getElementById("energy").innerHTML); + var temp_health = current_life + food_effects[food_type_here]; + remove_food(row, col); + maze_redraw_needed = true; + if (temp_health > 100) { + temp_health = 100; + } + if (temp_health <= 0) { + game_over(GO_BAD_FOOD, -1); + return; + } + + update_life_visuals(temp_health); + } + + // Are we next to the small guy with a visible line? + if( row > h - 4 && col > w - 4 && game_type === GAME_TYPE_STORY) + { + small_fellow_needs_to_be_drawn = false; + maze_redraw_needed = true; + small_fellow_anim_runs = true; + } + + // see if there is a wepon there + var weapon_here = have_weapon_here(row, col); + if (weapon_here !== -1) + { + resolve_weapon(weapon_here); + remove_weapon(row, col); + maze_redraw_needed = true; + } + + // if this is an adventure game, see if there is any loot here + if(game_type === GAME_TYPE_ADVENTURE) + { + // see if there is a wepon there + var loot_here = have_loot_here(row, col); + if (loot_here !== -1) + { + resolve_loot(loot_here); + remove_loot(row, col); + maze_redraw_needed = true; + } + } + + if (maze_redraw_needed) + { + draw_maze(col, row, true); + imgDataPrev = context.getImageData(x, y, anim_size, anim_size); + } + + // see if this was a hit operation or not + if(anim_modder === thrust_anim_cnt) + { + // update the image set for the walking + anim_modder = step_anim_cnt; + player_imgset_index -= 6; + } + } + context.drawImage(get_current_player_image(player_dir), x, y); + } + } + + draw_torches(true); + + window.walkers_updating = true; + var drawn_walkers = []; + + for (var i = 0; i < igw.length; i++) { + if (igw[i].state === DEAD) { + context.drawImage(skel_dies[5], igw[i].x, igw[i].y); + continue; + } + + igw[i].anim_mod = !igw[i].anim_mod; + if (igw[i].anim_mod) igw[i].anim_ctr++; + igw[i].just_updated = false; + + igw[i].saver = igw[i].r; + igw[i].savec = igw[i].c; + + if (igw_touches_player(i,false)) + { + if(igw_kills_player(i)) + { + return; + } + } + + if (igw[i].anim_ctr > pic_size / 2 - 1) { + // means we have stepped over the current tile, time to advance to the next one + igw[i].path_ctr = igw[i].path_ctr + igw[i].move_dir; + // console.log("igw[",i,'].path_ctr=',igw[i].path_ctr,' path_len=',igw[i].path_len); + if (igw[i].path_ctr === igw[i].path_len || igw[i].path_ctr < 0) { + // just perform a routine check that the last step of the igw does not kill the player + var tempr = igw[i].r; + var tempc = igw[i].c; + update_igw_location(i); + if (igw_touches_player(i,false)) + { + if(igw_kills_player(i)) + { + return; + } + } + //draw_a_wall(igw[i].r, igw[i].c); + + igw[i].r = tempr; + igw[i].c = tempc; + + // this reverses the igw + igw[i].move_dir = -igw[i].move_dir; + igw[i].path_ctr --; + if(igw[i].path_ctr < 0) + { + igw[i].path_ctr = 0; + } + } + + igw[i].dir = get_direction(igw[i].p[igw[i].path_ctr], igw[i].move_dir); + update_igw_location(i); + igw[i].anim_ctr = 0; + } + + if (maze_stats[igw[i].r][igw[i].c] === FULL_DRAWN && igw[i].savedImage === null) + { + igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); + } + else + { + if (maze_stats[igw[i].r][igw[i].c] === FOG_DRAWN) { + context.drawImage(fog_drawn_wall_images[maze[igw[i].r][igw[i].c]], igw[i].c * pic_size, igw[i].r * pic_size); + } + + // do we move away from a full drawn cell? + if (maze_stats[igw[i].saver][igw[i].savec] === FULL_DRAWN) { + draw_a_wall(igw[i].saver, igw[i].savec); + } + if (igw[i].savedImage === null) { + igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); + } + + } + + // restore the background + 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 ) + { + draw_a_wall(igw[i].r, igw[i].c); + // draw_extra_objects(igw[i].r, igw[i].c, igw[i].r * pic_size, igw[i].c * pic_size) + context.putImageData(igw[i].savedImage, igw[i].x, igw[i].y); + + // draw only torches which are being affected + for (var k = 0; k < torches.length; k++) { + if (torches[k].i === igw[i].c && torches[k].j === igw[i].r) { + context.drawImage(torch_images[torches[k].anim_idx], torches[k].x, torches[k].y); + } + } + } + + // move the walker on the screen + move_igw(i); + + if (maze_stats[igw[i].r][igw[i].c] === FULL_DRAWN) { + igw[i].savedImage = context.getImageData(igw[i].x, igw[i].y, anim_size, anim_size); + let dir_idx = get_dir_idx(igw[i].dir); + let ani_idx = igw[i].anim_ctr % igw[i].step_cnt; + // console.log('igw[i].dir',igw[i].dir, 'dir_idx',dir_idx,'ani_idx',ani_idx); + context.drawImage(walker_images[igw[i].t][dir_idx][ani_idx], igw[i].x, igw[i].y); + drawn_walkers.push({ + img: walker_images[igw[i].t][dir_idx][igw[i].anim_ctr % igw[i].step_cnt], + x: igw[i].x, + y: igw[i].y, + r: igw[i].r, + c: igw[i].c + }); + } + } + + // and just draw again all the walkers that were drawn + if (drawn_walkers.length > 0) { + for (i = 0; i < drawn_walkers.length; i++) { + if (maze_stats[drawn_walkers[i].r][drawn_walkers[i].c] === FULL_DRAWN && + maze_stats[igw[i].saver][igw[i].savec] === FULL_DRAWN) { + context.drawImage(drawn_walkers[i].img, drawn_walkers[i].x, drawn_walkers[i].y); + } + } + // and the player + context.drawImage(get_current_player_image(player_dir), x, y); + } + window.walkers_updating = false; + + requestAnimationFrame(walkers_simulation); +} + +// previous image which was placed on the canvas +var imgDataPrev = null; + + +/* Mouse scroll handling */ +// The canvas will scroll back to these coordinates when done with mouse move +var marginLeftBackup = 0; +var marginTopBackup = 0; +var dragging = false; +var lastX = 0; +var lastY = 0; +var marginLeft = 0; +var marginTop = 0; +// mouse press handler +function mouse_press(e) +{ + var evt = e || event; + dragging = true; + lastX = evt.clientX; + lastY = evt.clientY; + marginLeftBackup = canvas.style.marginLeft; + marginTopBackup = canvas.style.marginTop; + e.preventDefault(); +} + +function mouse_move(e) +{ + var evt = e || event; + if (dragging) { + var deltaX = evt.clientX - lastX; + lastX = evt.clientX; + + if(marginLeft + deltaX <= 0 && canvas.width - Math.abs(marginLeft + deltaX) >= canvas_container.offsetWidth) // did we scroll out left/right + { + marginLeft += deltaX; + canvas.style.marginLeft = marginLeft + "px"; + } + + var deltaY = evt.clientY - lastY; + lastY = evt.clientY; + if(marginTop + deltaY <= 0 && canvas.height - Math.abs(marginTop + deltaY) >= canvas_container.offsetHeight) + { + marginTop += deltaY; + canvas.style.marginTop = marginTop + "px"; + } + } + e.preventDefault(); +} + +function mouse_up() { + dragging = false; + canvas.style.marginLeft = marginLeftBackup; + canvas.style.marginTop = marginTopBackup; +} + +var canv_anim_counter = 0; +var canv_moving_direction_ud = 0; +function animate_canvas_movement_up_down() { + canv_anim_counter += 1; + marginTop += canv_moving_direction_ud; + if (canv_anim_counter < pic_size) + { + requestAnimationFrame(animate_canvas_movement_up_down); + } + else + { + canv_anim_counter = 0; + } + if(canv_moving_direction_ud === -1) + { + if(canvas.height - Math.abs(marginTop) >= canvas_container.offsetHeight) + { + canvas.style.marginTop = marginTop + "px"; + } + } + else + { + if(marginTop <= 0) + { + canvas.style.marginTop = marginTop + "px"; + } + } +} + +var canv_moving_direction_lr = 0; +function animate_canvas_movement_left_right() { + canv_anim_counter += 1; + marginLeft += canv_moving_direction_lr; + if (canv_anim_counter < pic_size) + { + requestAnimationFrame(animate_canvas_movement_left_right); + } + else + { + canv_anim_counter = 0; + } + if(canv_moving_direction_lr === -1) + { + if(canvas.width - Math.abs(marginLeft) >= canvas_container.offsetWidth) + { + canvas.style.marginLeft = marginLeft + "px"; + } + } + else + { + if(marginLeft <= 0) + { + canvas.style.marginLeft = marginLeft + "px"; + } + } +} + +function remove_food(row, col) +{ + for(var i=0; i<food.length; i++) + { + if(food[i].r === row && food[i].c === col) + { + food.splice(i, 1); + return; + } + } +} + +function remove_weapon(row, col) +{ + for(var i=0; i<weapon_locations.length; i++) + { + if(weapon_locations[i].r === row && weapon_locations[i].c === col) + { + weapon_locations.splice(i, 1); + return; + } + } +} + +function remove_loot(row, col) +{ + for(var i=0; i<loot.length; i++) + { + if(loot[i].r === row && loot[i].c === col) + { + loot.splice(i, 1); + return; + } + } +} +var time_pass_timeout_set = false; + +function step(dir, px_delta, py_delta) +{ + if(!time_pass_timeout_set) + { + time_passing_timeout = setTimeout(time_tick, 1000); + time_pass_timeout_set = true; + } + + if(! animation_running) // this tells us if we are in an animation + { + var current_life = parseInt(document.getElementById("energy").innerHTML); + + player_dir = dir; + + var t_row = row + py_delta; + var t_col = col + px_delta; + if(can_go(row, col, px_delta, py_delta)) + { + + if(player_steps === STEPS_COST) { + current_life --; + } + else + if(player_steps === STEPS_SICK) { + current_life -= 2; + } + if(current_life <= 0) + { + game_over(GO_FATIGUE, -1); + return; + } + update_life_visuals(current_life); + step_count ++; + if(game_type === GAME_TYPE_TIMERUN) + { + update_step_visuals(); + } + + var ud_scrolled = false; + var lr_scrolled = false; + + if(px_delta === 1) // right + { + if(col >= last_col_drawn - MAZE_EDGE_DISTANCE_SCROLL_TRIGGER) + { + last_col_drawn ++ ; + first_col_drawn ++; + canv_moving_direction_lr = -1; + canv_anim_counter = 0; + animate_canvas_movement_left_right(); + lr_scrolled = true; + } + } + + if(px_delta === -1) // left + { + if(col <= first_col_drawn + MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled && first_col_drawn > 0) + { + last_col_drawn -- ; + first_col_drawn --; + canv_moving_direction_lr = 1; + canv_anim_counter = 0; + animate_canvas_movement_left_right(); + lr_scrolled = true; + } + } + + if(py_delta === 1) // down + { + if(row >= last_row_drawn - MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled) + { + last_row_drawn ++ ; + first_row_drawn ++; + canv_moving_direction_ud = -1; + canv_anim_counter = 0; + animate_canvas_movement_up_down(); + ud_scrolled = true; + } + } + + if(py_delta === -1) //up + { + if(row <= first_row_drawn + MAZE_EDGE_DISTANCE_SCROLL_TRIGGER && !lr_scrolled && !ud_scrolled && first_row_drawn > 0) + { + last_row_drawn -- ; + first_row_drawn --; + canv_moving_direction_ud = 1; + canv_anim_counter = 0; + animate_canvas_movement_up_down(); + } + } + + row = t_row; + col = t_col; + + anim_modder = step_anim_cnt; + window.animation_running = true; + current_anim_counter = 0; + x_delta = px_delta; + y_delta = py_delta; + } + + // and finally make sure the player is drawn back normally where it's supposed to be + draw_maze(col, row, true); + context.drawImage(get_current_player_image(player_dir), x, y); + } +} + +function sleep(ms) +{ + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function hit_something() +{ + if(! animation_running) // this tells us if we are in an animation + { + x_delta = 0; + y_delta = 0; + player_imgset_index += 6; + anim_modder = thrust_anim_cnt; + window.animation_running = true; + current_anim_counter = 0; + } +} + +// the keyboard handler +async function keypressed(e) +{ + if(player_state === PLAYER_ALIVE) + { + let slept = 1; + while(window.walkers_updating && slept < 60) + { + slept ++; + await sleep(20); + } + + var code = e.keyCode; + switch (code) { + case 37: step(MOVE_IDX_LEFT, -1, 0); break; //Left key + case 38: step(MOVE_IDX_UP, 0, -1); break; //Up key + case 39: step(MOVE_IDX_RIGHT, 1, 0); break; //Right key + case 40: step(MOVE_IDX_DOWN, 0, 1); break; //Down key + case 32: hit_something(); // Space + + default: console.log(code); //Everything else + } + } +} + +Element.prototype.remove = function() { + this.parentElement.removeChild(this); +}; +NodeList.prototype.remove = HTMLCollection.prototype.remove = function() { + for(var i = this.length - 1; i >= 0; i--) { + if(this[i] && this[i].parentElement) { + this[i].parentElement.removeChild(this[i]); + } + } +}; + +function toggle_gos(visible) +{ + document.getElementById('gotext1').style.visibility=visible?'visible':'hidden'; + document.getElementById('gotext2').style.visibility=visible?'visible':'hidden'; + document.getElementById('gotext3').style.visibility=visible?'visible':'hidden'; + document.getElementById('gotext4').style.visibility=visible?'visible':'hidden'; + document.getElementById('gotext5').style.visibility=visible?'visible':'hidden'; + +} + +document.getElementById('badbrowser').remove(); +document.getElementById('gobutton').style.visibility='visible'; +toggle_gos(true); + +function time_passes_by() +{ + if(player_state === PLAYER_DIED) + { + context.drawImage(dying_player[5], x, y); + return; + } + if(time_effect === TIME_HAS_EFFECT) + { + var current_life = parseInt(document.getElementById("energy").innerHTML); + current_life --; + update_life_visuals(current_life); + if(current_life <= 0) + { + game_over(GO_FATIGUE, -1); + + } + else + { + setTimeout(time_passes_by, 3000); + } + } +} + +var seconds_passed = 0; +var minutes_passed = 0; +var hours_passed = 0; +var days_passed = 0; + +function time_tick() +{ + seconds_passed ++; + if(seconds_passed === 60) + { + minutes_passed ++; + if(minutes_passed === 60) + { + hours_passed ++; + if(hours_passed === 24) + { + days_passed ++; + hours_passed = 0; + } + minutes_passed = 0; + } + seconds_passed = 0; + } + + let new_time = hours_passed.toString() + "h : " + minutes_passed.toString() + "m : " + seconds_passed.toString() + "s " + " "; + if(days_passed > 0) + { + new_time = days_passed.toString() + "days "; + } + document.getElementById('time_passed').innerHTML = new_time; + time_passing_timeout = setTimeout(time_tick, 1000); + +} + +function get_current_level() { + var lid = document.getElementById('lid').value; + var level = parseInt(lid); + return {level: level}; +} + +function setup_labyrinth(evt) +{ + if(game_type === GAME_TYPE_TIMERUN) + { + time_effect = TIME_HAS_NO_EFFECT; + player_steps = STEPS_FREE; + } + + var __ret = get_current_level(); + var level = __ret.level; + document.getElementById('levelctr').innerHTML = level.toString(); + + var gid = document.getElementById('gid').value; + document.getElementById('gameid').innerHTML = "<a href=/" + gid + ">" + gid + " </a>"; + + document.getElementById('messages').style.visibility='hidden'; + document.getElementById('gobutton').style.visibility='hidden'; + + toggle_gos(false); + + // draw the maze + draw_maze(col, row, false); + + // set up the previous image data in order to save it for the next animation step + imgDataPrev = context.getImageData(x,y, anim_size, anim_size); + + // draw the player + context.drawImage(get_current_player_image(player_dir), x, y); + + // setting up the event listeners + window.addEventListener('keydown', keypressed, false); + canvas.addEventListener('mousedown', mouse_press, false); + window.addEventListener('mousemove', mouse_move, false); + window.addEventListener('mouseup', mouse_up, false); + + init_walkers(); + + setTimeout(time_passes_by, 3000); + + game_state = GAME_RUNNING; + + walkers_simulation(); +} + + +function post(path, params, method) { + method = method || "post"; + var form = document.createElement("form"); + form.setAttribute("method", method); + form.setAttribute("action", path); + + for(var key in params) { + if(params.hasOwnProperty(key)) { + console.log(key, params[key]); + + var hiddenField = document.createElement("input"); + hiddenField.setAttribute("type", "hidden"); + hiddenField.setAttribute("name", key); + hiddenField.setAttribute("value", params[key]); + + form.appendChild(hiddenField); + } + } + + document.body.appendChild(form); + form.submit(); +} + +function go_next_level() +{ + var gid = document.getElementById('gid').value; + post(window.location.href, {gid: gid}); +} + +function show_menu() +{ + document.getElementById("messages").style.visibility = 'visible'; + document.getElementById("messages").style.display = 'block'; + document.getElementById("sysmenu").style.visibility = 'visible'; + document.getElementById("sysmenu").style.display = 'block'; + + document.getElementById("goimg").src = "/img/dungeonslogo.png"; +} + +document.addEventListener('touchstart', handleTouchStart, false); +document.addEventListener('touchmove', handleTouchMove, false); + +var xDown = null; +var yDown = null; + +function handleTouchStart(evt) { + xDown = evt.touches[0].clientX; + yDown = evt.touches[0].clientY; +}; + +function handleTouchMove(evt) { + if ( ! xDown || ! yDown ) { + return; + } + + var xUp = evt.touches[0].clientX; + var yUp = evt.touches[0].clientY; + + var xDiff = xDown - xUp; + var yDiff = yDown - yUp; + + if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/ + if ( xDiff > 0 ) { + step(MOVE_IDX_LEFT, -1, 0); + } else { + step(MOVE_IDX_RIGHT, 1, 0); + } + } else { + if ( yDiff > 0 ) { + step(MOVE_IDX_UP, 0, -1); + } else { + step(MOVE_IDX_DOWN, 0, 1); + } + } + /* reset values */ + xDown = null; + yDown = null; +};
