diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/assets/sounds/tenSecondsRemaining.wav b/unpublishedScripts/DomainContent/Toybox/towerDefense/assets/sounds/tenSecondsRemaining.wav new file mode 100644 index 0000000000..b5375b3024 Binary files /dev/null and b/unpublishedScripts/DomainContent/Toybox/towerDefense/assets/sounds/tenSecondsRemaining.wav differ diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/launch.js b/unpublishedScripts/DomainContent/Toybox/towerDefense/launch.js index 22f84478bb..41988397e0 100644 --- a/unpublishedScripts/DomainContent/Toybox/towerDefense/launch.js +++ b/unpublishedScripts/DomainContent/Toybox/towerDefense/launch.js @@ -18,7 +18,7 @@ y: 1, z: 1 }; - var BLOCK_LIFETIME = 10; + var BLOCK_LIFETIME = 120; var MUZZLE_SOUND_URL = Script.resolvePath("air_gun_1_converted.wav"); var MUZZLE_SOUND_VOLUME = 0.5; @@ -35,6 +35,7 @@ } this.launchBlock = function () { + print("launch.js | Launching block"); var cylinder = Entities.getEntityProperties(cylinderID, ["position", "rotation", "dimensions"]); var muzzlePosition = Vec3.sum(cylinder.position, Vec3.multiplyQbyV(cylinder.rotation, { x: 0.0, y: 0.5 * (cylinder.dimensions.y + BLOCK_DIMENSIONS.y), Z: 0.0 })); @@ -45,6 +46,7 @@ Entities.addEntity({ type: "Model", + name: "TD.block", modelURL: BLOCK_MODEL_URL, shapeType: "compound", //compoundShapeURL: BLOCK_COMPOUND_SHAPE_URL, @@ -55,7 +57,8 @@ position: muzzlePosition, rotation: Quat.multiply(cylinder.rotation, Quat.fromPitchYawRollDegrees(0.0, Math.random() * 360.0, 0.0)), velocity: muzzleVelocity, - lifetime: BLOCK_LIFETIME + lifetime: BLOCK_LIFETIME, + script: Script.resolvePath("destructibleEntity.js") }); Audio.playSound(muzzleSound, { @@ -66,14 +69,17 @@ } this.clickDownOnEntity = function () { + print("launch.js | got click down"); this.launchBlock(); } this.startNearTrigger = function () { + print("launch.js | got start near trigger"); this.launchBlock(); } this.startFarTrigger = function () { + print("launch.js | got start far trigger"); this.launchBlock(); } }) diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/roofEntity.js b/unpublishedScripts/DomainContent/Toybox/towerDefense/roofEntity.js new file mode 100644 index 0000000000..1be8721636 --- /dev/null +++ b/unpublishedScripts/DomainContent/Toybox/towerDefense/roofEntity.js @@ -0,0 +1,9 @@ +(function() { + return { + preload: function(entityID) { + Entities.editEntity(entityID, { + localRenderAlpha: 0.2 + }); + } + }; +}); diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/targetEntity.js b/unpublishedScripts/DomainContent/Toybox/towerDefense/targetEntity.js index 322d721cb2..7fa5edb605 100644 --- a/unpublishedScripts/DomainContent/Toybox/towerDefense/targetEntity.js +++ b/unpublishedScripts/DomainContent/Toybox/towerDefense/targetEntity.js @@ -46,10 +46,11 @@ if (!Function.prototype.bind) { var userData = Entities.getEntityProperties(this.entityID, 'userData').userData; var data = parseJSON(userData); - if (data !== undefined && data.gameChannel) { - this.gameChannel = data.gameChannel + if (data !== undefined && data.gameChannel !== undefined && data.teamNumber !== undefined) { + this.gameChannel = data.gameChannel; + this.teamNumber = data.teamNumber; } else { - print("targetEntity.js | ERROR: userData does not contain a game channel"); + print("targetEntity.js | ERROR: userData does not contain a game channel and/or team number"); } }, onCollide: function(entityA, entityB, collision) { @@ -63,7 +64,8 @@ if (!Function.prototype.bind) { this.entityIDsThatHaveCollidedWithMe.push(entityB); Messages.sendMessage(this.gameChannel, JSON.stringify({ type: "target-hit", - entityID: this.entityID + entityID: this.entityID, + teamNumber: this.teamNumber })); } } diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/towerButton.js b/unpublishedScripts/DomainContent/Toybox/towerDefense/towerButton.js index d4c7f0b847..9e4c346f63 100644 --- a/unpublishedScripts/DomainContent/Toybox/towerDefense/towerButton.js +++ b/unpublishedScripts/DomainContent/Toybox/towerDefense/towerButton.js @@ -7,10 +7,12 @@ function signalAC() { var userData = Entities.getEntityProperties(itemID, ["userData"]).userData; - Messages.sendMessage(JSON.parse(userData).gameChannel, "START"); + Messages.sendMessage(JSON.parse(userData).gameChannel, JSON.stringify({ + type: 'start-game' + })); } this.startNearTrigger = signalAC; this.startFarTrigger = signalAC; this.clickDownOnEntity = signalAC; -}) \ No newline at end of file +}) diff --git a/unpublishedScripts/DomainContent/Toybox/towerDefense/towerDefense.js b/unpublishedScripts/DomainContent/Toybox/towerDefense/towerDefense.js index b32d96ac0d..66f1cf526b 100644 --- a/unpublishedScripts/DomainContent/Toybox/towerDefense/towerDefense.js +++ b/unpublishedScripts/DomainContent/Toybox/towerDefense/towerDefense.js @@ -29,38 +29,34 @@ var GAME_STATES = { }; var BASE_POSITION = findSurfaceBelowPosition(MyAvatar.position); -var BUILD_TIME_SECONDS = 5; +var BUILD_TIME_SECONDS = 60; var BUTTON_POSITION = { x: 0, y: 0, z: 0 }; -var BASES = [ - { - position: { x: -20, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, - spawnerPosition: { x: -20, y: 0, z: -1 }, - buttonPosition: { x: -20, y: 0, z: -1 }, - }, - { - position: { x: 20, y: 0, z: 0 }, - color: { red: 0, green: 255, blue: 0 }, - spawnerPosition: { x: 20, y: 0, z: 1 }, - buttonPosition: { x: 20, y: 0, z: 1 }, - }, - { - position: { x: 0, y: 0, z: -20 }, - color: { red: 0, green: 0, blue: 255 }, - spawnerPosition: { x: 1, y: 0, z: -20 }, - buttonPosition: { x: 1, y: 0, z: -20 }, - }, - { - position: { x: 0, y: 0, z: 20 }, - color: { red: 255, green: 0, blue: 255 }, - spawnerPosition: { x: -1, y: 0, z: 20 }, - buttonPosition: { x: -1, y: 0, z: 20 }, - }, -]; -var BASES_SIZE = 20; +var BASES_SIZE = 15; var TARGET_SIZE = 2; var BASES_HEIGHT = 6; var BASES_TRANSPARENCY = 0.2; +var BASES = [ + { + position: { x: -15, y: 0, z: 0 }, + color: { red: 255, green: 0, blue: 0 }, + spawnerPosition: { x: -15 + BASES_SIZE/2, y: 0, z: -BASES_SIZE/2 }, + }, + { + position: { x: 15, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 0 }, + spawnerPosition: { x: 15 - BASES_SIZE/2, y: 0, z: BASES_SIZE/2 }, + }, + { + position: { x: 0, y: 0, z: -15 }, + color: { red: 0, green: 0, blue: 255 }, + spawnerPosition: { x: BASES_SIZE/2, y: 0, z: -15 + BASES_SIZE/2 }, + }, + { + position: { x: 0, y: 0, z: 15 }, + color: { red: 255, green: 0, blue: 255 }, + spawnerPosition: { x: -BASES_SIZE/2, y: 0, z: 15 - BASES_SIZE/2 }, + }, +]; var CHANNEL_NAME = "tower-defense-" //+ Math.floor(Math.random() * 9999); @@ -73,6 +69,13 @@ var bases = []; var entityIDs = []; var spawnerIDs = []; +var teamEntities = { + 0: {}, + 1: {}, + 2: {}, + 3: {}, +}; + var currentState = GAME_STATES.IDLE; Messages.subscribe(CHANNEL_NAME); @@ -85,6 +88,7 @@ if (this.EntityViewer) { var BEGIN_BUILDING_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/letTheGamesBegin.wav")); var BEGIN_FIGHTING_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/fight.wav")); var GAME_OVER_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/gameOver.wav")); +var TEN_SECONDS_REMAINING_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/tenSecondsRemaining.wav")); setup(); @@ -123,7 +127,9 @@ function setup() { }) }; // Base block - bases.push(Entities.addEntity(arenaProperties)); + var arenaID = Entities.addEntity(arenaProperties) + bases.push(arenaID); + teamEntities[i].arenaID = arenaID; const ROOF_HEIGHT = 0.2; @@ -135,16 +141,29 @@ function setup() { registrationPoint: { x: 0.5, y: 0, z: 0.5 }, dimensions: { x: BASES_SIZE, y: ROOF_HEIGHT, z: BASES_SIZE }, color: BASES[i].color, + script: Script.resolvePath('roofEntity.js'), userData: JSON.stringify({ gameChannel: CHANNEL_NAME, }) } // Player area - bases.push(Entities.addEntity(roofProperties)); + var roofID = Entities.addEntity(roofProperties) + bases.push(roofID); + teamEntities[i].roofID = roofID; } } function cleanup() { + for (var i = 0; i < 4; ++i) { + var t = teamEntities[i]; + Entities.deleteEntity(t.targetID); + Entities.deleteEntity(t.spawnerID); + for (var j = 0; j < t.bowIDs.length; ++j) { + Entities.deleteEntity(t.bowIDs[j]); + } + Entities.deleteEntity(t.roofID); + Entities.deleteEntity(t.arenaID); + } while (bases.length > 0) { Entities.deleteEntity(bases.pop()); } @@ -155,16 +174,33 @@ function cleanup() { Entities.deleteEntity(entityIDs[i]); } entityIDs = []; + for (var i = 0; i < spawnerIDs.length; ++i) { + Entities.deleteEntity(spawnerIDs[i]); + } + spawnerIDs = []; print("============= Script Stopping ============="); Script.stop(); } -Messages.messageReceived.connect(function(channel, message, senderID) { - print("Recieved: " + message + " from " + senderID); +function parseJSON(json) { + try { + return JSON.parse(json); + } catch (e) { + return undefined; + } +} + +Messages.messageReceived.connect(function(channel, messageJSON, senderID) { + print("Recieved: " + messageJSON + " from " + senderID); if (channel === CHANNEL_NAME) { - switch (message) { - case "START": + var message = parseJSON(messageJSON); + if (message === undefined) { + print("towerDefense.js | Received non-json message"); + return; + } + switch (message.type) { + case 'start-game': if (currentState != GAME_STATES.IDLE) { print("Got start message, but not in idle state. Current state: " + currentState); return; @@ -185,9 +221,11 @@ Messages.messageReceived.connect(function(channel, message, senderID) { script: Script.resolvePath("targetEntity.js"), userData: JSON.stringify({ gameChannel: CHANNEL_NAME, + teamNumber: i }), }); entityIDs.push(targetID); + teamEntities[i].targetID = targetID; // Create box spawner var spawnerID = Entities.addEntity({ @@ -198,9 +236,13 @@ Messages.messageReceived.connect(function(channel, message, senderID) { color: BASES[i].color, script: Script.resolvePath("launch.js"), userData: JSON.stringify({ + grabbableKey: { + wantsTrigger: true + }, gameChannel: CHANNEL_NAME, }) }); + teamEntities[i].spawnerID = targetID; spawnerIDs.push(spawnerID); Audio.playSound(BEGIN_BUILDING_SOUND, { volume: 1.0, @@ -210,6 +252,12 @@ Messages.messageReceived.connect(function(channel, message, senderID) { currentState = GAME_STATES.BUILD; + Script.setTimeout(function() { + Audio.playSound(TEN_SECONDS_REMAINING_SOUND, { + volume: 1.0, + position: BASE_POSITION + }); + }, (BUILD_TIME_SECONDS - 10) * 1000); Script.setTimeout(function() { print("============ Done building, FIGHT ============="); @@ -228,17 +276,74 @@ Messages.messageReceived.connect(function(channel, message, senderID) { } // Spawn bows + for (var i = 0; i < BASES.length; ++i) { + var position = Vec3.sum(BASE_POSITION, BASES[i].position); + position.y += BASES_HEIGHT + 1; + teamEntities[i].bowIDs = []; + + for (var j = 0; j < 4; ++j) { + teamEntities[i].bowIDs.push(Entities.addEntity({ + position: position, + "collisionsWillMove": 1, + "compoundShapeURL": Script.resolvePath("bow/bow_collision_hull.obj"), + "created": "2016-09-01T23:57:55Z", + "dimensions": { + "x": 0.039999999105930328, + "y": 1.2999999523162842, + "z": 0.20000000298023224 + }, + "dynamic": 1, + "gravity": { + "x": 0, + "y": -1, + "z": 0 + }, + "modelURL": Script.resolvePath("bow/bow-deadly.fbx"), + "name": "TD.Hifi-Bow", + "rotation": { + "w": 0.9718012809753418, + "x": 0.15440607070922852, + "y": -0.10469216108322144, + "z": -0.14418250322341919 + }, + "script": Script.resolvePath("bow/bow.js"), + "shapeType": "compound", + "type": "Model", + "userData": "{\"grabbableKey\":{\"grabbable\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.0813,\"y\":0.0452,\"z\":0.0095},{\"x\":-0.3946,\"y\":-0.6604,\"z\":0.4748,\"w\":-0.4275}],\"LeftHand\":[{\"x\":-0.0881,\"y\":0.0259,\"z\":0.0159},{\"x\":0.4427,\"y\":-0.6519,\"z\":0.4592,\"w\":0.4099}]}}}" + })); + } + } }, BUILD_TIME_SECONDS * 1000); break; - case "STOP": + case 'target-hit': + print("Got target-hit for: ", message.teamNumber); + if (currentState !== GAME_STATES.FIGHT) { + return; + } + print("game is over"); Audio.playSound(GAME_OVER_SOUND, { volume: 1.0, position: BASE_POSITION }); currentState = GAME_STATES.GAME_OVER; - cleanup(); - currentState = GAME_STATES.IDLE; + + // Delete the entities for the team that lost + var t = teamEntities[message.teamNumber]; + Entities.deleteEntity(t.targetID); + Entities.deleteEntity(t.spawnerID); + Entities.deleteEntity(t.roofID); + Entities.deleteEntity(t.arenaID); + + // TODO If more than 1 team is still alive, don't cleanup + // Do a full cleanup after 10 seconds + Script.setTimeout(function() { + cleanup(); + currentState = GAME_STATES.IDLE; + }, 10 * 1000); + break; + default: + print("towerDefense.js | Ignoring unknown message type: ", message.type); break; }