Code viewer for World: Final Pool (clone by Musth...

// Cloned by Musthaq Ahmed on 22 Sep 2022 from World "Final Pool" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 1 Mar 2019 from World "v0.98 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 28 Feb 2019 from World "v0.97 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 27 Feb 2019 from World "v0.95 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 25 Feb 2019 from World "v0.94 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 25 Feb 2019 from World "v0.94 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 25 Feb 2019 from World "v0.92 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 20 Feb 2019 from World "v0.91 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 20 Feb 2019 from World "v0.9 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 20 Feb 2019 from World "v0.8 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 19 Feb 2019 from World "v0.7 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 18 Feb 2019 from World "v0.6 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 16 Feb 2019 from World "v0.5 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 14 Feb 2019 from World "v0.44 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 12 Feb 2019 from World "v0.43 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 5 Feb 2019 from World "v0.3 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 4 Feb 2019 from World "v0.2 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 


// Cloned by Ian Gilligan on 1 Feb 2019 from World "lillisl2 0.2 Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 


// Cloned by lillisl2 on 1 Feb 2019 from World "0.1 Pool Predictor Prototype" by Ian Gilligan 
// Please leave this clone trail here.
 
// Cloned by Ian Gilligan on 30 Jan 2019 from World "Pool Predictor Prototype" by lillisl2 
// Please leave this clone trail here.
 
// Cloned by lillisl2 on 28 Jan 2019 from World "Cloned Bouncy Balls" by Ian Gilligan 
// Please leave this clone trail here.
 
// Cloned by Ian Gilligan on 28 Jan 2019 from World "Bouncy Balls" by Starter user 
// Please leave this clone trail here.

// ==== Starter World ===============================================================================================
// (c) Ancient Brain Ltd. All rights reserved.
// This code is only for use on the Ancient Brain site.
// This code may be freely copied and edited by anyone on the Ancient Brain site.
// This code may not be copied, re-published or used on any other website.
// To include a run of this code on another website, see the "Embed code" links provided on the Ancient Brain site.
// ==================================================================================================================

// Physijs based World
// Bouncy balls 

// ===================================================================================================================
// === Start of tweaker's box ======================================================================================== 
// ===================================================================================================================

// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?
AB.maxSteps                 = 100000; 
AB.screenshotStep           = 50;   
AB.clockTick                = 50;
  	// Take screenshot on this step. (All resources should have finished loading.) Default 50.


// ===================================================================================================================
// === Variables & Constants =========================================================================================
// ===================================================================================================================

const OBJPATH = "/uploads/gilligi2/";	                // path of OBJ and MTL 
const OBJNAME = "pooltable.obj";
const MTLNAME = "pooltable.mtl";
const SOUND_COLLISION = "/uploads/gilligi2/pool_sound.mp3";
const SOUND_GOOD = "/uploads/gilligi2/good_shot_sound.mp3";
const SOUND_BAD = "/uploads/gilligi2/bad_shot_sound.mp3";
const SOUND_CLICK = "/uploads/gilligi2/button_click_sound.mp3";

const MODELLENGTH 			= 11054;							
const MODELWIDTH			= 6279; 
const SCALE                 = 0.009;

const BALLSIZE              = 2;                        // size of each ball
const BALLPOS               = BALLSIZE * 100; 		    // x,z start position of balls is random in this interval
const BALLHEIGHT            = BALLSIZE * 2; 		    // y start position of balls 
const HEAVYIMPACT           = 1;     			        // heavy impacts have y velocity greater than this 

const GROUNDSIZE            = 150;                      // size of table
const startRadius           = MODELWIDTH * 0.02;        // camera start distance
const maxRadius             = MODELLENGTH * 0.03;       // camera max distance
	
const SKYCOLOR              = 0xccddff;                 //colour of sky / background (white)

// friction and restitution between 0 and 1: 
const GROUND_FRICTION 		= 1;                     // friction of ground
const GROUND_RESTITUTION 	= 0.40;                      // restitution of ground
const WALL_FRICTION 		= 0.7;                     // friction of wall
const WALL_RESTITUTION  	= 0.9;                      // restitution of wall
const BALL_FRICTION 		= 1;                     // friction of ball
const BALL_RESTITUTION 		= 1;                      // restitution of ball

var   LIMIT                 = [7,7,1,1];                // limit values of each type of balls [yellow, red, black, white]
var   BALL_CHOICE           = 0;                        // the ball that the user has currently selected
const BALL_TYPE             = [                         // what each value of ball choice (0-3) represent
                            "Yellow",
                            "Red", 
                            "Black",
                            "White"];
                            
// define gravity along x,y,z dimensions:
const gravity               = new THREE.Vector3 (0, -150, 0);
const BALL_MASS             = 1;                       // mass of ball

const BASE_POWER            = 500;
var   CURRENT_POWER         = 0;
var   MIN_POWER             = 1;
var   MAX_POWER             = 5;
var   SAVED_POWER           = 0;

var   YOURCOLOR             = "None";

var   CALCULATED            = false;
var   CALCULATING           = false;

// == Balls Placed ==
var   BALL_COUNT            = 0;
var   BALLS                 = [];
var   WHITE                 = -1;

// == Ball Colors ==
const BALL_COLORS           = {
                            0 : "Yellow",
                            1 : "Red",
                            2 : "Black",
                            3 : "White"};
const BALL_COLORS_R         = {
                            "Yellow" : 0,
                            "Red"    : 1,
                            "Black"  : 2,
                            "White"  : 3};

// == Ball Movement ==
var   OBSTRUCTION           = false;
var   isSpace               = true;
var   movFin                = false;
var   MOVING                = false;
var   last_velocity         = 0;
const SLOWFACTOR            = 0.1;

// == Score Calculation ==
var   POCKETED              = false;
var   WHITE_POT             = false;
var   BLACK_POT             = false;
var   firstHit              = true;
var   YOURSCORE             = 0;
var   OTHERSCORE            = 0;
var   FOUL                  = false;
var   setUpScene;
var   SCORE                 = 50;
var   ANGLE;

// == Shot Choice ==
var   BALLS_TO_POT          = 0;
var   BALLS_ATTEMPTED       = 0;
var   SHOT_PICKED           = 0;

// == Shot Angle ==
var   ANGLE_COUNT           = 0;
var   ANGLES                = [
                            [-0.04, -0.04],
                            [-0.04, 0.04],
                            [0.04, -0.04],
                            [0.04, 0.04]];

// == Camera ==
ABWorld.drawCameraControls  = false; 
var CAMERA              = "On";
var cameraPocketCounter = 0;
var CONTROLLER          = false;
// == UI ==
var   PAGE_SELECT = 1;
var   CURRENT_PAGE = 0;
const line4_1 = ("<p align=\"center\"><button id='yellowButton'>__</button><button id='redButton'>__</button> <button id='blackButton'>__</button> <button id='whiteButton'>__</button></p>");
const line4_3 = ("<p align=\"center\"><button id='gameButton'> Game Controls</button> <button id='cameraButton'>Camera Controls</button></p>");
const line6_1 = ("<p align=\"center\"><button id='yourNButton'>__</button> <button id='yourYButton'>__</button> <button id='yourRButton'>__</button></p>");
const line6_2 = ("<p align=\"center\"><button id='decreasePowerMinButton'>- Min</button> <button id='increasePowerMinButton'>+ Min</button> <button id='decreasePowerMaxButton'>- Max</button> <button id='increasePowerMaxButton'>+ Max</button></p> ");
const line6_3 = ("<p align=\"center\"><button id='cameraFlatButton'>Flat Down View</button> <button id='cameraCornerButton'>Pocket View</button></p> ");

// == Save ==
BALL_LAYOUT = [];
BEST_SCORES = [];

var toggleBasic = false;
var toggledBasic = false;

// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================

// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.

// ===================================================================================================================
// === Inits & Loads =================================================================================================
// ===================================================================================================================


var resourcesLoaded = false;
var splashClicked = false;

function World() 
{ 
	var ground_texture;
	var self = this;
	var GROUND;
	var table;
	var p_height = 25;
	
	// --- Cue -----------------------------------------------------------------------  
	var cue_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({color : "#c96120"}));
    var cue = new THREE.Mesh(new THREE.CylinderGeometry(0.5,1.5,100, 100), cue_material , 0);
    cue.position.x = -51;
    cue.position.y = 4;
    cue.rotation.z = (Math.PI / 2) * 3 - 0.05;
    var pivot = new THREE.Mesh(new THREE.SphereGeometry(0.001, 10, 10), cue_material, 0);
    pivot.add(cue);

	// --- Ball ----------------------------------------------------------------------
	var ball_material_0 = Physijs.createMaterial(new THREE.MeshLambertMaterial({color : "yellow"}), BALL_FRICTION, BALL_RESTITUTION);
	var ball_material_1 = Physijs.createMaterial(new THREE.MeshLambertMaterial({color : "red"}), BALL_FRICTION, BALL_RESTITUTION);
	var ball_material_2 = Physijs.createMaterial(new THREE.MeshLambertMaterial({color : "black"}), BALL_FRICTION, BALL_RESTITUTION);
	var ball_material_3 = Physijs.createMaterial(new THREE.MeshLambertMaterial({color : "white"}), BALL_FRICTION, BALL_RESTITUTION);
	var ball_geometry = new THREE.SphereGeometry(BALLSIZE, 100, 100);
	
	// --- Space ---------------------------------------------------------------------
	var space_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({color : "#c96120"}));
	
