627 lines
14 KiB
C#
627 lines
14 KiB
C#
/***************************************************************************
|
|
* TileMatrix.cs
|
|
* -------------------
|
|
* begin : May 1, 2002
|
|
* copyright : (C) The RunUO Software Team
|
|
* email : info@runuo.com
|
|
*
|
|
* $Id$
|
|
*
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
***************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Server
|
|
{
|
|
public class TileMatrix
|
|
{
|
|
private StaticTile[][][][][] m_StaticTiles;
|
|
private LandTile[][][] m_LandTiles;
|
|
|
|
private LandTile[] m_InvalidLandBlock;
|
|
private StaticTile[][][] m_EmptyStaticBlock;
|
|
|
|
private FileStream m_Map;
|
|
|
|
private FileStream m_Index;
|
|
private BinaryReader m_IndexReader;
|
|
|
|
private FileStream m_Statics;
|
|
|
|
private int m_FileIndex;
|
|
private int m_BlockWidth, m_BlockHeight;
|
|
private int m_Width, m_Height;
|
|
|
|
private Map m_Owner;
|
|
|
|
private TileMatrixPatch m_Patch;
|
|
private int[][] m_StaticPatches;
|
|
private int[][] m_LandPatches;
|
|
|
|
public Map Owner
|
|
{
|
|
get
|
|
{
|
|
return m_Owner;
|
|
}
|
|
}
|
|
|
|
public TileMatrixPatch Patch
|
|
{
|
|
get
|
|
{
|
|
return m_Patch;
|
|
}
|
|
}
|
|
|
|
public int BlockWidth
|
|
{
|
|
get
|
|
{
|
|
return m_BlockWidth;
|
|
}
|
|
}
|
|
|
|
public int BlockHeight
|
|
{
|
|
get
|
|
{
|
|
return m_BlockHeight;
|
|
}
|
|
}
|
|
|
|
public int Width
|
|
{
|
|
get
|
|
{
|
|
return m_Width;
|
|
}
|
|
}
|
|
|
|
public int Height
|
|
{
|
|
get
|
|
{
|
|
return m_Height;
|
|
}
|
|
}
|
|
|
|
public FileStream MapStream
|
|
{
|
|
get{ return m_Map; }
|
|
set{ m_Map = value; }
|
|
}
|
|
|
|
public FileStream IndexStream
|
|
{
|
|
get{ return m_Index; }
|
|
set{ m_Index = value; }
|
|
}
|
|
|
|
public FileStream DataStream
|
|
{
|
|
get{ return m_Statics; }
|
|
set{ m_Statics = value; }
|
|
}
|
|
|
|
public BinaryReader IndexReader
|
|
{
|
|
get{ return m_IndexReader; }
|
|
set{ m_IndexReader = value; }
|
|
}
|
|
|
|
public bool Exists
|
|
{
|
|
get{ return ( m_Map != null && m_Index != null && m_Statics != null ); }
|
|
}
|
|
|
|
private static List<TileMatrix> m_Instances = new List<TileMatrix>();
|
|
private List<TileMatrix> m_FileShare = new List<TileMatrix>();
|
|
|
|
public TileMatrix( Map owner, int fileIndex, int mapID, int width, int height )
|
|
{
|
|
for ( int i = 0; i < m_Instances.Count; ++i )
|
|
{
|
|
TileMatrix tm = m_Instances[i];
|
|
|
|
if ( tm.m_FileIndex == fileIndex )
|
|
{
|
|
tm.m_FileShare.Add( this );
|
|
m_FileShare.Add( tm );
|
|
}
|
|
}
|
|
|
|
m_Instances.Add( this );
|
|
m_FileIndex = fileIndex;
|
|
m_Width = width;
|
|
m_Height = height;
|
|
m_BlockWidth = width >> 3;
|
|
m_BlockHeight = height >> 3;
|
|
|
|
m_Owner = owner;
|
|
|
|
if ( fileIndex != 0x7F )
|
|
{
|
|
string mapPath = Core.FindDataFile( "map{0}.mul", fileIndex );
|
|
|
|
if ( File.Exists( mapPath ) )
|
|
m_Map = new FileStream( mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
|
|
|
|
string indexPath = Core.FindDataFile( "staidx{0}.mul", fileIndex );
|
|
|
|
if ( File.Exists( indexPath ) )
|
|
{
|
|
m_Index = new FileStream( indexPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
|
|
m_IndexReader = new BinaryReader( m_Index );
|
|
}
|
|
|
|
string staticsPath = Core.FindDataFile( "statics{0}.mul", fileIndex );
|
|
|
|
if ( File.Exists( staticsPath ) )
|
|
m_Statics = new FileStream( staticsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
|
|
}
|
|
|
|
m_EmptyStaticBlock = new StaticTile[8][][];
|
|
|
|
for ( int i = 0; i < 8; ++i )
|
|
{
|
|
m_EmptyStaticBlock[i] = new StaticTile[8][];
|
|
|
|
for ( int j = 0; j < 8; ++j )
|
|
m_EmptyStaticBlock[i][j] = new StaticTile[0];
|
|
}
|
|
|
|
m_InvalidLandBlock = new LandTile[196];
|
|
|
|
m_LandTiles = new LandTile[m_BlockWidth][][];
|
|
m_StaticTiles = new StaticTile[m_BlockWidth][][][][];
|
|
m_StaticPatches = new int[m_BlockWidth][];
|
|
m_LandPatches = new int[m_BlockWidth][];
|
|
|
|
m_Patch = new TileMatrixPatch( this, mapID );
|
|
}
|
|
|
|
public StaticTile[][][] EmptyStaticBlock
|
|
{
|
|
get
|
|
{
|
|
return m_EmptyStaticBlock;
|
|
}
|
|
}
|
|
|
|
public void SetStaticBlock( int x, int y, StaticTile[][][] value )
|
|
{
|
|
if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight )
|
|
return;
|
|
|
|
if ( m_StaticTiles[x] == null )
|
|
m_StaticTiles[x] = new StaticTile[m_BlockHeight][][][];
|
|
|
|
m_StaticTiles[x][y] = value;
|
|
|
|
if ( m_StaticPatches[x] == null )
|
|
m_StaticPatches[x] = new int[(m_BlockHeight + 31) >> 5];
|
|
|
|
m_StaticPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
|
}
|
|
|
|
public StaticTile[][][] GetStaticBlock( int x, int y )
|
|
{
|
|
if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight || m_Statics == null || m_Index == null )
|
|
return m_EmptyStaticBlock;
|
|
|
|
if ( m_StaticTiles[x] == null )
|
|
m_StaticTiles[x] = new StaticTile[m_BlockHeight][][][];
|
|
|
|
StaticTile[][][] tiles = m_StaticTiles[x][y];
|
|
|
|
if ( tiles == null )
|
|
{
|
|
for ( int i = 0; tiles == null && i < m_FileShare.Count; ++i )
|
|
{
|
|
TileMatrix shared = m_FileShare[i];
|
|
|
|
if ( x >= 0 && x < shared.m_BlockWidth && y >= 0 && y < shared.m_BlockHeight )
|
|
{
|
|
StaticTile[][][][] theirTiles = shared.m_StaticTiles[x];
|
|
|
|
if ( theirTiles != null )
|
|
tiles = theirTiles[y];
|
|
|
|
if ( tiles != null )
|
|
{
|
|
int[] theirBits = shared.m_StaticPatches[x];
|
|
|
|
if ( theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0 )
|
|
tiles = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( tiles == null )
|
|
tiles = ReadStaticBlock( x, y );
|
|
|
|
m_StaticTiles[x][y] = tiles;
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
|
|
public StaticTile[] GetStaticTiles( int x, int y )
|
|
{
|
|
StaticTile[][][] tiles = GetStaticBlock( x >> 3, y >> 3 );
|
|
|
|
return tiles[x & 0x7][y & 0x7];
|
|
}
|
|
|
|
private static TileList m_TilesList = new TileList();
|
|
|
|
public StaticTile[] GetStaticTiles( int x, int y, bool multis )
|
|
{
|
|
StaticTile[][][] tiles = GetStaticBlock( x >> 3, y >> 3 );
|
|
|
|
if ( multis )
|
|
{
|
|
IPooledEnumerable eable = m_Owner.GetMultiTilesAt( x, y );
|
|
|
|
if ( eable == Map.NullEnumerable.Instance )
|
|
return tiles[x & 0x7][y & 0x7];
|
|
|
|
bool any = false;
|
|
|
|
foreach ( StaticTile[] multiTiles in eable )
|
|
{
|
|
if ( !any )
|
|
any = true;
|
|
|
|
m_TilesList.AddRange( multiTiles );
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if ( !any )
|
|
return tiles[x & 0x7][y & 0x7];
|
|
|
|
m_TilesList.AddRange( tiles[x & 0x7][y & 0x7] );
|
|
|
|
return m_TilesList.ToArray();
|
|
}
|
|
else
|
|
{
|
|
return tiles[x & 0x7][y & 0x7];
|
|
}
|
|
}
|
|
|
|
public void SetLandBlock( int x, int y, LandTile[] value )
|
|
{
|
|
if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight )
|
|
return;
|
|
|
|
if ( m_LandTiles[x] == null )
|
|
m_LandTiles[x] = new LandTile[m_BlockHeight][];
|
|
|
|
m_LandTiles[x][y] = value;
|
|
|
|
if ( m_LandPatches[x] == null )
|
|
m_LandPatches[x] = new int[(m_BlockHeight + 31) >> 5];
|
|
|
|
m_LandPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
|
}
|
|
|
|
public LandTile[] GetLandBlock( int x, int y )
|
|
{
|
|
if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight || m_Map == null )
|
|
return m_InvalidLandBlock;
|
|
|
|
if ( m_LandTiles[x] == null )
|
|
m_LandTiles[x] = new LandTile[m_BlockHeight][];
|
|
|
|
LandTile[] tiles = m_LandTiles[x][y];
|
|
|
|
if ( tiles == null )
|
|
{
|
|
for ( int i = 0; tiles == null && i < m_FileShare.Count; ++i )
|
|
{
|
|
TileMatrix shared = m_FileShare[i];
|
|
|
|
if ( x >= 0 && x < shared.m_BlockWidth && y >= 0 && y < shared.m_BlockHeight )
|
|
{
|
|
LandTile[][] theirTiles = shared.m_LandTiles[x];
|
|
|
|
if ( theirTiles != null )
|
|
tiles = theirTiles[y];
|
|
|
|
if ( tiles != null )
|
|
{
|
|
int[] theirBits = shared.m_LandPatches[x];
|
|
|
|
if ( theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0 )
|
|
tiles = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( tiles == null )
|
|
tiles = ReadLandBlock( x, y );
|
|
|
|
m_LandTiles[x][y] = tiles;
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
|
|
public LandTile GetLandTile( int x, int y )
|
|
{
|
|
LandTile[] tiles = GetLandBlock( x >> 3, y >> 3 );
|
|
|
|
return tiles[((y & 0x7) << 3) + (x & 0x7)];
|
|
}
|
|
|
|
private static TileList[][] m_Lists;
|
|
|
|
private static StaticTile[] m_TileBuffer = new StaticTile[128];
|
|
|
|
private unsafe StaticTile[][][] ReadStaticBlock( int x, int y )
|
|
{
|
|
try
|
|
{
|
|
m_IndexReader.BaseStream.Seek( ((x * m_BlockHeight) + y) * 12, SeekOrigin.Begin );
|
|
|
|
int lookup = m_IndexReader.ReadInt32();
|
|
int length = m_IndexReader.ReadInt32();
|
|
|
|
if ( lookup < 0 || length <= 0 )
|
|
{
|
|
return m_EmptyStaticBlock;
|
|
}
|
|
else
|
|
{
|
|
int count = length / 7;
|
|
|
|
m_Statics.Seek( lookup, SeekOrigin.Begin );
|
|
|
|
if ( m_TileBuffer.Length < count )
|
|
m_TileBuffer = new StaticTile[count];
|
|
|
|
StaticTile[] staTiles = m_TileBuffer;//new StaticTile[tileCount];
|
|
|
|
fixed ( StaticTile *pTiles = staTiles )
|
|
{
|
|
#if !MONO
|
|
NativeReader.Read( m_Statics.SafeFileHandle.DangerousGetHandle(), pTiles, length );
|
|
#else
|
|
NativeReader.Read( m_Statics.Handle, pTiles, length );
|
|
#endif
|
|
if ( m_Lists == null )
|
|
{
|
|
m_Lists = new TileList[8][];
|
|
|
|
for ( int i = 0; i < 8; ++i )
|
|
{
|
|
m_Lists[i] = new TileList[8];
|
|
|
|
for ( int j = 0; j < 8; ++j )
|
|
m_Lists[i][j] = new TileList();
|
|
}
|
|
}
|
|
|
|
TileList[][] lists = m_Lists;
|
|
|
|
StaticTile *pCur = pTiles, pEnd = pTiles + count;
|
|
|
|
while ( pCur < pEnd )
|
|
{
|
|
lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( pCur->m_ID, pCur->m_Z );
|
|
pCur = pCur + 1;
|
|
}
|
|
|
|
StaticTile[][][] tiles = new StaticTile[8][][];
|
|
|
|
for ( int i = 0; i < 8; ++i )
|
|
{
|
|
tiles[i] = new StaticTile[8][];
|
|
|
|
for ( int j = 0; j < 8; ++j )
|
|
tiles[i][j] = lists[i][j].ToArray();
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
}
|
|
}
|
|
catch ( EndOfStreamException )
|
|
{
|
|
if ( DateTime.Now >= m_NextStaticWarning )
|
|
{
|
|
Console.WriteLine( "Warning: Static EOS for {0} ({1}, {2})", m_Owner, x, y );
|
|
m_NextStaticWarning = DateTime.Now + TimeSpan.FromMinutes( 1.0 );
|
|
}
|
|
|
|
return m_EmptyStaticBlock;
|
|
}
|
|
}
|
|
|
|
private DateTime m_NextStaticWarning;
|
|
private DateTime m_NextLandWarning;
|
|
|
|
public void Force()
|
|
{
|
|
if ( ScriptCompiler.Assemblies == null || ScriptCompiler.Assemblies.Length == 0 )
|
|
throw new Exception();
|
|
}
|
|
|
|
private unsafe LandTile[] ReadLandBlock( int x, int y )
|
|
{
|
|
try
|
|
{
|
|
m_Map.Seek( ((x * m_BlockHeight) + y) * 196 + 4, SeekOrigin.Begin );
|
|
|
|
LandTile[] tiles = new LandTile[64];
|
|
|
|
fixed ( LandTile *pTiles = tiles )
|
|
{
|
|
#if !MONO
|
|
NativeReader.Read( m_Map.SafeFileHandle.DangerousGetHandle(), pTiles, 192 );
|
|
#else
|
|
NativeReader.Read( m_Map.Handle, pTiles, 192 );
|
|
#endif
|
|
}
|
|
|
|
return tiles;
|
|
}
|
|
catch
|
|
{
|
|
if ( DateTime.Now >= m_NextLandWarning )
|
|
{
|
|
Console.WriteLine( "Warning: Land EOS for {0} ({1}, {2})", m_Owner, x, y );
|
|
m_NextLandWarning = DateTime.Now + TimeSpan.FromMinutes( 1.0 );
|
|
}
|
|
|
|
return m_InvalidLandBlock;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if ( m_Map != null )
|
|
m_Map.Close();
|
|
|
|
if ( m_Statics != null )
|
|
m_Statics.Close();
|
|
|
|
if ( m_IndexReader != null )
|
|
m_IndexReader.Close();
|
|
}
|
|
}
|
|
|
|
[System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1 )]
|
|
public struct LandTile
|
|
{
|
|
internal short m_ID;
|
|
internal sbyte m_Z;
|
|
|
|
public int ID
|
|
{
|
|
get { return m_ID; }
|
|
}
|
|
|
|
public int Z
|
|
{
|
|
get { return m_Z; }
|
|
set { m_Z = (sbyte)value; }
|
|
}
|
|
|
|
public int Height
|
|
{
|
|
get { return 0; }
|
|
|
|
}
|
|
|
|
public bool Ignored
|
|
{
|
|
get { return ( m_ID == 2 || m_ID == 0x1DB || ( m_ID >= 0x1AE && m_ID <= 0x1B5 ) ); }
|
|
}
|
|
|
|
public LandTile( short id, sbyte z )
|
|
{
|
|
m_ID = id;
|
|
m_Z = z;
|
|
}
|
|
|
|
public void Set( short id, sbyte z )
|
|
{
|
|
m_ID = id;
|
|
m_Z = z;
|
|
}
|
|
}
|
|
|
|
[System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1 )]
|
|
public struct StaticTile
|
|
{
|
|
internal ushort m_ID;
|
|
internal byte m_X;
|
|
internal byte m_Y;
|
|
internal sbyte m_Z;
|
|
internal short m_Hue;
|
|
|
|
public int ID
|
|
{
|
|
get { return m_ID; }
|
|
}
|
|
|
|
public int X
|
|
{
|
|
get { return m_X; }
|
|
set { m_X = (byte)value; }
|
|
}
|
|
|
|
public int Y
|
|
{
|
|
get { return m_Y; }
|
|
set { m_Y = (byte)value; }
|
|
}
|
|
|
|
public int Z
|
|
{
|
|
get { return m_Z; }
|
|
set { m_Z = (sbyte)value; }
|
|
}
|
|
|
|
public int Hue
|
|
{
|
|
get { return m_Hue; }
|
|
set { m_Hue = (short)value; }
|
|
}
|
|
|
|
public int Height
|
|
{
|
|
get { return TileData.ItemTable[m_ID & TileData.MaxItemValue].Height; }
|
|
}
|
|
|
|
public StaticTile( ushort id, sbyte z )
|
|
{
|
|
m_ID = id;
|
|
m_Z = z;
|
|
|
|
m_X = 0;
|
|
m_Y = 0;
|
|
m_Hue = 0;
|
|
}
|
|
|
|
public StaticTile( ushort id, byte x, byte y, sbyte z, short hue )
|
|
{
|
|
m_ID = id;
|
|
m_X = x;
|
|
m_Y = y;
|
|
m_Z = z;
|
|
m_Hue = hue;
|
|
}
|
|
|
|
public void Set( ushort id, sbyte z )
|
|
{
|
|
m_ID = id;
|
|
m_Z = z;
|
|
}
|
|
|
|
public void Set( ushort id, byte x, byte y, sbyte z, short hue )
|
|
{
|
|
m_ID = id;
|
|
m_X = x;
|
|
m_Y = y;
|
|
m_Z = z;
|
|
m_Hue = hue;
|
|
}
|
|
}
|
|
}
|