Example #1
0
        /// <summary>
        /// Gets the dominant race in player's current region.
        /// This seems to be based on subclimate rather than FACTION.TXT.
        /// The faction data has very little diversity and does not match observed race in many regions.
        /// </summary>
        public Races GetRaceOfCurrentRegion()
        {
            // Racial distribution in Daggerfall:
            //  * Desert, Desert2, Rainforest = Redguard
            //  * Mountain, MountainWoods = Nord
            //  * Swamp, Subtropical, Woodlands, Default = Breton

            DFLocation.ClimateSettings settings = MapsFile.GetWorldClimateSettings(climateSettings.WorldClimate);
            switch (settings.People)
            {
            case FactionFile.FactionRaces.Redguard:
                return(Races.Redguard);

            case FactionFile.FactionRaces.Nord:
                return(Races.Nord);

            case FactionFile.FactionRaces.Breton:
            default:
                return(Races.Breton);
            }
        }
Example #2
0
        /// <summary>
        /// Updates discovered building data in current location.
        /// </summary>
        /// <param name="discoveredBuilding">Updated data to write back to live discovery database.</param>
        void UpdateDiscoveredBuilding(DiscoveredBuilding discoveredBuildingIn)
        {
            // Must have discovered building
            if (!HasDiscoveredBuilding(discoveredBuildingIn.buildingKey))
            {
                return;
            }

            // Get the location discovery for this mapID
            int mapPixelID        = MapsFile.GetMapPixelIDFromLongitudeLatitude((int)CurrentLocation.MapTableData.Longitude, CurrentLocation.MapTableData.Latitude);
            DiscoveredLocation dl = discoveredLocations[mapPixelID];

            if (dl.discoveredBuildings == null)
            {
                return;
            }

            // Replace discovery data for building
            dl.discoveredBuildings.Remove(discoveredBuildingIn.buildingKey);
            dl.discoveredBuildings.Add(discoveredBuildingIn.buildingKey, discoveredBuildingIn);
        }
        /// <summary>
        /// Creates a new block node.
        /// </summary>
        /// <param name="name">Block name.</param>
        /// <param name="climate">Climate settings.</param>
        /// <param name="clearGroundTextures">Clear ground plane texture dictionary.</param>
        /// <returns>BlockNode.</returns>
        public BlockNode CreateBlockNode(string name, DFLocation.ClimateSettings?climate, bool clearGroundTextures)
        {
            // Load block
            DFBlock block;

            if (!LoadDaggerfallBlock(name, out block))
            {
                return(null);
            }

            // Set default world climate
            if (climate == null)
            {
                climate = MapsFile.GetWorldClimateSettings(defaultWorldClimate);
            }

            // Reset ground plane texture cache
            if (clearGroundTextures)
            {
                textureManager.ClearGroundTextures();
            }

            // Build node
            BlockNode node = null;

            switch (block.Type)
            {
            case DFBlock.BlockTypes.Rmb:
                textureManager.ClimateType = climate.Value.ClimateType;
                node = BuildRMBNode(ref block, climate.Value);
                break;

            case DFBlock.BlockTypes.Rdb:
                textureManager.ClimateType = DFLocation.ClimateBaseType.None;
                node = BuildRDBNode(ref block);
                break;
            }

            return(node);
        }
        /// <summary>
        /// Constructor. Accepts pre-created TextureManager, ModelManager,
        ///  BlocksFile, and MapsFile objects.
        ///  These content managers cannot be NULL and must
        ///  be configured ready for use.
        /// </summary>
        /// <param name="textureManager">TextureManager.</param>
        /// <param name="modelManager">ModelManager</param>
        /// <param name="blocksFile">BlocksFile.</param>
        /// <param name="mapsFile">MapsFile.</param>
        public SceneBuilder(
            TextureManager textureManager,
            ModelManager modelManager,
            BlocksFile blocksFile,
            MapsFile mapsFile)
        {
            // Check managers non-null
            if (textureManager == null ||
                modelManager == null ||
                blocksFile == null ||
                mapsFile == null)
            {
                throw new Exception(
                          "One or more content managers are NULL.");
            }

            // Store
            this.textureManager = textureManager;
            this.modelManager   = modelManager;
            this.blocksFile     = blocksFile;
            this.mapsFile       = mapsFile;
        }
        /// <summary>
        /// Setup API file readers.
        /// </summary>
        private void SetupReaders()
        {
            // Try to setup Arena2-dependent content readers
            if (blockFileReader == null)
            {
                blockFileReader = new BlocksFile(Path.Combine(arena2Path, BlocksFile.Filename), FileUsage.UseMemory, true);
            }
            if (mapFileReader == null)
            {
                mapFileReader = new MapsFile(Path.Combine(arena2Path, MapsFile.Filename), FileUsage.UseMemory, true);
            }
            if (monsterFileReader == null)
            {
                monsterFileReader = new MonsterFile(Path.Combine(arena2Path, MonsterFile.Filename), FileUsage.UseMemory, true);
            }
            if (woodsFileReader == null)
            {
                woodsFileReader = new WoodsFile(Path.Combine(arena2Path, WoodsFile.Filename), FileUsage.UseMemory, true);
            }
            if (factionFileReader == null)
            {
                factionFileReader = new FactionFile(Path.Combine(arena2Path, FactionFile.Filename), FileUsage.UseMemory, true);
            }
            if (flatsFileReader == null)
            {
                flatsFileReader = new FlatsFile(Path.Combine(arena2Path, FlatsFile.Filename), FileUsage.UseMemory, true);
            }
            //if (spellIconCollection == null)
            //    spellIconCollection = new SpellIconCollection();

            // Build map lookup dictionary
            if (mapDict == null && mapFileReader != null)
            {
                EnumerateMaps();
            }

            // Raise ready flag
            isReady = true;
        }
