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); }
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; }
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))); }
/// <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); }
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); } }); }
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); });
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); }); }
/// <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); } }
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; } }