#region Copyright & License Information /* * Copyright 2007-2011 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.Collections; using OpenRA.Effects; using OpenRA.FileFormats; using OpenRA.Network; using OpenRA.Orders; using OpenRA.Support; using OpenRA.Traits; using XRandom = OpenRA.Thirdparty.Random; namespace OpenRA { public class World { internal TraitDictionary traitDict = new TraitDictionary(); Set actors = new Set(); List effects = new List(); Queue> frameEndActions = new Queue>(); public int FrameNumber { get { return orderManager.LocalFrameNumber; } } internal readonly OrderManager orderManager; public Session LobbyInfo { get { return orderManager.LobbyInfo; } } public XRandom SharedRandom; public readonly List Players = new List(); public void AddPlayer(Player p) { Players.Add(p); } public Player LocalPlayer { get; private set; } public readonly Shroud LocalShroud; public void SetLocalPlayer(string pr) { if (!(orderManager.Connection is ReplayConnection)) LocalPlayer = Players.FirstOrDefault(p => p.InternalName == pr); } public readonly Actor WorldActor; public readonly Map Map; public readonly TileSet TileSet; public readonly ActorMap ActorMap; public void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */ IOrderGenerator orderGenerator_; public IOrderGenerator OrderGenerator { get { return orderGenerator_; } set { Sync.AssertUnsynced( "The current order generator may not be changed from synced code" ); orderGenerator_ = value; } } public Selection Selection = new Selection(); public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); } public bool ToggleInputMode() where T : IOrderGenerator, new() { if (OrderGenerator is T) { CancelInputMode(); return false; } else { OrderGenerator = new T(); return true; } } internal World(Manifest manifest, Map map, OrderManager orderManager) { this.orderManager = orderManager; orderGenerator_ = new UnitOrderGenerator(); Map = map; TileSet = Rules.TileSets[Map.Tileset]; TileSet.LoadTiles(); SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed); WorldActor = CreateActor( "World", new TypeDictionary() ); LocalShroud = WorldActor.Trait(); ActorMap = new ActorMap(this); // Add players foreach (var cmp in WorldActor.TraitsImplementing()) cmp.CreatePlayers(this); // Set defaults for any unset stances foreach (var p in Players) foreach (var q in Players) if (!p.Stances.ContainsKey(q)) p.Stances[q] = Stance.Neutral; Sound.SoundVolumeModifier = 1.0f; foreach (var wlh in WorldActor.TraitsImplementing()) wlh.WorldLoaded(this); } public Actor CreateActor( string name, TypeDictionary initDict ) { return CreateActor( true, name, initDict ); } public Actor CreateActor( bool addToWorld, string name, TypeDictionary initDict ) { var a = new Actor( this, name, initDict ); if( addToWorld ) Add( a ); return a; } public void Add(Actor a) { a.IsInWorld = true; actors.Add(a); ActorAdded(a); } public void Remove(Actor a) { a.IsInWorld = false; actors.Remove(a); ActorRemoved(a); } public void Add(IEffect b) { effects.Add(b); } public void Remove(IEffect b) { effects.Remove(b); } public void AddFrameEndTask( Action a ) { frameEndActions.Enqueue( a ); } public event Action ActorAdded = _ => { }; public event Action ActorRemoved = _ => { }; // Will do bad things in multiplayer games public bool EnableTick = true; public bool IsShellmap = false; bool ShouldTick() { if (!EnableTick) return false; return !IsShellmap || Game.Settings.Game.ShowShellmap; } public void Tick() { // Todo: Expose this as an order so it can be synced if (ShouldTick()) { using( new PerfSample("tick_idle") ) foreach( var ni in ActorsWithTrait() ) if (ni.Actor.IsIdle) ni.Trait.TickIdle(ni.Actor); using( new PerfSample("tick_activities") ) foreach( var a in actors ) a.Tick(); ActorsWithTrait().DoTimed( x => { x.Trait.Tick( x.Actor ); }, "[{2}] Trait: {0} ({1:0.000} ms)", Game.Settings.Debug.LongTickThreshold ); effects.DoTimed( e => e.Tick( this ), "[{2}] Effect: {0} ({1:0.000} ms)", Game.Settings.Debug.LongTickThreshold ); } while (frameEndActions.Count != 0) frameEndActions.Dequeue()(this); Game.viewport.Tick(); } public IEnumerable Actors { get { return actors; } } public IEnumerable Effects { get { return effects; } } uint nextAID = 0; internal uint NextAID() { return nextAID++; } public int SyncHash() { //using (new PerfSample("synchash")) { int n = 0; int ret = 0; // hash all the actors foreach (var a in Actors) ret += n++ * (int)(1+a.ActorID) * Sync.CalculateSyncHash(a); // hash all the traits that tick foreach (var x in traitDict.ActorsWithTraitMultiple(this)) ret += n++ * (int)(1+x.Actor.ActorID) * Sync.CalculateSyncHash(x.Trait); // Hash the shared rng ret += SharedRandom.Last; return ret; } } public IEnumerable> ActorsWithTrait() { return traitDict.ActorsWithTraitMultiple(this); } } public struct TraitPair { public Actor Actor; public T Trait; public override string ToString() { return "{0}->{1}".F( Actor.Info.Name, Trait.GetType().Name ); } } }