Skip to content

Enhance GroundCraft animations and recipes#1171

Merged
ACaiCat merged 2 commits into
UnrealMultiple:masterfrom
loguhan:codex/groundcraft-v1-1
Jun 30, 2026
Merged

Enhance GroundCraft animations and recipes#1171
ACaiCat merged 2 commits into
UnrealMultiple:masterfrom
loguhan:codex/groundcraft-v1-1

Conversation

@loguhan

@loguhan loguhan commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add locked spiral fusion animation for consumed ground-craft drops
  • add dedicated Zenith animation/finale and default recipes for Water Candle, Life Crystal, Wormhole Potion, Bloody Tear, and Zenith
  • enable exact ingredient type matching by default to avoid similar-recipe miscrafts
  • update GroundCraft docs, manifest, examples, and plugin index

Validation

  • dotnet build ./src/GroundCraft/GroundCraft.csproj -c Release
  • parsed Plugins.json, manifest.json, config.example.json, and recipes.example.json

Summary by Sourcery

引入带有专属 Zenith 视觉效果的螺旋工艺动画,收紧材料匹配规则,并扩展默认 GroundCraft 配方和文档。

New Features:

  • 为被消耗的地面工艺掉落物添加可配置的螺旋动画,包括更高的专属动画路径以及针对 Zenith 工艺的终章动画。
  • 引入 Water Candle、Life Crystal、Wormhole Potion、Bloody Tear 和 Zenith 的默认地面配方,并支持 Mythril/Orichalcum Anvil。

Enhancements:

  • 默认要求材料类型完全匹配,并追踪因多余物品类型被拒绝的情况,以减少相似配方导致的误制。
  • 提升每次扫描中每个集群的默认最大工艺次数,并通过 GroundCraft info 和 audit 命令公开新的设置和运行时统计数据。
  • 优化合成台中心检测,使动画和产物在适用情况下从对应的合成台位置生成。

Documentation:

  • 记录新的精确匹配和动画选项,更新配置示例,并添加 v1.1.0 更新日志条目,说明动画和配方方面的变更。
  • 更新插件清单描述,以提及精确匹配和螺旋动画。
Original summary in English

Summary by Sourcery

Introduce spiral craft animations with dedicated Zenith visuals, tighten ingredient matching, and expand default GroundCraft recipes and docs.

New Features:

  • Add configurable spiral animation for consumed ground-craft drops, including a taller dedicated animation path and finale for Zenith crafts.
  • Introduce default ground recipes for Water Candle, Life Crystal, Wormhole Potion, Bloody Tear, and Zenith, including Mythril/Orichalcum Anvil support.

Enhancements:

  • Require exact ingredient type matching by default and track rejects due to extra item types to reduce similar-recipe miscrafts.
  • Increase the default maximum crafts per cluster per scan and expose new settings and runtime stats via the GroundCraft info and audit commands.
  • Refine crafting station center detection so animations and outputs originate from the associated station where applicable.

Documentation:

  • Document new exact-matching and animation options, update configuration examples, and add a v1.1.0 changelog entry describing the animation and recipe changes.
  • Update plugin manifest descriptions to mention exact matching and spiral animations.
Original summary in English

Summary by Sourcery

引入带有专属 Zenith 视觉效果的螺旋工艺动画,收紧材料匹配规则,并扩展默认 GroundCraft 配方和文档。

New Features:

  • 为被消耗的地面工艺掉落物添加可配置的螺旋动画,包括更高的专属动画路径以及针对 Zenith 工艺的终章动画。
  • 引入 Water Candle、Life Crystal、Wormhole Potion、Bloody Tear 和 Zenith 的默认地面配方,并支持 Mythril/Orichalcum Anvil。

Enhancements:

  • 默认要求材料类型完全匹配,并追踪因多余物品类型被拒绝的情况,以减少相似配方导致的误制。
  • 提升每次扫描中每个集群的默认最大工艺次数,并通过 GroundCraft info 和 audit 命令公开新的设置和运行时统计数据。
  • 优化合成台中心检测,使动画和产物在适用情况下从对应的合成台位置生成。

