Пример #1
0
        public static void DestroyAllDecals(
            Vector2Ushort tilePosition,
            StaticObjectLayoutReadOnly layout)
        {
            Api.ValidateIsServer();

            foreach (var tileOffset in layout.TileOffsets)
            {
                var tile = World.GetTile(tilePosition.X + tileOffset.X,
                                         tilePosition.Y + tileOffset.Y);
                if (!tile.IsValidTile)
                {
                    continue;
                }

                var staticObjects = tile.StaticObjects;
                if (!HasFloorDecals(staticObjects))
                {
                    continue;
                }

                // remove floor decals
                using var tempList = Api.Shared.WrapInTempList(staticObjects);
                foreach (var staticWorldObject in tempList.AsList())
                {
                    if (staticWorldObject.ProtoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                    {
                        World.DestroyObject(staticWorldObject);
                    }
                }
            }

            bool HasFloorDecals(ReadOnlyListWrapper <IStaticWorldObject> staticObjects)
            {
                foreach (var staticWorldObject in staticObjects)
                {
                    if (staticWorldObject.ProtoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                    {
                        return(true);
                    }
                }

                return(false);
            }
        }
Пример #2
0
        private static bool SharedIsWithinInteractionDistance(
            ICharacter character,
            Vector2Ushort tilePosition,
            out bool obstaclesOnTheWay)
        {
            var interactionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                s => s.CollisionGroup == CollisionGroups.CharacterInteractionArea);

            if (interactionAreaShape is null)
            {
                // no interaction area shape (probably a spectator character)
                obstaclesOnTheWay = false;
                return(false);
            }

            var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                sourceShape: interactionAreaShape,
                targetShape: new CircleShape(
                    center: tilePosition.ToVector2D() + (0.5, 0.5),
                    radius: ClickAreaRadius,
                    collisionGroup: CollisionGroups.ClickArea),
                sourceShapeOffset: character.PhysicsBody.Position);

            if (!penetration.HasValue)
            {
                // outside of interaction area
                obstaclesOnTheWay = false;
                return(false);
            }

            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = (Vector2D)tilePosition + new Vector2D(0.5, 0.5);
            var worldObjectPointClosestToCharacter = new BoundsInt(tilePosition, Vector2Int.One)
                                                     .ClampInside(characterCenter);

            obstaclesOnTheWay = ObstacleTestHelper.SharedHasObstaclesOnTheWay(characterCenter,
                                                                              physicsSpace,
                                                                              worldObjectCenter,
                                                                              worldObjectPointClosestToCharacter,
                                                                              sendDebugEvents: false);

            return(!obstaclesOnTheWay);
        }
Пример #3
0
        public static CreateItemResult TryDropToCharacterOrGround(
            IReadOnlyDropItemsList dropItemsList,
            ICharacter toCharacter,
            Vector2Ushort tilePosition,
            bool sendNotificationWhenDropToGround,
            double probabilityMultiplier,
            DropItemContext context,
            out IItemsContainer groundContainer)
        {
            CreateItemResult result;

            if (toCharacter != null)
            {
                result = TryDropToCharacter(
                    dropItemsList,
                    toCharacter,
                    sendNoFreeSpaceNotification: false,
                    probabilityMultiplier,
                    context);
                if (result.IsEverythingCreated)
                {
                    groundContainer = null;
                    return(result);
                }

                // cannot add to character - rollback and drop on ground instead
                result.Rollback();
            }

            result = TryDropToGround(dropItemsList,
                                     tilePosition,
                                     probabilityMultiplier,
                                     context,
                                     out groundContainer);
            if (sendNotificationWhenDropToGround &&
                result.TotalCreatedCount > 0)
            {
                // notify player that there were not enough space in inventory so the items were dropped to the ground
                NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(
                    toCharacter,
                    result.ItemAmounts.FirstOrDefault().Key?.ProtoItem);
            }

            return(result);
        }
