Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Scripting;
namespace OpenRA.Mods.Common.Effects
{
public class Beacon : IEffect, IScriptBindable, IEffectAboveShroud
{
const int MaxArrowHeight = 512;
readonly Player owner;
readonly WPos position;
readonly bool isPlayerPalette;
readonly string beaconPalette, posterPalette;
readonly Animation arrow, beacon, circles, clock, poster;
readonly int duration;
int delay;
int arrowHeight = MaxArrowHeight;
int arrowSpeed = 50;
int tick;
// Player-placed beacons are removed after a delay
public Beacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette,
string beaconCollection, string beaconSequence, string arrowSprite, string circleSprite, int delay = 0)
{
this.owner = owner;
this.position = position;
this.beaconPalette = beaconPalette;
this.isPlayerPalette = isPlayerPalette;
this.duration = duration;
this.delay = delay;
if (!string.IsNullOrEmpty(beaconSequence))
{
beacon = new Animation(owner.World, beaconCollection);
beacon.PlayRepeating(beaconSequence);
}
if (!string.IsNullOrEmpty(arrowSprite))
{
arrow = new Animation(owner.World, beaconCollection);
arrow.Play(arrowSprite);
}
if (!string.IsNullOrEmpty(circleSprite))
{
circles = new Animation(owner.World, beaconCollection);
circles.Play(circleSprite);
}
}
// By default, support power beacons are expected to clean themselves up
public Beacon(Player owner, WPos position, bool isPlayerPalette, string palette, string posterCollection, string posterType, string posterPalette,
string beaconSequence, string arrowSequence, string circleSequence, string clockSequence, Func<float> clockFraction, int delay = 0, int duration = -1)
: this(owner, position, duration, palette, isPlayerPalette, posterCollection, beaconSequence, arrowSequence, circleSequence, delay)
{
this.posterPalette = posterPalette;
if (posterType != null)
{
poster = new Animation(owner.World, posterCollection);
poster.Play(posterType);
if (clockFraction != null)
{
clock = new Animation(owner.World, posterCollection);
clock.PlayFetchIndex(clockSequence, () => ((int)(clockFraction() * (clock.CurrentSequence.Length - 1))).Clamp(0, clock.CurrentSequence.Length - 1));
}
}
}
void IEffect.Tick(World world)
{
if (delay-- > 0)
return;
arrowHeight += arrowSpeed;
var clamped = arrowHeight.Clamp(0, MaxArrowHeight);
if (arrowHeight != clamped)
{
arrowHeight = clamped;
arrowSpeed *= -1;
}
arrow?.Tick();
beacon?.Tick();
circles?.Tick();
clock?.Tick();
if (duration > 0 && duration <= tick++)
owner.World.AddFrameEndTask(w => w.Remove(this));
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer r) { return SpriteRenderable.None; }
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer r)
{
if (delay > 0)
yield break;
if (!owner.IsAlliedWith(owner.World.RenderPlayer))
yield break;
var palette = r.Palette(isPlayerPalette ? beaconPalette + owner.InternalName : beaconPalette);
if (beacon != null)
foreach (var a in beacon.Render(position, palette))
yield return a;
if (circles != null)
foreach (var a in circles.Render(position, palette))
yield return a;
if (arrow != null)
foreach (var a in arrow.Render(position + new WVec(0, 0, arrowHeight), palette))
yield return a;
if (poster != null)
{
foreach (var a in poster.Render(position, r.Palette(posterPalette)))
yield return a;
if (clock != null)
foreach (var a in clock.Render(position, r.Palette(posterPalette)))
yield return a;
}
}
}
}

View File

@@ -0,0 +1,44 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
namespace OpenRA.Mods.Common.Effects
{
public class ContrailFader : IEffect
{
readonly WPos pos;
readonly ContrailRenderable trail;
int ticks;
public ContrailFader(WPos pos, ContrailRenderable trail)
{
this.pos = pos;
this.trail = trail;
}
public void Tick(World world)
{
if (ticks++ == trail.Length)
world.AddFrameEndTask(w => w.Remove(this));
trail.Update(pos);
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
yield return trail;
}
}
}

