uox3/source/mapstuff.cpp
2002-10-25 05:15:37 +00:00

1586 lines
47 KiB
C++

#include "uox3.h"
#include "debug.h"
#undef DBGFILE
#define DBGFILE "mapstuff.cpp"
/*
** Hey, its me fur again. I'm going to tear through this file next
** so I can get a better understanding of the map/tile stuff and
** especially the z values so we can nail down the walking bugs once
** and for all. Gosh, I feel like a high-tech version of Steve the
** Crocodile Hunter these days.. C'mon mate, lets go squish some bugs!
*/
/*
** New Notes: 11/3/1999
** I changed all of the places where z was handled as a signed char and
** upped them to an int. While the basic terrain never goes above 127,
** calculations sure can. For example, if you are at a piece of land at 100
** and put a 50 high static object, its going to roll over. - fur
*/
/*
** Tile Flags Bit Definitions - Please fill in if you know what something does!!
** I updated some of these with what LB knew 11/17/1999
**
** (tile.flag1&0x01) 1 = At Floor Level(?)
** (tile.flag1&0x02) 1 = Wearable/Holdable(?)
** (tile.flag1&0x04) 1 = Signs, Guilds, banners, (?)
** (tile.flag1&0x08) 1 = Web, dirt, blood, footsteps(?)
** (tile.flag1&0x10) 1 = Wall/Vertical Tile(?)
** (tile.flag1&0x20) 1 = Causes Damage(?)
** (tile.flag1&0x40) 1 = Blocking, cannot travel over
** (tile.flag1&0x80) 1 = IsWet, Maybe Water or Blood
**
** (tile.flag2&0x01) Nothing Uses This Bit
** (tile.flag2&0x02) Standable, Mostly Flat Surfaces(?)
** (tile.flag2&0x04) Climbable, eg. Stairs/Ladders(?)
** (tile.flag2&0x08) Pileable
** (tile.flag2&0x10) Windows/Doors(?)
** (tile.flag2&0x20) Cannot shoot thru
** (tile.flag2&0x40) Display as 'a xxxx'
** (tile.flag2&0x80) Display as 'an xxxx;
**
** (tile.flag3&0x01) Internal Only Tile(?)
** (tile.flag3&0x02) Fades with CoT(?)
** (tile.flag3&0x04) (?)
** (tile.flag3&0x08) Never used(?)
** (tile.flag3&0x10) Map(?)
** (tile.flag3&0x20) Container(?)
** (tile.flag3&0x40) Wearable(?)
** (tile.flag3&0x80) Lightsource(?)
**
**
** (tile.flag4&0x01) Animated Tile(?)
** (tile.flag4&0x02) (?)
** (tile.flag4&0x04) Walk(?)
** (tile.flag4&0x08) Whole bodyitem/Items you cannot remove from paper doll(?)
** (tile.flag4&0x10) Wall/Roof? Weapon(?)
** (tile.flag4&0x20) Door(?)
** (tile.flag4&0x40) Climbable direction (2-bits)
** (tile.flag4&0x80) Climbable direction(?)
**
** Other things, one bit somewhere is probably whether items decay or not, whether
** something is magical or not, one bit for editible or not, bit for dyeable, can be poisioned,
** wearable and/or wear it can be worn, regeants bit? required str? damage for weapons?
** readable? nameable?
*/
/*
** 11/17/1999 Rewrite plans
**
** In reality most of the 'caching' code found within is really 'shadowing' code. That is,
** its all just slurped up into memory once at startup (very slow) I plan on rewriting all
** of this so that its actually cached, that is if a piece is already in memory it will be
** used, otherwise gotten from disk. There's no need to load them all at start up, in fact
** this is wasteful, because you wait at startup loading a lot of pieces you will never use.
** Also the map cache is retarded because it is alway shifting the entire buffer on a miss,
** rather than using a rolling queue. Finally, the verdata.mul stuff is not cached at all
** and has to be a huge performance hit constantly sifting through the index for that file.
** -fur
*/
/*
** 12/08/1999 New Info
**
** OK, Zippy had taken a shot at the circular cache for the map0. He was close, but I had to
** fix a few bugs. Also, I implemented the caching of the verdata.mul. Its important to note
** that the verdata.mul has lots of patches for all sorts of .mul files, I only kept the ones
** we were actively using.
**
** New rewrite plans, its way confusing having every function in here using 'Height' to mean
** elevation or actual height of a tile. Here's the new renaming plan:
** Elevation - the z value of the bottom edge of an item
** Height - the height of the item, it is independant of the elevation, like a person is 6ft tall
** Top - the z value of the top edge of an item, is always Elevation + Height of item
** -fur
*/
/*
** I thought it might be helpful to others to include a quick guide to what things
** meant in this code.
** Tile - a single item in the game, maybe static or dynamic
** Land - a single square from the map, the ground/water
** Static - a single item that comes as part of the basic map. NOTE: Land and Statics are both
** saved in the same .mul file! A lot of statics look like land as well.
** Dynamic - any item added that doesn't come as part of the basic map
** Single - an item in the game that will fit in a single cell
** Multi - any item in the game that is composed of multiple single items across multiple cells
** maybe static or dynamic, may have multiple singles within same cell
**
*/
#ifdef DEBUG_MAP_STUFF
void bitprint(FILE *fp, UI08 x )
{
for( int i = 7; i >= 0; --i )
{
if( ( x & 0x80 ) == 0 )
fprintf( fp, "0" );
else
fprintf( fp, "1" );
if( 4 == i )
fprintf( fp, " " );
x = x << 1;
}
}
#endif
cMapStuff::cMapStuff() : versionCache( NULL ), versionRecordCount( 0 ), versionMemory( 0 ), StaMem( 0 ), TileMem( 0 ),
Cache( false ), Map0CacheHit( 0 ), Map0CacheMiss( 0 ), StaticBlocks( 0 ), verfile( NULL ), tilefile( NULL ), multifile( NULL ), midxfile( NULL )
{
// after a mess of bugs with the structures not matching the physical record sizes
// i've gotten paranoid...
assert( sizeof( versionrecord ) >= VersionRecordSize );
assert( sizeof( st_multi ) >= MultiRecordSize );
assert( sizeof( map_st ) >= MapRecordSize );
assert( sizeof( st_multiidx ) >= MultiIndexRecordSize );
// staticrecord is not listed here because we explicitly don't read in some
// unknown bytes to save memory
for( UI32 i = 0; i < NumberOfWorlds; i++ )
{
mapArrays[i] = NULL;
statArrays[i] = NULL;
sidxArrays[i] = NULL;
}
mapname[0] = sidxname[0] = statname[0] = vername[0] = tilename[0] = multiname[0] = midxname[0] = '\0';
memset( tilecache, 0x00, sizeof( tilecache ) );
}
cMapStuff::~cMapStuff()
{
if( versionCache )
delete [] versionCache;
for( UI32 i = 0; i < NumberOfWorlds; i++ )
{
if( mapArrays[i] != NULL )
{
delete mapArrays[i];
mapArrays[i] = NULL;
}
if( statArrays[i] != NULL )
{
delete statArrays[i];
statArrays[i] = NULL;
}
if( sidxArrays[i] != NULL )
{
delete sidxArrays[i];
sidxArrays[i] = NULL;
}
}
if( verfile )
delete verfile;
if( tilefile )
delete tilefile;
if( multifile )
delete multifile;
if( midxfile )
delete midxfile;
for( UI32 j = 0; j < multiCache.size(); j++ )
{
delete[] multiCache[j]->cache;
delete multiCache[j];
}
multiCache.resize( 0 );
}
//o--------------------------------------------------------------------------o
//| Function/Class- void cMapStuff::Load( void )
//| Date - 03/12/2002
//| Developer(s) - Unknown / EviLDeD
//| Company/Team - UOX3 DevTeam
//| Status -
//o--------------------------------------------------------------------------o
//| Description - Prepare access streams to the server required mul files.
//| This function basicaly just opens the streams and validates
//| that the file is open and available
//o--------------------------------------------------------------------------o
//| Returns - N/A
//o--------------------------------------------------------------------------o
void cMapStuff::Load( void )
{
Console.PrintSectionBegin();
Console << "Preparing to open *.mul files..." << myendl << "(If they don't open, fix your paths in the uox3.ini)" << myendl;
string temp = "";
char lookupName[MAX_PATH];
UI32 i;
for( i = 0; i < NumberOfWorlds; i++ )
{
// Don't even bother to load unknown maps
if( uomapTypes[i] != UOMT_UNKNOWN )
{
sprintf( lookupName, "map%i.mul", i );
temp = BuildFilePath( lookupName );
mapArrays[i] = new UOXFile( temp.c_str(), "rb" );
Console << "\t" << temp.c_str() << "\t\t";
if( mapArrays[i] != NULL && mapArrays[i]->ready() )
{
uomapTypes[i] = CalcFromFileLength( mapArrays[i] );
if (uomapTypes[i] == UOMT_UNKNOWN)
Console.PrintFailed();
else
Console.PrintDone();
}
else
{
Console.PrintPassed();
// If the map file doesn't exist, there's no reason to keep looking for the other files
uomapTypes[i] = UOMT_UNKNOWN;
continue;
}
sprintf( lookupName, "staidx%i.mul", i );
temp = BuildFilePath( lookupName );
sidxArrays[i] = new UOXFile( temp.c_str(), "rb" );
Console << "\t" << temp.c_str() << "\t";
if( sidxArrays[i] != NULL && sidxArrays[i]->ready() )
{
Console.PrintDone();
}
else
{
uomapTypes[i] = UOMT_UNKNOWN;
Console.PrintFailed();
}
sprintf( lookupName, "statics%i.mul", i );
temp = BuildFilePath( lookupName );
statArrays[i] = new UOXFile( temp.c_str(), "rb" );
Console << "\t" << temp.c_str() << "\t";
if( statArrays[i] != NULL && statArrays[i]->ready() )
{
Console.PrintDone();
}
else
{
Console.PrintFailed();
uomapTypes[i] = UOMT_UNKNOWN;
}
}
}
for( i = 0; i < NumberOfWorlds; i++ )
{
if( uomapTypes[i] != UOMT_UNKNOWN )
break;
}
if( i == NumberOfWorlds )
{
// Hmm, no maps were valid, so not much sense in continuing
Console.Error( 1, " Fatal Error: No maps found" );
Console.Error( 1, " Check the settings for DATADIRECTORY in uox.ini" );
Shutdown( FATAL_UOX3_MAP_NOT_FOUND );
}
temp = BuildFilePath( "verdata.mul" );
Console << "\t" << temp.c_str() << "\t";
verfile = new UOXFile( temp.c_str(), "rb" );
if( verfile == NULL || !verfile->ready() )
{
Console.PrintPassed();
}
else
{
Console.PrintDone();
}
temp = BuildFilePath( "tiledata.mul" );
Console << "\t" << temp.c_str() << "\t";
tilefile = new UOXFile( temp.c_str(), "rb" );
if( tilefile == NULL || !tilefile->ready() )
{
Console.PrintFailed();
Shutdown( FATAL_UOX3_TILEDATA_NOT_FOUND );
}
Console.PrintDone();
temp = BuildFilePath( "multi.mul" );
Console << "\t" << temp.c_str() << "\t\t";
multifile = new UOXFile( temp.c_str(), "rb" );
if( multifile == NULL || !multifile->ready() )
{
Console.PrintFailed();
Shutdown( FATAL_UOX3_MULTI_DATA_NOT_FOUND );
}
Console.PrintDone();
temp = BuildFilePath( "multi.idx" );
Console << "\t" << temp.c_str() << "\t\t";
midxfile = new UOXFile( temp.c_str(), "rb" );
if( midxfile == NULL || !midxfile->ready() )
{
Console.PrintFailed();
Shutdown( FATAL_UOX3_MULTI_INDEX_NOT_FOUND );
}
Console.PrintDone();
Console << "MUL files successfully opened (but not yet loaded.)" << myendl;
Console.PrintSectionBegin();
CacheVersion();
CacheMultis();
if( Cache )
{
CacheTiles(); // has to be exactly here, or loadnewlorld cant access correct tiles
CacheStatics();
}
Console.PrintSectionBegin();
}
// this stuff is rather buggy thats why I separted it from uox3.cpp
// feel free to correct it, but be carefull
// bugfixing this stuff often has a domino-effect with walking etc.
// LB 24/7/99
// oh yah, well that's encouraging.. NOT! at least LB was kind enough to
// move this out into a separate file. he gets kudos for that!
//int cMapStuff::TileHeight(int tilenum)
SI08 cMapStuff::TileHeight( int tilenum )
{
CTile tile;
SeekTile(tilenum, &tile);
// For Stairs+Ladders
if( tile.Climbable() )
return (SI08)(tile.Height()/2); // hey, lets just RETURN half!
return (tile.Height());
}
//o-------------------------------------------------------------o
//| Function : SI08 StaticTop( SI16, SI16 y, SI08 oldz);
//| Date : Unknown Touched: Dec 21, 1998
//| Programmer : Unknown
//o-------------------------------------------------------------o
//| Purpose : Top of statics at/above given coordinates
//o-------------------------------------------------------------o
SI08 cMapStuff::StaticTop( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
SI08 top = illegal_z;
MapStaticIterator msi( x, y, worldNumber );
staticrecord *stat = NULL;
while( stat = msi.Next() )
{
SI08 tempTop = (SI08)(stat->zoff + TileHeight(stat->itemid));
if( ( tempTop <= oldz + MaxZstep ) && ( tempTop > top ) )
top = tempTop;
}
return top;
}
//
// i guess this function returns where the tile is a 'blocker' meaning you can't pass through it
bool cMapStuff::DoesTileBlock(int tilenum)
{
CTile tile;
SeekTile(tilenum, &tile);
return tile.Blocking();
}
//o--------------------------------------------------------------------------
//| Function - void SeekMultiSizes( UI16 multiNum, SI16& x1, SI16& x2, SI16& y1, SI16& y2 )
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Updates the box coordinates based on cached information
//o--------------------------------------------------------------------------
void cMapStuff::SeekMultiSizes( UI16 multiNum, SI16& x1, SI16& x2, SI16& y1, SI16& y2 )
{
x1 = multiCache[multiNum]->lx;
x2 = multiCache[multiNum]->ly;
y1 = multiCache[multiNum]->hx;
y2 = multiCache[multiNum]->hy;
}
//o--------------------------------------------------------------------------
//| Function - void MultiArea( CMultiObj *i, SI16 &x1, SI16 &y1, SI16 &x2, SI16 &y2 )
//| Date - 6th September, 1999
//| Programmer - Crackerjack
//| Modified - Abaddon, 26th September, 2001 - fetches from cache
//o--------------------------------------------------------------------------
//| Purpose - Finds the corners of a multi object
//o--------------------------------------------------------------------------
void cMapStuff::MultiArea( CMultiObj *i, SI16 &x1, SI16 &y1, SI16 &x2, SI16 &y2 )
{
x1 = y1 = x2 = y2 = 0;
if( i == NULL )
return;
const SI16 xAdd = i->GetX();
const SI16 yAdd = i->GetY();
SeekMultiSizes( (UI16)(i->GetID() - 0x4000), x1, x2, y1, y2 );
x1 += xAdd;
x2 += xAdd;
y1 += yAdd;
y2 += yAdd;
}
// return the height of a multi item at the given x,y. this seems to actually return a height
SI08 cMapStuff::MultiHeight( CItem *i, SI16 x, SI16 y, SI08 oldz )
{
SI32 length = 0;
UI16 multiID = ( i->GetID() ) - 0x4000;
SeekMulti( multiID, &length );
st_multi *multi = NULL;
if( length == -1 || length >= 17000000 ) //Too big... bug fix hopefully (Abaddon 13 Sept 1999)
length = 0;
for( int j = 0; j < length; j++ )
{
multi = SeekIntoMulti( multiID, j );
if( multi->visible && ( i->GetX() + multi->x == x) && ( i->GetY() + multi->y == y ) )
{
int tmpTop = i->GetZ() + multi->z;
if( ( tmpTop <= oldz + MaxZstep ) && ( tmpTop >= oldz - 1 ) )
{
return multi->z;
}
else if( ( tmpTop >= oldz - MaxZstep ) && ( tmpTop < oldz - 1 ) )
{
return multi->z;
}
}
}
return 0;
}
// This was fixed to actually return the *elevation* of dynamic items at/above given coordinates
SI08 cMapStuff::DynamicElevation( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
SI08 z = illegal_z;
SubRegion *MapArea = MapRegion->GetCell( x, y, worldNumber );
if( MapArea == NULL ) // no valid region
return z;
MapArea->PushItem();
for( CItem *tempItem = MapArea->FirstItem(); !MapArea->FinishedItems(); tempItem = MapArea->GetNextItem() )
{
if( tempItem == NULL )
continue;
if( tempItem->GetID( 1 ) >= 0x40 )
{
z = MultiHeight( tempItem, x, y, oldz );
z += tempItem->GetZ() + 1;
}
if( tempItem->GetX() == x && tempItem->GetY() == y && tempItem->GetID( 1 ) < 0x40 )
{
SI08 ztemp = (SI08)(tempItem->GetZ() + TileHeight( tempItem->GetID() ));
if( ( ztemp <= oldz + MaxZstep ) && ztemp > z )
z = ztemp;
}
}
MapArea->PopItem();
return z;
}
int cMapStuff::MultiTile( CItem *i, SI16 x, SI16 y, SI08 oldz)
{
SI32 length = 0;
st_multi *multi = NULL;
UI16 multiID = (UI16)(i->GetID() - 0x4000);
SeekMulti( multiID, &length );
if( length == -1 || length >= 17000000 )//Too big... bug fix hopefully (Abaddon 13 Sept 1999)
{
Console << "cMapStuff::MultiTile->Bad length in multi file. Avoiding stall." << myendl;
length = 0;
}
for( int j = 0; j < length; j++ )
{
multi = SeekIntoMulti( multiID, j );
if( ( multi->visible && ( i->GetX() + multi->x == x) && (i->GetY() + multi->y == y)
&& ( abs( i->GetZ() + multi->z - oldz ) <= 1 ) ) )
{
return multi->tile;
}
}
return 0;
}
// returns which dynamic tile is present at (x,y) or -1 if no tile exists
int cMapStuff::DynTile( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
SubRegion *MapArea = MapRegion->GetCell( x, y, worldNumber );
if( MapArea == NULL ) // no valid region
return -1;
MapArea->PushItem();
for( CItem *tempItem = MapArea->FirstItem(); !MapArea->FinishedItems(); tempItem = MapArea->GetNextItem() )
{
if( tempItem == NULL )
continue;
if( tempItem->GetID( 1 ) >= 0x40 )
{
MapArea->PopItem();
return MultiTile( tempItem, x, y, oldz );
}
else if( tempItem->GetX() == x && tempItem->GetY() == y )
{
MapArea->PopItem();
return tempItem->GetID();
}
}
MapArea->PopItem();
return -1;
}
// return the elevation of MAP0.MUL at given coordinates, we'll assume since its land
// the height is inherently 0
SI08 cMapStuff::MapElevation( SI16 x, SI16 y, UI08 worldNumber )
{
map_st map = SeekMap0( x, y, worldNumber );
// make sure nothing can move into black areas
if( 430 == map.id || 475 == map.id || 580 == map.id || 610 == map.id ||
611 == map.id || 612 == map.id || 613 == map.id)
return illegal_z;
return map.z;
}
// compute the 'average' map height by looking at three adjacent cells
SI08 cMapStuff::AverageMapElevation( SI16 x, SI16 y, UI16 &id, UI08 worldNumber )
{
// first thing is to get the map where we are standing
map_st map1 = Map->SeekMap0( x, y, worldNumber );
id = map1.id;
// if this appears to be a valid land id, <= 2 is invalid
if( map1.id > 2 && illegal_z != MapElevation( x, y, worldNumber ) )
{
// get three other nearby maps to decide on an average z?
SI08 map2z = MapElevation( x + 1, y, worldNumber );
SI08 map3z = MapElevation( x, y + 1, worldNumber );
SI08 map4z = MapElevation( x + 1, y + 1, worldNumber );
SI08 testz = 0;
if( abs( map1.z - map4z ) <= abs( map2z - map3z ) )
{
if( illegal_z == map4z )
testz = map1.z;
else
{
testz = (SI08)((map1.z + map4z) >> 1);
if( testz%2 < 0 )
--testz;
// ^^^ Fix to make it round DOWN, not just in the direction of zero
}
}
else
{
if( illegal_z == map2z || illegal_z == map3z )
testz = map1.z;
else
{
testz = (SI08)((map2z + map3z) >> 1);
if( testz%2<0 )
--testz;
// ^^^ Fix to make it round DOWN, not just in the direction of zero
}
}
return testz;
}
return illegal_z;
}
// since the version data will potentiall affect every map related operation
// we are always going to cache it. we are going to allocate maxRecordCount as
// given by the file, but actually we aren't going to use all of them, since we
// only care about the patches made to the 6 files the server needs. so the
// versionRecordCount hold how many we actually saved
void cMapStuff::CacheVersion( void )
{
if( verfile == NULL || !verfile->ready() )
{
return;
}
verfile->seek( 0, SEEK_SET );
UI32 maxRecordCount = 0;
verfile->getULong( &maxRecordCount );
if( 0 == maxRecordCount )
return;
if( NULL == ( versionCache = new versionrecord[maxRecordCount] ) )
return;
Console << "Caching version data...";
versionMemory = maxRecordCount * sizeof( versionrecord );
for( UI32 i = 0; i < maxRecordCount; ++i )
{
if( verfile->eof() )
{
Console.Error( 1, " Avoiding bad read crash with verdata.mul.\n" );
return;
}
versionrecord *ver = versionCache + versionRecordCount;
assert( ver );
verfile->get_versionrecord( ver );
// see if its a record we care about
switch( ver->file )
{
case VERFILE_MULTIIDX:
case VERFILE_MULTI:
Console << "Hit on Multi file!" << myendl;
case VERFILE_TILEDATA:
++versionRecordCount;
break;
case VERFILE_MAP:
case VERFILE_STAIDX:
case VERFILE_STATICS:
// at some point we may need to handle these cases, but OSI hasn't patched them as of
// yet, so no need slowing things down processing them
// Console.Error( "Eeek! OSI has patched the static data and I don't know what to do!" );
break;
default:
// otherwise its for a file we don't care about
break;
}
}
Console << "Done (Cached " << versionRecordCount << "/" << maxRecordCount << " patches)" << myendl;
}
SI32 cMapStuff::VerSeek( SI32 file, SI32 block )
{
for( UI32 i = 0; i < versionRecordCount; ++i )
{
if( versionCache[i].file == file && versionCache[i].block == block )
{
verfile->seek( versionCache[i].filepos, SEEK_SET );
return versionCache[i].length;
}
}
return 0;
}
bool cMapStuff::VerTile(int tilenum, CTile *tile)
{
if( tilenum == -1 )
return false;
const SI32 block=(tilenum/32);
if( VerSeek( VERFILE_TILEDATA, block + 0x200 ) == 0 )
return false;
else
{
const SI32 pos=4+(TileRecordSize*(tilenum%32)); // correct
verfile->seek(pos, SEEK_CUR);
verfile->get_tile_st(tile);
return true;
}
}
void cMapStuff::SeekTile(int tilenum, CTile *tile)
{
// assert(tilenum >= 0);
assert(tile);
if( tilenum < 0 )
{
tile->Flag1( 0 );
tile->Flag2( 0 );
tile->Flag3( 0 );
tile->Flag4( 0 );
tile->Weight( 255 );
tile->Height( 0 );
tile->Name( "bad tile" );
return;
}
if( tilenum >= 0x4000 )
{
tile->Name( "multi" );
tile->Flag1( 0 );
tile->Flag2( 0 );
tile->Flag3( 0 );
tile->Flag4( 0 );
tile->Weight( 255 );
tile->Height( 0 );
return;
}
if( Cache )
{
// fill it up straight from the cache
memcpy(tile, tilecache + tilenum, sizeof( CTile ));
#ifdef DEBUG_MAP_STUFF
Console << "SeekTile - cache hit!" << myendl;
#endif
}
else
{
if( VerTile( tilenum, tile ) )
{
#ifdef DEBUG_MAP_STUFF
Console << "Loaded tile " << tilenum << " from verdata.mul" << myendl;
#endif
}
else
{
// TILEDATA_TILE is the amount to skip past all of the land_st's
// plus skip 4 bytes per block for the long separating them
const SI32 block = ( tilenum / 32 );
const SI32 pos = TILEDATA_TILES + ( ( block + 1 ) * 4 ) + ( TileRecordSize * tilenum ); // correct
tilefile->seek( pos, SEEK_SET );
tilefile->get_tile_st( tile );
}
#ifdef DEBUG_MAP_STUFF
printf("Tile #%d is '%s' ", tilenum, tile->name);
printf("flag1: "); bitprint(stdout, tile->flag1);
printf("flag2: "); bitprint(stdout, tile->flag2);
printf("flag3: "); bitprint(stdout, tile->flag3);
printf("flag4: "); bitprint(stdout, tile->flag4);
printf("\n");
#endif
}
}
void cMapStuff::CacheTiles( void )
{
// temp disable caching so we can fill the cache
Cache = false;
Console << "Caching tiledata... ";
Console.TurnYellow();
Console << " 0%";
TileMem = 0x4000 * sizeof( CTile );
memset(tilecache, 0, TileMem);
const int tenPercent = 0x4000 / 10;
for( UI32 i = 0; i < 0x4000; ++i )
{
SeekTile(i, tilecache + i);
if( i % tenPercent == 0 )
{
if( i/tenPercent < 2 )
Console << "\b\b" << (SI16)( i / tenPercent * 10 ) << "%";
else
Console << "\b\b\b" << (SI16)( i / tenPercent * 10 ) << "%";
}
//Console.Print( "\b\b%d0%%\n", 1 + (i / tenPercent));
}
Console << "\b\b\b\b";
Console.PrintDone();
Cache = true;
#ifdef DEBUG_TILE_BITS
for( int bit = 0x01; bit <= 0x0080; bit = bit << 1 )
{
char buf[30];
sprintf( buf, "static1-%d.txt", bit );
FILE *fp = fopen( buf, "w" );
for( int i = 0; i < 0x4000; ++i )
{
CTile *tile = tilecache + i;
if( ( tile->flag1 & bit ) == bit)
{
fprintf( fp, "%04x tile '%-20.20s'\t", i, tile->name );
fprintf( fp, "flag1: "); bitprint( fp, tile->flag1 );
fprintf( fp, " flag2: "); bitprint( fp, tile->flag2 );
fprintf( fp, " flag3: "); bitprint( fp, tile->flag3 );
fprintf( fp, " flag4: "); bitprint( fp, tile->flag4 );
fprintf( fp, "\n");
}
}
fclose(fp);
sprintf(buf, "static2-%d.txt", bit);
fp = fopen(buf, "w");
for( int i = 0; i < 0x4000; ++i )
{
CTile *tile = tilecache + i;
if( ( tile->flag2 & bit ) == bit )
{
fprintf( fp, "%04x tile '%-20.20s'\t", i, tile->name );
fprintf( fp, "flag1: "); bitprint( fp, tile->flag1 );
fprintf( fp, " flag2: "); bitprint( fp, tile->flag2 );
fprintf( fp, " flag3: "); bitprint( fp, tile->flag3 );
fprintf( fp, " flag4: "); bitprint( fp, tile->flag4 );
fprintf( fp, "\n");
}
}
fclose( fp );
sprintf( buf, "static3-%d.txt", bit );
fp = fopen( buf, "w" );
for( int i = 0; i < 0x4000; ++i )
{
CTile *tile = tilecache + i;
if( ( tile->flag3 & bit ) == bit )
{
fprintf( fp, "%04x tile '%-20.20s'\t", i, tile->name );
fprintf( fp, "flag1: "); bitprint( fp, tile->flag1 );
fprintf( fp, " flag2: "); bitprint( fp, tile->flag2 );
fprintf( fp, " flag3: "); bitprint( fp, tile->flag3 );
fprintf( fp, " flag4: "); bitprint( fp, tile->flag4 );
fprintf( fp, "\n");
}
}
fclose(fp);
sprintf(buf, "static4-%d.txt", bit);
fp = fopen(buf, "w");
for( int i = 0; i < 0x4000; ++i )
{
CTile *tile = tilecache + i;
if( ( tile->flag4 & bit ) == bit )
{
fprintf( fp, "%04x tile '%-20.20s'\t", i, tile->name );
fprintf( fp, "flag1: "); bitprint( fp, tile->flag1 );
fprintf( fp, " flag2: "); bitprint( fp, tile->flag2 );
fprintf( fp, " flag3: "); bitprint( fp, tile->flag3 );
fprintf( fp, " flag4: "); bitprint( fp, tile->flag4 );
fprintf( fp, "\n" );
}
}
fclose( fp );
}
#endif
}
bool cMapStuff::VerLand(int landnum, CLand *land)
{
const SI32 block=(landnum/32);
if( VerSeek( VERFILE_TILEDATA, block ) == 0 )
return false;
const SI32 pos = 4 + ( LandRecordSize * ( landnum % 32 ) ); // correct
verfile->seek( pos, SEEK_CUR );
verfile->get_land_st( land );
return true;
}
void cMapStuff::SeekLand( int landnum, CLand *land)
{
const SI32 block = ( landnum / 32 );
if( !VerLand( landnum, land ) )
{
const SI32 pos = ( ( block + 1 ) * 4 ) + ( LandRecordSize * landnum ); // correct
tilefile->seek( pos, SEEK_SET );
tilefile->get_land_st( land );
}
}
bool cMapStuff::InsideValidWorld( SI16 x, SI16 y, UI08 worldNumber )
{
if( x < 0 || y < 0 )
return false;
if( worldNumber >= NumberOfWorlds )
return false;
UOMapType toGet = GetMapType( worldNumber );
return( x >= 0 && x < MapTileWidths[toGet] && y >= 0 && y < MapTileHeights[toGet] );
}
/*
** Use this iterator class anywhere you would have used SeekInit() and SeekStatic()
** Unlike those functions however, it will only return the location of tiles that match your
** (x,y) EXACTLY. They also should be significantly faster since the iterator saves
** a lot of info that was recomputed over and over and it returns a pointer instead
** of an entire structure on the stack. You can call First() if you need to reset it.
** This iterator hides all of the map implementation from other parts of the program.
** If you supply the exact variable, it tells it whether to iterate over those items
** with your exact (x,y) otherwise it will loop over all items in the same cell as you.
** (Thats normally only used for filling the cache)
**
** Usage:
** MapStaticIterator msi( x, y, worldNumber );
**
** staticrecord *stat = NULL;
** CTile tile;
** while( stat = msi.Next() )
** {
** msi.GetTile( &tile );
** ... your code here...
** }
*/
MapStaticIterator::MapStaticIterator( UI32 x, UI32 y, UI08 world, bool exact ) : worldNumber( world ), baseX( x / 8 ), baseY( y / 8 ), remainX( x % 8 ), remainY( y % 8 ), length( 0 ), index( 0 ), pos( 0 ), exactCoords( exact ), tileid( 0 )
{
if( !Map->InsideValidWorld( baseX, baseY, world ) )
{
Console.Error( 3, "ASSERT: MapStaticIterator(); Not inside a valid world" );
return;
}
if( Map->Cache )
{
StaCache_st *cacheEntry = Map->GrabCacheEntry( baseX, baseY, world );
if( cacheEntry == NULL )
length = 0;
else
length = cacheEntry->CacheLen;
}
else
{
UOXFile *reading = Map->sidxArrays[world];
if( reading == NULL )
{
length = 0;
return;
}
UOMapType gMap = Map->GetMapType( world );
const SI16 TileHeight = MapTileHeights[gMap];
const SI32 indexPos = (( baseX * TileHeight * 12L ) + ( baseY * 12L ));
reading->seek( indexPos, SEEK_SET );
if( !reading->eof() )
{
reading->getLong( &pos );
if( pos != -1 )
{
reading->getULong( &length );
length /= StaticRecordSize;
}
}
}
}
staticrecord *MapStaticIterator::First( void )
{
index = 0;
return Next();
}
staticrecord *MapStaticIterator::Next( void )
{
tileid = 0;
if( index >= length )
return NULL;
if( Map->Cache )
{
#ifdef MAP_CACHE_DEBUG
// turn this on for debugging z-level stuff where you want to see where
// the characters are in relation to their cell, and which places in the 8x8
// cell have static tiles defined.
if( index == 0 )
{
Console << "baseX: " << baseX << ", baseY: " << baseY << ", remX: " << (int)remainX << ", remY: " << (int)remainY << myendl;
Console << " 01234567" << myendl;
char testmap[9][9];
memset(testmap, ' ', 9*9);
for( int tmp = 0; tmp < length; ++tmp )
{
staticrecord *ptr = Map->StaticCache[baseX][baseY].Cache + tmp;
testmap[ptr->yoff][ptr->xoff] = 'X';
}
testmap[remainY][remainX] = 'O';
for( int foo = 0; foo < 8; ++foo )
{
testmap[foo][8] = '\0';
Console << foo << testmap[foo] << myendl;
}
}
#endif
StaCache_st *mCache = Map->GrabCacheEntry( baseX, baseY, worldNumber );
if( mCache != NULL )
{
do
{
staticrecord *ptr = (mCache->Cache) + index++;
if( !exactCoords || ( ptr->xoff == remainX && ptr->yoff == remainY ) )
{
tileid = ptr->itemid;
return ptr;
}
} while( index < length );
}
return NULL;
}
UOXFile *mFile = Map->statArrays[worldNumber];
if( mFile == NULL )
return NULL;
// this was sizeof(OldStaRec) which SHOULD be 7, but on some systems which don't know how to pack,
const SI32 pos2 = pos + (StaticRecordSize * index); // skip over all the ones we've read so far
mFile->seek( pos2, SEEK_SET );
do
{
if( mFile->eof( ) )
return NULL;
mFile->get_staticrecord( &staticArray );
++index;
//assert( staticArray.itemid >= 0 ); //itemid must always be >=0, because it is a Unsigned integer. - Shadowlord
// if these are ever larger than 7 we've gotten out of sync
assert( staticArray.xoff < 0x08 );
assert( staticArray.yoff < 0x08 );
if( !exactCoords || ( staticArray.xoff == remainX && staticArray.yoff == remainY ) )
{
#ifdef DEBUG_MAP_STUFF
Console << "Found static at index: " << index << ", Length: " << length << ", indepos: " << pos2 << myendl;
Console << "item is " << (int)staticArray.itemid << ", x" << (int)staticArray.xoff << ", y: " << (int) staticArray.yoff << myendl;
#endif
tileid = staticArray.itemid;
return &staticArray;
}
} while( index < length );
return NULL;
}
// since 99% of the time we want the tile at the requested location, here's a
// helper function. pass in the pointer to a struct you want filled.
void MapStaticIterator::GetTile( CTile *tile ) const
{
assert( tile );
Map->SeekTile( tileid, tile );
}
//
// some clean up to the caching and it wasn't reporting all the memory actually
// used by the StaticCache[][] in cMapStuff
//
void cMapStuff::CacheStatics( void )
{
// we must be in caching mode, only turn it off for now because we are
// trying to fill the cache.
assert( Cache );
Cache = false;
UI32 tableMemory = 0; // StaticBlocks * sizeof( staticrecord );
UI32 indexMemory = 0; // StaticBlocks * sizeof( StaCache_st );
UI32 tenPercent = StaticBlocks / 10;
UI32 currentBlock = 0;
for( UI08 wCtr = 0; wCtr < NumberOfWorlds; wCtr++ )
{
Console << "Caching Statics for World " << (SI16)wCtr << "... ";
Console.TurnYellow();
Console << "0%";
currentBlock = 0;
if( statArrays[wCtr] == NULL )
continue;
UI08 wrldCtr = uomapTypes[wCtr];
if( wrldCtr == 0xFF )
{
Console << "\b\b";
Console.PrintFailed();
Console.TurnRed();
Console << "Unknown world type found, please verify the map file is correct" << myendl;
Console.TurnNormal();
continue;
}
if( !statArrays[wCtr]->ready() )
continue;
SI16 maxX = MapTileWidths[wrldCtr];
SI16 maxY = MapTileHeights[wrldCtr];
StaticBlocks = ( maxX * maxY );
tableMemory += StaticBlocks * sizeof( staticrecord );
indexMemory += StaticBlocks * sizeof( StaCache_st );
tenPercent = StaticBlocks / 10;
StaticCache[wCtr].Resize( maxX, maxY );
for( SI16 x = 0; x < maxX; x++ )
{
for( SI16 y = 0; y < maxY; y++ )
{
StaCache_st *mCache = GrabCacheEntry( x, y, wCtr );
if( mCache == NULL )
continue;
mCache->Cache = NULL;
mCache->CacheLen = 0;
MapStaticIterator msi( x * 8, y * 8, wrldCtr, false );
UI32 length = msi.GetLength();
if( length )
{
mCache->CacheLen = length;
mCache->Cache = new staticrecord[length];
// read them all in at once!
statArrays[wrldCtr]->seek( msi.GetPos(), SEEK_SET );
statArrays[wrldCtr]->get_staticrecord( mCache->Cache, length );
}
if( currentBlock++ % tenPercent == 0 )
{
if( currentBlock/tenPercent < 2 )
Console << "\b\b" << (SI16)(currentBlock/tenPercent*10) << "%";
else
Console << "\b\b\b" << (SI16)(currentBlock/tenPercent*10) << "%";
}
}
}
Console << "\b\b\b\b";
Console.PrintDone();
}
// reenable the caching now that its filled
Cache = true;
StaMem = tableMemory + indexMemory;
Console << "Used " << tableMemory << " table bytes + " << indexMemory << " index bytes = " << StaMem << " total bytes to cache statics..." << myendl;
}
map_st cMapStuff::SeekMap0( SI16 x, SI16 y, UI08 worldNumber )
{
const SI16 x1 = x / 8, y1 = y / 8;
const UI08 x2 = (UI08)(x % 8), y2 = (UI08)(y % 8);
static SI16 CurCachePos = 0;
map_st map;
memset( &map, 0, sizeof( map_st ) );
if( worldNumber >= NumberOfWorlds )
return map;
UOMapType gMap = GetMapType( worldNumber );
if( gMap == UOMT_UNKNOWN )
return map;
if( !Cache )
{
const SI32 pos = ( x1 * MapTileHeights[gMap] * 196 ) + ( y1 * 196 ) + ( y2 * 24 ) + ( x2 * 3 ) + 4;
mapArrays[worldNumber]->seek( pos, SEEK_SET );
mapArrays[worldNumber]->get_map_st( &map );
return map;
}
SI16 i;
// sorry zip, but these loops should have been checking the newest stuff in the
// cache first, so its more likely to find a hit faster - fur
MapCache *mapCache = Map0Cache[worldNumber];
for( i = CurCachePos; i >= 0; --i )
{
if( ( mapCache[i].xb == x1 && mapCache[i].yb == y1 ) && ( mapCache[i].xo == x2 && mapCache[i].yo == y2 ) )
{
++Map0CacheHit;
return mapCache[i].Cache;
}
}
// if still not found, start at the end where the newer items are
for( i = MAP0CACHE - 1; i > CurCachePos; --i )
{
if( ( mapCache[i].xb == x1 && mapCache[i].yb == y1 ) && ( mapCache[i].xo == x2 && mapCache[i].yo == y2 ) )
{
++Map0CacheHit;
return mapCache[i].Cache;
}
}
Map0CacheMiss++;
const SI32 pos = ( x1 * MapTileHeights[gMap] * 196 ) + ( y1 * 196 ) + ( y2 * 24 ) + ( x2 * 3 ) + 4;
mapArrays[worldNumber]->seek( pos, SEEK_SET );
map_st *ptr = &(Map0Cache[worldNumber][CurCachePos].Cache);
mapArrays[worldNumber]->get_map_st( ptr );
Map0Cache[worldNumber][CurCachePos].xb = x1;
Map0Cache[worldNumber][CurCachePos].yb = y1;
Map0Cache[worldNumber][CurCachePos].xo = x2;
Map0Cache[worldNumber][CurCachePos].yo = y2;
// don't increment this until AFTER we have loaded everything, i had to fix what zippy did
CurCachePos++;
if( CurCachePos >= MAP0CACHE )
CurCachePos = 0;
return *ptr;
}
// these two functions don't look like they are actually used by anything
// anymore, at least we know which bit means wet
bool cMapStuff::IsTileWet(int tilenum) // lord binary
{
CTile tile;
SeekTile( tilenum, &tile );
return tile.LiquidWet();
}
// i don't know what this bit means exactly, its a walkway? or you are allowed
// to walk?
bool cMapStuff::TileWalk(int tilenum)
{
CTile tile;
SeekTile( tilenum, &tile );
return tile.Walk();
}
// Blocking statics at/above given coordinates?
bool cMapStuff::DoesStaticBlock( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
MapStaticIterator msi( x, y, worldNumber );
staticrecord *stat = NULL;
while( stat = msi.Next() )
{
const int elev = stat->zoff + TileHeight( stat->itemid );
if( (elev > oldz) && (stat->zoff <= oldz ) )
{
bool btemp = DoesTileBlock( stat->itemid );
if( btemp )
return true;
}
}
return false;
}
// Return new height of player who walked to X/Y but from OLDZ
SI08 cMapStuff::Height( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
// let's check in this order.. dynamic, static, then the map
SI08 dynz = DynamicElevation( x, y, oldz, worldNumber );
if( illegal_z != dynz )
return dynz;
SI08 staticz = StaticTop( x, y, oldz, worldNumber );
if( illegal_z != staticz )
return staticz;
return MapElevation( x, y, worldNumber );
}
// can the monster move here from an adjacent cell at elevation 'oldz'
// use illegal_z if they are teleporting from an unknown z
bool cMapStuff::CanMonsterMoveHere( SI16 x, SI16 y, SI08 oldz, UI08 worldNumber )
{
UOMapType gMap = GetMapType( worldNumber );
if( x < 0 || y < 0 || x >= MapWidths[gMap] || y >= MapHeights[gMap] )
return false;
const SI08 elev = Height( x, y, oldz, worldNumber );
if( illegal_z == elev )
return false;
// is it too great of a difference z-value wise?
if( oldz != illegal_z )
{
// you can climb MaxZstep, but fall up to 15
if( elev - oldz > MaxZstep )
return false;
else if( oldz - elev > 15 )
return false;
}
// get the tile id of any dynamic tiles at this spot
const int dt = DynTile( x, y, elev, worldNumber );
// if there is a dynamic tile at this spot, check to see if its a blocker
// if it does block, might as well short-circuit and return right away
if( dt >= 0 && DoesTileBlock( dt ) )
return false;
// if there's a static block here in our way, return false
if( DoesStaticBlock( x, y, elev, worldNumber ) )
return false;
return true;
}
bool cMapStuff::IsRoofOrFloorTile( CTile *tile )
// checks to see if the tile is either a roof or floor tile
{
if( tile->AtFloorLevel() )
return true; // check the floor bit
if( strstr( "roof", tile->Name()) || strstr( "shingle", tile->Name() ) )
return true;
if( strstr( "floor", tile->Name() ) )
return true;
// now why would not want to check the z value of wooden boards first??
// this was after the if( .. > z ), i'm moving this up inside of it
if( !strcmp( tile->Name(), "wooden boards" ) )
return true;
// i'll stick these back in. even if these were bogus tile names it can't hurt
if( !strcmp( tile->Name(), "wooden board" ) ||
!strcmp( tile->Name(), "stone pavern" ) ||
!strcmp( tile->Name(), "stone pavers" ) )
return true;
return false;
}
bool cMapStuff::IsRoofOrFloorTile( CTileUni *tile )
// checks to see if the tile is either a roof or floor tile
{
CTile newTile;
SeekTile( tile->ID(), &newTile );
return IsRoofOrFloorTile( &newTile );
}
void cMapStuff::CalculateMultiSizes( void )
{
for( UI32 i = 0; i < multiCache.size(); i++ )
{
if( multiCache[i] == NULL )
continue;
st_multi *multi = multiCache[i]->cache;
if( multi == NULL )
continue;
SI16 lx = 0, ly = 0, hx = 0, hy = 0;
for( int j = 0; j < multiCache[i]->length; j++ )
{
if( multi[j].x < lx )
lx = multi[j].x;
if( multi[j].x > hx )
hx = multi[j].x;
if( multi[j].y < ly )
ly = multi[j].y;
if( multi[j].y > hy )
hy = multi[j].y;
}
multiCache[i]->lx = lx;
multiCache[i]->ly = ly;
multiCache[i]->hx = hx;
multiCache[i]->hy = hy;
}
}
void cMapStuff::CacheMultis( void )
{
UI32 indexMemoryUsed = 0;
UI32 multiMemoryUsed = 0;
Console << "Caching Multis.... ";
// we must be in caching mode, only turn it off for now because we are
// trying to fill the cache.
string temp = BuildFilePath( "multi.idx" );
FILE *multiIDXRec = fopen( temp.c_str(), "rb" );
if( multiIDXRec == NULL )
{
Console.PrintFailed();
Console.Error( 1, "Can't cache %s! File cannot be opened", temp.c_str() );
return;
}
temp = BuildFilePath( "multi.mul" );
UOXFile *multis = new UOXFile( temp.c_str(), "rb" );
if( multis == NULL || !multis->ready() )
{
Console.PrintFailed();
Console.Error( 1, "Can't cache %s! File cannot be opened", temp.c_str() );
fclose(multiIDXRec);
return;
}
fseek( multiIDXRec, 0, SEEK_END );
SI32 fileLen = ftell( multiIDXRec );
fclose( multiIDXRec );
UI16 numRecords = fileLen / MultiIndexRecordSize;
temp = BuildFilePath( "multi.idx" );
UOXFile *multiIDX = new UOXFile( temp.c_str(), "rb" );
if( multiIDX == NULL || !multiIDX->ready() )
{
Console.Error( 1, "Can't cache %s! File cannot be opened", temp.c_str() );
fclose(multiIDXRec);
return;
}
multiCache.resize( numRecords ); // do one big resize here
indexMemoryUsed = multiCache.size() * sizeof( MultiCache );
const UI32 tenPercent = numRecords / 10;
st_multiidx multiidx;
Console.TurnYellow();
Console << " 0%";
for( UI32 counter = 0; counter < numRecords; counter++ )
{
multiCache[counter] = new MultiCache;
multiIDX->seek( counter * MultiIndexRecordSize, SEEK_SET );
multiIDX->get_st_multiidx( &multiidx );
multiCache[counter]->length = multiidx.length;
if( multiCache[counter]->length != -1 )
{
multiCache[counter]->length /= MultiRecordSize;
multiCache[counter]->cache = new st_multi[multiCache[counter]->length];
multiMemoryUsed += ( multiCache[counter]->length * sizeof( st_multi ) );
multis->seek( multiidx.start, SEEK_SET );
for( int multiCounter = 0; multiCounter < multiCache[counter]->length; multiCounter++ )
multis->get_st_multi( &multiCache[counter]->cache[multiCounter] );
}
if( counter % tenPercent == 0 )
{
if( counter/tenPercent < 2 )
Console << "\b\b" << (SI16)(counter/tenPercent*10) << "%";
else
Console << "\b\b\b" << (SI16)(counter/tenPercent*10) << "%";
}
}
Console << "\b\b\b\b";
Console.TurnNormal();
CalculateMultiSizes();
// reenable the caching now that its filled
Console.PrintDone();
Console << "Multi caching taking up " << indexMemoryUsed + multiMemoryUsed << " bytes, " << indexMemoryUsed << " in index, " << multiMemoryUsed << " in data..." << myendl;
fclose(multiIDXRec);
}
void cMapStuff::SeekMulti( UI32 multinum, SI32 *length )
// multinum is also the index into the array!!! Makes our life MUCH easier, doesn't it?
// TODO: Make it verseek compliant!!!!!!!!!
// Currently, there is no hit on either of the multi files, so we're fairly safe
// But we probably want to not rely on this being always true
// especially with the extra land patch coming up!
{
if( multinum >= multiCache.size() )
*length = -1;
else
*length = multiCache[multinum]->length;
}
st_multi *cMapStuff::SeekIntoMulti( int multinum, int number )
{
return &multiCache[multinum]->cache[number];
}
string cMapStuff::BuildFilePath( const char *fName )
{
char temp[MAX_PATH];
sprintf( temp, "%s/%s", cwmWorldState->ServerData()->GetDataDirectory(), fName );
return temp;
}
UOMapType cMapStuff::GetMapType( UI08 worldNumber )
{
if( worldNumber >= NumberOfWorlds )
return UOMT_BRITANNIA;
return uomapTypes[worldNumber];
}
void CTile::Read( UOXFile *toRead )
{
toRead->getUChar( &flag1 );
toRead->getUChar( &flag2 );
toRead->getUChar( &flag3 );
toRead->getUChar( &flag4 );
toRead->getUChar( &weight );
toRead->getChar( &layer );
toRead->getLong( &unknown1 );
toRead->getLong( &animation );
toRead->getChar( &unknown2 );
toRead->getChar( &unknown3 );
toRead->getChar( &height );
toRead->getChar( name, 20 );
}
void CLand::Read( UOXFile *toRead )
{
toRead->getUChar( &flag1 );
toRead->getUChar( &flag2 );
toRead->getUChar( &flag3 );
toRead->getUChar( &flag4 );
toRead->getChar( &unknown1 );
toRead->getChar( &unknown2 );
toRead->getChar( name, 20 );
}
StaWorldCache::StaWorldCache( SI16 maxX, SI16 maxY )
{
Resize( maxX, maxY );
}
//o--------------------------------------------------------------------------
//| Function - ~StaWorldCache()
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Deallocates any memory associated with the world cache
//o--------------------------------------------------------------------------
StaWorldCache::~StaWorldCache()
{
}
//o--------------------------------------------------------------------------
//| Function - StaCacheLine *GrabCacheLine( SI16 x )
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Returns the cache line associated with line x
//o--------------------------------------------------------------------------
StaCacheLine *StaWorldCache::GrabCacheLine( SI16 trgX )
{
if( trgX >= (SI16)CacheList.size() || trgX < 0 )
return NULL;
return &(CacheList[trgX]);
}
StaWorldCache::StaWorldCache( void )
{
}
//o--------------------------------------------------------------------------
//| Function - Resize( SI16 maxX, SI16 maxY )
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Resizes the world cache to a grid size of maxX, maxY
//o--------------------------------------------------------------------------
void StaWorldCache::Resize( SI16 maxX, SI16 maxY )
{
CacheList.resize( maxX );
for( SI16 i = 0; i < maxX; i++ )
CacheList[i].resize( maxY );
}
//o--------------------------------------------------------------------------
//| Function - StaCache_st *GrabCacheEntry( SI16 x, SI16 y, UI08 worldNumber )
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Returns a pointer to the static cache entry for the
//| grid x,y in worldNumber
//o--------------------------------------------------------------------------
StaCache_st *cMapStuff::GrabCacheEntry( SI16 x, SI16 y, UI08 worldNumber )
{
if( worldNumber >= NumberOfWorlds )
return NULL;
StaCacheLine *line = StaticCache[worldNumber].GrabCacheLine( x );
if( line == NULL )
return NULL;
return &((*line)[y]);
}
//o--------------------------------------------------------------------------
//| Function - UOMapType CalcFromFileLength( UOXFile *toCalcFrom )
//| Date - 26th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - It returns the map type, based on the map's file size
//o--------------------------------------------------------------------------
UOMapType cMapStuff::CalcFromFileLength( UOXFile *toCalcFrom )
{
if( toCalcFrom == NULL )
{
Console << "Invalid file in cMapStuff:CaclFromFileLength";
return UOMT_UNKNOWN;
}
SI32 fLength = toCalcFrom->getLength();
for( UI32 i = 0; i < UOMT_COUNT; i++ )
{
if( MapFileLengths[i] == fLength )
return (UOMapType)i;
}
Console << "Incorrect map Length " << fLength;
return UOMT_UNKNOWN;
}
//o--------------------------------------------------------------------------
//| Function - bool MapExists( UI08 worldNumber )
//| Date - 27th September, 2001
//| Programmer - Abaddon
//| Modified -
//o--------------------------------------------------------------------------
//| Purpose - Returns true if the server has that map in memory
//o--------------------------------------------------------------------------
bool cMapStuff::MapExists( UI08 worldNumber )
{
if( worldNumber >= NumberOfWorlds )
return false;
return (mapArrays[worldNumber] != NULL);
}