void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
        {
            if (_entMan.Deleted(Owner))
            {
                return;
            }

            var sourceLocation = eventArgs.Source;
            var targetLocation = _entMan.GetComponent <TransformComponent>(eventArgs.Target).Coordinates;

            if (sourceLocation.Equals(targetLocation))
            {
                return;
            }

            var offset = (targetLocation.ToMapPos(_entMan) - sourceLocation.ToMapPos(_entMan));

            //Don't throw if the direction is center (0,0)
            if (offset == Vector2.Zero)
            {
                return;
            }

            var direction = offset.Normalized;

            var throwForce = eventArgs.Severity switch
            {
                ExplosionSeverity.Heavy => 30,
                ExplosionSeverity.Light => 20,
                _ => 0,
            };

            Owner.TryThrow(direction, throwForce);
        }
예제 #2
0
        public override void Update(float frameTime)
        {
            base.Update(frameTime);

            if (!Started || !Running)
            {
                return;
            }

            _timeUntilLeak -= frameTime;

            if (_timeUntilLeak > 0f)
            {
                return;
            }
            _timeUntilLeak += LeakCooldown;

            var atmosphereSystem = EntitySystem.Get <AtmosphereSystem>();

            if (!_foundTile ||
                _targetGrid == default ||
                _entityManager.Deleted(_targetGrid) ||
                !atmosphereSystem.IsSimulatedGrid(_entityManager.GetComponent <TransformComponent>(_targetGrid).GridID))
            {
                Running = false;
                return;
            }

            var environment = atmosphereSystem.GetTileMixture(_entityManager.GetComponent <TransformComponent>(_targetGrid).GridID, _targetTile, true);

            environment?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond);
        }
        public bool DoHarvest(EntityUid user)
        {
            if (Seed == null || _entMan.Deleted(user) || !EntitySystem.Get <ActionBlockerSystem>().CanInteract(user))
            {
                return(false);
            }

            if (Harvest && !Dead)
            {
                if (_entMan.TryGetComponent(user, out HandsComponent? hands))
                {
                    if (!Seed.CheckHarvest(user, hands.GetActiveHand?.Owner))
                    {
                        return(false);
                    }
                }
                else if (!Seed.CheckHarvest(user))
                {
                    return(false);
                }

                Seed.Harvest(user, YieldMod);
                AfterHarvest();
                return(true);
            }

            if (!Dead)
            {
                return(false);
            }

            RemovePlant();
            AfterHarvest();
            return(true);
        }
        public bool DoHarvest(EntityUid user)
        {
            if (Seed == null || _entMan.Deleted(user))
                return false;

            var botanySystem = EntitySystem.Get<BotanySystem>();

            if (Harvest && !Dead)
            {
                if (_entMan.TryGetComponent(user, out HandsComponent? hands))
                {
                    if (!botanySystem.CanHarvest(Seed, hands.ActiveHandEntity))
                        return false;
                }
                else if (!botanySystem.CanHarvest(Seed))
                {
                    return false;
                }

                botanySystem.Harvest(Seed, user, YieldMod);
                AfterHarvest();
                return true;
            }

            if (!Dead) return false;

            RemovePlant();
            AfterHarvest();
            return true;
        }
예제 #5
0
            protected override void FrameUpdate(FrameEventArgs eventArgs)
            {
                base.FrameUpdate(eventArgs);
                ScreenCoordinates screenCoords;

                if (Entity == null)
                {
                    screenCoords = _eyeManager.CoordinatesToScreen(InitialPos);
                }
                else if (_entityManager.TryGetComponent(Entity.Value, out TransformComponent? xform) &&
                         xform.MapID == _eyeManager.CurrentMap)
                {
                    screenCoords = _eyeManager.CoordinatesToScreen(xform.Coordinates);
                }
                else
                {
                    Visible = false;
                    if (Entity != null && _entityManager.Deleted(Entity))
                    {
                        TotalTime += PopupLifetime;
                    }
                    return;
                }

                Visible = true;
                var position = screenCoords.Position / UIScale - DesiredSize / 2;

                LayoutContainer.SetPosition(this, position - (0, 20 * (TotalTime * TotalTime + TotalTime)));
            }