Example #6
0
        /// <summary>
        /// Convert a scene position back into virtual world space.
        /// This is specific to the peered location due to floating origin.
        /// </summary>
        /// <param name="scenePosition">Scene position to convert to nearest point in world space.</param>
        /// <returns>World DFPosition.</returns>
        public DFPosition SceneToWorldPosition(Vector3 scenePosition)
        {
            // Validate DaggerfallLocation
            if (!dfLocation || dfLocation.Summary.MapID == 0)
            {
                throw new Exception(noLocationError);
            }

            // Get location origin in both scene and world
            // The SW origin of location in scene spaces aligns with SW terrain tile origin in world space
            Vector3    locationOrigin = dfLocation.transform.position;
            DFPosition worldOrigin    = MapsFile.MapPixelToWorldCoord(dfLocation.Summary.MapPixelX, dfLocation.Summary.MapPixelY);

            // Get difference between origin and target position in scene space
            Vector3 difference = scenePosition - locationOrigin;

            // Convert difference into Daggerfall units and apply to origin in world space
            DFPosition result = new DFPosition(
                (int)(difference.x * StreamingWorld.SceneMapRatio) + worldOrigin.X,
                (int)(difference.z * StreamingWorld.SceneMapRatio) + worldOrigin.Y);

            return(result);
        }
        /// <summary>
        /// Gets discovered building data for current location.
        /// </summary>
        /// <param name="buildingKey">Building key in current location.</param>
        /// <param name="discoveredBuildingOut">Building discovery data out.</param>
        /// <returns>True if building discovered, false if building not discovered.</returns>
        public bool GetDiscoveredBuilding(int buildingKey, out DiscoveredBuilding discoveredBuildingOut)
        {
            discoveredBuildingOut = new DiscoveredBuilding();

            // Must have discovered building
            if (!HasDiscoveredBuilding(buildingKey))
            {
                return(false);
            }

            // Get the location discovery for this mapID
            int mapPixelID        = MapsFile.GetMapPixelIDFromLongitudeLatitude((int)CurrentLocation.MapTableData.Longitude, CurrentLocation.MapTableData.Latitude);
            DiscoveredLocation dl = discoveredLocations[mapPixelID];

            if (dl.discoveredBuildings == null)
            {
                return(false);
            }

            // Get discovery data for building
            bool discovered = GetBuildingDiscoveryData(buildingKey, out discoveredBuildingOut);

            return(true);
        }
Example #8
0
        public override void Update(Task caller)
        {
            base.Update(caller);

            // Do nothing while player respawning
            if (GameManager.Instance.PlayerEnterExit.IsRespawning)
            {
                return;
            }

            // Handle resume on next tick of action after respawn process complete
            if (resumePending)
            {
                GameObject player = GameManager.Instance.PlayerObject;
                player.transform.position = resumePosition;
                resumePending             = false;
                SetComplete();
                return;
            }

            // Create SiteLink if not already present
            if (!QuestMachine.HasSiteLink(ParentQuest, targetPlace))
            {
                QuestMachine.CreateSiteLink(ParentQuest, targetPlace);
            }

            // Attempt to get Place resource
            Place place = ParentQuest.GetPlace(targetPlace);

            if (place == null)
            {
                return;
            }

            // Get selected spawn QuestMarker for this Place
            bool        usingMarker = false;
            QuestMarker marker      = new QuestMarker();

            if (targetMarker >= 0 && targetMarker < place.SiteDetails.questSpawnMarkers.Length)
            {
                marker      = place.SiteDetails.questSpawnMarkers[targetMarker];
                usingMarker = true;
            }

            // Attempt to get location data - using GetLocation(regionName, locationName) as it can support all locations
            DFLocation location;

            if (!DaggerfallUnity.Instance.ContentReader.GetLocation(place.SiteDetails.regionName, place.SiteDetails.locationName, out location))
            {
                return;
            }

            // Spawn inside dungeon at this world position
            DFPosition mapPixel = MapsFile.LongitudeLatitudeToMapPixel((int)location.MapTableData.Longitude, location.MapTableData.Latitude);
            DFPosition worldPos = MapsFile.MapPixelToWorldCoord(mapPixel.X, mapPixel.Y);

            GameManager.Instance.PlayerEnterExit.RespawnPlayer(
                worldPos.X,
                worldPos.Y,
                true,
                true);

            // Determine start position
            if (usingMarker)
            {
                // Use specified quest marker
                Vector3 dungeonBlockPosition = new Vector3(marker.dungeonX * RDBLayout.RDBSide, 0, marker.dungeonZ * RDBLayout.RDBSide);
                resumePosition = dungeonBlockPosition + marker.flatPosition;
            }
            else
            {
                // Use first quest marker
                marker = place.SiteDetails.questSpawnMarkers[0];
                Vector3 dungeonBlockPosition = new Vector3(marker.dungeonX * RDBLayout.RDBSide, 0, marker.dungeonZ * RDBLayout.RDBSide);
                resumePosition = dungeonBlockPosition + marker.flatPosition;
            }

            resumePending = true;
        }
