Пример #1
0
        bool ShouldExplode(World world)
        {
            // Check for walls or other blocking obstacles
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width, out var blockedPos))
            {
                pos = blockedPos;
                return(true);
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, GetEffectiveFacing(), w,
                                                                  info.TrailImage, info.TrailSequences.Random(world.SharedRandom), trailPalette)));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var flightLengthReached = ticks++ >= length;
            var shouldBounce        = remainingBounces > 0;

            if (flightLengthReached && shouldBounce)
            {
                var cell = world.Map.CellContaining(pos);
                if (!world.Map.Contains(cell))
                {
                    return(true);
                }

                if (info.InvalidBounceTerrain.Contains(world.Map.GetTerrainInfo(cell).Type))
                {
                    return(true);
                }

                if (AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
                {
                    return(true);
                }

                target += (pos - source) * info.BounceRangeModifier / 100;
                var dat = world.Map.DistanceAboveTerrain(target);
                target += new WVec(0, 0, -dat.Length);
                length  = Math.Max((target - pos).Length / speed.Length, 1);

                ticks  = 0;
                source = pos;
                Game.Sound.Play(SoundType.World, info.BounceSound, source);
                remainingBounces--;
            }

            // Flight length reached / exceeded
            if (flightLengthReached && !shouldBounce)
            {
                return(true);
            }

            // Driving into cell with higher height level
            if (world.Map.DistanceAboveTerrain(pos).Length < 0)
            {
                return(true);
            }

            // After first bounce, check for targets each tick
            if (remainingBounces < info.BounceCount && AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
            {
                return(true);
            }

            return(false);
        }
Пример #2
0
        public void Tick(World world)
        {
            if (info.TrackTarget)
            {
                TrackTarget();
            }

            if (++headTicks >= length)
            {
                headPos          = target;
                isHeadTravelling = false;
            }
            else if (isHeadTravelling)
            {
                headPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, headTicks, length);
            }

            if (tailTicks <= 0 && args.SourceActor.IsInWorld && !args.SourceActor.IsDead)
            {
                args.Source = args.CurrentSource();
                tailPos     = args.Source;
            }

            // Allow for leniency to avoid edge case stuttering (start firing and immediately stop again).
            var outOfWeaponRange = args.Weapon.Range + info.BeyondTargetRange < new WDist((args.PassiveTarget - args.Source).Length);

            // While the head is travelling, the tail must start to follow Duration ticks later.
            // Alternatively, also stop emitting the beam if source actor dies or is ordered to stop.
            if ((headTicks >= info.Duration && !isTailTravelling) || args.SourceActor.IsDead ||
                !actorAttackBase.IsAiming || outOfWeaponRange)
            {
                StopTargeting();
            }

            if (isTailTravelling)
            {
                if (++tailTicks >= length)
                {
                    tailPos          = target;
                    isTailTravelling = false;
                }
                else
                {
                    tailPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, tailTicks, length);
                }
            }

            // Check for blocking actors
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, args.SourceActor.Owner, tailPos, headPos, info.Width, out var blockedPos))
            {
                headPos = blockedPos;
                target  = headPos;
                length  = Math.Min(headTicks, length);
            }

            // Damage is applied to intersected actors every DamageInterval ticks
            if (headTicks % info.DamageInterval == 0)
            {
                var actors = world.FindActorsOnLine(tailPos, headPos, info.Width);
                foreach (var a in actors)
                {
                    var adjustedModifiers = args.DamageModifiers.Append(GetFalloff((args.Source - a.CenterPosition).Length));

                    var warheadArgs = new WarheadArgs(args)
                    {
                        ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(args.Source, target), args.CurrentMuzzleFacing()),

                        // Calculating an impact position is bogus for line damage.
                        // FindActorsOnLine guarantees that the beam touches the target's HitShape,
                        // so we just assume a center hit to avoid bogus warhead recalculations.
                        ImpactPosition  = a.CenterPosition,
                        DamageModifiers = adjustedModifiers.ToArray(),
                    };

                    args.Weapon.Impact(Target.FromActor(a), warheadArgs);
                }
            }

            if (IsBeamComplete)
            {
                world.AddFrameEndTask(w => w.Remove(this));
            }
        }