예제 #6
0
 /// <summary>
 /// Remove the character interface master from this entity now that we have detached ourselves from it
 /// </summary>
 public void DetachPlayerFromEntity(EntityDetachedEventArgs eventArgs)
 {
     // TODO This is shitcode. Move this to an entity system, FOR F**K'S SAKE
     if (!_entityManager.Deleted(eventArgs.OldEntity))
     {
         _entityManager.RemoveComponent <CharacterInterfaceComponent>(eventArgs.OldEntity);
     }
 }
 protected override void OnKill()
 {
     if (_entMan.Deleted(Owner))
     {
         return;
     }
     _entMan.DeleteEntity(Owner);
 }
예제 #8
0
        public void FireEffects(EntityUid user, float distance, Angle angle, EntityUid?hitEntity = null)
        {
            var effectSystem = EntitySystem.Get <EffectSystem>();

            _startTime = _gameTiming.CurTime;
            _deathTime = _startTime + TimeSpan.FromSeconds(1);

            var mapManager = IoCManager.Resolve <IMapManager>();

            // We'll get the effects relative to the grid / map of the firer
            var gridOrMap = _entMan.GetComponent <TransformComponent>(user).GridID == GridId.Invalid ? mapManager.GetMapEntityId(_entMan.GetComponent <TransformComponent>(user).MapID) :
                            mapManager.GetGrid(_entMan.GetComponent <TransformComponent>(user).GridID).GridEntityId;

            var parentXform = _entMan.GetComponent <TransformComponent>(gridOrMap);

            var localCoordinates = new EntityCoordinates(gridOrMap, parentXform.InvWorldMatrix.Transform(_entMan.GetComponent <TransformComponent>(user).WorldPosition));
            var localAngle       = angle - parentXform.WorldRotation;

            var afterEffect = AfterEffects(localCoordinates, localAngle, distance, 1.0f);

            if (afterEffect != null)
            {
                effectSystem.CreateParticle(afterEffect);
            }

            // if we're too close we'll stop the impact and muzzle / impact sprites from clipping
            if (distance > 1.0f)
            {
                var impactEffect = ImpactFlash(distance, localAngle);
                if (impactEffect != null)
                {
                    effectSystem.CreateParticle(impactEffect);
                }

                var muzzleEffect = MuzzleFlash(localCoordinates, localAngle);
                if (muzzleEffect != null)
                {
                    effectSystem.CreateParticle(muzzleEffect);
                }
            }

            if (hitEntity != null && _soundHitWall != null)
            {
                // TODO: No wall component so ?
                var offset      = localAngle.ToVec().Normalized / 2;
                var coordinates = localCoordinates.Offset(offset);
                SoundSystem.Play(Filter.Pvs(coordinates), _soundHitWall.GetSound(), coordinates);
            }

            Owner.SpawnTimer((int)_deathTime.TotalMilliseconds, () =>
            {
                if (!_entMan.Deleted(Owner))
                {
                    _entMan.DeleteEntity(Owner);
                }
            });
        }
