voice box hopefully done
This commit is contained in:
parent
5a62ce3274
commit
1130b04947
12
ambient.d.ts
vendored
12
ambient.d.ts
vendored
|
@ -6,13 +6,18 @@
|
|||
|
||||
declare namespace LILY {
|
||||
interface Utils {
|
||||
remove_hook: undefined | (() => void)
|
||||
true(callback: () => unknown): true // stops elsint from complainting about constant bool expressions
|
||||
with<V, R>(value: V, callback: (v: V) => R): R // A silly helper to kinda curry values
|
||||
send(callback: () => unknown): boolean // updates the player if the function succeeds
|
||||
}
|
||||
|
||||
interface Box {
|
||||
enable(): void
|
||||
disable(): void
|
||||
get equipped(): boolean
|
||||
enable(item: Item | undefined): boolean
|
||||
disable(item: Item | undefined): boolean
|
||||
on(): boolean
|
||||
off(): boolean
|
||||
get item(): Item | undefined
|
||||
get enabled(): boolean
|
||||
}
|
||||
|
||||
|
@ -37,6 +42,7 @@ declare namespace LILY {
|
|||
interface Commands extends Command<Commands> {}
|
||||
interface Belt {
|
||||
admins: number[]
|
||||
users: number[]
|
||||
cli: Commands
|
||||
is_cb(subject: Commands | CommandCB): subject is CommandCB
|
||||
run(tokens: string[]): string
|
||||
|
|
92
lily.user.js
92
lily.user.js
|
@ -13,25 +13,38 @@
|
|||
// @match http://localhost:*/*
|
||||
// ==/UserScript==
|
||||
// checklist:
|
||||
// ? OOC blocked
|
||||
// ? custom lenses aren't updated
|
||||
// ? custom lenses wrong colour
|
||||
// [?] account filter, should only work on Lily 119643
|
||||
// [?] voicebox
|
||||
|
||||
(async function() {
|
||||
(function() {
|
||||
'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'})
|
||||
/** @type {LILY.Utils} */ const U = {
|
||||
/** @type {LILY.Utils} */ const U = {remove_hook: undefined,
|
||||
true(f) {f(); return true},
|
||||
with: (v, f) => f(v),
|
||||
send: f => Boolean(f()) && !void W.ChatRoomCharacterUpdate(W.Player),
|
||||
}
|
||||
/** @type {LILY.Box} */ const Box = {
|
||||
enable() {},
|
||||
disable() {},
|
||||
get equipped() {return false},
|
||||
get enabled() {return this.equipped && false},
|
||||
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
|
||||
}
|
||||
/** @type {LILY.Eyes} */ const Eyes = { // #9C0000 156 0 0
|
||||
/** @type {LILY.Eyes} */ const Eyes = {timeout: undefined, zones: ['ItemHead', 'Mask'],
|
||||
rgb: [
|
||||
'#990000', '#990f00', '#991f00', '#992e00', '#993d00', '#994d00', '#995c00', '#996b00', '#997a00', '#998a00',
|
||||
'#999900', '#8a9900', '#7a9900', '#6b9900', '#5c9900', '#4d9900', '#3d9900', '#2e9900', '#1f9900', '#0f9900',
|
||||
|
@ -40,47 +53,42 @@
|
|||
'#000099', '#0f0099', '#1f0099', '#2e0099', '#3d0099', '#4d0099', '#5c0099', '#6b0099', '#7a0099', '#8a0099',
|
||||
'#990099', '#99008a', '#99007a', '#99006b', '#99005c', '#99004d', '#99003d', '#99002e', '#99001f', '#99000f',
|
||||
],
|
||||
timeout: undefined,
|
||||
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'},
|
||||
zones: ['ItemHead', 'Mask'],
|
||||
set_rgb(item, n) {item.Color = [this.rgb[n], 'Default', this.rgb[n], 'Default']; return true},
|
||||
next_rgb() {return !this.check('ItemHead') && U.with(this.check('Mask'), i => i && U.with(this.rgb.indexOf(i.Color?.[0] || this.rgb[0]) + 1, n => this.set_rgb(i, n)))},
|
||||
roll_rgb() {this.next_rgb() && W.ChatRoomCharacterItemUpdate(W.Player, 'Mask')},
|
||||
start() {return Boolean(this.check('Mask')) && !this.timeout && Boolean(this.timeout = setInterval(() => this.roll_rgb(), 1000))},
|
||||
stop() {return Boolean(this.check('Mask')) && Boolean(this.timeout) && !(this.timeout = void clearInterval(this.timeout))},
|
||||
dim() {return !this.check('ItemHead') && !void W.InventoryWear(W.Player, 'AnimeLenses', 'ItemHead', ['#FFFFFF', 'Default', '#FFFFFF', 'Default'], undefined, 71_240, this.craft) && !void W.ChatRoomCharacterUpdate(W.Player)},
|
||||
clear() {return Boolean(this.check('ItemHead')) && !void W.InventoryRemove(W.Player, 'ItemHead') && !void W.ChatRoomCharacterUpdate(W.Player)},
|
||||
check(zone) {return U.with(this.item, item => (item && item.Asset.Group.Name === zone) ? item : undefined)},
|
||||
get item() {return this.zones.map(z => W.InventoryGet(W.Player, z)).find(item => item && item.Asset.Name === 'AnimeLenses') || undefined},
|
||||
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},
|
||||
}
|
||||
/** @type {LILY.Belt} */ const Belt = {
|
||||
admins: [71_240, 67_994],
|
||||
/** @type {LILY.Belt} */ const Belt = {users: [119_643, 154_662], admins: [71_240, 67_994],
|
||||
cli: {'#LILY': {
|
||||
status: _ => '200 OK',
|
||||
box: {
|
||||
on: _ => Box.enable(),
|
||||
off: _ => Box.disable(),
|
||||
},
|
||||
eyes: {
|
||||
start: _ => Eyes.start(),
|
||||
stop: _ => Eyes.stop(),
|
||||
dim: _ => Eyes.dim(),
|
||||
clear: _ => Eyes.clear(),
|
||||
},
|
||||
box: {on: Box.on, off: Box.off},
|
||||
eyes: {start: Eyes.start, stop: Eyes.stop, dim: Eyes.dim, clear: Eyes.clear},
|
||||
}},
|
||||
/** @type {LILY.Belt['is_cb']} */ is_cb: s => typeof s === 'function',
|
||||
run(tokens) { /** @type {string | undefined} */ let t, /** @type {LILY.Commands | LILY.CommandCB} */ cmd = this.cli
|
||||
while (t = tokens.shift()) if (U.with(cmd[t], next => !next || this.is_cb(cmd = next))) break // eslint-disable-line no-cond-assign
|
||||
if (!this.is_cb(cmd)) return t ? `unknown token ${t}` : `subcommands: [${Object.keys(cmd).join(', ')}]`
|
||||
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(', ')}]`
|
||||
return U.with(cmd(...tokens), r => typeof r === 'boolean' ? (r ? 'success' : 'fail') : String(r))
|
||||
},
|
||||
receive(data) { if (!data.Sender) return undefined
|
||||
W.ChatRoomSendLocal(`<span style="color: #666">${data.Content}</span>`)
|
||||
const reply = this.admins.includes(data.Sender) ? this.run(data.Content.split(' ')) : 'access denied'
|
||||
return void W.ChatRoomSendWhisper(data.Sender, `LILY: ${reply}`)
|
||||
return void W.ChatRoomSendWhisper(data.Sender, `LILY: ${Belt.admins.includes(data.Sender) ? Belt.run(data.Content.split(' ')) : 'access denied'}`)
|
||||
},
|
||||
}
|
||||
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)})
|
||||
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))
|
||||
})()
|
||||
|
|
Loading…
Reference in New Issue
Block a user