#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.Effects; using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Network; using OpenRA.Orders; using OpenRA.Primitives; using OpenRA.Support; using OpenRA.Traits; using XRandom = OpenRA.Support.Random; namespace OpenRA { public class World { internal TraitDictionary traitDict = new TraitDictionary(); HashSet actors = new HashSet(); List effects = new List(); Queue> frameEndActions = new Queue>(); public int Timestep; 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; } Player renderPlayer; public bool ObserveAfterWinOrLose; public Player RenderPlayer { get { return renderPlayer == null || (ObserveAfterWinOrLose && renderPlayer.WinState != WinState.Undefined) ? null : renderPlayer; } set { renderPlayer = value; } } public bool FogObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(a); } public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); } public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); } public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); } public Rectangle? VisibleBounds { get { if (RenderPlayer == null) return null; return RenderPlayer.Shroud.ExploredBounds; } } public bool IsReplay { get { return orderManager.Connection is ReplayConnection; } } public void SetLocalPlayer(string pr) { if (IsReplay) return; LocalPlayer = Players.FirstOrDefault(p => p.InternalName == pr); RenderPlayer = LocalPlayer; } public readonly Actor WorldActor; public readonly Map Map; public readonly TileSet TileSet; public readonly ActorMap ActorMap; public readonly ScreenMap ScreenMap; 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(Map map, OrderManager orderManager, bool isShellmap) { IsShellmap = isShellmap; this.orderManager = orderManager; orderGenerator_ = new UnitOrderGenerator(); Map = map; TileSet = map.Rules.TileSets[Map.Tileset]; SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed); WorldActor = CreateActor("World", new TypeDictionary()); ActorMap = WorldActor.Trait(); ScreenMap = WorldActor.Trait(); // 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; } public void LoadComplete(WorldRenderer wr) { foreach (var wlh in WorldActor.TraitsImplementing()) { using (new Support.PerfTimer(wlh.GetType().Name + ".WorldLoaded")) wlh.WorldLoaded(this, wr); } } 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); foreach (var t in a.TraitsImplementing()) t.AddedToWorld(a); } public void Remove(Actor a) { a.IsInWorld = false; actors.Remove(a); ActorRemoved(a); foreach (var t in a.TraitsImplementing()) t.RemovedFromWorld(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 = _ => { }; public bool Paused { get; internal set; } public bool PredictedPaused { get; internal set; } public bool PauseStateLocked { get; set; } public bool IsShellmap = false; public int WorldTick { get; private set; } public void SetPauseState(bool paused) { if (PauseStateLocked) return; IssueOrder(Order.PauseGame(paused)); PredictedPaused = paused; } public void SetLocalPauseState(bool paused) { Paused = PredictedPaused = paused; } public void Tick() { if (!Paused && (!IsShellmap || Game.Settings.Game.ShowShellmap)) { WorldTick++; 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); } // For things that want to update their render state once per tick, ignoring pause state public void TickRender(WorldRenderer wr) { ActorsWithTrait().Do(x => x.Trait.TickRender(wr, x.Actor)); } 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); // TODO: don't go over all effects foreach (var e in Effects) { var sync = e as ISync; if (sync != null) ret += n++ * Sync.CalculateSyncHash(sync); } // 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); } } }