Documentation:

  • 记录新的精确匹配和动画选项,更新配置示例,并添加 v1.1.0 更新日志条目,说明动画和配方方面的变更。
  • 更新插件清单描述,以提及精确匹配和螺旋动画。
Original summary in English

Summary by Sourcery

Introduce spiral craft animations with dedicated Zenith visuals, tighten ingredient matching, and expand default GroundCraft recipes and docs.

New Features:

  • Add configurable spiral animation for consumed ground-craft drops, including a taller dedicated animation path and finale for Zenith crafts.
  • Introduce default ground recipes for Water Candle, Life Crystal, Wormhole Potion, Bloody Tear, and Zenith, including Mythril/Orichalcum Anvil support.

Enhancements:

  • Require exact ingredient type matching by default and track rejects due to extra item types to reduce similar-recipe miscrafts.
  • Increase the default maximum crafts per cluster per scan and expose new settings and runtime stats via the GroundCraft info and audit commands.
  • Refine crafting station center detection so animations and outputs originate from the associated station where applicable.

Documentation:

  • Document new exact-matching and animation options, update configuration examples, and add a v1.1.0 changelog entry describing the animation and recipe changes.
  • Update plugin manifest descriptions to mention exact matching and spiral animations.

@loguhan loguhan requested a review from a team as a code owner June 30, 2026 07:46

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了一个问题,并留下了一些高层次的反馈:

  • TryStartCraftAnimation 中,如果后续某个 IngredientTake 校验失败,你会在已经向 _lockedItemIndexes 添加了一些索引并发送了 SyncItemNoGrab 之后返回 false,但没有回滚这些更改;建议要么在加锁/同步之前先对所有的 take 做完整校验,要么增加一条回滚路径,以避免部分被锁定的物品处于不一致状态。
  • IsLockedItemPacket/OnGetData 的逻辑把所有相关消息的前两个字节都当作物品索引来处理,但不同的数据包类型(例如 SyncItemDespawnItemOwner)可能有不同的数据布局;更安全的做法可能是按消息类型分别解析索引,或使用 tModLoader/TShock 的数据包辅助工具,以避免读取到错误的索引。
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- In `TryStartCraftAnimation`, if a later `IngredientTake` validation fails you return `false` after having already added some indices to `_lockedItemIndexes` and sent `SyncItemNoGrab`, but without rolling back those changes; consider either validating all takes before locking/syncing or adding a rollback path to avoid leaving partially locked items in an inconsistent state.
- The `IsLockedItemPacket`/`OnGetData` logic treats the first two bytes of all relevant messages as an item index, but different packet types (e.g. `SyncItemDespawn`, `ItemOwner`) may have different layouts; it may be safer to parse indices per-message-type or use the tModLoader/TShock packet helpers to avoid reading the wrong index.

## Individual Comments

### Comment 1
<location path="src/GroundCraft/GroundCraft.Crafting.cs" line_range="141-150" />
<code_context>
+        foreach (DropRecipe recipe in _recipes)
</code_context>
<issue_to_address>
**question (bug_risk):** New crafting loop now stops after the first matching recipe, changing multi-recipe behavior per cluster

Previously `TryCraftCluster` could apply multiple recipes per scan (up to `MaxCraftsPerClusterPerScan`), tracking `craftedRecipeSignatures` to avoid repeats, and only stopped when `available` was exhausted or the limit was hit. The new code returns on the first successful recipe/animation, so clusters that can satisfy multiple recipes now craft only one per scan. If this isn’t intended, consider reintroducing a loop that continues trying other recipes while honoring `MaxCraftsPerClusterPerScan`, or restricting early returns only to cases where animation must be exclusive. If it is intended, please document how `MaxCraftsPerClusterPerScan` should now be interpreted given this changed behavior.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的代码审查有帮助,欢迎分享 ✨
帮助我变得更有用!请对每条评论点击 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In TryStartCraftAnimation, if a later IngredientTake validation fails you return false after having already added some indices to _lockedItemIndexes and sent SyncItemNoGrab, but without rolling back those changes; consider either validating all takes before locking/syncing or adding a rollback path to avoid leaving partially locked items in an inconsistent state.
  • The IsLockedItemPacket/OnGetData logic treats the first two bytes of all relevant messages as an item index, but different packet types (e.g. SyncItemDespawn, ItemOwner) may have different layouts; it may be safer to parse indices per-message-type or use the tModLoader/TShock packet helpers to avoid reading the wrong index.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `TryStartCraftAnimation`, if a later `IngredientTake` validation fails you return `false` after having already added some indices to `_lockedItemIndexes` and sent `SyncItemNoGrab`, but without rolling back those changes; consider either validating all takes before locking/syncing or adding a rollback path to avoid leaving partially locked items in an inconsistent state.
