2024-07-10 01:57:42 +00:00
// ==UserScript==
// @name LILY
// @namespace https://code.fleshless.org/mute
// @version 0.0.1
// @description Lily's Integrated Logic Yoke
// @grant none
// @author Mute
// @require https://code.fleshless.org/mute/LILY/raw/branch/mistress/bcmodsdk-1.2.0.mjs
// @match https://bondageprojects.elementfx.com/*
// @match https://www.bondageprojects.elementfx.com/*
// @match https://bondage-europe.com/*
// @match https://www.bondage-europe.com/*
// @match http://localhost:*/*
// ==/UserScript==
2024-07-10 02:23:08 +00:00
// checklist:
2024-07-10 23:08:50 +00:00
// [?] account filter, should only work on Lily 119643
// [?] voicebox
2024-07-10 01:57:42 +00:00
2024-07-10 23:08:50 +00:00
( function ( ) {
2024-07-10 01:57:42 +00:00
'use strict' ;
const W = /** @type {Window & typeof globalThis & {bcModSdk: import('bondage-club-mod-sdk').ModSDKGlobalAPI}} */ ( window )
const SDK = W . bcModSdk . registerMod ( { name : GM _info . script . name , fullName : GM _info . script . description || '' , version : GM _info . script . version , repository : 'finger://your.mom' } )
2024-07-10 23:08:50 +00:00
/** @type {LILY.Utils} */ const U = { remove _hook : undefined ,
true ( f ) { f ( ) ; return true } ,
2024-07-10 01:57:42 +00:00
with : ( v , f ) => f ( v ) ,
2024-07-10 23:08:50 +00:00
send : f => Boolean ( f ( ) ) && ! void W . ChatRoomCharacterUpdate ( W . Player ) ,
2024-07-10 01:57:42 +00:00
}
/** @type {LILY.Box} */ const Box = {
2024-07-10 23:08:50 +00:00
enable ( item ) { if ( ! item ) return false
const op = item . Property ? . OverridePriority
if ( op && typeof op === 'object' ) op . Light = 41 // todo Okay, we need to be more clever here. This should be above the box itself.
return true
} ,
disable ( item ) { if ( ! item ) return false
item . Property || = { }
const ip = item . Property
if ( typeof ip . OverridePriority === 'number' ) ip . OverridePriority = { Lock : ip . OverridePriority , Unit : ip . OverridePriority }
ip . OverridePriority || = { }
ip . OverridePriority . Light = 0
return true
} ,
on : ( ) => U . with ( Box . item , i => U . send ( ( ) => Box . enable ( i ) ) ) ,
off : ( ) => U . with ( Box . item , i => U . send ( ( ) => Box . disable ( i ) ) ) ,
get item ( ) { return U . with ( W . InventoryGet ( W . Player , 'ItemNeckAccessories' ) , item => item ? . Craft ? . Name === 'Lily\'s voicebox' ? item : undefined ) } ,
get enabled ( ) { return U . with ( Box . item , i => Boolean ( i ) && U . with ( i ? . Property ? . OverridePriority , op => typeof op !== 'object' || ! Object . hasOwn ( op , 'Light' ) || op . Light > 0 ) ) } , // todo check if the light is above the box, the neck and the collar
2024-07-10 01:57:42 +00:00
}
2024-07-10 23:08:50 +00:00
/** @type {LILY.Eyes} */ const Eyes = { timeout : undefined , zones : [ 'ItemHead' , 'Mask' ] ,
2024-07-10 01:57:42 +00:00
rgb : [
'#990000' , '#990f00' , '#991f00' , '#992e00' , '#993d00' , '#994d00' , '#995c00' , '#996b00' , '#997a00' , '#998a00' ,
'#999900' , '#8a9900' , '#7a9900' , '#6b9900' , '#5c9900' , '#4d9900' , '#3d9900' , '#2e9900' , '#1f9900' , '#0f9900' ,
'#009900' , '#00990f' , '#00991f' , '#00992e' , '#00993d' , '#00994d' , '#00995c' , '#00996b' , '#00997a' , '#00998a' ,
'#009999' , '#008a99' , '#007a99' , '#006b99' , '#005c99' , '#004d99' , '#003d99' , '#002e99' , '#001f99' , '#000f99' ,
'#000099' , '#0f0099' , '#1f0099' , '#2e0099' , '#3d0099' , '#4d0099' , '#5c0099' , '#6b0099' , '#7a0099' , '#8a0099' ,
'#990099' , '#99008a' , '#99007a' , '#99006b' , '#99005c' , '#99004d' , '#99003d' , '#99002e' , '#99001f' , '#99000f' ,
] ,
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-10 23:08:50 +00:00
set _rgb : ( item , n ) => U . true ( ( ) => item . Color = [ Eyes . rgb [ n ] , 'Default' , Eyes . rgb [ n ] , 'Default' ] ) ,
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 ) ) ) ,
roll _rgb : ( ) => Eyes . next _rgb ( ) && W . ChatRoomCharacterItemUpdate ( W . Player , 'Mask' ) ,
start : ( ) => Boolean ( Eyes . check ( 'Mask' ) ) && ! Eyes . timeout && Boolean ( Eyes . timeout = setInterval ( Eyes . roll _rgb , 1000 ) ) ,
stop : ( ) => Boolean ( Eyes . check ( 'Mask' ) ) && Boolean ( Eyes . timeout ) && ! ( Eyes . timeout = void clearInterval ( Eyes . timeout ) ) ,
dim : ( ) => U . send ( ( ) => ! Eyes . check ( 'ItemHead' ) && ! void W . InventoryWear ( W . Player , 'AnimeLenses' , 'ItemHead' , [ '#FFFFFF' , 'Default' , '#FFFFFF' , 'Default' ] , undefined , 71_240 , Eyes . craft ) ) ,
clear : ( ) => U . send ( ( ) => Boolean ( Eyes . check ( 'ItemHead' ) ) && ! void W . InventoryRemove ( W . Player , 'ItemHead' ) ) ,
check : zone => U . with ( Eyes . item , item => ( item && item . Asset . Group . Name === zone ) ? item : undefined ) ,
get item ( ) { return Eyes . zones . map ( z => W . InventoryGet ( W . Player , z ) ) . find ( item => item && item . Asset . Name === 'AnimeLenses' ) || undefined } ,
2024-07-10 01:57:42 +00:00
}
2024-07-10 23:08:50 +00:00
/** @type {LILY.Belt} */ const Belt = { users : [ 119_643 , 154_662 ] , admins : [ 71_240 , 67_994 ] ,
2024-07-10 01:58:29 +00:00
cli : { '#LILY' : {
2024-07-10 01:57:42 +00:00
status : _ => '200 OK' ,
2024-07-10 23:08:50 +00:00
box : { on : Box . on , off : Box . off } ,
eyes : { start : Eyes . start , stop : Eyes . stop , dim : Eyes . dim , clear : Eyes . clear } ,
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
while ( t = tokens . shift ( ) ) if ( U . with ( cmd [ t ] , next => ! next || Belt . is _cb ( cmd = next ) ) ) break // eslint-disable-line no-cond-assign
if ( ! Belt . is _cb ( cmd ) ) return t ? ` unknown token ${ t } ` : ` subcommands: [ ${ Object . keys ( cmd ) . join ( ', ' ) } ] `
2024-07-10 01:57:42 +00:00
return U . with ( cmd ( ... tokens ) , r => typeof r === 'boolean' ? ( r ? 'success' : 'fail' ) : String ( r ) )
} ,
receive ( data ) { if ( ! data . Sender ) return undefined
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-10 23:08:50 +00:00
const load _after _login = ( ) => {
if ( U . remove _hook ) { U . remove _hook ? . ( ) ; U . remove _hook = undefined }
if ( ! W . Player . MemberNumber ) return console . debug ( 'LILY: member not identified' )
if ( ! Belt . users . includes ( W . Player . MemberNumber ) ) return console . debug ( 'LILY: member not eligible for loading' )
SDK . hookFunction ( 'SpeechTransformGagGarble' , 0 , ( [ text ] ) => text ) // disable garbling
SDK . hookFunction ( 'ChatRoomSendChatMessage' , 0 , ( na , n ) => Box . enabled || na [ 0 ] . startsWith ( '(' ) ? n ( na ) : Boolean ( 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' ) && ( Belt . receive ( data ) || true ) } )
console . debug ( ` LILY: eligible member detected, loaded version ${ GM _info . script . version } ` )
}
U . remove _hook = SDK . hookFunction ( 'AsylumGGTSSAddItems' , 0 , ( na , n ) => U . true ( load _after _login ) && n ( na ) )
2024-07-10 01:57:42 +00:00
} ) ( )