2024-07-10 01:57:42 +00:00
// ==UserScript==
// @name LILY
// @namespace https://code.fleshless.org/mute
2025-05-09 20:33:07 +02:00
// @version 0.0.3.5
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
} ) ( )