#region Copyright & License Information /* * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. * This file is part of OpenRA. * * OpenRA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenRA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenRA. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using OpenRA.Collections; using OpenRA.Effects; using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Support; using OpenRA.Traits; using XRandom = OpenRA.Thirdparty.Random; namespace OpenRA { public class World { Set actors = new Set(); List effects = new List(); List> frameEndActions = new List>(); public XRandom SharedRandom = new XRandom(0); // synced public XRandom CosmeticRandom = new XRandom(); // not synced public readonly Dictionary players = new Dictionary(); int localPlayerIndex; public Player LocalPlayer { get { return players[localPlayerIndex]; } } public Player NeutralPlayer { get { return players[0]; } // todo, perhaps. } public void SetLocalPlayer(int index) { if (index != localPlayerIndex) { localPlayerIndex = index; Game.viewport.GoToStartLocation(LocalPlayer); Game.chat.AddLine(LocalPlayer, "is now YOU"); } if (!string.IsNullOrEmpty(Game.Settings.PlayerName) && LocalPlayer.PlayerName != Game.Settings.PlayerName) Game.IssueOrder(Order.Chat("/name " + Game.Settings.PlayerName)); } public readonly Actor WorldActor; public readonly PathFinder PathFinder; public readonly Map Map; public readonly TileSet TileSet; // for tricky things like bridges. public readonly ICustomTerrain[,] customTerrain; public readonly WorldRenderer WorldRenderer; internal readonly Minimap Minimap; public World() { Timer.Time( "----World.ctor" ); Map = new Map( Game.LobbyInfo.GlobalSettings.Map ); customTerrain = new ICustomTerrain[Map.MapSize, Map.MapSize]; Timer.Time( "new Map: {0}" ); var theaterInfo = Rules.Info["world"].Traits.WithInterface().FirstOrDefault(t => t.Theater == Map.Theater); TileSet = new TileSet(theaterInfo.Tileset, theaterInfo.Templates, theaterInfo.Suffix); SpriteSheetBuilder.Initialize( Map ); Timer.Time( "Tileset: {0}" ); WorldRenderer = new WorldRenderer(this, Game.renderer); Timer.Time("renderer: {0}"); WorldActor = CreateActor("World", new int2(int.MaxValue, int.MaxValue), null); for (int i = 0; i < 8; i++) players[i] = new Player(this, i, Game.LobbyInfo.Clients.FirstOrDefault(a => a.Index == i)); Timer.Time( "worldActor, players: {0}" ); Queries = new AllQueries( this ); Timer.Time( "queries: {0}" ); foreach (var wlh in WorldActor.traits.WithInterface()) wlh.WorldLoaded(this); PathFinder = new PathFinder(this); Timer.Time( "hooks, pathing: {0}" ); Minimap = new Minimap(this, Game.renderer); Timer.Time( "minimap: {0}" ); Timer.Time( "----end World.ctor" ); } public Actor CreateActor( string name, int2 location, Player owner ) { var a = new Actor( this, name, location, owner ); 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.Add( a ); } public event Action ActorAdded = _ => { }; public event Action ActorRemoved = _ => { }; public void Tick() { foreach (var a in actors) a.Tick(); Queries.WithTraitMultiple().Do( x => x.Trait.Tick( x.Actor ) ); foreach (var e in effects) e.Tick( this ); Game.viewport.Tick(); var acts = frameEndActions; frameEndActions = new List>(); foreach (var a in acts) a(this); Minimap.Update(); foreach (var player in players.Values) player.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 ret = 0; foreach (var a in Actors) ret += (int)a.ActorID * Sync.CalculateSyncHash(a); return ret; } } public class AllQueries { readonly World world; public readonly Dictionary OwnedBy = new Dictionary(); 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> WithTrait() { return WithTraitInner( world.actors, hasTrait ); } static CachedView> WithTraitInner( Set set, TypeDictionary hasTrait ) { var ret = hasTrait.GetOrDefault>>(); if( ret != null ) return ret; ret = new CachedView>( set, x => x.traits.Contains(), x => new TraitPair { Actor = x, Trait = x.traits.Get() } ); hasTrait.Add( ret ); return ret; } public CachedView> WithTraitMultiple() { var ret = hasTrait.GetOrDefault>>(); if( ret != null ) return ret; ret = new CachedView>( world.actors, x => x.traits.Contains(), x => x.traits.WithInterface().Select( t => new TraitPair { Actor = x, Trait = t } ) ); hasTrait.Add( ret ); return ret; } public struct TraitPair { public Actor Actor; public T Trait; } public class OwnedByCachedView : CachedView { readonly TypeDictionary hasTrait = new TypeDictionary(); public OwnedByCachedView( World world, Set set, Func include ) : base( set, include, a => a ) { } public CachedView> WithTrait() { return WithTraitInner( this, hasTrait ); } } } public readonly AllQueries Queries; } }