Skip to content

Commit 13d5751

Browse files
committed
pulsating effect andautotarget for artillery parachute
1 parent ad99211 commit 13d5751

File tree

3 files changed

+220
-10
lines changed

3 files changed

+220
-10
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright (c) The OpenRA Developers and Contributors
4+
* This file is part of OpenRA, which is free software. It is made
5+
* available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of
7+
* the License, or (at your option) any later version. For more
8+
* information, see COPYING.
9+
*/
10+
#endregion
11+
12+
using System;
13+
using System.Collections.Generic;
14+
using OpenRA.Effects;
15+
using OpenRA.Graphics;
16+
using OpenRA.Primitives;
17+
18+
namespace OpenRA.Mods.Common.Effects
19+
{
20+
public class PulsatingSpriteEffect : IEffect, ISpatiallyPartitionable
21+
{
22+
enum PulseStage { Grow, Hold, Shrink, Done }
23+
24+
readonly World world;
25+
readonly string palette;
26+
readonly Animation anim;
27+
readonly Func<WPos> posFunc;
28+
readonly Func<WAngle> facingFunc;
29+
readonly bool visibleThroughFog;
30+
readonly string sequence;
31+
readonly float startScale;
32+
readonly float peakScale;
33+
readonly float endScale;
34+
readonly int growTicks;
35+
readonly int holdTicks;
36+
readonly int shrinkTicks;
37+
readonly bool loop;
38+
39+
WPos pos;
40+
int delay;
41+
bool initialized;
42+
float currentScale;
43+
PulseStage stage = PulseStage.Grow;
44+
int stageTick;
45+
46+
public PulsatingSpriteEffect(WPos pos, World world, string image, string sequence, string palette,
47+
float startScale, float peakScale, float endScale,
48+
int growTicks, int holdTicks, int shrinkTicks, bool loop,
49+
bool visibleThroughFog = false, int delay = 0)
50+
: this(() => pos, () => WAngle.Zero, world, image, sequence, palette,
51+
startScale, peakScale, endScale, growTicks, holdTicks, shrinkTicks, loop, visibleThroughFog, delay) { }
52+
53+
public PulsatingSpriteEffect(Func<WPos> posFunc, World world, string image, string sequence, string palette,
54+
float startScale, float peakScale, float endScale,
55+
int growTicks, int holdTicks, int shrinkTicks, bool loop,
56+
bool visibleThroughFog = false, int delay = 0)
57+
: this(posFunc, () => WAngle.Zero, world, image, sequence, palette,
58+
startScale, peakScale, endScale, growTicks, holdTicks, shrinkTicks, loop, visibleThroughFog, delay) { }
59+
60+
public PulsatingSpriteEffect(Func<WPos> posFunc, Func<WAngle> facingFunc, World world, string image, string sequence, string palette,
61+
float startScale, float peakScale, float endScale,
62+
int growTicks, int holdTicks, int shrinkTicks, bool loop,
63+
bool visibleThroughFog = false, int delay = 0)
64+
{
65+
this.world = world;
66+
this.posFunc = posFunc;
67+
this.facingFunc = facingFunc ?? (() => WAngle.Zero);
68+
this.palette = palette;
69+
this.sequence = sequence;
70+
this.visibleThroughFog = visibleThroughFog;
71+
this.delay = delay;
72+
this.startScale = startScale;
73+
this.peakScale = peakScale;
74+
this.endScale = endScale;
75+
this.growTicks = growTicks;
76+
this.holdTicks = holdTicks;
77+
this.shrinkTicks = shrinkTicks;
78+
this.loop = loop;
79+
80+
pos = posFunc();
81+
anim = new Animation(world, image, this.facingFunc);
82+
currentScale = startScale;
83+
}
84+
85+
public void Tick(World world)
86+
{
87+
if (delay-- > 0)
88+
return;
89+
90+
if (!initialized)
91+
{
92+
anim.PlayThen(sequence, () => world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); }));
93+
world.ScreenMap.Add(this, pos, anim.Image);
94+
initialized = true;
95+
}
96+
else
97+
{
98+
anim.Tick();
99+
UpdateScale();
100+
101+
pos = posFunc();
102+
world.ScreenMap.Update(this, pos, anim.Image);
103+
}
104+
}
105+
106+
void UpdateScale()
107+
{
108+
switch (stage)
109+
{
110+
case PulseStage.Grow:
111+
if (growTicks <= 0)
112+
{
113+
currentScale = peakScale;
114+
AdvanceStage(PulseStage.Hold);
115+
return;
116+
}
117+
118+
currentScale = Lerp(startScale, peakScale, stageTick, growTicks);
119+
stageTick++;
120+
if (stageTick >= growTicks)
121+
AdvanceStage(PulseStage.Hold);
122+
123+
break;
124+
case PulseStage.Hold:
125+
currentScale = peakScale;
126+
if (holdTicks <= 0)
127+
{
128+
AdvanceStage(PulseStage.Shrink);
129+
return;
130+
}
131+
132+
stageTick++;
133+
if (stageTick >= holdTicks)
134+
AdvanceStage(PulseStage.Shrink);
135+
136+
break;
137+
case PulseStage.Shrink:
138+
if (shrinkTicks <= 0)
139+
{
140+
currentScale = endScale;
141+
CompleteCycle();
142+
return;
143+
}
144+
145+
currentScale = Lerp(peakScale, endScale, stageTick, shrinkTicks);
146+
stageTick++;
147+
if (stageTick >= shrinkTicks)
148+
CompleteCycle();
149+
150+
break;
151+
case PulseStage.Done:
152+
currentScale = endScale;
153+
break;
154+
}
155+
}
156+
157+
static float Lerp(float from, float to, int tick, int duration)
158+
{
159+
if (duration <= 0)
160+
return to;
161+
162+
var progress = Math.Min(1f, tick / (float)duration);
163+
return from + (to - from) * progress;
164+
}
165+
166+
void AdvanceStage(PulseStage next)
167+
{
168+
stage = next;
169+
stageTick = 0;
170+
}
171+
172+
void CompleteCycle()
173+
{
174+
if (loop)
175+
AdvanceStage(PulseStage.Grow);
176+
else
177+
stage = PulseStage.Done;
178+
}
179+
180+
public IEnumerable<IRenderable> Render(WorldRenderer wr)
181+
{
182+
if (!initialized || (!visibleThroughFog && world.FogObscures(pos)))
183+
return SpriteRenderable.None;
184+
185+
var sequence = anim.CurrentSequence;
186+
if (sequence == null)
187+
return SpriteRenderable.None;
188+
189+
var facing = facingFunc();
190+
var tintModifiers = sequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
191+
var alpha = sequence.GetAlpha(anim.CurrentFrame);
192+
var (image, rotation) = sequence.GetSpriteWithRotation(anim.CurrentFrame, facing);
193+
var scale = sequence.Scale * currentScale;
194+
195+
var imageRenderable = new SpriteRenderable(
196+
image, pos, WVec.Zero, sequence.ZOffset, wr.Palette(palette),
197+
scale, alpha, float3.Ones, tintModifiers, anim.IsDecoration, rotation);
198+
199+
var shadow = sequence.GetShadow(anim.CurrentFrame, facing);
200+
if (shadow != null)
201+
{
202+
var height = world.Map.DistanceAboveTerrain(pos).Length;
203+
204+
var shadowRenderable = new SpriteRenderable(
205+
shadow, pos, -new WVec(0, 0, height), sequence.ShadowZOffset + height, wr.Palette(palette),
206+
scale, 1f, float3.Ones, tintModifiers, true, rotation);
207+
208+
return new IRenderable[] { shadowRenderable, imageRenderable };
209+
}
210+
211+
return new IRenderable[] { imageRenderable };
212+
}
213+
}
214+
}

