feedback1d WL: review follow-ups (源石溶剂 on 炎佑, enemy BUFF damage, self-damage stats, chimera aura)

- 源石溶剂: PRTS 备注 "携带后,全场范围内的所有敌人类我方单位也会获得此装备的“每秒受到60真实伤害”效果
  (该效果的付与为我方阵营索敌,可对空,无视目标可选性)". Every enemy-record ally of the field (炎佑,
  a partner's too) takes the 无来源 60 true-damage tick while a carrier is on the field, credited to
  nobody, once per second however many carriers (PRTS 作战机制 "同名buff的默认叠加策略buff只能表现出一个").
- 码头水手 drowning ("每秒受到1000点无来源真实伤害") and 弧光锋卫's 失衡 bleed (修正 "失衡移动时持续受到真实
  伤害") are damage, not 流失 (PRTS 伤害分类 lists both as BUFF damage).
- 孽罪奇美拉 污染模式 follows PRTS: radius 1.2, a tick every 0.5 s, one tick per unit per period
  however many chimeras reach it ("同类效果取最高").
- Damage-dealt stats (unit dmg, the player's damageDealt) no longer count damage to the source's own
  side (a carrier's own drain, an operator's own 流失); taken and the kill credit are unchanged.
- 机变 自愈 uses damage.js isHpLoss.
- SIM.md: the loseHp rule lists the remaining 流失 sources; the chimera aura; the stats rule.
  data/items.json regenerated (源石溶剂 implFormula text only).
This commit is contained in:
sganggs committed 2026-10-03 05:09:42 +08:00
1 parent 01f5b216c6
commit 88d578b3fe
9 files changed
+204 -23

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"chess_item_1_01_e_a":{"id":"chess_item_1_01_e_a","baseId":"chess_item_1_01_e_a","goldenId":"chess_item_1_01_e_b","isGolden":false,"trapId":"trap_1041_acarm041","iconId":"trap_1041_acarm041","identifier":267,"name":"维式重锤","itemType":"EQUIP","tier":1,"shopSortId":1,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_01_e_b","duration":-1,"giveBondId":"victoriaShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm041","effectName":"维式重锤","desc":"攻击力+15%","descRaw":"攻击力<@ba.vup>+15%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"atk":0.15},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"atk":0.15,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"ATK% += atk (additive with other ATK% buffs)","rangeGrid":[[0,0]],"flavor":"一柄曾在维多利亚的工厂内铸造和平的重锤。\n“用同样的铁与火,去终结那场战争。”"},"chess_item_1_01_e_b":{"id":"chess_item_1_01_e_b","baseId":"chess_item_1_01_e_a","goldenId":"chess_item_1_01_e_b","isGolden":true,"trapId":"trap_1041_acarm041","iconId":"trap_1041_acarm041","identifier":326,"name":"维式重锤","itemType":"EQUIP","tier":1,"shopSortId":1,"price":5,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":false,"upgradeNum":0,"upgradeChessId":null,"duration":0,"giveBondId":"victoriaShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acgarm041","effectName":"维式重锤","desc":"攻击力+25%","descRaw":"攻击力<@ba.vup>+25%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"atk":0.25},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"atk":0.25,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"ATK% += atk (additive with other ATK% buffs)","rangeGrid":[[0,0]],"flavor":"一柄曾在维多利亚的工厂内铸造和平的重锤。\n“用同样的铁与火,去终结那场战争。”"},"chess_item_1_02_e_a":{"id":"chess_item_1_02_e_a","baseId":"chess_item_1_02_e_a","goldenId":"chess_item_1_02_e_b","isGolden":false,"trapId":"trap_1047_acarm047","iconId":"trap_1047_acarm047","identifier":273,"name":"坚守盾牌","itemType":"EQUIP","tier":1,"shopSortId":2,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_02_e_b","duration":-1,"giveBondId":"steadShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm047","effectName":"坚守盾牌","desc":"防御力+20%","descRaw":"防御力<@ba.vup>+20%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"def":0.2},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"def":0.2,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"DEF% += def","rangeGrid":[[0,0]],"flavor":"一面属于坚守之人的盾牌。\n“推击、拨挡、压制,主动防御的基础在于对盾的运用。”"},"chess_item_1_02_e_b":{"id":"chess_item_1_02_e_b","baseId":"chess_item_1_02_e_a","goldenId":"chess_item_1_02_e_b","isGolden":true,"trapId":"trap_1047_acarm047","iconId":"trap_1047_acarm047","identifier":332,"name":"坚守盾牌","itemType":"EQUIP","tier":1,"shopSortId":2,"price":5,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":false,"upgradeNum":0,"upgradeChessId":null,"duration":0,"giveBondId":"steadShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acgarm047","effectName":"坚守盾牌","desc":"防御力+35%","descRaw":"防御力<@ba.vup>+35%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"def":0.35},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"def":0.35,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"DEF% += def","rangeGrid":[[0,0]],"flavor":"一面属于坚守之人的盾牌。\n“推击、拨挡、压制,主动防御的基础在于对盾的运用。”"},"chess_item_1_03_e_a":{"id":"chess_item_1_03_e_a","baseId":"chess_item_1_03_e_a","goldenId":"chess_item_1_03_e_b","isGolden":false,"trapId":"trap_1066_acarm066","iconId":"trap_1066_acarm066","identifier":292,"name":"盟约之币","itemType":"EQUIP","tier":1,"shopSortId":3,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_03_e_b","duration":-1,"giveBondId":null,"givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm066","effectName":"盟约之币","desc":"装备时销毁,并获得1资金","descRaw":"装备时销毁,并获得<@ba.vup>1</>资金","buffs":[{"key":"equip_destory_gain_random_coin","countType":"NONE","bb":{"min":1,"max":1},"bbStr":{}}],"params":{"min":1,"max":1},"category":"ECONOMY",Line truncated
{"chess_item_1_01_e_a":{"id":"chess_item_1_01_e_a","baseId":"chess_item_1_01_e_a","goldenId":"chess_item_1_01_e_b","isGolden":false,"trapId":"trap_1041_acarm041","iconId":"trap_1041_acarm041","identifier":267,"name":"维式重锤","itemType":"EQUIP","tier":1,"shopSortId":1,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_01_e_b","duration":-1,"giveBondId":"victoriaShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm041","effectName":"维式重锤","desc":"攻击力+15%","descRaw":"攻击力<@ba.vup>+15%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"atk":0.15},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"atk":0.15,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"ATK% += atk (additive with other ATK% buffs)","rangeGrid":[[0,0]],"flavor":"一柄曾在维多利亚的工厂内铸造和平的重锤。\n“用同样的铁与火,去终结那场战争。”"},"chess_item_1_01_e_b":{"id":"chess_item_1_01_e_b","baseId":"chess_item_1_01_e_a","goldenId":"chess_item_1_01_e_b","isGolden":true,"trapId":"trap_1041_acarm041","iconId":"trap_1041_acarm041","identifier":326,"name":"维式重锤","itemType":"EQUIP","tier":1,"shopSortId":1,"price":5,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":false,"upgradeNum":0,"upgradeChessId":null,"duration":0,"giveBondId":"victoriaShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acgarm041","effectName":"维式重锤","desc":"攻击力+25%","descRaw":"攻击力<@ba.vup>+25%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"atk":0.25},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"atk":0.25,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"ATK% += atk (additive with other ATK% buffs)","rangeGrid":[[0,0]],"flavor":"一柄曾在维多利亚的工厂内铸造和平的重锤。\n“用同样的铁与火,去终结那场战争。”"},"chess_item_1_02_e_a":{"id":"chess_item_1_02_e_a","baseId":"chess_item_1_02_e_a","goldenId":"chess_item_1_02_e_b","isGolden":false,"trapId":"trap_1047_acarm047","iconId":"trap_1047_acarm047","identifier":273,"name":"坚守盾牌","itemType":"EQUIP","tier":1,"shopSortId":2,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_02_e_b","duration":-1,"giveBondId":"steadShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm047","effectName":"坚守盾牌","desc":"防御力+20%","descRaw":"防御力<@ba.vup>+20%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"def":0.2},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"def":0.2,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"DEF% += def","rangeGrid":[[0,0]],"flavor":"一面属于坚守之人的盾牌。\n“推击、拨挡、压制,主动防御的基础在于对盾的运用。”"},"chess_item_1_02_e_b":{"id":"chess_item_1_02_e_b","baseId":"chess_item_1_02_e_a","goldenId":"chess_item_1_02_e_b","isGolden":true,"trapId":"trap_1047_acarm047","iconId":"trap_1047_acarm047","identifier":332,"name":"坚守盾牌","itemType":"EQUIP","tier":1,"shopSortId":2,"price":5,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":false,"upgradeNum":0,"upgradeChessId":null,"duration":0,"giveBondId":"steadShip","givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acgarm047","effectName":"坚守盾牌","desc":"防御力+35%","descRaw":"防御力<@ba.vup>+35%</>","buffs":[{"key":"env_gbuff_new_with_verify","countType":"NONE","bb":{"def":0.35},"bbStr":{"key":"attr_common_global_buff"}}],"params":{"def":0.35,"key":"attr_common_global_buff"},"category":"STAT","kind":"passive","family":null,"implFormula":"DEF% += def","rangeGrid":[[0,0]],"flavor":"一面属于坚守之人的盾牌。\n“推击、拨挡、压制,主动防御的基础在于对盾的运用。”"},"chess_item_1_03_e_a":{"id":"chess_item_1_03_e_a","baseId":"chess_item_1_03_e_a","goldenId":"chess_item_1_03_e_b","isGolden":false,"trapId":"trap_1066_acarm066","iconId":"trap_1066_acarm066","identifier":292,"name":"盟约之币","itemType":"EQUIP","tier":1,"shopSortId":3,"price":1,"hideInShop":false,"shopExcluded":false,"shopExcludedBy":null,"mergeable":true,"upgradeNum":2,"upgradeChessId":"chess_item_1_03_e_b","duration":-1,"giveBondId":null,"givePowerId":null,"canGiveBond":false,"requiresBondId":null,"effectId":"eff_acarm066","effectName":"盟约之币","desc":"装备时销毁,并获得1资金","descRaw":"装备时销毁,并获得<@ba.vup>1</>资金","buffs":[{"key":"equip_destory_gain_random_coin","countType":"NONE","bb":{"min":1,"max":1},"bbStr":{}}],"params":{"min":1,"max":1},"category":"ECONOMY",Line truncated
+17 -7
View File
@@ -224,9 +224,10 @@ tick, damage-taken modifiers scale it and it counts for 受击回复 SP and TAKE
excepted). 【污染秽蚀】 (萨卡兹枯朽战车's 秽蚀轰击,
萨卡兹枯朽战士's death) is **true** damage, 50 / 25 per second on low / high ground (PRTS "每秒受到50/25点真实普通伤害 …
同名效果不叠加", user playtest #6): a unit covered by several zones takes one tick per second (`unit.mem.pollutedAt`), so
a crowd of dying 萨卡兹枯朽战士 totals 50 / s, not 50 × n. An activated 孽罪奇美拉's aura (`kitChimera`, PRTS "持续视为受到源石
污染区影响…受到50真实持续伤害") is the same kind of damage — 无来源 true (like the terrain it stands for [ASSUMED]), the
chimera credited — not a 流失 (player report D1 audit; its radius 1 and 1 s pulse stay [ASSUMED] against PRTS's 1.2 / 0.5 s).
a crowd of dying 萨卡兹枯朽战士 totals 50 / s, not 50 × n. An activated 孽罪奇美拉's aura (`kitChimera`, PRTS "自身半径1.2
范围内的所有单位持续视为受到源石污染区影响,技力自然回复速度倍率-80%,每0.5秒受到50真实持续伤害(同类效果取最高)") is the same
kind of damage — 无来源 true (like the terrain it stands for [ASSUMED]), the chimera credited — not a 流失 (player report
D1 audit): radius 1.2, a tick every 0.5 s, one tick per unit per 0.5 s however many chimeras reach it (`mem.chimeraAt`).
**Ownership** (`enemy.ownerId`, used for `killed/total` and leak attribution): `ownerPlayerId` if given, else the
player whose half contains the spawn tile (cols ≥ 11 = right half / player with colOffset 8 or side R). A leak is
@@ -255,6 +256,9 @@ recorded for the player whose half contains the goal it reached, with `sourcePla
- `battleEnd {result}` fires before the result is frozen: handlers may still call `addLayers`/`addCoins`.
- `layerGains` holds what `addLayers` actually added — never more than a bond's room under `BOND_LAYER_CAP` (999) from
its starting layers; `bossDamage` / `damageDealt` never include a leader hit cancelled by 限伤 (§4).
- `damageDealt` and `unitStats[].dmg` count only HP removed from the other side: a unit's own drain or 流失 (源石溶剂,
史尔特尔 S3 …) and friendly damage (the drain on 炎佑) count as the target's `taken`, never as damage dealt; the kill
credit is unchanged (`damage.js applyHpLoss`).
### 1.4 Robustness
@@ -504,10 +508,16 @@ events (PRTS 作战机制 "生命流失不会触发反伤、受击回复等受
`damaged` ctx carries the tag `'hpLoss'` (and `noSp`; `damage.js isHpLoss`), and "受到伤害时" content skips it (信仰搅拌机 S3
counters, 雷蛇 战术防御, 乌尔比安 本性的坚守, 录武官's guard, 远牙's 未受伤害 timer, 伪装服, 坚守 thorn chances, 机变 自愈) —
while it counts every real damage instance, 无来源 ones included (a zone tick, the 源石溶剂 drain; 蒂比 S2 "受到伤害前
触发"). Use it only for what the official calls 流失 (operator skill / trait 流失, 阿戈尔's 物理流失, leader-part 传递);
"受到N真实伤害" over time is damage — the official `periodic_damage` template (源石溶剂: PRTS 盟约记录 修正 "并非流失",
"造成无来源真实持续环境伤害"; 狂暴宿主 "自身每秒受到N无来源真实伤害") is `dealDamage(credit, target,
damage.js periodicDamage(n))`: 无来源 true, `canDodge: false`, tags `'dot'` / `'periodic'` (player report D1). On a leader
触发"). Use it only for what the official calls 流失 — operator skill / trait 流失 (华法琳, 史尔特尔, 瑰盐 …), 阿戈尔's 物理流失,
a leader part's 传递 and the 胄 drone link ("无来源生命流失") — and, [ASSUMED] for want of an official word, 心烛's transfer
and 扎罗's 溶血骇惧; "受到N真实伤害" over time is damage (PRTS 伤害分类: BUFF damage, "自残类型" included) — the official
`periodic_damage` template (源石溶剂: PRTS 盟约记录 修正 "并非流失", "造成无来源真实持续环境伤害"; 狂暴宿主 "自身每秒受到N无来源
真实伤害"), 码头水手's drowning ("每秒受到1000点无来源真实伤害"), 弧光锋卫's 失衡 bleed (修正 "失衡移动时持续受到真实伤害") and
孽罪奇美拉's aura are `dealDamage(credit, target, damage.js periodicDamage(n))`: 无来源 true, `canDodge: false`, tags
`'dot'` / `'periodic'` (player report D1). The 源石溶剂 drain also ticks on every 敌人类我方单位 of the field (炎佑, a
partner's too: PRTS 备注 "全场范围内的所有敌人类我方单位也会获得此装备的…效果…无视目标可选性"), credited to nobody, once per
second however many carriers (PRTS 作战机制 "同名buff的默认叠加策略buff只能表现出一个"), while a carrier is on the field
[ASSUMED]. On a leader
in a boss / hidden battle a loss of ≥ `BOSS_HIT_LIMIT` (a part's 传递, a drone's death) is cancelled like a hit. Every HP-damage kind
meets the limit (phys / arts / true / 元素伤害 incl. element bursts, DoT ticks); element 损伤 (the gauge, `type: 'element'`)
removes no HP and never does.
+1 -1
View File
@@ -1670,7 +1670,7 @@
],
"flavor": "“经典的工业材料军事化应用案例。”",
"talentNotes": [],
"implFormula": "ATK% += atk; in battle the carrier takes `damage` true damage per second — 无来源 持续 damage, not a 流失 (PRTS 盟约记录 修正 \"并非流失\", 备注 \"造成无来源真实持续环境伤害\"): shields and damage-taken modifiers apply, it counts as 受到伤害 (受击回复 SP, the 重装 skill trigger, 信仰搅拌机 S3 counters); can kill the carrier.",
"implFormula": "ATK% += atk; in battle the carrier takes `damage` true damage per second — 无来源 持续 damage, not a 流失 (PRTS 盟约记录 修正 \"并非流失\", 备注 \"造成无来源真实持续环境伤害\"): shields and damage-taken modifiers apply, it counts as 受到伤害 (受击回复 SP, the 重装 skill trigger, 信仰搅拌机 S3 counters); can kill the carrier. Every 敌人类我方单位 of the field (炎佑, a partner's too) takes the same tick while a carrier is on the field — once per second however many carriers (PRTS 备注 \"全场范围内的所有敌人类我方单位也会获得此装备的…效果\").",
"normal": {
"chessId": "chess_item_1_05_e_a",
"isGolden": false,
+1 -1
View File
@@ -155,7 +155,7 @@ Param names below are the keys of `items[].normal.params` / `items[].golden.para
params: `atk=0.15/0.25`; buff keys: `env_gbuff_new_with_verify`
- **坚守盾牌** (`chess_item_1_02_e_a`, TI, 1) - DEF% += def
params: `def=0.2/0.35`; buff keys: `env_gbuff_new_with_verify`
- **源石溶剂** (`chess_item_1_05_e_a`, TI, 1) - ATK% += atk; in battle the carrier takes `damage` true damage per second — 无来源 持续 damage, not a 流失 (PRTS 盟约记录 修正 "并非流失", 备注 "造成无来源真实持续环境伤害"): shields and damage-taken modifiers apply, it counts as 受到伤害 (受击回复 SP, the 重装 skill trigger, 信仰搅拌机 S3 counters); can kill the carrier.
- **源石溶剂** (`chess_item_1_05_e_a`, TI, 1) - ATK% += atk; in battle the carrier takes `damage` true damage per second — 无来源 持续 damage, not a 流失 (PRTS 盟约记录 修正 "并非流失", 备注 "造成无来源真实持续环境伤害"): shields and damage-taken modifiers apply, it counts as 受到伤害 (受击回复 SP, the 重装 skill trigger, 信仰搅拌机 S3 counters); can kill the carrier. Every 敌人类我方单位 of the field (炎佑, a partner's too) takes the same tick while a carrier is on the field — once per second however many carriers (PRTS 备注 "全场范围内的所有敌人类我方单位也会获得此装备的…效果").
params: `atk=0.4/0.6, damage=60.0`; buff keys: `env_gbuff_new_with_verify, env_gbuff_new_with_verify`
- **不屈弹射器** (`chess_item_2_01_e_a`, TII, 1) - respawnTime *= (1 + respawn_time) (i.e. -30%/-50%); maxHP% += max_hp (-30%).
params: `respawn_time=-0.3/-0.5, max_hp=-0.3`; buff keys: `env_gbuff_new_with_verify`
+2 -1
View File
@@ -60,6 +60,7 @@
import { gameData, num, buffsOf, passiveBuff, effectRecord, directMods } from './support/index.js';
import { isShopItem } from '../simdata.js';
import { isHpLoss } from '../damage.js';
// =====================================================================================================================
// data helpers
@@ -472,7 +473,7 @@ export function install(battle) {
if (!t || t.side !== 'ally' || (t.kind !== 'op' && t.kind !== 'token')) return;
// 自愈: every damage instance an own unit takes (not HP loss 流失, not element gauge fills, not under 禁疗)
const amt = heal.get(t.ownerId);
if (amt > 0 && ctx.amount > 0 && ctx.type !== 'element' && !(ctx.dmg && Array.isArray(ctx.dmg.tags) && ctx.dmg.tags.includes('hpLoss'))
if (amt > 0 && ctx.amount > 0 && ctx.type !== 'element' && !isHpLoss(ctx.dmg)
&& t.alive && t.deployed && t.hp > 0 && !t.s.flags.noHeal) {
battle.heal(null, t, amt, { self: true });
}
+18 -10
View File
@@ -90,8 +90,9 @@ const SHELL_FLIGHT = 3, SHELL_RADIUS = 1.2;
/** 假想敌:黑云 抓取: the blackboard radius counts ×2.5 (PRTS "2.5倍可变半径": range_radius 1.5 → 3.75), at most 3 prey,
* "短暂延迟后" the 延迟吞噬 lands (delay [ASSUMED] 0.5 s); its SP (= 全弹发射 hits) caps at the data's spData.maxSp. */
const GRAB_RADIUS_SCALE = 2.5, GRAB_MAX_PREY = 3, GRAB_DELAY = 0.5;
/** 源石污染区 aura radius of an activated 奇美拉 [ASSUMED]. */
const CHIMERA_AURA_RADIUS = 1;
/** 孽罪奇美拉 污染模式 aura (PRTS 天赋 "自身半径1.2范围内的所有单位…每0.5秒受到50真实持续伤害(同类效果取最高)"): radius,
* damage period (s). */
const CHIMERA_AURA_RADIUS = 1.2, CHIMERA_AURA_EVERY = 0.5;
/** Bombardments per 自行炮 Cannon cast ("数次") [ASSUMED]. */
const CANNON_SHOTS = 3;
/** 枯朽之种 summoned per BornBugs cast ("数个") [ASSUMED]. */
@@ -995,7 +996,9 @@ function kitDeepsea(ab, { swim = false, drown = false }) {
if (!wet && a.wet) b.removeBuff(e, 'ab:swim');
if (wet) b.removeBuff(e, 'terrain:deepsea'); // 免疫水蚀: drop devices.js' deep-water debuff
}
if (drown && wet) { const v = T(ab, 'Drown.damage') ?? 0; if (v > 0) b.loseHp(e, v * dt, { source: null }); }
// PRTS 码头水手 "水蚀状态下或处于清澈水域时,每秒受到1000点无来源真实伤害" (伤害分类: 深水区/涨潮水蚀 is BUFF damage):
// damage, not a 流失 — 脆弱 scales it (player report D1 audit)
if (drown && wet) { const v = T(ab, 'Drown.damage') ?? 0; if (v > 0) b.dealDamage(null, e, { ...periodicDamage(v * dt), tags: ['dot', 'periodic', 'drown'] }); }
a.wet = wet;
},
hitIn(c, b, e, a) { if (swim && a.wet && c.dmg.tags && c.dmg.tags.some((t) => t === 'terrain' || t === 'deepsea' || t === 'drown')) c.dmg.cancel = true; },
@@ -1022,13 +1025,16 @@ function kitChimera(ab) {
activate(b, e, a);
if (!a.on) return;
a.acc = (a.acc ?? 0) + 0.25;
const pulse = a.acc >= 1 - 1e-9;
if (pulse) a.acc -= 1;
const pulse = a.acc >= CHIMERA_AURA_EVERY - 1e-9;
if (pulse) a.acc -= CHIMERA_AURA_EVERY;
for (const u of b.alliesInRadius(e.x, e.y, CHIMERA_AURA_RADIUS)) {
auraBuff(b, u, 'ab:originium', 0.25, { spRecoveryMul: Math.max(0, 1 + spr) }, null, true);
// PRTS "持续视为受到源石污染区影响…受到50真实持续伤害": a damage instance (受击回复, the 重装 trigger), not a 流失 —
// 无来源 like the 源石污染区 terrain it stands for [ASSUMED], the chimera keeps the credit
if (pulse && dmg > 0) b.dealDamage(e, u, { ...periodicDamage(dmg), tags: ['dot', 'pollution'] });
// PRTS "持续视为受到源石污染区影响…每0.5秒受到50真实持续伤害": a damage instance (受击回复, the 重装 trigger), not a
// 流失 — 无来源 like the 源石污染区 terrain it stands for [ASSUMED], the chimera keeps the credit; "同类效果取最高": one
// tick per unit per period however many chimeras reach it (`mem.chimeraAt`; every chimera's is 50)
if (!pulse || !(dmg > 0) || b.time - (u.mem.chimeraAt ?? -Infinity) < CHIMERA_AURA_EVERY - 1e-6) continue;
u.mem.chimeraAt = b.time;
b.dealDamage(e, u, { ...periodicDamage(dmg), tags: ['dot', 'pollution'] });
}
},
}];
@@ -2473,9 +2479,11 @@ export const KITS = Object.freeze({
enemy_2085_skzjxd: (ab) => [frontGuard(T(ab, 'Weakness.damage_resistance') ?? 0, faceCrowd)], // 圆仔 · faces the bigger crowd, front damage −80 %
enemy_2085_skzjxd_2: (ab) => [frontGuard(T(ab, 'Weakness.damage_resistance') ?? 0, faceCrowd)], // 圆仔 · same
// 失衡 (pushed / pulled by operators)
enemy_1328_cbjedi: (ab) => [unbalanced((b, e, a, d) => { // 弧光锋卫 · loses HP in proportion to the distance moved while unbalanced
enemy_1328_cbjedi: (ab) => [unbalanced((b, e, a, d) => { // 弧光锋卫 · takes damage in proportion to the distance moved while unbalanced
// PRTS 修正 "失衡移动时持续受到真实伤害", "处于失衡状态时,每0.066s受到400点无来源真实持续伤害" (伤害分类: 弧光锋卫失衡状态下的自残
// 伤害 is BUFF damage): damage, not a 流失 (player report D1 audit)
const v = T(ab, 'unbalanced_bleed.damage') ?? 0, iv = T(ab, 'unbalanced_bleed.interval') ?? 1;
if (v > 0 && iv > 0) b.loseHp(e, (v * d) / (iv * UNBALANCE_SPEED), { source: null, tags: ['unbalanced'] });
if (v > 0 && iv > 0) b.dealDamage(null, e, { ...periodicDamage((v * d) / (iv * UNBALANCE_SPEED)), tags: ['dot', 'periodic', 'unbalanced'] });
})],
enemy_10112_ymgds: (ab) => [unbalanced((b, e) => { // 冒失的小弟 · stunned after being unbalanced
const st = T(ab, 'StunAfterUnbalance.stun') ?? 0;
+15 -1
View File
@@ -56,6 +56,8 @@ function partnerOf(p) {
const isProc = (dmg) => !!(dmg && dmg.tags && dmg.tags.indexOf('item') >= 0);
const procTags = (k) => ['item', `item:${k}`];
const chance = (battle, p) => p > 0 && (p >= 1 || battle.rng() < p);
/** An ally built from an enemy record — PRTS's 敌人类我方单位 (炎佑 enemy_9012_acloon). */
const ENEMY_RECORD = /^enemy_/;
/** 'fatal' priorities (see header). */
export const PRIO_REVIVE = -100;
const PRIO_FREE_UNDYING = 20;
@@ -320,10 +322,22 @@ const BY_BUFF = {
// not a 流失 — shields, damage-taken modifiers and the target-side `hit` effects apply, and it is a "受到伤害" for 受击回复
// SP, the 重装 TAKE_DAMAGE trigger and 信仰搅拌机 S3's counters (player report D1). 无来源: hooks see no source; the carrier
// keeps the credit (a carrier the drain finishes off is its own kill, as before). May kill.
// The same 备注: "携带后,全场范围内的所有敌人类我方单位也会获得此装备的“每秒受到60真实伤害”效果(该效果的付与为我方阵营索敌,
// 可对空,无视目标可选性)" — every enemy-record unit on our side of the whole field (炎佑 enemy_9012_acloon, a partner's
// too; flying, 孤立) takes the same tick, credited to nobody, while a carrier is on the field [ASSUMED: it ends with its
// carriers]; one effect per unit however many carriers (PRTS 作战机制 "同名buff的默认叠加策略buff只能表现出一个").
periodic_damage(battle, u, p, S) {
const d = num(p.damage);
if (!(d > 0)) return;
S.stat('drain', null, { interval: 1, onTick: ({ unit }) => { if (unit.deployed) battle.dealDamage(unit, unit, periodicDamage(d)); } });
S.stat('drain', null, { interval: 1, onTick: ({ unit }) => {
if (!unit.deployed) return;
battle.dealDamage(unit, unit, periodicDamage(d));
for (const a of battle.allies()) {
if (a === unit || !ENEMY_RECORD.test(a.defId ?? '') || battle.time - (a.mem.solventAt ?? -Infinity) < 1 - 1e-6) continue;
a.mem.solventAt = battle.time;
battle.dealDamage(null, a, periodicDamage(d));
}
} });
},
// 奥术法阵: 攻击使目标失去特殊能力 silence s
silence_attachment(battle, u, p, S) {
+5 -1
View File
@@ -6,6 +6,8 @@
// → 限伤 (leaders in boss / hidden battles: a hit of ceil(final) ≥ BOSS_HIT_LIMIT is cancelled, see leaderHitCancelled)
// → shields (hit-negating barriers first, then HP shields) → HP loss (boss pool routing) → 'damaged' hook
// → SP-on-hurt / TAKE_DAMAGE trigger → fatal/kill.
// Damage-dealt stats (the source's `stats.dmg`, the player's `damageDealt`) count only HP removed from the other side:
// self and friendly damage (a 源石溶剂 drain, an operator's own 流失) is the target's `taken` and keeps the kill credit.
// Phys: max(A − max(0, D×(1−defIgnorePct) − defIgnoreFlat), 5 %·A); Arts: max(A×(1 − R′/100), 5 %·A) with
// R′ = max(0, R×(1−resIgnorePct) − resIgnoreFlat); True: A; Elemental (元素伤害): max(A×(1 − 元素抗性/100), 5 %·A)
// (PRTS 游戏数据基础 DMG_e; 元素抗性 = the target's data `epDamageResistance`: 0 on every enemy in data/enemies.json).
@@ -277,7 +279,9 @@ export function applyHpLoss(battle, source, target, amount, dmg) {
}
dealt = Math.max(0, before - Math.max(0, target.hp));
}
if (source) {
// damage dealt (unit stats, the results screen's 造成伤害) never counts a unit's own side — its own drain or 流失 (源石溶剂,
// 史尔特尔 S3 …), friendly damage; `taken` and the kill credit do
if (source && source.side !== target.side) {
source.stats.dmg += dealt;
if (source.side === 'ally' && source.ownerId != null) { const pp = battle._pp(source.ownerId); if (pp) pp.damageDealt += dealt; }
}
+144
View File
@@ -13,6 +13,7 @@ import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.
import { makeMatch, give, giveItem, DATA } from '../match/harness.js';
import { buildBattleSpec, createBattleFromSpec } from '../../server/sim/spec.js';
import { getDefaultSource } from '../../server/sim/simdata.js';
import { spawnYanyou } from '../../server/sim/content/tokens.js';
const ds = getDefaultSource();
const SOLVENT = 'chess_item_1_05_e_a', SOLVENT_B = 'chess_item_1_05_e_b';
@@ -339,3 +340,146 @@ test('audit — "受到伤害时" content counts the drain and never a 流失:
approx(u.mem['bond:steadShip:cd'], h.b.time, 1e-9, '无来源 damage used it');
}
});
// ---------------------------------------------------------------------------------------------------------------
// review follow-ups: 敌人类我方单位, the enemy-side BUFF damage, self-damage stats, the chimera aura numbers
test('源石溶剂 also drains every 敌人类我方单位 on the field (炎佑): one 无来源 60 true-damage tick per second however many carriers; none once no carrier is on the field', () => {
// PRTS 盟约记录 源石溶剂 备注: "携带后,全场范围内的所有敌人类我方单位也会获得此装备的“每秒受到60真实伤害”效果(该效果的付与为
// 我方阵营索敌,可对空,无视目标可选性)"; one effect per unit (PRTS 作战机制 "同名buff的默认叠加策略buff只能表现出一个")
for (const carriers of [1, 2]) {
const units = [{ chessId: YAK, row: 10, col: 4, items: [SOLVENT], skillIndex: 1 }];
if (carriers > 1) units.push({ chessId: INSIDE, row: 11, col: 4, items: [SOLVENT_B] });
const h = makeBattle({ units, timeLimit: 60, hooks: ['damaged'], captureNoisy: true, autoFinish: false });
h.step();
const [y] = spawnYanyou(h.b, 'p1', { atk: 400, hp: 5000 });
assert.ok(y && y.deployed && y.s.flags.isolated, '炎佑 on the field (孤立: "无视目标可选性")');
const hp0 = y.hp;
h.run(3.02);
const ticks = h.hooksOf('damaged').filter((c) => c.target === y);
assert.equal(ticks.length, 3, `${carriers} carrier(s): one tick per second`);
for (const c of ticks) {
assert.equal(c.source, null, '无来源');
assert.equal(c.credit, null, 'nobody is credited with damage to an ally');
assert.equal(c.type, 'true');
assert.ok(!hpLoss(c), 'damage, not 流失');
}
approx(hp0 - y.hp, 3 * DRAIN, 1e-9);
// no carrier left on the field: no more ticks
for (const u of h.b.allyUnits.filter((a) => a.kind === 'op')) h.b.kill(u, null);
const n = ticks.length;
h.run(2.02);
assert.equal(h.hooksOf('damaged').filter((c) => c.target === y).length, n, 'the effect ends with its carriers');
assert.equal(h.b.errorCount, 0, JSON.stringify(h.b.errors));
}
// the 9-炎 炎佑 (受到的伤害 ×0.1) takes a tenth of it: a damage instance, damage-taken modifiers apply
const h = makeBattle({ units: [{ chessId: YAK, row: 10, col: 4, items: [SOLVENT], skillIndex: 1 }], timeLimit: 60, hooks: [], autoFinish: false });
h.step();
const [y] = spawnYanyou(h.b, 'p1', { atk: 400, hp: 5000, dmgTakenMul: 0.1 });
const hp0 = y.hp;
h.run(1.02);
approx(hp0 - y.hp, DRAIN * 0.1, 1e-9);
});
test('a co-op field: a carrier drains the partner\'s 炎佑 too ("全场范围内…我方单位")', () => {
const h = makeBattle({
kind: 'boss', autoFinish: false, timeLimit: 60, hooks: ['damaged'], captureNoisy: true,
players: [
{ playerId: 'p1', seat: 0, side: 'L', colOffset: 0, units: [{ uid: 1, kind: 'chess', chessId: YAK, row: 12, col: 3, items: [SOLVENT], skillIndex: 1 }], bonds: {} },
{ playerId: 'p2', seat: 1, side: 'R', colOffset: 0, units: [], bonds: {} },
],
});
h.step();
const [y] = spawnYanyou(h.b, 'p2', { atk: 400, hp: 5000 });
assert.ok(y && y.ownerId === 'p2');
h.run(2.02);
assert.equal(h.hooksOf('damaged').filter((c) => c.target === y).length, 2);
});
test('self / friendly damage is not damage dealt: the 源石溶剂 carrier\'s own drain counts as taken, never as its dmg or the player\'s damageDealt; the kill stays its own', () => {
const h = makeBattle({ units: [{ chessId: YAK, row: 10, col: 4, items: [SOLVENT], skillIndex: 1 }], timeLimit: 60, hooks: ['kill'], autoFinish: false });
h.step();
const u = h.unit(YAK);
u.skill.sp = 0;
const [y] = spawnYanyou(h.b, 'p1', { atk: 400, hp: 5000 });
h.run(3.02);
approx(u.stats.taken, 3 * DRAIN, 1e-9, 'taken');
assert.equal(u.stats.dmg, 0, 'its own drain is not damage it dealt');
approx(y.stats.taken, 3 * DRAIN, 1e-9);
// an operator's own 流失 (Battle.loseHp with itself as the source) likewise
h.b.loseHp(u, 100, { source: u });
assert.equal(u.stats.dmg, 0);
h.b.forceEnd?.('test');
const r = h.b.result();
assert.equal(r.perPlayer.p1.damageDealt, 0, 'the results screen 造成伤害');
const st = r.perPlayer.p1.unitStats.find((s) => s.defId === YAK);
assert.equal(st.dmg, 0);
// the carrier finished off by its own drain is still its own kill (kill credit untouched)
const g = makeBattle({ units: [{ chessId: YAK, row: 10, col: 4, items: [SOLVENT], skillIndex: 1 }], timeLimit: 60, hooks: ['kill'], autoFinish: false });
g.step();
const v = g.unit(YAK);
v.skill.sp = 0;
v.hp = 10;
g.run(1.02);
assert.ok(!v.alive);
assert.equal(g.hooksOf('kill').find((c) => c.victim === v)?.killer, v);
});
test('enemy-side BUFF damage (PRTS 伤害分类 "深水区/涨潮水蚀", "弧光锋卫失衡状态下的自残伤害") is damage, not 流失: 码头水手 drowning, 弧光锋卫 失衡 bleed', () => {
// 码头水手 天赋 (PRTS): "水蚀状态下或处于清澈水域时,每秒受到1000点无来源真实伤害"
for (const key of ['enemy_1160_hvyslr', 'enemy_1160_hvyslr_2']) {
const h = makeBattle({ units: [], flat: { rows: { 11: '##hddddrrrfrrrrrrrf##' } }, timeLimit: 60, hooks: ['damaged'], captureNoisy: true, autoFinish: false });
h.step();
const e = h.spawn(key, { pos: [11, 4], routeIndex: 0, mods: { speedMul: 0 } });
// (the deep-water tiles' own terrain tick — devices.js, already damage — is left out: tag 'drown' only)
const drown = () => h.hooksOf('damaged').filter((c) => c.target === e && (c.dmg.tags || []).includes('drown'));
h.run(1);
assert.ok(drown().length >= 1);
for (const c of drown()) { assert.equal(c.source, null); assert.equal(c.type, 'true'); assert.ok(!hpLoss(c), `${key}: drowning is damage`); }
const per = DATA.enemies[key].talents.bb['Drown.damage'];
const sum = (l) => l.reduce((s, c) => s + c.amount, 0);
approx(sum(drown()), per, 0.02, 'Drown.damage per second');
h.b.applyStatus(e, 'fragile', { duration: 30, value: 0.5 });
const n = drown().length;
h.run(1);
approx(sum(drown().slice(n)), per * 1.5, 0.02, '脆弱 scales it');
}
// 弧光锋卫 天赋 (PRTS 修正 "失衡移动时持续受到真实伤害"; "处于失衡状态时,每0.066s受到400点无来源真实持续伤害")
const h = makeBattle({ units: [], timeLimit: 60, hooks: ['damaged'], captureNoisy: true, autoFinish: false });
h.step();
const j = h.spawn('enemy_1328_cbjedi', { pos: [10, 7], routeIndex: 0, mods: { speedMul: 0 } });
h.step(2);
const hp0 = j.hp;
h.b.displace(j, { x: 1, y: 0 }, 1, { force: 3 });
h.step();
const bleed = h.hooksOf('damaged').filter((c) => c.target === j);
assert.ok(bleed.length >= 1 && hp0 > j.hp, 'bled');
for (const c of bleed) { assert.equal(c.source, null); assert.equal(c.type, 'true'); assert.ok(!hpLoss(c), '失衡 bleed is damage'); }
});
test('孽罪奇美拉 污染模式 (PRTS "自身半径1.2范围内的所有单位…每0.5秒受到50真实持续伤害(同类效果取最高)"): radius 1.2, a tick every 0.5 s, never stacked by two chimeras', () => {
const run = (chimeras, dx = 0) => {
const h = makeBattle({
stageId: 'act1autochess_m04', units: [{ chessId: INSIDE, row: 10, col: 5 }],
timeLimit: 60, hooks: ['damaged'], captureNoisy: true, autoFinish: false,
});
h.step();
const u = h.unit(INSIDE);
const es = chimeras.map((pos) => { const e = h.spawn('enemy_1425_lrcmra', { pos }); h.b.applyStatus(e, 'stun', { duration: 60 }); e.x += dx; return e; });
h.run(0.3); // both activated on their infection tile
assert.ok(es.every((e) => e.mem.ab.atkType === 'arts'), 'activated (污染模式)');
const t0 = h.b.time;
h.run(2);
return h.hooksOf('damaged').filter((c) => c.target === u && es.includes(c.credit) && c.t > t0 + 1e-9);
};
const aura = DATA.enemies.enemy_1425_lrcmra.talents.bb['OrigAura.damage'];
// one chimera beside her: 4 ticks in 2 s, 0.5 s apart
const one = run([[10, 6]]);
assert.equal(one.length, 4, `ticks every 0.5 s (${one.map((c) => c.t.toFixed(2))})`);
for (let i = 1; i < one.length; i++) approx(one[i].t - one[i - 1].t, 0.5, 1e-6);
for (const c of one) approx(c.amount, aura, 1e-9);
// two chimeras in reach (both on the infection tile beside her): still one tick per 0.5 s (同类效果取最高)
assert.equal(run([[10, 6], [10, 6]]).length, 4, 'not stacked');
// 1.15 tiles away (inside 1.2, outside the old radius 1)
assert.equal(run([[10, 6]], 0.15).length, 4, 'radius 1.2');
});