smart queries for World.Actors

This commit is contained in:
Bob
2010-01-31 01:27:50 +13:00
parent c012cf3c7f
commit 73c16d5d9d
23 changed files with 201 additions and 64 deletions

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IjwFramework.Types;
using System.Collections;
namespace OpenRa.Collections
{
public class Set<T> : IEnumerable<T>
{
Dictionary<T, bool> data = new Dictionary<T, bool>();
public void Add( T obj )
{
data.Add( obj, false );
if( OnAdd != null )
OnAdd( obj );
}
public void Remove( T obj )
{
data.Remove( obj );
if( OnRemove != null )
OnRemove( obj );
}
public event Action<T> OnAdd;
public event Action<T> OnRemove;
public IEnumerator<T> GetEnumerator()
{
return data.Keys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class CachedView<T,U> : Set<U>
{
public CachedView( Set<T> set, Func<T,bool> include, Func<T,U> store )
{
foreach( var t in set )
if( include( t ) )
Add( store( t ) );
set.OnAdd += obj => { if( include( obj ) ) Add( store( obj ) ); };
set.OnRemove += obj => { if( include( obj ) ) Remove( store( obj ) ); };
}
}
}

View File

@@ -51,6 +51,7 @@
<Compile Include="AudLoader.cs" /> <Compile Include="AudLoader.cs" />
<Compile Include="Blowfish.cs" /> <Compile Include="Blowfish.cs" />
<Compile Include="BlowfishKeyProvider.cs" /> <Compile Include="BlowfishKeyProvider.cs" />
<Compile Include="Collections\Set.cs" />
<Compile Include="DisposableAction.cs" /> <Compile Include="DisposableAction.cs" />
<Compile Include="Dune2ShpReader.cs" /> <Compile Include="Dune2ShpReader.cs" />
<Compile Include="Exts.cs" /> <Compile Include="Exts.cs" />

View File

@@ -196,5 +196,16 @@ namespace OpenRa
{ {
return currentActivity; return currentActivity;
} }
public override int GetHashCode()
{
return (int)ActorID;
}
public override bool Equals( object obj )
{
var o = obj as Actor;
return ( o != null && o.ActorID == ActorID );
}
} }
} }

View File

