Move everything Capture-related + Transform activity to Mods.Common

This commit is contained in:
penev92
2015-01-06 02:10:15 +02:00
parent 18ac3953cc
commit 37cdcaf354
11 changed files with 29 additions and 32 deletions

View File

@@ -0,0 +1,77 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Activities
{
public class CaptureActor : Enter
{
readonly Actor actor;
readonly Capturable capturable;
readonly CapturesInfo capturesInfo;
public CaptureActor(Actor self, Actor target)
: base(self, target)
{
actor = target;
capturesInfo = self.Info.Traits.Get<CapturesInfo>();
capturable = target.Trait<Capturable>();
}
protected override bool CanReserve(Actor self)
{
return !capturable.BeingCaptured && capturable.Info.CanBeTargetedBy(self, actor.Owner);
}
protected override void OnInside(Actor self)
{
if (actor.IsDead || capturable.BeingCaptured)
return;
var b = actor.TraitOrDefault<Building>();
if (b != null && !b.Lock())
return;
self.World.AddFrameEndTask(w =>
{
if (b != null && b.Locked)
b.Unlock();
if (actor.IsDead || capturable.BeingCaptured)
return;
var health = actor.Trait<Health>();
var lowEnoughHealth = health.HP <= capturable.Info.CaptureThreshold * health.MaxHP;
if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant)
{
var oldOwner = actor.Owner;
actor.ChangeOwner(self.Owner);
foreach (var t in actor.TraitsImplementing<INotifyCapture>())
t.OnCapture(actor, self, oldOwner, self.Owner);
if (b != null && b.Locked)
b.Unlock();
}
else
{
var damage = (int)(health.MaxHP * capturesInfo.SabotageHPRemoval);
actor.InflictDamage(self, damage, null);
}
self.Destroy();
});
}
}
}

View File

@@ -0,0 +1,94 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Activities
{
public class Transform : Activity
{
public readonly string ToActor;
public CVec Offset = CVec.Zero;
public int Facing = 96;
public string[] Sounds = { };
public int ForceHealthPercentage = 0;
public bool SkipMakeAnims = false;
public string Race = null;
public Transform(Actor self, string toActor)
{
ToActor = toActor;
}
public override Activity Tick(Actor self)
{
if (IsCanceled)
return NextActivity;
self.World.AddFrameEndTask(w =>
{
if (self.IsDead)
return;
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.OnTransform(self);
var selected = w.Selection.Contains(self);
var controlgroup = w.Selection.GetControlGroupForActor(self);
self.Destroy();
foreach (var s in Sounds)
Sound.PlayToPlayer(self.Owner, s, self.CenterPosition);
var init = new TypeDictionary
{
new LocationInit(self.Location + Offset),
new OwnerInit(self.Owner),
new FacingInit(Facing),
};
if (SkipMakeAnims)
init.Add(new SkipMakeAnimsInit());
if (Race != null)
init.Add(new RaceInit(Race));
var health = self.TraitOrDefault<Health>();
if (health != null)
{
var newHP = (ForceHealthPercentage > 0)
? ForceHealthPercentage / 100f
: (float)health.HP / health.MaxHP;
init.Add(new HealthInit(newHP));
}
var cargo = self.TraitOrDefault<Cargo>();
if (cargo != null)
init.Add(new RuntimeCargoInit(cargo.Passengers.ToArray()));
var a = w.CreateActor(ToActor, init);
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.AfterTransform(a);
if (selected)
w.Selection.Add(w, a);
if (controlgroup.HasValue)
w.Selection.AddToControlGroup(a, controlgroup.Value);
});
return this;
}
}
}

View File

