2022-07-04 22:07:57 +00:00
// ==UserScript==
// @name MBCHC-local
// @version trunk
// @description Mute's Bondage Club Hacks Collection (development version)
// @author codename.mute@proton.me
// @namespace https://code.fleshless.org/mute/
// @homepage https://code.fleshless.org/mute/MBCHC
// @updateURL https://code.fleshless.org/mute/MBCHC/raw/branch/master/mbchc-dev.user.js
// @downloadURL https://code.fleshless.org/mute/MBCHC/raw/branch/master/mbchc-dev.user.js
// @match https://bondageprojects.elementfx.com/R*
// @match https://www.bondageprojects.elementfx.com/R*
// @match https://bondage-europe.com/R*
// @match https://www.bondage-europe.com/R*
// @grant none
// ==/UserScript==
// Bondage Club Mod Development Kit (1.0.2)
// For more info see: https://github.com/Jomshir98/bondage-club-mod-sdk
/** @type {ModSDKGlobalAPI} */ // eslint-disable-next-line
var bcModSdk = function ( ) { "use strict" ; const o = "1.0.2" ; function e ( o ) { alert ( "Mod ERROR:\n" + o ) ; const e = new Error ( o ) ; throw console . error ( e ) , e } const t = new TextEncoder ; function n ( o ) { return ! ! o && "object" == typeof o && ! Array . isArray ( o ) } function r ( o ) { const e = new Set ; return o . filter ( ( o => ! e . has ( o ) && e . add ( o ) ) ) } const a = new Map , i = new Set ; function d ( o ) { i . has ( o ) || ( i . add ( o ) , console . warn ( o ) ) } function c ( o , e ) { if ( 0 === e . size ) return o ; let t = o . toString ( ) . replaceAll ( "\r\n" , "\n" ) ; for ( const [ n , r ] of e . entries ( ) ) t . includes ( n ) || d ( ` ModSDK: Patching ${ o . name } : Patch ${ n } not applied ` ) , t = t . replaceAll ( n , r ) ; return ( 0 , eval ) ( ` ( ${ t } ) ` ) } function s ( o ) { const e = [ ] , t = new Map , n = new Set ; for ( const r of u . values ( ) ) { const a = r . patching . get ( o . name ) ; if ( a ) { e . push ( ... a . hooks ) ; for ( const [ e , i ] of a . patches . entries ( ) ) t . has ( e ) && t . get ( e ) !== i && d ( ` ModSDK: Mod ' ${ r . name } ' is patching function ${ o . name } with same pattern that is already applied by different mod, but with different pattern: \n Pattern: \n ${ e } \n Patch1: \n ${ t . get ( e ) || "" } \n Patch2: \n ${ i } ` ) , t . set ( e , i ) , n . add ( r . name ) } } return e . sort ( ( ( o , e ) => e . priority - o . priority ) ) , { hooks : e , patches : t , patchesSources : n , final : c ( o . original , t ) } } function l ( o , e = ! 1 ) { let r = a . get ( o ) ; if ( r ) e && ( r . precomputed = s ( r ) ) ; else { let e = window ; const i = o . split ( "." ) ; for ( let t = 0 ; t < i . length - 1 ; t ++ ) if ( e = e [ i [ t ] ] , ! n ( e ) ) throw new Error ( ` ModSDK: Function ${ o } to be patched not found; ${ i . slice ( 0 , t + 1 ) . join ( "." ) } is not object ` ) ; const d = e [ i [ i . length - 1 ] ] ; if ( "function" != typeof d ) throw new Error ( ` ModSDK: Function ${ o } to be patched not found ` ) ; const c = function ( o ) { let e = - 1 ; for ( const n of t . encode ( o ) ) { let o = 255 & ( e ^ n ) ; for ( let e = 0 ; e < 8 ; e ++ ) o = 1 & o ? - 306674912 ^ o >>> 1 : o >>> 1 ; e = e >>> 8 ^ o } return ( ( - 1 ^ e ) >>> 0 ) . toString ( 16 ) . padStart ( 8 , "0" ) . toUpperCase ( ) } ( d . toString ( ) . replaceAll ( "\r\n" , "\n" ) ) , l = { name : o , original : d , originalHash : c } ; r = Object . assign ( Object . assign ( { } , l ) , { precomputed : s ( l ) } ) , a . set ( o , r ) , e [ i [ i . length - 1 ] ] = function ( o ) { return function ( ... e ) { const t = o . precomputed , n = t . hooks , r = t . final ; let a = 0 ; const i = d => { var c , s , l , f ; if ( a < n . length ) { const e = n [ a ] ; a ++ ; const t = null === ( s = ( c = w . errorReporterHooks ) . hookEnter ) || void 0 === s ? void 0 : s . call ( c , o . name , e . mod ) , r = e . hook ( d , i ) ; return null == t || t ( ) , r } { const n = null === ( f = ( l = w . errorReporterHooks ) . hookChainExit ) || void 0 === f ? void 0 : f . call ( l , o . name , t . patchesSources ) , a = r . apply ( this , e ) ; return null == n || n ( ) , a } } ; return i ( e ) } } ( r ) } return r } function f ( ) { const o = new Set ; for ( const e of u . values ( ) ) for ( const t of e . patching . keys ( ) ) o . add ( t ) ; for ( const e of a . keys ( ) ) o . add ( e ) ; for ( const e of o ) l ( e , ! 0 ) } function p ( ) { const o = new Map ; for ( const [ e , t ] of a ) o . set ( e , { name : e , originalHash : t . originalHash , hookedByMods : r ( t . precomputed . hooks . map ( ( o => o . mod ) ) ) , patchedByMods : Array . from ( t . precomputed . patchesSources ) } ) ; return o } const u = new Map ; function h ( o ) { u . get ( o . name ) !== o && e ( ` Failed to unload mod ' ${ o . name } ': Not registered ` ) , u . delete ( o . name ) , o . loaded = ! 1 } function g ( o , t , r ) { "string" == typeof o && o || e ( "Failed to register mod: Expected non-empty name string, got " + typeof o ) , "string" != typeof t && e ( ` Failed to register mod ' ${ o } ': Expected version string, got ${ typeof t } ` ) , r = ! 0 === r ; const a = u . get ( o ) ; a && ( a . allowReplace && r || e ( ` Refusing to load mod ' ${ o } ': it is already loaded and doesn't allow being replaced. \n Was the mod loaded multiple times? ` ) , h ( a ) ) ; const i = t => { "string" == typeof t && t || e ( ` Mod ' ${ o } ' failed to patch a function: Expected function name string, got ${ typeof t } ` ) ; let n = c . patching . get ( t ) ; return n || ( n = { hooks : [ ] , patches : new Map } , c . patching . set ( t , n ) ) , n } , d = { unload : ( ) => h ( c ) , hookFunction : ( t , n , r ) => { c . loaded || e ( ` Mod ' ${ c . name } ' attempted to call SDK function after being unloaded ` ) ; const a = i ( t ) ; "number" != typeof n && e ( ` Mod ' ${ o } ' failed to hook function ' ${ t } ': Expected priority number, got ${ typeof n } ` ) , "function" != typeof r && e ( ` Mod ' ${ o } ' failed to hook function ' ${ t } ': Expected hook function, got ${ typeof r } ` ) ; const d = { mod : c . name , priority : n , hook : r } ; return a . hooks . push ( d ) , f ( ) , ( ) => { const o = a . hooks . indexOf ( d ) ; o >= 0 && ( a . hooks . splice ( o , 1 ) , f ( ) ) } } , patchFunction : ( t , r ) => { c . loaded || e ( ` Mod ' ${ c . name } ' attempted to call SDK function after being unloaded ` ) ; const a = i ( t ) ; n ( r ) || e ( ` Mod ' ${ o } ' failed to patch function ' ${ t } ': Expected patches object, go
( function ( ) {
"use strict" ;
if ( ! window . AsylumGGTSSAddItems ) throw "AsylumGGTSSAddItems() not found, aborting MBCHC loading"
if ( window . MBCHC ) throw "MBCHC found, aborting loading"
window . MBCHC = {
VERSION : 'trunk' ,
NEXT _MESSAGE : 1 ,
LOG _MESSAGES : false ,
LOADED : false ,
AUTOHACK _ENABLED : false ,
LAST _HACKED : null ,
RE _TITLE : /^[a-zA-Z]+$/ ,
RE _PREF _ACTIVITY _ME : /^@/ ,
RE _PREF _ACTIVITY : /^@@/ ,
RE _ACT _CHARS : /^<(\d+)?:(\d+)?>/ ,
RGB _MUTE : "#6c2132" ,
RGB _POLLY : "#81b1e7" ,
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" , "Mouth" , "Nipples" , "Pussy" ] ,
HIDE _CLOTHES : [
"Cloth" , "ClothAccessory" , "Necklace" , "Suit" , "ClothLower" , "SuitLower" , "Bra" , "Corset" , "Panties" ,
"Socks" , "RightAnklet" , "LeftAnklet" , "Garters" , "Shoes" , "Hat" , "HairAccessory3" , "HairAccessory1" , "HairAccessory2" ,
"Gloves" , "Bracelet" , "Glasses" , "Mask" , "TailStraps" , "Wings"
] ,
HIDE _ITEMS : [
"ItemMisc" , "ItemEars" , "ItemHead" , "ItemNose" , "ItemHood" , "ItemAddon" , "ItemMouth" , "ItemMouth2" , "ItemMouth3" ,
"ItemArms" , "ItemNeckAccessories" , "ItemNeck" , "ItemNeckRestraints" , "ItemNipples" , "ItemNipplesPiercings" , "ItemBreast" , "ItemTorso" ,
"ItemHands" , "ItemPelvis" , "ItemVulva" , "ItemVulvaPiercings" ,
"ItemDevices" , "ItemLegs" , "ItemFeet" , "ItemBoots"
] ,
MAP _ACTIONS : { //ActivityFemale3DCG
// action
"nod|yes" : { Head : { self : "Nod" } } ,
"no" : { Head : { self : "Wiggle" } } ,
"moan" : { Mouth : { self : "MoanGag" } } ,
"mumble" : { Mouth : { self : "MoanGagTalk" } } ,
"whimper" : { Mouth : { self : "MoanGagWhimper" } } ,
"groan" : { Mouth : { self : "MoanGagGroan" } } ,
"scream" : { Mouth : { self : "MoanGagAngry" } } ,
"giggle" : { Mouth : { self : "MoanGagGiggle" } } ,
"struggle" : { Arms : { self : "StruggleArms" } } ,
"thrash" : { Legs : { self : "StruggleLegs" } } ,
// action zone
"wiggle|shake" : { "Arms,Breast,Boots,Butt,Ears,Feet,Hands,Nose,Pelvis,Torso" : { self : "Wiggle" } } ,
// action target
"whisper" : { Ears : { others : "Whisper" } } ,
"choke" : { Neck : { all : "Choke" } } ,
"brush" : { Head : { all : "TakeCare" } } ,
"french" : { Mouth : { others : "FrenchKiss" } } ,
"sit" : { Legs : { others : "Sit" } } ,
"rim" : { Butt : { others : "MasturbateTongue" } } ,
"press" : { Butt : { others : "Step" } } ,
"rest" : { Torso : { others : "Step" } } ,
"pet" : { Head : { all : "Pet" } } ,
"boop" : { Nose : { all : "Pet" } } ,
"cuddle" : { Arms : { others : "Cuddle" } } ,
"nuzzle" : { Nose : { others : "Cuddle" } } ,
"grab" : { Arms : { others : "Grope" } } ,
"clean" : { Mouth : { all : "Caress" } } ,
"lap" : { Legs : { others : "RestHead" } } ,
"lean" : { Breast : { others : "RestHead" } } ,
"peck" : { Mouth : { others : "PoliteKiss" } } ,
// action zone target
"item" : {
"Breast,Butt,Feet,Legs" : { all : "SpankItem|TickleItem|RubItem|RollItem|MasturbateItem|PourItem|ShockItem|Inject" } ,
"Nipples,Pelvis" : { all : "SpankItem|TickleItem|RubItem|RollItem|MasturbateItem|PourItem|ShockItem" } ,
Arms : { all : "SpankItem|TickleItem|RubItem|RollItem|PourItem|ShockItem|Inject" } ,
Boots : { all : "SpankItem|TickleItem|RubItem|RollItem|PourItem|ShockItem" } ,
"Ears,Mouth" : { all : "TickleItem|RubItem|RollItem" } ,
"Hood,Nose" : { all : "TickleItem|RubItem" } ,
Neck : { all : "TickleItem|RubItem|RollItem|Inject" } ,
Torso : { all : "SpankItem|TickleItem|RubItem|RollItem|PourItem|ShockItem" } ,
Vulva : { all : "SpankItem|TickleItem|RubItem|MasturbateItem|ShockItem" } ,
VulvaPiercings : { all : "SpankItem|TickleItem|RubItem|MasturbateItem|ShockItem|Inject" } ,
} ,
"kiss" : {
Mouth : { others : "Kiss|GagKiss|GaggedKiss" } ,
"Arms,Boots,Breast,Hands,Nipples" : { self : "Kiss" , others : "Kiss|GaggedKiss" } ,
"Butt,Ears,Feet,Head,Legs,Neck,Nose,Pelvis,Torso,Vulva,VulvaPiercings" : { others : "Kiss|GaggedKiss" }
} ,
"nibble|chew" : { "Arms,Hands,Boots,Mouth,Nipples" : { all : "Nibble" } , "Ears,Feet,Legs,Neck,Nose,Pelvis,Torse,Vulva,VulvaPiercings" : { others : "Nibble" } } ,
"slap|spank" : { "Head,Breast,Vulva,VulvaPiercings" : { all : "Slap" } , "Arms,Boots,Butt,Feet,Hands,Legs,Pelvis,Torso" : { all : "Spank" } } ,
"tickle" : { "Arms,Boots,Breast,Feet,Legs,Neck,Pelvis,Torso" : { all : "Tickle" } } ,
"massage" : { "Arms,Boots,Feet,Legs,Neck,Pelvis,Torso" : { all : "MassageHands" } } ,
"lick" : { "Arms,Boots,Breast,Hands,Mouth,Nipples" : { all : "Lick" } , "Ears,Feet,Legs,Neck,Nose,Pelvis,Torso,Vulva,VulvaPiercings" : { others : "Lick" } } ,
"suck" : { "Nipples,Hands,Boots" : { all : "Suck" } } ,
"bite" : { "Arms,Boots,Feet,Hands,Legs,Mouth" : { all : "Bite" } , "Breast,Butt,Ears,Head,Neck,Nipples,Nose,Torso" : { others : "Bite" } } ,
"pinch" : { "Arms,Ears,Nipples,Nose,Pelvis" : { all : "Pinch" } } ,
"clamp" : { Mouth : { all : "HandGag" } , Nose : { all : "Choke" } } ,
"step" : { "Breast,Neck,Pelvis" : { others : "Step" } } ,
"pull" : { "Head,Nose,Nipples" : { all : "Pull" } } ,
"grope" : { "Butt,Breast" : { all : "Grope" } , "Feet,Legs,Pelvis" : { others : "Grope" } } ,
"rub" : { "Head,Torso" : { others : "Rub" } , Nose : { all : "Rub" } , Legs : { self : "Wiggle" } , Hands : { self : "Caress" } } ,
"caress" : { Hands : { others : "Caress" } , "Arms,Breast,Butt,Ears,Feet,Head,Legs,Neck,Nipples,Nose,Pelvis,Torso,Vulva,VulvaPiercings" : { all : "Caress" } } ,
"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" } } ,
"pound" : { "Mouth,Vulva,Butt" : { others : "PenetrateFast" } } ,
"tongue" : { "Vulva,VulvaPiercings" : { others : "MasturbateTongue" } } ,
"finger" : { "Breast,Butt,Vulva,VulvaPiercings" : { all : "MasturbateHand" } } ,
"graze" : { "Hands,Boots" : { all : "PoliteKiss" } } ,
} ,
MAP _ZONES : {
"ItemBoots" : [ "foot" , "feet" , "boot" , "boots" , "shoe" , "shoes" , "toes" , "toenails" , "sole" , "soles" , "heel" , "heels" ] ,
"ItemFeet" : [ "leg" , "legs" , "ankle" , "ankles" ] ,
"ItemLegs" : [ "hips" , "hip" , "thighs" , "thigh" ] ,
"ItemVulva" : [ "vulva" , "pussy" ] ,
"ItemVulvaPiercings" : [ "clit" , "clitoris" ] ,
"ItemButt" : [ "butt" , "ass" ] ,
"ItemPelvis" : [ "tummy" , "pelvis" ] ,
"ItemTorso" : [ "body" , "torso" , "back" , "ribs" ] ,
"ItemBreast" : [ "breast" , "breasts" , "boob" , "boobs" , "booby" , "boobie" , "boobies" , "tit" , "tits" , "titty" , "tittie" , "titties" ] ,
"ItemNipples" : [ "nip" , "nips" , "nipple" , "nipples" ] ,
"ItemHands" : [ "hand" , "hands" , "fingers" , "fingernails" , "nails" ] ,
"ItemArms" : [ "arm" , "arms" , "elbow" , "elbows" ] ,
"ItemNeck" : [ "neck" ] ,
"ItemMouth" : [ "mouth" , "lip" , "lips" , "teeth" , "tongue" , "gag" , "cheek" , "cheeks" ] ,
"ItemNose" : [ "nose" , "nostrils" ] ,
"ItemEars" : [ "ear" , "ears" , "earlobe" , "earlobes" ] ,
"ItemHead" : [ "head" , "face" , "hair" , "eyes" ] ,
} ,
USAGE _MBCHC _LINES : [
"<div>Usage: /mbchc SUBCOMMAND [ARGS...]</div>" ,
"<div>/mbchc versions : show the mod versions across the room</div>" ,
"<div>/mbchc autohack : toggle the autohack feature</div>" ,
"<div>/mbchc disappear : become invisible (requires anal hook -> hair)</div>" ,
"<div>/mbchc donate MEMBERNUMBER : Buy data and send it to recipient</div>" ,
"<div>/mbchc title TITLE : set a custom title (<b>WIP</b>)</div>" ,
] ,
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 ) } } ,
] ,
log : function ( msg ) { return ( "MBCHC: " + String ( msg ) ) } ,
empty : function ( text ) {
if ( ! text ) return ( true )
if ( String ( text ) . trim ( ) . length < 1 ) return ( true )
return ( false )
} ,
normalise _message : function ( text , options = { } ) {
let result = text
if ( options . trim ) result = result . trim ( )
if ( options . low ) result = result . toLocaleLowerCase ( )
if ( options . up ) {
let first = result . at ( 0 ) . toLocaleUpperCase ( )
let rest = result . slice ( 1 )
result = first + rest
}
if ( options . dot && result . match ( /[\w]$/ ) ) result = ` ${ result } . `
return ( result )
} ,
inform : function ( html ) { window . ChatRoomSendLocal ( ` <div style="background: ${ this . RGB _MUTE } "> ${ html } </div> ` , 60000 ) } ,
report : function ( x ) { this . inform ( ` Error: ${ x . toString ( ) } ` ) } ,
id2char : function ( id ) { return ( window . ChatRoomCharacter . find ( c => c . MemberNumber === Number . parseInt ( id ) ) ) } ,
donate _data : function ( recipient ) {
let id = Number . parseInt ( recipient )
if ( isNaN ( id ) ) throw "empty or invalid recipient"
if ( id === window . Player . MemberNumber ) throw "recipient must not be you"
const char = this . id2char ( id )
if ( ! char ) throw "recipient not found"
if ( ! char . IsRestrained ( ) ) throw "recipient must be bound"
const cost = ( Math . random ( ) * 10 + 15 ) . toFixed ( 0 )
if ( window . Player . Money < cost ) throw "not enough money"
window . CharacterChangeMoney ( window . Player , - cost )
window . ServerSend ( "ChatRoomChat" , { Content : "ReceiveSuitcaseMoney" , Type : "Hidden" , Target : id } )
return ( { cost : cost , name : window . CharacterNickname ( char ) } )
} ,
run _activity : function ( char , ag , action ) { try {
char . FocusGroup = window . AssetGroupGet ( char . AssetFamily , ag )
if ( ! char . FocusGroup ) throw "invalid AssetGroup"
let activity = window . ActivityAllowedForGroup ( char , char . FocusGroup . Name ) . find ( a => a . Name === action )
if ( ! activity ) throw "invalid activity"
window . ActivityRun ( char , activity )
} finally {
char . FocusGroup = null
} } ,
char2dict : function ( type , id ) { return ( { Tag : ` ${ type } Character ` , MemberNumber : id , Text : window . CharacterNickname ( this . id2char ( id ) ) } ) } ,
send _activity : function ( msg ) {
let dict = [ { Tag : "MISSING PLAYER DIALOG: " , Text : "" } ]
let chars = msg . match ( this . RE _ACT _CHARS )
if ( chars ) {
msg = msg . replace ( this . RE _ACT _CHARS , "" )
if ( chars [ 1 ] ) dict . push ( this . char2dict ( "Source" , chars [ 1 ] ) )
if ( chars [ 2 ] ) dict . push ( this . char2dict ( "Target" , chars [ 2 ] ) , this . char2dict ( "Destination" , chars [ 2 ] ) )
}
window . ServerSend ( "ChatRoomChat" , { Type : "Action" , Content : msg , Dictionary : dict } )
} ,
receive : function ( data ) {
if ( data . Sender === window . Player . MemberNumber ) return // this is our own message, sent back to us
let char = this . id2char ( data . Sender )
if ( ! char ) throw ` Invalid message sender: ${ data . Sender } `
let payload = data . Dictionary [ 0 ]
if ( ! payload ) throw "Empty message"
switch ( payload . type ) {
case "greetings" : case "hello" :
char . MBCHC = payload . value
if ( "greetings" === payload . type ) this . hello ( char . MemberNumber )
break
default : // if we don't know the type it may be from a newer version
}
} ,
hello : function ( target = null ) {
let payload = { type : "greetings" , value : window . Player . MBCHC }
if ( target ) payload . type = "hello"
let message = { Content : "MBCHC" , Type : "Hidden" , Dictionary : [ payload ] }
if ( target ) message . Target = target
window . ServerSend ( "ChatRoomChat" , message )
} ,
disappear : function ( ) {
let item = window . InventoryGet ( window . Player , "ItemButt" )
if ( ! item || ! item . Asset || ! item . Asset . Name ) throw "butt seems empty"
if ( item . Asset . Name !== "AnalHook" ) throw "butt seems occupied by something other than the anal hook"
if ( ! item . Property . Type || item . Property . Type !== "Hair" ) throw "anal hook seems not tied to hair"
item . Property = { Type : "Hair" , Hide : this . HIDE _ALL }
window . CharacterRefresh ( window . Player , true , true )
} ,
title : function ( args ) {
let title = args . shift ( )
if ( this . empty ( title ) ) throw "empty title"
title = this . normalise _message ( title , { trim : true , up : true , low : true } )
if ( title . length > 16 ) throw "title too long"
if ( ! title . match ( this . RE _TITLE ) ) throw "invalid title"
window . TitleSet ( title )
// TODO: this needs much more work. at least don't push a second title
// we need to patch the text cache
// we need to check for other players' custom titles
//window.TitleList.push({Name: title, Requirement: () => {return true}})
} ,
patch _handheld : function ( ) {
let options = InventoryItemHandsSpankingToysOptions /* eslint-disable-line no-undef */ // window.InventoryItemHandsSpankingToysOptions is undefined
for ( let i in this . HAND _PENETRATORS ) {
let option = options . find ( o => o . Name === this . HAND _PENETRATORS [ i ] )
if ( option && option . Property ) {
if ( ! option . Property . Attribute ) option . Property . Attribute = [ ]
if ( option . Property . Attribute . indexOf ( "PenetrateItem" ) < 0 ) option . Property . Attribute . push ( "PenetrateItem" )
}
}
} ,
gather _versions : function ( ) {
let result = [ ]
for ( let i in window . ChatRoomCharacter ) {
let c = window . ChatRoomCharacter [ i ]
if ( c . MBCHC ) result . push ( { name : window . CharacterNickname ( c ) , id : c . MemberNumber , version : c . MBCHC . VERSION } )
}
return ( result )
} ,
2022-07-04 22:19:38 +00:00
need _load _hook : function ( module , screen ) {
if ( ! module || ! screen ) return ( true )
if ( ( "Character" === module ) && ( "Login" === screen ) ) return ( true )
return ( false )
} ,
2022-07-04 22:07:57 +00:00
// Command actions
command _mbchc : function ( argline , cmdline , args ) { try {
if ( args . length < 1 ) return ( this . inform ( this . USAGE _MBCHC _LINES . join ( "" ) ) )
let cmd = String ( args . shift ( ) ) . toLocaleLowerCase ( )
switch ( cmd ) {
case "versions" : this . inform ( this . gather _versions ( ) . map ( c => { return ( ` <div><b> ${ c . name } </b> ( ${ c . id } ): ${ c . version } </div> ` ) } ) . join ( "" ) ) ; break
case "disappear" : this . disappear ( ) ; break
case "title" : this . title ( args ) ; break
case "autohack" :
this . AUTOHACK _ENABLED = ! this . AUTOHACK _ENABLED
this . inform ( ` Autohack is now ${ this . AUTOHACK _ENABLED ? "enabled" : "disabled" } ` )
break
case "donate" :
var result = this . donate _data ( args [ 0 ] )
window . ChatRoomMessage ( { Sender : window . Player . MemberNumber , Type : "Action" , Content : ` You've bought data for $ ${ result . cost } and sent it to ${ result . name } . ` , Dictionary : [ { Tag : "MISSING PLAYER DIALOG: " , Text : "" } ] } )
break
default : throw ` unknown subcommand " ${ cmd } " `
}
} 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 ) {
}
let char = window . Player
if ( "-" !== args [ 0 ] ) {
let id = Number . parseInt ( args [ 0 ] )
let char = this . id2char ( id )
}
if ( ! char ) throw "invalid member number"
if ( args . length < 2 ) {
}
if ( args . length < 3 ) {
}
let ag = args [ 1 ]
let action = args [ 2 ]
this . run _activity ( char , ag , action )
} catch ( x ) { this . report ( x ) } } ,
loader : function ( ) {
if ( this . LOADED ) return //FIXME: unhook if hooked, no need to call this every time
// Calculated values
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 ` )
this . PREF _ACTIVITY = ` ${ this . CommandsKey } activity `
// Actions
this . patch _handheld ( )
window . Player . MBCHC = { VERSION : this . VERSION }
window . CommandCombine ( this . COMMANDS )
this . LOADED = true
console . info ( this . log ( ` loaded version ${ this . VERSION } ` ) )
}
} // MBCHC
// Hooks
window . MBCHC . sdk = window . bcModSdk . registerMod ( "MBCHC" , window . MBCHC . VERSION )
window . MBCHC . sdk . hookFunction ( "ChatRoomMessageInvolvesPlayer" , 0 , ( args , next ) => {
let data = args [ 0 ]
if ( ! data . MBCHC _ID ) {
data . MBCHC _ID = window . MBCHC . NEXT _MESSAGE
window . MBCHC . NEXT _MESSAGE += 1
if ( window . MBCHC . LOG _MESSAGES ) console . debug ( data )
if ( ( "ReceiveSuitcaseMoney" === data . Content ) && ( "Hidden" === data . Type ) ) { window . MBCHC . LAST _HACKED = data . Sender }
if ( ( "ServerEnter" === data . Content ) && ( "Action" === data . Type ) && ( data . Sender === window . Player . MemberNumber ) ) { window . MBCHC . hello ( ) }
if ( ( "MBCHC" === data . Content ) && ( "Hidden" === data . Type ) ) { window . MBCHC . receive ( data ) }
}
return ( next ( args ) )
} )
window . MBCHC . sdk . hookFunction ( "ChatRoomReceiveSuitcaseMoney" , 0 , ( args , next ) => {
let result = next ( args )
if ( window . MBCHC . AUTOHACK _ENABLED && window . MBCHC . LAST _HACKED ) {
window . CurrentCharacter = { MemberNumber : window . MBCHC . LAST _HACKED }
window . MBCHC . LAST _HACKED = null
window . ChatRoomTryToTakeSuitcase ( )
}
return ( result )
} )
window . MBCHC . sdk . hookFunction ( "ChatRoomSendChat" , 0 , ( args , 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 . MemberNumber } :>SourceCharacter ` )
}
window . ElementValue ( "InputChat" , input )
return ( next ( args ) )
} )
window . MBCHC . sdk . hookFunction ( "ChatRoomDrawCharacterOverlay" , 0 , ( args , next ) => {
let [ C , CharX , CharY , Zoom , Pos ] = args
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 + 225 * Zoom , CharY , 50 * Zoom , 50 * Zoom , colour )
}
return ( next ( args ) )
} )
2022-07-04 22:19:38 +00:00
// MAIN SCREEN TURN ON
if ( window . MBCHC . need _load _hook ( window . CurrentModule , window . CurrentScreen ) ) {
window . MBCHC . sdk . hookFunction ( "AsylumGGTSSAddItems" , 0 , ( args , next ) => { window . MBCHC . loader ( ) ; return ( next ( args ) ) } )
} else {
window . MBCHC . loader ( )
2022-07-04 22:22:47 +00:00
if ( ( "Online" === window . CurrentModule ) && ( "ChatRoom" === window . CurrentScreen ) ) window . MBCHC . hello ( )
2022-07-04 22:19:38 +00:00
}
2022-07-04 22:07:57 +00:00
} ) ( )