diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 089405d89..a146e3226 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -1419,16 +1419,13 @@ void AddSpectralArrow(Missile &missile, AddMissileParameter ¶meter) missile.var3 = av; } -void AddWarp(Missile &missile, AddMissileParameter & /*parameter*/) +void AddWarp(Missile &missile, AddMissileParameter ¶meter) { int minDistanceSq = std::numeric_limits::max(); - Point src = missile.position.start; - Point tile = src; - MissileSource missileSource = missile.sourceType(); - if (missileSource == MissileSource::Player) { - tile = missile.sourcePlayer()->position.tile; - } + int id = missile._misource; + Player &player = Players[id]; + Point tile = player.position.tile; for (int i = 0; i < numtrigs && i < MAXTRIGGERS; i++) { TriggerStruct *trg = &trigs[i]; @@ -1470,7 +1467,7 @@ void AddWarp(Missile &missile, AddMissileParameter & /*parameter*/) }; Displacement triggerOffset = getTriggerOffset(trg); candidate += triggerOffset; - Displacement off = src - candidate; + Displacement off = player.position.tile - candidate; int distanceSq = off.deltaY * off.deltaY + off.deltaX * off.deltaX; if (distanceSq < minDistanceSq) { minDistanceSq = distanceSq; @@ -1478,7 +1475,23 @@ void AddWarp(Missile &missile, AddMissileParameter & /*parameter*/) } } missile._mirange = 2; - missile.position.tile = tile; + std::optional teleportDestination = FindClosestValidPosition( + [&player](Point target) { + for (int i = 0; i < numtrigs; i++) { + if (trigs[i].position == target) + return false; + } + return PosOkPlayer(player, target); + }, + tile, 0, 5); + + if (teleportDestination) { + missile.position.tile = *teleportDestination; + } else { + // No valid teleport destination found + missile._miDelFlag = true; + parameter.spellFizzled = true; + } } void AddLightningWall(Missile &missile, AddMissileParameter ¶meter) @@ -3612,9 +3625,18 @@ void ProcessTeleport(Missile &missile) int id = missile._misource; Player &player = Players[id]; + std::optional teleportDestination = FindClosestValidPosition( + [&player](Point target) { + return PosOkPlayer(player, target); + }, + missile.position.tile, 0, 5); + + if (!teleportDestination) + return; + dPlayer[player.position.tile.x][player.position.tile.y] = 0; PlrClrTrans(player.position.tile); - player.position.tile = missile.position.tile; + player.position.tile = *teleportDestination; player.position.future = player.position.tile; player.position.old = player.position.tile; PlrDoTrans(player.position.tile);