예제 #9
0
        protected override void FrameUpdate(FrameEventArgs args)
        {
            base.FrameUpdate(args);

            _timeLeft -= args.DeltaSeconds;
            if (_entityManager.Deleted(_senderEntity) || _timeLeft <= 0)
            {
                // Timer spawn to prevent concurrent modification exception.
                Timer.Spawn(0, Die);
                return;
            }

            // Lerp to our new vertical offset if it's been modified.
            if (MathHelper.CloseToPercent(_verticalOffsetAchieved - VerticalOffset, 0, 0.1))
            {
                _verticalOffsetAchieved = VerticalOffset;
            }
            else
            {
                _verticalOffsetAchieved = MathHelper.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
            }

            if (!_entityManager.TryGetComponent <TransformComponent>(_senderEntity, out var xform) ||
                !xform.Coordinates.IsValid(_entityManager))
            {
                Modulate = Color.White.WithAlpha(0);
                return;
            }

            if (_timeLeft <= FadeTime)
            {
                // Update alpha if we're fading.
                Modulate = Color.White.WithAlpha(_timeLeft / FadeTime);
            }
            else
            {
                // Make opaque otherwise, because it might have been hidden before
                Modulate = Color.White;
            }


            var worldPos    = xform.WorldPosition;
            var scale       = _eyeManager.MainViewport.GetRenderScale();
            var offset      = new Vector2(0, EntityVerticalOffset * EyeManager.PixelsPerMeter * scale);
            var lowerCenter = (_eyeManager.WorldToScreen(worldPos) - offset) / UIScale;

            var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);

            // Round to nearest 0.5
            screenPos = (screenPos * 2).Rounded() / 2;
            LayoutContainer.SetPosition(this, screenPos);

            var height = MathF.Ceiling(MathHelper.Clamp(lowerCenter.Y - screenPos.Y, 0, ContentHeight));

            SetHeight = height;
        }
        public void Cremate()
        {
            if (Open)
            {
                CloseStorage();
            }

            if (_entities.TryGetComponent(Owner, out AppearanceComponent appearanceComponent))
            {
                appearanceComponent.SetData(CrematoriumVisuals.Burning, true);
            }
            Cooking = true;

            SoundSystem.Play(Filter.Pvs(Owner), _crematingSound.GetSound(), Owner);

            _cremateCancelToken?.Cancel();

            _cremateCancelToken = new CancellationTokenSource();
            Owner.SpawnTimer(_burnMilis, () =>
            {
                if (_entities.Deleted(Owner))
                {
                    return;
                }
                if (_entities.TryGetComponent(Owner, out appearanceComponent))
                {
                    appearanceComponent.SetData(CrematoriumVisuals.Burning, false);
                }
                Cooking = false;

                if (Contents.ContainedEntities.Count > 0)
                {
                    for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--)
                    {
                        var item = Contents.ContainedEntities[i];
                        Contents.Remove(item);
                        _entities.DeleteEntity(item);
                    }

                    var ash = _entities.SpawnEntity("Ash", _entities.GetComponent <TransformComponent>(Owner).Coordinates);
                    Contents.Insert(ash);
                }

                TryOpenStorage(Owner);

                SoundSystem.Play(Filter.Pvs(Owner), _cremateFinishSound.GetSound(), Owner);
            }, _cremateCancelToken.Token);
        }
        bool IUse.UseEntity(UseEntityEventArgs eventArgs)
        {
            if (_countDown || (_grenadesContainer.ContainedEntities.Count + _unspawnedCount) <= 0)
            {
                return(false);
            }
            Owner.SpawnTimer((int)(_delay * 1000), () =>
            {
                if (_entMan.Deleted(Owner))
                {
                    return;
                }
                _countDown           = true;
                var random           = IoCManager.Resolve <IRobustRandom>();
                var delay            = 20;
                var grenadesInserted = _grenadesContainer.ContainedEntities.Count + _unspawnedCount;
                var thrownCount      = 0;
                var segmentAngle     = 360 / grenadesInserted;
                while (TryGetGrenade(out var grenade))
                {
                    var angleMin = segmentAngle * thrownCount;
                    var angleMax = segmentAngle * (thrownCount + 1);
                    var angle    = Angle.FromDegrees(random.Next(angleMin, angleMax));
                    // var distance = random.NextFloat() * _throwDistance;

                    delay += random.Next(550, 900);
                    thrownCount++;

                    // TODO: Suss out throw strength
                    grenade.TryThrow(angle.ToVec().Normalized *_throwDistance);

                    grenade.SpawnTimer(delay, () =>
                    {
                        if ((!_entMan.EntityExists(grenade) ? EntityLifeStage.Deleted : _entMan.GetComponent <MetaDataComponent>(grenade).EntityLifeStage) >= EntityLifeStage.Deleted)
                        {
                            return;
                        }

                        EntitySystem.Get <TriggerSystem>().Trigger(grenade, eventArgs.User);
                    });
                }

                _entMan.DeleteEntity(Owner);
            });
예제 #12
0
        protected override void OnKill()
        {
            if (_entMan.Deleted(Owner))
            {
                return;
            }
            if (_entMan.TryGetComponent(Owner, out AppearanceComponent? appearance))
            {
                appearance.SetData(FoamVisuals.State, true);
            }

            Owner.SpawnTimer(600, () =>
            {
                if (!string.IsNullOrEmpty(_foamedMetalPrototype))
                {
                    _entMan.SpawnEntity(_foamedMetalPrototype, _entMan.GetComponent <TransformComponent>(Owner).Coordinates);
                }

                _entMan.QueueDeleteEntity(Owner);
            });
        }
예제 #13
0
        /// <summary>
        ///     Highlight the currently hovered entity.
        /// </summary>
        public override void FrameUpdate(FrameEventArgs e)
        {
            base.FrameUpdate(e);

            // If there is no local player, there is no session, and therefore nothing to do here.
            var localPlayer = PlayerManager.LocalPlayer;

            if (localPlayer == null)
            {
                return;
            }

            // TODO InteractionOutlineComponent
            // BUG: The logic that gets the renderScale here assumes that the entity is only visible in a single
            // viewport. The entity will be highlighted in ALL viewport where it is visible, regardless of which
            // viewport is being used to hover over it. If these Viewports have very different render scales, this may
            // lead to extremely thick outlines in the other viewports. Fixing this probably requires changing how the
            // hover outline works, so that it only highlights the entity in a single viewport.

            EntityUid entityToClick = default;
            var       renderScale   = 1;

            if (UserInterfaceManager.CurrentlyHovered is IViewportControl vp)
            {
                var mousePosWorld = vp.ScreenToMap(InputManager.MouseScreenPosition.Position);
                entityToClick = GetEntityUnderPosition(mousePosWorld);

                if (vp is ScalingViewport svp)
                {
                    renderScale = svp.CurrentRenderScale;
                }
            }
            else if (UserInterfaceManager.CurrentlyHovered is EntityMenuElement element)
            {
                entityToClick = element.Entity;
                // TODO InteractionOutlineComponent
                // Currently we just take the renderscale from the main viewport. In the future, when the bug mentioned
                // above is fixed, the viewport should probably be the one that was clicked on to open the entity menu
                // in the first place.
                renderScale = _eyeManager.MainViewport.GetRenderScale();
            }

            var inRange = false;

            if (localPlayer.ControlledEntity != default && entityToClick != default)
            {
                inRange = localPlayer.InRangeUnobstructed(entityToClick, ignoreInsideBlocker: true);
            }

            InteractionOutlineComponent?outline;

            if (!_outlineEnabled || !ConfigurationManager.GetCVar(CCVars.OutlineEnabled))
            {
                if (entityToClick != default && _entityManager.TryGetComponent(entityToClick, out outline))
                {
                    outline.OnMouseLeave(); //Prevent outline remains from persisting post command.
                }
                return;
            }

            if (entityToClick == _lastHoveredEntity)
            {
                if (entityToClick != default && _entityManager.TryGetComponent(entityToClick, out outline))
                {
                    outline.UpdateInRange(inRange, renderScale);
                }

                return;
            }

            if (_lastHoveredEntity != default && !_entityManager.Deleted(_lastHoveredEntity) &&
                _entityManager.TryGetComponent(_lastHoveredEntity, out outline))
            {
                outline.OnMouseLeave();
            }

            _lastHoveredEntity = entityToClick;

            if (_lastHoveredEntity != default && _entityManager.TryGetComponent(_lastHoveredEntity, out outline))
            {
                outline.OnMouseEnter(inRange, renderScale);
            }
        }
예제 #14
0
        protected override async Task <Queue <TileRef>?> Process()
        {
            if (_startNode == null ||
                _endNode == null ||
                Status == JobStatus.Finished)
            {
                return(null);
            }

            // If we couldn't get a nearby node that's good enough
            if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs))
            {
                return(null);
            }

            if (_entityManager.Deleted(_pathfindingArgs.Start.GridIndex))
            {
                return(null);
            }

            var frontier  = new PriorityQueue <ValueTuple <float, PathfindingNode> >(new PathfindingComparer());
            var costSoFar = new Dictionary <PathfindingNode, float>();
            var cameFrom  = new Dictionary <PathfindingNode, PathfindingNode>();

            PathfindingNode?currentNode = null;

            frontier.Add((0.0f, _startNode));
            costSoFar[_startNode] = 0.0f;
            var routeFound = false;
            var count      = 0;

            while (frontier.Count > 0)
            {
                // Handle whether we need to pause if we've taken too long
                count++;
                if (count % 20 == 0 && count > 0)
                {
                    await SuspendIfOutOfTime();

                    if (_startNode == null || _endNode == null)
                    {
                        return(null);
                    }
                }

                // Actual pathfinding here
                (_, currentNode) = frontier.Take();
                if (currentNode.Equals(_endNode))
                {
                    routeFound = true;
                    break;
                }

                foreach (var nextNode in currentNode.GetNeighbors())
                {
                    // If tile is untraversable it'll be null
                    var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode);
                    if (tileCost == null)
                    {
                        continue;
                    }

                    // So if we're going NE then that means either N or E needs to be free to actually get there
                    var direction = PathfindingHelpers.RelativeDirection(nextNode, currentNode);
                    if (!PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction))
                    {
                        continue;
                    }

                    // f = g + h
                    // gScore is distance to the start node
                    // hScore is distance to the end node
                    var gScore = costSoFar[currentNode] + tileCost.Value;
                    if (costSoFar.TryGetValue(nextNode, out var nextValue) && gScore >= nextValue)
                    {
                        continue;
                    }

                    cameFrom[nextNode]  = currentNode;
                    costSoFar[nextNode] = gScore;
                    // pFactor is tie-breaker where the fscore is otherwise equal.
                    // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties
                    // There's other ways to do it but future consideration
                    // The closer the fScore is to the actual distance then the better the pathfinder will be
                    // (i.e. somewhere between 1 and infinite)
                    // Can use hierarchical pathfinder or whatever to improve the heuristic but this is fine for now.
                    var fScore = gScore + PathfindingHelpers.OctileDistance(_endNode, nextNode) * (1.0f + 1.0f / 1000.0f);
                    frontier.Add((fScore, nextNode));
                }
            }

            if (!routeFound)
            {
                return(null);
            }

            DebugTools.AssertNotNull(currentNode);

            var route = PathfindingHelpers.ReconstructPath(cameFrom, currentNode !);

            if (route.Count == 1)
            {
                return(null);
            }

