/// <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);
            }
        }
Beispiel #3
0
        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);
     }
 }
Beispiel #5
0
        /// <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);
        }
Beispiel #7
0
        /// <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);
        }
Beispiel #9
0
        /// <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);
                    }
                }
            }
        }
Beispiel #10
0
        /// <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);
                        }
                    }
                }
            }
        }
Beispiel #15
0
        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);
                    }
                }
            }
        }
Beispiel #17
0
        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);
        }
Beispiel #19
0
        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);
        }
Beispiel #20
0
        /// <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);
        }
Beispiel #27
0
        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);
        }
Beispiel #30
0
        /// <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;
        }