View File

@@ -0,0 +1,82 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Effects
{
public class FlashTarget : IEffect
{
readonly Actor target;
readonly int count;
readonly int interval;
readonly TintModifiers modifiers;
readonly float3 tint;
readonly float? alpha;
int tick;
FlashTarget(Actor target, int count, int interval, int delay)
{
this.target = target;
this.count = count;
this.interval = interval;
tick = -delay;
target.World.RemoveAll(effect => effect is FlashTarget flashTarget && flashTarget.target == target);
}
public FlashTarget(Actor target, Color color, float alpha = 0.5f, int count = 2, int interval = 2, int delay = 0)
: this(target, count, interval, delay)
{
modifiers = TintModifiers.ReplaceColor;
tint = new float3(color.R, color.G, color.B) / 255f;
this.alpha = alpha;
}
public FlashTarget(Actor target, float3 tint, int count = 2, int interval = 2, int delay = 0)
: this(target, count, interval, delay)
{
this.tint = tint;
}
public void Tick(World world)
{
if (++tick >= count * interval || !target.IsInWorld)
world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (target.IsInWorld && tick >= 0 && tick % interval == 0)
{
return target.Render(wr)
.Where(r => !r.IsDecoration && r is IModifyableRenderable)
.Select(r =>
{
var mr = (IModifyableRenderable)r;
mr = mr.WithTint(tint, mr.TintModifiers | modifiers);
if (alpha.HasValue)
mr = mr.WithAlpha(alpha.Value);
return mr;
});
}
return SpriteRenderable.None;
}
}
}

View File

@@ -0,0 +1,95 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Effects;
using OpenRA.Graphics;
namespace OpenRA.Mods.Common.Effects
{
public sealed class FloatingSprite : IEffect, ISpatiallyPartitionable
{
readonly ImmutableArray<WDist> speed;
readonly ImmutableArray<WDist> gravity;
readonly Animation anim;
readonly bool visibleThroughFog;
readonly int turnRate;
readonly int randomRate;
readonly string palette;
WPos pos;
WVec offset;
int lifetime;
int ticks;
WAngle facing;
public FloatingSprite(Actor emitter, string image, ImmutableArray<string> sequences, string palette, bool isPlayerPalette,
ImmutableArray<int> lifetime, ImmutableArray<WDist> speed, ImmutableArray<WDist> gravity, int turnRate, int randomRate, WPos pos, WAngle facing,
bool visibleThroughFog = false)
{
var world = emitter.World;
this.pos = pos;
this.turnRate = turnRate;
this.randomRate = randomRate;
this.speed = speed;
this.gravity = gravity;
this.visibleThroughFog = visibleThroughFog;
this.facing = facing;
anim = new Animation(world, image, () => facing);
anim.PlayRepeating(sequences.Random(world.LocalRandom));
world.ScreenMap.Add(this, pos, anim.Image);
this.lifetime = Util.RandomInRange(world.LocalRandom, lifetime);
this.palette = isPlayerPalette ? palette + emitter.Owner.InternalName : palette;
}
public void Tick(World world)
{
if (--lifetime < 0)
{
world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); });
return;
}
if (--ticks < 0)
{
var forward = Util.RandomDistance(world.LocalRandom, speed).Length;
var height = Util.RandomDistance(world.LocalRandom, gravity).Length;
offset = new WVec(forward, 0, height);
if (turnRate > 0)
facing = WAngle.FromFacing(Util.NormalizeFacing(facing.Facing + world.LocalRandom.Next(-turnRate, turnRate)));
offset = offset.Rotate(WRot.FromYaw(facing));
ticks = randomRate;
}
anim.Tick();
pos += offset;
world.ScreenMap.Update(this, pos, anim.Image);
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (!visibleThroughFog && wr.World.FogObscures(pos))
return SpriteRenderable.None;
return anim.Render(pos, wr.Palette(palette));
}
}
}

