HylZee : Game : Page Components JAVASCRIPT DHTML TUTORIALS


JAVASCRIPT DHTML TUTORIALS » Page Components » Game »

 

HylZee



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="content-language" content="en">
<title>HylZee</title>
<!--META HTTP-EQUIV="Expires" CONTENT="Fri, Jan 01 1900 00:00:00 GMT"-->
<!-- commented out, seems to affect images META HTTP-EQUIV="Pragma" CONTENT="no-cache"-->
<!--META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"-->
<meta name="author" content="peter.schaefer@gmail.com">
<META HTTP-EQUIV="Reply-to" CONTENT="peter.schaefer@gmail.com">
<meta name="generator" content="Code Monkey 1970.01.18(tm Peter Schaefer)">
<META NAME="description" CONTENT="HylZee - A DHTML puzzle game">
<meta name="keywords" content="HylZee DHTML javascript puzzle game huelsi tal pyr">
<META NAME="Creation_Date" CONTENT="09.10.2004">
<meta name="revisit-after" content="30 days">

<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" src="favicon.ico">

<!-- copyright 2004 peter schaefer -->
<!-- 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your optionany later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-->

<style type="text/css">

{font-size:16px;}

.map {color:white;}
.controlembossed {
        background: #66CC33;
        border-top-style:solid;
        border-right-style:solid;
        border-bottom-style:solid;
        border-left-style:solid;
        border-top-width: 1px;
        border-right-width: 1px;
        border-bottom-width: 1px;
        border-left-width: 1px;
        border-right-color: #99FF66;
        border-top-color: #99FF66;
        border-left-color: #666666;
        border-bottom-color: #666666;
}
a:link color: #99FF66; }
a:hover color: #FFFF66; }
a:active color: #FF9966; }
a:visited color: #66FF33; }

table,tbody,th,td,img,span {
        margin-left: 0px;
        margin-top: 0px;
        margin-right: 0px;
        margin-bottom 0px;
        padding-left: 0px;
        padding-top: 0px;
        padding-right: 0px;
        padding-bottom: 0px;
}

</style>

<script language="JavaScript" type="text/JavaScript">
<!--

// placement of map window
var map_top= 18;
var map_left= 20;

// grid size of map tiles
var cell_height=32;
var cell_width= cell_height;

// size of bullet graphics
var bullet_width= 12;
var bullet_height= 12;
var status_font= 18;

//status bar minimum width
var minStatusbarWidth= 10*32;

var tileset="default"

// number of space units/tile
var step_unit= 100;

// number of ticks/1 tile movement
var ticks_unit= 420;

// ticks/processing of mainLoop
//FIXME: this doesn't work with 40/240;
var theTick= 42;

// frequency of mainloop
var mainDelay= 20;

// which level to load
var lastLevel= "maps/test/devel-001.html";

// show shrinking animations
var shrink= true;

//continue running
var mainProceed= true;

var debug= false;

//
// debugall switches on most annoying debug output
//
var debugall= true;

var hideDebug= true || debug;

//input constants
var key_fire= 70;
var key_right= 39;
var key_down= 40;
var key_up= 38;
var key_left= 37;

//map object
var map= null;

//log repeat functionality
var log_lastMsg="";
var log_count=0;
var log_threshold=2;
var log_limit=3000;

//which codes signal start and home
var code_start='A';
var code_home ='H';

//
// arguments.caller has been deprecated
// So the stacktrace will not work in new browsers
//
function funcname(f) {
 var s = f.toString().match(/function (w*)/)[1];
 if ((s == null|| (s.length==0)) return "anonymous";
 return s;
}

function stacktrace() {
 if(!arguments.caller) {
        return "no stacktrace available";
 }

 var s = "";
 for (var a = arguments.caller; a !=null; a = a.caller) {
   s += "->"+funcname(a.callee"n";
   if (a.caller == a) {s+="*"break;}
 }
 return s;
}


function debugMsg(message) {
         if(debug) {
           //window.status= message;
           if(log_lastMsg!=message) {
              log_lastMsg=message;
              log_count=0;
              log_threshold=2;
              message="<br>"+message;
           }else {
               log_count++;
               if(log_count>=log_threshold) {
                  message= "<br>last message repeated "+log_count+" more times.";
                  log_threshold<<=1;
                  log_count=0;
               }else {
                  message="";
               }
           }

           if(message) {
               var debugDiv= document.getElementById("debugoutput");
               if(debugDiv){
                 var startpos= (debugDiv.innerHTML.length>log_limit)?(debugDiv.innerHTML.length-log_limit):0;
                 debugDiv.innerHTML= debugDiv.innerHTML.substring(startpos, debugDiv.innerHTML.length)+message;
               }else if(debug && debugall) {
                 alert("debugMsg "+log_lastMsg+" (page isn't loaded yet/no debug window found)");
                 debugall= false;
               }

           }
         }
}

function debugEval(test) {
         debugMsg(test+"="+eval(test));
}


function showLayer(layer) {
        var div= document.getElementById(layer);
        if(div)
         div.style.visibility = 'visible';
}

function hideLayer(layer) {
        var div= document.getElementById(layer);
        if(div)
         div.style.visibility = 'hidden';
}

// the fudge factor is used to work around math rounding errors
var fudge= 0.000001;


function toGridX(x) {
        return  Math.floor(x/step_unit+fudge);
}

function toGridY(y) {
        return  Math.floor(y/step_unit+fudge);
}

function toScreenX(x) {
        return  map_left+Math.floor(x*cell_width+fudge);
}

function toScreenY(y) {
        return  map_top+Math.floor(y*cell_height+fudge);
}

var protagonist=null;
var gameState=null;

var Coordinate_toString= function() {
        return '('+this.x+','+this.y+')';
}

function Coordinate_copy() {
        return new Coordinate(this.x,this.y);
}

function Coordinate_clip() {
        var clipped= false;
        ifthis.x<-fudge ) {
          this.x=0;
          clipped= true;
        else ifthis.x-fudge>step_unit*(map.xmax-1) ) {
          this.x= step_unit*(map.xmax-1);
          clipped= true;
        }

        ifthis.y<-fudge ) {
          this.y=0;
          clipped= true;
        else ifthis.y-fudge>step_unit*(map.ymax-1) ) {
          this.y= step_unit*(map.ymax-1);
          clipped= true;
        }
        return clipped;
}

function Coordinate_add(coordinate) {
        this.x+= coordinate.x;
        this.y+= coordinate.y;
}

function Coordinate_gridLock() {
        this.x= step_unit*Math.floor((this.x+step_unit/2+fudge)/step_unit);
        this.y= step_unit*Math.floor((this.y+step_unit/2+fudge)/step_unit);
}

function Coordinate(x,y) {
        this.x= x;
        this.y= y;
        //methods
        this.clip= Coordinate_clip;
        this.toString= Coordinate_toString;
        this.add= Coordinate_add;
        this.copy= Coordinate_copy;
        this.gridLock= Coordinate_gridLock;
}

function toName() {
        return 'Avatar of:'+this.name;
}

function Avatar(name,modifier,facing) {
        this.name= name;
        this.modifier= modifier;
        this.facing= facing;

        //methods
        this.toString= toName;
}

//
// image preloading
//
var myImages= new Array();
function loadImages() {
  for (i=0;i<loadImages.arguments.length;i++) {
          var pos= myImages.length;
          myImages[pos]=new Image();
          myImages[pos].src= loadImages.arguments[i];
  }
}

function imagePath(imageName) {
 return 'images/'+tileset+'/'+imageName;
}

var browsercaps= new Object();

//get internal browser representation/ordering of html
function fixHTML(html) {
        if(browsercaps.fixHTML) {
          var div= document.getElementById("scratchpad");
          div.innerHTML= html;
          return div.innerHTML;
        }else{
          return html;
        }
}

//
// Some browser, like gecko, covert innerHTML to canonic representation.
// this causes flickering
//
function debugHTMLrep(text){
  var fixed= fixHTML(text);
  iffixed!= text) {
    debugMsg("txt:"+escape(text));
    debugMsg("fix:"+escape(fixed));
  }
}


function Direction_normalize(direction) {
        if(direction<0)
           direction= (direction- 360*Math.floor(direction/360+fudge))
        direction= (direction+360)%360;
        return direction;
}

//
// This function almost works for all agents
//
function Protagonist_mapToImage() {
        this.image="protagonist";
        if(this.mode) {
                this.image+= "_"+this.mode;
        }

        if(this.mode!="victorious") {
              if(this.facing>=&& this.facing<=45)
                      this.image+= "_right";
              else if(this.facing>=45 && this.facing<=135)
                      this.image+= "_up";
              else if(this.facing>=135 && this.facing<=225)
                      this.image+= "_left";
              else if(this.facing>=225 && this.facing<=315)
                      this.image+= "_down";
              else if(this.facing>=315 && this.facing<=360)
                      this.image+= "_right";
              else {
                      var msg='mapToImage: ('+this.facing+', bad facing)';
                      debugMsg(msg);
              }
        }

        this.image+="_64.gif";

        return this.image;
}