Example #9
0
        private void UpdateMode(TransportModes transportMode)
        {
            // Update the transport mode and stop any riding sounds playing.
            mode = transportMode;
            if (ridingAudioSource.isPlaying)
            {
                ridingAudioSource.Stop();
            }

            if (mode == TransportModes.Horse || mode == TransportModes.Cart)
            {
                // Tell player motor we're riding.
                playerMotor.IsRiding = true;

                // Setup appropriate riding sounds.
                SoundClips sound = (mode == TransportModes.Horse) ? horseRidingSound2 : cartRidingSound;
                ridingAudioSource.clip = dfAudioSource.GetAudioClip((int)sound);

                // Setup appropriate riding textures.
                string textureName = (mode == TransportModes.Horse) ? horseTextureName : cartTextureName;
                for (int i = 0; i < 4; i++)
                {
                    ridingTexures[i] = ImageReader.GetImageData(textureName, 0, i, true, true);
                }
                ridingTexture = ridingTexures[0];

                // Initialise neighing timer.
                neighTime = Time.time + Random.Range(1, 5);
            }
            else
            {
                // Tell player motor we're not riding.
                playerMotor.IsRiding = false;
            }

            if (mode == TransportModes.Ship)
            {
                GameManager.Instance.PlayerMotor.CancelMovement = true;
                SerializablePlayer serializablePlayer = GetComponent <SerializablePlayer>();
                DaggerfallUI.Instance.FadeBehaviour.SmashHUDToBlack();
                StreamingWorld world      = GameManager.Instance.StreamingWorld;
                DFPosition     shipCoords = DaggerfallBankManager.GetShipCoords();

                // Is there recorded position before boarding and is player on the ship?
                if (IsOnShip())
                {
                    // Check for terrain sampler changes. (so don't fall through floor)
                    StreamingWorld.RepositionMethods reposition = StreamingWorld.RepositionMethods.None;
                    if (boardShipPosition.terrainSamplerName != DaggerfallUnity.Instance.TerrainSampler.ToString() ||
                        boardShipPosition.terrainSamplerVersion != DaggerfallUnity.Instance.TerrainSampler.Version)
                    {
                        reposition = StreamingWorld.RepositionMethods.RandomStartMarker;
                        if (DaggerfallUI.Instance.DaggerfallHUD != null)
                        {
                            DaggerfallUI.Instance.DaggerfallHUD.PopupText.AddText("Terrain sampler changed. Repositioning player.");
                        }
                    }
                    // Restore player position from before boarding ship, caching ship scene first.
                    SaveLoadManager.CacheScene(world.SceneName);    // TODO: Should this should move into teleport to support other teleports? Issue only if inside. (e.g. recall)
                    DFPosition mapPixel = MapsFile.WorldCoordToMapPixel(boardShipPosition.worldPosX, boardShipPosition.worldPosZ);
                    world.TeleportToCoordinates(mapPixel.X, mapPixel.Y, reposition);
                    serializablePlayer.RestorePosition(boardShipPosition);
                    boardShipPosition = null;
                    // Restore cached scene (ship is special case, cache will not be cleared)
                    SaveLoadManager.RestoreCachedScene(world.SceneName);
                }
                else
                {
                    // Record current player position before boarding ship, and cache scene. (ship is special case, cache will not be cleared)
                    boardShipPosition = serializablePlayer.GetPlayerPositionData();
                    SaveLoadManager.CacheScene(world.SceneName);
                    // Teleport to the players ship, restoring cached scene.
                    world.TeleportToCoordinates(shipCoords.X, shipCoords.Y, StreamingWorld.RepositionMethods.RandomStartMarker);
                    SaveLoadManager.RestoreCachedScene(world.SceneName);
                }
                DaggerfallUI.Instance.FadeBehaviour.FadeHUDFromBlack();
                mode = TransportModes.Foot;
            }
        }
        // Drops nature flats based on random chance scaled by simple rules
        public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale, int terrainDist)
        {
            const float maxSteepness      = 50f;        // 50
            const float slopeSinkRatio    = 70f;        // Sink flats slightly into ground as slope increases to prevent floaty trees.
            const float baseChanceOnDirt  = 0.2f;       // 0.2
            const float baseChanceOnGrass = 0.9f;       // 0.4
            const float baseChanceOnStone = 0.05f;      // 0.05

            // Location Rect is expanded slightly to give extra clearance around locations
            const int natureClearance = 4;
            Rect      rect            = dfTerrain.MapData.locationRect;

            if (rect.x > 0 && rect.y > 0)
            {
                rect.xMin -= natureClearance;
                rect.xMax += natureClearance;
                rect.yMin -= natureClearance;
                rect.yMax += natureClearance;
            }
            // Chance scaled based on map pixel height
            // This tends to produce sparser lowlands and denser highlands
            // Adjust or remove clamp range to influence nature generation
            float elevationScale = (dfTerrain.MapData.worldHeight / 128f);

            elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f);

            // Chance scaled by base climate type
            float climateScale = 1.0f;

            DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
            switch (climate.ClimateType)
            {
            case DFLocation.ClimateBaseType.Desert:             // Just lower desert for now
                climateScale = 0.25f;
                break;
            }
            float chanceOnDirt  = baseChanceOnDirt * elevationScale * climateScale;
            float chanceOnGrass = baseChanceOnGrass * elevationScale * climateScale;
            float chanceOnStone = baseChanceOnStone * elevationScale * climateScale;

            // Get terrain
            Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>();

            if (!terrain)
            {
                return;
            }

            // Get terrain data
            TerrainData terrainData = terrain.terrainData;

            if (!terrainData)
            {
                return;
            }

            // Remove exiting billboards
            dfBillboardBatch.Clear();
            MeshReplacement.ClearNatureGameObjects(terrain);

            // Seed random with terrain key
            Random.InitState(MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY));

            // Just layout some random flats spread evenly across entire map pixel area
            // Flats are aligned with tiles, max 16129 billboards per batch
            Vector2 tilePos          = Vector2.zero;
            int     tDim             = MapsFile.WorldMapTileDim;
            int     hDim             = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension;
            float   scale            = terrainData.heightmapScale.x * (float)hDim / (float)tDim;
            float   maxTerrainHeight = DaggerfallUnity.Instance.TerrainSampler.MaxTerrainHeight;
            float   beachLine        = DaggerfallUnity.Instance.TerrainSampler.BeachElevation;

            for (int y = 0; y < tDim; y++)
            {
                for (int x = 0; x < tDim; x++)
                {
                    // Reject based on steepness
                    float steepness = terrainData.GetSteepness((float)x / tDim, (float)y / tDim);
                    if (steepness > maxSteepness)
                    {
                        continue;
                    }

                    // Reject if inside location rect (expanded slightly to give extra clearance around locations)
                    tilePos.x = x;
                    tilePos.y = y;
                    if (rect.x > 0 && rect.y > 0 && rect.Contains(tilePos))
                    {
                        continue;
                    }

                    // Chance also determined by tile type
                    int tile = dfTerrain.MapData.tilemapSamples[x, y] & 0x3F;
                    if (tile == 1)
                    {   // Dirt
                        if (Random.Range(0f, 1f) > chanceOnDirt)
                        {
                            continue;
                        }
                    }
                    else if (tile == 2)
                    {   // Grass
                        if (Random.Range(0f, 1f) > chanceOnGrass)
                        {
                            continue;
                        }
                    }
                    else if (tile == 3)
                    {   // Stone
                        if (Random.Range(0f, 1f) > chanceOnStone)
                        {
                            continue;
                        }
                    }
                    else
                    {   // Anything else
                        continue;
                    }

                    int   hx     = (int)Mathf.Clamp(hDim * ((float)x / (float)tDim), 0, hDim - 1);
                    int   hy     = (int)Mathf.Clamp(hDim * ((float)y / (float)tDim), 0, hDim - 1);
                    float height = dfTerrain.MapData.heightmapSamples[hy, hx] * maxTerrainHeight;  // x & y swapped in heightmap for TerrainData.SetHeights()

                    // Reject if too close to water
                    if (height < beachLine)
                    {
                        continue;
                    }

                    // Sample height and position billboard
                    Vector3 pos     = new Vector3(x * scale, 0, y * scale);
                    float   height2 = terrain.SampleHeight(pos + terrain.transform.position);
                    pos.y = height2 - (steepness / slopeSinkRatio);

                    // Add to batch unless a mesh replacement is found
                    int record = Random.Range(1, 32);
                    if (terrainDist > 1 || !MeshReplacement.ImportNatureGameObject(dfBillboardBatch.TextureArchive, record, terrain, x, y))
                    {
                        dfBillboardBatch.AddItem(record, pos);
                    }
                    else if (!NatureMeshUsed)
                    {
                        NatureMeshUsed = true;  // Signal that nature mesh has been used to initiate extra terrain updates
                    }
                }
            }

            // Apply new batch
            dfBillboardBatch.Apply();
        }
        IEnumerator Respawner(int worldX, int worldZ, bool insideDungeon, bool insideBuilding, bool forceReposition)
        {
            // Wait for end of frame so existing world data can be removed
            yield return(new WaitForEndOfFrame());

            // Reset dungeon block on new spawn
            lastPlayerDungeonBlockIndex = -1;
            playerDungeonBlockData      = new DFLocation.DungeonBlock();

            // Reset inside state
            isPlayerInside              = false;
            isPlayerInsideDungeon       = false;
            isPlayerInsideDungeonPalace = false;

            // Set player GPS coordinates
            playerGPS.WorldX = worldX;
            playerGPS.WorldZ = worldZ;

            // Set streaming world coordinates
            DFPosition pos = MapsFile.WorldCoordToMapPixel(worldX, worldZ);

            world.MapPixelX = pos.X;
            world.MapPixelY = pos.Y;

            // Get location at this position
            ContentReader.MapSummary summary;
            bool hasLocation = dfUnity.ContentReader.HasLocation(pos.X, pos.Y, out summary);

            if (!insideDungeon && !insideBuilding)
            {
                // Start outside
                EnableExteriorParent();
                if (!forceReposition)
                {
                    // Teleport to explicit world coordinates
                    world.TeleportToWorldCoordinates(worldX, worldZ);
                }
                else
                {
                    // Force reposition to closest start marker if available
                    world.TeleportToCoordinates(pos.X, pos.Y, StreamingWorld.RepositionMethods.RandomStartMarker);
                }

                // Wait until world is ready
                while (world.IsInit)
                {
                    yield return(new WaitForEndOfFrame());
                }
            }
            else if (hasLocation && insideDungeon)
            {
                // Start in dungeon
                DFLocation location;
                world.TeleportToCoordinates(pos.X, pos.Y, StreamingWorld.RepositionMethods.None);
                dfUnity.ContentReader.GetLocation(summary.RegionIndex, summary.MapIndex, out location);
                StartDungeonInterior(location, true);
                world.suppressWorld = true;
            }
            else if (hasLocation && insideBuilding && exteriorDoors != null)
            {
                // Start in building
                DFLocation location;
                world.TeleportToCoordinates(pos.X, pos.Y, StreamingWorld.RepositionMethods.None);
                dfUnity.ContentReader.GetLocation(summary.RegionIndex, summary.MapIndex, out location);
                StartBuildingInterior(location, exteriorDoors[0]);
                world.suppressWorld = true;
            }
            else
            {
                // All else fails teleport to map pixel
                DaggerfallUnity.LogMessage("Something went wrong! Teleporting to origin of nearest map pixel.");
                EnableExteriorParent();
                world.TeleportToCoordinates(pos.X, pos.Y);
            }

            // Lower respawn flag
            isRespawning = false;
        }
