diff --git a/noname/get/index.js b/noname/get/index.js
index 97a2943ea..6940135d4 100644
--- a/noname/get/index.js
+++ b/noname/get/index.js
@@ -2229,10 +2229,13 @@ export class Get extends Uninstantable {
}
/**
* @template T
- * @type {{
- * (key: T) => GameEvent[T];
- * () => GameEvent;
- * }}
+ * @overload
+ * @param {T} key
+ * @returns {import("../library/index.js").GameEvent[T]}
+ */
+ /**
+ * @overload
+ * @returns {import("../library/index.js").GameEvent}
*/
static event(key) { return key ? _status.event[key] : _status.event }
static player() { return _status.event.player }
diff --git a/noname/library/element/button.js b/noname/library/element/button.js
new file mode 100644
index 000000000..b497eda7f
--- /dev/null
+++ b/noname/library/element/button.js
@@ -0,0 +1,42 @@
+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';
+
+export class Button extends HTMLDivElement {
+ /**
+ * @param {{}} item
+ * @param {keyof typeof ui.create.buttonPresets | ((item: {}, type: Function, position?: HTMLDivElement | DocumentFragment, noClick?: true, button?: typeof Button) => typeof Button)} type
+ * @param {HTMLDivElement|DocumentFragment} [position]
+ * @param {true} [noClick]
+ * @param { typeof Button } [button]
+ */
+ // @ts-ignore
+ constructor(item, type, position, noClick, button) {
+ if (ui.create.buttonPresets[type]) button = ui.create.buttonPresets[type](item, type, position, noClick, button);
+ else if (typeof type == 'function') button = type(item, type, position, noClick, button);
+ Object.setPrototypeOf(button, Button.prototype);
+ // @ts-ignore
+ if (!noClick) button.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.button);
+ else {
+ // @ts-ignore
+ button.classList.add('noclick');
+ // @ts-ignore
+ const intro = button.querySelector('.intro');
+ if (intro) intro.remove();
+ }
+ // @ts-ignore
+ return button;
+ }
+ exclude() {
+ if (_status.event.excludeButton == undefined) {
+ _status.event.excludeButton = [];
+ }
+ _status.event.excludeButton.add(this);
+ }
+ get updateTransform() {
+ return lib.element.Card.prototype.updateTransform;
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/card.js b/noname/library/element/card.js
new file mode 100644
index 000000000..f75be62db
--- /dev/null
+++ b/noname/library/element/card.js
@@ -0,0 +1,787 @@
+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';
+
+export class Card extends HTMLDivElement {
+ /**
+ * @param {HTMLDivElement|DocumentFragment} [position]
+ * @param {'noclick'} [info]
+ * @param {true} [noclick]
+ */
+ // @ts-ignore
+ constructor(position, info, noclick) {
+ /**
+ * @type {this}
+ */
+ // @ts-ignore
+ const card = ui.create.div('.card', position);
+ Object.setPrototypeOf(card, Card.prototype);
+ card.build(info, noclick);
+ return card;
+ }
+ build(info, noclick) {
+ let card = this;
+ card.buildNode();
+ card.buildIntro(noclick);
+ card.buildProperty();
+ card.buildEventListener(info);
+ }
+ buildEventListener(info) {
+ let card = this;
+ if (info != 'noclick') {
+ card.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.card);
+ if (lib.config.touchscreen) {
+ card.addEventListener('touchstart', ui.click.cardtouchstart);
+ card.addEventListener('touchmove', ui.click.cardtouchmove);
+ }
+ if (lib.cardSelectObserver) lib.cardSelectObserver.observe(card, {
+ attributes: true
+ });
+ }
+ }
+ buildProperty() {
+ let card = this;
+ card.storage = {};
+ card.vanishtag = [];
+ card.gaintag = [];
+ card._uncheck = [];
+ }
+ buildNode() {
+ this.node = {
+ image: ui.create.div('.image', this),
+ info: ui.create.div('.info', this),
+ name: ui.create.div('.name', this),
+ name2: ui.create.div('.name2', this),
+ background: ui.create.div('.background', this),
+ intro: ui.create.div('.intro', this),
+ range: ui.create.div('.range', this),
+ gaintag: ui.create.div('.gaintag', this),
+ };
+ this.node.intro.innerHTML = lib.config.intro;
+ }
+ buildIntro(noclick) {
+ if (!noclick) lib.setIntro(this);
+ }
+ //执行销毁一张牌的钩子函数
+ selfDestroy(event) {
+ if (this._selfDestroyed) return;
+ this._selfDestroyed = true;
+ this.fix();
+ this.delete();
+ const info = get.info(this, false);
+ if (!info) return;
+ if (info.destroyLog !== false) game.log(this, '被销毁了');
+ if (info.onDestroy) info.onDestroy(this, event);
+ }
+ //判断一张牌进入某个区域后是否会被销毁
+ willBeDestroyed(targetPosition, player, event) {
+ const destroyed = this.destroyed;
+ if (typeof destroyed == 'function') {
+ return destroyed(this, targetPosition, player, event);
+ }
+ else if (lib.skill[destroyed]) {
+ if (player) {
+ if (player.hasSkill(destroyed)) {
+ delete this.destroyed;
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (typeof destroyed == 'string') {
+ return (destroyed == targetPosition);
+ }
+ return destroyed;
+ }
+ hasNature(nature, player) {
+ return game.hasNature(this, nature, player);
+ }
+ //只针对【杀】起效果
+ addNature(nature) {
+ let natures = [];
+ if (!this.nature) this.nature = '';
+ else {
+ natures.addArray(get.natureList(this.nature));
+ }
+ natures.addArray(get.natureList(nature));
+ this.nature = get.nature(natures);
+ this.classList.add(nature);
+ let str = get.translation(this.nature) + '杀';
+ this.node.name.innerText = str;
+ let name = get.name(this, false);
+ do {
+ if (name == 'sha') {
+ let _bg;
+ for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
+ if (_bg) {
+ this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
+ break;
+ }
+ }
+ this.node.image.setBackgroundImage('image/card/' + name + '.png');
+ }
+ while (0);
+ return this.nature;
+ }
+ removeNature(nature) {
+ if (!this.nature) return;
+ let natures = get.natureList(this.nature);
+ natures.remove(nature);
+ if (!natures.length) delete this.nature;
+ else this.nature = get.nature(natures);
+ this.classList.remove(nature);
+ let str = get.translation(this.nature) + '杀';
+ this.node.name.innerText = str;
+ let name = get.name(this, false);
+ do {
+ if (name == 'sha') {
+ let _bg;
+ for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
+ if (_bg) {
+ this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
+ break;
+ }
+ }
+ this.node.image.setBackgroundImage('image/card/' + name + '.png');
+ }
+ while (0);
+ return this.nature;
+ }
+ addGaintag(gaintag) {
+ if (Array.isArray(gaintag)) this.gaintag = gaintag.slice(0);
+ else this.gaintag.add(gaintag);
+ var str = '';
+ for (var gi = 0; gi < this.gaintag.length; gi++) {
+ var translate = get.translation(this.gaintag[gi]);
+ if (translate != 'invisible') {
+ str += translate;
+ if (gi < this.gaintag.length - 1) str += ' ';
+ }
+ }
+ this.node.gaintag.innerHTML = str;
+ }
+ removeGaintag(tag) {
+ if (tag === true) {
+ if (this.gaintag && this.gaintag.length || this.node.gaintag.innerHTML.length) this.addGaintag([]);
+ }
+ else if (this.hasGaintag(tag)) {
+ this.gaintag.remove(tag);
+ this.addGaintag(this.gaintag);
+ }
+ }
+ hasGaintag(tag) {
+ return this.gaintag && this.gaintag.contains(tag);
+ }
+ /**
+ * @param {[string, number, string, string] | {
+ * suit: string;
+ * number: number;
+ * name: string;
+ * nature: string;
+ * }} card
+ */
+ init(card) {
+ if (Array.isArray(card)) {
+ if (card[2] == 'huosha') {
+ card[2] = 'sha';
+ card[3] = 'fire';
+ }
+ else if (card[2] == 'leisha') {
+ card[2] = 'sha';
+ card[3] = 'thunder';
+ }
+ else if (card[2] == 'cisha') {
+ card[2] = 'sha';
+ card[3] = 'stab';
+ }
+ else if (card[2].length > 3) {
+ let prefix = card[2].slice(0, card[2].lastIndexOf('sha'));
+ if (lib.nature.has(prefix)) {
+ if (prefix.length + 3 == card[2].length) {
+ card[2] = 'sha';
+ card[3] = prefix;
+ }
+ }
+ if (card[2].startsWith('sha_')) {
+ let suffix = card[2].slice(4);
+ let natureList = suffix.split('_');
+ card[2] = 'sha';
+ card[3] = get.nature(natureList);
+ }
+ }
+ }
+ else if (typeof card == 'object') {
+ card = [card.suit, card.number, card.name, card.nature];
+ }
+ var cardnum = card[1] || '';
+ if (parseInt(cardnum) == cardnum) cardnum = parseInt(cardnum);
+
+ if (!lib.card[card[2]]) {
+ lib.card[card[2]] = {};
+ }
+ var info = lib.card[card[2]];
+ if (info.global && !this.classList.contains('button')) {
+ if (Array.isArray(info.global)) {
+ while (info.global.length) {
+ game.addGlobalSkill(info.global.shift());
+ }
+ }
+ else if (typeof info.global == 'string') {
+ game.addGlobalSkill(info.global);
+ }
+ delete info.global;
+ }
+ this.suit = card[0];
+ this.number = parseInt(card[1]) || 0;
+ this.name = card[2];
+
+ if (info.destroy && (typeof info.destroy != 'boolean' && !lib.skill[info.destroy])) {
+ this.destroyed = info.destroy;
+ }
+
+ if (_status.connectMode && !game.online && lib.cardOL && !this.cardid) {
+ this.cardid = get.id();
+ lib.cardOL[this.cardid] = this;
+ }
+ if (!_status.connectMode && !_status.video) {
+ this.cardid = get.id();
+ }
+
+ this.$init(card);
+
+ if (this.inits) {
+ for (var i = 0; i < this.inits.length; i++) {
+ this.inits[i](this);
+ }
+ }
+ if (typeof info.init == 'function') info.init();
+
+ return this;
+ }
+ /**
+ * @param {[string, number, string, string]} card
+ */
+ $init(card) {
+ var info = lib.card[card[2]];
+ var cardnum = card[1] || '';
+ if (parseInt(cardnum) == cardnum) cardnum = parseInt(cardnum);
+ if (cardnum > 0 && cardnum < 14) {
+ cardnum = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'][cardnum - 1];
+ }
+ if (this.name) {
+ this.classList.remove('epic');
+ this.classList.remove('legend');
+ this.classList.remove('gold');
+ this.classList.remove('unique');
+ this.style.background = '';
+ var subtype = get.subtype(this, false);
+ if (subtype) {
+ this.classList.remove(subtype);
+ }
+ }
+ if (info.epic) {
+ this.classList.add('epic');
+ }
+ else if (info.legend) {
+ this.classList.add('legend');
+ }
+ else if (info.gold) {
+ this.classList.add('gold');
+ }
+ else if (info.unique) {
+ this.classList.add('unique');
+ }
+ var bg = card[2];
+ if (info.cardimage) {
+ bg = info.cardimage;
+ }
+ var img = lib.card[bg].image;
+ if (img) {
+ if (img.startsWith('db:')) {
+ img = img.slice(3);
+ }
+ else if (!img.startsWith('ext:')) {
+ img = null;
+ }
+ }
+ this.classList.remove('fullskin');
+ this.classList.remove('fullimage');
+ this.classList.remove('fullborder');
+ this.dataset.cardName = card[2];
+ this.dataset.cardType = info.type || '';
+ this.dataset.cardSubype = info.subtype || '';
+ this.dataset.cardMultitarget = info.multitarget ? '1' : '0';
+ this.node.name.dataset.nature = '';
+ this.node.info.classList.remove('red');
+ if (!lib.config.hide_card_image && lib.card[bg].fullskin) {
+ this.classList.add('fullskin');
+ if (img) {
+ if (img.startsWith('ext:')) {
+ this.node.image.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
+ }
+ else {
+ this.node.image.setBackgroundDB(img);
+ }
+ }
+ else {
+ if (lib.card[bg].modeimage) {
+ this.node.image.setBackgroundImage('image/mode/' + lib.card[bg].modeimage + '/card/' + bg + '.png');
+ }
+ else {
+ do {
+ let nature = card[3];
+ if (bg == 'sha' && typeof nature == 'string') {
+ let natures = get.natureList(nature), _bg;
+ for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
+ if (_bg) {
+ this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
+ break;
+ }
+ }
+ this.node.image.setBackgroundImage('image/card/' + bg + '.png');
+ }
+ while (0);
+ }
+ }
+ }
+ else if (lib.card[bg].image == 'background') {
+ if (card[3]) this.node.background.setBackground(bg + '_' + get.natureList(card[3])[0], 'card');
+ else this.node.background.setBackground(bg, 'card');
+ }
+ else if (lib.card[bg].fullimage) {
+ this.classList.add('fullimage');
+ if (img) {
+ if (img.startsWith('ext:')) {
+ this.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
+ this.style.backgroundSize = 'cover';
+ }
+ else {
+ this.setBackgroundDB(img);
+ }
+ }
+ else if (lib.card[bg].image) {
+ if (lib.card[bg].image.startsWith('character:')) {
+ this.setBackground(lib.card[bg].image.slice(10), 'character');
+ }
+ else {
+ this.setBackground(lib.card[bg].image);
+ }
+ }
+ else {
+ var cardPack = lib.cardPack['mode_' + get.mode()];
+ if (Array.isArray(cardPack) && cardPack.contains(bg)) {
+ this.setBackground('mode/' + get.mode() + '/card/' + bg);
+ }
+ else {
+ this.setBackground('card/' + bg);
+ }
+ }
+ }
+ else if (lib.card[bg].fullborder) {
+ this.classList.add('fullborder');
+ if (lib.card[bg].fullborder == 'gold') {
+ this.node.name.dataset.nature = 'metalmm';
+ }
+ else if (lib.card[bg].fullborder == 'silver') {
+ this.node.name.dataset.nature = 'watermm';
+ }
+ if (!this.node.avatar) {
+ this.node.avatar = ui.create.div('.cardavatar');
+ this.insertBefore(this.node.avatar, this.firstChild);
+ }
+ if (!this.node.framebg) {
+ this.node.framebg = ui.create.div('.cardframebg');
+ this.node.framebg.dataset.auto = lib.card[bg].fullborder;
+ this.insertBefore(this.node.framebg, this.firstChild);
+ }
+ if (img) {
+ if (img.startsWith('ext:')) {
+ this.node.avatar.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
+ this.node.avatar.style.backgroundSize = 'cover';
+ }
+ else {
+ this.node.avatar.setBackgroundDB(img);
+ }
+ }
+ else if (lib.card[bg].image) {
+ if (lib.card[bg].image.startsWith('character:')) {
+ this.node.avatar.setBackground(lib.card[bg].image.slice(10), 'character');
+ }
+ else {
+ this.node.avatar.setBackground(lib.card[bg].image);
+ }
+ }
+ else {
+ var cardPack = lib.cardPack['mode_' + get.mode()];
+ if (Array.isArray(cardPack) && cardPack.contains(bg)) {
+ this.node.avatar.setBackground('mode/' + get.mode() + '/card/' + bg);
+ }
+ else {
+ this.node.avatar.setBackground('card/' + bg);
+ }
+ }
+ }
+ else if (lib.card[bg].image == 'card') {
+ if (card[3]) this.setBackground(bg + '_' + get.natureList(card[3])[0], 'card');
+ else this.setBackground(bg, 'card');
+ }
+ else if (typeof lib.card[bg].image == 'string' && !lib.card[bg].fullskin) {
+ if (img) {
+ if (img.startsWith('ext:')) {
+ this.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
+ this.style.backgroundSize = 'cover';
+ }
+ else {
+ this.setBackgroundDB(img);
+ }
+ }
+ else {
+ this.setBackground(lib.card[bg].image);
+ }
+ }
+ else {
+ this.node.background.innerHTML = lib.translate[bg + '_cbg'] || lib.translate[bg + '_bg'] || get.translation(bg)[0];
+ // this.node.background.style.fontFamily=lib.config.card_font;
+ if (this.node.background.innerHTML.length > 1) this.node.background.classList.add('tight');
+ else this.node.background.classList.remove('tight');
+ }
+ if (!lib.card[bg].fullborder && this.node.avatar && this.node.framebg) {
+ this.node.avatar.remove();
+ this.node.framebg.remove();
+ delete this.node.avatar;
+ delete this.node.framebg;
+ }
+ if (info.noname && !this.classList.contains('button')) {
+ this.node.name.style.display = 'none';
+ }
+ if (info.color) {
+ this.style.color = info.color;
+ }
+ if (info.textShadow) {
+ this.style.textShadow = info.textShadow;
+ }
+ if (info.opacity) {
+ this.node.info.style.opacity = info.opacity;
+ this.node.name.style.opacity = info.opacity;
+ }
+ if (info.modinfo) {
+ this.node.info.innerHTML = info.modinfo;
+ }
+ else {
+ this.node.info.innerHTML = get.translation(card[0]) + ' ' + cardnum + '';
+ }
+ if (info.addinfo) {
+ if (!this.node.addinfo) {
+ this.node.addinfo = ui.create.div('.range', this);
+ }
+ this.node.addinfo.innerHTML = info.addinfo;
+ }
+ else if (this.node.addinfo) {
+ this.node.addinfo.remove();
+ delete this.node.addinfo;
+ }
+ if (card[0] == 'heart' || card[0] == 'diamond') {
+ this.node.info.classList.add('red');
+ }
+ this.node.image.className = 'image';
+ var name = get.translation(card[2]);
+ if (card[2] == 'sha') {
+ name = '';
+ let nature = card[3];
+ if (nature) {
+ let natures = get.natureList(nature);
+ natures.sort(lib.sort.nature);
+ for (let nature of natures) {
+ name += lib.translate['nature_' + nature] || lib.translate[nature] || '';
+ if (nature != 'stab') this.node.image.classList.add(nature);
+ }
+ }
+ name += '杀';
+ }
+ this.node.name.innerHTML = name;
+ if (name.length >= 5) {
+ this.node.name.classList.add('long');
+ if (name.length >= 7) {
+ this.node.name.classList.add('longlong');
+ }
+ }
+ this.node.name2.innerHTML = get.translation(card[0]) + cardnum + ' ' + name;
+ this.classList.add('card');
+ if (card[3]) {
+ let natures = get.natureList(card[3]);
+ natures.forEach(n => { if (n) this.classList.add(n); });
+ this.nature = natures.filter(n => lib.nature.has(n)).sort(lib.sort.nature).join(lib.natureSeparator);
+ }
+ else if (this.nature) {
+ this.classList.remove(this.nature);
+ delete this.nature;
+ }
+ if (info.subtype) this.classList.add(info.subtype);
+ this.node.range.innerHTML = '';
+ switch (get.subtype(this, false)) {
+ case 'equip1':
+ var added = false;
+ if (lib.card[this.name] && lib.card[this.name].distance) {
+ var dist = lib.card[this.name].distance;
+ if (dist.attackFrom) {
+ added = true;
+ this.node.range.innerHTML = '范围: ' + (-dist.attackFrom + 1);
+ }
+ }
+ if (!added) {
+ this.node.range.innerHTML = '范围: 1';
+ }
+ break;
+ case 'equip3':
+ if (info.distance && info.distance.globalTo) {
+ this.node.range.innerHTML = '防御: ' + info.distance.globalTo;
+ this.node.name2.innerHTML += '+';
+ }
+ break;
+ case 'equip4':
+ if (info.distance && info.distance.globalFrom) {
+ this.node.range.innerHTML = '进攻: ' + (-info.distance.globalFrom);
+ this.node.name2.innerHTML += '-';
+ }
+ break;
+ }
+ var tags = [];
+ if (Array.isArray(card[4])) {
+ tags.addArray(card[4]);
+ }
+ if (this.cardid) {
+ if (!_status.cardtag) {
+ _status.cardtag = {};
+ }
+ for (var i in _status.cardtag) {
+ if (_status.cardtag[i].contains(this.cardid)) {
+ tags.add(i);
+ }
+ }
+ if (tags.length) {
+ var tagstr = ' ';
+ for (var i = 0; i < tags.length; i++) {
+ var tag = tags[i];
+ if (!_status.cardtag[tag]) {
+ _status.cardtag[tag] = [];
+ }
+ _status.cardtag[tag].add(this.cardid);
+ tagstr += lib.translate[tag + '_tag'];
+ //if(i';
+ this.node.range.innerHTML += tagstr;
+ }
+ }
+ return this;
+ }
+ updateTransform(bool, delay) {
+ if (delay) {
+ var that = this;
+ setTimeout(function () {
+ that.updateTransform(that.classList.contains('selected'));
+ }, delay);
+ }
+ else {
+ if (_status.event.player != game.me) return;
+ if (this._transform && this.parentNode && this.parentNode.parentNode &&
+ this.parentNode.parentNode.parentNode == ui.me &&
+ (!_status.mousedown || _status.mouseleft) &&
+ (!this.parentNode.parentNode.classList.contains('scrollh') || (game.layout == 'long2' || game.layout == 'nova'))) {
+ if (bool) {
+ this.style.transform = this._transform + ' translateY(-20px)';
+ }
+ else {
+ this.style.transform = this._transform || '';
+ }
+ }
+ }
+ }
+ aiexclude() {
+ _status.event._aiexclude.add(this);
+ }
+ //为此牌添加知情者。参数可为数组,若参数为字符串'everyone',则所有玩家均为知情者。
+ addKnower(player) {
+ if (!this._knowers) {
+ this._knowers = [];
+ }
+ if (typeof player == 'string') {
+ this._knowers.add(player);
+ } else {
+ let type = get.itemtype(player);
+ if (type == 'player') {
+ this._knowers.add(player.playerid);
+ } else if (type == 'players') {
+ player.forEach(p => this._knowers.add(p.playerid));
+ }
+ }
+ }
+ removeKnower(player) {
+ if (!this._knowers) {
+ return;
+ }
+ if (typeof player == 'string') {
+ this._knowers.remove(player);
+ } else {
+ let type = get.itemtype(player);
+ if (type == 'player') {
+ this._knowers.remove(player.playerid);
+ } else if (type == 'players') {
+ player.forEach(p => this._knowers.remove(p.playerid));
+ }
+ }
+ }
+ //清除此牌的知情者。
+ clearKnowers() {
+ if (this._knowers) delete this._knowers;
+ }
+ //判断玩家对此牌是否知情。
+ isKnownBy(player) {
+ if (['e', 'j'].includes(get.position(this))) return true;//装备区或者判定区的牌,必知情。
+ let owner = get.owner(this);
+ if (owner) {
+ if (owner == player) return true;//是牌主,必知情。
+ if (player.hasSkillTag('viewHandcard', null, owner, true)) return true;//有viewHandcard标签,必知情。
+ if (owner.isUnderControl(true, player)) return true;//被操控,必知情。
+ }
+ if (get.is.shownCard(this)) return true;//此牌是明置牌,必知情。
+ if (this._knowers) {
+ return this._knowers.includes('everyone') || this._knowers.includes(player.playerid);
+ }
+ return false;
+ }
+ getSource(name) {
+ if (this.name == name) return true;
+ var info = lib.card[this.name];
+ if (info && Array.isArray(info.source)) {
+ return info.source.contains(name);
+ }
+ return false;
+ }
+ moveDelete(player) {
+ this.fixed = true;
+ if (!this._listeningEnd || this._transitionEnded) {
+ this.moveTo(player);
+ var that = this;
+ setTimeout(function () {
+ that.delete();
+ }, 200);
+ }
+ else {
+ this._onEndMoveDelete = player;
+ }
+ }
+ moveTo(player) {
+ this.fixed = true;
+ var dx, dy;
+ if (this.classList.contains('center')) {
+ var nx = [50, -52];
+ var ny = [50, -52];
+ nx = nx[0] * ui.arena.offsetWidth / 100 + nx[1];
+ ny = ny[0] * ui.arena.offsetHeight / 100 + ny[1];
+ dx = player.getLeft() + player.offsetWidth / 2 - 52 - nx;
+ dy = player.getTop() + player.offsetHeight / 2 - 52 - ny;
+ }
+ else {
+ this.style.left = this.offsetLeft + 'px';
+ this.style.top = this.offsetTop + 'px';
+
+ dx = player.getLeft() + player.offsetWidth / 2 - 52 - this.offsetLeft;
+ dy = player.getTop() + player.offsetHeight / 2 - 52 - this.offsetTop;
+ }
+ if (get.is.mobileMe(player)) {
+ dx += get.cardOffset();
+ if (ui.arena.classList.contains('oblongcard')) {
+ dy -= 16;
+ }
+ }
+
+
+ if (this.style.transform && this.style.transform != 'none' && this.style.transform.indexOf('translate') == -1) {
+ this.style.transform += ' translate(' + dx + 'px,' + dy + 'px)';
+ }
+ else {
+ this.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
+ }
+ return this;
+ }
+ copy() {
+ /**
+ * @type {Card}
+ */
+ var node = this.cloneNode(true);
+ node.style.transform = '';
+ node.name = this.name;
+ node.suit = this.suit;
+ node.number = this.number;
+ node.nature = this.nature;
+ node.classList.remove('hidden');
+ node.classList.remove('start');
+ node.classList.remove('thrown');
+ node.classList.remove('selectable');
+ node.classList.remove('selected');
+ node.classList.remove('removing');
+ node.classList.remove('drawinghidden');
+ node.classList.remove('glows');
+ node.node = {
+ name: node.querySelector('.name'),
+ info: node.querySelector('.info'),
+ intro: node.querySelector('.intro'),
+ background: node.querySelector('.background'),
+ image: node.querySelector('.image'),
+ gaintag: node.querySelector('.gaintag'),
+ };
+ node.node.gaintag.innerHTML = '';
+ var clone = true;
+ var position;
+ for (var i = 0; i < arguments.length; i++) {
+ if (typeof arguments[i] == 'string') node.classList.add(arguments[i]);
+ else if (['div', 'fragment'].includes(get.objtype(arguments[i]))) position = arguments[i];
+ else if (typeof arguments[i] == 'boolean') clone = arguments[i];
+ }
+ node.moveTo = lib.element.Card.prototype.moveTo;
+ node.moveDelete = lib.element.Card.prototype.moveDelete;
+ if (clone) this.clone = node;
+ if (position) position.appendChild(node);
+ return node;
+ }
+ uncheck(skill) {
+ if (skill) this._uncheck.add(skill);
+ this.classList.add('uncheck');
+ }
+ recheck(skill) {
+ if (skill) this._uncheck.remove(skill);
+ else this._uncheck.length = 0;
+ if (this._uncheck.length == 0) this.classList.remove('uncheck');
+ }
+ discard(bool) {
+ if (!this._selfDestroyed) {
+ this.fix();
+ ui.discardPile.appendChild(this);
+ }
+ this.classList.remove('glow');
+ if (bool === false) {
+ ui.cardPile.insertBefore(this, ui.cardPile.childNodes[Math.floor(Math.random() * ui.cardPile.childNodes.length)]);
+ }
+ else {
+ if (_status.discarded) {
+ _status.discarded.add(this);
+ }
+ }
+ }
+ hasTag(tag) {
+ if (this.cardid && _status.cardtag && _status.cardtag[tag] && _status.cardtag[tag].contains(this.cardid)) {
+ return true;
+ }
+ return false;
+ }
+ hasPosition() {
+ return ['h', 'e', 'j', 's', 'x'].contains(get.position(this));
+ }
+ isInPile() {
+ return ['c', 'd'].contains(get.position(this));
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/client.js b/noname/library/element/client.js
new file mode 100644
index 000000000..6aa0649d4
--- /dev/null
+++ b/noname/library/element/client.js
@@ -0,0 +1,75 @@
+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';
+
+export class Client {
+ /**
+ * @param {import('../index.js').NodeWS | InstanceType} ws
+ */
+ constructor(ws) {
+ this.ws = ws;
+ /**
+ * @type { string }
+ */
+ // @ts-ignore
+ this.id = ws.wsid || get.id();
+ this.closed = false;
+ }
+ send() {
+ if (this.closed) return this;
+ var args = Array.from(arguments);
+ if (typeof args[0] == 'function') {
+ args.unshift('exec');
+ }
+ for (var i = 1; i < args.length; i++) {
+ args[i] = get.stringifiedResult(args[i]);
+ }
+ try {
+ this.ws.send(JSON.stringify(args));
+ }
+ catch (e) {
+ this.ws.close();
+ }
+ return this;
+ }
+ close() {
+ lib.node.clients.remove(this);
+ lib.node.observing.remove(this);
+ if (ui.removeObserve && !lib.node.observing.length) {
+ ui.removeObserve.remove();
+ delete ui.removeObserve;
+ }
+ this.closed = true;
+ if (_status.waitingForPlayer) {
+ for (var i = 0; i < game.connectPlayers.length; i++) {
+ if (game.connectPlayers[i].playerid == this.id) {
+ game.connectPlayers[i].uninitOL();
+ delete game.connectPlayers[i].playerid;
+ }
+ }
+ if (game.onlinezhu == this.id) {
+ game.onlinezhu = null;
+ }
+ game.updateWaiting();
+ }
+ else if (lib.playerOL[this.id]) {
+ var player = lib.playerOL[this.id];
+ player.setNickname(player.nickname + ' - 离线');
+ // @ts-ignore
+ game.broadcast(function (player) {
+ player.setNickname(player.nickname + ' - 离线');
+ }, player);
+ player.unwait('ai');
+ }
+
+ if (window.isNonameServer) {
+ // @ts-ignore
+ document.querySelector('#server_count').innerHTML = lib.node.clients.length;
+ }
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/content.js b/noname/library/element/content.js
index d0b258254..5de904b7e 100644
--- a/noname/library/element/content.js
+++ b/noname/library/element/content.js
@@ -7,7 +7,7 @@ import { UI as ui } from '../../ui/index.js';
import { GNC as gnc } from '../../gnc/index.js';
// 未来再改
-export default {
+export const Content = {
emptyEvent: async (event) => {
event.trigger(event.name);
},
diff --git a/noname/library/element/contents.js b/noname/library/element/contents.js
index 2a90d1f04..ec9d5f332 100644
--- a/noname/library/element/contents.js
+++ b/noname/library/element/contents.js
@@ -6,7 +6,7 @@ import { status as _status } from '../../status/index.js';
import { UI as ui } from '../../ui/index.js';
import { GNC as gnc } from '../../gnc/index.js';
-export default {
+export const Contents = {
phase: [
async (event, _trigger, player) => {
// 初始化阶段列表
diff --git a/noname/library/element/control.js b/noname/library/element/control.js
new file mode 100644
index 000000000..b3d4252dd
--- /dev/null
+++ b/noname/library/element/control.js
@@ -0,0 +1,132 @@
+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';
+
+export class Control extends HTMLDivElement {
+ // @ts-ignore
+ constructor() {
+ const nc = !ui.control.querySelector('div:not(.removing):not(.stayleft)');
+ const controls = Array.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
+ /**
+ * @type {this}
+ */
+ // @ts-ignore
+ const control = ui.create.div('.control');
+ Object.setPrototypeOf(control, Control.prototype);
+ ui.control.insertBefore(control, _status.createControl || ui.confirm);
+ controls.forEach(argument => {
+ if (argument == 'nozoom') return;
+ if (typeof argument == 'function') control.custom = argument;
+ else if (argument == 'stayleft') {
+ control.stayleft = true;
+ control.classList.add('stayleft');
+ }
+ else control.add(argument);
+ });
+ ui.controls.unshift(control);
+ if (nc) ui.control.animate('nozoom', 100);
+ if (control.childNodes.length) {
+ control.style.transition = 'opacity 0.5s';
+ control.animate('controlpressdownx', 500);
+ ui.refresh(control);
+ if (!control.stayleft) control.style.transform = `translateX(-${control.offsetWidth / 2}px)`;
+ control.style.opacity = 1;
+ ui.refresh(control);
+ control.style.transition = '';
+ }
+
+ control.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.control2);
+
+ if (lib.config.button_press) {
+ control.addEventListener(lib.config.touchscreen ? 'touchstart' : 'mousedown', function () {
+ if (this.classList.contains('disabled')) return;
+ this.classList.add('controlpressdown');
+ if (typeof this._offset == 'number') this.style.transform = `translateX(${this._offset}px) scale(0.97)`;
+ });
+ control.addEventListener(lib.config.touchscreen ? 'touchend' : 'mouseup', function () {
+ this.classList.remove('controlpressdown');
+ if (typeof this._offset == 'number') this.style.transform = `translateX(${this._offset}px)`;
+ });
+ }
+
+ ui.updatec();
+ return control;
+ }
+ open() {
+ ui.control.insertBefore(this, _status.createControl || ui.confirm);
+ ui.controls.unshift(this);
+ if (this.childNodes.length) {
+ this.style.transition = 'opacity 0.5s';
+ ui.refresh(this);
+ this.style.transform = 'translateX(-' + (this.offsetWidth / 2) + 'px)';
+ this.style.opacity = 1;
+ ui.refresh(this);
+ this.style.transition = '';
+ }
+ else {
+ this.animate('controlpressdownx', 500);
+ }
+ ui.updatec();
+ return this;
+ }
+ add(item) {
+ var node = document.createElement('div');
+ this.appendChild(node);
+ node.link = item;
+ node.innerHTML = get.translation(item);
+ node.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.control);
+ }
+ close() {
+ this.animate('controlpressdownx', 500);
+
+ ui.controls.remove(this);
+ this.delete();
+
+ setTimeout(ui.updatec, 100);
+
+
+ if (ui.confirm == this) delete ui.confirm;
+ if (ui.skills == this) delete ui.skills;
+ if (ui.skills2 == this) delete ui.skills2;
+ if (ui.skills3 == this) delete ui.skills3;
+ }
+ replace() {
+ // this.animate('controlpressdownx',500);
+ if (this.replaceTransition === false) {
+ this.style.transitionProperty = 'none';
+ ui.refresh(this);
+ }
+
+ while (this.childNodes.length) this.firstChild.remove();
+ var i, controls;
+ if (Array.isArray(arguments[0])) controls = arguments[0];
+ else controls = arguments;
+ delete this.custom;
+ for (i = 0; i < controls.length; i++) {
+ if (typeof controls[i] == 'function') {
+ this.custom = controls[i];
+ }
+ else {
+ this.add(controls[i]);
+ }
+ }
+ if (this.childNodes.length) {
+ var width = 0;
+ for (i = 0; i < this.childNodes.length; i++) width += this.childNodes[i].offsetWidth;
+ ui.refresh(this);
+ this.style.width = width + 'px';
+ }
+ ui.updatec();
+ if (this.replaceTransition === false) {
+ var that = this;
+ setTimeout(function () {
+ that.style.transitionProperty = '';
+ }, 200);
+ }
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/dialog.js b/noname/library/element/dialog.js
new file mode 100644
index 000000000..647ec8fb9
--- /dev/null
+++ b/noname/library/element/dialog.js
@@ -0,0 +1,178 @@
+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';
+
+export class Dialog extends HTMLDivElement {
+ // @ts-ignore
+ constructor() {
+ let hidden = false;
+ let noTouchScroll = false;
+ let forceButton = false;
+ let noForceButton = false;
+ /** @type {this} */
+ // @ts-ignore
+ const dialog = ui.create.div('.dialog');
+ Object.setPrototypeOf(dialog, Dialog.prototype);
+ dialog.contentContainer = ui.create.div('.content-container', dialog);
+ dialog.content = ui.create.div('.content', dialog.contentContainer);
+ dialog.bar1 = ui.create.div('.bar.top', dialog);
+ dialog.bar2 = ui.create.div('.bar.bottom', dialog);
+ dialog.buttons = [];
+ Array.from(arguments).forEach(argument => {
+ if (typeof argument == 'boolean') dialog.static = argument;
+ else if (argument == 'hidden') hidden = true;
+ else if (argument == 'notouchscroll') noTouchScroll = true;
+ else if (argument == 'forcebutton') forceButton = true;
+ else if (argument == 'noforcebutton') noForceButton = true;
+ else dialog.add(argument);
+ });
+ if (!hidden) dialog.open();
+ if (!lib.config.touchscreen) dialog.contentContainer.onscroll = ui.update;
+ if (!noTouchScroll) {
+ dialog.contentContainer.ontouchstart = ui.click.dialogtouchStart;
+ dialog.contentContainer.ontouchmove = ui.click.touchScroll;
+ dialog.contentContainer.style.webkitOverflowScrolling = 'touch';
+ dialog.ontouchstart = ui.click.dragtouchdialog;
+ }
+ if (noForceButton) dialog.noforcebutton = true;
+ else if (forceButton) {
+ dialog.forcebutton = true;
+ dialog.classList.add('forcebutton');
+ }
+ return dialog;
+ }
+ add(item, noclick, zoom) {
+ if (typeof item == 'string') {
+ if (item.startsWith('###')) {
+ var items = item.slice(3).split('###');
+ this.add(items[0], noclick, zoom);
+ this.addText(items[1], items[1].length <= 20, zoom);
+ }
+ else if (noclick) {
+ var strstr = item;
+ item = ui.create.div('', this.content);
+ item.innerHTML = strstr;
+ }
+ else {
+ item = ui.create.caption(item, this.content);
+ }
+ }
+ else if (['div', 'fragment'].includes(get.objtype(item))) {
+ this.content.appendChild(item);
+ }
+ else if (get.itemtype(item) == 'cards') {
+ var buttons = ui.create.div('.buttons', this.content);
+ if (zoom) buttons.classList.add('smallzoom');
+ this.buttons = this.buttons.concat(ui.create.buttons(item, 'card', buttons, noclick));
+ }
+ else if (get.itemtype(item) == 'players') {
+ var buttons = ui.create.div('.buttons', this.content);
+ if (zoom) buttons.classList.add('smallzoom');
+ this.buttons = this.buttons.concat(ui.create.buttons(item, 'player', buttons, noclick));
+ }
+ else if (item[1] == 'textbutton') {
+ ui.create.textbuttons(item[0], this, noclick);
+ }
+ else {
+ var buttons = ui.create.div('.buttons', this.content);
+ if (zoom) buttons.classList.add('smallzoom');
+ this.buttons = this.buttons.concat(ui.create.buttons(item[0], item[1], buttons, noclick));
+ }
+ if (this.buttons.length) {
+ if (this.forcebutton !== false) this.forcebutton = true;
+ if (this.buttons.length > 3 || (zoom && this.buttons.length > 5)) {
+ this.classList.remove('forcebutton-auto');
+ }
+ else if (!this.noforcebutton) {
+ this.classList.add('forcebutton-auto');
+ }
+ }
+ ui.update();
+ return item;
+ }
+ addText(str, center) {
+ if (str && str.startsWith('' + str + '
');
+ }
+ else {
+ this.add('' + str + '
');
+ }
+ return this;
+ }
+ addSmall(item, noclick) {
+ return this.add(item, noclick, true);
+ }
+ addAuto(content) {
+ if (content && content.length > 4 && !this._hovercustomed) {
+ this.addSmall(content);
+ }
+ else {
+ this.add(content);
+ }
+ }
+ open() {
+ if (this.noopen) return;
+ for (var i = 0; i < ui.dialogs.length; i++) {
+ if (ui.dialogs[i] == this) {
+ this.show();
+ this.refocus();
+ ui.dialogs.remove(this);
+ ui.dialogs.unshift(this);
+ ui.update();
+ return this;
+ }
+ if (ui.dialogs[i].static) ui.dialogs[i].unfocus();
+ else ui.dialogs[i].hide();
+ }
+ ui.dialog = this;
+ var translate;
+ if (lib.config.remember_dialog && lib.config.dialog_transform && !this.classList.contains('fixed')) {
+ translate = lib.config.dialog_transform;
+ this._dragtransform = translate;
+ this.style.transform = 'translate(' + translate[0] + 'px,' + translate[1] + 'px) scale(0.8)';
+ }
+ else {
+ this.style.transform = 'scale(0.8)';
+ }
+ this.style.transitionProperty = 'opacity,transform';
+ this.style.opacity = 0;
+ ui.arena.appendChild(this);
+ ui.dialogs.unshift(this);
+ ui.update();
+ ui.refresh(this);
+ if (lib.config.remember_dialog && lib.config.dialog_transform && !this.classList.contains('fixed')) {
+ this.style.transform = 'translate(' + translate[0] + 'px,' + translate[1] + 'px) scale(1)';
+ }
+ else {
+ this.style.transform = 'scale(1)';
+ }
+ this.style.opacity = 1;
+ var that = this;
+ setTimeout(function () {
+ that.style.transitionProperty = '';
+ }, 500);
+ return this;
+ }
+ close() {
+ ui.dialogs.remove(this);
+ this.delete();
+ if (ui.dialogs.length > 0) {
+ ui.dialog = ui.dialogs[0];
+ ui.dialog.show();
+ ui.dialog.refocus();
+ ui.update();
+ }
+ // if(ui.arenalog){
+ // ui.arenalog.classList.remove('withdialog');
+ // }
+ return this;
+ }
+ setCaption(str) {
+ this.querySelector('.caption').innerHTML = str;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/gameEvent.js b/noname/library/element/gameEvent.js
new file mode 100644
index 000000000..4715597c9
--- /dev/null
+++ b/noname/library/element/gameEvent.js
@@ -0,0 +1,855 @@
+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 { import('./gameEventPromise.js').default } */
+ #promise;
+ /**
+ * @param {string} [name]
+ * @param {false} [trigger]
+ */
+ constructor(name, trigger) {
+ if (typeof name == 'string') {
+ this.name = name;
+ const gameEvent = get.event();
+ if (gameEvent) {
+ const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`;
+ if (gameEvent.hasHandler(type)) this.pushHandler(...gameEvent.getHandler(type));
+ }
+ game.globalEventHandlers.addHandlerToEvent(this);
+ }
+ this.step = 0;
+ this.finished = false;
+ /**
+ * @type {(import('./GameEventPromise.js').default)[]}
+ */
+ this.next = [];
+ /**
+ * @type {(import('./GameEventPromise.js').default)[]}
+ */
+ this.after = [];
+ this.custom = {
+ add: {},
+ replace: {}
+ };
+ this._aiexclude = [];
+ this._notrigger = [];
+ 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;
+ }
+ static initialGameEvent() {
+ return new GameEvent().finish().toPromise();
+ }
+ /**
+ * @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.contains(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.contains(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 {import("../util/index.js").AsyncFunction[] | 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;
+ const toreturn = forced ? null : {};
+ if (!includeSelf || typeof level === 'number') {
+ if (event._modparent && game.online) event = event._modparent;
+ else event = this.parent;
+ }
+ if (typeof level === 'number') {
+ for (let i = 1; i < level; i++) {
+ if (!event) return toreturn;
+ event = event.parent;
+ }
+ return event;
+ }
+ const historys = [];
+ const filter = typeof level === 'function' ? level : evt => evt.name === level;
+ while (true) {
+ if (!event) return toreturn;
+ historys.push(event);
+ if (filter(event)) return event;
+ event = event.parent;
+ if (historys.includes(event)) return toreturn;
+ }
+ }
+ getTrigger() {
+ return this.getParent('arrangeTrigger')._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 this;
+ if ((this.name === 'gain' || this.name === 'lose') && !_status.gameDrawed) return this;
+ 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 this;
+ if (!game.players || !game.players.length) return this;
+ 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 this;
+ 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 this;
+ }
+ untrigger(all = true, player) {
+ const evt = this._triggering;
+ if (all) {
+ 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 { import('../index.js').GameEventPromise }
+ */
+ toPromise() {
+ if (!this.#promise) {
+ this.#promise = new lib.element.GameEventPromise(this);
+ }
+ return this.#promise;
+ }
+ /**
+ * @returns {never}
+ */
+ typeAnnotation() {
+ /**
+ * @type {import('../index.js').Player}
+ */
+ // @ts-ignore
+ this.source;
+ /**
+ * @type {import('../index.js').Player}
+ */
+ // @ts-ignore
+ this.player;
+ /**
+ * @type {import('../index.js').Player}
+ */
+ // @ts-ignore
+ this.target;
+ /**
+ * @type {import('../index.js').Player[]}
+ */
+ // @ts-ignore
+ this.targets;
+ /**
+ * @type {import('../index.js').Card}
+ */
+ // @ts-ignore
+ this.card;
+ /**
+ * @type {import('../index.js').Card[]}
+ */
+ // @ts-ignore
+ this.cards;
+ /**
+ * @type {string}
+ */
+ this.skill;
+ /**
+ * @type {boolean}
+ */
+ this.forced;
+ /**
+ * @type {number}
+ */
+ this.num;
+ /**
+ * @type {GameEvent}
+ */
+ // @ts-ignore
+ this._trigger;
+ /**
+ * @type {Record}
+ */
+ this._result;
+ /**
+ * @type {number}
+ */
+ // @ts-ignore
+ this.baseDamage;
+ /**
+ * @type {import('../index.js').Player}
+ */
+ // @ts-ignore
+ this.customSource;
+ /**
+ * @type {number}
+ */
+ // @ts-ignore
+ this.extraDamage;
+ /**
+ * @type {string}
+ */
+ // @ts-ignore
+ this.nature;
+ /**
+ * @type {boolean}
+ */
+ // @ts-ignore
+ this.notrigger;
+ /**
+ * @type {number}
+ */
+ // @ts-ignore
+ this.original_num;
+ /**
+ * @type {boolean}
+ */
+ // @ts-ignore
+ this.unreal;
+ /**
+ * @type { import('../index.js').Button[] }
+ */
+ // @ts-ignore
+ this.excludeButton;
+ throw new Error('Do not call this method');
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/gameEventPromise.js b/noname/library/element/gameEventPromise.js
new file mode 100644
index 000000000..30d0ac8cd
--- /dev/null
+++ b/noname/library/element/gameEventPromise.js
@@ -0,0 +1,161 @@
+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';
+
+/**
+ * 将事件Promise化以使用async异步函数来执行事件。
+ *
+ * 事件Promise化后,需要既能使用await等待事件完成,
+ * 又需要在执行之前对事件进行配置。
+ *
+ * 所以这个类的实例集成了事件和Promise二者的所有属性,
+ * 且Promise的原有属性无法被修改,一切对这个类实例的属性修改,删除,
+ * 再配置等操作都会转发到事件对应的属性中。
+ *
+ * @todo 需要完成异步事件的debugger方法
+ *
+ * @example
+ * 使用await xx()等待异步事件执行:
+ * ```js
+ * await game.xxx().setContent('yyy').set(zzz, 'i');
+ * ```
+ * 使用await player.xxx()等待异步事件执行:
+ * ```js
+ * await player.draw(2);
+ * game.log('等待', player, '摸牌完成执行log');
+ * ```
+ */
+export class GameEventPromise extends Promise {
+ // 我谢谢你,这里是必须有的
+ // 否则Promise的方法对其子类无效
+ static get [Symbol.species]() {
+ return Promise;
+ }
+ #event;
+ /**
+ * @param { import('./gameEvent.js').default } event
+ * @returns { Promise & import('./gameEvent.js').default }
+ */
+ constructor(event) {
+ super(resolve => {
+ // 设置为异步事件
+ event.async = true;
+ // 事件结束后触发resolve
+ event.resolve = resolve;
+ if (!_status.event) return;
+ // game.createEvent的时候还没立即push到next里
+ Promise.resolve().then(() => {
+ game.executingAsyncEventMap.set(_status.event.toEvent(), (game.executingAsyncEventMap.get(_status.event.toEvent()) || Promise.resolve()).then(() => {
+ let eventPromise = _status.event.next.find(e => e.toEvent() == event);
+ // 如果父级事件也是一个异步的话,那应该立即执行这个事件的
+ // 如果在AsyncFunction执行过程中在别的位置新建了一个异步事件,那也直接(等会set配置完)执行
+ if (eventPromise && (_status.event.content instanceof AsyncFunction || Array.isArray(_status.event.contents))) {
+ // 异步执行game.loop
+ // 不直接game.loop(event)是因为需要让别人可以手动set()和setContent()
+ // 再执行game.loop是因为原有的game.loop被await卡住了,
+ // 得新执行一个只执行这个异步事件的game.loop
+
+ // 事件自行处理skip情况
+ if (event.player && event.player.skipList.includes(event.name)) {
+ _status.event.trigger(event.name + 'Skipped');
+ event.player.skipList.remove(event.name);
+ if (lib.phaseName.includes(event.name)) event.player.getHistory('skipped').add(event.name);
+ _status.event.next.remove(eventPromise);
+ event.finish();
+ resolve();
+ return eventPromise;
+ }
+
+ if (_status.event != eventPromise) {
+ eventPromise.parent = _status.event;
+ _status.event = eventPromise;
+ game.getGlobalHistory('everything').push(eventPromise);
+ }
+ return game.loop(eventPromise).then(() => {
+ // 有时候event.finished还是false
+ return eventPromise;
+ });
+ }
+ }));
+ });
+ });
+ this.#event = event;
+ return new Proxy(this, {
+ get(target, prop, receiver) {
+ const thisValue = Reflect.get(target, prop);
+ if (thisValue) {
+ if (typeof thisValue == 'function') {
+ return thisValue.bind(target);
+ }
+ return thisValue;
+ }
+ const eventValue = Reflect.get(event, prop);
+ return eventValue == event ? receiver : eventValue;
+ },
+ set(target, prop, newValue) {
+ return Reflect.set(event, prop, newValue);
+ },
+ deleteProperty(target, prop) {
+ return Reflect.deleteProperty(event, prop);
+ },
+ defineProperty(target, prop, attributes) {
+ return Reflect.defineProperty(event, prop, attributes);
+ },
+ has(target, prop) {
+ return Reflect.has(event, prop);
+ },
+ ownKeys(target) {
+ return Reflect.ownKeys(event);
+ },
+ getOwnPropertyDescriptor(target, prop) {
+ return Reflect.getOwnPropertyDescriptor(event, prop);
+ },
+ });
+ }
+ /** 获取原事件对象 */
+ toEvent() {
+ return this.#event;
+ }
+ /**
+ * 在某个异步事件中调试变量信息
+ *
+ * 注: 在调试步骤中`定义的变量只在当前输入的语句有效`
+ *
+ * @example
+ * 在技能中调试技能content相关的信息
+ * ```js
+ * await event.debugger();
+ * ```
+ * 在技能中调试触发此技能事件的相关的信息
+ * ```js
+ * await trigger.debugger();
+ * ```
+ */
+ async debugger() {
+ return new Promise(resolve => {
+ const runCode = function (event, code) {
+ try {
+ // 为了使玩家调试时使用var player=xxx时不报错,故使用var
+ var { player, _trigger: trigger, _result: result } = event;
+ return eval(code);
+ } catch (error) {
+ return error;
+ }
+ }.bind(window);
+ const inputCallback = inputResult => {
+ if (inputResult === false) {
+ resolve(null);
+ } else {
+ const obj = runCode(this.toEvent(), inputResult);
+ alert((!obj || obj instanceof Error) ? String(obj) : get.stringify(obj));
+ game.promises.prompt('debugger调试').then(inputCallback);
+ }
+ };
+ game.promises.prompt('debugger调试').then(inputCallback);
+ });
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/index.js b/noname/library/element/index.js
new file mode 100644
index 000000000..817d3cc2e
--- /dev/null
+++ b/noname/library/element/index.js
@@ -0,0 +1,12 @@
+export { Button } from "./button.js";
+export { Card } from "./card.js";
+export { Client } from "./client.js";
+export { Content } from "./content.js";
+export { Contents } from "./contents.js";
+export { Control } from "./control.js";
+export { Dialog } from "./dialog.js";
+export { GameEvent } from "./gameEvent.js";
+export { GameEventPromise } from "./gameEventPromise.js";
+export { NodeWS } from "./nodeWs.js";
+export { Player } from "./player.js";
+export { VCard } from "./vcard.js";
\ No newline at end of file
diff --git a/noname/library/element/nodeWS.js b/noname/library/element/nodeWS.js
new file mode 100644
index 000000000..bcbf3588a
--- /dev/null
+++ b/noname/library/element/nodeWS.js
@@ -0,0 +1,25 @@
+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';
+
+export class NodeWS {
+ /**
+ * @param {string} id
+ */
+ constructor(id) {
+ this.wsid = id;
+ }
+ send(message) {
+ game.send('server', 'send', this.wsid, message);
+ }
+ on(type, func) {
+ this['on' + type] = func;
+ }
+ close() {
+ game.send('server', 'close', this.wsid);
+ }
+}
\ No newline at end of file
diff --git a/noname/library/element/player.js b/noname/library/element/player.js
index 5b6f97fab..a4c04dcb5 100644
--- a/noname/library/element/player.js
+++ b/noname/library/element/player.js
@@ -5,9 +5,9 @@ import { Library as lib } from "../index.js";
import { status as _status } from '../../status/index.js';
import { UI as ui } from '../../ui/index.js';
-export default class extends HTMLDivElement {
+export class Player extends HTMLDivElement {
/**
- * @param {HTMLDivElement} [position]
+ * @param {HTMLDivElement|DocumentFragment} [position]
* @param {true} [noclick]
*/
// @ts-ignore
@@ -17,7 +17,7 @@ export default class extends HTMLDivElement {
*/
// @ts-ignore
const player = ui.create.div('.player', position);
- Object.setPrototypeOf(player, lib.element.Player.prototype);
+ Object.setPrototypeOf(player, Player.prototype);
player.build(noclick);
return player;
}
diff --git a/noname/library/element/vcard.js b/noname/library/element/vcard.js
new file mode 100644
index 000000000..3a8e73515
--- /dev/null
+++ b/noname/library/element/vcard.js
@@ -0,0 +1,114 @@
+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';
+
+export class VCard {
+ /**
+ * @param {any} [suitOrCard]
+ * @param {number | Card[]} [numberOrCards]
+ * @param {string} [name]
+ * @param {string} [nature]
+ */
+ constructor(suitOrCard, numberOrCards, name, nature) {
+ if (Array.isArray(suitOrCard)) {
+ /**
+ * @type {string}
+ */
+ this.suit = suitOrCard[0];
+ /**
+ * @type {number}
+ */
+ this.number = suitOrCard[1];
+ /**
+ * @type {string}
+ */
+ this.name = suitOrCard[2];
+ /**
+ * @type {string}
+ */
+ this.nature = suitOrCard[3];
+ }
+ else if (get.itemtype(suitOrCard) == 'card') {
+ this.name = get.name(suitOrCard);
+ this.suit = get.suit(suitOrCard);
+ this.color = get.color(suitOrCard);
+ this.number = get.number(suitOrCard);
+ this.nature = get.nature(suitOrCard);
+ this.isCard = true;
+ this.cardid = suitOrCard.cardid;
+ this.wunature = suitOrCard.wunature;
+ /**
+ * @type {Record}
+ */
+ this.storage = get.copy(suitOrCard.storage);
+ if (Array.isArray(numberOrCards)) this.cards = numberOrCards.slice();
+ else this.cards = [suitOrCard];
+ const info = get.info(this, false);
+ if (info) {
+ const autoViewAs = info.autoViewAs;
+ if (typeof autoViewAs == 'string') this.name = autoViewAs;
+ }
+ }
+ else if (suitOrCard && typeof suitOrCard != 'string') {
+ Object.keys(suitOrCard).forEach(key => {
+ const propertyDescriptor = Object.getOwnPropertyDescriptor(suitOrCard, key), value = propertyDescriptor.value;
+ if (Array.isArray(value)) this[key] = value.slice();
+ else Object.defineProperty(this, key, propertyDescriptor);
+ });
+ if (Array.isArray(numberOrCards)) {
+ const noCards = !this.cards;
+ /**
+ * @type {Card[]}
+ */
+ this.cards = numberOrCards.slice();
+ if (noCards) {
+ if (!lib.suits.includes(this.suit)) this.suit = get.suit(this);
+ if (!Object.keys(lib.color).includes(this.color)) this.color = get.color(this);
+ if (typeof this.number != 'number') this.number = get.number(this);
+ if (!this.nature) this.nature = get.nature(this);
+ }
+ }
+ const info = get.info(this, false);
+ if (info) {
+ const autoViewAs = info.autoViewAs;
+ if (typeof autoViewAs == 'string') this.name = autoViewAs;
+ }
+ }
+ if (typeof suitOrCard == 'string') this.suit = suitOrCard;
+ if (typeof numberOrCards == 'number') this.number = numberOrCards;
+ if (typeof name == 'string') this.name = name;
+ if (typeof nature == 'string') this.nature = nature;
+ if (!this.storage) this.storage = {};
+ if (!this.cards) this.cards = [];
+ }
+ sameSuitAs(card) {
+ return get.suit(this) == get.suit(card);
+ }
+ differentSuitFrom(card) {
+ return get.suit(this) != get.suit(card);
+ }
+ sameNumberAs(card) {
+ return get.number(this) == get.number(card);
+ }
+ differentNumberFrom(card) {
+ return get.number(this) != get.number(card);
+ }
+ sameNameAs(card) {
+ return get.name(this) == get.name(card);
+ }
+ differentNameFrom(card) {
+ return get.name(this) != get.name(card);
+ }
+ /**
+ * @param {Player} player
+ */
+ hasNature(nature, player) {
+ const natures = get.natureList(this, player);
+ if (!nature) return natures.length > 0;
+ if (nature == 'linked') return natures.some(n => lib.linked.includes(n));
+ return get.is.sameNature(natures, nature);
+ }
+}
\ No newline at end of file
diff --git a/noname/library/index.js b/noname/library/index.js
index 97162e6c8..8e42ec2fc 100644
--- a/noname/library/index.js
+++ b/noname/library/index.js
@@ -20,9 +20,8 @@ import { GNC as gnc } from '../gnc/index.js';
import { LibInit } from "./init/index.js";
import { Announce } from "./announce/index.js";
-import LibElementContent from "./element/content.js";
-import LibElementContents from "./element/contents.js";
-import LibElementPlayer from "./element/player.js";
+import * as Element from "./element/index.js";
+
export class Library extends Uninstantable {
static configprefix = 'noname_0.9_';
@@ -97,6 +96,25 @@ export class Library extends Uninstantable {
*/
// @ts-ignore
this.videos;
+ /**
+ * @type { {
+ * fs: typeof import("fs"),
+ * path: typeof import("path"),
+ * debug: () => void,
+ * clients: Element.Client[],
+ * banned:[],
+ * observing:[],
+ * torespond:{},
+ * torespondtimeout:{},
+ * } }
+ */
+ // @ts-ignore
+ this.node;
+ /**
+ * @type { { [key: string]: string } }
+ */
+ // @ts-ignore
+ this.playerOL;
throw new Error('Do not call this method');
}
//函数钩子
@@ -9225,2268 +9243,18 @@ export class Library extends Uninstantable {
_stratagem_add_buff: '强化'
};
static element = {
- content: LibElementContent,
- contents: LibElementContents,
- Player: LibElementPlayer,
- Card: class extends HTMLDivElement {
- /**
- * @param {HTMLDivElement} [position]
- * @param {'noclick'} [info]
- * @param {true} [noclick]
- */
- constructor(position, info, noclick) {
- const card = ui.create.div('.card', position);
- Object.setPrototypeOf(card, lib.element.Card.prototype);
- card.build(info, noclick);
- return card;
- }
- build(info, noclick) {
- let card = this;
- card.buildNode();
- card.buildIntro(noclick);
- card.buildProperty();
- card.buildEventListener(info);
- }
- buildEventListener(info) {
- let card = this;
- if (info != 'noclick') {
- card.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.card);
- if (lib.config.touchscreen) {
- card.addEventListener('touchstart', ui.click.cardtouchstart);
- card.addEventListener('touchmove', ui.click.cardtouchmove);
- }
- if (lib.cardSelectObserver) lib.cardSelectObserver.observe(card, {
- attributes: true
- });
- }
- }
- buildProperty() {
- let card = this;
- card.storage = {};
- card.vanishtag = [];
- card.gaintag = [];
- card._uncheck = [];
- }
- buildNode() {
- this.node = {
- image: ui.create.div('.image', this),
- info: ui.create.div('.info', this),
- name: ui.create.div('.name', this),
- name2: ui.create.div('.name2', this),
- background: ui.create.div('.background', this),
- intro: ui.create.div('.intro', this),
- range: ui.create.div('.range', this),
- gaintag: ui.create.div('.gaintag', this),
- };
- this.node.intro.innerHTML = lib.config.intro;
- }
- buildIntro(noclick) {
- if (!noclick) lib.setIntro(this);
- }
- //执行销毁一张牌的钩子函数
- selfDestroy(event) {
- if (this._selfDestroyed) return;
- this._selfDestroyed = true;
- this.fix();
- this.delete();
- const info = get.info(this, false);
- if (!info) return;
- if (info.destroyLog !== false) game.log(this, '被销毁了');
- if (info.onDestroy) info.onDestroy(this, event);
- }
- //判断一张牌进入某个区域后是否会被销毁
- willBeDestroyed(targetPosition, player, event) {
- const destroyed = this.destroyed;
- if (typeof destroyed == 'function') {
- return destroyed(this, targetPosition, player, event);
- }
- else if (lib.skill[destroyed]) {
- if (player) {
- if (player.hasSkill(destroyed)) {
- delete this.destroyed;
- return false;
- }
- }
- return true;
- }
- else if (typeof destroyed == 'string') {
- return (destroyed == targetPosition);
- }
- return destroyed;
- }
- hasNature(nature, player) {
- return game.hasNature(this, nature, player);
- }
- //只针对【杀】起效果
- addNature(nature) {
- let natures = [];
- if (!this.nature) this.nature = '';
- else {
- natures.addArray(get.natureList(this.nature));
- }
- natures.addArray(get.natureList(nature));
- this.nature = get.nature(natures);
- this.classList.add(nature);
- let str = get.translation(this.nature) + '杀';
- this.node.name.innerText = str;
- let name = get.name(this, false);
- do {
- if (name == 'sha') {
- let _bg;
- for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
- if (_bg) {
- this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
- break;
- }
- }
- this.node.image.setBackgroundImage('image/card/' + name + '.png');
- }
- while (0);
- return this.nature;
- }
- removeNature(nature) {
- if (!this.nature) return;
- let natures = get.natureList(this.nature);
- natures.remove(nature);
- if (!natures.length) delete this.nature;
- else this.nature = get.nature(natures);
- this.classList.remove(nature);
- let str = get.translation(this.nature) + '杀';
- this.node.name.innerText = str;
- let name = get.name(this, false);
- do {
- if (name == 'sha') {
- let _bg;
- for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
- if (_bg) {
- this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
- break;
- }
- }
- this.node.image.setBackgroundImage('image/card/' + name + '.png');
- }
- while (0);
- return this.nature;
- }
- addGaintag(gaintag) {
- if (Array.isArray(gaintag)) this.gaintag = gaintag.slice(0);
- else this.gaintag.add(gaintag);
- var str = '';
- for (var gi = 0; gi < this.gaintag.length; gi++) {
- var translate = get.translation(this.gaintag[gi]);
- if (translate != 'invisible') {
- str += translate;
- if (gi < this.gaintag.length - 1) str += ' ';
- }
- }
- this.node.gaintag.innerHTML = str;
- }
- removeGaintag(tag) {
- if (tag === true) {
- if (this.gaintag && this.gaintag.length || this.node.gaintag.innerHTML.length) this.addGaintag([]);
- }
- else if (this.hasGaintag(tag)) {
- this.gaintag.remove(tag);
- this.addGaintag(this.gaintag);
- }
- }
- hasGaintag(tag) {
- return this.gaintag && this.gaintag.contains(tag);
- }
- /**
- * @param {[string, number, string, string] | {
- * suit: string;
- * number: number;
- * name: string;
- * nature: string;
- * }} card
- */
- init(card) {
- if (Array.isArray(card)) {
- if (card[2] == 'huosha') {
- card[2] = 'sha';
- card[3] = 'fire';
- }
- else if (card[2] == 'leisha') {
- card[2] = 'sha';
- card[3] = 'thunder';
- }
- else if (card[2] == 'cisha') {
- card[2] = 'sha';
- card[3] = 'stab';
- }
- else if (card[2].length > 3) {
- let prefix = card[2].slice(0, card[2].lastIndexOf('sha'));
- if (lib.nature.has(prefix)) {
- if (prefix.length + 3 == card[2].length) {
- card[2] = 'sha';
- card[3] = prefix;
- }
- }
- if (card[2].startsWith('sha_')) {
- let suffix = card[2].slice(4);
- let natureList = suffix.split('_');
- card[2] = 'sha';
- card[3] = get.nature(natureList);
- }
- }
- }
- else if (typeof card == 'object') {
- card = [card.suit, card.number, card.name, card.nature];
- }
- var cardnum = card[1] || '';
- if (parseInt(cardnum) == cardnum) cardnum = parseInt(cardnum);
-
- if (!lib.card[card[2]]) {
- lib.card[card[2]] = {};
- }
- var info = lib.card[card[2]];
- if (info.global && !this.classList.contains('button')) {
- if (Array.isArray(info.global)) {
- while (info.global.length) {
- game.addGlobalSkill(info.global.shift());
- }
- }
- else if (typeof info.global == 'string') {
- game.addGlobalSkill(info.global);
- }
- delete info.global;
- }
- this.suit = card[0];
- this.number = parseInt(card[1]) || 0;
- this.name = card[2];
-
- if (info.destroy && (typeof info.destroy != 'boolean' && !lib.skill[info.destroy])) {
- this.destroyed = info.destroy;
- }
-
- if (_status.connectMode && !game.online && lib.cardOL && !this.cardid) {
- this.cardid = get.id();
- lib.cardOL[this.cardid] = this;
- }
- if (!_status.connectMode && !_status.video) {
- this.cardid = get.id();
- }
-
- this.$init(card);
-
- if (this.inits) {
- for (var i = 0; i < this.inits.length; i++) {
- this.inits[i](this);
- }
- }
- if (typeof info.init == 'function') info.init();
-
- return this;
- }
- /**
- * @param {[string, number, string, string]} card
- */
- $init(card) {
- var info = lib.card[card[2]];
- var cardnum = card[1] || '';
- if (parseInt(cardnum) == cardnum) cardnum = parseInt(cardnum);
- if (cardnum > 0 && cardnum < 14) {
- cardnum = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'][cardnum - 1];
- }
- if (this.name) {
- this.classList.remove('epic');
- this.classList.remove('legend');
- this.classList.remove('gold');
- this.classList.remove('unique');
- this.style.background = '';
- var subtype = get.subtype(this, false);
- if (subtype) {
- this.classList.remove(subtype);
- }
- }
- if (info.epic) {
- this.classList.add('epic');
- }
- else if (info.legend) {
- this.classList.add('legend');
- }
- else if (info.gold) {
- this.classList.add('gold');
- }
- else if (info.unique) {
- this.classList.add('unique');
- }
- var bg = card[2];
- if (info.cardimage) {
- bg = info.cardimage;
- }
- var img = lib.card[bg].image;
- if (img) {
- if (img.startsWith('db:')) {
- img = img.slice(3);
- }
- else if (!img.startsWith('ext:')) {
- img = null;
- }
- }
- this.classList.remove('fullskin');
- this.classList.remove('fullimage');
- this.classList.remove('fullborder');
- this.dataset.cardName = card[2];
- this.dataset.cardType = info.type || '';
- this.dataset.cardSubype = info.subtype || '';
- this.dataset.cardMultitarget = info.multitarget ? '1' : '0';
- this.node.name.dataset.nature = '';
- this.node.info.classList.remove('red');
- if (!lib.config.hide_card_image && lib.card[bg].fullskin) {
- this.classList.add('fullskin');
- if (img) {
- if (img.startsWith('ext:')) {
- this.node.image.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
- }
- else {
- this.node.image.setBackgroundDB(img);
- }
- }
- else {
- if (lib.card[bg].modeimage) {
- this.node.image.setBackgroundImage('image/mode/' + lib.card[bg].modeimage + '/card/' + bg + '.png');
- }
- else {
- do {
- let nature = card[3];
- if (bg == 'sha' && typeof nature == 'string') {
- let natures = get.natureList(nature), _bg;
- for (const n of natures) if (lib.natureBg.has(n)) _bg = n;
- if (_bg) {
- this.node.image.setBackgroundImage(lib.natureBg.get(_bg));
- break;
- }
- }
- this.node.image.setBackgroundImage('image/card/' + bg + '.png');
- }
- while (0);
- }
- }
- }
- else if (lib.card[bg].image == 'background') {
- if (card[3]) this.node.background.setBackground(bg + '_' + get.natureList(card[3])[0], 'card');
- else this.node.background.setBackground(bg, 'card');
- }
- else if (lib.card[bg].fullimage) {
- this.classList.add('fullimage');
- if (img) {
- if (img.startsWith('ext:')) {
- this.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
- this.style.backgroundSize = 'cover';
- }
- else {
- this.setBackgroundDB(img);
- }
- }
- else if (lib.card[bg].image) {
- if (lib.card[bg].image.startsWith('character:')) {
- this.setBackground(lib.card[bg].image.slice(10), 'character');
- }
- else {
- this.setBackground(lib.card[bg].image);
- }
- }
- else {
- var cardPack = lib.cardPack['mode_' + get.mode()];
- if (Array.isArray(cardPack) && cardPack.contains(bg)) {
- this.setBackground('mode/' + get.mode() + '/card/' + bg);
- }
- else {
- this.setBackground('card/' + bg);
- }
- }
- }
- else if (lib.card[bg].fullborder) {
- this.classList.add('fullborder');
- if (lib.card[bg].fullborder == 'gold') {
- this.node.name.dataset.nature = 'metalmm';
- }
- else if (lib.card[bg].fullborder == 'silver') {
- this.node.name.dataset.nature = 'watermm';
- }
- if (!this.node.avatar) {
- this.node.avatar = ui.create.div('.cardavatar');
- this.insertBefore(this.node.avatar, this.firstChild);
- }
- if (!this.node.framebg) {
- this.node.framebg = ui.create.div('.cardframebg');
- this.node.framebg.dataset.auto = lib.card[bg].fullborder;
- this.insertBefore(this.node.framebg, this.firstChild);
- }
- if (img) {
- if (img.startsWith('ext:')) {
- this.node.avatar.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
- this.node.avatar.style.backgroundSize = 'cover';
- }
- else {
- this.node.avatar.setBackgroundDB(img);
- }
- }
- else if (lib.card[bg].image) {
- if (lib.card[bg].image.startsWith('character:')) {
- this.node.avatar.setBackground(lib.card[bg].image.slice(10), 'character');
- }
- else {
- this.node.avatar.setBackground(lib.card[bg].image);
- }
- }
- else {
- var cardPack = lib.cardPack['mode_' + get.mode()];
- if (Array.isArray(cardPack) && cardPack.contains(bg)) {
- this.node.avatar.setBackground('mode/' + get.mode() + '/card/' + bg);
- }
- else {
- this.node.avatar.setBackground('card/' + bg);
- }
- }
- }
- else if (lib.card[bg].image == 'card') {
- if (card[3]) this.setBackground(bg + '_' + get.natureList(card[3])[0], 'card');
- else this.setBackground(bg, 'card');
- }
- else if (typeof lib.card[bg].image == 'string' && !lib.card[bg].fullskin) {
- if (img) {
- if (img.startsWith('ext:')) {
- this.setBackgroundImage(img.replace(/^ext:/, 'extension/'));
- this.style.backgroundSize = 'cover';
- }
- else {
- this.setBackgroundDB(img);
- }
- }
- else {
- this.setBackground(lib.card[bg].image);
- }
- }
- else {
- this.node.background.innerHTML = lib.translate[bg + '_cbg'] || lib.translate[bg + '_bg'] || get.translation(bg)[0];
- // this.node.background.style.fontFamily=lib.config.card_font;
- if (this.node.background.innerHTML.length > 1) this.node.background.classList.add('tight');
- else this.node.background.classList.remove('tight');
- }
- if (!lib.card[bg].fullborder && this.node.avatar && this.node.framebg) {
- this.node.avatar.remove();
- this.node.framebg.remove();
- delete this.node.avatar;
- delete this.node.framebg;
- }
- if (info.noname && !this.classList.contains('button')) {
- this.node.name.style.display = 'none';
- }
- if (info.color) {
- this.style.color = info.color;
- }
- if (info.textShadow) {
- this.style.textShadow = info.textShadow;
- }
- if (info.opacity) {
- this.node.info.style.opacity = info.opacity;
- this.node.name.style.opacity = info.opacity;
- }
- if (info.modinfo) {
- this.node.info.innerHTML = info.modinfo;
- }
- else {
- this.node.info.innerHTML = get.translation(card[0]) + ' ' + cardnum + '';
- }
- if (info.addinfo) {
- if (!this.node.addinfo) {
- this.node.addinfo = ui.create.div('.range', this);
- }
- this.node.addinfo.innerHTML = info.addinfo;
- }
- else if (this.node.addinfo) {
- this.node.addinfo.remove();
- delete this.node.addinfo;
- }
- if (card[0] == 'heart' || card[0] == 'diamond') {
- this.node.info.classList.add('red');
- }
- this.node.image.className = 'image';
- var name = get.translation(card[2]);
- if (card[2] == 'sha') {
- name = '';
- let nature = card[3];
- if (nature) {
- let natures = get.natureList(nature);
- natures.sort(lib.sort.nature);
- for (let nature of natures) {
- name += lib.translate['nature_' + nature] || lib.translate[nature] || '';
- if (nature != 'stab') this.node.image.classList.add(nature);
- }
- }
- name += '杀';
- }
- this.node.name.innerHTML = name;
- if (name.length >= 5) {
- this.node.name.classList.add('long');
- if (name.length >= 7) {
- this.node.name.classList.add('longlong');
- }
- }
- this.node.name2.innerHTML = get.translation(card[0]) + cardnum + ' ' + name;
- this.classList.add('card');
- if (card[3]) {
- let natures = get.natureList(card[3]);
- natures.forEach(n => { if (n) this.classList.add(n); });
- this.nature = natures.filter(n => lib.nature.has(n)).sort(lib.sort.nature).join(lib.natureSeparator);
- }
- else if (this.nature) {
- this.classList.remove(this.nature);
- delete this.nature;
- }
- if (info.subtype) this.classList.add(info.subtype);
- this.node.range.innerHTML = '';
- switch (get.subtype(this, false)) {
- case 'equip1':
- var added = false;
- if (lib.card[this.name] && lib.card[this.name].distance) {
- var dist = lib.card[this.name].distance;
- if (dist.attackFrom) {
- added = true;
- this.node.range.innerHTML = '范围: ' + (-dist.attackFrom + 1);
- }
- }
- if (!added) {
- this.node.range.innerHTML = '范围: 1';
- }
- break;
- case 'equip3':
- if (info.distance && info.distance.globalTo) {
- this.node.range.innerHTML = '防御: ' + info.distance.globalTo;
- this.node.name2.innerHTML += '+';
- }
- break;
- case 'equip4':
- if (info.distance && info.distance.globalFrom) {
- this.node.range.innerHTML = '进攻: ' + (-info.distance.globalFrom);
- this.node.name2.innerHTML += '-';
- }
- break;
- }
- var tags = [];
- if (Array.isArray(card[4])) {
- tags.addArray(card[4]);
- }
- if (this.cardid) {
- if (!_status.cardtag) {
- _status.cardtag = {};
- }
- for (var i in _status.cardtag) {
- if (_status.cardtag[i].contains(this.cardid)) {
- tags.add(i);
- }
- }
- if (tags.length) {
- var tagstr = ' ';
- for (var i = 0; i < tags.length; i++) {
- var tag = tags[i];
- if (!_status.cardtag[tag]) {
- _status.cardtag[tag] = [];
- }
- _status.cardtag[tag].add(this.cardid);
- tagstr += lib.translate[tag + '_tag'];
- //if(i';
- this.node.range.innerHTML += tagstr;
- }
- }
- return this;
- }
- updateTransform(bool, delay) {
- if (delay) {
- var that = this;
- setTimeout(function () {
- that.updateTransform(that.classList.contains('selected'));
- }, delay);
- }
- else {
- if (_status.event.player != game.me) return;
- if (this._transform && this.parentNode && this.parentNode.parentNode &&
- this.parentNode.parentNode.parentNode == ui.me &&
- (!_status.mousedown || _status.mouseleft) &&
- (!this.parentNode.parentNode.classList.contains('scrollh') || (game.layout == 'long2' || game.layout == 'nova'))) {
- if (bool) {
- this.style.transform = this._transform + ' translateY(-20px)';
- }
- else {
- this.style.transform = this._transform || '';
- }
- }
- }
- }
- aiexclude() {
- _status.event._aiexclude.add(this);
- }
- //为此牌添加知情者。参数可为数组,若参数为字符串'everyone',则所有玩家均为知情者。
- addKnower(player) {
- if (!this._knowers) {
- this._knowers = [];
- }
- if (typeof player == 'string') {
- this._knowers.add(player);
- } else {
- let type = get.itemtype(player);
- if (type == 'player') {
- this._knowers.add(player.playerid);
- } else if (type == 'players') {
- player.forEach(p => this._knowers.add(p.playerid));
- }
- }
- }
- removeKnower(player) {
- if (!this._knowers) {
- return;
- }
- if (typeof player == 'string') {
- this._knowers.remove(player);
- } else {
- let type = get.itemtype(player);
- if (type == 'player') {
- this._knowers.remove(player.playerid);
- } else if (type == 'players') {
- player.forEach(p => this._knowers.remove(p.playerid));
- }
- }
- }
- //清除此牌的知情者。
- clearKnowers() {
- if (this._knowers) delete this._knowers;
- }
- //判断玩家对此牌是否知情。
- isKnownBy(player) {
- if (['e', 'j'].includes(get.position(this))) return true;//装备区或者判定区的牌,必知情。
- let owner = get.owner(this);
- if (owner) {
- if (owner == player) return true;//是牌主,必知情。
- if (player.hasSkillTag('viewHandcard', null, owner, true)) return true;//有viewHandcard标签,必知情。
- if (owner.isUnderControl(true, player)) return true;//被操控,必知情。
- }
- if (get.is.shownCard(this)) return true;//此牌是明置牌,必知情。
- if (this._knowers) {
- return this._knowers.includes('everyone') || this._knowers.includes(player.playerid);
- }
- return false;
- }
- getSource(name) {
- if (this.name == name) return true;
- var info = lib.card[this.name];
- if (info && Array.isArray(info.source)) {
- return info.source.contains(name);
- }
- return false;
- }
- moveDelete(player) {
- this.fixed = true;
- if (!this._listeningEnd || this._transitionEnded) {
- this.moveTo(player);
- var that = this;
- setTimeout(function () {
- that.delete();
- }, 200);
- }
- else {
- this._onEndMoveDelete = player;
- }
- }
- moveTo(player) {
- this.fixed = true;
- var dx, dy;
- if (this.classList.contains('center')) {
- var nx = [50, -52];
- var ny = [50, -52];
- nx = nx[0] * ui.arena.offsetWidth / 100 + nx[1];
- ny = ny[0] * ui.arena.offsetHeight / 100 + ny[1];
- dx = player.getLeft() + player.offsetWidth / 2 - 52 - nx;
- dy = player.getTop() + player.offsetHeight / 2 - 52 - ny;
- }
- else {
- this.style.left = this.offsetLeft + 'px';
- this.style.top = this.offsetTop + 'px';
-
- dx = player.getLeft() + player.offsetWidth / 2 - 52 - this.offsetLeft;
- dy = player.getTop() + player.offsetHeight / 2 - 52 - this.offsetTop;
- }
- if (get.is.mobileMe(player)) {
- dx += get.cardOffset();
- if (ui.arena.classList.contains('oblongcard')) {
- dy -= 16;
- }
- }
-
-
- if (this.style.transform && this.style.transform != 'none' && this.style.transform.indexOf('translate') == -1) {
- this.style.transform += ' translate(' + dx + 'px,' + dy + 'px)';
- }
- else {
- this.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
- }
- return this;
- }
- copy() {
- /**
- * @type {Card}
- */
- var node = this.cloneNode(true);
- node.style.transform = '';
- node.name = this.name;
- node.suit = this.suit;
- node.number = this.number;
- node.nature = this.nature;
- node.classList.remove('hidden');
- node.classList.remove('start');
- node.classList.remove('thrown');
- node.classList.remove('selectable');
- node.classList.remove('selected');
- node.classList.remove('removing');
- node.classList.remove('drawinghidden');
- node.classList.remove('glows');
- node.node = {
- name: node.querySelector('.name'),
- info: node.querySelector('.info'),
- intro: node.querySelector('.intro'),
- background: node.querySelector('.background'),
- image: node.querySelector('.image'),
- gaintag: node.querySelector('.gaintag'),
- };
- node.node.gaintag.innerHTML = '';
- var clone = true;
- var position;
- for (var i = 0; i < arguments.length; i++) {
- if (typeof arguments[i] == 'string') node.classList.add(arguments[i]);
- else if (['div', 'fragment'].includes(get.objtype(arguments[i]))) position = arguments[i];
- else if (typeof arguments[i] == 'boolean') clone = arguments[i];
- }
- node.moveTo = lib.element.Card.prototype.moveTo;
- node.moveDelete = lib.element.Card.prototype.moveDelete;
- if (clone) this.clone = node;
- if (position) position.appendChild(node);
- return node;
- }
- uncheck(skill) {
- if (skill) this._uncheck.add(skill);
- this.classList.add('uncheck');
- }
- recheck(skill) {
- if (skill) this._uncheck.remove(skill);
- else this._uncheck.length = 0;
- if (this._uncheck.length == 0) this.classList.remove('uncheck');
- }
- discard(bool) {
- if (!this._selfDestroyed) {
- this.fix();
- ui.discardPile.appendChild(this);
- }
- this.classList.remove('glow');
- if (bool === false) {
- ui.cardPile.insertBefore(this, ui.cardPile.childNodes[Math.floor(Math.random() * ui.cardPile.childNodes.length)]);
- }
- else {
- if (_status.discarded) {
- _status.discarded.add(this);
- }
- }
- }
- hasTag(tag) {
- if (this.cardid && _status.cardtag && _status.cardtag[tag] && _status.cardtag[tag].contains(this.cardid)) {
- return true;
- }
- return false;
- }
- hasPosition() {
- return ['h', 'e', 'j', 's', 'x'].contains(get.position(this));
- }
- isInPile() {
- return ['c', 'd'].contains(get.position(this));
- }
- },
- VCard: class {
- /**
- * @param {any} [suitOrCard]
- * @param {number | Card[]} [numberOrCards]
- * @param {string} [name]
- * @param {string} [nature]
- */
- constructor(suitOrCard, numberOrCards, name, nature) {
- if (Array.isArray(suitOrCard)) {
- /**
- * @type {string}
- */
- this.suit = suitOrCard[0];
- /**
- * @type {number}
- */
- this.number = suitOrCard[1];
- /**
- * @type {string}
- */
- this.name = suitOrCard[2];
- /**
- * @type {string}
- */
- this.nature = suitOrCard[3];
- }
- else if (get.itemtype(suitOrCard) == 'card') {
- this.name = get.name(suitOrCard);
- this.suit = get.suit(suitOrCard);
- this.color = get.color(suitOrCard);
- this.number = get.number(suitOrCard);
- this.nature = get.nature(suitOrCard);
- this.isCard = true;
- this.cardid = suitOrCard.cardid;
- this.wunature = suitOrCard.wunature;
- /**
- * @type {Record}
- */
- this.storage = get.copy(suitOrCard.storage);
- if (Array.isArray(numberOrCards)) this.cards = numberOrCards.slice();
- else this.cards = [suitOrCard];
- const info = get.info(this, false);
- if (info) {
- const autoViewAs = info.autoViewAs;
- if (typeof autoViewAs == 'string') this.name = autoViewAs;
- }
- }
- else if (suitOrCard && typeof suitOrCard != 'string') {
- Object.keys(suitOrCard).forEach(key => {
- const propertyDescriptor = Object.getOwnPropertyDescriptor(suitOrCard, key), value = propertyDescriptor.value;
- if (Array.isArray(value)) this[key] = value.slice();
- else Object.defineProperty(this, key, propertyDescriptor);
- });
- if (Array.isArray(numberOrCards)) {
- const noCards = !this.cards;
- /**
- * @type {Card[]}
- */
- this.cards = numberOrCards.slice();
- if (noCards) {
- if (!lib.suits.includes(this.suit)) this.suit = get.suit(this);
- if (!Object.keys(lib.color).includes(this.color)) this.color = get.color(this);
- if (typeof this.number != 'number') this.number = get.number(this);
- if (!this.nature) this.nature = get.nature(this);
- }
- }
- const info = get.info(this, false);
- if (info) {
- const autoViewAs = info.autoViewAs;
- if (typeof autoViewAs == 'string') this.name = autoViewAs;
- }
- }
- if (typeof suitOrCard == 'string') this.suit = suitOrCard;
- if (typeof numberOrCards == 'number') this.number = numberOrCards;
- if (typeof name == 'string') this.name = name;
- if (typeof nature == 'string') this.nature = nature;
- if (!this.storage) this.storage = {};
- if (!this.cards) this.cards = [];
- }
- sameSuitAs(card) {
- return get.suit(this) == get.suit(card);
- }
- differentSuitFrom(card) {
- return get.suit(this) != get.suit(card);
- }
- sameNumberAs(card) {
- return get.number(this) == get.number(card);
- }
- differentNumberFrom(card) {
- return get.number(this) != get.number(card);
- }
- sameNameAs(card) {
- return get.name(this) == get.name(card);
- }
- differentNameFrom(card) {
- return get.name(this) != get.name(card);
- }
- /**
- * @param {Player} player
- */
- hasNature(nature, player) {
- const natures = get.natureList(this, player);
- if (!nature) return natures.length > 0;
- if (nature == 'linked') return natures.some(n => lib.linked.includes(n));
- return get.is.sameNature(natures, nature);
- }
- },
- Button: class extends HTMLDivElement {
- /**
- * @param {{}} item
- * @param {keyof typeof ui.create.buttonPresets | (item: {}, type: Function, position?: HTMLDivElement, noClick?: true, button?: HTMLDivElement) => HTMLDivElement} type
- * @param {HTMLDivElement|DocumentFragment} [position]
- * @param {true} [noClick]
- * @param {HTMLDivElement} [button]
- */
- constructor(item, type, position, noClick, button) {
- if (ui.create.buttonPresets[type]) button = ui.create.buttonPresets[type](item, type, position, noClick, button);
- else if (typeof type == 'function') button = type(item, type, position, noClick, button);
- Object.setPrototypeOf(button, lib.element.Button.prototype);
- if (!noClick) button.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.button);
- else {
- button.classList.add('noclick');
- const intro = button.querySelector('.intro');
- if (intro) intro.remove();
- }
- return button;
- }
- exclude() {
- if (_status.event.excludeButton == undefined) {
- _status.event.excludeButton = [];
- }
- _status.event.excludeButton.add(this);
- }
- get updateTransform() {
- return lib.element.Card.prototype.updateTransform;
- }
- },
- GameEvent: class {
- /** @type { GameEventPromise } */
- #promise;
- /**
- * @param {string} [name]
- * @param {false} [trigger]
- */
- constructor(name, trigger) {
- if (typeof name == 'string') {
- this.name = name;
- const gameEvent = get.event();
- if (gameEvent) {
- const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`;
- 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 = [];
- 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;
- }
- static initialGameEvent() {
- return new lib.element.GameEvent().finish().toPromise();
- }
- /**
- * @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.contains(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.contains(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 {import("../util/index.js").AsyncFunction[] | 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;
- const toreturn = forced ? null : {};
- if (!includeSelf || typeof level === 'number') {
- if (event._modparent && game.online) event = event._modparent;
- else event = this.parent;
- }
- if (typeof level === 'number') {
- for (let i = 1; i < level; i++) {
- if (!event) return toreturn;
- event = event.parent;
- }
- return event;
- }
- const historys = [];
- const filter = typeof level === 'function' ? level : evt => evt.name === level;
- while (true) {
- if (!event) return toreturn;
- historys.push(event);
- if (filter(event)) return event;
- event = event.parent;
- if (historys.includes(event)) return toreturn;
- }
- }
- getTrigger() {
- return this.getParent('arrangeTrigger')._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 this;
- if ((this.name === 'gain' || this.name === 'lose') && !_status.gameDrawed) return this;
- 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 this;
- if (!game.players || !game.players.length) return this;
- 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 this;
- 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 this;
- }
- untrigger(all = true, player) {
- const evt = this._triggering;
- if (all) {
- 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;
- }
- /**
- * @returns {never}
- */
- typeAnnotation() {
- /**
- * @type {Player}
- */
- this.source;
- /**
- * @type {Player}
- */
- this.player;
- /**
- * @type {Player}
- */
- this.target;
- /**
- * @type {Player[]}
- */
- this.targets;
- /**
- * @type {Card}
- */
- this.card;
- /**
- * @type {Card[]}
- */
- this.cards;
- /**
- * @type {string}
- */
- this.skill;
- /**
- * @type {boolean}
- */
- this.forced;
- /**
- * @type {number}
- */
- this.num;
- /**
- * @type {GameEvent}
- */
- this._trigger;
- /**
- * @type {Record}
- */
- this._result;
- /**
- * @type {number}
- */
- this.baseDamage;
- /**
- * @type {Player}
- */
- this.customSource;
- /**
- * @type {number}
- */
- this.extraDamage;
- /**
- * @type {string}
- */
- this.nature;
- /**
- * @type {boolean}
- */
- this.notrigger;
- /**
- * @type {number}
- */
- this.original_num;
- /**
- * @type {boolean}
- */
- this.unreal;
- throw new Error('Do not call this method');
- }
- /**
- * 事件转为Promise化
- *
- * @returns { GameEventPromise }
- */
- toPromise() {
- if (!this.#promise) {
- this.#promise = new lib.element.GameEventPromise(this);
- }
- return this.#promise;
- }
- },
- /**
- * 将事件Promise化以使用async异步函数来执行事件。
- *
- * 事件Promise化后,需要既能使用await等待事件完成,
- * 又需要在执行之前对事件进行配置。
- *
- * 所以这个类的实例集成了事件和Promise二者的所有属性,
- * 且Promise的原有属性无法被修改,一切对这个类实例的属性修改,删除,
- * 再配置等操作都会转发到事件对应的属性中。
- *
- * @todo 需要完成异步事件的debugger方法
- *
- * @example
- * 使用await xx()等待异步事件执行:
- * ```js
- * await game.xxx().setContent('yyy').set(zzz, 'i');
- * ```
- * 使用await player.xxx()等待异步事件执行:
- * ```js
- * await player.draw(2);
- * game.log('等待', player, '摸牌完成执行log');
- * ```
- */
- GameEventPromise: class extends Promise {
- // 我谢谢你,这里是必须有的
- // 否则Promise的方法对其子类无效
- static get [Symbol.species]() {
- return Promise;
- }
- #event;
- /**
- * @param { GameEvent } event
- * @returns { Promise & GameEvent }
- */
- constructor(event) {
- super(resolve => {
- // 设置为异步事件
- event.async = true;
- // 事件结束后触发resolve
- event.resolve = resolve;
- if (!_status.event) return;
- // game.createEvent的时候还没立即push到next里
- Promise.resolve().then(() => {
- game.executingAsyncEventMap.set(_status.event.toEvent(), (game.executingAsyncEventMap.get(_status.event.toEvent()) || Promise.resolve()).then(() => {
- let eventPromise = _status.event.next.find(e => e.toEvent() == event);
- // 如果父级事件也是一个异步的话,那应该立即执行这个事件的
- // 如果在AsyncFunction执行过程中在别的位置新建了一个异步事件,那也直接(等会set配置完)执行
- if (eventPromise && (_status.event.content instanceof AsyncFunction || Array.isArray(_status.event.contents))) {
- // 异步执行game.loop
- // 不直接game.loop(event)是因为需要让别人可以手动set()和setContent()
- // 再执行game.loop是因为原有的game.loop被await卡住了,
- // 得新执行一个只执行这个异步事件的game.loop
-
- // 事件自行处理skip情况
- if (event.player && event.player.skipList.includes(event.name)) {
- _status.event.trigger(event.name + 'Skipped');
- event.player.skipList.remove(event.name);
- if (lib.phaseName.includes(event.name)) event.player.getHistory('skipped').add(event.name);
- _status.event.next.remove(eventPromise);
- event.finish();
- resolve();
- return eventPromise;
- }
-
- if (_status.event != eventPromise) {
- eventPromise.parent = _status.event;
- _status.event = eventPromise;
- game.getGlobalHistory('everything').push(eventPromise);
- }
- return game.loop(eventPromise).then(() => {
- // 有时候event.finished还是false
- return eventPromise;
- });
- }
- }));
- });
- });
- this.#event = event;
- return new Proxy(this, {
- get(target, prop, receiver) {
- const thisValue = Reflect.get(target, prop);
- if (thisValue) {
- if (typeof thisValue == 'function') {
- return thisValue.bind(target);
- }
- return thisValue;
- }
- const eventValue = Reflect.get(event, prop);
- return eventValue == event ? receiver : eventValue;
- },
- set(target, prop, newValue) {
- return Reflect.set(event, prop, newValue);
- },
- deleteProperty(target, prop) {
- return Reflect.deleteProperty(event, prop);
- },
- defineProperty(target, prop, attributes) {
- return Reflect.defineProperty(event, prop, attributes);
- },
- has(target, prop) {
- return Reflect.has(event, prop);
- },
- ownKeys(target) {
- return Reflect.ownKeys(event);
- },
- getOwnPropertyDescriptor(target, prop) {
- return Reflect.getOwnPropertyDescriptor(event, prop);
- },
- });
- }
- /** 获取原事件对象 */
- toEvent() {
- return this.#event;
- }
- /**
- * 在某个异步事件中调试变量信息
- *
- * 注: 在调试步骤中`定义的变量只在当前输入的语句有效`
- *
- * @example
- * 在技能中调试技能content相关的信息
- * ```js
- * await event.debugger();
- * ```
- * 在技能中调试触发此技能事件的相关的信息
- * ```js
- * await trigger.debugger();
- * ```
- */
- async debugger() {
- return new Promise(resolve => {
- const runCode = function (event, code) {
- try {
- // 为了使玩家调试时使用var player=xxx时不报错,故使用var
- var { player, _trigger: trigger, _result: result } = event;
- return eval(code);
- } catch (error) {
- return error;
- }
- }.bind(window);
- const inputCallback = inputResult => {
- if (inputResult === false) {
- resolve(null);
- } else {
- const obj = runCode(this.toEvent(), inputResult);
- alert((!obj || obj instanceof Error) ? String(obj) : get.stringify(obj));
- game.promises.prompt('debugger调试').then(inputCallback);
- }
- };
- game.promises.prompt('debugger调试').then(inputCallback);
- });
- }
- },
- Dialog: class extends HTMLDivElement {
- constructor() {
- let hidden = false;
- let noTouchScroll = false;
- let forceButton = false;
- let noForceButton = false;
- /** @type {this} */
- const dialog = ui.create.div('.dialog');
- Object.setPrototypeOf(dialog, lib.element.Dialog.prototype);
- dialog.contentContainer = ui.create.div('.content-container', dialog);
- dialog.content = ui.create.div('.content', dialog.contentContainer);
- dialog.bar1 = ui.create.div('.bar.top', dialog);
- dialog.bar2 = ui.create.div('.bar.bottom', dialog);
- dialog.buttons = [];
- Array.from(arguments).forEach(argument => {
- if (typeof argument == 'boolean') dialog.static = argument;
- else if (argument == 'hidden') hidden = true;
- else if (argument == 'notouchscroll') noTouchScroll = true;
- else if (argument == 'forcebutton') forceButton = true;
- else if (argument == 'noforcebutton') noForceButton = true;
- else dialog.add(argument);
- });
- if (!hidden) dialog.open();
- if (!lib.config.touchscreen) dialog.contentContainer.onscroll = ui.update;
- if (!noTouchScroll) {
- dialog.contentContainer.ontouchstart = ui.click.dialogtouchStart;
- dialog.contentContainer.ontouchmove = ui.click.touchScroll;
- dialog.contentContainer.style.webkitOverflowScrolling = 'touch';
- dialog.ontouchstart = ui.click.dragtouchdialog;
- }
- if (noForceButton) dialog.noforcebutton = true;
- else if (forceButton) {
- dialog.forcebutton = true;
- dialog.classList.add('forcebutton');
- }
- return dialog;
- }
- add(item, noclick, zoom) {
- if (typeof item == 'string') {
- if (item.startsWith('###')) {
- var items = item.slice(3).split('###');
- this.add(items[0], noclick, zoom);
- this.addText(items[1], items[1].length <= 20, zoom);
- }
- else if (noclick) {
- var strstr = item;
- item = ui.create.div('', this.content);
- item.innerHTML = strstr;
- }
- else {
- item = ui.create.caption(item, this.content);
- }
- }
- else if (['div', 'fragment'].includes(get.objtype(item))) {
- this.content.appendChild(item);
- }
- else if (get.itemtype(item) == 'cards') {
- var buttons = ui.create.div('.buttons', this.content);
- if (zoom) buttons.classList.add('smallzoom');
- this.buttons = this.buttons.concat(ui.create.buttons(item, 'card', buttons, noclick));
- }
- else if (get.itemtype(item) == 'players') {
- var buttons = ui.create.div('.buttons', this.content);
- if (zoom) buttons.classList.add('smallzoom');
- this.buttons = this.buttons.concat(ui.create.buttons(item, 'player', buttons, noclick));
- }
- else if (item[1] == 'textbutton') {
- ui.create.textbuttons(item[0], this, noclick);
- }
- else {
- var buttons = ui.create.div('.buttons', this.content);
- if (zoom) buttons.classList.add('smallzoom');
- this.buttons = this.buttons.concat(ui.create.buttons(item[0], item[1], buttons, noclick));
- }
- if (this.buttons.length) {
- if (this.forcebutton !== false) this.forcebutton = true;
- if (this.buttons.length > 3 || (zoom && this.buttons.length > 5)) {
- this.classList.remove('forcebutton-auto');
- }
- else if (!this.noforcebutton) {
- this.classList.add('forcebutton-auto');
- }
- }
- ui.update();
- return item;
- }
- addText(str, center) {
- if (str && str.startsWith('' + str + '
');
- }
- else {
- this.add('' + str + '
');
- }
- return this;
- }
- addSmall(item, noclick) {
- return this.add(item, noclick, true);
- }
- addAuto(content) {
- if (content && content.length > 4 && !this._hovercustomed) {
- this.addSmall(content);
- }
- else {
- this.add(content);
- }
- }
- open() {
- if (this.noopen) return;
- for (var i = 0; i < ui.dialogs.length; i++) {
- if (ui.dialogs[i] == this) {
- this.show();
- this.refocus();
- ui.dialogs.remove(this);
- ui.dialogs.unshift(this);
- ui.update();
- return this;
- }
- if (ui.dialogs[i].static) ui.dialogs[i].unfocus();
- else ui.dialogs[i].hide();
- }
- ui.dialog = this;
- var translate;
- if (lib.config.remember_dialog && lib.config.dialog_transform && !this.classList.contains('fixed')) {
- translate = lib.config.dialog_transform;
- this._dragtransform = translate;
- this.style.transform = 'translate(' + translate[0] + 'px,' + translate[1] + 'px) scale(0.8)';
- }
- else {
- this.style.transform = 'scale(0.8)';
- }
- this.style.transitionProperty = 'opacity,transform';
- this.style.opacity = 0;
- ui.arena.appendChild(this);
- ui.dialogs.unshift(this);
- ui.update();
- ui.refresh(this);
- if (lib.config.remember_dialog && lib.config.dialog_transform && !this.classList.contains('fixed')) {
- this.style.transform = 'translate(' + translate[0] + 'px,' + translate[1] + 'px) scale(1)';
- }
- else {
- this.style.transform = 'scale(1)';
- }
- this.style.opacity = 1;
- var that = this;
- setTimeout(function () {
- that.style.transitionProperty = '';
- }, 500);
- return this;
- }
- close() {
- ui.dialogs.remove(this);
- this.delete();
- if (ui.dialogs.length > 0) {
- ui.dialog = ui.dialogs[0];
- ui.dialog.show();
- ui.dialog.refocus();
- ui.update();
- }
- // if(ui.arenalog){
- // ui.arenalog.classList.remove('withdialog');
- // }
- return this;
- }
- setCaption(str) {
- this.querySelector('.caption').innerHTML = str;
- return this;
- }
- },
- Control: class extends HTMLDivElement {
- constructor() {
- const nc = !ui.control.querySelector('div:not(.removing):not(.stayleft)');
- const controls = Array.isArray(arguments[0]) ? arguments[0] : Array.from(arguments);
- const control = ui.create.div('.control');
- Object.setPrototypeOf(control, lib.element.Control.prototype);
- ui.control.insertBefore(control, _status.createControl || ui.confirm);
- controls.forEach(argument => {
- if (argument == 'nozoom') return;
- if (typeof argument == 'function') control.custom = argument;
- else if (argument == 'stayleft') {
- control.stayleft = true;
- control.classList.add('stayleft');
- }
- else control.add(argument);
- });
- ui.controls.unshift(control);
- if (nc) ui.control.animate('nozoom', 100);
- if (control.childNodes.length) {
- control.style.transition = 'opacity 0.5s';
- control.animate('controlpressdownx', 500);
- ui.refresh(control);
- if (!control.stayleft) control.style.transform = `translateX(-${control.offsetWidth / 2}px)`;
- control.style.opacity = 1;
- ui.refresh(control);
- control.style.transition = '';
- }
-
- control.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.control2);
-
- if (lib.config.button_press) {
- control.addEventListener(lib.config.touchscreen ? 'touchstart' : 'mousedown', function () {
- if (this.classList.contains('disabled')) return;
- this.classList.add('controlpressdown');
- if (typeof this._offset == 'number') this.style.transform = `translateX(${this._offset}px) scale(0.97)`;
- });
- control.addEventListener(lib.config.touchscreen ? 'touchend' : 'mouseup', function () {
- this.classList.remove('controlpressdown');
- if (typeof this._offset == 'number') this.style.transform = `translateX(${this._offset}px)`;
- });
- }
-
- ui.updatec();
- return control;
- }
- open() {
- ui.control.insertBefore(this, _status.createControl || ui.confirm);
- ui.controls.unshift(this);
- if (this.childNodes.length) {
- this.style.transition = 'opacity 0.5s';
- ui.refresh(this);
- this.style.transform = 'translateX(-' + (this.offsetWidth / 2) + 'px)';
- this.style.opacity = 1;
- ui.refresh(this);
- this.style.transition = '';
- }
- else {
- this.animate('controlpressdownx', 500);
- }
- ui.updatec();
- return this;
- }
- add(item) {
- var node = document.createElement('div');
- this.appendChild(node);
- node.link = item;
- node.innerHTML = get.translation(item);
- node.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', ui.click.control);
- }
- close() {
- this.animate('controlpressdownx', 500);
-
- ui.controls.remove(this);
- this.delete();
-
- setTimeout(ui.updatec, 100);
-
-
- if (ui.confirm == this) delete ui.confirm;
- if (ui.skills == this) delete ui.skills;
- if (ui.skills2 == this) delete ui.skills2;
- if (ui.skills3 == this) delete ui.skills3;
- }
- replace() {
- // this.animate('controlpressdownx',500);
- if (this.replaceTransition === false) {
- this.style.transitionProperty = 'none';
- ui.refresh(this);
- }
-
- while (this.childNodes.length) this.firstChild.remove();
- var i, controls;
- if (Array.isArray(arguments[0])) controls = arguments[0];
- else controls = arguments;
- delete this.custom;
- for (i = 0; i < controls.length; i++) {
- if (typeof controls[i] == 'function') {
- this.custom = controls[i];
- }
- else {
- this.add(controls[i]);
- }
- }
- if (this.childNodes.length) {
- var width = 0;
- for (i = 0; i < this.childNodes.length; i++) width += this.childNodes[i].offsetWidth;
- ui.refresh(this);
- this.style.width = width + 'px';
- }
- ui.updatec();
- if (this.replaceTransition === false) {
- var that = this;
- setTimeout(function () {
- that.style.transitionProperty = '';
- }, 200);
- }
- return this;
- }
- },
- Client: class {
- /**
- * @param {NodeWS | InstanceType} ws
- */
- constructor(ws) {
- this.ws = ws;
- this.id = ws.wsid || get.id();
- this.closed = false;
- }
- send() {
- if (this.closed) return this;
- var args = Array.from(arguments);
- if (typeof args[0] == 'function') {
- args.unshift('exec');
- }
- for (var i = 1; i < args.length; i++) {
- args[i] = get.stringifiedResult(args[i]);
- }
- try {
- this.ws.send(JSON.stringify(args));
- }
- catch (e) {
- this.ws.close();
- }
- return this;
- }
- close() {
- lib.node.clients.remove(this);
- lib.node.observing.remove(this);
- if (ui.removeObserve && !lib.node.observing.length) {
- ui.removeObserve.remove();
- delete ui.removeObserve;
- }
- this.closed = true;
- if (_status.waitingForPlayer) {
- for (var i = 0; i < game.connectPlayers.length; i++) {
- if (game.connectPlayers[i].playerid == this.id) {
- game.connectPlayers[i].uninitOL();
- delete game.connectPlayers[i].playerid;
- }
- }
- if (game.onlinezhu == this.id) {
- game.onlinezhu = null;
- }
- game.updateWaiting();
- }
- else if (lib.playerOL[this.id]) {
- var player = lib.playerOL[this.id];
- player.setNickname(player.nickname + ' - 离线');
- game.broadcast(function (player) {
- player.setNickname(player.nickname + ' - 离线');
- }, player);
- player.unwait('ai');
- }
-
- if (window.isNonameServer) {
- document.querySelector('#server_count').innerHTML = lib.node.clients.length;
- }
- return this;
- }
- },
- NodeWS: class {
- /**
- * @param {string} id
- */
- constructor(id) {
- this.wsid = id;
- }
- send(message) {
- game.send('server', 'send', this.wsid, message);
- }
- on(type, func) {
- this['on' + type] = func;
- }
- close() {
- game.send('server', 'close', this.wsid);
- }
- },
+ content: Element.Content,
+ contents: Element.Contents,
+ Player: Element.Player,
+ Card: Element.Card,
+ VCard: Element.VCard,
+ Button: Element.Button,
+ GameEvent: Element.GameEvent,
+ GameEventPromise: Element.GameEventPromise,
+ Dialog: Element.Dialog,
+ Control: Element.Control,
+ Client: Element.Client,
+ NodeWS: Element.NodeWS,
ws: {
onopen: function () {
if (_status.connectCallback) {
@@ -13664,7 +11432,7 @@ export class Library extends Uninstantable {
static message = {
server: {
init: function (version, config, banned_info) {
- if (lib.node.banned.contains(banned_info)) {
+ if (lib.node.banned.includes(banned_info)) {
this.send('denied', 'banned');
}
else if (config.id && lib.playerOL && lib.playerOL[config.id]) {
diff --git a/noname/util/index.js b/noname/util/index.js
index 8d351134f..7d27f1050 100644
--- a/noname/util/index.js
+++ b/noname/util/index.js
@@ -14,8 +14,8 @@ export class Uninstantable {
/**
*
- * @param {number} ms
- * @returns {Promise}
+ * @param { number } ms
+ * @returns { Promise }
*/
export function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));