// ACmoneyTree.js // Created by Mark Brosche on 10-18-2018 // 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 // /* global EventBridge Users AccountServices Agent Avatar EntityViewer */ (function(){ var googleURL = "https://script.google.com/a/highfidelity.io/macros/s/AKfycbxvLCGJwYKGOlszXwm2m-Xa1U90RmnIEJiD6nD7Z8-1RlZaADY/exec"; var request = Script.require('./modules/request.js').request; // The Metaverse to use var HIFI_METAVERSE_URL = "https://highfidelity.com"; // The Recent Economic Activity endpoint on the High Fidelity Metaverse var HIFI_HISTORY_ENDPOINT_URL = HIFI_METAVERSE_URL + "/api/v1/commerce/history"; // The Redmption endpoint on the High Fidelity Metaverse var HIFI_REDEMPTION_ENDPOINT_URL = HIFI_METAVERSE_URL + "/api/v1/commerce/redeem"; var AVERAGE_INTERVAL = 1.5, // one and a half minutes AVERAGE_HFC_AMOUNT = 10, STANDARD_DEVIATION = 5, SHOW_TIME_LENGTH = 60000, // One minute RECIPIENT_MAX = 3, MONEY_TREE_CHANNEL = "MoneyTreeChannel", AC_SCRIPT_RUN = true, SEARCH_CENTER = {"x": 0,"y":0,"z":0}, SEARCH_AREA= 1000; var counterText, activeText, targetGiver = [], targetRecipients = [], payOnce = false, randomHFCAmount = null, userList= [], coin = [], interval = null, indexGiver, bankers = [], treeOn = true, tempList, messageHandler, power, octreeInterval; var approvedBankerList = [ "markb", "ryan" ]; var staffList = [ "b", "Jazmin", "marko8904", "mhard", "theo", "adamisawake", "Aitolda", "Alan_", "alt.howard", "amantley", "amer", "amukul", "AndrewNyan", "andy_batman", "antoninas", "asbecker8", "Ashleigh_Harris", "Becky", "BenB", "bimyou", "birarda", "c", "Caitlyn", "ChangX", "Cheetah_Pattern", "Clement", "dantescalves", "dback", "diogenes", "dogbones", "emily", "Emily_Sykes", "emilythethird", "Firebird25", "freidrica", "GoofyGoober", "hifiDave", "hifi_jamil", "HifiJaz", "hifimarko", "HiFiMicki", "High.Fidelity", "howard.stearns", "huffman", "huffman2", "hyperlogic", "ingerjm0", "JeffClinton", "Jess", "Jherico", "john.highfidelity", "JSON", "jyoum", "Kayla_HF", "kencooke", "kkurian", "lameboycool", "leviathan", "luiscuenca", "markb", "maspring", "miladn", "mold", "MissLiviRose", "nbaldassini", "Nik", "Nissim", "nvrndr", "OneLisa", "ovohightower", "philip", "r3tk0n", "realMissLiviRose", "rickcarr044", "Roxie", "Sabrina", "sam", "SamGondelman", "sbbsteam", "sbirarda", "Sean", "seanjones2848", "serling", "sethalves", "shanzam", "simon_walton", "sophocles", "TheBanker", "thoys", "WadeWatts", "wayne", "yoHighness", "Ypsilanti", "zack_hifi", "ZappoMan", "zfox", "zfox1", "zeenai", "Battery", "conference2", "conference3", "demo1", "demo2", "Langton", "market", "purchases", "Anton.von.Trier", "kitchen", "mic1", "mic2", "ejahner", "Freyja", "qligmaloney", "userTest", "highfidelity", "ryan", "siphnos", "ephesos", "pytho", "skelina" ]; if (AC_SCRIPT_RUN) { Agent.isAvatar = true; Avatar.skeletonModelURL = 'http://hifi-content.s3.amazonaws.com/ozan/dev/avatars/invisible_avatar/invisible_avatar.fst'; Avatar.displayName = "Money Tree Agent"; Avatar.position = {"x":-19.109256744384766,"y":-20.8349714279174805,"z":-11.181184768676758}; // Tree Position var initialized = false; var update = function(deltaTime) { if (!initialized) { if (Entities.serversExist() && Entities.canRez()) { Entities.setPacketsPerSecond(6000); EntityViewer.setPosition(SEARCH_CENTER); EntityViewer.setCenterRadius(SEARCH_AREA); // This should allow us to see nano-scale entities from great distances EntityViewer.setVoxelSizeScale(Number.MAX_VALUE); octreeInterval = Script.setInterval(function() { EntityViewer.queryOctree(); }, 1000); initialized = true; Script.update.disconnect(update); } } }; Script.update.connect(update); Script.setTimeout(function(){ try { Entities.deleteEntity(Entities.findEntitiesByName("Money Tree Counter", SEARCH_CENTER, SEARCH_AREA)[0]); Entities.deleteEntity(Entities.findEntitiesByName("Money Tree Status", SEARCH_CENTER, SEARCH_AREA)[0]); Entities.deleteEntity(Entities.findEntitiesByName("Power Button Spawner", SEARCH_CENTER, SEARCH_AREA)[0]); console.log("[MONEY TREE] deleted stuff"); } catch (e) { console.log("[MONEY TREE] could not find and delete anything"); } counterText = Entities.addEntity({ type: "Text", dimensions: { x: 0.3, y: 0.1854, z: 0.01 }, lineHeight: 0.125, text: "-", textColor: {"red": 255, "green": 255, "blue": 255}, backgroundColor: {"red": 0, "green": 0, "blue": 0}, name: "Money Tree Counter", position: {"x":-17.9620418548584,"y":-10.537128448486328,"z":-10.67333984375}, rotation: {"x":-0.16071832180023193,"y":0.6887953877449036,"z":0.1588284969329834,"w":0.6887840032577515}, visible: true, collisionless: true, userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" }); activeText = Entities.addEntity({ type: "Text", dimensions: { x: 0.7585, y: 0.1695, z: 0.01 }, lineHeight: 0.115, text: "STARTING...", textColor: {"red": 255, "green": 255, "blue": 255}, backgroundColor: {"red": 0, "green": 0, "blue": 0}, name: "Money Tree Status", position: {"x":-17.9667,"y":-10.5250,"z":-11.4147}, rotation: {"x":-0.15957793593406677,"y":0.6889334321022034,"z":0.1589823216199875,"w":0.6888881921768188}, visible: true, collisionless: true, userData: "{ \"grabbableKey\": { \"grabbable\": false, \"kinematic\": false } }" }); messageHandler = function(channel, message, senderUUID, localOnly) { if (channel !== MONEY_TREE_CHANNEL) { print("not on channel"); return; } else { message = JSON.parse(message); } if (message.type === 'moneyGiven') { if (senderUUID === targetGiver.nodeID){ for (var i = 0; i < targetRecipients.length; i++) { if (message.recipientID === targetRecipients[i].nodeID && payOnce === false){ payOnce = true; sendInput(targetRecipients[i].username); spawnReceiverMessage(targetRecipients[i].nodeID, randomHFCAmount); Entities.editEntity(activeText, {text: "WAITING..."}); Script.setTimeout(function(){ payOnce = false; }, SHOW_TIME_LENGTH); } } } } else if (message.type === 'tree power') { treeOn = message.state; console.log("[MONEY TREE] tree state: ", treeOn); if (treeOn === true) { if (interval){ Script.clearInterval(interval); } startTree(); Entities.editEntity(activeText, {text: "STARTING..."}); } else { if (interval){ Script.clearInterval(interval); console.log("[MONEY TREE] money tree stopping soon"); } Entities.editEntity(activeText, {text: "OUT OF ORDER"}); } } else if (message.type === 'entering') { var nameOnList = false; var bankerOnList = false; for ( var i = 0; i < approvedBankerList.length; i++) { if (message.username === approvedBankerList[i]) { for (var i = 0; i < bankers.length; i++) { if ( message.username === bankers[i].username) { bankerOnList = true; } } if (!bankerOnList) { bankers.push({ username: message.username, nodeID: message.nodeID }); bankerOverlay(bankers.length-1); bankerOnList = true; } Entities.editEntity(counterText, {text: userList.length}); var activeRead = Entities.getEntityProperties(activeText, ['text']); if (userList.length > 3 && activeRead.text === "MOAR PPL PLZ") { Entities.editEntity(activeText, {text: "K LET'$ GROW!"}); } } } for (var i = 0; i < userList.length; i++) { if (message.username === userList[i].username) { nameOnList = true; } } if (!nameOnList) { userList.push({ username: message.username, nodeID: message.nodeID, staff: false }); for (var staff = 0; staff < staffList.length; staff++) { if (message.username === staffList[staff]){ userList[userList.length-1].staff = true; } } Entities.editEntity(counterText, {text: userList.length}); console.log("[MONEY TREE] user list length: ", userList.length); var activeRead = Entities.getEntityProperties(activeText, ['text']); if (userList.length >= 3 && activeRead.text === "MOAR PPL PLZ") { Entities.editEntity(activeText, {text: "K LET'$ GROW!"}); } else if (userList.length < 3) { Entities.editEntity(activeText, {text: "MOAR PPL PLZ"}); } } } else if (message.type === 'leaving') { try { for ( var i = 0; i < bankers.length; i++) { if (message.username === bankers[i].username) { bankers.splice(i, 1)[0]; Entities.deleteEntity(power); if (!treeOn) { Messages.sendMessage(MONEY_TREE_CHANNEL, JSON.stringify({ type: "delete power" })); if (power) { var buttons = Entities.findEntitiesByName("Power Button Spawner", { x: -16.9779, y: -10.3132, z: -10.7944 }, 10); for (var i = 0; i < buttons.length; i++) { Entities.deleteEntity(buttons[i]); } } } } } for (var i = 0; i < userList.length; i++) { if (message.username === userList[i].username) { userList.splice(i, 1)[0]; Entities.editEntity(counterText, {text: userList.length}); i--; if (userList.length < 3) { Entities.editEntity(activeText, {text: "RESTING"}); } } } } catch (e) { print("[MONEY TREE] Error in retrieving username for deletion"); return; } } }; Messages.messageReceived.connect(messageHandler); Messages.subscribe(MONEY_TREE_CHANNEL); try { for (var i = 0; i < userList.length; i++) { if (userList[i].username == "") { userList.splice(i, 1)[0]; } } } catch (e) { print("[MONEY TREE] userList error"); return; } }, 1500); } function bankerOverlay(index){ if (power){ var buttons = Entities.findEntitiesByName("Power Button Spawner", { x: -16.9779, y: -10.3132, z: -10.7944 }, 10); for (var i = 0; i < buttons.length; i++) { Entities.deleteEntity(buttons[i]); } Entities.deleteEntity(power); } var userData = { bankerID: bankers[index].nodeID, power: treeOn}; console.log("[MONEY TREE] CREATING POWER BUTTON, TREE is:", treeOn); power = Entities.addEntity({ type: "Box", dimensions: { x: 0.5, y: 0.5, z: 0.5 }, name: "Power Button Spawner", script: "https://hifi-content.s3.amazonaws.com/brosche/DomainContent/Hub/MoneyTree/moneyTreeBankerClient.js", userData: JSON.stringify(userData), position: { x: -16.9779, y: -9.132, z: -10.7944 }, visible: false, collisionless: true }); } function leftDomain(id){ for (var i = 0; i < userList.length; i++) { if (userList[i].nodeID === id){ userList.splice(i, 1)[0]; } } Entities.editEntity(counterText, {text: userList.length}); if (userList.length < 3) { Entities.editEntity(activeText, {text: "RESTING"}); } } function getMoney() { try { var payoutURL = "https://script.google.com/macros/s/AKfycbwwKdRHuSLQX5Ay595PXmKLmJnwzSX13SojHUH9or5LCdclzHg/exec?"; triviaURL = triviaURL + "category=" + "Giraffage_Hemsworth"; request(triviaURL, function (error, data) { if (!error) { console.log(JSON.stringify(data)); triviaData = data; } }); } catch (err) { console.log("err:", err); print("Could not get domain data using userData domainAPIURL"); } } // This function is used to send this script's specific version of an HTTP request. // It's a stripped-down version of High Fidelity's included `request.js` module. // This function will make the HTTP request and handle the data that's retrieved via a callback. // It'll also handle dispensing HFC via pre-authorized transactions using HTTP PUT requests. function request(options, callback) { var httpRequest = new XMLHttpRequest(), key; // QT bug: apparently doesn't handle onload. Workaround using readyState. httpRequest.onreadystatechange = function () { var READY_STATE_DONE = 4; var HTTP_OK = 200; if (httpRequest.readyState >= READY_STATE_DONE) { var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, response = !error && httpRequest.responseText, contentType = !error && httpRequest.getResponseHeader('content-type'); if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. try { response = JSON.parse(response); } catch (e) { error = e; } } if (error) { response = { statusCode: httpRequest.status }; } callback(error, response); } }; if (!options.method) { options.method = 'GET'; } if (options.body && (options.method === 'GET')) { // add query parameters var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; for (key in options.body) { if (options.body.hasOwnProperty(key)) { params.push(key + '=' + options.body[key]); } } options.uri += appender + params.join('&'); delete options.body; } if (options.json) { options.headers = options.headers || {}; options.headers["Content-type"] = "application/json"; options.body = JSON.stringify(options.body); } for (key in options.headers || {}) { if (options.headers.hasOwnProperty(key)) { httpRequest.setRequestHeader(key, options.headers[key]); } } httpRequest.open(options.method, options.uri, true); httpRequest.send(options.body || null); } // This function will get auth data related to a 25-HFC pre-authorized // transaction from our Google Sheet auth data database. function getGoogleSheetAuthData(successCallback) { var googleSheetRequestBody = { hfc: "25" }; request({ uri: GOOGLE_SHEET_AUTH_SCRIPT, method: 'GET', body: googleSheetRequestBody }, function(error, response) { try { JSON.parse(response); } catch(e) { if (e instanceof SyntaxError) { error = true; } } if (error) { console.log("ERROR while retrieving authorization data: " + JSON.stringify(response)); console.log("ERROR during slot machine payout: Couldn't get auth data from Google Sheet"); console.log("Slot machine was supposed to pay out to " + slotMachineCurrentPlayer + "."); slotMachineCurrentPlayer = false; if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "Error during payout :("}); } return false; } else if (JSON.parse(response).status !== "success") { console.log("Slot machine DOES NOT have payout funds available!"); console.log("Slot machine was supposed to pay out to " + slotMachineCurrentPlayer + "."); slotMachineCurrentPlayer = false; if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "Error during payout :("}); } return false; } else { console.log("Slot machine DOES have payout funds available! Returning auth data..."); response = JSON.parse(response); successCallback(response.authorizationID, response.couponID); // payOutToCurrentPlayer() return true; } }); } // This function will pay out to the current slot machine player in `slotMachineCurrentPlayer` // based on the passed `authID` and `coupon_id`. function payOutToCurrentPlayer(authID, couponID) { // Set up the `redeem` endpoint request body var hifiRedemptionRequestBody = { authorization_id: authID, coupon_id: couponID, username: slotMachineCurrentPlayer }; console.log("Attempting payout! Redemption request body: " + JSON.stringify(hifiRedemptionRequestBody)); // Make the call to the redemption endpoint to pay out! request({ uri: HIFI_REDEMPTION_ENDPOINT_URL, method: 'PUT', json: true, body: hifiRedemptionRequestBody }, function (error, response) { if (error || (response.status !== 'success')) { console.log("ERROR during slot machine payout: ", error || response.status); console.log("Full response: " + JSON.stringify(response)); console.log("Slot machine was supposed to pay out to " + slotMachineCurrentPlayer + "."); console.log("SLOT MACHINE DID NOT PAY OUT - Google Sheet will say auth used, but it wasn't!"); slotMachineCurrentPlayer = false; if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "Error during payout :("}); } return; } else { console.log("Slot machine paid out to " + slotMachineCurrentPlayer + "!"); slotMachineCurrentPlayer = false; if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "YOU WON 25 HFC!!"}); resetPlayTextAfterDelay(); } } }); } // This function will check our pre-authorized funds database to make // sure we have funds available to pay out. // If we do have funds available to pay out, we'll call `successCallback(callbackParam)`. function checkIfSlotMachineHasAvailableFunds(successCallback, callbackParam) { var googleSheetRequestBody = { hfc: "25", justChecking: true }; console.log("Checking Google Sheet for available authorization data..."); request({ uri: GOOGLE_SHEET_AUTH_SCRIPT, method: 'GET', body: googleSheetRequestBody }, function(error, response) { try { JSON.parse(response); } catch (e) { if (e instanceof SyntaxError) { error = true; } } if (error) { console.log("ERROR while retrieving authorization data: " + JSON.stringify(response)); return false; } else if (JSON.parse(response).status !== "success") { console.log("Slot machine DOES NOT have payout authorizations available."); if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "Out of order :("}); } return false; } else { console.log("Slot machine DOES have payout authorizations available."); successCallback(callbackParam); // startSlotMachine(player); return true; } }); } // This function is used to allow the AC script to see and change entities // in the domain. function allowEntityAccess() { Entities.setPacketsPerSecond(6000); EntityViewer.setPosition(SLOT_MACHINE_AREA); EntityViewer.setCenterRadius(1000); // This should allow us to see nano-scale entities from great distances EntityViewer.setVoxelSizeScale(Number.MAX_VALUE); Script.setInterval(function() { EntityViewer.queryOctree(); }, 1000); console.log("This AC script now has access to entities in this domain!"); } // This function checks to make sure that the entity server exists // and that the AC script has Rez permissions. // If one or both of those things is false, we'll check again in 5 seconds. function maybeAllowEntityAccess() { console.log("Attempting to give this AC script entity access..."); if (Entities.serversExist() && Entities.canRez()) { allowEntityAccess(); } else { if (!Entities.canRez()) { console.log("This AC script doesn't have rez permissions!"); } Script.setTimeout(maybeAllowEntityAccess, 5000); } } // This function will be called on startup. function startup() { // Listen on the slot machine messaging channel! Messages.subscribe(SLOT_MACHINE_MESSAGING_CHANNEL); Messages.messageReceived.connect(onMessageReceived); maybeAllowEntityAccess(); // Make sure the Play Text is set to the default when we're restarting the script. if (entityExistsInDomain(SLOT_MACHINE_PLAY_TEXT_ID)) { Entities.editEntity(SLOT_MACHINE_PLAY_TEXT_ID, {text: "Click Red Ball to Play!"}); } // This function sets up a repeating interval. When the interval timer expires, // the script will request our Recent Economic Activity. Script.setInterval(function() { checkForNewPlayers(); }, CHECK_RECENT_ACTIVITY_INTERVAL_MS); } startup(); // This function will be called when the script shuts down. function shutdown() { Messages.messageReceived.disconnect(onMessageReceived); } function spawnReceiverMessage(receiver, amount){ print("[MONEY TREE] spawing reciever message", JSON.stringify(receiver)); var userData = { receiverID: receiver, amount: amount }; var avatar = AvatarList.getAvatar(receiver); Entities.addEntity({ type: "Box", dimensions: { x: 0.5, y: 0.5, z: 0.5 }, name: "Tree Gift Receipt", script: "https://hifi-content.s3.amazonaws.com/brosche/DomainContent/Hub/MoneyTree/moneyTreeReceiverClient.js", userData: JSON.stringify(userData), lifetime: 5, position: Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, { x: -2.5, y: 0, z: -5 })), visible: false, collisionless: true, parentID: receiver }); try { print("[MONEY TREE] making coin particle"); Entities.addEntity({ lifetime: 5, collidesWith: "", collisionMask: 0, collisionless: true, position: Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, { x: 0, y: 0, z: -1 })), dimensions: { x: 0.15, y: 0.15, z: 0.15 }, isEmitting: true, name: "Coin Particle", type: "ParticleEffect", userData: "{\"grabbableKey\":{\"grabbable\":false}}", lifespan: 0.5, maxParticles: 10, textures: "https://hifi-content.s3.amazonaws.com/brosche/Trivia/Four%20Square/assets/pictures/tempsnip.png", emitRate: 10, emitSpeed: 1.5, speedSpread: 2, emitDimensions: { x: 0, y: 0, z: 0 }, emitOrientation: { x: 0, y: 0, z: 0 }, emitterShouldTrail: false, particleRadius: 0.15, radiusSpread: 0, radiusStart: 0, radiusFinish: 0.15, color:{ red:255, blue:255, green:255 }, colorSpread:{ red:0, blue:0, green:0 }, colorStart:{ red:255, blue:255, green:255 }, colorFinish:{ red:255, blue:255, green:255 }, emitAcceleration:{ x:0, y:-10, z:1}, accelerationSpread:{ x:5, y:3, z:5 }, alpha: 1, alphaSpread: 0, alphaStart: 1, alphaFinish: 1, particleSpin: 0, spinSpread: 0, spinStart: 0, spinFinish: 0, rotateWithEntity: true, polarStart: 0, polarFinish: 0, azimuthStart: -2.9321532249450684, azimuthFinish: 0.5235987901687622 }); } catch (e) { print("[MONEY TREE] error spawning coin particle", e); } } function randomizeHFC(){ var funds = true; var rand = gaussian(AVERAGE_HFC_AMOUNT, STANDARD_DEVIATION); var listLength =Math.sqrt(userList.length); var amount = Math.ceil(rand*listLength) - Math.ceil(rand*listLength) % 5; if (!funds){ Entities.editEntity(activeText, {text: "BANKRUPT!"}); return 0; } if (amount >= 50){ amount = 50; } else if (amount <= 5){ amount = 5; } return amount; } function randomizeInterval(){ var interval = gaussian(AVERAGE_INTERVAL, 1); return interval; } function createCoinSpawner() { console.log("[MONEY TREE] SPAWNING COINS"); if (coin.length > 0){ console.log("[MONEY TREE] DELETING COINS"); for (var i = 0; i < coin.length; i++ ) { Entities.deleteEntity(coin[i]); } } // Show the coins to the giver as overlays if (targetRecipients[0] == null){ console.log("[MONEY TREE] could not find eligible recipients"); return; } else if (targetRecipients.length < 2){ console.log("[MONEY TREE] not enough eligible recipients"); return; } else { console.log("[MONEY TREE] SPAWNING COINS..."); for (var i = 0; i < targetRecipients.length; i++){ var avatar = AvatarList.getAvatar(targetRecipients[i].nodeID); var sum = Vec3.sum(avatar.position, Vec3.UP); var userData = { giverID: targetGiver.nodeID }; coin[i] = Entities.addEntity({ type: "Box", dimensions: { x: 0.5, y: 0.5, z: 0.5 }, name: "Money Tree Gift", script: "https://hifi-content.s3.amazonaws.com/brosche/DomainContent/Hub/MoneyTree/moneyTreeClient.js", userData: JSON.stringify(userData), lifetime: 60, position: sum, visible: false, collisionless: true, parentID: avatar.sessionUUID }); } } } function pickAGiver(){ console.log("[MONEY TREE] PICKING GIVER"); // Choose a random user to display coins for giving // Do not choose a user again until all other users in range have been selected. indexGiver = randInt(0, userList.length-1); if (indexGiver < 0){ return; } else { if (targetGiver){ if (userList[indexGiver].username === ""){ userList.splice(indexGiver, 1)[0]; if (indexGiver >= userList.length){ indexGiver = randInt(0, userList.length-1); } } if (targetGiver.username !== userList[indexGiver].username){ targetGiver = userList[indexGiver]; } else { indexGiver = randInt(0, userList.length-1); targetGiver = userList[indexGiver]; } } else { targetGiver = userList[indexGiver]; } } } function pickRecipients(){ console.log("[MONEY TREE] PICKING RECIPIENTS"); targetRecipients = []; tempList = []; tempList = userList.slice(); tempList.splice(indexGiver, 1)[0]; for (var i = 0; i < tempList.length; i++) { if (tempList[i].staff === true) { console.log("[MONEY TREE] removing staff", tempList[i].username); tempList.splice(i,1)[0]; i--; } } if (tempList.length < 2) { console.log("[MONEY TREE] Not enough eligible recipients for receiver selection."); Entities.editEntity(activeText, {text: "MOAR PPL PLZ"}); return; } var recipientCount = (tempList.length > RECIPIENT_MAX) ? RECIPIENT_MAX : tempList.length; for (var i = 0; i < recipientCount; i++){ var index = randInt(0, tempList.length-1); targetRecipients.push(tempList.splice(index-i, 1)[0]); console.log("[MONEY TREE] recipient:", JSON.stringify(targetRecipients[i].username)); } } function randFloat(low, high) { return low + Math.random() * (high - low); } function randInt(low, high) { return Math.floor(randFloat(low, high)); } function gaussian(mean, stdev) { // returns a gaussian random function with the given mean and stdev. var y2; var useLast = false; var y1; if (useLast) { y1 = y2; useLast = false; } else { var x1, x2, w; do { x1 = 2.0 * Math.random() - 1.0; x2 = 2.0 * Math.random() - 1.0; w = x1 * x1 + x2 * x2; } while ( w >= 1.0); w = Math.sqrt((-2.0 * Math.log(w))/w); y1 = x1 * w; y2 = x2 * w; useLast = true; } var retval = mean + stdev * y1; if (retval > 0) { return retval; } return -retval; } function sendInput(recipientUsername) { // curl -X PUT -d authorization_id= -d coupon_id= -d username=steve https://highfidelity.com/api/v1/commerce/redeem; var paramString = encodeURLParams({ date: new Date().toLocaleString(), recipientUsername: recipientUsername, amount: randomHFCAmount }); var request = new XMLHttpRequest(); request.open('GET', googleURL + "?" + paramString); request.timeout = 10000; request.send(); } function encodeURLParams(params) { var paramPairs = []; for (var key in params) { paramPairs.push(key + "=" + params[key]); } return paramPairs.join("&"); } function sweep(){ try { var homelessParticles = Entities.findEntitiesByType("ParticleEffect", SEARCH_CENTER, SEARCH_AREA); if (homelessParticles.length > 0) { for (var i = 0; i < homelessParticles.length; i++ ) { Entities.deleteEntity(homelessParticles[i]); } } } catch (e) { print("[MONEY TREE] particle sweep failed."); return; } try { var collect = Entities.findEntitiesByName("Coin Particle", SEARCH_CENTER, SEARCH_AREA); if (collect.length > 0) { for (var i = 0; i < collect.length; i++ ) { Entities.editEntity(collect[i], { parentID: null }); Entities.deleteEntity(collect[i]); } } if (coin.length > 0){ for (var i = 0; i < coin.length; i++ ) { Entities.editEntity(coin[i], { parentID: null }); Entities.deleteEntity(coin[i]); } } } catch (e) { print("[MONEY TREE] entity sweep failed."); return; } } function startTree(){ interval = Script.setInterval(function(){ sweep(); Entities.editEntity(activeText, {text: "SEARCHING..."}); if (userList.length >= RECIPIENT_MAX){ randomHFCAmount = randomizeHFC(); if (randomHFCAmount === 0) { Script.clearInterval(interval); return; } pickAGiver(); console.log("[MONEY TREE] GIVER", JSON.stringify(targetGiver.username)); pickRecipients(); if (targetRecipients.length > 1) { Entities.editEntity(activeText, {text: "OOH SHINY!"}); createCoinSpawner(); } } else { Entities.editEntity(activeText, {text: "MOAR PPL PLZ"}); } }, randomizeInterval()*SHOW_TIME_LENGTH); } function appEnding() { sweep(); Entities.deleteEntity(counterText); Entities.deleteEntity(activeText); if (interval){ Script.clearInterval(interval); } if (octreeInterval){ Script.clearInterval(octreeInterval); } Messages.unsubscribe(MONEY_TREE_CHANNEL); Messages.messageReceived.disconnect(messageHandler); } startTree(); AvatarList.avatarRemovedEvent.connect(leftDomain); Messages.subscribe(MONEY_TREE_CHANNEL); Script.scriptEnding.connect(appEnding); }());