Skip to content

Instantly share code, notes, and snippets.

@phrohdoh
Last active August 29, 2015 14:04
Show Gist options
  • Save phrohdoh/6ac0738589f510cdc2ab to your computer and use it in GitHub Desktop.
Save phrohdoh/6ac0738589f510cdc2ab to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Traits;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Render;
using OpenRA.Graphics;
namespace OpenRA.Mods.RA
{
[Desc("Actor allows/restricts pathing under certain conditions.")]
public class GateInfo : ITraitInfo, Requires<BuildingInfo>, Requires<RenderBuildingInfo>
{
[Desc("Actor should wait N ticks before attempting to close again.")]
public readonly int TryCloseTicks = 50;
[Desc("Animation to play when closing. This will be played in reverse when opening.")]
public readonly string CloseAnimation = "closing";
public object Create(ActorInitializer init) { return new Gate(init.self, this); }
}
public interface IConditionalPathBlocking { bool ShouldBlockActor(Actor self, Actor other); }
public class Gate : ITick, INotifyBlockingMove, IConditionalPathBlocking
{
public bool IsOpen { get; private set; }
GateInfo info;
Animation animation;
RenderBuilding render;
int ticks;
public Gate(Actor self, GateInfo info)
{
this.info = info;
ticks = info.TryCloseTicks;
render = self.Trait<RenderBuilding>();
animation = new Animation(self.World, render.GetImage(self));
}
public bool ShouldBlockActor(Actor self, Actor other)
{
var hostile = other.AppearsFriendlyTo(self);
if (hostile && !IsOpen)
{
Game.Debug("Hostile {0}, Open {1}".F(hostile, IsOpen));
return true;
}
Game.Debug("Not blocking!");
return false;
}
public void OnNotifyBlockingMove(Actor self, Actor blocking)
{
if (ShouldBlockActor(self, blocking))
{
Game.Debug("{0} is blocking {1}. Abort Open().".F(self.Info.Name, blocking.Info.Name));
return;
}
Open(self);
}
public void Tick(Actor self)
{
if (--ticks <= 0)
return;
ticks = info.TryCloseTicks;
var occupied = self.OccupiesSpace.OccupiedCells();
var cells = occupied.Select(c => c.First);
var blockers = cells.SelectMany(c => self.World.ActorMap.GetUnitsAt(c));
if (blockers.Any())
{
Game.Debug("Gate{0} wants to close, but has {1} blockers!".F(self.ActorID, blockers.Count()));
return;
}
Close(self);
}
void Open(Actor self)
{
if (IsOpen)
{
ticks = info.TryCloseTicks;
return;
}
Game.Debug("Gate{0} opening!".F(self.ActorID));
animation.PlayBackwardsThen(info.CloseAnimation, () => IsOpen = true);
}
void Close(Actor self)
{
if (!IsOpen)
return;
Game.Debug("Gate{0} closing!".F(self.ActorID));
IsOpen = false;
animation.Play(info.CloseAnimation);
}
}
}
// In MobileInfo
public bool CanEnterCell(World world, Actor self, CPos cell, Actor ignoreActor, bool checkTransientActors, bool blockedByMovers)
{
if (MovementCostForCell(world, cell) == int.MaxValue)
return false;
if (SharesCell && world.ActorMap.HasFreeSubCell(cell))
return true;
if (checkTransientActors)
{
var canIgnoreMovingAllies = self != null && !blockedByMovers;
var needsCellExclusively = self == null || Crushes == null;
foreach(var a in world.ActorMap.GetUnitsAt(cell))
{
if (a == ignoreActor) continue;
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue;
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them.
if (needsCellExclusively) return false;
foreach (var pathing in a.TraitsImplementing<IConditionalPathBlocking>())
{
Game.Debug("Trying TI ICPB");
if (pathing.ShouldBlockActor(a, self))
{
Game.Debug("a is blocking self");
return false;
}
}
if (!a.HasTrait<ICrushable>())
{
Game.Debug("{0} does not have any ICrushable traits!".F(a.Info.Name));
return false;
}
foreach (var crushable in a.TraitsImplementing<ICrushable>())
if (!crushable.CrushableBy(Crushes, self.Owner))
return false;
}
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment