const CLOCKTICK = 100; // speed of run - move things every n milliseconds
const MAXSTEPS = 1000; // length of a run before final score
const SCREENSHOT_STEP = 50;
//---- global constants: -------------------------------------------------------
const gridsize = 30; // number of squares along side of world
const NOBOXES = Math.trunc ( (gridsize * gridsize) / 10 );
const squaresize = 100; // size of square in pixels
const MAXPOS = gridsize * squaresize; // length of one side in pixels
const SKYCOLOR = 0xffffcc; // a number, not a string
const BLANKCOLOR = SKYCOLOR ; // make objects this color until texture arrives (from asynchronous file read)
const startRadiusConst = MAXPOS * 0.8 ; // distance from centre to start the camera at
const skyboxConst = MAXPOS * 3;
const maxRadiusConst = MAXPOS * 100 ; // maximum distance from camera we will render things
const show3d = false;
//--- Mind can pick one of these actions -----------------
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
const ACTION_STAYSTILL = 4;
const ACTION_LEFT2 = 0;
const ACTION_RIGHT2 = 1;
const ACTION_UP2 = 2;
const ACTION_DOWN2 = 3;
// --- some useful random functions -------------------------------------------
function randomfloatAtoB ( A, B )
{
return ( A + ( Math.random() * (B-A) ) );
}
function randomintAtoB ( A, B )
{
return ( Math.round ( randomfloatAtoB ( A, B ) ) );
}
function randomBoolean()
{
if ( Math.random() < 0.5 ) { return false; }
else { return true; }
}
//---- start of World class -------------------------------------------------------
function World() {
var playerOneScore = 0;
var playerTwoScore = 0;
var bounces = 0;
// most of World can be private
// regular "var" syntax means private variables:
var GRID = new Array(gridsize); // can query GRID about whether squares are occupied, will in fact be initialised as a 2D array
var WALLS = new Array ( 4 * gridsize ); // need to keep handle to each wall block object so can find it later to paint it
var myPaddle, enemyPaddle, ball;
// enemy and agent position on squares
var si, sj, ai, aj , bi, bj;
var direct = 0;
var goodsteps;
var self = this;
var audio = new Audio('/uploads/strelki2/bounce1.mp3');
var error = new Audio('/uploads/strelki2/error.mp3');
// regular "function" syntax means private functions:
function initGrid()
{
for (var i = 0; i < gridsize ; i++)
{
GRID[i] = new Array(gridsize); // each element is an array
for (var j = 0; j < gridsize ; j++)
{
GRID[i][j] = false;
}
}
}
function occupied ( i, j ) // is this square occupied
{
if ( ( ai == i ) && ( aj == j ) ) return true;
if ( ( si == i ) && ( sj == j ) ) return true;
if ( GRID[i+2][j] === true ) return true;
if ( GRID[i-2][j] === true ) return true;// fixed object
return false;
}
function translate ( x )
{
return ( x - ( MAXPOS/2 ) );
}
function initSkybox()
{
var materialArray = [
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_z.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_z.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_y.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_y.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_x.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_x.jpg" ), side: THREE.BackSide } ) ),
];
var skyGeometry = new THREE.CubeGeometry ( skyboxConst, skyboxConst, skyboxConst );
var skyMaterial = new THREE.MeshFaceMaterial ( materialArray );
var theskybox = new THREE.Mesh ( skyGeometry, skyMaterial );
threeworld.scene.add( theskybox ); // We are inside a giant cube
}
function loadTextures()
{
var loader1 = new THREE.TextureLoader();
loader1.load ( '/uploads/strelki2/black.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader2 = new THREE.TextureLoader();
loader2.load ( '/uploads/strelki2/pink.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
myPaddle.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
var loader3 = new THREE.TextureLoader();
loader3.load ( '/uploads/strelki2/green.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
enemyPaddle.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
var loader4 = new THREE.TextureLoader();
loader4.load ( '/uploads/strelki2/blue.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
ball.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
}
function initLogicalWalls() // set up logical walls in data structure, whether doing graphical run or not
{
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < gridsize ; j++)
if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) )
{
GRID[i][j] = true; // set up data structure, whether using Three.js or not
}
}
function initThreeWalls() // graphical run only, set up blank boxes, painted later
{
var t = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < gridsize ; j++)
if ( GRID[i][j] == true )
{
var shape = new THREE.BoxGeometry( squaresize, squaresize, squaresize );
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 0;
threeworld.scene.add(thecube);
WALLS[t] = thecube; // save it for later
t++;
}
}
function paintWalls ( material ) // paint blank boxes
{
for ( var i = 0; i < WALLS.length; i++ )
{
if ( WALLS[i] ) WALLS[i].material = material;
}
}
// --- ball functions -----------------------------------
function drawBall() // given ei, ej, draw it
{
var x = translate ( bi * squaresize );
var z = translate ( bj * squaresize );
var y = 0;
var dir = 0;
var velocity
ball.position.x = x;
ball.position.y = y;
ball.position.z = z;
ball.direction = dir
threeworld.scene.add(ball);
}
function occupied ( i, j ) // is this square occupied
{
if ( ( ai == i ) && ( aj == j ) ) return true;
if ( ( si == i ) && ( sj == j ) ) return true;
if ( GRID[i+2][j] === true ) return true;
if ( GRID[i-2][j] === true ) return true;// fixed object
return false;
}
var ballDeltaX = -1;
var ballDeltaY = 3;
var diameter = 3;
function processBallMovement() {
// --- the coordinates of the ball after it moves------
var nextBallLeft = bi + ballDeltaX;
var nextBallRight = bi + diameter + ballDeltaX;
var nextBallTop = bj + ballDeltaY;
var nextBallBottom = bj + diameter + ballDeltaY;
// --- what happens when the ball bounces of the top or bottom side-------------
if(nextBallTop < 1 || nextBallBottom > gridsize){
if (audio.duration > 0 && !audio.paused) { // takes care of the sound when two bounces happen in quick succession
audio.stop();
audio.play();
}
audio.play();
ballDeltaY *= -1; // changes the direction of the ball
bounces++;
}
// --- what happens when the ball bounces of the left side-------------
if (nextBallLeft < 1) {
if (audio.duration > 0 && !audio.paused) {
audio.stop();
audio.play();
}
audio.play();
ballDeltaX *= -1; // changes the direction of the ball
bounces++;
}
// --- what happens when the ball bounces of the right side-------------
if (nextBallRight > gridsize) {
if (audio.duration > 0 && !audio.paused) {
audio.stop();
audio.play();
}
audio.play();
ballDeltaX *= -1; // changes the direction of the ball
bounces++
}
// --- check if the ball hits players pad (controlled by user)
if(bj==27){
if(((bi != ai) && (bi != (ai+1)) && (bi != (ai+2)) && (bi != (ai-1)) && (bi != (ai-2)))){
error.play();
playerTwoScore++ // adds to platers score
}
}
// updates the location of the ball
bi+=ballDeltaX;
bj+=ballDeltaY;
}
function drawEnemyPaddle() // given ei, ej, draw it
{
var x = translate ( si * squaresize );
var z = translate ( sj * squaresize );
var y = 0;
enemyPaddle.position.x = x;
enemyPaddle.position.y = y;
enemyPaddle.position.z = z;
threeworld.scene.add(enemyPaddle);
}
function initLogicalEnemy()
{
si = 7; // this square will be free
sj = 1; // (bug) use Math.trunc or else you get a bad square number if gridsize is odd
}
function initLogicalBall()
{
bi = gridsize/2; // this square will be free
bj = gridsize/2; // (bug) use Math.trunc or else you get a bad square number if gridsize is odd
}
function initThreeBall()
{
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0xD43001});
// Create a ball with sphere geometry
ball = new THREE.Mesh(
new THREE.SphereGeometry(100,50,50), sphereMaterial);
drawBall();
}
function initThreeEnemy()
{
var shape = new THREE.BoxGeometry( 500, squaresize, squaresize );
enemyPaddle = new THREE.Mesh( shape );
enemyPaddle.material.color.setHex( BLANKCOLOR );
drawEnemyPaddle();
}
function moveLogicalEnemy()
{
// --- follows the location of the ball on the X axis
var i;
if ( si < bi ) i = randomintAtoB(si, si+2.5);
if ( si == bi ) i = si;
if ( si > bi ) i = randomintAtoB(si-2.5, si);
// --- check if the ball hits the oponents pad
if(bj==3){
if(((bi != si) && (bi != (si+1)) && (bi != (si+2)) && (bi != (si-1)) && (bi != (si-2)))){
error.play();
playerOneScore++ // adds to computers score
}
}
if ( ! occupied(i, sj+2))
{
si = i;
}
}
function drawMyPaddle() // given ai, aj, draw it
{
var x = translate ( ai * squaresize );
var z = translate ( aj * squaresize );
var y = 0;
myPaddle.position.x = x;
myPaddle.position.y = y;
myPaddle.position.z = z;
threeworld.scene.add(myPaddle);
}
function initLogicalAgent()
{
// start at same place every time:
ai = gridsize/2 - 5;
aj = 28;
}
function initThreeAgent()
{
var shape = new THREE.BoxGeometry( 500, squaresize, squaresize );
myPaddle = new THREE.Mesh( shape );
myPaddle.material.color.setHex( BLANKCOLOR );
drawMyPaddle();
}
function moveLogicalAgent( a ) // this is called by the infrastructure that gets action a from the Mind
{
var i = ai;
var j = aj;
if ( a == ACTION_LEFT ) i--;
else if ( a == ACTION_RIGHT ) i++;
if ( ! occupied(i,j) )
{
ai = i;
aj = j;
}
}
function keyHandler(e)
// user control
// Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
{
if (e.keyCode == 37) moveLogicalAgent ( ACTION_LEFT );
if (e.keyCode == 38) moveLogicalAgent ( ACTION_DOWN );
if (e.keyCode == 39) moveLogicalAgent ( ACTION_RIGHT );
if (e.keyCode == 40) moveLogicalAgent ( ACTION_UP );
}
// --- score: -----------------------------------
var bounces;
function updateStatus()
{
var score = self.getScore();
var status = "You: " + playerOneScore +
" Computer: " + playerTwoScore+" --------------------> Player with highest score wins";
$("#user_span1").html( status );
}
//--- public functions / interface / API ----------------------------------------------------------
// must have this public variable:
this.endCondition; // If set to true, run will end.
this.newRun = function()
{
// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)
this.endCondition = false;
step = 0;
// define logical data structure for the World, even if no graphical representation:
initGrid();
initLogicalWalls();
initLogicalAgent();
initLogicalEnemy();
initLogicalBall();
// if Three.js graphical representation:
if ( true )
{
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
// Set up blank objects first:
initSkybox();
initThreeWalls();
initThreeAgent();
initThreeEnemy();
initThreeBall();
// Then paint them with textures - asynchronous load of textures from files.
// The texture file loads return at some unknown future time in some unknown order.
// Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return.
// It is safe to paint objects in random order, but might not be safe to create objects in random order.
loadTextures(); // will return sometime later, but can go ahead and render now
document.onkeydown = keyHandler;
}
};
this.getState = function()
{
var x = [ ai, aj, si, sj, bi, bj ];
return ( x );
};
var win = new Audio('/uploads/jordanm/win.mp3');
var lose = new Audio('/uploads/jordanm/lose.mp3');
this.nextStep = function()
{
var a = 4;
moveLogicalEnemy();
processBallMovement();
if ( true )
{
drawMyPaddle();
drawEnemyPaddle();
drawBall();
updateStatus();
// ends the game when either computer or user reaches 10 points
if(playerOneScore === 5){
win.play();
this.endCondition = true;
}
if(playerTwoScore === 5){
lose.play();
this.endCondition = true;
}
}
};
this.endRun = function()
{
if ( true )
{
if(playerTwoScore>playerOneScore){
$("#user_span6").html( " <font color=red> <B> Computer has Won </B> </font>");
}else if((playerTwoScore<playerOneScore)){
$("#user_span6").html( " <font color=red> <B> Congratulations. You have Won </B> </font>" );
}else{
$("#user_span6").html( " <font color=red> <B> Its a draw </B> </font>" );
}
}
};
this.getScore = function()
{
return goodsteps;
};
}