Example #12
0
            public override string LocationDirection()
            {
                Vector2 positionPlayer;
                Vector2 positionLocation = Vector2.zero;

                Place questLastPlaceReferenced = parent.LastPlaceReferenced;

                if (questLastPlaceReferenced == null)
                {
                    QuestMachine.Log(parent, "Trying to get direction to quest location when no location has been referenced in the quest.");
                    return(TextManager.Instance.GetText("ConversationText", "resolvingError"));
                }

                DFPosition position  = new DFPosition();
                PlayerGPS  playerGPS = GameManager.Instance.PlayerGPS;

                if (playerGPS)
                {
                    position = playerGPS.CurrentMapPixel;
                }

                positionPlayer = new Vector2(position.X, position.Y);

                int region = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetPoliticIndex(position.X, position.Y) - 128;

                if (region < 0 || region >= DaggerfallUnity.Instance.ContentReader.MapFileReader.RegionCount)
                {
                    region = -1;
                }

                DFRegion.RegionMapTable locationInfo = new DFRegion.RegionMapTable();

                DFRegion currentDFRegion = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetRegion(region);

                string name = questLastPlaceReferenced.SiteDetails.locationName.ToLower();

                string[] locations = currentDFRegion.MapNames;
                for (int i = 0; i < locations.Length; i++)
                {
                    if (locations[i].ToLower() == name) // Valid location found with exact name
                    {
                        if (currentDFRegion.MapNameLookup.ContainsKey(locations[i]))
                        {
                            int index = currentDFRegion.MapNameLookup[locations[i]];
                            locationInfo     = currentDFRegion.MapTable[index];
                            position         = MapsFile.LongitudeLatitudeToMapPixel((int)locationInfo.Longitude, (int)locationInfo.Latitude);
                            positionLocation = new Vector2(position.X, position.Y);
                        }
                    }
                }

                if (positionLocation != Vector2.zero)
                {
                    Vector2 vecDirectionToTarget = positionLocation - positionPlayer;
                    vecDirectionToTarget.y = -vecDirectionToTarget.y; // invert y axis
                    return(GameManager.Instance.TalkManager.DirectionVector2DirectionHintString(vecDirectionToTarget));
                }
                else
                {
                    return("... never mind ...");
                }
            }