- The `IsLockedItemPacket`/`OnGetData` logic treats the first two bytes of all relevant messages as an item index, but different packet types (e.g. `SyncItemDespawn`, `ItemOwner`) may have different layouts; it may be safer to parse indices per-message-type or use the tModLoader/TShock packet helpers to avoid reading the wrong index.

## Individual Comments

### Comment 1
<location path="src/GroundCraft/GroundCraft.Crafting.cs" line_range="141-150" />
<code_context>
+        foreach (DropRecipe recipe in _recipes)
</code_context>
<issue_to_address>
**question (bug_risk):** New crafting loop now stops after the first matching recipe, changing multi-recipe behavior per cluster

Previously `TryCraftCluster` could apply multiple recipes per scan (up to `MaxCraftsPerClusterPerScan`), tracking `craftedRecipeSignatures` to avoid repeats, and only stopped when `available` was exhausted or the limit was hit. The new code returns on the first successful recipe/animation, so clusters that can satisfy multiple recipes now craft only one per scan. If this isn’t intended, consider reintroducing a loop that continues trying other recipes while honoring `MaxCraftsPerClusterPerScan`, or restricting early returns only to cases where animation must be exclusive. If it is intended, please document how `MaxCraftsPerClusterPerScan` should now be interpreted given this changed behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/GroundCraft/GroundCraft.Crafting.cs Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些高层次的反馈:

  • 新的 _craftAnimations_lockedItemIndexes 集合在 OnGameUpdateOnGetData 中都会被修改,但没有任何同步机制;可以考虑使用一个专门的锁(或复用 _stateLock)来保护它们,或者使用并发集合,以避免游戏循环与网络数据钩子之间产生竞争条件。
  • 目前与 Zenith 相关的特定行为是通过 IsZenithRecipe(检查 recipe.OutputType == ItemID.Zenith)硬编码的;如果你预期之后会有其他配方需要相同的动画路径,把这部分逻辑迁移到配方的一个标记或配置字段中,可能会比依赖特定的物品 ID 更加清晰。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `_craftAnimations` and `_lockedItemIndexes` collections are mutated from both `OnGameUpdate` and `OnGetData` without any synchronization; consider guarding them with a dedicated lock (or reusing `_stateLock`) or using concurrent collections to avoid race conditions between the game loop and net data hooks.
- The Zenith-specific behavior is currently hard-coded via `IsZenithRecipe` (checking `recipe.OutputType == ItemID.Zenith`); if you anticipate other recipes needing the same animation path, it might be cleaner to move this to a recipe flag or configuration field rather than relying on a specific item ID.

## Individual Comments

### Comment 1
<location path="src/GroundCraft/GroundCraft.Animation.cs" line_range="168-155" />
<code_context>
+        NotifyNearby(target, animation.Recipe, animation.OutputStack, animation.ConsumedStacks);
+    }
+
+    private void CancelCraftAnimation(CraftAnimation animation)
+    {
+        foreach (AnimatedIngredient ingredient in animation.Ingredients)
+        {
+            _lockedItemIndexes.Remove(ingredient.Index);
+            if (ingredient.Index < 0 || ingredient.Index >= Main.item.Length)
+                continue;
+
+            WorldItem item = Main.item[ingredient.Index];
+            if (!item.active)
+                continue;
+
+            item.noGrabDelay = 0;
+            item.playerIndexTheItemIsReservedFor = 255;
+            item.velocity = Vector2.Zero;
+            SyncItem(ingredient.Index);
+            NetMessage.SendData(MessageID.ItemOwner, -1, -1, null, ingredient.Index);
+        }
+
+        SpawnDeferredLeftovers(animation);
+    }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Cancelling a craft animation appears to duplicate ingredient stacks.

