.
diff --git a/extension.css b/extension.css
new file mode 100644
index 0000000..31d989c
--- /dev/null
+++ b/extension.css
@@ -0,0 +1,494 @@
+.manual {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0,0,0,0.4);
+ z-index: 4;
+}
+
+.manual>.menu {
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 50px;
+ background: rgba(0,0,0,0.6);
+}
+
+.manual>.content {
+ position: absolute;
+ left: 0;
+ top: 50px;
+ width: 100%;
+ height: calc(100% - 50px) !important;
+ background: none;
+ overflow-y: auto;
+}
+
+.manual>.content>.content-container>.content {
+ width: 100%;
+ height: 100%;
+}
+
+.manual>.content>.content-container>.content>.caption {
+ display: inline-block;
+ width: 100%;
+ height: auto;
+ margin: 0;
+}
+
+.manual>.content>.content-container>.content>.caption div {
+ position: static;
+ height: auto;
+}
+
+.manual>.menu>.addNewBoss {
+ position: absolute;
+ left: calc(50% + 110px);
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+
+.manual>.menu>.newBossList {
+ position: absolute;
+ left: calc(5% - 15px);
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+
+.manual>.menu>.deleteNewBossAllList {
+ position: absolute;
+ left: calc(20% - 15px);
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+
+.manual>.menu>.deleteNewBoss {
+ position: absolute;
+ left: calc(50% - 163px);
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+.manual>.menu>.playerList {
+ position: absolute;
+ left: calc(80% - 15px);
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+
+.manual>.menu>.close {
+ position: absolute;
+ right: 5px;
+ top: calc(50% - 15px);
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: red;
+}
+
+.manual>.menu>input {
+ position: absolute;
+ left: calc(50% - 100px);
+ top: calc(50% - 15px);
+ width: 200px;
+ height: 30px;
+ color: white;
+ font-size: 20px;
+ text-align: center;
+ border: 2px solid rgba(255,255,100,0.4);
+ border-radius: 2px;
+ background: transparent;
+}
+
+.shuYing-character-dialog-mask {
+ position: fixed !important;
+ inset: 0;
+ z-index: 99999;
+ display: flex !important;
+ align-items: center;
+ justify-content: center;
+ background: rgba(0, 0, 0, 0.58);
+ font-size: 14px;
+ line-height: normal;
+}
+
+.shuYing-character-dialog-mask *,
+.shuYing-character-dialog-mask *::before,
+.shuYing-character-dialog-mask *::after {
+ box-sizing: border-box;
+ float: none !important;
+ writing-mode: horizontal-tb !important;
+ letter-spacing: 0 !important;
+}
+
+.shuYing-character-dialog {
+ position: relative !important;
+ width: min(680px, 92vw);
+ max-height: 86vh;
+ display: flex !important;
+ flex-direction: column !important;
+ gap: 10px;
+ height: auto !important;
+ padding: 16px 18px 14px;
+ border: 1px solid rgba(246, 211, 101, 0.7);
+ border-radius: 8px;
+ background: rgba(24, 21, 26, 0.96);
+ box-shadow: 0 20px 54px rgba(0, 0, 0, 0.65), inset 0 1px 0 rgba(255, 255, 255, 0.08);
+ color: #f6d365;
+ font-family: "Microsoft YaHei", "SimHei", sans-serif;
+ text-align: left;
+ transform: none;
+ overflow: hidden;
+}
+
+.shuYing-character-dialog>.shuYing-character-dialog-header,
+.shuYing-character-dialog>.shuYing-character-dialog-intro,
+.shuYing-character-dialog>.shuYing-character-dialog-search,
+.shuYing-character-dialog>.shuYing-character-dialog-toolbar,
+.shuYing-character-dialog>.shuYing-character-dialog-grid-wrap,
+.shuYing-character-dialog>.shuYing-character-dialog-controls {
+ position: static !important;
+ left: auto !important;
+ top: auto !important;
+ right: auto !important;
+ bottom: auto !important;
+ transform: none !important;
+}
+
+.shuYing-character-dialog-header {
+ display: flex !important;
+ flex: none !important;
+ flex-direction: row !important;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 16px;
+ min-height: 30px;
+ width: 100%;
+}
+
+.shuYing-character-dialog-title {
+ display: block !important;
+ position: static !important;
+ flex: 1 1 auto !important;
+ width: auto !important;
+ height: auto !important;
+ font-size: 22px;
+ line-height: 30px;
+ font-weight: 600;
+ white-space: nowrap !important;
+ writing-mode: horizontal-tb !important;
+ text-shadow: 0 0 5px #000;
+}
+
+.shuYing-character-dialog-count {
+ display: block !important;
+ position: static !important;
+ flex: none;
+ width: auto !important;
+ height: auto !important;
+ font-size: 14px;
+ line-height: 22px;
+ color: rgba(255, 255, 255, 0.82);
+}
+
+.shuYing-character-dialog-intro {
+ display: block !important;
+ flex: none !important;
+ width: 100%;
+ height: auto !important;
+ font-size: 14px;
+ line-height: 22px;
+ color: rgba(255, 255, 255, 0.82);
+ text-align: left;
+}
+
+.shuYing-character-dialog-toolbar {
+ display: flex !important;
+ flex: none !important;
+ flex-direction: column !important;
+ gap: 8px;
+ width: 100%;
+ max-height: 74px;
+ overflow-y: auto;
+ padding: 2px 0 4px;
+}
+
+.shuYing-character-dialog-search {
+ all: unset;
+ display: block !important;
+ flex: none !important;
+ width: 100%;
+ height: 32px;
+ padding: 4px 10px;
+ border: 1px solid rgba(246, 211, 101, 0.55);
+ border-radius: 6px;
+ background: rgba(0, 0, 0, 0.32);
+ color: #fff;
+ font-size: 15px;
+ line-height: 24px;
+ text-align: left;
+}
+
+.shuYing-character-dialog-groups {
+ display: flex !important;
+ flex-wrap: wrap;
+ gap: 8px;
+ width: 100%;
+ min-height: 28px;
+ padding: 1px 0 3px;
+}
+
+.shuYing-character-dialog-group {
+ all: unset;
+ display: inline-flex !important;
+ align-items: center;
+ gap: 5px;
+ min-height: 28px;
+ padding: 3px 9px;
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ border-radius: 6px;
+ background: rgba(255, 255, 255, 0.06);
+ color: rgba(255, 255, 255, 0.78);
+ font-size: 14px;
+ line-height: 20px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.shuYing-character-dialog-group.selected {
+ border-color: rgba(246, 211, 101, 0.75);
+ color: #f6d365;
+}
+
+.shuYing-character-dialog-check {
+ position: relative;
+ display: inline-block;
+ width: 13px;
+ height: 13px;
+ flex: none;
+ border: 1px solid rgba(255, 255, 255, 0.45);
+ border-radius: 3px;
+ background: rgba(0, 0, 0, 0.28);
+}
+
+.shuYing-character-dialog-group.selected .shuYing-character-dialog-check {
+ border-color: rgba(246, 211, 101, 0.9);
+ background: rgba(246, 211, 101, 0.85);
+}
+
+.shuYing-character-dialog-group.selected .shuYing-character-dialog-check::after {
+ content: "";
+ position: absolute;
+ left: 3px;
+ top: 1px;
+ width: 4px;
+ height: 7px;
+ border: solid #24170b;
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+}
+
+.shuYing-character-dialog-group-text {
+ display: inline-block;
+ min-width: 1em;
+ white-space: nowrap;
+}
+
+.shuYing-character-dialog-grid-wrap {
+ position: relative !important;
+ display: block !important;
+ flex: 1 1 auto !important;
+ width: 100%;
+ min-height: 168px;
+ margin-top: 35px;
+ padding: 12px 10px 10px;
+ border: 1px solid rgba(246, 211, 101, 0.34);
+ border-radius: 7px;
+ background: rgba(255, 255, 255, 0.035);
+ overflow: hidden;
+}
+
+.shuYing-character-dialog-grid {
+ position: relative !important;
+ display: grid !important;
+ flex: none !important;
+ grid-template-columns: repeat(auto-fill, 104px);
+ justify-content: center;
+ align-content: start;
+ gap: 12px;
+ min-height: 152px;
+ max-height: min(39vh, 350px);
+ overflow-y: auto;
+ padding: 0 3px 2px;
+ width: 100%;
+}
+
+.shuYing-character-dialog-card {
+ all: unset;
+ position: relative !important;
+ display: flex !important;
+ flex-direction: column !important;
+ align-items: center;
+ justify-content: flex-start;
+ gap: 7px;
+ width: 104px;
+ min-height: 152px;
+ padding: 8px 7px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.06);
+ cursor: pointer;
+ transform: none;
+ text-align: center;
+}
+
+.shuYing-character-dialog-card.selected {
+ border-color: #f6d365;
+ background: rgba(246, 211, 101, 0.15);
+ box-shadow: 0 0 16px rgba(246, 211, 101, 0.34);
+}
+
+.shuYing-character-dialog-card.selected::after {
+ content: "已选";
+ position: absolute;
+ right: 6px;
+ top: 6px;
+ padding: 2px 7px;
+ border-radius: 999px;
+ background: rgba(246, 211, 101, 0.95);
+ color: #22160d;
+ font-size: 12px;
+ line-height: 18px;
+}
+
+.shuYing-character-dialog-avatar {
+ position: relative !important;
+ display: block !important;
+ width: 80px;
+ height: 108px;
+ flex: none;
+ margin: 0;
+ border-radius: 5px;
+ background-position: center;
+ background-size: cover;
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.42);
+}
+
+.shuYing-character-dialog-name {
+ width: 100%;
+ overflow: hidden;
+ color: #fff;
+ font-size: 15px;
+ line-height: 22px;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ text-shadow: 0 0 4px #000;
+}
+
+.shuYing-character-dialog-empty {
+ grid-column: 1 / -1;
+ padding: 34px 0;
+ color: rgba(255, 255, 255, 0.72);
+ font-size: 16px;
+ line-height: 26px;
+ text-align: center;
+}
+
+.shuYing-character-dialog-controls {
+ display: flex !important;
+ flex: none !important;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 10px;
+ padding-top: 2px;
+}
+
+.shuYing-character-dialog-button {
+ all: unset;
+ display: inline-block !important;
+ min-width: 72px;
+ height: 30px;
+ padding: 0 14px;
+ border: 1px solid rgba(246, 211, 101, 0.58);
+ border-radius: 5px;
+ background: linear-gradient(180deg, rgba(111, 78, 32, 0.92), rgba(55, 35, 18, 0.92));
+ color: #f6d365;
+ font-size: 15px;
+ line-height: 28px;
+ text-align: center;
+ text-shadow: 0 1px 2px #000;
+ cursor: pointer;
+}
+
+.shuYing-character-dialog-button:hover {
+ border-color: rgba(246, 211, 101, 0.9);
+ background: linear-gradient(180deg, rgba(139, 99, 42, 0.95), rgba(70, 45, 22, 0.95));
+}
+
+.shuYing-character-dialog-button.disabled {
+ opacity: 0.45;
+ pointer-events: none;
+}
+
+@media screen and (max-width: 520px) {
+ .shuYing-character-dialog {
+ width: 96vw;
+ padding: 14px 12px 12px;
+ }
+
+ .shuYing-character-dialog-header {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 2px;
+ }
+
+ .shuYing-character-dialog-toolbar {
+ max-height: 64px;
+ }
+
+ .shuYing-character-dialog-grid-wrap {
+ min-height: 154px;
+ padding: 10px 7px 8px;
+ }
+
+ .shuYing-character-dialog-grid {
+ grid-template-columns: repeat(auto-fill, 92px);
+ gap: 10px;
+ max-height: 36vh;
+ }
+
+ .shuYing-character-dialog-card {
+ width: 92px;
+ min-height: 138px;
+ }
+
+ .shuYing-character-dialog-avatar {
+ width: 72px;
+ height: 98px;
+ }
+}
diff --git a/extension.js b/extension.js
new file mode 100644
index 0000000..ac4d52d
--- /dev/null
+++ b/extension.js
@@ -0,0 +1,1007 @@
+import { lib, game, ui, get, ai, _status } from "../../noname.js";
+import updater from "./update.js"
+import func from "./function.js"
+import initShuYingMenu from "./menu.js";
+
+export const type = "extension";
+export let shuYing = new Object();
+const shuYingLocalVersion = "2.0.0.8";
+
+shuYing.name = "术樱包";
+shuYing.editable = false;
+shuYing.localVersion = shuYingLocalVersion;
+shuYing.m_bIsDownload = true; //用于判断下载状态,true: 可以下载,false:下载被占用
+shuYing.url = `${lib.assetURL}extension/术樱包`; //包路径
+shuYing.text = {}; //中上方文本
+shuYing.text_style = {}; //中上方文本样式
+shuYing.moduleCache = {}; //动态加载模块缓存,避免同一文件重复import
+shuYing.voices = {}; //语音台词表,voices.js缺失时保持为空
+
+//按需加载扩展内部模块。optional为true时,文件缺失只关闭对应功能,不中断整个扩展入口。
+shuYing.loadModule = async (path, label, optional = true) => {
+ if (!shuYing.moduleCache[path]) {
+ shuYing.moduleCache[path] = import(path)
+ .then(module => module.default ?? module)
+ .catch(error => {
+ const message = `术樱包:${label || path}加载失败`;
+ if (!optional) throw error;
+ console.warn(message, error);
+ return null;
+ });
+ }
+ return shuYing.moduleCache[path];
+};
+
+//加载语音翻译表。该文件只影响台词显示,缺失时不影响武将包主体加载。
+shuYing.loadVoices = async () => {
+ const voices = await shuYing.loadModule("./voices.js", "语音台词表", true);
+ shuYing.voices = voices || {};
+ return shuYing.voices;
+};
+
+// 公共函数需要在菜单创建前初始化,扩展菜单可在选将预备阶段被点击,此时还没有 player。
+shuYing.initFunction = () => {
+ if (shuYing.functionInitialized) return;
+ if (typeof func == "function") {
+ func(lib, game, ui, get, ai, _status, shuYing);
+ shuYing.functionInitialized = true;
+ }
+};
+
+shuYing.getOnlineVersionText = () => `最新版本:${lib.config.shuYing_online_version || "检测中..."}`;
+shuYing.updateOnlineVersionMenu = (version) => {
+ if (!version) return;
+
+ game.saveConfig("shuYing_online_version", version);
+
+ const text = `最新版本:${version}`;
+ if (lib.extensionMenu.extension_术樱包?.online_version) {
+ lib.extensionMenu.extension_术樱包.online_version.name = text;
+ }
+
+ if (typeof document != "undefined") {
+ const nodes = document.querySelectorAll(".config");
+ for (const node of nodes) {
+ if (node.textContent && node.textContent.trim().startsWith("最新版本:")) {
+ node.innerHTML = `${text}`;
+ }
+ }
+ }
+};
+
+/**
+ * @param { String } name 扩展包英文名
+ * @param { String } translate 扩展包翻译名
+ * @param { Object } obj 类对象
+ */
+shuYing.appendExtension = function (name, translate, obj) {
+ let oobj = get.copy(obj);
+ oobj.name = name;
+ oobj.character = obj.character;
+ oobj.skill = obj.skill;
+ Object.assign(obj.translate, shuYing.voices || {});
+ oobj.translate = obj.translate;
+ game.import('character', () => { return oobj; });
+ lib.config.all.characters.push(name);
+ if (!lib.config.characters.contains(name)) {
+ lib.config.characters.push(name);
+ }
+ lib.translate[name + '_character_config'] = translate;
+};
+
+shuYing.getVersion = (callback) => {
+ return updater.getOnlineVersion()
+ .then(version => {
+ shuYing.updateOnlineVersionMenu(version?.online_version);
+ if (typeof callback == "function") {
+ callback(version);
+ }
+ return version;
+ })
+ .catch(error => {
+ console.error("术樱包获取在线版本失败:", error);
+ return null;
+ });
+}
+
+//进入游戏时
+shuYing.content = (config, pack) => {
+};
+
+//术樱包初始化CSS样式
+shuYing.initCSS = () => {
+ //初始化加载CSS
+ lib.init.css(lib.assetURL + 'extension/术樱包', 'extension');
+
+ shuYing.text = document.createElement("div");
+ shuYing.text_style =
+ {
+ width: "360px",
+ minHeight: "92px",
+ boxSizing: "border-box",
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "stretch",
+ gap: "8px",
+ background: "linear-gradient(135deg, rgba(22,24,30,0.94), rgba(42,38,50,0.94))",
+ border: "1px solid rgba(255,255,255,0.18)",
+ borderRadius: "8px",
+ boxShadow: "0 12px 32px rgba(0,0,0,0.38), inset 0 1px 0 rgba(255,255,255,0.08)",
+ backdropFilter: "blur(8px)",
+ padding: "14px 16px",
+ position: "absolute",
+ top: "14px",
+ left: "50%",
+ transform: "translateX(-50%)",
+ zIndex: "10",
+ color: "#f5f1e8",
+ textAlign: "left",
+ writingMode: "horizontal-tb",
+ textOrientation: "mixed",
+ whiteSpace: "normal",
+ wordBreak: "keep-all",
+ fontSize: "14px",
+ lineHeight: "1.35",
+ fontFamily: "'Microsoft YaHei','STXinwei','xinwei',sans-serif",
+ pointerEvents: "none",
+ };
+
+ for (let k in shuYing.text_style) {
+ shuYing.text.style[k] = shuYing.text_style[k];
+ };
+}
+
+//术樱包初始化Config配置文件
+shuYing.initConfig = () => {
+ if (lib.config.show_splash != 'off') game.saveConfig('show_splash', 'off'); //关闭启动页
+
+ //崩坏3包
+ if (lib.config.extension_shuYing_bengHuai_Off == undefined) {
+ game.saveConfig('extension_shuYing_bengHuai_Off', true);
+ }
+
+ //原神包
+ if (lib.config.extension_术樱包_yuanShenOff == undefined) {
+ game.saveConfig('extension_术樱包_yuanShenOff', true);
+ }
+
+ //崩铁包
+ if (lib.config.extension_术樱包_bengTieOff == undefined) {
+ game.saveConfig('extension_术樱包_bengTieOff', true);
+ }
+
+ //天书包
+ if (lib.config.extension_shuYing_tianShu_Off == undefined) {
+ game.saveConfig('extension_shuYing_tianShu_Off', true);
+ }
+
+ //天书一共多少层
+ if (lib.config.extension_shuYing_tianShu_Instances == undefined) {
+ game.saveConfig('extension_shuYing_tianShu_Instances', 4);
+ }
+
+ //天书自定义Boss列表
+ if (lib.config.extension_shuYing_tianShu_DiyBoss == undefined) {
+ game.saveConfig('extension_shuYing_tianShu_DiyBoss', '');
+ }
+
+ //天书狂暴
+ if (lib.config.extension_shuYing_tianShu_KuangBao == undefined) {
+ game.saveConfig('extension_shuYing_tianShu_KuangBao', '');
+ }
+
+ //百战天书
+ if (lib.config.extension_shuYing_baiZhan_Off == undefined) {
+ game.saveConfig('extension_shuYing_baiZhan_Off', false);
+ }
+
+ //
+ // if(lib.config.extension_术樱包_skillsoff==undefined) game.saveConfig('extension_术樱包_skillsoff',false);
+
+ //版本号
+ game.saveConfig('shuYing_local_version', shuYingLocalVersion);
+
+};
+
+//术樱包菜单配置由 ./menu.js 初始化
+
+//术樱包初始化武将包
+shuYing.initCharacter = async () => {
+ let shuYingList;
+ if (lib.config.mode == 'boss') {
+ const shuying_boss = await shuYing.loadModule("./pve/boss/index.js", "活动BOSS", true);
+ if (shuying_boss) {
+ shuYing.appendExtension("shuYing_pve_boss", "活动BOSS", shuying_boss);
+ }
+
+ const initTianShu = await shuYing.loadModule("./tianshu/extension.js", "天书乱斗", true);
+ if (typeof initTianShu == "function") {
+ initTianShu(lib, game, ui, get, ai, _status, shuYing);
+ }
+ // shuYingList =
+ // [
+ // "yuanshen",
+ // ];
+ }
+ else {
+ // initBengTie(lib, game, ui, get, ai, _status, shuYing);
+
+ // shuYingList =
+ // [
+ // "yuanshen",
+ // ];
+ }
+
+ // shuYingList.forEach(url => {
+ // let extUrl = shuYing.url + "/" + url;
+
+ // lib.init.js(extUrl, "extension", () => {
+ // window.func(lib, game, ui, get, ai, _status, shuYing);
+ // });
+ // });
+}
+
+//初始化游戏时
+shuYing.precontent = async () => {
+ await shuYing.loadVoices();
+ shuYing.initCSS();
+ shuYing.initFunction();
+ shuYing.initConfig();
+ initShuYingMenu({ lib, game, ui, shuYing, updater });
+ shuYing.getVersion();
+ await shuYing.initCharacter();
+};
+
+shuYing.config = () => {
+
+};
+
+shuYing.help = () => {
+
+};
+
+//添加自定义Boss TODO
+shuYing.newTianShuBossList = function () {
+ _status.buttonNode; //用于判断输入框回车之后响应事件
+ _status.nameList;
+ let manual = ui.create.div('.manual', manual);
+ let menu = ui.create.div('.menu', manual);
+ let input = menu.appendChild(document.createElement('input'));
+ input.onkeydown = function (e) {
+ if (e && e.keyCode == 13) {
+ if (_status.buttonNode == 2) {
+ this.deleteBossList(dialog, manual, input.value);
+ }
+ else {
+ this.searchBossList(dialog, manual, input.value);
+ }
+ }
+ };
+
+ let addNewBoss = ui.create.div('.addNewBoss', menu); //加入新boss
+ let newBossList = ui.create.div('.newBossList', menu);
+ let deleteNewBossAllList = ui.create.div('.deleteNewBossAllList', menu);
+ let deleteNewBoss = ui.create.div('.deleteNewBoss', menu);
+ let playerList = ui.create.div('.playerList', menu);
+
+ let close = ui.create.div('.close', menu);
+ let dialog = ui.create.dialog();
+ dialog.noImage = true;
+ dialog.style.backgroundImage = "";
+
+ let content = manual.appendChild(dialog);
+ content.classList.remove('nobutton');
+ content.classList.add('content');
+ content.style.transform = '';
+ content.style.opacity = '';
+ content.style.height = '';
+
+ newBossList.innerHTML = '列表';
+ newBossList.id = 'newBossList';
+ newBossList.addEventListener('click', function () {
+ this.addBossShow(dialog, manual);
+ });
+
+ deleteNewBossAllList.innerHTML = '清除';
+ deleteNewBossAllList.addEventListener('click', function () {
+ if (confirm('点击确定清除全部自定义Boss')) {
+ game.saveConfig('extension_shuYing_tianShu_DiyBoss', '');
+ this.addBossShow(dialog, manual);
+ alert('清除完毕!');
+ }
+ });
+
+ addNewBoss.innerHTML = '搜索';
+ addNewBoss.addEventListener('click', function () {
+ this.searchBossList(dialog, manual, input.value);
+
+ input.value = "";
+ _status.buttonNode = 1;
+ });
+
+ deleteNewBoss.innerHTML = '登陆'; //暂时弃用
+ deleteNewBoss.addEventListener('click', function () {
+ alert('暂未开放');
+ //deleteNewBossList(dialog,manual,input.value);
+ //input.value="";
+ //_status.buttonNode=2;
+ });
+
+ playerList.innerHTML = '排行榜';
+ playerList.addEventListener('click', function () {
+ alert('暂未开放');
+ });
+
+ dialog.addText("使用说明: 点击列表查看自定义BOSS列表
" +
+ "点击清空按钮清空自定义BOSS列表中全部Boss
" +
+ "点击列表列举出Boss列表,并可以删除列表中的Boss
" +
+ "点击搜索并在输入框内输入武将名称即可罗列相似武将
" +
+ "需要注意的是,输入框需要输入武将名称而不是英文
一旦启用自定义Boss,那么必须保持不禁用武将和Boss列表大于1");
+
+ // close1.addEventListener('click', function(){
+ // alert(close1.value);
+ // //game.tujianBegin(content , close , input.value , manual);
+ // //input.value="";
+ // });
+
+ close.innerHTML = '关闭';
+ close.addEventListener('click', function () {
+ manual.remove();
+ ui.arena.show();
+ ui.system.show();
+
+ game.resume2();
+ if (game.onresume2) {
+ game.onresume2();
+ }
+ ui.arena.classList.remove('menupaused');
+ ui.historybar.classList.remove('menupaused');
+ ui.window.classList.remove('touchinfohidden');
+ ui.config2.classList.remove('pressdown2');
+ ui.menuContainer.show();
+ });
+
+ _status.paused = true;
+ ui.arena.classList.remove('menupaused');
+ ui.arena.hide();
+ ui.system.hide();
+ ui.menuContainer.hide();
+ ui.window.appendChild(manual);
+},
+
+ //添加自定义Boss TODO
+ shuYing.addBossShow = function (dialog, manual) {
+ dialog = ui.create.dialog();
+ dialog.noImage = true;
+ dialog.style.backgroundImage = "";
+
+ let content = manual.appendChild(dialog);
+ content.classList.remove('nobutton');
+ content.classList.add('content');
+ content.style.transform = '';
+ content.style.opacity = '';
+ content.style.height = '';
+
+ let list = lib.config.extension_术樱_tianshu_add_list;
+ if (list == undefined || list == '') {
+ dialog.addText("无BOSS");
+ } else {
+ list = list.split(",");
+
+ for (let i = 0; i < list.length; i++) {
+ for (let j in lib.character) {
+ if (j == list[i]) {
+ dialog.addSmall([[j], 'character']);
+ let del = ui.create.div('.shadowed.reduce_radius.pointerdiv.tdnode');
+ del.link = i;
+ del.innerHTML = '' + '删除' + '';
+ del.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', function () {
+ if (lib.config.extension_术樱_tianshu_add_list.length <= 1) {
+ game.saveConfig('extension_术樱_tianshu_add_list', '');
+ } else if (lib.config.extension_术樱_tianshu_add_list.length == 2) {
+ if (this.link != 1) {
+ game.saveConfig('extension_术樱_tianshu_add_list', list[this.link]);
+ } else {
+ game.saveConfig('extension_术樱_tianshu_add_list', list[0]);
+ }
+ } else {
+ game.saveConfig('extension_术樱_tianshu_add_list', '');
+ for (let k = 0; k < list.length; k++) {
+ if (k == this.link) continue;
+ if (lib.config.extension_术樱_tianshu_add_list == undefined || lib.config.extension_术樱_tianshu_add_list == '') {
+ game.saveConfig('extension_术樱_tianshu_add_list', list[k]);
+ } else {
+ game.saveConfig('extension_术樱_tianshu_add_list', lib.config.extension_术樱_tianshu_add_list + ',' + list[k]);
+ }
+ }
+ document.getElementById('newBossList').click(); //刷新列表
+ }
+ });
+ dialog.addAuto(del);
+ }
+ }
+ }
+ }
+ },
+
+ //搜索Boss TODO
+ shuYing.searchBossList = function (dialog, manual, val) {
+ dialog = ui.create.dialog();
+ dialog.noImage = true;
+ dialog.style.backgroundImage = "";
+
+ let content = manual.appendChild(dialog);
+ content.classList.remove('nobutton');
+ content.classList.add('content');
+ content.style.transform = '';
+ content.style.opacity = '';
+ content.style.height = '';
+
+ let result = val;
+ let value = false;
+ let list = [];
+
+ if (result == "" || result == null) {
+ result = "请输入武将名称";
+ alert(result);
+ } else {
+ for (let a in lib.character) {
+ if (lib.translate[a] && lib.translate[a].indexOf(result) != -1) {
+ list.add(a);
+ value = true;
+ }
+ }
+
+ for (let i = 0; i < list.length; i++) {
+ dialog.addSmall([[list[i]], 'character']);
+ let add = ui.create.div('.shadowed.reduce_radius.pointerdiv.tdnode');
+ add.link = i;
+ add.innerHTML = '' + '加入' + '';
+ add.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', function () {
+ if (lib.config.extension_术樱_tianshu_add_list == undefined || lib.config.extension_术樱_tianshu_add_list == '') {
+ game.saveConfig('extension_术樱_tianshu_add_list', list[this.link]);
+ } else {
+ game.saveConfig('extension_术樱_tianshu_add_list', lib.config.extension_术樱_tianshu_add_list + ',' + list[this.link]);
+ }
+ alert('成功加入至列表');
+ });
+ dialog.addAuto(add);
+ }
+
+ _status.nameList = list;
+
+ if (value == false) {
+ alert('找不到名为' + result + '的武将!');
+ }
+ }
+ },
+
+ //删除Boss TODO
+ shuYing.deleteBossList = function (dialog, manual, val) {
+ dialog = ui.create.dialog();
+ dialog.noImage = true;
+ dialog.style.backgroundImage = "";
+
+ let content = manual.appendChild(dialog);
+ content.classList.remove('nobutton');
+ content.classList.add('content');
+ content.style.transform = '';
+ content.style.opacity = '';
+ content.style.height = '';
+
+ let result = val;
+
+ if (result == "" || result == null) {
+ result = "你没有输入名称";
+ alert(result);
+ return;
+ }
+
+ let list = lib.config.extension_术樱_tianshu_add_list.split(",");
+ let value = false;
+ let newList = [];
+
+ for (let i = 0; i < list.length; i++) {
+ let pushBool = true;
+ if (String(lib.translate[list[i]]) == String(result)) {
+ if (confirm('已找到' + result + '武将,点击确定移除自定义Boss列表。')) {
+ pushBool = false;
+ }
+ value = true;
+ }
+ if (pushBool) newList.push(list[i]);
+ }
+ if (newList) {
+ game.saveConfig('extension_术樱_tianshu_add_list', '');
+ for (let j = 0; j < newList.length; j++) {
+ if (lib.config.extension_术樱_tianshu_add_list == undefined || lib.config.extension_术樱_tianshu_add_list == '') {
+ game.saveConfig('extension_术樱_tianshu_add_list', newList[j]);
+ } else {
+ game.saveConfig('extension_术樱_tianshu_add_list', lib.config.extension_术樱_tianshu_add_list + ',' + newList[j]);
+ }
+ }
+ }
+
+
+ if (value == false) {
+ alert('找不到名为' + result + '的武将或者武将不存在自定义Boss列表中。');
+ }
+ },
+
+ shuYing.download_beta_version = function () {
+ let online_version;
+ let httpRequest = new XMLHttpRequest();
+ httpRequest.open("GET", 'http://diuse.hao1237.top/Diuse/beta_extension/online_version.js', true);
+ httpRequest.send(null);
+ httpRequest.onreadystatechange = function () {
+ if (httpRequest.readyState == 4 && httpRequest.status == 200) {
+ online_version = httpRequest.responseText;
+ game.saveConfig('Diuse_online_version', httpRequest.responseText)
+ lib.init.js(url, 'version', function () {
+ try {
+ let local_version = Diuse_version;
+ let Diuse_num = 1;
+ } catch (error) {
+ if (confirm('本地资源不完整!点击确认重新获取!')) {
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/files.js', 'extension/术樱/files.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/version.js', 'extension/术樱/version.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/extension.js', 'extension/术樱/extension.js', function () {
+ game.saveConfig('Diuse_local_version', online_version);
+ Diuse_Button = true;
+ alert('下载完成,重启生效');
+ }, function () {
+ Diuse_Button = true;
+ alert('下载失败');
+ });
+ } else {
+ Diuse_Button = true;
+ }
+ }
+ if (local_version != online_version && Diuse_num == 1) {
+ if (confirm('检测到最新测试版本为:' + online_version + '本地版本为:' + local_version)) {
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/files.js', 'extension/术樱/files.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/version.js', 'extension/术樱/version.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/extension.js', 'extension/术樱/extension.js', function () {
+ game.saveConfig('Diuse_local_version', online_version);
+ Diuse_Button = true;
+ alert('下载完成,重启生效');
+ }, function () {
+ Diuse_Button = true;
+ alert('下载失败');
+ });
+ } else {
+ Diuse_Button = true;
+ }
+ } else {
+ if (Diuse_num == 1) {
+ Diuse_Button = true;
+ alert('本地版本为最新版');
+ }
+ }
+ }, function () {
+ if (confirm('本地资源不完整!点击确认重新获取!')) {
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/files.js', 'extension/术樱/files.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/version.js', 'extension/术樱/version.js', function () { }, function () { });
+ game.download('http://diuse.hao1237.top/Diuse/beta_extension/extension.js', 'extension/术樱/extension.js', function () {
+ game.saveConfig('Diuse_local_version', online_version);
+ Diuse_Button = true;
+ alert('下载完成,重启生效');
+ }, function () {
+ Diuse_Button = true;
+ alert('下载失败');
+ });
+ } else {
+ Diuse_Button = true;
+ }
+ });
+ }
+ };
+ };
+
+shuYing.download_mp3 = function () {
+ lib.init.js(url, 'files', function () {
+ let list = Diuse_mp3;
+ let num = 0;
+ let num1 = list.length;
+ document.body.appendChild(Diuse_Text);
+ let download1 = function () {
+ game.download('http://diuse.hao1237.top/Diuse/skin/' + list[0], 'extension/术樱/' + list[0], function () {
+ num++
+ list.remove(list[0]);
+ if (list.length > 0) {
+ Diuse_Text.innerHTML = '正在下载(' + num + '/' + num1 + ')';
+ download1();
+ } else {
+ Diuse_Text.innerHTML = '下载完毕';
+ Diuse_Button = true;
+ alert('语音下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ }, function () {
+ if (confirm('下载' + list[0] + '失败,是否继续下载?')) {
+ download1();
+ }
+ });
+ }
+ download1();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+shuYing.download_static = function () {
+ lib.init.js(url, 'files', function () {
+ let list = Diuse_static;
+ let num = 0;
+ let num1 = list.length;
+ document.body.appendChild(Diuse_Text);
+ let download1 = function () {
+ game.download('http://diuse.hao1237.top/Diuse/image/static/' + list[0], 'extension/术樱/' + list[0], function () {
+ num++
+ list.remove(list[0]);
+ if (list.length > 0) {
+ Diuse_Text.innerHTML = '正在下载(' + num + '/' + num1 + ')';
+ download1();
+ } else {
+ Diuse_Text.innerHTML = '下载完毕';
+ Diuse_Button = true;
+ alert('静态皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ }, function () {
+ if (confirm('下载' + list[0] + '失败,是否继续下载?')) {
+ download1();
+ }
+ });
+ }
+ download1();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+shuYing.download_tianshu = function () {
+ lib.init.js(url, 'files', function () {
+ let list = Diuse_tianshu;
+ let num = 0;
+ let num1 = list.length;
+ document.body.appendChild(Diuse_Text);
+ let download1 = function () {
+ game.download('http://diuse.hao1237.top/Diuse/image/tianshu/' + list[0], 'extension/术樱/' + list[0], function () {
+ num++
+ list.remove(list[0]);
+ if (list.length > 0) {
+ Diuse_Text.innerHTML = '正在下载(' + num + '/' + num1 + ')';
+ download1();
+ } else {
+ Diuse_Text.innerHTML = '下载完毕';
+ Diuse_Button = true;
+ alert('天书皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ }, function () {
+ if (confirm('下载' + list[0] + '失败,是否继续下载?')) {
+ download1();
+ }
+ });
+ }
+ download1();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+shuYing.download_dynamic = function (dynamic_name) {
+ if (lib.config.extension_术樱_Skin) {
+ let url_files = dynamic_name.substring(0, dynamic_name.length - 4);
+ skinList(url_files);
+ } else {
+ game.download('http://diuse.hao1237.top/Diuse/image/dynamic/' + dynamic_name, 'extension/术樱/' + dynamic_name, function () {
+ Diuse_Button = true;
+ alert('所选皮肤下载完毕!');
+ }, function () {
+ if (confirm('下载' + dynamic_name + '失败,是否继续下载?')) {
+ download_dynamic(dynamic_name);
+ }
+ });
+ }
+};
+
+shuYing.download_dynamic_all = function () {
+ lib.init.js(url, 'files', function () {
+ let list = Diuse_dynamic;
+ let num = 0;
+ let num1 = list.length;
+ document.body.appendChild(Diuse_Text);
+ let download1 = function () {
+ game.download('http://diuse.hao1237.top/Diuse/image/dynamic/' + list[0], 'extension/术樱/' + list[0], function () {
+ num++
+ list.remove(list[0]);
+ if (list.length > 0) {
+ Diuse_Text.innerHTML = '正在下载(' + num + '/' + num1 + ')';
+ download1();
+ } else {
+ Diuse_Text.innerHTML = '下载完毕';
+ Diuse_Button = true;
+ alert('动态皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ }, function () {
+ if (confirm('下载' + list[0] + '失败,是否继续下载?')) {
+ download1();
+ }
+ });
+ }
+ download1();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+shuYing.download_files_all = function () {
+ lib.init.js(url, 'files', function () {
+ let list = Diuse_dynamic;
+ let num = 0;
+ let num1 = list.length;
+ document.body.appendChild(Diuse_Text);
+ let download1 = function () {
+ game.download('http://diuse.hao1237.top/Diuse/image/dynamic/' + list[0], 'extension/术樱/' + list[0], function () {
+ num++
+ list.remove(list[0]);
+ if (list.length > 0) {
+ Diuse_Text.innerHTML = '正在下载(' + num + '/' + num1 + ')';
+ download1();
+ } else {
+ Diuse_Text.innerHTML = '下载完毕';
+ Diuse_Button = true;
+ alert('动态皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ }, function () {
+ if (confirm('下载' + list[0] + '失败,是否继续下载?')) {
+ download1();
+ }
+ });
+ }
+ download1();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+shuYing.skinList = function () {
+ lib.init.js(url, 'files', function () {
+ let num = 0;
+ let skin_list = Skin;
+ let skin_num;
+ let file_url = 'image/skin/';
+ let file_length = 0;
+ document.body.appendChild(Diuse_Text);
+ let down = function () {
+ switch (skin_list[0]) {
+ case 'Diuse_Fuhua': {
+ skin_num = Diuse_Fuhua_Skin;
+ break;
+ }
+ case 'Diuse_Bachongying': {
+ skin_num = Diuse_Bachongying_Skin;
+ break;
+ }
+ case 'Diuse_Buluoniya': {
+ skin_num = Diuse_Buluoniya_Skin;
+ break;
+ }
+ case 'Diuse_Kalian': {
+ skin_num = Diuse_Kalian_Skin;
+ break;
+ }
+ case 'Diuse_Shangxian': {
+ skin_num = Diuse_Shangxian_Skin;
+ break;
+ }
+ case 'Diuse_Shilv': {
+ skin_num = Diuse_Shilv_Skin;
+ break;
+ }
+ case 'Diuse_Xier': {
+ skin_num = Diuse_Xier_Skin;
+ break;
+ }
+ case 'Diuse_Yayi': {
+ skin_num = Diuse_Yayi_Skin;
+ break;
+ }
+ case 'Diuse_Yuexia': {
+ skin_num = Diuse_Yuexia_Skin;
+ break;
+ }
+ case 'Diuse_Konglv': {
+ skin_num = Diuse_Konglv_Skin;
+ break;
+ }
+ case 'Diuse_Heixi': {
+ skin_num = Diuse_Heixi_Skin;
+ break;
+ }
+ case 'Diuse_Bachongshenzi': {
+ skin_num = Diuse_Bachongshenzi_Skin;
+ break;
+ }
+ case 'Diuse_Keli': {
+ skin_num = Diuse_Keli_Skin;
+ break;
+ }
+ case 'Diuse_Shuangzi': {
+ skin_num = Diuse_Shuangzi_Skin;
+ break;
+ }
+ case 'Diuse_Lanmei': {
+ skin_num = Diuse_Lanmei_Skin;
+ break;
+ }
+ case 'Diuse_Yingtao': {
+ skin_num = Diuse_Yingtao_Skin;
+ break;
+ }
+ }
+ url_Diuse_search = file_url + skin_list[0] + '/' + skin_num[0] + '.jpg';
+ file_length += skin_list.length;
+ game.readFile(url_Diuse_search, function () {
+ num++;
+ skin_num.remove(skin_num[0]);
+ if (skin_list.length > 0 || skin_num.length > 0) {
+ if (skin_num.length <= 0) skin_list.remove(skin_list[0]);
+ Diuse_Text.innerHTML = '正在补充Skin(' + num + '/' + file_length + ')';
+ down();
+ } else {
+ Diuse_Text.innerHTML = '补充完毕';
+ Diuse_Button = true;
+ alert('皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ }
+ }, function () {
+ num++;
+ skin_num.remove(skin_num[0]);
+ if (skin_list.length > 0 || skin_num.length > 0) {
+ if (skin_num.length <= 0) skin_list.remove(skin_list[0]);
+ Diuse_Text.innerHTML = '正在补充Skin(' + num + '/' + file_length + ')';
+ down();
+ } else {
+ Diuse_Text.innerHTML = '补充完毕';
+ Diuse_Button = true;
+ alert('皮肤下载完毕!');
+ document.body.removeChild(Diuse_Text);
+ };
+ // game.download('https://diuse.coding.net/p/extension/d/noname_Skin/git/raw/master/Skin/'+skin_list[0]+'/'+skin_num[0]+'.jpg','image/skin/'+skin_list[0]+'/'+skin_num[0]+'.jpg',function(){
+ // num++;
+ // skin_num.remove(skin_num[0]);
+ // if(skin_list.length>0||skin_num.length>0){
+ // if(skin_num.length<=0) skin_list.remove(skin_list[0]);
+ // Diuse_Text.innerHTML='正在补充Skin('+num+'/'+file_length+')';
+ // down();
+ // }else{
+ // Diuse_Text.innerHTML='补充完毕';
+ // Diuse_Button=true;
+ // alert('皮肤下载完毕!');
+ // document.body.removeChild(Diuse_Text);
+ // };
+ // },function(){
+ // if(confirm('下载'+skin_list[0]+'失败,是否继续下载?')){
+ // down();
+ // }
+ // });
+ });
+ }
+ down();
+ }, function () {
+ Diuse_Button = true;
+ alert('本地资源不完整!请检查文件完整性。');
+ });
+};
+
+//本地资源修复
+shuYing.RepairBug = () => {
+ updater.repairCoreFiles(shuYing);
+};
+
+//重新下载重要js文件
+shuYing.RepairBugGo = () => {
+ updater.repairCoreFiles(shuYing);
+};
+
+shuYing.Diuse_downText = function () {
+ time = setTimeout(() => {
+ }, 1000)
+};
+
+shuYing.Diuse_upText = function (type) {
+ clearTimeout(time);
+ switch (type) {
+ case 0:
+ alert();
+ case 1:
+ alert('三张重复' +
+ '\n【桃】 恢复两点体力并摸三张牌' +
+ '\n【闪】 此杀必须两张闪响应 然后你摸两张牌' +
+ '\n【杀】 此杀不可被响应 出杀次数上限+2' +
+ '\n【酒】 此杀伤害+3\n' +
+ '\n两张重复' +
+ '\n【桃】 恢复一点体力并摸两张牌' +
+ '\n【闪】 此杀需要打出两张闪响应' +
+ '\n【杀】 不可以被响应' +
+ '\n【酒】 此杀伤害+2\n' +
+ '\n一张重复' +
+ '\n【桃】 命中后恢复一点体力' +
+ '\n【闪】 命中后摸一张牌' +
+ '\n【杀】 出杀次数上限+1' +
+ '\n【酒】 此杀伤害+1');
+ break;
+ case 2:
+ alert('根据武器攻击距离获得相应技能\n' +
+ '一:当你于你的回合内使用一张牌后,你可以弃置一张手牌并摸一张牌。\n' +
+ '二:当你于回合内获得一张牌且不是因为此技能获得牌时,你摸一张牌。\n' +
+ '三:出牌阶段限两次。你造成伤害后你可以让场上的一名角色受到一点无伤害来源的伤害。\n' +
+ '四:你使用杀或普通锦囊后你可以多增加一个目标,如果取消则摸X张牌(X为你已损失的体力,如果为0则摸1)\n' +
+ '五:出牌阶段限一次,当你使用可造成伤害的牌指定目标后你可以选择其一个目标然后你摸X张牌。(X为目标当前体力)\n' +
+ '六:获得全部技能效果。\n');
+
+ break;
+ }
+};
+
+// game.导入card=function(英文名,翻译名,obj){let oobj=get.copy(obj);oobj.list=obj.card.list;oobj.card=obj.card.card;oobj.skill=obj.skill.skill;oobj.translate=Object.assign({},obj.card.translate,obj.skill.translate);game.import('card',function(){return oobj});lib.config.all.cards.push(英文名);if(!lib.config.cards.contains(英文名))lib.config.cards.push(英文名);lib.translate[英文名+'_card_config']=翻译名;};
+// game.新增势力=function(名字,映射,渐变){let n,t;if(!名字)return;if(typeof 名字=="string"){n=名字;t=名字}else if(Array.isArray(名字)&&名字.length==2&&typeof 名字[0]=="string"){n=名字[0];t=名字[1]}else return;if(!映射||!Array.isArray(映射)||映射.length!=3)映射=[199,21,133];let y="("+映射[0]+","+映射[1]+","+映射[2];let y1=y+",1)",y2=y+")";let s=document.createElement('style');let l;l=".player .identity[data-color='diy"+n+"'],";l+="div[data-nature='diy"+n+"'],";l+="span[data-nature='diy"+n+"'] {text-shadow: black 0 0 1px,rgba"+y1+" 0 0 2px,rgba"+y1+" 0 0 5px,rgba"+y1+" 0 0 10px,rgba"+y1+" 0 0 10px}";l+="div[data-nature='diy"+n+"m'],";l+="span[data-nature='diy"+n+"m'] {text-shadow: black 0 0 1px,rgba"+y1+" 0 0 2px,rgba"+y1+" 0 0 5px,rgba"+y1+" 0 0 5px,rgba"+y1+" 0 0 5px,black 0 0 1px;}";l+="div[data-nature='diy"+n+"mm'],";l+="span[data-nature='diy"+n+"mm'] {text-shadow: black 0 0 1px,rgba"+y1+" 0 0 2px,rgba"+y1+" 0 0 2px,rgba"+y1+" 0 0 2px,rgba"+y1+" 0 0 2px,black 0 0 1px;}";s.innerHTML=l;document.head.appendChild(s);if(渐变&&Array.isArray(渐变)&&Array.isArray(渐变[0])&&渐变[0].length==3){let str="",st2=[];for(let i=0;i<渐变.length;i++){str+=",rgb("+渐变[i][0]+","+渐变[i][1]+","+渐变[i][2]+")";if(i<2)st2[i]="rgb("+渐变[i][0]+","+渐变[i][1]+","+渐变[i][2]+")";}let tenUi = document.createElement('style');tenUi.innerHTML = ".player>.camp-zone[data-camp='"+n+"']>.camp-back {background: linear-gradient(to bottom"+str+");}";tenUi.innerHTML += ".player>.camp-zone[data-camp='"+n+"']>.camp-name {text-shadow: 0 0 5px "+st2[0]+", 0 0 10px "+st2[1]+";}";document.head.appendChild(tenUi);}lib.group.add(n);lib.translate[n]= t;lib.groupnature[n]= "diy"+n;};
+
+
+shuYing.package =
+{
+ character: {
+ character: {
+ },
+ translate: {
+ },
+ },
+ card: {
+ game:
+ {
+ },
+ card:
+ {
+ },
+ skill:
+ {
+ },
+ translate:
+ {
+ },
+ list: [],
+ },
+ skill: {
+ skill: {
+ },
+ translate: {
+ },
+ },
+ intro: "所有素材均来自互联网,侵权必删。",
+ author: "术樱",
+ diskURL: "",
+ forumURL: "",
+ version: "1.0",
+};
+
+shuYing.files =
+{
+ "character": [],
+ "card": [],
+ "skill": [],
+ "audio": []
+}
+
+export default function () {
+ return shuYing;
+}
diff --git a/function.js b/function.js
new file mode 100644
index 0000000..d6243e6
--- /dev/null
+++ b/function.js
@@ -0,0 +1,226 @@
+export default function initFunction(lib, game, ui, get, ai, _status, shuYing = {}) {
+ game.shuying_getDiscardNamesNum = function () {
+ return Array.from(ui.discardPile.childNodes)
+ .map(card => get.name(card))
+ .unique()
+ .length;
+ };
+
+ game.shuying_getDiscardNum = function () {
+ return game
+ .getGlobalHistory("cardMove", evt => evt.name == "cardsDiscard" || evt.name == "lose" && evt.position == ui.discardPile)
+ .reduce((num, evt) => num + (evt.cards?.length || 0), 0);
+ };
+
+ lib.element.player.shuYing_randomNum = function (max, min = 0) {
+ const upper = Number(max);
+ const lower = Number(min);
+ if (!Number.isFinite(upper) || !Number.isFinite(lower) || upper <= lower) return lower || 0;
+ return Math.floor(Math.random() * (upper - lower)) + lower;
+ };
+
+ lib.element.player.shuYing_numRandom = function () {
+ return this.shuYing_randomNum(100, 1);
+ };
+
+ const getCharacterPack = id => {
+ for (const packName in lib.characterPack) {
+ if (id in lib.characterPack[packName]) return packName;
+ }
+ return "unknown";
+ };
+
+ const getCharacterName = id => get.translation(id) || id;
+
+ const normalizeCharacterDialogItems = list => {
+ return (list || []).map(item => {
+ if (typeof item == "string") {
+ return {
+ key: item,
+ id: item,
+ name: getCharacterName(item),
+ group: getCharacterPack(item),
+ };
+ }
+ const id = item.id || item.name || item.key;
+ return {
+ key: item.key || id,
+ id,
+ name: item.name || getCharacterName(id),
+ group: item.group || getCharacterPack(id),
+ };
+ }).filter(item => item.key && item.id);
+ };
+
+ const getGroupText = group => {
+ if (group == "unknown") return "未知";
+ return lib.translate[`${group}_character_config`] || get.translation(group) || group;
+ };
+
+ const makeSearchMatcher = text => {
+ const value = String(text || "").trim();
+ if (!value) return () => true;
+ try {
+ const regexp = new RegExp(value, "i");
+ return item => regexp.test(item.key) || regexp.test(item.id) || regexp.test(item.name) || regexp.test(get.translation(item.id));
+ } catch (e) {
+ return item => [item.key, item.id, item.name, get.translation(item.id)].some(str => String(str || "").includes(value));
+ }
+ };
+
+ shuYing.chooseCharacterPoolDialog = ({ title = "请选择角色", intro = "", list = [], selected = [] } = {}) => {
+ return new Promise(resolve => {
+ const items = normalizeCharacterDialogItems(list);
+ const selectedSet = new Set(selected);
+ const activeGroups = new Set(items.map(item => item.group));
+ const createNode = (tag, className, text, parent) => {
+ const node = document.createElement(tag);
+ if (className) node.className = className;
+ if (text !== undefined && text !== null) node.textContent = text;
+ if (parent) parent.appendChild(node);
+ return node;
+ };
+ const mask = createNode("div", "shuYing-character-dialog-mask", null, document.body);
+ const dialog = createNode("div", "shuYing-character-dialog", null, mask);
+ const header = createNode("div", "shuYing-character-dialog-header", null, dialog);
+ createNode("div", "shuYing-character-dialog-title", title, header);
+ const countNode = createNode("div", "shuYing-character-dialog-count", "", header);
+ if (intro) createNode("div", "shuYing-character-dialog-intro", intro, dialog);
+ const searchInput = createNode("input", "shuYing-character-dialog-search", null, dialog);
+ searchInput.type = "text";
+ searchInput.placeholder = "搜索武将名 / ID / 正则";
+ searchInput.autocomplete = "off";
+ searchInput.spellcheck = false;
+ const toolbar = createNode("div", "shuYing-character-dialog-toolbar", null, dialog);
+ const groupBox = createNode("div", "shuYing-character-dialog-groups", null, toolbar);
+ const gridWrap = createNode("div", "shuYing-character-dialog-grid-wrap", null, dialog);
+ const grid = createNode("div", "shuYing-character-dialog-grid", null, gridWrap);
+ const controls = createNode("div", "shuYing-character-dialog-controls", null, dialog);
+ let closed = false;
+ let visibleItems = items.slice();
+
+ const close = result => {
+ if (closed) return;
+ closed = true;
+ mask.remove();
+ resolve(result);
+ };
+
+ const refreshCard = card => {
+ card.classList.toggle("selected", selectedSet.has(card.dataset.key));
+ };
+
+ const updateCount = () => {
+ countNode.textContent = `已选择 ${selectedSet.size}/${items.length}`;
+ confirmNode.classList.toggle("disabled", selectedSet.size == 0);
+ confirmNode.disabled = selectedSet.size == 0;
+ };
+
+ const renderGrid = () => {
+ grid.innerHTML = "";
+ const matcher = makeSearchMatcher(searchInput.value);
+ visibleItems = items.filter(item => activeGroups.has(item.group) && matcher(item));
+ if (!visibleItems.length) {
+ createNode("div", "shuYing-character-dialog-empty", "没有符合条件的武将", grid);
+ updateCount();
+ return;
+ }
+ visibleItems.forEach(item => {
+ const card = createNode("div", "shuYing-character-dialog-card", null, grid);
+ card.tabIndex = 0;
+ card.dataset.key = item.key;
+ const avatar = createNode("div", "shuYing-character-dialog-avatar", null, card);
+ avatar.setBackground(item.id, "character");
+ createNode("span", "shuYing-character-dialog-name", item.name || getCharacterName(item.id), card);
+ card.addEventListener("click", event => {
+ event.stopPropagation();
+ if (selectedSet.has(item.key)) selectedSet.delete(item.key);
+ else selectedSet.add(item.key);
+ refreshCard(card);
+ updateCount();
+ });
+ refreshCard(card);
+ });
+ updateCount();
+ };
+
+ const makeControl = (text, handler) => {
+ const button = createNode("div", "shuYing-character-dialog-button", text, controls);
+ button.tabIndex = 0;
+ button.addEventListener("click", event => {
+ event.stopPropagation();
+ handler();
+ });
+ button.addEventListener("keydown", event => {
+ event.stopPropagation();
+ if (event.key == "Enter" || event.key == " ") {
+ event.preventDefault();
+ handler();
+ }
+ });
+ return button;
+ };
+
+ mask.addEventListener("click", event => {
+ event.stopPropagation();
+ close(false);
+ });
+ dialog.addEventListener("click", event => event.stopPropagation());
+ searchInput.addEventListener("click", event => event.stopPropagation());
+ searchInput.addEventListener("input", renderGrid);
+ searchInput.addEventListener("keydown", event => {
+ event.stopPropagation();
+ if (event.key == "Escape") close(false);
+ if (event.key == "Enter") {
+ event.preventDefault();
+ renderGrid();
+ }
+ });
+
+ items.map(item => item.group).unique().forEach(group => {
+ const label = createNode("div", "shuYing-character-dialog-group selected", null, groupBox);
+ label.tabIndex = 0;
+ label.dataset.group = group;
+ createNode("span", "shuYing-character-dialog-check", "", label);
+ createNode("span", "shuYing-character-dialog-group-text", getGroupText(group), label);
+ label.addEventListener("click", event => {
+ event.stopPropagation();
+ const checked = !label.classList.contains("selected");
+ label.classList.toggle("selected", checked);
+ if (checked) activeGroups.add(group);
+ else activeGroups.delete(group);
+ renderGrid();
+ });
+ label.addEventListener("keydown", event => {
+ event.stopPropagation();
+ if (event.key == "Enter" || event.key == " ") {
+ event.preventDefault();
+ label.click();
+ }
+ });
+ });
+
+ makeControl("全选", () => {
+ visibleItems.forEach(item => selectedSet.add(item.key));
+ grid.querySelectorAll(".shuYing-character-dialog-card").forEach(refreshCard);
+ updateCount();
+ });
+ makeControl("清空", () => {
+ visibleItems.forEach(item => selectedSet.delete(item.key));
+ grid.querySelectorAll(".shuYing-character-dialog-card").forEach(refreshCard);
+ updateCount();
+ });
+ makeControl("反选", () => {
+ visibleItems.forEach(item => selectedSet.has(item.key) ? selectedSet.delete(item.key) : selectedSet.add(item.key));
+ grid.querySelectorAll(".shuYing-character-dialog-card").forEach(refreshCard);
+ updateCount();
+ });
+ const confirmNode = makeControl("确定", () => {
+ if (!selectedSet.size) return;
+ close(items.map(item => item.key).filter(key => selectedSet.has(key)));
+ });
+ makeControl("取消", () => close(false));
+ renderGrid();
+ });
+ };
+}
diff --git a/menu.js b/menu.js
new file mode 100644
index 0000000..f5e3267
--- /dev/null
+++ b/menu.js
@@ -0,0 +1,271 @@
+export default function initShuYingMenu({ lib, game, ui, shuYing, updater }) {
+ const menu = lib.extensionMenu.extension_术樱包;
+ const createCollapseMenu = (title, endId) => ({
+ name: ``,
+ clear: true,
+ onclick() {
+ if (!this.shuyingCollapseNodes) {
+ const nodes = [];
+ let node = this.nextSibling;
+
+ while (node && !node.querySelector?.(`#${endId}`)) {
+ nodes.push(node);
+ node = node.nextSibling;
+ }
+ this.shuyingCollapseNodes = nodes;
+ }
+
+ const collapsed = !this.shuyingCollapsed;
+ this.shuyingCollapsed = collapsed;
+ this.shuyingCollapseNodes.forEach(node => {
+ node.style.display = collapsed ? "none" : "";
+ });
+ this.innerHTML = ``;
+ },
+ });
+
+ menu.local_version = {
+ name: `扩展版本:${lib.config.shuYing_local_version}`,
+ clear: true,
+ nopointer: true,
+ };
+
+ menu.online_version = {
+ name: shuYing.getOnlineVersionText(),
+ clear: true,
+ nopointer: true,
+ };
+
+ menu.updateLog = {
+ name: '',
+ clear: true,
+ onclick() {
+ if (this.hth_more) {
+ this.hth_more.remove();
+ delete this.hth_more;
+ this.innerHTML = '';
+ return;
+ }
+
+ const logs = [
+ "-----< 202607 改动 >-----",
+ "新增可以自定义虚拟偶像随机助战池功能",
+ "现在过关奖励提供的技能池会过滤武将牌上的技能",
+ "-----< 202607 修复 >-----",
+ "修复BOSS死亡尸体不会消失的BUG",
+ "修正过关后BOSS的初始手牌不再是摸牌事件",
+ "适配部分技能需要依赖BOSS的起始手牌",
+ "适配部分技能需要选择已经死亡的BOSS为目标时,提供一个按钮来列出已死亡的BOSS列表",
+ "-----< 20260722 改动 >-----",
+ "新增BOSS:玄女!",
+ "不用担心困难,虚拟偶像前来援助!",
+ "新增BOSS选项开启,现在可以在扩展选项中开启新增1位BOSS来提高难度!",
+ "-----< 20260722 修复 >-----",
+ "修复当过关奖励执行完毕轮数和下轮角色异常的BUG",
+ "修复了一个BUG,该BUG会导致进入下关时BOSS的初始牌会被技能检索",
+ "修复黄蜂技能【冥虫】无法正常发动的BUG",
+ "修正司马懿AI向其友方发动【反馈】【狼顾】的问题",
+ ];
+ const keywordColors = {
+ BOSS: "#ff6b6b",
+ 武将牌: "#ff6b6b",
+ BUG: "#ff9800",
+ 虚拟偶像: "#66ccff",
+ 玄女: "#d77cff",
+ "【冥虫】": "#7ed957",
+ "【反馈】": "#7ed957",
+ "【狼顾】": "#7ed957",
+ 黄蜂: "#d77cff",
+ 司马懿: "#d77cff",
+ };
+ const highlightKeywords = text => {
+ for (const [keyword, color] of Object.entries(keywordColors)) {
+ text = text.replaceAll(
+ keyword,
+ `${keyword}`
+ );
+ }
+ return text;
+ };
+ const formatLog = text => {
+ if (text.includes("改动 >")) {
+ return `${text}`;
+ }
+ if (text.includes("修复 >")) {
+ return `${text}`;
+ }
+ return `${highlightKeywords(text)}`;
+ };
+ const more = ui.create.div(
+ ".hth_more",
+ `${logs.map(formatLog).join("
")}
`
+ );
+
+ this.parentNode.insertBefore(more, this.nextSibling);
+ this.hth_more = more;
+ this.innerHTML = '';
+ },
+ };
+
+ menu.updata = {
+ name: "版本检测",
+ clear: true,
+ onclick() {
+ if (confirm("点击确定会检测版本") && shuYing.m_bIsDownload) {
+ shuYing.m_bIsDownload = false;
+ updater.checkVersion(shuYing);
+ } else if (!shuYing.m_bIsDownload) {
+ alert("有其他文件正在下载,请稍后再试吧。");
+ }
+ },
+ };
+
+ menu.repairBug = {
+ name: "本地资源修复",
+ clear: true,
+ onclick() {
+ if (confirm("点击确定会检测本地资源,并尝试修复。") && shuYing.m_bIsDownload) {
+ shuYing.m_bIsDownload = false;
+ shuYing.RepairBug();
+ } else if (!shuYing.m_bIsDownload) {
+ alert("有其他文件正在下载,请稍后再试吧。");
+ }
+ },
+ };
+
+ menu.filling = {
+ name: "查漏补缺 <--补齐丢失资源 建议更新后必点",
+ clear: true,
+ onclick() {
+ if (
+ confirm("点击确定会检测本地丢失资源,并开始修补;若是武将立绘则是静态,若需动态请手动下载(请注意流量使用情况!务必在WIFI状态下修补)") &&
+ shuYing.m_bIsDownload
+ ) {
+ shuYing.m_bIsDownload = false;
+ updater.repairMissingFiles(shuYing);
+ } else if (!shuYing.m_bIsDownload) {
+ alert("有其他文件正在下载,请稍后再试吧。");
+ }
+ },
+ };
+
+ menu.br4 = {
+ clear: true,
+ nopointer: true,
+ name: "--------------------",
+ };
+
+ menu.tianShuGroup = createCollapseMenu("天书乱斗选项", "shuying_tianshu_menu_end");
+
+ menu.tianShuOff = {
+ name: "天书乱斗开关",
+ init: true,
+ intro: "关闭后,关闭天书乱斗模式(在非挑战模式下自动关闭)",
+ };
+
+ menu.giveAiSkill = {
+ name: "单人控制AI技能",
+ init: false,
+ intro: "开启后,天书乱斗模式每次过关且处于单人控制规则下则玩家给AI选择技能。",
+ };
+
+ menu.tianShu_Xvni = {
+ name: "虚拟偶像",
+ init: "random",
+ item: {
+ Xiaojiu: "小酒",
+ Xiaosha: "小杀",
+ Xiaoshan: "小闪",
+ Xiaole: "小乐",
+ Xiaotao: "小桃",
+ random: "随机",
+ off: "关闭",
+ },
+ onclick(layout) {
+ game.saveConfig("extension_术樱包_tianShu_Xvni", layout);
+ },
+ };
+
+ const virtualIdols = [
+ { key: "Xiaojiu", id: "vtb_xiaojiu", name: "小酒" },
+ { key: "Xiaosha", id: "vtb_xiaosha", name: "小杀" },
+ { key: "Xiaoshan", id: "vtb_xiaoshan", name: "小闪" },
+ { key: "Xiaole", id: "vtb_xiaole", name: "小乐" },
+ { key: "Xiaotao", id: "vtb_xiaotao", name: "小桃" },
+ ];
+ const virtualIdolNames = Object.fromEntries(virtualIdols.map(item => [item.key, item.name]));
+ const getVirtualIdolPoolName = () => {
+ const list = lib.config.extension_术樱包_tianShu_XvniRandomPool;
+ if (!Array.isArray(list) || !list.length) return "虚拟偶像随机池:全部";
+ return `虚拟偶像随机池:${list.map(key => virtualIdolNames[key]).filter(Boolean).join("、") || "全部"}`;
+ };
+
+ menu.tianShu_XvniRandomPool = {
+ name: getVirtualIdolPoolName(),
+ clear: true,
+ intro: "点击后选择允许被随机到的虚拟偶像。全选时视为全部随机。",
+ async onclick() {
+ const current =
+ Array.isArray(lib.config.extension_术樱包_tianShu_XvniRandomPool) &&
+ lib.config.extension_术樱包_tianShu_XvniRandomPool.length
+ ? lib.config.extension_术樱包_tianShu_XvniRandomPool
+ : virtualIdols.map(item => item.key);
+ const node = this;
+ const result = await shuYing.chooseCharacterPoolDialog({
+ title: "选择虚拟偶像随机池",
+ intro: "只会影响“虚拟偶像:随机”。全选时保存为全部随机。",
+ list: virtualIdols,
+ selected: current,
+ });
+ if (result === false) return;
+
+ const list = result.length === virtualIdols.length ? [] : result;
+ game.saveConfig("extension_术樱包_tianShu_XvniRandomPool", list);
+ node.firstChild.innerHTML = getVirtualIdolPoolName();
+ },
+ };
+
+ menu.tianShu_dead = {
+ name: "虚拟偶像复活",
+ init: false,
+ intro: "开启后,进入下一关后若虚拟偶像阵亡则复活",
+ };
+
+ menu.tianShu_revivePlayers = {
+ name: "过关复活玩家",
+ init: false,
+ intro: "开启后,进入下一关前复活玩家方已阵亡的角色。虚拟偶像不会因此复活。",
+ };
+
+ menu.tianShu_addBoss = {
+ name: "增加Boss",
+ init: false,
+ intro: "开启后,可以新增一位Boss",
+ };
+
+ menu.br5 = {
+ clear: true,
+ nopointer: true,
+ name: '',
+ };
+
+ menu.Log = {
+ name: "反馈BUG",
+ clear: true,
+ onclick() {
+ game.open("https://tieba.baidu.com/p/7934495658");
+ },
+ };
+
+ menu.qq = {
+ name: "QQ交流群:957537184",
+ clear: true,
+ nopointer: true,
+ };
+
+ menu.thank = {
+ name: "感谢极光佬,诗笺佬和其他大佬网络上的文献。以及反馈BUG的玩家。谢谢!特别感谢少年A对本扩展进行的优化描述。",
+ clear: true,
+ nopointer: true,
+ };
+}
diff --git a/pve/audio/dead/KuaFu_Dead.mp3 b/pve/audio/dead/KuaFu_Dead.mp3
new file mode 100644
index 0000000..ec3f11d
Binary files /dev/null and b/pve/audio/dead/KuaFu_Dead.mp3 differ
diff --git a/pve/audio/dead/十二生肖/chenlong.mp3 b/pve/audio/dead/十二生肖/chenlong.mp3
new file mode 100644
index 0000000..88de656
Binary files /dev/null and b/pve/audio/dead/十二生肖/chenlong.mp3 differ
diff --git a/pve/audio/dead/十二生肖/chouniu.mp3 b/pve/audio/dead/十二生肖/chouniu.mp3
new file mode 100644
index 0000000..09b32a4
Binary files /dev/null and b/pve/audio/dead/十二生肖/chouniu.mp3 differ
diff --git a/pve/audio/dead/十二生肖/haizhu.mp3 b/pve/audio/dead/十二生肖/haizhu.mp3
new file mode 100644
index 0000000..3024033
Binary files /dev/null and b/pve/audio/dead/十二生肖/haizhu.mp3 differ
diff --git a/pve/audio/dead/十二生肖/maotu.mp3 b/pve/audio/dead/十二生肖/maotu.mp3
new file mode 100644
index 0000000..62af6f8
Binary files /dev/null and b/pve/audio/dead/十二生肖/maotu.mp3 differ
diff --git a/pve/audio/dead/十二生肖/shenhou.mp3 b/pve/audio/dead/十二生肖/shenhou.mp3
new file mode 100644
index 0000000..0741c8f
Binary files /dev/null and b/pve/audio/dead/十二生肖/shenhou.mp3 differ
diff --git a/pve/audio/dead/十二生肖/sishe.mp3 b/pve/audio/dead/十二生肖/sishe.mp3
new file mode 100644
index 0000000..fe75880
Binary files /dev/null and b/pve/audio/dead/十二生肖/sishe.mp3 differ
diff --git a/pve/audio/dead/十二生肖/weiyang.mp3 b/pve/audio/dead/十二生肖/weiyang.mp3
new file mode 100644
index 0000000..f4452ae
Binary files /dev/null and b/pve/audio/dead/十二生肖/weiyang.mp3 differ
diff --git a/pve/audio/dead/十二生肖/wuma.mp3 b/pve/audio/dead/十二生肖/wuma.mp3
new file mode 100644
index 0000000..f83d3a0
Binary files /dev/null and b/pve/audio/dead/十二生肖/wuma.mp3 differ
diff --git a/pve/audio/dead/十二生肖/xvgou.mp3 b/pve/audio/dead/十二生肖/xvgou.mp3
new file mode 100644
index 0000000..0207c67
Binary files /dev/null and b/pve/audio/dead/十二生肖/xvgou.mp3 differ
diff --git a/pve/audio/dead/十二生肖/yinhu.mp3 b/pve/audio/dead/十二生肖/yinhu.mp3
new file mode 100644
index 0000000..bbecf76
Binary files /dev/null and b/pve/audio/dead/十二生肖/yinhu.mp3 differ
diff --git a/pve/audio/dead/十二生肖/youji.mp3 b/pve/audio/dead/十二生肖/youji.mp3
new file mode 100644
index 0000000..d306845
Binary files /dev/null and b/pve/audio/dead/十二生肖/youji.mp3 differ
diff --git a/pve/audio/dead/十二生肖/zishu.mp3 b/pve/audio/dead/十二生肖/zishu.mp3
new file mode 100644
index 0000000..1af3e73
Binary files /dev/null and b/pve/audio/dead/十二生肖/zishu.mp3 differ
diff --git a/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3 b/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3
new file mode 100644
index 0000000..9c1d4b2
Binary files /dev/null and b/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3 differ
diff --git a/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3 b/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3
new file mode 100644
index 0000000..f9d7130
Binary files /dev/null and b/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3 differ
diff --git a/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3 b/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3
new file mode 100644
index 0000000..926606d
Binary files /dev/null and b/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3 differ
diff --git a/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3 b/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3
new file mode 100644
index 0000000..2286f16
Binary files /dev/null and b/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3 differ
diff --git a/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3 b/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3
new file mode 100644
index 0000000..a26df55
Binary files /dev/null and b/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3 differ
diff --git a/pve/audio/skill/KuaFu_ZhuRi_01.mp3 b/pve/audio/skill/KuaFu_ZhuRi_01.mp3
new file mode 100644
index 0000000..65e9518
Binary files /dev/null and b/pve/audio/skill/KuaFu_ZhuRi_01.mp3 differ
diff --git a/pve/audio/skill/spell8014_1.mp3 b/pve/audio/skill/spell8014_1.mp3
new file mode 100644
index 0000000..6431060
Binary files /dev/null and b/pve/audio/skill/spell8014_1.mp3 differ
diff --git a/pve/audio/skill/spell8016_1.mp3 b/pve/audio/skill/spell8016_1.mp3
new file mode 100644
index 0000000..a0e87c3
Binary files /dev/null and b/pve/audio/skill/spell8016_1.mp3 differ
diff --git a/pve/audio/skill/spell8017_1.mp3 b/pve/audio/skill/spell8017_1.mp3
new file mode 100644
index 0000000..ded6517
Binary files /dev/null and b/pve/audio/skill/spell8017_1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/chenlong_shuying1.mp3 b/pve/audio/skill/十二生肖/chenlong_shuying1.mp3
new file mode 100644
index 0000000..23cc56a
Binary files /dev/null and b/pve/audio/skill/十二生肖/chenlong_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/chouniu_shuying1.mp3 b/pve/audio/skill/十二生肖/chouniu_shuying1.mp3
new file mode 100644
index 0000000..fe8cfe4
Binary files /dev/null and b/pve/audio/skill/十二生肖/chouniu_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/haizhu_shuying1.mp3 b/pve/audio/skill/十二生肖/haizhu_shuying1.mp3
new file mode 100644
index 0000000..40054e1
Binary files /dev/null and b/pve/audio/skill/十二生肖/haizhu_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/moutu_shuying1.mp3 b/pve/audio/skill/十二生肖/moutu_shuying1.mp3
new file mode 100644
index 0000000..d007ed2
Binary files /dev/null and b/pve/audio/skill/十二生肖/moutu_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/shenhou_shuying1.mp3 b/pve/audio/skill/十二生肖/shenhou_shuying1.mp3
new file mode 100644
index 0000000..a7c5600
Binary files /dev/null and b/pve/audio/skill/十二生肖/shenhou_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/sishe_shuying1.mp3 b/pve/audio/skill/十二生肖/sishe_shuying1.mp3
new file mode 100644
index 0000000..b1dc21d
Binary files /dev/null and b/pve/audio/skill/十二生肖/sishe_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/weiyang_shuying1.mp3 b/pve/audio/skill/十二生肖/weiyang_shuying1.mp3
new file mode 100644
index 0000000..edbd2f0
Binary files /dev/null and b/pve/audio/skill/十二生肖/weiyang_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/wuma_shuying1.mp3 b/pve/audio/skill/十二生肖/wuma_shuying1.mp3
new file mode 100644
index 0000000..1b1714e
Binary files /dev/null and b/pve/audio/skill/十二生肖/wuma_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/xvgou_shuying1.mp3 b/pve/audio/skill/十二生肖/xvgou_shuying1.mp3
new file mode 100644
index 0000000..647097a
Binary files /dev/null and b/pve/audio/skill/十二生肖/xvgou_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/yinhu_shuying1.mp3 b/pve/audio/skill/十二生肖/yinhu_shuying1.mp3
new file mode 100644
index 0000000..b7df02c
Binary files /dev/null and b/pve/audio/skill/十二生肖/yinhu_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/youji_shuying1.mp3 b/pve/audio/skill/十二生肖/youji_shuying1.mp3
new file mode 100644
index 0000000..4149062
Binary files /dev/null and b/pve/audio/skill/十二生肖/youji_shuying1.mp3 differ
diff --git a/pve/audio/skill/十二生肖/zishu_shuying1.mp3 b/pve/audio/skill/十二生肖/zishu_shuying1.mp3
new file mode 100644
index 0000000..0797e4b
Binary files /dev/null and b/pve/audio/skill/十二生肖/zishu_shuying1.mp3 differ
diff --git a/pve/audio/skill/天书乱斗/dishi_shuying.mp3 b/pve/audio/skill/天书乱斗/dishi_shuying.mp3
new file mode 100644
index 0000000..a71a4c3
Binary files /dev/null and b/pve/audio/skill/天书乱斗/dishi_shuying.mp3 differ
diff --git a/pve/audio/skill/天书乱斗/jiutian_shuying.mp3 b/pve/audio/skill/天书乱斗/jiutian_shuying.mp3
new file mode 100644
index 0000000..45ca089
Binary files /dev/null and b/pve/audio/skill/天书乱斗/jiutian_shuying.mp3 differ
diff --git a/pve/audio/skill/天书乱斗/xuanlie_shuying.mp3 b/pve/audio/skill/天书乱斗/xuanlie_shuying.mp3
new file mode 100644
index 0000000..91a8839
Binary files /dev/null and b/pve/audio/skill/天书乱斗/xuanlie_shuying.mp3 differ
diff --git a/pve/audio/skill/青青子衿/baonue_shuying1.mp3 b/pve/audio/skill/青青子衿/baonue_shuying1.mp3
new file mode 100644
index 0000000..3c0dd43
Binary files /dev/null and b/pve/audio/skill/青青子衿/baonue_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/fankui_shuying1.mp3 b/pve/audio/skill/青青子衿/fankui_shuying1.mp3
new file mode 100644
index 0000000..908bb86
Binary files /dev/null and b/pve/audio/skill/青青子衿/fankui_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/guicai_shuying1.mp3 b/pve/audio/skill/青青子衿/guicai_shuying1.mp3
new file mode 100644
index 0000000..271588a
Binary files /dev/null and b/pve/audio/skill/青青子衿/guicai_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/jianxiong_shuying1.mp3 b/pve/audio/skill/青青子衿/jianxiong_shuying1.mp3
new file mode 100644
index 0000000..66520f7
Binary files /dev/null and b/pve/audio/skill/青青子衿/jianxiong_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/jiuchi_shuying1.mp3 b/pve/audio/skill/青青子衿/jiuchi_shuying1.mp3
new file mode 100644
index 0000000..9ef04fe
Binary files /dev/null and b/pve/audio/skill/青青子衿/jiuchi_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/langgu_shuying1.mp3 b/pve/audio/skill/青青子衿/langgu_shuying1.mp3
new file mode 100644
index 0000000..ba42fcf
Binary files /dev/null and b/pve/audio/skill/青青子衿/langgu_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/lingba_shuying1.mp3 b/pve/audio/skill/青青子衿/lingba_shuying1.mp3
new file mode 100644
index 0000000..9ab259b
Binary files /dev/null and b/pve/audio/skill/青青子衿/lingba_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/qubu_shuying1.mp3 b/pve/audio/skill/青青子衿/qubu_shuying1.mp3
new file mode 100644
index 0000000..c6e1905
Binary files /dev/null and b/pve/audio/skill/青青子衿/qubu_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/roulin_shuying1.mp3 b/pve/audio/skill/青青子衿/roulin_shuying1.mp3
new file mode 100644
index 0000000..a935182
Binary files /dev/null and b/pve/audio/skill/青青子衿/roulin_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/shenij_shuying1.mp3 b/pve/audio/skill/青青子衿/shenij_shuying1.mp3
new file mode 100644
index 0000000..3dfa7ae
Binary files /dev/null and b/pve/audio/skill/青青子衿/shenij_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/shenij_shuying2.mp3 b/pve/audio/skill/青青子衿/shenij_shuying2.mp3
new file mode 100644
index 0000000..b1f3995
Binary files /dev/null and b/pve/audio/skill/青青子衿/shenij_shuying2.mp3 differ
diff --git a/pve/audio/skill/青青子衿/wushuang_shuying1.mp3 b/pve/audio/skill/青青子衿/wushuang_shuying1.mp3
new file mode 100644
index 0000000..d440921
Binary files /dev/null and b/pve/audio/skill/青青子衿/wushuang_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/wushuang_shuying2.mp3 b/pve/audio/skill/青青子衿/wushuang_shuying2.mp3
new file mode 100644
index 0000000..2507a35
Binary files /dev/null and b/pve/audio/skill/青青子衿/wushuang_shuying2.mp3 differ
diff --git a/pve/audio/skill/青青子衿/yishen_shuying1.mp3 b/pve/audio/skill/青青子衿/yishen_shuying1.mp3
new file mode 100644
index 0000000..3976627
Binary files /dev/null and b/pve/audio/skill/青青子衿/yishen_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/yuanlv_shuying1.mp3 b/pve/audio/skill/青青子衿/yuanlv_shuying1.mp3
new file mode 100644
index 0000000..d548b90
Binary files /dev/null and b/pve/audio/skill/青青子衿/yuanlv_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/zhanjia_shuying1.mp3 b/pve/audio/skill/青青子衿/zhanjia_shuying1.mp3
new file mode 100644
index 0000000..17cec94
Binary files /dev/null and b/pve/audio/skill/青青子衿/zhanjia_shuying1.mp3 differ
diff --git a/pve/audio/skill/青青子衿/zhanjia_shuying2.mp3 b/pve/audio/skill/青青子衿/zhanjia_shuying2.mp3
new file mode 100644
index 0000000..d93b6a1
Binary files /dev/null and b/pve/audio/skill/青青子衿/zhanjia_shuying2.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/anchao_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/anchao_shuying1.mp3
new file mode 100644
index 0000000..9580a90
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/anchao_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/duane_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/duane_shuying1.mp3
new file mode 100644
index 0000000..c9ff749
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/duane_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/duzhen_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/duzhen_shuying1.mp3
new file mode 100644
index 0000000..6623679
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/duzhen_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/guixi_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/guixi_shuying1.mp3
new file mode 100644
index 0000000..710f203
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/guixi_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/huiyun_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/huiyun_shuying1.mp3
new file mode 100644
index 0000000..f0372d0
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/huiyun_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/mingchong_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/mingchong_shuying1.mp3
new file mode 100644
index 0000000..68c9b6a
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/mingchong_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/mizui_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/mizui_shuying1.mp3
new file mode 100644
index 0000000..fd23ad0
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/mizui_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/qiangzheng_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/qiangzheng_shuying1.mp3
new file mode 100644
index 0000000..839b35c
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/qiangzheng_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/taiping_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/taiping_shuying1.mp3
new file mode 100644
index 0000000..7fc9431
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/taiping_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/xixing_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/xixing_shuying1.mp3
new file mode 100644
index 0000000..fef6098
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/xixing_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/yezhong_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/yezhong_shuying1.mp3
new file mode 100644
index 0000000..aa6468e
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/yezhong_shuying1.mp3 differ
diff --git a/pve/audio/skill/驱鬼逐邪/zhoucha_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/zhoucha_shuying1.mp3
new file mode 100644
index 0000000..f297654
Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/zhoucha_shuying1.mp3 differ
diff --git a/pve/boss/character.js b/pve/boss/character.js
new file mode 100644
index 0000000..9c50019
--- /dev/null
+++ b/pve/boss/character.js
@@ -0,0 +1,217 @@
+const characters = {
+ zishu_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["zishu_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/zishu_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/zishu.mp3"]
+ },
+ // chouniu_boss_shuying: {},
+ // yinhu_boss_shuying: {},
+ // maotu_boss_shuying: {},
+ chenlong_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["chenlong_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/chenlong_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/chenlong.mp3"]
+ },
+ // sishe_boss_shuying: {},
+ wuma_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["wuma_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/wuma_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/wuma_shuying.mp3"]
+ },
+ weiyang_boss_shuying: {
+ sex: "female",
+ group: "qun",
+ hp: 4,
+ skills: ["weiyang_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/weiyang_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/weiyang.mp3"]
+ },
+ shenhou_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["shenhou_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/shenhou_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/shenhou.mp3"]
+ },
+ // youji_boss_shuying: {},
+ xvgou_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["xvgou_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/十二生肖/xvgou_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/xvgou.mp3"]
+ },
+ // haizhu_boss_shuying: {},
+
+ heibaiwuchang_boss_shuying:
+ {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["mizui_shuying", "qiangzheng_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png"
+ },
+ niutoumamian_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["shiyv_shuying", "manji_shuying", "xiaoshou_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png"
+ },
+ yvsai_boss_shuying: {
+ sex: "female",
+ group: "qun",
+ hp: 4,
+ skills: ["anchao_shuying", "guixi_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/捉鬼驱邪/yvsai_boss_shuying.png"
+ },
+ huangfeng_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["mingchong_shuying", "duzhen_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png"
+ },
+ riyeyoushen_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["huiyun_shuying", "yezhong_shuying", "zhoucha_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png"
+ },
+
+
+
+ caocao_boss_shuying: {
+ sex: "male",
+ group: "wei",
+ hp: 4,
+ skills: ["jianxiong_shuying", "lingba_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/青青子衿/caocao_boss_shuying.jpg",
+ dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3"]
+ },
+ simayi_boss_shuying: {
+ sex: "male",
+ group: "wei",
+ hp: 4,
+ skills: ["fankui_shuying", "guicai_shuying", "langgu_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/青青子衿/simayi_boss_shuying.jpg",
+ dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3"]
+ },
+ lvbu_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["mashu", "wushuang_shuying", "shenij_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/青青子衿/lvbu_boss_shuying.jpg",
+ dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3"]
+ },
+ dongzhuo_boss_shuying: {
+ sex: "male",
+ group: "qun",
+ hp: 4,
+ skills: ["jiuchi_shuying", "roulin_shuying", "baonue_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/青青子衿/dongzhuo_boss_shuying.jpg",
+ dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3"]
+ },
+
+ shuishengonggong_boss_shuying:
+ {
+ sex: "male",
+ group: "shen",
+ hp: 4,
+ skills: ["tuanliu_shuying", "juehong_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/天书乱斗/shuishengonggong_boss_shuying.png"
+ },
+ // huoshenzhurong_boss_shuying: {},
+
+
+ baiqi_boss_shuying:
+ {
+ sex: "male",
+ group: "shen",
+ hp: 4,
+ skills: ["changsheng_shuying", "shashen_shuying", "wuan_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/天书乱斗/baiqi_boss_shuying.png"
+ },
+
+ kuafu_boss_shuying:
+ {
+ sex: "male",
+ group: "shen",
+ hp: 4,
+ skills: ["shenqu_shuying", "lieben_shuying", "yinjiang_shuying", "zhuri_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/天书乱斗/kuafu_boss_shuying.png"
+ },
+
+ shaohao_boss_shuying:
+ {
+ sex: "male",
+ group: "shen",
+ hp: 4,
+ skills: ["baiyi_shuying", "shenen_shuying",],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/天书乱斗/shaohao_boss_shuying.png"
+ },
+ xuannv_boss_shuying: {
+ sex: "female",
+ group: "shen",
+ hp: 4,
+ skills: ["xuanlie_shuying", "jiutian_shuying", "dishi_shuying"],
+ isHiddenBoss: true,
+ isBossAllowed: true,
+ img: "extension/术樱包/pve/images/天书乱斗/xuannv_boss_shuying.png",
+ dieAudios: ["ext:术樱包/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3"]
+ },
+
+};
+
+export default characters;
diff --git a/pve/boss/characterFilter.js b/pve/boss/characterFilter.js
new file mode 100644
index 0000000..ef70aca
--- /dev/null
+++ b/pve/boss/characterFilter.js
@@ -0,0 +1,4 @@
+const characterFilters = {
+};
+
+export default characterFilters;
diff --git a/pve/boss/characterTitle.js b/pve/boss/characterTitle.js
new file mode 100644
index 0000000..b901a42
--- /dev/null
+++ b/pve/boss/characterTitle.js
@@ -0,0 +1,4 @@
+const characterTitles = {
+};
+
+export default characterTitles;
diff --git a/pve/boss/dynamicTranslate.js b/pve/boss/dynamicTranslate.js
new file mode 100644
index 0000000..2df76fb
--- /dev/null
+++ b/pve/boss/dynamicTranslate.js
@@ -0,0 +1,187 @@
+import { lib, game, ui, get, ai, _status } from "../../../../noname.js";
+import tianshuConfig from "../../tianshu/config.js";
+
+const getTianshuDifficulty = () => {
+ return _status[tianshuConfig.settings.difficultyStatusKey] || "normal";
+};
+
+const dynamicTranslates = {
+ //黑白无常
+ mizui_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "normal" ? 1 : 2
+
+ return `锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的${num}张牌。`
+ },
+ qiangzheng_shuying(player, skill) {
+ let num = 2;
+ if (getTianshuDifficulty() == "hard") num = 4;
+ if (getTianshuDifficulty() == "nightmare") num = 6;
+
+ return `锁定技,敌方角色的结束阶段,若其手牌数小于${num},你获得其所有手牌。`
+ },
+ xixing_shuying(player, skill) {
+ const difficulty = getTianshuDifficulty();
+ if (difficulty == "nightmare") {
+ return "锁定技,准备阶段,你对每名敌方角色各造成2点雷电伤害,然后你回复等量体力";
+ }
+
+ return "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力。";
+ },
+
+ //鱼鳃
+ guixi_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
+
+ return `锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸${num}张牌。`;
+ },
+
+ //黄蜂
+ mingchong_shuying(player, skill) {
+ if (getTianshuDifficulty() == "nightmare") {
+ return "锁定技,你死亡时,杀死你的角色弃置所有牌。"
+ }
+
+ return "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。"
+ },
+ duzhen_shuyingg(player, skill) {
+ if (getTianshuDifficulty() == "nightmare") {
+ return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。"
+ }
+
+ return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置2张牌(优先装备区的牌)。"
+
+ },
+
+ //日夜游神
+ mingchong_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "normal" ? 1 : 2
+
+ return `出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成${num}点伤害。`
+ },
+ yezhong_shuying(player, skill) {
+ let num = 1;
+ if (getTianshuDifficulty() == "hard") num = 2;
+ if (getTianshuDifficulty() == "nightmare") num = 3;
+
+ return `锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃${num}张手牌。`
+ },
+ zhoucha_shuying(player, skill) {
+ let num = 1;
+ if (getTianshuDifficulty() == "hard") num = 2;
+ if (getTianshuDifficulty() == "nightmare") num = 3;
+
+ return `锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+${num}。`
+ },
+
+
+ //曹操
+ lingba_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
+
+ return `锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成${num}点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。`
+ },
+ yishen_shuying(player, skill) {
+ const difficulty = getTianshuDifficulty();
+ if (difficulty == "nightmare") {
+ return "当你回复体力时,可以改为获得所有敌人各一张随机装备。";
+ }
+
+ return "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。";
+ },
+
+ //司马懿
+ yuanlv_shuying(player, skill) {
+ const difficulty = getTianshuDifficulty();
+
+ if (difficulty == "nightmare") {
+ return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成过1点伤害。";
+ }
+
+ return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。";
+
+ },
+
+ //吕布
+ wushuang_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
+
+ return `锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限${num}次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。`;
+ },
+ shenij_shuying(player, skill) {
+ const difficulty = getTianshuDifficulty();
+ let card = 2;
+ let sha = 1;
+ let target = 1;
+
+ if (difficulty == "hard") {
+ card = 3;
+ sha = 2;
+ } else if (difficulty == "nightmare") {
+ card = 4;
+ sha = 2;
+ target = 2;
+ }
+
+ return `判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸${card}张牌;出牌阶段,你可以多使用${sha}张【杀】,你的【杀】可以多指定${target}名角色为目标。`;
+ },
+ zhanjia_shuying(player, skill) {
+ const num = getTianshuDifficulty() == "nightmare" ? 3 : 2
+
+ return `锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸${num}张牌。`
+ },
+
+ //董卓
+ baonue_shuying(player, skill) {
+ let num = 3;
+ if (getTianshuDifficulty() == "hard") num = 4;
+ if (getTianshuDifficulty() == "nightmare") num = 5;
+
+ return `锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为${num}。`
+ },
+
+ //水神共工
+ tuanliu_shuying(player, skill) {
+ let hp = 2;
+ let card = 4;
+ let damage = 1;
+
+ if (getTianshuDifficulty() == "nightmare") {
+ hp = 3;
+ card = 6;
+ }
+ return `锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复${hp}点体力;大于5,你摸${card}张牌;大于9,你对所有敌方角色造成${damage}点伤害。`
+ },
+
+ //少昊
+ baiyi_shuying(player, skill) {
+ let num = 1;
+ const difficulty = getTianshuDifficulty();
+ if (difficulty == "hard") {
+ num = 2;
+ } else if (difficulty == "nightmare") {
+ num = 3;
+ }
+
+ return `锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其${num}张牌;若当前轮数小于5,对其造成${num}点雷电伤害;若当前轮数小于7,其随机弃置${num}张牌。`;
+ },
+
+ //玄女
+ xuanlie_shuying(player, skill) {
+ let num = 2;
+ if (getTianshuDifficulty() == "normal") num = 1;
+
+ return `锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成${num}点伤害。`;
+ },
+ jiutian_shuying(player, skill) {
+ const difficulty = getTianshuDifficulty();
+ const num = difficulty == "normal" ? 1 : 2;
+ const extra =
+ difficulty == "normal" || difficulty == "hard"
+ ? ""
+ : "若这些牌包含4种花色,这些角色再额外失去1点体力。";
+
+ return `锁定技,准备阶段,你获得所有敌方角色各${num}张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。${extra}`;
+ }
+};
+
+export default dynamicTranslates;
diff --git a/pve/boss/index.js b/pve/boss/index.js
new file mode 100644
index 0000000..2cefd62
--- /dev/null
+++ b/pve/boss/index.js
@@ -0,0 +1,28 @@
+import characters from "./character.js";
+import skills from "./skill.js";
+import translates from "./translate.js";
+import characterFilters from "./characterFilter.js";
+import characterTitles from "./characterTitle.js";
+import characterIntros from "./intro.js";
+import dynamicTranslates from "./dynamicTranslate.js";
+import perfectPairs from "./perfectPairs.js";
+import voices from "./voices.js";
+import { characterSort, characterSortTranslate } from "./sort.js";
+
+const shuying_boss = {
+ name: "shuYing_pve_boss",
+ connect: true,
+ character: { ...characters },
+ characterSort: {
+ shuying_boss: characterSort,
+ },
+ characterFilter: { ...characterFilters },
+ characterTitle: { ...characterTitles },
+ dynamicTranslate: { ...dynamicTranslates },
+ characterIntro: { ...characterIntros },
+ skill: { ...skills },
+ perfectPair: { ...perfectPairs },
+ translate: { ...translates, ...voices, ...characterSortTranslate },
+};
+
+export default shuying_boss;
diff --git a/pve/boss/intro.js b/pve/boss/intro.js
new file mode 100644
index 0000000..15a8a83
--- /dev/null
+++ b/pve/boss/intro.js
@@ -0,0 +1,4 @@
+const characterIntros = {
+};
+
+export default characterIntros;
diff --git a/pve/boss/perfectPairs.js b/pve/boss/perfectPairs.js
new file mode 100644
index 0000000..47a0a7b
--- /dev/null
+++ b/pve/boss/perfectPairs.js
@@ -0,0 +1,4 @@
+const perfectPairs = {
+};
+
+export default perfectPairs;
diff --git a/pve/boss/skill.js b/pve/boss/skill.js
new file mode 100644
index 0000000..944e815
--- /dev/null
+++ b/pve/boss/skill.js
@@ -0,0 +1,2020 @@
+import { lib, game, ui, get, ai, _status } from "../../../../noname.js";
+import tianshuConfig from "../../tianshu/config.js";
+
+const getTianshuDifficulty = () => {
+ return _status[tianshuConfig.settings.difficultyStatusKey] || "normal";
+};
+
+const isTianshuDifficultyAtLeast = difficulty => {
+ return tianshuDifficultyRank[getTianshuDifficulty()] >= tianshuDifficultyRank[difficulty];
+};
+
+const skills = {
+ //子鼠
+ zishu_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ enable: "phaseUse",
+ usable: 1,
+ filter(event, player) {
+ return game.hasPlayer(current => {
+ return current != player && current.countCards("h") > player.countCards("h");
+ });
+ },
+ async content(event, trigger, player) {
+ while (player.isIn()) {
+ const max = Math.max(...game.players.map(current => current.countCards("h")));
+ if (player.countCards("h") >= max) break;
+
+ const targets = game.filterPlayer(current => {
+ return current != player && current.countCards("h") > player.countCards("h");
+ });
+ if (!targets.length) break;
+
+ const hasEnemy = targets.some(target => player.getEnemies(null, false).includes(target));
+
+ const result = await player
+ .chooseTarget("恣疏:获得一名手牌数大于你的角色一张手牌", (card, player, target) => {
+ return target != player && target.countCards("h") > player.countCards("h");
+ })
+ .set("ai", target => {
+ const player = get.player();
+ const enemies = player.getEnemies(null, false);
+ const isEnemy = enemies.includes(target);
+ const hasEnemy = game.hasPlayer(current => {
+ return current != player && current.countCards("h") > player.countCards("h") && enemies.includes(current);
+ });
+
+ if (hasEnemy && !isEnemy) return 0;
+ if (isEnemy) return 8 + target.countCards("h") - get.attitude(player, target);
+ return 1 + target.countCards("h") / 10;
+ })
+ .forResult();
+
+ if (!result.bool) break;
+
+ const target = result.targets[0];
+ player.line(target);
+ await player.gainPlayerCard({
+ target,
+ position: "h",
+ forced: true,
+ });
+ }
+ },
+ ai: {
+ order: 4.5,
+ result: {
+ player(player) {
+ const enemies = player.getEnemies(null, false);
+ if (game.hasPlayer(current => current != player && current.countCards("h") > player.countCards("h") && enemies.includes(current))) {
+ return 1;
+ }
+ if (game.hasPlayer(current => current != player && current.countCards("h") > player.countCards("h") && !enemies.includes(current))) {
+ return 0.2;
+ }
+ return 0;
+ },
+ },
+ threaten: 1.4,
+ },
+ },
+
+ //辰龙
+ chenlong_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ enable: "phaseUse",
+ limited: true,
+ skillAnimation: true,
+ animationColor: "fire",
+ selectTarget: 1,
+ filter(event, player) {
+ return !player.storage.chenlong_shuying_used && game.hasPlayer(current => current != player && player.getEnemies(null, false).includes(current));
+ },
+ filterTarget(card, player, target) {
+ return target != player && player.getEnemies(null, false).includes(target);
+ },
+ async content(event, trigger, player) {
+ const target = event.targets[0];
+ const result = await player.chooseControl([1, 2, 3, 4, 5])
+ .set("prompt", "失去任意点体力,然后对一名其他角色造成等量伤害")
+ .set("ai", () => 4)
+ .forResult();
+ const num = result.control;
+ if (!num) return;
+ player.storage.chenlong_shuying_used = true;
+ player.storage.chenlong_shuying_dying = true;
+ await player.loseHp(num);
+ delete player.storage.chenlong_shuying_dying;
+ player.awakenSkill(event.name);
+ if (target.isIn()) {
+ player.line(target);
+ await target.damage(num, player);
+ }
+ },
+ ai: {
+ order: 4,
+ result: {
+ target(player, target) {
+ if (!player.getEnemies(null, false).includes(target)) return 0;
+ const effect = get.damageEffect(target, player, target);
+ if (effect >= 0) return 0;
+ return target.hp <= 2 ? effect * 2 : effect;
+ },
+ },
+ },
+ group: "chenlong_shuying_dying",
+ subSkill: {
+ dying: {
+ trigger: { player: ["dying", "dyingAfter"] },
+ forced: true,
+ popup: false,
+ charlotte: true,
+ filter(event, player) {
+ return player.storage.chenlong_shuying_dying && player.hp <= 0;
+ },
+ async content(event, trigger, player) {
+ await player.recoverTo(1);
+ if (player.maxHp > 0) await player.loseMaxHp();
+ },
+ },
+ },
+ },
+
+ //午马
+ wuma_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ trigger: { target: "useCardToTargeted" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return event.player != player && get.type2(event.card) == "trick";
+ },
+ async content(event, trigger, player) {
+ await player.draw();
+ },
+ group: ["wuma_shuying_turn", "wuma_shuying_skip"],
+ subSkill: {
+ turn: {
+ audio: "wuma_shuying",
+ trigger: { player: "turnOverBefore" },
+ forced: true,
+ filter(event, player) {
+ return !player.isTurnedOver();
+ },
+ async content(event, trigger, player) {
+ trigger.cancel();
+ },
+ },
+ skip: {
+ audio: "wuma_shuying",
+ trigger: { player: ["phaseAnySkipped", "phaseAnyCancelled"] },
+ forced: true,
+ async content(event, trigger, player) {
+ game.log(player, "恢复了", trigger.name);
+ await player[trigger.name]();
+ },
+ },
+ },
+ ai: {
+ effect: {
+ target(card, player, target) {
+ if (player != target && get.type2(card) == "trick") return [1, 0.5];
+ },
+ },
+ },
+ },
+ //未羊
+ weiyang_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ enable: "phaseUse",
+ usable: 1,
+ position: "he",
+ selectCard: [1, Infinity],
+ filter(event, player) {
+ return game.hasPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
+ },
+ filterCard(card, player) {
+ const type = get.type(card);
+ const friends = game.countPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
+ return ui.selected.cards.length < friends && !ui.selected.cards.some(cardx => get.type(cardx) == type);
+ },
+ check(card) {
+ return 6 - get.value(card);
+ },
+ async content(event, trigger, player) {
+ const num = event.cards.length;
+ const friends = game.filterPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
+ if (!friends.length) return;
+ const result = await player.chooseTarget(`令至多${get.cnNumber(num)}名角色回复1点体力`, [1, Math.min(num, friends.length)], (card, player, target) => {
+ return target.isDamaged() && !player.getEnemies(null, false).includes(target);
+ }).set("ai", target => get.recoverEffect(target, get.player(), get.player())).forResult();
+ if (result.bool) {
+ for (const target of result.targets) {
+ player.line(target);
+ await target.recover();
+ }
+ }
+ },
+ ai: {
+ order: 6,
+ result: {
+ player(player) {
+ return game.hasPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current) && get.recoverEffect(current, player, player) > 0) ? 1 : 0;
+ },
+ },
+ },
+ },
+
+ //申猴
+ shenhou_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ trigger: { target: "useCardToTargeted" },
+ filter(event, player) {
+ return event.card.name == "sha";
+ },
+ async cost(event, trigger, player) {
+ event.result = await player
+ .chooseBool(get.prompt2(event.skill))
+ .set("ai", () => {
+ const player = get.player();
+ const trigger = get.event().getTrigger();
+ return get.effect(player, trigger.card, trigger.player, player) < 0;
+ })
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ const result = await player
+ .judge(card => get.color(card) == "red" ? 2 : -1)
+ .set("judge2", result => result.bool)
+ .forResult();
+
+ if (result.bool) {
+ trigger.getParent().excluded.add(player);
+ }
+ },
+ ai: {
+ effect: {
+ target(card, player, target) {
+ if (card.name == "sha") return [1, 0.6];
+ },
+ },
+ },
+ },
+
+ //戌狗
+ xvgou_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
+ forced: true,
+ group: ["xvgou_shuying_cancel", "xvgou_shuying_damage"],
+ mod: {
+ targetInRange(card, player, target) {
+ if (card.name == "sha" && get.color(card) == "red") return true;
+ },
+ },
+ subSkill: {
+ cancel: {
+ trigger: { target: "useCardToTargeted" },
+ forced: true,
+ filter(event, player) {
+ return event.card.name == "sha" && get.color(event.card) == "red";
+ },
+ async content(event, trigger, player) {
+ trigger.getParent().excluded.add(player);
+ },
+ },
+ damage: {
+ trigger: { source: "damageBegin1" },
+ forced: true,
+ filter(event, player) {
+ return event.card?.name == "sha" && get.color(event.card) == "red";
+ },
+ async content(event, trigger, player) {
+ trigger.num++;
+ },
+ },
+ },
+ },
+
+ //黑白无常 over
+ mizui_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { source: "damageEnd" },
+ forced: true,
+ filter(event, player) {
+ return event.player?.isIn() && event.player.countDiscardableCards(player, "he") > 0 && event.card && get.is.damageCard(event.card);
+ },
+ async content(event, trigger, player) {
+ const num = getTianshuDifficulty() == "normal" ? 1 : 2;
+
+ player.line(trigger.player);
+ await player.discardPlayerCard(trigger.player, num, "he", true);
+ },
+ ai: { expose: 0.2 },
+ },
+ qiangzheng_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { global: "phaseEnd" },
+ forced: true,
+ logTarget: "player",
+ filter(event, player) {
+ let num = 2;
+ if (getTianshuDifficulty() == "hard") num = 4;
+ if (getTianshuDifficulty() == "nightmare") num = 6;
+
+ return event.player?.isIn() && player.getEnemies(null, false).includes(event.player) && event.player.countCards("h") > 0 && event.player.countCards("h") < num;
+ },
+ async content(event, trigger, player) {
+ player.line(trigger.player);
+ await player.gain(trigger.player.getCards("h"), trigger.player, "giveAuto");
+ },
+ },
+ xixing_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "phaseZhunbeiBegin" },
+ forced: true,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn());
+ },
+ async content(event, trigger, player) {
+ const enemies = player.getEnemies(null, false).filter(target => target.isIn());
+
+ if (difficulty == "nightmare") {
+ for (const target of enemies) {
+ player.line(target, "thunder");
+ await target.damage(2, "thunder", player);
+ }
+ } else {
+ const max = Math.max(...enemies.map(target => target.hp));
+ const target = enemies.filter(target => target.hp == max).randomGet();
+ player.line(target, "thunder");
+ await target.damage(2, "thunder", player);
+ }
+
+ await player.recover(2);
+ },
+ },
+ taiping_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "damageEnd" },
+ forced: true,
+ locked: true,
+ logTarget: "source",
+ filter(event, player) {
+ return (
+ event.num > 0 &&
+ event.source?.isIn() &&
+ player.getEnemies(null, false).includes(event.source)
+ );
+ },
+ async content(event, trigger, player) {
+ const source = trigger.source;
+ player.line(source);
+
+ const result = await source
+ .chooseToDiscard("h", 2, `太平:弃置两张花色不同的手牌,否则失去1点体力`, card => {
+ const cards = ui.selected.cards;
+ return !cards.some(cardx => get.suit(cardx) == get.suit(card));
+ })
+ .set("ai", card => {
+ const player = get.player();
+ if (player.hp <= 1) return 8 - get.value(card);
+ return 6 - get.value(card);
+ })
+ .forResult();
+
+ if (!result.bool) {
+ await source.loseHp();
+ }
+ },
+ ai: {
+ maixie_defend: true,
+ effect: {
+ target(card, player, target) {
+ if (get.tag(card, "damage") && target.getEnemies(null, false).includes(player)) {
+ if (player.countCards("h") < 2) return [1, -1];
+ return [1, 0.4];
+ }
+ },
+ },
+ },
+ },
+
+ //牛头马面
+ shiyv_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "phaseDrawBegin2" },
+ forced: true,
+ filter(event, player) {
+ return !event.numFixed;
+ },
+ async content(event, trigger, player) {
+ trigger.num = 0;
+ const cards = [];
+ for (const suit of lib.suit) {
+ const card = get.cardPile(card => get.suit(card) == suit, "cardPile", "random");
+ if (card) cards.push(card);
+ }
+ if (cards.length) await player.gain(cards, "gain2");
+ },
+ },
+ manji_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "useCardToPlayered" },
+ filter(event, player) {
+ return event.card.name == "sha" && event.targets?.length == 1 && player.getEnemies(null, false).includes(event.target) && event.target.countDiscardableCards(player, "h") > 0;
+ },
+ async cost(event, trigger, player) {
+ event.result = await player.chooseBool(get.prompt(event.skill, trigger.target))
+ .set("ai", () => get.attitude(get.player(), get.event().getTrigger().target) < 0)
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ const target = trigger.target;
+ player.line(target);
+ const cards = target.getCards("h").randomGets(Math.min(2, target.countCards("h")));
+ await target.discard(cards);
+ if (cards.some(card => get.name(card, target) == "sha")) trigger.getParent().baseDamage++;
+ },
+ ai: { expose: 0.2 },
+ },
+ xiaoshou_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "phaseZhunbeiBegin" },
+ forced: true,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn());
+ },
+ async content(event, trigger, player) {
+ const target = player.getEnemies(null, false).filter(target => target.isIn()).randomGet();
+ player.line(target);
+ await target.damage(2, player);
+ },
+ },
+
+ //鱼鳃 over
+ anchao_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ forced: true,
+ locked: true,
+ group: ["anchao_shuying_gain", "anchao_shuying_start"],
+ marktext: "潮",
+ intro: {
+ name: "暗潮",
+ content: "mark",
+ },
+ subSkill: {
+ gain: {
+ audio: "anchao_shuying",
+ trigger: { global: "phaseJieshuBegin" },
+ forced: true,
+ logTarget: "player",
+ filter(event, player) {
+ return (
+ event.player?.isIn() &&
+ !player.getEnemies(null, false).includes(event.player) &&
+ !event.player.getHistory("sourceDamage", evt => evt.num > 0).length
+ );
+ },
+ async content(event, trigger, player) {
+ trigger.player.addMark("anchao_shuying", 1);
+ },
+ },
+ start: {
+ audio: "anchao_shuying",
+ trigger: { global: "phaseBegin" },
+ forced: true,
+ logTarget: "player",
+ filter(event, player) {
+ return (
+ event.player?.isIn() &&
+ !player.getEnemies(null, false).includes(event.player) &&
+ event.player.countMark("anchao_shuying") > 0
+ );
+ },
+ async content(event, trigger, player) {
+ const target = trigger.player;
+ target.storage.anchao_shuying_effect = target.countMark("anchao_shuying");
+ target.addTempSkill("anchao_shuying_effect", { player: "phaseAfter" });
+ },
+ },
+ effect: {
+ charlotte: true,
+ group: ["anchao_shuying_draw", "anchao_shuying_damage"],
+ onremove(player) {
+ delete player.storage.anchao_shuying_effect;
+ },
+ },
+ draw: {
+ audio: "anchao_shuying",
+ trigger: { player: "phaseDrawBegin2" },
+ forced: true,
+ filter(event, player) {
+ return !event.numFixed && player.storage.anchao_shuying_effect > 0;
+ },
+ async content(event, trigger, player) {
+ trigger.num += player.storage.anchao_shuying_effect;
+ },
+ },
+ damage: {
+ audio: "anchao_shuying",
+ trigger: { source: "damageBegin1" },
+ forced: true,
+ filter(event, player) {
+ return (
+ player.storage.anchao_shuying_effect > 0 &&
+ event.player?.isIn() &&
+ player.getEnemies(null, false).includes(event.player)
+ );
+ },
+ async content(event, trigger, player) {
+ trigger.num += player.storage.anchao_shuying_effect;
+ },
+ },
+ },
+ ai: {
+ threaten: 2,
+ },
+ },
+ guixi_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "damageEnd" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return event.num > 0;
+ },
+ async content(event, trigger, player) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
+ const result = await player
+ .judge(card => get.color(card) == "red" ? 2 : 0)
+ .set("judge2", result => result.bool)
+ .forResult();
+
+ if (result.bool && player.isDamaged()) {
+ await player.recover();
+ }
+ else {
+ await player.draw(num);
+ }
+ },
+ ai: {
+ maixie: true,
+ maixie_hp: true,
+ effect: {
+ target(card, player, target) {
+ if (get.tag(card, "damage")) {
+ if (player.hasSkillTag("jueqing", false, target)) return [1, -1];
+ return [1, 0.7];
+ }
+ },
+ },
+ },
+ },
+
+ //黄蜂 over
+ mingchong_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "die" },
+ forced: true,
+ locked: true,
+ forceDie: true,
+ logTarget: "source",
+ filter(event, player) {
+ return event.source?.isIn() && event.source.countDiscardableCards(player, "he") > 0;
+ },
+ async content(event, trigger, player) {
+ const source = trigger.source;
+ player.line(source);
+
+ const cards = source
+ .getCards("he")
+ .filter(card => lib.filter.cardDiscardable(card, source, "mingchong_shuying"));
+
+ if (cards.length) {
+ if (getTianshuDifficulty() == "nightmare") {
+ await source.discard(cards);
+ }
+ else {
+ await source.discard(cards.randomGets(Math.ceil(cards.length / 2)));
+ }
+ }
+ },
+ ai: {
+ threaten: 0.8,
+ effect: {
+ target(card, player, target) {
+ if (get.tag(card, "damage") && target.hp <= 1 && player.countCards("he") > 2) {
+ return [1, -0.8];
+ }
+ },
+ },
+ },
+ },
+ duzhen_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "useCardToPlayered" },
+ forced: true,
+ locked: true,
+ logTarget: "target",
+ filter(event, player) {
+ return (
+ event.targets?.length == 1 &&
+ event.target?.isIn() &&
+ player.getEnemies(null, false).includes(event.target) &&
+ event.target.countDiscardableCards(player, "he") > 0
+ );
+ },
+ async content(event, trigger, player) {
+ const target = trigger.target;
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
+ const cards = [];
+
+ player.line(target);
+
+ const equips = target
+ .getCards("e")
+ .filter(card => lib.filter.cardDiscardable(card, target, "duzhen_shuying"))
+ .randomGets(num);
+ cards.addArray(equips);
+
+ if (cards.length < num) {
+ const hands = target
+ .getCards("h")
+ .filter(card => lib.filter.cardDiscardable(card, target, "duzhen_shuying"))
+ .randomGets(num - cards.length);
+ cards.addArray(hands);
+ }
+
+ if (cards.length) {
+ await target.discard(cards);
+ }
+ },
+ ai: {
+ threaten: 1.8,
+ effect: {
+ player_use(card, player, target) {
+ if (target && player.getEnemies(null, false).includes(target) && target.countCards("he") > 0) {
+ return [1, 0.5];
+ }
+ },
+ },
+ },
+ },
+
+ //日夜游神 over
+ huiyun_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ enable: "phaseUse",
+ usable: 1,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn() && target.countCards("h") > 0);
+ },
+ filterTarget(card, player, target) {
+ return target.isIn() && player.getEnemies(null, false).includes(target) && target.countCards("h") > 0;
+ },
+ async content(event, trigger, player) {
+ const target = event.targets[0];
+ const hs = target.getCards("h");
+ player.line(target);
+ await target.showCards(hs, `${get.translation(player)}对${get.translation(target)}发动了【${get.translation(event.name)}】`);
+
+ const result = await player
+ .choosePlayerCard(target, "h", [1, 2], `弃置${get.translation(target)}至多两张手牌`)
+ .set("filterButton", button => {
+ const player = get.player();
+ const target = get.event().target;
+ return lib.filter.canBeDiscarded(button.link, player, target);
+ })
+ .set("ai", button => {
+ const card = button.link;
+ const player = get.player();
+ const target = get.event().target;
+ let value = get.value(card, target);
+ if (get.name(card, target) == "tao") value += 3;
+ if (get.name(card, target) == "shan") value += 1;
+ return value;
+ })
+ .set("target", target)
+ .forResult();
+
+ if (!result.bool || !result.cards?.length) return;
+
+ const names = result.cards.map(card => get.name(card, target)).unique();
+ await target.discard(result.cards);
+
+ if (!target.isIn()) return;
+
+ const result2 = await player
+ .chooseToDiscard("he", `是否弃置一张同名牌,对${get.translation(target)}造成2点伤害?`, card => {
+ return get.event().names.includes(get.name(card));
+ })
+ .set("names", names)
+ .set("ai", card => {
+ const player = get.player();
+ const target = get.event().target;
+ if (!player.getEnemies(null, false).includes(target)) return 0;
+ const effect = get.damageEffect(target, player, player);
+ if (effect <= 0) return 0;
+ return effect * 3 - get.value(card);
+ })
+ .set("target", target)
+ .forResult();
+
+ if (result2.bool) {
+ const num = getTianshuDifficulty() == "normal" ? 1 : 2
+
+ player.line(target);
+ await target.damage(num, player);
+ }
+ },
+ ai: {
+ order: 5,
+ result: {
+ target(player, target) {
+ if (!player.getEnemies(null, false).includes(target)) return 0;
+ return -Math.min(2, target.countCards("h"));
+ },
+ },
+ threaten: 1.6,
+ },
+ },
+ yezhong_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "phaseJieshuBegin" },
+ forced: true,
+ locked: true,
+ async content(event, trigger, player) {
+ const result = await player
+ .judge(card => get.color(card) == "black" ? 2 : -1)
+ .set("judge2", result => result.bool)
+ .forResult();
+
+ if (result.card) {
+ await player.gain(result.card, "gain2");
+ }
+
+ if (result.bool) {
+ let num = 1;
+ if (getTianshuDifficulty() == "hard") num = 2;
+ if (getTianshuDifficulty() == "nightmare") num = 3;
+
+ const targets = player.getEnemies(null, false).filter(target => target.isIn() && target.countCards("h") > 0);
+ for (const target of targets) {
+ player.line(target);
+ const cards = target
+ .getCards("h")
+ .filter(card => lib.filter.cardDiscardable(card, target, "yezhong_shuying"))
+ .randomGets(num);
+
+ if (cards.length) {
+ await target.discard(cards);
+ }
+ }
+ }
+ },
+ ai: {
+ threaten: 1.8,
+ },
+ },
+ zhoucha_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { player: "phaseZhunbeiBegin" },
+ forced: true,
+ locked: true,
+ async content(event, trigger, player) {
+ const result = await player
+ .judge(card => get.color(card) == "red" ? 2 : -1)
+ .set("judge2", result => result.bool)
+ .forResult();
+
+ if (result.card) {
+ await player.gain(result.card, "gain2");
+ }
+
+ if (result.bool) {
+ player.addTempSkill("zhoucha_shuying_effect", { player: "phaseAfter" });
+ }
+ },
+ subSkill: {
+ effect: {
+ charlotte: true,
+ mod: {
+ cardUsable(card, player, num) {
+ let number = 1;
+ if (getTianshuDifficulty() == "hard") number = 2;
+ if (getTianshuDifficulty() == "nightmare") number = 3;
+
+ if (card.name == "sha") return num + number;
+ },
+ },
+ },
+ },
+ ai: {
+ threaten: 1.4,
+ },
+ },
+ duane_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
+ trigger: { global: "phaseDiscardEnd" },
+ forced: true,
+ locked: true,
+ logTarget: "player",
+ filter(event, player) {
+ const target = event.player;
+ return (
+ target?.isIn() &&
+ player.getEnemies(null, false).includes(target) &&
+ target.getHistory("lose", evt => {
+ return (
+ evt.type == "discard" &&
+ evt.getParent("phaseDiscard") == event &&
+ evt.cards2?.some(card => get.color(card, target) == "black")
+ );
+ }).length
+ );
+ },
+ async content(event, trigger, player) {
+ const target = trigger.player;
+ player.line(target);
+ await target.loseHp(2);
+ },
+ ai: {
+ threaten: 1.5,
+ },
+ },
+
+
+ //曹操 over
+ jianxiong_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "damageEnd" },
+ filter(event, player) {
+ return event.num > 0;
+ },
+ async cost(event, trigger, player) {
+ event.result = await player.chooseBool(get.prompt2(event.skill))
+ .set("ai", () => true)
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ if (get.itemtype(trigger.cards) == "cards" && get.position(trigger.cards[0], true) == "o") {
+ await player.gain(trigger.cards, "gain2");
+ }
+ await player.draw();
+ },
+ ai: {
+ maixie: true,
+ maixie_hp: true,
+ effect: {
+ target(card, player, target) {
+ if (player.hasSkillTag("jueqing", false, target)) return [1, -1];
+ if (get.tag(card, "damage")) return [1, 0.6];
+ },
+ },
+ },
+ },
+ lingba_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "phaseBegin" },
+ forced: true,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn()) && game.players.every(current => player.countCards("h") >= current.countCards("h"));
+ },
+ async content(event, trigger, player) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
+ const enemies = player.getEnemies(null, false).filter(target => target.isIn());
+ if (player.countCards("h") >= player.hp * 2) {
+ for (const target of enemies) {
+ player.line(target);
+ await target.damage(num, player);
+ }
+ }
+ else {
+ const target = enemies.randomGet();
+ player.line(target);
+ await target.damage(num, player);
+ }
+ },
+ },
+ yishen_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "recoverBegin" },
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.countGainableCards(player, "e") > 0);
+ },
+ async cost(event, trigger, player) {
+ event.result = await player.chooseBool(get.prompt2(event.skill))
+ .set("ai", () => {
+ const player = get.player();
+ const trigger = get.event().getTrigger();
+
+ if (player.hp <= 1) return false;
+ if (player.hp + trigger.num <= 2) return false;
+
+ const enemies = player.getEnemies(null, false).filter(target => {
+ return target.isIn() && target.countGainableCards(player, "e") > 0;
+ });
+ if (!enemies.length) return false;
+
+ const recoverEffect = get.recoverEffect(player, player, player);
+ const difficulty = getTianshuDifficulty();
+
+ if (difficulty == "nightmare") {
+ let total = 0;
+ let max = 0;
+ for (const target of enemies) {
+ for (const card of target.getCards("e")) {
+ const value = get.value(card, target);
+ total += value;
+ if (value > max) max = value;
+ }
+ }
+ if (max < 5) return false;
+ return total >= recoverEffect + 4;
+ }
+
+ let best = 0;
+ for (const target of enemies) {
+ for (const card of target.getCards("e")) {
+ const value = get.value(card, target);
+ if (value > best) best = value;
+ }
+ }
+
+ if (best < 5) return false;
+ return best >= recoverEffect;
+ })
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ const enemies = player.getEnemies(null, false).filter(target => target.countGainableCards(player, "e") > 0);
+ const difficulty = getTianshuDifficulty();
+
+ trigger.cancel();
+
+ if (difficulty == "nightmare") {
+ for (const target of enemies) {
+ player.line(target);
+ await player.gainPlayerCard({ target, position: "e", forced: true });
+ }
+ }
+ else {
+ const target = enemies.randomGet();
+ player.line(target);
+ await player.gainPlayerCard({ target, position: "e", forced: true });
+ }
+ },
+ ai: {
+ threaten: 1.4,
+ effect: {
+ target(card, player, target) {
+ if (get.tag(card, "recover") && target.hasSkill("yishen_shuying")) {
+ if (target.hp <= 1) return [1, 1.2];
+ if (target.hp <= 2) return [1, 0.8];
+
+ const enemies = target.getEnemies(null, false).filter(current => {
+ return current.isIn() && current.countGainableCards(target, "e") > 0;
+ });
+ if (!enemies.length) return;
+
+ let best = 0;
+ for (const enemy of enemies) {
+ for (const equip of enemy.getCards("e")) {
+ best = Math.max(best, get.value(equip, enemy));
+ }
+ }
+
+ if (best >= 5) return [1, 0.4];
+ }
+ },
+ },
+ },
+ },
+
+ //司马懿 over
+ fankui_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "damageEnd" },
+ logTarget: "source",
+ filter(event, player) {
+ return (
+ event.num > 0 &&
+ event.source?.isIn() &&
+ player.getEnemies(null, false).includes(event.source) &&
+ event.source.hasGainableCards(player, "he")
+ );
+ },
+ async content(event, trigger, player) {
+ await player.gainPlayerCard({
+ target: trigger.source,
+ position: "he",
+ forced: true,
+ });
+ },
+ ai: {
+ maixie_defend: true,
+ effect: {
+ target(card, player, target) {
+ if (player.countCards("he") > 1 && get.tag(card, "damage")) {
+ if (player.hasSkillTag("jueqing", false, target)) return [1, -1.5];
+ if (target.getEnemies(null, false).includes(player)) return [1, 1];
+ }
+ },
+ },
+ },
+ },
+ guicai_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { global: "judge" },
+ filter(event, player) {
+ return player.countCards("hes") > 0;
+ },
+ async cost(event, trigger, player) {
+ event.result = await player.chooseCard(`${get.translation(trigger.player)}的${trigger.judgestr || ""}判定为${get.translation(trigger.player.judging[0])},${get.prompt(event.skill)}`, "hes", card => {
+ const player = get.player();
+ const mod2 = game.checkMod(card, player, "unchanged", "cardEnabled2", player);
+ if (mod2 != "unchanged") return mod2;
+ const mod = game.checkMod(card, player, "unchanged", "cardRespondable", player);
+ if (mod != "unchanged") return mod;
+ return true;
+ }).set("ai", card => {
+ const trigger = get.event().getTrigger(), player = get.player(), judging = trigger.player.judging[0];
+ const result = trigger.judge(card) - trigger.judge(judging);
+ const attitude = player.getEnemies(null, false).includes(trigger.player) ? -1 : 1;
+ let value = get.value(card, player);
+ value /= get.subtype(card) == "equip2" ? 2 : 4;
+ if (result == 0) return 0;
+ return attitude > 0 ? result - value : -result - value;
+ }).set("judging", trigger.player.judging[0]).forResult();
+ },
+ popup: false,
+ async content(event, trigger, player) {
+ const next = player.respond(event.cards, event.name, "highlight", "noOrdering");
+ await next;
+ const { cards } = next;
+ if (cards?.length) {
+ if (trigger.player.judging[0].clone) {
+ trigger.player.judging[0].clone.classList.remove("thrownhighlight");
+ game.broadcast(card => {
+ if (card.clone) card.clone.classList.remove("thrownhighlight");
+ }, trigger.player.judging[0]);
+ game.addVideo("deletenode", player, get.cardsInfo([trigger.player.judging[0].clone]));
+ }
+ await game.cardsDiscard(trigger.player.judging[0]);
+ trigger.player.judging[0] = cards[0];
+ trigger.orderingCards.addArray(cards);
+ game.log(trigger.player, "的判定牌改为", cards);
+ await game.delay(2);
+ }
+ },
+ ai: { rejudge: true, tag: { rejudge: 1 } },
+ },
+ langgu_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "gainAfter" },
+ forced: true,
+ filter(event, player) {
+ if (player.hasSkill("langgu_shuying_used")) return false;
+ return game.hasPlayer(current => {
+ return (
+ current != player &&
+ player.getEnemies(null, false).includes(current) &&
+ event.getl?.(current)?.cards2?.length
+ );
+ });
+ },
+ async content(event, trigger, player) {
+ const source = game.findPlayer(current => {
+ return (
+ current != player &&
+ player.getEnemies(null, false).includes(current) &&
+ trigger.getl?.(current)?.cards2?.length
+ );
+ });
+ if (!source) return;
+
+ player.addTempSkill("langgu_shuying_used", "roundStart");
+
+ const result = await player.judge(card => get.suit(card) == "spade" ? 2 : 0).forResult();
+ if (result.bool && source.isIn() && source.countDiscardableCards(player, "h") > 0) {
+ player.removeSkill("langgu_shuying_used");
+ player.line(source);
+ const card = source.getCards("h").randomGet();
+ await source.discard(card);
+ }
+ },
+ subSkill: {
+ used: { charlotte: true },
+ },
+ },
+ yuanlv_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { source: "damageBegin4" },
+ filter(event, player) {
+ return event.card && get.type2(event.card) == "trick" && event.player?.isIn() && player.getEnemies(null, false).includes(event.player);
+ },
+ async cost(event, trigger, player) {
+ event.result = await player.chooseBool(get.prompt(event.skill, trigger.player))
+ .set("ai", () => true)
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ const target = trigger.player;
+ trigger.cancel();
+ await player.draw();
+ target.line(player);
+ if (getTianshuDifficulty() == "nightmare") {
+ await player.damage(1, target, "unreal");
+ }
+ else {
+ await player.damage(1, target);
+ }
+ },
+ },
+
+ //吕布 over
+ wushuang_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
+ trigger: { source: "damageBegin1" },
+ forced: true,
+ locked: true,
+ usable(skill, player) {
+ return getTianshuDifficulty() == "nightmare" ? 2 : 1;
+ },
+ logTarget: "player",
+ filter(event, player) {
+ const target = event.player;
+ const evtx = event.getParent(2);
+ const card = event.card;
+ if (!card || !["sha", "juedou"].includes(card.name)) return false;
+
+ if (card.name == "sha") {
+ return !target.hasHistory("useCard", evt => {
+ return evt.card.name == "shan" && evt.respondTo && evt.getParent(3) == evtx;
+ });
+ }
+
+ return !target.hasHistory("respond", evt => {
+ return evt.card.name == "sha" && evt.respondTo && evt.getParent(3) == evtx;
+ });
+ },
+ async content(event, trigger, player) {
+ trigger.num++;
+ },
+ group: ["wushuang_shuying_1", "wushuang_shuying_2"],
+ subSkill: {
+ 1: {
+ audio: "wushuang_shuying",
+ sourceSkill: "wushuang_shuying",
+ trigger: { player: "useCardToPlayered" },
+ forced: true,
+ logTarget: "target",
+ filter(event, player) {
+ return event.card.name == "sha" && !event.getParent().directHit.includes(event.target);
+ },
+ async content(event, trigger, player) {
+ const id = trigger.target.playerid;
+ const map = trigger.getParent().customArgs;
+ if (!map[id]) map[id] = {};
+
+ if (typeof map[id].shanRequired == "number") map[id].shanRequired++;
+ else map[id].shanRequired = 2;
+ },
+ ai: {
+ directHit_ai: true,
+ skillTagFilter(player, tag, arg) {
+ if (arg.card.name != "sha" || arg.target.countCards("h", "shan") > 1) return false;
+ },
+ },
+ },
+ 2: {
+ audio: "wushuang_shuying",
+ sourceSkill: "wushuang_shuying",
+ trigger: { player: "useCardToPlayered", target: "useCardToTargeted" },
+ forced: true,
+ logTarget(trigger, player) {
+ return player == trigger.player ? trigger.target : trigger.player;
+ },
+ filter(event, player) {
+ return event.card.name == "juedou";
+ },
+ async content(event, trigger, player) {
+ const id = (player == trigger.player ? trigger.target : trigger.player).playerid;
+ const idt = trigger.target.playerid;
+ const map = trigger.getParent().customArgs;
+
+ if (!map[idt]) map[idt] = {};
+ if (!map[idt].shaReq) map[idt].shaReq = {};
+ if (!map[idt].shaReq[id]) map[idt].shaReq[id] = 1;
+
+ map[idt].shaReq[id]++;
+ },
+ ai: {
+ directHit_ai: true,
+ skillTagFilter(player, tag, arg) {
+ if (
+ arg.card.name != "juedou" ||
+ Math.floor(arg.target.countCards("h", "sha") / 2) > player.countCards("h", "sha")
+ ) {
+ return false;
+ }
+ },
+ },
+ },
+ },
+ ai: {
+ threaten: 1.8,
+ directHit_ai: true,
+ },
+ },
+ shenij_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
+ group: [
+ "shenij_shuying_judge",
+ "shenij_shuying_draw",
+ "shenij_shuying_sha",
+ "shenij_shuying_target",
+ ],
+ subSkill: {
+ judge: {
+ audio: "shenij_shuying",
+ trigger: { player: "phaseJudgeBegin" },
+ filter(event, player) {
+ return player.countCards("h") >= 2 && player.countCards("j") > 0;
+ },
+ async cost(event, trigger, player) {
+ event.result = await player
+ .chooseToDiscard("h", 2, get.prompt2(event.skill))
+ .set("ai", card => {
+ const player = get.player();
+ if (!player.countCards("j")) return 0;
+ return 7 - get.value(card);
+ })
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ const cards = player.getCards("j");
+ await player.discard(cards);
+ },
+ ai: {
+ effect: {
+ target(card, player, target) {
+ if (get.type(card) == "delay") return [0, 2];
+ },
+ },
+ },
+ },
+ draw: {
+ audio: "shenij_shuying",
+ trigger: { player: "phaseDrawBegin2" },
+ forced: true,
+ filter(event, player) {
+ return !event.numFixed;
+ },
+ async content(event, trigger, player) {
+ let num = 2;
+ if (getTianshuDifficulty() == "hard") num = 3;
+ if (getTianshuDifficulty() == "nightmare") num = 4;
+
+ trigger.num += num;
+ },
+ },
+ sha: {
+ audio: "shenij_shuying",
+ forced: true,
+ mod: {
+ cardUsable(card, player, num) {
+ let shaNum = 2;
+ if (getTianshuDifficulty() == "normal") shaNum = 1;
+
+ if (card.name == "sha") return num + shaNum;
+ },
+ },
+ },
+ target: {
+ audio: "shenij_shuying",
+ trigger: { player: "useCard2" },
+ filter(event, player) {
+ if (event.card.name != "sha") return false;
+
+ const info = get.info(event.card);
+ if (info.allowMultiple == false || info.multitarget || !event.targets) return false;
+
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
+ return game.countPlayer(current => {
+ return !event.targets.includes(current) &&
+ lib.filter.targetEnabled2(event.card, player, current) &&
+ lib.filter.targetInRange(event.card, player, current);
+ }) > 0;
+ },
+ async cost(event, trigger, player) {
+ const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
+
+ event.result = await player
+ .chooseTarget(
+ get.prompt(event.skill),
+ `为此【杀】额外指定至多${get.cnNumber(num)}名目标`,
+ [1, num],
+ (card, player, target) => {
+ const trigger = get.event().getTrigger();
+ return !trigger.targets.includes(target) &&
+ lib.filter.targetEnabled2(trigger.card, player, target) &&
+ lib.filter.targetInRange(trigger.card, player, target);
+ }
+ )
+ .set("ai", target => {
+ const player = get.player();
+ if (!player.getEnemies(null, false).includes(target)) return 0;
+ return get.effect(target, get.event().getTrigger().card, player, player);
+ })
+ .forResult();
+ },
+ async content(event, trigger, player) {
+ for (const target of event.targets) {
+ player.line(target);
+ trigger.targets.add(target);
+ game.log(target, "成为了", trigger.card, "的额外目标");
+ }
+ },
+ ai: {
+ effect: {
+ player_use(card, player, target) {
+ if (card.name == "sha" && player.getEnemies(null, false).includes(target)) return [1, 0.5];
+ },
+ },
+ },
+ },
+ },
+ },
+ zhanjia_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
+ trigger: { player: "damageBegin4" },
+ forced: true,
+ locked: true,
+ usable: 1,
+ filter(event, player) {
+ return event.num > 2;
+ },
+ async content(event, trigger, player) {
+ trigger.num = 2;
+ await player.draw(getTianshuDifficulty() == "nightmare" ? 3 : 2);
+ },
+ ai: {
+ filterDamage: true,
+ effect: {
+ target(card, player, target) {
+ if (get.tag(card, "damage")) {
+ if (player.hasSkillTag("jueqing", false, target)) return;
+ return [1, 0.8];
+ }
+ },
+ },
+ },
+ },
+
+ //董卓 over
+ jiuchi_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ enable: "chooseToUse",
+ filterCard(card, player) {
+ return get.suit(card, player) == "spade";
+ },
+ position: "h",
+ viewAs: { name: "jiu" },
+ viewAsFilter(player) {
+ return player.countCards("h", card => get.suit(card, player) == "spade") > 0;
+ },
+ prompt: "将一张黑桃手牌当【酒】使用",
+ check(card) {
+ return 6 - get.value(card);
+ },
+ ai: {
+ order: 3.5,
+ respondJiu: true,
+ skillTagFilter(player) {
+ return player.countCards("h", card => get.suit(card, player) == "spade") > 0;
+ },
+ },
+ },
+ roulin_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "useCardToPlayered", target: "useCardToTargeted" },
+ forced: true,
+ locked: true,
+ logTarget(trigger, player) {
+ return player == trigger.player ? trigger.target : trigger.player;
+ },
+ filter(event, player) {
+ if (event.card.name != "sha") return false;
+ if (player == event.player) return event.target.hasSex("female");
+ return event.player.hasSex("female");
+ },
+ async content(event, trigger, player) {
+ const id = (player == trigger.player ? trigger.target : player).playerid;
+ const map = trigger.getParent().customArgs;
+ if (!map[id]) map[id] = {};
+
+ if (typeof map[id].shanRequired == "number") map[id].shanRequired++;
+ else map[id].shanRequired = 2;
+ },
+ ai: {
+ halfneg: true,
+ directHit_ai: true,
+ skillTagFilter(player, tag, arg) {
+ if (tag === "directHit_ai") return;
+ if (arg.card.name != "sha" || !arg.target.hasSex("female") || arg.target.countCards("h", "shan") > 1) {
+ return false;
+ }
+ },
+ },
+ },
+ baonue_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { player: "phaseBegin" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return player.getDamagedHp() > 0;
+ },
+ async content(event, trigger, player) {
+ let maxNum = 3;
+ if (getTianshuDifficulty() == "hard") maxNum = 4;
+ if (getTianshuDifficulty() == "nightmare") maxNum = 5;
+
+ const num = Math.min(maxNum, player.getDamagedHp());
+ await player.draw(num);
+
+ const result = await player
+ .chooseTarget(`对至多${get.cnNumber(num)}名角色造成1点伤害`, [1, num], (card, player, target) => {
+ return target.isIn() && player.getEnemies(null, false).includes(target);
+ })
+ .set("ai", target => {
+ const player = get.player();
+ return get.damageEffect(target, player, player);
+ })
+ .forResult();
+
+ if (result.bool) {
+ for (const target of result.targets) {
+ player.line(target);
+ await target.damage(1, player);
+ }
+ }
+
+ await player.loseHp();
+ },
+ ai: {
+ threaten: 2,
+ },
+ },
+ qubu_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
+ trigger: { global: "useCardToPlayered" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return (
+ event.card.name == "sha" &&
+ event.player?.isIn() &&
+ !player.getEnemies(null, false).includes(event.player) &&
+ event.targets?.length > 0
+ );
+ },
+ async content(event, trigger, player) {
+ const result = await player
+ .judge(card => get.color(card) == "black" ? 2 : 1)
+ .set("judge2", result => result.bool)
+ .forResult();
+
+ if (result.bool) {
+ const targets = trigger.targets.filter(target => target.isIn());
+ for (const target of targets) {
+ player.line(target);
+ await target.damage(1, player);
+ }
+ }
+ },
+ ai: {
+ threaten: 1.8,
+ },
+ },
+
+
+ //水神共工 over
+ shuishen_shuying: {
+ mode: ["boss"],
+ forced: true,
+ group: ["shuishen_shuying_recover", "shuishen_shuying_draw", "shuishen_shuying_damage"],
+ subSkill: {
+ recover: {
+ trigger: { player: "recoverBegin" },
+ audio: false,
+ forced: true,
+ filter(event, player) {
+ return game.shuying_getDiscardNamesNum() >= 4;
+ },
+ async content(event, trigger, player) {
+ trigger.num++;
+ },
+ },
+ draw: {
+ trigger: { player: "phaseDrawBegin2" },
+ audio: false,
+ forced: true,
+ filter(event, player) {
+ return !event.numFixed && game.shuying_getDiscardNamesNum() >= 5;
+ },
+ async content(event, trigger, player) {
+ trigger.num += 5;
+ },
+ },
+ damage: {
+ trigger: { source: "damageBegin1" },
+ audio: false,
+ forced: true,
+ filter(event, player) {
+ return game.shuying_getDiscardNamesNum() >= 9;
+ },
+ async content(event, trigger, player) {
+ trigger.num++;
+ },
+ },
+ },
+ },
+ tuanliu_shuying: {
+ mode: ["boss"],
+ trigger: { player: "phaseJieshuBegin" },
+ audio: false,
+ forced: true,
+ filter(event, player) {
+ return game.shuying_getDiscardNum() > 4;
+ },
+ async content(event, trigger, player) {
+ let hp = 2;
+ let card = 4;
+ let damage = 1;
+
+ if (getTianshuDifficulty() == "nightmare") {
+ hp = 3;
+ card = 6;
+ }
+
+
+ const num = game.shuying_getDiscardNum();
+ if (num > 4) await player.recover(hp);
+ if (num > 5) await player.draw(card);
+ if (num > 9) {
+ for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
+ player.line(target);
+ await target.damage(player, damage);
+ }
+ }
+ },
+ },
+ juehong_shuying: {
+ mode: ["boss"],
+ trigger: { player: "phaseZhunbeiBegin" },
+ audio: false,
+ forced: true,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn());
+ },
+ async content(event, trigger, player) {
+ for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
+ player.line(target);
+ if (target.countCards("e")) await target.chooseToDiscard("e", true, target.countCards("e"));
+ else if (target.countCards("h")) await target.discard(target.getCards("h").randomGets(Math.min(2, target.countCards("h"))));
+ }
+ },
+ },
+
+ //白起
+ changsheng_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ forced: true,
+ mod: {
+ targetInRange(card, player, target) {
+ if (card.name == "sha") return true;
+ },
+ },
+ },
+ shashen_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ enable: ["chooseToUse", "chooseToRespond"],
+ filterCard: true,
+ position: "h",
+ viewAs: { name: "sha" },
+ viewAsFilter(player) {
+ return player.countCards("h") > 0;
+ },
+ prompt: "将一张手牌当【杀】使用或打出",
+ check(card) {
+ return 6 - get.value(card);
+ },
+ group: "shashen_shuying_draw",
+ subSkill: {
+ draw: {
+ trigger: { source: "damageEnd" },
+ filter(event, player) {
+ return event.card?.name == "sha";
+ },
+ async content(event, trigger, player) {
+ await player.draw(3);
+ },
+ },
+ },
+ ai: {
+ respondSha: true,
+ skillTagFilter(player) {
+ return player.countCards("h") > 0;
+ },
+ order: 4,
+ },
+ },
+ wuan_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ forced: true,
+ mod: {
+ cardUsable(card, player, num) {
+ if (card.name == "sha") return num + 3;
+ },
+ },
+ trigger: { source: "damageBegin1" },
+ filter(event, player) {
+ return event.card?.name == "sha";
+ },
+ async content(event, trigger, player) {
+ trigger.num++;
+ },
+ },
+
+ //盘古
+ shenqu_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ trigger: { player: "damageEnd" },
+ forced: true,
+ filter(event, player) {
+ return player.countCards("h", card => get.color(card) == "red") > 0;
+ },
+ async content(event, trigger, player) {
+ const cards = player.getCards("h", card => get.color(card) == "red");
+ await player.lose(cards, ui.cardPile, "insert");
+ await player.draw(cards.length);
+ },
+ },
+ lieben_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ trigger: { player: "useCardToPlayered" },
+ forced: true,
+ filter(event, player) {
+ return event.card.name == "sha" && ui.cardPile.lastChild;
+ },
+ async content(event, trigger, player) {
+ const judge = player.judge(card => get.color(card) == "red" ? 2 : -1);
+ judge.directresult = ui.cardPile.lastChild;
+ judge.judge2 = result => result.bool;
+ const result = await judge.forResult();
+ if (result.bool) {
+ trigger.getParent().addCount = false;
+ trigger.getParent().baseDamage++;
+ }
+ },
+ },
+ yinjiang_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ trigger: { player: "drawEnd" },
+ forced: true,
+ filter(event, player) {
+ return player.isPhaseUsing() && !player.hasSkill("yinjiang_shuying_disabled") && ui.cardPile.lastChild;
+ },
+ async content(event, trigger, player) {
+ const card = ui.cardPile.lastChild;
+ await player.gain(card, "gain2");
+ if (get.color(card) == "red") {
+ let count = 0;
+ for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
+ player.line(target);
+ await target.damage(player);
+ count++;
+ }
+ player.storage.yinjiang_shuying_count = (player.storage.yinjiang_shuying_count || 0) + count;
+ if (player.storage.yinjiang_shuying_count >= 2) player.addTempSkill("yinjiang_shuying_disabled", { player: "phaseUseEnd" });
+ }
+ },
+ group: "yinjiang_shuying_clear",
+ subSkill: {
+ clear: {
+ trigger: { player: "phaseUseEnd" },
+ silent: true,
+ async content(event, trigger, player) {
+ delete player.storage.yinjiang_shuying_count;
+ },
+ },
+ disabled: { charlotte: true },
+ },
+ },
+ zhuri_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill:1",
+ forced: true,
+ group: ["zhuri_shuying_draw", "zhuri_shuying_bottom"],
+ subSkill: {
+ draw: {
+ trigger: { player: "useCard" },
+ forced: true,
+ filter(event, player) {
+ return get.color(event.card) == "red";
+ },
+ async content(event, trigger, player) {
+ await player.draw();
+ },
+ },
+ bottom: {
+ trigger: { global: "useCardAfter" },
+ forced: true,
+ filter(event, player) {
+ return event.targets?.includes(player) && (event.card.name == "sha" || get.type(event.card) == "trick") && event.cards?.filterInD("d").length;
+ },
+ async content(event, trigger, player) {
+ const cards = trigger.cards.filterInD("d");
+ game.log(cards, "被置于牌堆底");
+ for (const card of cards) ui.cardPile.appendChild(card);
+ },
+ },
+ },
+ },
+
+ //少昊 over
+ baiyi_shuying: {
+ mode: ["boss"],
+ trigger: { global: "phaseBegin" },
+ forced: true,
+ logTarget: "player",
+ filter(event, player) {
+ return (
+ game.roundNumber < 7 &&
+ event.player?.isIn() &&
+ player.getEnemies(null, false).includes(event.player)
+ );
+ },
+ async content(event, trigger, player) {
+ let num = 1;
+ const target = trigger.player;
+ player.line(target);
+
+ const difficulty = getTianshuDifficulty();
+
+ if (difficulty == "hard") {
+ num = 2;
+ } else if (difficulty == "nightmare") {
+ num = 3;
+ }
+
+ if (game.roundNumber < 3 && target.countGainableCards(player, "he") > 0) {
+ const cards = target.getCards("he").randomGets(Math.min(num, target.countCards("he")));
+ await player.gain(cards, target, "giveAuto");
+ }
+
+ if (game.roundNumber < 5 && target.isIn()) {
+ await target.damage(num, "thunder", player);
+ }
+
+ if (game.roundNumber < 7 && target.isIn()) {
+ const cards = target
+ .getCards("he")
+ .filter(card => lib.filter.cardDiscardable(card, target, "baiyi_shuying"))
+ .randomGets(num);
+
+ if (cards.length) {
+ await target.discard(cards);
+ }
+ }
+ },
+ ai: {
+ threaten: 3,
+ },
+ },
+ shenen_shuying: {
+ mode: ["boss"],
+ forced: true,
+ global: "shenen_shuying_global",
+ subSkill: {
+ global: {
+ mod: {
+ targetInRange(card, player, target) {
+ const owner = game.findPlayer(current => current.hasSkill("shenen_shuying"));
+ if (owner && !owner.getEnemies(null, false).includes(player)) return true;
+ },
+ },
+ },
+ },
+ ai: {
+ threaten: 1.5,
+ },
+ },
+
+ //玄女 over
+ xuanlie_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
+ trigger: { player: "phaseJieshuBegin" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return game.hasPlayer(current => {
+ return (
+ current.isIn() &&
+ player.getEnemies(null, false).includes(current) &&
+ player.getHistory("gain", evt => evt.getl?.(current)?.cards2?.length).length
+ );
+ });
+ },
+ async content(event, trigger, player) {
+ const targets = game.filterPlayer(current => {
+ return (
+ current.isIn() &&
+ player.getEnemies(null, false).includes(current) &&
+ player.getHistory("gain", evt => evt.getl?.(current)?.cards2?.length).length
+ );
+ });
+
+ let num = 2;
+ if (getTianshuDifficulty() == "normal") num = 1;
+
+ for (const target of targets) {
+ player.line(target);
+ await target.damage(num, player);
+ }
+ },
+ ai: {
+ threaten: 2,
+ },
+ },
+ jiutian_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
+ trigger: { player: "phaseZhunbeiBegin" },
+ forced: true,
+ locked: true,
+ filter(event, player) {
+ return player.getEnemies(null, false).some(target => target.isIn() && target.countGainableCards(player, "h") > 0);
+ },
+ async content(event, trigger, player) {
+ let num = 2;
+ const difficulty = getTianshuDifficulty();
+ if (difficulty == "normal") num = 1;
+
+ const targets = player.getEnemies(null, false).filter(target => target.isIn() && target.countGainableCards(player, "h") > 0);
+ const gained = [];
+
+ for (const target of targets) {
+ const cards = target.getCards("h").randomGets(Math.min(num, target.countCards("h")));
+ if (cards.length) {
+ player.line(target);
+ await player.gain(cards, target, "giveAuto");
+ gained.addArray(cards);
+ }
+ }
+
+ if (!gained.length) return;
+
+ const colors = gained.map(card => get.color(card, player)).unique();
+ const suits = gained.map(card => get.suit(card, player)).unique();
+
+ if (colors.length >= 2) {
+ for (const target of targets) {
+ if (!target.isIn()) continue;
+ player.line(target);
+
+ target.damage(1, player);
+ }
+ }
+
+ if (difficulty == "nightmare" && suits.length >= 4) {
+ for (const target of targets) {
+ if (!target.isIn()) continue;
+ player.line(target);
+ await target.loseHp();
+ }
+ }
+ },
+ ai: {
+ threaten: 3,
+ },
+ },
+ dishi_shuying: {
+ mode: ["boss"],
+ audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
+ trigger: { player: "useCard2" },
+ filter(event, player) {
+ if (!event.targets?.length) return false;
+ if (event.card.name != "sha" && get.type(event.card) != "trick") return false;
+
+ const info = get.info(event.card);
+ if (event.targets.length == 1) {
+ if (info.allowMultiple == false || info.multitarget) return false;
+ return game.hasPlayer(current => {
+ return (
+ !event.targets.includes(current) &&
+ lib.filter.targetEnabled2(event.card, player, current) &&
+ lib.filter.targetInRange(event.card, player, current)
+ );
+ });
+ }
+
+ return event.targets.length > 1;
+ },
+ async cost(event, trigger, player) {
+ if (trigger.targets.length == 1) {
+ event.result = await player
+ .chooseTarget(get.prompt(event.skill), `为${get.translation(trigger.card)}增加一个目标`, (card, player, target) => {
+ const trigger = get.event().getTrigger();
+ return (
+ !trigger.targets.includes(target) &&
+ lib.filter.targetEnabled2(trigger.card, player, target) &&
+ lib.filter.targetInRange(trigger.card, player, target)
+ );
+ })
+ .set("ai", target => {
+ const player = get.player();
+ const trigger = get.event().getTrigger();
+ const effect = get.effect(target, trigger.card, player, player);
+ if (player.getEnemies(null, false).includes(target)) return effect;
+ return Math.min(0, effect);
+ })
+ .forResult();
+ }
+ else {
+ event.result = await player
+ .chooseTarget(get.prompt(event.skill), `令${get.translation(trigger.card)}减少一个目标`, (card, player, target) => {
+ return get.event().getTrigger().targets.includes(target);
+ })
+ .set("ai", target => {
+ const player = get.player();
+ const trigger = get.event().getTrigger();
+ return -get.effect(target, trigger.card, player, player);
+ })
+ .forResult();
+ }
+ },
+ async content(event, trigger, player) {
+ const target = event.targets[0];
+ player.line(target);
+
+ if (trigger.targets.includes(target)) {
+ trigger.targets.remove(target);
+ game.log(target, "不再是", trigger.card, "的目标");
+ }
+ else {
+ trigger.targets.add(target);
+ game.log(target, "成为了", trigger.card, "的额外目标");
+ }
+ },
+ ai: {
+ threaten: 1.6,
+ },
+ },
+};
+
+export default skills;
diff --git a/pve/boss/sort.js b/pve/boss/sort.js
new file mode 100644
index 0000000..740f802
--- /dev/null
+++ b/pve/boss/sort.js
@@ -0,0 +1,9 @@
+const characterSort = {
+ shuYing_pve_tianshu: ["chenlong_shuying"],
+};
+
+const characterSortTranslate = {
+ shuYing_pve_tianshu: "天书乱斗",
+};
+
+export { characterSort, characterSortTranslate };
diff --git a/pve/boss/translate.js b/pve/boss/translate.js
new file mode 100644
index 0000000..8e9cb62
--- /dev/null
+++ b/pve/boss/translate.js
@@ -0,0 +1,144 @@
+const translates = {
+ zishu_boss_shuying: "子鼠",
+ zishu_shuying: "子鼠",
+ zishu_shuying_info: "出牌阶段限一次,你可以获得手牌数大于你的其他角色一张手牌,你可以重复此流程,直到你的手牌数为全场最多。",
+
+ chenlong_boss_shuying: "辰龙",
+ chenlong_shuying: "辰龙",
+ chenlong_shuying_info: "限定技,出牌阶段,你可以失去任意点体力(至多为5),然后对一名其他角色造成等量的伤害。若你以此法进入濒死状态,则你将体力值回复至1,然后减少1点体力上限。",
+
+ wuma_boss_shuying: "午马",
+ wuma_shuying: "午马",
+ wuma_shuying_info: "锁定技,你不能被翻面;你的阶段不能被跳过;当你成为其他角色使用锦囊牌的目标后,摸一张牌。",
+
+ weiyang_boss_shuying: "未羊",
+ weiyang_shuying: "未羊",
+ weiyang_shuying_info: "出牌阶段限一次,你可以弃置任意张不同类型的牌,然后令至多等量角色回复1点体力。",
+
+ shenhou_boss_shuying: "申猴",
+ shenhou_shuying: "申猴",
+ shenhou_shuying_info: "当你成为【杀】的目标时,你可以进行判定,若结果为红色,则此【杀】对你无效。",
+
+ xvgou_boss_shuying: "戌狗",
+ xvgou_shuying: "戌狗",
+ xvgou_shuying_info: "锁定技,红色【杀】对你无效;你使用红色【杀】无距离限制且造成伤害+1.",
+
+ heibaiwuchang_boss_shuying: "黑白无常",
+ mizui_shuying: "迷醉",
+ mizui_shuying_info: "锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的1张牌。",
+ qiangzheng_shuying: "强征",
+ qiangzheng_shuying_info: "锁定技,敌方角色的结束阶段,若其手牌数小于4,你获得其所有手牌。",
+ xixing_shuying: "吸星",
+ xixing_shuying_info: "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力",
+
+ niutoumamian_boss_shuying: "牛头马面",
+ shiyv_shuying: "施狱",
+ shiyv_shuying_info: "锁定技,摸牌阶段,你放弃摸牌改为随机获得牌堆中每种花色的牌各一张。",
+ manji_shuying: "蛮击",
+ manji_shuying_info: "你使用【杀】指定单一目标后,你可以弃置该角色2张手牌。若其中有【杀】,你本次【杀】造成的伤害+1。",
+ xiaoshou_shuying: "枭首",
+ xiaoshou_shuying_info: "锁定技,准备阶段,你对随机一名敌方角色造成2点伤害",
+
+ yvsai_boss_shuying: "鱼鳃",
+ anchao_shuying: "暗潮",
+ anchao_shuying_info: "锁定技,己方角色的回合结束时,若此回合该角色没有造成过伤害,则获得一个“暗潮”标记。己方回合开始时,若该角色有“暗潮”标记,则本回合摸牌阶段多摸X张牌且对敌方角色造成的伤害+X(X为“暗潮”标记数)",
+ guixi_shuying: "鬼吸",
+ guixi_shuying_info: "锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸1张牌。",
+
+ huangfeng_boss_shuying: "黄蜂",
+ mingchong_shuying: "冥虫",
+ mingchong_shuying_info: "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。",
+ duzhen_shuying: "毒针",
+ duzhen_shuying_info: "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。",
+
+ riyeyoushen_boss_shuying: "日夜游神",
+ huiyun_shuying: "晦运",
+ huiyun_shuying_info: "出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成2点伤害。",
+ yezhong_shuying: "夜冢",
+ yezhong_shuying_info: "锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃1张手牌。",
+ zhoucha_shuying: "昼刹",
+ zhoucha_shuying_info: "锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+1。",
+ duane_shuying: "断恶",
+ duane_shuying_info: "锁定技,敌方角色的弃牌阶段结束时,若其此阶段弃置了黑色牌,则该角色失去2点体力。",
+
+
+ caocao_boss_shuying: "曹操",
+ jianxiong_shuying: "奸雄",
+ jianxiong_shuying_info: "当你受到伤害后,你可以获得对你造成伤害的牌并摸一张牌。",
+ lingba_shuying: "凌霸",
+ lingba_shuying_info: "锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成1点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。",
+ yishen_shuying: "疑神",
+ yishen_shuying_info: "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。",
+
+ simayi_boss_shuying: "司马懿",
+ fankui_shuying: "反馈",
+ fankui_shuying_info: "当你受到1点伤害后,你可以获得伤害来源的一张牌。",
+ guicai_shuying: "鬼才",
+ guicai_shuying_info: "当一名角色的判定牌生效前,你可以打出一张牌代替之。",
+ langgu_shuying: "狼顾",
+ langgu_shuying_info: " 锁定技,每回合限一次,当你获得其他角色的牌时,进行一次判定:若结果为黑桃,随机弃置其1张手牌,且视为此技能本回合未发动过。",
+ yuanlv_shuying: "远虑",
+ yuanlv_shuying_info: "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。",
+
+ lvbu_boss_shuying: "吕布",
+ wushuang_shuying: "无双",
+ wushuang_shuying_info: "锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限1次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。",
+ shenij_shuying: "神戟",
+ shenij_shuying_info: "判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸两张牌;出牌阶段,你可以多使用一张【杀】,你的【杀】可以多指定一名角色为目标。",
+ zhanjia_shuying: "战甲",
+ zhanjia_shuying_info: "锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸两张牌。",
+
+ dongzhuo_boss_shuying: "董卓",
+ jiuchi_shuying: "酒池",
+ jiuchi_shuying_info: "你可以将一张黑桃手牌当【酒】使用。",
+ roulin_shuying: "肉林",
+ roulin_shuying_info: "锁定技,你对女性角色使用的【杀】和女性角色对你使用的【杀】均需使用两张【闪】才能抵消。",
+ baonue_shuying: "暴虐",
+ baonue_shuying_info: "锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为3。",
+ qubu_shuying: "驭布",
+ qubu_shuying_info: "锁定技,当友方角色使用【杀】指定目标时,进行一次判定:若结果为黑桃,你对此【杀】的所有目标造成1点伤害。",
+
+
+ shuishengonggong_boss_shuying: "水神共工",
+ shuishen_shuying: "水神",
+ shuishen_shuying_info: "锁定技,弃牌堆中,不同牌名的牌数达到4时,你回复体力时回复量+1;达到5时,摸牌阶段额外摸5张牌;达到9时,你造成伤害时伤害+1。",
+ tuanliu_shuying: "湍流",
+ tuanliu_shuying_info: "锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复2点体力;大于5,你摸4张牌;大于9,你对所有敌方角色造成1点伤害。",
+ juehong_shuying: "决洪",
+ juehong_shuying_info: "锁定技,准备阶段,你令所有敌方角色自己弃置自己的装备区内的所有牌,若其装备区内没有牌,则改为随机弃置2张手牌",
+
+ baiqi_boss_shuying: "白起",
+ changsheng_shuying: "常胜",
+ changsheng_shuying_info: "锁定技,你使用【杀】无距离限制。",
+ shashen_shuying: "杀神",
+ shashen_shuying_info: "你可以将手牌中的任意牌当【杀】使用或打出。你使用的【杀】造成伤害后,摸三张牌。",
+ wuan_shuying: "武安",
+ wuan_shuying_info: "锁定技,你可使用的【杀】的次数+3,【杀】造成的伤害+1。",
+
+ kuafu_boss_shuying: "盘古",
+ shenqu_shuying: "神躯",
+ shenqu_shuying_info: "锁定技,当你受到伤害后,将所有红色手牌置于牌堆底,并摸等量的牌。",
+ lieben_shuying: "烈奔",
+ lieben_shuying_info: "锁定技,当你使用【杀】指定目标后,使用牌堆底的牌进行一次判定:若判定结果为红色,则此杀不计入出牌阶段使用次数且伤害+1。",
+ yinjiang_shuying: "饮江",
+ yinjiang_shuying_info: "锁定技,当你在出牌阶段摸牌后,获得牌堆底的1张牌。若该牌为红色,则对所有敌方角色造成1点伤害。当你在同一个阶段内以此法造成过两次活更多的伤害后,该技能失效直到回合结束。",
+ zhuri_shuying: "逐日",
+ zhuri_shuying_info: "锁定技,你使用红色牌时摸1张牌。当你成为【杀】或普通锦囊牌的目标后,结算完毕后将该牌放置在牌堆底。",
+
+ shaohao_boss_shuying: "少昊",
+ baiyi_shuying: "白仪",
+ baiyi_shuying_info: "锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其1张牌;若当前轮数小于5,对其造成1点雷电伤害;若当前轮数小于7,其随机弃置1张牌。",
+ shenen_shuying: "神恩",
+ shenen_shuying_info: "锁定技,所有己方角色使用牌锁定技,所有己方角色使用牌没有距离限制。",
+
+
+ xuannv_boss_shuying: "玄女",
+ xuanlie_shuying: "玄烈",
+ xuanlie_shuying_info: "锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成1点伤害。",
+ jiutian_shuying: "九天",
+ jiutian_shuying_info: "锁定技,准备阶段,你获得所有敌方角色各1张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。",
+ dishi_shuying: "帝师",
+ dishi_shuying_info: "当你使用【杀】或普通锦囊牌指定目标时,如果目标数为1,可以增加一个目标;如果目标数大于1,你可以减少一个目标。",
+};
+
+export default translates;
diff --git a/pve/boss/voices.js b/pve/boss/voices.js
new file mode 100644
index 0000000..9b345cd
--- /dev/null
+++ b/pve/boss/voices.js
@@ -0,0 +1,4 @@
+const voices = {
+};
+
+export default voices;
diff --git a/pve/images/tianshu.jpg b/pve/images/tianshu.jpg
new file mode 100644
index 0000000..233b774
Binary files /dev/null and b/pve/images/tianshu.jpg differ
diff --git a/pve/images/十二生肖/chenlong_boss_shuying.png b/pve/images/十二生肖/chenlong_boss_shuying.png
new file mode 100644
index 0000000..34d3a00
Binary files /dev/null and b/pve/images/十二生肖/chenlong_boss_shuying.png differ
diff --git a/pve/images/十二生肖/chouniu_boss_shuying.png b/pve/images/十二生肖/chouniu_boss_shuying.png
new file mode 100644
index 0000000..3161535
Binary files /dev/null and b/pve/images/十二生肖/chouniu_boss_shuying.png differ
diff --git a/pve/images/十二生肖/haizhu_boss_shuying.png b/pve/images/十二生肖/haizhu_boss_shuying.png
new file mode 100644
index 0000000..3c3a191
Binary files /dev/null and b/pve/images/十二生肖/haizhu_boss_shuying.png differ
diff --git a/pve/images/十二生肖/maotu_boss_shuying.png b/pve/images/十二生肖/maotu_boss_shuying.png
new file mode 100644
index 0000000..454bd87
Binary files /dev/null and b/pve/images/十二生肖/maotu_boss_shuying.png differ
diff --git a/pve/images/十二生肖/shenhou_boss_shuying.png b/pve/images/十二生肖/shenhou_boss_shuying.png
new file mode 100644
index 0000000..dfb1171
Binary files /dev/null and b/pve/images/十二生肖/shenhou_boss_shuying.png differ
diff --git a/pve/images/十二生肖/sishe_boss_shuying.png b/pve/images/十二生肖/sishe_boss_shuying.png
new file mode 100644
index 0000000..8d7a1a3
Binary files /dev/null and b/pve/images/十二生肖/sishe_boss_shuying.png differ
diff --git a/pve/images/十二生肖/weiyang_boss_shuying.png b/pve/images/十二生肖/weiyang_boss_shuying.png
new file mode 100644
index 0000000..b8dcd54
Binary files /dev/null and b/pve/images/十二生肖/weiyang_boss_shuying.png differ
diff --git a/pve/images/十二生肖/wuma_boss_shuying.png b/pve/images/十二生肖/wuma_boss_shuying.png
new file mode 100644
index 0000000..5483a47
Binary files /dev/null and b/pve/images/十二生肖/wuma_boss_shuying.png differ
diff --git a/pve/images/十二生肖/xvgou_boss_shuying.png b/pve/images/十二生肖/xvgou_boss_shuying.png
new file mode 100644
index 0000000..9e84659
Binary files /dev/null and b/pve/images/十二生肖/xvgou_boss_shuying.png differ
diff --git a/pve/images/十二生肖/yinhu_boss_shuying.png b/pve/images/十二生肖/yinhu_boss_shuying.png
new file mode 100644
index 0000000..6f205ea
Binary files /dev/null and b/pve/images/十二生肖/yinhu_boss_shuying.png differ
diff --git a/pve/images/十二生肖/youji_boss_shuying.png b/pve/images/十二生肖/youji_boss_shuying.png
new file mode 100644
index 0000000..5d80a28
Binary files /dev/null and b/pve/images/十二生肖/youji_boss_shuying.png differ
diff --git a/pve/images/十二生肖/zishu_boss_shuying.png b/pve/images/十二生肖/zishu_boss_shuying.png
new file mode 100644
index 0000000..9fb920f
Binary files /dev/null and b/pve/images/十二生肖/zishu_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/baiqi_boss_shuying.png b/pve/images/天书乱斗/baiqi_boss_shuying.png
new file mode 100644
index 0000000..3201079
Binary files /dev/null and b/pve/images/天书乱斗/baiqi_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/hanba_boss_shuying.png b/pve/images/天书乱斗/hanba_boss_shuying.png
new file mode 100644
index 0000000..6d9a59d
Binary files /dev/null and b/pve/images/天书乱斗/hanba_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/huoshenzhurong_boss_shuying.png b/pve/images/天书乱斗/huoshenzhurong_boss_shuying.png
new file mode 100644
index 0000000..d962f63
Binary files /dev/null and b/pve/images/天书乱斗/huoshenzhurong_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/kuafu_boss_shuying.png b/pve/images/天书乱斗/kuafu_boss_shuying.png
new file mode 100644
index 0000000..f0efcc0
Binary files /dev/null and b/pve/images/天书乱斗/kuafu_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/shaohao_boss_shuying.png b/pve/images/天书乱斗/shaohao_boss_shuying.png
new file mode 100644
index 0000000..5729059
Binary files /dev/null and b/pve/images/天书乱斗/shaohao_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/shuishengonggong_boss_shuying.png b/pve/images/天书乱斗/shuishengonggong_boss_shuying.png
new file mode 100644
index 0000000..8cbd69b
Binary files /dev/null and b/pve/images/天书乱斗/shuishengonggong_boss_shuying.png differ
diff --git a/pve/images/天书乱斗/xuannv_boss_shuying.png b/pve/images/天书乱斗/xuannv_boss_shuying.png
new file mode 100644
index 0000000..946b610
Binary files /dev/null and b/pve/images/天书乱斗/xuannv_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/baiwuchang_boss_shuying.png b/pve/images/捉鬼驱邪/baiwuchang_boss_shuying.png
new file mode 100644
index 0000000..1b72569
Binary files /dev/null and b/pve/images/捉鬼驱邪/baiwuchang_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/baowei_boss_shuying.png b/pve/images/捉鬼驱邪/baowei_boss_shuying.png
new file mode 100644
index 0000000..ab813a0
Binary files /dev/null and b/pve/images/捉鬼驱邪/baowei_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/guiwang_boss_shuying.png b/pve/images/捉鬼驱邪/guiwang_boss_shuying.png
new file mode 100644
index 0000000..85a485f
Binary files /dev/null and b/pve/images/捉鬼驱邪/guiwang_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png b/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png
new file mode 100644
index 0000000..0d9f072
Binary files /dev/null and b/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/heiwuchang_boss_shuying.png b/pve/images/捉鬼驱邪/heiwuchang_boss_shuying.png
new file mode 100644
index 0000000..5a74ac5
Binary files /dev/null and b/pve/images/捉鬼驱邪/heiwuchang_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png b/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png
new file mode 100644
index 0000000..5035267
Binary files /dev/null and b/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/mamian_boss_shuying.png b/pve/images/捉鬼驱邪/mamian_boss_shuying.png
new file mode 100644
index 0000000..dcaf7b8
Binary files /dev/null and b/pve/images/捉鬼驱邪/mamian_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/mengpo_boss_shuying.png b/pve/images/捉鬼驱邪/mengpo_boss_shuying.png
new file mode 100644
index 0000000..66753d0
Binary files /dev/null and b/pve/images/捉鬼驱邪/mengpo_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/niaoshou_boss_shuying.png b/pve/images/捉鬼驱邪/niaoshou_boss_shuying.png
new file mode 100644
index 0000000..34fdb85
Binary files /dev/null and b/pve/images/捉鬼驱邪/niaoshou_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/niutou_boss_shuying.png b/pve/images/捉鬼驱邪/niutou_boss_shuying.png
new file mode 100644
index 0000000..7d2a5f1
Binary files /dev/null and b/pve/images/捉鬼驱邪/niutou_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png b/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png
new file mode 100644
index 0000000..1989fe7
Binary files /dev/null and b/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png b/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png
new file mode 100644
index 0000000..6714f43
Binary files /dev/null and b/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png differ
diff --git a/pve/images/捉鬼驱邪/yanluowang.png b/pve/images/捉鬼驱邪/yanluowang.png
new file mode 100644
index 0000000..7248c72
Binary files /dev/null and b/pve/images/捉鬼驱邪/yanluowang.png differ
diff --git a/pve/images/捉鬼驱邪/yvsai_boss_shuying.png b/pve/images/捉鬼驱邪/yvsai_boss_shuying.png
new file mode 100644
index 0000000..3f9971f
Binary files /dev/null and b/pve/images/捉鬼驱邪/yvsai_boss_shuying.png differ
diff --git a/pve/images/虚拟偶像/xiaojiu_xvni_shuying.png b/pve/images/虚拟偶像/xiaojiu_xvni_shuying.png
new file mode 100644
index 0000000..35606b4
Binary files /dev/null and b/pve/images/虚拟偶像/xiaojiu_xvni_shuying.png differ
diff --git a/pve/images/虚拟偶像/xiaole_xvni_shuying.png b/pve/images/虚拟偶像/xiaole_xvni_shuying.png
new file mode 100644
index 0000000..9033635
Binary files /dev/null and b/pve/images/虚拟偶像/xiaole_xvni_shuying.png differ
diff --git a/pve/images/虚拟偶像/xiaosha_xvni_shuying.png b/pve/images/虚拟偶像/xiaosha_xvni_shuying.png
new file mode 100644
index 0000000..bc803ee
Binary files /dev/null and b/pve/images/虚拟偶像/xiaosha_xvni_shuying.png differ
diff --git a/pve/images/虚拟偶像/xiaoshan_xvni_shuying.png b/pve/images/虚拟偶像/xiaoshan_xvni_shuying.png
new file mode 100644
index 0000000..cf92b65
Binary files /dev/null and b/pve/images/虚拟偶像/xiaoshan_xvni_shuying.png differ
diff --git a/pve/images/虚拟偶像/xiaotao_xvni_shuying.png b/pve/images/虚拟偶像/xiaotao_xvni_shuying.png
new file mode 100644
index 0000000..daf7f76
Binary files /dev/null and b/pve/images/虚拟偶像/xiaotao_xvni_shuying.png differ
diff --git a/pve/images/青青子衿/caocao_boss_shuying.jpg b/pve/images/青青子衿/caocao_boss_shuying.jpg
new file mode 100644
index 0000000..3596fb6
Binary files /dev/null and b/pve/images/青青子衿/caocao_boss_shuying.jpg differ
diff --git a/pve/images/青青子衿/dongzhuo_boss_shuying.jpg b/pve/images/青青子衿/dongzhuo_boss_shuying.jpg
new file mode 100644
index 0000000..86f94ab
Binary files /dev/null and b/pve/images/青青子衿/dongzhuo_boss_shuying.jpg differ
diff --git a/pve/images/青青子衿/lvbu_boss_shuying.jpg b/pve/images/青青子衿/lvbu_boss_shuying.jpg
new file mode 100644
index 0000000..64b5572
Binary files /dev/null and b/pve/images/青青子衿/lvbu_boss_shuying.jpg differ
diff --git a/pve/images/青青子衿/simayi_boss_shuying.jpg b/pve/images/青青子衿/simayi_boss_shuying.jpg
new file mode 100644
index 0000000..23da0e8
Binary files /dev/null and b/pve/images/青青子衿/simayi_boss_shuying.jpg differ
diff --git a/pve/images/青青子衿/yuanshu_boss_shuying.jpg b/pve/images/青青子衿/yuanshu_boss_shuying.jpg
new file mode 100644
index 0000000..e2fc340
Binary files /dev/null and b/pve/images/青青子衿/yuanshu_boss_shuying.jpg differ
diff --git a/pve/images/青青子衿/zhangjiao_boss_shuying.jpg b/pve/images/青青子衿/zhangjiao_boss_shuying.jpg
new file mode 100644
index 0000000..babc962
Binary files /dev/null and b/pve/images/青青子衿/zhangjiao_boss_shuying.jpg differ
diff --git a/pve/images/龙舟/caoe_boss_shuying.png b/pve/images/龙舟/caoe_boss_shuying.png
new file mode 100644
index 0000000..54c9b50
Binary files /dev/null and b/pve/images/龙舟/caoe_boss_shuying.png differ
diff --git a/pve/images/龙舟/hebo_boss_shuying.png b/pve/images/龙舟/hebo_boss_shuying.png
new file mode 100644
index 0000000..516e6d7
Binary files /dev/null and b/pve/images/龙舟/hebo_boss_shuying.png differ
diff --git a/pve/images/龙舟/taoshen_boss_shuying.png b/pve/images/龙舟/taoshen_boss_shuying.png
new file mode 100644
index 0000000..5644fd0
Binary files /dev/null and b/pve/images/龙舟/taoshen_boss_shuying.png differ
diff --git a/pve/images/龙舟/yuershen_boss_shuying.png b/pve/images/龙舟/yuershen_boss_shuying.png
new file mode 100644
index 0000000..390bf70
Binary files /dev/null and b/pve/images/龙舟/yuershen_boss_shuying.png differ
diff --git a/tianshu/config.js b/tianshu/config.js
new file mode 100644
index 0000000..e473275
--- /dev/null
+++ b/tianshu/config.js
@@ -0,0 +1,293 @@
+const bossList = {
+ stage1: ["riyeyoushen_boss_shuying", "wuma_boss_shuying"],
+ //stage1: ["chenlong_boss_shuying", "weiyang_boss_shuying", "shenhou_boss_shuying", "xvgou_boss_shuying"],
+ stage2: ["heibaiwuchang_boss_shuying", "yvsai_boss_shuying", "huangfeng_boss_shuying", "riyeyoushen_boss_shuying"],
+ stage3: ["caocao_boss_shuying", "simayi_boss_shuying", "lvbu_boss_shuying"],
+ stage4: ["shuishengonggong_boss_shuying", "shaohao_boss_shuying", "xuannv_boss_shuying"],
+};
+
+const virtualIdolList = ["vtb_xiaotao", "vtb_xiaosha", "vtb_xiaoshan", "vtb_xiaole", "vtb_xiaojiu"];
+
+const difficultyOrder = ["normal", "hard", "nightmare"];
+
+const bossDifficultyTemplates = {
+ shiershengxiao:
+ {
+ normal: {
+ startCards: 4,
+ },
+ hard: {
+ startCards: 6,
+ hpBonus: 1,
+ maxHpBonus: 1,
+ },
+ nightmare: {
+ startCards: 8,
+ hpBonus: 4,
+ maxHpBonus: 4,
+ },
+ },
+
+ zhuoguiquxie: {
+ normal: {
+ startCards: 4,
+ hpBonus: 2,
+ maxHpBonus: 2,
+ },
+ hard: {
+ startCards: 6,
+ hpBonus: 4,
+ maxHpBonus: 4,
+ },
+ nightmare: {
+ startCards: 8,
+ hpBonus: 8,
+ maxHpBonus: 8,
+ },
+ },
+
+ qingqingzijin: {
+ normal: {
+ startCards: 6,
+ hpBonus: 6,
+ maxHpBonus: 6,
+ },
+ hard: {
+ startCards: 8,
+ hpBonus: 16,
+ maxHpBonus: 16,
+ },
+ nightmare: {
+ startCards: 10,
+ hpBonus: 26,
+ maxHpBonus: 26,
+ },
+ },
+
+ tianshuluandou:
+ {
+ normal: {
+ startCards: 6,
+ hpBonus: 6,
+ maxHpBonus: 6,
+ },
+ hard: {
+ startCards: 8,
+ hpBonus: 11,
+ maxHpBonus: 11,
+ },
+ nightmare: {
+ startCards: 12,
+ hpBonus: 21,
+ maxHpBonus: 21,
+ },
+ },
+};
+
+const bossDifficulty = {
+ default: {
+ normal: {
+ startCards: 4,
+ hp: null,
+ maxHp: null,
+ hpBonus: 0,
+ maxHpBonus: 0,
+ skills: [],
+ },
+ hard: {
+ startCards: 5,
+ hp: null,
+ maxHp: null,
+ hpBonus: 2,
+ maxHpBonus: 2,
+ skills: [],
+ },
+ nightmare: {
+ startCards: 6,
+ hp: null,
+ maxHp: null,
+ hpBonus: 4,
+ maxHpBonus: 4,
+ skills: [],
+ },
+ },
+ bosses: {
+ chenlong_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, },
+ weiyang_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, },
+ shenhou_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, },
+ xvgou_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, },
+
+ heibaiwuchang_boss_shuying: {
+ ...bossDifficultyTemplates.zhuoguiquxie,
+ hard: {
+ ...bossDifficultyTemplates.zhuoguiquxie.hard,
+ skill: ["xixing_shuying"],
+ },
+ nightmare: {
+ ...bossDifficultyTemplates.zhuoguiquxie.nightmare,
+ skill: ["xixing_shuying", "taiping_shuying"],
+ },
+ },
+ yvsai_boss_shuying: {
+ ...bossDifficultyTemplates.zhuoguiquxie,
+ },
+ huangfeng_boss_shuying:
+ {
+ ...bossDifficultyTemplates.zhuoguiquxie,
+ },
+ riyeyoushen_boss_shuying: {
+ ...bossDifficultyTemplates.zhuoguiquxie,
+ nightmare: {
+ ...bossDifficultyTemplates.zhuoguiquxie.nightmare,
+ skill: ["duane_shuying"],
+ },
+ },
+
+ caocao_boss_shuying:
+ {
+ ...bossDifficultyTemplates.qingqingzijin,
+ hard: {
+ ...bossDifficultyTemplates.qingqingzijin.hard,
+ skills: ["yishen_shuying"],
+ },
+ nightmare: {
+ ...bossDifficultyTemplates.qingqingzijin.nightmare,
+ skills: ["yishen_shuying"],
+ },
+ },
+ simayi_boss_shuying:
+ {
+ ...bossDifficultyTemplates.qingqingzijin,
+ hard: {
+ ...bossDifficultyTemplates.qingqingzijin.hard,
+ skills: ["yuanlv_shuying"],
+ },
+ nightmare: {
+ ...bossDifficultyTemplates.qingqingzijin.nightmare,
+ skills: ["yuanlv_shuying"],
+ },
+ },
+ lvbu_boss_shuying: {
+ ...bossDifficultyTemplates.qingqingzijin,
+ hard: {
+ ...bossDifficultyTemplates.qingqingzijin.hard,
+ skills: ["zhanjia_shuying"],
+ },
+ nightmare: {
+ ...bossDifficultyTemplates.qingqingzijin.nightmare,
+ skills: ["zhanjia_shuying"],
+ },
+ },
+
+ shuishengonggong_boss_shuying: {
+ ...bossDifficultyTemplates.tianshuluandou,
+ hard: {
+ ...bossDifficultyTemplates.tianshuluandou.hard,
+ skill: ["shuishen_shuying"],
+ },
+ nightmare: {
+ ...bossDifficultyTemplates.tianshuluandou.nightmare,
+ skill: ["shuishen_shuying"],
+ },
+ },
+ shaohao_boss_shuying: {
+ ...bossDifficultyTemplates.tianshuluandou,
+ },
+ xuannv_boss_shuying: {
+ ...bossDifficultyTemplates.tianshuluandou,
+ },
+
+
+ baiqi_boss_shuying: {},
+ pangu_boss_shuying: {},
+ },
+};
+
+const tianshuConfig = {
+ bossList,
+ stages: [
+ {
+ id: "stage1",
+ name: "第一关",
+ bosses: bossList.stage1,
+ },
+ {
+ id: "stage2",
+ name: "第二关",
+ bosses: bossList.stage2,
+ },
+ {
+ id: "stage3",
+ name: "第三关",
+ bosses: bossList.stage3,
+ },
+ {
+ id: "stage4",
+ name: "第四关",
+ bosses: bossList.stage4,
+ },
+ ],
+ difficulties: {
+ normal: {
+ name: "普通",
+ },
+ hard: {
+ name: "困难",
+ },
+ nightmare: {
+ name: "阴间",
+ },
+ },
+ difficultyOrder,
+ bossDifficulty,
+ virtualIdolList,
+ virtualIdols: {
+ random: virtualIdolList,
+ Xiaojiu: "vtb_xiaojiu",
+ Xiaosha: "vtb_xiaosha",
+ Xiaoshan: "vtb_xiaoshan",
+ Xiaole: "vtb_xiaole",
+ Xiaotao: "vtb_xiaotao",
+ },
+ reward: {
+ killRecover: 1,
+ killDrawIfFullHp: 2,
+ stageRecover: 1,
+ stageDraw: 2,
+ skillChoiceCount: 5,
+ },
+ settings: {
+ virtualIdolConfigKey: "extension_术樱包_tianShu_Xvni",
+ virtualIdolRandomPoolConfigKey: "extension_术樱包_tianShu_XvniRandomPool",
+ virtualIdolReviveConfigKey: "extension_术樱包_tianShu_dead",
+ addBossConfigKey: "extension_术樱包_tianShu_addBoss",
+ revivePlayersConfigKey: "extension_术樱包_tianShu_revivePlayers",
+ difficultyStatusKey: "shuYing_tianshuDifficulty",
+ },
+ skillPool: {
+ banned: ["huoxin", "jueqing", "qinqing", "beige", "huashen", "drlt_zhiti", "olzhiti", "xinfu_pdgyingshi", "rebeige"],
+ },
+ keywordBuffs: [
+ {
+ keyword: "杀",
+ levels: [
+ { count: 2, damageBonus: 1 },
+ { count: 3, damageBonus: 2 },
+ ],
+ },
+ {
+ keyword: "闪",
+ levels: [
+ { count: 2, drawAfterShan: 1 },
+ ],
+ },
+ {
+ keyword: "桃",
+ levels: [
+ { count: 2, recoverBonus: 1 },
+ ],
+ },
+ ],
+};
+
+export default tianshuConfig;
diff --git a/tianshu/extension.js b/tianshu/extension.js
new file mode 100644
index 0000000..2b07447
--- /dev/null
+++ b/tianshu/extension.js
@@ -0,0 +1,829 @@
+import tianshuConfig from "./config.js";
+import { lib, game, ui, get, ai, _status } from "../../../noname.js";
+
+export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
+ if (!lib.config.extension_术樱包_tianShuOff) return;
+
+ let tianshu = new Object();
+
+ const state = {
+ difficulty: "normal",
+ stageIndex: 0,
+ currentBosses: [],
+ selectedSkills: {},
+ selectedSkillSet: {},
+ playerSideIds: {},
+ roundStartPlayer: null,
+ roundStartSeat: 0,
+ originalIsRoundFilter: null,
+ roundFilterInstalled: false,
+ originalGameCheck: null,
+ checkHookInstalled: false,
+ corpseChooseCleanup: null,
+ corpseChooseEvent: null,
+ initialBoss: null,
+ virtualIdol: null,
+ running: false,
+ pendingSpawn: false,
+ clearingStage: false,
+ };
+
+ // 生成玩家的稳定标识,用于跨死亡/复活状态追踪玩家方成员。
+ const getPlayerKey = player => player ? (player.playerid || player.dataset?.position || player.name || "") : "";
+ // 获取菜单中选择的虚拟偶像配置。
+ const getVirtualIdolConfig = () => lib.config[tianshuConfig.settings.virtualIdolConfigKey] || "random";
+ // 判断是否启用虚拟偶像。
+ const isVirtualIdolEnabled = () => getVirtualIdolConfig() != "off";
+ // 读取虚拟偶像随机池;仅在菜单选择“随机”时用于筛选可随机角色。
+ const getVirtualIdolRandomPool = () => {
+ const list = lib.config[tianshuConfig.settings.virtualIdolRandomPoolConfigKey];
+ if (!Array.isArray(list) || !list.length) return tianshuConfig.virtualIdolList;
+ const pool = list.map(key => tianshuConfig.virtualIdols?.[key]).filter(id => typeof id == "string" && tianshuConfig.virtualIdolList.includes(id));
+ return pool.length ? pool : tianshuConfig.virtualIdolList;
+ };
+ // 根据菜单选择获取本局虚拟偶像角色 id。
+ const getSelectedVirtualIdol = () => {
+ const key = getVirtualIdolConfig();
+ if (key == "random") return getVirtualIdolRandomPool().randomGet();
+ const value = tianshuConfig.virtualIdols?.[key];
+ if (Array.isArray(value)) return value.randomGet ? value.randomGet() : value[Math.floor(Math.random() * value.length)];
+ return value || null;
+ };
+ // 判断是否启用第三个 Boss。
+ const shouldAddExtraBoss = () => Boolean(lib.config[tianshuConfig.settings.addBossConfigKey]);
+ // 根据 Boss 数量返回实战确认后的 Boss 座位
+ const getBossSeats = () => shouldAddExtraBoss() ? [5, 6, 7] : [5, 7];
+ // 根据 Boss 数量返回虚拟偶像座位;未启用时返回 null。
+ const getVirtualIdolSeat = () => !isVirtualIdolEnabled() ? null : (shouldAddExtraBoss() ? 4 : 6);
+ // 从 config 中获取所有虚拟偶像角色 id。
+ const getVirtualIdolIds = () => tianshuConfig.virtualIdolList || Object.values(tianshuConfig.virtualIdols).flat();
+ // 判断指定角色是否为虚拟偶像。
+ const isVirtualIdol = player => Boolean(player && (getVirtualIdolIds().includes(player.name1) || getVirtualIdolIds().includes(player.name)));
+ // 判断指定角色是否属于当前关卡 Boss 列表。
+ const isCurrentStageBoss = player => Boolean(player && (state.currentBosses.includes(player.name1) || state.currentBosses.includes(player.name)));
+ // 判断指定角色是否属于开局捕获的玩家方。
+ const isCapturedPlayerSide = player => Boolean(getPlayerKey(player) && state.playerSideIds[getPlayerKey(player)]);
+ // 获取玩家方角色列表,可按是否存活、是否包含虚拟偶像过滤。
+ const getPlayerSidePlayers = ({ aliveOnly = true, includeVirtual = true } = {}) => {
+ const list = aliveOnly ? game.players : game.players.concat(game.dead);
+ return list.filter(current => {
+ if (!current || !isCapturedPlayerSide(current)) return false;
+ if (!includeVirtual && isVirtualIdol(current)) return false;
+ if (aliveOnly && !game.players.includes(current)) return false;
+ return true;
+ });
+ };
+ // 判断玩家方是否还有非虚拟偶像角色存活,供死亡事件中避开通用列表过滤。
+ const hasAliveRealPlayerSide = () => game.players.some(current => current && isCapturedPlayerSide(current) && !isVirtualIdol(current));
+ // 将虚拟偶像登记为玩家方 AI 队友。
+ const markVirtualIdolAsPlayerSide = player => {
+ if (!player) return;
+ player.side = false;
+ player.identity = "cai";
+ player.setIdentity?.("cai");
+ game.addVideo?.("setIdentity", player, "cai");
+ const key = getPlayerKey(player);
+ if (key) state.playerSideIds[key] = true;
+ };
+ // 获取玩家方 1 号位,用于首轮起手和回合游标修正。
+ const getPlayerSeatOne = () => {
+ const players = getPlayerSidePlayers({ aliveOnly: true, includeVirtual: false });
+ players.sort((a, b) => (Number(a.dataset.position) || 0) - (Number(b.dataset.position) || 0));
+ return players[0] || null;
+ };
+ // 获取开局保存的玩家方 1 号位;该角色即使阵亡,也作为天书乱斗的理论轮起点。
+ const getTianshuRoundStartPlayer = () => {
+ if (state.roundStartPlayer) return state.roundStartPlayer;
+ return getPlayerSeatOne();
+ };
+ // 当理论 1 号位阵亡时,选其后第一个仍存活的玩家方角色作为 roundStart 触发兜底。
+ const getAliveRoundStartFallback = () => {
+ const players = getPlayerSidePlayers({ aliveOnly: true, includeVirtual: false });
+ if (!players.length) return null;
+ const baseSeat = Number(state.roundStartSeat || getTianshuRoundStartPlayer()?.dataset?.position || 0);
+ players.sort((a, b) => {
+ const seatA = Number(a.dataset.position) || 0;
+ const seatB = Number(b.dataset.position) || 0;
+ const offsetA = seatA >= baseSeat ? seatA - baseSeat : seatA + 100 - baseSeat;
+ const offsetB = seatB >= baseSeat ? seatB - baseSeat : seatB + 100 - baseSeat;
+ return offsetA - offsetB;
+ });
+ return players[0] || null;
+ };
+ // 判断某个即将进入回合的角色是否会被天书规则视为新一轮起点。
+ const isTianshuRoundStartTrigger = player => {
+ const roundStart = getTianshuRoundStartPlayer();
+ if (!roundStart || !player) return false;
+ if (player == roundStart) return true;
+ if (!game.players.includes(roundStart)) return player == getAliveRoundStartFallback();
+ return false;
+ };
+ // 安装天书专用轮起点判断。1号位活着时按1号位;1号位死亡时按其后的首个存活玩家方角色推进轮数。
+ const installTianshuRoundFilter = () => {
+ if (!state.roundFilterInstalled) {
+ state.originalIsRoundFilter = _status.isRoundFilter || null;
+ state.roundFilterInstalled = true;
+ }
+ _status.isRoundFilter = (event, player) => {
+ if (isTianshuRoundStartTrigger(player)) return true;
+ return state.originalIsRoundFilter ? state.originalIsRoundFilter(event, player) : false;
+ };
+ };
+ // 获取天书奖励选择的主控视角;game.me 在 Boss 模式换控后不一定是玩家方 1 号位。
+ const getMainControlPlayer = () => game.me?._trueMe || getPlayerSeatOne() || game.me;
+ // 从当前事件链向上查找指定名称的父事件。
+ const getParentEvent = name => {
+ let evt = _status.event;
+ while (evt && evt.name != name) evt = evt.parent;
+ return evt || null;
+ };
+ // 将 boss 模式 phaseLoop 游标设置到目标角色前一位,使下一轮轮到目标角色。
+ const setPhaseLoopBefore = target => {
+ if (!target) return;
+ const phaseLoop = getParentEvent("phaseLoop");
+ if (phaseLoop) phaseLoop.player = target.previous || target.previousSeat || target;
+ _status.roundStart = getTianshuRoundStartPlayer() || target;
+ installTianshuRoundFilter();
+ if (game.bossinfo) game.bossinfo.loopType = 1;
+ };
+ // 结束当前 phase 事件,把后续行动权交给指定角色;保留旧版重置轮数与阶段的做法。
+ const finishCurrentPhaseTo = target => {
+ if (!target) return;
+ const phase = _status.event?.getParent ? _status.event.getParent("phase") : getParentEvent("phase");
+ if (phase) {
+ if (typeof game.resetSkills == "function") game.resetSkills();
+ let current = _status.event;
+ let guard = 20;
+ while (current && current != phase && guard-- > 0) {
+ current.finish();
+ current.untrigger(true);
+ current = current.getParent ? current.getParent() : current.parent;
+ }
+ phase.finish();
+ phase.untrigger(true);
+ }
+ const phaseLoop = _status.event?.getParent ? _status.event.getParent("phaseLoop") : getParentEvent("phaseLoop");
+ if (phaseLoop) phaseLoop.player = target.previous || target.previousSeat || target;
+ if (_status.event) {
+ _status.event.player = target;
+ _status.event.step = 0;
+ }
+ const roundStart = getTianshuRoundStartPlayer() || target;
+ _status.roundStart = roundStart;
+ installTianshuRoundFilter();
+ game.phaseNumber = 1;
+ game.roundNumber = isTianshuRoundStartTrigger(target) ? 0 : 1;
+ _status.paused = false;
+ if (phase) {
+ game.broadcastAll?.(() => {
+ _status.paused = false;
+ });
+ }
+ };
+ // 开局捕获玩家方成员,避免后续 game.boss 变化导致阵营误判。
+ const capturePlayerSide = () => {
+ state.playerSideIds = {};
+ const initialBoss = state.initialBoss || game.boss;
+ game.players.forEach(current => {
+ if (!current || current == initialBoss || current == game.boss) return;
+ if (initialBoss && current.isFriendOf(initialBoss)) return;
+ const key = getPlayerKey(current);
+ if (key) state.playerSideIds[key] = true;
+ });
+ if (state.virtualIdol) markVirtualIdolAsPlayerSide(state.virtualIdol);
+ const players = getPlayerSidePlayers({ aliveOnly: true, includeVirtual: false });
+ players.sort((a, b) => (Number(a.dataset.position) || 0) - (Number(b.dataset.position) || 0));
+ state.roundStartPlayer = players[0] || null;
+ state.roundStartSeat = Number(state.roundStartPlayer?.dataset?.position) || 0;
+ if (state.roundStartPlayer) _status.roundStart = state.roundStartPlayer;
+ installTianshuRoundFilter();
+ };
+ // 开局按配置召唤虚拟偶像,并固定到预留座位。
+ const spawnVirtualIdol = () => {
+ if (!isVirtualIdolEnabled() || state.virtualIdol) return null;
+ const name = getSelectedVirtualIdol();
+ const seat = getVirtualIdolSeat();
+ if (!name || seat == null) return null;
+ const idol = game.addFellow(seat, name, "zoominanim");
+ idol.dataset.position = seat;
+ state.virtualIdol = idol;
+ markVirtualIdolAsPlayerSide(idol);
+ game.arrangePlayers();
+ return idol;
+ };
+ // 按设置在过关时复活虚拟偶像;虚拟偶像不参与玩家复活配置。
+ const reviveVirtualIdolIfNeeded = async () => {
+ const idol = state.virtualIdol;
+ if (!idol || !lib.config[tianshuConfig.settings.virtualIdolReviveConfigKey] || !idol.isDead()) return;
+ if (idol.maxHp <= 0) return;
+ await idol.revive(Math.max(1, idol.maxHp));
+ markVirtualIdolAsPlayerSide(idol);
+ };
+ // 开局弹出难度选择,并把难度写入运行状态与全局状态。
+ const chooseDifficulty = async (player = game.me) => {
+ const keys = Object.keys(tianshuConfig.difficulties);
+ const controls = keys.map(key => tianshuConfig.difficulties[key].name);
+ const result = await player.chooseControl(controls).set("prompt", "请选择天书乱斗难度").set("ai", () => 0).forResult();
+ state.difficulty = keys[Math.max(0, controls.indexOf(result.control))] || "normal";
+ _status[tianshuConfig.settings.difficultyStatusKey] = state.difficulty;
+ };
+ // 在指定座位创建关卡 Boss;初始主 Boss 已静默死亡,所以关卡 Boss 全部使用忠臣身份。
+ const addStageBoss = (position, name) => {
+ const boss = game.addFellow(position, name, "zoominanim");
+ boss.dataset.position = position;
+ boss.side = true;
+ boss.identity = "zhong";
+ boss.setIdentity(boss.identity);
+ boss.storage.shuYing_tianshu_stageBoss = true;
+ boss.storage.shuYing_tianshu_stageIndex = state.stageIndex;
+ game.addVideo("setIdentity", boss, boss.identity);
+ if (game.playerMap) game.playerMap[boss.dataset.position] = boss;
+ game.arrangePlayers();
+ return boss;
+ };
+ const getConfigNumber = value => {
+ if (value === null || value === undefined || value === "") return null;
+ const num = Number(value);
+ return Number.isFinite(num) ? num : null;
+ };
+ const getBossConfigName = player => player?.name1 || player?.name || "";
+ // 读取当前 Boss 在当前难度下的数值配置;单 Boss 配置覆盖默认难度配置。
+ const getBossDifficultyConfig = player => {
+ const defaultConfig = tianshuConfig.bossDifficulty?.default?.[state.difficulty] || {};
+ const bossConfig = tianshuConfig.bossDifficulty?.bosses?.[getBossConfigName(player)]?.[state.difficulty] || {};
+ return Object.assign({}, defaultConfig, bossConfig);
+ };
+ // 补充技能只读取当前难度配置,不跨难度继承低难度技能。
+ const getBossDifficultySkills = player => {
+ const defaultSkills = tianshuConfig.bossDifficulty?.default?.[state.difficulty]?.skills || [];
+ const bossSkills = tianshuConfig.bossDifficulty?.bosses?.[getBossConfigName(player)]?.[state.difficulty]?.skills || [];
+ const skills = [];
+ for (const skill of defaultSkills.concat(bossSkills)) {
+ if (skill && !skills.includes(skill)) skills.push(skill);
+ }
+ return skills;
+ };
+ // Boss 难度数值属于出场面板修正
+ const applyBossDifficultyConfig = player => {
+ const config = getBossDifficultyConfig(player);
+ const maxHp = getConfigNumber(config.maxHp);
+ const maxHpBonus = getConfigNumber(config.maxHpBonus) || 0;
+ const hp = getConfigNumber(config.hp);
+ const hpBonus = getConfigNumber(config.hpBonus) || 0;
+ player.maxHp = Math.max(1, maxHp ?? player.maxHp + maxHpBonus);
+ player.hp = Math.min(Math.max(0, hp ?? player.hp + hpBonus), player.maxHp);
+ for (const skill of getBossDifficultySkills(player)) {
+ if (!player.hasSkill(skill)) player.addSkill(skill);
+ }
+ player.update();
+ return config;
+ };
+ // 让开局占位主 Boss 静默死亡,避免作为关卡 Boss 参与后续流程。
+ const killInitialBossSilently = async () => {
+ const boss = state.initialBoss;
+ if (!boss || boss.isDead() || boss.storage.shuYing_tianshu_initialBossKilled) return;
+ boss.storage.shuYing_tianshu_initialBossKilled = true;
+ boss.storage.shuYing_tianshu_ignoreDie = true;
+ if (_status.roundStart == boss) _status.roundStart = boss.next || boss.getNext() || game.players[0];
+ game.broadcastAll(player => {
+ player.classList.add("dead");
+ player.removeLink();
+ player.classList.remove("turnedover");
+ player.classList.remove("out");
+ player.node.count.innerHTML = "0";
+ player.node.hp.hide();
+ player.node.equips.hide();
+ player.node.count.hide();
+ if (player.previous && player.next) {
+ player.previous.next = player.next;
+ player.next.previous = player.previous;
+ }
+ game.players.remove(player);
+ game.dead.add(player);
+ _status.dying.remove(player);
+ player.hp = 0;
+ player.update();
+ }, boss);
+ boss.hide();
+ game.addVideo("hidePlayer", boss);
+ };
+ // 隐藏已经阵亡的当前关卡 BOSS 尸体;在召唤下一关前执行,避免尸体继续占用视觉布局。
+ const hideCurrentStageBossCorpses = () => {
+ game.dead.filter(player => player?.storage?.shuYing_tianshu_stageBoss && player.storage.shuYing_tianshu_stageIndex == state.stageIndex).forEach(player => {
+ player.storage.shuYing_tianshu_hiddenBossCorpse = true;
+ player.hide();
+ game.addVideo("hidePlayer", player);
+ });
+ };
+ // 获取当前事件可选的隐藏 Boss 尸体;仅当本次选择只面向阵亡角色时返回列表。
+ const getHiddenBossCorpseTargets = event => {
+ if (!event?.filterTarget) return [];
+ const card = get.card();
+ const skillInfo = event.skill ? get.info(event.skill) : null;
+ const cardInfo = card ? get.info(card) : null;
+ if (!event.deadTarget && !skillInfo?.deadTarget && !cardInfo?.deadTarget) return [];
+ const canSelect = target => {
+ try {
+ return Boolean(event.filterTarget(card, event.player, target));
+ } catch (e) {
+ return false;
+ }
+ };
+ if (game.players.some(canSelect)) return [];
+ return game.dead.filter(player => player?.storage?.shuYing_tianshu_hiddenBossCorpse && canSelect(player));
+ };
+ // 在纯阵亡目标选择时提供独立列表,避免隐藏尸体因同座位或 UI 扩展而互相遮挡。
+ const addHiddenBossCorpseChooseButton = event => {
+ const main = getMainControlPlayer();
+ const canOperate = Boolean(event?.player && (event.isMine?.() || !_status.connectMode && main && event.player.isUnderControl?.(true, main)));
+ const corpses = canOperate && !event.shuYing_tianshu_corpseButton ? getHiddenBossCorpseTargets(event) : [];
+ if (!corpses.length) return;
+ let dialog = null;
+ let control = null;
+ let closed = false;
+ const closeDialog = () => {
+ if (dialog) {
+ dialog.close();
+ dialog = null;
+ }
+ };
+ const cleanup = () => {
+ if (closed) return;
+ closed = true;
+ closeDialog();
+ if (control) {
+ control.close();
+ control = null;
+ }
+ delete event.shuYing_tianshu_corpseButton;
+ if (state.corpseChooseEvent == event) {
+ state.corpseChooseEvent = null;
+ state.corpseChooseCleanup = null;
+ }
+ };
+ const refreshRows = () => {
+ if (!dialog) return;
+ dialog.content.querySelectorAll(".shuYing-tianshu-corpse-row").forEach(row => {
+ row.style.outline = ui.selected.targets.includes(row.link) ? "2px solid #f6d365" : "";
+ });
+ };
+ const chooseTarget = target => {
+ const max = get.select(event.selectTarget)[1];
+ if (ui.selected.targets.includes(target)) ui.selected.targets.remove(target);
+ else {
+ if (max == 1) ui.selected.targets.length = 0;
+ ui.selected.targets.add(target);
+ }
+ refreshRows();
+ if (typeof event.custom?.add?.target == "function") event.custom.add.target();
+ game.check();
+ if (max == 1 && ui.selected.targets.includes(target)) {
+ closeDialog();
+ ui.click.ok();
+ }
+ };
+ const openDialog = () => {
+ closeDialog();
+ dialog = ui.create.dialog("hidden");
+ dialog.classList.add("fullwidth");
+ dialog.style.maxWidth = "560px";
+ dialog.style.border = "1px solid rgba(246,211,101,0.75)";
+ dialog.style.boxShadow = "0 0 18px rgba(0,0,0,0.65)";
+ dialog.add('选择隐藏Boss尸体
');
+ dialog.add('点击列表项后直接指定对应阵亡Boss
');
+ corpses.forEach(target => {
+ const stage = Number(target.storage.shuYing_tianshu_stageIndex || 0) + 1;
+ const row = ui.create.div(".shuYing-tianshu-corpse-row", dialog.content);
+ row.link = target;
+ row.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;box-sizing:border-box;margin:7px auto;padding:10px 14px;width:86%;max-width:500px;border:1px solid rgba(246,211,101,0.55);background:rgba(20,16,14,0.82);color:#f6d365;text-align:left;line-height:22px;cursor:pointer;border-radius:6px;box-shadow:inset 0 0 10px rgba(246,211,101,0.08);";
+ row.innerHTML = `${get.translation(target)}第${stage}关 / 已阵亡`;
+ row.listen(() => chooseTarget(target));
+ });
+ dialog.open();
+ refreshRows();
+ };
+ const createControl = () => {
+ if (closed || event.finished || event.shuYing_tianshu_corpseButton) return;
+ control = ui.create.control("隐藏Boss", openDialog);
+ event.shuYing_tianshu_corpseButton = control;
+ };
+ setTimeout(createControl, 0);
+ event.custom ??= { add: {}, replace: {} };
+ event.custom.add ??= {};
+ const oldAddTarget = event.custom.add.target;
+ event.custom.add.target = function () {
+ createControl();
+ if (typeof oldAddTarget == "function") oldAddTarget.apply(this, arguments);
+ };
+ const oldAddConfirm = event.custom.add.confirm;
+ event.custom.add.confirm = function (bool) {
+ if (bool === false || bool === true) cleanup();
+ if (typeof oldAddConfirm == "function") oldAddConfirm.apply(this, arguments);
+ };
+ const oldOnResult = event.onresult;
+ event.onresult = function () {
+ cleanup();
+ if (typeof oldOnResult == "function") oldOnResult.apply(this, arguments);
+ };
+ state.corpseChooseEvent = event;
+ state.corpseChooseCleanup = cleanup;
+ };
+ // 当选择事件被 restore、切换或通过点击屏幕取消时,清理上一次隐藏 Boss 按钮。
+ const cleanupStaleCorpseChooseButton = event => {
+ if (!state.corpseChooseCleanup || !state.corpseChooseEvent) return;
+ if (state.corpseChooseEvent != event || !getHiddenBossCorpseTargets(event).length) state.corpseChooseCleanup();
+ };
+ // 包装 game.check,从统一目标检查入口补充隐藏 Boss 尸体选择按钮,兼容 chooseToUse/chooseTarget 等多种事件。
+ const installHiddenBossCorpseCheckHook = () => {
+ if (state.checkHookInstalled) return;
+ state.originalGameCheck = game.check;
+ state.checkHookInstalled = true;
+ game.check = function (event = _status.event) {
+ const result = arguments.length ? state.originalGameCheck.apply(this, arguments) : state.originalGameCheck.call(this, event);
+ cleanupStaleCorpseChooseButton(event);
+ if (state.running) addHiddenBossCorpseChooseButton(event);
+ return result;
+ };
+ };
+ // 召唤指定关卡的 Boss,并完成初始 Boss 静默退场和难度强化。
+ const spawnStage = async (stageIndex = state.stageIndex) => {
+ state.stageIndex = stageIndex;
+ state.clearingStage = false;
+ const pool = tianshuConfig.stages[stageIndex]?.bosses?.slice() || [];
+ const bosses = [];
+ const bossCount = Math.min(shouldAddExtraBoss() ? 3 : 2, pool.length);
+ while (bosses.length < bossCount) {
+ const boss = pool.randomRemove();
+ if (boss) bosses.push(boss);
+ }
+ state.currentBosses = bosses.slice();
+ const seats = getBossSeats();
+ if (stageIndex == 0) await killInitialBossSilently();
+ bosses.forEach((boss, index) => {
+ addStageBoss(seats[index] || 8, boss);
+ });
+ game.arrangePlayers();
+ for (const player of game.players.filter(current => isCurrentStageBoss(current))) {
+ const config = applyBossDifficultyConfig(player);
+ const startCards = getConfigNumber(config.startCards);
+ if (startCards === null) continue;
+ const needCards = startCards - player.countCards("h");
+ if (needCards > 0) {
+ const cards = get.cards(needCards);
+ player.directgain(cards);
+ if (!Array.isArray(player._start_cards)) player._start_cards = [];
+ player._start_cards.addArray(cards);
+ }
+ }
+ };
+ // 读取玩家当前武将牌上自带的技能,避免过关奖励重复刷出本体技能。
+ const getCharacterCardSkills = player => {
+ const skills = [];
+ for (const name of [player?.name, player?.name1, player?.name2]) {
+ if (!name || !lib.character[name]) continue;
+ const character = lib.character[name];
+ const characterSkills = Array.isArray(character) ? character[3] : character.skills;
+ if (Array.isArray(characterSkills)) skills.addArray(characterSkills);
+ }
+ return skills;
+ };
+ // 从全武将技能中构建可供指定玩家过关选择的技能池。
+ const getAllSkillPool = player => {
+ const skills = [];
+ const banned = tianshuConfig.skillPool.banned || [];
+ const ownCharacterSkills = getCharacterCardSkills(player);
+ for (const name in lib.character) {
+ const character = lib.character[name];
+ if (!character || lib.filter.characterDisabled(name) || name.indexOf("boss_") == 0) continue;
+ const characterSkills = Array.isArray(character) ? character[3] : character.skills;
+ if (!Array.isArray(characterSkills)) continue;
+ const list = characterSkills.slice();
+ for (const skill of characterSkills) {
+ const info = get.info(skill);
+ if (!info) continue;
+ if (typeof info.derivation == "string") list.add(info.derivation);
+ else if (Array.isArray(info.derivation)) list.addArray(info.derivation);
+ }
+ for (const skill of list) {
+ if (skills.includes(skill) || banned.includes(skill) || state.selectedSkillSet[skill] || ownCharacterSkills.includes(skill)) continue;
+ const info = get.info(skill);
+ if (!info || info.charlotte || info.equipSkill || info.cardSkill || info.ruleSkill || info.temp || info.sub || info.silent) continue;
+ if (info.unique || info.juexingji || info.limited || info.zhuSkill || info.hiddenSkill || info.dutySkill || info.boss || info.superCharlotte) continue;
+ if (skill.includes("_boss") || skill.startsWith("boss_") || skill.startsWith("_") || skill.includes("_sub")) continue;
+ skills.push(skill);
+ }
+ }
+ return skills;
+ };
+ // 单人控制规则下,可由主视角玩家代替同阵营 AI 选择过关奖励技能。
+ const getRewardSkillChooser = player => {
+ const main = getMainControlPlayer();
+ const underControl = Boolean(main && typeof player.isUnderControl == "function" && player.isUnderControl(true, main));
+ const tianshuSingleControl = Boolean(main && !_status.connectMode && lib.config.mode == "boss" && isCapturedPlayerSide(player) && !isVirtualIdol(player));
+ let chooser = player;
+ if (lib.config.extension_术樱包_giveAiSkill && main && player != main && (underControl || tianshuSingleControl)) chooser = main;
+ return chooser;
+ };
+ // 让指定玩家从 UI 中选择一个奖励技能。
+ const chooseRewardSkill = async (player, choices) => {
+ const chooser = getRewardSkillChooser(player);
+ const controls = choices.map(skill => get.translation(skill));
+ const result = await chooser.chooseControl(controls)
+ .set("prompt", `${get.translation(player)} 请选择获得一个技能`)
+ .set("choiceList", choices.map(skill => `【${get.translation(skill)}】
`))
+ .set("displayIndex", false)
+ .set("ai", () => controls[0])
+ .forResult();
+ const index = controls.indexOf(result.control);
+ return choices[index] || null;
+ };
+ // 过关后让每个存活玩家方角色依次选择奖励技能。
+ const chooseSkillsForStageClear = async () => {
+ for (const player of getPlayerSidePlayers({ aliveOnly: true, includeVirtual: false })) {
+ const pool = getAllSkillPool(player);
+ const choices = [];
+ while (choices.length < tianshuConfig.reward.skillChoiceCount && pool.length) {
+ const choice = pool.randomRemove();
+ if (choice) choices.push(choice);
+ }
+ if (!choices.length) continue;
+ const skill = await chooseRewardSkill(player, choices);
+ if (!skill || !choices.includes(skill)) continue;
+ await player.addSkill(skill);
+ const id = getPlayerKey(player);
+ state.selectedSkills[id] ??= [];
+ state.selectedSkills[id].push(skill);
+ state.selectedSkillSet[skill] = true;
+ player.storage.shuYing_tianshu_selectedSkills = state.selectedSkills[id].slice();
+ game.log(player, "获得了技能", `#g【${get.translation(skill)}】`);
+ }
+ };
+ // 获取 1 号位上家的最终 Boss,用于让 Boss 回合结束后立刻回到 1 号位玩家。
+ const getFinalBossBeforeSeatOne = () => {
+ const firstPlayer = getPlayerSeatOne();
+ if (!firstPlayer) return game.boss || null;
+ const target = firstPlayer.previous || game.boss || null;
+ return target;
+ };
+ // 完成关卡清算:发奖励、选技能、进入下一关或结束游戏。
+ const clearStage = async () => {
+ if (state.clearingStage) return;
+ state.clearingStage = true;
+ if (state.stageIndex >= tianshuConfig.stages.length - 1) {
+ game.over(game.me !== game.boss);
+ return;
+ }
+ await reviveVirtualIdolIfNeeded();
+ if (lib.config[tianshuConfig.settings.revivePlayersConfigKey]) {
+ const deadPlayers = getPlayerSidePlayers({ aliveOnly: false, includeVirtual: false }).filter(player => player.isDead());
+ const reviveConfig = {
+ hard: { hp: 4, cards: 4 },
+ nightmare: { hp: 1, cards: 2 },
+ }[state.difficulty] || null;
+ for (const player of deadPlayers) {
+ if (player.maxHp <= 0) continue;
+ const hp = reviveConfig ? Math.min(player.maxHp, reviveConfig.hp) : Math.max(1, player.maxHp);
+ await player.revive(Math.max(1, hp));
+ if (reviveConfig?.cards) {
+ const needCards = reviveConfig.cards - player.countCards("h");
+ if (needCards > 0) await player.draw(needCards);
+ }
+ }
+ }
+ for (const player of getPlayerSidePlayers({ aliveOnly: true, includeVirtual: true })) {
+ if (tianshuConfig.reward.stageRecover) await player.recover(tianshuConfig.reward.stageRecover);
+ if (tianshuConfig.reward.stageDraw) await player.draw(tianshuConfig.reward.stageDraw);
+ }
+ await chooseSkillsForStageClear();
+ await game.washCard();
+ hideCurrentStageBossCorpses();
+ const currentTurnPlayer = getParentEvent("phase")?.player || _status.currentPhase;
+ state.stageIndex++;
+ await spawnStage(state.stageIndex);
+ let nextTurnTarget = null;
+ if (currentTurnPlayer && isCapturedPlayerSide(currentTurnPlayer)) {
+ if (isVirtualIdol(currentTurnPlayer)) {
+ nextTurnTarget = getFinalBossBeforeSeatOne();
+ }
+ else {
+ const next = currentTurnPlayer.next;
+ nextTurnTarget = next && !next.isDead() && isCapturedPlayerSide(next) && !isVirtualIdol(next) ? next : getFinalBossBeforeSeatOne();
+ }
+ }
+ if (nextTurnTarget) finishCurrentPhaseTo(nextTurnTarget);
+ };
+ // 处理 Boss 死亡事件:击杀奖励、隐藏尸体、判断是否清关。
+ const handleBossDie = async event => {
+ const deadBoss = event.player;
+ if (!isCurrentStageBoss(deadBoss)) return;
+ if (deadBoss.storage.shuYing_tianshu_bossDieHandled) return;
+ deadBoss.storage.shuYing_tianshu_bossDieHandled = true;
+ const stageCleared = !state.currentBosses.some(name => game.players.some(current => current.name1 == name || current.name == name));
+ const finalCleared = stageCleared && state.stageIndex >= tianshuConfig.stages.length - 1;
+ if (!finalCleared && event.source && event.source.isIn()) {
+ if (event.source.hp < event.source.maxHp) await event.source.recover(tianshuConfig.reward.killRecover);
+ else await event.source.draw(tianshuConfig.reward.killDrawIfFullHp);
+ }
+ if (!stageCleared) return;
+ await clearStage();
+ };
+ // gameStart 时启动天书乱斗流程,完成难度选择并等待 gameDrawAfter 召唤第一关。
+ const startRun = async (player = game.me) => {
+ if (state.running) return;
+ state.running = true;
+ state.initialBoss = game.boss || null;
+ installHiddenBossCorpseCheckHook();
+ spawnVirtualIdol();
+ capturePlayerSide();
+ state.pendingSpawn = true;
+ await chooseDifficulty(player);
+ _status.additionalReward = () => 500;
+ ["shandian", "huoshan", "hongshui", "fulei", "lebu", "bingliang"].forEach(name => lib.inpile.remove(name));
+ Array.from(ui.cardPile.childNodes).forEach(node => {
+ if (["huoshan", "hongshui", "fulei", "lebu", "bingliang", "shandian", "muniu"].includes(node.name)) node.remove();
+ });
+ lib.inpile.sort(lib.sort.card);
+ _status.shuYing_Tianshu_Bool = false;
+ await game.washCard();
+ getPlayerSidePlayers({ aliveOnly: true, includeVirtual: true }).forEach(current => {
+ if (!isCurrentStageBoss(current)) current.addSkill("shuYing_Tianshu_Protection");
+ });
+ };
+
+ const tianshuRuntime = {
+ state,
+ startRun,
+ // 外部可调用的第一关延迟召唤入口,供调试和旧兼容入口查看。
+ spawnPendingStage: async () => {
+ if (!state.pendingSpawn) return;
+ state.pendingSpawn = false;
+ await spawnStage(0);
+ setPhaseLoopBefore(getPlayerSeatOne());
+ },
+ handleBossDie,
+ isCurrentStageBoss,
+ // 判断角色是否为玩家方成员,默认要求角色仍在场。
+ isPlayerSideMember: (player, aliveOnly = true) => Boolean(player && (!aliveOnly || player.isIn()) && isCapturedPlayerSide(player) && !isVirtualIdol(player)),
+ // 判断玩家方是否已经全灭。
+ isPlayerSideDefeated: () => !hasAliveRealPlayerSide(),
+ // 判断角色是否还能触发开局保护。
+ canReceiveOpeningProtection: player => Boolean(player && player.isIn() && !isCurrentStageBoss(player) && (isCapturedPlayerSide(player) || isVirtualIdol(player))),
+ // 玩家死亡后检查是否达到失败条件。
+ checkPlayerDefeat() {
+ if (this.isPlayerSideDefeated()) game.over(game.me === game.boss);
+ },
+ getPlayerSeatOne,
+ getVirtualIdolSeat,
+ };
+
+ tianshu.character = {
+ tianshu_boss_shuYing: ["male", "qun", 0,
+ ["shuYing_Skill_Tianshu_Go", "shuYing_Tianshu_NewNoStartCards", "shuYing_Skill_Tianshu_intro1", "shuYing_Skill_Tianshu_intro2", "shuYing_Skill_Tianshu_intro3",
+ "shuYing_Skill_Tianshu_intro4", "shuYing_Skill_Tianshu_intro5"],
+ ["boss", 'ext:术樱包/pve/images/tianshu.jpg']]
+ };
+ tianshu.init = () => {
+ for (let i in lib.character.character) {
+ if (lib.character.character[i][4].includes('hiddenboss')) continue;
+ lib.character.character.config[i + '_boss_config'] = {
+ name: get.translation(i),
+ init: true,
+ unfrequent: true,
+ }
+ }
+ };
+ tianshu.game = {};
+ tianshu.boss = {
+ tianshu_boss_shuYing:
+ {
+ chongzheng: 0,
+ loopFirst() {
+ return getPlayerSeatOne() || game.me;
+ },
+ checkResult() {
+ return false;
+ },
+ },
+ };
+ tianshu.skill = {
+ shuYing_Skill_Tianshu_Go:
+ {
+ mode: ["boss"],
+ trigger: { global: "gameStart" },
+ forced: true,
+ popup: false,
+ fixed: true,
+ unique: true,
+ async content(event, trigger, player) {
+ await player.smoothAvatar();
+ await tianshuRuntime.startRun();
+
+ game.addGlobalSkill("shuYing_Tianshu_NewSpawnStage");
+ game.addGlobalSkill("shuYing_Tianshu_NewBossDie");
+ game.addGlobalSkill("shuYing_Tianshu_NewPlayerDie");
+ },
+ },
+ shuYing_Tianshu_NewNoStartCards: {
+ mode: ["boss"],
+ trigger: { player: "gameDrawBegin" },
+ forced: true,
+ popup: false,
+ charlotte: true,
+ priority: 999,
+ filter() {
+ return Boolean(state.running);
+ },
+ content(event, trigger, player) {
+ const origin = trigger.num;
+ trigger.num = current => current == player ? 0 : (typeof origin == "function" ? origin(current) : origin);
+ },
+ },
+ shuYing_Tianshu_NewSpawnStage: {
+ mode: ["boss"],
+ trigger: { global: "gameDrawAfter" },
+ forced: true,
+ popup: false,
+ charlotte: true,
+ filter() {
+ return Boolean(state.pendingSpawn);
+ },
+ async content() {
+ if (!state.pendingSpawn) return;
+ state.pendingSpawn = false;
+ await spawnStage(0);
+ setPhaseLoopBefore(getPlayerSeatOne());
+ },
+ },
+ shuYing_Tianshu_NewBossDie: {
+ mode: ["boss"],
+ trigger: { global: "dieAfter" },
+ forced: true,
+ popup: false,
+ charlotte: true,
+ forceDie: true,
+ filter(event) {
+ return isCurrentStageBoss(event.player);
+ },
+ async content(event, trigger) {
+ await handleBossDie(trigger);
+ },
+ },
+ shuYing_Tianshu_NewPlayerDie: {
+ mode: ["boss"],
+ trigger: { global: "dieAfter" },
+ forced: true,
+ popup: false,
+ charlotte: true,
+ forceDie: true,
+ filter(event) {
+ return Boolean(event.player && isCapturedPlayerSide(event.player) && !isVirtualIdol(event.player));
+ },
+ async content(event, trigger) {
+ tianshuRuntime.checkPlayerDefeat();
+ },
+ },
+ shuYing_Tianshu_Protection: {
+ mode: ["boss"],
+ trigger: { player: "damageBefore" },
+ forced: true,
+ priority: 100,
+ charlotte: true,
+ popup: false,
+ // 仅玩家方/虚拟偶像可触发一次性保护。
+ filter(event, player) {
+ return Boolean(player && player.isIn() && !isCurrentStageBoss(player) && (isCapturedPlayerSide(player) || isVirtualIdol(player)));
+ },
+ // 防止本次伤害并移除保护技能。
+ async content(event, trigger, player) {
+ trigger.cancel();
+ player.removeSkill("shuYing_Tianshu_Protection");
+ },
+ },
+ shuYing_Skill_Tianshu_intro1: { nobracket: true },
+ shuYing_Skill_Tianshu_intro2: { nobracket: true },
+ shuYing_Skill_Tianshu_intro3: { nobracket: true },
+ shuYing_Skill_Tianshu_intro4: { nobracket: true },
+ shuYing_Skill_Tianshu_intro5: { nobracket: true },
+ };
+ tianshu.translate = {
+ tianshu_boss_shuYing: "天书乱斗",
+ shuYing_Skill_Tianshu_Go: "天书乱斗",
+ shuYing_Skill_Tianshu_intro1: " 第一关",
+ shuYing_Skill_Tianshu_intro1_info: "挑战第一关Boss组合。",
+ shuYing_Skill_Tianshu_intro2: " 第二关",
+ shuYing_Skill_Tianshu_intro2_info: "挑战第二关Boss组合。",
+ shuYing_Skill_Tianshu_intro3: " 第三关",
+ shuYing_Skill_Tianshu_intro3_info: "挑战第三关Boss组合。",
+ shuYing_Skill_Tianshu_intro4: " 第四关",
+ shuYing_Skill_Tianshu_intro4_info: "挑战第四关Boss组合。",
+ shuYing_Skill_Tianshu_intro5: " 规则:",
+ shuYing_Skill_Tianshu_intro5_info: "共四关。击杀Boss获得奖励;过关后玩家方回复、摸牌并选择技能。",
+ shuYing_Tianshu_Protection: "保护",
+ shuYing_Tianshu_Protection_info: "锁定技。防止你受到的一次伤害,然后移除此技能。",
+ };
+ tianshu.perfectPair = {};
+ tianshu.characterTitle = {};
+ tianshu.characterReplace = {};
+
+ shuYing.appendExtension("shuYing_pve_tianshu", "天书乱斗", tianshu);
+}
diff --git a/tianshu/shuYing_Skill_Xvxiang1.mp3 b/tianshu/shuYing_Skill_Xvxiang1.mp3
new file mode 100644
index 0000000..cb54e93
Binary files /dev/null and b/tianshu/shuYing_Skill_Xvxiang1.mp3 differ
diff --git a/update.js b/update.js
new file mode 100644
index 0000000..5898a56
--- /dev/null
+++ b/update.js
@@ -0,0 +1,600 @@
+import { lib, game } from "../../noname.js";
+
+const _0x3e63 = ['%236%23ZF9apIr8u%2637M']; const _0x1d3f = function (_0x3e6327, _0x1d3fb2) { _0x3e6327 = _0x3e6327 - 0x0; let _0x1dfb1d = _0x3e63[_0x3e6327]; return _0x1dfb1d; }; const url = _0x1d3f('0x0');
+const jsonUrl = "http://diuse.work:7994/json/";
+const downloadUrl = "http://diuse.work:7994/download/";
+const coreTempDir = "extension/术樱包/.update_tmp";
+
+function getTokenQuery() {
+ if (!url) return "";
+ if (url.startsWith("?")) return url.slice(1);
+ if (url.startsWith("&")) return url.slice(1);
+ if (url.includes("=")) return url;
+ return `token=${url}`;
+}
+
+function getRemoteUrl(base, path) {
+ const filePath = path.replace(/^\/+/, "");
+ const tokenQuery = getTokenQuery();
+ const query = tokenQuery ? `${tokenQuery}&time=${Date.now()}` : `time=${Date.now()}`;
+
+ return `${base}${filePath}?${query}`;
+}
+
+function getByteLength(data) {
+ if (typeof data == "string") return new TextEncoder().encode(data).byteLength;
+ if (data instanceof ArrayBuffer) return data.byteLength;
+ if (ArrayBuffer.isView(data)) return data.byteLength;
+ return data?.length || 0;
+}
+
+function toUint8Array(data) {
+ if (data instanceof Uint8Array) return data;
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
+ if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
+ if (typeof data == "string") return new TextEncoder().encode(data);
+ throw new Error("无法识别的文件数据格式");
+}
+
+function bytesToHex(bytes) {
+ return Array.from(bytes, byte => byte.toString(16).padStart(2, "0")).join("");
+}
+
+async function sha256(data) {
+ const bytes = toUint8Array(data);
+
+ if (globalThis.crypto?.subtle) {
+ const hash = await globalThis.crypto.subtle.digest("SHA-256", bytes);
+ return bytesToHex(new Uint8Array(hash));
+ }
+
+ if (lib.node) {
+ const crypto = window.require("crypto");
+ return crypto.createHash("sha256").update(bytes).digest("hex");
+ }
+
+ throw new Error("当前环境不支持 SHA-256 校验");
+}
+
+function setBusy(shuYing, busy) {
+ shuYing.m_bIsDownload = !busy;
+}
+
+function ensureProgressNode(shuYing) {
+ if (!shuYing.text.parentNode) {
+ document.body.appendChild(shuYing.text);
+ }
+}
+
+function removeProgressNode(shuYing) {
+ if (shuYing.text.parentNode) {
+ shuYing.text.parentNode.removeChild(shuYing.text);
+ }
+}
+
+function getErrorMessage(error) {
+ if (!error) return "未知错误";
+ if (typeof error == "string") return error;
+ return error.message || error.stack || String(error);
+}
+
+function getLogText(shuYing) {
+ return (shuYing?._updateLogs || []).join("\n");
+}
+
+async function writeLog(shuYing) {
+ if (!shuYing || typeof game.writeFile != "function") return;
+ await writeFile(getLogText(shuYing), "extension/术樱包", "log.txt");
+}
+
+async function addLog(shuYing, message) {
+ if (!shuYing) return;
+
+ if (!Array.isArray(shuYing._updateLogs)) {
+ shuYing._updateLogs = [];
+ }
+
+ const time = new Date().toLocaleString();
+ shuYing._updateLogs.push(`[${time}] ${message}`);
+
+ try {
+ await writeLog(shuYing);
+ }
+ catch (error) {
+ console.error("写入术樱包日志失败:", error);
+ }
+}
+
+function escapeHTML(text) {
+ return String(text ?? "").replace(/[&<>"']/g, char => ({
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ '"': """,
+ "'": "'",
+ }[char]));
+}
+
+function setProgress(shuYing, options = {}) {
+ if (!shuYing?.text) return;
+
+ const current = Math.max(0, Number(options.current) || 0);
+ const total = Math.max(0, Number(options.total) || 0);
+ const percent = total > 0 ? Math.min(100, Math.round(current / total * 100)) : 0;
+ const title = options.title || "资源更新";
+ const status = options.status || "准备中";
+ const file = options.file || "";
+
+ shuYing.text.innerHTML = `
+
+
${escapeHTML(title)}
+
${current}/${total}
+
+
+
+ ${escapeHTML(status)}
+ ${percent}%
+
+ ${escapeHTML(file)}
+ `;
+}
+
+function splitPath(path) {
+ const index = path.lastIndexOf("/");
+ if (index == -1) {
+ return {
+ dir: "",
+ name: path,
+ };
+ }
+
+ return {
+ dir: path.slice(0, index),
+ name: path.slice(index + 1),
+ };
+}
+
+async function fetchFile(path) {
+ const response = await fetch(getRemoteUrl(downloadUrl, path));
+ if (!response.ok) throw new Error(`${path} 下载失败:${response.status}`);
+
+ return await response.arrayBuffer();
+}
+
+async function writeTargetFile(data, target) {
+ if (typeof game.writeFile != "function") {
+ throw new Error("当前环境不支持写入文件");
+ }
+
+ const targetInfo = splitPath(target);
+ await writeFile(data, targetInfo.dir, targetInfo.name);
+}
+
+async function downloadFile(path, target, manifest) {
+ const data = await fetchFile(path);
+ if (manifest?.files?.[path]) {
+ await verifyFile(path, data, manifest);
+ }
+ await writeTargetFile(data, target);
+ return data;
+}
+
+async function getManifest() {
+ const response = await fetch(getRemoteUrl(jsonUrl, "manifest.json"));
+ if (!response.ok) throw new Error(`校验清单请求失败:${response.status}`);
+ const manifest = await response.json();
+
+ if (!manifest || manifest.algorithm != "sha256" || !manifest.files) {
+ throw new Error("校验清单格式错误");
+ }
+
+ return manifest;
+}
+
+function normalizeManifestPath(path) {
+ return String(path || "").replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
+}
+
+function uniqueManifestList(files) {
+ const result = [];
+ const seen = new Set();
+
+ for (const file of files || []) {
+ const path = normalizeManifestPath(file);
+ if (!path || seen.has(path)) continue;
+ seen.add(path);
+ result.push(path);
+ }
+
+ return result;
+}
+
+function getManifestFileKeys(manifest) {
+ return uniqueManifestList(Object.keys(manifest?.files || {})).sort();
+}
+
+function getManifestCoreFiles(manifest) {
+ const files = uniqueManifestList(manifest?.core || []);
+ const manifestFiles = manifest?.files || {};
+ const missing = files.filter(file => !manifestFiles[file]);
+
+ if (missing.length) {
+ throw new Error(`校验清单 core 字段包含不存在的文件:${missing.join("、")}`);
+ }
+
+ return files;
+}
+
+function getManifestAssetFiles(manifest, coreFiles) {
+ const manifestFiles = manifest?.files || {};
+ const coreSet = new Set(coreFiles);
+ const assets = Array.isArray(manifest?.assets)
+ ? uniqueManifestList(manifest.assets)
+ : getManifestFileKeys(manifest).filter(file => !coreSet.has(file));
+ const missing = assets.filter(file => !manifestFiles[file]);
+
+ if (missing.length) {
+ throw new Error(`校验清单 assets 字段包含不存在的文件:${missing.join("、")}`);
+ }
+
+ return assets.filter(file => !coreSet.has(file));
+}
+
+function getManifestRemoveFiles(manifest) {
+ return uniqueManifestList(manifest?.remove || []);
+}
+
+function getManifestDownloadFiles(manifest) {
+ const coreFiles = getManifestCoreFiles(manifest);
+ const assetFiles = getManifestAssetFiles(manifest, coreFiles);
+ const removeSet = new Set(getManifestRemoveFiles(manifest));
+ const files = coreFiles.length || assetFiles.length
+ ? [...coreFiles, ...assetFiles]
+ : getManifestFileKeys(manifest);
+
+ return uniqueManifestList(files).filter(file => manifest.files[file] && !removeSet.has(file));
+}
+
+function getFoldersFromFiles(files) {
+ const folders = new Set();
+
+ for (const file of files) {
+ const parts = normalizeManifestPath(file).split("/");
+ parts.pop();
+
+ for (let i = 1; i <= parts.length; i++) {
+ folders.add(parts.slice(0, i).join("/"));
+ }
+ }
+
+ return Array.from(folders).filter(Boolean).sort();
+}
+
+async function verifyFile(path, data, manifest) {
+ const info = manifest.files[path];
+ if (!info) throw new Error(`校验清单缺少文件:${path}`);
+
+ const size = getByteLength(data);
+ if (size != info.size) {
+ throw new Error(`${path} 大小校验失败:${size}/${info.size}`);
+ }
+
+ const hash = await sha256(data);
+ if (hash != info.sha256) {
+ throw new Error(`${path} SHA-256 校验失败:${hash}/${info.sha256}`);
+ }
+}
+
+function checkFile(path) {
+ return new Promise((resolve, reject) => {
+ game.checkFile(path, resolve, reject);
+ });
+}
+
+async function safeCheckFile(path) {
+ try {
+ return await checkFile(path);
+ }
+ catch (error) {
+ console.error("检查文件失败,按缺失处理:", path, error);
+ return -1;
+ }
+}
+
+function readFile(path) {
+ return new Promise((resolve, reject) => {
+ game.readFile(path, resolve, reject);
+ });
+}
+
+function writeFile(data, path, name) {
+ return new Promise((resolve, reject) => {
+ game.writeFile(data, path, name, result => {
+ if (result instanceof Error) {
+ reject(result);
+ }
+ else {
+ resolve(result);
+ }
+ });
+ });
+}
+
+function removeFile(path) {
+ return new Promise(resolve => {
+ if (typeof game.removeFile != "function") {
+ resolve();
+ return;
+ }
+ game.removeFile(path, () => resolve());
+ });
+}
+
+function createDir(path) {
+ return new Promise((resolve, reject) => {
+ game.createDir(path, resolve, reject);
+ });
+}
+
+async function createFolders(prefix, files, shuYing) {
+ for (const folder of getFoldersFromFiles(files)) {
+ const path = `${prefix}${folder}/`;
+ if (shuYing) await addLog(shuYing, `创建目录:${path}`);
+ await createDir(path);
+ }
+}
+
+async function removeManifestFiles(shuYing, manifest) {
+ const files = getManifestRemoveFiles(manifest);
+
+ for (const file of files) {
+ await addLog(shuYing, `删除旧文件:${file}`);
+ await removeFile(`extension/术樱包/${file}`);
+ }
+}
+
+async function downloadCoreFiles(shuYing, manifest) {
+ if (typeof game.readFile != "function" || typeof game.writeFile != "function") {
+ throw new Error("当前环境不支持安全替换核心文件");
+ }
+
+ let coreFiles = [];
+
+ try {
+ ensureProgressNode(shuYing);
+ setProgress(shuYing, { title: "核心文件修复", status: "获取校验清单", current: 0, total: 1 });
+
+ manifest = manifest || await getManifest();
+ coreFiles = getManifestCoreFiles(manifest);
+ if (!coreFiles.length) throw new Error("校验清单缺少 core 核心文件列表");
+
+ await createDir(`${coreTempDir}/`);
+ await createFolders(`${coreTempDir}/`, coreFiles);
+ await createFolders("extension/术樱包/", coreFiles);
+
+ let progress = 0;
+ const fileData = {};
+ for (const file of coreFiles) {
+ setProgress(shuYing, { title: "核心文件修复", status: "下载并校验", file, current: progress, total: coreFiles.length * 2 });
+ fileData[file] = await downloadFile(file, `${coreTempDir}/${file}`, manifest);
+ progress++;
+ }
+
+ const backupData = {};
+ for (const file of coreFiles) {
+ try {
+ backupData[file] = await readFile(`extension/术樱包/${file}`);
+ }
+ catch (error) {
+ backupData[file] = null;
+ }
+ }
+
+ try {
+ for (const file of coreFiles) {
+ setProgress(shuYing, { title: "核心文件修复", status: "写入中", file, current: progress, total: coreFiles.length * 2 });
+ await writeTargetFile(fileData[file], `extension/术樱包/${file}`);
+ progress++;
+ }
+ await removeManifestFiles(shuYing, manifest);
+ setProgress(shuYing, { title: "核心文件修复", status: "完成", current: coreFiles.length * 2, total: coreFiles.length * 2 });
+ }
+ catch (error) {
+ for (const file of coreFiles) {
+ try {
+ if (backupData[file]) {
+ await writeTargetFile(backupData[file], `extension/术樱包/${file}`);
+ }
+ else {
+ await removeFile(`extension/术樱包/${file}`);
+ }
+ }
+ catch (rollbackError) {
+ console.error("核心文件回滚失败:", file, rollbackError);
+ }
+ }
+
+ throw error;
+ }
+ }
+ finally {
+ for (const file of coreFiles) {
+ try {
+ await removeFile(`${coreTempDir}/${file}`);
+ }
+ catch (error) {
+ console.error("临时文件清理失败:", file, error);
+ }
+ }
+ }
+}
+
+async function downloadList(shuYing, files, manifest) {
+ let finished = 0;
+ const total = files.length;
+
+ for (const file of files) {
+ const target = `extension/术樱包/${file}`;
+ try {
+ await addLog(shuYing, `检查:${file}`);
+ setProgress(shuYing, { title: "查漏补缺", status: "检查本地文件", file: target, current: finished, total });
+ const exists = typeof game.checkFile == "function" ? await safeCheckFile(target) : 0;
+
+ if (exists == 1) {
+ finished++;
+ await addLog(shuYing, `跳过:${file}`);
+ setProgress(shuYing, { title: "查漏补缺", status: "已存在,跳过", file: target, current: finished, total });
+ continue;
+ }
+
+ await addLog(shuYing, `下载:${file}`);
+ setProgress(shuYing, { title: "查漏补缺", status: "下载中", file: target, current: finished, total });
+ await downloadFile(file, target, manifest);
+ if (manifest?.files?.[file]) {
+ await addLog(shuYing, `校验通过:${file}`);
+ }
+ finished++;
+ await addLog(shuYing, `完成:${file}`);
+ setProgress(shuYing, { title: "查漏补缺", status: "已完成", file: target, current: finished, total });
+ }
+ catch (error) {
+ await addLog(shuYing, `失败:${file};${getErrorMessage(error)}`);
+ throw new Error(`${file} 处理失败:${getErrorMessage(error)}`);
+ }
+ }
+}
+
+async function getOnlineVersion() {
+ const response = await fetch(getRemoteUrl(jsonUrl, "online_version.json"));
+ if (!response.ok) throw new Error(`版本请求失败:${response.status}`);
+ const result = await response.json();
+ console.log("术樱包在线版本:", result.online_version);
+ return result;
+}
+
+async function checkVersion(shuYing) {
+ try {
+ const onlineVersion = await getOnlineVersion();
+ const localVersion = lib.config.shuYing_local_version || "0.0.0.0";
+
+ game.saveConfig("shuYing_online_version", onlineVersion.online_version);
+
+ if (localVersion == onlineVersion.online_version) {
+ alert("本地版本为最新版");
+ return;
+ }
+
+ if (!confirm(`检测到最新版本为:${onlineVersion.online_version}, 本地版本为:${localVersion},是否更新`)) {
+ return;
+ }
+
+ await downloadCoreFiles(shuYing);
+ game.saveConfig("shuYing_local_version", onlineVersion.online_version);
+ alert("下载完成,重启生效");
+ }
+ catch (error) {
+ console.error(error);
+ alert("版本检测或下载失败,请检查网络或服务器配置。");
+ }
+ finally {
+ removeProgressNode(shuYing);
+ setBusy(shuYing, false);
+ }
+}
+
+async function repairMissingFiles(shuYing) {
+ if (typeof game.checkFile != "function") {
+ alert("此端暂不支持查缺补漏!");
+ setBusy(shuYing, false);
+ return;
+ }
+
+ try {
+ shuYing._updateLogs = [];
+ await addLog(shuYing, "查漏补缺开始");
+ ensureProgressNode(shuYing);
+ setProgress(shuYing, { title: "查漏补缺", status: "获取校验清单", current: 0, total: 1 });
+ const manifest = await getManifest();
+ const files = getManifestDownloadFiles(manifest);
+ await addLog(shuYing, `待检查文件数:${files.length}`);
+ await addLog(shuYing, `校验清单版本:${manifest.version || "未知"};清单文件数:${Object.keys(manifest.files || {}).length}`);
+
+ setProgress(shuYing, { title: "查漏补缺", status: "正在创建资源目录", current: 0, total: files.length });
+
+ await createFolders("extension/术樱包/", files, shuYing);
+
+ await downloadList(shuYing, files, manifest);
+ await removeManifestFiles(shuYing, manifest);
+
+ setProgress(shuYing, { title: "查漏补缺", status: "完成", current: files.length, total: files.length });
+ await addLog(shuYing, "查漏补缺完成");
+ alert("查漏补缺完毕!");
+ }
+ catch (error) {
+ console.error(error);
+ await addLog(shuYing, `查漏补缺失败:${getErrorMessage(error)}`);
+ alert("查漏补缺失败,请查看 extension/术樱包/log.txt");
+ }
+ finally {
+ removeProgressNode(shuYing);
+ setBusy(shuYing, false);
+ }
+}
+
+async function repairCoreFiles(shuYing) {
+ let shouldDownload = false;
+ let manifest;
+ let coreFiles;
+
+ try {
+ manifest = await getManifest();
+ coreFiles = getManifestCoreFiles(manifest);
+ }
+ catch (error) {
+ console.error(error);
+ alert("获取服务器校验清单失败,请检查网络或 manifest.json。");
+ setBusy(shuYing, false);
+ return;
+ }
+
+ if (typeof game.readFile != "function") {
+ shouldDownload = confirm("此端暂不支持本地资源检查。如有问题,点击确定重新下载核心文件。");
+ }
+ else {
+ try {
+ for (const file of coreFiles) {
+ await readFile(`extension/术樱包/${file}`);
+ }
+ shouldDownload = confirm("本地资源文件检测无误,若有问题点击确定重新下载主要js文件(包含关键js文件, 请勿关机、断网等操作,注意备份!)");
+ }
+ catch (error) {
+ console.error(error);
+ shouldDownload = confirm("本地资源不完整!点击确认重新获取!");
+ }
+ }
+
+ if (!shouldDownload) {
+ setBusy(shuYing, false);
+ return;
+ }
+
+ try {
+ await downloadCoreFiles(shuYing, manifest);
+ alert("下载完成,重启生效");
+ }
+ catch (error) {
+ console.error(error);
+ alert("核心文件下载或校验失败,请检查服务器文件与 manifest.json 是否一致。");
+ }
+ finally {
+ removeProgressNode(shuYing);
+ setBusy(shuYing, false);
+ }
+}
+
+export default {
+ getOnlineVersion,
+ checkVersion,
+ repairMissingFiles,
+ repairCoreFiles,
+};
diff --git a/voices.js b/voices.js
new file mode 100644
index 0000000..e40e621
--- /dev/null
+++ b/voices.js
@@ -0,0 +1,45 @@
+/*
+ * 语音台词表。
+ *
+ * 作用:
+ * 这里登记的是“音频文件 -> 台词文本”的翻译,不负责播放语音。
+ * 语音播放仍由各技能中的 audio: "ext:..." 或 game.playAudio(...) 控制。
+ *
+ * 写法:
+ * 左边填音频路径,不写 .mp3;右边填显示的台词。
+ * 例如技能 shuYing_Shizuishi 使用:
+ * audio: "ext:术樱包/yuanshen/八重神子:4"
+ * 那么底层会按技能名寻找:
+ * extension/术樱包/yuanshen/八重神子/shuYing_Shizuishi1.mp3
+ * extension/术樱包/yuanshen/八重神子/shuYing_Shizuishi2.mp3
+ * ...
+ * 对应台词键就是:
+ * "#术樱包/yuanshen/八重神子/shuYing_Shizuishi1": "台词"
+ */
+
+const shuYingVoice = {
+ // 原神 - 八重神子
+ // "#术樱包/yuanshen/八重神子/shuYing_Shizuishi1": "此处填写台词。",
+ // "#术樱包/yuanshen/八重神子/shuYing_Shashengying1": "此处填写台词。",
+ // "#术樱包/yuanshen/八重神子/shuYing_Tianhuxianzhen1": "此处填写台词。",
+
+ // 原神 - 可莉
+ // "#术樱包/yuanshen/可莉/shuYing_Pengpeng1": "此处填写台词。",
+ // "#术樱包/yuanshen/可莉/shuYing_Honghong1": "此处填写台词。",
+ // "#术樱包/yuanshen/可莉/shuYing_Lieyan1": "此处填写台词。",
+
+ // 崩铁 - 花火
+ // "#术樱包/bengtie/花火/jiaMian_Skill_shuYing1": "此处填写台词。",
+ // "#术樱包/bengtie/花火/huanYv_Skill_shuYing1": "此处填写台词。",
+};
+
+function getVoice(obj) {
+ const result = {};
+ for (const key in obj) {
+ const path = key.startsWith("#") ? key.slice(1) : key;
+ result[`#ext:${path}`] = obj[key];
+ }
+ return result;
+}
+
+export default getVoice(shuYingVoice);
diff --git a/yuanshen/extension.js b/yuanshen/extension.js
new file mode 100644
index 0000000..b96cb98
--- /dev/null
+++ b/yuanshen/extension.js
@@ -0,0 +1,39 @@
+window.func = (lib, game, ui, get, ai, _status, shuYing) =>
+{
+ if(!lib.config.extension_术樱包_yuanShenOff) return;
+
+ let yuanShen = new Object();
+
+ yuanShen.connect = true;
+ yuanShen.character = {};
+ yuanShen.game = {};
+ yuanShen.skill = {};
+ yuanShen.translate = {};
+ yuanShen.perfectPair = {};
+ yuanShen.characterTitle = {};
+ yuanShen.characterReplace = {};
+
+ let url = shuYing.url + "/yuanshen/"
+ let characterList = ["可莉", "八重神子"];
+
+ characterList.forEach(name =>
+ {
+ let characterUrl = `${url + name}`;
+
+ lib.init.js(characterUrl, 'character', function()
+ {
+ try
+ {
+ Object.assign(yuanShen.character, character.character);
+ Object.assign(yuanShen.skill, character.skill);
+ Object.assign(yuanShen.translate, character.translate);
+ }
+ catch(error)
+ {
+
+ }
+ });
+ });
+
+ shuYing.appendExtension("shuYing_yuanShen", "原神", yuanShen)
+}
\ No newline at end of file
diff --git a/yuanshen/八重神子/1.jpg b/yuanshen/八重神子/1.jpg
new file mode 100644
index 0000000..91f4b1e
Binary files /dev/null and b/yuanshen/八重神子/1.jpg differ
diff --git a/yuanshen/八重神子/character.js b/yuanshen/八重神子/character.js
new file mode 100644
index 0000000..fb0ab66
--- /dev/null
+++ b/yuanshen/八重神子/character.js
@@ -0,0 +1,316 @@
+character =
+{
+ character:
+ {
+ shuYing_Bachongshenzi:["female", "qun", 3,
+ ["shuYing_Shizuishi", "shuYing_Shashengying", "shuYing_Tianhuxianzhen"],
+ ['ext:术樱包/yuanshen/八重神子/1.jpg',
+ "die:true", "die:ext:术樱包/yuanshen/八重神子:true"]]
+ },
+
+ skill:
+ {
+ shuYing_Shizuishi:
+ {
+ audio:"ext:术樱包/yuanshen/八重神子:4",
+ trigger:{player:"useCardBefore"},
+ forced:true,
+ filter:(event, player) =>
+ {
+ if (event.player != player && event.player.hasSkill('shuYing_Shizuishi')) return false;
+ return event.player == player && event.card.isCard;
+ },
+ content:() =>
+ {
+ var num, sBool = false;
+ var i = 0;
+
+ while (true) {
+ i++;
+ num = player.shuYing_randomNum(game.players.length - 1, 0);
+ if(game.players[num] != player && !game.players[num].hasSkill('shuYing_Shizuishi'))
+ {
+ sBool = true;
+ break;
+ }
+ if (i == game.players.length) break;
+ }
+
+ if (sBool) {
+ trigger.player = game.players[num];
+ player.line(game.players[num]);
+ trigger.untrigger();
+ trigger.trigger('useCardBefore');
+ }
+
+ if(trigger.card.name=='sha'){ player.getStat().card.sha++; } //限制出杀次数
+ }
+ },
+
+ shuYing_Shashengying:{
+ audio:"ext:术樱包/yuanshen/八重神子:2",
+ trigger:{target:"useCardToTarget"},
+ usable:2,
+ check:(event, player, card) =>
+ {
+ if (event.card.name == 'shunshou' || event.card.name == 'guohe' || event.card.name == 'zhujinqiyuan' && event.player.isFriendOf(player) && player.countCards('j')) return false;
+ if (get.type(event.card) == 'equip' || event.targets.length == game.players.length) return false;
+ return true;
+ },
+ filter:(event, player) =>
+ {
+ var suit1 = event.card.suit;
+ return player.countCards('h', function (card)
+ {
+ return get.suit(card, player) == suit1;
+ });
+ },
+ content:() =>
+ {
+ var suit1 = trigger.card.suit;
+ 'step 0';
+ player.chooseControl('全部', '一张', '一张并选择一个目标').set('prompt', '请选择并弃置' + get.translation(suit1) + '花色').set('ai', function () {
+ var suit1 = trigger.card.suit;
+ var num = player.countCards('h', function (card) {
+ return get.suit(card, player) == suit1;
+ });
+ var friend;
+
+ for (var i = 0; i < game.players.length; i++) {
+ if (game.players[i] == player) continue;
+ if (game.players[i].isFriendOf(player)) {
+ break;
+ friend = true;
+ }
+ if (i == game.players.length) friend = false;
+ }
+
+ var trigger1 = _status.event.getTrigger();
+ var card1 = get.effect(player, trigger1.card, trigger1.player, _status.event.player);
+
+ if (get.type(trigger.card) == 'delay' && num == 1) return '全部';
+ if (get.type(trigger.card) == 'delay' && num > 1) return '一张';
+ if (card1 <= 1 && num == 1) return '全部';
+ if (card1 >= 2 && num == 1 && friend) return '一张并选择一个目标';
+ if (card1 <= 1 && num > 1) return '一张';
+ if (card1 >= 2 && num > 1 && friend) return '一张并选择一个目标';
+ if (card1 >= 2 && num > 1 && friend == false) return '一张';
+ });
+ 'step 1';
+ if (result.control == '全部') {
+ player.discard(player.getCards('h', { suit: suit1 }));
+ trigger.getParent().excluded.add(player);
+ player.storage.cardName = trigger.card.name;
+ player.storage.skillBool = true;
+ } else if (result.control == '一张') {
+ player.chooseToDiscard('h', 1, function (card) {
+ return get.suit(card) == suit1;
+ }, true);
+ trigger.getParent().excluded.add(player);
+ } else {
+ player.chooseToDiscard('h', 1, function (card) {
+ return get.suit(card) == suit1;
+ }, true);
+ player.storage.skillBool2 = true;
+ }
+ },
+ group:['shuYing_Shashengying_Buff','shuYing_Shashengying_Buff1'],
+ subSkill:{
+ Buff:{
+ auido:false,
+ trigger:{global:'useCardAfter'},
+ forced:true,
+ sub:true,
+ filter:function(event,player){
+ return (player.storage.skillBool&&event.card.name==player.storage.cardName&&event.player!=player&&get.itemtype(event.cards)=='cards'&&get.position(event.cards[0],true)=='o');
+ },
+ content:function(){
+ if(player.countCards('h')==0) player.draw(player.hp);
+ player.gain(trigger.cards,'gain2');
+ player.storage.skillBool=false;
+ player.storage.cardName='';
+ }
+ },
+ Buff1:{
+ trigger:{target:"useCardToTargeted",},
+ forced:true,
+ popup:false,
+ auido:false,
+ sub:true,
+ filter:function(event,player){
+ return player.storage.skillBool2;
+ },
+ content:function(){
+ 'step 0'
+ var prompt2='为'+get.translation(trigger.card)+'额外指定一个目标';
+ player.chooseTarget([1,player.storage.fumian_red],get.prompt(event.name),function(card,player,target){
+ var player=_status.event.player;
+ if(_status.event.targets.contains(target)) return false;
+ return lib.filter.targetEnabled2(_status.event.card,player,target);
+ }).set('prompt2',prompt2).set('ai',function(target){
+ var trigger=_status.event.getTrigger();
+ var player=_status.event.player;
+ return get.effect(target,trigger.card,player,player);
+ }).set('targets',trigger.targets).set('card',trigger.card);
+ 'step 1'
+ if(result.bool){
+ if(!_status.connectMode&&!event.isMine()) game.delayx();
+ event.targets=result.targets;
+ }
+ 'step 2'
+ if(event.targets){
+ player.logSkill(event.name,event.targets);
+ trigger.targets.addArray(event.targets);
+ }
+ player.storage.skillBool2=false;
+ },
+ },
+ },
+ },
+ shuYing_Tianhuxianzhen:{
+ audio:"ext:术樱包/yuanshen/八重神子:2",
+ marktext:"狐",
+ mark:true,
+ intro:{
+ content:function (storage,player,skill){
+ var num=player.countMark('shuYing_Tianhuxianzhen');
+ return '本回合手牌上限+'+num;
+ },
+ },
+ locked:true,
+ enable:"phaseUse",
+ usable:2,
+ content:function(){
+ 'step 0'
+ player.chooseControl('黑桃','梅花','方块','红桃').set('prompt','选择一种花色并展示牌堆底的一张牌,如果花色相同则你获得之并可以重复执行').set('ai',function(){
+ var randomNum = player.shuYing_randomNum(100,0);
+ if(randomNum<=35){
+ var card=get.bottomCards()[0];
+ if(get.suit(card)=='spade'){
+ return '黑桃';
+ }else if(get.suit(card)=='club'){
+ return '梅花';
+ }else if(get.suit(card)=='diamond'){
+ return '方块';
+ }else {
+ return '红桃';
+ }
+ } else {
+ list=['黑桃','梅花','方块','红桃'].randomGet();
+ return list;
+ }
+ });
+ 'step 1'
+ if(result.control=='黑桃'){
+ var card=get.bottomCards()[0];
+ player.showCards(card);
+ if(get.suit(card)=='spade'){
+ if(player.countCards('h')==0) player.draw();
+ player.gain(card,'gain2');
+ event.goto(0);
+ } else {
+ game.log(player,'将',card,'放置牌堆顶');
+ player.addMark('shuYing_Tianhuxianzhen',1);
+ ui.cardPile.insertBefore(card,ui.cardPile.firstChild);
+ }
+ } else if(result.control=='梅花'){
+ var card=get.bottomCards()[0];
+ player.showCards(card);
+ if(get.suit(card)=='club'){
+ if(player.countCards('h')==0) player.draw();
+ player.gain(card,'gain2');
+ event.goto(0);
+ } else {
+ game.log(player,'将',card,'放置牌堆顶');
+ player.addMark('shuYing_Tianhuxianzhen',1);
+ ui.cardPile.insertBefore(card,ui.cardPile.firstChild);
+ }
+ } else if(result.control=='方块'){
+ var card=get.bottomCards()[0];
+ player.showCards(card);
+ if(get.suit(card)=='diamond'){
+ if(player.countCards('h')==0) player.draw();
+ player.gain(card,'gain2');
+ event.goto(0);
+ } else {
+ game.log(player,'将',card,'放置牌堆顶');
+ player.addMark('shuYing_Tianhuxianzhen',1);
+ ui.cardPile.insertBefore(card,ui.cardPile.firstChild);
+ }
+ } else {
+ var card=get.bottomCards()[0];
+ player.showCards(card);
+ if(get.suit(card)=='heart'){
+ if(player.countCards('h')==0) player.draw();
+ player.gain(card,'gain2');
+ event.goto(0);
+ } else {
+ game.log(player,'将',card,'放置牌堆顶');
+ player.addMark('shuYing_Tianhuxianzhen',1);
+ ui.cardPile.insertBefore(card,ui.cardPile.firstChild);
+ }
+ }
+ game.updateRoundNumber();
+ },
+ mod:{
+ maxHandcardBase:function(player,num){
+ var num1=player.countMark('shuYing_Tianhuxianzhen');
+ return num+num1;
+ },
+ },
+ ai:{
+ order:10,
+ threaten:0.5,
+ result:{
+ player:function (player,target){
+ return 1;
+ },
+ },
+ },
+ group:'shuYing_Tianhuxianzhen_Buff',
+ subSkill:{
+ Buff:{
+ trigger:{player:"phaseJieshuBegin"},
+ forced:true,
+ priority:100,
+ silent:true,
+ sub:true,
+ firstDo:true,
+ frequent:true,
+ popup:false,
+ audio:false,
+ filter:function(event,player){
+ return player.countMark('shuYing_Tianhuxianzhen');
+ },
+ content:function(){
+ var num=player.countMark('shuYing_Tianhuxianzhen');
+ player.removeMark('shuYing_Tianhuxianzhen',num);
+ },
+ },
+ },
+ },
+ },
+
+ translate:
+ {
+ shuYing_Bachongshenzi:"八重神子",
+
+ shuYing_Shizuishi:"食罪式",
+ shuYing_Shizuishi_info:"锁定技,当你使用非转化牌指定目标后,将此牌使用者改为场上不拥有此技能的随机其他角色。",
+ shuYing_Shashengying:"杀生樱",
+ shuYing_Shashengying_Buff:"杀生樱",
+ shuYing_Shashengying_Buff1:"杀生樱",
+ shuYing_Shashengying_info:"每回合限两次。当你成为使用牌的目标后,若该牌花色与你手牌中的花色有相同,则你可以选择:1:弃置全部该花色的手牌使该牌对你无效,并在该牌结算后获得之,若你因此弃置全部手牌则你将手牌补充至当前体力值,2:弃置一张该花色的手牌,使该牌对你无效,3:弃置一张该花色的手牌并指定一名其他角色,令其也成为该牌的目标。",
+ shuYing_Tianhuxianzhen:"天狐显真",
+ shuYing_Tianhuxianzhen_info:"出牌阶段限两次,你声明一种花色并亮出牌堆底的一张牌,如果花色相同则你获得之,若在此之前你没有手牌则你从牌堆顶摸一张牌并重复此技能,如果花色不同则将该牌置于牌堆顶然后本回合你手牌上限+1。",
+ }
+}
+
+
+
+
+
+
+
+
+
diff --git a/yuanshen/八重神子/shuYing_Shashengying1.mp3 b/yuanshen/八重神子/shuYing_Shashengying1.mp3
new file mode 100644
index 0000000..cd71613
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shashengying1.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Shashengying2.mp3 b/yuanshen/八重神子/shuYing_Shashengying2.mp3
new file mode 100644
index 0000000..bbbe701
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shashengying2.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Shizuishi1.mp3 b/yuanshen/八重神子/shuYing_Shizuishi1.mp3
new file mode 100644
index 0000000..7466493
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shizuishi1.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Shizuishi2.mp3 b/yuanshen/八重神子/shuYing_Shizuishi2.mp3
new file mode 100644
index 0000000..cd70bc0
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shizuishi2.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Shizuishi3.mp3 b/yuanshen/八重神子/shuYing_Shizuishi3.mp3
new file mode 100644
index 0000000..054b163
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shizuishi3.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Shizuishi4.mp3 b/yuanshen/八重神子/shuYing_Shizuishi4.mp3
new file mode 100644
index 0000000..85b96ae
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Shizuishi4.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Tianhuxianzhen1.mp3 b/yuanshen/八重神子/shuYing_Tianhuxianzhen1.mp3
new file mode 100644
index 0000000..c708075
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Tianhuxianzhen1.mp3 differ
diff --git a/yuanshen/八重神子/shuYing_Tianhuxianzhen2.mp3 b/yuanshen/八重神子/shuYing_Tianhuxianzhen2.mp3
new file mode 100644
index 0000000..2323f4d
Binary files /dev/null and b/yuanshen/八重神子/shuYing_Tianhuxianzhen2.mp3 differ
diff --git a/yuanshen/可莉/1.jpg b/yuanshen/可莉/1.jpg
new file mode 100644
index 0000000..afd7843
Binary files /dev/null and b/yuanshen/可莉/1.jpg differ
diff --git a/yuanshen/可莉/character.js b/yuanshen/可莉/character.js
new file mode 100644
index 0000000..867e9e7
--- /dev/null
+++ b/yuanshen/可莉/character.js
@@ -0,0 +1,299 @@
+character =
+{
+ character:
+ {
+ shuYing_Keli:["female", "qun", 4,
+ ["shuYing_Pengpeng", "shuYing_Honghong", "shuYing_Lieyan"],
+ ['ext:术樱包/yuanshen/可莉/1.jpg',
+ "die:true", "die:ext:术樱包/yuanshen/可莉:true"]],
+ },
+
+ skill:
+ {
+ shuYing_Pengpeng:{
+ mark:true,
+ marktext:'砰',
+ intro:
+ {
+ content:(storage, player, skill) =>
+ {
+ var num = player.storage.shuYing_Pengpeng, num1;
+ if (num >= 3 && parseInt(num % 3) == 0)
+ {
+ return '下张伤害类牌就可以多丢一个炸弹';
+ } else {
+ num1 = num < 3 ? 3 - num : 3 - parseInt(num % 3);
+ return '距离可莉的炸弹还有' + num1 + '张哦';
+ }
+ },
+ },
+ audio:"ext:术樱包/yuanshen/可莉:4",
+ trigger:{player:"useCardToTargeted"},
+ forced:true,
+ firstDo:true,
+ popup:false,
+ init:function(player)
+ {
+ player.storage.shuYing_Pengpeng=0;
+ player.storage.shuYing_Pengpeng_Damage=false;
+ player.storage.shuYing_Pengpeng_Bool=false;
+ player.storage.shuYing_Keli_Bool=false;
+ },
+ filter:function(event,player)
+ {
+ if(!get.tag(event.card,'damage')) return false;
+ if(!event.isFirstTarget) return false;
+ var info = get.info(event.card);
+ if(info.allowMultiple==false) return false;
+ if(event.targets&&!info.multitarget)
+ {
+ if(game.hasPlayer(function(current){return lib.filter.targetEnabled2(event.card,player,current)}))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ },
+ content:function()
+ {
+ var num = player.storage.shuYing_Pengpeng;
+ var mp3 = num+1;
+ if(mp3 >= 3 && parseInt(mp3 % 3) == 0){
+ var list=[1,2,3].randomGet();
+ game.playAudio('..','extension/术樱包/yuanshen/可莉','shuYing_Pengpeng' + list);
+ }
+ if(num>=3&&parseInt(num%3)==0){
+ player.storage.shuYing_Pengpeng_Bool=true;
+ player.storage.shuYing_Keli_Bool=true;
+ player.storage.shuYing_Pengpeng++;
+ } else {
+ player.storage.shuYing_Pengpeng_Damage=false;
+ player.storage.shuYing_Pengpeng_Bool=false;
+ player.storage.shuYing_Keli_Bool=false;
+ player.storage.shuYing_Pengpeng++;
+ }
+ },
+ group:['shuYing_Pengpeng_Damage','shuYing_Pengpeng_Target'],
+ subSkill:{
+ Damage:{
+ trigger:{source:"damageBefore"},
+ forced:true,
+ firstDo:true,
+ popup:false,
+ onremove:true,
+ sub:true,
+ filter:function(event,player){
+ return get.tag(event.card,'damage')&&player.storage.shuYing_Keli_Bool;
+ },
+ content:function(){
+ trigger.nature='fire';
+ if(player.storage.shuYing_Pengpeng_Damage) {
+ trigger.num++;
+ }
+ }
+ },
+ Target:{
+ trigger:{player:"useCardToTargeted"},
+ forced:true,
+ popup:false,
+ sub:true,
+ filter:function(event,player){
+ if(!get.tag(event.card,'damage')||event.targets.length!=1) return false;
+ if(!event.isFirstTarget) return false;
+ var info=get.info(event.card);
+ if(info.allowMultiple==false) return false;
+ if(event.targets&&!info.multitarget){
+ if(game.hasPlayer(function(current){
+ return lib.filter.targetEnabled2(event.card,player,current);
+ })){
+ return player.storage.shuYing_Pengpeng_Bool;
+ }
+ }
+ return false;
+ },
+ content:function(){
+ 'step 0'
+ var prompt2='为'+get.translation(trigger.card)+'额外指定一个目标';
+ player.chooseTarget([1,player.storage.fumian_red],get.prompt(event.name),function(card,player,target){
+ var player=_status.event.player;
+ if(_status.event.targets.contains(target)) return false;
+ return lib.filter.targetEnabled2(_status.event.card,player,target);
+ }).set('prompt2',prompt2).set('ai',function(target){
+ var trigger=_status.event.getTrigger();
+ var player=_status.event.player;
+ return get.effect(target,trigger.card,player,player);
+ }).set('targets',trigger.targets).set('card',trigger.card);
+ 'step 1'
+ if(result.bool){
+ if(!_status.connectMode&&!event.isMine()) game.delayx();
+ event.targets=result.targets;
+ } else {
+ event.finish();
+ player.storage.shuYing_Pengpeng_Bool=false;
+ player.storage.shuYing_Pengpeng_Damage=true;
+ }
+ 'step 2'
+ if(event.targets){
+ player.logSkill(event.name,event.targets);
+ trigger.targets.addArray(event.targets);
+ }
+ player.storage.shuYing_Pengpeng_Bool=false;
+ },
+ },
+ },
+ },
+ shuYing_Honghong:{
+ audio:"ext:术樱包/yuanshen/可莉:3",
+ enable:'phaseUse',
+ usable:1,
+ init:function(player){
+ player.storage.shuYing_Honghong_Bool=false;
+ },
+ check:function(){
+ return true;
+ },
+ filter:function(event,player){
+ var hs=player.getCards('h',function(card){
+ return get.tag(card,'damage');
+ });
+ if(!hs.length) return false;
+ return true;
+ },
+ content:function(){
+ 'step 0'
+ event.card=get.cardPile(function(card){
+ return get.tag(card,'damage');
+ });
+ if(event.card) {
+ player.showCards(event.card);
+ player.chooseToDiscard('h',1,function(card){
+ return get.tag(card,'damage');
+ },false).set('ai',function(card){
+ return 8-ai.get.value(card);
+ });
+ } else {
+ game.log(player,'将',event.card,'置入弃牌堆');
+ game.cardsDiscard(event.card);
+ event.finish();
+ }
+ 'step 1'
+ if(result.bool){
+ player.storage.shuYing_Honghong_Bool=true;
+ player.chooseUseTarget(event.card,false);
+ }
+ 'step 2'
+ if(!result.targets){
+ game.log(player,'将',event.card,'置入弃牌堆');
+ game.cardsDiscard(event.card);
+ } else {
+ event.finish();
+ }
+ },
+ contentAfter:function(){
+ player.storage.shuYing_Honghong_Bool=false;
+ },
+ group:['shuYing_Honghong_Damage','shuYing_Honghong_Jieshu'],
+ subSkill:{
+ Damage:{
+ trigger:{source:"damageBefore"},
+ forced:true,
+ firstDo:true,
+ popup:false,
+ onremove:true,
+ sub:true,
+ filter:function(event,player){
+ return get.tag(event.card,'damage')&&player.storage.shuYing_Honghong_Bool;
+ },
+ content:function(){
+ trigger.nature='fire';
+ }
+ },
+ Jieshu:{
+ trigger:{player:"phaseJieshu"},
+ frequent:true,
+ filter:function(event,player){
+ var sourceDamage=player.getHistory('sourceDamage').length;
+ return sourceDamage==0&&!player.countCards('h',function(card){
+ return get.tag(card,'damage');
+ });
+ },
+ content:function(){
+ var card=get.cardPile2(function(card){
+ return get.tag(card,'damage');
+ });
+ if(card) player.gain(card,'gain2');
+ game.updateRoundNumber();
+ },
+ },
+ },
+ ai:{
+ order:6,
+ result:{
+ player:1,
+ },
+ threaten:0.5,
+ },
+ },
+ shuYing_Lieyan:
+ {
+ audio:"ext:术樱包/yuanshen/可莉:1",
+ trigger:{player:'damageBegin4'},
+ filter:function(event){
+ return event.nature=='fire';
+ },
+ forced:true,
+ content:function(){
+ trigger.cancel();
+ },
+ ai:{
+ nofire:true,
+ effect:{
+ target:function(card,player,target,current){
+ if(get.tag(card,'fireDamage')) return 'zerotarget';
+ }
+ }
+ },
+ group:'shuYing_Lieyan_Damage',
+ subSkill:{
+ Damage:{
+ audio:"ext:术樱包/yuanshen/可莉:3",
+ trigger:{source:"damageBegin"},
+ forced:true,
+ sub:true,
+ filter:function(event,player){
+ return event.nature=='fire';
+ },
+ content:function(){
+ if(player.countCards('h')!=player.hp){
+ player.draw(player.countCards('h')==0?2:1);
+ } else {
+ trigger.num++;
+ }
+ },
+ ai:{
+ effect:{
+ player:function(card,player,target){
+ if(card.name=='sha'||card.nature=='fire'||card.name=='zhuque') return [1,3];
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ translate:
+ {
+ shuYing_Keli:"可莉",
+
+ shuYing_Pengpeng:"砰砰",
+ shuYing_Pengpeng_info:"锁定技: 你每使用三次伤害类型牌,下张伤害类型牌的伤害为火元素。若该牌目标唯一,则你可以选择一个额外的合法目标。若取消则该牌伤害+1",
+ shuYing_Honghong:"轰轰",
+ shuYing_Honghong_info:"出牌阶段限一次,你展示牌堆中一张可以造成伤害的牌,然后你可以弃置一张可以造成伤害的手牌并使用该牌(这张牌造成的伤害为火元素)否则将此牌置入弃牌堆。结束阶段,若你此回合没有造成伤害且手牌没有伤害类牌则你获得一张伤害类牌",
+ shuYing_Lieyan:"烈焰",
+ shuYing_Lieyan_info:"锁定技,你受到火元素伤害时,免疫此伤害;当你造成火元素伤害时, 若你手牌不等于当前体力, 则你摸一张牌, 若你没有手牌则额外摸一张手牌;若相等则该伤害+1",
+
+ }
+};
+
diff --git a/yuanshen/可莉/shuYing_Honghong1.mp3 b/yuanshen/可莉/shuYing_Honghong1.mp3
new file mode 100644
index 0000000..152f576
Binary files /dev/null and b/yuanshen/可莉/shuYing_Honghong1.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Honghong2.mp3 b/yuanshen/可莉/shuYing_Honghong2.mp3
new file mode 100644
index 0000000..a400330
Binary files /dev/null and b/yuanshen/可莉/shuYing_Honghong2.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Honghong3.mp3 b/yuanshen/可莉/shuYing_Honghong3.mp3
new file mode 100644
index 0000000..66d173e
Binary files /dev/null and b/yuanshen/可莉/shuYing_Honghong3.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Lieyan1.mp3 b/yuanshen/可莉/shuYing_Lieyan1.mp3
new file mode 100644
index 0000000..38a8238
Binary files /dev/null and b/yuanshen/可莉/shuYing_Lieyan1.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Lieyan_Damage1.mp3 b/yuanshen/可莉/shuYing_Lieyan_Damage1.mp3
new file mode 100644
index 0000000..cafed33
Binary files /dev/null and b/yuanshen/可莉/shuYing_Lieyan_Damage1.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Lieyan_Damage2.mp3 b/yuanshen/可莉/shuYing_Lieyan_Damage2.mp3
new file mode 100644
index 0000000..ea62e43
Binary files /dev/null and b/yuanshen/可莉/shuYing_Lieyan_Damage2.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Lieyan_Damage3.mp3 b/yuanshen/可莉/shuYing_Lieyan_Damage3.mp3
new file mode 100644
index 0000000..06a038f
Binary files /dev/null and b/yuanshen/可莉/shuYing_Lieyan_Damage3.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Pengpeng1.mp3 b/yuanshen/可莉/shuYing_Pengpeng1.mp3
new file mode 100644
index 0000000..25cc7d6
Binary files /dev/null and b/yuanshen/可莉/shuYing_Pengpeng1.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Pengpeng2.mp3 b/yuanshen/可莉/shuYing_Pengpeng2.mp3
new file mode 100644
index 0000000..c46467d
Binary files /dev/null and b/yuanshen/可莉/shuYing_Pengpeng2.mp3 differ
diff --git a/yuanshen/可莉/shuYing_Pengpeng3.mp3 b/yuanshen/可莉/shuYing_Pengpeng3.mp3
new file mode 100644
index 0000000..9b7c829
Binary files /dev/null and b/yuanshen/可莉/shuYing_Pengpeng3.mp3 differ