Code viewer for World: v0.1 Pool Predictor Prototype
// 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.screenshotStep  = 200;   
  	// Take screenshot on this step. (All resources should have finished loading.) Default 50.


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

const BALLSIZE      = 5;                   // 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   = 10;     			// heavy impacts have y velocity greater than this 

const GROUNDSIZE    = 150;                  // size of table
const startRadius   = BALLSIZE * 30;       // camera start distance
const maxRadius     = BALLSIZE * 250;      // camera max distance
	
const TEXTURE_GROUND 	= "/uploads/gilligi2/8451323128.png";   //colour of ground (green)
const TEXTURE_WALL      = "/uploads/lillisl2/8451232143.png";   //colour of walls (brown)
const SKYCOLOR          = 0xffffff;    //colour of sky / background (white)

// friction and restitution between 0 and 1: 
var   GROUND_FRICTION 		= 0.4;      // friction of ground
const GROUND_RESTITUTION 	= 0.95;     // restitution of ground
const WALL_FRICTION 		= 0.9;      // friction of wall
const WALL_RESTITUTION  	= 0.1;      // restitution of wall
const BALL_FRICTION 		= 0.4;      // friction of ball
const BALL_RESTITUTION 		= 0.5;      // 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:
var gravity                 = new THREE.Vector3 (0, -50, 0);
const BALL_MASS             = 6;        // mass of ball

const FILE_ARRAY = [                    // colours of each ball [yellow, red, black, white]
                "/uploads/gilligi2/8451323595.png",
                "/uploads/gilligi2/8451323612.png",
                "/uploads/gilligi2/8451147306.png",
                "/uploads/gilligi2/8451147292.png"];
ABWorld.drawCameraControls = false; 

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

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

var resourcesLoaded = false;
var splashClicked = false;

function World() 
{ 
	var textureArray = new Array ( FILE_ARRAY.length );
	var ground_texture;
	var self = this;	
	
	
function loadResources()		// asynchronous file loads - call initScene() when all finished 
{
	var loader = new THREE.TextureLoader();
	loader.load (TEXTURE_GROUND, function (thetexture)  		// asynchronous file load
	{
		thetexture.minFilter  = THREE.LinearFilter;
	    ground_texture = thetexture;
		if (asynchFinished()) initScene();		 
	});
	for (var i = 0; i < FILE_ARRAY.length; i++){ 
	    startFileLoad (i);}		  								// start n more asynchronous file loads 
	  
	var loader2 = new THREE.TextureLoader();
	loader2.load (TEXTURE_WALL, function (thetexture)  		// asynchronous file load
	{
		thetexture.minFilter = THREE.LinearFilter;
		wall_texture = thetexture;
		if (asynchFinished()) initScene();		 
	});
	for (var j = 0; j < FILE_ARRAY.length; j++){ 
	  startFileLoad (j);}}

function startFileLoad(n)				// asynchronous file load of texture n 
{
	var loader = new THREE.TextureLoader();
	loader.load (FILE_ARRAY[n], function (thetexture)  	 
	{
		thetexture.minFilter = THREE.LinearFilter;
		textureArray[n] = thetexture;
		if (asynchFinished()) initScene();		 
	});}
 
function asynchFinished()		// all file loads returned 
{
	if (! ground_texture){
	    return false;}
	for (var i = 0; i < FILE_ARRAY.length; i++){ 
		if (! textureArray[i]){ 
			return false;}}
	return true;}
	
function initScene()			// all file loads have returned 
{
	// --- Light ------------------------------------------------------------------
	var light = new THREE.DirectionalLight(0xFFFFFF, 1.3);
	// close to origin, high up, works best for shadows
	light.position.set(BALLHEIGHT, (BALLHEIGHT * 2), 0);
	light.target.position.copy(ABWorld.scene.position);
	light.castShadow = true;
	// how far away to draw shadows:
	light.shadow.camera.left    = -GROUNDSIZE;
	light.shadow.camera.right   =  GROUNDSIZE;
	light.shadow.camera.bottom  = -GROUNDSIZE;
	light.shadow.camera.top     =  GROUNDSIZE;
    // higher quality shadows at expense of computation time:
	light.shadow.mapSize.width  = 2048;
	light.shadow.mapSize.height = 2048;
	light.shadow.bias           = -0.0001;
	ABWorld.scene.add(light);


	// --- Ground ------------------------------------------------------------------
	var ground_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({map: ground_texture}), GROUND_FRICTION, GROUND_RESTITUTION);
	ground_material.map.wrapS = THREE.RepeatWrapping;
	ground_material.map.wrapT = THREE.RepeatWrapping;
	ground_material.map.repeat.set(50, 50);
	// ground as plane allows bounces but seems to be infinite plane
	var ground = new Physijs.BoxMesh(new THREE.BoxGeometry(GROUNDSIZE, 1, GROUNDSIZE*2), ground_material, 0);
	ground.receiveShadow = true;
	ABWorld.scene.add(ground);
	
	// --- Wall --------------------------------------------------------------------
	var wall_material = Physijs.createMaterial(new THREE.MeshStandardMaterial({map: wall_texture}), WALL_FRICTION, WALL_RESTITUTION);
	var wall1 = new Physijs.BoxMesh(new THREE.BoxGeometry(6, 25, GROUNDSIZE*2), wall_material, 0);
	wall1.position.set((GROUNDSIZE/2), 0, 0);
	wall1.receiveShadow = true;
	ABWorld.scene.add(wall1);
	var wall2 = new Physijs.BoxMesh(new THREE.BoxGeometry(GROUNDSIZE, 25, 6), wall_material, 0);
	wall2.position.set(0, 0, (GROUNDSIZE));
	wall2.receiveShadow = true;
	ABWorld.scene.add(wall2);
	var wall3 = new Physijs.BoxMesh(new THREE.BoxGeometry(6, 25, GROUNDSIZE*2), wall_material, 0);
	wall3.position.set(-(GROUNDSIZE/2), 0, 0);
	wall3.receiveShadow = true;
	ABWorld.scene.add(wall3);
	var wall4 = new Physijs.BoxMesh(new THREE.BoxGeometry(GROUNDSIZE, 25, 6), wall_material, 0);
	wall4.position.set(0, 0, -(GROUNDSIZE));
	wall4.receiveShadow = true;
	ABWorld.scene.add(wall4);

	ABWorld.render();
	console.log("Resources loaded.");
	resourcesLoaded = true;}