Example #13
0
        /// <summary>
        /// Creates a path from player's current location to destination and
        /// returns minutes taken to travel.
        /// </summary>
        /// <param name="endPos">Endpoint in map pixel coordinates.</param>
        public int CalculateTravelTime(DFPosition endPos,
                                       bool speedCautious = false,
                                       bool sleepModeInn  = false,
                                       bool travelShip    = false,
                                       bool hasHorse      = false,
                                       bool hasCart       = false)
        {
            int transportModifier = 0;

            if (hasHorse)
            {
                transportModifier = 128;
            }
            else if (hasCart)
            {
                transportModifier = 192;
            }
            else
            {
                transportModifier = 256;
            }

            int playerXMapPixel         = GameManager.Instance.PlayerGPS.CurrentMapPixel.X;
            int playerYMapPixel         = GameManager.Instance.PlayerGPS.CurrentMapPixel.Y;
            int distanceXMapPixels      = endPos.X - playerXMapPixel;
            int distanceYMapPixels      = endPos.Y - playerYMapPixel;
            int distanceXMapPixelsAbs   = Mathf.Abs(distanceXMapPixels);
            int distanceYMapPixelsAbs   = Mathf.Abs(distanceYMapPixels);
            int furthestOfXandYDistance = 0;

            if (distanceXMapPixelsAbs <= distanceYMapPixelsAbs)
            {
                furthestOfXandYDistance = distanceYMapPixelsAbs;
            }
            else
            {
                furthestOfXandYDistance = distanceXMapPixelsAbs;
            }

            int xPixelMovementDirection;
            int yPixelMovementDirection;

            if (distanceXMapPixels >= 0)
            {
                xPixelMovementDirection = 1;
            }
            else
            {
                xPixelMovementDirection = -1;
            }

            if (distanceYMapPixels >= 0)
            {
                yPixelMovementDirection = 1;
            }
            else
            {
                yPixelMovementDirection = -1;
            }

            int numberOfMovements = 0;
            int shorterOfXandYDistanceIncrementer = 0;

            int minutesTakenThisMove = 0;
            int minutesTakenTotal    = 0;

            MapsFile mapsFile = DaggerfallUnity.Instance.ContentReader.MapFileReader;

            pixelsTraveledOnOcean = 0;

            while (numberOfMovements < furthestOfXandYDistance)
            {
                if (furthestOfXandYDistance == distanceXMapPixelsAbs)
                {
                    playerXMapPixel += xPixelMovementDirection;
                    shorterOfXandYDistanceIncrementer += distanceYMapPixelsAbs;

                    if (shorterOfXandYDistanceIncrementer > distanceXMapPixelsAbs)
                    {
                        shorterOfXandYDistanceIncrementer -= distanceXMapPixelsAbs;
                        playerYMapPixel += yPixelMovementDirection;
                    }
                }
                else
                {
                    playerYMapPixel += yPixelMovementDirection;
                    shorterOfXandYDistanceIncrementer += distanceXMapPixelsAbs;

                    if (shorterOfXandYDistanceIncrementer > distanceYMapPixelsAbs)
                    {
                        shorterOfXandYDistanceIncrementer -= distanceYMapPixelsAbs;
                        playerXMapPixel += xPixelMovementDirection;
                    }
                }

                int terrainMovementIndex = 0;
                int terrain = mapsFile.GetClimateIndex(playerXMapPixel, playerYMapPixel);
                if (terrain == (int)MapsFile.Climates.Ocean)
                {
                    ++pixelsTraveledOnOcean;
                    if (travelShip)
                    {
                        minutesTakenThisMove = 51;
                    }
                    else
                    {
                        minutesTakenThisMove = 255;
                    }
                }
                else
                {
                    terrainMovementIndex = climateIndices[terrain - (int)MapsFile.Climates.Ocean];
                    minutesTakenThisMove = (((102 * transportModifier) >> 8)
                                            * (256 - terrainMovementModifiers[terrainMovementIndex] + 256)) >> 8;
                }

                if (!sleepModeInn)
                {
                    minutesTakenThisMove = (300 * minutesTakenThisMove) >> 8;
                }
                minutesTakenTotal += minutesTakenThisMove;
                ++numberOfMovements;
            }

            if (!speedCautious)
            {
                minutesTakenTotal = minutesTakenTotal >> 1;
            }

            return(minutesTakenTotal);
        }
