Example #1
0
        // Notify player when they enter location rect
        // For exterior towns, print out "You are entering %s".
        // For exterior dungeons, print out flavour text.
        private void PlayerGPS_OnEnterLocationRect(DFLocation location)
        {
            const int set1StartID = 500;
            const int set2StartID = 520;

            if (playerGPS && !isPlayerInside)
            {
                if (location.HasDungeon &&
                    location.MapTableData.DungeonType != DFRegion.DungeonTypes.NoDungeon &&
                    location.MapTableData.DungeonType != DFRegion.DungeonTypes.Palace)
                {
                    // Get text ID based on set start and dungeon type index
                    int dungeonTypeIndex = (int)location.MapTableData.DungeonType >> 8;
                    int set1ID           = set1StartID + dungeonTypeIndex;
                    int set2ID           = set2StartID + dungeonTypeIndex;

                    // Select two sets of flavour text based on dungeon type
                    string flavourText1 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set1ID);
                    string flavourText2 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set2ID);

                    // Show flavour text a bit longer than in classic
                    DaggerfallUI.AddHUDText(flavourText1, 3);
                    DaggerfallUI.AddHUDText(flavourText2, 3);
                }
                else
                {
                    // Show "You are entering %s"
                    string youAreEntering = HardStrings.youAreEntering;
                    youAreEntering = youAreEntering.Replace("%s", location.Name);
                    DaggerfallUI.AddHUDText(youAreEntering, 2);
                }
            }
        }
        /// <summary>
        /// Check if should do rappel, and do rappel and attach to wall.
        /// </summary>
        private void RappelChecks(bool airborneGraspWall)
        {
            if (airborneGraspWall)
            {
                // TODO: prevent rappelling if small falls
                if (!IsRappelling)
                {
                    // should rappelling start?
                    bool movingBackward = InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards);

                    IsRappelling = (movingBackward && !acrobatMotor.Jumping);
                    if (IsRappelling)
                    {
                        DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.rappelMode);
                    }
                    lastPosition = controller.transform.position;
                    rappelTimer  = 0f;
                }

                if (IsRappelling)
                {
                    const float firstTimerMax = 0.7f;
                    overrideSkillCheck = true;

                    rappelTimer += Time.deltaTime;

                    if (rappelTimer <= firstTimerMax)
                    {
                        Vector3 rappelPosition = Vector3.zero;
                        // create C-shaped movement to plant self against wall beneath
                        Vector3 pos    = lastPosition;
                        float   yDist  = 1.60f;
                        float   xzDist = 0.17f;
                        rappelPosition.x = Mathf.Lerp(pos.x, pos.x - (controller.transform.forward.x * xzDist), Mathf.Sin(Mathf.PI * (rappelTimer / firstTimerMax)));
                        rappelPosition.z = Mathf.Lerp(pos.z, pos.z - (controller.transform.forward.z * xzDist), Mathf.Sin(Mathf.PI * (rappelTimer / firstTimerMax)));
                        rappelPosition.y = Mathf.Lerp(pos.y, pos.y - yDist, rappelTimer / firstTimerMax);

                        controller.transform.position = rappelPosition;
                    }
                    else
                    {
                        Vector3 rappelDirection = Vector3.zero;
                        // Auto forward to grab wall
                        float speed = speedChanger.GetBaseSpeed();
                        if (myLedgeDirection != Vector3.zero)
                        {
                            rappelDirection = myLedgeDirection;
                        }
                        else
                        {
                            rappelDirection = controller.transform.forward;
                        }
                        rappelDirection *= speed * 1.25f;
                        groundMotor.MoveWithMovingPlatform(rappelDirection);
                    }
                }
            }
        }
Example #3
0
        // Notify player when they enter location rect
        // For exterior towns, print out "You are entering %s".
        // For exterior dungeons, print out flavour text.
        private void PlayerGPS_OnEnterLocationRect(DFLocation location)
        {
            const int set1StartID = 500;
            const int set2StartID = 520;

            if (playerGPS && !isPlayerInside)
            {
                if (location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonLabyrinth ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonKeep ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonRuin ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.Graveyard)
                {
                    // Get text ID based on set start and dungeon type index
                    int dungeonTypeIndex = (int)location.MapTableData.DungeonType;
                    int set1ID           = set1StartID + dungeonTypeIndex;
                    int set2ID           = set2StartID + dungeonTypeIndex;

                    // Select two sets of flavour text based on dungeon type
                    string flavourText1 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set1ID);
                    string flavourText2 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set2ID);

                    // Show flavour text a bit longer than in classic
                    DaggerfallUI.AddHUDText(flavourText1, 3);
                    DaggerfallUI.AddHUDText(flavourText2, 3);
                }
                else if (location.MapTableData.LocationType != DFRegion.LocationTypes.Coven &&
                         location.MapTableData.LocationType != DFRegion.LocationTypes.HomeYourShips)
                {
                    // Show "You are entering %s"
                    string youAreEntering = HardStrings.youAreEntering;
                    youAreEntering = youAreEntering.Replace("%s", location.Name);
                    DaggerfallUI.AddHUDText(youAreEntering, 2);

                    // Check room rentals in this location, and display how long any rooms are rented for
                    int          mapId        = playerGPS.CurrentLocation.MapTableData.MapId;
                    PlayerEntity playerEntity = GameManager.Instance.PlayerEntity;
                    playerEntity.RemoveExpiredRentedRooms();
                    List <RoomRental_v1> rooms = playerEntity.GetRentedRooms(mapId);
                    if (rooms.Count > 0)
                    {
                        foreach (RoomRental_v1 room in rooms)
                        {
                            string remainingHours = PlayerEntity.GetRemainingHours(room).ToString();
                            DaggerfallUI.AddHUDText(HardStrings.youHaveRentedRoom.Replace("%s", room.name).Replace("%d", remainingHours), 6);
                        }
                    }

                    if (holidayTextTimer <= 0 && !holidayTextPrimed)
                    {
                        holidayTextTimer  = 2.5f; // Short delay to give save game fade-in time to finish
                        holidayTextPrimed = true;
                    }
                    holidayTextLocation = location;

                    TalkManager.Instance.LastExteriorEntered = location.LocationIndex;
                }
            }
        }
        /// <summary>
        /// Assigns a new spell to be cast.
        /// For player entity, this will display "press button to fire spell" message.
        /// </summary>
        /// <param name="spell"></param>
        public void SetReadySpell(FakeSpell spell)
        {
            readySpell = spell;

            if (isPlayerEntity)
            {
                DaggerfallUI.AddHUDText(HardStrings.pressButtonToFireSpell);
            }
        }
Example #5
0
        void FixedUpdate()
        {
            if (Player != null)
            {
                Vector3 toPlayer = Player.transform.position - transform.position;
                directionToPlayer = toPlayer.normalized;
                distanceToPlayer  = toPlayer.magnitude;

                playerInSight = CanSeePlayer();

                if (playerInSight)
                {
                    detectedPlayer = true;
                }

                // Classic stealth mechanics would be interfered with by hearing, so only enable
                // hearing if the enemy has detected the player. If player has been seen we can omit hearing.
                if (detectedPlayer && !playerInSight)
                {
                    playerInEarshot = CanHearPlayer();
                }
                else
                {
                    playerInEarshot = false;
                }

                if ((playerInEarshot || playerInSight) && !hasEncounteredPlayer)
                {
                    hasEncounteredPlayer = true;

                    // Check appropriate language skill to see if player can pacify enemy
                    DaggerfallEntityBehaviour entityBehaviour = GetComponent <DaggerfallEntityBehaviour>();
                    EnemyMotor motor = GetComponent <EnemyMotor>();
                    if (entityBehaviour && motor &&
                        (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass))
                    {
                        EnemyEntity     enemyEntity   = entityBehaviour.Entity as EnemyEntity;
                        DFCareer.Skills languageSkill = enemyEntity.GetLanguageSkill();
                        if (languageSkill != DFCareer.Skills.None)
                        {
                            PlayerEntity player = GameManager.Instance.PlayerEntity;
                            if (FormulaHelper.CalculateEnemyPacification(player, languageSkill))
                            {
                                motor.IsHostile = false;
                                DaggerfallUI.AddHUDText(HardStrings.languagePacified.Replace("%e", enemyEntity.Name).Replace("%s", languageSkill.ToString()), 5);
                                player.TallySkill(languageSkill, 3);    // BCHG: increased skill uses from (assumed) 1 in classic on success to make raising language skills easier
                            }
                            else if (languageSkill != DFCareer.Skills.Etiquette && languageSkill != DFCareer.Skills.Streetwise)
                            {
                                player.TallySkill(languageSkill, 1);
                            }
                        }
                    }
                }
            }
        }
 /// <summary>
 /// Set climbing to true and show climbing mode message once
 /// </summary>
 private void StartClimbing()
 {
     if (!isClimbing)
     {
         if (showClimbingModeMessage)
         {
             DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.climbingMode);
         }
         // Disable further showing of climbing mode message until current climb attempt is stopped
         // to keep it from filling message log
         showClimbingModeMessage = false;
         isClimbing = true;
     }
 }
Example #7
0
        private void StartHanging()
        {
            if (!IsHanging)
            {
                if (showHangingModeMessage)
                {
                    DaggerfallUI.AddHUDText(TextManager.Instance.GetLocalizedText("hangingMode"));
                }

                showHangingModeMessage = false;
                IsHanging = true;
                climbingMotor.StopClimbing(IsHanging);
            }
        }
Example #8
0
        private void StartHanging()
        {
            if (!IsHanging)
            {
                if (showHangingModeMessage)
                {
                    DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.hangingMode);
                }

                showHangingModeMessage = false;
                IsHanging = true;
                climbingMotor.StopClimbing(IsHanging);
            }
        }
        // Output NPC info to HUD
        private void PresentNPCInfo(StaticNPC npc)
        {
            DaggerfallUI.AddHUDText(HardStrings.youSee.Replace("%s", npc.DisplayName));

            // Add debug info
            if (DaggerfallUI.Instance.DaggerfallHUD.ShowQuestDebugger)
            {
                // Get faction info of this NPC
                FactionFile.FactionData factionData;
                if (GameManager.Instance.PlayerEntity.FactionData.GetFactionData(npc.Data.factionID, out factionData))
                {
                    string debugInfo = string.Format("Debugger: Your reputation with this NPC is {0}.", factionData.rep);
                    DaggerfallUI.AddHUDText(debugInfo);
                }
            }
        }
 /// <summary>
 /// Set climbing to true and show climbing mode message once
 /// </summary>
 private void StartClimbing()
 {
     if (!isClimbing)
     {
         if (showClimbingModeMessage)
         {
             DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.climbingMode);
         }
         // Disable further showing of climbing mode message until current climb attempt is stopped
         // to keep it from filling message log
         showClimbingModeMessage = false;
         isClimbing = true;
         hangingMotor.CancelHanging();
         // reset jumping in case we jumped onto the wall
         acrobatMotor.Jumping = false;
     }
 }
        // Output building info to HUD
        private void PresentBuildingInfo(StaticBuilding building)
        {
            // Get building directory for location
            BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory();

            if (!buildingDirectory)
            {
                return;
            }

            // Get detailed building data from directory
            BuildingSummary buildingSummary;

            if (!buildingDirectory.GetBuildingSummary(building.buildingKey, out buildingSummary))
            {
                int layoutX, layoutY, recordIndex;
                BuildingDirectory.ReverseBuildingKey(building.buildingKey, out layoutX, out layoutY, out recordIndex);
                Debug.LogFormat("Unable to find expected building key {0} in {1}.{2}", building.buildingKey, buildingDirectory.LocationData.RegionName, buildingDirectory.LocationData.Name);
                Debug.LogFormat("LayoutX={0}, LayoutY={1}, RecordIndex={2}", layoutX, layoutY, recordIndex);
                throw new Exception("Error finding building key in directory.");
            }

            // Resolve name by building type
            string buildingName;

            if (RMBLayout.IsResidence(buildingSummary.BuildingType))
            {
                // Residence
                // TODO: Link to quest system active sites
                buildingName = HardStrings.residence;
            }
            else
            {
                // Fixed building name
                buildingName = BuildingNames.GetName(
                    buildingSummary.NameSeed,
                    buildingSummary.BuildingType,
                    buildingSummary.FactionId,
                    buildingDirectory.LocationData.Name,
                    buildingDirectory.LocationData.RegionName);
            }

            // Output building name to HUD
            DaggerfallUI.AddHUDText(buildingName);
        }
        // Notify player when they enter location rect
        // For exterior towns, print out "You are entering %s".
        // For exterior dungeons, print out flavour text.
        private void PlayerGPS_OnEnterLocationRect(DFLocation location)
        {
            const int set1StartID = 500;
            const int set2StartID = 520;

            if (playerGPS && !isPlayerInside)
            {
                if (location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonLabyrinth ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonKeep ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonRuin ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.Graveyard)
                {
                    // Get text ID based on set start and dungeon type index
                    int dungeonTypeIndex = (int)location.MapTableData.DungeonType;
                    int set1ID           = set1StartID + dungeonTypeIndex;
                    int set2ID           = set2StartID + dungeonTypeIndex;

                    // Select two sets of flavour text based on dungeon type
                    string flavourText1 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set1ID);
                    string flavourText2 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set2ID);

                    // Show flavour text a bit longer than in classic
                    DaggerfallUI.AddHUDText(flavourText1, 3);
                    DaggerfallUI.AddHUDText(flavourText2, 3);
                }
                else if (location.MapTableData.LocationType != DFRegion.LocationTypes.Coven &&
                         location.MapTableData.LocationType != DFRegion.LocationTypes.HomeYourShips)
                {
                    // Show "You are entering %s"
                    string youAreEntering = HardStrings.youAreEntering;
                    youAreEntering = youAreEntering.Replace("%s", location.Name);
                    DaggerfallUI.AddHUDText(youAreEntering, 2);

                    if (holidayTextTimer <= 0 && !holidayTextPrimed)
                    {
                        holidayTextTimer  = 2.5f; // Short delay to give save game fade-in time to finish
                        holidayTextPrimed = true;
                    }
                    holidayTextLocation = location;
                }
            }
        }
