diff --git a/mbchc-dev.user.js b/mbchc-dev.user.js
index 1a50903..5e56112 100644
--- a/mbchc-dev.user.js
+++ b/mbchc-dev.user.js
@@ -41,7 +41,6 @@
RE_TZ: /(?:GMT|UTC)([+-]\d\d?)/i,
RE_ALL_LEFT: /^<+$/,
RE_ALL_RIGHT: /^>+$/,
- RE_CARET: /^\^/,
RE_SPACES: /\s{2,}/g,
RE_LAST_WORD: /(^|\s)([^\s]*)$/,
RE_LAST_LETTER: /[\w]$/,
@@ -218,6 +217,7 @@
if (options.dot && result.match(this.RE_LAST_LETTER)) result = `${result}.`
return(result)
},
+ tokenise: function(text) { return text.replace(this.RE_SPACES, " ").split(" ") },
inform: function(html, timeout = 60000) { window.ChatRoomSendLocal(`
${html}
`, timeout) },
report: function(x) {
this.inform(`Error: ${x.toString()}`)
@@ -267,6 +267,13 @@
if (found.length > 1) throw `target "${input}": multiple matches (${found.map(c => `${c.cid}|${c.Name}|${c.Nickname || c.Name}`).join(",")})`
return(found[0])
},
+ char2targets: function(char) {
+ let [result, cid] = [new Set(), char.cid.toString()]
+ result.add(cid).add(`=${cid}`)
+ this.tokenise(char.Name).forEach(t => {result.add(t); result.add(`@${t}`)})
+ if (char.Nickname) this.tokenise(char.Nickname).forEach(t => {result.add(t); result.add(`@${t}`)})
+ return result
+ },
donate_data: function(target) {
let char = this.target2char(target)
if (char.IsPlayer()) throw "target must not be you"
@@ -278,6 +285,7 @@
window.ChatRoomMessage({Sender: window.Player.cid, Type: "Action", Content: `You've bought data for $${cost} and sent it to ${char.dn}.`, Dictionary: [{Tag: "MISSING PLAYER DIALOG: ", Text: ""}]})
},
run_activity: function(char, ag, action) { try {
+ if (!window.ActivityAllowed()) throw "activities disabled in this room"
if (!window.ServerChatRoomGetAllowItem(window.Player, char)) throw "no permissions"
char.FocusGroup = this.ensure("invalid AssetGroup", () => window.AssetGroupGet(char.AssetFamily, ag))
let activity = this.ensure("invalid activity", () => window.ActivityAllowedForGroup(char, char.FocusGroup.Name, true).find(a => a.Name === action))
@@ -359,7 +367,7 @@
/* (["anim", "pose"]).forEach(tag => {let cmd = window.Commands.find(c => tag === c.Tag); if (cmd) cmd.AutoComplete = this[`complete_bce_${tag}`]}) */ // this line explodes, don't ask me why
let cmd = window.Commands.find(c => "anim" === c.Tag)
if (cmd) cmd.AutoComplete = this.complete_bce_anim
- cmd = window.Commands.find(c => "pose" === c.Tag)
+ cmd = window.Commands.find(c => "pose" === c.Tag)
if (cmd) cmd.AutoComplete = this.complete_bce_pose
},
gather_versions: function() { return(window.ChatRoomCharacter.filter(c => c.MBCHC).map(c => ({name: c.dn, cid: c.cid, version: c.MBCHC.VERSION}))) },
@@ -448,23 +456,23 @@
comp_hint_visible: function() {return(this.COMP_HINT.parentElement && "flex" === this.COMP_HINT.style.display)},
complete_hint_hide: function(options) {if (!this.comp_hint_visible()) return; this.COMP_HINT.style.display = "none"; window.ChatRoomResize(false)},
complete_target: function(token, me2 = true, check_perms = false) {
- let locase = token.toLocaleLowerCase()
- let found = {}
+ let [locase, found] = [token.toLocaleLowerCase(), new Set()]
for (let c of window.ChatRoomCharacter) {
- let [n, nn, cid] = [c.Name.toLocaleLowerCase(), (c.Nickname || c.Name).toLocaleLowerCase(), c.cid.toString()]
- if (!c.IsPlayer() || me2) [n, nn, cid, `@${n}`, `@${nn}`, `=${cid}`].forEach(s => {if (s.startsWith(locase) && (!check_perms || window.ServerChatRoomGetAllowItem(window.Player, c))) found[s] = true})
+ if ((c.IsPlayer() && !me2) || (check_perms && !window.ServerChatRoomGetAllowItem(window.Player, c))) continue
+ this.char2targets(c).forEach(s => {if (s.toLocaleLowerCase().startsWith(locase)) found.add(s)})
}
- this.complete(Object.keys(found))
+ this.complete(Array.from(found))
},
complete_common: function() {
let input = document.getElementById("InputChat").value
- return([this, input, input.replace(this.RE_SPACES, " ").split(" ")])
+ return([this, input, this.tokenise(input)])
},
complete_mbchc: function(args, locase, cmdline) { const [mbchc, input, tokens] = window.MBCHC.complete_common(); // `this` is command object
if (tokens.length < 1) return
if (tokens.length < 2) return(mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
- if (tokens.length < 3) return(mbchc.complete(Object.keys(mbchc.SUBCOMMANDS_MBCHC).filter(c => c.startsWith(tokens[1])))) // complete subcommand name
- let sub = mbchc.SUBCOMMANDS_MBCHC[tokens[1]]
+ let subname = tokens[1].toLocaleLowerCase()
+ if (tokens.length < 3) return(mbchc.complete(Object.keys(mbchc.SUBCOMMANDS_MBCHC).filter(c => c.startsWith(subname)))) // complete subcommand name
+ let sub = mbchc.SUBCOMMANDS_MBCHC[subname]
if (sub && sub.args) {
let argname = Object.keys(sub.args)[tokens.length - 3]
if ("TARGET" === argname) return(mbchc.complete_target(tokens[tokens.length - 1], false))
@@ -481,25 +489,26 @@
if (tokens.length < 1) return
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
- if (tokens.length < 3) return(mbchc.complete(Object.keys(mbchc.DO_DATA.verbs).filter(c => c.startsWith(tokens[1])))) // complete verb
- let verb = tokens[1].toLocaleLowerCase()
- let ags = mbchc.DO_DATA.verbs[verb]
- if (!ags) return
+ let low = tokens[1].toLocaleLowerCase()
+ if (tokens.length < 3) return(mbchc.complete(Object.keys(mbchc.DO_DATA.verbs).filter(c => c.startsWith(low)))) // complete verb
+ let ags = mbchc.DO_DATA.verbs[low]
+ if (!ags) return(mbchc.bell())
+ low = tokens[2].toLocaleLowerCase()
if (tokens.length < 4) { // complete zone or target
if (Object.keys(ags).length < 2) return(mbchc.complete_do_target(ags[Object.keys(ags)[0]], tokens[2])) // zone implied, complete target
- let zones = Object.entries(mbchc.DO_DATA.zones).filter(([zone, ag]) => zone.startsWith(tokens[2]) && ags[ag]).map(([zone,ag]) => zone)
+ let zones = Object.entries(mbchc.DO_DATA.zones).filter(([zone, ag]) => zone.startsWith(low) && ags[ag]).map(([zone,ag]) => zone)
return(mbchc.complete(zones))
}
if (tokens.length < 5) { // complete target where it belongs
if (Object.keys(ags).length < 2) return // zone implied, target already given
- let zone = tokens[2].toLocaleLowerCase()
- return(mbchc.complete_do_target(ags[mbchc.DO_DATA.zones[zone]], tokens[3]))
+ return(mbchc.complete_do_target(ags[mbchc.DO_DATA.zones[low]], tokens[3]))
}
+ mbchc.bell()
},
complete_bce_anim: function(args, locase, cmdline) { const [mbchc, input, tokens] = window.MBCHC.complete_common(); // `this` is command object
if (tokens.length < 1) return
if (tokens.length < 2) return(mbchc.complete([`${mbchc.CommandsKey}${this.Tag}`]))
- if (tokens.length > 2) return
+ if (tokens.length > 2) return(mbchc.bell())
let anim = tokens[1].toLocaleLowerCase()
return(mbchc.complete(Object.keys(window.bce_EventExpressions).filter(a => a.toLocaleLowerCase().startsWith(anim))))
},