function loadResources()		// asynchronous file loads - call initScene() when all finished 
{
    var m = new THREE.MTLLoader();
	m.setTexturePath (OBJPATH);
	m.setPath        (OBJPATH);
	
	m.load ( MTLNAME, function ( materials ) 
	{
		materials.preload(); 
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		
		o.load ( OBJNAME, function ( object ) 
		{
			table = object;
			if ( asynchFinished() )	initScene();		 
		});
	});}

function asynchFinished()		// all file loads returned 
{
	if (! table){
	    return false;}
	return true;}
	
function initScene()			// all file loads have returned 
{
	// --- Light ------------------------------------------------------------------
	var light = new THREE.DirectionalLight(0xFFFFFF, 0.4);
	// close to origin, high up, works best for shadows
	light.position.set(0, 40, 0);
	light.target.position.copy(ABWorld.scene.position);
	light.castShadow = true;
	// how far away to draw shadows:
	light.shadow.camera.left    = -20;
	light.shadow.camera.right   =  20;
	light.shadow.camera.bottom  = -20;
	light.shadow.camera.top     =  20;
    // higher quality shadows at expense of computation time:
	light.shadow.mapSize.width  = 2048;
	light.shadow.mapSize.height = 2048;
	light.shadow.bias           = -0.1;
	ABWorld.scene.add(light);
	var light1 = new THREE.DirectionalLight(0xFFFFFF, 0.4);
	var light2 = new THREE.DirectionalLight(0xFFFFFF, 0.4);
	var light3 = new THREE.DirectionalLight(0xFFFFFF, 0.4);
	var light4 = new THREE.DirectionalLight(0xFFFFFF, 0.4);
	light1.position.set(100, 30, 0);
	light2.position.set(-100, 30, 0);
	light3.position.set(0, 30, 100);
	light4.position.set(0, 30, -100);
	ABWorld.scene.add(light1);
	ABWorld.scene.add(light2);
	ABWorld.scene.add(light3);
	ABWorld.scene.add(light4);

	// --- Table -------------------------------------------------------------------
	table.position.y = 0;
    table.position.x = -15;
    table.position.z = 0; 
    ABWorld.scene.add(table);

	// --- Ground ------------------------------------------------------------------
	var ground_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({wireframe : true, color : "#444444"}), GROUND_FRICTION, GROUND_RESTITUTION);
	// ground as plane allows bounces but seems to be infinite plane
	var ground = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.93, 1, MODELWIDTH * SCALE * 0.68), ground_material, 0);
	ground.position.y = p_height;
	ground.collisions = 0;
	ground.receiveShadow = true;
	GROUND = ground;
	
	// --- Floor -------------------------------------------------------------------
    var floor_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({color: "#a0192b"}), 0.1, 0.1);
	var floor = new Physijs.PlaneMesh(new THREE.PlaneGeometry(1000, 1000), floor_material);
    floor.rotation.x = (Math.PI / 2) * 3;
    floor.receiveShadow = true;
    floor.addEventListener('collision', function(other_object, relative_velocity, relative_rotation, contact_normal) 
	{
	    POCKETED = false;
	    FOUL = true;
        removeBall(other_object);
	});
	ABWorld.scene.add(floor);

	// --- Wall --------------------------------------------------------------------
	var wall_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({wireframe : true}), WALL_FRICTION, WALL_RESTITUTION);
	var wall1 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 1.03, 5.9, MODELWIDTH * SCALE * 0.055), wall_material, 0);
	wall1.position.z = 27.5;
    wall1.collisions = 0;
	wall1.receiveShadow = true;
	var wall2 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 1.03, 5.9, MODELWIDTH * SCALE * 0.055), wall_material, 0);
	wall2.position.z = -27.7;
    wall2.collisions = 0;
	wall2.receiveShadow = true;
	var wall3 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.02, 5.9, MODELWIDTH * SCALE * 0.92), wall_material, 0);
	wall3.position.x = 52.2;
	wall3.collisions = 0;
	wall3.receiveShadow = true;
	var wall4 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.02, 5.9, MODELWIDTH * SCALE * 0.92), wall_material, 0);
	wall4.position.x = -52.2;
    wall4.collisions = 0;
	wall4.receiveShadow = true;
    wall1.visible = false;
    wall2.visible = false;
    wall3.visible = false;
    wall4.visible = false;

    // --- Bumper --------------------------------------------------------------------
    var bumper1 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.33, 5.9, MODELWIDTH * SCALE * 0.075 * 1.65), wall_material, 0);
	bumper1.position.z = 23.5;
	bumper1.position.x = 23.5;
	bumper1.position.y = -0.1;
	bumper1.collisions = 0;
	bumper1.receiveShadow = true;
    var bumper2 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.33, 5.9, MODELWIDTH * SCALE * 0.075 * 1.65), wall_material, 0);
	bumper2.position.z = 23.5;
	bumper2.position.x = -23;
	bumper2.collisions = 0;
	bumper2.receiveShadow = true;
	var bumper3 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.33, 5.9, MODELWIDTH * SCALE * 0.075 * 1.58), wall_material, 0);
	bumper3.position.z = -23.5;
	bumper3.position.x = 23.5;
	bumper3.position.y = -0.1;
	bumper3.collisions = 0;
	bumper3.receiveShadow = true;
	var bumper4 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.33, 5.9, MODELWIDTH * SCALE * 0.075 * 1.58), wall_material, 0);
	bumper4.position.z = -23.5;
	bumper4.position.x = -23;
	bumper4.collisions = 0;
	bumper4.receiveShadow = true;
	var bumper5 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.047, 5.9, MODELWIDTH * SCALE * 0.56), wall_material, 0);
	bumper5.position.z = 0;
	bumper5.position.x = 49;
	bumper5.receiveShadow = true;
	var bumper6 = new Physijs.BoxMesh(new THREE.BoxGeometry(MODELLENGTH * SCALE * 0.047, 5.9, MODELWIDTH * SCALE * 0.56), wall_material, 0);
	bumper6.position.z = 0;
	bumper6.position.x = -49;
	bumper6.receiveShadow = true;
    bumper1.visible = false;
    bumper2.visible = false;
    bumper3.visible = false;
    bumper4.visible = false;
    bumper5.visible = false;
    bumper6.visible = false;
	
	ground.add(wall1);
	ground.add(wall2);
	ground.add(wall3);
	ground.add(wall4);
	ground.add(bumper1);
	ground.add(bumper2);
	ground.add(bumper3);
	ground.add(bumper4);
	ground.add(bumper5);
	ground.add(bumper6);

    // --- Bumper Edge ---------------------------------------------------------------
    var bumperedge1 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge1.position.z = 23;
	bumperedge1.position.x = 7.8;
	bumperedge1.receiveShadow = true;
    var bumperedge2 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge2.position.z = 23;
	bumperedge2.position.x = -7.5;
	bumperedge2.receiveShadow = true;
    var bumperedge3 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge3.position.z = -23.1;
	bumperedge3.position.x = 7.8;
	bumperedge3.receiveShadow = true;
    var bumperedge4 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge4.position.z = -23.1;
	bumperedge4.position.x = -7.5;
	bumperedge4.receiveShadow = true;
	var bumperedge5 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge5.position.z = 23;
	bumperedge5.position.x = 39.1;
	bumperedge5.receiveShadow = true;
    var bumperedge6 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge6.position.z = 23;
	bumperedge6.position.x = -39;
	bumperedge6.receiveShadow = true;
    var bumperedge7 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge7.position.z = -23.1;
	bumperedge7.position.x = 39.1;
	bumperedge7.receiveShadow = true;
    var bumperedge8 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge8.position.z = -23.1;
	bumperedge8.position.x = -39;
	bumperedge8.receiveShadow = true;
    var bumperedge9 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge9.position.z = 14.2;
	bumperedge9.position.x = 49.6;
	bumperedge9.receiveShadow = true;
    var bumperedge10 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge10.position.z = 14.2;
	bumperedge10.position.x = -49.6;
	bumperedge10.receiveShadow = true;
    var bumperedge11 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge11.position.z = -14.2;
	bumperedge11.position.x = 49.6;
	bumperedge11.receiveShadow = true;
    var bumperedge12 = new Physijs.CylinderMesh( new THREE.CylinderGeometry( 3, 2.9, 5.7, 100 ), wall_material );
    bumperedge12.position.z = -14.2;
	bumperedge12.position.x = -49.6;
	bumperedge12.receiveShadow = true;   
    bumperedge1.visible = false;
    bumperedge2.visible = false;
    bumperedge3.visible = false;
    bumperedge4.visible = false;
    bumperedge5.visible = false;
    bumperedge6.visible = false;
    bumperedge7.visible = false;
    bumperedge8.visible = false;
    bumperedge9.visible = false;
    bumperedge10.visible = false;
    bumperedge11.visible = false;
    bumperedge12.visible = false;
	
    ground.add(bumperedge1);
    ground.add(bumperedge2);
    ground.add(bumperedge3);
    ground.add(bumperedge4);
    ground.add(bumperedge5);
    ground.add(bumperedge6);
    ground.add(bumperedge7);
    ground.add(bumperedge8);
    ground.add(bumperedge9);
    ground.add(bumperedge10);
    ground.add(bumperedge11);
    ground.add(bumperedge12);
    
    ABWorld.scene.add(ground);

    // --- Pocket --------------------------------------------------------------------
	var pocket_material = Physijs.createMaterial(new THREE.MeshStandardMaterial());
    var pocket = new Physijs.SphereMesh(new THREE.SphereGeometry(1), pocket_material, 0);
    pocket.position.y = -4;
    var pocket1 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket1.position.y = p_height;
    pocket1.position.z = 21.5;
    pocket1.position.x = 47;
    var pocket2 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket2.position.y = p_height;
    pocket2.position.z = -21.5;
    pocket2.position.x = 47;
    var pocket3 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket3.position.y = p_height;
    pocket3.position.z = 24;
    var pocket4 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket4.position.y = p_height;
    pocket4.position.z = 21.5;
    pocket4.position.x = -47;
    var pocket5 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket5.position.y = p_height;
    pocket5.position.z = -21.5;
    pocket5.position.x = -47;
    var pocket6 = new Physijs.SphereMesh(new THREE.SphereGeometry(4, 15, 15), pocket_material, 0);
    pocket6.position.y = p_height;
    pocket6.position.z = -24;
    pocket.add(pocket1);
    pocket.add(pocket2);
    pocket.add(pocket3);
    pocket.add(pocket4);
    pocket.add(pocket5);
    pocket.add(pocket6);
    pocket.addEventListener('collision', function(other_object, relative_velocity, relative_rotation, contact_normal) 
	{
	    POCKETED = true;
        removeBall(other_object);
	});
	pocket.visible = false;
    ABWorld.scene.add(pocket);
	ABWorld.render();
	console.log("Resources loaded.");
	resourcesLoaded = true;}

