Exemple #1
0
 public EditorToolTerrainItem(IProtoTile protoTile)
     : base(protoTile?.Name ?? "(don't change the terrain sprite)",
            id: string.Empty,
            displayShortName: protoTile is not null)
 {
     this.ProtoTile = protoTile;
 }
Exemple #2
0
        public void Apply(
            long seed,
            double noiseProbability,
            BoundsUshort selectionBounds,
            IProtoTile protoTileTarget,
            IProtoTile protoTileNoise)
        {
            Api.Assert(protoTileTarget is not null, "Please select target tile proto");
            Api.Assert(protoTileNoise is not null, "Please select noise tile proto");
            Api.Assert(selectionBounds.Size.LengthSquared > 0, "Please select world area");
            Api.Assert(noiseProbability >= 0 || noiseProbability <= 1,
                       "Noise probability must be in range from 0 to 1 inclusive.");

            var random = new Random((int)seed);

            var world = Client.World;

            var tilesToModify = new List <Vector2Ushort>();

            for (var x = selectionBounds.MinX; x < selectionBounds.MaxX; x++)
            {
                for (var y = selectionBounds.MinY; y < selectionBounds.MaxY; y++)
                {
                    if (random.NextDouble() > noiseProbability)
                    {
                        // do not process this tile
                        continue;
                    }

                    // check tile type
                    var tilePosition = new Vector2Ushort(x, y);
                    var tile         = world.GetTile(tilePosition);
                    if (tile.ProtoTile == protoTileTarget)
                    {
                        tilesToModify.Add(tilePosition);
                    }
                }
            }

            if (tilesToModify.Count == 0)
            {
                return;
            }

            EditorClientActionsHistorySystem.DoAction(
                "Modify terrain tiles (noise)",
                onDo: () => tilesToModify.ChunkedInvoke(
                    5000,
                    chunk => this.CallServer(_ => _.ServerRemote_PlaceAt(chunk, protoTileNoise))),
                onUndo: () => tilesToModify.ChunkedInvoke(
                    5000,
                    chunk => this.CallServer(_ => _.ServerRemote_PlaceAt(chunk, protoTileTarget))),
                canGroupWithPreviousAction: false);
        }
 public WorldMapResourceMark(
     uint id,
     Vector2Ushort position,
     IProtoStaticWorldObject protoWorldObject,
     double serverSpawnTime,
     IProtoTile biome,
     Vector2Ushort searchAreaCirclePosition,
     ushort searchAreaCircleRadius)
 {
     this.Id                       = id;
     this.Position                 = position;
     this.ProtoWorldObject         = protoWorldObject;
     this.ServerSpawnTime          = serverSpawnTime;
     this.Biome                    = biome;
     this.SearchAreaCirclePosition = searchAreaCirclePosition;
     this.SearchAreaCircleRadius   = searchAreaCircleRadius;
 }
Exemple #4
0
        private void ServerRemote_PlaceAt(IList <Vector2Ushort> modifyRequests, IProtoTile protoTile)
        {
            if (modifyRequests.Count == 0)
            {
                throw new Exception("Incorrect modify request - at least one tile is required");
            }

            Logger.Important("Modify terrain at " + modifyRequests[0]);

            var worldService = Server.World;

            foreach (var tilePosition in modifyRequests)
            {
                var sourceTile = worldService.GetTile(tilePosition);
                worldService.SetTileData(
                    tilePosition,
                    protoTile,
                    tileHeight: sourceTile.Height,
                    isSlope: sourceTile.IsSlope,
                    isCliff: sourceTile.IsCliff);
            }

            worldService.FixMapTilesRecentlyModified();
        }