View File

@@ -0,0 +1,63 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Effects
{
public class FloatingText : IEffect, IEffectAnnotation
{
static readonly WVec Velocity = new(0, 0, 86);
readonly SpriteFont font;
readonly string text;
readonly Color color;
int remaining;
WPos pos;
public FloatingText(WPos pos, Color color, string text, int duration)
{
font = Game.Renderer.Fonts["TinyBold"];
this.pos = pos;
this.color = color;
this.text = text;
remaining = duration;
}
void IEffect.Tick(World world)
{
if (--remaining <= 0)
world.AddFrameEndTask(w => w.Remove(this));
pos += Velocity;
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { return SpriteRenderable.None; }
IEnumerable<IRenderable> IEffectAnnotation.RenderAnnotation(WorldRenderer wr)
{
if (wr.World.FogObscures(pos) || wr.World.ShroudObscures(pos))
yield break;
yield return new TextAnnotationRenderable(font, pos, 0, color, text);
}
public static string FormatCashTick(int cashAmount)
{
return $"{(cashAmount < 0 ? "-" : "+")}${Math.Abs(cashAmount)}";
}
}
}

View File

@@ -0,0 +1,64 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Effects
{
public class MapNotificationEffect : IEffect
{
readonly RadarPings radarPings;
readonly WPos pos;
readonly Player player;
readonly int duration;
readonly string category;
readonly string notification;
readonly bool visible;
readonly Color color;
int remainingDelay;
public MapNotificationEffect(Player player, string category, string notification, int delay,
bool pingVisible, WPos pos, Color pingColor, int pingDuration = 50)
{
this.player = player;
remainingDelay = delay;
this.category = category;
this.notification = notification;
this.pos = pos;
duration = pingDuration;
visible = pingVisible;
color = pingColor;
radarPings = player.World.WorldActor.TraitOrDefault<RadarPings>();
}
public void Tick(World world)
{
if (remainingDelay-- > 0)
return;
Game.Sound.PlayNotification(player.World.Map.Rules, player, category, notification, player.Faction.InternalName);
if (visible && radarPings != null && player == player.World.RenderPlayer)
radarPings.Add(() => true, pos, color, duration);
world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<IRenderable> Render(WorldRenderer wr) { return SpriteRenderable.None; }
}
}

View File

@@ -0,0 +1,136 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Effects
{
public class RallyPointIndicator : IEffect, IEffectAboveShroud, IEffectAnnotation
{
readonly Actor building;
readonly RallyPoint rp;
readonly Animation flag;
readonly Animation circles;
readonly List<WPos> targetLineNodes = [];
List<CPos> cachedLocations;
public RallyPointIndicator(Actor building, RallyPoint rp)
{
this.building = building;
this.rp = rp;
if (rp.Info.Image != null)
{
if (rp.Info.FlagSequence != null)
{
flag = new Animation(building.World, rp.Info.Image);
flag.PlayRepeating(rp.Info.FlagSequence);
}
if (rp.Info.CirclesSequence != null)
{
circles = new Animation(building.World, rp.Info.Image);
circles.Play(rp.Info.CirclesSequence);
}
}
UpdateTargetLineNodes(building.World);
}
void IEffect.Tick(World world)
{
flag?.Tick();
circles?.Tick();
if (cachedLocations == null || !cachedLocations.SequenceEqual(rp.Path))
{
UpdateTargetLineNodes(world);
circles?.Play(rp.Info.CirclesSequence);
}
if (!building.IsInWorld || building.IsDead)
world.AddFrameEndTask(w => w.Remove(this));
}
void UpdateTargetLineNodes(World world)
{
cachedLocations = rp.Path.ToList();
targetLineNodes.Clear();
foreach (var c in cachedLocations)
targetLineNodes.Add(world.Map.CenterOfCell(c));
if (targetLineNodes.Count == 0)
return;
var exit = building.NearestExitOrDefault(targetLineNodes[0]);
targetLineNodes.Insert(0, building.CenterPosition + (exit?.Info.SpawnOffset ?? WVec.Zero));
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { return SpriteRenderable.None; }
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer wr)
{
if (!building.IsInWorld || !building.Owner.IsAlliedWith(building.World.LocalPlayer))
return SpriteRenderable.None;
if (!building.World.Selection.Contains(building))
return SpriteRenderable.None;
var renderables = SpriteRenderable.None;
if (targetLineNodes.Count > 0 && (circles != null || flag != null))
{
var palette = wr.Palette(rp.PaletteName);
if (circles != null)
renderables = renderables.Concat(circles.Render(targetLineNodes[^1], palette));
if (flag != null)
renderables = renderables.Concat(flag.Render(targetLineNodes[^1], palette));
}
return renderables;
}
IEnumerable<IRenderable> IEffectAnnotation.RenderAnnotation(WorldRenderer wr)
{
if (Game.Settings.Game.TargetLines == TargetLinesType.Disabled)
return SpriteRenderable.None;
if (!building.IsInWorld || !building.Owner.IsAlliedWith(building.World.LocalPlayer))
return SpriteRenderable.None;
if (!building.World.Selection.Contains(building))
return SpriteRenderable.None;
if (targetLineNodes.Count == 0)
return SpriteRenderable.None;
return RenderInner();
}
IEnumerable<IRenderable> RenderInner()
{
var prev = targetLineNodes[0];
foreach (var pos in targetLineNodes.Skip(1))
{
var targetLine = new[] { prev, pos };
prev = pos;
yield return new TargetLineRenderable(targetLine, building.OwnerColor(), rp.Info.LineWidth, 1);
}
}
}
}

View File

@@ -0,0 +1,86 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Effects
{
public class RevealShroudEffect : IEffect
{
static readonly PPos[] NoCells = [];
readonly WPos pos;
readonly Player player;
readonly Shroud.SourceType sourceType;
readonly WDist revealRadius;
readonly PlayerRelationship validStances;
readonly int duration;
int ticks;
public RevealShroudEffect(WPos pos, WDist radius, Shroud.SourceType type, Player forPlayer, PlayerRelationship stances, int delay = 0, int duration = 50)
{
this.pos = pos;
player = forPlayer;
revealRadius = radius;
validStances = stances;
sourceType = type;
this.duration = duration;
ticks = -delay;
}
void AddCellsToPlayerShroud(Player p, PPos[] uv)
{
if (!validStances.HasRelationship(player.RelationshipWith(p)))
return;
p.Shroud.AddSource(this, sourceType, uv);
}
void RemoveCellsFromPlayerShroud(Player p) { p.Shroud.RemoveSource(this); }
PPos[] ProjectedCells(World world)
{
var map = world.Map;
var range = revealRadius;
if (range == WDist.Zero)
return NoCells;
return Shroud.ProjectedCellsInRange(map, pos, WDist.Zero, range).ToArray();
}
public void Tick(World world)
{
if (ticks == 0)
{
var cells = ProjectedCells(world);
foreach (var p in world.Players)
AddCellsToPlayerShroud(p, cells);
}
if (ticks == duration)
{
foreach (var p in world.Players)
RemoveCellsFromPlayerShroud(p);
world.AddFrameEndTask(w => w.Remove(this));
}
ticks++;
}
public IEnumerable<IRenderable> Render(WorldRenderer wr) { return SpriteRenderable.None; }
}
}

View File

@@ -0,0 +1,61 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Activities;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Effects
{
public class SpawnActorEffect : IEffect
{
readonly Actor actor;
readonly CPos[] pathAfterSpawn;
readonly Activity activityAtDestination;
readonly IMove move;
int remainingDelay;
public SpawnActorEffect(Actor actor)
: this(actor, 0, [], null) { }
public SpawnActorEffect(Actor actor, int delay)
: this(actor, delay, [], null) { }
public SpawnActorEffect(Actor actor, int delay, CPos[] pathAfterSpawn, Activity activityAtDestination)
{
this.actor = actor;
remainingDelay = delay;
this.pathAfterSpawn = pathAfterSpawn;
this.activityAtDestination = activityAtDestination;
move = actor.TraitOrDefault<IMove>();
}
public void Tick(World world)
{
if (remainingDelay-- > 0)
return;
world.Add(actor);
if (move != null)
for (var j = 0; j < pathAfterSpawn.Length; j++)
actor.QueueActivity(move.MoveTo(pathAfterSpawn[j], 2));
if (activityAtDestination != null)
actor.QueueActivity(activityAtDestination);
world.AddFrameEndTask(w => w.Remove(this));
}
public IEnumerable<IRenderable> Render(WorldRenderer wr) { return SpriteRenderable.None; }
}
}

View File

@@ -0,0 +1,47 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
namespace OpenRA.Mods.Common.Effects
{
public class SpriteAnnotation : IEffect, IEffectAnnotation
{
readonly string palette;
readonly Animation anim;
readonly WPos pos;
public SpriteAnnotation(WPos pos, World world, string image, string sequence, string palette)
{
this.palette = palette;
this.pos = pos;
anim = new Animation(world, image);
anim.PlayThen(sequence, () => world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); }));
world.ScreenMap.Add(this, pos, anim.Image);
}
void IEffect.Tick(World world)
{
anim.Tick();
world.ScreenMap.Update(this, pos, anim.Image);
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { yield break; }
IEnumerable<IRenderable> IEffectAnnotation.RenderAnnotation(WorldRenderer wr)
{
var screenPos = wr.Viewport.WorldToViewPx(wr.ScreenPxPosition(pos));
return anim.RenderUI(wr, screenPos, WVec.Zero, 0, wr.Palette(palette));
}
}
}

