/// <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> /// Undiscover the specified building in current location. /// used to undiscover residences when they are a quest resource (named residence) when "add dialog" is done for this quest resource or on quest startup /// otherwise previously discovered residences will automatically show up on the automap when used in a quest /// </summary> /// <param name="buildingKey">Building key of building to be undiscovered</param> /// <param name="onlyIfResidence">gets undiscovered only if buildingType is residence</param> public void UndiscoverBuilding(int buildingKey, bool onlyIfResidence = false) { // Must have a location loaded if (!CurrentLocation.Loaded) { return; } // Get building information DiscoveredBuilding db; if (!GetBuildingDiscoveryData(buildingKey, out db)) { return; } // Get location discovery int mapPixelID = MapsFile.GetMapPixelIDFromLongitudeLatitude((int)CurrentLocation.MapTableData.Longitude, CurrentLocation.MapTableData.Latitude); DiscoveredLocation dl = new DiscoveredLocation(); if (discoveredLocations.ContainsKey(mapPixelID)) { dl = discoveredLocations[mapPixelID]; } if (onlyIfResidence && !RMBLayout.IsResidence(db.buildingType)) { return; } if (dl.discoveredBuildings != null && dl.discoveredBuildings.ContainsKey(db.buildingKey)) { dl.discoveredBuildings.Remove(db.buildingKey); } }
/// <summary> /// Sets a variant for a new location. /// </summary> /// <param name="regionIndex">Region index</param> /// <param name="locationName">Location name (as index is assigned at runtime)</param> /// <param name="variant">Variant name</param> /// <returns>True if overwriting an existing variant set for this location</returns> public static bool SetNewLocationVariant(int regionIndex, string locationName, string variant) { int locationIndex = WorldDataReplacement.GetNewDFLocationIndex(regionIndex, locationName); if (locationIndex >= 0) { int locationKey = WorldDataReplacement.MakeLocationKey(regionIndex, locationIndex); bool overwrite = !locationVariants.ContainsKey(locationKey); if (variant == NoVariant) { locationVariants.Remove(locationKey); } else { locationVariants[locationKey] = variant; } if (!newLocationVariants.Contains(locationKey)) { newLocationVariants.Add(locationKey); } Debug.LogFormat("Set variant \"{0}\" for the new location \"{1}\" in region {2}", variant, locationName, regionIndex); RMBLayout.ClearLocationCache(); return(overwrite); } DaggerfallUnity.LogMessage("Failed to set a new location variant.", true); return(false); }
/// <summary> /// Sets block information during scene layout. /// </summary> /// <param name="blockData">DFBlock data.</param> public void SetBlockData(DFBlock blockData) { // Create block summary Name = blockData.Name; BuildingCount = blockData.RmbBlock.SubRecords.Length; Buildings = RMBLayout.GetBuildingData(blockData); }
/// <summary> /// Add interior people flats. /// </summary> private void AddPeople(PlayerGPS.DiscoveredBuilding buildingData) { GameObject node = new GameObject("People Flats"); node.transform.parent = this.transform; bool isMemberOfBuildingGuild = GameManager.Instance.GuildManager.GetGuild(buildingData.factionID).IsMember(); // Add block flats foreach (DFBlock.RmbBlockPeopleRecord obj in recordData.Interior.BlockPeopleRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Import 3D character instead of billboard if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform) != null) { continue; } // Spawn billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add RMB data to billboard dfBillboard.SetRMBPeopleData(obj); // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, entryDoor.buildingKey); // Disable people if shop or building is closed DFLocation.BuildingTypes buildingType = buildingData.buildingType; if ((RMBLayout.IsShop(buildingType) && !GameManager.Instance.PlayerEnterExit.IsPlayerInsideOpenShop) || (buildingType <= DFLocation.BuildingTypes.Palace && !RMBLayout.IsShop(buildingType) && !PlayerActivate.IsBuildingOpen(buildingType))) { go.SetActive(false); } // Disable people if player owns this house else if (DaggerfallBankManager.IsHouseOwned(buildingData.buildingKey)) { go.SetActive(false); } // Disable people if this is TG/DB house and player is not a member else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID != 0 && !isMemberOfBuildingGuild) { go.SetActive(false); } // Disable people if they are TG spymaster, but not in a legit TG house (TODO: spot any other instances for TG/DB) else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID == 0 && npc.Data.factionID == (int)GuildNpcServices.TG_Spymaster) { go.SetActive(false); } } }
/// <summary> /// Add interior people flats. /// </summary> private void AddPeople(PlayerGPS.DiscoveredBuilding buildingData) { GameObject node = new GameObject(peopleFlats); node.transform.parent = this.transform; IGuild guild = GameManager.Instance.GuildManager.GetGuild(buildingData.factionID); bool isMemberOfBuildingGuild = guild.IsMember(); // Add block flats foreach (DFBlock.RmbBlockPeopleRecord obj in recordData.Interior.BlockPeopleRecords) { // Calculate position Vector3 billboardPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; // Make person gameobject GameObject go = MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, node.transform); if (!go) { // Spawn billboard gameobject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, node.transform); // Set position DaggerfallBillboard dfBillboard = go.GetComponent <DaggerfallBillboard>(); go.transform.position = billboardPosition; go.transform.position += new Vector3(0, dfBillboard.Summary.Size.y / 2, 0); // Add RMB data to billboard dfBillboard.SetRMBPeopleData(obj); } // Add StaticNPC behaviour StaticNPC npc = go.AddComponent <StaticNPC>(); npc.SetLayoutData(obj, entryDoor.buildingKey); // Disable people if shop or building is closed DFLocation.BuildingTypes buildingType = buildingData.buildingType; if ((RMBLayout.IsShop(buildingType) && !GameManager.Instance.PlayerEnterExit.IsPlayerInsideOpenShop) || (buildingType <= DFLocation.BuildingTypes.Palace && !RMBLayout.IsShop(buildingType) && !(PlayerActivate.IsBuildingOpen(buildingType) || buildingType == DFLocation.BuildingTypes.GuildHall && guild.HallAccessAnytime()))) { go.SetActive(false); } // Disable people if player owns this house else if (DaggerfallBankManager.IsHouseOwned(buildingData.buildingKey)) { go.SetActive(false); } // Disable people if this is TG/DB house and player is not a member else if (buildingData.buildingType == DFLocation.BuildingTypes.House2 && buildingData.factionID != 0 && !isMemberOfBuildingGuild) { go.SetActive(false); } } }
/// <summary> /// Gets building information from current location. /// Does not change discovery state for building. /// </summary> /// <param name="buildingKey">Key of building to query.</param> bool GetBuildingDiscoveryData(int buildingKey, out DiscoveredBuilding buildingDiscoveryData) { buildingDiscoveryData = new DiscoveredBuilding(); // Get building directory for location BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory(); if (!buildingDirectory) { return(false); } // Get detailed building data from directory BuildingSummary buildingSummary; if (!buildingDirectory.GetBuildingSummary(buildingKey, out buildingSummary)) { int layoutX, layoutY, recordIndex; BuildingDirectory.ReverseBuildingKey(buildingKey, out layoutX, out layoutY, out recordIndex); Debug.LogFormat("Unable to find expected building key {0} in {1}.{2}", buildingKey, buildingDirectory.LocationData.RegionName, buildingDirectory.LocationData.Name); Debug.LogFormat("LayoutX={0}, LayoutY={1}, RecordIndex={2}", layoutX, layoutY, recordIndex); return(false); } // 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); } // Add to data buildingDiscoveryData.buildingKey = buildingKey; buildingDiscoveryData.displayName = buildingName; buildingDiscoveryData.factionID = buildingSummary.FactionId; buildingDiscoveryData.quality = buildingSummary.Quality; buildingDiscoveryData.buildingType = buildingSummary.BuildingType; return(true); }
private static void OnTransitionToInterior_VariantShopTavernNPCsprites(PlayerEnterExit.TransitionEventArgs args) { PlayerEnterExit playerEnterExit = GameManager.Instance.PlayerEnterExit; DFLocation.BuildingData buildingData = playerEnterExit.Interior.BuildingData; if (buildingData.BuildingType == DFLocation.BuildingTypes.Tavern || RMBLayout.IsShop(buildingData.BuildingType)) { Billboard[] dfBillboards = playerEnterExit.Interior.GetComponentsInChildren <Billboard>(); foreach (Billboard billboard in dfBillboards) { int record = -1; if (billboard.Summary.Archive == 182 && billboard.Summary.Record == 0) { record = GetRecord_182_0(buildingData.Quality); // (buildingData.Quality - 1) / 4; #if UNITY_EDITOR Debug.LogFormat("Shop quality {0} using record {1} to replace 182_0", buildingData.Quality, record); #endif } else if (billboard.Summary.Archive == 182 && billboard.Summary.Record == 1) { if (buildingData.Quality < 12) { // Using big test flats version record = 4; } else if (buildingData.Quality > 14) { record = 5; } #if UNITY_EDITOR Debug.LogFormat("Tavern quality {0} using record {1} to replace 182_1", buildingData.Quality, record); #endif } else if (billboard.Summary.Archive == 182 && billboard.Summary.Record == 2) { if (buildingData.Quality > 12) { record = 6; } #if UNITY_EDITOR Debug.LogFormat("Tavern quality {0} using record {1} to replace 182_2", buildingData.Quality, record); #endif } if (record > -1) { billboard.SetMaterial(197, record); GameObjectHelper.AlignBillboardToGround(billboard.gameObject, billboard.Summary.Size); } } } }
private static void ShopShelfBurglar(RaycastHit hit) { PlayerGPS.DiscoveredBuilding buildingData = GameManager.Instance.PlayerEnterExit.BuildingDiscoveryData; if (RMBLayout.IsShop(buildingData.buildingType) && !PlayerActivate.IsBuildingOpen(buildingData.buildingType)) { int stealthValue = playerEntity.Skills.GetLiveSkillValue(DFCareer.Skills.Stealth); stealthValue -= buildingData.quality * 2; if (Dice100.FailedRoll(StealthCalc(stealthValue, false))) { burglaryCounter += Mathf.Clamp(UnityEngine.Random.Range(100, 200) - playerEntity.Stats.LiveLuck, 10, 100); } } }
private void AddFurnitureAction(DFBlock.RmbBlock3dObjectRecord obj, GameObject go, PlayerGPS.DiscoveredBuilding buildingData) { // Create unique LoadID for save system, using 9 lsb and the sign bit from each coord pos int ulong loadID = ((ulong)buildingData.buildingKey) << 30 | (uint)(obj.XPos << 1 & posMask) << 20 | (uint)(obj.YPos << 1 & posMask) << 10 | (uint)(obj.ZPos << 1 & posMask); DFLocation.BuildingTypes buildingType = buildingData.buildingType; // Handle shelves: if (shopShelvesObjectGroupIndices.Contains(obj.ModelIdNum - containerObjectGroupOffset)) { if (RMBLayout.IsShop(buildingType)) { // Shop shelves, so add a DaggerfallLoot component DaggerfallLoot loot = go.AddComponent <DaggerfallLoot>(); if (loot) { // Set as shelves, assign load id and create serialization object loot.ContainerType = LootContainerTypes.ShopShelves; loot.ContainerImage = InventoryContainerImages.Shelves; loot.LoadID = loadID; if (SaveLoadManager.Instance != null) { go.AddComponent <SerializableLootContainer>(); } } } else if (buildingType == DFLocation.BuildingTypes.Library || buildingType == DFLocation.BuildingTypes.GuildHall || buildingType == DFLocation.BuildingTypes.Temple) { // Bookshelves, add DaggerfallBookshelf component go.AddComponent <DaggerfallBookshelf>(); } else if (DaggerfallBankManager.IsHouseOwned(buildingData.buildingKey)) { // Player owned house, everything is a house container MakeHouseContainer(obj, go, loadID); } } // Handle generic furniture as (private) house containers: // (e.g. shelves, boxes, wardrobes, drawers etc) else if (obj.ModelIdNum / 100 == houseContainerObjectGroup || houseContainerObjectGroupIndices.Contains(obj.ModelIdNum - containerObjectGroupOffset)) { MakeHouseContainer(obj, go, loadID); } }
// 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); }
/// <summary> /// Sets a variant for a location. /// </summary> /// <param name="regionIndex">Region index</param> /// <param name="locationIndex">Location index</param> /// <param name="variant">Variant name</param> /// <returns>True if overwriting an existing variant set for this location</returns> public static bool SetLocationVariant(int regionIndex, int locationIndex, string variant) { int locationKey = WorldDataReplacement.MakeLocationKey(regionIndex, locationIndex); bool overwrite = !locationVariants.ContainsKey(locationKey); if (variant == NoVariant) { locationVariants.Remove(locationKey); } else { locationVariants[locationKey] = variant; } Debug.LogFormat("Set variant \"{0}\" for the location index \"{1}\" in region {2}", variant, locationIndex, regionIndex); RMBLayout.ClearLocationCache(); return(overwrite); }
/// <summary> /// Sets a variant for a block. /// </summary> /// <param name="blockName">Block name</param> /// <param name="variant">Variant name</param> /// <param name="locationKey">Location key if the variant is only for a specific location</param> /// <returns>True if overwriting an existing variant set for this location</returns> public static bool SetBlockVariant(string blockName, string variant, int locationKey = AnyLocationKey) { VariantBlockKey blockKey = new VariantBlockKey(locationKey, blockName); bool overwrite = !blockVariants.ContainsKey(blockKey); if (variant == NoVariant) { blockVariants.Remove(blockKey); } else { blockVariants[blockKey] = variant; } Debug.LogFormat("Set variant \"{0}\" for the block {1} at locationKey {2}", variant, blockName, locationKey); RMBLayout.ClearLocationCache(); return(overwrite); }
/// <summary> /// Sets a variant for a building. (block record) /// </summary> /// <param name="blockName">Block name</param> /// <param name="variant">Variant name</param> /// <param name="locationKey">Location key if the variant is only for a specific location</param> /// <returns>True if overwriting an existing variant set for this location</returns> public static bool SetBuildingVariant(string blockName, int recordIndex, string variant, int locationKey = AnyLocationKey) { VariantBuildingKey buildingKey = new VariantBuildingKey(locationKey, blockName, recordIndex); bool overwrite = !buildingVariants.ContainsKey(buildingKey); if (variant == NoVariant) { buildingVariants.Remove(buildingKey); } else { buildingVariants[buildingKey] = variant; } Debug.LogFormat("Set variant \"{0}\" for building {2} of {1} at locationKey {3}", variant, blockName, recordIndex, locationKey); RMBLayout.ClearLocationCache(); return(overwrite); }
/// <summary> /// Update NPC presence for shops and guilds after resting/idling. /// </summary> public void UpdateNpcPresence() { PlayerEnterExit playerEnterExit = GameManager.Instance.PlayerEnterExit; DFLocation.BuildingTypes buildingType = playerEnterExit.BuildingType; if ((RMBLayout.IsShop(buildingType) && !playerEnterExit.IsPlayerInsideOpenShop) || (!RMBLayout.IsShop(buildingType) && buildingType <= DFLocation.BuildingTypes.Palace && buildingType != DFLocation.BuildingTypes.HouseForSale)) { Transform npcTransforms = transform.Find(peopleFlats); if (PlayerActivate.IsBuildingOpen(buildingType)) { foreach (Transform npcTransform in npcTransforms) { npcTransform.gameObject.SetActive(true); } Debug.Log("Updated npcs to be present."); } } }
private void LayoutLocation(ref DFLocation location) { // Get city dimensions int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; // Import blocks for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = RMBLayout.CreateGameObject(blockName); go.transform.parent = this.transform; go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); } } // Enumerate start marker game objects EnumerateStartMarkers(); }
/// <summary> /// Setup building directory from specified location. /// </summary> /// <param name="location">Source location data.</param> public void SetLocation(DFLocation location) { // Clear existing buildings buildingDict.Clear(); // Get block data pre-populated with map building data. DFBlock[] blocks; RMBLayout.GetLocationBuildingData(location, out blocks); // Construct building directory int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Get all buildings for this block // Some blocks have zero buildings int index = y * width + x; BuildingSummary[] buildings = RMBLayout.GetBuildingData(blocks[index], x, y); if (buildings == null || buildings.Length == 0) { continue; } // Add all buildings to directory for (int i = 0; i < buildings.Length; i++) { buildingDict.Add(buildings[i].buildingKey, buildings[i]); } } } // Store location info locationId = location.Exterior.ExteriorData.LocationId; mapId = location.MapTableData.MapId; locationData = location; }
/// <summary> /// Add interior models. /// </summary> private void AddModels(PlayerGPS.DiscoveredBuilding buildingData) { List <StaticDoor> doors = new List <StaticDoor>(); GameObject node = new GameObject("Models"); GameObject doorsNode = new GameObject("Doors"); node.transform.parent = this.transform; doorsNode.transform.parent = this.transform; // Iterate through models in this subrecord combiner.NewCombiner(); foreach (DFBlock.RmbBlock3dObjectRecord obj in recordData.Interior.Block3dObjectRecords) { bool stopCombine = false; // Filter out bad interior models if (IsBadInteriorModel(obj.ModelIdNum)) { continue; } // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Get model position by type (3 seems to indicate props/clutter) // Also stop these from being combined as some may carry a loot container Vector3 modelPosition; if (obj.ObjectType == propModelType) { // Props axis needs to be transformed to lowest Y point Vector3 bottom = modelData.Vertices[0]; for (int i = 0; i < modelData.Vertices.Length; i++) { if (modelData.Vertices[i].y < bottom.y) { bottom = modelData.Vertices[i]; } } modelPosition = new Vector3(obj.XPos, obj.YPos, obj.ZPos) * MeshReader.GlobalScale; modelPosition += new Vector3(0, -bottom.y, 0); stopCombine = true; } else { modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; } // Stop special object from being combined if (obj.ModelIdNum == ladderModelId) { stopCombine = true; } // Get model transform Vector3 modelRotation = new Vector3(-obj.XRotation / BlocksFile.RotationDivisor, -obj.YRotation / BlocksFile.RotationDivisor, -obj.ZRotation / BlocksFile.RotationDivisor); Vector3 modelScale = RMBLayout.GetModelScaleVector(obj); Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), modelScale); // Does this model have doors? if (modelData.Doors != null) { doors.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, entryDoor.blockIndex, entryDoor.recordIndex, modelMatrix)); } // Inject custom GameObject if available GameObject go = MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, node.transform, modelMatrix); // Otherwise use Daggerfall mesh - combine or add if (!go) { if (dfUnity.Option_CombineRMB && !stopCombine) { combiner.Add(ref modelData, modelMatrix); } else { // Add individual GameObject go = GameObjectHelper.CreateDaggerfallMeshGameObject(obj.ModelIdNum, node.transform, dfUnity.Option_SetStaticFlags); go.transform.position = modelMatrix.GetColumn(3); go.transform.rotation = modelMatrix.rotation; go.transform.localScale = modelMatrix.lossyScale; // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } // Make ladder collider convex and ladder functionality, if set up as propModelType if (obj.ModelIdNum == ladderModelId && obj.ObjectType == propModelType) { var meshCollider = go.GetComponent <MeshCollider>(); if (meshCollider) { meshCollider.convex = true; } go.AddComponent <DaggerfallLadder>(); } // Optionally add action objects to specific furniture items (e.g. loot containers), except when laying out map (buildingType=AllValid) if (obj.ObjectType == propModelType && buildingData.buildingType != DFLocation.BuildingTypes.AllValid) { AddFurnitureAction(obj, go, buildingData); } } // Add combined GameObject if (dfUnity.Option_CombineRMB) { if (combiner.VertexCount > 0) { combiner.Apply(); GameObject go = GameObjectHelper.CreateCombinedMeshGameObject(combiner, "CombinedModels", node.transform, dfUnity.Option_SetStaticFlags); // Update climate DaggerfallMesh dfMesh = go.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, climateSeason, WindowStyle.Disabled); } } // Add static doors component DaggerfallStaticDoors c = this.gameObject.AddComponent <DaggerfallStaticDoors>(); c.Doors = doors.ToArray(); }
private void GetBuildingList() { listBuildings = new List <BuildingInfo>(); ContentReader.MapSummary mapSummary; DFPosition mapPixel = GameManager.Instance.PlayerGPS.CurrentMapPixel; if (!DaggerfallUnity.Instance.ContentReader.HasLocation(mapPixel.X, mapPixel.Y, out mapSummary)) { // no location found return; // do nothing } DFLocation location = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex); if (!location.Loaded) { // Location not loaded, something went wrong DaggerfallUnity.LogMessage("error when loading location for in TalkManager.GetBuildingList", true); } DaggerfallExteriorAutomap.BlockLayout[] blockLayout = GameManager.Instance.ExteriorAutomap.ExteriorLayout; DFBlock[] blocks; RMBLayout.GetLocationBuildingData(location, out blocks); int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = y * width + x; BuildingSummary[] buildingsInBlock = RMBLayout.GetBuildingData(blocks[index], x, y); foreach (BuildingSummary buildingSummary in buildingsInBlock) { try { string locationName = BuildingNames.GetName(buildingSummary.NameSeed, buildingSummary.BuildingType, buildingSummary.FactionId, location.Name, location.RegionName); BuildingInfo item; item.buildingType = buildingSummary.BuildingType; item.name = locationName; item.buildingKey = buildingSummary.buildingKey; // compute building position in map coordinate system float xPosBuilding = blockLayout[index].rect.xpos + (int)(buildingSummary.Position.x / (BlocksFile.RMBDimension * MeshReader.GlobalScale) * DaggerfallExteriorAutomap.blockSizeWidth) - GameManager.Instance.ExteriorAutomap.LocationWidth * DaggerfallExteriorAutomap.blockSizeWidth * 0.5f; float yPosBuilding = blockLayout[index].rect.ypos + (int)(buildingSummary.Position.z / (BlocksFile.RMBDimension * MeshReader.GlobalScale) * DaggerfallExteriorAutomap.blockSizeHeight) - GameManager.Instance.ExteriorAutomap.LocationHeight * DaggerfallExteriorAutomap.blockSizeHeight * 0.5f; item.position = new Vector2(xPosBuilding, yPosBuilding); listBuildings.Add(item); } catch (Exception e) { string exceptionMessage = String.Format("exception occured in function BuildingNames.GetName (exception message: " + e.Message + @") with params: seed: {0}, type: {1}, factionID: {2}, locationName: {3}, regionName: {4}", buildingSummary.NameSeed, buildingSummary.BuildingType, buildingSummary.FactionId, location.Name, location.RegionName); DaggerfallUnity.LogMessage(exceptionMessage, true); } } } } }
static void ThiefEffects_OnNewMagicRound() { //Debug.Log("[ThiefOverhaul] Magic Round"); if (playerEnterExit.IsPlayerInsideBuilding) { PlayerGPS.DiscoveredBuilding buildingData = GameManager.Instance.PlayerEnterExit.BuildingDiscoveryData; if (RMBLayout.IsShop(buildingData.buildingType) && !PlayerActivate.IsBuildingOpen(buildingData.buildingType)) { int stealthValue = playerEntity.Skills.GetLiveSkillValue(DFCareer.Skills.Stealth); stealthValue -= buildingData.quality * 2; if (Dice100.FailedRoll(StealthCalc(stealthValue, false))) { if (burglaryCounter >= 100) { DaggerfallUI.MessageBox("'Guards! Guards! We're being robbed!'"); if (Dice100.SuccessRoll(playerEntity.Stats.LiveLuck)) { playerEntity.MagicalConcealmentFlags = MagicalConcealmentFlags.None; DaggerfallUI.AddHUDText("Your magical concealment is broken"); } playerEntity.CrimeCommitted = PlayerEntity.Crimes.Breaking_And_Entering; playerEntity.SpawnCityGuards(true); } else if (burglaryCounter == 0) { DaggerfallUI.MessageBox(burglaryString1()); burglaryCounter += Mathf.Clamp(UnityEngine.Random.Range(100, 200) - playerEntity.Stats.LiveLuck, 10, 100); } else if (burglaryCounter < 50) { DaggerfallUI.MessageBox(burglaryString2()); burglaryCounter += Mathf.Clamp(UnityEngine.Random.Range(100, 200) - playerEntity.Stats.LiveLuck, 10, 100); } else { burglaryCounter += Mathf.Clamp(UnityEngine.Random.Range(100, 200) - playerEntity.Stats.LiveLuck, 10, 100); } } } } DaggerfallUnityItem ringSlot0 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Ring0); DaggerfallUnityItem ringSlot1 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Ring1); DaggerfallUnityItem markSlot0 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Mark0); DaggerfallUnityItem markSlot1 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Mark1); DaggerfallUnityItem braceletSlot0 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Bracelet0); DaggerfallUnityItem braceletSlot1 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Bracelet1); DaggerfallUnityItem bracerSlot0 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Bracer0); DaggerfallUnityItem bracerSlot1 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Bracer1); DaggerfallUnityItem crystalSlot0 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Crystal0); DaggerfallUnityItem crystalSlot1 = playerEntity.ItemEquipTable.GetItem(EquipSlots.Crystal1); if (ringSlot0 != null && ringSlot0.TemplateIndex == templateIndex_Ring) { lockpickingBonus = 20; } else if (ringSlot1 != null && ringSlot1.TemplateIndex == templateIndex_Ring) { lockpickingBonus = 20; } else { lockpickingBonus = 0; } if (markSlot0 != null && markSlot0.TemplateIndex == templateIndex_Mark) { streetwiseBonus = 20; } else if (markSlot1 != null && markSlot1.TemplateIndex == templateIndex_Mark) { streetwiseBonus = 20; } else { streetwiseBonus = 0; } if (braceletSlot0 != null && braceletSlot0.TemplateIndex == templateIndex_Bracelet) { pickpocketBonus = 20; } else if (braceletSlot1 != null && braceletSlot1.TemplateIndex == templateIndex_Bracelet) { pickpocketBonus = 20; } else { pickpocketBonus = 0; } if (bracerSlot0 != null && bracerSlot0.TemplateIndex == templateIndex_Bracer) { climbingBonus = 20; } else if (bracerSlot1 != null && bracerSlot1.TemplateIndex == templateIndex_Bracer) { climbingBonus = 20; } else { climbingBonus = 0; } if (crystalSlot0 != null && crystalSlot0.TemplateIndex == templateIndex_Crystal) { stealthBonus = 20; } else if (crystalSlot1 != null && crystalSlot1.TemplateIndex == templateIndex_Crystal) { stealthBonus = 20; } else { stealthBonus = 0; } if (!GameManager.IsGamePaused && playerEntity.CurrentHealth > 0) { int[] skillMods = new int[DaggerfallSkills.Count]; skillMods[(int)DFCareer.Skills.Lockpicking] = +lockpickingBonus; skillMods[(int)DFCareer.Skills.Streetwise] = +streetwiseBonus; skillMods[(int)DFCareer.Skills.Pickpocket] = +pickpocketBonus; skillMods[(int)DFCareer.Skills.Climbing] = +climbingBonus; skillMods[(int)DFCareer.Skills.Stealth] = +stealthBonus; playerEffectManager.MergeDirectSkillMods(skillMods); } }
/// <summary> /// Generate a list of potential sites based on building type. /// This uses actual map layout and block data rather than the (often inaccurate) list of building in map data. /// Specify BuildingTypes.AllValid to find all valid building types /// </summary> SiteDetails[] CollectQuestSitesOfBuildingType(DFLocation location, DFLocation.BuildingTypes buildingType) { // Valid building types for valid search int[] validBuildingTypes = { 0, 2, 3, 5, 6, 8, 9, 11, 12, 13, 14, 15, 17, 18, 19, 20 }; List <SiteDetails> foundSites = new List <SiteDetails>(); // Iterate through all blocks DFBlock[] blocks; RMBLayout.GetLocationBuildingData(location, out blocks); int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Iterate through all buildings in this block int index = y * width + x; BuildingSummary[] buildingSummary = RMBLayout.GetBuildingData(blocks[index], x, y); for (int i = 0; i < buildingSummary.Length; i++) { // When enumAllValid is specified accept all valid building types bool forceAccept = false; if (buildingType == DFLocation.BuildingTypes.AllValid) { for (int j = 0; j < validBuildingTypes.Length; j++) { if (validBuildingTypes[j] == (int)buildingSummary[i].BuildingType) { forceAccept = true; break; } } } // Match building against required type if (buildingSummary[i].BuildingType == buildingType || forceAccept) { // Building must be a valid quest site QuestMarker[] questSpawnMarkers, questItemMarkers; EnumerateBuildingQuestMarkers(blocks[index], i, out questSpawnMarkers, out questItemMarkers); if (!ValidateQuestMarkers(questSpawnMarkers, questItemMarkers)) { continue; } // Get building name based on type string buildingName; if (RMBLayout.IsResidence(buildingType)) { // Generate a random surname for this residence DFRandom.srand(Time.renderedFrameCount); string surname = DaggerfallUnity.Instance.NameHelper.Surname(Utility.NameHelper.BankTypes.Breton); buildingName = HardStrings.theNamedResidence.Replace("%s", surname); } else { // Use fixed name buildingName = BuildingNames.GetName( buildingSummary[i].NameSeed, buildingSummary[i].BuildingType, buildingSummary[i].FactionId, location.Name, location.RegionName); } // Configure new site details SiteDetails site = new SiteDetails(); site.questUID = ParentQuest.UID; site.siteType = SiteTypes.Building; site.mapId = location.MapTableData.MapId; site.locationId = location.Exterior.ExteriorData.LocationId; site.regionName = location.RegionName; site.locationName = location.Name; site.buildingKey = buildingSummary[i].buildingKey; site.buildingName = buildingName; site.questSpawnMarkers = questSpawnMarkers; site.questItemMarkers = questItemMarkers; // Asssign markers only if available if (questSpawnMarkers != null) { siteDetails.selectedQuestSpawnMarker = UnityEngine.Random.Range(0, questSpawnMarkers.Length); } if (questItemMarkers != null) { siteDetails.selectedQuestItemMarker = UnityEngine.Random.Range(0, questItemMarkers.Length); } foundSites.Add(site); } } } } return(foundSites.ToArray()); }
private static void OnTransitionToInterior_VariantResidenceNPCsprites(PlayerEnterExit.TransitionEventArgs args) { if (villagerVarietyMod != null && villagerVarietyNumVariants == 0) { ModManager.Instance.SendModMessage(VILLAGERVARIETY_MODNAME, "getNumVariants", null, (string message, object data) => { villagerVarietyNumVariants = (int)data; }); } PlayerEnterExit playerEnterExit = GameManager.Instance.PlayerEnterExit; DFLocation.BuildingData buildingData = playerEnterExit.Interior.BuildingData; if (RMBLayout.IsResidence(buildingData.BuildingType)) { Races race = GetClimateRace(); int gender = -1; Billboard[] dfBillboards = playerEnterExit.Interior.GetComponentsInChildren <Billboard>(); foreach (Billboard billboard in dfBillboards) { if (billboard.Summary.Archive == 182) { gender = GetGender182(billboard.Summary.Record); } else if (billboard.Summary.Archive == 184) { gender = GetGender184(billboard.Summary.Record); } if (gender != -1) { StaticNPC npc = billboard.GetComponent <StaticNPC>(); if (npc && npc.Data.factionID == 0) { int faceVariant = npc.Data.nameSeed % 29; Debug.LogFormat("Replace house NPC {0}.{1} with faceVariant {2} - {3}", billboard.Summary.Archive, billboard.Summary.Record, faceVariant, faceVariant < 24); if (faceVariant < 24) { int outfitVariant = npc.Data.nameSeed % 4; int archive = gender == (int)Genders.Male ? raceArchivesMale[race][outfitVariant] : raceArchivesFemale[race][outfitVariant]; int record = 5; int faceRecord = gender == (int)Genders.Male ? raceFaceRecordMale[race][outfitVariant] : raceFaceRecordFemale[race][outfitVariant]; faceRecord += faceVariant; bool materialSet = false; if (villagerVarietyMod != null) { int variant = npc.Data.nameSeed % villagerVarietyNumVariants; string season = ""; //ModManager.Instance.SendModMessage(VILLAGERVARIETY_MODNAME, "getSeasonStr", null, (string message, object data) => { season = (string)data; }); Debug.LogFormat("Replace house NPC {0}.{1} with {2}.{3}, outfit: {4} faceRecord: {5} ({6}) variant: {7} season: {8}", billboard.Summary.Archive, billboard.Summary.Record, archive, record, outfitVariant, faceRecord, faceVariant, variant, season); string imageName = null; ModManager.Instance.SendModMessage(VILLAGERVARIETY_MODNAME, "getImageName", new object[] { archive, record, 0, faceRecord, variant, season }, (string message, object data) => { imageName = (string)data; }); if (!string.IsNullOrEmpty(imageName) && villagerVarietyMod.HasAsset(imageName)) { // Get texture and create material Texture2D texture = villagerVarietyMod.GetAsset <Texture2D>(imageName); Material material = MaterialReader.CreateStandardMaterial(MaterialReader.CustomBlendMode.Cutout); material.mainTexture = texture; // Apply material to mesh renderer MeshRenderer meshRenderer = billboard.GetComponent <MeshRenderer>(); meshRenderer.sharedMaterial = material; // Create mesh and setup UV map for mesh filter Vector2 size; Mesh mesh = DaggerfallUnity.Instance.MeshReader.GetBillboardMesh(new Rect(0, 0, 1, 1), archive, record, out size); mesh.uv = new Vector2[] { new Vector2(0, 1), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0) }; MeshFilter meshFilter = billboard.GetComponent <MeshFilter>(); Destroy(meshFilter.sharedMesh); meshFilter.sharedMesh = mesh; materialSet = true; } } if (!materialSet) { billboard.SetMaterial(archive, record); billboard.FramesPerSecond = 1; } GameObjectHelper.AlignBillboardToGround(billboard.gameObject, billboard.Summary.Size); Dictionary <int, FlatsFile.FlatData> flatsDict = DaggerfallUnity.Instance.ContentReader.FlatsFileReader.FlatsDict; int flatId = FlatsFile.GetFlatID(npc.Data.billboardArchiveIndex, npc.Data.billboardRecordIndex); #if UNITY_EDITOR Debug.LogFormat("Replacing face dict for {0} with {1} (for {2}.{3} / {4}.{5})", flatsDict[flatId].faceIndex, faceRecord, npc.Data.billboardArchiveIndex, npc.Data.billboardRecordIndex, billboard.Summary.Archive, billboard.Summary.Record); #endif flatsDict[flatId] = new FlatsFile.FlatData() { archive = billboard.Summary.Archive, record = billboard.Summary.Record, faceIndex = faceRecord, }; } } } } } }
internal void DisplayLocationInfo() { if (LocationSummary.LocationType == DFRegion.LocationTypes.Coven || LocationSummary.LocationType == DFRegion.LocationTypes.DungeonKeep || LocationSummary.LocationType == DFRegion.LocationTypes.DungeonLabyrinth || LocationSummary.LocationType == DFRegion.LocationTypes.DungeonRuin || LocationSummary.LocationType == DFRegion.LocationTypes.Graveyard || LocationSummary.LocationType == DFRegion.LocationTypes.None) { return; } Dictionary <int, PlayerGPS.DiscoveredLocation> discoveryData = GameManager.Instance.PlayerGPS.GetDiscoverySaveData(); if (discoveryData.ContainsKey(LocationSummary.ID)) { PlayerGPS.DiscoveredLocation discoveredLocation = discoveryData[locationSummary.ID]; Dictionary <int, PlayerGPS.DiscoveredBuilding> locBuildings = discoveredLocation.discoveredBuildings; if (locBuildings != null && locBuildings.Count > 0) { IDictionary <DFLocation.BuildingTypes, int> buildingTypeCounts = new SortedDictionary <DFLocation.BuildingTypes, int>(); List <string> guildNames = new List <string>(); foreach (PlayerGPS.DiscoveredBuilding building in locBuildings.Values) { if (RMBLayout.IsNamedBuilding(building.buildingType)) { string guildName = building.displayName.StartsWith("The ") ? building.displayName.Substring(4) : building.displayName; if (building.buildingType == DFLocation.BuildingTypes.GuildHall && !guildNames.Contains(guildName)) { guildNames.Add(guildName); } if (building.buildingType != DFLocation.BuildingTypes.GuildHall) { if (buildingTypeCounts.ContainsKey(building.buildingType)) { buildingTypeCounts[building.buildingType]++; } else { buildingTypeCounts.Add(building.buildingType, 1); } } } } List <TextFile.Token> tokens = new List <TextFile.Token>(); tokens.Add(new TextFile.Token() { text = GetLocationNameInCurrentRegion(locationSummary.MapIndex, true), formatting = TextFile.Formatting.TextHighlight }); tokens.Add(newLine); tokens.Add(newLine); guildNames.Sort(); string guilds = ""; foreach (string guildName in guildNames) { if (!string.IsNullOrWhiteSpace(guilds)) { guilds += ", "; } guilds += guildName; } TextFile.Token tab1 = TextFile.TabToken; tab1.x = 45; TextFile.Token tab2 = TextFile.TabToken; tab2.x = 100; TextFile.Token tab3 = TextFile.TabToken; tab3.x = 145; if (!string.IsNullOrWhiteSpace(guilds)) { tokens.Add(TextFile.CreateTextToken("Guild Halls: " + guilds)); } tokens.Add(newLine); tokens.Add(TextFile.NewLineToken); bool secondColumn = false; foreach (DFLocation.BuildingTypes buildingType in buildingTypeCounts.Keys) { tokens.Add(TextFile.CreateTextToken(buildingType.ToString())); tokens.Add(!secondColumn ? tab1 : tab3); tokens.Add(TextFile.CreateTextToken(buildingTypeCounts[buildingType].ToString())); if (!secondColumn) { tokens.Add(tab2); } else { tokens.Add(TextFile.NewLineToken); } secondColumn = !secondColumn; } infoBox = new DaggerfallMessageBox(uiManager, this); infoBox.ClickAnywhereToClose = true; infoBox.SetHighlightColor(Color.white); infoBox.SetTextTokens(tokens.ToArray()); infoBox.OnClose += InfoBox_Close; infoBox.Show(); return; } } DaggerfallUI.MessageBox("You have no knowledge of " + GetLocationNameInCurrentRegion(locationSummary.MapIndex, true) + "."); }
// Update terrain data // Spreads loading across several frames to reduce gameplay stalls // This can also be done using true multi-threading, but at much greater // complexity for only minor visible gains. // Only yields after initial init complete private IEnumerator UpdateTerrains() { // First stage updates terrain heightmaps for (int i = 0; i < terrainArray.Length; i++) { if (terrainArray[i].active && terrainArray[i].updateHeights) { UpdateTerrainHeights(terrainArray[i]); terrainArray[i].updateHeights = false; if (!init) { yield return(new WaitForEndOfFrame()); } } } // Wait for physics update when streaming if (!init) { yield return(new WaitForFixedUpdate()); } // Second stage updates terrain nature for (int i = 0; i < terrainArray.Length; i++) { if (terrainArray[i].active && terrainArray[i].updateNature) { UpdateTerrainNature(terrainArray[i]); terrainArray[i].updateNature = false; if (!init) { yield return(new WaitForEndOfFrame()); } } } // Get key for central player terrain int playerKey = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY); // Third stage updates location if present // Vast majority of terrains will not have a location // Locations are not optimised as yet and are quite heavy on drawcalls for (int i = 0; i < terrainArray.Length; i++) { // Get key for this terrain int key = TerrainHelper.MakeTerrainKey(terrainArray[i].mapPixelX, terrainArray[i].mapPixelY); if (terrainArray[i].active && terrainArray[i].hasLocation) { // Create location if not present if (!locationDict.ContainsKey(key)) { // Create location object DFLocation location; GameObject locationObject = CreateLocationGameObject(i, out location); if (!locationObject) { continue; } // Add location object to dictionary locationDict.Add(key, locationObject); // Create location beacon // This is parented to location and shares its lifetime if (AddLocationBeacon) { const float beaconHeight = 900f; const float beaconOffset = (MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale) / 2f; GameObject locationMarker = (GameObject)GameObject.Instantiate(Resources.Load <GameObject>("LocationBeacon")); locationMarker.hideFlags = HideFlags.HideAndDontSave; locationMarker.transform.parent = locationObject.transform; locationMarker.transform.localPosition = new Vector3(beaconOffset, beaconHeight, beaconOffset); } // Add one nature batch for entire location // This is parented to location and shares its lifetime GameObject natureBatchObject = new GameObject("NatureBatch"); natureBatchObject.hideFlags = HideFlags.HideAndDontSave; natureBatchObject.transform.parent = locationObject.transform; natureBatchObject.transform.localPosition = Vector3.zero; DaggerfallBillboardBatch natureBatch = natureBatchObject.AddComponent <DaggerfallBillboardBatch>(); int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue); natureBatch.SetMaterial(natureArchive); // RMB blocks are laid out in centre of terrain to align with ground int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; float offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2; float offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2; Vector3 origin = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ); // Perform layout and yield after each block is placed DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Set origin for billboard batch add // This causes next additions to be offset by this position Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); natureBatch.origin = blockOrigin; // Add block and yield string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = RMBLayout.CreateGameObject(blockName, true, natureBatch); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = locationObject.transform; go.transform.localPosition = blockOrigin; dfLocation.ApplyClimateSettings(); if (!init) { yield return(new WaitForEndOfFrame()); } } } // If this is the player terrain we may need to reposition player if (playerKey == key && repositionPlayer) { // Position to location and use start marker for large cities bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity); PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker); repositionPlayer = false; } // Apply nature batch natureBatch.Apply(); } } else if (terrainArray[i].active) { if (playerKey == key && repositionPlayer) { PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero); repositionPlayer = false; } } } // If this is an init we can use the load time to unload unused assets // Keeps memory usage much lower over time if (init) { Resources.UnloadUnusedAssets(); } // Finish by collecting stale data and setting neighbours CollectTerrains(); CollectLocations(); UpdateNeighbours(); }
// 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); }
/// <summary> /// Gets building information from current location. /// Does not change discovery state for building. /// </summary> /// <param name="buildingKey">Key of building to query.</param> bool GetBuildingDiscoveryData(int buildingKey, out DiscoveredBuilding buildingDiscoveryData) { buildingDiscoveryData = new DiscoveredBuilding(); // Get building directory for location BuildingDirectory buildingDirectory = GameManager.Instance.StreamingWorld.GetCurrentBuildingDirectory(); if (!buildingDirectory) { return(false); } // Get detailed building data from directory BuildingSummary buildingSummary; if (!buildingDirectory.GetBuildingSummary(buildingKey, out buildingSummary)) { int layoutX, layoutY, recordIndex; BuildingDirectory.ReverseBuildingKey(buildingKey, out layoutX, out layoutY, out recordIndex); Debug.LogFormat("Unable to find expected building key {0} in {1}.{2}", buildingKey, buildingDirectory.LocationData.RegionName, buildingDirectory.LocationData.Name); Debug.LogFormat("LayoutX={0}, LayoutY={1}, RecordIndex={2}", layoutX, layoutY, recordIndex); return(false); } // Resolve name by building type string buildingName; if (RMBLayout.IsResidence(buildingSummary.BuildingType)) { // Residence buildingName = HardStrings.residence; // Link to quest system active sites // note Nystul: do this via TalkManager, this might seem odd at first glance but there is a reason to do so: // get info from TalkManager if pc learned about existence of the building (i.e. its name) // either through dialog ("add dialog" or by dialog-link) or quest (quest did not hide location via "dialog link" command) bool pcLearnedAboutExistence = false; bool receivedDirectionalHints = false; bool locationWasMarkedOnMapByNPC = false; string overrideBuildingName = string.Empty; if (GameManager.Instance.TalkManager.IsBuildingQuestResource(buildingSummary.buildingKey, ref overrideBuildingName, ref pcLearnedAboutExistence, ref receivedDirectionalHints, ref locationWasMarkedOnMapByNPC)) { if (pcLearnedAboutExistence) { buildingName = overrideBuildingName; } } } else { // Fixed building name buildingName = BuildingNames.GetName( buildingSummary.NameSeed, buildingSummary.BuildingType, buildingSummary.FactionId, buildingDirectory.LocationData.Name, buildingDirectory.LocationData.RegionName); } // Add to data buildingDiscoveryData.buildingKey = buildingKey; buildingDiscoveryData.displayName = buildingName; buildingDiscoveryData.factionID = buildingSummary.FactionId; buildingDiscoveryData.quality = buildingSummary.Quality; buildingDiscoveryData.buildingType = buildingSummary.BuildingType; return(true); }
// Player has clicked on a static NPC void StaticNPCClick(StaticNPC npc) { // Do nothing if no NPC passed or fade in progress // Quest machine does not tick while fading (to prevent things happening while screen is black) // But this can result in player clicking a quest NPC before quest state ticks after load and breaking quest if (!npc || DaggerfallUI.Instance.FadeInProgress) { return; } // Store the NPC just clicked in quest engine QuestMachine.Instance.LastNPCClicked = npc; // Check if this NPC is a quest giver and show temp guild quest popup // This will be changed later when temp guild system replaced with real thing if (QuestorCheck(npc)) { return; } // Handle quest NPC click and exit if linked to a Person resource QuestResourceBehaviour questResourceBehaviour = npc.gameObject.GetComponent <QuestResourceBehaviour>(); if (questResourceBehaviour) { if (TriggerQuestResourceBehaviourClick(questResourceBehaviour)) { return; } } // Do nothing further if a quest is actively listening on this individual NPC // This NPC not reserved as a Person resource but has a WhenNpcIsAvailable action listening on it // This effectively shuts down several named NPCs during main quest, but not trivial to otherwise determine appropriate access // TODO: Try to find a good solution for releasing listeners when the owning action is disabled if (QuestMachine.Instance.HasFactionListener(npc.Data.factionID)) { return; } // Get faction data. FactionFile.FactionData factionData; if (playerEnterExit.IsPlayerInsideBuilding && GameManager.Instance.PlayerEntity.FactionData.GetFactionData(npc.Data.factionID, out factionData)) { UserInterfaceManager uiManager = DaggerfallUI.Instance.UserInterfaceManager; Debug.LogFormat("faction id: {0}, social group: {1}, guild: {2}", npc.Data.factionID, (FactionFile.SocialGroups)factionData.sgroup, (FactionFile.GuildGroups)factionData.ggroup); // Check if the NPC offers a guild service. if (Enum.IsDefined(typeof(GuildServices), npc.Data.factionID)) { FactionFile.GuildGroups guild = (FactionFile.GuildGroups)factionData.ggroup; GuildServices service = (GuildServices)npc.Data.factionID; Debug.Log("NPC offers guild service: " + service.ToString()); uiManager.PushWindow(new DaggerfallGuildServicePopupWindow(uiManager, npc, guild, service)); } // Check if this NPC is a merchant. else if ((FactionFile.SocialGroups)factionData.sgroup == FactionFile.SocialGroups.Merchants) { // Shop? if (RMBLayout.IsShop(playerEnterExit.BuildingDiscoveryData.buildingType)) { if (RMBLayout.IsRepairShop(playerEnterExit.BuildingDiscoveryData.buildingType)) { uiManager.PushWindow(new DaggerfallMerchantRepairPopupWindow(uiManager, npc)); } else { uiManager.PushWindow(new DaggerfallMerchantServicePopupWindow(uiManager, npc, DaggerfallMerchantServicePopupWindow.Services.Sell)); } } // Bank? else if (playerEnterExit.BuildingDiscoveryData.buildingType == DFLocation.BuildingTypes.Bank) { uiManager.PushWindow(new DaggerfallMerchantServicePopupWindow(uiManager, npc, DaggerfallMerchantServicePopupWindow.Services.Banking)); } // Tavern? else if (playerEnterExit.BuildingDiscoveryData.buildingType == DFLocation.BuildingTypes.Tavern) { // for now only talk to all npc in taverns - TODO: add tavern option in here GameManager.Instance.TalkManager.TalkToStaticNPC(npc); } } // TODO - more checks for npc social types? else // if no special handling had to be done for npc with social group of type merchant: talk to the static npc { GameManager.Instance.TalkManager.TalkToStaticNPC(npc); } } else // if no special handling had to be done (all remaining npcs of the remaining social groups not handled explicitely above): default is talk to the static npc { // with one exception: guards if (npc.Data.billboardArchiveIndex == 183 && npc.Data.billboardRecordIndex == 3) // detect if clicked guard (comment Nystul: didn't find a better mechanism than billboard texture check) { return; // if guard was clicked don't open talk window } // otherwise open talk window GameManager.Instance.TalkManager.TalkToStaticNPC(npc); } }