Add a new INotifyCrushed interface

This commit is contained in:
abcdefg30
2016-01-22 10:56:04 +01:00
parent e343803810
commit 8b59ce4dc2
5 changed files with 70 additions and 48 deletions

View File

@@ -303,11 +303,16 @@ namespace OpenRA.Traits
public interface ICrushable public interface ICrushable
{ {
void OnCrush(Actor crusher);
void WarnCrush(Actor crusher);
bool CrushableBy(HashSet<string> crushClasses, Player owner); bool CrushableBy(HashSet<string> crushClasses, Player owner);
} }
[RequireExplicitImplementation]
public interface INotifyCrushed
{
void OnCrush(Actor self, Actor crusher, HashSet<string> crushClasses);
void WarnCrush(Actor self, Actor crusher, HashSet<string> crushClasses);
}
public interface ITraitInfoInterface { } public interface ITraitInfoInterface { }
public interface ITraitInfo : ITraitInfoInterface { object Create(ActorInitializer init); } public interface ITraitInfo : ITraitInfoInterface { object Create(ActorInitializer init); }

View File

@@ -38,7 +38,8 @@ namespace OpenRA.Mods.Common.Traits
bool IOccupySpaceInfo.SharesCell { get { return false; } } bool IOccupySpaceInfo.SharesCell { get { return false; } }
} }
class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld class Crate : ITick, IPositionable, ICrushable, ISync,
INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyCrushed
{ {
readonly Actor self; readonly Actor self;
readonly CrateInfo info; readonly CrateInfo info;
@@ -56,36 +57,14 @@ namespace OpenRA.Mods.Common.Traits
SetPosition(self, init.Get<LocationInit, CPos>()); SetPosition(self, init.Get<LocationInit, CPos>());
} }
public void WarnCrush(Actor crusher) { } void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet<string> crushClasses) { }
public void OnCrush(Actor crusher) void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet<string> crushClasses)
{ {
if (collected) if (!CrushableBy(crushClasses, crusher.Owner))
return; return;
var crateActions = self.TraitsImplementing<CrateAction>(); OnCrushInner(crusher);
self.Dispose();
collected = true;
if (crateActions.Any())
{
var shares = crateActions.Select(a => Pair.New(a, a.GetSelectionSharesOuter(crusher)));
var totalShares = shares.Sum(a => a.Second);
var n = self.World.SharedRandom.Next(totalShares);
foreach (var s in shares)
{
if (n < s.Second)
{
s.First.Activate(crusher);
return;
}
else
n -= s.Second;
}
}
} }
public void OnLanded() public void OnLanded()
@@ -110,11 +89,41 @@ namespace OpenRA.Mods.Common.Traits
// Destroy the crate if none of the units in the cell are valid collectors // Destroy the crate if none of the units in the cell are valid collectors
if (collector != null) if (collector != null)
OnCrush(collector); OnCrushInner(collector);
else else
self.Dispose(); self.Dispose();
} }
void OnCrushInner(Actor crusher)
{
if (collected)
return;
var crateActions = self.TraitsImplementing<CrateAction>();
self.Dispose();
collected = true;
if (crateActions.Any())
{
var shares = crateActions.Select(a => Pair.New(a, a.GetSelectionSharesOuter(crusher)));
var totalShares = shares.Sum(a => a.Second);
var n = self.World.SharedRandom.Next(totalShares);
foreach (var s in shares)
{
if (n < s.Second)
{
s.First.Activate(crusher);
return;
}
n -= s.Second;
}
}
}
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (info.Lifetime != 0 && self.IsInWorld && ++ticks >= info.Lifetime * 25) if (info.Lifetime != 0 && self.IsInWorld && ++ticks >= info.Lifetime * 25)

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new Crushable(init.Self, this); } public object Create(ActorInitializer init) { return new Crushable(init.Self, this); }
} }
class Crushable : ICrushable class Crushable : ICrushable, INotifyCrushed
{ {
readonly Actor self; readonly Actor self;
readonly CrushableInfo info; readonly CrushableInfo info;
@@ -40,16 +40,23 @@ namespace OpenRA.Mods.Common.Traits
this.info = info; this.info = info;
} }
public void WarnCrush(Actor crusher) void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet<string> crushClasses)
{ {
if (!CrushableBy(crushClasses, crusher.Owner))
return;
var mobile = self.TraitOrDefault<Mobile>(); var mobile = self.TraitOrDefault<Mobile>();
if (mobile != null && self.World.SharedRandom.Next(100) <= info.WarnProbability) if (mobile != null && self.World.SharedRandom.Next(100) <= info.WarnProbability)
mobile.Nudge(self, crusher, true); mobile.Nudge(self, crusher, true);
} }
public void OnCrush(Actor crusher) void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet<string> crushClasses)
{ {
if (!CrushableBy(crushClasses, crusher.Owner))
return;
Game.Sound.Play(info.CrushSound, crusher.CenterPosition); Game.Sound.Play(info.CrushSound, crusher.CenterPosition);
var wda = self.TraitsImplementing<WithDeathAnimation>() var wda = self.TraitsImplementing<WithDeathAnimation>()
.FirstOrDefault(s => s.Info.CrushedSequence != null); .FirstOrDefault(s => s.Info.CrushedSequence != null);
if (wda != null) if (wda != null)

View File

@@ -594,10 +594,10 @@ namespace OpenRA.Mods.Common.Traits
if (!self.IsAtGroundLevel()) if (!self.IsAtGroundLevel())
return; return;
var crushables = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self)
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(b => b.CrushableBy(Info.Crushes, self.Owner))); .SelectMany(a => a.TraitsImplementing<INotifyCrushed>().Select(t => new TraitPair<INotifyCrushed>(a, t)));
foreach (var crushable in crushables) foreach (var notifyCrushed in notifiers)
crushable.WarnCrush(self); notifyCrushed.Trait.WarnCrush(notifyCrushed.Actor, self, Info.Crushes);
} }
public void FinishedMoving(Actor self) public void FinishedMoving(Actor self)
@@ -606,10 +606,10 @@ namespace OpenRA.Mods.Common.Traits
if (!self.IsAtGroundLevel()) if (!self.IsAtGroundLevel())
return; return;
var crushables = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self) var notifiers = self.World.ActorMap.GetActorsAt(ToCell).Where(a => a != self)
.SelectMany(a => a.TraitsImplementing<ICrushable>().Where(c => c.CrushableBy(Info.Crushes, self.Owner))); .SelectMany(a => a.TraitsImplementing<INotifyCrushed>().Select(t => new TraitPair<INotifyCrushed>(a, t)));
foreach (var crushable in crushables) foreach (var notifyCrushed in notifiers)
crushable.OnCrush(self); notifyCrushed.Trait.OnCrush(notifyCrushed.Actor, self, Info.Crushes);
} }
public int MovementSpeedForCell(Actor self, CPos cell) public int MovementSpeedForCell(Actor self, CPos cell)

