var start_r =100//camera start posvar max_r =300// Max render distancevar p1score =0;// Player scoresvar p2score =0;var max_targets =3;// Target count to ensure only 3 targets can spawnvar alltargets =newArray(0);// array of all targets currently in playconst mixers =[];// Store for bird animationconst animClock =new THREE.Clock();// Clock to time animation cycleconst birdClock =new THREE.Clock();// Clock to determine when the bird will spawnconst gameClock =new THREE.Clock();// Clock to determine when the game will endvar bird;var hitbox;// Info about bird: object, hitbox and flight direction.var dir;const SKYBOX_ARRAY =["/uploads/kellyn88/sbleft.jpg",// left"/uploads/kellyn88/sbr.png",// right "/uploads/kellyn88/test.png","/uploads/kellyn88/skybox_base.png",//base"/uploads/kellyn88/test.png","/uploads/kellyn88/test.png"];var gridSetup =[[-160,-100,70],[-80,-100,70],[0,-100,70],[80,-100,70],[160,-100,70],[-160,-100,-10],[-80,-100,-10],[0,-100,-10],[80,-100,-10],[160,-100,-10],// Layout of poosible target spawn positions[-160,-100,-90],[-80,-100,-90],[0,-100,-90],[80,-100,-90],[160,-100,-90]]// Audio files const MUSICFILE ='/uploads/kellyn88/ambience.mp3';
AB.backgroundMusic ( MUSICFILE );const shoot_short ="uploads/kellyn88/shoot_short.wav";const shoot_long ="uploads/kellyn88/shoot_long.wav";const squak ="uploads/kellyn88/eagle_call.wav";// Initial splash screen
AB.newSplash ( splashScreenStart());
document.getElementById("splashbutton").id ="startsplash";
AB.world.newRun =function(){// Camera is pulled back on mobile to allow user to see all targets.if(AB.onMobile()){
start_r =500;
max_r =1000;}
AB.socketStart();
initScene();
AB.runReady =false;
cameraControls();// Alter the size of the run header for mobile devices so user can still see scores and timer.if(AB.onMobile()){
AB.headerLHS();
AB.runheaderNormal();
document.getElementById("ab-runheaderbox").style.height ="250px";
document.getElementById("w2m_loggedin").remove();
let b = document.getElementsByTagName("b");
b[0].remove();
let a = document.getElementsByTagName("a");
a[1].remove();}else{
document.getElementById("ab-runheaderbox").style.width ="200px";}}function initScene(){var color =new THREE.Color();ABWorld.init2d ( start_r, max_r, color );ABWorld.scene.background =new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY );// Initiate scenevar light =new THREE.AmbientLight("white",0.8);ABWorld.scene.add(light);}
AB.world.nextStep =function(){if(Math.trunc(gameClock.getElapsedTime())==60){
AB.runReady =false;
AB.newSplash ( splashScreenEnd());
document.getElementById("splashbutton").innerHTML ="Play Again";
document.getElementById("splashbutton").onclick = reload;// Splash screen displays when time runs out to display results. Game can be refreshed from here.}if(alltargets.length < max_targets){// Spawn targets
spawnTarget();}if(AB.socket){if(AB.socket.connected){
AB.socketOut(p1score);
AB.msg("P1 Score = "+ p1score +" P2 score = "+ p2score +"\n Time remaining: "+Math.trunc(60- gameClock.getElapsedTime()));// Sends player scores to other player.}}if(bird){if(bird.position.x ==25|| bird.position.x ==-25){for(var i =0; i < alltargets.length; i++){if(alltargets[i].name =="bird"){
alltargets.splice(i,1);}}ABWorld.scene.remove(bird)&&ABWorld.scene.remove(hitbox);// If bird has left the screen remove it
bird =null;
max_targets =3;}else{
anim_bird();
bird.translateY(-0.5)&& hitbox.translateX(dir);// Move bird}}else{var t = birdClock.getElapsedTime();if(t >=15){var randomIndex = AB.randomIntAtoB(1,50);// Bird has a 1/50 chance of spawning once 20 seconds from the last bird spawn has elapsedif(randomIndex ==25){
loadBird();
birdClock.start();}}}};function spawnTarget(){var texture =new THREE.TextureLoader().load("/uploads/kellyn88/target.png");var shape =new THREE.CylinderGeometry(30,30,5,32);var cover =new THREE.MeshBasicMaterial({map: texture});var target =new THREE.Mesh(shape, cover);// Create target meshvar randomIndex = AB.randomIntAtoB(0, gridSetup.length);var item = gridSetup[randomIndex];
target.position.set(item[0], item[1], item[2]);// Select Random spawn locationABWorld.scene.add(target);// Add target to scene
gridSetup.splice(randomIndex,1);// Grid position is now occupied
alltargets.push(target);// Add to list of targets}function cameraControls(){ABHandler.initTouchDrag = mouseClick;// Override default mobile controlsABHandler.touchDrag = mouseDrag;ABHandler.touchZoom = mouseZoom;ABHandler.initMouseDrag = mouseClick;// Override default desktop controlsABHandler.mouseDrag = mouseDrag;ABHandler.mouseZoom = mouseZoom;}function mouseClick(x, y)//Check if mouse click hits target{
audioHandler("shoot");
targetHit(x, y);return;}function mouseDrag(x, y){// Disable dragreturn;}function mouseZoom(x){// Disable zoomreturn;}function targetHit(x, y)// Go through list of targets and check if any are hit by mouse click{if(alltargets.length >0){for(var i =0; i < alltargets.length; i++){var target = alltargets[i];if(ABWorld.hitsObject ( x, y, target )){if(target.name !="bird"){var pos =[target.position.x, target.position.y, target.position.z];
gridSetup.push(pos);// Grid pos is free againABWorld.scene.remove(target);// Remove target from scene
alltargets.splice(i,1);// Remove from list of all targets
p1score++;// Increase score}else{ABWorld.scene.remove(bird)&&ABWorld.scene.remove(target);
bird =null;
alltargets.splice(i,1);
max_targets =3;
p1score = p1score +10;}break;}}}return;}function audioHandler(instance){// Function to play audiovar a;if(instance =="shoot"){
a =newAudio( shoot_short );
a.play();}elseif(instance =="bird"){
a =newAudio( squak );
a.play();}}function loadBird()// Function decides bird's orientation and spawn location and spawns in the object{var xIndex;var zIndex = AB.randomIntAtoB(-5,5)var rotation =newArray(0);
ran = AB.randomIntAtoB(0,1);if(ran ===0){
dir =-0.5
rotation =[0,Math.PI,Math.PI /2];
xIndex =20;}else{
dir =0.5
rotation =[Math.PI,0,Math.PI /2];
xIndex =-20;}const loader =new THREE.GLTFLoader();const onLoad =( gltf, position )=>{const model = gltf.scene.children[0];
model.position.copy( position );const animation = gltf.animations[0];const mixer =new THREE.AnimationMixer( model );
mixers.push( mixer );const action = mixer.clipAction( animation );
action.play()
model.rotation.set( rotation[0], rotation[1], rotation[2]);
loadHitbox(position);ABWorld.scene.add( model );
bird = model;};const birdpos =new THREE.Vector3(xIndex,(start_r -(start_r *0.1)), zIndex);// can load 3D models of other user:
audioHandler("bird");
loader.load ('/uploads/kellyn88/simple_bird.glb', gltf => onLoad ( gltf, birdpos ));}function loadHitbox(pos){var shape =new THREE.BoxGeometry(2,2,0.7);var cover =new THREE.MeshBasicMaterial(0xfff);// Create mesh of hitbox for birdvar target =new THREE.Mesh(shape, cover);
max_targets =4;
target.visible =false;
target.name ="bird";
target.position.set(pos.x, pos.y, pos.z -0.1);ABWorld.scene.add(target);
hitbox = target;
alltargets.push(target);}function anim_bird(){const delta = animClock.getDelta();
mixers.forEach(( mixer )=>{ mixer.update( delta );});// Initiate bird flying animation}
AB.world.endRun =function(){};function splashScreenStart()// HTML format string of instructions for splash screen {var s ="<p>Shoot targets as they appear to earn points.</p>";if( AB.onDesktop()) s = s +"<p>Desktop instructions: Use your mouse to aim and click to shoot.</p>";else s = s +"<p>Mobile instructions: Tap to shoot.</p>";
s = s +"<p>1 point is awarded for each target destroyed. Keep an eye out for Hawks. They are more difficult to hit but are worth 10 points. Earn more points than your opponent to win!</p>";return( s );}function splashScreenEnd()// HTML format string of instructions for splash screen {var s ="<h2>Game Over!</h2>";if(p1score > p2score){
s = s +"<p>p1 won with a score of:</p>"+ p1score
}elseif(p1score < p2score){
s = s +"<p>p2 won with a score of:</p>"+ p2score
}elseif(p1score == p2score){
s = s +"<p>Draw! Both players had a score of:</p>"+ p1score
}
s = s +"<p>Your score:</p>"+ p1score
return( s );}function gameFull(){var s ="<p>Game session is currently full. Please try again later.</p>";return(s);}
$("#startsplash").click(function(){// If initial splash screen is clicked:if(AB.socket){if(AB.socket.connected){
AB.socketOut("ready");// If another player is connected other players game will start simultaneously}}
AB.runReady =true;// And your game will start
AB.removeSplash();});function reload(){
window.location.reload();// Function to reload game.}
AB.socketIn =function(s){// Socket functionalityif(s =="ready"){
AB.runReady =true;// Start both games simultaneously
AB.removeSplash();}else{
p2score = s // Receive other players score}}
AB.socketUserlist =function( array ){// Retrieve player countif(array.length >2){// If a third player attempts to join:
AB.runReady =false;
AB.newSplash ( gameFull());// They will receive a message that the game is full and be prompted to retry.
document.getElementById("splashbutton").innerHTML ="Try Again";
document.getElementById("splashbutton").onclick = reload;}
AB.socketUserlist =function(){}// Ensures player count is only retrieved once by unassigning function.};