view 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 source
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 " + "&nbsp;";
    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 + "&nbsp;</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;
};