Пример #4
0
        protected override void PrepareProtoTile(Settings settings)
        {
            settings.AmbientSoundProvider = new TileAmbientSoundProvider(
                new AmbientSoundPreset(new SoundResource("Ambient/Pit")));

            settings.AddGroundTexture(
                new ProtoTileGroundTexture(
                    texture: GroundTexture1,
                    blendMaskTexture: BlendMaskTextureGeneric2Smooth,
                    noiseSelector: null));

            // add clay stones decals
            var clayStonesTextures      = ProtoTileDecal.CollectTextures("Terrain/Clay/ClayStones*");
            var clayStonesSize          = new Vector2Ushort(2, 2);
            var clayStonesNoiseSelector = new NoiseSelector(
                from: 0.5,
                to: 0.55,
                noise: new PerlinNoise(seed: 642571234,
                                       scale: 5,
                                       octaves: 4,
                                       persistance: 0.9,
                                       lacunarity: 3.9));

            for (ushort x = 0; x <= 1; x++)
            {
                for (ushort y = 0; y <= 1; y++)
                {
                    settings.AddDecal(
                        new ProtoTileDecal(clayStonesTextures,
                                           size: clayStonesSize,
                                           offset: (x, y),
                                           noiseSelector: clayStonesNoiseSelector));
                }
            }

            // add clay pit decals
            settings.AddDecal(
                new ProtoTileDecal("Terrain/Clay/ClayPit*",
                                   size: (2, 2),
                                   drawOrder: DrawOrder.GroundDecalsUnder,
                                   noiseSelector: new NoiseSelector(
                                       from: 0.95,
                                       to: 1,
                                       noise: new WhiteNoise(seed: 306721460))));
        }
Пример #5
0
        private bool ServerRemote_ApplyWatering(Vector2Ushort tilePosition, IItem item)
        {
            var character = ServerRemoteContext.Character;

            this.ServerValidateItemForRemoteCall(item, character);

            var result = SharedGetAvailablePlantAt(tilePosition, character);

            if (!result.IsSuccess)
            {
                Logger.Warning(result.ErrorTitle + ": " + result.ErrorMessage, character);
                return(false);
            }

            var objectPlant       = result.ObjectPlant;
            var protoPlant        = (IProtoObjectPlant)objectPlant.ProtoStaticWorldObject;
            var plantPrivateState = objectPlant.GetPrivateState <PlantPrivateState>();

            // validate if can apply this
            var wateringDuration = this.WateringDuration;

            if (plantPrivateState.ServerTimeWateringEnds >= double.MaxValue)
            {
                // already applied
                this.CallClient(character, _ => _.ClientRemote_CannotApplyAlreadyApplied());
                return(false);
            }

            if (plantPrivateState.ProducedHarvestsCount == protoPlant.NumberOfHarvests &&
                protoPlant.NumberOfHarvests > 0)
            {
                // no need to apply - last harvest
                this.CallClient(character, _ => _.ClientRemote_CannotApplyLastHarvest());
                return(false);
            }

            // apply watering
            ServerItemUseObserver.NotifyItemUsed(character, item);
            Server.Items.SetCount(item, item.Count - 1);
            protoPlant.ServerOnWatered(character, objectPlant, wateringDuration: wateringDuration);

            // notify client
            Logger.Important($"Watering applied: {this} to {objectPlant}");
            return(true);
        }
Пример #6
0
        private void ServerRemote_PlaceAt(IItem item, Vector2Ushort tilePosition)
        {
            var character = ServerRemoteContext.Character;

            this.ServerValidateItemForRemoteCall(item, character);

            this.SharedIsValidPlacementPosition(
                tilePosition,
                character,
                logErrors: true,
                canPlace: out var canPlace,
                isTooFar: out var isTooFar,
                errorCodeOrMessage: out _);
            if (!canPlace || isTooFar)
            {
                return;
            }

            var plantObject = Server.World.CreateStaticWorldObject(this.ObjectPlantProto, tilePosition);

            if (this.ObjectPlantProto is IProtoObjectPlant protoFarmPlant)
            {
                protoFarmPlant.ServerSetBonusForCharacter(plantObject, character);
            }

            Logger.Important($"{character} has placed plant {plantObject} from seed {item}");

            this.ServerNotifyItemUsed(character, item);
            // decrease item count
            Server.Items.SetCount(item, (ushort)(item.Count - 1));

            character.ServerAddSkillExperience <SkillFarming>(SkillFarming.ExperienceForSeedPlanting);

            // restore structure points and reset decay for the farm(s) in the tile where the seed was planted
            foreach (var tileObject in plantObject.OccupiedTile.StaticObjects)
            {
                if (tileObject.ProtoStaticWorldObject is not IProtoObjectFarm protoFarm)
                {
                    continue;
                }

                tileObject.GetPublicState <StaticObjectPublicState>()
                .StructurePointsCurrent = protoFarm.SharedGetStructurePointsMax(tileObject);
            }
        }