Пример #3
0
        public void Tick(World world)
        {
            if (info.TrackTarget)
            {
                TrackTarget();
            }

            if (++headTicks >= length)
            {
                headPos          = target;
                isHeadTravelling = false;
            }
            else if (isHeadTravelling)
            {
                headPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, headTicks, length);
            }

            if (tailTicks <= 0 && args.SourceActor.IsInWorld && !args.SourceActor.IsDead)
            {
                args.Source = args.CurrentSource();
                tailPos     = args.Source;
            }

            // Allow for leniency to avoid edge case stuttering (start firing and immediately stop again).
            var outOfWeaponRange = args.Weapon.Range + info.BeyondTargetRange < new WDist((args.PassiveTarget - args.Source).Length);

            // While the head is travelling, the tail must start to follow Duration ticks later.
            // Alternatively, also stop emitting the beam if source actor dies or is ordered to stop.
            if ((headTicks >= info.Duration && !isTailTravelling) || args.SourceActor.IsDead ||
                !actorAttackBase.IsAiming || outOfWeaponRange)
            {
                StopTargeting();
            }

            if (isTailTravelling)
            {
                if (++tailTicks >= length)
                {
                    tailPos          = target;
                    isTailTravelling = false;
                }
                else
                {
                    tailPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, tailTicks, length);
                }
            }

            // Check for blocking actors
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, tailPos, headPos,
                                                                             info.Width, info.BlockerScanRadius, out blockedPos))
            {
                headPos = blockedPos;
                target  = headPos;
                length  = Math.Min(headTicks, length);
            }

            // Damage is applied to intersected actors every DamageInterval ticks
            if (headTicks % info.DamageInterval == 0)
            {
                var actors = world.FindActorsOnLine(tailPos, headPos, info.Width, info.AreaVictimScanRadius);
                foreach (var a in actors)
                {
                    var adjustedModifiers = args.DamageModifiers.Append(GetFalloff((args.Source - a.CenterPosition).Length));
                    args.Weapon.Impact(Target.FromActor(a), args.SourceActor, adjustedModifiers);
                }
            }

            if (IsBeamComplete)
            {
                world.AddFrameEndTask(w => w.Remove(this));
            }
        }
Пример #4
0
        public void Tick(World world)
        {
            if (anim != null)
            {
                anim.Tick();
            }

            var lastPos = pos;

            pos = WPos.LerpQuadratic(source, target, angle, ticks, length);

            // Check for walls or other blocking obstacles
            var  shouldExplode = false;
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width,
                                                                             out blockedPos))
            {
                pos           = blockedPos;
                shouldExplode = true;
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, facing: GetEffectiveFacing())));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var flightLengthReached = ticks++ >= length;
            var shouldBounce        = remainingBounces > 0;

            if (flightLengthReached && shouldBounce)
            {
                shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true);
                target        += (pos - source) * info.BounceRangeModifier / 100;
                var dat = world.Map.DistanceAboveTerrain(target);
                target += new WVec(0, 0, -dat.Length);
                length  = Math.Max((target - pos).Length / speed.Length, 1);
                ticks   = 0;
                source  = pos;
                remainingBounces--;
            }

            // Flight length reached / exceeded
            shouldExplode |= flightLengthReached && !shouldBounce;

            // Driving into cell with higher height level
            shouldExplode |= world.Map.DistanceAboveTerrain(pos).Length < 0;

            // After first bounce, check for targets each tick
            if (remainingBounces < info.BounceCount)
            {
                shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true);
            }

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Пример #5
0
        public void Tick(World world)
        {
            if (launchDelay-- > 0)
            {
                return;
            }

            if (!isLaunched)
            {
                if (weapon.Report != null && weapon.Report.Any())
                {
                    Game.Sound.Play(SoundType.World, weapon.Report, world, pos);
                }

                if (anim != null)
                {
                    anim.PlayRepeating(upSequence);
                    world.ScreenMap.Add(this, pos, anim.Image);
                }

                isLaunched = true;
            }

            if (anim != null)
            {
                anim.Tick();

                if (ticks == turn)
                {
                    anim.PlayRepeating(downSequence);
                }
            }

            var isDescending = ticks >= turn;

            if (!isDescending)
            {
                pos = WPos.LerpQuadratic(ascendSource, ascendTarget, WAngle.Zero, ticks, turn);
            }
            else
            {
                pos = WPos.LerpQuadratic(descendSource, descendTarget, WAngle.Zero, ticks - turn, impactDelay - turn);
            }

            if (!string.IsNullOrEmpty(trailImage) && --trailTicks < 0)
            {
                var trailPos = !isDescending?WPos.LerpQuadratic(ascendSource, ascendTarget, WAngle.Zero, ticks - trailDelay, turn)
                                   : WPos.LerpQuadratic(descendSource, descendTarget, WAngle.Zero, ticks - turn - trailDelay, impactDelay - turn);

                world.AddFrameEndTask(w => w.Add(new SpriteEffect(trailPos, w, trailImage, trailSequences.Random(world.SharedRandom),
                                                                  trailPalette)));

                trailTicks = trailInterval;
            }

            var dat = world.Map.DistanceAboveTerrain(pos);

            if (ticks == impactDelay || (isDescending && dat <= detonationAltitude))
            {
                Explode(world, ticks == impactDelay || removeOnDetonation);
            }

            if (anim != null)
            {
                world.ScreenMap.Update(this, pos, anim.Image);
            }

            ticks++;
        }