Bring Mods.D2k in line with our new folder/namespace structure
This commit is contained in:
56
OpenRA.Mods.D2k/Traits/AttackSwallow.cs
Normal file
56
OpenRA.Mods.D2k/Traits/AttackSwallow.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
#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.D2k.Activities;
|
||||
using OpenRA.Mods.RA;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
{
|
||||
[Desc("Sandworms use this attack model.")]
|
||||
class AttackSwallowInfo : AttackFrontalInfo
|
||||
{
|
||||
[Desc("The number of ticks it takes to return underground.")]
|
||||
public readonly int ReturnTime = 60;
|
||||
|
||||
[Desc("The number of ticks it takes to get in place under the target to attack.")]
|
||||
public readonly int AttackTime = 30;
|
||||
|
||||
public readonly string WormAttackNotification = "WormAttack";
|
||||
|
||||
public override object Create(ActorInitializer init) { return new AttackSwallow(init.self, this); }
|
||||
}
|
||||
|
||||
class AttackSwallow : AttackFrontal
|
||||
{
|
||||
public AttackSwallow(Actor self, AttackSwallowInfo info)
|
||||
: base(self, info) { }
|
||||
|
||||
public override void DoAttack(Actor self, Target target)
|
||||
{
|
||||
// This is so that the worm does not launch an attack against a target that has reached solid rock
|
||||
if (target.Type != TargetType.Actor || !CanAttack(self, target))
|
||||
{
|
||||
self.CancelActivity();
|
||||
return;
|
||||
}
|
||||
|
||||
var a = ChooseArmamentForTarget(target);
|
||||
if (a == null)
|
||||
return;
|
||||
|
||||
if (!target.IsInRange(self.CenterPosition, a.Weapon.Range))
|
||||
return;
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new SwallowActor(self, target, a.Weapon));
|
||||
}
|
||||
}
|
||||
}
|
||||
177
OpenRA.Mods.D2k/Traits/AutoCarryall.cs
Normal file
177
OpenRA.Mods.D2k/Traits/AutoCarryall.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
#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.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Mods.D2k.Activities;
|
||||
using OpenRA.Mods.RA;
|
||||
using OpenRA.Mods.RA.Activities;
|
||||
using OpenRA.Mods.RA.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
{
|
||||
[Desc("Automatically transports harvesters with the Carryable trait between resource fields and refineries")]
|
||||
public class AutoCarryallInfo : ITraitInfo, Requires<IBodyOrientationInfo>
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new AutoCarryall(init.self, this); }
|
||||
}
|
||||
|
||||
public class AutoCarryall : INotifyBecomingIdle, INotifyKilled, ISync, IRender
|
||||
{
|
||||
readonly Actor self;
|
||||
readonly WRange carryHeight;
|
||||
|
||||
// The actor we are currently carrying.
|
||||
[Sync] Actor carrying;
|
||||
|
||||
// TODO: Use ActorPreviews so that this can support actors with multiple sprites
|
||||
Animation anim;
|
||||
|
||||
public bool Busy { get; internal set; }
|
||||
|
||||
public AutoCarryall(Actor self, AutoCarryallInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
carryHeight = self.Trait<Helicopter>().Info.LandAltitude;
|
||||
}
|
||||
|
||||
public void OnBecomingIdle(Actor self)
|
||||
{
|
||||
FindCarryableForTransport();
|
||||
|
||||
if (!Busy)
|
||||
self.QueueActivity(new HeliFlyCircle(self));
|
||||
}
|
||||
|
||||
// A carryable notifying us that he'd like to be carried
|
||||
public bool RequestTransportNotify(Actor carryable)
|
||||
{
|
||||
if (Busy)
|
||||
return false;
|
||||
|
||||
if (ReserveCarryable(carryable))
|
||||
{
|
||||
self.QueueActivity(false, new CarryUnit(self, carryable));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FindCarryableForTransport()
|
||||
{
|
||||
// get all carryables who want transport
|
||||
var carryables = self.World.ActorsWithTrait<Carryable>()
|
||||
.Where(p =>
|
||||
{
|
||||
var actor = p.Actor;
|
||||
if (actor == null)
|
||||
return false;
|
||||
|
||||
if (actor.Owner != self.Owner)
|
||||
return false;
|
||||
|
||||
if (actor.IsDead)
|
||||
return false;
|
||||
|
||||
var trait = p.Trait;
|
||||
if (trait.Reserved)
|
||||
return false;
|
||||
|
||||
if (!trait.WantsTransport)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
})
|
||||
.OrderBy(p => (self.Location - p.Actor.Location).LengthSquared);
|
||||
|
||||
|
||||
foreach (var p in carryables)
|
||||
{
|
||||
// Check if its actually me who's the best candidate
|
||||
if (p.Trait.GetClosestIdleCarrier() == self && ReserveCarryable(p.Actor))
|
||||
{
|
||||
self.QueueActivity(false, new CarryUnit(self, p.Actor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve the carryable so its ours exclusively
|
||||
public bool ReserveCarryable(Actor carryable)
|
||||
{
|
||||
if (carryable.Trait<Carryable>().Reserve(self))
|
||||
{
|
||||
carrying = carryable;
|
||||
Busy = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unreserve the carryable
|
||||
public void UnreserveCarryable()
|
||||
{
|
||||
if (carrying != null)
|
||||
{
|
||||
if (carrying.IsInWorld && !carrying.IsDead)
|
||||
carrying.Trait<Carryable>().UnReserve(self);
|
||||
|
||||
carrying = null;
|
||||
}
|
||||
|
||||
Busy = false;
|
||||
}
|
||||
|
||||
// INotifyKilled
|
||||
public void Killed(Actor self, AttackInfo e)
|
||||
{
|
||||
if (carrying != null)
|
||||
{
|
||||
carrying.Kill(e.Attacker);
|
||||
carrying = null;
|
||||
}
|
||||
|
||||
UnreserveCarryable();
|
||||
}
|
||||
|
||||
// Called when carryable is inside.
|
||||
public void AttachCarryable(Actor carryable)
|
||||
{
|
||||
// Create a new animation for our carryable unit
|
||||
anim = new Animation(self.World, RenderSprites.GetImage(carryable.Info), RenderSprites.MakeFacingFunc(self));
|
||||
anim.PlayRepeating("idle");
|
||||
anim.IsDecoration = true;
|
||||
}
|
||||
|
||||
// Called when released
|
||||
public void CarryableReleased()
|
||||
{
|
||||
anim = null;
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
// Render the carryable below us TODO: Implement RenderSprites trait
|
||||
if (anim != null && !self.World.FogObscures(self))
|
||||
{
|
||||
anim.Tick();
|
||||
var renderables = anim.Render(self.CenterPosition + new WVec(0, 0, -carryHeight.Range), wr.Palette("player" + carrying.Owner.InternalName));
|
||||
|
||||
foreach (var rr in renderables)
|
||||
yield return rr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
OpenRA.Mods.D2k/Traits/Carryable.cs
Normal file
168
OpenRA.Mods.D2k/Traits/Carryable.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
#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.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Mods.D2k.Activities;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
{
|
||||
[Desc("Can be carried by units with the trait `AutoCarryall`.")]
|
||||
public class CarryableInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Required distance away from destination before requesting a pickup.")]
|
||||
public int MinDistance = 6;
|
||||
|
||||
public object Create(ActorInitializer init) { return new Carryable(init.self, this); }
|
||||
}
|
||||
|
||||
public class Carryable : IDisableMove, INotifyHarvesterAction
|
||||
{
|
||||
readonly CarryableInfo info;
|
||||
readonly Actor self;
|
||||
|
||||
public bool Reserved { get; private set; }
|
||||
|
||||
// If we're locked there isnt much we can do. We'll have to wait for the carrier to finish with us. We should not move or get new orders!
|
||||
bool locked;
|
||||
|
||||
public bool WantsTransport { get; private set; }
|
||||
public CPos Destination;
|
||||
Activity afterLandActivity;
|
||||
|
||||
public Carryable(Actor self, CarryableInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
this.self = self;
|
||||
|
||||
locked = false;
|
||||
Reserved = false;
|
||||
WantsTransport = false;
|
||||
}
|
||||
|
||||
public void MovingToResources(Actor self, CPos targetCell, Activity next) { RequestTransport(targetCell, next); }
|
||||
public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { RequestTransport(targetCell, next); }
|
||||
|
||||
void RequestTransport(CPos destination, Activity afterLandActivity)
|
||||
{
|
||||
if (locked || Reserved)
|
||||
return;
|
||||
|
||||
if (destination == CPos.Zero)
|
||||
return;
|
||||
|
||||
if ((self.Location - destination).Length < info.MinDistance)
|
||||
return;
|
||||
|
||||
Destination = destination;
|
||||
this.afterLandActivity = afterLandActivity;
|
||||
WantsTransport = true;
|
||||
|
||||
// Inform all idle carriers
|
||||
var carriers = self.World.ActorsWithTrait<AutoCarryall>()
|
||||
.Where(c => !c.Trait.Busy && !c.Actor.IsDead && c.Actor.Owner == self.Owner)
|
||||
.OrderBy(p => (self.Location - p.Actor.Location).LengthSquared);
|
||||
|
||||
foreach (var carrier in carriers)
|
||||
{
|
||||
// Notify the carrier and see if he's willing to transport us..
|
||||
if (carrier.Trait.RequestTransportNotify(self))
|
||||
break; // If true then we're done
|
||||
}
|
||||
}
|
||||
|
||||
// No longer want to be carried
|
||||
public void MovementCancelled(Actor self)
|
||||
{
|
||||
if (locked)
|
||||
return;
|
||||
|
||||
WantsTransport = false;
|
||||
Reserved = false;
|
||||
|
||||
// TODO: We could implement something like a carrier.Trait<AutoCarryAll>().CancelTransportNotify(self) and call it here
|
||||
}
|
||||
|
||||
// We do not handle Harvested notification
|
||||
public void Harvested(Actor self, ResourceType resource) { }
|
||||
|
||||
public Actor GetClosestIdleCarrier()
|
||||
{
|
||||
// Find carriers
|
||||
var carriers = self.World.ActorsWithTrait<AutoCarryall>()
|
||||
.Where(p => p.Actor.Owner == self.Owner && !p.Trait.Busy)
|
||||
.Select(h => h.Actor);
|
||||
|
||||
return WorldUtils.ClosestTo(carriers, self);
|
||||
}
|
||||
|
||||
// This gets called by carrier after we touched down
|
||||
public void Dropped()
|
||||
{
|
||||
WantsTransport = false;
|
||||
locked = false;
|
||||
|
||||
if (afterLandActivity != null)
|
||||
self.QueueActivity(false, afterLandActivity);
|
||||
}
|
||||
|
||||
public bool Reserve(Actor carrier)
|
||||
{
|
||||
if ((self.Location - Destination).Length < info.MinDistance)
|
||||
{
|
||||
MovementCancelled(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
Reserved = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UnReserve(Actor carrier)
|
||||
{
|
||||
Reserved = false;
|
||||
locked = false;
|
||||
}
|
||||
|
||||
// Prepare for transport pickup
|
||||
public bool StandbyForPickup(Actor carrier)
|
||||
{
|
||||
if (Destination == CPos.Zero)
|
||||
return false;
|
||||
|
||||
if (locked || !WantsTransport)
|
||||
return false;
|
||||
|
||||
// Last change to change our mind...
|
||||
if ((self.Location - Destination).Length < info.MinDistance)
|
||||
{
|
||||
MovementCancelled(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel our activities
|
||||
self.CancelActivity();
|
||||
locked = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// IMoveDisabled
|
||||
public bool MoveDisabled(Actor self)
|
||||
{
|
||||
// We do not want to move while being locked. The carrier will try to pick us up.
|
||||
return locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
OpenRA.Mods.D2k/Traits/TemporaryOwnerManager.cs
Normal file
82
OpenRA.Mods.D2k/Traits/TemporaryOwnerManager.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
#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.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
{
|
||||
[Desc("Interacts with the ChangeOwner warhead.",
|
||||
"Displays a bar how long this actor is affected and reverts back to the old owner on temporary changes.")]
|
||||
public class TemporaryOwnerManagerInfo : ITraitInfo
|
||||
{
|
||||
public readonly Color BarColor = Color.Orange;
|
||||
|
||||
public object Create(ActorInitializer init) { return new TemporaryOwnerManager(init.self, this); }
|
||||
}
|
||||
|
||||
public class TemporaryOwnerManager : ISelectionBar, ITick, ISync, INotifyOwnerChanged
|
||||
{
|
||||
readonly TemporaryOwnerManagerInfo info;
|
||||
|
||||
Player originalOwner;
|
||||
Player changingOwner;
|
||||
|
||||
[Sync] int remaining = -1;
|
||||
int duration;
|
||||
|
||||
public TemporaryOwnerManager(Actor self, TemporaryOwnerManagerInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
originalOwner = self.Owner;
|
||||
}
|
||||
|
||||
public void ChangeOwner(Actor self, Player newOwner, int duration)
|
||||
{
|
||||
remaining = this.duration = duration;
|
||||
changingOwner = newOwner;
|
||||
self.ChangeOwner(newOwner);
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (!self.IsInWorld)
|
||||
return;
|
||||
|
||||
if (--remaining == 0)
|
||||
{
|
||||
changingOwner = originalOwner;
|
||||
self.ChangeOwner(originalOwner);
|
||||
self.CancelActivity(); // Stop shooting, you have got new enemies
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)
|
||||
{
|
||||
if (changingOwner == null || changingOwner != newOwner)
|
||||
originalOwner = newOwner; // It wasn't a temporary change, so we need to update here
|
||||
else
|
||||
changingOwner = null; // It was triggered by this trait: reset
|
||||
}
|
||||
|
||||
public float GetValue()
|
||||
{
|
||||
if (remaining <= 0)
|
||||
return 0;
|
||||
|
||||
return (float)remaining / duration;
|
||||
}
|
||||
|
||||
public Color GetColor()
|
||||
{
|
||||
return info.BarColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
126
OpenRA.Mods.D2k/Traits/World/WormManager.cs
Normal file
126
OpenRA.Mods.D2k/Traits/World/WormManager.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
#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.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Traits
|
||||
{
|
||||
[Desc("Controls the spawning of sandworms. Attach this to the world actor.")]
|
||||
class WormManagerInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Minimum number of worms")]
|
||||
public readonly int Minimum = 2;
|
||||
|
||||
[Desc("Maximum number of worms")]
|
||||
public readonly int Maximum = 4;
|
||||
|
||||
[Desc("Average time (seconds) between worm spawn")]
|
||||
public readonly int SpawnInterval = 120;
|
||||
|
||||
public readonly string WormSignNotification = "WormSign";
|
||||
|
||||
public readonly string WormSignature = "sandworm";
|
||||
public readonly string WormOwnerPlayer = "Creeps";
|
||||
|
||||
public object Create(ActorInitializer init) { return new WormManager(this, init.self); }
|
||||
}
|
||||
|
||||
class WormManager : ITick
|
||||
{
|
||||
readonly WormManagerInfo info;
|
||||
readonly Lazy<Actor[]> spawnPoints;
|
||||
readonly Lazy<RadarPings> radarPings;
|
||||
|
||||
int countdown;
|
||||
int wormsPresent;
|
||||
|
||||
public WormManager(WormManagerInfo info, Actor self)
|
||||
{
|
||||
this.info = info;
|
||||
radarPings = Exts.Lazy(() => self.World.WorldActor.Trait<RadarPings>());
|
||||
spawnPoints = Exts.Lazy(() => self.World.ActorsWithTrait<WormSpawner>().Select(x => x.Actor).ToArray());
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
// TODO: Add a lobby option to disable worms just like crates
|
||||
|
||||
// TODO: It would be even better to stop
|
||||
if (!spawnPoints.Value.Any())
|
||||
return;
|
||||
|
||||
// Apparantly someone doesn't want worms or the maximum number of worms has been reached
|
||||
if (info.Maximum < 1 || wormsPresent >= info.Maximum)
|
||||
return;
|
||||
|
||||
if (--countdown > 0 && wormsPresent >= info.Minimum)
|
||||
return;
|
||||
|
||||
countdown = info.SpawnInterval * 25;
|
||||
|
||||
var wormLocations = new List<WPos>();
|
||||
|
||||
do
|
||||
{
|
||||
// Always spawn at least one worm, plus however many
|
||||
// more we need to reach the defined minimum count.
|
||||
wormLocations.Add(SpawnWorm(self));
|
||||
} while (wormsPresent < info.Minimum);
|
||||
|
||||
AnnounceWormSign(self, wormLocations);
|
||||
}
|
||||
|
||||
WPos SpawnWorm(Actor self)
|
||||
{
|
||||
var spawnPoint = GetRandomSpawnPoint(self);
|
||||
self.World.AddFrameEndTask(w => w.CreateActor(info.WormSignature, new TypeDictionary
|
||||
{
|
||||
new OwnerInit(w.Players.First(x => x.PlayerName == info.WormOwnerPlayer)),
|
||||
new LocationInit(spawnPoint.Location)
|
||||
}));
|
||||
|
||||
wormsPresent++;
|
||||
|
||||
return spawnPoint.CenterPosition;
|
||||
}
|
||||
|
||||
Actor GetRandomSpawnPoint(Actor self)
|
||||
{
|
||||
return spawnPoints.Value.Random(self.World.SharedRandom);
|
||||
}
|
||||
|
||||
public void DecreaseWorms()
|
||||
{
|
||||
wormsPresent--;
|
||||
}
|
||||
|
||||
void AnnounceWormSign(Actor self, IEnumerable<WPos> wormLocations)
|
||||
{
|
||||
if (self.World.LocalPlayer != null)
|
||||
Sound.PlayNotification(self.World.Map.Rules, self.World.LocalPlayer, "Speech", info.WormSignNotification, self.World.LocalPlayer.Country.Race);
|
||||
|
||||
if (radarPings.Value == null)
|
||||
return;
|
||||
|
||||
foreach (var wormLocation in wormLocations)
|
||||
radarPings.Value.Add(() => true, wormLocation, Color.Red, 50);
|
||||
}
|
||||
}
|
||||
|
||||
[Desc("An actor with this trait indicates a valid spawn point for sandworms.")]
|
||||
class WormSpawnerInfo : TraitInfo<WormSpawner> { }
|
||||
class WormSpawner { }
|
||||
}
|
||||
Reference in New Issue
Block a user