diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..61c31431d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +[*.js] +indent_style = tab +indent_size = 4 diff --git a/.gitignore b/.gitignore index 1545eea38..175e01c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ ehthumbs.db Thumbs.db localStorage.json -.vscode \ No newline at end of file +.vscode diff --git a/game/game.js b/game/game.js index a23e5c8bd..d808e948b 100644 --- a/game/game.js +++ b/game/game.js @@ -354,25 +354,77 @@ } }], }, - announce:{ - init(){ - _status._announce=document.createElement("Announce"); - _status._announce_cache=new Map(); - delete lib.announce.init; - }, - //推送一个对象给所有监听了name的订阅者。 + /** + * **无名杀消息推送库** + * + * 通过`EventTarget`机制,实现消息推送和接收的解耦, + * 从而使消息接收方无需依赖发布方,发布方也无需考虑接收方 + * + * > `lib.announce`不是`actor`模型,若不存在订阅者,则消息发送将无意义 + * + * @example + * // 甲扩展(如《千幻聆音》)在角色皮肤切换后,调用: + * lib.announce.publish("skinChange", { + * player, + * playerName: "zhangfei", + * originSkin: "image/xxx.jpg", + * currentSkin: "image/yyy.jpg" + * }); + * + * // 乙扩展监听此`skinChange`事件,并修改自己扩展相关界面的图片: + * const method = lib.announce.subscribe("skinChange", (e) => { + * div.setBackgroundImage(e.currentSkin); + * }); + * + * // 若此时乙扩展不想继续订阅`skinChange`事件,可以通过`unsubscribe`解除订阅 + * lib.announce.unsubscribe("skinChange", method); + */ + announce:new class{ + constructor(){ + /** + * @type {HTMLElement} + */ + this._announce=document.createElement("Announce"); + /** + * @type {Map} + */ + this._announce_cache=new Map(); + } + + /** + * 推送任意数据给所有监听了指定事件的订阅者,并返回给定的数据 + * + * 若不存在订阅指定事件的订阅者,则推送的数据将无意义 + * + * @template T + * @param {string} name - 要推送事件的名称 + * @param {T} values - 要推送的数据 + * @returns {T} + */ publish(name,values){ - if(_status._announce) _status._announce.dispatchEvent(new CustomEvent(name,{ + if(this._announce) this._announce.dispatchEvent(new CustomEvent(name,{ detail:values })); return values; - }, - //订阅name相关的事件。 + } + + /** + * 订阅给定名字的事件,并返回给定的函数 + * + * 在事件触发时执行给定的函数 + * + * 给定的函数将被存储至当前实例中,用于取消订阅时获取 + * + * @template T + * @param {string} name - 要订阅事件的名称 + * @param {(values: T) => void} method - 事件触发时执行的函数 + * @returns {(values: T) => void} + */ subscribe(name,method){ - if(_status._announce&&_status._announce_cache) { + if(this._announce&&this._announce_cache) { let subscribeFunction; - if(_status._announce_cache.has(method)){ - let records=_status._announce_cache.get(method); + if(this._announce_cache.has(method)){ + let records=this._announce_cache.get(method); subscribeFunction=records.get("Listener"); records.get("EventTargets").add(name); } @@ -381,21 +433,31 @@ let records=new Map(); records.set("Listener",subscribeFunction); records.set("EventTargets",[name]); - _status._announce_cache.set(method,records); + this._announce_cache.set(method,records); } - _status._announce.addEventListener(name,subscribeFunction); + this._announce.addEventListener(name,subscribeFunction); } return method; - }, - //取消对事件name的订阅 + } + + /** + * 取消指定事件某一函数的订阅,并返回该函数 + * + * 给定的函数将不再于事件触发时执行,其余同事件需触发的函数不受限制 + * + * @template T + * @param {string} name - 要取消订阅事件的名称 + * @param {(values: T) => void} method - 订阅指定事件的函数 + * @returns {(values: T) => void} + */ unsubscribe(name,method){ - if(_status._announce&&_status._announce_cache&&_status._announce_cache.has(method)){ - let records=_status._announce_cache.get(method); + if(this._announce&&this._announce_cache&&this._announce_cache.has(method)){ + let records=this._announce_cache.get(method); const listener=records.get("Listener"); let eventTargets=records.get("EventTargets"); eventTargets.remove(name); - if(eventTargets.length<=0) _status._announce_cache.remove(method); - _status._announce.removeEventListener(name,listener); + if(eventTargets.length<=0) this._announce_cache.remove(method); + this._announce.removeEventListener(name,listener); } return method; } @@ -9566,7 +9628,6 @@ delete _status.htmlbg; window.game=game; - lib.announce.init(); // node:path library alternative if (typeof module!="object"||typeof module.exports!="object") lib.init.js(`${lib.assetURL}game`,"path",()=>{ lib.path=window._noname_path; @@ -11579,12 +11640,16 @@ result:result }); var res=gen.next((lastEvent&&("result" in lastEvent))?lastEvent.result:null); - if(res.done) event.finish(); - else { - var currentResult=res.value; - // TODO: use `event.debugger` to replace source - if(typeof currentResult=="function") yield currentResult; - else lastEvent=currentResult; + if(res.done) return event.finish(); + var currentResult=res.value; + // TODO: use `event.debugger` to replace source + if(typeof currentResult=="function") yield currentResult; + else{ + if(Array.isArray(currentResult)){ + event.step=currentResult[1]; + currentResult=currentResult[0]; + } + lastEvent=currentResult; } } }else if(item._parsed) return item; @@ -41228,7 +41293,7 @@ event.callHandler(event.getDefaultHandlerType(),event,{ state:'end' }); - event.step++; + if(typeof event.step=="number") ++event.step; } } } @@ -58445,6 +58510,23 @@ }, }; const get={ + /** + * 获取当前内核版本信息 + * + * 目前仅考虑`chrome`, `firefox`和`safari`三种浏览器的信息,其余均归于其他范畴 + * + * > 其他后续或许会增加,但`IE`永无可能 + * + * @returns {["firefox" | "chrome" | "safari" | "other", number]} + */ + coreInfo(){ + const regex=/(firefox|chrome|safari)\/([\d.]+)/; + let result; + if (!(result=userAgent.match(regex))) return ["other",NaN]; + if (result[1]!="safari") return [result[1],parseInt(result[2])]; + result=userAgent.match(/version\/([\d.]+).*safari/); + return ["safari",parseInt(result[1])]; + }, /** * 返回 VCard[] 形式的所有牌,用于印卡将遍历 * @param {Function} filter