public EditorToolTerrainItem(IProtoTile protoTile) : base(protoTile?.Name ?? "(don't change the terrain sprite)", id: string.Empty, displayShortName: protoTile is not null) { this.ProtoTile = protoTile; }
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; }
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(); }
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)))); }
private TaskVisitTile(IProtoTile protoTile, string description) : base(description) { this.ProtoTile = protoTile; }
public ProtoTileForHeight(double limit, IProtoTile protoTile) { this.ProtoTile = protoTile; this.Limit = limit; }
/// <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); } }
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); }
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); }
public EditorToolTerrainItem(IProtoTile protoTile) : base(protoTile?.Name ?? "(don't change the terrain sprite)", id: string.Empty) { this.ProtoTile = protoTile; }
private RequirementVisitTile(IProtoTile protoTile, string description) : base(description) { this.ProtoTile = protoTile; }