2024-07-10 01:57:42 +00:00
// ==UserScript==
// @name LILY
// @namespace https://code.fleshless.org/mute
2025-04-13 14:18:54 +00:00
// @version 0.0.3
2024-07-10 01:57:42 +00:00
// @description Lily's Integrated Logic Yoke
// @grant none
// @author Mute
2024-09-18 20:32:34 +00:00
// @require https://jomshir98.github.io/bondage-club-mod-sdk/bcmodsdk.js
// @match https://*.bondageprojects.elementfx.com/R*/*/
// @match https://*.bondage-europe.com/R*/*/
2025-04-13 14:33:36 +00:00
// @match https://*.bondageprojects.com/R*/
2025-03-15 19:06:48 +00:00
// @match http://localhost:*/*
2024-07-10 01:57:42 +00:00
// ==/UserScript==
2024-07-10 23:08:50 +00:00
( function () {
2024-07-10 01:57:42 +00:00
'use strict' ;
2025-04-13 14:18:54 +00:00
const $ = undefined
2024-07-10 01:57:42 +00:00
const W = /** @type {Window & typeof globalThis & {bcModSdk: import('bondage-club-mod-sdk').ModSDKGlobalAPI}} */ ( window )
2024-07-12 00:58:51 +00:00
const SDK = W . bcModSdk . registerMod ({ name : GM_info . script . name , fullName : GM_info . script . description ?? '' , version : GM_info . script . version , repository : 'finger://your.mom' })
2025-04-13 14:18:54 +00:00
/** @type {LILY.Utils} */ const U = { remove_hook : $ ,
2024-07-10 23:08:50 +00:00
true ( f ) { f (); return true },
2024-07-10 01:57:42 +00:00
with : ( v , f ) => f ( v ),
2025-04-13 14:18:54 +00:00
ass : ( v , f ) => v === $ || v === null ? $ : U . with ( f ( v ), r => r === null ? $ : r ), //eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
2024-07-12 00:58:51 +00:00
send : f => f () && U . true (() => void W . ChatRoomCharacterUpdate ( W . Player )),
2024-07-10 01:57:42 +00:00
}
/** @type {LILY.Box} */ const Box = {
2025-04-13 14:18:54 +00:00
enable ( item ) { if ( item === $ ) return false // TODO Okay, we need to be more clever here. The light should be above the box itself
2024-07-12 00:58:51 +00:00
return U . with ( item . Property , ip => typeof ip ? . OverridePriority === 'object' && ( ip . OverridePriority [ 'Light' ] = 41 ) > 0 )
2024-07-10 23:08:50 +00:00
},
2025-04-13 14:18:54 +00:00
disable ( item ) { return item === $ ? false : U . with ( item . Property ??= {}, ip => U . with ( ip . OverridePriority ??= {}, op =>
2024-07-12 00:58:51 +00:00
typeof op === 'number' ? Boolean ( ip . OverridePriority = { Lock : op , Unit : op }) : (( op [ 'Light' ] = 0 ) < 1 )
))},
2024-07-10 23:08:50 +00:00
on : () => U . with ( Box . item , i => U . send (() => Box . enable ( i ))),
off : () => U . with ( Box . item , i => U . send (() => Box . disable ( i ))),
2025-04-13 14:18:54 +00:00
get item () { return U . ass ( W . Player , p => U . ass ( Belt . users [ p . MemberNumber ?? - 1 ], u => U . ass ( W . InventoryGet ( p , u . zone ), i => i . Craft ? . Name === u . name ? i : $ )))},
get enabled () { return U . with ( Box . item , i => i !== $ && U . with ( i ? . Property ? . OverridePriority , op => typeof op !== 'object' || ! Object . hasOwn ( op , 'Light' ) || ( op [ 'Light' ] ?? 1 ) > 0 ))}, // TODO check if the light is above the box
2024-07-10 01:57:42 +00:00
}
2025-05-09 20:29:43 +02:00
/** @type {LILY.Eyes} */ const Eyes = { timeout : $ , speed : 1500 , zones : [ 'Eyes' , 'Eyes2' ],
2024-07-10 01:57:42 +00:00
rgb : [
2025-05-09 20:29:43 +02:00
'#ff4d4d' , '#ff714d' , '#ff944d' , '#ffb84d' , '#ffe04d' , '#e4ff4d' , '#c0ff4d' , '#9cff4d' , '#78ff4d' , '#55ff4d' ,
'#4dff62' , '#4dff85' , '#4dffa8' , '#4dffcc' , '#4dfff0' , '#4ddcff' , '#4db8ff' , '#4d94ff' , '#4d70ff' , '#4d4dff' ,
'#704dff' , '#944dff' , '#b84dff' , '#dc4dff' , '#ff4df0' , '#ff4dcc' , '#ff4da8' , '#ff4d85' , '#ff4d62' , '#ff4d4d' ,
'#ff6a4d' , '#ff874d' , '#ffa44d' , '#ffc14d' , '#ffde4d' , '#e3ff4d' , '#c6ff4d' , '#a9ff4d' , '#8cff4d' , '#6fff4d' ,
'#4dff4d' , '#4dff6f' , '#4dff8c' , '#4dffaa' , '#4dffc7' , '#4dffe4' , '#4dffff' , '#4ddcff' , '#4dbfff' , '#4da3ff' ,
'#4d86ff' , '#4d6aff' , '#4d4dff' , '#6a4dff' , '#864dff' , '#a34dff' , '#bf4dff' , '#dc4dff' , '#ff4dff' , '#ff4ddc' ,
'#ff4dbf' , '#ff4da3' , '#ff4d86' , '#ff4d6a' , '#ff4d4d' , '#ff6e4d' , '#ff914d' , '#ffb44d' , '#ffd84d' , '#f1ff4d' ,
'#ceff4d' , '#abff4d' , '#88ff4d' , '#65ff4d' , '#4dff5b' , '#4dff7e' , '#4dffa1' , '#4dffc4' , '#4dffe7' , '#4dffff' ,
'#4dcfff' , '#4dacff' , '#4d89ff' , '#4d66ff' , '#4d4dff' , '#664dff' , '#894dff' , '#ac4dff' , '#cf4dff' , '#f24dff' ,
'#ff4dce' , '#ff4dab' , '#ff4d88' , '#ff4d65' , '#ff4d4d' , '#ff774d' , '#ff9f4d' , '#ffc74d' , '#fff04d' , '#d4ff4d' ,
'#acff4d' , '#84ff4d' , '#5cff4d' , '#4dff74' , '#4dff9c' , '#4dffc4' , '#4dffee' , '#4dbdff' , '#4d95ff' , '#4d6dff' ,
'#6d4dff' , '#954dff' , '#bd4dff' , '#e54dff' , '#ff4dd5' , '#ff4dad' , '#ff4d85' , '#ff4d5d'
2024-07-10 01:57:42 +00:00
],
craft : { Item : 'AnimeLenses' , Name : 'Akihabara souvenir' , Description : 'you will never be the same uwu' , Color : '#FFFFFF,Default,#FFFFFF,Default' , Property : 'Thick' , Lock : '' , Private : true , ItemProperty : {}, Type : null , TypeRecord : null , MemberNumber : 71_240 , MemberName : 'Mute' },
2024-07-12 00:58:51 +00:00
set_rgb : ( item , n ) => U . true (() => item . Color = [ Eyes . rgb [ n ] ?? 'Default' , 'Default' , Eyes . rgb [ n ] ?? 'Default' , 'Default' ]),
2025-04-13 14:18:54 +00:00
next_rgb : () => Eyes . check ( 'ItemHead' ) === $ && U . with ( Eyes . check ( 'Mask' ), i => i !== $ && U . with ( Eyes . rgb . indexOf ( i . Color ? .[ 0 ] ?? Eyes . rgb [ 0 ] ?? '' ) + 1 , n => Eyes . set_rgb ( i , n ))),
2024-07-12 01:21:46 +00:00
roll_rgb : () => Eyes . next_rgb () && void W . ChatRoomCharacterItemUpdate ( W . Player , 'Mask' ),
2025-05-09 20:29:43 +02:00
start : () => Eyes . check ( 'Mask' ) !== $ && Eyes . timeout === $ && Boolean ( Eyes . timeout = setInterval ( Eyes . roll_rgb , Eyes . speed )),
2025-04-13 14:18:54 +00:00
stop : () => Eyes . check ( 'Mask' ) !== $ && Eyes . timeout !== $ && U . true (() => Eyes . timeout = void clearInterval ( Eyes . timeout )),
2025-05-09 20:29:43 +02:00
set_speed ( seconds ) { const ms = Math . max ( 100 , seconds * 1000 ); Eyes . speed = ms ; if ( Eyes . timeout !== $ ) { clearInterval ( Eyes . timeout ); Eyes . timeout = setInterval ( Eyes . roll_rgb , Eyes . speed );}},
2025-04-13 14:18:54 +00:00
dim : () => U . send (() => Eyes . check ( 'ItemHead' ) === $ && U . true (() => W . InventoryWear ( W . Player , 'AnimeLenses' , 'ItemHead' , [ '#FFFFFF' , 'Default' , '#FFFFFF' , 'Default' ], $ , 71_240 , Eyes . craft ))),
clear : () => U . send (() => Eyes . check ( 'ItemHead' ) !== $ && U . true (() => void W . InventoryRemove ( W . Player , 'ItemHead' ))),
check : zone => U . with ( Eyes . item , item => item ? . Asset . Group . Name === zone ? item : $ ),
get item () { return Eyes . zones . map ( z => W . InventoryGet ( W . Player , z )). find ( item => item ? . Asset . Name === 'AnimeLenses' ) ?? $ },
2024-07-10 01:57:42 +00:00
}
2025-05-09 20:29:43 +02:00
/** @type {LILY.Belt} */ const Belt = { admins : [ 71_240 , 67_994 , 21_504 ],
2025-04-13 14:18:54 +00:00
users : {
119_643 : { zone : 'ItemNeckAccessories' , name : 'Lily\'s voicebox' },
//154_662: '?hell if I remember?',
62_808 : { zone : 'ItemNeck' , name : 'CKO voicebox' },
},
2024-07-10 01:58:29 +00:00
cli : { '#LILY' : {
2025-05-09 20:29:43 +02:00
status : _ => '200 OK[User is currently operating in her own Beta Branch]' ,
2024-07-10 23:08:50 +00:00
box : { on : Box . on , off : Box . off },
2025-05-09 20:29:43 +02:00
version : _ => `LILY version ${ GM_info ? . script ? . version ?? 'unknown' } ` ,
eyes : {
start : Eyes . start ,
stop : Eyes . stop ,
dim : Eyes . dim ,
clear : Eyes . clear ,
speed ( s ) {
const seconds = parseFloat ( s );
if ( isNaN ( seconds ) || seconds <= 0 ) return 'invalid speed' ;
Eyes . set_speed ( seconds );
return `Eye speed set to ${ seconds } s` ;
}
},
2024-07-10 01:57:42 +00:00
}},
/** @type {LILY.Belt['is_cb']} */ is_cb : s => typeof s === 'function' ,
2024-07-10 23:08:50 +00:00
run ( tokens ) { /** @type {string | undefined} */ let t , /** @type {LILY.Commands | LILY.CommandCB} */ cmd = Belt . cli
2025-04-13 14:18:54 +00:00
while (( t = tokens . shift ()) !== $ ) if ( U . with ( cmd [ t ], next => next !== $ && Belt . is_cb ( cmd = next ))) break // eslint-disable-line @typescript-eslint/no-loop-func
if ( ! Belt . is_cb ( cmd )) return t === $ ? `subcommands: [ ${ Object . keys ( cmd ). join ( ', ' ) } ]` : `unknown token ${ t } `
2024-07-10 01:57:42 +00:00
return U . with ( cmd (... tokens ), r => typeof r === 'boolean' ? ( r ? 'success' : 'fail' ) : String ( r ))
},
2025-04-13 14:18:54 +00:00
receive ( data ) { if ( data . Sender === $ ) return $
2024-07-10 02:23:08 +00:00
W . ChatRoomSendLocal ( `<span style="color: #666"> ${ data . Content } </span>` )
2024-07-10 23:08:50 +00:00
return void W . ChatRoomSendWhisper ( data . Sender , `LILY: ${ Belt . admins . includes ( data . Sender ) ? Belt . run ( data . Content . split ( ' ' )) : 'access denied' } ` )
2024-07-10 01:57:42 +00:00
},
}
2024-07-12 00:58:51 +00:00
/** @type {(text: string) => undefined} */ const unload = text => U . true ( SDK . unload ) && void console . debug ( `LILY: ${ text } , unloading the mod` )
2024-07-10 23:08:50 +00:00
const load_after_login = () => {
2025-04-13 14:18:54 +00:00
if ( U . remove_hook !== $ ) { U . remove_hook ? .(); U . remove_hook = $ }
if ( W . Player . MemberNumber === $ ) return void unload ( 'member not identified' )
if ( ! Object . hasOwn ( Belt . users , W . Player . MemberNumber )) return void unload ( 'member not eligible for loading' )
2024-09-18 20:32:34 +00:00
SDK . hookFunction ( 'SpeechTransformGagGarbleIntensity' , 0 , _ => 0 ) // disable garbling
2024-07-12 00:58:51 +00:00
SDK . hookFunction ( 'ChatRoomSendChatMessage' , 0 , ( na , n ) => Box . enabled || na [ 0 ]. startsWith ( '(' ) ? n ( na ) : ! U . true (() => void W . ChatRoomSendLocal ( '<span style="color: red">You try to talk, but nothing comes out.</span>' )))
W . ChatRoomRegisterMessageHandler ({ Priority : - 169 , Description : 'LILY' , Callback : data => data . Type === 'Whisper' && data . Content . startsWith ( '#LILY' ) && U . true (() => void Belt . receive ( data ))})
2024-07-10 23:08:50 +00:00
console . debug ( `LILY: eligible member detected, loaded version ${ GM_info . script . version } ` )
}
2024-07-12 00:58:51 +00:00
U . remove_hook = SDK . hookFunction ( 'AsylumGGTSSAddItems' , 0 , ( na , n ) => U . true ( load_after_login ) && void n ( na ))
2024-07-10 01:57:42 +00:00
})()