diff --git a/mbchc-dev.user.js b/mbchc-dev.user.js index 93b1d3f..05db5af 100644 --- a/mbchc-dev.user.js +++ b/mbchc-dev.user.js @@ -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,138 +577,101 @@ document.head.appendChild(css) // Actions this.calculate_maps() - this.patch_handheld() window.Player.MBCHC = {VERSION: this.VERSION} window.CommandCombine(COMMANDS) + + // Hooks + 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() + } + }) + this.before("ChatRoomSendChat", () => { + let input = window.ElementValue("InputChat") + if (!input.startsWith("@@@") && input.startsWith("@")) { + 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) + } + }) + this.after("ChatRoomSendChat", () => { + const history = window.ChatRoomLastMessage + if ((history.length > 1) && (history[history.length - 1] === history[history.length - 2])) {history.pop(); window.ChatRoomLastMessageIndex -= 1} + }) + 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") + } + }) + 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(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200) + this.COMP_HINT.style.display = "flex" + } + }) + 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 + event.preventDefault() + return(window.MBCHC.history(window.KeyPress - 33)) + } + if (window.MBCHC.HISTORY_MODE) { + window.ChatRoomLastMessage.pop() + window.MBCHC.HISTORY_MODE = false + } + return(next(nextargs)) + }) + + // Chat room handlers + window.ChatRoomRegisterMessageHandler({ Priority: -220, Description: "MBCHC preprocessor", + Callback: (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)) 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 this.receive(data) } + }) + window.ChatRoomRegisterMessageHandler({ Priority: -219, Description: "MBCHC autohack lookup", + Callback: (data, sender, msg, metadata) => { if (("Hidden" === data.Type) && ("ReceiveSuitcaseMoney" === data.Content)) this.LAST_HACKED = data.Sender } + }) + + // footer 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 + 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 => 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 - // 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 - window.ChatRoomTryToTakeSuitcase() - } - return(result) - }) - window.MBCHC.sdk.hookFunction("ChatRoomSendChat", 0, (nextargs, next) => { - 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) - window.ElementValue("InputChat", input) - } - let result = next(nextargs) - let 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) - } - 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 - 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" - } - 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) => { - let [event] = nextargs - window.MBCHC.complete_hint_hide() - if ((window.KeyPress == 33) || (window.KeyPress == 34)) { // better history - event.preventDefault() - return(window.MBCHC.history(window.KeyPress - 33)) - } - if (window.MBCHC.HISTORY_MODE) { - window.ChatRoomLastMessage.pop() - window.MBCHC.HISTORY_MODE = false - } - 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}) - } - }) - 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() } - }) - 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) } - }) - 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 } - }) - - // 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() - if (("Online" === window.CurrentModule) && ("ChatRoom" === window.CurrentScreen)) { - window.ChatRoomCharacter.forEach(c => window.MBCHC.update_char(c)) - window.MBCHC.player_enters_room() - } - } + window.MBCHC.preloader() })()