// ===================================================================================================================
// === Button Functions ==============================================================================================
// ===================================================================================================================

function decreasePowerMin(){
    if (MIN_POWER > 1) {MIN_POWER -= 1;}
}

function increasePowerMin(){
    if (MIN_POWER < MAX_POWER) {MIN_POWER += 1;}
}

function decreasePowerMax(){
    if (MIN_POWER < MAX_POWER) {MAX_POWER -= 1;}
}
    
function increasePowerMax(){
    if (MAX_POWER < 5) {MAX_POWER += 1;}
}

function selectBall(x)
{
    BALL_CHOICE = x;
}

function setColor(x){
    YOURCOLOR = x;
}

function pageChange(x){
    PAGE_SELECT = x;
}

function cameraFlatDown(){
    ABWorld.camera.position.set(0,100,0);
}

function cameraSomeCornerPocket(){

    if (cameraPocketCounter % 4 === 0){
        ABWorld.camera.position.set(50,50,30);
    }
    else if (cameraPocketCounter % 4 == 1){
        ABWorld.camera.position.set(50,50,-30);
    }
    else if(cameraPocketCounter % 4 == 2){
        ABWorld.camera.position.set(-50,50,-30);
    }
    else if(cameraPocketCounter % 4 == 3){
       ABWorld.camera.position.set(-50,50, 30); 
    }
    cameraPocketCounter = cameraPocketCounter + 1;
}

