* Fixed MISSING PLAYER DIALOG in donate subcommand
* Removed the club version check, it really doesn't do anything
* Removed the broken disappear subcommand
* Removed the mostly useless title subcommand
* Removed the versions subcommand, it was superceded by /versions
* Took another dive in the keystroke catcher, should fix all known conflicts
* Disabled the indicator square
* Thought about moving the time digits to a proper place, but ultimately decided to leave them be for now
This commit is contained in:
Mute 2023-12-19 01:15:39 +00:00
parent 526b51e158
commit 378d0af8d3

201
mbchc.mjs
View File

@ -1,10 +1,11 @@
export {} export {}
if (!window.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading') // Zero-width non-joiner, used to break up ligatures, does nothing here, but an empty string is a falsy value
const MISSING_PLAYER_DIALOG = {Tag: 'MISSING PLAYER DIALOG: ', Text: '\u200C'}
if (window.MBCHC) throw new Error('MBCHC found, aborting loading') if (window.MBCHC) throw new Error('MBCHC found, aborting loading')
window.MBCHC = { window.MBCHC = {
VERSION: 'dev.9', VERSION: 'dev.9',
TARGET_VERSION: 'R99',
NEXT_MESSAGE: 1, NEXT_MESSAGE: 1,
LOG_MESSAGES: false, LOG_MESSAGES: false,
RETHROW: false, RETHROW: false,
@ -12,7 +13,7 @@ window.MBCHC = {
AUTOHACK_ENABLED: false, AUTOHACK_ENABLED: false,
LAST_HACKED: null, LAST_HACKED: null,
HISTORY_MODE: false, HISTORY_MODE: false,
RE_TITLE: /^[a-zA-Z]+$/, // RE_TITLE: /^[a-zA-Z]+$/,
RE_PREF_ACTIVITY_ME: /^@/, RE_PREF_ACTIVITY_ME: /^@/,
RE_PREF_ACTIVITY: /^@@/, RE_PREF_ACTIVITY: /^@@/,
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/, RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
@ -25,62 +26,62 @@ window.MBCHC = {
RGB_MUTE: '#6c2132', RGB_MUTE: '#6c2132',
RGB_POLLY: '#81b1e7', RGB_POLLY: '#81b1e7',
UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000, UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000,
HIDE_SPECIAL: ['Activity', 'Emoticon'], // HIDE_SPECIAL: ['Activity', 'Emoticon'],
HIDE_BODY: ['Blush', 'BodyLower', 'BodyUpper', 'Eyebrows', 'Eyes', 'Eyes2', 'Face', 'Fluids', 'HairBack', 'HairFront', 'Hands', 'Head', 'LeftHand', 'Mouth', 'Nipples', 'Pussy', 'RightHand'], // HIDE_BODY: ['Blush', 'BodyLower', 'BodyUpper', 'Eyebrows', 'Eyes', 'Eyes2', 'Face', 'Fluids', 'HairBack', 'HairFront', 'Hands', 'Head', 'LeftHand', 'Mouth', 'Nipples', 'Pussy', 'RightHand'],
HIDE_CLOTHES: [ // HIDE_CLOTHES: [
'Cloth', // 'Cloth',
'ClothAccessory', // 'ClothAccessory',
'Necklace', // 'Necklace',
'Suit', // 'Suit',
'ClothLower', // 'ClothLower',
'SuitLower', // 'SuitLower',
'Bra', // 'Bra',
'Corset', // 'Corset',
'Panties', // 'Panties',
'Socks', // 'Socks',
'RightAnklet', // 'RightAnklet',
'LeftAnklet', // 'LeftAnklet',
'Garters', // 'Garters',
'Shoes', // 'Shoes',
'Hat', // 'Hat',
'HairAccessory3', // 'HairAccessory3',
'HairAccessory1', // 'HairAccessory1',
'HairAccessory2', // 'HairAccessory2',
'Gloves', // 'Gloves',
'Bracelet', // 'Bracelet',
'Glasses', // 'Glasses',
'Mask', // 'Mask',
'TailStraps', // 'TailStraps',
'Wings', // 'Wings',
], // ],
HIDE_ITEMS: [ // HIDE_ITEMS: [
'ItemMisc', // 'ItemMisc',
'ItemEars', // 'ItemEars',
'ItemHead', // 'ItemHead',
'ItemNose', // 'ItemNose',
'ItemHood', // 'ItemHood',
'ItemAddon', // 'ItemAddon',
'ItemMouth', // 'ItemMouth',
'ItemMouth2', // 'ItemMouth2',
'ItemMouth3', // 'ItemMouth3',
'ItemArms', // 'ItemArms',
'ItemNeckAccessories', // 'ItemNeckAccessories',
'ItemNeck', // 'ItemNeck',
'ItemNeckRestraints', // 'ItemNeckRestraints',
'ItemNipples', // 'ItemNipples',
'ItemNipplesPiercings', // 'ItemNipplesPiercings',
'ItemBreast', // 'ItemBreast',
'ItemTorso', // 'ItemTorso',
'ItemTorso2', // 'ItemTorso2',
'ItemHands', // 'ItemHands',
'ItemPelvis', // 'ItemPelvis',
'ItemVulva', // 'ItemVulva',
'ItemVulvaPiercings', // 'ItemVulvaPiercings',
'ItemDevices', // 'ItemDevices',
'ItemLegs', // 'ItemLegs',
'ItemFeet', // 'ItemFeet',
'ItemBoots', // 'ItemBoots',
], // ],
MAP_ACTIONS: { // ActivityFemale3DCG MAP_ACTIONS: { // ActivityFemale3DCG
// action // action
'nod|yes': {Head: {self: 'Nod'}}, 'nod|yes': {Head: {self: 'Nod'}},
@ -182,11 +183,11 @@ window.MBCHC = {
[/([^\\])\$/g, '$1\\.?$$'], [/([^\\])\$/g, '$1\\.?$$'],
], ],
SUBCOMMANDS_MBCHC: { SUBCOMMANDS_MBCHC: {
versions: {desc: 'show the mod versions across the room', cb: mbchc => mbchc.inform(mbchc.gather_versions().map(c => `<div><b>${c.name}</b> (${c.cid}): ${c.version}</div>`).join(''))}, // versions: {desc: 'show the mod versions across the room', cb: mbchc => mbchc.inform(mbchc.gather_versions().map(c => `<div><b>${c.name}</b> (${c.cid}): ${c.version}</div>`).join(''))},
autohack: {desc: 'toggle the autohack feature', cb: mbchc => mbchc.inform(`Autohack is now ${(mbchc.AUTOHACK_ENABLED = !mbchc.AUTOHACK_ENABLED) ? 'enabled' : 'disabled'}`)}, // eslint-disable-line no-cond-assign autohack: {desc: 'toggle the autohack feature', cb: mbchc => mbchc.inform(`Autohack is now ${(mbchc.AUTOHACK_ENABLED = !mbchc.AUTOHACK_ENABLED) ? 'enabled' : 'disabled'}`)}, // eslint-disable-line no-cond-assign
disappear: {desc: 'become invisible (requires anal hook -> hair)', cb: mbchc => mbchc.disappear()}, // disappear: {desc: 'become invisible (requires anal hook -> hair)', cb: mbchc => mbchc.disappear()},
donate: {desc: 'Buy data and send it to recipient', args: {TARGET: {}}, cb: (mbchc, args) => mbchc.donate_data(args[0])}, donate: {desc: 'Buy data and send it to recipient', args: {TARGET: {}}, cb: (mbchc, args) => mbchc.donate_data(args[0])},
title: {desc: 'set a custom title (<b>WIP</b>)', args: {TITLE: {}}, cb: (mbchc, args) => mbchc.title(args[0])}, // title: {desc: 'set a custom title (<b>WIP</b>)', args: {TITLE: {}}, cb: (mbchc, args) => mbchc.title(args[0])},
tz: {desc: 'set target\'s UTC offset', args: {OFFSET: {}, '[TARGET]': {}}, cb: (mbchc, args) => mbchc.set_timezone(args)}, tz: {desc: 'set target\'s UTC offset', args: {OFFSET: {}, '[TARGET]': {}}, cb: (mbchc, args) => mbchc.set_timezone(args)},
'purge!': {desc: 'delete MBCHC online saved data', cb(mbchc) { 'purge!': {desc: 'delete MBCHC online saved data', cb(mbchc) {
if (window.Player.OnlineSettings.MBCHC) { if (window.Player.OnlineSettings.MBCHC) {
@ -209,10 +210,8 @@ window.MBCHC = {
const processed = {self: (actions.self) ? actions.self.split('|').concat(all) : all, others: (actions.others) ? actions.others.split('|').concat(all) : all} const processed = {self: (actions.self) ? actions.self.split('|').concat(all) : all, others: (actions.others) ? actions.others.split('|').concat(all) : all}
for (const zone of zones.split(',')) unwound[`Item${zone}`] = processed for (const zone of zones.split(',')) unwound[`Item${zone}`] = processed
} }
for (const verb of verbs.split('|')) this.DO_DATA.verbs[verb] = unwound for (const verb of verbs.split('|')) this.DO_DATA.verbs[verb] = unwound
} }
for (const [ag, zones] of Object.entries(this.MAP_ZONES)) for (const zone of zones) this.DO_DATA.zones[zone] = ag for (const [ag, zones] of Object.entries(this.MAP_ZONES)) for (const zone of zones) this.DO_DATA.zones[zone] = ag
}, },
settings(setting = null) { settings(setting = null) {
@ -224,7 +223,6 @@ window.MBCHC = {
if (!window.Player.OnlineSettings.MBCHC) window.Player.OnlineSettings.MBCHC = {} if (!window.Player.OnlineSettings.MBCHC) window.Player.OnlineSettings.MBCHC = {}
cb.call(this, window.Player.OnlineSettings.MBCHC) cb.call(this, window.Player.OnlineSettings.MBCHC)
} }
window.ServerAccountUpdate.QueueData({OnlineSettings: window.Player.OnlineSettings}) window.ServerAccountUpdate.QueueData({OnlineSettings: window.Player.OnlineSettings})
}, },
log(level, message) { log(level, message) {
@ -244,7 +242,6 @@ window.MBCHC = {
const rest = result.slice(1) const rest = result.slice(1)
result = first + rest result = first + rest
} }
if (options.dot && this.RE_LAST_LETTER.test(result)) result = `${result}.` if (options.dot && this.RE_LAST_LETTER.test(result)) result = `${result}.`
return (result) return (result)
}, },
@ -331,7 +328,7 @@ window.MBCHC = {
if (window.Player.Money < cost) throw new Error('not enough money') if (window.Player.Money < cost) throw new Error('not enough money')
window.CharacterChangeMoney(window.Player, -cost) window.CharacterChangeMoney(window.Player, -cost)
window.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: char.cid}) window.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: char.cid})
window.ChatRoomMessage({Sender: window.Player.cid, Type: 'Action', Content: `You've bought data for $${cost} and sent it to ${char.dn}.`, Dictionary: [{Tag: 'MISSING PLAYER DIALOG: ', Text: ''}]}) window.ChatRoomMessage({Sender: window.Player.cid, Type: 'Action', Content: `You've bought data for $${cost} and sent it to ${char.dn}.`, Dictionary: [MISSING_PLAYER_DIALOG]})
}, },
run_activity(char, ag, action) { run_activity(char, ag, action) {
try { try {
@ -357,14 +354,13 @@ window.MBCHC = {
return ({Tag: `${type}Character`, MemberNumber: cid, Text: this.cid2char(cid).dn}) return ({Tag: `${type}Character`, MemberNumber: cid, Text: this.cid2char(cid).dn})
}, },
send_activity(message) { send_activity(message) {
const dict = [{Tag: 'MISSING PLAYER DIALOG: ', Text: '\u200C'}] // Zero-width non-joiner, used to break up ligatures, does nothing here, but an empty string is a falsy value const dict = [MISSING_PLAYER_DIALOG]
const cids = message.match(this.RE_ACT_CIDS) const cids = message.match(this.RE_ACT_CIDS)
if (cids) { if (cids) {
message = message.replace(this.RE_ACT_CIDS, '') message = message.replace(this.RE_ACT_CIDS, '')
if (cids[1]) dict.push(this.cid2dict('Source', cids[1])) if (cids[1]) dict.push(this.cid2dict('Source', cids[1]))
if (cids[2]) dict.push(this.cid2dict('Target', cids[2]), this.cid2dict('Destination', cids[2])) if (cids[2]) dict.push(this.cid2dict('Target', cids[2]), this.cid2dict('Destination', cids[2]))
} }
window.ServerSend('ChatRoomChat', {Type: 'Action', Content: message, Dictionary: dict}) window.ServerSend('ChatRoomChat', {Type: 'Action', Content: message, Dictionary: dict})
}, },
receive(data) { receive(data) {
@ -377,10 +373,8 @@ window.MBCHC = {
if (payload.type === 'greetings') this.hello(char) if (payload.type === 'greetings') this.hello(char)
break break
} }
default: // If we don't know the type it may be from a newer version default: // If we don't know the type it may be from a newer version
} }
return true return true
}, },
hello(char = null) { hello(char = null) {
@ -390,22 +384,22 @@ window.MBCHC = {
if (char) message.Target = char.cid if (char) message.Target = char.cid
window.ServerSend('ChatRoomChat', message) window.ServerSend('ChatRoomChat', message)
}, },
disappear() { // disappear() {
const item = window.InventoryGet(window.Player, 'ItemButt') // const item = window.InventoryGet(window.Player, 'ItemButt')
if (!item || !item.Asset || !item.Asset.Name) throw new Error('butt seems empty') // if (!item || !item.Asset || !item.Asset.Name) throw new Error('butt seems empty')
if (item.Asset.Name !== 'AnalHook') throw new Error('butt seems occupied by something other than the anal hook') // if (item.Asset.Name !== 'AnalHook') throw new Error('butt seems occupied by something other than the anal hook')
if (!item.Property.Type || item.Property.Type !== 'Hair') throw new Error('anal hook seems not tied to hair') // if (!item.Property.Type || item.Property.Type !== 'Hair') throw new Error('anal hook seems not tied to hair')
item.Property = {Type: 'Hair', Hide: this.HIDE_ALL} // item.Property = {Type: 'Hair', Hide: this.HIDE_ALL}
window.CharacterRefresh(window.Player, true, true) // window.CharacterRefresh(window.Player, true, true)
}, // },
title(title) { // WIP // title(title) { // WIP
if (this.empty(title)) throw new Error('empty title') // if (this.empty(title)) throw new Error('empty title')
title = this.normalise_message(title, {trim: true, up: true, low: true}) // title = this.normalise_message(title, {trim: true, up: true, low: true})
if (title.length > 16) throw new Error('title too long') // if (title.length > 16) throw new Error('title too long')
if (!this.RE_TITLE.test(title)) throw new Error('invalid title') // if (!this.RE_TITLE.test(title)) throw new Error('invalid title')
window.TitleSet(title) // window.TitleSet(title)
// Window.TitleList.push({Name: title, Requirement: () => true}) // check for existing first // // Window.TitleList.push({Name: title, Requirement: () => true}) // check for existing first
}, // },
copy_fbc_trigger(trigger) { copy_fbc_trigger(trigger) {
const result = { const result = {
Type: 'Action', Type: 'Action',
@ -418,15 +412,15 @@ window.MBCHC = {
this.remove_fbc_hook() this.remove_fbc_hook()
delete this.remove_fbc_hook delete this.remove_fbc_hook
window.bce_ActivityTriggers.push(...window.bce_ActivityTriggers.filter(t => t.Type === 'Emote').map(t => this.copy_fbc_trigger(t))) window.bce_ActivityTriggers.push(...window.bce_ActivityTriggers.filter(t => t.Type === 'Emote').map(t => this.copy_fbc_trigger(t)))
/* (["anim", "pose"]).forEach(tag => {let cmd = window.Commands.find(c => tag === c.Tag); if (cmd) cmd.AutoComplete = this[`complete_fbc_${tag}`]}) */ // this line explodes, don't ask me why /* (["anim", "pose"]).forEach(tag => {let cmd = window.Commands.find(c => tag === c.Tag); if (cmd) cmd.AutoComplete = this[`complete_fbc_${tag}`]}) */ // this line explodes, because it needs a semicolon in front if it
let cmd = window.Commands.find(c => c.Tag === 'anim') let cmd = window.Commands.find(c => c.Tag === 'anim')
if (cmd) cmd.AutoComplete = this.complete_fbc_anim if (cmd) cmd.AutoComplete = this.complete_fbc_anim
cmd = window.Commands.find(c => c.Tag === 'pose') cmd = window.Commands.find(c => c.Tag === 'pose')
if (cmd) cmd.AutoComplete = this.complete_fbc_pose if (cmd) cmd.AutoComplete = this.complete_fbc_pose
}, },
gather_versions() { // gather_versions() {
return (window.ChatRoomCharacter.filter(c => c.MBCHC).map(c => ({name: c.dn, cid: c.cid, version: c.MBCHC.VERSION}))) // return (window.ChatRoomCharacter.filter(c => c.MBCHC).map(c => ({name: c.dn, cid: c.cid, version: c.MBCHC.VERSION})))
}, // },
find_timezone(char) { find_timezone(char) {
const timezones = this.settings('timezones') const timezones = this.settings('timezones')
if (timezones && typeof timezones[char.cid] === 'number') return (timezones[char.cid]) if (timezones && typeof timezones[char.cid] === 'number') return (timezones[char.cid])
@ -629,15 +623,20 @@ window.MBCHC = {
window.ElementValue('InputChat', history[found]) window.ElementValue('InputChat', history[found])
window.ChatRoomLastMessageIndex = found window.ChatRoomLastMessageIndex = found
}, },
focus_chat_checks() { // we only want to catch chatlog and canvas (no map though) keypresses
if (document.activeElement === document.body) return true
if (document.activeElement.id !== 'MainCanvas') return false
return !window.ChatRoomMapVisible
},
focus_chat_whitelist(event) { focus_chat_whitelist(event) {
if (event.ctrlKey && event.key === 'v') return true // Ctrl+V should paste if (event.ctrlKey && event.key === 'v') return true // Ctrl+V should paste
return false return false
}, },
focus_chat(event) { // TODO: this is not ideal, but it will have to do for now focus_chat(event) {
if (event.repeat) return // Only unique presses please if (event.repeat) return // Only unique presses please
if (document.querySelector('#InputChat')?.style.display !== 'inline') return // Input chat missing if (!this.focus_chat_checks()) return
if (['InputChat', 'bce-message-input'].includes(document.activeElement.id)) return // Focus already set
if ([event.altKey, event.ctrlKey, event.metaKey].some(Boolean) && !this.focus_chat_whitelist(event)) return // Alt, ctrl and meta should all be false if ([event.altKey, event.ctrlKey, event.metaKey].some(Boolean) && !this.focus_chat_whitelist(event)) return // Alt, ctrl and meta should all be false
if (document.querySelector('#InputChat')?.style.display !== 'inline') return // Input chat missing
window.ElementFocus('InputChat') window.ElementFocus('InputChat')
}, },
loader() { loader() {
@ -653,14 +652,13 @@ window.MBCHC = {
{Tag: 'activity', Description: '[Message]: Send a custom activity (or "@@Message", or "@Message" as yourself)', Action: this.command_activity}, {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}, {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.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.CommandsKey = CommandsKey /* eslint-disable-line no-undef */ // window.CommandsKey is undefined
this.RE_ACTIVITY = new RegExp(`^${this.CommandsKey}activity `) this.RE_ACTIVITY = new RegExp(`^${this.CommandsKey}activity `)
this.PREF_ACTIVITY = `${this.CommandsKey}activity ` this.PREF_ACTIVITY = `${this.CommandsKey}activity `
this.COMP_HINT = document.createElement('div') this.COMP_HINT = document.createElement('div')
this.COMP_HINT.id = 'mbchcCompHint' this.COMP_HINT.id = 'mbchcCompHint'
const css = document.createElement('style') const css = document.createElement('style')
css.type = 'text/css'
css.textContent = ` css.textContent = `
#TextAreaChatLog .mbchc, #TextAreaChatLog .mbchc { #TextAreaChatLog .mbchc, #TextAreaChatLog .mbchc {
background-color: ${this.RGB_POLLY}; background-color: ${this.RGB_POLLY};
@ -715,9 +713,9 @@ window.MBCHC = {
} }
}) })
this.before('ChatRoomDrawCharacterOverlay', (C, CharX, CharY, Zoom, _Pos) => { this.before('ChatRoomDrawCharacterOverlay', (C, CharX, CharY, Zoom, _Pos) => {
if (window.ChatRoomHideIconState < 1 && C.MBCHC) { // if (window.ChatRoomHideIconState < 1 && C.MBCHC) {
window.DrawRect(CharX + (175 * Zoom), CharY, 50 * Zoom, 50 * Zoom, C.MBCHC.VERSION === window.Player.MBCHC.VERSION ? this.RGB_POLLY : this.RGB_MUTE) // window.DrawRect(CharX + (175 * Zoom), CharY, 50 * Zoom, 50 * Zoom, C.MBCHC.VERSION === window.Player.MBCHC.VERSION ? this.RGB_POLLY : this.RGB_MUTE)
} // }
if (window.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && typeof C.MBCHC_LOCAL.TZ === 'number') { if (window.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && typeof C.MBCHC_LOCAL.TZ === 'number') {
const hours = new Date(window.CommonTime() + this.UTC_OFFSET + (C.MBCHC_LOCAL.TZ * 60 * 60 * 1000)).getHours() const hours = new Date(window.CommonTime() + this.UTC_OFFSET + (C.MBCHC_LOCAL.TZ * 60 * 60 * 1000)).getHours()
window.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black') window.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black')
@ -780,13 +778,14 @@ window.MBCHC = {
// Footer // Footer
this.LOADED = true this.LOADED = true
this.log('info', `loaded version ${this.VERSION}`) this.log('info', `loaded version ${this.VERSION}`)
if (window.GameVersion !== this.TARGET_VERSION) this.log('warn', `Game version doesn't match the target ("${this.TARGET_VERSION}"), beware of incompatibilities`) // TODO: betas are like R86Beta1; cheat?
if ((window.CurrentModule === 'Online') && (window.CurrentScreen === 'ChatRoom')) { if ((window.CurrentModule === 'Online') && (window.CurrentScreen === 'ChatRoom')) {
for (const c of window.ChatRoomCharacter) this.update_char(c) for (const c of window.ChatRoomCharacter) this.update_char(c)
this.player_enters_room() this.player_enters_room()
} }
}, },
preloader() { preloader() {
if (!window.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading')
if (!window.bcModSdk) throw new Error('SDK not found, please load with (or after) FUSAM')
this.SDK = window.bcModSdk.registerMod({name: 'MBCHC', fullName: 'Mute\'s Bondage Club Hacks Collection', version: this.VERSION, repository: 'https://code.fleshless.org/mute/MBCHC/'}) this.SDK = window.bcModSdk.registerMod({name: 'MBCHC', fullName: 'Mute\'s Bondage Club Hacks Collection', version: this.VERSION, repository: 'https://code.fleshless.org/mute/MBCHC/'})
this.before = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs, next) => { this.before = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs, next) => {
try { try {
@ -811,3 +810,5 @@ window.MBCHC = {
} }
window.MBCHC.preloader() window.MBCHC.preloader()
// TODO: with the removal of /mbchc versions it makes no sense to keep track of the mod version and the whole hello() infrastructure can go away