Example #13
0
        void ShowDamageMsg(EnemyEntity enemyEntity, int damage)
        {
            float percentDamage = enemyEntity.CurrentHealthPercent * 100;

            if (percentDamage == 0)
            {
                return;
            }

            if ((int)(percentDamage / 20) == (int)(((enemyEntity.CurrentHealth + damage) * 100 / enemyEntity.MaxHealth) / 20))
            {
                return;
            }

            if (percentDamage > 80)
            {
                DaggerfallUI.AddHUDText($"{enemyEntity.Name} is lightly wounded.");
                return;
            }
            if (percentDamage > 60)
            {
                DaggerfallUI.AddHUDText($"{enemyEntity.Name} is wounded.");
                return;
            }
            if (percentDamage > 40)
            {
                DaggerfallUI.AddHUDText($"{enemyEntity.Name} is severely wounded.");
                return;
            }
            if (percentDamage > 20)
            {
                DaggerfallUI.AddHUDText($"{enemyEntity.Name} is critically wounded.");
                return;
            }
            DaggerfallUI.AddHUDText($"{enemyEntity.Name} is nearing death.");
            return;
        }
Example #14
0
        public static void CheckOverdueLoans(uint lastGameMinutes)
        {
            uint gameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime();

            for (int regionIndex = 0; regionIndex < DaggerfallBankManager.BankAccounts.Length; regionIndex++)
            {
                long paymentDueMinutes = DaggerfallBankManager.GetLoanDueDate(regionIndex);

                if (paymentDueMinutes != 0)
                {
                    if (paymentDueMinutes < gameMinutes)
                    {
                        Debug.Log("loan overdue " + paymentDueMinutes + " < " + gameMinutes);
                        OverdueLoan(regionIndex);
                    }
                    else
                    {
                        long lastRemainingMonths = (paymentDueMinutes - lastGameMinutes) / MinutesPerMonth;
                        long remainingMonths     = (paymentDueMinutes - gameMinutes) / MinutesPerMonth;
                        if (remainingMonths < lastRemainingMonths)
                        {
                            // Months left before due date
                            int[] sendReminderMonths = { 6, 3, 1 };
                            if (Array.Exists(sendReminderMonths, month => lastRemainingMonths >= month && remainingMonths < month))
                            {
                                // Send letters before due date instead?
                                DaggerfallUI.AddHUDText(String.Format(TextManager.Instance.GetLocalizedText("loanReminder"),
                                                                      DaggerfallBankManager.GetLoanedTotal(regionIndex)), loanReminderHUDDelay);
                                DaggerfallUI.AddHUDText(String.Format(TextManager.Instance.GetLocalizedText("loanReminder2"),
                                                                      remainingMonths + 1, MapsFile.RegionNames[regionIndex]), loanReminderHUDDelay);
                            }
                        }
                    }
                }
            }
        }
        void Update()
        {
            if (mainCamera == null)
            {
                return;
            }

            // Change activate mode
            if (InputManager.Instance.ActionStarted(InputManager.Actions.StealMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Steal);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.GrabMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Grab);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.InfoMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Info);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.TalkMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Talk);
            }

            // Fire ray into scene
            if (InputManager.Instance.ActionStarted(InputManager.Actions.ActivateCenterObject))
            {
                // TODO: Clean all this up

                // Ray origin is slightly below camera height to ensure it originates inside player's own collider
                // This prevents ray from intersecting with player's own collider and blocking looting or low triggers
                Ray        ray = new Ray(transform.position + Vector3.up * 0.7f, mainCamera.transform.forward);
                RaycastHit hit;
                RayDistance = 75f; // Approximates classic at full view distance (default setting). Classic seems to do raycasts for as far as it can render objects.
                bool hitSomething = Physics.Raycast(ray, out hit, RayDistance);
                if (hitSomething)
                {
                    bool hitBuilding      = false;
                    bool buildingUnlocked = false;
                    DFLocation.BuildingTypes buildingType = DFLocation.BuildingTypes.AllValid;
                    StaticBuilding           building     = new StaticBuilding();

                    #region Hit Checks

                    // Trigger quest resource behaviour click on anything but NPCs
                    QuestResourceBehaviour questResourceBehaviour;
                    if (QuestResourceBehaviourCheck(hit, out questResourceBehaviour))
                    {
                        if (!(questResourceBehaviour.TargetResource is Person))
                        {
                            if (hit.distance > (DefaultActivationDistance * MeshReader.GlobalScale))
                            {
                                DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                return;
                            }

                            // Only trigger click when not in info mode
                            if (currentMode != PlayerActivateModes.Info)
                            {
                                TriggerQuestResourceBehaviourClick(questResourceBehaviour);
                            }
                        }
                    }

                    // Check for a static building hit
                    Transform buildingOwner;
                    DaggerfallStaticBuildings buildings = GetBuildings(hit.transform, out buildingOwner);
                    if (buildings)
                    {
                        if (buildings.HasHit(hit.point, out building))
                        {
                            hitBuilding = true;

                            // Get building directory for location
                            BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory();
                            if (!buildingDirectory)
                            {
                                return;
                            }

                            // Get detailed building data from directory
                            BuildingSummary buildingSummary;
                            if (!buildingDirectory.GetBuildingSummary(building.buildingKey, out buildingSummary))
                            {
                                return;
                            }

                            // Check if door is unlocked
                            buildingUnlocked = BuildingIsUnlocked(buildingSummary);

                            // Store building type
                            buildingType = buildingSummary.BuildingType;

                            if (currentMode == PlayerActivateModes.Info)
                            {
                                // Discover building
                                GameManager.Instance.PlayerGPS.DiscoverBuilding(building.buildingKey);

                                // Get discovered building
                                PlayerGPS.DiscoveredBuilding db;
                                if (GameManager.Instance.PlayerGPS.GetDiscoveredBuilding(building.buildingKey, out db))
                                {
                                    // TODO: Check against quest system for an overriding quest-assigned display name for this building
                                    DaggerfallUI.AddHUDText(db.displayName);

                                    if (!buildingUnlocked && buildingType < DFLocation.BuildingTypes.Temple &&
                                        buildingType != DFLocation.BuildingTypes.HouseForSale)
                                    {
                                        string storeClosedMessage = HardStrings.storeClosed;
                                        storeClosedMessage = storeClosedMessage.Replace("%d1", openHours[(int)buildingType].ToString());
                                        storeClosedMessage = storeClosedMessage.Replace("%d2", closeHours[(int)buildingType].ToString());
                                        DaggerfallUI.Instance.PopupMessage(storeClosedMessage);
                                    }
                                }
                            }
                        }
                    }

                    // Check for a static door hit
                    Transform             doorOwner;
                    DaggerfallStaticDoors doors = GetDoors(hit.transform, out doorOwner);
                    if (doors && playerEnterExit)
                    {
                        StaticDoor door;
                        if (doors.HasHit(hit.point, out door))
                        {
                            // Check if close enough to activate
                            if (hit.distance > (DoorActivationDistance * MeshReader.GlobalScale))
                            {
                                DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                return;
                            }

                            if (door.doorType == DoorTypes.Building && !playerEnterExit.IsPlayerInside)
                            {
                                // Discover building
                                GameManager.Instance.PlayerGPS.DiscoverBuilding(building.buildingKey);

                                // TODO: Implement lockpicking and door bashing for exterior doors
                                // For now, any locked building door can be entered by using steal mode
                                if (!buildingUnlocked)
                                {
                                    if (currentMode != PlayerActivateModes.Steal)
                                    {
                                        string Locked = "Locked.";
                                        DaggerfallUI.Instance.PopupMessage(Locked);
                                        return;
                                    }
                                    else     // Breaking into building
                                    {
                                        PlayerEntity player = GameManager.Instance.PlayerEntity;
                                        //player.TallyCrimeGuildRequirements(true, 1);
                                    }
                                }

                                // If entering a shop let player know the quality level
                                // If entering an open home, show greeting
                                if (hitBuilding)
                                {
                                    const int houseGreetingsTextId = 256;

                                    DaggerfallMessageBox mb;

                                    if (buildingUnlocked && buildingType >= DFLocation.BuildingTypes.House1 &&
                                        buildingType <= DFLocation.BuildingTypes.House4)
                                    {
                                        string greetingText = DaggerfallUnity.Instance.TextProvider.GetRandomText(houseGreetingsTextId);
                                        mb = DaggerfallUI.MessageBox(greetingText);
                                    }
                                    else
                                    {
                                        mb = PresentShopQuality(building);
                                    }

                                    if (mb != null)
                                    {
                                        // Defer transition to interior to after user closes messagebox
                                        deferredInteriorDoorOwner = doorOwner;
                                        deferredInteriorDoor      = door;
                                        mb.OnClose += Popup_OnClose;
                                        return;
                                    }
                                }

                                // Hit door while outside, transition inside
                                TransitionInterior(doorOwner, door, true);
                                return;
                            }
                            else if (door.doorType == DoorTypes.Building && playerEnterExit.IsPlayerInside)
                            {
                                // Hit door while inside, transition outside
                                playerEnterExit.TransitionExterior(true);
                                return;
                            }
                            else if (door.doorType == DoorTypes.DungeonEntrance && !playerEnterExit.IsPlayerInside)
                            {
                                if (playerGPS)
                                {
                                    // Hit dungeon door while outside, transition inside
                                    playerEnterExit.TransitionDungeonInterior(doorOwner, door, playerGPS.CurrentLocation, true);
                                    return;
                                }
                            }
                            else if (door.doorType == DoorTypes.DungeonExit && playerEnterExit.IsPlayerInside)
                            {
                                // Hit dungeon exit while inside, ask if access wagon or transition outside
                                if (GameManager.Instance.PlayerEntity.Items.Contains(ItemGroups.Transportation, (int)Transportation.Small_cart))
                                {
                                    DaggerfallMessageBox messageBox = new DaggerfallMessageBox(DaggerfallUI.UIManager, DaggerfallMessageBox.CommonMessageBoxButtons.YesNo, 38, DaggerfallUI.UIManager.TopWindow);
                                    messageBox.OnButtonClick += DungeonWagonAccess_OnButtonClick;
                                    DaggerfallUI.UIManager.PushWindow(messageBox);
                                    return;
                                }
                                else
                                {
                                    playerEnterExit.TransitionDungeonExterior(true);
                                }
                            }
                        }
                    }

                    // Check for an action door hit
                    DaggerfallActionDoor actionDoor;
                    if (ActionDoorCheck(hit, out actionDoor))
                    {
                        // Check if close enough to activate
                        if (hit.distance > (DoorActivationDistance * MeshReader.GlobalScale))
                        {
                            DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                            return;
                        }

                        if (currentMode == PlayerActivateModes.Steal && actionDoor.IsLocked && !actionDoor.IsOpen)
                        {
                            actionDoor.AttemptLockpicking();
                        }
                        else
                        {
                            actionDoor.ToggleDoor(true);
                        }
                    }

                    // Check for action record hit
                    DaggerfallAction action;
                    if (ActionCheck(hit, out action))
                    {
                        if (hit.distance <= (DefaultActivationDistance * MeshReader.GlobalScale))
                        {
                            action.Receive(this.gameObject, DaggerfallAction.TriggerTypes.Direct);
                        }
                    }

                    // Check for lootable object hit
                    DaggerfallLoot loot;
                    if (LootCheck(hit, out loot))
                    {
                        switch (currentMode)
                        {
                        case PlayerActivateModes.Info:
                            if (loot.ContainerType == LootContainerTypes.CorpseMarker && !string.IsNullOrEmpty(loot.entityName))
                            {
                                string message = string.Empty;
                                if (loot.isEnemyClass)
                                {
                                    message = HardStrings.youSeeADeadPerson;
                                }
                                else
                                {
                                    message = HardStrings.youSeeADead;
                                    message = message.Replace("%s", loot.entityName);
                                }
                                DaggerfallUI.Instance.PopupMessage(message);
                            }
                            break;

                        case PlayerActivateModes.Grab:
                        case PlayerActivateModes.Talk:
                        case PlayerActivateModes.Steal:
                            // Check if close enough to activate
                            if (loot.ContainerType == LootContainerTypes.CorpseMarker)
                            {
                                if (hit.distance > CorpseActivationDistance * MeshReader.GlobalScale)
                                {
                                    DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                    break;
                                }
                            }
                            else if (hit.distance > TreasureActivationDistance * MeshReader.GlobalScale)
                            {
                                DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                break;
                            }

                            // For bodies, check has treasure first
                            if (loot.ContainerType == LootContainerTypes.CorpseMarker && loot.Items.Count == 0)
                            {
                                DaggerfallUI.AddHUDText(HardStrings.theBodyHasNoTreasure);
                                break;
                            }

                            // Open inventory window with loot as remote target
                            DaggerfallUI.Instance.InventoryWindow.LootTarget = loot;
                            DaggerfallUI.PostMessage(DaggerfallUIMessages.dfuiOpenInventoryWindow);
                            break;
                        }
                    }

                    // Check for static NPC hit
                    StaticNPC npc;
                    if (NPCCheck(hit, out npc))
                    {
                        switch (currentMode)
                        {
                        case PlayerActivateModes.Info:
                            PresentNPCInfo(npc);
                            break;

                        case PlayerActivateModes.Grab:
                        case PlayerActivateModes.Talk:
                        case PlayerActivateModes.Steal:
                            if (hit.distance > (StaticNPCActivationDistance * MeshReader.GlobalScale))
                            {
                                DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                break;
                            }
                            StaticNPCClick(npc);
                            break;
                        }
                    }

                    // Check for mobile NPC hit
                    MobilePersonNPC mobileNpc = null;
                    if (MobilePersonMotorCheck(hit, out mobileNpc))
                    {
                        switch (currentMode)
                        {
                        case PlayerActivateModes.Info:
                        case PlayerActivateModes.Grab:
                        case PlayerActivateModes.Talk:
                            if (hit.distance > (MobileNPCActivationDistance * MeshReader.GlobalScale))
                            {
                                DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                break;
                            }
                            GameManager.Instance.TalkManager.TalkToMobileNPC(mobileNpc);
                            break;

                        case PlayerActivateModes.Steal:
                            if (!mobileNpc.PickpocketByPlayerAttempted)
                            {
                                if (hit.distance > (PickpocketDistance * MeshReader.GlobalScale))
                                {
                                    DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                    break;
                                }
                                mobileNpc.PickpocketByPlayerAttempted = true;
                                Pickpocket();
                            }
                            break;
                        }
                    }

                    // Check for mobile enemy hit
                    DaggerfallEntityBehaviour mobileEnemyBehaviour;
                    if (MobileEnemyCheck(hit, out mobileEnemyBehaviour))
                    {
                        EnemyEntity enemyEntity = mobileEnemyBehaviour.Entity as EnemyEntity;
                        switch (currentMode)
                        {
                        case PlayerActivateModes.Info:
                        case PlayerActivateModes.Grab:
                        case PlayerActivateModes.Talk:
                            if (enemyEntity != null)
                            {
                                MobileEnemy mobileEnemy     = enemyEntity.MobileEnemy;
                                bool        startsWithVowel = "aeiouAEIOU".Contains(mobileEnemy.Name[0].ToString());
                                string      message;
                                if (startsWithVowel)
                                {
                                    message = HardStrings.youSeeAn;
                                }
                                else
                                {
                                    message = HardStrings.youSeeA;
                                }
                                message = message.Replace("%s", mobileEnemy.Name);
                                DaggerfallUI.Instance.PopupMessage(message);
                            }
                            break;

                        case PlayerActivateModes.Steal:
                            // Classic allows pickpocketing of NPC mobiles and enemy mobiles.
                            // In early versions the only enemy mobiles that can be pickpocketed are classes,
                            // but patch 1.07.212 allows pickpocketing of creatures.
                            // For now, the only enemy mobiles being allowed by DF Unity are classes.
                            if (mobileEnemyBehaviour && (mobileEnemyBehaviour.EntityType != EntityTypes.EnemyClass))
                            {
                                break;
                            }
                            // Classic doesn't set any flag when pickpocketing enemy mobiles, so infinite attempts are possible
                            if (enemyEntity != null && !enemyEntity.PickpocketByPlayerAttempted)
                            {
                                if (hit.distance > (PickpocketDistance * MeshReader.GlobalScale))
                                {
                                    DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                                    break;
                                }
                                enemyEntity.PickpocketByPlayerAttempted = true;
                                Pickpocket(mobileEnemyBehaviour);
                            }
                            break;
                        }
                    }

                    // Trigger ladder hit
                    DaggerfallLadder ladder = hit.transform.GetComponent <DaggerfallLadder>();
                    if (ladder)
                    {
                        if (hit.distance > (DefaultActivationDistance * MeshReader.GlobalScale))
                        {
                            DaggerfallUI.SetMidScreenText(HardStrings.youAreTooFarAway);
                            return;
                        }

                        ladder.ClimbLadder();
                    }

                    #endregion
                }
            }
        }
