* R86Beta1 compatibility
* patch by Lyra for /do item actions
* removed handheld penetration patch until R86 release
* time zone regexp accepts spaces now
* fixed a bug with UTC+0
* better global keydown interception
* exception handlers for hooks, shouldn't break the whole club now if something goes wrong
* general cleanups and refactor
This commit is contained in:
Mute 2022-11-13 16:40:33 +00:00
parent eaf4e3f26f
commit 45f6f80493

View File

@ -1,6 +1,6 @@
// ==UserScript==
// @name MBCHC
// @version dev.5
// @version dev.6
// @description Mute's Bondage Club Hacks Collection
// @author codename.mute@proton.me
// @namespace https://code.fleshless.org/mute/
@ -11,6 +11,8 @@
// @match https://www.bondageprojects.elementfx.com/R*
// @match https://bondage-europe.com/R*
// @match https://www.bondage-europe.com/R*
// @match http://localhost:*/*
// @match http://127.0.0.1:*/*
// @grant none
// ==/UserScript==
@ -24,7 +26,7 @@
if (!window.AsylumGGTSSAddItems) throw "AsylumGGTSSAddItems() not found, aborting MBCHC loading"
if (window.MBCHC) throw "MBCHC found, aborting loading"
window.MBCHC = {
VERSION: "dev.5",
VERSION: "dev.6",
TARGET_VERSION: "R85",
NEXT_MESSAGE: 1,
LOG_MESSAGES: false,
@ -37,7 +39,7 @@
RE_PREF_ACTIVITY_ME: /^@/,
RE_PREF_ACTIVITY: /^@@/,
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
RE_TZ: /(?:GMT|UTC)([+-]\d\d?)/i,
RE_TZ: /(?:GMT|UTC)\s*([+-])\s*(\d\d?)/i,
RE_ALL_LEFT: /^<+$/,
RE_ALL_RIGHT: /^>+$/,
RE_SPACES: /\s{2,}/g,
@ -46,7 +48,6 @@
RGB_MUTE: "#6c2132",
RGB_POLLY: "#81b1e7",
UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000,
HAND_PENETRATORS: ["Flogger", "Whip", "TennisRacket", "Gavel", "SmallVibratingWand", "LargeDildo", "Vibrator", "Hairbrush", "SmallDildo", "Baguette", "Spatula", "Broom"],
HIDE_SPECIAL: ["Activity","Emoticon"],
HIDE_BODY: ["Blush","BodyLower","BodyUpper","Eyebrows","Eyes","Eyes2","Face","Fluids","HairBack","HairFront","Hands","Head","LeftHand","Mouth","Nipples","Pussy","RightHand"],
HIDE_CLOTHES: [
@ -287,10 +288,10 @@
if (!window.ActivityAllowed()) throw "activities disabled in this room"
if (!window.ServerChatRoomGetAllowItem(window.Player, char)) throw "no permissions"
char.FocusGroup = this.ensure("invalid AssetGroup", () => window.AssetGroupGet(char.AssetFamily, ag))
let activity = this.ensure("invalid activity", () => window.ActivityAllowedForGroup(char, char.FocusGroup.Name, true).find(a => a.Name === action))
if (activity.Name.endsWith("Item")) {
let item = this.ensure("no toy found", () => window.Player.Inventory.find(i => (i.Asset != null) && (i.Asset.Group.Name == char.FocusGroup.Name) && i.Asset.DynamicAllowInventoryAdd(char)))
window.DialogPublishAction(char, window.DialogInventoryCreateItem(char, item, false))
let activity = this.ensure("invalid activity", () => window.ActivityAllowedForGroup(char, char.FocusGroup.Name, true).find(a => a.Name === action || a.Activity?.Name === action))
if ((activity.Name || activity.Activity.Name).endsWith("Item")) {
const item = this.ensure("no toy found", () => window.Player.Inventory.find(i => i.Asset?.Name === "SpankingToys" && i.Asset.Group?.Name === char.FocusGroup.Name && window.AssetSpankingToys.DynamicActivity(char) === (activity.Name || activity.Activity.Name)))
window.DialogPublishAction(char, item)
} else window.ActivityRun(char, activity)
} finally {
char.FocusGroup = null
@ -348,16 +349,6 @@
window.TitleSet(title)
//window.TitleList.push({Name: title, Requirement: () => true}) // check for existing first
},
patch_handheld: function() {
let options = InventoryItemHandsSpankingToysOptions /* eslint-disable-line no-undef */ // window.InventoryItemHandsSpankingToysOptions is undefined
for (let name of this.HAND_PENETRATORS) {
let option = options.find(o => o.Name === name)
if (option && option.Property) {
if (!option.Property.AllowActivity) option.Property.AllowActivity = []
if (option.Property.AllowActivity.indexOf("PenetrateItem") < 0) option.Property.AllowActivity.push("PenetrateItem")
}
}
},
copy_fbc_trigger: function(trigger) {
let result = {
Type: "Action",
@ -377,16 +368,12 @@
if (cmd) cmd.AutoComplete = this.complete_fbc_pose
},
gather_versions: function() { return(window.ChatRoomCharacter.filter(c => c.MBCHC).map(c => ({name: c.dn, cid: c.cid, version: c.MBCHC.VERSION}))) },
need_load_hook: function(module, screen) {
if (!module || !screen) return(true)
if (("Character" === module) && ("Login" === screen)) return(true)
return(false)
},
find_timezone: function(char) {
let timezones = this.settings("timezones")
if (timezones && timezones[char.cid]) return(timezones[char.cid])
let match = (char.Description) ? char.Description.match(this.RE_TZ) : null
if (match) return(Number.parseInt(match[1]))
const timezones = this.settings("timezones")
if (timezones && "number" === typeof timezones[char.cid]) return(timezones[char.cid])
const match = (char.Description) ? char.Description.match(this.RE_TZ) : null
const int = match ? Number.parseInt(match[1] + match[2]) : 42
if (this.in(int, -12, 12)) return(int)
return(null)
},
player_enters_room: function() { // or if the mod is loaded while player is in the room
@ -432,9 +419,9 @@
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)))
if (toy && toy.Asset.Name === "SpankingToys") available.push(window.AssetAllActivities(char.AssetFamily).find(a => a.Name === window.InventorySpankingToysGetActivity?.(window.Player)))
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)))
let action = mbchc.ensure(`invalid action (${verb} ${zone} ${target})`, () => actions.find(name => available.find(a => a.Name === name || a.Activity?.Name === name)))
mbchc.run_activity(char, ag, action)
} catch (x) { mbchc.report(x) } },
bell: function() {
@ -534,7 +521,18 @@
window.ElementValue("InputChat", history[found])
window.ChatRoomLastMessageIndex = found
},
loader: function() {
focus_chat_whitelist(event) {
if (event.ctrlKey && "v" === event.key) return true // ctrl+V should paste
return false
},
focus_chat(event) { // TODO: this is not ideal, but it will have to do for now
if (event.repeat) return // only unique presses please
if ("inline" !== document.getElementById("InputChat")?.style.display) return // input chat missing
if (["InputChat","bce-message-input"].includes(document.activeElement.id)) return // focus already set
if ([event.altKey, event.ctrlKey, event.metaKey].some(i => i) && !this.focus_chat_whitelist(event)) return // alt, ctrl and meta should all be false
window.ElementFocus("InputChat")
},
loader() {
if (this.remove_load_hook) {
this.remove_load_hook()
delete this.remove_load_hook
@ -579,90 +577,52 @@
document.head.appendChild(css)
// Actions
this.calculate_maps()
this.patch_handheld()
window.Player.MBCHC = {VERSION: this.VERSION}
window.CommandCombine(COMMANDS)
this.LOADED = true
this.log("info", `loaded version ${this.VERSION}`)
if (window.GameVersion !== this.TARGET_VERSION) console.warn(`Game version doesn't match the target ("${this.TARGET_VERSION}"), beware of incompatibilities`) // TODO: check betas & cheat
}
} // MBCHC
// Hooks
window.MBCHC.sdk = window.bcModSdk.registerMod("MBCHC", window.MBCHC.VERSION)
window.MBCHC.sdk.hookFunction("CharacterOnlineRefresh", 0, (nextargs, next) => {
let result = next(nextargs)
window.MBCHC.update_char(nextargs[0])
return(result)
})
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
this.remove_fbc_hook = this.before("MainRun", () => window.bce_ActivityTriggers && this.patch_fbc())
this.after("CharacterOnlineRefresh", char => this.update_char(char))
this.after("ChatRoomReceiveSuitcaseMoney", () => {
if (this.AUTOHACK_ENABLED && this.LAST_HACKED) {
window.CurrentCharacter = this.cid2char(this.LAST_HACKED)
this.LAST_HACKED = null
window.ChatRoomTryToTakeSuitcase()
}
return(result)
})
window.MBCHC.sdk.hookFunction("ChatRoomSendChat", 0, (nextargs, next) => {
this.before("ChatRoomSendChat", () => {
let input = window.ElementValue("InputChat")
if (!input.startsWith("@@@") && 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.replace_me)
input = input.replace(this.RE_PREF_ACTIVITY, this.PREF_ACTIVITY)
input = input.replace(this.RE_PREF_ACTIVITY_ME, this.replace_me)
window.ElementValue("InputChat", input)
}
let result = next(nextargs)
let history = window.ChatRoomLastMessage
})
this.after("ChatRoomSendChat", () => {
const history = window.ChatRoomLastMessage
if ((history.length > 1) && (history[history.length - 1] === history[history.length - 2])) {history.pop(); window.ChatRoomLastMessageIndex -= 1}
return(result)
})
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)
this.before("ChatRoomDrawCharacterOverlay", (C, CharX, CharY, Zoom, Pos) => {
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)
if (window.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && "number" === typeof C.MBCHC_LOCAL.TZ) {
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")
}
if ((window.ChatRoomHideIconState < 1) && C.MBCHC_LOCAL && C.MBCHC_LOCAL.TZ) {
let hours = new Date(window.CommonTime() + window.MBCHC.UTC_OFFSET + C.MBCHC_LOCAL.TZ * 60 * 60 * 1000).getHours()
let text = (hours < 10) ? "0" + hours.toString() : hours.toString()
window.DrawTextFit(text, CharX + 200 * Zoom, CharY + 25 * Zoom, 46 * Zoom, "white", "black")
}
return(next(nextargs))
})
window.MBCHC.sdk.hookFunction("ElementValue", 0, (nextargs, next) => { // TODO: layer priority will be locked too if it's the same as difficulty
let [ID, Value] = nextargs
let result = next(nextargs)
if (("bce_LayerPriority" === ID) && window.CurrentCharacter?.FocusGroup && (window.InventoryGet(window.CurrentCharacter, window.CurrentCharacter.FocusGroup.Name)?.Difficulty.toString() === Value) && window.InventoryLocked(window.CurrentCharacter, window.CurrentCharacter.FocusGroup.Name, true)) window.ElementSetAttribute(ID, "disabled", true)
return(result)
})
window.MBCHC.sdk.hookFunction("ChatRoomCreateElement", 0, (nextargs, next) => {
let result = next(nextargs)
if (!window.MBCHC.COMP_HINT.parentElement) document.body.appendChild(window.MBCHC.COMP_HINT)
return(result)
})
window.MBCHC.sdk.hookFunction("ChatRoomClearAllElements", 0, (nextargs, next) => {
window.MBCHC.complete_hint_hide()
window.MBCHC.COMP_HINT.remove()
return(next(nextargs))
})
window.MBCHC.sdk.hookFunction("ChatRoomResize", 0, (nextargs, next) => {
let result = next(nextargs)
if (window.CharacterGetCurrent() == null && window.CurrentScreen == "ChatRoom" && document.getElementById("InputChat") && document.getElementById("TextAreaChatLog") && window.MBCHC.comp_hint_visible()) { // upstream
let fontsize = ChatRoomFontSize /* eslint-disable-line no-undef */ // window.ChatRoomFontSize is undefined
this.after("ElementValue", (ID, Value, cc = window.CurrentCharacter) => "bce_LayerPriority" === ID && cc?.FocusGroup && window.InventoryGet(cc, cc.FocusGroup.Name)?.Difficulty.toString() === Value && window.InventoryLocked(cc, cc.FocusGroup.Name, true) && window.ElementSetAttribute(ID, "disabled", true))
this.after("ChatRoomCreateElement", () => this.COMP_HINT.parentElement || document.body.appendChild(this.COMP_HINT))
this.before("ChatRoomClearAllElements", () => !void this.complete_hint_hide() && this.COMP_HINT.remove())
this.before("ChatRoomClick", () => this.complete_hint_hide())
this.after("ChatRoomResize", () => {
if (window.CharacterGetCurrent() == null && window.CurrentScreen == "ChatRoom" && document.getElementById("InputChat") && document.getElementById("TextAreaChatLog") && this.comp_hint_visible()) { // upstream
const fontsize = ChatRoomFontSize /* eslint-disable-line no-undef */ // window.ChatRoomFontSize is undefined
window.ElementPositionFix("TextAreaChatLog", fontsize, 1005, 66, 988, 630)
window.ElementPositionFix(window.MBCHC.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
window.MBCHC.COMP_HINT.style.display = "flex"
window.ElementPositionFix(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
this.COMP_HINT.style.display = "flex"
}
return(result)
})
window.MBCHC.sdk.hookFunction("DocumentKeyDown", 0, (nextargs, next) => {
let [event] = nextargs
if ("InputChat" === document.activeElement.id || "bce-message-input" === document.activeElement.id) return(next(nextargs))
if ("inline" === document.getElementById("InputChat")?.style.display && [event.altKey, event.ctrlKey, event.metaKey].every(i => !i)) window.ElementFocus("InputChat") // alt, ctrl and meta should all be false
// TODO: this is not ideal, but it will have to do for now
return(next(nextargs))
})
window.MBCHC.sdk.hookFunction("ChatRoomKeyDown", 0, (nextargs, next) => {
document.addEventListener("keydown", event => this.focus_chat(event))
this.SDK.hookFunction("ChatRoomKeyDown", 0, (nextargs, next) => { // this fires on chat input events
let [event] = nextargs
window.MBCHC.complete_hint_hide()
if ((window.KeyPress == 33) || (window.KeyPress == 34)) { // better history
@ -675,42 +635,43 @@
}
return(next(nextargs))
})
window.MBCHC.sdk.hookFunction("ChatRoomClick", 0, (nextargs, next) => {
window.MBCHC.complete_hint_hide()
return(next(nextargs))
})
window.MBCHC.remove_fbc_hook = window.MBCHC.sdk.hookFunction("MainRun", 0, (nextargs, next) => {
if (window.bce_ActivityTriggers) window.MBCHC.patch_fbc()
return(next(nextargs))
})
// Chat room handlers
window.ChatRoomRegisterMessageHandler({ Priority: -220, Description: "MBCHC preprocessor",
Callback: (data, sender, msg, metadata) => {
data.MBCHC_ID = window.MBCHC.NEXT_MESSAGE
window.MBCHC.NEXT_MESSAGE += 1
if (window.MBCHC.LOG_MESSAGES) console.debug({data, sender, msg, metadata})
data.MBCHC_ID = this.NEXT_MESSAGE
this.NEXT_MESSAGE += 1
if (this.LOG_MESSAGES) console.debug({data, sender, msg, metadata})
}
})
window.ChatRoomRegisterMessageHandler({ Priority: -219, Description: "MBCHC room enter hook",
Callback: (data, sender, msg, metadata) => { if (("Action" === data.Type) && ("ServerEnter" === data.Content) && (data.Sender === window.Player.cid)) window.MBCHC.player_enters_room() }
Callback: (data, sender, msg, metadata) => { if (("Action" === data.Type) && ("ServerEnter" === data.Content) && (data.Sender === window.Player.cid)) this.player_enters_room() }
})
window.ChatRoomRegisterMessageHandler({ Priority: -219, Description: "MBCHC specific consumer",
Callback: (data, sender, msg, metadata) => { if (("Hidden" === data.Type) && ("MBCHC" === data.Content)) return window.MBCHC.receive(data) }
Callback: (data, sender, msg, metadata) => { if (("Hidden" === data.Type) && ("MBCHC" === data.Content)) return this.receive(data) }
})
window.ChatRoomRegisterMessageHandler({ Priority: -219, Description: "MBCHC autohack lookup",
Callback: (data, sender, msg, metadata) => { if (("Hidden" === data.Type) && ("ReceiveSuitcaseMoney" === data.Content)) window.MBCHC.LAST_HACKED = data.Sender }
Callback: (data, sender, msg, metadata) => { if (("Hidden" === data.Type) && ("ReceiveSuitcaseMoney" === data.Content)) this.LAST_HACKED = data.Sender }
})
// 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, (nextargs, next) => {window.MBCHC.loader(); return(next(nextargs))})
} else {
window.MBCHC.loader()
// footer
this.LOADED = true
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 (("Online" === window.CurrentModule) && ("ChatRoom" === window.CurrentScreen)) {
window.ChatRoomCharacter.forEach(c => window.MBCHC.update_char(c))
window.MBCHC.player_enters_room()
window.ChatRoomCharacter.forEach(c => this.update_char(c))
this.player_enters_room()
}
},
preloader() {
this.SDK = window.bcModSdk.registerMod("MBCHC", this.VERSION)
this.before = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs,next) => {try {cb?.(...nextargs)} catch (x) {console.error(x)} finally {return next(nextargs)}})
this.after = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs,next) => {const result = next(nextargs); try {cb?.(...nextargs)} catch (x) {console.error(x)} finally {return result}})
if (window.CurrentModule && window.CurrentScreen && !("Character" === window.CurrentModule && "Login" === window.CurrentScreen)) return this.loader()
this.remove_load_hook = this.before("AsylumGGTSSAddItems", () => this.loader())
}
} // MBCHC
window.MBCHC.preloader()
})()