Skip to content

feat(Yggdrasil): multiple content - #406

Open
Omnielement wants to merge 172 commits into
masterfrom
Yggdrasil/newContent0
Open

feat(Yggdrasil): multiple content#406
Omnielement wants to merge 172 commits into
masterfrom
Yggdrasil/newContent0

Conversation

@Omnielement

Copy link
Copy Markdown
Collaborator

Many many new contents.

Omnielement and others added 30 commits September 8, 2025 23:19
@CloudeaSoft CloudeaSoft changed the title Yggdrasil/new content0 feat(Yggdrasil): multiple content Sep 10, 2026
@CycloneClub CycloneClub deleted a comment from cursor Bot Sep 10, 2026

@CloudeaSoft CloudeaSoft left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

建议修改后再合并(Request changes)。

审查基于提交 5a9646b,相对基准 f222f93。本 review 汇总两轮审查的 24 项独立问题(6 项 P1、18 项 P2),包含此前单独发布的 6 条局部问题。

应优先处理客户端/服务端 Boss 初始化和死亡回调、箱体覆盖、熔炉商店越界索引以及藤蔓重复占用异常;其余意见覆盖内容与积分兼容、多人同步、房间切换、跨世界状态、武器行为和世界生成边界。每条行内意见均包含触发条件、实际影响、验证依据或限制,以及修复方向。

验证记录:

  • 此提交此前执行 dotnet build /p:WarningLevel=0 成功:0 错误、7 条与 shader 输出文件占用相关的警告。
  • dotnet test --no-build --no-restore --verbosity normal /p:WarningLevel=0:309/309 通过。
  • 按仓库要求执行字节级 UTF-8 BOM 检查,通过。
  • 使用已编译程序集建立最小内存场景,复现旧积分归零、Boss 接收端空引用和死亡回调异常、攻击初始化差异、错误脱战、形状覆盖/越界、藤蔓重复占用与跨世界残留,以及墙体和几何算法错误。

未启动完整专用服务器、双客户端会话、图形游戏循环或世界生成种子回放;静态调用链结论与方法复现已分别标注。现有单元测试通过不代表这些运行路径已通过验证。

Comment on lines +109 to +112
AICoroutine.Update();
RealCenter += NPC.velocity;
BodyRope.Masses[0].Position = RealCenter;
BodyRope.ApplyForce_VelocityDecay(0.2f);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] VampireMat 接收端首次 AI 会发生空引用

联机客户端收到 Boss 后,BodyRope 仍为 null:唯一赋值位于 OnSpawn,而该回调只在单机或服务端执行;AI 却直接访问 BodyRope.Masses。已使用本次构建的程序集创建接收端初始化状态,调用 AI 后在第 111 行得到 NullReferenceException。客户端需要在首次使用前初始化绳体,并单独同步战斗状态。

验证:编译后方法最小复现;未运行真实多人会话。回调语义见 tML ModNPC 文档

Comment on lines +29 to +31
pWST.PlaceAtTileObjectDataOrigin(x, y);
Item.stack--;
return false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] 放置墩台可以覆盖装有物品的箱子

CanUseItem 在检查占用和锚点之前直接调用形状写入方法,再扣除物品并返回 false。该方法直接替换 TileType,不经过普通放置的保护。将鼠标对准箱子左上角时,13×8 形状的相应四个像素均为实心,会覆盖箱子四格,使内容无法通过箱子正常取出。应先验证整个形状的可放置性,再在成功放置后扣除物品。

验证:用真实形状 BMP 和已编译放置方法建立内存场景,箱子四格从类型 21 被替换为合成目标类型 0,Chest 记录及其中 7 个物品仍存在,但箱体已被覆盖。没有操作真实世界存档。

Comment on lines +292 to +294
if (CurrentSpecialShop >= 0 && Main.npcShop == 0)
{
Main.npcShop = 65536 + CurrentSpecialShop;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] 与 NPC 交谈时打开熔炉商店会使用越界索引

与附近 NPC 保持对话、背包打开时再打开熔炉积分商店,会把 npcShop 设成 65536。CanInteract 没有排除对话状态,也没有关闭对话。本机 tML 的 DrawInventory 在 talkNPC != -1 时不会清掉该值,随后用它访问 shop[npcShop],造成 IndexOutOfRangeException。应使用明确的 UI 隐藏/替换逻辑,避免把无效下标传入原版绘制。

验证:已核对本机 tModLoader.dll 的 npcShop setter 及 DrawInventory IL:IL_33df 起的判断、IL_3623–IL_362d 的数组访问;未执行图形绘制。

Comment on lines +395 to +397
hTP.SwitchVineCoolTimer = 30;
hTP.Grasping = true;
RopeGraspingPlayer.Add(tilePos, player);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] 第二名玩家抓住已占用藤蔓会触发重复键异常

第一名玩家抓住藤蔓后,第二名玩家在范围内按上,GraspHangingTile 仅排除第二名玩家已抓住其他藤蔓、以及藤蔓正在调节的情况,没有排除该藤蔓已被占用。此处对同一 tilePos 再次 Dictionary.Add 将抛出 ArgumentException,且此前已将玩家设为 Grasping。切换藤蔓的路径也缺少目标占用检查。应先通过 TryAdd 成功占用,再修改玩家状态。

验证:调用已编译的 AddPlayerToRope,两名玩家依次抓取同一坐标,第二次调用在第 397 行抛出 ArgumentException;占用表只有 1 项,两名玩家的 Grasping 却均为 True。未运行完整双人会话。