Example #16
0
        /// <summary>
        /// Check if should do rappel, and do rappel and attach to wall.
        /// </summary>
        public void RappelChecks()
        {
            if (rappelStage == RappelStage.Inactive)
            {
                measure = null;
            }
            else
            {
                // Player can become grounded with a partial rappel state based on object height
                // If player is grounded then reset back to inactive state
                if (GameManager.Instance.PlayerMotor.IsGrounded)
                {
                    ResetRappelState();
                    return;
                }
            }

            InitialSetRappelType();

            bool    inputBackward = InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards);
            Vector3 origin        = Vector3.zero;
            Vector3 direction     = -controller.transform.forward;

            #region Scan For AdjacentSurface
            if (inputBackward)
            {
                // check from different origins to find different surfaces
                if (!climbingMotor.IsClimbing)
                {
                    origin = controller.transform.position + Vector3.down * (0.25f * controller.height) + controller.transform.forward * (0.8f * controller.radius);
                }
                else
                {
                    origin = controller.transform.position + Vector3.up * (0.25f * controller.height) + controller.transform.forward * (0.8f * controller.radius);
                }

                playerScanner.FindAdjacentSurface(origin, direction, PlayerMoveScanner.RotationDirection.YZCounterClockwise);
            }
            #endregion

            // the only time Ground closeness can cancel the start of the rappel is when we are
            // going to rappel down and behind to a wall
            if (InitialTooCloseToGround(rappelDirection))
            {
                IsRappelling = false;
                return;
            }

            if (rappelStage == RappelStage.Activated)
            {
                DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.rappelMode);
                rappelStage = RappelStage.Swooping;
                InitialSetGrappleDirection();

                swoopBasePosition = controller.transform.position;
                rappelTimer       = 0f;
                measure           = null;
            }

            // Rappel swooping
            if (rappelStage == RappelStage.Swooping)
            {
                player.TallySkill(DFCareer.Skills.Climbing, 1);
                Vector3 swoopDirection = grappleDirection;
                // if we are rappelling under to ceiling, grappledirection is different so use adjacentSurfaceRay
                // direction to get right direction to go under the ceiling.
                if (rappelDirection == RappelDirection.DownUnder || rappelDirection == RappelDirection.FrontUp)
                {
                    swoopDirection = playerScanner.WallDetachedVector;
                }
                rappelTimer += Time.deltaTime;

                switch (rappelDirection)
                {
                case RappelDirection.DownBehind:
                    CurlDown(swoopBasePosition, swoopDirection);
                    break;

                case RappelDirection.UpBehind:
                    CurlOver(swoopBasePosition, swoopDirection);
                    break;

                //case RappelDirection.DownUnder:
                //    CurlUnder(swoopBasePosition, swoopDirection);
                //    break;
                case RappelDirection.FrontUp:
                    if (updateSwoopBasePosition)
                    {       // enables player to bottom out on DownUnder and continue FrontUp
                        swoopBasePosition       = controller.transform.position;
                        updateSwoopBasePosition = false;
                        rappelTimer             = 0;
                    }
                    CurlUpHalf(swoopBasePosition, swoopDirection);
                    break;

                default:
                    break;
                }

                controller.transform.position = rappelPosition;
            }

            if (rappelStage == RappelStage.Grappling)
            // perform horizontal measurement-based Wall-grapple direction or vertical for ceiling
            {
                // if measurement hasn't started, start measuring grapple-to-surface movement
                if (measure == null)
                {
                    measure = new VectorMeasurement(controller.transform.position);
                }

                if (!(/*hangingMotor.IsHanging ||*/ climbingMotor.IsClimbing) &&
                    measure.Distance(controller.transform.position) < 1f)
                {
                    // Auto move toward surface to grab
                    float speed = speedChanger.GetBaseSpeed();

                    grappleDirection = grappleDirection.normalized * speed * 1.15f;
                    groundMotor.MoveWithMovingPlatform(grappleDirection);
                }
                else // if we've moved past the distance limit
                {
                    ResetRappelState();
                }
            }
        }
 // Output NPC info to HUD
 private void PresentNPCInfo(StaticNPC npc)
 {
     DaggerfallUI.AddHUDText(HardStrings.youSee.Replace("%s", npc.DisplayName));
 }
        void Update()
        {
            if (mainCamera == null)
            {
                return;
            }

            // Change activate mode
            if (InputManager.Instance.ActionStarted(InputManager.Actions.StealMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Steal);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.GrabMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Grab);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.InfoMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Info);
            }
            else if (InputManager.Instance.ActionStarted(InputManager.Actions.TalkMode))
            {
                ChangeInteractionMode(PlayerActivateModes.Talk);
            }

            // Fire ray into scene
            if (InputManager.Instance.ActionStarted(InputManager.Actions.ActivateCenterObject))
            {
                // TODO: Clean all this up and support mobile enemy info-clicks

                // Using RaycastAll as hits can be blocked by decorations or other models
                // When this happens activation feels unresponsive to player
                // Also processing hit detection in order of priority
                Ray          ray = new Ray(mainCamera.transform.position, mainCamera.transform.forward);
                RaycastHit[] hits;
                hits = Physics.RaycastAll(ray, RayDistance);
                if (hits != null)
                {
                    // Check each hit in range for action, exit on first valid action processed
                    bool           hitBuilding = false;
                    StaticBuilding building    = new StaticBuilding();
                    for (int i = 0; i < hits.Length; i++)
                    {
                        #region Hit Checks

                        // Check for a static building hit
                        Transform buildingOwner;
                        DaggerfallStaticBuildings buildings = GetBuildings(hits[i].transform, out buildingOwner);
                        if (buildings)
                        {
                            if (buildings.HasHit(hits[i].point, out building))
                            {
                                hitBuilding = true;

                                // Show building info
                                if (currentMode == PlayerActivateModes.Info)
                                {
                                    PresentBuildingInfo(building);
                                }
                            }
                        }

                        // Check for a static door hit
                        Transform             doorOwner;
                        DaggerfallStaticDoors doors = GetDoors(hits[i].transform, out doorOwner);
                        if (doors && playerEnterExit)
                        {
                            StaticDoor door;
                            if (doors.HasHit(hits[i].point, out door))
                            {
                                if (door.doorType == DoorTypes.Building && !playerEnterExit.IsPlayerInside)
                                {
                                    // If entering a shop let player know the quality level
                                    if (hitBuilding)
                                    {
                                        DaggerfallMessageBox mb = PresentShopQuality(building);
                                        if (mb != null)
                                        {
                                            // Defer transition to interior to after user closes messagebox
                                            deferredInteriorDoorOwner = doorOwner;
                                            deferredInteriorDoor      = door;
                                            mb.OnClose += ShopQualityPopup_OnClose;
                                            return;
                                        }
                                    }

                                    // Hit door while outside, transition inside
                                    playerEnterExit.TransitionInterior(doorOwner, door, true);
                                    return;
                                }
                                else if (door.doorType == DoorTypes.Building && playerEnterExit.IsPlayerInside)
                                {
                                    // Hit door while inside, transition outside
                                    playerEnterExit.TransitionExterior(true);
                                    return;
                                }
                                else if (door.doorType == DoorTypes.DungeonEntrance && !playerEnterExit.IsPlayerInside)
                                {
                                    if (playerGPS)
                                    {
                                        // Hit dungeon door while outside, transition inside
                                        playerEnterExit.TransitionDungeonInterior(doorOwner, door, playerGPS.CurrentLocation, true);
                                        return;
                                    }
                                }
                                else if (door.doorType == DoorTypes.DungeonExit && playerEnterExit.IsPlayerInside)
                                {
                                    // Hit dungeon exit while inside, transition outside
                                    playerEnterExit.TransitionDungeonExterior(true);
                                    return;
                                }
                            }
                        }

                        // Check for an action door hit
                        DaggerfallActionDoor actionDoor;
                        if (ActionDoorCheck(hits[i], out actionDoor))
                        {
                            if (currentMode == PlayerActivateModes.Steal && actionDoor.IsLocked)
                            {
                                if (actionDoor.IsNoLongerPickable)
                                {
                                    return;
                                }
                                else
                                {
                                    actionDoor.AttemptLockpicking();
                                }
                            }
                            else
                            {
                                actionDoor.ToggleDoor();
                            }
                        }

                        // Check for action record hit
                        DaggerfallAction action;
                        if (ActionCheck(hits[i], out action))
                        {
                            action.Receive(this.gameObject, DaggerfallAction.TriggerTypes.Direct);
                        }

                        // Check for lootable object hit
                        DaggerfallLoot loot;
                        if (LootCheck(hits[i], out loot))
                        {
                            // For bodies, check has treasure first
                            if (loot.ContainerType == LootContainerTypes.CorpseMarker && loot.Items.Count == 0)
                            {
                                DaggerfallUI.AddHUDText(HardStrings.theBodyHasNoTreasure);
                                return;
                            }

                            // Open inventory window with loot as remote target
                            DaggerfallUI.Instance.InventoryWindow.LootTarget = loot;
                            DaggerfallUI.PostMessage(DaggerfallUIMessages.dfuiOpenInventoryWindow);
                        }

                        // Check for static NPC hit
                        StaticNPC npc;
                        if (NPCCheck(hits[i], out npc))
                        {
                            switch (currentMode)
                            {
                            case PlayerActivateModes.Info:
                                PresentNPCInfo(npc);
                                break;

                            case PlayerActivateModes.Grab:
                            case PlayerActivateModes.Talk:
                                SpecialNPCClick(npc);
                                QuestorCheck(npc);
                                break;
                            }
                        }

                        // Trigger general quest resource behaviour click
                        // Note: This will cause a second click on special NPCs, look into a way to unify this handling
                        QuestResourceBehaviour questResourceBehaviour;
                        if (QuestResourceBehaviourCheck(hits[i], out questResourceBehaviour))
                        {
                            TriggerQuestResourceBehaviourClick(questResourceBehaviour);
                        }

                        #endregion
                    }
                }
            }
        }