var bullet_right= new Avatar('bullet_small','right',0);
var bullet_up= new Avatar('bullet_small','up',90);
var bullet_left= new Avatar('bullet_small','left',180);
var bullet_down= new Avatar('bullet_small','down',270);

var bird_right= new Avatar('phoenix_moving','right',0);
var bird_up= new Avatar('phoenix_moving','up',90);
var bird_left= new Avatar('phoenix_moving','left',180);
var bird_down= new Avatar('phoenix_moving','down',270);

var siren_right= new Avatar('chess-rook_moving','right',0);
var siren_up= new Avatar('chess-rook_moving','up',90);
var siren_left= new Avatar('chess-rook_moving','left',180);
var siren_down= new Avatar('chess-rook_moving','down',270);


function getSirenForDirection(direction) {
        direction= Direction_normalize(direction);

        if(direction>=&& direction<=45)
                return siren_right;
        else if(direction>=45 && direction<=135)
                return siren_up;
        else if(direction>=135 && direction<=225)
                return siren_left;
        else if(direction>=225 && direction<=315)
                return siren_down;
        else if(direction>=315 && direction<=360)
                return siren_right;
        else {
                var avatar= 'getSiren: fuck up('+direction+', bad argument)';
                debugMsg(avatar);
                return null;
        }
}

function getBirdForDirection(direction) {
        direction= Direction_normalize(direction);

        if(direction>=&& direction<=45)
                return bird_right;
        else if(direction>=45 && direction<=135)
                return bird_up;
        else if(direction>=135 && direction<=225)
                return bird_left;
        else if(direction>=225 && direction<=315)
                return bird_down;
        else if(direction>=315 && direction<=360)
                return bird_right;
        else {
                var avatar= 'getBird: fuck up('+direction+', bad argument)';
                debugMsg(avatar);
                return null;
        }
}

//FIXME: replace all the verbatim stuff with imagePath(imageName)
function mapCodeToImage(code){
        var src='';
        switch(code) {

                 case 'H':src='images/'+tileset+'/'+'house_64.gif'; break;
                 case 'h':src='images/'+tileset+'/'+'house_open_64.gif'; break;


                 case 'K':src='images/'+tileset+'/'+'ball_red_64.gif'; break;
                 case 'I':src='images/'+tileset+'/'+'ball_blue_64.gif'; break;
                 case 'J':src='images/'+tileset+'/'+'ball_green_64.gif'; break;

                case 'L':src='images/'+tileset+'/'+'box_blue_64.gif'; break;

                 case 'C':src='images/'+tileset+'/'+'bricks_red_64.gif'; break;
                 case 'g':src='images/'+tileset+'/'+'bricks_green_64.gif'; break;
                 case 'N':src='images/'+tileset+'/'+'bricks_grey_64.gif'; break;

                 case 'M':src='images/'+tileset+'/'+'centerstone-blue_64.gif'; break;
                 case 'O':src='images/'+tileset+'/'+'centerstone-grey_64.gif'; break;

                case 'R':src='images/'+tileset+'/'+'phoenix_sleeping_left_64.gif'; break;
                case 'S':src='images/'+tileset+'/'+'phoenix_sleeping_up_64.gif'; break;
                case 'T':src='images/'+tileset+'/'+'phoenix_sleeping_right_64.gif'; break;
                case 'U':src='images/'+tileset+'/'+'phoenix_sleeping_down_64.gif'; break;

                case 'r':src='images/'+tileset+'/'+'phoenix_awake_left_64.gif'; break;
                case 's':src='images/'+tileset+'/'+'phoenix_awake_up_64.gif'; break;
                case 't':src='images/'+tileset+'/'+'phoenix_awake_right_64.gif'; break;
                case 'u':src='images/'+tileset+'/'+'phoenix_awake_down_64.gif'; break;

                case 'P':src='images/'+tileset+'/'+'chess-rook_sleeping_left_64.gif'; break;
                case 'p':src='images/'+tileset+'/'+'chess-rook_awake_left_64.gif'; break;

                case 'P.right':src='images/'+tileset+'/'+'chess-rook_sleeping_right_64.gif'; break;
                case 'p.right':src='images/'+tileset+'/'+'chess-rook_awake_right_64.gif'; break;

                case 'p.moving.left':src='images/'+tileset+'/'+'chess-rook_moving_left_64.gif'; break;
                case 'p.moving.up':src='images/'+tileset+'/'+'chess-rook_moving_up_64.gif'; break;
                case 'p.moving.right':src='images/'+tileset+'/'+'chess-rook_moving_right_64.gif'; break;
                case 'p.moving.down':src='images/'+tileset+'/'+'chess-rook_moving_down_64.gif'; break;

                //FIXME: this should be handled more elegantly by checking for code.mapToImage and writing adding mapToImage function to avatar
                case bullet_right:
                case bullet_left:
                case bullet_up:
                case bullet_down:

                case siren_right:
                case siren_left:
                case siren_up:
                case siren_down:

                case bird_right:
                case bird_left:
                case bird_up:
                case bird_down:
                        src='images/'+tileset+'/'+code.name+'_'+code.modifier+'_64.gif';
                        //debugMsg('setting image:'+src);
                        break;

                case 'Z':src='images/'+tileset+'/'+'cone_up_64.gif'; break;
                case '[':src='images/'+tileset+'/'+'cone_down_64.gif'; break;

                case 'bullet_big':src='images/'+tileset+'/'+'bullet_big_right_64.gif'; break;

                case 'background':src='images/'+tileset+'/'+'background.gif'; break;

                 default: src='';
        }
        return src;
}

function mapCodeToHtml(code,width,height) {
             var box="";
             var imgsrc= mapCodeToImage(code);
             if(imgsrc) {
                box+='<img src="'+imgsrc+'" alt="'+code+'" title="'+code+'" style="margin: 0px;" border="0" height="'+Math.floor(height)+'" width="'+Math.floor(width)+'">';
                if(debug && debugall) {
                        debugHTMLrep(box);
                }
             else {
                   switchcode ){
                          case '_':
                          case ' ':
                          case 'A':
                               box= "&nbsp;";
                               break;
                          default:
                            box= "&nbsp;"+code+"&nbsp;";
                   }
             }
             return box;
}

function Snapshot(what,pos,zoom,time) {
        this.what= what;
        this.pos= pos;
        this.zoom= zoom;
        this.time= time;
}

function Code_animate(t) {

        var completed= (t-this.a.time)/(this.b.time-this.a.time);
        var zoom = this.b.zoom*completed+this.a.zoom*(1.0-completed);
        var posx= this.b.pos.x*completed+this.a.pos.x*(1.0-completed);
        var posy= this.b.pos.y*completed+this.a.pos.y*(1.0-completed);

        var pos= new Coordinate(posx,posy);

        //note: clipping suffers badly from bad math which gives >xmax*step_
        ifpos.clip() ) {
                this.b.time=t;//FIXME:zayin ba debug!
        }

        if(this.updateObj && this.updateObj.update) {
                this.updateObj.update(pos);
        }

        var animDiv= document.getElementById(this.div);
        if(animDiv) {
         animDiv.style.left= toScreenX(pos.x/step_unit+(1.0-zoom)*0.5);
         animDiv.style.top=  toScreenY(pos.y/step_unit+(1.0-zoom)*0.5);

         var node= mapCodeToNode(this.a.what,cell_width*zoom,cell_height*zoom,animDiv.id);
         if(node!= animDiv.firstChild){
             clearChildren(animDiv);
             animDiv.appendChild(node);
         }
         //this redraw often isn't necessary => don't redraw
//          var text= mapCodeToHtml(this.a.what,cell_width*zoom,cell_height*zoom);
         //
         //text= fixHTML(text);
         // fixed already !?
//          if(text!=animDiv.innerHTML)
//               animDiv.innerHTML= text;
        else {
          debugMsg("Error: no layer left for animations");
        }
}

var animation_divs= new Array();

function Animation_begin() {
        this.finished= false;
        this.hasChanged= true;
        for(var i=0; i<1000; ++i) {
                if(!animation_divs[i]) {
                        animation_divs[i]"animation"+i;
                        this.divid= i;
                        this.div= animation_divs[this.divid];
                        //var animDiv= document.getElementById(this.div);
                        //animDiv.innerHTML="";
                        showLayer(this.div);
                        break;
                }
        }
}

function Animation_end() {

        if(this.div) {

          var animDiv= document.getElementById(this.div);
          animDiv.innerHTML="";
          hideLayer(this.div);
          animation_divs[this.divid]null;
          this.div= null;
          this.hasChanged= true;
        }

        //avoid endless recursion
        if(!this.finished) {
          this.finished= true;
          if(this.updateObj && this.updateObj.finish) {
                  this.updateObj.finish();
          }
        }

}