Пример #7
0
        private static bool ClampTextureSize(ref Vector2Ushort textureSize, out double scale)
        {
            var size    = Math.Max(textureSize.X, textureSize.Y);
            var maxSize = MaxIconTextureSIze;

            if (size < maxSize)
            {
                scale = 1;
                return(false);
            }

            scale = Math.Min(maxSize / (double)textureSize.X,
                             maxSize / (double)textureSize.Y);

            textureSize = new Vector2Ushort((ushort)(textureSize.X * scale),
                                            (ushort)(textureSize.Y * scale));
            return(true);
        }
Пример #8
0
        // Please note: the start position is located in bottom left corner of the layout.
        protected static IEnumerable <IStaticWorldObject> SharedFindObjectsNearby
        <TProtoObject>(Vector2Ushort startPosition)
            where TProtoObject : class, IProtoStaticWorldObject
        {
            var world = IsServer
                            ? (IWorldService)Server.World
                            : (IWorldService)Client.World;

            // TODO: the offset here should be taken from current prototype Layout (its center)
            var bounds = new RectangleInt(startPosition + (1, 1),
                                          size: (1, 1));

            bounds = bounds.Inflate(MinDistanceBetweenExtractors);

            var objectsInBounds = world.GetStaticWorldObjectsOfProtoInBounds <TProtoObject>(bounds);

            return(objectsInBounds);
        }
        private void AddMarker(Vector2Ushort position)
        {
            if (this.markers.ContainsKey(position))
            {
                Api.Logger.Warning("Dropped items already has the map visualizer: " + position);
                return;
            }

            var mapControl = new WorldMapMarkDroppedItems();
            var canvasPosition = this.worldMapController.WorldToCanvasPosition(position.ToVector2D());
            Canvas.SetLeft(mapControl, canvasPosition.X);
            Canvas.SetTop(mapControl, canvasPosition.Y);
            Panel.SetZIndex(mapControl, 12);

            this.worldMapController.AddControl(mapControl);

            this.markers[position] = mapControl;
        }
Пример #10
0
        private void ServerStructureRelocatedHandler(
            ICharacter character,
            Vector2Ushort fromPosition,
            IStaticWorldObject structure)
        {
            if (structure.ProtoGameObject != this)
            {
                return;
            }

            var bedPrivateState = GetPrivateState(structure);
            var currentOwner    = bedPrivateState.Owner;

            if (currentOwner is not null)
            {
                ServerSetCurrentBed(structure, currentOwner);
            }
        }
        private IStaticWorldObject ServerTryCreateGroundContainer(
            Vector2Ushort tilePosition,
            bool writeWarningsToLog = true)
        {
            if (this.CheckTileRequirements(tilePosition, character: null, logErrors: false))
            {
                Logger.Info("Creating ground container at " + tilePosition);
                return(Server.World.CreateStaticWorldObject(this, tilePosition));
            }

            if (writeWarningsToLog)
            {
                Logger.Warning(
                    $"Cannot create ground container at {tilePosition} - tile contains something preventing it.");
            }

            return(null);
        }
