🎨 PVE文件夹重构,根据BOSS文件进行分包
📝 新增术樱包武将
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
const characters = {
|
||||
zuoci_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 3,
|
||||
skills: ["huashen_shuying", "xinsheng_shuying", "jihun_shuying"],
|
||||
img: "extension/术樱包/character/images/zuoci_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/character/audio/zuoci/zuoci_shuying.mp3"]
|
||||
},
|
||||
};
|
||||
|
||||
export default characters;
|
||||
@@ -0,0 +1,3 @@
|
||||
const characterFilters = {};
|
||||
|
||||
export default characterFilters;
|
||||
@@ -0,0 +1,3 @@
|
||||
const characterTitles = {};
|
||||
|
||||
export default characterTitles;
|
||||
@@ -0,0 +1,3 @@
|
||||
const dynamicTranslates = {};
|
||||
|
||||
export default dynamicTranslates;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
@@ -0,0 +1,28 @@
|
||||
import characters from "./character.js";
|
||||
import skills from "./skill.js";
|
||||
import translates from "./translate.js";
|
||||
import characterFilters from "./characterFilter.js";
|
||||
import characterTitles from "./characterTitle.js";
|
||||
import characterIntros from "./intro.js";
|
||||
import dynamicTranslates from "./dynamicTranslate.js";
|
||||
import perfectPairs from "./perfectPairs.js";
|
||||
import voices from "./voices.js";
|
||||
import { characterSort, characterSortTranslate } from "./sort.js";
|
||||
|
||||
const shuying_character = {
|
||||
name: "shuYing_character",
|
||||
connect: true,
|
||||
character: { ...characters },
|
||||
characterSort: {
|
||||
shuYing_character: characterSort,
|
||||
},
|
||||
characterFilter: { ...characterFilters },
|
||||
characterTitle: { ...characterTitles },
|
||||
dynamicTranslate: { ...dynamicTranslates },
|
||||
characterIntro: { ...characterIntros },
|
||||
skill: { ...skills },
|
||||
perfectPair: { ...perfectPairs },
|
||||
translate: { ...translates, ...voices, ...characterSortTranslate },
|
||||
};
|
||||
|
||||
export default shuying_character;
|
||||
@@ -0,0 +1,3 @@
|
||||
const characterIntros = {};
|
||||
|
||||
export default characterIntros;
|
||||
@@ -0,0 +1,3 @@
|
||||
const perfectPairs = {};
|
||||
|
||||
export default perfectPairs;
|
||||
@@ -0,0 +1,388 @@
|
||||
import { lib, game, ui, get, ai, _status } from "../../../noname.js";
|
||||
|
||||
const skills = {
|
||||
huashen_shuying: {
|
||||
audio: "ext:术樱包/character/audio/zuoci:2",
|
||||
inherit: "rehuashen",
|
||||
unique: true,
|
||||
init(player, skill) {
|
||||
if (!player.storage.rehuashen) {
|
||||
player.storage.rehuashen = {
|
||||
character: [],
|
||||
choosed: [],
|
||||
map: {},
|
||||
};
|
||||
}
|
||||
player.storage[skill] = player.storage.rehuashen;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const oldCharacter = player.storage.rehuashen?.current;
|
||||
const oldSkill = player.storage.rehuashen?.current2;
|
||||
const transitionKey = "jihun_shuying_transition";
|
||||
|
||||
// 单独处理重铸,保留本次实际移除的化身牌信息
|
||||
if (event.cost_data === "制衡其他化身") {
|
||||
_status.noclearcountdown = true;
|
||||
const id = lib.status.videoId++;
|
||||
const prompt = "化身:选择制衡至多两张武将牌";
|
||||
const cards = player.storage.rehuashen.character;
|
||||
|
||||
if (player.isOnline2()) {
|
||||
player.send(
|
||||
(cards, prompt, id) => {
|
||||
const dialog = ui.create.dialog(
|
||||
prompt,
|
||||
[cards, lib.skill.rehuashen.$createButton]
|
||||
);
|
||||
dialog.videoId = id;
|
||||
},
|
||||
cards,
|
||||
prompt,
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
const dialog = ui.create.dialog(
|
||||
prompt,
|
||||
[cards, lib.skill.rehuashen.$createButton]
|
||||
);
|
||||
dialog.videoId = id;
|
||||
if (!event.isMine()) {
|
||||
dialog.style.display = "none";
|
||||
}
|
||||
|
||||
const result = await player
|
||||
.chooseButton(true)
|
||||
.set("dialog", id)
|
||||
.set("selectButton", [1, 2])
|
||||
.set(
|
||||
"filterButton",
|
||||
button => button.link !== get.event().current
|
||||
)
|
||||
.set("current", player.storage.rehuashen.current)
|
||||
.forResult();
|
||||
|
||||
if (player.isOnline2()) {
|
||||
player.send("closeDialog", id);
|
||||
}
|
||||
dialog.close();
|
||||
delete _status.noclearcountdown;
|
||||
if (!_status.noclearcountdown) {
|
||||
game.stopCountChoose();
|
||||
}
|
||||
|
||||
const removedCharacters = result.links || [];
|
||||
lib.skill.rehuashen.removeHuashen(
|
||||
player,
|
||||
removedCharacters
|
||||
);
|
||||
|
||||
if (
|
||||
player.storage.jihun_shuying_retainedCharacter &&
|
||||
removedCharacters.includes(
|
||||
player.storage.jihun_shuying_retainedCharacter
|
||||
)
|
||||
) {
|
||||
delete player.storage.jihun_shuying_retained;
|
||||
delete player.storage.jihun_shuying_retainedCharacter;
|
||||
delete player.storage.jihun_shuying_ignoredEvents;
|
||||
await player.removeAdditionalSkills(
|
||||
"jihun_shuying_retained"
|
||||
);
|
||||
}
|
||||
|
||||
lib.skill.rehuashen.addHuashens(
|
||||
player,
|
||||
removedCharacters.length
|
||||
);
|
||||
player.storage.huashen_shuying =
|
||||
player.storage.rehuashen;
|
||||
player.syncStorage("huashen_shuying");
|
||||
player.updateMarks("huashen_shuying");
|
||||
return;
|
||||
}
|
||||
|
||||
// 切换前为当前技能增加临时所有权。官方化身移除旧来源时,
|
||||
// getRemovableAdditionalSkills 会因此保留技能本体及全部状态。
|
||||
const protectOldSkill = Boolean(
|
||||
oldSkill && player.hasSkill("jihun_shuying")
|
||||
);
|
||||
if (
|
||||
protectOldSkill &&
|
||||
!player.additionalSkills[transitionKey]?.includes(oldSkill)
|
||||
) {
|
||||
if (!Array.isArray(player.additionalSkills[transitionKey])) {
|
||||
player.additionalSkills[transitionKey] = [];
|
||||
}
|
||||
player.additionalSkills[transitionKey].push(oldSkill);
|
||||
game.broadcast(
|
||||
(player, map) => {
|
||||
player.additionalSkills = map;
|
||||
},
|
||||
player,
|
||||
player.additionalSkills
|
||||
);
|
||||
_status.event.clearStepCache();
|
||||
}
|
||||
|
||||
await lib.skill.rehuashen.content(event, trigger, player);
|
||||
|
||||
const storage = player.storage.rehuashen;
|
||||
const changed = Boolean(
|
||||
oldSkill &&
|
||||
(
|
||||
oldCharacter !== storage?.current ||
|
||||
oldSkill !== storage?.current2
|
||||
)
|
||||
);
|
||||
|
||||
if (
|
||||
changed &&
|
||||
player.hasSkill("jihun_shuying")
|
||||
) {
|
||||
// 过渡来源会阻止官方流程物理移除旧技能,因此这里仅删除
|
||||
// rehuashen 对旧技能的映射,不触发 removeSkill/onremove。
|
||||
if (
|
||||
oldSkill !== storage.current2 &&
|
||||
player.additionalSkills.rehuashen?.includes(oldSkill)
|
||||
) {
|
||||
player.$removeAdditionalSkills("rehuashen", oldSkill);
|
||||
_status.event.clearStepCache();
|
||||
}
|
||||
|
||||
// 新的当前技能已经登记完毕后,再结束上一次羁魂保留。
|
||||
if (player.additionalSkills.jihun_shuying_retained) {
|
||||
await player.removeAdditionalSkills(
|
||||
"jihun_shuying_retained"
|
||||
);
|
||||
}
|
||||
delete player.storage.jihun_shuying_retained;
|
||||
delete player.storage.jihun_shuying_retainedCharacter;
|
||||
delete player.storage.jihun_shuying_ignoredEvents;
|
||||
|
||||
player.logSkill("jihun_shuying");
|
||||
|
||||
if (oldSkill && oldSkill !== storage.current2) {
|
||||
const ignoredEvents = player
|
||||
.getAllHistory("useSkill", history => {
|
||||
return get.sourceSkillFor(history) === oldSkill;
|
||||
})
|
||||
.map(history => history.event)
|
||||
.filter(Boolean);
|
||||
|
||||
let currentEvent = _status.event;
|
||||
while (currentEvent) {
|
||||
const eventSkill = currentEvent.sourceSkill ||
|
||||
currentEvent.skill ||
|
||||
(lib.skill[currentEvent.name] ? currentEvent.name : null);
|
||||
if (
|
||||
eventSkill &&
|
||||
get.sourceSkillFor(eventSkill) === oldSkill &&
|
||||
!ignoredEvents.includes(currentEvent)
|
||||
) {
|
||||
ignoredEvents.push(currentEvent);
|
||||
}
|
||||
currentEvent = currentEvent.parent;
|
||||
}
|
||||
|
||||
player.storage.jihun_shuying_retained = oldSkill;
|
||||
player.storage.jihun_shuying_retainedCharacter =
|
||||
oldCharacter;
|
||||
player.storage.jihun_shuying_ignoredEvents = ignoredEvents;
|
||||
// 此时 oldSkill 仍由过渡键持有,增加羁魂来源不会重新
|
||||
// 初始化技能,也不会影响牌、标记和 storage。
|
||||
const retainedKey = "jihun_shuying_retained";
|
||||
if (!Array.isArray(player.additionalSkills[retainedKey])) {
|
||||
player.additionalSkills[retainedKey] = [];
|
||||
}
|
||||
if (!player.additionalSkills[retainedKey].includes(oldSkill)) {
|
||||
player.additionalSkills[retainedKey].push(oldSkill);
|
||||
}
|
||||
game.broadcast(
|
||||
(player, map) => {
|
||||
player.additionalSkills = map;
|
||||
},
|
||||
player,
|
||||
player.additionalSkills
|
||||
);
|
||||
_status.event.clearStepCache();
|
||||
}
|
||||
}
|
||||
|
||||
// 最终所有权建立完毕后再撤销过渡来源。
|
||||
if (player.additionalSkills[transitionKey]) {
|
||||
await player.removeAdditionalSkills(transitionKey);
|
||||
}
|
||||
|
||||
player.storage.huashen_shuying = storage;
|
||||
player.syncStorage("huashen_shuying");
|
||||
player.updateMarks("huashen_shuying");
|
||||
},
|
||||
onremove(player) {
|
||||
player.removeAdditionalSkill("rehuashen");
|
||||
player.removeAdditionalSkill("jihun_shuying_retained");
|
||||
player.removeAdditionalSkill("jihun_shuying_transition");
|
||||
delete player.storage.rehuashen;
|
||||
delete player.storage.huashen_shuying;
|
||||
delete player.storage.jihun_shuying_retained;
|
||||
delete player.storage.jihun_shuying_retainedCharacter;
|
||||
delete player.storage.jihun_shuying_ignoredEvents;
|
||||
},
|
||||
ai: {
|
||||
threaten: 2,
|
||||
},
|
||||
},
|
||||
|
||||
xinsheng_shuying: {
|
||||
audio: "ext:术樱包/character/audio/zuoci:2",
|
||||
unique: true,
|
||||
trigger: {
|
||||
player: "damageEnd",
|
||||
},
|
||||
filter(event, player) {
|
||||
return event.num > 0 &&
|
||||
player.hasSkill("huashen_shuying") &&
|
||||
player.storage.rehuashen;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
const controls = ["获得一张新化身"];
|
||||
if (player.storage.rehuashen.character?.length) {
|
||||
controls.push("更换所展示的化身");
|
||||
}
|
||||
controls.push("cancel2");
|
||||
|
||||
const result = await player
|
||||
.chooseControl(controls)
|
||||
.set("prompt", get.prompt(event.skill))
|
||||
.set("prompt2", "获得一张新化身牌,或更换所展示的化身牌")
|
||||
.set("ai", () => {
|
||||
const player = get.player();
|
||||
const controls = get.event().controls;
|
||||
if (!controls.includes("更换所展示的化身")) {
|
||||
return "获得一张新化身";
|
||||
}
|
||||
|
||||
const storage = player.storage.rehuashen;
|
||||
const currentRank = storage.current2 ?
|
||||
get.skillRank(storage.current2, "in") : 0;
|
||||
const skills = storage.character
|
||||
.flatMap(name => storage.map[name] || [])
|
||||
.unique();
|
||||
const bestRank = skills.reduce((max, skill) => {
|
||||
return Math.max(max, get.skillRank(skill, "in"));
|
||||
}, 0);
|
||||
|
||||
if (bestRank > currentRank + 1) {
|
||||
return "更换所展示的化身";
|
||||
}
|
||||
return "获得一张新化身";
|
||||
})
|
||||
.forResult();
|
||||
|
||||
event.result = {
|
||||
bool: result.control !== "cancel2",
|
||||
cost_data: result.control,
|
||||
};
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
if (event.cost_data === "获得一张新化身") {
|
||||
lib.skill.rehuashen.addHuashens(player, 1);
|
||||
player.storage.huashen_shuying = player.storage.rehuashen;
|
||||
player.syncStorage("huashen_shuying");
|
||||
player.updateMarks("huashen_shuying");
|
||||
return;
|
||||
}
|
||||
|
||||
const next = game.createEvent("huashen_shuying_change");
|
||||
next.player = player;
|
||||
next.skill = "huashen_shuying";
|
||||
next.cost_data = "替换当前化身";
|
||||
next.setContent(lib.skill.huashen_shuying.content);
|
||||
await next;
|
||||
},
|
||||
ai: {
|
||||
combo: "huashen_shuying",
|
||||
maixie: true,
|
||||
maixie_hp: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (
|
||||
get.tag(card, "damage") &&
|
||||
target.hasSkill("huashen_shuying")
|
||||
) {
|
||||
return [1, 0.7];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
jihun_shuying: {
|
||||
audio: "ext:术樱包/character/audio/zuoci:2",
|
||||
locked: true,
|
||||
group: "jihun_shuying_clear",
|
||||
onremove(player) {
|
||||
player.removeAdditionalSkill("jihun_shuying_retained");
|
||||
player.removeAdditionalSkill("jihun_shuying_transition");
|
||||
delete player.storage.jihun_shuying_retained;
|
||||
delete player.storage.jihun_shuying_retainedCharacter;
|
||||
delete player.storage.jihun_shuying_ignoredEvents;
|
||||
},
|
||||
ai: {
|
||||
combo: "huashen_shuying",
|
||||
},
|
||||
subSkill: {
|
||||
clear: {
|
||||
charlotte: true,
|
||||
silent: true,
|
||||
lastDo: true,
|
||||
priority: -Infinity,
|
||||
trigger: {
|
||||
player: [
|
||||
"useSkillAfter",
|
||||
"useCardAfter",
|
||||
"respondAfter",
|
||||
"triggerAfter",
|
||||
"skillAfter",
|
||||
],
|
||||
},
|
||||
hookTrigger: {
|
||||
after(event, player) {
|
||||
const retained =
|
||||
player.storage.jihun_shuying_retained;
|
||||
return Boolean(
|
||||
retained &&
|
||||
event.skill &&
|
||||
get.sourceSkillFor(event.skill) === retained
|
||||
);
|
||||
},
|
||||
},
|
||||
filter(event, player) {
|
||||
const retained =
|
||||
player.storage.jihun_shuying_retained;
|
||||
if (
|
||||
!retained ||
|
||||
!event.skill ||
|
||||
get.sourceSkillFor(event.skill) !== retained
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ignoredEvents =
|
||||
player.storage.jihun_shuying_ignoredEvents || [];
|
||||
return !ignoredEvents.includes(event);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
delete player.storage.jihun_shuying_retained;
|
||||
delete player.storage.jihun_shuying_retainedCharacter;
|
||||
delete player.storage.jihun_shuying_ignoredEvents;
|
||||
await player.removeAdditionalSkills(
|
||||
"jihun_shuying_retained"
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default skills;
|
||||
@@ -0,0 +1,4 @@
|
||||
const characterSort = {};
|
||||
const characterSortTranslate = {};
|
||||
|
||||
export { characterSort, characterSortTranslate };
|
||||
@@ -0,0 +1,11 @@
|
||||
const translates = {
|
||||
zuoci_shuying: "术左慈",
|
||||
huashen_shuying: "化身",
|
||||
huashen_shuying_info: "游戏开始时,你随机获得三张未加入游戏的武将牌,选一张置于你面前并声明该武将的一项技能,你拥有该技能且同时将性别和势力属性变为与该武将相同,直到该化身被替换。回合开始时或结束时,你可以选择一项:①弃置至多两张未展示的化身并重新获得等量化身;②更换所展示的化身牌或技能。(你不可以声明限定技、觉醒技、隐匿技、使命技、主公技等特殊技能)",
|
||||
xinsheng_shuying: "新生",
|
||||
xinsheng_shuying_info: "若你拥有技能〖化身〗,当你受到伤害后,你可以选择一项:①获得一张新化身牌;②更换所展示的化身牌。",
|
||||
jihun_shuying: "羁魂",
|
||||
jihun_shuying_info: "若你拥有技能〖化身〗,当你更换所展示的化身牌或技能时,保留上次声明的技能,直至该技能下次触发、再次更换所展示的化身牌或技能、重铸该技能对应的化身牌。",
|
||||
};
|
||||
|
||||
export default translates;
|
||||
@@ -0,0 +1,3 @@
|
||||
const voices = {};
|
||||
|
||||
export default voices;
|
||||
+7
-6
@@ -223,13 +223,10 @@ shuYing.initCharacter = async () => {
|
||||
// "yuanshen",
|
||||
// ];
|
||||
}
|
||||
else {
|
||||
// initBengTie(lib, game, ui, get, ai, _status, shuYing);
|
||||
|
||||
// shuYingList =
|
||||
// [
|
||||
// "yuanshen",
|
||||
// ];
|
||||
const shuying_character = await shuYing.loadModule("./character/index.js", "术樱包", true);
|
||||
if (shuying_character) {
|
||||
shuYing.appendExtension("shuYing_character", "术樱包", shuying_character);
|
||||
}
|
||||
|
||||
// shuYingList.forEach(url => {
|
||||
@@ -247,6 +244,10 @@ shuYing.precontent = async () => {
|
||||
shuYing.initCSS();
|
||||
shuYing.initFunction();
|
||||
shuYing.initConfig();
|
||||
// const initMengsan = await shuYing.loadModule("./mengsan/mode.js", "梦三模式", true);
|
||||
// if (typeof initMengsan == "function") {
|
||||
// await initMengsan();
|
||||
// }
|
||||
initShuYingMenu({ lib, game, ui, shuYing, updater });
|
||||
shuYing.getVersion();
|
||||
await shuYing.initCharacter();
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
const mengsanConfig = {
|
||||
modeId: "mengsan_shuying",
|
||||
saveKey: "mengsan_run_shuying",
|
||||
profileKey: "mengsan_profile_shuying",
|
||||
schemaVersion: 1,
|
||||
mapLayoutVersion: 3,
|
||||
routeCount: 4,
|
||||
characters: ["re_zhaoyun", "sunquan", "re_huangyueying", "re_zhangfei"],
|
||||
acts: [
|
||||
{
|
||||
id: "act1",
|
||||
name: "第一关·初梦",
|
||||
floorNodes: [1, 4, 4, 4, 1],
|
||||
minFloorNodes: [1, 3, 3, 3, 1],
|
||||
nodeWeights: { battle: 65, event: 15, rest: 10, shop: 10 },
|
||||
enemies: ["re_xiahoudun", "re_zhangliao", "re_xuzhu"],
|
||||
boss: "re_lvbu",
|
||||
baseGold: 20,
|
||||
},
|
||||
{
|
||||
id: "act2",
|
||||
name: "第二关·迷梦",
|
||||
floorNodes: [1, 4, 4, 4, 4, 1],
|
||||
minFloorNodes: [1, 3, 2, 3, 3, 1],
|
||||
nodeWeights: { battle: 55, elite: 15, event: 12, rest: 9, shop: 9 },
|
||||
enemies: ["re_ganning", "re_zhangfei", "re_huangzhong", "re_weiyan"],
|
||||
eliteEnemies: ["re_guanyu", "dianwei"],
|
||||
boss: "shen_guanyu",
|
||||
baseGold: 30,
|
||||
},
|
||||
{
|
||||
id: "act3",
|
||||
name: "第三关·终梦",
|
||||
floorNodes: [1, 4, 4, 4, 4, 4, 1],
|
||||
minFloorNodes: [1, 3, 2, 3, 2, 3, 1],
|
||||
nodeWeights: { battle: 45, elite: 20, event: 15, rest: 10, shop: 10 },
|
||||
enemies: ["re_simayi", "re_sunben", "dongzhuo", "re_yuanshao"],
|
||||
eliteEnemies: ["shen_zhaoyun", "shen_simayi"],
|
||||
boss: "shen_caocao",
|
||||
baseGold: 40,
|
||||
},
|
||||
],
|
||||
startingDeck: [
|
||||
["spade", 7, "sha"],
|
||||
["heart", 10, "sha"],
|
||||
["club", 4, "sha"],
|
||||
["diamond", 6, "sha"],
|
||||
["heart", 2, "shan"],
|
||||
["diamond", 7, "shan"],
|
||||
["club", 2, "shan"],
|
||||
["heart", 3, "tao"],
|
||||
["spade", 3, "guohe"],
|
||||
["heart", 7, "wuzhong"],
|
||||
["spade", 2, "bagua"],
|
||||
["club", 6, "sha", null, ["annihilate"]],
|
||||
],
|
||||
nodeNames: {
|
||||
battle: "战斗",
|
||||
elite: "精英",
|
||||
event: "事件",
|
||||
story: "剧情战斗",
|
||||
rest: "休息",
|
||||
shop: "商店",
|
||||
boss: "Boss",
|
||||
},
|
||||
};
|
||||
|
||||
export default mengsanConfig;
|
||||
+540
@@ -0,0 +1,540 @@
|
||||
import { lib, game, ui, get, _status } from "../../../noname.js";
|
||||
import config from "./config.js";
|
||||
import {
|
||||
completeNode,
|
||||
createRun,
|
||||
enterNextAct,
|
||||
generateActMap,
|
||||
getNodeEncounter,
|
||||
getSelectableNodes,
|
||||
insertStoryNode,
|
||||
nextRandom,
|
||||
} from "./state.js";
|
||||
|
||||
const MODE_ID = config.modeId;
|
||||
const STYLE_PATH = `${lib.assetURL}extension/术樱包/mengsan`;
|
||||
|
||||
const copy = value => JSON.parse(JSON.stringify(value));
|
||||
|
||||
const persistModeStorage = async () => {
|
||||
lib.storage.version = lib.version;
|
||||
if (lib.db) {
|
||||
await game.putDB("data", MODE_ID, copy(lib.storage));
|
||||
}
|
||||
else {
|
||||
localStorage.setItem(`${lib.configprefix}${MODE_ID}`, JSON.stringify(lib.storage));
|
||||
}
|
||||
};
|
||||
|
||||
const saveRun = async run => {
|
||||
try {
|
||||
lib.storage[config.saveKey] = copy(run);
|
||||
await persistModeStorage();
|
||||
}
|
||||
catch (error) {
|
||||
delete lib.storage[config.saveKey];
|
||||
try {
|
||||
await persistModeStorage();
|
||||
}
|
||||
catch {}
|
||||
alert("梦三存档失败,当前征程存档已清空。请查看控制台错误。");
|
||||
console.error("梦三存档失败:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const clearRun = async () => {
|
||||
delete lib.storage[config.saveKey];
|
||||
await persistModeStorage();
|
||||
};
|
||||
|
||||
const createOverlay = (title, className = "") => {
|
||||
const overlay = ui.create.div(`.mengsan-overlay-shuying${className ? `.${className}` : ""}`, ui.window);
|
||||
const panel = ui.create.div(".mengsan-panel-shuying", overlay);
|
||||
ui.create.div(".mengsan-title-shuying", title, panel);
|
||||
return { overlay, panel };
|
||||
};
|
||||
|
||||
const chooseButtons = (title, choices, description = "") => new Promise(resolve => {
|
||||
const { overlay, panel } = createOverlay(title);
|
||||
if (description) ui.create.div(".mengsan-description-shuying", description, panel);
|
||||
const buttons = ui.create.div(".mengsan-choice-list-shuying", panel);
|
||||
choices.forEach(choice => {
|
||||
const button = ui.create.div(".mengsan-choice-shuying", buttons);
|
||||
button.innerHTML = `<div class="mengsan-choice-name-shuying">${choice.name}</div>${choice.description ? `<div class="mengsan-choice-description-shuying">${choice.description}</div>` : ""}`;
|
||||
if (choice.disabled) button.classList.add("disabled");
|
||||
button.addEventListener(lib.config.touchscreen ? "touchend" : "click", event => {
|
||||
event.preventDefault();
|
||||
if (choice.disabled || overlay.dataset.resolved) return;
|
||||
overlay.dataset.resolved = "true";
|
||||
overlay.remove();
|
||||
resolve(choice.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const chooseRun = async savedRun => {
|
||||
if (!savedRun || savedRun.status != "running") return "new";
|
||||
const act = config.acts[savedRun.actIndex];
|
||||
return chooseButtons("梦三", [
|
||||
{
|
||||
id: "continue",
|
||||
name: "继续征程",
|
||||
description: `${act?.name || "未知区域"} · 已通过 ${savedRun.statistics?.completedNodes || 0} 个节点`,
|
||||
},
|
||||
{
|
||||
id: "new",
|
||||
name: "新的开始",
|
||||
description: "覆盖当前征程并重新生成第一关地图",
|
||||
},
|
||||
], "Demo 采用单一自动存档。节点完成后才会覆盖检查点。" );
|
||||
};
|
||||
|
||||
const chooseCharacter = async () => {
|
||||
const choices = config.characters.filter(name => lib.character[name]).map(name => ({
|
||||
id: name,
|
||||
name: get.translation(name),
|
||||
description: lib.translate[`${name}_title`] || "作为本次征程的角色",
|
||||
}));
|
||||
return chooseButtons("选择角色", choices.length ? choices : [{ id: "re_zhaoyun", name: "赵云" }]);
|
||||
};
|
||||
|
||||
const nodeColor = {
|
||||
battle: "#9d4937",
|
||||
elite: "#7e3f79",
|
||||
event: "#477aa6",
|
||||
story: "#8c6a34",
|
||||
rest: "#3f8560",
|
||||
shop: "#a27b35",
|
||||
boss: "#a62727",
|
||||
};
|
||||
|
||||
const showMap = run => new Promise(resolve => {
|
||||
const act = config.acts[run.actIndex];
|
||||
const selectable = getSelectableNodes(run);
|
||||
const selectableIds = new Set(selectable.map(node => node.id));
|
||||
const { overlay, panel } = createOverlay(act.name, "mengsan-map-overlay-shuying");
|
||||
panel.classList.add("mengsan-map-panel-shuying");
|
||||
const status = ui.create.div(".mengsan-map-status-shuying", panel);
|
||||
status.innerHTML = `体力:${run.player.hp == null ? "未初始化" : `${run.player.hp}/${run.player.maxHp}`} 金币:${run.player.gold} 牌组:${run.player.deck.length}`;
|
||||
const map = ui.create.div(".mengsan-map-shuying", panel);
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.classList.add("mengsan-lines-shuying");
|
||||
svg.setAttribute("viewBox", "0 0 100 100");
|
||||
svg.setAttribute("preserveAspectRatio", "none");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
map.appendChild(svg);
|
||||
const nodeMap = new Map(run.map.nodes.map(node => [node.id, node]));
|
||||
run.map.edges.forEach(edge => {
|
||||
const source = nodeMap.get(edge[0]);
|
||||
const target = nodeMap.get(edge[1]);
|
||||
if (!source || !target) return;
|
||||
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
||||
line.setAttribute("x1", source.x);
|
||||
line.setAttribute("y1", source.y);
|
||||
line.setAttribute("x2", target.x);
|
||||
line.setAttribute("y2", target.y);
|
||||
if (source.completed) line.classList.add("completed");
|
||||
if (source.dynamic || target.dynamic) line.classList.add("dynamic");
|
||||
svg.appendChild(line);
|
||||
});
|
||||
run.map.nodes.forEach(node => {
|
||||
const button = ui.create.div(".mengsan-node-shuying", map);
|
||||
button.style.left = `${node.x}%`;
|
||||
button.style.top = `${node.y}%`;
|
||||
button.style.setProperty("--node-color", nodeColor[node.type] || "#666");
|
||||
button.innerHTML = `<span>${config.nodeNames[node.type] || node.type}</span>`;
|
||||
button.dataset.nodeId = node.id;
|
||||
if (node.completed) button.classList.add("completed");
|
||||
else if (selectableIds.has(node.id)) button.classList.add("selectable");
|
||||
else button.classList.add("locked");
|
||||
button.addEventListener(lib.config.touchscreen ? "touchend" : "click", event => {
|
||||
event.preventDefault();
|
||||
if (!selectableIds.has(node.id) || overlay.dataset.resolved) return;
|
||||
overlay.dataset.resolved = "true";
|
||||
overlay.remove();
|
||||
resolve(node);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const createCardInstance = (run, name = "sha", upgrade = 0, affixes = []) => ({
|
||||
id: `mengsan_card_${Date.now()}_${Math.floor(nextRandom(run) * 1e6)}`,
|
||||
suit: ["spade", "heart", "club", "diamond"][Math.floor(nextRandom(run) * 4)],
|
||||
number: 1 + Math.floor(nextRandom(run) * 13),
|
||||
name,
|
||||
nature: null,
|
||||
upgrade,
|
||||
affixes: affixes.slice(),
|
||||
});
|
||||
|
||||
const applyReward = (run, rewardId) => {
|
||||
const player = run.player;
|
||||
switch (rewardId) {
|
||||
case "card_sha":
|
||||
player.deck.push(createCardInstance(run, "sha"));
|
||||
break;
|
||||
case "card_tao":
|
||||
player.deck.push(createCardInstance(run, "tao"));
|
||||
break;
|
||||
case "upgrade": {
|
||||
const card = player.deck[Math.floor(nextRandom(run) * player.deck.length)];
|
||||
if (card) card.upgrade = Number(card.upgrade || 0) + 1;
|
||||
break;
|
||||
}
|
||||
case "heal":
|
||||
player.hp = Math.min(player.maxHp, player.hp + 2);
|
||||
break;
|
||||
case "max_hp":
|
||||
player.maxHp++;
|
||||
player.hp++;
|
||||
break;
|
||||
case "item_hand":
|
||||
if (!player.items.includes("mengsan_hand_charm_shuying")) {
|
||||
player.items.push("mengsan_hand_charm_shuying");
|
||||
player.handLimitBonus++;
|
||||
}
|
||||
break;
|
||||
case "skill_yingyong":
|
||||
if (!player.permanentSkills.includes("mengsan_yingyong_shuying")) {
|
||||
player.permanentSkills.push("mengsan_yingyong_shuying");
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const chooseBattleReward = async (run, boss) => {
|
||||
const choices = boss ? [
|
||||
{ id: "skill_yingyong", name: "极品技能·英勇", description: "本次征程永久获得【英勇】" },
|
||||
{ id: "item_hand", name: "极品道具·束带", description: "基础手牌上限额外 +1" },
|
||||
{ id: "max_hp", name: "极品强化·生机", description: "体力上限与当前体力各 +1" },
|
||||
] : [
|
||||
{ id: "card_sha", name: "获得一张【杀】", description: "加入独立永久牌组" },
|
||||
{ id: "card_tao", name: "获得一张【桃】", description: "加入独立永久牌组" },
|
||||
{ id: "upgrade", name: "随机强化", description: "随机一张牌强化一级" },
|
||||
];
|
||||
const reward = await chooseButtons(boss ? "极品奖励(三选一)" : "战斗奖励(三选一)", choices);
|
||||
applyReward(run, reward);
|
||||
};
|
||||
|
||||
const finishUtilityNode = async (run, node) => {
|
||||
if (node.type == "rest") {
|
||||
const choice = await chooseButtons("休息节点", [
|
||||
{ id: "heal", name: "休息", description: "回复 2 点体力" },
|
||||
{ id: "upgrade", name: "磨砺", description: "随机强化一张卡牌" },
|
||||
]);
|
||||
applyReward(run, choice);
|
||||
}
|
||||
else if (node.type == "shop") {
|
||||
const price = 20;
|
||||
const choice = await chooseButtons("商店 Demo", [
|
||||
{ id: "card_sha", name: `购买【杀】(${price}金币)`, description: "加入个人牌组", disabled: run.player.gold < price },
|
||||
{ id: "heal", name: `恢复体力(${price}金币)`, description: "回复 2 点体力", disabled: run.player.gold < price },
|
||||
{ id: "leave", name: "离开", description: "暂不购买" },
|
||||
]);
|
||||
if (choice != "leave") {
|
||||
run.player.gold -= price;
|
||||
applyReward(run, choice);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const choice = await chooseButtons("随机事件", [
|
||||
{ id: "branch", name: "调查异象", description: "在当前路线后插入一个额外剧情战斗节点" },
|
||||
{ id: "gold", name: "收下钱袋", description: "获得 15 金币" },
|
||||
]);
|
||||
if (choice == "branch") insertStoryNode(run, node.id);
|
||||
else {
|
||||
run.player.gold += 15;
|
||||
run.statistics.goldEarned += 15;
|
||||
}
|
||||
}
|
||||
completeNode(run, node.id);
|
||||
await saveRun(run);
|
||||
game.reload();
|
||||
};
|
||||
|
||||
const shuffleBattlePile = (run, cards) => {
|
||||
for (let index = cards.length - 1; index > 0; index--) {
|
||||
const target = Math.floor(nextRandom(run) * (index + 1));
|
||||
[cards[index], cards[target]] = [cards[target], cards[index]];
|
||||
}
|
||||
};
|
||||
|
||||
const setupBattle = async (run, node) => {
|
||||
const encounter = getNodeEncounter(run, node);
|
||||
_status.mengsanRun_shuying = run;
|
||||
_status.mengsanNode_shuying = node;
|
||||
_status.mengsanEncounter_shuying = encounter;
|
||||
_status.mengsanBattle_shuying = {
|
||||
resolving: false,
|
||||
drawPile: copy(run.player.deck),
|
||||
discardPile: [],
|
||||
exhaustPile: [],
|
||||
};
|
||||
shuffleBattlePile(run, _status.mengsanBattle_shuying.drawPile);
|
||||
|
||||
game.prepareArena(2);
|
||||
const me = game.me;
|
||||
const enemy = game.players.find(current => current != me);
|
||||
me.init(run.player.character);
|
||||
enemy.init(lib.character[encounter.enemy] ? encounter.enemy : "re_lvbu");
|
||||
me.identity = "zhu";
|
||||
enemy.identity = "fan";
|
||||
me.side = false;
|
||||
enemy.side = true;
|
||||
me.setIdentity("zhu");
|
||||
enemy.setIdentity("fan");
|
||||
me.ai.shown = 1;
|
||||
enemy.ai.shown = 1;
|
||||
me.storage.mengsanPlayer_shuying = true;
|
||||
game.zhu = me;
|
||||
if (run.player.maxHp == null) {
|
||||
run.player.maxHp = me.maxHp;
|
||||
run.player.hp = me.hp;
|
||||
}
|
||||
me.maxHp = run.player.maxHp;
|
||||
me.hp = Math.max(1, Math.min(run.player.hp, me.maxHp));
|
||||
me.update();
|
||||
run.player.permanentSkills.forEach(skill => {
|
||||
if (lib.skill[skill] && !me.hasSkill(skill)) me.addSkill(skill);
|
||||
});
|
||||
game.addGlobalSkill("mengsan_draw_shuying");
|
||||
game.addGlobalSkill("mengsan_card_use_shuying");
|
||||
game.syncState();
|
||||
_status.event.trigger("gameStart");
|
||||
await game.mengsanDraw_shuying(me, 4);
|
||||
game.gameDraw(me, current => current == me ? 0 : 4);
|
||||
game.phaseLoop(me);
|
||||
};
|
||||
|
||||
const createMode = identityMode => {
|
||||
const identityElement = identityMode.element || {};
|
||||
const identityPlayer = identityElement.player || {};
|
||||
return {
|
||||
...identityMode,
|
||||
name: MODE_ID,
|
||||
connect: false,
|
||||
start: [
|
||||
async () => {
|
||||
lib.translate.restart = "返回";
|
||||
let run = copy(lib.storage[config.saveKey] || null);
|
||||
const action = await chooseRun(run);
|
||||
if (action == "new") {
|
||||
if (run) {
|
||||
const confirmNew = await chooseButtons("确认新的开始", [
|
||||
{ id: "cancel", name: "返回", description: "保留当前征程" },
|
||||
{ id: "confirm", name: "覆盖存档", description: "当前征程将被清除" },
|
||||
]);
|
||||
if (confirmNew == "cancel") {
|
||||
run = copy(lib.storage[config.saveKey]);
|
||||
}
|
||||
else {
|
||||
const character = await chooseCharacter();
|
||||
run = createRun(character);
|
||||
await saveRun(run);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const character = await chooseCharacter();
|
||||
run = createRun(character);
|
||||
await saveRun(run);
|
||||
}
|
||||
}
|
||||
const canRefreshOldMap = !run.map?.completedNodeIds?.length && run.map?.layoutVersion != config.mapLayoutVersion;
|
||||
if (!run.map || run.map.actIndex != run.actIndex || canRefreshOldMap) {
|
||||
run.map = generateActMap(run, run.actIndex);
|
||||
await saveRun(run);
|
||||
}
|
||||
const node = await showMap(run);
|
||||
if (["event", "rest", "shop"].includes(node.type)) {
|
||||
await finishUtilityNode(run, node);
|
||||
return;
|
||||
}
|
||||
await setupBattle(run, node);
|
||||
},
|
||||
],
|
||||
element: {
|
||||
...identityElement,
|
||||
player: {
|
||||
...identityPlayer,
|
||||
getHandcardLimit() {
|
||||
if (this.storage?.mengsanPlayer_shuying) {
|
||||
const run = _status.mengsanRun_shuying;
|
||||
const bonus = Number(run?.player?.handLimitBonus || 0);
|
||||
return Math.max(0, Math.min(8, this.hp) + bonus);
|
||||
}
|
||||
let number = Math.max(this.hp, 0);
|
||||
number = game.checkMod(this, number, "maxHandcardBase", this);
|
||||
number = game.checkMod(this, number, "maxHandcard", this);
|
||||
number = game.checkMod(this, number, "maxHandcardFinal", this);
|
||||
return Math.max(0, number);
|
||||
},
|
||||
},
|
||||
},
|
||||
game: {
|
||||
...(identityMode.game || {}),
|
||||
async mengsanPersistRun_shuying(run) {
|
||||
await saveRun(run);
|
||||
},
|
||||
async mengsanClearRun_shuying() {
|
||||
await clearRun();
|
||||
},
|
||||
async mengsanFailRun_shuying(reason = "征程失败") {
|
||||
if (_status.mengsanEnding_shuying) return;
|
||||
_status.mengsanEnding_shuying = true;
|
||||
await clearRun();
|
||||
game.log(reason);
|
||||
game.over(false);
|
||||
},
|
||||
async mengsanDraw_shuying(player, number = 1) {
|
||||
if (!player?.storage?.mengsanPlayer_shuying || number <= 0) return [];
|
||||
const battle = _status.mengsanBattle_shuying;
|
||||
const run = _status.mengsanRun_shuying;
|
||||
if (!battle || battle.drawPile.length + battle.discardPile.length < number) {
|
||||
await game.mengsanFailRun_shuying("个人牌堆与弃牌堆不足,挑战失败");
|
||||
return [];
|
||||
}
|
||||
const result = [];
|
||||
while (result.length < number) {
|
||||
if (!battle.drawPile.length) {
|
||||
battle.drawPile = battle.discardPile.splice(0);
|
||||
shuffleBattlePile(run, battle.drawPile);
|
||||
game.log(player, "洗切了个人弃牌堆");
|
||||
}
|
||||
const data = battle.drawPile.shift();
|
||||
const card = game.createCard(data.name, data.suit, data.number, data.nature);
|
||||
card.storage.mengsanCard_shuying = copy(data);
|
||||
card.destroyLog = false;
|
||||
card.destroyed = (current, position) => {
|
||||
if (position != "discardPile") return false;
|
||||
const info = copy(current.storage.mengsanCard_shuying);
|
||||
if (info.affixes?.includes("annihilate") && current.storage.mengsanUsed_shuying) battle.exhaustPile.push(info);
|
||||
else battle.discardPile.push(info);
|
||||
return true;
|
||||
};
|
||||
if (data.affixes?.includes("annihilate")) card.addGaintag("湮灭");
|
||||
if (data.upgrade) card.addGaintag(`强化+${data.upgrade}`);
|
||||
result.push(card);
|
||||
}
|
||||
player.directgain(result);
|
||||
player.$draw(result.length);
|
||||
game.log(player, "从个人牌堆摸了", get.cnNumber(result.length), "张牌");
|
||||
return result;
|
||||
},
|
||||
async mengsanFinishBattle_shuying() {
|
||||
const battle = _status.mengsanBattle_shuying;
|
||||
if (!battle || battle.resolving || _status.mengsanEnding_shuying) return;
|
||||
battle.resolving = true;
|
||||
game.pause();
|
||||
const run = _status.mengsanRun_shuying;
|
||||
const node = _status.mengsanNode_shuying;
|
||||
const encounter = _status.mengsanEncounter_shuying;
|
||||
run.player.hp = Math.max(1, game.me.hp);
|
||||
run.player.gold += encounter.gold;
|
||||
run.statistics.goldEarned += encounter.gold;
|
||||
run.statistics.defeatedEnemies++;
|
||||
await chooseBattleReward(run, encounter.boss);
|
||||
completeNode(run, node.id);
|
||||
if (encounter.boss) {
|
||||
if (!enterNextAct(run)) {
|
||||
const profile = copy(lib.storage[config.profileKey] || { wins: 0, completedRuns: [] });
|
||||
profile.wins++;
|
||||
profile.completedRuns.push({
|
||||
runId: run.runId,
|
||||
character: run.player.character,
|
||||
completedNodes: run.statistics.completedNodes,
|
||||
goldEarned: run.statistics.goldEarned,
|
||||
completedAt: Date.now(),
|
||||
});
|
||||
lib.storage[config.profileKey] = profile;
|
||||
delete lib.storage[config.saveKey];
|
||||
await persistModeStorage();
|
||||
game.over(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await saveRun(run);
|
||||
game.reload();
|
||||
},
|
||||
checkResult() {
|
||||
if (_status.mengsanEnding_shuying || _status.mengsanBattle_shuying?.resolving) return;
|
||||
if (!game.me || game.me.isDead()) {
|
||||
game.mengsanFailRun_shuying("角色死亡,征程结束");
|
||||
return;
|
||||
}
|
||||
const hasEnemy = game.players.some(current => current != game.me && current.side === true && current.isAlive());
|
||||
if (!hasEnemy) game.mengsanFinishBattle_shuying();
|
||||
},
|
||||
},
|
||||
skill: {
|
||||
...(identityMode.skill || {}),
|
||||
mengsan_draw_shuying: {
|
||||
trigger: { player: "drawBegin" },
|
||||
firstDo: true,
|
||||
forced: true,
|
||||
silent: true,
|
||||
priority: 10000,
|
||||
filter(event, player) {
|
||||
return Boolean(player.storage?.mengsanPlayer_shuying && event.num > 0);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const number = trigger.num;
|
||||
trigger.cancel();
|
||||
await game.mengsanDraw_shuying(player, number);
|
||||
},
|
||||
},
|
||||
mengsan_card_use_shuying: {
|
||||
trigger: { global: "useCard1" },
|
||||
forced: true,
|
||||
silent: true,
|
||||
popup: false,
|
||||
filter(event) {
|
||||
return event.player?.storage?.mengsanPlayer_shuying && event.cards?.some(card => card.storage?.mengsanCard_shuying);
|
||||
},
|
||||
async content(event, trigger) {
|
||||
trigger.cards.forEach(card => {
|
||||
if (card.storage?.mengsanCard_shuying) card.storage.mengsanUsed_shuying = true;
|
||||
});
|
||||
},
|
||||
},
|
||||
mengsan_yingyong_shuying: {
|
||||
trigger: { source: "damageBegin1" },
|
||||
forced: true,
|
||||
usable: 1,
|
||||
filter(event, player) {
|
||||
return Boolean(event.card && _status.currentPhase == player);
|
||||
},
|
||||
async content(event, trigger) {
|
||||
trigger.num++;
|
||||
},
|
||||
},
|
||||
},
|
||||
translate: {
|
||||
...(identityMode.translate || {}),
|
||||
mengsan_draw_shuying: "梦三牌组",
|
||||
mengsan_card_use_shuying: "梦三词缀",
|
||||
mengsan_yingyong_shuying: "英勇",
|
||||
mengsan_yingyong_shuying_info: "锁定技,每回合限一次,你于自己的回合内使用牌造成的伤害+1。",
|
||||
},
|
||||
config: {},
|
||||
help: {
|
||||
梦三: "梦三 Demo:每次完成节点后保存检查点;玩家使用个人牌组,敌人继续使用 noname 公共牌堆。",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default async function initMengsan() {
|
||||
if (lib.config.all.mode.includes(MODE_ID)) return;
|
||||
await new Promise(resolve => {
|
||||
const style = lib.init.css(STYLE_PATH, "style", resolve);
|
||||
style.addEventListener("error", resolve, { once: true });
|
||||
});
|
||||
const identityMode = await game.loadModeAsync("identity");
|
||||
const mode = createMode(identityMode);
|
||||
mode.splash = "ext:术樱包/pve/images/tianshu.jpg";
|
||||
game.addMode(MODE_ID, mode, {
|
||||
translate: "梦三",
|
||||
config: {},
|
||||
extension: "术樱包",
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import config from "./config.js";
|
||||
|
||||
const hashText = text => {
|
||||
let value = 2166136261;
|
||||
for (let index = 0; index < text.length; index++) {
|
||||
value ^= text.charCodeAt(index);
|
||||
value = Math.imul(value, 16777619);
|
||||
}
|
||||
return value >>> 0;
|
||||
};
|
||||
|
||||
export const nextRandom = run => {
|
||||
let value = Number(run.randomState || run.seed || Date.now()) >>> 0;
|
||||
value += 0x6d2b79f5;
|
||||
run.randomState = value >>> 0;
|
||||
let result = value;
|
||||
result = Math.imul(result ^ result >>> 15, result | 1);
|
||||
result ^= result + Math.imul(result ^ result >>> 7, result | 61);
|
||||
return ((result ^ result >>> 14) >>> 0) / 4294967296;
|
||||
};
|
||||
|
||||
export const randomGet = (run, list) => list[Math.floor(nextRandom(run) * list.length)];
|
||||
|
||||
const weightedType = (run, weights) => {
|
||||
const entries = Object.entries(weights);
|
||||
const total = entries.reduce((sum, entry) => sum + entry[1], 0);
|
||||
let cursor = nextRandom(run) * total;
|
||||
for (const [type, weight] of entries) {
|
||||
cursor -= weight;
|
||||
if (cursor <= 0) return type;
|
||||
}
|
||||
return entries[0][0];
|
||||
};
|
||||
|
||||
const connectFloors = (run, edges, currentFloor, nextFloor) => {
|
||||
const addEdge = (source, target) => {
|
||||
if (!edges.some(edge => edge[0] == source.id && edge[1] == target.id)) {
|
||||
edges.push([source.id, target.id]);
|
||||
}
|
||||
};
|
||||
currentFloor.forEach(source => {
|
||||
const targets = nextFloor.slice().sort((left, right) => {
|
||||
const distance = Math.abs(left.routeIndex - source.routeIndex) - Math.abs(right.routeIndex - source.routeIndex);
|
||||
return distance || left.routeIndex - right.routeIndex;
|
||||
});
|
||||
addEdge(source, targets[0]);
|
||||
if (targets.length > 1 && Math.abs(targets[1].routeIndex - source.routeIndex) <= 1 && nextRandom(run) < 0.45) {
|
||||
addEdge(source, targets[1]);
|
||||
}
|
||||
});
|
||||
nextFloor.forEach(target => {
|
||||
if (!edges.some(edge => edge[1] == target.id)) {
|
||||
const source = currentFloor.slice().sort((left, right) => {
|
||||
const distance = Math.abs(left.routeIndex - target.routeIndex) - Math.abs(right.routeIndex - target.routeIndex);
|
||||
return distance || left.routeIndex - right.routeIndex;
|
||||
})[0];
|
||||
addEdge(source, target);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getFloorRoutes = (run, act, floor, lastFloor) => {
|
||||
const routeCount = Math.max(1, config.routeCount);
|
||||
if (floor == 0 || floor == lastFloor) return [(routeCount - 1) / 2];
|
||||
const maximum = Math.max(1, Math.min(routeCount, act.floorNodes[floor] || routeCount));
|
||||
const configuredMinimum = act.minFloorNodes?.[floor] ?? Math.max(2, maximum - 1);
|
||||
const minimum = Math.max(1, Math.min(maximum, configuredMinimum));
|
||||
const count = minimum + Math.floor(nextRandom(run) * (maximum - minimum + 1));
|
||||
const routes = Array.from({ length: routeCount }, (_, index) => index);
|
||||
for (let index = routes.length - 1; index > 0; index--) {
|
||||
const target = Math.floor(nextRandom(run) * (index + 1));
|
||||
[routes[index], routes[target]] = [routes[target], routes[index]];
|
||||
}
|
||||
return routes.slice(0, count).sort((left, right) => left - right);
|
||||
};
|
||||
|
||||
export const generateActMap = (run, actIndex) => {
|
||||
const act = config.acts[actIndex];
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
const floors = [];
|
||||
const lastFloor = act.floorNodes.length - 1;
|
||||
act.floorNodes.forEach((count, floor) => {
|
||||
const floorNodes = [];
|
||||
const routes = getFloorRoutes(run, act, floor, lastFloor);
|
||||
routes.forEach((routeIndex, index) => {
|
||||
let type;
|
||||
if (floor == lastFloor) type = "boss";
|
||||
else if (floor == 0) type = "battle";
|
||||
else type = weightedType(run, act.nodeWeights);
|
||||
const id = `${act.id}_f${floor}_n${index}_${Math.floor(nextRandom(run) * 1e6)}`;
|
||||
const node = {
|
||||
id,
|
||||
floor,
|
||||
type,
|
||||
routeIndex,
|
||||
x: Math.round((routeIndex + 1) * 100 / (config.routeCount + 1)),
|
||||
y: Math.round(90 - floor * 80 / Math.max(1, lastFloor)),
|
||||
completed: false,
|
||||
};
|
||||
nodes.push(node);
|
||||
floorNodes.push(node);
|
||||
});
|
||||
floors.push(floorNodes);
|
||||
});
|
||||
for (let floor = 0; floor < floors.length - 1; floor++) {
|
||||
connectFloors(run, edges, floors[floor], floors[floor + 1]);
|
||||
}
|
||||
return {
|
||||
layoutVersion: config.mapLayoutVersion,
|
||||
routeCount: config.routeCount,
|
||||
actIndex,
|
||||
actId: act.id,
|
||||
nodes,
|
||||
edges,
|
||||
currentNodeId: null,
|
||||
completedNodeIds: [],
|
||||
};
|
||||
};
|
||||
|
||||
const createDeck = () => config.startingDeck.map((entry, index) => ({
|
||||
id: `mengsan_card_${Date.now()}_${index}`,
|
||||
suit: entry[0],
|
||||
number: entry[1],
|
||||
name: entry[2],
|
||||
nature: entry[3] || null,
|
||||
affixes: entry[4] ? entry[4].slice() : [],
|
||||
upgrade: 0,
|
||||
}));
|
||||
|
||||
export const createRun = character => {
|
||||
const seed = (Date.now() ^ Math.floor(Math.random() * 0x7fffffff)) >>> 0;
|
||||
const run = {
|
||||
schemaVersion: config.schemaVersion,
|
||||
runId: `mengsan_${Date.now()}`,
|
||||
seed,
|
||||
randomState: seed,
|
||||
status: "running",
|
||||
revision: 0,
|
||||
actIndex: 0,
|
||||
player: {
|
||||
id: "player_1",
|
||||
ownerId: "local",
|
||||
character,
|
||||
hp: null,
|
||||
maxHp: null,
|
||||
gold: 0,
|
||||
deck: createDeck(),
|
||||
permanentSkills: [],
|
||||
temporarySkills: [],
|
||||
items: [],
|
||||
handLimitBonus: 0,
|
||||
},
|
||||
statistics: {
|
||||
completedNodes: 0,
|
||||
defeatedEnemies: 0,
|
||||
goldEarned: 0,
|
||||
},
|
||||
};
|
||||
run.map = generateActMap(run, 0);
|
||||
return run;
|
||||
};
|
||||
|
||||
export const getSelectableNodes = run => {
|
||||
const map = run.map;
|
||||
if (!map.currentNodeId) {
|
||||
return map.nodes.filter(node => node.floor == 0 && !node.completed);
|
||||
}
|
||||
const ids = map.edges.filter(edge => edge[0] == map.currentNodeId).map(edge => edge[1]);
|
||||
return map.nodes.filter(node => ids.includes(node.id) && !node.completed);
|
||||
};
|
||||
|
||||
export const completeNode = (run, nodeId) => {
|
||||
const node = run.map.nodes.find(current => current.id == nodeId);
|
||||
if (!node || node.completed) return false;
|
||||
node.completed = true;
|
||||
run.map.currentNodeId = node.id;
|
||||
run.map.completedNodeIds.push(node.id);
|
||||
run.statistics.completedNodes++;
|
||||
run.revision++;
|
||||
return true;
|
||||
};
|
||||
|
||||
export const insertStoryNode = (run, sourceId) => {
|
||||
const map = run.map;
|
||||
const source = map.nodes.find(node => node.id == sourceId);
|
||||
const edgeIndex = map.edges.findIndex(edge => edge[0] == sourceId);
|
||||
if (!source || edgeIndex < 0) return null;
|
||||
const oldEdge = map.edges[edgeIndex];
|
||||
const target = map.nodes.find(node => node.id == oldEdge[1]);
|
||||
if (!target) return null;
|
||||
const node = {
|
||||
id: `story_${hashText(`${run.runId}_${sourceId}_${run.revision}`)}`,
|
||||
floor: source.floor + 0.45,
|
||||
type: "story",
|
||||
x: Math.max(8, Math.min(92, Math.round((source.x + target.x) / 2 + (nextRandom(run) - 0.5) * 16))),
|
||||
y: Math.round((source.y + target.y) / 2),
|
||||
completed: false,
|
||||
dynamic: true,
|
||||
};
|
||||
map.edges.splice(edgeIndex, 1, [source.id, node.id], [node.id, target.id]);
|
||||
map.nodes.push(node);
|
||||
return node;
|
||||
};
|
||||
|
||||
export const enterNextAct = run => {
|
||||
run.actIndex++;
|
||||
if (run.actIndex >= config.acts.length) {
|
||||
run.status = "completed";
|
||||
return false;
|
||||
}
|
||||
run.map = generateActMap(run, run.actIndex);
|
||||
run.revision++;
|
||||
return true;
|
||||
};
|
||||
|
||||
export const getNodeEncounter = (run, node) => {
|
||||
const act = config.acts[run.actIndex];
|
||||
let pool = act.enemies;
|
||||
if (node.type == "elite" || node.type == "story") pool = act.eliteEnemies?.length ? act.eliteEnemies : act.enemies;
|
||||
if (node.type == "boss") return { enemy: act.boss, gold: act.baseGold * 3, boss: true };
|
||||
return {
|
||||
enemy: randomGet(run, pool.filter(Boolean)),
|
||||
gold: node.type == "elite" || node.type == "story" ? act.baseGold * 2 : act.baseGold,
|
||||
boss: false,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,258 @@
|
||||
.mengsan-overlay-shuying {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 80;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
padding: 18px;
|
||||
background: radial-gradient(circle at 50% 20%, rgba(68, 62, 83, 0.72), rgba(8, 10, 15, 0.96));
|
||||
color: #f6ead0;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.mengsan-overlay-shuying *,
|
||||
.mengsan-overlay-shuying *::before,
|
||||
.mengsan-overlay-shuying *::after {
|
||||
box-sizing: border-box;
|
||||
float: none !important;
|
||||
writing-mode: horizontal-tb !important;
|
||||
-webkit-writing-mode: horizontal-tb !important;
|
||||
text-orientation: mixed !important;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
.mengsan-panel-shuying {
|
||||
position: relative !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
flex: none !important;
|
||||
width: min(720px, 94vw);
|
||||
height: auto !important;
|
||||
min-height: 160px;
|
||||
max-height: 92vh;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
padding: 22px;
|
||||
border: 1px solid rgba(232, 202, 140, 0.55);
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(145deg, rgba(31, 28, 38, 0.98), rgba(17, 19, 26, 0.98));
|
||||
box-shadow: 0 20px 70px rgba(0, 0, 0, 0.58), inset 0 1px rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.mengsan-panel-shuying > .mengsan-title-shuying,
|
||||
.mengsan-panel-shuying > .mengsan-description-shuying,
|
||||
.mengsan-panel-shuying > .mengsan-choice-list-shuying,
|
||||
.mengsan-panel-shuying > .mengsan-map-status-shuying,
|
||||
.mengsan-panel-shuying > .mengsan-map-shuying,
|
||||
.mengsan-choice-list-shuying > .mengsan-choice-shuying,
|
||||
.mengsan-choice-shuying > .mengsan-choice-name-shuying,
|
||||
.mengsan-choice-shuying > .mengsan-choice-description-shuying {
|
||||
position: relative !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.mengsan-title-shuying {
|
||||
display: block !important;
|
||||
flex: none !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-bottom: 14px;
|
||||
color: #f2d28e;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
letter-spacing: 4px;
|
||||
line-height: 1.35;
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
.mengsan-description-shuying,
|
||||
.mengsan-map-status-shuying {
|
||||
display: block !important;
|
||||
flex: none !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-bottom: 14px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.mengsan-choice-list-shuying {
|
||||
display: grid;
|
||||
flex: none !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mengsan-choice-shuying {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
min-height: 64px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid rgba(242, 210, 142, 0.34);
|
||||
border-radius: 9px;
|
||||
background: rgba(255, 255, 255, 0.055);
|
||||
cursor: pointer;
|
||||
transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
|
||||
}
|
||||
|
||||
.mengsan-choice-shuying:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(242, 210, 142, 0.8);
|
||||
background: rgba(242, 210, 142, 0.12);
|
||||
}
|
||||
|
||||
.mengsan-choice-shuying.disabled {
|
||||
cursor: default;
|
||||
opacity: 0.35;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.mengsan-choice-name-shuying {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
color: #f6dca5;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.mengsan-choice-description-shuying {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-top: 5px;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.mengsan-map-panel-shuying {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
padding: clamp(12px, 2vw, 24px);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mengsan-map-overlay-shuying {
|
||||
padding: 0 !important;
|
||||
background:
|
||||
radial-gradient(circle at 50% 18%, rgba(67, 59, 82, 0.82), rgba(9, 10, 16, 0.97)),
|
||||
repeating-linear-gradient(12deg, rgba(255, 255, 255, 0.018) 0 2px, transparent 2px 12px);
|
||||
}
|
||||
|
||||
.mengsan-map-panel-shuying > .mengsan-title-shuying {
|
||||
margin-bottom: 6px;
|
||||
font-size: clamp(24px, 3vw, 36px);
|
||||
}
|
||||
|
||||
.mengsan-map-panel-shuying > .mengsan-map-status-shuying {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mengsan-map-shuying {
|
||||
position: relative;
|
||||
display: block !important;
|
||||
flex: 1 1 auto !important;
|
||||
width: 100%;
|
||||
height: auto !important;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.mengsan-lines-shuying {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mengsan-lines-shuying line {
|
||||
vector-effect: non-scaling-stroke;
|
||||
stroke: rgba(232, 219, 190, 0.68);
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 6 5;
|
||||
}
|
||||
|
||||
.mengsan-lines-shuying line.completed {
|
||||
stroke: rgba(243, 207, 128, 0.95);
|
||||
}
|
||||
|
||||
.mengsan-lines-shuying line.dynamic {
|
||||
stroke: rgba(118, 205, 255, 0.95);
|
||||
stroke-dasharray: 3 4;
|
||||
}
|
||||
|
||||
.mengsan-node-shuying {
|
||||
position: absolute;
|
||||
width: 66px;
|
||||
height: 40px;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid var(--node-color);
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 38% 28%, rgba(255, 255, 255, 0.2), transparent 42%), var(--node-color);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.42);
|
||||
color: #fff4df;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mengsan-node-shuying.selectable {
|
||||
cursor: pointer;
|
||||
animation: mengsan-node-pulse-shuying 1.6s infinite;
|
||||
}
|
||||
|
||||
.mengsan-node-shuying.locked {
|
||||
opacity: 0.45;
|
||||
filter: grayscale(0.65);
|
||||
}
|
||||
|
||||
.mengsan-node-shuying.completed {
|
||||
opacity: 0.72;
|
||||
box-shadow: 0 0 0 3px rgba(244, 211, 139, 0.3);
|
||||
}
|
||||
|
||||
@keyframes mengsan-node-pulse-shuying {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(242, 207, 132, 0.18), 0 5px 15px rgba(0, 0, 0, 0.42); }
|
||||
50% { box-shadow: 0 0 0 8px rgba(242, 207, 132, 0), 0 5px 18px rgba(0, 0, 0, 0.55); }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.mengsan-overlay-shuying { padding: 7px; }
|
||||
.mengsan-panel-shuying { padding: 14px; }
|
||||
.mengsan-title-shuying { font-size: 24px; }
|
||||
.mengsan-map-panel-shuying { padding: 8px; }
|
||||
.mengsan-map-shuying { min-height: 0; }
|
||||
.mengsan-node-shuying { width: 54px; height: 36px; font-size: 11px; }
|
||||
}
|
||||
+12
-224
@@ -1,227 +1,15 @@
|
||||
const characters = {
|
||||
zishu_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["zishu_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/zishu_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/zishu.mp3"]
|
||||
},
|
||||
// chouniu_boss_shuying: {},
|
||||
// yinhu_boss_shuying: {},
|
||||
// maotu_boss_shuying: {},
|
||||
chenlong_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["chenlong_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/chenlong_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/chenlong.mp3"]
|
||||
},
|
||||
// sishe_boss_shuying: {},
|
||||
wuma_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["wuma_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/wuma_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/wuma_shuying.mp3"]
|
||||
},
|
||||
weiyang_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["weiyang_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/weiyang_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/weiyang.mp3"]
|
||||
},
|
||||
shenhou_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["shenhou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/shenhou_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/shenhou.mp3"]
|
||||
},
|
||||
// youji_boss_shuying: {},
|
||||
xvgou_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["xvgou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/xvgou_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/xvgou.mp3"]
|
||||
},
|
||||
// haizhu_boss_shuying: {},
|
||||
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";
|
||||
|
||||
heibaiwuchang_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mizui_shuying", "qiangzheng_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png"
|
||||
},
|
||||
niutoumamian_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["shiyv_shuying", "manji_shuying", "xiaoshou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png"
|
||||
},
|
||||
yvsai_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["anchao_shuying", "guixi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/yvsai_boss_shuying.png"
|
||||
},
|
||||
huangfeng_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mingchong_shuying", "duzhen_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png"
|
||||
},
|
||||
riyeyoushen_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["huiyun_shuying", "yezhong_shuying", "zhoucha_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png"
|
||||
},
|
||||
|
||||
|
||||
|
||||
caocao_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "wei",
|
||||
hp: 4,
|
||||
skills: ["jianxiong_shuying", "lingba_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/caocao_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3"]
|
||||
},
|
||||
simayi_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "wei",
|
||||
hp: 4,
|
||||
skills: ["fankui_shuying", "guicai_shuying", "langgu_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/simayi_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3"]
|
||||
},
|
||||
lvbu_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mashu", "wushuang_shuying", "shenij_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/lvbu_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3"]
|
||||
},
|
||||
dongzhuo_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["jiuchi_shuying", "roulin_shuying", "baonue_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/dongzhuo_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3"]
|
||||
},
|
||||
|
||||
shuishengonggong_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["tuanliu_shuying", "juehong_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/shuishengonggong_boss_shuying.png"
|
||||
},
|
||||
// huoshenzhurong_boss_shuying: {},
|
||||
|
||||
|
||||
baiqi_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["changsheng_shuying", "shashen_shuying", "wuan_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/baiqi_boss_shuying.png"
|
||||
},
|
||||
|
||||
kuafu_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["shenqu_shuying", "lieben_shuying", "yinjiang_shuying", "zhuri_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/kuafu_boss_shuying.png"
|
||||
},
|
||||
|
||||
shaohao_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["baiyi_shuying", "shenen_shuying",],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/shaohao_boss_shuying.png"
|
||||
},
|
||||
xuannv_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["xuanlie_shuying", "jiutian_shuying", "dishi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/xuannv_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3"]
|
||||
},
|
||||
hanba_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["xinji_shuying", "zhiri_shuying", "fenshi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/hanba_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/天书乱斗/hanba_boss_shuying.mp3"]
|
||||
},
|
||||
|
||||
};
|
||||
// 汇总所有主题武将,PVE 玩法只需引用唯一的 BOSS ID。
|
||||
const characters = mergeBossModules("武将", [
|
||||
["十二生肖", shiershengxiao],
|
||||
["捉鬼驱邪", zhuoguiquxie],
|
||||
["青青子衿", qingqingzijin],
|
||||
["天书乱斗", tianshu],
|
||||
]);
|
||||
|
||||
export default characters;
|
||||
|
||||
+10
-202
@@ -1,205 +1,13 @@
|
||||
import { lib, game, ui, get, ai, _status } from "../../../../noname.js";
|
||||
import tianshuConfig from "../../tianshu/config.js";
|
||||
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";
|
||||
|
||||
const getTianshuDifficulty = () => {
|
||||
return _status[tianshuConfig.settings.difficultyStatusKey] || "normal";
|
||||
};
|
||||
|
||||
const dynamicTranslates = {
|
||||
//黑白无常
|
||||
mizui_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2
|
||||
|
||||
return `锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的${num}张牌。`
|
||||
},
|
||||
qiangzheng_shuying(player, skill) {
|
||||
let num = 2;
|
||||
if (getTianshuDifficulty() == "hard") num = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 6;
|
||||
|
||||
return `锁定技,敌方角色的结束阶段,若其手牌数小于${num},你获得其所有手牌。`
|
||||
},
|
||||
xixing_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "nightmare") {
|
||||
return "锁定技,准备阶段,你对每名敌方角色各造成2点雷电伤害,然后你回复等量体力";
|
||||
}
|
||||
|
||||
return "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力。";
|
||||
},
|
||||
|
||||
//鱼鳃
|
||||
guixi_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸${num}张牌。`;
|
||||
},
|
||||
|
||||
//黄蜂
|
||||
mingchong_shuying(player, skill) {
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
return "锁定技,你死亡时,杀死你的角色弃置所有牌。"
|
||||
}
|
||||
|
||||
return "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。"
|
||||
},
|
||||
duzhen_shuyingg(player, skill) {
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。"
|
||||
}
|
||||
|
||||
return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置2张牌(优先装备区的牌)。"
|
||||
|
||||
},
|
||||
|
||||
//日夜游神
|
||||
mingchong_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2
|
||||
|
||||
return `出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成${num}点伤害。`
|
||||
},
|
||||
yezhong_shuying(player, skill) {
|
||||
let num = 1;
|
||||
if (getTianshuDifficulty() == "hard") num = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 3;
|
||||
|
||||
return `锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃${num}张手牌。`
|
||||
},
|
||||
zhoucha_shuying(player, skill) {
|
||||
let num = 1;
|
||||
if (getTianshuDifficulty() == "hard") num = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 3;
|
||||
|
||||
return `锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+${num}。`
|
||||
},
|
||||
|
||||
|
||||
//曹操
|
||||
lingba_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成${num}点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。`
|
||||
},
|
||||
yishen_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "nightmare") {
|
||||
return "当你回复体力时,可以改为获得所有敌人各一张随机装备。";
|
||||
}
|
||||
|
||||
return "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。";
|
||||
},
|
||||
|
||||
//司马懿
|
||||
yuanlv_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
|
||||
if (difficulty == "nightmare") {
|
||||
return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成过1点伤害。";
|
||||
}
|
||||
|
||||
return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。";
|
||||
|
||||
},
|
||||
|
||||
//吕布
|
||||
wushuang_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限${num}次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。`;
|
||||
},
|
||||
shenij_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
let card = 2;
|
||||
let sha = 1;
|
||||
let target = 1;
|
||||
|
||||
if (difficulty == "hard") {
|
||||
card = 3;
|
||||
sha = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
card = 4;
|
||||
sha = 2;
|
||||
target = 2;
|
||||
}
|
||||
|
||||
return `判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸${card}张牌;出牌阶段,你可以多使用${sha}张【杀】,你的【杀】可以多指定${target}名角色为目标。`;
|
||||
},
|
||||
zhanjia_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 3 : 2
|
||||
|
||||
return `锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸${num}张牌。`
|
||||
},
|
||||
|
||||
//董卓
|
||||
baonue_shuying(player, skill) {
|
||||
let num = 3;
|
||||
if (getTianshuDifficulty() == "hard") num = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 5;
|
||||
|
||||
return `锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为${num}。`
|
||||
},
|
||||
|
||||
//水神共工
|
||||
tuanliu_shuying(player, skill) {
|
||||
let hp = 2;
|
||||
let card = 4;
|
||||
let damage = 1;
|
||||
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
hp = 3;
|
||||
card = 6;
|
||||
}
|
||||
return `锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复${hp}点体力;大于5,你摸${card}张牌;大于9,你对所有敌方角色造成${damage}点伤害。`
|
||||
},
|
||||
|
||||
//少昊
|
||||
baiyi_shuying(player, skill) {
|
||||
let num = 1;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
return `锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其${num}张牌;若当前轮数小于5,对其造成${num}点雷电伤害;若当前轮数小于7,其随机弃置${num}张牌。`;
|
||||
},
|
||||
|
||||
//玄女
|
||||
xuanlie_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "normal" ? 1 : 2;
|
||||
|
||||
return `锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成${num}点伤害。`;
|
||||
},
|
||||
jiutian_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "normal" ? 1 : 2;
|
||||
const extra =
|
||||
difficulty == "normal" || difficulty == "hard"
|
||||
? ""
|
||||
: "若这些牌包含4种花色,这些角色再额外失去1点体力。";
|
||||
|
||||
return `锁定技,准备阶段,你获得所有敌方角色各${num}张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。${extra}`;
|
||||
},
|
||||
|
||||
xinji_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "nightmare" ? 2 : 1;
|
||||
|
||||
return `锁定技,当友方于回合外因弃置而失去手牌时,你对当前角色造成${num}点伤害。`
|
||||
},
|
||||
zhiri_shuying(player, skill) {
|
||||
let num = 1;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
return `锁定技,当敌方角色使用红色锦囊牌指定目标后,你摸${num}张牌。`
|
||||
},
|
||||
};
|
||||
// 汇总存在动态描述的主题技能;无动态描述的主题不需要空模块占位。
|
||||
const dynamicTranslates = mergeBossModules("动态翻译", [
|
||||
["捉鬼驱邪", zhuoguiquxie],
|
||||
["青青子衿", qingqingzijin],
|
||||
["天书乱斗", tianshu],
|
||||
]);
|
||||
|
||||
export default dynamicTranslates;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 合并各主题的同类数据;重复 ID 保留先加载项,跳过后加载项,避免中断整个 BOSS 包。
|
||||
const mergeBossModules = (dataType, modules) => {
|
||||
const result = {};
|
||||
for (const [moduleName, data] of modules) {
|
||||
for (const [id, value] of Object.entries(data || {})) {
|
||||
if (Object.prototype.hasOwnProperty.call(result, id)) {
|
||||
console.warn(`术樱包活动BOSS:跳过${moduleName}中重复的${dataType}ID“${id}”`);
|
||||
continue;
|
||||
}
|
||||
result[id] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export default mergeBossModules;
|
||||
@@ -0,0 +1,44 @@
|
||||
const characters = {
|
||||
caocao_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "wei",
|
||||
hp: 4,
|
||||
skills: ["jianxiong_shuying", "lingba_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/caocao_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/caocao_boss_shuying.mp3"]
|
||||
},
|
||||
simayi_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "wei",
|
||||
hp: 4,
|
||||
skills: ["fankui_shuying", "guicai_shuying", "langgu_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/simayi_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/simayi_boss_shuying.mp3"]
|
||||
},
|
||||
lvbu_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mashu", "wushuang_shuying", "shenij_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/lvbu_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/lvbu_boss_shuying.mp3"]
|
||||
},
|
||||
dongzhuo_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["jiuchi_shuying", "roulin_shuying", "baonue_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/青青子衿/dongzhuo_boss_shuying.jpg",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/青青子衿/dongzhuo_boss_shuying.mp3"]
|
||||
},
|
||||
};
|
||||
|
||||
export default characters;
|
||||
@@ -0,0 +1,71 @@
|
||||
import { getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const dynamicTranslates = {
|
||||
//曹操
|
||||
lingba_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成${num}点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。`
|
||||
},
|
||||
yishen_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "nightmare") {
|
||||
return "当你回复体力时,可以改为获得所有敌人各一张随机装备。";
|
||||
}
|
||||
|
||||
return "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。";
|
||||
},
|
||||
|
||||
//司马懿
|
||||
yuanlv_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
|
||||
if (difficulty == "nightmare") {
|
||||
return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成过1点伤害。";
|
||||
}
|
||||
|
||||
return "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。";
|
||||
|
||||
},
|
||||
|
||||
//吕布
|
||||
wushuang_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限${num}次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。`;
|
||||
},
|
||||
shenij_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
let card = 2;
|
||||
let sha = 1;
|
||||
let target = 1;
|
||||
|
||||
if (difficulty == "hard") {
|
||||
card = 3;
|
||||
sha = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
card = 4;
|
||||
sha = 2;
|
||||
target = 2;
|
||||
}
|
||||
|
||||
return `判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸${card}张牌;出牌阶段,你可以多使用${sha}张【杀】,你的【杀】可以多指定${target}名角色为目标。`;
|
||||
},
|
||||
zhanjia_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 3 : 2
|
||||
|
||||
return `锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸${num}张牌。`
|
||||
},
|
||||
|
||||
//董卓
|
||||
baonue_shuying(player, skill) {
|
||||
let num = 3;
|
||||
if (getTianshuDifficulty() == "hard") num = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 5;
|
||||
|
||||
return `锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为${num}。`
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default dynamicTranslates;
|
||||
@@ -0,0 +1,676 @@
|
||||
import { lib, game, ui, get, ai, _status, getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const skills = {
|
||||
//曹操 over
|
||||
jianxiong_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "damageEnd" },
|
||||
filter(event, player) {
|
||||
return event.num > 0;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player.chooseBool(get.prompt2(event.skill))
|
||||
.set("ai", () => true)
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
if (get.itemtype(trigger.cards) == "cards" && get.position(trigger.cards[0], true) == "o") {
|
||||
await player.gain(trigger.cards, "gain2");
|
||||
}
|
||||
await player.draw();
|
||||
},
|
||||
ai: {
|
||||
maixie: true,
|
||||
maixie_hp: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (player.hasSkillTag("jueqing", false, target)) return [1, -1];
|
||||
if (get.tag(card, "damage")) return [1, 0.6];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
lingba_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "phaseBegin" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn()) && game.players.every(current => player.countCards("h") >= current.countCards("h"));
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
const enemies = player.getEnemies(null, false).filter(target => target.isIn());
|
||||
if (player.countCards("h") >= player.hp * 2) {
|
||||
for (const target of enemies) {
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const target = enemies.randomGet();
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
}
|
||||
},
|
||||
},
|
||||
yishen_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "recoverBegin" },
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.countGainableCards(player, "e") > 0);
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player.chooseBool(get.prompt2(event.skill))
|
||||
.set("ai", () => {
|
||||
const player = get.player();
|
||||
const trigger = get.event().getTrigger();
|
||||
|
||||
if (player.hp <= 1) return false;
|
||||
if (player.hp + trigger.num <= 2) return false;
|
||||
|
||||
const enemies = player.getEnemies(null, false).filter(target => {
|
||||
return target.isIn() && target.countGainableCards(player, "e") > 0;
|
||||
});
|
||||
if (!enemies.length) return false;
|
||||
|
||||
const recoverEffect = get.recoverEffect(player, player, player);
|
||||
const difficulty = getTianshuDifficulty();
|
||||
|
||||
if (difficulty == "nightmare") {
|
||||
let total = 0;
|
||||
let max = 0;
|
||||
for (const target of enemies) {
|
||||
for (const card of target.getCards("e")) {
|
||||
const value = get.value(card, target);
|
||||
total += value;
|
||||
if (value > max) max = value;
|
||||
}
|
||||
}
|
||||
if (max < 5) return false;
|
||||
return total >= recoverEffect + 4;
|
||||
}
|
||||
|
||||
let best = 0;
|
||||
for (const target of enemies) {
|
||||
for (const card of target.getCards("e")) {
|
||||
const value = get.value(card, target);
|
||||
if (value > best) best = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (best < 5) return false;
|
||||
return best >= recoverEffect;
|
||||
})
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const enemies = player.getEnemies(null, false).filter(target => target.countGainableCards(player, "e") > 0);
|
||||
const difficulty = getTianshuDifficulty();
|
||||
|
||||
trigger.cancel();
|
||||
|
||||
if (difficulty == "nightmare") {
|
||||
for (const target of enemies) {
|
||||
player.line(target);
|
||||
await player.gainPlayerCard({ target, position: "e", forced: true });
|
||||
}
|
||||
}
|
||||
else {
|
||||
const target = enemies.randomGet();
|
||||
player.line(target);
|
||||
await player.gainPlayerCard({ target, position: "e", forced: true });
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.4,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.tag(card, "recover") && target.hasSkill("yishen_shuying")) {
|
||||
if (target.hp <= 1) return [1, 1.2];
|
||||
if (target.hp <= 2) return [1, 0.8];
|
||||
|
||||
const enemies = target.getEnemies(null, false).filter(current => {
|
||||
return current.isIn() && current.countGainableCards(target, "e") > 0;
|
||||
});
|
||||
if (!enemies.length) return;
|
||||
|
||||
let best = 0;
|
||||
for (const enemy of enemies) {
|
||||
for (const equip of enemy.getCards("e")) {
|
||||
best = Math.max(best, get.value(equip, enemy));
|
||||
}
|
||||
}
|
||||
|
||||
if (best >= 5) return [1, 0.4];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//司马懿 over
|
||||
fankui_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "damageEnd" },
|
||||
logTarget: "source",
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.num > 0 &&
|
||||
event.source?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.source) &&
|
||||
event.source.hasGainableCards(player, "he")
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
await player.gainPlayerCard({
|
||||
target: trigger.source,
|
||||
position: "he",
|
||||
forced: true,
|
||||
});
|
||||
},
|
||||
ai: {
|
||||
maixie_defend: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (player.countCards("he") > 1 && get.tag(card, "damage")) {
|
||||
if (player.hasSkillTag("jueqing", false, target)) return [1, -1.5];
|
||||
if (target.getEnemies(null, false).includes(player)) return [1, 1];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
guicai_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { global: "judge" },
|
||||
filter(event, player) {
|
||||
return player.countCards("hes") > 0;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player.chooseCard(`${get.translation(trigger.player)}的${trigger.judgestr || ""}判定为${get.translation(trigger.player.judging[0])},${get.prompt(event.skill)}`, "hes", card => {
|
||||
const player = get.player();
|
||||
const mod2 = game.checkMod(card, player, "unchanged", "cardEnabled2", player);
|
||||
if (mod2 != "unchanged") return mod2;
|
||||
const mod = game.checkMod(card, player, "unchanged", "cardRespondable", player);
|
||||
if (mod != "unchanged") return mod;
|
||||
return true;
|
||||
}).set("ai", card => {
|
||||
const trigger = get.event().getTrigger(), player = get.player(), judging = trigger.player.judging[0];
|
||||
const result = trigger.judge(card) - trigger.judge(judging);
|
||||
const attitude = player.getEnemies(null, false).includes(trigger.player) ? -1 : 1;
|
||||
let value = get.value(card, player);
|
||||
value /= get.subtype(card) == "equip2" ? 2 : 4;
|
||||
if (result == 0) return 0;
|
||||
return attitude > 0 ? result - value : -result - value;
|
||||
}).set("judging", trigger.player.judging[0]).forResult();
|
||||
},
|
||||
popup: false,
|
||||
async content(event, trigger, player) {
|
||||
const next = player.respond(event.cards, event.name, "highlight", "noOrdering");
|
||||
await next;
|
||||
const { cards } = next;
|
||||
if (cards?.length) {
|
||||
if (trigger.player.judging[0].clone) {
|
||||
trigger.player.judging[0].clone.classList.remove("thrownhighlight");
|
||||
game.broadcast(card => {
|
||||
if (card.clone) card.clone.classList.remove("thrownhighlight");
|
||||
}, trigger.player.judging[0]);
|
||||
game.addVideo("deletenode", player, get.cardsInfo([trigger.player.judging[0].clone]));
|
||||
}
|
||||
await game.cardsDiscard(trigger.player.judging[0]);
|
||||
trigger.player.judging[0] = cards[0];
|
||||
trigger.orderingCards.addArray(cards);
|
||||
game.log(trigger.player, "的判定牌改为", cards);
|
||||
await game.delay(2);
|
||||
}
|
||||
},
|
||||
ai: { rejudge: true, tag: { rejudge: 1 } },
|
||||
},
|
||||
langgu_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "gainAfter" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
if (player.hasSkill("langgu_shuying_used")) return false;
|
||||
return game.hasPlayer(current => {
|
||||
return (
|
||||
current != player &&
|
||||
player.getEnemies(null, false).includes(current) &&
|
||||
event.getl?.(current)?.cards2?.length
|
||||
);
|
||||
});
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const source = game.findPlayer(current => {
|
||||
return (
|
||||
current != player &&
|
||||
player.getEnemies(null, false).includes(current) &&
|
||||
trigger.getl?.(current)?.cards2?.length
|
||||
);
|
||||
});
|
||||
if (!source) return;
|
||||
|
||||
player.addTempSkill("langgu_shuying_used", "roundStart");
|
||||
|
||||
const result = await player.judge(card => get.suit(card) == "spade" ? 2 : 0).forResult();
|
||||
if (result.bool && source.isIn() && source.countDiscardableCards(player, "h") > 0) {
|
||||
player.removeSkill("langgu_shuying_used");
|
||||
player.line(source);
|
||||
const card = source.getCards("h").randomGet();
|
||||
await source.discard(card);
|
||||
}
|
||||
},
|
||||
subSkill: {
|
||||
used: { charlotte: true },
|
||||
},
|
||||
},
|
||||
yuanlv_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { source: "damageBegin4" },
|
||||
filter(event, player) {
|
||||
return event.card && get.type2(event.card) == "trick" && event.player?.isIn() && player.getEnemies(null, false).includes(event.player);
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player.chooseBool(get.prompt(event.skill, trigger.player))
|
||||
.set("ai", () => true)
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = trigger.player;
|
||||
trigger.cancel();
|
||||
await player.draw();
|
||||
target.line(player);
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
await player.damage(1, target, "unreal");
|
||||
}
|
||||
else {
|
||||
await player.damage(1, target);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
//吕布 over
|
||||
wushuang_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
|
||||
trigger: { source: "damageBegin1" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
usable(skill, player) {
|
||||
return getTianshuDifficulty() == "nightmare" ? 2 : 1;
|
||||
},
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
const target = event.player;
|
||||
const evtx = event.getParent(2);
|
||||
const card = event.card;
|
||||
if (!card || !["sha", "juedou"].includes(card.name)) return false;
|
||||
|
||||
if (card.name == "sha") {
|
||||
return !target.hasHistory("useCard", evt => {
|
||||
return evt.card.name == "shan" && evt.respondTo && evt.getParent(3) == evtx;
|
||||
});
|
||||
}
|
||||
|
||||
return !target.hasHistory("respond", evt => {
|
||||
return evt.card.name == "sha" && evt.respondTo && evt.getParent(3) == evtx;
|
||||
});
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num++;
|
||||
},
|
||||
group: ["wushuang_shuying_1", "wushuang_shuying_2"],
|
||||
subSkill: {
|
||||
1: {
|
||||
audio: "wushuang_shuying",
|
||||
sourceSkill: "wushuang_shuying",
|
||||
trigger: { player: "useCardToPlayered" },
|
||||
forced: true,
|
||||
logTarget: "target",
|
||||
filter(event, player) {
|
||||
return event.card.name == "sha" && !event.getParent().directHit.includes(event.target);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const id = trigger.target.playerid;
|
||||
const map = trigger.getParent().customArgs;
|
||||
if (!map[id]) map[id] = {};
|
||||
|
||||
if (typeof map[id].shanRequired == "number") map[id].shanRequired++;
|
||||
else map[id].shanRequired = 2;
|
||||
},
|
||||
ai: {
|
||||
directHit_ai: true,
|
||||
skillTagFilter(player, tag, arg) {
|
||||
if (arg.card.name != "sha" || arg.target.countCards("h", "shan") > 1) return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
2: {
|
||||
audio: "wushuang_shuying",
|
||||
sourceSkill: "wushuang_shuying",
|
||||
trigger: { player: "useCardToPlayered", target: "useCardToTargeted" },
|
||||
forced: true,
|
||||
logTarget(trigger, player) {
|
||||
return player == trigger.player ? trigger.target : trigger.player;
|
||||
},
|
||||
filter(event, player) {
|
||||
return event.card.name == "juedou";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const id = (player == trigger.player ? trigger.target : trigger.player).playerid;
|
||||
const idt = trigger.target.playerid;
|
||||
const map = trigger.getParent().customArgs;
|
||||
|
||||
if (!map[idt]) map[idt] = {};
|
||||
if (!map[idt].shaReq) map[idt].shaReq = {};
|
||||
if (!map[idt].shaReq[id]) map[idt].shaReq[id] = 1;
|
||||
|
||||
map[idt].shaReq[id]++;
|
||||
},
|
||||
ai: {
|
||||
directHit_ai: true,
|
||||
skillTagFilter(player, tag, arg) {
|
||||
if (
|
||||
arg.card.name != "juedou" ||
|
||||
Math.floor(arg.target.countCards("h", "sha") / 2) > player.countCards("h", "sha")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.8,
|
||||
directHit_ai: true,
|
||||
},
|
||||
},
|
||||
shenij_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
|
||||
group: [
|
||||
"shenij_shuying_judge",
|
||||
"shenij_shuying_draw",
|
||||
"shenij_shuying_sha",
|
||||
"shenij_shuying_target",
|
||||
],
|
||||
subSkill: {
|
||||
judge: {
|
||||
audio: "shenij_shuying",
|
||||
trigger: { player: "phaseJudgeBegin" },
|
||||
filter(event, player) {
|
||||
return player.countCards("h") >= 2 && player.countCards("j") > 0;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player
|
||||
.chooseToDiscard("h", 2, get.prompt2(event.skill))
|
||||
.set("ai", card => {
|
||||
const player = get.player();
|
||||
if (!player.countCards("j")) return 0;
|
||||
return 7 - get.value(card);
|
||||
})
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const cards = player.getCards("j");
|
||||
await player.discard(cards);
|
||||
},
|
||||
ai: {
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.type(card) == "delay") return [0, 2];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
draw: {
|
||||
audio: "shenij_shuying",
|
||||
trigger: { player: "phaseDrawBegin2" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return !event.numFixed;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let num = 2;
|
||||
if (getTianshuDifficulty() == "hard") num = 3;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 4;
|
||||
|
||||
trigger.num += num;
|
||||
},
|
||||
},
|
||||
sha: {
|
||||
audio: "shenij_shuying",
|
||||
forced: true,
|
||||
mod: {
|
||||
cardUsable(card, player, num) {
|
||||
let shaNum = 2;
|
||||
if (getTianshuDifficulty() == "normal") shaNum = 1;
|
||||
|
||||
if (card.name == "sha") return num + shaNum;
|
||||
},
|
||||
},
|
||||
},
|
||||
target: {
|
||||
audio: "shenij_shuying",
|
||||
trigger: { player: "useCard2" },
|
||||
filter(event, player) {
|
||||
if (event.card.name != "sha") return false;
|
||||
|
||||
const info = get.info(event.card);
|
||||
if (info.allowMultiple == false || info.multitarget || !event.targets) return false;
|
||||
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
|
||||
return game.countPlayer(current => {
|
||||
return !event.targets.includes(current) &&
|
||||
lib.filter.targetEnabled2(event.card, player, current) &&
|
||||
lib.filter.targetInRange(event.card, player, current);
|
||||
}) > 0;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
|
||||
|
||||
event.result = await player
|
||||
.chooseTarget(
|
||||
get.prompt(event.skill),
|
||||
`为此【杀】额外指定至多${get.cnNumber(num)}名目标`,
|
||||
[1, num],
|
||||
(card, player, target) => {
|
||||
const trigger = get.event().getTrigger();
|
||||
return !trigger.targets.includes(target) &&
|
||||
lib.filter.targetEnabled2(trigger.card, player, target) &&
|
||||
lib.filter.targetInRange(trigger.card, player, target);
|
||||
}
|
||||
)
|
||||
.set("ai", target => {
|
||||
const player = get.player();
|
||||
if (!player.getEnemies(null, false).includes(target)) return 0;
|
||||
return get.effect(target, get.event().getTrigger().card, player, player);
|
||||
})
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
for (const target of event.targets) {
|
||||
player.line(target);
|
||||
trigger.targets.add(target);
|
||||
game.log(target, "成为了", trigger.card, "的额外目标");
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
effect: {
|
||||
player_use(card, player, target) {
|
||||
if (card.name == "sha" && player.getEnemies(null, false).includes(target)) return [1, 0.5];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zhanjia_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:2",
|
||||
trigger: { player: "damageBegin4" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
usable: 1,
|
||||
filter(event, player) {
|
||||
return event.num > 2;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num = 2;
|
||||
await player.draw(getTianshuDifficulty() == "nightmare" ? 3 : 2);
|
||||
},
|
||||
ai: {
|
||||
filterDamage: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.tag(card, "damage")) {
|
||||
if (player.hasSkillTag("jueqing", false, target)) return;
|
||||
return [1, 0.8];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//董卓 over
|
||||
jiuchi_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
enable: "chooseToUse",
|
||||
filterCard(card, player) {
|
||||
return get.suit(card, player) == "spade";
|
||||
},
|
||||
position: "h",
|
||||
viewAs: { name: "jiu" },
|
||||
viewAsFilter(player) {
|
||||
return player.countCards("h", card => get.suit(card, player) == "spade") > 0;
|
||||
},
|
||||
prompt: "将一张黑桃手牌当【酒】使用",
|
||||
check(card) {
|
||||
return 6 - get.value(card);
|
||||
},
|
||||
ai: {
|
||||
order: 3.5,
|
||||
respondJiu: true,
|
||||
skillTagFilter(player) {
|
||||
return player.countCards("h", card => get.suit(card, player) == "spade") > 0;
|
||||
},
|
||||
},
|
||||
},
|
||||
roulin_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "useCardToPlayered", target: "useCardToTargeted" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
logTarget(trigger, player) {
|
||||
return player == trigger.player ? trigger.target : trigger.player;
|
||||
},
|
||||
filter(event, player) {
|
||||
if (event.card.name != "sha") return false;
|
||||
if (player == event.player) return event.target.hasSex("female");
|
||||
return event.player.hasSex("female");
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const id = (player == trigger.player ? trigger.target : player).playerid;
|
||||
const map = trigger.getParent().customArgs;
|
||||
if (!map[id]) map[id] = {};
|
||||
|
||||
if (typeof map[id].shanRequired == "number") map[id].shanRequired++;
|
||||
else map[id].shanRequired = 2;
|
||||
},
|
||||
ai: {
|
||||
halfneg: true,
|
||||
directHit_ai: true,
|
||||
skillTagFilter(player, tag, arg) {
|
||||
if (tag === "directHit_ai") return;
|
||||
if (arg.card.name != "sha" || !arg.target.hasSex("female") || arg.target.countCards("h", "shan") > 1) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
baonue_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { player: "phaseBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return player.getDamagedHp() > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let maxNum = 3;
|
||||
if (getTianshuDifficulty() == "hard") maxNum = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") maxNum = 5;
|
||||
|
||||
const num = Math.min(maxNum, player.getDamagedHp());
|
||||
await player.draw(num);
|
||||
|
||||
const result = await player
|
||||
.chooseTarget(`对至多${get.cnNumber(num)}名角色造成1点伤害`, [1, num], (card, player, target) => {
|
||||
return target.isIn() && player.getEnemies(null, false).includes(target);
|
||||
})
|
||||
.set("ai", target => {
|
||||
const player = get.player();
|
||||
return get.damageEffect(target, player, player);
|
||||
})
|
||||
.forResult();
|
||||
|
||||
if (result.bool) {
|
||||
for (const target of result.targets) {
|
||||
player.line(target);
|
||||
await target.damage(1, player);
|
||||
}
|
||||
}
|
||||
|
||||
await player.loseHp();
|
||||
},
|
||||
ai: {
|
||||
threaten: 2,
|
||||
},
|
||||
},
|
||||
qubu_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/青青子衿:1",
|
||||
trigger: { global: "useCardToPlayered" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.card.name == "sha" &&
|
||||
event.player?.isIn() &&
|
||||
!player.getEnemies(null, false).includes(event.player) &&
|
||||
event.targets?.length > 0
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const result = await player
|
||||
.judge(card => get.color(card) == "black" ? 2 : -1)
|
||||
.set("judge2", result => result.bool)
|
||||
.forResult();
|
||||
|
||||
if (result.bool) {
|
||||
const targets = trigger.targets.filter(target => target.isIn());
|
||||
for (const target of targets) {
|
||||
player.line(target);
|
||||
await target.damage(1, player);
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.8,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default skills;
|
||||
@@ -0,0 +1,41 @@
|
||||
const translates = {
|
||||
caocao_boss_shuying: "曹操",
|
||||
jianxiong_shuying: "奸雄",
|
||||
jianxiong_shuying_info: "当你受到伤害后,你可以获得对你造成伤害的牌并摸一张牌。",
|
||||
lingba_shuying: "凌霸",
|
||||
lingba_shuying_info: "锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成1点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。",
|
||||
yishen_shuying: "疑神",
|
||||
yishen_shuying_info: "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。",
|
||||
|
||||
simayi_boss_shuying: "司马懿",
|
||||
fankui_shuying: "反馈",
|
||||
fankui_shuying_info: "当你受到1点伤害后,你可以获得伤害来源的一张牌。",
|
||||
guicai_shuying: "鬼才",
|
||||
guicai_shuying_info: "当一名角色的判定牌生效前,你可以打出一张牌代替之。",
|
||||
langgu_shuying: "狼顾",
|
||||
langgu_shuying_info: " 锁定技,每回合限一次,当你获得其他角色的牌时,进行一次判定:若结果为黑桃,随机弃置其1张手牌,且视为此技能本回合未发动过。",
|
||||
yuanlv_shuying: "远虑",
|
||||
yuanlv_shuying_info: "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。",
|
||||
|
||||
lvbu_boss_shuying: "吕布",
|
||||
wushuang_shuying: "无双",
|
||||
wushuang_shuying_info: "锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限1次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。",
|
||||
shenij_shuying: "神戟",
|
||||
shenij_shuying_info: "判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸两张牌;出牌阶段,你可以多使用一张【杀】,你的【杀】可以多指定一名角色为目标。",
|
||||
zhanjia_shuying: "战甲",
|
||||
zhanjia_shuying_info: "锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸两张牌。",
|
||||
|
||||
dongzhuo_boss_shuying: "董卓",
|
||||
jiuchi_shuying: "酒池",
|
||||
jiuchi_shuying_info: "你可以将一张黑桃手牌当【酒】使用。",
|
||||
roulin_shuying: "肉林",
|
||||
roulin_shuying_info: "锁定技,你对女性角色使用的【杀】和女性角色对你使用的【杀】均需使用两张【闪】才能抵消。",
|
||||
baonue_shuying: "暴虐",
|
||||
baonue_shuying_info: "锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为3。",
|
||||
qubu_shuying: "驭布",
|
||||
qubu_shuying_info: "锁定技,当友方角色使用【杀】指定目标时,进行一次判定:若结果为黑桃,你对此【杀】的所有目标造成1点伤害。",
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default translates;
|
||||
@@ -0,0 +1,70 @@
|
||||
const characters = {
|
||||
zishu_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["zishu_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/zishu_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/zishu.mp3"]
|
||||
},
|
||||
// chouniu_boss_shuying: {},
|
||||
// yinhu_boss_shuying: {},
|
||||
// maotu_boss_shuying: {},
|
||||
chenlong_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["chenlong_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/chenlong_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/chenlong.mp3"]
|
||||
},
|
||||
// sishe_boss_shuying: {},
|
||||
wuma_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["wuma_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/wuma_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/wuma_shuying.mp3"]
|
||||
},
|
||||
weiyang_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["weiyang_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/weiyang_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/weiyang.mp3"]
|
||||
},
|
||||
shenhou_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["shenhou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/shenhou_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/shenhou.mp3"]
|
||||
},
|
||||
// youji_boss_shuying: {},
|
||||
xvgou_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["xvgou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/十二生肖/xvgou_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/十二生肖/xvgou.mp3"]
|
||||
},
|
||||
// haizhu_boss_shuying: {},
|
||||
};
|
||||
|
||||
export default characters;
|
||||
@@ -0,0 +1,297 @@
|
||||
import { lib, game, ui, get, ai, _status, getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const skills = {
|
||||
//子鼠
|
||||
zishu_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
enable: "phaseUse",
|
||||
usable: 1,
|
||||
filter(event, player) {
|
||||
return game.hasPlayer(current => {
|
||||
return current != player && current.countCards("h") > player.countCards("h");
|
||||
});
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
while (player.isIn()) {
|
||||
const max = Math.max(...game.players.map(current => current.countCards("h")));
|
||||
if (player.countCards("h") >= max) break;
|
||||
|
||||
const targets = game.filterPlayer(current => {
|
||||
return current != player && current.countCards("h") > player.countCards("h");
|
||||
});
|
||||
if (!targets.length) break;
|
||||
|
||||
const hasEnemy = targets.some(target => player.getEnemies(null, false).includes(target));
|
||||
|
||||
const result = await player
|
||||
.chooseTarget("恣疏:获得一名手牌数大于你的角色一张手牌", (card, player, target) => {
|
||||
return target != player && target.countCards("h") > player.countCards("h");
|
||||
})
|
||||
.set("ai", target => {
|
||||
const player = get.player();
|
||||
const enemies = player.getEnemies(null, false);
|
||||
const isEnemy = enemies.includes(target);
|
||||
const hasEnemy = game.hasPlayer(current => {
|
||||
return current != player && current.countCards("h") > player.countCards("h") && enemies.includes(current);
|
||||
});
|
||||
|
||||
if (hasEnemy && !isEnemy) return 0;
|
||||
if (isEnemy) return 8 + target.countCards("h") - get.attitude(player, target);
|
||||
return 1 + target.countCards("h") / 10;
|
||||
})
|
||||
.forResult();
|
||||
|
||||
if (!result.bool) break;
|
||||
|
||||
const target = result.targets[0];
|
||||
player.line(target);
|
||||
await player.gainPlayerCard({
|
||||
target,
|
||||
position: "h",
|
||||
forced: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
order: 4.5,
|
||||
result: {
|
||||
player(player) {
|
||||
const enemies = player.getEnemies(null, false);
|
||||
if (game.hasPlayer(current => current != player && current.countCards("h") > player.countCards("h") && enemies.includes(current))) {
|
||||
return 1;
|
||||
}
|
||||
if (game.hasPlayer(current => current != player && current.countCards("h") > player.countCards("h") && !enemies.includes(current))) {
|
||||
return 0.2;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
threaten: 1.4,
|
||||
},
|
||||
},
|
||||
|
||||
//辰龙
|
||||
chenlong_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
enable: "phaseUse",
|
||||
limited: true,
|
||||
skillAnimation: true,
|
||||
animationColor: "fire",
|
||||
selectTarget: 1,
|
||||
filter(event, player) {
|
||||
return !player.storage.chenlong_shuying_used && game.hasPlayer(current => current != player && player.getEnemies(null, false).includes(current));
|
||||
},
|
||||
filterTarget(card, player, target) {
|
||||
return target != player && player.getEnemies(null, false).includes(target);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = event.targets[0];
|
||||
const result = await player.chooseControl([1, 2, 3, 4, 5])
|
||||
.set("prompt", "失去任意点体力,然后对一名其他角色造成等量伤害")
|
||||
.set("ai", () => 4)
|
||||
.forResult();
|
||||
const num = result.control;
|
||||
if (!num) return;
|
||||
player.storage.chenlong_shuying_used = true;
|
||||
player.storage.chenlong_shuying_dying = true;
|
||||
await player.loseHp(num);
|
||||
delete player.storage.chenlong_shuying_dying;
|
||||
player.awakenSkill(event.name);
|
||||
if (target.isIn()) {
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
order: 4,
|
||||
result: {
|
||||
target(player, target) {
|
||||
if (!player.getEnemies(null, false).includes(target)) return 0;
|
||||
const effect = get.damageEffect(target, player, target);
|
||||
if (effect >= 0) return 0;
|
||||
return target.hp <= 2 ? effect * 2 : effect;
|
||||
},
|
||||
},
|
||||
},
|
||||
group: "chenlong_shuying_dying",
|
||||
subSkill: {
|
||||
dying: {
|
||||
trigger: { player: ["dying", "dyingAfter"] },
|
||||
forced: true,
|
||||
popup: false,
|
||||
charlotte: true,
|
||||
filter(event, player) {
|
||||
return player.storage.chenlong_shuying_dying && player.hp <= 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
await player.recoverTo(1);
|
||||
if (player.maxHp > 0) await player.loseMaxHp();
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//午马
|
||||
wuma_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
trigger: { target: "useCardToTargeted" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return event.player != player && get.type2(event.card) == "trick";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
await player.draw();
|
||||
},
|
||||
group: ["wuma_shuying_turn", "wuma_shuying_skip"],
|
||||
subSkill: {
|
||||
turn: {
|
||||
audio: "wuma_shuying",
|
||||
trigger: { player: "turnOverBefore" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return !player.isTurnedOver();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.cancel();
|
||||
},
|
||||
},
|
||||
skip: {
|
||||
audio: "wuma_shuying",
|
||||
trigger: { player: ["phaseAnySkipped", "phaseAnyCancelled"] },
|
||||
forced: true,
|
||||
async content(event, trigger, player) {
|
||||
game.log(player, "恢复了", trigger.name);
|
||||
await player[trigger.name]();
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (player != target && get.type2(card) == "trick") return [1, 0.5];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//未羊
|
||||
weiyang_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
enable: "phaseUse",
|
||||
usable: 1,
|
||||
position: "he",
|
||||
selectCard: [1, Infinity],
|
||||
filter(event, player) {
|
||||
return game.hasPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
|
||||
},
|
||||
filterCard(card, player) {
|
||||
const type = get.type(card);
|
||||
const friends = game.countPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
|
||||
return ui.selected.cards.length < friends && !ui.selected.cards.some(cardx => get.type(cardx) == type);
|
||||
},
|
||||
check(card) {
|
||||
return 6 - get.value(card);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const num = event.cards.length;
|
||||
const friends = game.filterPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current));
|
||||
if (!friends.length) return;
|
||||
const result = await player.chooseTarget(`令至多${get.cnNumber(num)}名角色回复1点体力`, [1, Math.min(num, friends.length)], (card, player, target) => {
|
||||
return target.isDamaged() && !player.getEnemies(null, false).includes(target);
|
||||
}).set("ai", target => get.recoverEffect(target, get.player(), get.player())).forResult();
|
||||
if (result.bool) {
|
||||
for (const target of result.targets) {
|
||||
player.line(target);
|
||||
await target.recover();
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
order: 6,
|
||||
result: {
|
||||
player(player) {
|
||||
return game.hasPlayer(current => current.isDamaged() && !player.getEnemies(null, false).includes(current) && get.recoverEffect(current, player, player) > 0) ? 1 : 0;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//申猴
|
||||
shenhou_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
trigger: { target: "useCardToTargeted" },
|
||||
filter(event, player) {
|
||||
return event.card.name == "sha";
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player
|
||||
.chooseBool(get.prompt2(event.skill))
|
||||
.set("ai", () => {
|
||||
const player = get.player();
|
||||
const trigger = get.event().getTrigger();
|
||||
return get.effect(player, trigger.card, trigger.player, player) < 0;
|
||||
})
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const result = await player
|
||||
.judge(card => get.color(card) == "red" ? 2 : -1)
|
||||
.set("judge2", result => result.bool)
|
||||
.forResult();
|
||||
|
||||
if (result.bool) {
|
||||
trigger.getParent().excluded.add(player);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (card.name == "sha") return [1, 0.6];
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//戌狗
|
||||
xvgou_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/十二生肖:1",
|
||||
forced: true,
|
||||
group: ["xvgou_shuying_cancel", "xvgou_shuying_damage"],
|
||||
mod: {
|
||||
targetInRange(card, player, target) {
|
||||
if (card.name == "sha" && get.color(card) == "red") return true;
|
||||
},
|
||||
},
|
||||
subSkill: {
|
||||
cancel: {
|
||||
trigger: { target: "useCardToTargeted" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return event.card.name == "sha" && get.color(event.card) == "red";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.getParent().excluded.add(player);
|
||||
},
|
||||
},
|
||||
damage: {
|
||||
trigger: { source: "damageBegin1" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return event.card?.name == "sha" && get.color(event.card) == "red";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num++;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default skills;
|
||||
@@ -0,0 +1,28 @@
|
||||
const translates = {
|
||||
zishu_boss_shuying: "子鼠",
|
||||
zishu_shuying: "子鼠",
|
||||
zishu_shuying_info: "出牌阶段限一次,你可以获得手牌数大于你的其他角色一张手牌,你可以重复此流程,直到你的手牌数为全场最多。",
|
||||
|
||||
chenlong_boss_shuying: "辰龙",
|
||||
chenlong_shuying: "辰龙",
|
||||
chenlong_shuying_info: "限定技,出牌阶段,你可以失去任意点体力(至多为5),然后对一名其他角色造成等量的伤害。若你以此法进入濒死状态,则你将体力值回复至1,然后减少1点体力上限。",
|
||||
|
||||
wuma_boss_shuying: "午马",
|
||||
wuma_shuying: "午马",
|
||||
wuma_shuying_info: "锁定技,你不能被翻面;你的阶段不能被跳过;当你成为其他角色使用锦囊牌的目标后,摸一张牌。",
|
||||
|
||||
weiyang_boss_shuying: "未羊",
|
||||
weiyang_shuying: "未羊",
|
||||
weiyang_shuying_info: "出牌阶段限一次,你可以弃置任意张不同类型的牌,然后令至多等量角色回复1点体力。",
|
||||
|
||||
shenhou_boss_shuying: "申猴",
|
||||
shenhou_shuying: "申猴",
|
||||
shenhou_shuying_info: "当你成为【杀】的目标时,你可以进行判定,若结果为红色,则此【杀】对你无效。",
|
||||
|
||||
xvgou_boss_shuying: "戌狗",
|
||||
xvgou_shuying: "戌狗",
|
||||
xvgou_shuying_info: "锁定技,红色【杀】对你无效;你使用红色【杀】无距离限制且造成伤害+1.",
|
||||
|
||||
};
|
||||
|
||||
export default translates;
|
||||
@@ -0,0 +1,65 @@
|
||||
const characters = {
|
||||
shuishengonggong_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["tuanliu_shuying", "juehong_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/shuishengonggong_boss_shuying.png"
|
||||
},
|
||||
// huoshenzhurong_boss_shuying: {},
|
||||
baiqi_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["changsheng_shuying", "shashen_shuying", "wuan_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/baiqi_boss_shuying.png"
|
||||
},
|
||||
kuafu_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["shenqu_shuying", "lieben_shuying", "yinjiang_shuying", "zhuri_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/kuafu_boss_shuying.png"
|
||||
},
|
||||
shaohao_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["baiyi_shuying", "shenen_shuying",],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/shaohao_boss_shuying.png"
|
||||
},
|
||||
xuannv_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["xuanlie_shuying", "jiutian_shuying", "dishi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/xuannv_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/天书乱斗/xuannv_boss_shuying.mp3"]
|
||||
},
|
||||
hanba_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "shen",
|
||||
hp: 4,
|
||||
skills: ["xinji_shuying", "zhiri_shuying", "fenshi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/天书乱斗/hanba_boss_shuying.png",
|
||||
dieAudios: ["ext:术樱包/pve/audio/dead/天书乱斗/hanba_boss_shuying.mp3"]
|
||||
},
|
||||
};
|
||||
|
||||
export default characters;
|
||||
@@ -0,0 +1,67 @@
|
||||
import { getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const dynamicTranslates = {
|
||||
//水神共工
|
||||
tuanliu_shuying(player, skill) {
|
||||
let hp = 2;
|
||||
let card = 4;
|
||||
let damage = 1;
|
||||
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
hp = 3;
|
||||
card = 6;
|
||||
}
|
||||
return `锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复${hp}点体力;大于5,你摸${card}张牌;大于9,你对所有敌方角色造成${damage}点伤害。`
|
||||
},
|
||||
|
||||
//少昊
|
||||
baiyi_shuying(player, skill) {
|
||||
let num = 1;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
return `锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其${num}张牌;若当前轮数小于5,对其造成${num}点雷电伤害;若当前轮数小于7,其随机弃置${num}张牌。`;
|
||||
},
|
||||
|
||||
//玄女
|
||||
xuanlie_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "normal" ? 1 : 2;
|
||||
|
||||
return `锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成${num}点伤害。`;
|
||||
},
|
||||
jiutian_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "normal" ? 1 : 2;
|
||||
const extra =
|
||||
difficulty == "normal" || difficulty == "hard"
|
||||
? ""
|
||||
: "若这些牌包含4种花色,这些角色再额外失去1点体力。";
|
||||
|
||||
return `锁定技,准备阶段,你获得所有敌方角色各${num}张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。${extra}`;
|
||||
},
|
||||
|
||||
xinji_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "nightmare" ? 2 : 1;
|
||||
|
||||
return `锁定技,当友方于回合外因弃置而失去手牌时,你对当前角色造成${num}点伤害。`
|
||||
},
|
||||
zhiri_shuying(player, skill) {
|
||||
let num = 1;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
return `锁定技,当敌方角色使用红色锦囊牌指定目标后,你摸${num}张牌。`
|
||||
},
|
||||
};
|
||||
|
||||
export default dynamicTranslates;
|
||||
@@ -0,0 +1,584 @@
|
||||
import { lib, game, ui, get, ai, _status, getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const skills = {
|
||||
//水神共工 over
|
||||
shuishen_shuying: {
|
||||
mode: ["boss"],
|
||||
forced: true,
|
||||
group: ["shuishen_shuying_recover", "shuishen_shuying_draw", "shuishen_shuying_damage"],
|
||||
subSkill: {
|
||||
recover: {
|
||||
trigger: { player: "recoverBegin" },
|
||||
audio: false,
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return game.shuying_getDiscardNamesNum() >= 4;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num++;
|
||||
},
|
||||
},
|
||||
draw: {
|
||||
trigger: { player: "phaseDrawBegin2" },
|
||||
audio: false,
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return !event.numFixed && game.shuying_getDiscardNamesNum() >= 5;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num += 5;
|
||||
},
|
||||
},
|
||||
damage: {
|
||||
trigger: { source: "damageBegin1" },
|
||||
audio: false,
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return game.shuying_getDiscardNamesNum() >= 9;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num++;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tuanliu_shuying: {
|
||||
mode: ["boss"],
|
||||
trigger: { player: "phaseJieshuBegin" },
|
||||
audio: false,
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return game.shuying_getDiscardNum() > 4;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let hp = 2;
|
||||
let card = 4;
|
||||
let damage = 1;
|
||||
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
hp = 3;
|
||||
card = 6;
|
||||
}
|
||||
|
||||
|
||||
const num = game.shuying_getDiscardNum();
|
||||
if (num > 4) await player.recover(hp);
|
||||
if (num > 5) await player.draw(card);
|
||||
if (num > 9) {
|
||||
for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
|
||||
player.line(target);
|
||||
await target.damage(player, damage);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
juehong_shuying: {
|
||||
mode: ["boss"],
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
audio: false,
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn());
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
|
||||
player.line(target);
|
||||
if (target.countCards("e")) await target.chooseToDiscard("e", true, target.countCards("e"));
|
||||
else if (target.countCards("h")) await target.discard(target.getCards("h").randomGets(Math.min(2, target.countCards("h"))));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
//白起
|
||||
changsheng_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
forced: true,
|
||||
mod: {
|
||||
targetInRange(card, player, target) {
|
||||
if (card.name == "sha") return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
shashen_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
enable: ["chooseToUse", "chooseToRespond"],
|
||||
filterCard: true,
|
||||
position: "h",
|
||||
viewAs: { name: "sha" },
|
||||
viewAsFilter(player) {
|
||||
return player.countCards("h") > 0;
|
||||
},
|
||||
prompt: "将一张手牌当【杀】使用或打出",
|
||||
check(card) {
|
||||
return 6 - get.value(card);
|
||||
},
|
||||
group: "shashen_shuying_draw",
|
||||
subSkill: {
|
||||
draw: {
|
||||
trigger: { source: "damageEnd" },
|
||||
filter(event, player) {
|
||||
return event.card?.name == "sha";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
await player.draw(3);
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
respondSha: true,
|
||||
skillTagFilter(player) {
|
||||
return player.countCards("h") > 0;
|
||||
},
|
||||
order: 4,
|
||||
},
|
||||
},
|
||||
wuan_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
forced: true,
|
||||
mod: {
|
||||
cardUsable(card, player, num) {
|
||||
if (card.name == "sha") return num + 3;
|
||||
},
|
||||
},
|
||||
trigger: { source: "damageBegin1" },
|
||||
filter(event, player) {
|
||||
return event.card?.name == "sha";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num++;
|
||||
},
|
||||
},
|
||||
|
||||
//盘古
|
||||
shenqu_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
trigger: { player: "damageEnd" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.countCards("h", card => get.color(card) == "red") > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const cards = player.getCards("h", card => get.color(card) == "red");
|
||||
await player.lose(cards, ui.cardPile, "insert");
|
||||
await player.draw(cards.length);
|
||||
},
|
||||
},
|
||||
lieben_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
trigger: { player: "useCardToPlayered" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return event.card.name == "sha" && ui.cardPile.lastChild;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const judge = player.judge(card => get.color(card) == "red" ? 2 : -1);
|
||||
judge.directresult = ui.cardPile.lastChild;
|
||||
judge.judge2 = result => result.bool;
|
||||
const result = await judge.forResult();
|
||||
if (result.bool) {
|
||||
trigger.getParent().addCount = false;
|
||||
trigger.getParent().baseDamage++;
|
||||
}
|
||||
},
|
||||
},
|
||||
yinjiang_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
trigger: { player: "drawEnd" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.isPhaseUsing() && !player.hasSkill("yinjiang_shuying_disabled") && ui.cardPile.lastChild;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const card = ui.cardPile.lastChild;
|
||||
await player.gain(card, "gain2");
|
||||
if (get.color(card) == "red") {
|
||||
let count = 0;
|
||||
for (const target of player.getEnemies(null, false).filter(target => target.isIn())) {
|
||||
player.line(target);
|
||||
await target.damage(player);
|
||||
count++;
|
||||
}
|
||||
player.storage.yinjiang_shuying_count = (player.storage.yinjiang_shuying_count || 0) + count;
|
||||
if (player.storage.yinjiang_shuying_count >= 2) player.addTempSkill("yinjiang_shuying_disabled", { player: "phaseUseEnd" });
|
||||
}
|
||||
},
|
||||
group: "yinjiang_shuying_clear",
|
||||
subSkill: {
|
||||
clear: {
|
||||
trigger: { player: "phaseUseEnd" },
|
||||
silent: true,
|
||||
async content(event, trigger, player) {
|
||||
delete player.storage.yinjiang_shuying_count;
|
||||
},
|
||||
},
|
||||
disabled: { charlotte: true },
|
||||
},
|
||||
},
|
||||
zhuri_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill:1",
|
||||
forced: true,
|
||||
group: ["zhuri_shuying_draw", "zhuri_shuying_bottom"],
|
||||
subSkill: {
|
||||
draw: {
|
||||
trigger: { player: "useCard" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return get.color(event.card) == "red";
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
await player.draw();
|
||||
},
|
||||
},
|
||||
bottom: {
|
||||
trigger: { global: "useCardAfter" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return event.targets?.includes(player) && (event.card.name == "sha" || get.type(event.card) == "trick") && event.cards?.filterInD("d").length;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const cards = trigger.cards.filterInD("d");
|
||||
game.log(cards, "被置于牌堆底");
|
||||
for (const card of cards) ui.cardPile.appendChild(card);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//少昊 over
|
||||
baiyi_shuying: {
|
||||
mode: ["boss"],
|
||||
trigger: { global: "phaseBegin" },
|
||||
forced: true,
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
return (
|
||||
game.roundNumber < 7 &&
|
||||
event.player?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.player)
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let num = 1;
|
||||
const target = trigger.player;
|
||||
player.line(target);
|
||||
|
||||
const difficulty = getTianshuDifficulty();
|
||||
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
if (game.roundNumber < 3 && target.countGainableCards(player, "he") > 0) {
|
||||
const cards = target.getCards("he").randomGets(Math.min(num, target.countCards("he")));
|
||||
await player.gain(cards, target, "giveAuto");
|
||||
}
|
||||
|
||||
if (game.roundNumber < 5 && target.isIn()) {
|
||||
await target.damage(num, "thunder", player);
|
||||
}
|
||||
|
||||
if (game.roundNumber < 7 && target.isIn()) {
|
||||
const cards = target
|
||||
.getCards("he")
|
||||
.filter(card => lib.filter.cardDiscardable(card, target, "baiyi_shuying"))
|
||||
.randomGets(num);
|
||||
|
||||
if (cards.length) {
|
||||
await target.discard(cards);
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 3,
|
||||
},
|
||||
},
|
||||
shenen_shuying: {
|
||||
mode: ["boss"],
|
||||
forced: true,
|
||||
global: "shenen_shuying_global",
|
||||
subSkill: {
|
||||
global: {
|
||||
mod: {
|
||||
targetInRange(card, player, target) {
|
||||
const owner = game.findPlayer(current => current.hasSkill("shenen_shuying"));
|
||||
if (owner && !owner.getEnemies(null, false).includes(player)) return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.5,
|
||||
},
|
||||
},
|
||||
|
||||
//玄女 over
|
||||
xuanlie_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
|
||||
trigger: { player: "phaseJieshuBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return game.hasPlayer(current => {
|
||||
return (
|
||||
current.isIn() &&
|
||||
player.getEnemies(null, false).includes(current) &&
|
||||
player.getHistory("gain", evt => evt.getl?.(current)?.cards2?.length).length
|
||||
);
|
||||
});
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const targets = game.filterPlayer(current => {
|
||||
return (
|
||||
current.isIn() &&
|
||||
player.getEnemies(null, false).includes(current) &&
|
||||
player.getHistory("gain", evt => evt.getl?.(current)?.cards2?.length).length
|
||||
);
|
||||
});
|
||||
|
||||
let num = 2;
|
||||
if (getTianshuDifficulty() == "normal") num = 1;
|
||||
|
||||
for (const target of targets) {
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 2,
|
||||
},
|
||||
},
|
||||
jiutian_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn() && target.countGainableCards(player, "h") > 0);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let num = 2;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "normal") num = 1;
|
||||
|
||||
const targets = player.getEnemies(null, false).filter(target => target.isIn() && target.countGainableCards(player, "h") > 0);
|
||||
const gained = [];
|
||||
|
||||
for (const target of targets) {
|
||||
const cards = target.getCards("h").randomGets(Math.min(num, target.countCards("h")));
|
||||
if (cards.length) {
|
||||
player.line(target);
|
||||
await player.gain(cards, target, "giveAuto");
|
||||
gained.addArray(cards);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gained.length) return;
|
||||
|
||||
const colors = gained.map(card => get.color(card, player)).unique();
|
||||
const suits = gained.map(card => get.suit(card, player)).unique();
|
||||
|
||||
if (colors.length >= 2) {
|
||||
for (const target of targets) {
|
||||
if (!target.isIn()) continue;
|
||||
player.line(target);
|
||||
|
||||
target.damage(1, player);
|
||||
}
|
||||
}
|
||||
|
||||
if (difficulty == "nightmare" && suits.length >= 4) {
|
||||
for (const target of targets) {
|
||||
if (!target.isIn()) continue;
|
||||
player.line(target);
|
||||
await target.loseHp();
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 3,
|
||||
},
|
||||
},
|
||||
dishi_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:1",
|
||||
trigger: { player: "useCard2" },
|
||||
filter(event, player) {
|
||||
if (!event.targets?.length) return false;
|
||||
if (event.card.name != "sha" && get.type(event.card) != "trick") return false;
|
||||
|
||||
const info = get.info(event.card);
|
||||
if (event.targets.length == 1) {
|
||||
if (info.allowMultiple == false || info.multitarget) return false;
|
||||
return game.hasPlayer(current => {
|
||||
return (
|
||||
!event.targets.includes(current) &&
|
||||
lib.filter.targetEnabled2(event.card, player, current) &&
|
||||
lib.filter.targetInRange(event.card, player, current)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return event.targets.length > 1;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
if (trigger.targets.length == 1) {
|
||||
event.result = await player
|
||||
.chooseTarget(get.prompt(event.skill), `为${get.translation(trigger.card)}增加一个目标`, (card, player, target) => {
|
||||
const trigger = get.event().getTrigger();
|
||||
return (
|
||||
!trigger.targets.includes(target) &&
|
||||
lib.filter.targetEnabled2(trigger.card, player, target) &&
|
||||
lib.filter.targetInRange(trigger.card, player, target)
|
||||
);
|
||||
})
|
||||
.set("ai", target => {
|
||||
const player = get.player();
|
||||
const trigger = get.event().getTrigger();
|
||||
const effect = get.effect(target, trigger.card, player, player);
|
||||
if (player.getEnemies(null, false).includes(target)) return effect;
|
||||
return Math.min(0, effect);
|
||||
})
|
||||
.forResult();
|
||||
}
|
||||
else {
|
||||
event.result = await player
|
||||
.chooseTarget(get.prompt(event.skill), `令${get.translation(trigger.card)}减少一个目标`, (card, player, target) => {
|
||||
return get.event().getTrigger().targets.includes(target);
|
||||
})
|
||||
.set("ai", target => {
|
||||
const player = get.player();
|
||||
const trigger = get.event().getTrigger();
|
||||
return -get.effect(target, trigger.card, player, player);
|
||||
})
|
||||
.forResult();
|
||||
}
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = event.targets[0];
|
||||
player.line(target);
|
||||
|
||||
if (trigger.targets.includes(target)) {
|
||||
trigger.targets.remove(target);
|
||||
game.log(target, "不再是", trigger.card, "的目标");
|
||||
}
|
||||
else {
|
||||
trigger.targets.add(target);
|
||||
game.log(target, "成为了", trigger.card, "的额外目标");
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.6,
|
||||
},
|
||||
},
|
||||
|
||||
//旱魃
|
||||
xinji_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:2",
|
||||
trigger: { global: "loseAfter" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return (
|
||||
_status.currentPhase &&
|
||||
_status.currentPhase.isIn() &&
|
||||
event.player?.isIn() &&
|
||||
event.player != _status.currentPhase &&
|
||||
!player.getEnemies(null, false).includes(event.player) &&
|
||||
event.type == "discard" &&
|
||||
event.cards2?.some(card => get.position(card, true) == "d" && event.hs?.includes(card))
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
const num = difficulty == "nightmare" ? 2 : 1;
|
||||
const target = _status.currentPhase;
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.6,
|
||||
},
|
||||
},
|
||||
zhiri_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:2",
|
||||
trigger: { global: "useCardToPlayered" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.isFirstTarget &&
|
||||
event.player?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.player) &&
|
||||
get.type2(event.card) == "trick" &&
|
||||
get.color(event.card) == "red"
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
let num = 1;
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "hard") {
|
||||
num = 2;
|
||||
} else if (difficulty == "nightmare") {
|
||||
num = 3;
|
||||
}
|
||||
|
||||
await player.draw(num);
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.4,
|
||||
},
|
||||
},
|
||||
fenshi_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/天书乱斗:2",
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
const num = player.countCards("h") - player.hp;
|
||||
if (num < 0) return true;
|
||||
if (num > 0) return player.getEnemies(null, false).some(target => target.isIn());
|
||||
return false;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const num = player.countCards("h") - player.hp;
|
||||
|
||||
if (num < 0) {
|
||||
await player.draw(-num);
|
||||
return;
|
||||
}
|
||||
|
||||
const enemies = player.getEnemies(null, false).filter(target => target.isIn());
|
||||
const map = new Map();
|
||||
|
||||
for (let i = 0; i < num && enemies.length; i++) {
|
||||
const target = enemies.randomGet();
|
||||
map.set(target, (map.get(target) || 0) + 1);
|
||||
}
|
||||
|
||||
for (const [target, damage] of map) {
|
||||
if (!target.isIn()) continue;
|
||||
player.line(target);
|
||||
await target.damage(damage, player);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default skills;
|
||||
@@ -0,0 +1,53 @@
|
||||
const translates = {
|
||||
shuishengonggong_boss_shuying: "水神共工",
|
||||
shuishen_shuying: "水神",
|
||||
shuishen_shuying_info: "锁定技,弃牌堆中,不同牌名的牌数达到4时,你回复体力时回复量+1;达到5时,摸牌阶段额外摸5张牌;达到9时,你造成伤害时伤害+1。",
|
||||
tuanliu_shuying: "湍流",
|
||||
tuanliu_shuying_info: "锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复2点体力;大于5,你摸4张牌;大于9,你对所有敌方角色造成1点伤害。",
|
||||
juehong_shuying: "决洪",
|
||||
juehong_shuying_info: "锁定技,准备阶段,你令所有敌方角色自己弃置自己的装备区内的所有牌,若其装备区内没有牌,则改为随机弃置2张手牌",
|
||||
|
||||
baiqi_boss_shuying: "白起",
|
||||
changsheng_shuying: "常胜",
|
||||
changsheng_shuying_info: "锁定技,你使用【杀】无距离限制。",
|
||||
shashen_shuying: "杀神",
|
||||
shashen_shuying_info: "你可以将手牌中的任意牌当【杀】使用或打出。你使用的【杀】造成伤害后,摸三张牌。",
|
||||
wuan_shuying: "武安",
|
||||
wuan_shuying_info: "锁定技,你可使用的【杀】的次数+3,【杀】造成的伤害+1。",
|
||||
|
||||
kuafu_boss_shuying: "盘古",
|
||||
shenqu_shuying: "神躯",
|
||||
shenqu_shuying_info: "锁定技,当你受到伤害后,将所有红色手牌置于牌堆底,并摸等量的牌。",
|
||||
lieben_shuying: "烈奔",
|
||||
lieben_shuying_info: "锁定技,当你使用【杀】指定目标后,使用牌堆底的牌进行一次判定:若判定结果为红色,则此杀不计入出牌阶段使用次数且伤害+1。",
|
||||
yinjiang_shuying: "饮江",
|
||||
yinjiang_shuying_info: "锁定技,当你在出牌阶段摸牌后,获得牌堆底的1张牌。若该牌为红色,则对所有敌方角色造成1点伤害。当你在同一个阶段内以此法造成过两次活更多的伤害后,该技能失效直到回合结束。",
|
||||
zhuri_shuying: "逐日",
|
||||
zhuri_shuying_info: "锁定技,你使用红色牌时摸1张牌。当你成为【杀】或普通锦囊牌的目标后,结算完毕后将该牌放置在牌堆底。",
|
||||
|
||||
shaohao_boss_shuying: "少昊",
|
||||
baiyi_shuying: "白仪",
|
||||
baiyi_shuying_info: "锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其1张牌;若当前轮数小于5,对其造成1点雷电伤害;若当前轮数小于7,其随机弃置1张牌。",
|
||||
shenen_shuying: "神恩",
|
||||
shenen_shuying_info: "锁定技,所有己方角色使用牌锁定技,所有己方角色使用牌没有距离限制。",
|
||||
|
||||
|
||||
xuannv_boss_shuying: "玄女",
|
||||
xuanlie_shuying: "玄烈",
|
||||
xuanlie_shuying_info: "锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成1点伤害。",
|
||||
jiutian_shuying: "九天",
|
||||
jiutian_shuying_info: "锁定技,准备阶段,你获得所有敌方角色各1张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。",
|
||||
dishi_shuying: "帝师",
|
||||
dishi_shuying_info: "当你使用【杀】或普通锦囊牌指定目标时,如果目标数为1,可以增加一个目标;如果目标数大于1,你可以减少一个目标。",
|
||||
|
||||
hanba_boss_shuying: "旱魃",
|
||||
xinji_shuying: "心悸",
|
||||
xinji_shuying_info: "锁定技,当友方于回合外因弃置而失去手牌时,你对当前角色造成1点伤害。",
|
||||
zhiri_shuying: "炙日",
|
||||
zhiri_shuying_info: "锁定技,当敌方角色使用红色锦囊牌指定目标后,你摸1张牌。",
|
||||
fenshi_shuying: "焚世",
|
||||
fenshi_shuying_info: "锁定技,准备阶段,若你的手牌数小于体力值,则将手牌摸至于体力值相等;若你的手牌数大于体力值,则你对敌方角色造成共计X点伤害,点数随机分配(X为手牌数减体力值)。",
|
||||
|
||||
};
|
||||
|
||||
export default translates;
|
||||
@@ -0,0 +1,50 @@
|
||||
const characters = {
|
||||
heibaiwuchang_boss_shuying:
|
||||
{
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mizui_shuying", "qiangzheng_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/heibaiwuchang_boss_shuying.png"
|
||||
},
|
||||
niutoumamian_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["shiyv_shuying", "manji_shuying", "xiaoshou_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/niutoumamian_boss_shuying.png"
|
||||
},
|
||||
yvsai_boss_shuying: {
|
||||
sex: "female",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["anchao_shuying", "guixi_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/yvsai_boss_shuying.png"
|
||||
},
|
||||
huangfeng_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["mingchong_shuying", "duzhen_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/huangfeng_boss_shuying.png"
|
||||
},
|
||||
riyeyoushen_boss_shuying: {
|
||||
sex: "male",
|
||||
group: "qun",
|
||||
hp: 4,
|
||||
skills: ["huiyun_shuying", "yezhong_shuying", "zhoucha_shuying"],
|
||||
isHiddenBoss: true,
|
||||
isBossAllowed: true,
|
||||
img: "extension/术樱包/pve/images/捉鬼驱邪/riyeyoushen_boss_shuying.png"
|
||||
},
|
||||
};
|
||||
|
||||
export default characters;
|
||||
@@ -0,0 +1,74 @@
|
||||
import { getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const dynamicTranslates = {
|
||||
//黑白无常
|
||||
mizui_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2
|
||||
|
||||
return `锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的${num}张牌。`
|
||||
},
|
||||
qiangzheng_shuying(player, skill) {
|
||||
let num = 2;
|
||||
if (getTianshuDifficulty() == "hard") num = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 6;
|
||||
|
||||
return `锁定技,敌方角色的结束阶段,若其手牌数小于${num},你获得其所有手牌。`
|
||||
},
|
||||
xixing_shuying(player, skill) {
|
||||
const difficulty = getTianshuDifficulty();
|
||||
if (difficulty == "nightmare") {
|
||||
return "锁定技,准备阶段,你对每名敌方角色各造成2点雷电伤害,然后你回复等量体力";
|
||||
}
|
||||
|
||||
return "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力。";
|
||||
},
|
||||
|
||||
//鱼鳃
|
||||
guixi_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1
|
||||
|
||||
return `锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸${num}张牌。`;
|
||||
},
|
||||
|
||||
//黄蜂
|
||||
mingchong_shuying(player, skill) {
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
return "锁定技,你死亡时,杀死你的角色弃置所有牌。"
|
||||
}
|
||||
|
||||
return "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。"
|
||||
},
|
||||
duzhen_shuying(player, skill) {
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。"
|
||||
}
|
||||
|
||||
return "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置2张牌(优先装备区的牌)。"
|
||||
|
||||
},
|
||||
|
||||
//日夜游神
|
||||
huiyun_shuying(player, skill) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2
|
||||
|
||||
return `出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成${num}点伤害。`
|
||||
},
|
||||
yezhong_shuying(player, skill) {
|
||||
let num = 1;
|
||||
if (getTianshuDifficulty() == "hard") num = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 3;
|
||||
|
||||
return `锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃${num}张手牌。`
|
||||
},
|
||||
zhoucha_shuying(player, skill) {
|
||||
let num = 1;
|
||||
if (getTianshuDifficulty() == "hard") num = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 3;
|
||||
|
||||
return `锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+${num}。`
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default dynamicTranslates;
|
||||
@@ -0,0 +1,570 @@
|
||||
import { lib, game, ui, get, ai, _status, getTianshuDifficulty } from "../../shared.js";
|
||||
|
||||
const skills = {
|
||||
//黑白无常 over
|
||||
mizui_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { source: "damageEnd" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return event.player?.isIn() && event.player.countDiscardableCards(player, "he") > 0 && event.card && get.is.damageCard(event.card);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2;
|
||||
|
||||
player.line(trigger.player);
|
||||
await player.discardPlayerCard(trigger.player, num, "he", true);
|
||||
},
|
||||
ai: { expose: 0.2 },
|
||||
},
|
||||
qiangzheng_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { global: "phaseEnd" },
|
||||
forced: true,
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
let num = 2;
|
||||
if (getTianshuDifficulty() == "hard") num = 4;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 6;
|
||||
|
||||
return event.player?.isIn() && player.getEnemies(null, false).includes(event.player) && event.player.countCards("h") > 0 && event.player.countCards("h") < num;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
player.line(trigger.player);
|
||||
await player.gain(trigger.player.getCards("h"), trigger.player, "giveAuto");
|
||||
},
|
||||
},
|
||||
xixing_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn());
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const enemies = player.getEnemies(null, false).filter(target => target.isIn());
|
||||
|
||||
if (difficulty == "nightmare") {
|
||||
for (const target of enemies) {
|
||||
player.line(target, "thunder");
|
||||
await target.damage(2, "thunder", player);
|
||||
}
|
||||
} else {
|
||||
const max = Math.max(...enemies.map(target => target.hp));
|
||||
const target = enemies.filter(target => target.hp == max).randomGet();
|
||||
player.line(target, "thunder");
|
||||
await target.damage(2, "thunder", player);
|
||||
}
|
||||
|
||||
await player.recover(2);
|
||||
},
|
||||
},
|
||||
taiping_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "damageEnd" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
logTarget: "source",
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.num > 0 &&
|
||||
event.source?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.source)
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const source = trigger.source;
|
||||
player.line(source);
|
||||
|
||||
const result = await source
|
||||
.chooseToDiscard("h", 2, `太平:弃置两张花色不同的手牌,否则失去1点体力`, card => {
|
||||
const cards = ui.selected.cards;
|
||||
return !cards.some(cardx => get.suit(cardx) == get.suit(card));
|
||||
})
|
||||
.set("ai", card => {
|
||||
const player = get.player();
|
||||
if (player.hp <= 1) return 8 - get.value(card);
|
||||
return 6 - get.value(card);
|
||||
})
|
||||
.forResult();
|
||||
|
||||
if (!result.bool) {
|
||||
await source.loseHp();
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
maixie_defend: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.tag(card, "damage") && target.getEnemies(null, false).includes(player)) {
|
||||
if (player.countCards("h") < 2) return [1, -1];
|
||||
return [1, 0.4];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//牛头马面
|
||||
shiyv_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "phaseDrawBegin2" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return !event.numFixed;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num = 0;
|
||||
const cards = [];
|
||||
for (const suit of lib.suit) {
|
||||
const card = get.cardPile(card => get.suit(card) == suit, "cardPile", "random");
|
||||
if (card) cards.push(card);
|
||||
}
|
||||
if (cards.length) await player.gain(cards, "gain2");
|
||||
},
|
||||
},
|
||||
manji_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "useCardToPlayered" },
|
||||
filter(event, player) {
|
||||
return event.card.name == "sha" && event.targets?.length == 1 && player.getEnemies(null, false).includes(event.target) && event.target.countDiscardableCards(player, "h") > 0;
|
||||
},
|
||||
async cost(event, trigger, player) {
|
||||
event.result = await player.chooseBool(get.prompt(event.skill, trigger.target))
|
||||
.set("ai", () => get.attitude(get.player(), get.event().getTrigger().target) < 0)
|
||||
.forResult();
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = trigger.target;
|
||||
player.line(target);
|
||||
const cards = target.getCards("h").randomGets(Math.min(2, target.countCards("h")));
|
||||
await target.discard(cards);
|
||||
if (cards.some(card => get.name(card, target) == "sha")) trigger.getParent().baseDamage++;
|
||||
},
|
||||
ai: { expose: 0.2 },
|
||||
},
|
||||
xiaoshou_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn());
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = player.getEnemies(null, false).filter(target => target.isIn()).randomGet();
|
||||
player.line(target);
|
||||
await target.damage(2, player);
|
||||
},
|
||||
},
|
||||
|
||||
//鱼鳃 over
|
||||
anchao_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
forced: true,
|
||||
locked: true,
|
||||
group: ["anchao_shuying_gain", "anchao_shuying_start"],
|
||||
marktext: "潮",
|
||||
intro: {
|
||||
name: "暗潮",
|
||||
content: "mark",
|
||||
},
|
||||
subSkill: {
|
||||
gain: {
|
||||
audio: "anchao_shuying",
|
||||
trigger: { global: "phaseJieshuBegin" },
|
||||
forced: true,
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.player?.isIn() &&
|
||||
!player.getEnemies(null, false).includes(event.player) &&
|
||||
!event.player.getHistory("sourceDamage", evt => evt.num > 0).length
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.player.addMark("anchao_shuying", 1);
|
||||
},
|
||||
},
|
||||
start: {
|
||||
audio: "anchao_shuying",
|
||||
trigger: { global: "phaseBegin" },
|
||||
forced: true,
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.player?.isIn() &&
|
||||
!player.getEnemies(null, false).includes(event.player) &&
|
||||
event.player.countMark("anchao_shuying") > 0
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = trigger.player;
|
||||
target.storage.anchao_shuying_effect = target.countMark("anchao_shuying");
|
||||
target.addTempSkill("anchao_shuying_effect", { player: "phaseAfter" });
|
||||
},
|
||||
},
|
||||
effect: {
|
||||
charlotte: true,
|
||||
group: ["anchao_shuying_draw", "anchao_shuying_damage"],
|
||||
onremove(player) {
|
||||
delete player.storage.anchao_shuying_effect;
|
||||
},
|
||||
},
|
||||
draw: {
|
||||
audio: "anchao_shuying",
|
||||
trigger: { player: "phaseDrawBegin2" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return !event.numFixed && player.storage.anchao_shuying_effect > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num += player.storage.anchao_shuying_effect;
|
||||
},
|
||||
},
|
||||
damage: {
|
||||
audio: "anchao_shuying",
|
||||
trigger: { source: "damageBegin1" },
|
||||
forced: true,
|
||||
filter(event, player) {
|
||||
return (
|
||||
player.storage.anchao_shuying_effect > 0 &&
|
||||
event.player?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.player)
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
trigger.num += player.storage.anchao_shuying_effect;
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
threaten: 2,
|
||||
},
|
||||
},
|
||||
guixi_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "damageEnd" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
filter(event, player) {
|
||||
return event.num > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
|
||||
const result = await player
|
||||
.judge(card => get.color(card) == "red" ? 2 : 0)
|
||||
.set("judge2", result => result.bool)
|
||||
.forResult();
|
||||
|
||||
if (result.bool && player.isDamaged()) {
|
||||
await player.recover();
|
||||
}
|
||||
else {
|
||||
await player.draw(num);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
maixie: true,
|
||||
maixie_hp: true,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.tag(card, "damage")) {
|
||||
if (player.hasSkillTag("jueqing", false, target)) return [1, -1];
|
||||
return [1, 0.7];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//黄蜂 over
|
||||
mingchong_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "die" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
forceDie: true,
|
||||
logTarget: "source",
|
||||
filter(event, player) {
|
||||
return event.source?.isIn() && event.source.countDiscardableCards(player, "he") > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const source = trigger.source;
|
||||
player.line(source);
|
||||
|
||||
const cards = source
|
||||
.getCards("he")
|
||||
.filter(card => lib.filter.cardDiscardable(card, source, "mingchong_shuying"));
|
||||
|
||||
if (cards.length) {
|
||||
if (getTianshuDifficulty() == "nightmare") {
|
||||
await source.discard(cards);
|
||||
}
|
||||
else {
|
||||
await source.discard(cards.randomGets(Math.ceil(cards.length / 2)));
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 0.8,
|
||||
effect: {
|
||||
target(card, player, target) {
|
||||
if (get.tag(card, "damage") && target.hp <= 1 && player.countCards("he") > 2) {
|
||||
return [1, -0.8];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
duzhen_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "useCardToPlayered" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
logTarget: "target",
|
||||
filter(event, player) {
|
||||
return (
|
||||
event.targets?.length == 1 &&
|
||||
event.target?.isIn() &&
|
||||
player.getEnemies(null, false).includes(event.target) &&
|
||||
event.target.countDiscardableCards(player, "he") > 0
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = trigger.target;
|
||||
const num = getTianshuDifficulty() == "nightmare" ? 2 : 1;
|
||||
const cards = [];
|
||||
|
||||
player.line(target);
|
||||
|
||||
const equips = target
|
||||
.getCards("e")
|
||||
.filter(card => lib.filter.cardDiscardable(card, target, "duzhen_shuying"))
|
||||
.randomGets(num);
|
||||
cards.addArray(equips);
|
||||
|
||||
if (cards.length < num) {
|
||||
const hands = target
|
||||
.getCards("h")
|
||||
.filter(card => lib.filter.cardDiscardable(card, target, "duzhen_shuying"))
|
||||
.randomGets(num - cards.length);
|
||||
cards.addArray(hands);
|
||||
}
|
||||
|
||||
if (cards.length) {
|
||||
await target.discard(cards);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.8,
|
||||
effect: {
|
||||
player_use(card, player, target) {
|
||||
if (target && player.getEnemies(null, false).includes(target) && target.countCards("he") > 0) {
|
||||
return [1, 0.5];
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
//日夜游神 over
|
||||
huiyun_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
enable: "phaseUse",
|
||||
usable: 1,
|
||||
filter(event, player) {
|
||||
return player.getEnemies(null, false).some(target => target.isIn() && target.countCards("h") > 0);
|
||||
},
|
||||
filterTarget(card, player, target) {
|
||||
return target.isIn() && player.getEnemies(null, false).includes(target) && target.countCards("h") > 0;
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = event.targets[0];
|
||||
const hs = target.getCards("h");
|
||||
player.line(target);
|
||||
await target.showCards(hs, `${get.translation(player)}对${get.translation(target)}发动了【${get.translation(event.name)}】`);
|
||||
|
||||
const result = await player
|
||||
.choosePlayerCard(target, "h", [1, 2], `弃置${get.translation(target)}至多两张手牌`)
|
||||
.set("filterButton", button => {
|
||||
const player = get.player();
|
||||
const target = get.event().target;
|
||||
return lib.filter.canBeDiscarded(button.link, player, target);
|
||||
})
|
||||
.set("ai", button => {
|
||||
const card = button.link;
|
||||
const player = get.player();
|
||||
const target = get.event().target;
|
||||
let value = get.value(card, target);
|
||||
if (get.name(card, target) == "tao") value += 3;
|
||||
if (get.name(card, target) == "shan") value += 1;
|
||||
return value;
|
||||
})
|
||||
.set("target", target)
|
||||
.forResult();
|
||||
|
||||
if (!result.bool || !result.cards?.length) return;
|
||||
|
||||
const names = result.cards.map(card => get.name(card, target)).unique();
|
||||
await target.discard(result.cards);
|
||||
|
||||
if (!target.isIn()) return;
|
||||
|
||||
const result2 = await player
|
||||
.chooseToDiscard("he", `是否弃置一张同名牌,对${get.translation(target)}造成2点伤害?`, card => {
|
||||
return get.event().names.includes(get.name(card));
|
||||
})
|
||||
.set("names", names)
|
||||
.set("ai", card => {
|
||||
const player = get.player();
|
||||
const target = get.event().target;
|
||||
if (!player.getEnemies(null, false).includes(target)) return 0;
|
||||
const effect = get.damageEffect(target, player, player);
|
||||
if (effect <= 0) return 0;
|
||||
return effect * 3 - get.value(card);
|
||||
})
|
||||
.set("target", target)
|
||||
.forResult();
|
||||
|
||||
if (result2.bool) {
|
||||
const num = getTianshuDifficulty() == "normal" ? 1 : 2
|
||||
|
||||
player.line(target);
|
||||
await target.damage(num, player);
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
order: 5,
|
||||
result: {
|
||||
target(player, target) {
|
||||
if (!player.getEnemies(null, false).includes(target)) return 0;
|
||||
return -Math.min(2, target.countCards("h"));
|
||||
},
|
||||
},
|
||||
threaten: 1.6,
|
||||
},
|
||||
},
|
||||
yezhong_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "phaseJieshuBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
async content(event, trigger, player) {
|
||||
const result = await player
|
||||
.judge(card => get.color(card) == "black" ? 2 : -1)
|
||||
.set("judge2", result => result.bool)
|
||||
.forResult();
|
||||
|
||||
if (result.card) {
|
||||
await player.gain(result.card, "gain2");
|
||||
}
|
||||
|
||||
if (result.bool) {
|
||||
let num = 1;
|
||||
if (getTianshuDifficulty() == "hard") num = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") num = 3;
|
||||
|
||||
const targets = player.getEnemies(null, false).filter(target => target.isIn() && target.countCards("h") > 0);
|
||||
for (const target of targets) {
|
||||
player.line(target);
|
||||
const cards = target
|
||||
.getCards("h")
|
||||
.filter(card => lib.filter.cardDiscardable(card, target, "yezhong_shuying"))
|
||||
.randomGets(num);
|
||||
|
||||
if (cards.length) {
|
||||
await target.discard(cards);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.8,
|
||||
},
|
||||
},
|
||||
zhoucha_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { player: "phaseZhunbeiBegin" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
async content(event, trigger, player) {
|
||||
const result = await player
|
||||
.judge(card => get.color(card) == "red" ? 2 : -1)
|
||||
.set("judge2", result => result.bool)
|
||||
.forResult();
|
||||
|
||||
if (result.card) {
|
||||
await player.gain(result.card, "gain2");
|
||||
}
|
||||
|
||||
if (result.bool) {
|
||||
player.addTempSkill("zhoucha_shuying_effect", { player: "phaseAfter" });
|
||||
}
|
||||
},
|
||||
subSkill: {
|
||||
effect: {
|
||||
charlotte: true,
|
||||
mod: {
|
||||
cardUsable(card, player, num) {
|
||||
let number = 1;
|
||||
if (getTianshuDifficulty() == "hard") number = 2;
|
||||
if (getTianshuDifficulty() == "nightmare") number = 3;
|
||||
|
||||
if (card.name == "sha") return num + number;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.4,
|
||||
},
|
||||
},
|
||||
duane_shuying: {
|
||||
mode: ["boss"],
|
||||
audio: "ext:术樱包/pve/audio/skill/驱鬼逐邪:1",
|
||||
trigger: { global: "phaseDiscardEnd" },
|
||||
forced: true,
|
||||
locked: true,
|
||||
logTarget: "player",
|
||||
filter(event, player) {
|
||||
const target = event.player;
|
||||
return (
|
||||
target?.isIn() &&
|
||||
player.getEnemies(null, false).includes(target) &&
|
||||
target.getHistory("lose", evt => {
|
||||
return (
|
||||
evt.type == "discard" &&
|
||||
evt.getParent("phaseDiscard") == event &&
|
||||
evt.cards2?.some(card => get.color(card, target) == "black")
|
||||
);
|
||||
}).length
|
||||
);
|
||||
},
|
||||
async content(event, trigger, player) {
|
||||
const target = trigger.player;
|
||||
player.line(target);
|
||||
await target.loseHp(2);
|
||||
},
|
||||
ai: {
|
||||
threaten: 1.5,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default skills;
|
||||
@@ -0,0 +1,45 @@
|
||||
const translates = {
|
||||
heibaiwuchang_boss_shuying: "黑白无常",
|
||||
mizui_shuying: "迷醉",
|
||||
mizui_shuying_info: "锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的1张牌。",
|
||||
qiangzheng_shuying: "强征",
|
||||
qiangzheng_shuying_info: "锁定技,敌方角色的结束阶段,若其手牌数小于4,你获得其所有手牌。",
|
||||
xixing_shuying: "吸星",
|
||||
xixing_shuying_info: "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力",
|
||||
taiping_shuying: "太平",
|
||||
taiping_shuying_info: "锁定技,当你受到敌方角色造成的伤害后,伤害来源须弃置两张花色不同的手牌,否则失去1点体力。",
|
||||
|
||||
niutoumamian_boss_shuying: "牛头马面",
|
||||
shiyv_shuying: "施狱",
|
||||
shiyv_shuying_info: "锁定技,摸牌阶段,你放弃摸牌改为随机获得牌堆中每种花色的牌各一张。",
|
||||
manji_shuying: "蛮击",
|
||||
manji_shuying_info: "你使用【杀】指定单一目标后,你可以弃置该角色2张手牌。若其中有【杀】,你本次【杀】造成的伤害+1。",
|
||||
xiaoshou_shuying: "枭首",
|
||||
xiaoshou_shuying_info: "锁定技,准备阶段,你对随机一名敌方角色造成2点伤害",
|
||||
|
||||
yvsai_boss_shuying: "鱼鳃",
|
||||
anchao_shuying: "暗潮",
|
||||
anchao_shuying_info: "锁定技,己方角色的回合结束时,若此回合该角色没有造成过伤害,则获得一个“暗潮”标记。己方回合开始时,若该角色有“暗潮”标记,则本回合摸牌阶段多摸X张牌且对敌方角色造成的伤害+X(X为“暗潮”标记数)",
|
||||
guixi_shuying: "鬼吸",
|
||||
guixi_shuying_info: "锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸1张牌。",
|
||||
|
||||
huangfeng_boss_shuying: "黄蜂",
|
||||
mingchong_shuying: "冥虫",
|
||||
mingchong_shuying_info: "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。",
|
||||
duzhen_shuying: "毒针",
|
||||
duzhen_shuying_info: "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。",
|
||||
|
||||
riyeyoushen_boss_shuying: "日夜游神",
|
||||
huiyun_shuying: "晦运",
|
||||
huiyun_shuying_info: "出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成2点伤害。",
|
||||
yezhong_shuying: "夜冢",
|
||||
yezhong_shuying_info: "锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃1张手牌。",
|
||||
zhoucha_shuying: "昼刹",
|
||||
zhoucha_shuying_info: "锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+1。",
|
||||
duane_shuying: "断恶",
|
||||
duane_shuying_info: "锁定技,敌方角色的弃牌阶段结束时,若其此阶段弃置了黑色牌,则该角色失去2点体力。",
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default translates;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { lib, game, ui, get, ai, _status } from "../../../../noname.js";
|
||||
import tianshuConfig from "../../tianshu/config.js";
|
||||
|
||||
// 统一提供活动 BOSS 技能依赖与天书难度,避免各主题模块重复维护加载路径。
|
||||
const getTianshuDifficulty = () => {
|
||||
return _status[tianshuConfig.settings.difficultyStatusKey] || "normal";
|
||||
};
|
||||
|
||||
export { lib, game, ui, get, ai, _status, getTianshuDifficulty };
|
||||
+13
-2116
File diff suppressed because it is too large
Load Diff
+12
-150
@@ -1,153 +1,15 @@
|
||||
const translates = {
|
||||
zishu_boss_shuying: "子鼠",
|
||||
zishu_shuying: "子鼠",
|
||||
zishu_shuying_info: "出牌阶段限一次,你可以获得手牌数大于你的其他角色一张手牌,你可以重复此流程,直到你的手牌数为全场最多。",
|
||||
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";
|
||||
|
||||
chenlong_boss_shuying: "辰龙",
|
||||
chenlong_shuying: "辰龙",
|
||||
chenlong_shuying_info: "限定技,出牌阶段,你可以失去任意点体力(至多为5),然后对一名其他角色造成等量的伤害。若你以此法进入濒死状态,则你将体力值回复至1,然后减少1点体力上限。",
|
||||
|
||||
wuma_boss_shuying: "午马",
|
||||
wuma_shuying: "午马",
|
||||
wuma_shuying_info: "锁定技,你不能被翻面;你的阶段不能被跳过;当你成为其他角色使用锦囊牌的目标后,摸一张牌。",
|
||||
|
||||
weiyang_boss_shuying: "未羊",
|
||||
weiyang_shuying: "未羊",
|
||||
weiyang_shuying_info: "出牌阶段限一次,你可以弃置任意张不同类型的牌,然后令至多等量角色回复1点体力。",
|
||||
|
||||
shenhou_boss_shuying: "申猴",
|
||||
shenhou_shuying: "申猴",
|
||||
shenhou_shuying_info: "当你成为【杀】的目标时,你可以进行判定,若结果为红色,则此【杀】对你无效。",
|
||||
|
||||
xvgou_boss_shuying: "戌狗",
|
||||
xvgou_shuying: "戌狗",
|
||||
xvgou_shuying_info: "锁定技,红色【杀】对你无效;你使用红色【杀】无距离限制且造成伤害+1.",
|
||||
|
||||
heibaiwuchang_boss_shuying: "黑白无常",
|
||||
mizui_shuying: "迷醉",
|
||||
mizui_shuying_info: "锁定技,你的伤害牌造成伤害后,你随机弃置受伤角色的1张牌。",
|
||||
qiangzheng_shuying: "强征",
|
||||
qiangzheng_shuying_info: "锁定技,敌方角色的结束阶段,若其手牌数小于4,你获得其所有手牌。",
|
||||
xixing_shuying: "吸星",
|
||||
xixing_shuying_info: "锁定技,准备阶段,随机对敌方体力最多的一名角色造成2点雷电伤害,然后你回复等量体力",
|
||||
|
||||
niutoumamian_boss_shuying: "牛头马面",
|
||||
shiyv_shuying: "施狱",
|
||||
shiyv_shuying_info: "锁定技,摸牌阶段,你放弃摸牌改为随机获得牌堆中每种花色的牌各一张。",
|
||||
manji_shuying: "蛮击",
|
||||
manji_shuying_info: "你使用【杀】指定单一目标后,你可以弃置该角色2张手牌。若其中有【杀】,你本次【杀】造成的伤害+1。",
|
||||
xiaoshou_shuying: "枭首",
|
||||
xiaoshou_shuying_info: "锁定技,准备阶段,你对随机一名敌方角色造成2点伤害",
|
||||
|
||||
yvsai_boss_shuying: "鱼鳃",
|
||||
anchao_shuying: "暗潮",
|
||||
anchao_shuying_info: "锁定技,己方角色的回合结束时,若此回合该角色没有造成过伤害,则获得一个“暗潮”标记。己方回合开始时,若该角色有“暗潮”标记,则本回合摸牌阶段多摸X张牌且对敌方角色造成的伤害+X(X为“暗潮”标记数)",
|
||||
guixi_shuying: "鬼吸",
|
||||
guixi_shuying_info: "锁定技,当你受到伤害后,你进行一次判定,若结果为红色,你回复1点体力;若结果不为红色,你摸1张牌。",
|
||||
|
||||
huangfeng_boss_shuying: "黄蜂",
|
||||
mingchong_shuying: "冥虫",
|
||||
mingchong_shuying_info: "锁定技,你死亡时,杀死你的角色随机弃置一半的牌。",
|
||||
duzhen_shuying: "毒针",
|
||||
duzhen_shuying_info: "锁定技,你使用牌指定单一敌方角色为目标后,该角色随机弃置1张牌(优先装备区的牌)。",
|
||||
|
||||
riyeyoushen_boss_shuying: "日夜游神",
|
||||
huiyun_shuying: "晦运",
|
||||
huiyun_shuying_info: "出牌阶段限一次,你可以展示一名敌方角色的手牌,并弃置其中至多两张牌。然后你可以弃置一张与该角色弃置牌牌名相同的牌,对其造成2点伤害。",
|
||||
yezhong_shuying: "夜冢",
|
||||
yezhong_shuying_info: "锁定技,结束阶段,你进行一次判定并获得判定牌,若次牌为黑色,每名敌方角色随机弃1张手牌。",
|
||||
zhoucha_shuying: "昼刹",
|
||||
zhoucha_shuying_info: "锁定技,准备阶段,你进行一次判定并获得判定牌,若此牌为红色,你本回合使用【杀】的次数+1。",
|
||||
duane_shuying: "断恶",
|
||||
duane_shuying_info: "锁定技,敌方角色的弃牌阶段结束时,若其此阶段弃置了黑色牌,则该角色失去2点体力。",
|
||||
|
||||
|
||||
caocao_boss_shuying: "曹操",
|
||||
jianxiong_shuying: "奸雄",
|
||||
jianxiong_shuying_info: "当你受到伤害后,你可以获得对你造成伤害的牌并摸一张牌。",
|
||||
lingba_shuying: "凌霸",
|
||||
lingba_shuying_info: "锁定技,你的回合开始时,若你手牌数为全场最多,则对一名随机敌人造成1点伤害。若你手牌数大于等于你体力值的两倍,则改为对所有敌人造成伤害。",
|
||||
yishen_shuying: "疑神",
|
||||
yishen_shuying_info: "当你回复体力时,可以改为获得一名随机敌方角色一张随机装备。",
|
||||
|
||||
simayi_boss_shuying: "司马懿",
|
||||
fankui_shuying: "反馈",
|
||||
fankui_shuying_info: "当你受到1点伤害后,你可以获得伤害来源的一张牌。",
|
||||
guicai_shuying: "鬼才",
|
||||
guicai_shuying_info: "当一名角色的判定牌生效前,你可以打出一张牌代替之。",
|
||||
langgu_shuying: "狼顾",
|
||||
langgu_shuying_info: " 锁定技,每回合限一次,当你获得其他角色的牌时,进行一次判定:若结果为黑桃,随机弃置其1张手牌,且视为此技能本回合未发动过。",
|
||||
yuanlv_shuying: "远虑",
|
||||
yuanlv_shuying_info: "当你使用锦囊牌对敌方角色造成伤害时,你可以防止该伤害,改为摸一张牌且该敌方角色对你造成1点伤害。",
|
||||
|
||||
lvbu_boss_shuying: "吕布",
|
||||
wushuang_shuying: "无双",
|
||||
wushuang_shuying_info: "锁定技。①你使用的【杀】需两张【闪】才能抵消;与你进行【决斗】的角色每次需要打出两张【杀】。②每回合限1次,当你使用【杀】或【决斗】造成伤害时,若受伤角色没有使用或打出过【杀】或【闪】响应此牌,则此伤害+1。",
|
||||
shenij_shuying: "神戟",
|
||||
shenij_shuying_info: "判定阶段,你可以弃置两张手牌,然后弃置你判定区里的牌;摸牌阶段,你多摸两张牌;出牌阶段,你可以多使用一张【杀】,你的【杀】可以多指定一名角色为目标。",
|
||||
zhanjia_shuying: "战甲",
|
||||
zhanjia_shuying_info: "锁定技,每回合限一次,当你受到大于2点的伤害时,将此伤害减至2点,然后摸两张牌。",
|
||||
|
||||
dongzhuo_boss_shuying: "董卓",
|
||||
jiuchi_shuying: "酒池",
|
||||
jiuchi_shuying_info: "你可以将一张黑桃手牌当【酒】使用。",
|
||||
roulin_shuying: "肉林",
|
||||
roulin_shuying_info: "锁定技,你对女性角色使用的【杀】和女性角色对你使用的【杀】均需使用两张【闪】才能抵消。",
|
||||
baonue_shuying: "暴虐",
|
||||
baonue_shuying_info: "锁定技,回合开始时,你摸X张牌并对至多X名角色造成1点伤害,然后你失去1点体力。X为你已损失体力且最大为3。",
|
||||
qubu_shuying: "驭布",
|
||||
qubu_shuying_info: "锁定技,当友方角色使用【杀】指定目标时,进行一次判定:若结果为黑桃,你对此【杀】的所有目标造成1点伤害。",
|
||||
|
||||
|
||||
shuishengonggong_boss_shuying: "水神共工",
|
||||
shuishen_shuying: "水神",
|
||||
shuishen_shuying_info: "锁定技,弃牌堆中,不同牌名的牌数达到4时,你回复体力时回复量+1;达到5时,摸牌阶段额外摸5张牌;达到9时,你造成伤害时伤害+1。",
|
||||
tuanliu_shuying: "湍流",
|
||||
tuanliu_shuying_info: "锁定技,结束阶段,若本回合进入弃牌堆的卡牌数量:大于4,你回复2点体力;大于5,你摸4张牌;大于9,你对所有敌方角色造成1点伤害。",
|
||||
juehong_shuying: "决洪",
|
||||
juehong_shuying_info: "锁定技,准备阶段,你令所有敌方角色自己弃置自己的装备区内的所有牌,若其装备区内没有牌,则改为随机弃置2张手牌",
|
||||
|
||||
baiqi_boss_shuying: "白起",
|
||||
changsheng_shuying: "常胜",
|
||||
changsheng_shuying_info: "锁定技,你使用【杀】无距离限制。",
|
||||
shashen_shuying: "杀神",
|
||||
shashen_shuying_info: "你可以将手牌中的任意牌当【杀】使用或打出。你使用的【杀】造成伤害后,摸三张牌。",
|
||||
wuan_shuying: "武安",
|
||||
wuan_shuying_info: "锁定技,你可使用的【杀】的次数+3,【杀】造成的伤害+1。",
|
||||
|
||||
kuafu_boss_shuying: "盘古",
|
||||
shenqu_shuying: "神躯",
|
||||
shenqu_shuying_info: "锁定技,当你受到伤害后,将所有红色手牌置于牌堆底,并摸等量的牌。",
|
||||
lieben_shuying: "烈奔",
|
||||
lieben_shuying_info: "锁定技,当你使用【杀】指定目标后,使用牌堆底的牌进行一次判定:若判定结果为红色,则此杀不计入出牌阶段使用次数且伤害+1。",
|
||||
yinjiang_shuying: "饮江",
|
||||
yinjiang_shuying_info: "锁定技,当你在出牌阶段摸牌后,获得牌堆底的1张牌。若该牌为红色,则对所有敌方角色造成1点伤害。当你在同一个阶段内以此法造成过两次活更多的伤害后,该技能失效直到回合结束。",
|
||||
zhuri_shuying: "逐日",
|
||||
zhuri_shuying_info: "锁定技,你使用红色牌时摸1张牌。当你成为【杀】或普通锦囊牌的目标后,结算完毕后将该牌放置在牌堆底。",
|
||||
|
||||
shaohao_boss_shuying: "少昊",
|
||||
baiyi_shuying: "白仪",
|
||||
baiyi_shuying_info: "锁定技,每名敌方角色的回合开始时,若当前轮数小于3,你随机获得其1张牌;若当前轮数小于5,对其造成1点雷电伤害;若当前轮数小于7,其随机弃置1张牌。",
|
||||
shenen_shuying: "神恩",
|
||||
shenen_shuying_info: "锁定技,所有己方角色使用牌锁定技,所有己方角色使用牌没有距离限制。",
|
||||
|
||||
|
||||
xuannv_boss_shuying: "玄女",
|
||||
xuanlie_shuying: "玄烈",
|
||||
xuanlie_shuying_info: "锁定技,回合结束时,对所有本回合你获得过其牌的敌方角色依次造成1点伤害。",
|
||||
jiutian_shuying: "九天",
|
||||
jiutian_shuying_info: "锁定技,准备阶段,你获得所有敌方角色各1张手牌。若你以此法获得的牌包含2种颜色,则对所有你以此法获得其牌的敌方角色造成1点伤害。",
|
||||
dishi_shuying: "帝师",
|
||||
dishi_shuying_info: "当你使用【杀】或普通锦囊牌指定目标时,如果目标数为1,可以增加一个目标;如果目标数大于1,你可以减少一个目标。",
|
||||
|
||||
hanba_boss_shuying: "旱魃",
|
||||
xinji_shuying: "心悸",
|
||||
xinji_shuying_info: "锁定技,当友方于回合外因弃置而失去手牌时,你对当前角色造成1点伤害。",
|
||||
zhiri_shuying: "炙日",
|
||||
zhiri_shuying_info: "锁定技,当敌方角色使用红色锦囊牌指定目标后,你摸1张牌。",
|
||||
fenshi_shuying: "焚世",
|
||||
fenshi_shuying_info: "锁定技,准备阶段,若你的手牌数小于体力值,则将手牌摸至于体力值相等;若你的手牌数大于体力值,则你对敌方角色造成共计X点伤害,点数随机分配(X为手牌数减体力值)。",
|
||||
|
||||
};
|
||||
// 汇总所有主题翻译,并阻止同名翻译在后加载模块中被覆盖。
|
||||
const translates = mergeBossModules("翻译", [
|
||||
["十二生肖", shiershengxiao],
|
||||
["捉鬼驱邪", zhuoguiquxie],
|
||||
["青青子衿", qingqingzijin],
|
||||
["天书乱斗", tianshu],
|
||||
]);
|
||||
|
||||
export default translates;
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
const bossList = {
|
||||
//stage1: ["riyeyoushen_boss_shuying", "dongzhuo_boss_shuying"],
|
||||
stage1: ["chenlong_boss_shuying", "weiyang_boss_shuying", "shenhou_boss_shuying", "xvgou_boss_shuying"],
|
||||
// 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"],
|
||||
@@ -121,11 +121,11 @@ const bossDifficulty = {
|
||||
...bossDifficultyTemplates.zhuoguiquxie,
|
||||
hard: {
|
||||
...bossDifficultyTemplates.zhuoguiquxie.hard,
|
||||
skill: ["xixing_shuying"],
|
||||
skills: ["xixing_shuying"],
|
||||
},
|
||||
nightmare: {
|
||||
...bossDifficultyTemplates.zhuoguiquxie.nightmare,
|
||||
skill: ["xixing_shuying", "taiping_shuying"],
|
||||
skills: ["xixing_shuying", "taiping_shuying"],
|
||||
},
|
||||
},
|
||||
yvsai_boss_shuying: {
|
||||
@@ -139,7 +139,7 @@ const bossDifficulty = {
|
||||
...bossDifficultyTemplates.zhuoguiquxie,
|
||||
nightmare: {
|
||||
...bossDifficultyTemplates.zhuoguiquxie.nightmare,
|
||||
skill: ["duane_shuying"],
|
||||
skills: ["duane_shuying"],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -194,11 +194,11 @@ const bossDifficulty = {
|
||||
...bossDifficultyTemplates.tianshuluandou,
|
||||
hard: {
|
||||
...bossDifficultyTemplates.tianshuluandou.hard,
|
||||
skill: ["shuishen_shuying"],
|
||||
skills: ["shuishen_shuying"],
|
||||
},
|
||||
nightmare: {
|
||||
...bossDifficultyTemplates.tianshuluandou.nightmare,
|
||||
skill: ["shuishen_shuying"],
|
||||
skills: ["shuishen_shuying"],
|
||||
},
|
||||
},
|
||||
shaohao_boss_shuying: { ...bossDifficultyTemplates.tianshuluandou, },
|
||||
|
||||
+64
-20
@@ -9,9 +9,10 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
const state = {
|
||||
difficulty: "normal",
|
||||
stageIndex: 0,
|
||||
currentBosses: [],
|
||||
selectedSkills: {},
|
||||
selectedSkillSet: {},
|
||||
difficultyBossMap: {},
|
||||
originalBossMap: {},
|
||||
playerSideIds: {},
|
||||
roundStartPlayer: null,
|
||||
roundStartSeat: 0,
|
||||
@@ -59,8 +60,8 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
const getVirtualIdolIds = () => tianshuConfig.virtualIdolList || Object.values(tianshuConfig.virtualIdols).flat();
|
||||
// 判断指定角色是否为虚拟偶像。
|
||||
const isVirtualIdol = player => Boolean(player && (getVirtualIdolIds().includes(player.name1) || getVirtualIdolIds().includes(player.name)));
|
||||
// 判断指定角色是否属于当前关卡 Boss 列表。
|
||||
const isCurrentStageBoss = player => Boolean(player && (state.currentBosses.includes(player.name1) || state.currentBosses.includes(player.name)));
|
||||
// 判断指定角色是否为当前关卡实际召唤的 Boss;玩家变身为同名动态武将时不会被误判。
|
||||
const isCurrentStageBoss = player => Boolean(player?.storage?.shuYing_tianshu_stageBoss && player.storage.shuYing_tianshu_stageIndex == state.stageIndex);
|
||||
// 判断指定角色是否属于开局捕获的玩家方。
|
||||
const isCapturedPlayerSide = player => Boolean(getPlayerKey(player) && state.playerSideIds[getPlayerKey(player)]);
|
||||
// 获取玩家方角色列表,可按是否存活、是否包含虚拟偶像过滤。
|
||||
@@ -227,8 +228,9 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
state.difficulty = keys[Math.max(0, controls.indexOf(result.control))] || "normal";
|
||||
_status[tianshuConfig.settings.difficultyStatusKey] = state.difficulty;
|
||||
};
|
||||
// 在指定座位创建关卡 Boss;初始主 Boss 已静默死亡,所以关卡 Boss 全部使用忠臣身份。
|
||||
const addStageBoss = (position, name) => {
|
||||
// 在指定座位创建当前难度的动态 Boss;初始主 Boss 已静默死亡,所以关卡 Boss 全部使用忠臣身份。
|
||||
const addStageBoss = (position, originalName) => {
|
||||
const name = state.difficultyBossMap[originalName] || originalName;
|
||||
const boss = game.addFellow(position, name, "zoominanim");
|
||||
boss.dataset.position = position;
|
||||
boss.side = true;
|
||||
@@ -236,6 +238,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
boss.setIdentity(boss.identity);
|
||||
boss.storage.shuYing_tianshu_stageBoss = true;
|
||||
boss.storage.shuYing_tianshu_stageIndex = state.stageIndex;
|
||||
boss.storage.shuYing_tianshu_originalBossName = originalName;
|
||||
game.addVideo("setIdentity", boss, boss.identity);
|
||||
if (game.playerMap) game.playerMap[boss.dataset.position] = boss;
|
||||
game.arrangePlayers();
|
||||
@@ -246,7 +249,12 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
const num = Number(value);
|
||||
return Number.isFinite(num) ? num : null;
|
||||
};
|
||||
const getBossConfigName = player => player?.name1 || player?.name || "";
|
||||
// 将动态难度武将 ID 还原为 config.js 中使用的原始 Boss ID。
|
||||
const getBossConfigName = source => {
|
||||
if (typeof source == "string") return state.originalBossMap[source] || source;
|
||||
const name = source?.storage?.shuYing_tianshu_originalBossName || source?.name1 || source?.name || "";
|
||||
return state.originalBossMap[name] || name;
|
||||
};
|
||||
// 读取当前 Boss 在当前难度下的数值配置;单 Boss 配置覆盖默认难度配置。
|
||||
const getBossDifficultyConfig = player => {
|
||||
const defaultConfig = tianshuConfig.bossDifficulty?.default?.[state.difficulty] || {};
|
||||
@@ -256,27 +264,61 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
// 补充技能只读取当前难度配置,不跨难度继承低难度技能。
|
||||
const getBossDifficultySkills = player => {
|
||||
const defaultSkills = tianshuConfig.bossDifficulty?.default?.[state.difficulty]?.skills || [];
|
||||
const bossSkills = tianshuConfig.bossDifficulty?.bosses?.[getBossConfigName(player)]?.[state.difficulty]?.skills || [];
|
||||
const bossConfig = tianshuConfig.bossDifficulty?.bosses?.[getBossConfigName(player)]?.[state.difficulty] || {};
|
||||
const bossSkills = [];
|
||||
if (Array.isArray(bossConfig.skills)) bossSkills.addArray(bossConfig.skills);
|
||||
const skills = [];
|
||||
for (const skill of defaultSkills.concat(bossSkills)) {
|
||||
if (skill && !skills.includes(skill)) skills.push(skill);
|
||||
}
|
||||
return skills;
|
||||
};
|
||||
// Boss 难度数值属于出场面板修正
|
||||
const applyBossDifficultyConfig = player => {
|
||||
const config = getBossDifficultyConfig(player);
|
||||
// 根据原始武将和当前难度,生成包含准确体力、上限及追加技能的隐藏动态武将。
|
||||
const createDifficultyBossCharacter = originalName => {
|
||||
const original = lib.character[originalName];
|
||||
if (!original) return null;
|
||||
const source = get.convertedCharacter(original);
|
||||
const data = {};
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
data[key] = value && typeof value == "object" ? get.copy(value) : value;
|
||||
}
|
||||
const config = getBossDifficultyConfig(originalName);
|
||||
const maxHp = getConfigNumber(config.maxHp);
|
||||
const maxHpBonus = getConfigNumber(config.maxHpBonus) || 0;
|
||||
const hp = getConfigNumber(config.hp);
|
||||
const hpBonus = getConfigNumber(config.hpBonus) || 0;
|
||||
player.maxHp = Math.max(1, maxHp ?? player.maxHp + maxHpBonus);
|
||||
player.hp = Math.min(Math.max(0, hp ?? player.hp + hpBonus), player.maxHp);
|
||||
for (const skill of getBossDifficultySkills(player)) {
|
||||
if (!player.hasSkill(skill)) player.addSkill(skill);
|
||||
data.maxHp = Math.max(1, maxHp ?? source.maxHp + maxHpBonus);
|
||||
data.hp = Math.min(Math.max(1, hp ?? source.hp + hpBonus), data.maxHp);
|
||||
data.skills = source.skills.slice();
|
||||
data.skills.addArray(getBossDifficultySkills(originalName));
|
||||
data.isHiddenBoss = true;
|
||||
data.isBossAllowed = true;
|
||||
data.isAiForbidden = true;
|
||||
data.tempname = Array.isArray(source.tempname) ? source.tempname.slice() : [];
|
||||
data.tempname.add(originalName);
|
||||
return get.convertedCharacter(data);
|
||||
};
|
||||
// 难度选择后一次性注册本局全部动态 Boss,供召唤、变身及武将牌技能读取共同使用。
|
||||
const prepareDifficultyBossCharacters = () => {
|
||||
state.difficultyBossMap = {};
|
||||
state.originalBossMap = {};
|
||||
const names = tianshuConfig.stages.flatMap(stage => stage?.bosses || []);
|
||||
for (const originalName of Array.from(new Set(names))) {
|
||||
const character = createDifficultyBossCharacter(originalName);
|
||||
if (!character) continue;
|
||||
const dynamicName = `${originalName}_tianshu_${state.difficulty}`;
|
||||
state.difficultyBossMap[originalName] = dynamicName;
|
||||
state.originalBossMap[dynamicName] = originalName;
|
||||
lib.character[dynamicName] = character;
|
||||
lib.translate[dynamicName] = lib.translate[originalName] || get.translation(originalName);
|
||||
if (lib.translate[`${originalName}_ab`]) lib.translate[`${dynamicName}_ab`] = lib.translate[`${originalName}_ab`];
|
||||
if (lib.translate[`${originalName}_prefix`]) lib.translate[`${dynamicName}_prefix`] = lib.translate[`${originalName}_prefix`];
|
||||
if (lib.characterIntro?.[originalName]) lib.characterIntro[dynamicName] = lib.characterIntro[originalName];
|
||||
if (lib.characterTitle?.[originalName]) lib.characterTitle[dynamicName] = lib.characterTitle[originalName];
|
||||
lib.config.forbidai?.add(dynamicName);
|
||||
lib.hiddenCharacters?.add(dynamicName);
|
||||
lib.skilllist.addArray(character.skills);
|
||||
}
|
||||
player.update();
|
||||
return config;
|
||||
};
|
||||
// 让开局占位主 Boss 静默死亡,避免作为关卡 Boss 参与后续流程。
|
||||
const killInitialBossSilently = async () => {
|
||||
@@ -445,7 +487,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
return result;
|
||||
};
|
||||
};
|
||||
// 召唤指定关卡的 Boss,并完成初始 Boss 静默退场和难度强化。
|
||||
// 召唤指定关卡的动态 Boss,并按配置补足仅属于登场流程的初始手牌。
|
||||
const spawnStage = async (stageIndex = state.stageIndex) => {
|
||||
state.stageIndex = stageIndex;
|
||||
state.clearingStage = false;
|
||||
@@ -456,7 +498,6 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
const boss = pool.randomRemove();
|
||||
if (boss) bosses.push(boss);
|
||||
}
|
||||
state.currentBosses = bosses.slice();
|
||||
const seats = getBossSeats();
|
||||
if (stageIndex == 0) await killInitialBossSilently();
|
||||
bosses.forEach((boss, index) => {
|
||||
@@ -464,7 +505,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
});
|
||||
game.arrangePlayers();
|
||||
for (const player of game.players.filter(current => isCurrentStageBoss(current))) {
|
||||
const config = applyBossDifficultyConfig(player);
|
||||
const config = getBossDifficultyConfig(player);
|
||||
const startCards = getConfigNumber(config.startCards);
|
||||
if (startCards === null) continue;
|
||||
const needCards = startCards - player.countCards("h");
|
||||
@@ -618,7 +659,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
if (!isCurrentStageBoss(deadBoss)) return;
|
||||
if (deadBoss.storage.shuYing_tianshu_bossDieHandled) return;
|
||||
deadBoss.storage.shuYing_tianshu_bossDieHandled = true;
|
||||
const stageCleared = !state.currentBosses.some(name => game.players.some(current => current.name1 == name || current.name == name));
|
||||
const stageCleared = !game.players.some(current => isCurrentStageBoss(current));
|
||||
const finalCleared = stageCleared && state.stageIndex >= tianshuConfig.stages.length - 1;
|
||||
if (!finalCleared && event.source && event.source.isIn()) {
|
||||
if (event.source.hp < event.source.maxHp) await event.source.recover(tianshuConfig.reward.killRecover);
|
||||
@@ -637,6 +678,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
capturePlayerSide();
|
||||
state.pendingSpawn = true;
|
||||
await chooseDifficulty(player);
|
||||
prepareDifficultyBossCharacters();
|
||||
_status.additionalReward = () => 500;
|
||||
["shandian", "huoshan", "hongshui", "fulei", "lebu", "bingliang"].forEach(name => lib.inpile.remove(name));
|
||||
Array.from(ui.cardPile.childNodes).forEach(node => {
|
||||
@@ -662,6 +704,8 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) {
|
||||
},
|
||||
handleBossDie,
|
||||
isCurrentStageBoss,
|
||||
getBossConfigName,
|
||||
getDifficultyBossName: originalName => state.difficultyBossMap[originalName] || originalName,
|
||||
// 判断角色是否为玩家方成员,默认要求角色仍在场。
|
||||
isPlayerSideMember: (player, aliveOnly = true) => Boolean(player && (!aliveOnly || player.isIn()) && isCapturedPlayerSide(player) && !isVirtualIdol(player)),
|
||||
// 判断玩家方是否已经全灭。
|
||||
|
||||
Reference in New Issue
Block a user