function Animation(a,b,updateObj) {
        this.a= a;
        this.b= b;
        this.updateObj= updateObj;

        this.animate= Code_animate;

        this.begin= Animation_begin;
        this.end= Animation_end;
}



var mapRaw= new Array(
new Array('_','_','_','C','_','M','N','O','_','_','_','_'),
new Array('_',' ','A','L',' ',' ',' ',' ',' ',' ','P','_'),
new Array('_','K',' ','g','g',' ',' ',' ',' ',' ',' ','_'),
new Array(' ','I',' ',' ','g',' ',' ',' ','?,' ',' ','_'),
new Array('I','J',' ','N','N','N',' ',' ',' ',' ',' ','_'),
new Array('I',' ',' ','N','N','N',' ','Z',' ',' ','U','_'),
new Array(' ',' ',' ',' ',' ',' ',' ','[',' ',' ',' ','_'),
new Array(' ',' ','L',' ','Z',' ',' ',' ','T',' ',' ','_'),
new Array(' ','N','[',' ','L',' ',' ',' ','T',' ',' ','R'),
new Array('R','P','_','_','_','_','H','_','_','S','_','_')
);

var notnagel=0;

//
// scans the map for an object specified by 'code'
// function is notorious for hanging the program ..
//
function Map_scan(code,last) {
        if(Map_scan.arguments.length<2) {
                x= 0;
                y= 0;
        else if(!last) {
                x= 0;
                y= 0;
        else {
                x= toGridX(last.x)+1;
                y= toGridY(last.y);
                notnagel++;
//                 if(notnagel>100) {
//                         debugMsg("halting");
//                         gameProceed= false;
//                         sleep(1000);
//                         notnagel=0;
//                 }
                //debugMsg("last:"+last+" x "+x+" y"+y);
        }

        //for y for x
        while(true) {
                if(x>=this.xmax) {
                        x= 0;
                        y= y+1;
                  if(y>=this.ymax) {
                        return null;
                  }
                }

                if(this.map[y][x]==code) {
                        var c= new Coordinate(x*step_unit,y*step_unit);
                        return c;
                }

                x= x+1;
        }

}

function fromDirection(direction) {
        direction= Direction_normalize(direction);
        if(direction>=&& direction<=45)
                return this.right;
        else if(direction>=45 && direction<=135)
                return this.up;
        else if(direction>=135 && direction<=225)
                return this.left;
        else if(direction>=225 && direction<=315)
                return this.down;
        else if(direction>=315 && direction<=360)
                return this.right;
        else {
                var msg= 'fuck up('+this.direction+', bad argument)';
                debugMsg(msg);
                return this.none;
        }
}


function Compass(scale) {
  if(!scale) {
        scale=1;
  }
  this.none= new Coordinate(0,0);

  this.right= new Coordinate(scale,0);
  this.up=  new Coordinate(0,-scale);
  this.left= new Coordinate(-scale,0);
  this.down= new Coordinate(0,scale);

  //methods
  this.fromDirection= fromDirection;
}

var compass= new Compass();
var stepcompass= new Compass(step_unit);


function scanTrap(code,direction,from,checkObstacle,checkForTarget) {
  var delta= stepcompass.fromDirection(direction);

  var pos= from.copy();

  pos.add(delta);

  while(!pos.clip()) {
        ifcheckObstacle(code,pos) )
                return false;
        ifcheckForTarget(code,pos,from) )
                return true;
        pos.add(delta);
  }
  return false;
}

function getXYCode(x,y) {
        x= Math.floor(x+fudge);
        y= Math.floor(y+fudge);
        if(!this.map[y]) {
                debugMsg("bad x,y in getXYCode x:"+x+" y:"+y);
        }
        return this.map[y][x];
}

function setXYCode(x,y,code) {
        x= Math.floor(x+fudge);
        y= Math.floor(y+fudge);
        this.mapHasChanged= true;
        this.map[y][x]=code;
}

function Map_addAnimation(animation) {
        var len= this.animations.length;
        for(var i=0; i<len+1; ++i) {
          if(!this.animations[i]) {
                this.animations[i]= animation;
                break;
          }
        }
        animation.begin();
}

function Map_removeAnimation(animation) {
        var len= this.animations.length;
        for(var i=0; i<len+1; ++i) {
          if(this.animations[i]==animation) {
                animation.end();
                this.animations[i]null;
                break;
          }
        }
}

function Map_cleanAnimations(t) {
        var len= this.animations.length;
        for(var i=0; i<len; ++i) {
                if(this.animations[i]
                && this.animations[i].b
                && this.animations[i].b.time<=t) {
                        this.removeAnimation(this.animations[i]);
                }
        }
}

function GameState_addBullet(bullet) {
        map.addAnimation(bullet.animation);
        gameState.bullet= bullet;
}

function GameState_removeBullet(bullet) {
        map.removeAnimation(bullet.animation);
        this.bullet= null;
}

function GameState_addMover(mover) {
        map.addAnimation(mover.animation);
        this.mover= mover;
}

function GameState_removeMover(mover) {
        map.removeAnimation(mover.animation);
        this.mover= null;
}


function Map_distance(a,b){
        var dx= Math.abs(a.x-b.x);
        var dy= Math.abs(a.y-b.y);
        var dist= dx>dy?dx:dy;
        return dist;
};

function Map_clear() {
        var len= this.animations.length;
        for(var i=0; i<len; ++i) {
                if(this.animations[i]) {
                        this.animations[i].end();
                        this.animations[i]null;
                }
        }
}

function Map_setMap(mapRaw) {
        this.ymax= mapRaw.length;
        this.xmax= mapRaw[0].length;
        this.map= mapRaw;
}


function Map(mapRaw) {
        this.animations= new Array();
        this.mapHasChanged= true;
        this.buffer=0;

        //methods
        this.scan= Map_scan;
        this.getXYCode= getXYCode;
        this.setXYCode= setXYCode;
        this.addAnimation= Map_addAnimation;
        this.cleanAnimations= Map_cleanAnimations;
        this.removeAnimation= Map_removeAnimation;
        this.distance= Map_distance;
        this.clear= Map_clear;

        this.setMap= Map_setMap;
        if(mapRaw) {
                this.setMap(mapRaw);
        }
}

function readMap(mapPre) {
        var mapRaw=new Array();

        //IE and gecko: var lines= mapPre.match(/.*/g);
        //opera is funny and divides the lines by whitespace
        var lines= mapPre.match(/S*s*/g);

        var y=0;
        for(var yy=0, ylen=lines.length;yy<ylen;++yy) {
          //debugMsg("line("+(lines[yy].length)+"):"+lines[yy]);
          if(lines[yy].length>2){
            var mapLine= new Array();
            theLine= lines[yy];
            var x=0;
            for(var xx=0, xlen=theLine.length;xx<xlen;++xx) {
                  var thechar= theLine.charAt(xx);
                  if(thechar!='n' && thechar!='r' && thechar>' ') {
                        mapLine[x]= thechar;
                        ++x;
                  }
            }
            mapRaw[y]= mapLine;
            ++y;
           }
        }

        var map= new Map(mapRaw);
        return map;
}


function GameState_isFillable(pos){
         var x= pos.x/step_unit;
         var y= pos.y/step_unit;
         if(x<0||y<0||x>=map.xmax||y>=map.ymax)
             return false;
         var code= map.getXYCode(x,y);
         switch(code) {
                case 'A':
                case '_':
                case ' ':
                         return true;
                         break;
                defaultreturn false;
         }
}

function GameState_pushTo(pos,from) {
        var two= new Coordinate(pos.x,pos.y);
        var step= step_unit;

        var angle=  Math.atan2(-(pos.y-from.y), pos.x-from.x);
        var direction= (180*angle/Math.PI+360)%360;

        if(direction>=&& direction<=45)
                two.x+= step;
        else if(direction>=45 && direction<=135)
                two.y-= step;
        else if(direction>=135 && direction<=225)
                two.x-= step;
        else if(direction>=225 && direction<=315)
                two.y+= step;
        else if(direction>=315 && direction<=360)
                this.image= basename+"_right.gif";
        else
                debugMsg("pushTo bug:"+pos+" "+from);


//         if(from.x<pos.x) {
//                 two.x+= step;
//         } else if(from.x>pos.x) {
//                 two.x-= step;
//         } else if(from.y<pos.y) {
//                 two.y+= step;
//         } else if(from.y>pos.y) {
//                 two.y-= step;
//         }
        return two;
}

function pos_update(pos) {
        //debugMsg("updating to pos:"+pos.toString());
        this.pos= pos;
}

function Bullet_finish() {
        if(!this.hit) {
          //wimp rules:
          //return unused bullet to protagonist
          protagonist.bullets++;
        }
        gameState.removeBullet(this);
}


function Mover_finish() {
        gameState.removeMover(this);
        map.setXYCode(toGridX(this.pos.x),toGridY(this.pos.y),this.avatar);
}

//FIXME: this bullets stuff needs to be rewritten for an action game
function CoordinateVector(pos,direction,steps) {
        this.direction= direction;
        this.dx= Math.cos(Math.PI*direction/180.0);
        this.dy= -Math.sin(Math.PI*direction/180.0);
        this.x= Math.floor(pos.x+this.dx*steps+fudge);
        this.y= Math.floor(pos.y+this.dy*steps+fudge);
        this.toString= Coordinate_toString;
}

function Mover(source,destination,avatar,from) {
        this.source= source;
        this.pos= source;
        this.destination= destination;
        //FIXME: superfluous:pushed from: this.from=from;

        //methods
        this.update= pos_update;
        this.finish= Mover_finish;
        this.avatar= avatar;
}


function Bullet(pos,direction,steps) {
        this.source= pos;
        this.pos= pos;
        this.destination= new CoordinateVector(pos,direction,steps);
        this.direction= (direction+360)%360;

        //methods
        this.update= pos_update;
        this.finish= Bullet_finish;

        if(this.direction>=&& this.direction<=45)
                this.avatar= bullet_right;
        else if(this.direction>=45 && this.direction<=135)
                this.avatar= bullet_up;
        else if(this.direction>=135 && this.direction<=225)
                this.avatar= bullet_left;
        else if(this.direction>=225 && this.direction<=315)
                this.avatar= bullet_down;
        else if(this.direction>=315 && this.direction<=360)
                this.avatar= bullet_right;
        else {
                this.avatar='fuck up('+this.direction+', bad argument)';
                debugMsg(this.avatar);
        }
}

function Attack_finish(animation) {
        map.removeAnimation(this.animation);
        // The following code was for giving the home precedence over attacks
        // Winners are immortal
        if(!gameState.victory) {
                protagonist.die();
        else {
                protagonist.freeze= false;
        }
}

//Well, I try to avoid the super() constructor, since so far I made it without prototypes..
function Attack_init(that,code,source,destination) {
        that.source= source;
        that.pos= source;
        that.destination= destination;

        var angle=  Math.atan2(-(destination.y-source.y), destination.x-source.x);
        that.direction= (180*angle/Math.PI+360)%360;

        //methods
        that.update= pos_update;
        that.finish= Attack_finish;
}

function Attack(code,source,destination) {
        Attack_init(this,code,source,destination);
        switch(code) {
                case 'R':
                case 'S':
                case 'T':
                case 'U':

                case 'r':
                case 's':
                case 't':
                case 'u'this.avatar= getBirdForDirection(this.direction)break;

                case 'P':
                case 'p':

                case 'P.right':
                case 'p.right': this.avatar= getSirenForDirection(this.direction)break;

                default:
                        debugMsg("unknown attacker "+code);
                        this.avatar="unknown attacker";
        }

}



function GameState_hitBullet(bullet,pos) {
        bullet.hit= true;
        gameState.removeBullet(bullet);

        var x= toGridX(pos.x);
        var y= toGridY(pos.y);
        var code= map.getXYCode(x,y);

       if(shrink) {
             map.addAnimation(
                    new Animation(
                            new Snapshot(code,pos,1.0,gameState.time),
                            new Snapshot(' ',pos,0.0,gameState.time+1.0*ticks_unit)
                    )
             );
       }

        map.setXYCode(x,y,' ');
        return true;
}



function GameState_checkBullet(bullet) {

         var x= toGridX(bullet.pos.x);
         var y= toGridY(bullet.pos.y);
         var code= map.getXYCode(x,y);

         //debugMsg("comparing x"+bullet.pos.x+" to x:"+x*step_unit);

         //FIXME: this should be 0.5 or 0.05 instead of 0.40, but..
         var target= new Coordinate(x*step_unit,y*step_unit);

// FIXME: sometimes bullets don't hit
//          debugMsg("bullet at "+bullet.pos+
//                   "target at "+target+
//                   "distance"+map.distance(bullet.pos,target));

         ifmap.distance(bullet.pos,target)
             0.40*step_unit
            ){
                return false;
         }

         switch(code) {
                case 'Z':
                case '[':
                case 'g':
                case 'C':
                        return this.hitBullet(bullet,target);
                        break;
                default:
                     var absorbed= !this.isPassable(bullet.pos,bullet.source,"bullet");
                     if(absorbed)
                        gameState.removeBullet(bullet);
                     return absorbed;
         }
}

function GameState_checkBullets(bullet) {
        ifthis.bullet!= null ) {
                this.checkBullet(this.bullet);
        }
}

