Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions OpenRA.Mods.CA/Projectiles/SpriteAthenaLaser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#region Copyright & License Information
/**
* Copyright (c) The OpenRA Combined Arms Developers (see CREDITS).
* This file is part of OpenRA Combined Arms, 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.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;

namespace OpenRA.Mods.CA.Projectiles
{
[Desc("Generate laser connect by image with different height offset, and trigger warheads on the ground until expires.")]
class SpriteAthenaLaserInfo : IProjectileInfo
{
[FieldLoader.Require]
[Desc("Laser Image to display.")]
public readonly string Image = null;

[SequenceReference(nameof(Image), allowNullImage: false)]
[Desc("Laser sprite Sequence of Image from this list while this projectile is moving.")]
public readonly string Sequence = "idle";

[Desc("Number of the laser sprite to form the beam.")]
public readonly int SpriteNumber = 8;

[Desc("Offset of laser sprite to form the beam.")]
public readonly int HeightOffset = 1024;

[Desc("Laser ring image to display.")]
public readonly string RingImage = null;

[SequenceReference(nameof(RingImage), allowNullImage: true)]
[Desc("Sequence of laser ring image from this list while this projectile is moving.")]
public readonly string RingSequence = "idle";

[PaletteReference(nameof(IsPlayerPalette))]
[Desc("The palette used to draw this projectile.")]
public readonly string Palette = "effect";

[Desc("Palette is a player palette BaseName")]
public readonly bool IsPlayerPalette = false;

[Desc("Projectile speed in WDist / tick.")]
public readonly WDist Speed = new(90);

[Desc("Rotation speed around the target.")]
public readonly WAngle RotSpeed = WAngle.Zero;

[Desc("Rotation speed slowly add to max.")]
public readonly bool RotStartFromZero = true;

[Desc("How many ticks will pass between explosions.")]
public readonly int ExplosionInterval = 3;

[Desc("How many ticks will the projectile pierce even after reach the target location.")]
public readonly int PierceTicks = 0;

[Desc("How many ticks will the projectile stay after motion.")]
public readonly int StayTicks = 8;

public IProjectile Create(ProjectileArgs args) { return new SpriteAthenaLaser(this, args); }
}

class SpriteAthenaLaser : IProjectile
{
readonly int explosionInterval;
readonly ProjectileArgs args;
readonly WarheadArgs warheadArgs;
readonly Animation[] animations;
readonly Animation ringAnim;
readonly int heightoffset;
readonly int rotAcc;
readonly int speed;
readonly World world;
readonly string paletteName;
readonly int length, flightticks, maxticks;
readonly WPos target;
readonly WPos source;

WPos projectilepos;
WAngle rot;
int ticks;
int rotSpeed;

protected bool FlightLengthReached => ticks > flightticks;

protected bool LifeExpired => ticks > maxticks;

public SpriteAthenaLaser(SpriteAthenaLaserInfo info, ProjectileArgs args)
{
this.args = args;
warheadArgs = new WarheadArgs(args);
speed = info.Speed.Length;

world = args.SourceActor.World;
source = new WPos(args.Source.X, args.Source.Y, 0);
target = new WPos(args.PassiveTarget.X, args.PassiveTarget.Y, 0);
length = Math.Max((target - source).Length / Math.Max(speed, 1), 1);
rotSpeed = info.RotStartFromZero ? 0 : info.RotSpeed.Angle * 1000;
rotAcc = info.RotStartFromZero ? info.RotSpeed.Angle * 1000 / length : 0;

projectilepos = source - new WVec(0, 0, world.Map.DistanceAboveTerrain(source).Length);
flightticks = length + info.PierceTicks;
maxticks = length + info.PierceTicks + info.StayTicks;
explosionInterval = Math.Max(info.ExplosionInterval, 1);
heightoffset = info.HeightOffset;

paletteName = info.Palette;
if (paletteName != null && info.IsPlayerPalette)
paletteName += args.SourceActor.Owner.InternalName;

if (!string.IsNullOrEmpty(info.Image))
{
animations = new Animation[info.SpriteNumber];

for (var i = 0; i < animations.Length; i++)
{
animations[i] = new Animation(world, info.Image);
animations[i].PlayRepeating(info.Sequence);
}
}

if (!string.IsNullOrEmpty(info.RingImage))
{
ringAnim = new Animation(world, info.RingImage);
ringAnim.PlayRepeating(info.RingSequence);
}
}

IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr)
{
if (LifeExpired)
yield break;

foreach (var r in RenderAnimation(wr))
yield return r;
}

void IEffect.Tick(World world)
{
ticks++;

if (ticks % explosionInterval == 0)
{
warheadArgs.ImpactPosition = projectilepos;
args.Weapon.Impact(Target.FromPos(projectilepos), warheadArgs);
}

ringAnim?.Tick();
for (var i = 0; i < animations.Length; i++)
animations[i].Tick();

rotSpeed += rotAcc;
if (!FlightLengthReached)
{
var pos = projectilepos;
if (speed != 0)
pos = WPos.Lerp(source, target, ticks, length);

if (rotSpeed != 0)
{
rot += new WAngle(rotSpeed / 1000);
pos = target + (pos - target).Rotate(WRot.FromYaw(rot));
}

projectilepos = pos - new WVec(0, 0, world.Map.DistanceAboveTerrain(pos).Length);
}

if (LifeExpired)
world.AddFrameEndTask(w => w.Remove(this));
}

protected IEnumerable<IRenderable> RenderAnimation(WorldRenderer wr)
{
var renderpos = projectilepos;
var palette = wr.Palette(paletteName);

if (world.FogObscures(projectilepos))
yield break;

if (ringAnim != null)
{
foreach (var r in ringAnim.Render(renderpos, palette))
yield return r;
}

for (var i = 0; i < animations.Length; i++)
{
foreach (var r in animations[i].Render(renderpos, palette))
yield return r;

renderpos += new WVec(0, 0, heightoffset);
}
}
}
}
114 changes: 114 additions & 0 deletions OpenRA.Mods.CA/Warheads/FireReverseRadiusWarhead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#region Copyright & License Information
/*
* Copyright 2015- OpenRA.Mods.AS Developers (see AUTHORS)
* This file is a part of a third-party plugin for 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. For more information, see COPYING.
*/
#endregion