@@ -72,6 +72,7 @@
<Compile Include="Activities\Air\ReturnToBase.cs" />
<Compile Include="Activities\Air\TakeOff.cs" />
<Compile Include="Activities\Attack.cs" />
<Compile Include="Activities\CaptureActor.cs" />
<Compile Include="Activities\Enter.cs" />
<Compile Include="Activities\EnterTransport.cs" />
<Compile Include="Activities\ExternalCaptureActor.cs" />
@@ -90,6 +91,7 @@
<Compile Include="Activities\RepairBuilding.cs" />
<Compile Include="Activities\Sell.cs" />
<Compile Include="Activities\SimpleTeleport.cs" />
<Compile Include="Activities\Transform.cs" />
<Compile Include="Activities\Turn.cs" />
<Compile Include="Activities\UnloadCargo.cs" />
<Compile Include="Activities\Wait.cs" />
@@ -198,6 +200,9 @@
<Compile Include="Traits\Buildings\Reservable.cs" />
<Compile Include="Traits\Buildings\TargetableBuilding.cs" />
<Compile Include="Traits\Burns.cs" />
<Compile Include="Traits\Capturable.cs" />
<Compile Include="Traits\CaptureNotification.cs" />
<Compile Include="Traits\Captures.cs" />
<Compile Include="Traits\Cargo.cs" />
<Compile Include="Traits\CashTrickler.cs" />
<Compile Include="Traits\Cloak.cs" />
@@ -256,6 +261,8 @@
<Compile Include="Traits\Power\RequiresPower.cs" />
<Compile Include="Traits\Power\ScalePowerWithHealth.cs" />
<Compile Include="Traits\ProvidesRadar.cs" />
<Compile Include="Traits\ProximityCaptor.cs" />
<Compile Include="Traits\ProximityCapturable.cs" />
<Compile Include="Traits\RadarColorFromTerrain.cs" />
<Compile Include="Traits\Reloads.cs" />
<Compile Include="Traits\Render\Hovers.cs" />
@@ -307,6 +314,7 @@
<Compile Include="Traits\TargetableUnit.cs" />
<Compile Include="Traits\ThrowsParticle.cs" />
<Compile Include="Traits\Tooltip.cs" />
<Compile Include="Traits\TransformOnCapture.cs" />
<Compile Include="Traits\Turreted.cs" />
<Compile Include="Traits\Upgrades\UpgradableTrait.cs" />
<Compile Include="Traits\Upgrades\UpgradeManager.cs" />

View File

@@ -0,0 +1,72 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This actor can be captured by a unit with Captures: trait.")]
public class CapturableInfo : ITraitInfo
{
[Desc("Type listed under Types in Captures: trait of actors that can capture this).")]
public readonly string Type = "building";
public readonly bool AllowAllies = false;
public readonly bool AllowNeutral = true;
public readonly bool AllowEnemies = true;
[Desc("Health percentage the target must be at (or below) before it can be captured.")]
public readonly float CaptureThreshold = 0.5f;
public readonly bool CancelActivity = false;
public object Create(ActorInitializer init) { return new Capturable(this); }
public bool CanBeTargetedBy(Actor captor, Player owner)
{
var c = captor.TraitOrDefault<Captures>();
if (c == null)
return false;
var playerRelationship = owner.Stances[captor.Owner];
if (playerRelationship == Stance.Ally && !AllowAllies)
return false;
if (playerRelationship == Stance.Enemy && !AllowEnemies)
return false;
if (playerRelationship == Stance.Neutral && !AllowNeutral)
return false;
if (!c.Info.CaptureTypes.Contains(Type))
return false;
return true;
}
}
public class Capturable : INotifyCapture
{
public readonly CapturableInfo Info;
public bool BeingCaptured { get; private set; }
public Capturable(CapturableInfo info) { Info = info; }
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
{
BeingCaptured = true;
self.World.AddFrameEndTask(w => BeingCaptured = false);
if (Info.CancelActivity)
{
var stop = new Order("Stop", self, false);
foreach (var t in self.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(self, stop);
}
}
}
}

View File