Exemple #5
0
        private void ClientPlaceAt(List <Vector2Ushort> tilePositions, IProtoTile selectedProtoTile, bool isRepeat)
        {
            var terrainHeightMode     = this.settings.SelectedHeightMode.Value;
            var isAllowTileKindChange = this.settings.IsAllowTileKindChange;
            var isAllowTileProtoChangeOnlyOnTheSameHeight = this.settings.IsAllowTileProtoChangeOnlyOnTheSameHeight;
            var isApplyOnlyOnTheSameTileProto             = this.settings.IsApplyOnlyOnTheSameTileProto;

            if (this.settings.IsFillMode)
            {
                if (isRepeat)
                {
                    // fill doesn't support repeat
                    return;
                }

                tilePositions = EditorTileHelper.GatherAllTilePositionsOfTheSameProtoTile(
                    tilePositions[0],
                    onlyOnTheSameHeight: isAllowTileProtoChangeOnlyOnTheSameHeight,
                    ignoreCliffsAndSlopes: false);

                // don't change the tile heights in the fill mode
                terrainHeightMode = TerrainHeightMode.Keep;
            }

            var worldService = Client.World;

            byte       targetHeight = 0;
            IProtoTile targetProto  = null;

            if (isRepeat)
            {
                // use target height from previous iteration
                targetHeight = this.lastTargetHeight;
                targetProto  = this.lastTargetProto;
            }
            else
            {
                if (isApplyOnlyOnTheSameTileProto)
                {
                    targetProto = EditorTileHelper.CalculateMostFrequentTileProto(tilePositions);
                }

                switch (terrainHeightMode)
                {
                case TerrainHeightMode.Keep:
                case TerrainHeightMode.Flatten:
                    // calculate average height for all the tiles
                    targetHeight = EditorTileHelper.CalculateAverageHeight(tilePositions);
                    break;

                case TerrainHeightMode.Increase:
                    targetHeight = byte.MaxValue;
                    goto case TerrainHeightMode.Decrease;

                case TerrainHeightMode.Decrease:
                    // calculate target height
                    foreach (var tilePosition in tilePositions)
                    {
                        var tile = worldService.GetTile(tilePosition);
                        var calculatedNewTileHeight = this.CalculateNewTileHeight(tile, terrainHeightMode);

                        if (terrainHeightMode == TerrainHeightMode.Increase &&
                            calculatedNewTileHeight < targetHeight ||
                            terrainHeightMode == TerrainHeightMode.Decrease &&
                            calculatedNewTileHeight > targetHeight)
                        {
                            targetHeight = calculatedNewTileHeight;
                        }
                    }

                    break;
                }
            }

            this.lastTargetHeight = targetHeight;
            this.lastTargetProto  = targetProto;

            var tilesToModify = new List <List <TileModifyRequest> >();
            var tilesToRevert = new List <List <TileModifyRequest> >();

            // separate on groups of world chunk size
            var worldChunkSize = 10 * ScriptingConstants.WorldChunkSize;
            var groups         = tilePositions
                                 .GroupBy(g => new Vector2Ushort((ushort)(g.X / worldChunkSize),
                                                                 (ushort)(g.Y / worldChunkSize)))
                                 .ToList();

            // gather tile modifications for each group
            foreach (var group in groups)
            {
                var groupTilesToModify = new List <TileModifyRequest>();
                var groupTilesToRevert = new List <TileModifyRequest>();

                foreach (var tilePosition in group)
                {
                    var tile = worldService.GetTile(tilePosition);
                    var previousProtoTile  = tile.ProtoTile;
                    var previousTileHeight = tile.Height;
                    var previousIsSlope    = tile.IsSlope;

                    var newProtoTile = selectedProtoTile ?? previousProtoTile;
                    var newIsSlope   = tile.IsSlope;

                    if (isApplyOnlyOnTheSameTileProto &&
                        previousProtoTile != targetProto)
                    {
                        continue;
                    }

                    if (isAllowTileProtoChangeOnlyOnTheSameHeight &&
                        this.lastTargetHeight != previousTileHeight)
                    {
                        continue;
                    }

                    if (!isAllowTileKindChange &&
                        previousProtoTile.Kind != newProtoTile.Kind &&
                        previousProtoTile.Kind != TileKind.Placeholder)
                    {
                        continue;
                    }

                    var newTileHeight = previousTileHeight;
                    if (terrainHeightMode == TerrainHeightMode.Flatten)
                    {
                        if (IsValidHeightFotTile(tile, targetHeight))
                        {
                            // can set tile height to target height
                            newTileHeight = targetHeight;
                        }
                    }
                    else if (terrainHeightMode == TerrainHeightMode.Increase ||
                             terrainHeightMode == TerrainHeightMode.Decrease)
                    {
                        newTileHeight = this.CalculateNewTileHeight(tile, terrainHeightMode);
                        if (newTileHeight != targetHeight)
                        {
                            // cannot change tile height
                            newTileHeight = previousTileHeight;
                        }
                    }

                    if (previousProtoTile == newProtoTile &&
                        newTileHeight == previousTileHeight &&
                        newIsSlope == previousIsSlope)
                    {
                        // nothing to change - the tile is already as desired
                        continue;
                    }

                    groupTilesToModify.Add(new TileModifyRequest(tilePosition,
                                                                 newProtoTile.SessionIndex,
                                                                 newTileHeight,
                                                                 newIsSlope));
                    groupTilesToRevert.Add(new TileModifyRequest(tilePosition,
                                                                 previousProtoTile.SessionIndex,
                                                                 previousTileHeight,
                                                                 previousIsSlope));
                }

                if (groupTilesToModify.Count == 0)
                {
                    // nothing to modify in this group
                    continue;
                }

                tilesToModify.Add(groupTilesToModify);
                tilesToRevert.Add(groupTilesToRevert);
            }

            if (tilesToModify.Count == 0)
            {
                // nothing to modify
                return;
            }

            EditorClientSystem.DoAction(
                "Modify terrain tiles",
                onDo: () => tilesToModify.ForEach(
                    chunk => this.CallServer(_ => _.ServerRemote_PlaceAt(chunk))),
                onUndo: () => tilesToRevert.ForEach(
                    chunk => this.CallServer(_ => _.ServerRemote_PlaceAt(chunk))));
        }
