Files
OpenRA/OpenRA.Mods.Common/Traits/ParaDrop.cs
reaperrr 2473b8763b Rename methods/activities with Visual in them
While they may be only 'visual' in terms of influence/cell grid,
they all do update CenterPosition, which is essentially the
actual world position of the actor.
'Visual' would imply that it only affects the position where the
actor is drawn, which is inaccurate.
Furthermore, using the term 'Visual' here would make
naming future methods/properties related to visual interpolation
unnecessarily complicated, because that's where we might
need a real 'Visual(Only)Position'.
2021-03-08 11:19:11 +01:00

122 lines
3.2 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* 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 OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This unit can spawn and eject other actors while flying.")]
public class ParaDropInfo : TraitInfo, Requires<CargoInfo>
{
[Desc("Distance around the drop-point to unload troops.")]
public readonly WDist DropRange = WDist.FromCells(4);
[Desc("Wait at least this many ticks between each drop.")]
public readonly int DropInterval = 5;
[Desc("Sound to play when dropping.")]
public readonly string ChuteSound = null;
public override object Create(ActorInitializer init) { return new ParaDrop(init.Self, this); }
}
public class ParaDrop : ITick, ISync, INotifyRemovedFromWorld
{
readonly ParaDropInfo info;
readonly Actor self;
readonly Cargo cargo;
public event Action<Actor> OnRemovedFromWorld = self => { };
public event Action<Actor> OnEnteredDropRange = self => { };
public event Action<Actor> OnExitedDropRange = self => { };
[Sync]
bool inDropRange;
[Sync]
Target target;
[Sync]
int dropDelay;
bool checkForSuitableCell;
public ParaDrop(Actor self, ParaDropInfo info)
{
this.info = info;
this.self = self;
cargo = self.Trait<Cargo>();
}
public void SetLZ(CPos lz, bool checkLandingCell)
{
target = Target.FromCell(self.World, lz);
checkForSuitableCell = checkLandingCell;
}
void ITick.Tick(Actor self)
{
if (dropDelay > 0)
{
dropDelay--;
return;
}
var wasInDropRange = inDropRange;
inDropRange = target.IsInRange(self.CenterPosition, info.DropRange);
if (inDropRange && !wasInDropRange)
OnEnteredDropRange(self);
if (!inDropRange && wasInDropRange)
OnExitedDropRange(self);
// Are we able to drop the next trooper?
if (!inDropRange || cargo.IsEmpty(self) || !self.World.Map.Contains(self.Location))
return;
var dropActor = cargo.Peek(self);
var dropPositionable = dropActor.Trait<IPositionable>();
var dropCell = self.Location;
var dropSubCell = dropPositionable.GetAvailableSubCell(dropCell);
if (dropSubCell == SubCell.Invalid)
{
if (checkForSuitableCell)
return;
dropSubCell = SubCell.Any;
}
// Unload here
if (cargo.Unload(self) != dropActor)
throw new InvalidOperationException("Peeked cargo was not unloaded!");
self.World.AddFrameEndTask(w =>
{
dropPositionable.SetPosition(dropActor, dropCell, dropSubCell);
var dropPosition = dropActor.CenterPosition + new WVec(0, 0, self.CenterPosition.Z - dropActor.CenterPosition.Z);
dropPositionable.SetCenterPosition(dropActor, dropPosition);
w.Add(dropActor);
});
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
dropDelay = info.DropInterval;
}
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
{
OnRemovedFromWorld(self);
}
}
}