@@ -0,0 +1,40 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class CaptureNotificationInfo : ITraitInfo
{
public readonly string Notification = "BuildingCaptured";
public readonly bool NewOwnerVoice = true;
public object Create(ActorInitializer init) { return new CaptureNotification(this); }
}
public class CaptureNotification : INotifyCapture
{
CaptureNotificationInfo info;
public CaptureNotification(CaptureNotificationInfo info)
{
this.info = info;
}
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
{
if (captor.World.LocalPlayer != captor.Owner)
return;
var race = info.NewOwnerVoice ? newOwner.Country.Race : oldOwner.Country.Race;
Sound.PlayNotification(self.World.Map.Rules, captor.World.LocalPlayer, "Speech", info.Notification, race);
}
}
}

View File

@@ -0,0 +1,127 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This actor can capture other actors which have the Capturable: trait.")]
public class CapturesInfo : ITraitInfo
{
[Desc("Types of actors that it can capture, as long as the type also exists in the Capturable Type: trait.")]
public readonly string[] CaptureTypes = { "building" };
[Desc("Unit will do damage to the actor instead of capturing it. Unit is destroyed when sabotaging.")]
public readonly bool Sabotage = true;
[Desc("Only used if Sabotage=true. Sabotage damage expressed as a percentage of enemy health removed.")]
public readonly float SabotageHPRemoval = 0.5f;
public object Create(ActorInitializer init) { return new Captures(init.Self, this); }
}
public class Captures : IIssueOrder, IResolveOrder, IOrderVoice
{
public readonly CapturesInfo Info;
public Captures(Actor self, CapturesInfo info)
{
Info = info;
}
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new CaptureOrderTargeter(Info.Sabotage);
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID != "CaptureActor")
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return order.OrderString == "CaptureActor" ? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString != "CaptureActor")
return;
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new CaptureActor(self, target.Actor));
}
class CaptureOrderTargeter : UnitOrderTargeter
{
readonly bool sabotage;
public CaptureOrderTargeter(bool sabotage)
: base("CaptureActor", 6, "enter", true, true)
{
this.sabotage = sabotage;
}
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
var c = target.Info.Traits.GetOrDefault<CapturableInfo>();
if (c == null || !c.CanBeTargetedBy(self, target.Owner))
{
cursor = "enter-blocked";
return false;
}
var health = target.Trait<Health>();
var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP;
cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant
? "enter" : "capture";
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var c = target.Info.Traits.GetOrDefault<CapturableInfo>();
if (c == null || !c.CanBeTargetedBy(self, target.Owner))
{
cursor = "enter-blocked";
return false;
}
var health = target.Info.Traits.GetOrDefault<HealthInfo>();
var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP;
cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant
? "enter" : "capture";
return true;
}
}
}
}

View File

@@ -0,0 +1,34 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Actor can capture ProximityCapturable actors.")]
public class ProximityCaptorInfo : ITraitInfo
{
public readonly string[] Types = { };
public object Create(ActorInitializer init) { return new ProximityCaptor(this); }
}
public class ProximityCaptor
{
public readonly ProximityCaptorInfo Info;
public ProximityCaptor(ProximityCaptorInfo info) { Info = info; }
public bool HasAny(string[] typesList)
{
return typesList.Any(flag => Info.Types.Contains(flag));
}
}
}

View File

