Page MenuHomePhabricator

No OneTemporary

diff --git a/scripts/game.js b/scripts/game.js
index ca0f862..2e43d15 100644
--- a/scripts/game.js
+++ b/scripts/game.js
@@ -1,1192 +1,1193 @@
/*
* 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()
{
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()
{
engine.console.print("Unable to load map file. Ignoring for now...\n",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 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].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)
{
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<engine.teams.length;++x) if(engine.teams[x])
{
for(var i=engine.teams[x].members.length-1;i>=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)
{
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)
{
if(typeof(dlmap) != "undefined")
{
engine.mapString = dlmap;
engine.loadedMap = settings.MAP_FILE;
}
engine.mapXML = xmlify(engine.mapString);
engine.console.print("Creating objects...\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();
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();
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);
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();
}
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();
}
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)
{
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(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()
{
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;
}//*/
}
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) 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<engine.playersByScore.length;++x) if(typeof(engine.playersByScore[x]) != "undefined")
{
var cycle = engine.playersByScore[x];
tmp += "<tr class=\"player\"><td>"+(cycle.chatting?"*":"&nbsp;")+replaceColors(cycle.getColoredName())+"</td><td>"+replaceColors(cycle.alive?"0x00ff00Yes":"0xff0000No")+"</td><td>"+cycle.score+"</td><td>"+cycle.ping+"</td><td>"+(cycle.team?cycle.team.name:"")+"</td><tr>";
}
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 0783e98..cff255a 100644
--- a/scripts/network.js
+++ b/scripts/network.js
@@ -1,353 +1,355 @@
/*
* 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)
{
engine.playGame = false; 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;
disconnectFromGame();menu("leave");showMenu();
menu("menu:connectfail");
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.");
}
}
connection.onclose = function(e)
{
console.log(e); var msg;
if(engine.network)
{
disconnectFromGame();menu("leave");showMenu();
menu("menu:connectterm");
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");
}
}
return connection;
}
catch(e)
{
engine.console.print(e+"\n");
disconnectFromGame();menu("leave");showMenu();
menu("menu:");
document.getElementById('menu').innerHTML = "<h1>An error occurred</h1><div style='text-align:left;font-size:11pt'>"+e+"</div>";
}
}
function doNetSlide(cycle,timestep=1)
{
if(isNaN(cycle.position.x) || isNaN(cycle.position.y))
{
cycle.position.x = cycle.newPos.x;
cycle.position.y = cycle.newPos.y;
delete cycle.newPos;
}
else
{
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((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,
team: engine.teams[data.team]||new Team({name:(data.alive?data.name:"")}),
});
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;
if(msg.gtime > engine.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);
if(settings.DEBUG_NETWORK_TURN_WAIT) cycle.lastTurnTime = 0;
//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 "team":
engine.teams.splice(0);
for(var i=msg.data.length-1;i>=0;--i) if(msg.data[i])
{
engine.teams[msg.data[i].id] = new Team(msg.data[i]);
}
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.children[zone.id])
{
var myZone = engine.zones.children[zone.id];
+ if(zone.type!==undefined) {myZone.cfg.type = zone.type;}
if(zone.x!==undefined) {myZone.position.x = zone.x; myZone.position.y = zone.y; myZone.position.z = zone.z;}
if(zone.xdir!==undefined) {myZone.cfg.xdir = zone.xdir; myZone.cfg.ydir = zone.ydir;}
if(zone.bounce!== undefined)myZone.cfg.bounce = zone.bounce;
+ if(zone.type == "flagHeld") myZone.cfg.heldBy = engine.players[zone.heldBy];
myZone.cfg.netObject = true;
}
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@ &bull; Connecting to "+settings.CONNECT_HOST+":"+settings.CONNECT_PORT);
menu("menu:connect");
}
}
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/zone.js b/scripts/zone.js
index e9dd84f..6faf8f5 100644
--- a/scripts/zone.js
+++ b/scripts/zone.js
@@ -1,371 +1,374 @@
/*
* 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');
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<engine.teams.length;w++)
{
var disValue = pointDistance(engine.teams[w].x,engine.teams[w].y, prop.x, prop.y);
if (disValue < lastDist) { lastDist = disValue; closestSpawn = w; }
}
}
else
{
for(var w=0;w<engine.map.spawns.length;w++)
{
var checkx = engine.map.spawns[w][0];
var checky = engine.map.spawns[w][1];
var disValue = pointDistance( checkx, checky, prop.x, prop.y );
if (disValue < lastDist) { lastDist = disValue; closestSpawn = w; }
}
}
if (closestSpawn > 7) { zoneColor = 0x4488FF; }
else { zoneColor = (this.type=="fortress"?teamColor: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<prop.points.length;i++)
{
var point = prop.points[i];
//point[0] += (1*prop.x)||0; point[1] += (1*prop.y)||0;
geo.vertices[i] = new THREE.Vector3(point[0],point[1],0);
geo.vertices[prop.points.length+i] = new THREE.Vector3(point[0],point[1],zoneHeight);
for(var z=1;z>=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;i<halfvert;i++)
{
var p1x = geo.vertices[i].x, p1y = geo.vertices[i].y;
var p2x = geo.vertices[i+1].x, p2y = geo.vertices[i+1].y;
var dist = Math.sqrt( (p2x-=p1x)*p2x + (p2y-=p1y)*p2y );
var normal = new THREE.Vector3( (p2y - p1y), -(p2x - p1x), 0 );
geo.faces.push(
new THREE.Face3( (i), (i+1), (i+(geo.vertices.length/2)), normal ), //a,b,c
new THREE.Face3( (i+(geo.vertices.length/2)+1), (i+(geo.vertices.length/2)), (i+1), normal ) //d,c,b
);
}
//prop.x = (max[0]-min[0])/2; prop.y = (max[1]-min[1])/2;
//this.radius = (max[0]<max[1]?max[0]:max[1])-(min[0]>min[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)
{
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(!cycle.hasFlag && engine.teams.indexOf(cycle.team) != this.team)
+ 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;

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 23, 9:17 PM (3 d, 18 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10793

Event Timeline