Move everything Repair-related to Mods.Common

This commit is contained in:
penev92
2015-01-06 01:44:36 +02:00
parent bcd5160604
commit 8504c233d9
12 changed files with 21 additions and 30 deletions

View File

@@ -0,0 +1,65 @@
#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.Graphics;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Effects
{
class RepairIndicator : IEffect
{
readonly Actor building;
readonly string palettePrefix;
readonly Animation anim;
readonly RepairableBuilding rb;
int shownPlayer = 0;
public RepairIndicator(Actor building, string palettePrefix)
{
this.building = building;
this.palettePrefix = palettePrefix;
rb = building.TraitOrDefault<RepairableBuilding>();
anim = new Animation(building.World, "allyrepair");
anim.Paused = () => !rb.RepairActive || rb.IsTraitDisabled;
CycleRepairer();
}
public void Tick(World world)
{
if (!building.IsInWorld || building.IsDead ||
rb == null || !rb.Repairers.Any())
world.AddFrameEndTask(w => w.Remove(this));
anim.Tick();
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (building.Destroyed || wr.World.FogObscures(building) || rb.Repairers.Count == 0)
return SpriteRenderable.None;
var palette = wr.Palette(palettePrefix + rb.Repairers[shownPlayer % rb.Repairers.Count].InternalName);
return anim.Render(building.CenterPosition, palette);
}
void CycleRepairer()
{
anim.PlayThen("repair", CycleRepairer);
if (++shownPlayer == rb.Repairers.Count)
shownPlayer = 0;
}
}
}

View File

@@ -108,6 +108,7 @@
<Compile Include="Effects\PowerdownIndicator.cs" />
<Compile Include="Effects\RallyPointIndicator.cs" />
<Compile Include="Effects\Rank.cs" />
<Compile Include="Effects\RepairIndicator.cs" />
<Compile Include="Effects\Smoke.cs" />
<Compile Include="Commands\ChatCommands.cs" />
<Compile Include="Commands\DevCommands.cs" />
@@ -124,6 +125,7 @@
<Compile Include="LoadScreens\ModChooserLoadScreen.cs" />
<Compile Include="Orders\DeployOrderTargeter.cs" />
<Compile Include="Orders\EnterAlliedActorTargeter.cs" />
<Compile Include="Orders\RepairOrderGenerator.cs" />
<Compile Include="Orders\UnitOrderTargeter.cs" />
<Compile Include="PlayerExtensions.cs" />
<Compile Include="ServerTraits\ColorValidator.cs" />
@@ -190,6 +192,7 @@
<Compile Include="Traits\Buildings\LineBuild.cs" />
<Compile Include="Traits\Buildings\LineBuildNode.cs" />
<Compile Include="Traits\Buildings\RallyPoint.cs" />
<Compile Include="Traits\Buildings\RepairableBuilding.cs" />
<Compile Include="Traits\Buildings\RepairsUnits.cs" />
<Compile Include="Traits\Buildings\Reservable.cs" />
<Compile Include="Traits\Buildings\TargetableBuilding.cs" />
@@ -232,6 +235,7 @@
<Compile Include="Traits\PaletteEffects\NukePaletteEffect.cs" />
<Compile Include="Traits\PaletteEffects\WaterPaletteRotation.cs" />
<Compile Include="Traits\Player\ActorGroupProxy.cs" />
<Compile Include="Traits\Player\AllyRepair.cs" />
<Compile Include="Traits\Player\ConquestVictoryConditions.cs" />
<Compile Include="Traits\Player\GlobalUpgradeManager.cs" />
<Compile Include="Traits\Player\MissionObjectives.cs" />
@@ -275,11 +279,15 @@
<Compile Include="Traits\Render\WithHarvestAnimation.cs" />
<Compile Include="Traits\Render\WithMuzzleFlash.cs" />
<Compile Include="Traits\Render\WithRangeCircle.cs" />
<Compile Include="Traits\Render\WithRepairAnimation.cs" />
<Compile Include="Traits\Render\WithRepairOverlay.cs" />
<Compile Include="Traits\Render\WithResources.cs" />
<Compile Include="Traits\Render\WithRotor.cs" />
<Compile Include="Traits\Render\WithShadow.cs" />
<Compile Include="Traits\Render\WithSmoke.cs" />
<Compile Include="Traits\Render\WithTurret.cs" />
<Compile Include="Traits\Repairable.cs" />
<Compile Include="Traits\RepairableNear.cs" />
<Compile Include="Traits\RepairsBridges.cs" />
<Compile Include="Traits\SeedsResource.cs" />
<Compile Include="Traits\StoresResources.cs" />