Exemple #6
0
 private TaskVisitTile(IProtoTile protoTile, string description)
     : base(description)
 {
     this.ProtoTile = protoTile;
 }
Exemple #7
0
 public ProtoTileForHeight(double limit, IProtoTile protoTile)
 {
     this.ProtoTile = protoTile;
     this.Limit     = limit;
 }
Exemple #8
0
        /// <summary>
        /// Generates the search circle area position and radius for the provided position.
        /// The search area will contain at least about 33% of the provided biome tiles
        /// and will include the provided world position.
        /// </summary>
        /// <returns>False if cannot generate a search area.</returns>
        public static bool GenerateSearchArea(
            Vector2Ushort worldPosition,
            IProtoTile biome,
            ushort circleRadius,
            out Vector2Ushort circleCenter,
            int maxAttempts,
            double waterMaxRatio = 0.3)
        {
            var biomeSessionIndex = biome.SessionIndex;

            if (TryToCreateSearchArea(desiredBiomeMatchRatio: 0.75, out circleCenter) ||
                TryToCreateSearchArea(desiredBiomeMatchRatio: 0.5, out circleCenter) ||
                TryToCreateSearchArea(desiredBiomeMatchRatio: 0.33, out circleCenter))
            {
                return(true);
            }

            return(false);

            bool TryToCreateSearchArea(
                double desiredBiomeMatchRatio,
                out Vector2Ushort circleCenter)
            {
                for (var attempt = 0; attempt < maxAttempts; attempt++)
                {
                    var offset  = circleRadius * RandomHelper.NextDouble();
                    var angle   = RandomHelper.NextDouble() * MathConstants.DoublePI;
                    var resultD = new Vector2D(worldPosition.X + offset * Math.Cos(angle),
                                               worldPosition.Y + offset * Math.Sin(angle));

                    circleCenter = new Vector2Ushort((ushort)MathHelper.Clamp(resultD.X, 0, ushort.MaxValue),
                                                     (ushort)MathHelper.Clamp(resultD.Y, 0, ushort.MaxValue));

                    if (IsValidCircle(circleCenter))
                    {
                        return(true);
                    }

                    bool IsValidCircle(Vector2Ushort circleCenter)
                    {
                        uint totalChecks        = 0,
                             biomeMathes        = 0,
                             waterOrOutOfBounds = 0;

                        for (var x = -circleRadius; x < circleRadius; x += 10)
                        {
                            for (var y = -circleRadius; y < circleRadius; y += 10)
                            {
                                totalChecks++;
                                var tile = World.GetTile(circleCenter.X + x,
                                                         circleCenter.Y + y,
                                                         logOutOfBounds: false);
                                if (tile.IsOutOfBounds)
                                {
                                    waterOrOutOfBounds++;
                                    biomeMathes++; // yes, consider it a biome match
                                    continue;
                                }

                                if (tile.ProtoTileSessionIndex == biomeSessionIndex)
                                {
                                    biomeMathes++;
                                }
                                else if (tile.ProtoTile.Kind == TileKind.Water)
                                {
                                    waterOrOutOfBounds++;
                                    biomeMathes++; // yes, consider it a biome match
                                }
                            }
                        }

                        var biomeMatchRatio         = biomeMathes / (double)totalChecks;
                        var waterOrOutOfBoundsRatio = waterOrOutOfBounds / (double)totalChecks;

                        return(biomeMatchRatio >= desiredBiomeMatchRatio &&
                               waterOrOutOfBoundsRatio <= waterMaxRatio);
                    }
                }

                circleCenter = default;
                return(false);
            }
        }
