RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

[RUO ALL] Interactive Seasons

Vorspire

Knight
[RUO ALL] Interactive Seasons

Interactive Seasons
----------
PACKAGE:
----------
namsepace Server.Seasons
----------
Click file names to view code.

¬Interactive Seasons
----------

----------
OVERVIEW:
----------

This system is intended for use with shards that would like to have dynamic seasons, without having to restart the server after making MapDefinitions.cs edits.

This system also enables the developer to add dynamic seasons to any Region class, making any Region inherit the ability to have a dynamic season that can be different to the overall Map Season.

This system also comes with it's own tracking module that will save and load the Season dynamically over server restarts.
----------

----------
INSTALLATION:
----------

Drag & Drop the Interactive Seasons directory into your Scripts directory and you're ready to get started...

----------

----------
IMPLIMENTATION:
----------

The ISeasons interface can be applied to any Region class, please see BaseRegion.edited.cs- required edits, or take a look at the copy of BaseRegion below, with all edits highlighted in red.
Rich (BB code):
//To enable RUNUO 2.0 compatibility, simply comment out the next line (#define);
//#define RUNUO_1

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using Server;

using Server.Mobiles;
using Server.Network;
using Server.Seasons;

namespace Server.Regions
{
	public enum SpawnZLevel
	{
		Lowest,
		Highest,
		Random
	}

	public class BaseRegion : Region, ISeasons
	{
		private Season m_Season = Season.Desolation;
		public Season Season { get { return m_Season; } set { m_Season = value; UpdateSeason(); } }

		public void UpdateSeason()
		{
#if(RUNUO_1)
			foreach (Mobile m in this.Mobiles)
			{ UpdateSeason(m); }
#else
			foreach (Mobile m in this.GetPlayers())
			{ UpdateSeason(m); }
#endif
		}

		public void UpdateSeason(Mobile m)
		{
			if (m is PlayerMobile)
			{
				NetState state = m.NetState;

				if (state != null)
				{ state.Send(new SeasonChange((int)m_Season)); }
			}
		}

		public override void OnEnter(Mobile m)
		{
			UpdateSeason(m);
			base.OnEnter(m);
		}

		public override void OnExit(Mobile m)
		{
			UpdateSeason(m);
			base.OnExit(m);
		}

		public static void Configure()
		{
			Region.DefaultRegionType = typeof( BaseRegion );
		}

		private string m_RuneName;
		private bool m_NoLogoutDelay;

		private SpawnEntry[] m_Spawns;
		private SpawnZLevel m_SpawnZLevel;
		private bool m_ExcludeFromParentSpawns;

		public string RuneName{ get{ return m_RuneName; } set{ m_RuneName = value; } }

		public bool NoLogoutDelay{ get{ return m_NoLogoutDelay; } set{ m_NoLogoutDelay = value; } }

		public SpawnEntry[] Spawns
		{
			get{ return m_Spawns; }
			set
			{
				if ( m_Spawns != null )
				{
					for ( int i = 0; i < m_Spawns.Length; i++ )
						m_Spawns.Delete();
				}

				m_Spawns = value;
			}
		}

		public SpawnZLevel SpawnZLevel{ get{ return m_SpawnZLevel; } set{ m_SpawnZLevel = value; } }

		public bool ExcludeFromParentSpawns{ get{ return m_ExcludeFromParentSpawns; } set{ m_ExcludeFromParentSpawns = value; } }

		public override void OnUnregister()
		{
			base.OnUnregister();

			this.Spawns = null;
		}

		public static string GetRuneNameFor( Region region )
		{
			while ( region != null )
			{
				BaseRegion br = region as BaseRegion;

				if ( br != null && br.m_RuneName != null )
					return br.m_RuneName;

				region = region.Parent;
			}

			return null;
		}

		public override TimeSpan GetLogoutDelay( Mobile m )
		{
			if ( m_NoLogoutDelay )
			{
				if ( m.Aggressors.Count == 0 && m.Aggressed.Count == 0 && !m.Criminal )
					return TimeSpan.Zero;
			}

			return base.GetLogoutDelay( m );
		}