Пример #12
0
        public static void ServerSpawnDestroyedWall(
            Vector2Ushort tilePosition,
            IProtoObjectWall originalProtoObjectWall)
        {
            if (Server.World
                .GetTile(tilePosition)
                .StaticObjects
                .Any(so => so.ProtoStaticWorldObject is ObjectWallDestroyed))
            {
                //Logger.Error("Already spawned a destroyed wall here: " + tilePosition);
                return;
            }

            var worldObject = Server.World.CreateStaticWorldObject <ObjectWallDestroyed>(tilePosition);

            GetPublicState(worldObject).OriginalProtoObjectWall = originalProtoObjectWall;
            Logger.Important($"Spawning a destroyed wall at: {tilePosition} ({originalProtoObjectWall})");
        }
        private static async ValueTask <Vector2Ushort> PrepareLayers(
            ProceduralTextureRequest request,
            List <ComposeLayer> spritesToCombine)
        {
            var textureDataTasks = spritesToCombine.Select(
                t => Rendering
                .GetTextureSizeWithMagentaPixelPosition(t.TextureResource))
                                   .ToList();

            foreach (var textureDataTask in textureDataTasks)
            {
                await textureDataTask;
                request.ThrowIfCancelled();
            }

            var extendX = 0f;
            var extendY = 0f;

            for (var index = 0; index < textureDataTasks.Count; index++)
            {
                var result      = textureDataTasks[index].Result;
                var pivotPos    = result.MagentaPixelPosition;
                var composeItem = spritesToCombine[index];
                composeItem.PivotPos    = pivotPos;
                spritesToCombine[index] = composeItem;

                var itemExtendX = Math.Max(pivotPos.X, result.Size.X - pivotPos.X);
                var itemExtendY = Math.Max(pivotPos.Y, result.Size.Y - pivotPos.Y);
                if (itemExtendX > extendX)
                {
                    extendX = itemExtendX;
                }

                if (itemExtendY > extendY)
                {
                    extendY = itemExtendY;
                }
            }

            var resultTextureSize = new Vector2Ushort((ushort)Math.Floor(extendX * 2),
                                                      (ushort)Math.Floor(extendY * 2));

            return(resultTextureSize);
        }
Пример #14
0
 public WallPattern(
     string name,
     Vector2Ushort atlasChunk,
     NeighborsPattern requiresNeighbors,
     double drawOffsetNormal                = 0,
     double?drawOffsetDestroyed             = null,
     Action <IPhysicsBody> physicsNormal    = null,
     Action <IPhysicsBody> physicsDestroyed = null,
     bool isValidDestroyed = true)
 {
     this.Name = name;
     this.AtlasChunkPosition    = atlasChunk;
     this.RequiresNeighbors     = requiresNeighbors;
     this.DrawOffsetNormal      = drawOffsetNormal;
     this.DrawOffsetDestroyed   = drawOffsetDestroyed ?? drawOffsetNormal;
     this.SetupPhysicsNormal    = physicsNormal;
     this.SetupPhysicsDestroyed = physicsDestroyed ?? physicsNormal;
     this.IsValidDestroyed      = isValidDestroyed;
 }
        /// <summary>
        /// When the explosion is processed this method will be called (see ObjectMineralPragmiumSourceExplosion).
        /// </summary>
        internal static void ServerOnExplode(
            Vector2Ushort epicenterPosition,
            double explosionRadius,
            ICharacter byCharacter)
        {
            // let's spawn scattered pragmium nodes around the explosion site
            var countToSpawnRemains = DestroySpawnNodeCount;
            var attemptsRemains     = 2000;

            while (countToSpawnRemains > 0)
            {
                attemptsRemains--;
                if (attemptsRemains <= 0)
                {
                    // attempts exceeded
                    return;
                }

                // calculate random distance from the explosion epicenter
                var distance = RandomHelper.Range(2, explosionRadius);

                // ensure we spawn more objects closer to the epicenter
                var spawnProbability = 1 - (distance / explosionRadius);
                spawnProbability = Math.Pow(spawnProbability, 1.5);
                if (!RandomHelper.RollWithProbability(spawnProbability))
                {
                    // random skip
                    continue;
                }

                var angle         = RandomHelper.NextDouble() * MathConstants.DoublePI;
                var spawnPosition = new Vector2Ushort(
                    (ushort)(epicenterPosition.X + distance * Math.Cos(angle)),
                    (ushort)(epicenterPosition.Y + distance * Math.Sin(angle)));

                // try spawn a pragmium node
                if (ServerTrySpawnNode(spawnPosition, pveTagForCharacter: byCharacter))
                {
                    // spawned successfully!
                    countToSpawnRemains--;
                }
            }
        }
Пример #16
0
 public WorldMapControllerMiniMap(
     PanningPanel panningPanel,
     ViewModelControlWorldMap viewModelControlWorldMap,
     bool isPlayerMarkDisplayed,
     bool isCurrentCameraViewDisplayed,
     bool isListeningToInput,
     int paddingChunks,
     Vector2Ushort mapAreaSize,
     ControlTemplate customControlTemplatePlayerMark)
     : base(panningPanel,
            viewModelControlWorldMap,
            isPlayerMarkDisplayed,
            isCurrentCameraViewDisplayed,
            isListeningToInput,
            paddingChunks,
            customControlTemplatePlayerMark)
 {
     this.mapAreaSize = mapAreaSize;
 }