function GameState_isPassable(pos,from,by){
         var x= pos.x/step_unit;
         var y= pos.y/step_unit;
         var code= map.getXYCode(x,y);

         switch(code) {
                case 'H':
                        return gameState.home;
                case 'A':
                case '_':
                case ' ':
                          return truebreak;

                case 'I':
                case 'J':
                case 'K':
                case 'i':
                case 'j':
                case 'k':
                          return by=="protagonist"break;
                case 'g':
                          return this.color!="green"break;
                case 'C':
                          return this.color!="red"break;
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                          return falsebreak;
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                          return falsebreak;
                case 'Z':
                case '[':
                          return falsebreak;
                case 'L':
                          return by=="protagonist" && this.isFillable(this.pushTo(pos,from))break;
                case 'l':
                          return false;
                default:
                     debugMsg("isPassable: unknown tile code:"+code);
                     return false;

         }
}

function GameState_beginEnter(pos,from){
         var x= toGridX(pos.x);
         var y= toGridY(pos.y);
         var code= map.getXYCode(x,y);

         switch(code) {
                case 'H':
                        //this.victory= true;
                        break;
                case 'A':
                case '_':
                case ' ':
                         break;
                case 'I':
                case 'J':
                case 'K':
                        if(shrink) {
                               map.addAnimation(
                                      new Animation(
                                              new Snapshot(code,pos,1.0,gameState.time),
                                              new Snapshot(' ',pos,0.0,gameState.time+1.0*ticks_unit)
                                      )
                               );
                        }
                         map.setXYCode(x,y,code.toLowerCase());
                         break;
                case 'g':
                         break;
                case 'C':
                         break;
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                         break;
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                         break;
                case 'Z':
                case '[':
                         break;
                case 'L':
                        var destination= gameState.pushTo(pos,from);
                         // place dummy
                         map.setXYCode(toGridX(destination.x),toGridY(destination.y),'l');
                         //empty tile
                         map.setXYCode(x,y,' ');
                         //begin animation
                          var range= 1;
                          var mover= new Mover(pos,destination,code,from);
                          var animation=
                                 new Animation(
                                          new Snapshot(mover.avatar,mover.source,1.0,gameState.time),
                                          new Snapshot(mover.avatar,mover.destination,1.0,gameState.time+range*ticks_unit),
                                          mover
                                  );

                          mover.animation= animation;
                          gameState.addMover(mover);

                         break;
                default:
                     debugMsg("beginEnter: unknown tile code:"+code);
         }
}

function removeBall(code,x,y) {
        gameState.sirens= true;
        map.setXYCode(x,y,' ');
}

function GameState_finishEnter(pos,from){
         var x= toGridX(pos.x);
         var y= toGridY(pos.y);
         var code= map.getXYCode(x,y);

         switch(code) {
                case 'H':
                        if(!protagonist.freeze) {
                                this.victory= true;
                                protagonist.setMode("victorious");
                        }
                        break;
                case 'A':
                case '_':
                case ' ':
                        break;
                case 'I':
                case 'J':
                case 'K':
                        break;
                case 'i':
                        removeBall(code,x,y);
                        protagonist.bullets++;
                        break;
                case 'j':
                        this.color="green";
                        removeBall(code,x,y);
                        break;
                case 'k':
                        this.color="red";
                        removeBall(code,x,y);
                        break;
                case 'g':
                        break;
                case 'C':
                        break;
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                        break;
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                        break;
                case 'Z':
                case '[':
                        break;
                case 'L','l':
                        //stop animation
                        //rest on new tile
                        break;
                default:
                     debugMsg("finishEnter: unknown tile code:"+code);
         }
}


function GameState_filterCode(code,x,y) {
             switch(code) {
                case 'H':
                          if(this.home)
                              code= code.toLowerCase();
                          break;
                case 'g':
                          ifthis.color!="green")
                              code=' ';
                          break;
                case 'C':
                          ifthis.color!="red")
                              code=' ';
                          break;
                case 'P':
                          if(this.sirens) {
                              code= code.toLowerCase();
                              //FIXME: bug is: map has to change to cause redraw, so a second map is needed ..
                              if(protagonist.pos.x>x) {
                                    code+= '.right';
                              }
                          }
                          break;
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                          if(this.birds)
                              code= code.toLowerCase();
                          break;
                case 'i':
                case 'j':
                case 'k':
                          code=' ';
                          break;
                case 'l':
                          code=' ';
                          break;
             }

             return code;
}

function GameState_mayShoot(){
        return this.bullet==null;
}


