diff --git a/mbchc-local.user.js b/mbchc-local.user.js
index 0f9e24b..36f6f67 100644
--- a/mbchc-local.user.js
+++ b/mbchc-local.user.js
@@ -39,6 +39,8 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
RE_ALL_LEFT: /^<+$/,
RE_ALL_RIGHT: /^>+$/,
RE_CARET: /^\^/,
+ RE_SPACES: /\s{2,}/g,
+ RE_LAST_WORD: /(^|\s)([^\s]*)$/,
RGB_MUTE: "#6c2132",
RGB_POLLY: "#81b1e7",
UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000,
@@ -128,7 +130,7 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
"polish": {"Hands,Boots": {all: "TakeCare"}},
"foot": {"Head,Nose": {others: "Step"}, "Torso,Boots": {others: "MassageFeet"}, "Vulva,VulvaPiercings": {others: "MasturbateFoot"}},
"fist": {"Vulva,Butt": {all: "MasturbateFist"}},
- "fuck": {"Mouth,Vulva,Butt": {others: "PenetrateSlow"}},
+ "fuck": {"Mouth,Vulva,Butt": {others: "PenetrateSlow"}}, //peg?
"pound": {"Mouth,Vulva,Butt": {others: "PenetrateFast"}},
"tongue": {"Vulva,VulvaPiercings": {others: "MasturbateTongue"}},
"finger": {"Breast,Butt,Vulva,VulvaPiercings": {all: "MasturbateHand"}},
@@ -152,11 +154,6 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
"ItemEars": ["ear", "ears", "earlobe", "earlobes"],
"ItemHead": ["head", "face", "hair", "eyes", "forehead"],
},
- COMMANDS: [
- { Tag: "mbchc", Description: ": Utility functions (\"/mbchc\" for help)", Action: (argline, cmdline, args) => window.MBCHC.command_mbchc(argline, cmdline, args) },
- { Tag: "activity", Description: "[Message]: Send a custom activity (or \"@@Message\", or \"@Message\" as yourself)", Action: (argline, cmdline, args) => window.MBCHC.command_activity(argline, cmdline, args) },
- { Tag: "do", Description: ": Do an activity, as if clicked on its button (\"/do\" for help)", Action: (argline, cmdline, args) => window.MBCHC.command_do(argline, cmdline, args) },
- ],
SUBCOMMANDS_MBCHC: {
"versions": {desc: "show the mod versions across the room", cb: mbchc => mbchc.inform(mbchc.gather_versions().map(c => `
${c.name} (${c.cid}): ${c.version}
`).join(""))},
"autohack": {desc: "toggle the autohack feature", cb: mbchc => mbchc.inform(`Autohack is now ${(mbchc.AUTOHACK_ENABLED = !mbchc.AUTOHACK_ENABLED) ? "enabled" : "disabled"}`)},
@@ -214,7 +211,7 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
return(result)
},
bg_colour: function() { return(document.getElementById("TextAreaChatLog").dataset.colortheme.startsWith("light") ? this.RGB_POLLY : this.RGB_MUTE) },
- inform: function(html) { window.ChatRoomSendLocal(`${html}
`, 60000) },
+ inform: function(html, timeout = 60000) { window.ChatRoomSendLocal(`${html}
`, timeout) },
report: function(x) {
this.inform(`Error: ${x.toString()}`)
if (this.RETHROW) throw x
@@ -383,38 +380,70 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
if (!char.MBCHC_LOCAL) char.MBCHC_LOCAL = {}
if (!char.MBCHC_LOCAL.TZ) char.MBCHC_LOCAL.TZ = this.find_timezone(char)
},
- command_mbchc: function(argline, cmdline, args) { try {
- if (args.length < 1) return(this.inform(Object.entries(this.SUBCOMMANDS_MBCHC).map(([cmd, sub]) => `/mbchc ${cmd} ${sub.args ? Object.keys(sub.args).join(" ") : ""}: ${sub.desc}
`).join("")))
+ command_mbchc: function(argline, cmdline, args) { const mbchc = window.MBCHC; try { // `this` is command object
+ if (args.length < 1) return(mbchc.inform(Object.entries(mbchc.SUBCOMMANDS_MBCHC).map(([cmd, sub]) => `/mbchc ${cmd} ${sub.args ? Object.keys(sub.args).join(" ") : ""}: ${sub.desc}
`).join("")))
let cmd = String(args.shift())
- let sub = this.ensure(`unknown subcommand "${cmd}"`, () => this.SUBCOMMANDS_MBCHC[cmd])
- sub.cb.call(this, this, args, argline, cmdline)
- } catch (x) { this.report(x) } },
- command_activity: function(argline, cmdline, args) { if (!this.empty(argline)) { try {
- let message = this.normalise_message(cmdline.replace(this.RE_ACTIVITY, ''), {trim: true, dot: true, up: true})
- this.send_activity(message)
- } catch (x) { this.report(x) } } },
- command_do: function(argline, cmdline, args) { try {
- if (args.length < 1) return(this.inform("Usage: /do VERB [ZONE] [TARGET]
Available verbs:
" + Object.keys(this.MAP_ACTIONS).join(", ") + "Available zones:
" + Object.keys(this.DO_DATA.zones).join(", ")))
+ let sub = mbchc.ensure(`unknown subcommand "${cmd}"`, () => mbchc.SUBCOMMANDS_MBCHC[cmd])
+ sub.cb.call(mbchc, mbchc, args, argline, cmdline)
+ } catch (x) { mbchc.report(x) } },
+ command_activity: function(argline, cmdline, args) { const mbchc = window.MBCHC; if (!mbchc.empty(argline)) { try { // `this` is command object
+ let message = mbchc.normalise_message(cmdline.replace(mbchc.RE_ACTIVITY, ''), {trim: true, dot: true, up: true})
+ mbchc.send_activity(message)
+ } catch (x) { mbchc.report(x) } } },
+ command_do: function(argline, cmdline, args) { const mbchc = window.MBCHC; try { // `this` is command object
+ if (args.length < 1) return(mbchc.inform("Usage: /do VERB [ZONE] [TARGET]
Available verbs:
" + Object.keys(mbchc.MAP_ACTIONS).join(", ") + "Available zones:
" + Object.keys(mbchc.DO_DATA.zones).join(", ")))
let [verb, zone, target] = args
- if (!this.DO_DATA.verbs[verb]) throw `unknown verb "${verb}"`
- let zones = this.DO_DATA.verbs[verb]
+ let zones = mbchc.ensure(`unknown verb "${verb}"`, () => mbchc.DO_DATA.verbs[verb])
if (1 === Object.keys(zones).length) {
if (!target) target = zone
- zone = this.MAP_ZONES[Object.keys(zones)[0]][0]
+ zone = mbchc.MAP_ZONES[Object.keys(zones)[0]][0]
}
if (!zone) throw "zone missing"
- let ag = this.ensure(`unknown zone "${zone}"`, () => this.DO_DATA.zones[zone])
- let types = this.ensure(`zone "${zone}" invalid for "${verb}"`, () => zones[ag])
+ let ag = mbchc.ensure(`unknown zone "${zone}"`, () => mbchc.DO_DATA.zones[zone])
+ let types = mbchc.ensure(`zone "${zone}" invalid for "${verb}"`, () => zones[ag])
let char = window.Player
- if (target && ((types.self.length < 1) || (types.others.length > 0))) char = this.target2char(target)
+ if (target && ((types.self.length < 1) || (types.others.length > 0))) char = mbchc.target2char(target)
let type = char.IsPlayer() ? "self" : "others"
let available = window.ActivityAllowedForGroup(char, ag)
let toy = window.InventoryGet(window.Player, "ItemHands")
if (toy && toy.Asset.Name === "SpankingToys") available.push(window.AssetAllActivities(char.AssetFamily).find(a => a.Name === window.InventorySpankingToysGetActivity(window.Player)))
- let actions = this.ensure(`zone "${zone}" invalid for ("${verb}" "${type}")`, () => types[type])
- let action = this.ensure(`invalid action (${verb} ${zone} ${target})`, () => actions.find(name => available.find(a => a.Name === name)))
- this.run_activity(char, ag, action)
- } catch (x) { this.report(x) } },
+ let actions = mbchc.ensure(`zone "${zone}" invalid for ("${verb}" "${type}")`, () => types[type])
+ let action = mbchc.ensure(`invalid action (${verb} ${zone} ${target})`, () => actions.find(name => available.find(a => a.Name === name)))
+ mbchc.run_activity(char, ag, action)
+ } catch (x) { mbchc.report(x) } },
+ complete_common: function() {
+ let input = document.getElementById("InputChat").value
+ return([this, input, input.replace(this.RE_SPACES, " ").split(" ")])
+ },
+ complete: function(options) {
+ const chat = document.getElementById("InputChat")
+ if (options.length < 1) {
+ // TODO: maybe signal no valid options?
+ return
+ }
+ if (options.length > 1) {
+ // TODO
+ // autocomplete to common prefix
+ // better hint: hide on input
+ // better hint: increase density
+ this.inform(options.sort().map(s => `${s}
`).join(""), 10000)
+ } else {
+ window.ElementValue("InputChat", chat.value.replace(this.RE_LAST_WORD, `$1${options[0]} `))
+ window.ElementFocus("InputChat")
+ }
+ },
+ complete_mbchc: function(args, locase, cmdline) { const [mbchc, input, tokens] = window.MBCHC.complete_common(); // `this` is command object
+//console.debug([input, tokens])
+ if (tokens.length < 1) return
+ if (tokens.length < 2) mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`])
+ if (tokens.length < 3) mbchc.complete(Object.keys(mbchc.SUBCOMMANDS_MBCHC).filter(c => c.startsWith(tokens[1]))) // complete subcommand name
+ },
+ complete_do: function(args, locase, cmdline) { const [mbchc, input, tokens] = window.MBCHC.complete_common(); // `this` is command object
+//console.debug([input, tokens])
+ if (tokens.length < 1) return
+ if (tokens.length < 2) mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`])
+ if (tokens.length < 3) mbchc.complete(Object.keys(mbchc.DO_DATA.verbs).filter(c => c.startsWith(tokens[1]))) // complete verb
+ },
loader: function() {
if (this.remove_load_hook) {
this.remove_load_hook()
@@ -422,6 +451,11 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
}
if (this.LOADED) return
// Calculated values
+ const COMMANDS = [
+ { Tag: "mbchc", Description: ": Utility functions (\"/mbchc\" for help)", Action: this.command_mbchc, AutoComplete: this.complete_mbchc },
+ { Tag: "activity", Description: "[Message]: Send a custom activity (or \"@@Message\", or \"@Message\" as yourself)", Action: this.command_activity },
+ { Tag: "do", Description: ": Do an activity, as if clicked on its button (\"/do\" for help)", Action: this.command_do, AutoComplete: this.complete_do },
+ ]
this.HIDE_ALL = this.HIDE_SPECIAL.concat(this.HIDE_BODY).concat(this.HIDE_CLOTHES).concat(this.HIDE_ITEMS)
this.CommandsKey = CommandsKey /* eslint-disable-line no-undef */ // window.CommandsKey is undefined
this.RE_ACTIVITY = RegExp(`^${this.CommandsKey}activity `)
@@ -430,7 +464,7 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
this.calculate_maps()
this.patch_handheld()
window.Player.MBCHC = {VERSION: this.VERSION}
- window.CommandCombine(this.COMMANDS)
+ window.CommandCombine(COMMANDS)
this.LOADED = true
console.info(this.log(`loaded version ${this.VERSION}`))
}
@@ -438,13 +472,13 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
// Hooks
window.MBCHC.sdk = window.bcModSdk.registerMod("MBCHC", window.MBCHC.VERSION)
- window.MBCHC.sdk.hookFunction("CharacterOnlineRefresh", 0, (args, next) => {
- let result = next(args)
- window.MBCHC.update_char(args[0])
+ window.MBCHC.sdk.hookFunction("CharacterOnlineRefresh", 0, (nextargs, next) => {
+ let result = next(nextargs)
+ window.MBCHC.update_char(nextargs[0])
return(result)
})
- window.MBCHC.sdk.hookFunction("ChatRoomMessageInvolvesPlayer", 0, (args, next) => {
- let data = args[0]
+ window.MBCHC.sdk.hookFunction("ChatRoomMessageInvolvesPlayer", 0, (nextargs, next) => {
+ let data = nextargs[0]
if (!data.MBCHC_ID) {
data.MBCHC_ID = window.MBCHC.NEXT_MESSAGE
window.MBCHC.NEXT_MESSAGE += 1
@@ -453,10 +487,10 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
if (("ServerEnter" === data.Content) && ("Action" === data.Type) && (data.Sender === window.Player.cid)) { window.MBCHC.player_enters_room() }
if (("MBCHC" === data.Content) && ("Hidden" === data.Type)) { window.MBCHC.receive(data) }
}
- return(next(args))
+ return(next(nextargs))
})
- window.MBCHC.sdk.hookFunction("ChatRoomReceiveSuitcaseMoney", 0, (args, next) => {
- let result = next(args)
+ window.MBCHC.sdk.hookFunction("ChatRoomReceiveSuitcaseMoney", 0, (nextargs, next) => {
+ let result = next(nextargs)
if (window.MBCHC.AUTOHACK_ENABLED && window.MBCHC.LAST_HACKED) {
window.CurrentCharacter = window.MBCHC.cid2char(window.MBCHC.LAST_HACKED)
window.MBCHC.LAST_HACKED = null
@@ -464,17 +498,17 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
}
return(result)
})
- window.MBCHC.sdk.hookFunction("ChatRoomSendChat", 0, (args, next) => {
+ window.MBCHC.sdk.hookFunction("ChatRoomSendChat", 0, (nextargs, next) => {
let input = window.ElementValue("InputChat")
if (!input.startsWith("@@@")) {
input = input.replace(window.MBCHC.RE_PREF_ACTIVITY, window.MBCHC.PREF_ACTIVITY)
input = input.replace(window.MBCHC.RE_PREF_ACTIVITY_ME, `${window.MBCHC.PREF_ACTIVITY}<${window.Player.cid}:>SourceCharacter `)
}
window.ElementValue("InputChat", input)
- return(next(args))
+ return(next(nextargs))
})
- window.MBCHC.sdk.hookFunction("ChatRoomDrawCharacterOverlay", 0, (args, next) => {
- let [C, CharX, CharY, Zoom, Pos] = args
+ window.MBCHC.sdk.hookFunction("ChatRoomDrawCharacterOverlay", 0, (nextargs, next) => {
+ let [C, CharX, CharY, Zoom, Pos] = nextargs
if ((window.ChatRoomHideIconState < 1) && C.MBCHC) {
let colour = (C.MBCHC.VERSION === window.Player.MBCHC.VERSION) ? window.MBCHC.RGB_POLLY : window.MBCHC.RGB_MUTE
window.DrawRect(CharX + 175 * Zoom, CharY, 50 * Zoom, 50 * Zoom, colour)
@@ -484,25 +518,22 @@ var bcModSdk=function(){"use strict";const o="1.0.2";function e(o){alert("Mod ER
let text = localtime.toLocaleTimeString([], {hourCycle: "h24", hour: "2-digit"})
window.DrawTextFit(text, CharX + 200 * Zoom, CharY + 25 * Zoom, 46 * Zoom, "white", "black")
}
- return(next(args))
+ return(next(nextargs))
})
- window.MBCHC.sdk.hookFunction("CommandAutoComplete", 0, (args, next) => {
- return(next(args))
- })
- window.MBCHC.sdk.hookFunction("ElementCreateInput", 0, (args, next) => {
- let [ID, Type, Value, MaxLength] = args
- let result = next(args)
+ window.MBCHC.sdk.hookFunction("ElementCreateInput", 0, (nextargs, next) => {
+ let [ID, Type, Value, MaxLength] = nextargs
+ let result = next(nextargs)
if (("bce_LayerPriority" === ID) && window.CurrentCharacter && window.CurrentCharacter.FocusGroup && window.InventoryLocked(window.CurrentCharacter, window.CurrentCharacter.FocusGroup.Name, true)) window.ElementSetAttribute(ID, "disabled", true)
return(result)
})
- window.MBCHC.remove_bce_hook = window.MBCHC.sdk.hookFunction("MainRun", 0, (args, next) => {
+ window.MBCHC.remove_bce_hook = window.MBCHC.sdk.hookFunction("MainRun", 0, (nextargs, next) => {
if (window.bce_ActivityTriggers) window.MBCHC.patch_bce()
- return(next(args))
+ return(next(nextargs))
})
// MAIN SCREEN TURN ON
if (window.MBCHC.need_load_hook(window.CurrentModule, window.CurrentScreen)) {
- window.MBCHC.remove_load_hook = window.MBCHC.sdk.hookFunction("AsylumGGTSSAddItems", 0, (args, next) => {window.MBCHC.loader(); return(next(args))})
+ window.MBCHC.remove_load_hook = window.MBCHC.sdk.hookFunction("AsylumGGTSSAddItems", 0, (nextargs, next) => {window.MBCHC.loader(); return(next(nextargs))})
} else {
window.MBCHC.loader()
if (("Online" === window.CurrentModule) && ("ChatRoom" === window.CurrentScreen)) {