View File

@@ -0,0 +1,63 @@
#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.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Orders
{
public class RepairOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
world.CancelInputMode();
return OrderInner(world, mi);
}
IEnumerable<Order> OrderInner(World world, MouseInput mi)
{
if (mi.Button == MouseButton.Left)
{
var underCursor = world.ScreenMap.ActorsAt(mi)
.FirstOrDefault(a => !world.FogObscures(a) && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor)
&& a.TraitsImplementing<RepairableBuilding>().Any(t => !t.IsTraitDisabled));
if (underCursor == null)
yield break;
if (underCursor.Info.Traits.Contains<RepairableBuildingInfo>()
&& underCursor.GetDamageState() > DamageState.Undamaged)
yield return new Order("RepairBuilding", world.LocalPlayer.PlayerActor, false) { TargetActor = underCursor };
}
}
public void Tick(World world)
{
if (world.LocalPlayer != null &&
world.LocalPlayer.WinState != WinState.Undefined)
world.CancelInputMode();
}
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
public string GetCursor(World world, CPos xy, MouseInput mi)
{
mi.Button = MouseButton.Left;
return OrderInner(world, mi).Any()
? "repair" : "repair-blocked";
}
}
}

View File

@@ -0,0 +1,125 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Building can be repaired by the repair button.")]
public class RepairableBuildingInfo : UpgradableTraitInfo, ITraitInfo, Requires<HealthInfo>
{
public readonly int RepairPercent = 20;
public readonly int RepairInterval = 24;
public readonly int RepairStep = 7;
public readonly int[] RepairBonuses = { 100, 150, 175, 200, 220, 240, 260, 280, 300 };
public readonly bool CancelWhenDisabled = false;
public readonly string IndicatorPalettePrefix = "player";
public object Create(ActorInitializer init) { return new RepairableBuilding(init.Self, this); }
}
public class RepairableBuilding : UpgradableTrait<RepairableBuildingInfo>, ITick
{
[Sync]
public int RepairersHash { get { return Repairers.Aggregate(0, (code, player) => code ^ Sync.HashPlayer(player)); } }
public List<Player> Repairers = new List<Player>();
readonly Health health;
public bool RepairActive = false;
public RepairableBuilding(Actor self, RepairableBuildingInfo info)
: base(info)
{
health = self.Trait<Health>();
}
public void RepairBuilding(Actor self, Player player)
{
if (!IsTraitDisabled && self.AppearsFriendlyTo(player.PlayerActor))
{
// If the player won't affect the repair, we won't add him
if (!Repairers.Remove(player) && Repairers.Count < Info.RepairBonuses.Length)
{
Repairers.Add(player);
Sound.PlayNotification(self.World.Map.Rules, player, "Speech", "Repairing", player.Country.Race);
self.World.AddFrameEndTask(w =>
{
if (!self.IsDead)
w.Add(new RepairIndicator(self, Info.IndicatorPalettePrefix));
});
}
}
}
int remainingTicks;
public void Tick(Actor self)
{
if (IsTraitDisabled)
{
if (RepairActive && Info.CancelWhenDisabled)
{
Repairers.Clear();
RepairActive = false;
}
return;
}
if (remainingTicks == 0)
{
Repairers = Repairers.Where(player => player.WinState == WinState.Undefined
&& player.Stances[self.Owner] == Stance.Ally).ToList();
// If after the previous operation there's no repairers left, stop
if (!Repairers.Any()) return;
var buildingValue = self.GetSellValue();
// The cost is the same regardless of the amount of people repairing
var hpToRepair = Math.Min(Info.RepairStep, health.MaxHP - health.HP);
var cost = Math.Max(1, (hpToRepair * Info.RepairPercent * buildingValue) / (health.MaxHP * 100));
// TakeCash will return false if the player can't pay, and will stop him from contributing this Tick
var activePlayers = Repairers.Count(player => player.PlayerActor.Trait<PlayerResources>().TakeCash(cost));
RepairActive = activePlayers > 0;
if (!RepairActive)
{
remainingTicks = 1;
return;
}
// Bonus is applied after finding players who can pay
// activePlayers won't cause IndexOutOfRange because we capped the max amount of players
// to the length of the array
self.InflictDamage(self, -(hpToRepair * Info.RepairBonuses[activePlayers - 1] / 100), null);
if (health.DamageState == DamageState.Undamaged)
{
Repairers.Clear();
RepairActive = false;
return;
}
remainingTicks = Info.RepairInterval;
}
else
--remainingTicks;
}
}
}