Example #19
0
        void FixedUpdate()
        {
            if (GameManager.Instance.DisableAI)
            {
                return;
            }

            targetPosPredictTimer += Time.deltaTime;
            if (targetPosPredictTimer >= predictionInterval)
            {
                targetPosPredictTimer = 0f;
                targetPosPredict      = true;
            }
            else
            {
                targetPosPredict = false;
            }

            // Update whether enemy would be spawned or not in classic.
            // Only check if within the maximum possible distance (Just under 1094 classic units)
            if (GameManager.ClassicUpdate)
            {
                if (distanceToPlayer < 1094 * MeshReader.GlobalScale)
                {
                    float upperXZ      = 0;
                    float upperY       = 0;
                    float lowerY       = 0;
                    bool  playerInside = GameManager.Instance.PlayerGPS.GetComponent <PlayerEnterExit>().IsPlayerInside;

                    if (!playerInside)
                    {
                        upperXZ = classicSpawnDespawnExterior;
                    }
                    else
                    {
                        if (!wouldBeSpawnedInClassic)
                        {
                            upperXZ = classicSpawnXZDist;
                            upperY  = classicSpawnYDistUpper;
                            lowerY  = classicSpawnYDistLower;
                        }
                        else
                        {
                            upperXZ = classicDespawnXZDist;
                            upperY  = classicDespawnYDist;
                        }
                    }

                    float YDiffToPlayer      = transform.position.y - Player.transform.position.y;
                    float YDiffToPlayerAbs   = Mathf.Abs(YDiffToPlayer);
                    float distanceToPlayerXZ = Mathf.Sqrt(distanceToPlayer * distanceToPlayer - YDiffToPlayerAbs * YDiffToPlayerAbs);

                    wouldBeSpawnedInClassic = true;

                    if (distanceToPlayerXZ > upperXZ)
                    {
                        wouldBeSpawnedInClassic = false;
                    }

                    if (playerInside)
                    {
                        if (lowerY == 0)
                        {
                            if (YDiffToPlayerAbs > upperY)
                            {
                                wouldBeSpawnedInClassic = false;
                            }
                        }
                        else if (YDiffToPlayer < lowerY || YDiffToPlayer > upperY)
                        {
                            wouldBeSpawnedInClassic = false;
                        }
                    }
                }
                else
                {
                    wouldBeSpawnedInClassic = false;
                }
            }

            if (GameManager.ClassicUpdate)
            {
                classicTargetUpdateTimer += Time.deltaTime / systemTimerUpdatesDivisor;

                if (target != null && target.Entity.CurrentHealth <= 0)
                {
                    target = null;
                }

                // Non-hostile mode
                if (GameManager.Instance.PlayerEntity.NoTargetMode || !motor.IsHostile)
                {
                    if (target == Player)
                    {
                        target = null;
                    }
                    if (secondaryTarget == Player)
                    {
                        secondaryTarget = null;
                    }
                }

                // Reset these values if no target
                if (target == null)
                {
                    lastKnownTargetPos   = ResetPlayerPos;
                    predictedTargetPos   = ResetPlayerPos;
                    directionToTarget    = ResetPlayerPos;
                    lastDistanceToTarget = 0;
                    targetRateOfApproach = 0;
                    distanceToTarget     = 0;
                    targetSenses         = null;

                    // If we have a valid secondary target that we acquired when we got the primary, switch to it.
                    // There will only be a secondary target if using enhanced combat AI.
                    if (secondaryTarget != null && secondaryTarget.Entity.CurrentHealth > 0)
                    {
                        target = secondaryTarget;

                        // If the secondary target was actually seen, use the last place we saw it to begin pursuit.
                        if (sawSecondaryTarget)
                        {
                            lastKnownTargetPos = secondaryTargetPos;
                        }
                        awareOfTargetForLastPrediction = false;
                    }
                }

                // Compare change in target position to give AI some ability to read opponent's movements
                if (target != null && target == targetOnLastUpdate)
                {
                    if (DaggerfallUnity.Settings.EnhancedCombatAI)
                    {
                        targetRateOfApproach = (lastDistanceToTarget - distanceToTarget);
                    }
                }
                else
                {
                    lastDistanceToTarget = 0;
                    targetRateOfApproach = 0;
                }

                if (target != null)
                {
                    lastDistanceToTarget = distanceToTarget;
                    targetOnLastUpdate   = target;
                }
            }

            if (Player != null)
            {
                // Get distance to player
                Vector3 toPlayer = Player.transform.position - transform.position;
                distanceToPlayer = toPlayer.magnitude;

                // If out of classic spawn range, still check for direct LOS to player so that enemies who see player will
                // try to attack.
                if (!wouldBeSpawnedInClassic)
                {
                    distanceToTarget  = distanceToPlayer;
                    directionToTarget = toPlayer.normalized;
                    playerInSight     = CanSeeTarget(Player);
                }

                if (classicTargetUpdateTimer > 5)
                {
                    classicTargetUpdateTimer = 0f;

                    // Is enemy in area around player or can see player?
                    if (wouldBeSpawnedInClassic || playerInSight)
                    {
                        GetTargets();

                        if (target != null && target != Player)
                        {
                            targetSenses = target.GetComponent <EnemySenses>();
                        }
                        else
                        {
                            targetSenses = null;
                        }
                    }

                    // Make targeted character also target this character if it doesn't have a target yet.
                    if (target != null && targetSenses && targetSenses.Target == null)
                    {
                        targetSenses.Target = entityBehaviour;
                    }
                }

                if (target == null)
                {
                    targetInSight  = false;
                    detectedTarget = false;
                    return;
                }

                if (!wouldBeSpawnedInClassic && target == Player)
                {
                    distanceToTarget  = distanceToPlayer;
                    directionToTarget = toPlayer.normalized;
                    targetInSight     = playerInSight;
                }
                else
                {
                    Vector3 toTarget = ResetPlayerPos;
                    toTarget = target.transform.position - transform.position;

                    if (toTarget != ResetPlayerPos)
                    {
                        distanceToTarget  = toTarget.magnitude;
                        directionToTarget = toTarget.normalized;
                    }
                    targetInSight = CanSeeTarget(target);
                }

                // Classic stealth mechanics would be interfered with by hearing, so only enable
                // hearing if the enemy has detected the target. If target is visible we can omit hearing.
                if (detectedTarget && !targetInSight)
                {
                    targetInEarshot = CanHearTarget(target);
                }
                else
                {
                    targetInEarshot = false;
                }

                // Note: In classic an enemy can continue to track the player as long as their
                // giveUpTimer is > 0. Since the timer is reset to 200 on every detection this
                // would make chameleon and shade essentially useless, since the enemy is sure
                // to detect the player during one of the many AI updates. Here, the enemy has to
                // successfully see through the illusion spell each classic update to continue
                // to know where the player is.
                if (GameManager.ClassicUpdate)
                {
                    blockedByIllusionEffect = BlockedByIllusionEffect();
                    if (lastHadLOSTimer > 0)
                    {
                        lastHadLOSTimer--;
                    }
                }

                if (!blockedByIllusionEffect && (targetInSight || targetInEarshot))
                {
                    detectedTarget     = true;
                    lastKnownTargetPos = target.transform.position;
                    lastHadLOSTimer    = 200f;
                }
                else if (!blockedByIllusionEffect && StealthCheck())
                {
                    detectedTarget = true;

                    // Only get the target's location from the stealth check if we haven't had
                    // actual LOS for a while. This gives better pursuit behavior since enemies
                    // will go to the last spot they saw the player instead of walking into walls.
                    if (lastHadLOSTimer <= 0)
                    {
                        lastKnownTargetPos = target.transform.position;
                    }
                }
                else
                {
                    detectedTarget = false;
                }

                if (oldLastKnownTargetPos == ResetPlayerPos)
                {
                    oldLastKnownTargetPos = lastKnownTargetPos;
                }

                if (predictedTargetPos == ResetPlayerPos || !DaggerfallUnity.Settings.EnhancedCombatAI)
                {
                    predictedTargetPos = lastKnownTargetPos;
                }

                // Predict target's next position
                if (targetPosPredict && lastKnownTargetPos != ResetPlayerPos)
                {
                    // Be sure to only take difference of movement if we've seen the target for two consecutive prediction updates
                    if (!blockedByIllusionEffect && (targetInSight || targetInEarshot))
                    {
                        if (awareOfTargetForLastPrediction)
                        {
                            lastPositionDiff = lastKnownTargetPos - oldLastKnownTargetPos;
                        }

                        // Store current last known target position for next prediction update
                        oldLastKnownTargetPos = lastKnownTargetPos;

                        awareOfTargetForLastPrediction = true;
                    }
                    else
                    {
                        awareOfTargetForLastPrediction = false;
                    }

                    if (DaggerfallUnity.Settings.EnhancedCombatAI)
                    {
                        float moveSpeed = (enemyEntity.Stats.LiveSpeed + PlayerSpeedChanger.dfWalkBase) * MeshReader.GlobalScale;
                        predictedTargetPos = PredictNextTargetPos(moveSpeed);
                    }
                }

                if (detectedTarget && !hasEncounteredPlayer && target == Player)
                {
                    hasEncounteredPlayer = true;

                    // Check appropriate language skill to see if player can pacify enemy
                    if (!questBehaviour && entityBehaviour && motor &&
                        (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass))
                    {
                        DFCareer.Skills languageSkill = enemyEntity.GetLanguageSkill();
                        if (languageSkill != DFCareer.Skills.None)
                        {
                            PlayerEntity player = GameManager.Instance.PlayerEntity;
                            if (FormulaHelper.CalculateEnemyPacification(player, languageSkill))
                            {
                                motor.IsHostile = false;
                                DaggerfallUI.AddHUDText(HardStrings.languagePacified.Replace("%e", enemyEntity.Name).Replace("%s", languageSkill.ToString()), 5);
                                player.TallySkill(languageSkill, 3);    // BCHG: increased skill uses from 1 in classic on success to make raising language skills easier
                            }
                            else if (languageSkill != DFCareer.Skills.Etiquette && languageSkill != DFCareer.Skills.Streetwise)
                            {
                                player.TallySkill(languageSkill, 1);
                            }
                        }
                    }
                }
            }
        }