function GameState_checkTriggers() {
        //TODO: check if one has to check for i,j,k too
        if(!map.scan('I')&&!map.scan('J')&&!map.scan('K')) {
                this.birds= true;
                this.home= true;
        else {
                 this.birds= false;
                 this.home= false;
        }
}

//trap functions

//currently there is only one type of ray
function checkForObstacle(code,pos) {
        //hack to disallow rays pushing
        var result=!gameState.isPassable(pos,pos,"ray");
        return result;
}

function checkForTarget(code,pos,source) {
        var hit= map.distance(protagonist.pos, pos)<=step_unit/2;
        if(hit) {
               protagonist.freeze= true;
               // start kill animation
               //empty tile
               map.setXYCode(x,y,' ');
               //begin animation
                var range= Math.floor(map.distance(source,protagonist.pos)/(2*step_unit))+1;
                var mover= new Attack(code,source,protagonist.pos);
                var animation=
                       new Animation(
                                new Snapshot(mover.avatar,mover.source,1.0,gameState.time),
                                new Snapshot(mover.avatar,mover.destination,1.0,gameState.time+range*ticks_unit),
                                mover
                        );

                mover.animation= animation;
                map.addAnimation(mover.animation);
        }
        return hit;
}


function checkTrap(code,direction,checkForObstacle,checkForTarget) {
        var pos= null;
        whilepos= map.scan(code,pos) ) {
                //debugMsg("checking trap "+code+" at "+pos);
                scanTrap(code,direction,pos,checkForObstacle,checkForTarget);
        }
}

function checkTraps() {
        //debugMsg("BEGIN checkTraps");
        if(this.birds) {
          checkTrap('R',180,checkForObstacle,checkForTarget);
          checkTrap('S',90,checkForObstacle,checkForTarget);
          checkTrap('T',0,checkForObstacle,checkForTarget);
          checkTrap('U',270,checkForObstacle,checkForTarget);
        }

        //debugMsg("end checkTraps birds");

        if(this.sirens) {
          checkTrap('P',180,checkForObstacle,checkForTarget);
          checkTrap('P',90,checkForObstacle,checkForTarget);
          checkTrap('P',0,checkForObstacle,checkForTarget);
          checkTrap('P',270,checkForObstacle,checkForTarget);
        }

        //debugMsg("end checkTraps sirens");

        if(this.home) {
           var pos= map.scan(code_home);
           if(pos && map.distance(protagonist.pos, pos)<step_unit/&& !protagonist.freeze ) {
                this.victory= true;
                protagonist.setMode("victorious");
           }
        }
        //debugMsg("END checkTraps");
}


function GameState() {
         this.time= 0;
         this.color="green";
         this.sirens= false;
         this.birds= false;
         this.home= false;
         this.victory= false;
         this.bullet= null;
         this.mover= null;
        //methods
        this.isPassable= GameState_isPassable;
        this.isFillable= GameState_isFillable;
        this.pushTo= GameState_pushTo;
        this.filterCode= GameState_filterCode;

        this.beginEnter= GameState_beginEnter;
        this.finishEnter= GameState_finishEnter;

        this.addBullet= GameState_addBullet;
        this.hitBullet= GameState_hitBullet;
        this.checkBullet= GameState_checkBullet;
        this.checkBullets= GameState_checkBullets;
        this.removeBullet= GameState_removeBullet;
        this.mayShoot= GameState_mayShoot;

        this.addMover= GameState_addMover;
        this.removeMover= GameState_removeMover;

        this.checkTriggers= GameState_checkTriggers;
        this.checkTraps= checkTraps;
}


function Facings() {
        this.right= 0;
        this.left= 180;
        this.up= 90;
        this.down= 270;
}
var facings= new Facings();

var keybuffer= new Array();

var Agent_tick= function (ticks) {

        //else used, so no diagonal movement allowed
        var nuarrived= true;
        var step= step_unit*ticks/ticks_unit;
        if(this.pos.x+step<=this.destination.x) {
                this.pos.x+= step;
                nuarrived= false;
        }
        else if(this.pos.x-step>=this.destination.x) {
                this.pos.x-= step;
                nuarrived= false;
        }
        else if(this.pos.y+step<=this.destination.y) {
                this.pos.y+= step;
                nuarrived= false;
        }
        else if(this.pos.y-step>=this.destination.y) {
                this.pos.y-= step;
                nuarrived= false;
        }

        var togox= Math.abs(this.destination.x-this.pos.x)/step_unit;
        var togoy= Math.abs(this.destination.y-this.pos.y)/step_unit;

        this.completion= 1.0-(togox>togoy?togox:togoy);

        //debugMsg("Arrived:"+this.arrived+" "+(100*this.completion)+"% x:"+this.pos.x+" tox:"+this.destination.x+" y:"+this.pos.y+" toy:"+this.destination.y+" step="+step);

        //process keys

        //optional: read keyboard after 50%
        if(this.completion<0.5) {
                 keybuffer= new Array();
        }

        //detect flank
        if(nuarrived & !this.arrived) {
             //optional: empty key buffer on arrival
             //keybuffer= new Array();
             //debugMsg("finish enter "+this.destination.x+","+this.destination.y);
             gameState.finishEnter(this.destination,this.source);
             this.setMode("");
        }

        if(nuarrived && keybuffer.length>&& !this.freeze) {
          var keyCode= keybuffer[0];

          //optional:use key buffer
           if(keybuffer.shift) {
                 keybuffer.shift();
           else
                 keybuffer= new Array();

          if (keyCode == key_right) {
            //r += 'arrow right';
            this.destination.x= this.pos.x+step_unit;
            this.setFacing(facings.right);
            this.setMode("moving");
          else if (keyCode == key_down) {
            //r += 'arrow down';
            this.destination.y= this.pos.y+step_unit;
            this.setFacing(facings.down);
            this.setMode("moving");
          else if (keyCode == key_up) {
            //r += 'arrow up';
            this.destination.y= this.pos.y-step_unit;
            this.setFacing(facings.up);
            this.setMode("moving");
          else if (keyCode == key_left) {
            //r += 'arrow left';
            this.destination.x= this.pos.x-step_unit;
            this.setFacing(facings.left);
            this.setMode("moving");
          else if (keyCode == key_fire) {
            // shoot
            if(this.bullets>&& gameState.mayShoot() ) {
                    var range= 2.44;
                    var bullet= new Bullet(this.pos,this.facing,step_unit*range);
                    var animation=
                           new Animation(
                                    new Snapshot(bullet.avatar,bullet.source,1.0,gameState.time),
                                    new Snapshot(bullet.avatar,bullet.destination,1.0,gameState.time+range*ticks_unit),
                                    bullet
                            );

                    bullet.animation= animation;
                    gameState.addBullet(bullet);
                    this.bullets--;
            }
          }

          this.destination.clip();
          if!gameState.isPassable(this.destination,this.pos,"protagonist") ) {
              this.destination.x= this.pos.x;
              this.destination.y= this.pos.y;
          }else {
              this.source.x= this.pos.x;
              this.source.y= this.pos.y;

              //fixme
              //this should not be needed
              //this.destination.gridLock();
              //this.pos.gridLock();
              gameState.beginEnter(this.destination,this.pos);
          }
        }

        this.arrived= nuarrived;
}

function Agent_setFacing(direction) {
        if(this.facing!=direction) {
                this.facing= direction;
                this.hasChanged= true;
        }
}

function Agent_setMode(mode) {
        if(this.mode!=mode) {
                this.mode= mode;
                this.hasChanged= true;
        }
}


function Agent_init(agent,name,x,y) {
        agent.name= name;
        agent.buffer= 0;

        agent.source= new Coordinate(x,y);
        agent.pos= new Coordinate(x,y);
        agent.destination= new Coordinate(x,y);

        agent.arrived= true;
        agent.completion= 1.0;
        agent.hasChanged= true;

        agent.facing= 0;//right
        //methods
        agent.tick= Agent_tick;
        agent.setFacing= Agent_setFacing;
        agent.setMode= Agent_setMode;
}

function Agent(name,x,y) {
        Agent_init(this, name, x ,y);
}


// function mapFromTemplate(map,initializer) {
//          var tis= new Array(map.length);
//          for(var y=0;y<tis.length;++y) {
//            tis[y]= new Array(map[y].length);
//              for(var x=0;x<tis[y].length;++x) {
//                tis[y][x]= initializer;
//              }
//          }
//          return tis;
// }


//
// Please don't much use browser detection, only for final polish
//
function setBrowser() {
        ifnavigator.appName=="Microsoft Internet Explorer" ) {

                debugMsg("like IE!");// or opera or any faker ..
                browsercaps.doublebuffer= true;

                //this is for IE, not opera FIXME!
                //IE is very slow with fixed and divs and scrolls anyway
                //Actually it seems IE is slow with big windows
                var wrapper= document.getElementById("mapwrapper");
                if(wrapper&&wrapper.style&&wrapper.style.position) {
                  wrapper.style.position="";
                }
        }

        ifnavigator.product=="Gecko" ) {
                debugMsg("like Gecko!");
                // switch on HTML normalization to avoid flicker.
                browsercaps.fixHTML= true;
        }
}

function preload() {
   var dummy= new Agent("dummy",0,0);
   dummy.mapToImage= Protagonist_mapToImage;

   dummy.setFacing(0);
   dummy.setMode("victorious");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setMode("");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setMode("moving");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setFacing(90);
   dummy.setMode("");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setMode("moving");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setFacing(180);
   dummy.setMode("");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setMode("moving");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setFacing(270);
   dummy.setMode("");
   loadImagesimagePath(dummy.mapToImage() ) );
   dummy.setMode("moving");
   loadImagesimagePath(dummy.mapToImage() ) );

   // only loading what can appear later
   loadImagesmapCodeToImage('h') );

   loadImagesmapCodeToImage('r') );
   loadImagesmapCodeToImage('s') );
   loadImagesmapCodeToImage('t') );
   loadImagesmapCodeToImage('u') );

   loadImagesmapCodeToImage(bird_right) );
   loadImagesmapCodeToImage(bird_down) );
   loadImagesmapCodeToImage(bird_left) );
   loadImagesmapCodeToImage(bird_up) );


   loadImagesmapCodeToImage('P') );
   loadImagesmapCodeToImage('p') );
   loadImagesmapCodeToImage('P.right') );
   loadImagesmapCodeToImage('p.right') );
   loadImagesmapCodeToImage('p.moving.left') );
   loadImagesmapCodeToImage('p.moving.up') );
   loadImagesmapCodeToImage('p.moving.down') );
   loadImagesmapCodeToImage('p.moving.right') );

   loadImagesmapCodeToImage('g') );
   loadImagesmapCodeToImage('C') );

   loadImagesmapCodeToImage('bullet_big' ) );
   loadImagesmapCodeToImagebullet_right ) );
   loadImagesmapCodeToImagebullet_up ) );
   loadImagesmapCodeToImagebullet_left ) );
   loadImagesmapCodeToImagebullet_down ) );

   debugMsg("commanded preloading of "+myImages.length+" images");
}

