/// <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 misc block flats. /// Batching is conditionally supported. /// </summary> public static void AddMiscBlockFlats( ref DFBlock blockData, Transform flatsParent, DaggerfallBillboardBatch animalsBillboardBatch = null, TextureAtlasBuilder miscBillboardsAtlas = null, DaggerfallBillboardBatch miscBillboardsBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Ignore lights as they are handled by AddLights() if (obj.TextureArchive == TextureReader.LightsTextureArchive) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Use misc billboard atlas where available if (miscBillboardsAtlas != null && miscBillboardsBatch != null) { TextureAtlasBuilder.AtlasItem item = miscBillboardsAtlas.GetAtlasItem(obj.TextureArchive, obj.TextureRecord); if (item.key != -1) { miscBillboardsBatch.AddItem(item.rect, item.textureItem.size, item.textureItem.scale, billboardPosition); continue; } } // Add to batch where available if (obj.TextureArchive == TextureReader.AnimalsTextureArchive && animalsBillboardBatch != null) { animalsBillboardBatch.AddItem(obj.TextureRecord, billboardPosition); continue; } // Add standalone billboard gameobject GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } }
private static void AddRandomRDBEnemy( DFBlock.RdbObject obj, DFRegion.DungeonTypes dungeonType, float monsterPower, int monsterVariance, Transform parent, ref DFBlock blockData, bool serialize) { // Must have a dungeon type if (dungeonType == DFRegion.DungeonTypes.NoDungeon) { return; } // Get dungeon type index int dungeonIndex = (int)dungeonType >> 8; if (dungeonIndex < RandomEncounters.EncounterTables.Length) { // Get encounter table RandomEncounterTable table = RandomEncounters.EncounterTables[dungeonIndex]; // Get base monster index into table int baseMonsterIndex = (int)((float)table.Enemies.Length * monsterPower); // Set min index int minMonsterIndex = baseMonsterIndex - monsterVariance; if (minMonsterIndex < 0) { minMonsterIndex = 0; } // Set max index int maxMonsterIndex = baseMonsterIndex + monsterVariance; if (maxMonsterIndex >= table.Enemies.Length) { maxMonsterIndex = table.Enemies.Length; } // Get random monster from table MobileTypes type = table.Enemies[UnityEngine.Random.Range(minMonsterIndex, maxMonsterIndex)]; // Create unique LoadID for save sytem long loadID = 0; if (serialize) { loadID = (blockData.Index << 24) + obj.This; } // Add enemy AddEnemy(obj, type, parent, loadID); } else { DaggerfallUnity.LogMessage(string.Format("RDBLayout: Dungeon type {0} is out of range or unknown.", dungeonType), true); } }
public RDBLayout(string blockName) { this.dfUnity = DaggerfallUnity.Instance; blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(blockName); if (blockData.Type != DFBlock.BlockTypes.Rdb) { throw new Exception(string.Format("Could not load RDB block {0}", blockName), null); } }
/// <summary> /// Collect all quest markers from inside a dungeon. /// </summary> void EnumerateDungeonQuestMarkers(DFLocation location, out QuestMarker[] questSpawnMarkers, out QuestMarker[] questItemMarkers) { questSpawnMarkers = null; questItemMarkers = null; List <QuestMarker> questSpawnMarkerList = new List <QuestMarker>(); List <QuestMarker> questItemMarkerList = new List <QuestMarker>(); // Step through dungeon layout to find all blocks with markers foreach (var dungeonBlock in location.Dungeon.Blocks) { // Get block data DFBlock blockData = DaggerfallUnity.Instance.ContentReader.BlockFileReader.GetBlock(dungeonBlock.BlockName); // Iterate all groups foreach (DFBlock.RdbObjectRoot group in blockData.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { continue; } // Look for flats in this group foreach (DFBlock.RdbObject obj in group.RdbObjects) { // Look for editor flats Vector3 position = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; if (obj.Type == DFBlock.RdbResourceTypes.Flat) { if (obj.Resources.FlatResource.TextureArchive == editorFlatArchive) { switch (obj.Resources.FlatResource.TextureRecord) { case spawnMarkerFlatIndex: questSpawnMarkerList.Add(CreateQuestMarker(MarkerTypes.QuestSpawn, position, dungeonBlock.X, dungeonBlock.Z)); break; case itemMarkerFlatIndex: questItemMarkerList.Add(CreateQuestMarker(MarkerTypes.QuestItem, position, dungeonBlock.X, dungeonBlock.Z)); break; } } } } } } // Assign arrays if at least one quest marker found if (questSpawnMarkerList.Count > 0) { questSpawnMarkers = questSpawnMarkerList.ToArray(); } if (questItemMarkerList.Count > 0) { questItemMarkers = questItemMarkerList.ToArray(); } }
/// <summary> /// Adds RMB ground plane to block node. /// </summary> /// <param name="block">DFBlock</param> /// <param name="climate">ClimateSettings.</param> private void AddRMBGroundPlane(ref DFBlock block, BlockNode blockNode, DFLocation.ClimateSettings climate) { // Add ground plane node GroundPlaneNode groundNode = new GroundPlaneNode( textureManager.GraphicsDevice, textureManager.LoadGroundPlaneTexture(ref block, climate)); groundNode.Position = new Vector3(0f, groundHeight, 0f); blockNode.Add(groundNode); }
/// <summary> /// Add simple ground plane to block layout. /// </summary> public static GameObject AddGroundPlane( ref DFBlock blockData, Transform parent = null, ClimateBases climateBase = ClimateBases.Temperate, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } GameObject go = new GameObject("Ground"); if (parent != null) { go.transform.parent = parent; } // Assign components DaggerfallGroundPlane dfGround = go.AddComponent <DaggerfallGroundPlane>(); MeshFilter meshFilter = go.GetComponent <MeshFilter>(); // Assign climate and mesh Color32[] tileMap; Mesh mesh = dfUnity.MeshReader.GetSimpleGroundPlaneMesh( ref blockData, out tileMap, dfUnity.MeshReader.AddMeshTangents, dfUnity.MeshReader.AddMeshLightmapUVs); if (mesh) { meshFilter.sharedMesh = mesh; } // Assign tileMap and climate dfGround.tileMap = tileMap; dfGround.SetClimate(dfUnity, climateBase, climateSeason); // Assign collider if (dfUnity.Option_AddMeshColliders) { go.AddComponent <BoxCollider>(); } // Assign static if (dfUnity.Option_SetStaticFlags) { go.isStatic = true; } return(go); }
private static bool HasAction(DFBlock blockData, DFBlock.RdbObject obj, int modelReference) { // Allow for known action types DFBlock.RdbActionResource action = obj.Resources.ModelResource.ActionResource; if (action.Flags != 0) { return(true); } return(false); }
/// <summary> /// Add nature billboards. /// </summary> public static void AddNatureFlats( ref DFBlock blockData, Transform flatsParent, DaggerfallBillboardBatch billboardBatch = null, ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { // Get scenery item - ignore indices -1 (empty) and 0 (marker/waypoint of some kind) DFBlock.RmbGroundScenery scenery = blockData.RmbBlock.FldHeader.GroundData.GroundScenery[x, 15 - y]; if (scenery.TextureRecord < 1) { continue; } // Calculate position Vector3 billboardPosition = new Vector3( x * BlocksFile.TileDimension, natureFlatsOffsetY, y * BlocksFile.TileDimension + BlocksFile.TileDimension) * MeshReader.GlobalScale; // Get Archive int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason); // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(natureArchive, scenery.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Add billboard to batch or standalone if (billboardBatch != null) { billboardBatch.AddItem(scenery.TextureRecord, billboardPosition); } else { GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(natureArchive, scenery.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } } } }
/// <summary> /// Adds light flats and prefabs. /// </summary> public static void AddLights( ref DFBlock blockData, Transform flatsParent, Transform lightsParent, DaggerfallBillboardBatch billboardBatch = null) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Do nothing if import option not enabled or missing prefab if (!dfUnity.Option_ImportLightPrefabs || dfUnity.Option_CityLightPrefab == null) { return; } // Iterate block flats for lights foreach (DFBlock.RmbBlockFlatObjectRecord obj in blockData.RmbBlock.MiscFlatObjectRecords) { // Add point lights if (obj.TextureArchive == TextureReader.LightsTextureArchive) { // Calculate position Vector3 billboardPosition = new Vector3( obj.XPos, -obj.YPos + blockFlatsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; // Import custom 3d gameobject instead of flat if (MeshReplacement.ImportCustomFlatGameobject(obj.TextureArchive, obj.TextureRecord, billboardPosition, flatsParent) != null) { continue; } // Add billboard to batch or standalone if (billboardBatch != null) { billboardBatch.AddItem(obj.TextureRecord, billboardPosition); } else { GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(obj.TextureArchive, obj.TextureRecord, flatsParent); go.transform.position = billboardPosition; AlignBillboardToBase(go); } // Import light prefab AddLight(dfUnity, obj, lightsParent); } } }
public static void SaveDungeonFile(DFBlock rdbData, string path) { if (string.IsNullOrEmpty(path)) { return; } StreamWriter writer = new StreamWriter(path, false); string saveData = SaveLoadManager.Serialize(rdbData.GetType(), rdbData); writer.Write(saveData); writer.Close(); }
/// <summary> /// Grabs the relevant data from a DFBlock struct and creates a BlockRecord /// </summary> /// <param name="recordOut"></param> /// <param name="blockData"></param> public void CreateBlockRecordFromDFBlock(out BlockRecord recordOut, DFBlock blockData) { DFBlock.RdbTypes RdBType = DaggerfallUnity.Instance.ContentReader.BlockFileReader.GetRdbType(blockData.Name); bool hasStartMarkers = false; if (blockData.Type == DFBlock.BlockTypes.Rdb && !startMarkerFilter.Contains(blockData.Name) && !blockData.Name.StartsWith("B")) { hasStartMarkers = true; } recordOut = new BlockRecord(blockData.Name, blockData.Index, hasStartMarkers, blockData.Type, RdBType); }
private static bool IsActionDoor(DFBlock blockData, DFBlock.RdbObject obj, int modelReference) { // Check if this is a door (DOR) or double-door (DDR) string description = blockData.RdbBlock.ModelReferenceList[modelReference].Description; if (description == "DOR" || description == "DDR") { return(true); } return(false); }
/// <summary> /// Adds RMB scenery flats to block node. /// </summary> /// <param name="block">DFBlock</param> /// <param name="blockNode">BlockNode.</param> /// <param name="sceneryArchive">Scenery texture archive index.</param> private void AddRMBSceneryFlats(ref DFBlock block, BlockNode blockNode, int sceneryArchive) { // Flags TextureManager.TextureCreateFlags flags = TextureManager.TextureCreateFlags.Dilate | TextureManager.TextureCreateFlags.PreMultiplyAlpha; if (Core.GraphicsProfile == GraphicsProfile.HiDef) { flags |= TextureManager.TextureCreateFlags.MipMaps; } // Add block scenery for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { // Get scenery item DFBlock.RmbGroundScenery scenery = block.RmbBlock.FldHeader.GroundData.GroundScenery[x, y]; // Ignore 0 as this appears to be a marker/waypoint of some kind if (scenery.TextureRecord > 0) { // Load flat int textureKey; Vector2 startSize; Vector2 finalSize; if (true == LoadDaggerfallFlat( sceneryArchive, scenery.TextureRecord, flags, out textureKey, out startSize, out finalSize)) { // Calcuate position Vector3 position = new Vector3( x * tileSide, (finalSize.Y / 2) - 4, -rmbSide + y * tileSide); // Create billboard node BillboardNode billboardNode = new BillboardNode( BillboardNode.BillboardType.ClimateScenery, textureKey, finalSize); billboardNode.Position = position; blockNode.Add(billboardNode); } } } } }
private static void AddActionModelHelper( GameObject go, Dictionary <int, ActionLink> actionLinkDict, DFBlock.RdbObject rdbObj, ref DFBlock blockData, bool serialize) { DFBlock.RdbModelResource obj = rdbObj.Resources.ModelResource; string description = blockData.RdbBlock.ModelReferenceList[obj.ModelIndex].Description; int soundID_Index = obj.SoundIndex; float duration = obj.ActionResource.Duration; float magnitude = obj.ActionResource.Magnitude; int axis = obj.ActionResource.Axis; DFBlock.RdbTriggerFlags triggerFlag = DFBlock.RdbTriggerFlags.None; DFBlock.RdbActionFlags actionFlag = DFBlock.RdbActionFlags.None; if (actionLinkDict != null) { // Set action flag if valid / known if (Enum.IsDefined(typeof(DFBlock.RdbActionFlags), (DFBlock.RdbActionFlags)obj.ActionResource.Flags)) { actionFlag = (DFBlock.RdbActionFlags)obj.ActionResource.Flags; } // Set trigger flag if valid / known if (Enum.IsDefined(typeof(DFBlock.RdbTriggerFlags), (DFBlock.RdbTriggerFlags)obj.TriggerFlag_StartingLock)) { triggerFlag = (DFBlock.RdbTriggerFlags)obj.TriggerFlag_StartingLock; } // Add action node to actionLink dictionary if (!actionLinkDict.ContainsKey(rdbObj.This)) { ActionLink link; link.nextKey = obj.ActionResource.NextObjectOffset; link.prevKey = obj.ActionResource.PreviousObjectOffset; link.gameObject = go; actionLinkDict.Add(rdbObj.This, link); } } // Create unique LoadID for save sytem long loadID = 0; if (serialize) { loadID = (blockData.Index << 24) + rdbObj.This; } AddAction(go, description, soundID_Index, duration, magnitude, axis, triggerFlag, actionFlag, loadID); }
/// <summary> /// Adds miscellaneous RMB flats to block node. /// </summary> /// <param name="block">DFBlock</param> /// <param name="blockNode">BlockNode.</param> private void AddRMBMiscFlats(ref DFBlock block, BlockNode blockNode) { // Iterate through all misc flat records foreach (DFBlock.RmbBlockFlatObjectRecord obj in block.RmbBlock.MiscFlatObjectRecords) { // Get flat type BillboardNode.BillboardType billboardType = GetFlatType(obj.TextureArchive); // Flags TextureManager.TextureCreateFlags flags = TextureManager.TextureCreateFlags.Dilate | TextureManager.TextureCreateFlags.PreMultiplyAlpha; if (Core.GraphicsProfile == GraphicsProfile.HiDef) { flags |= TextureManager.TextureCreateFlags.MipMaps; } // Load flat int textureKey; Vector2 startSize; Vector2 finalSize; if (true == LoadDaggerfallFlat( obj.TextureArchive, obj.TextureRecord, flags, out textureKey, out startSize, out finalSize)) { // Calcuate position Vector3 position = new Vector3( obj.XPos, -obj.YPos + (finalSize.Y / 2) - 4, -rmbSide + -obj.ZPos); // Create billboard node BillboardNode billboardNode = new BillboardNode( billboardType, textureKey, finalSize); billboardNode.Position = position; blockNode.Add(billboardNode); // Add point light node if (billboardType == BillboardNode.BillboardType.Light) { AddPointLight(position, PointLightNode.ExteriorRadius, blockNode); } } } }
private static void AddModels( DaggerfallUnity dfUnity, ref DFBlock blockData, out List <StaticDoor> doorsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); // Iterate through all subrecords int recordCount = 0; foreach (DFBlock.RmbSubRecord subRecord in blockData.RmbBlock.SubRecords) { // Get subrecord transform Vector3 subRecordPosition = new Vector3(subRecord.XPos, 0, BlocksFile.RMBDimension - subRecord.ZPos) * MeshReader.GlobalScale; Vector3 subRecordRotation = new Vector3(0, -subRecord.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 subRecordMatrix = Matrix4x4.TRS(subRecordPosition, Quaternion.Euler(subRecordRotation), Vector3.one); // Iterate through models in this subrecord foreach (DFBlock.RmbBlock3dObjectRecord obj in subRecord.Exterior.Block3dObjectRecords) { // Get model transform Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos, obj.ZPos) * MeshReader.GlobalScale; Vector3 modelRotation = new Vector3(0, -obj.YRotation / BlocksFile.RotationDivisor, 0); Matrix4x4 modelMatrix = subRecordMatrix * Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), Vector3.one); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Does this model have doors? if (modelData.Doors != null) { doorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, recordCount, modelMatrix)); } // Add or combine if (combiner == null || IsCityGate(obj.ModelIdNum)) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } } // Increment record count recordCount++; } }
/// <summary> /// Constructs a scene node from the specified RMB block. /// </summary> /// <param name="block">DFBlock.</param> /// <param name="climate">Climate settings.</param> /// <returns>BlockNode.</returns> private BlockNode BuildRMBNode(ref DFBlock block, DFLocation.ClimateSettings climate) { // Create parent block node BlockNode blockNode = new BlockNode(block); // Add child nodes AddRMBModels(ref block, blockNode); AddRMBMiscModels(ref block, blockNode); AddRMBGroundPlane(ref block, blockNode, climate); AddRMBMiscFlats(ref block, blockNode); AddRMBSceneryFlats(ref block, blockNode, climate.SceneryArchive); return(blockNode); }
private static void AddActionFlatHelper( GameObject go, Dictionary <int, ActionLink> actionLinkDict, ref DFBlock blockData, DFBlock.RdbObject rdbObj, bool serialize = true) { DFBlock.RdbFlatResource obj = rdbObj.Resources.FlatResource; string description = "FLT"; int soundID_Index = obj.Sound_index; float duration = 0.0f; float magnitude = obj.Magnitude; int axis = obj.Magnitude; DFBlock.RdbTriggerFlags triggerFlag = DFBlock.RdbTriggerFlags.None; DFBlock.RdbActionFlags actionFlag = DFBlock.RdbActionFlags.None; //set action flag if valid / known if (Enum.IsDefined(typeof(DFBlock.RdbActionFlags), (DFBlock.RdbActionFlags)obj.Action)) { actionFlag = (DFBlock.RdbActionFlags)obj.Action; } //set trigger flag if valid / known if (Enum.IsDefined(typeof(DFBlock.RdbTriggerFlags), (DFBlock.RdbTriggerFlags)obj.TriggerFlag)) { triggerFlag = (DFBlock.RdbTriggerFlags)obj.TriggerFlag; } //add action node to actionLink dictionary if (!actionLinkDict.ContainsKey(rdbObj.This)) { ActionLink link; link.nextKey = obj.NextObjectOffset; link.prevKey = -1; link.gameObject = go; actionLinkDict.Add(rdbObj.This, link); } // Create unique LoadID for save sytem long loadID = 0; if (serialize) { loadID = (blockData.Index << 24) + rdbObj.This; } AddAction(go, description, soundID_Index, duration, magnitude, axis, triggerFlag, actionFlag, loadID); }
/// <summary> /// Add random enemies from encounter tables based on dungeon type, monster power, and seed. /// </summary> /// <param name="go">GameObject to add monsters to.</param> /// <param name="editorObjects">Editor objects containing flats.</param> /// <param name="dungeonType">Dungeon type selects the encounter table.</param> /// <param name="monsterPower">Value between 0-1 for lowest monster power to highest.</param> /// <param name="monsterVariance">Adjust final index +/- this value in encounter table.</param> /// <param name="seed">Random seed for encounters.</param> /// <param name="serialize">Allow for serialization when available.</param> public static void AddRandomEnemies( GameObject go, DFBlock.RdbObject[] editorObjects, DFRegion.DungeonTypes dungeonType, float monsterPower, ref DFBlock blockData, int monsterVariance = 4, int seed = 0, bool serialize = true) { const int randomMonsterFlatIndex = 15; DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Must have import enabled and prefab set if (!dfUnity.Option_ImportEnemyPrefabs || dfUnity.Option_EnemyPrefab == null) { return; } // Editor objects array must be populated if (editorObjects == null || editorObjects.Length == 0) { return; } // Add parent node GameObject randomEnemiesNode = new GameObject("Random Enemies"); randomEnemiesNode.transform.parent = go.transform; // Seed random generator UnityEngine.Random.seed = seed; // Iterate editor flats for enemies for (int i = 0; i < editorObjects.Length; i++) { // Add random enemy objects if (editorObjects[i].Resources.FlatResource.TextureRecord == randomMonsterFlatIndex) { AddRandomRDBEnemy(editorObjects[i], dungeonType, monsterPower, monsterVariance, randomEnemiesNode.transform, ref blockData, serialize); } } }
/// <summary> /// Constructs a scene node from the specified RDB block. /// </summary> /// <param name="block">DFBlock.</param> /// <returns>BlockNode.</returns> private BlockNode BuildRDBNode(ref DFBlock block) { // Create parent block node BlockNode blockNode = new BlockNode(block); // Clear action link dictionary actionLinkDict.Clear(); // Iterate through object groups int groupIndex = 0; foreach (DFBlock.RdbObjectRoot group in block.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { groupIndex++; continue; } // Iterate through objects in this group foreach (DFBlock.RdbObject obj in group.RdbObjects) { // Filter by type switch (obj.Type) { case DFBlock.RdbResourceTypes.Model: AddRDBModel(ref block, obj, blockNode, groupIndex); break; case DFBlock.RdbResourceTypes.Flat: AddRDBFlat(obj, blockNode); break; default: // Only drawing models and flats for now break; } } // Increment group index groupIndex++; } // Link action nodes LinkActionNodes(); return(blockNode); }
/// <summary> /// Adds miscellaneous RMB models to block node. /// </summary> /// <param name="block">DFBlock</param> /// <param name="blockNode">BlockNode.</param> private void AddRMBMiscModels(ref DFBlock block, BlockNode blockNode) { // Iterate through all misc records float degrees; foreach (DFBlock.RmbBlock3dObjectRecord obj in block.RmbBlock.Misc3dObjectRecords) { // Create misc record node SceneNode miscNode = CreateModelNode(obj.ModelIdNum); degrees = obj.YRotation / rotationDivisor; miscNode.Position = new Vector3(obj.XPos, -obj.YPos, -rmbSide + -obj.ZPos); miscNode.Rotation = new Vector3(0f, MathHelper.ToRadians(degrees), 0f); blockNode.Add(miscNode); } }
/// <summary> /// Checks for replacement block data. Only RDB block replacement is currently possible. /// </summary> /// Replacing entire RMB blocks is not currently possible as RmbFldGroundData contains /// groundData as a 2D array and FullSerializer can't do 2D arrays out of the box. /// <param name="block">Block index</param> /// <param name="blockName">Block name</param> /// <param name="dfBlock">DFBlock data output</param> /// <returns>True if replacement data found, false otherwise</returns> public static bool GetDFBlockReplacementData(int block, string blockName, out DFBlock dfBlock) { if (DaggerfallUnity.Settings.AssetInjection) { // Check the block cache and return if found or not (variants are not marked as not present) string variant = WorldDataVariants.GetBlockVariant(blockName); string blockKey = blockName + variant; if (blocks.ContainsKey(blockKey)) { dfBlock = blocks[blockKey]; return(dfBlock.Index != noReplacementBlock.Index); } string fileName = GetDFBlockReplacementFilename(blockName, variant); TextAsset blockReplacementJsonAsset; // Seek from loose files if (File.Exists(Path.Combine(worldDataPath, fileName))) { string blockReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName)); dfBlock = (DFBlock)SaveLoadManager.Deserialize(typeof(DFBlock), blockReplacementJson); } // Seek from mods else if (ModManager.Instance != null && ModManager.Instance.TryGetAsset(fileName, false, out blockReplacementJsonAsset)) { dfBlock = (DFBlock)SaveLoadManager.Deserialize(typeof(DFBlock), blockReplacementJsonAsset.text); } else { #if !UNITY_EDITOR // Cache that there's no replacement block data, non variant. So only look for replaced blocks once (unless running in editor) if (variant == WorldDataVariants.NoVariant) { blocks.Add(blockName, noReplacementBlock); } #endif dfBlock = noReplacementBlock; return(false); } dfBlock.Index = block; #if !UNITY_EDITOR // Cache block data for added/replaced blocks (unless running in editor) blocks.Add(blockKey, dfBlock); #endif Debug.LogFormat("Found DFBlock override: {0} (index: {1})", blockName, block); return(true); } dfBlock = noReplacementBlock; return(false); }
/// <summary> /// Stores miscellaneous flats. /// </summary> /// <param name="block">Block data.</param> private void AddRMBMiscFlats(ref DFBlock block) { // Add block flats foreach (DFBlock.RmbBlockFlatObjectRecord obj in block.RmbBlock.MiscFlatObjectRecords) { BlockFlat flat = new BlockFlat { Dungeon = false, Archive = obj.TextureArchive, Record = obj.TextureRecord, Position = new Vector3(obj.XPos, -obj.YPos, -obj.ZPos) * ModelManager.GlobalScale, Type = GetFlatType(obj.TextureArchive), }; blockFlats.Add(flat); } }
/// <summary> /// Builds an RDB block. /// </summary> /// <param name="blockData">Block data.</param> /// <param name="scene">Scene to add physics properties.</param> private void BuildRDB(ref DFBlock blockData, Scene scene) { // Add RDB data AddRDBObjects(ref blockData); // Finish batch building Seal(scene); // Set component bounding sphere this.BoundingSphere = new BoundingSphere( new Vector3( (BlocksFile.RDBDimension / 2) * ModelManager.GlobalScale, 0, (-(BlocksFile.RDBDimension / 2)) * ModelManager.GlobalScale), BlocksFile.RDBDimension * ModelManager.GlobalScale); }
/// <summary> /// Loads a Daggerfall block. /// </summary> /// <param name="name">Name of block.</param> /// <param name="block">DFBlock.</param> /// <returns>True if successful.</returns> private bool LoadDaggerfallBlock(string name, out DFBlock block) { try { // Load block block = blocksFile.GetBlock(name); } catch (Exception e) { Console.WriteLine(e.Message); block = new DFBlock(); return(false); } return(true); }
private static void AddProps( DaggerfallUnity dfUnity, ref DFBlock blockData, out List <StaticDoor> doorsOut, ModelCombiner combiner = null, Transform parent = null) { doorsOut = new List <StaticDoor>(); // Iterate through all misc records foreach (DFBlock.RmbBlock3dObjectRecord obj in blockData.RmbBlock.Misc3dObjectRecords) { // Get model transform Vector3 modelPosition = new Vector3(obj.XPos, -obj.YPos + propsOffsetY, obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale; Vector3 modelRotation = new Vector3(-obj.XRotation / BlocksFile.RotationDivisor, -obj.YRotation / BlocksFile.RotationDivisor, -obj.ZRotation / BlocksFile.RotationDivisor); Vector3 modelScale = GetModelScaleVector(obj); Matrix4x4 modelMatrix = Matrix4x4.TRS(modelPosition, Quaternion.Euler(modelRotation), modelScale); // Get model data ModelData modelData; dfUnity.MeshReader.GetModelData(obj.ModelIdNum, out modelData); // Does this model have doors? if (modelData.Doors != null) { doorsOut.AddRange(GameObjectHelper.GetStaticDoors(ref modelData, blockData.Index, 0, modelMatrix)); } // Import custom GameObject if (MeshReplacement.ImportCustomGameobject(obj.ModelIdNum, parent, modelMatrix) != null) { continue; } // Use Daggerfall Model // Add or combine if (combiner == null || IsBulletinBoard(obj.ModelIdNum) || PlayerActivate.HasCustomActivation(obj.ModelIdNum)) { AddStandaloneModel(dfUnity, ref modelData, modelMatrix, parent); } else { combiner.Add(ref modelData, modelMatrix); } } }
/// <summary> /// Add actions doors to block. /// </summary> public static void AddActionDoors(GameObject go, ref DFBlock blockData, int[] textureTable) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Use default texture table if one not specified if (textureTable == null) { textureTable = StaticTextureTables.DefaultTextureTable; } // Add parent node GameObject actionDoorsNode = new GameObject("Action Doors"); actionDoorsNode.transform.parent = go.transform; // Iterate all groups foreach (DFBlock.RdbObjectRoot group in blockData.RdbBlock.ObjectRootList) { // Skip empty object groups if (null == group.RdbObjects) { continue; } // Look for models in this group foreach (DFBlock.RdbObject obj in group.RdbObjects) { if (obj.Type == DFBlock.RdbResourceTypes.Model) { // Look for action doors int modelReference = obj.Resources.ModelResource.ModelIndex; uint modelId = blockData.RdbBlock.ModelReferenceList[modelReference].ModelIdNum; if (IsActionDoor(ref blockData, obj, modelReference)) { GameObject cgo = AddActionDoor(dfUnity, modelId, obj, actionDoorsNode.transform); cgo.GetComponent <DaggerfallMesh>().SetDungeonTextures(textureTable); } } } } }
/// <summary> /// Check if model is a hinged action door. /// </summary> private static bool IsActionDoor(ref DFBlock blockData, DFBlock.RdbObject obj, int modelReference) { // Always reject red brick doors, they are not action doors despite having "DOR" attached if (blockData.RdbBlock.ModelReferenceList[modelReference].ModelIdNum == redBrickDoorModelID) { return(false); } // Otherwise Check if this is a door (DOR) or double-door (DDR) string description = blockData.RdbBlock.ModelReferenceList[modelReference].Description; if (description == "DOR" || description == "DDR") { return(true); } return(false); }
/// <summary> /// Attempts to get a Daggerfall block from BLOCKS.BSA. /// </summary> /// <param name="name">Name of block.</param> /// <param name="blockOut">DFBlock data out.</param> /// <returns>True if successful.</returns> public bool GetBlock(string name, out DFBlock blockOut) { blockOut = new DFBlock(); if (!isReady) { return(false); } // Get block data blockOut = blockFileReader.GetBlock(name); if (blockOut.Type == DFBlock.BlockTypes.Unknown) { DaggerfallUnity.LogMessage(string.Format("Unknown block '{0}'.", name), true); return(false); } return(true); }
private void ReadRdbFlatResource(BinaryReader reader, ref DFBlock.RdbObject rdbObject) { // Go to resource offset reader.BaseStream.Position = rdbObject.ResourceOffset; // Read flat data rdbObject.Resources.FlatResource.TextureBitfield = reader.ReadUInt16(); rdbObject.Resources.FlatResource.TextureArchive = rdbObject.Resources.FlatResource.TextureBitfield >> 7; rdbObject.Resources.FlatResource.TextureRecord = rdbObject.Resources.FlatResource.TextureBitfield & 0x7f; rdbObject.Resources.FlatResource.TriggerFlag = reader.ReadUInt16(); rdbObject.Resources.FlatResource.Magnitude = reader.ReadByte(); rdbObject.Resources.FlatResource.SoundIndex = reader.ReadByte(); rdbObject.Resources.FlatResource.Gender = (DFBlock.RdbFlatGenders)rdbObject.Resources.FlatResource.TriggerFlag; rdbObject.Resources.FlatResource.FactionMobileId = BitConverter.ToUInt16(new byte[] { rdbObject.Resources.FlatResource.Magnitude, rdbObject.Resources.FlatResource.SoundIndex }, 0); rdbObject.Resources.FlatResource.NextObjectOffset = reader.ReadInt32(); rdbObject.Resources.FlatResource.Action = reader.ReadByte(); }
/// <summary> /// Read a flat object subrecord. /// </summary> /// <param name="reader">A binary reader to file.</param> /// <param name="recordsOut">Destination object.</param> private void ReadRmbFlatObjectRecords(BinaryReader reader, ref DFBlock.RmbBlockFlatObjectRecord[] recordsOut) { // Read all flat object records into array for (int i = 0; i < recordsOut.Length; i++) { recordsOut[i].XPos = reader.ReadInt32(); recordsOut[i].YPos = reader.ReadInt32(); recordsOut[i].ZPos = reader.ReadInt32(); recordsOut[i].TextureBitfield = reader.ReadUInt16(); recordsOut[i].TextureArchive = recordsOut[i].TextureBitfield >> 7; recordsOut[i].TextureRecord = recordsOut[i].TextureBitfield & 0x7f; recordsOut[i].Unknown1 = reader.ReadInt16(); recordsOut[i].Unknown2 = reader.ReadByte(); } }
/// <summary> /// Read block subrecords, i.e. the resources embedded in block data. /// </summary> /// <param name="reader">A binary reader to file</param> /// <param name="blockData">Destination record index</param> private void ReadRmbBlockSubRecord(BinaryReader reader, ref DFBlock.RmbBlockData blockData) { // Header blockData.Header.Position = reader.BaseStream.Position; blockData.Header.Num3dObjectRecords = reader.ReadByte(); blockData.Header.NumFlatObjectRecords = reader.ReadByte(); blockData.Header.NumSection3Records = reader.ReadByte(); blockData.Header.NumPeopleRecords = reader.ReadByte(); blockData.Header.NumDoorRecords = reader.ReadByte(); blockData.Header.Unknown1 = reader.ReadInt16(); blockData.Header.Unknown2 = reader.ReadInt16(); blockData.Header.Unknown3 = reader.ReadInt16(); blockData.Header.Unknown4 = reader.ReadInt16(); blockData.Header.Unknown5 = reader.ReadInt16(); blockData.Header.Unknown6 = reader.ReadInt16(); // 3D object records blockData.Block3dObjectRecords = new DFBlock.RmbBlock3dObjectRecord[blockData.Header.Num3dObjectRecords]; ReadRmbModelRecords(reader, ref blockData.Block3dObjectRecords); // Flat object record blockData.BlockFlatObjectRecords = new DFBlock.RmbBlockFlatObjectRecord[blockData.Header.NumFlatObjectRecords]; ReadRmbFlatObjectRecords(reader, ref blockData.BlockFlatObjectRecords); // Section3 records int numSection3Records = blockData.Header.NumSection3Records; blockData.BlockSection3Records = new DFBlock.RmbBlockSection3Record[numSection3Records]; for (int i = 0; i < numSection3Records; i++) { blockData.BlockSection3Records[i].XPos = reader.ReadInt32(); blockData.BlockSection3Records[i].YPos = reader.ReadInt32(); blockData.BlockSection3Records[i].ZPos = reader.ReadInt32(); blockData.BlockSection3Records[i].Unknown1 = reader.ReadByte(); blockData.BlockSection3Records[i].Unknown2 = reader.ReadByte(); blockData.BlockSection3Records[i].Unknown3 = reader.ReadInt16(); } // People records int numPeopleRecords = blockData.Header.NumPeopleRecords; blockData.BlockPeopleRecords = new DFBlock.RmbBlockPeopleRecord[numPeopleRecords]; for (int i = 0; i < numPeopleRecords; i++) { blockData.BlockPeopleRecords[i].XPos = reader.ReadInt32(); blockData.BlockPeopleRecords[i].YPos = reader.ReadInt32(); blockData.BlockPeopleRecords[i].ZPos = reader.ReadInt32(); blockData.BlockPeopleRecords[i].TextureBitfield = reader.ReadUInt16(); blockData.BlockPeopleRecords[i].TextureArchive = blockData.BlockPeopleRecords[i].TextureBitfield >> 7; blockData.BlockPeopleRecords[i].TextureRecord = blockData.BlockPeopleRecords[i].TextureBitfield & 0x7f; blockData.BlockPeopleRecords[i].NpcType = reader.ReadInt16(); blockData.BlockPeopleRecords[i].Unknown1 = reader.ReadByte(); } // Door records int numDoorRecords = blockData.Header.NumDoorRecords; blockData.BlockDoorRecords = new DFBlock.RmbBlockDoorRecord[numDoorRecords]; for (int i = 0; i < numDoorRecords; i++) { blockData.BlockDoorRecords[i].This = (Int32)reader.BaseStream.Position; blockData.BlockDoorRecords[i].XPos = reader.ReadInt32(); blockData.BlockDoorRecords[i].YPos = reader.ReadInt32(); blockData.BlockDoorRecords[i].ZPos = reader.ReadInt32(); blockData.BlockDoorRecords[i].YRotation = reader.ReadInt16(); blockData.BlockDoorRecords[i].OpenRotation = reader.ReadInt16(); blockData.BlockDoorRecords[i].Unknown3 = reader.ReadInt16(); blockData.BlockDoorRecords[i].NullValue1 = reader.ReadByte(); } }
private void ReadRdbObjects(BinaryReader reader, ref DFBlock.RdbObjectRoot objectRoot) { // Go to root of object linked list reader.BaseStream.Position = objectRoot.RootOffset; // Iterate through RDB objects linked list to build managed // array and collect baseline information about all objects. int index = 0; while (true) { // Read object data objectRoot.RdbObjects[index].This = (Int32)reader.BaseStream.Position; objectRoot.RdbObjects[index].Next = reader.ReadInt32(); objectRoot.RdbObjects[index].Previous = reader.ReadInt32(); objectRoot.RdbObjects[index].Index = index; objectRoot.RdbObjects[index].XPos = reader.ReadInt32(); objectRoot.RdbObjects[index].YPos = reader.ReadInt32(); objectRoot.RdbObjects[index].ZPos = reader.ReadInt32(); objectRoot.RdbObjects[index].Type = (DFBlock.RdbResourceTypes)reader.ReadByte(); objectRoot.RdbObjects[index].ResourceOffset = reader.ReadUInt32(); objectRoot.RdbObjects[index].Resources.ModelResource.ActionResource.PreviousObjectOffset = -1; objectRoot.RdbObjects[index].Resources.ModelResource.ActionResource.NextObjectIndex = -1; // Exit if finished if (objectRoot.RdbObjects[index].Next < 0) break; // Go to next offset in list reader.BaseStream.Position = objectRoot.RdbObjects[index].Next; // Increment index index++; } // Iterate through managed array to read specific resources for each object for (int i = 0; i < objectRoot.RdbObjects.Length; i++) { // Read resource-specific data switch (objectRoot.RdbObjects[i].Type) { case DFBlock.RdbResourceTypes.Model: ReadRdbModelResource(reader, ref objectRoot.RdbObjects[i], objectRoot.RdbObjects); break; case DFBlock.RdbResourceTypes.Flat: ReadRdbFlatResource(reader, ref objectRoot.RdbObjects[i]); break; case DFBlock.RdbResourceTypes.Light: ReadRdbLightResource(reader, ref objectRoot.RdbObjects[i]); break; default: throw (new Exception("Unknown RDB resource type encountered.")); } } }
private void ReadRdbModelResource(BinaryReader reader, ref DFBlock.RdbObject rdbObject, DFBlock.RdbObject[] rdbObjects) { // Go to resource offset reader.BaseStream.Position = rdbObject.ResourceOffset; // Read model data rdbObject.Resources.ModelResource.XRotation = reader.ReadInt32(); rdbObject.Resources.ModelResource.YRotation = reader.ReadInt32(); rdbObject.Resources.ModelResource.ZRotation = reader.ReadInt32(); rdbObject.Resources.ModelResource.ModelIndex = reader.ReadUInt16(); rdbObject.Resources.ModelResource.TriggerFlag_StartingLock = reader.ReadUInt32(); rdbObject.Resources.ModelResource.SoundIndex = reader.ReadByte(); rdbObject.Resources.ModelResource.ActionOffset = reader.ReadInt32(); // Read action data if (rdbObject.Resources.ModelResource.ActionOffset > 0) ReadRdbModelActionRecords(reader, ref rdbObject, rdbObjects); }
/// <summary> /// Read a 3D object subrecord. /// </summary> /// <param name="reader">A binary reader to file.</param> /// <param name="recordsOut">Destination object.</param> private void ReadRmbModelRecords(BinaryReader reader, ref DFBlock.RmbBlock3dObjectRecord[] recordsOut) { // Read all 3d object records into array for (int i = 0; i < recordsOut.Length; i++) { Int16 objectId1 = reader.ReadInt16(); Byte objectId2 = reader.ReadByte(); recordsOut[i].ObjectId1 = objectId1; recordsOut[i].ObjectId2 = objectId2; recordsOut[i].ModelId = ((objectId1 * 100) + objectId2).ToString(); recordsOut[i].ModelIdNum = UInt32.Parse(recordsOut[i].ModelId); recordsOut[i].ObjectType = reader.ReadByte(); recordsOut[i].Unknown1 = reader.ReadUInt32(); recordsOut[i].Unknown2 = reader.ReadUInt32(); recordsOut[i].Unknown3 = reader.ReadUInt32(); recordsOut[i].NullValue1 = reader.ReadUInt64(); recordsOut[i].XPos1 = reader.ReadInt32(); recordsOut[i].YPos1 = reader.ReadInt32(); recordsOut[i].ZPos1 = reader.ReadInt32(); recordsOut[i].XPos = reader.ReadInt32(); recordsOut[i].YPos = reader.ReadInt32(); recordsOut[i].ZPos = reader.ReadInt32(); recordsOut[i].NullValue2 = reader.ReadUInt32(); recordsOut[i].YRotation = reader.ReadInt16(); recordsOut[i].Unknown4 = reader.ReadUInt16(); recordsOut[i].NullValue3 = reader.ReadUInt32(); recordsOut[i].Unknown5 = reader.ReadUInt32(); recordsOut[i].NullValue4 = reader.ReadUInt16(); } }
private void ReadRdbLightResource(BinaryReader reader, ref DFBlock.RdbObject rdbObject) { // Go to resource offset reader.BaseStream.Position = rdbObject.ResourceOffset; // Read light data rdbObject.Resources.LightResource.Unknown1 = reader.ReadUInt32(); rdbObject.Resources.LightResource.Unknown2 = reader.ReadUInt32(); rdbObject.Resources.LightResource.Radius = reader.ReadUInt16(); }
private void ReadRdbFlatResource(BinaryReader reader, ref DFBlock.RdbObject rdbObject) { // Go to resource offset reader.BaseStream.Position = rdbObject.ResourceOffset; // Read flat data rdbObject.Resources.FlatResource.TextureBitfield = reader.ReadUInt16(); rdbObject.Resources.FlatResource.TextureArchive = rdbObject.Resources.FlatResource.TextureBitfield >> 7; rdbObject.Resources.FlatResource.TextureRecord = rdbObject.Resources.FlatResource.TextureBitfield & 0x7f; rdbObject.Resources.FlatResource.Gender = (DFBlock.RdbFlatGenders)reader.ReadUInt16(); rdbObject.Resources.FlatResource.FactionMobileId = reader.ReadUInt16(); rdbObject.Resources.FlatResource.FlatData.Unknown1 = reader.ReadByte(); rdbObject.Resources.FlatResource.FlatData.Unknown2 = reader.ReadByte(); rdbObject.Resources.FlatResource.FlatData.Unknown3 = reader.ReadByte(); rdbObject.Resources.FlatResource.FlatData.Unknown4 = reader.ReadByte(); rdbObject.Resources.FlatResource.FlatData.Reaction = reader.ReadByte(); }
private void ReadRdbModelActionRecords(BinaryReader reader, ref DFBlock.RdbObject rdbObject, DFBlock.RdbObject[] rdbObjects) { // Go to action offset reader.BaseStream.Position = rdbObject.Resources.ModelResource.ActionOffset; // Read action data rdbObject.Resources.ModelResource.ActionResource.Position = reader.BaseStream.Position; rdbObject.Resources.ModelResource.ActionResource.Axis = reader.ReadByte(); rdbObject.Resources.ModelResource.ActionResource.Duration = reader.ReadUInt16(); rdbObject.Resources.ModelResource.ActionResource.Magnitude = reader.ReadUInt16(); rdbObject.Resources.ModelResource.ActionResource.NextObjectOffset = reader.ReadInt32(); rdbObject.Resources.ModelResource.ActionResource.Flags = reader.ReadByte(); // Exit if no action target if (rdbObject.Resources.ModelResource.ActionResource.NextObjectOffset < 0) return; // Find index of action offset in object array int index = 0; foreach (DFBlock.RdbObject obj in rdbObjects) { if (obj.This == rdbObject.Resources.ModelResource.ActionResource.NextObjectOffset) { // Set target and and parent indices rdbObject.Resources.ModelResource.ActionResource.NextObjectIndex = index; rdbObjects[index].Resources.ModelResource.ActionResource.PreviousObjectOffset = rdbObject.This; break; } index++; } }
private int CountRdbObjects(BinaryReader reader, ref DFBlock.RdbObjectRoot objectRoot) { // Go to root of object linked list reader.BaseStream.Position = objectRoot.RootOffset; // Count objects in list int objectCount = 0; while (true) { // Increment object count objectCount++; // Get next position and exit if finished long next = reader.ReadInt32(); if (next < 0) break; // Go to next offset in list reader.BaseStream.Position = next; } return objectCount; }