Example #20
0
        void FixedUpdate()
        {
            classicUpdateTimer += Time.deltaTime;
            if (classicUpdateTimer >= PlayerEntity.ClassicUpdateInterval)
            {
                classicUpdateTimer = 0;
                classicUpdate      = true;
            }
            else
            {
                classicUpdate = false;
            }

            // Reset whether enemy would be spawned or not in classic.
            if (classicUpdate)
            {
                wouldBeSpawnedInClassic = false;
            }

            // Update whether enemy would be spawned or not in classic.
            // Only check if within the maximum possible distance (Just under 1094 classic units)
            if (classicUpdate && distanceToPlayer < 1094 * MeshReader.GlobalScale)
            {
                float upperXZ      = 0;
                float upperY       = 0;
                float lowerY       = 0;
                bool  playerInside = GameManager.Instance.PlayerGPS.GetComponent <PlayerEnterExit>().IsPlayerInside;

                if (!playerInside)
                {
                    upperXZ = classicSpawnDespawnExterior;
                }
                else
                {
                    if (!wouldBeSpawnedInClassic)
                    {
                        upperXZ = classicSpawnXZDist;
                        upperY  = classicSpawnYDistUpper;
                        lowerY  = classicSpawnYDistLower;
                    }
                    else
                    {
                        upperXZ = classicDespawnXZDist;
                        upperY  = classicDespawnYDist;
                    }
                }

                float YDiffToPlayer      = transform.position.y - Player.transform.position.y;
                float YDiffToPlayerAbs   = Mathf.Abs(YDiffToPlayer);
                float distanceToPlayerXZ = Mathf.Sqrt(distanceToPlayer * distanceToPlayer - YDiffToPlayerAbs * YDiffToPlayerAbs);

                wouldBeSpawnedInClassic = true;

                if (distanceToPlayerXZ > upperXZ)
                {
                    wouldBeSpawnedInClassic = false;
                }

                if (playerInside)
                {
                    if (lowerY == 0)
                    {
                        if (YDiffToPlayerAbs > upperY)
                        {
                            wouldBeSpawnedInClassic = false;
                        }
                    }
                    else if (YDiffToPlayer < lowerY || YDiffToPlayer > upperY)
                    {
                        wouldBeSpawnedInClassic = false;
                    }
                }
            }

            if (classicUpdate)
            {
                classicTargetUpdateTimer += Time.deltaTime / systemTimerUpdatesDivisor;

                if (entityBehaviour.Target != null && entityBehaviour.Target.Entity.CurrentHealth <= 0)
                {
                    entityBehaviour.Target = null;
                }

                // NoTarget mode
                if ((GameManager.Instance.PlayerEntity.NoTargetMode || !motor.IsHostile) && entityBehaviour.Target == Player)
                {
                    entityBehaviour.Target = null;
                }

                if (entityBehaviour.Target == null)
                {
                    lastKnownTargetPos = ResetPlayerPos;
                }

                if ((motor.IsHostile && entityBehaviour.Target == null) || classicTargetUpdateTimer > 10) // Timing is 200 in classic, about 10 seconds.
                {
                    classicTargetUpdateTimer = 0f;

                    // Is enemy in area around player or can see player?
                    if (wouldBeSpawnedInClassic || playerInSight)
                    {
                        entityBehaviour.Target = GetTarget();
                    }

                    // Make targeted character also target this character if it doesn't have a target yet.
                    if (entityBehaviour.Target != null && entityBehaviour.Target.Target == null)
                    {
                        entityBehaviour.Target.Target = entityBehaviour;
                    }
                }
            }

            if (Player != null)
            {
                // Get distance to player
                Vector3 toPlayer = Player.transform.position - transform.position;
                distanceToPlayer = toPlayer.magnitude;

                // If out of classic spawn range, still check for direct LOS to player so that enemies who see player will
                // try to attack.
                if (!wouldBeSpawnedInClassic)
                {
                    distanceToTarget  = distanceToPlayer;
                    directionToTarget = toPlayer.normalized;
                    playerInSight     = CanSeeTarget(Player);
                }

                Vector3 toTarget = ResetPlayerPos;
                if (entityBehaviour.Target != null)
                {
                    toTarget = entityBehaviour.Target.transform.position - transform.position;
                }

                if (toTarget != ResetPlayerPos)
                {
                    distanceToTarget  = toTarget.magnitude;
                    directionToTarget = toTarget.normalized;
                }

                if (entityBehaviour.Target == null)
                {
                    targetInSight  = false;
                    detectedTarget = false;
                    return;
                }

                targetInSight = CanSeeTarget(entityBehaviour.Target);

                // Classic stealth mechanics would be interfered with by hearing, so only enable
                // hearing if the enemy has detected the target. If target is visible we can omit hearing.
                if (detectedTarget && !targetInSight)
                {
                    targetInEarshot = CanHearTarget(entityBehaviour.Target);
                }
                else
                {
                    targetInEarshot = false;
                }

                // Note: In classic an enemy can continue to track the player as long as their
                // giveUpTimer is > 0. Since the timer is reset to 200 on every detection this
                // would make chameleon and shade essentially useless, since the enemy is sure
                // to detect the player during one of the many AI updates. Here, the enemy has to
                // successfully see through the illusion spell each classic update to continue
                // to know where the player is.
                if (classicUpdate)
                {
                    blockedByIllusionEffect = BlockedByIllusionEffect();
                    if (lastHadLOSTimer > 0)
                    {
                        lastHadLOSTimer--;
                    }
                }

                if (!blockedByIllusionEffect && (targetInSight || targetInEarshot))
                {
                    detectedTarget     = true;
                    lastKnownTargetPos = entityBehaviour.Target.transform.position;
                    lastHadLOSTimer    = 200f;
                }
                else if (!blockedByIllusionEffect && StealthCheck())
                {
                    detectedTarget = true;

                    // Only get the target's location from the stealth check if we haven't had
                    // actual LOS for a while. This gives better pursuit behavior since enemies
                    // will go to the last spot they saw the player instead of walking into walls.
                    if (lastHadLOSTimer <= 0)
                    {
                        lastKnownTargetPos = entityBehaviour.Target.transform.position;
                    }
                }
                else
                {
                    detectedTarget = false;
                }

                if (detectedTarget && !hasEncounteredPlayer && entityBehaviour.Target == Player)
                {
                    hasEncounteredPlayer = true;

                    // Check appropriate language skill to see if player can pacify enemy
                    DaggerfallEntityBehaviour entityBehaviour = GetComponent <DaggerfallEntityBehaviour>();
                    if (entityBehaviour && motor &&
                        (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass))
                    {
                        DFCareer.Skills languageSkill = enemyEntity.GetLanguageSkill();
                        if (languageSkill != DFCareer.Skills.None)
                        {
                            PlayerEntity player = GameManager.Instance.PlayerEntity;
                            if (FormulaHelper.CalculateEnemyPacification(player, languageSkill))
                            {
                                motor.IsHostile = false;
                                DaggerfallUI.AddHUDText(HardStrings.languagePacified.Replace("%e", enemyEntity.Name).Replace("%s", languageSkill.ToString()), 5);
                                player.TallySkill(languageSkill, 3);    // BCHG: increased skill uses from (assumed) 1 in classic on success to make raising language skills easier
                            }
                            else if (languageSkill != DFCareer.Skills.Etiquette && languageSkill != DFCareer.Skills.Streetwise)
                            {
                                player.TallySkill(languageSkill, 1);
                            }
                        }
                    }
                }
            }
        }