function Protagonist_die() {
    this.shrink= true;
    if(typeof this.zoom!='number') {
            this.zoom= 1.0;
    }
}

function Protagonist_tick(ticks) {
           //well, I worked without prototypes so far, I'm not going to start it now
           //this.prototype.tick(ticks)
           if(this.shrink) {
                this.zoom-= ticks/ticks_unit;
                this.hasChanged= true;
           }
}

function Protagonist(ppos) {
   Agent_init(this,"protagonist",ppos.x,ppos.y);
   this.draw= Protagonist_draw;
   this.bullets=0;
   this.mapToImage= Protagonist_mapToImage;
   this.freeze= false;
   this.die= Protagonist_die;
   this.zoom= null;
   this.shrink= false;
   this.ptick= Protagonist_tick;
}

function resetGameState() {
   gameState= new GameState();

   var ppos= map.scan(code_start);
   if(!ppos) {
          alert("this map lacks a starting position");
          ppos= new Coordinate(0,0);
   }

   protagonist= new Protagonist(ppos);
}

function layout2(left) {
  var div;
  //controls
  var div= document.getElementById("controls");
  div.style.top= 20;
  div.style.left= left;
  //scratchpad
  var div= document.getElementById("help");
  div.style.top= 150;
  div.style.left= left;

  var div= document.getElementById("by");
  div.style.top= 210;
  div.style.left= left;

  var div= document.getElementById("sp0");
  div.style.top= 360;
  div.style.left= left+300;

  //debug
  var div= document.getElementById("debug");
  div.style.left= left;

}

function resizeDiv(name,width,height) {
    var div= document.getElementById(name);
    if(div && div.style) {
      div.style.width= width;
      div.style.height= height;
    }
}

function configure() {
  var search= window.location.search;

  ifsearch.match(/debug=1/) ){
        debug= true;
        hideDebug= false;
  }

  ifsearch.match(/shrink=0/) ){
        shrink= false;
  }

  ifsearch.match(/size=32/) ){
        map_left= 20;
        cell_width= 32;
        cell_height= 32;
        setTimeout("layout2("+(cell_width*12+20+map_left)+")",1);
        if(!debug) {
                resizeDiv("mapwrapper",1000-206,580-170);
        }
  }

  ifsearch.match(/size=48/) ){
        cell_width= 48;
        cell_height= 48;
        setTimeout("layout2("+(cell_width*12+28+map_left)+")",1);
        resizeDiv("mapwrapper",1000,580-20);
  }

  ifsearch.match(/size=64/) ){
        cell_width= 64;
        cell_height= 64;
        setTimeout("layout2("+(cell_width*12+28+map_left)+")",1);
        resizeDiv("mapwrapper",1000+210,580+100);
  }

  var debugDiv= document.getElementById("debugoutput");
  debugDiv.style.visibility= debug?'visible':'hidden';

  var debugonoffDiv= document.getElementById("debugonoff");
  debugonoffDiv.style.visibility= (!hideDebug)?'visible':'hidden';
}

function resizeStatusbar(map){
        var statusDiv= document.getElementById("status");
        var width= (map.xmax)*cell_width;
        if(width<minStatusbarWidth)
           width= minStatusbarWidth;
        if(statusDiv && statusDiv.style) {
                statusDiv.style.width= width;
        }
        var height= bullet_height>status_font?bullet_height:status_font;
        statusDiv.style.height= height;
        statusDiv.style.top= map_top-height;
}


function resetGUI() {
         resizeStatusbar(map);
         var mapObjO= document.getElementById("map"+0);
         if(mapObjO) {
                mapObjO.innerHTML="";
         }
         var mapObjN= document.getElementById("map"+1);
         if(mapObjN) {
                mapObjN.innerHTML="";
         }
         keybuffer= new Array();
         scroll(0,0);
         window.focus();
}

function init() {

         configure();

         //The code is supposed to work in all browsers, but we can improve performance sometimes if we know browser
         setBrowser();
         preload();
         placeMap();
         drawStatusBullets();
         loadSetIndex();

         map= new Map(mapRaw);
         resetGameState();
         resetGUI();

         setTimeout("loadNextMap()",4999);

         mainLoop();

}

function mainLoop() {
         if(mainProceed) {

           protagonist.tick(theTick);
           protagonist.ptick(theTick);
           gameState.time+=theTick;

           drawMap();

           drawAnimations();
           map.cleanAnimations(gameState.time);
           gameState.checkBullets();

           if(gameState.time%ticks_unit<theTick) {
             gameState.checkTriggers();
             //FIXME: the tiles affected by traps should be marked,
             //so that processing is faster
             gameState.checkTraps();
           }

           if(gameState.time%(ticks_unit/3)<theTick) {
             drawStatus();
           }
           //drawMap();//FIXME: This takes too long e.g. push box
           protagonist.draw();
         }
         setTimeout("mainLoop()",mainDelay);
}

function drawAnimations() {
         for(var i=0;i<map.animations.length;++i) {
                if(map.animations[i]) {
                        map.animations[i].animate(gameState.time);
                }
         }
}

var hashImages= new Array();

function imageFor(imgsrc,title,height,width,id) {
        var key= imgsrc+" "+title+" "+height+" "+width;
        var fullkey= key+" "+id;

        saved= hashImages[fullkey];
        if(saved) {
                return saved;
        }

        saved= hashImages[key];
        if(saved) {
                saved= saved.cloneNode(false);
                hashImages[fullkey]= saved;
                return saved;
        }

        var img= document.createElement("IMG");
        img.setAttribute("src",imgsrc);
        img.setAttribute("alt",title);
        img.setAttribute("title",title);
        img.setAttribute("height",""+height);
        img.setAttribute("width",""+width);
        img.setAttribute("style","margin: 0px 0px 0px 0px;");
        img.setAttribute("border","0");

        hashImages[key]= img;
        hashImages[fullkey]= img;

        return img;
}

function mapCodeToNode(code,width,height,forId) {
    var node= null;
    var imgsrc= mapCodeToImage(code);
    if(imgsrc) {
      node= imageFor(imgsrc,code,height,width,forId);
    else {
         var box="";
         switchcode ){
                case '_':
                case ' ':
                case 'A':
                     box= "&nbsp;";
                     break;
                default:
                  box= "&nbsp;"+code+"&nbsp;";
         }
         node= document.createElement("SPAN");
         node.innerHTML= box;
    }
    return node;
}

function clearChildren(node) {
        while(node.firstChild != null ) {
                node.removeChild(node.firstChild);
        }
}

function makeMap() {

    var text="";

    text+='<table border=cellpadding=cellspacing=style="background-image:url(''+mapCodeToImage("background")+'')">';
    var tr= "<tr height="+cell_height+">";
    var td= "<td class=map width="+cell_width+">";

    for(y=0;y<map.ymax;++y) {
    text+= tr;
    for(x=0;x<map.xmax;++x) {
        text+= td;

        var code= map.map[y][x];

        code= gameState.filterCode(code,x*step_unit,y*step_unit);

        var box= mapCodeToHtml(code,cell_width,cell_height);

        text+= box;
        text+="</td>";
    }
    text+="</tr>";
    }
    text+="</table>";

    return text;
}

