コード例 #1
0
        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;
            }
        }
コード例 #2
0
        public EntityUid GetEntityUnderPosition(MapCoordinates coordinates)
        {
            var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates);

            return(entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : default);
コード例 #3
0
        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);
        }
コード例 #4
0
 public abstract IEntity TakeProjectile(GridCoordinates spawnAtGrid, MapCoordinates spawnAtMap);
コード例 #5
0
 /// <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);
            });
        }
コード例 #7
0
 public CompassUpdateMessage(sbyte type, MapCoordinates coords)
 {
     this.type   = type;
     this.coords = coords;
 }
コード例 #8
0
        private EntityUid GetEntityUnderMouse(ScalingViewport viewport, Vector2 mousePos, MapCoordinates coords)
        {
            EntityUid?entity = GetEntityOnScreen(mousePos, viewport);

            entity ??= GetEntityOnMap(coords);

            return(entity ?? EntityUid.Invalid);
        }
コード例 #9
0
 /// <inheritdoc />
 public abstract IEntity CreateEntityUninitialized(string?prototypeName, MapCoordinates coordinates);
コード例 #10
0
 public SpawnPointGenerator(MapCoordinates mapCoordinates, float offset)
 {
     _mapCoordinates = mapCoordinates;
     _offset         = offset;
 }
コード例 #11
0
 public GridDatum(List <ChunkDatum> chunkData, MapCoordinates coordinates)
 {
     ChunkData   = chunkData;
     Coordinates = coordinates;
 }
コード例 #12
0
        public IEntity SpawnEntity(string?protoId, MapCoordinates coordinates)
        {
            var entMan = Collection.Resolve <IEntityManager>();

            return(entMan.SpawnEntity(protoId, coordinates));
        }
コード例 #13
0
 public TabletopMoveEvent(EntityUid movedEntityUid, MapCoordinates coordinates, EntityUid tableUid)
 {
     MovedEntityUid = movedEntityUid;
     Coordinates    = coordinates;
     TableUid       = tableUid;
 }
コード例 #14
0
 public GridDatum(ChunkDatum[] chunkData, MapCoordinates coordinates)
 {
     ChunkData   = chunkData;
     Coordinates = coordinates;
 }
コード例 #15
0
        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));
        }
コード例 #16
0
 /// <inheritdoc />
 public abstract IEntity SpawnEntity(string?protoName, MapCoordinates coordinates);
コード例 #17
0
        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);
        }
コード例 #18
0
 /// <inheritdoc />
 public IEnumerable <IEntity> GetEntitiesIntersecting(MapCoordinates position, bool approximate = false)
 {
     return(GetEntitiesIntersecting(position.MapId, position.Position, approximate));
 }
コード例 #19
0
        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);
            });
        }
コード例 #20
0
        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);
        }
コード例 #21
0
 public override void Deserialize(IDataReader reader)
 {
     this.type   = reader.ReadSByte();
     this.coords = ProtocolTypeManager.GetInstance <MapCoordinates>(reader.ReadUShort());
     this.coords.Deserialize(reader);
 }
コード例 #22
0
    /// <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);
    }
コード例 #23
0
        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);
            });
        }
コード例 #24
0
        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);
            }
        }
コード例 #25
0
 /// <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));
コード例 #26
0
        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);
                }
            }
        }
コード例 #27
0
        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);
                    }
                }
            });
        }
コード例 #28
0
        public IEntity GetEntityUnderPosition(MapCoordinates coordinates)
        {
            var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates);

            return(entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null);
        }
コード例 #29
0
        /// <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);
        }
コード例 #30
0
 /// <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)));
 }