`TryStartCraftAnimation` reduces each ingredient item’s `stack` to the consumed amount and stores the remainder in `DeferredLeftover`. On completion, `CompleteCraftAnimation` removes the ingredient items (`TurnToAir`) and spawns the leftovers, so total stacks stay correct.

`CancelCraftAnimation` instead leaves the reduced-stack items in the world (only unlocking/resetting them) and still calls `SpawnDeferredLeftovers`. So after cancellation you have both the consumed stacks and the leftovers, effectively duplicating items.

Cancellation should either restore the original stacks and skip spawning leftovers, or mirror completion by removing the reduced items and only spawning the appropriate stacks. Alternatively, defer any `stack` modification until completion so both completion and cancellation paths are simpler and non-duplicating.
</issue_to_address>

Sourcery 对开源项目免费——如果你喜欢我们的代码审查,请考虑分享它们 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The new _craftAnimations and _lockedItemIndexes collections are mutated from both OnGameUpdate and OnGetData without any synchronization; consider guarding them with a dedicated lock (or reusing _stateLock) or using concurrent collections to avoid race conditions between the game loop and net data hooks.
  • The Zenith-specific behavior is currently hard-coded via IsZenithRecipe (checking recipe.OutputType == ItemID.Zenith); if you anticipate other recipes needing the same animation path, it might be cleaner to move this to a recipe flag or configuration field rather than relying on a specific item ID.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `_craftAnimations` and `_lockedItemIndexes` collections are mutated from both `OnGameUpdate` and `OnGetData` without any synchronization; consider guarding them with a dedicated lock (or reusing `_stateLock`) or using concurrent collections to avoid race conditions between the game loop and net data hooks.
- The Zenith-specific behavior is currently hard-coded via `IsZenithRecipe` (checking `recipe.OutputType == ItemID.Zenith`); if you anticipate other recipes needing the same animation path, it might be cleaner to move this to a recipe flag or configuration field rather than relying on a specific item ID.

## Individual Comments

### Comment 1
<location path="src/GroundCraft/GroundCraft.Animation.cs" line_range="168-155" />
<code_context>
+        NotifyNearby(target, animation.Recipe, animation.OutputStack, animation.ConsumedStacks);
+    }
+
+    private void CancelCraftAnimation(CraftAnimation animation)
+    {
+        foreach (AnimatedIngredient ingredient in animation.Ingredients)
+        {
+            _lockedItemIndexes.Remove(ingredient.Index);
+            if (ingredient.Index < 0 || ingredient.Index >= Main.item.Length)
+                continue;
+
+            WorldItem item = Main.item[ingredient.Index];
+            if (!item.active)
+                continue;
+
+            item.noGrabDelay = 0;
+            item.playerIndexTheItemIsReservedFor = 255;
+            item.velocity = Vector2.Zero;
+            SyncItem(ingredient.Index);
+            NetMessage.SendData(MessageID.ItemOwner, -1, -1, null, ingredient.Index);
+        }
+
+        SpawnDeferredLeftovers(animation);
+    }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Cancelling a craft animation appears to duplicate ingredient stacks.

`TryStartCraftAnimation` reduces each ingredient item’s `stack` to the consumed amount and stores the remainder in `DeferredLeftover`. On completion, `CompleteCraftAnimation` removes the ingredient items (`TurnToAir`) and spawns the leftovers, so total stacks stay correct.

`CancelCraftAnimation` instead leaves the reduced-stack items in the world (only unlocking/resetting them) and still calls `SpawnDeferredLeftovers`. So after cancellation you have both the consumed stacks and the leftovers, effectively duplicating items.

Cancellation should either restore the original stacks and skip spawning leftovers, or mirror completion by removing the reduced items and only spawning the appropriate stacks. Alternatively, defer any `stack` modification until completion so both completion and cancellation paths are simpler and non-duplicating.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

