Apps Home
|
Create an App
PLINKO
Author:
nicky7282
Description
Source Code
Launch App
Current Users
Created by:
Nicky7282
/******************************************************************** ** PLINKO for chaturbate ** by Nicky7282 (druixure@yahoo.com) (twitter: @nicky7282) ** This app/bot is still in beta and being actively developed, your mileage may vary. ** ** If you modify, adapt, or copy parts of this code in ANY WAY please credit me: nicky7282 ** *Liam Neeson voice* ** IF YOU DON'T CREDIT ME... ಠ_ಠ ** I WILL FIND YOU... ಡ_ಡ ** AND I WILL KILL YOU... (⌐■_■)︻╦╤─ (╥﹏╥) ** ... ** .. ** So thanks for crediting me! THAT'S FUCKIN' TEAMWORK! ᕕ( ᐛ )ᕗ ** ** ** ** HIGH FIVE! ¯\_(ツ)_/¯ ********************************************************************/ cb.settings_choices = [ { name: 'plinkoPrice', label: "(Multiples of 10 are easiest) Plinko chip price:", type: 'int', defaultValue: 40, minValue: 10, }, { name: 'boardSpeed', label: "How fast should the board be drawn?", type: 'choice', choice1: 'VeryFast', choice2: 'Fast', choice3: 'Normal', choice4: 'Slow', choice5: 'VerySlow', defaultValue: 'Normal', }, { name: 'plinkoChip', label: "What should the plinko chip be?", type: 'choice', choice1: 'white', choice2: 'black', choice3: 'red', choice4: 'blue', choice5: 'volleyball', defaultValue: 'white', }, { name: 'plinkoHeight', label: 'How tall should the plinko board be? (Default: 8)', type:'int', minValue: 4, maxValue: 20, defaultValue: 8, }, { name: 'disableBounce', label: "Disable plinko chip bouncing?", type: 'choice', choice1: 'no', choice2: 'yes', defaultValue: 'no', }, { name: 'goalType', label: "What type of goal should the room have?", type: 'choice', choice1: 'Number of Boards Cleared', choice2: 'Number of Chips Dropped', choice3: 'Token Goal', defaultValue: 'Number of Boards Cleared', }, { name: 'tokenGoalPrice', label: "(Token Goal Only) How many tokens should the goal be?", type: 'int', defaultValue: 1000, minValue: 1, }, { name: 'chipGoalNumber', label: "(Chip Goal Only) How many chips should be dropped before the goal is met?", type: 'int', defaultValue: 50, minValue: 1, }, { name: 'boardGoalNumber', label: "(Board Goal Only) How many boards should be cleared before the goal is met?", type: 'int', defaultValue: 4, minValue: 1, }, { name: 'prize0', label: "Prizes go here:", type: 'str', required: false, defaultValue: '',}, { name: 'prize1', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize2', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize3', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize4', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize5', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize6', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize7', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize8', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'prize9', label: "", type: 'str', required: false, defaultValue: '',}, { name: 'subject', label: "What should the room subject be? (Always starts with PLINKO)", type: 'str', required: true, defaultValue: ' is running! Tip to drop chips and WIN PRIZES!',}, { name: 'changeSubject', label: "Let plinko change the room subject?", type: 'choice', choice1: 'no', choice2: 'yes', defaultValue: 'yes', }, ]; //As of 11/27/2016 slight refactoring is needed but I wanted to get this released ASAP so... yeah... //Could fix "bounced" and "moved" to be variables set in getPath(), as they are calculated multiple times in multiple functions. //Integer math operations are less of a concern than the amount of times Math.random is called. I would like to consolodate getpath's random path generation to ONE Math.random call in the future. // (possibly Math.random().toString().split(''), adding a decimal to each one, then running them through an addOrSub() function whose formula is similar to getRandomInt()) // Surprisingly that might be less intensive than just calling Math.random multiple times... yay javascript. ¯\_(ツ)_/¯ //I need to move slot tracking for prize removal OUTSIDE of prizes array. var totalGoalsMet = 0; var tokensTipped = 0; var chipsDropped = 0; var timesBoardCleared = 0; var TOKENGOAL = 0; var CHIPGOAL = 1; var BOARDGOAL = 2; var goal; var goalType; if (cb.settings.goalType == 'Token Goal'){ goalType = TOKENGOAL; goal = cb.settings.tokenGoalPrice; } else if (cb.settings.goalType == 'Number of Chips Dropped'){ goalType = CHIPGOAL; goal = cb.settings.chipGoalNumber; } else { goalType = BOARDGOAL; goal = cb.settings.boardGoalNumber; } var boardSpeed = {VeryFast:200, Fast:500, Normal:700, Slow:1000, VerySlow:1400}[cb.settings.boardSpeed]; var boardQueue = []; var beingDrawn = false; var prizeQueue = []; var plinkoWidth = 10; // excludes 0. If you modify my code, you will have to update the prizes array, fancyNumbers arrays, and cb.settings_choices to change this value without breaking things. var plinkoHeight = cb.settings.plinkoHeight; // excludes 0. excludes plinkoBottom(). var chips = {white:'\u{26AA}', black:'\u{26AB}', red:'\u{1F534}', blue:'\u{1F535}', volleyball:'\u{1F3D0}'}; var plinkoChip = chips[cb.settings.plinkoChip]; var canBounce = {no:true,yes:false}[cb.settings.disableBounce]; //interface psychology. I think it's more interesting if the ball bounces. var bouncePanelMessage; if (canBounce){ bouncePanelMessage = 'enabled'; } else { bouncePanelMessage = 'disabled'; } var removePrizes; if (goalType == BOARDGOAL){ removePrizes = true; } else { removePrizes = false; } var plinkoBorder = '\u{1F536}'; var fancyNumbers1 = ['\u{1D7CF}','\u{1D7D0}','\u{1D7D1}','\u{1D7D2}','\u{1D7D3}','\u{1D7D4}','\u{1D7D5}','\u{1D7D6}','\u{1D7D7}','\u{1D7CF}\u{1D7CE}']; //Not much a difference. var fancyNumbers2 = ['\u{1D7D9}','\u{1D7DA}','\u{1D7DB}','\u{1D7DC}','\u{1D7DD}','\u{1D7DE}','\u{1D7DF}','\u{1D7E0}','\u{1D7E1}','\u{1D7D9}\u{1D7D8}']; //Meh in firefox. Will mess up fonts. var fancyNumbers3 = ['\u{2776}','\u{2777}','\u{2778}','\u{2779}','\u{277A}','\u{277B}','\u{277C}','\u{277D}','\u{277E}','\u{277F}']; //Okay in chrome. Bad in firefox. var fancyNumbers4 = ['\u{2780}','\u{2781}','\u{2782}','\u{2783}','\u{2784}','\u{2785}','\u{2786}','\u{2787}','\u{2788}','\u{2789}']; //Okay in chrome. Terrible in firefox. function plinkoBottom(){ var bottomString = '\n' + plinkoBorder; for (var i = 0; i < plinkoWidth; i++){ if (prizes[i][2]){ bottomString += fancyNumbers1[i]; } else { bottomString += (i+1); } if ((i+1) != plinkoWidth){ bottomString += '\u{2591}'; } } bottomString += plinkoBorder; return bottomString; } var plinkoCat = '\u{227D}^\u{072B}^\u{227C}'; // THIS CAT IS NECESSARY DO NOT REMOVE UNDER ANY CIRCUMSTANCE cb.chatNotice('Welcome to PLINKO by Nicky7282! ' + plinkoCat + '\nThis app is brand new; if you encounter any issues please contact me on twitter: @nicky7282','','','','bold'); var prizes = [[cb.settings.prize0, false, false], [cb.settings.prize1, false, false], [cb.settings.prize2, false, false], [cb.settings.prize3, false, false], [cb.settings.prize4, false, false], [cb.settings.prize5, false, false], [cb.settings.prize6, false, false], [cb.settings.prize7, false, false], [cb.settings.prize8, false, false], [cb.settings.prize9, false, false]]; var canChangeSubject = {no:false,yes:true}[cb.settings.changeSubject]; if (canChangeSubject) { cb.changeRoomSubject('PLINKO ' + cb.settings.subject); } cb.onTip(function (tip) { var user = tip['from_user']; var amountTipped = parseInt(tip['amount']); tokensTipped += amountTipped; if (amountTipped >= cb.settings.plinkoPrice && amountTipped <= (cb.settings.plinkoPrice + (plinkoWidth-1))){ dropChip(amountTipped, user); } if (goalType == TOKENGOAL){ tokenGoal(); } }); function tokenGoal(){ var goalsMet = 0; if (tokensTipped >= goal){ goalsMet += Math.floor(tokensTipped / goal); tokensTipped -= (goal * goalsMet); for (var i = 1; i <= (goalsMet); i++){ cb.chatNotice('GOAL has been met for the ' + ordinalify(i + totalGoalsMet) + ' time!','','','#FF0000','bold'); } totalGoalsMet += goalsMet; } cb.drawPanel(); } function manageGoal(){ //This function is not as clearly articulated as I would like. Copied and pasted from tokenGoal(). Too lazy. Not lazy enough to not write this. I'm tired okay. var goalsMet = 0; if ((goalType == CHIPGOAL) && (chipsDropped >= goal)){ goalsMet += Math.floor(chipsDropped / goal); chipsDropped -= (goal * goalsMet); for (var i = 1; i <= (goalsMet); i++){ cb.chatNotice('GOAL has been met for the ' + ordinalify(i + totalGoalsMet) + ' time!','','','#FF0000','bold'); } totalGoalsMet += goalsMet; } else if ((goalType == BOARDGOAL) && (timesBoardCleared >= goal)){ goalsMet += Math.floor(timesBoardCleared / goal); timesBoardCleared -= (goal * goalsMet); for (var i = 1; i <= (goalsMet); i++){ cb.chatNotice('GOAL has been met for the ' + ordinalify(i + totalGoalsMet) + ' time!','','','#FF0000','bold'); } totalGoalsMet += goalsMet; } } function dropChip(amountTipped, user){ //cb.log('in dropChip()'); //TRACE chipsDropped++; var startPosition = amountTipped - cb.settings.plinkoPrice; var pathAndMovement = getPath(startPosition); generateBoard(pathAndMovement[0], pathAndMovement[1], user); findPrize(pathAndMovement[0], pathAndMovement[1], user); if (!beingDrawn) { beingDrawn = true; displayTheBoard(); } } function checkForEmptyBoard() { //cb.log('in checkForEmptyBoard()'); //TRACE for (var i = 0; i < prizes.length; i++){ if (!prizes[i][2]){ return; } } timesBoardCleared++; cb.chatNotice('Board was cleared for the ' + ordinalify(timesBoardCleared) + ' time this goal!','','','#FF0000','bold'); for (var i = 0; i < prizes.length; i++){ prizes[i][1] = false; prizes[i][2] = false; } } function displayThePrizes(){ //cb.log("in displayThePrizes()"); //TRACE prizeQueue.pop(); cb.chatNotice(prizeQueue.join(''),'','','#FF0000','bold'); prizeQueue = []; checkForEmptyBoard(); manageGoal(); cb.drawPanel(); } //I was really really tired when I wrote this. function findPrize(path, movement, user){ // My articulation is poor here. Sorry? Shruglife. Still, you should be able to get the gist of this function. //cb.log("in findPrize()"); //TRACE var slot1 = path[path.length-1]; var slot2 = path[path.length-2]; var prize1 = prizes[slot1][0]; var prize2 = prizes[slot2][0]; var beenUsed1 = prizes[slot1][1]; var beenUsed2 = prizes[slot2][1]; var prizePhrase = ''; var moved = movement[movement.length-1]; var bounced = path.length - plinkoHeight; if (prize1 && !beenUsed1){ prizePhrase += user + ' won: ' + prize1; } else if (!prize1 && !beenUsed1){ prizePhrase += user + '\'s chip landed in slot ' + (slot1+1); } else if (prize1 && beenUsed1){ prizePhrase += user + '\'s chip landed in slot ' + (slot1+1) + ' but that prize was already won.'; } else if (!prize1 && beenUsed1){ prizePhrase += user + '\'s chip landed in slot ' + (slot1+1) + ' but that slot already had a chip in it.'; } else { cb.log('Something went wrong finding the first prize.'); } if (removePrizes){ prizes[slot1][1] = true; } prizeQueue.push(prizePhrase); prizeQueue.push('\n'); if (bounced && moved){ //cb.log("in if(bounced&&moved) in findPrize()"); //TRACE prizePhrase = ''; if (prize2 && !beenUsed2){ prizePhrase += user + ' won: ' + prize2; } else if (!prize2 && !beenUsed2){ prizePhrase += user + '\'s chip landed in slot ' + (slot2+1); } else if (prize2 && beenUsed2){ prizePhrase += user + '\'s chip landed in slot ' + (slot2+1) + ' but that prize was already won.'; } else if (!prize2 && beenUsed2){ prizePhrase += user + '\'s chip landed in slot ' + (slot2+1) + ' but that slot already had a chip in it.'; } else { cb.log('Something went wrong finding the second prize.'); } if (removePrizes){ prizes[slot2][1] = true; } prizeQueue.push(prizePhrase); prizeQueue.push('\n'); } } function displayTheBoard() { //cb.log("in displayTheBoard()"); //TRACE var time; if (boardQueue.length > (plinkoHeight * 2)){ time = 0; } else { time = boardSpeed; } cb.setTimeout(function (){ //This is probably better done WITHOUT an inline function but fuck it. if (!boardQueue[0]){ //cb.log("in if(!boardQueue[0]) in setTimeout in displayTheBoard()"); //TRACE displayThePrizes(); beingDrawn = false; return; } else { cb.chatNotice(boardQueue.shift().join(''),'','','','bold'); } displayTheBoard(); }, time); } function generateBoard(path, movement, user){ //cb.log("in generateBoard()"); //TRACE var bounced = path.length - plinkoHeight; var moved = movement[movement.length-1]; var board = []; for (var i = 0; i < path.length; i++){ var row = []; for (var x = 0; x < plinkoWidth; x++){ if (isEven(i) && isEven(x)){ row.push("\u{1F53A}"); } else if (isEven(i) && isOdd(x)) { row.push("\u{1F53B}"); } else if (isOdd(i) && isEven(x)){ row.push("\u{1F53B}"); } else { row.push("\u{1F53A}"); } } board.push(row); board[i][path[i]] = plinkoChip; board[i].unshift(plinkoBorder); //These only exist to force firefox to display emoji correctly. (If the chip is on the far left, it gets a different font for some reason.) board[i].push(plinkoBorder); //Microsoft Edge doesn't display them correctly 11/22/2016. Nobody was surprised. if (movement[i]){ board[i].push(chipSound()); } } if (removePrizes) { if (bounced && moved) { prizes[path[path.length-2]][2] = true; } prizes[path[path.length-1]][2] = true; } if (bounced){ board[(board.length-2)].push(bouncePhrase()+plinkoBottom()); var leftOrRight = movement[movement.length-1]; board[(board.length-1)].push(itBounced(leftOrRight)+plinkoBottom()); } else { board[(board.length-1)].push(plinkoBottom()); } board[0].push(' The chip is dropped...'); board.unshift([user,approachesTheBoard()]); //displayTheBoard() requires array for (var i = 0; i < board.length; i++){ boardQueue.push(board[i]); } } function itBounced(bounce){ if (bounce == 1){ return " It bounced right! Both prize buttons were hit!"; } else if (bounce == -1){ return " It bounced left! Both prize buttons were hit!"; } else { return ' But it settled back to where it was.'; } } function bouncePhrase(){ var randomPhrase = [' The chip bounced out!', ' The chip bounced out of it\'s slot!', ' The chip bounced out and teeters on the edge!']; return randomPhrase[Math.floor(Math.random() * randomPhrase.length)]; } function chipSound(){ var sounds = [' *plink*', ' *plink*', ' *plink*', ' *plink*', ' *plink*', ' *plink*', ' *clack*', ' *clack*', ' *clack*', ' *tink*', ' *tink*', ' *tink*', ' *tink*', ' *ping*', ' *ting*', ' *click*', ' *pop*']; return sounds[Math.floor(Math.random() * sounds.length)]; } function approachesTheBoard(){ var randomPhrase = [' crosses their fingers as they step up to the plinko board', ' nervously approaches the plinko board', ' steps up the plinko board', ' confidently walks up to the plinko board', ' quietly sneaks up to the board while nobody\'s looking', ' tries to eat the plinko chip. '+cb['room_slug']+' drops the chip for them', ' gnaws at the plinko chip. '+cb['room_slug']+' drops the chip for them']; return randomPhrase[Math.floor(Math.random() * randomPhrase.length)]; } function getPath(startPosition){ //cb.log("in getPath()"); //TRACE var path = [startPosition]; var movement = [0]; var bounces = plinkoHeight-1; if (canBounce) { var sticky = getRandomInt(0,20); // Default(0,20) Most chips are sticky. if (!sticky){ bounces++; } } for (var i = 0; i < bounces; i++){ var move = getRandomInt(-1,2); var newPosition = path[i] + move; if (newPosition < 0){ newPosition = path[i] + Math.abs(move); path.push(newPosition); movement.push(Math.abs(move)); } else if (newPosition > (plinkoWidth-1)){ newPosition = path[i] - Math.abs(move); path.push(newPosition); movement.push(-Math.abs(move)); } else { path.push(newPosition); movement.push(move); } } return [path, movement]; } function remainingSlotNumbers(){ var remainingPrizes = ['']; for (var i = 0; i < prizes.length; i++) { if (!prizes[i][2]){ remainingPrizes.push(i+1); remainingPrizes.push(','); } } remainingPrizes.pop(); return remainingPrizes.join(''); } cb.onDrawPanel(function(user) { if (goalType == TOKENGOAL){ return { 'template': '3_rows_of_labels', 'row1_label': 'Goal [' + totalGoalsMet + ']', 'row1_value': tokensTipped + '/' + goal + 'tk', 'row2_label': 'PLINKO chip cost:', 'row2_value': cb.settings.plinkoPrice + ' to ' + (cb.settings.plinkoPrice+(plinkoWidth-1)) + ' tokens', 'row3_label': 'Bonus bounce:', 'row3_value': bouncePanelMessage }; } else if (goalType == CHIPGOAL) { return { 'template': '3_rows_of_labels', 'row1_label': 'Goal [' + totalGoalsMet + ']', 'row1_value': chipsDropped + ' of ' + goal + ' chip'+plural(goal) + ' dropped', 'row2_label': 'PLINKO chip cost:', 'row2_value': cb.settings.plinkoPrice + ' to ' + (cb.settings.plinkoPrice+(plinkoWidth-1)) + ' tokens', 'row3_label': 'Bonus bounce:', 'row3_value': bouncePanelMessage }; } else { return { 'template': '3_rows_of_labels', 'row1_label': 'Goal [' + totalGoalsMet + ']', 'row1_value': timesBoardCleared + ' of ' + goal + ' board'+plural(goal) + ' cleared', 'row2_label': 'PLINKO chip cost:', 'row2_value': cb.settings.plinkoPrice + ' to ' + (cb.settings.plinkoPrice+(plinkoWidth-1)) + ' tokens', 'row3_label': 'Prizes Remaining:', 'row3_value': remainingSlotNumbers(), }; } }); function plural(n){ if (n != 1){ return 's'; } else return ''; } function ordinalify(n) { //If you don't credit me I will find you. var nModHundred = n % 100; //Give it a number and it will spit out a string with proper ordinality. 1st 2nd 3rd 4th etc. It will ordinalIFY it. ¯\_(ツ)_/¯ var nModTen = nModHundred % 10; if (nModHundred > 3 && nModHundred < 21){ return (n + 'th'); } else if (nModTen == 1) { return (n + 'st'); } else if (nModTen == 2) { return (n + 'nd'); } else if (nModTen == 3) { return (n + 'rd'); } else { return (n + 'th'); } } function isEven(n) { return n == parseFloat(n) && !(n % 2); } function isOdd(n) { return n == parseFloat(n) && !!(n % 2); //Checks if number. isOdd != !isEven } function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; }
© Copyright Chaturbate 2011- 2024. All Rights Reserved.