Пример #17
0
        public static bool operator ==(CustomMarkData markData, CustomMarkData markDataK)
        {
            if (object.ReferenceEquals(markData, null))
            {
                return(false);
            }
            if (object.ReferenceEquals(markDataK, null))
            {
                return(false);
            }

            string[]      sm = markData.Key.Split(';');
            Vector2Ushort m  = new Vector2Ushort(Convert.ToUInt16(sm[0]), Convert.ToUInt16(sm[1]));

            string[]      sk = markDataK.Key.Split(';');
            Vector2Ushort k  = new Vector2Ushort(Convert.ToUInt16(sk[0]), Convert.ToUInt16(sk[1]));

            return(m.X == k.X && m.Y == k.Y);
        }
        private void OnPlaceSelected(Vector2Ushort tilePosition, bool isButtonHeld)
        {
            if (this.delayRemainsSeconds > 0)
            {
                return;
            }

            this.validateCanBuildCallback(tilePosition,
                                          logErrors: !isButtonHeld,
                                          out var canPlace,
                                          out var isTooFar);

            if (!canPlace || isTooFar)
            {
                return;
            }

            this.placeSelectedCallback(tilePosition);
        }
Пример #19
0
 private void ClientRemote_OnWeaponHitOrTrace(
     ICharacter firingCharacter,
     IProtoItemWeapon protoWeapon,
     IProtoItemAmmo protoAmmo,
     IProtoCharacter protoCharacter,
     Vector2Ushort fallbackCharacterPosition,
     WeaponHitData[] hitObjects,
     Vector2D endPosition,
     bool endsWithHit)
 {
     WeaponSystemClientDisplay.ClientOnWeaponHitOrTrace(firingCharacter,
                                                        protoWeapon,
                                                        protoAmmo,
                                                        protoCharacter,
                                                        fallbackCharacterPosition,
                                                        hitObjects,
                                                        endPosition,
                                                        endsWithHit);
 }
Пример #20
0
            public Sector(
                WorldMapSectorProvider sectorProvider,
                Vector2Ushort sectorWorldPosition,
                Vector2D sectorVisualPosition)
            {
                this.SectorWorldPosition = sectorWorldPosition;
                this.SectorRenderer      = sectorProvider.GetSectorRenderer(sectorWorldPosition);
                var sectorRectangle = new Rectangle()
                {
                    // + 1 is required to prevent a seam from appearing between sector rectangles
                    Width  = WorldMapSectorProvider.SectorPixelSize + 1,
                    Height = WorldMapSectorProvider.SectorPixelSize + 1,
                    Fill   = Api.Client.UI.GetTextureBrush(this.SectorRenderer.RenderTexture)
                };

                Canvas.SetLeft(sectorRectangle, sectorVisualPosition.X);
                Canvas.SetTop(sectorRectangle, sectorVisualPosition.Y - WorldMapSectorProvider.SectorPixelSize);
                this.SectorRectangle = sectorRectangle;
            }
        /// <summary>
        /// Perform distance, height, and other checks.
        /// </summary>
        public static bool SharedIsValidStartLocation(
            ICharacter character,
            Vector2Ushort targetPosition,
            out bool hasObstacles)
        {
            hasObstacles = false;
            if (character.TilePosition.TileSqrDistanceTo(targetPosition)
                > DroneStartDistanceMax * DroneStartDistanceMax)
            {
                return(false);
            }

            var targetTile = IsServer
                                 ? Server.World.GetTile(targetPosition)
                                 : Client.World.GetTile(targetPosition);

            if (targetTile.Height != character.Tile.Height)
            {
                return(false);
            }

            // check for obstacles
            using var testResults = character.PhysicsBody.PhysicsSpace.TestLine(
                      fromPosition: character.Position,
                      toPosition: (targetPosition.X + 0.5,
                                   targetPosition.Y + 0.5),
                      collisionGroup: CollisionGroups.Default,
                      sendDebugEvent: false);

            foreach (var testResult in testResults.AsList())
            {
                switch (testResult.PhysicsBody.AssociatedWorldObject?.ProtoGameObject)
                {
                case IProtoObjectWall _:
                case IProtoObjectDoor _:
                    // don't allow mining through walls and doors
                    hasObstacles = true;
                    return(false);
                }
            }

            return(true);
        }