// ===================================================================================================================
// === Shot Calculations =============================================================================================
// ===================================================================================================================


function calculateShot()
{
    if(validSetUp() === true){
        CALCULATING = true;
        initOptions();
        prepareHit();
    }
}

function prepareHit()
{
    if (YOURCOLOR == "None"){
        BALLS_TO_POT = 14;
    }
    else if (YOURCOLOR != "None"){
        BALLS_TO_POT = colorCount(YOURCOLOR);
        if (BALLS_TO_POT === 0){
            BALLS_TO_POT = 1;
        }
    }
    BALLS_ATTEMPTED = 0;
    CURRENT_POWER = MIN_POWER;
    if (toggledBasic === false){
        for (var i in BALLS){
            var x = BALLS[i].position.x;
            var z = BALLS[i].position.z;
            var name = BALLS[i].name;
            BALL_LAYOUT.push([x, p_height + 0.5, z, BALL_COLORS_R[name]]);
        }
    }
    hitBall();

}

function hitBall()
{
    movFin = false;
    MOVING = true;
    ABWorld.scene.addEventListener("update", function(){
        if (movementFinished()){
            if (CALCULATED === false){
                SCORE = calculateScore(YOURSCORE, OTHERSCORE, WHITE_POT, BLACK_POT);
                //console.log("Score for this turn: " + SCORE + ".");
            }
            clearTable();
        }
    });
    firstHit = true;
    if (CALCULATED === false){
        ANGLE = ballChoice();
    }
    cue = makeCue(ANGLE);
    WHITE.applyCentralImpulse(ANGLE);
    removeCue(cue);
}

function clearTable()
{
    while (BALLS.length !== 0){
        LIMIT[BALL_COLORS_R[BALLS[0].name]] += 1;
        BALLS[0].name = "None";
        BALLS[0].geometry.dispose();
        BALLS[0].material.dispose();
        ABWorld.scene.remove(BALLS[0]);
        BALLS.splice(0, 1);
    }
    reset();
}

function reset()
{
    BALL_COUNT = 0;
    WHITE_POT = false;
    BLACK_POT = false;
    YOURSCORE = 0;
    OTHERSCORE = 0;
    FOUL = false;
    if (toggledBasic === true){
        YOURCOLOR = "None";
        basicSetUp();
    }
    else{
        for (var i in BALL_LAYOUT){
            createBall(BALL_LAYOUT[i][0], BALL_LAYOUT[i][1], BALL_LAYOUT[i][2], BALL_LAYOUT[i][3]);
        }
        MOVING = false;
        $("#simulate").css({"background-color" : "red" , "color" : "white", "padding" : "15px", "font-size" : "25px", "border-radius" : "10px", "font-weight" : "bold"});
        if (CALCULATING === true && BALLS_TO_POT > BALLS_ATTEMPTED){
            hitBall();
        }
        else if (CALCULATING === true && BALLS_TO_POT == BALLS_ATTEMPTED){
            CALCULATING = false;
            CALCULATED = true;
            //console.log("Finished Calculations");
            //console.log("Best Scores: ", BEST_SCORES);
            initPostOptions();
        }
    }
}

function calculateScore(x, y, w, b)
{
    var score = 50;
    if ((FOUL === true || w === true) && y === 0){
        score += (-8) + (x * 7);
    }
    else{
        score += (-9 * y) + (x * 7);
    }
    if (b === true && (w === true || YOURCOLOR == "None" || LIMIT[BALL_COLORS_R[YOURCOLOR]] != 7)){
        score = -1;
    }
    else if (b === true){
        score = 100;
    }
    if (BEST_SCORES.length < 3){
        BEST_SCORES.push([score, ANGLE, SAVED_POWER]);
    }
    else{
        chooseBest(score, ANGLE, SAVED_POWER);
    }
    return(score);
}

function chooseBest(s, a, p)
{
    var min = 0;
    for (var i in BEST_SCORES){
        if (BEST_SCORES[i][0] < BEST_SCORES[min][0]){
            min = i;
        }
    }
    if (BEST_SCORES[min][0] < s){
        BEST_SCORES[min] = [s, a, p];
        soundGood();
    }
    else{soundBad()}
}

function validSetUp()
{
    if (LIMIT[2] + LIMIT[3] > 0){
        errorSplash("Invalid Set Up! Black or white ball missing.");
        return(false);
    }
    else if (YOURCOLOR == "None" && LIMIT[0] + LIMIT[1] + LIMIT[2] + LIMIT[3] > 0){
        errorSplash("Invalid Set Up! Incorrect setting of your color, should be Yellow or Red.");
        return(false);
    }
    else if ((YOURCOLOR == "Red" || YOURCOLOR == "Yellow") && LIMIT[0] + LIMIT[1] === 0){
        errorSplash("Invalid Set Up! Incorrect setting of your color, should be None.");
        return(false);
    }
    else{
        return(true);
    }
}

