From dc64a928bfb29da054d0276d0c84c9001630e050 Mon Sep 17 00:00:00 2001 From: numidium Date: Sun, 16 Oct 2022 21:49:08 -0400 Subject: [PATCH 1/7] Fix exceptions when bashing certain doors Leverages ActivateStaticDoor when bashing static doors so all door types are handled. Currently, only exterior doors in settlements are handled. This mimics classic behavior with the exception that dungeon exterior doors can be bashed too. --- Assets/Scripts/Game/PlayerActivate.cs | 183 ++++++++++++++------------ Assets/Scripts/Game/WeaponManager.cs | 4 +- 2 files changed, 102 insertions(+), 85 deletions(-) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index c673ba319d..4791c7ad11 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -490,7 +490,8 @@ void ActivateStaticDoor( DFLocation.BuildingTypes buildingType, bool buildingUnlocked, int buildingLockValue, - Transform doorOwner) + Transform doorOwner, + bool isBash = false) { StaticDoor door; if (CustomDoor.HasHit(hit, out door) || (doors && doors.HasHit(hit.point, out door))) @@ -502,70 +503,92 @@ void ActivateStaticDoor( return; } + // Play sound when bashing. + if (isBash && door.doorType != DoorTypes.DungeonExit) + if (TryGetComponent(out var dfAudioSource)) + dfAudioSource.PlayOneShot(SoundClips.PlayerDoorBash); + if (door.doorType == DoorTypes.Building && !playerEnterExit.IsPlayerInside) { // Discover building GameManager.Instance.PlayerGPS.DiscoverBuilding(building.buildingKey); // Handle clicking exterior door with Open spell active - if (HandleOpenEffectOnExteriorDoor(buildingLockValue)) + if (HandleOpenEffectOnExteriorDoor(buildingLockValue) && !isBash) buildingUnlocked = true; // Handle locked buildings if (!buildingUnlocked) { - if (currentMode != PlayerActivateModes.Steal) + if (!isBash) { - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockedExteriorDoor")); - LookAtInteriorLock(buildingLockValue); - return; - } - else // Breaking into building - { - // Reject if player has already failed this building at current skill level - PlayerEntity player = GameManager.Instance.PlayerEntity; - int skillValue = player.Skills.GetLiveSkillValue(DFCareer.Skills.Lockpicking); - int lastAttempt = GameManager.Instance.PlayerGPS.GetLastLockpickAttempt(building.buildingKey); - if (skillValue <= lastAttempt) + if (currentMode != PlayerActivateModes.Steal) { + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockedExteriorDoor")); LookAtInteriorLock(buildingLockValue); return; } - - // Attempt to unlock building - Random.InitState(Time.frameCount); - player.TallySkill(DFCareer.Skills.Lockpicking, 1); - int chance = FormulaHelper.CalculateExteriorLockpickingChance(buildingLockValue, skillValue); - int roll = Random.Range(1, 101); - Debug.LogFormat("Attempting pick against lock strength {0}. Chance={1}, Roll={2}.", buildingLockValue, chance, roll); - if (chance > roll) + else // Breaking into building { - // Show success and play unlock sound - player.TallyCrimeGuildRequirements(true, 1); - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingSuccess")); - DaggerfallAudioSource dfAudioSource = GetComponent(); - if (dfAudioSource != null) - dfAudioSource.PlayOneShot(SoundClips.ActivateLockUnlock); - } - else - { - // Show failure and record attempt skill level in discovery data - // Have not been able to create a guard response in classic, even when early morning NPCs are nearby - // Assuming for now that exterior lockpicking is discrete enough that no response on failure is required - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingFailure")); - GameManager.Instance.PlayerGPS.SetLastLockpickAttempt(building.buildingKey, skillValue); - return; + // Reject if player has already failed this building at current skill level + PlayerEntity player = GameManager.Instance.PlayerEntity; + int skillValue = player.Skills.GetLiveSkillValue(DFCareer.Skills.Lockpicking); + int lastAttempt = GameManager.Instance.PlayerGPS.GetLastLockpickAttempt(building.buildingKey); + if (skillValue <= lastAttempt) + { + LookAtInteriorLock(buildingLockValue); + return; + } + + // Attempt to unlock building + Random.InitState(Time.frameCount); + player.TallySkill(DFCareer.Skills.Lockpicking, 1); + int chance = FormulaHelper.CalculateExteriorLockpickingChance(buildingLockValue, skillValue); + int roll = Random.Range(1, 101); + Debug.LogFormat("Attempting pick against lock strength {0}. Chance={1}, Roll={2}.", buildingLockValue, chance, roll); + if (chance > roll) + { + // Show success and play unlock sound + player.TallyCrimeGuildRequirements(true, 1); + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingSuccess")); + DaggerfallAudioSource dfAudioSource = GetComponent(); + if (dfAudioSource != null) + dfAudioSource.PlayOneShot(SoundClips.ActivateLockUnlock); + } + else + { + // Show failure and record attempt skill level in discovery data + // Have not been able to create a guard response in classic, even when early morning NPCs are nearby + // Assuming for now that exterior lockpicking is discrete enough that no response on failure is required + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingFailure")); + GameManager.Instance.PlayerGPS.SetLastLockpickAttempt(building.buildingKey, skillValue); + return; + } } } } + // Attempt to bash the door. Classic makes a roll whether it is locked or not. + if (isBash && Dice100.FailedRoll(25 - buildingLockValue)) + { + // 10% chance that you are noticed. + if (Dice100.SuccessRoll(10)) + { + PlayerEntity player = GameManager.Instance.PlayerEntity; + player.CrimeCommitted = PlayerEntity.Crimes.Attempted_Breaking_And_Entering; + player.SpawnCityGuards(true); + } + + return; + } + // If entering a shop let player know the quality level // If entering an open home, show greeting if (hitBuilding && buildingGreetingsEnabled) { const int houseGreetingsTextId = 256; - DaggerfallMessageBox mb; + DaggerfallMessageBox mb = null; PlayerGPS.DiscoveredBuilding buildingData; GameManager.Instance.PlayerGPS.GetDiscoveredBuilding(building.buildingKey, out buildingData); @@ -577,8 +600,11 @@ void ActivateStaticDoor( buildingData.factionID != (int)FactionFile.FactionIDs.The_Dark_Brotherhood && !DaggerfallBankManager.IsHouseOwned(building.buildingKey)) { - string greetingText = DaggerfallUnity.Instance.TextProvider.GetRandomText(houseGreetingsTextId); - mb = DaggerfallUI.MessageBox(greetingText); + if (!isBash) // Residents don't greet you when you kick in their door. + { + string greetingText = DaggerfallUnity.Instance.TextProvider.GetRandomText(houseGreetingsTextId); + mb = DaggerfallUI.MessageBox(greetingText); + } } else mb = PresentShopQuality(building); @@ -591,6 +617,14 @@ void ActivateStaticDoor( mb.OnClose += BuildingGreetingPopup_OnClose; return; } + + // Bashing open an unlocked door potentially alerts the guards. + if (isBash && Dice100.SuccessRoll(10)) + { + PlayerEntity player = GameManager.Instance.PlayerEntity; + player.CrimeCommitted = PlayerEntity.Crimes.Breaking_And_Entering; + player.SpawnCityGuards(true); + } } // Hit door while outside, transition inside @@ -614,6 +648,8 @@ void ActivateStaticDoor( } else if (door.doorType == DoorTypes.DungeonExit && playerEnterExit.IsPlayerInside) { + if (isBash) + return; // Hit dungeon exit while inside, ask if access wagon or transition outside if (GameManager.Instance.PlayerEntity.Items.Contains(ItemGroups.Transportation, (int)Transportation.Small_cart) && DaggerfallUnity.Settings.DungeonExitWagonPrompt) { @@ -1017,52 +1053,33 @@ public void SetClickDelay(float delay = 0.3f) clickDelayStartTime = Time.realtimeSinceStartup; } - public bool AttemptExteriorDoorBash(RaycastHit hit) + public bool AttemptStaticDoorBash(RaycastHit hit) { - Transform doorOwner; - DaggerfallStaticDoors doors = GetDoors(hit.transform, out doorOwner); - StaticDoor door; - if (CustomDoor.HasHit(hit, out door) || (doors && doors.HasHit(hit.point, out door))) + var doors = GetDoors(hit.transform, out Transform doorOwner); + if (!doors || !doors.HasHit(hit.point, out var door) || !playerEnterExit) + return false; + var hitBuilding = false; + var buildingType = DFLocation.BuildingTypes.AllValid; + var buildingUnlocked = false; + var buildingLockValue = 0; + var building = new StaticBuilding(); + DaggerfallStaticBuildings buildings = GetBuildings(hit.transform, out Transform buildingOwner); + if (buildings && buildings.HasHit(hit.point, out building)) { - // Discover building - this is needed to check lock level and transition to interior - GameManager.Instance.PlayerGPS.DiscoverBuilding(door.buildingKey); - - // Play bashing sound - DaggerfallAudioSource dfAudioSource = GetComponent(); - if (dfAudioSource != null) - dfAudioSource.PlayOneShot(SoundClips.PlayerDoorBash); - - // Get lock value from discovered building - int lockValue = 0; - PlayerGPS.DiscoveredBuilding discoveredBuilding; - if (GameManager.Instance.PlayerGPS.GetDiscoveredBuilding(door.buildingKey, out discoveredBuilding)) - lockValue = GetBuildingLockValue(discoveredBuilding.quality); - - // Roll for chance to open - Lower lock values have a higher chance - PlayerEntity playerEntity = GameManager.Instance.PlayerEntity; - Random.InitState(Time.frameCount); - int chance = 25 - lockValue; - if (Dice100.SuccessRoll(chance)) - { - // Success - player has forced their way into building - if (Dice100.SuccessRoll(10)) // 10% chance someone saw you breaking in, as with Attempted - playerEntity.CrimeCommitted = PlayerEntity.Crimes.Breaking_And_Entering; - playerEntity.TallyCrimeGuildRequirements(true, 1); - TransitionInterior(doorOwner, door, true); - return true; - } - else - { - // Bashing doors in cities is a crime - 10% chance of summoning guards on each failed bash attempt - if (Dice100.SuccessRoll(10)) - { - Debug.Log("Breaking and entering detected - spawning city guards."); - playerEntity.CrimeCommitted = PlayerEntity.Crimes.Attempted_Breaking_And_Entering; - playerEntity.SpawnCityGuards(true); - } - } + var buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory(); + if (!buildingDirectory) + return false; + if (!buildingDirectory.GetBuildingSummary(building.buildingKey, out BuildingSummary buildingSummary)) + return false; + + buildingUnlocked = BuildingIsUnlocked(buildingSummary); + buildingLockValue = GetBuildingLockValue(buildingSummary); + buildingType = buildingSummary.BuildingType; + hitBuilding = true; } - return false; + + ActivateStaticDoor(doors, hit, hitBuilding, building, buildingType, buildingUnlocked, buildingLockValue, doorOwner, true); + return door.doorType != DoorTypes.DungeonExit; // Dungeon exits should not respond to bashes. } public void PrivateProperty_OnButtonClick(DaggerfallMessageBox sender, DaggerfallMessageBox.MessageBoxButtons messageBoxButton) diff --git a/Assets/Scripts/Game/WeaponManager.cs b/Assets/Scripts/Game/WeaponManager.cs index e88797cb5a..aa97c8ca7e 100644 --- a/Assets/Scripts/Game/WeaponManager.cs +++ b/Assets/Scripts/Game/WeaponManager.cs @@ -467,8 +467,8 @@ public bool WeaponEnvDamage(DaggerfallUnityItem strikingWeapon, RaycastHit hit) return true; } - // Check if player hit a static exterior door - if (GameManager.Instance.PlayerActivate.AttemptExteriorDoorBash(hit)) + // Check if player hit a static door + if (GameManager.Instance.PlayerActivate.AttemptStaticDoorBash(hit)) { return true; } From aec077e1a524dec7aa24f1c671e824894d993e8b Mon Sep 17 00:00:00 2001 From: numidium Date: Mon, 17 Oct 2022 13:26:58 -0400 Subject: [PATCH 2/7] Remove redundant guard alert check --- Assets/Scripts/Game/PlayerActivate.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index 4791c7ad11..12ac4ca7e2 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -617,14 +617,6 @@ void ActivateStaticDoor( mb.OnClose += BuildingGreetingPopup_OnClose; return; } - - // Bashing open an unlocked door potentially alerts the guards. - if (isBash && Dice100.SuccessRoll(10)) - { - PlayerEntity player = GameManager.Instance.PlayerEntity; - player.CrimeCommitted = PlayerEntity.Crimes.Breaking_And_Entering; - player.SpawnCityGuards(true); - } } // Hit door while outside, transition inside From cba4f597c837fa650ffcd3e2f6052609591fe424 Mon Sep 17 00:00:00 2001 From: numidium Date: Mon, 17 Oct 2022 13:29:44 -0400 Subject: [PATCH 3/7] Revert "Remove redundant guard alert check" This reverts commit aec077e1a524dec7aa24f1c671e824894d993e8b. --- Assets/Scripts/Game/PlayerActivate.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index 12ac4ca7e2..4791c7ad11 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -617,6 +617,14 @@ void ActivateStaticDoor( mb.OnClose += BuildingGreetingPopup_OnClose; return; } + + // Bashing open an unlocked door potentially alerts the guards. + if (isBash && Dice100.SuccessRoll(10)) + { + PlayerEntity player = GameManager.Instance.PlayerEntity; + player.CrimeCommitted = PlayerEntity.Crimes.Breaking_And_Entering; + player.SpawnCityGuards(true); + } } // Hit door while outside, transition inside From 627b16645b64fef79a922717aa0de0b50c4659ae Mon Sep 17 00:00:00 2001 From: numidium Date: Tue, 6 Feb 2024 21:32:58 -0500 Subject: [PATCH 4/7] Undo renaming public method --- Assets/Scripts/Game/PlayerActivate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index 4791c7ad11..231dbf7c3a 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -1053,7 +1053,7 @@ public void SetClickDelay(float delay = 0.3f) clickDelayStartTime = Time.realtimeSinceStartup; } - public bool AttemptStaticDoorBash(RaycastHit hit) + public bool AttemptExteriorDoorBash(RaycastHit hit) { var doors = GetDoors(hit.transform, out Transform doorOwner); if (!doors || !doors.HasHit(hit.point, out var door) || !playerEnterExit) From 758b37ac5fcb1593940efe7fa428e10373e259d4 Mon Sep 17 00:00:00 2001 From: numidium Date: Sat, 10 Feb 2024 19:53:22 -0500 Subject: [PATCH 5/7] Update usage of method --- Assets/Scripts/Game/WeaponManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Game/WeaponManager.cs b/Assets/Scripts/Game/WeaponManager.cs index aa97c8ca7e..61b355197b 100644 --- a/Assets/Scripts/Game/WeaponManager.cs +++ b/Assets/Scripts/Game/WeaponManager.cs @@ -468,7 +468,7 @@ public bool WeaponEnvDamage(DaggerfallUnityItem strikingWeapon, RaycastHit hit) } // Check if player hit a static door - if (GameManager.Instance.PlayerActivate.AttemptStaticDoorBash(hit)) + if (GameManager.Instance.PlayerActivate.AttemptExteriorDoorBash(hit)) { return true; } From f76bd8ce5cdf2c538b8ed364975c74982bfad8fe Mon Sep 17 00:00:00 2001 From: numidium Date: Thu, 15 Feb 2024 21:10:38 -0500 Subject: [PATCH 6/7] Remove greeting when bashing door, refactor --- Assets/Scripts/Game/PlayerActivate.cs | 83 +++++++++++++-------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index 231dbf7c3a..94fb644be5 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -514,56 +514,55 @@ void ActivateStaticDoor( GameManager.Instance.PlayerGPS.DiscoverBuilding(building.buildingKey); // Handle clicking exterior door with Open spell active - if (HandleOpenEffectOnExteriorDoor(buildingLockValue) && !isBash) - buildingUnlocked = true; + var isBrokenIn = isBash; // Breaking in can be done via unlocking or bashing. + if (!buildingUnlocked && !isBash && HandleOpenEffectOnExteriorDoor(buildingLockValue)) + buildingUnlocked = isBrokenIn = true; // Handle locked buildings - if (!buildingUnlocked) + if (!buildingUnlocked && !isBash) { - if (!isBash) + if (currentMode != PlayerActivateModes.Steal) { - if (currentMode != PlayerActivateModes.Steal) + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockedExteriorDoor")); + LookAtInteriorLock(buildingLockValue); + return; + } + else // Breaking into building + { + // Reject if player has already failed this building at current skill level + PlayerEntity player = GameManager.Instance.PlayerEntity; + int skillValue = player.Skills.GetLiveSkillValue(DFCareer.Skills.Lockpicking); + int lastAttempt = GameManager.Instance.PlayerGPS.GetLastLockpickAttempt(building.buildingKey); + if (skillValue <= lastAttempt) { - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockedExteriorDoor")); LookAtInteriorLock(buildingLockValue); return; } - else // Breaking into building + + // Attempt to unlock building + Random.InitState(Time.frameCount); + player.TallySkill(DFCareer.Skills.Lockpicking, 1); + int chance = FormulaHelper.CalculateExteriorLockpickingChance(buildingLockValue, skillValue); + int roll = Random.Range(1, 101); + Debug.LogFormat("Attempting pick against lock strength {0}. Chance={1}, Roll={2}.", buildingLockValue, chance, roll); + if (chance > roll) + { + // Show success and play unlock sound + player.TallyCrimeGuildRequirements(true, 1); + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingSuccess")); + DaggerfallAudioSource dfAudioSource = GetComponent(); + if (dfAudioSource != null) + dfAudioSource.PlayOneShot(SoundClips.ActivateLockUnlock); + isBrokenIn = true; + } + else { - // Reject if player has already failed this building at current skill level - PlayerEntity player = GameManager.Instance.PlayerEntity; - int skillValue = player.Skills.GetLiveSkillValue(DFCareer.Skills.Lockpicking); - int lastAttempt = GameManager.Instance.PlayerGPS.GetLastLockpickAttempt(building.buildingKey); - if (skillValue <= lastAttempt) - { - LookAtInteriorLock(buildingLockValue); - return; - } - - // Attempt to unlock building - Random.InitState(Time.frameCount); - player.TallySkill(DFCareer.Skills.Lockpicking, 1); - int chance = FormulaHelper.CalculateExteriorLockpickingChance(buildingLockValue, skillValue); - int roll = Random.Range(1, 101); - Debug.LogFormat("Attempting pick against lock strength {0}. Chance={1}, Roll={2}.", buildingLockValue, chance, roll); - if (chance > roll) - { - // Show success and play unlock sound - player.TallyCrimeGuildRequirements(true, 1); - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingSuccess")); - DaggerfallAudioSource dfAudioSource = GetComponent(); - if (dfAudioSource != null) - dfAudioSource.PlayOneShot(SoundClips.ActivateLockUnlock); - } - else - { - // Show failure and record attempt skill level in discovery data - // Have not been able to create a guard response in classic, even when early morning NPCs are nearby - // Assuming for now that exterior lockpicking is discrete enough that no response on failure is required - DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingFailure")); - GameManager.Instance.PlayerGPS.SetLastLockpickAttempt(building.buildingKey, skillValue); - return; - } + // Show failure and record attempt skill level in discovery data + // Have not been able to create a guard response in classic, even when early morning NPCs are nearby + // Assuming for now that exterior lockpicking is discrete enough that no response on failure is required + DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("lockpickingFailure")); + GameManager.Instance.PlayerGPS.SetLastLockpickAttempt(building.buildingKey, skillValue); + return; } } } @@ -600,7 +599,7 @@ void ActivateStaticDoor( buildingData.factionID != (int)FactionFile.FactionIDs.The_Dark_Brotherhood && !DaggerfallBankManager.IsHouseOwned(building.buildingKey)) { - if (!isBash) // Residents don't greet you when you kick in their door. + if (!isBrokenIn) { string greetingText = DaggerfallUnity.Instance.TextProvider.GetRandomText(houseGreetingsTextId); mb = DaggerfallUI.MessageBox(greetingText); From e5281299bc65d7ee26a697b58e23e91d920e980a Mon Sep 17 00:00:00 2001 From: numidium Date: Sun, 18 Feb 2024 19:36:33 -0500 Subject: [PATCH 7/7] Fix bashing logic for locked vs unlocked doors --- Assets/Scripts/Game/PlayerActivate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Game/PlayerActivate.cs b/Assets/Scripts/Game/PlayerActivate.cs index 94fb644be5..87fda6e2ca 100644 --- a/Assets/Scripts/Game/PlayerActivate.cs +++ b/Assets/Scripts/Game/PlayerActivate.cs @@ -568,7 +568,7 @@ void ActivateStaticDoor( } // Attempt to bash the door. Classic makes a roll whether it is locked or not. - if (isBash && Dice100.FailedRoll(25 - buildingLockValue)) + if (isBash && !buildingUnlocked && Dice100.FailedRoll(25 - buildingLockValue)) { // 10% chance that you are noticed. if (Dice100.SuccessRoll(10))