Example #21
0
        void FixedUpdate()
        {
            // Clear movement
            if (cancelMovement)
            {
                moveDirection  = Vector3.zero;
                cancelMovement = false;
                ClearActivePlatform();
                ClearFallingDamage();
                return;
            }

            // Handle freeze movement
            if (freezeMotor > 0)
            {
                freezeMotor -= Time.deltaTime;
                if (freezeMotor <= 0)
                {
                    freezeMotor    = 0;
                    CancelMovement = true;
                }
                return;
            }

            if (isClimbing)
            {
                collisionFlags = CollisionFlags.Sides;
            }
            // Get collision flags for swimming as well, so it's possible to climb out of water TODO: Collision flags from swimming aren't working
            else if (fakeLevitate.IsSwimming)
            {
                collisionFlags = fakeLevitate.CollisionFlags;
            }

            // Climbing
            uint gameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime();

            if (!InputManager.Instance.HasAction(InputManager.Actions.MoveForwards) ||
                (collisionFlags & CollisionFlags.Sides) == 0 ||
                failedClimbingCheck ||
                fakeLevitate.IsLevitating ||
                isRiding ||
                Vector2.Distance(lastHorizontalPosition, new Vector2(controller.transform.position.x, controller.transform.position.z)) >= (0.003f))    // Approximation based on observing classic in-game
            {
                isClimbing = false;
                showClimbingModeMessage = true;
                climbingStartTimer      = 0;
                timeOfLastClimbingCheck = gameMinutes;
            }
            else
            {
                if (climbingStartTimer <= (systemTimerUpdatesPerSecond * 14))
                {
                    climbingStartTimer += Time.deltaTime;
                }
                else
                {
                    if (!isClimbing)
                    {
                        if (showClimbingModeMessage)
                        {
                            DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.climbingMode);
                        }
                        // Disable further showing of climbing mode message until current climb attempt is stopped
                        // to keep it from filling message log
                        showClimbingModeMessage = false;
                        isClimbing = true;
                    }

                    // Initial check to start climbing
                    if ((gameMinutes - timeOfLastClimbingCheck) > 18)
                    {
                        Entity.PlayerEntity player = GameManager.Instance.PlayerEntity;
                        player.TallySkill(DFCareer.Skills.Climbing, 1);
                        timeOfLastClimbingCheck = gameMinutes;
                        if (UnityEngine.Random.Range(1, 101) > 95)
                        {
                            if (UnityEngine.Random.Range(1, 101) > player.Skills.GetLiveSkillValue(DFCareer.Skills.Climbing))
                            {
                                isClimbing          = false;
                                failedClimbingCheck = true;
                            }
                        }
                    }
                }
            }

            if (isClimbing)
            {
                falling = false;
                ClimbMovement();
            }

            // Do nothing if player fake levitating/swimming or climbing - replacement motor will take over movement for levitating/swimming
            if (fakeLevitate && (fakeLevitate.IsLevitating || fakeLevitate.IsSwimming) || isClimbing)
            {
                return;
            }

            //float inputX = Input.GetAxis("Horizontal");
            //float inputY = Input.GetAxis("Vertical");
            float inputX = InputManager.Instance.Horizontal;
            float inputY = InputManager.Instance.Vertical;
            // If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed
            float inputModifyFactor = (inputX != 0.0f && inputY != 0.0f && limitDiagonalSpeed) ? .7071f : 1.0f;

            // Player assumed to be in movement for now
            standingStill = false;

            if (grounded)
            {
                // Set standing still while grounded flag
                // Casting moveDirection to a Vector2 so constant downward force of gravity not included in magnitude
                standingStill = (new Vector2(moveDirection.x, moveDirection.z).magnitude == 0);

                if (jumping)
                {
                    jumping = false;
                }
                bool sliding = false;
                // See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point,
                // because that interferes with step climbing amongst other annoyances
                if (Physics.Raycast(myTransform.position, -Vector3.up, out hit, rayDistance))
                {
                    if (Vector3.Angle(hit.normal, Vector3.up) > slideLimit)
                    {
                        sliding = true;
                    }
                }
                // However, just raycasting straight down from the center can fail when on steep slopes
                // So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead
                else
                {
                    Physics.Raycast(contactPoint + Vector3.up, -Vector3.up, out hit);
                    if (Vector3.Angle(hit.normal, Vector3.up) > slideLimit)
                    {
                        sliding = true;
                    }
                }

                // If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine
                if (falling)
                {
                    falling = false;
                    float fallDistance = fallStartLevel - myTransform.position.y;
                    if (fallDistance > fallingDamageThreshold)
                    {
                        FallingDamageAlert(fallDistance);
                    }
                    else if (fallDistance > fallingDamageThreshold / 2f)
                    {
                        BadFallDetected(fallDistance);
                    }
                    //if (myTransform.position.y < fallStartLevel - fallingDamageThreshold)
                    //    FallingDamageAlert(fallDistance);
                }

                // Get walking/crouching/riding speed
                speed = GetBaseSpeed();

                if (!riding)
                {
                    if (!isCrouching)
                    {
                        controller.height = standingHeight;
                    }

                    try
                    {
                        // If running isn't on a toggle, then use the appropriate speed depending on whether the run button is down
                        if (!toggleRun && InputManager.Instance.HasAction(InputManager.Actions.Run))
                        {
                            speed = GetRunSpeed(speed);
                        }
                    }
                    catch
                    {
                        speed = GetRunSpeed(speed);
                    }
                }

                // Handle sneak key. Reduces movement speed to half, then subtracts 1 in classic speed units
                if (InputManager.Instance.HasAction(InputManager.Actions.Sneak))
                {
                    speed /= 2;
                    speed -= (1 / classicToUnitySpeedUnitRatio);
                }

                // If sliding (and it's allowed), or if we're on an object tagged "Slide", get a vector pointing down the slope we're on
                if ((sliding && slideWhenOverSlopeLimit) || (slideOnTaggedObjects && hit.collider.tag == "Slide"))
                {
                    Vector3 hitNormal = hit.normal;
                    moveDirection = new Vector3(hitNormal.x, -hitNormal.y, hitNormal.z);
                    Vector3.OrthoNormalize(ref hitNormal, ref moveDirection);
                    moveDirection *= slideSpeed;
                    playerControl  = false;
                }
                // Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines
                else
                {
                    moveDirection = new Vector3(inputX * inputModifyFactor, -antiBumpFactor, inputY * inputModifyFactor);
                    moveDirection = myTransform.TransformDirection(moveDirection) * speed;
                    playerControl = true;
                }

                try
                {
                    // Jump! But only if the jump button has been released and player has been grounded for a given number of frames
                    if (!InputManager.Instance.HasAction(InputManager.Actions.Jump))
                    {
                        jumpTimer++;
                    }
                    //if (!Input.GetButton("Jump"))
                    //    jumpTimer++;
                    else if (jumpTimer >= antiBunnyHopFactor)
                    {
                        moveDirection.y = jumpSpeed;
                        jumpTimer       = 0;
                        jumping         = true;

                        // Modify crouching jump speed
                        if (isCrouching)
                        {
                            moveDirection.y *= crouchingJumpDelta;
                        }
                    }
                    else
                    {
                        jumping = false;
                    }
                }
                catch
                {
                }
            }
            else
            {
                // If we stepped over a cliff or something, set the height at which we started falling
                if (!falling)
                {
                    falling        = true;
                    fallStartLevel = myTransform.position.y;
                }

                // If air control is allowed, check movement but don't touch the y component
                if (airControl && playerControl)
                {
                    moveDirection.x = inputX * speed * inputModifyFactor;
                    moveDirection.z = inputY * speed * inputModifyFactor;
                    moveDirection   = myTransform.TransformDirection(moveDirection);
                }
            }

            // Apply gravity
            moveDirection.y -= gravity * Time.deltaTime;

            // If we hit something above us AND we are moving up, reverse vertical movement
            if ((controller.collisionFlags & CollisionFlags.Above) != 0)
            {
                if (moveDirection.y > 0)
                {
                    moveDirection.y = -moveDirection.y;
                }
            }

            // Moving platform support
            if (activePlatform != null)
            {
                var newGlobalPlatformPoint = activePlatform.TransformPoint(activeLocalPlatformPoint);
                var moveDistance           = (newGlobalPlatformPoint - activeGlobalPlatformPoint);
                if (moveDistance != Vector3.zero)
                {
                    controller.Move(moveDistance);
                }
                //lastPlatformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;

                // If you want to support moving platform rotation as well:
                var newGlobalPlatformRotation = activePlatform.rotation * activeLocalPlatformRotation;
                var rotationDiff = newGlobalPlatformRotation * Quaternion.Inverse(activeGlobalPlatformRotation);

                // Prevent rotation of the local up vector
                rotationDiff = Quaternion.FromToRotation(rotationDiff * transform.up, transform.up) * rotationDiff;

                transform.rotation = rotationDiff * transform.rotation;
            }
            //else
            //{
            //    lastPlatformVelocity = Vector3.zero;
            //}

            activePlatform = null;

            // Move the controller, and set grounded true or false depending on whether we're standing on something
            collisionFlags = controller.Move(moveDirection * Time.deltaTime);

            // Get pre-movement position for climbing check
            lastHorizontalPosition = new Vector2(controller.transform.position.x, controller.transform.position.z);

            grounded = (collisionFlags & CollisionFlags.Below) != 0;

            // Moving platforms support
            if (activePlatform != null)
            {
                activeGlobalPlatformPoint = transform.position;
                activeLocalPlatformPoint  = activePlatform.InverseTransformPoint(transform.position);

                // If you want to support moving platform rotation as well:
                activeGlobalPlatformRotation = transform.rotation;
                activeLocalPlatformRotation  = Quaternion.Inverse(activePlatform.rotation) * transform.rotation;
            }
        }
        // Display a shop quality level
        private DaggerfallMessageBox PresentShopQuality(StaticBuilding building)
        {
            const int qualityLevel1TextId = 266;    // "Incense and soft music soothe your nerves"
            const int qualityLevel2TextId = 267;    // "The shop is better appointed than many"
            const int qualityLevel3TextId = 268;    // "The shop is laid out in a practical"
            const int qualityLevel4TextId = 269;    // "Sturdy shelves, cobbled together"
            const int qualityLevel5TextId = 270;    // "Rusty relics lie wherever they were last tossed"

            // Get building directory for location
            BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory();

            if (!buildingDirectory)
            {
                return(null);
            }

            // Get detailed building data from directory
            BuildingSummary buildingSummary;

            if (!buildingDirectory.GetBuildingSummary(building.buildingKey, out buildingSummary))
            {
                return(null);
            }

            // Do nothing if not a shop
            if (!RMBLayout.IsShop(buildingSummary.BuildingType))
            {
                return(null);
            }

            // Set quality level text ID from quality value 01-20
            // UESP states this is building quality / 4 but Daggerfall uses manual thresholds
            int qualityTextId;

            if (buildingSummary.Quality <= 3)
            {
                qualityTextId = qualityLevel5TextId;        // 01 - 03
            }
            else if (buildingSummary.Quality <= 7)
            {
                qualityTextId = qualityLevel4TextId;        // 04 - 07
            }
            else if (buildingSummary.Quality <= 13)
            {
                qualityTextId = qualityLevel3TextId;        // 08 - 13
            }
            else if (buildingSummary.Quality <= 17)
            {
                qualityTextId = qualityLevel2TextId;        // 14 - 17
            }
            else
            {
                qualityTextId = qualityLevel1TextId;        // 18 - 20
            }
            // Log quality of building entered for debugging
            //Debug.Log("Entered store with quality of " + buildingData.Quality);

            // Output quality text based on settings
            switch (DaggerfallUnity.Settings.ShopQualityPresentation)
            {
            case 0:         // Display popup as per classic
                return(DaggerfallUI.MessageBox(qualityTextId));

            case 1:         // Display HUD text only with variable delay
                TextFile.Token[] tokens = DaggerfallUnity.Instance.TextProvider.GetRSCTokens(qualityTextId);
                for (int i = 0; i < tokens.Length; i++)
                {
                    if (tokens[i].formatting == TextFile.Formatting.Text)
                    {
                        DaggerfallUI.AddHUDText(tokens[i].text, DaggerfallUnity.Settings.ShopQualityHUDDelay);
                    }
                }
                break;

            case 2:         // Display nothing about shop quality
            default:
                return(null);
            }

            return(null);
        }
