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= " ";
break;
default:
box= " "+code+" ";
}
}
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= " ";
break;
default:
box= " "+code+" ";
}
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;"> </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">
</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ülsmann</A>
<br>javascript coding:<A href="mailto:peter.schafer@gmail.com">(c)2004 Peter Schäfer</A>
<br>level design, set A:<A href="http://www.huelsmann.net/">Roland G. Hülsmann</A>
<br>level design, set B:<A href="mailto:peter.schafer@gmail.com">Peter Schä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');">
<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)Related examples in the same category