		public static bool CanSpawn( Region region, params Type[] types )
		{
			while ( region != null )
			{
				if ( !region.AllowSpawn() )
					return false;

				BaseRegion br = region as BaseRegion;

				if ( br != null )
				{
					if ( br.Spawns != null )
					{
						for ( int i = 0; i < br.Spawns.Length; i++ )
						{
							SpawnEntry entry = br.Spawns;

							if ( entry.Definition.CanSpawn( types ) )
								return true;
						}
					}

					if ( br.ExcludeFromParentSpawns )
						return false;
				}

				region = region.Parent;
			}

			return false;
		}

		public override bool AcceptsSpawnsFrom( Region region )
		{
			if ( region == this || !m_ExcludeFromParentSpawns )
				return base.AcceptsSpawnsFrom( region );

			return false;
		}

		private Rectangle3D[] m_Rectangles;
		private int[] m_RectangleWeights;
		private int m_TotalWeight;

		private static List<Rectangle3D> m_RectBuffer1 = new List<Rectangle3D>();
		private static List<Rectangle3D> m_RectBuffer2 = new List<Rectangle3D>();

		private void InitRectangles()
		{
			if ( m_Rectangles != null )
				return;

			// Test if area rectangles are overlapping, and in that case break them into smaller non overlapping rectangles
			for ( int i = 0; i < this.Area.Length; i++ )
			{
				m_RectBuffer2.Add( this.Area );

				for ( int j = 0; j < m_RectBuffer1.Count && m_RectBuffer2.Count > 0; j++ )
				{
					Rectangle3D comp = m_RectBuffer1[j];

					for ( int k = m_RectBuffer2.Count - 1; k >= 0; k-- )
					{
						Rectangle3D rect = m_RectBuffer2[k];

						int l1 = rect.Start.X, r1 = rect.End.X, t1 = rect.Start.Y, b1 = rect.End.Y;
						int l2 = comp.Start.X, r2 = comp.End.X, t2 = comp.Start.Y, b2 = comp.End.Y;

						if ( l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2 )
						{
							m_RectBuffer2.RemoveAt( k );

							int sz = rect.Start.Z;
							int ez = rect.End.X;

							if ( l1 < l2 )
							{
								m_RectBuffer2.Add( new Rectangle3D( new Point3D( l1, t1, sz ), new Point3D( l2, b1, ez ) ) );
							}

							if ( r1 > r2 )
							{
								m_RectBuffer2.Add( new Rectangle3D( new Point3D( r2, t1, sz ), new Point3D( r1, b1, ez ) ) );
							}

							if ( t1 < t2 )
							{
								m_RectBuffer2.Add( new Rectangle3D( new Point3D( Math.Max( l1, l2 ), t1, sz ), new Point3D( Math.Min( r1, r2 ), t2, ez ) ) );
							}

							if ( b1 > b2 )
							{
								m_RectBuffer2.Add( new Rectangle3D( new Point3D( Math.Max( l1, l2 ), b2, sz ), new Point3D( Math.Min( r1, r2 ), b1, ez ) ) );
							}
						}
					}
				}

				m_RectBuffer1.AddRange( m_RectBuffer2 );
				m_RectBuffer2.Clear();
			}

			m_Rectangles = m_RectBuffer1.ToArray();
			m_RectBuffer1.Clear();

			m_RectangleWeights = new int[m_Rectangles.Length];
			for ( int i = 0; i < m_Rectangles.Length; i++ )
			{
				Rectangle3D rect = m_Rectangles;
				int weight = rect.Width * rect.Height;

				m_RectangleWeights = weight;
				m_TotalWeight += weight;
			}
		}

		private static List<Int32> m_SpawnBuffer1 = new List<Int32>();
		private static List<Item>  m_SpawnBuffer2 = new List<Item>();