using System;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;

namespace OpenRA.Mods.CA.Warheads
{
[Desc("Fires a defined amount of weapons with their maximum range in a reverse wave pattern.")]
public class FireReverseRadiusWarhead : WarheadAS, IRulesetLoaded<WeaponInfo>
{
[WeaponReference]
[FieldLoader.Require]
[Desc("Has to be defined in weapons.yaml as well.")]
public readonly string Weapon = null;

[Desc("Amount of weapons fired.")]
public readonly int[] Amount = { 1 };

[Desc("Should the weapons be fired around the intended target or at the explosion's epicenter.")]
public readonly bool AroundTarget = false;

WeaponInfo weapon;

public void RulesetLoaded(Ruleset rules, WeaponInfo info)
{
if (!rules.Weapons.TryGetValue(Weapon.ToLowerInvariant(), out weapon))
throw new YamlException($"Weapons Ruleset does not contain an entry '{Weapon.ToLowerInvariant()}'");
}

public override void DoImpact(in Target target, WarheadArgs args)
{
var firedBy = args.SourceActor;
if (!target.IsValidFor(firedBy))
return;

var world = firedBy.World;
var map = world.Map;

if (!IsValidImpact(target.CenterPosition, firedBy))
return;

var epicenter = AroundTarget && args.WeaponTarget.Type != TargetType.Invalid
? args.WeaponTarget.CenterPosition
: target.CenterPosition;

var amount = Amount.Length == 2
? world.SharedRandom.Next(Amount[0], Amount[1])
: Amount[0];

var offset = 1024 / amount;

for (var i = 0; i < amount; i++)
{
var radiusSource = Target.Invalid;

var rotation = WRot.FromYaw(new WAngle(i * offset));
var targetpos = epicenter + new WVec(weapon.Range.Length, 0, 0).Rotate(rotation);
radiusSource = Target.FromPos(new WPos(targetpos.X, targetpos.Y, map.CenterOfCell(map.CellContaining(targetpos)).Z));

if (radiusSource.Type == TargetType.Invalid)
continue;

// Lambdas can't use 'in' variables, so capture a copy for later
var centerPosition = target.CenterPosition;

var projectileArgs = new ProjectileArgs
{
Weapon = weapon,
Facing = (target.CenterPosition - radiusSource.CenterPosition).Yaw,
CurrentMuzzleFacing = () => (centerPosition - radiusSource.CenterPosition).Yaw,

DamageModifiers = args.DamageModifiers,

InaccuracyModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing<IInaccuracyModifier>()
.Select(a => a.GetInaccuracyModifier()).ToArray() : Array.Empty<int>(),

RangeModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing<IRangeModifier>()
.Select(a => a.GetRangeModifier()).ToArray() : Array.Empty<int>(),

Source = radiusSource.CenterPosition,
CurrentSource = () => radiusSource.CenterPosition,
SourceActor = firedBy,
GuidedTarget = target,
PassiveTarget = target.CenterPosition
};

if (projectileArgs.Weapon.Projectile != null)
{
var projectile = projectileArgs.Weapon.Projectile.Create(projectileArgs);
if (projectile != null)
firedBy.World.AddFrameEndTask(w => w.Add(projectile));

if (projectileArgs.Weapon.Report != null && projectileArgs.Weapon.Report.Length > 0)
{
var pos = target.CenterPosition;
if ((!firedBy.World.ShroudObscures(pos) && !firedBy.World.FogObscures(pos)))
Game.Sound.Play(SoundType.World, projectileArgs.Weapon.Report, firedBy.World, pos, null);
}
}
}
}
}
}
Binary file added mods/ca/bits/tsionbeam.shp
Binary file not shown.
Binary file added mods/ca/bits/tsionbeamstart.shp
Binary file not shown.
Binary file added mods/ca/bits/tsring1.shp
Binary file not shown.
Binary file added mods/ca/bits/tsringmini2.shp
Binary file not shown.
1 change: 1 addition & 0 deletions mods/ca/rules/powers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@
^SurgicalStrikePower:
IonCannonPower@SurgicalStrike:
OrderName: surgicalstrike
Weapon: TSIonCannonSpawner
Prerequisites: ~eye.zocom
Icon: surgicalstrike
IconPalette: chrometd
Expand Down
31 changes: 31 additions & 0 deletions mods/ca/sequences/misc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3371,5 +3371,36 @@ laserhit:
Frames: 0, 0, 0, 0
Alpha: 1, 0.8, 0.6, 0.3
idle1:

idle2:
FlipX: true

tsioncannon:
Defaults:
Length: *
ZOffset: 2047
Tick: 35
IgnoreWorldTint: true
ionring:
Filename: tsring1.shp
ZRamp: 1
Alpha: 0.75
Tick: 50
ionbeam:
Filename: tsionbeam.shp
Offset: 0, -60, 60
ZRamp: 1
Alpha: 0.75
Tick: 40
ionbeamstart:
Filename: tsionbeamstart.shp
Offset: 0, -60, 60
ZRamp: 1
Alpha: 0.75
Tick: 30
ringmini2:
Filename: tsringmini2.shp
Alpha: 0.75
Offset: 0,-6
Tick: 5
Scale: 0.8
Loading
Loading