context: using UnityEngine;
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Xml.Serialization;
  using Frontiers.Data;
  
  namespace Frontiers.World.WIScripts
  {
  	public class Spawner : WIScript
  	{
  		//general purpose spawner script
  		//this is used whenever we need random things spawned on a regular basis
  		public SpawnerState State = new SpawnerState ();
  
  		public override void OnInitialized ()
  		{
  			worlditem.OnVisible += OnVisible;
  		}
  
  		public virtual void OnActive ()
  		{
  			if (mIsSpawning) {
  				return;
  			}
  			mIsSpawning = true;
  			StartCoroutine (SpawnItemsOverTime ());
  		}
  
  		public virtual void OnVisible ()
  		{
  			if (mIsSpawning) {
  				return;
  			}
  			mIsSpawning = true;
  			StartCoroutine (SpawnItemsOverTime ());
  		}
  
  		public IEnumerator SpawnItemsOverTime ()
  		{
  			System.Random random = new System.Random (Profile.Get.CurrentGame.Seed + worlditem.GetHashCode ());
  
  			Location location = null;
  			if (!worlditem.Is <Location> (out location)) {
  				mIsSpawning = false;
  				yield break;
  			}
  
  			while (!Player.Local.HasSpawned || location.LocationGroup == null || !location.LocationGroup.Is (WIGroupLoadState.Loaded)) {
  				yield return null;
  				if (worlditem.Is (WIActiveState.Invisible) || !worlditem.Is (WILoadState.Initialized)) {
  					//Debug.Log ("We went invisible before we could spawn in " + name);
  					mIsSpawning = false;
  					yield break;
  				}
  			}
  
  			WIGroup spawnGroup = location.LocationGroup;
  			for (int i = 0; i < State.SpawnerSettings.Count; i++) {
  				SpawnerStateSetting setting = State.SpawnerSettings [i];
  				if (setting.TimeToSpawn) {
  					setting.LastManualSpawnPointIndex = 0;//reset this just in case
  					var enumerator = GetSpawnPoints (setting, location, spawnGroup, Player.Local, random).GetEnumerator ();
  					while (enumerator.MoveNext ()) {
  						if (!spawnGroup.Is (WIGroupLoadState.Initialized | WIGroupLoadState.Loading | WIGroupLoadState.Loaded)) {
  							//whoops, it unloaded
  							yield break;
  						}
  						SpawnPoint spawnPoint = enumerator.Current;
  						//wait a tick after fetching the spawn point
  						yield return null;
  						if (spawnPoint != SpawnPoint.Empty) {
  							var spawn = Spawn (spawnPoint, setting, location, spawnGroup, random);
  							while (spawn.MoveNext ()) {
  								yield return spawn.Current;
  							}
  						}
  						double waitUntil = WorldClock.RealTime + Globals.SpawnerRTYieldInterval + UnityEngine.Random.value * 0.25f;
  						while (WorldClock.RealTime < waitUntil) {
  							yield return null;
  						}
  					}
  					int gameHoursToNextSpawnTime = random.Next (setting.MinHoursBetweenSpawns, setting.MaxHoursBetweenSpawns);
  					setting.NextSpawnTime = WorldClock.AdjustedRealTime + WorldClock.HoursToSeconds (gameHoursToNextSpawnTime);
  				}
  				yield return null;
  			}
  
  			mIsSpawning = false;
  			yield break;
  		}
  
  		#region static functions
  
  		public IEnumerable <SpawnPoint> GetSpawnPoints (SpawnerStateSetting setting, Location location, WIGroup spawnGroup, PlayerBase player, System.Random random)
  		{
  			//figure out how many we need
  			setting.NumAttempts = 0;
  			setting.NumFailedAttempts = 0;
  
  			int numExistingObjects = spawnGroup.NumChildItemsByCategory (setting.CategoryName, setting.MaxSpawnedObjects);//TODO make sure this works
  			int numObjectsToSpawn = random.Next (setting.MinSpawnedObjects, setting.MaxSpawnedObjects) - numExistingObjects;
  			if (numObjectsToSpawn <= 0) {
  				//we don't need any more right now
  				#if UNITY_EDITOR
  				Debug.Log ("Skipping spawning in " + name + " because num objects to spawn is <= 0");
  				#endif
  				yield break;
  			}
  			bool succeededThisAttempt = false;
  
  			switch (setting.Type) {
  			case SpawnerType.Characters:
  										//when we're spawning characters, we need action nodes
  				WorldChunk chunk = spawnGroup.GetParentChunk ();
  				List <ActionNodeState> nodeStates = null;
  				if (chunk.GetNodesForLocation (location.worlditem.StaticReference.FullPath, out nodeStates)) {
  					for (int i = 0; i < nodeStates.Count; i++) {
  						mNextSpawnPoint.nodeState = nodeStates [i];
  						yield return mNextSpawnPoint;
  					}
  				} else {
  					nodeStates = new List<ActionNodeState> ();
  					Debug.Log ("Didn't find spawn points at location, so we're going to create some");
  					//if we can't find any nodes for this location
  					//that means we've never spawned here before
  					//we need to create some nodes
  					for (int i = 0; i < numObjectsToSpawn; i++) {
  						//are we totally done?
  						if (setting.NumFailedAttempts >= setting.MaxFailedAttempts) {
  							yield break;
  						}
  						//if not, try to get another spawn point
  						if (spawnGroup.Props.Interior) {
  							succeededThisAttempt = GetInteriorSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  						} else if (spawnGroup.Props.TerrainType == LocationTerrainType.AboveGround) {
  							succeededThisAttempt = GetAboveGroundSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  						} else {
  							succeededThisAttempt = GetBelowGroundSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  						}
  						//did we do it?
  						if (succeededThisAttempt) {
  							//hooray we created a new action node
  							setting.NumAttempts++;
  							//turn this spawn point into an action node
  							ActionNodeState nodeState = new ActionNodeState ();
  							nodeState.Name = location.worlditem.FileName + "-Spawn-" + nodeStates.Count.ToString ();
  							nodeState.Type = ActionNodeType.Generic;
  							nodeState.Users = ActionNodeUsers.AnyOccupant;
  							nodeState.Transform.CopyFrom (mNextSpawnPoint.Transform);
  							mNextSpawnPoint.nodeState = nodeState;
  							ActionNode node = chunk.SpawnActionNode (spawnGroup, nodeState, location.worlditem.tr);
  							yield return mNextSpawnPoint;
  						} else {
  							//boo, we failed
  							setting.NumFailedAttempts++;
  						}
  					}
  
  				}
  				nodeStates.Clear ();
  				nodeStates = null;
  				break;
  
  			default:
  										//when we're spawning other things, we just need spawn points
  										//ok, try to spawn this stuff
  				for (int i = 0; i < numObjectsToSpawn; i++) {
  					//are we totally done?
  					if (setting.NumFailedAttempts >= setting.MaxFailedAttempts) {
  						yield break;
  					}
  					//if not, try to get another spawn point
  					if (spawnGroup.Props.Interior) {
  						succeededThisAttempt = GetInteriorSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  					} else if (spawnGroup.Props.TerrainType == LocationTerrainType.AboveGround) {
  						succeededThisAttempt = GetAboveGroundSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  					} else {
  						succeededThisAttempt = GetBelowGroundSpawnPoint (ref mNextSpawnPoint, setting, location, spawnGroup);
  					}
  					//did we do it?
  					if (succeededThisAttempt) {
  						//hooray, we got another spawn point
  						setting.NumAttempts++;
  						yield return mNextSpawnPoint;
  					} else {
  						//boo, we failed
  						setting.NumFailedAttempts++;
  						yield return SpawnPoint.Empty;
  					}
  				}
  				break;
  			}
  			yield break;
  		}
  
  		protected SpawnPoint mNextSpawnPoint = new SpawnPoint ();
  
  		public static bool GetInteriorSpawnPoint (ref SpawnPoint spawnPoint, SpawnerStateSetting setting, Location spawnLocation, WIGroup spawnGroup)
  		{
  			return true;
  		}
  
  		public static bool GetAboveGroundSpawnPoint (ref SpawnPoint spawnPoint, SpawnerStateSetting setting, Location spawnLocation, WIGroup spawnGroup)
  		{
  			bool succeeded = false;
  			spawnPoint = new SpawnPoint ();
  
gt: 			switch (setting.Method) {