From 2a91f4367507d975b7505528aa706a8d3c9cacce Mon Sep 17 00:00:00 2001 From: Art_Sakura <1754798088@qq.com> Date: Wed, 14 Oct 2026 18:06:14 +0800 Subject: [PATCH] =?UTF-8?q?fixed=20#=20=E6=96=B0=E5=A2=9E=E5=8D=B0?= =?UTF-8?q?=E7=89=8C=E7=BE=81=E7=BB=8A=20fixed=20#=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extension.js | 2 +- function.js | 14 ++++ menu.js | 8 +++ tianshu/config.js | 29 ++++++++ tianshu/directDamage.js | 3 + tianshu/extension.js | 134 +++++++++++++++++++++++++++++++++++-- tianshu/skillCard.js | 64 ++++++++++++++++++ tianshu/skillRecover.js | 3 + tools/manifest_config.json | 1 + 9 files changed, 251 insertions(+), 7 deletions(-) create mode 100644 tianshu/skillCard.js diff --git a/extension.js b/extension.js index 0d064c8..c751895 100644 --- a/extension.js +++ b/extension.js @@ -6,7 +6,7 @@ import { configureDiagnostics } from "./diagnostics/index.js"; export const type = "extension"; export let shuYing = new Object(); -const shuYingLocalVersion = "2.0.2.3-rc.4"; +const shuYingLocalVersion = "2.0.2.3-rc.5"; shuYing.name = "术樱包"; shuYing.editable = false; diff --git a/function.js b/function.js index 761162c..a2920ea 100644 --- a/function.js +++ b/function.js @@ -1,5 +1,6 @@ import { getDirectDamageSkillCount } from "./tianshu/directDamage.js"; import { getSkillRecoverCount } from "./tianshu/skillRecover.js"; +import { getSkillCardCount } from "./tianshu/skillCard.js"; export default function initFunction(lib, game, ui, get, ai, _status, shuYing = {}) { game.shuying_getDiscardNamesNum = function () { @@ -397,6 +398,10 @@ export default function initFunction(lib, game, ui, get, ai, _status, shuYing = return getSkillRecoverCount(player, currentSkills, lib) >= Math.max(1, Number(condition.count) || 1); } + if (condition.type == "skillCard") { + return getSkillCardCount(player, currentSkills, lib) + >= Math.max(1, Number(condition.count) || 1); + } if (condition.type == "skill") return currentSkills.includes(condition.skill); if (condition.type == "skillName") return currentSkills.some(skill => lib.translate[skill] == condition.name); if (condition.type == "skills") { @@ -445,6 +450,9 @@ export default function initFunction(lib, game, ui, get, ai, _status, shuYing = if (condition.type == "skillRecover") { return getSkillRecoverCount(player, currentSkills, lib) > 0; } + if (condition.type == "skillCard") { + return getSkillCardCount(player, currentSkills, lib) > 0; + } if (condition.type == "skill") return currentSkills.includes(condition.skill); if (condition.type == "skillName") return currentSkills.some(skill => lib.translate[skill] == condition.name); if (condition.type == "skills") return [...(condition.all || []), ...(condition.any || [])].some(skill => currentSkills.includes(skill)); @@ -482,6 +490,12 @@ export default function initFunction(lib, game, ui, get, ai, _status, shuYing = return `已实际回复体力的奖励技能 ${Math.min(count, need)}` + `/${need};首次有效技能回复后登记`; } + if (condition.type == "skillCard") { + const count = getSkillCardCount(player, currentSkills, lib); + const need = Math.max(1, Number(condition.count) || 1); + return `已实际视为或转化牌的奖励技能 ${Math.min(count, need)}` + + `/${need};首次使用或打出后登记`; + } if (condition.type == "skill") return `${currentSkills.includes(condition.skill) ? "已" : "未"}选择【${get.translation(condition.skill)}】`; if (condition.type == "skillName") return `${currentSkills.some(skill => lib.translate[skill] == condition.name) ? "已" : "未"}选择任意版本的【${condition.name}】`; if (condition.type == "skills") { diff --git a/menu.js b/menu.js index 066f0b6..db89709 100644 --- a/menu.js +++ b/menu.js @@ -78,6 +78,9 @@ export default function initShuYingMenu({ lib, game, ui, shuYing, updater }) { if (condition.type == "skillRecover") { return ["直接回复体力的过关技能"]; } + if (condition.type == "skillCard") { + return ["视为打出牌或转化牌的过关技能"]; + } if (condition.type == "skill") return condition.skill ? [get.translation(condition.skill)] : []; if (condition.type == "skillName") return condition.name ? [condition.name] : []; if (condition.type == "skills") { @@ -108,6 +111,11 @@ export default function initShuYingMenu({ lib, game, ui, shuYing, updater }) { + "按当前仍持有的不同技能译名统计,新等级从后续回复生效;" + "二级追加回复不会再次触发桃园,三级溢出只增加体力上限"; } + if (condition.type == "skillCard") { + return "奖励技能首次实际使用或打出视为牌、转化牌后登记,子技能归到主技能;" + + "按当前仍持有的不同技能译名统计,新等级从后续用牌生效;" + + "响应打出只登记;目标调整至少保留1个,三级对全部指定目标再次结算"; + } if (!condition.type && [...(condition.all || []), ...(condition.any || [])].some(item => item?.type == "skillNames")) { return "按不同译名统计;同译名的不同技能 ID 只计 1 种过关技能"; } diff --git a/tianshu/config.js b/tianshu/config.js index a6cf4e5..e15d565 100644 --- a/tianshu/config.js +++ b/tianshu/config.js @@ -214,6 +214,35 @@ const tianshuConfig = { banned: ["huoxin", "jueqing", "qinqing", "beige", "huashen", "drlt_zhiti", "olzhiti", "xinfu_pdgyingshi", "rebeige"], }, bonds: [ + { + id: "yinpai", + name: "印牌", + condition: { type: "skillCard" }, + levels: [ + { + level: 1, + count: 1, + skills: ["shuYing_Tianshu_BondYinpai1"], + text: "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标," + + "至少保留1个目标", + }, + { + level: 2, + count: 2, + skills: ["shuYing_Tianshu_BondYinpai2"], + text: "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标," + + "至少保留1个目标,且此牌无法被响应", + }, + { + level: 3, + count: 3, + skills: ["shuYing_Tianshu_BondYinpai3"], + text: "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标," + + "至少保留1个目标,且此牌无法被响应," + + "并对全部指定目标额外结算一次,不重新选择目标", + }, + ], + }, { id: "taoyuan", name: "桃园", diff --git a/tianshu/directDamage.js b/tianshu/directDamage.js index 90dd28e..52b33d2 100644 --- a/tianshu/directDamage.js +++ b/tianshu/directDamage.js @@ -7,6 +7,9 @@ export function getDirectDamageOrigin(event, lib, get) { const visited = new Set(); while (current && !visited.has(current)) { visited.add(current); + if (["card", "precard", "postcard"].includes(current.type)) { + return null; + } if (["useCard", "respond", "damage", "_lianhuan", "_lianhuan2"] .includes(current.name) || current.name?.endsWith("_cost")) { return null; diff --git a/tianshu/extension.js b/tianshu/extension.js index 1e0ab3d..255f17f 100644 --- a/tianshu/extension.js +++ b/tianshu/extension.js @@ -4,6 +4,8 @@ import { directDamageStorage, getDirectDamageOrigin, getDirectDamageSkillCount } from "./directDamage.js"; import { skillRecoverStorage, getSkillRecoverOrigin, getSkillRecoverCount, wrapSkillRecoverEvent } from "./skillRecover.js"; +import { skillCardStorage, getSkillCardOrigin, getSkillCardCount, + getSkillCardTargetOptions, applySkillCardEffects } from "./skillCard.js"; import { lib, game, ui, get, ai, _status } from "../../../noname.js"; export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { @@ -756,6 +758,10 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { return getSkillRecoverCount(player, owned, lib) >= Math.max(1, Number(condition.count) || 1); } + if (condition.type == "skillCard") { + return getSkillCardCount(player, owned, lib) + >= Math.max(1, Number(condition.count) || 1); + } if (condition.type == "keyword") { if (!condition.keyword) return false; const marker = `【${condition.keyword}】`; @@ -836,6 +842,9 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { if (condition.type == "skillRecover") { return getSkillRecoverCount(player, [skill], lib) > 0; } + if (condition.type == "skillCard") { + return getSkillCardCount(player, [skill], lib) > 0; + } if (condition.type == "keyword") { if (!condition.keyword) return false; const info = typeof get.skillInfoTranslation == "function" ? get.skillInfoTranslation(skill, player) : lib.translate[`${skill}_info`]; @@ -1364,6 +1373,7 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { game.addGlobalSkill("shuYing_Tianshu_DirectDamageCapture"); game.addGlobalSkill("shuYing_Tianshu_DirectDamageSettle"); game.addGlobalSkill("shuYing_Tianshu_SkillRecoverCapture"); + game.addGlobalSkill("shuYing_Tianshu_SkillCardCapture"); }, }, shuYing_Tianshu_DefendingControl: { @@ -1402,9 +1412,15 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { game.getGlobalHistory().isRound = true; }, }, - shuYing_Tianshu_BondDirectDamage1: { charlotte: true, popup: false }, - shuYing_Tianshu_BondDirectDamage2: { charlotte: true, popup: false }, - shuYing_Tianshu_BondDirectDamage3: { charlotte: true, popup: false }, + shuYing_Tianshu_BondDirectDamage1: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondDirectDamage2: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondDirectDamage3: { + charlotte: true, popup: false, nopop: true, + }, shuYing_Tianshu_DirectDamageCapture: { mode: ["boss"], trigger: { global: "damageBegin1" }, @@ -1483,9 +1499,15 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { if (level >= 2 && player.isIn()) await player.draw(num); }, }, - shuYing_Tianshu_BondTaoyuan1: { charlotte: true, popup: false }, - shuYing_Tianshu_BondTaoyuan2: { charlotte: true, popup: false }, - shuYing_Tianshu_BondTaoyuan3: { charlotte: true, popup: false }, + shuYing_Tianshu_BondTaoyuan1: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondTaoyuan2: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondTaoyuan3: { + charlotte: true, popup: false, nopop: true, + }, shuYing_Tianshu_SkillRecoverCapture: { mode: ["boss"], trigger: { global: "recoverBegin" }, @@ -1509,6 +1531,95 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { if (record.level >= 1) trigger.num++; }, }, + shuYing_Tianshu_BondYinpai1: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondYinpai2: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_BondYinpai3: { + charlotte: true, popup: false, nopop: true, + }, + shuYing_Tianshu_SkillCardCapture: { + mode: ["boss"], + trigger: { global: ["useCard2", "respond"] }, + forced: true, + silent: true, + charlotte: true, + firstDo: true, + priority: 1000, + filter(event) { + const player = event.player; + if (!state.running || event.shuYing_tianshu_yinpaiHandled + || !player?.isIn() || !isCapturedPlayerSide(player) + || (isVirtualIdol(player) + && !canVirtualIdolReceiveRewards())) return false; + return Boolean(getSkillCardOrigin(event, lib, get)); + }, + async content(event, trigger) { + if (trigger.shuYing_tianshu_yinpaiHandled) return; + const origin = getSkillCardOrigin(trigger, lib, get); + if (!origin) return; + trigger.shuYing_tianshu_yinpaiHandled = true; + const player = trigger.player; + const level = player.storage.shuYing_tianshu_bondLevels + ?.yinpai || 0; + const rewardSkill = getTianshuRewardSkills(player) + .find(current => get.sourceSkillFor(current) + == origin.skill); + if (rewardSkill) { + const recorded = player.storage[skillCardStorage] || []; + if (!recorded.includes(rewardSkill)) { + recorded.push(rewardSkill); + player.storage[skillCardStorage] = recorded; + game.broadcast((current, key, value) => { + current.storage[key] = value; + }, player, skillCardStorage, recorded); + player.syncStorage?.(skillCardStorage); + refreshTianshuBonds(player); + game.log(player, "登记印牌技能", + `【${get.translation(rewardSkill)}】`); + } + } + if (!level || trigger.name != "useCard") return; + const options = getSkillCardTargetOptions( + trigger, lib, game, get); + const candidates = options.add.concat(options.remove); + if (candidates.length) { + const result = await player.chooseTarget( + "印牌:增加或减少1个合法目标(至少保留1个)", + (card, current, target) => target.isIn() + && candidates.includes(target)) + .set("ai", target => { + const effect = get.effect(target, trigger.card, + player, player); + return options.remove.includes(target) + ? -effect : effect; + }).forResult(); + const target = result.targets?.[0]; + if (result.bool && target?.isIn()) { + const valid = getSkillCardTargetOptions( + trigger, lib, game, get); + if (options.remove.includes(target) + && valid.remove.includes(target)) { + trigger.targets.splice( + trigger.targets.indexOf(target), 1); + game.log(player, "移除", trigger.card, + "的目标", target); + } else if (options.add.includes(target) + && valid.add.includes(target)) { + trigger.targets.push(target); + game.log(player, "为", trigger.card, + "增加目标", target); + } + player.line(target); + } + } + applySkillCardEffects(trigger, level, game.players); + if (level >= 3) game.log(trigger.card, + "对全部指定目标额外结算一次"); + }, + }, shuYing_Tianshu_BondManager: { mode: ["boss"], trigger: { global: "changeSkillsAfter" }, @@ -1609,6 +1720,17 @@ export default function initTianshu(lib, game, ui, get, ai, _status, shuYing) { shuYing_Skill_Tianshu_intro5: { nobracket: true }, }; tianshu.translate = { + shuYing_Tianshu_BondYinpai1: "印牌·壹", + shuYing_Tianshu_BondYinpai1_info: + "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标,至少保留1个目标。", + shuYing_Tianshu_BondYinpai2: "印牌·贰", + shuYing_Tianshu_BondYinpai2_info: + "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标," + + "至少保留1个目标,且此牌无法被响应。", + shuYing_Tianshu_BondYinpai3: "印牌·叁", + shuYing_Tianshu_BondYinpai3_info: + "你使用技能视为牌或转化牌时,可以增加或减少1个合法目标," + + "至少保留1个目标,且此牌无法被响应,并对全部指定目标额外结算一次。", shuYing_Tianshu_BondTaoyuan1: "桃园·壹", shuYing_Tianshu_BondTaoyuan1_info: "你通过技能回复体力时,回复值+1。", shuYing_Tianshu_BondTaoyuan2: "桃园·贰", diff --git a/tianshu/skillCard.js b/tianshu/skillCard.js new file mode 100644 index 0000000..bb3f2a5 --- /dev/null +++ b/tianshu/skillCard.js @@ -0,0 +1,64 @@ +export const skillCardStorage = "shuYing_tianshu_skillCardSkills"; + +export function getSkillCardOrigin(event, lib, get) { + if (!["useCard", "respond"].includes(event?.name) || !event.card) { + return null; + } + const virtual = get.is.virtualCard(event.card) + && (!event.card.isCard || !event.cards?.length); + if (!virtual && !get.is.convertedCard(event.card)) return null; + if (event.skill && lib.skill[event.skill]) { + return { skill: get.sourceSkillFor(event.skill), player: event.player }; + } + let current = event.getParent?.(1, true); + const visited = new Set(); + while (current && !visited.has(current)) { + visited.add(current); + if (["useCard", "respond"].includes(current.name) + || ["card", "precard", "postcard"].includes(current.type) + || current.name?.endsWith("_cost")) return null; + const skill = lib.skill[current.name] ? current.name : current.skill; + if (skill && lib.skill[skill]) { + if (current.player != event.player) return null; + return { skill: get.sourceSkillFor(skill), player: current.player }; + } + current = current.getParent?.(1, true); + } + return null; +} + +export function getSkillCardCount(player, skills, lib) { + const recorded = player?.storage?.[skillCardStorage]; + if (!Array.isArray(recorded)) return 0; + return new Set(skills.filter(skill => recorded.includes(skill)) + .map(skill => lib.translate[skill] || skill)).size; +} + +export function getSkillCardTargetOptions(event, lib, game, get) { + const options = { add: [], remove: [] }; + const info = get.info(event.card); + if (!info || !event.targets?.length || info.notarget || info.toself + || info.complexTarget || info.singleCard + || info.filterAddedTarget) return options; + if (event.targets.length > 1) { + options.remove = event.targets.filter(target => target.isIn()); + } + if (info.allowMultiple !== false) { + options.add = game.players.filter(target => target.isIn() + && !event.targets.includes(target) + && lib.filter.targetEnabled2(event.card, event.player, target) + && lib.filter.targetInRange(event.card, event.player, target)); + } + return options; +} + +export function applySkillCardEffects(event, level, players) { + if (level >= 2) { + if (!Array.isArray(event.directHit)) event.directHit = []; + for (const player of players) { + if (!event.directHit.includes(player)) event.directHit.push(player); + } + event.nowuxie = true; + } + if (level >= 3) event.effectCount = (Number(event.effectCount) || 1) + 1; +} diff --git a/tianshu/skillRecover.js b/tianshu/skillRecover.js index 2467667..dd3d944 100644 --- a/tianshu/skillRecover.js +++ b/tianshu/skillRecover.js @@ -8,6 +8,9 @@ export function getSkillRecoverOrigin(event, lib, get) { const visited = new Set(); while (current && !visited.has(current)) { visited.add(current); + if (["card", "precard", "postcard"].includes(current.type)) { + return null; + } if (["useCard", "respond", "recover", "damage"] .includes(current.name) || current.name?.endsWith("_cost")) { return null; diff --git a/tools/manifest_config.json b/tools/manifest_config.json index c97138e..5b79fcf 100644 --- a/tools/manifest_config.json +++ b/tools/manifest_config.json @@ -11,6 +11,7 @@ "tianshu/config.js", "tianshu/directDamage.js", "tianshu/skillRecover.js", + "tianshu/skillCard.js", "pve/boss/index.js", "pve/boss/card.js", "pve/boss/character.js",