_stableScans.Remove(ingredient.Index);
}

SpawnDeferredLeftovers(animation);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 取消合成动画时似乎会导致配方材料堆叠数量被复制。

TryStartCraftAnimation 会将每个材料物品的 stack 减少到被消耗的数量,并把多余的部分存放在 DeferredLeftover 中。完成时,CompleteCraftAnimation 会移除这些材料物品(TurnToAir),并生成这些剩余物品,所以总的堆叠数量是正确的。

CancelCraftAnimation 则是把减少过堆叠的物品保留在世界中(只解锁/重置它们),同时仍然调用 SpawnDeferredLeftovers。因此在取消之后,世界中会同时存在被消耗的堆叠和剩余的堆叠,相当于复制了物品。

取消操作要么应该恢复原始堆叠并跳过生成剩余物品,要么像完成路径那样移除减少后的物品,只生成对应数量的堆叠。另一种方案是将任何对 stack 的修改推迟到完成时再执行,这样完成和取消路径都会更简单,并且不会产生重复的物品。

Original comment in English

issue (bug_risk): Cancelling a craft animation appears to duplicate ingredient stacks.

TryStartCraftAnimation reduces each ingredient item’s stack to the consumed amount and stores the remainder in DeferredLeftover. On completion, CompleteCraftAnimation removes the ingredient items (TurnToAir) and spawns the leftovers, so total stacks stay correct.

CancelCraftAnimation instead leaves the reduced-stack items in the world (only unlocking/resetting them) and still calls SpawnDeferredLeftovers. So after cancellation you have both the consumed stacks and the leftovers, effectively duplicating items.

Cancellation should either restore the original stacks and skip spawning leftovers, or mirror completion by removing the reduced items and only spawning the appropriate stacks. Alternatively, defer any stack modification until completion so both completion and cancellation paths are simpler and non-duplicating.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the GroundCraft plugin with new craft animations (including a dedicated Zenith sequence), expands the built-in default recipe set, and tightens recipe triggering by requiring exact ingredient type matching by default.

Changes:

  • Added animated “consumed drop” crafting flow (spiral animation + Zenith finale/trail) and hooked NetGetData to keep animating items unpickable.
  • Enabled exact ingredient type matching by default and added runtime stats to track “extra type” rejects.
  • Expanded default recipe examples and updated documentation/manifest/plugin index for v1.1.0.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/GroundCraft/README.md Documents new defaults/options and adds v1.1.0 changelog entry (CN).
src/GroundCraft/README.en-US.md Documents new defaults/options and adds v1.1.0 changelog entry (EN).
src/GroundCraft/manifest.json Updates manifest descriptions to mention exact matching + spiral animations.
src/GroundCraft/GroundCraft.Models.cs Adds animation model types, new config flags/defaults, new runtime stat, and new default recipes.
src/GroundCraft/GroundCraft.Effects.cs Adds FX helpers for craft effect + Zenith finale.
src/GroundCraft/GroundCraft.cs Bumps plugin version to 1.1.0, adds animation constants/state, registers NetGetData hook, disposes animations.
src/GroundCraft/GroundCraft.Crafting.cs Integrates animation updates, exact-type matching, station center selection, and animation-based craft start path.
src/GroundCraft/GroundCraft.Commands.cs Extends /groundcraft info and audit output with new config/stats.
src/GroundCraft/GroundCraft.Animation.cs Implements animation lifecycle, item locking, trail, completion/cancel, and NetGetData reassertion.
src/GroundCraft/examples/config.example.json Updates example config with new defaults and new flags.
Plugins.json Adds/updates plugin index entry for GroundCraft v1.1.0.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/GroundCraft/GroundCraft.Models.cs
Comment thread src/GroundCraft/GroundCraft.Models.cs
Comment thread src/GroundCraft/GroundCraft.cs
@ACaiCat ACaiCat added this pull request to the merge queue Jun 30, 2026
Merged via the queue into UnrealMultiple:master with commit 44eb9d7 Jun 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants