LILY/lily.user.js

110 lines
7.9 KiB
JavaScript
Raw Normal View History

2024-07-10 01:57:42 +00:00
// ==UserScript==
// @name LILY
// @namespace https://code.fleshless.org/mute
// @version 0.0.3
2024-07-10 01:57:42 +00:00
// @description Lily's Integrated Logic Yoke
// @grant none
// @author Mute
// @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';
const $ = undefined
2024-07-10 01:57:42 +00:00
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'})
/** @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),
ass: (v, f) => v === $ || v === null ? $ : U.with(f(v), r => r === null ? $ : r), //eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
send: f => f() && U.true(() => void W.ChatRoomCharacterUpdate(W.Player)),
2024-07-10 01:57:42 +00:00
}
/** @type {LILY.Box} */ const Box = {
enable(item) { if (item === $) return false // TODO Okay, we need to be more clever here. The light should be above the box itself
return U.with(item.Property, ip => typeof ip?.OverridePriority === 'object' && (ip.OverridePriority['Light'] = 41) > 0)
2024-07-10 23:08:50 +00:00
},
disable(item) { return item === $ ? false : U.with(item.Property ??= {}, ip => U.with(ip.OverridePriority ??= {}, op =>
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))),
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'},
set_rgb: (item, n) => U.true(() => item.Color = [Eyes.rgb[n] ?? 'Default', 'Default', Eyes.rgb[n] ?? 'Default', '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))),
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)),
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);}},
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],
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
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))
},
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
},
}
/** @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 = () => {
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')
SDK.hookFunction('SpeechTransformGagGarbleIntensity', 0, _ => 0) // disable garbling
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}`)
}
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
})()