		public Point3D RandomSpawnLocation( int spawnHeight, bool land, bool water, Point3D home, int range )
		{
			Map map = this.Map;

			if ( map == Map.Internal )
				return Point3D.Zero;

			InitRectangles();

			if ( m_TotalWeight <= 0 )
				return Point3D.Zero;

			for ( int i = 0; i < 10; i++ ) // Try 10 times
			{
				int x, y, minZ, maxZ;

				if ( home == Point3D.Zero )
				{
					int rand = Utility.Random( m_TotalWeight );

					x = int.MinValue; y = int.MinValue;
					minZ = int.MaxValue; maxZ = int.MinValue;
					for ( int j = 0; j < m_RectangleWeights.Length; j++ )
					{
						int curWeight = m_RectangleWeights[j];

						if ( rand < curWeight )
						{
							Rectangle3D rect = m_Rectangles[j];

							x = rect.Start.X + rand % rect.Width;
							y = rect.Start.Y + rand / rect.Width;

							minZ = rect.Start.Z;
							maxZ = rect.End.Z;

							break;
						}

						rand -= curWeight;
					}
				}
				else
				{
					x = Utility.RandomMinMax( home.X - range, home.X + range );
					y = Utility.RandomMinMax( home.Y - range, home.Y + range );

					minZ = int.MaxValue; maxZ = int.MinValue;
					for ( int j = 0; j < this.Area.Length; j++ )
					{
						Rectangle3D rect = this.Area[j];

						if ( x >= rect.Start.X && x < rect.End.X && y >= rect.Start.Y && y < rect.End.Y )
						{
							minZ = rect.Start.Z;
							maxZ = rect.End.Z;
							break;
						}
					}

					if ( minZ == int.MaxValue )
						continue;
				}

				if ( x < 0 || y < 0 || x >= map.Width || y >= map.Height )
					continue;

				Tile lt = map.Tiles.GetLandTile( x, y );

				int ltLowZ = 0, ltAvgZ = 0, ltTopZ = 0;
				map.GetAverageZ( x, y, ref ltLowZ, ref ltAvgZ, ref ltTopZ );

				TileFlag ltFlags = TileData.LandTable[lt.ID & 0x3FFF].Flags;
				bool ltImpassable = ( (ltFlags & TileFlag.Impassable) != 0 );

				if ( !lt.Ignored && ltAvgZ >= minZ && ltAvgZ < maxZ )
					if ( (ltFlags & TileFlag.Wet) != 0 ) {
						if ( water )
							m_SpawnBuffer1.Add( ltAvgZ );
					}
					else if ( land && !ltImpassable )
						m_SpawnBuffer1.Add( ltAvgZ );

				Tile[] staticTiles = map.Tiles.GetStaticTiles( x, y, true );

				for ( int j = 0; j < staticTiles.Length; j++ )
				{
					Tile tile = staticTiles[j];
					ItemData id = TileData.ItemTable[tile.ID & 0x3FFF];
					int tileZ = tile.Z + id.CalcHeight;

					if ( tileZ >= minZ && tileZ < maxZ )
						if ( (id.Flags & TileFlag.Wet) != 0 ) {
							if ( water )
								m_SpawnBuffer1.Add( tileZ );
						}
						else if ( land && id.Surface && !id.Impassable )
							m_SpawnBuffer1.Add( tileZ );
				}


				Sector sector = map.GetSector( x, y );

				for ( int j = 0; j < sector.Items.Count; j++ )
				{
					Item item = sector.Items[j];

					if ( item.ItemID < 0x4000 && item.AtWorldPoint( x, y ) )
					{
						m_SpawnBuffer2.Add( item );

						if ( !item.Movable )
						{
							ItemData id = item.ItemData;
							int itemZ = item.Z + id.CalcHeight;

							if ( itemZ >= minZ && itemZ < maxZ )
								if ( (id.Flags & TileFlag.Wet) != 0 ) {
									if ( water )
										m_SpawnBuffer1.Add( itemZ );
								}
								else if ( land && id.Surface && !id.Impassable )
									m_SpawnBuffer1.Add( itemZ );
						}
					}
				}


				if ( m_SpawnBuffer1.Count == 0 )
				{
					m_SpawnBuffer1.Clear();
					m_SpawnBuffer2.Clear();
					continue;
				}

				int z;
				switch ( m_SpawnZLevel )
				{
					case SpawnZLevel.Lowest:
					{
						z = int.MaxValue;

						for ( int j = 0; j < m_SpawnBuffer1.Count; j++ )
						{
							int l = m_SpawnBuffer1[j];

							if ( l < z )
								z = l;
						}

						break;
					}
					case SpawnZLevel.Highest:
					{
						z = int.MinValue;

						for ( int j = 0; j < m_SpawnBuffer1.Count; j++ )
						{
							int l = m_SpawnBuffer1[j];

							if ( l > z )
								z = l;
						}

						break;
					}
					default: // SpawnZLevel.Random
					{
						int index = Utility.Random( m_SpawnBuffer1.Count );
						z = m_SpawnBuffer1[index];

						break;
					}
				}

				m_SpawnBuffer1.Clear();


				if ( !Region.Find( new Point3D( x, y, z ), map ).AcceptsSpawnsFrom( this ) )
				{
					m_SpawnBuffer2.Clear();
					continue;
				}

				int top = z + spawnHeight;

				bool ok = true;
				for ( int j = 0; j < m_SpawnBuffer2.Count; j++ )
				{
					Item item = m_SpawnBuffer2[j];
					ItemData id = item.ItemData;

					if ( ( id.Surface || id.Impassable ) && item.Z + id.CalcHeight > z && item.Z < top )
					{
						ok = false;
						break;
					}
				}

				m_SpawnBuffer2.Clear();

				if ( !ok )
					continue;

				if ( ltImpassable && ltAvgZ > z && ltLowZ < top )
					continue;

				for ( int j = 0; j < staticTiles.Length; j++ )
				{
					Tile tile = staticTiles[j];
					ItemData id = TileData.ItemTable[tile.ID & 0x3FFF];

					if ( ( id.Surface || id.Impassable ) && tile.Z + id.CalcHeight > z && tile.Z < top )
					{
						ok = false;
						break;
					}
				}

				if ( !ok )
					continue;

				for ( int j = 0; j < sector.Mobiles.Count; j++ )
				{
					Mobile m = sector.Mobiles[j];

					if ( m.X == x && m.Y == y && ( m.AccessLevel == AccessLevel.Player || !m.Hidden ) )
						if ( m.Z + 16 > z && m.Z < top )
						{
							ok = false;
							break;
						}
				}

				if ( ok )
					return new Point3D( x, y, z );
			}

			return Point3D.Zero;
		}

