diff --git a/examples/example/entities/platform.js b/examples/example/entities/platform.js index d4fe7d0f68..1748198cce 100644 --- a/examples/example/entities/platform.js +++ b/examples/example/entities/platform.js @@ -15,29 +15,29 @@ // UI and debug console implemented using uiwidgets / 2d overlays Script.include("../../libraries/uiwidgets.js"); -if (typeof(UI) === 'undefined') { // backup link in case the user downloaded this somewhere - print("Missing library script -- loading from public.highfidelity.io"); - Script.include('http://public.highfidelity.io/scripts/libraries/uiwidgets.js'); - if (typeof(UI) === 'undefined') { - print("Cannot load UIWidgets library -- check your internet connection", COLORS.RED); - throw new Error("Could not load uiwidgets.js"); - } +if (typeof(UI) === 'undefined') { // backup link in case the user downloaded this somewhere + print("Missing library script -- loading from public.highfidelity.io"); + Script.include('http://public.highfidelity.io/scripts/libraries/uiwidgets.js'); + if (typeof(UI) === 'undefined') { + print("Cannot load UIWidgets library -- check your internet connection", COLORS.RED); + throw new Error("Could not load uiwidgets.js"); + } } // Platform script (function () { var SCRIPT_NAME = "platform.js"; -var USE_DEBUG_LOG = true; // Turns on the 2dOverlay-based debug log. If false, just redirects to print. +var USE_DEBUG_LOG = true; // Turns on the 2dOverlay-based debug log. If false, just redirects to print. var NUM_DEBUG_LOG_LINES = 10; -var LOG_ENTITY_CREATION_MESSAGES = false; // detailed debugging (init) -var LOG_UPDATE_STATUS_MESSAGES = false; // detailed debugging (startup) +var LOG_ENTITY_CREATION_MESSAGES = false; // detailed debugging (init) +var LOG_UPDATE_STATUS_MESSAGES = false; // detailed debugging (startup) -var MAX_UPDATE_INTERVAL = 0.2; // restrict to 5 updates / sec +var MAX_UPDATE_INTERVAL = 0.2; // restrict to 5 updates / sec var AVATAR_HEIGHT_OFFSET = 1.5; // offset to make the platform spawn under your feet. Might need to be adjusted for unusually proportioned avatars. var USE_ENTITY_TIMEOUTS = true; -var ENTITY_TIMEOUT_DURATION = 30.0; // kill entities in 30 secs if they don't get any updates -var ENTITY_REFRESH_INTERVAL = 10.0; // poke the entities every 10s so they don't die until we're done with them +var ENTITY_TIMEOUT_DURATION = 30.0; // kill entities in 30 secs if they don't get any updates +var ENTITY_REFRESH_INTERVAL = 10.0; // poke the entities every 10s so they don't die until we're done with them // Initial state var NUM_PLATFORM_ENTITIES = 400; @@ -46,1039 +46,1039 @@ var RADIUS = 5.0; // Defines min/max for onscreen platform radius, density, and entity width/height/depth sliders. // Color limits are hardcoded at [0, 255]. var PLATFORM_RADIUS_RANGE = [ 1.0, 15.0 ]; -var PLATFORM_DENSITY_RANGE = [ 0.0, 35.0 ]; // do NOT increase this above 40! (~20k limit). Entity count = Math.PI * radius * radius * density. +var PLATFORM_DENSITY_RANGE = [ 0.0, 35.0 ]; // do NOT increase this above 40! (~20k limit). Entity count = Math.PI * radius * radius * density. var PLATFORM_SHAPE_DIMENSIONS_RANGE = [ 0.001, 2.0 ]; // axis-aligned entity dimension limits // Utils (function () { - if (typeof(Math.randRange) === 'undefined') { - Math.randRange = function (min, max) { - return Math.random() * (max - min) + min; - } - } - if (typeof(Math.randInt) === 'undefined') { - Math.randInt = function (n) { - return Math.floor(Math.random() * n) | 0; - } - } - function fromComponents (r, g, b, a) { - this.red = r; - this.green = g; - this.blue = b; - this.alpha = a || 1.0; - } - function fromHex (c) { - this.red = parseInt(c[1] + c[2], 16); - this.green = parseInt(c[3] + c[4], 16); - this.blue = parseInt(c[5] + c[6], 16); - } - var Color = this.Color = function () { - if (arguments.length >= 3) { - fromComponents.apply(this, arguments); - } else if (arguments.length == 1 && arguments[0].length == 7 && arguments[0][0] == '#') { - fromHex.apply(this, arguments); - } else { - throw new Error("Invalid arguments to new Color(): " + JSON.stringify(arguments)); - } - } - Color.prototype.toString = function () { - return "[Color: " + JSON.stringify(this) + "]"; - } + if (typeof(Math.randRange) === 'undefined') { + Math.randRange = function (min, max) { + return Math.random() * (max - min) + min; + } + } + if (typeof(Math.randInt) === 'undefined') { + Math.randInt = function (n) { + return Math.floor(Math.random() * n) | 0; + } + } + function fromComponents (r, g, b, a) { + this.red = r; + this.green = g; + this.blue = b; + this.alpha = a || 1.0; + } + function fromHex (c) { + this.red = parseInt(c[1] + c[2], 16); + this.green = parseInt(c[3] + c[4], 16); + this.blue = parseInt(c[5] + c[6], 16); + } + var Color = this.Color = function () { + if (arguments.length >= 3) { + fromComponents.apply(this, arguments); + } else if (arguments.length == 1 && arguments[0].length == 7 && arguments[0][0] == '#') { + fromHex.apply(this, arguments); + } else { + throw new Error("Invalid arguments to new Color(): " + JSON.stringify(arguments)); + } + } + Color.prototype.toString = function () { + return "[Color: " + JSON.stringify(this) + "]"; + } })(); // RNG models (function () { - /// Encapsulates a simple color model that generates colors using a linear, pseudo-random color distribution. - var RandomColorModel = this.RandomColorModel = function () { - this.shadeRange = 0; // = 200; - this.minColor = 55; // = 100; - this.redRange = 255; // = 200; - this.greenRange = 0; // = 10; - this.blueRange = 0; // = 0; - }; - /// Generates 4 numbers in [0, 1] corresponding to each color attribute (uniform shade and additive red, green, blue). - /// This is done in a separate step from actually generating the colors, since it allows us to either A) completely - /// rebuild / re-randomize the color values, or B) reuse the RNG values but with different color parameters, which - /// enables us to do realtime color editing on the same visuals (awesome!). - RandomColorModel.prototype.generateSeed = function () { - return [ Math.random(), Math.random(), Math.random(), Math.random() ]; - }; - /// Takes a random 'seed' (4 floats from this.generateSeed()) and calculates a pseudo-random - /// color by combining that with the color model's current parameters. - RandomColorModel.prototype.getRandom = function (r) { - // logMessage("color seed values " + JSON.stringify(r)); - var shade = Math.min(255, this.minColor + r[0] * this.shadeRange); + /// Encapsulates a simple color model that generates colors using a linear, pseudo-random color distribution. + var RandomColorModel = this.RandomColorModel = function () { + this.shadeRange = 0; // = 200; + this.minColor = 55; // = 100; + this.redRange = 255; // = 200; + this.greenRange = 0; // = 10; + this.blueRange = 0; // = 0; + }; + /// Generates 4 numbers in [0, 1] corresponding to each color attribute (uniform shade and additive red, green, blue). + /// This is done in a separate step from actually generating the colors, since it allows us to either A) completely + /// rebuild / re-randomize the color values, or B) reuse the RNG values but with different color parameters, which + /// enables us to do realtime color editing on the same visuals (awesome!). + RandomColorModel.prototype.generateSeed = function () { + return [ Math.random(), Math.random(), Math.random(), Math.random() ]; + }; + /// Takes a random 'seed' (4 floats from this.generateSeed()) and calculates a pseudo-random + /// color by combining that with the color model's current parameters. + RandomColorModel.prototype.getRandom = function (r) { + // logMessage("color seed values " + JSON.stringify(r)); + var shade = Math.min(255, this.minColor + r[0] * this.shadeRange); - // No clamping on the color components, so they may overflow. - // However, this creates some pretty interesting visuals, so we're not "fixing" this. - var color = { - red: shade + r[1] * this.redRange, - green: shade + r[2] * this.greenRange, - blue: shade + r[3] * this.blueRange - }; - // logMessage("this: " + JSON.stringify(this)); - // logMessage("color: " + JSON.stringify(color), COLORS.RED); - return color; - }; - /// Custom property iterator used to setup UI (sliders, etc) - RandomColorModel.prototype.setupUI = function (callback) { - var _this = this; - [ - ['shadeRange', 'shade range'], - ['minColor', 'shade min'], - ['redRange', 'red (additive)'], - ['greenRange', 'green (additive)'], - ['blueRange', 'blue (additive)'] - ].forEach(function (v) { - // name, value, min, max, onValueChanged - callback(v[1], _this[v[0]], 0, 255, function (value) { _this[v[0]] = value }); - }); - } + // No clamping on the color components, so they may overflow. + // However, this creates some pretty interesting visuals, so we're not "fixing" this. + var color = { + red: shade + r[1] * this.redRange, + green: shade + r[2] * this.greenRange, + blue: shade + r[3] * this.blueRange + }; + // logMessage("this: " + JSON.stringify(this)); + // logMessage("color: " + JSON.stringify(color), COLORS.RED); + return color; + }; + /// Custom property iterator used to setup UI (sliders, etc) + RandomColorModel.prototype.setupUI = function (callback) { + var _this = this; + [ + ['shadeRange', 'shade range'], + ['minColor', 'shade min'], + ['redRange', 'red (additive)'], + ['greenRange', 'green (additive)'], + ['blueRange', 'blue (additive)'] + ].forEach(function (v) { + // name, value, min, max, onValueChanged + callback(v[1], _this[v[0]], 0, 255, function (value) { _this[v[0]] = value }); + }); + } - /// Generates pseudo-random dimensions for our cubes / shapes. - var RandomShapeModel = this.RandomShapeModel = function () { - this.widthRange = [ 0.3, 0.7 ]; - this.depthRange = [ 0.5, 0.8 ]; - this.heightRange = [ 0.01, 0.08 ]; - }; - /// Generates 3 seed numbers in [0, 1] - RandomShapeModel.prototype.generateSeed = function () { - return [ Math.random(), Math.random(), Math.random() ]; - } - /// Combines seed values with width/height/depth ranges to produce vec3 dimensions for a cube / sphere. - RandomShapeModel.prototype.getRandom = function (r) { - return { - x: r[0] * (this.widthRange[1] - this.widthRange[0]) + this.widthRange[0], - y: r[1] * (this.heightRange[1] - this.heightRange[0]) + this.heightRange[0], - z: r[2] * (this.depthRange[1] - this.depthRange[0]) + this.depthRange[0] - }; - } - /// Custom property iterator used to setup UI (sliders, etc) - RandomShapeModel.prototype.setupUI = function (callback) { - var _this = this; - var dimensionsMin = PLATFORM_SHAPE_DIMENSIONS_RANGE[0]; - var dimensionsMax = PLATFORM_SHAPE_DIMENSIONS_RANGE[1]; - [ - ['widthRange', 'width'], - ['depthRange', 'depth'], - ['heightRange', 'height'] - ].forEach(function (v) { - // name, value, min, max, onValueChanged - callback(v[1], _this[v[0]], dimensionsMin, dimensionsMax, function (value) { _this[v[0]] = value }); - }); - } + /// Generates pseudo-random dimensions for our cubes / shapes. + var RandomShapeModel = this.RandomShapeModel = function () { + this.widthRange = [ 0.3, 0.7 ]; + this.depthRange = [ 0.5, 0.8 ]; + this.heightRange = [ 0.01, 0.08 ]; + }; + /// Generates 3 seed numbers in [0, 1] + RandomShapeModel.prototype.generateSeed = function () { + return [ Math.random(), Math.random(), Math.random() ]; + } + /// Combines seed values with width/height/depth ranges to produce vec3 dimensions for a cube / sphere. + RandomShapeModel.prototype.getRandom = function (r) { + return { + x: r[0] * (this.widthRange[1] - this.widthRange[0]) + this.widthRange[0], + y: r[1] * (this.heightRange[1] - this.heightRange[0]) + this.heightRange[0], + z: r[2] * (this.depthRange[1] - this.depthRange[0]) + this.depthRange[0] + }; + } + /// Custom property iterator used to setup UI (sliders, etc) + RandomShapeModel.prototype.setupUI = function (callback) { + var _this = this; + var dimensionsMin = PLATFORM_SHAPE_DIMENSIONS_RANGE[0]; + var dimensionsMax = PLATFORM_SHAPE_DIMENSIONS_RANGE[1]; + [ + ['widthRange', 'width'], + ['depthRange', 'depth'], + ['heightRange', 'height'] + ].forEach(function (v) { + // name, value, min, max, onValueChanged + callback(v[1], _this[v[0]], dimensionsMin, dimensionsMax, function (value) { _this[v[0]] = value }); + }); + } - /// Combines color + shape PRNG models and hides their implementation details. - var RandomAttribModel = this.RandomAttribModel = function () { - this.colorModel = new RandomColorModel(); - this.shapeModel = new RandomShapeModel(); - } - /// Completely re-randomizes obj's `color` and `dimensions` parameters based on the current model params. - RandomAttribModel.prototype.randomizeShapeAndColor = function (obj) { - // logMessage("randomizing " + JSON.stringify(obj)); - obj._colorSeed = this.colorModel.generateSeed(); - obj._shapeSeed = this.shapeModel.generateSeed(); - this.updateShapeAndColor(obj); - // logMessage("color seed: " + JSON.stringify(obj._colorSeed), COLORS.RED); - // logMessage("randomized color: " + JSON.stringify(obj.color), COLORS.RED); - // logMessage("randomized: " + JSON.stringify(obj)); - return obj; - } - /// Updates obj's `color` and `dimensions` params to use the current model params. - /// Reuses hidden seed attribs; _must_ have called randomizeShapeAndColor(obj) at some point before - /// calling this. - RandomAttribModel.prototype.updateShapeAndColor = function (obj) { - try { - // logMessage("update shape and color: " + this.colorModel); - obj.color = this.colorModel.getRandom(obj._colorSeed); - obj.dimensions = this.shapeModel.getRandom(obj._shapeSeed); - } catch (e) { - logMessage("update shape / color failed", COLORS.RED); - logMessage('' + e, COLORS.RED); - logMessage("obj._colorSeed = " + JSON.stringify(obj._colorSeed)); - logMessage("obj._shapeSeed = " + JSON.stringify(obj._shapeSeed)); - // logMessage("obj = " + JSON.stringify(obj)); - throw e; - } - return obj; - } + /// Combines color + shape PRNG models and hides their implementation details. + var RandomAttribModel = this.RandomAttribModel = function () { + this.colorModel = new RandomColorModel(); + this.shapeModel = new RandomShapeModel(); + } + /// Completely re-randomizes obj's `color` and `dimensions` parameters based on the current model params. + RandomAttribModel.prototype.randomizeShapeAndColor = function (obj) { + // logMessage("randomizing " + JSON.stringify(obj)); + obj._colorSeed = this.colorModel.generateSeed(); + obj._shapeSeed = this.shapeModel.generateSeed(); + this.updateShapeAndColor(obj); + // logMessage("color seed: " + JSON.stringify(obj._colorSeed), COLORS.RED); + // logMessage("randomized color: " + JSON.stringify(obj.color), COLORS.RED); + // logMessage("randomized: " + JSON.stringify(obj)); + return obj; + } + /// Updates obj's `color` and `dimensions` params to use the current model params. + /// Reuses hidden seed attribs; _must_ have called randomizeShapeAndColor(obj) at some point before + /// calling this. + RandomAttribModel.prototype.updateShapeAndColor = function (obj) { + try { + // logMessage("update shape and color: " + this.colorModel); + obj.color = this.colorModel.getRandom(obj._colorSeed); + obj.dimensions = this.shapeModel.getRandom(obj._shapeSeed); + } catch (e) { + logMessage("update shape / color failed", COLORS.RED); + logMessage('' + e, COLORS.RED); + logMessage("obj._colorSeed = " + JSON.stringify(obj._colorSeed)); + logMessage("obj._shapeSeed = " + JSON.stringify(obj._shapeSeed)); + // logMessage("obj = " + JSON.stringify(obj)); + throw e; + } + return obj; + } })(); // Status / logging UI (ignore this) (function () { - var COLORS = this.COLORS = { - 'GREEN': new Color("#2D870C"), - 'RED': new Color("#AF1E07"), - 'LIGHT_GRAY': new Color("#CCCCCC"), - 'DARK_GRAY': new Color("#4E4E4E") - }; - function buildDebugLog () { - var LINE_WIDTH = 400; - var LINE_HEIGHT = 20; - - var lines = []; - var lineIndex = 0; - for (var i = 0; i < NUM_DEBUG_LOG_LINES; ++i) { - lines.push(new UI.Label({ - text: " ", visible: false, - width: LINE_WIDTH, height: LINE_HEIGHT, - })); - } - var title = new UI.Label({ - text: SCRIPT_NAME, visible: true, - width: LINE_WIDTH, height: LINE_HEIGHT, - }); - - var overlay = new UI.Box({ - visible: true, - width: LINE_WIDTH, height: 0, - backgroundColor: COLORS.DARK_GRAY, - backgroundAlpha: 0.3 - }); - overlay.setPosition(280, 10); - relayoutFrom(0); - UI.updateLayout(); - - function relayoutFrom (n) { - var layoutPos = { - x: overlay.position.x, - y: overlay.position.y - }; - - title.setPosition(layoutPos.x, layoutPos.y); - layoutPos.y += LINE_HEIGHT; - - // for (var i = n; i >= 0; --i) { - for (var i = n + 1; i < lines.length; ++i) { - if (lines[i].visible) { - lines[i].setPosition(layoutPos.x, layoutPos.y); - layoutPos.y += LINE_HEIGHT; - } - } - // for (var i = lines.length - 1; i > n; --i) { - for (var i = 0; i <= n; ++i) { - if (lines[i].visible) { - lines[i].setPosition(layoutPos.x, layoutPos.y); - layoutPos.y += LINE_HEIGHT; - } - } - overlay.height = (layoutPos.y - overlay.position.y + 10); - overlay.getOverlay().update({ - height: overlay.height - }); - } - this.logMessage = function (text, color, alpha) { - lines[lineIndex].setVisible(true); - relayoutFrom(lineIndex); - - lines[lineIndex].getOverlay().update({ - text: text, - visible: true, - color: color || COLORS.LIGHT_GRAY, - alpha: alpha !== undefined ? alpha : 1.0, - x: lines[lineIndex].position.x, - y: lines[lineIndex].position.y - }); - lineIndex = (lineIndex + 1) % lines.length; - UI.updateLayout(); - } - } - if (USE_DEBUG_LOG) { - buildDebugLog(); - } else { - this.logMessage = function (msg) { - print(SCRIPT_NAME + ": " + msg); - } - } + var COLORS = this.COLORS = { + 'GREEN': new Color("#2D870C"), + 'RED': new Color("#AF1E07"), + 'LIGHT_GRAY': new Color("#CCCCCC"), + 'DARK_GRAY': new Color("#4E4E4E") + }; + function buildDebugLog () { + var LINE_WIDTH = 400; + var LINE_HEIGHT = 20; + + var lines = []; + var lineIndex = 0; + for (var i = 0; i < NUM_DEBUG_LOG_LINES; ++i) { + lines.push(new UI.Label({ + text: " ", visible: false, + width: LINE_WIDTH, height: LINE_HEIGHT, + })); + } + var title = new UI.Label({ + text: SCRIPT_NAME, visible: true, + width: LINE_WIDTH, height: LINE_HEIGHT, + }); + + var overlay = new UI.Box({ + visible: true, + width: LINE_WIDTH, height: 0, + backgroundColor: COLORS.DARK_GRAY, + backgroundAlpha: 0.3 + }); + overlay.setPosition(280, 10); + relayoutFrom(0); + UI.updateLayout(); + + function relayoutFrom (n) { + var layoutPos = { + x: overlay.position.x, + y: overlay.position.y + }; + + title.setPosition(layoutPos.x, layoutPos.y); + layoutPos.y += LINE_HEIGHT; + + // for (var i = n; i >= 0; --i) { + for (var i = n + 1; i < lines.length; ++i) { + if (lines[i].visible) { + lines[i].setPosition(layoutPos.x, layoutPos.y); + layoutPos.y += LINE_HEIGHT; + } + } + // for (var i = lines.length - 1; i > n; --i) { + for (var i = 0; i <= n; ++i) { + if (lines[i].visible) { + lines[i].setPosition(layoutPos.x, layoutPos.y); + layoutPos.y += LINE_HEIGHT; + } + } + overlay.height = (layoutPos.y - overlay.position.y + 10); + overlay.getOverlay().update({ + height: overlay.height + }); + } + this.logMessage = function (text, color, alpha) { + lines[lineIndex].setVisible(true); + relayoutFrom(lineIndex); + + lines[lineIndex].getOverlay().update({ + text: text, + visible: true, + color: color || COLORS.LIGHT_GRAY, + alpha: alpha !== undefined ? alpha : 1.0, + x: lines[lineIndex].position.x, + y: lines[lineIndex].position.y + }); + lineIndex = (lineIndex + 1) % lines.length; + UI.updateLayout(); + } + } + if (USE_DEBUG_LOG) { + buildDebugLog(); + } else { + this.logMessage = function (msg) { + print(SCRIPT_NAME + ": " + msg); + } + } })(); // Utils (ignore) (function () { - // Utility function - var withDefaults = this.withDefaults = function (properties, defaults) { - // logMessage("withDefaults: " + JSON.stringify(properties) + JSON.stringify(defaults)); - properties = properties || {}; - if (defaults) { - for (var k in defaults) { - properties[k] = defaults[k]; - } - } - return properties; - } - var withReadonlyProp = this.withReadonlyProp = function (propname, value, obj) { - Object.defineProperty(obj, propname, { - value: value, - writable: false - }); - return obj; - } + // Utility function + var withDefaults = this.withDefaults = function (properties, defaults) { + // logMessage("withDefaults: " + JSON.stringify(properties) + JSON.stringify(defaults)); + properties = properties || {}; + if (defaults) { + for (var k in defaults) { + properties[k] = defaults[k]; + } + } + return properties; + } + var withReadonlyProp = this.withReadonlyProp = function (propname, value, obj) { + Object.defineProperty(obj, propname, { + value: value, + writable: false + }); + return obj; + } - // Math utils - if (typeof(Math.randRange) === 'undefined') { - Math.randRange = function (min, max) { - return Math.random() * (max - min) + min; - } - } - if (typeof(Math.randInt) === 'undefined') { - Math.randInt = function (n) { - return Math.floor(Math.random() * n) | 0; - } - } + // Math utils + if (typeof(Math.randRange) === 'undefined') { + Math.randRange = function (min, max) { + return Math.random() * (max - min) + min; + } + } + if (typeof(Math.randInt) === 'undefined') { + Math.randInt = function (n) { + return Math.floor(Math.random() * n) | 0; + } + } - /// Random distrib: Get a random point within a circle on the xz plane with radius r, center p. - this.randomCirclePoint = function (r, pos) { - var a = Math.random(), b = Math.random(); - if (b < a) { - var tmp = b; - b = a; - a = tmp; - } - var point = { - x: pos.x + b * r * Math.cos(2 * Math.PI * a / b), - y: pos.y, - z: pos.z + b * r * Math.sin(2 * Math.PI * a / b) - }; - if (LOG_ENTITY_CREATION_MESSAGES) { - // logMessage("input params: " + JSON.stringify({ radius: r, position: pos }), COLORS.GREEN); - // logMessage("a = " + a + ", b = " + b); - logMessage("generated point: " + JSON.stringify(point), COLORS.RED); - } - return point; - } + /// Random distrib: Get a random point within a circle on the xz plane with radius r, center p. + this.randomCirclePoint = function (r, pos) { + var a = Math.random(), b = Math.random(); + if (b < a) { + var tmp = b; + b = a; + a = tmp; + } + var point = { + x: pos.x + b * r * Math.cos(2 * Math.PI * a / b), + y: pos.y, + z: pos.z + b * r * Math.sin(2 * Math.PI * a / b) + }; + if (LOG_ENTITY_CREATION_MESSAGES) { + // logMessage("input params: " + JSON.stringify({ radius: r, position: pos }), COLORS.GREEN); + // logMessage("a = " + a + ", b = " + b); + logMessage("generated point: " + JSON.stringify(point), COLORS.RED); + } + return point; + } - // Entity utils. NOT using overlayManager for... reasons >.> - var makeEntity = this.makeEntity = function (properties) { - if (LOG_ENTITY_CREATION_MESSAGES) { - logMessage("Creating entity: " + JSON.stringify(properties)); - } - var entity = Entities.addEntity(properties); - return withReadonlyProp("type", properties.type, { - update: function (properties) { - Entities.editEntity(entity, properties); - }, - destroy: function () { - Entities.deleteEntity(entity) - }, - getId: function () { - return entity; - } - }); - } - // this.makeLight = function (properties) { - // return makeEntity(withDefaults(properties, { - // type: "Light", - // isSpotlight: false, - // diffuseColor: { red: 255, green: 100, blue: 100 }, - // ambientColor: { red: 200, green: 80, blue: 80 } - // })); - // } - this.makeBox = function (properties) { - // logMessage("Creating box: " + JSON.stringify(properties)); - return makeEntity(withDefaults(properties, { - type: "Box" - })); - } + // Entity utils. NOT using overlayManager for... reasons >.> + var makeEntity = this.makeEntity = function (properties) { + if (LOG_ENTITY_CREATION_MESSAGES) { + logMessage("Creating entity: " + JSON.stringify(properties)); + } + var entity = Entities.addEntity(properties); + return withReadonlyProp("type", properties.type, { + update: function (properties) { + Entities.editEntity(entity, properties); + }, + destroy: function () { + Entities.deleteEntity(entity) + }, + getId: function () { + return entity; + } + }); + } + // this.makeLight = function (properties) { + // return makeEntity(withDefaults(properties, { + // type: "Light", + // isSpotlight: false, + // diffuseColor: { red: 255, green: 100, blue: 100 }, + // ambientColor: { red: 200, green: 80, blue: 80 } + // })); + // } + this.makeBox = function (properties) { + // logMessage("Creating box: " + JSON.stringify(properties)); + return makeEntity(withDefaults(properties, { + type: "Box" + })); + } })(); // Platform (function () { - /// Encapsulates a platform 'piece'. Owns an entity (`box`), and handles destruction and some other state. - var PlatformComponent = this.PlatformComponent = function (properties) { - // logMessage("Platform component initialized with " + Object.keys(properties), COLORS.GREEN); - this.position = properties.position || null; - this.color = properties.color || null; - this.dimensions = properties.dimensions || null; - this.entityType = properties.type || "Box"; + /// Encapsulates a platform 'piece'. Owns an entity (`box`), and handles destruction and some other state. + var PlatformComponent = this.PlatformComponent = function (properties) { + // logMessage("Platform component initialized with " + Object.keys(properties), COLORS.GREEN); + this.position = properties.position || null; + this.color = properties.color || null; + this.dimensions = properties.dimensions || null; + this.entityType = properties.type || "Box"; - // logMessage("Spawning with type: '" + this.entityType + "' (properties.type = '" + properties.type + "')", COLORS.GREEN); + // logMessage("Spawning with type: '" + this.entityType + "' (properties.type = '" + properties.type + "')", COLORS.GREEN); - if (properties._colorSeed) - this._colorSeed = properties._colorSeed; - if (properties._shapeSeed) - this._shapeSeed = properties._shapeSeed; + if (properties._colorSeed) + this._colorSeed = properties._colorSeed; + if (properties._shapeSeed) + this._shapeSeed = properties._shapeSeed; - // logMessage("dimensions: " + JSON.stringify(this.dimensions)); - // logMessage("color: " + JSON.stringify(this.color)); + // logMessage("dimensions: " + JSON.stringify(this.dimensions)); + // logMessage("color: " + JSON.stringify(this.color)); - this.cachedEntity = null; - this.activeEntity = this.spawnEntity(this.entityType); - }; - PlatformComponent.prototype.spawnEntity = function (type) { - return makeEntity({ - type: type, - position: this.position, - dimensions: this.dimensions, - color: this.color, - lifetime: USE_ENTITY_TIMEOUTS ? ENTITY_TIMEOUT_DURATION : -1.0, - alpha: 0.5 - }); - } - if (USE_ENTITY_TIMEOUTS) { - PlatformComponent.prototype.pokeEntity = function () { - // Kinda inefficient, but there's no way to get around this :/ - var age = Entities.getEntityProperties(this.activeEntity.getId()).age; - this.activeEntity.update({ lifetime: ENTITY_TIMEOUT_DURATION + age }); - } - } else { - PlatformComponent.prototype.pokeEntity = function () {} - } - /// Updates platform to be at position p, and calls .update() with the current - /// position, color, and dimensions parameters. - PlatformComponent.prototype.update = function (position) { - if (position) - this.position = position; - // logMessage("updating with " + JSON.stringify(this)); - this.activeEntity.update(this); - } - function swap (a, b) { - var tmp = a; - a = b; - b = tmp; - } - PlatformComponent.prototype.swapEntityType = function (newType) { - if (this.entityType !== newType) { - this.entityType = newType; - // logMessage("Destroying active entity and rebuilding it (newtype = '" + newType + "')"); - if (this.activeEntity) { - this.activeEntity.destroy(); - } - this.activeEntity = this.spawnEntity(newType); - // if (this.cachedEntity && this.cachedEntity.type == newType) { - // this.cachedEntity.update({ visible: true }); - // this.activeEntity.update({ visible: false }); - // swap(this.cachedEntity, this.activeEntity); - // this.update(this.position); - // } else { - // this.activeEntity.update({ visible: false }); - // this.cachedEntity = this.activeEntity; - // this.activeEntity = spawnEntity(newType); - // } - } - } - /// Swap state with another component - PlatformComponent.prototype.swap = function (other) { - swap(this.position, other.position); - swap(this.dimensions, other.dimensions); - swap(this.color, other.color); - swap(this.entityType, other.entityType); - swap(this.activeEntity, other.activeEntity); - swap(this._colorSeed, other._colorSeed); - swap(this._shapeSeed, other._shapeSeed); - } - PlatformComponent.prototype.destroy = function () { - if (this.activeEntity) { - this.activeEntity.destroy(); - this.activeEntity = null; - } - if (this.cachedEntity) { - this.cachedEntity.destroy(); - this.cachedEntity = null; - } - } + this.cachedEntity = null; + this.activeEntity = this.spawnEntity(this.entityType); + }; + PlatformComponent.prototype.spawnEntity = function (type) { + return makeEntity({ + type: type, + position: this.position, + dimensions: this.dimensions, + color: this.color, + lifetime: USE_ENTITY_TIMEOUTS ? ENTITY_TIMEOUT_DURATION : -1.0, + alpha: 0.5 + }); + } + if (USE_ENTITY_TIMEOUTS) { + PlatformComponent.prototype.pokeEntity = function () { + // Kinda inefficient, but there's no way to get around this :/ + var age = Entities.getEntityProperties(this.activeEntity.getId()).age; + this.activeEntity.update({ lifetime: ENTITY_TIMEOUT_DURATION + age }); + } + } else { + PlatformComponent.prototype.pokeEntity = function () {} + } + /// Updates platform to be at position p, and calls .update() with the current + /// position, color, and dimensions parameters. + PlatformComponent.prototype.update = function (position) { + if (position) + this.position = position; + // logMessage("updating with " + JSON.stringify(this)); + this.activeEntity.update(this); + } + function swap (a, b) { + var tmp = a; + a = b; + b = tmp; + } + PlatformComponent.prototype.swapEntityType = function (newType) { + if (this.entityType !== newType) { + this.entityType = newType; + // logMessage("Destroying active entity and rebuilding it (newtype = '" + newType + "')"); + if (this.activeEntity) { + this.activeEntity.destroy(); + } + this.activeEntity = this.spawnEntity(newType); + // if (this.cachedEntity && this.cachedEntity.type == newType) { + // this.cachedEntity.update({ visible: true }); + // this.activeEntity.update({ visible: false }); + // swap(this.cachedEntity, this.activeEntity); + // this.update(this.position); + // } else { + // this.activeEntity.update({ visible: false }); + // this.cachedEntity = this.activeEntity; + // this.activeEntity = spawnEntity(newType); + // } + } + } + /// Swap state with another component + PlatformComponent.prototype.swap = function (other) { + swap(this.position, other.position); + swap(this.dimensions, other.dimensions); + swap(this.color, other.color); + swap(this.entityType, other.entityType); + swap(this.activeEntity, other.activeEntity); + swap(this._colorSeed, other._colorSeed); + swap(this._shapeSeed, other._shapeSeed); + } + PlatformComponent.prototype.destroy = function () { + if (this.activeEntity) { + this.activeEntity.destroy(); + this.activeEntity = null; + } + if (this.cachedEntity) { + this.cachedEntity.destroy(); + this.cachedEntity = null; + } + } - // util - function inRange (p1, p2, radius) { - return Vec3.distance(p1, p2) < Math.abs(radius); - } + // util + function inRange (p1, p2, radius) { + return Vec3.distance(p1, p2) < Math.abs(radius); + } - /// Encapsulates a moving platform that follows the avatar around (mostly). - var DynamicPlatform = this.DynamicPlatform = function (n, position, radius) { - this.position = position; - this.radius = radius; - this.randomizer = new RandomAttribModel(); - this.boxType = "Box"; - this.boxTypes = [ "Box", "Sphere" ]; + /// Encapsulates a moving platform that follows the avatar around (mostly). + var DynamicPlatform = this.DynamicPlatform = function (n, position, radius) { + this.position = position; + this.radius = radius; + this.randomizer = new RandomAttribModel(); + this.boxType = "Box"; + this.boxTypes = [ "Box", "Sphere" ]; - logMessage("Spawning " + n + " entities", COLORS.GREEN); - var boxes = this.boxes = []; - while (n > 0) { - boxes.push(this.spawnEntity()); - --n; - } - this.targetDensity = this.getEntityDensity(); - this.pendingUpdates = {}; - this.updateTimer = 0.0; + logMessage("Spawning " + n + " entities", COLORS.GREEN); + var boxes = this.boxes = []; + while (n > 0) { + boxes.push(this.spawnEntity()); + --n; + } + this.targetDensity = this.getEntityDensity(); + this.pendingUpdates = {}; + this.updateTimer = 0.0; - this.platformHeight = position.y; - this.oldPos = { x: position.x, y: position.y, z: position.z }; - this.oldRadius = radius; + this.platformHeight = position.y; + this.oldPos = { x: position.x, y: position.y, z: position.z }; + this.oldRadius = radius; - // this.sendPokes(); - } - DynamicPlatform.prototype.toString = function () { - return "[DynamicPlatform (" + this.boxes.length + " entities)]"; - } - DynamicPlatform.prototype.spawnEntity = function () { - // logMessage("Called spawn entity. this.boxType = '" + this.boxType + "'") - var properties = { position: this.randomPoint(), type: this.boxType }; - this.randomizer.randomizeShapeAndColor(properties); - return new PlatformComponent(properties); - } - DynamicPlatform.prototype.updateEntityAttribs = function () { - var _this = this; - this.setPendingUpdate('updateEntityAttribs', function () { - // logMessage("updating model", COLORS.GREEN); - _this.boxes.forEach(function (box) { - this.randomizer.updateShapeAndColor(box); - box.update(); - }, _this); - }); - } - DynamicPlatform.prototype.toggleBoxType = function () { - var _this = this; - this.setPendingUpdate('toggleBoxType', function () { - // Swap / cycle through types: find index of current type and set next type to idx+1 - for (var idx = 0; idx < _this.boxTypes.length; ++idx) { - if (_this.boxTypes[idx] === _this.boxType) { - var nextIndex = (idx + 1) % _this.boxTypes.length; - logMessage("swapping box type from '" + _this.boxType + "' to '" + _this.boxTypes[nextIndex] + "'", COLORS.GREEN); - _this.boxType = _this.boxTypes[nextIndex]; - break; - } - } - _this.boxes.forEach(function (box) { - box.swapEntityType(_this.boxType); - }, _this); - }); - } - DynamicPlatform.prototype.getBoxType = function () { - return this.boxType; - } + // this.sendPokes(); + } + DynamicPlatform.prototype.toString = function () { + return "[DynamicPlatform (" + this.boxes.length + " entities)]"; + } + DynamicPlatform.prototype.spawnEntity = function () { + // logMessage("Called spawn entity. this.boxType = '" + this.boxType + "'") + var properties = { position: this.randomPoint(), type: this.boxType }; + this.randomizer.randomizeShapeAndColor(properties); + return new PlatformComponent(properties); + } + DynamicPlatform.prototype.updateEntityAttribs = function () { + var _this = this; + this.setPendingUpdate('updateEntityAttribs', function () { + // logMessage("updating model", COLORS.GREEN); + _this.boxes.forEach(function (box) { + this.randomizer.updateShapeAndColor(box); + box.update(); + }, _this); + }); + } + DynamicPlatform.prototype.toggleBoxType = function () { + var _this = this; + this.setPendingUpdate('toggleBoxType', function () { + // Swap / cycle through types: find index of current type and set next type to idx+1 + for (var idx = 0; idx < _this.boxTypes.length; ++idx) { + if (_this.boxTypes[idx] === _this.boxType) { + var nextIndex = (idx + 1) % _this.boxTypes.length; + logMessage("swapping box type from '" + _this.boxType + "' to '" + _this.boxTypes[nextIndex] + "'", COLORS.GREEN); + _this.boxType = _this.boxTypes[nextIndex]; + break; + } + } + _this.boxes.forEach(function (box) { + box.swapEntityType(_this.boxType); + }, _this); + }); + } + DynamicPlatform.prototype.getBoxType = function () { + return this.boxType; + } - // if (USE_ENTITY_TIMEOUTS) { - // DynamicPlatform.prototype.sendPokes = function () { - // var _this = this; - // function poke () { - // logMessage("Poking entities so they don't die", COLORS.GREEN); - // _this.boxes.forEach(function (box) { - // box.pokeEntity(); - // }, _this); + // if (USE_ENTITY_TIMEOUTS) { + // DynamicPlatform.prototype.sendPokes = function () { + // var _this = this; + // function poke () { + // logMessage("Poking entities so they don't die", COLORS.GREEN); + // _this.boxes.forEach(function (box) { + // box.pokeEntity(); + // }, _this); - // if (_this.pendingUpdates['keepalive']) { - // logMessage("previous timer: " + _this.pendingUpdates['keepalive'].timer + "; new timer: " + ENTITY_REFRESH_INTERVAL) - // } - // _this.pendingUpdates['keepalive'] = { - // callback: poke, - // timer: ENTITY_REFRESH_INTERVAL, - // skippedUpdates: 0 - // }; - // // _this.setPendingUpdate('keepalive', poke); - // // _this.pendingUpdates['keepalive'].timer = ENTITY_REFRESH_INTERVAL; - // } - // poke(); - // } - // } else { - // DynamicPlatform.prototype.sendPokes = function () {}; - // } + // if (_this.pendingUpdates['keepalive']) { + // logMessage("previous timer: " + _this.pendingUpdates['keepalive'].timer + "; new timer: " + ENTITY_REFRESH_INTERVAL) + // } + // _this.pendingUpdates['keepalive'] = { + // callback: poke, + // timer: ENTITY_REFRESH_INTERVAL, + // skippedUpdates: 0 + // }; + // // _this.setPendingUpdate('keepalive', poke); + // // _this.pendingUpdates['keepalive'].timer = ENTITY_REFRESH_INTERVAL; + // } + // poke(); + // } + // } else { + // DynamicPlatform.prototype.sendPokes = function () {}; + // } - /// Queue impl that uses the update loop to limit potentially expensive updates to only execute every x seconds (default: 200 ms). - /// This is to prevent UI code from running full entity updates every 10 ms (or whatever). - DynamicPlatform.prototype.setPendingUpdate = function (name, callback) { - if (!this.pendingUpdates[name]) { - // logMessage("Queued update for " + name, COLORS.GREEN); - this.pendingUpdates[name] = { - callback: callback, - timer: 0.0, - skippedUpdates: 0 - } - } else { - // logMessage("Deferred update for " + name, COLORS.GREEN); - this.pendingUpdates[name].callback = callback; - this.pendingUpdates[name].skippedUpdates++; - // logMessage("scheduling update for \"" + name + "\" to run in " + this.pendingUpdates[name].timer + " seconds"); - } - } - /// Runs all queued updates as soon as they can execute (each one has a cooldown timer). - DynamicPlatform.prototype.processPendingUpdates = function (dt) { - for (var k in this.pendingUpdates) { - if (this.pendingUpdates[k].timer >= 0.0) - this.pendingUpdates[k].timer -= dt; + /// Queue impl that uses the update loop to limit potentially expensive updates to only execute every x seconds (default: 200 ms). + /// This is to prevent UI code from running full entity updates every 10 ms (or whatever). + DynamicPlatform.prototype.setPendingUpdate = function (name, callback) { + if (!this.pendingUpdates[name]) { + // logMessage("Queued update for " + name, COLORS.GREEN); + this.pendingUpdates[name] = { + callback: callback, + timer: 0.0, + skippedUpdates: 0 + } + } else { + // logMessage("Deferred update for " + name, COLORS.GREEN); + this.pendingUpdates[name].callback = callback; + this.pendingUpdates[name].skippedUpdates++; + // logMessage("scheduling update for \"" + name + "\" to run in " + this.pendingUpdates[name].timer + " seconds"); + } + } + /// Runs all queued updates as soon as they can execute (each one has a cooldown timer). + DynamicPlatform.prototype.processPendingUpdates = function (dt) { + for (var k in this.pendingUpdates) { + if (this.pendingUpdates[k].timer >= 0.0) + this.pendingUpdates[k].timer -= dt; - if (this.pendingUpdates[k].callback && this.pendingUpdates[k].timer < 0.0) { - // logMessage("Dispatching update for " + k); - try { - this.pendingUpdates[k].callback(); - } catch (e) { - logMessage("update for \"" + k + "\" failed: " + e, COLORS.RED); - } - this.pendingUpdates[k].timer = MAX_UPDATE_INTERVAL; - this.pendingUpdates[k].skippedUpdates = 0; - this.pendingUpdates[k].callback = null; - } else { - // logMessage("Deferred update for " + k + " for " + this.pendingUpdates[k].timer + " seconds"); - } - } - } + if (this.pendingUpdates[k].callback && this.pendingUpdates[k].timer < 0.0) { + // logMessage("Dispatching update for " + k); + try { + this.pendingUpdates[k].callback(); + } catch (e) { + logMessage("update for \"" + k + "\" failed: " + e, COLORS.RED); + } + this.pendingUpdates[k].timer = MAX_UPDATE_INTERVAL; + this.pendingUpdates[k].skippedUpdates = 0; + this.pendingUpdates[k].callback = null; + } else { + // logMessage("Deferred update for " + k + " for " + this.pendingUpdates[k].timer + " seconds"); + } + } + } - /// Updates the platform based on the avatar's current position (spawning / despawning entities as needed), - /// and calls processPendingUpdates() once this is done. - /// Does NOT have any update interval limits (it just updates every time it gets run), but these are not full - /// updates (they're incremental), so the network will not get flooded so long as the avatar is moving at a - /// normal walking / flying speed. - DynamicPlatform.prototype.updatePosition = function (dt, position) { - // logMessage("updating " + this); - position.y = this.platformHeight; - this.position = position; + /// Updates the platform based on the avatar's current position (spawning / despawning entities as needed), + /// and calls processPendingUpdates() once this is done. + /// Does NOT have any update interval limits (it just updates every time it gets run), but these are not full + /// updates (they're incremental), so the network will not get flooded so long as the avatar is moving at a + /// normal walking / flying speed. + DynamicPlatform.prototype.updatePosition = function (dt, position) { + // logMessage("updating " + this); + position.y = this.platformHeight; + this.position = position; - var toUpdate = []; - this.boxes.forEach(function (box, i) { - // if (Math.abs(box.position.y - position.y) > HEIGHT_TOLERANCE || !inRange(box, position, radius)) { - if (!inRange(box.position, this.position, this.radius)) { - toUpdate.push(i); - } - }, this); + var toUpdate = []; + this.boxes.forEach(function (box, i) { + // if (Math.abs(box.position.y - position.y) > HEIGHT_TOLERANCE || !inRange(box, position, radius)) { + if (!inRange(box.position, this.position, this.radius)) { + toUpdate.push(i); + } + }, this); - var MAX_TRIES = toUpdate.length * 8; - var tries = MAX_TRIES; - var moved = 0; - var recalcs = 0; - toUpdate.forEach(function (index) { - if ((index % 2 == 0) || tries > 0) { - do { - var randomPoint = this.randomPoint(this.position, this.radius); - ++recalcs - } while (--tries > 0 && inRange(randomPoint, this.oldPos, this.oldRadiuss)); - - if (LOG_UPDATE_STATUS_MESSAGES && tries <= 0) { - logMessage("updatePlatform() gave up after " + MAX_TRIES + " iterations (" + moved + " / " + toUpdate.length + " successful updates)", COLORS.RED); - logMessage("old pos: " + JSON.stringify(this.oldPos) + ", old radius: " + this.oldRadius); - logMessage("new pos: " + JSON.stringify(this.position) + ", new radius: " + this.radius); - } - } else { - var randomPoint = this.randomPoint(position, this.radius); - } + var MAX_TRIES = toUpdate.length * 8; + var tries = MAX_TRIES; + var moved = 0; + var recalcs = 0; + toUpdate.forEach(function (index) { + if ((index % 2 == 0) || tries > 0) { + do { + var randomPoint = this.randomPoint(this.position, this.radius); + ++recalcs + } while (--tries > 0 && inRange(randomPoint, this.oldPos, this.oldRadiuss)); + + if (LOG_UPDATE_STATUS_MESSAGES && tries <= 0) { + logMessage("updatePlatform() gave up after " + MAX_TRIES + " iterations (" + moved + " / " + toUpdate.length + " successful updates)", COLORS.RED); + logMessage("old pos: " + JSON.stringify(this.oldPos) + ", old radius: " + this.oldRadius); + logMessage("new pos: " + JSON.stringify(this.position) + ", new radius: " + this.radius); + } + } else { + var randomPoint = this.randomPoint(position, this.radius); + } - this.randomizer.randomizeShapeAndColor(this.boxes[index]); - this.boxes[index].update(randomPoint); - // this.boxes[index].setValues({ - // position: randomPoint, - // // dimensions: this.randomDimensions(), - // // color: this.randomColor() - // }); - ++moved; - }, this); - recalcs = recalcs - toUpdate.length; + this.randomizer.randomizeShapeAndColor(this.boxes[index]); + this.boxes[index].update(randomPoint); + // this.boxes[index].setValues({ + // position: randomPoint, + // // dimensions: this.randomDimensions(), + // // color: this.randomColor() + // }); + ++moved; + }, this); + recalcs = recalcs - toUpdate.length; - this.oldPos = position; - this.oldRadius = this.radius; - if (LOG_UPDATE_STATUS_MESSAGES && toUpdate.length > 0) { - logMessage("updated " + toUpdate.length + " entities w/ " + recalcs + " recalcs"); - } - } + this.oldPos = position; + this.oldRadius = this.radius; + if (LOG_UPDATE_STATUS_MESSAGES && toUpdate.length > 0) { + logMessage("updated " + toUpdate.length + " entities w/ " + recalcs + " recalcs"); + } + } - DynamicPlatform.prototype.update = function (dt, position) { - this.updatePosition(dt, position); - this.processPendingUpdates(dt); - this.sendPokes(dt); - } + DynamicPlatform.prototype.update = function (dt, position) { + this.updatePosition(dt, position); + this.processPendingUpdates(dt); + this.sendPokes(dt); + } - if (USE_ENTITY_TIMEOUTS) { - DynamicPlatform.prototype.sendPokes = function (dt) { - logMessage("starting keepalive", COLORS.GREEN); - // logMessage("dt = " + dt, COLORS.RED); - // var original = this.sendPokes; - var pokeTimer = 0.0; - this.sendPokes = function (dt) { - // logMessage("dt = " + dt); - if ((pokeTimer -= dt) < 0.0) { - // logMessage("Poking entities so they don't die", COLORS.GREEN); - this.boxes.forEach(function (box) { - box.pokeEntity(); - }, this); - pokeTimer = ENTITY_REFRESH_INTERVAL; - } else { - // logMessage("Poking entities in " + pokeTimer + " seconds"); - } - } - // logMessage("this.sendPokes === past this.sendPokes? " + (this.sendPokes === original), COLORS.GREEN); - this.sendPokes(dt); - } - } else { - DynamicPlatform.prototype.sendPokes = function () {}; - } - DynamicPlatform.prototype.getEntityCount = function () { - return this.boxes.length; - } - DynamicPlatform.prototype.getEntityCountWithRadius = function (radius) { - var est = Math.floor((radius * radius) / (this.radius * this.radius) * this.getEntityCount()); - var actual = Math.floor(Math.PI * radius * radius * this.getEntityDensity()); + if (USE_ENTITY_TIMEOUTS) { + DynamicPlatform.prototype.sendPokes = function (dt) { + logMessage("starting keepalive", COLORS.GREEN); + // logMessage("dt = " + dt, COLORS.RED); + // var original = this.sendPokes; + var pokeTimer = 0.0; + this.sendPokes = function (dt) { + // logMessage("dt = " + dt); + if ((pokeTimer -= dt) < 0.0) { + // logMessage("Poking entities so they don't die", COLORS.GREEN); + this.boxes.forEach(function (box) { + box.pokeEntity(); + }, this); + pokeTimer = ENTITY_REFRESH_INTERVAL; + } else { + // logMessage("Poking entities in " + pokeTimer + " seconds"); + } + } + // logMessage("this.sendPokes === past this.sendPokes? " + (this.sendPokes === original), COLORS.GREEN); + this.sendPokes(dt); + } + } else { + DynamicPlatform.prototype.sendPokes = function () {}; + } + DynamicPlatform.prototype.getEntityCount = function () { + return this.boxes.length; + } + DynamicPlatform.prototype.getEntityCountWithRadius = function (radius) { + var est = Math.floor((radius * radius) / (this.radius * this.radius) * this.getEntityCount()); + var actual = Math.floor(Math.PI * radius * radius * this.getEntityDensity()); - if (est != actual) { - logMessage("assert failed: getEntityCountWithRadius() -- est " + est + " != actual " + actual); - } - return est; - } - DynamicPlatform.prototype.getEntityCountWithDensity = function (density) { - return Math.floor(Math.PI * this.radius * this.radius * density); - } + if (est != actual) { + logMessage("assert failed: getEntityCountWithRadius() -- est " + est + " != actual " + actual); + } + return est; + } + DynamicPlatform.prototype.getEntityCountWithDensity = function (density) { + return Math.floor(Math.PI * this.radius * this.radius * density); + } - /// Sets the entity count to n. Don't call this directly -- use setRadius / density instead. - DynamicPlatform.prototype.setEntityCount = function (n) { - if (n > this.boxes.length) { - // logMessage("Setting entity count to " + n + " (adding " + (n - this.boxes.length) + " entities)", COLORS.GREEN); + /// Sets the entity count to n. Don't call this directly -- use setRadius / density instead. + DynamicPlatform.prototype.setEntityCount = function (n) { + if (n > this.boxes.length) { + // logMessage("Setting entity count to " + n + " (adding " + (n - this.boxes.length) + " entities)", COLORS.GREEN); - // Spawn new boxes - n = n - this.boxes.length; - for (; n > 0; --n) { - // var properties = { position: this.randomPoint() }; - // this.randomizer.randomizeShapeAndColor(properties); - // this.boxes.push(new PlatformComponent(properties)); - this.boxes.push(this.spawnEntity()); - } - } else if (n < this.boxes.length) { - // logMessage("Setting entity count to " + n + " (removing " + (this.boxes.length - n) + " entities)", COLORS.GREEN); + // Spawn new boxes + n = n - this.boxes.length; + for (; n > 0; --n) { + // var properties = { position: this.randomPoint() }; + // this.randomizer.randomizeShapeAndColor(properties); + // this.boxes.push(new PlatformComponent(properties)); + this.boxes.push(this.spawnEntity()); + } + } else if (n < this.boxes.length) { + // logMessage("Setting entity count to " + n + " (removing " + (this.boxes.length - n) + " entities)", COLORS.GREEN); - // Destroy random boxes (technically, the most recent ones, but it should be sorta random) - n = this.boxes.length - n; - for (; n > 0; --n) { - this.boxes.pop().destroy(); - } - } - } - /// Calculate the entity density based on radial surface area. - DynamicPlatform.prototype.getEntityDensity = function () { - return (this.boxes.length * 1.0) / (Math.PI * this.radius * this.radius); - } - /// Queues a setDensity update. This is expensive, so we don't call it directly from UI. - DynamicPlatform.prototype.setDensityOnNextUpdate = function (density) { - var _this = this; - this.targetDensity = density; - this.setPendingUpdate('density', function () { - _this.updateEntityDensity(density); - }); - } - DynamicPlatform.prototype.updateEntityDensity = function (density) { - this.setEntityCount(Math.floor(density * Math.PI * this.radius * this.radius)); - } - DynamicPlatform.prototype.getRadius = function () { - return this.radius; - } - /// Queues a setRadius update. This is expensive, so we don't call it directly from UI. - DynamicPlatform.prototype.setRadiusOnNextUpdate = function (radius) { - var _this = this; - this.setPendingUpdate('radius', function () { - _this.setRadius(radius); - }); - } - var DEBUG_RADIUS_RECALC = false; - DynamicPlatform.prototype.setRadius = function (radius) { - if (radius < this.radius) { // Reduce case - // logMessage("Setting radius to " + radius + " (shrink by " + (this.radius - radius) + ")", COLORS.GREEN ); - this.radius = radius; + // Destroy random boxes (technically, the most recent ones, but it should be sorta random) + n = this.boxes.length - n; + for (; n > 0; --n) { + this.boxes.pop().destroy(); + } + } + } + /// Calculate the entity density based on radial surface area. + DynamicPlatform.prototype.getEntityDensity = function () { + return (this.boxes.length * 1.0) / (Math.PI * this.radius * this.radius); + } + /// Queues a setDensity update. This is expensive, so we don't call it directly from UI. + DynamicPlatform.prototype.setDensityOnNextUpdate = function (density) { + var _this = this; + this.targetDensity = density; + this.setPendingUpdate('density', function () { + _this.updateEntityDensity(density); + }); + } + DynamicPlatform.prototype.updateEntityDensity = function (density) { + this.setEntityCount(Math.floor(density * Math.PI * this.radius * this.radius)); + } + DynamicPlatform.prototype.getRadius = function () { + return this.radius; + } + /// Queues a setRadius update. This is expensive, so we don't call it directly from UI. + DynamicPlatform.prototype.setRadiusOnNextUpdate = function (radius) { + var _this = this; + this.setPendingUpdate('radius', function () { + _this.setRadius(radius); + }); + } + var DEBUG_RADIUS_RECALC = false; + DynamicPlatform.prototype.setRadius = function (radius) { + if (radius < this.radius) { // Reduce case + // logMessage("Setting radius to " + radius + " (shrink by " + (this.radius - radius) + ")", COLORS.GREEN ); + this.radius = radius; - // Remove all entities outside of current bounds. Requires swapping, since we want to maintain a contiguous array. - // Algorithm: two pointers at front and back. We traverse fwd and back, swapping elems so that all entities in bounds - // are at the front of the array, and all entities out of bounds are at the back. We then pop + destroy all entities - // at the back to reduce the entity count. - var count = this.boxes.length; - var toDelete = 0; - var swapList = []; - if (DEBUG_RADIUS_RECALC) { - logMessage("starting at i = 0, j = " + (count - 1)); - } - for (var i = 0, j = count - 1; i < j; ) { - // Find first elem outside of bounds that we can move to the back - while (inRange(this.boxes[i].position, this.position, this.radius) && i < j) { - ++i; - } - // Find first elem in bounds that we can move to the front - while (!inRange(this.boxes[j].position, this.position, this.radius) && i < j) { - --j; ++toDelete; - } - if (i < j) { - // swapList.push([i, j]); - if (DEBUG_RADIUS_RECALC) { - logMessage("swapping " + i + ", " + j); - } - this.boxes[i].swap(this.boxes[j]); - ++i, --j; ++toDelete; - } else { - if (DEBUG_RADIUS_RECALC) { - logMessage("terminated at i = " + i + ", j = " + j, COLORS.RED); - } - } - } - if (DEBUG_RADIUS_RECALC) { - logMessage("toDelete = " + toDelete, COLORS.RED); - } - // Sanity check - if (toDelete > this.boxes.length) { - logMessage("Error: toDelete " + toDelete + " > entity count " + this.boxes.length + " (setRadius algorithm)", COLORS.RED); - toDelete = this.boxes.length; - } - if (toDelete > 0) { - // logMessage("Deleting " + toDelete + " entities as part of radius resize", COLORS.GREEN); - } - // Delete cleared boxes - for (; toDelete > 0; --toDelete) { - this.boxes.pop().destroy(); - } - // fix entity density (just in case -- we may have uneven entity distribution) - this.updateEntityDensity(this.targetDensity); - } else if (radius > this.radius) { - // Grow case (much simpler) - // logMessage("Setting radius to " + radius + " (grow by " + (radius - this.radius) + ")", COLORS.GREEN); + // Remove all entities outside of current bounds. Requires swapping, since we want to maintain a contiguous array. + // Algorithm: two pointers at front and back. We traverse fwd and back, swapping elems so that all entities in bounds + // are at the front of the array, and all entities out of bounds are at the back. We then pop + destroy all entities + // at the back to reduce the entity count. + var count = this.boxes.length; + var toDelete = 0; + var swapList = []; + if (DEBUG_RADIUS_RECALC) { + logMessage("starting at i = 0, j = " + (count - 1)); + } + for (var i = 0, j = count - 1; i < j; ) { + // Find first elem outside of bounds that we can move to the back + while (inRange(this.boxes[i].position, this.position, this.radius) && i < j) { + ++i; + } + // Find first elem in bounds that we can move to the front + while (!inRange(this.boxes[j].position, this.position, this.radius) && i < j) { + --j; ++toDelete; + } + if (i < j) { + // swapList.push([i, j]); + if (DEBUG_RADIUS_RECALC) { + logMessage("swapping " + i + ", " + j); + } + this.boxes[i].swap(this.boxes[j]); + ++i, --j; ++toDelete; + } else { + if (DEBUG_RADIUS_RECALC) { + logMessage("terminated at i = " + i + ", j = " + j, COLORS.RED); + } + } + } + if (DEBUG_RADIUS_RECALC) { + logMessage("toDelete = " + toDelete, COLORS.RED); + } + // Sanity check + if (toDelete > this.boxes.length) { + logMessage("Error: toDelete " + toDelete + " > entity count " + this.boxes.length + " (setRadius algorithm)", COLORS.RED); + toDelete = this.boxes.length; + } + if (toDelete > 0) { + // logMessage("Deleting " + toDelete + " entities as part of radius resize", COLORS.GREEN); + } + // Delete cleared boxes + for (; toDelete > 0; --toDelete) { + this.boxes.pop().destroy(); + } + // fix entity density (just in case -- we may have uneven entity distribution) + this.updateEntityDensity(this.targetDensity); + } else if (radius > this.radius) { + // Grow case (much simpler) + // logMessage("Setting radius to " + radius + " (grow by " + (radius - this.radius) + ")", COLORS.GREEN); - // Add entities based on entity density - // var density = this.getEntityDensity(); - var density = this.targetDensity; - var oldArea = Math.PI * this.radius * this.radius; - var n = Math.floor(density * Math.PI * (radius * radius - this.radius * this.radius)); + // Add entities based on entity density + // var density = this.getEntityDensity(); + var density = this.targetDensity; + var oldArea = Math.PI * this.radius * this.radius; + var n = Math.floor(density * Math.PI * (radius * radius - this.radius * this.radius)); - if (n > 0) { - // logMessage("Adding " + n + " entities", COLORS.GREEN); + if (n > 0) { + // logMessage("Adding " + n + " entities", COLORS.GREEN); - // Add entities (we use a slightly different algorithm to place them in the area between two concentric circles. - // This is *slightly* less uniform (the reason we're not using this everywhere is entities would be tightly clustered - // at the platform center and become spread out as the radius increases), but the use-case here is just incremental - // radius resizes and the user's not likely to notice the difference). - for (; n > 0; --n) { - var theta = Math.randRange(0.0, Math.PI * 2.0); - var r = Math.randRange(this.radius, radius); - // logMessage("theta = " + theta + ", r = " + r); - var pos = { - x: Math.cos(theta) * r + this.position.x, - y: this.position.y, - z: Math.sin(theta) * r + this.position.y - }; + // Add entities (we use a slightly different algorithm to place them in the area between two concentric circles. + // This is *slightly* less uniform (the reason we're not using this everywhere is entities would be tightly clustered + // at the platform center and become spread out as the radius increases), but the use-case here is just incremental + // radius resizes and the user's not likely to notice the difference). + for (; n > 0; --n) { + var theta = Math.randRange(0.0, Math.PI * 2.0); + var r = Math.randRange(this.radius, radius); + // logMessage("theta = " + theta + ", r = " + r); + var pos = { + x: Math.cos(theta) * r + this.position.x, + y: this.position.y, + z: Math.sin(theta) * r + this.position.y + }; - // var properties = { position: pos }; - // this.randomizer.randomizeShapeAndColor(properties); - // this.boxes.push(new PlatformComponent(properties)); - this.boxes.push(this.spawnEntity()); - } - } - this.radius = radius; - } - } - DynamicPlatform.prototype.updateHeight = function (height) { - logMessage("Setting platform height to " + height); - this.platformHeight = height; + // var properties = { position: pos }; + // this.randomizer.randomizeShapeAndColor(properties); + // this.boxes.push(new PlatformComponent(properties)); + this.boxes.push(this.spawnEntity()); + } + } + this.radius = radius; + } + } + DynamicPlatform.prototype.updateHeight = function (height) { + logMessage("Setting platform height to " + height); + this.platformHeight = height; - // Invalidate current boxes to trigger a rebuild - this.boxes.forEach(function (box) { - box.position.x += this.oldRadius * 100; - }); - // this.update(dt, position, radius); - } - /// Gets a random point within the platform bounds. - /// Should maybe get moved to the RandomAttribModel (would be much cleaner), but this works for now. - DynamicPlatform.prototype.randomPoint = function (position, radius) { - position = position || this.position; - radius = radius !== undefined ? radius : this.radius; - return randomCirclePoint(radius, position); - } - /// Old. The RandomAttribModel replaces this and enables realtime editing of the *****_RANGE params. - // DynamicPlatform.prototype.randomDimensions = function () { - // return { - // x: Math.randRange(WIDTH_RANGE[0], WIDTH_RANGE[1]), - // y: Math.randRange(HEIGHT_RANGE[0], HEIGHT_RANGE[1]), - // z: Math.randRange(DEPTH_RANGE[0], DEPTH_RANGE[1]) - // }; - // } - // DynamicPlatform.prototype.randomColor = function () { - // var shade = Math.randRange(SHADE_RANGE[0], SHADE_RANGE[1]); - // // var h = HUE_RANGE; - // return { - // red: shade + Math.randRange(RED_RANGE[0], RED_RANGE[1]) | 0, - // green: shade + Math.randRange(GREEN_RANGE[0], GREEN_RANGE[1]) | 0, - // blue: shade + Math.randRange(BLUE_RANGE[0], BLUE_RANGE[1]) | 0 - // } - // // return COLORS[Math.randInt(COLORS.length)] - // } + // Invalidate current boxes to trigger a rebuild + this.boxes.forEach(function (box) { + box.position.x += this.oldRadius * 100; + }); + // this.update(dt, position, radius); + } + /// Gets a random point within the platform bounds. + /// Should maybe get moved to the RandomAttribModel (would be much cleaner), but this works for now. + DynamicPlatform.prototype.randomPoint = function (position, radius) { + position = position || this.position; + radius = radius !== undefined ? radius : this.radius; + return randomCirclePoint(radius, position); + } + /// Old. The RandomAttribModel replaces this and enables realtime editing of the *****_RANGE params. + // DynamicPlatform.prototype.randomDimensions = function () { + // return { + // x: Math.randRange(WIDTH_RANGE[0], WIDTH_RANGE[1]), + // y: Math.randRange(HEIGHT_RANGE[0], HEIGHT_RANGE[1]), + // z: Math.randRange(DEPTH_RANGE[0], DEPTH_RANGE[1]) + // }; + // } + // DynamicPlatform.prototype.randomColor = function () { + // var shade = Math.randRange(SHADE_RANGE[0], SHADE_RANGE[1]); + // // var h = HUE_RANGE; + // return { + // red: shade + Math.randRange(RED_RANGE[0], RED_RANGE[1]) | 0, + // green: shade + Math.randRange(GREEN_RANGE[0], GREEN_RANGE[1]) | 0, + // blue: shade + Math.randRange(BLUE_RANGE[0], BLUE_RANGE[1]) | 0 + // } + // // return COLORS[Math.randInt(COLORS.length)] + // } - /// Cleanup. - DynamicPlatform.prototype.destroy = function () { - this.boxes.forEach(function (box) { - box.destroy(); - }); - this.boxes = []; - } + /// Cleanup. + DynamicPlatform.prototype.destroy = function () { + this.boxes.forEach(function (box) { + box.destroy(); + }); + this.boxes = []; + } })(); // UI (function () { - var CATCH_SETUP_ERRORS = true; + var CATCH_SETUP_ERRORS = true; - // Util functions for setting up widgets (the widget library is intended to be used like this) - function makePanel (dir, properties) { - return new UI.WidgetStack(withDefaults(properties, { - dir: dir - })); - } - function addSpacing (parent, width, height) { - parent.add(new UI.Box({ - backgroundAlpha: 0.0, - width: width, height: height - })); - } - function addLabel (parent, text) { - return parent.add(new UI.Label({ - text: text, - width: 200, - height: 20 - })); - } - function addSlider (parent, label, min, max, getValue, onValueChanged) { - try { - var layout = parent.add(new UI.WidgetStack({ dir: "+x" })); - var textLabel = layout.add(new UI.Label({ - text: label, - width: 130, - height: 20 - })); - var valueLabel = layout.add(new UI.Label({ - text: "" + (+getValue().toFixed(1)), - width: 60, - height: 20 - })); - var slider = layout.add(new UI.Slider({ - value: getValue(), minValue: min, maxValue: max, - width: 300, height: 20, - slider: { - width: 30, - height: 18 - }, - onValueChanged: function (value) { - valueLabel.setText("" + (+value.toFixed(1))); - onValueChanged(value, slider); - UI.updateLayout(); - } - })); - return slider; - } catch (e) { - logMessage("" + e, COLORS.RED); - logMessage("parent: " + parent, COLORS.RED); - logMessage("label: " + label, COLORS.RED); - logMessage("min: " + min, COLORS.RED); - logMessage("max: " + max, COLORS.RED); - logMessage("getValue: " + getValue, COLORS.RED); - logMessage("onValueChanged: " + onValueChanged, COLORS.RED); - throw e; - } - } - function addButton (parent, label, onClicked) { - var button = parent.add(new UI.Box({ - text: label, - width: 160, - height: 26, - leftMargin: 8, - topMargin: 3 - })); - button.addAction('onClick', onClicked); - return button; - } - function moveToBottomLeftScreenCorner (widget) { - var border = 5; - var pos = { - x: border, - y: Controller.getViewportDimensions().y - widget.getHeight() - border - }; - if (widget.position.x != pos.x || widget.position.y != pos.y) { - widget.setPosition(pos.x, pos.y); - UI.updateLayout(); - } - } - var _export = this; + // Util functions for setting up widgets (the widget library is intended to be used like this) + function makePanel (dir, properties) { + return new UI.WidgetStack(withDefaults(properties, { + dir: dir + })); + } + function addSpacing (parent, width, height) { + parent.add(new UI.Box({ + backgroundAlpha: 0.0, + width: width, height: height + })); + } + function addLabel (parent, text) { + return parent.add(new UI.Label({ + text: text, + width: 200, + height: 20 + })); + } + function addSlider (parent, label, min, max, getValue, onValueChanged) { + try { + var layout = parent.add(new UI.WidgetStack({ dir: "+x" })); + var textLabel = layout.add(new UI.Label({ + text: label, + width: 130, + height: 20 + })); + var valueLabel = layout.add(new UI.Label({ + text: "" + (+getValue().toFixed(1)), + width: 60, + height: 20 + })); + var slider = layout.add(new UI.Slider({ + value: getValue(), minValue: min, maxValue: max, + width: 300, height: 20, + slider: { + width: 30, + height: 18 + }, + onValueChanged: function (value) { + valueLabel.setText("" + (+value.toFixed(1))); + onValueChanged(value, slider); + UI.updateLayout(); + } + })); + return slider; + } catch (e) { + logMessage("" + e, COLORS.RED); + logMessage("parent: " + parent, COLORS.RED); + logMessage("label: " + label, COLORS.RED); + logMessage("min: " + min, COLORS.RED); + logMessage("max: " + max, COLORS.RED); + logMessage("getValue: " + getValue, COLORS.RED); + logMessage("onValueChanged: " + onValueChanged, COLORS.RED); + throw e; + } + } + function addButton (parent, label, onClicked) { + var button = parent.add(new UI.Box({ + text: label, + width: 160, + height: 26, + leftMargin: 8, + topMargin: 3 + })); + button.addAction('onClick', onClicked); + return button; + } + function moveToBottomLeftScreenCorner (widget) { + var border = 5; + var pos = { + x: border, + y: Controller.getViewportDimensions().y - widget.getHeight() - border + }; + if (widget.position.x != pos.x || widget.position.y != pos.y) { + widget.setPosition(pos.x, pos.y); + UI.updateLayout(); + } + } + var _export = this; - /// Setup the UI. Creates a bunch of sliders for setting the platform radius, density, and entity color / shape properties. - /// The entityCount slider is readonly. - function _setupUI (platform) { - var layoutContainer = makePanel("+y", { visible: true }); - // layoutContainer.setPosition(10, 280); - // makeDraggable(layoutContainer); - _export.onScreenResize = function () { - moveToBottomLeftScreenCorner(layoutContainer); - } - var topSection = layoutContainer.add(makePanel("+x")); addSpacing(layoutContainer, 1, 5); - var btmSection = layoutContainer.add(makePanel("+x")); + /// Setup the UI. Creates a bunch of sliders for setting the platform radius, density, and entity color / shape properties. + /// The entityCount slider is readonly. + function _setupUI (platform) { + var layoutContainer = makePanel("+y", { visible: false }); + // layoutContainer.setPosition(10, 280); + // makeDraggable(layoutContainer); + _export.onScreenResize = function () { + moveToBottomLeftScreenCorner(layoutContainer); + } + var topSection = layoutContainer.add(makePanel("+x")); addSpacing(layoutContainer, 1, 5); + var btmSection = layoutContainer.add(makePanel("+x")); - var controls = topSection.add(makePanel("+y")); addSpacing(topSection, 20, 1); - var buttons = topSection.add(makePanel("+y")); addSpacing(topSection, 20, 1); + var controls = topSection.add(makePanel("+y")); addSpacing(topSection, 20, 1); + var buttons = topSection.add(makePanel("+y")); addSpacing(topSection, 20, 1); - var colorControls = btmSection.add(makePanel("+y")); addSpacing(btmSection, 20, 1); - var shapeControls = btmSection.add(makePanel("+y")); addSpacing(btmSection, 20, 1); + var colorControls = btmSection.add(makePanel("+y")); addSpacing(btmSection, 20, 1); + var shapeControls = btmSection.add(makePanel("+y")); addSpacing(btmSection, 20, 1); - // Top controls - addLabel(controls, "Platform (platform.js)"); - controls.radiusSlider = addSlider(controls, "radius", PLATFORM_RADIUS_RANGE[0], PLATFORM_RADIUS_RANGE[1], function () { return platform.getRadius() }, - function (value) { - platform.setRadiusOnNextUpdate(value); - controls.entityCountSlider.setValue(platform.getEntityCountWithRadius(value)); - }); - addSpacing(controls, 1, 2); - controls.densitySlider = addSlider(controls, "entity density", PLATFORM_DENSITY_RANGE[0], PLATFORM_DENSITY_RANGE[1], function () { return platform.getEntityDensity() }, - function (value) { - platform.setDensityOnNextUpdate(value); - controls.entityCountSlider.setValue(platform.getEntityCountWithDensity(value)); - }); - addSpacing(controls, 1, 2); + // Top controls + addLabel(controls, "Platform (platform.js)"); + controls.radiusSlider = addSlider(controls, "radius", PLATFORM_RADIUS_RANGE[0], PLATFORM_RADIUS_RANGE[1], function () { return platform.getRadius() }, + function (value) { + platform.setRadiusOnNextUpdate(value); + controls.entityCountSlider.setValue(platform.getEntityCountWithRadius(value)); + }); + addSpacing(controls, 1, 2); + controls.densitySlider = addSlider(controls, "entity density", PLATFORM_DENSITY_RANGE[0], PLATFORM_DENSITY_RANGE[1], function () { return platform.getEntityDensity() }, + function (value) { + platform.setDensityOnNextUpdate(value); + controls.entityCountSlider.setValue(platform.getEntityCountWithDensity(value)); + }); + addSpacing(controls, 1, 2); - var minEntities = Math.PI * PLATFORM_RADIUS_RANGE[0] * PLATFORM_RADIUS_RANGE[0] * PLATFORM_DENSITY_RANGE[0]; - var maxEntities = Math.PI * PLATFORM_RADIUS_RANGE[1] * PLATFORM_RADIUS_RANGE[1] * PLATFORM_DENSITY_RANGE[1]; - controls.entityCountSlider = addSlider(controls, "entity count", minEntities, maxEntities, function () { return platform.getEntityCount() }, - function (value) {}); - controls.entityCountSlider.actions = {}; // hack: make this slider readonly (clears all attached actions) - controls.entityCountSlider.slider.actions = {}; + var minEntities = Math.PI * PLATFORM_RADIUS_RANGE[0] * PLATFORM_RADIUS_RANGE[0] * PLATFORM_DENSITY_RANGE[0]; + var maxEntities = Math.PI * PLATFORM_RADIUS_RANGE[1] * PLATFORM_RADIUS_RANGE[1] * PLATFORM_DENSITY_RANGE[1]; + controls.entityCountSlider = addSlider(controls, "entity count", minEntities, maxEntities, function () { return platform.getEntityCount() }, + function (value) {}); + controls.entityCountSlider.actions = {}; // hack: make this slider readonly (clears all attached actions) + controls.entityCountSlider.slider.actions = {}; - // Buttons - addSpacing(buttons, 1, 22); - addButton(buttons, 'rebuild', function () { - platform.updateHeight(MyAvatar.position.y - AVATAR_HEIGHT_OFFSET); - }); - addSpacing(buttons, 1, 2); - addButton(buttons, 'toggle entity type', function () { - platform.toggleBoxType(); - }); - - // Bottom controls + // Buttons + addSpacing(buttons, 1, 22); + addButton(buttons, 'rebuild', function () { + platform.updateHeight(MyAvatar.position.y - AVATAR_HEIGHT_OFFSET); + }); + addSpacing(buttons, 1, 2); + addButton(buttons, 'toggle entity type', function () { + platform.toggleBoxType(); + }); + + // Bottom controls - // Iterate over controls (making sliders) for the RNG shape / dimensions model - platform.randomizer.shapeModel.setupUI(function (name, value, min, max, setValue) { - // logMessage("platform.randomizer.shapeModel." + name + " = " + value); - var internal = { - avg: (value[0] + value[1]) * 0.5, - range: Math.abs(value[0] - value[1]) - }; - // logMessage(JSON.stringify(internal), COLORS.GREEN); - addSlider(shapeControls, name + ' avg', min, max, function () { return internal.avg; }, function (value) { - internal.avg = value; - setValue([ internal.avg - internal.range * 0.5, internal.avg + internal.range * 0.5 ]); - platform.updateEntityAttribs(); - }); - addSpacing(shapeControls, 1, 2); - addSlider(shapeControls, name + ' range', min, max, function () { return internal.range }, function (value) { - internal.range = value; - setValue([ internal.avg - internal.range * 0.5, internal.avg + internal.range * 0.5 ]); - platform.updateEntityAttribs(); - }); - addSpacing(shapeControls, 1, 2); - }); - // Do the same for the color model - platform.randomizer.colorModel.setupUI(function (name, value, min, max, setValue) { - // logMessage("platform.randomizer.colorModel." + name + " = " + value); - addSlider(colorControls, name, min, max, function () { return value; }, function (value) { - setValue(value); - platform.updateEntityAttribs(); - }); - addSpacing(colorControls, 1, 2); - }); - - moveToBottomLeftScreenCorner(layoutContainer); - layoutContainer.setVisible(true); - } - this.setupUI = function (platform) { - if (CATCH_SETUP_ERRORS) { - try { - _setupUI(platform); - } catch (e) { - logMessage("Error setting up ui: " + e, COLORS.RED); - } - } else { - _setupUI(platform); - } - } + // Iterate over controls (making sliders) for the RNG shape / dimensions model + platform.randomizer.shapeModel.setupUI(function (name, value, min, max, setValue) { + // logMessage("platform.randomizer.shapeModel." + name + " = " + value); + var internal = { + avg: (value[0] + value[1]) * 0.5, + range: Math.abs(value[0] - value[1]) + }; + // logMessage(JSON.stringify(internal), COLORS.GREEN); + addSlider(shapeControls, name + ' avg', min, max, function () { return internal.avg; }, function (value) { + internal.avg = value; + setValue([ internal.avg - internal.range * 0.5, internal.avg + internal.range * 0.5 ]); + platform.updateEntityAttribs(); + }); + addSpacing(shapeControls, 1, 2); + addSlider(shapeControls, name + ' range', min, max, function () { return internal.range }, function (value) { + internal.range = value; + setValue([ internal.avg - internal.range * 0.5, internal.avg + internal.range * 0.5 ]); + platform.updateEntityAttribs(); + }); + addSpacing(shapeControls, 1, 2); + }); + // Do the same for the color model + platform.randomizer.colorModel.setupUI(function (name, value, min, max, setValue) { + // logMessage("platform.randomizer.colorModel." + name + " = " + value); + addSlider(colorControls, name, min, max, function () { return value; }, function (value) { + setValue(value); + platform.updateEntityAttribs(); + }); + addSpacing(colorControls, 1, 2); + }); + + moveToBottomLeftScreenCorner(layoutContainer); + layoutContainer.setVisible(true); + } + this.setupUI = function (platform) { + if (CATCH_SETUP_ERRORS) { + try { + _setupUI(platform); + } catch (e) { + logMessage("Error setting up ui: " + e, COLORS.RED); + } + } else { + _setupUI(platform); + } + } })(); // Error handling w/ explicit try / catch blocks. Good for catching unexpected errors with the onscreen debugLog @@ -1092,119 +1092,130 @@ var CATCH_ERRORS_FROM_EVENT_UPDATES = false; // Setup everything (function () { - var doLater = null; - if (CATCH_ERRORS_FROM_EVENT_UPDATES) { - // Decorates a function w/ explicit error catching + printing to the debug log. - function catchErrors (fcn) { - return function () { - try { - fcn.apply(this, arguments); - } catch (e) { - logMessage('' + e, COLORS.RED); - logMessage("while calling " + fcn); - logMessage("Called by: " + arguments.callee.caller); - } - } - } - // We need to do this after the functions are registered... - doLater = function () { - // Intercept errors from functions called by Script.update and Script.ScriptEnding. - [ 'teardown', 'startup', 'update', 'initPlatform', 'setupUI' ].forEach(function (fcn) { - this[fcn] = catchErrors(this[fcn]); - }); - }; - // These need to be wrapped first though: + var doLater = null; + if (CATCH_ERRORS_FROM_EVENT_UPDATES) { + // Decorates a function w/ explicit error catching + printing to the debug log. + function catchErrors (fcn) { + return function () { + try { + fcn.apply(this, arguments); + } catch (e) { + logMessage('' + e, COLORS.RED); + logMessage("while calling " + fcn); + logMessage("Called by: " + arguments.callee.caller); + } + } + } + // We need to do this after the functions are registered... + doLater = function () { + // Intercept errors from functions called by Script.update and Script.ScriptEnding. + [ 'teardown', 'startup', 'update', 'initPlatform', 'setupUI' ].forEach(function (fcn) { + this[fcn] = catchErrors(this[fcn]); + }); + }; + // These need to be wrapped first though: - // Intercept errors from UI functions called by Controller.****Event. - [ 'handleMousePress', 'handleMouseMove', 'handleMouseRelease' ].forEach(function (fcn) { - UI[fcn] = catchErrors(UI[fcn]); - }); - } + // Intercept errors from UI functions called by Controller.****Event. + [ 'handleMousePress', 'handleMouseMove', 'handleMouseRelease' ].forEach(function (fcn) { + UI[fcn] = catchErrors(UI[fcn]); + }); + } - function getTargetPlatformPosition () { - var pos = MyAvatar.position; - pos.y -= AVATAR_HEIGHT_OFFSET; - return pos; - } + function getTargetPlatformPosition () { + var pos = MyAvatar.position; + pos.y -= AVATAR_HEIGHT_OFFSET; + return pos; + } - // Program state - var platform = this.platform = null; - var lastHeight = null; + // Program state + var platform = this.platform = null; + var lastHeight = null; - // Init - this.initPlatform = function () { - platform = new DynamicPlatform(NUM_PLATFORM_ENTITIES, getTargetPlatformPosition(), RADIUS); - lastHeight = getTargetPlatformPosition().y; - } + // Init + this.initPlatform = function () { + platform = new DynamicPlatform(NUM_PLATFORM_ENTITIES, getTargetPlatformPosition(), RADIUS); + lastHeight = getTargetPlatformPosition().y; + } - // Handle relative screen positioning (UI) - var lastDimensions = Controller.getViewportDimensions(); - function checkScreenDimensions () { - var dimensions = Controller.getViewportDimensions(); - if (dimensions.x != lastDimensions.x || dimensions.y != lastDimensions.y) { - onScreenResize(dimensions.x, dimensions.y); - } - lastDimensions = dimensions; - } + // Handle relative screen positioning (UI) + var lastDimensions = Controller.getViewportDimensions(); + function checkScreenDimensions () { + var dimensions = Controller.getViewportDimensions(); + if (dimensions.x != lastDimensions.x || dimensions.y != lastDimensions.y) { + onScreenResize(dimensions.x, dimensions.y); + } + lastDimensions = dimensions; + } - // Update - this.update = function (dt) { - checkScreenDimensions(); - var pos = getTargetPlatformPosition(); - platform.update(dt, getTargetPlatformPosition(), platform.getRadius()); - } + // Update + this.update = function (dt) { + checkScreenDimensions(); + var pos = getTargetPlatformPosition(); + platform.update(dt, getTargetPlatformPosition(), platform.getRadius()); + } - // Teardown - this.teardown = function () { - try { - platform.destroy(); - UI.teardown(); + // Teardown + this.teardown = function () { + try { + platform.destroy(); + UI.teardown(); - Controller.mousePressEvent.disconnect(UI.handleMousePress); - Controller.mouseMoveEvent.disconnect(UI.handleMouseMove); - Controller.mouseReleaseEvent.disconnect(UI.handleMouseRelease); - } catch (e) { - logMessage("" + e, COLORS.RED); - } - } + Controller.mousePressEvent.disconnect(UI.handleMousePress); + Controller.mouseMoveEvent.disconnect(UI.handleMouseMove); + Controller.mouseReleaseEvent.disconnect(UI.handleMouseRelease); + } catch (e) { + logMessage("" + e, COLORS.RED); + } + } - if (doLater) { - doLater(); - } + if (doLater) { + doLater(); + } - // Delays startup until / if entities can be spawned. - this.startup = function () { - if (Entities.canAdjustLocks() && Entities.canRez()) { - Script.update.disconnect(this.startup); + // Delays startup until / if entities can be spawned. + this.startup = function (dt) { + if (Entities.canAdjustLocks() && Entities.canRez()) { + Script.update.disconnect(this.startup); - function init () { - logMessage("initializing..."); - - this.initPlatform(); + function init () { + logMessage("initializing..."); + + this.initPlatform(); - Script.update.connect(this.update); - Script.scriptEnding.connect(this.teardown); - - this.setupUI(platform); - - logMessage("finished initializing.", COLORS.GREEN); - } - if (CATCH_INIT_ERRORS) { - try { - init(); - } catch (error) { - logMessage("" + error, COLORS.RED); - } - } else { - init(); - } + Script.update.connect(this.update); + Script.scriptEnding.connect(this.teardown); + + this.setupUI(platform); + + logMessage("finished initializing.", COLORS.GREEN); + } + if (CATCH_INIT_ERRORS) { + try { + init(); + } catch (error) { + logMessage("" + error, COLORS.RED); + } + } else { + init(); + } - Controller.mousePressEvent.connect(UI.handleMousePress); - Controller.mouseMoveEvent.connect(UI.handleMouseMove); - Controller.mouseReleaseEvent.connect(UI.handleMouseRelease); - } - } - Script.update.connect(this.startup); + Controller.mousePressEvent.connect(UI.handleMousePress); + Controller.mouseMoveEvent.connect(UI.handleMouseMove); + Controller.mouseReleaseEvent.connect(UI.handleMouseRelease); + } else { + if (!startup.printedWarnMsg) { + startup.timer = startup.timer || startup.ENTITY_SERVER_WAIT_TIME; + if ((startup.timer -= dt) < 0.0) { + logMessage("Waiting for entity server"); + startup.printedWarnMsg = true; + } + + } + } + } + startup.ENTITY_SERVER_WAIT_TIME = 0.2; // print "waiting for entity server" if more than this time has elapsed in startup() + + Script.update.connect(this.startup); })(); })();