content/hifi-public/sam/2018-oct/code/CarrotHunt.js
Dale Glass 0d14e5a379 Initial data.
Needs a lot of cleanup. Data has been de-duplicated, and where identical copies existed, one of them
has been replaced with a symlink.

Some files have been excluded, such as binaries, installers and debug dumps. Some of that may still
be present.
2022-02-13 18:59:11 +01:00

543 lines
20 KiB
JavaScript

"use strict";
//
// App.js
// Team 8 Hifi Hackathon 2018
// Chang Kayla Sam Lab
//
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
//*****************************************************
// THE APP TABLET ICON ON OFF
//*****************************************************
var TABLET_BUTTON_NAME = "Carrot Hunt";
// var ICON_URL = Script.resolvePath("https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/carrot.svg");
var ICON_URL = "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/CarrotHunt.png";
var ACTIVE_ICON_URL = Script.resolvePath("../models/asset/carrot%20%281%29.svg");
// var ACTIVE_ICON_URL = "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/CarrotHunt.png";
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var button = tablet.addButton({
text: TABLET_BUTTON_NAME,
icon: ICON_URL,
activeIcon: ACTIVE_ICON_URL
});
var gameOn = false;
var window;
function onClicked() {
if (gameOn) {
killGame()
} else {
createGame()
}
}
button.clicked.connect(onClicked);
Script.scriptEnding.connect(function () {
tablet.removeButton(button);
button.clicked.disconnect(onClicked);
killGame()
});
//*****************************************************
// THE GAME CONFIG
//*****************************************************
var theConfig = {
grid: {
size: 5.0,
},
tile: {
modelURL: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/model.obj",
},
terrain: {
numTiles: 16,
numCarrotsPerTile: 2,
},
carrot: {
dim: { x: 0.3, y: 1.0, z: 0.3 },
modelURL: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/carrot2.fbx",
clientScript: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/code/carrot-client.js",
serverScript:"https://hifi-public.s3.amazonaws.com/sam/2018-oct/code/carrot-server.js",
},
props: {
models: [
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/GRASS.fbx", yoff: 0.7 },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/BUNNY3.fbx", yoff: 0.9, },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/BUNNY3.fbx", yoff: 0.9, },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/CABAGEfbx.fbx", yoff: 0.7, dim: {"x": 0.8126206040382385, "y": 0.7492879271507263, "z": 0.8094865620136261} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/Lowpoly_tree_sample.obj", yoff: 2.9, dim: {"x": 4.765009880065918, "y": 5.040099143981934, "z": 3.54240512847900} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/rainbow.fbx", yoff: 1.0, dim: {x: 1.5, y: 1.5, z: 1.1} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/real%20rainbow.fbx", yoff: 3.0, dim: {"x": 0.6, "y": 1.84, "z": 3.39} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/Lowpoly_tree_sample.obj", yoff: 2.9, dim: {"x": 4.765009880065918, "y": 5.040099143981934, "z": 3.54240512847900} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/GRASS.fbx", yoff: 0.7 },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/GRASS.fbx", yoff: 0.7 },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/GRASS.fbx", yoff: 0.7 },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/mountain3.fbx", yoff: 0.7, dim: {"x": 3.8655, "y": 5.6837, "z": 1.8314} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/rainbow.fbx", yoff: 1.0, dim: {x: 1.5, y: 1.5, z: 1.1} },
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/rainbow.fbx", yoff: 1.0, dim: {x: 1.5, y: 1.5, z: 1.1} },
],
notUsed: [
{ url: "https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/model%20%281%29.fbx", yoff: 1.2, dim: {"x":1.58, "y": 1.74, "z": 1.54} },
],
},
debug: false,
lifetime: 50,
audio: {
begin: SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sam/2018-oct/sounds/cartoon-vintage-surf-organ-melody_zk633uNd.mp3"),
end: SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sam/2018-oct/sounds/chinese-gong-crash_z1sgFyHO.mp3"),
tick: SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sam/2018-oct/sounds/jg-032316-sfx-elearning-clock-ticking-for-5-seconds.mp3"),
}
};
function Print(m) {
if (true) {
print(m)
}
}
var AUDIO_VOLUME_LEVEL = 0.25;
function playSound(sound, position, repeat) {
if (sound.downloaded) {
return Audio.playSound(sound, {
//position: position,
//localOnly: (local === undefined) ? false : local,
loop: (repeat === undefined) ? false : repeat,
volume: AUDIO_VOLUME_LEVEL
});
}
}
//*****************************************************
// THE GAME
//*****************************************************
function Game(config, killer) {
Print("Game Created:" + JSON.stringify(this));
playSound(theConfig.audio.begin)
this.grid = new Grid(config);
this.balance = new Balance(config);
this.tiles = []
var numTiles = config.terrain.numTiles;
for (var index = 0; index < numTiles; index++) {
this.tiles.push(new Tile(index, config, this.grid, this.balance));
}
this.tickSound = playSound(config.audio.tick, 1, false)
this.killself = killer
this.bannerProperties = {
"dimensions": {
"x": 10,
"y": 5,
"z": 0.009200000204145908
},
"ignoreForCollisions": true,
"modelURL": "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx",
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
/* "rotation": {
"w": 1,
"x": -1.52587890625e-05,
"y": -1.52587890625e-05,
"z": -1.52587890625e-05
},*/
position: tileCoordRelPosToPos(this.grid, {x: -4, y: 0, z: 2}, { x: 0, y: 8.0, z: 0}),
rotation: this.grid.stageOrientation,
"shapeType": "box",
"textures": "{\"tex.picture\":\"https://hifi-public.s3.amazonaws.com/sam/2018-oct/models/CarrotHunt.png\"}",
"type": "Model",
"userData": "{\"grabbableKey\":{\"grabbable\":true}}",
lifetime: config.lifetime,
}
this.banner = Entities.addEntity( this.bannerProperties );
// put an end to this in lifetime seconds
this.timer = Script.setTimeout(killer, config.lifetime * 1000);
}
Game.prototype.win = function () {
this.killself();
}
Game.prototype.kill = function () {
this.timer.stop();
Entities.deleteEntity(this.banner);
this.balance.kill();
for (var i = 0; i < this.tiles.length; i++) {
this.tiles[i].kill();
}
this.tickSound.stop();
playSound(theConfig.audio.end)
Print("Game Killed:" + JSON.stringify(this));
};
var theGame = {}
function killGame() {
gameOn = false;
theGame.kill();
button.editProperties({isActive: false})
Print("Carotte Game End")
}
function createGame() {
Print("Carotte Game Start")
gameOn = true
theGame = new Game(theConfig, killGame);
button.editProperties({isActive: true});
}
//*****************************************************
// BALANCE
//*****************************************************
function Balance(config) {
var numTiles = config.terrain.numTiles;
var numCarrotsPerTile = config.terrain.numCarrotsPerTile;
// Total carrots
this.numCarrots = numTiles * numCarrotsPerTile;
// Find the tile containing the winning carrot
var winningTileIndex = Math.floor(numTiles * Math.random());
// Find the winning carrot in there
var carrotIndex = winningTileIndex * numCarrotsPerTile;
var winningCarrotIndex = carrotIndex + Math.floor(numCarrotsPerTile * Math.random());
this.winningTile = winningTileIndex;
this.winningCarrots = winningCarrotIndex;
// pick a looser carrot
var loosingCarrotIndex = winningCarrotIndex;
while (loosingCarrotIndex == winningCarrotIndex) {
loosingCarrotIndex = Math.floor(this.numCarrots * Math.random());
}
this.loosingCarrots = loosingCarrotIndex;
// Based on the number of props and the number of tiles, assign some:
var numProps = config.props.models.length;
var ratioPropsToTile = numProps / numTiles;
var maxNumProps = Math.min(numProps, numTiles);
this.propTiles = [];
for (var i = 0; i < numProps; i++) {
var propTileIndex = Math.floor(numTiles * Math.random());
while (this.propTiles.indexOf(propTileIndex) !== -1) {
propTileIndex = Math.floor(numTiles * Math.random());
}
this.propTiles.push(propTileIndex)
}
}
Balance.prototype.getCarrotState = function (index) {
if (this.winningCarrots == index) {
return 1;
} else if (this.loosingCarrots == index) {
return -1;
} else {
return 0;
}
}
Balance.prototype.isWinningTile = function (index) {
return this.winningTileIndex == index
}
Balance.prototype.isPropTile = function (tileIndex) {
return -1 != this.propTiles.indexOf(tileIndex);
}
Balance.prototype.getPropAtTile = function (tileIndex) {
return this.propTiles.indexOf(tileIndex);
}
Balance.prototype.kill = function () {
Print("Balance Killed:" + JSON.stringify(this));
};
//*****************************************************
// GRID
//*****************************************************
var GRID_AXIS_UNIT = 1.0;
var GRID_CELL_SIZE = 5.0;
var GRID_HEX_H = (GRID_CELL_SIZE / 2) * 0.86602540378;
var GRID_HEX_V = GRID_CELL_SIZE / 4;
var GRID_X_OFFSET = 3 * GRID_HEX_V;
var GRID_Y_OFFSET = GRID_AXIS_UNIT;
var GRID_Z_OFFSET = 2 * GRID_HEX_H;
var GRID_ROOT_Y_OFFSET = -GRID_AXIS_UNIT;
var GRID_HEX_TILE_DIM = {x: 4 * GRID_HEX_V, y: GRID_AXIS_UNIT, z: GRID_HEX_H * 2.0}
function Grid(config) {
this.stageOrientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
this.stageRoot = {"x":0.0,"y":0.0,"z":0.0};
this.stageAxisA = Vec3.multiply(GRID_AXIS_UNIT, Quat.getForward(this.stageOrientation));
this.stageAxisB = Vec3.multiply(GRID_AXIS_UNIT, Quat.getRight(this.stageOrientation));
this.stageAxisC = Vec3.multiply(GRID_AXIS_UNIT, Quat.getUp(this.stageOrientation));
this.initializeGrid = function() {
MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
var orientation = MyAvatar.orientation;
orientation = Quat.safeEulerAngles(orientation);
orientation.x = 0;
orientation = Quat.fromVec3Degrees(orientation);
this.stageOrientation = orientation;
this.stageAxisA = Vec3.multiply(GRID_AXIS_UNIT, Quat.getForward(this.stageOrientation));
this.stageAxisB = Vec3.multiply(GRID_AXIS_UNIT, Quat.getRight(this.stageOrientation));
this.stageAxisC = Vec3.multiply(GRID_AXIS_UNIT, Quat.getUp(this.stageOrientation));
var pos = Vec3.sum(MyAvatar.position, Vec3.multiply(-1.0, Quat.getForward(orientation)));
this.stageRoot = Vec3.sum(pos, Vec3.multiply(-1.0, Quat.getUp(orientation)));
Print("Game Killed:" + JSON.stringify(this));
}
this.initializeGrid();
this.generateTileCoords = function(numTiles) {
var tiles = [];
var index = 0;
var radius = Math.ceil(Math.sqrt(numTiles));
var halfRadius = Math.max(1, Math.floor(radius * 0.5));
Print("radius = " + radius + "halfRadius = " + halfRadius)
for (var j = 1; j <=radius; j++) {
for (var i = 0; i < radius; i++) {
tiles.push({x: -i, y: 0, z: j});
// Print(JSON.stringify(tiles))
index++;
if (index >= numTiles) {
return tiles;
}
}
}
return tiles;
}
this.tiles = this.generateTileCoords(config.terrain.numTiles);
this.getTileCoord = function(index) {
if (index < this.tiles.length) {
return this.tiles[index];
}
return {x: 0, y: 0, z: 0};
};
this.getResetCoord = function() {
return {x: 0, y: 5.0, z: 0};
};
}
function tileCoordToPos(grid, coord) {
var isEven = coord.z == 2 * Math.floor(coord.z / 2);
return Vec3.sum(grid.stageRoot, { x: coord.z * GRID_X_OFFSET,
y: coord.y,
z: coord.x * GRID_Z_OFFSET
+ (coord.z * GRID_HEX_H)} );
}
function tileCoordRelPosToPos(grid, coord, pos) {
var origin = tileCoordToPos(grid, coord)
return Vec3.sum(origin, { x: pos.z * 0.6 * 4.0 * GRID_HEX_V,
y: pos.y,
z: pos.x * 0.6 * 2.0 * GRID_HEX_H} );
}
function tileDim() {
return { x: GRID_CELL_SIZE,y: GRID_AXIS_UNIT,z: GRID_CELL_SIZE };
// return GRID_HEX_TILE_DIM;
}
//*****************************************************
// TERRAIN TILE
//*****************************************************
var shapeTypes = [
"none",
"box",
"sphere",
"compound",
"simple-hull",
"simple-compound",
"static-mesh"
];
function Tile(index, config, grid, balance) {
this.index = index;
this.winner = balance.isWinningTile(index);
this.coord = grid.getTileCoord(this.index);
var pos = tileCoordToPos(grid, this.coord);
if (config.debug) {
if (this.winner) {
print("Tile Winner:" + this.index + JSON.stringify(this.coord));
} else {
print("Tile not Winner:" + this.index + JSON.stringify(this.coord));
}
}
this.entityProperties = {
type: "Shape",
shape: "Hexagon",
name: "Tile-" + index,
color: (this.winner && config.debug ? { red: 120, green: 20, blue: 240 } : { red: 120 + Math.random() * 50, green: 175 + Math.random() * 75, blue: 100 + Math.random() * 150 }),
position: pos,
rotation: grid.stageOrientation,
dimensions: tileDim(),
lifetime: config.lifetime,
userData: JSON.stringify({ grabbableKey: { grabbable: false } }),
shapeType:shapeTypes[1]
}
this.entity = Entities.addEntity( this.entityProperties );
this.entityProperties = Entities.getEntityProperties(this.entity);
this.carrots = []
var numCarrots = config.terrain.numCarrotsPerTile;
var carrotIndex = this.index * numCarrots;
for (var i = 0; i < numCarrots; i++) {
this.carrots.push(new Carrot(carrotIndex, this, config, grid, balance));
carrotIndex++;
}
this.props = [];
if (balance.isPropTile(index)) {
this.props.push(new Prop(balance.getPropAtTile(index), this, config, grid, balance));
}
// print("Tile Created:" + JSON.stringify(this));
}
Tile.prototype.kill = function () {
for (var i = 0; i < this.props.length; i++) {
this.props[i].kill();
}
for (var i = 0; i < this.carrots.length; i++) {
this.carrots[i].kill();
}
if (this.entity) {
Entities.deleteEntity(this.entity);
}
Print("Tile Killed:" + JSON.stringify(this));
};
//*****************************************************
// CARROT
//*****************************************************
function Carrot(index, tile, config, grid, balance) {
this.tileCoord = tile.coord;
this.winner = balance.getCarrotState(index);
this.tileRelPos = {x: Math.random() - 0.5, y: 0.5, z: Math.random() - 0.5};
var pos = tileCoordRelPosToPos(grid, this.tileCoord, this.tileRelPos);
var userData = { grabbableKey: { grabbable: false }, winner: this.winner }
// if looser capture the reset pos
if (this.winner == -1) {
userData.resetPos = tileCoordToPos(grid, grid.getResetCoord())
}
this.entityProperties = {
type: "Model",
name: "Carrot-" + index,
position: pos,
rotation: grid.stageOrientation,
dimensions: config.carrot.dim,
lifetime: config.lifetime,
modelURL: config.carrot.modelURL,
userData: JSON.stringify(userData),
shapeType:shapeTypes[4],
script: config.carrot.clientScript + "?" + Date.now(),
serverScripts: config.carrot.serverScript + "?" + Date.now(),
}
this.entity = Entities.addEntity( this.entityProperties );
this.entityProperties = Entities.getEntityProperties(this.entity);
// Print("Carrot Created:" + JSON.stringify(this));
}
Carrot.prototype.kill = function () {
if (this.entity) {
Entities.deleteEntity(this.entity);
// this.entity = null
}
Print("Carrot Killed:" + JSON.stringify(this));
};
//*****************************************************
// PROP
//*****************************************************
function Prop(index, tile, config, grid) {
this.tileCoord = tile.coord;
this.tileRelPos = {x: Math.random() - 0.5, y: config.props.models[index].yoff, z: Math.random() - 0.5};
var pos = tileCoordRelPosToPos(grid, this.tileCoord, this.tileRelPos);
this.entityProperties = {
type: "Model",
name: "Prop-" + index,
position: pos,
rotation: grid.stageOrientation,
lifetime: config.lifetime,
modelURL: config.props.models[index].url,
userData: JSON.stringify({ grabbableKey: { grabbable: false } }),
}
if (config.props.models[index].dim) {
this.entityProperties.dimensions = config.props.models[index].dim;
}
this.entity = Entities.addEntity( this.entityProperties );
this.entityProperties = Entities.getEntityProperties(this.entity);
// Print("Prop Created:" + JSON.stringify(this));
}
Prop.prototype.kill = function () {
if (this.entity) {
Entities.deleteEntity(this.entity);
// this.entity = null
}
Print("Prop Killed:" + JSON.stringify(this));
};
}());