OpenRA.Mods.Common/Traits/AutoTarget.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,8 @@ Target ChooseTarget(Actor self, AttackBase ab, PlayerRelationship attackStances,
360360
var chosenTargetRange = 0;
361361

362362
var activePriorities = activeTargetPriorities.ToList();
363-
if (activePriorities.Count == 0)
364-
365-
\t\t\tif (self.Owner == null)
366-
\t\t\t\treturn Target.Invalid;
367-
return chosenTarget;
363+
if (activePriorities.Count == 0 || self.Owner == null)
364+
return Target.Invalid;
368365

369366
var targetsInRange = self.World.FindActorsInCircle(self.CenterPosition, scanRange)
370367
.Select(Target.FromActor);
@@ -389,9 +386,8 @@ Target ChooseTarget(Actor self, AttackBase ab, PlayerRelationship attackStances,
389386

390387
// Check whether we can auto-target this actor
391388
targetTypes = target.Actor.GetEnabledTargetTypes();
392-
393-
\t\t\t\tif (target.Actor.Owner == null)
394-
\t\t\t\t\tcontinue;
389+
if (target.Actor.Owner == null)
390+
continue;
395391
if (PreventsAutoTarget(self, target.Actor) || !target.Actor.CanBeViewedByPlayer(self.Owner))
396392
continue;
397393

OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public class CreateEffectWarhead : Warhead
6060
static readonly BitSet<TargetableType> TargetTypeAir = new("Air");
6161

6262
/// <summary>Checks if there are any actors at impact position and if the warhead is valid against any of them.</summary>
63-
ImpactActorType ActorTypeAtImpact(World world, WPos pos, Actor firedBy)
63+
protected ImpactActorType ActorTypeAtImpact(World world, WPos pos, Actor firedBy)
6464
{
6565
var anyInvalidActor = false;
6666

@@ -143,7 +143,7 @@ public override void DoImpact(in Target target, WarheadArgs args)
143143
}
144144

145145
/// <summary>Checks if the warhead is valid against the terrain at impact position.</summary>
146-
bool IsValidAgainstTerrain(World world, WPos pos)
146+
protected bool IsValidAgainstTerrain(World world, WPos pos)
147147
{
148148
var cell = world.Map.CellContaining(pos);
149149
if (!world.Map.Contains(cell))

0 commit comments

Comments
 (0)