@@ -503,9 +503,9 @@ namespace OpenRa
void DrawRadar( World world ) void DrawRadar( World world )
{ {
var hasNewRadar = world.Actors.Any(a => a.Owner == world.LocalPlayer var hasNewRadar = world.Queries.OwnedBy[world.LocalPlayer]
&& a.traits.Contains<ProvidesRadar>() .WithTrait<ProvidesRadar>()
&& a.traits.Get<ProvidesRadar>().IsActive); .Any(a => a.Trait.IsActive);
if (hasNewRadar != hasRadar) if (hasNewRadar != hasRadar)
{ {
@@ -661,7 +661,7 @@ namespace OpenRa
Rectangle repairRect = new Rectangle(buttonOrigin.X, buttonOrigin.Y, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height); Rectangle repairRect = new Rectangle(buttonOrigin.X, buttonOrigin.Y, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height);
var repairDrawPos = new float2(repairRect.Location); var repairDrawPos = new float2(repairRect.Location);
var hasFact = world.Actors.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<ConstructionYard>()); var hasFact = world.Queries.OwnedBy[world.LocalPlayer].WithTrait<ConstructionYard>().Any();
if (Game.Settings.RepairRequiresConyard && !hasFact) if (Game.Settings.RepairRequiresConyard && !hasFact)
repairButton.ReplaceAnim("disabled"); repairButton.ReplaceAnim("disabled");

View File

@@ -23,7 +23,7 @@ namespace OpenRa.GameRules
public Cache<string, List<Actor>> GatherBuildings( Player player ) public Cache<string, List<Actor>> GatherBuildings( Player player )
{ {
var ret = new Cache<string, List<Actor>>( x => new List<Actor>() ); var ret = new Cache<string, List<Actor>>( x => new List<Actor>() );
foreach( var b in player.World.Actors.Where( x => x.Owner == player && x.Info.Traits.Contains<BuildingInfo>() ) ) foreach( var b in player.World.Queries.OwnedBy[player].Where( x=>x.Info.Traits.Contains<BuildingInfo>() ) )
{ {
ret[ b.Info.Name ].Add( b ); ret[ b.Info.Name ].Add( b );
var buildable = b.Info.Traits.GetOrDefault<BuildableInfo>(); var buildable = b.Info.Traits.GetOrDefault<BuildableInfo>();

View File

@@ -93,7 +93,7 @@ namespace OpenRa.Graphics
mapOnlySheet.Texture.SetData(oreLayer); mapOnlySheet.Texture.SetData(oreLayer);
if (!world.Actors.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<ProvidesRadar>())) if (!world.Queries.OwnedBy[world.LocalPlayer].WithTrait<ProvidesRadar>().Any())
return; return;
var bitmap = new Bitmap(oreLayer); var bitmap = new Bitmap(oreLayer);
@@ -114,9 +114,9 @@ namespace OpenRa.Graphics
(b.Owner != null ? playerColors[(int)b.Owner.Palette] : colors[4]).ToArgb(); (b.Owner != null ? playerColors[(int)b.Owner.Palette] : colors[4]).ToArgb();
} }
foreach (var a in world.Actors.Where(a => a.traits.Contains<Unit>())) foreach (var a in world.Queries.WithTrait<Unit>())
*(c + (a.Location.Y * bitmapData.Stride >> 2) + a.Location.X) = *(c + (a.Actor.Location.Y * bitmapData.Stride >> 2) + a.Actor.Location.X) =
playerColors[(int)a.Owner.Palette].ToArgb(); playerColors[(int)a.Actor.Owner.Palette].ToArgb();
unchecked unchecked
{ {

View File

@@ -126,7 +126,7 @@ namespace OpenRa.Graphics
public void GoToStartLocation( Player player ) public void GoToStartLocation( Player player )
{ {
Center(player.World.Actors.Where(a => a.Owner == player && a.traits.Contains<Selectable>())); Center( player.World.Queries.OwnedBy[ player ].WithTrait<Selectable>().Select( a => a.Actor ) );
} }
} }
} }

View File

@@ -38,8 +38,9 @@ namespace OpenRa.Orders
if (!Game.Settings.RepairRequiresConyard) if (!Game.Settings.RepairRequiresConyard)
return; return;
var hasFact = world.Actors var hasFact = world.Queries.OwnedBy[world.LocalPlayer]
.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<ConstructionYard>()); .WithTrait<ConstructionYard>()
.Any();
if (!hasFact) if (!hasFact)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();

View File

@@ -49,12 +49,12 @@ namespace OpenRa
PowerProvided = 0; PowerProvided = 0;
PowerDrained = 0; PowerDrained = 0;
var myBuildings = World.Actors var myBuildings = World.Queries.OwnedBy[this]
.Where(a => a.Owner == this && a.traits.Contains<Building>()); .WithTrait<Building>();
foreach (var a in myBuildings) foreach (var a in myBuildings)
{ {
var p = a.traits.Get<Building>().GetPowerUsage(); var p = a.Trait.GetPowerUsage();
if (p > 0) if (p > 0)
PowerProvided += p; PowerProvided += p;
else else
@@ -80,8 +80,8 @@ namespace OpenRa
void UpdateOreCapacity() void UpdateOreCapacity()
{ {
OreCapacity = World.Actors OreCapacity = World.Queries.OwnedBy[this]
.Where(a => a.Owner == this && a.traits.Contains<StoresOre>()) .Where(a => a.traits.Contains<StoresOre>())
.Select(a => a.Info.Traits.Get<StoresOreInfo>()) .Select(a => a.Info.Traits.Get<StoresOreInfo>())
.Sum(b => b.Capacity); .Sum(b => b.Capacity);
} }

View File

@@ -35,9 +35,9 @@ namespace OpenRa
{ {
// Clear active flags // Clear active flags
gapActive = new bool[128, 128]; gapActive = new bool[128, 128];
foreach (var a in world.Actors.Where(a => a.traits.Contains<GeneratesGap>() && owner != a.Owner)) foreach (var a in world.Queries.WithTrait<GeneratesGap>().Where(a => owner != a.Actor.Owner))
{ {
foreach (var t in a.traits.Get<GeneratesGap>().GetShroudedTiles()) foreach (var t in a.Trait.GetShroudedTiles())
gapActive[t.X, t.Y] = true; gapActive[t.X, t.Y] = true;
} }

View File

@@ -42,8 +42,9 @@ namespace OpenRa.Traits.Activities
umt = mobile.GetMovementType(), umt = mobile.GetMovementType(),
checkForBlocked = false, checkForBlocked = false,
}; };
var refineries = self.World.Actors.Where( x => x.traits.Contains<AcceptsOre>() var refineries = self.World.Queries.OwnedBy[self.Owner]
&& x.Owner == self.Owner ).ToList(); .Where( x => x.traits.Contains<AcceptsOre>())
.ToList();
if( refinery != null ) if( refinery != null )
search.AddInitialCell( self.World, refinery.Location + refineryDeliverOffset ); search.AddInitialCell( self.World, refinery.Location + refineryDeliverOffset );
else else

View File

@@ -9,9 +9,8 @@ namespace OpenRa.Traits.Activities
static Actor ChooseHelipad(Actor self) static Actor ChooseHelipad(Actor self)
{ {
return self.World.Actors.FirstOrDefault( return self.World.Queries.OwnedBy[self.Owner].FirstOrDefault(
a => a.Info.Name == "hpad" && a => a.Info.Name == "hpad" &&
a.Owner == self.Owner &&
!Reservable.IsReserved(a)); !Reservable.IsReserved(a));
} }

View File

@@ -18,9 +18,8 @@ namespace OpenRa.Traits.Activities
Actor ChooseAirfield(Actor self) Actor ChooseAirfield(Actor self)
{ {
var airfield = self.World.Actors var airfield = self.World.Queries.OwnedBy[self.Owner]
.Where(a => a.Info.Name == "afld" .Where(a => a.Info.Name == "afld"
&& a.Owner == self.Owner
&& !Reservable.IsReserved(a)) && !Reservable.IsReserved(a))
.FirstOrDefault(); .FirstOrDefault();

View File

@@ -46,8 +46,10 @@ namespace OpenRa.Traits
if (!movement.CanEnterCell(order.TargetLocation)) if (!movement.CanEnterCell(order.TargetLocation))
return; return;
var chronosphere = self.World.Actors.Where(a => a.Owner == self.Owner var chronosphere = self.World.Queries
&& a.traits.Contains<Chronosphere>()).FirstOrDefault(); .OwnedBy[self.Owner]
.WithTrait<Chronosphere>()
.Select(x=>x.Actor).FirstOrDefault();
bool success = order.TargetActor.traits.Get<Chronoshiftable>().Activate(order.TargetActor, bool success = order.TargetActor.traits.Get<Chronoshiftable>().Activate(order.TargetActor,
order.TargetLocation, order.TargetLocation,
@@ -60,8 +62,8 @@ namespace OpenRa.Traits
Sound.Play("chrono2.aud"); Sound.Play("chrono2.aud");
// Trigger screen desaturate effect // Trigger screen desaturate effect
foreach (var a in self.World.Actors.Where(a => a.traits.Contains<ChronoshiftPaletteEffect>())) foreach (var a in self.World.Queries.WithTrait<ChronoshiftPaletteEffect>())
a.traits.Get<ChronoshiftPaletteEffect>().DoChronoshift(); a.Trait.DoChronoshift();
if (chronosphere != null) if (chronosphere != null)
chronosphere.traits.Get<RenderBuilding>().PlayCustomAnim(chronosphere, "active"); chronosphere.traits.Get<RenderBuilding>().PlayCustomAnim(chronosphere, "active");
@@ -98,8 +100,9 @@ namespace OpenRa.Traits
public void Tick( World world ) public void Tick( World world )
{ {
var hasChronosphere = world.Actors var hasChronosphere = world.Queries.OwnedBy[world.LocalPlayer]
.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<Chronosphere>()); .WithTrait<Chronosphere>()
.Any();
if (!hasChronosphere) if (!hasChronosphere)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();
@@ -135,8 +138,9 @@ namespace OpenRa.Traits
public void Tick(World world) public void Tick(World world)
{ {
var hasChronosphere = world.Actors var hasChronosphere = world.Queries.OwnedBy[world.LocalPlayer]
.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<Chronosphere>()); .WithTrait<Chronosphere>()
.Any();
if (!hasChronosphere) if (!hasChronosphere)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();

View File

@@ -26,8 +26,9 @@ namespace OpenRa.Traits
{ {
if (order.OrderString == "NuclearMissile") if (order.OrderString == "NuclearMissile")
{ {
var silo = self.World.Actors.Where(a => a.Owner == self.Owner var silo = self.World.Queries.OwnedBy[self.Owner]
&& a.traits.Contains<NukeSilo>()).FirstOrDefault(); .Where(a => a.traits.Contains<NukeSilo>())
.FirstOrDefault();
if (silo != null) if (silo != null)
silo.traits.Get<RenderBuilding>().PlayCustomAnim(silo, "active"); silo.traits.Get<RenderBuilding>().PlayCustomAnim(silo, "active");
@@ -70,8 +71,9 @@ namespace OpenRa.Traits
public void Tick(World world) public void Tick(World world)
{ {
var hasStructure = world.Actors var hasStructure = world.Queries.OwnedBy[world.LocalPlayer]
.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<NukeSilo>()); .WithTrait<NukeSilo>()
.Any();
if (!hasStructure) if (!hasStructure)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();

View File

@@ -86,12 +86,12 @@ namespace OpenRa.Traits
// Cancel existing primaries // Cancel existing primaries
foreach (var p in self.Info.Traits.Get<ProductionInfo>().Produces) foreach (var p in self.Info.Traits.Get<ProductionInfo>().Produces)
{ {
foreach (var b in self.World.Actors.Where(x => x.traits.Contains<Production>() foreach (var b in self.World.Queries.OwnedBy[self.Owner]
&& x.Owner == self.Owner .WithTrait<Production>()
&& x.traits.Get<Production>().IsPrimary == true .Where(x => x.Trait.IsPrimary
&& (x.Info.Traits.Get<ProductionInfo>().Produces.Contains(p)))) && (x.Actor.Info.Traits.Get<ProductionInfo>().Produces.Contains(p))))
{ {
b.traits.Get<Production>().SetPrimaryProducer(b, false); b.Trait.SetPrimaryProducer(b.Actor, false);
} }
} }
isPrimary = true; isPrimary = true;

View File

@@ -131,18 +131,17 @@ namespace OpenRa.Traits
Actor producer = null; Actor producer = null;
// Prioritise primary structure in build order // Prioritise primary structure in build order
var primaryProducers = self.World.Actors var primaryProducers = self.World.Queries.OwnedBy[self.Owner]
.Where(x => x.traits.Contains<Production>() .WithTrait<Production>()
&& producerTypes.Contains(x.Info) .Where(x => producerTypes.Contains(x.Actor.Info)
&& x.Owner == self.Owner && x.Trait.IsPrimary);
&& x.traits.Get<Production>().IsPrimary == true);
foreach (var p in primaryProducers) foreach (var p in primaryProducers)
{ {
// Ignore buildings that are disabled // Ignore buildings that are disabled
if (p.traits.Contains<Building>() && p.traits.Get<Building>().Disabled) if (p.Actor.traits.Contains<Building>() && p.Actor.traits.Get<Building>().Disabled)
continue; continue;
producer = p; producer = p.Actor;
break; break;
} }
@@ -152,8 +151,8 @@ namespace OpenRa.Traits
// Pick the first available producer // Pick the first available producer
if (producer == null) if (producer == null)
{ {
producer = self.World.Actors producer = self.World.Queries.OwnedBy[self.Owner]
.Where( x => producerTypes.Contains( x.Info ) && x.Owner == self.Owner ) .Where( x => producerTypes.Contains( x.Info ) )
.FirstOrDefault(); .FirstOrDefault();
} }

View File

@@ -16,9 +16,8 @@ namespace OpenRa.Traits
var b = self.traits.Get<Building>(); var b = self.traits.Get<Building>();
if (b != null && b.Disabled) return false; if (b != null && b.Disabled) return false;
var isJammed = self.World.Actors.Any(a => a.traits.Contains<JamsRadar>() var isJammed = self.World.Queries.WithTrait<JamsRadar>().Any(a => self.Owner != a.Actor.Owner
&& self.Owner != a.Owner && (self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get<JamsRadarInfo>().Range);
&& (self.Location - a.Location).Length < a.Info.Traits.Get<JamsRadarInfo>().Range);
return !isJammed; return !isJammed;
} }

View File

@@ -30,8 +30,9 @@ namespace OpenRa.Traits
// Does this belong here? NO, but it's your mess. // Does this belong here? NO, but it's your mess.
// Get the crushable actors // Get the crushable actors
foreach (var a in self.World.Actors.Where(b => b.traits.Contains<ICrushable>())) foreach (var aa in self.World.Queries.WithTrait<ICrushable>())
{ {
var a = aa.Actor;
// Are there any units in the same cell that can crush this? // Are there any units in the same cell that can crush this?
foreach( var ios in a.traits.WithInterface<IOccupySpace>() ) foreach( var ios in a.traits.WithInterface<IOccupySpace>() )
foreach( var cell in ios.OccupiedCells() ) foreach( var cell in ios.OccupiedCells() )

View File

@@ -6,12 +6,13 @@ using OpenRa.Support;
using OpenRa.FileFormats; using OpenRa.FileFormats;
using OpenRa.Graphics; using OpenRa.Graphics;
using OpenRa.Traits; using OpenRa.Traits;
using OpenRa.Collections;
namespace OpenRa namespace OpenRa
{ {
public class World public class World
{ {
List<Actor> actors = new List<Actor>(); Set<Actor> actors = new Set<Actor>();
List<IEffect> effects = new List<IEffect>(); List<IEffect> effects = new List<IEffect>();
List<Action<World>> frameEndActions = new List<Action<World>>(); List<Action<World>> frameEndActions = new List<Action<World>>();
@@ -77,6 +78,9 @@ namespace OpenRa
WorldRenderer = new WorldRenderer(this, Game.renderer); WorldRenderer = new WorldRenderer(this, Game.renderer);
Minimap = new Minimap(this, Game.renderer); Minimap = new Minimap(this, Game.renderer);
Timer.Time( "renderer, minimap: {0}" ); Timer.Time( "renderer, minimap: {0}" );
Queries = new AllQueries( this );
Timer.Time( "queries: {0}" );
Timer.Time( "----end World.ctor" ); Timer.Time( "----end World.ctor" );
} }
@@ -153,5 +157,65 @@ namespace OpenRa
return ret; return ret;
} }
} }
public class AllQueries
{
readonly World world;
public readonly Dictionary<Player, OwnedByCachedView> OwnedBy = new Dictionary<Player, OwnedByCachedView>();
readonly TypeDictionary hasTrait = new TypeDictionary();
public AllQueries( World world )
{
this.world = world;
foreach( var p in world.players.Values )
{
var player = p;
OwnedBy.Add( player, new OwnedByCachedView( world, world.actors, x => x.Owner == player ) );
}
}
public CachedView<Actor, TraitPair<T>> WithTrait<T>()
{
return WithTraitInner<T>( world, hasTrait );
}
static CachedView<Actor, TraitPair<T>> WithTraitInner<T>( World world, TypeDictionary hasTrait )
{
var ret = hasTrait.GetOrDefault<CachedView<Actor, TraitPair<T>>>();
if( ret != null )
return ret;
ret = new CachedView<Actor, TraitPair<T>>(
world.actors,
x => x.traits.Contains<T>(),
x => new TraitPair<T> { Actor = x, Trait = x.traits.Get<T>() } );
hasTrait.Add( ret );
return ret; }
public struct TraitPair<T>
{
public Actor Actor;
public T Trait;
}
public class OwnedByCachedView : CachedView<Actor, Actor>
{
readonly World world;
readonly TypeDictionary hasTrait = new TypeDictionary();
public OwnedByCachedView( World world, Set<Actor> set, Func<Actor, bool> include )
: base( set, include, a => a )
{
this.world = world;
}
public CachedView<Actor, TraitPair<T>> WithTrait<T>()
{
return WithTraitInner<T>( world, hasTrait );
}
}
}
public readonly AllQueries Queries;
} }
} }

View File

@@ -55,8 +55,8 @@ namespace OpenRa.Mods.Aftermath
Sound.Play("chrotnk1.aud"); Sound.Play("chrotnk1.aud");
chargeTick = chargeLength; chargeTick = chargeLength;
foreach (var a in self.World.Actors.Where(a => a.traits.Contains<ChronoshiftPaletteEffect>())) foreach (var a in self.World.Queries.WithTrait<ChronoshiftPaletteEffect>())
a.traits.Get<ChronoshiftPaletteEffect>().DoChronoshift(); a.Trait.DoChronoshift();
} }
} }

View File

@@ -18,8 +18,8 @@ namespace OpenRa.Mods.RA
protected override void OnFinishCharging() protected override void OnFinishCharging()
{ {
var launchSite = Owner.World.Actors var launchSite = Owner.World.Queries.OwnedBy[Owner]
.FirstOrDefault(a => a.Owner == Owner && a.traits.Contains<GpsLaunchSite>()); .FirstOrDefault(a => a.traits.Contains<GpsLaunchSite>());
if (launchSite == null) if (launchSite == null)
return; return;

View File

@@ -29,8 +29,9 @@ namespace OpenRa.Mods.RA
if (self.Owner == self.World.LocalPlayer) if (self.Owner == self.World.LocalPlayer)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();
var curtain = self.World.Actors.Where(a => a.Owner != null var curtain = self.World.Queries.WithTrait<IronCurtain>()
&& a.traits.Contains<IronCurtain>()).FirstOrDefault(); .Where(a => a.Actor.Owner != null)
.FirstOrDefault().Actor;
if (curtain != null) if (curtain != null)
curtain.traits.Get<RenderBuilding>().PlayCustomAnim(curtain, "active"); curtain.traits.Get<RenderBuilding>().PlayCustomAnim(curtain, "active");
@@ -73,8 +74,9 @@ namespace OpenRa.Mods.RA
public void Tick(World world) public void Tick(World world)
{ {
var hasStructure = world.Actors var hasStructure = world.Queries.OwnedBy[world.LocalPlayer]
.Any(a => a.Owner == world.LocalPlayer && a.traits.Contains<IronCurtain>()); .WithTrait<IronCurtain>()
.Any();
if (!hasStructure) if (!hasStructure)
Game.controller.CancelInputMode(); Game.controller.CancelInputMode();