public override void Update(float frameTime) { base.Update(frameTime); if (!Started) { return; } if (_waveCounter <= 0) { Running = false; return; } _cooldown -= frameTime; if (_cooldown > 0f) { return; } _waveCounter--; _cooldown += (MaximumCooldown - MinimumCooldown) * _robustRandom.NextFloat() + MinimumCooldown; Box2?playableArea = null; var mapId = EntitySystem.Get <GameTicker>().DefaultMap; foreach (var grid in _mapManager.GetAllGrids()) { if (grid.ParentMapId != mapId || !_entityManager.TryGetComponent(grid.GridEntityId, out PhysicsComponent? gridBody)) { continue; } var aabb = gridBody.GetWorldAABB(); playableArea = playableArea?.Union(aabb) ?? aabb; } if (playableArea == null) { EndAfter = float.MinValue; return; } var minimumDistance = (playableArea.Value.TopRight - playableArea.Value.Center).Length + 50f; var maximumDistance = minimumDistance + 100f; var center = playableArea.Value.Center; for (var i = 0; i < MeteorsPerWave; i++) { var angle = new Angle(_robustRandom.NextFloat() * MathF.Tau); var offset = angle.RotateVec(new Vector2((maximumDistance - minimumDistance) * _robustRandom.NextFloat() + minimumDistance, 0)); var spawnPosition = new MapCoordinates(center + offset, mapId); var meteor = _entityManager.SpawnEntity("MeteorLarge", spawnPosition); var physics = _entityManager.GetComponent <PhysicsComponent>(meteor); physics.BodyStatus = BodyStatus.InAir; physics.LinearDamping = 0f; physics.AngularDamping = 0f; physics.ApplyLinearImpulse(-offset.Normalized * MeteorVelocity * physics.Mass); physics.ApplyAngularImpulse( // Get a random angular velocity. physics.Mass * ((MaxAngularVelocity - MinAngularVelocity) * _robustRandom.NextFloat() + MinAngularVelocity)); // TODO: God this disgusts me but projectile needs a refactor. IoCManager.Resolve <IEntityManager>().GetComponent <ProjectileComponent>(meteor).TimeLeft = 120f; } }
public EntityUid GetEntityUnderPosition(MapCoordinates coordinates) { var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); return(entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : default);
public static object GetInstance(uint typeId) { object obj = null; switch (typeId) { case 11: obj = new Types.Version(); break; case 25: obj = new GameServerInformations(); break; case 55: obj = new EntityLook(); break; case 54: obj = new SubEntity(); break; case 110: obj = new CharacterMinimalInformations(); break; case 163: obj = new CharacterMinimalPlusLookInformations(); break; case 193: obj = new CharacterMinimalPlusLookAndGradeInformations(); break; case 45: obj = new CharacterBaseInformations(); break; case 212: obj = new CharacterToRecolorInformation(); break; case 86: obj = new CharacterHardcoreInformations(); break; case 63: obj = new EntityMovementInformations(); break; case 60: obj = new EntityDispositionInformations(); break; case 107: obj = new IdentifiedEntityDispositionInformations(); break; case 217: obj = new FightEntityDispositionInformations(); break; case 127: obj = new GuildInformations(); break; case 204: obj = new ActorRestrictionsInformations(); break; case 201: obj = new ActorAlignmentInformations(); break; case 183: obj = new PaddockContentInformations(); break; case 184: obj = new MountInformationsForPaddock(); break; case 202: obj = new ActorExtendedAlignmentInformations(); break; case 135: obj = new AlignmentBonusInformations(); break; case 142: obj = new PrismSubAreaInformation(); break; case 152: obj = new PrismConquestInformation(); break; case 187: obj = new TaxCollectorName(); break; case 96: obj = new TaxCollectorBasicInformations(); break; case 4: obj = new CharacterBaseCharacteristic(); break; case 215: obj = new CharacterSpellModification(); break; case 8: obj = new CharacterCharacteristicsInformations(); break; case 117: obj = new FightExternalInformations(); break; case 43: obj = new FightCommonInformations(); break; case 44: obj = new FightTeamMemberInformations(); break; case 13: obj = new FightTeamMemberCharacterInformations(); break; case 6: obj = new FightTeamMemberMonsterInformations(); break; case 177: obj = new FightTeamMemberTaxCollectorInformations(); break; case 20: obj = new FightOptionsInformations(); break; case 116: obj = new AbstractFightTeamInformations(); break; case 33: obj = new FightTeamInformations(); break; case 115: obj = new FightTeamLightInformations(); break; case 31: obj = new GameFightMinimalStats(); break; case 41: obj = new FightLoot(); break; case 16: obj = new FightResultListEntry(); break; case 189: obj = new FightResultFighterListEntry(); break; case 191: obj = new FightResultAdditionalData(); break; case 192: obj = new FightResultExperienceData(); break; case 190: obj = new FightResultPvpData(); break; case 24: obj = new FightResultPlayerListEntry(); break; case 216: obj = new FightResultMutantListEntry(); break; case 84: obj = new FightResultTaxCollectorListEntry(); break; case 206: obj = new AbstractFightDispellableEffect(); break; case 208: obj = new FightDispellableEffectExtendedInformations(); break; case 209: obj = new FightTemporaryBoostEffect(); break; case 210: obj = new FightTriggeredEffect(); break; case 207: obj = new FightTemporarySpellBoostEffect(); break; case 211: obj = new FightTemporaryBoostWeaponDamagesEffect(); break; case 214: obj = new FightTemporaryBoostStateEffect(); break; case 205: obj = new GameFightSpellCooldown(); break; case 7: obj = new Item(); break; case 49: obj = new SpellItem(); break; case 76: obj = new ObjectEffect(); break; case 74: obj = new ObjectEffectString(); break; case 70: obj = new ObjectEffectInteger(); break; case 82: obj = new ObjectEffectMinMax(); break; case 73: obj = new ObjectEffectDice(); break; case 72: obj = new ObjectEffectDate(); break; case 75: obj = new ObjectEffectDuration(); break; case 71: obj = new ObjectEffectCreature(); break; case 81: obj = new ObjectEffectLadder(); break; case 179: obj = new ObjectEffectMount(); break; case 178: obj = new MountClientData(); break; case 168: obj = new ItemDurability(); break; case 85: obj = new GameActionMarkedCell(); break; case 123: obj = new GoldItem(); break; case 124: obj = new ObjectItemMinimalInformation(); break; case 119: obj = new ObjectItemQuantity(); break; case 134: obj = new ObjectItemNotInContainer(); break; case 37: obj = new ObjectItem(); break; case 120: obj = new ObjectItemToSell(); break; case 164: obj = new ObjectItemToSellInBid(); break; case 198: obj = new ObjectItemInRolePlay(); break; case 197: obj = new ObjectItemWithLookInRolePlay(); break; case 199: obj = new OrientedObjectItemWithLookInRolePlay(); break; case 185: obj = new PaddockItem(); break; case 121: obj = new SellerBuyerDescriptor(); break; case 122: obj = new BidExchangerObjectInfo(); break; case 52: obj = new StartupActionAddObject(); break; case 106: obj = new IgnoredInformations(); break; case 105: obj = new IgnoredOnlineInformations(); break; case 78: obj = new FriendInformations(); break; case 92: obj = new FriendOnlineInformations(); break; case 77: obj = new FriendSpouseInformations(); break; case 93: obj = new FriendSpouseOnlineInformations(); break; case 88: obj = new GuildMember(); break; case 87: obj = new GuildEmblem(); break; case 80: obj = new InteractiveElement(); break; case 108: obj = new StatedElement(); break; case 200: obj = new MapObstacle(); break; case 213: obj = new PartyUpdateCommonsInformations(); break; case 90: obj = new PartyMemberInformations(); break; case 97: obj = new JobCrafterDirectorySettings(); break; case 194: obj = new JobCrafterDirectoryEntryPlayerInfo(); break; case 195: obj = new JobCrafterDirectoryEntryJobInfo(); break; case 196: obj = new JobCrafterDirectoryListEntry(); break; case 101: obj = new JobDescription(); break; case 102: obj = new SkillActionDescription(); break; case 103: obj = new SkillActionDescriptionTimed(); break; case 99: obj = new SkillActionDescriptionCollect(); break; case 100: obj = new SkillActionDescriptionCraft(); break; case 104: obj = new SkillActionDescriptionCraftExtended(); break; case 98: obj = new JobExperience(); break; case 111: obj = new HouseInformations(); break; case 112: obj = new HouseInformationsExtended(); break; case 170: obj = new HouseInformationsForGuild(); break; case 132: obj = new PaddockInformations(); break; case 130: obj = new PaddockBuyableInformations(); break; case 133: obj = new PaddockAbandonnedInformations(); break; case 131: obj = new PaddockPrivateInformations(); break; case 150: obj = new GameContextActorInformations(); break; case 141: obj = new GameRolePlayActorInformations(); break; case 157: obj = new HumanInformations(); break; case 153: obj = new HumanWithGuildInformations(); break; case 154: obj = new GameRolePlayNamedActorInformations(); break; case 159: obj = new GameRolePlayHumanoidInformations(); break; case 36: obj = new GameRolePlayCharacterInformations(); break; case 3: obj = new GameRolePlayMutantInformations(); break; case 129: obj = new GameRolePlayMerchantInformations(); break; case 146: obj = new GameRolePlayMerchantWithGuildInformations(); break; case 180: obj = new GameRolePlayMountInformations(); break; case 147: obj = new TaxCollectorStaticInformations(); break; case 148: obj = new GameRolePlayTaxCollectorInformations(); break; case 167: obj = new TaxCollectorInformations(); break; case 166: obj = new TaxCollectorInformationsInWaitForHelpState(); break; case 186: obj = new ProtectedEntityWaitingForHelpInfo(); break; case 169: obj = new TaxCollectorFightersInformation(); break; case 165: obj = new AdditionalTaxCollectorInformations(); break; case 144: obj = new MonsterInGroupInformations(); break; case 140: obj = new GroupMonsterStaticInformations(); break; case 160: obj = new GameRolePlayGroupMonsterInformations(); break; case 155: obj = new NpcStaticInformations(); break; case 156: obj = new GameRolePlayNpcInformations(); break; case 161: obj = new GameRolePlayPrismInformations(); break; case 143: obj = new GameFightFighterInformations(); break; case 158: obj = new GameFightFighterNamedInformations(); break; case 46: obj = new GameFightCharacterInformations(); break; case 50: obj = new GameFightMutantInformations(); break; case 151: obj = new GameFightAIInformations(); break; case 29: obj = new GameFightMonsterInformations(); break; case 203: obj = new GameFightMonsterWithAlignmentInformations(); break; case 48: obj = new GameFightTaxCollectorInformations(); break; case 174: obj = new MapCoordinates(); break; case 176: obj = new MapCoordinatesExtended(); break; case 175: obj = new AtlasPointsInformations(); break; default: throw new Exception("Type with id " + typeId + " is unknown."); } return(obj); }
public abstract IEntity TakeProjectile(GridCoordinates spawnAtGrid, MapCoordinates spawnAtMap);
/// <summary> /// Checks that these coordinates are within a certain distance without any /// entity that matches the collision mask obstructing them. /// If the <paramref name="range"/> is zero or negative, /// this method will only check if nothing obstructs the two sets of coordinates.. /// </summary> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="range">maximum distance between the two sets of coordinates.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="ignoredEnt">the entity to be ignored when checking for collisions.</param> /// <param name="insideBlockerValid">if coordinates inside obstructions count as obstructed or not</param> /// <returns>True if the two points are within a given range without being obstructed.</returns> public bool InRangeUnobstructed(MapCoordinates coords, MapCoordinates otherCoords, float range = InteractionRange, int collisionMask = (int)CollisionGroup.Impassable, IEntity ignoredEnt = null, bool insideBlockerValid = false) => InRangeUnobstructed(coords, otherCoords, range, collisionMask, ignoredEnt == null ? null : (Func <IEntity, bool>)(entity => ignoredEnt == entity), insideBlockerValid);
public async Task Test() { var server = StartServerDummyTicker(new ServerContentIntegrationOption { ExtraPrototypes = Prototypes, ContentBeforeIoC = () => { IoCManager.Resolve <IComponentFactory>().RegisterClass <TestThresholdListenerComponent>(); } }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency <IEntityManager>(); var sMapManager = server.ResolveDependency <IMapManager>(); IEntity sDestructibleEntity; IDamageableComponent sDamageableComponent = null; DestructibleComponent sDestructibleComponent = null; TestThresholdListenerComponent sThresholdListenerComponent = null; await server.WaitPost(() => { var mapId = new MapId(1); var coordinates = new MapCoordinates(0, 0, mapId); sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleEntityId, coordinates); sDamageableComponent = sDestructibleEntity.GetComponent <IDamageableComponent>(); sDestructibleComponent = sDestructibleEntity.GetComponent <DestructibleComponent>(); sThresholdListenerComponent = sDestructibleEntity.GetComponent <TestThresholdListenerComponent>(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); }); await server.WaitAssertion(() => { Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // No thresholds reached yet, the earliest one is at 20 damage Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // Only one threshold reached, 20 Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 20 var msg = sThresholdListenerComponent.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sThresholdListenerComponent.ThresholdsReached.Clear(); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 30, true)); // One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 50 msg = sThresholdListenerComponent.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); var soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; var spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; var actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound, Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sThresholdListenerComponent.ThresholdsReached.Clear(); // Damage for 50 again, up to 100 now Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true)); // No thresholds reached as they weren't healed below the trigger amount Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Heal down to 0 sDamageableComponent.Heal(); // Damage for 100, up to 100 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 100, true)); // Two thresholds reached as damage increased past the previous, 20 and 50 Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2)); sThresholdListenerComponent.ThresholdsReached.Clear(); // Heal the entity for 40 damage, down to 60 sDamageableComponent.ChangeDamage(DamageType.Blunt, -40, true); // Thresholds don't work backwards Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); // Damage for 10, up to 70 sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true); // Not enough healing to de-trigger a threshold Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); // Heal by 30, down to 40 sDamageableComponent.ChangeDamage(DamageType.Blunt, -30, true); // Thresholds don't work backwards Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); // Damage up to 50 again sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true); // The 50 threshold should have triggered again, after being healed Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); msg = sThresholdListenerComponent.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; // Check that it matches the YAML prototype Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound, Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached sThresholdListenerComponent.ThresholdsReached.Clear(); // Heal all damage sDamageableComponent.Heal(); // Damage up to 50 sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // Both thresholds should have triggered Assert.That(sThresholdListenerComponent.ThresholdsReached, Has.Exactly(2).Items); // Verify the first one, should be the lowest one (20) msg = sThresholdListenerComponent.ThresholdsReached[0]; var trigger = (DamageTrigger)msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(20)); threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); // Verify the second one, should be the highest one (50) msg = sThresholdListenerComponent.ThresholdsReached[1]; trigger = (DamageTrigger)msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(50)); threshold = msg.Threshold; Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; // Check that it matches the YAML prototype Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound, Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached sThresholdListenerComponent.ThresholdsReached.Clear(); // Heal the entity completely sDamageableComponent.Heal(); // Check that the entity has 0 damage Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); // Set both thresholds to only trigger once foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = true; } // Damage the entity up to 50 damage again sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // No thresholds should have triggered as they were already triggered before, and they are set to only trigger once Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); // Set both thresholds to trigger multiple times foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = false; } // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // They shouldn't have been triggered by changing TriggersOnce Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); }); }
public CompassUpdateMessage(sbyte type, MapCoordinates coords) { this.type = type; this.coords = coords; }
private EntityUid GetEntityUnderMouse(ScalingViewport viewport, Vector2 mousePos, MapCoordinates coords) { EntityUid?entity = GetEntityOnScreen(mousePos, viewport); entity ??= GetEntityOnMap(coords); return(entity ?? EntityUid.Invalid); }
/// <inheritdoc /> public abstract IEntity CreateEntityUninitialized(string?prototypeName, MapCoordinates coordinates);
public SpawnPointGenerator(MapCoordinates mapCoordinates, float offset) { _mapCoordinates = mapCoordinates; _offset = offset; }
public GridDatum(List <ChunkDatum> chunkData, MapCoordinates coordinates) { ChunkData = chunkData; Coordinates = coordinates; }
public IEntity SpawnEntity(string?protoId, MapCoordinates coordinates) { var entMan = Collection.Resolve <IEntityManager>(); return(entMan.SpawnEntity(protoId, coordinates)); }
public TabletopMoveEvent(EntityUid movedEntityUid, MapCoordinates coordinates, EntityUid tableUid) { MovedEntityUid = movedEntityUid; Coordinates = coordinates; TableUid = tableUid; }
public GridDatum(ChunkDatum[] chunkData, MapCoordinates coordinates) { ChunkData = chunkData; Coordinates = coordinates; }
public static bool InRangeUnOccluded(IEntity origin, MapCoordinates other, float range, Ignored?predicate, bool ignoreInsideBlocker = true) { var originPos = origin.Transform.MapPosition; return(InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker)); }
/// <inheritdoc /> public abstract IEntity SpawnEntity(string?protoName, MapCoordinates coordinates);
public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, Ignored?predicate, bool ignoreInsideBlocker = true) { if (origin.MapId == MapId.Nullspace || other.MapId == MapId.Nullspace) { return(false); } var occluderSystem = Get <OccluderSystem>(); if (!origin.InRange(other, range)) { return(false); } var dir = other.Position - origin.Position; if (dir.LengthSquared.Equals(0f)) { return(true); } if (range > 0f && !(dir.LengthSquared <= range * range)) { return(false); } predicate ??= _ => false; var ray = new Ray(origin.Position, dir.Normalized); var rayResults = occluderSystem .IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList(); if (rayResults.Count == 0) { return(true); } if (!ignoreInsideBlocker) { return(false); } foreach (var result in rayResults) { if (!result.HitEntity.TryGetComponent(out OccluderComponent? o)) { continue; } var bBox = o.BoundingBox.Translated(o.Owner.Transform.WorldPosition); if (bBox.Contains(origin.Position) || bBox.Contains(other.Position)) { continue; } return(false); } return(true); }
/// <inheritdoc /> public IEnumerable <IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false) { return(GetEntitiesIntersecting(position.MapId, position.Position, approximate)); }
public async Task Test() { var server = StartServerDummyTicker(new ServerContentIntegrationOption { ExtraPrototypes = Prototypes, ContentBeforeIoC = () => { IoCManager.Resolve <IComponentFactory>().RegisterClass <TestThresholdListenerComponent>(); } }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency <IEntityManager>(); var sMapManager = server.ResolveDependency <IMapManager>(); IEntity sDestructibleEntity; IDamageableComponent sDamageableComponent = null; TestThresholdListenerComponent sThresholdListenerComponent = null; await server.WaitPost(() => { var mapId = new MapId(1); var coordinates = new MapCoordinates(0, 0, mapId); sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageTypeEntityId, coordinates); sDamageableComponent = sDestructibleEntity.GetComponent <IDamageableComponent>(); sThresholdListenerComponent = sDestructibleEntity.GetComponent <TestThresholdListenerComponent>(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); }); await server.WaitAssertion(() => { // Raise blunt damage to 5 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true)); // No thresholds reached yet, the earliest one is at 10 damage Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise blunt damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true)); // No threshold reached, slash needs to be 10 as well Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise slash damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); // One threshold reached, blunt 10 + slash 10 Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold blunt 10 + slash 10 var msg = sThresholdListenerComponent.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); Assert.IsInstanceOf <AndTrigger>(threshold.Trigger); var trigger = (AndTrigger)threshold.Trigger; Assert.IsInstanceOf <DamageTypeTrigger>(trigger.Triggers[0]); Assert.IsInstanceOf <DamageTypeTrigger>(trigger.Triggers[1]); sThresholdListenerComponent.ThresholdsReached.Clear(); // Raise blunt damage to 20 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // No new thresholds reached Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise slash damage to 20 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); // No new thresholds reached Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Lower blunt damage to 0 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -20, true)); // No new thresholds reached, healing should not trigger it Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise blunt damage back up to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); sThresholdListenerComponent.ThresholdsReached.Clear(); // Heal both types of damage to 0 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true)); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -20, true)); // No new thresholds reached, healing should not trigger it Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise blunt damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // No new thresholds reached Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise slash damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); // Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold blunt 10 + slash 10 msg = sThresholdListenerComponent.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); Assert.IsInstanceOf <AndTrigger>(threshold.Trigger); trigger = (AndTrigger)threshold.Trigger; Assert.IsInstanceOf <DamageTypeTrigger>(trigger.Triggers[0]); Assert.IsInstanceOf <DamageTypeTrigger>(trigger.Triggers[1]); sThresholdListenerComponent.ThresholdsReached.Clear(); // Change triggers once to true threshold.TriggersOnce = true; // Heal blunt and slash back to 0 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true)); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -10, true)); // No new thresholds reached from healing Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise blunt damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // No new thresholds reached Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); // Raise slash damage to 10 Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); // No new thresholds reached as triggers once is set to true and it already triggered before Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); }); }
public static bool InRangeUnOccluded <TState>(MapCoordinates origin, MapCoordinates other, float range, TState state, Func <EntityUid, TState, bool> predicate, bool ignoreInsideBlocker = true, IEntityManager?entMan = null) { if (other.MapId != origin.MapId || other.MapId == MapId.Nullspace) { return(false); } var dir = other.Position - origin.Position; var length = dir.Length; // If range specified also check it if (range > 0f && length > range) { return(false); } if (MathHelper.CloseTo(length, 0)) { return(true); } if (length > MaxRaycastRange) { Logger.Warning("InRangeUnOccluded check performed over extreme range. Limiting CollisionRay size."); length = MaxRaycastRange; } var occluderSystem = Get <OccluderSystem>(); IoCManager.Resolve(ref entMan); var ray = new Ray(origin.Position, dir.Normalized); var rayResults = occluderSystem .IntersectRayWithPredicate(origin.MapId, ray, length, state, predicate, false).ToList(); if (rayResults.Count == 0) { return(true); } if (!ignoreInsideBlocker) { return(false); } foreach (var result in rayResults) { if (!entMan.TryGetComponent(result.HitEntity, out OccluderComponent? o)) { continue; } var bBox = o.BoundingBox.Translated(entMan.GetComponent <TransformComponent>(o.Owner).WorldPosition); if (bBox.Contains(origin.Position) || bBox.Contains(other.Position)) { continue; } return(false); } return(true); }
public override void Deserialize(IDataReader reader) { this.type = reader.ReadSByte(); this.coords = ProtocolTypeManager.GetInstance <MapCoordinates>(reader.ReadUShort()); this.coords.Deserialize(reader); }
/// <summary> /// This is the main explosion generating function. /// </summary> /// <param name="epicenter">The center of the explosion</param> /// <param name="typeID">The explosion type. this determines the explosion damage</param> /// <param name="totalIntensity">The final sum of the tile intensities. This governs the overall size of the /// explosion</param> /// <param name="slope">How quickly does the intensity decrease when moving away from the epicenter.</param> /// <param name="maxIntensity">The maximum intensity that the explosion can have at any given tile. This /// effectively caps the damage that this explosion can do.</param> /// <returns>A list of tile-sets and a list of intensity values which describe the explosion.</returns> private (int, List <float>, ExplosionSpaceTileFlood?, Dictionary <EntityUid, ExplosionGridTileFlood>, Matrix3)? GetExplosionTiles( MapCoordinates epicenter, string typeID, float totalIntensity, float slope, float maxIntensity) { if (totalIntensity <= 0 || slope <= 0) { return(null); } if (!_explosionTypes.TryGetValue(typeID, out var typeIndex)) { Logger.Error("Attempted to spawn explosion using a prototype that was not defined during initialization. Explosion prototype hot-reload is not currently supported."); return(null); } Vector2i initialTile; EntityUid?epicentreGrid = null; var(localGrids, referenceGrid, maxDistance) = GetLocalGrids(epicenter, totalIntensity, slope, maxIntensity); // get the epicenter tile indices if (_mapManager.TryFindGridAt(epicenter, out var candidateGrid) && candidateGrid.TryGetTileRef(candidateGrid.WorldToTile(epicenter.Position), out var tileRef) && !tileRef.Tile.IsEmpty) { epicentreGrid = candidateGrid.GridEntityId; initialTile = tileRef.GridIndices; } else if (referenceGrid != null) { // reference grid defines coordinate system that the explosion in space will use initialTile = _mapManager.GetGrid(referenceGrid.Value).WorldToTile(epicenter.Position); } else { // this is a space-based explosion that (should) not touch any grids. initialTile = new Vector2i( (int)Math.Floor(epicenter.Position.X / DefaultTileSize), (int)Math.Floor(epicenter.Position.Y / DefaultTileSize)); } // Main data for the exploding tiles in space and on various grids Dictionary <EntityUid, ExplosionGridTileFlood> gridData = new(); ExplosionSpaceTileFlood?spaceData = null; // The intensity slope is how much the intensity drop over a one-tile distance. The actual algorithm step-size is half of thhat. var stepSize = slope / 2; // Hashsets used for when grid-based explosion propagate into space. Basically: used to move data between // `gridData` and `spaceData` in-between neighbor finding iterations. HashSet <Vector2i> spaceJump = new(); HashSet <Vector2i> previousSpaceJump; // As above, but for space-based explosion propagating from space onto grids. HashSet <EntityUid> encounteredGrids = new(); Dictionary <EntityUid, HashSet <Vector2i> >?previousGridJump; // variables for transforming between grid and space-coordiantes var spaceMatrix = Matrix3.Identity; var spaceAngle = Angle.Zero; if (referenceGrid != null) { var xform = Transform(_mapManager.GetGrid(referenceGrid.Value).GridEntityId); spaceMatrix = xform.WorldMatrix; spaceAngle = xform.WorldRotation; } // is the explosion starting on a grid? if (epicentreGrid != null) { // set up the initial `gridData` instance encounteredGrids.Add(epicentreGrid.Value); if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap)) { airtightMap = new(); } var initialGridData = new ExplosionGridTileFlood( _mapManager.GetGrid(epicentreGrid.Value), airtightMap, maxIntensity, stepSize, typeIndex, _gridEdges[epicentreGrid.Value], referenceGrid, spaceMatrix, spaceAngle); gridData[epicentreGrid.Value] = initialGridData; initialGridData.InitTile(initialTile); } else { // set up the space explosion data spaceData = new ExplosionSpaceTileFlood(this, epicenter, referenceGrid, localGrids, maxDistance); spaceData.InitTile(initialTile); } // Is this even a multi-tile explosion? if (totalIntensity < stepSize) { // Bit anticlimactic. All that set up for nothing.... return(1, new List <float> { totalIntensity }, spaceData, gridData, spaceMatrix); } // These variables keep track of the total intensity we have distributed List <int> tilesInIteration = new() { 1 }; List <float> iterationIntensity = new() { stepSize }; var totalTiles = 1; var remainingIntensity = totalIntensity - stepSize; var iteration = 1; var maxIntensityIndex = 0; // If an explosion is trapped in an indestructible room, we can end the neighbor finding steps early. // These variables are used to check if we can abort early. float previousIntensity; var intensityUnchangedLastLoop = false; // Main flood-fill / neighbor-finding loop while (remainingIntensity > 0 && iteration <= MaxIterations && totalTiles < MaxArea) { previousIntensity = remainingIntensity; // First, we increase the intensity of the tiles that were already discovered in previous iterations. for (var i = maxIntensityIndex; i < iteration; i++) { var intensityIncrease = MathF.Min(stepSize, maxIntensity - iterationIntensity[i]); if (tilesInIteration[i] * intensityIncrease >= remainingIntensity) { // there is not enough intensity left to distribute. add a fractional amount and break. iterationIntensity[i] += (float)remainingIntensity / tilesInIteration[i]; remainingIntensity = 0; break; } iterationIntensity[i] += intensityIncrease; remainingIntensity -= tilesInIteration[i] * intensityIncrease; // Has this tile-set has reached max intensity? If so, stop iterating over it in future if (intensityIncrease < stepSize) { maxIntensityIndex++; } } if (remainingIntensity <= 0) { break; } // Next, we will add a new iteration of tiles // In order to treat "cost" of moving off a grid on the same level as moving onto a grid, both space -> grid and grid -> space have to be delayed by one iteration. previousSpaceJump = spaceJump; previousGridJump = spaceData?.GridJump; spaceJump = new(); var newTileCount = 0; if (previousGridJump != null) { encounteredGrids.UnionWith(previousGridJump.Keys); } foreach (var grid in encounteredGrids) { // is this a new grid, for which we must create a new explosion data set if (!gridData.TryGetValue(grid, out var data)) { if (!_airtightMap.TryGetValue(grid, out var airtightMap)) { airtightMap = new(); } data = new ExplosionGridTileFlood( _mapManager.GetGrid(grid), airtightMap, maxIntensity, stepSize, typeIndex, _gridEdges[grid], referenceGrid, spaceMatrix, spaceAngle); gridData[grid] = data; } // get the new neighbours, and populate gridToSpaceTiles in the process. newTileCount += data.AddNewTiles(iteration, previousGridJump?.GetValueOrDefault(grid)); spaceJump.UnionWith(data.SpaceJump); } // if space-data is null, but some grid-based explosion reached space, we need to initialize it. if (spaceData == null && previousSpaceJump.Count != 0) { spaceData = new ExplosionSpaceTileFlood(this, epicenter, referenceGrid, localGrids, maxDistance); } // If the explosion has reached space, do that neighbors finding step as well. if (spaceData != null) { newTileCount += spaceData.AddNewTiles(iteration, previousSpaceJump); } // Does adding these tiles bring us above the total target intensity? tilesInIteration.Add(newTileCount); if (newTileCount * stepSize >= remainingIntensity) { iterationIntensity.Add((float)remainingIntensity / newTileCount); break; } // add the new tiles and decrement available intensity remainingIntensity -= newTileCount * stepSize; iterationIntensity.Add(stepSize); totalTiles += newTileCount; // It is possible that the explosion has some max intensity and is stuck in a container whose walls it // cannot break. if the remaining intensity remains unchanged TWO loops in a row, we know that this is the // case. if (intensityUnchangedLastLoop && remainingIntensity == previousIntensity) { break; } intensityUnchangedLastLoop = remainingIntensity == previousIntensity; iteration += 1; } // Neighbor finding is done. Perform final clean up and return. foreach (var grid in gridData.Values) { grid.CleanUp(); } spaceData?.CleanUp(); return(totalTiles, iterationIntensity, spaceData, gridData, spaceMatrix); }
public async Task InsideContainerInteractionBlockTest() { var server = StartServerDummyTicker(new ServerContentIntegrationOption { ContentBeforeIoC = () => { IoCManager.Resolve <IEntitySystemManager>().LoadExtraSystemType <TestAttackEntitySystem>(); } }); await server.WaitIdleAsync(); var entityManager = server.ResolveDependency <IEntityManager>(); var mapManager = server.ResolveDependency <IMapManager>(); IEntity origin = null; IEntity other = null; IEntity containerEntity = null; IContainer container = null; server.Assert(() => { var mapId = mapManager.CreateMap(); var coordinates = new MapCoordinates(Vector2.Zero, mapId); origin = entityManager.SpawnEntity(null, coordinates); origin.EnsureComponent <HandsComponent>(); other = entityManager.SpawnEntity(null, coordinates); containerEntity = entityManager.SpawnEntity(null, coordinates); container = ContainerHelpers.EnsureContainer <Container>(containerEntity, "InteractionTestContainer"); }); await server.WaitIdleAsync(); var attack = false; var interactUsing = false; var interactHand = false; server.Assert(() => { Assert.That(container.Insert(origin)); Assert.That(origin.Transform.Parent !.Owner, Is.EqualTo(containerEntity)); var entitySystemManager = IoCManager.Resolve <IEntitySystemManager>(); Assert.That(entitySystemManager.TryGetEntitySystem <InteractionSystem>(out var interactionSystem)); Assert.That(entitySystemManager.TryGetEntitySystem <TestAttackEntitySystem>(out var testAttackEntitySystem)); testAttackEntitySystem.AttackEvent = (ev) => { Assert.That(ev.Target, Is.EqualTo(containerEntity.Uid)); attack = true; }; testAttackEntitySystem.InteractUsingEvent = (ev) => { Assert.That(ev.Attacked, Is.EqualTo(containerEntity)); interactUsing = true; }; testAttackEntitySystem.InteractHandEvent = (ev) => { Assert.That(ev.Attacked, Is.EqualTo(containerEntity)); interactHand = true; }; interactionSystem.DoAttack(origin, other.Transform.Coordinates, false, other.Uid); interactionSystem.UserInteraction(origin, other.Transform.Coordinates, other.Uid); Assert.That(attack, Is.False); Assert.That(interactUsing, Is.False); Assert.That(interactHand, Is.False); interactionSystem.DoAttack(origin, containerEntity.Transform.Coordinates, false, containerEntity.Uid); interactionSystem.UserInteraction(origin, containerEntity.Transform.Coordinates, containerEntity.Uid); Assert.That(attack); Assert.That(interactUsing, Is.False); Assert.That(interactHand); var itemEntity = entityManager.SpawnEntity(null, origin.Transform.Coordinates); var item = itemEntity.EnsureComponent <ItemComponent>(); Assert.That(origin.TryGetComponent <HandsComponent>(out var hands)); hands.PutInHand(item); interactionSystem.UserInteraction(origin, other.Transform.Coordinates, other.Uid); Assert.That(interactUsing, Is.False); interactionSystem.UserInteraction(origin, containerEntity.Transform.Coordinates, containerEntity.Uid); Assert.That(interactUsing); }); }
private void SpawnPieces(TabletopSession session, IEntityManager entityManager, MapCoordinates topLeft, float separation = 1f) { var(mapId, x, y) = topLeft; // Spawn all black pieces SpawnPieces(session, entityManager, "Black", topLeft, separation); // Spawn all white pieces SpawnPieces(session, entityManager, "White", new MapCoordinates(x, y - 7 * separation, mapId), separation); // Queens for (int i = 1; i < 4; i++) { EntityUid tempQualifier = entityManager.SpawnEntity("BlackCheckerQueen", new MapCoordinates(x + 9 * separation + 9f / 32, y - i * separation, mapId)); session.Entities.Add(tempQualifier); EntityUid tempQualifier1 = entityManager.SpawnEntity("WhiteCheckerQueen", new MapCoordinates(x + 8 * separation + 9f / 32, y - i * separation, mapId)); session.Entities.Add(tempQualifier1); } }
/// <summary> /// Traces a ray from coords to otherCoords and returns the length /// of the vector between coords and the ray's first hit. /// </summary> /// <param name="coords">Set of coordinates to use.</param> /// <param name="otherCoords">Other set of coordinates to use.</param> /// <param name="collisionMask">the mask to check for collisions</param> /// <param name="ignoredEnt">the entity to be ignored when checking for collisions.</param> /// <returns>Length of resulting ray.</returns> public float UnobstructedRayLength(MapCoordinates coords, MapCoordinates otherCoords, int collisionMask = (int)CollisionGroup.Impassable, IEntity ignoredEnt = null) => UnobstructedRayLength(coords, otherCoords, collisionMask, ignoredEnt == null ? null : (Func <IEntity, bool>)(entity => ignoredEnt == entity));
private void SpawnPieces(TabletopSession session, IEntityManager entityManager, string color, MapCoordinates left, float separation = 1f) { var(mapId, x, y) = left; // If white is being placed it must go from bottom->up var reversed = (color == "White") ? 1 : -1; for (int i = 0; i < 3; i++) { var x_offset = i % 2; if (reversed == -1) { x_offset = 1 - x_offset; // Flips it } for (int j = 0; j < 8; j += 2) { // Prevents an extra piece on the middle row if (x_offset + j > 8) { continue; } EntityUid tempQualifier4 = entityManager.SpawnEntity(color + "CheckerPiece", new MapCoordinates(x + (j + x_offset) * separation, y + i * reversed * separation, mapId)); session.Entities.Add(tempQualifier4); } } }
public async Task TestDamageClassDamageAndHeal() { var server = StartServerDummyTicker(new ServerContentIntegrationOption { ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency <IEntityManager>(); var sMapManager = server.ResolveDependency <IMapManager>(); IEntity sDamageableEntity; IDamageableComponent sDamageableComponent = null; await server.WaitPost(() => { var mapId = sMapManager.NextMapId(); var coordinates = new MapCoordinates(0, 0, mapId); sMapManager.CreateMap(mapId); sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates); sDamageableComponent = sDamageableEntity.GetComponent <IDamageableComponent>(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); foreach (var @class in Enum.GetValues <DamageClass>()) { Assert.That(sDamageableComponent.SupportsDamageClass(@class)); var types = @class.ToTypes(); foreach (var type in types) { Assert.That(sDamageableComponent.SupportsDamageType(type)); } var damageToDeal = types.Count * 5; // Damage Assert.That(sDamageableComponent.ChangeDamage(@class, damageToDeal, true), Is.True); Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); Assert.That(sDamageableComponent.TryGetDamage(@class, out var classDamage), Is.True); Assert.That(classDamage, Is.EqualTo(damageToDeal)); foreach (var type in types) { Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True); Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count)); } // Heal Assert.That(sDamageableComponent.ChangeDamage(@class, -damageToDeal, true), Is.True); Assert.That(sDamageableComponent.TotalDamage, Is.Zero); Assert.That(sDamageableComponent.TryGetDamage(@class, out classDamage), Is.True); Assert.That(classDamage, Is.Zero); foreach (var type in types) { Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True); Assert.That(typeDamage, Is.Zero); } } }); }
public IEntity GetEntityUnderPosition(MapCoordinates coordinates) { var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); return(entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null); }
/// <summary> /// Get all of the entities in an area for displaying on the context menu. /// </summary> public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true)] out List <EntityUid>?result) { result = null; if (_stateManager.CurrentState is not GameScreenBase gameScreenBase) { return(false); } var player = _playerManager.LocalPlayer?.ControlledEntity; if (player == null) { return(false); } // If FOV drawing is disabled, we will modify the visibility option to ignore visiblity checks. var visibility = _eyeManager.CurrentEye.DrawFov ? Visibility : Visibility | MenuVisibility.NoFov; // Do we have to do FoV checks? if ((visibility & MenuVisibility.NoFov) == 0) { var entitiesUnderMouse = gameScreenBase.GetEntitiesUnderPosition(targetPos); bool Predicate(EntityUid e) => e == player || entitiesUnderMouse.Contains(e); if (!_examineSystem.CanExamine(player.Value, targetPos, Predicate)) { return(false); } } // Get entities var entities = _entityLookup.GetEntitiesInRange(targetPos.MapId, targetPos.Position, EntityMenuLookupSize) .ToList(); if (entities.Count == 0) { return(false); } if (visibility == MenuVisibility.All) { result = entities; return(true); } // remove any entities in containers if ((visibility & MenuVisibility.InContainer) == 0) { foreach (var entity in entities.ToList()) { if (!player.Value.IsInSameOrTransparentContainer(entity)) { entities.Remove(entity); } } } // remove any invisible entities if ((visibility & MenuVisibility.Invisible) == 0) { foreach (var entity in entities.ToList()) { if (!EntityManager.TryGetComponent(entity, out ISpriteComponent? spriteComponent) || !spriteComponent.Visible) { entities.Remove(entity); continue; } if (entity.HasTag("HideContextMenu")) { entities.Remove(entity); } } } // Remove any entities that do not have LOS if ((visibility & MenuVisibility.NoFov) == 0) { var playerPos = EntityManager.GetComponent <TransformComponent>(player.Value).MapPosition; foreach (var entity in entities.ToList()) { if (!ExamineSystemShared.InRangeUnOccluded( playerPos, EntityManager.GetComponent <TransformComponent>(entity).MapPosition, ExamineSystemShared.ExamineRange, null)) { entities.Remove(entity); } } } if (entities.Count == 0) { return(false); } result = entities; return(true); }
/// <summary> /// Checks whether a tile's gas mixture is probably safe. /// This only checks temperature and pressure, not gas composition. /// </summary> /// <param name="coordinates">Coordinates where to get the tile.</param> /// <returns>Whether the tile's gas mixture is probably safe.</returns> public bool IsTileMixtureProbablySafe(MapCoordinates coordinates) { return(IsMixtureProbablySafe(GetTileMixture(coordinates))); }