View File

@@ -20,24 +20,25 @@ namespace OpenRA.Mods.RA.Traits
public readonly bool AvoidFriendly = true; public readonly bool AvoidFriendly = true;
public readonly HashSet<string> DetonateClasses = new HashSet<string>(); public readonly HashSet<string> DetonateClasses = new HashSet<string>();
public object Create(ActorInitializer init) { return new Mine(init, this); } public object Create(ActorInitializer init) { return new Mine(this); }
} }
class Mine : ICrushable class Mine : ICrushable, INotifyCrushed
{ {
readonly Actor self;
readonly MineInfo info; readonly MineInfo info;
public Mine(ActorInitializer init, MineInfo info) public Mine(MineInfo info)
{ {
self = init.Self;
this.info = info; this.info = info;
} }
public void WarnCrush(Actor crusher) { } void INotifyCrushed.WarnCrush(Actor self, Actor crusher, HashSet<string> crushClasses) { }
public void OnCrush(Actor crusher) void INotifyCrushed.OnCrush(Actor self, Actor crusher, HashSet<string> crushClasses)
{ {
if (!CrushableBy(crushClasses, crusher.Owner))
return;
if (crusher.Info.HasTraitInfo<MineImmuneInfo>() || (self.Owner.Stances[crusher.Owner] == Stance.Ally && info.AvoidFriendly)) if (crusher.Info.HasTraitInfo<MineImmuneInfo>() || (self.Owner.Stances[crusher.Owner] == Stance.Ally && info.AvoidFriendly))
return; return;