function colorCount(color)
{
    var x = 0;
    for (var i in BALLS){
        if (BALLS[i].name == color){
            x += 1;
        }
    }
    return(x);
}

function makeCue(angle)
{
    firstHit = true;
    if (WHITE != -1){
        pivot.position.x = WHITE.position.x;
        pivot.position.y = WHITE.position.y;
        pivot.position.z = WHITE.position.z;
        var rotate = -(Math.atan((angle.z - WHITE.position.z)/(angle.x - WHITE.position.x)));
        if((angle.x - WHITE.position.x) < 0){
            if ((angle.z - WHITE.position.z) < 0){
                rotate -= Math.PI;
            }
            else if ((angle.z - WHITE.position.z) > 0){
                rotate += Math.PI;
            }
            else {rotate = Math.PI}
        }
        pivot.rotation.y = rotate;
        ABWorld.scene.add(pivot);
        return pivot;
    }
}

function waiting(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function removeCue(object){
        await waiting(2000);
        object.geometry.dispose();
        object.material.dispose();
        ABWorld.scene.remove(object);    
}


function isValid(ball){
    if (YOURCOLOR == "None"){
        if (ball.name == "Yellow" || ball.name == "Red"){
            //console.log("Valid Selection");
            return(true);
        }
    }
    else if (ball.name == YOURCOLOR){
        //console.log("Valid Selection");
        return(true);
    }
    else if (ball.name == "Black" && LIMIT[BALL_COLORS_R[YOURCOLOR]] == 7){
        return(true);
    }
    return(false);
}

function errorSplash(msg){
    AB.newSplash ("<p align=\"center\">Error!</p><p align=\"center\"> "+ msg +"</p>");
    $("#splashbutton").click (function()        
        {
                AB.removeSplash();                      // remove splash screen 
                soundClick();
                splashClicked = true;
        });
}

function ballChoice()
{
    // choose a valid ball to calculate angle
    var valid_balls = 0;
    for (var i in BALLS){
        if (isValid(BALLS[i])){
            if (valid_balls == BALLS_ATTEMPTED){
                var angle = pickAngle(BALLS[i]);
                if (ANGLE_COUNT < 3){
                    ANGLE_COUNT += 1;
                }
                else if (ANGLE_COUNT == 3){
                    CURRENT_POWER += 1;
                    ANGLE_COUNT = 0;
                }
                if (CURRENT_POWER > MAX_POWER){
                    BALLS_ATTEMPTED += 1;
                    CURRENT_POWER = MIN_POWER;
                }

                return(angle);
            }
            valid_balls += 1;
        }
    }
}

function pickAngle(ball)
{
    // calculate angle to hit ball
    var length = (Math.sqrt(Math.pow((WHITE.position.x - ball.position.x), 2) + Math.pow((WHITE.position.z - ball.position.z), 2)));
    SAVED_POWER = CURRENT_POWER;
    angle = (new THREE.Vector3 ((BASE_POWER * CURRENT_POWER * (((ball.position.x - WHITE.position.x) / length) + ANGLES[ANGLE_COUNT][0])), 0, (BASE_POWER * CURRENT_POWER * (((ball.position.z - WHITE.position.z) / length) + ANGLES[ANGLE_COUNT][1]))));
    return(angle);
    
}

function movementFinished()
{
    if (firstHit === false && movFin === false){
        var vel = 0;
        for (var x in BALLS){
            vel += BALLS[x].getAngularVelocity().length();
        }
        if (vel < 0.2){
            //console.log("Movement Finished!");
            movFin = true;
            return(true);
        }
        return(false);
    }
}

function simulateShot()
{
    if (MOVING === false){
        ANGLE = BEST_SCORES[SHOT_PICKED][1];
        hitBall();
    }
    else {errorSplash("Wait for current simulation to end.");}
}

// ===================================================================================================================
// === Ball Creation =================================================================================================
// ===================================================================================================================


function basicSetUp()
{
    if (BALL_COUNT === 0){
        createBall(-30, p_height + 0.5, 0, 3); //white
        createBall(30, p_height + 0.5, 0, 2); //black
        createBall(23, p_height + 0.5, 0, 1); //red
        createBall(26.5, p_height + 0.5, -2, 1); //red
        createBall(30, p_height + 0.5, 4, 1); //red
        createBall(33.5, p_height + 0.5, 2, 1); //red
        createBall(33.5, p_height + 0.5, -6, 1); // red
        createBall(37, p_height + 0.5, -4, 1); //red
        createBall(37, p_height + 0.5, 8, 1); //red
        createBall(26.5, p_height + 0.5, 2, 0); //yellow
        createBall(30, p_height + 0.5, -4, 0); //yellow
        createBall(33.5, p_height + 0.5, -2, 0); //yellow
        createBall(33.5, p_height + 0.5, 6, 0); //yellow
        createBall(37, p_height + 0.5, 0, 0); //yellow
        createBall(37, p_height + 0.5, 4, 0); //yellow
        createBall(37, p_height + 0.5, -8, 0); //yellow
        MOVING = false;
        
    }
    if (CALCULATING === true && BALLS_TO_POT > BALLS_ATTEMPTED){
        hitBall();
    }
    else if (CALCULATING === true && BALLS_TO_POT == BALLS_ATTEMPTED){
            CALCULATING = false;
            CALCULATED = true;
            console.log("Finished Calculations");
            console.log("Best Scores: ", BEST_SCORES);
            initPostOptions();

    }
}

function createBall(x, y, z, CURRENT_COLOR) 	 
{
	
 if ( resourcesLoaded && LIMIT[CURRENT_COLOR] !== 0 && splashClicked)		  
 {  
    var ball;
    // choose ball color
    if (CURRENT_COLOR === 0){
        ball = new Physijs.SphereMesh(ball_geometry, ball_material_0, BALL_MASS);
    }
    else if (CURRENT_COLOR == 1){
        ball = new Physijs.SphereMesh(ball_geometry, ball_material_1, BALL_MASS);
    }
    else if (CURRENT_COLOR == 2){
        ball = new Physijs.SphereMesh(ball_geometry, ball_material_2, BALL_MASS);
    }
    else{
        ball = new Physijs.SphereMesh(ball_geometry, ball_material_3, BALL_MASS);
    }
    ball.name = BALL_COLORS[CURRENT_COLOR];
	ball.collisions = 0;
	ball.castShadow = true;
	//ball.position.set(1, 40, 1);
	ball.position.set(x, y + 2, z);
	ball.addEventListener('collision', function(other_object, relative_velocity, relative_rotation, contact_normal) 
	{
	    if (other_object != GROUND){
	    	var mainImpact = relative_velocity.y;		// impact in direction of gravity
 		    if (Math.abs(mainImpact) > HEAVYIMPACT)			// main impact, not lesser ones as it settles  
		    {
			    soundCollision();
		    }
	    }
	    if (ball == WHITE && other_object != GROUND){

	        if (firstHit === true){
	            if(other_object.name == YOURCOLOR || YOURCOLOR == "None"){
	                //console.log("Valid Hit");
	            }
	            else if (LIMIT[BALL_COLORS_R[YOURCOLOR]] == 7 && other_object.name == "Black"){
	                //console.log("Valid Hit");
	            }
	            else{
	                console.log("Invalid Hit");
	                FOUL = true;
	            }
	            firstHit = false;

	       }
        }

	});
	BALLS.push(ball);
	if (CURRENT_COLOR == 3){
	    WHITE = ball;
	}
	BALL_COUNT += 1;
	ABWorld.scene.add(ball);
	ball.setDamping(0.5, 0.5);
	LIMIT[CURRENT_COLOR] -= 1;
 }
}

function removeBall(ball)
{
    var index = BALLS.indexOf(ball);
    if (index > -1) {
        BALLS.splice(index, 1);
    }
    if (POCKETED === true){
        if (ball.name == YOURCOLOR){
            console.log("Good Shot!");
            YOURSCORE += 1;
        }
        else if (YOURCOLOR == "None" && (ball.name !== "Black" && ball.name !== "White")){
            console.log("You are now " + ball.name + "s.");
            YOURCOLOR = ball.name;
            YOURSCORE += 1;
        }
        else if (ball.name == "Black"){
            BLACK_POT = true;
        }
        else if (ball.name == "White"){
            WHITE_POT = true;
            console.log("Foul Shot.");
        }
        else{
            console.log("Bad Shot :(");
            OTHERSCORE += 1;
        }
    }
    LIMIT[BALL_COLORS_R[ball.name]] += 1;
    ball.name = "None";
    ball.geometry.dispose();
    ball.material.dispose();
    ABWorld.scene.remove(ball);

}

// ===================================================================================================================
// === Keyboard & Mouse Control ======================================================================================
// ===================================================================================================================


var OURKEYS = [49, 50, 51, 52];
function ourKeys(event) {return(OURKEYS.includes(event.keyCode));}
function keyHandler(event)           
{
    if      (! ABRun.runReady)          {return true;}            // not ready yet 
    // if not handling this key, send it to default: 
    if      (! ourKeys(event))          {return true;}
    // else handle it and prevent default:
    if      ( event.keyCode == 49 )     {selectBall(0);}
    else if ( event.keyCode == 50 )     {selectBall(1);} 
    else if ( event.keyCode == 51 )     {selectBall(2);}
    else if ( event.keyCode == 52 )     {selectBall(3);} 
    event.stopPropagation(); event.preventDefault(); return false;
}

var startX, startY; 
var dragevents;         // number of events in the current drag 

function click(x, y)
{
    var object = (ABWorld.hitsObjectPoint(x, y, GROUND));  // returns null if no hit
    if (object !== null){
        createBall(object.x, object.y, object.z, BALL_CHOICE);}
}

//for future drag functionality
function initTouch(x, y)
{
    var object = (ABWorld.hitsObjectPoint(x, y, GROUND));  // returns null if no hit
    if (object !== null){
        createBall(object.x, object.y, object.z, BALL_CHOICE);}
}

function dummy(){
}

// ===================================================================================================================
// === User Interface ================================================================================================
// ===================================================================================================================


function initPreOptions()
{
    $("#user_span1").html("<p align=\"center\">Page: <b>"+ PAGE_SELECT + "</b></p>");
    $("#user_span2").html("<p align=\"center\"><button id='page1'>Page 1</button> <button id='page2'>Page 2</button></p>");
    $("#user_span4").html(line4_1);
    $("#user_span6").html(line6_1);
    $("#user_span7").html("<p align=\"center\">The Camera Settings :</p>");
    $("#user_span8").html(line6_3);
    $("#user_span10").html("<p align=\"center\"><button id='calculateShot'>GO!</button></p>");


    $(document).ready(function(){
        //Button Functionality
        $("#page1").click(function(){pageChange(1), soundClick();});
        $("#page2").click(function(){pageChange(2), soundClick();});
        $("#calculateShot").click(function(){calculateShot(),ABHandler.defaultTouch(), ABHandler.defaultMouse(), soundClick();});
        $("#cameraFlatButton").click(function(){cameraFlatDown(), soundClick();});
        $("#cameraCornerButton").click(function(){cameraSomeCornerPocket(), soundClick();});
        //Button CSS
        $("#page1").css({"background-color" : "#99ff33" , "border-radius" : "10px" , "padding" : "5px"});
        $("#page2").css({"background-color" : "#99ff33" , "border-radius" : "10px" , "padding" : "5px"});
        $("#calculateShot").css({"background-color" : "red" , "color" : "white", "padding" : "15px", "font-size" : "25px", "border-radius" : "10px", "font-weight" : "bold"});
        $("#cameraFlatButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
        $("#cameraCornerButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
    });

}

function initOptions()
{
    $("#user_span1").html("<p align=\"center\"><b>Calculating Table ...</b></p>");
    $("#user_span4").html("<p align=\"center\">Camera</p>");
    $("#user_span5").html(line6_3);
    $("#user_span7").html("<p></p>");
    $("#user_span8").html("<p></p>");
    $("#user_span10").html("<p></p>");
    
    $(document).ready(function(){
        //Button Functionality
        $("#cameraFlatButton").click(function(){cameraFlatDown(), soundClick();});
        $("#cameraCornerButton").click(function(){cameraSomeCornerPocket(), soundClick();});
        //Button CSS
        $("#cameraFlatButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
        $("#cameraCornerButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
    }); 
    
 
}

function initPostOptions()
{
    $("#user_span1").html("<p align=\"center\"><b>Finished Calculation</b></p>");
    $("#user_span5").html("<p align=\"center\"><button id='show1'>1</button> <button id='show2'>2</button> <button id='show3'>3</button></p>");
    $("#user_span6").html("<p align=\"center\"><button id='simulate'>Simulate Shot</button></p>");
    $("#user_span7").html("<p align=\"center\">Camera</p>");
    $("#user_span8").html(line6_3);
    $("#user_span9").html("<p></p>");
    $("#user_span10").html("<p></p>");
    
    $(document).ready(function(){
        //Button Functionality
        $("#show1").click(function(){SHOT_PICKED = 0, soundClick();});
        $("#show2").click(function(){SHOT_PICKED = 1, soundClick();});
        $("#show3").click(function(){SHOT_PICKED = 2, soundClick();});
        $("#simulate").click(function(){simulateShot(), soundClick(), $("#simulate").css({"background-color" : "#333333" , "color" : "#aaaaaa", "padding" : "15px", "font-size" : "25px", "border-radius" : "10px", "font-weight" : "bold"});});
        $("#cameraFlatButton").click(function(){cameraFlatDown(), soundClick();});
        $("#cameraCornerButton").click(function(){cameraSomeCornerPocket(), soundClick();});
        //Button CSS
        $("#show1").css({"background-color" : "black" , "color" : "white" , "padding" : "10px 20px", "font-size" : "16px", "border-radius" : "5px"});
        $("#show2").css({"background-color" : "black" , "color" : "white" , "padding" : "10px 20px", "font-size" : "16px", "border-radius" : "5px"});
        $("#show3").css({"background-color" : "black" , "color" : "white" , "padding" : "10px 20px", "font-size" : "16px", "border-radius" : "5px"});
        $("#simulate").css({"background-color" : "red" , "color" : "white", "padding" : "15px", "font-size" : "25px", "border-radius" : "10px", "font-weight" : "bold"});
        $("#cameraFlatButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
        $("#cameraCornerButton").css({"background-color" : "white" , "color" : "black" , "border-radius" : "10px" , "padding" : "10px 10px"});
    }); 
}

function updatePreHUD()
{
    $("#user_span1").html("<p align=\"center\">Page: <b>"+ PAGE_SELECT + "</b></p>");
    if (PAGE_SELECT == 1){
        $("#user_span3").html("<p align=\"center\">Ball Selected: <b>"+ BALL_TYPE[BALL_CHOICE] + "</b></p>");
        $("#user_span5").html("<p align=\"center\">The Colour you are playing as is set to: <b>" + YOURCOLOR + "</b></p>");
        if (CURRENT_PAGE !== PAGE_SELECT){
            $("#user_span4").html(line4_1);
            $("#user_span6").html(line6_1);
            
            $(document).ready(function(){
                //Button Functionality
                $("#yellowButton").click(function(){selectBall(0), soundClick();});
                $("#redButton").click(function(){selectBall(1), soundClick();});
                $("#blackButton").click(function(){selectBall(2), soundClick();});
                $("#whiteButton").click(function(){selectBall(3), soundClick();});
                $("#yourNButton").click(function(){setColor("None"), soundClick();});
                $("#yourYButton").click(function(){setColor("Yellow"), soundClick();});
                $("#yourRButton").click(function(){setColor("Red"), soundClick();});
                //Button CSS
                $("#yellowButton").css({"background-color" : "yellow" , "color" : "yellow" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#redButton").css({"background-color" : "red" , "color" : "red" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#blackButton").css({"background-color" : "black" , "color" : "black" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#whiteButton").css({"background-color" : "white" , "color" : "white" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#yourNButton").css({"background-color" : "#dddddd" , "color" : "#dddddd" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#yourYButton").css({"background-color" : "yellow" , "color" : "yellow" , "border-radius" : "30px" , "padding" : "10px 10px"});
                $("#yourRButton").css({"background-color" : "red" , "color" : "red" , "border-radius" : "30px" , "padding" : "10px 10px"});
            });   
        }
    }
    
    else if (PAGE_SELECT == 2){
        $("#user_span3").html("<p align=\"center\">Toggle controls: <b>Camera " + CAMERA + "</b></p>");
        $("#user_span5").html("<p align=\"center\">Power: <b>" + MIN_POWER + " to " + MAX_POWER + "</b></p>");
        if (CURRENT_PAGE !== PAGE_SELECT){
            $("#user_span4").html(line4_3);
            $("#user_span6").html(line6_2);
            $(document).ready(function(){
                //Button Functionality
                $("#gameButton").click(function(){myControls(), soundClick(), CAMERA = "Off";});
                $("#cameraButton").click(function(){ABHandler.defaultTouch(), ABHandler.defaultMouse(), soundClick(), CAMERA = "On";});
                $("#decreasePowerMinButton").click(function(){decreasePowerMin(), soundClick();});
                $("#increasePowerMinButton").click(function(){increasePowerMin(), soundClick();});
                $("#decreasePowerMaxButton").click(function(){decreasePowerMax(), soundClick();});
                $("#increasePowerMaxButton").click(function(){increasePowerMax(), soundClick();});
                //Button CSS
                $("#gameButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
                $("#cameraButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
                $("#decreasePowerMinButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
                $("#increasePowerMinButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
                $("#decreasePowerMaxButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
                $("#increasePowerMaxButton").css({"background-color" : "black" , "color" : "white" , "border-radius" : "10px" , "padding" : "10px 10px"});
            });   
        }
    }
    CURRENT_PAGE = PAGE_SELECT;
}

function updateHUD(){
    $("#user_span2").html("<p align=\"center\">The Colour you are playing as is set to: <b>" + YOURCOLOR + "</b>.</p>");
    $("#user_span3").html("<p align=\"center\"></p>");
    $("#user_span6").html("<p align=\"center\">Balls on Table: <b>" + BALLS.length + "</b>.</p>");
    $("#user_span7").html("<p align=\"center\">Balls Calculated: <b>" + BALLS_ATTEMPTED + "</b> of <b>" + BALLS_TO_POT + "</b>.</p>");
}

function updatePostHUD(){
    $("#user_span2").html("<p align=\"center\">Current Shot: <b>" + ((SHOT_PICKED) + 1) + "</b>.</p>");
    $("#user_span3").html("<p align=\"center\">Shot Score: <b>" + BEST_SCORES[SHOT_PICKED][0] + "</b>.</p>");
    $("#user_span4").html("<p align=\"center\">Shot Power: <b>" + BEST_SCORES[SHOT_PICKED][2] + "</b>.</p>");

    
}

// ===================================================================================================================
// === Ancient Brain Functions =======================================================================================
// ===================================================================================================================

	
this.newRun = function() 
{
	ABWorld.init3d(startRadius, maxRadius, SKYCOLOR); 			// sets up renderer, scene, camera
	initPreOptions();
 // can adjust renderer:
	ABWorld.renderer.shadowMap.enabled = true;
 // scene is Physijs.Scene, not THREE.Scene
 // can adjust scene - change gravity from default:
	ABWorld.scene.setGravity ( gravity );
	loadResources();	// asynchronous file loads 
						// calls initScene() when all done 
	ABWorld.lookat.copy ( ABWorld.scene.position );  
	ABWorld.scene.simulate();		// Physics simulate - runs on independent timer to AB nextStep
	// user actions
    document.onkeydown          = keyHandler;
};

function myControls()
{
                ABHandler.initTouchDrag         = initTouch;
                ABHandler.touchDrag             = dummy;
                
                ABHandler.initMouseDrag         = click;
                ABHandler.mouseDrag             = dummy;
}

this.nextStep = function()		// not used 
{
    if (splashClicked === true && CONTROLLER === true){
        CONTROLLER = true;
        myControls();
    }
    if (splashClicked === true && toggleBasic === true){
        basicSetUp();
        toggleBasic = false;
        toggledBasic = true;
    }

    if (CALCULATED === false && CALCULATING === false){
        updatePreHUD();}
    else if (CALCULATING === true){
        updateHUD();
    }
    else{
        updatePostHUD();
    }
};

}
	
// ===================================================================================================================
// === Audio =========================================================================================================
// ===================================================================================================================
	
var colaudio1 = new Audio( SOUND_COLLISION );
var colaudio2 = new Audio( SOUND_COLLISION );
var colaudio3 = new Audio( SOUND_COLLISION );
var colaudio4 = new Audio( SOUND_COLLISION );
var colaudio5 = new Audio( SOUND_COLLISION );

var goodaudio1 = new Audio( SOUND_GOOD );
var goodaudio2 = new Audio( SOUND_GOOD );
var goodaudio3 = new Audio( SOUND_GOOD );
var goodaudio4 = new Audio( SOUND_GOOD );
var goodaudio5 = new Audio( SOUND_GOOD );

var badaudio1 = new Audio( SOUND_BAD );
var badaudio2 = new Audio( SOUND_BAD );
var badaudio3 = new Audio( SOUND_BAD );
var badaudio4 = new Audio( SOUND_BAD );
var badaudio5 = new Audio( SOUND_BAD );

var clkaudio1 = new Audio( SOUND_CLICK );
var clkaudio2 = new Audio( SOUND_CLICK );
var clkaudio3 = new Audio( SOUND_CLICK );
var clkaudio4 = new Audio( SOUND_CLICK );
var clkaudio5 = new Audio( SOUND_CLICK );

function soundCollision()		// triggered by JS, not by user interaction 
{
	
	if ( ! AB.audioIsPlaying ( colaudio1 ) ) 	{ colaudio1.play(); return; }
	if ( ! AB.audioIsPlaying ( colaudio2 ) ) 	{ colaudio2.play(); return; }
	if ( ! AB.audioIsPlaying ( colaudio3 ) ) 	{ colaudio3.play(); return; }
	if ( ! AB.audioIsPlaying ( colaudio4 ) ) 	{ colaudio4.play(); return; }
	if ( ! AB.audioIsPlaying ( colaudio5 ) ) 	{ colaudio5.play(); return; }
	
	// else try to make a new one, local variable, without user interaction
	// but this will not play on some browsers:

	var a = new Audio( SOUND_COLLISION );		
	a.play();
}

function soundGood()		// triggered by JS, not by user interaction 
{
	
	if ( ! AB.audioIsPlaying ( goodaudio1 ) ) 	{ goodaudio1.play(); return; }
	if ( ! AB.audioIsPlaying ( goodaudio2 ) ) 	{ goodaudio2.play(); return; }
	if ( ! AB.audioIsPlaying ( goodaudio3 ) ) 	{ goodaudio3.play(); return; }
	if ( ! AB.audioIsPlaying ( goodaudio4 ) ) 	{ goodaudio4.play(); return; }
	if ( ! AB.audioIsPlaying ( goodaudio5 ) ) 	{ goodaudio5.play(); return; }
	
	// else try to make a new one, local variable, without user interaction
	// but this will not play on some browsers:

	var a = new Audio( SOUND_GOOD );		
	a.play();
}

function soundBad()		// triggered by JS, not by user interaction 
{
	
	if ( ! AB.audioIsPlaying ( badaudio1 ) ) 	{ badaudio1.play(); return; }
	if ( ! AB.audioIsPlaying ( badaudio2 ) ) 	{ badaudio2.play(); return; }
	if ( ! AB.audioIsPlaying ( badaudio3 ) ) 	{ badaudio3.play(); return; }
	if ( ! AB.audioIsPlaying ( badaudio4 ) ) 	{ badaudio4.play(); return; }
	if ( ! AB.audioIsPlaying ( badaudio5 ) ) 	{ badaudio5.play(); return; }
	
	// else try to make a new one, local variable, without user interaction
	// but this will not play on some browsers:

	var a = new Audio( SOUND_BAD );		
	a.play();
}

function soundClick()		// triggered by JS, not by user interaction 
{
	
	if ( ! AB.audioIsPlaying ( clkaudio1 ) ) 	{ clkaudio1.play(); return; }
	if ( ! AB.audioIsPlaying ( clkaudio2 ) ) 	{ clkaudio2.play(); return; }
	if ( ! AB.audioIsPlaying ( clkaudio3 ) ) 	{ clkaudio3.play(); return; }
	if ( ! AB.audioIsPlaying ( clkaudio4 ) ) 	{ clkaudio4.play(); return; }
	if ( ! AB.audioIsPlaying ( clkaudio5 ) ) 	{ clkaudio5.play(); return; }
	
	// else try to make a new one, local variable, without user interaction
	// but this will not play on some browsers:

	var a = new Audio( SOUND_CLICK );		
	a.play();
}

// ===================================================================================================================
// === Splash Screens ================================================================================================
// ===================================================================================================================

	AB.newSplash ("<p align=\"center\">Optional Start Settings</p><p align=\"center\">Set up balls in initial break arrangement</p><p align=\"center\"><button id='basicSetUp1'>Initial Break Set Up</button></p>");
        $(document).ready(function(){
        //Button Functionality
        $("#basicSetUp1").click(function(){toggleBasic = true, soundClick(), $("#basicSetUp1").css({"background-color" : "green" , "color" : "white" , "padding" : "15px 30px", "font-size" : "16px", "border-radius" : "5px"});});
        //Button CSS
        $("#basicSetUp1").css({"background-color" : "black" , "color" : "white" , "padding" : "15px 30px", "font-size" : "16px", "border-radius" : "5px"});});  
        // when user clicks/touches button on splash screen, audio starts and run starts:
        $("#splashbutton").click (function()        
        {
                AB.removeSplash();              // remove splash screen
                soundClick();
                splashClicked = true;

        });