mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-15 03:29:58 +08:00
fixed # 移除依赖身份模式
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ import initShuYingMenu from "./menu.js";
|
|||||||
|
|
||||||
export const type = "extension";
|
export const type = "extension";
|
||||||
export let shuYing = new Object();
|
export let shuYing = new Object();
|
||||||
const shuYingLocalVersion = "2.0.2.3-rc.1";
|
const shuYingLocalVersion = "2.0.2.3-rc.2";
|
||||||
|
|
||||||
shuYing.name = "术樱包";
|
shuYing.name = "术樱包";
|
||||||
shuYing.editable = false;
|
shuYing.editable = false;
|
||||||
|
|||||||
@@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
`register.js` 是扩展加载入口,`mode.js` 负责组装模式、注册技能并连接各领域模块,`config.js` 提供模式配置。发布器和外部扩展应继续从 `register.js` 加载,避免依赖内部文件路径。
|
`register.js` 是扩展加载入口,`mode.js` 负责组装模式、注册技能并连接各领域模块,`config.js` 提供模式配置。发布器和外部扩展应继续从 `register.js` 加载,避免依赖内部文件路径。
|
||||||
|
|
||||||
|
梦三独立组装模式,不加载或继承身份模式。敌我关系统一由
|
||||||
|
`battle/camps.js` 读取角色的 `storage.mengsanCamp_shuying`:主角、支援和
|
||||||
|
羁绊助战为 `ally`,怪物及敌方援军为 `enemy`。`side` 与梦三专属的
|
||||||
|
`identity` 标记只用于核心及界面兼容,不赋予主公、忠臣或反贼规则。
|
||||||
|
|
||||||
|
技能应使用 `isFriendOf` / `isEnemyOf`、`getFriends` / `getEnemies` 或
|
||||||
|
`get.attitude` 判断敌我。基础态度为同阵营及自身 `+8`、对立阵营 `-8`,
|
||||||
|
未设置有效阵营的其他对象为 `0`;剧情切换阵营后按新阵营判断。
|
||||||
|
`get.effect`、`get.effect_use`、`get.damageEffect`、`get.recoverEffect` 和
|
||||||
|
牌价值、技能效果修正继续使用核心算法,并通过上述态度计算行动收益。
|
||||||
|
有益效果不一律视为正收益,伤害也不一律视为负收益,仍遵循技能自己的 AI 配置。
|
||||||
|
|
||||||
|
梦三不启用主公技,初始技能及动态授予技能均按此限制处理。阵营始终公开,
|
||||||
|
出牌或技能的 `logAi` / `addExpose` 调用不推断隐藏身份;击杀不附加身份奖惩,
|
||||||
|
胜负与奖励沿用梦三关卡和结算流程。
|
||||||
|
|
||||||
| 目录 | 职责 |
|
| 目录 | 职责 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `battle/` | 战斗会话、回合、结算、费用与通用战斗效果 |
|
| `battle/` | 战斗会话、回合、结算、费用与通用战斗效果 |
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// 阵营是梦三敌我关系的唯一来源;side/identity 仅供核心及界面兼容。
|
||||||
|
export const battleCamp = player => {
|
||||||
|
const camp = player?.storage?.mengsanCamp_shuying;
|
||||||
|
return camp === "ally" || camp === "enemy" ? camp : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const campFriend = (from, to) => Boolean(from && to &&
|
||||||
|
(from === to || (battleCamp(from) !== null && battleCamp(from) === battleCamp(to))));
|
||||||
|
|
||||||
|
export const campEnemy = (from, to) => Boolean(from && to && from !== to &&
|
||||||
|
battleCamp(from) !== null && battleCamp(to) !== null &&
|
||||||
|
battleCamp(from) !== battleCamp(to));
|
||||||
|
|
||||||
|
// get.attitude 会继续处理混乱、技能态度修正;get.effect 等沿用核心收益计算。
|
||||||
|
export const campAttitude = (from, to) =>
|
||||||
|
campFriend(from, to) ? 8 : campEnemy(from, to) ? -8 : 0;
|
||||||
|
|
||||||
|
export function setBattleCamp(player, camp) {
|
||||||
|
if (camp !== "ally" && camp !== "enemy") throw new Error("梦三战斗阵营无效:" + camp);
|
||||||
|
player.storage.mengsanCamp_shuying = camp;
|
||||||
|
player.side = camp === "enemy";
|
||||||
|
player.identity = `mengsan_${camp}_shuying`;
|
||||||
|
player.isZhu = false;
|
||||||
|
player.setIdentity(player.identity);
|
||||||
|
player.identityShown = true;
|
||||||
|
player.ai.shown = 1;
|
||||||
|
player.classList.add("mengsan-hide-identity-shuying");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCampPlayerMethods(game) {
|
||||||
|
return {
|
||||||
|
isFriendOf(player) { return campFriend(this, player); },
|
||||||
|
isEnemyOf(player) { return campEnemy(this, player); },
|
||||||
|
getEnemies(filter, includeDie) {
|
||||||
|
return game[includeDie ? "filterPlayer2" : "filterPlayer"](player =>
|
||||||
|
campEnemy(this, player) && (typeof filter !== "function" || filter(player)));
|
||||||
|
},
|
||||||
|
getFriends(filter, includeDie) {
|
||||||
|
const includeSelf = filter === true;
|
||||||
|
return game[includeDie ? "filterPlayer2" : "filterPlayer"](player =>
|
||||||
|
(player !== this || includeSelf) && campFriend(this, player) &&
|
||||||
|
(typeof filter !== "function" || filter(player)));
|
||||||
|
},
|
||||||
|
// 阵营始终公开,不根据行动收益推断隐藏身份或积累暴露值。
|
||||||
|
logAi() { this.ai.shown = 1; },
|
||||||
|
addExpose() { return this; },
|
||||||
|
showIdentity() {
|
||||||
|
const camp = battleCamp(this);
|
||||||
|
if (camp) setBattleCamp(this, camp);
|
||||||
|
},
|
||||||
|
isZhu2() { return false; },
|
||||||
|
hasZhuSkill() { return false; },
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,11 +2,10 @@ import { applyMengsanDebuff } from "../monsters/artifact-status.js";
|
|||||||
import { applyTangled } from "../monsters/vine-tangled.js";
|
import { applyTangled } from "../monsters/vine-tangled.js";
|
||||||
import { attackHitCount, outgoingAttackDamage } from "./intent-damage.js";
|
import { attackHitCount, outgoingAttackDamage } from "./intent-damage.js";
|
||||||
import { beginDeathBlow, isDeathBlowIntent } from "./death-blow.js";
|
import { beginDeathBlow, isDeathBlowIntent } from "./death-blow.js";
|
||||||
|
import { campEnemy } from "./camps.js";
|
||||||
|
|
||||||
export const getIntentHostiles = (game, source) =>
|
export const getIntentHostiles = (game, source) =>
|
||||||
game.players.filter(target => target !== source && target.isAlive() &&
|
game.players.filter(target => target.isAlive() && campEnemy(source, target));
|
||||||
target.storage?.mengsanCamp_shuying !==
|
|
||||||
source.storage?.mengsanCamp_shuying);
|
|
||||||
|
|
||||||
export function createIntentExecutor(game, getActiveBattle) {
|
export function createIntentExecutor(game, getActiveBattle) {
|
||||||
const active = source => source.isAlive() &&
|
const active = source => source.isAlive() &&
|
||||||
|
|||||||
+28
-51
@@ -15,6 +15,7 @@ import { ironcladBlocksDraw, ironcladPreservesBlock, afterIroncladDraw, beforeIr
|
|||||||
import { afterSharedDraw, beforeSharedBlock, sharedStrengthPenalty } from "./cards/shared-hooks.js";
|
import { afterSharedDraw, beforeSharedBlock, sharedStrengthPenalty } from "./cards/shared-hooks.js";
|
||||||
import { hasCardAffix } from "./cards/intrinsic-affixes.js";
|
import { hasCardAffix } from "./cards/intrinsic-affixes.js";
|
||||||
import { createBattleEnergy } from "./cards/battle-energy.js";
|
import { createBattleEnergy } from "./cards/battle-energy.js";
|
||||||
|
import { setBattleCamp, createCampPlayerMethods, campAttitude } from "./battle/camps.js";
|
||||||
import { createRandomHandExhauster } from "./cards/random-hand-exhaust.js";
|
import { createRandomHandExhauster } from "./cards/random-hand-exhaust.js";
|
||||||
import { createBattleCardCopier } from "./cards/battle-card-copy.js";
|
import { createBattleCardCopier } from "./cards/battle-card-copy.js";
|
||||||
import { createBattleCardReclaimer } from "./cards/battle-card-reclaim.js";
|
import { createBattleCardReclaimer } from "./cards/battle-card-reclaim.js";
|
||||||
@@ -335,15 +336,6 @@ const shuffleBattlePile = (run, cards) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setBattleCamp = (player, camp) => {
|
|
||||||
player.storage.mengsanCamp_shuying = camp;
|
|
||||||
player.side = camp === "enemy";
|
|
||||||
player.identity = player === game.me ? "zhu" : camp === "ally" ? "zhong" : "fan";
|
|
||||||
player.setIdentity(player.identity);
|
|
||||||
player.identityShown = true;
|
|
||||||
player.ai.shown = 1;
|
|
||||||
player.classList.add("mengsan-hide-identity-shuying");
|
|
||||||
};
|
|
||||||
const initBattleUnit = (player, spec) => {
|
const initBattleUnit = (player, spec) => {
|
||||||
// 只在实际入场且未指定生命时,用征程随机源抽取各怪物的普通生命范围;资料列表不消耗随机数。
|
// 只在实际入场且未指定生命时,用征程随机源抽取各怪物的普通生命范围;资料列表不消耗随机数。
|
||||||
const monsterSpec = spec.camp === "enemy" && spec.hp == null ?
|
const monsterSpec = spec.camp === "enemy" && spec.hp == null ?
|
||||||
@@ -612,7 +604,6 @@ const prepareBattle = async (run, node, encounter, session, resources) => {
|
|||||||
log: message => game.log(message),
|
log: message => game.log(message),
|
||||||
});
|
});
|
||||||
if (!session.active) return;
|
if (!session.active) return;
|
||||||
game.zhu = me;
|
|
||||||
if (run.player.maxHp == null) {
|
if (run.player.maxHp == null) {
|
||||||
run.player.maxHp = me.maxHp;
|
run.player.maxHp = me.maxHp;
|
||||||
run.player.hp = me.hp;
|
run.player.hp = me.hp;
|
||||||
@@ -723,7 +714,6 @@ const registerPlayers = (session, players) => {
|
|||||||
const index = list.indexOf(player); if (index >= 0) list.splice(index, 1);
|
const index = list.indexOf(player); if (index >= 0) list.splice(index, 1);
|
||||||
}
|
}
|
||||||
if (game.me === player) game.me = null;
|
if (game.me === player) game.me = null;
|
||||||
if (game.zhu === player) game.zhu = null;
|
|
||||||
player.remove();
|
player.remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -893,11 +883,10 @@ const startJourney = async () => {
|
|||||||
await routeRun(run);
|
await routeRun(run);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createMode = identityMode => {
|
const createMode = () => {
|
||||||
const identityElement = identityMode.element || {};
|
const nativeChangeHujia = lib.element.Player.prototype.changeHujia;
|
||||||
const identityPlayer = identityElement.player || {};
|
const nativeChangeHp = lib.element.Player.prototype.changeHp;
|
||||||
const nativeChangeHujia = identityPlayer.changeHujia || lib.element.Player.prototype.changeHujia;
|
const nativeAddSkill = lib.element.Player.prototype.addSkill;
|
||||||
const nativeChangeHp = identityPlayer.changeHp || lib.element.Player.prototype.changeHp;
|
|
||||||
const cardContext = {
|
const cardContext = {
|
||||||
game, get, lib, random:nextRandom, status:_status, getBattle:()=>activeBattle,
|
game, get, lib, random:nextRandom, status:_status, getBattle:()=>activeBattle,
|
||||||
copyToDiscard: createBattleCardCopier(game, () => activeBattle),
|
copyToDiscard: createBattleCardCopier(game, () => activeBattle),
|
||||||
@@ -921,10 +910,9 @@ const createMode = identityMode => {
|
|||||||
const modeCards = createMengsanCards({cardPacks,isBattleActive:cardContext.isBattleActive});
|
const modeCards = createMengsanCards({cardPacks,isBattleActive:cardContext.isBattleActive});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...identityMode,
|
|
||||||
name: MODE_ID,
|
name: MODE_ID,
|
||||||
character: { ...(identityMode.character || {}), ...createScenarioCharacters(lib.element.Character) },
|
character: createScenarioCharacters(lib.element.Character),
|
||||||
card: { ...(identityMode.card || {}), ...modeCards.card },
|
card: modeCards.card,
|
||||||
connect: false,
|
connect: false,
|
||||||
start: [
|
start: [
|
||||||
async () => {
|
async () => {
|
||||||
@@ -933,9 +921,14 @@ const createMode = identityMode => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
element: {
|
element: {
|
||||||
...identityElement,
|
|
||||||
player: {
|
player: {
|
||||||
...identityPlayer,
|
...createCampPlayerMethods(game),
|
||||||
|
addSkill(skill, ...args) {
|
||||||
|
// 初始武将技能由核心过滤;同时阻止奖励、剧情等动态授予主公技。
|
||||||
|
if (Array.isArray(skill)) skill = skill.filter(name => !lib.skill[name]?.zhuSkill);
|
||||||
|
else if (lib.skill[skill]?.zhuSkill) return;
|
||||||
|
return nativeAddSkill.call(this, skill, ...args);
|
||||||
|
},
|
||||||
async dieAfter() {
|
async dieAfter() {
|
||||||
// 原生死亡已成立;在战斗结算关闭会话前落实斩杀的永久收益。
|
// 原生死亡已成立;在战斗结算关闭会话前落实斩杀的永久收益。
|
||||||
if(this.isDead())await cardPacks.onDeath(_status.event);
|
if(this.isDead())await cardPacks.onDeath(_status.event);
|
||||||
@@ -948,11 +941,7 @@ const createMode = identityMode => {
|
|||||||
message => game.log(message));
|
message => game.log(message));
|
||||||
game.checkResult();
|
game.checkResult();
|
||||||
},
|
},
|
||||||
dieAfter2() {}, // Identity-mode kill rewards/loyalist penalties do not apply here.
|
dieAfter2() {}, // 击杀不额外摸牌或弃牌,奖励由梦三结算流程处理。
|
||||||
isFriendOf(player) { return this === player || (this.storage.mengsanCamp_shuying === player?.storage?.mengsanCamp_shuying); },
|
|
||||||
isEnemyOf(player) { return Boolean(player && this.storage.mengsanCamp_shuying !== player.storage?.mengsanCamp_shuying); },
|
|
||||||
getEnemies(filter, includeDie) { return game[includeDie ? "filterPlayer2" : "filterPlayer"](p => this.isEnemyOf(p) && (!filter || filter(p))); },
|
|
||||||
getFriends(filter, includeDie) { const self = filter === true; return game[includeDie ? "filterPlayer2" : "filterPlayer"](p => (p !== this || self) && this.isFriendOf(p) && (typeof filter !== "function" || filter(p))); },
|
|
||||||
changeHp(num, popup) {
|
changeHp(num, popup) {
|
||||||
const next = nativeChangeHp.call(this, num, popup);
|
const next = nativeChangeHp.call(this, num, popup);
|
||||||
if (activeBattle?.session.active) capInkletHpLoss(this, next);
|
if (activeBattle?.session.active) capInkletHpLoss(this, next);
|
||||||
@@ -985,7 +974,12 @@ const createMode = identityMode => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
game: {
|
game: {
|
||||||
...(identityMode.game || {}),
|
getVideoName() {
|
||||||
|
const me = game.me;
|
||||||
|
const names = [me?.name1 || me?.name, me?.name2].filter(Boolean);
|
||||||
|
return [names.map(name => get.translation(name)).join("/") || "梦三",
|
||||||
|
"梦三" + (_status.mengsanEncounter_shuying?.name ? " · " + _status.mengsanEncounter_shuying.name : "")];
|
||||||
|
},
|
||||||
mengsanSetEnemyIntent_shuying(player, intent) {
|
mengsanSetEnemyIntent_shuying(player, intent) {
|
||||||
const update = activeBattle?.intentUI.get(player);
|
const update = activeBattle?.intentUI.get(player);
|
||||||
if (!update) return false;
|
if (!update) return false;
|
||||||
@@ -1239,11 +1233,10 @@ const createMode = identityMode => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
get: {
|
get: {
|
||||||
...(identityMode.get || {}),
|
rawAttitude: campAttitude,
|
||||||
rawAttitude(from, to) { return from === to || from.storage.mengsanCamp_shuying === to.storage.mengsanCamp_shuying ? 8 : -8; },
|
realAttitude: campAttitude,
|
||||||
},
|
},
|
||||||
skill: {
|
skill: {
|
||||||
...(identityMode.skill || {}),
|
|
||||||
...cardPacks.skills,
|
...cardPacks.skills,
|
||||||
...createRelicSkills(() => activeBattle, () => game.me),
|
...createRelicSkills(() => activeBattle, () => game.me),
|
||||||
...createRelicCombatSkills(() => activeBattle, () => game.me),
|
...createRelicCombatSkills(() => activeBattle, () => game.me),
|
||||||
@@ -1829,7 +1822,10 @@ const createMode = identityMode => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
translate: {
|
translate: {
|
||||||
...(identityMode.translate || {}),
|
mengsan_ally_shuying: "友",
|
||||||
|
mengsan_ally_shuying2: "友方",
|
||||||
|
mengsan_enemy_shuying: "敌",
|
||||||
|
mengsan_enemy_shuying2: "敌方",
|
||||||
...modeCards.translate,
|
...modeCards.translate,
|
||||||
...scenarioTranslations,
|
...scenarioTranslations,
|
||||||
mengsan_draw_shuying: "梦三牌组",
|
mengsan_draw_shuying: "梦三牌组",
|
||||||
@@ -1912,26 +1908,7 @@ export async function createMengsanMode() {
|
|||||||
const style = lib.init.css(`${STYLE_PATH}/ui`, "style", resolve);
|
const style = lib.init.css(`${STYLE_PATH}/ui`, "style", resolve);
|
||||||
style.addEventListener("error", resolve, { once: true });
|
style.addEventListener("error", resolve, { once: true });
|
||||||
});
|
});
|
||||||
const identityMode = await new Promise((resolve, reject) => {
|
const mode = createMode();
|
||||||
let received = false;
|
|
||||||
const receiveMode = mode => {
|
|
||||||
if (!mode || typeof mode !== "object" || mode.name !== "identity") {
|
|
||||||
reject(new Error("梦三启动失败:未取得有效的身份模式,请检查游戏核心与身份模式文件。"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
received = true;
|
|
||||||
resolve(mode);
|
|
||||||
};
|
|
||||||
// 部分核心仅通过回调交付模式;新版也可能返回 Promise。
|
|
||||||
const loading = game.loadModeAsync("identity", receiveMode, reject);
|
|
||||||
if (loading && typeof loading.then === "function") {
|
|
||||||
Promise.resolve(loading).then(mode => {
|
|
||||||
// 回调版的 Promise 可能返回 undefined,此时模式已由回调交付。
|
|
||||||
if (!received) receiveMode(mode);
|
|
||||||
}, reject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const mode = createMode(identityMode);
|
|
||||||
mode.splash = "ext:术樱包/pve/images/tianshu.jpg";
|
mode.splash = "ext:术樱包/pve/images/tianshu.jpg";
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user