static string GetImpactSound(WarheadInfo warhead, bool isWater) { if (isWater && warhead.WaterImpactSound != null) return warhead.WaterImpactSound; if (warhead.ImpactSound != null) return warhead.ImpactSound; return null; }
public static void AddSmudge(this Map map, int2 targetTile, WarheadInfo warhead) { if (warhead.SmudgeType == SmudgeType.None) return; if (warhead.Size[0] == 0 && warhead.Size[1] == 0) map.AddSmudge(warhead.SmudgeType == SmudgeType.Crater, targetTile.X, targetTile.Y); else foreach (var t in Game.world.FindTilesInCircle(targetTile, warhead.Size[0])) if ((t - targetTile).LengthSquared >= warhead.Size[1] * warhead.Size[1]) if (Rules.TerrainTypes[Game.world.GetTerrainType(t)].AcceptSmudge) map.AddSmudge(warhead.SmudgeType == SmudgeType.Crater, t.X, t.Y); }
public static void DoImpact(WarheadInfo warhead, ProjectileArgs args, int2 visualLocation) { var world = args.firedBy.World; var targetTile = ((1f / Game.CellSize) * args.dest.ToFloat2()).ToInt2(); var isWater = world.GetTerrainType(targetTile) == TerrainType.Water; if (warhead.Explosion != 0) world.AddFrameEndTask( w => w.Add(new Explosion(w, visualLocation, warhead.Explosion, isWater))); Sound.Play(GetImpactSound(warhead, isWater)); if (!isWater) world.Map.AddSmudge(targetTile, warhead); if (warhead.Ore) world.WorldActor.traits.Get<ResourceLayer>().Destroy(targetTile); var firepowerModifier = args.firedBy.traits .WithInterface<IFirepowerModifier>() .Select(a => a.GetFirepowerModifier()) .Product(); switch (warhead.DamageModel) { case DamageModel.Normal: { var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2); var hitActors = world.FindUnitsInCircle(args.dest, maxSpread); foreach (var victim in hitActors) victim.InflictDamage(args.firedBy, (int)GetDamageToInflict(victim, args, warhead, firepowerModifier), warhead); } break; case DamageModel.PerCell: { foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindUnits(Game.CellSize * t, Game.CellSize * (t + new float2(1,1)))) unit.InflictDamage(args.firedBy, (int)(warhead.Damage * warhead.EffectivenessAgainst( unit.Info.Traits.Get<OwnedActorInfo>().Armor)), warhead); } break; } }
public float GetDamageModifier(Actor attacker, WarheadInfo warhead) { return State == PopupState.Closed ? Info.ClosedDamageMultiplier : 1f; }
public static void DoImpact(WarheadInfo warhead, ProjectileArgs args) { var world = args.firedBy.World; var targetTile = Util.CellContaining(args.dest); if (!world.Map.IsInMap(targetTile)) return; var isWater = world.GetTerrainInfo(targetTile).IsWater; var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion; if (explosionType != null) world.AddFrameEndTask( w => w.Add(new Explosion(w, args.dest, explosionType, isWater, args.destAltitude))); Sound.Play(GetImpactSound(warhead, isWater), args.dest); if (warhead.SmudgeType != null) { var smudgeLayer = world.WorldActor.TraitsImplementing<SmudgeLayer>() .FirstOrDefault(x => x.Info.Type == warhead.SmudgeType); if (smudgeLayer == null) throw new NotImplementedException("Unknown smudge type `{0}`".F(warhead.SmudgeType)); if (warhead.Size[0] > 0) { var smudgeCells = world.FindTilesInCircle(targetTile, warhead.Size[0]); if (warhead.Size.Length == 2 ) smudgeCells = smudgeCells.Except(world.FindTilesInCircle(targetTile, warhead.Size[1])) ; foreach (var sc in smudgeCells) { smudgeLayer.AddSmudge(sc); if (warhead.Ore) world.WorldActor.Trait<ResourceLayer>().Destroy(sc); } } else smudgeLayer.AddSmudge(targetTile); } if (warhead.Ore) world.WorldActor.Trait<ResourceLayer>().Destroy(targetTile); switch (warhead.DamageModel) { case DamageModel.Normal: { var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2); var hitActors = world.FindUnitsInCircle(args.dest, maxSpread); foreach (var victim in hitActors) { var damage = (int)GetDamageToInflict(victim, args, warhead, args.firepowerModifier); victim.InflictDamage(args.firedBy, damage, warhead); } } break; case DamageModel.PerCell: { foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindUnits(Game.CellSize * t, Game.CellSize * (t + new float2(1,1)))) unit.InflictDamage(args.firedBy, (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); } break; } }
static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier) { // don't hit air units with splash from ground explosions, etc if (!WeaponValidForTarget(args.weapon, target)) return 0f; var health = target.Info.Traits.GetOrDefault<HealthInfo>(); if( health == null ) return 0f; var distance = (int)Math.Max(0, (target.CenterLocation - args.dest).Length - health.Radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var rawDamage = (float)(warhead.Damage * modifier * falloff); var multiplier = (float)warhead.EffectivenessAgainst(target); return (float)(rawDamage * multiplier); }
public float GetDamageModifier(Actor attacker, WarheadInfo warhead) { return ArmorLevel > 0 ? (1 / (1 + ArmorLevel * info.ArmorModifier)) : 1; }
public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead) { if (IsDead) return; /* overkill! don't count extra hits as more kills! */ var oldState = GetDamageState(); /* apply the damage modifiers, if we have any. */ damage = (int)traits.WithInterface<IDamageModifier>().Aggregate( (float)damage, (a, t) => t.GetDamageModifier() * a); Health -= damage; if (Health <= 0) { Health = 0; if (attacker.Owner != null) attacker.Owner.Kills++; if (RemoveOnDeath) World.AddFrameEndTask(w => w.Remove(this)); } var maxHP = this.GetMaxHP(); if (Health > maxHP) Health = maxHP; var newState = GetDamageState(); foreach (var nd in traits.WithInterface<INotifyDamage>()) nd.Damaged(this, new AttackInfo { Attacker = attacker, Damage = damage, DamageState = newState, DamageStateChanged = newState != oldState, Warhead = warhead }); }
public static void DoImpact(WarheadInfo warhead, ProjectileArgs args) { var world = args.firedBy.World; var targetTile = args.dest.ToCPos(); if (!world.Map.IsInMap(targetTile)) return; var isWater = args.destAltitude == 0 && world.GetTerrainInfo(targetTile).IsWater; var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion; if (explosionType != null) world.AddFrameEndTask( w => w.Add(new Explosion(w, args.dest, explosionType, isWater, args.destAltitude))); Sound.Play(GetImpactSound(warhead, isWater), args.dest); var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type); if (warhead.Size[0] > 0) { var resLayer = world.WorldActor.Trait<ResourceLayer>(); var allCells = world.FindTilesInCircle(targetTile, warhead.Size[0]).ToList(); // `smudgeCells` might want to just be an outer shell of the cells: IEnumerable<CPos> smudgeCells = allCells; if (warhead.Size.Length == 2) smudgeCells = smudgeCells.Except(world.FindTilesInCircle(targetTile, warhead.Size[1])); // Draw the smudges: foreach (var sc in smudgeCells) { var smudgeType = world.GetTerrainInfo(sc).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType == null) continue; SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(sc); if (warhead.Ore) resLayer.Destroy(sc); } // Destroy all resources in range, not just the outer shell: foreach (var cell in allCells) { if (warhead.Ore) resLayer.Destroy(cell); } } else { var smudgeType = world.GetTerrainInfo(targetTile).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType != null) { SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(targetTile); } } if (warhead.Ore) world.WorldActor.Trait<ResourceLayer>().Destroy(targetTile); switch (warhead.DamageModel) { case DamageModel.Normal: { var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2); var hitActors = world.FindUnitsInCircle(args.dest, (int)maxSpread); foreach (var victim in hitActors) { var damage = (int)GetDamageToInflict(victim, args, warhead, args.firepowerModifier); victim.InflictDamage(args.firedBy, damage, warhead); } } break; case DamageModel.PerCell: { foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindUnits(t.ToPPos(), (t + new CVec(1,1)).ToPPos())) unit.InflictDamage(args.firedBy, (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); } break; } }
static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier) { var selectable = target.Info.Traits.GetOrDefault<SelectableInfo>(); var radius = selectable != null ? selectable.Radius : 0; var distance = Math.Max(0, (target.CenterLocation - args.dest).Length - radius); var rawDamage = warhead.Damage * modifier * (float)Math.Exp(-distance / warhead.Spread); var multiplier = warhead.EffectivenessAgainst(target.Info.Traits.Get<OwnedActorInfo>().Armor); return rawDamage * multiplier; }
public static void DoImpact(WPos pos, WarheadInfo warhead, WeaponInfo weapon, Actor firedBy, float firepowerModifier) { var world = firedBy.World; var targetTile = world.Map.CellContaining(pos); if (!world.Map.Contains(targetTile)) return; var isWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater; var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion; var explosionTypePalette = isWater ? warhead.WaterExplosionPalette : warhead.ExplosionPalette; if (explosionType != null) world.AddFrameEndTask(w => w.Add(new Explosion(w, pos, explosionType, explosionTypePalette))); Sound.Play(GetImpactSound(warhead, isWater), pos); var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type); var resLayer = warhead.DestroyResources || !string.IsNullOrEmpty(warhead.AddsResourceType) ? world.WorldActor.Trait<ResourceLayer>() : null; if (warhead.Size[0] > 0) { var allCells = world.Map.FindTilesInCircle(targetTile, warhead.Size[0]).ToList(); // `smudgeCells` might want to just be an outer shell of the cells: IEnumerable<CPos> smudgeCells = allCells; if (warhead.Size.Length == 2) smudgeCells = smudgeCells.Except(world.Map.FindTilesInCircle(targetTile, warhead.Size[1])); // Draw the smudges: foreach (var sc in smudgeCells) { var smudgeType = world.Map.GetTerrainInfo(sc).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType == null) continue; SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(sc); if (warhead.DestroyResources) resLayer.Destroy(sc); } // Destroy all resources in range, not just the outer shell: if (warhead.DestroyResources) foreach (var cell in allCells) resLayer.Destroy(cell); // Splatter resources: if (!string.IsNullOrEmpty(warhead.AddsResourceType)) { var resourceType = world.WorldActor.TraitsImplementing<ResourceType>() .FirstOrDefault(t => t.Info.Name == warhead.AddsResourceType); if (resourceType == null) Log.Write("debug", "Warhead defines an invalid resource type '{0}'".F(warhead.AddsResourceType)); else { foreach (var cell in allCells) { if (!resLayer.CanSpawnResourceAt(resourceType, cell)) continue; var splash = world.SharedRandom.Next(1, resourceType.Info.MaxDensity - resLayer.GetResourceDensity(cell)); resLayer.AddResource(resourceType, cell, splash); } } } } else { var smudgeType = world.Map.GetTerrainInfo(targetTile).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType != null) { SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(targetTile); } } if (warhead.DestroyResources) world.WorldActor.Trait<ResourceLayer>().Destroy(targetTile); switch (warhead.DamageModel) { case DamageModel.Normal: { var maxSpread = new WRange((int)(warhead.Spread.Range * (float)Math.Log(Math.Abs(warhead.Damage), 2))); var hitActors = world.FindActorsInCircle(pos, maxSpread); foreach (var victim in hitActors) { var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, true); victim.InflictDamage(firedBy, damage, warhead); } } break; case DamageModel.PerCell: { foreach (var t in world.Map.FindTilesInCircle(targetTile, warhead.Size[0])) { foreach (var unit in world.ActorMap.GetUnitsAt(t)) { var damage = (int)GetDamageToInflict(pos, unit, warhead, weapon, firepowerModifier, false); unit.InflictDamage(firedBy, damage, warhead); } } } break; case DamageModel.HealthPercentage: { var range = new WRange(warhead.Size[0] * 1024); var hitActors = world.FindActorsInCircle(pos, range); foreach (var victim in hitActors) { var damage = GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, false); if (damage != 0) // will be 0 if the target doesn't have HealthInfo { var healthInfo = victim.Info.Traits.Get<HealthInfo>(); damage = (float)(damage / 100 * healthInfo.HP); } victim.InflictDamage(firedBy, (int)damage, warhead); } } break; } }
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 = Math.Max(0, (target.CenterPosition - pos).Length - healthInfo.Radius.Range); var falloff = (float)GetDamageFalloff(distance * 1f / warhead.Spread.Range); rawDamage = (float)(falloff * rawDamage); } return (float)(rawDamage * modifier * (float)warhead.EffectivenessAgainst(target.Info)); }
static float GetDamageToInflict(WPos pos, Actor target, WarheadInfo warhead, WeaponInfo weapon, float modifier) { // don't hit air units with splash from ground explosions, etc if (!weapon.IsValidAgainst(target)) return 0f; var health = target.Info.Traits.GetOrDefault<HealthInfo>(); if( health == null ) return 0f; var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var rawDamage = (float)(warhead.Damage * modifier * falloff); var multiplier = (float)warhead.EffectivenessAgainst(target.Info); return (float)(rawDamage * multiplier); }
public WeaponInfo(string name, MiniYaml content) { foreach (var kv in content.Nodes) { var key = kv.Key.Split('@')[0]; switch (key) { case "Range": FieldLoader.LoadField(this, "Range", content.Nodes["Range"].Value); break; case "ROF": FieldLoader.LoadField(this, "ROF", content.Nodes["ROF"].Value); break; case "Report": FieldLoader.LoadField(this, "Report", content.Nodes["Report"].Value); break; case "Burst": FieldLoader.LoadField(this, "Burst", content.Nodes["Burst"].Value); break; case "Charges": FieldLoader.LoadField(this, "Charges", content.Nodes["Charges"].Value); break; case "ValidTargets": FieldLoader.LoadField(this, "ValidTargets", content.Nodes["ValidTargets"].Value); break; case "Underwater": FieldLoader.LoadField(this, "Underwater", content.Nodes["Underwater"].Value); break; case "Warhead": { var warhead = new WarheadInfo(); FieldLoader.Load(warhead, kv.Value); Warheads.Add(warhead); } break; // in this case, it's an implementation of IProjectileInfo default: { Projectile = Game.CreateObject<IProjectileInfo>(key + "Info"); FieldLoader.Load(Projectile, kv.Value); } break; } } }
public static void DoImpact(WPos pos, WarheadInfo warhead, WeaponInfo weapon, Actor firedBy, float firepowerModifier) { var world = firedBy.World; var targetTile = pos.ToCPos(); if (!world.Map.IsInMap(targetTile)) return; var isWater = pos.Z == 0 && world.GetTerrainInfo(targetTile).IsWater; var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion; if (explosionType != null) world.AddFrameEndTask(w => w.Add(new Explosion(w, pos, explosionType))); Sound.Play(GetImpactSound(warhead, isWater), pos); var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type); if (warhead.Size[0] > 0) { var resLayer = world.WorldActor.Trait<ResourceLayer>(); var allCells = world.FindTilesInCircle(targetTile, warhead.Size[0]).ToList(); // `smudgeCells` might want to just be an outer shell of the cells: IEnumerable<CPos> smudgeCells = allCells; if (warhead.Size.Length == 2) smudgeCells = smudgeCells.Except(world.FindTilesInCircle(targetTile, warhead.Size[1])); // Draw the smudges: foreach (var sc in smudgeCells) { var smudgeType = world.GetTerrainInfo(sc).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType == null) continue; SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(sc); if (warhead.Ore) resLayer.Destroy(sc); } // Destroy all resources in range, not just the outer shell: foreach (var cell in allCells) { if (warhead.Ore) resLayer.Destroy(cell); } } else { var smudgeType = world.GetTerrainInfo(targetTile).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t)); if (smudgeType != null) { SmudgeLayer smudgeLayer; if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer)) throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType)); smudgeLayer.AddSmudge(targetTile); } } if (warhead.Ore) world.WorldActor.Trait<ResourceLayer>().Destroy(targetTile); switch (warhead.DamageModel) { case DamageModel.Normal: { var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2); var range = new WRange((int)maxSpread * 1024 / Game.CellSize); var hitActors = world.FindActorsInCircle(pos, range); foreach (var victim in hitActors) { var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, true); victim.InflictDamage(firedBy, damage, warhead); } } break; case DamageModel.PerCell: { foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.ActorMap.GetUnitsAt(t)) { var damage = (int)GetDamageToInflict(pos, unit, warhead, weapon, firepowerModifier, false); unit.InflictDamage(firedBy, damage, warhead); } } break; case DamageModel.HealthPercentage: { var range = new WRange(warhead.Size[0] * 1024); var hitActors = world.FindActorsInCircle(pos, range); foreach (var victim in hitActors) { var damage = GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, false); if (damage != 0) // will be 0 if the target doesn't have HealthInfo { var healthInfo = victim.Info.Traits.Get<HealthInfo>(); damage = (float)(damage / 100 * healthInfo.HP); } victim.InflictDamage(firedBy, (int)damage, warhead); } } break; } }