Comment on lines +208 to +210
if (i > Main.maxTilesX - TotalWidth || i < 0 || j > Main.maxTilesY || j - TotalHeight < 0)
{
return;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 形状放置的边界校验没有计入原点偏移

检查鼠标坐标后才减去 TileObjectData.Origin,检查的并非最终写入矩形。例如宽 13、原点 X=6 的墩台在 i=5 时通过检查,之后将访问负的 X;底边也只检查 j 是否超过地图,没有预留形状高度。放置道具绕过普通放置流程,因此可到达这些写入。应先算左上角,再检查完整矩形处于地图范围内。

验证:用宽 13、Origin.X=6 的真实放置方法,在 i=5 时于第 225 行复现 IndexOutOfRangeException;这是内存 Tilemap 的方法复现,未操作真实世界。

Comment on lines +2149 to +2150
bounds.Z = bounds.Z - bounds.Z % 16 - 8;
bounds.W = bounds.W - bounds.W % 16 - 8;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 最大边界漏掉最后一行和最后一列格子

最大 X/Y 向下对齐后再减 8,会把最右、最下的格子中心排除在循环上界外。传入 tile 顶点 (100,100)、(102,100)、(102,102)、(100,102),当前编译后函数只返回 X/Y 为 100 或 101 的 4 格;MathUtils.IsPointInPolygon 明确包含边界,应包含 9 格。开发工具多边形选择也直接调用这里。需要修正这两行对最大边界的对齐,使最后一行/列的格子中心能够进入包含判断。

Comment on lines 197 to +200
if (CurrentPlayer != null)
{
FurnacePlayer fPlayer = CurrentPlayer.GetModPlayer<FurnacePlayer>();
CurrentScore = fPlayer.FurnaceScore;
CurrentScore = fPlayer.TotalFurnaceScore;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 熔炉保留上一角色的 Player 引用,后续积分归属错误

本 PR 删除了每帧把 CurrentPlayer 更新为 Main.LocalPlayer 的逻辑,却没有在世界卸载/加载时清理该静态引用。角色 A 站在地磅上退出,再用角色 B 进入且尚未踩秤时,CurrentPlayer 仍指向 A;地磅离开检测也只清理等于当前本地玩家的引用。MeltingButton.MeltDown 第 109–112 行将积分加给该旧对象,因此新角色的物品可被熔掉而积分未进入其存档。

验证:用两个内存 Player 执行系统 OnWorldUnload、OnWorldLoad、PostUpdateEverything,得到 OldPlayerRetained=True、NewPlayerIsLocal=True、DisplayedScore=25000。静态调用链确认积分写入目标;没有执行会消耗实际物品的完整熔炼流程。

修复方向:在世界生命周期清理 CurrentPlayer 及对应 UI 状态,熔炼前重新验证当前角色及有效地磅占用者。

/// Be valid only when CanGrasp is true.<br></br>
/// 1 rope can be grasped by only 1 player.
/// </summary>
public static Dictionary<Point, Player> RopeGraspingPlayer = new Dictionary<Point, Player>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 抓藤蔓的静态占用状态跨世界残留

新增 RopeGraspingPlayer 保存世界坐标和 Player 引用,但 OnWorldUnload/OnWorldLoad 只清空各 tile 的绳体字典,没有清除占用表和 HangingTile_Player.Grasping。玩家抓住藤蔓时切换子世界并复用 Player 后,ContainsValue(Player) 会阻止其抓新藤蔓;旧世界的坐标键也可能与新世界绳体冲突。

验证:实际 AddPlayerToRope 后执行两个世界回调,结果 AfterWorldReloadEntries=1、P1Grasping=True。相关入口判断位于第 1377 行。此处复现的是清理回调缺失,未启动完整子世界过渡。

修复方向:把占用表、玩家抓取状态和世界绳体一起在切换时清理;注意模组卸载和玩家离开。

}
}
}
List<int> noSolidMazeTile = WaterDeliveryHoleTiles;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 复制列表,避免把装饰物加入传送口集合

这里是引用赋值,后续对 noSolidMazeTile 的 16 次 Add() 也会修改 WaterDeliveryHoleTiles。后面 VampireMatCave()WaterDeliveryHoleTiles.Contains(tile.TileType) 判断房间是否已有传送口,于是氧气罐、绿藻等装饰也会让 hasDeliveryHole 成为 true,跳过补建连接。这里应创建独立列表,例如 new List<int>(WaterDeliveryHoleTiles)

for (int i = 0; i < 200; i++)
{
Vector2 pos = new Vector2(Main.rand.Next(40, Main.maxTilesX / 2 + 50), Main.rand.Next(Main.maxTilesY - 700, Main.maxTilesY - 20));
FillLiquid(pos);
FillLiquid(pos.ToTileCoordinates());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 补水位置已经是 tile 坐标,不应再次除以 16

上一行直接按 Main.maxTilesX/maxTilesY 抽样,所以 pos 的数值已经是 tile 坐标。ToTileCoordinates() 又除以 16,例如 (1000,20500) 会变成 (62,1281),使这 200 次补水偏离底部矿道。当前 FillLiquid(Point) 直接使用传入点;原来的 FillLiquid(Vector2) 也是使用 center.ToPoint()。此处应改为 FillLiquid(pos.ToPoint())

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.

7 participants