render arena
This commit is contained in:
parent
6bad86730e
commit
33f94af784
113
ui/arena/index.html
Normal file
113
ui/arena/index.html
Normal file
@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Arena</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
|
||||
<link href="/ui/bs/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/ui/bs/css/bootstrap-theme.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/ui/css/hackerbots.css" rel="stylesheet" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="error" class="alert alert-danger">
|
||||
<div class="container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="float:right; text-align:right;">
|
||||
<strong><a href="/ui">HackerBots</a>  </strong>
|
||||
<br>{{ server.hostname }}  {{game}}  
|
||||
</div>
|
||||
<ul class="nav nav-tabs" id='tabs'>
|
||||
<li><a href="#code" data-toggle="tab" class='active'>Code</a></li>
|
||||
<li><a href="#arena" data-toggle="tab">Arena</a></li>
|
||||
<li><a href="#raw" data-toggle="tab">Raw Bot Data</a></li>
|
||||
<li><a href="#json" data-toggle="tab">Bot Data</a></li>
|
||||
<li><a href="#about" data-toggle="tab">About</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="code">
|
||||
<div id="output" class="error"></div>
|
||||
<div style="float:left; padding:20px">
|
||||
<div id="starters">
|
||||
<button id='starter' class="btn btn-primary" style="width:100px;">Play</button><br><br>
|
||||
<button id='watch' class="btn btn-success" style="width:100px;">Watch</button><br><br>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<select id="robot_picker" class="form-control" style='width:100px'>
|
||||
{% for bot in bots %}
|
||||
<option value="{{bot.id}}">{{bot.name}}</option>
|
||||
{% endfor %}
|
||||
</select><br>
|
||||
<button class='btn bt-primary' id='save' style="width:100px;">Save Bot</button><br>
|
||||
<span class='under-button' id='lastsave'></span><br><br>
|
||||
<button class='btn bt-primary' id='new' style="width:100px;">New Bot</button><br><br>
|
||||
<div id='rename-block' style='display:none;'>
|
||||
<input type='text' id='newname' style="width:100px; "><br><br>
|
||||
</div>
|
||||
<button class='btn bt-primary' id='rename' style="width:100px;">Rename Bot</button><br><br>
|
||||
<br><br>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="editor" class="center"></pre>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="arena">
|
||||
<p>
|
||||
<div>
|
||||
<pre id="players"></pre>
|
||||
</div>
|
||||
<div id="bf" class="center">
|
||||
<canvas id="battlefield" width="1000px" height="800px"></canvas>
|
||||
Use 'v' to cycle views<br>
|
||||
Use 't' to cycle robot focus<br>
|
||||
Use 'd' to toggle debug display<br>
|
||||
Use '-' / '=' to zoom in/out<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="raw">
|
||||
<p>
|
||||
<pre id="raw-debug" class="center debug"></pre>
|
||||
<p>
|
||||
<pre class='center debug'>Raw Data is exactly what the server send unprocessed. 'p' to pause</pre>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="json">
|
||||
<p>
|
||||
<pre id="debug" class="center debug"></pre>
|
||||
<p>
|
||||
<pre class='center debug'>Bot Data is the JSON from the server after it's been processed by the gorobots javascript code to fill our the scanner fields with data from the bot list.
|
||||
|
||||
'p' to pause</pre>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="about">
|
||||
<h4> Hackerbots is Awesome </h4>
|
||||
<p>
|
||||
I would love to tell you more, but we're too busy writing the code.
|
||||
|
||||
You should follow @smcquay or @twisted_weasel.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/ui/jquery.js"></script>
|
||||
<script src="/ui/bs/js/bootstrap.min.js"></script>
|
||||
<script src="/ui/js/botui.js"></script>
|
||||
<script src="/ui/js/arena.js"></script>
|
||||
<script src="/ui/js/vec2d.js"></script>
|
||||
<script src="/ui/bs/js/bootstrap.js" %}"></script>
|
||||
<script src="/ui/js/stats.min.js"></script>
|
||||
<script src="/ui/js/three.min.js"></script>
|
||||
<script src="/ui/js/OBJLoader.js"></script>
|
||||
<script src="/ui/js/TrackballControls.js"></script>
|
||||
<script src="/ui/js/gorobots_render_webgl.js"></script>
|
||||
<script src="/ui/js/gorobots.js"></script>
|
||||
<script src="/ui/js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
431
ui/js/OBJLoader.js
Normal file
431
ui/js/OBJLoader.js
Normal file
@ -0,0 +1,431 @@
|
||||
/**
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
THREE.OBJLoader = function ( manager ) {
|
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
||||
|
||||
};
|
||||
|
||||
THREE.OBJLoader.prototype = {
|
||||
|
||||
constructor: THREE.OBJLoader,
|
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var loader = new THREE.XHRLoader( scope.manager );
|
||||
loader.setCrossOrigin( this.crossOrigin );
|
||||
loader.load( url, function ( text ) {
|
||||
|
||||
onLoad( scope.parse( text ) );
|
||||
|
||||
} );
|
||||
|
||||
},
|
||||
|
||||
parse: function ( text ) {
|
||||
|
||||
function vector( x, y, z ) {
|
||||
|
||||
return new THREE.Vector3( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
function uv( u, v ) {
|
||||
|
||||
return new THREE.Vector2( u, v );
|
||||
|
||||
}
|
||||
|
||||
function face3( a, b, c, normals ) {
|
||||
|
||||
return new THREE.Face3( a, b, c, normals );
|
||||
|
||||
}
|
||||
|
||||
var object = new THREE.Object3D();
|
||||
var geometry, material, mesh;
|
||||
|
||||
// create mesh if no objects in text
|
||||
|
||||
if ( /^o /gm.test( text ) === false ) {
|
||||
|
||||
geometry = new THREE.Geometry();
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
mesh = new THREE.Mesh( geometry, material );
|
||||
object.add( mesh );
|
||||
|
||||
}
|
||||
|
||||
var vertices = [];
|
||||
var verticesCount = 0;
|
||||
var normals = [];
|
||||
var uvs = [];
|
||||
|
||||
// v float float float
|
||||
|
||||
var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
|
||||
|
||||
// vn float float float
|
||||
|
||||
var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
|
||||
|
||||
// vt float float
|
||||
|
||||
var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;
|
||||
|
||||
// f vertex vertex vertex ...
|
||||
|
||||
var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/;
|
||||
|
||||
// f vertex/uv vertex/uv vertex/uv ...
|
||||
|
||||
var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/;
|
||||
|
||||
// f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
|
||||
|
||||
var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/;
|
||||
|
||||
// f vertex//normal vertex//normal vertex//normal ...
|
||||
|
||||
var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/
|
||||
|
||||
//
|
||||
|
||||
var lines = text.split( '\n' );
|
||||
|
||||
for ( var i = 0; i < lines.length; i ++ ) {
|
||||
|
||||
var line = lines[ i ];
|
||||
line = line.trim();
|
||||
|
||||
var result;
|
||||
|
||||
if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
|
||||
|
||||
continue;
|
||||
|
||||
} else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
|
||||
|
||||
// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
|
||||
|
||||
vertices.push( vector(
|
||||
parseFloat( result[ 1 ] ),
|
||||
parseFloat( result[ 2 ] ),
|
||||
parseFloat( result[ 3 ] )
|
||||
) );
|
||||
|
||||
} else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
|
||||
|
||||
// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
|
||||
|
||||
normals.push( vector(
|
||||
parseFloat( result[ 1 ] ),
|
||||
parseFloat( result[ 2 ] ),
|
||||
parseFloat( result[ 3 ] )
|
||||
) );
|
||||
|
||||
} else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
|
||||
|
||||
// ["vt 0.1 0.2", "0.1", "0.2"]
|
||||
|
||||
uvs.push( uv(
|
||||
parseFloat( result[ 1 ] ),
|
||||
parseFloat( result[ 2 ] )
|
||||
) );
|
||||
|
||||
} else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
|
||||
|
||||
// ["f 1 2 3", "1", "2", "3", undefined]
|
||||
|
||||
if ( result[ 4 ] === undefined ) {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 1 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 3 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
verticesCount ++
|
||||
) );
|
||||
|
||||
} else {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 1 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 3 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 4 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount,
|
||||
verticesCount + 1,
|
||||
verticesCount + 3
|
||||
) );
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount + 1,
|
||||
verticesCount + 2,
|
||||
verticesCount + 3
|
||||
) );
|
||||
|
||||
verticesCount += 4;
|
||||
|
||||
}
|
||||
|
||||
} else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
|
||||
|
||||
// ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
|
||||
|
||||
if ( result[ 10 ] === undefined ) {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 5 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 8 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
verticesCount ++
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 3 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 6 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 9 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
} else {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 5 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 8 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 11 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount,
|
||||
verticesCount + 1,
|
||||
verticesCount + 3
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 3 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 6 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 12 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount + 1,
|
||||
verticesCount + 2,
|
||||
verticesCount + 3
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 6 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 9 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 12 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
verticesCount += 4;
|
||||
|
||||
}
|
||||
|
||||
} else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
|
||||
|
||||
// ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
|
||||
|
||||
if ( result[ 13 ] === undefined ) {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 6 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 10 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
[
|
||||
normals[ parseInt( result[ 4 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 8 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 12 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 3 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 7 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 11 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
} else {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 6 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 10 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 14 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount,
|
||||
verticesCount + 1,
|
||||
verticesCount + 3,
|
||||
[
|
||||
normals[ parseInt( result[ 4 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 8 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 16 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 3 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 7 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 15 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount + 1,
|
||||
verticesCount + 2,
|
||||
verticesCount + 3,
|
||||
[
|
||||
normals[ parseInt( result[ 8 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 12 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 16 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
geometry.faceVertexUvs[ 0 ].push( [
|
||||
uvs[ parseInt( result[ 7 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 11 ] ) - 1 ],
|
||||
uvs[ parseInt( result[ 15 ] ) - 1 ]
|
||||
] );
|
||||
|
||||
verticesCount += 4;
|
||||
|
||||
}
|
||||
|
||||
} else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
|
||||
|
||||
// ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
|
||||
|
||||
if ( result[ 10 ] === undefined ) {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 5 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 8 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
verticesCount ++,
|
||||
[
|
||||
normals[ parseInt( result[ 3 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 6 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 9 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
} else {
|
||||
|
||||
geometry.vertices.push(
|
||||
vertices[ parseInt( result[ 2 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 5 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 8 ] ) - 1 ],
|
||||
vertices[ parseInt( result[ 11 ] ) - 1 ]
|
||||
);
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount,
|
||||
verticesCount + 1,
|
||||
verticesCount + 3,
|
||||
[
|
||||
normals[ parseInt( result[ 3 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 6 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 12 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
geometry.faces.push( face3(
|
||||
verticesCount + 1,
|
||||
verticesCount + 2,
|
||||
verticesCount + 3,
|
||||
[
|
||||
normals[ parseInt( result[ 6 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 9 ] ) - 1 ],
|
||||
normals[ parseInt( result[ 12 ] ) - 1 ]
|
||||
]
|
||||
) );
|
||||
|
||||
verticesCount += 4;
|
||||
|
||||
}
|
||||
|
||||
} else if ( /^o /.test( line ) ) {
|
||||
|
||||
// object
|
||||
|
||||
geometry = new THREE.Geometry();
|
||||
material = new THREE.MeshLambertMaterial();
|
||||
|
||||
mesh = new THREE.Mesh( geometry, material );
|
||||
mesh.name = line.substring( 2 ).trim();
|
||||
object.add( mesh );
|
||||
|
||||
verticesCount = 0;
|
||||
|
||||
} else if ( /^g /.test( line ) ) {
|
||||
|
||||
// group
|
||||
|
||||
} else if ( /^usemtl /.test( line ) ) {
|
||||
|
||||
// material
|
||||
|
||||
material.name = line.substring( 7 ).trim();
|
||||
|
||||
} else if ( /^mtllib /.test( line ) ) {
|
||||
|
||||
// mtl file
|
||||
|
||||
} else if ( /^s /.test( line ) ) {
|
||||
|
||||
// smooth shading
|
||||
|
||||
} else {
|
||||
|
||||
// console.log( "THREE.OBJLoader: Unhandled line " + line );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( var i = 0, l = object.children.length; i < l; i ++ ) {
|
||||
|
||||
var geometry = object.children[ i ].geometry;
|
||||
|
||||
geometry.computeCentroids();
|
||||
geometry.computeFaceNormals();
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
}
|
||||
|
||||
return object;
|
||||
|
||||
}
|
||||
|
||||
};
|
607
ui/js/TrackballControls.js
Executable file
607
ui/js/TrackballControls.js
Executable file
@ -0,0 +1,607 @@
|
||||
/**
|
||||
* @author Eberhard Graether / http://egraether.com/
|
||||
* @author Mark Lundin / http://mark-lundin.com
|
||||
*/
|
||||
|
||||
THREE.TrackballControls = function ( object, domElement ) {
|
||||
|
||||
var _this = this;
|
||||
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
|
||||
|
||||
this.object = object;
|
||||
this.domElement = ( domElement !== undefined ) ? domElement : document;
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.screen = { left: 0, top: 0, width: 0, height: 0 };
|
||||
|
||||
this.rotateSpeed = 1.0;
|
||||
this.zoomSpeed = 1.2;
|
||||
this.panSpeed = 0.3;
|
||||
|
||||
this.noRotate = false;
|
||||
this.noZoom = false;
|
||||
this.noPan = false;
|
||||
this.noRoll = true;
|
||||
|
||||
this.staticMoving = true;
|
||||
this.dynamicDampingFactor = 0.2;
|
||||
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
|
||||
|
||||
// internals
|
||||
|
||||
this.target = new THREE.Vector3();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
|
||||
var _state = STATE.NONE,
|
||||
_prevState = STATE.NONE,
|
||||
|
||||
_eye = new THREE.Vector3(),
|
||||
|
||||
_rotateStart = new THREE.Vector3(),
|
||||
_rotateEnd = new THREE.Vector3(),
|
||||
|
||||
_zoomStart = new THREE.Vector2(),
|
||||
_zoomEnd = new THREE.Vector2(),
|
||||
|
||||
_touchZoomDistanceStart = 0,
|
||||
_touchZoomDistanceEnd = 0,
|
||||
|
||||
_panStart = new THREE.Vector2(),
|
||||
_panEnd = new THREE.Vector2();
|
||||
|
||||
// for reset
|
||||
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
this.up0 = this.object.up.clone();
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
var startEvent = { type: 'start'};
|
||||
var endEvent = { type: 'end'};
|
||||
|
||||
|
||||
// methods
|
||||
|
||||
this.handleResize = function () {
|
||||
|
||||
if ( this.domElement === document ) {
|
||||
|
||||
this.screen.left = 0;
|
||||
this.screen.top = 0;
|
||||
this.screen.width = window.innerWidth;
|
||||
this.screen.height = window.innerHeight;
|
||||
|
||||
} else {
|
||||
|
||||
this.screen = this.domElement.getBoundingClientRect();
|
||||
// adjustments come from similar code in the jquery offset() function
|
||||
var d = this.domElement.ownerDocument.documentElement
|
||||
this.screen.left += window.pageXOffset - d.clientLeft
|
||||
this.screen.top += window.pageYOffset - d.clientTop
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.handleEvent = function ( event ) {
|
||||
|
||||
if ( typeof this[ event.type ] == 'function' ) {
|
||||
|
||||
this[ event.type ]( event );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.getMouseOnScreen = function ( pageX, pageY, vector ) {
|
||||
|
||||
return vector.set(
|
||||
( pageX - _this.screen.left ) / _this.screen.width,
|
||||
( pageY - _this.screen.top ) / _this.screen.height
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
this.getMouseProjectionOnBall = (function(){
|
||||
|
||||
var objectUp = new THREE.Vector3(),
|
||||
mouseOnBall = new THREE.Vector3();
|
||||
|
||||
|
||||
return function ( pageX, pageY, projection ) {
|
||||
|
||||
mouseOnBall.set(
|
||||
( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
|
||||
( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5),
|
||||
0.0
|
||||
);
|
||||
|
||||
var length = mouseOnBall.length();
|
||||
|
||||
if ( _this.noRoll ) {
|
||||
|
||||
if ( length < Math.SQRT1_2 ) {
|
||||
|
||||
mouseOnBall.z = Math.sqrt( 1.0 - length*length );
|
||||
|
||||
} else {
|
||||
|
||||
mouseOnBall.z = .5 / length;
|
||||
|
||||
}
|
||||
|
||||
} else if ( length > 1.0 ) {
|
||||
|
||||
mouseOnBall.normalize();
|
||||
|
||||
} else {
|
||||
|
||||
mouseOnBall.z = Math.sqrt( 1.0 - length * length );
|
||||
|
||||
}
|
||||
|
||||
_eye.copy( _this.object.position ).sub( _this.target );
|
||||
|
||||
projection.copy( _this.object.up ).setLength( mouseOnBall.y )
|
||||
projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
|
||||
projection.add( _eye.setLength( mouseOnBall.z ) );
|
||||
|
||||
return projection;
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
this.rotateCamera = (function(){
|
||||
|
||||
var axis = new THREE.Vector3(),
|
||||
quaternion = new THREE.Quaternion();
|
||||
|
||||
|
||||
return function () {
|
||||
|
||||
var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
|
||||
|
||||
if ( angle ) {
|
||||
|
||||
axis.crossVectors( _rotateStart, _rotateEnd ).normalize();
|
||||
|
||||
angle *= _this.rotateSpeed;
|
||||
|
||||
quaternion.setFromAxisAngle( axis, -angle );
|
||||
|
||||
_eye.applyQuaternion( quaternion );
|
||||
_this.object.up.applyQuaternion( quaternion );
|
||||
|
||||
_rotateEnd.applyQuaternion( quaternion );
|
||||
|
||||
if ( _this.staticMoving ) {
|
||||
|
||||
_rotateStart.copy( _rotateEnd );
|
||||
|
||||
} else {
|
||||
|
||||
quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
|
||||
_rotateStart.applyQuaternion( quaternion );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
this.zoomCamera = function () {
|
||||
|
||||
if ( _state === STATE.TOUCH_ZOOM ) {
|
||||
|
||||
var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
|
||||
_touchZoomDistanceStart = _touchZoomDistanceEnd;
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
} else {
|
||||
|
||||
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
|
||||
|
||||
if ( factor !== 1.0 && factor > 0.0 ) {
|
||||
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
if ( _this.staticMoving ) {
|
||||
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.panCamera = (function(){
|
||||
|
||||
var mouseChange = new THREE.Vector2(),
|
||||
objectUp = new THREE.Vector3(),
|
||||
pan = new THREE.Vector3();
|
||||
|
||||
return function () {
|
||||
|
||||
mouseChange.copy( _panEnd ).sub( _panStart );
|
||||
|
||||
if ( mouseChange.lengthSq() ) {
|
||||
|
||||
mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
|
||||
|
||||
pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
|
||||
pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
|
||||
|
||||
_this.object.position.add( pan );
|
||||
_this.target.add( pan );
|
||||
|
||||
if ( _this.staticMoving ) {
|
||||
|
||||
_panStart.copy( _panEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
this.checkDistances = function () {
|
||||
|
||||
if ( !_this.noZoom || !_this.noPan ) {
|
||||
|
||||
if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
|
||||
|
||||
_this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
|
||||
|
||||
}
|
||||
|
||||
if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
|
||||
|
||||
_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
var old = new THREE.Vector3();
|
||||
old.copy(_this.object.position);
|
||||
|
||||
_eye.subVectors( _this.object.position, _this.target );
|
||||
|
||||
if ( !_this.noRotate ) {
|
||||
|
||||
_this.rotateCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( !_this.noZoom ) {
|
||||
|
||||
_this.zoomCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( !_this.noPan ) {
|
||||
|
||||
_this.panCamera();
|
||||
|
||||
}
|
||||
|
||||
_this.object.position.addVectors( _this.target, _eye );
|
||||
|
||||
// HACKERBOTS CHANGES
|
||||
// Up is always up
|
||||
_this.object.up.copy( _this.up0 );
|
||||
|
||||
// Stay above ground
|
||||
if (_this.object.position.z <= 20){
|
||||
_this.object.position.copy(old);
|
||||
}
|
||||
if (_this.object.position.z >= 500){
|
||||
_this.object.position.copy(old);
|
||||
}
|
||||
// END HACKERBOTS
|
||||
|
||||
_this.checkDistances();
|
||||
|
||||
_this.object.lookAt( _this.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
|
||||
|
||||
_this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( _this.object.position );
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.reset = function () {
|
||||
|
||||
_state = STATE.NONE;
|
||||
_prevState = STATE.NONE;
|
||||
|
||||
_this.target.copy( _this.target0 );
|
||||
_this.object.position.copy( _this.position0 );
|
||||
_this.object.up.copy( _this.up0 );
|
||||
|
||||
_eye.subVectors( _this.object.position, _this.target );
|
||||
|
||||
_this.object.lookAt( _this.target );
|
||||
|
||||
_this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( _this.object.position );
|
||||
|
||||
};
|
||||
|
||||
// listeners
|
||||
|
||||
function keydown( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
window.removeEventListener( 'keydown', keydown );
|
||||
|
||||
_prevState = _state;
|
||||
|
||||
if ( _state !== STATE.NONE ) {
|
||||
|
||||
return;
|
||||
|
||||
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
|
||||
|
||||
_state = STATE.ROTATE;
|
||||
|
||||
} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
|
||||
|
||||
_state = STATE.ZOOM;
|
||||
|
||||
} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
|
||||
|
||||
_state = STATE.PAN;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function keyup( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
_state = _prevState;
|
||||
|
||||
window.addEventListener( 'keydown', keydown, false );
|
||||
|
||||
}
|
||||
|
||||
function mousedown( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if ( _state === STATE.NONE ) {
|
||||
|
||||
_state = event.button;
|
||||
|
||||
}
|
||||
|
||||
if ( _state === STATE.ROTATE && !_this.noRotate ) {
|
||||
|
||||
_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
|
||||
_rotateEnd.copy(_rotateStart)
|
||||
|
||||
} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
|
||||
|
||||
_this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
|
||||
_zoomEnd.copy(_zoomStart);
|
||||
|
||||
} else if ( _state === STATE.PAN && !_this.noPan ) {
|
||||
|
||||
_this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
|
||||
_panEnd.copy(_panStart)
|
||||
|
||||
}
|
||||
|
||||
document.addEventListener( 'mousemove', mousemove, false );
|
||||
document.addEventListener( 'mouseup', mouseup, false );
|
||||
_this.dispatchEvent( startEvent );
|
||||
|
||||
|
||||
}
|
||||
|
||||
function mousemove( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if ( _state === STATE.ROTATE && !_this.noRotate ) {
|
||||
|
||||
_this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
|
||||
|
||||
} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
|
||||
|
||||
_this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
|
||||
|
||||
} else if ( _state === STATE.PAN && !_this.noPan ) {
|
||||
|
||||
_this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function mouseup( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
_state = STATE.NONE;
|
||||
|
||||
document.removeEventListener( 'mousemove', mousemove );
|
||||
document.removeEventListener( 'mouseup', mouseup );
|
||||
_this.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function mousewheel( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var delta = 0;
|
||||
|
||||
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
|
||||
|
||||
delta = event.wheelDelta / 40;
|
||||
|
||||
} else if ( event.detail ) { // Firefox
|
||||
|
||||
delta = - event.detail / 3;
|
||||
|
||||
}
|
||||
|
||||
_zoomStart.y += delta * 0.01;
|
||||
_this.dispatchEvent( startEvent );
|
||||
_this.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1:
|
||||
_state = STATE.TOUCH_ROTATE;
|
||||
_rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_state = STATE.TOUCH_ZOOM;
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_state = STATE.TOUCH_PAN;
|
||||
_panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
|
||||
break;
|
||||
|
||||
default:
|
||||
_state = STATE.NONE;
|
||||
|
||||
}
|
||||
_this.dispatchEvent( startEvent );
|
||||
|
||||
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1:
|
||||
_this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
|
||||
break;
|
||||
|
||||
default:
|
||||
_state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( event ) {
|
||||
|
||||
if ( _this.enabled === false ) return;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1:
|
||||
_rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
_state = STATE.NONE;
|
||||
_this.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
||||
|
||||
this.domElement.addEventListener( 'mousedown', mousedown, false );
|
||||
|
||||
this.domElement.addEventListener( 'mousewheel', mousewheel, false );
|
||||
this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart, false );
|
||||
this.domElement.addEventListener( 'touchend', touchend, false );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove, false );
|
||||
|
||||
window.addEventListener( 'keydown', keydown, false );
|
||||
window.addEventListener( 'keyup', keyup, false );
|
||||
|
||||
this.handleResize();
|
||||
|
||||
};
|
||||
|
||||
THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
|
148
ui/js/arena.js
Normal file
148
ui/js/arena.js
Normal file
@ -0,0 +1,148 @@
|
||||
var savebot = function(name, callback){
|
||||
data = {
|
||||
"id": $("#robot_picker").val(),
|
||||
"code": editor.getValue()
|
||||
};
|
||||
|
||||
if (name){
|
||||
data.name = name;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
method:"POST",
|
||||
data: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
url:"/api/v1/bot/" + $("#robot_picker").val() + "/",
|
||||
success: function(data){
|
||||
var d = new Date();
|
||||
$("#lastsave").html("Saved at " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds());
|
||||
|
||||
if (callback){
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var newbot = function(){
|
||||
data = {
|
||||
"code": editor.getValue(),
|
||||
};
|
||||
$.ajax({
|
||||
method:"POST",
|
||||
data: JSON.stringify(data),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
url:"/api/v1/bot/" + $("#robot_picker").val() + "/",
|
||||
success: function(data){
|
||||
console.log(data);
|
||||
if (data.id){
|
||||
$("#robot_picker").val(data.id);
|
||||
}
|
||||
// I'm lazy
|
||||
set_current_bot(data.id);
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var set_current_bot = function(name){
|
||||
var queryParameters = {}, queryString = location.search.substring(1),
|
||||
re = /([^&=]+)=([^&]*)/g, m;
|
||||
|
||||
while (m = re.exec(queryString)) {
|
||||
queryParameters[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
|
||||
}
|
||||
queryParameters['bot'] = name;
|
||||
history.pushState(null, null, "?"+ $.param(queryParameters));
|
||||
};
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#error").hide();
|
||||
editor = ace.edit("editor");
|
||||
editor.setTheme("ace/theme/monokai");
|
||||
editor.getSession().setMode("ace/mode/javascript");
|
||||
|
||||
function resizeAce() {
|
||||
return $('#editor').height($(window).height() - $("#editor").offset().top - 20);
|
||||
};
|
||||
|
||||
$(window).resize(resizeAce);
|
||||
resizeAce();
|
||||
|
||||
var gorobots = gorobots_init({}, true);
|
||||
|
||||
$("#robot_picker").change(function(e){
|
||||
$.ajax({
|
||||
url:"/api/v1/bot/" + $("#robot_picker").val(),
|
||||
success: function(data){
|
||||
editor.setValue(data.code);
|
||||
set_current_bot($("#robot_picker").val());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#save").click(function(e){
|
||||
savebot();
|
||||
});
|
||||
|
||||
$("#new").click(function(e){
|
||||
newbot();
|
||||
});
|
||||
|
||||
var renaming = false;
|
||||
$("#rename").click(function(e){
|
||||
if (renaming){
|
||||
// POST new name to server
|
||||
savebot($("#newname").val(), function(){
|
||||
// BE LAZY
|
||||
location.reload();
|
||||
});
|
||||
|
||||
$("#rename-block").hide();
|
||||
$("#rename").html("Rename Bot");
|
||||
renaming = false;
|
||||
} else {
|
||||
$("#rename-block").show();
|
||||
$("#rename").html("Ok");
|
||||
renaming = true;
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url:"/api/v1/bot/1/",
|
||||
success: function(data){
|
||||
editor.setValue(data.code);
|
||||
$("#robot_picker").val(1);
|
||||
}
|
||||
});
|
||||
|
||||
$('#starter').click(function() {
|
||||
savebot();
|
||||
|
||||
$('#tabs a[href="#arena"]').tab('show');
|
||||
var url = "ws://{{ server.hostname }}:8666/ws/";
|
||||
gorobots.set_spectator(false);
|
||||
gorobots.set_server(
|
||||
url,
|
||||
"{{ game }}"
|
||||
);
|
||||
});
|
||||
$('#watch').click(function() {
|
||||
savebot();
|
||||
|
||||
$('#tabs a[href="#arena"]').tab('show');
|
||||
var url = "ws://{{ server.hostname }}:8666/ws/";
|
||||
gorobots.set_spectator(true);
|
||||
gorobots.set_server(
|
||||
url,
|
||||
"{{ game }}"
|
||||
);
|
||||
});
|
||||
|
||||
$('#tabs a[href="#code"]').tab('show');
|
||||
|
||||
});
|
@ -11,7 +11,7 @@ function loadGames() {
|
||||
$("#games").empty();
|
||||
for (var key in data) {
|
||||
var name = data[key]["id"];
|
||||
var s = "<li><a href='/ui/game/" + name + "/'>join</a> " + name + "</li>";
|
||||
var s = "<li><a href='/ui/arena/?id=" + name + "'>join</a> " + name + "</li>";
|
||||
$("#games").append(s);
|
||||
}
|
||||
});
|
||||
|
561
ui/js/gorobots.js
Normal file
561
ui/js/gorobots.js
Normal file
@ -0,0 +1,561 @@
|
||||
var gorobots_init = function gorobots(my, webgl){
|
||||
my.width = null;
|
||||
my.height = null;
|
||||
my.server = "ws://localhost:8666/ws/";
|
||||
my.websocket = null;
|
||||
my.id = null;
|
||||
my.connection_retry = 3000;
|
||||
my.state = null;
|
||||
my.spectator = false;
|
||||
|
||||
my.debug = false;
|
||||
my.debug_draw = true;
|
||||
|
||||
var grey = "#999999";
|
||||
|
||||
var colors = [
|
||||
"#222222", // Grey
|
||||
"#000099",
|
||||
"#009900",
|
||||
"#990000",
|
||||
"#009999",
|
||||
"#990099",
|
||||
"#999900",
|
||||
"#000044",
|
||||
"#004400",
|
||||
"#440000",
|
||||
"#004444",
|
||||
"#440044",
|
||||
"#444400",
|
||||
"#0000cc",
|
||||
"#00cc00",
|
||||
"#cc0000",
|
||||
"#00cccc",
|
||||
"#cc00cc",
|
||||
"#cccc00"
|
||||
];
|
||||
|
||||
|
||||
|
||||
my.connect = function(server){
|
||||
connection = new WebSocket(server, null);
|
||||
my.websocket = connection;
|
||||
console.log(connection);
|
||||
connection.onerror = function (error) {
|
||||
console.log('WebSocket Error ' + error);
|
||||
};
|
||||
|
||||
connection.onopen = function(){
|
||||
console.log("Connected to " + server);
|
||||
my.send_game_request();
|
||||
};
|
||||
|
||||
connection.onclose = function(){
|
||||
my.id = null;
|
||||
my.state = null;
|
||||
my.renderer.reset();
|
||||
console.log("Connection Closed");
|
||||
|
||||
if (my.connection_retry > 0){
|
||||
// Retry every few seconds
|
||||
console.log("Lost Connection: " + server);
|
||||
setTimeout(function(){
|
||||
my.websocket = my.connect(my.server);
|
||||
}, my.connection_retry);
|
||||
}
|
||||
};
|
||||
|
||||
connection.onmessage = function (e) {
|
||||
|
||||
// Handshake protocol, we must establish a game ID and send
|
||||
// the robot config for approval before we can play
|
||||
|
||||
new_data = JSON.parse(e.data);
|
||||
|
||||
if (my.state === null) {
|
||||
if (new_data.type == "idreq") {
|
||||
if ('id' in new_data){
|
||||
my.id = new_data['id'];
|
||||
console.log("Assigned ID " + my.id + " by server");
|
||||
} else {
|
||||
console.log("server failed to send us an id");
|
||||
}
|
||||
my.send_client_id();
|
||||
my.state = "gameparam";
|
||||
// send supported encodings
|
||||
my.websocket.send(JSON.stringify(["json"]));
|
||||
}
|
||||
} else if (my.state == "gameparam"){
|
||||
if (new_data.type == "gameparam") {
|
||||
// > [OK | FULL | NOT AUTH], board size, game params
|
||||
my.parse_game_params(new_data);
|
||||
my.state = "handshake";
|
||||
my.setup_robot();
|
||||
}
|
||||
} else if (my.state == "handshake") {
|
||||
// We have game details, make the render target
|
||||
console.log("completed handshake");
|
||||
console.log(new_data);
|
||||
if('success' in new_data) {
|
||||
if (!new_data.success){
|
||||
alert("Your Robot Config is not valid, adjust your point distributions and try again");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
my.renderer.setup_render_target(my.width, my.height, my.width, my.height);
|
||||
my.state = "play";
|
||||
} else if(my.state == "play") {
|
||||
if (new_data.type == "handshake") {
|
||||
// This is the handshake response, we've been assigned an ID
|
||||
// and are in the game (or TODO: in a lobby).
|
||||
console.log("we got a handshake when we expect a boardstate packet");
|
||||
} else if (new_data.type == "stats") {
|
||||
// XXX: need to actually assign this to the player/robots
|
||||
// ...
|
||||
console.log("\\o/ we have stats: " + JSON.stringify(new_data));
|
||||
} else if (new_data.type == "boardstate") {
|
||||
my.renderer.begin_frame(new_data['turn']);
|
||||
var draw_list = my.process_gameplay_packet(new_data);
|
||||
my.renderer.render(draw_list);
|
||||
my.renderer.end_frame();
|
||||
} else if (new_data.type == "gameover") {
|
||||
my.renderer.reset();
|
||||
} else {
|
||||
console.error("unexpected state ...");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
my.prepare_data = function(data){
|
||||
|
||||
// get all the robot id's in the scanner list and find them in the
|
||||
// overall robot list so we can pass them to the robot code
|
||||
if (data['my_robots']){
|
||||
for (var i=0; i < data['my_robots'].length; i++){
|
||||
var bot = data['my_robots'][i];
|
||||
bot.messages = data.messages;
|
||||
|
||||
for(var p in bot['scanners']){
|
||||
var bot_id = bot['scanners'][p]['id'];
|
||||
|
||||
// find the scanner object in the pool of objects
|
||||
if (bot['scanners'][p]['type'] == 'robot'){
|
||||
|
||||
for (var b in data['robots']){
|
||||
if (data['robots'][b]['id'] == bot_id){
|
||||
bot['scanners'][p] = data['robots'][b];
|
||||
bot['scanners'][p].type = "robot";
|
||||
bot['scanners'][p].friend = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var mybot in data['my_robots']){
|
||||
if (data['my_robots'][mybot].id == bot['scanners'][p].id){
|
||||
bot['scanners'][p].friend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bot['scanners'][p]['type'] == 'projectile'){
|
||||
for (var b in data['projectiles']){
|
||||
if (data['projectiles'][b]['id'] == bot_id){
|
||||
bot['scanners'][p] = data['projectiles'][b];
|
||||
bot['scanners'][p].type = "projectile";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
my.process_gameplay_packet = function(new_data){
|
||||
|
||||
my.stats.begin();
|
||||
my.renderer.clear_debug();
|
||||
|
||||
var draw_list = [];
|
||||
|
||||
if (new_data.reset){
|
||||
// my.setup_robot();
|
||||
my.renderer.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my.paused) {
|
||||
var debug_div = document.getElementById("raw-debug");
|
||||
if (debug_div){
|
||||
debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Prep the data
|
||||
new_data = my.prepare_data(new_data);
|
||||
|
||||
// Dump the contents of the message from the server
|
||||
if (!my.paused) {
|
||||
var debug_div = document.getElementById("debug");
|
||||
if (debug_div){
|
||||
debug_div.innerHTML = JSON.stringify(new_data, undefined, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the robots and build a list of what to draw
|
||||
var my_robots = new_data['my_robots'];
|
||||
var robots = new_data['robots'];
|
||||
var all_bots = new_data['all_bots'];
|
||||
|
||||
my.all_bots = [];
|
||||
my.my_bots = my_robots;
|
||||
// Set the list of players and assign colors
|
||||
var players = "";
|
||||
|
||||
var col = 2;
|
||||
for (i=0; i < all_bots.length; i++){
|
||||
my.all_bots.push(all_bots[i]['robot_id']);
|
||||
|
||||
// I hate javascript
|
||||
var health_str = "";
|
||||
if (all_bots[i]['health'] < 0) {
|
||||
health_str = "000";
|
||||
all_bots[i]['color'] = grey;
|
||||
}
|
||||
else if (all_bots[i]['health'] < 10) {
|
||||
health_str = "00" + all_bots[i]['health'];
|
||||
all_bots[i]['color'] = colors[col];
|
||||
}
|
||||
else if (all_bots[i]['health'] < 100){
|
||||
health_str = "0" + all_bots[i]['health'];
|
||||
all_bots[i]['color'] = colors[col];
|
||||
}
|
||||
else{
|
||||
health_str = all_bots[i]['health'];
|
||||
all_bots[i]['color'] = colors[col];
|
||||
}
|
||||
|
||||
var mine = false;
|
||||
for (var mybot in my.my_bots){
|
||||
if (my.my_bots[mybot].id == all_bots[i]['robot_id']){
|
||||
mine = true;
|
||||
all_bots[i]['color'] = colors[1];
|
||||
}
|
||||
}
|
||||
if (!mine){
|
||||
col++;
|
||||
}
|
||||
|
||||
players += ("<span style='color: " +
|
||||
all_bots[i]['color'] +
|
||||
";'>  " +
|
||||
all_bots[i]['robot_id'] +
|
||||
" [" + health_str + "]</span>");
|
||||
}
|
||||
|
||||
var players_div = document.getElementById("players");
|
||||
if (players_div){
|
||||
players_div.innerHTML = players;
|
||||
}
|
||||
|
||||
// Now let's deal with all the things in the world
|
||||
var i = 0;
|
||||
if (my_robots){
|
||||
instructions = {};
|
||||
for (i=0; i < my_robots.length; i++){
|
||||
if (my_robots[i].health > 0) {
|
||||
instructions[my_robots[i]['id']] = my.update_robot(my_robots[i], new_data['obj']);
|
||||
}
|
||||
|
||||
for (var bot in all_bots){
|
||||
if (all_bots[bot]['robot_id'] == my_robots[i]['id']){
|
||||
col = all_bots[bot]['color'];
|
||||
}
|
||||
}
|
||||
|
||||
if (my_robots[i].health <= 0){
|
||||
col = grey;
|
||||
}
|
||||
|
||||
if ("position" in my_robots[i]){
|
||||
my_robots[i]['color'] = col;
|
||||
my_robots[i]['type'] = 'robot';
|
||||
draw_list.push(my_robots[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (my.websocket){
|
||||
var payload = JSON.stringify(instructions);
|
||||
// console.log(payload);
|
||||
my.websocket.send(payload);
|
||||
}
|
||||
|
||||
if (my.target < 0 && !my.spectator){
|
||||
console.log("Watching " + my.my_bots[0].id);
|
||||
my.renderer.set_focus_robot(my.my_bots[0].id);
|
||||
my.target = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (robots){
|
||||
for (i=0; i < robots.length; i++){
|
||||
|
||||
// Get the color from all_bots
|
||||
var col = grey;
|
||||
for (var bot in all_bots){
|
||||
if (all_bots[bot]['robot_id'] == robots[i]['id']){
|
||||
col = all_bots[bot]['color'];
|
||||
}
|
||||
}
|
||||
|
||||
if (robots[i].health <= 0){
|
||||
col = grey;
|
||||
}
|
||||
|
||||
if ("position" in robots[i]){
|
||||
robots[i]['color'] = col;
|
||||
robots[i]['type'] = 'robot';
|
||||
draw_list.push(robots[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the projecticles
|
||||
var projectiles = new_data['projectiles'];
|
||||
if (projectiles){
|
||||
for (i=0; i < projectiles.length; i++){
|
||||
|
||||
if ("position" in projectiles[i]){
|
||||
projectiles[i]['type'] = 'bullet';
|
||||
draw_list.push(projectiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the projecticles
|
||||
var splosions = new_data['splosions'];
|
||||
if (splosions){
|
||||
for (i=0; i < splosions.length; i++){
|
||||
if ("position" in splosions[i]){
|
||||
splosions[i]['type'] = 'explosion';
|
||||
draw_list.push(splosions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the objects
|
||||
var obj = new_data['objects'];
|
||||
if (obj){
|
||||
for (i=0; i < obj.length; i++){
|
||||
var o = {
|
||||
bounds : obj[i]
|
||||
};
|
||||
o['type'] = "object";
|
||||
o['position'] = {x: obj[i][0], y: obj[i][1]};
|
||||
draw_list.push(o);
|
||||
}
|
||||
}
|
||||
my.stats.end();
|
||||
|
||||
return draw_list;
|
||||
};
|
||||
|
||||
my.eval_input = function( input, output ){
|
||||
var theResult, evalSucceeded;
|
||||
|
||||
try{
|
||||
theResult = eval( input );
|
||||
evalSucceeded = true;
|
||||
}
|
||||
catch(e){
|
||||
output.innerHTML = e;
|
||||
}
|
||||
|
||||
if ( evalSucceeded ) {
|
||||
output.innerHTML = "";
|
||||
// console.log("OK");
|
||||
}
|
||||
|
||||
return theResult;
|
||||
};
|
||||
|
||||
my.get_robot_code = function(){
|
||||
var robot_code = editor.getSession().getValue();
|
||||
var output = document.getElementById('output');
|
||||
|
||||
var code = "( " + robot_code + " )";
|
||||
var rc = my.eval_input(code, output);
|
||||
|
||||
return rc.call(this);
|
||||
};
|
||||
|
||||
my.send_game_request = function() {
|
||||
game = {
|
||||
"id": my.game_id,
|
||||
};
|
||||
console.log(my.websocket.readyState);
|
||||
my.websocket.send(JSON.stringify(game));
|
||||
console.log("sent clientid: " + JSON.stringify(game));
|
||||
};
|
||||
|
||||
my.send_client_id = function() {
|
||||
client_id = {
|
||||
"type": my.spectator ? "spectator" : "robot",
|
||||
"name": "dummy",
|
||||
"id": "24601",
|
||||
"useragent": "gorobots.js",
|
||||
};
|
||||
my.websocket.send(JSON.stringify(client_id));
|
||||
};
|
||||
|
||||
my.parse_game_params = function(params) {
|
||||
// TODO: flesh out validation?
|
||||
my.width = new_data.boardsize.width;
|
||||
my.height = new_data.boardsize.height;
|
||||
return true;
|
||||
};
|
||||
|
||||
my.setup_robot = function(){
|
||||
var robot = my.get_robot_code();
|
||||
if ('setup' in robot){
|
||||
var map = {"width": my.width, "height": my.height};
|
||||
var config = {
|
||||
"stats": robot.setup(map),
|
||||
"id": my.id
|
||||
};
|
||||
// console.log(config);
|
||||
if (!my.spectator) {
|
||||
my.websocket.send(JSON.stringify(config));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
my.update_robot = function(data, objects){
|
||||
var robot = my.get_robot_code();
|
||||
var map = {"width": my.width, "height": my.height, "obj": objects};
|
||||
|
||||
var instructions = null;
|
||||
if ('update' in robot){
|
||||
|
||||
var dbg = {
|
||||
"msg" : function(x, y, msg){
|
||||
my.renderer.debug_label(x,y,msg);
|
||||
},
|
||||
"msg_screen" : function(x, y, msg){
|
||||
my.renderer.debug_label_screen(x,y,msg);
|
||||
},
|
||||
"line" : function(x, y, x2, y2){
|
||||
my.renderer.debug_line(x,y,x2,y2);
|
||||
},
|
||||
};
|
||||
|
||||
instructions = robot.update(data, map, dbg);
|
||||
}
|
||||
return instructions;
|
||||
};
|
||||
|
||||
my.set_spectator = function(s){
|
||||
my.spectator = s;
|
||||
my.target=-1;
|
||||
$('body').keypress(function(e){
|
||||
if (my.renderer && my.renderer.canvas.clientWidth === 0){
|
||||
// dont respond to key events when canvas is not in view
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.which == 116){
|
||||
// t
|
||||
if (my.spectator){
|
||||
my.target++;
|
||||
if( my.target < my.all_bots.length){
|
||||
console.log("Watching " + my.all_bots[my.target]);
|
||||
my.renderer.set_focus_robot(my.all_bots[my.target]);
|
||||
}
|
||||
else {
|
||||
my.target = -1;
|
||||
my.renderer.set_focus_robot(null);
|
||||
}
|
||||
} else {
|
||||
my.target++;
|
||||
if( my.target < my.my_bots.length){
|
||||
console.log(my.my_bots);
|
||||
console.log(my.my_bots[my.target].id);
|
||||
console.log("Watching " + my.my_bots[my.target].id);
|
||||
my.renderer.set_focus_robot(my.my_bots[my.target].id);
|
||||
}
|
||||
else {
|
||||
my.target = -1;
|
||||
my.renderer.set_focus_robot(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
my.set_server = function(server_name, game_id){
|
||||
my.server = server_name;
|
||||
my.game_id = game_id;
|
||||
my.state = null;
|
||||
if (my.websocket){
|
||||
console.log("Switching Server: " + my.server);
|
||||
my.websocket.close();
|
||||
my.renderer.shutdown();
|
||||
}
|
||||
else{
|
||||
console.log("Setting Server: " + my.server);
|
||||
my.websocket = my.connect(my.server);
|
||||
}
|
||||
};
|
||||
|
||||
my.init = function(){
|
||||
console.log("Welcome to GoRobots");
|
||||
if (webgl){
|
||||
my.renderer = gorobots_render_webgl();
|
||||
}
|
||||
else{
|
||||
my.renderer = gorobots_render_canvas();
|
||||
}
|
||||
|
||||
my.renderer.init("battlefield");
|
||||
|
||||
my.stats = new Stats();
|
||||
my.stats.setMode(0); // 0: fps, 1: ms
|
||||
|
||||
// Align top-left
|
||||
my.stats.domElement.style.position = 'absolute';
|
||||
|
||||
my.stats.domElement.style.left = '5px';
|
||||
my.stats.domElement.style.top = '120px';
|
||||
|
||||
document.body.appendChild( my.stats.domElement );
|
||||
$(my.stats.domElement).hide();
|
||||
|
||||
my.paused = false;
|
||||
$('body').keypress(function(e){
|
||||
|
||||
// alert(e.which);
|
||||
if(e.which == 112){
|
||||
// p
|
||||
my.paused = !my.paused;
|
||||
}
|
||||
|
||||
if (my.renderer && my.renderer.canvas.clientWidth === 0){
|
||||
// dont respond to key events when canvas is not in view
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.which == 100){
|
||||
// d
|
||||
$(my.stats.domElement).toggle();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
my.init();
|
||||
return my;
|
||||
};
|
608
ui/js/gorobots_render_webgl.js
Normal file
608
ui/js/gorobots_render_webgl.js
Normal file
@ -0,0 +1,608 @@
|
||||
var gorobots_render_webgl = function(){
|
||||
var my = {};
|
||||
var grey = "rgba(0, 0, 0, 0.5)";
|
||||
var red = "rgba(200, 0, 0, 0.5)";
|
||||
|
||||
// Tracking last 50 frame lengths
|
||||
var d = new Date();
|
||||
my.delta_history = [];
|
||||
my.last_frame_time = d.getTime();
|
||||
my.delta = 0;
|
||||
|
||||
my.width = null;
|
||||
my.height = null;
|
||||
my.y_base = 16; // The top status bar
|
||||
|
||||
my.scene = null;
|
||||
|
||||
// Tracking the objects in the scene
|
||||
my.robots = {};
|
||||
my.objects = {};
|
||||
my.dom_id = null;
|
||||
my.target = null;
|
||||
|
||||
|
||||
my.init = function(dom_id) {
|
||||
my.dom_id = dom_id;
|
||||
my.canvas = document.getElementById(dom_id);
|
||||
$('body').keypress(function(e){
|
||||
|
||||
if (my.canvas.clientWidth === 0){
|
||||
// dont respond to key events when canvas is not in view
|
||||
return;
|
||||
}
|
||||
|
||||
if(e.which == 118){
|
||||
// v
|
||||
my.currentcamera += 1;
|
||||
if (my.currentcamera >= my.cameras.length) {
|
||||
my.currentcamera = 0;
|
||||
}
|
||||
my.use_camera = my.cameras[my.currentcamera];
|
||||
}
|
||||
|
||||
if (e.which == 45){
|
||||
// -
|
||||
if (my.use_camera == my.followcamera){
|
||||
my.followcamera.fov = my.followcamera.fov - 5;
|
||||
if (my.followcamera.fov < 5){
|
||||
my.followcamera.fov = 5;
|
||||
}
|
||||
|
||||
my.followcamera.updateProjectionMatrix();
|
||||
}
|
||||
if (my.use_camera == my.trackcamera){
|
||||
my.track_area -= 10;
|
||||
if (my.track_area < 50){
|
||||
my.track_area = 50;
|
||||
}
|
||||
my.resize_view();
|
||||
}
|
||||
}
|
||||
if (e.which == 61){
|
||||
// =
|
||||
if (my.use_camera == my.followcamera){
|
||||
my.followcamera.fov = my.followcamera.fov + 5;
|
||||
if (my.followcamera.fov > 120){
|
||||
my.followcamera.fov = 120;
|
||||
}
|
||||
|
||||
my.followcamera.updateProjectionMatrix();
|
||||
}
|
||||
if (my.use_camera == my.trackcamera){
|
||||
my.track_area += 10;
|
||||
if (my.track_area > 1000){
|
||||
my.track_area = 1000;
|
||||
}
|
||||
my.resize_view();
|
||||
}
|
||||
|
||||
}
|
||||
if (e.which == 100){
|
||||
// d
|
||||
$(my.stats.domElement).toggle();
|
||||
}
|
||||
});
|
||||
|
||||
my.stats = new Stats();
|
||||
my.stats.setMode(0); // 0: fps, 1: ms
|
||||
|
||||
// Align top-left
|
||||
my.stats.domElement.style.position = 'absolute';
|
||||
|
||||
my.stats.domElement.style.left = '5px';
|
||||
my.stats.domElement.style.top = '200px';
|
||||
|
||||
document.body.appendChild( my.stats.domElement );
|
||||
$(my.stats.domElement).hide();
|
||||
};
|
||||
|
||||
var random_color = function () {
|
||||
var letters = '0123456789ABCDEF'.split('');
|
||||
var color = '#';
|
||||
for (var i = 0; i < 6; i++ ) {
|
||||
color += letters[Math.round(Math.random() * 15)];
|
||||
}
|
||||
return color;
|
||||
};
|
||||
|
||||
my.setup_cameras = function(){
|
||||
console.log("Setting up cameras");
|
||||
my.canvas = document.getElementById(my.dom_id);
|
||||
my.cameras = [];
|
||||
|
||||
var pos = new THREE.Vector3(my.width / 2, my.height / 2, 0);
|
||||
var game_aspect = my.height / my.width;
|
||||
var aspect = my.canvas.clientHeight / my.canvas.clientWidth;
|
||||
var scale = game_aspect/aspect;
|
||||
if (scale < 1){
|
||||
var new_scale = (1/game_aspect) / (1/aspect);
|
||||
var s = (my.height * new_scale) / 2;
|
||||
my.camera = new THREE.OrthographicCamera( 0, my.width , s + (my.height/2), -(s - (my.height/2)), 0.1, 200 );
|
||||
} else {
|
||||
var s = (my.width * scale) / 2;
|
||||
my.camera = new THREE.OrthographicCamera( -(s - (my.width/2)), s + (my.width/2), my.height, 0, 0.1, 200 );
|
||||
}
|
||||
|
||||
my.camera.position.z = 99;
|
||||
my.camera.updateProjectionMatrix();
|
||||
my.cameras.push(my.camera);
|
||||
|
||||
var view_aspect = my.canvas.clientHeight / my.canvas.clientWidth;
|
||||
my.track_area = 300;
|
||||
my.trackcamera = new THREE.OrthographicCamera( -my.track_area, my.track_area, my.track_area * view_aspect, -my.track_area * view_aspect, -100, 200 );
|
||||
my.trackcamera.position.z = 100;
|
||||
my.trackcamera.updateProjectionMatrix();
|
||||
my.cameras.push(my.trackcamera);
|
||||
|
||||
my.followcamera = new THREE.PerspectiveCamera( 35, my.canvas.clientWidth / my.canvas.clientHeight, 1, 10000 );
|
||||
my.followcamera.position = new THREE.Vector3(my.width / 2, -my.height + (my.height/2), (my.height / 2) + 200);
|
||||
my.followcamera.up = new THREE.Vector3(0,0,1);
|
||||
my.followcamera.lookAt(pos);
|
||||
my.followcamera.updateProjectionMatrix();
|
||||
my.controls = new THREE.TrackballControls( my.followcamera );
|
||||
|
||||
my.cameras.push(my.followcamera);
|
||||
};
|
||||
|
||||
my.setup_render_target = function(map_width, map_height, screen_width, screen_height){
|
||||
if (my.renderer){
|
||||
console.log("Renderer already initialized");
|
||||
my.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
my.turn = 0;
|
||||
my.robots = {};
|
||||
my.objects = {};
|
||||
my.labels = [];
|
||||
my.scene = new THREE.Scene();
|
||||
my.quit = false;
|
||||
|
||||
my.width = screen_width;
|
||||
my.height = screen_height;
|
||||
|
||||
my.renderer = new THREE.WebGLRenderer({"canvas": my.canvas, antialias: true });
|
||||
|
||||
my.setup_cameras();
|
||||
|
||||
my.floorobj = new THREE.CubeGeometry(my.width, my.height, 0.1);
|
||||
var material = new THREE.MeshLambertMaterial( { color: random_color() } );
|
||||
|
||||
my.floor = new THREE.Mesh(my.floorobj, material);
|
||||
my.floor.position.x = my.width / 2;
|
||||
my.floor.position.y = my.height / 2 ;
|
||||
my.floor.position.z = -2;
|
||||
my.scene.add(my.floor);
|
||||
|
||||
// Basic primitives
|
||||
my.grey_material = new THREE.MeshLambertMaterial( { color: "#999999", transparent: false } );
|
||||
my.red_material = new THREE.MeshLambertMaterial( { color: "#ff0000", transparent: true } );
|
||||
my.black_material = new THREE.MeshLambertMaterial( { color: "#000000", transparent: false } );
|
||||
|
||||
my.robot_geom = new THREE.CubeGeometry(5,5,5);
|
||||
|
||||
var loader = new THREE.OBJLoader();
|
||||
loader.load( '/s/robot.obj', function ( object ) {
|
||||
object.position.x = 100;
|
||||
object.position.y = 100;
|
||||
object.position.z = 1;
|
||||
object.up = new THREE.Vector3(0,0,1);
|
||||
object.scale.set(5,5,5);
|
||||
my.robot_geom = object;
|
||||
} );
|
||||
|
||||
my.bullet_geom = new THREE.CubeGeometry(3,3,3);
|
||||
my.splosion_geom = new THREE.SphereGeometry(1,50,50);
|
||||
|
||||
var axis = new THREE.AxisHelper(100);
|
||||
my.scene.add(axis);
|
||||
|
||||
my.wallobjx = new THREE.CubeGeometry(my.width, 3, 3);
|
||||
my.wallobjy = new THREE.CubeGeometry(3, my.height, 3);
|
||||
var material = new THREE.MeshLambertMaterial( { color: "#cccccc" } );
|
||||
|
||||
my.wall = new THREE.Mesh(my.wallobjx, material);
|
||||
my.wall.position.x = my.width / 2;
|
||||
my.scene.add(my.wall);
|
||||
|
||||
my.wall2 = new THREE.Mesh(my.wallobjx, material);
|
||||
my.wall2.position.x = my.width / 2;
|
||||
my.wall2.position.y = my.height ;
|
||||
my.scene.add(my.wall2);
|
||||
|
||||
my.wall3 = new THREE.Mesh(my.wallobjy, material);
|
||||
my.wall3.position.y = my.height / 2;
|
||||
my.scene.add(my.wall3);
|
||||
|
||||
my.wall4 = new THREE.Mesh(my.wallobjy, material);
|
||||
my.wall4.position.y = my.height / 2;
|
||||
my.wall4.position.x = my.width ;
|
||||
my.scene.add(my.wall4);
|
||||
|
||||
my.scene.add(my.camera);
|
||||
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.9 );
|
||||
directionalLight.position.set( 0, 0, -1 );
|
||||
my.scene.add( directionalLight );
|
||||
|
||||
var directionalLight2 = new THREE.DirectionalLight( 0xffffff, 0.9 );
|
||||
directionalLight2.position.set( 0, 0.707, 0.707 );
|
||||
my.scene.add( directionalLight2 );
|
||||
|
||||
var ambientLight = new THREE.AmbientLight(0x222222, 0.7);
|
||||
my.scene.add(ambientLight);
|
||||
|
||||
my.currentcamera = 2;
|
||||
my.use_camera = my.cameras[my.currentcamera];
|
||||
|
||||
var render = function render() {
|
||||
my.stats.begin();
|
||||
my.renderer.render(my.scene, my.use_camera);
|
||||
my.update_labels();
|
||||
if (my.controls){
|
||||
my.controls.update();
|
||||
}
|
||||
|
||||
if (!my.quit)
|
||||
requestAnimationFrame(render);
|
||||
|
||||
my.stats.end();
|
||||
};
|
||||
render();
|
||||
|
||||
$(window).resize(function(){
|
||||
my.resize_view();
|
||||
});
|
||||
|
||||
my.resize_view();
|
||||
};
|
||||
|
||||
my.resize_view = function(){
|
||||
my.canvas.height = ($(window).height() - $(my.canvas).offset().top);
|
||||
my.canvas.width = $(window).width();
|
||||
var game_aspect = my.height / my.width;
|
||||
var aspect = my.canvas.clientHeight / my.canvas.clientWidth;
|
||||
var scale = game_aspect/aspect;
|
||||
if (scale < 1){
|
||||
var new_scale = (1/game_aspect) / (1/aspect);
|
||||
var s = (my.height * new_scale) / 2;
|
||||
my.camera.left = 0;
|
||||
my.camera.right = my.width;
|
||||
my.camera.top = s + (my.height/2);
|
||||
my.camera.bottom = -(s - (my.height/2));
|
||||
} else {
|
||||
var s = (my.width * scale) / 2;
|
||||
my.camera.left = -(s - (my.width/2));
|
||||
my.camera.right = s + (my.width/2);
|
||||
my.camera.top = my.height;
|
||||
my.camera.bottom = 0;
|
||||
}
|
||||
my.camera.updateProjectionMatrix();
|
||||
|
||||
var view_aspect = my.canvas.clientHeight / my.canvas.clientWidth;
|
||||
my.trackcamera.left = -my.track_area;
|
||||
my.trackcamera.right = my.track_area;
|
||||
my.trackcamera.top = my.track_area * view_aspect;
|
||||
my.trackcamera.bottom = -my.track_area * view_aspect;
|
||||
my.trackcamera.updateProjectionMatrix();
|
||||
|
||||
my.followcamera.aspect = my.canvas.clientWidth / my.canvas.clientHeight;
|
||||
my.followcamera.updateProjectionMatrix();
|
||||
|
||||
my.use_camera = my.cameras[my.currentcamera];
|
||||
my.renderer.context.viewport(0, 0, my.canvas.width, my.canvas.height);
|
||||
};
|
||||
|
||||
my.begin_frame = function(turn){
|
||||
my.turn = turn;
|
||||
var d = new Date();
|
||||
var time = d.getTime();
|
||||
my.delta = time - my.last_frame_time;
|
||||
my.delta_history.unshift(my.delta);
|
||||
if (my.delta_history.length > 50){
|
||||
my.delta_history.pop();
|
||||
}
|
||||
|
||||
my.last_frame_time = time;
|
||||
|
||||
// Status Bar Text
|
||||
var slowest = Math.max.apply(null, my.delta_history);
|
||||
var debug_string = slowest + "ms";
|
||||
};
|
||||
|
||||
my.render = function(draw_list){
|
||||
var obj_count = 0;
|
||||
|
||||
for (var i in draw_list) {
|
||||
var robot_data = draw_list[i];
|
||||
var key, geometry, material, cube = null;
|
||||
|
||||
if (robot_data['type'] == 'object'){
|
||||
|
||||
key = robot_data['type'] + robot_data.bounds[0] + robot_data.bounds[1];
|
||||
if (!(key in my.objects)){
|
||||
obj_count++;
|
||||
console.log(robot_data);
|
||||
x = robot_data.bounds[0];
|
||||
y = robot_data.bounds[1];
|
||||
w = robot_data.bounds[2] - robot_data.bounds[0];
|
||||
h = robot_data.bounds[3] - robot_data.bounds[1];
|
||||
|
||||
geometry = new THREE.CubeGeometry(w,h,5 + Math.round(Math.random() * 25));
|
||||
var material = new THREE.MeshLambertMaterial( { color: random_color(), transparent: false } );
|
||||
cube = new THREE.Mesh( geometry, material );
|
||||
|
||||
my.scene.add( cube );
|
||||
cube.position.x = x + (w / 2);
|
||||
cube.position.y = y + (h / 2);
|
||||
cube.position.z = 0;
|
||||
cube.turn = my.turn;
|
||||
|
||||
my.objects[key] = cube;
|
||||
continue;
|
||||
} else {
|
||||
my.objects[key].turn = my.turn;
|
||||
}
|
||||
}
|
||||
else{
|
||||
key = robot_data['type'] + robot_data['id'];
|
||||
|
||||
if (!(key in my.robots)){
|
||||
|
||||
if (robot_data['type'] == 'bullet'){
|
||||
material = my.black_material;
|
||||
geometry = my.bullet_geom;
|
||||
cube = new THREE.Mesh( geometry, material );
|
||||
}
|
||||
else if (robot_data['type'] == 'explosion'){
|
||||
size = robot_data.radius;
|
||||
material = my.red_material;
|
||||
geometry = my.splosion_geom;
|
||||
material.opacity = 0.8;
|
||||
cube = new THREE.Mesh( geometry, material );
|
||||
cube.scale.set(size, size, size);
|
||||
}
|
||||
else {
|
||||
material = new THREE.MeshLambertMaterial( { color: robot_data['color'], transparent: false } );
|
||||
geometry = my.robot_geom;
|
||||
cube = geometry.clone();
|
||||
cube.traverse(function ( geo ) {
|
||||
geo.material = new THREE.MeshLambertMaterial( { color: robot_data['color'] } );
|
||||
} );
|
||||
my.make_bot_label(robot_data.id, robot_data.name, cube);
|
||||
}
|
||||
|
||||
my.robots[key] = cube;
|
||||
my.scene.add(cube);
|
||||
cube.position.x = robot_data['position']['x'];
|
||||
cube.position.y = robot_data['position']['y'];
|
||||
cube.position.z = -1;
|
||||
cube.turn = my.turn;
|
||||
}
|
||||
else{
|
||||
my.robots[key].position.x = robot_data['position']['x'];
|
||||
my.robots[key].position.y = robot_data['position']['y'];
|
||||
my.robots[key].turn = my.turn;
|
||||
|
||||
if ("heading" in robot_data){
|
||||
if (robot_data['heading']['x'] == 0 && robot_data['heading']['y'] == 0) {
|
||||
robot_data['heading']['y'] = 1;
|
||||
}
|
||||
var tmp = new THREE.Vector3(
|
||||
robot_data['heading']['x'] + robot_data.position.x,
|
||||
robot_data['heading']['y'] + robot_data.position.y,
|
||||
-1);
|
||||
my.robots[key].up = new THREE.Vector3(0,0,1);
|
||||
my.robots[key].lookAt( tmp );
|
||||
}
|
||||
|
||||
if (robot_data.health){
|
||||
var h = Math.round((robot_data.health / 200) * 100) + "%";
|
||||
var x = "#health_" + robot_data.id;
|
||||
$(x).css({
|
||||
"width": h
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Worth noting this list contains robots, bullets and explosions
|
||||
// it's badly named, I am sorry!
|
||||
for (var q in my.robots){
|
||||
if (my.turn - my.robots[q].turn > 1){
|
||||
// find and remove label?
|
||||
for (var w in my.labels){
|
||||
if (my.labels[w].target == my.robots[q]){
|
||||
my.labels[w].label.remove();
|
||||
my.labels.splice(w, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Can I do this? I dont think so, we dont know
|
||||
// if these things will ever be back
|
||||
|
||||
// my.robots[q].traverse(function ( geo ) {
|
||||
// geo.visible = false;
|
||||
// });
|
||||
|
||||
my.scene.remove(my.robots[q]);
|
||||
delete my.robots[q];
|
||||
}
|
||||
}
|
||||
|
||||
for (var q in my.objects){
|
||||
if (my.turn - my.objects[q].turn > 1){
|
||||
my.objects[q].visible = false;
|
||||
} else {
|
||||
my.objects[q].visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (var p in draw_list){
|
||||
if (draw_list[p]['type'] == "robot"){
|
||||
if (draw_list[p]['id'] == my.target){
|
||||
var pos = new THREE.Vector3(draw_list[p].position.x, draw_list[p].position.y, 0);
|
||||
my.followcamera.lookAt(pos);
|
||||
my.controls.target = pos;
|
||||
my.trackcamera.position.x = pos.x;
|
||||
my.trackcamera.position.y = pos.y;
|
||||
|
||||
my.trackcamera.updateProjectionMatrix();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var projector = new THREE.Projector();
|
||||
|
||||
my.update_labels = function(){
|
||||
for (var i in my.labels){
|
||||
var label = my.labels[i];
|
||||
var screen_pos = label.target.position.clone();
|
||||
var x = $(my.canvas).position();
|
||||
if (my.canvas.clientWidth === 0){
|
||||
$(label.label).css({
|
||||
display: "none"});
|
||||
} else {
|
||||
projector.projectVector( screen_pos, my.use_camera );
|
||||
if (screen_pos.x > 1 || screen_pos.x < -1 || screen_pos.y > 1 || screen_pos.y < -1){
|
||||
$(label.label).css({
|
||||
display: "none"});
|
||||
continue;
|
||||
}
|
||||
|
||||
var sx = (screen_pos.x * ((my.canvas.clientWidth)/2)) + ((my.canvas.clientWidth)/2) + x.left;
|
||||
var sy = -(screen_pos.y * ((my.canvas.clientHeight)/2)) + ((my.canvas.clientHeight)/2) + x.top - 30;
|
||||
$(label.label).css({
|
||||
position: "absolute",
|
||||
display:"block",
|
||||
marginLeft: 0,
|
||||
marginTop: 0,
|
||||
top: sy,
|
||||
left: sx});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
my.make_bot_label = function(id, text, track_object){
|
||||
var obj = $("<div class='label' style='display:none;'>" + text + "<div class='health' id='health_" + id + "'></div></div>");
|
||||
$("body").append(obj);
|
||||
my.labels.push({
|
||||
label: obj,
|
||||
target: track_object
|
||||
});
|
||||
};
|
||||
|
||||
my.debug_labels = [];
|
||||
my.debug_lines = [];
|
||||
|
||||
my.clear_debug = function(){
|
||||
for (var i in my.debug_labels){
|
||||
my.debug_labels[i].remove();
|
||||
}
|
||||
|
||||
my.debug_labels = [];
|
||||
|
||||
for (var i in my.debug_lines){
|
||||
my.scene.remove(my.debug_lines[i]);
|
||||
}
|
||||
delete my.debug_lines;
|
||||
my.debug_lines = [];
|
||||
};
|
||||
|
||||
my.debug_label = function(x, y, msg){
|
||||
if (my.canvas.clientWidth > 0){
|
||||
var screen_pos = new THREE.Vector3(x, y, 0);
|
||||
var canvas_pos = $(my.canvas).position();
|
||||
projector.projectVector( screen_pos, my.use_camera );
|
||||
|
||||
if (screen_pos.x > 1 || screen_pos.x < -1 || screen_pos.y > 1 || screen_pos.y < -1){
|
||||
return;
|
||||
}
|
||||
|
||||
var sx = (screen_pos.x * ((my.canvas.clientWidth)/2)) + ((my.canvas.clientWidth)/2) + canvas_pos.left;
|
||||
var sy = -(screen_pos.y * ((my.canvas.clientHeight)/2)) + ((my.canvas.clientHeight)/2) + canvas_pos.top;
|
||||
var obj = $("<div class='label'>" + msg + "</div>");
|
||||
$("body").append(obj);
|
||||
$(obj).css({
|
||||
position: "absolute",
|
||||
marginLeft: 0,
|
||||
marginTop: 0,
|
||||
top: sy,
|
||||
left: sx});
|
||||
|
||||
my.debug_labels.push(obj);
|
||||
}
|
||||
};
|
||||
|
||||
my.debug_label_screen = function(x, y, msg){
|
||||
if (my.canvas.clientWidth > 0){
|
||||
|
||||
var canvas_pos = $(my.canvas).position();
|
||||
var obj = $("<div class='label'>" + msg + "</div>");
|
||||
$("body").append(obj);
|
||||
$(obj).css({
|
||||
position: "absolute",
|
||||
marginLeft: 0,
|
||||
marginTop: 0,
|
||||
top: y + canvas_pos.top,
|
||||
left: x + canvas_pos.left});
|
||||
|
||||
my.debug_labels.push(obj);
|
||||
}
|
||||
};
|
||||
|
||||
my.debug_line = function(x1, y1, x2, y2){
|
||||
var material = new THREE.LineBasicMaterial({
|
||||
color: 0x0000ff
|
||||
});
|
||||
|
||||
var geometry = new THREE.Geometry();
|
||||
geometry.vertices.push(new THREE.Vector3(x1, y1, 0));
|
||||
geometry.vertices.push(new THREE.Vector3(x2, y2, 0));
|
||||
|
||||
var line = new THREE.Line(geometry, material);
|
||||
my.scene.add(line);
|
||||
my.debug_lines.push(line);
|
||||
};
|
||||
|
||||
my.set_focus_robot = function(id){
|
||||
my.target = id;
|
||||
};
|
||||
|
||||
my.reset = function(){
|
||||
for (var i in my.objects){
|
||||
my.scene.remove(my.objects[i]);
|
||||
}
|
||||
delete my.objects;
|
||||
my.objects = {};
|
||||
|
||||
for (i in my.robots){
|
||||
my.scene.remove(my.robots[i]);
|
||||
}
|
||||
delete my.robots;
|
||||
my.robots = {};
|
||||
|
||||
my.clear_debug();
|
||||
|
||||
for (i in my.labels){
|
||||
my.labels[i].label.remove();
|
||||
}
|
||||
my.labels = [];
|
||||
};
|
||||
|
||||
my.end_frame = function(){
|
||||
|
||||
};
|
||||
|
||||
my.shutdown = function(){
|
||||
my.reset();
|
||||
my.quit = true;
|
||||
};
|
||||
|
||||
return my;
|
||||
};
|
6
ui/js/stats.min.js
vendored
Normal file
6
ui/js/stats.min.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// stats.js - http://github.com/mrdoob/stats.js
|
||||
var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
|
||||
i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
|
||||
k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
|
||||
"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
|
||||
a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};
|
705
ui/js/three.min.js
vendored
Normal file
705
ui/js/three.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
774
ui/js/vec2d.js
Normal file
774
ui/js/vec2d.js
Normal file
@ -0,0 +1,774 @@
|
||||
// Copyright (c) 2013 Evan Shortiss
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
(function() {
|
||||
// Default is to not use objects
|
||||
var useObjects = false;
|
||||
// Default is to use standard array
|
||||
var AxesArray = Array;
|
||||
|
||||
|
||||
/**
|
||||
* Parse the arguments passed into Vec2D and Vec3D.
|
||||
* @param {Mixed} args The argument array from caller function.
|
||||
* @return {Array} The resulting axes array.
|
||||
*/
|
||||
function parseArgs(args) {
|
||||
// User passed x and y
|
||||
if (args.length === 2) {
|
||||
return new Array(args[0], args[1]);
|
||||
}
|
||||
// User passed an array
|
||||
else if (Object.prototype.toString.call(args[0]) === '[object Array]') {
|
||||
return args[0];
|
||||
}
|
||||
// User passed x and y in an object
|
||||
else if (typeof(args[0]) === 'object') {
|
||||
return [args[0].x, args[0].y];
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check is the passed param a Vec2D instance.
|
||||
* @param {Mixed} v The var to check.
|
||||
* @return {Boolean} The result of check.
|
||||
*/
|
||||
function isVector(v) {
|
||||
return v instanceof Vector || v instanceof ObjectVector;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Main array based vector class. Has instance methods attached to prototype.
|
||||
* @constructor
|
||||
* @param {Array} axes
|
||||
****************************************************************************/
|
||||
var Vector = function(axes) {
|
||||
// Constructor for each array type is different. (Float32Array vs Array)
|
||||
if (AxesArray.name === 'Float32Array') {
|
||||
this._axes = new AxesArray([axes[0], axes[1]]);
|
||||
} else {
|
||||
this._axes = new AxesArray(axes[0], axes[1]);
|
||||
}
|
||||
};
|
||||
|
||||
Vector.prototype = {
|
||||
/**
|
||||
* Set both x and y
|
||||
* @param x New x val
|
||||
* @param y New y val
|
||||
*/
|
||||
setAxes: function(x, y) {
|
||||
this._axes[0] = x;
|
||||
this._axes[1] = y;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Getter for x axis.
|
||||
* @return {Number}
|
||||
*/
|
||||
getX: function() {
|
||||
return this._axes[0];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Setter for x axis.
|
||||
*/
|
||||
setX: function(x) {
|
||||
this._axes[0] = x;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Getter for y axis.
|
||||
* @return {Number}
|
||||
*/
|
||||
getY: function() {
|
||||
return this._axes[1];
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Setter for y axis.
|
||||
*/
|
||||
setY: function(y) {
|
||||
this._axes[1] = y;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* View vector as a string such as "Vec2D: (0, 4)"
|
||||
* @param {Boolean}
|
||||
* @return {String}
|
||||
*/
|
||||
toString: function(round) {
|
||||
if (round) {
|
||||
return '(' + Math.round(this.getX()) + ', ' + Math.round(this.getY()) + ')'
|
||||
}
|
||||
return '(' + this.getX() + ', ' + this.getY() + ')'
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return an array containing the vector axes.
|
||||
* @return {Array}
|
||||
*/
|
||||
toArray: function() {
|
||||
return new Array(this.getX(), this.getY());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return an array containing the vector axes.
|
||||
* @return {Object}
|
||||
*/
|
||||
toObject: function() {
|
||||
return {
|
||||
"x": this.getX(),
|
||||
"y": this.getY()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add the provided Vector to this one.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
add: function(vec) {
|
||||
this._axes[0] += vec._axes[0];
|
||||
this._axes[1] += vec._axes[1];
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Subtract the provided vector from this one.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
subtract: function(vec) {
|
||||
this._axes[0] -= vec._axes[0];
|
||||
this._axes[1] -= vec._axes[1];
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Check is the vector provided equal to this one.
|
||||
* @param {Vec2D} vec
|
||||
* @return {Boolean}
|
||||
*/
|
||||
equals: function(vec) {
|
||||
return (vec._axes[0] == this._axes[0] && vec._axes[1] == this._axes[1]);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
multiplyByVector: function(vec) {
|
||||
this._axes[0] *= vec._axes[0];
|
||||
this._axes[1] *= vec._axes[1];
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
divideByVector: function(vec) {
|
||||
this._axes[0] /= vec._axes[0];
|
||||
this._axes[1] /= vec._axes[1];
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided number
|
||||
* @param {Number} n
|
||||
*/
|
||||
multiplyByScalar: function(n) {
|
||||
this._axes[0] *= n;
|
||||
this._axes[1] *= n;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Divive this vector by the provided number
|
||||
* @param {Number} n
|
||||
*/
|
||||
divideByScalar: function(n) {
|
||||
this._axes[0] /= n;
|
||||
this._axes[1] /= n;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Normalise this vector. Directly affects this vector.
|
||||
* Use Vec2D.normalise(vector) to create a normalised clone of this.
|
||||
*/
|
||||
normalise: function() {
|
||||
return this.multiplyByScalar(1 / this.magnitude());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* For American spelling.
|
||||
* Same as unit/normalise function.
|
||||
*/
|
||||
normalize: function() {
|
||||
return this.normalise();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* The same as normalise.
|
||||
*/
|
||||
unit: function() {
|
||||
return this.normalise();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the magnitude (length) of this vector.
|
||||
* @return {Number}
|
||||
*/
|
||||
magnitude: function() {
|
||||
return Math.sqrt((this._axes[0] * this._axes[0]) + (this._axes[1] * this._axes[1]));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the magnitude (length) of this vector.
|
||||
* @return {Number}
|
||||
*/
|
||||
length: function() {
|
||||
return this.magnitude();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the dot product of this vector by another.
|
||||
* @param {Vector} vec
|
||||
* @return {Number}
|
||||
*/
|
||||
dot: function(vec) {
|
||||
return (vec._axes[0] * this._axes[0]) + (vec._axes[1] * this._axes[1]);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the cross product of this vector by another.
|
||||
* @param {Vector} vec
|
||||
* @return {Number}
|
||||
*/
|
||||
cross: function(vec) {
|
||||
return ((this._axes[0] * vec._axes[1]) - (this._axes[1] * vec._axes[0]));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Returns the reverse of the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
reverse: function(vec) {
|
||||
this._axes[0] = -this._axes[0];
|
||||
this._axes[1] = -this._axes[1];
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Create a copy of this vector.
|
||||
* @return {Vector}
|
||||
*/
|
||||
clone: function() {
|
||||
return new Vector([this._axes[0], this._axes[1]]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Main object based vector class. Has instance methods attached to prototype.
|
||||
* @constructor
|
||||
* @param {Array} axes
|
||||
****************************************************************************/
|
||||
var ObjectVector = function(axes) {
|
||||
// Constructor for each array type is different. (Float32Array vs Array)
|
||||
this._axes = {
|
||||
x: axes[0],
|
||||
y: axes[1]
|
||||
};
|
||||
};
|
||||
|
||||
ObjectVector.prototype = {
|
||||
/**
|
||||
* Set both x and y
|
||||
* @param x New x val
|
||||
* @param y New y val
|
||||
*/
|
||||
setAxes: function(x, y) {
|
||||
this._axes.x = x;
|
||||
this._axes.y = y;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Getter for x axis.
|
||||
* @return {Number}
|
||||
*/
|
||||
getX: function() {
|
||||
return this._axes.x;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Setter for x axis.
|
||||
*/
|
||||
setX: function(x) {
|
||||
this._axes.x = x;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Getter for y axis.
|
||||
* @return {Number}
|
||||
*/
|
||||
getY: function() {
|
||||
return this._axes.y;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Setter for y axis.
|
||||
*/
|
||||
setY: function(y) {
|
||||
this._axes.y = y;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* View vector as a string such as "Vec2D: (0, 4)"
|
||||
* @param {Boolean}
|
||||
* @return {String}
|
||||
*/
|
||||
toString: function(round) {
|
||||
if (round) {
|
||||
return '(' + Math.round(this.getX()) + ', ' + Math.round(this.getY()) + ')'
|
||||
}
|
||||
return '(' + this.getX() + ', ' + this.getY() + ')'
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return an array containing the vector axes.
|
||||
* @return {Array}
|
||||
*/
|
||||
toArray: function() {
|
||||
return new Array(this.getX(), this.getY());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return an array containing the vector axes.
|
||||
* @return {Object}
|
||||
*/
|
||||
toObject: function() {
|
||||
return {
|
||||
"x": this.getX(),
|
||||
"y": this.getY()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add the provided Vector to this one.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
add: function(vec) {
|
||||
this._axes.x += vec._axes.x;
|
||||
this._axes.y += vec._axes.y;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Subtract the provided vector from this one.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
subtract: function(vec) {
|
||||
this._axes.x -= vec._axes.x;
|
||||
this._axes.y -= vec._axes.y;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Check is the vector provided equal to this one.
|
||||
* @param {Vec2D} vec
|
||||
* @return {Boolean}
|
||||
*/
|
||||
equals: function(vec) {
|
||||
return (vec._axes.x == this._axes.x && vec._axes.y == this._axes.y);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
multiplyByVector: function(vec) {
|
||||
this._axes.x *= vec._axes.x;
|
||||
this._axes.y *= vec._axes.y;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
divideByVector: function(vec) {
|
||||
this._axes.x /= vec._axes.x;
|
||||
this._axes.y /= vec._axes.y;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply this vector by the provided number
|
||||
* @param {Number} n
|
||||
*/
|
||||
multiplyByScalar: function(n) {
|
||||
this._axes.x *= n;
|
||||
this._axes.y *= n;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Divive this vector by the provided number
|
||||
* @param {Number} n
|
||||
*/
|
||||
divideByScalar: function(n) {
|
||||
this._axes.x /= n;
|
||||
this._axes.y /= n;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Normalise this vector. Directly affects this vector.
|
||||
* Use Vec2D.normalise(vector) to create a normalised clone of this.
|
||||
*/
|
||||
normalise: function() {
|
||||
return this.multiplyByScalar(1 / this.magnitude());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* For American spelling.
|
||||
* Same as unit/normalise function.
|
||||
*/
|
||||
normalize: function() {
|
||||
return this.normalise();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* The same as normalise.
|
||||
*/
|
||||
unit: function() {
|
||||
return this.normalise();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the magnitude (length) of this vector.
|
||||
* @return {Number}
|
||||
*/
|
||||
magnitude: function() {
|
||||
return Math.sqrt((this._axes.x * this._axes.x) + (this._axes.y * this._axes.y));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the magnitude (length) of this vector.
|
||||
* @return {Number}
|
||||
*/
|
||||
length: function() {
|
||||
return this.magnitude();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the dot product of this vector by another.
|
||||
* @param {Vector} vec
|
||||
* @return {Number}
|
||||
*/
|
||||
dot: function(vec) {
|
||||
return (vec._axes.x * this._axes.x) + (vec._axes.y * this._axes.y);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the cross product of this vector by another.
|
||||
* @param {Vector} vec
|
||||
* @return {Number}
|
||||
*/
|
||||
cross: function(vec) {
|
||||
return ((this._axes.x * vec._axes.y) - (this._axes.y * vec._axes.x));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Returns the reverse of the provided vector.
|
||||
* @param {Vector} vec
|
||||
*/
|
||||
reverse: function(vec) {
|
||||
this._axes.x = -this._axes.x;
|
||||
this._axes.y = -this._axes.y;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Create a copy of this vector.
|
||||
* @return {Vector}
|
||||
*/
|
||||
clone: function() {
|
||||
return new ObjectVector([this._axes.x, this._axes.y]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Publically exposed Vector interface.
|
||||
****************************************************************************/
|
||||
var Vec2D = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
Vec2D.prototype = {
|
||||
Vector: Vector,
|
||||
ObjectVector: ObjectVector,
|
||||
|
||||
/**
|
||||
* Create a new Vector.
|
||||
* @param {Number} [x]
|
||||
* @param {Number} [y]
|
||||
* @param {Object} [axes]
|
||||
* @param {Array} [axes]
|
||||
*/
|
||||
create: function() {
|
||||
if(useObjects) {
|
||||
return new this.ObjectVector(parseArgs(arguments));
|
||||
}
|
||||
return new this.Vector(parseArgs(arguments));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Instruct the library to use standard JavaScript arrays.
|
||||
* Otherwise the library will try use Float32Arrays if available. (This is default)
|
||||
*/
|
||||
useStandardArrays: function() {
|
||||
useObjects = false;
|
||||
AxesArray = Array;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Instruct the library to use Float32 JavaScript arrays. (This is default)
|
||||
* Otherwise the library will use standard array.
|
||||
*/
|
||||
useFloat32Arrays: function() {
|
||||
useObjects = false;
|
||||
AxesArray = Float32Array;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Instruct library to use Objects to represent vectors.
|
||||
*/
|
||||
useObjects: function() {
|
||||
useObjects = true;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add v0 to v1 to produce a new vector
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Vector}
|
||||
*/
|
||||
add: function(v0, v1) {
|
||||
return this.create(v0.getX() + v1.getX(), v0.getY() + v1.getY())
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Subtract v0 from v1 to produce a new Vector.
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Vector}
|
||||
*/
|
||||
subtract: function(v0, v1) {
|
||||
return this.create(v0.getX() - v1.getX(), v0.getY()- v1.getY())
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Check are the provided vectors equal.
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Boolean}
|
||||
*/
|
||||
equals: function(v0, v1) {
|
||||
return v0.equals(v1);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply a vector by a vector to produce a new Vector
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Vector}
|
||||
*/
|
||||
vectorTimesVector: function(v0, v1) {
|
||||
return this.create(v0.getX() * v1.getX(), v0.getY() * v1.getY());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Multiply a vector by a number to produce a new vector.
|
||||
* @param {Vector} vec The var to store a result in.
|
||||
* @param {Number} n The Vector to subtract from this one.
|
||||
* @return {Vector}
|
||||
*/
|
||||
vectorTimesScalar: function(vec, n) {
|
||||
return this.create(vec.getX() * n, vec.getY() * n);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return a normalised version of provided vector.
|
||||
* @param {Vector} vec
|
||||
* @return {Vector}
|
||||
*/
|
||||
nomalise: function(vec) {
|
||||
return this.timesScalar(vec, 1 / vec.magnitude());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Same as normalise
|
||||
*/
|
||||
normalize: function(vec) {
|
||||
return this.normalise(vec);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Same as normalise.
|
||||
*/
|
||||
unit: function(vec) {
|
||||
return this.normalise(vec);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the dot product of two vectors or this vector dot another.
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Number}
|
||||
*/
|
||||
dot: function(v0, v1) {
|
||||
return (v0.getX() * v1.getX()) + (v0.getY() * v1.getY());
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the cross product of two vectors.
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @param {Vector}
|
||||
*/
|
||||
cross: function(v0, v1) {
|
||||
return ((v0.getX() * v1.getY()) - (v0.getY() * v1.getX()));
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Return the magnitude (length) of a vector.
|
||||
* @param {Vector} vec
|
||||
* @return {Number}
|
||||
*/
|
||||
magnitude: function(vec) {
|
||||
return vec.magnitude();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Same as the magnitude.
|
||||
*/
|
||||
length: function(vec) {
|
||||
return vec.magnitude();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Find sqaure distance between two vectors.
|
||||
* @param {Vector} v0
|
||||
* @param {Vector} v1
|
||||
* @return {Number}
|
||||
*/
|
||||
distance: function(v0, v1) {
|
||||
return Math.sqrt((v0.getX() - v1.getX()) * (v0.getX() - v1.getX()) + (v0.getY() - v1.getY()) * (v0.getY() - v1.getY()))
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Returns the reverse of the provided vector.
|
||||
* @param {Vector} vec
|
||||
* @return {Vector}
|
||||
*/
|
||||
reverse: function(vec) {
|
||||
return this.create(-vec.getX(), -vec.getY());
|
||||
}
|
||||
};
|
||||
|
||||
// Expose publically
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Vec2D = new Vec2D();
|
||||
} else {
|
||||
module.exports = new Vec2D();
|
||||
}
|
||||
|
||||
})();
|
Loading…
Reference in New Issue
Block a user