105.13.0 semver; settings migration
[lots of internal change] I'm determined to clean this mess up after all. Instead of a total rewrite (which didn't work), I'll migrate the code to JSDoc piece by piece. This is the first piece, setting up some basic framework and testing out this and that. Until I figure out how test this thing, I'll just ask peeps to import the module from testing branch I guess. Probably not gonna deal with anything new until I can turn "strict" on.
This commit is contained in:
parent
90231cb2ae
commit
16308eccf1
62
ambient.d.ts
vendored
Normal file
62
ambient.d.ts
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
interface ServerChatRoomMessage {
|
||||||
|
MBCHC_ID?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
declare namespace MBCHC {
|
||||||
|
type WGT = Window & typeof globalThis
|
||||||
|
interface Root extends WGT {
|
||||||
|
MBCHC?: any
|
||||||
|
bcModSdk?: import('bondage-club-mod-sdk').ModSDKGlobalAPI
|
||||||
|
bce_ActivityTriggers?: any
|
||||||
|
bce_EventExpressions?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
type V0 = PlayerOnlineSettings & {MBCHC: {timezones?: Record<number, number>}} // V0 is the whole onlinesettings
|
||||||
|
interface V1 { // V1 is specifically MBCHC inside extensionsettings
|
||||||
|
TZ: Record<number, number>
|
||||||
|
}
|
||||||
|
interface Methods {
|
||||||
|
migrate_0_1(v0: V0): true
|
||||||
|
save(cb?: (v1: V1) => unknown): true
|
||||||
|
replace(new_v1: V1): true
|
||||||
|
'purge!'(): true
|
||||||
|
get v1(): V1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Interval {
|
||||||
|
proxy: object // eslint-disable-line @typescript-eslint/ban-types
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
mini: boolean
|
||||||
|
maxi: boolean
|
||||||
|
upd(min: number, max: number, min_inclusive?: boolean, max_inclusive?: boolean): undefined
|
||||||
|
has(_: unknown, x: string): boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Utils {
|
||||||
|
interval: Interval
|
||||||
|
cid(character: Character): number | undefined
|
||||||
|
dn(character: Character): string
|
||||||
|
current(): string
|
||||||
|
with<V, R>(value: V, cb: (value: V) => R): R // A silly helper to kinda curry values
|
||||||
|
true<F extends (...args: unknown[]) => unknown>(cb: F): true // Useful for type-safe chaining
|
||||||
|
mutate<T>(value: T, cb: (v: T) => unknown): T // A silly helper for chaining
|
||||||
|
rm<T>(object: T, property: keyof T): T
|
||||||
|
mrg<T extends Record<string, unknown>>(target: T, ...source: T[]): T // Shorter, also less confusing with types
|
||||||
|
style<T>(query: string, cb: (s: CSSStyleDeclaration) => T): T | undefined
|
||||||
|
range(min: number, max: number, min_inclusive?: boolean, max_inclusive?: boolean): Proxy
|
||||||
|
assert<T>(error: string, value: T, cb?: (value: T) => boolean): T | never
|
||||||
|
each<T extends Record<string, unknown>>(object: T, cb: (key: string, value: unknown) => unknown): true
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TZ_Cache {
|
||||||
|
map: Map<number, number>
|
||||||
|
RE: RegExp
|
||||||
|
for(character: Character): number | undefined
|
||||||
|
memo(member_number: number, description?: string | undefined): number | undefined
|
||||||
|
parse(description: string | undefined): number | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
"include": [
|
"include": [
|
||||||
"node_modules/bc-stubs/bc/**/*.d.ts",
|
"node_modules/bc-stubs/bc/**/*.d.ts",
|
||||||
"node_modules/bondage-club-mod-sdk/dist/**/*.d.ts",
|
"node_modules/bondage-club-mod-sdk/dist/**/*.d.ts",
|
||||||
"typedef.d.ts",
|
"ambient.d.ts",
|
||||||
"mbchc.mjs",
|
"mbchc.mjs",
|
||||||
"server.js"
|
"server.js"
|
||||||
],
|
],
|
||||||
|
@ -13,6 +13,7 @@
|
||||||
],
|
],
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
|
"strictNullChecks": true,
|
||||||
"noImplicitOverride": true
|
"noImplicitOverride": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
378
mbchc.mjs
378
mbchc.mjs
|
@ -1,45 +1,57 @@
|
||||||
export {}
|
/** @type {MBCHC.Root} */ const W = window, D = W.document
|
||||||
|
if (W.MBCHC !== undefined) throw new Error('MBCHC found, aborting loading')
|
||||||
|
|
||||||
/** @typedef {import('bondage-club-mod-sdk').ModSDKGlobalAPI} ModSDKGlobalAPI */
|
export const VERSION = '105.13.0'
|
||||||
|
const MISSING_PLAYER_DIALOG = {Tag: 'MISSING TEXT IN "Interface.csv": ', Text: '\u200C'} // Zero-width non-joiner, used to break up ligatures, does nothing here, but an empty string is a falsey value
|
||||||
|
|
||||||
/** @type {Window & typeof globalThis & {MBCHC?: any, bcModSdk?: ModSDKGlobalAPI, bce_ActivityTriggers?: any, bce_EventExpressions?: any}} */
|
/** @implements {MBCHC.Interval} */ class Interval { proxy = new Proxy({}, this); min = 0; max = 0; mini = false; maxi = false
|
||||||
const w = window
|
/** @type {MBCHC.Interval['upd']} */ upd(min, max, mini = true, maxi = true) {this.min = min; this.max = max; this.mini = mini; this.maxi = maxi}
|
||||||
|
/** @type {MBCHC.Interval['has']} */ has(_, x) {return U.with(Number(x), x => (this.mini ? x >= this.min : x > this.min) && (this.maxi ? x <= this.max : x < this.max))}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** @type {MBCHC.Utils} */ const U = { interval: new Interval(),
|
||||||
* A silly helper to memorise values in callbacks
|
cid: char => char.MemberNumber,
|
||||||
* @template V, R
|
dn: char => W.CharacterNickname(char),
|
||||||
* @param {V} v Value to memorise
|
current: () => `${W.CurrentModule}/${W.CurrentScreen}`,
|
||||||
* @param {function(V): R} cb Callback
|
with: (v, cb) => cb(v),
|
||||||
* @returns {R} Return value of the callback
|
true(cb) {cb(); return true},
|
||||||
*/
|
mutate(v, cb) {cb(v); return v},
|
||||||
const take = (v, cb) => cb(v)
|
rm(o, p) {delete o[p]; return o}, // eslint-disable-line @typescript-eslint/no-dynamic-delete
|
||||||
|
mrg: (t, ...s) => Object.assign(t, ...s), // eslint-disable-line @typescript-eslint/no-unsafe-return
|
||||||
|
style: (q, cb) => U.with(D.querySelector(q), E => E instanceof HTMLElement ? cb(E.style) : undefined),
|
||||||
|
range: (...args) => U.true(() => void U.interval.upd(...args)) && U.interval.proxy,
|
||||||
|
assert(x, v, cb = Boolean) {if (!cb(v)) throw new Error(x); return v},
|
||||||
|
each: (o, cb) => U.true(() => void Object.entries(o).forEach(kv => cb(...kv))),
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** @type {MBCHC.Settings.Methods} */ const Settings = {
|
||||||
* Takes a DOM query and passes the element's style into a callback, returning its result
|
migrate_0_1(v0) { // I hate change
|
||||||
* @template T
|
U.with(v0.MBCHC.timezones, tz => tz !== undefined && this.save(v1 => void U.each(tz, (k, v) => v1.TZ[k] ||= v)))
|
||||||
* @param {string} q Query
|
W.ServerAccountUpdate.QueueData({OnlineSettings: U.rm(v0, 'MBCHC')})
|
||||||
* @param {function(CSSStyleDeclaration): T} cb Callback
|
return U.true(() => void console.warn('MBCHC: settings migration done (v0 -> v1). This should never appear again.'))
|
||||||
* @returns {T | void} Return value of the callback, if it was called
|
},
|
||||||
*/
|
save(cb = undefined) {W.Player.ExtensionSettings.MBCHC ||= {}; cb?.(this.v1); W.ServerPlayerExtensionSettingsSync('MBCHC'); return true},
|
||||||
const style = (q, cb) => take(document.querySelector(q), E => E && E instanceof HTMLElement && E.style ? cb(E.style) : undefined)
|
replace: v1 => U.true(() => W.Player.ExtensionSettings.MBCHC = v1) && Settings.save(),
|
||||||
|
'purge!': () => Settings.replace(/** @type {MBCHC.Settings.V1} */ ({})),
|
||||||
|
get v1() {return U.mutate(/** @type {MBCHC.Settings.V1} */ (W.Player.ExtensionSettings.MBCHC) ?? {}, v1 => { // we need to check and repair the whole object every time we access it
|
||||||
|
v1.TZ ||= {}
|
||||||
|
})},
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** @type {MBCHC.TZ_Cache} */ const TZ = { map: new Map(),
|
||||||
* @returns {string} Current view
|
RE: /(?:gmt|utc)\s*([+-])\s*(\d\d?)/i,
|
||||||
*/
|
for: c => c.MemberNumber === undefined ? undefined : TZ.map.get(c.MemberNumber) ?? TZ.memo(c.MemberNumber, c.Description),
|
||||||
const current = () => `${w.CurrentModule}/${w.CurrentScreen}`
|
memo: (cid, desc = undefined) => U.with(Settings.v1.TZ[cid] ?? TZ.parse(desc), n => n === undefined ? undefined : U.true(() => TZ.map.set(cid, n)) && n),
|
||||||
|
parse: desc => desc === undefined ? undefined : U.with(TZ.RE.exec(desc), m => m === null ? undefined : U.with(Number.parseInt(m[1] + m[2], 10), n => n in U.range(-12, 12) ? n : undefined)),
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// ^ type-safe (still need strict later)
|
||||||
* @param {Character} char
|
// =================================================================================
|
||||||
* @returns {number}
|
// v legacy mess
|
||||||
*/
|
|
||||||
const cid = char => char.MemberNumber
|
|
||||||
|
|
||||||
// Zero-width non-joiner, used to break up ligatures, does nothing here, but an empty string is a falsey value
|
W.MBCHC = {
|
||||||
const MISSING_PLAYER_DIALOG = {Tag: 'MISSING TEXT IN "Interface.csv": ', Text: '\u200C'}
|
VERSION,
|
||||||
|
Settings,
|
||||||
if (w.MBCHC) throw new Error('MBCHC found, aborting loading')
|
|
||||||
w.MBCHC = {
|
|
||||||
VERSION: 'dev.12',
|
|
||||||
NEXT_MESSAGE: 1,
|
NEXT_MESSAGE: 1,
|
||||||
LOG_MESSAGES: false,
|
LOG_MESSAGES: false,
|
||||||
RETHROW: false,
|
RETHROW: false,
|
||||||
|
@ -50,7 +62,6 @@ w.MBCHC = {
|
||||||
RE_PREF_ACTIVITY_ME: /^@/,
|
RE_PREF_ACTIVITY_ME: /^@/,
|
||||||
RE_PREF_ACTIVITY: /^@@/,
|
RE_PREF_ACTIVITY: /^@@/,
|
||||||
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
|
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
|
||||||
RE_TZ: /(?:gmt|utc)\s*([+-])\s*(\d\d?)/i,
|
|
||||||
RE_ALL_LEFT: /^<+$/,
|
RE_ALL_LEFT: /^<+$/,
|
||||||
RE_ALL_RIGHT: /^>+$/,
|
RE_ALL_RIGHT: /^>+$/,
|
||||||
RE_SPACES: /\s{2,}/g,
|
RE_SPACES: /\s{2,}/g,
|
||||||
|
@ -163,18 +174,13 @@ w.MBCHC = {
|
||||||
autohack: {desc: 'toggle the autohack feature', cb: mbchc => mbchc.inform(`Autohack is now ${(mbchc.AUTOHACK_ENABLED = !mbchc.AUTOHACK_ENABLED) ? 'enabled' : 'disabled'}`)}, // eslint-disable-line no-cond-assign
|
autohack: {desc: 'toggle the autohack feature', cb: mbchc => mbchc.inform(`Autohack is now ${(mbchc.AUTOHACK_ENABLED = !mbchc.AUTOHACK_ENABLED) ? 'enabled' : 'disabled'}`)}, // eslint-disable-line no-cond-assign
|
||||||
donate: {desc: 'Buy data and send it to recipient', args: {TARGET: {}}, cb: (mbchc, args) => mbchc.donate_data(args[0])},
|
donate: {desc: 'Buy data and send it to recipient', args: {TARGET: {}}, cb: (mbchc, args) => mbchc.donate_data(args[0])},
|
||||||
tz: {desc: 'set target\'s UTC offset', args: {OFFSET: {}, '[TARGET]': {}}, cb: (mbchc, args) => mbchc.set_timezone(args)},
|
tz: {desc: 'set target\'s UTC offset', args: {OFFSET: {}, '[TARGET]': {}}, cb: (mbchc, args) => mbchc.set_timezone(args)},
|
||||||
'purge!': {desc: 'delete MBCHC online saved data', cb(mbchc) {
|
'purge!': {desc: 'delete MBCHC online saved data', cb: () => Settings['purge!']()},
|
||||||
if (w.Player.OnlineSettings.MBCHC) {
|
// if (W.Player.ExtensionSettings.MBCHC) {
|
||||||
delete w.Player.OnlineSettings.MBCHC
|
// delete W.Player.ExtensionSettings.MBCHC
|
||||||
mbchc.save_settings()
|
// mbchc.save_settings() // FIXME
|
||||||
}
|
// }
|
||||||
}},
|
//}},
|
||||||
},
|
},
|
||||||
ensure(text, callback) {
|
|
||||||
const result = callback.call(this)
|
|
||||||
if (!result) throw new Error(text)
|
|
||||||
return (result)
|
|
||||||
},
|
|
||||||
calculate_maps() {
|
calculate_maps() {
|
||||||
this.DO_DATA = {verbs: {}, zones: {}}
|
this.DO_DATA = {verbs: {}, zones: {}}
|
||||||
for (const [verbs, data] of Object.entries(this.MAP_ACTIONS)) {
|
for (const [verbs, data] of Object.entries(this.MAP_ACTIONS)) {
|
||||||
|
@ -188,17 +194,6 @@ w.MBCHC = {
|
||||||
}
|
}
|
||||||
for (const [ag, zones] of Object.entries(this.MAP_ZONES)) for (const zone of zones) this.DO_DATA.zones[zone] = ag
|
for (const [ag, zones] of Object.entries(this.MAP_ZONES)) for (const zone of zones) this.DO_DATA.zones[zone] = ag
|
||||||
},
|
},
|
||||||
settings(setting = null) {
|
|
||||||
const settings = w.Player.OnlineSettings.MBCHC || {}
|
|
||||||
return (setting ? settings[setting] : settings)
|
|
||||||
},
|
|
||||||
save_settings(cb = null) {
|
|
||||||
if (cb) {
|
|
||||||
if (!w.Player.OnlineSettings.MBCHC) w.Player.OnlineSettings.MBCHC = {}
|
|
||||||
cb.call(this, w.Player.OnlineSettings.MBCHC)
|
|
||||||
}
|
|
||||||
w.ServerAccountUpdate.QueueData({OnlineSettings: w.Player.OnlineSettings})
|
|
||||||
},
|
|
||||||
log(level, message) {
|
log(level, message) {
|
||||||
console[level]('MBCHC: ' + String(message))
|
console[level]('MBCHC: ' + String(message))
|
||||||
},
|
},
|
||||||
|
@ -223,64 +218,61 @@ w.MBCHC = {
|
||||||
return text.replace(this.RE_SPACES, ' ').split(' ')
|
return text.replace(this.RE_SPACES, ' ').split(' ')
|
||||||
},
|
},
|
||||||
inform(html, timeout = 60_000) {
|
inform(html, timeout = 60_000) {
|
||||||
w.ChatRoomSendLocal(`<div class="mbchc">${html}</div>`, timeout)
|
W.ChatRoomSendLocal(`<div class="mbchc">${html}</div>`, timeout)
|
||||||
},
|
},
|
||||||
report(x) {
|
report(x) {
|
||||||
this.inform(`${x.toString()}`)
|
this.inform(`${x.toString()}`)
|
||||||
if (this.RETHROW) throw x
|
if (this.RETHROW) throw x
|
||||||
},
|
},
|
||||||
in(x, floor, ceiling) {
|
|
||||||
return ((x >= floor) && (x <= ceiling))
|
|
||||||
},
|
|
||||||
cid2char(id) {
|
cid2char(id) {
|
||||||
id = Number.parseInt(id, 10)
|
id = Number.parseInt(id, 10)
|
||||||
if (id === cid(w.Player)) return (w.Player)
|
if (id === U.cid(W.Player)) return (W.Player)
|
||||||
return (this.ensure(`character ${id} not found in the room`, () => w.ChatRoomCharacter.find(c => cid(c) === id)))
|
return U.assert(`character ${id} not found in the room`, W.ChatRoomCharacter.find(c => U.cid(c) === id))
|
||||||
},
|
},
|
||||||
pos2char(pos) {
|
pos2char(pos) {
|
||||||
if (!this.in(pos, 0, w.ChatRoomCharacter.length - 1)) throw new Error(`invalid position ${pos}`)
|
if (!(pos in U.range(0, W.ChatRoomCharacter.length - 1))) throw new Error(`invalid position ${pos}`)
|
||||||
return (w.ChatRoomCharacter[pos])
|
return (W.ChatRoomCharacter[pos])
|
||||||
},
|
},
|
||||||
rel2char(target) {
|
rel2char(target) {
|
||||||
const me = this.ensure('can\'t find my position', () => w.ChatRoomCharacter.findIndex(char => char.IsPlayer()) + 1) - 1 // 0 is falsey, but is valid index
|
const me = U.assert('can\'t find my position', W.ChatRoomCharacter.findIndex(char => char.IsPlayer()) + 1) - 1 // 0 is falsey, but is valid index
|
||||||
let pos = null
|
let pos = null
|
||||||
if (this.RE_ALL_LEFT.test(target)) pos = me - target.length
|
if (this.RE_ALL_LEFT.test(target)) pos = me - target.length
|
||||||
if (this.RE_ALL_RIGHT.test(target)) pos = me + target.length
|
if (this.RE_ALL_RIGHT.test(target)) pos = me + target.length
|
||||||
if (pos === null) throw new Error(`failed to parse target "${target}"`)
|
if (pos === null) throw new Error(`failed to parse target "${target}"`)
|
||||||
pos %= w.ChatRoomCharacter.length
|
pos %= W.ChatRoomCharacter.length
|
||||||
if (pos < 0) pos += w.ChatRoomCharacter.length
|
if (pos < 0) pos += W.ChatRoomCharacter.length
|
||||||
return (this.pos2char(pos))
|
return (this.pos2char(pos))
|
||||||
},
|
},
|
||||||
target2char(target) { // Target should be lowcase
|
target2char(target) { // Target should be lowcase
|
||||||
const input = target
|
const input = target
|
||||||
if (this.empty(target)) return (w.Player)
|
if (this.empty(target)) return (W.Player)
|
||||||
const int = Number.parseInt(target, 10)
|
const int = Number.parseInt(target, 10)
|
||||||
target = String(target)
|
target = String(target)
|
||||||
let found = []
|
let found = []
|
||||||
if (target.startsWith('=')) return (this.cid2char(target.slice(1)))
|
if (target.startsWith('=')) return (this.cid2char(target.slice(1)))
|
||||||
if (target.startsWith('<') || target.startsWith('>')) return (this.rel2char(target))
|
if (target.startsWith('<') || target.startsWith('>')) return (this.rel2char(target))
|
||||||
if (!Number.isNaN(int) && int.toString() === target) { // We got a number
|
if (!Number.isNaN(int) && int.toString() === target) { // We got a number
|
||||||
if (this.in(int, 0, 9)) return (this.pos2char(int))
|
if (int in U.range(0, 9)) return (this.pos2char(int))
|
||||||
if (this.in(int, 11, 15)) return (this.pos2char(int - 11))
|
if (int in U.range(11, 15)) return (this.pos2char(int - 11))
|
||||||
if (this.in(int, 21, 25)) return (this.pos2char(int - 16))
|
if (int in U.range(21, 25)) return (this.pos2char(int - 16))
|
||||||
found.push(...w.ChatRoomCharacter.filter(c => cid(c).toString().includes(target)))
|
found.push(...W.ChatRoomCharacter.filter(c => U.cid(c).toString().includes(target)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.startsWith('@')) target = target.slice(1)
|
if (target.startsWith('@')) target = target.slice(1)
|
||||||
found.push(...w.ChatRoomCharacter.filter(c => c.Name.toLocaleLowerCase().includes(target)))
|
found.push(...W.ChatRoomCharacter.filter(c => c.Name.toLocaleLowerCase().includes(target)))
|
||||||
found.push(...w.ChatRoomCharacter.filter(c => c.Nickname && (c.Nickname.toLocaleLowerCase().includes(target)))) // eslint-disable-line unicorn/no-array-push-push
|
found.push(...W.ChatRoomCharacter.filter(c => c.Nickname && (c.Nickname.toLocaleLowerCase().includes(target)))) // eslint-disable-line unicorn/no-array-push-push
|
||||||
const map = {}
|
const map = {}
|
||||||
for (const c of found) {
|
for (const c of found) {
|
||||||
if (!map[cid(c)]) map[cid(c)] = c
|
if (!map[U.cid(c)]) map[U.cid(c)] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
found = Object.values(map)
|
found = Object.values(map)
|
||||||
if (found.length === 0) throw new Error(`target "${input}": no match`)
|
if (found.length === 0) throw new Error(`target "${input}": no match`)
|
||||||
if (found.length > 1) throw new Error(`target "${input}": multiple matches (${found.map(c => `${cid(c)}|${c.Name}|${c.Nickname || c.Name}`).join(',')})`)
|
if (found.length > 1) throw new Error(`target "${input}": multiple matches (${found.map(c => `${U.cid(c)}|${c.Name}|${c.Nickname || c.Name}`).join(',')})`)
|
||||||
return (found[0])
|
return (found[0])
|
||||||
},
|
},
|
||||||
char2targets(char) {
|
char2targets(char) {
|
||||||
const [result, id] = [new Set(), cid(char).toString()]
|
const [result, id] = [new Set(), U.cid(char).toString()]
|
||||||
result.add(id).add(`=${id}`)
|
result.add(id).add(`=${id}`)
|
||||||
for (const t of this.tokenise(char.Name)) {
|
for (const t of this.tokenise(char.Name)) {
|
||||||
result.add(t)
|
result.add(t)
|
||||||
|
@ -294,27 +286,28 @@ w.MBCHC = {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
//get settings() {return Settings.v1},
|
||||||
donate_data(target) {
|
donate_data(target) {
|
||||||
const char = this.target2char(target)
|
const char = this.target2char(target)
|
||||||
if (char.IsPlayer()) throw new Error('target must not be you')
|
if (char.IsPlayer()) throw new Error('target must not be you')
|
||||||
if (!char.IsRestrained()) throw new Error('target must be bound')
|
if (!char.IsRestrained()) throw new Error('target must be bound')
|
||||||
const cost = Math.round(((Math.random() * 10) + 15))
|
const cost = Math.round(((Math.random() * 10) + 15))
|
||||||
if (w.Player.Money < cost) throw new Error('not enough money')
|
if (W.Player.Money < cost) throw new Error('not enough money')
|
||||||
w.CharacterChangeMoney(w.Player, -cost)
|
W.CharacterChangeMoney(W.Player, -cost)
|
||||||
w.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: cid(char)})
|
W.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: U.cid(char)})
|
||||||
w.ChatRoomMessage({Sender: cid(w.Player), Type: 'Action', Content: `You've bought data for $${cost} and sent it to ${char.dn}.`, Dictionary: [MISSING_PLAYER_DIALOG]})
|
W.ChatRoomMessage({Sender: U.cid(W.Player), Type: 'Action', Content: `You've bought data for $${cost} and sent it to ${U.dn(char)}.`, Dictionary: [MISSING_PLAYER_DIALOG]})
|
||||||
},
|
},
|
||||||
run_activity(char, ag, action) {
|
run_activity(char, ag, action) {
|
||||||
try {
|
try {
|
||||||
if (!w.ActivityAllowed()) throw new Error('activities disabled in this room')
|
if (!W.ActivityAllowed()) throw new Error('activities disabled in this room')
|
||||||
if (!w.ServerChatRoomGetAllowItem(w.Player, char)) throw new Error('no permissions')
|
if (!W.ServerChatRoomGetAllowItem(W.Player, char)) throw new Error('no permissions')
|
||||||
char.FocusGroup = this.ensure('invalid AssetGroup', () => w.AssetGroupGet(char.AssetFamily, ag))
|
char.FocusGroup = U.assert('invalid AssetGroup', W.AssetGroupGet(char.AssetFamily, ag))
|
||||||
const activity = this.ensure('invalid activity', () => w.ActivityAllowedForGroup(char, char.FocusGroup.Name).find(a => a.Activity?.Name === action))
|
const activity = U.assert('invalid activity', W.ActivityAllowedForGroup(char, char.FocusGroup.Name).find(a => a.Activity?.Name === action))
|
||||||
//if ((activity.Name || activity.Activity.Name).endsWith('Item')) {
|
//if ((activity.Name || activity.Activity.Name).endsWith('Item')) {
|
||||||
// const item = this.ensure('no toy found', () => w.Player.Inventory.find(i => i.Asset?.Name === 'SpankingToys' && i.Asset.Group?.Name === char.FocusGroup.Name && w.AssetSpankingToys.DynamicActivity(char) === (activity.Name || activity.Activity.Name)))
|
// const item = this.ensure('no toy found', () => w.Player.Inventory.find(i => i.Asset?.Name === 'SpankingToys' && i.Asset.Group?.Name === char.FocusGroup.Name && w.AssetSpankingToys.DynamicActivity(char) === (activity.Name || activity.Activity.Name)))
|
||||||
// w.DialogPublishAction(char, item)
|
// w.DialogPublishAction(char, item)
|
||||||
//} else w.ActivityRun(w.Player, char, char.FocusGroup, activity)
|
//} else w.ActivityRun(w.Player, char, char.FocusGroup, activity)
|
||||||
w.ActivityRun(w.Player, char, char.FocusGroup, activity)
|
W.ActivityRun(W.Player, char, char.FocusGroup, activity)
|
||||||
} finally {
|
} finally {
|
||||||
char.FocusGroup = null
|
char.FocusGroup = null
|
||||||
}
|
}
|
||||||
|
@ -323,10 +316,10 @@ w.MBCHC = {
|
||||||
const text = string.slice(1)
|
const text = string.slice(1)
|
||||||
let suffix = ' '
|
let suffix = ' '
|
||||||
if (text.startsWith('\'') || text.startsWith(' ')) suffix = ''
|
if (text.startsWith('\'') || text.startsWith(' ')) suffix = ''
|
||||||
return `${w.MBCHC.PREF_ACTIVITY}<${cid(w.Player)}:>SourceCharacter${suffix}`
|
return `${W.MBCHC.PREF_ACTIVITY}<${U.cid(W.Player)}:>SourceCharacter${suffix}`
|
||||||
},
|
},
|
||||||
cid2dict(type, cid) {
|
cid2dict(type, cid) {
|
||||||
return ({Tag: `${type}Character`, MemberNumber: cid, Text: this.cid2char(cid).dn})
|
return ({Tag: `${type}Character`, MemberNumber: cid, Text: U.dn(this.cid2char(cid))})
|
||||||
},
|
},
|
||||||
send_activity(message) {
|
send_activity(message) {
|
||||||
const dict = [MISSING_PLAYER_DIALOG]
|
const dict = [MISSING_PLAYER_DIALOG]
|
||||||
|
@ -336,7 +329,7 @@ w.MBCHC = {
|
||||||
if (cids[1]) dict.push(this.cid2dict('Source', cids[1]))
|
if (cids[1]) dict.push(this.cid2dict('Source', cids[1]))
|
||||||
if (cids[2]) dict.push(this.cid2dict('Target', cids[2]), this.cid2dict('Destination', cids[2]))
|
if (cids[2]) dict.push(this.cid2dict('Target', cids[2]), this.cid2dict('Destination', cids[2]))
|
||||||
}
|
}
|
||||||
w.ServerSend('ChatRoomChat', {Type: 'Action', Content: message, Dictionary: dict})
|
W.ServerSend('ChatRoomChat', {Type: 'Action', Content: message, Dictionary: dict})
|
||||||
},
|
},
|
||||||
//receive(data) {
|
//receive(data) {
|
||||||
// const char = this.cid2char(data.Sender)
|
// const char = this.cid2char(data.Sender)
|
||||||
|
@ -370,54 +363,43 @@ w.MBCHC = {
|
||||||
patch_fbc() {
|
patch_fbc() {
|
||||||
this.remove_fbc_hook()
|
this.remove_fbc_hook()
|
||||||
delete this.remove_fbc_hook
|
delete this.remove_fbc_hook
|
||||||
w.bce_ActivityTriggers.push(...w.bce_ActivityTriggers.filter(t => t.Type === 'Emote').map(t => this.copy_fbc_trigger(t)))
|
W.bce_ActivityTriggers.push(...W.bce_ActivityTriggers.filter(t => t.Type === 'Emote').map(t => this.copy_fbc_trigger(t)))
|
||||||
/* (["anim", "pose"]).forEach(tag => {let cmd = w.Commands.find(c => tag === c.Tag); if (cmd) cmd.AutoComplete = this[`complete_fbc_${tag}`]}) */ // this line explodes, because it needs a semicolon in front if it
|
/* (["anim", "pose"]).forEach(tag => {let cmd = w.Commands.find(c => tag === c.Tag); if (cmd) cmd.AutoComplete = this[`complete_fbc_${tag}`]}) */ // this line explodes, because it needs a semicolon in front if it
|
||||||
let cmd = w.Commands.find(c => c.Tag === 'anim')
|
let cmd = W.Commands.find(c => c.Tag === 'anim')
|
||||||
if (cmd) cmd.AutoComplete = this.complete_fbc_anim
|
if (cmd) cmd.AutoComplete = this.complete_fbc_anim
|
||||||
cmd = w.Commands.find(c => c.Tag === 'pose')
|
cmd = W.Commands.find(c => c.Tag === 'pose')
|
||||||
if (cmd) cmd.AutoComplete = this.complete_fbc_pose
|
if (cmd) cmd.AutoComplete = this.complete_fbc_pose
|
||||||
},
|
},
|
||||||
find_timezone(char) {
|
|
||||||
const timezones = this.settings('timezones')
|
|
||||||
if (timezones && typeof timezones[cid(char)] === 'number') return (timezones[cid(char)])
|
|
||||||
const match = (char.Description) ? char.Description.match(this.RE_TZ) : null
|
|
||||||
const int = match ? Number.parseInt(match[1] + match[2], 10) : 42
|
|
||||||
if (this.in(int, -12, 12)) return (int)
|
|
||||||
return (null)
|
|
||||||
},
|
|
||||||
//player_enters_room() { // Or if the mod is loaded while player is in the room
|
//player_enters_room() { // Or if the mod is loaded while player is in the room
|
||||||
// this.hello()
|
// this.hello()
|
||||||
//},
|
//},
|
||||||
set_timezone(args) {
|
set_timezone(args) {
|
||||||
const tz = Number.parseInt(args[0], 10)
|
const tz = Number.parseInt(args[0], 10)
|
||||||
if (Number.isNaN(tz)) throw new Error(`invalid offset "${args[0]}"`)
|
if (Number.isNaN(tz)) throw new Error(`invalid offset "${args[0]}"`)
|
||||||
if (!this.in(tz, -12, 12)) throw new Error('offset should be [-12,12]')
|
if (!(tz in U.range(-12, 12))) throw new Error('offset should be [-12,12]')
|
||||||
const char = this.target2char(args[1])
|
const char = this.target2char(args[1])
|
||||||
char.MBCHC_LOCAL.TZ = tz
|
if (char.MemberNumber === undefined) return
|
||||||
this.save_settings(s => {
|
Settings.save(v1 => v1.TZ[char.MemberNumber] = tz)
|
||||||
if (!s.timezones) s.timezones = {}
|
TZ.memo(char.MemberNumber)
|
||||||
s.timezones[cid(char)] = tz
|
//char.MBCHC_LOCAL.TZ = tz
|
||||||
})
|
//this.save_settings(s => { // FIXME
|
||||||
},
|
// if (!s.timezones) s.timezones = {}
|
||||||
update_char(char) {
|
// s.timezones[U.cid(char)] = tz
|
||||||
//char.cid = char.MemberNumber // Club ID (shorter)
|
//})
|
||||||
char.dn = w.CharacterNickname(char) // DisplayName (shortcut)
|
|
||||||
if (!char.MBCHC_LOCAL) char.MBCHC_LOCAL = {}
|
|
||||||
if (!char.MBCHC_LOCAL.TZ) char.MBCHC_LOCAL.TZ = this.find_timezone(char)
|
|
||||||
},
|
},
|
||||||
command_mbchc(argline, cmdline, args) {
|
command_mbchc(argline, cmdline, args) {
|
||||||
const mbchc = w.MBCHC
|
const mbchc = W.MBCHC
|
||||||
try { // `this` is command object
|
try { // `this` is command object
|
||||||
if (args.length === 0) return (mbchc.inform(Object.entries(mbchc.SUBCOMMANDS_MBCHC).map(([cmd, sub]) => `<div>/mbchc ${cmd} ${sub.args ? Object.keys(sub.args).join(' ') : ''}: ${sub.desc}</div>`).join('')))
|
if (args.length === 0) return (mbchc.inform(Object.entries(mbchc.SUBCOMMANDS_MBCHC).map(([cmd, sub]) => `<div>/mbchc ${cmd} ${sub.args ? Object.keys(sub.args).join(' ') : ''}: ${sub.desc}</div>`).join('')))
|
||||||
const cmd = String(args.shift())
|
const cmd = String(args.shift())
|
||||||
const sub = mbchc.ensure(`unknown subcommand "${cmd}"`, () => mbchc.SUBCOMMANDS_MBCHC[cmd])
|
const sub = U.assert(`unknown subcommand "${cmd}"`, mbchc.SUBCOMMANDS_MBCHC[cmd])
|
||||||
sub.cb.call(mbchc, mbchc, args, argline, cmdline)
|
sub.cb.call(mbchc, mbchc, args, argline, cmdline)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
mbchc.report(error)
|
mbchc.report(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
command_activity(argline, cmdline, _) {
|
command_activity(argline, cmdline, _) {
|
||||||
const mbchc = w.MBCHC
|
const mbchc = W.MBCHC
|
||||||
if (!mbchc.empty(argline)) {
|
if (!mbchc.empty(argline)) {
|
||||||
try { // `this` is command object
|
try { // `this` is command object
|
||||||
const message = mbchc.normalise_message(cmdline.replace(mbchc.RE_ACTIVITY, ''), {trim: true, dot: true, up: true})
|
const message = mbchc.normalise_message(cmdline.replace(mbchc.RE_ACTIVITY, ''), {trim: true, dot: true, up: true})
|
||||||
|
@ -428,27 +410,27 @@ w.MBCHC = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
command_do(argline, cmdline, args) {
|
command_do(argline, cmdline, args) {
|
||||||
const mbchc = w.MBCHC
|
const mbchc = W.MBCHC
|
||||||
try { // `this` is command object
|
try { // `this` is command object
|
||||||
if (args.length === 0) return (mbchc.inform('<div>Usage: /do VERB [ZONE] [TARGET]</div><div>Available verbs:</div>' + Object.keys(mbchc.MAP_ACTIONS).join(', ') + '<div>Available zones:</div>' + Object.keys(mbchc.DO_DATA.zones).join(', ')))
|
if (args.length === 0) return (mbchc.inform('<div>Usage: /do VERB [ZONE] [TARGET]</div><div>Available verbs:</div>' + Object.keys(mbchc.MAP_ACTIONS).join(', ') + '<div>Available zones:</div>' + Object.keys(mbchc.DO_DATA.zones).join(', ')))
|
||||||
let [verb, zone, target] = args
|
let [verb, zone, target] = args
|
||||||
const zones = mbchc.ensure(`unknown verb "${verb}"`, () => mbchc.DO_DATA.verbs[verb])
|
const zones = U.assert(`unknown verb "${verb}"`, mbchc.DO_DATA.verbs[verb])
|
||||||
if (Object.keys(zones).length === 1) {
|
if (Object.keys(zones).length === 1) {
|
||||||
if (!target) target = zone
|
if (!target) target = zone
|
||||||
zone = mbchc.MAP_ZONES[Object.keys(zones)[0]][0]
|
zone = mbchc.MAP_ZONES[Object.keys(zones)[0]][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!zone) throw new Error('zone missing')
|
if (!zone) throw new Error('zone missing')
|
||||||
const ag = mbchc.ensure(`unknown zone "${zone}"`, () => mbchc.DO_DATA.zones[zone])
|
const ag = U.assert(`unknown zone "${zone}"`, mbchc.DO_DATA.zones[zone])
|
||||||
const types = mbchc.ensure(`zone "${zone}" invalid for "${verb}"`, () => zones[ag])
|
const types = U.assert(`zone "${zone}" invalid for "${verb}"`, zones[ag])
|
||||||
let char = w.Player
|
let char = W.Player
|
||||||
if (target && ((types.self.length === 0) || (types.others.length > 0))) char = mbchc.target2char(target)
|
if (target && ((types.self.length === 0) || (types.others.length > 0))) char = mbchc.target2char(target)
|
||||||
const type = char.IsPlayer() ? 'self' : 'others'
|
const type = char.IsPlayer() ? 'self' : 'others'
|
||||||
const available = w.ActivityAllowedForGroup(char, ag)
|
const available = W.ActivityAllowedForGroup(char, ag)
|
||||||
//const toy = w.InventoryGet(w.Player, 'ItemHands')
|
//const toy = w.InventoryGet(w.Player, 'ItemHands')
|
||||||
//if (toy && toy.Asset.Name === 'SpankingToys') available.push(w.AssetAllActivities(char.AssetFamily).find(a => a.Name === w.InventorySpankingToysGetActivity?.(w.Player)))
|
//if (toy && toy.Asset.Name === 'SpankingToys') available.push(w.AssetAllActivities(char.AssetFamily).find(a => a.Name === w.InventorySpankingToysGetActivity?.(w.Player)))
|
||||||
const actions = mbchc.ensure(`zone "${zone}" invalid for ("${verb}" "${type}")`, () => types[type])
|
const actions = U.assert(`zone "${zone}" invalid for ("${verb}" "${type}")`, types[type])
|
||||||
const action = mbchc.ensure(`invalid action (${verb} ${zone} ${target})`, () => actions.find(name => available.find(a => a.Activity?.Name === name)))
|
const action = U.assert(`invalid action (${verb} ${zone} ${target})`, actions.find(name => available.find(a => a.Activity?.Name === name)))
|
||||||
mbchc.run_activity(char, ag, action)
|
mbchc.run_activity(char, ag, action)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
mbchc.report(error)
|
mbchc.report(error)
|
||||||
|
@ -456,9 +438,9 @@ w.MBCHC = {
|
||||||
},
|
},
|
||||||
bell() {
|
bell() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
style('#InputChat', s => s.outline = '')
|
U.style('#InputChat', s => s.outline = '')
|
||||||
}, 100)
|
}, 100)
|
||||||
style('#InputChat', s => s.outline = 'solid red')
|
U.style('#InputChat', s => s.outline = 'solid red')
|
||||||
},
|
},
|
||||||
complete(options, space = true) {
|
complete(options, space = true) {
|
||||||
if (options.length === 0) return (this.bell())
|
if (options.length === 0) return (this.bell())
|
||||||
|
@ -475,22 +457,22 @@ w.MBCHC = {
|
||||||
|
|
||||||
if (pref) this.complete([pref], false)
|
if (pref) this.complete([pref], false)
|
||||||
this.comp_hint(options)
|
this.comp_hint(options)
|
||||||
} else w.ElementValue('InputChat', w.ElementValue('InputChat').replace(this.RE_LAST_WORD, `$1${options[0]}${space ? ' ' : ''}`))
|
} else W.ElementValue('InputChat', W.ElementValue('InputChat').replace(this.RE_LAST_WORD, `$1${options[0]}${space ? ' ' : ''}`))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays strings as completion hint
|
* Displays strings as completion hint
|
||||||
* @param {string[]} options List of words to display. The order will be modified without copy.
|
* @param {string[]} options List of words to display. The order will be modified without copy.
|
||||||
* @returns {void}
|
* @returns {undefined}
|
||||||
*/
|
*/
|
||||||
comp_hint(options) {
|
comp_hint(options) {
|
||||||
if (options.length === 0) return
|
if (options.length === 0) return
|
||||||
this.COMP_HINT.innerHTML = '<div>' + options.sort().reverse().map(s => `<div>${s}</div>`).join('') + '</div>'
|
this.COMP_HINT.innerHTML = '<div>' + options.sort().reverse().map(s => `<div>${s}</div>`).join('') + '</div>'
|
||||||
this.COMP_HINT.style.display = 'block'
|
this.COMP_HINT.style.display = 'block'
|
||||||
w.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (w.Player.ChatSettings.ColorTheme || 'Light'))
|
W.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (W.Player.ChatSettings?.ColorTheme || 'Light'))
|
||||||
const rescroll = w.ElementIsScrolledToEnd('TextAreaChatLog')
|
const rescroll = W.ElementIsScrolledToEnd('TextAreaChatLog')
|
||||||
w.ChatRoomResize(false)
|
W.ChatRoomResize(false)
|
||||||
if (rescroll) w.ElementScrollToEnd('TextAreaChatLog')
|
if (rescroll) W.ElementScrollToEnd('TextAreaChatLog')
|
||||||
this.COMP_HINT.firstChild?.lastChild?.scrollIntoView({behaviour: 'instant'})
|
this.COMP_HINT.firstChild?.lastChild?.scrollIntoView({behaviour: 'instant'})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -504,12 +486,12 @@ w.MBCHC = {
|
||||||
comp_hint_hide() {
|
comp_hint_hide() {
|
||||||
if (!this.comp_hint_visible()) return
|
if (!this.comp_hint_visible()) return
|
||||||
this.COMP_HINT.style.display = 'none'
|
this.COMP_HINT.style.display = 'none'
|
||||||
w.ChatRoomResize(false)
|
W.ChatRoomResize(false)
|
||||||
},
|
},
|
||||||
complete_target(token, me2 = true, check_perms = false) {
|
complete_target(token, me2 = true, check_perms = false) {
|
||||||
const [locase, found] = [token.toLocaleLowerCase(), new Set()]
|
const [locase, found] = [token.toLocaleLowerCase(), new Set()]
|
||||||
for (const c of w.ChatRoomCharacter) {
|
for (const c of W.ChatRoomCharacter) {
|
||||||
if ((c.IsPlayer() && !me2) || (check_perms && !w.ServerChatRoomGetAllowItem(w.Player, c))) continue
|
if ((c.IsPlayer() && !me2) || (check_perms && !W.ServerChatRoomGetAllowItem(W.Player, c))) continue
|
||||||
for (const s of this.char2targets(c)) {
|
for (const s of this.char2targets(c)) {
|
||||||
if (s.toLocaleLowerCase().startsWith(locase)) found.add(s)
|
if (s.toLocaleLowerCase().startsWith(locase)) found.add(s)
|
||||||
}
|
}
|
||||||
|
@ -519,12 +501,12 @@ w.MBCHC = {
|
||||||
},
|
},
|
||||||
complete_common() {
|
complete_common() {
|
||||||
// w.ElementValue('InputChat') will strip the trailing whitespace
|
// w.ElementValue('InputChat') will strip the trailing whitespace
|
||||||
const E = document.querySelector('#InputChat')
|
const E = D.querySelector('#InputChat')
|
||||||
if (!(E && E instanceof HTMLTextAreaElement)) throw new Error('somehow InputChat is broken')
|
if (!(E && E instanceof HTMLTextAreaElement)) throw new Error('somehow InputChat is broken')
|
||||||
return ([this, E.value, this.tokenise(E.value)])
|
return ([this, E.value, this.tokenise(E.value)])
|
||||||
},
|
},
|
||||||
complete_mbchc(_args, _locase, _cmdline) {
|
complete_mbchc(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = w.MBCHC.complete_common(); // `this` is command object
|
const [mbchc, _input, tokens] = W.MBCHC.complete_common(); // `this` is command object
|
||||||
if (tokens.length === 0) return
|
if (tokens.length === 0) return
|
||||||
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
||||||
const subname = tokens[1].toLocaleLowerCase()
|
const subname = tokens[1].toLocaleLowerCase()
|
||||||
|
@ -539,11 +521,11 @@ w.MBCHC = {
|
||||||
complete_do_target(actions, token) {
|
complete_do_target(actions, token) {
|
||||||
if (!actions) return
|
if (!actions) return
|
||||||
const me2 = (actions.self.length > 0)
|
const me2 = (actions.self.length > 0)
|
||||||
if (me2 && actions.others.length === 0) return (this.complete([cid(w.Player).toString()])) // Target is always the player
|
if (me2 && actions.others.length === 0) return (this.complete([U.cid(W.Player).toString()])) // Target is always the player
|
||||||
this.complete_target(token, me2, true)
|
this.complete_target(token, me2, true)
|
||||||
},
|
},
|
||||||
complete_do(_args, _locase, _cmdline) {
|
complete_do(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = w.MBCHC.complete_common(); // `this` is command object
|
const [mbchc, _input, tokens] = W.MBCHC.complete_common(); // `this` is command object
|
||||||
if (tokens.length === 0) return
|
if (tokens.length === 0) return
|
||||||
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
||||||
// Now, we *could* run a filter to exclude impossible activities, but it isn't very useful, and also seems like a lot of CPU to iterate over every action on every zone of every char in the room
|
// Now, we *could* run a filter to exclude impossible activities, but it isn't very useful, and also seems like a lot of CPU to iterate over every action on every zone of every char in the room
|
||||||
|
@ -566,22 +548,22 @@ w.MBCHC = {
|
||||||
mbchc.bell()
|
mbchc.bell()
|
||||||
},
|
},
|
||||||
complete_fbc_anim(_args, _locase, _cmdline) {
|
complete_fbc_anim(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = w.MBCHC.complete_common(); // `this` is command object
|
const [mbchc, _input, tokens] = W.MBCHC.complete_common(); // `this` is command object
|
||||||
if (tokens.length === 0) return
|
if (tokens.length === 0) return
|
||||||
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
||||||
if (tokens.length > 2) return (mbchc.bell())
|
if (tokens.length > 2) return (mbchc.bell())
|
||||||
const anim = tokens[1].toLocaleLowerCase()
|
const anim = tokens[1].toLocaleLowerCase()
|
||||||
return (mbchc.complete(Object.keys(w.bce_EventExpressions).filter(a => a.toLocaleLowerCase().startsWith(anim))))
|
return (mbchc.complete(Object.keys(W.bce_EventExpressions).filter(a => a.toLocaleLowerCase().startsWith(anim))))
|
||||||
},
|
},
|
||||||
complete_fbc_pose(_args, _locase, _cmdline) {
|
complete_fbc_pose(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = w.MBCHC.complete_common(); // `this` is command object
|
const [mbchc, _input, tokens] = W.MBCHC.complete_common(); // `this` is command object
|
||||||
if (tokens.length === 0) return
|
if (tokens.length === 0) return
|
||||||
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
if (tokens.length < 2) return (mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
|
||||||
const pose = tokens.at(-1).toLocaleLowerCase()
|
const pose = tokens.at(-1).toLocaleLowerCase()
|
||||||
return (mbchc.complete(w.PoseFemale3DCG.map(p => p.Name).filter(p => p.toLocaleLowerCase().startsWith(pose))))
|
return (mbchc.complete(W.PoseFemale3DCG.map(p => p.Name).filter(p => p.toLocaleLowerCase().startsWith(pose))))
|
||||||
},
|
},
|
||||||
history(down) {
|
history(down) {
|
||||||
const [text, history] = [w.ElementValue('InputChat'), w.ChatRoomLastMessage]
|
const [text, history] = [W.ElementValue('InputChat'), W.ChatRoomLastMessage]
|
||||||
if (!this.HISTORY_MODE) {
|
if (!this.HISTORY_MODE) {
|
||||||
history.push(text)
|
history.push(text)
|
||||||
this.HISTORY_MODE = true
|
this.HISTORY_MODE = true
|
||||||
|
@ -589,15 +571,15 @@ w.MBCHC = {
|
||||||
|
|
||||||
const ids = history.map((t, i) => ({t, i})).filter(r => r.t.startsWith(history.at(-1))).map(r => r.i)
|
const ids = history.map((t, i) => ({t, i})).filter(r => r.t.startsWith(history.at(-1))).map(r => r.i)
|
||||||
if (!down) ids.reverse()
|
if (!down) ids.reverse()
|
||||||
const found = ids.find(id => (down) ? id > w.ChatRoomLastMessageIndex : id < w.ChatRoomLastMessageIndex)
|
const found = ids.find(id => (down) ? id > W.ChatRoomLastMessageIndex : id < W.ChatRoomLastMessageIndex)
|
||||||
if (!found) return (this.bell())
|
if (!found) return (this.bell())
|
||||||
w.ElementValue('InputChat', history[found])
|
W.ElementValue('InputChat', history[found])
|
||||||
w.ChatRoomLastMessageIndex = found
|
W.ChatRoomLastMessageIndex = found
|
||||||
},
|
},
|
||||||
focus_chat_checks() { // we only want to catch chatlog and canvas (no map though) keypresses
|
focus_chat_checks() { // we only want to catch chatlog and canvas (no map though) keypresses
|
||||||
if (document.activeElement === document.body) return true
|
if (D.activeElement === D.body) return true
|
||||||
if (document.activeElement.id !== 'MainCanvas') return false
|
if (D.activeElement?.id !== 'MainCanvas') return false
|
||||||
return !w.ChatRoomMapViewIsActive()
|
return !W.ChatRoomMapViewIsActive()
|
||||||
},
|
},
|
||||||
focus_chat_whitelist(event) {
|
focus_chat_whitelist(event) {
|
||||||
if (event.ctrlKey && event.key === 'v') return true // Ctrl+V should paste
|
if (event.ctrlKey && event.key === 'v') return true // Ctrl+V should paste
|
||||||
|
@ -607,8 +589,8 @@ w.MBCHC = {
|
||||||
if (event.repeat) return // Only unique presses please
|
if (event.repeat) return // Only unique presses please
|
||||||
if (!this.focus_chat_checks()) return
|
if (!this.focus_chat_checks()) return
|
||||||
if ([event.altKey, event.ctrlKey, event.metaKey].some(Boolean) && !this.focus_chat_whitelist(event)) return // Alt, ctrl and meta should all be false
|
if ([event.altKey, event.ctrlKey, event.metaKey].some(Boolean) && !this.focus_chat_whitelist(event)) return // Alt, ctrl and meta should all be false
|
||||||
if (style('#InputChat', s => s.display) !== 'inline') return // Input chat missing
|
if (U.style('#InputChat', s => s.display) !== 'inline') return // Input chat missing
|
||||||
w.ElementFocus('InputChat')
|
W.ElementFocus('InputChat')
|
||||||
},
|
},
|
||||||
loader() {
|
loader() {
|
||||||
if (this.remove_load_hook) {
|
if (this.remove_load_hook) {
|
||||||
|
@ -617,18 +599,21 @@ w.MBCHC = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.LOADED) return
|
if (this.LOADED) return
|
||||||
|
|
||||||
|
U.with(/** @type {MBCHC.Settings.V0} */ (W.Player.OnlineSettings), os => os?.MBCHC && Settings.migrate_0_1(os))
|
||||||
|
|
||||||
// Calculated values
|
// Calculated values
|
||||||
const COMMANDS = [
|
const COMMANDS = [
|
||||||
{Tag: 'mbchc', Description: ': Utility functions ("/mbchc" for help)', Action: this.command_mbchc, AutoComplete: this.complete_mbchc},
|
{Tag: 'mbchc', Description: ': Utility functions ("/mbchc" for help)', Action: this.command_mbchc, AutoComplete: this.complete_mbchc},
|
||||||
{Tag: 'activity', Description: '[Message]: Send a custom activity (or "@@Message", or "@Message" as yourself)', Action: this.command_activity},
|
{Tag: 'activity', Description: '[Message]: Send a custom activity (or "@@Message", or "@Message" as yourself)', Action: this.command_activity},
|
||||||
{Tag: 'do', Description: ': Do an activity, as if clicked on its button ("/do" for help)', Action: this.command_do, AutoComplete: this.complete_do},
|
{Tag: 'do', Description: ': Do an activity, as if clicked on its button ("/do" for help)', Action: this.command_do, AutoComplete: this.complete_do},
|
||||||
]
|
]
|
||||||
this.CommandsKey = CommandsKey /* eslint-disable-line no-undef */ // window.CommandsKey is undefined
|
this.CommandsKey = CommandsKey
|
||||||
this.RE_ACTIVITY = new RegExp(`^${this.CommandsKey}activity `)
|
this.RE_ACTIVITY = new RegExp(`^${this.CommandsKey}activity `)
|
||||||
this.PREF_ACTIVITY = `${this.CommandsKey}activity `
|
this.PREF_ACTIVITY = `${this.CommandsKey}activity `
|
||||||
this.COMP_HINT = document.createElement('div')
|
this.COMP_HINT = D.createElement('div')
|
||||||
this.COMP_HINT.id = 'mbchcCompHint'
|
this.COMP_HINT.id = 'mbchcCompHint'
|
||||||
const css = document.createElement('style')
|
const css = D.createElement('style')
|
||||||
css.textContent = `
|
css.textContent = `
|
||||||
#TextAreaChatLog .mbchc, #TextAreaChatLog .mbchc {
|
#TextAreaChatLog .mbchc, #TextAreaChatLog .mbchc {
|
||||||
background-color: ${this.RGB_POLLY};
|
background-color: ${this.RGB_POLLY};
|
||||||
|
@ -659,47 +644,48 @@ w.MBCHC = {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
document.head.append(css)
|
D.head.append(css)
|
||||||
// Actions
|
// Actions
|
||||||
this.calculate_maps()
|
this.calculate_maps()
|
||||||
//w.Player.MBCHC = {VERSION: this.VERSION}
|
//w.Player.MBCHC = {VERSION: this.VERSION}
|
||||||
w.CommandCombine(COMMANDS)
|
W.CommandCombine(COMMANDS)
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
this.remove_fbc_hook = this.before('MainRun', () => w.bce_ActivityTriggers && this.patch_fbc())
|
this.remove_fbc_hook = this.before('MainRun', () => W.bce_ActivityTriggers && this.patch_fbc())
|
||||||
this.after('CharacterOnlineRefresh', char => this.update_char(char))
|
// this.after('CharacterOnlineRefresh', char => this.update_char(char))
|
||||||
this.after('ChatRoomReceiveSuitcaseMoney', () => {
|
this.after('ChatRoomReceiveSuitcaseMoney', () => {
|
||||||
if (this.AUTOHACK_ENABLED && this.LAST_HACKED) {
|
if (this.AUTOHACK_ENABLED && this.LAST_HACKED) {
|
||||||
w.CurrentCharacter = this.cid2char(this.LAST_HACKED)
|
W.CurrentCharacter = this.cid2char(this.LAST_HACKED)
|
||||||
this.LAST_HACKED = null
|
this.LAST_HACKED = null
|
||||||
w.ChatRoomTryToTakeSuitcase()
|
W.ChatRoomTryToTakeSuitcase()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.before('ChatRoomSendChat', () => {
|
this.before('ChatRoomSendChat', () => {
|
||||||
let input = w.ElementValue('InputChat')
|
let input = W.ElementValue('InputChat')
|
||||||
if (!input.startsWith('@@@') && input.startsWith('@')) {
|
if (!input.startsWith('@@@') && input.startsWith('@')) {
|
||||||
input = input.replace(this.RE_PREF_ACTIVITY, this.PREF_ACTIVITY)
|
input = input.replace(this.RE_PREF_ACTIVITY, this.PREF_ACTIVITY)
|
||||||
input = input.replace(this.RE_PREF_ACTIVITY_ME, this.replace_me)
|
input = input.replace(this.RE_PREF_ACTIVITY_ME, this.replace_me)
|
||||||
w.ElementValue('InputChat', input)
|
W.ElementValue('InputChat', input)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.after('ChatRoomSendChat', () => {
|
this.after('ChatRoomSendChat', () => {
|
||||||
const history = w.ChatRoomLastMessage
|
const history = W.ChatRoomLastMessage
|
||||||
if ((history.length > 1) && (history.at(-1) === history.at(-2))) {
|
if ((history.length > 1) && (history.at(-1) === history.at(-2))) {
|
||||||
history.pop()
|
history.pop()
|
||||||
w.ChatRoomLastMessageIndex -= 1
|
W.ChatRoomLastMessageIndex -= 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.before('ChatRoomCharacterViewDrawOverlay', (C, CharX, CharY, Zoom, _Pos) => {
|
this.before('ChatRoomCharacterViewDrawOverlay', (C, CharX, CharY, Zoom, _Pos) => {
|
||||||
// if (w.ChatRoomHideIconState < 1 && C.MBCHC) {
|
// if (w.ChatRoomHideIconState < 1 && C.MBCHC) {
|
||||||
// w.DrawRect(CharX + (175 * Zoom), CharY, 50 * Zoom, 50 * Zoom, C.MBCHC.VERSION === w.Player.MBCHC.VERSION ? this.RGB_POLLY : this.RGB_MUTE)
|
// w.DrawRect(CharX + (175 * Zoom), CharY, 50 * Zoom, 50 * Zoom, C.MBCHC.VERSION === w.Player.MBCHC.VERSION ? this.RGB_POLLY : this.RGB_MUTE)
|
||||||
// }
|
// }
|
||||||
if (w.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && typeof C.MBCHC_LOCAL.TZ === 'number') {
|
const ctz = TZ.for(C)
|
||||||
const hours = new Date(w.CommonTime() + this.UTC_OFFSET + (C.MBCHC_LOCAL.TZ * 60 * 60 * 1000)).getHours()
|
if (W.ChatRoomHideIconState < 1 && ctz !== undefined) {
|
||||||
w.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black')
|
const hours = new Date(W.CommonTime() + this.UTC_OFFSET + (ctz * 60 * 60 * 1000)).getHours()
|
||||||
|
W.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.after('ChatRoomCreateElement', () => this.COMP_HINT.parentElement || document.body.append(this.COMP_HINT))
|
this.after('ChatRoomCreateElement', () => this.COMP_HINT.parentElement || D.body.append(this.COMP_HINT))
|
||||||
this.before('ChatRoomClearAllElements', () => {
|
this.before('ChatRoomClearAllElements', () => {
|
||||||
this.comp_hint_hide()
|
this.comp_hint_hide()
|
||||||
this.COMP_HINT.remove()
|
this.COMP_HINT.remove()
|
||||||
|
@ -708,33 +694,33 @@ w.MBCHC = {
|
||||||
this.comp_hint_hide()
|
this.comp_hint_hide()
|
||||||
})
|
})
|
||||||
this.after('ChatRoomResize', () => {
|
this.after('ChatRoomResize', () => {
|
||||||
if (w.CharacterGetCurrent() === null && w.CurrentScreen === 'ChatRoom' && document.querySelector('#InputChat') && document.querySelector('#TextAreaChatLog') && this.comp_hint_visible()) { // Upstream
|
if (W.CharacterGetCurrent() === null && W.CurrentScreen === 'ChatRoom' && D.querySelector('#InputChat') && D.querySelector('#TextAreaChatLog') && this.comp_hint_visible()) { // Upstream
|
||||||
const fontsize = ChatRoomFontSize /* eslint-disable-line no-undef */ // window.ChatRoomFontSize is undefined
|
const fontsize = ChatRoomFontSize
|
||||||
//w.ElementPositionFix('TextAreaChatLog', fontsize, 1005, 66, 988, 630)
|
//w.ElementPositionFix('TextAreaChatLog', fontsize, 1005, 66, 988, 630)
|
||||||
//w.ElementPositionFix(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
|
//w.ElementPositionFix(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
|
||||||
w.ElementPositionFix(this.COMP_HINT.id, fontsize, 800, 65, 200, 835)
|
W.ElementPositionFix(this.COMP_HINT.id, fontsize, 800, 65, 200, 835)
|
||||||
//this.COMP_HINT.style.display = 'flex'
|
//this.COMP_HINT.style.display = 'flex'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
document.addEventListener('keydown', event => this.focus_chat(event))
|
D.addEventListener('keydown', event => this.focus_chat(event))
|
||||||
this.SDK.hookFunction('ChatRoomKeyDown', 0, (nextargs, next) => { // This fires on chat input events
|
this.SDK.hookFunction('ChatRoomKeyDown', 0, (nextargs, next) => { // This fires on chat input events
|
||||||
const [event] = nextargs
|
const [event] = nextargs
|
||||||
w.MBCHC.comp_hint_hide()
|
W.MBCHC.comp_hint_hide()
|
||||||
if ((w.KeyPress === 33) || (w.KeyPress === 34)) { // Better history
|
if ((W.KeyPress === 33) || (W.KeyPress === 34)) { // Better history
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return (w.MBCHC.history(w.KeyPress - 33))
|
return (W.MBCHC.history(W.KeyPress - 33))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w.MBCHC.HISTORY_MODE) {
|
if (W.MBCHC.HISTORY_MODE) {
|
||||||
w.ChatRoomLastMessage.pop()
|
W.ChatRoomLastMessage.pop()
|
||||||
w.MBCHC.HISTORY_MODE = false
|
W.MBCHC.HISTORY_MODE = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return (next(nextargs))
|
return (next(nextargs))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Chat room handlers
|
// Chat room handlers
|
||||||
w.ChatRoomRegisterMessageHandler({Priority: -220, Description: 'MBCHC preprocessor', Callback: (data, sender, message, metadata) => {
|
W.ChatRoomRegisterMessageHandler({Priority: -220, Description: 'MBCHC preprocessor', Callback: (data, sender, message, metadata) => {
|
||||||
data.MBCHC_ID = this.NEXT_MESSAGE
|
data.MBCHC_ID = this.NEXT_MESSAGE
|
||||||
this.NEXT_MESSAGE += 1
|
this.NEXT_MESSAGE += 1
|
||||||
if (this.LOG_MESSAGES) console.debug({data, sender, msg: message, metadata})
|
if (this.LOG_MESSAGES) console.debug({data, sender, msg: message, metadata})
|
||||||
|
@ -742,7 +728,7 @@ w.MBCHC = {
|
||||||
}})
|
}})
|
||||||
//w.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC room enter hook',
|
//w.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC room enter hook',
|
||||||
// Callback: (data, _sender, _message, _metadata) => {
|
// Callback: (data, _sender, _message, _metadata) => {
|
||||||
// if ((data.Type === 'Action') && (data.Content === 'ServerEnter') && (data.Sender === cid(w.Player))) this.player_enters_room()
|
// if ((data.Type === 'Action') && (data.Content === 'ServerEnter') && (data.Sender === U.cid(w.Player))) this.player_enters_room()
|
||||||
// return false
|
// return false
|
||||||
// },
|
// },
|
||||||
//})
|
//})
|
||||||
|
@ -752,7 +738,7 @@ w.MBCHC = {
|
||||||
// return false
|
// return false
|
||||||
// },
|
// },
|
||||||
//})
|
//})
|
||||||
w.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC autohack lookup',
|
W.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC autohack lookup',
|
||||||
Callback: (data, _sender, _message, _metadata) => {
|
Callback: (data, _sender, _message, _metadata) => {
|
||||||
if ((data.Type === 'Hidden') && (data.Content === 'ReceiveSuitcaseMoney')) this.LAST_HACKED = data.Sender
|
if ((data.Type === 'Hidden') && (data.Content === 'ReceiveSuitcaseMoney')) this.LAST_HACKED = data.Sender
|
||||||
return false
|
return false
|
||||||
|
@ -762,15 +748,15 @@ w.MBCHC = {
|
||||||
// Footer
|
// Footer
|
||||||
this.LOADED = true
|
this.LOADED = true
|
||||||
this.log('info', `loaded version ${this.VERSION}`)
|
this.log('info', `loaded version ${this.VERSION}`)
|
||||||
if ((w.CurrentModule === 'Online') && (w.CurrentScreen === 'ChatRoom')) {
|
//if ((W.CurrentModule === 'Online') && (W.CurrentScreen === 'ChatRoom')) {
|
||||||
for (const c of w.ChatRoomCharacter) this.update_char(c)
|
// for (const c of W.ChatRoomCharacter) this.update_char(c)
|
||||||
//this.player_enters_room()
|
// //this.player_enters_room()
|
||||||
}
|
//}
|
||||||
},
|
},
|
||||||
preloader() {
|
preloader() {
|
||||||
if (!w.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading')
|
if (!W.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading')
|
||||||
if (!w.bcModSdk) throw new Error('SDK not found, please load with (or after) FUSAM')
|
if (!W.bcModSdk) throw new Error('SDK not found, please load with (or after) FUSAM')
|
||||||
this.SDK = w.bcModSdk.registerMod({name: 'MBCHC', fullName: 'Mute\'s Bondage Club Hacks Collection', version: this.VERSION, repository: 'https://code.fleshless.org/mute/MBCHC/'})
|
this.SDK = W.bcModSdk.registerMod({name: 'MBCHC', fullName: 'Mute\'s Bondage Club Hacks Collection', version: this.VERSION, repository: 'https://code.fleshless.org/mute/MBCHC/'})
|
||||||
this.before = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs, next) => {
|
this.before = (name, cb) => this.SDK.hookFunction(name, 0, (nextargs, next) => {
|
||||||
try {
|
try {
|
||||||
cb?.(...nextargs)
|
cb?.(...nextargs)
|
||||||
|
@ -788,13 +774,9 @@ w.MBCHC = {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
// for some reason many (not all) hooks don't work if the mod is loaded in the room
|
|
||||||
// to be honest I have no idea what's going on. the hooks get registered, they just don't get called by SDK.
|
U.current() === 'Character/Login' ? this.remove_load_hook = this.before('AsylumGGTSSAddItems', () => this.loader()) :this.loader()
|
||||||
if (current() === 'Online/ChatRoom') throw new Error('please do not load in a chat room')
|
|
||||||
if (current() === 'Character/Login') {
|
|
||||||
this.remove_load_hook = this.before('AsylumGGTSSAddItems', () => this.loader())
|
|
||||||
} else this.loader()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
w.MBCHC.preloader()
|
W.MBCHC.preloader()
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "mbchc",
|
"name": "mbchc",
|
||||||
"version": "0.0.12",
|
"version": "105.13.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mbchc",
|
"name": "mbchc",
|
||||||
"version": "0.0.12",
|
"version": "105.13.0",
|
||||||
"license": "SEE LICENSE IN LICENSE.",
|
"license": "SEE LICENSE IN LICENSE.",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bc-stubs": "^105.0.0",
|
"bc-stubs": "^105.0.0",
|
||||||
|
|
51
package.json
51
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mbchc",
|
"name": "mbchc",
|
||||||
"version": "0.0.12",
|
"version": "105.13.0",
|
||||||
"description": "Mute's Bondage Club Hacks Collection",
|
"description": "Mute's Bondage Club Hacks Collection",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -9,12 +9,37 @@
|
||||||
"bondage-club-mod-sdk": "^1.2.0"
|
"bondage-club-mod-sdk": "^1.2.0"
|
||||||
},
|
},
|
||||||
"license": "SEE LICENSE IN LICENSE.",
|
"license": "SEE LICENSE IN LICENSE.",
|
||||||
"xo": {
|
"eslintConfig": {
|
||||||
"env": [
|
"root": true,
|
||||||
"browser",
|
"extends": [
|
||||||
"node"
|
"xo",
|
||||||
|
"xo-typescript"
|
||||||
],
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": { "project": ["./jsconfig.json"] },
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"globals": {"GM": true, "GM_info": true},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"@typescript-eslint/brace-style": "off",
|
||||||
|
"@typescript-eslint/comma-dangle": ["error", "only-multiline"],
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "off",
|
||||||
|
"@typescript-eslint/consistent-type-imports": "off",
|
||||||
|
"@typescript-eslint/member-delimiter-style": "off",
|
||||||
|
"@typescript-eslint/naming-convention": "off",
|
||||||
|
"@typescript-eslint/no-confusing-void-expression": ["error", {
|
||||||
|
"ignoreVoidOperator": true
|
||||||
|
}],
|
||||||
|
"@typescript-eslint/no-meaningless-void-operator": "off",
|
||||||
|
"@typescript-eslint/object-curly-spacing": "off",
|
||||||
|
"@typescript-eslint/padding-line-between-statements": "off",
|
||||||
|
"@typescript-eslint/semi": "off",
|
||||||
|
"@typescript-eslint/space-before-function-paren": "off",
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": ["error", {
|
||||||
|
"allowString": false,
|
||||||
|
"allowNumber": false,
|
||||||
|
"allowNullableObject": false
|
||||||
|
}],
|
||||||
|
"array-element-newline": "off",
|
||||||
"brace-style": "off",
|
"brace-style": "off",
|
||||||
"camelcase": "off",
|
"camelcase": "off",
|
||||||
"capitalized-comments": "off",
|
"capitalized-comments": "off",
|
||||||
|
@ -28,11 +53,27 @@
|
||||||
"argsIgnorePattern": "^_",
|
"argsIgnorePattern": "^_",
|
||||||
"destructuredArrayIgnorePattern": "^_"
|
"destructuredArrayIgnorePattern": "^_"
|
||||||
}],
|
}],
|
||||||
|
"no-void": "off",
|
||||||
"padding-line-between-statements": "off",
|
"padding-line-between-statements": "off",
|
||||||
|
"object-curly-newline": "off",
|
||||||
|
"one-var": "off",
|
||||||
|
"one-var-declaration-per-line": "off",
|
||||||
"semi": "off",
|
"semi": "off",
|
||||||
|
"space-before-function-paren": "off",
|
||||||
"spaced-comment": "off",
|
"spaced-comment": "off",
|
||||||
|
"unicorn/no-array-for-each": "off",
|
||||||
"unicorn/no-array-reduce": "off",
|
"unicorn/no-array-reduce": "off",
|
||||||
|
"unicorn/prefer-module": "off",
|
||||||
|
"unicorn/prefer-top-level-await": "off",
|
||||||
"fake/fuck-commas": "off"
|
"fake/fuck-commas": "off"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.d.ts"],
|
||||||
|
"rules": {
|
||||||
|
"no-unused-vars": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
10
typedef.d.ts
vendored
10
typedef.d.ts
vendored
|
@ -1,10 +0,0 @@
|
||||||
/* eslint-disable no-unused-vars */
|
|
||||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
|
||||||
|
|
||||||
interface PlayerOnlineSettings {
|
|
||||||
MBCHC?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ServerChatRoomMessage {
|
|
||||||
MBCHC_ID?: number;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user