Exemple #9
0
        private void ClientPlaceAt(List <Vector2Ushort> tilePositions, IProtoTile selectedProtoTile, bool isRepeat)
        {
            var terrainHeightMode     = this.settings.SelectedHeightMode.Value;
            var isAllowTileKindChange = this.settings.IsAllowTileKindChange;
            var isAllowTileProtoChangeOnlyOnTheSameHeight = this.settings.IsAllowTileProtoChangeOnlyOnTheSameHeight;
            var isApplyOnlyOnTheSameTileProto             = this.settings.IsApplyOnlyOnTheSameTileProto;

            if (this.settings.IsFillMode)
            {
                if (isRepeat)
                {
                    // fill doesn't support repeat
                    return;
                }

                tilePositions = EditorTileHelper.GatherAllTilePositionsOfTheSameProtoTile(
                    tilePositions[0],
                    onlyOnTheSameHeight: isAllowTileProtoChangeOnlyOnTheSameHeight,
                    ignoreCliffsAndSlopes: false);

                // don't change the tile heights in the fill mode
                terrainHeightMode = TerrainHeightMode.Keep;
            }

            var worldService = Client.World;

            byte       targetHeight = 0;
            IProtoTile targetProto  = null;

            if (isRepeat)
            {
                // use target height from previous iteration
                targetHeight = this.lastTargetHeight;
                targetProto  = this.lastTargetProto;
            }
            else
            {
                if (isApplyOnlyOnTheSameTileProto)
                {
                    targetProto = EditorTileHelper.CalculateMostFrequentTileProto(tilePositions);
                }

                switch (terrainHeightMode)
                {
                case TerrainHeightMode.Keep:
                case TerrainHeightMode.Flatten:
                    // calculate average height for all the tiles
                    targetHeight = EditorTileHelper.CalculateAverageHeight(tilePositions);
                    break;

                case TerrainHeightMode.Increase:
                    targetHeight = byte.MaxValue;
                    goto case TerrainHeightMode.Decrease;

                case TerrainHeightMode.Decrease:
                    // calculate target height
                    foreach (var tilePosition in tilePositions)
                    {
                        var tile = worldService.GetTile(tilePosition);
                        var calculatedNewTileHeight = this.CalculateNewTileHeight(tile, terrainHeightMode);

                        if (terrainHeightMode == TerrainHeightMode.Increase &&
                            calculatedNewTileHeight < targetHeight ||
                            terrainHeightMode == TerrainHeightMode.Decrease &&
                            calculatedNewTileHeight > targetHeight)
                        {
                            targetHeight = calculatedNewTileHeight;
                        }
                    }

                    break;
                }
            }

            this.lastTargetHeight = targetHeight;
            this.lastTargetProto  = targetProto;

            var tilesToModify = new List <TerrainEditingSystem.TileModifyRequest>();

            foreach (var tilePosition in tilePositions)
            {
                var tile = worldService.GetTile(tilePosition);
                var previousProtoTile  = tile.ProtoTile;
                var previousTileHeight = tile.Height;
                var previousIsSlope    = tile.IsSlope;

                var newProtoTile = selectedProtoTile ?? previousProtoTile;
                var newIsSlope   = tile.IsSlope;

                if (isApplyOnlyOnTheSameTileProto &&
                    previousProtoTile != targetProto)
                {
                    continue;
                }

                if (isAllowTileProtoChangeOnlyOnTheSameHeight &&
                    this.lastTargetHeight != previousTileHeight)
                {
                    continue;
                }

                if (!isAllowTileKindChange &&
                    previousProtoTile.Kind != newProtoTile.Kind &&
                    previousProtoTile.Kind != TileKind.Placeholder)
                {
                    continue;
                }

                var newTileHeight = previousTileHeight;
                if (terrainHeightMode == TerrainHeightMode.Flatten)
                {
                    if (IsValidHeightFotTile(tile, targetHeight))
                    {
                        // can set tile height to target height
                        newTileHeight = targetHeight;
                    }
                }
                else if (terrainHeightMode == TerrainHeightMode.Increase ||
                         terrainHeightMode == TerrainHeightMode.Decrease)
                {
                    newTileHeight = this.CalculateNewTileHeight(tile, terrainHeightMode);
                    if (newTileHeight != targetHeight)
                    {
                        // cannot change tile height
                        newTileHeight = previousTileHeight;
                    }
                }

                if (previousProtoTile == newProtoTile &&
                    newTileHeight == previousTileHeight &&
                    newIsSlope == previousIsSlope)
                {
                    // nothing to change - the tile is already as desired
                    continue;
                }

                tilesToModify.Add(new TerrainEditingSystem.TileModifyRequest(
                                      tilePosition,
                                      newProtoTile.SessionIndex,
                                      newTileHeight,
                                      newIsSlope));
            }

            TerrainEditingSystem.ClientModifyTerrain(tilesToModify);
        }
