fix(teleport): preserve dungeon cell identity on respawn (#215)

Classify teleport landblock transitions from the source and destination Position cell IDs instead of render-space coordinates. This keeps same-dungeon death respawns on their resident floor physics while preserving the existing true cross-landblock streaming path.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-13 20:08:21 +02:00
parent b0c175afc0
commit 0ad6700a07
7 changed files with 296 additions and 9 deletions

View file

@ -0,0 +1,42 @@
namespace AcDream.App.Streaming;
/// <summary>
/// Classifies whether a local-player teleport crosses an AC landblock boundary.
/// Cell identity comes from the complete retail <c>Position</c>, never from its
/// frame coordinates: dungeon EnvCells may have valid negative local origins.
/// </summary>
/// <remarks>
/// Retail carries <c>Position::objcell_id</c> through
/// <c>SmartBox::TeleportPlayer</c> (0x00453910) into
/// <c>CPhysicsObj::SetPositionSimple</c> (0x005162B0). acdream's asynchronous
/// streaming layer needs this extra comparison, but preserves that identity rule.
/// See <c>docs/research/2026-07-13-same-dungeon-respawn-landblock-identity-pseudocode.md</c>.
/// </remarks>
internal readonly record struct TeleportLandblockTransition(
uint SourceLandblockId,
uint DestinationLandblockId)
{
public bool CrossesLandblock => SourceLandblockId != DestinationLandblockId;
/// <summary>
/// Build the transition from the player's current cell and the received
/// destination cell. A zero source can occur before initial placement; in
/// that case the current streaming center is the only valid identity fallback.
/// </summary>
public static TeleportLandblockTransition Classify(
uint sourceCellId,
uint destinationCellId,
uint currentStreamingCenterLandblockId)
{
uint sourceLandblockId = sourceCellId != 0
? NormalizeLandblockId(sourceCellId)
: NormalizeLandblockId(currentStreamingCenterLandblockId);
return new TeleportLandblockTransition(
sourceLandblockId,
NormalizeLandblockId(destinationCellId));
}
private static uint NormalizeLandblockId(uint cellOrLandblockId)
=> (cellOrLandblockId & 0xFFFF0000u) | 0xFFFFu;
}