diff --git a/scripts/game.js b/scripts/game.js index f277dd3..c0aa731 100644 --- a/scripts/game.js +++ b/scripts/game.js @@ -1,957 +1,961 @@ /* * 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() { 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 } function endRound() { 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("Deleting objects...\n",false); if(!engine.network) { 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() { endRound(); engine.players.splice(0); engine.round = 0; } function playGame() { engine.playGame = true; if(!engine.scene) { init(); } else { engine.paused = false; if(engine.hud) engine.hud.show(); } hideMenu(); 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() { var mapfile = settings.RESOURCE_REPOSITORY_CACHE+(settings.MAP_FILE.replace(/\(.+\)/,"")); engine.console.print("Downloading map from "+mapfile+"...\n",false); httpGetAsync(mapfile,loadRound,function() { engine.console.print("Unable to load map file. Ignoring for now...",false); loadRound(); }); } function newRound() { 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)); } else { loadRound(); } } function teamColor(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 ensurePlayersSane(removeAIs=true) { var minplayers = Math.max(settings.TEAMS_MIN,settings.MIN_PLAYERS,settings.NUM_AIS+settings.players.length); if(removeAIs) for(var x=minplayers;x=0;--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(); 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(); }//end of init main function game(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;} //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); 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:engine.gtime})); + 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}); window.svr.clients.forEach(function(ws){ws.send(data)}); } } 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.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(); } 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; //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]; if(zone.type == "ball" || zone.type == "soccerball") { for(var z=engine.zones.children.length-1;z>=0;--z) { var z2n = engine.map.zones[z]; if( ( (zone.type == "ball" && z2n[0] == "fortress") || (zone.type == "soccerball" && z2n[0] == "soccergoal") ) && is_in_circle(z2n[1],z2n[2],z2n[3],zone.x,zone.y,zone.radius)) { if(!engine.network && engine.winner == undefined) { centerMessage("0x00ff00Goal!"); startNewRound(); } else { zone.xdir *= 1-timestep; zone.ydir *= 1-timestep; } } } } //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.walldist <= timestep) { var mindirx,mindiry,mindist=Infinity,apc=0; var px = zone.x+(zone.walldist*zone.xdir), py = zone.y+(zone.walldist*zone.ydir); for(var i=359;i>0;i--) { var xdir = Math.sin(Math.PI*2*(i/360)), ydir=Math.cos(Math.PI*2*(i/360)); var xpos = xdir*zone.radius+zone.x, ypos=ydir*zone.radius+zone.y; var dist = pointDistance(xpos,ypos,px,py); if(dist < mindist) { //mindist += dist; mindirx += xdir; mindiry += ydir; //apc++; mindist=dist;mindirx=xdir;mindiry=ydir;apc=1; } } //mindist /= apc; mindirx /= apc; mindiry /= apc; var speed = Math.sqrt((zone.xdir*zone.xdir)+(zone.ydir*zone.ydir)); if(mindist != Infinity) { var angle = Math.atan2(mindiry,mindirx); //var angle = Math.atan2(mindiry,mindirx)*2; //var angle = Math.atan2(mindiry,mindirx)+(Math.PI/2); //var angle = Math.PI-Math.atan2(mindiry,mindirx); var dir = cdir(angle); zone.xdir = dir[0]*speed; zone.ydir = dir[1]*speed; zone.x -= dir[0]*realzone.walldist; zone.y -= dir[1]*realzone.walldist; zone.x += dir[0]*speed*timestep; zone.y += dir[1]*speed*timestep; console.log(zone); } else { zone.xdir *= -1; zone.ydir *= -1; } } 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) { 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) { console.warn("Deprecated call to simulatePlayer"); cycle.update(timestep); } function getGoing() { if(!engine.gameRunning) game(); if(!engine.renderRunning) render(); } function pauseRender() { engine.paused = true;//cuts off the loop engine.startOfPause = performance.now(); audioStop(); } function unpauseRender() { 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 } } function changeViewTarget(a=1,forcechange=false) { 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].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() { var alivecount = aliveaicount = 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].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 && settings.TEAMS_MIN > 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); startNewRound(); }//*/ /*if(aliveaicount == alivecount) { engine.console.print("Vroom!"); engine.timemult = 2; engine.asendtm = 1.1; }//*/ } function startNewRound() { 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); } function updateScoreBoard() { if(window.svr) { var tmp = []; for(var x=engine.players.length-1;x>=0;--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+""; } 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+" "; } }*/ diff --git a/scripts/network.js b/scripts/network.js index 6587e64..9b99d4f 100644 --- a/scripts/network.js +++ b/scripts/network.js @@ -1,310 +1,332 @@ /* * 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 connectTo(host,port) { hideMenu(); engine.inputState = "game"; try { var connection = new WebSocket('ws'+(settings.CONNECT_SSL?'s':'')+'://'+host+':'+port); connection.onmessage = connectionHandler; connection.onerror = function(e) { console.log(e); var msg; if(engine.network) { engine.console.print(msg="An error occurred with the connection."); } else { engine.console.print(msg="Couldn't connect to server. Please ensure that you have the right host and port."); } disconnectFromGame();menu("leave");showMenu(); menu("menu:"); document.getElementById('menu').innerHTML = "

Connection Error

"+msg+"
"; } connection.onclose = function(e) { console.log(e); var msg; if(engine.network) { engine.console.print(msg="Our connection with the server has been terminated."); msg += " "; if(e.reason == "") engine.console.print(msg+="No reason given.\n"); else engine.console.print(msg+="Reason: "+e.reason+"\n"); disconnectFromGame();menu("leave");showMenu(); menu("menu:"); document.getElementById('menu').innerHTML = "

Connection Terminated

"+msg+"
"; } } return connection; } catch(e) { engine.console.print(e+"\n"); disconnectFromGame();menu("leave");showMenu(); menu("menu:"); document.getElementById('menu').innerHTML = "

An error occurred

"+e+"
"; } } function doNetSlide(cycle,timestep=1) { timestep *= settings.CYCLE_SMOOTH_TIME; if(timestep > 1) timestep = 1; cycle.lastpos.x = (cycle.position.x += (cycle.newPos.x-cycle.position.x)*timestep); cycle.lastpos.y = (cycle.position.y += (cycle.newPos.y-cycle.position.y)*timestep); cycle.resetCurrWallSegment(); if(cycle.position.x == cycle.newPos.x && cycle.position.y == cycle.newPos.y) delete cycle.newPos; } function connectionHandler(e) { //console.log(e); var msg = JSON.parse(e.data); switch(msg.type) { - case "ping": engine.connection.send('{"type":"pong"}'); break; + case "ping": engine.connection.send((JSON.stringify({type:"pong"}))); break; + case "timeSync": + if(msg.data) + { + engine.gtime = msg.data; + engine.connection.send(JSON.stringify({type:"timeSync",data:engine.gtime})); + } + engine.connection.timeSync = false; + break; case "version": engine.connection.send(JSON.stringify({type:"version",data:0.7})); break; case "endRound": if(inround()) endRound(); break; case "newRound": if(!engine.playGame) playGame(); else if(!inround()) newRound(); break; case "setting": console.log(e); netcfg(msg.setting,""+msg.data); break; case "con": engine.console.print(msg.data); break; case "cen": centerMessage(msg.data.msg,msg.data.time); break; case "syncdata": console.log(msg.gtime); engine.connection.send(JSON.stringify({type:"player",data:settings.player})); if(typeof(msg.netid) != "undefined") { engine.activePlayer = msg.netid||0; //we got id engine.network = true; if(!engine.scene) init(); //endRound(); } //engine.totalPauseTime += (msg.gtime||0)-engine.gtime; if(msg.gtime !== undefined) engine.gtime = msg.gtime; break; case "leave": if(engine.players[msg.data]) { if(msg.data == engine.activePlayer) { console.warn("Player being deleted is ours."); } if(msg.data == engine.viewTarget) { changeViewTarget(1); } if(engine.players[msg.data].alive) engine.players[msg.data].kill(); engine.players.splice(msg.data,1); updateScoreBoard(); } else { console.warn("Left player doesn't seem to exist."); } break; case "playerdata": console.log(msg.data); for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; if(!engine.players[data.netid]) { var cycleinfo; data.ai = false; // AIs should be exclusively handled by the server. Maybe a different solution in the future? engine.players[data.netid] = new Player(cycleinfo={ x:data.x||0, y:data.y||0, z:data.z||0, dir:data.dir||0, ai:data.ai||false, name:data.name||"1", cycleColor:data.cycleColor, tailColor:data.tailColor, engineType:data.engineType||settings.player.engineType, }); console.log(data); engine.players[data.netid].spawn(cycleinfo,false); audioMixing(engine.players[data.netid]); } else { engine.players[data.netid].name = data.name; engine.players[data.netid].cycleColor = data.cycleColor; engine.players[data.netid].tailColor = data.tailColor; } } break; case "griddata": if(!engine.playGame) playGame(); //console.log(msg.data); - engine.gtime = (performance.now()/settings.TIME_FACTOR)-engine.timeStart-engine.totalPauseTime-4000; + //engine.gtime = (performance.now()/settings.TIME_FACTOR)-engine.timeStart-engine.totalPauseTime-4000; if(msg.gtime > engine.gtime) { - engine.gtime = msg.gtime; + //engine.gtime = msg.gtime; //console.log("S: ",msg.gtime-engine.gtime); + if(!engine.connection.timeSync) + { + engine.connection.timeSync = true; + engine.connection.send(JSON.stringify({type:"timeSync",data:engine.gtime})); + } } var delta = engine.gtime-(msg.gtime); var timestep = delta/1000; //var delta = timestep*1000; for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; var cycle = engine.players[data.netid]; if(cycle.alive != data.alive) { if(cycle.alive == true) cycle.killAt(data.position[0],data.position[1],data.position[2]); else cycle.spawn({x:data.position[0],y:data.position[1],z:data.position[2]},(!!data.spawntime)); delete cycle.newPos; } cycle.speed = data.speed; cycle.rubber = data.rubber; if(cycle.alive) { var olddir = cycle.rotation.z, newdir = deg2rad(data.direction||0); if(!data.wall && normalizeRad(olddir) == normalizeRad(newdir) && isFinite(data.position[0]) && isFinite(data.position[1])) //slide to position { if(!cycle.newPos) cycle.newPos = new THREE.Vector2(cycle.position.x,cycle.position.y); cycle.newPos.x = data.position[0]; cycle.newPos.y = data.position[1]; if(!cycle.handleNetTurn) { doNetSlide(cycle,Infinity); cycle.handleNetTurn = true; } } else if(cycle.handleNetTurn) //jump straight to the position, we're doing a turn { //also, don't handle updates until our last turn has been sent by the server cycle.lastpos.x = cycle.position.x = data.position[0]||0; cycle.lastpos.y = cycle.position.y = data.position[1]||0; delete cycle.newPos; cycle.gameTime = Math.max(0,msg.gtime); + + //HACK to avoid cycle sticking to wall on a turn + { + var fakeTS = 0.01/cycle.speed; + var move2d = Math.cos(cycle.model.rotation.y)*(cycle.speed*fakeTS), dir = cdir(cycle.rotation.z); + cycle.lastpos.x = (cycle.position.x += move2d*dir[0]); + cycle.lastpos.y = (cycle.position.y += move2d*dir[1]); + cycle.gameTime += fakeTS; + } } if(data.wall) { cycle.walls.map = data.wall; cycle.resetWall(false); } cycle.lastpos.z = cycle.position.z = data.position[2]||0; if(cycle.haswall) { if(rad2deg(olddir) == rad2deg(newdir)) { cycle.walls.map[cycle.walls.map.length-1] = [cycle.position.x,cycle.position.y]; } else { cycle.rotation.z = newdir; var wallmod = cycle.walls.children[cycle.walls.children.length-1]; cycle.newWallSegment(); } } else { cycle.rotation.z = newdir; } } //if(isFinite(timestep)) cycle.update(timestep); } if(!engine.lastPingTime) engine.players[engine.activePlayer].ping = parseInt(delta); updateScoreBoard(); //console.log(timestep); break; case "scoredata": for(var i=msg.data.length;i--;) if(typeof(msg.data[i]) != "undefined") { var data = msg.data[i]; var cycle = engine.players[data.netid]; cycle.chatting = data.chatting||false; cycle.ping = data.ping||0; cycle.score = data.score||0; engine.lastPingTime = performance.now(); } updateScoreBoard(); break; case "zone": for(var i=msg.data.length-1;i>=0;--i) if(msg.data[i]) { var zone = msg.data[i]; if(zone.destroyed) { if(engine.zones[zone.id]) { engine.zones[zone.id].destroy(); } } else if(engine.zones[zone.id]) { //maybe a bit hacky but it's the best way I can think of to ensure everything is correct, short of destroying and recreating the zone entirely (which would likely be too slow) engine.zones[zone.id].constructor(zone); } else { try { var s = new Zone(zone).spawn(); //we don't need to process the zone ourselves (as that will all be handled by the server), however we should still know the type for our chatbot. s.netObject = true; } catch(e) { engine.console.print("0xff7f7fAn error occurred when syncing zones. You may be missing some important elements of the game.\n"); console.error(e); } } } break; } } function connectToGame() { if(!engine.connection) { engine.connection = connectTo(settings.CONNECT_HOST,settings.CONNECT_PORT) document.getElementById("progtitle").innerHTML = tStringify("@progtitleshort@ • Connecting to "+settings.CONNECT_HOST+":"+settings.CONNECT_PORT); } } function disconnectFromGame() { if(engine.connection && engine.connection.close) engine.connection.close(); engine.connection = engine.network = false; engine.viewTarget = engine.activePlayer = 0; for(var i=netChanged.length-1;i>=0;--i) { chsetting(netChanged[i][0],netChanged[i][1],true); } netChanged = []; } diff --git a/scripts/player.js b/scripts/player.js index 68645ea..13fdb96 100644 --- a/scripts/player.js +++ b/scripts/player.js @@ -1,631 +1,631 @@ /* * 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'); class Player extends THREE.Object3D { setScore(x) { this.score = (x*1)||0; engine.playersByScore.sort(function(a,b){return b.score-a.score}); if(engine.playersByScore.indexOf(this) > -1) 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. { 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(cycle,full=true) //! Completely redoes the 3D wall according to the actual wall + 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(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); 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(); 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 } } 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 === 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); } } 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) { 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 { 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)); } 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 = null; 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;