		public override string ToString()
		{
			if ( this.Name != null )
				return this.Name;
			else if ( this.RuneName != null )
				return this.RuneName;
			else
				return this.GetType().Name;
		}

		public BaseRegion( string name, Map map, int priority, params Rectangle2D[] area ) : base( name, map, priority, area )
		{
		}

		public BaseRegion( string name, Map map, int priority, params Rectangle3D[] area ) : base( name, map, priority, area )
		{
		}

		public BaseRegion( string name, Map map, Region parent, params Rectangle2D[] area ) : base( name, map, parent, area )
		{
		}

		public BaseRegion( string name, Map map, Region parent, params Rectangle3D[] area ) : base( name, map, parent, area )
		{
		}

		public BaseRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent )
		{
			ReadString( xml["rune"], "name", ref m_RuneName, false );

			bool logoutDelayActive = true;
			ReadBoolean( xml["logoutDelay"], "active", ref logoutDelayActive, false );
			m_NoLogoutDelay = !logoutDelayActive;


			XmlElement spawning = xml["spawning"];
			if ( spawning != null )
			{
				ReadBoolean( spawning, "excludeFromParent", ref m_ExcludeFromParentSpawns, false );

				object zLevel = SpawnZLevel.Lowest;
				ReadEnum( spawning, "zLevel", typeof( SpawnZLevel ), ref zLevel, false );
				m_SpawnZLevel = (SpawnZLevel) zLevel;


				List<SpawnEntry> list = new List<SpawnEntry>();

				foreach ( XmlNode node in spawning.ChildNodes )
				{
					XmlElement el = node as XmlElement;

					if ( el != null )
					{
						SpawnDefinition def = SpawnDefinition.GetSpawnDefinition( el );
						if ( def == null )
							continue;

						int id = 0;
						if ( !ReadInt32( el, "id", ref id, true ) )
							continue;

						int amount = 0;
						if ( !ReadInt32( el, "amount", ref amount, true ) )
							continue;

						TimeSpan minSpawnTime = SpawnEntry.DefaultMinSpawnTime;
						ReadTimeSpan( el, "minSpawnTime", ref minSpawnTime, false );

						TimeSpan maxSpawnTime = SpawnEntry.DefaultMaxSpawnTime;
						ReadTimeSpan( el, "maxSpawnTime", ref maxSpawnTime, false );

						Point3D home = Point3D.Zero;
						int range = 0;

						XmlElement homeEl = el["home"];
						if ( ReadPoint3D( homeEl, map, ref home, false ) )
							ReadInt32( homeEl, "range", ref range, false );

						object oDir = SpawnEntry.InvalidDirection;
						ReadEnum( el["direction"], "value" , typeof( Direction ), ref oDir, false );
						Direction dir = (Direction) oDir;

						SpawnEntry entry = new SpawnEntry( id, this, home, range, dir, def, amount, minSpawnTime, maxSpawnTime );
						list.Add( entry );
					}
				}

				if ( list.Count > 0 )
				{
					m_Spawns = list.ToArray();
				}
			}
		}
	}
}