Exemple #10
0
        private void ClientOnPaintZone(List <Vector2Ushort> tilePositions, bool isRepeat)
        {
            var selectedZoneForBrush = this.settings.SelectedZoneForBrush;

            if (selectedZoneForBrush is null ||
                !selectedZoneForBrush.IsRendered)
            {
                // no zone selected for brush or zone is not visible
                return;
            }

            var protoZone    = selectedZoneForBrush.Zone;
            var zoneProvider = ClientZoneProvider.Get(protoZone);

            if (!zoneProvider.IsDataReceived)
            {
                return;
            }

            var world = Client.World;
            var onlyOnTheSameHeight = this.settings.IsAllowZoneChangeOnlyOnTheSameHeight;

            if (this.settings.IsFillZoneMode)
            {
                if (isRepeat)
                {
                    // fill doesn't support repeat
                    return;
                }

                tilePositions = EditorTileHelper.GatherAllTilePositionsOfTheSameProtoTile(
                    tilePositions[0],
                    onlyOnTheSameHeight,
                    ignoreCliffsAndSlopes: true);
            }

            this.lastUsedZoneProvider = zoneProvider;

            if (onlyOnTheSameHeight &&
                !isRepeat)
            {
                // capture height when starting painting the zone
                this.capturedHeight = this.settings.IsFillZoneMode
                                          ? world.GetTile(tilePositions[0]).Height
                                          : EditorTileHelper.CalculateAverageHeight(tilePositions);
            }

            var onlyOnTheSameTileProto = this.settings.IsAllowZoneChangeOnlyOnTheSameTileProto;

            if (onlyOnTheSameTileProto &&
                !isRepeat)
            {
                // capture tile proto when starting painting the zone
                this.capturedTileProto = this.settings.IsFillZoneMode
                                             ? world.GetTile(tilePositions[0]).ProtoTile
                                             : EditorTileHelper.CalculateMostFrequentTileProto(tilePositions);
            }

            // determine the mode - adding points to zone or removing them
            var isAddMode = ClientInputManager.IsButtonHeld(GameButton.ActionUseCurrentItem, evenIfHandled: true) ||
                            ClientInputManager.IsButtonUp(GameButton.ActionUseCurrentItem, evenIfHandled: true);

            if (isAddMode)
            {
                // remove points which are already added to the zone
                tilePositions.RemoveAll(tilePosition => zoneProvider.IsFilledPosition(tilePosition));
            }
            else
            {
                // remove points which are not presented in the zone
                tilePositions.RemoveAll(tilePosition => !zoneProvider.IsFilledPosition(tilePosition));
            }

            if (tilePositions.Count == 0)
            {
                // nothing to add/remove
                return;
            }

            if (onlyOnTheSameHeight)
            {
                // remove tiles with different height
                tilePositions.RemoveAll(
                    tilePosition =>
                {
                    var tile = world.GetTile(tilePosition);
                    return(tile.Height != this.capturedHeight
                           // also do not allow painting on cliffs and slopes
                           || tile.IsCliff ||
                           tile.IsSlope);
                });
                if (tilePositions.Count == 0)
                {
                    // nothing to add/remove
                    return;
                }
            }

            if (onlyOnTheSameTileProto)
            {
                // remove tiles with different proto
                var worldService = Client.World;
                tilePositions.RemoveAll(
                    tilePosition => worldService.GetTile(tilePosition).ProtoTile
                    != this.capturedTileProto);

                if (tilePositions.Count == 0)
                {
                    // nothing to add/remove
                    return;
                }
            }

            foreach (var tilePosition in tilePositions)
            {
                if (isAddMode)
                {
                    // add point to the zone
                    zoneProvider.SetFilledPosition(tilePosition);
                }
                else
                {
                    // remove point from the zone
                    zoneProvider.ResetFilledPosition(tilePosition);
                }
            }

            this.lastUsedZoneProvider.ApplyClientChanges(forcePushChangesImmediately: tilePositions.Count > 10000);
        }
Exemple #11
0
 public EditorToolTerrainItem(IProtoTile protoTile)
     : base(protoTile?.Name ?? "(don't change the terrain sprite)", id: string.Empty)
 {
     this.ProtoTile = protoTile;
 }
Exemple #12
0
 private RequirementVisitTile(IProtoTile protoTile, string description)
     : base(description)
 {
     this.ProtoTile = protoTile;
 }