Apps Home
|
Create an App
Devoted Test
Author:
chadmck
Description
Source Code
Launch App
Current Users
Created by:
Chadmck
//local vars var XpMod = 1; var tick = 60000; var tips_this_session = 0; var hinttask = 0; //Colors var Colors = { "blood": "#b30000", "red": "#ff0000", "lilac": "#c299ff", "darkblue": "#0000b3", "orange": "#ff471a", "gold": "#ffd11a", "silver": "#f2f2f2", "grass": "#2eb82e", "rose": "#ff66cc", "pussy": "#ff99cc", "dirt": "#663300", "royal": "#6600cc", "absence": "#1a1a00", "canary": "#ffff66", "black": "#000000", "white": "#ffffff", "pumpkin": "#ff9933", "bark": "#67594c", "seafoam": "#ccffcc", "grey": "#bfbfbf", "softyellow": "#ffffe5", "babybreath": "#e5ffff", "cyan": "#1affff", "teal": "#66ffff", "velvet": "#b784a7", "wine": "#ffb3b3", "toupe": "#b38b6d", "lime": "#8cff1a", "drearysky": "#94bfac", "olive": "#4b5729", "lupin": "#f7d0cf", "scarlet": "#FD0E35", "tangerine": "#FF9966" } //Genders var Genders = { "male" : { "longDesc":"male", "cbVal":"m", "pronoun": "he" , "posessive": "his" }, "female": {"longDesc":"female","cbVal":"f", "pronoun":"she" , "posessive":"her" }, "trans": {"longDesc":"transsexual","cbVal":"s","pronoun":"(s)he", "posessive":"his/her"}, "couple": {"longDesc":"a couple","cbVal": "c", "pronoun":"they" , "posessive":"their" }, "invalid": { "longDesc": "unknown", "cbVal": "", "pronoun": "he", "posessive": "his" } } Genders.getByCbVal = function (val) { for (var key in this) { if (this[key].cbVal == val) { return this[key]; } } return Genders.invalid; } //Levels var Levels = [0,100,250,500,1000,2000,5000,10000,25000,50000,100000,250000,500000,750000,1000000,1500000,2500000]; //Ranks var Ranks = { "Follower": {"name": "Follower","powers": 0, "title" : "", "color": "", "levelReq": 0 }, "Initiate": { "name": "Initiate","powers": 0, "title": "", "color": Colors.absence, "levelReq": 1 }, "Novice": { "name":"Novice","powers": 1, "title": "", "color": Colors.grass, "levelReq": 3 }, "Devoted": { "name":"Devoted","powers": 2, "title": "", "color": Colors.darkblue, "levelReq": 5 }, "Watcher": { "name" : "Watcher","powers": 3, "title": "Watcher", "color": Colors.silver, "levelReq": 8 }, "Obsessed": { "name" : "Obsessed","powers": 4, "title": "Obsessed", "color": Colors.gold, "levelReq": 10 }, "Favored": { "name" : "Favored","powers": 5, "title": "Favored", "color": Colors.pussy, "levelReq": 13 }, "Master": { "name":"Master","powers": 7, "title": "Eminence", "color": Colors.royal, "levelReq": 16 } } //Powers var Powers = { "Speak": { "name": "Speak with the Goddess", "cmd": "/speak", "helpText": "/speak","usesPerDay": 0, "usesLeft": 0, "rankReq": 1 }, "MinorBless": { "name": "Request Minor Blessing", "cmd": "/minor", "helpText": "/minor", "usesPerDay": 0, "usesLeft": 0, "rankReq": 3 }, "Proclamation": { "name": "Make a Proclomation (Change Subject)", "cmd": "/proclaim", "helpText": "/proclaim *Subject*", "usesPerDay": 0, "usesLeft": 0, "rankReq": 5 }, "MajorBless": { "name": "Request Major Blessing", "cmd": "/major", "helpText": "/major", "usesLeft": 0, "usesPerDay": 0, "rankReq": 8 }, "Declaration": { "name": "Declare a Rule (Within Reason)", "cmd": "/declare", "helpText": "/declare *rule request*","usesPerDay":0,"usesLeft":0,"rankReq": 10} } //Data for save/restore -also handles logic for user experience etc var Data = function (json) { var o = new Object(); if (json) { o.high_tip_username = json.high_tip_username; o.high_tip_amount = json.high_tip_amount; o.last_tip_username = json.last_tip_username; o.last_tip_amount = json.last_tip_amount; o.users = json.users || new Array(); o.events = json.events || new Array(); } else { o.high_tip_username = null; o.high_tip_amount = 0; o.last_tip_username = null; o.last_tip_amount = 0; o.users = new Array(); o.events = new Array(); } o.loadEvents = function () { //this is necessary after a load to not copy the datastore var t = this; var eventArray = t.events; t.events = new Array(); for (var i = 0; i < eventArray.length; i++) { var obj = eventArray[i]; var event = o.newEvent(obj.title, obj.description, obj.goal, obj.reward, obj.battle); event.highest_tip = obj.highest_tip; event.highest_tipper = obj.highest_tipper; event.tip_total = obj.tip_total; t.events.push(event); } } o.load = function(json) { //load this object fromt he Data choice from cb if (json) { var data = new Data(JSON.parse(json)); return data; } else { return Data(); } } o.save = function(){ //display the Data object as a notice for copy/paste //lets go through the users, if they have 0 experience, we don't need to save them var toDelete = new Array(); for (var i = 0; i < this.users.length; i++) { var obj = this.users[i]; if (obj.experience < 1) { toDelete.push(obj); } } for (var i = 0; i < toDelete.length; i++) { var obj = toDelete[i]; this.users = cbjs.arrayRemove(this.users, obj); } selfNotice(JSON.stringify(this)); } //events o.newEvent = function (title, description, goal, reward, battle,toAnnounce) { var t = this; var e = new Object(); e.title = title; //event title e.description = description; //long description of the goal or quest e.goal = goal; //token requirement to 'win' the eventmyEvent e.reward = reward; //text string reward displayed to the users e.battle = battle; //boolean to treat this as a battle type event or not . Battle events will include damage strings etc on tips. e.highest_tipper = ""; //username of current highest tipper for the event e.highest_tip = 0; //highest tip for current event e.tip_total = 0; //progress toward var checkEvent = t.getEventFromList(title); if (checkEvent) { selfNotice("There is already an Event by the title " + title + ". Please create the event again with a different name , or end the previous event"); return false; } //event functions e.tipTowardGoal = function(username,amount) { e.tip_total += amount; if (amount > e.highest_tip ) { e.highest_tip = amount; e.highest_tipper = username; } if (e.tip_total >= e.goal) { e.goalMet(); } else { var msg = username + " has offered " + amount + " toward " + e.title + ". The Goddess Thanks You!\n" + "Currently at " + e.tip_total + "/" + e.goal + "!"; cb.sendNotice(msg, "", "", Colors.red); } } e.goalMet = function () { var msg = e.title + " has been completed! The Goddess is pleased!\nHere comes the Reward! " + e.reward; cb.sendNotice(msg, "", "", Colors.red); selfNotice("You owe the room a " + e.reward); e.Remove(); } e.Announce = function (user) { if (!user) user = ""; var msg = "Show your devotion and " + e.title + "!\n" + "" + e.description + "\n" + "We have currently collected " + e.tip_total + " of the " + e.goal + " required in offerings.\n" + "A " + e.reward + " awaits our Success!"; cb.sendNotice(msg, user, "", Colors.velvet); } e.Remove = function () { t.events = cbjs.arrayRemove(t.events, e); selfNotice("Event " + e.title + " has been removed"); e = null; } if (toAnnounce == true) { //add the event t.events.push(e); e.Announce(); } else { //strange way to do this, but this means its a loaded event and we need to return it. return e; } } //get event from events array in Data o.getEventFromList = function(title) { var t = this; for (var i = 0; i < t.events.length; i++) { var obj = t.events[i]; if (obj.title == title ) { return obj; } } //if we get here there is no event, return false; return false; } //get user object from the users array in Data o.getUserFromList = function (username) { var t = this; for (var i = 0; i < t.users.length; i++) { var obj = t.users[i]; if (obj.username == username) { return obj; } } //if we get here there is no user, we must create it var retobj = new Object(); retobj.username = username; retobj.experience = 0; return retobj; } //get users level o.getUserLevel = function (user) { return (user.experience > Levels[1]) ? (user.experience > Levels[2]) ? (user.experience > Levels[3]) ? (user.experience > Levels[4]) ? (user.experience > Levels[5]) ? (user.experience > Levels[6]) ? (user.experience > Levels[7]) ? (user.experience > Levels[8]) ? (user.experience > Levels[9]) ? (user.experience > Levels[10]) ? (user.experience > Levels[11]) ? (user.experience > Levels[12]) ? (user.experience > Levels[13]) ? (user.experience > Levels[14]) ? (user.experience > Levels[15]) ? (user.experience > Levels[16]) ? 16:15:14:13:12:11:10:9:8:7:6:5:4:3:2 : 1 : 0; } //Rank by ReqNum o.getRankByLevelReq = function(num) { var CurRank = Ranks.Follower; for (var key in Ranks) { if (Ranks[key].levelReq == num) { CurRank = Ranks[key]; } } return CurRank; } //Rank by LEvel o.getUserRank = function (lvl) { var CurRank = Ranks.Follower; for (var key in Ranks) { if (lvl >= Ranks[key].levelReq && Ranks[key].levelReq > CurRank.levelReq) { CurRank = Ranks[key]; } } return CurRank; } //Security Level o.getUserSecurity = function(username) { if (username == cb.room_slug) return 3; var lvl = this.getUserLevel(this.getUserFromList(username)); if (!lvl) return 0; return (lvl > 13 ) ? 2 : (lvl > 0) ? 1 :0; } o.experienceGain = function (userObj, amount) { var t = this; var oldlvl = t.getUserLevel(userObj); t.users = cbjs.arrayRemove(t.users, userObj); userObj = { "username": userObj.username, "experience": parseInt(userObj.experience) + amount }; var newlvl = t.getUserLevel(userObj); t.users.push(userObj); //if the level has changed if (newlvl > oldlvl) { t.levelUp(userObj, oldlvl, newlvl); } return userObj; } o.levelUp = function( user, oldlvl, newlvl) { //alert the room that the user has levelled up! var msg = "Congratulations are in order! " + user.username + "'s devotion has brought them closer to the Goddess! They are now a level " + newlvl + " " + this.getUserRank(newlvl).name + " of the Devoted!"; cb.sendNotice(msg, "", "", Colors.tangerine); } o.giftExperience = function (username, amount,notifyRoom) { var userObj = this.getUserFromList(username); o.experienceGain(userObj, amount); //notifications cb.sendNotice(amount + " experience has just been bestowed upon you by the Goddess! Rejoice!", username, "", Colors.lilac); if (notifyRoom) cb.sendNotice(amount + " experience has just been bestowed upon " + username + " by the Goddess! Rejoice!", "", "", Colors.lilac); cb.drawPanel(); } //update users experience from a tip o.ManageUserList = function (tip) { var t = this; var userObj = this.getUserFromList(tip['from_user']); var togain = tip['amount'] * XpMod; userObj = o.experienceGain(userObj, togain); var lvl = this.getUserLevel(userObj); var myMessage = userObj.username + " has just placed an offering of " + tip['amount'] + " and gained " + togain + " experience "; if (XpMod > 1) { //xp bonus is on myMessage = myMessage + "( " + XpMod + "x Bonus Exp)"; } myMessage = myMessage + "! " + Genders.getByCbVal(tip['from_user_gender']).pronoun + " is a Level " + lvl + " " + this.getUserRank(lvl).name + " member of the Devoted!"; cb.sendNotice(myMessage, "", "", Colors.pussy); return userObj; } return o; } //cb settings/choices cb.settings_choices = [ { name: 'Data', type: 'str', minLength: 1, maxLength: 800000, required: false } ]; // handlers //onMessage cb.onMessage(function (msg) { //Parse commands if they exist if (ParseCommand(msg)) { msg['X-Spam'] = true; } else { //not a command , additional processing here. } return msg; }); //onEnter cb.onEnter(function (user) { greeting(user['user']); }); //onTip cb.onTip(function (tip) { var t = this; t.dataStore.total_tipped += tip['amount'] ; t.tips_this_session += tip['amount']; var tipUser = this.dataStore.ManageUserList(tip); //events for (var i = 0; i < t.dataStore.events.length; i++) { var e = t.dataStore.events[i]; e.tipTowardGoal(tipUser.username,tip['amount']); } this.dataStore.last_tip_amount = tip['amount']; this.dataStore.last_tip_username = tip['from_user']; if (tip['amount'] > this.dataStore.high_tip_amount) { this.dataStore.high_tip_amount = tip['amount']; this.dataStore.high_tip_username = tip['from_user']; announceNewConsort(); } cb.drawPanel(); }); //onDrayPanel cb.onDrawPanel(function (user) { if (user['user'] == cb.room_slug) { return { 'template': '3_rows_of_labels', 'row1_label': 'Tips this session :', 'row1_value': '' + this.tips_this_session , 'row2_label': 'Consort:', 'row2_value': this.dataStore.high_tip_username + ' (' + this.dataStore.high_tip_amount + ')', 'row3_label': 'Latest Tip Received:', 'row3_value': this.dataStore.last_tip_username + ' (' + this.dataStore.last_tip_amount + ')' }; } else { var realUser = this.dataStore.getUserFromList(user['user']); var lvl = this.dataStore.getUserLevel(realUser); var rank = this.dataStore.getUserRank(lvl); return { 'template': '3_rows_12_22_31', 'row1_label': 'Devoted Level :', 'row1_value': lvl, 'row2_label': 'Devoted Rank :', 'row2_value': rank.name, 'row3_value': realUser.experience + "/" + Levels[lvl + 1] + " experience to next level." }; } }); // helper functions function ParseCommand(m) { var args = m['m'].split(" "); var cmd = args[0]; if (cmd) { var sec = this.dataStore.getUserSecurity(m['user']); var cmduser = this.dataStore.getUserFromList(m['user']); var lvl = this.dataStore.getUserLevel(cmduser); switch (cmd) { case '/save': if (sec < 3) return false; this.dataStore.save(); return true; break; case '/greeting': if (sec == 3) greeting(); return true; break; case '/bestow': //bestow experience on a user - args[1] is user, args[2] is , args[3] is a boolean for sharing with the room if (sec < 3) return false; var user = this.dataStore.getUserFromList(args[1]); var amount = args[2]; this.dataStore.giftExperience(user.username, parseInt(amount),args[2]); selfNotice("You have gifted " + amount + " experience to " + user.username); return true; break; case '/bonusxp': //initiate or turn off bonus xp ...format is /bonusxp *multiplier* ..a multiplier of off will unset the bonus if (sec < 3) return false; setBonus(args[1]); return true; break; case '/newevent': //start a new event ... args[1] is title, args[2] is description, args[3] is goal, args[4] is reward, args[5] is battle. if (sec < 3) return false; this.dataStore.newEvent(args[1].replace(/_/g, " "), args[2].replace(/_/g, " "), args[3], args[4].replace(/_/g, " "), args[5], true); return true; break; case '/colors': if (sec < 3) return false; showColors(); return true; break; case '/killevent': if (sec < 3) return false; var e = this.dataStore.getEventFromList(args[1]); e.Remove(); return true; break; case '/status': announceAllEvents(m['user']); return true; break; case '/help': //return help on available commands, available to anyone showCommands(cmduser); return true; break; case '/tick': //changes eventsummary interval - starts at 30 seconds. if there is no args[1] we set back to 30 secs, otherwise to args[1] if (sec < 3) return false; if (args[1]) { tick = args[1] * 1000; } else { tick = 30000; } return true; break; case '/roster': if (sec < 3) return false; //show a roster of devoted - no args[1] goes to everyone, args[1] is a private notice roster(args[1]); return true; break; case '/info': info(m['user']); return true; break; case '/speak': if (lvl < Powers.Speak.rankReq) return true; speak(cmduser); return true; break; case '/minor': if (lvl < Powers.MinorBless.rankReq) return true; minor(cmduser); return true; break; case '/proclaim': if (lvl < Powers.Proclamation.rankReq) return true; //crush the args back together var subject = "" for (i = 1; i < args.length ; i++) { subject += " "; subject += args[i]; } proclaim(cmduser,subject); return true; break; case '/major': if (lvl < Powers.MajorBless.rankReq) return true; major(cmduser); return true; break; case '/declare': if (lvl < Powers.Declaration.rankReq) return true; //crush the args back together var subject = "" for (i = 1; i < args.length ; i++) { subject += " "; subject += args[i]; } declare(cmduser,subject); return true; break; default: return false; break; } } else { return false; } } function eventSummary() { if (this.dataStore.events.length = 0) return; var msg = "Currently Running Events: "; //print a summary line of running events to the room. for (var i = 0; i < this.dataStore.events.length; i++) { var obj = this.dataStore.events[i]; msg = msg + obj.title + " " + obj.tip_total + "/" + obj.goal + " Reward - " + obj.reward + " | "; } cb.sendNotice(msg, "", "", Colors.wine); cb.setTimeout(eventSummary,tick); } function announceAllEvents(username) { //notify the user of all events running. for (var i = 0; i < this.dataStore.events.length; i++) { var obj = this.dataStore.events[i]; obj.Announce(username); } } //Powers - more on this later , for now alot of its on the goddess. function speak(user) { msg = user.username + " has used their speak to the goddess power. You should PM them."; selfNotice(msg); } function minor(user) { msg = user.username + " has used their minor blessing power...give them something already!"; selfNotice(msg); } function major(user) { msg = user.username + " has used thier major blessing power ...give them something good!"; selfNotice(msg); } function proclaim(user, subject) { //change the subject in the room to what the user has asked for .....this may be scary. cb.changeRoomSubject(subject); } function declare(user, subject) { //this is not implemented yet cb.sendNotice("This feature is not ready, message the Goddess your rule :) ", user.username); } function showCommands(user) { var lvl = this.dataStore.getUserLevel(user); var sec = this.dataStore.getUserSecurity(user.username); var msg = "The following is the list of commands that you have available at your rank and level, along with a brief description." + "Some of these commands have more detailed help using /help *command*\n"; //commands available to everyone msg = msg + "/help - this command, provides help on the app.\n"; msg = msg + "/status - return information on any running events and their totals.\n"; msg = msg + "/info - get general information regarding the Devoted guild system, the room in general , and basic help.\n"; //commands only available to the goddess if (sec == 3) { msg = msg + "/tick - change the interval between event updates to the room (Default is 30 seconds) - format: /tick *seconds*\n"; msg = msg + "/save - provides the save data string as a notice to you alone. This string should be copied to a file and saved for use next launch\n"; msg = msg + "/greeting - displays the basic room greeting to the room.\n"; msg = msg + "/bestow - grant experience to a user, format: /bestow *user* *amount* *announce* , where announce is a true or false.\n"; msg = msg + "/newevent - begin a new event - titles must be unique - format: /newevent *title* *description* *goal* *reward* *battle* - where battle is a true or false. To imitate spaces in title and description , use underscores.\n"; msg = msg + "/killevent - stops an event prematurely - format: /killevent *title*.\n"; msg = msg + "/colors - displays a string in each available color also showing its name.\n"; msg = msg + "/roster - display a roster of the devoted. Format: /roster *user* - if user is ommitted it goes to the room.\n" } //level specific commands if (lvl >= Powers.Speak.rankReq) msg += "/speak - call upon your speak to the goddess power.\n"; if (lvl >= Powers.MinorBless.rankReq) msg += "/minor - request a minor blessing from the goddess.\n"; if (lvl >= Powers.MajorBless.rankReq) msg += "/major - request a major blessing from the goddess.\n"; if (lvl >= Powers.Proclamation.rankReq) msg += "/proclaim *subject* - make a proclamation (change room subject).\n"; if (lvl >= Powers.Declaration.rankReq) msg += "/declare *rule* - make the goddess obey your whim for one hour.\n"; cb.sendNotice(msg, user.username, "", Colors.blood); } //handle bonusxp function setBonus(bonus) { if (bonus == "off") { this.XpMod = 1; cb.sendNotice("Bonus Experience is over, orders of the Goddess","","",Colors.drearysky); } else { this.XpMod = parseInt(bonus); cb.sendNotice("An Experience Bonus of " + bonus + "x has been initiated. All Praise the Goddess!!"); } } //send notice to self function selfNotice(c) { cb.sendNotice(c,cb.room_slug,Colors.black,Colors.white); } function showColors() { for (var color in Colors) { cb.sendNotice("This line has been colored using " + color, cb.room_slug, "", Colors[color]); } } function format_username(val) { if (val === null) { return "--"; } else { return val.substring(0, 12); } } function announceNewConsort() { //there is a new consort, let everyone know var msg = "A new Consort has been chosen by the Goddess! Congratulations " + this.dataStore.high_tip_username + ", you have claimed " + "this title with an offering of " + this.dataStore.high_tip_amount + "tokens. The Goddess smiles upon you , pondering what to do with her new toy."; cb.sendNotice(msg, "", "", Colors.rose); } function announceConsort() { //let people know the current Consort is , and how much they need to offer to upend them. if (this.dataStore.high_tip_username == null) return false; var msg = "The Goddess' current Consort is " + this.dataStore.high_tip_username + " with an offering of " + this.dataStore.high_tip_amount + "tokens. Become the Godess' new favorite plaything by showering her with a greater offering!"; cb.sendNotice(msg, "", "", Colors.rose); cb.setTimeout(announceConsort, tick); } function info(username) { //generates an informational message explaining how this app works, and how to get started. var msg = "This room operates a guild management app for the Devoted guild. Show love for your Goddess of Pleasure, HaileyRains " + "by joining the Devoted! As a member of the devoted, your tips will earn you experience, and that experience will earn you levels." + "Raising levels will raise your rank, and with increased rank comes increased power within the guild. Each rank has powers that are available " + "to it , as well as additional bonuses as the Goddess wishes. This will be an ever evolving system, and you are free to supply Hailey with suggestions " + "for additions you would like to see. This info command will be updated frequently with new information. Relax, have fun, and be one of the Devoted! \n"; msg += "Level and Rank Structure\n"; msg+= "============================\n"; var lastRank = ""; for (i = 0; i < Levels.length; i++) { var rank = this.dataStore.getUserRank(i); var ranktxt = ""; if (rank.name != lastRank) { lastRank = rank.name; ranktxt = "Rank: " + rank.name; } msg += "Level " + i + " Experience: " + Levels[i] + " " + ranktxt + "\n"; } msg += "\n"; msg += "Powers:\n"; msg += "=========================\n"; for (var key in Powers) { var p = Powers[key]; var r = this.dataStore.getRankByLevelReq(p.rankReq); msg += p.name + ": " + p.helpText + " | Rank Required: " + r.name + "\n"; } msg += "\n"; msg += "Goddess' Consort: this is a fancy , Goddess pleasing term for the room's highest tipper. This favored toy of the Godess will receive a random blessing when entering the temple.\n"; cb.sendNotice(msg, username, "", Colors.drearysky); } function roster(username) { //generates a roster of the devoted and sends a notice to username, or the entire room if no username provided. var msg = " Roster of the Devoted \n"; msg += "===========================\n"; var sorted = this.dataStore.users.sort(mycomparator); for (var i = 0; i < sorted.length; i++) { var obj = sorted[i]; var lvl = this.dataStore.getUserLevel(obj); var rank = this.dataStore.getUserRank(lvl); var consort = ""; if (obj.username == this.dataStore.high_tip_username) { consort = "*CONSORT*"; } msg += obj.username + " | Level " + lvl + " " + rank.name + " " + consort + "\n"; } cb.sendNotice(msg, username, "", Colors.pumpkin); } // a and b are object elements of your array function mycomparator(a,b) { return parseInt(a.experience) - parseInt(b.experience); } function greeting(user) { var msg = "Welcome to the domain of your Goddess " + cb.room_slug + ". This room is powered by -=Devoted=- App by ChadMck.\n" + "Give offerings to the goddess , and become a member of the Devoted! In order to participate, you must be a loyal follower, so " + "go ahead and take care of that now. Your Goddess Awaits....."; if (user) { var realuser = this.dataStore.getUserFromList(user); var lvl = this.dataStore.getUserLevel(realuser); if ( lvl > 0) { msg = "Welcome back " + realuser.username + "! You are currently a level " + lvl + " " + this.dataStore.getUserRank(lvl).name + " of the devoted!\n" + "Continue to please your Goddess, and the rewards shall be great indeed.\n"; } if (realuser.username == this.dataStore.high_tip_username) { cb.sendNotice( "The Goddess' Consort has arrived! All praise " + realuser.username + ", the most deviant, devoted worshipper of the Goddess!","","",Colors.royal); } } cb.sendNotice(msg, user, "", Colors.lilac, "bold"); } function init() { this.dataStore = new Data().load(cb.settings.Data); this.dataStore.loadEvents(); greeting(); cb.setTimeout(this.eventSummary, tick); cb.setTimeout(this.announceConsort, tick); } init();
© Copyright Chaturbate 2011- 2025. All Rights Reserved.