This package comes with a pre-edited BaseRegion.edited.cs which can be used to replace the original BaseRegion.cs, otherwise, all ISeasons must be implimented manually in the same fashion shown in the above example.

----------

----------
USAGE:
----------

When standing anywhere in the world, you can edit the Season of your character's current Map by using the [SetMapSeason command.

When standing within a Region that supports the ISeasons interface, you can use the [SetSeason command.

The commands accept the following arguments:
  • Spring (0)
  • Summer (1)
  • Autumn or Fall (2)
  • Winter (3)
  • Desolation (4)
-Words are not always needed, the command will accept the Season ID as an argument too. 0 to 4 respectively.
If a Season ID is not within the required range, it will automatically default to Desolation.

----------

The system, by default, is set to run on RunUO 2.0, but by simply taking out the // before #define(RUNUO_1) will revert the system to using RunUO 1.0 standard interfaces. -RunUO 1.0 Support is not 100% verified or tested.

You will also have to remove all .Net Framework 2.0 assembly references listed below;
  • using System.Collections.Generic;
----------

----------
ISSUES:
----------

  • It is entirely possible that, upon entering or exiting a Region using ISeasons, the Player may get a rubber-banding effect.
  • The season for your current Region will not reflect the Season of Regions out of bounds of your current location.
    Example:

    If Region A has Summer and Region B has Winter...
    You stand in Region A - Region B appears to be using Summer too, given that Region B is on the screen while standing within Region A.
    This can not be fixed, it is a client-side issue.
  • If a Region's bounds are changed and the SeasonTracker doesn't know about it, the season for a Region may not be correctly saved or loaded. This is an untested theory and has a probability chance of 0.5% of actually ever happening.
----------

Cheers to a seasonal xmas release :D
 

Attachments

  • Interactive Seasons.rar
    5.4 KB · Views: 102
  • Interactive Seasons.zip
    6.4 KB · Views: 50

test444

Wanderer
Hmmm what this actually does? I was standing in Luna, where everything is green... i used command SetMapSeason 3 (winter), console answered, Season has been set to Winter, but i dont see any difference... :eek:
 

Vorspire

Knight
test444;822784 said:
Hmmm what this actually does? I was standing in Luna, where everything is green... i used command SetMapSeason 3 (winter), console answered, Season has been set to Winter, but i dont see any difference... :eek:

It depends on if OSI implimented certain seasons on certain Maps. That isn't an error with this system, simply a lack of implimentation on EA's behalf.
 

Vorspire

Knight
Ahh I know what the problem is :)

You should modify Commands.cs with this edit:
Code:
map.Season = (int)((Season)Enum.Parse(typeof(Season), (e.GetString(0).Trim()), true));
from.SendMessage("Season has been set to {0}.", ((Season)map.Season).ToString());

[COLOR="Red"]foreach(Region r in map.Regions.Values)
{
    if(r is ISeasons)
        ((ISeasons)r).UpdateSeason();
    else
    {
#if(RUNUO_1)
        foreach (Mobile m in r.Mobiles)
#else
        foreach (Mobile m in r.GetPlayers())
#endif
        {
              Server.Network.NetState state = m.NetState;

              if (state != null)
              { state.Send(new Server.Network.SeasonChange(map.Season)); }
        }
    }
}[/COLOR]

That should temporarily fix the problem, but it won't update anyone who isn't in a Region, and even then, the Region Season will override the Map Season :)

Exiting and re-entering the Map should also work.
 

test444

Wanderer
Even if i disconnect and log in, still i dont see any changes... what i should imagine under of effect of this script? It will change the green grass to white, or it will just change the weather, that instead of raining there can be snowing?
 
Top