Mega Code Archive

 
Categories / JavaScript DHTML / Page Components
 

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 option) any 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"> b {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;         if( this.x<-fudge ) {           this.x=0;           clipped= true;         } else if( this.x-fudge>step_unit*(map.xmax-1) ) {           this.x= step_unit*(map.xmax-1);           clipped= true;         }         if( this.y<-fudge ) {           this.y=0;           clipped= true;         } else if( this.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);   if( fixed!= 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>=0 && 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>=0 && 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>=0 && 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 {                    switch( code ){                           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_         if( pos.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>=0 && 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()) {         if( checkObstacle(code,pos) )                 return false;         if( checkForTarget(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;                 default: return 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>=0 && 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>=0 && 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));          if( map.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) {         if( this.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 true; break;                 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 false; break;                 case 'R':                 case 'S':                 case 'T':                 case 'U':                           return false; break;                 case 'Z':                 case '[':                           return false; break;                 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':                           if( this.color!="green")                               code=' ';                           break;                 case 'C':                           if( this.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;         while( pos= 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/3 && !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>0 && !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>0 && 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() {         if( navigator.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="";                 }         }         if( navigator.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");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setMode("");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setMode("moving");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setFacing(90);    dummy.setMode("");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setMode("moving");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setFacing(180);    dummy.setMode("");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setMode("moving");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setFacing(270);    dummy.setMode("");    loadImages( imagePath(dummy.mapToImage() ) );    dummy.setMode("moving");    loadImages( imagePath(dummy.mapToImage() ) );    // only loading what can appear later    loadImages( mapCodeToImage('h') );    loadImages( mapCodeToImage('r') );    loadImages( mapCodeToImage('s') );    loadImages( mapCodeToImage('t') );    loadImages( mapCodeToImage('u') );    loadImages( mapCodeToImage(bird_right) );    loadImages( mapCodeToImage(bird_down) );    loadImages( mapCodeToImage(bird_left) );    loadImages( mapCodeToImage(bird_up) );    loadImages( mapCodeToImage('P') );    loadImages( mapCodeToImage('p') );    loadImages( mapCodeToImage('P.right') );    loadImages( mapCodeToImage('p.right') );    loadImages( mapCodeToImage('p.moving.left') );    loadImages( mapCodeToImage('p.moving.up') );    loadImages( mapCodeToImage('p.moving.down') );    loadImages( mapCodeToImage('p.moving.right') );    loadImages( mapCodeToImage('g') );    loadImages( mapCodeToImage('C') );    loadImages( mapCodeToImage('bullet_big' ) );    loadImages( mapCodeToImage( bullet_right ) );    loadImages( mapCodeToImage( bullet_up ) );    loadImages( mapCodeToImage( bullet_left ) );    loadImages( mapCodeToImage( bullet_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;   if( search.match(/debug=1/) ){         debug= true;         hideDebug= false;   }   if( search.match(/shrink=0/) ){         shrink= false;   }   if( search.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);         }   }   if( search.match(/size=48/) ){         cell_width= 48;         cell_height= 48;         setTimeout("layout2("+(cell_width*12+28+map_left)+")",1);         resizeDiv("mapwrapper",1000,580-20);   }   if( search.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="";          switch( code ){                 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=0 cellpadding=0 cellspacing=0 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();          }          if( browsercaps.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 if( keyCode == 220 || keyCode == 49 || keyCode== 70 || keyCode== 81 || keyCode== 45) {     //^1FQ0     r += 'action key 1';     keyCode = key_fire;   } else if( String.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==0 && 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) {                 while( value.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;         if( selected>=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 scrolling) makes IE 5 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>                     hylZee-001.zip( 182 k)