public static void Throw(IEntity thrownEnt, float throwForce, GridCoordinates targetLoc, GridCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null) { if (!thrownEnt.TryGetComponent(out CollidableComponent colComp)) { return; } var mapManager = IoCManager.Resolve <IMapManager>(); colComp.CollisionEnabled = true; // I can now collide with player, so that i can do damage. if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp)) { projComp = thrownEnt.AddComponent <ThrownItemComponent>(); if (colComp.PhysicsShapes.Count == 0) { colComp.PhysicsShapes.Add(new PhysShapeAabb()); } colComp.PhysicsShapes[0].CollisionMask |= (int)CollisionGroup.MobImpassable; colComp.IsScrapingFloor = false; } var angle = new Angle(targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)); if (spread) { var spreadRandom = IoCManager.Resolve <IRobustRandom>(); angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3)); } if (throwSourceEnt != null) { projComp.User = throwSourceEnt; projComp.IgnoreEntity(throwSourceEnt); throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle(); } if (!thrownEnt.TryGetComponent(out PhysicsComponent physComp)) { physComp = thrownEnt.AddComponent <PhysicsComponent>(); } // TODO: Move this into PhysicsSystem, we need an ApplyForce function. var a = throwForce / (float)Math.Max(0.001, physComp.Mass); // a = f / m var timing = IoCManager.Resolve <IGameTiming>(); var spd = a / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately physComp.LinearVelocity = angle.ToVec() * spd; }
public bool IsColliding(GridCoordinates coordinates) { var bounds = pManager.ColliderAABB; var worldcoords = coordinates.ToMapPos(pManager.MapManager); var collisionbox = Box2.FromDimensions( bounds.Left + worldcoords.X, bounds.Bottom + worldcoords.Y, bounds.Width, bounds.Height); if (pManager.PhysicsManager.TryCollideRect(collisionbox, pManager.MapManager.GetGrid(coordinates.GridID).ParentMapId)) return true; return false; }
/// <summary> /// Play an audio stream at a static position. /// </summary> /// <param name="stream">The audio stream to play.</param> /// <param name="coordinates">The coordinates at which to play the audio.</param> /// <param name="audioParams"></param> public IPlayingAudioStream Play(AudioStream stream, GridCoordinates coordinates, AudioParams?audioParams = null) { var source = _clyde.CreateAudioSource(stream); source.SetPosition(coordinates.ToMapPos(_mapManager)); ApplyAudioParams(audioParams, source); source.StartPlaying(); var playing = new PlayingStream { Source = source, TrackingCoordinates = coordinates, }; PlayingClydeStreams.Add(playing); return(playing); }
/// <summary> /// Throw an entity at the position of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>, /// without overshooting. /// </summary> /// <param name="thrownEnt">The entity to throw.</param> /// <param name="throwForceMax"> /// The MAXIMUM force to throw the entity with. /// Throw force increases with distance to target, this is the maximum force allowed. /// </param> /// <param name="targetLoc"> /// The target location to throw at. /// This function will try to land at this exact spot, /// if <paramref name="throwForceMax"/> is large enough to allow for it to be reached. /// </param> /// <param name="sourceLoc"> /// The position to start the throw from. /// </param> /// <param name="spread"> /// If true, slightly spread the actual throw angle. /// </param> /// <param name="throwSourceEnt"> /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in. /// </param> public static void ThrowTo(IEntity thrownEnt, float throwForceMax, GridCoordinates targetLoc, GridCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null) { var mapManager = IoCManager.Resolve <IMapManager>(); var timing = IoCManager.Resolve <IGameTiming>(); // Calculate the force necessary to land a throw based on throw duration, mass and distance. var distance = (targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)).Length; var throwDuration = ThrowController.DefaultThrowTime; var mass = 1f; if (thrownEnt.TryGetComponent(out PhysicsComponent physicsComponent)) { mass = physicsComponent.Mass; } var velocityNecessary = distance / throwDuration; var impulseNecessary = velocityNecessary * mass; var forceNecessary = impulseNecessary * (1f / timing.TickRate); // Then clamp it to the max force allowed and call Throw(). Throw(thrownEnt, Math.Min(forceNecessary, throwForceMax), targetLoc, sourceLoc, spread, throwSourceEnt); }
/// <summary> /// Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>. /// </summary> /// <param name="thrownEnt">The entity to throw.</param> /// <param name="throwForce"> /// The force to throw the entity with. /// Total impulse applied is equal to this force applied for one second. /// </param> /// <param name="targetLoc"> /// The target location to throw at. /// This is only used to calculate a direction, /// actual distance is purely determined by <paramref name="throwForce"/>. /// </param> /// <param name="sourceLoc"> /// The position to start the throw from. /// </param> /// <param name="spread"> /// If true, slightly spread the actual throw angle. /// </param> /// <param name="throwSourceEnt"> /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in. /// </param> public static void Throw(IEntity thrownEnt, float throwForce, GridCoordinates targetLoc, GridCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null) { if (!thrownEnt.TryGetComponent(out CollidableComponent colComp)) { return; } var mapManager = IoCManager.Resolve <IMapManager>(); colComp.CanCollide = true; // I can now collide with player, so that i can do damage. if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp)) { projComp = thrownEnt.AddComponent <ThrownItemComponent>(); if (colComp.PhysicsShapes.Count == 0) { colComp.PhysicsShapes.Add(new PhysShapeAabb()); } colComp.PhysicsShapes[0].CollisionMask |= (int)(CollisionGroup.MobImpassable | CollisionGroup.Impassable); colComp.Status = BodyStatus.InAir; } var angle = new Angle(targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)); if (spread) { var spreadRandom = IoCManager.Resolve <IRobustRandom>(); angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3)); } if (throwSourceEnt != null) { projComp.User = throwSourceEnt; projComp.IgnoreEntity(throwSourceEnt); throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle(); } if (!thrownEnt.TryGetComponent(out PhysicsComponent physComp)) { physComp = thrownEnt.AddComponent <PhysicsComponent>(); } var timing = IoCManager.Resolve <IGameTiming>(); var spd = throwForce / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately physComp.SetController <ThrowController>(); (physComp.Controller as ThrowController)?.StartThrow(angle.ToVec() * spd); if (throwSourceEnt != null && throwSourceEnt.TryGetComponent <PhysicsComponent>(out var physics) && physics.Controller is MoverController mover) { var physicsMgr = IoCManager.Resolve <IPhysicsManager>(); if (physicsMgr.IsWeightless(throwSourceEnt.Transform.GridPosition)) { // We don't check for surrounding entities, // so you'll still get knocked around if you're hugging the station wall in zero g. // I got kinda lazy is the reason why. Also it makes a bit of sense. // If somebody wants they can come along and make it so magboots completely hold you still. // Would be a cool incentive to use them. const float ThrowFactor = 5.0f; // Break Newton's Third Law for better gameplay mover.Push(-angle.ToVec(), spd * ThrowFactor / physics.Mass); } } }
/// <summary> /// Throw an entity in the direction of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>. /// </summary> /// <param name="thrownEnt">The entity to throw.</param> /// <param name="throwForce"> /// The force to throw the entity with. /// Total impulse applied is equal to this force applied for one second. /// </param> /// <param name="targetLoc"> /// The target location to throw at. /// This is only used to calculate a direction, /// actual distance is purely determined by <paramref name="throwForce"/>. /// </param> /// <param name="sourceLoc"> /// The position to start the throw from. /// </param> /// <param name="spread"> /// If true, slightly spread the actual throw angle. /// </param> /// <param name="throwSourceEnt"> /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in. /// </param> public static void Throw(IEntity thrownEnt, float throwForce, GridCoordinates targetLoc, GridCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null) { if (!thrownEnt.TryGetComponent(out ICollidableComponent colComp)) { return; } var mapManager = IoCManager.Resolve <IMapManager>(); colComp.CanCollide = true; // I can now collide with player, so that i can do damage. if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp)) { projComp = thrownEnt.AddComponent <ThrownItemComponent>(); if (colComp.PhysicsShapes.Count == 0) { colComp.PhysicsShapes.Add(new PhysShapeAabb()); } colComp.PhysicsShapes[0].CollisionMask |= (int)CollisionGroup.ThrownItem; colComp.Status = BodyStatus.InAir; } var angle = new Angle(targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)); if (spread) { var spreadRandom = IoCManager.Resolve <IRobustRandom>(); angle += Angle.FromDegrees(spreadRandom.NextGaussian(0, 3)); } if (throwSourceEnt != null) { projComp.User = throwSourceEnt; projComp.IgnoreEntity(throwSourceEnt); if (ActionBlockerSystem.CanChangeDirection(throwSourceEnt)) { throwSourceEnt.Transform.LocalRotation = angle.GetCardinalDir().ToAngle(); } } // scaling is handled elsewhere, this is just multiplying by 10 independent of timing as a fix until elsewhere values are updated var spd = throwForce * 10; projComp.StartThrow(angle.ToVec(), spd); if (throwSourceEnt != null && throwSourceEnt.TryGetComponent <IPhysicsComponent>(out var physics) && physics.TryGetController(out MoverController mover)) { var physicsMgr = IoCManager.Resolve <IPhysicsManager>(); if (physicsMgr.IsWeightless(throwSourceEnt.Transform.GridPosition)) { // We don't check for surrounding entities, // so you'll still get knocked around if you're hugging the station wall in zero g. // I got kinda lazy is the reason why. Also it makes a bit of sense. // If somebody wants they can come along and make it so magboots completely hold you still. // Would be a cool incentive to use them. const float ThrowFactor = 5.0f; // Break Newton's Third Law for better gameplay mover.Push(-angle.ToVec(), spd * ThrowFactor / physics.Mass); } } }
private void UserInteraction(IEntity player, GridCoordinates coordinates, EntityUid clickedUid) { // Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null if (!EntityManager.TryGetEntity(clickedUid, out var attacked)) { attacked = null; } // Verify player has a transform component if (!player.TryGetComponent <ITransformComponent>(out var playerTransform)) { return; } // Verify player is on the same map as the entity he clicked on if (_mapManager.GetGrid(coordinates.GridID).ParentMapId != playerTransform.MapID) { Logger.WarningS("system.interaction", $"Player named {player.Name} clicked on a map he isn't located on"); return; } // Verify player has a hand, and find what object he is currently holding in his active hand if (!player.TryGetComponent <IHandsComponent>(out var hands)) { return; } var item = hands.GetActiveHand?.Owner; if (!ActionBlockerSystem.CanInteract(player)) { return; } playerTransform.LocalRotation = new Angle(coordinates.ToMapPos(_mapManager) - playerTransform.MapPosition.Position); // TODO: Check if client should be able to see that object to click on it in the first place // Clicked on empty space behavior, try using ranged attack if (attacked == null) { if (item != null) { // After attack: Check if we clicked on an empty location, if so the only interaction we can do is AfterAttack InteractAfterAttack(player, item, coordinates); } return; } // Verify attacked object is on the map if we managed to click on it somehow if (!attacked.Transform.IsMapTransform) { Logger.WarningS("system.interaction", $"Player named {player.Name} clicked on object {attacked.Name} that isn't currently on the map somehow"); return; } // Check if ClickLocation is in object bounds here, if not lets log as warning and see why if (attacked.TryGetComponent(out ICollidableComponent collideComp)) { if (!collideComp.WorldAABB.Contains(coordinates.ToMapPos(_mapManager))) { Logger.WarningS("system.interaction", $"Player {player.Name} clicked {attacked.Name} outside of its bounding box component somehow"); return; } } // RangedAttack/AfterAttack: Check distance between user and clicked item, if too large parse it in the ranged function // TODO: have range based upon the item being used? or base it upon some variables of the player himself? var distance = (playerTransform.WorldPosition - attacked.Transform.WorldPosition).LengthSquared; if (distance > InteractionRangeSquared) { if (item != null) { RangedInteraction(player, item, attacked, coordinates); return; } return; // Add some form of ranged AttackHand here if you need it someday, or perhaps just ways to modify the range of AttackHand } // We are close to the nearby object and the object isn't contained in our active hand // AttackBy/AfterAttack: We will either use the item on the nearby object if (item != null) { Interaction(player, item, attacked, coordinates); } // AttackHand/Activate: Since our hand is empty we will use AttackHand/Activate else { Interaction(player, attacked); } }
public void Update(float frameTime) { Age += TimeSpan.FromSeconds(frameTime); if (Age >= Deathtime) { return; } Velocity += Acceleration * frameTime; RadialVelocity += RadialAcceleration * frameTime; TangentialVelocity += TangentialAcceleration * frameTime; var deltaPosition = new Vector2(0f, 0f); //If we have an emitter we can do special effects around that emitter position if (_mapManager.GridExists(EmitterCoordinates.GridID)) { //Calculate delta p due to radial velocity var positionRelativeToEmitter = Coordinates.ToMapPos(_mapManager) - EmitterCoordinates.ToMapPos(_mapManager); var deltaRadial = RadialVelocity * frameTime; deltaPosition = positionRelativeToEmitter * (deltaRadial / positionRelativeToEmitter.Length); //Calculate delta p due to tangential velocity var radius = positionRelativeToEmitter.Length; if (radius > 0) { var theta = (float)Math.Atan2(positionRelativeToEmitter.Y, positionRelativeToEmitter.X); theta += TangentialVelocity * frameTime; deltaPosition += new Vector2(radius * (float)Math.Cos(theta), radius * (float)Math.Sin(theta)) - positionRelativeToEmitter; } } //Calculate new position from our velocity as well as possible rotation/movement around emitter deltaPosition += Velocity * frameTime; Coordinates = new GridCoordinates(Coordinates.Position + deltaPosition, Coordinates.GridID); //Finish calculating new rotation, size, color Rotation += RotationRate * frameTime; Size += SizeDelta * frameTime; Color += ColorDelta * frameTime; if (RsiState == null) { return; } // Calculate RSI animations. var delayCount = RsiState.DelayCount; if (delayCount > 0 && (AnimationLoops || AnimationIndex < delayCount - 1)) { AnimationTime += frameTime; while (RsiState.GetDelay(AnimationIndex) < AnimationTime) { var delay = RsiState.GetDelay(AnimationIndex); AnimationIndex += 1; AnimationTime -= delay; if (AnimationIndex == delayCount) { if (AnimationLoops) { AnimationIndex = 0; } else { break; } } EffectSprite = RsiState.GetFrame(RSI.State.Direction.South, AnimationIndex); } } }
public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid) { var player = session?.AttachedEntity; if (player == null) { return(false); } if (_pointers.TryGetValue(session !, out var lastTime) && _gameTiming.CurTime < lastTime + PointDelay) { return(false); } if (!InRange(coords, player.Transform.GridPosition)) { player.PopupMessage(player, Loc.GetString("You can't reach there!")); return(false); } if (ActionBlockerSystem.CanChangeDirection(player)) { var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } } var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15); EntityManager.SpawnEntity("pointingarrow", coords); string selfMessage; string viewerMessage; string?viewerPointedAtMessage = null; if (EntityManager.TryGetEntity(uid, out var pointed)) { selfMessage = player == pointed ? Loc.GetString("You point at yourself.") : Loc.GetString("You point at {0:theName}.", pointed); viewerMessage = player == pointed ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}" : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}"; viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}"; } else { var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords); var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName); viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}"; } _pointers[session !] = _gameTiming.CurTime;
void TryStartStructureConstruction(GridCoordinates loc, string prototypeName, Angle angle, int ack) { var prototype = _prototypeManager.Index <ConstructionPrototype>(prototypeName); if (!InteractionChecks.InRangeUnobstructed(Owner, loc.ToMapPos(_mapManager), ignoredEnt: Owner, insideBlockerValid: prototype.CanBuildInImpassable)) { return; } if (prototype.Stages.Count < 2) { throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages."); } var stage0 = prototype.Stages[0]; if (!(stage0.Forward is ConstructionStepMaterial matStep)) { throw new NotImplementedException(); } // Try to find the stack with the material in the user's hand. var hands = Owner.GetComponent <HandsComponent>(); var activeHand = hands.GetActiveHand?.Owner; if (activeHand == null) { return; } if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack)) { return; } if (!stack.Use(matStep.Amount)) { return; } // OK WE'RE GOOD CONSTRUCTION STARTED. _entitySystemManager.GetEntitySystem <AudioSystem>().Play("/Audio/items/deconstruct.ogg", loc); if (prototype.Stages.Count == 2) { // Exactly 2 stages, so don't make an intermediate frame. var ent = _serverEntityManager.SpawnEntity(prototype.Result, loc); ent.Transform.LocalRotation = angle; } else { var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", loc); var construction = frame.GetComponent <ConstructionComponent>(); construction.Init(prototype); frame.Transform.LocalRotation = angle; } var msg = new AckStructureConstructionMessage(ack); SendNetworkMessage(msg); }
public bool TryPoint(ICommonSession?session, GridCoordinates coords, EntityUid uid) { var player = (session as IPlayerSession)?.ContentData()?.Mind?.CurrentEntity; if (player == null) { return(false); } if (_pointers.TryGetValue(session !, out var lastTime) && _gameTiming.CurTime < lastTime + PointDelay) { return(false); } if (EntityManager.TryGetEntity(uid, out var entity) && entity.HasComponent <PointingArrowComponent>()) { // this is a pointing arrow. no pointing here... return(false); } if (!InRange(coords, player.Transform.GridPosition)) { player.PopupMessage(Loc.GetString("You can't reach there!")); return(false); } if (ActionBlockerSystem.CanChangeDirection(player)) { var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } } var arrow = EntityManager.SpawnEntity("pointingarrow", coords); var layer = (int)VisibilityFlags.Normal; if (player.TryGetComponent(out VisibilityComponent? playerVisibility)) { var arrowVisibility = arrow.EnsureComponent <VisibilityComponent>(); layer = arrowVisibility.Layer = playerVisibility.Layer; } // Get players that are in range and whose visibility layer matches the arrow's. var viewers = _playerManager.GetPlayersBy((playerSession) => { if ((playerSession.VisibilityMask & layer) == 0) { return(false); } var ent = playerSession.ContentData()?.Mind?.CurrentEntity; return(ent != null && ent.Transform.MapPosition.InRange(player.Transform.MapPosition, PointingRange)); }); string selfMessage; string viewerMessage; string?viewerPointedAtMessage = null; if (EntityManager.TryGetEntity(uid, out var pointed)) { selfMessage = player == pointed ? Loc.GetString("You point at yourself.") : Loc.GetString("You point at {0:theName}.", pointed); viewerMessage = player == pointed ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}" : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}"; viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}"; } else { var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords); var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName); viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}"; } _pointers[session !] = _gameTiming.CurTime;
public static void SpawnExplosion(GridCoordinates coords, int devastationRange, int heavyImpactRange, int lightImpactRange, int flashRange) { var tileDefinitionManager = IoCManager.Resolve <ITileDefinitionManager>(); var serverEntityManager = IoCManager.Resolve <IServerEntityManager>(); var entitySystemManager = IoCManager.Resolve <IEntitySystemManager>(); var mapManager = IoCManager.Resolve <IMapManager>(); var robustRandom = IoCManager.Resolve <IRobustRandom>(); var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0f); //Entity damage calculation var entitiesAll = serverEntityManager.GetEntitiesInRange(coords, maxRange).ToList(); foreach (var entity in entitiesAll) { if (entity.Deleted) { continue; } if (!entity.Transform.IsMapTransform) { continue; } var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(mapManager, coords); var exAct = entitySystemManager.GetEntitySystem <ActSystem>(); var severity = ExplosionSeverity.Destruction; if (distanceFromEntity < devastationRange) { severity = ExplosionSeverity.Destruction; } else if (distanceFromEntity < heavyImpactRange) { severity = ExplosionSeverity.Heavy; } else if (distanceFromEntity < lightImpactRange) { severity = ExplosionSeverity.Light; } else { continue; } //exAct.HandleExplosion(Owner, entity, severity); exAct.HandleExplosion(null, entity, severity); } //Tile damage calculation mockup //TODO: make it into some sort of actual damage component or whatever the boys think is appropriate var mapGrid = mapManager.GetGrid(coords.GridID); var circle = new Circle(coords.Position, maxRange); var tiles = mapGrid.GetTilesIntersecting(circle); foreach (var tile in tiles) { var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices); var tileDef = (ContentTileDefinition)tileDefinitionManager[tile.Tile.TypeId]; var distanceFromTile = (int)tileLoc.Distance(mapManager, coords); if (!string.IsNullOrWhiteSpace(tileDef.SubFloor)) { if (distanceFromTile < devastationRange) { mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId)); } if (distanceFromTile < heavyImpactRange) { if (robustRandom.Prob(80)) { mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId)); } else { mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId)); } } if (distanceFromTile < lightImpactRange) { if (robustRandom.Prob(50)) { mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId)); } } } } //Effects and sounds var time = IoCManager.Resolve <IGameTiming>().CurTime; var message = new EffectSystemMessage { EffectSprite = "Effects/explosion.rsi", RsiState = "explosionfast", Born = time, DeathTime = time + TimeSpan.FromSeconds(5), Size = new Vector2(flashRange / 2, flashRange / 2), Coordinates = coords, //Rotated from east facing Rotation = 0f, ColorDelta = new Vector4(0, 0, 0, -1500f), Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f), Shaded = false }; entitySystemManager.GetEntitySystem <EffectSystem>().CreateParticle(message); entitySystemManager.GetEntitySystem <AudioSystem>().Play("/Audio/effects/explosion.ogg", coords); // Knock back cameras of all players in the area. var playerManager = IoCManager.Resolve <IPlayerManager>(); foreach (var player in playerManager.GetAllPlayers()) { if (player.AttachedEntity == null || player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId || !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil)) { continue; } var playerPos = player.AttachedEntity.Transform.WorldPosition; var delta = coords.ToMapPos(mapManager) - playerPos; var distance = delta.LengthSquared; var effect = 1 / (1 + 0.2f * distance); if (effect > 0.01f) { var kick = -delta.Normalized * effect; recoil.Kick(kick); } } }