#if DEBUG
            // Need to get data into an easier format to send to the relevant clients
            if (DebugRoute != null && route.Count > 0)
            {
                var debugCameFrom = new Dictionary <TileRef, TileRef>(cameFrom.Count);
                var debugGScores  = new Dictionary <TileRef, float>(costSoFar.Count);
                foreach (var(node, parent) in cameFrom)
                {
                    debugCameFrom.Add(node.TileRef, parent.TileRef);
                }

                foreach (var(node, score) in costSoFar)
                {
                    debugGScores.Add(node.TileRef, score);
                }

                var debugRoute = new SharedAiDebug.AStarRouteDebug(
                    _pathfindingArgs.Uid,
                    route,
                    debugCameFrom,
                    debugGScores,
                    DebugTime);

                DebugRoute.Invoke(debugRoute);
            }
#endif

            return(route);
        }
        private void MoreFrameUpdate(FrameEventArgs args)
        {
            if (_entities.Deleted(Entity))
            {
                return;
            }

            if (!_entities.TryGetComponent(Entity, out MobStateComponent? mobState) ||
                !_entities.TryGetComponent(Entity, out DamageableComponent? damageable))
            {
                CritBar.Visible   = false;
                HealthBar.Visible = false;
                return;
            }

            FixedPoint2 threshold;

            if (mobState.IsAlive())
            {
                if (!mobState.TryGetEarliestCriticalState(damageable.TotalDamage, out _, out threshold))
                {
                    CritBar.Visible   = false;
                    HealthBar.Visible = false;
                    return;
                }

                CritBar.Ratio     = 1;
                CritBar.Visible   = true;
                HealthBar.Ratio   = 1 - (damageable.TotalDamage / threshold).Float();
                HealthBar.Visible = true;
            }
            else if (mobState.IsCritical())
            {
                HealthBar.Ratio   = 0;
                HealthBar.Visible = false;

                if (!mobState.TryGetPreviousCriticalState(damageable.TotalDamage, out _, out var critThreshold) ||
                    !mobState.TryGetEarliestDeadState(damageable.TotalDamage, out _, out var deadThreshold))
                {
                    CritBar.Visible = false;
                    return;
                }

                CritBar.Visible = true;
                CritBar.Ratio   = 1 -
                                  ((damageable.TotalDamage - critThreshold) /
                                   (deadThreshold - critThreshold)).Float();
            }
            else if (mobState.IsDead())
            {
                CritBar.Ratio     = 0;
                CritBar.Visible   = false;
                HealthBar.Ratio   = 0;
                HealthBar.Visible = true;
            }
            else
            {
                CritBar.Visible   = false;
                HealthBar.Visible = false;
            }
        }