Пример #22
0
        private void ClientRemote_OnWeaponShot(
            ICharacter whoFires,
            uint partyId,
            IProtoItemWeapon protoWeapon,
            IProtoCharacter fallbackProtoCharacter,
            Vector2Ushort fallbackPosition)
        {
            if (whoFires != null &&
                !whoFires.IsInitialized)
            {
                whoFires = null;
            }

            WeaponSystemClientDisplay.ClientOnWeaponShot(whoFires,
                                                         partyId,
                                                         protoWeapon,
                                                         fallbackProtoCharacter,
                                                         fallbackPosition);
        }
Пример #23
0
        private ItemExplosiveRequest ClientTryCreateRequest(ICharacter character, Vector2Ushort targetPosition)
        {
            var item = character.SharedGetPlayerSelectedHotbarItem();

            if (!(item.ProtoItem is IProtoItemExplosive protoItemExplosive))
            {
                // no explosive item selected
                return(null);
            }

            if (!protoItemExplosive.SharedValidatePlacement(character,
                                                            targetPosition,
                                                            logErrors: true))
            {
                return(null);
            }

            return(new ItemExplosiveRequest(character, item, targetPosition));
        }
Пример #24
0
        public static void ClientPaste(Vector2Ushort tilePosition)
        {
            if (lastBufferEntry is null)
            {
                return;
            }

            var bufferEntry = lastBufferEntry.Value;

            if (ClientEditorAreaSelectorHelper.Instance is not null)
            {
                NotificationSystem.ClientShowNotification(
                    title: null,
                    message:
                    "You're already in object placement mode."
                    + ObjectPlacementGuide,
                    color: NotificationColor.Neutral);
                return;
            }

            NotificationSystem.ClientShowNotification(
                title: null,
                message:
                $"{bufferEntry.Entries.Count} tiles ready for paste!"
                + ObjectPlacementGuide,
                color: NotificationColor.Good);

            var originalSize = bufferEntry.Size;

            // ReSharper disable once ObjectCreationAsStatement
            new ClientEditorAreaSelectorHelper(tilePosition,
                                               originalSize,
                                               selectedCallback: PlaceSelectedCallback);

            void PlaceSelectedCallback(Vector2Ushort selectedTilePosition)
            {
                var entries = bufferEntry.Entries
                              .Select(e => e.ApplyOffset(selectedTilePosition))
                              .ToList();

                TerrainEditingSystem.ClientModifyTerrain(entries);
            }
        }
        private static void SetupBoundsForLandClaimsInScope(
            IClientSceneObject sceneObject,
            Vector2D sceneObjectPosition,
            Vector2Ushort originTilePosition,
            RectangleInt originBounds,
            IProtoObjectLandClaim originProtoObjectLandClaim)
        {
            var landClaims = Api.Client.World.GetStaticWorldObjectsOfProto <IProtoObjectLandClaim>();

            foreach (var landClaim in landClaims)
            {
                var protoObjectLandClaim    = (IProtoObjectLandClaim)landClaim.ProtoGameObject;
                var landClaimCenterPosition = LandClaimSystem
                                              .SharedCalculateLandClaimObjectCenterTilePosition(
                    landClaim.TilePosition,
                    protoObjectLandClaim);

                var landClaimBounds = LandClaimSystem.SharedCalculateLandClaimAreaBounds(
                    landClaimCenterPosition,
                    protoObjectLandClaim.LandClaimWithGraceAreaSize);

                var intersectionDepth = CalculateIntersectionDepth(originBounds, landClaimBounds);
                if (intersectionDepth < 0)
                {
                    // no intersection
                    continue;
                }

                intersectionDepth = (intersectionDepth + 1) / 2;
                intersectionDepth = Math.Min(intersectionDepth,
                                             originProtoObjectLandClaim.LandClaimGraceAreaPaddingSizeOneDirection + 1);

                var exceptBounds = originBounds.Inflate(-intersectionDepth);
                using var tempList = Api.Shared.WrapObjectInTempList(exceptBounds);

                AddBoundLabels(sceneObject,
                               sceneObjectPosition,
                               exceptBounds: tempList.AsList(),
                               protoObjectLandClaim,
                               positionOffset: landClaimCenterPosition.ToVector2D()
                               - originTilePosition.ToVector2D());
            }
        }