Example #14
0
        private void UpdateMode(TransportModes transportMode)
        {
            // Update the transport mode and stop any riding sounds playing.
            mode = transportMode;
            if (ridingAudioSource.isPlaying)
            {
                ridingAudioSource.Stop();
            }

            if (mode == TransportModes.Horse || mode == TransportModes.Cart)
            {
                // Tell player motor we're riding. TODO: Change to event system so other classes can listen for transport changes.
                playerMotor.IsRiding = true;

                // Setup appropriate riding sounds.
                SoundClips sound = (mode == TransportModes.Horse) ? horseRidingSound2 : cartRidingSound;
                ridingAudioSource.clip = dfAudioSource.GetAudioClip((int)sound);

                // Setup appropriate riding textures.
                string textureName = (mode == TransportModes.Horse) ? horseTextureName : cartTextureName;
                for (int i = 0; i < 4; i++)
                {
                    ridingTexures[i] = ImageReader.GetImageData(textureName, 0, i, true, true);
                }
                ridingTexure = ridingTexures[0];

                // Initialise neighing timer.
                neighTime = Time.time + Random.Range(1, 5);
            }
            else
            {
                // Tell player motor we're not riding.
                playerMotor.IsRiding = false;
            }

            if (mode == TransportModes.Ship)
            {
                GameManager.Instance.PlayerMotor.CancelMovement = true;
                SerializablePlayer serializablePlayer = GetComponent <SerializablePlayer>();
                DaggerfallUI.Instance.SmashHUDToBlack();

                // Is player on board ship?
                if (boardShipPosition != null)
                {
                    // Check for terrain sampler changes. (so don't fall through floor)
                    StreamingWorld.RepositionMethods reposition = StreamingWorld.RepositionMethods.None;
                    if (boardShipPosition.terrainSamplerName != DaggerfallUnity.Instance.TerrainSampler.ToString() ||
                        boardShipPosition.terrainSamplerVersion != DaggerfallUnity.Instance.TerrainSampler.Version)
                    {
                        reposition = StreamingWorld.RepositionMethods.RandomStartMarker;
                        if (DaggerfallUI.Instance.DaggerfallHUD != null)
                        {
                            DaggerfallUI.Instance.DaggerfallHUD.PopupText.AddText("Terrain sampler changed. Repositioning player.");
                        }
                    }
                    // Restore player position from before boarding ship.
                    DFPosition mapPixel = MapsFile.WorldCoordToMapPixel(boardShipPosition.worldPosX, boardShipPosition.worldPosZ);
                    GameManager.Instance.StreamingWorld.TeleportToCoordinates(mapPixel.X, mapPixel.Y, reposition);
                    serializablePlayer.RestorePosition(boardShipPosition);
                    boardShipPosition = null;
                }
                else
                {
                    // Record current player position before boarding ship.
                    boardShipPosition = serializablePlayer.GetPlayerPositionData();

                    // Teleport to ship
                    GameManager.Instance.StreamingWorld.TeleportToCoordinates(2, 2, StreamingWorld.RepositionMethods.RandomStartMarker);
                }
                DaggerfallUI.Instance.FadeHUDFromBlack();
                mode = TransportModes.Foot;
            }
        }