function placeMap(){
   var mapObjO= document.getElementById("map0");
   mapObjO.style.left=map_left;
   mapObjO.style.top=map_top;

   var mapObjN= document.getElementById("map1");
   mapObjN.style.left=map_left;
   mapObjN.style.top=map_top;

   var mapObjO= document.getElementById("status");
   mapObjO.style.left=map_left;
}

function drawMap() {
         if(map.mapHasChanged) {
                map.mapHasChanged=false;

         var starttime;
         if(debug && debugall) {
                starttime= new Date();
         }

         ifbrowsercaps.doublebuffer ) {
           //Opera and IE seem to double buffer anyway
           var mapObjO= document.getElementById("map"+map.buffer);
           mapObjO.innerHTML= makeMap();
         }else{
           //doublebuffering for Gecko/FireFox
           var mapObjO= document.getElementById("map"+map.buffer);
           map.buffer^=1;
           var mapObjN= document.getElementById("map"+map.buffer);
           mapObjN.style.zIndex=0;

           mapObjN.innerHTML= makeMap();
           mapObjO.style.zIndex=5;
           mapObjN.style.zIndex=10;
           mapObjO.style.zIndex=0;
         }

         if(debug && debugall) {
                var endtime= new Date();
                debugMsg("redrawing map took "+(endtime-starttime)+"ms");
         }


         }// map has changed


}

