diff --git a/extension.js b/extension.js index 179af8d..f1b5cd4 100644 --- a/extension.js +++ b/extension.js @@ -39,7 +39,7 @@ shuYing.loadVoices = async () => { return shuYing.voices; }; -// 公共函数需要在菜单创建前初始化,扩展菜单可在选将预备阶段被点击,此时还没有 player。 +// 公共函数需要在菜单创建前初始化,扩展菜单可在选将预备阶段被点击 shuYing.initFunction = () => { if (shuYing.functionInitialized) return; if (typeof func == "function") { @@ -89,6 +89,49 @@ shuYing.appendExtension = function (name, translate, obj) { lib.translate[name + '_character_config'] = translate; }; +// 将玩法模块的数据合并进同一个武将包,重复 ID 保留主包内容,避免生成额外的武将包菜单项。 +shuYing.mergeCharacterPack = function (target, source, sourceName = "附加模块") { + if (!target || !source) return target; + const sections = [ + "character", "card", "skill", "translate", "characterSort", "characterFilter", + "characterTitle", "dynamicTranslate", "characterIntro", "perfectPair", "pinyins", + "boss", "game", "characterReplace", + ]; + for (const section of sections) { + const data = source[section]; + if (!data || typeof data != "object") continue; + const targetData = target[section] || (target[section] = {}); + for (const [id, value] of Object.entries(data)) { + if (Object.prototype.hasOwnProperty.call(targetData, id)) { + console.warn(`术樱包:跳过${sourceName}中重复的${section} ID“${id}”`); + continue; + } + targetData[id] = value; + } + } + return target; +}; + +// 清理旧版本曾注册的独立武将包,防止历史配置让已合并模块继续出现在武将包列表。 +shuYing.removeLegacyCharacterPack = function (name) { + const removeFrom = list => { + if (!Array.isArray(list)) return false; + let changed = false; + for (let index = list.indexOf(name); index >= 0; index = list.indexOf(name)) { + list.splice(index, 1); + changed = true; + } + return changed; + }; + removeFrom(lib.config.all.characters); + if (removeFrom(lib.config.characters)) { + game.saveConfig("characters", lib.config.characters); + } + removeFrom(lib.connectCharacterPack); + delete lib.characterPack[name]; + delete lib.translate[`${name}_character_config`]; +}; + shuYing.getVersion = (callback) => { return updater.getOnlineVersion() .then(version => { @@ -203,20 +246,24 @@ shuYing.initConfig = () => { }; -//术樱包菜单配置由 ./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); - } + shuYing.removeLegacyCharacterPack("shuYing_pve_tianshu"); + let shuying_boss = await shuYing.loadModule("./pve/boss/index.js", "活动BOSS", true); const initTianShu = await shuYing.loadModule("./tianshu/extension.js", "天书乱斗", true); if (typeof initTianShu == "function") { - initTianShu(lib, game, ui, get, ai, _status, shuYing); + const tianshu = initTianShu(lib, game, ui, get, ai, _status, shuYing); + if (tianshu) { + if (shuying_boss) shuYing.mergeCharacterPack(shuying_boss, tianshu, "天书乱斗"); + else shuying_boss = tianshu; + } + } + + if (shuying_boss) { + shuYing.appendExtension("shuYing_pve_boss", "活动BOSS", shuying_boss); } // shuYingList = // [ diff --git a/pve/audio/dead/KuaFu_Dead.mp3 b/pve/audio/dead/KuaFu_Dead.mp3 deleted file mode 100644 index ec3f11d..0000000 Binary files a/pve/audio/dead/KuaFu_Dead.mp3 and /dev/null differ diff --git a/pve/audio/dead/驱鬼逐邪/guiwang.mp3 b/pve/audio/dead/驱鬼逐邪/guiwang.mp3 new file mode 100644 index 0000000..a226ae8 Binary files /dev/null and b/pve/audio/dead/驱鬼逐邪/guiwang.mp3 differ diff --git a/pve/audio/dead/驱鬼逐邪/heibaiwuchang.mp3 b/pve/audio/dead/驱鬼逐邪/heibaiwuchang.mp3 new file mode 100644 index 0000000..5389e3c Binary files /dev/null and b/pve/audio/dead/驱鬼逐邪/heibaiwuchang.mp3 differ diff --git a/pve/audio/dead/驱鬼逐邪/niutoumamian.mp3 b/pve/audio/dead/驱鬼逐邪/niutoumamian.mp3 new file mode 100644 index 0000000..1175072 Binary files /dev/null and b/pve/audio/dead/驱鬼逐邪/niutoumamian.mp3 differ diff --git a/pve/audio/dead/驱鬼逐邪/riyeyoushen.mp3 b/pve/audio/dead/驱鬼逐邪/riyeyoushen.mp3 new file mode 100644 index 0000000..1780b35 Binary files /dev/null and b/pve/audio/dead/驱鬼逐邪/riyeyoushen.mp3 differ diff --git a/pve/audio/skill/KuaFu_ZhuRi_01.mp3 b/pve/audio/skill/KuaFu_ZhuRi_01.mp3 deleted file mode 100644 index 65e9518..0000000 Binary files a/pve/audio/skill/KuaFu_ZhuRi_01.mp3 and /dev/null differ diff --git a/pve/audio/skill/驱鬼逐邪/aotang_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/aotang_shuying1.mp3 new file mode 100644 index 0000000..edceba5 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/aotang_shuying1.mp3 differ diff --git a/pve/audio/skill/spell8016_1.mp3 b/pve/audio/skill/驱鬼逐邪/bingyi_shuying1.mp3 similarity index 100% rename from pve/audio/skill/spell8016_1.mp3 rename to pve/audio/skill/驱鬼逐邪/bingyi_shuying1.mp3 diff --git a/pve/audio/skill/驱鬼逐邪/danshi_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/danshi_shuying1.mp3 new file mode 100644 index 0000000..b76e589 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/danshi_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/dianwei_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/dianwei_shuying1.mp3 new file mode 100644 index 0000000..8b1861b Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/dianwei_shuying1.mp3 differ diff --git a/pve/audio/skill/spell8014_1.mp3 b/pve/audio/skill/驱鬼逐邪/eli_shuying1.mp3 similarity index 100% rename from pve/audio/skill/spell8014_1.mp3 rename to pve/audio/skill/驱鬼逐邪/eli_shuying1.mp3 diff --git a/pve/audio/skill/驱鬼逐邪/guimei_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/guimei_shuying1.mp3 new file mode 100644 index 0000000..adc6548 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/guimei_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/guizhao_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/guizhao_shuying1.mp3 new file mode 100644 index 0000000..cf8049b Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/guizhao_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/jizhou_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/jizhou_shuying1.mp3 new file mode 100644 index 0000000..7e7b5f6 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/jizhou_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/manji_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/manji_shuying1.mp3 new file mode 100644 index 0000000..2743ece Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/manji_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/shiyu_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/shiyu_shuying1.mp3 new file mode 100644 index 0000000..02c0290 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/shiyu_shuying1.mp3 differ diff --git a/pve/audio/skill/spell8017_1.mp3 b/pve/audio/skill/驱鬼逐邪/suoxue_shuying1.mp3 similarity index 100% rename from pve/audio/skill/spell8017_1.mp3 rename to pve/audio/skill/驱鬼逐邪/suoxue_shuying1.mp3 diff --git a/pve/audio/skill/驱鬼逐邪/xiaoshou_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/xiaoshou_shuying1.mp3 new file mode 100644 index 0000000..e7141f4 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/xiaoshou_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/xingpan_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/xingpan_shuying1.mp3 new file mode 100644 index 0000000..ab8f2b1 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/xingpan_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/xuanpan_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/xuanpan_shuying1.mp3 new file mode 100644 index 0000000..62306ff Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/xuanpan_shuying1.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/xuanpan_shuying2.mp3 b/pve/audio/skill/驱鬼逐邪/xuanpan_shuying2.mp3 new file mode 100644 index 0000000..6c8668c Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/xuanpan_shuying2.mp3 differ diff --git a/pve/audio/skill/驱鬼逐邪/yunju_shuying1.mp3 b/pve/audio/skill/驱鬼逐邪/yunju_shuying1.mp3 new file mode 100644 index 0000000..022d7b7 Binary files /dev/null and b/pve/audio/skill/驱鬼逐邪/yunju_shuying1.mp3 differ diff --git a/pve/boss/card.js b/pve/boss/card.js new file mode 100644 index 0000000..2ae2a94 --- /dev/null +++ b/pve/boss/card.js @@ -0,0 +1,6 @@ +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const cards = mergeBossSection("卡牌", bossSets, "card"); + +export default cards; diff --git a/pve/boss/character.js b/pve/boss/character.js index b09aa6d..58add16 100644 --- a/pve/boss/character.js +++ b/pve/boss/character.js @@ -1,15 +1,7 @@ -import shiershengxiao from "./sets/shiershengxiao/character.js"; -import zhuoguiquxie from "./sets/zhuoguiquxie/character.js"; -import qingqingzijin from "./sets/qingqingzijin/character.js"; -import tianshu from "./sets/tianshu/character.js"; -import mergeBossModules from "./merge.js"; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; // 汇总所有主题武将,PVE 玩法只需引用唯一的 BOSS ID。 -const characters = mergeBossModules("武将", [ - ["十二生肖", shiershengxiao], - ["捉鬼驱邪", zhuoguiquxie], - ["青青子衿", qingqingzijin], - ["天书乱斗", tianshu], -]); +const characters = mergeBossSection("武将", bossSets, "character"); export default characters; diff --git a/pve/boss/characterFilter.js b/pve/boss/characterFilter.js index ef70aca..04f3d8a 100644 --- a/pve/boss/characterFilter.js +++ b/pve/boss/characterFilter.js @@ -1,4 +1,6 @@ -const characterFilters = { -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const characterFilters = mergeBossSection("武将过滤", bossSets, "characterFilter"); export default characterFilters; diff --git a/pve/boss/characterTitle.js b/pve/boss/characterTitle.js index b901a42..ad825f2 100644 --- a/pve/boss/characterTitle.js +++ b/pve/boss/characterTitle.js @@ -1,4 +1,6 @@ -const characterTitles = { -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const characterTitles = mergeBossSection("武将称号", bossSets, "characterTitle"); export default characterTitles; diff --git a/pve/boss/dynamicTranslate.js b/pve/boss/dynamicTranslate.js index 2912e8a..681c7dd 100644 --- a/pve/boss/dynamicTranslate.js +++ b/pve/boss/dynamicTranslate.js @@ -1,13 +1,7 @@ -import zhuoguiquxie from "./sets/zhuoguiquxie/dynamicTranslate.js"; -import qingqingzijin from "./sets/qingqingzijin/dynamicTranslate.js"; -import tianshu from "./sets/tianshu/dynamicTranslate.js"; -import mergeBossModules from "./merge.js"; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; -// 汇总存在动态描述的主题技能;无动态描述的主题不需要空模块占位。 -const dynamicTranslates = mergeBossModules("动态翻译", [ - ["捉鬼驱邪", zhuoguiquxie], - ["青青子衿", qingqingzijin], - ["天书乱斗", tianshu], -]); +// 汇总所有主题动态描述;空主题模块会被安全忽略。 +const dynamicTranslates = mergeBossSection("动态翻译", bossSets, "dynamicTranslate"); export default dynamicTranslates; diff --git a/pve/boss/index.js b/pve/boss/index.js index 2cefd62..6dff01b 100644 --- a/pve/boss/index.js +++ b/pve/boss/index.js @@ -1,4 +1,6 @@ import characters from "./character.js"; +import cards from "./card.js"; +import pinyins from "./pinyin.js"; import skills from "./skill.js"; import translates from "./translate.js"; import characterFilters from "./characterFilter.js"; @@ -20,9 +22,11 @@ const shuying_boss = { characterTitle: { ...characterTitles }, dynamicTranslate: { ...dynamicTranslates }, characterIntro: { ...characterIntros }, + card: { ...cards }, skill: { ...skills }, perfectPair: { ...perfectPairs }, translate: { ...translates, ...voices, ...characterSortTranslate }, + pinyins: { ...pinyins }, }; export default shuying_boss; diff --git a/pve/boss/intro.js b/pve/boss/intro.js index 15a8a83..8f3ac0a 100644 --- a/pve/boss/intro.js +++ b/pve/boss/intro.js @@ -1,4 +1,6 @@ -const characterIntros = { -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const characterIntros = mergeBossSection("武将介绍", bossSets, "characterIntro"); export default characterIntros; diff --git a/pve/boss/merge.js b/pve/boss/merge.js index 98a5abb..0704c7e 100644 --- a/pve/boss/merge.js +++ b/pve/boss/merge.js @@ -13,4 +13,10 @@ const mergeBossModules = (dataType, modules) => { return result; }; +// 从标准主题入口中提取指定字段,再复用统一的重复 ID 处理规则。 +const mergeBossSection = (dataType, modules, section) => { + return mergeBossModules(dataType, modules.map(([moduleName, data]) => [moduleName, data?.[section]])); +}; + +export { mergeBossSection }; export default mergeBossModules; diff --git a/pve/boss/perfectPairs.js b/pve/boss/perfectPairs.js index 47a0a7b..6be3729 100644 --- a/pve/boss/perfectPairs.js +++ b/pve/boss/perfectPairs.js @@ -1,4 +1,6 @@ -const perfectPairs = { -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const perfectPairs = mergeBossSection("珠联璧合", bossSets, "perfectPair"); export default perfectPairs; diff --git a/pve/boss/pinyin.js b/pve/boss/pinyin.js new file mode 100644 index 0000000..1b49c09 --- /dev/null +++ b/pve/boss/pinyin.js @@ -0,0 +1,6 @@ +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const pinyins = mergeBossSection("拼音", bossSets, "pinyins"); + +export default pinyins; diff --git a/pve/boss/sets.js b/pve/boss/sets.js new file mode 100644 index 0000000..8a36d06 --- /dev/null +++ b/pve/boss/sets.js @@ -0,0 +1,14 @@ +import shiershengxiao from "./sets/shiershengxiao/index.js"; +import zhuoguiquxie from "./sets/zhuoguiquxie/index.js"; +import qingqingzijin from "./sets/qingqingzijin/index.js"; +import tianshu from "./sets/tianshu/index.js"; + +// 活动 BOSS 主题清单;新增主题时只需在此接入一次。 +const bossSets = [ + ["十二生肖", shiershengxiao], + ["捉鬼驱邪", zhuoguiquxie], + ["青青子衿", qingqingzijin], + ["天书乱斗", tianshu], +]; + +export default bossSets; diff --git a/pve/boss/sets/qingqingzijin/card.js b/pve/boss/sets/qingqingzijin/card.js new file mode 100644 index 0000000..263e5b7 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/card.js @@ -0,0 +1,5 @@ +import { lib, game, ui, get, ai, _status } from "../../../../../../noname.js"; + +const cards = {}; + +export default cards; diff --git a/pve/boss/sets/qingqingzijin/characterFilter.js b/pve/boss/sets/qingqingzijin/characterFilter.js new file mode 100644 index 0000000..dfc3d19 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/characterFilter.js @@ -0,0 +1,3 @@ +const characterFilters = {}; + +export default characterFilters; diff --git a/pve/boss/sets/qingqingzijin/characterTitle.js b/pve/boss/sets/qingqingzijin/characterTitle.js new file mode 100644 index 0000000..b65d634 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/characterTitle.js @@ -0,0 +1,3 @@ +const characterTitles = {}; + +export default characterTitles; diff --git a/pve/boss/sets/qingqingzijin/index.js b/pve/boss/sets/qingqingzijin/index.js new file mode 100644 index 0000000..1806e4d --- /dev/null +++ b/pve/boss/sets/qingqingzijin/index.js @@ -0,0 +1,29 @@ +import characters from "./character.js"; +import cards from "./card.js"; +import pinyins from "./pinyin.js"; +import skills from "./skill.js"; +import translates from "./translate.js"; +import characterTitles from "./characterTitle.js"; +import characterIntros from "./intro.js"; +import characterFilters from "./characterFilter.js"; +import dynamicTranslates from "./dynamicTranslate.js"; +import perfectPairs from "./perfectPairs.js"; +import voices from "./voices.js"; +import { characterSort, characterSortTranslate } from "./sort.js"; + +// 青青子衿主题数据片段,由活动 BOSS 根入口统一注册。 +export default { + character: characters, + card: cards, + pinyins, + skill: skills, + translate: translates, + characterTitle: characterTitles, + characterIntro: characterIntros, + characterFilter: characterFilters, + dynamicTranslate: dynamicTranslates, + perfectPair: perfectPairs, + voices, + characterSort, + characterSortTranslate, +}; diff --git a/pve/boss/sets/qingqingzijin/intro.js b/pve/boss/sets/qingqingzijin/intro.js new file mode 100644 index 0000000..94c8d75 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/intro.js @@ -0,0 +1,3 @@ +const characterIntros = {}; + +export default characterIntros; diff --git a/pve/boss/sets/qingqingzijin/perfectPairs.js b/pve/boss/sets/qingqingzijin/perfectPairs.js new file mode 100644 index 0000000..af5539c --- /dev/null +++ b/pve/boss/sets/qingqingzijin/perfectPairs.js @@ -0,0 +1,3 @@ +const perfectPairs = {}; + +export default perfectPairs; diff --git a/pve/boss/sets/qingqingzijin/pinyin.js b/pve/boss/sets/qingqingzijin/pinyin.js new file mode 100644 index 0000000..2a4c4b3 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/pinyin.js @@ -0,0 +1,3 @@ +const pinyins = {}; + +export default pinyins; diff --git a/pve/boss/sets/qingqingzijin/sort.js b/pve/boss/sets/qingqingzijin/sort.js new file mode 100644 index 0000000..27b5221 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/sort.js @@ -0,0 +1,14 @@ +const characterSort = { + shuYing_pve_qingqingzijin: [ + "caocao_boss_shuying", + "simayi_boss_shuying", + "lvbu_boss_shuying", + "dongzhuo_boss_shuying", + ], +}; + +const characterSortTranslate = { + shuYing_pve_qingqingzijin: "青青子衿", +}; + +export { characterSort, characterSortTranslate }; diff --git a/pve/boss/sets/qingqingzijin/voices.js b/pve/boss/sets/qingqingzijin/voices.js new file mode 100644 index 0000000..97412c7 --- /dev/null +++ b/pve/boss/sets/qingqingzijin/voices.js @@ -0,0 +1,3 @@ +const voices = {}; + +export default voices; diff --git a/pve/boss/sets/shiershengxiao/card.js b/pve/boss/sets/shiershengxiao/card.js new file mode 100644 index 0000000..263e5b7 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/card.js @@ -0,0 +1,5 @@ +import { lib, game, ui, get, ai, _status } from "../../../../../../noname.js"; + +const cards = {}; + +export default cards; diff --git a/pve/boss/sets/shiershengxiao/character.js b/pve/boss/sets/shiershengxiao/character.js index eb2b7f8..e54b5e0 100644 --- a/pve/boss/sets/shiershengxiao/character.js +++ b/pve/boss/sets/shiershengxiao/character.js @@ -9,9 +9,40 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/zishu_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/zishu.mp3"] }, - // chouniu_boss_shuying: {}, - // yinhu_boss_shuying: {}, - // maotu_boss_shuying: {}, + + chouniu_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["chouniu_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/chouniu_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/chouniu.mp3"] + }, + + yinhu_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["yinhu_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/yinhu_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/yinhu.mp3"] + }, + + maotu_boss_shuying: { + sex: "female", + group: "qun", + hp: 4, + skills: ["maotu_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/maotu_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/maotu.mp3"] + }, + chenlong_boss_shuying: { sex: "male", group: "qun", @@ -22,7 +53,18 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/chenlong_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/chenlong.mp3"] }, - // sishe_boss_shuying: {}, + + sishe_boss_shuying: { + sex: "female", + group: "qun", + hp: 4, + skills: ["sishe_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/sishe_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/sishe.mp3"] + }, + wuma_boss_shuying: { sex: "male", group: "qun", @@ -33,6 +75,7 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/wuma_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/wuma_shuying.mp3"] }, + weiyang_boss_shuying: { sex: "female", group: "qun", @@ -43,6 +86,7 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/weiyang_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/weiyang.mp3"] }, + shenhou_boss_shuying: { sex: "male", group: "qun", @@ -53,7 +97,18 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/shenhou_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/shenhou.mp3"] }, - // youji_boss_shuying: {}, + + youji_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["youji_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/youji_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/youji.mp3"] + }, + xvgou_boss_shuying: { sex: "male", group: "qun", @@ -64,7 +119,17 @@ const characters = { img: "extension/术樱包/pve/images/十二生肖/xvgou_boss_shuying.png", dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/xvgou.mp3"] }, - // haizhu_boss_shuying: {}, + + haizhu_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["haizhu_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/十二生肖/haizhu_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/haizhu.mp3"] + }, }; export default characters; diff --git a/pve/boss/sets/shiershengxiao/characterFilter.js b/pve/boss/sets/shiershengxiao/characterFilter.js new file mode 100644 index 0000000..dfc3d19 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/characterFilter.js @@ -0,0 +1,3 @@ +const characterFilters = {}; + +export default characterFilters; diff --git a/pve/boss/sets/shiershengxiao/characterTitle.js b/pve/boss/sets/shiershengxiao/characterTitle.js new file mode 100644 index 0000000..b65d634 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/characterTitle.js @@ -0,0 +1,3 @@ +const characterTitles = {}; + +export default characterTitles; diff --git a/pve/boss/sets/shiershengxiao/dynamicTranslate.js b/pve/boss/sets/shiershengxiao/dynamicTranslate.js new file mode 100644 index 0000000..162e610 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/dynamicTranslate.js @@ -0,0 +1,3 @@ +const dynamicTranslates = {}; + +export default dynamicTranslates; diff --git a/pve/boss/sets/shiershengxiao/index.js b/pve/boss/sets/shiershengxiao/index.js new file mode 100644 index 0000000..9ceaeeb --- /dev/null +++ b/pve/boss/sets/shiershengxiao/index.js @@ -0,0 +1,29 @@ +import characters from "./character.js"; +import cards from "./card.js"; +import pinyins from "./pinyin.js"; +import skills from "./skill.js"; +import translates from "./translate.js"; +import characterTitles from "./characterTitle.js"; +import characterIntros from "./intro.js"; +import characterFilters from "./characterFilter.js"; +import dynamicTranslates from "./dynamicTranslate.js"; +import perfectPairs from "./perfectPairs.js"; +import voices from "./voices.js"; +import { characterSort, characterSortTranslate } from "./sort.js"; + +// 十二生肖主题数据片段,由活动 BOSS 根入口统一注册。 +export default { + character: characters, + card: cards, + pinyins, + skill: skills, + translate: translates, + characterTitle: characterTitles, + characterIntro: characterIntros, + characterFilter: characterFilters, + dynamicTranslate: dynamicTranslates, + perfectPair: perfectPairs, + voices, + characterSort, + characterSortTranslate, +}; diff --git a/pve/boss/sets/shiershengxiao/intro.js b/pve/boss/sets/shiershengxiao/intro.js new file mode 100644 index 0000000..94c8d75 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/intro.js @@ -0,0 +1,3 @@ +const characterIntros = {}; + +export default characterIntros; diff --git a/pve/boss/sets/shiershengxiao/perfectPairs.js b/pve/boss/sets/shiershengxiao/perfectPairs.js new file mode 100644 index 0000000..af5539c --- /dev/null +++ b/pve/boss/sets/shiershengxiao/perfectPairs.js @@ -0,0 +1,3 @@ +const perfectPairs = {}; + +export default perfectPairs; diff --git a/pve/boss/sets/shiershengxiao/pinyin.js b/pve/boss/sets/shiershengxiao/pinyin.js new file mode 100644 index 0000000..2a4c4b3 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/pinyin.js @@ -0,0 +1,3 @@ +const pinyins = {}; + +export default pinyins; diff --git a/pve/boss/sets/shiershengxiao/skill.js b/pve/boss/sets/shiershengxiao/skill.js index 30ca4bd..585e3bb 100644 --- a/pve/boss/sets/shiershengxiao/skill.js +++ b/pve/boss/sets/shiershengxiao/skill.js @@ -71,6 +71,137 @@ const skills = { }, }, + //丑牛 + chouniu_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/十二生肖:1", + trigger: { + player: "phaseJieshuBegin", + }, + forced: true, + locked: true, + filter(event, player) { + return player.isDamaged() && player.isMinHp(); + }, + async content(event, trigger, player) { + await player.recover(); + }, + ai: { + threaten: 1.2, + }, + }, + + //寅虎 + yinhu_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/十二生肖:1", + enable: "phaseUse", + position: "he", + filter(event, player) { + const enemies = player.getEnemies(null, false); + return ( + enemies.some(target => target.isIn()) && + player.hasCard(card => lib.skill.yinhu_shuying.filterCard(card, player), "he") + ); + }, + filterCard(card, player) { + if (!lib.filter.cardDiscardable(card, player)) return false; + return !player.getStorage("yinhu_shuying_used").some(cardx => { + return get.type2(cardx[2]) == get.type2(card); + }); + }, + filterTarget(card, player, target) { + return ( + target != player && + target.isIn() && + player.getEnemies(null, false).includes(target) + ); + }, + check(card) { + return 8 - get.value(card); + }, + async content(event, trigger, player) { + const card = event.cards[0]; + const target = event.target; + + player.addTempSkill("yinhu_shuying_used", "phaseUseAfter"); + player.markAuto("yinhu_shuying_used", [ + [get.translation(get.type2(card)), "", card.name], + ]); + + player.line(target); + const damageEvent = target.damage(1, player); + await damageEvent; + + const causedDying = game.getGlobalHistory("everything", evt => { + return evt.name == "dying" && evt.getParent(damageEvent.name) === damageEvent; + }).length > 0; + + if (causedDying) { + player.tempBanSkill("yinhu_shuying"); + } + }, + ai: { + order: 7, + result: { + target(player, target) { + if (!player.getEnemies(null, false).includes(target)) return 0; + return get.damageEffect(target, player, target); + }, + }, + }, + subSkill: { + used: { + charlotte: true, + onremove: true, + intro: { + name: "已弃置的牌类型", + mark(dialog, content = []) { + if (content.length) { + dialog.addSmall([content, "vcard"]); + } + }, + }, + }, + }, + }, + + //卯兔 + maotu_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/十二生肖:1", + trigger: { + global: "dieAfter", + }, + forced: true, + locked: true, + filter(event, player) { + return !player.hasSkill("maotu_shuying_effect", null, false, false); + }, + async content(event, trigger, player) { + player.addTempSkill("maotu_shuying_effect", { + player: "phaseBegin", + }); + }, + subSkill: { + effect: { + charlotte: true, + mark: true, + marktext: "兔", + intro: { + content: "你不是体力值大于等于你的其他角色使用牌的合法目标", + }, + mod: { + targetEnabled(card, source, target) { + if (source != target && source.getHp() >= target.getHp()) { + return false; + } + }, + }, + }, + }, + }, + //辰龙 chenlong_shuying: { mode: ["boss"], @@ -133,6 +264,55 @@ const skills = { }, }, + //巳蛇 + sishe_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) + ); + }, + async cost(event, trigger, player) { + event.result = await player + .chooseBool(get.prompt2(event.skill, trigger.source)) + .set("ai", () => { + const player = get.player(); + const trigger = get.event().getTrigger(); + return ( + player.getEnemies(null, false).includes(trigger.source) && + get.damageEffect(trigger.source, player, player) > 0 + ); + }) + .forResult(); + }, + async content(event, trigger, player) { + const source = trigger.source; + player.line(source); + await source.damage(trigger.num, player); + }, + ai: { + threaten: 0.6, + maixie: true, + effect: { + target(card, source, target) { + if ( + get.tag(card, "damage") && + target.getEnemies(null, false).includes(source) + ) { + return [1, 0, 0, -0.7]; + } + }, + }, + }, + }, + //午马 wuma_shuying: { mode: ["boss"], @@ -177,6 +357,7 @@ const skills = { }, }, }, + //未羊 weiyang_shuying: { mode: ["boss"], @@ -257,6 +438,26 @@ const skills = { }, }, + //酉鸡 + youji_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/十二生肖:1", + trigger: { + player: "phaseDrawBegin2", + }, + forced: true, + locked: true, + filter(event, player) { + return game.roundNumber > 0 && !event.numFixed; + }, + async content(event, trigger, player) { + trigger.num += Math.min(5, game.roundNumber); + }, + ai: { + threaten: 1.4, + }, + }, + //戌狗 xvgou_shuying: { mode: ["boss"], @@ -292,6 +493,77 @@ const skills = { }, }, + //亥猪 + haizhu_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/十二生肖:1", + trigger: { + player: "phaseZhunbeiBegin", + global: ["loseAfter", "loseAsyncAfter"], + }, + forced: true, + locked: true, + filter(event, player) { + if (event.name == "phaseZhunbei") { + return player.isMaxHandcard(); + } + + if (event.type != "discard" || event.getlx === false) { + return false; + } + + return game.hasPlayer(target => { + if (target == player) return false; + + return event.getl?.(target)?.cards2?.some(card => { + return ( + get.color(card) == "black" && + get.position(card, true) == "d" + ); + }); + }); + }, + async content(event, trigger, player) { + if (trigger.name == "phaseZhunbei") { + await player.loseHp(); + return; + } + + const cards = game + .filterPlayer(target => { + if (target == player) return false; + + return trigger.getl?.(target)?.cards2?.some(card => { + return ( + get.color(card) == "black" && + get.position(card, true) == "d" + ); + }); + }) + .reduce((list, target) => { + return list.addArray( + trigger + .getl(target) + .cards2.filter(card => { + return ( + get.color(card) == "black" && + get.position(card, true) == "d" + ); + }) + ); + }, []); + + if (!cards.length) return; + + if (trigger.delay === false) { + await game.delay(); + } + await player.gain(cards, "gain2"); + }, + ai: { + threaten: 1.6, + }, + }, }; export default skills; diff --git a/pve/boss/sets/shiershengxiao/sort.js b/pve/boss/sets/shiershengxiao/sort.js new file mode 100644 index 0000000..993c2d5 --- /dev/null +++ b/pve/boss/sets/shiershengxiao/sort.js @@ -0,0 +1,16 @@ +const characterSort = { + shuYing_pve_shiershengxiao: [ + "zishu_boss_shuying", + "chenlong_boss_shuying", + "wuma_boss_shuying", + "weiyang_boss_shuying", + "shenhou_boss_shuying", + "xvgou_boss_shuying", + ], +}; + +const characterSortTranslate = { + shuYing_pve_shiershengxiao: "十二生肖", +}; + +export { characterSort, characterSortTranslate }; diff --git a/pve/boss/sets/shiershengxiao/translate.js b/pve/boss/sets/shiershengxiao/translate.js index 398c712..f22bb55 100644 --- a/pve/boss/sets/shiershengxiao/translate.js +++ b/pve/boss/sets/shiershengxiao/translate.js @@ -3,10 +3,26 @@ const translates = { zishu_shuying: "子鼠", zishu_shuying_info: "出牌阶段限一次,你可以获得手牌数大于你的其他角色一张手牌,你可以重复此流程,直到你的手牌数为全场最多。", + chouniu_boss_shuying: "丑牛", + chouniu_shuying: "丑牛", + chouniu_shuying_info: "锁定技,结束阶段,若你的体力值为全场最小,则你回复1点体力。", + + yinhu_boss_shuying: "寅虎", + yinhu_shuying: "寅虎", + yinhu_shuying_info: "出牌阶段,你可以弃置一张牌(以此法弃置的牌须类型各不相同),然后对一名其他角色造成1点伤害;若你以此法导致一名角色进入濒死状态,则此技能失效直到回合结束。", + + maotu_boss_shuying: "卯兔", + maotu_shuying: "卯兔", + maotu_shuying_info: "锁定技,场上有角色阵亡时,你获得【卯兔】标记,你的回合开始时,你移除【卯兔标记】(【卯兔】标记:你不是体力大于等于你的其他角色使用牌的合法目标)。", + chenlong_boss_shuying: "辰龙", chenlong_shuying: "辰龙", chenlong_shuying_info: "限定技,出牌阶段,你可以失去任意点体力(至多为5),然后对一名其他角色造成等量的伤害。若你以此法进入濒死状态,则你将体力值回复至1,然后减少1点体力上限。", + sishe_boss_shuying: "巳蛇", + sishe_shuying: "巳蛇", + sishe_shuying_info: "当你受到伤害后,你可以对伤害来源造成等量伤害。", + wuma_boss_shuying: "午马", wuma_shuying: "午马", wuma_shuying_info: "锁定技,你不能被翻面;你的阶段不能被跳过;当你成为其他角色使用锦囊牌的目标后,摸一张牌。", @@ -19,10 +35,17 @@ const translates = { shenhou_shuying: "申猴", shenhou_shuying_info: "当你成为【杀】的目标时,你可以进行判定,若结果为红色,则此【杀】对你无效。", + youji_boss_shuying: "酉鸡", + youji_shuying: "酉鸡", + youji_shuying_info: "锁定技,摸牌阶段,你多摸X张牌(X为游戏轮数且最多为5)。", + xvgou_boss_shuying: "戌狗", xvgou_shuying: "戌狗", xvgou_shuying_info: "锁定技,红色【杀】对你无效;你使用红色【杀】无距离限制且造成伤害+1.", + haizhu_boss_shuying: "亥猪", + haizhu_shuying: "亥猪", + haizhu_shuying_info: "锁定技,当其他角色的黑色牌因弃置而置入弃牌堆后,你获得这些牌;准备阶段开始时,若你的手牌数为场上最多的或之一,你失去1点体力。", }; export default translates; diff --git a/pve/boss/sets/shiershengxiao/voices.js b/pve/boss/sets/shiershengxiao/voices.js new file mode 100644 index 0000000..3d4003f --- /dev/null +++ b/pve/boss/sets/shiershengxiao/voices.js @@ -0,0 +1,39 @@ +const voices = { + "#zishu_shuying1": "这些牌都归我吧!", + "#zishu_boss_shuying:die": "油米全没了。", + + "#chouniu_shuying1": "牛角之歌,自保足以。", + "#chouniu_boss_shuying:die": "请将我埋于此地吧。", + + "#yinhu_shuying1": "尝尝我的厉害吧。", + "#yinhu_boss_shuying:die": "百兽之王也有终老。", + + "#maotu_shuying1": "想抓到我,不可能!", + "#maotu_boss_shuying:die": "这灾祸,是躲不过了。", + + "#chenlong_shuying1": "龙怒的威力,不是你所能承受的。", + "#chenlong_boss_shuying:die": "龙威不再,龙鳞已落。", + + "#sishe_shuying1": "伤我者,一一奉还。", + "#sishe_boss_shuying:die": "我的毒液失效了。", + + "#wuma_shuying1": "有我在,必成功。", + "#wuma_boss_shuying:die": "马有失蹄啊。", + + "#weiyang_shuying1": "共享绵泽,同甘共苦。", + "#weiyang_boss_shuying:die": "看不到青草翠绿时。", + + "#shenhou_shuying1": "百般变化,真假难辨。", + "#shenhou_boss_shuying:die": "这仙桃无用了。", + + "#youji_shuying1": "鸡豚之息,虽微渐厚。", + "#youji_boss_shuying:die": "杀鸡取卵,不可取呀!", + + "#xvgou_shuying1": "驱邪吠恶,遇凶斩杀。", + "#xvgou_boss_shuying:die": "不能守护家园了。", + + "#haizhu_shuying1": "这些,都归我吧。", + "#haizhu_boss_shuying:die": "果然,还是吃太多了。", +}; + +export default voices; diff --git a/pve/boss/sets/tianshu/card.js b/pve/boss/sets/tianshu/card.js new file mode 100644 index 0000000..263e5b7 --- /dev/null +++ b/pve/boss/sets/tianshu/card.js @@ -0,0 +1,5 @@ +import { lib, game, ui, get, ai, _status } from "../../../../../../noname.js"; + +const cards = {}; + +export default cards; diff --git a/pve/boss/sets/tianshu/characterFilter.js b/pve/boss/sets/tianshu/characterFilter.js new file mode 100644 index 0000000..dfc3d19 --- /dev/null +++ b/pve/boss/sets/tianshu/characterFilter.js @@ -0,0 +1,3 @@ +const characterFilters = {}; + +export default characterFilters; diff --git a/pve/boss/sets/tianshu/characterTitle.js b/pve/boss/sets/tianshu/characterTitle.js new file mode 100644 index 0000000..b65d634 --- /dev/null +++ b/pve/boss/sets/tianshu/characterTitle.js @@ -0,0 +1,3 @@ +const characterTitles = {}; + +export default characterTitles; diff --git a/pve/boss/sets/tianshu/index.js b/pve/boss/sets/tianshu/index.js new file mode 100644 index 0000000..0a1fcaa --- /dev/null +++ b/pve/boss/sets/tianshu/index.js @@ -0,0 +1,29 @@ +import characters from "./character.js"; +import cards from "./card.js"; +import pinyins from "./pinyin.js"; +import skills from "./skill.js"; +import translates from "./translate.js"; +import characterTitles from "./characterTitle.js"; +import characterIntros from "./intro.js"; +import characterFilters from "./characterFilter.js"; +import dynamicTranslates from "./dynamicTranslate.js"; +import perfectPairs from "./perfectPairs.js"; +import voices from "./voices.js"; +import { characterSort, characterSortTranslate } from "./sort.js"; + +// 天书乱斗主题数据片段,由活动 BOSS 根入口统一注册。 +export default { + character: characters, + card: cards, + pinyins, + skill: skills, + translate: translates, + characterTitle: characterTitles, + characterIntro: characterIntros, + characterFilter: characterFilters, + dynamicTranslate: dynamicTranslates, + perfectPair: perfectPairs, + voices, + characterSort, + characterSortTranslate, +}; diff --git a/pve/boss/sets/tianshu/intro.js b/pve/boss/sets/tianshu/intro.js new file mode 100644 index 0000000..94c8d75 --- /dev/null +++ b/pve/boss/sets/tianshu/intro.js @@ -0,0 +1,3 @@ +const characterIntros = {}; + +export default characterIntros; diff --git a/pve/boss/sets/tianshu/perfectPairs.js b/pve/boss/sets/tianshu/perfectPairs.js new file mode 100644 index 0000000..af5539c --- /dev/null +++ b/pve/boss/sets/tianshu/perfectPairs.js @@ -0,0 +1,3 @@ +const perfectPairs = {}; + +export default perfectPairs; diff --git a/pve/boss/sets/tianshu/pinyin.js b/pve/boss/sets/tianshu/pinyin.js new file mode 100644 index 0000000..2a4c4b3 --- /dev/null +++ b/pve/boss/sets/tianshu/pinyin.js @@ -0,0 +1,3 @@ +const pinyins = {}; + +export default pinyins; diff --git a/pve/boss/sets/tianshu/sort.js b/pve/boss/sets/tianshu/sort.js new file mode 100644 index 0000000..dab98dc --- /dev/null +++ b/pve/boss/sets/tianshu/sort.js @@ -0,0 +1,16 @@ +const characterSort = { + shuYing_pve_tianshu: [ + "shuishengonggong_boss_shuying", + "baiqi_boss_shuying", + "kuafu_boss_shuying", + "shaohao_boss_shuying", + "xuannv_boss_shuying", + "hanba_boss_shuying", + ], +}; + +const characterSortTranslate = { + shuYing_pve_tianshu: "天书乱斗", +}; + +export { characterSort, characterSortTranslate }; diff --git a/pve/boss/sets/tianshu/voices.js b/pve/boss/sets/tianshu/voices.js new file mode 100644 index 0000000..97412c7 --- /dev/null +++ b/pve/boss/sets/tianshu/voices.js @@ -0,0 +1,3 @@ +const voices = {}; + +export default voices; diff --git a/pve/boss/sets/zhuoguiquxie/card.js b/pve/boss/sets/zhuoguiquxie/card.js new file mode 100644 index 0000000..263e5b7 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/card.js @@ -0,0 +1,5 @@ +import { lib, game, ui, get, ai, _status } from "../../../../../../noname.js"; + +const cards = {}; + +export default cards; diff --git a/pve/boss/sets/zhuoguiquxie/character.js b/pve/boss/sets/zhuoguiquxie/character.js index c2726db..0254c7b 100644 --- a/pve/boss/sets/zhuoguiquxie/character.js +++ b/pve/boss/sets/zhuoguiquxie/character.js @@ -1,4 +1,36 @@ const characters = { + mengpo_boss_shuying: { + sex: "female", + group: "qun", + hp: 4, + skills: ["aotang_shuying", "yunju_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/捉鬼驱邪/mengpo_boss_shuying.png" + }, + + baowei_boss_shuying: + { + sex: "male", + group: "qun", + hp: 4, + skills: ["eli_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/捉鬼驱邪/baowei_boss_shuying.png" + }, + + niaozui_boss_shuying: + { + sex: "male", + group: "qun", + hp: 4, + skills: ["suoxue_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/捉鬼驱邪/niaozui_boss_shuying.png" + }, + heibaiwuchang_boss_shuying: { sex: "male", @@ -7,17 +39,21 @@ const characters = { skills: ["mizui_shuying", "qiangzheng_shuying"], isHiddenBoss: true, isBossAllowed: true, - img: "extension/术樱包/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png" + img: "extension/术樱包/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/捉鬼驱邪/heibaiwuchang.mp3"] }, + niutoumamian_boss_shuying: { sex: "male", group: "qun", hp: 4, - skills: ["shiyv_shuying", "manji_shuying", "xiaoshou_shuying"], + skills: ["manji_shuying", "shiyv_shuying"], isHiddenBoss: true, isBossAllowed: true, - img: "extension/术樱包/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png" + img: "extension/术樱包/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/捉鬼驱邪/niutoumamian.mp3"] }, + yvsai_boss_shuying: { sex: "female", group: "qun", @@ -27,6 +63,7 @@ const characters = { isBossAllowed: true, img: "extension/术樱包/pve/images/捉鬼驱邪/yvsai_boss_shuying.png" }, + huangfeng_boss_shuying: { sex: "male", group: "qun", @@ -36,6 +73,7 @@ const characters = { isBossAllowed: true, img: "extension/术樱包/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png" }, + riyeyoushen_boss_shuying: { sex: "male", group: "qun", @@ -43,7 +81,29 @@ const characters = { skills: ["huiyun_shuying", "yezhong_shuying", "zhoucha_shuying"], isHiddenBoss: true, isBossAllowed: true, - img: "extension/术樱包/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png" + img: "extension/术樱包/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/捉鬼驱邪/riyeyoushen.mp3"] + }, + + guiwang_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["jizhou_shuying", "danshi_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/捉鬼驱邪/guiwang_boss_shuying.png", + dieAudios: ["ext:术樱包/pve/audio/dead/捉鬼驱邪/guiwang.mp3"] + }, + + yanluowang_boss_shuying: { + sex: "male", + group: "qun", + hp: 4, + skills: ["jizhou_shuying", "danshi_shuying"], + isHiddenBoss: true, + isBossAllowed: true, + img: "extension/术樱包/pve/images/捉鬼驱邪/yanluowang_boss_shuying.png" }, }; diff --git a/pve/boss/sets/zhuoguiquxie/characterFilter.js b/pve/boss/sets/zhuoguiquxie/characterFilter.js new file mode 100644 index 0000000..dfc3d19 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/characterFilter.js @@ -0,0 +1,3 @@ +const characterFilters = {}; + +export default characterFilters; diff --git a/pve/boss/sets/zhuoguiquxie/characterTitle.js b/pve/boss/sets/zhuoguiquxie/characterTitle.js new file mode 100644 index 0000000..b65d634 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/characterTitle.js @@ -0,0 +1,3 @@ +const characterTitles = {}; + +export default characterTitles; diff --git a/pve/boss/sets/zhuoguiquxie/dynamicTranslate.js b/pve/boss/sets/zhuoguiquxie/dynamicTranslate.js index 300b4d0..db975ca 100644 --- a/pve/boss/sets/zhuoguiquxie/dynamicTranslate.js +++ b/pve/boss/sets/zhuoguiquxie/dynamicTranslate.js @@ -1,6 +1,86 @@ import { getTianshuDifficulty } from "../../shared.js"; const dynamicTranslates = { + //孟婆 + aotang_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + + if (difficulty == "nightmare") { + return "锁定技,你的回合开始时,令随机两名敌方角色遗忘所有武将技能直到你的下回合开始。"; + } + if (difficulty == "hard") { + return "锁定技,你的回合开始时,令随机一名敌方角色遗忘所有武将技能直到你的下回合开始。"; + } + + return "锁定技,你的回合开始时,令随机一名敌方角色随机遗忘1个武将技能直到你的下回合开始。"; + }, + yunju_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + return `锁定技,敌方角色的回合结束时,该角色随机弃置${num}张手牌。`; + }, + + //豹尾 + eli_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + + if (difficulty == "nightmare") { + return "锁定技,你对敌方角色造成伤害时,你进行一次判定:若结果为红色,此伤害+1;若结果为黑色,你摸1张牌并获得“完杀”直到回合结束。"; + } + + return "锁定技,你每回合首次对敌方角色造成伤害时,你进行一次判定:若结果为红色,此伤害+1;若结果为黑色,你获得“完杀”直到回合结束。"; + }, + + //鸟嘴 + bingyi_shuying(player, skill) { + const num = getTianshuDifficulty() == "nightmare" ? 8 : 5; + + return `锁定技,每回合首次失去最后的手牌时,你摸${num}张牌。`; + }, + suoxue_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + return `每回合限${get.cnNumber(num)}次,你使用伤害牌指定单一目标后,若其手牌数大于你,你可将手牌摸至与该角色相同(至多5张);若其手牌数小于你,你可弃置一张手牌令此牌不能抵消。`; + }, + + //牛头马面 + xiaoshou_shuying(player, skill) { + const num = getTianshuDifficulty() == "nightmare" ? 3 : 2; + + return `锁定技,准备阶段,你对随机一名敌方角色造成${num}点伤害。`; + }, + manji_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + const extra = + difficulty == "nightmare" + ? "且你此阶段使用【杀】次数+1" + : ""; + + return `你使用【杀】指定单一目标后,你可以弃置该角色${num}张手牌。若其中有【杀】,你本次【杀】的伤害+1${extra}。`; + }, + //黑白无常 mizui_shuying(player, skill) { const num = getTianshuDifficulty() == "normal" ? 1 : 2 @@ -68,7 +148,24 @@ const dynamicTranslates = { return `锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+${num}。` }, + //鬼王 + jizhou_shuying(player, skill) { + const num = getTianshuDifficulty() == "nightmare" ? 2 : 1; + return `锁定技,敌方角色的出牌阶段开始时,你进行一次判定,然后其选择一项:1.弃置任意张点数之和大于判定结果的牌,你获得${num}个“噬”标记;2.失去${num}点体力。`; + }, + danshi_shuying(player, skill) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + return `锁定技,你受到伤害时此伤害+1,然后你摸${num}张牌并移去1个“噬”。`; + }, }; export default dynamicTranslates; diff --git a/pve/boss/sets/zhuoguiquxie/index.js b/pve/boss/sets/zhuoguiquxie/index.js new file mode 100644 index 0000000..bbee735 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/index.js @@ -0,0 +1,29 @@ +import characters from "./character.js"; +import cards from "./card.js"; +import pinyins from "./pinyin.js"; +import skills from "./skill.js"; +import translates from "./translate.js"; +import characterTitles from "./characterTitle.js"; +import characterIntros from "./intro.js"; +import characterFilters from "./characterFilter.js"; +import dynamicTranslates from "./dynamicTranslate.js"; +import perfectPairs from "./perfectPairs.js"; +import voices from "./voices.js"; +import { characterSort, characterSortTranslate } from "./sort.js"; + +// 捉鬼驱邪主题数据片段,由活动 BOSS 根入口统一注册。 +export default { + character: characters, + card: cards, + pinyins, + skill: skills, + translate: translates, + characterTitle: characterTitles, + characterIntro: characterIntros, + characterFilter: characterFilters, + dynamicTranslate: dynamicTranslates, + perfectPair: perfectPairs, + voices, + characterSort, + characterSortTranslate, +}; diff --git a/pve/boss/sets/zhuoguiquxie/intro.js b/pve/boss/sets/zhuoguiquxie/intro.js new file mode 100644 index 0000000..94c8d75 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/intro.js @@ -0,0 +1,3 @@ +const characterIntros = {}; + +export default characterIntros; diff --git a/pve/boss/sets/zhuoguiquxie/perfectPairs.js b/pve/boss/sets/zhuoguiquxie/perfectPairs.js new file mode 100644 index 0000000..af5539c --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/perfectPairs.js @@ -0,0 +1,3 @@ +const perfectPairs = {}; + +export default perfectPairs; diff --git a/pve/boss/sets/zhuoguiquxie/pinyin.js b/pve/boss/sets/zhuoguiquxie/pinyin.js new file mode 100644 index 0000000..2a4c4b3 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/pinyin.js @@ -0,0 +1,3 @@ +const pinyins = {}; + +export default pinyins; diff --git a/pve/boss/sets/zhuoguiquxie/skill.js b/pve/boss/sets/zhuoguiquxie/skill.js index 0fff0e1..3a916c3 100644 --- a/pve/boss/sets/zhuoguiquxie/skill.js +++ b/pve/boss/sets/zhuoguiquxie/skill.js @@ -1,6 +1,472 @@ import { lib, game, ui, get, ai, _status, getTianshuDifficulty } from "../../shared.js"; const skills = { + //孟婆 over + aotang_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "phaseBegin", + }, + forced: true, + locked: true, + filter(event, player) { + return player.getEnemies(null, false).some(target => { + if (!target.isIn()) return false; + + return target.getStockSkills(false, true).some(skill => { + const info = get.info(skill); + return info && !info.charlotte && !info.temp && !info.sub; + }); + }); + }, + async content(event, trigger, player) { + const difficulty = getTianshuDifficulty(); + const enemies = player.getEnemies(null, false).filter(target => { + if (!target.isIn()) return false; + + return target.getStockSkills(false, true).some(skill => { + const info = get.info(skill); + return info && !info.charlotte && !info.temp && !info.sub; + }); + }); + if (!enemies.length) return; + + const targetNum = difficulty == "nightmare" ? 2 : 1; + const targets = enemies.randomGets(Math.min(targetNum, enemies.length)); + const disableKey = `aotang_shuying_${player.playerid}`; + + player.addSkill("aotang_shuying_clear"); + player.storage.aotang_shuying_disabled = []; + + for (const target of targets) { + let skills = target.getStockSkills(false, true).filter(skill => { + const info = get.info(skill); + return info && !info.charlotte && !info.temp && !info.sub; + }); + if (!skills.length) continue; + + if (difficulty == "normal") { + skills = [skills.randomGet()]; + } + + player.line(target); + target.disableSkill(disableKey, skills); + player.storage.aotang_shuying_disabled.push({ + target, + disableKey, + }); + + game.log( + target, + "暂时遗忘了技能", + `#g${get.translation(skills)}` + ); + } + }, + onremove(player) { + player.removeSkill("aotang_shuying_clear"); + }, + ai: { + threaten: 2, + }, + subSkill: { + clear: { + charlotte: true, + forced: true, + forceDie: true, + popup: false, + firstDo: true, + priority: 100, + trigger: { + player: ["phaseBegin", "dieAfter"], + }, + async content(event, trigger, player) { + player.removeSkill("aotang_shuying_clear"); + }, + onremove(player) { + for (const record of player.getStorage("aotang_shuying_disabled")) { + record.target?.enableSkill(record.disableKey); + } + delete player.storage.aotang_shuying_disabled; + }, + }, + }, + }, + yunju_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + global: "phaseEnd", + }, + forced: true, + locked: true, + logTarget: "player", + filter(event, player) { + return ( + event.player?.isIn() && + event.player.countCards("h") > 0 && + player.getEnemies(null, false).includes(event.player) + ); + }, + async content(event, trigger, player) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + player.line(trigger.player); + await trigger.player.randomDiscard(num, "h"); + }, + ai: { + threaten: 1.5, + }, + }, + guimei_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + forced: true, + locked: true, + group: [ + "guimei_shuying_turn", + "guimei_shuying_redirect", + ], + subSkill: { + turn: { + audio: "guimei_shuying", + trigger: { + player: "turnOverBefore", + }, + forced: true, + locked: true, + filter(event, player) { + return ( + getTianshuDifficulty() == "nightmare" && + !player.isTurnedOver() + ); + }, + async content(event, trigger, player) { + trigger.cancel(); + }, + }, + redirect: { + audio: "guimei_shuying", + trigger: { + global: "useCardToPlayer", + }, + forced: true, + locked: true, + usable: 1, + filter(event, player) { + return ( + getTianshuDifficulty() == "nightmare" && + event.player != player && + event.target == player && + event.isFirstTarget && + event.targets?.length == 1 + ); + }, + async content(event, trigger, player) { + const source = trigger.player; + const originalTarget = trigger.target; + let targets; + + if (get.type(trigger.card) == "delay") { + targets = game.filterPlayer(target => { + return lib.filter.judge(trigger.card, source, target); + }); + } else { + targets = game.filterPlayer(target => { + return ( + lib.filter.targetEnabled2(trigger.card, source, target) && + lib.filter.targetInRange(trigger.card, source, target) + ); + }); + } + + targets.add(originalTarget); + const target = targets.randomGet(); + + trigger.targets.remove(originalTarget); + trigger.getParent().triggeredTargets1?.remove(originalTarget); + trigger.getParent().triggeredTargets2?.remove(originalTarget); + trigger.untrigger(); + trigger.targets.add(target); + + source.line(target, "thunder"); + game.log(trigger.card, "的目标被改为了", target); + + if (target == player) { + await source.loseHp(); + } else { + await player.recover(); + } + }, + }, + }, + ai: { + threaten: 1.8, + effect: { + target(card, source, target) { + if ( + getTianshuDifficulty() == "nightmare" && + source != target + ) { + return [1, 0.5]; + } + }, + }, + }, + }, + + //豹尾 over + eli_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + source: "damageBegin1", + }, + forced: true, + locked: true, + usable(skill, player) { + return getTianshuDifficulty() == "nightmare" ? Infinity : 1; + }, + filter(event, player) { + return ( + event.num > 0 && + event.player?.isIn() && + player.getEnemies(null, false).includes(event.player) + ); + }, + async content(event, trigger, player) { + const difficulty = getTianshuDifficulty(); + const result = await player.judge(card => { + return get.color(card) == "red" ? 2 : 1; + }).forResult(); + + if (result.color == "red") { + trigger.num++; + return; + } + + if (difficulty == "nightmare") { + await player.draw(); + } + player.addTempSkill("wansha", { + global: "phaseAfter", + }); + }, + ai: { + threaten: 1.7, + damageBonus: true, + }, + }, + yinsha_shuying: { + mode: ["boss"], + audio: false, + forced: true, + locked: true, + mod: { + targetEnabled(card, source, target) { + if ( + getTianshuDifficulty() != "normal" && + source != target && + !source.inRange(target) + ) { + return false; + } + }, + }, + group: "yinsha_shuying_response", + subSkill: { + response: { + audio: "yinsha_shuying", + trigger: { + player: "useCard", + }, + forced: true, + locked: true, + filter(event, player) { + return ( + getTianshuDifficulty() != "normal" && + game.hasPlayer(target => { + return target != player && !target.inRange(player); + }) + ); + }, + async content(event, trigger, player) { + const targets = game.filterPlayer(target => { + return target != player && !target.inRange(player); + }); + + trigger.directHit.addArray(targets); + game.log(targets, "不能响应", player, "使用的", trigger.card); + }, + }, + }, + ai: { + threaten: 1.8, + }, + }, + + //鸟嘴 over + suoxue_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "useCardToPlayered", + }, + usable(skill, player) { + const difficulty = getTianshuDifficulty(); + + if (difficulty == "nightmare") return 3; + if (difficulty == "hard") return 2; + return 1; + }, + filter(event, player) { + if ( + !event.target?.isIn() || + event.targets?.length != 1 || + !get.tag(event.card, "damage") || + !player.getEnemies(null, false).includes(event.target) + ) { + return false; + } + + const playerCount = player.countCards("h"); + const targetCount = event.target.countCards("h"); + + if (targetCount > playerCount) return true; + if (targetCount >= playerCount) return false; + + return player.hasCard(card => { + return lib.filter.cardDiscardable(card, player); + }, "h"); + }, + async cost(event, trigger, player) { + const target = trigger.target; + const playerCount = player.countCards("h"); + const targetCount = target.countCards("h"); + + if (targetCount > playerCount) { + const num = Math.min(5, targetCount - playerCount); + const result = await player + .chooseBool( + get.prompt(event.skill, target), + `将手牌摸至与${get.translation(target)}相同,至多摸${get.cnNumber(num)}张牌` + ) + .set("ai", () => true) + .forResult(); + + if (result.bool) { + result.cost_data = "draw"; + } + event.result = result; + return; + } + + const result = await player + .chooseToDiscard( + "h", + 1, + get.prompt(event.skill, target), + `弃置一张手牌,令${get.translation(trigger.card)}不能被${get.translation(target)}响应` + ) + .set("filterCard", lib.filter.cardDiscardable) + .set("ai", card => { + const player = get.player(); + const trigger = get.event().getTrigger(); + + if ( + get.damageEffect( + trigger.target, + player, + player + ) <= 0 + ) { + return 0; + } + return 7 - get.value(card); + }) + .forResult(); + + if (result.bool) { + result.cost_data = "directHit"; + } + event.result = result; + }, + async content(event, trigger, player) { + const target = trigger.target; + player.line(target); + + if (event.cost_data == "draw") { + const num = Math.min( + 5, + target.countCards("h") - player.countCards("h") + ); + if (num > 0) { + await player.draw(num); + } + return; + } + + trigger.getParent().directHit.add(target); + game.log(target, "不能响应", trigger.card); + }, + ai: { + threaten: 1.4, + }, + }, + bingyi_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "loseAfter", + global: [ + "equipAfter", + "addJudgeAfter", + "gainAfter", + "loseAsyncAfter", + "addToExpansionAfter", + ], + }, + usable: 1, + forced: true, + locked: true, + filter(event, player) { + if ( + getTianshuDifficulty() == "normal" || + player.countCards("h") > 0 + ) { + return false; + } + + const evt = event.getl(player); + return evt?.player == player && evt.hs?.length > 0; + }, + async content(event, trigger, player) { + const num = getTianshuDifficulty() == "nightmare" ? 8 : 5; + await player.draw(num); + }, + ai: { + threaten: 1.5, + effect: { + target(card, source, target) { + if ( + get.tag(card, "loseCard") && + target.countCards("h") == 1 + ) { + return [1, 0.6]; + } + }, + }, + }, + }, + //黑白无常 over mizui_shuying: { mode: ["boss"], @@ -109,58 +575,188 @@ const skills = { }, }, - //牛头马面 - 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"); - }, - }, + //牛头马面 over manji_shuying: { mode: ["boss"], audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", - trigger: { player: "useCardToPlayered" }, + 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; + return ( + event.card.name == "sha" && + event.targets?.length == 1 && + event.target?.isIn() && + 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) + const difficulty = getTianshuDifficulty(); + const target = trigger.target; + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + num = Math.min( + num, + target.countDiscardableCards(player, "h") + ); + + event.result = await player + .discardPlayerCard( + target, + "h", + get.prompt2(event.skill, target), + num + ) + .set("chooseonly", true) + .set("ai", button => { + const card = button.link; + if (get.name(card, false) == "sha") { + return 12 - get.value(card); + } + return 6 - get.value(card); + }) .forResult(); }, async content(event, trigger, player) { const target = trigger.target; + const cards = event.cards; + 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++; + await target.modedDiscard(cards, player); + + if (!cards.some(card => get.name(card, false) == "sha")) { + return; + } + + trigger.getParent().baseDamage++; + + if (getTianshuDifficulty() == "nightmare") { + player.addTempSkill( + "manji_shuying_sha", + "phaseUseAfter" + ); + player.addMark("manji_shuying_sha", 1, false); + } + }, + ai: { + threaten: 1.5, + }, + subSkill: { + sha: { + charlotte: true, + onremove: true, + mark: true, + marktext: "杀", + intro: { + content: "本阶段使用【杀】的次数上限+#", + }, + mod: { + cardUsable(card, player, num) { + if (card.name == "sha") { + return num + player.countMark("manji_shuying_sha"); + } + }, + }, + }, + }, + }, + shiyu_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "phaseDrawBegin1", + }, + forced: true, + locked: true, + async content(event, trigger, player) { + trigger.changeToZero(); + + const pileCards = Array.from(ui.cardPile.childNodes); + const cards = []; + + for (const suit of lib.suit) { + const list = pileCards.filter(card => { + return get.suit(card, false) == suit; + }); + if (list.length) { + cards.push(list.randomGet()); + } + } + + if (cards.length) { + await player.gain(cards, "gain2"); + } + }, + ai: { + threaten: 1.4, }, - ai: { expose: 0.2 }, }, xiaoshou_shuying: { mode: ["boss"], audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", - trigger: { player: "phaseZhunbeiBegin" }, + trigger: { + player: "phaseZhunbeiBegin", + }, forced: true, + locked: true, filter(event, player) { - return player.getEnemies(null, false).some(target => target.isIn()); + return ( + getTianshuDifficulty() != "normal" && + player.getEnemies(null, false).some(target => target.isIn()) + ); }, async content(event, trigger, player) { - const target = player.getEnemies(null, false).filter(target => target.isIn()).randomGet(); + const enemies = player + .getEnemies(null, false) + .filter(target => target.isIn()); + if (!enemies.length) return; + + const target = enemies.randomGet(); + const num = getTianshuDifficulty() == "nightmare" ? 3 : 2; + player.line(target); - await target.damage(2, player); + await target.damage(num, player); + }, + ai: { + threaten: 1.8, + }, + }, + guizhao_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "useCard", + }, + forced: true, + locked: true, + filter(event, player) { + if ( + getTianshuDifficulty() != "nightmare" || + _status.currentPhase != player + ) { + return false; + } + + const type = get.type2(event.card, false); + return !player.getHistory("useCard").some(evt => { + return ( + evt !== event && + get.type2(evt.card, false) == type + ); + }); + }, + async content(event, trigger, player) { + await player.draw(); + }, + ai: { + threaten: 1.3, }, }, @@ -564,7 +1160,472 @@ const skills = { }, }, + //鬼王 over + jizhou_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + global: "phaseUseBegin", + }, + forced: true, + locked: true, + logTarget: "player", + filter(event, player) { + return ( + event.player?.isIn() && + player.getEnemies(null, false).includes(event.player) + ); + }, + async content(event, trigger, player) { + const target = trigger.player; + const difficulty = getTianshuDifficulty(); + const num = difficulty == "nightmare" ? 2 : 1; + const result = await player.judge().forResult(); + const judgeNumber = result.number; + const cards = target.getCards("he", card => { + return lib.filter.cardDiscardable(card, target); + }); + const canDiscard = + cards.reduce((sum, card) => { + return sum + get.number(card, false); + }, 0) > judgeNumber; + + let discarded = false; + + if (canDiscard) { + const discardResult = await target + .chooseToDiscard( + "he", + `疾咒:弃置任意张点数之和大于${get.cnNumber(judgeNumber)}的牌,或取消并失去${get.cnNumber(num)}点体力` + ) + .set("complexCard", true) + .set("judgeNumber", judgeNumber) + .set("selectCard", () => { + const sum = ui.selected.cards.reduce((total, card) => { + return total + get.number(card, false); + }, 0); + + if (sum > get.event().judgeNumber) { + return ui.selected.cards.length; + } + return ui.selected.cards.length + 1; + }) + .set("ai", card => { + const target = get.player(); + const selected = ui.selected.cards.reduce((sum, current) => { + return sum + get.number(current, false); + }, 0); + + if (selected > get.event().judgeNumber) return 0; + + const urgency = target.hp <= num ? 4 : target.hp <= 2 ? 2 : 1; + return get.number(card, false) * urgency - get.value(card); + }) + .set("num", num) + .forResult(); + + discarded = discardResult.bool; + } + + if (discarded) { + target.line(player); + player.addMark("jizhou_shuying", num, false); + } else { + player.line(target); + await target.loseHp(num); + } + }, + marktext: "噬", + intro: { + name: "噬", + content: "mark", + }, + ai: { + threaten: 1.7, + }, + }, + danshi_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "damageBegin1", + }, + forced: true, + locked: true, + filter(event, player) { + return event.num > 0; + }, + async content(event, trigger, player) { + const difficulty = getTianshuDifficulty(); + let num = 1; + + if (difficulty == "hard") { + num = 2; + } else if (difficulty == "nightmare") { + num = 3; + } + + trigger.num++; + await player.draw(num); + + if (player.countMark("jizhou_shuying") > 0) { + player.removeMark("jizhou_shuying", 1, false); + } + }, + ai: { + threaten: 0.8, + maixie: true, + effect: { + target(card, source, target) { + if (get.tag(card, "damage")) { + return [1, -1.3, 0, 0.4]; + } + }, + }, + }, + }, + chihu_shuying: { + mode: ["boss"], + audio: false, + forced: true, + locked: true, + group: [ + "chihu_shuying_draw", + "chihu_shuying_damage", + ], + subSkill: { + draw: { + audio: "chihu_shuying", + trigger: { + player: "phaseDrawBegin2", + }, + forced: true, + locked: true, + filter(event, player) { + return ( + getTianshuDifficulty() == "nightmare" && + !event.numFixed && + !player.isMaxHandcard() + ); + }, + async content(event, trigger, player) { + trigger.num += 4; + }, + }, + damage: { + audio: "chihu_shuying", + trigger: { + source: "damageBegin1", + }, + forced: true, + locked: true, + filter(event, player) { + return ( + getTianshuDifficulty() == "nightmare" && + event.num > 0 && + event.player?.isIn() && + player.getEnemies(null, false).includes(event.player) && + !player.isMaxHp() + ); + }, + async content(event, trigger, player) { + trigger.num++; + }, + }, + }, + ai: { + threaten: 1.8, + }, + }, + + //阎罗王 over + dianwei_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()); + }, + async content(event, trigger, player) { + let enemies = player + .getEnemies(null, false) + .filter(target => target.isIn()); + + const sha = { + name: "sha", + isCard: true, + }; + const shaTargets = enemies.filter(target => { + return ( + target.countCards("e") == 0 && + player.canUse(sha, target, false) + ); + }); + + if (shaTargets.length) { + await player.useCard(sha, shaTargets, false); + } + + enemies = player + .getEnemies(null, false) + .filter(target => { + return target.isIn() && target.countCards("e") > 0; + }); + + for (const target of enemies) { + player.line(target); + await target.randomDiscard("e", player); + } + }, + ai: { + threaten: 1.7, + }, + }, + xingpan_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1", + trigger: { + player: "phaseUseBegin", + }, + forced: true, + locked: 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 (!enemies.length) return; + + const result = await player.judge().forResult(); + + if (result.color == "red") { + const target = enemies.find(current => { + return enemies.every(other => { + return ( + other == current || + other.countCards("h") < current.countCards("h") + ); + }); + }); + if (!target) return; + + const num = Math.floor(target.countCards("h") / 2); + if (num <= 0) return; + + target.line(player); + await target + .chooseToGive( + player, + "h", + num, + true, + `刑判:将${get.cnNumber(num)}张手牌交给${get.translation(player)}` + ) + .set("ai", card => 7 - get.value(card)); + return; + } + + const target = enemies.find(current => { + return enemies.every(other => { + return ( + other == current || + other.getHp() < current.getHp() + ); + }); + }); + if (!target) return; + + player.line(target); + await target.loseHp(); + }, + ai: { + threaten: 1.6, + }, + }, + zhennu_shuying: { + mode: ["boss"], + audio: false, + trigger: { + player: "changeHp", + }, + forced: true, + locked: true, + mark: true, + filter(event, player) { + return ( + getTianshuDifficulty() != "normal" && + !player.storage.zhennu_shuying_used && + event.changedHp < 0 && + player.hp <= player.maxHp / 2 && + player.hp - event.changedHp > player.maxHp / 2 + ); + }, + async content(event, trigger, player) { + player.storage.zhennu_shuying_used = true; + player.markSkill("zhennu_shuying"); + + await player.draw(4); + player.insertPhase(); + }, + intro: { + content(storage, player) { + return player.storage.zhennu_shuying_used + ? "本局游戏已经发动" + : "本局游戏尚未发动"; + }, + }, + ai: { + threaten: 1.8, + }, + }, + xuanpan_shuying: { + mode: ["boss"], + audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:2", + trigger: { + global: "phaseAfter", + }, + filter(event, player) { + if ( + getTianshuDifficulty() != "nightmare" || + !event.player?.isIn() || + !player.getEnemies(null, false).includes(event.player) + ) { + return false; + } + + const target = event.player; + const damage = target + .getHistory("sourceDamage", evt => { + return evt.player == player; + }) + .reduce((sum, evt) => sum + evt.num, 0); + + const draw = target + .getHistory("gain", evt => { + return evt.getParent()?.name == "draw"; + }) + .reduce((sum, evt) => sum + evt.cards.length, 0); + + const recover = game + .getGlobalHistory("changeHp", evt => { + return ( + evt.player == target && + evt.getParent()?.name == "recover" + ); + }) + .reduce((sum, evt) => sum + evt.num, 0); + + const discard = player + .getHistory("lose", evt => { + return ( + evt.type == "discard" && + evt.position == ui.discardPile && + evt.getlx !== false + ); + }) + .reduce((sum, evt) => sum + (evt.cards2?.length || 0), 0); + + return ( + damage >= 4 || + draw >= 8 || + recover >= 3 || + discard >= 4 + ); + }, + async cost(event, trigger, player) { + const target = trigger.player; + const data = { + damage: + target + .getHistory("sourceDamage", evt => { + return evt.player == player; + }) + .reduce((sum, evt) => sum + evt.num, 0) >= 4, + draw: + target + .getHistory("gain", evt => { + return evt.getParent()?.name == "draw"; + }) + .reduce((sum, evt) => sum + evt.cards.length, 0) >= 8, + recover: + game + .getGlobalHistory("changeHp", evt => { + return ( + evt.player == target && + evt.getParent()?.name == "recover" + ); + }) + .reduce((sum, evt) => sum + evt.num, 0) >= 3, + discard: + player + .getHistory("lose", evt => { + return ( + evt.type == "discard" && + evt.position == ui.discardPile && + evt.getlx !== false + ); + }) + .reduce((sum, evt) => { + return sum + (evt.cards2?.length || 0); + }, 0) >= 4, + }; + + const list = []; + if (data.damage) list.push("对其造成1至3点伤害"); + if (data.draw) list.push("随机摸1至4张牌"); + if (data.recover) list.push("随机回复1至3点体力"); + if (data.discard) list.push(`令其随机弃置1至3张手牌`); + + const result = await player + .chooseBool( + get.prompt(event.skill, target), + `依次执行:${list.join(";")}` + ) + .set("ai", () => true) + .forResult(); + + if (result.bool) { + result.cost_data = data; + } + event.result = result; + }, + async content(event, trigger, player) { + const target = trigger.player; + const data = event.cost_data; + + if (data.damage && target.isIn()) { + player.line(target); + await target.damage(get.rand(1, 3), player); + } + + if (data.draw && player.isIn()) { + await player.draw(get.rand(1, 4)); + } + + if (data.recover && player.isIn() && player.isDamaged()) { + await player.recover(get.rand(1, 3)); + } + + if ( + data.discard && + target.isIn() && + target.countCards("h") > 0 + ) { + player.line(target); + await target.randomDiscard(get.rand(1, 3), "h"); + } + }, + ai: { + threaten: 1.5, + }, + }, }; export default skills; diff --git a/pve/boss/sets/zhuoguiquxie/sort.js b/pve/boss/sets/zhuoguiquxie/sort.js new file mode 100644 index 0000000..c25a2a6 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/sort.js @@ -0,0 +1,15 @@ +const characterSort = { + shuYing_pve_zhuoguiquxie: [ + "heibaiwuchang_boss_shuying", + "niutoumamian_boss_shuying", + "yvsai_boss_shuying", + "huangfeng_boss_shuying", + "riyeyoushen_boss_shuying", + ], +}; + +const characterSortTranslate = { + shuYing_pve_zhuoguiquxie: "捉鬼驱邪", +}; + +export { characterSort, characterSortTranslate }; diff --git a/pve/boss/sets/zhuoguiquxie/translate.js b/pve/boss/sets/zhuoguiquxie/translate.js index 9eb5409..489edc0 100644 --- a/pve/boss/sets/zhuoguiquxie/translate.js +++ b/pve/boss/sets/zhuoguiquxie/translate.js @@ -1,4 +1,24 @@ const translates = { + mengpo_boss_shuying: "孟婆", + aotang_shuying: "熬汤", + aotang_shuying_info: "锁定技,你的回合开始时,令随机一名敌方角色随机遗忘1个武将技能直到你的下回合开始。", + yunju_shuying: "云飓", + yunju_shuying_info: "锁定技,敌方角色的回合结束时,该角色随机弃置1张手牌。", + guimei_shuying: "鬼魅", + guimei_shuying_info: "锁定技,你不会被翻面。你每回合首次成为其他角色使用牌的唯一目标时,将该牌目标改为随机角色,若目标仍为你,此牌使用者失去1点体力;若目标不为你,你回复1点体力。", + + baowei_boss_shuying: "豹尾", + eli_shuying: "恶力", + eli_shuying_info: "锁定技,你每回合首次对敌方角色造成伤害时,你进行一次判定:若结果为红色,此伤害+1;若结果为黑色,你获得“完杀”直到回合结束", + yinsha_shuying: "隐煞", + yinsha_shuying_info: "锁定技,若其他角色的攻击范围内不包括你,其不能使用牌指定你为目标。且这些角色不能响应你使用的牌。", + + niaozui_boss_shuying: "鸟嘴", + suoxue_shuying: "索穴", + suoxue_shuying_info: "每回合限一次,你使用伤害牌指定单一目标后,若其手牌数大于你,你可将手牌摸至与该角色相同(至多5张);若其手牌数小于你,你可弃置一张手牌令此牌不能抵消。", + bingyi_shuying: "病疑", + bingyi_shuying_info: "锁定技,每回合首次失去最后的手牌时,你摸5张牌。", + heibaiwuchang_boss_shuying: "黑白无常", mizui_shuying: "迷醉", mizui_shuying_info: "锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的1张牌。", @@ -13,9 +33,11 @@ const translates = { shiyv_shuying: "施狱", shiyv_shuying_info: "锁定技,摸牌阶段,你放弃摸牌改为随机获得牌堆中每种花色的牌各一张。", manji_shuying: "蛮击", - manji_shuying_info: "你使用【杀】指定单一目标后,你可以弃置该角色2张手牌。若其中有【杀】,你本次【杀】造成的伤害+1。", + manji_shuying_info: "你使用【杀】指定单一目标后,你可以弃置该角色1张手牌。若此牌是【杀】,你本次【杀】的伤害+1。", xiaoshou_shuying: "枭首", - xiaoshou_shuying_info: "锁定技,准备阶段,你对随机一名敌方角色造成2点伤害", + xiaoshou_shuying_info: "锁定技,准备阶段,你对随机一名敌方角色造成2点伤害。", + guizhao_shuying: "诡招", + guizhao_shuying_info: "锁定技,当你于回合内使用一张牌时,若此牌的类别是你本回合第一次使用,则你摸一张牌。", yvsai_boss_shuying: "鱼鳃", anchao_shuying: "暗潮", @@ -39,7 +61,23 @@ const translates = { duane_shuying: "断恶", duane_shuying_info: "锁定技,敌方角色的弃牌阶段结束时,若其此阶段弃置了黑色牌,则该角色失去2点体力。", + guiwang_boss_shuying: "鬼王", + jizhou_shuying: "疾咒", + jizhou_shuying_info: "锁定技,敌方角色的出牌阶段开始时,你进行一次判定,然后其选择一项:1.弃置任意张点数之和大于判定结果的牌,你获得1个“噬”标记;2.失去1点体力。", + danshi_shuying: "啖噬", + danshi_shuying_info: "锁定技,你受到伤害时此伤害+1,然后你摸1张牌并移去1个“噬”。", + chihu_shuying: "赤虎", + chihu_shuying_info: "锁定技,若你的手牌数不是全场最多的,摸牌阶段你多摸4张牌;若你的体力值不是全场最多的,你造成的伤害+1。", + yanluowang_boss_shuying: "阎罗王", + dianwei_shuying: "殿威", + dianwei_shuying_info: "锁定技,准备阶段,你视为对装备区里没牌的敌方角色使用一张【杀】,然后装备区里有牌的敌方角色随机弃置一张装备区里的牌。", + xingpan_shuying: "刑判", + xingpan_shuying_info: "锁定技,出牌阶段开始时,你进行一次判定:若结果为红色,敌方唯一手牌最多的角色将一半数量的手牌交给你(向下取整);若结果为黑色,敌方唯一体力最多的角色失去1点体力。", + zhennu_shuying: "震怒", + zhennu_shuying_info: "锁定技,当你的体力值首次降至一半以下时,你立即开始你的回合并摸四张牌。", + xuanpan_shuying: "宣判", + xuanpan_shuying_info: "一名敌方角色的回合结束后,若其本回合对你造成了4点或更多伤害,你随机对其造成1- 3点伤害;若其本回合摸牌数达到8张或更多,你随机摸1-4张牌;若其本回合回复了3点或更多体力值,你随机回复1 - 3点体力;若你本回合因弃置而进入弃牌堆的牌达到4张或更多,其随机弃置1 - 3张手牌。", }; export default translates; diff --git a/pve/boss/sets/zhuoguiquxie/voices.js b/pve/boss/sets/zhuoguiquxie/voices.js new file mode 100644 index 0000000..97412c7 --- /dev/null +++ b/pve/boss/sets/zhuoguiquxie/voices.js @@ -0,0 +1,3 @@ +const voices = {}; + +export default voices; diff --git a/pve/boss/skill.js b/pve/boss/skill.js index 98dba2a..fa45429 100644 --- a/pve/boss/skill.js +++ b/pve/boss/skill.js @@ -1,15 +1,7 @@ -import shiershengxiao from "./sets/shiershengxiao/skill.js"; -import zhuoguiquxie from "./sets/zhuoguiquxie/skill.js"; -import qingqingzijin from "./sets/qingqingzijin/skill.js"; -import tianshu from "./sets/tianshu/skill.js"; -import mergeBossModules from "./merge.js"; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; // 汇总所有主题技能,确保跨 PVE 复用 BOSS 时技能始终随统一入口注册。 -const skills = mergeBossModules("技能", [ - ["十二生肖", shiershengxiao], - ["捉鬼驱邪", zhuoguiquxie], - ["青青子衿", qingqingzijin], - ["天书乱斗", tianshu], -]); +const skills = mergeBossSection("技能", bossSets, "skill"); export default skills; diff --git a/pve/boss/sort.js b/pve/boss/sort.js index 740f802..bef4d35 100644 --- a/pve/boss/sort.js +++ b/pve/boss/sort.js @@ -1,9 +1,7 @@ -const characterSort = { - shuYing_pve_tianshu: ["chenlong_shuying"], -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; -const characterSortTranslate = { - shuYing_pve_tianshu: "天书乱斗", -}; +const characterSort = mergeBossSection("武将分组", bossSets, "characterSort"); +const characterSortTranslate = mergeBossSection("武将分组翻译", bossSets, "characterSortTranslate"); export { characterSort, characterSortTranslate }; diff --git a/pve/boss/translate.js b/pve/boss/translate.js index 648e591..4ad87ae 100644 --- a/pve/boss/translate.js +++ b/pve/boss/translate.js @@ -1,15 +1,7 @@ -import shiershengxiao from "./sets/shiershengxiao/translate.js"; -import zhuoguiquxie from "./sets/zhuoguiquxie/translate.js"; -import qingqingzijin from "./sets/qingqingzijin/translate.js"; -import tianshu from "./sets/tianshu/translate.js"; -import mergeBossModules from "./merge.js"; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; // 汇总所有主题翻译,并阻止同名翻译在后加载模块中被覆盖。 -const translates = mergeBossModules("翻译", [ - ["十二生肖", shiershengxiao], - ["捉鬼驱邪", zhuoguiquxie], - ["青青子衿", qingqingzijin], - ["天书乱斗", tianshu], -]); +const translates = mergeBossSection("翻译", bossSets, "translate"); export default translates; diff --git a/pve/boss/voices.js b/pve/boss/voices.js index 9b345cd..d4498ee 100644 --- a/pve/boss/voices.js +++ b/pve/boss/voices.js @@ -1,4 +1,6 @@ -const voices = { -}; +import bossSets from "./sets.js"; +import { mergeBossSection } from "./merge.js"; + +const voices = mergeBossSection("语音台词", bossSets, "voices"); export default voices; diff --git a/pve/images/捉鬼驱邪/niaoshou_boss_shuying.png b/pve/images/捉鬼驱邪/niaozui_boss_shuying.png similarity index 100% rename from pve/images/捉鬼驱邪/niaoshou_boss_shuying.png rename to pve/images/捉鬼驱邪/niaozui_boss_shuying.png diff --git a/pve/images/捉鬼驱邪/yanluowang.png b/pve/images/捉鬼驱邪/yanluowang_boss_shuying.png similarity index 100% rename from pve/images/捉鬼驱邪/yanluowang.png rename to pve/images/捉鬼驱邪/yanluowang_boss_shuying.png diff --git a/tianshu/config.js b/tianshu/config.js index 4412bc7..30d3e34 100644 --- a/tianshu/config.js +++ b/tianshu/config.js @@ -1,210 +1,125 @@ -const bossList = { - // stage1: ["caocao_boss_shuying", "dongzhuo_boss_shuying"], - stage1: ["zishu_boss_shuying", "wuma_boss_shuying", "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", "dongzhuo_boss_shuying"], - stage4: ["shuishengonggong_boss_shuying", "shaohao_boss_shuying", "xuannv_boss_shuying", "hanba_boss_shuying"], +const difficultyOrder = [normal, hard, nightmare]; + +const stages = [ + { + id: stage1, + name: 第一关, + // bosses: [caocao_boss_shuying, dongzhuo_boss_shuying], + bosses: [zishu_boss_shuying, chouniu_boss_shuying, yinhu_boss_shuying, maotu_boss_shuying, chenlong_boss_shuying, sishe_boss_shuying, wuma_boss_shuying, weiyang_boss_shuying, shenhou_boss_shuying, youji_boss_shuying, xvgou_boss_shuying, haizhu_boss_shuying], + }, + { + id: stage2, + name: 第二关, + bosses: [mengpo_boss_shuying, baowei_boss_shuying, niaozui_boss_shuying, heibaiwuchang_boss_shuying, niutoumamian_boss_shuying, yvsai_boss_shuying, huangfeng_boss_shuying, riyeyoushen_boss_shuying, guiwang_boss_shuying, yanluowang_boss_shuying], + }, + { + id: stage3, + name: 第三关, + bosses: [caocao_boss_shuying, simayi_boss_shuying, lvbu_boss_shuying, dongzhuo_boss_shuying], + }, + { + id: stage4, + name: 第四关, + bosses: [shuishengonggong_boss_shuying, shaohao_boss_shuying, xuannv_boss_shuying, hanba_boss_shuying], + }, +]; + +const bossList = Object.fromEntries(stages.map(stage => [stage.id, stage.bosses])); + +const virtualIdols = { + Xiaotao: vtb_xiaotao, + Xiaosha: vtb_xiaosha, + Xiaoshan: vtb_xiaoshan, + Xiaole: vtb_xiaole, + Xiaojiu: vtb_xiaojiu, }; +const virtualIdolList = Object.values(virtualIdols); -const virtualIdolList = ["vtb_xiaotao", "vtb_xiaosha", "vtb_xiaoshan", "vtb_xiaole", "vtb_xiaojiu"]; - -const difficultyOrder = ["normal", "hard", "nightmare"]; +// Boss 的体力与体力上限始终使用相同加成,只需在配置中填写一次。 +const createDifficultyConfig = (startCards, hpBonus = 0) => ({ + startCards, + hpBonus, + maxHpBonus: hpBonus, +}); const bossDifficultyTemplates = { - shiershengxiao: - { - normal: { - startCards: 4, - }, - hard: { - startCards: 6, - hpBonus: 1, - maxHpBonus: 1, - }, - nightmare: { - startCards: 8, - hpBonus: 4, - maxHpBonus: 4, - }, + shiershengxiao: { + normal: createDifficultyConfig(4), + hard: createDifficultyConfig(6, 1), + nightmare: createDifficultyConfig(8, 4), }, - zhuoguiquxie: { - normal: { - startCards: 4, - hpBonus: 2, - maxHpBonus: 2, - }, - hard: { - startCards: 6, - hpBonus: 4, - maxHpBonus: 4, - }, - nightmare: { - startCards: 8, - hpBonus: 8, - maxHpBonus: 8, - }, + normal: createDifficultyConfig(4, 2), + hard: createDifficultyConfig(6, 4), + nightmare: createDifficultyConfig(8, 8), }, - qingqingzijin: { - normal: { - startCards: 6, - hpBonus: 6, - maxHpBonus: 6, - }, - hard: { - startCards: 8, - hpBonus: 16, - maxHpBonus: 16, - }, - nightmare: { - startCards: 10, - hpBonus: 26, - maxHpBonus: 26, - }, + normal: createDifficultyConfig(6, 6), + hard: createDifficultyConfig(8, 16), + nightmare: createDifficultyConfig(10, 26), }, - - tianshuluandou: - { - normal: { - startCards: 6, - hpBonus: 6, - maxHpBonus: 6, - }, - hard: { - startCards: 8, - hpBonus: 11, - maxHpBonus: 11, - }, - nightmare: { - startCards: 12, - hpBonus: 21, - maxHpBonus: 21, - }, + tianshuluandou: { + normal: createDifficultyConfig(6, 6), + hard: createDifficultyConfig(8, 11), + nightmare: createDifficultyConfig(12, 21), }, }; +// 为单个 Boss 复制模板,并按难度覆盖其补充技能。 +const createBossConfig = (template, skills = {}) => ({ + normal: { ...template.normal, ...(skills.normal?.length ? { skills: skills.normal } : {}) }, + hard: { ...template.hard, ...(skills.hard?.length ? { skills: skills.hard } : {}) }, + nightmare: { ...template.nightmare, ...(skills.nightmare?.length ? { skills: skills.nightmare } : {}) }, +}); + +// 同组 Boss 使用相同模板,但生成彼此独立的配置对象。 +const createBossGroup = (bosses, template) => Object.fromEntries( + bosses.map(boss => [boss, createBossConfig(template)]) +); + +const addSkillFromHard = skill => ({ hard: [skill], nightmare: [skill] }); + 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: [], - }, + normal: createDifficultyConfig(4), + hard: createDifficultyConfig(5, 2), + nightmare: createDifficultyConfig(6, 4), }, bosses: { - chenlong_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, }, - weiyang_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, }, - shenhou_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, }, - xvgou_boss_shuying: { ...bossDifficultyTemplates.shiershengxiao, }, + ...createBossGroup(stages[0].bosses, bossDifficultyTemplates.shiershengxiao), - heibaiwuchang_boss_shuying: { - ...bossDifficultyTemplates.zhuoguiquxie, - hard: { - ...bossDifficultyTemplates.zhuoguiquxie.hard, - skills: ["xixing_shuying"], - }, - nightmare: { - ...bossDifficultyTemplates.zhuoguiquxie.nightmare, - skills: ["xixing_shuying", "taiping_shuying"], - }, - }, - yvsai_boss_shuying: { - ...bossDifficultyTemplates.zhuoguiquxie, - }, - huangfeng_boss_shuying: - { - ...bossDifficultyTemplates.zhuoguiquxie, - }, - riyeyoushen_boss_shuying: { - ...bossDifficultyTemplates.zhuoguiquxie, - nightmare: { - ...bossDifficultyTemplates.zhuoguiquxie.nightmare, - skills: ["duane_shuying"], - }, - }, + ...createBossGroup([yvsai_boss_shuying, huangfeng_boss_shuying], bossDifficultyTemplates.zhuoguiquxie), + mengpo_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + nightmare: [guimei_shuying], + }), + baowei_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, addSkillFromHard(yinsha_shuying)), + niaozui_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, addSkillFromHard(bingyi_shuying)), + heibaiwuchang_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + hard: [xixing_shuying], + nightmare: [xixing_shuying, taiping_shuying], + }), + riyeyoushen_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + nightmare: [duane_shuying], + }), + niutoumamian_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + hard: [xiaoshou_shuying], + nightmare: [xiaoshou_shuying, guizhao_shuying], + }), + guiwang_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + nightmare: [chihu_shuying], + }), + yanluowang_boss_shuying: createBossConfig(bossDifficultyTemplates.zhuoguiquxie, { + hard: [zhennu_shuying], + nightmare: [zhennu_shuying, xuanpan_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"], - }, - }, - dongzhuo_boss_shuying: { - ...bossDifficultyTemplates.qingqingzijin, - hard: { - ...bossDifficultyTemplates.qingqingzijin.hard, - skills: ["qubu_shuying"], - }, - nightmare: { - ...bossDifficultyTemplates.qingqingzijin.nightmare, - skills: ["qubu_shuying"], - }, - }, - - shuishengonggong_boss_shuying: { - ...bossDifficultyTemplates.tianshuluandou, - hard: { - ...bossDifficultyTemplates.tianshuluandou.hard, - skills: ["shuishen_shuying"], - }, - nightmare: { - ...bossDifficultyTemplates.tianshuluandou.nightmare, - skills: ["shuishen_shuying"], - }, - }, - shaohao_boss_shuying: { ...bossDifficultyTemplates.tianshuluandou, }, - xuannv_boss_shuying: { ...bossDifficultyTemplates.tianshuluandou, }, - hanba_boss_shuying: { ...bossDifficultyTemplates.tianshuluandou, }, + caocao_boss_shuying: createBossConfig(bossDifficultyTemplates.qingqingzijin, addSkillFromHard(yishen_shuying)), + simayi_boss_shuying: createBossConfig(bossDifficultyTemplates.qingqingzijin, addSkillFromHard(yuanlv_shuying)), + lvbu_boss_shuying: createBossConfig(bossDifficultyTemplates.qingqingzijin, addSkillFromHard(zhanjia_shuying)), + dongzhuo_boss_shuying: createBossConfig(bossDifficultyTemplates.qingqingzijin, addSkillFromHard(qubu_shuying)), + ...createBossGroup([shaohao_boss_shuying, xuannv_boss_shuying, hanba_boss_shuying], bossDifficultyTemplates.tianshuluandou), + shuishengonggong_boss_shuying: createBossConfig(bossDifficultyTemplates.tianshuluandou, addSkillFromHard(shuishen_shuying)), baiqi_boss_shuying: {}, pangu_boss_shuying: {}, @@ -213,37 +128,16 @@ const bossDifficulty = { 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, - }, - ], + stages, difficulties: { normal: { - name: "普通", + name: 普通, }, hard: { - name: "困难", + name: 困难, }, nightmare: { - name: "阴间", + name: 阴间, }, }, difficultyOrder, @@ -251,11 +145,7 @@ const tianshuConfig = { virtualIdolList, virtualIdols: { random: virtualIdolList, - Xiaojiu: "vtb_xiaojiu", - Xiaosha: "vtb_xiaosha", - Xiaoshan: "vtb_xiaoshan", - Xiaole: "vtb_xiaole", - Xiaotao: "vtb_xiaotao", + ...virtualIdols, }, reward: { killRecover: 1, @@ -265,32 +155,32 @@ const tianshuConfig = { 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", + 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"], + banned: [huoxin, jueqing, qinqing, beige, huashen, drlt_zhiti, olzhiti, xinfu_pdgyingshi, rebeige], }, keywordBuffs: [ { - keyword: "杀", + keyword: 杀, levels: [ { count: 2, damageBonus: 1 }, { count: 3, damageBonus: 2 }, ], }, { - keyword: "闪", + keyword: 闪, levels: [ { count: 2, drawAfterShan: 1 }, ], }, { - keyword: "桃", + keyword: 桃, levels: [ { count: 2, recoverBonus: 1 }, ], diff --git a/tianshu/extension.js b/tianshu/extension.js index ae674df..8707320 100644 --- a/tianshu/extension.js +++ b/tianshu/extension.js @@ -721,20 +721,23 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { }; 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_boss_shuYing: { + sex: "male", + group: "", + hp: 0, + skills: [ + "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", + ], + isBoss: true, + extraModeData: "qun", + img: "extension/术樱包/pve/images/tianshu.jpg", + }, }; tianshu.game = {}; tianshu.boss = { @@ -869,5 +872,5 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { tianshu.characterTitle = {}; tianshu.characterReplace = {}; - shuYing.appendExtension("shuYing_pve_tianshu", "天书乱斗", tianshu); + return tianshu; } diff --git a/update.js b/update.js index 5898a56..7ca45ab 100644 --- a/update.js +++ b/update.js @@ -188,6 +188,9 @@ async function getManifest() { throw new Error("校验清单格式错误"); } + getManifestRemoveFiles(manifest); + getManifestRemoveDirectories(manifest); + return manifest; } @@ -241,7 +244,46 @@ function getManifestAssetFiles(manifest, coreFiles) { } function getManifestRemoveFiles(manifest) { - return uniqueManifestList(manifest?.remove || []); + const files = uniqueManifestList(manifest?.remove || []); + const unsafe = files.filter(file => !isSafeManifestPath(file)); + + if (unsafe.length) { + throw new Error(`校验清单 remove 包含不安全路径:${unsafe.join("、")}`); + } + + const conflicts = files.filter(file => manifest?.files?.[file]); + if (conflicts.length) { + throw new Error(`待删除文件仍存在于新版清单:${conflicts.join("、")}`); + } + + return files; +} + +function isSafeManifestPath(path) { + const parts = normalizeManifestPath(path).split("/"); + return parts.length > 0 + && parts.every(part => part && part != "." && part != "..") + && !parts.some(part => part.includes(":")); +} + +function getManifestRemoveDirectories(manifest) { + const directories = uniqueManifestList(manifest?.removeDirectories || []); + const unsafe = directories.filter(directory => !isSafeManifestPath(directory)); + + if (unsafe.length) { + throw new Error(`校验清单 removeDirectories 包含不安全路径:${unsafe.join("、")}`); + } + + const manifestFiles = getManifestFileKeys(manifest); + const conflicts = directories.filter(directory => + manifestFiles.some(file => file == directory || file.startsWith(`${directory}/`)) + ); + + if (conflicts.length) { + throw new Error(`待删除目录仍包含新版文件:${conflicts.join("、")}`); + } + + return directories.sort((a, b) => b.split("/").length - a.split("/").length || b.length - a.length); } function getManifestDownloadFiles(manifest) { @@ -330,6 +372,20 @@ function removeFile(path) { }); } +function removeDir(path) { + return new Promise(resolve => { + if (typeof game.removeDir != "function") { + resolve({ removed: false, error: new Error("当前环境不支持删除文件夹") }); + return; + } + game.removeDir( + path, + () => resolve({ removed: true }), + error => resolve({ removed: false, error }) + ); + }); +} + function createDir(path) { return new Promise((resolve, reject) => { game.createDir(path, resolve, reject); @@ -353,6 +409,28 @@ async function removeManifestFiles(shuYing, manifest) { } } +async function removeManifestDirectories(shuYing, manifest) { + const directories = getManifestRemoveDirectories(manifest); + + for (const directory of directories) { + const target = `extension/术樱包/${directory}`; + await addLog(shuYing, `删除废弃目录:${directory}`); + const result = await removeDir(target); + + if (result.removed) { + await addLog(shuYing, `废弃目录已删除:${directory}`); + } + else { + await addLog(shuYing, `废弃目录不存在或删除失败,已跳过:${directory};${getErrorMessage(result.error)}`); + } + } +} + +async function removeManifestOldPaths(shuYing, manifest) { + await removeManifestFiles(shuYing, manifest); + await removeManifestDirectories(shuYing, manifest); +} + async function downloadCoreFiles(shuYing, manifest) { if (typeof game.readFile != "function" || typeof game.writeFile != "function") { throw new Error("当前环境不支持安全替换核心文件"); @@ -396,7 +474,7 @@ async function downloadCoreFiles(shuYing, manifest) { await writeTargetFile(fileData[file], `extension/术樱包/${file}`); progress++; } - await removeManifestFiles(shuYing, manifest); + await removeManifestOldPaths(shuYing, manifest); setProgress(shuYing, { title: "核心文件修复", status: "完成", current: coreFiles.length * 2, total: coreFiles.length * 2 }); } catch (error) { @@ -524,7 +602,7 @@ async function repairMissingFiles(shuYing) { await createFolders("extension/术樱包/", files, shuYing); await downloadList(shuYing, files, manifest); - await removeManifestFiles(shuYing, manifest); + await removeManifestOldPaths(shuYing, manifest); setProgress(shuYing, { title: "查漏补缺", status: "完成", current: files.length, total: files.length }); await addLog(shuYing, "查漏补缺完成");