/// <summary> /// Layout interior for automap based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayoutAutomap(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; // Get block data blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(door.blockIndex); if (blockData.Type != DFBlock.BlockTypes.Rmb) { throw new Exception(string.Format("Could not load RMB block index {0}", door.blockIndex), null); } // Get record data recordData = blockData.RmbBlock.SubRecords[door.recordIndex]; if (recordData.Interior.Header.Num3dObjectRecords == 0) { throw new Exception(string.Format("No interior 3D models found for record index {0}", door.recordIndex), null); } // Layout interior data AddModels(mapBD); return(true); }
/// <summary> /// Find closest door to player position in world space. /// Owner position and rotation must be set. /// </summary> /// <param name="playerPos">Player position in world space.</param> /// <param name="record">Door record index.</param> /// <param name="doorPosOut">Position of closest door in world space.</param> /// <param name="doorIndexOut">Door index in Doors array of closest door.</param> /// <returns></returns> public static bool FindClosestDoorToPlayer(Vector3 playerPos, StaticDoor[] doors, out Vector3 doorPosOut, out int doorIndexOut) { // Init output doorPosOut = playerPos; doorIndexOut = -1; // Must have door array if (doors == null || doors.Length == 0) return false; // Find closest door to player position float minDistance = float.MaxValue; for (int i = 0; i < doors.Length; i++) { // Get this door centre in world space Vector3 centre = doors[i].ownerRotation * doors[i].buildingMatrix.MultiplyPoint3x4(doors[i].centre) + doors[i].ownerPosition; // Check distance and save closest float distance = Vector3.Distance(playerPos, centre); if (distance < minDistance) { doorPosOut = centre; doorIndexOut = i; minDistance = distance; } } return true; }
/// <summary> /// Layout interior based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayout(Transform doorOwner, StaticDoor door, ClimateBases climateBase, PlayerGPS.DiscoveredBuilding buildingData) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; AssignBlockData(door); // Layout interior data AddModels(buildingData); AddFlats(buildingData); AddPeople(buildingData); AddActionDoors(); AddSpawnPoints(); return(true); }
/// <summary> /// Set block data corresponding to interior. /// </summary> private void AssignBlockData(StaticDoor door) { // Get block data DFLocation location = GameManager.Instance.PlayerGPS.CurrentLocation; DFBlock[] blocks; RMBLayout.GetLocationBuildingData(location, out blocks); bool foundBlock = false; for (int index = 0; index < blocks.Length && !foundBlock; ++index) { if (blocks[index].Index == door.blockIndex) { this.blockData = blocks[index]; foundBlock = true; } } if (!foundBlock || this.blockData.Type != DFBlock.BlockTypes.Rmb) { throw new Exception(string.Format("Could not load RMB block index {0}", door.blockIndex), null); } // Get record data recordData = blockData.RmbBlock.SubRecords[door.recordIndex]; if (recordData.Interior.Header.Num3dObjectRecords == 0) { throw new Exception(string.Format("No interior 3D models found for record index {0}", door.recordIndex), null); } }
/// <summary> /// Check for a door hit in world space. /// </summary> /// <param name="point">Hit point from ray test in world space.</param> /// <param name="doorOut">StaticDoor out if hit found.</param> /// <returns>True if point hits a static door.</returns> public bool HasHit(Vector3 point, out StaticDoor doorOut) { doorOut = new StaticDoor(); if (Doors == null) { return(false); } // Using a single hidden trigger created when testing door positions // This avoids problems with AABBs as trigger rotates nicely with model transform // A trigger is also more useful for debugging as its drawn by editor GameObject go = new GameObject(); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = transform; BoxCollider c = go.AddComponent <BoxCollider>(); c.isTrigger = true; // Test each door in array bool found = false; for (int i = 0; i < Doors.Length; i++) { Quaternion buildingRotation = GameObjectHelper.QuaternionFromMatrix(Doors[i].buildingMatrix); Vector3 doorNormal = buildingRotation * Doors[i].normal; Quaternion facingRotation = Quaternion.LookRotation(doorNormal, Vector3.up); // Setup single trigger position and size over each door in turn // This method plays nice with transforms c.size = Doors[i].size; go.transform.parent = transform; go.transform.position = transform.rotation * Doors[i].buildingMatrix.MultiplyPoint3x4(Doors[i].centre); go.transform.position += transform.position; go.transform.rotation = facingRotation; // Check if hit was inside trigger if (c.bounds.Contains(point)) { found = true; doorOut = Doors[i]; if (doorOut.doorType == DoorTypes.DungeonExit) { break; } } } // Remove temp trigger if (go) { Destroy(go); } return(found); }
/// <summary> /// Check for a door hit in world space. /// </summary> /// <param name="point">Hit point from ray test in world space.</param> /// <param name="doorOut">StaticDoor out if hit found.</param> /// <returns>True if point hits a static door.</returns> public bool HasHit(Vector3 point, out StaticDoor doorOut) { doorOut = new StaticDoor(); if (Doors == null) { return(false); } // Using a single hidden trigger created when testing door positions // This avoids problems with AABBs as trigger rotates nicely with model transform // A trigger is also more useful for debugging as its drawn by editor GameObject go = new GameObject(); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = transform; BoxCollider c = go.AddComponent <BoxCollider>(); c.isTrigger = true; // Test each door in array bool found = false; for (int i = 0; i < Doors.Length; i++) { // Setup single trigger position and size over each door in turn // This method plays nice with transforms c.size = GameObjectHelper.QuaternionFromMatrix(Doors[i].buildingMatrix) * Doors[i].size; go.transform.position = transform.rotation * Doors[i].buildingMatrix.MultiplyPoint3x4(Doors[i].centre); go.transform.position += transform.position; go.transform.rotation = transform.rotation; // Sometimes doors injected from mods don't trigger because hit point is not exactly // on the plane calculated from classic door data so we ensure the collider has some depth if (c.size.x < 1) { c.size = new Vector3(1, c.size.y, c.size.z); } // Check if hit was inside trigger if (c.bounds.Contains(point)) { found = true; doorOut = Doors[i]; break; } } // Remove temp trigger if (go) { Destroy(go); } return(found); }
/// <summary> /// Check for a door hit in world space. /// </summary> /// <param name="point">Hit point from ray test in world space.</param> /// <param name="doorOut">StaticDoor out if hit found.</param> /// <returns>True if point hits a static door.</returns> public bool HasHit(Vector3 point, out StaticDoor doorOut) { doorOut = new StaticDoor(); if (Doors == null) { return(false); } // Using a single hidden trigger created when testing door positions // This avoids problems with AABBs as trigger rotates nicely with model transform // A trigger is also more useful for debugging as its drawn by editor GameObject go = new GameObject(); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = transform; BoxCollider c = go.AddComponent <BoxCollider>(); c.isTrigger = true; // Test each door in array bool found = false; for (int i = 0; i < Doors.Length; i++) { // Setup single trigger position and size over each door in turn // This method plays nice with transforms go.transform.position = transform.rotation * Doors[i].buildingMatrix.MultiplyPoint3x4(Doors[i].centre); c.size = GameObjectHelper.QuaternionFromMatrix(Doors[i].buildingMatrix) * Doors[i].size; go.transform.position += transform.position; go.transform.rotation = transform.rotation; // Deprecated: Bounds checking method. //Vector3 centre = transform.rotation * Doors[i].buildingMatrix.MultiplyPoint3x4(Doors[i].centre) + transform.position; //Vector3 size = new Vector3(50, 90, 50) * MeshReader.GlobalScale; // Native door fit //Bounds bounds = new Bounds(centre, size); // Check if hit was inside trigger if (c.bounds.Contains(point)) { found = true; doorOut = Doors[i]; break; } } // Remove temp trigger if (go) { Destroy(go); } return(found); }
/// <summary> /// Finds all doors of type across multiple door collections. /// </summary> /// <param name="doorCollections">Door collections.</param> /// <param name="type">Type of door to search for.</param> /// <returns>Array of matching doors.</returns> public static StaticDoor[] FindDoorsInCollections(DaggerfallStaticDoors[] doorCollections, DoorTypes type) { List <StaticDoor> doorsOut = new List <StaticDoor>(); foreach (var collection in doorCollections) { for (int i = 0; i < collection.Doors.Length; i++) { if (collection.Doors[i].doorType == type) { StaticDoor newDoor = collection.Doors[i]; newDoor.ownerPosition = collection.transform.position; newDoor.ownerRotation = collection.transform.rotation; doorsOut.Add(newDoor); } } } return(doorsOut.ToArray()); }
/// <summary> /// Layout interior for automap based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayoutAutomap(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; AssignBlockData(door); // Layout interior data AddModels(mapBD); return(true); }
public StaticDoor[] Doors; // Array of doors attached this building or group of buildings #endregion Fields #region Methods /// <summary> /// Finds closest door in any array of static doors. /// Owner position and rotation must be set. /// </summary> /// <param name="position">Position to find closest door to.</param> /// <param name="doors">Door array.</param> /// <returns>Position of closest door in world space.</returns> public static Vector3 FindClosestDoor(Vector3 position, StaticDoor[] doors, out StaticDoor closestDoorOut) { closestDoorOut = new StaticDoor(); Vector3 closestDoorPos = position; float minDistance = float.MaxValue; for (int i = 0; i < doors.Length; i++) { // Get this door centre in world space Vector3 centre = doors[i].ownerRotation * doors[i].buildingMatrix.MultiplyPoint3x4(doors[i].centre) + doors[i].ownerPosition; // Check distance and store closest float distance = Vector3.Distance(position, centre); if (distance < minDistance) { closestDoorPos = centre; minDistance = distance; closestDoorOut = doors[i]; } } return closestDoorPos; }
/// <summary> /// Finds closest door in any array of static doors. /// Owner position and rotation must be set. /// </summary> /// <param name="position">Position to find closest door to.</param> /// <param name="doors">Door array.</param> /// <returns>Position of closest door in world space.</returns> public static Vector3 FindClosestDoor(Vector3 position, StaticDoor[] doors, out StaticDoor closestDoorOut) { closestDoorOut = new StaticDoor(); Vector3 closestDoorPos = position; float minDistance = float.MaxValue; for (int i = 0; i < doors.Length; i++) { // Get this door centre in world space Vector3 centre = doors[i].ownerRotation * doors[i].buildingMatrix.MultiplyPoint3x4(doors[i].centre) + doors[i].ownerPosition; // Check distance and store closest float distance = Vector3.Distance(position, centre); if (distance < minDistance) { closestDoorPos = centre; minDistance = distance; closestDoorOut = doors[i]; } } return(closestDoorPos); }
/// <summary> /// Check for a door hit in world space. /// </summary> /// <param name="point">Hit point from ray test in world space.</param> /// <param name="doorOut">StaticDoor out if hit found.</param> /// <returns>True if point hits a static door.</returns> public bool HasHit(Vector3 point, out StaticDoor doorOut) { doorOut = new StaticDoor(); if (Doors == null) return false; // Using a single hidden trigger created when testing door positions // This avoids problems with AABBs as trigger rotates nicely with model transform // A trigger is also more useful for debugging as its drawn by editor GameObject go = new GameObject(); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = transform; BoxCollider c = go.AddComponent<BoxCollider>(); c.isTrigger = true; // Test each door in array bool found = false; for (int i = 0; i < Doors.Length; i++) { // Setup single trigger position and size over each door in turn // This method plays nice with transforms c.size = GameObjectHelper.QuaternionFromMatrix(Doors[i].buildingMatrix) * Doors[i].size; go.transform.position = transform.rotation * Doors[i].buildingMatrix.MultiplyPoint3x4(Doors[i].centre); go.transform.position += transform.position; go.transform.rotation = transform.rotation; // Check if hit was inside trigger if (c.bounds.Contains(point)) { found = true; doorOut = Doors[i]; break; } } // Remove temp trigger if (go) Destroy(go); return found; }
/// <summary> /// Gets door normal in world space. /// Owner position and rotation must be set. /// </summary> /// <param name="door">Door to calculate normal for.</param> /// <returns>Normal pointing away from door in world.</returns> public static Vector3 GetDoorNormal(StaticDoor door) { return(Vector3.Normalize(door.ownerRotation * door.buildingMatrix.MultiplyVector(door.normal))); }
/// <summary> /// Gets door position in world space. /// Owner position and rotation must be set. /// </summary> /// <param name="index">Door index.</param> /// <returns>Door position in world space.</returns> public static Vector3 GetDoorPosition(StaticDoor door) { Vector3 centre = door.ownerRotation * door.buildingMatrix.MultiplyPoint3x4(door.centre) + door.ownerPosition; return(centre); }
/// <summary> /// Gets door position in world space. /// Owner position and rotation must be set. /// </summary> /// <param name="index">Door index.</param> /// <returns>Door position in world space.</returns> public static Vector3 GetDoorPosition(StaticDoor door) { Vector3 centre = door.ownerRotation * door.buildingMatrix.MultiplyPoint3x4(door.centre) + door.ownerPosition; return centre; }
/// <summary> /// Layout interior based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayout(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) dfUnity = DaggerfallUnity.Instance; // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; // Get block data blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(door.blockIndex); if (blockData.Type != DFBlock.BlockTypes.Rmb) throw new Exception(string.Format("Could not load RMB block index {0}", door.blockIndex), null); // Get record data recordData = blockData.RmbBlock.SubRecords[door.recordIndex]; if (recordData.Interior.Header.Num3dObjectRecords == 0) throw new Exception(string.Format("No interior 3D models found for record index {0}", door.recordIndex), null); // Layout interior data AddModels(); AddFlats(); AddPeople(); AddActionDoors(); return true; }
void InitWhenInInteriorOrDungeon(StaticDoor? door = null, bool initFromLoadingSave = false) { if ((GameManager.Instance.IsPlayerInsideBuilding) && (door.HasValue)) { createIndoorGeometryForAutomap(door.Value); restoreStateAutomapDungeon(true); resetAutomapSettingsFromExternalScript = true; // set flag so external script (DaggerfallAutomapWindow) can pull flag and reset automap values on next window push gameobjectGeometry.SetActive(false); gameobjectBeacons.SetActive(false); } else if ((GameManager.Instance.IsPlayerInsideDungeon) || (GameManager.Instance.IsPlayerInsidePalace)) { createDungeonGeometryForAutomap(); restoreStateAutomapDungeon(!initFromLoadingSave); // if a save game was loaded, do not reset the revisited state (don't set parameter forceNotVisitedInThisRun to true) resetAutomapSettingsFromExternalScript = true; // set flag so external script (DaggerfallAutomapWindow) can pull flag and reset automap values on next window push gameobjectGeometry.SetActive(false); gameobjectBeacons.SetActive(false); } else { Debug.LogError("Error in function InitWhenInInteriorOrDungeon: reached forbidden control flow point"); } }
/// <summary> /// creates the indoor geometry used for automap rendering /// </summary> /// <param name="args"> the static door for loading the correct interior </param> private void createIndoorGeometryForAutomap(StaticDoor door) { String newGeometryName = string.Format("DaggerfallInterior [Block={0}, Record={1}]", door.blockIndex, door.recordIndex); if (gameobjectGeometry != null) { UnityEngine.Object.Destroy(gameobjectGeometry); gameobjectGeometry = null; } gameobjectGeometry = new GameObject("GeometryAutomap (Interior)"); foreach (Transform elem in GameManager.Instance.InteriorParent.transform) { if (elem.name.Contains("DaggerfallInterior")) { // Get climate ClimateBases climateBase = ClimateBases.Temperate; climateBase = ClimateSwaps.FromAPIClimateBase(GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType); // Layout interior GameObject gameobjectInterior = new GameObject(newGeometryName); DaggerfallInterior interior = gameobjectInterior.AddComponent<DaggerfallInterior>(); // automap layout is a simplified layout in contrast to normal layout (less objects) interior.DoLayoutAutomap(null, door, climateBase); gameobjectInterior.transform.SetParent(gameobjectGeometry.transform); // copy position and rotation from real level geometry gameobjectGeometry.transform.position = elem.transform.position; gameobjectGeometry.transform.rotation = elem.transform.rotation; // do this (here in createIndoorGeometryForAutomap()) analog in the same way and the same place like in createDungeonGeometryForAutomap() setupBeacons(); } } // put all objects inside gameobjectGeometry in layer "Automap" SetLayerRecursively(gameobjectGeometry, layerAutomap); gameobjectGeometry.transform.SetParent(gameobjectAutomap.transform); // inject all materials of automap geometry with automap shader and reset MeshRenderer enabled state (this is used for the discovery mechanism) injectMeshAndMaterialProperties(); //oldGeometryName = newGeometryName; }
/// <summary> /// Gets the scene name for the interior behind the given door. /// </summary> public static string GetSceneName(DFLocation location, StaticDoor door) { return(GetSceneName(location.MapTableData.MapId, door.buildingKey)); }
/// <summary> /// Gets door normal in world space. /// Owner position and rotation must be set. /// </summary> /// <param name="door">Door to calculate normal for.</param> /// <returns>Normal pointing away from door in world.</returns> public static Vector3 GetDoorNormal(StaticDoor door) { return Vector3.Normalize(door.ownerRotation * door.buildingMatrix.MultiplyVector(door.normal)); }