function increaseFriction(){
    if (GROUND_FRICTION + 0.5 < 1.0){
        GROUND_FRICTION += 0.5;}
    else    {GROUND_FRICTION = 1.0;}
    }

function decreaseFriction(){
    if (GROUND_FRICTION - 0.5 > 0.0){
        GROUND_FRICTION -= 0.5;}
    else    {GROUND_FRICTION = 0.0;}
    }

function selectBall(x)
{
    BALL_CHOICE = x;}

function createBall(x, y) 	 
{
	
 if ( resourcesLoaded && splashClicked && LIMIT[BALL_CHOICE] !== 0) 		  
 {
    var ball_material = Physijs.createMaterial(new THREE.MeshLambertMaterial({map: textureArray[BALL_CHOICE]}), BALL_FRICTION, BALL_RESTITUTION);
	var ball_geometry = new THREE.SphereGeometry(BALLSIZE, 50, 50);
    var ball = new Physijs.SphereMesh(ball_geometry, ball_material, BALL_MASS);
	ball.collisions = 0;
	ball.castShadow = true;
	//$("#user_span2").html("<br><font color=red><b>" + x + " " + y + "</b></font>");
	ball.position.set(1, BALLHEIGHT, 1);
	ball.addEventListener('collision', function(other_object, relative_velocity, relative_rotation, contact_normal) 
	{
		var mainImpact = relative_velocity.y;		// impact in direction of gravity
		//console.log(Math.abs(mainImpact));
 		if (Math.abs(mainImpact) > HEAVYIMPACT)			// main impact, not lesser ones as it settles  
		{
			//soundCollision();
		}
	});
	ABWorld.scene.add(ball);
	LIMIT[BALL_CHOICE] -= 1;
 }
}

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(event)
{
    createBall(event.screenX, event.screenY);}

//for future drag functionality
//function initDrag (x, y)
//{}
//function drag (x, y)
//{}

function initOptions()
{
    $("#user_span1").html("<p align=\"center\">Ball Selected: <b>"+ BALL_TYPE[BALL_CHOICE] + "</b></p>");
    var s = "<p align=\"center\"><button id='yellowButton'>Yellow</button> <button id='redButton'>Red</button> <button id='blackButton'>Black</button> <button id='whiteButton'>White</button></p>";
    $("#user_span2").html(s);
    $("#user_span3").html("<p align=\"center\">Camera Angles: " + "</p>");
    s = "<p align=\"center\"><button id='accuteButton'>Acute</button> <button id='obtuseButton'>Obtuse</button> <button id='flatdownButton'>Flat</button></p>";
    $("#user_span4").html(s);
    $("#user_span5").html("<p align=\"center\">Ground Friction: " + "</p>");
    s  ="<p align=\"center\"><button id='decreaseButton'>Decrease</button> <button id='increaseButton'>Increase</button></p> ";
    $("#user_span6").html(s);

    //$("#user_span7").html( "<p align=\"center\"><font color=\"#C9C9C9\"><b>"+timer/AB.clockTick+" seconds remaining</b></font><p></b></p>" );
    document.getElementById("yellowButton").addEventListener("click", function() { selectBall(0); }, false);
    document.getElementById("redButton").addEventListener("click", function() { selectBall(1); }, false);
    document.getElementById("blackButton").addEventListener("click", function() { selectBall(2); }, false);
    document.getElementById("whiteButton").addEventListener("click", function() { selectBall(3); }, false);
    
    //document.getElementById("idButton").addEventListener("click", function() { func(var); }, false);
    }

function updateHUD()
{
    $("#user_span1").html("<p align=\"center\">Ball Selected: <b>"+ BALL_TYPE[BALL_CHOICE] + "</b></p>");
    $("#user_span3").html("<p align=\"center\">Camera Angles: " + "</p>");
    $("#user_span5").html("<p align=\"center\">Ground Friction: <b>" + GROUND_FRICTION + "</b></p>");}
    
// --- public interface ----------------------------------------------------------------------
	
this.newRun = function() 
{
	ABWorld.init3d ( startRadius, maxRadius, SKYCOLOR  ); 			// sets up renderer, scene, camera
	initOptions();
 // 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;
    document.ondblclick         = click;
    //ABHandler.initTouchDrag   = initDrag;
    //ABHandler.touchDrag       = drag
    //ABHandler.initMouseDrag   = initDrag;
    //ABHandler.mouseDrag       = drag
};

this.nextStep = function()		// not used 
{
    updateHUD();};
}
	
// --- Splash screen --------------------------------------------------------------

	AB.newSplash ("Demo of Physics API");	
	// when user clicks/touches button on splash screen, audio starts and run starts:
	$("#splashbutton").click (function()        
	{
		AB.removeSplash();			// remove splash screen 
		splashClicked = true;
	});