View File

@@ -0,0 +1,32 @@
#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
{
[Desc("Attach this to the player actor to allow building repair by team mates.")]
class AllyRepairInfo : TraitInfo<AllyRepair> { }
class AllyRepair : IResolveOrder
{
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "RepairBuilding")
{
var building = order.TargetActor;
if (building.HasTrait<RepairableBuilding>())
if (building.AppearsFriendlyTo(self))
building.Trait<RepairableBuilding>().RepairBuilding(building, self.Owner);
}
}
}
}

View File

@@ -0,0 +1,46 @@
#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.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Replaces the building animation when it repairs a unit.")]
public class WithRepairAnimationInfo : ITraitInfo, Requires<RenderBuildingInfo>
{
[Desc("Sequence name to use")]
public readonly string Sequence = "active";
public readonly bool PauseOnLowPower = false;
public object Create(ActorInitializer init) { return new WithRepairAnimation(init.Self, this); }
}
public class WithRepairAnimation : INotifyRepair
{
IEnumerable<IDisable> disabled;
WithRepairAnimationInfo info;
public WithRepairAnimation(Actor self, WithRepairAnimationInfo info)
{
disabled = self.TraitsImplementing<IDisable>();
this.info = info;
}
public void Repairing(Actor self, Actor host)
{
var building = host.TraitOrDefault<RenderBuilding>();
if (building != null && !(info.PauseOnLowPower && disabled.Any(d => d.Disabled)))
building.PlayCustomAnim(host, info.Sequence);
}
}
}

View File

@@ -0,0 +1,83 @@
#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.Effects;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Displays an overlay when the building is being repaired by the player.")]
public class WithRepairOverlayInfo : ITraitInfo, Requires<RenderSpritesInfo>, Requires<IBodyOrientationInfo>
{
[Desc("Sequence name to use")]
public readonly string Sequence = "active";
[Desc("Position relative to body")]
public readonly WVec Offset = WVec.Zero;
[Desc("Custom palette name")]
public readonly string Palette = null;
[Desc("Custom palette is a player palette BaseName")]
public readonly bool IsPlayerPalette = false;
public readonly bool PauseOnLowPower = false;
public object Create(ActorInitializer init) { return new WithRepairOverlay(init.Self, this); }
}
public class WithRepairOverlay : INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyRepair
{
Animation overlay;
bool buildComplete;
public WithRepairOverlay(Actor self, WithRepairOverlayInfo info)
{
var rs = self.Trait<RenderSprites>();
var body = self.Trait<IBodyOrientation>();
var disabled = self.TraitsImplementing<IDisable>();
buildComplete = !self.HasTrait<Building>(); // always render instantly for units
overlay = new Animation(self.World, rs.GetImage(self));
overlay.Play(info.Sequence);
rs.Add("repair_{0}".F(info.Sequence),
new AnimationWithOffset(overlay,
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
() => !buildComplete,
() => info.PauseOnLowPower && disabled.Any(d => d.Disabled),
p => WithTurret.ZOffsetFromCenter(self, p, 1)),
info.Palette, info.IsPlayerPalette);
}
public void BuildingComplete(Actor self)
{
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(120, () =>
buildComplete = true)));
}
public void Sold(Actor self) { }
public void Selling(Actor self)
{
buildComplete = false;
}
public void DamageStateChanged(Actor self, AttackInfo e)
{
overlay.ReplaceAnim(RenderSprites.NormalizeSequence(overlay, e.DamageState, overlay.CurrentSequence.Name));
}
public void Repairing(Actor self, Actor host)
{
overlay.Play(overlay.CurrentSequence.Name);
}
}
}

