Skip to content

Commit

Permalink
Unlock Castle Daggerfall doors after teleporting inside
Browse files Browse the repository at this point in the history
Fixes Interkarma#1584.
Tracks when player teleports into a dungeon using a flag set by Teleport effect.
If player clicks on the two magically held doors in Castle Daggerfall after teleporting inside then these doors will unlock and open.
Also works if player teleports from inside > inside, e.g. from foyer to anchor on other side of doors.
Flag is lowered anytime player leaves interiors or teleports to a non-dungeon anchor.
Test happens on every action click, but is very fast as using purely numerical comparisons without any scene searches.
I'm not entirely happy with this here, but there's no other more satisfying place I can think of to intercept this click and change behaviour. This sequence of events is driven purely by classic gamedata actions, it's not coded into the game logic itself. To change this requires intercepting the action logic for these specific door records.
Uses deterministic LoadID to identify DaggerfallAction on doors. This could theoretically change if player has non-standard gamedata, but then they can just click the guard through doors as per classic.
This case only gets special handling as Castle Daggerfall is central to main plot and this scenario happens frequently enough to be an issue. There are many other places player can trap themselves in dungeons using a poorly placed Recall spell. This is on the player to manage intelligently.
Limitation: This flag is not serialized, so if player exits game and starts up again before clicking on doors then "teleported into dungeon" flag is lost. But player can just anchor/teleport in place to trigger logic again, or resolve in some other way.
  • Loading branch information
Interkarma committed Apr 28, 2021
1 parent ce11e06 commit e81b2d7
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
Expand Up @@ -236,7 +236,10 @@ bool IsSameInterior()
DaggerfallConnect.Utility.DFPosition anchorMapPixel = DaggerfallConnect.Arena2.MapsFile.WorldCoordToMapPixel(anchorPosition.worldPosX, anchorPosition.worldPosZ);
DaggerfallConnect.Utility.DFPosition playerMapPixel = GameManager.Instance.PlayerGPS.CurrentMapPixel;
if (anchorMapPixel.X == playerMapPixel.X && anchorMapPixel.Y == playerMapPixel.Y)
{
GameManager.Instance.PlayerEnterExit.PlayerTeleportedIntoDungeon = true;
return true;
}
}

return false;
Expand All @@ -260,6 +263,9 @@ private void PlayerEnterExit_OnRespawnerComplete()
serializablePlayer.RestorePosition(anchorPosition);
PlayerEnterExit.OnRespawnerComplete -= PlayerEnterExit_OnRespawnerComplete;

// Set "teleported into dungeon" flag when anchor is inside a dungeon
GameManager.Instance.PlayerEnterExit.PlayerTeleportedIntoDungeon = anchorPosition.insideDungeon;

// Restore scene cache on arrival
if (!playerEnterExit.IsPlayerInside)
SaveLoadManager.RestoreCachedScene(GameManager.Instance.StreamingWorld.SceneName); // Player is outside
Expand Down
8 changes: 8 additions & 0 deletions Assets/Scripts/Game/PlayerEnterExit.cs
Expand Up @@ -223,6 +223,12 @@ public bool IsRespawning
get { return isRespawning; }
}

/// <summary>
/// True when player just teleported into a dungeon via Teleport spell, otherwise false.
/// Flag is only raised by Teleport spell and is lowered any time player exits a dungeon or interior, or teleports to a non-dungeon anchor.
/// </summary>
public bool PlayerTeleportedIntoDungeon { get; set; }

/// <summary>
/// Gets current player dungeon.
/// Only valid when player is inside a dungeon.
Expand Down Expand Up @@ -856,6 +862,7 @@ private void BuildingTransitionExteriorLogic()

// Player is now outside building
isPlayerInside = false;
PlayerTeleportedIntoDungeon = false;
buildingType = DFLocation.BuildingTypes.None;
factionID = 0;

Expand Down Expand Up @@ -1175,6 +1182,7 @@ private void DungeonTransitionExteriorLogic()
isPlayerInsideDungeonCastle = false;
lastPlayerDungeonBlockIndex = -1;
playerDungeonBlockData = new DFLocation.DungeonBlock();
PlayerTeleportedIntoDungeon = false;

// Position player to door
world.SetAutoReposition(StreamingWorld.RepositionMethods.DungeonEntrance, Vector3.zero);
Expand Down
29 changes: 29 additions & 0 deletions Assets/Scripts/Internal/DaggerfallAction.cs
Expand Up @@ -174,6 +174,12 @@ public void Receive(GameObject prev = null, TriggerTypes triggerType = TriggerTy
if (IsPlaying())
return;

// Special handling for magically locked foyer doors in Castle Daggerfall
// Allows player to just open doors after teleporting inside and finding themselves locked in
// Not entirely happy with check here, but there's no really satisfying way to intercept and change action behavior directly on these doors
// Check is fast using direct numeric tests for player location and doors
CastleDaggerfallMagicDoorsSpecialOpenHack();

//assume actions triggered by other action objects are always valid,
//otherwise make sure trigger type is valid for this action
if(triggerType != TriggerTypes.ActionObject)
Expand Down Expand Up @@ -245,6 +251,29 @@ public void Receive(GameObject prev = null, TriggerTypes triggerType = TriggerTy
return;
}

void CastleDaggerfallMagicDoorsSpecialOpenHack()
{
// Execute only when player recently teleported inside Castle Daggerfall dungeon and clicks on either magically held door in foyer
// Uses LoadID to identify doors which is based on unique position in gamedata and always the same
// Theoretically could break if player has non-standard gamedata, but they can still speak with guard through crack in door as per classic
// This check is very fast and doesn't require any scene searches, just numerical comparisons
if (GameManager.Instance.PlayerEnterExit.PlayerTeleportedIntoDungeon &&
GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeon &&
GameManager.Instance.PlayerGPS.CurrentLocation.MapTableData.MapId == 1291010263 &&
(loadID == 29331574 || loadID == 29331622))
{
// If door is still locked and closed then unlock and open doors
// Player still sees "this is a magically held lock" but door will open anyway
// The purpose of this change is just to prevent player being locked inside throne room under special circumstances
DaggerfallActionDoor door = GetComponent<DaggerfallActionDoor>();
if (door && door.IsLocked && door.IsClosed)
{
door.CurrentLockValue = 0;
door.ToggleDoor();
}
}
}

public void Play(GameObject prev)
{
ActionDelegate d = null;
Expand Down

0 comments on commit e81b2d7

Please sign in to comment.