static float GetDamageToInflict(WPos pos, Actor target, WarheadInfo warhead, WeaponInfo weapon, float modifier, bool withFalloff) { // don't hit air units with splash from ground explosions, etc if (!weapon.IsValidAgainst(target)) { return(0); } var healthInfo = target.Info.Traits.GetOrDefault <HealthInfo>(); if (healthInfo == null) { return(0); } var rawDamage = (float)warhead.Damage; if (withFalloff) { var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - healthInfo.Radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); rawDamage = (float)(falloff * rawDamage); } return((float)(rawDamage * modifier * (float)warhead.EffectivenessAgainst(target.Info))); }
protected virtual bool CanFire(Actor self, Target target) { if (IsReloading || IsTraitPaused) { return(false); } if (turret != null && !turret.HasAchievedDesiredFacing) { return(false); } if ((!target.IsInRange(self.CenterPosition, MaxRange())) || (Weapon.MinRange != WDist.Zero && target.IsInRange(self.CenterPosition, Weapon.MinRange))) { return(false); } if (!Weapon.IsValidAgainst(target, self.World, self)) { return(false); } return(true); }
bool WormAttack(Actor worm) { var targetLocation = target.Actor.Location; // The target has moved too far away if ((location - targetLocation).Length > NearEnough) { return(false); } var lunch = worm.World.ActorMap.GetUnitsAt(targetLocation) .Where(t => !t.Equals(worm) && weapon.IsValidAgainst(t, worm)); if (!lunch.Any()) { return(false); } stance = AttackState.EmergingAboveGround; sandworm.IsAttacking = true; foreach (var actor in lunch) { var actor1 = actor; // loop variable in closure hazard actor.World.AddFrameEndTask(_ => { actor1.Destroy(); // Harvester insurance if (!actor1.HasTrait <Harvester>()) { return; } var insurance = actor1.Owner.PlayerActor.TraitOrDefault <HarvesterInsurance>(); if (insurance != null) { actor1.World.AddFrameEndTask(__ => insurance.TryActivate()); } }); } positionable.SetPosition(worm, targetLocation); var attackPosition = worm.CenterPosition; var affectedPlayers = lunch.Select(x => x.Owner).Distinct().ToList(); PlayAttack(worm, attackPosition, affectedPlayers); foreach (var notify in worm.TraitsImplementing <INotifyAttack>()) { notify.Attacking(worm, target, null, null); } return(true); }
public override Activity Tick(Actor self) { if (ticks == 0 && IsCanceled) { return(NextActivity); } mobile.SetVisualPosition(self, WPos.LerpQuadratic(from, to, angle, ++ticks, length)); if (ticks >= length) { mobile.SetLocation(mobile.ToCell, mobile.ToSubCell, mobile.ToCell, mobile.ToSubCell); mobile.FinishedMoving(self); mobile.IsMoving = false; self.World.ActorMap.GetActorsAt(mobile.ToCell, mobile.ToSubCell) .Except(new[] { self }).Where(t => weapon.IsValidAgainst(t, self)) .Do(t => t.Kill(self, damageTypes)); return(NextActivity); } return(this); }
public override bool Tick(Actor self) { switch (stance) { case AttackState.Uninitialized: stance = AttackState.Burrowed; countdown = swallow.Info.AttackDelay; burrowLocation = self.Location; if (conditionManager != null && attackingToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(swallow.Info.AttackingCondition)) { attackingToken = conditionManager.GrantCondition(self, swallow.Info.AttackingCondition); } break; case AttackState.Burrowed: if (--countdown > 0) { return(false); } var targetLocation = target.Actor.Location; // The target has moved too far away if ((burrowLocation - targetLocation).Length > NearEnough) { RevokeCondition(self); return(true); } // The target reached solid ground if (!positionable.CanEnterCell(targetLocation, null, false)) { RevokeCondition(self); return(true); } var targets = self.World.ActorMap.GetActorsAt(targetLocation) .Where(t => !t.Equals(self) && weapon.IsValidAgainst(t, self)); if (!targets.Any()) { RevokeCondition(self); return(true); } stance = AttackState.Attacking; countdown = swallow.Info.ReturnDelay; sandworm.IsAttacking = true; AttackTargets(self, targets); break; case AttackState.Attacking: if (--countdown > 0) { return(false); } sandworm.IsAttacking = false; // There is a chance that the worm would just go away after attacking if (self.World.SharedRandom.Next(100) <= sandworm.WormInfo.ChanceToDisappear) { self.CancelActivity(); self.World.AddFrameEndTask(w => self.Dispose()); } RevokeCondition(self); return(true); } return(false); }
// Note: facing is only used by the legacy positioning code // The world coordinate model uses Actor.Orientation public Barrel CheckFire(Actor self, IFacing facing, Target target) { if (IsReloading) { return(null); } if (ammoPool != null && !ammoPool.HasAmmo()) { return(null); } if (turret != null && !turret.HasAchievedDesiredFacing) { return(null); } if (!target.IsInRange(self.CenterPosition, MaxRange())) { return(null); } if (Weapon.MinRange != WDist.Zero && target.IsInRange(self.CenterPosition, Weapon.MinRange)) { return(null); } if (!Weapon.IsValidAgainst(target, self.World, self)) { return(null); } var barrel = Barrels[Burst % Barrels.Length]; Func <WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel); var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4; var args = new ProjectileArgs { Weapon = Weapon, Facing = legacyFacing, DamageModifiers = self.TraitsImplementing <IFirepowerModifier>() .Select(a => a.GetFirepowerModifier()).ToArray(), InaccuracyModifiers = self.TraitsImplementing <IInaccuracyModifier>() .Select(a => a.GetInaccuracyModifier()).ToArray(), RangeModifiers = self.TraitsImplementing <IRangeModifier>() .Select(a => a.GetRangeModifier()).ToArray(), Source = muzzlePosition(), CurrentSource = muzzlePosition, SourceActor = self, PassiveTarget = target.CenterPosition, GuidedTarget = target }; ScheduleDelayedAction(Info.FireDelay, () => { if (args.Weapon.Projectile != null) { var projectile = args.Weapon.Projectile.Create(args); if (projectile != null) { self.World.Add(projectile); } if (args.Weapon.Report != null && args.Weapon.Report.Any()) { Game.Sound.Play(args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); } foreach (var na in self.TraitsImplementing <INotifyAttack>()) { na.Attacking(self, target, this, barrel); } Recoil = Info.Recoil; } }); if (--Burst > 0) { FireDelay = Weapon.BurstDelay; } else { var modifiers = self.TraitsImplementing <IReloadModifier>() .Select(m => m.GetReloadModifier()); FireDelay = Util.ApplyPercentageModifiers(Weapon.ReloadDelay, modifiers); Burst = Weapon.Burst; } return(barrel); }
// Note: facing is only used by the legacy positioning code // The world coordinate model uses Actor.Orientation public virtual Barrel CheckFire(Actor self, IFacing facing, Target target) { if (IsReloading) { return(null); } if (ammoPool != null && !ammoPool.HasAmmo()) { return(null); } if (turret != null && !turret.HasAchievedDesiredFacing) { return(null); } if (!target.IsInRange(self.CenterPosition, MaxRange())) { return(null); } if (Weapon.MinRange != WDist.Zero && target.IsInRange(self.CenterPosition, Weapon.MinRange)) { return(null); } if (!Weapon.IsValidAgainst(target, self.World, self)) { return(null); } if (ticksSinceLastShot >= Weapon.ReloadDelay) { Burst = Weapon.Burst; } ticksSinceLastShot = 0; var barrel = Barrels[Burst % Barrels.Length]; Func <WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel); var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4; var passiveTarget = Weapon.TargetActorCenter ? target.CenterPosition : target.Positions.PositionClosestTo(muzzlePosition()); var initialOffset = Weapon.FirstBurstTargetOffset; if (initialOffset != WVec.Zero) { // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up initialOffset = new WVec(initialOffset.Y, -initialOffset.X, initialOffset.Z); passiveTarget += initialOffset.Rotate(WRot.FromFacing(legacyFacing)); } var followingOffset = Weapon.FollowingBurstTargetOffset; if (followingOffset != WVec.Zero) { // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up followingOffset = new WVec(followingOffset.Y, -followingOffset.X, followingOffset.Z); passiveTarget += ((Weapon.Burst - Burst) * followingOffset).Rotate(WRot.FromFacing(legacyFacing)); } var args = new ProjectileArgs { Weapon = Weapon, Facing = legacyFacing, DamageModifiers = damageModifiers.ToArray(), InaccuracyModifiers = inaccuracyModifiers.ToArray(), RangeModifiers = rangeModifiers.ToArray(), Source = muzzlePosition(), CurrentSource = muzzlePosition, SourceActor = self, PassiveTarget = passiveTarget, GuidedTarget = target }; foreach (var na in notifyAttacks) { na.PreparingAttack(self, target, this, barrel); } ScheduleDelayedAction(Info.FireDelay, () => { if (args.Weapon.Projectile != null) { var projectile = args.Weapon.Projectile.Create(args); if (projectile != null) { self.World.Add(projectile); } if (args.Weapon.Report != null && args.Weapon.Report.Any()) { Game.Sound.Play(SoundType.World, args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); } if (Burst == args.Weapon.Burst && args.Weapon.StartBurstReport != null && args.Weapon.StartBurstReport.Any()) { Game.Sound.Play(SoundType.World, args.Weapon.StartBurstReport.Random(self.World.SharedRandom), self.CenterPosition); } foreach (var na in notifyAttacks) { na.Attacking(self, target, this, barrel); } Recoil = Info.Recoil; } }); if (--Burst > 0) { FireDelay = Weapon.BurstDelay; } else { var modifiers = reloadModifiers.ToArray(); FireDelay = Util.ApplyPercentageModifiers(Weapon.ReloadDelay, modifiers); Burst = Weapon.Burst; if (args.Weapon.AfterFireSound != null && args.Weapon.AfterFireSound.Any()) { ScheduleDelayedAction(Weapon.AfterFireSoundDelay, () => { Game.Sound.Play(SoundType.World, Weapon.AfterFireSound.Random(self.World.SharedRandom), self.CenterPosition); }); } foreach (var nbc in notifyBurstComplete) { nbc.FiredBurst(self, target, this); } } return(barrel); }
// Note: facing is only used by the legacy positioning code // The world coordinate model uses Actor.Orientation public void CheckFire(Actor self, AttackBase attack, IFacing facing, Target target) { if (FireDelay > 0) { return; } var limitedAmmo = self.TraitOrDefault <LimitedAmmo>(); if (limitedAmmo != null && !limitedAmmo.HasAmmo()) { return; } // TODO: Define weapon ranges as WRange var range = new WRange((int)(1024 * Weapon.Range)); var minRange = new WRange((int)(1024 * Weapon.MinRange)); if (!target.IsInRange(self.CenterPosition, range)) { return; } if (minRange != WRange.Zero && target.IsInRange(self.CenterPosition, minRange)) { return; } if (!Weapon.IsValidAgainst(target, self.World)) { return; } var barrel = Barrels[Burst % Barrels.Length]; var muzzlePosition = self.CenterPosition + MuzzleOffset(self, barrel); var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4; var args = new ProjectileArgs { Weapon = Weapon, Facing = legacyFacing, FirepowerModifier = self.TraitsImplementing <IFirepowerModifier>() .Select(a => a.GetFirepowerModifier()) .Product(), Source = muzzlePosition, SourceActor = self, PassiveTarget = target.CenterPosition, GuidedTarget = target }; attack.ScheduleDelayedAction(Info.FireDelay, () => { if (args.Weapon.Projectile != null) { var projectile = args.Weapon.Projectile.Create(args); if (projectile != null) { self.World.Add(projectile); } if (args.Weapon.Report != null && args.Weapon.Report.Any()) { Sound.Play(args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); } } }); foreach (var na in self.TraitsImplementing <INotifyAttack>()) { na.Attacking(self, target, this, barrel); } Recoil = Info.Recoil; if (--Burst > 0) { FireDelay = Weapon.BurstDelay; } else { FireDelay = Weapon.ROF; Burst = Weapon.Burst; } }