@@ -0,0 +1,149 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Actor can be captured by units in a specified proximity.")]
public class ProximityCapturableInfo : ITraitInfo
{
public readonly bool Permanent = false;
public readonly int Range = 5;
public readonly bool MustBeClear = false;
public readonly string[] CaptorTypes = { "Vehicle", "Tank", "Infantry" };
public object Create(ActorInitializer init) { return new ProximityCapturable(init.Self, this); }
}
public class ProximityCapturable : ITick
{
public readonly Player OriginalOwner;
public bool Captured { get { return Self.Owner != OriginalOwner; } }
public ProximityCapturableInfo Info;
public Actor Self;
public ProximityCapturable(Actor self, ProximityCapturableInfo info)
{
Info = info;
Self = self;
OriginalOwner = self.Owner;
}
public void Tick(Actor self)
{
if (Captured && Info.Permanent) return; // Permanent capture
if (!Captured)
{
var captor = GetInRange(self);
if (captor != null)
if (!Info.MustBeClear || IsClear(self, captor.Owner, OriginalOwner))
ChangeOwnership(self, captor);
return;
}
// if the area must be clear, and there is more than 1 player nearby => return ownership to default
if (Info.MustBeClear && !IsClear(self, self.Owner, OriginalOwner))
{
// Revert Ownership
ChangeOwnership(self, OriginalOwner.PlayerActor);
return;
}
// See if the 'temporary' owner still is in range
if (!IsStillInRange(self))
{
// no.. So find a new one
var captor = GetInRange(self);
// got one
if (captor != null)
{
ChangeOwnership(self, captor);
return;
}
// Revert Ownership otherwise
ChangeOwnership(self, OriginalOwner.PlayerActor);
}
}
static void ChangeOwnership(Actor self, Actor captor)
{
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed || captor.Destroyed) return;
var previousOwner = self.Owner;
// momentarily remove from world so the ownership queries don't get confused
w.Remove(self);
self.Owner = captor.Owner;
w.Add(self);
if (self.Owner == self.World.LocalPlayer)
w.Add(new FlashTarget(self));
foreach (var t in self.TraitsImplementing<INotifyCapture>())
t.OnCapture(self, captor, previousOwner, self.Owner);
});
}
bool CanBeCapturedBy(Actor a)
{
var pc = a.TraitOrDefault<ProximityCaptor>();
return pc != null && pc.HasAny(Info.CaptorTypes);
}
IEnumerable<Actor> UnitsInRange()
{
return Self.World.FindActorsInCircle(Self.CenterPosition, WRange.FromCells(Info.Range))
.Where(a => a.IsInWorld && a != Self && !a.Destroyed && !a.Owner.NonCombatant);
}
bool IsClear(Actor self, Player currentOwner, Player originalOwner)
{
return UnitsInRange()
.All(a => a.Owner == originalOwner || a.Owner == currentOwner ||
WorldUtils.AreMutualAllies(a.Owner, currentOwner) || !CanBeCapturedBy(a));
}
// TODO exclude other NeutralActor that arent permanent
bool IsStillInRange(Actor self)
{
return UnitsInRange().Any(a => a.Owner == self.Owner && CanBeCapturedBy(a));
}
IEnumerable<Actor> CaptorsInRange(Actor self)
{
return UnitsInRange()
.Where(a => a.Owner != OriginalOwner && CanBeCapturedBy(a));
}
// TODO exclude other NeutralActor that arent permanent
Actor GetInRange(Actor self)
{
return CaptorsInRange(self).ClosestTo(self);
}
int CountPlayersNear(Actor self, Player ignoreMe)
{
return CaptorsInRange(self).Select(a => a.Owner).Where(p => p != ignoreMe)
.Distinct().Count();
}
}
}

View File

@@ -0,0 +1,41 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using OpenRA.Mods.Common.Activities;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public class TransformOnCaptureInfo : ITraitInfo
{
[ActorReference] public readonly string IntoActor = null;
public readonly int ForceHealthPercentage = 0;
public readonly bool SkipMakeAnims = true;
public virtual object Create(ActorInitializer init) { return new TransformOnCapture(this); }
}
public class TransformOnCapture : INotifyCapture
{
readonly TransformOnCaptureInfo info;
public TransformOnCapture(TransformOnCaptureInfo info) { this.info = info; }
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
{
var facing = self.TraitOrDefault<IFacing>();
var transform = new Transform(self, info.IntoActor) { ForceHealthPercentage = info.ForceHealthPercentage };
if (facing != null) transform.Facing = facing.Facing;
transform.SkipMakeAnims = info.SkipMakeAnims;
self.CancelActivity();
self.QueueActivity(transform);
}
}
}