Change the structure.

This commit is contained in:
Tipx-L 2023-12-05 06:23:52 -08:00
parent 611658114f
commit c53db8f4f5
43 changed files with 1025 additions and 71 deletions

View File

@ -1,4 +0,0 @@
export * from "./noname/ai.js";
export * from "./noname/status.js";
export * from "./noname/library.js";
export * from "./noname/get.js";

View File

@ -1,7 +1,7 @@
import { Get } from "../noname.js"; import { Basic } from "./ai/basic.js";
import { BasicAI } from "./ai/basic.js"; import { Get } from "./get.js";
export const ai = { export const ai = {
basic: BasicAI, basic: Basic,
get: Get get: Get
}; };

View File

@ -1,24 +1,32 @@
import { status as _status } from "../../noname.js"; import { Game } from "../game.js";
import { Get } from "../get.js";
import { status } from "../status.js";
import { Click } from "../ui/click.js";
import { selected } from "../ui/selected.js";
export class Basic {
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
export class BasicAI {
static chooseButton(check) { static chooseButton(check) {
var event = _status.event; var event = status.event;
var i, j, range, buttons, buttons2; var i, j, range, buttons, buttons2;
var ok = false, forced = event.forced; var ok = false, forced = event.forced;
var iwhile = 100; var iwhile = 100;
while (iwhile--) { while (iwhile--) {
range = get.select(event.selectButton); range = Get.select(event.selectButton);
if (ui.selected.buttons.length >= range[0]) { if (selected.buttons.length >= range[0]) {
ok = true; ok = true;
} }
if (range[1] <= -1) { if (range[1] <= -1) {
j = 0; j = 0;
for (i = 0; i < ui.selected.buttons.length; i++) { for (i = 0; i < selected.buttons.length; i++) {
j += check(ui.selected.buttons[i]); j += check(selected.buttons[i]);
} }
return (j > 0); return (j > 0);
} }
buttons = get.selectableButtons(); buttons = Get.selectableButtons();
if (buttons.length == 0) { if (buttons.length == 0) {
return ok; return ok;
} }
@ -41,40 +49,41 @@ export class BasicAI {
} }
} }
buttons[ix].classList.add('selected'); buttons[ix].classList.add('selected');
ui.selected.buttons.add(buttons[ix]); selected.buttons.add(buttons[ix]);
game.check(); Game.check();
if (ui.selected.buttons.length >= range[0]) { if (selected.buttons.length >= range[0]) {
ok = true; ok = true;
} }
if (ui.selected.buttons.length == range[1]) { if (selected.buttons.length == range[1]) {
return true; return true;
} }
} }
} }
static chooseCard(check) { static chooseCard(check) {
var event = _status.event; var event = status.event;
if (event.filterCard == undefined) return (check() > 0); if (event.filterCard == undefined) return (check() > 0);
var i, j, range, cards, cards2, skills, check, effect; var i, j, range, cards, cards2, skills, check, effect;
var ok = false, forced = event.forced; var ok = false, forced = event.forced;
var iwhile = 100; var iwhile = 100;
while (iwhile--) { while (iwhile--) {
range = get.select(event.selectCard); range = Get.select(event.selectCard);
if (ui.selected.cards.length >= range[0]) { if (selected.cards.length >= range[0]) {
ok = true; ok = true;
} }
if (range[1] <= -1) { if (range[1] <= -1) {
if (ui.selected.cards.length == 0) return true; if (selected.cards.length == 0) return true;
j = 0; j = 0;
for (i = 0; i < ui.selected.cards.length; i++) { for (i = 0; i < selected.cards.length; i++) {
effect = check(ui.selected.cards[i]); effect = check(selected.cards[i]);
if (effect < 0) j -= Math.sqrt(-effect); if (effect < 0) j -= Math.sqrt(-effect);
else j += Math.sqrt(effect); else j += Math.sqrt(effect);
} }
return (j > 0); return (j > 0);
} }
cards = get.selectableCards(); cards = Get.selectableCards();
if (!_status.event.player._noSkill) { if (!status.event.player._noSkill) {
cards = cards.concat(get.skills()); cards = cards.concat(Get.skills());
} }
if (cards.length == 0) { if (cards.length == 0) {
return ok; return ok;
@ -98,11 +107,11 @@ export class BasicAI {
} }
} }
if (typeof cards[ix] == 'string') { if (typeof cards[ix] == 'string') {
ui.click.skill(cards[ix]); Click.skill(cards[ix]);
var info = get.info(event.skill); var info = Get.info(event.skill);
if (info.filterCard) { if (info.filterCard) {
check = info.check || get.unuseful2; check = info.check || Get.unuseful2;
return (ai.basic.chooseCard(check)); return (this.chooseCard(check));
} }
else { else {
return true; return true;
@ -110,32 +119,33 @@ export class BasicAI {
} }
else { else {
cards[ix].classList.add('selected'); cards[ix].classList.add('selected');
ui.selected.cards.add(cards[ix]); selected.cards.add(cards[ix]);
game.check(); Game.check();
if (ui.selected.cards.length >= range[0]) { if (selected.cards.length >= range[0]) {
ok = true; ok = true;
} }
if (ui.selected.cards.length == range[1]) { if (selected.cards.length == range[1]) {
return true; return true;
} }
} }
} }
} }
static chooseTarget(check) { static chooseTarget(check) {
var event = _status.event; var event = status.event;
if (event.filterTarget == undefined) return (check() > 0); if (event.filterTarget == undefined) return (check() > 0);
var i, j, range, targets, targets2, effect; var i, j, range, targets, targets2, effect;
var ok = false, forced = event.forced; var ok = false, forced = event.forced;
var iwhile = 100; var iwhile = 100;
while (iwhile--) { while (iwhile--) {
range = get.select(event.selectTarget); range = Get.select(event.selectTarget);
if (ui.selected.targets.length >= range[0]) { if (selected.targets.length >= range[0]) {
ok = true; ok = true;
} }
if (range[1] <= -1) { if (range[1] <= -1) {
j = 0; j = 0;
for (i = 0; i < ui.selected.targets.length; i++) { for (i = 0; i < selected.targets.length; i++) {
effect = check(ui.selected.targets[i]); effect = check(selected.targets[i]);
if (effect < 0) j -= Math.sqrt(-effect); if (effect < 0) j -= Math.sqrt(-effect);
else j += Math.sqrt(effect); else j += Math.sqrt(effect);
} }
@ -144,7 +154,7 @@ export class BasicAI {
else if (range[1] == 0) { else if (range[1] == 0) {
return check() > 0 return check() > 0
} }
targets = get.selectableTargets(); targets = Get.selectableTargets();
if (targets.length == 0) { if (targets.length == 0) {
return range[0] == 0 || ok; return range[0] == 0 || ok;
} }
@ -167,12 +177,12 @@ export class BasicAI {
} }
} }
targets[ix].classList.add('selected'); targets[ix].classList.add('selected');
ui.selected.targets.add(targets[ix]); selected.targets.add(targets[ix]);
game.check(); Game.check();
if (ui.selected.targets.length >= range[0]) { if (selected.targets.length >= range[0]) {
ok = true; ok = true;
} }
if (ui.selected.targets.length == range[1]) { if (selected.targets.length == range[1]) {
return true; return true;
} }
} }

5
noname/game.js Normal file
View File

@ -0,0 +1,5 @@
export class Game {
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
}

View File

@ -1,6 +1,10 @@
import { Library as lib, status as _status } from "../noname.js"; import { status as _status } from "./status";
export class Get { export class Get {
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
/** /**
* @template T * @template T
* @overload * @overload
@ -14,4 +18,102 @@ export class Get {
static event(key) { static event(key) {
return key ? _status.event[key] : _status.event; return key ? _status.event[key] : _status.event;
} }
/**
* @returns {string}
*/
static translation(str, arg) {
if (str && typeof str == "object" && (str.name || str._tempTranslate)) {
if (str._tempTranslate) return str._tempTranslate;
var str2;
if (arg == "viewAs" && str.viewAs) {
str2 = get.translation(str.viewAs);
}
else {
str2 = get.translation(str.name);
}
if (str2 == "杀") {
str2 = "";
if (typeof str.nature == "string") {
let natures = str.nature.split(lib.natureSeparator).sort(lib.sort.nature);
for (let nature of natures) {
str2 += lib.translate["nature_" + nature] || lib.translate[nature] || "";
}
}
str2 += "杀";
}
if (get.itemtype(str) == "card" || str.isCard) {
if (_status.cardtag && str.cardid) {
var tagstr = "";
for (var i in _status.cardtag) {
if (_status.cardtag[i].contains(str.cardid)) {
tagstr += lib.translate[i + "_tag"];
}
}
if (tagstr) {
str2 += "·" + tagstr;
}
}
if (str.suit && str.number || str.isCard) {
var cardnum = get.number(str, false) || "";
if ([1, 11, 12, 13].contains(cardnum)) {
cardnum = { "1": "A", "11": "J", "12": "Q", "13": "K" }[cardnum]
}
if (arg == "viewAs" && str.viewAs != str.name && str.viewAs) {
str2 += "" + get.translation(str) + "";
}
else {
str2 += "【" + get.translation(get.suit(str, false)) + cardnum + "】";
}
}
}
return str2;
}
if (Array.isArray(str)) {
var str2 = get.translation(str[0], arg);
for (var i = 1; i < str.length; i++) {
str2 += "、" + get.translation(str[i], arg);
}
return str2;
}
if (get.itemtype(str) == "natures") {
let natures = str.split(lib.natureSeparator).sort(lib.sort.nature);
var str2 = "";
for (var nature of natures) {
str2 += lib.translate["nature_" + nature] || lib.translate[nature] || "";
}
return str2;
}
if (arg == "skill") {
if (lib.translate[str + "_ab"]) return lib.translate[str + "_ab"];
if (lib.translate[str]) return lib.translate[str].slice(0, 2);
return str;
}
else if (arg == "info") {
if (lib.translate[str + "_info"]) return lib.translate[str + "_info"];
var str2 = str.slice(0, str.length - 1);
if (lib.translate[str2 + "_info"]) return lib.translate[str2 + "_info"];
if (str.lastIndexOf("_") > 0) {
str2 = str.slice(0, str.lastIndexOf("_"));
if (lib.translate[str2 + "_info"]) return lib.translate[str2 + "_info"];
}
str2 = str.slice(0, str.length - 2);
if (lib.translate[str2 + "_info"]) return lib.translate[str2 + "_info"];
if (lib.skill[str] && lib.skill[str].prompt) return lib.skill[str].prompt;
}
if (lib.translate[str]) {
return lib.translate[str];
}
if (typeof str == "string") {
if (lib.translate["nature_" + str]) return lib.translate["nature_" + str];
return str;
}
if (typeof str == "number" || typeof str == "boolean") {
return str.toString();
}
if (str && str.toString) {
return str.toString();
}
return "";
}
} }

View File

@ -1,20 +1,31 @@
import { animate } from "./library/animate.js"; import { animate } from "./library/animate.js";
import { announce } from "./library/announce.js";
import { cardPack } from "./library/card-pack.js"; import { cardPack } from "./library/card-pack.js";
import { cardType } from "./library/card-type.js"; import { cardType } from "./library/card-type.js";
import { Channel } from "./library/channel.js";
import { CharacterDialogGroup } from "./library/character-dialog-group.js";
import { characterFilter } from "./library/character-filter.js"; import { characterFilter } from "./library/character-filter.js";
import { characterIntro } from "./library/character-intro.js"; import { characterIntro } from "./library/character-intro.js";
import { characterPack } from "./library/character-pack.js"; import { characterPack } from "./library/character-pack.js";
import { characterReplace } from "./library/character-replace.js"; import { characterReplace } from "./library/character-replace.js";
import { characterSort } from "./library/character-sort.js"; import { characterSort } from "./library/character-sort.js";
import { characterTitle } from "./library/character-title.js"; import { characterTitle } from "./library/character-title.js";
import { configMenu } from "./library/config-menu.js";
import { dynamicTranslate } from "./library/dynamic-translate.js"; import { dynamicTranslate } from "./library/dynamic-translate.js";
import { element } from "./library/element.js"; import { element } from "./library/element.js";
import { emotionList } from "./library/emotion-list.js"; import { emotionList } from "./library/emotion-list.js";
import { extensionPack } from "./library/extension-pack.js"; import { extensionPack } from "./library/extension-pack.js";
import { hookMap } from "./library/hook-map.js";
import { hook } from "./library/hook.js";
import { hooks } from "./library/hooks.js";
import { imported } from "./library/imported.js";
import { pinyins } from "./library/pinyins.js";
import { skin } from "./library/skin.js"; import { skin } from "./library/skin.js";
import { stratagemBuff } from "./library/stratagem-buff.js";
import { updateURLs } from "./library/update-urls.js"; import { updateURLs } from "./library/update-urls.js";
import { yingbian } from "./library/yingbian.js";
const nonameInitialized = localStorage.getItem('noname_inited'); const nonameInitialized = localStorage.getItem("noname_inited");
export class Library { export class Library {
static configprefix = "noname_0.9_"; static configprefix = "noname_0.9_";
@ -23,9 +34,9 @@ export class Library {
static updateURL = updateURLs.github; static updateURL = updateURLs.github;
static mirrorURL = updateURLs.coding; static mirrorURL = updateURLs.coding;
static hallURL = "47.99.105.222"; static hallURL = "47.99.105.222";
static assetURL = typeof nonameInitialized != 'string' || nonameInitialized == 'nodejs' ? '' : nonameInitialized; static assetURL = typeof nonameInitialized != "string" || nonameInitialized == "nodejs" ? "" : nonameInitialized;
static userAgent = navigator.userAgent.toLowerCase(); static userAgent = navigator.userAgent.toLowerCase();
static compatibleEdition = Boolean(typeof nonameInitialized == 'string' && nonameInitialized.match(/\/(?:com\.widget|yuri\.nakamura)\.noname\//)); static compatibleEdition = Boolean(typeof nonameInitialized == "string" && nonameInitialized.match(/\/(?:com\.widget|yuri\.nakamura)\.noname\//));
static changeLog = []; static changeLog = [];
static updates = []; static updates = [];
static canvasUpdates = []; static canvasUpdates = [];
@ -61,9 +72,53 @@ export class Library {
static extensions = []; static extensions = [];
static extensionPack = extensionPack; static extensionPack = extensionPack;
static cardType = cardType; static cardType = cardType;
static hook = hook;
static hooks = hooks;
static element = element; static element = element;
static Channel = Channel;
/**
* @todo Waiting for [Rintim](https://github.com/Rintim)s pull request.
*/
static announce = announce;
/**
* @type {Map<string, string>}
*/
static objectURL = new Map();
static hookmap = hookMap;
static imported = imported;
static layoutfixed = ["chess", "tafang", "stone"];
static pinyins = pinyins;
static yingbian = yingbian;
static stratagemBuff = stratagemBuff;
/**
* The actual card name
*
* 实际的卡牌名称
*/
static actualCardName = new Map([
["挟令", "挟天子以令诸侯"],
["霹雳投石车", "霹雳车"]
])
static characterDialogGroup = CharacterDialogGroup;
static configMenu = configMenu;
constructor() { constructor() {
throw new TypeError(`${new.target.name} is not a constructor`); throw new TypeError(`${new.target.name} is not a constructor`);
} }
static listenEnd(node) {
if (!node._listeningEnd) {
node._listeningEnd = true;
node.listenTransition(function () {
delete node._listeningEnd;
if (node._onEndMoveDelete) {
node.moveDelete(node._onEndMoveDelete);
}
else if (node._onEndDelete) {
node.delete();
}
node._transitionEnded = true;
});
}
}
} }

View File

@ -1,7 +1,7 @@
import { animateCard } from "./animate/card.js"; import { card } from "./animate/card.js";
import { animateSkill } from "./animate/skill.js"; import { skill } from "./animate/skill.js";
export const animate = { export const animate = {
skill: animateSkill, skill,
card: animateCard card
}; };

View File

@ -1,3 +1,3 @@
interface AnimateCard extends Record<string, Function> { } interface Card extends Record<string, Function> { }
export const animateCard: AnimateCard; export const card: Card;

View File

@ -1 +1 @@
export const animateCard = {}; export const card = {};

View File

@ -1,3 +1,3 @@
interface AnimateSkill extends Record<string, Function> { } interface Skill extends Record<string, Function> { }
export const animateSkill: AnimateSkill; export const skill: Skill;

View File

@ -1 +1 @@
export const animateSkill = {}; export const skill = {};

101
noname/library/announce.js Normal file
View File

@ -0,0 +1,101 @@
/**
* **无名杀消息推送库**
*
* 通过`EventTarget`机制实现消息推送和接收的解耦
* 从而使消息接收方无需依赖发布方发布方也无需考虑接收方
*
* > `lib.announce`不是`actor`模型若不存在订阅者则消息发送将无意义
*
* @example
* // 甲扩展(如《千幻聆音》)在角色皮肤切换后,调用:
* lib.announce.publish("skinChange", {
* player,
* playerName: "zhangfei",
* originSkin: "image/xxx.jpg",
* currentSkin: "image/yyy.jpg"
* });
*
* // 乙扩展监听此`skinChange`事件,并修改自己扩展相关界面的图片:
* const method = lib.announce.subscribe("skinChange", (e) => {
* div.setBackgroundImage(e.currentSkin);
* });
*
* // 若此时乙扩展不想继续订阅`skinChange`事件,可以通过`unsubscribe`解除订阅
* lib.announce.unsubscribe("skinChange", method);
*/
export const announce = {
_announce: document.createElement("Announce"),
/**
* @type {Map<(values: T) => void, Map<string, (event: Event) => void>>}
*/
_announce_cache: new Map(),
/**
* 推送任意数据给所有监听了指定事件的订阅者并返回给定的数据
*
* 若不存在订阅指定事件的订阅者则推送的数据将无意义
*
* @template T
* @param {string} name - 要推送事件的名称
* @param {T} values - 要推送的数据
* @returns {T}
*/
publish(name, values) {
if (this._announce) this._announce.dispatchEvent(new CustomEvent(name, {
detail: values
}));
return values;
},
/**
* 订阅给定名字的事件并返回给定的函数
*
* 在事件触发时执行给定的函数
*
* 给定的函数将被存储至当前实例中用于取消订阅时获取
*
* @template T
* @param {string} name - 要订阅事件的名称
* @param {(values: T) => void} method - 事件触发时执行的函数
* @returns {(values: T) => void}
*/
subscribe(name, method) {
if (this._announce && this._announce_cache) {
let subscribeFunction;
if (this._announce_cache.has(method)) {
let records = this._announce_cache.get(method);
subscribeFunction = records.get("Listener");
records.get("EventTargets").add(name);
}
else {
subscribeFunction = event => method(event.detail);
let records = new Map();
records.set("Listener", subscribeFunction);
records.set("EventTargets", [name]);
this._announce_cache.set(method, records);
}
this._announce.addEventListener(name, subscribeFunction);
}
return method;
},
/**
* 取消指定事件某一函数的订阅并返回该函数
*
* 给定的函数将不再于事件触发时执行其余同事件需触发的函数不受限制
*
* @template T
* @param {string} name - 要取消订阅事件的名称
* @param {(values: T) => void} method - 订阅指定事件的函数
* @returns {(values: T) => void}
*/
unsubscribe(name, method) {
if (this._announce && this._announce_cache && this._announce_cache.has(method)) {
let records = this._announce_cache.get(method);
const listener = records.get("Listener");
let eventTargets = records.get("EventTargets");
eventTargets.remove(name);
if (eventTargets.length <= 0) this._announce_cache.remove(method);
this._announce.removeEventListener(name, listener);
}
return method;
}
};

98
noname/library/channel.js Normal file
View File

@ -0,0 +1,98 @@
/**
* **无名杀频道推送机制**
*
* 鉴于`Javascript`的特性及自身对所需功能的思考这是一个参考`Golang``channel`设计的完全和`go channel`不一样的异步消息传递对象
*
* 当且仅当接收方和发送方均存在时进行消息传递完全保证信息传递的单一性发送方/接收方一旦确定则无法更改和准确性发送方必然将消息发送给接收方
*
* 若存在发送方/接收方时调用`send`/`receive`将报错
*
* 若需要异步/不报错发送信息请等待`lib.actor`
*
* @template T
* @example
* // 创建一个频道
* const channel = new lib.channel();
*
* // 从某个角落接收channel发出的消息若无消息则等待
* const message = await channel.receive();
*
* // 从某个角落向channel发消息若无消息接收则等待
* await channel.send(item);
*/
export class Channel {
constructor() {
/**
* @type {"active" | "receiving" | "sending"}
*/
this.status = "active";
/**
* @type {import("./promise-resolve").PromiseResolve<T> | [T, import("./promise-resolve").PromiseResolve<void>] | null}
*/
this._buffer = null;
}
/**
* 向该频道发送消息在消息未被接受前将等待
*
* @param {T} value - 要发送的消息
* @returns {Promise<void>}
*/
send(value) {
return new Promise((resolve, reject) => {
switch (this.status) {
case "sending":
// TODO: handle the error.
reject(new Error());
break;
case "receiving": {
/**
* @type {import("./promise-resolve").PromiseResolve<T>}
*/
const buffer = this._buffer;
this._buffer = null;
buffer(value);
this.status = "active";
resolve();
break;
}
case "active":
this.status = "sending";
this._buffer = [value, resolve];
break;
}
});
}
/**
* 接收频道所发送的消息若无消息发送则等待
*
* @returns {Promise<T>} 接收到的消息
*/
receive() {
return new Promise((resolve, reject) => {
switch (this.status) {
case "receiving":
// TODO: handle the error.
reject(new Error());
break;
case "sending": {
/**
* @type {[T, import("./promise-resolve").PromiseResolve<void>]}
*/
const buffer = this._buffer;
this._buffer = null;
resolve(buffer[0]);
this.status = "active";
buffer[1]();
break;
}
case "active":
this.status = "receiving";
this._buffer = resolve;
break;
}
});
}
}

View File

@ -0,0 +1,17 @@
import { Get } from "../get.js";
import { config } from "./config.js";
export class CharacterDialogGroup {
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
static 收藏(name, capt) {
return config.favouriteCharacter.includes(name) ? capt : null;
}
static 最近(name, capt) {
var list = Get.config("recentCharacter") || [];
return list.includes(name) ? capt : null;
}
}

View File

@ -0,0 +1,5 @@
import { general } from "./config-menu/general.js";
export const configMenu = {
general
};

View File

@ -0,0 +1,6 @@
import { config } from "./general/config.js";
export const general = {
name: "通用",
config: config
};

View File

@ -0,0 +1,7 @@
import { lowPerformance } from "./config/low-performance.js";
import { mountCombine } from "./config/mount-combine.js";
export const config = {
mount_combine: mountCombine,
low_performance: lowPerformance
};

View File

@ -0,0 +1,17 @@
import { Game } from "../../../../game.js";
import { UI } from "../../../../ui.js";
export const lowPerformance = {
name: "流畅模式",
init: false,
intro: "减少部分游戏特效,提高游戏速度",
onclick(bool) {
Game.saveConfig("low_performance", bool);
if (bool) {
UI.window.classList.add("low_performance");
}
else {
UI.window.classList.remove("low_performance");
}
}
};

View File

@ -0,0 +1,9 @@
const listItem = document.createElement("li");
listItem.textContent = "将进攻坐骑栏和防御坐骑栏合并为同一个位置(重启后生效)。";
export const mountCombine = {
name: "合并坐骑栏",
init: false,
intro: listItem.outerHTML,
restart: true
};

5
noname/library/config.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
interface Config extends Record<string, any> {
favouriteCharacter: string[];
}
export const config: Config;

1
noname/library/config.js Normal file
View File

@ -0,0 +1 @@
export const config = {};

View File

@ -2,6 +2,6 @@ import { GameEvent } from "./element/game-event.js";
import { Player } from "./element/player.js"; import { Player } from "./element/player.js";
export const element = { export const element = {
Player: Player, Player,
GameEvent: GameEvent GameEvent
}; };

View File

@ -1,4 +1,5 @@
import { Get as get } from "../../../noname.js"; import { Game } from "../../game.js";
import { Get } from "../../get.js";
export class GameEvent { export class GameEvent {
/** /**
@ -8,24 +9,24 @@ export class GameEvent {
constructor(name, trigger) { constructor(name, trigger) {
if (typeof name == 'string') { if (typeof name == 'string') {
this.name = name; this.name = name;
const gameEvent = get.event(); const gameEvent = Get.event();
if (gameEvent) { if (gameEvent) {
const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`; const type = `onNext${name[0].toUpperCase()}${name.slice(1)}`;
if (gameEvent.hasHandler(type)) this.pushHandler(...gameEvent.getHandler(type)); if (gameEvent.hasHandler(type)) this.pushHandler(...gameEvent.getHandler(type));
} }
game.globalEventHandlers.addHandlerToEvent(this); Game.globalEventHandlers.addHandlerToEvent(this);
} }
this.step = 0; this.step = 0;
this.finished = false; this.finished = false;
/** /**
* @type {GameEvent[]} * @type {this[]}
*/ */
this.next = []; this.next = [];
/** /**
* @type {GameEvent[]} * @type {this[]}
*/ */
this.after = []; this.after = [];
this.custom = { this.custom = {
@ -36,7 +37,7 @@ export class GameEvent {
this._notrigger = []; this._notrigger = [];
this._result = {}; this._result = {};
this._set = []; this._set = [];
if (trigger !== false && !game.online) this._triggered = 0; if (trigger !== false && !Game.online) this._triggered = 0;
} }
static initialGameEvent() { static initialGameEvent() {

View File

@ -0,0 +1 @@
export class VCard { }

3
noname/library/handler-option.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export interface HandlerOption extends Record<string, unknown> {
state: "begin" | "end";
}

3
noname/library/hook-map.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
interface HookMap extends Record<string, true> { }
export const hookMap: HookMap;

View File

@ -0,0 +1 @@
export const hookMap = {};

5
noname/library/hook.js Normal file
View File

@ -0,0 +1,5 @@
import { globalSkill } from "./hook/global-skill.js";
export const hook = {
globalskill: globalSkill
};

View File

@ -0,0 +1 @@
export const globalSkill = {};

190
noname/library/hooks.js Normal file
View File

@ -0,0 +1,190 @@
import { Game } from "../game.js";
import { Library } from "../library.js";
/**
* 函数钩子
*/
export const hooks = {
/**
* 本体势力的颜色
*/
addGroup: [(id, _short, _name, config) => {
if ("color" in config && config.color != null) {
let color1, color2, color3, color4;
if (typeof config.color == "string" && /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(config.color)) {
let c1 = parseInt(`0x${config.color.slice(1, 3)}`);
let c2 = parseInt(`0x${config.color.slice(3, 5)}`);
let c3 = parseInt(`0x${config.color.slice(5, 7)}`);
color1 = color2 = color3 = color4 = [c1, c2, c3, 1];
}
else if (Array.isArray(config.color) && config.color.length == 4) {
if (config.color.every(item => Array.isArray(item))) {
color1 = config.color[0];
color2 = config.color[1];
color3 = config.color[2];
color4 = config.color[3];
}
else color1 = color2 = color3 = color4 = config.color;
}
if (color1 && color2 && color3 && color4) {
const cs = Library.linq.cselector;
const g1 = cs.group(
cs.of(
cs.class("player", "identity"),
cs.isAttr("data-color", `"${id}"`)
),
cs.of(
"div",
cs.isAttr("data-nature", `"${id}"`)
),
cs.of(
"span",
cs.isAttr("data-nature", `"${id}"`)
)
);
const g2 = cs.group(
cs.of(
"div",
cs.isAttr("data-nature", `"${id}m"`)
),
cs.of(
"span",
cs.isAttr("data-nature", `"${id}m"`)
)
);
const g3 = cs.group(
cs.of(
"div",
cs.isAttr("data-nature", `"${id}mm"`)
),
cs.of(
"span",
cs.isAttr("data-nature", `"${id}mm"`)
)
);
let result = {};
result[g1] = {
textShadow: cs.group(
"black 0 0 1px",
`rgba(${color1.join()}) 0 0 2px`,
`rgba(${color2.join()}) 0 0 5px`,
`rgba(${color3.join()}) 0 0 10px`,
`rgba(${color4.join()}) 0 0 10px`
)
};
result[g2] = {
textShadow: cs.group(
"black 0 0 1px",
`rgba(${color1.join()}) 0 0 2px`,
`rgba(${color2.join()}) 0 0 5px`,
`rgba(${color3.join()}) 0 0 5px`,
`rgba(${color4.join()}) 0 0 5px`,
"black 0 0 1px"
)
};
result[g3] = {
textShadow: cs.group(
"black 0 0 1px",
`rgba(${color1.join()}) 0 0 2px`,
`rgba(${color2.join()}) 0 0 2px`,
`rgba(${color3.join()}) 0 0 2px`,
`rgba(${color4.join()}) 0 0 2px`,
"black 0 0 1px"
)
};
Game.dynamicStyle.addObject(result);
Library.groupnature[id] = id;
}
}
if (typeof config.image == "string") Object.defineProperty(Library.card, `group_${id}`, {
configurable: true,
enumerable: false,
writable: true,
value: {
fullskin: true,
image: config.image
}
});
}],
/**
* 增加新属性杀
*/
addNature: [(nature, _translation, config) => {
if (typeof config != "object") config = {};
let linked = config.linked, order = config.order, background = config.background, lineColor = config.lineColor;
if (typeof linked != "boolean") linked = true;
if (typeof order != "number") order = 0;
if (typeof background != "string") background = "";
if (!Array.isArray(lineColor) || lineColor.length != 3) lineColor = [];
else if (background.startsWith("ext:")) {
background = background.replace(/^ext:/, "extension/");
}
if (linked) Library.linked.add(nature);
if (lineColor.length) Library.lineColor.set(nature, lineColor);
Library.nature.set(nature, order);
if (background.length > 0) Library.natureBg.set(nature, background);
if (config.audio) {
for (let key in config.audio) {
if (!Library.natureAudio[key]) {
Library.natureAudio[key] = config.audio[key];
} else {
for (let key2 in config.audio[key]) {
Library.natureAudio[key][key2] = config.audio[key][key2];
}
}
}
}
let color1, color2;
if (typeof config.color == "string" && /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(config.color)) {
let c1 = parseInt(`0x${item[1].slice(1, 3)}`);
let c2 = parseInt(`0x${item[1].slice(3, 5)}`);
let c3 = parseInt(`0x${item[1].slice(5, 7)}`);
color1 = color2 = [c1, c2, c3, 1];
}
else if (Array.isArray(config.color) && config.color.length >= 2 && config.color.length <= 4) {
if (config.color.every(item => Array.isArray(item))) {
color1 = config.color[0];
color2 = config.color[1];
}
else {
let color = config.color.slice();
if (color.length == 3) color.push(1);
color1 = color2 = color;
}
}
if (color1 && color2) {
const cs = Library.linq.cselector;
const g1 = cs.group(
cs.of(
cs.class("card", "fullskin", `${nature}`),
">",
cs.class("name")
)
);
let result = {};
result[g1] = {
color: `rgba(${color1.join()})`,
border: cs.merge(
"1px",
"solid",
`rgba(${color2.join()})`
),
};
Game.dynamicStyle.addObject(result);
const g2 = cs.group(
cs.of(
cs.class("tempname", `${nature}`),
":not([data-nature])>",
cs.class("span")
)
)
let result2 = {};
result2[g2] = {
color: `rgba(${color1.join()})`,
};
Game.dynamicStyle.addObject(result2);
}
}]
};

View File

@ -0,0 +1 @@
export const imported = {};

View File

@ -0,0 +1,5 @@
import { pinyinsMetadata } from "./pinyins/metadata.js";
export const pinyins = {
_metadata: pinyinsMetadata
};

View File

@ -0,0 +1,10 @@
import { nonMedial } from "./metadata/non-medial.js";
import { rhyme } from "./metadata/rhyme.js";
export const pinyinsMetadata = {
shengmu: ["zh", "ch", "sh", "b", "p", "m", "f", "d", "t", "l", "n", "g", "k", "h", "j", "q", "x", "r", "z", "c", "s", "y", "w"],
special_shengmu: ["j", "q", "x", "y"],
feijiemu: nonMedial,
zhengtirendu: ["zhi", "chi", "shi", "ri", "zi", "ci", "si"],
yunjiao: rhyme
};

View File

@ -0,0 +1,5 @@
export const nonMedial = {
i: ["ing", "iu", "ie", "in"],
u: ["ui", "un"],
ü: ["üe", "ün"],
};

View File

@ -0,0 +1,16 @@
export const rhyme = {
一麻: ["a", "ia", "ua"],
二波: ["o", "e", "uo"],
三皆: ["ie", "üe"],
四开: ["ai", "uai"],
五微: ["ei", "ui"],
六豪: ["ao", "iao"],
七尤: ["ou", "iu"],
八寒: ["an", "ian", "uan", "üan"],
九文: ["en", "in", "un", "ün"],
十唐: ["ang", "iang", "uang"],
十一庚: ["eng", "ing", "ong", "ung"],
十二齐: ["i", "er", "ü"],
十三支: ["-i"],
十四姑: ["u"]
};

1
noname/library/promise-resolve.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export type PromiseResolve<T> = (value: T) => void;

View File

@ -0,0 +1,72 @@
import { Game } from "../game.js";
import { Get } from "../get.js";
import { GameEvent } from "./element/game-event.js";
import { VCard } from "./element/v-card.js";
export const stratagemBuff = {
cost: new Map([
["sha", 1],
["shan", 1],
["juedou", 2],
["huogong", 2],
["tao", 3]
]),
/**
* @type {Map<string, (event: GameEvent, option: import("./handler-option").HandlerOption) => void>}
*/
effect: new Map([
["sha", (event, option) => {
if (event.step != 0 || option.state != "end") return;
Game.log(event.player, "触发了强化效果");
Game.log(event.card, "抵消所需要的", new VCard({
name: "shan"
}), "数+1");
const map = event.customArgs;
Game.players.concat(Game.dead).forEach(current => {
const id = current.playerid;
if (!map[id]) map[id] = {};
if (typeof map[id].shanRequired == "number") map[id].shanRequired++;
else map[id].shanRequired = 2;
});
}],
["shan", (event, option) => {
if (event.step != 0 || option.state != "end") return;
Game.log(event.player, "触发了强化效果");
Game.log("使用", event.card, "时视为两张", new VCard({
name: "shan"
}), "的效果");
event.player.when("useCard").filter(evt => evt == event).then(() => {
trigger.getParent(2).decrease("shanRequired", 1);
});
}],
["juedou", (event, option) => {
if (event.step != 0 || option.state != "end") return;
Game.log(event.player, "触发了强化效果");
Game.log("对", event.card, "的目标造成伤害时,伤害+1");
event.player.when({
source: "damageBegin1"
}).filter(evt => evt.getParent(2) == event && event.targets.includes(evt.player)).then(() => {
trigger.increase("num");
});
}],
["huogong", (event, option) => {
if (event.step != 0 || option.state != "end") return;
Game.log(event.player, "触发了强化效果");
Game.log(event.card, "造成的伤害+1");
event.increase("baseDamage", 1);
}],
["tao", (event, option) => {
if (event.step != 0 || option.state != "end") return;
Game.log(event.player, "触发了强化效果");
Game.log(event.card, "回复的体力+1");
event.increase("baseDamage", 1);
}]
]),
prompt: new Map([
["sha", () => `抵消所需要的【${Get.translation("shan")}】数+1。`],
["shan", () => `使用时视为两张【${Get.translation("shan")}】的效果。`],
["juedou", () => "对此牌的目标造成伤害时,伤害+1。"],
["huogong", () => "造成的伤害+1。"],
["tao", () => "回复的体力+1。"]
])
};

View File

@ -0,0 +1,42 @@
import { condition } from "./yingbian/condition.js";
export const yingbian = {
condition,
effect: new Map([
["add", () => {
trigger.yingbian_addTarget = true;
}],
["remove", () => {
trigger.yingbian_removeTarget = true;
}],
["damage", () => {
if (typeof trigger.baseDamage != "number") trigger.baseDamage = 1;
trigger.baseDamage++;
game.log(card, "的伤害值基数+1");
}],
["draw", () => {
player.draw();
}],
["gain", () => {
const cardx = trigger.respondTo;
if (cardx && cardx[1] && cardx[1].cards && cardx[1].cards.filterInD("od").length) player.gain(cardx[1].cards.filterInD("od"), "gain2");
}],
["hit", () => {
trigger.directHit.addArray(game.players).addArray(game.dead);
game.log(card, "不可被响应");
}],
["all", () => {
card.yingbian_all = true;
game.log(card, "执行所有选项");
}]
]),
prompt: new Map([
["add", "目标+1"],
["remove", "目标-1"],
["damage", "伤害+1"],
["draw", "摸一张牌"],
["gain", "获得响应的牌"],
["hit", "此牌不可被响应"],
["all", "无视条件执行所有选项"]
])
};

View File

@ -0,0 +1,132 @@
export const condition = {
color: new Map([
["zhuzhan", "wood"],
["kongchao", "soil"],
["fujia", "orange"],
["canqu", "fire"],
["force", "metal"]
]),
complex: new Map([
["zhuzhan", function (event) {
const yingbianZhuzhan = game.createEvent("yingbianZhuzhan");
yingbianZhuzhan.player = event.player;
yingbianZhuzhan.card = event.card;
yingbianZhuzhan._trigger = event;
yingbianZhuzhan.yingbianZhuzhanAI = event.yingbianZhuzhanAI;
yingbianZhuzhan.afterYingbianZhuzhan = event.afterYingbianZhuzhan;
yingbianZhuzhan.setContent(() => {
"step 0"
event._global_waiting = true;
event.send = (player, card, source, targets, id, id2, yingbianZhuzhanAI, skillState) => {
if (skillState) player.applySkills(skillState);
var type = get.type2(card), str = get.translation(source);
if (targets && targets.length) str += `${get.translation(targets)}`;
str += `使用了${get.translation(card)},是否弃置一张${get.translation(type)}为其助战?`;
player.chooseCard({
filterCard: (card, player) => get.type2(card) == type && lib.filter.cardDiscardable(card, player),
prompt: str,
position: "h",
_global_waiting: true,
id: id,
id2: id2,
ai: typeof yingbianZhuzhanAI == "function" ? yingbianZhuzhanAI(player, card, source, targets) : cardx => {
var info = get.info(card);
if (info && info.ai && info.ai.yingbian) {
var ai = info.ai.yingbian(card, source, targets, player);
if (!ai) return 0;
return ai - get.value(cardx);
}
else if (get.attitude(player, source) <= 0) return 0;
return 5 - get.value(cardx);
}
});
if (!game.online) return;
_status.event._resultid = id;
game.resume();
};
"step 1"
var type = get.type2(card);
event.list = game.filterPlayer(current => current != player && current.countCards("h") && (_status.connectMode || current.hasCard(cardx => get.type2(cardx) == type, "h"))).sortBySeat(_status.currentPhase || player);
event.id = get.id();
"step 2"
if (!event.list.length) event.finish();
else if (_status.connectMode && (event.list[0].isOnline() || event.list[0] == game.me)) event.goto(4);
else event.send(event.current = event.list.shift(), event.card, player, trigger.targets, event.id, trigger.parent.id, trigger.yingbianZhuzhanAI);
"step 3"
if (result.bool) {
event.zhuzhanresult = event.current;
event.zhuzhanresult2 = result;
if (event.current != game.me) game.delayx();
event.goto(8);
}
else event.goto(2);
"step 4"
var id = event.id, sendback = (result, player) => {
if (result && result.id == id && !event.zhuzhanresult && result.bool) {
event.zhuzhanresult = player;
event.zhuzhanresult2 = result;
game.broadcast("cancel", id);
if (_status.event.id == id && _status.event.name == "chooseCard" && _status.paused) return () => {
event.resultOL = _status.event.resultOL;
ui.click.cancel();
if (ui.confirm) ui.confirm.close();
};
}
else if (_status.event.id == id && _status.event.name == "chooseCard" && _status.paused) return () => event.resultOL = _status.event.resultOL;
}, withme = false, withol = false, list = event.list;
for (var i = 0; i < list.length; i++) {
var current = list[i];
if (current.isOnline()) {
withol = true;
current.wait(sendback);
current.send(event.send, current, event.card, player, trigger.targets, event.id, trigger.parent.id, trigger.yingbianZhuzhanAI, get.skillState(current));
list.splice(i--, 1);
}
else if (current == game.me) {
withme = true;
event.send(current, event.card, player, trigger.targets, event.id, trigger.parent.id, trigger.yingbianZhuzhanAI);
list.splice(i--, 1);
}
}
if (!withme) event.goto(6);
if (_status.connectMode && (withme || withol)) game.players.forEach(value => {
if (value != player) value.showTimer();
});
event.withol = withol;
"step 5"
if (!result || !result.bool || event.zhuzhanresult) return;
game.broadcast("cancel", event.id);
event.zhuzhanresult = game.me;
event.zhuzhanresult2 = result;
"step 6"
if (event.withol && !event.resultOL) game.pause();
"step 7"
game.players.forEach(value => value.hideTimer());
"step 8"
if (event.zhuzhanresult) {
var target = event.zhuzhanresult;
target.line(player, "green");
target.discard(event.zhuzhanresult2.cards).discarder = target;
if (typeof event.afterYingbianZhuzhan == "function") event.afterYingbianZhuzhan(event, trigger);
var yingbianCondition = event.name.slice(8).toLowerCase(), yingbianConditionTag = `yingbian_${yingbianCondition}_tag`;
target.popup(yingbianConditionTag, lib.yingbian.condition.color.get(yingbianCondition));
game.log(target, "响应了", player, "发起的", yingbianConditionTag);
target.addExpose(0.2);
event.result = {
bool: true
}
}
else event.result = {
bool: false
};
});
yingbianZhuzhan._args = Array.from(arguments);
return yingbianZhuzhan;
}]
]),
simple: new Map([
["kongchao", event => !event.player.countCards("h")],
["fujia", event => event.player.isMaxHandcard()],
["canqu", event => event.player.getHp() == 1]
])
};

View File

@ -1,4 +1,4 @@
import { Library as lib } from "../noname.js"; import { GameEvent } from "./library/element/game-event.js";
import { aiStatus } from "./status/ai.js"; import { aiStatus } from "./status/ai.js";
import { cardTag } from "./status/card-tag.js"; import { cardTag } from "./status/card-tag.js";
import { postReconnect } from "./status/post-reconnect.js"; import { postReconnect } from "./status/post-reconnect.js";
@ -10,7 +10,7 @@ export const status = {
over: false, over: false,
clicked: false, clicked: false,
auto: false, auto: false,
event: lib.element.GameEvent.initialGameEvent(), event: GameEvent.initialGameEvent(),
ai: aiStatus, ai: aiStatus,
lastdragchange: [], lastdragchange: [],
skillaudio: [], skillaudio: [],
@ -30,5 +30,5 @@ export const status = {
cardtag: cardTag, cardtag: cardTag,
renku: [], renku: [],
prehidden_skills: [], prehidden_skills: [],
postReconnect: postReconnect postReconnect
} }

21
noname/ui.js Normal file
View File

@ -0,0 +1,21 @@
import { Click } from "./ui/click.js";
import { selected } from "./ui/selected.js";
class HTMLWindowElement extends HTMLDivElement { }
customElements.define("window", HTMLWindowElement, {
extends: "div"
});
export class UI {
static click = Click;
static selected = selected;
/**
* @type {HTMLWindowElement}
*/
static window;
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
}

5
noname/ui/click.js Normal file
View File

@ -0,0 +1,5 @@
export class Click {
constructor() {
throw new TypeError(`${new.target.name} is not a constructor`);
}
}

5
noname/ui/selected.js Normal file
View File

@ -0,0 +1,5 @@
export const selected = {
buttons: [],
cards: [],
targets: []
};