Example #23
0
        void Update()
        {
            if (mainCamera == null)
            {
                return;
            }

            // Fire ray into scene
            if (InputManager.Instance.ActionStarted(InputManager.Actions.ActivateCenterObject))
            {
                // Using RaycastAll as hits can be blocked by decorations or other models
                // When this happens activation feels unresponsive to player
                // Also processing hit detection in order of priority
                Ray          ray = new Ray(mainCamera.transform.position, mainCamera.transform.forward);
                RaycastHit[] hits;
                hits = Physics.RaycastAll(ray, RayDistance);
                if (hits != null)
                {
                    // Check each hit in range for action, exit on first valid action processed
                    for (int i = 0; i < hits.Length; i++)
                    {
                        // Check for a static door hit
                        Transform             doorOwner;
                        DaggerfallStaticDoors doors = GetDoors(hits[i].transform, out doorOwner);
                        if (doors && playerEnterExit)
                        {
                            StaticDoor door;
                            if (doors.HasHit(hits[i].point, out door))
                            {
                                if (door.doorType == DoorTypes.Building && !playerEnterExit.IsPlayerInside)
                                {
                                    // Hit door while outside, transition inside
                                    playerEnterExit.TransitionInterior(doorOwner, door, true);
                                    return;
                                }
                                else if (door.doorType == DoorTypes.Building && playerEnterExit.IsPlayerInside)
                                {
                                    // Hit door while inside, transition outside
                                    playerEnterExit.TransitionExterior(true);
                                    return;
                                }
                                else if (door.doorType == DoorTypes.DungeonEntrance && !playerEnterExit.IsPlayerInside)
                                {
                                    if (playerGPS)
                                    {
                                        // Hit dungeon door while outside, transition inside
                                        playerEnterExit.TransitionDungeonInterior(doorOwner, door, playerGPS.CurrentLocation, true);
                                        return;
                                    }
                                }
                                else if (door.doorType == DoorTypes.DungeonExit && playerEnterExit.IsPlayerInside)
                                {
                                    // Hit dungeon exit while inside, transtion outside
                                    playerEnterExit.TransitionDungeonExterior(true);
                                    return;
                                }
                            }
                        }

                        // Check for an action door hit
                        DaggerfallActionDoor actionDoor;
                        if (ActionDoorCheck(hits[i], out actionDoor))
                        {
                            actionDoor.ToggleDoor();
                        }

                        // Check for action record hit
                        DaggerfallAction action;
                        if (ActionCheck(hits[i], out action))
                        {
                            action.Receive(this.gameObject, DaggerfallAction.TriggerTypes.Direct);
                        }

                        // Check for lootable object hit
                        DaggerfallLoot loot;
                        if (LootCheck(hits[i], out loot))
                        {
                            // For bodies, check has treasure first
                            if (loot.ContainerType == LootContainerTypes.CorpseMarker && loot.Items.Count == 0)
                            {
                                DaggerfallUI.AddHUDText(HardStrings.theBodyHasNoTreasure);
                                return;
                            }

                            // Open inventory window with loot as remote target
                            DaggerfallUI.Instance.InventoryWindow.LootTarget = loot;
                            DaggerfallUI.PostMessage(DaggerfallUIMessages.dfuiOpenInventoryWindow);
                        }
                    }
                }
            }
        }
Example #24
0
        public void ClimbingCheck(ref CollisionFlags collisionFlags)
        {
            // Get pre-movement position for climbing check
            lastHorizontalPosition = new Vector2(controller.transform.position.x, controller.transform.position.z);

            if (isClimbing)
            {
                collisionFlags = CollisionFlags.Sides;
            }
            // Get collision flags for swimming as well, so it's possible to climb out of water TODO: Collision flags from swimming aren't working
            else if (levitateMotor.IsSwimming)
            {
                collisionFlags = levitateMotor.CollisionFlags;
            }

            // Climbing
            uint gameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime();

            if (!InputManager.Instance.HasAction(InputManager.Actions.MoveForwards) ||
                (collisionFlags & CollisionFlags.Sides) == 0 ||
                failedClimbingCheck ||
                levitateMotor.IsLevitating ||
                playerMotor.IsRiding ||
                Vector2.Distance(lastHorizontalPosition, new Vector2(controller.transform.position.x, controller.transform.position.z)) >= (0.003f))    // Approximation based on observing classic in-game
            {
                isClimbing = false;
                showClimbingModeMessage = true;
                climbingStartTimer      = 0;
                timeOfLastClimbingCheck = gameMinutes;
            }
            else
            {
                if (climbingStartTimer <= (playerMotor.systemTimerUpdatesPerSecond * 14))
                {
                    climbingStartTimer += Time.deltaTime;
                }
                else
                {
                    if (!isClimbing)
                    {
                        if (showClimbingModeMessage)
                        {
                            DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.climbingMode);
                        }
                        // Disable further showing of climbing mode message until current climb attempt is stopped
                        // to keep it from filling message log
                        showClimbingModeMessage = false;
                        isClimbing = true;
                    }

                    // Initial check to start climbing
                    if ((gameMinutes - timeOfLastClimbingCheck) > 18)
                    {
                        Entity.PlayerEntity player = GameManager.Instance.PlayerEntity;
                        player.TallySkill(DFCareer.Skills.Climbing, 1);
                        timeOfLastClimbingCheck = gameMinutes;
                        if (UnityEngine.Random.Range(1, 101) > 95)
                        {
                            if (UnityEngine.Random.Range(1, 101) > player.Skills.GetLiveSkillValue(DFCareer.Skills.Climbing))
                            {
                                isClimbing          = false;
                                failedClimbingCheck = true;
                            }
                        }
                    }
                }
            }

            if (isClimbing)
            {
                ClimbMovement();
            }
        }
Example #25
0
        void FixedUpdate()
        {
            classicUpdateTimer += Time.deltaTime;
            if (classicUpdateTimer >= PlayerEntity.ClassicUpdateInterval)
            {
                classicUpdateTimer = 0;
                classicUpdate      = true;
            }
            else
            {
                classicUpdate = false;
            }

            if (Player != null)
            {
                Vector3 toPlayer = Player.transform.position - transform.position;
                directionToPlayer = toPlayer.normalized;
                distanceToPlayer  = toPlayer.magnitude;

                playerInSight = CanSeePlayer();

                // Classic stealth mechanics would be interfered with by hearing, so only enable
                // hearing if the enemy has detected the player. If player has been seen we can omit hearing.
                if (detectedPlayer && !playerInSight)
                {
                    playerInEarshot = CanHearPlayer();
                }
                else
                {
                    playerInEarshot = false;
                }

                // Note: In classic an enemy can continue to track the player as long as their
                // giveUpTimer is > 0. Since the timer is reset to 200 on every detection this
                // would make chameleon and shade essentially useless, since the enemy is sure
                // to detect the player during one of the many AI updates. Here, the enemy has to
                // successfully see through the illusion spell each classic update to continue
                // to know where the player is.
                if (classicUpdate)
                {
                    blockedByIllusionEffect = BlockedByIllusionEffect();
                    if (lastHadLOSTimer > 0)
                    {
                        lastHadLOSTimer--;
                    }
                }

                if (!blockedByIllusionEffect && (playerInSight || playerInEarshot))
                {
                    detectedPlayer     = true;
                    lastKnownPlayerPos = Player.transform.position;
                    lastHadLOSTimer    = 200f;
                }
                else if (!blockedByIllusionEffect && StealthCheck())
                {
                    detectedPlayer = true;

                    // Only get the player's location from the stealth check if we haven't had
                    // actual LOS for a while. This gives better pursuit behavior since enemies
                    // will go to the last spot they saw the player instead of walking into walls.
                    if (lastHadLOSTimer <= 0)
                    {
                        lastKnownPlayerPos = Player.transform.position;
                    }
                }
                else
                {
                    detectedPlayer = false;
                }

                if (detectedPlayer && !hasEncounteredPlayer)
                {
                    hasEncounteredPlayer = true;

                    // Check appropriate language skill to see if player can pacify enemy
                    DaggerfallEntityBehaviour entityBehaviour = GetComponent <DaggerfallEntityBehaviour>();
                    EnemyMotor motor = GetComponent <EnemyMotor>();
                    if (entityBehaviour && motor &&
                        (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass))
                    {
                        EnemyEntity     enemyEntity   = entityBehaviour.Entity as EnemyEntity;
                        DFCareer.Skills languageSkill = enemyEntity.GetLanguageSkill();
                        if (languageSkill != DFCareer.Skills.None)
                        {
                            PlayerEntity player = GameManager.Instance.PlayerEntity;
                            if (FormulaHelper.CalculateEnemyPacification(player, languageSkill))
                            {
                                motor.IsHostile = false;
                                DaggerfallUI.AddHUDText(HardStrings.languagePacified.Replace("%e", enemyEntity.Name).Replace("%s", languageSkill.ToString()), 5);
                                player.TallySkill(languageSkill, 3);    // BCHG: increased skill uses from (assumed) 1 in classic on success to make raising language skills easier
                            }
                            else if (languageSkill != DFCareer.Skills.Etiquette && languageSkill != DFCareer.Skills.Streetwise)
                            {
                                player.TallySkill(languageSkill, 1);
                            }
                        }
                    }
                }
            }
        }