Example #15
0
        bool parseCommand(string[] args)
        {
            bool   handled = false;
            Logger l       = Logger.GetInstance();

            switch (args[0])
            {
            case (SPAWN_ENEMY_CMD):
                if (args.Length == 2)
                {
                    int mobileType = System.Convert.ToInt32(args[1]);
                    if (mobileType > -1 && mobileType < 147)
                    {
                        l.log("Spawning enemy of type " + (MobileTypes)mobileType + ".\n");
                        GameObject.FindGameObjectWithTag("EnemySpawner").SendMessage("SpawnEnemy", mobileType);
                        handled = true;
                    }
                }
                break;

            case (TRAVEL_CMD):
                if (args.Length >= 2)
                {
                    DFLocation location;
                    string     nameWithPossibleSpaces = string.Join(" ", args);
                    nameWithPossibleSpaces = nameWithPossibleSpaces.Substring(TRAVEL_CMD.Length + 1);
                    if (!GameObjectHelper.FindMultiNameLocation(nameWithPossibleSpaces, out location))
                    {
                        l.log("Unable to find location " + nameWithPossibleSpaces + ".\n");
                    }
                    else
                    {
                        l.log("Found location in " + location.RegionName + "!\n");
                        DFPosition mapPos = MapsFile.LongitudeLatitudeToMapPixel((int)location.MapTableData.Longitude, (int)location.MapTableData.Latitude);
                        if (mapPos.X >= TerrainHelper.minMapPixelX || mapPos.X < TerrainHelper.maxMapPixelX ||
                            mapPos.Y >= TerrainHelper.minMapPixelY || mapPos.Y < TerrainHelper.maxMapPixelY)
                        {
                            streamingWorldOwner.TeleportToCoordinates(mapPos.X, mapPos.Y);
                        }
                        else
                        {
                            l.log("Requested location is out of bounds!\n");
                        }
                    }
                }
                break;

            case (SNOW_COMMAND):
                if (weatherManager.IsSnowing)
                {
                    weatherManager.StopSnowing();
                }
                else
                {
                    weatherManager.StartSnowing();
                }
                break;

            case (XML_DEBUG):
                QuestManager.Instance.doDebugQuest();
                break;

            case (QUEST_DEBUG):
                l.log("Dumping all quests:");
                QuestManager.Instance.dumpAllQuests();
                break;

            case (TIME_DEBUG):
                l.log("The time is: " + dfUnity.WorldTime.Now.LongDateTimeString());
                ulong timeInSeconds = dfUnity.WorldTime.Now.ToSeconds();
                l.log("The time in seconds is: " + timeInSeconds.ToString());
                timeInSeconds -= 60 * 60 * 24;
                l.log("Setting the time 1 day in the past: " + timeInSeconds.ToString());
                dfUnity.WorldTime.Now.FromSeconds(timeInSeconds);
                l.log("The time is now: " + dfUnity.WorldTime.Now.LongDateTimeString());
                l.log("The time in seconds is now: " + dfUnity.WorldTime.Now.ToSeconds().ToString());

                break;

            case (DISPLAY_SCROLL):
                if (args.Length == 2)
                {
                    scrollManager.displayScroll(args[1]);
                }
                break;

            default:
                break;
            }


            return(handled);
        }