Пример #26
0
        private void ClientRemote_OnStructurePlaced(
            IProtoStaticWorldObject protoStaticWorldObject,
            Vector2Ushort position,
            bool isByCurrentPlayer)
        {
            var soundPreset = protoStaticWorldObject.SharedGetObjectSoundPreset();

            if (isByCurrentPlayer)
            {
                // play 2D sound
                soundPreset.PlaySound(ObjectSound.Place, limitOnePerFrame: false);
            }
            else
            {
                // play 3D sound (at the built object location)
                soundPreset.PlaySound(ObjectSound.Place,
                                      position.ToVector2D() + protoStaticWorldObject.Layout.Center);
            }
        }
Пример #27
0
        /// <summary>
        /// Server spawn callback for mob.
        /// </summary>
        /// <param name="trigger">Trigger leading to this spawn.</param>
        /// <param name="zone">Server zone instance.</param>
        /// <param name="protoMob">Prototype of character mob object to spawn.</param>
        /// <param name="tilePosition">Position to try spawn at.</param>
        protected virtual IGameObjectWithProto ServerSpawnMob(
            IProtoTrigger trigger,
            IServerZone zone,
            IProtoCharacterMob protoMob,
            Vector2Ushort tilePosition)
        {
            var worldPosition = tilePosition.ToVector2D();

            if (!ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(worldPosition,
                                                                             isPlayer: false))
            {
                // position is not valid for spawning
                return(null);
            }

            return(Server.Characters.SpawnCharacter(
                       protoMob,
                       worldPosition));
        }
Пример #28
0
        public static RectangleInt SharedCalculateLandClaimAreaBounds(Vector2Ushort centerTilePosition, ushort size)
        {
            var worldBounds = IsServer
                                  ? ServerWorld.WorldBounds
                                  : ClientWorld.WorldBounds;
            var pos      = centerTilePosition;
            var halfSize = size / 2.0;

            var start = new Vector2Ushort(
                (ushort)Math.Max(Math.Ceiling(pos.X - halfSize), worldBounds.MinX),
                (ushort)Math.Max(Math.Ceiling(pos.Y - halfSize), worldBounds.MinY));

            var endX = Math.Min(Math.Ceiling(pos.X + halfSize), worldBounds.MaxX);
            var endY = Math.Min(Math.Ceiling(pos.Y + halfSize), worldBounds.MaxY);

            var calculatedSize = new Vector2Ushort((ushort)(endX - start.X),
                                                   (ushort)(endY - start.Y));

            return(new RectangleInt(start, size: calculatedSize));
        }
Пример #29
0
        private bool CheckIsValidOffset(Vector2Ushort offset)
        {
            var worldBounds = Api.Client.World.WorldBounds;
            var maxPosition = offset + this.size;

            if (maxPosition.X < worldBounds.MaxX &&
                maxPosition.Y < worldBounds.MaxY &&
                offset.X > worldBounds.MinX &&
                offset.Y > worldBounds.MinY)
            {
                return(true);
            }

            NotificationSystem.ClientShowNotification(
                title: null,
                message:
                "Out of world bounds, please select different location",
                color: NotificationColor.Bad);
            return(false);
        }
Пример #30
0
        private static GetPlantResult SharedGetAvailablePlantAt(Vector2Ushort tilePosition, ICharacter character)
        {
            var tile = WorldService.GetTile(tilePosition);
            var objectPlant = tile.StaticObjects.FirstOrDefault(so => so.ProtoStaticWorldObject is IProtoObjectPlant);
            if (objectPlant is null)
            {
                return GetPlantResult.Fail(CannotApplyErrorTitle, "Can apply only on plants.");
            }

            if (!objectPlant.ProtoStaticWorldObject
                            .SharedCanInteract(
                                character,
                                objectPlant,
                                writeToLog: false))
            {
                return GetPlantResult.Fail(CoreStrings.Notification_TooFar, "Come closer to apply fertilizer.");
            }

            return GetPlantResult.Success(objectPlant);
        }