Example #26
0
        /// <summary>
        /// Transition player through an exterior door into building interior.
        /// </summary>
        /// <param name="doorOwner">Parent transform owning door array..</param>
        /// <param name="door">Exterior door player clicked on.</param>
        public void TransitionInterior(Transform doorOwner, StaticDoor door, bool doFade = false, bool start = true)
        {
            // Ensure we have component references
            if (!ReferenceComponents())
            {
                return;
            }

            // Copy owner position to door
            // This ensures the door itself is all we need to reposition interior
            // Useful when loading a save and doorOwner is null (as outside world does not exist)
            if (doorOwner)
            {
                door.ownerPosition = doorOwner.position;
                door.ownerRotation = doorOwner.rotation;
            }

            if (!start)
            {
                // Update scene cache from serializable state for exterior->interior transition
                SaveLoadManager.CacheScene(world.SceneName);
                // Explicitly deregister all stateful objects since exterior isn't destroyed
                SaveLoadManager.DeregisterAllSerializableGameObjects(true);
                // Clear all stateful objects from world loose object tracking
                world.ClearStatefulLooseObjects();
            }

            // Raise event
            RaiseOnPreTransitionEvent(TransitionType.ToBuildingInterior, door);

            // Ensure expired rooms are removed
            GameManager.Instance.PlayerEntity.RemoveExpiredRentedRooms();

            // Get climate
            ClimateBases climateBase = ClimateBases.Temperate;

            if (OverrideLocation)
            {
                climateBase = OverrideLocation.Summary.Climate;
            }
            else if (playerGPS)
            {
                climateBase = ClimateSwaps.FromAPIClimateBase(playerGPS.ClimateSettings.ClimateType);
            }

            // Layout interior
            // This needs to be done first so we know where the enter markers are
            GameObject newInterior = new GameObject(DaggerfallInterior.GetSceneName(playerGPS.CurrentLocation, door));

            newInterior.hideFlags = defaultHideFlags;
            interior = newInterior.AddComponent <DaggerfallInterior>();

            // Try to layout interior
            // If we fail for any reason, use that old chestnut "this house has nothing of value"
            try
            {
                interior.DoLayout(doorOwner, door, climateBase, buildingDiscoveryData);
            }
            catch
            {
                DaggerfallUI.AddHUDText(HardStrings.thisHouseHasNothingOfValue);
                Destroy(newInterior);
                return;
            }

            // Position interior directly inside of exterior
            // This helps with finding closest enter/exit point relative to player position
            interior.transform.position = door.ownerPosition + (Vector3)door.buildingMatrix.GetColumn(3);
            interior.transform.rotation = GameObjectHelper.QuaternionFromMatrix(door.buildingMatrix);

            // Position player above closest enter marker
            Vector3 marker;

            if (!interior.FindClosestEnterMarker(transform.position, out marker))
            {
                // Could not find an enter marker, probably not a valid interior
                Destroy(newInterior);
                return;
            }

            // Enumerate all exterior doors belonging to this building
            DaggerfallStaticDoors exteriorStaticDoors = interior.ExteriorDoors;

            if (exteriorStaticDoors && doorOwner)
            {
                List <StaticDoor> buildingDoors = new List <StaticDoor>();
                for (int i = 0; i < exteriorStaticDoors.Doors.Length; i++)
                {
                    if (exteriorStaticDoors.Doors[i].recordIndex == door.recordIndex)
                    {
                        StaticDoor newDoor = exteriorStaticDoors.Doors[i];
                        newDoor.ownerPosition = doorOwner.position;
                        newDoor.ownerRotation = doorOwner.rotation;
                        buildingDoors.Add(newDoor);
                    }
                }
                SetExteriorDoors(buildingDoors.ToArray());
            }

            // Assign new interior to parent
            if (InteriorParent != null)
            {
                newInterior.transform.parent = InteriorParent.transform;
            }

            // Cache some information about this interior
            buildingType = interior.BuildingData.BuildingType;
            factionID    = interior.BuildingData.FactionId;

            // Set player to marker position
            // TODO: Find closest door for player facing
            transform.position = marker + Vector3.up * (controller.height * 0.6f);
            SetStanding();

            EnableInteriorParent();

            // Add quest resources
            GameObjectHelper.AddQuestResourceObjects(SiteTypes.Building, interior.transform, interior.EntryDoor.buildingKey);

            // Update serializable state from scene cache for exterior->interior transition (unless new/load game)
            if (!start)
            {
                SaveLoadManager.RestoreCachedScene(interior.name);
            }

            // Raise event
            RaiseOnTransitionInteriorEvent(door, interior);

            // Fade in from black
            if (doFade)
            {
                DaggerfallUI.Instance.FadeHUDFromBlack();
            }
        }
        /// <summary>
        /// Physically check for wall info between player and wall he's attached to.  Searches in front of player if no wall already set.
        /// </summary>
        private void GetClimbedWallInfo()
        {
            RaycastHit hit;

            Vector3 p1 = controller.transform.position + controller.center + Vector3.up * -controller.height * 0.40f;
            Vector3 p2 = p1 + Vector3.up * controller.height;

            // decide what direction to look towards to get the ledge direction vector
            if (moveScanner.AboveBehindWall != null)
            {
                moveScanner.CutAndPasteAboveBehindWallTo(ref myLedgeDirection);
            }
            else if (moveScanner.BelowBehindWall != null)
            {
                moveScanner.CutAndPasteBelowBehindWallTo(ref myLedgeDirection);
            }

            if (myLedgeDirection == Vector3.zero)
            {
                wallDirection = controller.transform.forward;
            }
            else if (!atOutsideCorner)
            {
                wallDirection = myLedgeDirection;
            }
            else
            {
                wallDirection = -cornerNormalRay.direction;
            }
            // Cast character controller shape forward to see if it is about to hit anything.
            Debug.DrawRay(controller.transform.position, wallDirection, Color.gray);
            if (Physics.CapsuleCast(p1, p2, controller.radius, wallDirection, out hit, controller.radius + 0.1f))
            {
                // Immediately stop climbing if object not valid
                if (!IsClimable(hit.transform))
                {
                    StopClimbing();
                    return;
                }

                // Show climbing message then disable further showing of climbing mode message until current climb attempt is stopped
                if (showClimbingModeMessage)
                {
                    DaggerfallUI.AddHUDText(TextManager.Instance.GetLocalizedText("climbingMode"));
                    showClimbingModeMessage = false;
                }

                // Get the negative horizontal component of the hitnormal, so gabled roofs don't mess it up
                myLedgeDirection = Vector3.ProjectOnPlane(-hit.normal, Vector3.up).normalized;

                // set origin of strafe ray to y level of controller
                // direction is set to hitnormal until it can be adjusted when we have a side movement direction
                myStrafeRay = new Ray(new Vector3(hit.point.x, controller.transform.position.y, hit.point.z), hit.normal);
            }
            else
            {
                if (myLedgeDirection == Vector3.zero && moveScanner.FrontWall != null)
                {
                    moveScanner.CutAndPasteFrontWallTo(ref myLedgeDirection);
                }
            }
        }
        /// <summary>
        /// Transition player through an exterior door into building interior.
        /// </summary>
        /// <param name="doorOwner">Parent transform owning door array..</param>
        /// <param name="door">Exterior door player clicked on.</param>
        public void TransitionInterior(Transform doorOwner, StaticDoor door, bool doFade = false)
        {
            // Ensure we have component references
            if (!ReferenceComponents())
            {
                return;
            }

            // Copy owner position to door
            // This ensures the door itself is all we need to reposition interior
            // Useful when loading a save and doorOwner is null (as outside world does not exist)
            if (doorOwner)
            {
                door.ownerPosition = doorOwner.position;
                door.ownerRotation = doorOwner.rotation;
            }

            // Raise event
            RaiseOnPreTransitionEvent(TransitionType.ToBuildingInterior, door);

            // Get climate
            ClimateBases climateBase = ClimateBases.Temperate;

            if (OverrideLocation)
            {
                climateBase = OverrideLocation.Summary.Climate;
            }
            else if (playerGPS)
            {
                climateBase = ClimateSwaps.FromAPIClimateBase(playerGPS.ClimateSettings.ClimateType);
            }

            // Layout interior
            // This needs to be done first so we know where the enter markers are
            GameObject newInterior = new GameObject(string.Format("DaggerfallInterior [Block={0}, Record={1}]", door.blockIndex, door.recordIndex));

            newInterior.hideFlags = defaultHideFlags;
            interior = newInterior.AddComponent <DaggerfallInterior>();

            // Try to layout interior
            // If we fail for any reason, use that old chestnut "this house has nothing of value"
            try
            {
                interior.DoLayout(doorOwner, door, climateBase);
            }
            catch
            {
                DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.thisHouseHasNothingOfValue);
                Destroy(newInterior);
                return;
            }

            // Position interior directly inside of exterior
            // This helps with finding closest enter/exit point relative to player position
            interior.transform.position = door.ownerPosition + (Vector3)door.buildingMatrix.GetColumn(3);
            interior.transform.rotation = GameObjectHelper.QuaternionFromMatrix(door.buildingMatrix);

            // Position player above closest enter marker
            Vector3 marker;

            if (!interior.FindClosestEnterMarker(transform.position, out marker))
            {
                // Could not find an enter marker, probably not a valid interior
                Destroy(newInterior);
                return;
            }

            // Enumerate all exterior doors belonging to this building
            DaggerfallStaticDoors exteriorStaticDoors = interior.ExteriorDoors;

            if (exteriorStaticDoors && doorOwner)
            {
                List <StaticDoor> buildingDoors = new List <StaticDoor>();
                for (int i = 0; i < exteriorStaticDoors.Doors.Length; i++)
                {
                    if (exteriorStaticDoors.Doors[i].recordIndex == door.recordIndex)
                    {
                        StaticDoor newDoor = exteriorStaticDoors.Doors[i];
                        newDoor.ownerPosition = doorOwner.position;
                        newDoor.ownerRotation = doorOwner.rotation;
                        buildingDoors.Add(newDoor);
                    }
                }
                SetExteriorDoors(buildingDoors.ToArray());
            }

            // Assign new interior to parent
            if (InteriorParent != null)
            {
                newInterior.transform.parent = InteriorParent.transform;
            }

            // Cache some information about this interior
            buildingType = interior.BuildingData.BuildingType;

            // Set player to marker position
            // TODO: Find closest door for player facing
            transform.position = marker + Vector3.up * (controller.height * 0.6f);
            SetStanding();

            EnableInteriorParent();

            // Raise event
            RaiseOnTransitionInteriorEvent(door, interior);

            // Fade in from black
            if (doFade)
            {
                DaggerfallUI.Instance.FadeHUDFromBlack();
            }
        }
        void Update()
        {
            if (!dfUnity.IsReady || !playerEnterExit || !PlayerTorch || playerEntity == null || GameManager.IsGamePaused)
            {
                return;
            }

            bool enableTorch = false;

            if (DaggerfallUnity.Settings.PlayerTorchFromItems)
            {
                DaggerfallUnityItem lightSource = playerEntity.LightSource;
                if (lightSource != null)
                {
                    enableTorch      = true;
                    torchLight.range = lightSource.ItemTemplate.capacityOrTarget;
                    // Consume durability / fuel
                    if (Time.realtimeSinceStartup > lastTickTime + tickTimeInterval)
                    {
                        lastTickTime = Time.realtimeSinceStartup;
                        if (lightSource.currentCondition > 0)
                        {
                            lightSource.currentCondition--;
                        }

                        if (lightSource.currentCondition == 0 && DaggerfallUnityItem.CompareItems(playerEntity.LightSource, lightSource))
                        {
                            DaggerfallUI.AddHUDText(TextManager.Instance.GetLocalizedText("lightDies").Replace("%it", lightSource.ItemName));
                            enableTorch = false;
                            playerEntity.LightSource = null;
                            if (!lightSource.IsOfTemplate(ItemGroups.UselessItems2, (int)UselessItems2.Lantern))
                            {
                                playerEntity.Items.RemoveItem(lightSource);
                            }
                        }
                    }

                    if (lightSource.currentCondition < 3)
                    {
                        // Give warning signs if running low of fuel
                        intensityMod = 0.6f + (Mathf.Cos(guttering) * 0.2f);
                        guttering   += Random.Range(-0.02f, 0.06f);
                    }
                    else
                    {
                        intensityMod = 1;
                        guttering    = 0;
                    }
                }
            }
            else
            {
                enableTorch = (!playerEnterExit.IsPlayerInside && dfUnity.WorldTime.Now.IsCityLightsOn) || playerEnterExit.IsPlayerInsideDungeon;
            }
            if (torchLight)
            {
                torchLight.intensity = torchIntensity * DaggerfallUnity.Settings.PlayerTorchLightScale * intensityMod;
            }

            PlayerTorch.SetActive(enableTorch);
        }
Example #30
0
        // Notify player when they enter location rect
        // For exterior towns, print out "You are entering %s".
        // For exterior dungeons, print out flavour text.
        private void PlayerGPS_OnEnterLocationRect(DFLocation location)
        {
            const int set1StartID = 500;
            const int set2StartID = 520;

            if (playerGPS && !isPlayerInside)
            {
                if (location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonLabyrinth ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonKeep ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.DungeonRuin ||
                    location.MapTableData.LocationType == DFRegion.LocationTypes.Graveyard)
                {
                    // Get text ID based on set start and dungeon type index
                    int dungeonTypeIndex = (int)location.MapTableData.DungeonType;
                    int set1ID           = set1StartID + dungeonTypeIndex;
                    int set2ID           = set2StartID + dungeonTypeIndex;

                    // Select two sets of flavour text based on dungeon type
                    string flavourText1 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set1ID);
                    string flavourText2 = DaggerfallUnity.Instance.TextProvider.GetRandomText(set2ID);

                    // Show flavour text a bit longer than in classic
                    DaggerfallUI.AddHUDText(flavourText1, 3);
                    DaggerfallUI.AddHUDText(flavourText2, 3);
                }
                else if (location.MapTableData.LocationType != DFRegion.LocationTypes.Coven &&
                         location.MapTableData.LocationType != DFRegion.LocationTypes.HomeYourShips)
                {
                    // Show "You are entering %s"
                    string youAreEntering = HardStrings.youAreEntering;
                    youAreEntering = youAreEntering.Replace("%s", location.Name);
                    DaggerfallUI.AddHUDText(youAreEntering, 2);

                    // Check room rentals in this location, and display how long any rooms are rented for
                    int          mapId        = playerGPS.CurrentLocation.MapTableData.MapId;
                    PlayerEntity playerEntity = GameManager.Instance.PlayerEntity;
                    playerEntity.RemoveExpiredRentedRooms();
                    List <RoomRental_v1> rooms = playerEntity.GetRentedRooms(mapId);
                    if (rooms.Count > 0)
                    {
                        foreach (RoomRental_v1 room in rooms)
                        {
                            string remainingHours = PlayerEntity.GetRemainingHours(room).ToString();
                            DaggerfallUI.AddHUDText(HardStrings.youHaveRentedRoom.Replace("%s", room.name).Replace("%d", remainingHours), 6);
                        }
                    }

                    if (holidayTextTimer <= 0 && !holidayTextPrimed)
                    {
                        holidayTextTimer  = 2.5f; // Short delay to give save game fade-in time to finish
                        holidayTextPrimed = true;
                    }
                    holidayTextLocation = location;

                    // note Nystul: this next line is not enough to manage questor dictionary update since player might load a savegame in an interior -
                    // so this never gets triggered and questor list is rebuild always as a consequence
                    // a better thing is if talkmanager handles all this by itself without making changes to PlayerEnterExit necessary and use events/delegates
                    // -> so I will outcomment next line but leave it in so that original author stumbles across this comment
                    // fixed this in TalkManager class
                    // TalkManager.Instance.LastExteriorEntered = location.LocationIndex;
                }
            }
        }