function Protagonist_draw() {
         var mapObjN= document.getElementById(this.name+this.buffer);
         //this.buffer= this.buffer^1;
         //var mapObjO= document.getElementById(this.name+this.buffer);
         var left= toScreenX(this.pos.x/step_unit);
         var top= toScreenY(this.pos.y/step_unit);
         //this redraw often isn't necessary => don't redraw
         if(this.hasChanged) {
           this.hasChanged= false;
           //FIXME: protagonist width and height are a hack
           var width= cell_width;
           var height= cell_height;

           if((typeof this.zoom)=='number') {
                zwidth= Math.floor(width*this.zoom+fudge);
                zheight= Math.floor(height*this.zoom+fudge);
                left+= Math.floor((width-zwidth)/2);
                top+= Math.floor((height-zheight)/2);
                width= zwidth;
                height= zheight;
           }

           var text;
           if(width<1||height<1) {
                text='';
                this.shrink= false;
           else {
                text= '<img src="'+imagePath(protagonist.mapToImage())+'" alt="@" border="0" height="'+Math.floor(height)+'" width="'+Math.floor(width)+'" style="margin:0px;">';
           }
           mapObjN.innerHTML= text;
         }

         mapObjN.style.left= left;
         mapObjN.style.top= top;

         //mapObjO.style.visibility="hidden";
         //mapObjN.style.visibility="visible";
}

function checkArrows (field, evt) {
  var keyCode =
    document.layers ? evt.which :
    document.all ? event.keyCode :
    document.getElementById ? evt.keyCode : 0;


  var r = '';
  if (keyCode == 39 || keyCode == 57388 || String.fromCharCode(keyCode).toLowerCase()=='d') {
    r += 'arrow right';
    keyCode = key_right;
  }
  else if (keyCode == 40 || keyCode == 57386 || String.fromCharCode(keyCode).toLowerCase()=='s') {
    r += 'arrow down';
    keyCode = key_down;
  }
  else if (keyCode == 38 || keyCode == 57385 || String.fromCharCode(keyCode).toLowerCase()=='w') {
    r += 'arrow up';
    keyCode = key_up;
  }
  else if (keyCode == 37  || keyCode == 57387 || String.fromCharCode(keyCode).toLowerCase()=='a') {
    r += 'arrow left';
    keyCode = key_left;
  else ifkeyCode == 220 || keyCode == 49 || keyCode== 70 || keyCode== 81 || keyCode== 45) {
    //^1FQ0
    r += 'action key 1';
    keyCode = key_fire;
  else ifString.fromCharCode(keyCode).toLowerCase() == 'r' ) {
    //r
    r += 'reload';
    loadFrom(lastLevel);
  }

  if(r!='') {
        if(keybuffer.push) {
                keybuffer.push(keyCode);
        }else{
               keybuffer[keybuffer.length]= keyCode;
        }

        //I don't manage to intercept keyboad events ..
        //scroll(0,0);
        return true;
  }

  //      var whichChar=String.fromCharCode(keyCode);
  r += ' ' + keyCode;
  if(debug) {
            window.status = r;
  }
  return false;
}

function focusOn(name) {
         var div= document.getElementById(name);
         if(div && div.focus) {
                div.focus();
         }
}

var focus_restart= false;

var bullets_number= 10;

// some versions of IE don't like loading many images, so we keep some static bullets

function drawStatusBullets() {
        var statusDiv= document.getElementById("status");
        text="";
        var x= 0;
        for(var i=0; i< bullets_number; ++i) {
          text+= '<div id="statusbullet'+i+'" style="position:absolute; left:'+x+';  z-index:16; visibility:hidden">';
          text+= '<img src="'+mapCodeToImage("bullet_big")+'" alt="o" border="0" height="'+bullet_height+'" width="'+bullet_width+'">';
          text+= '</div>';
          x+= bullet_width;
        }
        text+= '<div id="statustext" style="position: absolute; z-index:17;">&nbsp;</div>';

        statusDiv.innerHTML= text;
}


var last_show_bullets= -1;
function drawStatus() {
         var statusDiv= document.getElementById("status");
         var statustextDiv= document.getElementById("statustext");

         var text="";

         var show_bullets= protagonist.bullets;

         if(gameState.victory) {
            text='<b style="color: rgb(127, 255, 127);">You won!</b>';
            show_bullets= 0;
         else if(protagonist.freeze) {
            text='<b style="color: red;">You loose! Failure lifts its ugly head.<a href="javascript:loadFrom(lastLevel);">restart!</a></b>';
            show_bullets= 0;
            if(!focus_restart) {
                focusOn("restart");
                focus_restart= true;
            }
         }

         if(show_bullets==&& text=="") {
                statusDiv.style.visibility= 'hidden';
         else {
                statusDiv.style.visibility= 'visible';
         }

         if(show_bullets!=last_show_bullets) {
            last_show_bullets= show_bullets;
            for(var i=0;i<bullets_number;++i) {
                    var bulletDiv= document.getElementById("statusbullet"+i);
                    var visibility= i<show_bullets?'visible':'hidden';
                    if(visibility!=bulletDiv.style.visibility) {
                            bulletDiv.style.visibility= visibility;
                            bulletDiv.style.zIndex= 16;
                    }
            }
         }
         if(show_bullets>bullets_number) {
            text= '<b>'+show_bullets+'</b>';
         }

         //uncomment this or fix html manually to stop flickering
         if(text!=statustextDiv.innerHTML) {
                //debug flickering
                if(debug && debugall) {
                        debugHTMLrep(text);
                }
                statustextDiv.innerHTML= text;
         }
}

//
// loading levels
//

function loadFrom(url){
     focus_restart= false;
     var scratchpadFrame= document.getElementById("scratchpadFrame");
     scratchpadFrame.src= url+'?'+Math.floor(Math.random()*1000000);
}


//
//fix IE mangled select values(remove trailing spaces)
//
function fixIE(value){
        if(value) {
                whilevalue.charCodeAt(value.length-1)<=32 ) {
                        value= value.substr(0,value.length-1);
                }
        }
        return value;
}

var levelSet= null;

function gotSets(setNames) {
        var setSelect= document.getElementById("setname");
        var options= setSelect.options;
        while(options.length>1)
                setSelect.remove(1);
//         var anOption = document.createElement("OPTION")
//         anOption.text = "--choose--";
//         anOption.value = "";
//         options.add(anOption);
        for(var i=0; i<setNames.length; ++i) {
                var anOption = document.createElement("OPTION")
                anOption.text = setNames[i];
                anOption.value = setNames[i];
                options.add(anOption);
        }
}

function gotLevels(levelNames) {
        var levelSelect= document.getElementById("levelname");
        var options= levelSelect.options;
        while(options.length>1)
                levelSelect.remove(1);
        for(var i=0; i<levelNames.length; ++i) {
                var anOption = document.createElement("OPTION")
                anOption.text = levelNames[i];
                anOption.value = "maps/"+levelSet+"/"+fixIE(levelNames[i])+'.html';
                options.add(anOption);
        }
}

//
// FIXME: some browser might require fiddling with selectedIndex
//

function loadSetIndex() {
     loadFrom("maps/list.html");
}


function loadSet(setInput) {
         var value= fixIE(setInput.value);
         if(value) {
             loadFrom("maps/"+value+"/list.html");
             levelSet= value;
             setInput.blur();
         }
}

function loadLevel(levelInput) {
        var level= fixIE(levelInput.value);
         if(level) {
             lastLevel= level;
             loadFrom(level);
             levelInput.blur();
         }
}

function loadNextMap(){
        var levelSelect= document.getElementById("levelname");
        var levelOptions= levelSelect.options;
        var selected= levelOptions.selectedIndex;
        ++selected;
        ifselected>=levelOptions.length ){
            var setSelect= document.getElementById("setname");
            var setOptions= setSelect.options;
            var selected= setOptions.selectedIndex;
            ++selected;
            setOptions.selectedIndex= selected>=setOptions.length?1:selected;
            loadSet(setSelect);
            setTimeout("loadNextMap()",1999);
        }else{
            levelOptions.selectedIndex= selected;
            loadLevel(levelSelect);
        }
}

function killBill(fileName) {
        return fileName.replace(/\/gi,"/");
}

function loadFileFromFileinput(fileInput) {
         var fileName=fileInput.value;
         debugMsg("loading file:"+fileName);
         if(fileName) {
             fileName= "file:///"+killBill(fileName);
             loadFrom(fileName);
             fileInput.blur();
             return true;
         }else {
             return false;
         }
}

function loadFileFromUrlinput(urlInput) {
        var fileName=urlInput.value;
         debugMsg("loading url:"+fileName);
         if(fileName) {
             if(!fileName.match(/^http:/) ) {
                fileName= "http://"+fileName;
             }
             loadFrom(fileName);
             urlInput.blur();
             return true;
         }else {
             return false;
         }
}

function loadFileFromForm(form) {
//         if(! loadFileFromFileinput(form.filename) ) {
//             if(! loadFileFromUrlinput(form.url) )
              loadLevel(form.levelname);
//         }
        form.load.blur();
}

function loadFileFromFormname(formname) {
        loadFileFromForm(document[formname]);
}

function gotMaps(newMapsPre) {
        //only the first map is used currently
        if(newMapsPre.length>0) {
                if(map) {
                        map.clear();
                }
                map= readMap(newMapsPre[0]);
                resetGameState();
                resetGUI();
        }
}

//code graveyard, usefull stuff that I don't know by heart

//        document.body.addEventListener("keydown", processKeypress, true);


// code sample
//for (prop in scratchpadFrame) {
//       debugMsg(prop+":"+scratchpadFrame[prop]);
//}
//

// code sample, adding event listeners
// function addEvent(obj, evType, fn){
//  if (obj.addEventListener){
//    obj.addEventListener(evType, fn, true);
//    return true;
//  } else if (obj.attachEvent){
//    var r = obj.attachEvent("on"+evType, fn);
//    return r;
//  } else {
//    return false;
//  }
// }

//-->
</script>

<!-- bgsound -->
<script language="JavaScript" type="text/JavaScript"><!--
// See also: http://www.ricocheting.com/js/music.html
// var music="gayatri.mp3";
// if(navigator.product=="Gecko"){
// document.write('<embed src="'+music+'" autostart="true" loop="true" controls="SmallConsole" width=145 height=15></embed>');}
// else if(navigator.appName=="Microsoft Internet Explorer"){
// document.write('<embed src="'+music+'" autostart="true" loop="true" width=285 height=25></embed>');}
// else{
// document.write('<embed src="'+music+'" autostart="true" loop="true"></embed>');}
// //-->
// </script>
<!--noscript><embed src="gayatri.mp3" autostart="true" loop="true"></embed></noscript>
<noembed><bgsound src="gayatri.mp3" loop=true></noembed-->



</head>
<body onLoad="init();" onKeyDown="checkArrows(this,event)" style="margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px;">
<!-- FIXME: this fixed div(against scrollingmakes IE incredibly slow -->
<div id="mapwrapper" style="position:fixed; top:0px; left:0px; height:400px; width:794px; overflow:hidden; background:#aa9988">

<!--begin screen-->
<div id="map0" style="position:absolute; z-index:10">
</div>
<div id="map1" style="position:absolute; z-index:10">
</div>
<div id="status" style="position:absolute; left:20px; width:384px; height:12px; z-index:15; background: rgb(0, 0, 127); visibility: hidden;" class="map">&nbsp;
</div>
<div id="protagonist0" style="position:absolute; z-index:20">
</div>
<div id="protagonist1" style="position:absolute; z-index:20; visibility:hidden;">
</div>



<div id="animation0" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation1" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation2" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation3" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation4" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation5" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation6" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation7" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation8" style="position:absolute; z-index:40; visibility:hidden;"></div>
<div id="animation9" style="position:absolute; z-index:40; visibility:hidden;"></div>


<div id="help" style="position:absolute;left:40px;width:320px;top:350px;">
<center>
Use the arrow keys to move. Also try "awds"-keys.
<br> 0,Q,F,1, or ^ shoots a bullet. R is restart. <A href="index.html" target="help">Help!</A>
</center>
</div>

<div id="by" style="position:absolute; left:452px; width:340px; top:150px;">
<center>
<h3>HylZee</h3>
game idea:<A href="http://www.huelsmann.net/">Roland G. H&uuml;lsmann</A>
<br>javascript coding:<A href="mailto:peter.schafer@gmail.com">(c)2004 Peter Sch&auml;fer</A>
<br>level design, set A:<A href="http://www.huelsmann.net/">Roland G. H&uuml;lsmann</A>
<br>level design, set B:<A href="mailto:peter.schafer@gmail.com">Peter Sch&auml;fer</A>
<br>Browsers tested: Opera7, IE5, FireFox 1.0
<br>This software is licensed under the GPL.
</center>
<p><!-- music -->
</div>

<!--end screen-->

<div id="controls" style="position:absolute; z-index:10; left:452px; top:20px; width:440px; height:90px;">
  <table border="0" class="controlembossed">
  <tr><td>

  <form name="form_controls" method="POST" enctype="multipart/form-data" style="margin:4px 4px 4px 4px" action="javascript:loadFileFromFormname('form_controls');">
  <table border="0" style="background: #66CC33;">
  <tr><td>
  level set:
  </td><td>
  <select id="setname" name="setname" OnChange="javascript:loadSet(this);return false;">
        <option value="">-- choose --</option>
        <option value="B">B</option>
  </select>
  level
  <select id="levelname"  name="levelname" OnChange="javascript:loadLevel(this);return false;">
        <option value="">-- choose --</option>
  </select>
<!--
  </td></tr><tr><td>
  load url:
   </td><td>
 <input type=text name="url" OnChange="javascript:loadFileFromUrlinput(this);return false;">
  </td></tr><tr><td>
  load file:
  </td><td>
  <input type=file name="filename" OnChange="javascript:loadFileFromFileinput(this);return false;">
-->
  </td></tr><tr><td align="center" colspan=2>
   <input type=button name="nextmap" value="next map" OnClick="javascript:
loadNextMap();">
   <input type=button id="restart" name="restart" value="restart last" OnClick="javascript:loadFrom(lastLevel);">
   <input type=button name="load" value="load" OnClick="javascript:loadFileFromFormname('form_controls');">
   &nbsp; &nbsp; <input type=reset value="reset" OnClick="javascript:return true;">
  </td></tr>
  </table>
  </form>

  </td></tr>
  </table>
</div>

<!-- load data with javascript -->
<!-- I have made this visible, since browsers might consider it bad security otherwise -->
<div id=sp0 style="position:absolute; z-index:0; visibility:visible; top:350px; left:600px; height:40px; width:40px; overflow:hidden">
        <iframe src="about:blank" id="scratchpadFrame" height="20" width="40"></iframe>
        <!-- scratchpad to convert innerHTML to browser representation -->
        <div id="scratchpad" style="position:absolute; z-index:0; visibility:visible; overflow: hidden;"></div>
</div>

<div id="debug" style="position:absolute;left:452px;width:300px;">
      <div id="debugonoff" style="position:absolute;width:300px;height:30px;top:155px; background:#EEEEEE; visibility: hidden;">
      <center>
      <form action="javascript:return false;" style="margin: 0px;">
       <input type=button value="debug on/off" OnClick="javascript:debug=!debug;document.getElementById('debugoutput').style.visibility='visible'; this.value='debug '+(debug?'off':'on')">
       <input type=button value="clear" OnClick="javascript:document.getElementById('debug').innerHTML=''">
       <input type=button value="proceed/stop" OnClick="javascript:mainProceed=!mainProceed;this.value=(mainProceed?'stop':'proceed')">
      </form>
      </center>
      </div>

      <div id="debugoutput" style="position:absolute; top:190px; width:300px; height:320px; overflow:auto; background:#EEEEEE; font-family: fixed; visibility: hidden;">
      <h4>debug log</h4>
      </div>
</div>

</div>

</body>
</html>



           
       

Download : Download nav_hylZee-001.zip nav_hylZee-001.zip


-

Leave a Comment / Note


 
Verification is used to prevent unwanted posts (spam). .

Follow Navioo On Twitter

JAVASCRIPT DHTML TUTORIALS

 Navioo Page Components
» Game