Apps Home
|
Create an App
asdfasfasdf
Author:
eberk123
Description
Source Code
Launch App
Current Users
Created by:
Eberk123
/* ******************************************************* * Title: Karen Keno * Author: eberk123 * Version: 1.9 (21/3/2017) * * Based on: * Token Keno v1.2 by Adambomb01 ******************************************************* */ // vars var gTotalTipToday = 0; var gTotalKenoTipToday = 0; var gBoardClearTipAmount = 0; var gHighTipUsername = null; var gHighTipAmount = 0; var last_tip_username = null; var TIME_ONE_SEC = 1000; var TIME_ONE_MINUTE = 60 * TIME_ONE_SEC; var CONFIG_ADVERT_MINUTES = 1 * TIME_ONE_MINUTE; var EMOTE_TITLE = ' :kenoapp1 '; var EMOTE_STARS = ' :3star '; var EMOTE_STAR = ' :1star '; var nl = '\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501'; var description = '#tokenkeno #multigoal'; var gUsingNudeMenu = false; var gSpecialUser = 'piglovesalmon'; // requires resetting too var sentwin = false; var boardNumbers = new Array(); var boardPrizes = {}; var boardPrizesLeft = 0; var board = ''; var prizes = ''; var userPrizes = new Array(); var playerStats = {}; var gameStats = {firstplace: '',secondplace: '',thirdplace: '',firsttips: 0,secondtips: 0,thirdtips: 0}; var partialTips = {}; var ignoredTips = new Array(); // hidden show variable (no need to reset and etc. these are fixed value or auto set value) var gHiddenShowInitialLength = 10; // track the initial hidden show length (in minutes) var gHiddenShowSubGoalLength = 1; // track how many minutes to add for each goal var gHiddenShowTicketPrivilegeDiscount = 0.5; // discount for fan/mod to get ticket var gHiddenShowInitialGoalCost = 1000; // initial goal to start hidden show var gHiddenShowStartedMultiplier = 1.5; // the multiplier after show started var gHiddenShowMaxVIPIdleTime = 5; // maximum time a VIP can be idle before they are removed from chat var gHiddenShowSaleTicketDefaultTimer = 60; // the default flash sale timer for hidden show ticket var gHiddenShowSaleSubgoalDefaultTimer = 60; // the default flash sale timer for hidden show subgoal var gHiddenShowSaleTicketCurrentTimer = 0; var gHiddenShowSaleSubgoalCurrentTimer = 0; var gHiddenShowTicketInitialCost = 49; var gHiddenShowTicketCost = gHiddenShowTicketInitialCost; // how many tip to get into the hidden show var gHiddenShowSubgoalInitialCost = 120; var gHiddenShowSubGoalCost = gHiddenShowSubgoalInitialCost; // track how many token for each subgoal // need to be set in init var gHiddenShowCurrentLength = gHiddenShowInitialLength; // indicate the current running length of hidden show var gHiddenShowCurrentToken = 0; // indicate token count for current goal var gHiddenShowInitialGoalHit = false; // indicate if we need double cost for ticket var gHiddenShowCoolingDown = false; // indicate if hidden show is currently cooling down (meaning you can't start your hidden show while cooling down, this prevent multiple count down advert bug) var gHiddenShowVIPList = new Array(); // indicate all VIP ticket holders var gHiddenShowTippers = new Array(); var gHiddenShowReset = true; var gHiddenShowIdleVIPList = new Array(); // list of all VIP that has been temporarily removed from vip list var gHIddenShowIdleTimeTracker = new Array(); // key is user name, and value is just a boolean on whether the person messaged in the last gMaxVIPIdleTime minute var gHiddenShowSaleTicketCooldown = false; var gHiddenShowSaleSubgoalCooldown = false; // set to false to disable them var gEnableHiddenShow = false; var gEnableMultiGoal = true; var gEnableKarenKeno = true; var gMuteNonVIPTalking = false; // prevent non vip from talking during hidden show var gNoIdleVIP = true; // remove user from vip list if they don't chat at all during hidden show (to prevent video recorder) var gCurrentCommandArg = new Array(); // i'm abusing global variable here, this is used to store current command argument (only valid during command parsing) // you need to populate this, refer to init() for more information var gAdvertInformation = new Array(); var gCommandInformation = new Array(); // NOTE: board number is a fixed value, no need to change it var gBoardNumMin = 20; var gBoardNumMax = gBoardNumMin + 1; // NOTE: karen_shut_it has the same copy pasted array var gTipMenuContainer = [ {text:"PM", cost:"5"}, {text:"Smile", cost:"10"}, {text:"c2c 5 min", cost:"35"}, {text:"Flash Left Boob", cost:"45"}, {text:"Flash Right Boob", cost:"46"}, {text:"Flash Ass", cost:"50"}, {text:"Twerk Ass", cost:"89"}, {text:"Flash Boob", cost:"100"}, {text:"Boob Close Up", cost:"111"}, {text:"Flash Pussy", cost:"200"} ]; // NOTE: karen_shut_it has the same copy pasted array var gNudeMenuContainer = [ {text:"Bra off for 7 min", cost:"400"}, {text:"Stay Nude for 4 min", cost:"555"}, {text:"Panties off for 7 min", cost:"600"}, {text:"Nude for 10 min", cost:"1000"} ]; var actualPrizeList = new Array(); var gPossiblePrizeListRoom = [ {text:"Wear Glove, 3 mins", frequency:"1"}, {text:"Wear Stocking, 3 mins", frequency:"1"}, {text:"Hug Bear", frequency:"2"}, {text:"Play with bear", frequency:"2"}, {text:"Jump 10 times without bra", frequency:"1"}, {text:"Jump 5 times without bra", frequency:"1"}, {text:"Short Dance", frequency:"2"}, {text:"Smile and look close into the camera", frequency:"2"}, {text:"Blow a kiss", frequency:"2"}, {text:"Lick lips", frequency:"2"}, {text:"Tongue Tease", frequency:"2"}, {text:"Mod for 5 minutes", frequency:"2"}, {text:"Say 'I love you'", frequency:"2"}, {text:"Name on arm", frequency:"2"}, {text:"Name on ass", frequency:"1"}, {text:"Name on boobs", frequency:"1"}, {text:"Show Belly", frequency:"3"}, {text:"Show tongue", frequency:"3"}, {text:"Close up on my beautiful eyes", frequency:"2"}, {text:"Play your song", frequency:"3"}, {text:"Mod for a day", frequency:"2"}, {text:"No prize(( try again :**", frequency:"2"}, {text:"Private for 30tk/min, max 10 min", frequency:"3"}, {text:"Pull nipples", frequency:"1"}, {text:"Tits Closeup", frequency:"1"}, {text:"Play boobs", frequency:"1"}, {text:"Pinch my nipples", frequency:"1"}, {text:"Nipple closeup", frequency:"1"}, {text:"Top off for 2 minutes", frequency:"1"}, {text:"Top off for 3 minutes", frequency:"1"}, {text:"Flash boobs", frequency:"1"}, {text:"Flash left boob", frequency:"1"}, {text:"Flash right boob", frequency:"1"}, {text:"Flash ass", frequency:"1"}, {text:"Flash pussy", frequency:"1"}, {text:"Flash choice, boobs, ass or pussy", frequency:"1"}, {text:"Prize of choice", frequency:"1"}, ]; var gPossiblePrizeListNeutral = [ {text:"Smile and look close into the camera", frequency:"2"}, {text:"Short Dance", frequency:"2"}, {text:"Blow a kiss", frequency:"2"}, {text:"Lick lips", frequency:"2"}, {text:"Tongue Tease", frequency:"3"}, {text:"Mod for 5 minutes", frequency:"2"}, {text:"Say 'I love you'", frequency:"3"}, {text:"Name on arm", frequency:"2"}, {text:"Name on ass", frequency:"1"}, {text:"Name on boobs", frequency:"1"}, {text:"Show Belly", frequency:"3"}, {text:"Show tongue", frequency:"3"}, {text:"Close up on my beautiful eyes", frequency:"2"}, {text:"Play your song", frequency:"3"}, {text:"Mod for a day", frequency:"3"}, {text:"No prize(( try again :**", frequency:"3"}, {text:"Private for 30tk/min, max 10 min", frequency:"5"}, {text:"Pull nipples", frequency:"1"}, {text:"Tits Closeup", frequency:"1"}, {text:"Play boobs", frequency:"1"}, {text:"Pinch my nipples", frequency:"1"}, {text:"Nipple closeup", frequency:"1"}, {text:"Top off for 2 minutes", frequency:"1"}, {text:"Top off for 3 minutes", frequency:"1"}, {text:"Flash boobs", frequency:"1"}, {text:"Flash left boob", frequency:"1"}, {text:"Flash right boob", frequency:"1"}, {text:"Flash ass", frequency:"2"}, {text:"Flash pussy", frequency:"1"}, {text:"Flash choice, boobs, ass or pussy", frequency:"1"}, {text:"Prize of choice", frequency:"2"}, ]; var gPossiblePrizeListShower = [ //{text:"Take a quick shower", frequency:"2"}, {text:"Smile and look close into the camera", frequency:"2"}, {text:"Short Dance", frequency:"2"}, {text:"Blow a kiss", frequency:"2"}, {text:"Lick lips", frequency:"2"}, {text:"Tongue Tease", frequency:"3"}, {text:"Mod for 5 minutes", frequency:"2"}, {text:"Say 'I love you'", frequency:"5"}, {text:"Name on arm", frequency:"2"}, {text:"Name on ass", frequency:"1"}, {text:"Name on boobs", frequency:"1"}, {text:"Show Belly", frequency:"3"}, {text:"Show tongue", frequency:"3"}, {text:"Close up on my beautiful eyes", frequency:"2"}, {text:"Play your song", frequency:"2"}, {text:"Mod for a day", frequency:"3"}, {text:"No prize(( try again :**", frequency:"3"}, {text:"Private for 30tk/min, max 10 min", frequency:"5"}, {text:"Pull nipples", frequency:"1"}, {text:"Tits Closeup", frequency:"1"}, {text:"Play boobs", frequency:"2"}, {text:"Pinch my nipples", frequency:"2"}, {text:"Nipple closeup", frequency:"1"}, {text:"Top off for 2 minutes", frequency:"1"}, {text:"Top off for 3 minutes", frequency:"1"}, {text:"Flash boobs", frequency:"1"}, {text:"Flash ass", frequency:"2"}, {text:"Flash pussy", frequency:"1"}, {text:"Flash choice, boobs, ass or pussy", frequency:"1"}, {text:"Prize of choice", frequency:"2"}, ]; var gPossibleMultiGoal = [ {text:"Top Off", cost:"2000"}, {text:"Bottom Off", cost:"2500"}, {text:"Naked", cost:"3000"} ]; var gMultiGoalStats = { currentGoalLeft: 0, currentGoalIndex: -1 }; /* SETTINGS */ cb.settings_choices = [ /**** SETTINGS ****/ { name: 'nudeTipMenu', label: 'Use nude menu tipping', type: 'choice', choice1: 'yes', choice2: 'no', defaultValue: 'no' }, { name: 'karenKenoPrizeListType', label: 'Prize List for KarenKeno', type: 'choice', choice1: 'bedroom', choice2: 'shower', choice3: 'anyRoom', defaultValue: 'anyRoom' }, { name: 'karenKeno', label: 'Use KarenKeno?', type: 'choice', choice1: 'yes', choice2: 'no', defaultValue: 'yes' }, { name: 'multiGoal', label: 'Use MultiGoal?', type: 'choice', choice1: 'yes', choice2: 'no', defaultValue: 'yes' }, /*{ name: 'hiddenShow', label: 'Use hidden show?', type: 'choice', choice1: 'yes', choice2: 'no', defaultValue: 'no' }, */ { name: 'goal_description', label: 'Cleared Board Goal Description', type: 'str', minLength: 1, maxLength: 255, defaultValue: 'Clear the board!' }, { name: 'mvp_goal_description', label: 'Special prize for the MVP at game end', type: 'str', minLength: 0, maxLength: 255, required: false, defaultValue: 'My email and naked photo :kisses4u ' }, ]; /* MultiGoal */ for (i = 0; i < gPossibleMultiGoal.length; ++i) { goalIndex = i + 1; cb.settings_choices.push({ name: 'multiGoalText' + goalIndex, label: 'MultiGoal #' + goalIndex, type: 'str', minLength: 1, maxLength: 255, required: true, defaultValue: gPossibleMultiGoal[i].text }); cb.settings_choices.push({ name: 'multiGoalCost' + goalIndex, label: 'MultiGoal Value #' + goalIndex + ' (Leave at 0 to ignore goal)', type: 'int', minValue: 0, defaultValue: gPossibleMultiGoal[i].cost, required: true }); } /* ON TIP */ cb.onTip(function (tip) { gTotalTipToday += tip['amount']; last_tip_username = tip['from_user']; if(tip['amount'] > gHighTipAmount) { gHighTipAmount = tip['amount']; gHighTipUsername = tip['from_user']; } IncrementAdvertMessagesCount(); if (last_tip_username in playerStats) { playerStats[last_tip_username].totaltips += tip['amount'] } else { playerStats[last_tip_username] = { totaltips: tip['amount'] } } // handle hidden show tipping //handleHiddenShowTipping(tip); // update tip statistic if (!sentwin) { trackTips(last_tip_username, tip['amount']); } // first check if it's inside our menu list var text = getTextFromCost(tip['amount'], gTipMenuContainer); // if nth can be found, check nude menu, but only check it if nude menu is activated if(gUsingNudeMenu && (text == null || text == '')) { text = getTextFromCost(tip['amount'], gNudeMenuContainer); } // if no matches found even at this point, it can be used for karen keno if(text == null || text == '') { // karen keno tipping will be paused when hidden show goal has hit if(gEnableKarenKeno) { gTotalKenoTipToday += tip['amount']; handleTokenKenoTip(tip['amount'], tip['from_user']); if (numbersRemaining() < 1) { if (!sentwin) { drawBoard(null); var s = description + ' Board Cleared!'; if (!isBlank(cb.settings.goal_description)) { s += ' Goal met [' + cb.settings.goal_description + ']'; } cb.changeRoomSubject(s) } sentwin = true } } } if(gEnableMultiGoal) { // we only process multi-goal if there's still a goal, else skip it //TODO change it enum or something of it's equivalent in javascript if(gMultiGoalStats.currentGoalIndex != -1) // -1 is a special value for the multi goal stats that means that it's invalid { var multiGoalTokenLeft = gMultiGoalStats.currentGoalLeft - tip['amount']; // GOAL HIT! if(multiGoalTokenLeft <= 0) { var goalHitText = 'Goal Hit for ' + eval('cb.settings.multiGoalText' + (gMultiGoalStats.currentGoalIndex + 1)) + '!!!! :pixelheart :pixelheart :pixelheart Thank you all tipper)))) :*******'; ++gMultiGoalStats.currentGoalIndex; // increment to skip current goal var foundValidGoal = false; // now loop till we found a valid goal while(!foundValidGoal && gMultiGoalStats.currentGoalIndex < gPossibleMultiGoal.length) { // first we evaluate new goal amount to see if it's a valid amount var newGoalTokenCount = eval('cb.settings.multiGoalCost' + (gMultiGoalStats.currentGoalIndex + 1)); if(newGoalTokenCount > 0) { foundValidGoal = true; } else { ++gMultiGoalStats.currentGoalIndex; } } // we only update to the correct value if one exist if(foundValidGoal) { // update our goal to the new goal value gMultiGoalStats.currentGoalLeft = eval('cb.settings.multiGoalCost' + (gMultiGoalStats.currentGoalIndex + 1)); } // else set it to a invalid amount else { notify('Thank you for tipping me :kisses4u', null, '#FFFFFF', '#FA8072', null); gMultiGoalStats.currentGoalLeft = 0; gMultiGoalStats.currentGoalIndex = -1; } // display goal hit notification notify(goalHitText, null, '#FFFFFF', '#FF0000', null); } // else just subtract and indicate how many tokens left else { var goalHitText = multiGoalTokenLeft + ' more tokens for ' + eval('cb.settings.multiGoalText' + (gMultiGoalStats.currentGoalIndex + 1)) + '! :kisses4u'; notify(goalHitText, null, '#FFFFFF', '#FA8072', null); gMultiGoalStats.currentGoalLeft = multiGoalTokenLeft; } } } cb.drawPanel(); }); /* DRAW PANEL */ cb.onDrawPanel(function (user) { var boardText = ''; if(gBoardClearTipAmount > gTotalKenoTipToday) { boardText = (gTotalKenoTipToday) + '/' + (gBoardClearTipAmount - gTotalKenoTipToday); } else { boardText = '0/0'; } // our preference is always for hidden show if(gEnableHiddenShow) { return { 'template': '3_rows_of_labels', 'row1_label': 'VIP Count:', 'row1_value': gHiddenShowVIPList.length, 'row2_label': 'Next Goal (token left):', 'row2_value': gHiddenShowInitialGoalHit? (gHiddenShowSubGoalCost - gHiddenShowCurrentToken): (gHiddenShowInitialGoalCost - gHiddenShowCurrentToken), 'row3_label': 'MVP:', 'row3_value': formatUsername(gameStats.firstplace) + ' (' + gameStats.firsttips + ')' } } else if(gEnableMultiGoal) { if(gMultiGoalStats.currentGoalIndex != -1) { var multigoalTokenLeft = gMultiGoalStats.currentGoalLeft; var multiGoalText = eval('cb.settings.multiGoalText' + (gMultiGoalStats.currentGoalIndex + 1)); return { 'template': '3_rows_of_labels', 'row1_label': 'MultiGoal:', 'row1_value': multiGoalText, 'row2_label': 'MultiGoal Tokens Left:', 'row2_value': multigoalTokenLeft, 'row3_label': 'Board Current/Board Left:', 'row3_value': boardText } } // not sure why, // if i left the default text as the current one i wrote as variable, // it can't update... so i'm forced to write it down again else { return { 'template': '3_rows_of_labels', 'row1_label': 'MultiGoal:', 'row1_value': 'All goal met', 'row2_label': 'MultiGoal Tokens Left:', 'row2_value': '0', 'row3_label': 'Board Current/Board Left:', 'row3_value': boardText } } } // if only using karenKeno else { return { 'template': '3_rows_of_labels', 'row1_label': 'Highest Single Tipper:', 'row1_value': gHighTipUsername, 'row2_label': 'MVP:', 'row2_value': formatUsername(gameStats.firstplace) + ' (' + gameStats.firsttips + ')', 'row3_label': 'Board Current/Board Left:', 'row3_value': boardText, } } }); function IncrementAdvertMessagesCount() { // everytime we see a message, increment everyone's message count for (var key in gAdvertInformation) { if (gAdvertInformation.hasOwnProperty(key)) { ++gAdvertInformation[key].currentMsgCount; } } } /* ON MESSAGE */ cb.onMessage(function (msg) { var msgString = msg['m'].trim(); var isCommand = msgString.charAt(0) == '/'; if (isCommand) { // first we tokenize it var stringArray = msgString['split'](' '); // Turn string into array at whiteSpace boundaries if(stringArray.length != 0) { // first word is the command var command = stringArray[0].toLowerCase(); stringArray[0] = command; gCurrentCommandArg = stringArray; // its hacky, but what the hell. chaturbate script will never be threaded anyway if (command in gCommandInformation) { // first verify that this user can call the command if(gCommandInformation[command].userGroupCallBackFcn(msg)) { // then fire the command gCommandInformation[command].callBackFcn(msg); } } // inform user that it's an invalid command so they dont go wondering why nothing happens else { notify('Unrecognized command: ' + command, null, '#FFFFFF', '#FF0000', null); } } } if(msg['m'] && msg['m'] != '') { IncrementAdvertMessagesCount(); // if fellow in idle list, remove him and add him back to vip list var index = getArrayIndex(msg['user'], gHiddenShowIdleVIPList); if(index != -1) { gHiddenShowIdleVIPList.splice(index, 1); cb.limitCam_addUsers(msg['user']); } // reset it each time we encounter his message if (msg['user'] in gHIddenShowIdleTimeTracker) { gHIddenShowIdleTimeTracker[msg['user']].minuteSinceLastMessage = 0; } } // add a vip tag for everyone that is in the VIP list if(inArray(msg['user'], gHiddenShowVIPList)) { msg['m'] = '[VIP] ' + msg['m']; } // if muting is on, forward their message directly to me else if(gMuteNonVIPTalking) { if(cb.limitCam_isRunning()) { msg['X-Spam'] = true; // mute them! // still need to forward their message to me so that i can read what they need notify(msg['user'] + ': ' + msg['m'], gSpecialUser, '#FFFFFF', '#BB8FCE', null); } } return msg; }); /* ON TIP OPTION */ /* cb.tipOptions(function (user) { return }); */ /* Initialization */ function init() { // this ensure app restart doesn't screw up //gTotalTipToday = 0; // we don't want it resetting since we want to know how much token we earned in total gTotalKenoTipToday = 0; gHighTipUsername = null; gHighTipAmount = 0; last_tip_username = null; gBoardClearTipAmount = 0; gHiddenShowCurrentLength = gHiddenShowInitialLength; // indicate the current running length of hidden show gHiddenShowCurrentToken = 0; // indicate token count for current goal gHiddenShowInitialGoalHit = false; // indicate if we need double cost for ticket gHiddenShowCoolingDown = false; // indicate if hidden show is currently cooling down (meaning you can't start your hidden show while cooling down, this prevent multiple count down advert bug) gHiddenShowVIPList = new Array(); // indicate all VIP ticket holders gHiddenShowTippers = new Array(); gHiddenShowReset = true; gHiddenShowIdleVIPList = new Array(); // list of all VIP that has been temporarily removed from vip list gHIddenShowIdleTimeTracker = new Array(); // key is user name, and value is just a boolean on whether the person messaged in the last gMaxVIPIdleTime minute gHiddenShowSaleTicketCooldown = false; gHiddenShowSaleSubgoalCooldown = false; // you need to populate this, refer to init() for more information gAdvertInformation = new Array(); gCommandInformation = new Array(); // setup our multi goal value for(var i = 0; i < gPossibleMultiGoal.length; ++i) { var cost = eval('cb.settings.multiGoalCost' + (i + 1)); if(cost != 0) { gMultiGoalStats.currentGoalLeft = cost; gMultiGoalStats.currentGoalIndex = i; break; } } // update our global variable depending on user setting gUsingNudeMenu = cb.settings.nudeTipMenu == 'yes'; gEnableKarenKeno = cb.settings.karenKeno == 'yes'; gEnableMultiGoal = cb.settings.multiGoal == 'yes'; //gEnableHiddenShow = cb.settings.hiddenShow == 'yes'; initKarenKeno(); initCommand(); // initialize our required information for notification // it's given in a key value pair where key refers to the function to trigger the notification // intialTimer refers to the initial timer before it's fired // periodicTimer refers to the periodic timer after the first firing // minMsgCount refers to the minimum number of messages before the message can fire (this prevent us from spamming the room when no one is typing) // maxNoFireCount refers to the max number of time we can avoid firing the notification before we will force it to fire gAdvertInformation[advertGamerules] = { initialTimer: 0.8 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 2}; gAdvertInformation[advertMVP] = { initialTimer: 0.65 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 8 }; gAdvertInformation[advertMultiGoalTarget] = { initialTimer: 0.2 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 6 }; gAdvertInformation[advertLeaderboard] = { initialTimer: 0.5 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 7 }; gAdvertInformation[advertHiddenShowSummaryInfo] = { initialTimer: 0.5 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 6 }; gAdvertInformation[advertHiddenShowTicketInfo] = { initialTimer: 0.3 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 4 }; gAdvertInformation[advertHiddenShowSubGoalInfo] = { initialTimer: 0.4 * CONFIG_ADVERT_MINUTES, periodicTimer: CONFIG_ADVERT_MINUTES, minMsgCount: 10, maxNoFireCount: 5 }; gAdvertInformation[advertHiddenShowTimer] = { initialTimer: TIME_ONE_MINUTE, periodicTimer: TIME_ONE_MINUTE, minMsgCount: 0, maxNoFireCount: 0 }; gAdvertInformation[advertHiddenShowSaleTicket] = { initialTimer: TIME_ONE_SEC, periodicTimer: TIME_ONE_SEC, minMsgCount: 0, maxNoFireCount: 0 }; gAdvertInformation[advertHiddenShowSaleSubgoal] = { initialTimer: TIME_ONE_SEC, periodicTimer: TIME_ONE_SEC, minMsgCount: 0, maxNoFireCount: 0 }; // start our periodic timer cb.sendNotice(EMOTE_TITLE, '', '', '', 'bold'); // automatically initialize those required variable, this is basically constructor in c++ class for (var key in gAdvertInformation) { if (gAdvertInformation.hasOwnProperty(key)) { gAdvertInformation[key].currentMsgCount = 0; // number of msg we have seen since we last fire it gAdvertInformation[key].missedFireCount = gAdvertInformation[key].maxNoFireCount; // number of time we skipped firing the messages (set to max to ensure it's fired) // this doesn't work.. no idea why // can try passing the function as another variable (cause i guess using it as a key force it into a string?) // if it's leaderboard, only show it if user wants to //cb.setTimeout(key, gAdvertInformation[key].initialTimer); } } cb.setTimeout(advertLeaderboard, gAdvertInformation[advertLeaderboard].initialTimer); cb.setTimeout(advertGamerules, gAdvertInformation[advertGamerules].initialTimer); cb.setTimeout(advertMVP, gAdvertInformation[advertMVP].initialTimer); cb.setTimeout(advertMultiGoalTarget, gAdvertInformation[advertMultiGoalTarget].initialTimer); cb.setTimeout(advertHiddenShowSummaryInfo, gAdvertInformation[advertHiddenShowSummaryInfo].initialTimer); cb.setTimeout(advertHiddenShowTicketInfo, gAdvertInformation[advertHiddenShowTicketInfo].initialTimer); cb.setTimeout(advertHiddenShowSubGoalInfo, gAdvertInformation[advertHiddenShowSubGoalInfo].initialTimer); } init(); /* Helper function */ // NOTE: call this whenever you enable/disable a mode function initKarenKeno() { sentwin = false; boardNumbers = new Array(); boardPrizes = {}; boardPrizesLeft = 0; board = ''; prizes = ''; userPrizes = new Array(); playerStats = {}; gameStats = {firstplace: '',secondplace: '',thirdplace: '',firsttips: 0,secondtips: 0,thirdtips: 0}; partialTips = {}; ignoredTips = new Array(); // NOTE: board number is a fixed value, no need to change it gBoardNumMin = 20; gBoardNumMax = gBoardNumMin + 1; var possiblePrizeList = new Array(); if(cb.settings.karenKenoPrizeListType == 'bedroom') { possiblePrizeList = gPossiblePrizeListRoom; } else if(cb.settings.karenKenoPrizeListType == 'shower') { possiblePrizeList = gPossiblePrizeListShower; } else if(cb.settings.karenKenoPrizeListType == 'anyRoom') { possiblePrizeList = gPossiblePrizeListNeutral; } // check how many times each prize should appear and start populating for(i = 0; i < possiblePrizeList.length; ++i) { for(j = 0; j < possiblePrizeList[i].frequency; ++j) { actualPrizeList.push(possiblePrizeList[i].text); } } // now update our boardMax amount after we got all the prizes we need gBoardNumMax = gBoardNumMin + actualPrizeList.length; /* now check if menu is listed inside board, if yes, we need to remove it */ if(gTipMenuContainer) { for(var i = 0; i < gTipMenuContainer.length; ++i) { if(gTipMenuContainer[i].cost >= gBoardNumMin && gTipMenuContainer[i].cost <= gBoardNumMax) { ignoredTips.push(gTipMenuContainer[i].cost); ++gBoardNumMax; } } for(var i = 0; i < gNudeMenuContainer.length; ++i) { if(gNudeMenuContainer[i].cost >= gBoardNumMin && gNudeMenuContainer[i].cost <= gBoardNumMax) { notifyErrorBold('Nude menu with cost ' + gNudeMenuContainer[i].cost + ' is inside KarenKenoBoard, they are ignored, make sure nude menu doesnt clash', cb.room_slug); } } } setupBoard(); updateBoard(); updatePrizes(); updateSubject(); drawBoard(null); cb.drawPanel(); } function initCommand() { gCommandInformation = new Array(); // initialize our command information // public command gCommandInformation['/help'] = { callBackFcn: handleHelpCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display all available command in KarenKeno' }; if(gEnableKarenKeno) { gCommandInformation['/board'] = { callBackFcn: handleBoardCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display KarenKeno board' }; gCommandInformation['/prize'] = { callBackFcn: handlePrizeCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display KarenKeno prizelist' }; gCommandInformation['/leaderboard'] = { callBackFcn: handleLeaderBoardCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display KarenKeno Leaderboard' }; } if(gEnableMultiGoal) { gCommandInformation['/multigoal'] = { callBackFcn: handleMultiGoalCommand, userGroupCallBackFcn: checkIsViewer }; } if(gEnableHiddenShow) { gCommandInformation['/hiddenvip'] = { callBackFcn: handleCommandHiddenVIPList, userGroupCallBackFcn: checkIsViewer, helpText: 'display vip list for hidden show' }; gCommandInformation['/hiddenshow'] = { callBackFcn: handleHiddenShowCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display hidden show information' }; gCommandInformation['/hiddenticket'] = { callBackFcn: handleHiddenTicketCommand, userGroupCallBackFcn: checkIsViewer, helpText: 'display token left for current user to get hidden show ticket' }; } // privilege user command gCommandInformation['/helpall'] = { callBackFcn: handleHelpCommandAll, userGroupCallBackFcn: checkHasPrivileges, helpText: 'send /help to everyone' }; if(gEnableKarenKeno) { gCommandInformation['/boardall'] = { callBackFcn: handleBoardCommandAll, userGroupCallBackFcn: checkHasPrivileges, helpText: 'send /board to everyone' }; gCommandInformation['/prizeall'] = { callBackFcn: handlePrizeCommandAll, userGroupCallBackFcn: checkHasPrivileges, helpText: 'send /prize to everyone' }; gCommandInformation['/leaderboardall'] = { callBackFcn: handleLeaderBoardCommandAll, userGroupCallBackFcn: checkHasPrivileges, helpText: 'send /leaderboard to everyone' }; } if(gEnableHiddenShow) { gCommandInformation['/hiddenshowall'] = { callBackFcn: handleHiddenShowCommandAll, userGroupCallBackFcn: checkHasPrivileges, helpText: 'send /hiddenshow to everyone' }; } if(gEnableMultiGoal) { gCommandInformation['/multigoalall'] = { callBackFcn: handleMultiGoalCommandAll, userGroupCallBackFcn: checkHasPrivileges }; } // host command //gCommandInformation['/totaltoken'] = { callBackFcn: handleTotalTokenCommand, userGroupCallBackFcn: checkIsHost, helpText: 'show total token gained so far (for this app, private and group arent included)' }; gCommandInformation['/nudemenu'] = { callBackFcn: handleNudeMenuCommandToggle, userGroupCallBackFcn: checkIsHost, helpText: 'toggle nude menu' }; gCommandInformation['/togglemultigoal'] = { callBackFcn: handleCommandToggleMultiGoal, userGroupCallBackFcn: checkIsHost, helpText: 'toggle multi-goal' }; //gCommandInformation['/togglehiddenshow'] = { callBackFcn: handleCommandToggleHiddenShow, userGroupCallBackFcn: checkIsHost, helpText: 'toggle hidden show' }; gCommandInformation['/togglekarenkeno'] = { callBackFcn: handleCommandToggleKarenKeno, userGroupCallBackFcn: checkIsHost, helpText: 'toggle karen keno' }; if(gEnableHiddenShow) { //gCommandInformation['/karenkenorestart'] = { callBackFcn: handleCommandKarenKenoRestart, userGroupCallBackFcn: checkIsHost, helpText: 'restart KarenKeno' }; gCommandInformation['/togglenonvipchat'] = { callBackFcn: handleCommandToggleNonVIPChat, userGroupCallBackFcn: checkIsHost, helpText: 'toggle chatting for non vip when hidden cam is running' }; gCommandInformation['/toggleidlevip'] = { callBackFcn: handleCommandToggleIdleVIP, userGroupCallBackFcn: checkIsHost, helpText: 'toggle idle vip (remove VIP from seeing cam if they dont type for a period of time (this is to prevent cam recorder' }; gCommandInformation['/hiddenlistidle'] = { callBackFcn: handleCommandHiddenShowListIdle, userGroupCallBackFcn: checkIsHost, helpText: 'List all idle VIP viewer (that has been blocked from viewing)' }; gCommandInformation['/hiddenstart'] = { callBackFcn: handleCommandHiddenStart, userGroupCallBackFcn: checkIsHost, helpText: 'start hidden show' }; gCommandInformation['/hiddenstop'] = { callBackFcn: handleCommandHiddenStop, userGroupCallBackFcn: checkIsHost, helpText: 'end hidden show (if hidden show timer is still counting down, this will reset the timer too)' }; gCommandInformation['/hiddenadd'] = { callBackFcn: handleCommandHiddenAdd, userGroupCallBackFcn: checkIsHost, helpText: 'add user to hidden show' }; gCommandInformation['/hiddenremove'] = { callBackFcn: handleCommandHiddenRemove, userGroupCallBackFcn: checkIsHost, helpText: 'remove user from hidden show' }; gCommandInformation['/hiddensaleticket'] = { callBackFcn: handleCommandHiddenSaleTicket, userGroupCallBackFcn: checkIsHost, helpText: 'Flash sale for half price hidden show ticket for x secs, if none stated, it will be 60 sec' }; gCommandInformation['/hiddensalesubgoal'] = { callBackFcn: handleCommandHiddenSaleSubgoal, userGroupCallBackFcn: checkIsHost, helpText: 'Flash sale for half price sub goal for x secs' }; gCommandInformation['/sethiddentimer'] = { callBackFcn: handleCommandSetHiddenTimer, userGroupCallBackFcn: checkIsHost, helpText: 'set current hidden show timer' }; gCommandInformation['/sethiddengoal'] = { callBackFcn: handleCommandSetHiddenGoal, userGroupCallBackFcn: checkIsHost, helpText: 'set hidden show goal price' }; gCommandInformation['/sethiddensubgoal'] = { callBackFcn: handleCommandSetHiddenSubGoal, userGroupCallBackFcn: checkIsHost, helpText: 'set hidden show sub goal price' }; gCommandInformation['/sethiddenticket'] = { callBackFcn: handleCommandSetHiddenTicket, userGroupCallBackFcn: checkIsHost, helpText: 'set hidden show ticket price' }; gCommandInformation['/sethiddenticketmultiplier'] = { callBackFcn: handleCommandSetTicketMultiplier, userGroupCallBackFcn: checkIsHost, helpText: 'set hidden show ticket multiplier after goal met' }; gCommandInformation['/sethiddenidletime'] = { callBackFcn: handleCommandSetHiddenIdleTime, userGroupCallBackFcn: checkIsHost, helpText: 'set hidden show max idle time before getting booted from view list' }; } } // recurring advertisement on how to play karen keno function advertGamerules() { if (!sentwin) { var text = ''; if(gEnableKarenKeno) { text += EMOTE_TITLE + ' Tip a number on the board to play! Type /board to see the board. Type /prize to see prizes remaining';//. Type /multigoal to see multi-goal.'; } handleAdvertFiring(text, '', '', '', 'bold', advertGamerules); } } // recurring advertisement of what mvp wins function advertMVP() { if (!sentwin) { if (!isBlank(cb.settings.mvp_goal_description)) { var text = ''; if(gEnableKarenKeno) { text += 'MVP wins [' + cb.settings.mvp_goal_description + ' ] when board is cleared!'; } handleAdvertFiring(text, null, '#FFFFFF', '#000000', null, advertMVP); } } } // recurring advertisement on leaderboard function advertLeaderboard() { if (!sentwin) { var text = ''; if(gEnableKarenKeno) { text += getLeaderBoard(); } handleAdvertFiring(text, null, '#FFFFFF', '#000000', '', advertLeaderboard); } } // display all remaining multi-goal and tokens to hit it function getMultiGoalText() { var goalText = ''; var accumulatedTip = gMultiGoalStats.currentGoalLeft; if(gMultiGoalStats.currentGoalIndex != -1) { for(var i = gMultiGoalStats.currentGoalIndex; i < gPossibleMultiGoal.length; ++i) { var currentGoalCost = eval('cb.settings.multiGoalCost' + (i + 1)); if(currentGoalCost > 0) { // if it's the current goal, it probably has some token subtracted, so we start from there instead if(i != gMultiGoalStats.currentGoalIndex) { accumulatedTip += currentGoalCost; } goalText += 'Goal #' + (i + 1) + ' '+ eval('cb.settings.multiGoalText' + (i + 1)) + ' in ' + accumulatedTip + ' tokens\n'; } } } else { goalText = 'All goal met!! Thanks for tipping :kisses4u :kisses4u\n' } return goalText; } // recurring advertisement on multi-goal function advertMultiGoalTarget() { var goalText = ''; if(!gEnableMultiGoal) { goalText = getMultiGoalText(); } handleAdvertFiring(goalText, null, '#FFFFFF', '#87CEEB', null, advertMultiGoalTarget); } function getHiddenShowGoalText() { var text = ''; if(gHiddenShowInitialGoalHit) { // ensure user knows we hit the goal and further tipping only extends the hidden show if(!cb.limitCam_isRunning()) { text += 'Goal met for hidden show! Hidden show will start shortly!\x0A'; } text += 'Tip ' + (gHiddenShowSubGoalCost - gHiddenShowCurrentToken) + ' more tokens to add ' + gHiddenShowSubGoalLength + ' min to hidden show (' +gHiddenShowCurrentLength + ' min left for hidden show)'; } else { text += (gHiddenShowInitialGoalCost - gHiddenShowCurrentToken) + ' more tokens to start ' + gHiddenShowCurrentLength + ' min hidden show'; } return text; } function getHiddenShowSummaryInfo() { var text = 'Hidden Show Information: \x0A'; if(gEnableHiddenShow) { text += 'Hidden Show Length: ' + gHiddenShowCurrentLength + ' mins (After subgoal hit, every ' + gHiddenShowSubGoalCost + ' token increase hidden show length by ' + gHiddenShowSubGoalLength + ' min)\x0A'; text += 'Ticket Cost: Combined tip of ' + gHiddenShowTicketCost + ' tokens. (Ticket cost ' + Math.ceil(gHiddenShowTicketCost * gHiddenShowStartedMultiplier) + ' token after show started)\x0A'; text += 'Ticket Discount (for fan/mod): ' + gHiddenShowTicketPrivilegeDiscount * 100 + '% discount\x0A'; text += 'Ticket Cost Command: Type /hiddenticket to view current ticket cost for you\x0A'; if(!gHiddenShowInitialGoalHit) { text += 'Tokens Left to Start Show: ' + (gHiddenShowInitialGoalCost - gHiddenShowCurrentToken) + ' tokens\x0A'; } else { text += 'Tokens Left to Start Show: Goal Met!\x0A'; } text += 'Subgoal (After Goal Met): Every ' + gHiddenShowSubGoalCost + ' tokens extend hidden show by ' + gHiddenShowSubGoalLength + ' minute\x0A'; } return text; } // advert on how much more to goal function advertHiddenShowSummaryInfo() { var text = ''; if(gEnableHiddenShow) { text = getHiddenShowSummaryInfo(); } handleAdvertFiring(text, null, '#FFFFFF', '#FF5733', null, advertHiddenShowSummaryInfo); } // advert on subgoal extension function advertHiddenShowSubGoalInfo() { var text = ''; if(gEnableHiddenShow) { text = 'After reaching hidden show goal, every ' + gHiddenShowSubGoalCost + ' tokens extend hidden show by ' + gHiddenShowSubGoalLength + ' min'; } handleAdvertFiring(text, null, '#FFFFFF', '#87CEEB', null, advertHiddenShowSubGoalInfo); } function advertHiddenShowTimer() { // if show is running and is enabled, we will subtract the timer by 1 minute each time it fire // periodic timer is also 1 minute thats why we subtract by 1 min if(gEnableHiddenShow && cb.limitCam_isRunning()) { // only count down if it has yet to end if(gHiddenShowCurrentLength >= 1) { gHiddenShowCurrentLength -= 1; // handle idle VIP user removal // increment the timer by 1 minute every time this loops // and if it exceed the threshold, remove them from VIP list for(var i = 0; i < gHiddenShowVIPList.length; ++i) { var user = gHiddenShowVIPList[i]; if (user in gHIddenShowIdleTimeTracker) { gHIddenShowIdleTimeTracker[user].minuteSinceLastMessage += 1; } else { gHIddenShowIdleTimeTracker[user] = { minuteSinceLastMessage: 1 } } if(gNoIdleVIP) { if(gHIddenShowIdleTimeTracker[user].minuteSinceLastMessage >= gHiddenShowMaxVIPIdleTime) { gHiddenShowIdleVIPList.push(user); cb.limitCam_removeUsers(user); } } } var text = ''; // we show a different text depending on whether there's still time left if(gHiddenShowCurrentLength > 0) { text = gHiddenShowCurrentLength + ' mins left for hidden show (Tip to extend hidden show!!!)'; } else { text = ':kisses4u Thanks for viewing my hidden show! :kisses4u' resetHiddenShow(); // NOTE: we only remove everyone from hidden show } handleAdvertFiring(text, null, '#FFFFFF', '#FF0000', null, advertHiddenShowTimer); } else { gHiddenShowCoolingDown = false; } } else { gHiddenShowCoolingDown = false; // this ensure the advert is no longer firing } } // TODO change it into a function function advertHiddenShowSaleTicket() { // NOTE: timer counts down every sec but we only display the text every sec during the last 10 sec if(gEnableHiddenShow) { // only count down if it has yet to end if(gHiddenShowSaleTicketCurrentTimer >= 1) { gHiddenShowSaleTicketCurrentTimer -= 1; var text = ''; // we show a different text depending on whether there's still time left if(gHiddenShowSaleTicketCurrentTimer > 0) { // only display timer every sec during the last 10 sec or every 10 sec if(gHiddenShowSaleTicketCurrentTimer < 10 || !(gHiddenShowSaleTicketCurrentTimer % 10)) { text = gHiddenShowSaleTicketCurrentTimer + ' secs left for half price ticket flash sale'; } } else { text = 'End of half price ticket flash sale!!!'; } handleAdvertFiring(text, null, '#FFFFFF', '#FF0000', null, advertHiddenShowSaleTicket); } else { hiddenShowSetTicket(gHiddenShowTicketInitialCost); gHiddenShowSaleTicketCooldown = false; } } else { hiddenShowSetTicket(gHiddenShowTicketInitialCost); gHiddenShowSaleTicketCooldown = false; // this ensure the advert is no longer firing } } function advertHiddenShowSaleSubgoal() { // NOTE: timer counts down every sec but we only display the text every sec during the last 10 sec if(gEnableHiddenShow) { // only count down if it has yet to end if(gHiddenShowSaleSubgoalCurrentTimer >= 1) { gHiddenShowSaleSubgoalCurrentTimer -= 1; var text = ''; // we show a different text depending on whether there's still time left if(gHiddenShowSaleSubgoalCurrentTimer > 0) { // only display timer every sec during the last 10 sec or every 10 sec if(gHiddenShowSaleSubgoalCurrentTimer < 10 || !(gHiddenShowSaleSubgoalCurrentTimer % 10)) { text = gHiddenShowSaleSubgoalCurrentTimer + ' secs left for half price subgoal flash sale'; } } else { text = 'End of half price subgoal flash sale!!!'; } handleAdvertFiring(text, null, '#FFFFFF', '#FF0000', null, advertHiddenShowSaleSubgoal); } else { hiddenShowSetSubgoal(gHiddenShowSubgoalInitialCost); gHiddenShowSaleSubgoalCooldown = false; } } else { hiddenShowSetSubgoal(gHiddenShowSubgoalInitialCost); gHiddenShowSaleSubgoalCooldown = false; // this ensure the advert is no longer firing } } function getHiddenShowTicketInfo() { var text = ''; if(gEnableHiddenShow) { text +='A combined tip of ' + gHiddenShowTicketCost + ' tokens gets you ticket for a ' + gHiddenShowCurrentLength + ' min hidden show\x0A'; text += 'Combined tip of ' + Math.ceil(gHiddenShowTicketCost * gHiddenShowStartedMultiplier) + ' token is needed after hidden show goal is reached to get a ticket\x0A'; text += 'Fan/Mod gets ' + gHiddenShowTicketPrivilegeDiscount * 100 + '% discount\x0A'; } return text; } // advert on hidden show ticket information function advertHiddenShowTicketInfo() { var text = ''; if(gEnableHiddenShow) { text += getHiddenShowTicketInfo(); } handleAdvertFiring(text, null, '#FFFFFF', '#87CEEB', null, advertHiddenShowTicketInfo); } // this function handles the notification firing mechanism, all you have to pass in are the information on what to show (if it fires) // and the function name, cause the rest of them are handled automatically function handleAdvertFiring(text, userGroup, backgroundColor, foregroundColor, textEffect, functionName) { // if there's a need to fire it, fire it! if(gAdvertInformation[functionName].currentMsgCount >= gAdvertInformation[functionName].minMsgCount || gAdvertInformation[functionName].missedFireCount >= gAdvertInformation[functionName].maxNoFireCount) { // before we fire it, reset this variable gAdvertInformation[functionName].currentMsgCount = 0; gAdvertInformation[functionName].missedFireCount = 0; // nothing to show, no reason to do anything if(text != null && text != '') { notify(text, userGroup, backgroundColor, foregroundColor, textEffect); } } else { // since we didnt fire this time round, increment the counter ++gAdvertInformation[functionName].missedFireCount; } // either way we schedule the timer for firing again cb.setTimeout(functionName, gAdvertInformation[functionName].periodicTimer) } function handleCommandHiddenVIPList(msg) { if(gEnableHiddenShow) { msg['m'] = msg['m'] + " (Hidden show VIP list sent to " + msg['user'] + ")"; notify(getHiddenShowVIPList(), '', '#FFFFFF', '#000000', ''); } } function handleCommandHiddenStart(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up startHiddenShow(); } function handleCommandHiddenStop(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up stopHiddenShow(); } function handleCommandSetHiddenTimer(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { var oldValue = gHiddenShowCurrentLength; gHiddenShowCurrentLength = gCurrentCommandArg[1]; notify('Hidden show timer changed from ' + oldValue + ' (minute) to ' + gCurrentCommandArg[1] + ' (minute)', '', '#FFFFFF', '#FF0000', null); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandHiddenAdd(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { addUserToHiddenShow(gCurrentCommandArg[1]); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandHiddenRemove(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { removeUserFromHiddenShow(gCurrentCommandArg[1]); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandHiddenSaleTicket(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up // we need to check that the current cooldown has expired, we don't want multiple concurrent ticket sale running if(!gHiddenShowSaleTicketCooldown) { gHiddenShowSaleTicketCooldown = true; gHiddenShowSaleTicketCurrentTimer = gHiddenShowSaleTicketDefaultTimer; if(gCurrentCommandArg.length >= 2) { gHiddenShowSaleTicketCurrentTimer = gCurrentCommandArg[1]; } notify('Hidden show ticket flash sale for ' + gHiddenShowSaleTicketCurrentTimer + ' seconds', '', '#FFFFFF', '#FF0000', null); // change our price by halving it from initial amount hiddenShowSetTicket(gHiddenShowTicketInitialCost/2); // fire timer cb.setTimeout(advertHiddenShowSaleTicket, gAdvertInformation[advertHiddenShowSaleTicket].initialTimer); } else { notify('A flash sale of the same type is still running', 'roomHost', '#FFFFFF', '#FF0000', null); } } function handleCommandHiddenSaleSubgoal(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gHiddenShowInitialGoalHit) { // we need to check that the current cooldown has expired, we don't want multiple concurrent ticket sale running if(!gHiddenShowSaleSubgoalCooldown) { gHiddenShowSaleSubgoalCooldown = true; gHiddenShowSaleSubgoalCurrentTimer = gHiddenShowSaleSubgoalDefaultTimer; if(gCurrentCommandArg.length >= 2) { gHiddenShowSaleSubgoalCurrentTimer = gCurrentCommandArg[1]; } notify('Hidden show subgoal flash sale for ' + gHiddenShowSaleSubgoalCurrentTimer + ' seconds', '', '#FFFFFF', '#FF0000', null); // change our price by halving it from initial amount hiddenShowSetSubgoal(gHiddenShowSubgoalInitialCost/2); // fire timer cb.setTimeout(advertHiddenShowSaleSubgoal, gAdvertInformation[advertHiddenShowSaleSubgoal].initialTimer); } else { notify('A flash sale of the same type is still running', 'roomHost', '#FFFFFF', '#FF0000', null); } } else { notify('Flash sale subgoal before hidden show goal is met doesnt make any sense...', 'roomHost', '#FFFFFF', '#FF0000', null); } } function handleCommandSetHiddenGoal(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { var oldValue = gHiddenShowInitialGoalCost; gHiddenShowInitialGoalCost = gCurrentCommandArg[1]; if(checkHiddenShowGoalHit()) { gHiddenShowCurrentToken = 0; // we need to force it to zero, so the remaining doesnt roll over and become our subgoal pool } notify('Hidden show goal changed from ' + oldValue + ' tokens to ' + gCurrentCommandArg[1] + ' tokens', '', '#FFFFFF', '#FF0000', null); notify(getHiddenShowGoalText(), '', '#FFFFFF', '#FF0000', ''); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandSetHiddenIdleTime(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { var oldValue = gHiddenShowMaxVIPIdleTime; gHiddenShowMaxVIPIdleTime = gCurrentCommandArg[1]; notify('Hidden show max idle time changed from ' + oldValue + ' to ' + gHiddenShowMaxVIPIdleTime, 'roomHost', '#FFFFFF', '#FF0000', null); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandSetHiddenSubGoal(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { hiddenShowSetSubgoal(gCurrentCommandArg[1]); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function hiddenShowSetTicket(newPrice) { var newFinalPrice = Math.ceil(newPrice); var oldValue = gHiddenShowTicketCost; gHiddenShowTicketCost = newFinalPrice; notify('Hidden show ticket cost changed from ' + oldValue + ' (tokens) to ' + newFinalPrice + ' (tokens)', '', '#FFFFFF', '#FF0000', null); // now run thru all current user and check if any of them met the goal (add them to the list if they met the goal) for(var key in gHiddenShowTippers) { // i'm treating everyone as none privilege user, not the best way to handle it, // but for mod/fan, they can always just tip 1 token to get in // there is no good alternative other than this if(gHiddenShowTippers[key].totalTips >= getHiddenShowTicketCostForUser(false)) { addUserToHiddenShow(key); } } } function hiddenShowSetSubgoal(newPrice) { var oldValue = gHiddenShowSubGoalCost; gHiddenShowSubGoalCost = newPrice; // this ensure we don't waste any of the existing subgoal tokens processSubGoal(); notify('Hidden show sub goal changed from ' + oldValue + ' tokens to ' + newPrice + ' tokens', '', '#FFFFFF', '#FF0000', null); notify(getHiddenShowGoalText(), '', '#FFFFFF', '#FF0000', ''); } function handleCommandSetHiddenTicket(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { hiddenShowSetTicket(gCurrentCommandArg[1]); } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function handleCommandSetTicketMultiplier(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up if(gCurrentCommandArg.length >= 2) { var oldValue = gHiddenShowStartedMultiplier; gHiddenShowStartedMultiplier = gCurrentCommandArg[1]; notify('Hidden show ticket started multiplier changed from ' + (oldValue * 100) + '% to ' + (gCurrentCommandArg[1] * 100) + '%', '', '#FFFFFF', '#FF0000', null); // since multiplier only applies when we hit goal, it's pointless to perform this check if goal has yet to hit if(gHiddenShowInitialGoalHit) { // now run thru all current user and check if any of them met the goal (add them to the list if they met the goal) for(var key in gHiddenShowTippers) { // i'm treating everyone as non-privilege user, not the best way to handle it, // but for mod/fan, they can always just tip 1 token to get in // there is no good alternative other than this if(gHiddenShowTippers[key].totalTips >= getHiddenShowTicketCostForUser(false)) { addUserToHiddenShow(key); } } } } else { notify('Invalid argument', msg['user'], '#FFFFFF', '#FF0000', null); } } function getCommandHelpText(msg) { var text = ''; for (var key in gCommandInformation) { if (gCommandInformation.hasOwnProperty(key) && gCommandInformation[key].userGroupCallBackFcn(msg)) { text += key + ' - ' + gCommandInformation[key].helpText + '\x0A'; } } return text; } function handleHelpCommand(msg) { msg['m'] = msg['m'] + " (Available command sent to " + msg['user'] + ")"; notify(getCommandHelpText(msg), msg['user'], '#FFFFFF', '#BB8FCE', null); } function handleBoardCommand(msg) { msg['m'] = msg['m'] + " (token keno: board sent to " + msg['user'] + ")"; drawBoard(msg['user']) } function handlePrizeCommand(msg) { msg['m'] = msg['m'] + " (token keno: prize list sent to " + msg['user'] + ")"; drawPrizes(msg['user']) } function handleLeaderBoardCommand(msg) { msg['m'] = msg['m'] + " (token keno: leaderboard sent to " + msg['user'] + ")"; cb.sendNotice(getLeaderBoard(), msg['user']); } function handleMultiGoalCommand(msg) { if(gEnableMultiGoal) { msg['m'] = msg['m'] + " (multi-goal: multi-goal sent to " + msg['user'] + ")"; notify(getMultiGoalText(), msg['user'], '#FFFFFF', '#87CEEB', null); } } function handleMultiGoalCommandAll(msg) { msg['m'] = msg['m'] + " (multi-goal: multi-goal sent to all)"; notify(getMultiGoalText(), null, '#FFFFFF', '#87CEEB', null); } function handleHelpCommandAll(msg) { msg['m'] = msg['m'] + " (Available command sent to all)"; notify(getCommandHelpText(msg), '', '#FFFFFF', '#BB8FCE', null); } function handleLeaderBoardCommandAll(msg) { msg['m'] = msg['m'] + " (token keno: leaderboard sent to all)"; cb.sendNotice(getLeaderBoard()) } function handlePrizeCommandAll(msg) { msg['m'] = msg['m'] + " (token keno: prize list sent to all)"; drawPrizes() } function handleBoardCommandAll(msg) { msg['m'] = msg['m'] + " (token keno: board sent to all)"; drawBoard(null) } function handleHiddenShowCommand(msg) { msg['m'] = msg['m'] + " (hidden show: info sent to " + msg['user'] + ")"; notify(getHiddenShowSummaryInfo(), msg['user'], '#FFFFFF', '#FF5733', null); } function handleHiddenTicketCommand(msg) { msg['m'] = msg['m'] + " (hidden show: ticket cost sent to " + msg['user'] + ")"; var currentTip = 0; if((msg['user'] in gHiddenShowTippers)) { currentTip = gHiddenShowTippers[msg['user']].totalTips; } cb.sendNotice((getHiddenShowTicketCostForUser(checkHasPrivileges(msg)) - currentTip) + ' more tokens to get a ticket for hidden show', msg['user']); } function handleHiddenShowCommandAll(msg) { msg['m'] = msg['m'] + " (hidden show: info sent to all)"; notify(getHiddenShowSummaryInfo(), '', '#FFFFFF', '#FF5733', null); } function handleNudeMenuCommandToggle(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up gUsingNudeMenu = !gUsingNudeMenu; var text = 'Use nude menu: ' + (gUsingNudeMenu? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandToggleMultiGoal(msg) { if(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up } gEnableMultiGoal = !gEnableMultiGoal; cb.drawPanel(); initCommand(); var text = 'Use multi-goal: ' + (gEnableMultiGoal? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandToggleHiddenShow(msg) { if(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up } gEnableHiddenShow = !gEnableHiddenShow; cb.drawPanel(); resetHiddenShow(); // always reset hidden show everytime we toggle this initCommand(); var text = 'Use hidden show: ' + (gEnableHiddenShow? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandToggleKarenKeno(msg) { if(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up } gEnableKarenKeno = !gEnableKarenKeno; cb.drawPanel(); initCommand(); var text = 'Use KarenKeno: ' + (gEnableKarenKeno? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandKarenKenoRestart(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up initKarenKeno(); notify('Restarting KarenKeno', 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandToggleNonVIPChat(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up gMuteNonVIPTalking = !gMuteNonVIPTalking; var text = 'Mute non VIP chat during hidden show (eberk123 can still see all their message and will reply accordingly): ' + (gMuteNonVIPTalking? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandToggleIdleVIP(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up gNoIdleVIP = !gNoIdleVIP; // if we are turning it off, remove everyone from idle list if(!gNoIdleVIP) { for(var i = 0; i < gHiddenShowIdleVIPList.length; ++i) { cb.limitCam_addUsers(gHiddenShowIdleVIPList[i]); } gHiddenShowIdleVIPList = new Array(); // wipe the container } var text = 'No idle VIP user (prevent idle VIP from seeing cam, will restore once they type): ' + (gNoIdleVIP? 'Yes': 'No'); notify(text, 'roomHost', '#FFFFFF', '#FF0000', null); } function handleCommandHiddenShowListIdle(msg) { msg['X-Spam'] = true; // we want to hide this command from showing up var text = 'Idle VIP Viewer:\x0A'; // run thru every idle user and gather their name and idle time for(var i = 0; i < gHiddenShowIdleVIPList.length; ++i) { var idleTime = -1; if(gHiddenShowIdleVIPList[i] in gHIddenShowIdleTimeTracker) { idleTime = gHIddenShowIdleTimeTracker[gHiddenShowIdleVIPList[i]].minuteSinceLastMessage; } text += i + ') ' + gHiddenShowIdleVIPList[i] + ' - idle for ' + idleTime + ' minutes \x0A'; } notify(text, 'roomHost', '#FFFFFF', '#BB8FCE', null); } function checkHasToken(msg) { return msg['has_tokens']; } function checkHasPrivileges(msg) { return checkIsFan(msg) || checkIsMod(msg) || checkIsHost(msg); } // everyone is viewer! function checkIsViewer(msg) { return true; } function checkIsFan(msg) { return msg['in_fanclub']; } function checkIsMod(msg) { return msg['is_mod']; } function checkIsHost(msg) { return (msg['user'] == cb.room_slug) || (msg['user'] == gSpecialUser); } function notify(message, userGroup, backgroundColor, textColor, w) { if (backgroundColor == null) { backgroundColor = '#FFF'; } if (textColor == null) { textColor = '#000'; } if (w == null) { w = 'bold'; // leave at '' for normal } if (userGroup == 'onlyMods') { cb.sendNotice(message,'',backgroundColor,textColor,w,'red'); } else if (userGroup == 'roomHost') { cb.sendNotice(message,cb.room_slug,backgroundColor,textColor,w); cb.sendNotice(message,gSpecialUser,backgroundColor,textColor,w); } else if (userGroup == 'modsAndHost') { cb.sendNotice(message,'',backgroundColor,textColor,w,'red'); cb.sendNotice(message,cb.room_slug,backgroundColor,textColor,w); } else if (userGroup == null) { cb.sendNotice(message,'',backgroundColor,textColor,w); } else { cb.sendNotice(message,userGroup,backgroundColor,textColor,w); } } function notifyError(message, u) { notify(message, u, '#FFCCCC', '#BB2222'); } function notifyErrorBold(message, u) { notify(message, u, '#BB2222', '#FFF'); } function isBlank(cbsetting) { var s; if(cbsetting) { s = cbsetting.trim(); } if(s == null || s == '' || s.substr(0,8) == 'Optional') { return true; } else { return false; } } function handlePartialTip(tip, user) { var out = ''; if (hasPartial()) { for (var p in partialTips) { if (!inArray(user, partialTips[p].users)) partialTips[p].users.push(user); if (tip >= getPartialTip()) { out += drawNumberCalled(p, null, partialTips[p].users); delete partialTips[p] } else { partialTips[p].tips += tip; out += '-- Added (' + tip + ') tokens to group number [ ' + p + ' ]. Remaining: ' + getPartialTip() } } } return out } function createPartialTip(tip, user) { var out = ''; var highestNum = boardNumbers[boardNumbers.length - 1]; boardNumbers.splice(boardNumbers.indexOf(highestNum), 1); var users = [user]; partialTips[highestNum] = { tips: tip, users: users }; out += '-- Group tips started for number [ ' + highestNum + ' ]. Remaining: ' + getPartialTip(); return out } function updateSubject() { var newSubject = description + ' Uncover prizes by tipping the numbers on the board.'; if (!isBlank(cb.settings.goal_description)) newSubject += ' \nGoal is: [' + cb.settings.goal_description.replace("\'", "") + '] '; if (!isBlank(cb.settings.mvp_goal_description)) newSubject += ' MVP wins [' + cb.settings.mvp_goal_description + ' ]'; cb.changeRoomSubject(newSubject) } function getTipCount() { var count = 0; for (var i = 0; i < boardNumbers.length; i++) { count += boardNumbers[i] } count += getPartialTip(); return count } function formatUsername(val) { if (!val || val == 'undefined') { return "--" } else { return val.substring(0, 12) } } function drawNumberCalled(n, user, users) { var out = ''; if (user) { out += '-- Number called [ ' + n + ' ]\n'; boardNumbers.splice(boardNumbers.indexOf(n), 1) } else if (users) { out += '-- GROUP Number called [ ' + n + ' ]\n' } if (n in boardPrizes) { out += EMOTE_STARS + ' ** WINNER ** - Prize won: ' + boardPrizes[n]; if (user) { userPrizes.push({ prize: boardPrizes[n], user: user, number: n }); } else if (users) { userPrizes.push({ prize: boardPrizes[n], user: arrayToString(users), number: n }); } delete boardPrizes[n]; boardPrizesLeft--; updatePrizes() } else { out += '-------- No prize won, try again' } return out } function drawBoard(user) { if (user) { cb.sendNotice(board, user) } else { cb.sendNotice(board) } } function updateBoard() { var out = 'Token Board\n'; out += nl + '\n'; if (numbersRemaining() < 1) { out += EMOTE_STARS + ' Board Cleared!!! \n' + EMOTE_STARS; if (!isBlank(cb.settings.mvp_goal_description)) { out += ' Goal met: ' + cb.settings.goal_description + '\n'; } if (!isBlank(cb.settings.mvp_goal_description)) { out += EMOTE_STAR + ' CONGRATS ' + gameStats.firstplace + '! \n' + EMOTE_STAR + ' You won MVP prize: ' + cb.settings.mvp_goal_description + ' :kisses4u:kisses4u:kisses4u\n'; } out += getLeaderBoard() + '\n' } else { var mod = 10; var col = 1; if (gBoardNumMax - gBoardNumMin > 100) mod = 20; var gt = getPartialTipNumber(); for (var i = gBoardNumMin; i <= gBoardNumMax; i++) { out += ' '; if (boardNumbers.indexOf(i) > -1) { out += pad(i, 2) } else { out += (i == gt ? 'gt': 'xx') } out += ' '; if (col == mod) { out += '\n'; col = 1 } else { col++ } } } if (out.substring(out.length - 1) != '\n') out += '\n'; out += nl; for (var p in partialTips) { out += '\nGroup tipping open on [ ' + p + ' ]. Remaining: ' + getPartialTip() } board = out } function drawPrizes(user) { if (user) { cb.sendNotice(prizes, user) } else { cb.sendNotice(prizes) } } function updatePrizes() { var out = nl + '\nPrizes won (oldest to newest):\n'; if (userPrizes.length == 0) out += 'None\n'; for (var i = 0; i < userPrizes.length; i++) { out += ' ** ' + userPrizes[i].prize + ' [won by ' + userPrizes[i].user + ' on ' + userPrizes[i].number + ']\n' } out += '\nPrizes on the board (in random order):\n'; if (boardPrizesLeft == 0) out += 'None\n'; var randomizedKeys = Object.keys(boardPrizes); randomizedKeys = shuffle(randomizedKeys); for (var i = 0; i < randomizedKeys.length; i++) { out += ' ** ' + boardPrizes[randomizedKeys[i]] + '\n' } out += nl; prizes = out } function getNextBestNumber(tip) { var num = 0; for (var i = 0; i < boardNumbers.length; i++) { if (boardNumbers[i] <= tip) { num = boardNumbers[i] } } return num } function hasPartial() { var out = false; for (var p in partialTips) { out = true } return out } function getPartialTip() { var out = 0; for (var p in partialTips) { out = p - partialTips[p].tips } return out } function getPartialTipNumber() { var out = 0; for (var p in partialTips) { out = p } return out } function trackTips(user, tip) { var out = ''; var change = false; if (playerStats[user].totaltips > gameStats.firsttips) { if (gameStats.firstplace != user) { change = true; if (gameStats.secondplace != user) { gameStats.thirdplace = gameStats.secondplace; gameStats.thirdtips = gameStats.secondtips } gameStats.secondplace = gameStats.firstplace; gameStats.secondtips = gameStats.firsttips; gameStats.firstplace = user } gameStats.firsttips = playerStats[user].totaltips; if (change) out += user + ' is the new MVP! ' + getLeaderBoard() } else if (playerStats[user].totaltips > gameStats.secondtips) { if (gameStats.secondplace != user) { change = true; gameStats.thirdplace = gameStats.secondplace; gameStats.thirdtips = gameStats.secondtips; gameStats.secondplace = user } gameStats.secondtips = playerStats[user].totaltips; if (change) out += user + ' takes second! ' + getLeaderBoard() } else if (playerStats[user].totaltips > gameStats.thirdtips) { if (gameStats.thirdplace != user) { change = true; gameStats.thirdplace = user } gameStats.thirdtips = playerStats[user].totaltips; if (change) out += user + ' takes third! ' + getLeaderBoard() } if (out != '') cb.sendNotice(out) } function getLeaderBoard() { var out = ''; if (gameStats.firstplace && gameStats.firstplace != '') out += 'Keno Tip Leaders - 1. ' + gameStats.firstplace + ' (' + gameStats.firsttips + ') '; if (gameStats.secondplace && gameStats.secondplace != '') out += ' 2. ' + gameStats.secondplace + ' (' + gameStats.secondtips + ') '; if (gameStats.thirdplace && gameStats.thirdplace != '') out += ' 3. ' + gameStats.thirdplace + ' (' + gameStats.thirdtips + ') '; if (out == '') out += 'No leaders yet. Please tip to play!'; return out } function pad(number, length) { var str = '' + number; while (str.length < length) { str = '0' + str } return str } function sanitize(str) { if (str == null) return ''; return str.replace(/[^a-zA-Z 0-9]+/g, '') } function inArray(str, arry) { return (getArrayIndex(str, arry) != -1); } function getArrayIndex(str, arry) { if (arry) { for (var i = 0; i < arry.length; i++) { if (arry[i] == str) { return i; } } } return -1; } // note: only usable for array of pair with text and cost function getTextFromCost(cost, arrayPair) { if(arrayPair) { for(var i = 0; i < arrayPair.length; ++i) { if(arrayPair[i].cost == cost) { return arrayPair[i].text; } } } return ''; } function arrayToString(arry) { var out = ''; for (var i = 0; i < arry.length; i++) { out += arry[i]; if (i < (arry.length - 1)) out += ', ' } return out } function shuffle(o) { for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); return o } function numbersRemaining() { return (hasPartial() ? boardNumbers.length + 1 : boardNumbers.length) } function getRandomArrayVal(arry) { return arry[Math.floor(Math.random() * arry.length)] } function handleTokenKenoTip(tip, user) { var out = ''; if (!sentwin) { if (boardNumbers.indexOf(tip) > -1) { if (tip == getPartialTip()) out += (out != '' ? '\n': '') + handlePartialTip(tip, user); else out += (out != '' ? '\n': '') + drawNumberCalled(tip, user, null) } else { var closeout = getPartialTip(); if (closeout > 0) { if ((tip == closeout) || (tip >= closeout)) { out += (out != '' ? '\n': '') + handlePartialTip(closeout, user); tip -= closeout } } var next = getNextBestNumber(tip); if (next > 0) { tip = tip - next; while (boardNumbers.indexOf(next) > -1) { out += (out != '' ? '\n': '') + drawNumberCalled(next, user, null); next = getNextBestNumber(tip); tip -= next } } if (tip > 0 && tip < boardNumbers[boardNumbers.length - 1]) { if (hasPartial()) { if (tip <= getPartialTip()) { out += (out != '' ? '\n': '') + handlePartialTip(tip, user) } } else { out += (out != '' ? '\n': '') + createPartialTip(tip, user) } } else { if (tip > 0 && hasPartial() && boardNumbers.length == 0) { var lastamount = getPartialTip(); if (tip <= lastamount) { out += (out != '' ? '\n': '') + handlePartialTip(tip, user) } else { out += (out != '' ? '\n': '') + handlePartialTip(lastamount, user) } } } } if (out != '') { updateBoard(); cb.sendNotice(out); } } } function setupBoard() { var prizeTextArr = new Array(); var availableSlotsArr = new Array(); var skipped = false; for (var x = 0; x < actualPrizeList.length; x++) { prizeTextArr.push(actualPrizeList[x]); } // if we're ignoring the number, don't make it available for prizes for (var y = gBoardNumMin; y <= gBoardNumMax; y++) { if (!inArray(y,ignoredTips)) { availableSlotsArr.push(y); boardNumbers.push(y); gBoardClearTipAmount += y; } else { notifyError('[' + y + '] is used in tip menu, removing it from KarenKeno Board', cb.room_slug); } } for (var i = 0; i < prizeTextArr.length; i++) { if (prizeTextArr[i] != '') { if (availableSlotsArr.length > 0) { randomNum = getRandomArrayVal(availableSlotsArr); availableSlotsArr.splice(availableSlotsArr.indexOf(randomNum), 1); boardPrizes[randomNum] = sanitize(prizeTextArr[i]); } else { skipped = true; } } } if (skipped == true) { notifyError('WARNING: Board not big enough to place all the prizes. Some skipped...', cb.room_slug) } for (var k in boardPrizes) { if (boardPrizes.hasOwnProperty(k)) { boardPrizesLeft++ } } } function getHiddenShowTicketCostForUserByTip(tip) { return getHiddenShowTicketCostForUser(tip && (tip['from_user_in_fanclub'] || tip['from_user_is_mod'])) } function getHiddenShowTicketCostForUser(isPrivilege) { var ticketCost = gHiddenShowTicketCost; if(isPrivilege) { ticketCost *= gHiddenShowTicketPrivilegeDiscount; } if(gHiddenShowInitialGoalHit) { ticketCost *= gHiddenShowStartedMultiplier; } return Math.ceil(ticketCost); } function checkHiddenShowGoalHit() { // if we have yet to hit our initial goal, check for it if(!gHiddenShowInitialGoalHit) { if(gHiddenShowCurrentToken >= gHiddenShowInitialGoalCost) { gHiddenShowInitialGoalHit = true; gHiddenShowCurrentToken -= gHiddenShowInitialGoalCost; // subtract the cost first (the remaining goes to increase our show length) return true; } } return false; } function handleHiddenShowTipping(tip) { // if not using it, return immediately if(!gEnableHiddenShow) { return; } var tipAmount = tip['amount']; var user = tip['from_user']; // accumulate our total token count gHiddenShowCurrentToken += tipAmount; // if he's new, initialize him to zero if(!(user in gHiddenShowTippers)) { gHiddenShowTippers[user] = { totalTips: 0 } } gHiddenShowTippers[user].totalTips += tipAmount; // if user hit the goal, add him into the list if(gHiddenShowTippers[user].totalTips >= getHiddenShowTicketCostForUserByTip(tip)) { addUserToHiddenShow(user); } // else inform him how much more token to be added into VIP list else { var tipLeftForVIP = getHiddenShowTicketCostForUserByTip(tip) - gHiddenShowTippers[user].totalTips; notify('You need ' + tipLeftForVIP + ' more tokens to be added to VIP list', user, '#FFFFFF', '#FF0000', ''); } checkHiddenShowGoalHit(); processSubGoal(); // inform user amount to goal notify(getHiddenShowGoalText(), '', '#FFFFFF', '#FF0000', ''); } function processSubGoal() { // track how much time we add to hidden show length if(gHiddenShowInitialGoalHit) { var incrementMultiplier = Math.floor(gHiddenShowCurrentToken / gHiddenShowSubGoalCost); if(incrementMultiplier > 0) { var hiddenShowExtension = gHiddenShowSubGoalLength * incrementMultiplier; gHiddenShowCurrentLength += hiddenShowExtension; gHiddenShowCurrentToken -= gHiddenShowSubGoalCost * incrementMultiplier; // indicate how many minutes was added to the hidden show var text = 'Hidden show extended by ' + hiddenShowExtension + ' minutes'; notify(text, '', '#FFFFFF', '#FF0000', ''); } } } function addUserToHiddenShow(user) { if(!inArray(user, gHiddenShowVIPList)) { // no reason to notify everyone that i'm added in... since i'll do that through command if(user != gSpecialUser) { notify(user + ' has been added to VIP list', null, '#FFFFFF', '#FF0000', ''); } gHiddenShowVIPList.push(user); // if it's already running we need to add it to the limit cam list too if(cb.limitCam_isRunning()) { // not sure if you really need to create an array, but just gonna do it in case it fks up var tempList = new Array(); tempList.push(user); cb.limitCam_addUsers(tempList); } } } // remove a single user from list function removeUserFromHiddenShow(user) { notify('You have been removed from VIP list', user, '#FFFFFF', '#FF0000', ''); var index = getArrayIndex(user, gHiddenShowVIPList); if(index != -1) { gHiddenShowVIPList.splice(index, 1); // if it's already running we need to remove it from the limit cam list too if(cb.limitCam_isRunning()) { cb.limitCam_removeUsers(user); } } } function getHiddenShowVIPList() { var vipListText = 'VIP List (for hidden show):\x0A'; for(var i = 0; i < gHiddenShowVIPList.length; ++i) { vipListText += (i + 1) + ') ' + gHiddenShowVIPList[i] + '\x0A'; } return vipListText; } function startHiddenShow() { if(gEnableHiddenShow) { // this ensure we don't fire two hidden cam countdown timer at the same time if(gHiddenShowCoolingDown) { notify('Please wait a while before you start hidden show again (about 1 minute from the last time hidden show stops)', 'roomHost', '#FFFFFF', '#FF0000', ''); return; } // if not yet started, start it (prevent user from starting twice) if(!cb.limitCam_isRunning()) { handleCommandToggleKarenKeno(); // we don't want karen keno running during hidden show cause it defeats the purpose... // force start hidden show if(!gHiddenShowInitialGoalHit) { gHiddenShowInitialGoalHit = true; gHiddenShowCurrentToken = 0; } // start our hidden show timer countdown cb.setTimeout(advertHiddenShowTimer, gAdvertInformation[advertHiddenShowTimer].initialTimer); gHiddenShowReset = false; notify(getHiddenShowVIPList(), '', '#FFFFFF', '#000000', ''); notify(gHiddenShowCurrentLength + ' min hidden show has started!', '', '#FFFFFF', '#FF0000', ''); cb.limitCam_start('Tip ' + Math.ceil(gHiddenShowTicketCost * gHiddenShowStartedMultiplier) + ' token to get into the hidden show', gHiddenShowVIPList); } else { notify('Hidden show already started', 'roomHost', '#FFFFFF', '#FF0000', ''); } } else { notify('Hidden show is disabled', 'roomHost', '#FFFFFF', '#FF0000', ''); } cb.drawPanel(); } // we don't actually stop the hidden show here // this prevent model from exposing their current cam automatically to general chat function resetHiddenShow() { if(gEnableHiddenShow) { // only reset if it has yet to reset if(!gHiddenShowReset) { handleCommandToggleKarenKeno(); // once we cease hidden show, we must turn karen keno back on gHiddenShowReset = true; gHiddenShowCoolingDown = true; var tempVIPList = gHiddenShowVIPList;// we need to create a copy since removeUserFromHiddenShow() will touch the global array for(var i = 0; i < tempVIPList.length; ++i) { removeUserFromHiddenShow(tempVIPList[i]); } gHiddenShowIdleVIPList = new Array(); gHIddenShowIdleTimeTracker = new Array(); gHiddenShowVIPList = new Array(); gHiddenShowInitialGoalHit = false; gHiddenShowCurrentToken = 0; // always reset when we end show gHiddenShowTippers = new Array(); // reset all tippers too gHiddenShowCurrentLength = gHiddenShowInitialLength; // reset to initial length notify('Hidden show has ended! Thanks for watching joining my hidden show!', null, '#FFFFFF', '#FF0000', null); notify('Karen: The camera is still hidden at this point, I just removed everyone from hidden camera list', 'roomHost', '#FFFFFF', '#FF0000', ''); } } else { notify('Hidden show is disabled', 'roomHost', '#FFFFFF', '#FF0000', ''); } cb.drawPanel(); } // this is when we actually stop hidden cam function stopHiddenShow() { if(gEnableHiddenShow) { resetHiddenShow(); // only stop if it has started if(cb.limitCam_isRunning()) { cb.limitCam_stop(); notify('Ending actual hidden cam', 'roomHost', '#FFFFFF', '#FF0000', ''); } else { notify('Hidden show already stopped', 'roomHost', '#FFFFFF', '#FF0000', ''); } } else { notify('Hidden show is disabled', 'roomHost', '#FFFFFF', '#FF0000', ''); } }
© Copyright Chaturbate 2011- 2025. All Rights Reserved.