Example #16
0
        private void UpdateWorldInfo(int x, int y)
        {
            // Requires DaggerfallUnity to be ready
            if (!ReadyCheck())
            {
                return;
            }

            // Requires MAPS.BSA connection
            if (dfUnity.ContentReader.MapFileReader == null)
            {
                return;
            }

            // Get climate and politic data
            currentClimateIndex = dfUnity.ContentReader.MapFileReader.GetClimateIndex(x, y);
            currentPoliticIndex = dfUnity.ContentReader.MapFileReader.GetPoliticIndex(x, y);
            climateSettings     = MapsFile.GetWorldClimateSettings(currentClimateIndex);
            if (currentPoliticIndex >= 128)
            {
                regionName = dfUnity.ContentReader.MapFileReader.GetRegionName(currentPoliticIndex - 128);
            }
            else if (currentPoliticIndex == 64)
            {
                regionName = "Ocean";
            }
            else
            {
                regionName = "Unknown";
            }

            // Get region data
            currentRegion = dfUnity.ContentReader.MapFileReader.GetRegion(CurrentRegionIndex);

            // Get location data
            ContentReader.MapSummary mapSummary;
            if (dfUnity.ContentReader.HasLocation(x, y, out mapSummary))
            {
                currentLocation    = dfUnity.ContentReader.MapFileReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex);
                hasCurrentLocation = true;
                CalculateWorldLocationRect();
            }
            else
            {
                currentLocation    = new DFLocation();
                hasCurrentLocation = false;
                ClearWorldLocationRect();
            }

            // Get location type
            if (hasCurrentLocation)
            {
                if (currentRegion.MapTable == null)
                {
                    DaggerfallUnity.LogMessage(string.Format("PlayerGPS: Location {0} in region{1} has a null MapTable.", currentLocation.Name, currentLocation.RegionName));
                }
                else
                {
                    currentLocationType = currentRegion.MapTable[mapSummary.MapIndex].LocationType;
                }
            }
        }
        void DisplaySaveStatsGUI()
        {
            if (currentSaveTree == null)
            {
                return;
            }

            SaveTreeBaseRecord positionRecord = currentSaveTree.FindRecord(RecordTypes.CharacterPositionRecord);

            EditorGUILayout.Space();
            GUILayoutHelper.Horizontal(() =>
            {
                EditorGUILayout.LabelField(new GUIContent("Version"), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(currentSaveTree.Header.Version.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            GUILayoutHelper.Horizontal(() =>
            {
                string positionText = string.Format("X={0}, Y={1}, Z={2}",
                                                    positionRecord.RecordRoot.Position.WorldX,
                                                    positionRecord.RecordRoot.Position.WorldY,
                                                    positionRecord.RecordRoot.Position.WorldZ);

                EditorGUILayout.LabelField(new GUIContent("Player Position", "Position of player in the world."), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(positionText, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            GUILayoutHelper.Horizontal(() =>
            {
                DFPosition mapPixel = MapsFile.WorldCoordToMapPixel(positionRecord.RecordRoot.Position.WorldX, positionRecord.RecordRoot.Position.WorldZ);
                string mapPixelText = string.Format("X={0}, Y={1}", mapPixel.X, mapPixel.Y);

                EditorGUILayout.LabelField(new GUIContent("Player Map Pixel", "Position of player on small map."), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(mapPixelText, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            GUILayoutHelper.Horizontal(() =>
            {
                DaggerfallDateTime time = new DaggerfallDateTime();
                time.FromClassicDaggerfallTime(currentSaveVars.GameTime);

                EditorGUILayout.LabelField(new GUIContent("Player Time", "World time of this save."), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(time.LongDateTimeString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            GUILayoutHelper.Horizontal(() =>
            {
                EditorGUILayout.LabelField(new GUIContent("Player Environment"), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(((Environments)currentSaveTree.Header.Environment).ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            GUILayoutHelper.Horizontal(() =>
            {
                EditorGUILayout.LabelField(new GUIContent("RecordElement records"), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                EditorGUILayout.SelectableLabel(currentSaveTree.RecordDictionary.Count.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            });
            //GUILayoutHelper.Horizontal(() =>
            //{
            //    EditorGUILayout.LabelField(new GUIContent("Header.Unknown"), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
            //    EditorGUILayout.SelectableLabel(currentSaveTree.Header.Unknown.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            //});
            //GUILayoutHelper.Horizontal(() =>
            //{
            //    EditorGUILayout.LabelField(new GUIContent("CharacterPosition.Unknown"), GUILayout.Width(EditorGUIUtility.labelWidth - 4));
            //    EditorGUILayout.SelectableLabel(currentSaveTree.Header.CharacterPosition.Unknown.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            //});
        }
Example #18
0
        // Drops nature flats based on random chance scaled by simple rules
        public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale)
        {
            const float maxSteepness  = 50f;        // 50
            const float chanceOnDirt  = 0.2f;       // 0.2
            const float chanceOnGrass = 0.9f;       // 0.4
            const float chanceOnStone = 0.05f;      // 0.05

            // Get terrain
            Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>();

            if (!terrain)
            {
                return;
            }

            // Get terrain data
            TerrainData terrainData = terrain.terrainData;

            if (!terrainData)
            {
                return;
            }

            // Remove exiting billboards
            dfBillboardBatch.Clear();

            // Seed random with terrain key
            UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY);

            // Just layout some random flats spread evenly across entire map pixel area
            // Flats are aligned with tiles, max 127x127 in billboard batch
            Vector2 tilePos = Vector2.zero;
            float   scale   = terrainData.heightmapScale.x;
            int     dim     = TerrainHelper.terrainTileDim - 1;

            for (int y = 0; y < dim; y++)
            {
                for (int x = 0; x < dim; x++)
                {
                    // Reject based on steepness
                    float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim);
                    if (steepness > maxSteepness)
                    {
                        continue;
                    }

                    // Reject if inside location rect
                    // Rect is expanded slightly to give extra clearance around locations
                    tilePos.x = x;
                    tilePos.y = y;
                    const int natureClearance = 4;
                    Rect      rect            = dfTerrain.MapData.locationRect;
                    if (rect.x > 0 && rect.y > 0)
                    {
                        rect.xMin -= natureClearance;
                        rect.xMin += natureClearance;
                        rect.yMin -= natureClearance;
                        rect.yMax += natureClearance;
                        if (rect.Contains(tilePos))
                        {
                            continue;
                        }
                    }

                    // Chance scaled based on map pixel height
                    // This tends to produce sparser lowlands and denser highlands
                    // Adjust or remove clamp range to influence nature generation
                    float elevationScale = (dfTerrain.MapData.worldHeight / 128f);
                    elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f);

                    // Chance scaled by base climate type
                    float climateScale = 1.0f;
                    DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate);
                    switch (climate.ClimateType)
                    {
                    case DFLocation.ClimateBaseType.Desert:             // Just lower desert for now
                        climateScale = 0.25f;
                        break;
                    }

                    // Chance also determined by tile type
                    WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y);
                    if (sample.record == 1)
                    {
                        // Dirt
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else if (sample.record == 2)
                    {
                        // Grass
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else if (sample.record == 3)
                    {
                        // Stone
                        if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // Anything else
                        continue;
                    }

                    // Sample height and position billboard
                    Vector3 pos    = new Vector3(x * scale, 0, y * scale);
                    float   height = terrain.SampleHeight(pos + terrain.transform.position);
                    pos.y = height;

                    // Reject if too close to water
                    float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation * terrainScale;
                    if (height < beachLine)
                    {
                        continue;
                    }

                    // Add to batch
                    int record = UnityEngine.Random.Range(1, 32);
                    dfBillboardBatch.AddItem(record, pos);
                }
            }

            // Apply new batch
            dfBillboardBatch.Apply();
        }