diff --git a/layout/menu.xml b/layout/menu.xml index 9e5a57e..dfa631c 100644 --- a/layout/menu.xml +++ b/layout/menu.xml @@ -1,335 +1,335 @@ - + - + diff --git a/scripts/functions-body.js b/scripts/functions-body.js index 21ef668..69d3c13 100644 --- a/scripts/functions-body.js +++ b/scripts/functions-body.js @@ -1,472 +1,450 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +if(typeof(global) === "undefined") + global = window; - -function getPlayer(name) +global.getPlayer = function(name) { name = removeColors(name).filter(); var matches = []; for(var i=engine.players.length-1;i>=0;--i) if(engine.players[i]) { if(engine.players[i].getBoringName().filter() == name) { return engine.players[i]; } else if(engine.players[i].getBoringName().filter().indexOf(name) > -1) { matches.push(i); } } if(matches.length > 1) { engine.console.print("Too many matches for "+name+". Try something more exact to narrow down the search.\n"); } else if(matches.length < 1) { engine.console.print("No matches for "+name+". Try a different name.\n"); } else { return engine.players[matches[0]]; } } function retToLastSafe(cycle,w1x,w1y,w2x,w2y) { var dist = distanceoflines(cycle.position.x,cycle.position.y,cycle.position.x,cycle.position.y,w1x,w1y,w2x,w2y); console.warn(cycle.name+" phased through a wall "+dist+"m.\n"); //if(dist == 0) return; dist -= settings.CYCLE_RUBBER_MINDISTANCE; cycle.position.x -= cycle.lastdir.front[0]*dist; cycle.position.y -= cycle.lastdir.front[1]*dist; if(lineIntersect(cycle.position.x,cycle.position.y,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y)) { //cycle.position.x += cycle.lastdir.front[0]*(dist*2); cycle.position.y += cycle.lastdir.front[1]*(dist*2); //if(lineIntersect(cycle.position.x,cycle.position.y,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y)) { var dist = distanceoflines(cycle.lastpos.x,cycle.lastpos.y,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y); dist -= settings.CYCLE_RUBBER_MINDISTANCE; cycle.position.x = cycle.lastpos.x+ (cycle.lastdir.front[0]*dist)/*+ (cycle.lastsensor.left*cycle.lastdir.left[0])+ (cycle.lastsensor.right*cycle.lastdir.right[0])*/; cycle.position.y = cycle.lastpos.y+ (cycle.lastdir.front[1]*dist)/*+ (cycle.lastsensor.left*cycle.lastdir.left[1])+ (cycle.lastsensor.right*cycle.lastdir.right[1])*/; /*while(lineIntersect(cycle.position.x,cycle.position.y,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y)) { cycle.position.x -= (1/1000)*cycle.lastdir.front[0]; cycle.position.y -= (1/1000)*cycle.lastdir.front[1]; } cycle.collidetime = 0; cycle.lastspeed = 0;*/ if(lineIntersect(cycle.position.x,cycle.position.y,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y)) { if(engine.network) { //request new cycle trails } else { doDeath(cycle); engine.console.print(cycle.name+" phased through a wall and has been terminated.\n"); } } else { cycle.rubber += cycle.speed; cycle.sensor.front = settings.CYCLE_RUBBER_MINDISTANCE; } } } else { cycle.rubber += cycle.speed; cycle.sensor.front = settings.CYCLE_RUBBER_MINDISTANCE; } if(engine.haswall) recalcCurrWall(cycle); } -function getCycleSensors(full=false) +global.getCycleSensors = function(full=false) { var oneline = ""; var range = settings.CYCLE_SENSORS_RANGE; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x] !== undefined) { //if(full) { engine.players[x].sensor.left = engine.players[x].sensor.right = engine.players[x].sensor.leftTurn = engine.players[x].sensor.rightTurn = engine.players[x].sensor.front = engine.players[x].sensor.rear = Infinity; engine.players[x].sensor.nearestobj = engine.players[x].sensor.lnearestobj = false; engine.players[x].sensor.rnearestobj = false; } engine.players[x].sensor.bottom = 0; engine.players[x].sensor.frontWallHeight = Infinity; engine.players[x].sensor.objleft = engine.players[x].sensor.objright = engine.players[x].sensor.objfront = engine.players[x].sensor.objrear = false; engine.players[x].sensor.nearcycle = false; engine.players[x].dir.front = cdir(engine.players[x].rotation.z); engine.players[x].dir.left = cdir(engine.players[x].rotation.z+(Math.PI/2)); engine.players[x].dir.right = cdir(engine.players[x].rotation.z-(Math.PI/2)); engine.players[x].dir.leftTurn = cdir(engine.players[x].rotation.z+(Math.PI/(settings.ARENA_AXES*0.5))); engine.players[x].dir.rightTurn = cdir(engine.players[x].rotation.z-(Math.PI/(settings.ARENA_AXES*0.5))); } for(var x=engine.zones.children.length-1;x>=0;--x) { engine.zones.children[x].walldist = Infinity; engine.zones.children[x].wall = [0,0,0,0]; } if(!engine.dedicated) var campos = engine.camera.position, ppos = engine.players[engine.viewTarget].position; for(var y=engine.map.walls.length-1;y>=0;--y) { //console.log("ohi"); var lookThroughWall = false; for(var i=engine.map.walls[y].length-1;i>=0;--i) { var w1x = engine.map.walls[y][i][0], w1y = engine.map.walls[y][i][1], p=engine.map.walls[y][i+1]; if(p !== undefined) { var w2x = p[0], w2y = p[1]/*,w2z = p[2]*/; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x] !== undefined && engine.players[x].alive) { var cycle = engine.players[x]; var posx = cycle.position.x, posy = cycle.position.y, posz = cycle.position.z; if(lineIntersect(posx,posy,cycle.lastpos.x,cycle.lastpos.y,w1x,w1y,w2x,w2y)) { retToLastSafe(cycle,w1x,w1y,w2x,w2y); } else { var dir = engine.players[x].dir.front, ldir = engine.players[x].dir.left, rdir = engine.players[x].dir.right ltdir = engine.players[x].dir.leftTurn, rtdir = engine.players[x].dir.rightTurn; var rg = /*cycle.sensor.front==Infinity?*/(cycle.speed*range);//:cycle.sensor.front; //var output = pointLineDistance(w1x,w1y,w2x,w2y,posx,posy); //var testx = (w2x-w1x)*dir[0], testy = (w2y-w1y)*dir[1], tpx = posx*(-dir[0]), tpy = posy*(-dir[1]); //if(cycle == engine.players[engine.viewTarget]) oneline += ""+(w2x-w1x)+"\t\t\t\t\t\t\t"+posx+"\t\t\t\t\t\t\t"+(w2x-w1x)+"\t\t\t\t\t\t\t"+posy+";\n"; //if(((w2x+w1x)/2) >= posy*dir[0] && ((w2y+w1y)/2) >= posx*dir[1]) //if(isinfront(w1x,w1y,w2x,w2y,posx,posy,dir)) coord = [w1x,w1y,w2x,w2y]; if(lineIntersect(posx,posy,posx+(dir[0]*rg),posy+(dir[1]*rg),w1x,w1y,w2x,w2y)) { //var ff = distanceoflines(posx,posy,posx+(dir[0]/6),posy+(dir[1]/6),w1x,w1y,w2x,w2y); var ff = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y); /*forward = Math.min(forward,ff); if(ff == forward) type = "rim";//*/ if(ff < cycle.sensor.front) { cycle.sensor.front=ff; cycle.sensor.nearestobj = "rim";} } else if(lineIntersect(posx,posy,posx+(ldir[0]*rg),posy+(ldir[1]*rg),w1x,w1y,w2x,w2y)) { var ll = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y); if(ll < cycle.sensor.left) { cycle.sensor.left=ll; cycle.sensor.lnearestobj = "rim";} } else if(lineIntersect(posx,posy,posx+(rdir[0]*rg),posy+(rdir[1]*rg),w1x,w1y,w2x,w2y)) { var rr = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y); if(rr < cycle.sensor.right) { cycle.sensor.right=rr; cycle.sensor.rnearestobj = "rim";} } if(lineIntersect(posx,posy,posx+(ltdir[0]*rg),posy+(ltdir[1]*rg),w1x,w1y,w2x,w2y)) { var ll = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y); if(ll < cycle.sensor.leftTurn) { cycle.sensor.leftTurn=ll; } } else if(lineIntersect(posx,posy,posx+(rtdir[0]*rg),posy+(rtdir[1]*rg),w1x,w1y,w2x,w2y)) { var rr = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y); if(rr < cycle.sensor.rightTurn) { cycle.sensor.rightTurn=rr;} } /* if(((w2x+w1x)/2) >= posy*ldir[0] && ((w2y+w1y)/2) >= posx*ldir[1]) { var ll = distanceoflines(posx,posy,posx+(ldir[0]),posy+(ldir[1]),w1x,w1y,w2x,w2y); left = Math.min(left,ll); } if(((w2x+w1x)/2) >= posy*rdir[0] && ((w2y+w1y)/2) >= posx*rdir[1]) { var rr = distanceoflines(posx,posy,posx+(rdir[0]),posy+(rdir[1]),w1x,w1y,w2x,w2y); right = Math.min(right,rr); } */ } } for(var x=engine.zones.children.length-1;x>=0;--x) { var zone = engine.zones.children[x]; var posx = zone.position.x, posy = zone.position.y; var walldist = distanceoflines(posx,posy,posx,posy,w1x,w1y,w2x,w2y)-zone.cfg.radius; if(walldist < zone.walldist) { zone.walldist = walldist; zone.wall[0]=w1x;zone.wall[1]=w1y;zone.wall[2]=w2x;zone.wall[3]=w2y; } } if(!engine.dedicated && !lookThroughWall) lookThroughWall = (engine.walls.children[y]&&engine.walls.children[y].geometry.vertices[engine.walls.children[y].geometry.vertices.length-1].z) > campos.z && lineIntersect(campos.x,campos.y,ppos.x,ppos.y,w1x,w1y,w2x,w2y); } } if(!engine.dedicated && engine.walls.children[y]) engine.walls.children[y].visible = !lookThroughWall; } for(var a=engine.players.length-1;a>=0;--a) if(engine.players[a] !== undefined) { var walls = engine.players[a].walls.map; var len = walls.length; for(var i=len-1;i>=0;--i) { var p1 = walls[i]; //var w1x = p1[0],w1y = p1[1],w1z = p1[2]; if(walls[i+1] !== undefined) { var p2 = walls[i+1]; //var w2x = p2[0],w2y = p2[1],w2z = p2[2]; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x] !== undefined && engine.players[x].alive) { //var cycle = engine.players[x]; //var isplayer = (engine.players[a] == engine.players[x]); if(engine.players[a] != engine.players[x] || i <= len-(settings.ARENA_AXES)) { //var posx = engine.players[x].position.x, posy = engine.players[x].position.y, posz = engine.players[x].position.z; if(p1[2]||0 <= engine.players[x].position.z && (p1[2]||0)+1 >= engine.players[x].position.z) { if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].lastpos.x,engine.players[x].lastpos.y,p1[0],p1[1],p2[0],p2[1])) { retToLastSafe(engine.players[x],p1[0],p1[1],p2[0],p2[1]); } else { var dir = engine.players[x].dir.front, ldir = engine.players[x].dir.left, rdir = engine.players[x].dir.right, ltdir = engine.players[x].dir.leftTurn, rtdir = engine.players[x].dir.rightTurn; //var rg = engine.players[x].sensor.front==Infinity?(engine.players[x].speed*range):engine.players[x].sensor.front; //if(engine.players[x].sensor.front==Infinity) var rg=engine.players[x].speed*range;//else var rg=engine.players[x].sensor.front; //if(!(engine.players[x].position.x == p2[0] && engine.players[x].position.y == p2[1])) { if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(dir[0]*rg),engine.players[x].position.y+(dir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var ff=distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); /*forward = Math.min(forward,ff); if(ff == forward) type = engine.players[x];//*/ if(ff < engine.players[x].sensor.front) { engine.players[x].sensor.front=ff; engine.players[x].sensor.nearestobj = engine.players[a]; if(engine.players[a] != engine.players[x]) { engine.players[x].sensor.lastnonselfobj = engine.players[a]; } } } else if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(ldir[0]*rg),engine.players[x].position.y+(ldir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var ll = distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); if(ll < engine.players[x].sensor.left) { engine.players[x].sensor.left=ll; engine.players[x].sensor.lnearestobj = engine.players[a]; engine.players[x].sensor.objleft = engine.players[a]; } } else if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(rdir[0]*rg),engine.players[x].position.y+(rdir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var rr = distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); if(rr < engine.players[x].sensor.right) { engine.players[x].sensor.right=rr; engine.players[x].sensor.rnearestobj = engine.players[x].sensor.objright = engine.players[a]; } } else if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(-dir[0]*rg),engine.players[x].position.y+(-dir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var bb=distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); if(bb < engine.players[x].sensor.rear) { engine.players[x].sensor.rear=bb; engine.players[x].sensor.objrear = engine.players[a]; } } if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(ltdir[0]*rg),engine.players[x].position.y+(ltdir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var ll = distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); if(ll < engine.players[x].sensor.leftTurn) { engine.players[x].sensor.leftTurn=ll;} } else if(lineIntersect(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x+(rtdir[0]*rg),engine.players[x].position.y+(rtdir[1]*rg),p1[0],p1[1],p2[0],p2[1])) { var rr = distanceoflines(engine.players[x].position.x,engine.players[x].position.y,engine.players[x].position.x,engine.players[x].position.y,p1[0],p1[1],p2[0],p2[1]); if(rr < engine.players[x].sensor.rightTurn) { engine.players[x].sensor.rightTurn=rr;} } } } } } } } } }//*/ /*for(var x=engine.map.zones.length-1;x>=0;--x) { var zone = engine.map.zones[x]; for(var y=engine.players.length-1;y>=0;--y) if(engine.players[y] !== undefined) { var ff = Math.sqrt(((zone[1]-p2x)**2)+((zone[2]-p2y)**2))-zone[3]; if(ff < cycle.sensor.front) { cycle.sensor.front=ff; cycle.sensor.nearestobj = "zone"; } } }*/ //console.log(forward); if(oneline != "") console.log(oneline); for(var x=engine.players.length-1;x>=0;--x) if((cycle=engine.players[x]) !== undefined && cycle.lastpos) { cycle.lastpos.x = cycle.position.x; cycle.lastpos.y = cycle.position.y; cycle.lastpos.z = cycle.position.z; engine.players[x].lastdir = { front:engine.players[x].dir.front, left:engine.players[x].dir.left, right:engine.players[x].dir.right }; engine.players[x].lastsensor = { front:engine.players[x].sensor.front, left:engine.players[x].sensor.left, right:engine.players[x].sensor.right }; cycle.intersectTest = []; } } -function maxSpeed() +global.maxSpeed = function() { if(settings.CYCLE_SPEED_DECAY_ABOVE > 0 ) var max_cs = settings.CYCLE_SPEED / settings.CYCLE_SPEED_DECAY_ABOVE; else var max_cs = settings.CYCLE_SPEED * 100; var max_fastest = engine.fastestSpeed; // return Math.max(max_cs,max_fastest); } -function handleChat(cycle,output) +global.handleChat = function(cycle,output) { var split = output.split(" "); - /*if(split[0][0] == "/") - { - switch(split[0]) - { - case "/console": case "/admin": - if(!engine.dedicated || split[0] != "/console") - { - split.shift(); - loadcfg(split.join(" ")); - break; - } - default: - if(engine.network) - { - - } - else - { - engine.console.print('Unknown chat command "'+split[0]+'".\n'); - } - break; - } - }*/ if(split[0] == "/console" && !engine.dedicated) { split.shift(); loadcfg(split.join(" ")); } else if(split[0][0] == "/" && !engine.network) { switch(split[0]) { case "/admin": split.shift(); var ln = split.join(" "); engine.console.print('Remote admin command from '+cycle.getBoringName()+'0x7f7fff: '+ln+'\n'); var text = []; engine.concatch = {to:text,type:"list"}; loadcfg(ln); engine.concatch = undefined; for(var i=0;i=0;--x) if(engine.players[x]) { engine.console.print(x+": "+engine.players[x].getColoredName()+"\n",cycle); } break; case "/me": cycle.doChat(output); break; default: break; } } else { cycle.doChat(output); } } if(typeof(document) == "undefined") { - function centerMessage(msg,time=5000) + global.centerMessage = function(msg,time=5000) { console.log(msg); if(window.svr) { if(time == Infinity) time = Number.MAX_VALUE; var data = JSON.stringify({type:"cen",data:{msg:msg,time:time}}); window.svr.clients.forEach(function(ws){ws.send(data);}); } } } else { - function centerMessage(msg,time=5000) + global.centerMessage = function(msg,time=5000) { var cm = document.getElementById("centerMessage"); cm.innerHTML = replaceColors(htmlEntities(msg)); cm.style.opacity = 1; cm.style.display = "block"; engine.cMFadeOutAfter = performance.now()+time; } } -function toLadderLog(event,params) +global.toLadderLog = function(event,params) { if(settings["LADDERLOG_WRITE_"+event]) { } } diff --git a/scripts/functions-head.js b/scripts/functions-head.js index 28a58b1..71c2b30 100644 --- a/scripts/functions-head.js +++ b/scripts/functions-head.js @@ -1,593 +1,596 @@ //FUNC -function gafd(a,b) //! returns angle from xdir, ydir +if(typeof(global) === "undefined") + global = window; + +global.gafd = function(a,b) //! returns angle from xdir, ydir { var c = 180 * Math.atan2(b,a) / Math.PI; 0 > c && (c += 360); 360 < c && (c -= 360); return c } -function cdir(theta) //! Gets [xdir, ydir] from angle +global.cdir = function(theta) //! Gets [xdir, ydir] from angle { var x = Math.cos(theta); var y = Math.sin(theta); return [x,y]; } -function fileOpen(callback,type="plain/text") +global.fileOpen = function(callback,type="plain/text") { if(window.FileReader) { var f = document.createElement("input"); f.type = "file"; f.onchange = function(e) { if(e.target.files.length > 0) { var r = new FileReader(); r.onload = function(e) { callback(e.target.result); } for(var i=0;i/g,">").replace(/"/g,"""); } -function colorIsDark(r,g,b) +global.colorIsDark = function(r,g,b) { return ( ( r < 255*settings.FONT_MIN_R && g < 255*settings.FONT_MIN_G && b < 255*settings.FONT_MIN_B )|| r+g+b < 255*settings.FONT_MIN_TOTAL ); } -function getDarkBGFromHex(hex) +global.getDarkBGFromHex = function(hex) { var c = new THREE.Color(hex); if(colorIsDark(c.r,c.g,c.b)) return "white"; else return "none"; } -function replaceColors(str) +global.replaceColors = function(str) { if(typeof(str) == "undefined") return typeof(str); var dark = "class=darktext"; //Capitals are allowed because the processes here can handle them str = str.replace(settings.VERIFY_COLOR_STRICT?/0x([0-9A-Fa-f]{6}|RESETT)(.*?)(?=0x(?:[0-9A-Fa-f]{6}|RESETT)|$)/gm : /0x(.{6})(.*?)(?=0x(?:.{6})|$)/gm, function(x) { if(x.substr(2,6) == "RESETT") { return x.substr(8); } else { //var darkI1="class=lighttext"; var darkI1=""; var r=parseInt(x[2]+x[3],16),g=parseInt(x[4]+x[5],16),b=parseInt(x[6]+x[7],16); //NOTE: unless using regex, javascript only replaces the first occurance if(isNaN(r)){if(isNaN(parseInt(x[2],16)))x=x.replace(x[2],"0");if(isNaN(parseInt(x[3],16)))x=x.replace(x[3],"0");r=parseInt(x[2]+x[3],16)} if(isNaN(g)){if(isNaN(parseInt(x[4],16)))x=x.replace(x[4],"0");if(isNaN(parseInt(x[5],16)))x=x.replace(x[5],"0");g=parseInt(x[4]+x[5],16)} if(isNaN(b)){if(isNaN(parseInt(x[6],16)))x=x.replace(x[6],"0");if(isNaN(parseInt(x[7],16)))x=x.replace(x[7],"0");b=parseInt(x[6]+x[7],16)} if(colorIsDark(r,g,b)) { if(settings.TEXT_DARK_HIGHLIGHT) { darkI1 = dark; } if(settings.TEXT_BRIGHTEN) { if(r < settings.FONT_MIN_R) r += settings.FONT_MIN_R; if(g < settings.FONT_MIN_G) g += settings.FONT_MIN_G; if(b < settings.FONT_MIN_B) b += settings.FONT_MIN_B; if(colorIsDark(r,g,b)) { r += settings.FONT_MIN_TOTAL/3; g += settings.FONT_MIN_TOTAL/3; b += settings.FONT_MIN_TOTAL/3; } } } return ""+x.substr(8)+""; } }); return str; } -function removeColors(str) +global.removeColors = function(str) { return str.replace(settings.VERIFY_COLOR_STRICT?/0x([0-9A-Fa-f]{6}|RESETT)(.*?)(?=0x(?:[0-9A-Fa-f]{6}|RESETT)|$)/gm : /0x(.{6})(.*?)(?=0x(?:.{6})|$)/gm,function(x){return x.substr(8)}); } String.prototype.filter = function() //! Filter illegal player characters. Heavily based on ArmagetronAd's filtering. { var char, out="", str = this.toString(); for(var i=0;i 32) //Leave ASCII characters but convert them to lower case { if(char == 48) out += "o"; //map 0 to o because z-man else out += str[i].toLowerCase(); } //! map umlauts and similar to their base characters else if(char >= 0xc0 && char <= 0xc5) out += 'a'; else if(char >= 0xd1 && char <= 0xd6) out += 'o'; else if(char >= 0xd9 && char <= 0xdD) out += 'u'; else if(char == 0xdf) out += 's'; else if(char >= 0xe0 && char <= 0xe5) out += 'a'; else if(char >= 0xe8 && char <= 0xeb) out += 'e'; else if(char >= 0xec && char <= 0xef) out += 'i'; else if(char >= 0xf0 && char <= 0xf6) out += 'o'; else if(char >= 0xf9 && char <= 0xfc) out += 'u'; else if(char >= 0xc0 && char <= 0xc5) out += 'a'; else switch(char) { //some of those are a bit questionable, but still better than lots of underscores case 161: out += '!'; break; case 162: out += 'c'; break; case 163: out += 'l'; break; case 165: out += 'y'; break; case 166: out += '|'; break; case 167: out += 's'; break; case 168: out += '"'; break; case 169: out += 'c'; break; case 170: out += 'a'; break; case 171: out += '"'; break; case 172: out += '!'; break; case 174: out += 'r'; break; case 176: out += 'o'; break; case 177: out += '+'; break; case 178: out += '2'; break; case 179: out += '3'; break; case 182: out += 'p'; break; case 183: out += '.'; break; case 185: out += '1'; break; case 187: out += '"'; break; case 198: out += 'a'; break; case 199: out += 'c'; break; case 208: out += 'd'; break; case 209: out += 'n'; break; case 215: out += 'x'; break; case 216: out += 'o'; break; case 221: out += 'y'; break; case 222: out += 'p'; break; case 231: out += 'c'; break; case 241: out += 'n'; break; case 247: out += '/'; break; case 248: out += 'o'; break; case 253: out += 'y'; break; case 254: out += 'p'; break; case 255: out += 'y'; break; default: out += "_"; break; //unknown character, mapped to underscore } } return out; } -function tStringify(str) //! +global.tStringify = function(str) //! { str = str.replace(/\$\w*/g,function(i){console.log(i.replace("\$",""));}); str = str.replace(new RegExp("@progtitle@",'g'),"3DCycles Web"); str = str.replace(new RegExp("@progtitleshort@",'g'),"3DCycles"); str = str.replace(new RegExp("@progname@",'g'),"webtron"); return str; } -function pi(a=1) {return Math.PI*a} //! Finds multiples of PI. +global.pi = function(a=1) {return Math.PI*a} //! Finds multiples of PI. -function setNewFont(input) //! Gets font from user input. Not used anywhere anymore. +global.setNewFont = function(input) //! Gets font from user input. Not used anywhere anymore. { input = input.toLowerCase(); var output = ""; switch(input) { case 'armagetronad':case 'armagetron':case 'arma':case 'tron': output = "Armagetronad"; break; case 'flynn':case 'flyn':case 'flyyn':case 'flin':case 'user':case 'legacy': output = "Flynn"; break; case 'serif':case 'srif':case 'serf':case 'srf':case 'font': output = "serif"; break; case 'sans-serif':case 'sansserif':case 'sans':case 'sanserif':case 'san': output = "sans-serif"; break; case 'nicefont':case 'nice':case 'nfont': output = "nicefont"; break; case 'monospace':case 'mono':case 'fixedwidth':case 'fixed': output = "monospace"; break; default: output = "Armagetronad"; } return output; } -function pointDistance(x1,y1,x2,y2) +global.pointDistance = function(x1,y1,x2,y2) { var xs = x2 - x1, ys = y2 - y1; return Math.sqrt( xs*xs + ys*ys ); } -function getLogicalBox(string) //! Parses map file and returns [x, y, minx, miny, maxx, maxy] or false on failure. +global.getLogicalBox = function(string) //! Parses map file and returns [x, y, minx, miny, maxx, maxy] or false on failure. { var re = /(x|y)\=.-?(\d*.)?\d+/gi; var matches = string.match(re); var temp = 0; var box = [0,0,Infinity,Infinity,-Infinity,-Infinity];// x, y, minx, miny, maxx, maxy if (matches) { for (var i = 0; i < matches.length; i++) { matches[i] = matches[i].replace(/\"|\'/g, ''); //console.log(matches[i]); if (matches[i].indexOf("x") > -1) { // x=250.25 matches[i] = matches[i].replace('x=', ''); temp = parseFloat(matches[i]); if (temp > box[4]) { box[4] = temp; } if (temp < box[2]) { box[2] = temp; } } if (matches[i].indexOf("y") > -1) { // y=-25 matches[i] = matches[i].replace('y=', ''); temp = parseFloat(matches[i]); if (temp > box[5]) { box[5] = temp; } if (temp < box[3]) { box[3] = temp; } } } //get center box[0] = ( box[2] + box[4] ) / 2; box[1] = ( box[3] + box[5] ) / 2; //console.log(box); return box; } else { return false; } } -function hasClass(element, cls) {//checks if element has classname, returns true | false +global.hasClass = function(element, cls) {//checks if element has classname, returns true | false return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1; } -function relPath(path,rel="/") +global.relPath = function(path,rel="/") { //if(rel.indexOf("/") != 0) rel = "/"+rel; if(rel.indexOf("/",rel.length-1) == -1) rel += "/"; if(path.indexOf("://") >= 0 || path.indexOf("/") == 0) return path; else return rel+path; } -function inround() //! Tries to determine if we're in a round or not. +global.inround = function() //! Tries to determine if we're in a round or not. { return !engine.roundCommencing && engine.gtime >= -4000; } -function deg2rad(angle) +global.deg2rad = function(angle) { if(!isFinite(angle)) return angle; angle %= 360; if(angle < 0) angle += 360; var radians = angle * Math.PI / 180; return radians; } -function rad2deg(radians) +global.rad2deg = function(radians) { if(!isFinite(radians)) return radians; var angle = radians * 180 / Math.PI; angle %= 360; if(angle < 0) angle += 360; return angle; } -function normalizeRad(radians) +global.normalizeRad = function(radians) { var pi2 = Math.PI*2; radians = radians%pi2; while(radians < 0) radians += pi2; return radians; } // BEGIN based on code from http://geomalgorithms.com/a07-_distance.html // Copyright 2001 softSurfer, 2012 Dan Sunday // This code may be freely used, distributed and modified for any purpose // providing that this copyright notice is included with it. // SoftSurfer makes no warranty for this code, and cannot be held // liable for any real or imagined damage resulting from its use. // Users of this code must verify correctness for their application. var dot = function(ux,uy,vx,vy) { return ((ux*vx)+(uy*vy)); }; var SMALL_NUM = 0.00000001; // anything that avoids division overflow -function distanceoflines(x1,y1, x2,y2, x3,y3, x4,y4) +global.distanceoflines = function(x1,y1, x2,y2, x3,y3, x4,y4) { var ux=x2-x1,uy=y2-y1; var vx=x4-x3,vy=y4-y3 var wx=x1-x3,wy=y1-y3; //var a = dot(ux,uy,ux,uy), b = dot(ux,uy,vx,vy), c = dot(vx,vy,vx,vy), d = dot(ux,uy,wx,wy), e = dot(vx,vy,wx,wy); var a = (ux*ux)+(uy*uy), b = (ux*vx)+(uy*vy), c = (vx*vx)+(vy*vy), d = (ux*wx)+(uy*wy), e = (vx*wx)+(vy*wy); //probably faster var D = a*c - b*b; var sc,sN,sD = D; var tc,tN,tD = D; if(D < SMALL_NUM) //lines almost parallel { sN = 0; //force point p0 on segment s1 sD = 1; //prevent possible division by zero tN = e; tD = c; } else //get closest points on the infinite lines { sN = (b*e - c*d); tN = (a*e - b*d); if(sN < 0) //sc < 0 => the s=0 edge is visible { sN = 0; tN = e; tD = c; } else if(sN > sD) //sc > 1 => the s=1 edge is visible { sN = sD; tN = e + b; tD = c; } } if(tN < 0) // tc < 0 => the t=0 edge is visible { tN = 0; //recompute sc this edge if(d>0) { sN = 0; } else if(d tD) //tc > 1 => the t=1 edge is visible { tN = tD; //recompute sc for this edge if((b-d) < 0) { sN = 0; } else if((b-d) > a) { sN = sD; } else { sN = (b-d); sD = a; } } //finally, do the division to get sc and tc; var sc = (Math.abs(sN) < SMALL_NUM ? 0.0 : sN / sD); var tc = (Math.abs(tN) < SMALL_NUM ? 0.0 : tN / tD); var dPx = wx + (ux * sc) - (vx * tc); var dPy = wy + (uy * sc) - (vy * tc); //console.log(dPx,dPy); //return Math.sqrt(dot(dPx,dPy,dPx,dPy)); return Math.hypot(dPx,dPy); } // END -function is_in_circle(p1x, p1y, r1, p2x, p2y, r2=0) +global.is_in_circle = function(p1x, p1y, r1, p2x, p2y, r2=0) { return(r1+r2 > Math.sqrt(Math.pow((p1x-p2x),2)+Math.pow((p1y-p2y),2))) } -function lineIntersect(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) +global.lineIntersect = function(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) { var s1_x = p1_x - p0_x, s1_y = p1_y - p0_y, s2_x = p3_x - p2_x, s2_y = p3_y - p2_y; var s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y), t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); return (s >= 0 && s <= 1 && t >= 0 && t <= 1); } ///////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////// //"encrytion" variables, not sure what they were for: String.prototype.toEncodedString = function () { var ostr = this.toString().replace(/\s+/g, ''); if (ostr.length < 8) { alert("Password must be at least 8 characters long with no spaces."); return null; }; var x, nstr = '', len = ostr.length; for (x = 0; x < len; ++x) { nstr += (255 - ostr.charCodeAt(x)).toString(36).toUpperCase().toPaddedString(2, '0'); }; return nstr; }; String.prototype.fromEncodedString = function () { var ostr = this.toString(); var x, nstr = '', len = ostr.length; for (x = 0; x < len; x += 2) { nstr += String.fromCharCode(255 - parseInt(ostr.substr(x, 2), 36)); }; return nstr; }; Number.prototype.toPaddedString = function (len, pad) { len = (len) ? Number(len) : 2; if (isNaN(len)) { alert("Padded String 'length' argument is not numeric."); return null; }; var dflt = (isNaN(this.toString())) ? " " : "0"; pad = (pad) ? pad.toString().substr(0, 1) : dflt; var str = this.toString(); if (dflt == "0") { while (str.length < len) str = pad + str; } else { while (str.length < len) str += pad; }; return str; }; String.prototype.toPaddedString = Number.prototype.toPaddedString; /**/ var encrypt1 = new Date().getTime(); encrypt1 = ""+encrypt1;//##############... //var encrypt2 = encrypt1.toEncodedString(); //var str3 = str2.fromEncodedString(); /**/ -function getSuperString() { +global.getSuperString = function() { var superstring = encrypt1.toEncodedString(); superstring = "lightcycle"+superstring; return superstring; } var ss = getSuperString(); diff --git a/scripts/game.js b/scripts/game.js index eb82820..ce63f61 100644 --- a/scripts/game.js +++ b/scripts/game.js @@ -1,1194 +1,1173 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -function doNewRound() +(function(game){ + +/*if(this.require) +{ + if(typeof(THREE) === "undefined") + var THREE = require('./lib/Three.js'); + if(typeof(Zone) === "undefined") + var Zone = require("./zone.js"); +}*/ + +game.doNewRound = function() { if(engine.uRound !== false) { clearTimeout(engine.uRound); engine.uRound = false; } - endRound(); - setTimeout(newRound,engine.dedicated?300:0); //give clients an opportunity to sync their data + game.endRound(); + setTimeout(game.newRound,engine.dedicated?300:0); //give clients an opportunity to sync their data } -function endRound() +game.endRound = function() { if(window.svr) { window.svr.clients.forEach(function(ws){ws.send('{"type":"endRound"}')}); window.svr.clients.forEach(function(ws){ws.send('{"type":"syncdata","gtime":-4000}')}); } if(ctx) { audioStop(); if(settings.SOUNDS_EXTRO) playSound(bufferLoader.bufferList[bufferLoader.other+2],0.5,1,false,ctx.destination); } engine.roundCommencing = true; //engine.hud.hide(); if(engine.hud) engine.hud.game.style.opacity = 0; engine.console.print("Clearing grid...\n",false); if(engine.renderer) engine.renderer.clear(); if(!engine.network) { - if(settings.ROUND_CENTER_MESSAGE != "") + //if(settings.ROUND_CENTER_MESSAGE != "") { centerMessage(settings.ROUND_CENTER_MESSAGE); } } engine.gtime = -4000; for(var x=engine.players.length-1;x>=0;--x) if(typeof(engine.players[x]) != "undefined") { engine.players[x].alive = false; } while(engine.scene.children.length > 0) { engine.scene.remove(engine.scene.children[0]); } //if(!engine.network) engine.players.splice(0); } -function endGame() +game.end = function() { - endRound(); + game.endRound(); engine.players.splice(0); engine.round = 0; } -function playGame() +game.play = function() { engine.playGame = true; if(!engine.scene) { init(); } else { engine.paused = false; if(engine.hud) engine.hud.show(); } - hideMenu(); newRound(); + hideMenu(); game.newRound(); engine.inputState = 'game'; //change input state to accept game controls if(engine.network) { document.getElementById("progtitle").innerHTML = tStringify("@progtitleshort@ • Playing online"); } else { document.getElementById("progtitle").innerHTML = tStringify("@progtitleshort@ • Playing locally"); } } function revertMap() { if(engine.dedicated) { engine.console.print("Unable to load map file. Reverting...\n",true); chsetting("MAP_FILE",engine.loadedMap); loadRound(); } else { var mapfile = settings.RESOURCE_REPOSITORY_CACHE+(settings.MAP_FILE.replace(/\(.+\)/,"")); engine.console.print("Downloading map from "+mapfile+"...\n",false); - httpGetAsync(mapfile,loadRound,function() + httpGetAsync(mapfile,game.loadRound,function() { engine.console.print("Unable to load map file. Ignoring for now...\n",false); - loadRound(); + game.loadRound(); }); } } -function newRound() +game.newRound = function() { engine.roundCommencing = true; /////////////LIGHTS var light1 = new THREE.AmbientLight( 0x666666 ); // soft white light engine.scene.add( light1 ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.castShadow = true; light.position.set( 4, 1, 0 ); engine.scene.add( light ); var light2 = new THREE.DirectionalLight( 0xffffff, 1 ); light2.castShadow = true; light2.position.set( -4, -1, 0 ); engine.scene.add( light2 ); //////////end of lights var aspectRatio = (window.innerWidth / window.innerHeight); engine.camera = new THREE.PerspectiveCamera( settings.CAMERA_FOV, aspectRatio, settings.CAMERA_NEAR_RENDER, settings.CAMERA_FAR_RENDER ); engine.camera.userViewDir = false; engine.camera.up = new THREE.Vector3(0,0,1); //Z is up, X and Y is l+r and b+f //engine.camera.position.set(247, 247, 3); engine.viewTarget = engine.activePlayer; //engine.cameraOrbit = new THREE.OrbitControls(engine.camera); //engine.cameraOrbit.maxDistance = 400; //BELOW THIS LINE DEPENDS ON GAME TYPE //MAP var maps = settings.MAP_ROTATION.split(";"); if(settings.ROTATION_TYPE > 0) { switch(settings.ROTATION_TYPE) { case 1: engine.currrot += 1; break; case 2: // when matches are implemented if(engine.rounds == 0) engine.currrot += 1; break; } if(engine.currrot > maps.length) engine.currrot = 0; settings.MAP_FILE = maps[engine.currrot]; } if(settings.MAP_FILE != "" && settings.MAP_FILE != engine.loadedMap) { var mapfile = settings.RESOURCE_REPOSITORY_SERVER+(settings.MAP_FILE.replace(/\(.+\)/,"")); engine.console.print("Downloading map from "+mapfile+"...\n",false); - httpGetAsync(mapfile,loadRound,revertMap); - //loadRound(httpGet(mapfile)); + httpGetAsync(mapfile,game.loadRound,revertMap); } else { - loadRound(); + game.loadRound(); } } -function teamColor(id) +game.teamColor = function(id) { id += 1; if(settings["TEAM_NAME_"+id]) { return new THREE.Color(settings["TEAM_RED_"+id]/15,settings["TEAM_GREEN_"+id]/15,settings["TEAM_BLUE_"+id]/15); } return new THREE.Color(); } function createAIsettings() { var AI_NUM = 1; for(var z=engine.players.length-1;z>=0;--z) if(engine.players[z] && engine.players[z].AI) {AI_NUM++;} var cycleColor = [0x000000,0xff0000,0x00ff00,0x0000ff][Math.round(Math.random()*3)]; var tailColor = [0x0000ff,0xff0000,0xffff00,0x00ff00][Math.round(Math.random()*3)]; var colorcode; if(settings.ALLOW_TEAM_NAME_COLOR) { colorcode = cycleColor.toString(16); colorcode = ("0".repeat(6-colorcode.length))+colorcode; } else { cycleColor = tailColor = teamColor(1); colorcode = cycleColor.getHexString(); } var cycleinfo = { ai:true, cycleColor:cycleColor, tailColor:tailColor, /*engineType: 5,*/ engineType:(settings.players[0])?settings.players[0].engineType:5, spectating:false, name: settings.AI_DUAL_COLOR_NAME?'AI0x'+colorcode+'#'+AI_NUM:'AI#'+AI_NUM }; return cycleinfo; } function calculateSpawn(x) { var spawnslength = engine.map.spawns.length; if(!engine.map.spawns[x]) { var mult = Math.floor((x/spawnslength)); var currspawn = x-(spawnslength*mult); var alt = (mult/2 != Math.floor(mult/2)); console.log(mult); if(alt) mult = -Math.ceil(mult/2) else mult = mult/2 console.log(mult,currspawn); var spawns = JSON.parse(JSON.stringify(engine.map.spawns[Math.min(currspawn,(spawnslength-1))]||[])); spawns[0] -= mult*settings.SPAWN_WINGMEN_SIDE; spawns[1] -= Math.abs(mult)*settings.SPAWN_WINGMEN_BACK; } else { var spawns = engine.map.spawns[x]; } return spawns; } function processPlayer(x,cfg) { var spawns = calculateSpawn(x); if(engine.players[x]) { var cycle = engine.players[x]; cycle.engineType = (typeof(cfg)=="undefined")?5:cfg.engineType; //if(x == engine.activePlayer && !engine.dedicated) { var cycleColor = cfg.cycleColor,tailColor = cfg.tailColor; if(!settings.ALLOW_TEAM_NAME_COLOR) { cycleColor = tailColor = teamColor(engine.teams.indexOf(engine.players[x].team)); } if(cycle.name != cfg.name) { var out = cycle.getColoredName()+"0x7fff7f renamed to "; cycle.cycleColor = cycleColor; cycle.tailColor = tailColor; cycle.name = cfg.name; engine.console.print(out+cycle.getColoredName()+"\n"); } else { cycle.cycleColor = cycleColor; cycle.tailColor = tailColor; } if(cycle.spectating != cfg.spectating) { cycle.spectating = cfg.spectating; if(cycle.spectating) { engine.console.print(cycle.getColoredName()+"0xff7f7f left to spectator mode.\n"); } else { engine.console.print(cycle.getColoredName()+"0x7fff7f entered from spectator mode.\n"); } } } } else { cfg.x = spawns[0]; cfg.y = spawns[1]; cfg.z = spawns[2]; engine.players[x] = (new Player(cfg)); var cycle = engine.players[x]; if(cycle.spectating) { engine.console.print(cycle.getColoredName()+"0xff7f7f entered as spectator.\n"); } else { engine.console.print(cycle.getColoredName()+"0x7fff7f entered the game.\n"); } } if(cycle.spectating) { cycle.team = null; } else if(!cycle.team) { if(engine.teams.length < settings.TEAMS_MAX) { engine.teams.push(cycle.team = new Team({name:cfg.name,x:spawns[0], y:spawns[1], z:spawns[2], dir:deg2rad(spawns[3])})); } else { var minPCount = 0, minPlayers = Infinity, minTeam; for(var x=engine.teams.length-1;x>=0;--x) if(engine.teams[x]) { if(engine.teams[x].members.length == minPlayers) { minPCount++; if(minTeam.push) { minTeam.push(x); } else { minTeam = [minTeam,x]; } } else if(engine.teams[x].members.length < minPlayers) { minPlayers = engine.teams[x].members.length; minTeam = x; minPCount = 1; } } if(minPCount != 1) { minTeam = Math.floor(Math.random()*minTeam.length); } cycle.team = engine.teams[minTeam]; } cycle.team.members.push(cycle); } } -function ensurePlayersSane(removeAIs=true) +game.processPlayers = function(removeAIs=true) { var numAIs = 0, numPlay = 0, numSpec = 0, numWantPlay = 0; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { if(engine.players[x].AI) numAIs++; if(engine.players[x].spectating) numSpec++; else numPlay++; //if(engine.players[x].spectating && !settings.players[x].spectating) numWantPlay++; } var numHuman = numPlay-numAIs; if(removeAIs) { for(var x=settings.TEAMS_MAX;x=0;--i) { engine.console.print(engine.teams[x].members[i].getColoredName()+"0xff7f7f left to spectator mode.\n"); engine.teams[x].members[i].spectating = true; } } engine.teams.splice(settings.TEAMS_MAX); } for(var x=settings.players.length-1;x>=0;--x) if(settings.players[x]) { if(engine.players[x] && engine.players[x].AI) { engine.players[engine.players.length-1] = engine.players[x]; engine.players[x] = undefined; } if(!engine.players[x]) { numPlay++; numHuman++; } processPlayer(x,settings.players[x]); } if(removeAIs) { console.log(numHuman); var shouldAIs = Math.max(0,(settings.MIN_PLAYERS-numHuman)); if(!settings.AI_TEAM) { shouldAIs += (numHuman <= settings.SP_HUMANS_COUNT)?settings.SP_NUM_AIS:settings.NUM_AIS; } if(shouldAIs > numAIs) { var AIsToAdd = (shouldAIs-numAIs); console.log(numAIs+" AIs in the game, adding "+AIsToAdd+"."); for(var x=AIsToAdd;x>0;--x) { var cycleinfo = createAIsettings(); processPlayer(engine.players.length,cycleinfo); } numAIs += AIsToAdd; } else if(numAIs != 0) { var AIsToDealWith = (numAIs-shouldAIs); console.log(numAIs+" AIs in the game, removing "+AIsToDealWith+"."); if(AIsToDealWith != 0) { for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { if(engine.players[x].AI) { engine.console.print(engine.players[x].getColoredName()+"0xff7f7f left the game.\n"); engine.players.splice(x,1); AIsToDealWith--; if(window.svr) { var data = JSON.stringify({type:"leave",data:x}); window.svr.clients.forEach(function(ws){ws.send(data)}); } } if(AIsToDealWith == 0) break; } } } //clean up teams / remove ghost teams for(var x=engine.teams.length-1;x>=0;--x) if(engine.teams[x]) { for(var i=engine.teams[x].members.length-1;i>=0;--i) { if(engine.players.indexOf(engine.teams[x].members[i]) == -1) { engine.teams[x].members.splice(i,1); } } if(engine.teams[x].members.length == 0) { engine.teams.splice(x,1); } else { engine.teams[x].spawn(false,false); //and finally spawn everyone } } if(!engine.dedicated && !engine.players[engine.activePlayer].alive) changeViewTarget(1); } if(window.svr) { var teams = []; for(var x=engine.teams.length-1;x>=0;--x) if(engine.teams[x]) { teams.push({id:x,name:engine.teams[x].name,x:engine.teams[x].x,y:engine.teams[x].y,z:engine.teams[x].z}); } teams=JSON.stringify({type:"team",data:teams}); window.svr.clients.forEach(function(ws){ws.send(teams);ws.senddata(0)}); } }//*/ -function loadRound(dlmap) +game.loadRound = function(dlmap) { if(typeof(dlmap) != "undefined") { engine.mapString = dlmap; engine.loadedMap = settings.MAP_FILE; } engine.mapXML = xmlify(engine.mapString); engine.console.print("Preparing grid...\n",false); if(window.svr) window.svr.clients.forEach(function(ws){ws.send('{"type":"newRound"}')}); //virtual map data (used for positions, lines and stuff to calculate) engine.map = {zones:[],spawns:[],walls:[]}; engine.expl = []; engine.deaths = 0; engine.winzone = false; //GRID buildGrid();//buildObjects.js engine.scene.add(engine.grid); //WALLS buildWalls();//buildObjects engine.scene.add(engine.walls); //ZONES buildZones(); engine.scene.add(engine.zones); if(settings.SOUNDS_INTRO) playSound(bufferLoader.bufferList[bufferLoader.other+1],0.5,1,false,ctx.destination); if(settings.ROUND_CONSOLE_MESSAGE != "") { engine.console.print(settings.ROUND_CONSOLE_MESSAGE+"\n"); } loadcfg(settings.ROUND_COMMAND.replace(/\\n/g,"\n")); //GAME engine.gtime = -4000; //PLAYERS //CYCLE if(!engine.network) { - ensurePlayersSane();ensurePlayersSane(); + game.processPlayers();game.processPlayers(); if(engine.round == 0) { engine.console.print("Resetting scores...\n"); for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { engine.players[x].score = 0; } } engine.round++; engine.console.print("Go (round "+engine.round+" of "+settings.LIMIT_ROUNDS+")!\n"); } if(!engine.camera) { var aspectRatio = (window.innerWidth / window.innerHeight); engine.camera = new THREE.PerspectiveCamera( settings.CAMERA_FOV, aspectRatio, settings.CAMERA_NEAR_RENDER, settings.CAMERA_FAR_RENDER ); engine.camera.up = new THREE.Vector3(0,0,1); //Z is up, X and Y is l+r and b+f } engine.camera.position.set(engine.logicalBox.center.x*engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.center.y*engine.REAL_ARENA_SIZE_FACTOR,3); try{engine.camera.lookAt( new THREE.Vector3(engine.players[engine.viewTarget].position.x,engine.players[engine.viewTarget].position.y,engine.player[engine.viewTarget].position.z) );} catch(e){engine.camera.lookAt( new THREE.Vector3(engine.logicalBox.center.x*engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.center.y*engine.REAL_ARENA_SIZE_FACTOR,0) );} - updateScoreBoard(); + game.updateScoreBoard(); engine.lastGameTime = engine.lastRenderTime = engine.fpsTime = engine.timeStart = performance.now(); engine.totalPauseTime = 0; engine.fastestPlayer = engine.fastestSpeed = 0; engine.timemult = 1; engine.asendtm = 0; engine.winner = undefined; engine.roundCommencing = false; - getGoing(); + game.start(); }//end of init main -function game(oneoff=false) +game.run = function(oneoff=false) { if(!engine.roundCommencing && !engine.paused) { - if(engine.network) - { - var cycle = engine.players[engine.activePlayer],data={},len=0; - if(cycle.braking != cycle.brakingPrev) {data.braking=cycle.braking; cycle.brakingPrev=cycle.braking; ++len;} - if(cycle.boosting != cycle.boostingPrev) {data.boosting=cycle.boosting; cycle.boostingPrev=cycle.boosting; ++len;} - - if(len > 0) - { - engine.connection.send(JSON.stringify({type:"playdata",data:data})); - } - } - if(!oneoff && settings.GAME_LOOP != 1) {setTimeout(game,1000/settings.DEDICATED_FPS); engine.gameRunning = true;} + if(engine.network) engine.network.syncPlayData(); + if(!oneoff && settings.GAME_LOOP != 1) {setTimeout(game.run,1000/settings.DEDICATED_FPS); engine.gameRunning = true;} //time handlers and delta var timenow = performance.now()/settings.TIME_FACTOR; var rDelta = (timenow-engine.lastGameTime); if(rDelta > settings.TIMESTEP_MAX*1000 && engine.gtime > 0) { var more = true; rDelta = settings.TIMESTEP_MAX*1000; //engine.timeStart += rDelta; } else var more = false; var delta = rDelta*engine.timemult; var timestep = delta/1000; engine.totalPauseTime += (timenow-engine.lastGameTime)-delta; engine.lastGameTime += rDelta; engine.avgTimeStep += rDelta/1000; engine.avgTimeStep /= 2; if(!engine.network && !engine.dedicated) engine.players[0].ping = parseInt(engine.avgTimeStep*1000) //if(!engine.network && timestep > engine.avgTimeStep*10 && rDelta == delta) {engine.totalPauseTime += timestep; console.log("Compensated skip of "+delta+"ms."); timestep = engine.avgTimeStep;} engine.timemult += (engine.asendtm*timestep); if(engine.timemult > 100) engine.timemult = 100; //var timeElapsed = timenow-engine.timeStart-engine.totalPauseTime-4000; //skipping ahead at the beginning of the round is silly //engine.gtime = timeElapsed; var timeElapsed = (engine.gtime += delta); if(!engine.thread_collisiondetect && (settings.GAME_LOOP != 0.5 || !oneoff)) getCycleSensors(); var pldata = {}; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { var cycle = engine.players[x]; if(cycle.alive) { if(cycle.newPos) doNetSlide(cycle,timestep); if(timeElapsed > 0) { if(x == engine.activePlayer) { if(settings.HACK_TURN_LEFT_WHEN_POSSIBLE > 0) { if(cycle.sensor.left > settings.HACK_TURN_SENSOR_DIST) { cycle.turn(-1); settings.HACK_TURN_LEFT_WHEN_POSSIBLE--; } } if(settings.HACK_TURN_RIGHT_WHEN_POSSIBLE > 0) { if(cycle.sensor.right > settings.HACK_TURN_SENSOR_DIST) { cycle.turn(1); settings.HACK_TURN_RIGHT_WHEN_POSSIBLE--; } } } //bot turning if(cycle.AI) { cycle.AI.think(timestep); } else if(x==engine.activePlayer && (cycle.chatting || settings.CHATBOT_ALWAYS_ACTIVE)) { if(timenow > cycle.lastTurnTime+(settings.CYCLE_DELAY*1000) && cycle.sensor.front < Math.min(5,cycle.sensor.left,cycle.sensor.right)) { if(cycle.sensor.right < cycle.sensor.left) cycle.turn(-1); else if(cycle.sensor.right > cycle.sensor.left) cycle.turn(1); else cycle.turn([-1,1][Math.round(Math.random()*1)]); } } //do turning //if(cycle.turnQueue.length > 0) { //if(cycle.turnQueue.length > 0 && x == 0)console.log(JSON.parse(JSON.stringify(cycle.turnQueue))); if(cycle.turnQueue.length > settings.CYCLE_TURN_MEMORY) cycle.turnQueue.splice(0,cycle.turnQueue.length-settings.CYCLE_TURN_MEMORY); var shouldTime = cycle.lastTurnTime+(settings.CYCLE_DELAY*1000); if(cycle.turnQueue.length > 0 && timeElapsed >= shouldTime/*-delta*/) { //allow the cycle to turn when it should (???) //var diff = (shouldTime-timeElapsed)/1000; //cycle.update(diff); if(settings.CYCLE_MIDAIR_TURN || cycle.position.z-cycle.sensor.bottom == cycle.model.rotation.y) { var dir = cycle.turnQueue[0],dirmult; var olddir = cdir(cycle.rotation.z); if(engine.network && settings.DEBUG_NETWORK_TURN_WAIT) { var rot = normalizeRad(cycle.rotation.z - (pi(2)/settings.ARENA_AXES)*dir); engine.connection.send(JSON.stringify({ type:"turn",data:rad2deg(rot),gtime:cycle.gameTime })); cycle.lastTurnTime = Infinity; cycle.turnQueue.splice(0,1); continue; } cycle.dir.front = (dirmult = cdir(cycle.rotation.z -= (pi(2)/settings.ARENA_AXES)*dir)); //cycle.rotation.z = cycle.rotation.z%(Math.PI*2); //if(cycle.rotation.z < 0) cycle.rotation.z += Math.PI*2; cycle.rotation.z = normalizeRad(cycle.rotation.z); cycle.speed *= settings.CYCLE_TURN_SPEED_FACTOR; cycle.rotation.x = Math.cos(cycle.rotation.z)*0.4*dir; //tilts the cycle cycle.rotation.y = Math.sin(cycle.rotation.z)*0.4*dir; //if(settings.GRAB_SENSORS_ON_TURN) { getCycleSensors(true); } cycle.collidetime = timeElapsed+(((cycle.sensor.front)/cycle.speed)*1000); var mult = (1-settings.CYCLE_RUBBER_MINADJUST); cycle.minDistance.front = Math.max(0,Math.min(cycle.sensor.front*mult,settings.CYCLE_RUBBER_MINDISTANCE)); cycle.lastpos = cycle.position.clone(); //redundant, should be handled by getCycleSensors if(cycle.haswall) cycle.newWallSegment(); if(engine.network) { engine.connection.send(JSON.stringify({ type:"turn",data:rad2deg(cycle.rotation.z),gtime:cycle.gameTime, position:[cycle.position.x,cycle.position.y,cycle.position.z], speed:cycle.speed, rubber:cycle.rubber, brakes:cycle.brakes })); } if(window.svr) //force a player sync { var data = JSON.stringify({type:"griddata",data:[{ position:[cycle.position.x,cycle.position.y,cycle.position.z], direction:rad2deg(cycle.rotation.z), speed:cycle.speed, rubber:cycle.rubber, alive:cycle.alive, netid:x, wall:cycle.walls.map, }],gtime:engine.gtime}); if(cycle.speed > 1) cycle.update(0.01/cycle.speed); var data2 = JSON.stringify({type:"griddata",data:[{ position:[cycle.position.x,cycle.position.y,cycle.position.z], direction:rad2deg(cycle.rotation.z), speed:cycle.speed, rubber:cycle.rubber, alive:cycle.alive, netid:x}],gtime:engine.gtime}); window.svr.clients.forEach(function(ws){ws.send(data);ws.send(data2)}); } } cycle.turnQueue.splice(0,1); cycle.lastTurnTime = timeElapsed; } } cycle.update(); } if(cycle.rubber > settings.CYCLE_RUBBER+0.0001) cycle.rubber = settings.CYCLE_RUBBER+0.0001; else if(cycle.rubber > 0) cycle.rubber -= (timestep/settings.CYCLE_RUBBER_TIME)*cycle.rubber; else cycle.rubber = 0; //if(timeElapsed/1000 > -3 && cycle.alive) } else if(cycle.walls.map.length != 0 && timenow-cycle.dedtime >= settings.WALLS_STAY_UP_DELAY*1000) { cycle.walls.map = []; console.log("DELETE WALLS id "+x); } else if(settings.RESPAWN_TIME >= 0 && !cycle.alive && timenow-cycle.dedtime > settings.RESPAWN_TIME*1000) { cycle.spawn({x:cycle.position.x||0,y:cycle.position.y||0,z:cycle.position.z||0,dir:cycle.rotation.z||0}); } } if(timeElapsed > -3000 && timeElapsed < 1000) { //timer var time=-timeElapsed/1000,gTime = Math.ceil(time); if(gTime < 0) gTime = 0; if(typeof(engine.timer)=="undefined" || engine.timer != gTime) { engine.timer = gTime; if(engine.hud) engine.hud.fadein = true; if(engine.dedicated) console.log(gTime); else centerMessage(gTime,(gTime-time)); //engine.console.print(gTime+"\n"); } } else if(!engine.network && typeof(engine.winner) == "undefined") { - checkForWinner(); + game.checkForWinner(); } if(!engine.network) { if(!engine.winzone && settings.WIN_ZONE_MIN_ROUND_TIME+engine.deaths*(settings.WIN_ZONE_MIN_LAST_DEATH) < timeElapsed/1000) { var zone = {expansion:settings.WIN_ZONE_EXPANSION}; if(settings.WIN_ZONE_DEATHS) { engine.console.print("Death zone activated. Avoid it!\n"); zone.type = "death"; } else { engine.console.print("Win zone activated. Enter it to win the round.\n"); zone.type = "win"; } zone.radius = Math.min(SMALL_NUM,settings.WIN_ZONE_INITIAL_SIZE)*engine.REAL_ARENA_SIZE_FACTOR; zone.x = engine.REAL_ARENA_SIZE_FACTOR*(engine.logicalBox.center.x+(settings.WIN_ZONE_RANDOMNESS*(Math.random()-0.5)*2)*engine.logicalBox.center.x); zone.y = engine.REAL_ARENA_SIZE_FACTOR*(engine.logicalBox.center.y+(settings.WIN_ZONE_RANDOMNESS*(Math.random()-0.5)*2)*engine.logicalBox.center.y); new Zone(zone).spawn(); console.log("ZONE SPAWNED: "+zone.type); engine.winzone = true; } } for(var x=engine.zones.children.length-1;x>=0;--x) { var zone = engine.zones.children[x].cfg; //zones expand if(zone.radius > 0) { engine.zones.children[x].scale.x = engine.zones.children[x].scale.y = (zone.radius += zone.expansion*timestep*engine.REAL_ARENA_SIZE_FACTOR); } else if(zone.radius < 0) engine.zones.children[x].scale.x = engine.zones.children[x].scale.y = zone.radius = 0; if(zone.type == "ball" || zone.type == "soccerball") { for(var z=engine.zones.children.length-1;z>=0;--z) { var z2n = engine.zones.children[z].cfg; if( ( (zone.type == "ball" && z2n.type == "fortress") || (zone.type == "soccerball" && z2n.type == "soccergoal") ) && is_in_circle(z2n.mesh.position.x,z2n.mesh.position.y,z2n.radius,zone.mesh.position.x,zone.mesh.position.y,zone.radius)) { if(!engine.network && engine.winner == undefined) { if(zone.lastHitCycle) { if(engine.teams.indexOf(zone.lastHitCycle.team) == z2n.team) { engine.console.print(zone.lastHitCycle.getColoredName()+"0xRESETT scored in their own goal and lost a point. Boo!\n"); zone.lastHitCycle.addScore(-1); } else { engine.console.print(zone.lastHitCycle.getColoredName()+"0xRESETT scored a goal for 1 point.\n"); zone.lastHitCycle.addScore(1); } } - engine.winner = false; startNewRound(); + engine.winner = false; game.reqNewRound(); } else { zone.xdir *= 1-timestep; zone.ydir *= 1-timestep; if(!engine.dedicated) centerMessage("Goal!"); } } } if(settings.BALL_SPEED_DECAY) { var dir = cdir(Math.atan2(zone.ydir,zone.xdir)); var speed = Math.sqrt((zone.xdir*zone.xdir)+(zone.ydir*zone.ydir)); var decay = settings.BALL_SPEED_DECAY*delta; if(decay > speed) decay = speed; speed -= decay; zone.xdir = dir[0]*speed; zone.ydir = dir[1]*speed; } } else if(zone.type == "flagHeld") { zone.mesh.position.x = zone.heldBy.position.x; zone.mesh.position.y = zone.heldBy.position.y; var h=[]; for(var z=engine.zones.children.length-1;z>=0;--z) { if(zone.type == "flagHeld") { h.push(zone.team); } } for(var z=engine.zones.children.length-1;z>=0;--z) { var z2n = engine.zones.children[z].cfg; if( (zone.type == "flagHeld" && z2n.type == "fortress") && is_in_circle(z2n.mesh.position.x,z2n.mesh.position.y,z2n.radius,zone.heldBy.position.x,zone.heldBy.position.y,zone.radius)) { if(engine.teams.indexOf(zone.heldBy.team) == z2n.team) { if(h.length > 1 && h.indexOf(z2n.team) > -1) { if(!zone.homeMSG) { engine.console.print(zone.heldBy.getColoredName()+"0xRESETT took the enemy flag home, but their team flag must be returned to their base. Get them!\n"); zone.homeMSG = true; } } else { engine.console.print(zone.heldBy.getColoredName()+"0xRESETT took the flag to their base for 1 point!\n"); zone.heldBy.addScore(1); zone.heldBy.hasFlag = null; zone.type = "flag"; zone.mesh.position.x = zone.px; zone.mesh.position.y = zone.py; zone.homeMSG = false; zone.netSync(); } } } } } //zone effect var inzone = false; for(var y=engine.players.length-1;y>=0;--y) if(engine.players[y] && engine.players[y].alive) { var cycle = engine.players[y]; //dont handle zones we don't need to if(!zone.netObject/* || zone.type.indexOf("ball") >= 0*/) { //var lastdist = zone.distance(cycle.lastpos); //var dist = zone.distance(cycle.position); var lastdist = pointDistance(zone.mesh.position.x,zone.mesh.position.y,cycle.lastpos.x,cycle.lastpos.y)-zone.radius; var dist = pointDistance(zone.mesh.position.x,zone.mesh.position.y,cycle.position.x,cycle.position.y)-zone.radius; var inZone = (dist <= 0), wasInZone = (lastdist <= 0); if(inZone) { var timediff = cycle.speed*dist; var hitTime = timeElapsed-timediff; if(!wasInZone) zone.onEnter(cycle,hitTime,timestep); if(cycle.alive) zone.onInside(cycle,engine.gtime,timestep); } else if(wasInZone) //left zone { zone.onLeave(cycle,engine.gtime,timestep); //TODO: figure out "precise" left time } else { zone.onOutside(cycle,engine.gtime,timestep); } } if(zone.type == "fortress") //fortress recover rate { zone.rotationSpeed += (settings.ZONE_SPIN_SPEED-zone.rotationSpeed)*timestep*settings.FORTRESS_CONQUEST_DECAY_RATE; } } if(typeof(zone.xdir)+typeof(zone.ydir) !== "undefinedundefined") { if(zone.bounce && zone.mesh.walldist <= timestep) { var dir = cdir(Math.atan2(zone.ydir,zone.xdir)); //zone.mesh.position.x -= dir[0]*zone.mesh.walldist; zone.mesh.position.y -= dir[1]*zone.mesh.walldist; var mindist=Infinity,apc=false; //var px = zone.mesh.position.x+(zone.mesh.walldist*zone.xdir), py = zone.mesh.position.y+(zone.mesh.walldist*zone.ydir); var px = zone.mesh.position.x+zone.radius*dir[0], py = zone.mesh.position.y+zone.radius*dir[1]; //lineIntersect(posx,posy,posx+(dir[0]*rg),posy+(dir[1]*rg),w1x,w1y,w2x,w2y) for(var i=4;i>0;--i) { var xdir = Math.sin(Math.PI*2*(i/4)), ydir=Math.cos(Math.PI*2*(i/4)); if(lineIntersect(zone.mesh.position.x,zone.mesh.position.y,zone.mesh.position.x+xdir*(zone.radius+zone.mesh.walldist),zone.mesh.position.y+ydir*(zone.radius+zone.mesh.walldist),zone.mesh.wall[0],zone.mesh.wall[1],zone.mesh.wall[2],zone.mesh.wall[3])) { //console.log(i); switch(i) { case 1: zone.xdir = -Math.abs(zone.xdir); break; case 2: zone.ydir = Math.abs(zone.ydir); break; case 3: zone.xdir = Math.abs(zone.xdir); break; case 4: zone.ydir = -Math.abs(zone.ydir); break; } apc = true; } } //console.log(apc); //*/zone.xdir *= -1; zone.ydir *= -1; //if(!apc) {console.log("?"); zone.xdir *= -1; zone.ydir *= -1;} var dir = cdir(Math.atan2(zone.ydir,zone.xdir)); //zone.mesh.position.x -= dir[0]*zone.mesh.walldist; zone.mesh.position.y -= dir[1]*zone.mesh.walldist; zone.mesh.position.x += dir[0]; zone.mesh.position.y += dir[1]; zone.mesh.position.x += zone.xdir*(timestep); zone.mesh.position.y += zone.ydir*(timestep); if(settings.BALL_SPEED_HIT_DECAY) { var speed = Math.sqrt((zone.xdir*zone.xdir)+(zone.ydir*zone.ydir)); var decay = settings.BALL_SPEED_HIT_DECAY*delta; if(decay > speed) decay = speed; speed -= decay; zone.xdir = dir[0]*speed; zone.ydir = dir[1]*speed; } zone.netSync(); } else { zone.mesh.position.x += zone.xdir*timestep; zone.mesh.position.y += zone.ydir*timestep; } } } var dc = Object.keys(engine.delayedcommands); for(var x=dc.length-1;x>=0;x--) { var cmd = engine.delayedcommands[dc[x]]; loadcfg(cmd[0]); if(dc[x] >= engine.gtime) { if(cmd[1] > 0) { engine.delayedcommands[dc[x]+cmd[1]] = cmd; } delete engine.delayedcommands[dc[x]]; } } //if(more) game(); } else { engine.gameRunning = false; } } -function doDeath(cycle,escape=false) +game.killBlame = function(cycle,escape=false) { if(escape || (cycle.sensor.nearestobj == "rim" && !cycle.sensor.lastnonselfobj)) { if(escape || ( cycle.position.x-cycle.minDistance.front > engine.logicalBox.max.x || cycle.position.y-cycle.minDistance.front > engine.logicalBox.max.y || cycle.position.x+cycle.minDistance.front < engine.logicalBox.min.x || cycle.position.y+cycle.minDistance.front < engine.logicalBox.min.y )) { engine.console.print(cycle.getColoredName()+"0xRESETT tried to escape the game grid.\n"); } else { engine.console.print(cycle.getColoredName()+"0xRESETT committed suicide.\n"); } } else { var objtoaccuse = typeof(cycle.sensor.lastnonselfobj)=="undefined"?cycle.sensor.nearestobj:cycle.sensor.lastnonselfobj; if(objtoaccuse == cycle) { engine.console.print(cycle.getColoredName()+"0xRESETT committed suicide.\n"); } else if(typeof(objtoaccuse) == "object") { engine.console.print(objtoaccuse.getColoredName()+"0xRESETT core dumped "+cycle.getColoredName()+"0xRESETT for 1 point.\n"); objtoaccuse.addScore(1); } else { engine.console.print(cycle.getColoredName()+"0xRESETT died.\n"); } } cycle.kill(); } -function simulatePlayer(cycle,timestep) +game.start = function() { - console.warn("Deprecated call to simulatePlayer"); - cycle.update(timestep); -} - -function getGoing() -{ - if(!engine.gameRunning) game(); + if(!engine.gameRunning) game.run(); if(!engine.renderRunning) render(); } -function pauseRender() +game.pause = function() { engine.paused = true;//cuts off the loop engine.startOfPause = performance.now(); audioStop(); } -function unpauseRender() +game.unpause = function() { for(var ctrl in engine.controls) { engine.controls[ctrl] = []; } //renderLoop = true;//replaces cutoff if(engine.paused) { engine.paused = false; audioStart(); engine.lastGameTime = engine.lastRenderTime = engine.fpsTime = performance.now();//resets delta so we don't pretend the game should have been playing the entire time we were paused var endOfPause = performance.now(); engine.totalPauseTime += (endOfPause - engine.startOfPause); - getGoing();//starts the loop again + game.start();//starts the loop again } } -function changeViewTarget(a=1,forcechange=false) +game.changeViewTarget = function(a=1,forcechange=false) { if(engine.dedicated) return; if(a != 0) { if(!forcechange && engine.players[engine.activePlayer].alive) return; var first = true; //force the loop to get started var itcount = 0; /*if(alive > 0) */while(first || !engine.players[engine.viewTarget] || !engine.players[engine.viewTarget].alive) { first = false; engine.viewTarget+=a; if(engine.viewTarget >= engine.players.length) engine.viewTarget = 0; if(engine.viewTarget < 0) engine.viewTarget = engine.players.length+engine.viewTarget; itcount++; if(itcount > engine.players.length) break; //console.log(engine.viewTarget); } } if(engine.view == 'cockpit') { for(var x=0;x>engine.players.length;x++) { if(x == engine.viewTarget) engine.players[x].audio.gain.setTargetAtTime(0.2, ctx.currentTime, 0.02); else engine.players[x].audio.gain.setTargetAtTime(6, ctx.currentTime, 1); } } if(!engine.network && !engine.players[engine.activePlayer].spectating && typeof(engine.winner) == "undefined") { switch(settings.FINISH_TYPE) { case 2: engine.asendtm = 0.2; centerMessage("Time Warp!",Infinity); if(engine.hud) engine.hud.fadein = false; break; case 3: centerMessage("Please wait...",Infinity); setTimeout(function() { engine.timemult = 100; while(typeof(engine.winner) == "undefined") { game(true) } engine.timemult = 1; },100); break; } } engine.console.print("Watching "+engine.players[engine.viewTarget].name+"...\n"); } -function checkForWinner() +game.checkForWinner = function() { var alivecount = aliveaicount = 0; var numPlay = 0; var alive = [], theplayer = false; var declareRoundWinner = typeof(engine.declareRoundWinner) != "undefined"; for(var x=engine.players.length-1;x>=0;--x) if(typeof(engine.players[x]) != "undefined") { if(!engine.players[x].spectating) numPlay++; if(engine.players[x].alive) { alivecount++; if(engine.players[x].AI) aliveaicount++; alive.push(engine.players[x]); } if(declareRoundWinner && engine.players[x].name == engine.declareRoundWinner) { engine.winner = engine.players[x]; engine.declareRoundWinner = undefined; } } if( (declareRoundWinner) || (settings.GAME_TYPE == 1 && numPlay > 1 && (alivecount <= 1 || (settings.FINISH_TYPE == 1 && aliveaicount == alivecount))) || (/*settings.GAME_TYPE == 0 && */(alivecount <= 0)) ) { if(!declareRoundWinner) { engine.winner = (typeof(alive[0]) == "undefined")?{name:undefined}:alive[0]; } if(settings.RIM_WALL_COLOR_MODE == 2) { if(engine.winner && settings.RIM_WALL_COLOR_MODE == 2) { var color = new THREE.Color(engine.winner.cycleColor); settings.RIM_WALL_RED = color.r; settings.RIM_WALL_GREEN = color.g; settings.RIM_WALL_BLUE = color.b; } } if(engine.asendtm > 0) {engine.asendtm = 0; engine.timemult = 1; centerMessage("Time Warp!",0);} setTimeout(function(){centerMessage("Winner: "+engine.winner.name)},1000); - if(!window.svr || window.svr.clients.size != 0) startNewRound(); - }//*/ - /*if(aliveaicount == alivecount) - { - engine.console.print("Vroom!"); - engine.timemult = 2; - engine.asendtm = 1.1; - }//*/ + if(!window.svr || window.svr.clients.size != 0) game.reqNewRound(); + } } -function startNewRound() +game.reqNewRound = function() { if(typeof(engine.winner) == "undefined") engine.winner = null; var endin = 4000; if(engine.round >= settings.LIMIT_ROUNDS) { var highscore = -Infinity, highplayer; for(var x=engine.players.length-1;x>=0;--x) if(typeof(engine.players[x]) != "undefined") { var cycle = engine.players[x]; if(cycle.score > highscore) { highplayer = cycle; highscore = cycle.score; } else if(cycle.score == highscore) { highplayer = null; } } if(highplayer != null) { setTimeout(function(){centerMessage("Match Winner: "+highplayer.name);engine.console.print("Match Winner: "+highplayer.name+" with "+highscore+" points.\n")},4000); } engine.round = 0; endin += 4000; } - if(!settings.ROUND_WAIT) engine.uRound = setTimeout(doNewRound,endin); + if(!settings.ROUND_WAIT) engine.uRound = setTimeout(game.doNewRound,endin); } -function updateScoreBoard() +game.updateScoreBoard = function() { if(window.svr) { var tmp = []; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { tmp.push({netid:x,score:engine.players[x].score,ping:engine.players[x].ping,chatting:engine.players[x].chatting}); } var data = JSON.stringify({type:"scoredata",data:tmp}); window.svr.clients.forEach(function(ws){ws.send(data);}); } if(engine.network) { engine.connection.send(JSON.stringify({type:"playdata",data:{chatting:engine.players[engine.activePlayer].chatting}})); } if(engine.dedicated || scoreboard.style.display == "none") return; var scoreBoard = document.getElementById("scoreboard").children[0]; var playersSB = scoreBoard.children[1]; var tmp = ""; for(var x=0;x"+(cycle.chatting?"*":" ")+replaceColors(cycle.getColoredName())+""+replaceColors(cycle.alive?"0x00ff00Yes":"0xff0000No")+""+cycle.score+""+cycle.ping+""+(cycle.team?cycle.team.name:"")+""; } playersSB.innerHTML = tmp; } -/*function updateScoreBoard() -{ - var scoreboard = ""; - for(var x=engine.players.length-1;x>=0;--x) if(typeof(engine.players[x]) != "undefined") - { - var cycle = engine.players[x]; - scoreboard += (cycle.chatting?"*":" ")+removeColors(cycle.name)+" "+cycle.alive?"Yes":"No"+" "+cycle.score+" "+cycle.ping+" "; - } -}*/ +}(typeof(exports) === 'undefined' ? this.game = {} : exports)); diff --git a/scripts/init.js b/scripts/init.js index 968e7d7..d08a13d 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -1,126 +1,136 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +if(typeof(global) == undefined) + global = window; + //resize window listener function var resizeWindow = function() { if(engine.renderer) { engine.renderer.setSize(window.innerWidth,window.innerHeight); engine.renderer.render(engine.scene, engine.camera); } if(engine.camera) { engine.camera.aspect = window.innerWidth / window.innerHeight; engine.camera.updateProjectionMatrix(); } }; -function init() +global.init = function() { - //set renderer after detecting available renderer - if (Detector.webgl) { engine.renderer = new THREE.WebGLRenderer({ antialias: true });} - else { engine.renderer = new THREE.SoftwareRenderer(); engine.usingWebgl = false; } - engine.renderer.setSize(window.innerWidth, window.innerHeight); - document.body.appendChild(engine.renderer.domElement); + //let's load settings first. + loadsettingcfgs(); + window.onbeforeunload = saveusercfg; + if(!engine.dedicated) + { + //set renderer after detecting available renderer + if (Detector.webgl) { engine.renderer = new THREE.WebGLRenderer({ antialias: settings.ANTIALIAS });} + else { engine.renderer = new THREE.SoftwareRenderer(); engine.usingWebgl = false; } + engine.renderer.setSize(window.innerWidth, window.innerHeight); + document.body.appendChild(engine.renderer.domElement); + } engine.scene = new THREE.Scene(); + if(engine.dedicated) return; loadTextures(); window.addEventListener('touchstart',touchControl); window.addEventListener('resize',resizeWindow); } function gameLostTab() { if(engine.inputState == 'game') { pauseMenuToggle(); console.log("PAUSED because the tab lost focus"); } } function gameLostFocus() { - if(engine.inputState == 'game') { engine.players[engine.activePlayer].chatting = true; updateScoreBoard() } + if(engine.inputState == 'game') { engine.players[engine.activePlayer].chatting = true; game.updateScoreBoard() } } function gameGainedFocus() { - if(engine.inputState == 'game') { engine.players[engine.activePlayer].chatting = false; updateScoreBoard() } + if(engine.inputState == 'game') { engine.players[engine.activePlayer].chatting = false; game.updateScoreBoard() } } window.onblur = gameLostFocus; window.onfocus = gameGainedFocus; window.onload = function() { if(!engine.scene) init(); httpGetAsync("layout/menu.xml",function(output){ engine.menu = xmlify(output); menu('menu:main'); aamenurender(); if(typeof(_GET["ssl"]) != "undefined") { chsetting("CONNECT_SSL",_GET["ssl"]); } if(typeof(_GET["preset"]) != "undefined") { menu('menu:preset_loaded');menu('menu:preset_loaded'); menu('menu:loading'); preset(_GET["preset"]); } else if(typeof(_GET["connect"]) != "undefined") { var s = _GET["connect"].split(":"); settings.CONNECT_HOST = ""+s[0]; if(s.length > 1) settings.CONNECT_PORT = s[1]; connectToGame(); } }); //keyboard input window.addEventListener('keydown', keyboardKeyDown ); window.addEventListener('keyup', keyboardKeyUp ); (function() {//event listener cross browser support for auto-pause when focus lost 'use strict'; // Set the name of the "hidden" property and the change event for visibility var hidden, visibilityChange; if (typeof document.hidden !== "undefined") { hidden = "hidden"; visibilityChange = "visibilitychange"; } else if (typeof document.mozHidden !== "undefined") { // Firefox up to v17 hidden = "mozHidden"; visibilityChange = "mozvisibilitychange"; } else if (typeof document.webkitHidden !== "undefined") { // Chrome up to v32, Android up to v4.4, Blackberry up to v10 hidden = "webkitHidden"; visibilityChange = "webkitvisibilitychange"; } var handleVisibilityChange = function() { if(document[hidden]) gameLostTab() }; // Warn if the browser doesn't support addEventListener or the Page Visibility API if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") { alert("AUTO-PAUSE UNAVAILABLE"); } else { // Handle page visibility change document.addEventListener(visibilityChange, handleVisibilityChange, false); } })(); } diff --git a/scripts/input.js b/scripts/input.js index 52e9efd..1a543b2 100644 --- a/scripts/input.js +++ b/scripts/input.js @@ -1,501 +1,501 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ function stopPropagation(e) { e = e || event;/* get IE event ( not passed ) */ e.stopPropagation? e.stopPropagation() : e.cancelBubble = true; e.returnValue = false; } //EVENT INPUTS var keyboardKeyDown = function(e) { e = e || event;//safety (against what?) //get state here, like: playing, menu, chatting, etc. //refer to engine.inputState: menu | menu2 | game | chat | pause //for each input state, either switch(keyCode) {}, or send keycode to appropriate function var keyCode = typeof(keyCodeRemap[e.keyCode]) == "undefined"?e.keyCode:keyCodeRemap[e.keyCode]; var specificState = engine.inputState.split(':'); if(specificState[0] != "input" && settings.controls.console.indexOf(keyCode) > -1) //console key works everywhere except other inputs { var input = document.getElementById("input"); input.style.display = "block"; engine.inputStatePrev = engine.inputState; engine.inputState = "input:console"; if(engine.players[engine.activePlayer]) engine.players[engine.activePlayer].chatting = true; - updateScoreBoard(); + game.updateScoreBoard(); input.children[0].innerText = "Con:"; input.children[1].focus(); stopPropagation(e); } else if(specificState[0] == 'menu'/* || engine.paused*/) //any menu, not game { var actmenu = document.getElementsByClassName("menu-active")[0]; if(!actmenu) { if(engine.menus.length == 1) menu("menu:"+engine.menus[engine.menus.length-1]); else if(engine.menus.length > 1) menu("exitmenu"); else menu("menu:main"); return; } if(actmenu.attributes.id) actid = actmenu.attributes.id.value; else actid = ""; var actsp = actid.split(":"); /*if(actsp[0] != "var" && (actsp[2] != "str" || actsp[2] == "color")) stopPropagation(e);*/ //console.log(actid.split(":")); switch(keyCode) { case 13: //enter //menuSelect('enter',"key"); break; case 27: //esc stopPropagation(e); menuSelect('esc',"key"); break; case 37: //left menuSelect('left',"key"); break; case 38: //up menuSelect('up',"key"); break; case 39: //right menuSelect('right',"key"); break; case 40: //down menuSelect('down',"key"); break; } } else if(specificState[0] == 'input') { switch(keyCode) { case 9: //tab var input = document.getElementById("input"); var textbox = input.children[1]; var output = textbox.value; var split = output.split(" "); if(specificState[1] == "console") { } else switch(split[0]) { case "/console": split[split.length-1] break; default: var p = getPlayer(split[split.length-1]); if(p) { //textbox.value += p.getColoredName(); split[split.length-1] = p.getColoredName()+"0xffff7f"; if(split.length == 1) split[split.length-1] += ", "; else split[split.length-1] += " "; textbox.value = split.join(" "); } else { engine.console.print("Matches:\n"); for(var i=engine.players.length;i--;) { if(engine.players[i].name.toLowerCase().indexOf(name.toLowerCase()) > -1) { engine.console.print(engine.players[i].name+"\n"); } } } break; } stopPropagation(e); break; case 27: case 13: //escape or return var input = document.getElementById("input"); var textbox = input.children[1]; var output = textbox.value; textbox.value = ""; input.style.display = "none"; if(engine.players[engine.activePlayer]) engine.players[engine.activePlayer].chatting = false; engine.inputState = engine.inputStatePrev; - updateScoreBoard(); + game.updateScoreBoard(); if(keyCode == 13) //return { switch(specificState[1]) { case "chat": handleChat(engine.players[engine.activePlayer],output); break; case "console": engine.console.print("0x7f7fff> "+output+"\n"); loadcfg(output); break; } //engine.inputState = engine.inputStatePrev; } break; } } else if(engine.inputState == 'game') //game settings.controls { gameControl(keyCode);//send keycode to control function //if(engine.inputState.split(':')[0] == "input" || keyCode == 9) stopPropagation(e); } else //parse input state (typically settings to add/remove controls) { if(specificState[0] == 'controlAR') { stopPropagation(e); if(!e.forceHACK && e.keyCode == 27) { engine.inputState = "menu:"+engine.menus[engine.menus.length-1]; menu("reload"); return; } var temp_items = Object.keys(settings.controls); var temp_item = temp_items.indexOf(specificState[1]) || 0; if(settings.controls[specificState[1]].indexOf(keyCode) > -1) //control is there already { settings.controls[specificState[1]].splice(settings.controls[specificState[1]].indexOf(keyCode), 1); //remove } else { for(var i=0;i -1) { settings.controls[control].splice(settings.controls[control].indexOf(keyCode),1); //remove control found } } } settings.controls[specificState[1]].push(keyCode); //add control to chosen option settings.controls[specificState[1]].sort(function(a,b){return a-b});//sort listing in order } engine.inputState = "menu:"+engine.menus[engine.menus.length-1]; menu("reload"); //menu('menu-controls_'+temp_item); } } } var keyboardKeyUp = function(e) { if(engine.inputState == 'game')//game settings.controls { gameControlUp(typeof(keyCodeRemap[e.keyCode]) == "undefined"?e.keyCode:keyCodeRemap[e.keyCode]); } } //GAME function touchControl(e) { if(!document.oncontextmenu) document.oncontextmenu = function(){return false} if(engine.inputState == 'game') { //console.log(e); var sepX = document.body.offsetWidth/3, sepY = document.body.offsetHeight/2; if(engine.gtime > 0) { /*engine.console.print(e.touches.length+" touches"); for(var x=e.touches.length-1;x>=0;x--)*/ var x = 0; //other touch appears to get sent in another message anyway { //var dir = (e.touches[x].clientX < half)?-1:1; if(e.touches[x].clientX > sepX*2) { var dir = 1; } else if(e.touches[x].clientX > sepX) { if(e.touches[x].clientY < sepY) { engine.players[engine.activePlayer].boosting = !engine.players[engine.activePlayer].boosting; } else { engine.players[engine.activePlayer].braking = !engine.players[engine.activePlayer].braking; } return; } else { var dir = -1; } if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turn(dir); else if(engine.players[engine.activePlayer].dedtime+1000 > performance.now()) changeViewTarget(dir); } } } } //game settings.controls - keydown function gameControl(keycode) { if(engine.controls.pressed.indexOf(keycode) == -1) { if(engine.gtime > 0) { if(settings.controls.left.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turn(-1); else if(engine.players[engine.activePlayer].dedtime+1000 < performance.now()) changeViewTarget(-1); engine.controls.pressed.push(keycode); } if(settings.controls.right.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turn(1); else if(engine.players[engine.activePlayer].dedtime+1000 < performance.now()) changeViewTarget(1); engine.controls.pressed.push(keycode); } if(settings.controls.north.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turnAbs(0,1); } if(settings.controls.south.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turnAbs(0,-1); } if(settings.controls.east.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turnAbs(1,0); } if(settings.controls.west.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].turnAbs(-1,0); } if(settings.controls.togglebrake.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].braking = !engine.players[engine.activePlayer].braking; engine.controls.pressed.push(keycode); } if(settings.controls.brake.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) engine.players[engine.activePlayer].braking = true; engine.controls.pressed.push(keycode); } if(settings.controls.boost.indexOf(keycode) > -1) { if(engine.players[engine.activePlayer].alive) { if(settings.CYCLE_BOOST == 0 && settings.CYCLE_BRAKE < 0) engine.players[engine.activePlayer].braking = true; else engine.players[engine.activePlayer].boosting = true; } engine.controls.pressed.push(keycode); } if(settings.controls.jump.indexOf(keycode) > -1) { if(settings.CYCLE_MIDAIR_JUMP || engine.players[engine.activePlayer].position.z == 0) engine.players[engine.activePlayer].model.rotation.y = -settings.CYCLE_JUMP; engine.controls.pressed.push(keycode); } } if(settings.controls.look_left.indexOf(keycode) > -1) { engine.camera.userViewDir = engine.players[engine.viewTarget].left; engine.controls.pressed.push(keycode); } if(settings.controls.look_right.indexOf(keycode) > -1) { engine.camera.userViewDir = engine.players[engine.viewTarget].right; engine.controls.pressed.push(keycode); } if(settings.controls.look_forward.indexOf(keycode) > -1) { engine.camera.userViewDir = cdir(engine.players[engine.viewTarget].rotation.z); engine.controls.pressed.push(keycode); } if(settings.controls.look_back.indexOf(keycode) > -1) { engine.camera.userViewDir = cdir(engine.players[engine.viewTarget].rotation.z-Math.PI); engine.controls.pressed.push(keycode); } } if(settings.controls.chat.indexOf(keycode) > -1) { var input = document.getElementById("input"); input.style.display = "block"; engine.inputStatePrev = engine.inputState; engine.inputState = "input:chat"; engine.players[engine.activePlayer].chatting = true; - updateScoreBoard(); + game.updateScoreBoard(); input.children[0].innerText = "Say:"; input.children[1].focus(); } if ( settings.controls.camera.indexOf(keycode) > -1 ) { //do camera change if ( engine.controls.pressed.indexOf(keycode) == -1 ) { //if not pressed, CHANGE if (engine.views.indexOf(engine.view) == (engine.views.length-1) ) { //if is last view in list, go back to first [0] engine.view = engine.views[0]; } else {//change UP in list engine.view = engine.views[((engine.views.indexOf(engine.view)) + 1)]; } engine.controls.pressed.push(keycode);//add only if not added already to pressed keys } if(engine.view == 'cockpit') engine.players[engine.viewTarget].audio.gain.setTargetAtTime(0.2, ctx.currentTime, 0.02); else engine.players[engine.viewTarget].audio.gain.setTargetAtTime(6, ctx.currentTime, 1); console.log('CHANGE_CAMERA to '+engine.view); } if(settings.controls.score.indexOf(keycode) > -1) { var scoreboard = document.getElementById("scoreboard"); scoreboard.style.display = scoreboard.style.display=="none"?"block":"none"; - updateScoreBoard(); + game.updateScoreBoard(); } if(settings.controls.esc.indexOf(keycode) > -1) { //do in game menu if(!engine.paused) pauseMenuToggle(); } if(settings.controls.scroll_up.indexOf(keycode) > -1) { engine.console.scroll(-10); engine.console.time_manual = performance.now(); } if(settings.controls.scroll_down.indexOf(keycode) > -1) { engine.console.scroll(10); engine.console.time_manual = performance.now(); } if(settings.controls.scroll_end.indexOf(keycode) > -1) { if(settings.TEXT_OUT_MODE == 1) { var lines = engine.console.scrollback var lnnum = engine.console.scrollby; var currln = lines[lnnum]; } else { var lines = engine.console.innerText.split("\n"); var lnnum = (-(parseFloat(engine.console.style.top)/engine.console.scrollby)); var currln = lines[lnnum-1]; } engine.console.time_manual = 0; engine.console.scroll(lines.length-lnnum-1); } if(settings.controls.pause.indexOf(keycode) > -1) { console.log('PAUSE');//do pause } for(var x=settings.instantchats.length-1;x>=0;--x) { var chat = settings.instantchats[x]; for(var y=chat.input.length-1;y>=0;--y) { if(keycode == chat.input[y]) handleChat(engine.players[engine.activePlayer],chat.text); } } } //keyup function gameControlUp(keycode) { if(engine.controls.pressed.indexOf(keycode) > -1) { var temp = engine.controls.pressed.indexOf(keycode); if ( temp > -1 ) //if is pressed engine.controls.pressed.splice(temp, 1); } if(settings.controls.brake.indexOf(keycode) > -1) { engine.players[engine.activePlayer].braking = false; } if(settings.controls.boost.indexOf(keycode) > -1 ) { if(settings.CYCLE_BOOST == 0 && settings.CYCLE_BRAKE < 0) engine.players[engine.activePlayer].braking = false; engine.players[engine.activePlayer].boosting = false; } if( settings.controls.look_left.indexOf(keycode) > -1 || settings.controls.look_right.indexOf(keycode) > -1 || settings.controls.look_forward.indexOf(keycode) > -1 || settings.controls.look_back.indexOf(keycode) > -1 ) { engine.camera.userViewDir = false; } } var keyCodeRemap = { 59:186, 61:187 }; //some browsers used alternate keycodes for ; and = var keycodeList = { 8: 'Backspace', 13: 'Enter', 16: 'Shift', 20: 'Capslock', 17: 'Ctrl', 18: 'Alt', 19: 'Pause/Break', 27: 'Escape', 93: 'Menu', 9: 'Tab', 32: 'Spacebar', 33: 'Page Up',34: 'Page Down', 36: 'Home', 35: 'End', 37: 'Left Arrow', 39: 'Right Arrow', 38: 'Up Arrow', 40: 'Down Arrow', 45: 'Insert', 46: 'Delete', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 4: '6', 55: '7', 56: '8', 57: '9', 65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z', 91: 'Left Meta', 92: 'Right Meta', 111: 'NUM /', 106: 'NUM *', 109: 'NUM -', 107: 'NUM +', 110: 'NUM .', 96: 'NUM 0', 97: 'NUM 1', 98: 'NUM 2', 99: 'NUM 3', 100: 'NUM 4', 101: 'NUM 5', 102: 'NUM 6', 103: 'NUM 7', 104: 'NUM 8', 105: 'NUM 9', 112: 'F1', 113: 'F2', 114: 'F3', 115: 'F4', 116: 'F5', 117: 'F6', 118: 'F7', 119: 'F8', 120: 'F9', 121: 'F10', 122: 'F11', 123: 'F12', 144: 'Num Lock', 145: 'Scroll Lock', 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', 219: '[', 221: ']', 220: '\\', 222: '\'', }; diff --git a/scripts/menu.js b/scripts/menu.js index dbb72c4..d103375 100644 --- a/scripts/menu.js +++ b/scripts/menu.js @@ -1,459 +1,459 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //change setting via keyboard (menuselect left or right) function changeMenuItem(name,add,wrap=false,from=false,ref=false) { var actmenu = document.getElementsByClassName("menu-active")[0]; var actsp = name.split(":"); //console.log(actsp); if(actsp[0] == "list") { var elem = actmenu.children[0].children[1]; var keys = Object.keys(elem.options); var elempos = -1; for(i=0;i= keys.length) if(wrap) elempos = 0; else elempos = keys.length-1; if(elempos < 0) if(wrap) elempos = key.length-1; else elempos = 0; elem.innerHTML = replaceColors(elem.options[keys[elempos]]); elem.call(keys[elempos]); } else if(actsp[2] == "num") { var elem = actmenu.children[0].children[1]; var value = parseFloat(elem.innerText); if(from != "key:enter") value += add*actmenu.add; if(value > actmenu.max) if(wrap) value = actmenu.min; else value = actmenu.max; if(value < actmenu.min) if(wrap) value = actmenu.max; else value = actmenu.min; elem.innerText = value; chsetting(actsp[1],value); } } //MENU SELECT //keyboard function menuSelect(direction) { //detect menu //if (from != 'menu-about') {//if its a screen that has a menu with options var themenu = document.getElementById('menuList').childNodes; //detect menu position var selectedItem = -1; for(var x=0;x= themenu.length) selectedItem = 0; //wrap to first item hoverSelect(themenu[selectedItem]); break; case "esc": //Exit Menu should be the last item, so convenient hack selectedItem = themenu.length-1; //[[FALLTHROUGH]] case "enter": document.activeElement.blur();//remove focus from any clicked menu items menu(themenu[selectedItem].id,"key:"+direction); break; //adjust setting - used to change a setting from current value, up or down some, whatever it is case "left": changeMenuItem(themenu[selectedItem].id,-1,false,"key:"+direction); break; case "right": changeMenuItem(themenu[selectedItem].id,1,false,"key:"+direction); break; } } function menuFindPrevSelectable() { var themenu = document.getElementById('menuList').childNodes; var selectedItem = -1; for(var x=themenu.length-1;x>=0;x--) { if(selectedItem >= 0 && themenu[x].href) { return x; } if(hasClass(themenu[x],'menu-active')) selectedItem = x; //find which one is selected } return themenu.length-1; } function menuFindNextSelectable() { var themenu = document.getElementById('menuList').childNodes; var selectedItem = -1; for(var x=0;x= 0 && themenu[x].href) { return x; } if(hasClass(themenu[x],'menu-active')) selectedItem = x; //find which one is selected } return 0; } function hoverSelect(item) { var themenu = document.getElementById('menuList').childNodes; for(var x=0;x 0) { var menus = doc[0].getElementsByTagName("Menu"); for(var x=0;x"; var items = cmenu.getElementsByTagName("Item"); var list = document.createElement("UL"); list.setAttribute("id","menuList"); for(var i=0;i -1) updateScoreBoard(); + if(engine.playersByScore.indexOf(this) > -1) game.updateScoreBoard(); } addScore(x) { this.setScore(this.score+((x*1)||0)); } softReset() //! Resets the cycle state to default variables { this.speed = 0; this.lastSpeed = this.speed; this.rubber = 0; this.brakes = 1; this.braking = false; this.boosting = false; this.boost = 0; this.dedtime = 0; this.alive = false; this.collidetime = Infinity; this.sensor = {left:Infinity,right:Infinity,front:Infinity}; this.dir = {front:[0,0],left:[0,0],right:[0,0]}; this.minDistance = {front:settings.CYCLE_RUBBER_MINDISTANCE}; this.turnQueue = []; this.lastTurnTime = 0; this.gameTime = 0; this.handleNetTurn = true; } hardReset() //! Same as soft reset but resets all varaibles { this.softReset(); this.setScore(0); this.ping = 0; } getColoredName() //! Name with colors... { switch(typeof(this.tailColor)) { case "string": return this.tailColor.replace("#","0x")+this.name; case "object": return "0x"+this.tailColor.getHexString()+this.name; case "number": var color = this.tailColor.toString(16); color = ("0".repeat(6-color.length))+color; return "0x"+color+this.name; default: console.warn("Can't get color"); return "0xRESETT"+this.name; } } getBoringName() //! Name without colors... { return removeColors(this.name); } newWallSegment() //should be called on turns { var adj = 0.7, wmap = this.walls.map, dirmult = this.dir.front; wmap[wmap.length-1] = [this.position.x,this.position.y,this.position.z]; wmap[wmap.length] = [this.position.x,this.position.y,this.position.z]; this.resetCurrWallSegment(false,1); var wall = newWall(this.tailColor,this.position.x,this.position.y,this.position.z); var adjx = (dirmult[0]*adj), adjy = (dirmult[1]*adj); wall.scale.x -= adjx/wall.size; wall.scale.y -= adjy/wall.size; this.walls.add(wall); } /*recalcCurrWallLength(tocurrpos=false) { var adj = 0.7, dirmult = this.dir.front, wall = this.walls.children[this.walls.children.length-1]; var adjx = (dirmult[0]*adj), adjy = (dirmult[1]*adj); wall.scale.x += adjx/wall.size; wall.scale.y += adjy/wall.size; this.resetCurrWallSegment(tocurrpos,0,true); wall.scale.x -= adjx/wall.size; wall.scale.y -= adjy/wall.size; }*/ resetCurrWallSegment(tocurrpos=false,offset=0,breakWallLength=false) //! Redoes the current 3D wall segment to the actual wall segment. { if(this.walls.children.length == 0) return; var wmap = this.walls.map; var oldwall = this.walls.children[this.walls.children.length-1]; if(breakWallLength) { var sizex = oldwall.scale.x*oldwall.size,sizey = oldwall.scale.y*oldwall.size; this.walls.netLength -= Math.sqrt((sizex*sizex)+(sizey*sizey)); } if(typeof(wmap[wmap.length-3]) == "undefined") { //console.warn("Wall was undefined when trying to calculate wall size"); //console.log(); return; } if(tocurrpos) { wmap[wmap.length-2] = [this.position.x,this.position.y,this.position.z]; } var a = 2+offset, b=1+offset; oldwall.position.set(wmap[wmap.length-a][0],wmap[wmap.length-a][1],wmap[wmap.length-3][2]||0); oldwall.scale.x = (wmap[wmap.length-b][0]-wmap[wmap.length-a][0])/oldwall.size||1; oldwall.scale.y = (wmap[wmap.length-b][1]-wmap[wmap.length-a][1])/oldwall.size||1; if(breakWallLength) { var sizex = oldwall.scale.x*oldwall.size,sizey = oldwall.scale.y*oldwall.size; this.walls.netLength += Math.sqrt((sizex*sizex)+(sizey*sizey)); } else { this.calcWallLength(); } } calcWallLength(cycle) //! sets the actual wall length { var wmap = this.walls.map; this.walls.netLength = 0; for(var x=wmap.length;x>=0;x--) { if(wmap[x+1] !== undefined) { var p1=wmap[x],p2=wmap[x+1]; this.walls.netLength += pointDistance(p1[0],p1[1],p2[0],p2[1]); } } return this.walls.netLength; } resetWall(full=true) //! Completely redoes the 3D wall according to the actual wall { if(full === true) { for(var x=0,len=this.walls.children.length;x Math.PI) { return this.turn(1); } else if(ang != 0) { return this.turn(-1); } } spawn(cfg,respawn=true,update=true) { //configure cycle this.softReset(); this.position.set(cfg.x,cfg.y,cfg.z); this.lastpos = this.position.clone(); this.rotation.set(0,0,cfg.dir); this.lastdir = {front:0}; this.alive = true; this.speed = settings.CYCLE_START_SPEED; this.gameTime = this.spawntime = Math.max(0,engine.gtime); this.haswall = !(respawn||settings.CYCLE_FIRST_SPAWN_PROTECTION); //walls if(this.haswall) { this.walls = createWall(this,cfg.x,cfg.y,cfg.z); engine.scene.add(this.walls); } if(this.audio) this.audio.panner.connect(ctx.destination); engine.scene.add(this); - if(update) updateScoreBoard(); + if(update) game.updateScoreBoard(); if(this == engine.players[engine.activePlayer]) engine.viewTarget = engine.activePlayer; } kill() { this.resetCurrWallSegment(); this.alive = false; this.dedtime = performance.now(); engine.scene.remove(this); if(this == engine.players[engine.viewTarget] && !engine.dedicated) - setTimeout(function(){if(!engine.players[engine.viewTarget].alive)changeViewTarget()},3000); + setTimeout(function(){if(!engine.players[engine.viewTarget].alive)game.changeViewTarget()},3000); if(this.audio) { this.audio.panner.disconnect(); playSound(bufferLoader.bufferList[this.engineType+6],0.5,1,false,ctx.destination); spawnExplosion(this.position,this.cycleColor,this.tailColor); } - updateScoreBoard(); + game.updateScoreBoard(); if(this.hasFlag) { engine.console.print(this.getColoredName()+"0xRESETT dropped the flag they were holding.\n"); this.hasFlag.type = "flag"; this.hasFlag = null; } if(engine.dedicated) { var alive = 0, aliveAI = 0; for(var x=engine.players.length-1;x>=0;--x) if(engine.players[x]) { if(engine.players[x].alive) { alive++; if(engine.players[x].AI) aliveAI++; } } - if(alive==aliveAI) changeViewTarget(0); //this will handle the finish type stuff + if(alive==aliveAI) game.changeViewTarget(0); //this will handle the finish type stuff } } killAt(position,y=false,z=false) { if(y !== false) var x = position; else var x=position.x,y=position.y,z=position.z; this.position.set(x,y,z===false?this.position.z:z); this.kill(); } killIn(timestep) { this.update(timestep); this.kill(); } doChat(msg) { if(engine.network) { engine.connection.send(JSON.stringify({type:"chat",data:msg})); } else { engine.console.print(this.getColoredName()+"0xffff7f: "+msg+"\n"); if(engine.dedicated) {} } } update(timestep=false) //! Simulates game movement on cycles { if(timestep === false) { timestep = (engine.gtime-this.gameTime)/1000; if(timestep < 0) return; } this.gameTime += timestep*1000; //var timeElapsed = engine.gtime; var timeElapsed = this.gameTime; //movement var acceleration = 0; if(this.braking) { if(this.brakes > 0) { acceleration -= settings.CYCLE_BRAKE; this.brakes -= timestep*settings.CYCLE_BRAKE_DEPLETE; } } else if(this.brakes < 1) { this.brakes += timestep*settings.CYCLE_BRAKE_REFILL; } if(this.brakes > 1) this.brakes = 1; else if(this.brakes < 0) this.brakes = 0; if(this.boosting) acceleration += settings.CYCLE_BOOST; if(this.speed < settings.CYCLE_SPEED) { acceleration += (settings.CYCLE_SPEED-this.speed)*settings.CYCLE_SPEED_DECAY_BELOW; } else if(this.speed > settings.CYCLE_SPEED) { acceleration += (settings.CYCLE_SPEED-this.speed)*settings.CYCLE_SPEED_DECAY_ABOVE; } if(this.sensor.left < settings.CYCLE_WALL_NEAR || this.sensor.right < settings.CYCLE_WALL_NEAR) { var wallAccel = settings.CYCLE_ACCEL; if(wallAccel != 0) //don't bother if there's no accel { var accelMult = [(settings.CYCLE_WALL_NEAR-this.sensor.left)/settings.CYCLE_WALL_NEAR,(settings.CYCLE_WALL_NEAR-this.sensor.right)/settings.CYCLE_WALL_NEAR]; var accelTargets = [this.sensor.lnearestobj,this.sensor.rnearestobj]; for(var z=2;z--;) { if(accelMult[z] > 0) { if(accelTargets[z] == "rim") { wallAccel *= settings.CYCLE_ACCEL_RIM; } else if(typeof(accelTargets[z]) == "object") { if(accelTargets[z] == this) { wallAccel *= settings.CYCLE_ACCEL_SELF; } else { wallAccel *= settings.CYCLE_ACCEL_ENEMY; } } else { throw "Accel target is unknown."; } wallAccel *= accelMult[z]; } } //console.log(wallAccel); acceleration += wallAccel; } } this.speed += acceleration*timestep; if(this.speed < settings.CYCLE_SPEED*settings.CYCLE_SPEED_MIN) { this.speed = settings.CYCLE_SPEED*settings.CYCLE_SPEED_MIN; } if(settings.CYCLE_SPEED_MAX != 0 && this.speed > settings.CYCLE_SPEED*settings.CYCLE_SPEED_MIN) { this.speed = settings.CYCLE_SPEED*settings.CYCLE_SPEED_MAX; } this.accel = acceleration; if(this.speed < 0) this.speed = 0; //wheel spin per player if(!engine.dedicated) { this.model.children[1].rotation.y += (deg2rad(this.model.rotaon.front * this.speed) * timestep);//0.5x wheel size this.model.children[2].rotation.y += (deg2rad(this.model.rotaon.back * this.speed) * timestep); } //collision test var dir = cdir(this.rotation.z); var posx = this.position.x, posy = this.position.y; var escape = false; if(posx+settings.ARENA_BOUNDARY > engine.logicalBox.max.x*engine.REAL_ARENA_SIZE_FACTOR) { escape = true; this.position.x = (engine.logicalBox.max.x*engine.REAL_ARENA_SIZE_FACTOR)-settings.ARENA_BOUNDARY; } if(posy+settings.ARENA_BOUNDARY > engine.logicalBox.max.y*engine.REAL_ARENA_SIZE_FACTOR) { escape = true; this.position.y = (engine.logicalBox.max.y*engine.REAL_ARENA_SIZE_FACTOR)-settings.ARENA_BOUNDARY; } if(posx-settings.ARENA_BOUNDARY < engine.logicalBox.min.x*engine.REAL_ARENA_SIZE_FACTOR) { escape = true; this.position.x = (engine.logicalBox.min.x*engine.REAL_ARENA_SIZE_FACTOR)+settings.ARENA_BOUNDARY; } if(posy-settings.ARENA_BOUNDARY < engine.logicalBox.min.y*engine.REAL_ARENA_SIZE_FACTOR) { escape = true; this.position.y = (engine.logicalBox.min.y*engine.REAL_ARENA_SIZE_FACTOR)+settings.ARENA_BOUNDARY; } /*var collided = false; for(var y=0; y= this.sensor.front) if(dist >= this.sensor.front-(this.minDistance.front)) //if(this.speed*timestep >= this.sensor.front) { dist = (this.sensor.front-(this.minDistance.front)); //if(x == engine.viewTarget) console.warn(dist+" "+this.sensor.front+"\n"); collided = true; } if(dist > this.sensor.front-(this.minDistance.front)) //shouldn't happen { dist = this.minDistance.front; } if(collided || (escape && settings.ARENA_BOUNDARY_KILLS)) { if(this.rubber >= settings.CYCLE_RUBBER) { if(!engine.network) { - doDeath(this,escape); + game.killBlame(this,escape); } } else if( (settings.CYCLE_RUBBER_DEPLETE_RIM && this.sensor.nearestobj == "rim") || (settings.CYCLE_RUBBER_DEPLETE_SELF && this.sensor.nearestobj == this) || (settings.CYCLE_RUBBER_DEPLETE_ENEMY && this.sensor.nearestobj != this && typeof(this.sensor.nearestobj) == "object") ) { this.rubber += radj; } } var move2d = Math.cos(this.model.rotation.y), movez = -this.model.rotation.y; var dist2d = dist*move2d; var newx = dist2d*dir[0], newy = dist2d*dir[1], newz = dist*movez; this.position.x += newx; this.position.y += newy; this.position.z += newz; if(this.newPos) { this.newPos.x += newx; this.newPos.y += newy; } if(this.position.z-this.sensor.bottom < this.model.rotation.y) { this.model.rotation.y = this.position.z; if(this.model.rotation.y < 0) { this.model.rotation.y = 0; this.position.z = this.sensor.bottom; } } if(this.position.z > this.sensor.bottom) { this.model.rotation.y += timestep*(1-this.model.rotation.y); } //if(typeof(this.walls.children) != "undefined") if(this.haswall && this.walls.children.length > 0 && this.walls.map.length > 0) { var wallmod = this.walls.children[this.walls.children.length-1]; var wallmap = this.walls.map[this.walls.map.length-1]; wallmap[0]+=(newx); wallmap[1]+=(newy); wallmod.scale.x += newx/wallmod.size; wallmod.scale.y += newy/wallmod.size; this.walls.netLength += dist; this.sensor.front -= dist; //assume distance until we have new real results. //this.sensor.front = var lendiff = this.walls.netLength - settings.WALLS_LENGTH; while(settings.WALLS_LENGTH > 0 && lendiff > 0) { var map = this.walls.map; var xdir = (map[1][0]-map[0][0]), ydir = (map[1][1]-map[0][1]); var len = Math.sqrt((xdir*xdir)+(ydir*ydir)); if(len > 1) { xdir /= len; ydir /= len; } if(isNaN(xdir)) xdir = SMALL_NUM; if(isNaN(ydir)) ydir = SMALL_NUM; //console.log(lendiff,xdir,ydir); if(xdir == 0 && ydir == 0 && this.walls.map.length > 2) { //this.walls.children.shift(); this.walls.remove(this.walls.children[0]); this.walls.map.shift(); } else if(this.walls.children[0] && this.walls.map[0]) { this.walls.children[0].scale.x -= xdir/wallmod.size; this.walls.children[0].position.x += xdir; this.walls.children[0].scale.y -= ydir/wallmod.size; this.walls.children[0].position.y += ydir; this.walls.map[0][0] += xdir; this.walls.map[0][1] += ydir; this.walls.netLength -= Math.sqrt((xdir*xdir)+(ydir*ydir)); } else { break; } lendiff = this.walls.netLength - settings.WALLS_LENGTH; }//*/ if(this.position.z > 0) this.newWallSegment(); } else if(engine.gtime > this.spawntime+(settings.CYCLE_WALL_TIME*1000)) { this.walls = createWall(this,this.position.x,this.position.y,this.position.z); engine.scene.add(this.walls); this.haswall = true; } this.collidetime = timeElapsed+(((this.sensor.front-this.minDistance.front)/this.speed)*1000); this.lastSpeed = this.speed; } if(this.speed > engine.fastestSpeed) { engine.fastestPlayer = this; engine.fastestSpeed = this.speed; } } constructor(cfg) { super(); //var dir = cdir(cfg.dir); //this.cfg = cfg;//carry over the cfg??? //choose model this.model = cycleModel(cfg.cycleColor); this.add(this.model); //this.shadow = cycleShadow(); //this.add(this.shadow); this.chatarrow = newChatArrow(); this.chatarrow.position.z = 1.20; this.chatarrow.position.x -= 0.5; this.chatarrow.scale.set(0.25,0.25,0.25); this.add(this.chatarrow); this.cycleColor = cfg.cycleColor; this.tailColor = cfg.tailColor; /*this.walls = {}; //this.walls.children = []; this.walls.netLength = 0; this.walls.map = []; this.walls.scale = this.walls.position = new THREE.Vector3();*/ this.walls = createWall(this,0,0,0); //walls are still called upon when spectating this.chatting = cfg.chatting||false; this.spectating = cfg.spectating||false; if(cfg.ai||settings.DEBUG_EVERYONE_IS_AI) { this.AI = new AI(this); } else { this.AI = false; } // this.playerID = cfg.playerID; this.name = cfg.name; this.team = cfg.team; this.hardReset(); //audio creation this.engineType = cfg.engineType; if(ctx) { this.audio = ctx.createGain(); this.audio.gain.value = 0.01; this.audio.panner = ctx.createPanner(); if(engine.retroSound) this.audio.panner.panningModel = "HRTF"; else this.audio.panner.panningModel = "equalpower"; this.audio.connect(this.audio.panner); this.audio.panner.connect(ctx.destination); //audio initialization this.engineSound = playSound(bufferLoader.bufferList[this.engineType], 0.5, 0, true, this.audio); this.audio.gain.setTargetAtTime(6, ctx.currentTime, 1.0); } } }; if(typeof(module) != "undefined") module.exports = Player; diff --git a/scripts/render.js b/scripts/render.js index a0d6a05..ba32523 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -1,562 +1,562 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ var draw2dmap = true; function render() { if(!engine.roundCommencing && !engine.paused) { engine.renderRunning = true; - if(settings.GAME_LOOP > 0) game(true); //update game right before render + if(settings.GAME_LOOP > 0) game.run(true); //update game right before render if(settings.REDRAW_MODE == 0) requestAnimationFrame(render); else setTimeout(render,1000/settings.TARGET_FPS); draw(); //actual 3d draw if(settings.HUD_MAP && draw2dmap && engine.hud.style.opacity > 0) { draw2dmap = false; if(settings.REDRAW_MODE == 0) setTimeout(draw2d_canvas,0); else requestAnimationFrame(draw2d_canvas); } } else { engine.renderRunning = false; if(engine.paused) { //var lines = engine.console.innerText.split("\n"); //if(lines[(parseFloat(0+engine.console.style.top)/engine.console.scrollby].length > 0) } } if(settings.TEXT_OUT_MODE == 1) { var lines = engine.console.scrollback var lnnum = engine.console.scrollby; var currln = lines[lnnum]; } else { var lines = engine.console.innerText.split("\n"); var lnnum = (-(parseFloat(engine.console.style.top)/engine.console.scrollby)); var currln = lines[lnnum-1]; } if(Math.round(engine.console.time/engine.console.scrolltime) < Math.round(performance.now()/engine.console.scrolltime) || (performance.now() > engine.console.time_manual+engine.console.scrolltime_manual && lines.length-lnnum > 10)) { if(!currln && lnnum != lines.length) { if(lnnum < 0) var scrby = 1; if(lnnum > lines.length) var scrby = -1; engine.console.scroll(scrby); console.log("scroll",scrby); } else { engine.console.time = performance.now(); if(typeof(currln) != "undefined" && currln.length > 0) engine.console.scroll(); console.log("scroll"); } } } function draw() { //time handlers and delta var timenow = performance.now(); var delta = (timenow - engine.lastRenderTime); var frametime = delta/1000;//time step engine.lastRenderTime = timenow; var fpsDelta = (timenow - engine.fpsTime); settings.TARGET_FPS = (1000/delta)*2; if(settings.TARGET_FPS < 10) settings.TARGET_FPS = 10; if(settings.MAX_TARGET_FPS < settings.TARGET_FPS) settings.TARGET_FPS = settings.MAX_TARGET_FPS; //if(fpsDelta > 999) //update fps gui once every second if(fpsDelta >= 500) // experimental quicker FPS updating... { //var fpsValue = fpsDelta/delta; /*var fpsValue = 1000/delta;*/ var fpsValue = engine.framesCount*(1000/fpsDelta); //settings.TARGET_FPS = fpsValue*2; engine.framesCount = 0; engine.fpsTime = timenow; /*document.getElementById("fps").innerHTML = "FPS: " + fpsValue;*/ updateHUD("current_framerate",fpsValue); } //updateHUD("current_framerate_now",1000/delta); updateHUD("current_frametime",delta); var timeElapsed = (timenow - engine.timeStart)-engine.totalPauseTime-4000; if(engine.zones) for(var x=engine.zones.children.length-1;x>=0;--x) { //zones spin switch(settings.ZONE_RENDER_TYPE) { case "cylinder": engine.zones.children[x].rotation.y += ((engine.zones.children[x].cfg.rotationSpeed * pi(2)) * frametime); break; default: engine.zones.children[x].rotation.z += ((engine.zones.children[x].cfg.rotationSpeed * pi(2)) * frametime); break; } } for(var x=engine.players.length-1;x>=0;x--) if(engine.players[x] !== undefined) { //cycle un-tilting var cycle=engine.players[x]; var xdir=Math.cos(cycle.rotation.z),ydir=Math.sin(cycle.rotation.z); var xd = Math.abs(xdir), yd = Math.abs(ydir); var sens = 0; if(cycle.sensor.left < 1) sens -= 0.02/cycle.sensor.left; if(cycle.sensor.right < 1) sens += 0.02/cycle.sensor.right; if(sens > 1) sens = 1; if(sens < -1) sens = -1; cycle.rotation.x -= xd*(((cycle.rotation.x)*frametime*4)+(sens*frametime*xdir)); cycle.rotation.y -= yd*(((cycle.rotation.y)*frametime*4)+(sens*frametime*ydir)); if(cycle.chatting != cycle.chatarrow.visible) cycle.chatarrow.visible = cycle.chatting; if(cycle.chatting) { cycle.chatarrow.rotation.z += frametime; } //sound if(window.mixCycle) mixCycle(cycle); } if(settings.WALLS_STAY_UP_DELAY >= 0) { for(var x=engine.players.length;x>=0;x--) if(engine.players[x] !== undefined) { if(!engine.players[x].alive && timenow-engine.players[x].dedtime >= settings.WALLS_STAY_UP_DELAY*1000) { if(engine.players[x].walls.scale.z > 0) { engine.players[x].walls.scale.z -= frametime*2; } else { engine.scene.remove(engine.players[x].walls); } } } } for(var x=engine.expl.length-1;x>=0;x--) { if(engine.expl[x].children[0].material.opacity <= 0) { engine.scene.remove(engine.expl[x]); engine.expl.splice(x,1); } else for(var y=engine.expl[x].children.length-1;y>=0;y--) { engine.expl[x].children[y].scale.x = engine.expl[x].children[y].scale.y = engine.expl[x].children[y].scale.z += frametime*10; //engine.expl[x].children[y].position.z += frametime; if(engine.expl[x].children[y].scale.z > 10) { engine.expl[x].children[y].material.opacity -= frametime/25; } } } if(settings.FLOOR_MIRROR && typeof(engine.grid.reflection) != "undefined") { engine.grid.visible = false; engine.grid.reflection.update(engine.renderer,engine.scene); engine.grid.visible = true; } if(settings.RIM_WALL_COLOR_MODE == 3) { var color = engine.walls.children[0].material.color; var p = settings.COLOR_MODE_3_COLORS.split(";"); var c = p[engine.currrim3clr].split(","); //var c = {r:parse[0],g:parse[1],b:parse[2]}; var sum = color.r+color.g+color.b; var keys = Object.keys(color); for(var x=keys.length-1;x>=0;--x) { if(c[x] < color[keys[x]]) { color[keys[x]] -= frametime*settings.COLOR_MODE_3_SPEED; } else if(c[x] > color[keys[x]]) { color[keys[x]] += frametime*settings.COLOR_MODE_3_SPEED; } if(color[keys[x]] > 1) color[keys[x]] = 1; if(color[keys[x]] < 0) color[keys[x]] = 0; } //console.log(color,c); if(color.r == c[0] && color.g == c[1] && color.b == c[2]) { engine.currrim3clr += 1; if(engine.currrim3clr >= p.length) engine.currrim3clr = 0; } for(var x=engine.walls.children.length-1;x>=0;--x) { engine.walls.children[x].material.color = color; } } if(timenow > engine.cMFadeOutAfter) { var cm = document.getElementById("centerMessage") cm.style.opacity -= frametime; if(cm.style.opacity <= 0) { cm.style.opacity = 0; cm.style.display = "none"; engine.cMFadeOutAfter = Infinity; } } if(engine.hud.fadein && engine.hud.game.style.opacity < 1) { engine.hud.game.style.opacity = (engine.hud.game.style.opacity*1)+(frametime*0.25); //workaround for opacity being a string if(engine.hud.game.style.opacity > 1) engine.hud.game.style.opacity = 1; } else if(!engine.hud.fadein && engine.hud.game.style.opacity > 0) { engine.hud.game.style.opacity -= frametime*0.25; if(engine.hud.game.style.opacity < 0) engine.hud.game.style.opacity = 0; } //update HUD (needs to be done for cycle being viewed) var cycle = engine.players[engine.viewTarget]; updateHUD("player_rubber",cycle.rubber,0,settings.CYCLE_RUBBER); var maxspeed = maxSpeed(); updateHUD("player_speed",cycle.speed,0,maxspeed); updateHUD("player_brake",cycle.brakes,0,1); updateHUD("max_speed",maxspeed); updateHUD("player_acceleration",cycle.accel); updateHUD("dist_to_impact_front",cycle.sensor.front); updateHUD("time_to_impact_front",cycle.sensor.front/cycle.speed); updateHUD("dist_to_impact_left",cycle.sensor.left); updateHUD("time_to_impact_left",cycle.sensor.left/cycle.speed); updateHUD("dist_to_impact_right",cycle.sensor.right); updateHUD("time_to_impact_right",cycle.sensor.right/cycle.speed); updateHUD("current_name",cycle.name); updateHUD("current_pos_x",cycle.position.x,engine.logicalBox.min.x*engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.max.x*engine.REAL_ARENA_SIZE_FACTOR); updateHUD("current_pos_y",cycle.position.y,engine.logicalBox.min.y*engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.max.y*engine.REAL_ARENA_SIZE_FACTOR); updateHUD("current_pos_x_adj",cycle.position.x/engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.min.x,engine.logicalBox.max.x); updateHUD("current_pos_y_adj",cycle.position.y/engine.REAL_ARENA_SIZE_FACTOR,engine.logicalBox.min.y,engine.logicalBox.max.y); var dir = cdir(cycle.rotation.z); updateHUD("current_angle_x",dir[0]); updateHUD("current_angle_y",dir[1]); updateHUD("current_time",Math.round(timeElapsed)/1000); //settings test var setnames = Object.keys(settings); for(var i=setnames.length;i--;) { var setting = setnames[i]; updateHUD(setting.toLowerCase(),settings[setting]); } //actual drawing /*if(engine.players[engine.viewTarget].alive)*/ cameraView(engine.players[engine.viewTarget],frametime*engine.timemult); if(ctx) audioMixing(); //renderer switch for post processing if (engine.usingPostProcessing) { engine.composer.render();//new render? for post processing } else { engine.renderer.render(engine.scene, engine.camera); } engine.framesCount++; } function updateHUD(celement,thevalue,min=false,max=false) { var elements = document.getElementsByName(celement); for(var i=elements.length;i--;) { var value = thevalue; var element = elements[i]; if(min && element.attributes.min) element.setAttribute("min",min); if(max && element.attributes.max) element.setAttribute("max",max); if(element.attributes.precision) { var prec = Math.pow(10,element.attributes.precision.value); value = Math.round(value*prec)/prec; if(isNaN(value)) value = 0; } if(element.attributes.toprecision) { var prec = 1*element.attributes.toprecision.value; if(!element.attributes.dontlimit) { value = parseFloat(value).toPrecision(prec-1+((""+Math.round(thevalue)).length)); } else { value = value.toPrecision(prec); } } //if(element.attributes.precision) element.attributes.precision.value==0?value=Math.round(value):value.toPrecision(element.attributes.precision.value); //if(value) { if(element.tagName == "PROGRESS") { element.setAttribute("value",value); //console.log(value); } else if(element.className == "progress") { element.style.width = (Math.min(1,value/max)*100)+"%"; } else if(!element.attributes.ignoretext) { element.innerHTML = ""+value; } if(element.attributes.bgcolorgrad) { var grad = element.attributes.bgcolorgrad.value; var p = grad.split(";"); var c1 = p[0].split(","); var c2 = p[1].split(","); var color = {r:c1[0]*1,g:c1[1]*1,b:c1[2]*1},key=['r','g','b']; var progval = (value/max)*15; for(var x=0;x<3;x++) { if(c1[x] < c2[x]) { color[key[x]] += progval; } else if(c1[x] > c2[x]) { color[key[x]] -= progval; } } element.style.backgroundColor = "rgb("+(color.r*255)+","+(color.g*255)+","+(color.b*255)+")"; } } } } function draw2d_canvas() //TODO: have an svg output option { var canvas = document.getElementById("canvas"); if(!canvas) return; var ctx = canvas.getContext("2d"); ctx.clearRect(0,0,canvas.width,canvas.height); var xsize = engine.logicalBox.max.x-engine.logicalBox.min.x, ysize = engine.logicalBox.max.y-engine.logicalBox.min.y; canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetWidth*(ysize/xsize); ctx.scale( canvas.width/(xsize*engine.REAL_ARENA_SIZE_FACTOR), canvas.height/(ysize*engine.REAL_ARENA_SIZE_FACTOR) ); var ax = engine.logicalBox.min.x * engine.REAL_ARENA_SIZE_FACTOR, ay = engine.logicalBox.min.y * engine.REAL_ARENA_SIZE_FACTOR; ctx.lineWidth = ((xsize*engine.REAL_ARENA_SIZE_FACTOR)/canvas.width); //ctx.lineWidth = canvas.width-canvas.height-2; //ctx.lineWidth = (canvas.offsetWidth-canvas.width)*engine.REAL_ARENA_SIZE_FACTOR; ctx.strokeStyle = "white"; for(var i=engine.map.walls.length-1;i>=0;i--) { ctx.beginPath(); var spl = engine.map.walls[i][engine.map.walls[i].length-1]; ctx.moveTo(1*spl[0],1*spl[1]); for(var z=engine.map.walls[i].length-1;z>=0;z--) { var spl = engine.map.walls[i][z]; ctx.lineTo(1*spl[0]-ax,1*spl[1]-ay); //console.log(spl[0],spl[1]); } ctx.stroke(); } for(var x=engine.players.length-1;x>=0;x--) if(engine.players[x] !== undefined) { var color = engine.players[x].walls.children[0].children[0].material.color; ctx.strokeStyle = "rgb("+(color.r*255)+","+(color.g*255)+","+(color.b*255)+")"; ctx.beginPath(); if(engine.players[x].walls.map.length > 0) { var walls = engine.players[x].walls.map; ctx.moveTo(walls[walls.length-1][0]-ax,walls[walls.length-1][1]-ay); for(var i=walls.length-1;i>=0;i--) { ctx.lineTo(walls[i][0]-ax,walls[i][1]-ay); } } ctx.stroke(); if(engine.players[x].alive) { var color = new THREE.Color(engine.players[x].cycleColor); ctx.fillStyle = "rgb("+(color.r*255)+","+(color.g*255)+","+(color.b*255)+")"; ctx.beginPath(); ctx.arc(engine.players[x].position.x-ax,engine.players[x].position.y-ay, ctx.lineWidth, 0,Math.PI*2); ctx.fill(); } } var lw = ctx.lineWidth; ctx.lineWidth = lw*settings.ZONE_ALPHA*settings.ZONE_ALPHA_SERVER; for(var x=engine.zones.children.length-1;x>=0;--x) { var zone = engine.zones.children[x]; var color = zone.material.color; ctx.strokeStyle = "rgb("+(color.r*255)+","+(color.g*255)+","+(color.b*255)+")"; var geo = zone.geometry.clone(); geo.applyMatrix(zone.matrix); //apply rotation, scale, and position ctx.beginPath(); var pX = zone.position.x-ax, pY = zone.position.y-ay; for(var i=geo.faces.length-2;i>=0;i-=2) { ctx.moveTo(geo.vertices[geo.faces[i].b].x-ax,geo.vertices[geo.faces[i].b].y-ay); ctx.lineTo(geo.vertices[geo.faces[i].a].x-ax,geo.vertices[geo.faces[i].a].y-ay); } ctx.stroke(); } lw *= 0.7; for(var x=engine.expl.length-1;x>=0;x--) { ctx.lineWidth = lw*engine.expl[x].children[0].material.opacity; if(ctx.lineWidth > 0) { var scale = (engine.expl[x].children[0].scale.x+engine.expl[x].children[0].scale.y)/2; var cx = engine.expl[x].children[0].position.x, cy = engine.expl[x].children[0].position.y; for(var y=engine.expl[x].children.length;y--;) { var color = engine.expl[x].children[y].material.color; ctx.strokeStyle = "rgb("+(color.r*255)+","+(color.g*255)+","+(color.b*255)+")"; ctx.beginPath(); ctx.moveTo(cx-ax,cy-ay); ctx.lineTo( (cx+engine.expl[x].children[y].geometry.vertices[1].x*scale)-ax, (cy+engine.expl[x].children[y].geometry.vertices[1].y*scale)-ay ); ctx.stroke(); } } } draw2dmap = true; } //camera view function (handles all views for view target) var cameraView = function(cycle, timestep) { var relativeCameraOffset, cameraOffset; var cameraEase = engine.cameraEase; if(engine.camera.userViewDir !== false) // HACK: camera rotation { var realRot = cycle.rotation.z; cycle.rotation.z = Math.atan2(engine.camera.userViewDir[1],engine.camera.userViewDir[0]); cycle.updateWorldMatrix(); cycle.rotation.z = realRot; } switch(engine.view) { case 'smart': if(engine.camera.userViewDir === false) { //relativeCameraOffset = new THREE.Vector3(-5,0,5+cycle.speed); relativeCameraOffset = new THREE.Vector3(-5,0,(5+cycle.speed*0.006)); cameraOffset = relativeCameraOffset.applyMatrix4(cycle.matrixWorld); engine.camera.position.x += (cameraOffset.x - engine.camera.position.x) * (engine.cameraEase/3) * timestep*60; engine.camera.position.y += (cameraOffset.y - engine.camera.position.y) * (engine.cameraEase/3) * timestep*60; engine.camera.position.z += (cameraOffset.z - engine.camera.position.z) * (engine.cameraEase/5) * timestep*60; engine.camera.lookAt(cycle.position); break; } else { relativeCameraOffset = new THREE.Vector3((-10-(cycle.speed*0.006)),0,(5+cycle.speed*0.006)); cameraEase = 0.3; // [[FALLTHROUGH]] } case 'chase': if(!relativeCameraOffset) relativeCameraOffset = new THREE.Vector3((-10-(cycle.speed*0.006)),0,(15+cycle.speed*0.006)); cameraOffset = relativeCameraOffset.applyMatrix4(cycle.matrixWorld); engine.camera.position.x += (cameraOffset.x - engine.camera.position.x) * cameraEase * timestep*60; engine.camera.position.y += (cameraOffset.y - engine.camera.position.y) * cameraEase * timestep*60; engine.camera.position.z += (cameraOffset.z - engine.camera.position.z) * cameraEase * timestep*60; engine.camera.lookAt(cycle.position); break; case 'custom': engine.camera.position.set(0,0,0); engine.camera.rotation.z = settings.CAMERA_CUSTOM_PITCH; break; case 'stationary': break; case 'track': engine.camera.lookAt(cycle.position); break; case 'topdown': engine.camera.position.set(cycle.position.x, (cycle.position.y-0.01), (10+cycle.speed*timestep)); engine.camera.lookAt(cycle.position); break; case 'birdseye': relativeCameraOffset = new THREE.Vector3(-0.1, 0, 10+(cycle.speed*timestep)); cameraOffset = relativeCameraOffset.applyMatrix4(cycle.matrixWorld); engine.camera.position.x += (cameraOffset.x - engine.camera.position.x) * engine.cameraEase * timestep*60; engine.camera.position.y += (cameraOffset.y - engine.camera.position.y) * engine.cameraEase * timestep*60; engine.camera.position.z += (cameraOffset.z - engine.camera.position.z) * engine.cameraEase * timestep*60; // engine.camera.position.set(cycle.position.x, cycle.position.y, (10+cycle.speed)); // engine.camera.rotation.z = cycle.rotation.z; engine.camera.lookAt(cycle.position); break; case 'cockpit': /**/// cockpit //if(cycle.speed > 2) { relativeCameraOffset = new THREE.Vector3(-2+(2.5*cycle.speed),0,0.5); } /*else { relativeCameraOffset = new THREE.Vector3(0.01,0,0.5); }*/ cameraOffset = relativeCameraOffset.applyMatrix4(cycle.matrixWorld); engine.camera.position.set(cycle.position.x,cycle.position.y,cycle.position.z+0.5); //engine.camera.rotation.set(cycle.rotation.x,cycle.rotation.y,0); // engine.camera.position.x += (cameraOffset.x - engine.camera.position.x) * 0.5; // engine.camera.position.y += (cameraOffset.y - engine.camera.position.y) * 0.5; // engine.camera.position.z = 2; engine.camera.lookAt(cameraOffset); //engine.camera.rotation.z = cycle.rotation.x+cycle.rotation.y; // cycle.audio.gain.setTargetAtTime(0.2, ctx.currentTime, 0.02); //cycle.textLabel.style.visibility = 'hidden'; //cycle.model.visible = false; /*/ if (cycle.walls.children[cycle.walls.children.length-2]) { cycle.walls.children[cycle.walls.children.length-1].visible = false; cycle.walls.children[cycle.walls.children.length-2].visible = true; } /**/ break; } }; diff --git a/scripts/zone.js b/scripts/zone.js index 05307e2..4c0d8cd 100644 --- a/scripts/zone.js +++ b/scripts/zone.js @@ -1,384 +1,389 @@ /* * 3DCycles - A lightcycle game. * Copyright (C) 2019 Glen Harpring * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -//if(typeof(THREE) == "undefined") var THREE = require('./lib/Three.js'); +if(this.require) +{ + if(typeof(THREE) == "undefined") + var THREE = require('./lib/Three.js'); +} var flagColors = [ 0x55AAFF, 0xFFFF55, 0xFF5555, 0x55FF55, 0xFF55FF, 0x55FFFF, 0xFFFFFF, 0x888888]; class Zone { spawn() { engine.zones.add(this.mesh); return this; } destroy() { engine.zones.remove(this.mesh); return this; } constructor(prop) { this.timesEntered = 0; this.netObject = false; this.type = prop.type||"null"; this.rotationSpeed = isNaN(prop.rot)?settings.ZONE_SPIN_SPEED:prop.rot; this.value = prop.value||0; this.expansion = prop.expansion||0; this.radius = prop.radius||0; this.xdir = prop.xdir||0; this.ydir = prop.ydir||0; this.bounce = !!prop.bounce; this.team = false; var zoneHeight = prop.height||settings.ZONE_HEIGHT; if (settings.ZONE_SEGMENTS < 1) { settings.ZONE_SEGMENTS = 11; } var zoneSegments = (Math.floor(this.radius/10)*10)+1; if (zoneSegments < 11) { zoneSegments = 11; } var zoneColor = 0xFFFFFF; if(prop.color || prop.color === 0) { zoneColor = prop.color; } else { switch(this.type) { case "death": zoneColor = 0xFF0000; break; case "soccerball": zoneColor = 0xFF8888; break; case "soccergoal": zoneColor = 0xBB6666; break; case "rubber": zoneColor = 0xFFB033; break; case "rubberadjust": zoneColor = 0xFF8800; break; case "oneway": zoneColor = 0xFFFF00; break; case "win": zoneColor = 0x00FF00; break; case "target": zoneColor = 0x00DD00; break; case "teleport": zoneColor = 0x00AA00; break; case "speed": zoneColor = 0x00FF88; break; case "acceleration": zoneColor = 0x00BB66; break; case "blast": zoneColor = 0x0088FF; break; case "fortress": case "flag": var lastDist = Infinity; var closestSpawn = 0; if(engine.teams.length > 0) { for(var w=0;w 7) { zoneColor = 0x4488FF; } - else { zoneColor = (this.type=="fortress"?teamColor:teamColor)(closestSpawn); } + else { zoneColor = (this.type=="fortress"?game.teamColor:game.teamColor)(closestSpawn); } this.team = closestSpawn; break; case "object": zoneColor = 0xBB0066; break; case "checkpoint": zoneColor = 0xFF0088; break; case "koh": zoneColor = 0xDDDDDD; break; case "wall": case "ball": zoneColor = 0xFFFFFF; break; case "switch": zoneColor = 0x999999; break; } } var color = typeof(zoneColor)=="object"?zoneColor:new THREE.Color(zoneColor), geo = new THREE.Geometry(); switch(prop.shape) { case "polygon": var min = [Infinity,Infinity], max = [-Infinity,-Infinity]; for(var i=0;i=0;z--) { if(point[z] < min[z]) min[z] = point[z]; if(point[z] > max[z]) max[z] = point[z]; console.log(point[z]); } } for(var i=0,halfvert=(geo.vertices.length/2)-1;imin[1]?min[0]:min[1]); this.shape = "polygon"; break; default: //circle var zoneSegCoords = [];//get the coordinates for each segment vertex var zoneSegMidpoints = [];//get midpoints for each segment var trueSegments = []; for (var i = 0; i < settings.ZONE_SEGMENTS; i++) { var zpx = Math.cos(2 * Math.PI * i / settings.ZONE_SEGMENTS); var zpy = Math.sin(2 * Math.PI * i / settings.ZONE_SEGMENTS); zoneSegCoords.push({x:zpx, y:zpy}); if (i > 0 && i != settings.ZONE_SEGMENTS-1) {//all segments starting from the second zoneSegMidpoints[i] = { x:((zoneSegCoords[i-1].x+zoneSegCoords[i].x)/2), y:((zoneSegCoords[i-1].y+zoneSegCoords[i].y)/2) }; } else if (i == settings.ZONE_SEGMENTS-1) {//last segment + first zoneSegMidpoints[i] = { x:((zoneSegCoords[i-1].x+zoneSegCoords[i].x)/2), y:((zoneSegCoords[i-1].y+zoneSegCoords[i].y)/2) }; zoneSegMidpoints[0] = { x:((zoneSegCoords[0].x+zoneSegCoords[i].x)/2), y:((zoneSegCoords[0].y+zoneSegCoords[i].y)/2) }; } } //with midpoints, determine segments for (var s = 0; s < settings.ZONE_SEGMENTS; s++) { //var segmentFullLength = pointDistance( zoneSegCoords[s].x, zoneSegCoords[s].y, zoneSegCoords[s+1].x, zoneSegCoords[s+1].y ); //var adjustedSegLength = segmentFullLength * settings.ZONE_SEG_LENGTH; if (s == 0) { var np1x = (zoneSegMidpoints[0].x + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[zoneSegCoords.length-1].x - zoneSegMidpoints[0].x) ) ); var np1y = (zoneSegMidpoints[0].y + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[zoneSegCoords.length-1].y - zoneSegMidpoints[0].y) ) ); var np2x = (zoneSegMidpoints[0].x + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s].x - zoneSegMidpoints[0].x) ) ); var np2y = (zoneSegMidpoints[0].y + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s].y - zoneSegMidpoints[0].y) ) ); } else { var np1x = (zoneSegMidpoints[s].x + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s-1].x - zoneSegMidpoints[s].x) ) ); var np1y = (zoneSegMidpoints[s].y + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s-1].y - zoneSegMidpoints[s].y) ) ); var np2x = (zoneSegMidpoints[s].x + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s].x - zoneSegMidpoints[s].x) ) ); var np2y = (zoneSegMidpoints[s].y + (settings.ZONE_SEG_LENGTH * ( zoneSegCoords[s].y - zoneSegMidpoints[s].y) ) ); } trueSegments[s] = { x1:np1x, y1:np1y, x2:np2x, y2:np2y }; } //have segment coordinates, now build it for (var n = 0; n < trueSegments.length; n++) { geo.vertices.push( new THREE.Vector3( (trueSegments[n].x1), (trueSegments[n].y1), 0) ); geo.vertices.push( new THREE.Vector3( (trueSegments[n].x2), (trueSegments[n].y2), 0) ); } for (var n = 0; n < trueSegments.length; n++) { geo.vertices.push( new THREE.Vector3( (trueSegments[n].x1), (trueSegments[n].y1), settings.ZONE_HEIGHT) ); geo.vertices.push( new THREE.Vector3( (trueSegments[n].x2), (trueSegments[n].y2), settings.ZONE_HEIGHT) ); } for (var i = 0; i < (geo.vertices.length/2)-1; i+=2) { geo.faces.push( new THREE.Face3( (i), (i+1), (i+(geo.vertices.length/2)) ), //a,b,c new THREE.Face3( (i+(geo.vertices.length/2)+1), (i+(geo.vertices.length/2)), (i+1) ) //d,c,b ); } this.shape = "circle"; break; } //var alpha = Math.max(color.r,color.g,color.b); //color.r /= alpha; color.g /= alpha; color.b /= alpha; if(!this.mat) this.mat = new THREE.MeshBasicMaterial( { color: color, transparent: settings.ALPHA_BLEND, opacity: settings.ZONE_ALPHA/**alpha*/, side: THREE.DoubleSide } ); if(!this.mesh) { this.mesh = new THREE.Mesh(geo,this.mat); this.mesh.cfg = this; } this.mesh.position.set(prop.x||0,prop.y||0,prop.z||0); this.mesh.scale.set(this.radius||1,this.radius||1,1); } netSync() { if(window.svr) { var zone = { id: engine.zones.children.indexOf(this.mesh), x: this.mesh.position.x, y:this.mesh.position.y, z:this.mesh.position.z, type:this.type, rotationSpeed:this.rot, value: this.value, expansion:this.expansion, radius: this.radius, xdir: this.xdir, ydir: this.ydir, bounce: this.bounce, team: this.team, color: this.mesh.material.color.getHex(), shape: this.shape, }; if(this.type == "flagHeld") zone.heldBy = engine.players.indexOf(this.heldBy); if(this.shape == "polygon") { zone.points = []; for(var i=geo.faces.length-2;i>=0;i-=2) { var geo = this.mesh.geometry.clone(); geo.applyMatrix(this.mesh.matrix); zone.points.push(geo.vertices[geo.faces[i].b].x,geo.vertices[geo.faces[i].b].y); } } var data = JSON.stringify({type:"zone",data:[zone],gtime:engine.gtime}); window.svr.clients.forEach(function(ws){ws.send(data)}); } return this; } distance(position) { switch(this.shape) { case "circle": return pointDistance(this.mesh.position.x,this.mesh.position.y,position.x,position.y); break; case "polygon": var min = Infinity, x, tmp, coords = this.mesh.geometry.vertices; for(var i=(coords.length/2)-1;i>=0;x=(--i)-1) { if(coords[x] !== undefined) { tmp = distanceoflines( coords[i].x,coords[i].y, coords[x].x,coords[x].y, position.x,position.y, position.x,position.y ); if(min > tmp) { min = tmp; } } } return min; break; } return Infinity; } onEnter(cycle,time) { switch(this.type) { case "wall": cycle.position.x -= cycle.dir.front[0]*(engine.gtime-time); cycle.position.y -= cycle.dir.front[1]*(engine.gtime-time); break; case "death": cycle.kill(); engine.console.print(cycle.getColoredName()+"0xRESETT exploded on a deathzone.\n"); break; case "win": if(engine.winner == undefined && engine.declareRoundWinner == undefined) { //engine.console.print(cycle.getColoredName()+"0xRESETT "); this.expansion = -1; engine.declareRoundWinner = cycle.name; } break; case "target": loadcfg(settings.DEFAULT_TARGET_COMMAND.replace(/\\n/g,"\n")); cycle.addScore(settings.TARGET_INITIAL_SCORE); break; } } onInside(cycle,time,timestep) { switch(this.type) { case "rubber": cycle.rubber += timestep*this.value; if(cycle.rubber >= settings.CYCLE_RUBBER) { cycle.kill(); engine.console.print(cycle.getColoredName()+"0xRESETT exploded on a rubberzone.\n"); } break; case "fortress": if(engine.gtime > 0) { if(settings.BASE_RESPAWN) { engine.teams[this.team].spawn(true,true,false); } if(this.team == engine.teams.indexOf(cycle.team)) { + this.lastContact = engine.gtime; } else { this.rotationSpeed += timestep*settings.FORTRESS_CONQUEST_RATE; if(this.rotationSpeed > settings.ZONE_SPIN_SPEED*16) { engine.console.print(cycle.getColoredName()+"0xRESETT conquered a fortress zone.\n"); this.type = "null"; this.expansion = -10; engine.declareRoundWinner = cycle.name; } } } break; case "ball": case "soccerball": var mindirx=0,mindiry=0,mindist=Infinity,apc=0; for(var i=359;i>0;i--) { var xdir = Math.cos(Math.PI*2*(i/360)), ydir=Math.sin(Math.PI*2*(i/360)); var xpos = xdir*this.radius+this.mesh.position.x, ypos=ydir*this.radius+this.mesh.position.y; var dist = pointDistance(xpos,ypos,cycle.position.x,cycle.position.y); if(dist < mindist) { /*if(mindist == Infinity)mindist = dist; else mindist += dist; mindirx -= xdir; mindiry -= ydir; apc++;*/ mindist=dist;mindirx=xdir;mindiry=ydir;apc=1; } } //mindist /= apc; mindirx /= apc; mindiry /= apc; if(mindist != Infinity) { this.xdir = -mindirx*cycle.speed; this.ydir = -mindiry*cycle.speed; if(!this.bounce) this.bounce = true; this.lastHitCycle = cycle; this.netSync(); } break; case "speed": cycle.speed = this.value||0; break; case "acceleration": accel = (this.value||0); cycle.accel += accel; cycle.speed += cycle.speed*accel; break; case "flag": if(!engine.network && !cycle.hasFlag && engine.teams.indexOf(cycle.team) != this.team) { engine.console.print(cycle.getColoredName()+"0xRESETT picked up "+engine.teams[this.team].name+"0xRESETT's flag.\n"); cycle.hasFlag = this; this.type = "flagHeld"; this.heldBy = cycle; if(!this.px) this.px = this.mesh.position.x; if(!this.py) this.py = this.mesh.position.y; this.mesh.position.set(cycle.position.x,cycle.position.y,0); this.netSync(); } break; } } onLeave(cycle,time) { } onOutside(cycle,time) { - + } } if(typeof(module) != "undefined") module.exports = Zone;