dev.12: R105 fixes
a massive diff due to infrastructure work the completion pane moved to the left of chat no other intended changes for users
This commit is contained in:
parent
b0961f4fb8
commit
90231cb2ae
18
jsconfig.json
Normal file
18
jsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"node_modules/bc-stubs/bc/**/*.d.ts",
|
||||||
|
"node_modules/bondage-club-mod-sdk/dist/**/*.d.ts",
|
||||||
|
"typedef.d.ts",
|
||||||
|
"mbchc.mjs",
|
||||||
|
"server.js"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"es2022",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"checkJs": true,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitOverride": true
|
||||||
|
}
|
||||||
|
}
|
486
mbchc.mjs
486
mbchc.mjs
|
@ -1,11 +1,45 @@
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
// Zero-width non-joiner, used to break up ligatures, does nothing here, but an empty string is a falsy value
|
/** @typedef {import('bondage-club-mod-sdk').ModSDKGlobalAPI} ModSDKGlobalAPI */
|
||||||
|
|
||||||
|
/** @type {Window & typeof globalThis & {MBCHC?: any, bcModSdk?: ModSDKGlobalAPI, bce_ActivityTriggers?: any, bce_EventExpressions?: any}} */
|
||||||
|
const w = window
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A silly helper to memorise values in callbacks
|
||||||
|
* @template V, R
|
||||||
|
* @param {V} v Value to memorise
|
||||||
|
* @param {function(V): R} cb Callback
|
||||||
|
* @returns {R} Return value of the callback
|
||||||
|
*/
|
||||||
|
const take = (v, cb) => cb(v)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a DOM query and passes the element's style into a callback, returning its result
|
||||||
|
* @template T
|
||||||
|
* @param {string} q Query
|
||||||
|
* @param {function(CSSStyleDeclaration): T} cb Callback
|
||||||
|
* @returns {T | void} Return value of the callback, if it was called
|
||||||
|
*/
|
||||||
|
const style = (q, cb) => take(document.querySelector(q), E => E && E instanceof HTMLElement && E.style ? cb(E.style) : undefined)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string} Current view
|
||||||
|
*/
|
||||||
|
const current = () => `${w.CurrentModule}/${w.CurrentScreen}`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Character} char
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
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
|
||||||
const MISSING_PLAYER_DIALOG = {Tag: 'MISSING TEXT IN "Interface.csv": ', Text: '\u200C'}
|
const MISSING_PLAYER_DIALOG = {Tag: 'MISSING TEXT IN "Interface.csv": ', Text: '\u200C'}
|
||||||
|
|
||||||
if (window.MBCHC) throw new Error('MBCHC found, aborting loading')
|
if (w.MBCHC) throw new Error('MBCHC found, aborting loading')
|
||||||
window.MBCHC = {
|
w.MBCHC = {
|
||||||
VERSION: 'dev.11',
|
VERSION: 'dev.12',
|
||||||
NEXT_MESSAGE: 1,
|
NEXT_MESSAGE: 1,
|
||||||
LOG_MESSAGES: false,
|
LOG_MESSAGES: false,
|
||||||
RETHROW: false,
|
RETHROW: false,
|
||||||
|
@ -13,7 +47,6 @@ window.MBCHC = {
|
||||||
AUTOHACK_ENABLED: false,
|
AUTOHACK_ENABLED: false,
|
||||||
LAST_HACKED: null,
|
LAST_HACKED: null,
|
||||||
HISTORY_MODE: false,
|
HISTORY_MODE: false,
|
||||||
// RE_TITLE: /^[a-zA-Z]+$/,
|
|
||||||
RE_PREF_ACTIVITY_ME: /^@/,
|
RE_PREF_ACTIVITY_ME: /^@/,
|
||||||
RE_PREF_ACTIVITY: /^@@/,
|
RE_PREF_ACTIVITY: /^@@/,
|
||||||
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
|
RE_ACT_CIDS: /^<(\d+)?:(\d+)?>/,
|
||||||
|
@ -26,62 +59,6 @@ window.MBCHC = {
|
||||||
RGB_MUTE: '#6c2132',
|
RGB_MUTE: '#6c2132',
|
||||||
RGB_POLLY: '#81b1e7',
|
RGB_POLLY: '#81b1e7',
|
||||||
UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000,
|
UTC_OFFSET: new Date().getTimezoneOffset() * 60 * 1000,
|
||||||
// HIDE_SPECIAL: ['Activity', 'Emoticon'],
|
|
||||||
// HIDE_BODY: ['Blush', 'BodyLower', 'BodyUpper', 'Eyebrows', 'Eyes', 'Eyes2', 'Face', 'Fluids', 'HairBack', 'HairFront', 'Hands', 'Head', 'LeftHand', 'Mouth', 'Nipples', 'Pussy', 'RightHand'],
|
|
||||||
// HIDE_CLOTHES: [
|
|
||||||
// 'Cloth',
|
|
||||||
// 'ClothAccessory',
|
|
||||||
// 'Necklace',
|
|
||||||
// 'Suit',
|
|
||||||
// 'ClothLower',
|
|
||||||
// 'SuitLower',
|
|
||||||
// 'Bra',
|
|
||||||
// 'Corset',
|
|
||||||
// 'Panties',
|
|
||||||
// 'Socks',
|
|
||||||
// 'RightAnklet',
|
|
||||||
// 'LeftAnklet',
|
|
||||||
// 'Garters',
|
|
||||||
// 'Shoes',
|
|
||||||
// 'Hat',
|
|
||||||
// 'HairAccessory3',
|
|
||||||
// 'HairAccessory1',
|
|
||||||
// 'HairAccessory2',
|
|
||||||
// 'Gloves',
|
|
||||||
// 'Bracelet',
|
|
||||||
// 'Glasses',
|
|
||||||
// 'Mask',
|
|
||||||
// 'TailStraps',
|
|
||||||
// 'Wings',
|
|
||||||
// ],
|
|
||||||
// HIDE_ITEMS: [
|
|
||||||
// 'ItemMisc',
|
|
||||||
// 'ItemEars',
|
|
||||||
// 'ItemHead',
|
|
||||||
// 'ItemNose',
|
|
||||||
// 'ItemHood',
|
|
||||||
// 'ItemAddon',
|
|
||||||
// 'ItemMouth',
|
|
||||||
// 'ItemMouth2',
|
|
||||||
// 'ItemMouth3',
|
|
||||||
// 'ItemArms',
|
|
||||||
// 'ItemNeckAccessories',
|
|
||||||
// 'ItemNeck',
|
|
||||||
// 'ItemNeckRestraints',
|
|
||||||
// 'ItemNipples',
|
|
||||||
// 'ItemNipplesPiercings',
|
|
||||||
// 'ItemBreast',
|
|
||||||
// 'ItemTorso',
|
|
||||||
// 'ItemTorso2',
|
|
||||||
// 'ItemHands',
|
|
||||||
// 'ItemPelvis',
|
|
||||||
// 'ItemVulva',
|
|
||||||
// 'ItemVulvaPiercings',
|
|
||||||
// 'ItemDevices',
|
|
||||||
// 'ItemLegs',
|
|
||||||
// 'ItemFeet',
|
|
||||||
// 'ItemBoots',
|
|
||||||
// ],
|
|
||||||
MAP_ACTIONS: { // ActivityFemale3DCG
|
MAP_ACTIONS: { // ActivityFemale3DCG
|
||||||
// action
|
// action
|
||||||
'nod|yes': {Head: {self: 'Nod'}},
|
'nod|yes': {Head: {self: 'Nod'}},
|
||||||
|
@ -183,22 +160,19 @@ window.MBCHC = {
|
||||||
[/([^\\])\$/g, '$1\\.?$$'],
|
[/([^\\])\$/g, '$1\\.?$$'],
|
||||||
],
|
],
|
||||||
SUBCOMMANDS_MBCHC: {
|
SUBCOMMANDS_MBCHC: {
|
||||||
// versions: {desc: 'show the mod versions across the room', cb: mbchc => mbchc.inform(mbchc.gather_versions().map(c => `<div><b>${c.name}</b> (${c.cid}): ${c.version}</div>`).join(''))},
|
|
||||||
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
|
||||||
// disappear: {desc: 'become invisible (requires anal hook -> hair)', cb: mbchc => mbchc.disappear()},
|
|
||||||
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])},
|
||||||
// title: {desc: 'set a custom title (<b>WIP</b>)', args: {TITLE: {}}, cb: (mbchc, args) => mbchc.title(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(mbchc) {
|
||||||
if (window.Player.OnlineSettings.MBCHC) {
|
if (w.Player.OnlineSettings.MBCHC) {
|
||||||
delete window.Player.OnlineSettings.MBCHC
|
delete w.Player.OnlineSettings.MBCHC
|
||||||
mbchc.save_settings()
|
mbchc.save_settings()
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
ensure(error, callback) {
|
ensure(text, callback) {
|
||||||
const result = callback.call(this)
|
const result = callback.call(this)
|
||||||
if (!result) throw error
|
if (!result) throw new Error(text)
|
||||||
return (result)
|
return (result)
|
||||||
},
|
},
|
||||||
calculate_maps() {
|
calculate_maps() {
|
||||||
|
@ -215,15 +189,15 @@ window.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) {
|
settings(setting = null) {
|
||||||
const settings = window.Player.OnlineSettings.MBCHC || {}
|
const settings = w.Player.OnlineSettings.MBCHC || {}
|
||||||
return (setting ? settings[setting] : settings)
|
return (setting ? settings[setting] : settings)
|
||||||
},
|
},
|
||||||
save_settings(cb = null) {
|
save_settings(cb = null) {
|
||||||
if (cb) {
|
if (cb) {
|
||||||
if (!window.Player.OnlineSettings.MBCHC) window.Player.OnlineSettings.MBCHC = {}
|
if (!w.Player.OnlineSettings.MBCHC) w.Player.OnlineSettings.MBCHC = {}
|
||||||
cb.call(this, window.Player.OnlineSettings.MBCHC)
|
cb.call(this, w.Player.OnlineSettings.MBCHC)
|
||||||
}
|
}
|
||||||
window.ServerAccountUpdate.QueueData({OnlineSettings: window.Player.OnlineSettings})
|
w.ServerAccountUpdate.QueueData({OnlineSettings: w.Player.OnlineSettings})
|
||||||
},
|
},
|
||||||
log(level, message) {
|
log(level, message) {
|
||||||
console[level]('MBCHC: ' + String(message))
|
console[level]('MBCHC: ' + String(message))
|
||||||
|
@ -249,37 +223,37 @@ window.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) {
|
||||||
window.ChatRoomSendLocal(`<div class="mbchc">${html}</div>`, timeout)
|
w.ChatRoomSendLocal(`<div class="mbchc">${html}</div>`, timeout)
|
||||||
},
|
},
|
||||||
report(x) {
|
report(x) {
|
||||||
this.inform(`Error: ${x.toString()}`)
|
this.inform(`${x.toString()}`)
|
||||||
if (this.RETHROW) throw x
|
if (this.RETHROW) throw x
|
||||||
},
|
},
|
||||||
in(x, floor, ceiling) {
|
in(x, floor, ceiling) {
|
||||||
return ((x >= floor) && (x <= ceiling))
|
return ((x >= floor) && (x <= ceiling))
|
||||||
},
|
},
|
||||||
cid2char(cid) {
|
cid2char(id) {
|
||||||
cid = Number.parseInt(cid, 10)
|
id = Number.parseInt(id, 10)
|
||||||
if (cid === window.Player.cid) return (window.Player)
|
if (id === cid(w.Player)) return (w.Player)
|
||||||
return (this.ensure(`character ${cid} not found in the room`, () => window.ChatRoomCharacter.find(c => c.cid === cid)))
|
return (this.ensure(`character ${id} not found in the room`, () => w.ChatRoomCharacter.find(c => cid(c) === id)))
|
||||||
},
|
},
|
||||||
pos2char(pos) {
|
pos2char(pos) {
|
||||||
if (!this.in(pos, 0, window.ChatRoomCharacter.length - 1)) throw new Error(`invalid position ${pos}`)
|
if (!this.in(pos, 0, w.ChatRoomCharacter.length - 1)) throw new Error(`invalid position ${pos}`)
|
||||||
return (window.ChatRoomCharacter[pos])
|
return (w.ChatRoomCharacter[pos])
|
||||||
},
|
},
|
||||||
rel2char(target) {
|
rel2char(target) {
|
||||||
const me = this.ensure('can\'t find my position', () => window.ChatRoomCharacter.findIndex(char => char.IsPlayer()) + 1) - 1 // 0 is falsy, but is valid index
|
const me = this.ensure('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 %= window.ChatRoomCharacter.length
|
pos %= w.ChatRoomCharacter.length
|
||||||
if (pos < 0) pos += window.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 (window.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 = []
|
||||||
|
@ -289,25 +263,25 @@ window.MBCHC = {
|
||||||
if (this.in(int, 0, 9)) return (this.pos2char(int))
|
if (this.in(int, 0, 9)) return (this.pos2char(int))
|
||||||
if (this.in(int, 11, 15)) return (this.pos2char(int - 11))
|
if (this.in(int, 11, 15)) return (this.pos2char(int - 11))
|
||||||
if (this.in(int, 21, 25)) return (this.pos2char(int - 16))
|
if (this.in(int, 21, 25)) return (this.pos2char(int - 16))
|
||||||
found.push(...window.ChatRoomCharacter.filter(c => c.cid.toString().includes(target)))
|
found.push(...w.ChatRoomCharacter.filter(c => cid(c).toString().includes(target)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.startsWith('@')) target = target.slice(1)
|
if (target.startsWith('@')) target = target.slice(1)
|
||||||
found.push(...window.ChatRoomCharacter.filter(c => c.Name.toLocaleLowerCase().includes(target)))
|
found.push(...w.ChatRoomCharacter.filter(c => c.Name.toLocaleLowerCase().includes(target)))
|
||||||
found.push(...window.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[c.cid]) map[c.cid] = c
|
if (!map[cid(c)]) map[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 => `${c.cid}|${c.Name}|${c.Nickname || c.Name}`).join(',')})`)
|
if (found.length > 1) throw new Error(`target "${input}": multiple matches (${found.map(c => `${cid(c)}|${c.Name}|${c.Nickname || c.Name}`).join(',')})`)
|
||||||
return (found[0])
|
return (found[0])
|
||||||
},
|
},
|
||||||
char2targets(char) {
|
char2targets(char) {
|
||||||
const [result, cid] = [new Set(), char.cid.toString()]
|
const [result, id] = [new Set(), cid(char).toString()]
|
||||||
result.add(cid).add(`=${cid}`)
|
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)
|
||||||
result.add(`@${t}`)
|
result.add(`@${t}`)
|
||||||
|
@ -325,21 +299,22 @@ window.MBCHC = {
|
||||||
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 (window.Player.Money < cost) throw new Error('not enough money')
|
if (w.Player.Money < cost) throw new Error('not enough money')
|
||||||
window.CharacterChangeMoney(window.Player, -cost)
|
w.CharacterChangeMoney(w.Player, -cost)
|
||||||
window.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: char.cid})
|
w.ServerSend('ChatRoomChat', {Content: 'ReceiveSuitcaseMoney', Type: 'Hidden', Target: cid(char)})
|
||||||
window.ChatRoomMessage({Sender: window.Player.cid, Type: 'Action', Content: `You've bought data for $${cost} and sent it to ${char.dn}.`, Dictionary: [MISSING_PLAYER_DIALOG]})
|
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]})
|
||||||
},
|
},
|
||||||
run_activity(char, ag, action) {
|
run_activity(char, ag, action) {
|
||||||
try {
|
try {
|
||||||
if (!window.ActivityAllowed()) throw new Error('activities disabled in this room')
|
if (!w.ActivityAllowed()) throw new Error('activities disabled in this room')
|
||||||
if (!window.ServerChatRoomGetAllowItem(window.Player, char)) throw new Error('no permissions')
|
if (!w.ServerChatRoomGetAllowItem(w.Player, char)) throw new Error('no permissions')
|
||||||
char.FocusGroup = this.ensure('invalid AssetGroup', () => window.AssetGroupGet(char.AssetFamily, ag))
|
char.FocusGroup = this.ensure('invalid AssetGroup', () => w.AssetGroupGet(char.AssetFamily, ag))
|
||||||
const activity = this.ensure('invalid activity', () => window.ActivityAllowedForGroup(char, char.FocusGroup.Name, true).find(a => a.Name === action || a.Activity?.Name === action))
|
const activity = this.ensure('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', () => window.Player.Inventory.find(i => i.Asset?.Name === 'SpankingToys' && i.Asset.Group?.Name === char.FocusGroup.Name && window.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)))
|
||||||
window.DialogPublishAction(char, item)
|
// w.DialogPublishAction(char, item)
|
||||||
} else window.ActivityRun(window.Player, char, char.FocusGroup, activity)
|
//} else w.ActivityRun(w.Player, char, char.FocusGroup, activity)
|
||||||
|
w.ActivityRun(w.Player, char, char.FocusGroup, activity)
|
||||||
} finally {
|
} finally {
|
||||||
char.FocusGroup = null
|
char.FocusGroup = null
|
||||||
}
|
}
|
||||||
|
@ -348,7 +323,7 @@ window.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 `${window.MBCHC.PREF_ACTIVITY}<${window.Player.cid}:>SourceCharacter${suffix}`
|
return `${w.MBCHC.PREF_ACTIVITY}<${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: this.cid2char(cid).dn})
|
||||||
|
@ -361,44 +336,28 @@ window.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]))
|
||||||
}
|
}
|
||||||
window.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)
|
||||||
if (char.IsPlayer()) return true // This is our own message, sent back to us
|
// if (char.IsPlayer()) return true // This is our own message, sent back to us
|
||||||
const payload = this.ensure('Empty message', () => data.Dictionary[0])
|
// const payload = this.ensure('Empty message', () => data.Dictionary[0])
|
||||||
switch (payload.type) {
|
// switch (payload.type) {
|
||||||
case 'greetings': case 'hello': {
|
// case 'greetings': case 'hello': {
|
||||||
char.MBCHC = payload.value
|
// char.MBCHC = payload.value
|
||||||
if (payload.type === 'greetings') this.hello(char)
|
// if (payload.type === 'greetings') this.hello(char)
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
default: // If we don't know the type it may be from a newer version
|
// default: // If we don't know the type it may be from a newer version
|
||||||
}
|
// }
|
||||||
return true
|
// return true
|
||||||
},
|
|
||||||
hello(char = null) {
|
|
||||||
const payload = {type: 'greetings', value: window.Player.MBCHC}
|
|
||||||
if (char) payload.type = 'hello'
|
|
||||||
const message = {Content: 'MBCHC', Type: 'Hidden', Dictionary: [payload]}
|
|
||||||
if (char) message.Target = char.cid
|
|
||||||
window.ServerSend('ChatRoomChat', message)
|
|
||||||
},
|
|
||||||
// disappear() {
|
|
||||||
// const item = window.InventoryGet(window.Player, 'ItemButt')
|
|
||||||
// if (!item || !item.Asset || !item.Asset.Name) throw new Error('butt seems empty')
|
|
||||||
// if (item.Asset.Name !== 'AnalHook') throw new Error('butt seems occupied by something other than the anal hook')
|
|
||||||
// if (!item.Property.Type || item.Property.Type !== 'Hair') throw new Error('anal hook seems not tied to hair')
|
|
||||||
// item.Property = {Type: 'Hair', Hide: this.HIDE_ALL}
|
|
||||||
// window.CharacterRefresh(window.Player, true, true)
|
|
||||||
//},
|
//},
|
||||||
// title(title) { // WIP
|
//hello(char = null) {
|
||||||
// if (this.empty(title)) throw new Error('empty title')
|
// const payload = {type: 'greetings', value: w.Player.MBCHC}
|
||||||
// title = this.normalise_message(title, {trim: true, up: true, low: true})
|
// if (char) payload.type = 'hello'
|
||||||
// if (title.length > 16) throw new Error('title too long')
|
// const message = {Content: 'MBCHC', Type: /** @type {const} */ ('Hidden'), Dictionary: [payload]}
|
||||||
// if (!this.RE_TITLE.test(title)) throw new Error('invalid title')
|
// if (char) message.Target = char.cid
|
||||||
// window.TitleSet(title)
|
// w.ServerSend('ChatRoomChat', message)
|
||||||
// // Window.TitleList.push({Name: title, Requirement: () => true}) // check for existing first
|
|
||||||
//},
|
//},
|
||||||
copy_fbc_trigger(trigger) {
|
copy_fbc_trigger(trigger) {
|
||||||
const result = {
|
const result = {
|
||||||
|
@ -411,27 +370,24 @@ window.MBCHC = {
|
||||||
patch_fbc() {
|
patch_fbc() {
|
||||||
this.remove_fbc_hook()
|
this.remove_fbc_hook()
|
||||||
delete this.remove_fbc_hook
|
delete this.remove_fbc_hook
|
||||||
window.bce_ActivityTriggers.push(...window.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 = window.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 = window.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 = window.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
|
||||||
},
|
},
|
||||||
// gather_versions() {
|
|
||||||
// return (window.ChatRoomCharacter.filter(c => c.MBCHC).map(c => ({name: c.dn, cid: c.cid, version: c.MBCHC.VERSION})))
|
|
||||||
// },
|
|
||||||
find_timezone(char) {
|
find_timezone(char) {
|
||||||
const timezones = this.settings('timezones')
|
const timezones = this.settings('timezones')
|
||||||
if (timezones && typeof timezones[char.cid] === 'number') return (timezones[char.cid])
|
if (timezones && typeof timezones[cid(char)] === 'number') return (timezones[cid(char)])
|
||||||
const match = (char.Description) ? char.Description.match(this.RE_TZ) : null
|
const match = (char.Description) ? char.Description.match(this.RE_TZ) : null
|
||||||
const int = match ? Number.parseInt(match[1] + match[2], 10) : 42
|
const int = match ? Number.parseInt(match[1] + match[2], 10) : 42
|
||||||
if (this.in(int, -12, 12)) return (int)
|
if (this.in(int, -12, 12)) return (int)
|
||||||
return (null)
|
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]}"`)
|
||||||
|
@ -440,17 +396,17 @@ window.MBCHC = {
|
||||||
char.MBCHC_LOCAL.TZ = tz
|
char.MBCHC_LOCAL.TZ = tz
|
||||||
this.save_settings(s => {
|
this.save_settings(s => {
|
||||||
if (!s.timezones) s.timezones = {}
|
if (!s.timezones) s.timezones = {}
|
||||||
s.timezones[char.cid] = tz
|
s.timezones[cid(char)] = tz
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
update_char(char) {
|
update_char(char) {
|
||||||
char.cid = char.MemberNumber // Club ID (shorter)
|
//char.cid = char.MemberNumber // Club ID (shorter)
|
||||||
char.dn = window.CharacterNickname(char) // DisplayName (shortcut)
|
char.dn = w.CharacterNickname(char) // DisplayName (shortcut)
|
||||||
if (!char.MBCHC_LOCAL) char.MBCHC_LOCAL = {}
|
if (!char.MBCHC_LOCAL) char.MBCHC_LOCAL = {}
|
||||||
if (!char.MBCHC_LOCAL.TZ) char.MBCHC_LOCAL.TZ = this.find_timezone(char)
|
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 = window.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())
|
||||||
|
@ -461,7 +417,7 @@ window.MBCHC = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
command_activity(argline, cmdline, _) {
|
command_activity(argline, cmdline, _) {
|
||||||
const mbchc = window.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})
|
||||||
|
@ -472,7 +428,7 @@ window.MBCHC = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
command_do(argline, cmdline, args) {
|
command_do(argline, cmdline, args) {
|
||||||
const mbchc = window.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
|
||||||
|
@ -485,14 +441,14 @@ window.MBCHC = {
|
||||||
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 = mbchc.ensure(`unknown zone "${zone}"`, () => mbchc.DO_DATA.zones[zone])
|
||||||
const types = mbchc.ensure(`zone "${zone}" invalid for "${verb}"`, () => zones[ag])
|
const types = mbchc.ensure(`zone "${zone}" invalid for "${verb}"`, () => zones[ag])
|
||||||
let char = window.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 = window.ActivityAllowedForGroup(char, ag)
|
const available = w.ActivityAllowedForGroup(char, ag)
|
||||||
const toy = window.InventoryGet(window.Player, 'ItemHands')
|
//const toy = w.InventoryGet(w.Player, 'ItemHands')
|
||||||
if (toy && toy.Asset.Name === 'SpankingToys') available.push(window.AssetAllActivities(char.AssetFamily).find(a => a.Name === window.InventorySpankingToysGetActivity?.(window.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 = mbchc.ensure(`zone "${zone}" invalid for ("${verb}" "${type}")`, () => types[type])
|
||||||
const action = mbchc.ensure(`invalid action (${verb} ${zone} ${target})`, () => actions.find(name => available.find(a => a.Name === name || a.Activity?.Name === name)))
|
const action = mbchc.ensure(`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)
|
||||||
|
@ -500,9 +456,9 @@ window.MBCHC = {
|
||||||
},
|
},
|
||||||
bell() {
|
bell() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.querySelector('#InputChat').style.outline = ''
|
style('#InputChat', s => s.outline = '')
|
||||||
}, 100)
|
}, 100)
|
||||||
document.querySelector('#InputChat').style.outline = 'solid red'
|
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())
|
||||||
|
@ -518,29 +474,42 @@ window.MBCHC = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pref) this.complete([pref], false)
|
if (pref) this.complete([pref], false)
|
||||||
this.complete_hint(options)
|
this.comp_hint(options)
|
||||||
} else window.ElementValue('InputChat', document.querySelector('#InputChat').value.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 ? ' ' : ''}`))
|
||||||
},
|
},
|
||||||
complete_hint(options) {
|
|
||||||
this.COMP_HINT.innerHTML = options.sort().map(s => `<div>${s}</div>`).join('')
|
/**
|
||||||
this.COMP_HINT.style.display = 'flex'
|
* Displays strings as completion hint
|
||||||
window.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (window.Player.ChatSettings.ColorTheme || 'Light'))
|
* @param {string[]} options List of words to display. The order will be modified without copy.
|
||||||
const rescroll = window.ElementIsScrolledToEnd('TextAreaChatLog')
|
* @returns {void}
|
||||||
window.ChatRoomResize(false)
|
*/
|
||||||
if (rescroll) window.ElementScrollToEnd('TextAreaChatLog')
|
comp_hint(options) {
|
||||||
|
if (options.length === 0) return
|
||||||
|
this.COMP_HINT.innerHTML = '<div>' + options.sort().reverse().map(s => `<div>${s}</div>`).join('') + '</div>'
|
||||||
|
this.COMP_HINT.style.display = 'block'
|
||||||
|
w.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (w.Player.ChatSettings.ColorTheme || 'Light'))
|
||||||
|
const rescroll = w.ElementIsScrolledToEnd('TextAreaChatLog')
|
||||||
|
w.ChatRoomResize(false)
|
||||||
|
if (rescroll) w.ElementScrollToEnd('TextAreaChatLog')
|
||||||
|
this.COMP_HINT.firstChild?.lastChild?.scrollIntoView({behaviour: 'instant'})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the completion box is attached to body and its display isn't none
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
comp_hint_visible() {
|
comp_hint_visible() {
|
||||||
return (this.COMP_HINT.parentElement && this.COMP_HINT.style.display === 'flex')
|
return (this.COMP_HINT.parentElement && this.COMP_HINT.style.display !== 'none')
|
||||||
},
|
},
|
||||||
complete_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'
|
||||||
window.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 window.ChatRoomCharacter) {
|
for (const c of w.ChatRoomCharacter) {
|
||||||
if ((c.IsPlayer() && !me2) || (check_perms && !window.ServerChatRoomGetAllowItem(window.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)
|
||||||
}
|
}
|
||||||
|
@ -549,11 +518,13 @@ window.MBCHC = {
|
||||||
this.complete(Array.from(found))
|
this.complete(Array.from(found))
|
||||||
},
|
},
|
||||||
complete_common() {
|
complete_common() {
|
||||||
const input = document.querySelector('#InputChat').value
|
// w.ElementValue('InputChat') will strip the trailing whitespace
|
||||||
return ([this, input, this.tokenise(input)])
|
const E = document.querySelector('#InputChat')
|
||||||
|
if (!(E && E instanceof HTMLTextAreaElement)) throw new Error('somehow InputChat is broken')
|
||||||
|
return ([this, E.value, this.tokenise(E.value)])
|
||||||
},
|
},
|
||||||
complete_mbchc(_args, _locase, _cmdline) {
|
complete_mbchc(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = window.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()
|
||||||
|
@ -568,11 +539,11 @@ window.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([window.Player.cid.toString()])) // Target is always the player
|
if (me2 && actions.others.length === 0) return (this.complete([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] = window.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
|
||||||
|
@ -595,38 +566,38 @@ window.MBCHC = {
|
||||||
mbchc.bell()
|
mbchc.bell()
|
||||||
},
|
},
|
||||||
complete_fbc_anim(_args, _locase, _cmdline) {
|
complete_fbc_anim(_args, _locase, _cmdline) {
|
||||||
const [mbchc, _input, tokens] = window.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(window.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] = window.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(window.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] = [window.ElementValue('InputChat'), window.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
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = history.map((t, i) => [t, i]).filter(([t, _]) => t.startsWith(history.at(-1))).map(([_, i]) => 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 > window.ChatRoomLastMessageIndex : id < window.ChatRoomLastMessageIndex)
|
const found = ids.find(id => (down) ? id > w.ChatRoomLastMessageIndex : id < w.ChatRoomLastMessageIndex)
|
||||||
if (!found) return (this.bell())
|
if (!found) return (this.bell())
|
||||||
window.ElementValue('InputChat', history[found])
|
w.ElementValue('InputChat', history[found])
|
||||||
window.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 (document.activeElement === document.body) return true
|
||||||
if (document.activeElement.id !== 'MainCanvas') return false
|
if (document.activeElement.id !== 'MainCanvas') return false
|
||||||
return !window.ChatRoomMapVisible
|
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
|
||||||
|
@ -636,8 +607,8 @@ window.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 (document.querySelector('#InputChat')?.style.display !== 'inline') return // Input chat missing
|
if (style('#InputChat', s => s.display) !== 'inline') return // Input chat missing
|
||||||
window.ElementFocus('InputChat')
|
w.ElementFocus('InputChat')
|
||||||
},
|
},
|
||||||
loader() {
|
loader() {
|
||||||
if (this.remove_load_hook) {
|
if (this.remove_load_hook) {
|
||||||
|
@ -652,7 +623,6 @@ window.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.HIDE_ALL = this.HIDE_SPECIAL.concat(this.HIDE_BODY).concat(this.HIDE_CLOTHES).concat(this.HIDE_ITEMS)
|
|
||||||
this.CommandsKey = CommandsKey /* eslint-disable-line no-undef */ // window.CommandsKey is undefined
|
this.CommandsKey = CommandsKey /* eslint-disable-line no-undef */ // window.CommandsKey is undefined
|
||||||
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 `
|
||||||
|
@ -667,126 +637,140 @@ window.MBCHC = {
|
||||||
background-color: ${this.RGB_MUTE};
|
background-color: ${this.RGB_MUTE};
|
||||||
}
|
}
|
||||||
#${this.COMP_HINT.id} {
|
#${this.COMP_HINT.id} {
|
||||||
flex-flow: column wrap;
|
|
||||||
overflow: auto;
|
|
||||||
display: none;
|
display: none;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#${this.COMP_HINT.id} > div {
|
||||||
|
overflow: auto;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
max-height: 100%;
|
||||||
|
padding: 0 0.5ex;
|
||||||
background-color: ${this.RGB_POLLY};
|
background-color: ${this.RGB_POLLY};
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
#${this.COMP_HINT.id}[data-colortheme="dark"], #${this.COMP_HINT.id}[data-colortheme="dark2"] {
|
#${this.COMP_HINT.id}[data-colortheme="dark"] > div, #${this.COMP_HINT.id}[data-colortheme="dark2"] > div {
|
||||||
background-color: ${this.RGB_MUTE};
|
background-color: ${this.RGB_MUTE};
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
#${this.COMP_HINT.id} div {
|
#${this.COMP_HINT.id} > div div {
|
||||||
margin: 0 0.5ex;
|
margin: 0.25ex 0;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
document.head.append(css)
|
document.head.append(css)
|
||||||
// Actions
|
// Actions
|
||||||
this.calculate_maps()
|
this.calculate_maps()
|
||||||
window.Player.MBCHC = {VERSION: this.VERSION}
|
//w.Player.MBCHC = {VERSION: this.VERSION}
|
||||||
window.CommandCombine(COMMANDS)
|
w.CommandCombine(COMMANDS)
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
this.remove_fbc_hook = this.before('MainRun', () => window.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) {
|
||||||
window.CurrentCharacter = this.cid2char(this.LAST_HACKED)
|
w.CurrentCharacter = this.cid2char(this.LAST_HACKED)
|
||||||
this.LAST_HACKED = null
|
this.LAST_HACKED = null
|
||||||
window.ChatRoomTryToTakeSuitcase()
|
w.ChatRoomTryToTakeSuitcase()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.before('ChatRoomSendChat', () => {
|
this.before('ChatRoomSendChat', () => {
|
||||||
let input = window.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)
|
||||||
window.ElementValue('InputChat', input)
|
w.ElementValue('InputChat', input)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.after('ChatRoomSendChat', () => {
|
this.after('ChatRoomSendChat', () => {
|
||||||
const history = window.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()
|
||||||
window.ChatRoomLastMessageIndex -= 1
|
w.ChatRoomLastMessageIndex -= 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.before('ChatRoomCharacterViewDrawOverlay', (C, CharX, CharY, Zoom, _Pos) => {
|
this.before('ChatRoomCharacterViewDrawOverlay', (C, CharX, CharY, Zoom, _Pos) => {
|
||||||
// if (window.ChatRoomHideIconState < 1 && C.MBCHC) {
|
// if (w.ChatRoomHideIconState < 1 && C.MBCHC) {
|
||||||
// window.DrawRect(CharX + (175 * Zoom), CharY, 50 * Zoom, 50 * Zoom, C.MBCHC.VERSION === window.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 (window.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && typeof C.MBCHC_LOCAL.TZ === 'number') {
|
if (w.ChatRoomHideIconState < 1 && C.MBCHC_LOCAL && typeof C.MBCHC_LOCAL.TZ === 'number') {
|
||||||
const hours = new Date(window.CommonTime() + this.UTC_OFFSET + (C.MBCHC_LOCAL.TZ * 60 * 60 * 1000)).getHours()
|
const hours = new Date(w.CommonTime() + this.UTC_OFFSET + (C.MBCHC_LOCAL.TZ * 60 * 60 * 1000)).getHours()
|
||||||
window.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black')
|
w.DrawTextFit(hours < 10 ? '0' + hours.toString() : hours.toString(), CharX + (200 * Zoom), CharY + (25 * Zoom), 46 * Zoom, 'white', 'black')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// this.after('ElementValue', (ID, Value, cc = window.CurrentCharacter) => ID === 'bce_LayerPriority' && cc?.FocusGroup && window.InventoryGet(cc, cc.FocusGroup.Name)?.Difficulty.toString() === Value && window.InventoryLocked(cc, cc.FocusGroup.Name, true) && window.ElementSetAttribute(ID, 'disabled', true))
|
|
||||||
this.after('ChatRoomCreateElement', () => this.COMP_HINT.parentElement || document.body.append(this.COMP_HINT))
|
this.after('ChatRoomCreateElement', () => this.COMP_HINT.parentElement || document.body.append(this.COMP_HINT))
|
||||||
this.before('ChatRoomClearAllElements', () => {
|
this.before('ChatRoomClearAllElements', () => {
|
||||||
this.complete_hint_hide()
|
this.comp_hint_hide()
|
||||||
this.COMP_HINT.remove()
|
this.COMP_HINT.remove()
|
||||||
})
|
})
|
||||||
this.before('ChatRoomClick', () => this.complete_hint_hide())
|
this.before('ChatRoomClick', () => {
|
||||||
|
this.comp_hint_hide()
|
||||||
|
})
|
||||||
this.after('ChatRoomResize', () => {
|
this.after('ChatRoomResize', () => {
|
||||||
if (window.CharacterGetCurrent() === null && window.CurrentScreen === 'ChatRoom' && document.querySelector('#InputChat') && document.querySelector('#TextAreaChatLog') && this.comp_hint_visible()) { // Upstream
|
if (w.CharacterGetCurrent() === null && w.CurrentScreen === 'ChatRoom' && document.querySelector('#InputChat') && document.querySelector('#TextAreaChatLog') && this.comp_hint_visible()) { // Upstream
|
||||||
const fontsize = ChatRoomFontSize /* eslint-disable-line no-undef */ // window.ChatRoomFontSize is undefined
|
const fontsize = ChatRoomFontSize /* eslint-disable-line no-undef */ // window.ChatRoomFontSize is undefined
|
||||||
window.ElementPositionFix('TextAreaChatLog', fontsize, 1005, 66, 988, 630)
|
//w.ElementPositionFix('TextAreaChatLog', fontsize, 1005, 66, 988, 630)
|
||||||
window.ElementPositionFix(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
|
//w.ElementPositionFix(this.COMP_HINT.id, fontsize, 1005, 701, 988, 200)
|
||||||
this.COMP_HINT.style.display = 'flex'
|
w.ElementPositionFix(this.COMP_HINT.id, fontsize, 800, 65, 200, 835)
|
||||||
|
//this.COMP_HINT.style.display = 'flex'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
document.addEventListener('keydown', event => this.focus_chat(event))
|
document.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
|
||||||
window.MBCHC.complete_hint_hide()
|
w.MBCHC.comp_hint_hide()
|
||||||
if ((window.KeyPress === 33) || (window.KeyPress === 34)) { // Better history
|
if ((w.KeyPress === 33) || (w.KeyPress === 34)) { // Better history
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return (window.MBCHC.history(window.KeyPress - 33))
|
return (w.MBCHC.history(w.KeyPress - 33))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.MBCHC.HISTORY_MODE) {
|
if (w.MBCHC.HISTORY_MODE) {
|
||||||
window.ChatRoomLastMessage.pop()
|
w.ChatRoomLastMessage.pop()
|
||||||
window.MBCHC.HISTORY_MODE = false
|
w.MBCHC.HISTORY_MODE = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return (next(nextargs))
|
return (next(nextargs))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Chat room handlers
|
// Chat room handlers
|
||||||
window.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})
|
||||||
|
return false
|
||||||
}})
|
}})
|
||||||
window.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 === window.Player.cid)) this.player_enters_room()
|
// if ((data.Type === 'Action') && (data.Content === 'ServerEnter') && (data.Sender === cid(w.Player))) this.player_enters_room()
|
||||||
},
|
// return false
|
||||||
})
|
// },
|
||||||
window.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC specific consumer',
|
//})
|
||||||
Callback: (data, _sender, _message, _metadata) => {
|
//w.ChatRoomRegisterMessageHandler({Priority: -219, Description: 'MBCHC specific consumer',
|
||||||
if ((data.Type === 'Hidden') && (data.Content === 'MBCHC')) return this.receive(data)
|
// Callback: (data, _sender, _message, _metadata) => {
|
||||||
},
|
// if ((data.Type === 'Hidden') && (data.Content === 'MBCHC')) return this.receive(data)
|
||||||
})
|
// return false
|
||||||
window.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
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
this.LOADED = true
|
this.LOADED = true
|
||||||
this.log('info', `loaded version ${this.VERSION}`)
|
this.log('info', `loaded version ${this.VERSION}`)
|
||||||
if ((window.CurrentModule === 'Online') && (window.CurrentScreen === 'ChatRoom')) {
|
if ((w.CurrentModule === 'Online') && (w.CurrentScreen === 'ChatRoom')) {
|
||||||
for (const c of window.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 (!window.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading')
|
if (!w.AsylumGGTSSAddItems) throw new Error('AsylumGGTSSAddItems() not found, aborting MBCHC loading')
|
||||||
if (!window.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 = window.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)
|
||||||
|
@ -804,11 +788,13 @@ window.MBCHC = {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
if (window.CurrentModule && window.CurrentScreen && !(window.CurrentModule === 'Character' && window.CurrentScreen === 'Login')) return this.loader()
|
// 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.
|
||||||
|
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())
|
this.remove_load_hook = this.before('AsylumGGTSSAddItems', () => this.loader())
|
||||||
|
} else this.loader()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
window.MBCHC.preloader()
|
w.MBCHC.preloader()
|
||||||
|
|
||||||
// TODO: with the removal of /mbchc versions it makes no sense to keep track of the mod version and the whole hello() infrastructure can go away
|
|
||||||
|
|
2245
package-lock.json
generated
2245
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -1,10 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "mbchc",
|
"name": "mbchc",
|
||||||
"version": "0.0.9",
|
"version": "0.0.12",
|
||||||
"description": "Mute's Bondage Club Hacks Collection",
|
"description": "Mute's Bondage Club Hacks Collection",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"xo": "^0.56.0"
|
"xo": "^0.56.0",
|
||||||
|
"bc-stubs": "^105.0.0",
|
||||||
|
"bondage-club-mod-sdk": "^1.2.0"
|
||||||
},
|
},
|
||||||
"license": "SEE LICENSE IN LICENSE.",
|
"license": "SEE LICENSE IN LICENSE.",
|
||||||
"xo": {
|
"xo": {
|
||||||
|
@ -13,18 +15,22 @@
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"brace-style": "off",
|
||||||
"camelcase": "off",
|
"camelcase": "off",
|
||||||
"capitalized-comments": "off",
|
"capitalized-comments": "off",
|
||||||
"curly": "off",
|
"curly": "off",
|
||||||
"max-params": "off",
|
"max-params": "off",
|
||||||
|
"max-statements-per-line": "off",
|
||||||
"new-cap": "off",
|
"new-cap": "off",
|
||||||
"no-return-assign": "off",
|
"no-return-assign": "off",
|
||||||
|
"no-unused-expressions": "off",
|
||||||
"no-unused-vars": ["error", {
|
"no-unused-vars": ["error", {
|
||||||
"argsIgnorePattern": "^_",
|
"argsIgnorePattern": "^_",
|
||||||
"destructuredArrayIgnorePattern": "^_"
|
"destructuredArrayIgnorePattern": "^_"
|
||||||
}],
|
}],
|
||||||
"padding-line-between-statements": "off",
|
"padding-line-between-statements": "off",
|
||||||
"semi": "off",
|
"semi": "off",
|
||||||
|
"spaced-comment": "off",
|
||||||
"unicorn/no-array-reduce": "off",
|
"unicorn/no-array-reduce": "off",
|
||||||
"fake/fuck-commas": "off"
|
"fake/fuck-commas": "off"
|
||||||
}
|
}
|
||||||
|
|
52
server.js
52
server.js
|
@ -1,35 +1,33 @@
|
||||||
import {readFileSync} from 'node:fs'
|
import {readFileSync} from 'node:fs'
|
||||||
import {createServer} from 'node:http'
|
import {createServer} from 'node:http'
|
||||||
|
|
||||||
function stamp_cors(rx) {
|
const config = {host: '127.0.0.1', port: 9696}
|
||||||
rx.setHeader('Access-Control-Max-Age', '86400')
|
|
||||||
rx.setHeader('Access-Control-Allow-Private-Network', 'true')
|
const h_cors = {
|
||||||
rx.setHeader('Access-Control-Allow-Origin', '*')
|
'Access-Control-Max-Age': '86400',
|
||||||
rx.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
|
'Access-Control-Allow-Private-Network': 'true',
|
||||||
rx.setHeader('Access-Control-Allow-Headers', '*')
|
'Access-Control-Allow-Origin': '*',
|
||||||
// rx.setHeader('Access-Control-Allow-Credentials', 'false') // omit this header to disallow
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': '*',
|
||||||
|
// 'Access-Control-Allow-Credentials': 'false', // omit this header to disallow
|
||||||
|
}
|
||||||
|
const h_all = Object.assign({
|
||||||
|
'Content-Type': 'text/javascript',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
}, h_cors)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('node:http').ServerResponse} ServerResponse
|
||||||
|
* @type {Record<string,function(ServerResponse):void>}
|
||||||
|
*/
|
||||||
|
const resp = {
|
||||||
|
GET(rx) { rx.writeHead(200, h_all); rx.write(readFileSync('./mbchc.mjs')) },
|
||||||
|
OPTIONS(rx) { rx.writeHead(204, h_cors) },
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = createServer((rq, rx) => {
|
const server = createServer((rq, rx) => {
|
||||||
switch (rq.method) {
|
resp[rq.method] && (new URL(`http://${config.host}:${config.port}${rq.url}`)).pathname === '/' ? resp[rq.method](rx) : rx.writeHead(400)
|
||||||
case 'GET': {
|
|
||||||
rx.statusCode = 200
|
|
||||||
stamp_cors(rx)
|
|
||||||
rx.setHeader('Content-Type', 'text/javascript')
|
|
||||||
rx.setHeader('Cache-Control', 'no-cache')
|
|
||||||
const data = readFileSync('./mbchc.mjs')
|
|
||||||
rx.write(data)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'OPTIONS': {
|
|
||||||
rx.statusCode = 204
|
|
||||||
stamp_cors(rx)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
rx.statusCode = 400
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rx.end()
|
rx.end()
|
||||||
|
console.log('%s %d %s %s', (new Date()).toISOString(), rx.statusCode, rq.method, rq.url)
|
||||||
})
|
})
|
||||||
server.listen(9696, '127.0.0.1', () => console.log('Server started.'))
|
server.listen(config.port, config.host, () => console.log(`Server started at http://${config.host}:${config.port}`))
|
||||||
|
|
10
typedef.d.ts
vendored
Normal file
10
typedef.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/* 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