View File

@@ -0,0 +1,99 @@
#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 System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("This actor can be sent to a structure for repairs.")]
class RepairableInfo : ITraitInfo, Requires<HealthInfo>
{
public readonly string[] RepairBuildings = { "fix" };
public virtual object Create(ActorInitializer init) { return new Repairable(init.Self); }
}
class Repairable : IIssueOrder, IResolveOrder, IOrderVoice
{
readonly Actor self;
readonly Health health;
public Repairable(Actor self)
{
this.self = self;
health = self.Trait<Health>();
}
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new EnterAlliedActorTargeter<Building>("Repair", 5,
target => CanRepairAt(target), _ => CanRepair());
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "Repair")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
}
bool CanRepairAt(Actor target)
{
return self.Info.Traits.Get<RepairableInfo>().RepairBuildings.Contains(target.Info.Name);
}
bool CanRepair()
{
var li = self.TraitOrDefault<LimitedAmmo>();
return health.DamageState > DamageState.Undamaged || (li != null && !li.FullAmmo());
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Repair" && CanRepair()) ? "Move" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Repair")
{
if (!CanRepairAt(order.TargetActor) || !CanRepair())
return;
var movement = self.Trait<IMove>();
var target = Target.FromOrder(self.World, order);
self.SetTargetLine(target, Color.Green);
self.CancelActivity();
self.QueueActivity(new MoveAdjacentTo(self, target));
self.QueueActivity(movement.MoveTo(self.World.Map.CellContaining(order.TargetActor.CenterPosition), order.TargetActor));
self.QueueActivity(new Rearm(self));
self.QueueActivity(new Repair(order.TargetActor));
var rp = order.TargetActor.TraitOrDefault<RallyPoint>();
if (rp != null)
self.QueueActivity(new CallFunc(() =>
{
self.SetTargetLine(Target.FromCell(self.World, rp.Location), Color.Green);
self.QueueActivity(movement.MoveTo(rp.Location, order.TargetActor));
}));
}
}
}
}

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 System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
class RepairableNearInfo : ITraitInfo, Requires<HealthInfo>
{
[ActorReference] public readonly string[] Buildings = { "spen", "syrd" };
public readonly int CloseEnough = 4; /* cells */
public object Create(ActorInitializer init) { return new RepairableNear(init.Self, this); }
}
class RepairableNear : IIssueOrder, IResolveOrder
{
readonly Actor self;
readonly RepairableNearInfo info;
public RepairableNear(Actor self, RepairableNearInfo info) { this.self = self; this.info = info; }
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new EnterAlliedActorTargeter<Building>("RepairNear", 5,
target => CanRepairAt(target), _ => ShouldRepair());
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "RepairNear")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
}
bool CanRepairAt(Actor target)
{
return info.Buildings.Contains(target.Info.Name);
}
bool ShouldRepair()
{
return self.GetDamageState() > DamageState.Undamaged;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "RepairNear" && CanRepairAt(order.TargetActor) && ShouldRepair())
{
var movement = self.Trait<IMove>();
var target = Target.FromOrder(self.World, order);
self.CancelActivity();
self.QueueActivity(movement.MoveWithinRange(target, new WRange(1024 * info.CloseEnough)));
self.QueueActivity(new Repair(order.TargetActor));
self.SetTargetLine(target, Color.Green, false);
}
}
}
}