diff --git a/noname/library/element/gameEvent.js b/noname/library/element/gameEvent.js index 2eddf8f6e..b80f5e8bc 100644 --- a/noname/library/element/gameEvent.js +++ b/noname/library/element/gameEvent.js @@ -1,870 +1,881 @@ -import { AI as ai } from '../../ai/index.js'; -import { Get as get } from '../../get/index.js'; -import { Game as game } from '../../game/index.js'; -import { Library as lib } from "../index.js"; -import { status as _status } from '../../status/index.js'; -import { UI as ui } from '../../ui/index.js'; -import { AsyncFunction } from '../../util/index.js'; - -export class GameEvent { - /** @type { GameEventPromise } */ - #promise; - /** - * @param {string | GameEvent} [name] - * @param {false} [trigger] - */ - constructor(name, trigger) { - if (name instanceof GameEvent) { - const other = name; - [name, trigger] = other.__args; - } - - if (typeof name == 'string') { - this.name = name; - const gameEvent = get.event(); - if (gameEvent) { - const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`; - // @ts-ignore - if (gameEvent.hasHandler(type)) this.pushHandler(...gameEvent.getHandler(type)); - } - game.globalEventHandlers.addHandlerToEvent(this); - } - this.step = 0; - this.finished = false; - /** - * @type {GameEventPromise[]} - */ - this.next = []; - /** - * @type {GameEventPromise[]} - */ - this.after = []; - this.custom = { - add: {}, - replace: {} - }; - this._aiexclude = []; - this._notrigger = []; - /** - * @type { Result } - */ - // @ts-ignore - this._result = {}; - this._set = []; - /** - * @type {boolean} 这个事件是否使用异步函数处理 - **/ - this.async = false; - /** - * @type {null|(event: GameEvent)=>any} 这个异步事件对应Promise的resolve函数 - **/ - this.resolve = null; - if (trigger !== false && !game.online) this._triggered = 0; - this.__args = [name, trigger]; - } - static initialGameEvent() { - return new GameEvent().finish().toPromise(); - } - /** - * @type { Player } - */ - // @ts-ignore - source; - /** - * @type { Player } - */ - // @ts-ignore - player; - /** - * @type { Player } - */ - // @ts-ignore - target; - /** - * @type { Player[] } - */ - // @ts-ignore - targets; - /** - * @type { Card } - */ - // @ts-ignore - card; - /** - * @type { Card[] } - */ - // @ts-ignore - cards; - /** - * @type { string } - */ - skill; - /** - * @type { boolean } - */ - forced; - /** - * @type { number } - */ - num; - /** - * @type { GameEvent } - */ - // @ts-ignore - _trigger; - /** - * @type { Result } - */ - _result; - /** - * @type { number } - */ - // @ts-ignore - baseDamage; - /** - * @type { Player } - */ - // @ts-ignore - customSource; - /** - * @type { number } - */ - // @ts-ignore - extraDamage; - /** - * @type { string } - */ - // @ts-ignore - nature; - /** - * @type { boolean } - */ - // @ts-ignore - notrigger; - /** - * @type { number } - */ - // @ts-ignore - original_num; - /** - * @type { boolean } - */ - // @ts-ignore - unreal; - /** - * @type { Button[] } - */ - // @ts-ignore - excludeButton; - /** - * @type { Result } - */ - // @ts-ignore - result; - /** - * @type { GameEventPromise | void | null } - */ - // @ts-ignore - parent; - /** - * @type { string } - */ - name; - /** - * @param {keyof this} key - * @param {number} [value] - * @param {number} [baseValue] - */ - addNumber(key, value, baseValue) { - if (typeof value != 'number') value = 0; - if (typeof this[key] == 'number') this[key] += value; - else { - if (typeof baseValue != 'number') baseValue = 0; - this[key] = baseValue + value; - } - return this; - } - /** - * @param {keyof this} key - * @param {number} [baseValue] - */ - decrease(key, baseValue) { - if (typeof this[key] == 'number') this[key]--; - else this.subtractNumber(key, 1, baseValue); - return this; - } - /** - * @param {keyof this} key - * @param {number} [baseValue] - */ - increase(key, baseValue) { - if (typeof this[key] == 'number') this[key]++; - else this.addNumber(key, 1, baseValue); - return this; - } - /** - * @param {keyof this} key - * @param {number} [value] - * @param {number} [baseValue] - */ - subtractNumber(key, value, baseValue) { - if (typeof value != 'number') value = 0; - if (typeof this[key] == 'number') this[key] -= value; - else { - if (typeof baseValue != 'number') baseValue = 0; - this[key] = baseValue - value; - } - return this; - } - /** - * @param {Parameters[0]} type - * @param {GameEvent} event - * @param {{ - * state?: 'begin' | 'end'; - * }} option - * @returns {this} - */ - callHandler(type, event, option) { - if (this.hasHandler(type)) this.getHandler(type).forEach(handler => { - if (typeof handler == 'function') handler(event, option); - }); - return this; - } - getDefaultHandlerType() { - const eventName = this.name; - if (eventName) return `on${eventName[0].toUpperCase()}${eventName.slice(1)}`; - } - /** - * @param {Parameters[0]} [type] - * @returns {((event: GameEvent, option: { - * state?: 'begin' | 'end'; - * }) => void)[]} - */ - getHandler(type) { - if (!type) type = this.getDefaultHandlerType(); - const currentHandler = this[type]; - if (!currentHandler) this[type] = []; - else if (!Array.isArray(currentHandler)) this[type] = [currentHandler]; - return this[type]; - } - /** - * @param {`on${Capitalize}`} [type] - */ - hasHandler(type) { - if (!type) type = this.getDefaultHandlerType(); - return Boolean(this[type] && this.getHandler(type).length); - } - /** - * @overload - * @param {...((event: GameEvent, option: { - * state?: 'begin' | 'end'; - * }) => void)[]} handlers - * @returns {number} - */ - /** - * @overload - * @param {Parameters[0]} type - * @param {...((event: GameEvent, option: { - * state?: 'begin' | 'end'; - * }) => void)[]} handlers - * @returns {number} - */ - pushHandler(type) { - return typeof type == 'string' ? this.getHandler(type).push(...Array.from(arguments).slice(1)) : this.getHandler().push(...arguments); - } - changeToZero() { - this.num = 0; - this.numFixed = true; - return this; - } - finish() { - this.finished = true; - return this; - } - putStepCache(key, value) { - if (!this._stepCache) { - this._stepCache = {}; - } - this._stepCache[key] = value; - return this; - } - getStepCache(key) { - if (!this._stepCache) return undefined; - return this._stepCache[key]; - } - clearStepCache(key) { - if (key !== undefined && key !== null) { - delete this._stepCache[key]; - } - delete this._stepCache; - return this; - } - callFuncUseStepCache(prefix, func, params) { - if (typeof func != 'function') return; - if (_status.closeStepCache) return func.apply(null, params); - var cacheKey = "[" + prefix + "]" + get.paramToCacheKey.apply(null, params); - var ret = this.getStepCache(cacheKey); - if (ret === undefined || ret === null) { - ret = func.apply(null, params); - this.putStepCache(cacheKey, ret); - } - return ret; - } - putTempCache(key1, key2, value) { - if (!this._tempCache) { - this._tempCache = {}; - } - if (!this._tempCache[key1]) { - this._tempCache[key1] = {}; - } - this._tempCache[key1][key2] = value; - return value; - } - getTempCache(key1, key2) { - if (!this._tempCache) { - return undefined; - } - if (!this._tempCache[key1]) { - return undefined; - } - return this._tempCache[key1][key2]; - } - cancel(arg1, arg2, notrigger) { - this.untrigger(arg1, arg2); - this.finish(); - if (notrigger != 'notrigger') { - this.trigger(this.name + 'Cancelled'); - if (this.player && lib.phaseName.includes(this.name)) this.player.getHistory('skipped').add(this.name); - } - return this; - } - neutralize(event) { - this.untrigger(); - this.finish(); - this._neutralized = true; - this.trigger('eventNeutralized'); - this._neutralize_event = event || _status.event; - return this; - } - unneutralize() { - this.untrigger(); - delete this._neutralized; - delete this.finished; - if (this.type == 'card' && this.card && this.name == 'sha') this.directHit = true; - return this; - } - goto(step) { - this.step = step - 1; - return this; - } - redo() { - this.step--; - return this; - } - setHiddenSkill(skill) { - if (!this.player) return this; - var hidden = this.player.hiddenSkills.slice(0); - game.expandSkills(hidden); - if (hidden.includes(skill)) this.set('hsskill', skill); - return this; - } - set(key, value) { - if (arguments.length == 1 && Array.isArray(arguments[0])) { - for (var i = 0; i < arguments[0].length; i++) { - if (Array.isArray(arguments[0][i])) { - this.set(arguments[0][i][0], arguments[0][i][1]); - } - } - } - else { - if (typeof key != 'string') { - console.log('warning: using non-string object as event key'); - console.log(key, value); - console.log(_status.event); - } - this[key] = value; - this._set.push([key, value]); - } - return this; - } - /** - * @param {ArrayLike | Function | keyof typeof lib.element.content} item - */ - setContent(item) { - switch (typeof item) { - case "object": - case "function": - if (item instanceof AsyncFunction) { - this.content = item; - } - else this.content = lib.init.parsex(item); - break; - default: - try { - if (!(lib.element.content[item] instanceof AsyncFunction) && !lib.element.content[item]._parsed) { - lib.element.content[item] = lib.init.parsex(lib.element.content[item]); - lib.element.content[item]._parsed = true; - } - } - catch { - throw new Error(`Content ${item} may not exist.\nlib.element.content[${item}] = ${lib.element.content[item]}`); - } - this.content = lib.element.content[item]; - break; - } - return this; - } - - /** - * - * @param {Function | keyof typeof lib.element.contents} contents - * @returns {GameEvent} - */ - setContents(contents) { - if (Array.isArray(contents)) this.contents = contents; - else if (contents in lib.element.contents) return this.setContents(lib.element.contents[contents]); - else throw new Error('not supported value.'); - return this; - } - - getLogv() { - for (var i = 1; i <= 3; i++) { - var event = this.getParent(i); - if (event && event.logvid) return event.logvid; - } - return null; - } - send() { - this.player.send(function (name, args, set, event, skills) { - game.me.applySkills(skills); - var next = game.me[name].apply(game.me, args); - for (var i = 0; i < set.length; i++) { - next.set(set[i][0], set[i][1]); - } - if (next._backupevent) { - next.backup(next._backupevent); - } - next._modparent = event; - game.resume(); - }, this.name, this._args || [], this._set, - get.stringifiedResult(this.parent), get.skillState(this.player)); - this.player.wait(); - game.pause(); - return this; - } - resume() { - delete this._cardChoice; - delete this._targetChoice; - delete this._skillChoice; - return this; - } - /** - * 获取事件的父节点。 - * 获取事件链上的指定事件。 - * 默认获取上一个父节点(核心)。 - * @param {number|string|(evt:gameEvent)=>boolean} [level=1] 获取深度(number)/指定名字(string)/指定特征(function) - * @param {boolean} [forced] 若获取不到节点,默认返回{},若forced为true则返回null - * @param {boolean} [includeSelf] 若level不是数字,指定搜索时是否包含事件本身 - * @returns {GameEvent|{}|null} - */ - getParent(level = 1, forced, includeSelf) { - let event = this; - let i = 0; - const toreturn = forced ? null : {}; - const historys = []; - const filter = - typeof level === 'function' ? level : - typeof level === 'number' ? evt => i === level : - evt => evt.name === level; - while (true) { - if (!event) return toreturn; - historys.push(event); - if (filter(event) && (includeSelf || i !== 0)) return event; - if (game.online && event._modparent) event = event._modparent; - else event = event.parent; - if (historys.includes(event)) return toreturn; - i++; - } - } - getTrigger() { - return this.getParent(e => e._trigger, false, true)._trigger; - } - getRand(name) { - if (name) { - if (!this._rand_map) this._rand_map = {}; - if (!this._rand_map[name]) this._rand_map[name] = Math.random(); - return this._rand_map[name]; - } - if (!this._rand) this._rand = Math.random(); - return this._rand; - } - insert(content, map) { - const next = (new lib.element.GameEvent(`${this.name}Inserted`, false)).toPromise(); - this.next.push(next); - next.setContent(content); - Object.entries(map).forEach(entry => next.set(entry[0], entry[1])); - return next; - } - insertAfter(content, map) { - const next = (new lib.element.GameEvent(`${this.name}Inserted`, false)).toPromise(); - this.after.push(next); - next.setContent(content); - Object.entries(map).forEach(entry => next.set(entry[0], entry[1])); - return next; - } - backup(skill) { - this._backup = { - filterButton: this.filterButton, - selectButton: this.selectButton, - filterTarget: this.filterTarget, - selectTarget: this.selectTarget, - filterCard: this.filterCard, - selectCard: this.selectCard, - position: this.position, - forced: this.forced, - fakeforce: this.fakeforce, - _aiexclude: this._aiexclude, - complexSelect: this.complexSelect, - complexCard: this.complexCard, - complexTarget: this.complexTarget, - _cardChoice: this._cardChoice, - _targetChoice: this._targetChoice, - _skillChoice: this._skillChoice, - ai1: this.ai1, - ai2: this.ai2, - filterOk: this.filterOk, - }; - if (skill) { - var info = get.info(skill); - this.skill = skill; - this._aiexclude = []; - if (typeof info.viewAs == 'function') { - if (info.filterButton != undefined) this.filterButton = get.filter(info.filterButton); - if (info.selectButton != undefined) this.selectButton = info.selectButton; - if (info.filterTarget != undefined) this.filterTarget = get.filter(info.filterTarget); - if (info.selectTarget != undefined) this.selectTarget = info.selectTarget; - if (info.filterCard != undefined) { - if (info.ignoreMod) this.ignoreMod = true; - this.filterCard2 = get.filter(info.filterCard); - this.filterCard = function (card, player, event) { - var evt = event || _status.event; - if (!evt.ignoreMod && player) { - var mod = game.checkMod(card, player, 'unchanged', 'cardEnabled2', player); - if (mod != 'unchanged') return mod; - } - return get.filter(evt.filterCard2).apply(this, arguments); - }; - } - if (info.filterOk == undefined) { - this.filterOk = function () { - var evt = _status.event; - var card = get.card(), player = get.player(); - var filter = evt._backup.filterCard; - if (filter && !filter(card, player, evt)) return false; - if (evt._backup.filterOk) return evt._backup.filterOk(); - return true; - }; - } - else this.filterOk = info.filterOk; - if (info.selectCard != undefined) this.selectCard = info.selectCard; - if (info.position != undefined) this.position = info.position; - //if(info.forced!=undefined) this.forced=info.forced; - if (info.complexSelect != undefined) this.complexSelect = info.complexSelect; - if (info.complexCard != undefined) this.complexCard = info.complexCard; - if (info.complexTarget != undefined) this.complexTarget = info.complexTarget; - if (info.ai1 != undefined) this.ai1 = info.ai1; - if (info.ai2 != undefined) this.ai2 = info.ai2; - } - else if (info.viewAs) { - if (info.filterButton != undefined) this.filterButton = get.filter(info.filterButton); - if (info.selectButton != undefined) this.selectButton = info.selectButton; - if (info.filterTarget != undefined) this.filterTarget = get.filter(info.filterTarget); - if (info.selectTarget != undefined) this.selectTarget = info.selectTarget; - if (info.filterCard != undefined) { - if (info.ignoreMod) this.ignoreMod = true; - this.filterCard2 = get.filter(info.filterCard); - this.filterCard = function (card, player, event) { - var evt = event || _status.event; - if (!evt.ignoreMod && player) { - var mod = game.checkMod(card, player, 'unchanged', 'cardEnabled2', player); - if (mod != 'unchanged') return mod; - } - return get.filter(evt.filterCard2).apply(this, arguments); - }; - } - if (info.filterOk == undefined) { - this.filterOk = function () { - var evt = _status.event; - var card = get.card(), player = get.player(); - var filter = evt._backup.filterCard; - if (filter && !filter(card, player, evt)) return false; - if (evt._backup.filterOk) return evt._backup.filterOk(); - return true; - }; - } - else this.filterOk = info.filterOk; - if (info.selectCard != undefined) this.selectCard = info.selectCard; - if (info.position != undefined) this.position = info.position; - //if(info.forced!=undefined) this.forced=info.forced; - if (info.complexSelect != undefined) this.complexSelect = info.complexSelect; - if (info.complexCard != undefined) this.complexCard = info.complexCard; - if (info.complexTarget != undefined) this.complexTarget = info.complexTarget; - if (info.ai1 != undefined) this.ai1 = info.ai1; - if (info.ai2 != undefined) this.ai2 = info.ai2; - } - else { - this.filterButton = info.filterButton ? get.filter(info.filterButton) : undefined; - this.selectButton = info.selectButton; - this.filterTarget = info.filterTarget ? get.filter(info.filterTarget) : undefined; - this.selectTarget = info.selectTarget; - this.filterCard = info.filterCard ? get.filter(info.filterCard) : undefined; - this.selectCard = info.selectCard; - this.position = info.position; - //this.forced=info.forced; - this.complexSelect = info.complexSelect; - this.complexCard = info.complexCard; - this.complexTarget = info.complexTarget; - if (info.ai1 != undefined) this.ai1 = info.ai1; - if (info.ai2 != undefined) this.ai2 = info.ai2; - this.filterOk = info.filterOk; - } - delete this.fakeforce; - } - delete this._cardChoice; - delete this._targetChoice; - delete this._skillChoice; - return this; - } - restore() { - if (this._backup) { - this.filterButton = this._backup.filterButton; - this.selectButton = this._backup.selectButton; - this.filterTarget = this._backup.filterTarget; - this.selectTarget = this._backup.selectTarget; - this.filterCard = this._backup.filterCard; - this.selectCard = this._backup.selectCard; - this.position = this._backup.position; - this.forced = this._backup.forced; - this.fakeforce = this._backup.fakeforce; - this._aiexclude = this._backup._aiexclude; - this.complexSelect = this._backup.complexSelect; - this.complexCard = this._backup.complexCard; - this.complexTarget = this._backup.complexTarget; - this.ai1 = this._backup.ai1; - this.ai2 = this._backup.ai2; - this._cardChoice = this._backup._cardChoice; - this._targetChoice = this._backup._targetChoice; - this._skillChoice = this._backup._skillChoice; - this.filterOk = this._backup.filterOk; - } - delete this.skill; - delete this.ignoreMod; - delete this.filterCard2; - return this; - } - isMine() { - return (this.player && this.player == game.me && !_status.auto && !this.player.isMad() && !game.notMe); - } - isOnline() { - return (this.player && this.player.isOnline()); - } - notLink() { - return this.getParent().name != '_lianhuan' && this.getParent().name != '_lianhuan2'; - } - isPhaseUsing(player) { - var evt = this.getParent('phaseUse'); - if (!evt || evt.name != 'phaseUse') return false; - return !player || player == evt.player; - } - addTrigger(skills, player) { - if (!player || !skills) return this; - let evt = this; - if (typeof skills == 'string') skills = [skills]; - game.expandSkills(skills); - while (true) { - evt = evt.getParent('arrangeTrigger'); - if (!evt || evt.name != 'arrangeTrigger' || !evt.doingList) return this; - const doing = evt.doingList.find(i => i.player === player); - const firstDo = evt.doingList.find(i => i.player === "firstDo"); - const lastDo = evt.doingList.find(i => i.player === "lastDo"); - - skills.forEach(skill => { - const info = lib.skill[skill]; - if (!info.trigger) return; - if (!Object.keys(info.trigger).some(i => { - if (Array.isArray(info.trigger[i])) return info.trigger[i].includes(evt.triggername); - return info.trigger[i] === evt.triggername; - })) return; - - const toadd = { - skill: skill, - player: player, - priority: get.priority(skill), - }; - const map = info.firstDo ? firstDo : info.lastDo ? lastDo : doing; - if (!map) return; - if (map.doneList.some(i => i.skill === toadd.skill && i.player === toadd.player)) return; - if (map.todoList.some(i => i.skill === toadd.skill && i.player === toadd.player)) return; - map.todoList.add(toadd); - if (typeof map.player === 'string') map.todoList.sort((a, b) => (b.priority - a.priority) || (evt.playerMap.indexOf(a) - evt.playerMap.indexOf(b))); - else map.todoList.sort((a, b) => b.priority - a.priority); - }); - } - } - removeTrigger(skills, player) { - if (!player || !skills) return this; - let evt = this; - if (typeof skills == 'string') skills = [skills]; - game.expandSkills(skills); - while (true) { - evt = evt.getParent('arrangeTrigger'); - if (!evt || evt.name != 'arrangeTrigger' || !evt.doingList) return this; - const doing = evt.doingList.find(i => i.player == player); - const firstDo = evt.doingList.find(i => i.player == "firstDo"); - const lastDo = evt.doingList.find(i => i.player == "lastDo"); - - skills.forEach(skill => [doing, firstDo, lastDo].forEach(map => { - if (!map) return; - const toremove = map.todoList.filter(i => i.skill == skill && i.player == player); - if (toremove.length > 0) map.todoList.removeArray(toremove); - })); - } - } - trigger(name) { - if (_status.video) return; - if ((this.name === 'gain' || this.name === 'lose') && !_status.gameDrawed) return; - if (name === 'gameDrawEnd') _status.gameDrawed = true; - if (name === 'gameStart') { - lib.announce.publish('gameStart', {}); - if (_status.brawl && _status.brawl.gameStart) _status.brawl.gameStart(); - if (lib.config.show_cardpile) ui.cardPileButton.style.display = ''; - _status.gameStarted = true; - game.showHistory(); - } - if (!lib.hookmap[name] && !lib.config.compatiblemode) return; - if (!game.players || !game.players.length) return; - const event = this; - let start = [_status.currentPhase, event.source, event.player, game.me, game.players[0]].find(i => get.itemtype(i) == 'player'); - if (!start) return; - if (!game.players.includes(start) && !game.dead.includes(start)) start = game.findNext(start); - const firstDo = { - player: "firstDo", - todoList: [], - doneList: [], - }; - const lastDo = { - player: "lastDo", - todoList: [], - doneList: [], - }; - const doingList = []; - const roles = ['player', 'source', 'target', 'global']; - const playerMap = game.players.concat(game.dead).sortBySeat(start); - let player = start; - let allbool = false; - do { - const doing = { - player: player, - todoList: [], - doneList: [], - listAdded: {}, - addList(skill) { - if (!skill) return; - if (Array.isArray(skill)) return skill.forEach(i => this.addList(i)); - if (this.listAdded[skill]) return; - this.listAdded[skill] = true; - - const info = lib.skill[skill]; - const list = info.firstDo ? firstDo.todoList : info.lastDo ? lastDo.todoList : this.todoList; - list.push({ - skill: skill, - player: this.player, - priority: get.priority(skill), - }); - if (typeof list.player == 'string') list.sort((a, b) => (b.priority - a.priority) || (playerMap.indexOf(a) - playerMap.indexOf(b))); - else list.sort((a, b) => b.priority - a.priority); - allbool = true; - } - }; - - const notemp = player.skills.slice(); - for (const j in player.additionalSkills) { - if (!j.startsWith('hidden:')) notemp.addArray(player.additionalSkills[j]); - } - Object.keys(player.tempSkills).filter(skill => { - if (notemp.includes(skill)) return false; - const expire = player.tempSkills[skill]; - if (typeof expire === 'function') return expire(event, player, name); - if (get.objtype(expire) === 'object') return roles.some(role => { - if (role !== 'global' && player !== event[role]) return false; - if (Array.isArray(expire[role])) return expire[role].includes(name); - return expire[role] === name; - }); - }).forEach(skill => { - delete player.tempSkills[skill]; - player.removeSkill(skill); - }); - - if (lib.config.compatiblemode) { - doing.addList(game.expandSkills(player.getSkills('invisible').concat(lib.skill.global)).filter(skill => { - const info = get.info(skill); - if (!info || !info.trigger) return false; - return roles.some(role => { - if (info.trigger[role] === name) return true; - if (Array.isArray(info.trigger[role]) && info.trigger[role].includes(name)) return true; - }); - })); - } - else roles.forEach(role => { - doing.addList(lib.hook.globalskill[role + '_' + name]); - doing.addList(lib.hook[player.playerid + '_' + role + '_' + name]); - }); - delete doing.listAdded; - delete doing.addList; - doingList.push(doing); - player = player.nextSeat; - } while (player && player !== start); - doingList.unshift(firstDo); - doingList.push(lastDo); - // console.log(name,event.player,doingList.map(i=>({player:i.player,todoList:i.todoList.slice(),doneList:i.doneList.slice()}))) - - if (allbool) { - const next = game.createEvent('arrangeTrigger', false, event); - next.setContent('arrangeTrigger'); - next.doingList = doingList; - next._trigger = event; - next.triggername = name; - next.playerMap = playerMap; - event._triggering = next; - return next; - } - return null; - } - untrigger(all = true, player) { - const evt = this._triggering; - if (all) { - if(all !== 'currentOnly') this._triggered = 5; - if (evt && evt.doingList) { - evt.doingList.forEach(doing => doing.todoList = []); - } - } - else if (player) { - this._notrigger.add(player); - // if(!evt||!evt.doingList) return this; - // const doing=evt.doingList.find(doing=>doing.player==player); - // if(doing) doing.todoList=[]; - } - return this; - } - /** - * 事件转为Promise化 - * - * @returns { GameEventPromise } - */ - toPromise() { - if (!this.#promise) { - this.#promise = new lib.element.GameEventPromise(this); - } - return this.#promise; - } -} +import { AI as ai } from '../../ai/index.js'; +import { Get as get } from '../../get/index.js'; +import { Game as game } from '../../game/index.js'; +import { Library as lib } from "../index.js"; +import { status as _status } from '../../status/index.js'; +import { UI as ui } from '../../ui/index.js'; +import { AsyncFunction } from '../../util/index.js'; + +export class GameEvent { + /** @type { GameEventPromise } */ + #promise; + /** + * @param {string | GameEvent} [name] + * @param {false} [trigger] + */ + constructor(name, trigger) { + if (name instanceof GameEvent) { + const other = name; + [name, trigger] = other.__args; + } + + if (typeof name == 'string') { + this.name = name; + const gameEvent = get.event(); + if (gameEvent) { + const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`; + // @ts-ignore + if (gameEvent.hasHandler(type)) this.pushHandler(...gameEvent.getHandler(type)); + } + game.globalEventHandlers.addHandlerToEvent(this); + } + this.step = 0; + this.finished = false; + /** + * @type {GameEventPromise[]} + */ + this.next = []; + /** + * @type {GameEventPromise[]} + */ + this.after = []; + this.custom = { + add: {}, + replace: {} + }; + this._aiexclude = []; + this._notrigger = []; + /** + * @type { Result } + */ + // @ts-ignore + this._result = {}; + this._set = []; + /** + * @type {boolean} 这个事件是否使用异步函数处理 + **/ + this.async = false; + /** + * @type {null|(event: GameEvent)=>any} 这个异步事件对应Promise的resolve函数 + **/ + this.resolve = null; + if (trigger !== false && !game.online) this._triggered = 0; + this.__args = [name, trigger]; + } + static initialGameEvent() { + return new GameEvent().finish().toPromise(); + } + /** + * @type { Player } + */ + // @ts-ignore + source; + /** + * @type { Player } + */ + // @ts-ignore + player; + /** + * @type { Player } + */ + // @ts-ignore + target; + /** + * @type { Player[] } + */ + // @ts-ignore + targets; + /** + * @type { Card } + */ + // @ts-ignore + card; + /** + * @type { Card[] } + */ + // @ts-ignore + cards; + /** + * @type { string } + */ + skill; + /** + * @type { boolean } + */ + forced; + /** + * @type { number } + */ + num; + /** + * @type { GameEvent } + */ + // @ts-ignore + _trigger; + /** + * @type { Result } + */ + _result; + /** + * @type { number } + */ + // @ts-ignore + baseDamage; + /** + * @type { Player } + */ + // @ts-ignore + customSource; + /** + * @type { number } + */ + // @ts-ignore + extraDamage; + /** + * @type { string } + */ + // @ts-ignore + nature; + /** + * @type { boolean } + */ + // @ts-ignore + notrigger; + /** + * @type { number } + */ + // @ts-ignore + original_num; + /** + * @type { boolean } + */ + // @ts-ignore + unreal; + /** + * @type { Button[] } + */ + // @ts-ignore + excludeButton; + /** + * @type { Result } + */ + // @ts-ignore + result; + /** + * @type { GameEventPromise | void | null } + */ + // @ts-ignore + parent; + /** + * @type { string } + */ + name; + /** + * @param {keyof this} key + * @param {number} [value] + * @param {number} [baseValue] + */ + addNumber(key, value, baseValue) { + if (typeof value != 'number') value = 0; + if (typeof this[key] == 'number') this[key] += value; + else { + if (typeof baseValue != 'number') baseValue = 0; + this[key] = baseValue + value; + } + return this; + } + /** + * @param {keyof this} key + * @param {number} [baseValue] + */ + decrease(key, baseValue) { + if (typeof this[key] == 'number') this[key]--; + else this.subtractNumber(key, 1, baseValue); + return this; + } + /** + * @param {keyof this} key + * @param {number} [baseValue] + */ + increase(key, baseValue) { + if (typeof this[key] == 'number') this[key]++; + else this.addNumber(key, 1, baseValue); + return this; + } + /** + * @param {keyof this} key + * @param {number} [value] + * @param {number} [baseValue] + */ + subtractNumber(key, value, baseValue) { + if (typeof value != 'number') value = 0; + if (typeof this[key] == 'number') this[key] -= value; + else { + if (typeof baseValue != 'number') baseValue = 0; + this[key] = baseValue - value; + } + return this; + } + /** + * @param {Parameters[0]} type + * @param {GameEvent} event + * @param {{ + * state?: 'begin' | 'end'; + * }} option + * @returns {this} + */ + callHandler(type, event, option) { + if (this.hasHandler(type)) this.getHandler(type).forEach(handler => { + if (typeof handler == 'function') handler(event, option); + }); + return this; + } + getDefaultHandlerType() { + const eventName = this.name; + if (eventName) return `on${eventName[0].toUpperCase()}${eventName.slice(1)}`; + } + /** + * @param {Parameters[0]} [type] + * @returns {((event: GameEvent, option: { + * state?: 'begin' | 'end'; + * }) => void)[]} + */ + getHandler(type) { + if (!type) type = this.getDefaultHandlerType(); + const currentHandler = this[type]; + if (!currentHandler) this[type] = []; + else if (!Array.isArray(currentHandler)) this[type] = [currentHandler]; + return this[type]; + } + /** + * @param {`on${Capitalize}`} [type] + */ + hasHandler(type) { + if (!type) type = this.getDefaultHandlerType(); + return Boolean(this[type] && this.getHandler(type).length); + } + /** + * @overload + * @param {...((event: GameEvent, option: { + * state?: 'begin' | 'end'; + * }) => void)[]} handlers + * @returns {number} + */ + /** + * @overload + * @param {Parameters[0]} type + * @param {...((event: GameEvent, option: { + * state?: 'begin' | 'end'; + * }) => void)[]} handlers + * @returns {number} + */ + pushHandler(type) { + return typeof type == 'string' ? this.getHandler(type).push(...Array.from(arguments).slice(1)) : this.getHandler().push(...arguments); + } + changeToZero() { + this.num = 0; + this.numFixed = true; + return this; + } + finish() { + this.finished = true; + return this; + } + putStepCache(key, value) { + if (!this._stepCache) { + this._stepCache = {}; + } + this._stepCache[key] = value; + return this; + } + getStepCache(key) { + if (!this._stepCache) return undefined; + return this._stepCache[key]; + } + clearStepCache(key) { + if (key !== undefined && key !== null) { + delete this._stepCache[key]; + } + delete this._stepCache; + return this; + } + callFuncUseStepCache(prefix, func, params) { + if (typeof func != 'function') return; + if (_status.closeStepCache) return func.apply(null, params); + var cacheKey = "[" + prefix + "]" + get.paramToCacheKey.apply(null, params); + var ret = this.getStepCache(cacheKey); + if (ret === undefined || ret === null) { + ret = func.apply(null, params); + this.putStepCache(cacheKey, ret); + } + return ret; + } + putTempCache(key1, key2, value) { + if (!this._tempCache) { + this._tempCache = {}; + } + if (!this._tempCache[key1]) { + this._tempCache[key1] = {}; + } + this._tempCache[key1][key2] = value; + return value; + } + getTempCache(key1, key2) { + if (!this._tempCache) { + return undefined; + } + if (!this._tempCache[key1]) { + return undefined; + } + return this._tempCache[key1][key2]; + } + cancel(arg1, arg2, notrigger) { + this.untrigger(arg1, arg2); + this.finish(); + if (notrigger != 'notrigger') { + this.trigger(this.name + 'Cancelled'); + if (this.player && lib.phaseName.includes(this.name)) this.player.getHistory('skipped').add(this.name); + } + return this; + } + neutralize(event) { + this.untrigger(); + this.finish(); + this._neutralized = true; + this.trigger('eventNeutralized'); + this._neutralize_event = event || _status.event; + return this; + } + unneutralize() { + this.untrigger(); + delete this._neutralized; + delete this.finished; + if (this.type == 'card' && this.card && this.name == 'sha') this.directHit = true; + return this; + } + goto(step) { + this.step = step - 1; + return this; + } + redo() { + this.step--; + return this; + } + setHiddenSkill(skill) { + if (!this.player) return this; + var hidden = this.player.hiddenSkills.slice(0); + game.expandSkills(hidden); + if (hidden.includes(skill)) this.set('hsskill', skill); + return this; + } + set(key, value) { + if (arguments.length == 1 && Array.isArray(arguments[0])) { + for (var i = 0; i < arguments[0].length; i++) { + if (Array.isArray(arguments[0][i])) { + this.set(arguments[0][i][0], arguments[0][i][1]); + } + } + } + else { + if (typeof key != 'string') { + console.log('warning: using non-string object as event key'); + console.log(key, value); + console.log(_status.event); + } + this[key] = value; + this._set.push([key, value]); + } + return this; + } + /** + * @param {ArrayLike | Function | keyof typeof lib.element.content} item + */ + setContent(item) { + switch (typeof item) { + case "object": + case "function": + if (item instanceof AsyncFunction) { + this.content = item; + } + else this.content = lib.init.parsex(item); + break; + default: + try { + if (!(lib.element.content[item] instanceof AsyncFunction) && !lib.element.content[item]._parsed) { + lib.element.content[item] = lib.init.parsex(lib.element.content[item]); + lib.element.content[item]._parsed = true; + } + } + catch { + throw new Error(`Content ${item} may not exist.\nlib.element.content[${item}] = ${lib.element.content[item]}`); + } + + if (typeof lib.element.content[item] === "undefined") + throw new Error(`Cannot find lib.element.content[${item}]`) + // Generator的状态重置 + else if (lib.element.content[item]._gen) { + this.content = lib.element.content[item].bind({ + gen: null, + last: undefined + }) + } else { + this.content = lib.element.content[item]; + } + break; + } + return this; + } + + /** + * + * @param {Function | keyof typeof lib.element.contents} contents + * @returns {GameEvent} + */ + setContents(contents) { + if (Array.isArray(contents)) this.contents = contents; + else if (contents in lib.element.contents) return this.setContents(lib.element.contents[contents]); + else throw new Error('not supported value.'); + return this; + } + + getLogv() { + for (var i = 1; i <= 3; i++) { + var event = this.getParent(i); + if (event && event.logvid) return event.logvid; + } + return null; + } + send() { + this.player.send(function (name, args, set, event, skills) { + game.me.applySkills(skills); + var next = game.me[name].apply(game.me, args); + for (var i = 0; i < set.length; i++) { + next.set(set[i][0], set[i][1]); + } + if (next._backupevent) { + next.backup(next._backupevent); + } + next._modparent = event; + game.resume(); + }, this.name, this._args || [], this._set, + get.stringifiedResult(this.parent), get.skillState(this.player)); + this.player.wait(); + game.pause(); + return this; + } + resume() { + delete this._cardChoice; + delete this._targetChoice; + delete this._skillChoice; + return this; + } + /** + * 获取事件的父节点。 + * 获取事件链上的指定事件。 + * 默认获取上一个父节点(核心)。 + * @param {number|string|(evt:gameEvent)=>boolean} [level=1] 获取深度(number)/指定名字(string)/指定特征(function) + * @param {boolean} [forced] 若获取不到节点,默认返回{},若forced为true则返回null + * @param {boolean} [includeSelf] 若level不是数字,指定搜索时是否包含事件本身 + * @returns {GameEvent|{}|null} + */ + getParent(level = 1, forced, includeSelf) { + let event = this; + let i = 0; + const toreturn = forced ? null : {}; + const historys = []; + const filter = + typeof level === 'function' ? level : + typeof level === 'number' ? evt => i === level : + evt => evt.name === level; + while (true) { + if (!event) return toreturn; + historys.push(event); + if (filter(event) && (includeSelf || i !== 0)) return event; + if (game.online && event._modparent) event = event._modparent; + else event = event.parent; + if (historys.includes(event)) return toreturn; + i++; + } + } + getTrigger() { + return this.getParent(e => e._trigger, false, true)._trigger; + } + getRand(name) { + if (name) { + if (!this._rand_map) this._rand_map = {}; + if (!this._rand_map[name]) this._rand_map[name] = Math.random(); + return this._rand_map[name]; + } + if (!this._rand) this._rand = Math.random(); + return this._rand; + } + insert(content, map) { + const next = (new lib.element.GameEvent(`${this.name}Inserted`, false)).toPromise(); + this.next.push(next); + next.setContent(content); + Object.entries(map).forEach(entry => next.set(entry[0], entry[1])); + return next; + } + insertAfter(content, map) { + const next = (new lib.element.GameEvent(`${this.name}Inserted`, false)).toPromise(); + this.after.push(next); + next.setContent(content); + Object.entries(map).forEach(entry => next.set(entry[0], entry[1])); + return next; + } + backup(skill) { + this._backup = { + filterButton: this.filterButton, + selectButton: this.selectButton, + filterTarget: this.filterTarget, + selectTarget: this.selectTarget, + filterCard: this.filterCard, + selectCard: this.selectCard, + position: this.position, + forced: this.forced, + fakeforce: this.fakeforce, + _aiexclude: this._aiexclude, + complexSelect: this.complexSelect, + complexCard: this.complexCard, + complexTarget: this.complexTarget, + _cardChoice: this._cardChoice, + _targetChoice: this._targetChoice, + _skillChoice: this._skillChoice, + ai1: this.ai1, + ai2: this.ai2, + filterOk: this.filterOk, + }; + if (skill) { + var info = get.info(skill); + this.skill = skill; + this._aiexclude = []; + if (typeof info.viewAs == 'function') { + if (info.filterButton != undefined) this.filterButton = get.filter(info.filterButton); + if (info.selectButton != undefined) this.selectButton = info.selectButton; + if (info.filterTarget != undefined) this.filterTarget = get.filter(info.filterTarget); + if (info.selectTarget != undefined) this.selectTarget = info.selectTarget; + if (info.filterCard != undefined) { + if (info.ignoreMod) this.ignoreMod = true; + this.filterCard2 = get.filter(info.filterCard); + this.filterCard = function (card, player, event) { + var evt = event || _status.event; + if (!evt.ignoreMod && player) { + var mod = game.checkMod(card, player, 'unchanged', 'cardEnabled2', player); + if (mod != 'unchanged') return mod; + } + return get.filter(evt.filterCard2).apply(this, arguments); + }; + } + if (info.filterOk == undefined) { + this.filterOk = function () { + var evt = _status.event; + var card = get.card(), player = get.player(); + var filter = evt._backup.filterCard; + if (filter && !filter(card, player, evt)) return false; + if (evt._backup.filterOk) return evt._backup.filterOk(); + return true; + }; + } + else this.filterOk = info.filterOk; + if (info.selectCard != undefined) this.selectCard = info.selectCard; + if (info.position != undefined) this.position = info.position; + //if(info.forced!=undefined) this.forced=info.forced; + if (info.complexSelect != undefined) this.complexSelect = info.complexSelect; + if (info.complexCard != undefined) this.complexCard = info.complexCard; + if (info.complexTarget != undefined) this.complexTarget = info.complexTarget; + if (info.ai1 != undefined) this.ai1 = info.ai1; + if (info.ai2 != undefined) this.ai2 = info.ai2; + } + else if (info.viewAs) { + if (info.filterButton != undefined) this.filterButton = get.filter(info.filterButton); + if (info.selectButton != undefined) this.selectButton = info.selectButton; + if (info.filterTarget != undefined) this.filterTarget = get.filter(info.filterTarget); + if (info.selectTarget != undefined) this.selectTarget = info.selectTarget; + if (info.filterCard != undefined) { + if (info.ignoreMod) this.ignoreMod = true; + this.filterCard2 = get.filter(info.filterCard); + this.filterCard = function (card, player, event) { + var evt = event || _status.event; + if (!evt.ignoreMod && player) { + var mod = game.checkMod(card, player, 'unchanged', 'cardEnabled2', player); + if (mod != 'unchanged') return mod; + } + return get.filter(evt.filterCard2).apply(this, arguments); + }; + } + if (info.filterOk == undefined) { + this.filterOk = function () { + var evt = _status.event; + var card = get.card(), player = get.player(); + var filter = evt._backup.filterCard; + if (filter && !filter(card, player, evt)) return false; + if (evt._backup.filterOk) return evt._backup.filterOk(); + return true; + }; + } + else this.filterOk = info.filterOk; + if (info.selectCard != undefined) this.selectCard = info.selectCard; + if (info.position != undefined) this.position = info.position; + //if(info.forced!=undefined) this.forced=info.forced; + if (info.complexSelect != undefined) this.complexSelect = info.complexSelect; + if (info.complexCard != undefined) this.complexCard = info.complexCard; + if (info.complexTarget != undefined) this.complexTarget = info.complexTarget; + if (info.ai1 != undefined) this.ai1 = info.ai1; + if (info.ai2 != undefined) this.ai2 = info.ai2; + } + else { + this.filterButton = info.filterButton ? get.filter(info.filterButton) : undefined; + this.selectButton = info.selectButton; + this.filterTarget = info.filterTarget ? get.filter(info.filterTarget) : undefined; + this.selectTarget = info.selectTarget; + this.filterCard = info.filterCard ? get.filter(info.filterCard) : undefined; + this.selectCard = info.selectCard; + this.position = info.position; + //this.forced=info.forced; + this.complexSelect = info.complexSelect; + this.complexCard = info.complexCard; + this.complexTarget = info.complexTarget; + if (info.ai1 != undefined) this.ai1 = info.ai1; + if (info.ai2 != undefined) this.ai2 = info.ai2; + this.filterOk = info.filterOk; + } + delete this.fakeforce; + } + delete this._cardChoice; + delete this._targetChoice; + delete this._skillChoice; + return this; + } + restore() { + if (this._backup) { + this.filterButton = this._backup.filterButton; + this.selectButton = this._backup.selectButton; + this.filterTarget = this._backup.filterTarget; + this.selectTarget = this._backup.selectTarget; + this.filterCard = this._backup.filterCard; + this.selectCard = this._backup.selectCard; + this.position = this._backup.position; + this.forced = this._backup.forced; + this.fakeforce = this._backup.fakeforce; + this._aiexclude = this._backup._aiexclude; + this.complexSelect = this._backup.complexSelect; + this.complexCard = this._backup.complexCard; + this.complexTarget = this._backup.complexTarget; + this.ai1 = this._backup.ai1; + this.ai2 = this._backup.ai2; + this._cardChoice = this._backup._cardChoice; + this._targetChoice = this._backup._targetChoice; + this._skillChoice = this._backup._skillChoice; + this.filterOk = this._backup.filterOk; + } + delete this.skill; + delete this.ignoreMod; + delete this.filterCard2; + return this; + } + isMine() { + return (this.player && this.player == game.me && !_status.auto && !this.player.isMad() && !game.notMe); + } + isOnline() { + return (this.player && this.player.isOnline()); + } + notLink() { + return this.getParent().name != '_lianhuan' && this.getParent().name != '_lianhuan2'; + } + isPhaseUsing(player) { + var evt = this.getParent('phaseUse'); + if (!evt || evt.name != 'phaseUse') return false; + return !player || player == evt.player; + } + addTrigger(skills, player) { + if (!player || !skills) return this; + let evt = this; + if (typeof skills == 'string') skills = [skills]; + game.expandSkills(skills); + while (true) { + evt = evt.getParent('arrangeTrigger'); + if (!evt || evt.name != 'arrangeTrigger' || !evt.doingList) return this; + const doing = evt.doingList.find(i => i.player === player); + const firstDo = evt.doingList.find(i => i.player === "firstDo"); + const lastDo = evt.doingList.find(i => i.player === "lastDo"); + + skills.forEach(skill => { + const info = lib.skill[skill]; + if (!info.trigger) return; + if (!Object.keys(info.trigger).some(i => { + if (Array.isArray(info.trigger[i])) return info.trigger[i].includes(evt.triggername); + return info.trigger[i] === evt.triggername; + })) return; + + const toadd = { + skill: skill, + player: player, + priority: get.priority(skill), + }; + const map = info.firstDo ? firstDo : info.lastDo ? lastDo : doing; + if (!map) return; + if (map.doneList.some(i => i.skill === toadd.skill && i.player === toadd.player)) return; + if (map.todoList.some(i => i.skill === toadd.skill && i.player === toadd.player)) return; + map.todoList.add(toadd); + if (typeof map.player === 'string') map.todoList.sort((a, b) => (b.priority - a.priority) || (evt.playerMap.indexOf(a) - evt.playerMap.indexOf(b))); + else map.todoList.sort((a, b) => b.priority - a.priority); + }); + } + } + removeTrigger(skills, player) { + if (!player || !skills) return this; + let evt = this; + if (typeof skills == 'string') skills = [skills]; + game.expandSkills(skills); + while (true) { + evt = evt.getParent('arrangeTrigger'); + if (!evt || evt.name != 'arrangeTrigger' || !evt.doingList) return this; + const doing = evt.doingList.find(i => i.player == player); + const firstDo = evt.doingList.find(i => i.player == "firstDo"); + const lastDo = evt.doingList.find(i => i.player == "lastDo"); + + skills.forEach(skill => [doing, firstDo, lastDo].forEach(map => { + if (!map) return; + const toremove = map.todoList.filter(i => i.skill == skill && i.player == player); + if (toremove.length > 0) map.todoList.removeArray(toremove); + })); + } + } + trigger(name) { + if (_status.video) return; + if ((this.name === 'gain' || this.name === 'lose') && !_status.gameDrawed) return; + if (name === 'gameDrawEnd') _status.gameDrawed = true; + if (name === 'gameStart') { + lib.announce.publish('gameStart', {}); + if (_status.brawl && _status.brawl.gameStart) _status.brawl.gameStart(); + if (lib.config.show_cardpile) ui.cardPileButton.style.display = ''; + _status.gameStarted = true; + game.showHistory(); + } + if (!lib.hookmap[name] && !lib.config.compatiblemode) return; + if (!game.players || !game.players.length) return; + const event = this; + let start = [_status.currentPhase, event.source, event.player, game.me, game.players[0]].find(i => get.itemtype(i) == 'player'); + if (!start) return; + if (!game.players.includes(start) && !game.dead.includes(start)) start = game.findNext(start); + const firstDo = { + player: "firstDo", + todoList: [], + doneList: [], + }; + const lastDo = { + player: "lastDo", + todoList: [], + doneList: [], + }; + const doingList = []; + const roles = ['player', 'source', 'target', 'global']; + const playerMap = game.players.concat(game.dead).sortBySeat(start); + let player = start; + let allbool = false; + do { + const doing = { + player: player, + todoList: [], + doneList: [], + listAdded: {}, + addList(skill) { + if (!skill) return; + if (Array.isArray(skill)) return skill.forEach(i => this.addList(i)); + if (this.listAdded[skill]) return; + this.listAdded[skill] = true; + + const info = lib.skill[skill]; + const list = info.firstDo ? firstDo.todoList : info.lastDo ? lastDo.todoList : this.todoList; + list.push({ + skill: skill, + player: this.player, + priority: get.priority(skill), + }); + if (typeof list.player == 'string') list.sort((a, b) => (b.priority - a.priority) || (playerMap.indexOf(a) - playerMap.indexOf(b))); + else list.sort((a, b) => b.priority - a.priority); + allbool = true; + } + }; + + const notemp = player.skills.slice(); + for (const j in player.additionalSkills) { + if (!j.startsWith('hidden:')) notemp.addArray(player.additionalSkills[j]); + } + Object.keys(player.tempSkills).filter(skill => { + if (notemp.includes(skill)) return false; + const expire = player.tempSkills[skill]; + if (typeof expire === 'function') return expire(event, player, name); + if (get.objtype(expire) === 'object') return roles.some(role => { + if (role !== 'global' && player !== event[role]) return false; + if (Array.isArray(expire[role])) return expire[role].includes(name); + return expire[role] === name; + }); + }).forEach(skill => { + delete player.tempSkills[skill]; + player.removeSkill(skill); + }); + + if (lib.config.compatiblemode) { + doing.addList(game.expandSkills(player.getSkills('invisible').concat(lib.skill.global)).filter(skill => { + const info = get.info(skill); + if (!info || !info.trigger) return false; + return roles.some(role => { + if (info.trigger[role] === name) return true; + if (Array.isArray(info.trigger[role]) && info.trigger[role].includes(name)) return true; + }); + })); + } + else roles.forEach(role => { + doing.addList(lib.hook.globalskill[role + '_' + name]); + doing.addList(lib.hook[player.playerid + '_' + role + '_' + name]); + }); + delete doing.listAdded; + delete doing.addList; + doingList.push(doing); + player = player.nextSeat; + } while (player && player !== start); + doingList.unshift(firstDo); + doingList.push(lastDo); + // console.log(name,event.player,doingList.map(i=>({player:i.player,todoList:i.todoList.slice(),doneList:i.doneList.slice()}))) + + if (allbool) { + const next = game.createEvent('arrangeTrigger', false, event); + next.setContent('arrangeTrigger'); + next.doingList = doingList; + next._trigger = event; + next.triggername = name; + next.playerMap = playerMap; + event._triggering = next; + return next; + } + return null; + } + untrigger(all = true, player) { + const evt = this._triggering; + if (all) { + if(all !== 'currentOnly') this._triggered = 5; + if (evt && evt.doingList) { + evt.doingList.forEach(doing => doing.todoList = []); + } + } + else if (player) { + this._notrigger.add(player); + // if(!evt||!evt.doingList) return this; + // const doing=evt.doingList.find(doing=>doing.player==player); + // if(doing) doing.todoList=[]; + } + return this; + } + /** + * 事件转为Promise化 + * + * @returns { GameEventPromise } + */ + toPromise() { + if (!this.#promise) { + this.#promise = new lib.element.GameEventPromise(this); + } + return this.#promise; + } +} diff --git a/noname/library/init/index.js b/noname/library/init/index.js index f63b8b9d0..e58a59956 100644 --- a/noname/library/init/index.js +++ b/noname/library/init/index.js @@ -1,832 +1,844 @@ -import { nonameInitialized, assetURL, userAgent, Uninstantable, GeneratorFunction, AsyncFunction } from "../../util/index.js"; -import { AI as ai } from '../../ai/index.js'; -import { Get as get } from '../../get/index.js'; -import { Game as game } from '../../game/index.js'; -import { Library as lib } from "../index.js"; -import { status as _status } from '../../status/index.js'; -import { UI as ui } from '../../ui/index.js'; -import { GNC as gnc } from '../../gnc/index.js'; - -import { LibInitPromises } from "./promises.js"; - -export class LibInit extends Uninstantable { - /** - * 部分函数的Promise版本 - */ - static promises = LibInitPromises - - static init() { - throw new Error('lib.init.init is moved to noname/init') - } - - static reset() { - if (window.inSplash) return; - if (window.resetExtension) { - if (confirm('游戏似乎未正常载入,有可能因为部分扩展未正常载入,或者因为部分扩展未载入完毕。\n是否禁用扩展并重新打开?')) { - window.resetExtension(); - window.location.reload(); - } - } - else { - if (lib.device) { - if (navigator.notification) { - navigator.notification.confirm( - '游戏似乎未正常载入,是否重置游戏?', - function (index) { - if (index == 2) { - localStorage.removeItem('noname_inited'); - window.location.reload(); - } - else if (index == 3) { - var noname_inited = localStorage.getItem('noname_inited'); - var onlineKey = localStorage.getItem(lib.configprefix + 'key'); - localStorage.clear(); - if (noname_inited) { - localStorage.setItem('noname_inited', noname_inited); - } - if (onlineKey) { - localStorage.setItem(lib.configprefix + 'key', onlineKey); - } - if (indexedDB) indexedDB.deleteDatabase(lib.configprefix + 'data'); - setTimeout(function () { - window.location.reload(); - }, 200); - } - }, - '确认退出', - ['取消', '重新下载', '重置设置'] - ); - } - else { - if (confirm('游戏似乎未正常载入,是否重置游戏?')) { - localStorage.removeItem('noname_inited'); - window.location.reload(); - } - } - } - else { - if (confirm('游戏似乎未正常载入,是否重置游戏?')) { - var onlineKey = localStorage.getItem(lib.configprefix + 'key'); - localStorage.clear(); - if (onlineKey) { - localStorage.setItem(lib.configprefix + 'key', onlineKey); - } - if (indexedDB) indexedDB.deleteDatabase(lib.configprefix + 'data'); - setTimeout(function () { - window.location.reload(); - }, 200); - } - } - } - } - - // 现在改lib.init.onload的都给我无报错被创 - static async onload() { - throw new Error('lib.init.onload is moved to noname/init/onload') - } - - static startOnline() { - 'step 0' - event._resultid = null; - event._result = null; - game.pause(); - 'step 1' - if (result) { - if (event._resultid) { - result.id = event._resultid; - } - game.send('result', result); - } - event.goto(0); - } - - static onfree() { - if (lib.onfree) { - clearTimeout(window.resetGameTimeout); - delete window.resetGameTimeout; - if (!game.syncMenu) { - delete window.resetExtension; - localStorage.removeItem(lib.configprefix + 'disable_extension'); - } - - if (game.removeFile && lib.config.brokenFile.length) { - while (lib.config.brokenFile.length) { - game.removeFile(lib.config.brokenFile.shift()); - } - game.saveConfigValue('brokenFile'); - } - - var onfree = lib.onfree; - delete lib.onfree; - var loop = function () { - if (onfree.length) { - (onfree.shift())(); - setTimeout(loop, 100); - } - }; - setTimeout(loop, 500); - if (!_status.new_tutorial) game.saveConfig('menu_loadondemand', true, lib.config.mode); - } - } - - static connection(ws) { - const client = new lib.element.Client(ws); - lib.node.clients.push(client); - if (window.isNonameServer) { - document.querySelector('#server_count').innerHTML = lib.node.clients.length; - } - ws.on('message', function (messagestr) { - var message; - try { - message = JSON.parse(messagestr); - if (!Array.isArray(message) || - typeof lib.message.server[message[0]] !== 'function') { - throw ('err'); - } - for (var i = 1; i < message.length; i++) { - message[i] = get.parsedResult(message[i]); - } - } - catch (e) { - console.log(e); - console.log('invalid message: ' + messagestr); - return; - } - lib.message.server[message.shift()].apply(client, message); - }); - ws.on('close', function () { - client.close(); - }); - client.send('opened'); - } - - static sheet() { - var style = document.createElement('style'); - document.head.appendChild(style); - for (var i = 0; i < arguments.length; i++) { - if (typeof arguments[i] == 'string') { - style.sheet.insertRule(arguments[i], 0); - } - } - return style; - } - - static css(path, file, before) { - const style = document.createElement("link"); - style.rel = "stylesheet"; - if (path) { - if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); - if (file) path = `${path}${/^db:extension-[^:]*$/.test(path) ? ':' : '/'}${file}.css`; - (path.startsWith('db:') ? game.getDB('image', path.slice(3)).then(get.objectURL) : new Promise(resolve => resolve(path))).then(resolvedPath => { - style.href = resolvedPath; - if (typeof before == 'function') { - style.addEventListener('load', before); - document.head.appendChild(style); - } - else if (before) document.head.insertBefore(style, before); - else document.head.appendChild(style); - }); - } - return style; - } - - //在扩展的precontent中调用,用于加载扩展必需的JS文件。 - //If any of the parameters is an Array, corresponding files will be loaded in order - //如果任意参数为数组,则按顺序加载加载相应的文件 - static jsForExtension(path, file, onLoad, onError) { - if (!_status.javaScriptExtensions) _status.javaScriptExtensions = []; - _status.javaScriptExtensions.push({ - path: path, - file: file, - onLoad: onLoad, - onError: onError - }); - } - - static js(path, file, onLoad, onError) { - if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); - if (path == `${lib.assetURL}mode` && lib.config.all.stockmode.indexOf(file) == -1) { - lib.genAwait(lib.init[`setMode_${file}`]()).then(onLoad); - return; - } - if (Array.isArray(file)) { - file.forEach(value => lib.init.js(path, value, onLoad, onError)); - return; - } - let scriptSource = file ? `${path}${/^db:extension-[^:]*$/.test(path) ? ':' : '/'}${file}.js` : path; - if (path.startsWith('http')) scriptSource += `?rand=${get.id()}`; - else if (lib.config.fuck_sojson && scriptSource.includes('extension') != -1 && scriptSource.startsWith(lib.assetURL)) { - const pathToRead = scriptSource.slice(lib.assetURL.length); - const alertMessage = `检测到您安装了使用免费版sojson进行加密的扩展。请谨慎使用这些扩展,避免游戏数据遭到破坏。\n扩展文件:${pathToRead}`; - if (typeof game.readFileAsText == 'function') game.readFileAsText(pathToRead, result => { - if (result.includes('sojson') || result.includes('jsjiami') || result.includes('var _0x')) alert(alertMessage); - }, () => void 0); - else if (location.origin != 'file://') lib.init.reqSync(pathToRead, function () { - const result = this.responseText; - if (result.includes('sojson') || result.includes('jsjiami') || result.includes('var _0x')) alert(alertMessage); - }, () => void 0); - } - const script = document.createElement('script'); - (scriptSource.startsWith('db:') ? game.getDB('image', scriptSource.slice(3)).then(get.objectURL) : new Promise(resolve => resolve(scriptSource))).then(resolvedScriptSource => { - script.src = resolvedScriptSource; - if (path.startsWith('http')) script.addEventListener('load', () => script.remove()); - document.head.appendChild(script); - if (typeof onLoad == 'function') script.addEventListener('load', onLoad); - if (typeof onError == 'function') script.addEventListener('error', onError); - }); - return script; - } - - /** - * 同步lib.init.js - * @returns { void } - */ - static jsSync(path, file, onLoad, onError) { - if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { - const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); - if (typeof onError == 'function') onError(e); - else throw e; - return; - } - if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); - if (path == `${lib.assetURL}mode` && lib.config.all.stockmode.indexOf(file) == -1) { - lib.genAwait(lib.init[`setMode_${file}`]()).then(onLoad); - return; - } - if (Array.isArray(file)) { - return file.forEach(value => lib.init.jsSync(path, value, onLoad, onError)); - } - let scriptSource; - if (!file) scriptSource = path; - else scriptSource = `${path}/${file}.js`; - if (path.startsWith('http')) scriptSource += `?rand=${get.id()}`; - const xmlHttpRequest = new XMLHttpRequest(); - let data; - xmlHttpRequest.addEventListener("load", () => { - if (![0, 200].includes(xmlHttpRequest.status)) { - // @ts-ignore - if (typeof onError == 'function') onError(new Error(oReq.statusText || oReq.status)); - return; - } - data = xmlHttpRequest.responseText; - if (!data) { - if (typeof onError == 'function') onError(new Error(`${scriptSource}加载失败!`)); - return; - } - if (lib.config.fuck_sojson && scriptSource.includes('extension') != -1 && scriptSource.startsWith(lib.assetURL)) { - const pathToRead = scriptSource.slice(lib.assetURL.length); - if (data.includes('sojson') || data.includes('jsjiami') || data.includes('var _0x')) alert(`检测到您安装了使用免费版sojson进行加密的扩展。请谨慎使用这些扩展,避免游戏数据遭到破坏。\n扩展文件:${pathToRead}`); - } - try { - window.eval(data); - if (typeof onLoad == 'function') onLoad(); - } - catch (error) { - if (typeof onError == 'function') onError(error); - } - }); - if (typeof onError == 'function') xmlHttpRequest.addEventListener("error", onError); - xmlHttpRequest.open("GET", scriptSource, false); - xmlHttpRequest.send(); - } - - static req(str, onload, onerror, master) { - let sScriptURL; - if (str.startsWith('http')) sScriptURL = str; - else if (str.startsWith('local:')) { - if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { - const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); - if (typeof onerror == 'function') onerror(e); - else throw e; - return; - } - sScriptURL = lib.assetURL + str.slice(6); - } - else { - let url = get.url(master); - if (url[url.length - 1] != '/') url += '/'; - sScriptURL = url + str; - } - const oReq = new XMLHttpRequest(); - if (typeof onload == 'function') oReq.addEventListener("load", result => { - if (![0, 200].includes(oReq.status)) { - // @ts-ignore - if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); - return; - } - onload(result); - }); - if (typeof onerror == 'function') oReq.addEventListener("error", onerror); - oReq.open("GET", sScriptURL); - oReq.send(); - } - - /** - * 同步lib.init.req - */ - static reqSync(str, onload, onerror, master) { - let sScriptURL; - if (str.startsWith('http')) sScriptURL = str; - else if (str.startsWith('local:')) { - if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { - const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); - if (typeof onerror == 'function') onerror(e); - else throw e; - return; - } - sScriptURL = lib.assetURL + str.slice(6); - } - else { - let url = get.url(master); - if (url[url.length - 1] != '/') url += '/'; - sScriptURL = url + str; - } - const oReq = new XMLHttpRequest(); - if (typeof onload == 'function') oReq.addEventListener("load", result => { - if (![0, 200].includes(oReq.status)) { - // @ts-ignore - if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); - return; - } - onload(result); - }); - if (typeof onerror == 'function') oReq.addEventListener("error", onerror); - oReq.open("GET", sScriptURL, false); - oReq.send(); - if (typeof onload !== 'function') return oReq.responseText; - } - - static json(url, onload, onerror) { - const oReq = new XMLHttpRequest(); - if (typeof onload == 'function') oReq.addEventListener("load", () => { - if (![0, 200].includes(oReq.status)) { - // @ts-ignore - if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); - return; - } - let result; - try { - result = JSON.parse(oReq.responseText); - if (!result) throw ('err'); - } - catch (e) { - if (typeof onerror == 'function') onerror(e); - return; - } - onload(result); - }); - if (typeof onerror == 'function') oReq.addEventListener("error", onerror); - oReq.open("GET", url); - oReq.send(); - } - - /** - * 同步lib.init.json - */ - static jsonSync(url, onload, onerror) { - if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { - const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); - if (typeof onerror == 'function') onerror(e); - else throw e; - return; - } - const oReq = new XMLHttpRequest(); - if (typeof onload == 'function') oReq.addEventListener("load", () => { - if (![0, 200].includes(oReq.status)) { - // @ts-ignore - if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); - return; - } - let result; - try { - result = JSON.parse(oReq.responseText); - if (!result) throw ('err'); - } - catch (e) { - if (typeof onerror == 'function') onerror(e); - return; - } - onload(result); - }); - if (typeof onerror == 'function') oReq.addEventListener("error", onerror); - oReq.open("GET", url, false); - oReq.send(); - } - - static cssstyles() { - if (ui.css.styles) { - ui.css.styles.remove(); - } - ui.css.styles = lib.init.sheet(); - ui.css.styles.sheet.insertRule('#arena .player>.name,#arena .button.character>.name {font-family: ' + (lib.config.name_font || 'xinwei') + ',xinwei}', 0); - ui.css.styles.sheet.insertRule('#arena .player>.name,.button.character>.name {font-family: ' + (lib.config.name_font || 'xinwei') + ',xinwei}', 0); - ui.css.styles.sheet.insertRule('#arena .player .identity>div {font-family: ' + (lib.config.identity_font || 'huangcao') + ',xinwei}', 0); - ui.css.styles.sheet.insertRule('.button.character.newstyle>.identity {font-family: ' + (lib.config.identity_font || 'huangcao') + ',xinwei}', 0); - if (lib.config.cardtext_font && lib.config.cardtext_font != 'default') { - ui.css.styles.sheet.insertRule('.card div:not(.info):not(.background) {font-family: ' + lib.config.cardtext_font + ';}', 0); - } - if (lib.config.global_font && lib.config.global_font != 'default') { - ui.css.styles.sheet.insertRule('#window {font-family: ' + lib.config.global_font + ',xinwei}', 0); - ui.css.styles.sheet.insertRule('#window #control{font-family: STHeiti,SimHei,Microsoft JhengHei,Microsoft YaHei,WenQuanYi Micro Hei,Suits,Helvetica,Arial,sans-serif}', 0); - } - switch (lib.config.glow_phase) { - case 'yellow': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgb(217, 152, 62) 0 0 15px, rgb(217, 152, 62) 0 0 15px !important;}', 0); break; - case 'green': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgba(10, 155, 67, 1) 0 0 15px, rgba(10, 155, 67, 1) 0 0 15px !important;}', 0); break; - case 'purple': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgb(189, 62, 170) 0 0 15px, rgb(189, 62, 170) 0 0 15px !important;}', 0); break; - } - } - - static layout(layout, nosave) { - const loadingScreen = ui.create.div('.loading-screen', document.body), loadingScreenStyle = loadingScreen.style; - loadingScreenStyle.animationDuration = '1s'; - loadingScreenStyle.animationFillMode = 'forwards'; - loadingScreenStyle.animationName = 'opacity-0-1'; - if (layout == 'default') layout = 'mobile'; - if (!nosave) game.saveConfig('layout', layout); - game.layout = layout; - ui.arena.hide(); - new Promise(resolve => setTimeout(resolve, 500)).then(() => { - if (game.layout == 'default') { - ui.css.layout.href = ''; - } - else { - ui.css.layout.href = lib.assetURL + 'layout/' + game.layout + '/layout.css'; - } - if (game.layout == 'mobile' || game.layout == 'long') { - ui.arena.classList.add('mobile'); - } - else { - ui.arena.classList.remove('mobile'); - } - if (game.layout == 'mobile' || game.layout == 'long' || game.layout == 'long2' || game.layout == 'nova') { - if (game.me && game.me.node.handcards2.childNodes.length) { - while (game.me.node.handcards2.childNodes.length) { - game.me.node.handcards1.appendChild(game.me.node.handcards2.firstChild); - } - } - } - if (game.layout == 'default') { - ui.arena.classList.add('oldlayout'); - } - else { - ui.arena.classList.remove('oldlayout'); - } - if (lib.config.cardshape == 'oblong' && (game.layout == 'long' || game.layout == 'mobile' || game.layout == 'long2' || game.layout == 'nova')) { - ui.arena.classList.add('oblongcard'); - ui.window.classList.add('oblongcard'); - } - else { - ui.arena.classList.remove('oblongcard'); - ui.window.classList.remove('oblongcard'); - } - //if(lib.config.textequip=='text'&&(game.layout=='long'||game.layout=='mobile')){ - if (game.layout == 'long' || game.layout == 'mobile') { - ui.arena.classList.add('textequip'); - } - else { - ui.arena.classList.remove('textequip'); - } - if (get.is.phoneLayout()) { - ui.css.phone.href = lib.assetURL + 'layout/default/phone.css'; - ui.arena.classList.add('phone'); - } - else { - ui.css.phone.href = ''; - ui.arena.classList.remove('phone'); - } - for (var i = 0; i < game.players.length; i++) { - if (get.is.linked2(game.players[i])) { - if (game.players[i].classList.contains('linked')) { - game.players[i].classList.remove('linked'); - game.players[i].classList.add('linked2'); - } - } - else { - if (game.players[i].classList.contains('linked2')) { - game.players[i].classList.remove('linked2'); - game.players[i].classList.add('linked'); - } - } - } - if (game.layout == 'long' || game.layout == 'long2') { - ui.arena.classList.add('long'); - } - else { - ui.arena.classList.remove('long'); - } - if (lib.config.player_border != 'wide' || game.layout == 'long' || game.layout == 'long2') { - ui.arena.classList.add('slim_player'); - } - else { - ui.arena.classList.remove('slim_player'); - } - if (lib.config.player_border == 'normal' && lib.config.mode != 'brawl' && (game.layout == 'long' || game.layout == 'long2')) { - ui.arena.classList.add('lslim_player'); - } - else { - ui.arena.classList.remove('lslim_player'); - } - if (lib.config.player_border == 'slim') { - ui.arena.classList.add('uslim_player'); - } - else { - ui.arena.classList.remove('uslim_player'); - } - if (lib.config.player_border == 'narrow') { - ui.arena.classList.add('mslim_player'); - } - else { - ui.arena.classList.remove('mslim_player'); - } - ui.updatej(); - ui.updatem(); - return new Promise(resolve => setTimeout(resolve, 100)); - }).then(() => { - ui.arena.show(); - if (game.me) game.me.update(); - return new Promise(resolve => setTimeout(resolve, 500)); - }).then(() => { - ui.updatex(); - ui.updatePlayerPositions(); - return new Promise(resolve => setTimeout(resolve, 500)); - }).then(() => { - ui.updatec(); - loadingScreenStyle.animationName = 'opacity-1-0'; - loadingScreen.addEventListener('animationend', animationEvent => animationEvent.target.remove()); - }); - } - - static background() { - if (lib.config.image_background_random) { - var list = []; - for (var i in lib.configMenu.appearence.config.image_background.item) { - if (i == 'default') continue; - list.push(i); - } - list.remove(lib.config.image_background); - localStorage.setItem(lib.configprefix + 'background', JSON.stringify(list)); - } - else if (lib.config.image_background && lib.config.image_background != 'default' && !lib.config.image_background.startsWith('custom_')) { - localStorage.setItem(lib.configprefix + 'background', lib.config.image_background); - } - else if (lib.config.image_background == 'default' && lib.config.theme == 'simple') { - localStorage.setItem(lib.configprefix + 'background', 'ol_bg'); - } - else { - localStorage.removeItem(lib.configprefix + 'background'); - } - } - - /** - * - * @param {*} item - * @param {Function} [scope] 作用域 - * @returns - */ - static parsex(item, scope) { - //by 诗笺、Tipx-L - /** - * @param {Function} func - */ - function Legacy(func) { - //Remove all comments - //移除所有注释 - let str = func.toString().replace(/((?:(?:^[ \t]*)?(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/(?:[ \t]*\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/)))?|\/\/(?:[^\\]|\\(?:\r?\n)?)*?(?:\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/))|(?=\r?\n))))+)|("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|(?:\r?\n|[\s\S])[^/"'\\\s]*)/mg, '$2').trim(); - //获取第一个 { 后的所有字符 - str = str.slice(str.indexOf('{') + 1); - //判断代码中是否有debugger - let regex = /event\.debugger\(\)/; - let hasDebugger = false; - let insertDebugger = `yield code=>eval(code);`; - let debuggerSkip = 0; - let debuggerResult; - while ((debuggerResult = str.slice(debuggerSkip).match(regex)) != null) { - let debuggerCopy = str; - debuggerCopy = debuggerCopy.slice(0, debuggerSkip + debuggerResult.index) + insertDebugger + debuggerCopy.slice(debuggerSkip + debuggerResult.index + debuggerResult[0].length, -1); - //测试是否有错误 - try { - new GeneratorFunction(debuggerCopy); - str = debuggerCopy + '}'; - debuggerSkip += debuggerResult.index + insertDebugger.length; - hasDebugger = true; - } catch (error) { - debuggerSkip += debuggerResult.index + debuggerResult[0].length; - } - } - //func中要写步骤的话,必须要写step 0 - if (str.indexOf('step 0') == -1) { - str = '{if(event.step==1) {event.finish();return;}\n' + str; - } else { - let skip = 0; - let k = 0; - let result; - //去除99个step的限制 - while ((result = str.slice(skip).match(new RegExp(`['"]step ${k}['"]`))) != null) { - let insertStr; - if (k == 0) { - insertStr = `switch(step){case 0:`; - } else { - insertStr = `break;case ${k}:`; - } - let copy = str; - copy = copy.slice(0, skip + result.index) + insertStr + copy.slice(skip + result.index + result[0].length); - //测试是否有错误 - try { - new (hasDebugger ? GeneratorFunction : Function)(copy); - str = copy; - skip += result.index + insertStr.length; - } catch (error) { - k--; - skip += result.index + result[0].length; - } - k++; - } - str = `if(event.step==${k}){event.finish();return;}` + str; - } - if (!scope) { - return (new (hasDebugger ? GeneratorFunction : Function)('event', 'step', 'source', 'player', 'target', 'targets', - 'card', 'cards', 'skill', 'forced', 'num', 'trigger', 'result', - '_status', 'lib', 'game', 'ui', 'get', 'ai', str)); - } else { - return scope(`function${hasDebugger ? '*' : ''} anonymous(event,step,source,player,target,targets, - card,cards,skill,forced,num,trigger,result, - _status,lib,game,ui,get,ai){${str}}; anonymous;`); - } - } - switch (typeof item) { - case "object": - if (Array.isArray(item)) { - let lastEvent = null; - return function* (event, step, source, player, target, targets, card, cards, skill, forced, num, trigger, result, _status, lib, game, ui, get, ai) { - if (step >= item.length) return event.finish(); - var current = item[step]; - if (typeof current != "function") throw new Error(`content ${step} of ${event.name} is not vaild: ${current}`); - var currentResult = current(event, { - event: event, - step: step, - source: source, - player: player, - target: target, - targets: targets, - card: card, - cards: cards, - skill: skill, - forced: forced, - num: num, - trigger: trigger, - result: result - }, (lastEvent && ("result" in lastEvent)) ? lastEvent.result : null); - // TODO: use `event.debugger` to replace source - if (gnc.is.generator(currentResult)) lastEvent = yield* currentResult; - else lastEvent = currentResult; - } - } - else { - if (Symbol.iterator in item) return lib.init.parsex(Array.from(item)); - if (item.toString !== Object.prototype.toString) return lib.init.parsex(item.toString()); - if ("render" in item) { - // TODO: Object Render Parse - throw new Error("NYI: Object Render Parse"); - } - // TODO: Object Other Parse - throw new Error("NYI: Object Other Parse"); - } - case "function": - if (gnc.is.generatorFunc(item)) { - let gen, lastEvent; - return function* (event, step, source, player, target, targets, card, cards, skill, forced, num, trigger, result, _status, lib, game, ui, get, ai) { - event.step = NaN; - if (!gen) gen = item(event, { - event: event, - step: step, - source: source, - player: player, - target: target, - targets: targets, - card: card, - cards: cards, - skill: skill, - forced: forced, - num: num, - trigger: trigger, - result: result - }); - var res = gen.next((lastEvent && (typeof lastEvent == 'object') && ("result" in lastEvent)) ? lastEvent.result : lastEvent); - if (res.done){ - gen = null; - 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; - // falls through - default: - return Legacy(item); - } - } - - static eval(func) { - if (typeof func == 'function') { - return eval('(' + func.toString() + ')'); - } - else if (typeof func == 'object') { - for (var i in func) { - if (Object.prototype.hasOwnProperty.call(func, i)) { - if(typeof func[i] == 'function'){ - let checkObject = {}; - checkObject[i] = func[i]; - return eval(`(function(){return ${get.stringify(checkObject)};})()`)[i]; - }else{ - func[i] = lib.init.eval(func[i]); - } - } - } - } - return func; - } - - static encode(strUni) { - var strUtf = strUni.replace( - /[\u0080-\u07ff]/g, function (c) { - var cc = c.charCodeAt(0); - return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); - }); - strUtf = strUtf.replace( - /[\u0800-\uffff]/g, function (c) { - var cc = c.charCodeAt(0); - return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f); - }); - return btoa(strUtf); - } - - static decode(str) { - var strUtf = atob(str); - var strUni = strUtf.replace( - /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, function (c) { - var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f); - return String.fromCharCode(cc); - }); - strUni = strUni.replace( - /[\u00c0-\u00df][\u0080-\u00bf]/g, function (c) { - var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f; - return String.fromCharCode(cc); - }); - return strUni; - } - - static stringify(obj) { - var str = '{' - for (var i in obj) { - str += '"' + i + '":' - if (Object.prototype.toString.call(obj[i]) == '[object Object]') { - str += lib.init.stringify(obj[i]); - } - else if (typeof obj[i] == 'function') { - str += obj[i].toString(); - } - else { - str += JSON.stringify(obj[i]); - } - str += ',' - } - str += '}'; - return str; - } - - static stringifySkill(obj) { - var str = ''; - for (var i in obj) { - str += i + ':' - if (Object.prototype.toString.call(obj[i]) == '[object Object]') { - str += '{\n' + lib.init.stringifySkill(obj[i]) + '}'; - } - else if (typeof obj[i] == 'function') { - str += obj[i].toString().replace(/\t/g, ''); - } - else { - str += JSON.stringify(obj[i]); - } - str += ',\n' - } - return str; - } - - /** - * 在返回当前加载的esm模块相对位置。 - * @param {*} url 传入import.meta.url - */ - static getCurrentFileLocation(url){ - let head = window.location.href.slice(0,window.location.href.lastIndexOf('/')+1); - let ret = url.replace(head,''); - return decodeURIComponent(ret); - } -} +import { nonameInitialized, assetURL, userAgent, Uninstantable, GeneratorFunction, AsyncFunction } from "../../util/index.js"; +import { AI as ai } from '../../ai/index.js' +import { Get as get } from '../../get/index.js' +import { Game, Game as game } from '../../game/index.js' +import { Library as lib } from "../index.js" +import { status as _status } from '../../status/index.js' +import { UI as ui } from '../../ui/index.js' +import { GNC as gnc } from '../../gnc/index.js' + +import { LibInitPromises } from "./promises.js" +import { GameEvent } from "../element/gameEvent.js" +import { GameEventPromise } from "../element/gameEventPromise.js" + +export class LibInit extends Uninstantable { + /** + * 部分函数的Promise版本 + */ + static promises = LibInitPromises + + static init() { + throw new Error('lib.init.init is moved to noname/init') + } + + static reset() { + if (window.inSplash) return; + if (window.resetExtension) { + if (confirm('游戏似乎未正常载入,有可能因为部分扩展未正常载入,或者因为部分扩展未载入完毕。\n是否禁用扩展并重新打开?')) { + window.resetExtension(); + window.location.reload(); + } + } + else { + if (lib.device) { + if (navigator.notification) { + navigator.notification.confirm( + '游戏似乎未正常载入,是否重置游戏?', + function (index) { + if (index == 2) { + localStorage.removeItem('noname_inited'); + window.location.reload(); + } + else if (index == 3) { + var noname_inited = localStorage.getItem('noname_inited'); + var onlineKey = localStorage.getItem(lib.configprefix + 'key'); + localStorage.clear(); + if (noname_inited) { + localStorage.setItem('noname_inited', noname_inited); + } + if (onlineKey) { + localStorage.setItem(lib.configprefix + 'key', onlineKey); + } + if (indexedDB) indexedDB.deleteDatabase(lib.configprefix + 'data'); + setTimeout(function () { + window.location.reload(); + }, 200); + } + }, + '确认退出', + ['取消', '重新下载', '重置设置'] + ); + } + else { + if (confirm('游戏似乎未正常载入,是否重置游戏?')) { + localStorage.removeItem('noname_inited'); + window.location.reload(); + } + } + } + else { + if (confirm('游戏似乎未正常载入,是否重置游戏?')) { + var onlineKey = localStorage.getItem(lib.configprefix + 'key'); + localStorage.clear(); + if (onlineKey) { + localStorage.setItem(lib.configprefix + 'key', onlineKey); + } + if (indexedDB) indexedDB.deleteDatabase(lib.configprefix + 'data'); + setTimeout(function () { + window.location.reload(); + }, 200); + } + } + } + } + + // 现在改lib.init.onload的都给我无报错被创 + static async onload() { + throw new Error('lib.init.onload is moved to noname/init/onload') + } + + static startOnline() { + 'step 0' + event._resultid = null; + event._result = null; + game.pause(); + 'step 1' + if (result) { + if (event._resultid) { + result.id = event._resultid; + } + game.send('result', result); + } + event.goto(0); + } + + static onfree() { + if (lib.onfree) { + clearTimeout(window.resetGameTimeout); + delete window.resetGameTimeout; + if (!game.syncMenu) { + delete window.resetExtension; + localStorage.removeItem(lib.configprefix + 'disable_extension'); + } + + if (game.removeFile && lib.config.brokenFile.length) { + while (lib.config.brokenFile.length) { + game.removeFile(lib.config.brokenFile.shift()); + } + game.saveConfigValue('brokenFile'); + } + + var onfree = lib.onfree; + delete lib.onfree; + var loop = function () { + if (onfree.length) { + (onfree.shift())(); + setTimeout(loop, 100); + } + }; + setTimeout(loop, 500); + if (!_status.new_tutorial) game.saveConfig('menu_loadondemand', true, lib.config.mode); + } + } + + static connection(ws) { + const client = new lib.element.Client(ws); + lib.node.clients.push(client); + if (window.isNonameServer) { + document.querySelector('#server_count').innerHTML = lib.node.clients.length; + } + ws.on('message', function (messagestr) { + var message; + try { + message = JSON.parse(messagestr); + if (!Array.isArray(message) || + typeof lib.message.server[message[0]] !== 'function') { + throw ('err'); + } + for (var i = 1; i < message.length; i++) { + message[i] = get.parsedResult(message[i]); + } + } + catch (e) { + console.log(e); + console.log('invalid message: ' + messagestr); + return; + } + lib.message.server[message.shift()].apply(client, message); + }); + ws.on('close', function () { + client.close(); + }); + client.send('opened'); + } + + static sheet() { + var style = document.createElement('style'); + document.head.appendChild(style); + for (var i = 0; i < arguments.length; i++) { + if (typeof arguments[i] == 'string') { + style.sheet.insertRule(arguments[i], 0); + } + } + return style; + } + + static css(path, file, before) { + const style = document.createElement("link"); + style.rel = "stylesheet"; + if (path) { + if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); + if (file) path = `${path}${/^db:extension-[^:]*$/.test(path) ? ':' : '/'}${file}.css`; + (path.startsWith('db:') ? game.getDB('image', path.slice(3)).then(get.objectURL) : new Promise(resolve => resolve(path))).then(resolvedPath => { + style.href = resolvedPath; + if (typeof before == 'function') { + style.addEventListener('load', before); + document.head.appendChild(style); + } + else if (before) document.head.insertBefore(style, before); + else document.head.appendChild(style); + }); + } + return style; + } + + //在扩展的precontent中调用,用于加载扩展必需的JS文件。 + //If any of the parameters is an Array, corresponding files will be loaded in order + //如果任意参数为数组,则按顺序加载加载相应的文件 + static jsForExtension(path, file, onLoad, onError) { + if (!_status.javaScriptExtensions) _status.javaScriptExtensions = []; + _status.javaScriptExtensions.push({ + path: path, + file: file, + onLoad: onLoad, + onError: onError + }); + } + + static js(path, file, onLoad, onError) { + if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); + if (path == `${lib.assetURL}mode` && lib.config.all.stockmode.indexOf(file) == -1) { + lib.genAwait(lib.init[`setMode_${file}`]()).then(onLoad); + return; + } + if (Array.isArray(file)) { + file.forEach(value => lib.init.js(path, value, onLoad, onError)); + return; + } + let scriptSource = file ? `${path}${/^db:extension-[^:]*$/.test(path) ? ':' : '/'}${file}.js` : path; + if (path.startsWith('http')) scriptSource += `?rand=${get.id()}`; + else if (lib.config.fuck_sojson && scriptSource.includes('extension') != -1 && scriptSource.startsWith(lib.assetURL)) { + const pathToRead = scriptSource.slice(lib.assetURL.length); + const alertMessage = `检测到您安装了使用免费版sojson进行加密的扩展。请谨慎使用这些扩展,避免游戏数据遭到破坏。\n扩展文件:${pathToRead}`; + if (typeof game.readFileAsText == 'function') game.readFileAsText(pathToRead, result => { + if (result.includes('sojson') || result.includes('jsjiami') || result.includes('var _0x')) alert(alertMessage); + }, () => void 0); + else if (location.origin != 'file://') lib.init.reqSync(pathToRead, function () { + const result = this.responseText; + if (result.includes('sojson') || result.includes('jsjiami') || result.includes('var _0x')) alert(alertMessage); + }, () => void 0); + } + const script = document.createElement('script'); + (scriptSource.startsWith('db:') ? game.getDB('image', scriptSource.slice(3)).then(get.objectURL) : new Promise(resolve => resolve(scriptSource))).then(resolvedScriptSource => { + script.src = resolvedScriptSource; + if (path.startsWith('http')) script.addEventListener('load', () => script.remove()); + document.head.appendChild(script); + if (typeof onLoad == 'function') script.addEventListener('load', onLoad); + if (typeof onError == 'function') script.addEventListener('error', onError); + }); + return script; + } + + /** + * 同步lib.init.js + * @returns { void } + */ + static jsSync(path, file, onLoad, onError) { + if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { + const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); + if (typeof onError == 'function') onError(e); + else throw e; + return; + } + if (path[path.length - 1] == '/') path = path.slice(0, path.length - 1); + if (path == `${lib.assetURL}mode` && lib.config.all.stockmode.indexOf(file) == -1) { + lib.genAwait(lib.init[`setMode_${file}`]()).then(onLoad); + return; + } + if (Array.isArray(file)) { + return file.forEach(value => lib.init.jsSync(path, value, onLoad, onError)); + } + let scriptSource; + if (!file) scriptSource = path; + else scriptSource = `${path}/${file}.js`; + if (path.startsWith('http')) scriptSource += `?rand=${get.id()}`; + const xmlHttpRequest = new XMLHttpRequest(); + let data; + xmlHttpRequest.addEventListener("load", () => { + if (![0, 200].includes(xmlHttpRequest.status)) { + // @ts-ignore + if (typeof onError == 'function') onError(new Error(oReq.statusText || oReq.status)); + return; + } + data = xmlHttpRequest.responseText; + if (!data) { + if (typeof onError == 'function') onError(new Error(`${scriptSource}加载失败!`)); + return; + } + if (lib.config.fuck_sojson && scriptSource.includes('extension') != -1 && scriptSource.startsWith(lib.assetURL)) { + const pathToRead = scriptSource.slice(lib.assetURL.length); + if (data.includes('sojson') || data.includes('jsjiami') || data.includes('var _0x')) alert(`检测到您安装了使用免费版sojson进行加密的扩展。请谨慎使用这些扩展,避免游戏数据遭到破坏。\n扩展文件:${pathToRead}`); + } + try { + window.eval(data); + if (typeof onLoad == 'function') onLoad(); + } + catch (error) { + if (typeof onError == 'function') onError(error); + } + }); + if (typeof onError == 'function') xmlHttpRequest.addEventListener("error", onError); + xmlHttpRequest.open("GET", scriptSource, false); + xmlHttpRequest.send(); + } + + static req(str, onload, onerror, master) { + let sScriptURL; + if (str.startsWith('http')) sScriptURL = str; + else if (str.startsWith('local:')) { + if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { + const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); + if (typeof onerror == 'function') onerror(e); + else throw e; + return; + } + sScriptURL = lib.assetURL + str.slice(6); + } + else { + let url = get.url(master); + if (url[url.length - 1] != '/') url += '/'; + sScriptURL = url + str; + } + const oReq = new XMLHttpRequest(); + if (typeof onload == 'function') oReq.addEventListener("load", result => { + if (![0, 200].includes(oReq.status)) { + // @ts-ignore + if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); + return; + } + onload(result); + }); + if (typeof onerror == 'function') oReq.addEventListener("error", onerror); + oReq.open("GET", sScriptURL); + oReq.send(); + } + + /** + * 同步lib.init.req + */ + static reqSync(str, onload, onerror, master) { + let sScriptURL; + if (str.startsWith('http')) sScriptURL = str; + else if (str.startsWith('local:')) { + if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { + const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); + if (typeof onerror == 'function') onerror(e); + else throw e; + return; + } + sScriptURL = lib.assetURL + str.slice(6); + } + else { + let url = get.url(master); + if (url[url.length - 1] != '/') url += '/'; + sScriptURL = url + str; + } + const oReq = new XMLHttpRequest(); + if (typeof onload == 'function') oReq.addEventListener("load", result => { + if (![0, 200].includes(oReq.status)) { + // @ts-ignore + if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); + return; + } + onload(result); + }); + if (typeof onerror == 'function') oReq.addEventListener("error", onerror); + oReq.open("GET", sScriptURL, false); + oReq.send(); + if (typeof onload !== 'function') return oReq.responseText; + } + + static json(url, onload, onerror) { + const oReq = new XMLHttpRequest(); + if (typeof onload == 'function') oReq.addEventListener("load", () => { + if (![0, 200].includes(oReq.status)) { + // @ts-ignore + if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); + return; + } + let result; + try { + result = JSON.parse(oReq.responseText); + if (!result) throw ('err'); + } + catch (e) { + if (typeof onerror == 'function') onerror(e); + return; + } + onload(result); + }); + if (typeof onerror == 'function') oReq.addEventListener("error", onerror); + oReq.open("GET", url); + oReq.send(); + } + + /** + * 同步lib.init.json + */ + static jsonSync(url, onload, onerror) { + if (lib.assetURL.length == 0 && location.origin == 'file://' && typeof game.readFile == 'undefined') { + const e = new Error('浏览器file协议下无法使用此api,请在http/https协议下使用此api'); + if (typeof onerror == 'function') onerror(e); + else throw e; + return; + } + const oReq = new XMLHttpRequest(); + if (typeof onload == 'function') oReq.addEventListener("load", () => { + if (![0, 200].includes(oReq.status)) { + // @ts-ignore + if (typeof onerror == 'function') onerror(new Error(oReq.statusText || oReq.status)); + return; + } + let result; + try { + result = JSON.parse(oReq.responseText); + if (!result) throw ('err'); + } + catch (e) { + if (typeof onerror == 'function') onerror(e); + return; + } + onload(result); + }); + if (typeof onerror == 'function') oReq.addEventListener("error", onerror); + oReq.open("GET", url, false); + oReq.send(); + } + + static cssstyles() { + if (ui.css.styles) { + ui.css.styles.remove(); + } + ui.css.styles = lib.init.sheet(); + ui.css.styles.sheet.insertRule('#arena .player>.name,#arena .button.character>.name {font-family: ' + (lib.config.name_font || 'xinwei') + ',xinwei}', 0); + ui.css.styles.sheet.insertRule('#arena .player>.name,.button.character>.name {font-family: ' + (lib.config.name_font || 'xinwei') + ',xinwei}', 0); + ui.css.styles.sheet.insertRule('#arena .player .identity>div {font-family: ' + (lib.config.identity_font || 'huangcao') + ',xinwei}', 0); + ui.css.styles.sheet.insertRule('.button.character.newstyle>.identity {font-family: ' + (lib.config.identity_font || 'huangcao') + ',xinwei}', 0); + if (lib.config.cardtext_font && lib.config.cardtext_font != 'default') { + ui.css.styles.sheet.insertRule('.card div:not(.info):not(.background) {font-family: ' + lib.config.cardtext_font + ';}', 0); + } + if (lib.config.global_font && lib.config.global_font != 'default') { + ui.css.styles.sheet.insertRule('#window {font-family: ' + lib.config.global_font + ',xinwei}', 0); + ui.css.styles.sheet.insertRule('#window #control{font-family: STHeiti,SimHei,Microsoft JhengHei,Microsoft YaHei,WenQuanYi Micro Hei,Suits,Helvetica,Arial,sans-serif}', 0); + } + switch (lib.config.glow_phase) { + case 'yellow': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgb(217, 152, 62) 0 0 15px, rgb(217, 152, 62) 0 0 15px !important;}', 0); break; + case 'green': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgba(10, 155, 67, 1) 0 0 15px, rgba(10, 155, 67, 1) 0 0 15px !important;}', 0); break; + case 'purple': ui.css.styles.sheet.insertRule('#arena .player:not(.selectable):not(.selected).glow_phase {box-shadow: rgba(0, 0, 0, 0.3) 0 0 0 1px, rgb(189, 62, 170) 0 0 15px, rgb(189, 62, 170) 0 0 15px !important;}', 0); break; + } + } + + static layout(layout, nosave) { + const loadingScreen = ui.create.div('.loading-screen', document.body), loadingScreenStyle = loadingScreen.style; + loadingScreenStyle.animationDuration = '1s'; + loadingScreenStyle.animationFillMode = 'forwards'; + loadingScreenStyle.animationName = 'opacity-0-1'; + if (layout == 'default') layout = 'mobile'; + if (!nosave) game.saveConfig('layout', layout); + game.layout = layout; + ui.arena.hide(); + new Promise(resolve => setTimeout(resolve, 500)).then(() => { + if (game.layout == 'default') { + ui.css.layout.href = ''; + } + else { + ui.css.layout.href = lib.assetURL + 'layout/' + game.layout + '/layout.css'; + } + if (game.layout == 'mobile' || game.layout == 'long') { + ui.arena.classList.add('mobile'); + } + else { + ui.arena.classList.remove('mobile'); + } + if (game.layout == 'mobile' || game.layout == 'long' || game.layout == 'long2' || game.layout == 'nova') { + if (game.me && game.me.node.handcards2.childNodes.length) { + while (game.me.node.handcards2.childNodes.length) { + game.me.node.handcards1.appendChild(game.me.node.handcards2.firstChild); + } + } + } + if (game.layout == 'default') { + ui.arena.classList.add('oldlayout'); + } + else { + ui.arena.classList.remove('oldlayout'); + } + if (lib.config.cardshape == 'oblong' && (game.layout == 'long' || game.layout == 'mobile' || game.layout == 'long2' || game.layout == 'nova')) { + ui.arena.classList.add('oblongcard'); + ui.window.classList.add('oblongcard'); + } + else { + ui.arena.classList.remove('oblongcard'); + ui.window.classList.remove('oblongcard'); + } + //if(lib.config.textequip=='text'&&(game.layout=='long'||game.layout=='mobile')){ + if (game.layout == 'long' || game.layout == 'mobile') { + ui.arena.classList.add('textequip'); + } + else { + ui.arena.classList.remove('textequip'); + } + if (get.is.phoneLayout()) { + ui.css.phone.href = lib.assetURL + 'layout/default/phone.css'; + ui.arena.classList.add('phone'); + } + else { + ui.css.phone.href = ''; + ui.arena.classList.remove('phone'); + } + for (var i = 0; i < game.players.length; i++) { + if (get.is.linked2(game.players[i])) { + if (game.players[i].classList.contains('linked')) { + game.players[i].classList.remove('linked'); + game.players[i].classList.add('linked2'); + } + } + else { + if (game.players[i].classList.contains('linked2')) { + game.players[i].classList.remove('linked2'); + game.players[i].classList.add('linked'); + } + } + } + if (game.layout == 'long' || game.layout == 'long2') { + ui.arena.classList.add('long'); + } + else { + ui.arena.classList.remove('long'); + } + if (lib.config.player_border != 'wide' || game.layout == 'long' || game.layout == 'long2') { + ui.arena.classList.add('slim_player'); + } + else { + ui.arena.classList.remove('slim_player'); + } + if (lib.config.player_border == 'normal' && lib.config.mode != 'brawl' && (game.layout == 'long' || game.layout == 'long2')) { + ui.arena.classList.add('lslim_player'); + } + else { + ui.arena.classList.remove('lslim_player'); + } + if (lib.config.player_border == 'slim') { + ui.arena.classList.add('uslim_player'); + } + else { + ui.arena.classList.remove('uslim_player'); + } + if (lib.config.player_border == 'narrow') { + ui.arena.classList.add('mslim_player'); + } + else { + ui.arena.classList.remove('mslim_player'); + } + ui.updatej(); + ui.updatem(); + return new Promise(resolve => setTimeout(resolve, 100)); + }).then(() => { + ui.arena.show(); + if (game.me) game.me.update(); + return new Promise(resolve => setTimeout(resolve, 500)); + }).then(() => { + ui.updatex(); + ui.updatePlayerPositions(); + return new Promise(resolve => setTimeout(resolve, 500)); + }).then(() => { + ui.updatec(); + loadingScreenStyle.animationName = 'opacity-1-0'; + loadingScreen.addEventListener('animationend', animationEvent => animationEvent.target.remove()); + }); + } + + static background() { + if (lib.config.image_background_random) { + var list = []; + for (var i in lib.configMenu.appearence.config.image_background.item) { + if (i == 'default') continue; + list.push(i); + } + list.remove(lib.config.image_background); + localStorage.setItem(lib.configprefix + 'background', JSON.stringify(list)); + } + else if (lib.config.image_background && lib.config.image_background != 'default' && !lib.config.image_background.startsWith('custom_')) { + localStorage.setItem(lib.configprefix + 'background', lib.config.image_background); + } + else if (lib.config.image_background == 'default' && lib.config.theme == 'simple') { + localStorage.setItem(lib.configprefix + 'background', 'ol_bg'); + } + else { + localStorage.removeItem(lib.configprefix + 'background'); + } + } + + /** + * + * @param {*} item + * @param {Function} [scope] 作用域 + * @returns + */ + static parsex(item, scope) { + //by 诗笺、Tipx-L + /** + * @param {Function} func + */ + function Legacy(func) { + //Remove all comments + //移除所有注释 + let str = func.toString().replace(/((?:(?:^[ \t]*)?(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/(?:[ \t]*\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/)))?|\/\/(?:[^\\]|\\(?:\r?\n)?)*?(?:\r?\n(?=[ \t]*(?:\r?\n|\/\*|\/\/))|(?=\r?\n))))+)|("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|(?:\r?\n|[\s\S])[^/"'\\\s]*)/mg, '$2').trim(); + //获取第一个 { 后的所有字符 + str = str.slice(str.indexOf('{') + 1); + //判断代码中是否有debugger + let regex = /event\.debugger\(\)/; + let hasDebugger = false; + let insertDebugger = `yield code=>eval(code);`; + let debuggerSkip = 0; + let debuggerResult; + while ((debuggerResult = str.slice(debuggerSkip).match(regex)) != null) { + let debuggerCopy = str; + debuggerCopy = debuggerCopy.slice(0, debuggerSkip + debuggerResult.index) + insertDebugger + debuggerCopy.slice(debuggerSkip + debuggerResult.index + debuggerResult[0].length, -1); + //测试是否有错误 + try { + new GeneratorFunction(debuggerCopy); + str = debuggerCopy + '}'; + debuggerSkip += debuggerResult.index + insertDebugger.length; + hasDebugger = true; + } catch (error) { + debuggerSkip += debuggerResult.index + debuggerResult[0].length; + } + } + //func中要写步骤的话,必须要写step 0 + if (str.indexOf('step 0') == -1) { + str = '{if(event.step==1) {event.finish();return;}\n' + str; + } else { + let skip = 0; + let k = 0; + let result; + //去除99个step的限制 + while ((result = str.slice(skip).match(new RegExp(`['"]step ${k}['"]`))) != null) { + let insertStr; + if (k == 0) { + insertStr = `switch(step){case 0:`; + } else { + insertStr = `break;case ${k}:`; + } + let copy = str; + copy = copy.slice(0, skip + result.index) + insertStr + copy.slice(skip + result.index + result[0].length); + //测试是否有错误 + try { + new (hasDebugger ? GeneratorFunction : Function)(copy); + str = copy; + skip += result.index + insertStr.length; + } catch (error) { + k--; + skip += result.index + result[0].length; + } + k++; + } + str = `if(event.step==${k}){event.finish();return;}` + str; + } + if (!scope) { + return (new (hasDebugger ? GeneratorFunction : Function)('event', 'step', 'source', 'player', 'target', 'targets', + 'card', 'cards', 'skill', 'forced', 'num', 'trigger', 'result', + '_status', 'lib', 'game', 'ui', 'get', 'ai', str)); + } else { + return scope(`function${hasDebugger ? '*' : ''} anonymous(event,step,source,player,target,targets, + card,cards,skill,forced,num,trigger,result, + _status,lib,game,ui,get,ai){${str}}; anonymous;`); + } + } + switch (typeof item) { + case "object": + if (Array.isArray(item)) { + let lastEvent = null; + return function* (event, step, source, player, target, targets, card, cards, skill, forced, num, trigger, result, _status, lib, game, ui, get, ai) { + if (step >= item.length) return event.finish(); + var current = item[step]; + if (typeof current != "function") throw new Error(`content ${step} of ${event.name} is not vaild: ${current}`); + var currentResult = current(event, { + event: event, + step: step, + source: source, + player: player, + target: target, + targets: targets, + card: card, + cards: cards, + skill: skill, + forced: forced, + num: num, + trigger: trigger, + result: result + }, (lastEvent && ("result" in lastEvent)) ? lastEvent.result : null); + // TODO: use `event.debugger` to replace source + if (gnc.is.generator(currentResult)) lastEvent = yield* currentResult; + else lastEvent = currentResult; + } + } + else { + if (Symbol.iterator in item) return lib.init.parsex(Array.from(item)); + if (item.toString !== Object.prototype.toString) return lib.init.parsex(item.toString()); + if ("render" in item) { + // TODO: Object Render Parse + throw new Error("NYI: Object Render Parse"); + } + // TODO: Object Other Parse + throw new Error("NYI: Object Other Parse"); + } + case "function": + if (gnc.is.generatorFunc(item)) { + // let gen, lastEvent; + return function* (event, step, source, player, target, targets, card, cards, skill, forced, num, trigger, result, _status, lib, game, ui, get, ai) { + event.step = NaN; + if (!this.gen) this.gen = item(event, { + event, + step, + source, + player, + target, + targets, + card, + cards, + skill, + forced, + num, + trigger, + result + }); + + let res + if (!this.last) res = this.gen.next() + else if (typeof this.last !== "object") res = this.gen.next(this.last) + else if (this.last instanceof GameEvent || this.last instanceof GameEventPromise) + res = this.gen.next(this.last.result) + else res = this.gen.next(this.last) + + if (res.done){ + this.gen = null; + return event.finish(); + } + let 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]; + } + this.last = currentResult; + } + }.bind({ + gen: null, + last: undefined + }) + } else if (item._parsed) return item; + // falls through + default: + return Legacy(item); + } + } + + static eval(func) { + if (typeof func == 'function') { + return eval('(' + func.toString() + ')'); + } + else if (typeof func == 'object') { + for (var i in func) { + if (Object.prototype.hasOwnProperty.call(func, i)) { + if(typeof func[i] == 'function'){ + let checkObject = {}; + checkObject[i] = func[i]; + return eval(`(function(){return ${get.stringify(checkObject)};})()`)[i]; + }else{ + func[i] = lib.init.eval(func[i]); + } + } + } + } + return func; + } + + static encode(strUni) { + var strUtf = strUni.replace( + /[\u0080-\u07ff]/g, function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); + }); + strUtf = strUtf.replace( + /[\u0800-\uffff]/g, function (c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f); + }); + return btoa(strUtf); + } + + static decode(str) { + var strUtf = atob(str); + var strUni = strUtf.replace( + /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, function (c) { + var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f); + return String.fromCharCode(cc); + }); + strUni = strUni.replace( + /[\u00c0-\u00df][\u0080-\u00bf]/g, function (c) { + var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f; + return String.fromCharCode(cc); + }); + return strUni; + } + + static stringify(obj) { + var str = '{' + for (var i in obj) { + str += '"' + i + '":' + if (Object.prototype.toString.call(obj[i]) == '[object Object]') { + str += lib.init.stringify(obj[i]); + } + else if (typeof obj[i] == 'function') { + str += obj[i].toString(); + } + else { + str += JSON.stringify(obj[i]); + } + str += ',' + } + str += '}'; + return str; + } + + static stringifySkill(obj) { + var str = ''; + for (var i in obj) { + str += i + ':' + if (Object.prototype.toString.call(obj[i]) == '[object Object]') { + str += '{\n' + lib.init.stringifySkill(obj[i]) + '}'; + } + else if (typeof obj[i] == 'function') { + str += obj[i].toString().replace(/\t/g, ''); + } + else { + str += JSON.stringify(obj[i]); + } + str += ',\n' + } + return str; + } + + /** + * 在返回当前加载的esm模块相对位置。 + * @param {*} url 传入import.meta.url + */ + static getCurrentFileLocation(url){ + let head = window.location.href.slice(0,window.location.href.lastIndexOf('/')+1); + let ret = url.replace(head,''); + return decodeURIComponent(ret); + } +}