/// <summary>Constructor.</summary> public TransitionEventArgs() { TransitionType = PlayerEnterExit.TransitionType.NotDefined; StaticDoor = new StaticDoor(); DaggerfallInterior = null; DaggerfallDungeon = null; }
/// <summary> /// Set static door data to all doors in the given building gameobject. /// </summary> /// <param name="building">The imported gameobject which provides building and doors.</param> /// <param name="staticDoors">The list of static doors for the vanilla building.</param> /// <param name="buildingKey">The key of the building that owns the doors.</param> /// <param name="dontCreateStaticDoors">If true, custom door components request to suppress classic doors.</param> public static void InitDoors(GameObject building, StaticDoor[] staticDoors, int buildingKey, out bool dontCreateStaticDoors) { dontCreateStaticDoors = false; // Set data to all doors in the building CustomDoor[] allCustomDoors = building.GetComponentsInChildren <CustomDoor>(); for (int i = 0; i < allCustomDoors.Length; i++) { if (allCustomDoors[i].DisableClassicDoors == true) { dontCreateStaticDoors = true; } if ((allCustomDoors[i].StaticDoorCopied < 0) || (allCustomDoors[i].StaticDoorCopied > staticDoors.Length)) { string objectName; if (allCustomDoors[i].name == "default") { objectName = allCustomDoors[i].transform.parent.name + "\\default"; } else { objectName = allCustomDoors[i].name; } Debug.LogErrorFormat("StaticDoorCopied for model {0} is outside the valid range for it's Static Door array. Defaulting to 0", objectName); allCustomDoors[i].StaticDoorCopied = 0; } // Make data for the static door; only the common data for the building is needed. StaticDoor staticDoor = staticDoors[allCustomDoors[i].StaticDoorCopied]; staticDoor.buildingKey = buildingKey; allCustomDoors[i].staticDoor = staticDoor; } }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = HideFlags.HideAndDontSave; dungeon = newDungeon.GetComponent <DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } // Cache player starting position and facing to use on exit dungeonEntrancePosition = transform.position; dungeonEntranceForward = transform.forward; // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable dungeon parent if (DungeonParent != null) { DungeonParent.SetActive(true); } // Set player to start position transform.position = dungeon.StartMarker.transform.position + Vector3.up * (controller.height * 0.6f); //// TODO: Find closest exit door to orient player //DaggerfallStaticDoors doors = newDungeon.GetComponent<DaggerfallStaticDoors>(); //if (doors) //{ // Vector3 doorPos; // int doorIndex; // if (doors.FindClosestDoorToPlayer(transform.position, 0, out doorPos, out doorIndex)) // { // } //} // Fix player standing SetStanding(); // Player is now inside dungeon isPlayerInside = true; isPlayerInsideDungeon = true; }
/// <summary>Constructor helper.</summary> public TransitionEventArgs(TransitionType transitionType, StaticDoor staticDoor, DaggerfallInterior daggerfallInterior = null, DaggerfallDungeon daggerfallDungeon = null) : base() { this.TransitionType = transitionType; this.StaticDoor = staticDoor; this.DaggerfallInterior = daggerfallInterior; this.DaggerfallDungeon = daggerfallDungeon; }
protected virtual void RaiseOnTransitionInteriorEvent(StaticDoor staticDoor, DaggerfallInterior daggerfallInterior) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToBuildingInterior, staticDoor, daggerfallInterior); if (OnTransitionInterior != null) { OnTransitionInterior(args); } }
protected virtual void RaiseOnTransitionDungeonInteriorEvent(StaticDoor staticDoor, DaggerfallDungeon daggerfallDungeon) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToDungeonInterior, staticDoor, null, daggerfallDungeon); if (OnTransitionDungeonInterior != null) { OnTransitionDungeonInterior(args); } }
/// <summary> /// Starts player inside building with no exterior world. /// </summary> public void StartBuildingInterior(DFLocation location, StaticDoor exteriorDoor) { // Ensure we have component references if (!ReferenceComponents()) { return; } TransitionInterior(null, exteriorDoor); }
/// <summary> /// Starts player inside building with no exterior world. /// </summary> public void StartBuildingInterior(DFLocation location, StaticDoor exteriorDoor) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Discover building GameManager.Instance.PlayerGPS.DiscoverBuilding(exteriorDoor.buildingKey); TransitionInterior(null, exteriorDoor); }
/// <summary> /// Set static door data to all doors in the given building gameobject. /// </summary> /// <param name="building">The imported gameobject which provides building and doors.</param> /// <param name="staticDoors">The list of static doors for the vanilla building.</param> /// <param name="buildingKey">The key of the building that owns the doors.</param> public static void InitDoors(GameObject building, StaticDoor[] staticDoors, int buildingKey) { // Make data for the static door; only the common data for the building is needed. StaticDoor staticDoor = staticDoors[0]; staticDoor.buildingKey = buildingKey; // Set data to all doors in the building var doors = building.GetComponentsInChildren <CustomDoor>(); for (int i = 0; i < doors.Length; i++) { doors[i].staticDoor = staticDoor; } }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = HideFlags.HideAndDontSave; dungeon = newDungeon.GetComponent <DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } // Cache player starting position and facing to use on exit dungeonEntrancePosition = transform.position; dungeonEntranceForward = transform.forward; // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable dungeon parent if (DungeonParent != null) { DungeonParent.SetActive(true); } // Set player to start position // Not sure how to set facing here as player transitions to a marker, not a door // Could always find closest exit door and use that transform.position = dungeon.StartMarker.transform.position + Vector3.up * (controller.height * 0.6f); SetStanding(); // Player is now inside dungeon isPlayerInside = true; isPlayerInsideDungeon = true; }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = HideFlags.HideAndDontSave; dungeon = newDungeon.GetComponent <DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } // Cache player starting position and facing to use on exit dungeonEntrancePosition = transform.position; dungeonEntranceForward = transform.forward; // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable dungeon parent if (DungeonParent != null) { DungeonParent.SetActive(true); } // Player is now inside dungeon isPlayerInside = true; isPlayerInsideDungeon = true; // Set to start position MovePlayerToDungeonStart(); }
/// <summary> /// Checks for a door hit. /// </summary> /// <param name="raycastHit">The raycast result.</param> /// <param name="door">The door found at hit position.</param> /// <returns>True if a door has been hit.</returns> /// <remarks> /// Modders should ensure that the CustomDoor component is added to the right gameobject. /// This allows to avoid unnecessary seeks on parent/children, which are potentially performance heavy. /// </remarks> public static bool HasHit(RaycastHit raycastHit, out StaticDoor door) { var doors = raycastHit.transform.GetComponents <CustomDoor>(); for (int i = 0; i < doors.Length; i++) { if (doors[i].DoorTrigger.bounds.Contains(raycastHit.point)) { if (doors[i].staticDoor.HasValue) { door = doors[i].staticDoor.Value; return(true); } Debug.LogErrorFormat("Static door for {0} is not set.", raycastHit.transform.name); } } door = new StaticDoor(); return(false); }
/// <summary> /// Gets static door array from door information stored in model data. /// </summary> /// <param name="modelData">Model data for doors.</param> /// <param name="blockIndex">Block index for RMB doors.</param> /// <param name="recordIndex">Record index of interior.</param> /// <param name="buildingMatrix">Individual building matrix.</param> /// <returns>Array of doors in this model data.</returns> public static StaticDoor[] GetStaticDoors(ref ModelData modelData, int blockIndex, int recordIndex, Matrix4x4 buildingMatrix) { // Exit if no doors if (modelData.Doors == null) { return(null); } // Add door triggers StaticDoor[] staticDoors = new StaticDoor[modelData.Doors.Length]; for (int i = 0; i < modelData.Doors.Length; i++) { // Get door and diagonal verts ModelDoor door = modelData.Doors[i]; Vector3 v0 = door.Vert0; Vector3 v2 = door.Vert2; // Get door size const float thickness = 0.025f; Vector3 size = new Vector3(v2.x - v0.x, v2.y - v0.y, v2.z - v0.z) + door.Normal * thickness; // Add door to array StaticDoor newDoor = new StaticDoor() { buildingMatrix = buildingMatrix, doorType = door.Type, blockIndex = blockIndex, recordIndex = recordIndex, doorIndex = door.Index, centre = (v0 + v2) / 2f, normal = door.Normal, size = size, }; staticDoors[i] = newDoor; } return(staticDoors); }
// Custom transition to store building data before entering building private void TransitionInterior(Transform doorOwner, StaticDoor door, bool doFade = false) { // Get building directory for location BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory(); if (!buildingDirectory) { Debug.LogError("PlayerActivate.TransitionInterior() could not retrieve BuildingDirectory."); return; } // Get building discovery data - this is added when player clicks door at exterior PlayerGPS.DiscoveredBuilding db; if (!GameManager.Instance.PlayerGPS.GetDiscoveredBuilding(door.buildingKey, out db)) { Debug.LogErrorFormat("PlayerActivate.TransitionInterior() could not retrieve DiscoveredBuilding for key {0}.", door.buildingKey); return; } // Perform transition playerEnterExit.BuildingDiscoveryData = db; playerEnterExit.TransitionInterior(doorOwner, door, doFade); }
/// <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) { // Ensure we have component references if (!ReferenceComponents()) return; // 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 = HideFlags.HideAndDontSave; interior = newInterior.AddComponent<DaggerfallInterior>(); interior.DoLayout(doorOwner, door, climateBase); // Position interior directly inside of exterior // This helps with finding closest enter/exit point relative to player position interior.transform.position = doorOwner.position + (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; } // Assign new interior to parent if (InteriorParent != null) newInterior.transform.parent = InteriorParent.transform; // Disable exterior parent if (ExteriorParent != null) ExteriorParent.SetActive(false); // Enable interior parent if (InteriorParent != null) InteriorParent.SetActive(true); // Cache some information about this interior buildingType = interior.BuildingData.BuildingType; // Set player to marker position // Not sure how to set facing here as player transitions to a marker, not a door // Could always find closest door and use that transform.position = marker + Vector3.up * (controller.height * 0.6f); SetStanding(); // Player is now inside building isPlayerInside = true; // Raise event RaiseOnTransitionInteriorEvent(door, interior); }
/// <summary> /// Prints debug buttons for editor at bottom. /// </summary> void PrintDebugEditorButtons() { EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(false)); float buttonWidth = 400; float buttonHeight = 30; // Map Tile Location, or Interior or Dungeon Tabs // Two options: Select then Replace/Insert // This zooms map to locations or blocks and allows user to replace or add entry to a location // Dungeon Layout // Dungeon Block // Door // Model / Flat / Event // Option 2: Import Location - Bring up the area first, then pick where to insert it // If Dungeon // Replace existing location or add new location // Add new location loads a dungeon layout, must have exit(s) the lead to location(s), but with // If add new location needs an above ground entrance with a map tile or keyed door // Verifies all features that need to be in area. Modifies prefab // Mod adds interruption in between specific location doors or when streaming a tile // Dungeon // Data for dungeon // Dropdown on how to access it // Override existing place (with mod) // key a specific door // Scripted command (Dialog, falling down a pit, or if you have another area that will reference it later) // Key to a generated location // backup area to send person to if it doesn't work. //DFLocation // DaggerfallLocation // RUNTIME // May be in DaggerFall unity during runtime // Playerenterexit has a location override // Dungeon --> DaggerfallDungeon monobehaviour // chidren have list of dungeon blocks and enemies // has location textures // When dungeon is gone it is removed from the dungeon branch // Outdoor locations have list of doors, which are automaticlaly mapped onto the terrain // DaggerfallStaticDoors // Location contains a dungeon // Has a block index in the static doors list - Block index in BLOCKS.BSA // Record index is for a specific interior (like shop or home) // each location(site) is marked on the world map. Each site can have a number of exterior blocks, and // a dungeon attached to it. // Everything for transport is in player enter exit // DFLocation.RegionMapTable //locationdungeon // Has array of dungeonblock // !! Must overwrite a normal dungeon block with a more base form. // That must simulate any features a normal dungeon would have for compatibility. // LocationIndex? // ?? Where is the index of locations you can intercept for mods? Based on GPS? // Can you intercept a transition request event? // Requests to Maps.bsa // DFValidator:152 // Supports alternate MAPS.BSA from Resources if available // MODIFY THE LOCATIONS INDEX // Have our own data file that has overwrites for regions already. // Each data point is driven by a mod. Use the mod's loading order and existing locations to ensure no overwrites // Have the "final" modified version of the additional map data parsed. // 1.) Wait until the maps file is loaded. // Use the start mod events unless better are found. // 2.) Then edit the maps file index in memory // 3.) Then reload the existing area if needed // ?? Content Reader Get Location is used, how to overwrite that? Does it mandate use of the data file? // Modify mapsfile.cs region record add the new region index in or change it if (GUILayout.Button("Teleport Player to test dungeon area", GUILayout.Width(buttonWidth), GUILayout.Height(buttonHeight))) { // Test if game is running if (IsGameRunning() == false) { return; } Transform doorOwner = null; StaticDoor door = new StaticDoor(); DFLocation location = new DFLocation(); // Assign an override location // Grab player and use their PlayerEnterExit // TransitionDungeonInterior GameManager.Instance.PlayerEnterExit.TransitionDungeonInterior(doorOwner, door, location); } if (GUILayout.Button("Do other thing", GUILayout.Width(buttonWidth), GUILayout.Height(buttonHeight))) { EditorUtility.SetDirty(soData); } PrintLocationInformation(); EditorGUILayout.EndHorizontal(); }
protected virtual void RaiseOnPreTransitionEvent(TransitionType transitionType, StaticDoor staticDoor) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToBuildingInterior, staticDoor); if (OnPreTransition != null) { OnPreTransition(args); } }
protected virtual void RaiseOnTransitionDungeonInteriorEvent(StaticDoor staticDoor, DaggerfallDungeon daggerfallDungeon) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToDungeonInterior, staticDoor, null, daggerfallDungeon); if (OnTransitionDungeonInterior != null) OnTransitionDungeonInterior(args); }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location) { // Ensure we have component references if (!ReferenceComponents()) return; // Override location if specified if (OverrideLocation != null) { DFLocation overrideLocation = dfUnity.ContentReader.MapFileReader.GetLocation(OverrideLocation.Summary.RegionName, OverrideLocation.Summary.LocationName); if (overrideLocation.Loaded) location = overrideLocation; } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToDungeonInterior, door); // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = HideFlags.HideAndDontSave; dungeon = newDungeon.GetComponent<DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } // Cache player starting position and facing to use on exit dungeonEntrancePosition = transform.position; dungeonEntranceForward = transform.forward; // Disable exterior parent if (ExteriorParent != null) ExteriorParent.SetActive(false); // Enable dungeon parent if (DungeonParent != null) DungeonParent.SetActive(true); // Player is now inside dungeon isPlayerInside = true; isPlayerInsideDungeon = true; // Set to start position MovePlayerToDungeonStart(); // Raise event RaiseOnTransitionDungeonInteriorEvent(door, dungeon); }
/// <summary> /// Instantiate base RMB block by DFBlock data. /// </summary> /// <param name="blockData">Block data.</param> /// <param name="layoutX">X coordinate in parent map layout.</param> /// /// <param name="layoutY">Y coordinate in parent map layout.</param> /// <param name="cloneFrom">Prefab to clone from.</param> /// <returns>Block GameObject.</returns> public static GameObject CreateBaseGameObject(ref DFBlock blockData, int layoutX, int layoutY, DaggerfallRMBBlock cloneFrom = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Create gameobject GameObject go; string name = string.Format("DaggerfallBlock [{0}]", blockData.Name); if (cloneFrom != null) { go = GameObjectHelper.InstantiatePrefab(cloneFrom.gameObject, name, null, Vector3.zero); } else { go = new GameObject(name); } // Setup combiner ModelCombiner combiner = null; if (dfUnity.Option_CombineRMB) { combiner = new ModelCombiner(); } // Lists to receive any doors found in this block List <StaticDoor> modelDoors; List <StaticDoor> propDoors; List <StaticBuilding> modelBuildings; // Add models and static props GameObject modelsNode = new GameObject("Models"); modelsNode.transform.parent = go.transform; AddModels(dfUnity, layoutX, layoutY, ref blockData, out modelDoors, out modelBuildings, combiner, modelsNode.transform); AddProps(dfUnity, ref blockData, out propDoors, combiner, modelsNode.transform); // Combine list of doors found in models and props List <StaticDoor> allDoors = new List <StaticDoor>(); if (modelDoors.Count > 0) { allDoors.AddRange(modelDoors); } if (propDoors.Count > 0) { allDoors.AddRange(propDoors); } // Assign building key to each door for (int i = 0; i < allDoors.Count; i++) { StaticDoor door = allDoors[i]; door.buildingKey = BuildingDirectory.MakeBuildingKey((byte)layoutX, (byte)layoutY, (byte)door.recordIndex); allDoors[i] = door; } // Assign building key to each building for (int i = 0; i < modelBuildings.Count; i++) { StaticBuilding building = modelBuildings[i]; building.buildingKey = BuildingDirectory.MakeBuildingKey((byte)layoutX, (byte)layoutY, (byte)building.recordIndex); modelBuildings[i] = building; } // Add static doors component if (allDoors.Count > 0) { AddStaticDoors(allDoors.ToArray(), go); } // Add static buildings component if (modelBuildings.Count > 0) { AddStaticBuildings(modelBuildings.ToArray(), go); } // Apply combiner if (combiner != null) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObjectHelper.CreateCombinedMeshGameObject( combiner, "CombinedModels", modelsNode.transform, dfUnity.Option_SetStaticFlags); } } return(go); }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location, bool doFade = false) { // Ensure we have component references if (!ReferenceComponents()) return; // Override location if specified if (OverrideLocation != null) { DFLocation overrideLocation = dfUnity.ContentReader.MapFileReader.GetLocation(OverrideLocation.Summary.RegionName, OverrideLocation.Summary.LocationName); if (overrideLocation.Loaded) location = overrideLocation; } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToDungeonInterior, door); // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = defaultHideFlags; dungeon = newDungeon.GetComponent<DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } EnableDungeonParent(); // Set to start position MovePlayerToMarker(dungeon.StartMarker); // Find closest dungeon exit door to orient player StaticDoor[] doors = DaggerfallStaticDoors.FindDoorsInCollections(dungeon.StaticDoorCollections, DoorTypes.DungeonExit); if (doors != null && doors.Length > 0) { Vector3 doorPos; int doorIndex; if (DaggerfallStaticDoors.FindClosestDoorToPlayer(transform.position, doors, out doorPos, out doorIndex)) { // Set player facing away from door PlayerMouseLook playerMouseLook = GameManager.Instance.PlayerMouseLook; if (playerMouseLook) { Vector3 normal = DaggerfallStaticDoors.GetDoorNormal(doors[doorIndex]); playerMouseLook.SetFacing(normal); } } } // Raise event RaiseOnTransitionDungeonInteriorEvent(door, dungeon); // Fade in from black if (doFade) DaggerfallUI.Instance.FadeHUDFromBlack(); }
/// <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(); } }
/// <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> /// 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); } } exteriorDoors = 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(); }
protected virtual void RaiseOnTransitionInteriorEvent(StaticDoor staticDoor, DaggerfallInterior daggerfallInterior) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToBuildingInterior, staticDoor, daggerfallInterior); if (OnTransitionInterior != null) OnTransitionInterior(args); }
/// <summary> /// Gets static door array from door information stored in model data. /// </summary> /// <param name="modelData">Model data for doors.</param> /// <param name="blockIndex">Block index for RMB doors.</param> /// <param name="recordIndex">Record index of interior.</param> /// <param name="buildingMatrix">Individual building matrix.</param> /// <returns>Array of doors in this model data.</returns> public static StaticDoor[] GetStaticDoors(ref ModelData modelData, int blockIndex, int recordIndex, Matrix4x4 buildingMatrix) { // Exit if no doors if (modelData.Doors == null) return null; // Add door triggers StaticDoor[] staticDoors = new StaticDoor[modelData.Doors.Length]; for (int i = 0; i < modelData.Doors.Length; i++) { // Get door and diagonal verts ModelDoor door = modelData.Doors[i]; Vector3 v0 = door.Vert0; Vector3 v2 = door.Vert2; // Get door size const float thickness = 0.05f; Vector3 size = new Vector3(v2.x - v0.x, v2.y - v0.y, v2.z - v0.z) + door.Normal * thickness; // Add door to array StaticDoor newDoor = new StaticDoor() { buildingMatrix = buildingMatrix, doorType = door.Type, blockIndex = blockIndex, recordIndex = recordIndex, doorIndex = door.Index, centre = (v0 + v2) / 2f, normal = door.Normal, size = size, }; staticDoors[i] = newDoor; } return staticDoors; }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location, bool doFade = false) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Reset dungeon block on entering dungeon lastPlayerDungeonBlockIndex = -1; playerDungeonBlockData = new DFLocation.DungeonBlock(); // Override location if specified if (OverrideLocation != null) { DFLocation overrideLocation = dfUnity.ContentReader.MapFileReader.GetLocation(OverrideLocation.Summary.RegionName, OverrideLocation.Summary.LocationName); if (overrideLocation.Loaded) { location = overrideLocation; } } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToDungeonInterior, door); // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = defaultHideFlags; dungeon = newDungeon.GetComponent <DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } EnableDungeonParent(); // Set to start position MovePlayerToMarker(dungeon.StartMarker); // Find closest dungeon exit door to orient player StaticDoor[] doors = DaggerfallStaticDoors.FindDoorsInCollections(dungeon.StaticDoorCollections, DoorTypes.DungeonExit); if (doors != null && doors.Length > 0) { Vector3 doorPos; int doorIndex; if (DaggerfallStaticDoors.FindClosestDoorToPlayer(transform.position, doors, out doorPos, out doorIndex)) { // Set player facing away from door PlayerMouseLook playerMouseLook = GameManager.Instance.PlayerMouseLook; if (playerMouseLook) { Vector3 normal = DaggerfallStaticDoors.GetDoorNormal(doors[doorIndex]); playerMouseLook.SetFacing(normal); } } } // Raise event RaiseOnTransitionDungeonInteriorEvent(door, dungeon); // Fade in from black if (doFade) { DaggerfallUI.Instance.FadeHUDFromBlack(); } }
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 } } } }
/// <summary> /// Starts player inside building with no exterior world. /// </summary> public void StartBuildingInterior(DFLocation location, StaticDoor exteriorDoor) { // Ensure we have component references if (!ReferenceComponents()) return; TransitionInterior(null, exteriorDoor); }
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 } } }
/// <summary> /// Respawn player at the specified world coordinates, optionally inside dungeon or building. /// Player can be forced to respawn to closest start marker or origin. /// </summary> public void RespawnPlayer( int worldX, int worldZ, bool insideDungeon, bool insideBuilding, StaticDoor[] exteriorDoors = null, bool forceReposition = false) { // Mark any existing world data for destruction if (dungeon) { Destroy(dungeon.gameObject); } if (interior) { Destroy(interior.gameObject); } // Deregister all serializable objects SaveLoadManager.DeregisterAllSerializableGameObjects(); // Start respawn process isRespawning = true; this.exteriorDoors = exteriorDoors; StartCoroutine(Respawner(worldX, worldZ, insideDungeon, insideBuilding, forceReposition)); }
/// <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) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Get current climate ClimateBases climateBase = ClimateBases.Temperate; 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 = HideFlags.HideAndDontSave; interior = newInterior.AddComponent <DaggerfallInterior>(); interior.DoLayout(doorOwner, door, climateBase); // Position interior directly inside of exterior // This helps with finding closest enter/exit point relative to player position interior.transform.position = doorOwner.position + (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; } // Assign new interior to parent if (InteriorParent != null) { newInterior.transform.parent = InteriorParent.transform; } // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable interior parent if (InteriorParent != null) { InteriorParent.SetActive(true); } // Cache some information about this interior buildingType = interior.BuildingData.BuildingType; // Set player to marker position // Not sure how to set facing here as player transitions to a marker, not a door // Could always find closest door and use that transform.position = marker + Vector3.up * (controller.height * 0.6f); SetStanding(); // Player is now inside building isPlayerInside = true; }
/// <summary> /// Transition player through a dungeon entrance door into dungeon interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Override location if specified if (OverrideLocation != null) { DFLocation overrideLocation = dfUnity.ContentReader.MapFileReader.GetLocation(OverrideLocation.Summary.RegionName, OverrideLocation.Summary.LocationName); if (overrideLocation.Loaded) { location = overrideLocation; } } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToDungeonInterior, door); // Layout dungeon GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform); newDungeon.hideFlags = HideFlags.HideAndDontSave; dungeon = newDungeon.GetComponent <DaggerfallDungeon>(); // Find start marker to position player if (!dungeon.StartMarker) { // Could not find a start marker Destroy(newDungeon); return; } // Cache player starting position and facing to use on exit dungeonEntrancePosition = transform.position; dungeonEntranceForward = transform.forward; // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable dungeon parent if (DungeonParent != null) { DungeonParent.SetActive(true); } // Player is now inside dungeon isPlayerInside = true; isPlayerInsideDungeon = true; // Set to start position MovePlayerToDungeonStart(); // Raise event RaiseOnTransitionDungeonInteriorEvent(door, dungeon); }
private static void AddStaticDoors(StaticDoor[] doors, GameObject target) { DaggerfallStaticDoors c = target.GetComponent<DaggerfallStaticDoors>(); if (c == null) c = target.AddComponent<DaggerfallStaticDoors>(); if (doors != null && target != null) c.Doors = doors; }
protected virtual void RaiseOnPreTransitionEvent(TransitionType transitionType, StaticDoor staticDoor) { TransitionEventArgs args = new TransitionEventArgs(TransitionType.ToBuildingInterior, staticDoor); if (OnPreTransition != null) OnPreTransition(args); }