public IEnumerable <IRenderable> Render(WorldRenderer wr) { if (info.ContrailLength > 0) { yield return(trail); } if (anim == null || ticks >= length) { yield break; } var cell = pos.ToCPos(); if (!args.SourceActor.World.FogObscures(cell)) { if (info.Shadow) { var shadowPos = pos - new WVec(0, 0, pos.Z); foreach (var r in anim.Render(shadowPos, wr.Palette("shadow"))) { yield return(r); } } var palette = wr.Palette(args.Weapon.Underwater ? "shadow" : "effect"); foreach (var r in anim.Render(pos, palette)) { yield return(r); } } }
public void Tick(World world) { // Fade the trail out gradually if (ticks > length && info.ContrailLength > 0) { trail.Update(pos); return; } if (anim != null) { anim.Tick(); } pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length); if (info.Trail != null && --smokeTicks < 0) { var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length); world.AddFrameEndTask(w => w.Add(new Smoke(w, delayedPos, info.Trail))); smokeTicks = info.TrailInterval; } if (info.ContrailLength > 0) { trail.Update(pos); } if (ticks++ >= length || (!info.High && world.ActorMap .GetUnitsAt(pos.ToCPos()).Any(a => a.HasTrait <IBlocksBullets>()))) { Explode(world); } }
public void SetPosition(Actor self, WPos pos) { self.World.ActorMap.Remove(self, this); CenterPosition = pos; TopLeft = pos.ToCPos(); self.World.ActorMap.Add(self, this); }
public IEnumerable<IRenderable> Render(WorldRenderer wr) { if (wr.world.FogObscures(pos.ToCPos())) yield break; yield return new TextRenderable(font, pos, 0, color, text); }
public void SetPosition(Actor self, WPos pos) { var cell = pos.ToCPos(); SetLocation(cell, fromSubCell, cell, fromSubCell); SetVisualPosition(self, pos); FinishedMoving(self); }
public Explosion(World world, WPos pos, string style) { this.world = world; this.pos = pos; this.cell = pos.ToCPos(); anim = new Animation("explosion"); anim.PlayThen(style, () => world.AddFrameEndTask(w => w.Remove(this))); }
public Corpse(World world, WPos pos, string image, string sequence, string paletteName) { this.world = world; this.pos = pos; this.cell = pos.ToCPos(); this.paletteName = paletteName; anim = new Animation(image); anim.PlayThen(sequence, () => world.AddFrameEndTask(w => w.Remove(this))); }
public Smoke(World world, WPos pos, string trail) { this.world = world; this.pos = pos; this.cell = pos.ToCPos(); anim = new Animation(trail); anim.PlayThen("idle", () => world.AddFrameEndTask(w => w.Remove(this))); }
public Explosion(World world, WPos pos, string sequence, string palette) { this.world = world; this.pos = pos; this.cell = pos.ToCPos(); this.palette = palette; anim = new Animation(world, "explosion"); anim.PlayThen(sequence, () => world.AddFrameEndTask(w => w.Remove(this))); }
public void SetPosition(Actor self, WPos pos) { self.World.ActorMap.RemoveInfluence(self, this); CenterPosition = pos; TopLeft = pos.ToCPos(); self.World.ActorMap.AddInfluence(self, this); self.World.ActorMap.UpdatePosition(self, this); self.World.ScreenMap.Update(self); }
public IEnumerable <IRenderable> Render(WorldRenderer wr) { if (info.ContrailLength > 0) { yield return(trail); } if (!args.SourceActor.World.FogObscures(pos.ToCPos())) { var palette = wr.Palette(args.Weapon.Palette); foreach (var r in anim.Render(pos, palette)) { yield return(r); } } }
public Parachute(Actor cargo, WPos dropPosition) { this.cargo = cargo; var pai = cargo.Info.Traits.GetOrDefault<ParachuteAttachmentInfo>(); paraAnim = new Animation(cargo.World, pai != null ? pai.ParachuteSprite : "parach"); paraAnim.PlayThen("open", () => paraAnim.PlayRepeating("idle")); if (pai != null) parachuteOffset = pai.Offset; // Adjust x,y to match the target subcell cargo.Trait<IPositionable>().SetPosition(cargo, dropPosition.ToCPos()); var cp = cargo.CenterPosition; pos = new WPos(cp.X, cp.Y, dropPosition.Z); }
public Parachute(Actor cargo, WPos dropPosition) { this.cargo = cargo; var pai = cargo.Info.Traits.GetOrDefault<ParachuteAttachmentInfo>(); paraAnim = new Animation(pai != null ? pai.ParachuteSprite : "parach"); paraAnim.PlayThen("open", () => paraAnim.PlayRepeating("idle")); if (pai != null) parachuteOffset = pai.Offset; // Adjust x,y to match the target subcell cargo.Trait<IPositionable>().SetPosition(cargo, dropPosition.ToCPos()); var cp = cargo.CenterPosition; pos = new WPos(cp.X, cp.Y, dropPosition.Z); }
public IEnumerable <IRenderable> Render(WorldRenderer wr) { var cell = pos.ToCPos(); if (!args.SourceActor.World.FogObscures(cell)) { if (info.Shadow) { var shadowPos = pos - new WVec(0, 0, pos.Z); foreach (var r in anim.Render(shadowPos, wr.Palette("shadow"))) { yield return(r); } } var palette = wr.Palette(args.Weapon.Palette); foreach (var r in anim.Render(pos, palette)) { yield return(r); } } }
public List <CPos> FindUnitPathToRange(CPos src, SubCell srcSub, WPos target, WRange range, Actor self) { using (new PerfSample("Pathfinder")) { var mi = self.Info.Traits.Get <MobileInfo>(); var targetCell = target.ToCPos(); var rangeSquared = range.Range * range.Range; // Correct for SubCell offset target -= MobileInfo.SubCellOffsets[srcSub]; // Select only the tiles that are within range from the requested SubCell // This assumes that the SubCell does not change during the path traversal var tilesInRange = world.FindTilesInCircle(targetCell, range.Range / 1024 + 1) .Where(t => (t.CenterPosition - target).LengthSquared <= rangeSquared && mi.CanEnterCell(self.World, self, t, null, true, true)); // See if there is any cell within range that does not involve a cross-domain request // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win var domainIndex = self.World.WorldActor.TraitOrDefault <DomainIndex>(); if (domainIndex != null) { var passable = mi.GetMovementClass(world.TileSet); tilesInRange = new List <CPos>(tilesInRange.Where(t => domainIndex.IsPassable(src, t, (uint)passable))); if (tilesInRange.Count() == 0) { return(emptyPath); } } var path = FindBidiPath( PathSearch.FromPoints(world, mi, self, tilesInRange, src, true), PathSearch.FromPoint(world, mi, self, src, targetCell, true).InReverse() ); return(path); } }
public void Tick(World world) { ticks++; anim.Tick(); // Missile tracks target if (args.GuidedTarget.IsValidFor(args.SourceActor)) { targetPosition = args.GuidedTarget.CenterPosition; } var dist = targetPosition + offset - pos; var desiredFacing = Traits.Util.GetFacing(dist, facing); var desiredAltitude = targetPosition.Z; var jammed = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(j => JammedBy(j)); if (jammed) { desiredFacing = facing + world.SharedRandom.Next(-20, 21); desiredAltitude = world.SharedRandom.Next(-43, 86); } else if (!args.GuidedTarget.IsValidFor(args.SourceActor)) { desiredFacing = facing; } facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT); var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * info.Speed.Range / 1024; if (targetPosition.Z > 0 && info.TurboBoost) { move = (move * 3) / 2; } if (pos.Z != desiredAltitude) { var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024; var dz = (targetPosition.Z - pos.Z).Clamp(-delta, delta); move += new WVec(0, 0, dz); } pos += move; if (info.Trail != null && --ticksToNextSmoke < 0) { world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail))); ticksToNextSmoke = info.TrailInterval; } if (info.ContrailLength > 0) { trail.Update(pos); } var cell = pos.ToCPos(); var shouldExplode = (pos.Z < 0) || // Hit the ground (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) || // Within range (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel (!info.High && world.ActorMap.GetUnitsAt(cell) .Any(a => a.HasTrait <IBlocksBullets>())) || // Hit a wall (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.GetTerrainType(cell) != info.BoundToTerrainType); // Hit incompatible terrain if (shouldExplode) { Explode(world); } }
public void Tick(World world) { ticks++; anim.Tick(); // Missile tracks target if (args.GuidedTarget.IsValidFor(args.SourceActor)) targetPosition = args.GuidedTarget.CenterPosition; var dist = targetPosition + offset - pos; var desiredFacing = Traits.Util.GetFacing(dist, facing); var desiredAltitude = targetPosition.Z; var jammed = info.Jammable && world.ActorsWithTrait<JamsMissiles>().Any(j => JammedBy(j)); if (jammed) { desiredFacing = facing + world.SharedRandom.Next(-20, 21); desiredAltitude = world.SharedRandom.Next(-43, 86); } else if (!args.GuidedTarget.IsValidFor(args.SourceActor)) desiredFacing = facing; facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT); var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * info.Speed.Range / 1024; if (targetPosition.Z > 0 && info.TurboBoost) move = (move * 3) / 2; if (pos.Z != desiredAltitude) { var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024; var dz = (targetPosition.Z - pos.Z).Clamp(-delta, delta); move += new WVec(0, 0, dz); } pos += move; if (info.Trail != null && --ticksToNextSmoke < 0) { world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail))); ticksToNextSmoke = info.TrailInterval; } if (info.ContrailLength > 0) trail.Update(pos); var cell = pos.ToCPos(); var shouldExplode = (pos.Z < 0) // Hit the ground || (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) // Within range || (info.RangeLimit != 0 && ticks > info.RangeLimit) // Ran out of fuel || (!info.High && world.ActorMap.GetUnitsAt(cell) .Any(a => a.HasTrait<IBlocksBullets>())) // Hit a wall || (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.GetTerrainType(cell) != info.BoundToTerrainType); // Hit incompatible terrain if (shouldExplode) Explode(world); }
public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, pos.ToCPos()); }
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); victim.InflictDamage(firedBy, damage, warhead); } } break; case DamageModel.PerCell: { foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindActorsInBox(t, t)) unit.InflictDamage(firedBy, (int)(warhead.Damage * warhead.EffectivenessAgainst(unit.Info)), warhead); } 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; } }
public void Tick(World world) { if (anim != null) anim.Tick(); pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length); if (info.Trail != null && --smokeTicks < 0) { var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length); world.AddFrameEndTask(w => w.Add(new Smoke(w, delayedPos, info.Trail))); smokeTicks = info.TrailInterval; } if (info.ContrailLength > 0) trail.Update(pos); if (ticks++ >= length || (!info.High && world.ActorMap .GetUnitsAt(pos.ToCPos()).Any(a => a.HasTrait<IBlocksBullets>()))) { Explode(world); } }