update - #92
Conversation
|
Too many files changed for review (270 files, 100 file limit). Bypass the limit by tagging |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough本次变更更新构筑描述模板、机器热量效果、玩家攻击流程、投掷武器、战斗效果、敌人掉落、关卡配置和 Godot 视觉资源喵。 Changes构筑与机器系统
战斗与资源
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4dfca9137a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (node is Node2D node2D) | ||
| { | ||
| // 独立加入世界层:不挂在本场景下,随本场景销毁而销毁 | ||
| world.AddChild(node2D); |
There was a problem hiding this comment.
Apply overrides before adding spawned nodes
When BriefcaseAttack spawns CubeData, AddChild runs the cube's _Ready() before the EffectOverrides loop sets TargetableFactions = Enemy. RotatingCube._Ready() builds the attack area's collision mask from the old scene value (Player), so these spawned attack cubes aim at enemies later but never subscribe to the enemy physics layer and won't receive enemy hit signals. Apply the overrides before adding the node to the tree, or reapply the cube's mask after overriding.
Useful? React with 👍 / 👎.
| _core.MaxHeat = _originalMaxHeat * CurrentCap; | ||
| _core.DecayRate = _originalDecayRate * CurrentDecay; | ||
| _core.HeatDrainRate = _originalHeatDrainRate * CurrentDecay; | ||
| _core.SetStatModifier(MachineCoreEffect.HeatStat.MoveHeatRate, EffectId, CurrentGain); |
There was a problem hiding this comment.
With both 战斗升温 and 高速导热 selected, attack-hit heat still uses the unmodified AttackHeatGain in MachineCoreEffect.OnDamageDealt, because this tier only registers a modifier for movement heat. The resource text and the previous implementation both apply 高速导热 to all heat accumulation, so hit-based heat gain now stays at 3/4/5 instead of receiving the +75%/+100% gain.
Useful? React with 👍 / 👎.
| { | ||
| if (_hasDamaged) return; | ||
| if (Damage <= 0 && KnockbackSpeed <= 0f && KnockbackDistance <= 0f) return; | ||
| if (_cachedPlayer == null) return; |
There was a problem hiding this comment.
Resolve non-player targets for enemy beams
The updated lightning scene is configured as TargetableFactions = Enemy with Damage = 20, but both beam damage paths still only test _cachedPlayer. In ECoreAttackEffect's child beams this means the target checked is always the player, and DamageDispatcher rejects it for an enemy-only beam, so the configured lightning damage/knockback never applies to enemies. The beam target lookup needs to follow TargetableFactions like RotatingCube does, or this scene should be marked visual-only.
Useful? React with 👍 / 👎.
| } | ||
| _wasMovingLastFrame = moving; | ||
|
|
||
| ApplyOverflowBuff(); |
There was a problem hiding this comment.
Recompute overflow stats during release
When overclock heat is being drained by the core release buff, DisableHeatGainDuringBuff is true by default, so OnTick returns before this new overflow stat update runs. If heat falls back below MaxHeat while the release buff is still active, the previously boosted speed, attack speed, and incoming damage multiplier stay stuck until the buff fully ends instead of tracking the current overflow amount. Move the overflow recomputation before the heat-gain early return or avoid returning before stat cleanup.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/fx/RotatingCube.cs (1)
351-374: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
target与area.Owner的解析不一致,Owner为 null 时击退会丢失喵。第 353 行用
area.Owner ?? area得到target,第 358 行把target传给DealDamage。但第 356 行的alreadyInvincible和第 373 行的ApplyKnockback都直接用area.Owner as GameActor喵。如果
HitArea所属节点是运行时AddChild添加的,Owner会是 null 喵。这时target退化成area本身,DealDamage可能仍然成功(内部会向上解析GameActor),但第 373 行的area.Owner as GameActor为 null,击退直接被跳过喵。同一次命中里,伤害生效而击退不生效,行为不一致喵。本 PR 的
scripts/effects/ECoreAttackEffect.cs第 202-204 行对同样的场景做了多级回退解析。建议这里也统一解析成一个GameActor变量,全流程复用它喵~🐛 建议的修复
// 仅接受目标的 HitArea,避免敌人的攻击判定区等误触发 if ((string)area.Name != "HitArea") return; - var target = area.Owner ?? area; + var hitActorResolved = area.Owner as GameActor + ?? area.GetParent() as GameActor + ?? area.GetParent()?.GetParent() as GameActor; + Node target = hitActorResolved ?? (Node)area; if (!AllowSelfDamage && DamageDispatcher.BelongsToActor(target, _attacker)) return; - bool alreadyInvincible = area.Owner is Actors.Heroes.MainCharacter mc && mc.IsHitInvincible; + bool alreadyInvincible = hitActorResolved is Actors.Heroes.MainCharacter mc && mc.IsHitInvincible; bool dealt = DamageDispatcher.DealDamage(target, Damage, GlobalPosition, _attacker, DamageSource.DirectAttack, TargetableFactions, AllowSelfDamage, null); @@ if (!dealt) return; - if (!alreadyInvincible && area.Owner is GameActor hitActor) - ApplyKnockback(hitActor); + if (!alreadyInvincible && hitActorResolved != null) + ApplyKnockback(hitActorResolved);对应地,第 360-366 行的日志判断也需要改用
hitActorResolved。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/fx/RotatingCube.cs` around lines 351 - 374, 统一命中目标解析逻辑:在当前 AreaEntered 处理流程中基于 area.Owner ?? area 解析并保存可用的 GameActor(参考 ECoreAttackEffect 的多级回退),然后让 alreadyInvincible、DealDamage 后的拒绝日志以及 ApplyKnockback 全部复用该变量,避免 Owner 为 null 时伤害成功但击退被跳过。
🧹 Nitpick comments (10)
scripts/ui/BuildSelectionWindow.cs (1)
175-193: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win建议加固数值解析与格式化喵。
这里有两个小隐患喵:
- Line 180 用
int.Parse。模板里写了超长数字(例如{99999999999})时会抛OverflowException,整个描述渲染都会炸掉喵。用int.TryParse失败就保留原文,和现有"无法解析保留原文"的策略一致喵~- Line 189 的
ToString()依赖当前区域性。区域性使用逗号小数分隔符时,12.5会显示成12,5,和卡面其他数字不一致喵。建议显式指定CultureInfo.InvariantCulture。♻️ 建议改动
- int index = int.Parse(match.Groups[2].Value); + if (!int.TryParse(match.Groups[2].Value, out int index)) + return match.Value; // 数字非法 → 保留原文 var values = effect.GetOverrideFloatArray(arrayName); if (values == null || index < 0 || index >= values.Length) return match.Value; // 无数据 → 保留原文 int tierIndex = Mathf.Clamp(stacks, 0, values.Length - 1); // 修改器百分比为负(减容/缓速类)时,描述按数值大小显示(降低 10%,而非 -10%) - string valueText = Mathf.Abs(values[index]).ToString(); + string valueText = Mathf.Abs(values[index]) + .ToString(System.Globalization.CultureInfo.InvariantCulture);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/ui/BuildSelectionWindow.cs` around lines 175 - 193, Harden the TierTokenRegex replacement by changing the index parsing in the template substitution callback to use int.TryParse, returning match.Value when parsing fails or overflows. Update the values[index] formatting to use CultureInfo.InvariantCulture explicitly, preserving the existing absolute-value and color-selection behavior.scripts/builds/machine/MachineDeathEmberEffect.cs (1)
62-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
FreezeHeatGain是共享布尔量,直接置false有风险喵。
ClearEmber无条件把_core.FreezeHeatGain设为false(Line 82)喵。MachineCoreEffect的注释说这个标志"由外部效果设置",也就是设计上允许多个效果使用喵。以后再有第二个效果冻结热量获取时,余温结束就会顺手把别人的冻结也解掉喵。现在只有这一个使用者,所以不阻塞合并喵。要更稳的话,可以在核心里改成计数式的
PushHeatFreeze()/PopHeatFreeze(),和PauseManager的做法一致喵~🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/builds/machine/MachineDeathEmberEffect.cs` around lines 62 - 84, 将 MachineCoreEffect 的共享 FreezeHeatGain 布尔状态改为类似 PauseManager 的计数式冻结接口,新增 PushHeatFreeze() 与 PopHeatFreeze() 并由计数决定实际状态;更新 MachineDeathEmberEffect 的 TriggerEmber 和 ClearEmber,分别调用对应接口,确保清理余温时只解除自身冻结而不影响其他效果。构筑卡牌描述模板机制.md (1)
7-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value代码块补一个语言标识喵。
markdownlint 报了 MD040 喵。这两个围栏代码块没有语言标识,加上
text就好了喵~♻️ 建议改动
-``` +```text 模板: {数组名:下标} 例: {GainValues:0} 简写: {0} ≡ {TierValues:0}</details> Also applies to: 13-21 <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@构筑卡牌描述模板机制.mdaround lines 7 - 9, 为文档中的围栏代码块补充 text
语言标识,覆盖模板示例及同文档中另一个对应的代码块(当前标注范围 13-21)。保持代码块内容不变,仅将开围栏统一改为 ```text 以满足 MD040。</details> <!-- cr-comment:v1:8451862c02fa03da0b7def63 --> _Source: Linters/SAST tools_ </blockquote></details> <details> <summary>scripts/effects/ECoreAttackEffect.cs (2)</summary><blockquote> `141-171`: _🩺 Stability & Availability_ | _🔵 Trivial_ | _⚡ Quick win_ **遍历 `_actorTimers` 期间调用 `DealDamageToActor` 有枚举失效风险喵。** 第 158 行在 `foreach` 内部调用 `DealDamageToActor`,它会走到 `DamageDispatcher.DealDamage`。如果伤害结算同步触发了目标死亡、节点移除或碰撞状态变化,进而同步回调到 `AddActorRef`,就会向 `_actorTimers` **新增键**喵。 .NET 允许在枚举期间更新已有键的值和删除键,但**新增键**会让枚举器失效并抛 `InvalidOperationException` 喵。 建议先快照键列表再遍历,把这个风险彻底排除喵~ <details> <summary>♻️ 建议的重构</summary> ```diff private void TickDamage(float dt) { if (_actorTimers.Count == 0) return; var dead = new List<GameActor>(); - foreach (var (actor, timer) in _actorTimers) + var snapshot = new List<GameActor>(_actorTimers.Keys); + foreach (var actor in snapshot) { + if (!_actorTimers.TryGetValue(actor, out float timer)) continue; + if (!GodotObject.IsInstanceValid(actor) || actor.IsDead) { dead.Add(actor); continue; } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/effects/ECoreAttackEffect.cs` around lines 141 - 171, Update TickDamage to iterate over a snapshot of _actorTimers keys or entries instead of enumerating the dictionary directly, while preserving the existing timer updates, damage calls, and dead-actor cleanup. Ensure actors added to _actorTimers during DealDamageToActor do not invalidate the active enumeration and are handled on a subsequent tick. ``` </details> <!-- cr-comment:v1:a5cbd151fe6ab20346578bcd --> --- `302-332`: _🎯 Functional Correctness_ | _🔵 Trivial_ | _⚡ Quick win_ **`_lastHitNormal` 是残留状态,且从未被使用喵。** 第 302 行的 `_lastHitNormal` 是成员字段,每次调用都不重置。第 329-331 行的返回值依赖它:如果本次射线命中的结果里没有 `normal` 键,函数会拿**上一次**命中的法线来判定,返回一个陈旧的 `true` 喵。 而且 `BounceXAxisOnly` 只翻转 X 方向,根本不读法线喵。这个字段既是死状态又会污染判定结果喵。 建议直接返回命中结果,并删除该字段喵~ <details> <summary>♻️ 建议的重构</summary> ```diff - private Vector2 _lastHitNormal = Vector2.Zero; - /// <summary> /// 沿移动方向发射短射线检测前方非角色物理体(墙/障碍)。 - /// 命中则记录法线并返回 true。 + /// 命中非角色物理体则返回 true。 /// </summary> private bool ProbeCollision(Vector2 from, float horizontalVelocity) { @@ var body = collider.As<GodotObject>(); if (body is GameActor) return false; - - if (result.TryGetValue("normal", out var normal)) - _lastHitNormal = normal.AsVector2(); - return _lastHitNormal != Vector2.Zero; + return true; } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/effects/ECoreAttackEffect.cs` around lines 302 - 332, Remove the unused _lastHitNormal field and update ProbeCollision to return true immediately after confirming a non-GameActor collider was hit, without reading or storing the ray result’s normal. Preserve the existing false returns for zero velocity, no hit, and GameActor collisions. ``` </details> <!-- cr-comment:v1:22bd14a9fafdba03dc80d3d0 --> </blockquote></details> <details> <summary>scripts/effects/DotBurnEffect.cs (1)</summary><blockquote> `97-105`: _🚀 Performance & Scalability_ | _🔵 Trivial_ | _⚡ Quick win_ **建议缓存 HitArea 查找结果喵~** `GetHitCenterWorld` 在每次 `OnTick` 都会调用。当 `GetNodeOrNull<Area2D>("HitArea")` 失败时,会走 `FindChild(recursive: true)` 递归遍历目标的整个子树喵。灼烧默认持续 3 秒,每帧都做一次递归搜索,对多目标同时灼烧的场景会累积开销喵。 建议在 `OnApply` 时解析一次并缓存 `CollisionShape2D` 引用,`OnTick` 只读缓存喵。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/effects/DotBurnEffect.cs` around lines 97 - 105, Update DotBurnEffect so OnApply resolves and caches the target’s CollisionShape2D (including the existing HitArea lookup fallback), then have GetHitCenterWorld or the OnTick path use that cached reference instead of searching the target hierarchy each tick; preserve the current fallback positions when the cached shape or HitArea is unavailable. ``` </details> <!-- cr-comment:v1:29e459159f082d148f3d33e2 --> </blockquote></details> <details> <summary>scripts/effects/BriefcaseOpenEffect.cs (1)</summary><blockquote> `110-113`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_ **注释语义写反了喵。** 第 111 行注释写「不挂在本场景下,随本场景销毁而销毁」,但代码把节点加到 `world` 下,正是为了让它**不**随本场景销毁。类头文档也写的是「生成的特效独立存在(不随本场景销毁)」。两处矛盾会误导后续维护者喵。 <details> <summary>📝 建议的注释修正</summary> ```diff - // 独立加入世界层:不挂在本场景下,随本场景销毁而销毁 + // 独立加入世界层:不挂在本场景下,因此不随本场景销毁 world.AddChild(node2D); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/effects/BriefcaseOpenEffect.cs` around lines 110 - 113, 修正 node2D 加入 world 的上一行注释语义,明确该特效独立挂载于世界层、不会随当前场景销毁,并与类头文档保持一致;仅更新注释,不改动 world.AddChild 或位置设置逻辑。 ``` </details> <!-- cr-comment:v1:6e7d30bb98c1c583496cc9cf --> </blockquote></details> <details> <summary>scripts/fx/LightningBeam.cs (2)</summary><blockquote> `196-207`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _⚡ Quick win_ **建议用 `RandomArcMode` 而不是 `_arcs.Count` 来选择分支喵。** 第 196 行用 `_arcs.Count > 0` 区分电弧模式与光束模式。如果随机电弧模式下某一帧 `_arcs` 恰好为空,代码会静默落到 `else` 分支,去调用 `UpdateBeam()` 和 `TryDamagePlayer()` 喵。 此时 `_ray` 为 null,`UpdateBeam` 在第 363 行直接 return,`_currentLength` 保持 0,`TryDamagePlayer` 的第 389 行判定必然失败喵。结果就是这一帧既没有视觉也没有伤害,而且不会有任何报错提示喵。 用模式标志判断可以让意图明确,也避免这种静默降级喵~ <details> <summary>♻️ 建议的重构</summary> ```diff - if (_arcs.Count > 0) + if (RandomArcMode) { UpdateArcs(); if (!_hasDamaged && _pulseTotal - _timer >= GrowDuration) TryDamageArcs(); } ``` ```diff _pulseTotal = GrowDuration + _pulseLifetime + FadeDuration; - if (_arcs.Count > 0) + if (RandomArcMode) { foreach (var arc in _arcs) arc.Sprite.QueueFree(); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/fx/LightningBeam.cs` around lines 196 - 207, Update the branch selection in the beam update flow to use the RandomArcMode state instead of _arcs.Count > 0. Keep UpdateArcs and TryDamageArcs in the arc-mode branch, and UpdateBeam and TryDamagePlayer in the beam-mode branch, so an empty arc collection cannot silently switch modes. ``` </details> <!-- cr-comment:v1:f94b1e6df1a47e2bbf07f2f0 --> --- `214-226`: _🚀 Performance & Scalability_ | _🔵 Trivial_ | _🏗️ Heavy lift_ **每个脉冲都重建全部 Sprite 与 ShaderMaterial,开销偏高喵。** `StartPulse` 每次都把全部电弧 `QueueFree` 再重新 `SpawnRandomArcs`。`SpawnRandomArcs` 里每条电弧都会新建一个 `Sprite2D`、执行一次 `ShaderMaterial.Duplicate()`,还要发一条物理射线喵。 `ArcCount` 上限是 64,`MinLifetime` 下限是 0.05 秒。极端配置下每秒会产生数百次节点创建、材质复制和射线查询喵。`ECoreAttackEffect.tscn` 里同时挂了 10 个 `LightningBeam` 实例,开销会叠加喵。 建议复用已有的 `Sprite2D` 与材质,脉冲之间只更新 `Direction`、`TargetLength` 和 shader 的 `seed` 喵~ <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/fx/LightningBeam.cs` around lines 214 - 226, 优化 StartPulse,避免每个脉冲通过 QueueFree 和 SpawnRandomArcs 重建全部电弧节点与材质;复用已有 Arc/Sprite2D 及其 ShaderMaterial。根据 ArcCount 增减或初始化可复用电弧,仅更新每条电弧的 Direction、TargetLength 和 shader seed,并避免不必要的重复物理射线查询。 ``` </details> <!-- cr-comment:v1:5a92b82807daf6d205e42976 --> </blockquote></details> <details> <summary>scripts/fx/RotatingCube.cs (1)</summary><blockquote> `187-189`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _⚡ Quick win_ **建议移除或收敛 `[Cube Debug]` 调试日志喵。** 新增的三处 `GD.Print` 都带 `[Cube Debug]` 前缀,是调试残留喵。 第 322 行的问题最明显:它在**每一次** `BodyEntered` 都执行喵。弹幕在飞行途中会碰到地面、墙体、其他投掷物,触发频率很高喵。而且插值参数里还额外调用了一次 `DamageDispatcher.ResolveDamageReceiver`,这次调用**仅仅是为了打日志**喵。第 189 行也会为每个生成的 cube 打印一次喵。 建议用一个 `[Export] bool DebugLog` 开关包起来,或者直接删掉喵~ Also applies to: 322-323, 360-370 <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/fx/RotatingCube.cs` around lines 187 - 189, 移除或收敛 RotatingCube 中所有带 “[Cube Debug]” 前缀的 GD.Print 调试日志,涵盖 ResolveAimTarget 附近的生成日志、BodyEntered 处理以及插值参数相关日志;不要为日志额外调用 DamageDispatcher.ResolveDamageReceiver。若保留日志,新增并使用一个 [Export] bool DebugLog 开关,仅在启用时输出。 ``` </details> <!-- cr-comment:v1:f1eb8ad63816ab7c6a78a199 --> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.Inline comments:
In@resources/builds/BuildMachine_A_010.tres:
- Around line 12-15: Update the overclock description associated with
AttackSpeedPercentPerHeat, DamageTakenPercentPerHeat, and SpeedPercentPerHeat so
it accurately states the configured movement-speed, attack-speed, and
damage-taken percentages; alternatively, set all three configuration values to
the intended 2.0 value while preserving the existing overclock behavior.In
@resources/builds/BuildMachine_B_006.tres:
- Around line 3-4: Configure EffectEntries for both BuildMachine_B_006.tres and
BuildMachine_B_008.tres by adding AttackEffectEntry subresources referencing
their intended effect scenes, then assign those subresources to each resource’s
EffectEntries array so BuildSelectionManager.ApplyEffectBonuses can instantiate
and apply the effects.In
@resources/items/Weapon_Throw_ECore.tres:
- Around line 17-18: 将 Weapon_Throw_ECore.tres 中的 Notes
字符串改为单行文本,或将其中的原始换行替换为转义序列 \n,确保 Notes 字符串正确闭合并可被 Godot 资源解析器加载。In
@scenes/builds/machine/MachineHeatFlowImpactEffect.tscn:
- Line 10: 将 MachineHeatFlowImpactEffect 场景中的 ShowDebugRadius 设置为 false,确保
MachineHeatFlowImpactEffect.CreateContactArea() 不创建
ContactRadiusDrawer,也不会在运行时绘制调试圆。- Around line 8-9: 使 MachineHeatFlowImpactEffect 的 TargetCollisionMask 与
TargetableFactions = 6 保持一致:若该效果应命中 WorldItem,将掩码扩展为包含 Godot layer 4,同时保留 Enemy
所需的层;若设计上仅命中 Enemy,则将 TargetableFactions 调整为仅对应 Enemy。In
@scenes/managers/BuildSelectionManager.tscn:
- Around line 43-47: Restore the RarityMultiplier values in the scene resource
to the intended weights: keep Common at 3.0, set Rare to 1.0, and set Epic to
0.3; do not add a Core entry, so its existing code default of 1.0 remains
effective.In
@scripts/actors/heroes/attacks/PlayerAttackTemplate.cs:
- Around line 276-285: 在初始化时序覆盖的代码中,先通过 GetPrimarySkillDefinition() 更新
_activeWeaponSkill,再计算 _pendingSkipWarmupStart。确保跳过 Warmup 的偏移使用当前技能的
WarmupDuration,并保留后续 _effectiveWarmup、_effectiveActive 和 _effectiveRecovery
的新技能时序计算。In
@scripts/builds/buildcore/MachineCoreEffect.cs:
- Around line 206-234: 修改 ApplyOverflowBuff 及其相关状态管理,仅在本效果实际处于超频并已接管属性时写入
Speed、AttackSpeedMultiplier 和
IncomingDamageMultiplier;用“上一帧是否应用超频”的状态记录进入与退出超频,仅在退出时还原一次,避免覆盖其他效果的修改。修正
DisableHeatGainDuringBuff && _buffActive 的提前返回,使本帧仍能处理超频状态转移,并在 OnRemoved 中仅当
_overflowApplied 为 true 时执行还原。In
@scripts/builds/machine/MachineBurstThrustEffect.cs:
- Around line 26-27: Handle empty exported tier arrays across the machine
effects: in scripts/builds/machine/MachineBurstThrustEffect.cs lines 26-27,
update CurrentKnockback and CurrentDamageBonus; in
scripts/builds/machine/MachineDeathEmberEffect.cs line 24, update
CurrentHoldDuration; in scripts/builds/machine/MachineDecayDelayEffect.cs line
20, update CurrentPercent; and in
scripts/builds/machine/MachineHeatFlowImpactEffect.cs line 39, update
CurrentRadius. Return a safe zero fallback when each array is empty, otherwise
clamp _tier to a valid array index; ensure the heat-flow effect skips area
creation when its radius fallback is zero, and prevent OnStackRefreshed from
producing a negative _tier.- Around line 66-85: Update OnDamageResolved to return immediately when target
is null before setting _armed or accessing target.TakeDamage,
target.GlobalPosition, or target.ActiveImmunities. Preserve the
one-trigger-per-buff behavior for valid GameActor targets while allowing later
attacks after non-GameActor hits.In
@scripts/builds/machine/MachineHeatFlowImpactEffect.cs:
- Around line 132-162: Update TickDamage to avoid mutating _actorTimers while
enumerating it: first snapshot or otherwise iterate the existing keys, use
TryGetValue before processing each actor, and defer removals through the
existing dead list. Ensure DealDamageToActor can trigger OnBodyExited,
OnAreaExited, or AddActorRef without modifying the collection being traversed.In
@scripts/effects/DotBurnEffect.cs:
- Around line 73-76: 在 DotBurnEffect 的 tick 伤害逻辑中,调用 Actor.TakeDamage 前统一验证
Actor 不为 null 且 IsInstanceValid(Actor) 为 true;目标已移除或失效时直接跳过该次伤害,避免 _Process
抛出异常。In
@scripts/effects/ECoreAttackEffect.cs:
- Around line 247-259: 为 ECoreAttackEffect 增加公开的 Attacker 属性,并在 ResolveAttacker
中优先保留已传入的攻击者,只有未设置时才执行父节点兄弟节点扫描;同时在 BriefcaseOpenEffect.SpawnEffect 创建
ECoreAttackEffect 时赋值 Attacker,确保投掷生成效果能正确排除投掷者并传递伤害来源。In
@scripts/fx/LightningBeam.cs:
- Around line 74-120: 延迟首次随机电弧生成,避免在 LightningBeam._Ready 中直接调用 SpawnRandomArcs
时读取尚未完成的 GlobalPosition 和 Rotation。参考 ECoreAttackEffect 的首帧初始化方式,在 _Ready
中仅完成节点与参数准备,并通过首个 _Process 帧触发 SpawnRandomArcs;确保 RandomArcMode=false
的现有初始化流程不受影响,并移除或保护重复生成。- Around line 240-264: 将 LightningBeam 的墙体探测碰撞掩码改为显式导出的配置属性,供 ProbeWallDistance
直接使用,移除对随机电弧模式下始终为空的 _ray.CollisionMask 及硬编码 4 的依赖;同时保留普通模式下与 RayCast2D
配置一致的现有行为,必要时在初始化阶段复用该节点的掩码作为默认值。In
@scripts/fx/RotatingCube.cs:
- Around line 326-333: 将 scripts/fx/RotatingCube.cs#L326-L333 中的 AirWall 名称比较改为对
air_wall 节点组调用 IsInGroup("air_wall"),并移除冗余的 body is Node node 检查;同时将
scripts/items/world/RigidBodyWorldItemEntity.cs#L1259-L1263
的相同名称判断改为同一组判定,并更新对应关卡场景将空气墙节点加入 air_wall 组。In
@scripts/items/world/RigidBodyWorldItemEntity.cs:
- Around line 718-736: Update the wall-hit branch around CheckWallHit to reset
_isDropping and call SetShadowVisible(true), matching the normal landing branch
before starting the hide timer. Also revise the nearby comment to describe the
actual AirWall-only detection and stop-then-hide behavior rather than claiming
all non-GameActor bodies are immediately destroyed.In
@scripts/ui/BuildCard.cs:
- Line 18: Update the theme font-size override for DescLabel to use the
RichTextLabel-specific theme key instead of Label’s “font_size” key, while
preserving the existing size value and override flow.In
@scripts/ui/CoreHUD.cs:
- Around line 59-65: 在 CoreHUD 中更新 factor 的计算,使 HeatFillBar 的最终缩放不超过已声明的 1.5
倍上限;对溢出比例或计算结果应用上限限制,并保留 MaxHeat 为零时的现有安全行为。
Outside diff comments:
In@scripts/fx/RotatingCube.cs:
- Around line 351-374: 统一命中目标解析逻辑:在当前 AreaEntered 处理流程中基于 area.Owner ?? area
解析并保存可用的 GameActor(参考 ECoreAttackEffect 的多级回退),然后让 alreadyInvincible、DealDamage
后的拒绝日志以及 ApplyKnockback 全部复用该变量,避免 Owner 为 null 时伤害成功但击退被跳过。
Nitpick comments:
In@scripts/builds/machine/MachineDeathEmberEffect.cs:
- Around line 62-84: 将 MachineCoreEffect 的共享 FreezeHeatGain 布尔状态改为类似
PauseManager 的计数式冻结接口,新增 PushHeatFreeze() 与 PopHeatFreeze() 并由计数决定实际状态;更新
MachineDeathEmberEffect 的 TriggerEmber 和
ClearEmber,分别调用对应接口,确保清理余温时只解除自身冻结而不影响其他效果。In
@scripts/effects/BriefcaseOpenEffect.cs:
- Around line 110-113: 修正 node2D 加入 world
的上一行注释语义,明确该特效独立挂载于世界层、不会随当前场景销毁,并与类头文档保持一致;仅更新注释,不改动 world.AddChild 或位置设置逻辑。In
@scripts/effects/DotBurnEffect.cs:
- Around line 97-105: Update DotBurnEffect so OnApply resolves and caches the
target’s CollisionShape2D (including the existing HitArea lookup fallback), then
have GetHitCenterWorld or the OnTick path use that cached reference instead of
searching the target hierarchy each tick; preserve the current fallback
positions when the cached shape or HitArea is unavailable.In
@scripts/effects/ECoreAttackEffect.cs:
- Around line 141-171: Update TickDamage to iterate over a snapshot of
_actorTimers keys or entries instead of enumerating the dictionary directly,
while preserving the existing timer updates, damage calls, and dead-actor
cleanup. Ensure actors added to _actorTimers during DealDamageToActor do not
invalidate the active enumeration and are handled on a subsequent tick.- Around line 302-332: Remove the unused _lastHitNormal field and update
ProbeCollision to return true immediately after confirming a non-GameActor
collider was hit, without reading or storing the ray result’s normal. Preserve
the existing false returns for zero velocity, no hit, and GameActor collisions.In
@scripts/fx/LightningBeam.cs:
- Around line 196-207: Update the branch selection in the beam update flow to
use the RandomArcMode state instead of _arcs.Count > 0. Keep UpdateArcs and
TryDamageArcs in the arc-mode branch, and UpdateBeam and TryDamagePlayer in the
beam-mode branch, so an empty arc collection cannot silently switch modes.- Around line 214-226: 优化 StartPulse,避免每个脉冲通过 QueueFree 和 SpawnRandomArcs
重建全部电弧节点与材质;复用已有 Arc/Sprite2D 及其 ShaderMaterial。根据 ArcCount 增减或初始化可复用电弧,仅更新每条电弧的
Direction、TargetLength 和 shader seed,并避免不必要的重复物理射线查询。In
@scripts/fx/RotatingCube.cs:
- Around line 187-189: 移除或收敛 RotatingCube 中所有带 “[Cube Debug]” 前缀的 GD.Print
调试日志,涵盖 ResolveAimTarget 附近的生成日志、BodyEntered 处理以及插值参数相关日志;不要为日志额外调用
DamageDispatcher.ResolveDamageReceiver。若保留日志,新增并使用一个 [Export] bool DebugLog
开关,仅在启用时输出。In
@scripts/ui/BuildSelectionWindow.cs:
- Around line 175-193: Harden the TierTokenRegex replacement by changing the
index parsing in the template substitution callback to use int.TryParse,
returning match.Value when parsing fails or overflows. Update the values[index]
formatting to use CultureInfo.InvariantCulture explicitly, preserving the
existing absolute-value and color-selection behavior.In
@构筑卡牌描述模板机制.md:
- Around line 7-9: 为文档中的围栏代码块补充 text 语言标识,覆盖模板示例及同文档中另一个对应的代码块(当前标注范围
13-21)。保持代码块内容不变,仅将开围栏统一改为 ```text 以满足 MD040。</details> <details> <summary>🪄 Autofix</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Organization UI **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `9779e4e1-e6df-406b-8983-ac57e7d52311` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between b45bb0638cd39fb73de64ced7e03def867e85cef and 4dfca9137a0cbce06eb9f9c8690c21388b3894dc. </details> <details> <summary>⛔ Files ignored due to path filters (86)</summary> * `assets/BuildIcon/Machine_B_001_快速放热.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_002_缓速放热.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_003_死亡余温.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_004_热流冲击.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_005_爆发推力.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_006_热能抽离.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_007_燃烧效率.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_008_热能闪避.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_009_齿轮关节.png` is excluded by `!**/*.png` * `assets/BuildIcon/Machine_B_010_延迟损伤.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_01.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_02.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_03.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_04.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_05.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_06.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_07.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_08.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_09.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_10.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_11.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_12.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_13.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_14.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_15.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_16.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_17.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_18.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_19.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_20.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_21.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_22.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_23.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_24.png` is excluded by `!**/*.png` * `assets/effect/AttackEffect/burn_b/BurnB_25.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0000.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0001.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0002.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0003.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0004.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0005.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0006.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0007.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0008.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0009.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0010.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0011.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0012.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0013.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0014.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0015.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0016.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0017.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0018.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0019.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0020.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0021.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0022.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0023.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0024.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0025.png` is excluded by `!**/*.png` * `assets/objects/briefcase/choose/时间轴 1_0026.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0000.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0001.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0002.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0003.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0004.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0005.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0006.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0007.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0008.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0009.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0010.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0011.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0012.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0013.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0014.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0015.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0016.png` is excluded by `!**/*.png` * `assets/objects/briefcase/open/时间轴 1_0017.png` is excluded by `!**/*.png` * `assets/weapons/回旋镖.png` is excluded by `!**/*.png` * `assets/weapons/滚雷.png` is excluded by `!**/*.png` * `data/characters.csv` is excluded by `!**/*.csv` * `data/items.csv` is excluded by `!**/*.csv` * `data/skills.csv` is excluded by `!**/*.csv` * `新构筑machine卡牌效果.csv` is excluded by `!**/*.csv` </details> <details> <summary>📒 Files selected for processing (99)</summary> * `project.godot` * `resources/builds/BuildMachine_A_001.tres` * `resources/builds/BuildMachine_A_002.tres` * `resources/builds/BuildMachine_A_003.tres` * `resources/builds/BuildMachine_A_004.tres` * `resources/builds/BuildMachine_A_005.tres` * `resources/builds/BuildMachine_A_006.tres` * `resources/builds/BuildMachine_A_007.tres` * `resources/builds/BuildMachine_A_008.tres` * `resources/builds/BuildMachine_A_009.tres` * `resources/builds/BuildMachine_A_010.tres` * `resources/builds/BuildMachine_B_001.tres` * `resources/builds/BuildMachine_B_002.tres` * `resources/builds/BuildMachine_B_003.tres` * `resources/builds/BuildMachine_B_004.tres` * `resources/builds/BuildMachine_B_005.tres` * `resources/builds/BuildMachine_B_006.tres` * `resources/builds/BuildMachine_B_008.tres` * `resources/effects/BoomerangAttackEffect.tscn` * `resources/effects/BriefcaseOpenEffect.tscn` * `resources/effects/CubeDataBoom.tscn` * `resources/effects/DotBurnEffect.tscn` * `resources/effects/ECoreAttackEffect.tscn` * `resources/effects/FireWall.tscn` * `resources/items/Weapon_Stab_drill.tres` * `resources/items/Weapon_Throw_Briefcase.tres` * `resources/items/Weapon_Throw_ECore.tres` * `resources/items/Weapon_Throw_EmojiBomb.tres` * `resources/items/skills/WeaponSkill_Stab_Drill.tres` * `scenes/Stage_test.tscn` * `scenes/actors/characters/Enemy_B1_fat.tscn` * `scenes/actors/characters/Enemy_B2_fat02.tscn` * `scenes/actors/characters/Enemy_D1_corpDoneB.tscn` * `scenes/actors/characters/Enemy_D1_drone.tscn` * `scenes/actors/characters/Enemy_D1_netAdmin.tscn` * `scenes/actors/characters/Enemy_Normal_guard3.tscn` * `scenes/actors/characters/main_character.tscn` * `scenes/actors/etc/BriefcaseHoldingAnimation.tscn` * `scenes/actors/etc/Briefcase_attack.tscn` * `scenes/actors/etc/Briefcase_defense.tscn` * `scenes/actors/etc/EmojiBoomAnimation.tscn` * `scenes/builds/MachineCore.tscn` * `scenes/builds/machine/MachineBurstThrustEffect.tscn` * `scenes/builds/machine/MachineDeathEmberEffect.tscn` * `scenes/builds/machine/MachineHeatFlowCycleEffect.tscn` * `scenes/builds/machine/MachineHeatFlowImpactEffect.tscn` * `scenes/builds/machine/MachineHighSpeedConductionEffect.tscn` * `scenes/builds/machine/MachineModifyHeatDrainEffect.tscn` * `scenes/builds/machine/MachineOverclockEffect.tscn` * `scenes/builds/machine/MachineScorchingFeedbackEffect.tscn` * `scenes/managers/BuildSelectionManager.tscn` * `scenes/objects/Animation_drone_ultimate.tscn` * `scenes/ui/components/BuildCard.tscn` * `scenes/ui/hud/BuildCoreHUD.tscn` * `scenes/weapons/Weapon_Throw_boomerang.tscn` * `scenes/weapons/Weapon_Throw_briefcase.tscn` * `scenes/weapons/Weapon_Throw_eCore.tscn` * `scenes/weapons/Weapon_Throw_metalSpike.tscn` * `scenes/weapons/Weapon_Throw_smokeGrenade.tscn` * `scripts/actors/enemies/EnemyD1NetAdmin.cs` * `scripts/actors/heroes/MainCharacter.cs` * `scripts/actors/heroes/PlayerItemAttachment.cs` * `scripts/actors/heroes/SamplePlayer.cs` * `scripts/actors/heroes/attacks/PlayerAttackTemplate.cs` * `scripts/actors/heroes/attacks/PlayerComboSkipWarmupAttack.cs` * `scripts/actors/heroes/states/PlayerIdleHoldingState.cs` * `scripts/actors/heroes/states/PlayerRunHoldingState.cs` * `scripts/builds/buildcore/MachineCoreEffect.cs` * `scripts/builds/machine/MachineBurstThrustEffect.cs` * `scripts/builds/machine/MachineDeathEmberEffect.cs` * `scripts/builds/machine/MachineDecayDelayEffect.cs` * `scripts/builds/machine/MachineHeatFlowCycleEffect.cs` * `scripts/builds/machine/MachineHeatFlowImpactEffect.cs` * `scripts/builds/machine/MachineHeatRecoveryEffect.cs` * `scripts/builds/machine/MachineHighSpeedConductionEffect.cs` * `scripts/builds/machine/MachineInertiaDriveEffect.cs` * `scripts/builds/machine/MachineModifyHeatCapEffect.cs` * `scripts/builds/machine/MachineModifyHeatDrainEffect.cs` * `scripts/builds/machine/MachineOverclockEffect.cs` * `scripts/builds/machine/MachineScorchingFeedbackEffect.cs` * `scripts/controllers/SpineController.gd` * `scripts/core/GameActor.cs` * `scripts/core/effects/AfterimageController.gd` * `scripts/effects/BriefcaseOpenEffect.cs` * `scripts/effects/DotBurnEffect.cs` * `scripts/effects/ECoreAttackEffect.cs` * `scripts/effects/MechGloveEffect.cs` * `scripts/fx/LightningBeam.cs` * `scripts/fx/RotatingCube.cs` * `scripts/items/world/RigidBodyWorldItemEntity.cs` * `scripts/managers/BuildSelectionManager.cs` * `scripts/systems/BuildEffectDefinition.cs` * `scripts/ui/BuildCard.cs` * `scripts/ui/BuildSelectionWindow.cs` * `scripts/ui/CoreHUD.cs` * `scripts/ui/FloatingDamageText.cs` * `shaders/lightning_beam.tscn` * `shaders/materials/lightning_beam.gdshader` * `构筑卡牌描述模板机制.md` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * resources/items/Weapon_Stab_drill.tres </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| [ext_resource type="Script" uid="uid://3bb6nng4vb53" path="res://scripts/systems/BuildEffectDefinition.cs" id="1_script"] | ||
| [ext_resource type="Script" uid="uid://fd5076f50ca0" path="res://scripts/actors/enemies/attacks/AttackEffectEntry.cs" id="1_uef0n"] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
为 B006 和 B008 配置 EffectEntries 喵。
当前两个资源都没有 EffectEntries 喵。BuildSelectionManager.ApplyEffectBonuses 只会在该数组非空时实例化并应用 ActorEffect 喵。选择这两张卡只会增加 _pickedEffectIds,不会应用卡牌效果,恢复构筑状态时也不会应用喵。
resources/builds/BuildMachine_B_006.tres#L3-L4: 添加引用目标效果场景的AttackEffectEntry子资源,并设置EffectEntries喵。resources/builds/BuildMachine_B_008.tres#L3-L4: 添加引用目标效果场景的AttackEffectEntry子资源,并设置EffectEntries喵。
📍 Affects 2 files
resources/builds/BuildMachine_B_006.tres#L3-L4(this comment)resources/builds/BuildMachine_B_008.tres#L3-L4
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@resources/builds/BuildMachine_B_006.tres` around lines 3 - 4, Configure
EffectEntries for both BuildMachine_B_006.tres and BuildMachine_B_008.tres by
adding AttackEffectEntry subresources referencing their intended effect scenes,
then assign those subresources to each resource’s EffectEntries array so
BuildSelectionManager.ApplyEffectBonuses can instantiate and apply the effects.
| Notes = "投掷的瞬间破坏并生成点击核心 | ||
| " |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
path = Path("resources/items/Weapon_Throw_ECore.tres")
text = path.read_text(encoding="utf-8")
raw_multiline = 'Notes = "投掷的瞬间破坏并生成点击核心\n"'
print(f"raw multiline Notes present: {raw_multiline in text}")
PYRepository: Project-N-E-K-O/K.U.R.O
Length of output: 195
🌐 Web query:
Godot Resource .tres parser double quote string raw newline variant
💡 Result:
In Godot.tres (and.tscn) files, string values are defined within double quotes (e.g., key = "value") [1][2][3]. The parser handles these strings by supporting standard escape sequences, such as \n for a newline character [4][5]. Key points regarding the.tres string format and parser behavior include: 1. Escape Sequences: Within a quoted string in a.tres file, the backslash () acts as an escape character [4]. Common sequences like \n (newline), \t (tab), and \ (escaped backslash) are interpreted by the resource loader [4]. 2. Raw Newlines:.tres files generally do not support "raw" (literal) newlines inside a quoted string unless they are escaped [1][2]. If a newline character appears literally within a set of double quotes without escaping, the parser will likely encounter a syntax error, as Godot's text resource parser expects string values to be defined on a single logical line or properly escaped [1][2]. 3. Parsing Mechanism: The text resource parsing is handled primarily by the ResourceFormatLoaderText class (in resource_format_text.cpp) [1][3]. Unlike GDScript, which allows for multiline strings using triple-double quotes (""") [5], the.tres file format structure is stricter and primarily relies on standard double-quoted strings where literal control characters are represented via escape sequences [1][2]. If you are manually editing.tres files, you should use the \n escape sequence to represent a newline within a string value to ensure the parser correctly interprets it [4][5]. If you are generating these files programmatically, ensure that your serialization logic escapes newline characters before writing the string to the file [1].
Citations:
- 1: https://docs.godotengine.org/en/4.5/engine%5Fdetails/file_formats/tscn.html
- 2: https://docs.godotengine.org/en/stable/engine_details/file_formats/tscn.html
- 3: https://github.com/godotengine/godot-docs/blob/4.2/contributing/development/file%5Fformats/tscn.rst
- 4: https://github.com/godotengine/godot/blob/master/modules/gdscript/gdscript_tokenizer.cpp
- 5: https://forum.godotengine.org/t/how-to-go-to-a-new-line-in-a-string-like-n-in-some-languages/5419
🌐 Web query:
Godot ResourceFormatLoaderText string parsing raw newline variant variant_parser error
💡 Result:
In Godot, parsing errors involving ResourceFormatLoaderText and the VariantParser are commonly triggered by syntax mismatches in text-based resource files (.tres,.tscn), such as unescaped characters, incorrect formatting of sub-resources, or unexpected newlines in contexts where the parser expects a specific token [1][2][3]. The VariantParser is the core utility used by ResourceFormatLoaderText to interpret the text-based structures of these files [1][4]. When it encounters a "raw newline" or a token it does not expect (such as a missing bracket or an incorrectly formatted tag), it throws a parse error [1][2][3]. Key insights into these errors: 1. Syntax Expectations: The parser is strict regarding the structure of text resources, which must start with specific headers like [gd_resource ...] or [gd_scene ...]. If a file is edited manually or saved with invalid characters, missing closing brackets, or improper indentation, the parser will fail [2][3]. 2. Context-Specific Parser Behavior: Issues have historically arisen where certain functions, like preload() in GDScript, used a more restrictive parsing ruleset than standard load() calls, leading to errors when newlines were introduced in certain formatting styles [5]. This was notably resolved in later versions of Godot [5]. 3. The "TextFile" Resource Hack: In Godot 3.x, the "TextFile" resource type was an internal editor utility rather than a robust public API. Attempting to use it as a general-purpose text handler often caused instability, cache conflicts, and parse errors because it was not designed for standard runtime resource management [2][6][7]. This type was subsequently removed from the public API in Godot 4.0 [2]. If you are encountering this error: - Verify file formatting: Ensure your.tres or.tscn file begins with the correct header and that all brackets are closed [2][3]. - Escape characters: Ensure that special characters within strings are correctly escaped if the parser is failing on unexpected characters [3]. - Avoid "TextFile": If you are on Godot 3.x, do not rely on "TextFile" resources. Use standard FileAccess to read and write text data, which is the supported method for handling arbitrary text files [7]. - Update/Consistency: Ensure that the file was not created on a platform with different line-ending conventions (CRLF vs. LF) that might conflict with the parser, though Godot is generally resilient to these [6].
Citations:
- 1: https://github.com/godotengine/godot/blob/master/scene/resources/resource_format_text.cpp
- 2: Opening resource of type "TextFile" causes error or freeze godotengine/godot#63216
- 3: https://forum.godotengine.org/t/how-do-you-get-godots-text-file-resources-to-work-properly/7684
- 4: https://github.com/godotengine/godot/blob/16bb065a/core/variant/variant_parser.h
- 5: preload() has different parsing rules from other function calls godotengine/godot#52499
- 6: Incompatible TextFile resource between Windows 10 and macOS godotengine/godot#36216
- 7: Using TextFiles results in faulty resource cache godotengine/godot#67515
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files resources/items/Weapon_Throw_ECore.tres
# Inspect surrounding .tres format metadata and the exact Notes line without executing repo code.
if command -v od >/dev/null 2>&1; then
od -c resources/items/Weapon_Throw_ECore.tres | sed -n '1,80p'
else
cat -A resources/items/Weapon_Throw_ECore.tres | sed -n '1,40p'
fiRepository: Project-N-E-K-O/K.U.R.O
Length of output: 2209
将 Notes 改为单行文本或 \n 喵
Notes = "..." 里直接包含了原始换行喵。这样会让 Godot 的资源解析器认为字符串未终结而报错喵。改成单行或把换行写成 \n,避免 Weapon_Throw_ECore.tres 无法加载喵。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@resources/items/Weapon_Throw_ECore.tres` around lines 17 - 18, 将
Weapon_Throw_ECore.tres 中的 Notes 字符串改为单行文本,或将其中的原始换行替换为转义序列 \n,确保 Notes
字符串正确闭合并可被 Godot 资源解析器加载。
| /// <summary> | ||
| /// 沿 dir 方向发射射线探测墙壁(避开角色),返回可延伸的最大长度。 | ||
| /// 碰撞层复用场景中 RayCast2D 的配置。 | ||
| /// </summary> | ||
| private float ProbeWallDistance(float dir, float maxLen) | ||
| { | ||
| var space = GetWorld2D().DirectSpaceState; | ||
| float worldAngle = Rotation + dir; | ||
| var query = new PhysicsRayQueryParameters2D | ||
| { | ||
| From = GlobalPosition, | ||
| To = GlobalPosition + new Vector2(Mathf.Cos(worldAngle), Mathf.Sin(worldAngle)) * maxLen, | ||
| CollideWithBodies = true, | ||
| CollideWithAreas = false, | ||
| CollisionMask = _ray?.CollisionMask ?? 4, | ||
| }; | ||
| var result = space.IntersectRay(query); | ||
| if (result.Count == 0 || !result.TryGetValue("collider", out var collider)) | ||
| return maxLen; | ||
| if (collider.As<GodotObject>() is GameActor) | ||
| return maxLen; | ||
| return result.TryGetValue("position", out var pos) | ||
| ? GlobalPosition.DistanceTo(pos.AsVector2()) | ||
| : maxLen; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
随机电弧模式下 _ray 恒为 null,碰撞掩码被硬编码成 4 喵。
第 242 行注释说「碰撞层复用场景中 RayCast2D 的配置」,但这句话在随机电弧模式下永远不成立喵。
SpawnRandomArcs 只在 RandomArcMode == true 时被调用(第 89 行和第 224 行)。而 _ray = GetNodeOrNull<RayCast2D>("RayCast2D") 只出现在第 93 行的 else 分支里喵。所以在随机电弧模式下 _ray 永远是 null,第 254 行的 _ray?.CollisionMask ?? 4 恒等于 4 喵。
后果是墙体探测固定只检测第 3 个碰撞层。若项目的墙体不在该层,电弧就会穿墙;若该层上有非墙体的物理体,电弧会被错误截断喵。
建议把碰撞掩码提升为显式导出属性,不要依赖一个在该模式下不存在的节点喵~
🐛 建议的修复
[ExportCategory("Plasma")]
/// <summary>随机电弧模式:以节点为中心向 360° 随机角度生成 ArcCount 条闪电。</summary>
[Export] public bool RandomArcMode = false;
[Export(PropertyHint.Range, "1,64,1")] public int ArcCount = 1;
+ /// <summary>随机电弧模式下墙体探测使用的碰撞掩码(该模式无 RayCast2D 子节点可复用)。</summary>
+ [Export(PropertyHint.Layers2DPhysics)] public uint WallProbeMask = 4; /// <summary>
/// 沿 dir 方向发射射线探测墙壁(避开角色),返回可延伸的最大长度。
- /// 碰撞层复用场景中 RayCast2D 的配置。
+ /// 碰撞层优先复用场景中 RayCast2D 的配置,随机电弧模式下使用 WallProbeMask。
/// </summary>
@@
- CollisionMask = _ray?.CollisionMask ?? 4,
+ CollisionMask = _ray?.CollisionMask ?? WallProbeMask,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/fx/LightningBeam.cs` around lines 240 - 264, 将 LightningBeam
的墙体探测碰撞掩码改为显式导出的配置属性,供 ProbeWallDistance 直接使用,移除对随机电弧模式下始终为空的 _ray.CollisionMask
及硬编码 4 的依赖;同时保留普通模式下与 RayCast2D 配置一致的现有行为,必要时在初始化阶段复用该节点的掩码作为默认值。
| // 仅 AirWall(空气墙)拦截销毁,其他物理体(地面/障碍/投掷物)不拦截 | ||
| if (body is not GameActor | ||
| && body is Node node | ||
| && string.Equals((string)node.Name, "AirWall", System.StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| SpawnDestroyEffect(); | ||
| QueueFree(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 统计仓库中 AirWall 相关的节点定义与名称判定,评估改造范围
echo "=== 场景中的 AirWall 节点 ==="
rg -n --glob '*.tscn' 'name="AirWall'
echo "=== 代码中按名称判定 AirWall 的位置 ==="
rg -n --glob '*.cs' --glob '*.gd' 'AirWall'
echo "=== 现有 air_wall 组的使用情况 ==="
rg -n 'air_wall'Repository: Project-N-E-K-O/K.U.R.O
Length of output: 185
🏁 Script executed:
#!/bin/bash
set +e
echo "=== repository files containing AirWall ==="
rg -n 'AirWall|"AirWall|AirWall' . || true
echo "=== repository files containing air_wall ==="
rg -n 'air_wall|Air_Wall' . || true
echo "=== candidate C# files ==="
git ls-files '*.cs' | sed -n '1,120p'
echo "=== relevant file excerpts if present ==="
for f in scripts/fx/RotatingCube.cs scripts/items/world/RigidBodyWorldItemEntity.cs; do
if [ -f "$f" ]; then
echo "--- $f exists, line count ---"
wc -l "$f"
echo "--- $f relevant lines ---"
nl -ba "$f" | sed -n '300,345p'
echo "--- $f relevant lines second file ---"
nl -ba "$f" | sed -n '1245,1272p'
else
echo "--- $f missing ---"
fi
doneRepository: Project-N-E-K-O/K.U.R.O
Length of output: 18081
🌐 Web query:
Godot add_child duplicate names add suffix @ suffix documentation
💡 Result:
In Godot, sibling nodes are required to have unique names [1][2]. When you call add_child(node) and the name of the new child conflicts with an existing sibling, the engine automatically renames the new node to maintain uniqueness [3][4][5]. The specific naming behavior is controlled by the force_readable_name argument (formerly legible_unique_name in older versions) [6][2][7]: - By default (force_readable_name = false): Godot assigns a dummy, machine-generated name, typically featuring the @ character (e.g., @Node@123) [1][8][2]. This approach is used because it is computationally efficient and avoids complex checks against existing sibling names [9][2]. - When force_readable_name = true: Godot attempts to preserve the original name of the node but appends a numerical suffix if a collision occurs (e.g., Node2). This operation is more performance-intensive, which is why it is not the default for runtime additions [9][2]. Important notes regarding this behavior: - Character Restrictions: The @ character is reserved by the engine for these auto-generated unique names [10][3]. If you manually attempt to set a node's name to a string containing @, the engine will strip or reject it during validation [10][3]. - Performance: Using force_readable_name = true is generally recommended only when human-readable debugging is required, as the necessary name-collision checks can impact performance when adding many nodes at once [8][9][2]. - NodePath Stability: Because node names are used in NodePath strings, reliance on auto-generated @ names can be fragile if the order or timing of node creation changes [4][5]. It is often better to rely on node types, groups, or direct references rather than specific node names [4][5][11].
Citations:
- 1: https://docs.godotengine.org/en/stable/classes/class_node.html
- 2: https://docs.godotengine.org/en/latest/classes/class_node.html
- 3: Instance-names can not include '@' signs godotengine/godot#27608
- 4: https://forum.godotengine.org/t/symbols-being-added-to-node-name/7197
- 5: https://forum.godotengine.org/t/non-unique-node-names-at-run-time/139583
- 6: add_child has a confusing argument called legible_unique_name godotengine/godot-docs#5515
- 7: https://docs.godotengine.org/en/3.0/classes/class_node.html
- 8: Nodes in the "Remote" window do not match the node name godotengine/godot#92687
- 9: Node names autoincrement not working godotengine/godot#82709
- 10: Node renaming filters out the at signs godotengine/godot#33068
- 11: https://forum.godotengine.org/t/duplicate-nodes/50687
🏁 Script executed:
#!/bin/bash
set +e
awk '112,148 { printf "%d\t%s\n", NR, $0 }' scenes/levels/A1.tscn
awk '202,210 { printf "%d\t%s\n", NR, $0 }' scenes/levels/C2.tscn
awk '168,180 { printf "%d\t%s\n", NR, $0 }' scripts/fx/RotatingCube.cs
awk '1255,1264 { printf "%d\t%s\n", NR, $0 }' scripts/items/world/RigidBodyWorldItemEntity.cs
echo "=== Godot project hints ==="
for f in project.godot .godot/global_script_class_cache.cfg; do
[ -f "$f" ] && { echo "--- $f ---"; sed -n '1,180p' "$f"; }
done
echo "=== GlobalClass / air_wall group occurrences ==="
rg -n 'GlobalClass|group|Groups|air_wall|AirWall' --glob '*.cs' scripts scripts/*.cs 2>/dev/null || trueRepository: Project-N-E-K-O/K.U.R.O
Length of output: 50380
🏁 Script executed:
#!/bin/bash
echo "=== RotatingCube relevant lines ==="
awk '306,333 { printf "%d\t%s\n", NR, $0 }' scripts/fx/RotatingCube.cs
echo "=== RigidBodyWorldItemEntity AirWall relevant lines ==="
awk '1237,1265 { printf "%d\t%s\n", NR, $0 }' scripts/items/world/RigidBodyWorldItemEntity.cs
echo "=== current global group declarations in C# classes ==="
rg -n 'GlobalClass|Godot.Register|RegisterClass|AddToGroup|\["groups"|groups\s*=' --glob '*.cs' scripts || true
echo "=== Godot project/global data around GlobalClass/cache if present ==="
mkdir -p /tmp/kuroc_check
for f in project.godot .godot/global_script_class_cache.cfg .godot/editor/global_script_class_cache.cfg; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n 'GlobalClass|RegisterClass|AddToGroup|groups|AirWall|air_wall' "$f" || true
fi
doneRepository: Project-N-E-K-O/K.U.R.O
Length of output: 50379
用节点组代替 "AirWall" 名称判断喵。 当前判定绑定到了场景节点名,名称修改或节点重名时都会让空气墙拦截漏判或只命中第一面喵。
两处共同改为把空气墙节点加入 air_wall 组,并用 IsInGroup("air_wall") 判断喵。需要同时更新对应的关卡场景。
scripts/fx/RotatingCube.cs#L326-L333:把名称比较换成组判定,并删除冗余的body is Node node检查喵。scripts/items/world/RigidBodyWorldItemEntity.cs#L1261:把名称比较换成同一组判定喵。
📍 Affects 2 files
scripts/fx/RotatingCube.cs#L326-L333(this comment)scripts/items/world/RigidBodyWorldItemEntity.cs#L1259-L1263
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/fx/RotatingCube.cs` around lines 326 - 333, 将
scripts/fx/RotatingCube.cs#L326-L333 中的 AirWall 名称比较改为对 air_wall 节点组调用
IsInGroup("air_wall"),并移除冗余的 body is Node node 检查;同时将
scripts/items/world/RigidBodyWorldItemEntity.cs#L1259-L1263
的相同名称判断改为同一组判定,并更新对应关卡场景将空气墙节点加入 air_wall 组。
|
|
||
| // 撞墙检测:手动物理射线(freeze 刚体子节点的 Area2D 信号不可靠), | ||
| // 沿水平飞行方向检测前方墙体,命中非 GameActor 物理体则停止并销毁 | ||
| if (_impactArmed && StopOnHit && CheckWallHit(_rigidBody.GlobalPosition, _throwHorizontalVelocity)) | ||
| { | ||
| _inFlight = false; | ||
| _flightTimer = 0.0; | ||
| _impactArmed = false; | ||
| _rigidBody.LinearVelocity = Vector2.Zero; | ||
| try { _rigidBody.Set("freeze", true); } catch { } | ||
| RestoreRigidBodyCollision(); | ||
| if (!_isDestroying) | ||
| { | ||
| _landingHideTimer = LandingHideDelay; | ||
| if (!IsDisposableCopy && IsThrowWeapon) | ||
| _inventoryReturnTimer = ThrowWeaponCooldown; | ||
| } | ||
| return; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
撞墙分支没有恢复阴影,也没有重置 _isDropping 喵。
请对比第 748-780 行的正常落地分支喵。那里第 765 行调用了 SetShadowVisible(true),第 751 行把 _isDropping 置 false 喵。
ApplyThrowImpulse 在第 563 行投掷时调用了 SetShadowVisible(false)。新增的撞墙分支停止飞行后没有把阴影恢复回来喵。物品会在墙边静止 LandingHideDelay 秒(默认 2 秒)且没有阴影,与正常落地的表现不一致喵。
另外第 719-720 行的注释也和实现对不上喵:注释写「命中非 GameActor 物理体则停止并销毁」,但 CheckWallHit 只认 AirWall,而且这个分支只是停止飞行并启动隐藏计时,并不会立即销毁喵。
🐛 建议的修复
- // 撞墙检测:手动物理射线(freeze 刚体子节点的 Area2D 信号不可靠),
- // 沿水平飞行方向检测前方墙体,命中非 GameActor 物理体则停止并销毁
+ // 撞墙检测:手动物理射线(freeze 刚体子节点的 Area2D 信号不可靠),
+ // 沿水平飞行方向检测前方空气墙,命中后停止飞行并进入落地隐藏流程
if (_impactArmed && StopOnHit && CheckWallHit(_rigidBody.GlobalPosition, _throwHorizontalVelocity))
{
_inFlight = false;
_flightTimer = 0.0;
_impactArmed = false;
+ _isDropping = false;
_rigidBody.LinearVelocity = Vector2.Zero;
try { _rigidBody.Set("freeze", true); } catch { }
RestoreRigidBodyCollision();
+ SetShadowVisible(true);
if (!_isDestroying)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 撞墙检测:手动物理射线(freeze 刚体子节点的 Area2D 信号不可靠), | |
| // 沿水平飞行方向检测前方墙体,命中非 GameActor 物理体则停止并销毁 | |
| if (_impactArmed && StopOnHit && CheckWallHit(_rigidBody.GlobalPosition, _throwHorizontalVelocity)) | |
| { | |
| _inFlight = false; | |
| _flightTimer = 0.0; | |
| _impactArmed = false; | |
| _rigidBody.LinearVelocity = Vector2.Zero; | |
| try { _rigidBody.Set("freeze", true); } catch { } | |
| RestoreRigidBodyCollision(); | |
| if (!_isDestroying) | |
| { | |
| _landingHideTimer = LandingHideDelay; | |
| if (!IsDisposableCopy && IsThrowWeapon) | |
| _inventoryReturnTimer = ThrowWeaponCooldown; | |
| } | |
| return; | |
| } | |
| // 撞墙检测:手动物理射线(freeze 刚体子节点的 Area2D 信号不可靠), | |
| // 沿水平飞行方向检测前方空气墙,命中后停止飞行并进入落地隐藏流程 | |
| if (_impactArmed && StopOnHit && CheckWallHit(_rigidBody.GlobalPosition, _throwHorizontalVelocity)) | |
| { | |
| _inFlight = false; | |
| _flightTimer = 0.0; | |
| _impactArmed = false; | |
| _isDropping = false; | |
| _rigidBody.LinearVelocity = Vector2.Zero; | |
| try { _rigidBody.Set("freeze", true); } catch { } | |
| RestoreRigidBodyCollision(); | |
| SetShadowVisible(true); | |
| if (!_isDestroying) | |
| { | |
| _landingHideTimer = LandingHideDelay; | |
| if (!IsDisposableCopy && IsThrowWeapon) | |
| _inventoryReturnTimer = ThrowWeaponCooldown; | |
| } | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/items/world/RigidBodyWorldItemEntity.cs` around lines 718 - 736,
Update the wall-hit branch around CheckWallHit to reset _isDropping and call
SetShadowVisible(true), matching the normal landing branch before starting the
hide timer. Also revise the nearby comment to describe the actual AirWall-only
detection and stop-then-hide behavior rather than claiming all non-GameActor
bodies are immediately destroyed.
| [Export] public Label? NameLabel { get; set; } | ||
| [Export] public Label? BuildClassLabel { get; set; } | ||
| [Export] public Label? DescLabel { get; set; } | ||
| [Export] public RichTextLabel? DescLabel { get; set; } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Godot 4 RichTextLabel theme font size normal_font_size add_theme_font_size_override
💡 Result:
In Godot 4, you can modify the font size of a RichTextLabel programmatically using the add_theme_font_size_override method [1][2]. To change the default ("normal") font size, you use the "normal_font_size" theme property identifier [1][2]. Example code: $RichTextLabel.add_theme_font_size_override("normal_font_size", 24) RichTextLabel exposes several distinct theme font size overrides that you can target individually [2]: - normal_font_size [3][2] - bold_font_size [3][2] - italics_font_size [3][2] - bold_italics_font_size [2] - mono_font_size [3][2] If you want to apply a font size override that applies to text generally or temporarily within the text itself (rather than the label's base theme), you can use the push_font_size(font_size) method while building your RichTextLabel's text stack [3][4]. To remove a programmatic override and revert to the theme default, use remove_theme_font_size_override("normal_font_size").
Citations:
- 1: https://godotforums.org/d/36425-implenting-font-options-in-game-settings
- 2: https://blog.febucci.com/2025/08/how-to-dynamically-scale-font-size-in-godot/
- 3: https://docs.godotengine.org/en/4.4/classes/class_richtextlabel.html
- 4: https://docs.godotengine.org/en/4.2/classes/class%5Frichtextlabel.html
🏁 Script executed:
#!/bin/bash
set -e
echo "== files =="
git ls-files | rg '(^|/)BuildCard\.cs$|构筑卡牌描述模板机制\.md$|godotproject\.gd$|project\.godot$' || true
echo "== BuildCard outline/sections =="
if [ -f scripts/ui/BuildCard.cs ]; then
wc -l scripts/ui/BuildCard.cs
sed -n '1,150p' scripts/ui/BuildCard.cs
fi
echo "== search font_size usages in project =="
rg -n 'AddThemeFontSizeOverride|font_size|normal_font_size|BoldFont|ItalicFont|MonoFont' scripts/ui/BuildCard.cs . 2>/dev/null || trueRepository: Project-N-E-K-O/K.U.R.O
Length of output: 32279
🌐 Web query:
Godot Label theme font size property font_size AddThemeFontSizeOverride
💡 Result:
In Godot (specifically version 4.0 and later), font size is handled as a theme property on Control nodes, including the Label node [1][2]. Unlike older versions where font size was often embedded within the font resource itself, Godot 4 separates font size into an integer value defined by the node [2]. To programmatically override the font size for a Label or any Control node that supports it, use the add_theme_font_size_override method [3][4]. Usage: label_node.add_theme_font_size_override("font_size", 24) Key Details: 1. Method Name: The method is add_theme_font_size_override [5][6]. Note that some users may incorrectly attempt to use paths like "theme_override_font_sizes/font_size" [5][6]; the correct string name for the theme item is simply "font_size" [5]. 2. Alternative Approaches: For Label nodes specifically, you can also use a LabelSettings resource [7][2]. If a LabelSettings resource is assigned to a Label, it takes priority over standard theme overrides [7][6]. 3. Theme Items: The "font_size" property is a standard theme item for Label nodes [1][8][9]. You can check if a theme item exists or remove overrides using related methods like remove_theme_font_size_override or has_theme_font_size_override [10]. If your font size change is not appearing, ensure that you are not accidentally overriding it with a LabelSettings resource, which has higher priority than theme property overrides [7][6].
Citations:
- 1: https://docs.godotengine.org/en/latest/classes/class_label.html
- 2: https://docs.godotengine.org/en/stable/tutorials/ui/gui_using_fonts.html
- 3: https://godotengine-godot-47.mintlify.app/ui/themes
- 4: https://docs.godotengine.org/en/stable/classes/class_fontvariation.html
- 5: https://forum.godotengine.org/t/godot-4-font-size-does-not-change-when-set-by-code/569
- 6: Can't set font size from code godotengine/godot#81341
- 7: https://docs.godotengine.org/en/latest/classes/class_labelsettings.html
- 8: https://docs.godotengine.org/en/4.6/classes/class%5Flabel.html
- 9: https://docs.godotengine.org/en/4.4/classes/class_label.html
- 10: https://docs.godotengine.org/en/4.2/classes/class%5Ftheme.html
Line 109 需要改用 RichTextLabel 的字号主题键喵。
DescLabel 已经是 RichTextLabel,不再用 Label 的 font_size 主题项,AddThemeFontSizeOverride("font_size", ...) 对描述文本不会生效喵。
🐛 建议改动(Line 109)
- DescLabel?.AddThemeFontSizeOverride("font_size", Mathf.RoundToInt(12 * ratio));
+ DescLabel?.AddThemeFontSizeOverride("normal_font_size", Mathf.RoundToInt(12 * ratio));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ui/BuildCard.cs` at line 18, Update the theme font-size override for
DescLabel to use the RichTextLabel-specific theme key instead of Label’s
“font_size” key, while preserving the existing size value and override flow.
| float maxHeat = _boundMachineCore.MaxHeat; | ||
| float heat = _boundMachineCore.Heat; | ||
| float overflow = Mathf.Max(heat - maxHeat, 0f); | ||
| float factor = 1f + (maxHeat > 0f ? overflow / maxHeat : 0f); | ||
| var fillScale = HeatFillBar.Scale; | ||
| fillScale.X = _baseFillScaleX * factor; | ||
| HeatFillBar.Scale = fillScale; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
限制 HeatFillBar 的溢出缩放上限喵。
第 62 行没有将 factor 限制为 1.5,但第 57-58 行声明最大缩放为 1.5 喵。
当 Heat 超过 1.5 * MaxHeat 时,热量条会继续扩大,显示结果不符合该上限喵。
请限制溢出比例,或者更新注释以明确支持无上限缩放喵。
建议修复喵
- float factor = 1f + (maxHeat > 0f ? overflow / maxHeat : 0f);
+ float factor = 1f + (maxHeat > 0f ? Mathf.Min(overflow / maxHeat, 0.5f) : 0f);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| float maxHeat = _boundMachineCore.MaxHeat; | |
| float heat = _boundMachineCore.Heat; | |
| float overflow = Mathf.Max(heat - maxHeat, 0f); | |
| float factor = 1f + (maxHeat > 0f ? overflow / maxHeat : 0f); | |
| var fillScale = HeatFillBar.Scale; | |
| fillScale.X = _baseFillScaleX * factor; | |
| HeatFillBar.Scale = fillScale; | |
| float maxHeat = _boundMachineCore.MaxHeat; | |
| float heat = _boundMachineCore.Heat; | |
| float overflow = Mathf.Max(heat - maxHeat, 0f); | |
| float factor = 1f + (maxHeat > 0f ? Mathf.Min(overflow / maxHeat, 0.5f) : 0f); | |
| var fillScale = HeatFillBar.Scale; | |
| fillScale.X = _baseFillScaleX * factor; | |
| HeatFillBar.Scale = fillScale; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ui/CoreHUD.cs` around lines 59 - 65, 在 CoreHUD 中更新 factor 的计算,使
HeatFillBar 的最终缩放不超过已声明的 1.5 倍上限;对溢出比例或计算结果应用上限限制,并保留 MaxHeat 为零时的现有安全行为。
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/builds/buildcore/MachineCoreEffect.cs (1)
106-113: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win在超频开启时也要钳制缩容后的
Heat喵。Line 112 只在
AllowHeatOverflow为false时限制Heat喵。
如果效果降低MaxHeat且超频已开启,现有Heat可以超过新的MaxHeat + MaxOverflowHeat喵。
随后ApplyOverflowBuff会把超出配置上限的热量转换为属性加成喵。
在更新MaxHeat后,始终使用GetHeatCap()钳制Heat喵。建议修复喵
- if (stat == HeatStat.MaxHeat && !AllowHeatOverflow && Heat > MaxHeat) - Heat = MaxHeat; + if (stat == HeatStat.MaxHeat) + Heat = Mathf.Min(Heat, GetHeatCap());🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/builds/buildcore/MachineCoreEffect.cs` around lines 106 - 113, Update the Heat adjustment in the MaxHeat branch of SetStatValue so it always clamps Heat after MaxHeat changes, including when AllowHeatOverflow is enabled. Compare Heat against GetHeatCap() and cap it accordingly, preserving the existing behavior when Heat is already within the effective limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@shaders/materials/fire_effect.gdshader`:
- Around line 74-76: 在计算 projected_uv 前更新 world_pos_3d.z 的保护逻辑,使其无论
cull_backface 状态如何,都丢弃绝对值接近零的深度值;保留现有背面剔除行为,并确保后续投影除法不会接收零或近零分母。
- Around line 1-2: 补充 fire_effect.gdshader 顶部许可归属注释,注明原作者 trisomie21、Shadertoy
转换来源及 Godot Shaders 转载信息,并明确该衍生着色器采用 CC BY-NC-SA 3.0;同时说明商业发布或闭源资产使用前需取得作者另行授权。
---
Outside diff comments:
In `@scripts/builds/buildcore/MachineCoreEffect.cs`:
- Around line 106-113: Update the Heat adjustment in the MaxHeat branch of
SetStatValue so it always clamps Heat after MaxHeat changes, including when
AllowHeatOverflow is enabled. Compare Heat against GetHeatCap() and cap it
accordingly, preserving the existing behavior when Heat is already within the
effective limit.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 56c5d992-ef83-4da1-9882-cbff02e193c7
📒 Files selected for processing (9)
resources/builds/BuildMachine_A_010.tresscenes/builds/machine/MachineHeatFlowImpactEffect.tscnscenes/builds/machine/MachineHeatFlowVisual.tscnscenes/managers/BuildSelectionManager.tscnscripts/builds/buildcore/MachineCoreEffect.csscripts/builds/machine/MachineBurstThrustEffect.csscripts/builds/machine/MachineHeatFlowImpactEffect.csscripts/effects/DotBurnEffect.csshaders/materials/fire_effect.gdshader
💤 Files with no reviewable changes (1)
- scenes/managers/BuildSelectionManager.tscn
🚧 Files skipped from review as they are similar to previous changes (4)
- scenes/builds/machine/MachineHeatFlowImpactEffect.tscn
- resources/builds/BuildMachine_A_010.tres
- scripts/builds/machine/MachineBurstThrustEffect.cs
- scripts/effects/DotBurnEffect.cs
| // Adopted from https://godotshaders.com/shader/ball-of-fire/ by https://godotshaders.com/author/ahaugas/ | ||
| // |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
确认 Godot Shaders 的 “Ball Of Fire” 和原始 Shadertoy “lsf3RH” 的作者、明确许可证,以及 CC BY-NC-SA 3.0 是否允许本项目的预期分发方式。
💡 Result:
关于 Godot Shaders 上的“Ball Of Fire”及原始 Shadertoy 项目“lsf3RH”的作者与许可信息如下: 1. 作者与来源 Godot Shaders 上的“Ball Of Fire”项目由用户 ahaugas 发布,明确标注其为 Shadertoy 项目的移植版本 [1][2]。原始 Shadertoy 项目“lsf3RH”的作者为 trisomie21 [3][4]。 2. 许可协议 Shadertoy 平台上的内容默认受知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版(CC BY-NC-SA 3.0)许可协议保护 [1][2][5]。Godot Shaders 平台亦声明其转载的 Shadertoy 移植项目同样受该协议约束,除非作者另有说明 [1][2]。 3. CC BY-NC-SA 3.0 对分发的限制 CC BY-NC-SA 3.0 协议包含三个核心限制,严重影响商业分发: 非商业性(NonCommercial, NC):禁止将作品用于任何主要以商业优势或金钱补偿为目的的使用 [6][7][8]。这意味着如果您的项目通过销售、内置广告、付费订阅或任何旨在获取商业利润的模式进行分发,则该协议不允许此类使用 [7][8]。 相同方式共享(ShareAlike, SA):如果您对该代码进行了修改(衍生作品)并将其公开分发,则您必须使用相同或兼容的协议(如 CC BY-NC-SA 3.0 或更高版本的协议)来许可您的贡献部分 [7][8][9]。 署名(Attribution, BY):您必须给出适当的信用,提供协议链接,并指示是否进行了更改 [7][8]。 总结:如果您计划的分发方式涉及商业获利(例如出售包含该着色器的软件),则 CC BY-NC-SA 3.0 协议通常不允许此类行为 [7][8]。此外,受“相同方式共享”条款限制,若您将其集成至软件中,可能需确保您的软件或该部分代码遵循开源共享要求 [7][8]。建议在进行商业分发前,咨询法律专业人士或联系原始作者获取明确的书面授权。
Citations:
- 1: https://godotshaders.com/shader/ball-of-fire/
- 2: https://godotshaders.com/shader/neuronal-network-waves/
- 3: https://gist.github.com/DavidSouther/5393329
- 4: https://discourse.threejs.org/t/help-with-portal-shader-border-from-shadertoy/56448
- 5: https://www.shadertoy.com/
- 6: https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode.en
- 7: https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
- 8: https://creativecommons.org/course/cc-cert-edu/unit-3-anatomy-of-a-cc-license/3-3-license-types/
- 9: https://spdx.org/licenses/CC-BY-NC-SA-3.0.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="shaders/materials/fire_effect.gdshader"
if [ -f "$file" ]; then
echo "== file header =="
sed -n '1,30p' "$file"
echo
echo "== license mentions in repository =="
rg -n "CC BY-NC-SA|CC-BY-NC-SA|ShareAlike|BY-NC-SA|license|licence|LICENSE|licen" . \
-g '!*/.git*' -g '!*bin*' -g '!*obj*' | head -200
else
echo "missing $file"
fiRepository: Project-N-E-K-O/K.U.R.O
Length of output: 8785
补全校重着色器的许可归属喵。
fire_effect.gdshader 标注来自 Godot Shaders,但 Godot Shaders 将该 Shadertoy 转换作品默认按 CC BY-NC-SA 3.0 处理喵。当前仓库是 MIT,不能覆盖这一衍生素材的非商业性限制或 ShareAlike 要求喵。非商用发布前,补署原作者 trisomie21 与转换来源,并明确该文件受 CC BY-NC-SA 3.0 约束喵;若计划商业或使用闭源资产,必须先取得作者另行授权喵。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shaders/materials/fire_effect.gdshader` around lines 1 - 2, 补充
fire_effect.gdshader 顶部许可归属注释,注明原作者 trisomie21、Shadertoy 转换来源及 Godot Shaders
转载信息,并明确该衍生着色器采用 CC BY-NC-SA 3.0;同时说明商业发布或闭源资产使用前需取得作者另行授权。
| if (cull_backface && world_pos_3d.z <= 0.0) discard; | ||
|
|
||
| vec2 projected_uv = (world_pos_3d.xy / world_pos_3d.z) - offset + 0.5; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
在投影前保护零深度除法喵。
当 cull_backface 为 false 且平面接近边缘朝向时,world_pos_3d.z 可以为零喵。
Line 76 会执行零除,并使 projected_uv 产生 NaN 或无穷值喵。
这会导致效果闪烁、缺块或错误像素喵。
在执行投影除法前,始终丢弃接近零的深度值喵。
建议修复喵
- if (cull_backface && world_pos_3d.z <= 0.0) discard;
+ if (abs(world_pos_3d.z) <= 0.0001) discard;
+ if (cull_backface && world_pos_3d.z < 0.0) discard;
vec2 projected_uv = (world_pos_3d.xy / world_pos_3d.z) - offset + 0.5;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (cull_backface && world_pos_3d.z <= 0.0) discard; | |
| vec2 projected_uv = (world_pos_3d.xy / world_pos_3d.z) - offset + 0.5; | |
| if (abs(world_pos_3d.z) <= 0.0001) discard; | |
| if (cull_backface && world_pos_3d.z < 0.0) discard; | |
| vec2 projected_uv = (world_pos_3d.xy / world_pos_3d.z) - offset + 0.5; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shaders/materials/fire_effect.gdshader` around lines 74 - 76, 在计算
projected_uv 前更新 world_pos_3d.z 的保护逻辑,使其无论 cull_backface
状态如何,都丢弃绝对值接近零的深度值;保留现有背面剔除行为,并确保后续投影除法不会接收零或近零分母。
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
scenes/builds/machine/MachineFlameRing.tscn (1)
256-374: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value建议给各火苗设置不同的起始帧喵。
这 20 个
AnimatedSprite2D共用同一个SpriteFrames,而且全部autoplay = "default"。它们会在同一帧播放同一张图,整圈火苗会同步闪烁喵。给每个节点加一个不同的frame初值就能打散节奏,成本很低哦~♻️ 示例改法
[node name="AnimatedSprite2D11" type="AnimatedSprite2D" parent="Sprite2D2"] position = Vector2(-74.28571, -228.57143) scale = Vector2(0.4, 0.4) sprite_frames = SubResource("SpriteFrames_3gppx") autoplay = "default" +frame = 7🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scenes/builds/machine/MachineFlameRing.tscn` around lines 256 - 374, 为 AnimatedSprite2D 至 AnimatedSprite2D20 的每个 AnimatedSprite2D 节点设置不同的初始 frame 值,以打散共用 SpriteFrames 和 autoplay="default" 导致的同步闪烁;保留现有位置、缩放及播放配置不变。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/fx/MachineFlameRingEffect.cs`:
- Around line 80-100: Constrain the capsule aspect ratio to at most 1 in both
affected sites: scripts/fx/MachineFlameRingEffect.cs lines 80-100 and
scripts/builds/machine/MachineHeatFlowImpactEffect.cs lines 106-121. Update the
DamageAreaAspectY export range and use the clamped aspect consistently when
calculating Radius and related capsule dimensions in MachineFlameRingEffect and
MachineHeatFlowImpactEffect, including the sibling calculation at line 154.
In `@shaders/materials/fire_cartoon.gdshader`:
- Around line 150-153: Update bounds_mask in the shader’s UV safety-margin block
to validate the original uv coordinates rather than uv_segura, so increasing
margem_seguranca expands sampling without shrinking the visible region to the
center. Preserve the uv_segura calculation for texture sampling and retain the
existing [0,1] bounds behavior.
In `@shaders/materials/fire_ring.gdshader`:
- Around line 44-58: Prevent projection from dividing by non-positive
world_pos_3d.z regardless of cull_backface: update
shaders/materials/fire_ring.gdshader lines 44-58 and
shaders/materials/fire_cartoon.gdshader lines 138-148 to unconditionally discard
z <= 0.0 before projection, or clamp the divisor to a small positive value,
keeping both shaders’ projection behavior consistent.
In `@shaders/materials/ring_of_shield.gdshader`:
- Around line 38-47: 在 ring shield shader 的 border_factor 计算中,先将其限制为不小于 0,再传入
pow,确保边角区域不会对负底数求幂并产生 NaN;保留现有 border_energy 和中心衰减逻辑不变。
---
Nitpick comments:
In `@scenes/builds/machine/MachineFlameRing.tscn`:
- Around line 256-374: 为 AnimatedSprite2D 至 AnimatedSprite2D20 的每个
AnimatedSprite2D 节点设置不同的初始 frame 值,以打散共用 SpriteFrames 和 autoplay="default"
导致的同步闪烁;保留现有位置、缩放及播放配置不变。
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 55dec9b0-4e48-4c06-9340-b8b153e2ad80
⛔ Files ignored due to path filters (4)
assets/effect/AttackEffect/ringA/ring.pngis excluded by!**/*.pngassets/effect/AttackEffect/ringA/ringB.pngis excluded by!**/*.pngassets/effect/AttackEffect/ringA/ringB_1.pngis excluded by!**/*.pngassets/effect/AttackEffect/ringA/ringB_2.pngis excluded by!**/*.png
📒 Files selected for processing (11)
scenes/builds/MachineCore.tscnscenes/builds/machine/MachineFlameRing.tscnscenes/builds/machine/MachineHeatFlowImpactEffect.tscnscenes/builds/machine/MachineHeatFlowVisual.tscnscenes/managers/BuildSelectionManager.tscnscripts/builds/machine/MachineHeatFlowImpactEffect.csscripts/fx/MachineFlameRingEffect.csshaders/materials/fire_cartoon.gdshadershaders/materials/fire_ring.gdshadershaders/materials/ring_of_power.gdshadershaders/materials/ring_of_shield.gdshader
💤 Files with no reviewable changes (2)
- scenes/managers/BuildSelectionManager.tscn
- scenes/builds/MachineCore.tscn
🚧 Files skipped from review as they are similar to previous changes (2)
- scenes/builds/machine/MachineHeatFlowImpactEffect.tscn
- scenes/builds/machine/MachineHeatFlowVisual.tscn
| // 持续扩散:0 → MaxRadius。 | ||
| // 水平胶囊:Radius = 上下半宽(短轴),Height = 总长(Godot 4 中 CapsuleShape2D.Height | ||
| // 是含两端半球的整体长度,直接 = 左右直径 2r)。 | ||
| // 总长 = Height = 2r,总高 = 2×Radius = 2r×aspect | ||
| float t = Mathf.Clamp(_elapsed / ExpandDuration, 0f, 1f); | ||
| _currentRadius = MaxRadius * t; | ||
| if (_contactShape != null) | ||
| { | ||
| _contactShape.Radius = _currentRadius * DamageAreaAspectY; | ||
| _contactShape.Height = 2f * _currentRadius; | ||
| } | ||
|
|
||
| // 视觉环同步扩张:Scale = 基准缩放 × 当前半径 / 基准半径 | ||
| if (_visualSprite != null && VisualBaseRadius > 0f) | ||
| { | ||
| float f = Mathf.Max(_currentRadius / VisualBaseRadius, 0.001f); | ||
| _visualSprite.Scale = _baseSpriteScale * f; | ||
| } | ||
|
|
||
| QueueRedraw(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
水平胶囊换算未约束 DamageAreaAspectY <= 1 喵。 两个文件用同一套换算:Radius = r * DamageAreaAspectY、Height = 2 * r。Godot 的 CapsuleShape2D 强制 Height >= 2 * Radius,所以 aspect 大于 1 时引擎会抬高 Height,判定区域比配置的范围更大且退化为圆。两处的 Export 范围都是 0.1,2,0.05,允许出现该取值喵。
scripts/fx/MachineFlameRingEffect.cs#L80-L100:把DamageAreaAspectY的 Export 范围改为0.1,1,0.05,或在第 88 行用Mathf.Min(DamageAreaAspectY, 1f)钳制后再计算Radius。scripts/builds/machine/MachineHeatFlowImpactEffect.cs#L106-L121:同样收紧第 28 行的 Export 范围,并在第 112 行和第 154 行使用钳制后的 aspect 值。
📍 Affects 2 files
scripts/fx/MachineFlameRingEffect.cs#L80-L100(this comment)scripts/builds/machine/MachineHeatFlowImpactEffect.cs#L106-L121
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/fx/MachineFlameRingEffect.cs` around lines 80 - 100, Constrain the
capsule aspect ratio to at most 1 in both affected sites:
scripts/fx/MachineFlameRingEffect.cs lines 80-100 and
scripts/builds/machine/MachineHeatFlowImpactEffect.cs lines 106-121. Update the
DamageAreaAspectY export range and use the clamped aspect consistently when
calculating Radius and related capsule dimensions in MachineFlameRingEffect and
MachineHeatFlowImpactEffect, including the sibling calculation at line 154.
| // ── 1. 安全边距:UV 向外扩张,让火焰贴边不被精灵边界硬切 ── | ||
| vec2 uv_segura = (uv - 0.5) * (1.0 + margem_seguranca * 2.0) + 0.5; | ||
| // 超出 [0,1] 的像素直接透明(bounds_mask = 0) | ||
| float bounds_mask = step(0.0, uv_segura.x) * step(uv_segura.x, 1.0) * step(0.0, uv_segura.y) * step(uv_segura.y, 1.0); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
margem_seguranca 的实现和注释相反喵。
注释说这个参数让 UV 向外扩张,用来防止火焰在精灵边缘被硬切。但代码先扩张 UV 得到 uv_segura,然后又用同一个 uv_segura 去做 [0,1] 边界裁剪。结果是有效区域被压缩成中心的 1/(1+2*margem_seguranca),margem_seguranca 越大可见范围越小喵。
scenes/builds/machine/MachineFlameRing.tscn 里 margem_seguranca 设为 4.0,按此逻辑只有中心约 11% 的区域会被绘制。请确认这是刻意的取舍。如果目标是防止硬切,bounds_mask 应该基于原始 uv 而不是 uv_segura 喵~
🐛 若目标是防止硬切的改法
vec2 uv_segura = (uv - 0.5) * (1.0 + margem_seguranca * 2.0) + 0.5;
- // 超出 [0,1] 的像素直接透明(bounds_mask = 0)
- float bounds_mask = step(0.0, uv_segura.x) * step(uv_segura.x, 1.0) * step(0.0, uv_segura.y) * step(uv_segura.y, 1.0);
+ // 精灵自身范围内才绘制(用原始 uv 判断,扩张仅用于噪声/形状采样)
+ float bounds_mask = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // ── 1. 安全边距:UV 向外扩张,让火焰贴边不被精灵边界硬切 ── | |
| vec2 uv_segura = (uv - 0.5) * (1.0 + margem_seguranca * 2.0) + 0.5; | |
| // 超出 [0,1] 的像素直接透明(bounds_mask = 0) | |
| float bounds_mask = step(0.0, uv_segura.x) * step(uv_segura.x, 1.0) * step(0.0, uv_segura.y) * step(uv_segura.y, 1.0); | |
| // ── 1. 安全边距:UV 向外扩张,让火焰贴边不被精灵边界硬切 ── | |
| vec2 uv_segura = (uv - 0.5) * (1.0 + margem_seguranca * 2.0) + 0.5; | |
| // 精灵自身范围内才绘制(用原始 uv 判断,扩张仅用于噪声/形状采样) | |
| float bounds_mask = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shaders/materials/fire_cartoon.gdshader` around lines 150 - 153, Update
bounds_mask in the shader’s UV safety-margin block to validate the original uv
coordinates rather than uv_segura, so increasing margem_seguranca expands
sampling without shrinking the visible region to the center. Preserve the
uv_segura calculation for texture sampling and retain the existing [0,1] bounds
behavior.
| void fragment() { | ||
| // ── 伪 3D:背面剔除 + 透视投影出当前片元对应的 UV ── | ||
| if (cull_backface && world_pos_3d.z <= 0.0) discard; | ||
|
|
||
| vec2 projected_uv = (world_pos_3d.xy / world_pos_3d.z) - proj_offset + 0.5; | ||
| if (projected_uv.x < 0.0 || projected_uv.x > 1.0 || projected_uv.y < 0.0 || projected_uv.y > 1.0) | ||
| discard; | ||
|
|
||
| // ── 噪声扭曲(fire_ring 原逻辑,采样改在投影后的 UV 上)── | ||
| float noise1_value = texture(noise1, projected_uv + TIME * 0.15).r - 0.5; | ||
| float noise2_value = texture(noise2, projected_uv - TIME * 0.25).r - 0.5; | ||
| float mixed_noise = noise1_value * noise2_value * influence; | ||
|
|
||
| vec2 offset = vec2(0.02, 0.15) * mixed_noise; | ||
| COLOR = texture(TEXTURE, projected_uv + offset); // 精灵纹理扭曲采样(形状) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
伪 3D 投影的除法只在 cull_backface 为 true 时才安全喵。 两个着色器共用同一段投影代码:先判断 if (cull_backface && world_pos_3d.z <= 0.0) discard;,再执行 world_pos_3d.xy / world_pos_3d.z。守卫依赖 uniform 开关,关闭后 z 为 0 或负值时会得到 NaN,随后的 projected_uv 越界比较对 NaN 全部为假,片元不会被丢弃喵。当前场景材质都设为 true,但 uniform 可在编辑器里关掉~
shaders/materials/fire_ring.gdshader#L44-L58:把第 46 行改为无条件丢弃z <= 0.0的片元,或在第 48 行使用max(world_pos_3d.z, 1e-5)作除数。shaders/materials/fire_cartoon.gdshader#L138-L148:对第 140 行和第 142 行做同样处理,保持两个着色器的投影行为一致。
📍 Affects 2 files
shaders/materials/fire_ring.gdshader#L44-L58(this comment)shaders/materials/fire_cartoon.gdshader#L138-L148
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shaders/materials/fire_ring.gdshader` around lines 44 - 58, Prevent
projection from dividing by non-positive world_pos_3d.z regardless of
cull_backface: update shaders/materials/fire_ring.gdshader lines 44-58 and
shaders/materials/fire_cartoon.gdshader lines 138-148 to unconditionally discard
z <= 0.0 before projection, or clamp the divisor to a small positive value,
keeping both shaders’ projection behavior consistent.
| // how close to frontier is | ||
| float border_factor = 1.0 - distance(2.0*(UV - center), center_uv); | ||
| // 呼吸:以 border_energy_offset 为基准,正弦在 0~1 间波动(关闭时保持原值,不改变原有行为) | ||
| float border_energy = border_energy_offset; | ||
| if (breath_active) | ||
| { | ||
| float wave = 0.5 + 0.5 * sin(TIME * breath_speed * TAU); | ||
| border_energy = mix(border_energy_offset, wave, breath_strength); | ||
| } | ||
| border_factor = pow(border_factor, border_energy*50.0); // speeding up center fading |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
pow 的底数可能为负,会产生 NaN 喵。
第 39 行 border_factor = 1.0 - distance(2.0*(UV - center), center_uv)。center_uv 是单位向量,2.0*(UV - center) 的长度最大约 1.414,两者距离最大约 2.414。因此 border_factor 在精灵四角附近会变成负值喵。
GLSL 中 pow(x, y) 在 x < 0 时结果未定义,通常返回 NaN。第 58 行 COLOR.a 乘上 NaN 后仍是 NaN,即使 is 为 0 也无法把它归零,可能出现黑块或闪烁喵。请先 clamp 再做 pow~
🐛 建议修复
- float border_factor = 1.0 - distance(2.0*(UV - center), center_uv);
+ float border_factor = clamp(1.0 - distance(2.0*(UV - center), center_uv), 0.0, 1.0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // how close to frontier is | |
| float border_factor = 1.0 - distance(2.0*(UV - center), center_uv); | |
| // 呼吸:以 border_energy_offset 为基准,正弦在 0~1 间波动(关闭时保持原值,不改变原有行为) | |
| float border_energy = border_energy_offset; | |
| if (breath_active) | |
| { | |
| float wave = 0.5 + 0.5 * sin(TIME * breath_speed * TAU); | |
| border_energy = mix(border_energy_offset, wave, breath_strength); | |
| } | |
| border_factor = pow(border_factor, border_energy*50.0); // speeding up center fading | |
| // how close to frontier is | |
| float border_factor = clamp(1.0 - distance(2.0*(UV - center), center_uv), 0.0, 1.0); | |
| // 呼吸:以 border_energy_offset 为基准,正弦在 0~1 间波动(关闭时保持原值,不改变原有行为) | |
| float border_energy = border_energy_offset; | |
| if (breath_active) | |
| { | |
| float wave = 0.5 + 0.5 * sin(TIME * breath_speed * TAU); | |
| border_energy = mix(border_energy_offset, wave, breath_strength); | |
| } | |
| border_factor = pow(border_factor, border_energy*50.0); // speeding up center fading |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shaders/materials/ring_of_shield.gdshader` around lines 38 - 47, 在 ring
shield shader 的 border_factor 计算中,先将其限制为不小于 0,再传入 pow,确保边角区域不会对负底数求幂并产生 NaN;保留现有
border_energy 和中心衰减逻辑不变。
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@resources/items/Furniture_C_FoodCartA.tres`:
- Line 8: 同步这些 Texture2D 外部资源的 UID,确保与同路径场景及资源中的 UID 一致;检查并更新
resources/items/Furniture_C_FoodCartA.tres:8、Furniture_C_TableRoundC.tres:8、Furniture_D_PrinterMachineA.tres:8、Furniture_D_TableOfficeB_rwd.tres:8、Furniture_D_WaterMachineA.tres:8、Furniture_D_WhiteBoardA.tres:8、Weapon_Brawl_MechGlove.tres:5、Weapon_Brawl_RiotGlove.tres:5、Weapon_Slash_BunnySword.tres:5,并搜索其他
.tscn/.tres 引用同步相同资源的 UID。
In `@scenes/actors/characters/Enemy_B2_fat02.tscn`:
- Line 4: 确认 Enemy_NormalDrops 与 Enemy_C_EliteDrops 的物品数量及 GlobalDropChance
是否符合游戏平衡,并核实 C/D 区新增通用普通掉落绑定是否为设计预期;若非预期,恢复对应敌人的原掉落表或调整掉落配置。涉及
scenes/actors/characters/Enemy_B2_fat02.tscn(4-4、81-81)、Enemy_C1_waiterA.tscn(5-5、78-78)、Enemy_C1_waiterB.tscn(11-11、106-106)、Enemy_C2_waiterA02.tscn(5-5、71-71)、Enemy_D1_corpDoneA.tscn(4-4、79-79)和
Enemy_D1_corpDoneB.tscn(6-6、90-90);逐一确认这些资源绑定与精英掉落概率,非预期位置需要直接修正,预期位置无需改动。
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2433311b-d0e7-4d73-bda4-a3b98bc2bad8
📒 Files selected for processing (93)
dialogic/styles/textbubble_A.tresresources/builds/BuildMachine_A_001.tresresources/builds/BuildMachine_A_002.tresresources/builds/BuildMachine_A_003.tresresources/builds/BuildMachine_A_004.tresresources/builds/BuildMachine_A_005.tresresources/builds/BuildMachine_A_006.tresresources/builds/BuildMachine_A_007.tresresources/builds/BuildMachine_A_008.tresresources/builds/BuildMachine_A_009.tresresources/builds/BuildMachine_A_010.tresresources/builds/BuildMachine_B_001.tresresources/builds/BuildMachine_B_002.tresresources/builds/BuildMachine_B_003.tresresources/builds/BuildMachine_B_004.tresresources/builds/BuildMachine_B_005.tresresources/builds/BuildMachine_B_006.tresresources/cutscene/B_begin_intro.tresresources/cutscene/C_begin_intro.tresresources/cutscene/D_begin_intro.tresresources/cutscene/D_end_intro.tresresources/items/Furniture_A_ChairOfficeB.tresresources/items/Furniture_A_CubeA.tresresources/items/Furniture_B_CautionTape_fwd.tresresources/items/Furniture_B_CautionTape_side.tresresources/items/Furniture_B_ChairA.tresresources/items/Furniture_B_ChairA_fwd.tresresources/items/Furniture_B_CoffeeMachineA.tresresources/items/Furniture_B_CounterA.tresresources/items/Furniture_B_CounterB.tresresources/items/Furniture_B_GarbageCanA.tresresources/items/Furniture_B_LuggageCartA.tresresources/items/Furniture_B_PlantA.tresresources/items/Furniture_B_SofaA.tresresources/items/Furniture_B_SofaA_fwd.tresresources/items/Furniture_B_SofaA_rwd.tresresources/items/Furniture_B_TableRoundA.tresresources/items/Furniture_B_TableRoundB.tresresources/items/Furniture_B_VendingMachineA.tresresources/items/Furniture_C_ChairB.tresresources/items/Furniture_C_ChairB_fwd.tresresources/items/Furniture_C_FoodCartA.tresresources/items/Furniture_C_TableRoundC.tresresources/items/Furniture_C_TableSquareA.tresresources/items/Furniture_D_PrinterMachineA.tresresources/items/Furniture_D_TableOfficeB_rwd.tresresources/items/Furniture_D_WaterMachineA.tresresources/items/Furniture_D_WhiteBoardA.tresresources/items/Weapon_Brawl_MechGlove.tresresources/items/Weapon_Brawl_RiotGlove.tresresources/items/Weapon_Slash_BunnySword.tresresources/items/Weapon_Slash_ViolinBow.tresresources/items/Weapon_Throw_Briefcase.tresresources/items/Weapon_Throw_GravityGrenade.tresresources/items/Weapon_Throw_MetalSpike.tresresources/items/Weapon_Throw_SmokeGrenade.tresresources/items/skills/WeaponSkill_Brawl_DiscoBall.tresresources/items/skills/WeaponSkill_Brawl_MechGlove.tresresources/items/skills/WeaponSkill_Brawl_RiotBracer.tresresources/items/skills/WeaponSkill_Brawl_RiotGlove.tresresources/items/skills/WeaponSkill_Slash_Baguette.tresresources/items/skills/WeaponSkill_Slash_BarrierGate.tresresources/items/skills/WeaponSkill_Slash_BunnySword.tresresources/items/skills/WeaponSkill_Slash_ExpandBaton.tresresources/items/skills/WeaponSkill_Slash_ViolinBow.tresresources/items/skills/WeaponSkill_Stab_Corkscrew.tresresources/items/skills/WeaponSkill_Stab_Drill.tresresources/items/skills/WeaponSkill_Stab_Umbrella.tresresources/loot/Enemy_B_EliteDrops.tresresources/loot/Enemy_B_NormalDrops.tresresources/loot/Enemy_C_EliteDrops.tresresources/loot/Enemy_C_NormalDrops.tresresources/loot/Enemy_EliteDrops.tresresources/loot/Enemy_NormalDrops.tresresources/loot/Enemy_Normal_NormalDrops.tresresources/loot/Enemy_SpecialDrops.tresscenes/Stage_2.tscnscenes/Stage_3.tscnscenes/actors/characters/Enemy_B1_fat.tscnscenes/actors/characters/Enemy_B1_thin.tscnscenes/actors/characters/Enemy_B2_fat02.tscnscenes/actors/characters/Enemy_C1_waiterA.tscnscenes/actors/characters/Enemy_C1_waiterB.tscnscenes/actors/characters/Enemy_C2_waiterA02.tscnscenes/actors/characters/Enemy_D1_corpDoneA.tscnscenes/actors/characters/Enemy_D1_corpDoneB.tscnscenes/actors/characters/Enemy_D1_netAdmin.tscnscenes/actors/characters/Enemy_D1_printerMachine.tscnscenes/actors/characters/Enemy_Normal_guard1.tscnscenes/actors/characters/Enemy_Normal_guard2.tscnscenes/actors/characters/Enemy_Normal_guard3.tscnscenes/levels/B_begin.tscnscenes/managers/BuildSelectionManager.tscn
🚧 Files skipped from review as they are similar to previous changes (16)
- resources/items/skills/WeaponSkill_Stab_Drill.tres
- resources/builds/BuildMachine_B_002.tres
- resources/items/skills/WeaponSkill_Brawl_RiotBracer.tres
- resources/builds/BuildMachine_A_004.tres
- resources/builds/BuildMachine_B_001.tres
- resources/builds/BuildMachine_A_009.tres
- resources/builds/BuildMachine_B_003.tres
- resources/builds/BuildMachine_B_005.tres
- resources/builds/BuildMachine_A_005.tres
- resources/items/Weapon_Throw_Briefcase.tres
- resources/builds/BuildMachine_A_006.tres
- resources/builds/BuildMachine_A_008.tres
- resources/builds/BuildMachine_B_006.tres
- resources/builds/BuildMachine_A_010.tres
- resources/builds/BuildMachine_B_004.tres
- resources/builds/BuildMachine_A_002.tres
| [ext_resource type="PackedScene" uid="uid://ciwlpetibguab" path="res://shaders/destroy_somke.tscn" id="4_1ch32"] | ||
| [ext_resource type="PackedScene" uid="uid://cmh3bnabinwi0" path="res://resources/effects/CameraShakeEffect.tscn" id="5_p8p0i"] | ||
| [ext_resource type="Texture2D" uid="uid://b3ipocumrfmc3" path="res://assets/furnitures/hotel_scene3/FoodCartA.png" id="6_oqybt"] | ||
| [ext_resource type="Texture2D" uid="uid://12j10fuh5jy7" path="res://assets/furnitures/hotel_scene3/FoodCartA.png" id="6_oqybt"] |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from collections import defaultdict
from pathlib import Path
import re
pattern = re.compile(
r'\[ext_resource type="Texture2D" uid="([^"]+)" path="([^"]+)"'
)
references = defaultdict(list)
for file_path in Path(".").rglob("*"):
if not file_path.is_file() or file_path.suffix not in {".tres", ".tscn"}:
continue
try:
text = file_path.read_text(encoding="utf-8")
except (UnicodeDecodeError, OSError):
continue
for uid, asset_path in pattern.findall(text):
references[asset_path].append((uid, str(file_path)))
for asset_path, entries in sorted(references.items()):
uids = {uid for uid, _ in entries}
if len(uids) > 1:
print(asset_path)
for uid, file_path in entries:
print(f" {uid} {file_path}")
PYRepository: Project-N-E-K-O/K.U.R.O
Length of output: 6758
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
files = [
"resources/items/Furniture_C_FoodCartA.tres",
"resources/items/Furniture_C_TableRoundC.tres",
"resources/items/Furniture_D_PrinterMachineA.tres",
"resources/items/Furniture_D_TableOfficeB_rwd.tres",
"resources/items/Furniture_D_WaterMachineA.tres",
"resources/items/Furniture_D_WhiteBoardA.tres",
"resources/items/Weapon_Brawl_MechGlove.tres",
"resources/items/Weapon_Brawl_RiotGlove.tres",
"resources/items/Weapon_Slash_BunnySword.tres",
]
pattern = re.compile(r'\[ext_resource type="Texture2D" uid="([^"]+)" path="([^"]+)" id="([^"]+)"\]')
for file_path in files:
text = Path(file_path).read_text(encoding="utf-8", errors="replace")
for uid, path, eid in pattern.findall(text):
candidates = []
for p in Path(".").rglob("*.tscn"):
try:
s = p.read_text(encoding="utf-8", errors="replace")
except Exception:
continue
for suid, spath, seid in pattern.findall(s):
if spath == path and seid != eid:
candidates.append((uid, spath, eid, suid, seid, p))
print(f"{file_path}: {uid} {path} id={eid}")
for cu, sp, ce, su, se, p in candidates:
print(f" SCENE MATCH: {p}: {cu} {sp} id={ce}")
# Also list all entries for listed paths so the canonical UID does not depend on the current patch only.
for path in [
"res://assets/furnitures/hotel_scene3/FoodCartA.png",
"res://assets/furnitures/hotel_scene3/TableRoundC.png",
"res://assets/furnitures/hotel_scene4/PrinterMachineA.png",
"res://assets/furnitures/hotel_scene4/TableOfficeB_rwd.png",
"res://assets/furnitures/hotel_scene4/WaterMachineA.png",
"res://assets/furnitures/hotel_scene4/WhiteBoardA.png",
"res://assets/weapons/机械冲撞臂.png",
"res://assets/weapons/镇暴拳套.png",
"res://assets/weapons/光剑.png",
]:
matches = []
for p in Path(".").rglob("*.tscn"):
try:
s = p.read_text(encoding="utf-8", errors="replace")
except Exception:
continue
matches.extend(pattern.findall(s))
print(f"PATH {path}: {[e[:3] for e in matches]}")
PYRepository: Project-N-E-K-O/K.U.R.O
Length of output: 50380
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
files = [
"resources/items/Furniture_C_FoodCartA.tres",
"resources/items/Furniture_C_TableRoundC.tres",
"resources/items/Furniture_D_PrinterMachineA.tres",
"resources/items/Furniture_D_TableOfficeB_rwd.tres",
"resources/items/Furniture_D_WaterMachineA.tres",
"resources/items/Furniture_D_WhiteBoardA.tres",
"resources/items/Weapon_Brawl_MechGlove.tres",
"resources/items/Weapon_Brawl_RiotGlove.tres",
"resources/items/Weapon_Slash_BunnySword.tres",
]
paths = [
"res://assets/furnitures/hotel_scene3/FoodCartA.png",
"res://assets/furnitures/hotel_scene3/TableRoundC.png",
"res://assets/furnitures/hotel_scene4/PrinterMachineA.png",
"res://assets/furnitures/hotel_scene4/TableOfficeB_rwd.png",
"res://assets/furnitures/hotel_scene4/WaterMachineA.png",
"res://assets/furnitures/hotel_scene4/WhiteBoardA.png",
"res://assets/weapons/机械冲撞臂.png",
"res://assets/weapons/镇暴拳套.png",
"res://assets/weapons/光剑.png",
]
pattern = re.compile(r'\[ext_resource type="Texture2D" uid="([^"]+)" path="([^"]+)" id="([^"]+)"\]')
tres_by_path = {}
tscn_by_path = {}
for file_path in files:
text = Path(file_path).read_text(encoding="utf-8", errors="replace")
for uid, path, eid in pattern.findall(text):
tres_by_path[path] = (uid, eid, file_path)
for tscn in Path(".").rglob("*.tscn"):
text = tscn.read_text(encoding="utf-8", errors="replace")
for uid, path, eid in pattern.findall(text):
if path in paths:
tscn_by_path.setdefault(path, []).append((uid, eid, tscn))
matches = []
for path in paths:
tres = tres_by_path.get(path)
scenes = tscn_by_path.get(path, [])
if not tres:
continue
matching_scenes = [s for s in scenes if s[0] == tres[0]]
differing_scenes = [s for s in scenes if s[0] != tres[0]]
matches.append((path, tres, matching_scenes, differing_scenes))
print("path; tres_uid; tres_id; scene_uid=scene_id matching_count; differing_scene_uid=scene_id differing_count")
for path, tres, matching_scenes, differing_scenes in matches:
print(f"{path}; uid={tres[0]} id={tres[1]} file={tres[2]} "
f"same={len(matching_scenes)} diff={len(differing_scenes)}")
for uid, eid, tscn in differing_scenes:
print(f" DIFF: {tscn}: uid={uid} id={eid}")
PYRepository: Project-N-E-K-O/K.U.R.O
Length of output: 2463
同步这些 Texture2D 的外部 UID 喵。
这些 .tres 已使用新的 UID,但同路径对应场景仍保留旧 UID,会破坏 Godot 的资源身份一致性喵。除现有场景外,还需要确认这些资源是否在其他 .tscn/.tres 引用中被同步喵。
📍 Affects 9 files
resources/items/Furniture_C_FoodCartA.tres#L8-L8(this comment)resources/items/Furniture_C_TableRoundC.tres#L8-L8resources/items/Furniture_D_PrinterMachineA.tres#L8-L8resources/items/Furniture_D_TableOfficeB_rwd.tres#L8-L8resources/items/Furniture_D_WaterMachineA.tres#L8-L8resources/items/Furniture_D_WhiteBoardA.tres#L8-L8resources/items/Weapon_Brawl_MechGlove.tres#L5-L5resources/items/Weapon_Brawl_RiotGlove.tres#L5-L5resources/items/Weapon_Slash_BunnySword.tres#L5-L5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@resources/items/Furniture_C_FoodCartA.tres` at line 8, 同步这些 Texture2D 外部资源的
UID,确保与同路径场景及资源中的 UID 一致;检查并更新
resources/items/Furniture_C_FoodCartA.tres:8、Furniture_C_TableRoundC.tres:8、Furniture_D_PrinterMachineA.tres:8、Furniture_D_TableOfficeB_rwd.tres:8、Furniture_D_WaterMachineA.tres:8、Furniture_D_WhiteBoardA.tres:8、Weapon_Brawl_MechGlove.tres:5、Weapon_Brawl_RiotGlove.tres:5、Weapon_Slash_BunnySword.tres:5,并搜索其他
.tscn/.tres 引用同步相同资源的 UID。
| [ext_resource type="Script" uid="uid://dklt5k3rq7qjb" path="res://scripts/actors/enemies/EnemyB2Fat02.cs" id="1_gvrrx"] | ||
| [ext_resource type="Resource" uid="uid://d2yxetxicyb1b" path="res://resources/loot/Enemy_B_NormalDrops.tres" id="2_ooggc"] | ||
| [ext_resource type="Script" uid="uid://bmrdoiyhd61t4" path="res://scripts/actors/enemies/EnemyB2Fat02.cs" id="1_gvrrx"] | ||
| [ext_resource type="Resource" uid="uid://cxoksd62s5rbw" path="res://resources/loot/Enemy_NormalDrops.tres" id="2_s42mu"] |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for file in \
resources/loot/Enemy_NormalDrops.tres \
resources/loot/Enemy_B_NormalDrops.tres \
resources/loot/Enemy_C_NormalDrops.tres \
resources/loot/Enemy_EliteDrops.tres \
resources/loot/Enemy_C_EliteDrops.tres
do
if [[ -f "$file" ]]; then
echo "===== $file ====="
nl -ba "$file" | sed -n '1,260p'
fi
done
echo "===== 场景掉落表绑定 ====="
rg -n -C 2 'LootTable\s*=' \
scenes/actors/characters/Enemy_B2_fat02.tscn \
scenes/actors/characters/Enemy_C1_waiterA.tscn \
scenes/actors/characters/Enemy_C1_waiterB.tscn \
scenes/actors/characters/Enemy_C2_waiterA02.tscn \
scenes/actors/characters/Enemy_D1_corpDoneA.tscn \
scenes/actors/characters/Enemy_D1_corpDoneB.tscnRepository: Project-N-E-K-O/K.U.R.O
Length of output: 252
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for file in \
resources/loot/Enemy_NormalDrops.tres \
resources/loot/Enemy_B_NormalDrops.tres \
resources/loot/Enemy_C_NormalDrops.tres \
resources/loot/Enemy_EliteDrops.tres \
resources/loot/Enemy_C_EliteDrops.tres
do
if [[ -f "$file" ]]; then
echo "===== $file ====="
awk '{printf "%7d\t%s\n", NR, $0}' "$file" | sed -n '1,260p'
else
echo "missing: $file"
fi
done
echo "===== 场景掉落表绑定 ====="
grep -RIn -C 2 'LootTable\s*=' \
scenes/actors/characters/Enemy_B2_fat02.tscn \
scenes/actors/characters/Enemy_C1_waiterA.tscn \
scenes/actors/characters/Enemy_C1_waiterB.tscn \
scenes/actors/characters/Enemy_C2_waiterA02.tscn \
scenes/actors/characters/Enemy_D1_corpDoneA.tscn \
scenes/actors/characters/Enemy_D1_corpDoneB.tscn || true
echo "===== 所有 L 区域掉落表文件 ====="
find resources/loot -maxdepth 1 -type f -name 'Enemy_*Drops.tres' | sort | while read -r file; do
echo "--- $file ---"
grep -n 'Entries\|Resource\|path=' "$file" | sed -n '1,220p'
doneRepository: Project-N-E-K-O/K.U.R.O
Length of output: 33130
确认掉落合并对游戏平衡的影响喵。
Enemy_NormalDrops GlobalDropChance 为 0.25,与 C 区普通掉落相同;但通用表加入了更多物品,掉落池更宽喵。
Enemy_C_EliteDrops 原本只有两项,现在 Enemy_C1_waiterB.tscn 绑到项数更多的通用精英掉落表,可能改变精英掉落概率喵。
C/D 区新增通用普通掉落绑定是否属于设计预期,请在游戏平衡角度确认喵。
📍 Affects 6 files
scenes/actors/characters/Enemy_B2_fat02.tscn#L4-L4(this comment)scenes/actors/characters/Enemy_B2_fat02.tscn#L81-L81scenes/actors/characters/Enemy_C1_waiterA.tscn#L5-L5scenes/actors/characters/Enemy_C1_waiterA.tscn#L78-L78scenes/actors/characters/Enemy_C1_waiterB.tscn#L11-L11scenes/actors/characters/Enemy_C1_waiterB.tscn#L106-L106scenes/actors/characters/Enemy_C2_waiterA02.tscn#L5-L5scenes/actors/characters/Enemy_C2_waiterA02.tscn#L71-L71scenes/actors/characters/Enemy_D1_corpDoneA.tscn#L4-L4scenes/actors/characters/Enemy_D1_corpDoneA.tscn#L79-L79scenes/actors/characters/Enemy_D1_corpDoneB.tscn#L6-L6scenes/actors/characters/Enemy_D1_corpDoneB.tscn#L90-L90
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scenes/actors/characters/Enemy_B2_fat02.tscn` at line 4, 确认 Enemy_NormalDrops
与 Enemy_C_EliteDrops 的物品数量及 GlobalDropChance 是否符合游戏平衡,并核实 C/D
区新增通用普通掉落绑定是否为设计预期;若非预期,恢复对应敌人的原掉落表或调整掉落配置。涉及
scenes/actors/characters/Enemy_B2_fat02.tscn(4-4、81-81)、Enemy_C1_waiterA.tscn(5-5、78-78)、Enemy_C1_waiterB.tscn(11-11、106-106)、Enemy_C2_waiterA02.tscn(5-5、71-71)、Enemy_D1_corpDoneA.tscn(4-4、79-79)和
Enemy_D1_corpDoneB.tscn(6-6、90-90);逐一确认这些资源绑定与精英掉落概率,非预期位置需要直接修正,预期位置无需改动。
machineHeatRecoveryeffect修复
修复了machine相关脚本数值根据当前值的变化而变化的问题,改为使用绝对值
优化RigidBodyWorldItemEntity,投掷后使其在接触airwall时停止飞行
添加briefcase投掷武器相关场景,新增briefcase生成特效逻辑
修复了dotburneffect在收到二次伤害后立刻重置burn伤害计时的错误逻辑
添加spinecotroller跳帧方法,
添加玩家攻击循环active的方法
优化cube追踪逻辑,实现360度追踪,以及根据伤害对象自动选择追踪目标
boomerang优化
新增电击核心以及相关脚本
修复cube无法找到attacker的问题
新增machinecore中heatbar在当前heat值大于maxheat值时继续等比增大的方法
优化电击核心运动逻辑
新增lightingBeam释放逻辑
新增machineB系列卡牌草图
新增machineB系列脚本效果
修改玩家攻击模板中hit 事件回合隔离防止数据污染
新增卡牌当前可选数值的高亮功能
新增B004B005构筑脚本
Summary by CodeRabbit