View File

@@ -0,0 +1,86 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
namespace OpenRA.Mods.Common.Effects
{
public class SpriteEffect : IEffect, ISpatiallyPartitionable
{
readonly World world;
readonly string palette;
readonly Animation anim;
readonly Func<WPos> posFunc;
readonly bool visibleThroughFog;
readonly string sequence;
WPos pos;
int delay;
bool initialized;
// Facing is last on these overloads partially for backwards compatibility with previous main ctor revision
// and partially because most effects don't need it. The latter is also the reason for placement of 'delay'.
public SpriteEffect(WPos pos, World world, string image, string sequence, string palette,
bool visibleThroughFog = false, int delay = 0)
: this(() => pos, () => WAngle.Zero, world, image, sequence, palette, visibleThroughFog, delay) { }
public SpriteEffect(Actor actor, World world, string image, string sequence, string palette,
bool visibleThroughFog = false, int delay = 0)
: this(() => actor.CenterPosition, () => WAngle.Zero, world, image, sequence, palette, visibleThroughFog, delay) { }
public SpriteEffect(WPos pos, WAngle facing, World world, string image, string sequence, string palette,
bool visibleThroughFog = false, int delay = 0)
: this(() => pos, () => facing, world, image, sequence, palette, visibleThroughFog, delay) { }
public SpriteEffect(Func<WPos> posFunc, Func<WAngle> facingFunc, World world, string image, string sequence, string palette,
bool visibleThroughFog = false, int delay = 0)
{
this.world = world;
this.posFunc = posFunc;
this.palette = palette;
this.sequence = sequence;
this.visibleThroughFog = visibleThroughFog;
this.delay = delay;
pos = posFunc();
anim = new Animation(world, image, facingFunc);
}
public void Tick(World world)
{
if (delay-- > 0)
return;
if (!initialized)
{
anim.PlayThen(sequence, () => world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); }));
world.ScreenMap.Add(this, pos, anim.Image);
initialized = true;
}
else
{
anim.Tick();
pos = posFunc();
world.ScreenMap.Update(this, pos, anim.Image);
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (!initialized || (!visibleThroughFog && world.FogObscures(pos)))
return SpriteRenderable.None;
return anim.Render(pos, wr.Palette(palette));
}
}
}